summaryrefslogtreecommitdiff
path: root/src/visualwall.ml
diff options
context:
space:
mode:
Diffstat (limited to 'src/visualwall.ml')
-rw-r--r--src/visualwall.ml382
1 files changed, 279 insertions, 103 deletions
diff --git a/src/visualwall.ml b/src/visualwall.ml
index 3c07363..773f226 100644
--- a/src/visualwall.ml
+++ b/src/visualwall.ml
@@ -4,29 +4,56 @@
(* Licensed under the Sunset License v1.0 *)
+module My_Algebra =
+ struct
+ type t = Z.t
+ let zero = Z.zero
+ let one = Z.one
+ let add = Z.add
+ let sub = Z.sub
+ let mul = Z.mul
+ let div = Z.div
+ let to_string = Z.to_string
+ end
+
+module My_Wall = Wall.Make (My_Algebra)
+
+
+
let main () =
ignore (GtkMain.Main.init ());
- let window = GWindow.window
- ~title:"Number Wall Viewer"
- ~width:250 () in
+ let window =
+ GWindow.window
+ ~title:"Number Wall Viewer"
+ ~width:250 () in
ignore (window#connect#destroy ~callback:GMain.quit);
- let main_vbox = GPack.vbox
- ~packing:window#add () in
+ let main_vbox =
+ GPack.vbox
+ ~packing:window#add () in
+
+ let menubar =
+ GMenu.menu_bar
+ ~packing:(main_vbox#pack ~expand:false) () in
- let menubar = GMenu.menu_bar
- ~packing:(main_vbox#pack ~expand:false) () in
- let wall_view = GBin.scrolled_window
- ~hpolicy:`AUTOMATIC
- ~vpolicy:`AUTOMATIC
- ~packing:(main_vbox#pack ~expand:true) () in
- let statusbar = GMisc.statusbar
- ~packing:(main_vbox#pack ~expand:false) () in
+ let wall_view =
+ GBin.scrolled_window
+ ~hpolicy:`AUTOMATIC
+ ~vpolicy:`AUTOMATIC
+ ~packing:(main_vbox#pack ~expand:true) () in
+ let statusbar =
+ GMisc.statusbar
+ ~packing:(main_vbox#pack ~expand:false) () in
+
+ let context =
+ statusbar#new_context ~name:"Program status" in
+
+ let system_colormap =
+ Gdk.Color.get_system_colormap () in
- let context = statusbar#new_context ~name:"Program status" in
let set_status ~msg =
context#pop ();
@@ -34,130 +61,261 @@ let main () =
- let choose_open_file ~title ?(folder = "") () =
- let chooser = GWindow.file_chooser_dialog
- ~action:`OPEN
- ~title:title () in
- chooser#add_button_stock `CANCEL `dialog;
- chooser#add_select_button_stock `OPEN `filechooser;
- ignore (chooser#set_current_folder folder);
- let code = chooser#run () in
- let maybe_result = chooser#filename in
- chooser#destroy ();
- match maybe_result with
- | None -> ""
- | Some result -> if code = `filechooser then result else "" in
-
- let choose_save_file ~title ?(folder = "") ?(name = "") () =
- let chooser = GWindow.file_chooser_dialog
- ~action:`SAVE
- ~title:title () in
- chooser#add_button_stock `CANCEL `dialog;
- chooser#add_select_button_stock `SAVE `filechooser;
- chooser#set_current_name name;
- ignore (chooser#set_current_folder folder);
- let code = chooser#run () in
- let maybe_result = chooser#filename in
- chooser#destroy ();
- match maybe_result with
- | None -> ""
- | Some result -> if code = `filechooser then result else "" in
+ let number_wall = ref My_Wall.empty in
+ let modulus = ref 2 in
+
+ let magnify = ref 2 in
+
+ (* Default to orange foreground, blue background. *)
+ let foreground_color = ref (65535, 40447, 0) in
+ let background_color = ref (11519, 21247, 65535) in
+
+ let x_dim = ref 1 in
+ let y_dim = ref 1 in
+ let x_offset = ref 0 in
+
+ let wall_loaded = ref false in
+
+
+
+ let expose (drawitem:GMisc.drawing_area) (backing:GDraw.pixmap ref) event =
+ let area = GdkEvent.Expose.area event in
+ let x = Gdk.Rectangle.x area in
+ let y = Gdk.Rectangle.y area in
+ let width = Gdk.Rectangle.width area in
+ let height = Gdk.Rectangle.height area in
+ let drawing =
+ drawitem#misc#realize ();
+ new GDraw.drawable (drawitem#misc#window) in
+ drawing#put_pixmap ~x ~y ~xsrc:x ~ysrc:y ~width ~height !backing#pixmap;
+ false in
+
+ let draw_area =
+ GMisc.drawing_area
+ ~packing:wall_view#add_with_viewport () in
+
+ let pixmap_area = ref
+ (GDraw.pixmap
+ ~width:!x_dim
+ ~height:!y_dim
+ ~window:wall_view
+ ~colormap:system_colormap ()) in
+
+ ignore (draw_area#event#connect#expose ~callback:(expose draw_area pixmap_area));
+ draw_area#event#add [`EXPOSURE];
+
+ let redraw_pixmap () =
+ for j = 0 to !y_dim - 1 do
+ for i = !x_offset to !x_dim + !x_offset - 1 do
+ if My_Wall.has_value !number_wall ~x:i ~y:j &&
+ Z.rem (My_Wall.get !number_wall ~x:i ~y:j) (Z.of_int !modulus) <> My_Algebra.zero
+ then
+ !pixmap_area#set_foreground (`RGB !background_color)
+ else
+ !pixmap_area#set_foreground (`RGB !foreground_color);
+ !pixmap_area#rectangle
+ ~x:((i - !x_offset) * !magnify)
+ ~y:(j * !magnify)
+ ~width:!magnify
+ ~height:!magnify
+ ~filled:true ()
+ done
+ done;
+ let update_rectangle =
+ Gdk.Rectangle.create
+ ~x:0 ~y:0
+ ~width:(!x_dim * !magnify)
+ ~height:(!y_dim * !magnify) in
+ draw_area#misc#draw (Some update_rectangle) in
+
+ let new_pixmap () =
+ pixmap_area :=
+ GDraw.pixmap
+ ~width:(!x_dim * !magnify)
+ ~height:(!y_dim * !magnify)
+ ~window:wall_view
+ ~colormap:system_colormap ();
+ draw_area#misc#set_size_request
+ ~width:(!x_dim * !magnify)
+ ~height:(!y_dim * !magnify) () in
let generate_unused ~basename ~extension =
let rec checker num =
let testname = basename ^ "-" ^ (string_of_int num) ^ "." ^ extension in
- if not (Sys.file_exists testname) then testname else checker (num + 1) in
+ if not (Sys.file_exists testname) then
+ testname
+ else
+ checker (num + 1) in
checker 1 in
- let overwrite_check ~filename =
- if not (Sys.file_exists filename) then true else
- (let asker = GWindow.dialog
- ~title:"Confirm Overwrite"
- ~resizable:false
- ~width:380 ~height:130 () in
- let box = GPack.hbox
- ~packing:asker#vbox#add () in
- let _ = GMisc.image
- ~stock:`DIALOG_ERROR
- ~packing:box#add () in
- let message = GMisc.label
- ~text:("Error: " ^ filename ^ " already exists.")
- ~packing:box#add () in
- message#set_line_wrap true;
- asker#add_button "Cancel" `dialog;
- asker#add_button "Overwrite" `filechooser;
- let code = asker#run () in
- asker#destroy ();
- code = `filechooser) in
+ let menus = ref ([] : (string * GMenu.menu) list) in
+
+ let disable_menu_items menu placelist =
+ let disable nth = (List.nth menu#children nth)#misc#set_sensitive false in
+ List.iter disable placelist in
+
+ let enable_menu_items menu placelist =
+ let enable nth = (List.nth menu#children nth)#misc#set_sensitive true in
+ List.iter enable placelist in
let new_cb () =
- print_endline "New..." in
+ let maybe_new = Dialog.new_wall () in
+ if Option.is_some maybe_new then
+ (let (x,y,s) = Option.get maybe_new in
+ x_dim := x;
+ y_dim := y;
+ x_offset := y;
+ let real_x_dim = x + 2 * y in
+ let real_y_dim = y + x / 2 + 1 in
+ if s = "Pagoda" then
+ number_wall :=
+ My_Wall.create
+ ~dimx:real_x_dim
+ ~dimy:real_y_dim
+ ~init:(Sequence.pagoda ~len:real_x_dim)
+ else
+ number_wall :=
+ My_Wall.create
+ ~dimx:real_x_dim
+ ~dimy:real_y_dim
+ ~init:(Sequence.random ~len:real_x_dim);
+ new_pixmap ();
+ redraw_pixmap ();
+ wall_loaded := true;
+ enable_menu_items (List.assoc "file" !menus) [3;4;6];
+ set_status ~msg:"New wall created")
+ else
+ (set_status ~msg:"Canceled new wall")in
let open_cb () =
- let filename = choose_open_file
- ~title:"Open Sequence - Number Wall Viewer"
- ~folder:(Sys.getcwd ()) () in
- if filename != "" then
- (set_status ~msg:("Opened " ^ filename);
- print_endline filename)
+ let maybe_filename =
+ Dialog.open_file
+ ~folder:(Sys.getcwd ()) () in
+ if Option.is_some maybe_filename then
+ (let filename = Option.get maybe_filename in
+ try
+ let input_seq = Util.read_sequence_file filename in
+ let real_x_dim = Array.length input_seq in
+ x_dim := real_x_dim / 3;
+ y_dim := !x_dim;
+ let real_y_dim = !y_dim + !x_dim / 2 + 1 in
+ x_offset := !y_dim;
+ number_wall :=
+ My_Wall.create
+ ~dimx:real_x_dim
+ ~dimy:real_y_dim
+ ~init:input_seq;
+ new_pixmap ();
+ redraw_pixmap ();
+ wall_loaded := true;
+ enable_menu_items (List.assoc "file" !menus) [3;4;6];
+ set_status ~msg:("Opened " ^ filename)
+ with
+ Util.Not_an_integer s ->
+ set_status ~msg:("Error: " ^ s ^ " is not an integer"))
else
(set_status ~msg:"Canceled open") in
let save_as_cb () =
- let filename = choose_save_file
- ~title:"Save Sequence - Number Wall Viewer"
- ~folder:(Sys.getcwd ())
- ~name:(generate_unused ~basename:"sequence" ~extension:"txt") () in
- if filename != "" && overwrite_check ~filename then
- (set_status ~msg:("Saved sequence to " ^ filename);
- print_endline filename)
+ let maybe_filename =
+ Dialog.save_file
+ ~folder:(Sys.getcwd ())
+ ~name:(generate_unused ~basename:"sequence" ~extension:"txt") () in
+ if Option.is_some maybe_filename then
+ (let filename = Option.get maybe_filename in
+ let write channel =
+ for i = 0 to My_Wall.x_length !number_wall - 2 do
+ output_string channel (Z.to_string (My_Wall.get !number_wall ~x:i ~y:1) ^ ",");
+ done;
+ output_string channel (Z.to_string
+ (My_Wall.get !number_wall ~x:(My_Wall.x_length !number_wall - 1) ~y:1) ^ "\n") in
+ Util.call_with_open_output_file ~filename ~func:write;
+ set_status ~msg:("Saved sequence to " ^ filename))
else
(set_status ~msg:"Canceled save") in
let export_cb () =
- let filename = choose_save_file
- ~title:"Export Image - Number Wall Viewer"
- ~folder:(Sys.getcwd ())
- ~name:(generate_unused ~basename:"image" ~extension:"png") () in
- if filename != "" && overwrite_check ~filename then
- (set_status ~msg:("Exported image to " ^ filename);
- print_endline filename)
+ let maybe_filename =
+ Dialog.export_file
+ ~folder:(Sys.getcwd ())
+ ~name:(generate_unused ~basename:"image" ~extension:"png") () in
+ if Option.is_some maybe_filename then
+ (let filename = Option.get maybe_filename in
+ let buffer = GdkPixbuf.create
+ ~width:(!x_dim * !magnify)
+ ~height:(!y_dim * !magnify)
+ ~has_alpha:false () in
+ !pixmap_area#get_pixbuf
+ ~dest_x:0 ~dest_y:0
+ ~width:(!x_dim * !magnify)
+ ~height:(!y_dim * !magnify)
+ ~src_x:0 ~src_y:0
+ buffer;
+ GdkPixbuf.save
+ ~filename
+ ~typ:"png"
+ buffer;
+ set_status ~msg:("Exported image to " ^ filename))
else
(set_status ~msg:"Canceled export") in
let discard_cb () =
- print_endline "Discard" in
+ number_wall := My_Wall.empty;
+ x_dim := 1;
+ y_dim := 1;
+ new_pixmap ();
+ redraw_pixmap ();
+ wall_loaded := false;
+ disable_menu_items (List.assoc "file" !menus) [3;4;6];
+ set_status ~msg:"Discarded sequence" in
let color_cb () =
- print_endline "Color..." in
+ let maybe_colors =
+ Dialog.colors ~fore:!foreground_color ~back:!background_color in
+ if Option.is_some maybe_colors then
+ (foreground_color := fst (Option.get maybe_colors);
+ background_color := snd (Option.get maybe_colors);
+ if !wall_loaded then redraw_pixmap ();
+ set_status ~msg:"Color change applied")
+ else
+ (set_status ~msg:"Canceled color change") in
let modulus_cb () =
- print_endline "Modulus..." in
+ let maybe_modulus =
+ Dialog.modulus ~current:!modulus in
+ if Option.is_some maybe_modulus then
+ (modulus := Option.get maybe_modulus;
+ if !wall_loaded then redraw_pixmap ();
+ set_status ~msg:("Changed modulus to " ^ string_of_int !modulus))
+ else
+ (set_status ~msg:"Canceled modulus change") in
+
+
+
+ let zoom_cb n active =
+ if active then
+ (magnify := n;
+ if !wall_loaded then
+ (new_pixmap ();
+ redraw_pixmap ());
+ set_status ~msg:("Zoom set to " ^ string_of_int n ^ "x")) in
let about_cb () =
- let about_window = GWindow.about_dialog
- ~authors:["Jedidiah Barber"]
- ~comments:"A simple program to generate and visualise number walls."
- ~license:"Sunset License v1.0"
- ~title:"About - Number Wall Viewer"
- ~modal:true () in
- let destroy_me button = about_window#destroy () in
- ignore (about_window#connect#response ~callback:destroy_me);
- about_window#show () in
+ Dialog.about () in
@@ -177,6 +335,13 @@ let main () =
`I ("Modulus...", modulus_cb)
] in
+ let zoom_menu_entries = [
+ `R [ ("1x", false, (zoom_cb 1))
+ ; ("2x", true, (zoom_cb 2))
+ ; ("3x", false, (zoom_cb 3))
+ ; ("4x", false, (zoom_cb 4))]
+ ] in
+
let help_menu_entries = [
`I ("About", about_cb)
] in
@@ -184,13 +349,24 @@ let main () =
let create_menu ~label ~bar ~entries =
- let item = GMenu.menu_item ~label ~packing:bar#append () in
- let menu = GMenu.menu ~packing:item#set_submenu () in
- GToolbox.build_menu menu ~entries in
-
- ignore (create_menu ~label:"File" ~bar:menubar ~entries:file_menu_entries);
- ignore (create_menu ~label:"Options" ~bar:menubar ~entries:option_menu_entries);
- ignore (create_menu ~label:"Help" ~bar:menubar ~entries:help_menu_entries);
+ let item =
+ GMenu.menu_item
+ ~label
+ ~packing:bar#append () in
+ let menu =
+ GMenu.menu
+ ~packing:item#set_submenu () in
+ GToolbox.build_menu menu ~entries;
+ menu in
+
+ let file_menu = create_menu ~label:"File" ~bar:menubar ~entries:file_menu_entries in
+ let option_menu = create_menu ~label:"Options" ~bar:menubar ~entries:option_menu_entries in
+ let zoom_menu = create_menu ~label:"Zoom" ~bar:menubar ~entries:zoom_menu_entries in
+ let help_menu = create_menu ~label:"Help" ~bar:menubar ~entries:help_menu_entries in
+
+ menus := [("file",file_menu); ("option",option_menu); ("zoom",zoom_menu); ("help",help_menu)];
+
+ disable_menu_items (List.assoc "file" !menus) [3;4;6];
set_status ~msg:"No wall loaded";
window#show ();