-(* $Id$ *)
-
-open Util
-open Os
-open Common
-open Uicommon
-open Printf
-open Trace
-
-(**********************************************************************)
-(* GRAPHICAL INTERFACE *)
-(**********************************************************************)
-
-module Private : Uicommon.UI = struct
-
-open GMain
-open GdkKeysyms
-
-let debugprogress = Trace.debug "progress"
-
-(**********************************************************************)
-(* UI preferences *)
-(**********************************************************************)
-
-let fontMonospaceMedium =
- if Sys.os_type = "Win32" then
- lazy (Gdk.Font.load "-*-Courier New-Medium-R-Normal--*-110-*-*-*-*-*-*")
- else
- lazy (Gdk.Font.load "-*-Clean-Medium-R-Normal--*-130-*-*-*-*-*-*")
-let fontMonospaceBold =
- if Sys.os_type = "Win32" then
- lazy (Gdk.Font.load "-*-Courier New-Bold-R-Normal--*-110-*-*-*-*-*-*")
- else
- lazy (Gdk.Font.load "-*-Courier-Bold-R-Normal--*-120-*-*-*-*-*-*")
-
-(**********************************************************************)
-(* UI state variables *)
-(**********************************************************************)
-
-type stateItem = { ri : reconItem;
- mutable bytesTransferred : int;
- mutable whatHappened : unit confirmation option }
-let theState = ref [||]
-
-let current = ref None
-
-let currentWindow = ref None
-let grabFocus t =
- match !currentWindow with
- Some w -> t#set_transient_for w;
- w#misc#set_sensitive false
- | None -> ()
-let releaseFocus () =
- begin match !currentWindow with
- Some w -> w#misc#set_sensitive true
- | None -> ()
- end
-
-(**********************************************************************)
-(* Lock management *)
-(**********************************************************************)
-
-let busy = ref false
-
-let getLock f =
- if !busy then
- Trace.message "Synchronizer is busy, please wait..\n"
- else begin
- busy := true; f (); busy := false
- end
-
-(**********************************************************************)
-(* Some widgets *)
-(**********************************************************************)
-
-class scrolled_text ?editable ?word_wrap ?width ?height ?packing ?show
- () =
- let sw =
- GBin.scrolled_window ?width ?height ?packing ~show:false
- ~hpolicy:`NEVER ~vpolicy:`AUTOMATIC ()
- in
- let text = GEdit.text ?editable ?word_wrap ~packing:sw#add () in
- object
- inherit GObj.widget_full sw#as_widget
- method text = text
- method insert ?(font=fontMonospaceMedium) s =
- text#freeze ();
- text#delete_text ~start:0 ~stop:text#length;
- text#insert ~font:(Lazy.force font) s;
- text#thaw ()
- method show () = sw#misc#show ()
- initializer
- if show <> Some false then sw#misc#show ()
- end
-
-let gtk_sync () = while Glib.Main.iteration false do () done
-
-(**********************************************************************)
-(* YES OR NO FUNCTION *)
-(**********************************************************************)
-(* val yesOrNo : string -> string -> (unit -> unit) -> (unit -> unit) *)
-(* -> unit *)
-(* Displays a window with two buttons : YES and NO *)
-(* yesOrNo title message yesFunction noFunction open the title *)
-(* window in which is displayed message. yesFunction and noFunctions *)
-(* are the functions associated two the two buttons *)
-(**********************************************************************)
-let yesOrNo ~title ~message ~yes:yesFunction ~no:noFunction =
- let t = GWindow.dialog ~title ~wm_name:title ~modal:true ~position:`CENTER () in
- grabFocus t;
- let theLabel = GMisc.label ~text:message
- ~packing:(t#vbox#pack ~padding:4) () in
- let yes = GButton.button ~label:"Yes" ~packing:t#action_area#add ()
- and no = GButton.button ~label:"No" ~packing:t#action_area#add () in
- no#grab_default ();
- ignore
- (yes#connect#clicked ~callback:(fun () -> t#destroy (); yesFunction ()));
- ignore
- (no#connect#clicked ~callback:(fun () -> t#destroy (); noFunction ()));
- t#show ();
- ignore (t#connect#destroy ~callback:Main.quit);
- Main.main ();
- releaseFocus ()
-
-(**********************************************************************)
-(* SAFE EXIT FUNCTION *)
-(**********************************************************************)
-
-(* Avoid recursive invocations of the function below
- (a window receives delete events even when it is not sensitive) *)
-let inExit = ref false
-
-let safeExit () =
- if not !inExit then begin
- inExit := true;
- if not !busy then exit 0 else
- yesOrNo ~title:"Premature exit"
- ~message:"Unison is working, exit anyway ?"
- ~yes:(fun () -> exit 0) ~no:(fun () -> ());
- inExit := false
- end
-
-(**********************************************************************)
-(* okBox: Display a message in a window and wait for the user *)
-(* to hit the "OK" button. *)
-(**********************************************************************)
-let okBox ~title ~message =
- let t = GWindow.dialog ~title ~wm_name:title ~modal:true ~position:`CENTER () in
- grabFocus t;
- let theLabel = GMisc.label ~text:message
- ~packing:(t#vbox#pack ~padding:4) () in
- let ok = GButton.button ~label:"OK" ~packing:t#action_area#add () in
- ok#grab_default ();
- ignore (ok#connect#clicked ~callback:(fun () -> t#destroy()));
- t#show ();
- (* Do nothing until user destroys window *)
- ignore (t#connect#destroy ~callback:Main.quit);
- Main.main ();
- releaseFocus ()
-
-(**********************************************************************)
-(* warnBox: Display a warning message in a window and wait for the *)
-(* user to hit "OK" or "Exit". *)
-(**********************************************************************)
-let warnBox ~title ~message =
- inExit := true;
- let t = GWindow.dialog ~title ~wm_name:title ~modal:true ~position:`CENTER () in
- grabFocus t;
- let theLabel =
- GMisc.label ~text:message
- ~packing:(t#vbox#pack ~padding:4) () in
- let ok = GButton.button ~label:"OK" ~packing:t#action_area#add () in
- ok#grab_default ();
- ignore (ok#connect#clicked ~callback:(fun () -> t#destroy()));
- let exi = GButton.button ~label:"Exit" ~packing:t#action_area#add () in
- ignore (exi#connect#clicked ~callback:(fun () -> exit 0));
- t#show ();
- (* Do nothing until user destroys window *)
- ignore (t#connect#destroy ~callback:Main.quit);
- Main.main ();
- releaseFocus ();
- inExit := false
-
-(**********************************************************************)
-(* Standard file dialog *)
-(**********************************************************************)
-let file_dialog ~title ~callback ?filename () =
- let sel = GWindow.file_selection ~title ~modal:true ?filename () in
- grabFocus sel;
- ignore (sel#cancel_button#connect#clicked ~callback:sel#destroy);
- ignore (sel#ok_button#connect#clicked ~callback:
- (fun () ->
- let name = sel#get_filename in
- sel#destroy ();
- callback name));
- sel#show ();
- Main.main ();
- releaseFocus ()
-
-(**********************************************************************)
-(* The root selection dialog *)
-(**********************************************************************)
-let rootSelect cont =
- let t = GWindow.dialog ~title:"Root selection" ~wm_name:"Root selection"
- ~modal:true ~allow_grow:true () in
- t#misc#grab_focus ();
-
- let makeGetRoot title =
- let fr =
- GBin.frame ~label:title ~border_width:2 ~packing:(t#vbox#add) () in
-
- let vb = GPack.vbox ~border_width:4 ~packing:fr#add () in
-
- let f = GPack.vbox ~packing:(vb#add) () in
- let f0 = GPack.hbox ~spacing:4 ~packing:f#add () in
- ignore (GMisc.label ~text:"Host:" ~packing:f0#pack ());
- let localB = GButton.radio_button ~packing:f0#pack
- ~label:"Local" () in
- let remoteB = GButton.radio_button ~group:localB#group
- ~packing:f0#pack ~label:"Remote" () in
- let hostE = GEdit.entry ~packing:f0#add () in
- let f1 = GPack.hbox ~spacing:4 ~packing:f#add () in
- ignore (GMisc.label ~text:"File:" ~packing:f1#pack ());
- let fileE = GEdit.entry ~packing:f1#add () in
- let browseCommand() =
- file_dialog ~title:"Select a local file"
- ~callback:(fun file -> fileE#set_text file) ~filename:fileE#text ()
- in
- let b = GButton.button ~label:"Browse"
- ~packing:f1#pack () in
- ignore (b#connect#clicked ~callback:browseCommand);
- let varLocalRemote = ref (`Local : [`Local|`Remote]) in
- let localState() =
- varLocalRemote := `Local;
- hostE#misc#set_sensitive false;
- b#misc#set_sensitive true
- in
- let remoteState() =
- varLocalRemote := `Remote;
- hostE#misc#set_sensitive true;
- b#misc#set_sensitive false
- in
- ignore (localB#connect#clicked ~callback:localState);
- ignore (remoteB#connect#clicked ~callback:remoteState);
- localState();
- let getRoot() =
- let filePart = fileE#text in
- let remoteHost = hostE#text in
- (* FIX: should do sanity checking here *)
- match !varLocalRemote with
- `Local -> filePart
- | `Remote -> "//"^remoteHost^"/"^filePart in
- getRoot
- in
-
- let getRoot1 = makeGetRoot "Root 1" in
- let getRoot2 = makeGetRoot "Root 2" in
-
- let f3 = t#action_area in
- let okCommand() =
- let root1 = getRoot1() in
- let root2 = getRoot2() in
- Prefs.setPref Uicommon.roots Prefs.TempSetting [root1;root2];
- t#destroy ();
- cont ()
- in
- let okButton = GButton.button ~label:"OK" ~packing:f3#add () in
- ignore (okButton#connect#clicked ~callback:okCommand);
- okButton#grab_default ();
- let cancelButton = GButton.button ~label:"Cancel" ~packing:f3#add () in
- ignore (cancelButton#connect#clicked ~callback:safeExit);
-
- (* The profile editing dialog has been installed into the Gtk
- main interaction loop; wait until it completes. *)
- t#show ();
- ignore (t#connect#destroy ~callback:Main.quit);
- Main.main ()
-
-(**********************************************************************)
-(* The root selection dialog *)
-(**********************************************************************)
-let editProfile prof =
- (* FIX:
- Scan the profile (if it is defined)
- Extract the roots
- Modifications
- Save with new roots *)
- let t = GWindow.dialog ~title:"Edit profile" ~wm_name:"Edit profile"
- ~modal:true ~allow_grow:true () in
- t#misc#grab_focus ();
-
- let vb = GPack.vbox ~border_width:4 ~packing:t#vbox#add () in
-
- let makeGetRoot() =
- let f = GPack.vbox ~packing:(vb#pack ~expand:true ~padding:4) () in
- let f0 = GPack.hbox ~spacing:4 ~packing:f#add () in
- ignore (GMisc.label ~text:"Host:" ~packing:f0#pack ());
- let localB = GButton.radio_button ~packing:f0#pack ~label:"Local" () in
- let remoteB = GButton.radio_button ~group:localB#group
- ~packing:f0#pack ~label:"Remote" () in
- let hostE = GEdit.entry ~packing:f0#add () in
- let f1 = GPack.hbox ~spacing:4 ~packing:f#add () in
- ignore (GMisc.label ~text:"File:" ~packing:f1#pack ());
- let fileE = GEdit.entry ~packing:f1#add () in
- let browseCommand() =
- file_dialog ~title:"Select a local file"
- ~callback:(fun file -> fileE#set_text file) ()
- in
- let b = GButton.button ~label:"Browse" ~packing:f1#pack () in
- ignore (b#connect#clicked ~callback:browseCommand);
- let varLocalRemote = ref (`Local : [`Local|`Remote]) in
- let localState() =
- varLocalRemote := `Local;
- hostE#set_editable false;
- b#misc#set_state `NORMAL
- in
- let remoteState() =
- varLocalRemote := `Remote;
- hostE#set_editable true;
- b#misc#set_state `INSENSITIVE
- in
- ignore (localB#connect#clicked ~callback:localState);
- ignore (remoteB#connect#clicked ~callback:remoteState);
- localState();
- let getRoot() =
- let filePart = fileE#text in
- let remoteHost = hostE#text in
- (* FIX: should do sanity checking here *)
- match !varLocalRemote with
- `Local -> filePart
- | `Remote -> "//"^remoteHost^"/"^filePart in
- getRoot
- in
-
-
- ignore (GMisc.label ~text:"Root 1:" ~xalign:0.
- ~packing:(vb#pack ~expand:true ~padding:4) ());
- let getRoot1 = makeGetRoot() in
-
- ignore (GMisc.label ~text:"Root 2:" ~xalign:0.
- ~packing:(vb#pack ~expand:true ~padding:4) ());
- let getRoot2 = makeGetRoot() in
-
- let f3 = t#action_area in
- let okCommand() =
- let root1 = getRoot1() in
- let root2 = getRoot2() in
- Prefs.setPref Uicommon.roots Prefs.PermanentSetting [root1;root2];
- Globals.savePrefs();
- t#destroy () in
- let okButton = GButton.button ~label:"OK" ~packing:f3#add () in
- ignore (okButton#connect#clicked ~callback:okCommand);
- let cancelCommand() =
- t#destroy ()
- in
- let cancelButton = GButton.button ~label:"Cancel" ~packing:f3#add () in
- ignore (cancelButton#connect#clicked ~callback:cancelCommand);
-
- (* The profile editing dialog has been installed into the Gtk
- main interaction loop; wait until it completes. *)
- t#show ();
- ignore (t#connect#destroy ~callback:Main.quit);
- Main.main ()
-
-(**********************************************************************)
-(* Documentation window *)
-(**********************************************************************)
-let documentation sect =
- let title = "Documentation" in
- let t = GWindow.dialog ~title ~wm_name:title () in
- let t_dismiss =
- GButton.button ~label:"dismiss" ~packing:t#action_area#add () in
- t_dismiss#grab_default ();
- let dismiss () = t#destroy () in
- ignore (t_dismiss#connect#clicked ~callback:dismiss);
- ignore (t#event#connect#delete ~callback:(fun _ -> dismiss (); true));
-
- let (name, docstr) = List.assoc sect Strings.docs in
- let hb = GPack.hbox ~packing:(t#vbox#pack ~expand:false ~padding:2) () in
- let optionmenu =
- GMenu.option_menu ~packing:(hb#pack ~fill:false) () in
-
- let charW = Gdk.Font.char_width (Lazy.force fontMonospaceMedium) 'M' in
- let charH = 16 in
- let t_text =
- new scrolled_text ~editable:false
- ~width:(charW * 80) ~height:(charH * 20) ~packing:t#vbox#add ()
- in
- t_text#insert docstr;
-
- let sect_idx = ref 0 in
- let idx = ref 0 in
- let menu = GMenu.menu () in
- let addDocSection (shortname, (name, docstr)) =
- if shortname <> "" && name <> "" then begin
- if shortname = sect then sect_idx := !idx;
- incr idx;
- let item = GMenu.menu_item ~label:name ~packing:menu#append () in
- ignore
- (item#connect#activate ~callback:(fun () -> t_text#insert docstr))
- end
- in
- Safelist.iter addDocSection Strings.docs;
- optionmenu#set_menu menu;
- optionmenu#set_history !sect_idx;
-
- t#show ()
-
-(**********************************************************************)
-(* The profile selection dialog *)
-(**********************************************************************)
-let profileSelect cont =
-(* FIX:
- - Choix du profil par defaut
-*)
- let profilesAndRoots =
- ref
- (Safelist.map
- (fun f ->
- let filename = fspath2string (Os.fileInUnisonDir f) in
- let roots =
- Safelist.map snd
- (Safelist.filter (fun (n, v) -> n = "root")
- (Prefs.scanPreferencesFile filename))
- in
- (Filename.chop_suffix f ".prf", roots))
- (Files.ls (fspath2string Os.synchronizerFspath) "*.prf")) in
-
- (* The selected profile *)
- let selection = ref None in
-
- (* Build the dialog *)
- let t = GWindow.dialog ~title:"Profiles" ~wm_name:"Profiles" () in
-
- let okCommand() =
- match !selection with
- Some profile ->
- Globals.prefsFileName := profile ^ ".prf";
- currentWindow := None;
- t#destroy ();
- cont ()
- | _ ->
- ()
- in
- let okButton = GButton.button ~label:"OK" ~packing:t#action_area#add () in
- ignore (okButton#connect#clicked ~callback:okCommand);
- okButton#misc#set_sensitive false;
- okButton#grab_default ();
- let cancelCommand() = t#destroy (); Main.quit () in
- let cancelButton = GButton.button ~label:"Cancel"
- ~packing:t#action_area#add () in
- ignore (cancelButton#connect#clicked ~callback:cancelCommand);
- cancelButton#misc#set_can_default true;
-
- let vb = t#vbox in
-
- ignore (GMisc.label
- ~text:"Select an existing profile or create a new one"
- ~xpad:2 ~ypad:2 ~packing:vb#pack ());
-
- let sw =
- GBin.scrolled_window ~packing:vb#add ~height:100
- ~hpolicy:`AUTOMATIC ~vpolicy:`AUTOMATIC ()
- in
- let lst = GList.clist ~selection_mode:`BROWSE ~packing:(sw#add) () in
- let fillLst default =
- lst#freeze ();
- lst#clear ();
- let selRow = ref 0 in
- let i = ref 0 in (* FIX: Work around a lablgtk bug *)
- Safelist.iter
- (fun (profile, roots) ->
- ignore (lst#append [profile]);
- if profile = default then selRow := !i;
- lst#set_row_data !i (profile, roots);
- incr i)
- (List.sort (fun (p, _) (p', _) -> compare p p') !profilesAndRoots);
- let r = lst#rows in
- let p = if r < 2 then 0. else float !selRow /. float (r - 1) in
- lst#scroll_vertical `JUMP p;
- lst#thaw ()
- in
- let tbl =
- GPack.table ~rows:2 ~columns:2 ~packing:vb#pack ()
- in
- tbl#misc#set_sensitive false;
- ignore (GMisc.label ~text:"Root 1:" ~xpad:2
- ~packing:(tbl#attach ~left:0 ~top:0 ~expand:`NONE) ());
- ignore (GMisc.label ~text:"Root 2:" ~xpad:2
- ~packing:(tbl#attach ~left:0 ~top:1 ~expand:`NONE) ());
- let root1 =
- GEdit.entry ~packing:(tbl#attach ~left:1 ~top:0) ~editable:false () in
- let root2 =
- GEdit.entry ~packing:(tbl#attach ~left:1 ~top:1) ~editable:false () in
- root1#misc#set_can_focus false;
- root2#misc#set_can_focus false;
- let hb =
- GPack.hbox ~border_width:2 ~spacing:2 ~packing:(vb#pack ~expand:false) ()
- in
- let nw =
- GButton.button ~label:"Create new profile"
- ~packing:hb#pack () in
- ignore (nw#connect#clicked ~callback:(fun () ->
- let t =
- GWindow.dialog ~title:"New profile" ~wm_name:"New profile" ~modal:true ()
- in
- let vb = GPack.vbox ~border_width:4 ~packing:t#vbox#add () in
- let f = GPack.vbox ~packing:(vb#pack ~expand:true ~padding:4) () in
- let f0 = GPack.hbox ~spacing:4 ~packing:f#add () in
- ignore (GMisc.label ~text:"Profile name:"
- ~packing:f0#pack ());
- let prof = GEdit.entry ~packing:f0#add () in
- prof#misc#grab_focus ();
-
- let exit () = t#destroy (); Main.quit () in
- ignore (t#event#connect#delete ~callback:(fun _ -> exit (); true));
-
- let f3 = t#action_area in
- let okCommand () =
- let profile = prof#text in
- if profile <> "" then
- let file = profile ^ ".prf" in
- let fspath = Os.fileInUnisonDir file in
- let filename = fspath2string fspath in
- if Sys.file_exists filename then
- okBox (myName ^ " error")
- ("Profile \""
- ^ profile
- ^ "\" already exists!\nPlease select another name.")
- else
- (* Make an empty file *)
- let ch =
- open_out_gen
- [Open_wronly; Open_creat; Open_trunc] 0o600 filename in
- close_out ch;
- profilesAndRoots := (profile, [])::!profilesAndRoots;
- fillLst profile;
- exit ()
- in
- let okButton = GButton.button ~label:"OK" ~packing:f3#add () in
- ignore (okButton#connect#clicked ~callback:okCommand);
- okButton#grab_default ();
- let cancelButton = GButton.button ~label:"Cancel" ~packing:f3#add () in
- ignore (cancelButton#connect#clicked ~callback:exit);
-
- t#show ();
- grabFocus t;
- Main.main ();
- releaseFocus ()));
- let ed =
- GButton.button ~label:"Edit" (*~packing:(hb#pack ~expand:false)*) () in
- let sd =
- GButton.button ~label:"Set default" (*~packing:(hb#pack ~expand:false)*) ()
- in
- let hlp =
- GButton.button ~label:"Help"
- ~packing:(hb#pack ~expand:false ~from:`END) () in
- ignore (hlp#connect#clicked ~callback:(fun () -> documentation "tutorial"));
-
- ignore (lst#connect#unselect_row ~callback:(fun _ _ _ ->
- root1#set_text ""; root2#set_text "";
- selection := None;
- tbl#misc#set_sensitive false;
- okButton#misc#set_sensitive false;
- ed#misc#set_sensitive false;
- sd#misc#set_sensitive false));
- ignore (lst#connect#select_row ~callback:(fun i _ _ ->
- (* Inserting the first row trigger the signal, even before the row
- data is set. So, we need to catch the corresponding exception *)
- try
- let (profile, roots) = lst#get_row_data i in
- selection := Some profile;
- begin match roots with
- [r1; r2] -> root1#set_text r1; root2#set_text r2;
- tbl#misc#set_sensitive true
- | _ -> root1#set_text ""; root2#set_text "";
- tbl#misc#set_sensitive false
- end;
- okButton#misc#set_sensitive true;
- ed#misc#set_sensitive true;
- sd#misc#set_sensitive true
- with Gpointer.Null -> ()));
- ignore (lst#event#connect#button_press ~callback:(fun ev ->
- match GdkEvent.get_type ev with
- `TWO_BUTTON_PRESS ->
- okCommand ();
- true
- | _ ->
- false));
- fillLst "default";
- lst#misc#grab_focus ();
- currentWindow := Some (t :> GWindow.window);
- ignore (t#event#connect#delete ~callback:(fun _ -> Main.quit (); true));
- t#show ()
-
-(**********************************************************************)
-(* Function to display a message in a new window *)
-(**********************************************************************)
-let messageBox ~title ?(label = "Dismiss") ?(action = fun t -> t#destroy)
- ?(modal = false) message =
- let t = GWindow.dialog ~title ~wm_name:title ~modal ~position:`CENTER () in
- let t_dismiss = GButton.button ~label ~packing:t#action_area#add () in
- t_dismiss#grab_default ();
- ignore (t_dismiss#connect#clicked ~callback:(action t));
- let charW = Gdk.Font.char_width (Lazy.force fontMonospaceMedium) 'M' in
- let charH = 16 in
- let t_text =
- new scrolled_text ~editable:false
- ~width:(charW * 80) ~height:(charH * 20) ~packing:t#vbox#add ()
- in
- t_text#insert message;
- ignore (t#event#connect#delete ~callback:(fun _ -> action t (); true));
- t#show ();
- if modal then begin
- grabFocus t;
- Main.main ();
- releaseFocus ()
- end
-
-(**********************************************************************)
-(* Fatal error handling *)
-(**********************************************************************)
-let fatalError =
- messageBox ~title:"Fatal Error" ~label:"Exit" ~modal:true
- ~action:(fun t () -> exit 1)
-
-
-(**********************************************************************)
-(* Toplevel window *)
-(**********************************************************************)
-let createToplevelWindow () =
- let toplevelWindow = GWindow.window ~wm_name:myName () in
- let toplevelVBox = GPack.vbox ~packing:toplevelWindow#add () in
-
- (**********************************************************************)
- (* Groups of same sensitivity *)
- (**********************************************************************)
- let grAction = ref [] in
- let grDiff = ref [] in
- let grProceed = ref [] in
- let grRestart = ref [] in
- let grAdd gr w = gr := w#misc::!gr in
- let grSet gr st = List.iter (fun x -> x#set_sensitive st) !gr in
-
- (**********************************************************************)
- (* Create the menu bar *)
- (**********************************************************************)
- let menuBar =
- GMenu.menu_bar ~border_width:2 ~packing:toplevelVBox#pack ()
- in
- let menus = new GMenu.factory ~accel_modi:[] menuBar in
- let accel_group = menus#accel_group in
- toplevelWindow#add_accel_group accel_group;
- let add_submenu ?(modi=[]) ~label () =
- new GMenu.factory ~accel_group ~accel_modi:modi (menus#add_submenu label)
- in
-
- (**********************************************************************)
- (* Create the menus *)
- (**********************************************************************)
- let fileMenu = add_submenu ~label:"Synchronization" ()
- and actionsMenu = add_submenu ~label:"Actions" ()
- and ignoreMenu = add_submenu ~modi:[`SHIFT] ~label:"Ignore" ()
- and helpMenu = add_submenu ~label:"Help" () in
-
- (**********************************************************************)
- (* Create the main window *)
- (**********************************************************************)
- let mainWindow =
- let sw =
- GBin.scrolled_window ~packing:(toplevelVBox#add)
- ~height:(Prefs.readPref mainWindowHeight * 12)
- ~hpolicy:`AUTOMATIC ~vpolicy:`AUTOMATIC ()
- in
- GList.clist
- ~columns:5 ~titles_show:true ~selection_mode:`BROWSE ~packing:sw#add ()
- in
- mainWindow#misc#grab_focus ();
- (* FIX: roots2string should return a pair *)
- let s = roots2string () in
- Array.iteri
- (fun i data ->
- mainWindow#set_column
- ~title_active:false ~auto_resize:true ~title:data i)
- [| " " ^ String.sub s ~pos:0 ~len:12 ^ " "; " Action ";
- " " ^ String.sub s ~pos:15 ~len:12 ^ " "; " Status "; " Path" |];
- let status_width =
- let font = mainWindow#misc#style#font in
- 4 + max (Gdk.Font.string_width font "working")
- (Gdk.Font.string_width font "skipped")
- in
- mainWindow#set_column ~justification:`CENTER 1;
- mainWindow#set_column
- ~justification:`CENTER ~auto_resize:false ~width:status_width 3;
-
- (**********************************************************************)
- (* Create the details window *)
- (**********************************************************************)
-
- let charW = Gdk.Font.char_width (Lazy.force fontMonospaceMedium) 'M' in
- let charH = if Sys.os_type = "Win32" then 20 else 16 in
-
- let detailsWindow =
- let sw =
- GBin.scrolled_window ~packing:(toplevelVBox#pack ~expand:false)
- ~hpolicy:`AUTOMATIC ~vpolicy:`AUTOMATIC ()
- in
- GEdit.text ~editable:false ~height:(3 * charH) ~width: (96 * charW)
- ~line_wrap:false ~packing:sw#add () in
- detailsWindow#misc#set_can_focus false;
- let style = detailsWindow#misc#style#copy in
- style#set_font (Lazy.force fontMonospaceMedium);
- detailsWindow#misc#set_style style;
- let updateButtons () =
- match !current with
- None ->
- grSet grAction false;
- grSet grDiff false
- | Some row ->
- let (activate1, activate2) =
- match !theState.(row).whatHappened, !theState.(row).ri.replicas with
- Some _, _ -> (false, false)
- | None, Different((FILE,_, _),(FILE,_, _),_) -> (true, true)
- | None, _ -> (true, false)
- in
- grSet grAction activate1;
- grSet grDiff activate2
- in
-
- let makeRowVisible row =
- if mainWindow#row_is_visible row <> `FULL then begin
- let adj = mainWindow#vadjustment in
- let current = adj#value
- and upper = adj#upper and lower = adj#lower in
- let v =
- float row /. float (mainWindow#rows + 1) *. (upper-.lower) +. lower
- in
- adj#set_value (min v (upper -. adj#page_size))
- end
- in
-
- let updateDetails () =
- detailsWindow#freeze ();
- detailsWindow#delete_text ~start:0 ~stop:detailsWindow#length;
- begin match !current with
- None ->
- ()
- | Some row ->
- makeRowVisible row;
- let details =
- match !theState.(row).whatHappened with
- None -> details2string !theState.(row).ri " "
- | Some(Succeeded(_)) -> details2string !theState.(row).ri " "
- | Some(Failed(s)) -> s in
- detailsWindow#insert (path2string !theState.(row).ri.path);
- detailsWindow#insert "\n";
- detailsWindow#insert details
- end;
- (* Display text *)
- detailsWindow#thaw ();
- updateButtons ()
- in
-
- (**********************************************************************)
- (* CREATE THE STATUS WINDOW *)
- (**********************************************************************)
-
- let statusWindow =
- GMisc.statusbar ~packing:toplevelVBox#pack () in
- let statusContext = statusWindow#new_context ~name:"status" in
- ignore (statusContext#push "");
-
- let displayStatus s1 s2 =
- Threads.do_on_main_thread (fun () ->
- (* Concatenate the new message *)
- let m =
- s1 ^ (String.make (max 2 (30 - String.length s1)) ' ') ^ s2 in
- statusContext#pop ();
- ignore (statusContext#push m);
- (* Force message to be displayed immediately *)
- gtk_sync ())
- in
-
- (* Tell the Trace module about the status printer *)
- Trace.statusPrinter := Some displayStatus;
-
- (**********************************************************************)
- (* FUNCTIONS USED TO PRINT IN THE MAIN WINDOW *)
- (**********************************************************************)
-
- let select i =
- let r = mainWindow#rows in
- let p = if r < 2 then 0. else (float i +. 0.5) /. float (r - 1) in
- mainWindow#scroll_vertical `JUMP (min p 1.)
- in
-
- ignore (mainWindow#connect#unselect_row ~callback:
- (fun ~row ~column ~event -> current := None; updateDetails ()));
- ignore (mainWindow#connect#select_row ~callback:
- (fun ~row ~column ~event -> current := Some row; updateDetails ()));
-
- let nextInteresting () =
- let l = Array.length !theState in
- let start = match !current with Some i -> i + 1 | None -> 0 in
- let rec loop i =
- if i < l then
- match !theState.(i).ri.replicas with
- Different (_, _, dir)
- when not (Prefs.readPref auto) || !dir = Conflict ->
- select i
- | _ ->
- loop (i + 1)
- in
- loop start
- in
- let selectSomethingIfPossible () =
- if !current=None then nextInteresting ()
- in
-
- let columnsOf i =
- let oldPath = if i = 0 then emptypath else !theState.(i-1).ri.path in
- let status =
- match !theState.(i).whatHappened with
- None -> " "
- | Some conf ->
- match !theState.(i).ri.replicas with
- Different(_,_,{contents=Conflict}) | Problem _ ->
- " "
- | _ ->
- match conf with
- Succeeded _ -> "done "
- | Failed _ -> "failed"
- in
- let s = reconItem2string oldPath !theState.(i).ri status in
- (* FIX: This is ugly *)
- (String.sub s ~pos:0 ~len:8,
- String.sub s ~pos:9 ~len:5,
- String.sub s ~pos:15 ~len:8,
- String.sub s ~pos:25 ~len:6,
- String.sub s ~pos:32 ~len:(String.length s - 32))
- in
-
- let rightArrow =
- GDraw.pixmap_from_xpm_d ~window:toplevelWindow ~data:Pixmaps.copyAB () in
- let leftArrow =
- GDraw.pixmap_from_xpm_d ~window:toplevelWindow ~data:Pixmaps.copyBA () in
- let rightArrowBlack =
- GDraw.pixmap_from_xpm_d
- ~window:toplevelWindow ~data:Pixmaps.copyABblack () in
- let leftArrowBlack =
- GDraw.pixmap_from_xpm_d
- ~window:toplevelWindow ~data:Pixmaps.copyBAblack () in
- let ignoreAct =
- GDraw.pixmap_from_xpm_d ~window:toplevelWindow ~data:Pixmaps.ignore () in
- let doneIcon =
- GDraw.pixmap_from_xpm_d ~window:toplevelWindow ~data:Pixmaps.success () in
- let failedIcon =
- GDraw.pixmap_from_xpm_d ~window:toplevelWindow ~data:Pixmaps.failure () in
-
- let displayArrow i action =
- match action with
- "<-?->" -> mainWindow#set_cell ~pixmap:ignoreAct i 1
- | "---->" -> mainWindow#set_cell ~pixmap:rightArrow i 1
- | "<----" -> mainWindow#set_cell ~pixmap:leftArrow i 1
- | "error" -> mainWindow#set_cell ~pixmap:failedIcon i 1
- | _ -> assert false
- in
-
- let displayStatusIcon i status =
- match status with
- | "failed" -> mainWindow#set_cell ~pixmap:failedIcon i 3
- | "done " -> mainWindow#set_cell ~pixmap:doneIcon i 3
- | _ -> mainWindow#set_cell ~text:status i 3
- in
-
- let displayMain() =
- mainWindow#freeze ();
- mainWindow#clear ();
- for i = 0 to Array.length !theState - 1 do
- let (r1, action, r2, status, path) = columnsOf i in
- ignore (mainWindow#append [ r1; ""; r2; status; path ]);
- displayArrow i action
- done;
- selectSomethingIfPossible ();
- begin match !current with Some idx -> select idx | None -> () end;
- mainWindow#thaw ();
- updateDetails ()
- in
-
- let redisplay i =
- let (r1, action, r2, status, path) = columnsOf i in
- mainWindow#freeze ();
- mainWindow#set_cell ~text:r1 i 0;
- displayArrow i action;
- mainWindow#set_cell ~text:r2 i 2;
- displayStatusIcon i status;
- mainWindow#set_cell ~text:path i 4;
- if status = " failed " then mainWindow#set_row ~foreground:(`NAME"red") i;
- mainWindow#thaw ();
- if !current = Some i then updateDetails ();
- updateButtons ()
- in
-
- let showProgress i bytes =
- !theState.(i).bytesTransferred <- !theState.(i).bytesTransferred + bytes;
- let b = !theState.(i).bytesTransferred in
- let len = Common.riLength !theState.(i).ri in
- let newstatus =
- if b=0 || len = 0 then "working "
- else if len = 0 then sprintf "%8d" b
- else
- let percentage = (int_of_float ((float b) *. 100.0 /. (float len))) in
- if percentage > 100 then
- debugprogress (fun() -> errmsg "Progress amount miscalculated for %s\n"
- (path2string (!theState.(i).ri.path)));
- sprintf " %3d%% " (max 100 percentage) in
- Threads.do_on_main_thread (fun () ->
- mainWindow#set_cell ~text:newstatus i 3;
- gtk_sync ())
- in
-
- (* Install showProgress so that we get called back by low-level
- file transfer stuff *)
- Util.progressPrinter := Some(showProgress);
-
- (* Apply new ignore patterns to the current state, expecting that the
- number of reconitems will grow smaller. Adjust the display, being
- careful to keep the cursor as near as possible to its position
- before the new ignore patterns take effect. *)
- let ignoreAndRedisplay () =
- let lst = Array.to_list !theState in
- (* FIX: we should actually test whether any prefix is now ignored *)
- let keep sI = not (Pred.test Globals.ignore (path2string sI.ri.path)) in
- begin match !current with
- None ->
- theState := Array.of_list (Safelist.filter keep lst)
- | Some index ->
- let i = ref index in
- let l = ref [] in
- Array.iteri
- (fun j sI -> if keep sI then l := sI::!l
- else if j < !i then decr i)
- !theState;
- current := if !l = [] then None else Some !i;
- theState := Array.of_list (Safelist.rev !l)
- end;
- displayMain();
- in
-
- (**********************************************************************)
- (* FUNCTION DETECT UPDATES *)
- (**********************************************************************)
-
- let detectUpdatesAndReconcile () =
- grSet grAction false;
- grSet grDiff false;
- grSet grProceed false;
- grSet grRestart false;
-
- let (r1,r2) = Globals.getReplicaRoots () in
- let t = Trace.startTimer "Checking for updates" in
- let findUpdates () =
- let updates = Update.findUpdates () in
- Trace.showTimer t;
- updates
- in
- let reconcile updates =
- let t = Trace.startTimer "Reconciling" in
- Recon.reconcileAll updates
- in
- let reconItemList = reconcile (findUpdates ()) in
- Trace.showTimer t;
- if reconItemList = [] then
- Trace.status "Everything is up to date"
- else
- Trace.status ("Check and/or adjust selected actions; "
- ^ "then press Proceed");
- theState :=
- Array.of_list
- (Safelist.map
- (fun ri -> { ri = ri; bytesTransferred = 0; whatHappened = None })
- reconItemList);
- current := None;
- displayMain();
- grSet grProceed (Array.length !theState > 0);
- grSet grRestart true
- in
-
- (**********************************************************************)
- (* The ignore dialog *)
- (**********************************************************************)
-
- let ignoreDialog () =
- let t = GWindow.dialog ~title: "Ignore" ~wm_name: "Ignore" () in
- let hbox = GPack.hbox ~packing:t#vbox#add () in
- let sb = GRange.scrollbar `VERTICAL
- ~packing:(hbox#pack ~from:`END) () in
- let regExpWindow =
- GList.clist ~columns:1 ~titles_show:false ~packing:hbox#add
- ~vadjustment:sb#adjustment ~width:400 ~height:150 () in
-
- (* Local copy of the regular expressions; the global copy will
- not be changed until the Apply button is pressed *)
- let theRegexps = Pred.extern Globals.ignore in
- Safelist.iter (fun r -> ignore (regExpWindow#append [r])) theRegexps;
- let maybeGettingBigger = ref false in
- let maybeGettingSmaller = ref false in
- let selectedRow = ref None in
- ignore
- (regExpWindow#connect#select_row ~callback:
- (fun ~row ~column ~event -> selectedRow := Some row));
- ignore
- (regExpWindow#connect#unselect_row ~callback:
- (fun ~row ~column ~event -> selectedRow := None));
-
- (* Configure the add frame *)
- let hbox = GPack.hbox ~spacing:4 ~packing:t#vbox#pack () in
- ignore (GMisc.label ~text: "Regular expression:"
- ~packing:(hbox#pack ~padding:2) ());
- let entry = GEdit.entry ~packing:hbox#add () in
- let add () =
- let theRegExp = entry#text in
- if theRegExp<>"" then begin
- entry#set_text "";
- regExpWindow#unselect_all ();
- ignore (regExpWindow#append [theRegExp]);
- maybeGettingSmaller := true
- end
- in
- let addButton = GButton.button ~label:"Add"
- ~packing:hbox#pack () in
- ignore (addButton#connect#clicked ~callback:add);
- ignore (entry#connect#activate ~callback:add);
- entry#misc#grab_focus ();
-
- (* Configure the delete button *)
- let delete () =
- match !selectedRow with
- Some x ->
- (* After a deletion, updates must be detected again *)
- maybeGettingBigger := true;
- (* Delete xth regexp *)
- regExpWindow#unselect_all ();
- regExpWindow#remove ~row:x
- | None ->
- ()
- in
- let deleteButton = GButton.button ~label:"Delete"
- ~packing:hbox#pack () in
- ignore (deleteButton#connect#clicked ~callback:delete);
-
- ignore
- (regExpWindow#event#connect#after#key_press ~callback:
- begin fun ev ->
- let key = GdkEvent.Key.keyval ev in
- if key = _Up || key = _Down || key = _Prior || key = _Next ||
- key = _Page_Up || key = _Page_Down then begin
- regExpWindow#select (regExpWindow#focus_row) 0;
- true
- end else if key = _Delete then begin
- delete (); true
- end else
- false
- end);
-
- (* A function to refresh the state and ignore list *)
- let refresh () =
- let theRegexps = ref [] in
- for i = regExpWindow#rows - 1 downto 0 do
- theRegexps := regExpWindow#cell_text i 0 :: !theRegexps
- done;
- Pred.intern Globals.ignore (!theRegexps);
- if !maybeGettingBigger || !maybeGettingSmaller then begin
- Globals.savePrefs();
- Globals.propagatePrefs()
- end;
- if !maybeGettingBigger then detectUpdatesAndReconcile ()
- else if !maybeGettingSmaller then ignoreAndRedisplay();
- maybeGettingBigger := false;
- maybeGettingSmaller := false;
- in
-
- (* Install the main buttons *)
- let applyButton =
- GButton.button ~label:"Apply" ~packing:t#action_area#add () in
- ignore (applyButton#connect#clicked ~callback:refresh);
- let cancelButton =
- GButton.button ~label:"Cancel" ~packing:t#action_area#add () in
- ignore (cancelButton#connect#clicked ~callback:(t#destroy));
- let okButton =
- GButton.button ~label:"OK" ~packing:t#action_area#add () in
- ignore
- (okButton#connect#clicked
- ~callback:(fun () -> refresh (); t#destroy ()));
- ignore (t#connect#destroy ~callback:Main.quit);
- grabFocus t;
- t#show ();
- Main.main ();
- releaseFocus ()
- in
-
- (**********************************************************************)
- (* Add entries to the Help menu *)
- (**********************************************************************)
- let addDocSection (shortname, (name, docstr)) =
- if shortname <> "" && name <> "" then
- ignore (helpMenu#add_item
- ~callback:(fun () -> documentation shortname)
- name)
- in
- Safelist.iter addDocSection Strings.docs;
-
- (**********************************************************************)
- (* Add entries to the Ignore menu *)
- (**********************************************************************)
- let addRegExpByPath pathfunc =
- match !current with
- Some i ->
- addIgnorePattern (pathfunc !theState.(i).ri.path);
- ignoreAndRedisplay ()
- | None ->
- ()
- in
- grAdd grAction
- (ignoreMenu#add_item ~key:_i
- ~callback:(fun () -> getLock (fun () -> addRegExpByPath ignorePath))
- "Ignore this file permanently");
- grAdd grAction
- (ignoreMenu#add_item ~key:_E
- ~callback:(fun () -> getLock (fun () -> addRegExpByPath ignoreExt))
- "Ignore files with this extension");
- grAdd grAction
- (ignoreMenu#add_item ~key:_N
- ~callback:(fun () -> getLock (fun () -> addRegExpByPath ignoreName))
- "Ignore files with this name");
-
-(*
- grAdd grRestart
- (ignoreMenu#add_item ~callback:
- (fun () -> getLock ignoreDialog) "Edit ignore patterns");
-*)
-
- (**********************************************************************)
- (* MAIN FUNCTION : SYNCHRONIZE *)
- (**********************************************************************)
- let synchronize () =
- if Array.length !theState = 0 then
- Trace.status "Nothing to synchronize"
- else begin
- grSet grAction false;
- grSet grDiff false;
- grSet grProceed false;
- grSet grRestart false;
-
- Trace.status "Propagating changes";
- let t = Trace.startTimer "Propagating changes" in
- let (start, wait) = Threads.thread_maker () in
- let background = let i = 55000 in `RGB (i, i, i) in
- let finish i =
- redisplay i;
- mainWindow#set_row ~background:`WHITE i;
- gtk_sync ()
- in
- for i = 0 to Array.length !theState - 1 do
- let theSI = !theState.(i) in
- assert (theSI.whatHappened = None);
- start
- (fun () ->
- Threads.do_on_main_thread (fun () ->
- mainWindow#set_row ~background i;
- makeRowVisible i);
- theSI.whatHappened <- Some (Transport.transportItem theSI.ri i);
- i)
- finish
- done;
- wait finish;
-
- Trace.showTimer t;
- Trace.status "Updating synchronizer state";
- let t = Trace.startTimer "Updating synchronizer state" in
- Update.commitUpdates();
- Trace.showTimer t;
- Trace.status "Synchronization complete";
-
- grSet grRestart true
- end
- in
-
- (**********************************************************************)
- (* CREATE THE ACTION BAR *)
- (**********************************************************************)
- let actionBar =
- GButton.toolbar
- ~orientation:`HORIZONTAL ~tooltips:true ~space_size:10
- ~packing:toplevelVBox#pack () in
-
- (**********************************************************************)
- (* CREATE AND CONFIGURE THE QUIT BUTTON *)
- (**********************************************************************)
- actionBar#insert_space ();
- ignore (actionBar#insert_button ~text:"Quit" ~callback:safeExit ());
-
- (**********************************************************************)
- (* CREATE AND CONFIGURE THE PROCEED BUTTON *)
- (**********************************************************************)
- actionBar#insert_space ();
- grAdd grProceed
- (actionBar#insert_button ~text:"Proceed"
- (* tooltip:"Proceed with displayed actions" *)
- ~callback:(fun () ->
- getLock synchronize) ());
-
- (**********************************************************************)
- (* CREATE AND CONFIGURE THE RESCAN BUTTON *)
- (**********************************************************************)
- let detectCmdName = "Restart" in
- let detectCmd () =
- getLock detectUpdatesAndReconcile;
- if Prefs.readPref batch then begin
- Prefs.setPref batch Prefs.TempSetting false; synchronize()
- end
- in
- actionBar#insert_space ();
- grAdd grRestart
- (actionBar#insert_button ~text:detectCmdName ~callback:detectCmd ());
-
- (**********************************************************************)
- (* Buttons for <--, -->, Skip *)
- (**********************************************************************)
- let doAction f =
- match !current with
- Some i ->
- let theSI = !theState.(i) in
- begin match theSI.whatHappened, theSI.ri.replicas with
- None, Different(_, _, dir) ->
- f dir;
- redisplay i;
- nextInteresting ()
- | _ ->
- ()
- end
- | None ->
- ()
- in
- let leftAction _ = doAction (fun dir -> dir := Replica2ToReplica1) in
- let rightAction _ = doAction (fun dir -> dir := Replica1ToReplica2) in
- let questionAction _ = doAction (fun dir -> dir := Conflict) in
-
- (**********************************************************************)
- (* CREATE AND CONFIGURE THE DIFF BUTTON and KEY *)
- (**********************************************************************)
- let diffCmd () =
- match !current with
- Some i ->
- getLock (fun () ->
- showDiffs !theState.(i).ri
- (fun title text -> messageBox ~title text)
- Trace.status i)
- | None ->
- ()
- in
-
- actionBar#insert_space ();
- grAdd grAction
- (actionBar#insert_button
- ~icon:((GMisc.pixmap leftArrowBlack ())#coerce)
- ~callback:leftAction ());
- actionBar#insert_space ();
- grAdd grAction
- (actionBar#insert_button
- ~icon:((GMisc.pixmap rightArrowBlack ())#coerce)
- ~callback:rightAction ());
- actionBar#insert_space ();
- grAdd grAction
- (actionBar#insert_button ~text:"Skip" ~callback:questionAction ());
- actionBar#insert_space ();
- grAdd grDiff (actionBar#insert_button ~text:"Diff" ~callback:diffCmd ());
-
- (**********************************************************************)
- (* Configure keyboard commands *)
- (**********************************************************************)
- ignore
- (mainWindow#event#connect#key_press ~callback:
- begin fun ev ->
- let key = GdkEvent.Key.keyval ev in
- if key = _Left then begin
- leftAction (); GtkSignal.stop_emit (); true
- end else if key = _Right then begin
- rightAction (); GtkSignal.stop_emit (); true
- end else
- false
- end);
-
- (**********************************************************************)
- (* Add entries to the Action menu *)
- (**********************************************************************)
- let (root1,root2) = Globals.getReplicaRoots () in
- let loc1 = root2hostname root1 in
- let loc2 = root2hostname root2 in
- let descr =
- if loc1 = loc2 then "left to right" else
- Printf.sprintf "from %s to %s" loc1 loc2
- in
- let left =
- actionsMenu#add_item ~key:_greater ~callback:rightAction
- ("Propagate " ^ descr) in
- grAdd grAction left;
- left#add_accelerator ~group:accel_group ~modi:[`SHIFT] _greater;
-
- let descl =
- if loc1 = loc2 then "right to left" else
- Printf.sprintf "from %s to %s" loc2 loc1
- in
- let right =
- actionsMenu#add_item ~key:_less ~callback:leftAction
- ("Propagate " ^ descl) in
- grAdd grAction right;
- right#add_accelerator ~group:accel_group ~modi:[`SHIFT] _less;
-
- grAdd grAction
- (actionsMenu#add_item ~key:_slash ~callback:questionAction
- "Do not propagate changes");
-
- ignore (actionsMenu#add_separator ());
- grAdd grDiff (actionsMenu#add_item ~key:_d ~callback:diffCmd "Show diffs");
-
- (**********************************************************************)
- (* Add commands to the Synchronization menu *)
- (**********************************************************************)
- grAdd grProceed
- (fileMenu#add_item ~key:_g
- ~callback:(fun () ->
- getLock synchronize)
- "Proceed");
- grAdd grRestart (fileMenu#add_item ~key:_r ~callback:detectCmd detectCmdName);
- grAdd grRestart
- (fileMenu#add_item ~key:_a
- ~callback:(fun () ->
- getLock detectUpdatesAndReconcile;
- getLock synchronize)
- "Atomically detect updates and proceed");
- ignore (fileMenu#add_separator ());
- let cm =
- fileMenu#add_check_item ~active:(Prefs.readPref Transport.backups)
- ~callback:(fun b -> Prefs.setPref Transport.backups Prefs.TempSetting b)
- "Make backups"
- in
- cm#set_show_toggle true;
- grAdd grRestart cm;
- ignore (fileMenu#add_separator ());
- ignore (fileMenu#add_item ~key:_q ~callback:safeExit "Quit");
-
- grSet grAction false;
- grSet grDiff false;
- grSet grProceed false;
- grSet grRestart false;
-
- ignore (toplevelWindow#event#connect#delete ~callback:
- (fun _ -> safeExit (); true));
- toplevelWindow#show ();
- currentWindow := Some toplevelWindow;
- detectCmd ()
-
-(**********************************************************************)
-(* Starting up... *)
-(**********************************************************************)
-let start _ =
- begin try
- (* Initialize the library *)
- ignore (Main.init ());
-
- Util.warnPrinter := Some (warnBox "Warning");
- (* Ask the Remote module to call us back at regular intervals during
- long network operations. *)
- Threads.tickProc := Some gtk_sync;
-
- (**********************************************************************)
- (* Set things up to initialize the client/server connection and *)
- (* detect updates after the ui is displayed. *)
- (* This makes a difference when the replicas are large and it takes *)
- (* a lot of time to detect updates. *)
- (**********************************************************************)
- let msg = ref None in
- Uicommon.uiInit
- profileSelect
- rootSelect
- (fun () ->
- let w =
- GWindow.window ~kind:`TOPLEVEL ~position:`CENTER
- ~wm_name:"Unison" ~border_width:16 () in
- ignore (GMisc.label ~text: "Contacting server..."
- ~packing:(w#add) ());
- w#show ();
- ignore (w#event#connect#delete ~callback:(fun _ -> exit 0));
- msg := Some w)
- (fun () ->
- begin match !msg with
- None -> ()
- | Some w -> w#destroy ()
- end;
- createToplevelWindow ());
-
- (**********************************************************************)
- (* Display the ui *)
- (**********************************************************************)
- ignore (Timeout.add 500 (fun _ -> true));
- (* Hack: this allows signals such as SIGINT to be
- handled even when Gtk is waiting for events *)
- Main.main ()
- with exn ->
- fatalError (exn2string exn)
- end
-
-end (* module Private *)
-
-(**********************************************************************)
-(* MODULE MAIN *)
-(**********************************************************************)
-
-module Body : Uicommon.UI = struct
-
-let start = function
- Text -> Uitext.Body.start Text
- | Graphic -> Private.start Graphic
-
-end (* module Body *)
-
-(*
-FIX:
-- Édition (minimale) et création des profiles
-- Profile par défaut
-- Sanity checks pour "Root selection"
-- Edition du filtrage
-*)