]> matita.cs.unibo.it Git - helm.git/blob - helm/matita/matitaGui.ml
cosmetic fix
[helm.git] / helm / matita / matitaGui.ml
1 (* Copyright (C) 2004-2005, HELM Team.
2  * 
3  * This file is part of HELM, an Hypertextual, Electronic
4  * Library of Mathematics, developed at the Computer Science
5  * Department, University of Bologna, Italy.
6  * 
7  * HELM is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  * 
12  * HELM is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with HELM; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
20  * MA  02111-1307, USA.
21  * 
22  * For details, see the HELM World-Wide-Web page,
23  * http://helm.cs.unibo.it/
24  *)
25
26 open Printf
27
28 open MatitaGeneratedGui
29 open MatitaGtkMisc
30 open MatitaMisc
31
32 let gui_instance = ref None ;;
33
34 class type browserWin =
35   (* this class exists only because GEdit.combo_box_entry is not supported by
36    * lablgladecc :-(((( *)
37 object
38   inherit MatitaGeneratedGui.browserWin
39   method browserUri: GEdit.combo_box_entry
40 end
41
42 class console ~(buffer: GText.buffer) () =
43   object (self)
44     val error_tag   = buffer#create_tag [ `FOREGROUND "red" ]
45     val warning_tag = buffer#create_tag [ `FOREGROUND "yellow" ]
46     val message_tag = buffer#create_tag []
47     val debug_tag   = buffer#create_tag [ `FOREGROUND "#888888" ]
48     method message s = buffer#insert ~iter:buffer#end_iter ~tags:[message_tag] s
49     method error s   = buffer#insert ~iter:buffer#end_iter ~tags:[error_tag] s
50     method warning s = buffer#insert ~iter:buffer#end_iter ~tags:[warning_tag] s
51     method debug s   = buffer#insert ~iter:buffer#end_iter ~tags:[debug_tag] s
52     method clear () =
53       buffer#delete ~start:buffer#start_iter ~stop:buffer#end_iter
54     method log_callback (tag: MatitaLog.log_tag) s =
55       match tag with
56       | `Debug -> self#debug (s ^ "\n")
57       | `Error -> self#error (s ^ "\n")
58       | `Message -> self#message (s ^ "\n")
59       | `Warning -> self#warning (s ^ "\n")
60   end
61
62 class gui () =
63     (* creation order _is_ relevant for windows placement *)
64   let main = new mainWin () in
65   let about = new aboutWin () in
66   let fileSel = new fileSelectionWin () in
67   let keyBindingBoxes = (* event boxes which should receive global key events *)
68     [ main#mainWinEventBox ]
69   in
70   let console = new console ~buffer:main#logTextView#buffer () in
71   object (self)
72     val mutable chosen_file = None
73     val mutable _ok_not_exists = false
74     val mutable script_fname = None 
75    
76     initializer
77         (* glade's check widgets *)
78       List.iter (fun w -> w#check_widgets ())
79         (let c w = (w :> <check_widgets: unit -> unit>) in
80          [ c about; c fileSel; c main ]);
81         (* key bindings *)
82       List.iter (* global key bindings *)
83         (fun (key, callback) -> self#addKeyBinding key callback)
84 (*
85         [ GdkKeysyms._F3,
86             toggle_win ~check:main#showProofMenuItem proof#proofWin;
87           GdkKeysyms._F4,
88             toggle_win ~check:main#showCheckMenuItem check#checkWin;
89 *)
90         [ ];
91         (* about win *)
92       ignore (about#aboutWin#event#connect#delete (fun _ -> true));
93       ignore (main#aboutMenuItem#connect#activate (fun _ ->
94         about#aboutWin#show ()));
95       connect_button about#aboutDismissButton (fun _ ->
96         about#aboutWin#misc#hide ());
97       about#aboutLabel#set_label (Pcre.replace ~pat:"@VERSION@"
98         ~templ:BuildTimeConf.version about#aboutLabel#label);
99         (* file selection win *)
100       ignore (fileSel#fileSelectionWin#event#connect#delete (fun _ -> true));
101       ignore (fileSel#fileSelectionWin#connect#response (fun event ->
102         let return r =
103           chosen_file <- r;
104           fileSel#fileSelectionWin#misc#hide ();
105           GMain.Main.quit ()
106         in
107         match event with
108         | `OK ->
109             let fname = fileSel#fileSelectionWin#filename in
110             if Sys.file_exists fname then
111               (if is_regular fname then return (Some fname))
112             else
113               (if _ok_not_exists then return (Some fname))
114         | `CANCEL -> return None
115         | `HELP -> ()
116         | `DELETE_EVENT -> return None));
117         (* menus *)
118       List.iter (fun w -> w#misc#set_sensitive false) [ main#saveMenuItem ];
119       main#helpMenu#set_right_justified true;
120         (* console *)
121       let adj = main#logScrolledWin#vadjustment in
122       ignore (adj#connect#changed
123                 (fun _ -> adj#set_value (adj#upper -. adj#page_size)));
124       console#message (sprintf "\tMatita version %s\n" BuildTimeConf.version);
125         (* toolbar *)
126       let module A = TacticAst in
127       let hole = CicAst.UserInput in
128       let loc = CicAst.dummy_floc in
129       let tac ast _ =
130         if (MatitaScript.instance ())#onGoingProof () then
131           (MatitaScript.instance ())#advance
132             ~statement:("\n" ^ TacticAstPp.pp_tactical (A.Tactic (loc, ast))) ()
133       in
134       let tac_w_term ast _ =
135         if (MatitaScript.instance ())#onGoingProof () then
136           let (buf: GText.buffer) = self#main#scriptTextView#buffer in
137           buf#insert ~iter:(buf#get_iter_at_mark (`NAME "locked"))
138             ("\n" ^ TacticAstPp.pp_tactic ast)
139       in
140       let tbar = self#main in
141       connect_button tbar#introsButton (tac (A.Intros (loc, None, [])));
142       connect_button tbar#applyButton (tac_w_term (A.Apply (loc, hole)));
143       connect_button tbar#exactButton (tac_w_term (A.Exact (loc, hole)));
144       connect_button tbar#elimButton (tac_w_term (A.Elim (loc, hole, None)));
145       connect_button tbar#elimTypeButton (tac_w_term (A.ElimType (loc, hole)));
146       connect_button tbar#splitButton (tac (A.Split loc));
147       connect_button tbar#leftButton (tac (A.Left loc));
148       connect_button tbar#rightButton (tac (A.Right loc));
149       connect_button tbar#existsButton (tac (A.Exists loc));
150       connect_button tbar#reflexivityButton (tac (A.Reflexivity loc));
151       connect_button tbar#symmetryButton (tac (A.Symmetry loc));
152       connect_button tbar#transitivityButton
153         (tac_w_term (A.Transitivity (loc, hole)));
154       connect_button tbar#assumptionButton (tac (A.Assumption loc));
155       connect_button tbar#cutButton (tac_w_term (A.Cut (loc, hole)));
156       connect_button tbar#autoButton (tac (A.Auto (loc,None)));
157       MatitaGtkMisc.toggle_widget_visibility
158        ~widget:(self#main#tacticsButtonsHandlebox :> GObj.widget)
159        ~check:self#main#tacticsBarMenuItem;
160       let module Hr = Helm_registry in
161       if not(Hr.get_opt_default Hr.get_bool false "matita.tactics_bar") then 
162         self#main#tacticsBarMenuItem#set_active false;
163         (* quit *)
164       self#setQuitCallback (fun () -> exit 0);
165         (* log *)
166       MatitaLog.set_log_callback self#console#log_callback;
167       GtkSignal.user_handler :=
168         (fun exn ->
169            MatitaLog.error
170              (sprintf "Uncaught exception: %s" (Printexc.to_string exn)));
171         (* script *)
172       let s () = MatitaScript.instance () in
173       let disableSave () =
174         script_fname <- None;
175         self#main#saveMenuItem#misc#set_sensitive false
176       in
177       let loadScript () =
178         let script = s () in
179         match self#chooseFile () with
180         | Some f -> 
181               script#reset (); 
182               script#loadFrom f; 
183               console#message ("'"^f^"' loaded.\n");
184               self#_enableSaveTo f
185         | None -> ()
186       in
187       let saveAsScript () =
188         let script = s () in
189         match self#chooseFile ~ok_not_exists:true () with
190         | Some f -> 
191               script#saveTo f; 
192               console#message ("'"^f^"' saved.\n");
193               self#_enableSaveTo f
194         | None -> ()
195       in
196       let saveScript () =
197         match script_fname with
198         | None -> saveAsScript ()
199         | Some f -> 
200               (s ())#saveTo f;
201               console#message ("'"^f^"' saved.\n");
202       in
203       let newScript () = (s ())#reset (); disableSave () in
204       let cursor () =
205         let buf = self#main#scriptTextView#buffer in
206         buf#place_cursor (buf#get_iter_at_mark (`NAME "locked"))
207       in
208       let advance _ = (MatitaScript.instance ())#advance (); cursor () in
209       let retract _ = (MatitaScript.instance ())#retract (); cursor () in
210       let top _ = (MatitaScript.instance ())#goto `Top (); cursor () in
211       let bottom _ = (MatitaScript.instance ())#goto `Bottom (); cursor () in
212       let jump _ = (MatitaScript.instance ())#goto `Cursor (); cursor () in
213       let connect_key sym f =
214         connect_key self#main#mainWinEventBox#event
215           ~modifiers:[`CONTROL] ~stop:true sym f;
216         connect_key self#main#scriptTextView#event
217           ~modifiers:[`CONTROL] ~stop:true sym f
218       in
219       connect_button self#main#scriptAdvanceButton advance;
220       connect_button self#main#scriptRetractButton retract;
221       connect_button self#main#scriptTopButton top;
222       connect_button self#main#scriptBottomButton bottom;
223       connect_key GdkKeysyms._Down advance;
224       connect_key GdkKeysyms._Up retract;
225       connect_key GdkKeysyms._Home top;
226       connect_key GdkKeysyms._End bottom;
227       connect_button self#main#scriptJumpButton jump;
228       connect_menu_item self#main#openMenuItem   loadScript;
229       connect_menu_item self#main#saveMenuItem   saveScript;
230       connect_menu_item self#main#saveAsMenuItem saveAsScript;
231       connect_menu_item self#main#newMenuItem    newScript;
232       connect_key GdkKeysyms._period
233         (fun () ->
234            let buf = self#main#scriptTextView#buffer in
235            buf#insert ~iter:(buf#get_iter_at_mark `INSERT) ".\n";
236            advance ());
237       connect_key GdkKeysyms._Return
238         (fun () ->
239            let buf = self#main#scriptTextView#buffer in
240            buf#insert ~iter:(buf#get_iter_at_mark `INSERT) "\n";
241            advance ());
242          (* script monospace font stuff *)  
243       let font =
244         Helm_registry.get_opt_default Helm_registry.get
245           BuildTimeConf.default_script_font "matita.script_font"
246       in
247 (*       let monospace_tag = 
248         self#main#scriptTextView#buffer#create_tag [`FONT_DESC font] 
249       in *)
250       self#main#scriptTextView#misc#modify_font_by_name font;
251 (*       let _ = 
252         self#main#scriptTextView#buffer#connect#changed ~callback:(fun _ ->
253           let start, stop = self#main#scriptTextView#buffer#bounds in
254           self#main#scriptTextView#buffer#apply_tag monospace_tag start stop)
255       in *)
256         (* debug menu *)
257       self#main#debugMenu#misc#hide ();
258         (* status bar *)
259       self#main#hintLowImage#set_file (image_path "matita-bulb-low.png");
260       self#main#hintMediumImage#set_file (image_path "matita-bulb-medium.png");
261       self#main#hintHighImage#set_file (image_path "matita-bulb-high.png");
262         (* focus *)
263       self#main#scriptTextView#misc#grab_focus ();
264         (* main win dimension *)
265       let width = Gdk.Screen.width () in
266       let height = Gdk.Screen.height () in
267       let main_w = width * 90 / 100 in 
268       let main_h = height * 80 / 100 in
269       let script_w = main_w * 6 / 10 in
270       self#main#toplevel#resize ~width:main_w ~height:main_h;
271       self#main#hpaneScriptSequent#set_position script_w  
272     
273     method loadScript file =       
274       let script = MatitaScript.instance () in
275       script#reset (); 
276       script#loadFrom file; 
277       console#message ("'"^file^"' loaded.");
278       self#_enableSaveTo file
279         
280     method private _enableSaveTo file =
281       script_fname <- Some file;
282       self#main#saveMenuItem#misc#set_sensitive true
283         
284
285     method console = console
286
287     method about = about
288     method fileSel = fileSel
289     method main = main
290
291     method newBrowserWin () =
292       object (self)
293         inherit browserWin ()
294         val combo = GEdit.combo_box_entry ()
295         initializer
296           self#check_widgets ();
297           let combo_widget = combo#coerce in
298           uriHBox#pack ~from:`END ~fill:true ~expand:true combo_widget;
299           combo#entry#misc#grab_focus ()
300         method browserUri = combo
301       end
302
303     method newUriDialog () =
304       let dialog = new uriChoiceDialog () in
305       dialog#check_widgets ();
306       dialog
307
308     method newInterpDialog () =
309       let dialog = new interpChoiceDialog () in
310       dialog#check_widgets ();
311       dialog
312
313     method newConfirmationDialog () =
314       let dialog = new confirmationDialog () in
315       dialog#check_widgets ();
316       dialog
317
318     method newEmptyDialog () =
319       let dialog = new emptyDialog () in
320       dialog#check_widgets ();
321       dialog
322
323     method private addKeyBinding key callback =
324       List.iter (fun evbox -> add_key_binding key callback evbox)
325         keyBindingBoxes
326
327     method setQuitCallback callback =
328       ignore (main#toplevel#connect#destroy callback);
329       ignore (main#quitMenuItem#connect#activate callback);
330       self#addKeyBinding GdkKeysyms._q callback
331
332     method chooseFile ?(ok_not_exists = false) () =
333       _ok_not_exists <- ok_not_exists;
334       fileSel#fileSelectionWin#show ();
335       GtkThread.main ();
336       chosen_file
337
338     method askText ?(title = "") ?(msg = "") () =
339       let dialog = new textDialog () in
340       dialog#textDialog#set_title title;
341       dialog#textDialogLabel#set_label msg;
342       let text = ref None in
343       let return v =
344         text := v;
345         dialog#textDialog#destroy ();
346         GMain.Main.quit ()
347       in
348       ignore (dialog#textDialog#event#connect#delete (fun _ -> true));
349       connect_button dialog#textDialogCancelButton (fun _ -> return None);
350       connect_button dialog#textDialogOkButton (fun _ ->
351         let text = dialog#textDialogTextView#buffer#get_text () in
352         return (Some text));
353       dialog#textDialog#show ();
354       GtkThread.main ();
355       !text
356
357   end
358
359 let gui () = 
360   let g = new gui () in
361   gui_instance := Some g;
362   g
363   
364 let instance = singleton gui
365
366 let non p x = not (p x)
367
368 let is_var_uri s =
369   try
370     String.sub s (String.length s - 4) 4 = ".var"
371   with Invalid_argument _ -> false
372
373 (* this is a shit and should be changed :-{ *)
374 let interactive_uri_choice
375   ?(selection_mode:[`SINGLE|`MULTIPLE] = `MULTIPLE) ?(title = "")
376   ?(msg = "") ?(nonvars_button = false) ?(hide_uri_entry=false) 
377   ?(hide_try=false) ?(ok_label="_Auto") ?(ok_action:[`SELECT|`AUTO] = `AUTO) 
378   ?copy_cb ()
379   ~id uris
380 =
381   let gui = instance () in
382   let nonvars_uris = lazy (List.filter (non is_var_uri) uris) in
383   if (selection_mode <> `SINGLE) &&
384     (Helm_registry.get_bool "matita.auto_disambiguation")
385   then
386     Lazy.force nonvars_uris
387   else begin
388     let dialog = gui#newUriDialog () in
389     if hide_uri_entry then
390       dialog#uriEntryHBox#misc#hide ();
391     if hide_try then
392       begin
393       dialog#uriChoiceSelectedButton#misc#hide ();
394       dialog#uriChoiceConstantsButton#misc#hide ();
395       end;
396     dialog#okLabel#set_label ok_label;  
397     dialog#uriChoiceTreeView#selection#set_mode
398       (selection_mode :> Gtk.Tags.selection_mode);
399     let model = new stringListModel dialog#uriChoiceTreeView in
400     let choices = ref None in
401     let nonvars = ref false in
402     (match copy_cb with
403     | None -> ()
404     | Some cb ->
405         dialog#copyButton#misc#show ();
406         connect_button dialog#copyButton 
407         (fun _ ->
408           match model#easy_selection () with
409           | [u] -> (cb u)
410           | _ -> ()));
411     dialog#uriChoiceDialog#set_title title;
412     dialog#uriChoiceLabel#set_text msg;
413     List.iter model#easy_append uris;
414     dialog#uriChoiceConstantsButton#misc#set_sensitive nonvars_button;
415     let return v =
416       choices := v;
417       dialog#uriChoiceDialog#destroy ();
418       GMain.Main.quit ()
419     in
420     ignore (dialog#uriChoiceDialog#event#connect#delete (fun _ -> true));
421     connect_button dialog#uriChoiceConstantsButton (fun _ ->
422       return (Some (Lazy.force nonvars_uris)));
423     if ok_action = `AUTO then
424       connect_button dialog#uriChoiceAutoButton (fun _ ->
425         Helm_registry.set_bool "matita.auto_disambiguation" true;
426         return (Some (Lazy.force nonvars_uris)))
427     else
428       connect_button dialog#uriChoiceAutoButton (fun _ ->
429         match model#easy_selection () with
430         | [] -> ()
431         | uris -> return (Some uris));
432     connect_button dialog#uriChoiceSelectedButton (fun _ ->
433       match model#easy_selection () with
434       | [] -> ()
435       | uris -> return (Some uris));
436     connect_button dialog#uriChoiceAbortButton (fun _ -> return None);
437     dialog#uriChoiceDialog#show ();
438     GtkThread.main ();
439     (match !choices with 
440     | None -> raise MatitaTypes.Cancel
441     | Some uris -> uris)
442   end
443
444 class interpModel =
445   let cols = new GTree.column_list in
446   let id_col = cols#add Gobject.Data.string in
447   let dsc_col = cols#add Gobject.Data.string in
448   let interp_no_col = cols#add Gobject.Data.int in
449   let tree_store = GTree.tree_store cols in
450   let id_renderer = GTree.cell_renderer_text [], ["text", id_col] in
451   let dsc_renderer = GTree.cell_renderer_text [], ["text", dsc_col] in
452   let id_view_col = GTree.view_column ~renderer:id_renderer () in
453   let dsc_view_col = GTree.view_column ~renderer:dsc_renderer () in
454   fun tree_view choices ->
455     object
456       initializer
457         tree_view#set_model (Some (tree_store :> GTree.model));
458         ignore (tree_view#append_column id_view_col);
459         ignore (tree_view#append_column dsc_view_col);
460         let name_of_interp =
461           (* try to find a reasonable name for an interpretation *)
462           let idx = ref 0 in
463           fun interp ->
464             try
465               List.assoc "0" interp
466             with Not_found ->
467               incr idx; string_of_int !idx
468         in
469         tree_store#clear ();
470         let idx = ref ~-1 in
471         List.iter
472           (fun interp ->
473             incr idx;
474             let interp_row = tree_store#append () in
475             tree_store#set ~row:interp_row ~column:id_col
476               (name_of_interp interp);
477             tree_store#set ~row:interp_row ~column:interp_no_col !idx;
478             List.iter
479               (fun (id, dsc) ->
480                 let row = tree_store#append ~parent:interp_row () in
481                 tree_store#set ~row ~column:id_col id;
482                 tree_store#set ~row ~column:dsc_col dsc;
483                 tree_store#set ~row ~column:interp_no_col !idx)
484               interp)
485           choices
486
487       method get_interp_no tree_path =
488         let iter = tree_store#get_iter tree_path in
489         tree_store#get ~row:iter ~column:interp_no_col
490     end
491
492 let interactive_interp_choice () choices =
493   let gui = instance () in
494   assert (choices <> []);
495   let dialog = gui#newInterpDialog () in
496   let model = new interpModel dialog#interpChoiceTreeView choices in
497   let interp_len = List.length (List.hd choices) in
498   dialog#interpChoiceDialog#set_title "Interpretation choice";
499   dialog#interpChoiceDialogLabel#set_label "Choose an interpretation:";
500   let interp_no = ref None in
501   let return _ =
502     dialog#interpChoiceDialog#destroy ();
503     GMain.Main.quit ()
504   in
505   let fail _ = interp_no := None; return () in
506   ignore (dialog#interpChoiceDialog#event#connect#delete (fun _ -> true));
507   connect_button dialog#interpChoiceOkButton (fun _ ->
508     match !interp_no with None -> () | Some _ -> return ());
509   connect_button dialog#interpChoiceCancelButton fail;
510   ignore (dialog#interpChoiceTreeView#connect#row_activated (fun path _ ->
511     interp_no := Some (model#get_interp_no path);
512     return ()));
513   let selection = dialog#interpChoiceTreeView#selection in
514   ignore (selection#connect#changed (fun _ ->
515     match selection#get_selected_rows with
516     | [path] ->
517         MatitaLog.debug (sprintf "selection: %d" (model#get_interp_no path));
518         interp_no := Some (model#get_interp_no path)
519     | _ -> assert false));
520   dialog#interpChoiceDialog#show ();
521   GtkThread.main ();
522   (match !interp_no with Some row -> [row] | _ -> raise MatitaTypes.Cancel)
523