Load/save GUI initial entry values

I assumed there must be already a question like mine, but my search was unsuccessful.
If one has a GUI with text, number, check (boolean) and combo entries, is there an iterative way to save and load all of them, initialising the GUI with a wished set of values (among several)?
I have 2 solutions myself, but I am not very satisfied:

  1. I store everything as xml and then have a xml->map conversion (and back). But for this I re-run the application, in practice I kill+start the program again. At least it would be nice to re-start it in the exact Desktop position as the killed one, in case the user shifted the GUI (this must be trivial, but I have no idea yet).
  2. I insert voids in the *.h file, like
    void SetMyBut(TGCheckButton *var){mybut = (TGCheckButton *)var;};
    This is more “elegant” for the user but it is not iterative, so for the programmer it is neither compact nor easily error-free.

Thanks in advance
Giovanni

I solved my problem by introducing maps also for the GUI widgets,

map<TString, TGTextEntry*>   d_txt;
map<TString, TGNumberEntry*> d_num;
map<TString, TGComboBox*>    d_cmb;
map<TString, TGCheckButton*> d_ckb;

In this way I can replace iteratively all the GUI entries without killing/restarting the main window. This is both user-friendly and easy to maintain/debug. Of course it works in both directions, loading parameter sets as well as saving them. Maybe using TTree it can get even simpler, but I am satisfied with the current approach. Summarising:
xml->maps->GUI (loading parameter set)
GUI->maps->xml (storing parameter set)
(My xml’s have only one level).
In case anyone is interested in the pieces of code I’ll post them.

Hi Giovanni,

That sounds interesting. Could you post a short macro demonstrating it? Thanks!

Cheers, Bertrand.

Sure! I have a debt to the ROOT community. I have never written a ROOT macro from scratch, so here’s my headers, Makefile, input file and code. If you have any improvements (I have not much experience neither with ROOT nor with c++) and if you want to make a macro out of my code, please go ahead!

LinkDef.h:

#ifdef __CINT__
#pragma link off all globals;
#pragma link off all classes;
#pragma link off all functions;
#pragma link C++ class gui_maps+;
#endif

gui_maps.h:

#include "TGFrame.h"

class gui_maps : public TGMainFrame
{
private:

public:
   gui_maps(const TGWindow *p, UInt_t w, UInt_t h, UInt_t options);
   virtual ~gui_maps() {}
   void store_xml(const char*);
   void load_xml(const char*);
   void HandleMenu(Int_t);
   void GetVar();
   void run_gui_maps(); 
   ClassDef(gui_maps, 0)
};

Makefile:

include $(ROOTSYS)/test/Makefile.arch

SRCS    := gui_maps.$(SrcSuf) gui_maps_Dict.$(SrcSuf)
OBJS    := $(SRCS:.$(SrcSuf)=.$(ObjSuf)) gui_maps_Dict.$(ObjSuf)
EXE      = GUI_MAPS$(ExeSuf)

PROGRAMS      = $(EXE)

.SUFFIXES: .$(SrcSuf) .$(ObjSuf)

all:        $(PROGRAMS)

$(EXE):  $(OBJS)
	$(LD) $(LDFLAGS) $^ $(ROOTGLIBS) -lXMLIO $(OutPutOpt)$@
	@echo "$@ done"

gui_maps_Dict.cxx: gui_maps.h LinkDef.h
		@echo "Generating dictionary $@..."
		@rootcint -f $@ -c $^

clean:
		rm -f *.o *.d *Dict.*
.$(SrcSuf).$(ObjSuf):
	$(CXX) $(CXXFLAGS) $(EXTRAFLAGS) -c $<

xml/default.xml:

<?xml version="1.0"?>
<main>
  <GraphPulse type="bool">true</GraphPulse>
  <GraphTime type="bool">false</GraphTime>
  <Acq type="string">26904</Acq>
  <Path type="string">/myhome/GUI_MAPS</Path>
  <PeakAlgorythm type="combo">1</PeakAlgorythm>
  <SeparationLines type="combo">2</SeparationLines>
  <TimeBin type="double">0.01</TimeBin>
  <WindowLength type="int">120</WindowLength>
</main>

gui_maps.cpp:

#include "Riostream.h"
#include "gui_maps.h"
#include "TGNumberEntry.h"
#include "TGFrame.h"
#include "TGLabel.h"
#include "TApplication.h"
#include "TFrame.h"
#include "TXMLEngine.h"
#include "TGFileDialog.h"
#include "TGMenu.h"
#include "TGComboBox.h"
#include "TGToolBar.h"
#include "TGToolTip.h"
#include "TRootHelpDialog.h"
#include <stdlib.h>
#include <sstream>
#include <map>

map<string, bool>   d_boo;
map<string, int>    d_int;
map<string, int>    d_com;
map<string, double> d_dbl;
map<string, string> d_str, typ_map;
map<TString, TGTextEntry*>   d_txt;
map<TString, TGNumberEntry*> d_num;
map<TString, TGComboBox*>    d_cmb;
map<TString, TGCheckButton*> d_ckb;

int jstr;
string homedir  = getenv("HOME");
string gui_maps_dir = homedir + "/root/gui_maps";
string xml_dir  = gui_maps_dir + "/xml/";

gui_maps *main_frame;

const char gHelpGUI_MAPS[] = "GUIs with maps, ask giovannitardini at yahoo.de";

enum EMyMessageTypes{
  M_FILE_LOAD,
  M_FILE_SAVE,
  M_FILE_EXE,
  M_FILE_EXIT,
  M_HELP_ABOUT
};

const char *gui_maps_types[] = {
   "xml files", "*.xml",
   "All files", "*",
    0         ,  0
};

static ToolBarData_t gToolBarData[] ={
  { "bld_exit.png"  , "Exit\tCtrl+Q"            , kFALSE, M_FILE_EXIT, 0 },
  { "ed_execute.png", "Run GUI_MAPS\tCtrl+R"    , kFALSE, M_FILE_EXE , 0 },
  { "bld_open.png"  , "Load setup...\tCtrl+L"   , kFALSE, M_FILE_LOAD, 0 },
  { "bld_save.png"  , "Save setup as...\tCtrl+U", kFALSE, M_FILE_SAVE, 0 },
  { 0               , 0                         , kFALSE, 0          , 0 }
};

void xml2dic(TXMLEngine* xml, XMLNodePointer_t node, Int_t level) {

  XMLNodePointer_t child;
  XMLAttrPointer_t attr = xml->GetFirstAttr(node);
  const char* cvar      = xml->GetNodeName(node);
  const char* content   = xml->GetNodeContent(node);
  const char* att_nam;
  const char* att_val;
  string satt_val,satt_nam;
  string svar = string(cvar);
  string sval;

  while (attr != 0){
    att_nam = xml->GetAttrName(attr);
    att_val = xml->GetAttrValue(attr);
    attr    = xml->GetNextAttr(attr);
    satt_nam=string(att_nam);
    satt_val=string(att_val);
    typ_map[svar] = satt_val;
    sval = string(content);

    if (satt_val == "bool"){
      if (sval == "true"){
        d_boo[svar]=true;
      }
      else{
        d_boo[svar]=false;
      }
    }
    if (satt_val == "string"){
      d_str[svar]=sval;
    }
    if (satt_val == "int"){
      stringstream ss;
      ss << sval;
      ss >> d_int[svar];
    }
    if (satt_val == "combo"){
      stringstream ss;
      ss << sval;
      ss >> d_com[svar];
    }
    if (satt_val == "double"){
      stringstream ss;
      ss << sval;
      ss >> d_dbl[svar];
    }
  }

// display all child nodes   
  child = xml->GetChild(node);
  while (child!=0){
    xml2dic(xml, child, level+2);
    child = xml->GetNext(child);
  }
}

gui_maps::gui_maps( const TGWindow *p, UInt_t w, UInt_t h, UInt_t options) : TGMainFrame(p, w, h, options) {

   int i,j;

// GUI objects
  TString clbl;
  TGLabel *wid_lbl;
  TGLabel *str_lbl;
  ULong_t ucolor;
  TGFont *ufont;
  TGGC *uGC;
  TGHorizontalFrame *data_box = 0;

// GUI graphics
  int const linspace=25,txt_wid=90,fld_wid=65,margin_hor=5;
  int const fld_hgt=22,ndline=35,lbl_wid=85,txt_fld=165,side_len=320;
  Int_t nline=ndline;
  int spacing=8;
  Int_t menu_h=28;
  int remap[4] = {2,3,1,0};

// Menubar
  TGMenuBar   *fMenuBar;
  TGPopupMenu *fMenuFile;
  TGPopupMenu *fMenuHelp;
  TGToolBar   *fToolBar;

//=====================
// Menubar and toolbar
//=====================
  
  SetCleanup(kDeepCleanup);
  fMenuBar  = new TGMenuBar(this, 0, 0, kHorizontalFrame|kRaisedFrame);
  fMenuFile = new TGPopupMenu(gClient->GetRoot());
  fToolBar  = new TGToolBar(this, 0, 0, kHorizontalFrame|kRaisedFrame);

  for (i = 0; gToolBarData[i].fPixmap; i++){
    j=remap[i];
    if (j == 0){
      fMenuFile->AddSeparator();
    }
    fMenuFile->AddEntry( gToolBarData[j].fTipText, gToolBarData[j].fId, 0,
                            fClient->GetPicture(gToolBarData[j].fPixmap) );

    TGPictureButton *pb = new TGPictureButton(fToolBar, 
          fClient->GetPicture(gToolBarData[i].fPixmap), gToolBarData[i].fId);
    pb->SetToolTipText(gToolBarData[i].fTipText);
    TGToolTip *tip = pb->GetToolTip();
    tip->SetDelay(100);
    fToolBar->AddButton(this, pb, spacing);
    spacing = 0;
  }

  fMenuHelp = new TGPopupMenu(gClient->GetRoot());
  fMenuHelp->AddEntry(" &About...", M_HELP_ABOUT, 0,gClient->GetPicture("about.xpm"));

  fMenuBar->AddPopup(" &File", fMenuFile, new TGLayoutHints(kLHintsTop|kLHintsLeft));
  fMenuBar->AddPopup(" &Help", fMenuHelp, new TGLayoutHints(kLHintsTop|kLHintsRight));

  AddFrame(fMenuBar);
  AddFrame(fToolBar);

  fMenuBar->MoveResize(0,0,side_len,menu_h);
  fToolBar->MoveResize(0,menu_h,side_len,menu_h);

  fMenuFile->Connect("Activated(Int_t)", "gui_maps", this, "HandleMenu(Int_t)");
  fToolBar ->Connect("Clicked(Int_t)", "gui_maps", this, "HandleMenu(Int_t)");
  fMenuHelp->Connect("Activated(Int_t)", "gui_maps", this, "HandleMenu(Int_t)");

//==========
// GUI frame
//==========

  SetLayoutBroken(kTRUE);

  gClient->GetColorByName("#ccccaa",ucolor);
  ufont = gClient->GetFont("-*-helvetica-medium-r-*-*-12-*-*-*-*-*-iso8859-1");

  data_box = new TGHorizontalFrame(this,248,112,kHorizontalFrame,ucolor);

  GCValues_t gval;
  gval.fMask = kGCForeground | kGCBackground | kGCFillStyle | kGCFont | kGCGraphicsExposures;
  gClient->GetColorByName("#000000",gval.fForeground);
  gClient->GetColorByName("#c0c0c0",gval.fBackground);
  gval.fFillStyle = kFillSolid;
  gval.fFont = ufont->GetFontHandle();
  gval.fGraphicsExposures = kFALSE;
  uGC = gClient->GetGC(&gval, kTRUE);

  gClient->GetColorByName("#ffff99",ucolor);

  TGLabel *data_lbl = new TGLabel(data_box,"Data I/O files",TGLabel::GetDefaultGC()(),TGLabel::GetDefaultFontStruct(),kChildFrame,ucolor);
  data_lbl->MoveResize(58,margin_hor,120,25);

// Example: Textentries
  string in_str[] = {"Path","Acq","stop"};
  for (jstr = 0; in_str[jstr] != "stop"; jstr++){
    clbl=in_str[jstr];
    str_lbl = new TGLabel(data_box,clbl,TGLabel::GetDefaultGC()(),TGLabel::GetDefaultFontStruct(),kChildFrame,ucolor);
    d_txt[clbl] = new TGTextEntry(data_box, new TGTextBuffer(31),-1,uGC->GetGC(),ufont->GetFontStruct(),kSunkenFrame | kDoubleBorder | kOwnBackground);
    d_txt[clbl]->SetName(clbl);
    d_txt[clbl]->Connect("TextChanged(char*)", "gui_maps", this, "GetVar()");
    str_lbl    ->MoveResize(margin_hor        ,nline,lbl_wid,fld_hgt);
    d_txt[clbl]->MoveResize(margin_hor+lbl_wid,nline,txt_fld,fld_hgt);
    nline += linspace;
  }

// Example: Numberentries, int and double

  string in_num[] = {"TimeBin","WindowLength","stop"};
  for (jstr=0; in_num[jstr] != "stop"; jstr++){
    clbl=in_num[jstr];
    wid_lbl = new TGLabel(data_box,clbl,TGLabel::GetDefaultGC()(),TGLabel::GetDefaultFontStruct(),kChildFrame,ucolor);
    d_num[clbl] = new TGNumberEntry(data_box, 0,11,-1,(TGNumberFormat::EStyle) 5);
    d_num[clbl]->GetNumberEntry()->SetName(clbl);
    d_num[clbl]->GetNumberEntry()->Connect("TextChanged(char*)", "gui_maps", this, "GetVar()");
    wid_lbl    ->MoveResize(margin_hor        ,nline,txt_wid,fld_hgt);
    d_num[clbl]->MoveResize(margin_hor+txt_wid,nline,fld_wid,fld_hgt);
    nline += linspace;
  }

// Example: Checkbuttons

  string in_boo[] = {"GraphPulse","GraphTime","stop"};
  for (jstr=0; in_boo[jstr] != "stop"; jstr++){
    clbl=in_boo[jstr];
    d_ckb[clbl] = new TGCheckButton(data_box, clbl);
    d_ckb[clbl]->SetBackgroundColor(ucolor);
    d_ckb[clbl]->SetName(clbl);
    d_ckb[clbl]->Connect("Toggled(Bool_t)", "gui_maps", this, "GetVar()");
    d_ckb[clbl]->MoveResize(margin_hor,nline,txt_wid,fld_hgt);
    nline += linspace;
  }

// Example: Comboboxes

  string in_cmb[] = {"SeparationLines","PeakAlgorythm","stop"};
  for (jstr=0; in_cmb[jstr] != "stop"; jstr++){
    clbl=in_cmb[jstr];
    wid_lbl = new TGLabel(data_box,clbl,TGLabel::GetDefaultGC()(),TGLabel::GetDefaultFontStruct(),kChildFrame,ucolor);
    d_cmb[clbl] = new TGComboBox(data_box,-1,kHorizontalFrame | kSunkenFrame | kDoubleBorder | kOwnBackground);
    d_cmb[clbl]->AddEntry("1",1);
    d_cmb[clbl]->AddEntry("2",2);
    wid_lbl    ->MoveResize(margin_hor        ,nline,txt_wid,fld_hgt);
    d_cmb[clbl]->MoveResize(margin_hor+txt_wid,nline,fld_wid,fld_hgt);
    data_box->AddFrame(d_cmb[clbl], new TGLayoutHints(kLHintsNormal,2,2,2,2));
    d_cmb[clbl]->SetName(clbl);
    d_cmb[clbl]->Connect("Selected(Int_t)", "gui_maps", this, "GetVar()");
    AddFrame(data_box, new TGLayoutHints(kLHintsExpandX | kLHintsExpandY));
    nline += linspace;
  }

  data_box->SetLayoutBroken(kTRUE);
  data_box->MoveResize(margin_hor,70,side_len,430);

//============
// MAIN frame
//============
  AddInput(kKeyPressMask | kKeyReleaseMask);
  MapSubwindows();
  SetWindowName("GUI_MAPS");
  MapWindow();
  MoveResize(300,300,side_len,side_len);

  string xmlfile = xml_dir+"default.xml";
  const char* cname = xmlfile.c_str();
  gui_maps::load_xml(cname);
}


void gui_maps::GetVar() {

// Updating the maps at every change in the GUI

  TGNumberEntry *entry = (TGNumberEntry*)gTQSender;
  string lbl=entry->GetName();
  string stype=typ_map[lbl];
  TString clbl;
  clbl=lbl;
  cout << clbl << " = ";
  if (stype == "int"){
    d_int[lbl]=d_num[clbl]->GetNumber();
    cout << d_int[lbl];
  }
  if (stype == "double"){
    d_dbl[lbl]=d_num[clbl]->GetNumber();
    cout << d_dbl[lbl];
  }
  if (stype == "string"){
    d_str[lbl]=d_txt[clbl]->GetText();
    cout << d_str[lbl];
  }
  if (stype == "combo"){
    d_com[lbl]=d_cmb[clbl]->GetSelected();
    cout << d_com[lbl];
  }
  if (stype == "bool"){
    d_boo[lbl]=d_ckb[clbl]->IsDown();
    cout << d_boo[lbl];
  }
  cout << endl;
}

int main(int argc, char *argv[]) {
  TApplication *theApp = new TApplication("gui_maps", &argc, argv);
  main_frame = new gui_maps(gClient->GetRoot(),10,10,kMainFrame | kVerticalFrame);
  theApp->Run();
}

void gui_maps::HandleMenu(Int_t menu_id) {

// Handle menu events

  TRootHelpDialog *hd;
  char * cxml = new char[xml_dir.length()];
  strcpy(cxml,xml_dir.c_str());
  TGFileInfo fi;
  fi.fFileTypes = gui_maps_types;
  fi.fIniDir = cxml;

  switch (menu_id) {
    case M_FILE_EXIT:
      CloseWindow();
      gApplication->Terminate(0);
      break;
    case M_FILE_LOAD:
      new TGFileDialog(gClient->GetRoot(), this, kFDOpen, &fi);
      cxml = fi.fIniDir;
      gui_maps::load_xml(fi.fFilename);
      break;
    case M_FILE_SAVE:
      new TGFileDialog(gClient->GetRoot(), this, kFDSave, &fi);
      cxml = fi.fIniDir;
      gui_maps::store_xml(fi.fFilename);
      break;
    case M_FILE_EXE:
      gui_maps::run_gui_maps();
      break;
    case M_HELP_ABOUT:
      hd = new TRootHelpDialog(this, "About GUI_MAPS...", 550, 250);
      hd->SetText(gHelpGUI_MAPS);
      hd->Popup();
      break;
  }
}

void dic2xml(XMLNodePointer_t mainnode) {

// Converts map into a 1-level xml object
  map<string, bool>  :: iterator biter;
  map<string, string>:: iterator siter;
  map<string, int>   :: iterator iiter;
  map<string, double>:: iterator diter;
  string lbl;
  bool bval;
  int val;
  double dval;
  TString clbl;
  TString cval;
  TXMLEngine* xml=0;
  XMLNodePointer_t xnode;

  for (biter = d_boo.begin(); biter != d_boo.end(); ++biter){
    clbl=biter->first;
    bval=biter->second;
    if (bval){
      xnode = xml->NewChild(mainnode, 0, clbl, "true");
    }
    else{
      xnode = xml->NewChild(mainnode, 0, clbl, "false");
    }
    xml->NewAttr(xnode, 0, "type","bool");
  }

  for (siter = d_str.begin(); siter != d_str.end(); ++siter){
    clbl=siter->first;
    cval=siter->second;
    xnode = xml->NewChild(mainnode, 0, clbl,cval);
    xml->NewAttr(xnode, 0, "type","string");
  }
 
  for (iiter = d_int.begin(); iiter != d_int.end(); ++iiter){
    clbl=iiter->first;
    val =iiter->second;
    stringstream ss;
    ss << val;
    cval = ss.str();
    xnode = xml->NewChild(mainnode, 0, clbl, cval);
    xml->NewAttr(xnode, 0, "type","int");
  }

  for (iiter = d_com.begin(); iiter != d_com.end(); ++iiter){
    clbl=iiter->first;
    val =iiter->second;
    stringstream ss;
    ss << val;
    cval = ss.str();
    xnode = xml->NewChild(mainnode, 0, clbl, cval);
    xml->NewAttr(xnode, 0, "type","combo");
  }

  for (diter = d_dbl.begin(); diter != d_dbl.end(); ++diter){
    clbl=diter->first;
    dval=diter->second;
    stringstream ss;
    ss << dval;
    cval = ss.str();
    xnode = xml->NewChild(mainnode, 0, clbl, cval);
    xml->NewAttr(xnode, 0, "type","double");
  }

}

void gui_maps::load_xml(const char* cname) {

// Reads in a xml file, it translates it into the working maps and fills the 
// proper GNU entries

  TString clbl;
  TString cval;
  bool bval;
  int val;
  double dval;
  map<string, bool>  :: iterator biter;
  map<string, string>:: iterator siter;
  map<string, int>   :: iterator iiter;
  map<string, double>:: iterator diter;

  TXMLEngine* xml=0;
  XMLNodePointer_t mainnode =  0;
  XMLDocPointer_t xmldoc = 0;
  xmldoc = xml->ParseFile(cname);

  if (xmldoc==0){
    delete xml;
    return;  
  }

  mainnode = xml->DocGetRootElement(xmldoc);
  xml2dic(xml, mainnode, 1);

  cout << "Loading " << cname << endl;
  for (biter = d_boo.begin(); biter != d_boo.end(); ++biter){
    clbl=biter->first;
    bval=biter->second;
    if (bval){
      d_ckb[clbl]->SetState(kButtonDown);
    }
    else{
      d_ckb[clbl]->SetState(kButtonUp);
    }
  }
  for (siter = d_str.begin(); siter != d_str.end(); ++siter){
    clbl=siter->first;
    cval=siter->second;
    d_txt[clbl]->SetText(cval);
  }
  for (iiter = d_int.begin(); iiter != d_int.end(); ++iiter){
    clbl=iiter->first;
    val =iiter->second;
    d_num[clbl]->SetIntNumber(Long_t(val));
  }
  for (iiter = d_com.begin(); iiter != d_com.end(); ++iiter){
    clbl=iiter->first;
    val =iiter->second;
    d_cmb[clbl]->Select(Long_t(val));
  }
  for (diter = d_dbl.begin(); diter != d_dbl.end(); ++diter){
    clbl=diter->first;
    dval=diter->second;
    d_num[clbl]->SetNumber(dval);
  }

  xml->FreeDoc(xmldoc);
  delete xml;
}

void gui_maps::store_xml(const char* cname) {

// Stores the current GUI entries values into a xml file
// via the maps, that are always up-to-date

  TXMLEngine* xml=0;
  XMLDocPointer_t xmldoc = 0;
  XMLNodePointer_t mainnode =  0;
  mainnode = xml->NewChild(0, 0, "main");

  cout << "Storing " << cname << endl;
  dic2xml(mainnode);
  xmldoc = xml->NewDoc();
  xml->DocSetRootElement(xmldoc, mainnode);
  xml->SaveDoc(xmldoc, cname);

  xml->FreeDoc(xmldoc);
  delete xml;
}

void gui_maps::run_gui_maps() {

  cout << "Running my prog" << endl;
}

Thank you very much Giovanni! :smiley:

Cheers, Bertrand.

You are welcome!
Sorry for not getting it into a macro!
My approach (maps) pays off when a program has more than 10 GUI entries.
I am considering putting the xml2dic, dic2xml parts in a separate class, in order to share them with other programs. The slight hurdle is that I am using global variables for my maps. I love maps, they are the closest to python dictionaries in c++ to my knowledge.

And here the improved version, where the translation from/to XML is in a separate source file, which one can share between different programs. It is also less code overall, with less maps. LinkDef.h, gui_maps.h and xml/default.xml are still as in my previous post.

Makefile

CC	= g++
OFLAGS	= -O2
LDFLAGS	= $(shell $(ROOTSYS)/bin/root-config --cflags --glibs)
DIC = gui_maps_Dict.cxx
SRCDIR = $(HOME)/root/src
INCDIR = $(HOME)/root/inc

GUI_MAPS: gui_maps.cpp $(SRCDIR)/xml.cpp $(DIC)
	$(CC) $(LDFLAGS) $(OFLAGS) -I$(INCDIR) -lXMLIO -o GUI_MAPS gui_maps.cpp $(SRCDIR)/xml.cpp $(DIC)

$(DIC): gui_maps.h $(INCDIR)/xml.h LinkDef.h
	rootcint -f $(DIC) -c gui_maps.h $(INCDIR)/xml.h LinkDef.h

clean:
	$(RM) -f *.o *.d

gui_maps.cpp

#include "gui_maps.h"
#include "xml.h"
#include "TGNumberEntry.h"
#include "TGLabel.h"
#include "TApplication.h"
#include "TFrame.h"
#include "TGFileDialog.h"
#include "TGMenu.h"
#include "TGComboBox.h"
#include "TGToolBar.h"
#include "TGToolTip.h"
#include "TRootHelpDialog.h"
#include <stdlib.h>
#include <sstream>
#include <map>

map<TString, TString*> map_xml;
map<TString, TGTextEntry*>   d_txt;
map<TString, TGNumberEntry*> d_num;
map<TString, TGComboBox*>    d_cmb;
map<TString, TGCheckButton*> d_ckb;

int jstr;
string homedir  = getenv("HOME");
string gui_maps_dir = homedir + "/root/gui_maps";
string xml_dir  = gui_maps_dir + "/xml/";

gui_maps *main_frame;

const char gHelpGUI_MAPS[] = "GUIs with maps, ask giovannitardini at yahoo.de";

enum EMyMessageTypes{
  M_FILE_LOAD,
  M_FILE_SAVE,
  M_FILE_EXE,
  M_FILE_EXIT,
  M_HELP_ABOUT
};

const char *gui_maps_types[] = {
   "xml files", "*.xml",
   "All files", "*",
    0         ,  0
};

static ToolBarData_t gToolBarData[] ={
  { "bld_exit.png"  , "Exit\tCtrl+Q"            , kFALSE, M_FILE_EXIT, 0 },
  { "ed_execute.png", "Run GUI_MAPS\tCtrl+R"    , kFALSE, M_FILE_EXE , 0 },
  { "bld_open.png"  , "Load setup...\tCtrl+L"   , kFALSE, M_FILE_LOAD, 0 },
  { "bld_save.png"  , "Save setup as...\tCtrl+U", kFALSE, M_FILE_SAVE, 0 },
  { 0               , 0                         , kFALSE, 0          , 0 }
};

gui_maps::gui_maps( const TGWindow *p, UInt_t w, UInt_t h, UInt_t options) : TGMainFrame(p, w, h, options) {

  int i,j;

// GUI objects
  TString clbl;
  TGLabel *wid_lbl;
  TGLabel *str_lbl;
  ULong_t ucolor;
  TGFont *ufont;
  TGGC *uGC;
  TGHorizontalFrame *data_box = 0;

// GUI graphics
  int const linspace=25,txt_wid=90,fld_wid=65,margin_hor=5;
  int const fld_hgt=22,ndline=35,lbl_wid=85,txt_fld=165,side_len=320;
  Int_t nline=ndline;
  int spacing=8;
  Int_t menu_h=28;
  int remap[4] = {2,3,1,0};

// Menubar
  TGMenuBar   *fMenuBar;
  TGPopupMenu *fMenuFile;
  TGPopupMenu *fMenuHelp;
  TGToolBar   *fToolBar;

//=====================
// Menubar and toolbar
//=====================

  SetCleanup(kDeepCleanup);
  fMenuBar  = new TGMenuBar(this, 0, 0, kHorizontalFrame|kRaisedFrame);
  fMenuFile = new TGPopupMenu(gClient->GetRoot());
  fToolBar  = new TGToolBar(this, 0, 0, kHorizontalFrame|kRaisedFrame);

  for (i = 0; gToolBarData[i].fPixmap; i++){
    j=remap[i];
    if (j == 0){
      fMenuFile->AddSeparator();
    }
    fMenuFile->AddEntry( gToolBarData[j].fTipText, gToolBarData[j].fId, 0,
                            fClient->GetPicture(gToolBarData[j].fPixmap) );

    TGPictureButton *pb = new TGPictureButton(fToolBar, 
          fClient->GetPicture(gToolBarData[i].fPixmap), gToolBarData[i].fId);
    pb->SetToolTipText(gToolBarData[i].fTipText);
    TGToolTip *tip = pb->GetToolTip();
    tip->SetDelay(100);
    fToolBar->AddButton(this, pb, spacing);
    spacing = 0;
  }

  fMenuHelp = new TGPopupMenu(gClient->GetRoot());
  fMenuHelp->AddEntry(" &About...", M_HELP_ABOUT, 0,gClient->GetPicture("about.xpm"));

  fMenuBar->AddPopup(" &File", fMenuFile, new TGLayoutHints(kLHintsTop|kLHintsLeft));
  fMenuBar->AddPopup(" &Help", fMenuHelp, new TGLayoutHints(kLHintsTop|kLHintsRight));

  AddFrame(fMenuBar);
  AddFrame(fToolBar);

  fMenuBar->MoveResize(0,0,side_len,menu_h);
  fToolBar->MoveResize(0,menu_h,side_len,menu_h);

  fMenuFile->Connect("Activated(Int_t)", "gui_maps", this, "HandleMenu(Int_t)");
  fToolBar ->Connect("Clicked(Int_t)"  , "gui_maps", this, "HandleMenu(Int_t)");
  fMenuHelp->Connect("Activated(Int_t)", "gui_maps", this, "HandleMenu(Int_t)");

//==========
// GUI frame
//==========

  SetLayoutBroken(kTRUE);

  gClient->GetColorByName("#ccccaa",ucolor);
  ufont = gClient->GetFont("-*-helvetica-medium-r-*-*-12-*-*-*-*-*-iso8859-1");

  data_box = new TGHorizontalFrame(this,248,112,kHorizontalFrame,ucolor);

  GCValues_t gval;
  gval.fMask = kGCForeground | kGCBackground | kGCFillStyle | kGCFont | kGCGraphicsExposures;
  gClient->GetColorByName("#000000",gval.fForeground);
  gClient->GetColorByName("#c0c0c0",gval.fBackground);
  gval.fFillStyle = kFillSolid;
  gval.fFont = ufont->GetFontHandle();
  gval.fGraphicsExposures = kFALSE;
  uGC = gClient->GetGC(&gval, kTRUE);

  gClient->GetColorByName("#ffff99",ucolor);

  TGLabel *data_lbl = new TGLabel(data_box,"Data I/O files",TGLabel::GetDefaultGC()(),TGLabel::GetDefaultFontStruct(),kChildFrame,ucolor);
  data_lbl->MoveResize(58,margin_hor,120,25);

// Example: Textentries
  string in_str[] = {"Path","Acq","stop"};
  for (jstr = 0; in_str[jstr] != "stop"; jstr++){
    clbl=in_str[jstr];
    str_lbl = new TGLabel(data_box,clbl,TGLabel::GetDefaultGC()(),TGLabel::GetDefaultFontStruct(),kChildFrame,ucolor);
    d_txt[clbl] = new TGTextEntry(data_box, new TGTextBuffer(31),-1,uGC->GetGC(),ufont->GetFontStruct(),kSunkenFrame | kDoubleBorder | kOwnBackground);
    d_txt[clbl]->SetName(clbl);
    d_txt[clbl]->Connect("TextChanged(char*)", "gui_maps", this, "GetVar()");
    str_lbl    ->MoveResize(margin_hor        ,nline,lbl_wid,fld_hgt);
    d_txt[clbl]->MoveResize(margin_hor+lbl_wid,nline,txt_fld,fld_hgt);
    nline += linspace;
  }

// Example: Numberentries, int and double

  string in_num[] = {"TimeBin","WindowLength","stop"};
  for (jstr=0; in_num[jstr] != "stop"; jstr++){
    clbl=in_num[jstr];
    wid_lbl = new TGLabel(data_box,clbl,TGLabel::GetDefaultGC()(),TGLabel::GetDefaultFontStruct(),kChildFrame,ucolor);
    d_num[clbl] = new TGNumberEntry(data_box, 0,11,-1,(TGNumberFormat::EStyle) 5);
    d_num[clbl]->GetNumberEntry()->SetName(clbl);
    d_num[clbl]->GetNumberEntry()->Connect("TextChanged(char*)", "gui_maps", this, "GetVar()");
    wid_lbl    ->MoveResize(margin_hor        ,nline,txt_wid,fld_hgt);
    d_num[clbl]->MoveResize(margin_hor+txt_wid,nline,fld_wid,fld_hgt);
    nline += linspace;
  }

// Example: Checkbuttons

  string in_boo[] = {"GraphPulse","GraphTime","stop"};
  for (jstr=0; in_boo[jstr] != "stop"; jstr++){
    clbl=in_boo[jstr];
    d_ckb[clbl] = new TGCheckButton(data_box, clbl);
    d_ckb[clbl]->SetBackgroundColor(ucolor);
    d_ckb[clbl]->SetName(clbl);
    d_ckb[clbl]->Connect("Toggled(Bool_t)", "gui_maps", this, "GetVar()");
    d_ckb[clbl]->MoveResize(margin_hor,nline,txt_wid,fld_hgt);
    nline += linspace;
  }

// Example: Comboboxes

  string in_cmb[] = {"SeparationLines","PeakAlgorythm","stop"};
  for (jstr=0; in_cmb[jstr] != "stop"; jstr++){
    clbl=in_cmb[jstr];
    wid_lbl = new TGLabel(data_box,clbl,TGLabel::GetDefaultGC()(),TGLabel::GetDefaultFontStruct(),kChildFrame,ucolor);
    d_cmb[clbl] = new TGComboBox(data_box,-1,kHorizontalFrame | kSunkenFrame | kDoubleBorder | kOwnBackground);
    d_cmb[clbl]->AddEntry("1",1);
    d_cmb[clbl]->AddEntry("2",2);
    wid_lbl    ->MoveResize(margin_hor        ,nline,txt_wid,fld_hgt);
    d_cmb[clbl]->MoveResize(margin_hor+txt_wid,nline,fld_wid,fld_hgt);
    data_box->AddFrame(d_cmb[clbl], new TGLayoutHints(kLHintsNormal,2,2,2,2));
    d_cmb[clbl]->SetName(clbl);
    d_cmb[clbl]->Connect("Selected(Int_t)", "gui_maps", this, "GetVar()");
    AddFrame(data_box, new TGLayoutHints(kLHintsExpandX | kLHintsExpandY));
    nline += linspace;
  }

  data_box->SetLayoutBroken(kTRUE);
  data_box->MoveResize(margin_hor,70,side_len,430);

//============
// MAIN frame
//============
  AddInput(kKeyPressMask | kKeyReleaseMask);
  MapSubwindows();
  SetWindowName("GUI_MAPS");
  MapWindow();
  MoveResize(300,300,side_len,side_len);

  string xmlfile = xml_dir+"default.xml";
  const char* cname = xmlfile.c_str();
  gui_maps::load_xml(cname);
}


void gui_maps::GetVar() {

// Updating the maps at every change in the GUI

  int val;
  double dval;
  stringstream ss;
  TGNumberEntry *entry = (TGNumberEntry*)gTQSender;
  TString clbl=entry->GetName();
  TString stype=map_xml[clbl][0];
  if (stype == "int"){
    val = d_num[clbl]->GetNumber();
    ss << val;
    map_xml[clbl][1] = ss.str();
  }
  if (stype == "double"){
    dval = d_num[clbl]->GetNumber();
    ss << dval;
    map_xml[clbl][1] = ss.str();
  }
  if (stype == "combo"){
    val = d_cmb[clbl]->GetSelected();
    ss << val;
    map_xml[clbl][1] = ss.str();
  }
  if (stype == "bool"){
    if (d_ckb[clbl]->IsDown()){
      map_xml[clbl][1]="true";
    }
    else{
      map_xml[clbl][1]="false";
    }
  }
  if (stype == "string"){
    map_xml[clbl][1] = d_txt[clbl]->GetText();
  }
}

int main(int argc, char *argv[]) {
  TApplication *theApp = new TApplication("gui_maps", &argc, argv);
  main_frame = new gui_maps(gClient->GetRoot(),10,10,kMainFrame | kVerticalFrame);
  theApp->Run();
}

void gui_maps::HandleMenu(Int_t menu_id) {

// Handle menu events

  TRootHelpDialog *hd;
  char * cxml = new char[xml_dir.length()];
  strcpy(cxml,xml_dir.c_str());
  TGFileInfo fi;
  fi.fFileTypes = gui_maps_types;
  fi.fIniDir = cxml;

  switch (menu_id) {
    case M_FILE_EXIT:
      CloseWindow();
      gApplication->Terminate(0);
      break;
    case M_FILE_LOAD:
      new TGFileDialog(gClient->GetRoot(), this, kFDOpen, &fi);
      cxml = fi.fIniDir;
      gui_maps::load_xml(fi.fFilename);
      break;
    case M_FILE_SAVE:
      new TGFileDialog(gClient->GetRoot(), this, kFDSave, &fi);
      cxml = fi.fIniDir;
      gui_maps::store_xml(fi.fFilename);
      break;
    case M_FILE_EXE:
      gui_maps::run_gui_maps();
      break;
    case M_HELP_ABOUT:
      hd = new TRootHelpDialog(this, "About GUI_MAPS...", 550, 250);
      hd->SetText(gHelpGUI_MAPS);
      hd->Popup();
      break;
  }
}

void gui_maps::load_xml(const char* cname) {

  TString clbl, stype;
  TString* cval;
  int val;
  double dval;
  map<TString, TString*>:: iterator siter;
  cval = new TString[2];

  map_xml = xml2map(cname);

  for (siter = map_xml.begin(); siter != map_xml.end(); ++siter){
    clbl=siter->first;
    cval=siter->second;
    stype = map_xml[clbl][0];
    if (stype == "bool"){
      if (cval[1] == "true"){
        d_ckb[clbl]->SetState(kButtonDown);
      }
      else{
        d_ckb[clbl]->SetState(kButtonUp);
      }
    }
    if (stype == "int"){
      val = atoi(cval[1]);
      d_num[clbl]->SetIntNumber(val);
    }
    if (stype == "double"){
      dval = atof(cval[1]);
      d_num[clbl]->SetNumber(dval);
    }
    if (stype == "combo"){
      val = atoi(cval[1]);
      d_cmb[clbl]->Select(val);
    }
    if (stype == "string"){
      d_txt[clbl]->SetText(cval[1]);
    }
  }
}

void gui_maps::store_xml(const char* cname) {
  map2xml(cname, map_xml);
}

void gui_maps::run_gui_maps() {
  cout << "Running my prog" << endl;
}

xml.cpp

#include "xml.h"

map<TString,TString*> xml2map(const char* cname) {

// Reading in a xml file and translating it into a map

  TXMLEngine* xml=0;
  XMLNodePointer_t mainnode =  0;
  XMLDocPointer_t xmldoc = 0;
  XMLNodePointer_t child;
  XMLAttrPointer_t attr = 0;
  TString svar,content,satt_val,satt_nam,sval;
  TString* cval;
  map<TString,TString*> map_loc;
  cval = new TString[2];

  cout << "Loading " << cname << endl;

  xmldoc = xml->ParseFile(cname);
  if (xmldoc==0){
    delete xml;
    return map_loc;
  }

  mainnode = xml->DocGetRootElement(xmldoc);
  attr     = xml->GetFirstAttr(mainnode);
  svar     = xml->GetNodeName(mainnode);
  content  = xml->GetNodeContent(mainnode);

  while (attr != 0){
    satt_nam = xml->GetAttrName(attr);
    satt_val = xml->GetAttrValue(attr);
    attr     = xml->GetNextAttr(attr);
    map_loc[svar] = new TString[2];
    map_loc[svar][0] = satt_val;
    map_loc[svar][1] = content;
  }

// display all child nodes   
  child = xml->GetChild(mainnode);
  while (child!=0){
    attr    = xml->GetFirstAttr(child);
    svar    = xml->GetNodeName(child);
    content = xml->GetNodeContent(child);
    while (attr != 0){
      satt_nam = xml->GetAttrName(attr);
      satt_val = xml->GetAttrValue(attr);
      attr    = xml->GetNextAttr(attr);
      sval = string(content);
      map_loc[svar] = new TString[2];
      map_loc[svar][0] = satt_val;
      map_loc[svar][1] = content;
    }
    child = xml->GetNext(child);
  }
  xml->FreeDoc(xmldoc);
  delete xml;

  return map_loc;
}

void map2xml(const char* cname, map<TString, TString*> map_loc) {

// Stores the map "map_loc" (representing the current GUI entries values)
// into the xml file named "cname"

  TXMLEngine* xml=0;
  XMLDocPointer_t xmldoc = 0;
  XMLNodePointer_t mainnode =  0;
  map<TString, TString*>:: iterator siter;
  TString clbl;
  TString* cval;
  XMLNodePointer_t xnode;
  cval = new TString[2];
  mainnode = xml->NewChild(0, 0, "main");

  cout << "Storing " << cname << endl;

  for (siter = map_loc.begin(); siter != map_loc.end(); ++siter){
    clbl=siter->first;
    cval=siter->second;
    xnode = xml->NewChild(mainnode, 0, clbl,cval[1]);
    xml->NewAttr(xnode, 0, "type",cval[0]);
  }

  xmldoc = xml->NewDoc();
  xml->DocSetRootElement(xmldoc, mainnode);
  xml->SaveDoc(xmldoc, cname);

  xml->FreeDoc(xmldoc);
  delete xml;
}

xml.h

#include <TROOT.h>
#include <Riostream.h>
#include <TXMLEngine.h>
#include <TString.h>
#include <stdlib.h>
#include <sstream>
#include <map>

map<TString, TString*> xml2map(const char*);
void map2xml(const char*, map<TString, TString*>);