Pastey Restoring open categories in Tree views

------------------------------------------------------------------------
--# GtkTreeStore - saving and restoring "open" categories;  
------------------------------------------------------------------------  
-- To restore opened categories after loading subcat data, 
-- call MapExpandedRows() before loading the data, 
-- then ExpandSelected() after loading. 
-- This uses tree_row_references so that the list can be sorted 
-- and re-ordered without losing track of the expanded rows. 
-- path will not work for this purpose, because it changes when 
-- data is sorted, added, or reordered.  
-- Interactive search is enabled on col1, called by ctl-f 
-- but only items visible on the screen will be searched.  
 
include GtkEngine.e  
  
object store = create(GtkTreeStore,{gSTR,gSTR,gSTR})  
 
sequence os = { 
 
    {"Windows","An Operating ? System",  
        {"Bill Gates","Zillionaire"} -- {1,1} 
    },  
    {"Linux","An Operating ! System", -- {2} 
        {"Linus Torvalds","Genius"}, -- {2,1} 
        {"Cast of thousands","Sweathogs"} -- {2,2} 
    },  
    {"Mac","A Religion",  
        {"Steve Jobs"}, -- {3,1} 
        {"The Woz",   -- {3,2} 
            {"Billy","The kid", -- {3,2,1}  
                {"Bowser","Good Dog!"}, -- {3,2,1,1}  
                {"Fido", 
                  {"Ball"}, 
                  {"Chew toy"}, 
                  {"Bone"} 
                } -- sub 2.2.2   -- {3,2,1,2} 
            }, 
            {  
             "Susan","The other kid", -- {3,2,2} 
                {"Grumpy Cat","Sharp on all 4 corners!", -- {3,2,2,1} 
                    {"Kitten 1",  -- {3,2,2,1,1} 
                        {"Toy mouse"}, -- {3,2,2,1,1,1} 
                        {"Real mouse","A bit nervous"} -- {3,2,2,1,1,2} 
                    }, 
                    {"Kitten 2"}, 
                    {"Kitten 3"} -- {3,2,2,1,3} 
                }                     
            } 
        }  
    } 
 } 
  
 
set(store,"data",os)  
  
constant   
    col1 = create(GtkColumn,"title=OS,type=text,markup=1,sort_column_id=1"),  
    col2 = create(GtkColumn,"title=Notes,type=text,markup=2,sort_column_id=2")    
constant tv  = create(GtkTreeView,{  
    {"model",store},  
    {"append columns",{col1,col2}},  
    {"enable tree lines",TRUE},  
    {"rules hint",TRUE},   
    {"hover expand",FALSE}, -- no, wait for mouse click; 
    {"enable search",TRUE}, -- typeahead;  
    {"search column",1}, 
    {"collapse all",TRUE}, -- start un-expanded;  
    $})  
 
atom selection = get(tv,"selection")   
object expanded_rows = 0 -- store a sequence of tree row references; 
       
constant   
    win = create(GtkWindow,"size=250x400,border=10,$destroy=Quit"),  
    panel = create(GtkBox,"orientation=vertical,spacing=10"), 
    btn4 = create(GtkButton,"#Map Expanded","MapExpandedRows"),  
    btn3 = create(GtkButton,"#Restore Expanded","ExpandSelected"), 
    btn2 = create(GtkButton,"#Collapse All","Collapse"),  
    btn1 = create(GtkButton,"gtk-quit","Quit"),  
    box = create(GtkButtonBox), 
    lbl = create(GtkLabel) 
  
    add(win,panel)  
    pack(panel,tv,TRUE,TRUE,5)  
    add(panel,lbl) 
    add(box,{btn1,btn4,btn2,btn3})  
    pack(panel,-box)  
      
show_all(win)  
    connect(tv,"row-activated","ShowSelection") -- double click; 
    --set(win,"interactive debugging",1) -- TRY ME! 
main()  
 
----------------------------------   
global function ShowSelection() --   
----------------------------------   
atom iter = allocate(32), model = allocate(32), parent = allocate(32)   
  
object path, result    
if get(selection,"selected",model,iter) then   
	  
    result = get(store,"value",iter,1)   
     
    -- if you just want to show the clicked item:   
        display("You clicked []",{result})  
     
    -- FYI, following 6 lines just convert to readable form 
    -- for display. Not required for a functional program.	 
        path = get(store,"path",iter) 
        object human_path = split(get(path,"to string"),':') -- e.g: 3:2:2:1:2 
        for i = 1 to length(human_path) do  
            human_path[i] = to_number(human_path[i]) + 1 -- zero based; 
        end for 
        display(human_path) 
    -- end FYI 
         
     -- obtain the full path, working upward from the clicked item: 
	while gtk_func("gtk_tree_model_iter_parent",{P,P,P},{store,parent,iter}) do   
            result = get(store,"value",parent,1)  & " -> " & result   
            iter = gtk_func("gtk_tree_iter_copy",{P},{parent})   
	end while  
 
	Info(,,result) 
	-- e.g: Mac -> The Woz -> Billy -> Fido   
 
end if   
  
return 1   
end function   
  
-------------------------------- 
global function ExpandSelected() 
-------------------------------- 
object path 
if atom(expanded_rows) and expanded_rows = 0 then  
   return Warn(,,"No selected rows to expand") 
end if 
for i = 1 to length(expanded_rows) do 
   path = get(expanded_rows[i],"path") 
   --set(tv,"expand to path",path) 
   set(tv,"expand row",path,0) 
end for 
return 1 
end function 
 
--------------------------------- 
global function MapExpandedRows() 
--------------------------------- 
expanded_rows = 0 -- empty; 
 -- for each:    
    get(tv,"map expanded rows",call_back(routine_id("MapExpRows"))) 
 -- will iterate through rows, adding a ref to each expanded row 
 -- to the expanded_rows list. 
 return 1 
 end function 
  
 -------------------------------------------------------- 
 function MapExpRows(atom view, object path, object data) 
 -------------------------------------------------------- 
 atom ref = create(GtkTreeRowReference,store,path) 
 if get(ref,"valid") then 
   expanded_rows &= ref -- add it to the list; 
 end if 
 return 1 
 end function 
  
--------------------------- 
global function Collapse()  
--------------------------- 
set(tv,"collapse all")  
return 1  
end function 

1. Comment by irv Aug 30, 2019

comments like {3,2} indicate the path to the item - e.g. 3 = Mac category, 2 = the Woz subcategory.

Mac -> the Woz

These are just there to give an idea how GTK organizes these things (I entered them by hand, may be typos). GTK calls these "paths". Obviously, if you sort the list alphabetically, the categories will then be Linux, Mac, and Windows, so {3} will now point to Windows instead of Mac. Pretty useless.

So there's a way to convert the path into a reference, which can be saved in a Eu list, and the ref will continue to point to Mac -> Woz regardless of how the list is reorganized. The ref must be converted back into a path before use.