File: custom_tree_generic.ml

package info (click to toggle)
lablgtk2 2.18.13-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 5,940 kB
  • sloc: ml: 41,454; ansic: 23,178; makefile: 685; sh: 75
file content (263 lines) | stat: -rw-r--r-- 8,344 bytes parent folder | download | duplicates (3)
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
(**************************************************************************)
(*    Lablgtk - Examples                                                  *)
(*                                                                        *)
(*    This code is in the public domain.                                  *)
(*    You may freely copy parts of it in your application.                *)
(*                                                                        *)
(**************************************************************************)

(* ../src/lablgtk2 -localdir custom_tree_generic.ml *)


let debug = false
let () = 
  if debug then begin 
  Gc.set { (Gc.get()) with Gc.verbose = 0x00d; space_overhead = 0 };
  ignore (Gc.create_alarm (fun () -> 
  let s = Gc.stat () in
  Format.printf "blocks=%d words=%d@."
  s.Gc.live_blocks
  s.Gc.live_words))
  end

module MAKE(TREE:sig type t 
		     val sons: t -> t array 
                     val custom_value: Gobject.g_type -> t -> column:int -> Gobject.basic
                     val column_list:GTree.column_list
	    end) = 
struct
  type custom_tree = 
      {finfo: TREE.t; 
       mutable sons: custom_tree array;
       mutable parent: custom_tree option;
       fidx: int (* invariant: parent.(fidx)==myself *) }
        
  let inbound i a = i>=0 && i<Array.length a
    
  (** The custom model itself *)
  class custom_tree_class column_list =
  object (self)
    inherit 
      [custom_tree,custom_tree,unit,unit] GTree.custom_tree_model column_list

    method custom_encode_iter cr = cr, (), ()
    method custom_decode_iter cr () () = cr

    val mutable num_roots : int = 0
    val mutable roots :  custom_tree array = [||]

    method custom_get_iter (path:Gtk.tree_path) : custom_tree option =
      let indices: int array  = GTree.Path.get_indices path in
      match indices with
      | [||] ->      
          None
      | _ -> 
          if inbound indices.(0) roots then
            let result = ref (roots.(indices.(0))) in
            try
              for depth=1 to Array.length indices - 1 do 
                let index = indices.(depth) in
                if inbound index !result.sons then       
                  result:=!result.sons.(index)
                else raise Not_found
              done;
              Some !result
            with Not_found -> 
              None
          else None

    method custom_get_path (row:custom_tree) : Gtk.tree_path =
      let current_row = ref row in
      let path = ref [] in
      while !current_row.parent <> None do
        path := !current_row.fidx::!path;
        current_row := match !current_row.parent with Some p -> p 
        | None -> assert false
      done;
      GTree.Path.create ((!current_row.fidx)::!path)

    method custom_value (t:Gobject.g_type) (row:custom_tree) ~column =
      TREE.custom_value t row.finfo ~column

    method custom_iter_next (row:custom_tree) : custom_tree option =
      let nidx = succ row.fidx in
      match row.parent with
      | None -> if inbound nidx roots then Some roots.(nidx)
        else None
      | Some parent ->
          if inbound nidx parent.sons then
            Some parent.sons.(nidx)
          else None

    method custom_iter_children (rowopt:custom_tree option) :custom_tree option =
      match rowopt with
      | None -> if inbound 0 roots then Some roots.(0) else None
      | Some row -> if inbound 0 row.sons then Some row.sons.(0) else None

    method custom_iter_has_child (row:custom_tree) : bool =
      Array.length row.sons  > 0 

    method custom_iter_n_children (rowopt:custom_tree option) : int =
      match rowopt with
      | None -> Array.length roots
      | Some row -> Array.length row.sons

    method custom_iter_nth_child (rowopt:custom_tree option) (n:int) 
      : custom_tree option =
      match rowopt with
      | None when inbound n roots -> Some roots.(n)
      | Some row when inbound n row.sons -> Some (row.sons.(n))
      | _ -> None 

    method custom_iter_parent (row:custom_tree) : custom_tree option =
      row.parent

    method append_tree (t:TREE.t) =
      let rec make_forest root sons = 
        Array.mapi 
          (fun i t -> let result = {finfo=t; fidx=i; parent = Some root; 
                                    sons = [||] }
           in 
           let sons = make_forest result (TREE.sons t) in
           result.sons<-sons;
           result)
          sons
      in
      let pos = num_roots in
      num_roots <- num_roots+1;
      let root = { finfo = t; sons = [||];
                   parent = None;
                   fidx = pos } 
      in
      
      let sons = make_forest root (TREE.sons t)
      in
      root.sons <- sons;
      roots <-
        Array.init num_roots (fun n -> if n = num_roots - 1 then root 
                              else roots.(n))

  end

  let custom_tree () = 
    new custom_tree_class TREE.column_list
end


module T=struct
  type leaf = {mutable checked: bool; mutable lname: string; }
  type t = Leaf of leaf |  Node of string* t list

  let sons t = match t with
  | Leaf _ -> [||]
  | Node (_,s)-> Array.of_list s

  (** The columns in our custom model *)
  let column_list = new GTree.column_list ;;
  let col_file = (column_list#add Gobject.Data.caml: t GTree.column);;
  let col_bool = column_list#add Gobject.Data.boolean;;
  let col_int = column_list#add Gobject.Data.int;;
  let col_is_leaf = column_list#add Gobject.Data.boolean;;
  

  let custom_value _ t ~column = 
    match column with
    | 0 -> (* col_file *) `CAML (Obj.repr t)
    | 1 -> (* col_bool *) `BOOL false
    | 2 -> (* col_int *) `INT 0
    | 3 -> (* col_is_leaf*) `BOOL (match t with Leaf _ -> true | _ -> false)
    | _ -> assert false

end

module MODEL=MAKE(T)

let nb = ref 0

let make_tree n p = 
  let rec aux p0 = 
    if p=p0 then 
      begin
        incr nb;
        T.Leaf {T.lname = "Leaf "^string_of_int !nb; checked = false}
      end
    else begin
      incr nb;
      let name = "Node "^string_of_int !nb in
      T.Node (name,aux_list n (succ p0))
    end
  and aux_list n p = 
    if n = 0 then []
    else aux p::aux_list (n-1) p
  in
  aux 0
    
let fill_model t =
  for i = 0 to 10000 do
    t#append_tree (make_tree 1 1)
  done



let create_view_and_model () : GTree.view =
  let custom_tree = MODEL.custom_tree () in
  fill_model custom_tree;
  let view = GTree.view ~fixed_height_mode:true ~model:custom_tree () in
  let renderer = GTree.cell_renderer_text [] in
  let col_name = GTree.view_column ~title:"Name" ~renderer:(renderer,[]) () in
  col_name#set_sizing `FIXED;
  col_name#set_fixed_width 150;
  col_name#set_cell_data_func 
    renderer
    (fun model row -> 
       try
	 let data = model#get ~row ~column:T.col_file in
	 match data with 
	 | T.Leaf {T.lname = s} | T.Node (s,_) -> 
	     renderer#set_properties [ `TEXT s ];
       with exn -> 
	 let s = GtkTree.TreePath.to_string (model#get_path row) in
	 Format.printf "Accessing %s, got '%s' @." s (Printexc.to_string exn));
  ignore (view#append_column col_name);
  
  let renderer = GTree.cell_renderer_toggle [] in
  let col_tog = GTree.view_column ~title:"Check" 
    ~renderer:(renderer,["visible", T.col_is_leaf])
    ()
  in
  col_tog#set_sizing `FIXED;
  col_tog#set_fixed_width 10;
  col_tog#set_cell_data_func 
    renderer
    (fun model row -> 
       try
	 let data = model#get ~row ~column:T.col_file in
	 match data with 
	 | T.Leaf {T.checked = b}  -> renderer#set_properties [ `ACTIVE b ]
         | _ -> ()
       with exn -> 
	 let s = GtkTree.TreePath.to_string (model#get_path row) in
	 Format.printf "Accessing %s, got '%s' @." s (Printexc.to_string exn));
  
  ignore(renderer#connect#toggled 
           (fun path -> 
              let row = custom_tree#custom_get_iter path in
              match row with 
              | Some {MODEL.finfo=T.Leaf l} -> 
                  l.T.checked <- not l.T.checked
              | _ -> ()));
  ignore (view#append_column col_tog);
  
  view

let _ =
  ignore (GtkMain.Main.init ());
  let window = GWindow.window ~width:200 ~height:400 () in
  ignore 
    (window#event#connect#delete 
       ~callback:(fun _ -> exit 0));
  let scrollwin = GBin.scrolled_window ~packing:window#add () in
  let view = create_view_and_model () in
  scrollwin#add view#coerce;
  window#show ();
  GtkMain.Main.main ()