12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007(**************************************************************************)(* *)(* SPDX-License-Identifier LGPL-2.1 *)(* Copyright (C) *)(* CEA (Commissariat à l'énergie atomique et aux énergies alternatives) *)(* *)(**************************************************************************)openCil_typesopenCil_datatypeopenGtk_helper(* To debug performance related to height of lines *)letfixed_height=falsetypefiletree_node=|FileofFilepath.t*Cil_types.globallist|GlobalofCil_types.globalletsame_noden1n2=matchn1,n2with|File(f1,_),File(f2,_)->f1=f2|Globalg1,Globalg2->Cil_datatype.Global.equalg1g2|_->falselet_pretty_nodefmt=function|File(s,_)->Filepath.prettyfmts|Global(GFun({svar=vi},_)|GVar(vi,_,_)|GFunDecl(_,vi,_)|GVarDecl(vi,_))->Format.fprintffmt"%s"vi.vname|_->()(* Fetches the internal (hidden) GtkButton of the column header.
Experimentally, to first force gtk to create a header button for the column,
you should:
- add first the column to the table;
- explicitly set the widget of the header (and this widget should not be a
button itself).
Otherwise, this function will return None. *)letget_column_header_button(col:GTree.view_column)=letrecget_button=function|None->None|Somew->ifw#misc#get_type="GtkButton"thenletbut_props=GtkButtonProps.Button.castw#as_widgetinSome(newGButton.buttonbut_props)elseget_buttonw#misc#parentinget_buttoncol#widgetclasstypet=objectmethodmodel:GTree.modelmethodflat_mode:boolmethodset_file_attribute:?strikethrough:bool->?text:string->Filepath.t->unitmethodset_global_attribute:?strikethrough:bool->?text:string->varinfo->unitmethodadd_global_filter:text:string->key:string->(Cil_types.global->bool)->(unit->bool)*GMenu.check_menu_itemmethodget_file_globals:Filepath.t->(string*bool)listmethodfind_visible_global:string->Cil_types.globaloptionmethodadd_select_function:(was_activated:bool->activating:bool->filetree_node->unit)->unitmethodappend_text_column:title:string->tooltip:string->visible:(unit->bool)->text:(global->string)->?sort:(global->global->int)->([`Visibility|`Contents]->unit)methodappend_pixbuf_column:title:string->(globallist->GTree.cell_properties_pixbuflist)->(unit->bool)->([`Visibility|`Contents]->unit)methodselect_global:Cil_types.global->boolmethodselected_globals:Cil_types.globallistmethodview:GTree.viewmethodreset:unit->unitmethodregister_reset_extension:(t->unit)->unitmethodrefresh_columns:unit->unitend(* crude way to to debug inefficiencies with the gtk interface *)(*let c = ref 0
let gtk s = incr c; Format.printf "[%d %s]@." !c s
*)moduleMAKE(TREE:sigtypetvalsons:t->tarrayend)=structtypecustom_tree={finfo:TREE.t;mutablesons:custom_treearray;parent:custom_treeoption;fidx:int(* invariant: parent.(fidx)==myself *)}letinboundia=i>=0&&i<Array.lengtha(** The custom model itself *)classcustom_tree_classcolumn_list=object(self)inherit[custom_tree,custom_tree,unit,unit]GTree.custom_tree_modelcolumn_listasparentmethodcustom_encode_itercr=cr,(),()methodcustom_decode_itercr()()=crmethod!custom_flags=[`ITERS_PERSIST]valmutablenum_roots:int=0valmutableroots:custom_treearray=[||]methodget_roots=rootsmethodcustom_get_iter(path:Gtk.tree_path):custom_treeoption=letindices:intarray=GTree.Path.get_indicespathinmatchindiceswith|[||]->None|_->ifinboundindices.(0)rootsthenletresult=ref(roots.(indices.(0)))intryfordepth=1toArray.lengthindices-1doletindex=indices.(depth)inifinboundindex!result.sonsthenresult:=!result.sons.(index)elseraiseNot_founddone;Some!resultwithNot_found->NoneelseNonemethodcustom_get_path(row:custom_tree):Gtk.tree_path=letcurrent_row=refrowinletpath=ref[]inwhile!current_row.parent<>Nonedopath:=!current_row.fidx::!path;current_row:=match!current_row.parentwithSomep->p|None->assertfalsedone;GTree.Path.create((!current_row.fidx)::!path)methodcustom_value(_t:Gobject.g_type)(_row:custom_tree)~column:_=assertfalsemethodcustom_iter_next(row:custom_tree):custom_treeoption=letnidx=succrow.fidxinmatchrow.parentwith|None->ifinboundnidxrootsthenSomeroots.(nidx)elseNone|Someparent->ifinboundnidxparent.sonsthenSomeparent.sons.(nidx)elseNonemethodcustom_iter_children(rowopt:custom_treeoption):custom_treeoption=matchrowoptwith|None->ifinbound0rootsthenSomeroots.(0)elseNone|Somerow->ifinbound0row.sonsthenSomerow.sons.(0)elseNonemethodcustom_iter_has_child(row:custom_tree):bool=Array.lengthrow.sons>0methodcustom_iter_n_children(rowopt:custom_treeoption):int=matchrowoptwith|None->Array.lengthroots|Somerow->Array.lengthrow.sonsmethodcustom_iter_nth_child(rowopt:custom_treeoption)(n:int):custom_treeoption=matchrowoptwith|Nonewheninboundnroots->Someroots.(n)|Somerowwheninboundnrow.sons->Some(row.sons.(n))|_->Nonemethodcustom_iter_parent(row:custom_tree):custom_treeoption=row.parentmethodcustom_foreachf=letfp_=fp(matchself#custom_get_iterpwith|Somev->v|None->assertfalse)inparent#foreachfmethodset_tree(fill_cache:intlist->custom_tree->unit)(t:TREE.tlist)=num_roots<-0;letrecmake_forestposrootsons=Array.mapi(funit->letresult={finfo=t;fidx=i;parent=Someroot;sons=[||]}infill_cache(i::pos)result;letsons=make_forest(i::pos)result(TREE.sonst)inresult.sons<-sons;result)sonsinletnew_roots=List.map(funt->letpos=num_rootsinnum_roots<-num_roots+1;letroot={finfo=t;sons=[||];parent=None;fidx=pos}infill_cache[pos]root;letsons=make_forest[pos]root(TREE.sonst)inroot.sons<-sons;root)tinroots<-Array.of_listnew_rootsmethodclear()=self#custom_foreach(funp_->self#custom_row_deletedp;false)endletcustom_tree()=newcustom_tree_class(newGTree.column_list)endmoduleMYTREE=structtypestorage={mutablename:string;globals:globalarray;mutablestrikethrough:bool}typet=MFileofstorage*tlist|MGlobalofstorage(* Sort order of the rows. *)typesort_order=|Ascending(* Ascending alphabetical order on names. *)|Descending(* Descending alphabetical order on names. *)|Customof(global->global->int)(* Custom order on globals. *)letinverse_sort=function|Ascending->Descending|Descending->Ascending|Customsort->Custom(fungh->sorthg)letstorage_type=function|MFile(s,_)->File(Filepath.of_strings.name,Array.to_lists.globals)|MGlobal{globals=[|g|]}->Globalg|MGlobal_->assertfalseletsonst=matchtwith|MFile(_,s)->Array.of_lists|MGlobal_->[||]letsons_info=function|MFile(_,l)->List.map(function|MGlobal{name=n;strikethrough=st}->(n,st)|MFile_->assertfalse(* should not happen, a file is
never under a file in the tree *))l|MGlobal_->[]letget_storaget=matchtwith|MFile(s,_)->s|MGlobals->sletis_function_global=function|GFun_|GFunDecl_->true|_->falseletis_defined_global=function|GFun_|GVar_|GEnumTag_|GCompTag_->true|_->falseletis_undefined_global=function|GFunDecl_|GVarDecl_|GEnumTagDecl_|GCompTagDecl_->true|_->falseletis_builtin_globalg=Ast_attributes.contains"FC_BUILTIN"(Cil_datatype.Global.attrg)letcomes_from_sharefilename=letpath=Filepath.of_stringfilenameinFilepath.is_relative~base:System_config.Share.mainpathletis_functiont=matchtwith|MFile_->false|MGlobal{globals=[|g|]}->is_function_globalg|MGlobal_->falseletdefault_storagesglobals={name=s;globals=globals;strikethrough=false;}letglobal_names=Pretty_utils.to_stringPrinter.pp_varnamesletextension_namee=Pretty_utils.to_stringPrinter.pp_short_extendedeletga_name=function|Dfun_or_pred(li,_)->Some(global_nameli.l_var_info.lv_name)|Dvolatile_->Some"volatile clause"|Daxiomatic(s,_,_,_)->Some(global_names)|Dmodule(s,_,_,_,_)->Some(global_names)|Dtype(lti,_)->Some(global_namelti.lt_name)|Dlemma(s,_,_,_,_,_)->Some(global_names)|Dinvariant(li,_)->Some(global_nameli.l_var_info.lv_name)|Dtype_annot(li,_)->Some(global_nameli.l_var_info.lv_name)|Dmodel_annot(mf,_)->Some(global_namemf.mi_name)|Dextended(e,_,_)->Some("ACSL extension "^(extension_namee))letmake_list_globalshidesort_orderglobs=(* Association list binding names to globals. *)letl=List.fold_left(* Correct the function sons_info above if a [File] constructor can
appear in [sons] *)(funaccglob->matchglobwith|GFun({svar=vi},_)|GVar(vi,_,_)|GVarDecl(vi,_)|GFunDecl(_,vi,_)->(* Only display the last declaration/definition *)ifhideglob||(not(Ast.is_def_or_last_declglob))thenaccelse((global_namevi.vname),glob)::acc|GAnnot(ga,_)->ifhideglobthenaccelse(matchga_namegawith|None->acc|Somes->(s,glob)::acc)|_->acc)[]globsinletsort=matchsort_orderwith|Ascending->fun(s1,_)(s2,_)->Extlib.compare_ignore_cases1s2|Descending->fun(s1,_)(s2,_)->Extlib.compare_ignore_cases2s1|Customsort->fun(name1,g1)(name2,g2)->letc=sortg1g2inifc=0thenExtlib.compare_ignore_casename1name2elsecinletsorted=List.sortsortlinList.map(fun(name,g)->MGlobal(default_storagename[|g|]))sortedletmake_filehidesort_order(path,globs)=letstorage=default_storage(Filepath.to_string_abspath)(Array.of_listglobs)inletsons=make_list_globalshidesort_orderglobsinstorage,sonsendmoduleMODEL=MAKE(MYTREE)(* Primitives to handle the filetree menu (which allows to hide some
entries) *)moduleMenusHide=structlethidekey()=Configuration.find_bool~default:falsekeyletmenu_item(menu:GMenu.menu)~label~key=letmi=GMenu.check_menu_item~label()inmi#set_active(hidekey());menu#add(mi:>GMenu.menu_item);miletmi_set_callback(mi:GMenu.check_menu_item)~keyreset=mi#connect#toggled~callback:(fun()->letv=mi#activeinConfiguration.setkey(Configuration.ConfBoolv);reset())endletkey_flat_mode="filetree_flat_mode"letflat_mode=MenusHide.hidekey_flat_modeletkey_hide_stdlib="filetree_hide_stdlib"lethide_stdlib=MenusHide.hidekey_hide_stdlibmoduleState=struct(* Caching between what is selected in the filetree and the gtk to the
gtk node *)typecache={cache_files:(intlist*MODEL.custom_tree)Filepath.Hashtbl.t;cache_vars:(intlist*MODEL.custom_tree)Varinfo.Hashtbl.t;cache_global_annot:(intlist*MODEL.custom_tree)Global_annotation.Hashtbl.t;}letdefault_cache()={cache_files=Filepath.Hashtbl.create17;cache_vars=Varinfo.Hashtbl.create17;cache_global_annot=Global_annotation.Hashtbl.create17;}letpath_from_nodecache=function|File(s,_)->(trySome(Filepath.Hashtbl.findcache.cache_filess)withNot_found->None)|Global(GFun({svar=vi},_)|GVar(vi,_,_)|GVarDecl(vi,_)|GFunDecl(_,vi,_))->(trySome(Varinfo.Hashtbl.findcache.cache_varsvi)withNot_found->None)|Global(GAnnot(ga,_))->(trySome(Global_annotation.Hashtbl.findcache.cache_global_annotga)withNot_found->None)|_->Noneletfill_cachecache(path:intlist)row=matchrow.MODEL.finfowith|MYTREE.MFile(storage,_)->Filepath.Hashtbl.addcache.cache_files(Filepath.of_stringstorage.MYTREE.name)(path,row)|MYTREE.MGlobalstorage->matchstorage.MYTREE.globalswith(* Only one element in this array by invariant: this is a leaf*)|[|GFun({svar=vi},_)|GVar(vi,_,_)|GVarDecl(vi,_)|GFunDecl(_,vi,_)|]->Varinfo.Hashtbl.addcache.cache_varsvi(path,row)|[|GAnnot(ga,_)|]->Global_annotation.Hashtbl.addcache.cache_global_annotga(path,row)|_->(* no cache for other globals yet *)()(* Extract Cil globals. We remove builtins that are not used in this project,
as well as files that do not contain anything afterwards *)letcil_files()=letfiles=Globals.FileIndex.get_files()inletglobals_of_filef=letall=List.rev(Globals.FileIndex.get_symbolsf)inletis_unused=function|GFun({svar=vi},_)|GFunDecl(_,vi,_)|GVar(vi,_,_)|GVarDecl(vi,_)->Cil_builtins.is_unused_builtinvi|_->falseinletgls=List.filter(fung->not@@is_unusedg)allinifgls=[]thenNoneelseSome(f,gls)inList.filter_mapglobals_of_filefiles(** Make and fill the custom model with default values. *)letcomputehide_filterssort_order=lethideg=List.exists(funfilter->filterg)hide_filtersinletmodel=MODEL.custom_tree()inletcache=default_cache()in(* Let's fill up the model with all files and functions. *)letfiles=cil_files()inbeginifflat_mode()thenletlist=List.concat(List.mapsndfiles)inletfiles=MYTREE.make_list_globalshidesort_orderlistinmodel#set_tree(fill_cachecache)fileselseletsorted_files=(List.sort(fun(p1,_)(p2,_)->(* invert comparison order due to inversion by fold_left below *)Filepath.compare_prettyp2p1)files)inletfiles=List.fold_left(funaccv->letname,globals=MYTREE.make_filehidesort_ordervinifnot((hide_stdlib())&&(MYTREE.comes_from_sharename.MYTREE.name))then(MYTREE.MFile(name,globals))::accelseacc)[]sorted_filesinmodel#set_tree(fill_cachecache)files;end;model,cacheend(* Definitions related to 'Find text' using [visible_nodes] *)exceptionFound_globalofCil_types.globalexceptionGlobal_not_foundletmake(tree_view:GTree.view)=(* Menu for configuring the filetree *)letmenu=GMenu.menu()in(* Buttons to show/hide variables and/or functions *)letkey_hide_variables="filetree_hide_variables"inletkey_hide_functions="filetree_hide_functions"inletkey_hide_defined="filetree_hide_defined"inletkey_hide_undefined="filetree_hide_undefined"inletkey_hide_builtins="filetree_hide_builtins"inletkey_hide_annotations="filetree_hide_annotattions"inlethide_variables=MenusHide.hidekey_hide_variablesinlethide_functions=MenusHide.hidekey_hide_functionsinlethide_defined=MenusHide.hidekey_hide_definedinlethide_undefined=MenusHide.hidekey_hide_undefinedinlethide_builtins=MenusHide.hidekey_hide_builtinsinlethide_annotations=MenusHide.hidekey_hide_annotationsinletinitial_filterg=lethide_kind=function|GFun_|GFunDecl_->hide_functions()|GVar_|GVarDecl_->hide_variables()|GAnnot_->hide_annotations()|_->falseinhide_kindg||(MYTREE.is_builtin_globalg&&hide_builtins())||(Cil.global_is_in_libcg&&hide_stdlib())||(MYTREE.is_defined_globalg&&hide_defined())||(MYTREE.is_undefined_globalg&&hide_undefined())inletinitial_sort_order=MYTREE.Ascendinginletmhide_variables=MenusHide.menu_itemmenu~label:"Hide variables"~key:key_hide_variablesinletmhide_functions=MenusHide.menu_itemmenu~label:"Hide functions"~key:key_hide_functionsinletmhide_stdlib=MenusHide.menu_itemmenu~label:"Hide stdlib"~key:key_hide_stdlibinletmhide_defined=MenusHide.menu_itemmenu~label:"Hide defined symbols"~key:key_hide_definedinletmhide_undefined=MenusHide.menu_itemmenu~label:"Hide undefined symbols"~key:key_hide_undefinedinletmhide_builtins=MenusHide.menu_itemmenu~label:"Hide built-ins"~key:key_hide_builtinsinletmhide_annotations=MenusHide.menu_itemmenu~label:"Hide global annotations"~key:key_hide_annotationsinlet()=menu#add(GMenu.separator_item():>GMenu.menu_item)inletmflat_mode=MenusHide.menu_itemmenu~label:"Flat mode"~key:key_flat_modein(* Initial filetree nodes to display *)letinit_model,init_path_cache=State.compute[initial_filter]initial_sort_orderinletset_rowmodel?strikethrough?text(path,raw_row)=letrow=raw_row.MODEL.finfoinOption.iter(funb->(MYTREE.get_storagerow).MYTREE.strikethrough<-b)strikethrough;Option.iter(funb->(MYTREE.get_storagerow).MYTREE.name<-b)text;iffalsethenmodel#custom_row_changed(GTree.Path.create(List.revpath))raw_rowinletmyself=object(self)(* Invariant: the filetree is always completely rebuilt when the project
changes, because Design calls [reset] below. *)(* GTK model of the filetree *)valmutablemodel_custom=init_model(* caching from nodes to paths *)valmutablepath_cache=init_path_cache(* node currently selected *)valmutablecurrent_node=None(* Extendable. See method register_reset_extension. *)valmutablereset_extensions=[](* Extendable. See method add_select_function. *)valmutableselect_functions=[](* Extendable. See method add_global_filter *)valmutablehide_globals_filters=[initial_filter](* Extendable. See method append_pixbuf_column. *)valmutablecolumns_visibility=[](* Should be we call the actions registered to be applied on a node,
even if the node is already selected. Used after 'reset' has been
called. *)valmutableforce_selection=false(* Forward reference to the first column. Always set *)valmutablename_column=None(* Sort order for the rows in the filetree. Alphabetical order on names by
default, can be changed for custom order by text columns. *)valmutablesort_order=initial_sort_order(* The direction of the current sorting, and the column id according to
which the tree is sorted. Used to maintain consistent sort indicators. *)valmutablesort_kind=`ASCENDING,-1(* Properly sets the sort indicator of [column], according to the current
[sort_kind]. *)methodprivateset_sort_indicatorcolumn=letorder,id=sort_kindinifid=column#get_oidthen(column#set_sort_indicatortrue;column#set_sort_orderorder)elsecolumn#set_sort_indicatorfalse(* Changes the sort order to [sort] when left-clicking on the header of
[column]. *)methodprivatechange_sortcolumnsort=matchsort_kindwith|`ASCENDING,idwhenid=column#get_oid->sort_kind<-`DESCENDING,column#get_oid;sort_order<-MYTREE.inverse_sortsort|_->sort_kind<-`ASCENDING,column#get_oid;sort_order<-sortmethodrefresh_columns()=List.iter(funf->f`Visibility)columns_visibilitymethodappend_text_column~title~tooltip~visible~text?sort=letrenderer=GTree.cell_renderer_text[`XALIGN0.5]inletcolumn=GTree.view_column~renderer:(renderer,[])()inignore(tree_view#append_columncolumn);letlabel=GMisc.label~text:title()inGtk_helper.do_tooltip~tooltiplabel;column#set_widget(Somelabel#coerce);column#set_alignment0.5;column#set_reorderabletrue;column#set_min_width50;iffixed_heightthen(column#set_sizing`FIXED;column#set_resizablefalse;column#set_fixed_width100)elsecolumn#set_resizabletrue;lettextsglobals=List.fold_left(funaccglobal->`TEXT(textglobal)::acc)[]globalsinletfmodelrow=ifvisible()thenletpath=model#get_pathrowinself#set_sort_indicatorcolumn;matchmodel_custom#custom_get_iterpathwith|Some{MODEL.finfo=v}->letglobals=Array.to_listMYTREE.((get_storagev).globals)inrenderer#set_properties(textsglobals)|None->()incolumn#set_cell_data_funcrendererf;letsort=matchsortwith|None->fungh->String.compare(textg)(texth)|Somesort->sortinletcallback()=self#change_sortcolumn(MYTREE.Customsort);self#reset()incolumn#set_clickabletrue;ignore(column#connect#clicked~callback);letrefresh=function|`Contents->self#reset()|`Visibility->column#set_visible(visible())inrefresh`Visibility;columns_visibility<-refresh::columns_visibility;refreshmethodappend_pixbuf_column~title(f:(globallist->GTree.cell_properties_pixbuflist))visible=letcolumn=GTree.view_column~title()incolumn#set_reorderabletrue;iffixed_heightthen(column#set_sizing`FIXED;column#set_resizablefalse;column#set_fixed_width100)elsecolumn#set_resizabletrue;letrenderer=GTree.cell_renderer_pixbuf[]incolumn#packrenderer;column#set_cell_data_funcrenderer(funmodelrow->ifvisible()thenlet(path:Gtk.tree_path)=model#get_pathrowinmatchmodel_custom#custom_get_iterpathwith|Some{MODEL.finfo=v}->renderer#set_properties(f(Array.to_list((MYTREE.get_storagev).MYTREE.globals)))|None->());ignore(tree_view#append_columncolumn);letfilter_active,mi=self#filter_from_columnvisibletitlefin(* We return a function showing or masking the column*)letrefresh=letprev=reftrueinfunr->letvisible=visible()inif!prev!=visiblethen((* Column freshly appeared or disappeared. Update it *)prev:=visible;column#set_visiblevisible;mi#misc#set_sensitivevisible;(* A filter is active for the column. The visible nodes have
probably changed, destroy the filetree and rebuild it *)iffilter_active()thenself#reset();)(* Column state has not changed. If it is visible and its
contents have changed, the nodes to display may change *)elseifvisible&&r=`Contents&&filter_active()thenself#reset()inrefresh`Visibility;columns_visibility<-refresh::columns_visibility;refreshmethodprivatefilter_from_columncol_visibletitlef=letopt_active=ref(fun()->false)inlethide_globalg=col_visible()&&(!opt_active)()&&f[g]=[(`STOCK_ID"":GTree.cell_properties_pixbuf)]inlettext=Printf.sprintf"Selected by %s only"titleinletkey="filter_"^titleinletvisible,mi=self#add_global_filter~text~keyhide_globalinopt_active:=visible;(visible,mi)methodview=tree_viewmethodmodel=model_custommethodreset()=self#reset_internal();self#refresh_columns();methodregister_reset_extensionf=reset_extensions<-f::reset_extensionsmethodset_file_attribute?strikethrough?textfilename=tryset_rowmodel_custom?strikethrough?text(Filepath.Hashtbl.findpath_cache.State.cache_filesfilename)withNot_found->()(* Some files might not be in the list because
of our filters. Ignore *)methodset_global_attribute?strikethrough?textv=tryset_rowmodel_custom?strikethrough?text(Varinfo.Hashtbl.findpath_cache.State.cache_varsv)withNot_found->()(* Some globals might not be in the list because of
our filters. Ignore *)methodflat_mode=flat_mode()methodget_file_globalsfile=trylet_,raw_row=Filepath.Hashtbl.findpath_cache.State.cache_filesfileinMYTREE.sons_inforaw_row.MODEL.finfowithNot_found->[](* Some files may be hidden if they contain nothing
interesting *)methodfind_visible_globaltext=(* We perform up to two iterations in the list of globals, as follows:
1. First, we advance until the selected element (if any);
2. Then, we start searching for [text] until the end of the list;
3. If nothing was found, we start again, this time from the beginning
of the list until the selected global. *)letregex=Str.regexp_case_foldtextinletname_matchesname=tryignore(Str.search_forwardregexname0);truewithNot_found->falseinletfound_selection=ref(current_node=None)inletmodel=model_custominletget_global=functionGlobalg->g|_->assertfalseinletis_current_nodenode=matchcurrent_nodewith|None->false|Somenode'->same_nodenodenode'in(* Called when the currently selected node has been found. Either
the real search can start, or we abort because we have finished
wrapping around. *)letnode_found()=ifnot!found_selectionthenfound_selection:=trueelseraiseGlobal_not_found(* finished *);inletrecauxtextt=matcht.MODEL.finfowith|MYTREE.MFile({MYTREE.name},_)->(* search children *)(* note: we avoid calling [storage_type] here because
we do not need the child nodes *)letfake_node=File(Filepath.of_stringname,[])inifis_current_nodefake_nodethennode_found();Array.iter(auxtext)t.MODEL.sons|MYTREE.MGlobal{MYTREE.name}asst->letnode=MYTREE.storage_typestinifis_current_nodenodethennode_found()else(* We never consider the current node as matching. This way, if
'foo' is selected, we can search for 'fo' and find it farther.*)if!found_selection&&name_matchesnamethenraise(Found_global(get_globalnode))intryArray.iter(auxtext)model#get_roots;(* First search did not succeed, will try second search if
user wants to wrap around. *)ifcurrent_node<>None&>oolbox.question_box~title:"Not found"(Printf.sprintf"No more occurrences for: %s\n\
Search from beginning?"text)~buttons:["Yes";"No"]=1(*yes*)thenbeginassert(!found_selection);(* try searching again *)Array.iter(auxtext)model#get_roots;end;Nonewith|Found_globalg->Someg|Global_not_found->Nonemethodprivateenable_select_functions()=letselectpathpath_currently_selected=letfaile=Gui_parameters.error"selector handler got an internal error, please report: %s@.%s@."(Printexc.to_stringe)(Printexc.get_backtrace())intrylet{MODEL.finfo=t}=Option.get(model_custom#custom_get_iterpath)inletselected_node=MYTREE.storage_typetinletwas_activated=matchcurrent_nodewith|None->false|Someold_node->same_nodeselected_nodeold_nodeinif(force_selection||notwas_activated)&¬path_currently_selectedthenbegin(*Format.printf "##Select %a: %b %b %b, %s@."
pretty_node selected_node force_selection was_activated
path_currently_selected (GTree.Path.to_string path) *)current_node<-Someselected_node;letold_force_selection=force_selectioninList.iter(funf->tryf~was_activated:(notold_force_selection&&was_activated)~activating:trueselected_nodewithe->faile)select_functions;end;force_selection<-false;truewithe->Gui_parameters.error"gui could not select row in filetree, please report: %s"(Printexc.to_stringe);trueintree_view#selection#set_select_functionselectmethodadd_select_functionf=select_functions<-select_functions@[f];methodprivatevarinfo_of_globalg=matchgwith|GVar(vi,_,_)|GVarDecl(vi,_)|GFun({svar=vi},_)|GFunDecl(_,vi,_)->Somevi|_->Nonemethodunselect=tree_view#selection#unselect_all();current_node<-None(* Display a path of the gtk filetree, by expanding and centering the
needed nodes *)methodprivateshow_path_in_treepath=expand_to_pathtree_viewpath;tree_view#selection#select_pathpath;(* set_cursor updates the keyboard cursor and scrolls to the element *)tree_view#set_cursorpath(Option.getname_column);tree_view#misc#grab_focus()(* TODO: keep the structure of the tree, ie. reexpand all the nodes that
are currently expanded (not only the currently selected) *)methodprivatereset_internal()=(* We force a full recomputation using our filters for globals *)letmc,cache=State.computehide_globals_filterssort_orderintree_view#set_model(Some(mc:>GTree.model));model_custom<-mc;path_cache<-cache;List.iter(funf->f(self:>t))reset_extensions;force_selection<-true;(* Here, current_node may come from another project. This is not
a problem, as we only use it to do a basic search. Otherwise,
the solution would be to projectify it outside of the class. *)(matchcurrent_nodewith|None->()|Somenode->matchState.path_from_nodepath_cachenodewith|None->()|Some(path,_)->self#show_path_in_tree(GTree.Path.create(List.revpath)))methodselect_globalg=matchState.path_from_nodepath_cache(Globalg)with|None->(* selection failed *)self#unselect;false|Some(path,_)->self#show_path_in_tree(GTree.Path.create(List.revpath));truemethodselected_globals=matchcurrent_nodewith|None->[]|Some(File(_,g))->g|Some(Globalg)->[g]methodadd_global_filter~text~keyf=hide_globals_filters<-f::hide_globals_filters;letmi=MenusHide.menu_itemmenu~label:text~keyinignore(MenusHide.mi_set_callbackmi~keyself#reset_internal);(MenusHide.hidekey,mi)initializer(* Name column *)letname_renderer=GTree.cell_renderer_text[`YALIGN0.0]inletcolumn=GTree.view_column~title:"Name"~renderer:((name_renderer:>GTree.cell_renderer),[])()inlet_=tree_view#append_columncolumninname_column<-Somecolumn;letm_name_rendererrenderer(lmodel:GTree.model)iter=self#set_sort_indicatorcolumn;let(path:Gtk.tree_path)=lmodel#get_pathiterinmatchself#model#custom_get_iterpathwith|Somep->letspecial,text,strike,underline=matchp.MODEL.finfowith|MYTREE.MFile({MYTREE.name=m;strikethrough=strike},_)->ifm=""(* Unknown location *)thentrue,"Unknown file",strike,falseelseletpath=Filepath.of_stringminfalse,Filepath.to_stringpath,strike,false|MYTREE.MGlobal({MYTREE.name=m;strikethrough=strike})ass->false,m,strike,MYTREE.is_functionsinrenderer#set_properties[`TEXTtext;`STRIKETHROUGHstrike;`WEIGHT(ifspecialthen`LIGHTelse`NORMAL);`UNDERLINE(ifunderlinethen`LOWelse`NONE)]|None->()incolumn#set_cell_data_funcname_renderer(m_name_renderername_renderer);iffixed_heightthencolumn#set_sizing`FIXED;iffixed_heightthen(column#set_resizablefalse;column#set_fixed_width100)elsecolumn#set_resizabletrue;column#set_clickabletrue;lettitle=GMisc.label~text:"Name"()incolumn#set_widget(Sometitle#coerce);(* Filter menu when right-clicking on the column header. *)letpop_menu()=menu#popup~button:3~time:(GtkMain.Main.get_current_event_time());inlet()=matchget_column_header_buttoncolumnwith|None->(* Should not happen, but who knowns? *)ignore(column#connect#clicked~callback:pop_menu)|Somebutton->(* Connect the menu to a right click. *)letcallbackevt=ifGdkEvent.Button.buttonevt=3then(pop_menu();true)elsefalseinignore(button#event#connect#button_release~callback)in(* Changes the sort order when left-clicking on the column header. *)letcallback()=self#change_sortcolumnMYTREE.Ascending;self#reset()inignore(column#connect#clicked~callback:callback);(* Sets the sort_kind to the initial sort. *)sort_kind<-`ASCENDING,column#get_oid;ignore(MenusHide.mi_set_callbackmhide_functions~key:key_hide_functionsself#reset_internal);ignore(MenusHide.mi_set_callbackmhide_variables~key:key_hide_variablesself#reset_internal);ignore(MenusHide.mi_set_callbackmhide_stdlib~key:key_hide_stdlibself#reset_internal);ignore(MenusHide.mi_set_callbackmhide_defined~key:key_hide_definedself#reset_internal);ignore(MenusHide.mi_set_callbackmhide_undefined~key:key_hide_undefinedself#reset_internal);ignore(MenusHide.mi_set_callbackmhide_builtins~key:key_hide_builtinsself#reset_internal);ignore(MenusHide.mi_set_callbackmhide_annotations~key:key_hide_annotationsself#reset_internal);ignore(MenusHide.mi_set_callbackmflat_mode~key:key_flat_modeself#reset_internal);menu#add(GMenu.separator_item():>GMenu.menu_item);tree_view#set_model(Some(init_model:>GTree.model));self#enable_select_functions();iffixed_heightthentree_view#set_fixed_height_modetrue;endin(myself:>t)