summaryrefslogtreecommitdiff
path: root/ocaml-binary-annot/otherlibs/labltk/jpf/fileselect.ml
diff options
context:
space:
mode:
Diffstat (limited to 'ocaml-binary-annot/otherlibs/labltk/jpf/fileselect.ml')
-rw-r--r--ocaml-binary-annot/otherlibs/labltk/jpf/fileselect.ml367
1 files changed, 367 insertions, 0 deletions
diff --git a/ocaml-binary-annot/otherlibs/labltk/jpf/fileselect.ml b/ocaml-binary-annot/otherlibs/labltk/jpf/fileselect.ml
new file mode 100644
index 0000000000..3d42ac2dc1
--- /dev/null
+++ b/ocaml-binary-annot/otherlibs/labltk/jpf/fileselect.ml
@@ -0,0 +1,367 @@
+(***********************************************************************)
+(* *)
+(* MLTk, Tcl/Tk interface of Objective Caml *)
+(* *)
+(* Francois Rouaix, Francois Pessaux, Jun Furuse and Pierre Weis *)
+(* projet Cristal, INRIA Rocquencourt *)
+(* Jacques Garrigue, Kyoto University RIMS *)
+(* *)
+(* Copyright 2002 Institut National de Recherche en Informatique et *)
+(* en Automatique and Kyoto University. All rights reserved. *)
+(* This file is distributed under the terms of the GNU Library *)
+(* General Public License, with the special exception on linking *)
+(* described in file LICENSE found in the Objective Caml source tree. *)
+(* *)
+(***********************************************************************)
+
+(* $Id$ *)
+
+(* file selection box *)
+
+(* This file selecter works only under the OS with the full unix support.
+ For the portability, Tk.getOpenFile and Tk.getSaveFile are recommended. *)
+
+open StdLabels
+open UnixLabels
+open Str
+open Filename
+
+open Tk
+open Widget
+
+exception Not_selected
+
+(********************************************************** Search directory *)
+(* Default is curdir *)
+let global_dir = ref (getcwd ())
+
+(***************************************************** Some widgets creation *)
+
+(* from frx_listbox.ml *)
+let scroll_link sb lb =
+ Listbox.configure lb ~yscrollcommand: (Scrollbar.set sb);
+ Scrollbar.configure sb ~command: (Listbox.yview lb)
+
+(* focus when enter binding *)
+let bind_enter_focus w =
+ bind w ~events:[`Enter] ~action:(fun _ -> Focus.set w);;
+
+let myentry_create p ~variable =
+ let w = Entry.create p ~relief: `Sunken ~textvariable: variable in
+ bind_enter_focus w; w
+
+(************************************************************* Subshell call *)
+
+let subshell cmd =
+ let r,w = pipe () in
+ match fork () with
+ 0 -> close r; dup2 ~src:w ~dst:stdout;
+ execv ~prog:"/bin/sh" ~args:[| "/bin/sh"; "-c"; cmd |]
+ | id ->
+ close w;
+ let rc = in_channel_of_descr r in
+ let rec it l =
+ match
+ try Some(input_line rc) with _ -> None
+ with
+ Some x -> it (x::l)
+ | None -> List.rev l
+ in
+ let answer = it [] in
+ close_in rc; (* because of finalize_channel *)
+ let _ = waitpid ~mode:[] id in answer
+
+(***************************************************************** Path name *)
+
+(* find directory name which doesn't contain "?*[" *)
+let dirget = regexp "^\\([^\\*?[]*/\\)\\(.*\\)"
+
+let parse_filter src =
+ (* replace // by / *)
+ let s = global_replace (regexp "/+") "/" src in
+ (* replace /./ by / *)
+ let s = global_replace (regexp "/\\./") "/" s in
+ (* replace ????/../ by "" *)
+ let s = global_replace
+ (regexp "\\([^/]\\|[^\\./][^/]\\|[^/][^\\./]\\|[^/][^/]+\\)/\\.\\./")
+ ""
+ s in
+ (* replace ????/..$ by "" *)
+ let s = global_replace
+ (regexp "\\([^/]\\|[^\\./][^/]\\|[^/][^\\./]\\|[^/][^/]+\\)/\\.\\.$")
+ ""
+ s in
+ (* replace ^/../../ by / *)
+ let s = global_replace (regexp "^\\(/\\.\\.\\)+/") "/" s in
+ if string_match dirget s 0 then
+ let dirs = matched_group 1 s
+ and ptrn = matched_group 2 s
+ in
+ dirs, ptrn
+ else "", s
+
+let ls dir pattern =
+ subshell ("cd " ^ dir ^ ";/bin/ls -ad " ^ pattern ^" 2>/dev/null")
+
+(*************************************************************** File System *)
+
+let get_files_in_directory dir =
+ let dirh = opendir dir in
+ let rec get_them l =
+ match
+ try Some(Unix.readdir dirh) with _ -> None
+ with
+ | None ->
+ Unix.closedir dirh; l
+ | Some x ->
+ get_them (x::l)
+ in
+ List.sort ~cmp:compare (get_them [])
+
+let rec get_directories_in_files path =
+ List.filter
+ ~f:(fun x -> try (stat (path ^ x)).st_kind = S_DIR with _ -> false)
+
+let remove_directories path =
+ List.filter
+ ~f:(fun x -> try (stat (path ^ x)).st_kind <> S_DIR with _ -> false)
+
+(************************* a nice interface to listbox - from frx_listbox.ml *)
+
+let add_completion lb action =
+ let prefx = ref "" (* current match prefix *)
+ and maxi = ref 0 (* maximum index (doesn'y matter actually) *)
+ and current = ref 0 (* current position *)
+ and lastevent = ref 0 in
+
+ let rec move_forward () =
+ if Listbox.get lb ~index:(`Num !current) < !prefx then
+ if !current < !maxi then begin incr current; move_forward() end
+
+ and recenter () =
+ let element = `Num !current in
+ (* Clean the selection *)
+ Listbox.selection_clear lb ~first:(`Num 0) ~last:`End;
+ (* Set it to our unique element *)
+ Listbox.selection_set lb ~first:element ~last:element;
+ (* Activate it, to keep consistent with Up/Down.
+ You have to be in Extended or Browse mode *)
+ Listbox.activate lb ~index:element;
+ Listbox.selection_anchor lb ~index:element;
+ Listbox.see lb ~index:element in
+
+ let complete time s =
+ if time - !lastevent < 500 then (* sorry, hard coded limit *)
+ prefx := !prefx ^ s
+ else begin (* reset *)
+ current := 0;
+ prefx := s
+ end;
+ lastevent := time;
+ move_forward();
+ recenter() in
+
+
+ bind lb ~events:[`KeyPress] ~fields:[`Char; `Time]
+ (* consider only keys producing characters. The callback is called
+ if you press Shift. *)
+ ~action:(fun ev -> if ev.ev_Char <> "" then complete ev.ev_Time ev.ev_Char);
+ (* Key specific bindings override KeyPress *)
+ bind lb ~events:[`KeyPressDetail "Return"] ~action;
+ (* Finally, we have to set focus, otherwise events dont get through *)
+ Focus.set lb;
+ recenter() (* so that first item is selected *);
+ (* returns init_completion function *)
+ (fun lb ->
+ prefx := "";
+ maxi := Listbox.size lb - 1;
+ current := 0)
+
+(****************************************************************** Creation *)
+
+let f ~title ~action:proc ~filter:deffilter ~file:deffile ~multi ~sync =
+ (* Ah ! Now I regret about the names of the widgets... *)
+
+ let current_pattern = ref ""
+ and current_dir = ref "" in
+
+ (* init_completions *)
+ let filter_init_completion = ref (fun _ -> ())
+ and directory_init_completion = ref (fun _ -> ()) in
+
+ let tl = Toplevel.create default_toplevel in
+ Focus.set tl;
+ Wm.title_set tl title;
+
+ let filter_var = Textvariable.create ~on:tl () (* new_temporary *)
+ and selection_var = Textvariable.create ~on:tl ()
+ and sync_var = Textvariable.create ~on:tl () in
+
+ let frm' = Frame.create tl ~borderwidth: 1 ~relief: `Raised in
+ let frm = Frame.create frm' ~borderwidth: 8 in
+ let fl = Label.create frm ~text: "Filter" in
+ let df = Frame.create frm in
+ let dfl = Frame.create df in
+ let dfll = Label.create dfl ~text: "Directories" in
+ let dflf = Frame.create dfl in
+ let directory_listbox = Listbox.create dflf ~relief: `Sunken
+ and directory_scrollbar = Scrollbar.create dflf in
+ scroll_link directory_scrollbar directory_listbox;
+ let dfr = Frame.create df in
+ let dfrl = Label.create dfr ~text: "Files" in
+ let dfrf = Frame.create dfr in
+ let filter_listbox = Listbox.create dfrf ~relief: `Sunken in
+ let filter_scrollbar = Scrollbar.create dfrf in
+ scroll_link filter_scrollbar filter_listbox;
+ let sl = Label.create frm ~text: "Selection" in
+ let filter_entry = myentry_create frm ~variable: filter_var in
+ let selection_entry = myentry_create frm ~variable: selection_var
+ in
+ let cfrm' = Frame.create tl ~borderwidth: 1 ~relief: `Raised in
+ let cfrm = Frame.create cfrm' ~borderwidth: 8 in
+ let dumf = Frame.create cfrm in
+ let dumf2 = Frame.create cfrm in
+
+ let configure filter =
+ (* OLDER let curdir = getcwd () in *)
+(* Printf.eprintf "CURDIR %s\n" curdir; *)
+ let filter =
+ if string_match (regexp "^/.*") filter 0 then filter
+ else
+ if filter = "" then !global_dir ^ "/*"
+ else !global_dir ^ "/" ^ filter in
+(* Printf.eprintf "FILTER %s\n" filter; *)
+ let dirname, patternname = parse_filter filter in
+(* Printf.eprintf "DIRNAME %s PATTERNNAME %s\n" dirname patternname; *)
+ current_dir := dirname;
+ global_dir := dirname;
+ let patternname = if patternname = "" then "*" else patternname in
+ current_pattern := patternname;
+ let filter = dirname ^ patternname in
+(* Printf.eprintf "FILTER : %s\n\n" filter; *)
+(* flush Pervasives.stderr; *)
+ try
+ let directories = get_directories_in_files dirname
+ (get_files_in_directory dirname) in
+ (* get matched file by subshell call. *)
+ let matched_files = remove_directories dirname (ls dirname patternname)
+ in
+ Textvariable.set filter_var filter;
+ Textvariable.set selection_var (dirname ^ deffile);
+ Listbox.delete directory_listbox ~first:(`Num 0) ~last:`End;
+ Listbox.insert directory_listbox ~index:`End ~texts:directories;
+ Listbox.delete filter_listbox ~first:(`Num 0) ~last:`End;
+ Listbox.insert filter_listbox ~index:`End ~texts:matched_files;
+ !directory_init_completion directory_listbox;
+ !filter_init_completion filter_listbox
+ with
+ Unix_error (ENOENT,_,_) ->
+ (* Directory is not found (maybe) *)
+ Bell.ring ()
+ in
+
+ let selected_files = ref [] in (* used for synchronous mode *)
+ let activate l () =
+ Grab.release tl;
+ destroy tl;
+ if sync then
+ begin
+ selected_files := l;
+ Textvariable.set sync_var "1"
+ end
+ else
+ begin
+ proc l;
+ break ()
+ end
+ in
+
+ (* and buttons *)
+ let okb = Button.create cfrm ~text: "OK" ~command:
+ begin fun () ->
+ let files =
+ List.map (Listbox.curselection filter_listbox)
+ ~f:(fun x -> !current_dir ^ (Listbox.get filter_listbox ~index:x))
+ in
+ let files = if files = [] then [Textvariable.get selection_var]
+ else files in
+ activate files ()
+ end
+ in
+ let flb = Button.create cfrm ~text: "Filter"
+ ~command: (fun () -> configure (Textvariable.get filter_var)) in
+ let ccb = Button.create cfrm ~text: "Cancel"
+ ~command: (fun () -> activate [] ()) in
+
+ (* binding *)
+ bind selection_entry ~events:[`KeyPressDetail "Return"] ~breakable:true
+ ~action:(fun _ -> activate [Textvariable.get selection_var] ());
+ bind filter_entry ~events:[`KeyPressDetail "Return"]
+ ~action:(fun _ -> configure (Textvariable.get filter_var));
+
+ let action _ =
+ let files =
+ List.map (Listbox.curselection filter_listbox)
+ ~f:(fun x -> !current_dir ^ (Listbox.get filter_listbox ~index:x))
+ in
+ activate files ()
+ in
+ bind filter_listbox ~events:[`Modified([`Double], `ButtonPressDetail 1)]
+ ~breakable:true ~action;
+ if multi then Listbox.configure filter_listbox ~selectmode: `Multiple;
+ filter_init_completion := add_completion filter_listbox action;
+
+ let action _ =
+ try
+ configure (!current_dir ^ ((function
+ [x] -> Listbox.get directory_listbox ~index:x
+ | _ -> (* you must choose at least one directory. *)
+ Bell.ring (); raise Not_selected)
+ (Listbox.curselection directory_listbox)) ^ "/" ^ !current_pattern)
+ with _ -> () in
+ bind directory_listbox ~events:[`Modified([`Double], `ButtonPressDetail 1)]
+ ~breakable:true ~action;
+ Listbox.configure directory_listbox ~selectmode: `Browse;
+ directory_init_completion := add_completion directory_listbox action;
+
+ pack [frm'; frm] ~fill: `X;
+ (* filter *)
+ pack [fl] ~side: `Top ~anchor: `W;
+ pack [filter_entry] ~side: `Top ~fill: `X;
+ (* directory + files *)
+ pack [df] ~side: `Top ~fill: `X ~ipadx: 8;
+ (* directory *)
+ pack [dfl] ~side: `Left;
+ pack [dfll] ~side: `Top ~anchor: `W;
+ pack [dflf] ~side: `Top;
+ pack [coe directory_listbox; coe directory_scrollbar]
+ ~side: `Left ~fill: `Y;
+ (* files *)
+ pack [dfr] ~side: `Right;
+ pack [dfrl] ~side: `Top ~anchor: `W;
+ pack [dfrf] ~side: `Top;
+ pack [coe filter_listbox; coe filter_scrollbar] ~side: `Left ~fill: `Y;
+ (* selection *)
+ pack [sl] ~side: `Top ~anchor: `W;
+ pack [selection_entry] ~side: `Top ~fill: `X;
+
+ (* create OK, Filter and Cancel buttons *)
+ pack [cfrm'] ~fill: `X;
+ pack [cfrm] ~fill: `X;
+ pack [okb] ~side: `Left;
+ pack [dumf] ~side: `Left ~expand: true;
+ pack [flb] ~side: `Left;
+ pack [dumf2] ~side: `Left ~expand: true;
+ pack [ccb] ~side: `Left;
+
+ configure deffilter;
+
+ Tkwait.visibility tl;
+ Grab.set tl;
+
+ if sync then
+ begin
+ Tkwait.variable sync_var;
+ proc !selected_files
+ end;
+ ()