123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445(**************************************************************************)(* *)(* This file is part of Frama-C. *)(* *)(* Copyright (C) 2007-2023 *)(* CEA (Commissariat à l'énergie atomique et aux énergies *)(* alternatives) *)(* *)(* 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). *)(* *)(**************************************************************************)classtypet=Wutil.widgetclasstypewidget=Wutil.widgetclasstypeaction=objectinheritwidgetmethodset_tooltip:string->unitendclasstype['a]signal=objectmethodfire:'a->unitmethodset_enabled:bool->unitmethodlock:(unit->unit)->unitmethodconnect:('a->unit)->unitmethodon_check:'a->(bool->unit)->unitmethodon_value:'a->(unit->unit)->unitmethodon_event:(unit->unit)->unitendclasstype['a]selector=objectinherit['a]signalmethodset:'a->unitmethodget:'amethodsend:('a->unit)->unit->unitend(* -------------------------------------------------------------------------- *)(* --- Labels --- *)(* -------------------------------------------------------------------------- *)openWutiltypealign=[`Left|`Right|`Center]typestyle=[`Label|`Descr|`Code|`Title]typecolor=[GDraw.color|`NORMAL]letxalign=function`Left->0.0|`Right->1.0|`Center->0.5classlabel?(style=`Label)?(align=`Left)?width?text()=letw=GMisc.label?text~xalign:(xalignalign)()inobjectinheritWutil.gobj_widgetwvalmutablefg=Nonevalmutablebg=Nonemethodset_fg(c:color)=matchfg,cwith|None,`NORMAL->()|Somec0,`NORMAL->w#misc#modify_fg[`NORMAL,`COLORc0]|None,(#GDraw.colorasc)->fg<-Some(w#misc#style#fg`NORMAL);w#misc#modify_fg[`NORMAL,c]|Some_,(#GDraw.colorasc)->w#misc#modify_fg[`NORMAL,c]methodset_bg(c:color)=matchbg,cwith|None,`NORMAL->()|Somec0,`NORMAL->w#misc#modify_bg[`NORMAL,`COLORc0]|None,(#GDraw.colorasc)->bg<-Some(w#misc#style#bg`NORMAL);w#misc#modify_bg[`NORMAL,c]|Some_,(#GDraw.colorasc)->w#misc#modify_bg[`NORMAL,c]initializerWutil.onwidthw#set_width_chars;matchstylewith|`Label->()|`Code->set_monospacew|`Title->set_bold_fontw|`Descr->w#set_single_line_modefalse;w#set_line_wraptrue;w#set_justify`LEFT;set_small_fontwmethodset_text=w#set_textmethodset_tooltipmsg=Wutil.set_tooltipw(ifmsg=""thenNoneelseSomemsg)end(* -------------------------------------------------------------------------- *)(* --- Icons --- *)(* -------------------------------------------------------------------------- *)typeicon=[GtkStock.id|`Shareofstring|`None]letdefault_icon=letxpm=[|"12 12 2 1";". c #ffffff";"# c #000000";"############";"#..........#";"#..........#";"#..........#";"#..........#";"#..........#";"#..........#";"#..........#";"#..........#";"#..........#";"#..........#";"############"|]inonce(fun()->GdkPixbuf.from_xpm_dataxpm)letpixbufs=Hashtbl.create63letshared_icon(f:string)=tryHashtbl.findpixbufsfwithNot_found->letfile=Printf.sprintf"%s/%s"!Wutil.sharefinletpixbuf=tryGdkPixbuf.from_filefilewithGlib.GError_->Wutil.warning"Icon '%s' not found"file;default_icon()inHashtbl.addpixbufsfpixbuf;pixbufletgimage=function|`None->GMisc.image()|`Sharef->GMisc.image~pixbuf:(shared_iconf)()|#GtkStock.idasstock->GMisc.image~stock()classimage(icn:icon)=letimg=gimageicninobjectinheritgobj_widgetimgmethodset_icon(icn:icon)=matchicnwith|`None->img#clear()|`Sharef->img#set_pixbuf(shared_iconf)|#GtkStock.idasid->img#set_stockidend(* -------------------------------------------------------------------------- *)(* --- Buttons --- *)(* -------------------------------------------------------------------------- *)classbutton_skel?align?(icon=`None)?tooltip(button:GButton.button_skel)=object(self)valmutableimages=[]initializerbeginself#set_iconicon;Wutil.onalign(funa->button#set_xalign(xaligna));set_tooltipbuttontooltip;button#misc#set_can_focusfalse;button#set_focus_on_clickfalse;endinheritgobj_actionbuttonmethodset_label=button#set_labelmethodset_bordere=button#set_relief(ifethen`NORMALelse`NONE)methodset_icon(i:icon)=matchiwith|`None->button#unset_image()|#iconasicn->letimage=tryList.associcnimageswithNot_found->letimg=gimageicninimages<-(icn,img)::images;imginbutton#set_imageimage#coerceendclassbutton?align?icon?label?(border=true)?tooltip()=letrelief=ifborderthen`NORMALelse`NONEinletbutton=GButton.button?label~relief~show:true()inobject(self)inherit[unit]signalassinherit!button_skel?align?icon?tooltip(button:>GButton.button_skel)asbmethod!set_enablede=s#set_enablede;b#set_enabledemethoddefault=button#grab_defaultinitializerignore(button#connect#clicked~callback:self#fire)end(* -------------------------------------------------------------------------- *)(* --- On/Off Buttons --- *)(* -------------------------------------------------------------------------- *)classcheckbox~label?tooltip()=letbutton=GButton.check_button~label~show:true()inobjectinherit[bool]selectorfalseassinherit!gobj_actionbuttonasbmethod!set_enablede=s#set_enablede;b#set_enabledemethodset_labell=button#set_labellmethod!seta=s#seta;button#set_activeainitializerbeginset_tooltipbuttontooltip;ignore(button#connect#clicked~callback:(fun()->s#setbutton#active));endendlettoggle_icon_warning=reftrueclasstoggle?align?icon?label?(border=true)?tooltip()=letrelief=ifborderthen`NORMALelse`NONEinletbutton=GButton.toggle_button?label~relief~show:true()inobjectinherit[bool]selectorfalseassinherit!button_skel?align?icon?tooltip(button:>GButton.button_skel)asbmethod!set_enablede=s#set_enablede;b#set_enabledemethod!set=button#set_activemethod!set_iconicn=ificn<>`None&&!toggle_icon_warningthen(Wutil.warning"[Widget] Icon may not appear on toggle buttons";toggle_icon_warning:=false);b#set_iconicninitializerignore(button#connect#clicked~callback:(fun()->s#setbutton#active))endclassswitch?tooltip()=letpix_on=shared_icon"switch-on.png"inletpix_off=shared_icon"switch-off.png"inletevt=GBin.event_box()inletimg=GMisc.image~pixbuf:pix_on~packing:evt#add()inobject(self)inherit[bool]selectorfalseassinherit!gobj_actionevtasbmethod!set_enablede=s#set_enablede;b#set_enabledemethod!seta=s#seta;img#set_pixbuf(ifathenpix_onelsepix_off)initializerbeginset_tooltipevttooltip;ignore(evt#event#connect#button_release~callback:(fun_evt->self#set(nots#get);false));endend(* -------------------------------------------------------------------------- *)(* --- Button Group --- *)(* -------------------------------------------------------------------------- *)(* only used inside groups -> not exported to API *)classradio_group~label?tooltip()=letbutton=GButton.radio_button~label~show:true()inobjectinherit[bool]selectorfalseassinherit!gobj_actionbuttonmethod!sete=s#sete;ifethenbutton#set_activetruemethodgroup=function|None->Somebutton#group|(Someg)assg->button#set_groupg;sginitializerbeginset_tooltipbuttontooltip;ignore(button#connect#clicked~callback:(fun()->s#setbutton#active));endend(* only used inside groups -> not exported to API *)classtoggle_group?label?icon?tooltip()=letbutton=GButton.button?label~show:true~relief:`NONE()inobjectinherit[bool]selectorfalseassinherit!button_skel?icon?tooltip(button:>GButton.button_skel)asbmethod!set_enablede=s#set_enablede;b#set_enabledemethod!seta=s#seta;button#set_relief(ifathen`NORMALelse`NONE)initializerignore(button#connect#clicked~callback:(fun()->s#set(nots#get)))endclass['a]group(default:'a)=object(self)inherit['a]selectordefaultvalmutablecases:(boolselector*'a)list=[]valmutablegroup=Noneinitializerself#connect(funv->List.iter(fun(w,v0)->w#set(v=v0))cases)methodprivateadd_case(w:boolselector)(v:'a)=beginw#set(v=self#get);w#connect(fune->ifethenself#setv);cases<-(w,v)::cases;endmethodadd_toggle?label?icon?tooltip~value()=lettoggle=newtoggle_group?label?icon?tooltip()inself#add_case(toggle:>boolselector)value;(toggle:>widget)methodadd_radio~label?tooltip~value()=letradio=newradio_group~label?tooltip()inself#add_case(radio:>boolselector)value;group<-radio#groupgroup;(radio:>widget)method!set_enablede=List.iter(fun(w,_)->w#set_enablede)casesend(* -------------------------------------------------------------------------- *)(* --- Spinner --- *)(* -------------------------------------------------------------------------- *)classspinner?min?max?(step=1)~value?tooltip()=letb=GEdit.spin_button~digits:0()inobjectinherit[int]selectorvalueassinherit!gobj_actionbmethod!set_enablede=s#set_enablede;b#misc#set_sensitiveemethod!seta=s#seta;b#set_value(floatvalue)methodset_minn=b#adjustment#set_bounds~lower:(floatn)()methodset_maxn=b#adjustment#set_bounds~upper:(floatn)()initializerbeginset_tooltipbtooltip;letfmapv=functionNone->v|Somex->floatxinb#adjustment#set_bounds~lower:(fmap(floatmin_int)min)~upper:(fmap(floatmax_int)max)~step_incr:(floatstep)();b#set_value(floatvalue);letcallback()=s#setb#value_as_intinignore(b#connect#value_changed~callback);endend(* -------------------------------------------------------------------------- *)(* --- PopDown --- *)(* -------------------------------------------------------------------------- *)letrender_optionsopta=tryList.assocaoptwithNot_found->"<unknown>"class['a]menu~default?(options=[])?render?items()=letstrings=List.mapsndoptionsinlet(cmb,(model,_))ascombo=GEdit.combo_box_text~strings~wrap_width:1()inobject(self)inheritgobj_actioncmbaswidgetinherit!['a]selectordefaultasselectinitializerbeginonrenderself#set_render;onitemsself#set_items;endvalmutableprinter=render_optionsoptionsvalmutablevalues=Array.of_list(List.mapfstoptions)methodset_optionsopt=printer<-render_optionsopt;self#set_items(List.mapfstopt)methodset_renderp=printer<-pmethod!set_enablede=select#set_enablede;widget#set_enabledemethodget_items=Array.to_listvaluesmethodset_itemsxs=beginvalues<-Array.of_listxs;model#clear();Array.iter(funx->GEdit.text_combo_addcombo(printerx))values;lete=select#getinself#lock(fun()->Array.iteri(funix->ifx=ethencmb#set_activei)values);endmethodprivateclickedn=if0<=n&&n<Array.lengthvaluesthenselect#setvalues.(n)method!setx=beginselect#setx;Array.iteri(funie->ifx=ethencmb#set_activei)values;endinitializerignore(cmb#connect#notify_active~callback:self#clicked)end(* -------------------------------------------------------------------------- *)(* --- Popup Menu --- *)(* -------------------------------------------------------------------------- *)classpopup()=letmenu=GMenu.menu()inobjectvalmutableempty=truevalmutableseparator=falsemethodclear=beginList.itermenu#removemenu#children;empty<-true;separator<-false;endmethodadd_separator=separator<-truemethodadd_item~label~callback=ifnotempty&&separatorthenignore(GMenu.separator_item~packing:menu#append());letitem=GMenu.menu_item~label~packing:menu#append()inignore(item#connect#activate~callback);empty<-false;separator<-falsemethodrun()=ifnotemptythenlettime=GMain.Event.get_current_time()inmenu#popup~button:3~timeend