From 769d3d04b974276ff3a39f140228c07f3332ac5c Mon Sep 17 00:00:00 2001
From: Benjamin Monate <benjamin.monate@cea.fr>
Date: Sat, 6 Sep 2008 19:02:02 +0000
Subject: [PATCH] new experimental filetree using lablgk custom models

---
 .make-clean-stamp                            |   2 +-
 Makefile.in                                  |  16 +-
 configure.in                                 |   7 +
 src/gui/.cvsignore                           |   1 +
 src/gui/filetree_custom.ml                   | 382 +++++++++++++++++++
 src/gui/{filetree.ml => filetree_default.ml} |  11 +-
 6 files changed, 407 insertions(+), 12 deletions(-)
 create mode 100644 src/gui/filetree_custom.ml
 rename src/gui/{filetree.ml => filetree_default.ml} (94%)

diff --git a/.make-clean-stamp b/.make-clean-stamp
index 00750edc07d..b8626c4cff2 100644
--- a/.make-clean-stamp
+++ b/.make-clean-stamp
@@ -1 +1 @@
-3
+4
diff --git a/Makefile.in b/Makefile.in
index 3bc90b6d287..b332085bf76 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -69,6 +69,7 @@ LABLGLADECC= @LABLGLADECC@ -hide-default
 HAS_GTKSOURCEVIEW=@HAS_GTKSOURCEVIEW@
 HAS_GNOMECANVAS=@HAS_GNOMECANVAS@
 HAS_LEGACY_GTKSOURCEVIEW=@HAS_LEGACY_GTKSOURCEVIEW@
+HAS_LABLGTK_CUSTOM_MODEL=@HAS_LABLGTK_CUSTOM_MODEL@
 OTAGS	= @OTAGS@
 DOT	= @DOT@
 
@@ -1464,6 +1465,19 @@ OPT_GUI_LIBS += lablgnomecanvas.cmxa
 GUICMO = view_graph/viewGraph view_graph/viewGraph_select
 endif
 
+src/gui/filetree.ml: Makefile
+
+ifeq ($(HAS_LABLGTK_CUSTOM_MODEL),yes)
+src/gui/filetree.ml: src/gui/filetree_custom.ml
+	$(CP) $< $@
+	@chmod -w $@
+else
+src/gui/filetree.ml: src/gui/filetree_default.ml
+	$(CP) $< $@
+	@chmod -w $@
+endif
+GENERATED += src/gui/filetree.ml
+
 GUICMO += gtk_helper source_viewer source_manager warning_manager \
 	 pretty_source filetree design project_manager journal_manager \
 	 about_dialog gui_boot
@@ -1901,7 +1915,7 @@ display_dependencies: datatype_dependencies.svg computation_dependencies.svg
 
 .PHONY: install
 install:
-	$(MAKE) -C ocamlgraph install
+#	$(MAKE) -C ocamlgraph install
 	$(MKDIR) $(DESTDIR)$(BINDIR)
 	$(MKDIR) $(DESTDIR)$(datadir)
 	$(MKDIR) $(DESTDIR)$(plugindir)
diff --git a/configure.in b/configure.in
index 0dafa305495..010c8d28812 100644
--- a/configure.in
+++ b/configure.in
@@ -822,6 +822,12 @@ configure_library \
   $OCAMLLIB/lablgtk2/lablgtk.cma \
   "lablgtk2 not found."
 
+HAS_LABLGTK_CUSTOM_MODEL=no
+AC_ARG_ENABLE(custommodel,
+[  --enable-custommodel
+     use a custom tree model in GUI. Need to have a recent lablgtk version],
+HAS_LABLGTK_CUSTOM_MODEL=yes,HAS_LABLGTK_CUSTOM_MODEL=no)
+
 # Gtksourceview
 ###############
 REQUIRE_GTKSOURCEVIEW=
@@ -1117,6 +1123,7 @@ AC_SUBST(HAS_LABLGTK)
 AC_SUBST(HAS_GTKSOURCEVIEW)
 AC_SUBST(HAS_LEGACY_GTKSOURCEVIEW)
 AC_SUBST(HAS_GNOMECANVAS)
+AC_SUBST(HAS_LABLGTK_CUSTOM_MODEL)
 AC_SUBST(LABLGLADECC)
 AC_SUBST(HAS_LABLGLADECC)
 AC_SUBST(JCCMO)
diff --git a/src/gui/.cvsignore b/src/gui/.cvsignore
index ec33ab274d9..80069ca3ca7 100644
--- a/src/gui/.cvsignore
+++ b/src/gui/.cvsignore
@@ -4,4 +4,5 @@ version.ml
 *.annot
 *.cm*
 *.o
+filetree.ml
 .DS_Store
diff --git a/src/gui/filetree_custom.ml b/src/gui/filetree_custom.ml
new file mode 100644
index 00000000000..90663c63a36
--- /dev/null
+++ b/src/gui/filetree_custom.ml
@@ -0,0 +1,382 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  This file is part of Frama-C.                                         *)
+(*                                                                        *)
+(*  Copyright (C) 2007-2008                                               *)
+(*    CEA (Commissariat à l'Énergie Atomique)                             *)
+(*                                                                        *)
+(*  you can redistribute it and/or modify it under the terms of the GNU   *)
+(*  Lesser General Public License as published by the Free Software       *)
+(*  Foundation, version 2.1.                                              *)
+(*                                                                        *)
+(*  It is distributed in the hope that it will be useful,                 *)
+(*  but WITHOUT ANY WARRANTY; without even the implied warranty of        *)
+(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *)
+(*  GNU Lesser General Public License for more details.                   *)
+(*                                                                        *)
+(*  See the GNU Lesser General Public License version 2.1                 *)
+(*  for more details (enclosed in the file licenses/LGPLv2.1).            *)
+(*                                                                        *)
+(**************************************************************************)
+
+open Extlib
+open Ast_info
+open Cil_types
+open Cil
+open CilE
+open Format
+open Db
+open Db_types
+open Pretty_source
+open Gtk_helper
+open Cilutil
+
+class type t =  object 
+  method model : GTree.model_filter
+  method set_file_attribute: ?strikethrough:bool -> ?visible:bool -> ?text:string -> string -> unit
+  method set_global_attribute: ?strikethrough:bool -> ?visible:bool -> ?text:string -> Cil_types.varinfo -> unit
+  method add_select_function : 
+    (was_activated:bool -> activating:bool -> Cil_types.global list -> unit) -> unit
+  method select_global : Cil_types.varinfo -> unit
+  method view : GTree.view
+  method reset : unit -> t
+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 as parent
+
+    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 custom_foreach f = 
+    let f p _ = f p (match self#custom_get_iter p with 
+                     | Some v -> v
+                     | None -> assert false)
+    in
+    parent#foreach f
+     
+    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 MYTREE = struct
+type storage = { mutable name : string; 
+                 mutable globals: global array;
+                 mutable strikethrough: bool;
+                 mutable visible: bool }
+
+
+type t = File of storage*t list | Global of storage
+
+let sons t = match t with
+| File (_,s) -> Array.of_list s
+| Global _ -> [||]
+
+let get_storage t = match t with
+| File (s,_) -> s
+| Global s -> s
+
+let default_storage s globals = 
+ {
+  name = s;
+  globals = globals;
+  strikethrough = false;
+  visible = true
+ }
+
+  (* Set up the columns *)
+let column_list = new GTree.column_list
+let filename_col = column_list#add Gobject.Data.string
+let (glob_col:global list GTree.column) = column_list#add Gobject.Data.caml
+let strikethrough_col = column_list#add Gobject.Data.boolean
+let visible_col = column_list#add Gobject.Data.boolean
+
+let custom_value _ t ~column = 
+    match column with
+    | 0 -> (* filename_col *) `STRING (Some (get_storage t).name)
+    | 1 -> (* glob_col *) `CAML (Obj.repr ((get_storage t).globals))
+    | 2 -> (* strikethrough_col *) `BOOL (get_storage t).strikethrough
+    | 3 -> (* visible_col *)  `BOOL (get_storage t).visible
+    | _ -> assert false
+
+let make_file fname : t = 
+ let globs = Globals.FileIndex.find fname in
+ let storage = default_storage fname (Array.of_list globs) in
+ let sons =  List.fold_left
+   (fun acc glob -> match glob with
+    | GFun ({svar={vname=name}},_) -> 
+       (Global(default_storage name [|glob|]))::acc
+    | _ -> acc)
+    []
+    globs
+ in
+ File (storage,sons)
+
+end
+
+module MODEL=MAKE(MYTREE)
+
+let rec make (tree_view:GTree.view) = 
+
+  (** Model Part *)
+
+  let treestore = MODEL.custom_tree () in
+  
+  (** Let's fill the model with all files. *) 
+  List.iter 
+    (fun s -> treestore#append_tree (MYTREE.make_file s))
+    (List.sort Pervasives.compare (Globals.FileIndex.get_files ()));
+
+  (** Let's build the table from cil standard types to rows in the model *)
+
+  (** These tables contain the path (in the treeview of file names)
+      to the global (reps. filename) *)
+  let global_path_tbl = Cilutil.VarinfoHashtbl.create 17 in
+  let file_path_tbl = Hashtbl.create 17 in
+
+  let cache path row = 
+      (match row.MODEL.finfo with 
+      | MYTREE.File (storage,_) -> 
+         Hashtbl.add file_path_tbl storage.MYTREE.name (path,row)
+      | MYTREE.Global storage -> 
+          let vi = 
+	   match storage.MYTREE.globals with 
+	   | [| GFun ({svar=vi},_) |] -> vi
+           | _ -> assert false
+          in
+          Cilutil.VarinfoHashtbl.add global_path_tbl vi (path,row));
+      false
+  in
+  treestore#custom_foreach cache;
+
+
+  let model_filtered = GTree.model_filter (treestore:>GTree.model) in
+  model_filtered#set_visible_column MYTREE.visible_col;
+
+  (** View part *)
+  let source_column = GTree.view_column ~title:"Source file" () in
+  let str_renderer = GTree.cell_renderer_text [] in
+  source_column#pack str_renderer;
+  source_column#add_attribute str_renderer "text" MYTREE.filename_col;
+  source_column#add_attribute str_renderer "strikethrough" MYTREE.strikethrough_col;
+  
+  let _ = tree_view#append_column source_column in
+
+  tree_view#set_model (Some (model_filtered:>GTree.model));
+
+  let set_row ?strikethrough ?visible ?text (path,raw_row) = 
+    let row = raw_row.MODEL.finfo in
+    may 
+     (fun b -> (MYTREE.get_storage row).MYTREE.visible<- b ) 
+     visible;
+    may 
+     (fun b -> (MYTREE.get_storage row).MYTREE.strikethrough <- b )
+     strikethrough;
+    may 
+     (fun b -> (MYTREE.get_storage row).MYTREE.name <- b )
+     text;
+     treestore#custom_row_changed path raw_row
+      
+  in
+  let set_file_attribute ?strikethrough ?visible ?text filename = 
+    set_row ?strikethrough ?visible ?text 
+      (Hashtbl.find file_path_tbl filename)
+  in
+  let set_global_attribute ?strikethrough ?visible ?text global = 
+    set_row ?strikethrough ?visible ?text
+      (Cilutil.VarinfoHashtbl.find global_path_tbl global)  
+  in
+  let myself = object
+    
+    val mutable select_functions = []
+
+    method view = tree_view
+    method model = model_filtered
+    method get_select_functions = select_functions
+    method set_file_attribute = set_file_attribute
+    method set_global_attribute = set_global_attribute
+    method set_row_attribute = set_row
+    method reset () = 
+      (** Cleanup the existing tree view *)
+      tree_view#set_model None;
+      (try
+         ignore (tree_view#remove_column (tree_view#get_column 0));
+         ignore (tree_view#remove_column (tree_view#get_column 0))
+       with Gpointer.Null -> ());
+      let myself = make tree_view in
+      List.iter myself#add_select_function select_functions;
+      myself
+	
+    val mutable activated_path = "" (* prevent double selection *)
+    method enable_select_functions =
+      let select path deactivating =
+	let fail e = Format.eprintf 
+          "selector handler got an internal error, please report: %s@." 
+          (Printexc.to_string e)
+	in
+	try
+          if !Gtk_helper.gui_unlocked then
+	    let path_s = GTree.Path.to_string path in
+	    let was_activated = activated_path = path_s in
+	    if not was_activated && not deactivating then 
+	      activated_path <- path_s;
+	    let {MODEL.finfo=t} = 
+              match treestore#custom_get_iter path  with
+              | Some s ->s | None -> assert false
+            in
+	    let globs = (MYTREE.get_storage t).MYTREE.globals in
+	    (*        	  prerr_endline 
+			  ("Select function "
+			  ^ (if (not deactivating) then "true" else "false") 
+			  ^ " on "^path_s);*)
+            let globs = Array.to_list globs in
+	    List.iter 
+	      (fun f -> 
+		 try 
+		   f ~was_activated ~activating:(not deactivating) globs
+		 with e -> fail e)
+	      select_functions;
+	    true
+          else false
+	with e ->
+          Format.eprintf "gui could not select row in filetree, please \
+report: %s@."
+	    (Printexc.to_string e);
+          true
+      in
+      tree_view#selection#set_select_function select
+
+    method add_select_function f = 
+      select_functions <- select_functions@[f];
+
+    method select_global vi = 
+      try 
+        let path = 
+          fst (Cilutil.VarinfoHashtbl.find global_path_tbl vi)
+        in
+        expand_to_path tree_view path;
+        tree_view#selection#select_path path
+      with Not_found -> ()
+  end
+  in 
+  myself#enable_select_functions;
+  (myself:>t)
+
+
+(*
+Local Variables:
+compile-command: "LC_ALL=C make -C ../.. -j"
+End:
+*)
diff --git a/src/gui/filetree.ml b/src/gui/filetree_default.ml
similarity index 94%
rename from src/gui/filetree.ml
rename to src/gui/filetree_default.ml
index b7c85d0f777..f284a7a39e9 100644
--- a/src/gui/filetree.ml
+++ b/src/gui/filetree_default.ml
@@ -31,16 +31,6 @@ open Pretty_source
 open Gtk_helper
 open Cilutil
 
-class type t =  object 
-  method model : GTree.model_filter
-  method set_file_attribute: ?strikethrough:bool -> ?visible:bool -> ?text:string -> string -> unit
-  method set_global_attribute: ?strikethrough:bool -> ?visible:bool -> ?text:string -> Cil_types.varinfo -> unit
-  method add_select_function : 
-    (was_activated:bool -> activating:bool -> Cil_types.global list -> unit) -> unit
-  method select_global : Cil_types.varinfo -> unit
-  method view : GTree.view
-  method reset : unit -> t
-end
 
 let rec make (tree_view:GTree.view) = 
 
@@ -219,6 +209,7 @@ report: %s@."
   myself#enable_select_functions;
   (myself:>t)
 
+
 (*
 Local Variables:
 compile-command: "LC_ALL=C make -C ../.. -j"
-- 
GitLab