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