1999-12-16 04:25:11 -08:00
|
|
|
(*************************************************************************)
|
|
|
|
(* *)
|
|
|
|
(* Objective Caml LablTk library *)
|
|
|
|
(* *)
|
|
|
|
(* Jacques Garrigue, Kyoto University RIMS *)
|
|
|
|
(* *)
|
|
|
|
(* Copyright 1999 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 *)
|
2001-12-07 05:41:02 -08:00
|
|
|
(* General Public License, with the special exception on linking *)
|
|
|
|
(* described in file ../../../LICENSE. *)
|
1999-12-16 04:25:11 -08:00
|
|
|
(* *)
|
|
|
|
(*************************************************************************)
|
|
|
|
|
1999-11-30 06:59:39 -08:00
|
|
|
(* $Id$ *)
|
|
|
|
|
2001-09-06 01:52:32 -07:00
|
|
|
open StdLabels
|
|
|
|
|
2000-04-11 20:43:25 -07:00
|
|
|
let rec gen_list ~f:f ~len =
|
|
|
|
if len = 0 then [] else f () :: gen_list ~f:f ~len:(len - 1)
|
1999-11-30 06:59:39 -08:00
|
|
|
|
2000-04-11 20:43:25 -07:00
|
|
|
let rec make_list ~len ~fill =
|
|
|
|
if len = 0 then [] else fill :: make_list ~len:(len - 1) ~fill
|
1999-11-30 06:59:39 -08:00
|
|
|
|
|
|
|
(* By column version
|
2000-04-11 20:43:25 -07:00
|
|
|
let rec firsts ~len l =
|
1999-11-30 06:59:39 -08:00
|
|
|
if len = 0 then ([],l) else
|
|
|
|
match l with
|
|
|
|
a::l ->
|
|
|
|
let (f,l) = firsts l len:(len - 1) in
|
|
|
|
(a::f,l)
|
|
|
|
| [] ->
|
|
|
|
(l,[])
|
|
|
|
|
2000-04-11 20:43:25 -07:00
|
|
|
let rec split ~len = function
|
1999-11-30 06:59:39 -08:00
|
|
|
[] -> []
|
|
|
|
| l ->
|
2000-04-11 20:43:25 -07:00
|
|
|
let (f,r) = firsts l ~len in
|
|
|
|
let ret = split ~len r in
|
1999-11-30 06:59:39 -08:00
|
|
|
f :: ret
|
|
|
|
|
2000-04-11 20:43:25 -07:00
|
|
|
let extend l ~len ~fill =
|
1999-11-30 06:59:39 -08:00
|
|
|
if List.length l >= len then l
|
2000-04-11 20:43:25 -07:00
|
|
|
else l @ make_list ~fill len:(len - List.length l)
|
1999-11-30 06:59:39 -08:00
|
|
|
*)
|
|
|
|
|
|
|
|
(* By row version *)
|
|
|
|
|
2000-04-11 20:43:25 -07:00
|
|
|
let rec first l ~len =
|
1999-11-30 06:59:39 -08:00
|
|
|
if len = 0 then [], l else
|
|
|
|
match l with
|
2000-04-11 20:43:25 -07:00
|
|
|
[] -> make_list ~len ~fill:"", []
|
1999-11-30 06:59:39 -08:00
|
|
|
| a::l ->
|
2000-04-11 20:43:25 -07:00
|
|
|
let (l',r) = first ~len:(len - 1) l in a::l',r
|
1999-11-30 06:59:39 -08:00
|
|
|
|
2000-04-11 20:43:25 -07:00
|
|
|
let rec split l ~len =
|
|
|
|
if l = [] then make_list ~len ~fill:[] else
|
|
|
|
let (cars,r) = first l ~len in
|
|
|
|
let cdrs = split r ~len in
|
|
|
|
List.map2 cars cdrs ~f:(fun a l -> a::l)
|
1999-11-30 06:59:39 -08:00
|
|
|
|
|
|
|
|
|
|
|
open Tk
|
|
|
|
|
2000-04-11 20:43:25 -07:00
|
|
|
class c ~cols ~texts ?maxheight ?width parent = object (self)
|
1999-11-30 06:59:39 -08:00
|
|
|
val parent' = coe parent
|
|
|
|
val length = List.length texts
|
|
|
|
val boxes =
|
|
|
|
let height = (List.length texts - 1) / cols + 1 in
|
|
|
|
let height =
|
|
|
|
match maxheight with None -> height
|
|
|
|
| Some max -> min max height
|
|
|
|
in
|
2000-04-11 20:43:25 -07:00
|
|
|
gen_list ~len:cols ~f:
|
1999-11-30 06:59:39 -08:00
|
|
|
begin fun () ->
|
2000-04-11 20:43:25 -07:00
|
|
|
Listbox.create parent ~height ?width
|
|
|
|
~highlightthickness:0
|
|
|
|
~borderwidth:1
|
1999-11-30 06:59:39 -08:00
|
|
|
end
|
|
|
|
val mutable current = 0
|
|
|
|
method cols = cols
|
|
|
|
method texts = texts
|
|
|
|
method parent = parent'
|
|
|
|
method boxes = boxes
|
|
|
|
method current = current
|
2000-04-11 20:43:25 -07:00
|
|
|
method recenter ?(aligntop=false) n =
|
1999-11-30 06:59:39 -08:00
|
|
|
current <-
|
|
|
|
if n < 0 then 0 else
|
|
|
|
if n < length then n else length - 1;
|
|
|
|
(* Activate it, to keep consistent with Up/Down.
|
|
|
|
You have to be in Extended or Browse mode *)
|
2000-04-02 18:57:52 -07:00
|
|
|
let box = List.nth boxes (current mod cols)
|
1999-11-30 06:59:39 -08:00
|
|
|
and index = `Num (current / cols) in
|
2000-04-11 20:43:25 -07:00
|
|
|
List.iter boxes ~f:
|
1999-11-30 06:59:39 -08:00
|
|
|
begin fun box ->
|
2000-04-11 20:43:25 -07:00
|
|
|
Listbox.selection_clear box ~first:(`Num 0) ~last:`End;
|
|
|
|
Listbox.selection_anchor box ~index;
|
|
|
|
Listbox.activate box ~index
|
1999-11-30 06:59:39 -08:00
|
|
|
end;
|
|
|
|
Focus.set box;
|
2000-04-11 20:43:25 -07:00
|
|
|
if aligntop then Listbox.yview_index box ~index
|
|
|
|
else Listbox.see box ~index;
|
1999-11-30 06:59:39 -08:00
|
|
|
let (first,last) = Listbox.yview_get box in
|
2000-04-11 20:43:25 -07:00
|
|
|
List.iter boxes ~f:(Listbox.yview ~scroll:(`Moveto first))
|
1999-11-30 06:59:39 -08:00
|
|
|
method init =
|
2000-04-11 20:43:25 -07:00
|
|
|
let textl = split ~len:cols texts in
|
|
|
|
List.iter2 boxes textl ~f:
|
1999-11-30 06:59:39 -08:00
|
|
|
begin fun box texts ->
|
|
|
|
Jg_bind.enter_focus box;
|
2000-04-11 20:43:25 -07:00
|
|
|
Listbox.insert box ~texts ~index:`End
|
1999-11-30 06:59:39 -08:00
|
|
|
end;
|
2000-04-11 20:43:25 -07:00
|
|
|
pack boxes ~side:`Left ~expand:true ~fill:`Both;
|
|
|
|
self#bind_mouse ~events:[`ButtonPressDetail 1]
|
|
|
|
~action:(fun _ ~index:n -> self#recenter n; break ());
|
1999-11-30 06:59:39 -08:00
|
|
|
let current_height () =
|
|
|
|
let (top,bottom) = Listbox.yview_get (List.hd boxes) in
|
|
|
|
truncate ((bottom -. top) *. float (Listbox.size (List.hd boxes))
|
|
|
|
+. 0.99)
|
|
|
|
in
|
|
|
|
List.iter
|
|
|
|
[ "Right", (fun n -> n+1);
|
|
|
|
"Left", (fun n -> n-1);
|
|
|
|
"Up", (fun n -> n-cols);
|
|
|
|
"Down", (fun n -> n+cols);
|
|
|
|
"Prior", (fun n -> n - current_height () * cols);
|
|
|
|
"Next", (fun n -> n + current_height () * cols);
|
|
|
|
"Home", (fun _ -> 0);
|
|
|
|
"End", (fun _ -> List.length texts) ]
|
2000-04-11 20:43:25 -07:00
|
|
|
~f:begin fun (key,f) ->
|
|
|
|
self#bind_kbd ~events:[`KeyPressDetail key]
|
|
|
|
~action:(fun _ ~index:n -> self#recenter (f n); break ())
|
1999-11-30 06:59:39 -08:00
|
|
|
end;
|
|
|
|
self#recenter 0
|
2000-04-11 20:43:25 -07:00
|
|
|
method bind_mouse ~events ~action =
|
1999-11-30 06:59:39 -08:00
|
|
|
let i = ref 0 in
|
2000-04-11 20:43:25 -07:00
|
|
|
List.iter boxes ~f:
|
1999-11-30 06:59:39 -08:00
|
|
|
begin fun box ->
|
|
|
|
let b = !i in
|
2000-04-11 20:43:25 -07:00
|
|
|
bind box ~events ~breakable:true ~fields:[`MouseX;`MouseY]
|
|
|
|
~action:(fun ev ->
|
|
|
|
let `Num n = Listbox.nearest box ~y:ev.ev_MouseY
|
|
|
|
in action ev ~index:(n * cols + b));
|
1999-11-30 06:59:39 -08:00
|
|
|
incr i
|
|
|
|
end
|
2000-04-11 20:43:25 -07:00
|
|
|
method bind_kbd ~events ~action =
|
1999-11-30 06:59:39 -08:00
|
|
|
let i = ref 0 in
|
2000-04-11 20:43:25 -07:00
|
|
|
List.iter boxes ~f:
|
1999-11-30 06:59:39 -08:00
|
|
|
begin fun box ->
|
|
|
|
let b = !i in
|
2000-04-11 20:43:25 -07:00
|
|
|
bind box ~events ~breakable:true ~fields:[`Char]
|
|
|
|
~action:(fun ev ->
|
|
|
|
let `Num n = Listbox.index box ~index:`Active in
|
|
|
|
action ev ~index:(n * cols + b));
|
1999-11-30 06:59:39 -08:00
|
|
|
incr i
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
let add_scrollbar (box : c) =
|
|
|
|
let boxes = box#boxes in
|
|
|
|
let sb =
|
|
|
|
Scrollbar.create (box#parent)
|
2000-04-11 20:43:25 -07:00
|
|
|
~command:(fun ~scroll -> List.iter boxes ~f:(Listbox.yview ~scroll)) in
|
1999-11-30 06:59:39 -08:00
|
|
|
List.iter boxes
|
2000-04-11 20:43:25 -07:00
|
|
|
~f:(fun lb -> Listbox.configure lb ~yscrollcommand:(Scrollbar.set sb));
|
|
|
|
pack [sb] ~before:(List.hd boxes) ~side:`Right ~fill:`Y;
|
1999-11-30 06:59:39 -08:00
|
|
|
sb
|
|
|
|
|
2000-04-11 20:43:25 -07:00
|
|
|
let add_completion ?action ?wait (box : c) =
|
|
|
|
let comp = new Jg_completion.timed (box#texts) ?wait in
|
|
|
|
box#bind_kbd ~events:[`KeyPress]
|
|
|
|
~action:(fun ev ~index ->
|
1999-11-30 06:59:39 -08:00
|
|
|
(* consider only keys producing characters. The callback is called
|
|
|
|
* even if you press Shift. *)
|
|
|
|
if ev.ev_Char <> "" then
|
2000-04-11 20:43:25 -07:00
|
|
|
box#recenter (comp#add ev.ev_Char) ~aligntop:true);
|
1999-11-30 06:59:39 -08:00
|
|
|
match action with
|
|
|
|
Some action ->
|
2000-04-11 20:43:25 -07:00
|
|
|
box#bind_kbd ~events:[`KeyPressDetail "space"]
|
|
|
|
~action:(fun ev ~index -> action (box#current));
|
|
|
|
box#bind_kbd ~events:[`KeyPressDetail "Return"]
|
|
|
|
~action:(fun ev ~index -> action (box#current));
|
|
|
|
box#bind_mouse ~events:[`ButtonPressDetail 1]
|
|
|
|
~action:(fun ev ~index ->
|
1999-11-30 06:59:39 -08:00
|
|
|
box#recenter index; action (box#current); break ())
|
|
|
|
| None -> ()
|