(* $Id$ *) open GdkKeysyms open Gtk open GObj open Utils open TiBase let main_project_modify = ref false let main_window = GWindow.window ~title:"ZOOM" ~x:10 ~y:10 () let main_vbox = GPack.vbox ~packing:main_window#add () let main_menu = GMenu.menu_bar ~packing:(main_vbox#pack ~expand:false) () let can_copy = ref (fun _ -> assert false) let can_paste = ref (fun _ -> assert false) class project () = let project_box = GPack.vbox ~packing:main_vbox#pack () in let project_tree = GTree2.tree ~packing:project_box#pack () in object(self) val mutable window_list = [] (* the selected window *) val mutable selected = (None : window_and_tree option) method change_selected sel = match selected with | None -> selected <- Some sel; sel#project_tree_item#misc#set_state `SELECTED; !can_copy true | Some old_sel -> if sel = old_sel then begin selected <- None; sel#project_tree_item#misc#set_state `NORMAL; !can_copy false end else begin old_sel#project_tree_item#misc#set_state `NORMAL; selected <- Some sel; sel#project_tree_item#misc#set_state `SELECTED; !can_copy true end val mutable filename = "" val mutable dirname = "" method set_filename f = let dir, file = split_filename f ~ext:".rad" in filename <- file; dirname <- dir method get_filename () = get_filename ~callback:self#set_filename ~dir:dirname () method dirname = dirname (* method set_dirname f = dirname <- f *) method add_window ~name ?tree:wt () = let wt = match wt with | None -> new window_and_tree ~name | Some wt -> wt in let tiwin = wt#tiwin and tw=wt#tree_window in let project_tree_item = wt#project_tree_item in project_tree#append project_tree_item; let show = ref true in project_tree_item#event#connect#button_press ~callback: (fun ev -> match GdkEvent.get_type ev with | `BUTTON_PRESS -> if GdkEvent.Button.button ev = 1 then begin self#change_selected wt end else if GdkEvent.Button.button ev = 3 then begin let menu = GMenu.menu () in let name = wt#tiwin#name in let mi_remove = GMenu.menu_item ~label:("delete " ^ name) ~packing:menu#append () and mi_copy = GMenu.menu_item ~label:("copy " ^ name) ~packing:menu#append () and mi_cut = GMenu.menu_item ~label:("cut " ^ name) ~packing:menu#append () in mi_remove#connect#activate ~callback:(fun () -> self#delete_window wt); mi_copy#connect#activate ~callback:(fun () -> self#copy_wt wt); mi_cut#connect#activate ~callback:(fun () -> self#cut_wt wt); menu#popup ~button:3 ~time:(GdkEvent.Button.time ev) end; GtkSignal.stop_emit (); true | `TWO_BUTTON_PRESS -> if GdkEvent.Button.button ev = 1 then begin if !show then begin show := false; tiwin#widget#misc#hide (); tw#misc#hide () end else begin show := true; tiwin#widget#misc#show (); tw#misc#show () end end; true | _ -> false); tiwin#connect_event#delete ~callback: (fun _ -> show := false; tiwin#widget#misc#hide (); true); tw#event#connect#delete ~callback: (fun _ -> show := false; tw#misc#hide (); true); window_list <- wt :: window_list; add_undo (Remove_window name); main_window#misc#set_can_focus false; main_window#misc#grab_focus () method add_window_by_node (Node ((classe, name, proplist), children)) = if classe <> "window" then failwith "add_window_by_node: class <> \"window\""; let name = change_name name in (* for paste *) let wt = new window_and_tree ~name in let tiwin = wt#tiwin in List.iter proplist ~f:(fun (n,v) -> tiwin#set_property n v); begin match children with | [] -> () | [ ch ] -> tiwin#add_children_wo_undo ch; () | _ -> failwith "add_window_by_node: more than one child" end; self#add_window ~name ~tree:wt () method delete_window (wt : window_and_tree) = let tiwin = wt#tiwin in project_tree#remove wt#project_tree_item; tiwin#remove_me (); wt#tree_window#destroy (); window_list <- list_remove ~f:(fun w -> w = wt) window_list method delete_window_by_name ~name = let wt = List.find window_list ~f:(fun wt -> wt#tiwin#name = name) in self#delete_window wt method delete () = List.iter window_list ~f:(fun wt -> self#delete_window wt); main_vbox#remove project_box#coerce; (* remove after test *) if !name_list <> [] then failwith "name_list not empty" method save_as () = if self#get_filename () then self#save () method save () = if filename = "" then self#save_as () else begin let outch = open_out (dirname ^ filename ^ ".rad") in let f = Format.formatter_of_out_channel outch in List.iter window_list ~f:(fun wt -> wt#tiwin#save f); close_out outch; main_project_modify := false end method copy_wt (wt : window_and_tree) = wt#tiwin#copy (); !can_paste true method cut_wt (wt : window_and_tree) = self#copy_wt wt; self#delete_window wt method copy () = match selected with | None -> failwith "main_project copy" | Some sel -> self#copy_wt sel method cut () = match selected with | None -> failwith "main_project cut" | Some sel -> self#cut_wt sel method paste () = let lexbuf = Lexing.from_string !window_selection in let node = Load_parser.window Load_lexer.token lexbuf in self#add_window_by_node node method emit () = let outc = open_out (dirname ^ filename ^ ".ml") in let f = Format.formatter_of_out_channel outc in List.iter window_list ~f:(fun wt -> wt#emit f); Format.fprintf f "let main () =@\n"; (* this is just for demo *) List.iter window_list ~f: begin fun wt -> let name = wt#tiwin#name in Format.fprintf f " let %s = new %s () in %s#show ();@\n" name name name end; Format.fprintf f " GMain.Main.main ()@\n@\nlet _ = main ()@\n"; close_out outc end let main_project = ref (new project ()) let load () = let filename = ref "" in get_filename ~callback:(fun f -> filename := f) ~dir:!main_project#dirname (); if !filename <> "" then begin !main_project#delete (); main_project := new project (); let inch = open_in !filename in let lexbuf = Lexing.from_channel inch in let project_list = Load_parser.project Load_lexer.token lexbuf in close_in inch; List.iter project_list ~f:(fun node -> !main_project#add_window_by_node node); !main_project#set_filename !filename end let interpret_undo = function | Add (parent_name, node, pos) -> let parent = Hashtbl.find widget_map parent_name in parent#add_children node ~pos | Remove child_name -> let child = Hashtbl.find widget_map child_name in child#remove_me () | Property (property, value_string) -> property#set value_string | Add_window node -> !main_project#add_window_by_node node | Remove_window name -> !main_project#delete_window_by_name ~name let undo () = if !last_action_was_undo then begin match !next_undo_info with | hd :: tl -> interpret_undo hd; next_undo_info := tl | [] -> message "no more undo info" end else begin match !undo_info with | hd :: tl -> interpret_undo hd; next_undo_info := tl | [] -> message "no undo info" end; last_action_was_undo := true let targets = [ { target = "STRING"; flags = []; info = 0} ] let xpm_window () = let source_drag_data_get classe _ (data : selection_data) ~info ~time = data#set ~typ:data#target ~format:0 ~data:classe in let window = GWindow.window ~title:"icons" ~x:250 ~y:10 () in window#misc#realize (); let vbox = GPack.vbox ~packing:window#add () in let table = GPack.table ~rows:1 ~columns:5 ~border_width:20 ~packing:vbox#pack () in let tooltips = GData.tooltips () in let add_xpm ~file ~left ~top ~tip = let gdk_pix = GDraw.pixmap_from_xpm ~file ~window () in let ev = GBin.event_box ~packing:(table#attach ~left ~top) () in let pix = GMisc.pixmap gdk_pix ~packing:ev#add () in ev#event#connect#button_press ~callback: (fun ev -> match GdkEvent.get_type ev with | `BUTTON_PRESS -> if GdkEvent.Button.button ev = 1 then begin !main_project#add_window ~name:(make_new_name "window") () end; true | _ -> false); tooltips#set_tip ev#coerce ~text:tip in add_xpm ~file:"window.xpm" ~left:0 ~top:0 ~tip:"window"; GMisc.separator `HORIZONTAL ~packing:vbox#pack (); let table = GPack.table ~rows:6 ~columns:6 ~packing:vbox#pack ~row_spacings:20 ~col_spacings:20 ~border_width:20 () in let add_xpm file ~left ~top ~classe = let gdk_pix = GDraw.pixmap_from_xpm ~file ~window () in let ev = GBin.event_box ~packing:(table#attach ~left ~top) () in let pix = GMisc.pixmap gdk_pix ~packing:ev#add () in ev#drag#source_set ~modi:[`BUTTON1] targets ~actions:[`COPY]; ev#drag#source_set_icon ~colormap:window#misc#style#colormap gdk_pix; ev#drag#connect#data_get ~callback:(source_drag_data_get classe); tooltips#set_tip ev#coerce ~text:classe in add_xpm "button.xpm" ~left:0 ~top:0 ~classe:"button"; add_xpm "togglebutton.xpm" ~left:1 ~top:0 ~classe:"toggle_button"; add_xpm "checkbutton.xpm" ~left:2 ~top:0 ~classe:"check_button"; add_xpm "radiobutton.xpm" ~left:3 ~top:0 ~classe:"radio_button"; add_xpm "toolbar.xpm" ~left:4 ~top:0 ~classe:"toolbar"; add_xpm "hbox.xpm" ~left:0 ~top:1 ~classe:"hbox"; add_xpm "vbox.xpm" ~left:1 ~top:1 ~classe:"vbox"; add_xpm "hbuttonbox.xpm" ~left:2 ~top:1 ~classe:"hbutton_box"; add_xpm "vbuttonbox.xpm" ~left:3 ~top:1 ~classe:"vbutton_box"; add_xpm "fixed.xpm" ~left:4 ~top:1 ~classe:"fixed"; add_xpm "frame.xpm" ~left:0 ~top:2 ~classe:"frame"; add_xpm "aspectframe.xpm" ~left:1 ~top:2 ~classe:"aspect_frame"; add_xpm "scrolledwindow.xpm" ~left:2 ~top:2 ~classe:"scrolled_window"; add_xpm "eventbox.xpm" ~left:3 ~top:2 ~classe:"event_box"; add_xpm "handlebox.xpm" ~left:4 ~top:2 ~classe:"handle_box"; add_xpm "viewport.xpm" ~left:5 ~top:2 ~classe:"viewport"; add_xpm "hseparator.xpm" ~left:0 ~top:3 ~classe:"hseparator"; add_xpm "vseparator.xpm" ~left:1 ~top:3 ~classe:"vseparator"; add_xpm "clist.xpm" ~left:2 ~top:3 ~classe:"clist"; add_xpm "label.xpm" ~left:0 ~top:4 ~classe:"label"; add_xpm "statusbar.xpm" ~left:1 ~top:4 ~classe:"statusbar"; add_xpm "notebook.xpm" ~left:2 ~top:4 ~classe:"notebook"; add_xpm "colorselection.xpm" ~left:3 ~top:4 ~classe:"color_selection"; add_xpm "pixmap.xpm" ~left:4 ~top:4 ~classe:"pixmap"; add_xpm "entry.xpm" ~left:0 ~top:5 ~classe:"entry"; add_xpm "spinbutton.xpm" ~left:1 ~top:5 ~classe:"spin_button"; add_xpm "combo.xpm" ~left:2 ~top:5 ~classe:"combo"; window#show (); window let main () = let _ = GMain.Main.init () in let prop_win = Propwin.init () in let palette = xpm_window () in main_window#show (); main_window#connect#destroy ~callback:GMain.Main.quit; let mp = main_project in let f = new GMenu.factory main_menu in let accel_group = f#accel_group in main_window#add_accel_group accel_group; prop_win#add_accel_group accel_group; palette#add_accel_group accel_group; let file_menu = new GMenu.factory (f#add_submenu "File") ~accel_group and edit_menu = new GMenu.factory (f#add_submenu "Edit") ~accel_group and view_menu = new GMenu.factory (f#add_submenu "View") ~accel_group and project_menu = new GMenu.factory (f#add_submenu "Project") ~accel_group in file_menu#add_item "Quit" ~key:_Q ~callback:GMain.Main.quit; project_menu#add_item "New" ~key:_N ~callback:(fun () -> !mp#delete (); mp := new project ()); project_menu#add_item "Open..." ~key:_O ~callback:load; project_menu#add_item "Save" ~key:_S ~callback:(fun () -> !mp#save ()); project_menu#add_item "Save as..." ~callback:(fun () -> !mp#save_as ()); project_menu#add_separator (); project_menu#add_item "Emit code" ~callback:(fun () -> !mp#emit ()); let copy_item = edit_menu#add_item "Copy" ~key:_C ~callback:(fun () -> !mp#copy ()) and cut_item = edit_menu#add_item "Cut" ~key:_X ~callback:(fun () -> !mp#cut ()) and paste_item = edit_menu#add_item "Paste" ~key:_V ~callback:(fun () -> !mp#paste ()) in can_copy := (fun b -> copy_item#misc#set_sensitive b; cut_item#misc#set_sensitive b); can_paste := paste_item#misc#set_sensitive; !can_copy false; !can_paste false; edit_menu#add_item "Undo" ~key:_Z ~callback:undo; let palette_visible = ref true in palette#event#connect#delete ~callback: (fun _ -> palette_visible := false; palette#misc#hide (); true); view_menu#add_item "Palette" ~callback:(fun () -> if !palette_visible then begin palette#misc#hide (); palette_visible := false end else begin palette#misc#show (); palette_visible := true end); let prop_win_visible = ref true in prop_win#event#connect#delete ~callback: (fun _ -> prop_win_visible := false; prop_win#misc#hide (); true); view_menu#add_item "Properties window" ~callback:(fun () -> if !prop_win_visible then begin prop_win#misc#hide (); prop_win_visible := false end else begin prop_win#misc#show (); prop_win_visible := true end); GMain.Main.main () let _ = main ()