File: tmkWidget.ml

package info (click to toggle)
ocaml-curses 1.0.2-2
  • links: PTS, VCS
  • area: main
  • in suites: lenny
  • size: 328 kB
  • ctags: 869
  • sloc: ml: 2,832; ansic: 673; makefile: 140; sh: 10
file content (266 lines) | stat: -rw-r--r-- 8,551 bytes parent folder | download | duplicates (8)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
open TmkStruct

exception Not_container
exception Not_toplevel

let rec find_next_widget prop prev cur d =
  let filtrer_direction = match d with
      | Direction.Previous | Direction.Left | Direction.Up ->
	  List.rev
      | Direction.Next | Direction.Right | Direction.Down ->
	  (fun x -> x) in
  let rec find_next_widget_list = function
    | [] -> None
    | h::t ->
	if prop h then Some h
	else
	  let c = try h#children () with Not_container -> [] in
	  let c = filtrer_direction c in
	  match find_next_widget_list c with
	    | (Some _) as r -> r
	    | None -> find_next_widget_list t in
  if prop cur then Some cur
  else
    let c = filtrer_direction (cur#children ()) in
    let rec split_list l = function
      | h::t when h == prev -> (List.rev (h::l), t)
      | h::t -> split_list (h::l) t
      | [] -> assert false in
    let (l,r) = split_list [] c in
    match find_next_widget_list r with
      | (Some _) as r -> r
      | None ->
	  let r =
	    try find_next_widget prop cur cur#parent d
	    with Not_found -> None in
	  match r with
	    | Some _ -> r
	    | None -> find_next_widget_list l


(****************************************************************************************
 * La classe Widget
 ****************************************************************************************)

let real_class_widget = Class.create "Widget" []

class virtual widget = object (self)
  val mutable window = Curses.null_window
  val mutable window_info = TmkArea.null_window
  val geometry = Geom.null ()
  val mutable state = State.normal
  val attributes = Array.create
    (succ State.to_int_max) Curses.A.normal
  val mutable attribute = Curses.A.normal
  val mutable name = ""
  val mutable need_redraw = false
  val mutable configured = false

  method virtual real_class : Class.t
  method virtual parent : widget
  method virtual terminal : widget TmkTerminal.terminal
  method can_focus = false
  method has_focus = State.has_focus state

  (* Gasp, I don't know how to write that type safely _and_ without
     writing all the type. *)
  method coerce = (Obj.magic self : widget)

  method set_name n =
    let p = if n = "" then "" else "." ^ n in
    let q =
      try (self#parent#name) ^ p
      with Not_found -> n in
    name <- q;
    if n <> "" then self#do_configuration ()

  method name = name

  method queue_redraw () =
    if not need_redraw then (
      need_redraw <- true;
      try self#parent#redraw_register self#coerce
      with Not_found ->
	Queue.add self#redraw_deliver self#terminal#event_queue
    )

  method redraw_deliver () =
    if geometry.Geom.w > 0 && geometry.Geom.h > 0 then (
      if need_redraw then self#signal_draw#emit ();
      need_redraw <- false
    )

  method is_container = false

  method add (w : widget) =
    (raise Not_container : unit)

  method remove (w : widget) =
    (raise Not_container : unit)

  method children () =
    (raise Not_container : widget list)

  method redraw_register (w : widget) =
    (raise Not_container : unit)

  method set_variable name subscripts value =
    match (name, subscripts, value) with
      | ("style", Some s, TmkStyle.S.Str v) ->
	  let res = self#terminal#resource in
	  let fixer_style n =
	    let v = TmkStyle.C.parse_style_string res attributes.(n) v in
	    attributes.(n) <- v;
	    if n = State.to_int state then attribute <- v in
	  List.iter fixer_style (TmkStyle.C.state_names s)
      | _ -> prerr_endline ("Unknown variable or illegal use: " ^ name)

  method do_configuration () =
    configured <- true;
    let v = self#terminal#configuration () in
    let v = TmkStyle.S.relevant_variables (fun _ -> "") name v in
    let accept_var (n, s, v) = self#set_variable n s v in
    List.iter accept_var v

  method toplevel_pass (m : widget Toplevel.m) =
    self#parent#toplevel_pass m

  method set_cursor (c : int * int) =
    (self#parent#set_cursor c : unit)

  (* Signals *)

  val signal_map =
    new TmkSignal.signal "map" TmkSignal.Marshall.all_unit
  val signal_get_size =
    new TmkSignal.signal "get_size" TmkSignal.Marshall.filter
  val signal_set_geometry =
    new TmkSignal.signal "set_geometry" TmkSignal.Marshall.all_unit
  val signal_set_state =
    new TmkSignal.signal "set_state" TmkSignal.Marshall.all_unit
  val signal_draw =
    new TmkSignal.signal "draw" TmkSignal.Marshall.all_unit
  val signal_got_focus =
    new TmkSignal.signal "got_focus" TmkSignal.Marshall.all_unit
  val signal_lost_focus =
    new TmkSignal.signal "lost_focus" TmkSignal.Marshall.all_unit
  val signal_key_event =
    new TmkSignal.signal "key_event" TmkSignal.Marshall.until_true
  val signal_add_descendant =
    new TmkSignal.signal "add_descendant" TmkSignal.Marshall.all_unit
  val signal_remove_descendant =
    new TmkSignal.signal "remove_descendant" TmkSignal.Marshall.all_unit
  val signal_toplevel_event = 
    new TmkSignal.signal "toplevel_event" TmkSignal.Marshall.all_unit

  method signal_map = signal_map
  method signal_get_size = signal_get_size
  method signal_set_geometry = signal_set_geometry
  method signal_set_state = signal_set_state
  method signal_draw = signal_draw
  method signal_got_focus = signal_got_focus
  method signal_lost_focus = signal_lost_focus
  method signal_key_event = signal_key_event
  method signal_add_descendant = signal_add_descendant
  method signal_remove_descendant = signal_remove_descendant
  method signal_toplevel_event = signal_toplevel_event

  method class_map w =
    window_info <- w;
    window <- w#window;
    if not configured then self#do_configuration ()

  method virtual class_get_size : int * int -> int * int

  method class_set_geometry g =
    Geom.record g geometry;
    self#queue_redraw ()

  method class_set_state s =
    state <- s;
    let n = attributes.(State.to_int s) in
    if n <> attribute then (
      attribute <- n;
      self#queue_redraw ()
    )

  method class_draw () =
    need_redraw <- false

  method class_got_focus () =
    assert self#can_focus;
    self#signal_set_state#emit (State.set_focus state true)

  method class_lost_focus () =
    assert self#can_focus;
    self#signal_set_state#emit (State.set_focus state false)

  method class_key_event k =
    let aux d =
      let w = match find_next_widget
	(fun w -> w#can_focus) self#coerce (self#parent) d with
	  | None -> assert false
	  | Some w -> w in
      let () = self#toplevel_pass (Toplevel.Give_focus w) in
      true in
    if k =  Curses.Key.up then aux Direction.Up
    else if k = Curses.Key.down then aux Direction.Down
    else if k = Curses.Key.left then aux Direction.Left
    else if k = Curses.Key.right then aux Direction.Right
    else if k = 9 then aux Direction.Next
    else
      try
	self#parent#signal_key_event#emit k
      with Not_found -> false

  method class_add_descendant (w : widget) =
    ()

  method class_remove_descendant (w : widget) =
    ()

  method class_toplevel_event (e : Toplevel.t) =
    raise Not_toplevel

  initializer
    let p = TmkStyle.R.color_pair_alloc self#terminal#resource 1 4 in
    attributes.(1) <- (Curses.A.color_pair p) lor Curses.A.bold;
    self#set_name "";
    self#signal_map#connect 101 (fun w -> self#class_map w);
    self#signal_get_size#connect 101 (fun t -> self#class_get_size t);
    self#signal_set_geometry#connect 101 (fun g -> self#class_set_geometry g);
    self#signal_set_state#connect 101 (fun s -> self#class_set_state s);
    self#signal_draw#connect 101 (fun () -> self#class_draw ());
    self#signal_got_focus#connect 101 (fun () -> self#class_got_focus ());
    self#signal_lost_focus#connect 101 (fun () -> self#class_lost_focus ());
    self#signal_key_event#connect (-1) (fun k -> self#class_key_event k);
    self#signal_add_descendant#connect 101 (fun w -> self#class_add_descendant w);
    self#signal_remove_descendant#connect 101 (fun w -> self#class_remove_descendant w);
    self#signal_toplevel_event#connect 101 (fun e -> self#class_toplevel_event e)
end

let warning w t =
  prerr_string w#name;
  prerr_string ": ";
  prerr_endline t

let rec full_tree_do_post f (w : widget) =
  if w#is_container then
    List.iter (full_tree_do_post f) (w#children ());
  f w

let rec find_first_focusable ex (w : widget) =
  if w#can_focus then (
    if w == ex then None
    else Some w
  ) else if w#is_container then
    let rec aux = function
      | [] -> None
      | h::t -> match find_first_focusable ex h with
	  | None -> aux t
	  | s -> s in
    aux (w#children ())
  else
    None

type terminal = widget TmkTerminal.terminal