]> matita.cs.unibo.it Git - helm.git/blob - helm/matita/matitaGui.ml
Bug fix: undo now respects the locked area.
[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 "orange" ]
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 let clean_current_baseuri status = 
63     try  
64       let baseuri = MatitaTypes.get_string_option status "baseuri" in
65       MatitacleanLib.clean_baseuris [baseuri]
66     with MatitaTypes.Option_error _ -> ()
67
68 let ask_and_save_moo_if_needed parent fname status = 
69   let save () =
70     MatitacLib.dump_moo_to_file fname status.MatitaTypes.moo_content_rev in
71   if (MatitaScript.instance ())#eos &&
72      status.MatitaTypes.proof_status = MatitaTypes.No_proof
73   then
74     begin
75       let mooname = 
76         MatitaMisc.obj_file_of_script fname
77       in
78       let rc = 
79         MatitaGtkMisc.ask_confirmation
80         ~title:"A .moo can be generated"
81         ~message:(Printf.sprintf 
82           "%s can be generated for %s.\n<i>Should I generate it?</i>"
83           mooname fname)
84         ~parent ()
85       in
86       let b = 
87         match rc with 
88         | `YES -> true 
89         | `NO -> false 
90         | `CANCEL -> raise MatitaTypes.Cancel 
91       in
92       if b then
93         save ()
94       else
95         clean_current_baseuri status
96     end
97   else
98     clean_current_baseuri status 
99     
100 let ask_unsaved parent =
101   MatitaGtkMisc.ask_confirmation 
102     ~parent ~title:"Unsaved work!" 
103     ~message:("Your work is <b>unsaved</b>!\n\n"^
104          "<i>Do you want to save the script before exiting?</i>")
105     ()
106
107 class gui () =
108     (* creation order _is_ relevant for windows placement *)
109   let main = new mainWin () in
110   let about = new aboutWin () in
111   let fileSel = new fileSelectionWin () in
112   let findRepl = new findReplWin () in
113   let develList = new develListWin () in
114   let newDevel = new newDevelopmentWin () in
115   let keyBindingBoxes = (* event boxes which should receive global key events *)
116     [ main#mainWinEventBox ]
117   in
118   let console = new console ~buffer:main#logTextView#buffer () in
119   let (source_view: GSourceView.source_view) =
120     GSourceView.source_view
121       ~auto_indent:true
122       ~insert_spaces_instead_of_tabs:true ~tabs_width:2
123       ~margin:80 ~show_margin:true
124       ~smart_home_end:true
125       ~packing:main#scriptScrolledWin#add
126       ()
127   in
128   let default_font_size =
129     Helm_registry.get_opt_default Helm_registry.int
130       ~default:BuildTimeConf.default_font_size "matita.font_size"
131   in
132   let source_buffer = source_view#source_buffer in
133   object (self)
134     val mutable chosen_file = None
135     val mutable _ok_not_exists = false
136     val mutable _only_directory = false
137     val mutable script_fname = None
138     val mutable font_size = default_font_size
139     val mutable next_devel_must_contain = None
140    
141     initializer
142         (* glade's check widgets *)
143       List.iter (fun w -> w#check_widgets ())
144         (let c w = (w :> <check_widgets: unit -> unit>) in
145         [ c about; c fileSel; c main; c findRepl]);
146         (* key bindings *)
147       List.iter (* global key bindings *)
148         (fun (key, callback) -> self#addKeyBinding key callback)
149 (*
150         [ GdkKeysyms._F3,
151             toggle_win ~check:main#showProofMenuItem proof#proofWin;
152           GdkKeysyms._F4,
153             toggle_win ~check:main#showCheckMenuItem check#checkWin;
154 *)
155         [ ];
156         (* about win *)
157       ignore (about#aboutWin#event#connect#delete (fun _ -> true));
158       ignore (main#aboutMenuItem#connect#activate (fun _ ->
159         about#aboutWin#show ()));
160       connect_button about#aboutDismissButton (fun _ ->
161         about#aboutWin#misc#hide ());
162       about#aboutLabel#set_label (Pcre.replace ~pat:"@VERSION@"
163         ~templ:BuildTimeConf.version about#aboutLabel#label);
164         (* findRepl win *)
165       let show_find_Repl () = 
166         findRepl#toplevel#misc#show ();
167         findRepl#toplevel#misc#grab_focus ()
168       in
169       let hide_find_Repl () = findRepl#toplevel#misc#hide () in
170       let find_forward _ = 
171           let highlight start end_ =
172             source_buffer#move_mark `INSERT ~where:start;
173             source_buffer#move_mark `SEL_BOUND ~where:end_;
174             source_view#scroll_mark_onscreen `INSERT
175           in
176           let text = findRepl#findEntry#text in
177           let iter = source_buffer#get_iter `SEL_BOUND in
178           match iter#forward_search text with
179           | None -> 
180               (match source_buffer#start_iter#forward_search text with
181               | None -> ()
182               | Some (start,end_) -> highlight start end_)
183           | Some (start,end_) -> highlight start end_ 
184       in
185       let replace _ =
186         let text = findRepl#replaceEntry#text in
187         let ins = source_buffer#get_iter `INSERT in
188         let sel = source_buffer#get_iter `SEL_BOUND in
189         if ins#compare sel < 0 then 
190           begin
191             ignore(source_buffer#delete_selection ());
192             source_buffer#insert text
193           end
194       in
195       connect_button findRepl#findButton find_forward;
196       connect_button findRepl#findReplButton replace;
197       connect_button findRepl#cancelButton (fun _ -> hide_find_Repl ());
198       ignore(findRepl#toplevel#event#connect#delete 
199         ~callback:(fun _ -> hide_find_Repl ();true));
200       ignore(self#main#undoMenuItem#connect#activate
201         ~callback:(fun () ->
202           (* phase 1: we save the actual status of the marks and we undo *)
203           let locked_mark = `MARK ((MatitaScript.instance ())#locked_mark) in
204           let locked_iter = source_view#buffer#get_iter_at_mark locked_mark in
205           let locked_iter_offset = locked_iter#offset in
206           let mark2 =
207            `MARK
208              (source_view#buffer#create_mark ~name:"lock_point"
209                ~left_gravity:true locked_iter) in
210           source_view#source_buffer#undo ();
211           (* phase 2: we save the cursor position and we redo, restoring
212              the previous status of all the marks *)
213           let cursor_iter = source_view#buffer#get_iter_at_mark `INSERT in
214           let mark =
215            `MARK
216              (source_view#buffer#create_mark ~name:"undo_point"
217                ~left_gravity:true cursor_iter)
218           in
219            source_view#source_buffer#redo ();
220            let mark_iter = source_view#buffer#get_iter_at_mark mark in
221            let mark2_iter = source_view#buffer#get_iter_at_mark mark2 in
222            let mark2_iter = mark2_iter#set_offset locked_iter_offset in
223             source_view#buffer#move_mark locked_mark ~where:mark2_iter;
224             source_view#buffer#delete_mark mark;
225             source_view#buffer#delete_mark mark2;
226             (* phase 3: if after the undo the cursor was in the locked area,
227                then we move it there again and we perform a goto *)
228             if mark_iter#offset < locked_iter_offset then
229              begin
230               source_view#buffer#move_mark `INSERT ~where:mark_iter;
231               source_view#buffer#move_mark `SEL_BOUND ~where:mark_iter;
232               (MatitaScript.instance ())#goto `Cursor ();
233              end;
234             (* phase 4: we perform again the undo. This time we are sure that
235                the text to undo is not locked *)
236             source_view#source_buffer#undo ();
237             source_view#misc#grab_focus ()
238          ));
239       ignore(source_view#source_buffer#connect#can_undo
240         ~callback:self#main#undoMenuItem#misc#set_sensitive);
241       ignore(self#main#redoMenuItem#connect#activate
242         ~callback:source_view#source_buffer#redo);
243       ignore(source_view#source_buffer#connect#can_redo
244         ~callback:self#main#redoMenuItem#misc#set_sensitive);
245       let clipboard =
246        let atom = Gdk.Atom.clipboard in
247         GData.clipboard atom in
248       ignore(self#main#cutMenuItem#connect#activate
249         ~callback:(fun () -> source_view#buffer#cut_clipboard clipboard));
250       ignore(self#main#copyMenuItem#connect#activate
251         ~callback:(fun () -> source_view#buffer#copy_clipboard clipboard));
252       ignore(self#main#pasteMenuItem#connect#activate
253         ~callback:(fun () ->
254           source_view#buffer#paste_clipboard clipboard;
255           (MatitaScript.instance ())#clean_dirty_lock));
256       ignore(self#main#deleteMenuItem#connect#activate
257         ~callback:(fun () -> ignore (source_view#buffer#delete_selection ())));
258       ignore(self#main#findReplMenuItem#connect#activate
259         ~callback:show_find_Repl);
260       ignore (findRepl#findEntry#connect#activate ~callback:find_forward);
261         (* developments win *)
262       let model = 
263         new MatitaGtkMisc.multiStringListModel 
264           ~cols:2 develList#developmentsTreeview
265       in
266       let refresh_devels_win () =
267         model#list_store#clear ();
268         List.iter 
269           (fun (name, root) -> model#easy_mappend [name;root]) 
270           (MatitamakeLib.list_known_developments ())
271       in
272       let get_devel_selected () = 
273         match model#easy_mselection () with
274         | [[name;_]] -> MatitamakeLib.development_for_name name
275         | _ -> assert false 
276       in
277       connect_button develList#newButton
278         (fun () -> 
279           next_devel_must_contain <- None;
280           newDevel#toplevel#misc#show());
281       connect_button develList#deleteButton
282         (fun () -> 
283           (match get_devel_selected () with
284           | None -> ()
285           | Some d -> MatitamakeLib.destroy_development d);
286           refresh_devels_win ());
287       let refresh () = 
288         while Glib.Main.pending () do 
289           ignore(Glib.Main.iteration false); 
290         done
291       in
292       connect_button develList#buildButton 
293         (fun () -> 
294           match get_devel_selected () with
295           | None -> ()
296           | Some d -> ignore(MatitamakeLib.build_development_in_bg refresh d));
297       connect_button develList#cleanButton 
298         (fun () -> 
299           match get_devel_selected () with
300           | None -> ()
301           | Some d -> ignore(MatitamakeLib.clean_development_in_bg refresh d));
302       connect_button develList#closeButton 
303         (fun () -> develList#toplevel#misc#hide());
304       ignore(develList#toplevel#event#connect#delete 
305         (fun _ -> develList#toplevel#misc#hide();true));
306       let selected_devel = ref None in
307       connect_menu_item self#main#developmentsMenuItem
308         (fun () -> refresh_devels_win ();develList#toplevel#misc#show ());
309       
310         (* add development win *)
311       let check_if_root_contains root =
312         match next_devel_must_contain with
313         | None -> true
314         | Some path -> 
315             let is_prefix_of d1 d2 =
316               let len1 = String.length d1 in
317               let len2 = String.length d2 in
318               if len2 < len1 then 
319                 false
320               else
321                 let pref = String.sub d2 0 len1 in
322                 pref = d1
323             in
324             is_prefix_of root path
325       in
326       connect_button newDevel#addButton 
327        (fun () -> 
328           let name = newDevel#nameEntry#text in
329           let root = newDevel#rootEntry#text in
330           if check_if_root_contains root then
331             begin
332               ignore (MatitamakeLib.initialize_development name root);
333               refresh_devels_win ();
334               newDevel#nameEntry#set_text "";
335               newDevel#rootEntry#set_text "";
336               newDevel#toplevel#misc#hide()
337             end
338           else
339             MatitaLog.error ("The selected root does not contain " ^ 
340               match next_devel_must_contain with 
341               | Some x -> x 
342               | _ -> assert false));
343       connect_button newDevel#chooseRootButton 
344        (fun () ->
345          let path = self#chooseDir () in
346          match path with
347          | Some path -> newDevel#rootEntry#set_text path
348          | None -> ());
349       connect_button newDevel#cancelButton 
350        (fun () -> newDevel#toplevel#misc#hide ());
351       ignore(newDevel#toplevel#event#connect#delete 
352         (fun _ -> newDevel#toplevel#misc#hide();true));
353       
354         (* file selection win *)
355       ignore (fileSel#fileSelectionWin#event#connect#delete (fun _ -> true));
356       ignore (fileSel#fileSelectionWin#connect#response (fun event ->
357         let return r =
358           chosen_file <- r;
359           fileSel#fileSelectionWin#misc#hide ();
360           GMain.Main.quit ()
361         in
362         match event with
363         | `OK ->
364             let fname = fileSel#fileSelectionWin#filename in
365             if Sys.file_exists fname then
366               begin
367                 if is_regular fname && not(_only_directory) then 
368                   return (Some fname) 
369                 else if _only_directory && is_dir fname then 
370                   return (Some fname)
371               end
372             else
373               begin
374                 if _ok_not_exists then 
375                   return (Some fname)
376               end
377         | `CANCEL -> return None
378         | `HELP -> ()
379         | `DELETE_EVENT -> return None));
380         (* menus *)
381       List.iter (fun w -> w#misc#set_sensitive false) [ main#saveMenuItem ];
382       main#helpMenu#set_right_justified true;
383         (* console *)
384       let adj = main#logScrolledWin#vadjustment in
385         ignore (adj#connect#changed
386                 (fun _ -> adj#set_value (adj#upper -. adj#page_size)));
387       console#message (sprintf "\tMatita version %s\n" BuildTimeConf.version);
388         (* toolbar *)
389       let module A = GrafiteAst in
390       let hole = CicNotationPt.UserInput in
391       let loc = Disambiguate.dummy_floc in
392       let tac ast _ =
393         if (MatitaScript.instance ())#onGoingProof () then
394           (MatitaScript.instance ())#advance
395             ~statement:("\n" ^ GrafiteAstPp.pp_tactical (A.Tactic (loc, ast)))
396             ()
397       in
398       let tac_w_term ast _ =
399         if (MatitaScript.instance ())#onGoingProof () then
400           let buf = source_buffer in
401           buf#insert ~iter:(buf#get_iter_at_mark (`NAME "locked"))
402             ("\n" ^ GrafiteAstPp.pp_tactic ast)
403       in
404       let tbar = self#main in
405       connect_button tbar#introsButton (tac (A.Intros (loc, None, [])));
406       connect_button tbar#applyButton (tac_w_term (A.Apply (loc, hole)));
407       connect_button tbar#exactButton (tac_w_term (A.Exact (loc, hole)));
408       connect_button tbar#elimButton (tac_w_term (A.Elim (loc, hole, None, None, [])));
409       connect_button tbar#elimTypeButton (tac_w_term (A.ElimType (loc, hole, None, None, [])));
410       connect_button tbar#splitButton (tac (A.Split loc));
411       connect_button tbar#leftButton (tac (A.Left loc));
412       connect_button tbar#rightButton (tac (A.Right loc));
413       connect_button tbar#existsButton (tac (A.Exists loc));
414       connect_button tbar#reflexivityButton (tac (A.Reflexivity loc));
415       connect_button tbar#symmetryButton (tac (A.Symmetry loc));
416       connect_button tbar#transitivityButton
417         (tac_w_term (A.Transitivity (loc, hole)));
418       connect_button tbar#assumptionButton (tac (A.Assumption loc));
419       connect_button tbar#cutButton (tac_w_term (A.Cut (loc, None, hole)));
420       connect_button tbar#autoButton (tac (A.Auto (loc,None,None)));
421       MatitaGtkMisc.toggle_widget_visibility
422        ~widget:(self#main#tacticsButtonsHandlebox :> GObj.widget)
423        ~check:self#main#tacticsBarMenuItem;
424       let module Hr = Helm_registry in
425       if
426         not (Hr.get_opt_default Hr.bool ~default:false "matita.tactics_bar")
427       then 
428         self#main#tacticsBarMenuItem#set_active false;
429       MatitaGtkMisc.toggle_callback 
430         ~callback:(function 
431           | true -> self#main#toplevel#fullscreen () 
432           | false -> self#main#toplevel#unfullscreen ())
433         ~check:self#main#fullscreenMenuItem;
434       self#main#fullscreenMenuItem#set_active false;
435         (* log *)
436       MatitaLog.set_log_callback self#console#log_callback;
437       GtkSignal.user_handler :=
438         (fun exn -> MatitaLog.error (MatitaExcPp.to_string exn));
439         (* script *)
440       let _ =
441         match GSourceView.source_language_from_file BuildTimeConf.lang_file with
442         | None ->
443             MatitaLog.warn (sprintf "can't load language file %s"
444               BuildTimeConf.lang_file)
445         | Some matita_lang ->
446             source_buffer#set_language matita_lang;
447             source_buffer#set_highlight true
448       in
449       let s () = MatitaScript.instance () in
450       let disableSave () =
451         script_fname <- None;
452         self#main#saveMenuItem#misc#set_sensitive false
453       in
454       let saveAsScript () =
455         let script = s () in
456         match self#chooseFile ~ok_not_exists:true () with
457         | Some f -> 
458               script#assignFileName f;
459               script#saveToFile (); 
460               console#message ("'"^f^"' saved.\n");
461               self#_enableSaveTo f
462         | None -> ()
463       in
464       let saveScript () =
465         match script_fname with
466         | None -> saveAsScript ()
467         | Some f -> 
468               (s ())#assignFileName f;
469               (s ())#saveToFile ();
470               console#message ("'"^f^"' saved.\n");
471       in
472       let loadScript () =
473         let script = s () in 
474         let status = script#status in
475         try 
476           if source_view#buffer#modified then
477             begin
478               match ask_unsaved main#toplevel with
479               | `YES -> saveScript ()
480               | `NO -> ()
481               | `CANCEL -> raise MatitaTypes.Cancel
482             end;
483           (match script_fname with
484           | None -> ()
485           | Some fname -> 
486               ask_and_save_moo_if_needed main#toplevel fname status);
487           match self#chooseFile () with
488           | Some f -> 
489                 script#reset (); 
490                 script#assignFileName f;
491                 source_view#source_buffer#begin_not_undoable_action ();
492                 script#loadFromFile (); 
493                 source_view#source_buffer#end_not_undoable_action ();
494                 console#message ("'"^f^"' loaded.\n");
495                 self#_enableSaveTo f
496           | None -> ()
497         with MatitaTypes.Cancel -> ()
498       in
499       let newScript () = 
500         source_view#source_buffer#begin_not_undoable_action ();
501         (s ())#reset (); 
502         (s ())#template (); 
503         source_view#source_buffer#end_not_undoable_action ();
504         disableSave ();
505         script_fname <- None
506       in
507       let cursor () =
508         source_buffer#place_cursor
509           (source_buffer#get_iter_at_mark (`NAME "locked"))
510       in
511       let lock_world _ =
512         main#buttonsToolbar#misc#set_sensitive false;
513         source_view#set_editable false
514       in
515       let unlock_world _ =
516         main#buttonsToolbar#misc#set_sensitive true;
517         source_view#set_editable true
518       in
519       let advance _ = (MatitaScript.instance ())#advance (); cursor () in
520       let retract _ = (MatitaScript.instance ())#retract (); cursor () in
521       let top _ = (MatitaScript.instance ())#goto `Top (); cursor () in
522       let bottom _ = (MatitaScript.instance ())#goto `Bottom (); cursor () in
523       let jump _ = (MatitaScript.instance ())#goto `Cursor (); cursor () in
524       let locker f = 
525         fun () -> 
526           lock_world ();
527           try f ();unlock_world () with exc -> unlock_world (); raise exc
528       in
529       let advance = locker advance in
530       let retract = locker retract in
531       let top = locker top in
532       let bottom = locker bottom in
533       let jump = locker jump in
534       let connect_key sym f =
535         connect_key self#main#mainWinEventBox#event
536           ~modifiers:[`CONTROL] ~stop:true sym f;
537         connect_key self#sourceView#event
538           ~modifiers:[`CONTROL] ~stop:true sym f
539       in
540         (* quit *)
541       self#setQuitCallback (fun () -> 
542         let status = (MatitaScript.instance ())#status in
543         if source_view#buffer#modified then
544           begin
545             let rc = ask_unsaved main#toplevel in 
546             try
547               match rc with
548               | `YES -> saveScript ();
549                         if not source_view#buffer#modified then
550                           begin
551                             (match script_fname with
552                             | None -> ()
553                             | Some fname -> 
554                                ask_and_save_moo_if_needed 
555                                  main#toplevel fname status);
556                           GMain.Main.quit ()
557                           end
558               | `NO -> GMain.Main.quit ()
559               | `CANCEL -> raise MatitaTypes.Cancel
560             with MatitaTypes.Cancel -> ()
561           end 
562         else 
563           begin  
564             (match script_fname with
565             | None -> clean_current_baseuri status; GMain.Main.quit ()
566             | Some fname ->
567                 try
568                   ask_and_save_moo_if_needed main#toplevel fname status;
569                   GMain.Main.quit ()
570                 with MatitaTypes.Cancel -> ())
571           end);
572       connect_button self#main#scriptAdvanceButton advance;
573       connect_button self#main#scriptRetractButton retract;
574       connect_button self#main#scriptTopButton top;
575       connect_button self#main#scriptBottomButton bottom;
576       connect_key GdkKeysyms._Down advance;
577       connect_key GdkKeysyms._Up retract;
578       connect_key GdkKeysyms._Home top;
579       connect_key GdkKeysyms._End bottom;
580       connect_button self#main#scriptJumpButton jump;
581       connect_menu_item self#main#openMenuItem   loadScript;
582       connect_menu_item self#main#saveMenuItem   saveScript;
583       connect_menu_item self#main#saveAsMenuItem saveAsScript;
584       connect_menu_item self#main#newMenuItem    newScript;
585       connect_key GdkKeysyms._period
586         (fun () ->
587           source_buffer#insert ~iter:(source_buffer#get_iter_at_mark `INSERT)
588             ".\n";
589           advance ());
590       connect_key GdkKeysyms._Return
591         (fun () ->
592           source_buffer#insert ~iter:(source_buffer#get_iter_at_mark `INSERT)
593             "\n";
594           advance ());
595          (* script monospace font stuff *)  
596       self#updateFontSize ();
597         (* debug menu *)
598       self#main#debugMenu#misc#hide ();
599         (* status bar *)
600       self#main#hintLowImage#set_file (image_path "matita-bulb-low.png");
601       self#main#hintMediumImage#set_file (image_path "matita-bulb-medium.png");
602       self#main#hintHighImage#set_file (image_path "matita-bulb-high.png");
603         (* focus *)
604       self#sourceView#misc#grab_focus ();
605         (* main win dimension *)
606       let width = Gdk.Screen.width () in
607       let height = Gdk.Screen.height () in
608       let main_w = width * 90 / 100 in 
609       let main_h = height * 80 / 100 in
610       let script_w = main_w * 6 / 10 in
611       self#main#toplevel#resize ~width:main_w ~height:main_h;
612       self#main#hpaneScriptSequent#set_position script_w;
613         (* source_view *)
614       ignore(source_view#connect#after#paste_clipboard 
615         ~callback:(fun () -> (MatitaScript.instance ())#clean_dirty_lock))
616     
617     method loadScript file =       
618       let script = MatitaScript.instance () in
619       script#reset (); 
620       script#assignFileName file;
621       if not (Sys.file_exists file) then
622         begin
623           let oc = open_out file in
624           let template = MatitaMisc.input_file BuildTimeConf.script_template in 
625           output_string oc template;
626           close_out oc
627         end;
628       source_view#source_buffer#begin_not_undoable_action ();
629       script#loadFromFile ();
630       source_view#source_buffer#end_not_undoable_action ();
631       console#message ("'"^file^"' loaded.");
632       self#_enableSaveTo file
633       
634     method setStar name b =
635       let l = main#scriptLabel in
636       if b then
637         l#set_text (name ^  " *")
638       else
639         l#set_text (name)
640         
641     method private _enableSaveTo file =
642       script_fname <- Some file;
643       self#main#saveMenuItem#misc#set_sensitive true
644         
645
646     method console = console
647     method sourceView: GSourceView.source_view = (source_view: GSourceView.source_view)
648     method about = about
649     method fileSel = fileSel
650     method findRepl = findRepl
651     method main = main
652     method develList = develList
653     method newDevel = newDevel
654
655     method newBrowserWin () =
656       object (self)
657         inherit browserWin ()
658         val combo = GEdit.combo_box_entry ()
659         initializer
660           self#check_widgets ();
661           let combo_widget = combo#coerce in
662           uriHBox#pack ~from:`END ~fill:true ~expand:true combo_widget;
663           combo#entry#misc#grab_focus ()
664         method browserUri = combo
665       end
666
667     method newUriDialog () =
668       let dialog = new uriChoiceDialog () in
669       dialog#check_widgets ();
670       dialog
671
672     method newInterpDialog () =
673       let dialog = new interpChoiceDialog () in
674       dialog#check_widgets ();
675       dialog
676
677     method newConfirmationDialog () =
678       let dialog = new confirmationDialog () in
679       dialog#check_widgets ();
680       dialog
681
682     method newEmptyDialog () =
683       let dialog = new emptyDialog () in
684       dialog#check_widgets ();
685       dialog
686
687     method private addKeyBinding key callback =
688       List.iter (fun evbox -> add_key_binding key callback evbox)
689         keyBindingBoxes
690
691     method setQuitCallback callback =
692       ignore (main#quitMenuItem#connect#activate callback);
693       ignore (main#toplevel#event#connect#delete 
694         (fun _ -> callback ();true));
695       self#addKeyBinding GdkKeysyms._q callback
696
697     method chooseFile ?(ok_not_exists = false) () =
698       _ok_not_exists <- ok_not_exists;
699       _only_directory <- false;
700       fileSel#fileSelectionWin#show ();
701       GtkThread.main ();
702       chosen_file
703
704     method private chooseDir ?(ok_not_exists = false) () =
705       _ok_not_exists <- ok_not_exists;
706       _only_directory <- true;
707       fileSel#fileSelectionWin#show ();
708       GtkThread.main ();
709       (* we should check that this is a directory *)
710       chosen_file
711   
712     method createDevelopment ~containing =
713       next_devel_must_contain <- containing;
714       newDevel#toplevel#misc#show()
715
716     method askText ?(title = "") ?(msg = "") () =
717       let dialog = new textDialog () in
718       dialog#textDialog#set_title title;
719       dialog#textDialogLabel#set_label msg;
720       let text = ref None in
721       let return v =
722         text := v;
723         dialog#textDialog#destroy ();
724         GMain.Main.quit ()
725       in
726       ignore (dialog#textDialog#event#connect#delete (fun _ -> true));
727       connect_button dialog#textDialogCancelButton (fun _ -> return None);
728       connect_button dialog#textDialogOkButton (fun _ ->
729         let text = dialog#textDialogTextView#buffer#get_text () in
730         return (Some text));
731       dialog#textDialog#show ();
732       GtkThread.main ();
733       !text
734
735     method private updateFontSize () =
736       self#sourceView#misc#modify_font_by_name
737         (sprintf "%s %d" BuildTimeConf.script_font font_size)
738
739     method increaseFontSize () =
740       font_size <- font_size + 1;
741       self#updateFontSize ()
742
743     method decreaseFontSize () =
744       font_size <- font_size - 1;
745       self#updateFontSize ()
746
747     method resetFontSize () =
748       font_size <- default_font_size;
749       self#updateFontSize ()
750
751   end
752
753 let gui () = 
754   let g = new gui () in
755   gui_instance := Some g;
756   g
757   
758 let instance = singleton gui
759
760 let non p x = not (p x)
761
762 (* this is a shit and should be changed :-{ *)
763 let interactive_uri_choice
764   ?(selection_mode:[`SINGLE|`MULTIPLE] = `MULTIPLE) ?(title = "")
765   ?(msg = "") ?(nonvars_button = false) ?(hide_uri_entry=false) 
766   ?(hide_try=false) ?(ok_label="_Auto") ?(ok_action:[`SELECT|`AUTO] = `AUTO) 
767   ?copy_cb ()
768   ~id uris
769 =
770   let gui = instance () in
771   let nonvars_uris = lazy (List.filter (non UriManager.uri_is_var) uris) in
772   if (selection_mode <> `SINGLE) &&
773     (Helm_registry.get_bool "matita.auto_disambiguation")
774   then
775     Lazy.force nonvars_uris
776   else begin
777     let dialog = gui#newUriDialog () in
778     if hide_uri_entry then
779       dialog#uriEntryHBox#misc#hide ();
780     if hide_try then
781       begin
782       dialog#uriChoiceSelectedButton#misc#hide ();
783       dialog#uriChoiceConstantsButton#misc#hide ();
784       end;
785     dialog#okLabel#set_label ok_label;  
786     dialog#uriChoiceTreeView#selection#set_mode
787       (selection_mode :> Gtk.Tags.selection_mode);
788     let model = new stringListModel dialog#uriChoiceTreeView in
789     let choices = ref None in
790     let nonvars = ref false in
791     (match copy_cb with
792     | None -> ()
793     | Some cb ->
794         dialog#copyButton#misc#show ();
795         connect_button dialog#copyButton 
796         (fun _ ->
797           match model#easy_selection () with
798           | [u] -> (cb u)
799           | _ -> ()));
800     dialog#uriChoiceDialog#set_title title;
801     dialog#uriChoiceLabel#set_text msg;
802     List.iter model#easy_append (List.map UriManager.string_of_uri uris);
803     dialog#uriChoiceConstantsButton#misc#set_sensitive nonvars_button;
804     let return v =
805       choices := v;
806       dialog#uriChoiceDialog#destroy ();
807       GMain.Main.quit ()
808     in
809     ignore (dialog#uriChoiceDialog#event#connect#delete (fun _ -> true));
810     connect_button dialog#uriChoiceConstantsButton (fun _ ->
811       return (Some (Lazy.force nonvars_uris)));
812     if ok_action = `AUTO then
813       connect_button dialog#uriChoiceAutoButton (fun _ ->
814         Helm_registry.set_bool "matita.auto_disambiguation" true;
815         return (Some (Lazy.force nonvars_uris)))
816     else
817       connect_button dialog#uriChoiceAutoButton (fun _ ->
818         match model#easy_selection () with
819         | [] -> ()
820         | uris -> return (Some (List.map UriManager.uri_of_string uris)));
821     connect_button dialog#uriChoiceSelectedButton (fun _ ->
822       match model#easy_selection () with
823       | [] -> ()
824       | uris -> return (Some (List.map UriManager.uri_of_string uris)));
825     connect_button dialog#uriChoiceAbortButton (fun _ -> return None);
826     dialog#uriChoiceDialog#show ();
827     GtkThread.main ();
828     (match !choices with 
829     | None -> raise MatitaTypes.Cancel
830     | Some uris -> uris)
831   end
832
833 class interpModel =
834   let cols = new GTree.column_list in
835   let id_col = cols#add Gobject.Data.string in
836   let dsc_col = cols#add Gobject.Data.string in
837   let interp_no_col = cols#add Gobject.Data.int in
838   let tree_store = GTree.tree_store cols in
839   let id_renderer = GTree.cell_renderer_text [], ["text", id_col] in
840   let dsc_renderer = GTree.cell_renderer_text [], ["text", dsc_col] in
841   let id_view_col = GTree.view_column ~renderer:id_renderer () in
842   let dsc_view_col = GTree.view_column ~renderer:dsc_renderer () in
843   fun tree_view choices ->
844     object
845       initializer
846         tree_view#set_model (Some (tree_store :> GTree.model));
847         ignore (tree_view#append_column id_view_col);
848         ignore (tree_view#append_column dsc_view_col);
849         let name_of_interp =
850           (* try to find a reasonable name for an interpretation *)
851           let idx = ref 0 in
852           fun interp ->
853             try
854               List.assoc "0" interp
855             with Not_found ->
856               incr idx; string_of_int !idx
857         in
858         tree_store#clear ();
859         let idx = ref ~-1 in
860         List.iter
861           (fun interp ->
862             incr idx;
863             let interp_row = tree_store#append () in
864             tree_store#set ~row:interp_row ~column:id_col
865               (name_of_interp interp);
866             tree_store#set ~row:interp_row ~column:interp_no_col !idx;
867             List.iter
868               (fun (id, dsc) ->
869                 let row = tree_store#append ~parent:interp_row () in
870                 tree_store#set ~row ~column:id_col id;
871                 tree_store#set ~row ~column:dsc_col dsc;
872                 tree_store#set ~row ~column:interp_no_col !idx)
873               interp)
874           choices
875
876       method get_interp_no tree_path =
877         let iter = tree_store#get_iter tree_path in
878         tree_store#get ~row:iter ~column:interp_no_col
879     end
880
881 let interactive_interp_choice () choices =
882   let gui = instance () in
883   assert (choices <> []);
884   let dialog = gui#newInterpDialog () in
885   let model = new interpModel dialog#interpChoiceTreeView choices in
886   let interp_len = List.length (List.hd choices) in
887   dialog#interpChoiceDialog#set_title "Interpretation choice";
888   dialog#interpChoiceDialogLabel#set_label "Choose an interpretation:";
889   let interp_no = ref None in
890   let return _ =
891     dialog#interpChoiceDialog#destroy ();
892     GMain.Main.quit ()
893   in
894   let fail _ = interp_no := None; return () in
895   ignore (dialog#interpChoiceDialog#event#connect#delete (fun _ -> true));
896   connect_button dialog#interpChoiceOkButton (fun _ ->
897     match !interp_no with None -> () | Some _ -> return ());
898   connect_button dialog#interpChoiceCancelButton fail;
899   ignore (dialog#interpChoiceTreeView#connect#row_activated (fun path _ ->
900     interp_no := Some (model#get_interp_no path);
901     return ()));
902   let selection = dialog#interpChoiceTreeView#selection in
903   ignore (selection#connect#changed (fun _ ->
904     match selection#get_selected_rows with
905     | [path] ->
906         MatitaLog.debug (sprintf "selection: %d" (model#get_interp_no path));
907         interp_no := Some (model#get_interp_no path)
908     | _ -> assert false));
909   dialog#interpChoiceDialog#show ();
910   GtkThread.main ();
911   (match !interp_no with Some row -> [row] | _ -> raise MatitaTypes.Cancel)
912