source: trunk/Mars/mbase/MStatusDisplay.cc @ 19240

Last change on this file since 19240 was 19240, checked in by tbretz, 2 years ago
Allow to link the marslogo into the shared object so that no path is required.
File size: 99.8 KB
Line 
1/* ======================================================================== *\
2!
3! *
4! * This file is part of MARS, the MAGIC Analysis and Reconstruction
5! * Software. It is distributed to you in the hope that it can be a useful
6! * and timesaving tool in analysing Data of imaging Cerenkov telescopes.
7! * It is distributed WITHOUT ANY WARRANTY.
8! *
9! * Permission to use, copy, modify and distribute this software and its
10! * documentation for any purpose is hereby granted without fee,
11! * provided that the above copyright notice appear in all copies and
12! * that both that copyright notice and this permission notice appear
13! * in supporting documentation. It is provided "as is" without express
14! * or implied warranty.
15! *
16!
17!
18!   Author(s): Thomas Bretz, 4/2003 <mailto:tbretz@astro.uni-wuerzburg.de>
19!
20!   Copyright: MAGIC Software Development, 2003-2008
21!
22!
23\* ======================================================================== */
24
25/////////////////////////////////////////////////////////////////////////////
26//
27// MStatusDisplay
28//
29// This status display can be used (and is used) to display results in
30// a tabbed window. The window can be written to and read from a root file
31// (see Read and Write) or printed as a postscript file (see SaveAsPS).
32//
33// To write gif files of C-Macros use SaveAsGif()/SaveAsPNG() or SaveAsC().
34// Direct printing to the default printer (via lpr) can be done by
35// PrintPS().
36//
37// It has also to half status lines which can be used to display the status
38// or something going on. Together with the status lines it has a progress
39// bar which can display the progress of a job or loop.
40// Access the progress bar by GetProgressBar()
41//
42// To add a new tab and get a pointer to the newly created TCanvas
43// use AddTab.
44//
45// If you have a MStatusDisplay and you are not sure whether it was
46// destroyed by the user meanwhile use:
47//   gROOT->GetListOfSpecials()->FindObject(pointer);
48// Each MStatusDisplay is added to list list by its constructor and
49// removed from the list by the destructor.
50//
51// You can redirect an output to a MLog-logstream by calling SetLogStream().
52// To disable redirction call SetLogStream(NULL)
53//
54// Because updates to the tabs are only done/displayed if a tab is active
55// using the gui doesn't make things slower (<1%) if the first (legend
56// tab) is displayed. This gives you the possibility to look into
57// the current progress of a loop without loosing more time than the
58// single update of the tab.
59//
60/////////////////////////////////////////////////////////////////////////////
61#include "MStatusDisplay.h"
62
63#include <errno.h>
64#include <stdlib.h>               // mktemp (Ubunto 8.10)
65
66#include <fstream>                // fstream
67
68#include <TH1.h>                  // TH1::AddDirectory
69#include <TPDF.h>                 // TPDF
70#include <TSVG.h>                 // TSVG
71#include <TEnv.h>                 // TEnv
72#include <TLine.h>                // TLine
73#include <TMath.h>
74#include <TText.h>                // TText
75#include <TFile.h>                // gFile
76#include <TFrame.h>               // TFrame
77#include <TStyle.h>               // gStyle
78#include <TCanvas.h>              // TCanvas
79#include <TSystem.h>              // gSystem
80#include <TDatime.h>              // TDatime
81#include <TRandom.h>              // TRandom
82#include <TRegexp.h>              // TRegexp
83#include <TThread.h>              // TThread::Self()
84#include <TBrowser.h>             // TBrowser
85#include <TObjArray.h>            // TObjArray
86#include <TPostScript.h>          // TPostScript
87#include <TMethodCall.h>          // TMethodCall
88
89#include <TInterpreter.h>         // gInterpreter
90
91#include <TGTab.h>                // TGTab
92#include <TGLabel.h>              // TGLabel
93#include <TG3DLine.h>             // TGHorizontal3DLine
94#include <TGButton.h>             // TGPictureButton
95#include <TGTextView.h>           // TGTextView
96#include <TGComboBox.h>           // TGComboBox
97#include <TGStatusBar.h>          // TGStatusBar
98#include <TGFileDialog.h>         // TGFileDialog
99#include <TGProgressBar.h>        // TGHProgressBar
100#include <TGTextEditDialogs.h>    // TGPrintDialog
101#include <TRootEmbeddedCanvas.h>  // TRootEmbeddedCanvas
102
103#include "MString.h"
104
105#include "MLog.h"                 // MLog
106#include "MLogManip.h"            // inf, warn, err
107
108#include "MGList.h"               // MGList
109#include "MGMenu.h"               // MGMenu, TGMenu
110#include "MSearch.h"              // MSearch
111#include "MParContainer.h"        // MParContainer::GetDescriptor
112#include "MStatusArray.h"         // MStatusArray
113
114#if ROOT_VERSION_CODE <= ROOT_VERSION(5,22,00)
115#include "../mhbase/MH.h"
116#endif
117
118#undef DEBUG
119//#define DEBUG
120
121ClassImp(MStatusDisplay);
122
123using namespace std;
124
125// ------------ Workaround for a non working TGTextView::Search -------------
126#if ROOT_VERSION_CODE < ROOT_VERSION(3,02,05)
127class MGTextView : public TGTextView
128{
129public:
130    MGTextView(const TGWindow *parent, UInt_t w, UInt_t h, Int_t id = -1,
131               UInt_t sboptions = 0, ULong_t back = GetWhitePixel()) :
132    TGTextView(parent, w, h, id, sboptions, back) {}
133    MGTextView(const TGWindow *parent, UInt_t w, UInt_t h, TGText *text,
134               Int_t id = -1, UInt_t sboptions = 0, ULong_t back = GetWhitePixel()) :
135    TGTextView(parent, w, h, text, id, sboptions, back) {}
136    MGTextView(const TGWindow *parent, UInt_t w, UInt_t h, const char *string,
137               Int_t id = -1, UInt_t sboptions = 0, ULong_t back = GetWhitePixel()) :
138    TGTextView(parent, w, h, string, id, sboptions, back) {}
139
140    void Mark(Long_t xPos, Long_t yPos) { TGTextView::Mark(xPos, yPos); }
141    void UnMark()                       { TGTextView::UnMark(); }
142
143    Bool_t Search(const char *string, Bool_t direction, Bool_t caseSensitive)
144    {
145        // Taken from TGTextView::Search and modified.
146
147        TGLongPosition pos, pos2;
148        pos2.fX = pos2.fY = 0;
149        if (fIsMarked) {
150            if (!direction)
151            {
152                pos2.fX = fMarkedStart.fX;
153                pos2.fY = fMarkedStart.fY;
154            }
155            else
156            {
157                pos2.fX = fMarkedEnd.fX + 1;
158                pos2.fY = fMarkedEnd.fY;
159            }
160        }
161        if (!fText->Search(&pos, pos2, string, direction, caseSensitive))
162            return kFALSE;
163        UnMark();
164        fIsMarked = kTRUE;
165        fMarkedStart.fY = fMarkedEnd.fY = pos.fY;
166        fMarkedStart.fX = pos.fX;
167        fMarkedEnd.fX = fMarkedStart.fX + strlen(string);
168        pos.fY = ToObjYCoord(fVisible.fY);
169        if ((fMarkedStart.fY < pos.fY) ||
170            (ToScrYCoord(fMarkedStart.fY) >= (Int_t)fCanvas->GetHeight()))
171            pos.fY = fMarkedStart.fY;
172        pos.fX = ToObjXCoord(fVisible.fX, pos.fY);
173        if ((fMarkedStart.fX < pos.fX) ||
174            (ToScrXCoord(fMarkedStart.fX, pos.fY) >= (Int_t)fCanvas->GetWidth()))
175            pos.fX = fMarkedStart.fX;
176
177        SetVsbPosition((ToScrYCoord(pos.fY) + fVisible.fY)/fScrollVal.fY);
178        SetHsbPosition((ToScrXCoord(pos.fX, pos.fY) + fVisible.fX)/fScrollVal.fX);
179        DrawRegion(0, (Int_t)ToScrYCoord(fMarkedStart.fY), fCanvas->GetWidth(),
180                   UInt_t(ToScrYCoord(fMarkedEnd.fY+1) - ToScrYCoord(fMarkedEnd.fY)));
181
182        return kTRUE;
183    }
184};
185#else
186#define MGTextView TGTextView
187#endif
188
189// --------------------------------------------------------------------------
190
191TGCompositeFrame *MStatusDisplay::GetTabContainer(const char *name) const
192{
193#if ROOT_VERSION_CODE < ROOT_VERSION(4,03,05)
194    if (!fTab)
195        return 0;
196
197   TGFrameElement *el;
198   TGTabElement *tab = 0;
199   TGCompositeFrame *comp = 0;
200
201   TIter next(fTab->GetList());
202   next();           // skip first container
203
204   while ((el = (TGFrameElement *) next())) {
205      el = (TGFrameElement *) next();
206      comp = (TGCompositeFrame *) el->fFrame;
207      next();
208      tab = (TGTabElement *)el->fFrame;
209      if (name == tab->GetText()->GetString()) {
210         return comp;
211      }
212   }
213
214   return 0;
215#else
216   return fTab ? fTab->GetTabContainer(name) : 0;
217#endif
218}
219
220TGTabElement *MStatusDisplay::GetTabTab(const char *name) const
221{
222#if ROOT_VERSION_CODE < ROOT_VERSION(4,03,05)
223    if (!fTab)
224        return 0;
225
226   TGFrameElement *el;
227   TGTabElement *tab = 0;
228
229   TIter next(fTab->GetList());
230   next();           // skip first container
231
232   while ((el = (TGFrameElement *) next())) {
233      next();
234      tab = (TGTabElement *)el->fFrame;
235      if (name == tab->GetText()->GetString()) {
236         return tab;
237      }
238   }
239
240   return 0;
241#else
242   return fTab ? fTab->GetTabTab(name) : 0;
243#endif
244}
245// --------------------------------------------------------------------------
246
247
248// --------------------------------------------------------------------------
249//
250// Add menu bar to the GUI
251//
252void MStatusDisplay::AddMenuBar()
253{
254    //
255    // File Menu
256    //
257    MGPopupMenu *filemenu = new MGPopupMenu(gClient->GetDefaultRoot());
258    filemenu->AddEntry("New &Canvas",       kFileCanvas);
259    filemenu->AddEntry("New &Browser",      kFileBrowser);
260    filemenu->AddEntry("New &Tab",          kFileTab);
261    filemenu->AddSeparator();
262
263    const TString fname(MString::Format("Save %s.", gROOT->GetName()));
264    MGPopupMenu *savemenu = new MGPopupMenu(gClient->GetDefaultRoot());
265    savemenu->AddEntry(MString::Format("%s&ps",  fname.Data()),  kFileSaveAsPS);
266    savemenu->AddEntry(MString::Format("%sp&df", fname.Data()),  kFileSaveAsPDF);
267    savemenu->AddEntry(MString::Format("%s&svg", fname.Data()),  kFileSaveAsSVG);
268    savemenu->AddSeparator();
269    savemenu->AddEntry(MString::Format("%sp&ng", fname.Data()),  kFileSaveAsPNG);
270    savemenu->AddEntry(MString::Format("%s&gif", fname.Data()),  kFileSaveAsGIF);
271    savemenu->AddEntry(MString::Format("%s&jpg", fname.Data()),  kFileSaveAsJPG);
272    savemenu->AddEntry(MString::Format("%s&xpm", fname.Data()),  kFileSaveAsXPM);
273    savemenu->AddEntry(MString::Format("%s&tiff",fname.Data()),  kFileSaveAsTIFF);
274    savemenu->AddEntry(MString::Format("%s&bmp", fname.Data()),  kFileSaveAsBMP);
275    savemenu->AddEntry(MString::Format("%sx&ml", fname.Data()),  kFileSaveAsXML);
276    savemenu->AddEntry(MString::Format("%scs&v", fname.Data()),  kFileSaveAsCSV);
277    savemenu->AddSeparator();
278    savemenu->AddEntry(MString::Format("%s&C",    fname.Data()), kFileSaveAsC);
279    savemenu->AddEntry(MString::Format("%s&root", fname.Data()), kFileSaveAsRoot);
280    savemenu->AddEntry(MString::Format("%s&root (plain)", fname.Data()), kFileSaveAsPlainRoot);
281    savemenu->Associate(this);
282
283    filemenu->AddEntry("&Open...",          kFileOpen);
284    filemenu->AddPopup("&Save", savemenu);
285    filemenu->AddEntry("Save &As...",       kFileSaveAs);
286    filemenu->AddSeparator();
287    filemenu->AddEntry("&Reset",            kFileReset);
288    filemenu->AddSeparator();
289    filemenu->AddEntry("&Print",            kFilePrint);
290    filemenu->AddSeparator();
291    filemenu->AddEntry("C&lose",            kFileClose);
292    filemenu->AddEntry("E&xit",             kFileExit);
293    filemenu->Associate(this);
294
295    //
296    // Tab Menu
297    //
298    MGPopupMenu *tabmenu = new MGPopupMenu(gClient->GetDefaultRoot());
299    tabmenu->AddEntry("Next [&+]",          kTabNext);
300    tabmenu->AddEntry("Previous [&-]",      kTabPrevious);
301    tabmenu->AddSeparator();
302
303    const TString fname2(MString::Format("Save %s-i.", gROOT->GetName()));
304    MGPopupMenu *savemenu2 = new MGPopupMenu(gClient->GetDefaultRoot());
305    savemenu2->AddEntry(MString::Format("%s&ps",  fname2.Data()),  kTabSaveAsPS);
306    savemenu2->AddEntry(MString::Format("%sp&df", fname2.Data()),  kTabSaveAsPDF);
307    savemenu2->AddEntry(MString::Format("%s&svg", fname2.Data()),  kTabSaveAsSVG);
308    savemenu2->AddSeparator();
309    savemenu2->AddEntry(MString::Format("%sp&ng", fname2.Data()),  kTabSaveAsPNG);
310    savemenu2->AddEntry(MString::Format("%s&gif", fname2.Data()),  kTabSaveAsGIF);
311    savemenu2->AddEntry(MString::Format("%s&jpg", fname2.Data()),  kTabSaveAsJPG);
312    savemenu2->AddEntry(MString::Format("%s&xpm", fname2.Data()),  kTabSaveAsXPM);
313    savemenu2->AddEntry(MString::Format("%s&tiff",fname2.Data()),  kTabSaveAsTIFF);
314    savemenu2->AddEntry(MString::Format("%s&bmp", fname2.Data()),  kTabSaveAsBMP);
315    savemenu2->AddEntry(MString::Format("%sx&ml", fname2.Data()),  kTabSaveAsXML);
316    savemenu2->AddEntry(MString::Format("%scs&v", fname2.Data()),  kTabSaveAsCSV);
317    savemenu2->AddSeparator();
318    savemenu2->AddEntry(MString::Format("%s&C",    fname2.Data()), kTabSaveAsC);
319    savemenu2->AddEntry(MString::Format("%s&root", fname2.Data()), kTabSaveAsRoot);
320    savemenu2->Associate(this);
321
322    tabmenu->AddPopup("&Save", savemenu2);
323    tabmenu->AddEntry("Save tab &As...",    kTabSaveAs);
324    tabmenu->AddSeparator();
325    tabmenu->AddEntry("&Remove",            kTabRemove);
326    tabmenu->AddSeparator();
327    tabmenu->AddEntry("&Print",             kTabPrint);
328    tabmenu->Associate(this);
329
330    //
331    // Loop Menu
332    //
333    MGPopupMenu *loopmenu = new MGPopupMenu(gClient->GetDefaultRoot());
334    loopmenu->AddEntry("&Pause",       kLoopPause);
335    loopmenu->AddEntry("Single S&tep", kLoopStep);
336    loopmenu->AddSeparator();
337    loopmenu->AddEntry("&Stop",  kLoopStop);
338    loopmenu->Associate(this);
339
340    loopmenu->DisableEntry(kLoopStep);
341
342    //
343    // Loop Menu
344    //
345    MGPopupMenu *sizemenu = new MGPopupMenu(gClient->GetDefaultRoot());
346    sizemenu->AddEntry("Fit to 640x&480",   kSize640);
347    sizemenu->AddEntry("Fit to 768x&576",   kSize768);
348    sizemenu->AddEntry("Fit to 800x&600",   kSize800);
349    sizemenu->AddEntry("Fit to 960x7&20",   kSize960);
350    sizemenu->AddEntry("Fit to 1024x&768",  kSize1024);
351    sizemenu->AddEntry("Fit to 1152x&864",  kSize1152);
352    sizemenu->AddEntry("Fit to 1280x&1024", kSize1280);
353    sizemenu->AddEntry("Fit to 1400x1050",  kSize1400);
354    sizemenu->AddEntry("Fit to 1600x1200",  kSize1600);
355    sizemenu->AddEntry("Fit to &Desktop",   kSizeOptimum);
356    sizemenu->Associate(this);
357
358    //
359    // Log Menu
360    //
361    MGPopupMenu *logmenu = new MGPopupMenu(gClient->GetDefaultRoot());
362    logmenu->AddEntry("&Copy Selected", kLogCopy);
363    logmenu->AddEntry("Cl&ear all",     kLogClear);
364    logmenu->AddSeparator();
365    logmenu->AddEntry("Select &All",    kLogSelect);
366    logmenu->AddSeparator();
367    logmenu->AddEntry("&Find...",       kLogFind);
368    logmenu->AddSeparator();
369    logmenu->AddEntry("&Save",          kLogSave);
370    logmenu->AddEntry("Save &append",   kLogAppend);
371    logmenu->AddSeparator();
372    logmenu->AddEntry("&Print",         kLogPrint);
373    logmenu->Associate(this);
374
375    //
376    // Menu Bar
377    //
378    TGLayoutHints *layitem = new TGLayoutHints(kLHintsNormal, 0, 4, 0, 0);
379    fList->Add(layitem);
380
381    fMenuBar = new MGMenuBar(this, 1, 1, kHorizontalFrame);
382    fMenuBar->AddPopup("&File", filemenu, layitem);
383    fMenuBar->AddPopup("Lo&g",  logmenu,  layitem);
384    fMenuBar->AddPopup("&Size", sizemenu, layitem);
385    fMenuBar->AddPopup("&Tab",  tabmenu,  layitem);
386    fMenuBar->AddPopup("&Loop", loopmenu, layitem);
387    fMenuBar->BindKeys(this);
388    AddFrame(fMenuBar);
389
390    //
391    // Line below menu bar
392    //
393    TGLayoutHints *laylinesep  = new TGLayoutHints(kLHintsTop|kLHintsExpandX);
394    fList->Add(laylinesep);
395
396    TGHorizontal3DLine *linesep = new TGHorizontal3DLine(this);
397    AddFrame(linesep, laylinesep);
398
399    //
400    // Add everything to autodel list
401    //
402    fList->Add(savemenu);
403    fList->Add(savemenu2);
404    fList->Add(filemenu);
405    fList->Add(loopmenu);
406    fList->Add(sizemenu);
407    fList->Add(fMenuBar);
408    fList->Add(tabmenu);
409    fList->Add(logmenu);
410    fList->Add(linesep);
411}
412
413// --------------------------------------------------------------------------
414//
415// Adds an empty TGCompositeFrame which might be filled by the user
416//
417void MStatusDisplay::AddUserFrame()
418{
419    TGLayoutHints *lay=new TGLayoutHints(kLHintsExpandX);
420    fList->Add(lay);
421
422    fUserFrame = new TGCompositeFrame(this, 1, 1);
423    AddFrame(fUserFrame, lay);
424    fList->Add(fUserFrame);
425}
426
427// --------------------------------------------------------------------------
428//
429// Add the title tab
430//
431void MStatusDisplay::AddMarsTab()
432{
433    // Create Tab1
434    TGCompositeFrame *f = fTab->AddTab("-=MARS=-");
435
436    // Add list of tabs
437
438    TGComboBox *filter = new TGComboBox(f, kTabs);
439    fList->Add(filter);
440    filter->Associate(this);
441    filter->AddEntry("-=MARS=-", 0);
442    filter->Select(0);
443
444
445    TGLayoutHints *lay3 = new TGLayoutHints(kLHintsCenterX|kLHintsTop, 10, 10, 10, 5);
446    fList->Add(lay3);
447    f->AddFrame(filter, lay3);
448
449    // Add MARS version
450    TGLabel *l = new TGLabel(f, MString::Format("Official Release: V%s", MARSVER));
451    fList->Add(l);
452
453    filter->SetWidth(5*l->GetWidth()/4);
454    filter->SetHeight(4*l->GetHeight()/3);
455    filter->GetListBox()->SetHeight(l->GetHeight()*16);
456
457    TGLayoutHints *layb = new TGLayoutHints(kLHintsCenterX|kLHintsTop, 10, 10, 5, 5);
458    fList->Add(layb);
459    f->AddFrame(l, layb);
460
461    // Add root version
462    l = new TGLabel(f, MString::Format("Using ROOT v%s", ROOT_RELEASE));
463    fList->Add(l);
464
465    TGLayoutHints *lay = new TGLayoutHints(kLHintsCenterX|kLHintsTop);
466    fList->Add(lay);
467    f->AddFrame(l, lay);
468
469    {
470#define static static const
471#include "marslogo.xpm"
472#undef static
473
474        // Add Mars logo picture
475        const TGPicture *pic2 = fList->GetPicture("marslogo", marslogo);
476        if (pic2)
477        {
478            TGPictureButton *mars  = new TGPictureButton(f, pic2, kPicMars);
479            fList->Add(mars);
480            mars->Associate(this);
481
482            TGLayoutHints *lay2 = new TGLayoutHints(kLHintsCenterX|kLHintsCenterY, 10, 10, 5, 5);
483            fList->Add(lay2);
484            f->AddFrame(mars, lay2);
485        }
486    }
487
488    // Add date and time
489    l = new TGLabel(f, TDatime().AsString());
490    fList->Add(l);
491    f->AddFrame(l, lay);
492
493    // Add copyright notice
494    l = new TGLabel(f, MString::Format("(c) MARS Software Development, 2000-%d", TDatime().GetYear()));
495    fList->Add(l);
496    f->AddFrame(l, layb);
497
498    TGLayoutHints *layc = new TGLayoutHints(kLHintsCenterX|kLHintsTop, 10, 10, 0, 5);
499    fList->Add(layc);
500
501    const char *txt = "<< Thomas Bretz >>";
502    l = new TGLabel(f, txt);
503    fList->Add(l);
504    f->AddFrame(l, layc);
505}
506
507// --------------------------------------------------------------------------
508//
509// Adds the logbook tab to the GUI if it was not added previously.
510//
511// The logbook is updated four times a second only if the tab is visible.
512//
513// You can redirect an output to a MLog-logstream by calling SetLogStream().
514// To disable redirction call SetLogStream(NULL)
515//
516// if enable==kFALSE the stdout is disabled/enabled. Otherwise stdout
517// is ignored.
518//
519void MStatusDisplay::SetLogStream(MLog *log, Bool_t enable)
520{
521    if (gROOT->IsBatch())
522        return;
523
524    if (log && fLogBox==NULL)
525    {
526        fLogIdx = fTab->GetNumberOfTabs();
527
528        // Create Tab1
529        TGCompositeFrame *f = AddRawTab("-Logbook-");//fTab->AddTab("-Logbook-");
530
531        // Create Text View
532        fLogBox = new MGTextView(f, 1, 1); // , -1, 0, TGFrame::GetDefaultFrameBackground());
533        if (fFont)
534            fLogBox->SetFont(fFont);
535        //fLogBox->Associate(this);
536
537        // Add List box to the tab
538        TGLayoutHints *lay = new TGLayoutHints(kLHintsNormal|kLHintsExpandX|kLHintsExpandY,2,2,2,2);
539        f->AddFrame(fLogBox, lay);
540
541        // layout and map tab
542        Layout();
543        MapSubwindows();
544
545        // make it visible
546        // FIXME: This is a workaround, because TApplication::Run is not
547        //        thread safe against ProcessEvents. We assume, that if
548        //        we are not in the Main-Thread ProcessEvents() is
549        //        called by the TApplication Event Loop...
550        if (!TThread::Self()/*gApplication->InheritsFrom(TRint::Class())*/)
551            gClient->ProcessEventsFor(fTab);
552    }
553
554    if (log)
555    {
556        fLog = log;
557
558        log->SetOutputGui(fLogBox, kTRUE);
559        log->EnableOutputDevice(MLog::eGui);
560        if (!enable)
561            log->DisableOutputDevice(MLog::eStdout);
562
563        fLogTimer.Start();
564    }
565    else
566    {
567        fLogTimer.Stop();
568
569        fLog->DisableOutputDevice(MLog::eGui);
570        fLog->SetOutputGui(NULL);
571        if (!enable)
572            fLog->EnableOutputDevice(MLog::eStdout);
573
574        fLog = &gLog;
575    }
576}
577
578// --------------------------------------------------------------------------
579//
580// Add the Tabs and the predifined Tabs to the GUI
581//
582void MStatusDisplay::AddTabs()
583{
584    fTab = new TGTab(this, 300, 300);
585
586    AddMarsTab();
587
588    // Add fTab to Frame
589    TGLayoutHints *laytabs = new TGLayoutHints(kLHintsNormal|kLHintsExpandX|kLHintsExpandY, 5, 5, 5);
590    AddFrame(fTab, laytabs);
591
592    fList->Add(fTab);
593    fList->Add(laytabs);
594}
595
596// --------------------------------------------------------------------------
597//
598// Add the progress bar to the GUI. The Progress Bar range is set to
599// (0,1) as default.
600//
601void MStatusDisplay::AddProgressBar()
602{
603    TGLayoutHints *laybar=new TGLayoutHints(kLHintsExpandX, 5, 5, 5, 5);
604    fList->Add(laybar);
605
606    fBar=new TGHProgressBar(this);
607    fBar->SetRange(0, 1);
608    fBar->ShowPosition();
609    AddFrame(fBar, laybar);
610    fList->Add(fBar);
611}
612
613// --------------------------------------------------------------------------
614//
615// Set the progress bar position between 0 and 1. The Progress bar range
616// is assumed to be (0,1)
617//
618void MStatusDisplay::SetProgressBarPosition(Float_t p, Bool_t upd)
619{
620    if (!gClient || gROOT->IsBatch())
621        return;
622
623    fBar->SetPosition(p);
624    if (upd)
625        gClient->ProcessEventsFor(fBar);
626}
627
628// --------------------------------------------------------------------------
629//
630// Adds the status bar to the GUI
631//
632void MStatusDisplay::AddStatusBar()
633{
634    fStatusBar = new TGStatusBar(this, 1, 1);
635
636    //
637    // Divide it like the 'Golden Cut' (goldener Schnitt)
638    //
639    //     1-a     a
640    // 1: ------|----
641    //
642    // a/(1-a) = (1-a)/1
643    // a^2+a-1 = 0
644    //       a = (-1+-sqrt(1+4))/2 = sqrt(5)/2-1/2 = 0.618
645    //
646    Int_t p[] = {38-2, 62-8, 10};
647
648    fStatusBar->SetParts(p, 3);
649
650    TGLayoutHints *layb = new TGLayoutHints(kLHintsNormal|kLHintsExpandX, 5, 4, 0, 3);
651    AddFrame(fStatusBar, layb);
652
653    fList->Add(fStatusBar);
654    fList->Add(layb);
655}
656
657// --------------------------------------------------------------------------
658//
659// Change the text in the status line 1
660//
661void MStatusDisplay::SetStatusLine(const char *txt, Int_t i)
662{
663    if (gROOT->IsBatch())
664        return;
665    fStatusBar->SetText(txt, i);
666
667    // FIXME: This is a workaround, because TApplication::Run is not
668    //        thread safe against ProcessEvents. We assume, that if
669    //        we are not in the Main-Thread ProcessEvents() is
670    //        called by the TApplication Event Loop...
671    if (!TThread::Self()/*gApplication->InheritsFrom(TRint::Class())*/)
672        gClient->ProcessEventsFor(fStatusBar);
673}
674
675// --------------------------------------------------------------------------
676//
677// Display information about the name of a container
678//
679void MStatusDisplay::SetStatusLine2(const MParContainer &cont)
680{
681    SetStatusLine2(MString::Format("%s: %s", cont.GetDescriptor().Data(), cont.GetTitle()));
682}
683
684// --------------------------------------------------------------------------
685//
686// Get TGPopupMenu as defined by name from fMenuBar
687//
688TGPopupMenu *MStatusDisplay::GetPopup(const char *name)
689{
690    if (!fMenuBar)
691        return 0;
692
693    TGPopupMenu *m = fMenuBar->GetPopup(name);
694    if (!m)
695    {
696        *fLog << warn << name << " doesn't exist in menu bar." << endl;
697        return 0;
698    }
699
700    return m;
701}
702
703// --------------------------------------------------------------------------
704//
705// Default constructor. Opens a window with a progress bar. Get a pointer
706// to the bar by calling GetBar. This pointer can be used for the
707// eventloop.
708//
709// Be carefull: killing or closing the window while the progress meter
710//   is still in use may cause segmentation faults. Please kill the window
711//   always by deleting the corresponding object.
712//
713// You can give either width or height. (Set the value not given to -1)
714// The other value is calculated accordingly. If width and height are
715// given height is ignored. If width=height=0 an optimum size from
716// the desktop size is calculated.
717//
718// Update time default: 10s
719//
720MStatusDisplay::MStatusDisplay(Int_t w, Int_t h, Long_t t)
721: TGMainFrame((TGWindow*)((gClient?gClient:new TGClient),NULL), 1, 1), fName("MStatusDisplay"), fLog(&gLog), fBar(NULL), fTab(NULL), fTimer(this, t, kTRUE), fStatus(kLoopNone), fLogIdx(-1), fLogTimer(this, 250, kTRUE), fLogBox(NULL), fIsLocked(0)
722{
723    // p==NULL means: Take gClient->GetRoot() if not in batch mode
724    // see TGWindow::TGWindow()
725
726    // Make sure that the display is removed via RecursiveRemove
727    // from whereever possible.
728    SetBit(kMustCleanup);
729
730    //
731    // This is a possibility for the user to check whether this
732    // object has already been deleted. It will be removed
733    // from the list in the destructor.
734    //
735    gROOT->GetListOfSpecials()->Add(this);
736
737    fFont = gVirtualX->LoadQueryFont("7x13bold");
738    fMutex = new TMutex;
739
740    //
741    // In case we are in batch mode use a list of canvases
742    // instead of the Root Embedded Canvases in the TGTab
743    //
744    fBatch = new TList;
745    fBatch->SetOwner();
746
747    //
748    // Create a list handling GUI widgets
749    //
750    fList = new MGList;
751    fList->SetOwner();
752
753    //
754    // Create the layout hint for the root embedded canavses
755    //
756    fLayCanvas = new TGLayoutHints(kLHintsExpandX|kLHintsExpandY);
757    fList->Add(fLayCanvas);
758
759    //
760    // Add Widgets (from top to bottom)
761    //
762    // In newer root versions gClient!=NULL in batch mode!
763    if (!gClient || !gClient->GetRoot() || gROOT->IsBatch()) // BATCH MODE
764    {
765        Resize(644, 484);
766        return;
767    }
768
769    AddMenuBar();
770    AddUserFrame();
771    AddTabs();
772    AddProgressBar();
773    AddStatusBar();
774
775    //
776    // set the smallest and biggest size of the Main frame
777    // and move it to its appearance position
778    SetWMSizeHints(566, 476, 2048, 1536, 1, 1);
779    MoveResize(rand()%100+566, rand()%100+476, 566, 476);
780    if (h>0)
781        SetDisplayHeight(h);
782    if (w>0)
783        SetDisplayWidth(w);
784    if (w==0 && h==0)
785        SetOptimumSize();
786
787    //
788    // Now do an automatic layout of the widgets and display the window
789    //
790    Layout();
791    MapSubwindows();
792
793    SetWindowName("Status Display");
794    SetIconName("Status Display");
795
796    MapWindow();
797
798    UpdateMemory();
799
800    // FIXME: This is a workaround, because TApplication::Run is not
801    //        thread safe against ProcessEvents. We assume, that if
802    //        we are not in the Main-Thread ProcessEvents() is
803    //        called by the TApplication Event Loop...
804    if (!TThread::Self()/*gApplication->InheritsFrom(TRint::Class())*/)
805        gSystem->ProcessEvents();
806}
807
808// --------------------------------------------------------------------------
809//
810// Destruct the window with all its tiles. Also the Progress Bar object
811// is deleted.
812//
813MStatusDisplay::~MStatusDisplay()
814{
815    fTimer.Stop();
816
817#if ROOT_VERSION_CODE < ROOT_VERSION(3,10,01)
818    fTab = NULL; // See HandleEvent
819#endif
820
821    //
822    // Delete object from global object table so it cannot
823    // be deleted by chance a second time
824    //
825    gInterpreter->DeleteGlobal(this);
826
827    //
828    // This is a possibility for the user to check whether this
829    // object has already been deleted. It has been added
830    // to the list in the constructor.
831    //
832    gROOT->GetListOfSpecials()->Remove(this);
833
834    SetLogStream(NULL);
835
836    //
837    // Delete the list of objects corresponding to this object
838    //
839    delete fList;
840
841    //
842    // Delete the list of canvases used in batch mode
843    // instead of the Root Embedded Canvases in the TGTab
844    //
845    delete fBatch;
846
847    //
848    // Delete the font used for the logging window
849    //
850    if (fFont)
851        gVirtualX->DeleteFont(fFont);
852
853    //
854    // Delete mutex
855    //
856    delete fMutex;
857}
858
859// --------------------------------------------------------------------------
860//
861// Takes a TGCompositeFrame as argument. Searches for the first
862// TRootEmbeddedCanvas which is contained by it and returns a pointer
863// to the corresponding TCanvas. If it isn't found NULL is returned.
864//
865TRootEmbeddedCanvas *MStatusDisplay::GetEmbeddedCanvas(TGCompositeFrame &cf)
866{
867    TIter Next(cf.GetList());
868
869    TGFrameElement *f;
870    while ((f=(TGFrameElement*)Next()))
871        if (f->fFrame->InheritsFrom(TRootEmbeddedCanvas::Class()))
872            return (TRootEmbeddedCanvas*)f->fFrame;
873
874    return NULL;
875}
876
877// --------------------------------------------------------------------------
878//
879// Takes a TGCompositeFrame as argument. Searches for the first
880// TRootEmbeddedCanvas which is contained by it and returns a pointer
881// to the corresponding TCanvas. If it isn't found NULL is returned.
882//
883TCanvas *MStatusDisplay::GetCanvas(TGCompositeFrame &cf)
884{
885    TRootEmbeddedCanvas *ec = GetEmbeddedCanvas(cf);
886    return ec ? ec->GetCanvas() : NULL;
887}
888
889// --------------------------------------------------------------------------
890//
891// Returns the range of tabs containing valid canvases for the condition
892// num.
893//
894void MStatusDisplay::GetCanvasRange(Int_t &from, Int_t &to, Int_t num) const
895{
896    const Int_t max  = gROOT->IsBatch() ? fBatch->GetSize()+1 : fTab->GetNumberOfTabs();
897
898    from = num<0 ?   1 : num;
899    to   = num<0 ? max : num+1;
900}
901
902// --------------------------------------------------------------------------
903//
904// Returns GetCanvas of the i-th Tab.
905//
906TCanvas *MStatusDisplay::GetCanvas(int i) const
907{
908    if (gROOT->IsBatch())
909        return (TCanvas*)fBatch->At(i-1);
910
911    if (i<0 || i>=fTab->GetNumberOfTabs())
912    {
913        *fLog << warn << "MStatusDisplay::GetCanvas: Out of range." << endl;
914        return NULL;
915    }
916
917    return GetCanvas(*fTab->GetTabContainer(i));
918}
919
920// --------------------------------------------------------------------------
921//
922// Returns j-th pad of the i-th Tab.
923// Sets the pad to fill an entire window.
924//
925// This function can be used if single pad's out of an MStatusDisplay
926// have to be stored to file.
927//
928// ATTENTION: This function modifies the requested tab in MStatusDisplay itself!
929//
930TVirtualPad *MStatusDisplay::GetFullPad(const Int_t i, const Int_t j)
931{
932    if (!GetCanvas(i))
933    {
934        *fLog << warn << "MStatusDisplay::GetFullPad: i-th canvas not dound." << endl;
935        return NULL;
936    }
937
938    TVirtualPad *vpad = GetCanvas(i)->GetPad(j);
939    if (!vpad)
940    {
941        *fLog << warn << "MStatusDisplay::GetFullPad: Pad is out of range." << endl;
942        return NULL;
943    }
944
945    vpad->SetPad(0.,0.,1.,1.);
946    return vpad;
947}
948
949// --------------------------------------------------------------------------
950//
951// Searches for a TRootEmbeddedCanvas in the TGCompositeFramme of the
952// Tab with the name 'name'. Returns the corresponding TCanvas or
953// NULL if something isn't found.
954//
955TCanvas *MStatusDisplay::GetCanvas(const TString &name) const
956{
957    if (gROOT->IsBatch())
958        return (TCanvas*)fBatch->FindObject(name);
959
960    TGFrameElement *f;
961    TIter Next(fTab->GetList());
962    while ((f=(TGFrameElement*)Next()))
963    {
964        TObject *frame = f->fFrame;
965        if (!frame->InheritsFrom(TGTabElement::Class()))
966            continue;
967
968        TGTabElement *tab = (TGTabElement*)frame;
969        if (tab->GetString()==name)
970            break;
971    }
972
973    // Search for the next TGCompositeFrame in the list
974    while ((f=(TGFrameElement*)Next()))
975    {
976        TObject *frame = f->fFrame;
977        if (frame->InheritsFrom(TGCompositeFrame::Class()))
978            return GetCanvas(*(TGCompositeFrame*)frame);
979    }
980
981    return NULL;
982}
983
984// --------------------------------------------------------------------------
985//
986// Calls TCanvas::cd(), for the canvas returned by GetCanvas.
987//
988Bool_t MStatusDisplay::CdCanvas(const TString &name)
989{
990    TCanvas *c = GetCanvas(name);
991    if (!c)
992        return kFALSE;
993
994    c->cd();
995    return kTRUE;
996}
997
998// --------------------------------------------------------------------------
999//
1000// Return the number of user added tabs (not that in batch mode this
1001// exclude tabs without a canvas)
1002//
1003Int_t MStatusDisplay::GetNumTabs() const
1004{
1005    return gROOT->IsBatch() ? fBatch->GetEntries() : fTab->GetNumberOfTabs()-1;
1006}
1007
1008TGCompositeFrame *MStatusDisplay::AddRawTab(const char *name)
1009{
1010    // Add new tab
1011    TGCompositeFrame *f = fTab->AddTab(name);
1012
1013    TGComboBox *box = (TGComboBox*)fList->FindWidget(kTabs);
1014    box->AddEntry(name, box->GetListBox()->GetNumberOfEntries());
1015
1016    // layout and map new tab
1017    Layout();
1018    MapSubwindows();
1019    Layout();
1020
1021    UpdateMemory();
1022
1023    // display new tab in the main frame
1024    // FIXME: This is a workaround, because TApplication::Run is not
1025    //        thread safe against ProcessEvents. We assume, that if
1026    //        we are not in the Main-Thread ProcessEvents() is
1027    //        called by the TApplication Event Loop...
1028    if (!TThread::Self()/*gApplication->InheritsFrom(TRint::Class())*/)
1029        gClient->ProcessEventsFor(fTab);
1030
1031    *fLog << inf3 << "Adding Raw Tab '" << name << "' (" << f->GetWidth() << "x";
1032    *fLog << f->GetHeight() << ")" << endl;
1033
1034    // return pointer to new canvas
1035    return f;
1036}
1037
1038// --------------------------------------------------------------------------
1039//
1040// This function was connected to all created canvases. It is used
1041// to redirect GetObjectInfo into our own status bar.
1042//
1043// The 'connection' is done in AddTab
1044//
1045void MStatusDisplay::EventInfo(Int_t /*event*/, Int_t px, Int_t py, TObject *selected)
1046{
1047    //  Writes the event status in the status bar parts
1048    if (!selected)
1049        return;
1050
1051    TCanvas *c = (TCanvas*)gTQSender;
1052
1053    TVirtualPad* save=gPad;
1054
1055    gPad = c ? c->GetSelectedPad() : NULL;
1056
1057    if (gPad)
1058    {
1059        // Find the object which will get picked by the GetObjectInfo
1060        // due to buffer overflows in many root-versions
1061        // in TH1 and TProfile we have to work around and implement
1062        // our own GetObjectInfo which make everything a bit more
1063        // complicated.
1064#if ROOT_VERSION_CODE > ROOT_VERSION(5,22,00)
1065        SetStatusLine2(selected->GetObjectInfo(px,py));
1066#else
1067        TObjLink *link=0;
1068        static_cast<TPad*>(gPad)->Pick(px, py, link);
1069
1070        const TObject *o = link ? link->GetObject() : 0;
1071        if (o)
1072            SetStatusLine2(MH::GetObjectInfo(px, py, *o));
1073#endif
1074    }
1075
1076    gPad=save;
1077}
1078
1079// --------------------------------------------------------------------------
1080//
1081// Adds a new tab with the name 'name'. Adds a TRootEmbeddedCanvas to the
1082// tab and returns a reference to the corresponding TCanvas.
1083//
1084TCanvas &MStatusDisplay::AddTab(const char *name, const char *title)
1085{
1086    /*
1087    if (GetCanvas(name))
1088    {
1089        *fLog << warn;
1090        *fLog << "WARNING - A canvas '" << name << "' is already existing in the Status Display." << endl;
1091        *fLog << "          This can cause unexpected crahes!" << endl;
1092    }*/
1093
1094    gROOT->SetSelectedPad(0);
1095
1096    if (gROOT->IsBatch())
1097    {
1098        // 4 = 2*default border width of a canvas
1099        const UInt_t cw = GetWidth();
1100        const UInt_t ch = 2*cw/3 + 25; // 25: Menu, etc
1101
1102        // The constructor of TCanvas adds the canvas to the global list
1103        // of canvases gROOT->GetListOfCanvases(). If a canvas with an
1104        // identical name exists already in this list, the canvas is
1105        // deleted. In normal operation this might make sense and doesn't harm
1106        // because the embedded canvases behave different.
1107        // By creating the canvas without a name it is made sure that no
1108        // older canvas/tab vanished silently from the system (deleted from
1109        // the construtor). To make the handling of our canvases nevertheless
1110        // work well the name is set later. The list of canvases is also
1111        // part of the list of cleanups, thus fBatch need not to be added
1112        // to the list of cleanups.
1113        TCanvas *c = new TCanvas("", title?title:"", -cw, ch);
1114        c->SetName(name);
1115        c->SetFillColor(10); // White
1116        c->SetFrameBorderMode(0);
1117        c->SetBorderMode(0);
1118        fBatch->Add(c);
1119
1120        *fLog << inf3 << "Adding Canvas '" << name << "' (" << c->GetWw() << "x";
1121        *fLog << c->GetWh() << ", TCanvas=" << c << ")" << endl;
1122
1123        // Remove the canvas from the global list to make sure it is
1124        // not found by gROOT->FindObject
1125        //gROOT->GetListOfCanvases()->Remove(c);
1126        //gROOT->GetListOfCleanups()->Add(c);
1127
1128        return *c;
1129    }
1130
1131    // Add new tab
1132    TGCompositeFrame *f = fTab->AddTab(name);
1133
1134    // create root embedded canvas and add it to the tab
1135    TRootEmbeddedCanvas *ec = new TRootEmbeddedCanvas(name, f, f->GetWidth(), f->GetHeight(), kSunkenFrame);
1136    f->AddFrame(ec, fLayCanvas);
1137    fList->Add(ec);
1138
1139    // set background and border mode of the canvas
1140    TCanvas &c = *ec->GetCanvas();
1141
1142    if (title)
1143        c.SetTitle(title);
1144
1145    c.SetFillColor(10); // White
1146    c.SetFrameBorderMode(0);
1147    c.SetBorderMode(0);
1148
1149    // If kNoContextMenu set set kNoContextMenu of the canvas
1150    if (TestBit(kNoContextMenu))
1151        c.SetBit(kNoContextMenu);
1152
1153    // Connect all TCanvas::ProcessedEvent to this->EventInfo
1154    // This means, that after TCanvas has processed an event
1155    // EventInfo of this class is called, see TCanvas::HandleInput
1156    c.Connect("ProcessedEvent(Int_t,Int_t,Int_t,TObject*)",
1157              "MStatusDisplay", this, "EventInfo(Int_t,Int_t,Int_t,TObject*)");
1158
1159    // Make sure that root itself doesn't try to call GetObjectInfo
1160    // This is now handled from EventsInfo. This is necessary
1161    // due to the buffer overflow bug in GetObjectInfo of
1162    // TProfile and TH1
1163    c.ResetBit(TCanvas::kShowEventStatus);
1164
1165    // Remove the canvas from the global list to make sure it is
1166    // not found by gROOT->FindObject
1167    //gROOT->GetListOfCanvases()->Remove(&c);
1168    //gROOT->GetListOfCleanups()->Add(&c);
1169
1170    TGComboBox *box = (TGComboBox*)fList->FindWidget(kTabs);
1171    box->AddEntry(name, box->GetListBox()->GetNumberOfEntries());
1172
1173    // layout and map new tab
1174    Layout();          // seems to layout the TGCompositeFrame
1175    MapSubwindows();   // maps the TGCompositeFrame
1176    Layout();          // layout the embedded canvas in the frame
1177
1178    UpdateMemory();
1179
1180    // display new tab in the main frame
1181    // FIXME: This is a workaround, because TApplication::Run is not
1182    //        thread safe against ProcessEvents. We assume, that if
1183    //        we are not in the Main-Thread ProcessEvents() is
1184    //        called by the TApplication Event Loop...
1185    if (!TThread::Self()/*gApplication->InheritsFrom(TRint::Class())*/)
1186        gClient->ProcessEventsFor(fTab);
1187
1188    *fLog << inf3 << "Adding Tab '" << name << "' (" << f->GetWidth() << "x";
1189    *fLog << f->GetHeight() << ", TCanvas=" << &c << ")" << endl;
1190
1191    // return pointer to new canvas
1192    return c;
1193}
1194
1195// --------------------------------------------------------------------------
1196//
1197// Update a canvas in a tab, takes the corresponding TGCompositeFrame
1198// as an argument. This is necessary, because not all functions
1199// changing the contents of a canvas or pad can call SetModified()
1200// for the corresponding tab. If this is not called correctly the
1201// tab won't be updated calling TCanvas::Update(). So we simply
1202// redraw it by our own (instead we could recursively call
1203// TPad::Modified() for everything contained by the TCanvas and
1204// call TCanvas::Update() afterwards)
1205//
1206void MStatusDisplay::UpdateTab(TGCompositeFrame *f)
1207{
1208    if (!f)
1209        return;
1210
1211    //
1212    // If we are in a multithreaded environment (gThreadXAR) we
1213    // have to make sure, that thus function is called from
1214    // the main thread.
1215    //
1216    if (gThreadXAR)
1217    {
1218        // Tell the X-Requester how to call this method
1219        const TString str = MString::Format("%ld", (ULong_t)f);
1220
1221        TMethodCall call(IsA(), "UpdateTab", "NULL");
1222        void *arr[4] = { NULL, &call, this, (void*)(const char*)str };
1223
1224        // If this is not the main thread return
1225        if (((*gThreadXAR)("METH", 4, arr, NULL)))
1226            return;
1227    }
1228
1229    TCanvas *c=GetCanvas(*f);
1230    if (!c)
1231        return;
1232
1233    const TString name  = c->GetName();
1234    const TString title = c->GetTitle();
1235
1236    if (!gROOT->IsBatch())
1237        fStatusBar->SetText(title, 0);
1238
1239    // It seems that we cannot use this because an update
1240    // initiated by a gui event can interfere with the regular
1241    // update from the timer and then ProcessEvents will
1242    // end in an infinite loop.
1243    // SetStatusLine1(title);
1244
1245
1246    //
1247    // Secure calls to update the tabs against itself, at least
1248    // c->Paint() or c->Flush() may crash X (bad drawable).
1249    // This makes sure, that a X call is not interuppted by
1250    // another X-call which was started from an gui interrrupt
1251    // in the same thread
1252    //
1253    const Int_t rc = fMutex->TryLock();
1254    if (rc==13)
1255        gLog << warn << "MStatusDisplay::UpdateTab - mutex is already locked by this thread." << endl;
1256
1257    if (rc)
1258        return;
1259
1260#if ROOT_VERSION_CODE < ROOT_VERSION(3,10,02)
1261    TPad *padsav = (TPad*)gPad;
1262    if (!gPad)
1263        c->cd();
1264#endif
1265
1266    if (!c->IsBatch())
1267        c->FeedbackMode(kFALSE);  // Goto double buffer mode
1268
1269    //
1270    // Doing this ourself gives us the possibility to repaint
1271    // the canvas in any case (Paint() instead of PaintModified())
1272    //
1273    c->Paint();                   // Repaint all pads
1274    c->Flush();                   // Copy all pad pixmaps to the screen
1275
1276#if ROOT_VERSION_CODE < ROOT_VERSION(3,10,02)
1277    if (padsav)
1278        padsav->cd();
1279    else
1280        gPad=NULL;
1281#endif
1282
1283    //c->SetCursor(kCross);
1284
1285    // Old version
1286    //c->Modified();
1287    //c->Update();
1288    //c->Paint();
1289
1290    fMutex->UnLock();
1291}
1292
1293TString MStatusDisplay::PrintDialog(TString &p, TString &c, TString &t, const char *ext)
1294{
1295    // If not in batch mode open a user dialog
1296    if (!gROOT->IsBatch())
1297    {
1298        char *cprinter = StrDup(p);
1299        char *ccmd     = StrDup(c);
1300
1301        Int_t rc=0;
1302        new TGPrintDialog(fClient->GetRoot(), this, 400, 150, &cprinter, &ccmd, &rc);
1303        if (rc)
1304        {
1305            p = cprinter; // default has been changed
1306            c = ccmd;
1307        }
1308
1309        delete [] cprinter;
1310        delete [] ccmd;
1311
1312        if (!rc)
1313            return "";
1314    }
1315
1316    if (c.Contains("%f") && ext)
1317    {
1318        // Get temporary file name
1319        TString name = "mars";
1320
1321        FILE *f = gSystem->TempFileName(name, t);
1322        if (!f)
1323        {
1324            *fLog << warn << "MStatusDisplay::PrintDialog: Couldn't create temporary file in " << t << endl;
1325            SetStatusLine2("failed!");
1326            return "";
1327        }
1328        fclose(f);
1329
1330        // remove temp file
1331        gSystem->Unlink(name);
1332        name += ".";
1333        name += ext;
1334
1335        t = name;
1336    }
1337
1338    // compile command
1339    TString cmd(c);
1340
1341    // if sprinter.IsNull we assume that everything around %p can
1342    // be omitted and the program uses some kind of default
1343    if (p.IsNull())
1344    {
1345        TString sub;
1346        while (1)
1347        {
1348            sub = TString(cmd(TRegexp(" .*%p.* "))).Strip(TString::kBoth);
1349            if (sub.IsNull())
1350                break;
1351
1352            cmd.ReplaceAll(sub, "");
1353        }
1354    }
1355
1356    cmd.ReplaceAll("%p", p);
1357    cmd.ReplaceAll("%f", t);
1358
1359    return cmd;
1360}
1361
1362// --------------------------------------------------------------------------
1363//
1364// Saves the given canvas (pad) or all pads (num<0) as a temporary
1365// postscript file and prints it.
1366//
1367// The default command line c is: lpr -P%p %f
1368//   %p: printer name
1369//   %f: temporary file name
1370//
1371// The default printer name p is: <empty>
1372//
1373// Both can be changed in .rootrc by:
1374//   PrintPS.Printer
1375//   PrintPS.Command
1376//
1377// Ant the location of the temporary file t can by changed by
1378//   Print.Directory
1379// the default is the system default directory (normally /tmp)
1380//
1381Int_t MStatusDisplay::PrintPS(Int_t num, const char *p, const char *c, const char *t)
1382{
1383    static TString sprinter = gEnv->GetValue("PrintPS.Printer", p&&*p?p:"");
1384    static TString scmd     = gEnv->GetValue("PrintPS.Command", c&&*c?c:"lpr -P%p %f");
1385
1386    TString tmp = gEnv->GetValue("Print.Directory", t&&*t?t:gSystem->TempDirectory());
1387
1388    TString cmd = PrintDialog(sprinter, scmd, tmp, "ps");
1389    if (cmd.IsNull())
1390        return 0;
1391
1392    // set status lines
1393    SetStatusLine1("Printing...");
1394    SetStatusLine2("");
1395
1396    // print to temporary file
1397    const Int_t pages = SaveAsPS(num, tmp);
1398
1399    // check
1400    if (!pages)
1401    {
1402        *fLog << warn << "MStatusDisplay::Print: Sorry, couldn't save file as temporary postscript!" << endl;
1403        SetStatusLine2("Failed!");
1404        return 0;
1405    }
1406
1407    // execute command
1408    *fLog << dbg << "Executing: " << cmd << endl;
1409    gSystem->Exec(cmd);
1410
1411    // remove temporary file
1412    gSystem->Unlink(tmp);
1413
1414    SetStatusLine2(MString::Format("Done (%dpage(s))", pages));
1415
1416    return pages;
1417}
1418
1419// --------------------------------------------------------------------------
1420//
1421// Remove tab no i if this tab contains a TRootEmbeddedCanvas
1422//
1423void MStatusDisplay::RemoveTab(int i)
1424{
1425    TGCompositeFrame *f = fTab->GetTabContainer(i);
1426    if (!f)
1427        return;
1428
1429    TRootEmbeddedCanvas *ec = GetEmbeddedCanvas(*f);
1430    if (!ec)
1431        return;
1432
1433    TCanvas *c = ec->GetCanvas();
1434    if (!c)
1435        return;
1436
1437    // Repair the "Workaround" being in the RecursiveRemove list
1438    // but not in a list checked by gROOT->FindObject
1439    //gROOT->GetListOfCleanups()->Remove(c);
1440    //gROOT->GetListOfCanvases()->Add(c);
1441
1442    // FIXME: Due to our workaround this is necessary for a successfull deletion
1443    //c->cd();
1444
1445    const TString name(c->GetName());
1446
1447    f->RemoveFrame(ec);
1448    delete fList->Remove(ec);
1449
1450    fTab->RemoveTab(i);
1451    fTab->SetTab(0);
1452
1453    TGComboBox *box = (TGComboBox*)fList->FindWidget(kTabs);
1454    box->RemoveEntry(i);
1455    for (int j=i; j<box->GetListBox()->GetNumberOfEntries(); j++)
1456    {
1457        TGTextLBEntry *entry = (TGTextLBEntry *)box->GetListBox()->Select(j+1, kFALSE);
1458        box->AddEntry(entry->GetText()->GetString(), j);
1459        box->RemoveEntry(j+1);
1460    }
1461    box->GetListBox()->Select(0);
1462
1463    // Looks strange...
1464    // const Int_t n = fTab->GetNumberOfTabs();
1465    // fTab->SetTab(i<=n-1 ? i : i-1);
1466
1467    // layout and map new tab
1468    Layout();          // seems to layout the TGCompositeFrame
1469    MapSubwindows();   // maps the TGCompositeFrame
1470    Layout();          // layout the embedded canvas in the frame
1471
1472    UpdateMemory();
1473
1474    // display new tab in the main frame
1475    // FIXME: This is a workaround, because TApplication::Run is not
1476    //        thread safe against ProcessEvents. We assume, that if
1477    //        we are not in the Main-Thread ProcessEvents() is
1478    //        called by the TApplication Event Loop...
1479    if (!TThread::Self()/*gApplication->InheritsFrom(TRint::Class())*/)
1480        gClient->ProcessEventsFor(fTab);
1481
1482    *fLog << inf << "Removed Tab #" << i << " '" << name << "'" << endl;
1483}
1484
1485// --------------------------------------------------------------------------
1486//
1487// Use this to check whether the MStatusDisplay still contains the
1488// TCanvas c. It could be removed meanwhile by menu usage.
1489//
1490Bool_t MStatusDisplay::HasCanvas(const TCanvas *c) const
1491{
1492    if (!c)
1493        return kFALSE;
1494
1495    // If you encounter unexpected crashes here, check if
1496    // a canvas is existing twice in the list or has been
1497    // deleted by accident. (Check AddTab)
1498
1499    if (gROOT->IsBatch())
1500        return (Bool_t)fBatch->FindObject(c);
1501
1502    for (int i=1; i<fTab->GetNumberOfTabs(); i++)
1503        if (c==GetCanvas(i))
1504            return kTRUE;
1505    return kFALSE;
1506}
1507
1508/*
1509    if (...)
1510       fMenu->AddPopup("&CaOs", fCaOs, NULL);
1511    else
1512       fMenu->RemovePopup("CaOs");
1513    fMenu->Resize(fMenu->GetDefaultSize());
1514    MapSubwindows();
1515    MapWindow();
1516 */
1517
1518void MStatusDisplay::Reset()
1519{
1520    if (gROOT->IsBatch())
1521    {
1522        fBatch->Delete();
1523        return;
1524    }
1525
1526    for (int i=fTab->GetNumberOfTabs()-1; i>0; i--)
1527        RemoveTab(i);
1528}
1529
1530Bool_t MStatusDisplay::SaveLogAsPS(const char *n) const
1531{
1532    TString name(n);
1533    AddExtension(name, "ps");
1534
1535    // Code taken from TGTextEdit::Print
1536    const TString pipe = MString::Format("a2ps -o%s", name.Data());
1537    FILE *p = gSystem->OpenPipe(pipe, "w");
1538    if (!p)
1539    {
1540        *fLog << err << "ERROR - Couldn't open pipe " << pipe << ": " << strerror(errno) << endl;
1541        return kFALSE;
1542    }
1543
1544    TGText *text = fLogBox->GetText();
1545
1546    char   *buf1, *buf2;
1547    Long_t  len;
1548    ULong_t i = 0;
1549    TGLongPosition pos;
1550
1551    Bool_t rc = kTRUE;
1552
1553    pos.fX = pos.fY = 0;
1554    while (pos.fY < text->RowCount())
1555    {
1556        len = text->GetLineLength(pos.fY);
1557        buf1 = text->GetLine(pos, len);
1558        buf2 = new char[len + 2];
1559        strncpy(buf2, buf1, (UInt_t)len);
1560        buf2[len]   = '\n';
1561        buf2[len+1] = '\0';
1562        while (buf2[i] != '\0') {
1563            if (buf2[i] == '\t') {
1564                ULong_t j = i+1;
1565                while (buf2[j] == 16 && buf2[j] != '\0')
1566                    j++;
1567                strcpy(buf2+i+1, buf2+j);
1568            }
1569            i++;
1570        }
1571
1572        len = sizeof(char)*(strlen(buf2)+1);
1573
1574        const size_t ret = fwrite(buf2, len, 1, p);
1575        delete [] buf1;
1576        delete [] buf2;
1577
1578        if (ret!=1)
1579        {
1580            *fLog << err << "ERROR - fwrite to pipe " << pipe << " failed: " << strerror(errno) << endl;
1581            rc = kFALSE;
1582            break;
1583        }
1584
1585        pos.fY++;
1586    }
1587    gSystem->ClosePipe(p);
1588
1589    return rc;
1590}
1591
1592// --------------------------------------------------------------------------
1593//
1594// Print the log text.
1595//
1596// The default command line c is: a2ps -P%p
1597//   %p: printer name
1598//
1599// The default printer name p is: <empty>
1600//
1601// Both can be changed in .rootrc by:
1602//   PrintText.Printer
1603//   PrintText.Command
1604//
1605Bool_t MStatusDisplay::PrintLog(const char *p, const char *c)
1606{
1607    static TString sprinter = gEnv->GetValue("PrintText.Printer", p&&*p?p:"");
1608    static TString scmd     = gEnv->GetValue("PrintText.Command", c&&*c?c:"a2ps -P%p");
1609
1610    TString tmp;
1611    TString cmd = PrintDialog(sprinter, scmd, tmp);
1612    if (cmd.IsNull())
1613        return kFALSE;
1614
1615    // set status lines
1616    SetStatusLine1("Printing...");
1617    SetStatusLine2("");
1618
1619    // print to temporary file
1620    if (!SaveLogAsPS(cmd))
1621    {
1622        *fLog << warn << "MStatusDisplay::PrintLog: Sorry, couldn't create postscript!" << endl;
1623        SetStatusLine2("Failed!");
1624        return kFALSE;
1625    }
1626
1627    // execute command
1628    *fLog << dbg << "Executing: " << cmd << endl;
1629    gSystem->Exec(cmd);
1630
1631    SetStatusLine2("Done.");
1632
1633    return kTRUE;
1634}
1635
1636// --------------------------------------------------------------------------
1637//
1638// Process the kC_COMMAND, kCM_MENU  messages
1639//
1640Bool_t MStatusDisplay::ProcessMessageCommandMenu(Long_t id)
1641{
1642    switch (id)
1643    {
1644    case kLoopPause:
1645        {
1646            TGPopupMenu *m = GetPopup("Loop");
1647            if (!m)
1648                return kTRUE;
1649
1650            if (fStatus==kLoopNone)
1651            {
1652                fStatus = (Status_t)kLoopPause;
1653                m->CheckEntry(kLoopPause);
1654                m->EnableEntry(kLoopStep);
1655                return kTRUE;
1656            }
1657            if (fStatus==kLoopPause)
1658            {
1659                fStatus = (Status_t)kLoopNone;
1660                m->UnCheckEntry(kLoopPause);
1661                m->DisableEntry(kLoopStep);
1662                return kTRUE;
1663            }
1664        }
1665        return kTRUE;
1666
1667    case kLoopStep:
1668        fStatus = (Status_t)kLoopStep;
1669        return kTRUE;
1670
1671    case kLoopStop:
1672    case kFileClose:
1673    case kFileExit:
1674        if (id==kFileExit || id==kFileClose)
1675            if (Close())
1676                delete this;
1677        fStatus = (Status_t)id;
1678        return kTRUE;
1679
1680    case kFileCanvas:
1681        new TCanvas;
1682        return kTRUE;
1683
1684    case kFileBrowser:
1685        new TBrowser;
1686        return kTRUE;
1687
1688    case kFileTab:
1689        AddTab(MString::Format("%d", fTab->GetNumberOfTabs()));
1690        return kTRUE;
1691
1692    case kFileReset:
1693        Reset();
1694        return kTRUE;
1695
1696    case kFileOpen:
1697        Open();
1698        return kTRUE;
1699
1700    case kFileSaveAs:
1701        SaveAs();
1702        return kTRUE;
1703
1704    case kFileSaveAsPS:
1705        SaveAsPS();
1706        return kTRUE;
1707
1708    case kFileSaveAsPDF:
1709        SaveAsPDF();
1710        return kTRUE;
1711
1712    case kFileSaveAsSVG:
1713        SaveAsSVG();
1714        return kTRUE;
1715
1716    case kFileSaveAsPNG:
1717        SaveAsPNG();
1718        return kTRUE;
1719
1720    case kFileSaveAsGIF:
1721        SaveAsGIF();
1722        return kTRUE;
1723
1724    case kFileSaveAsXPM:
1725        SaveAsXPM();
1726        return kTRUE;
1727
1728    case kFileSaveAsJPG:
1729        SaveAsJPG();
1730        return kTRUE;
1731
1732    case kFileSaveAsTIFF:
1733        SaveAsTIFF();
1734        return kTRUE;
1735
1736    case kFileSaveAsBMP:
1737        SaveAsBMP();
1738        return kTRUE;
1739
1740    case kFileSaveAsXML:
1741        SaveAsXML();
1742        return kTRUE;
1743
1744    case kFileSaveAsCSV:
1745        SaveAsCSV();
1746        return kTRUE;
1747
1748    case kFileSaveAsC:
1749        SaveAsC();
1750        return kTRUE;
1751
1752    case kFileSaveAsRoot:
1753        SaveAsRoot();
1754        return kTRUE;
1755
1756    case kFileSaveAsPlainRoot:
1757        SaveAsPlainRoot(kTRUE);
1758        return kTRUE;
1759
1760    case kFilePrint:
1761        PrintPS();
1762        return kTRUE;
1763
1764    case kTabSaveAs:
1765        SaveAs(fTab->GetCurrent());
1766        return kTRUE;
1767
1768    case kTabSaveAsPS:
1769        SaveAsPS(fTab->GetCurrent());
1770        return kTRUE;
1771
1772    case kTabSaveAsPDF:
1773        SaveAsPDF(fTab->GetCurrent());
1774        return kTRUE;
1775
1776    case kTabSaveAsSVG:
1777        SaveAsSVG(fTab->GetCurrent());
1778        return kTRUE;
1779
1780    case kTabSaveAsPNG:
1781        SaveAsPNG(fTab->GetCurrent());
1782        return kTRUE;
1783
1784    case kTabSaveAsGIF:
1785        SaveAsGIF(fTab->GetCurrent());
1786        return kTRUE;
1787
1788    case kTabSaveAsXPM:
1789        SaveAsXPM(fTab->GetCurrent());
1790        return kTRUE;
1791
1792    case kTabSaveAsJPG:
1793        SaveAsJPG(fTab->GetCurrent());
1794        return kTRUE;
1795
1796    case kTabSaveAsTIFF:
1797        SaveAsTIFF(fTab->GetCurrent());
1798        return kTRUE;
1799
1800    case kTabSaveAsBMP:
1801        SaveAsBMP(fTab->GetCurrent());
1802        return kTRUE;
1803
1804    case kTabSaveAsXML:
1805        SaveAsXML(fTab->GetCurrent());
1806        return kTRUE;
1807
1808    case kTabSaveAsCSV:
1809        SaveAsCSV(fTab->GetCurrent());
1810        return kTRUE;
1811
1812    case kTabSaveAsC:
1813        SaveAsC(fTab->GetCurrent());
1814        return kTRUE;
1815
1816    case kTabSaveAsPlainRoot:
1817        SaveAsPlainRoot(fTab->GetCurrent());
1818        return kTRUE;
1819
1820    case kTabSaveAsRoot:
1821        SaveAsRoot(fTab->GetCurrent());
1822        return kTRUE;
1823
1824    case kTabPrint:
1825        PrintPS(fTab->GetCurrent());
1826        return kTRUE;
1827
1828    case kTabNext:
1829        fTab->SetTab(fTab->GetCurrent()+1);
1830        return kTRUE;
1831
1832    case kTabPrevious:
1833        fTab->SetTab(fTab->GetCurrent()-1);
1834        return kTRUE;
1835
1836    case kTabRemove:
1837        RemoveTab(fTab->GetCurrent());
1838        return kTRUE;
1839
1840    case kSize640:
1841        SetDisplaySize(640, 480);
1842        return kTRUE;
1843    case kSize768: 
1844        SetDisplaySize(768, 576);
1845        return kTRUE;
1846    case kSize800: 
1847        SetDisplaySize(800, 600);
1848        return kTRUE;
1849    case kSize960: 
1850        SetDisplaySize(960, 720);
1851        return kTRUE;
1852    case kSize1024: 
1853        SetDisplaySize(1024, 768);
1854        return kTRUE;
1855    case kSize1152:
1856        SetDisplaySize(1152, 864);
1857        return kTRUE;
1858    case kSize1280: 
1859        SetDisplaySize(1280, 1024);
1860        return kTRUE;
1861    case kSize1400:
1862        SetDisplaySize(1400, 1050);
1863        return kTRUE;
1864    case kSize1600:
1865        SetDisplaySize(1600, 1200);
1866        return kTRUE;
1867    case kSizeOptimum:
1868        SetOptimumSize();
1869        return kTRUE;
1870
1871    case kLogClear:
1872        fLogBox->Clear();
1873        return kTRUE;
1874    case kLogCopy:
1875        fLogBox->Copy();
1876        return kTRUE;
1877    case kLogSelect:
1878        fLogBox->SelectAll();
1879        return kTRUE;
1880    case kLogFind:
1881        new MSearch(this);
1882        return kTRUE;
1883    case kLogSave:
1884        SetStatusLine1("Saving log...");
1885        SetStatusLine2("");
1886        *fLog << inf << "Saving log... " << flush;
1887        if (fLogBox->GetText()->Save(MString::Format("%s.log", gROOT->GetName())))
1888        {
1889            *fLog << "done." << endl;
1890            SetStatusLine2("done.");
1891        }
1892        else
1893        {
1894            *fLog << "failed!" << endl;
1895            SetStatusLine2("Failed!");
1896        }
1897        return kTRUE;
1898
1899    case kLogAppend:
1900        SetStatusLine1("Appending log...");
1901        SetStatusLine2("");
1902        *fLog << inf << "Appending log... " << flush;
1903        if (fLogBox->GetText()->Append(MString::Format("%s.log", gROOT->GetName())))
1904        {
1905            *fLog << "done." << endl;
1906            SetStatusLine2("done.");
1907        }
1908        else
1909        {
1910            *fLog << "failed!" << endl;
1911            SetStatusLine2("Failed!");
1912        }
1913        return kTRUE;
1914
1915    case kLogPrint:
1916        PrintLog();
1917        return kTRUE;
1918#ifdef DEBUG
1919    default:
1920        cout << "Command-Menu #" << id << endl;
1921#endif
1922    }
1923    return kTRUE;
1924
1925}
1926
1927// --------------------------------------------------------------------------
1928//
1929// Process the kC_COMMAND messages
1930//
1931Bool_t MStatusDisplay::ProcessMessageCommand(Long_t submsg, Long_t mp1, Long_t mp2)
1932{
1933    switch (submsg)
1934    {
1935    case kCM_MENU: // 1
1936        return ProcessMessageCommandMenu(mp1); // mp2=userdata
1937    case kCM_TAB:  // 8
1938        /*
1939         for (int i=0; i<fTab->GetNumberOfTabs(); i++)
1940         fTab->GetTabContainer(i)->UnmapWindow();
1941         */
1942        UpdateTab(fTab->GetTabContainer(mp1));
1943        //fTab->GetTabContainer(mp1)->MapWindow();
1944
1945        /*
1946        if (mp1>0)
1947            fMenu->AddPopup("&CaOs", fCaOs, NULL);
1948        else
1949            fMenu->RemovePopup("CaOs");
1950        fMenu->Resize(fMenu->GetDefaultSize());
1951        MapSubwindows();
1952        MapWindow();
1953        */
1954        return kTRUE;
1955    case kCM_COMBOBOX: // 7
1956        if (mp1==kTabs)
1957            fTab->SetTab(mp2);
1958        return kTRUE;
1959#ifdef DEBUG
1960    case kCM_MENUSELECT: // 2
1961        cout << "Command-Menuselect #" << mp1 << " (UserData=" << (void*)mp2 << ")" << endl;
1962        return kTRUE;
1963
1964    case kCM_BUTTON: // 3
1965        cout << "Command-Button." << endl;
1966        return kTRUE;
1967
1968    case kCM_CHECKBUTTON: // 4
1969        cout << "Command-CheckButton." << endl;
1970        return kTRUE;
1971
1972    case kCM_RADIOBUTTON: // 5
1973        cout << "Command-RadioButton." << endl;
1974        return kTRUE;
1975
1976    case kCM_LISTBOX: // 6
1977        cout << "Command-Listbox #" << mp1 << " (LineId #" << mp2 << ")" << endl;
1978        return kTRUE;
1979    default:
1980        cout << "Command: " << "Submsg:" << submsg << " Mp1=" << mp1 << " Mp2=" << mp2 << endl;
1981#endif
1982    }
1983    return kTRUE;
1984}
1985
1986// --------------------------------------------------------------------------
1987//
1988// Process the kC_TEXTVIEW messages
1989//
1990Bool_t MStatusDisplay::ProcessMessageTextview(Long_t /*submsg*/, Long_t /*mp1*/, Long_t /*mp2*/)
1991{
1992    // kC_TEXTVIEW, kTXT_ISMARKED, widget id, [true|false]                  //
1993    // kC_TEXTVIEW, kTXT_DATACHANGE, widget id, 0                           //
1994    // kC_TEXTVIEW, kTXT_CLICK2, widget id, position (y << 16) | x)         //
1995    // kC_TEXTVIEW, kTXT_CLICK3, widget id, position (y << 16) | x)         //
1996    // kC_TEXTVIEW, kTXT_F3, widget id, true                                //
1997    // kC_TEXTVIEW, kTXT_OPEN, widget id, 0                                 //
1998    // kC_TEXTVIEW, kTXT_CLOSE, widget id, 0                                //
1999    // kC_TEXTVIEW, kTXT_SAVE, widget id, 0                                 //
2000#ifdef DEBUG
2001    switch (submsg)
2002    {
2003    case kTXT_ISMARKED:
2004        cout << "Textview-IsMarked #" << mp1 << " " << (mp2?"yes":"no") << endl;
2005        return kTRUE;
2006
2007    case kTXT_DATACHANGE:
2008        cout << "Textview-DataChange #" << mp1 << endl;
2009        return kTRUE;
2010
2011    case kTXT_CLICK2:
2012        cout << "Textview-Click2 #" << mp1 << " x=" << (mp2&0xffff) << " y= " << (mp2>>16) << endl;
2013        return kTRUE;
2014
2015    case kTXT_CLICK3:
2016        cout << "Textview-Click3 #" << mp1 << " x=" << (mp2&0xffff) << " y= " << (mp2>>16) << endl;
2017        return kTRUE;
2018
2019    case kTXT_F3:
2020        cout << "Textview-F3 #" << mp1 << endl;
2021        return kTRUE;
2022
2023    case kTXT_OPEN:
2024        cout << "Textview-Open #" << mp1 << endl;
2025        return kTRUE;
2026
2027    case kTXT_CLOSE:
2028        cout << "Textview-Close #" << mp1 << endl;
2029        return kTRUE;
2030
2031    case kTXT_SAVE:
2032        cout << "Textview-Save #" << mp1 << endl;
2033        return kTRUE;
2034
2035    default:
2036        cout << "Textview: " << "Submsg:" << submsg << " Mp1=" << mp1 << " Mp2=" << mp2 << endl;
2037    }
2038#endif
2039    return kTRUE;
2040}
2041
2042// --------------------------------------------------------------------------
2043//
2044// Process the kC_USER messages
2045//
2046Bool_t MStatusDisplay::ProcessMessageUser(Long_t submsg, Long_t mp1, Long_t mp2)
2047{
2048    // kS_START, case sensitive | backward<<1, char *txt
2049    switch (submsg)
2050    {
2051    case kS_START:
2052        fLogBox->Search((char*)mp2, !(mp1&2>>1), mp1&1);
2053        return kTRUE;
2054#ifdef DEBUG
2055    default:
2056        cout << "User: " << "Submsg:" << submsg << " Mp1=" << mp1 << " Mp2=" << mp2 << endl;
2057#endif
2058    }
2059    return kTRUE;
2060}
2061
2062// --------------------------------------------------------------------------
2063//
2064// Process the messages from the GUI
2065//
2066Bool_t MStatusDisplay::ProcessMessage(Long_t msg, Long_t mp1, Long_t mp2)
2067{
2068    // Can be found in WidgetMessageTypes.h
2069#ifdef DEBUG
2070    cout << "Msg: " << GET_MSG(msg) << " Submsg:" << GET_SUBMSG(msg);
2071    cout << " Mp1=" << mp1 << " Mp2=" << mp2 << endl;
2072#endif
2073    switch (GET_MSG(msg))
2074    {
2075    case kC_COMMAND:  // 1
2076        return ProcessMessageCommand(GET_SUBMSG(msg), mp1, mp2);
2077
2078    case kC_TEXTVIEW: // 9
2079        return ProcessMessageTextview(GET_SUBMSG(msg), mp1, mp2);
2080
2081    case kC_USER:     // 1001
2082        return ProcessMessageUser(GET_SUBMSG(msg), mp1, mp2);
2083    }
2084#ifdef DEBUG
2085    cout << "Msg: " << GET_MSG(msg) << " Submsg:" << GET_SUBMSG(msg);
2086    cout << " Mp1=" << mp1 << " Mp2=" << mp2 << endl;
2087#endif
2088    return kTRUE;
2089}
2090
2091Bool_t MStatusDisplay::Close()
2092{
2093    // Got close message for this MainFrame. Calls parent CloseWindow()
2094    // (which destroys the window) and terminate the application.
2095    // The close message is generated by the window manager when its close
2096    // window menu item is selected.
2097
2098    // CloseWindow must be overwritten because otherwise CloseWindow
2099    // and the destructor are calling DestroyWindow which seems to be
2100    // in conflict with the TRootEmbeddedCanvas.
2101
2102    // FIXME: Make sure that the Status Display is deleted from every
2103    //        where (eg Eventloop) first!
2104
2105    //gLog << dbg << fName << " is on heap: " << (int)IsOnHeap() << endl;
2106
2107    if (TestBit(kExitLoopOnExit) || TestBit(kExitLoopOnClose))
2108    {
2109        //gLog << dbg << "CloseWindow() calling ExitLoop." << endl;
2110        gSystem->ExitLoop();
2111    }
2112
2113    if (fIsLocked<=0 && IsOnHeap())
2114        return kTRUE;
2115
2116    fStatus = kFileExit;
2117    return kFALSE;
2118}
2119
2120void MStatusDisplay::CloseWindow()
2121{
2122    if (Close())
2123        delete this;
2124}
2125
2126// --------------------------------------------------------------------------
2127//
2128// Calls SetBit(kNoContextMenu) for all TCanvas objects found in the
2129// Tabs.
2130//
2131void MStatusDisplay::SetNoContextMenu(Bool_t flag)
2132{
2133    if (fIsLocked>1 || gROOT->IsBatch())
2134        return;
2135
2136    flag ? SetBit(kNoContextMenu) : ResetBit(kNoContextMenu);
2137
2138    for (int i=1; i<fTab->GetNumberOfTabs(); i++)
2139    {
2140        TCanvas *c = GetCanvas(i);
2141        if (c)
2142            flag ? c->SetBit(kNoContextMenu) : c->ResetBit(kNoContextMenu);
2143    }
2144}
2145
2146// --------------------------------------------------------------------------
2147//
2148// Update the memory display in the status bar
2149//
2150void MStatusDisplay::UpdateMemory() const
2151{
2152    const TString path = MString::Format("/proc/%d/status", gSystem->GetPid());
2153    if (gSystem->AccessPathName(path, kFileExists))
2154        return;
2155
2156    TEnv env(path);
2157    const UInt_t kb = env.GetValue("VmSize", 0);
2158    if (kb==0)
2159        return;
2160
2161    char type = 'k';
2162    Float_t val = kb;
2163
2164    if (val>999)
2165    {
2166        type = 'M';
2167        val /= 1000;
2168    }
2169    if (val>999)
2170    {
2171        type = 'G';
2172        val /= 1000;
2173    }
2174    const TString txt = MString::Format("%.1f%c", val, type);
2175    fStatusBar->SetText(txt, 2);
2176}
2177
2178// --------------------------------------------------------------------------
2179//
2180// Updates the canvas (if existing) in the currenly displayed Tab.
2181// The update intervall is controlled by StartUpdate and StopUpdate
2182//
2183Bool_t MStatusDisplay::HandleTimer(TTimer *timer)
2184{
2185    if (gROOT->IsBatch())
2186        return kTRUE;
2187
2188    UpdateMemory();
2189
2190    const Int_t c = fTab->GetCurrent();
2191
2192    // Skip Legend Tab
2193    if (c==0)
2194        return kTRUE;
2195
2196    // Update a canvas tab (if visible)
2197    if (timer==&fTimer && c!=fLogIdx)
2198    {
2199        UpdateTab(fTab->GetCurrentContainer());
2200        return kTRUE;
2201    }
2202
2203    // update the logbook tab (if visible)
2204    if (timer==&fLogTimer && c==fLogIdx)
2205    {
2206        fLog->UpdateGui();
2207
2208        /*
2209        if (!fLogBox->TestBit(kHasChanged))
2210            return kTRUE;
2211
2212            fLogBox->ResetBit(kHasChanged);
2213            */
2214        return kTRUE;
2215    }
2216
2217    return kTRUE;
2218}
2219
2220// --------------------------------------------------------------------------
2221//
2222// Find an object in a canvas (uses MStatusArray as helper)
2223//
2224void MStatusDisplay::PrintContent(Option_t *o) const
2225{
2226    MStatusArray(*this).Print(o);
2227}
2228
2229// --------------------------------------------------------------------------
2230//
2231// Find an object in a canvas (uses MStatusArray as helper)
2232//
2233TObject *MStatusDisplay::FindObjectInCanvas(const char *obj, const char *base, const char *canv) const
2234{
2235    return MStatusArray(*this).FindObjectInCanvas(obj, base, canv);
2236}
2237
2238// --------------------------------------------------------------------------
2239//
2240// Draws a clone of a canvas into a new canvas. Taken from TCanvas.
2241//
2242void MStatusDisplay::DrawClonePad(TCanvas &newc, TCanvas &oldc) const
2243{
2244    //copy pad attributes
2245    newc.Range(oldc.GetX1(),oldc.GetY1(),oldc.GetX2(),oldc.GetY2());
2246    newc.SetTickx(oldc.GetTickx());
2247    newc.SetTicky(oldc.GetTicky());
2248    newc.SetGridx(oldc.GetGridx());
2249    newc.SetGridy(oldc.GetGridy());
2250    newc.SetLogx(oldc.GetLogx());
2251    newc.SetLogy(oldc.GetLogy());
2252    newc.SetLogz(oldc.GetLogz());
2253    newc.SetBorderSize(oldc.GetBorderSize());
2254    newc.SetBorderMode(oldc.GetBorderMode());
2255    ((TAttLine&)oldc).Copy((TAttLine&)newc);
2256    ((TAttFill&)oldc).Copy((TAttFill&)newc);
2257    ((TAttPad&)oldc).Copy((TAttPad&)newc);
2258
2259    // This must be there: Otherwise GetDrawOption() won't work
2260    TVirtualPad *padsav = gPad;
2261    oldc.cd();
2262
2263    const Bool_t store = TH1::AddDirectoryStatus();
2264    TH1::AddDirectory(kFALSE);
2265
2266    //copy primitives
2267    TObject *obj;
2268    TIter next(oldc.GetListOfPrimitives());
2269    while ((obj=next()))
2270    {
2271        // Old line - I think it is not necessary anymore because of the cd()
2272        //gROOT->SetSelectedPad(&newc);
2273
2274        // Now make a clone of the object
2275        TObject *clone = obj->Clone();
2276
2277        // Clone also important bits (FIXME: Is this correct)
2278        clone->SetBit(obj->TestBits(kCannotPick|kNoContextMenu));
2279
2280        // Now make sure that the clones are deleted at a later time
2281        clone->SetBit(kCanDelete|kMustCleanup);
2282
2283        // FIXME: This is a workaround for the problem with the MAstroCatalog in
2284        // MHFalseSource. It doesn't harm. We'll still try to find the reason
2285        //if (clone->IsA()==TPad::Class())
2286        //    gROOT->GetListOfCleanups()->Add(clone);
2287
2288        // Add the clone and its draw-option to the current pad
2289        TVirtualPad *save2 = gPad;
2290        gPad = &oldc; // Don't do this before Clone()!
2291        newc.GetListOfPrimitives()->Add(clone, obj->GetDrawOption());
2292        gPad = save2;
2293    }
2294    newc.Modified();
2295    newc.Update();
2296
2297    TH1::AddDirectory(store);
2298
2299    padsav->cd();
2300}
2301
2302// --------------------------------------------------------------------------
2303//
2304// Display the contexts of a TObjArray in the display (all canvases)
2305//
2306Bool_t MStatusDisplay::Display(const TObjArray &list, const char *tab)
2307{
2308    TIter Next(&list);
2309
2310    TObject *o=Next();
2311    if (!o)
2312    {
2313        *fLog << err << "MStatusDisplay::Display: No entry in TObjArray." << endl;
2314        return kFALSE;
2315    }
2316
2317    // The first entry in the list is a TNamed which is expected to
2318    // contain the status display title.
2319    fTitle = o->GetTitle();
2320
2321    TCanvas *c;
2322    while ((c=(TCanvas*)Next()))
2323        //if (!GetCanvas(c->GetName()))
2324        if (c->InheritsFrom(TCanvas::Class()))
2325            if (!tab || c->GetName()==(TString)tab)
2326                DrawClonePad(AddTab(c->GetName(), c->GetTitle()), *c);
2327
2328    return kTRUE;
2329}
2330
2331// --------------------------------------------------------------------------
2332//
2333// Reads the contents of a saved MStatusDisplay from a file.
2334//
2335Int_t MStatusDisplay::Read(const char *name, const char *tab)
2336{
2337    if (!gFile)
2338    {
2339        *fLog << warn << "MStatusDisplay::Read: No file found. Please create a TFile first." << endl;
2340        return 0;
2341    }
2342
2343    if (!gFile->IsOpen())
2344    {
2345        *fLog << warn << "MStatusDisplay::Read: File not open. Please open the TFile first." << endl;
2346        return 0;
2347    }
2348
2349    MStatusArray list;
2350
2351    Int_t n = list.Read(name);
2352
2353    //
2354    // If no status display was found with this name try to find canvases
2355    // in the file and display them instead.
2356    //
2357    if (n==0)
2358    {
2359        // Either read the title from the file or create our own
2360        TNamed *title=0;
2361        gFile->GetObject("Title", title); // FIXME: Is the list allowed to take ownership?
2362        list.Add(title ? title : new TNamed(GetName(), GetTitle()));
2363
2364        const Bool_t store = TH1::AddDirectoryStatus();
2365        TH1::AddDirectory(kFALSE);
2366
2367        TIter Next(gFile->GetListOfKeys());
2368        TObject *key = 0;
2369        while ((key=Next()))
2370        {
2371            MStatusArray *arr=0;
2372            gFile->GetObject(key->GetName(), arr);
2373            if (arr)
2374            {
2375                Display(*arr, tab);
2376                n++;
2377                continue;
2378            }
2379
2380            if (tab && key->GetName()!=(TString)tab)
2381                continue;
2382
2383            TCanvas *c=0;
2384            gFile->GetObject(key->GetName(), c);
2385
2386            // If key doesn't correspond to a TCanvas create a new canvas
2387            // and draw the object itself instead.
2388            if (!c)
2389            {
2390                TObject *obj = gFile->Get(key->GetName());
2391
2392                if (MParContainer::Overwrites(TObject::Class(), *obj, "Draw") ||
2393                    MParContainer::Overwrites(TObject::Class(), *obj, "Paint"))
2394                {
2395                    AddTab(key->GetName(), key->GetTitle());
2396                    obj->SetBit(kCanDelete);
2397                    obj->Draw();
2398                }
2399                // FIXME: Is the object deleted?
2400                continue;
2401            }
2402
2403            // Add the canvas to the list
2404            const TString _name  = c->GetName();
2405            const TString _title = c->GetTitle();
2406            if (_title.IsNull() || _name==_title)
2407                c->SetTitle(gFile->GetName());
2408            list.Add(c);
2409        }
2410
2411        TH1::AddDirectory(store);
2412
2413        if (list.GetEntries()<=1)
2414        {
2415            *fLog << warn << "MStatusDisplay::Read: No drawable objects read from " << gFile->GetName() << endl;
2416            return 0;
2417        }
2418
2419        *fLog << inf << "MStatusDisplay: " << list.GetEntries()-1 << " canvases directly read from file." << endl;
2420    }
2421
2422
2423    if (!Display(list, tab))
2424    {
2425        *fLog << err << "MStatusDisplay::Display: No entries found." << endl;
2426        return 0;
2427    }
2428
2429    if (n==0)
2430        return list.GetEntries();
2431
2432    *fLog << inf << "MStatusDisplay: Key " << name << " with " << n << " keys read from file." << endl;
2433    return n;
2434}
2435
2436// --------------------------------------------------------------------------
2437//
2438// Add all canvases to the MStatusArray
2439//
2440void MStatusDisplay::FillArray(MStatusArray &list, Int_t num) const
2441{
2442    Int_t from, to;
2443    GetCanvasRange(from, to, num);
2444
2445    TCanvas *c;
2446    for (int i=from; i<to; i++)
2447        if ((c = GetCanvas(i)))
2448            list.Add(c);
2449}
2450
2451// --------------------------------------------------------------------------
2452//
2453// Writes the contents of a MStatusDisplay to a file.
2454//
2455Int_t MStatusDisplay::Write(Int_t num, const char *name, Option_t *opt) const
2456{
2457    if (!gFile)
2458    {
2459        *fLog << warn << "MStatusDisplay::Write: No file found. Please create a TFile first." << endl;
2460        return 0;
2461    }
2462
2463    if (!gFile->IsOpen())
2464    {
2465        *fLog << warn << "MStatusDisplay::Write: File not open. Please open the TFile first." << endl;
2466        return 0;
2467    }
2468
2469    if (!gFile->IsWritable())
2470    {
2471        *fLog << warn << "MStatusDisplay::Write: File not writable." << endl;
2472        return 0;
2473    }
2474
2475    if (num==0)
2476    {
2477        *fLog << warn << "MStatusDisplay::Write: Tab doesn't contain an embedded Canvas... skipped." << endl;
2478        return 0;
2479    }
2480
2481    if (!gROOT->IsBatch() && num>=fTab->GetNumberOfTabs())
2482    {
2483        *fLog << warn << "MStatusDisplay::Write: Tab doesn't exist... skipped." << endl;
2484        return 0;
2485    }
2486    if (gROOT->IsBatch() && num>fBatch->GetSize())
2487    {
2488        *fLog << warn << "MStatusDisplay::Write: Tab doesn't exist... skipped." << endl;
2489        return 0;
2490    }
2491
2492    Int_t n = 0; // Number of keys written
2493
2494    TNamed named("Title", fTitle.Data());
2495
2496    if (TString(opt)=="plain")
2497    {
2498        // Get canvas range to store
2499        Int_t from, to;
2500        GetCanvasRange(from, to, num);
2501
2502        n += named.Write();
2503
2504        TCanvas *c;
2505        for (int i=from; i<to; i++)
2506            if ((c = GetCanvas(i)))
2507                n += c->Write();
2508    }
2509    else
2510    {
2511        // Be careful: So far Display() assumes that the TNamed
2512        // is the first entry in the list
2513        MStatusArray list;
2514        list.Add(&named);
2515
2516        FillArray(list, num);
2517
2518        n += list.Write(name, kSingleKey);
2519    }
2520
2521    *fLog << inf << "MStatusDisplay: " << n << " keys written to file as key " << name << "." << endl;
2522
2523    return n;
2524}
2525
2526// --------------------------------------------------------------------------
2527//
2528// Use this to start the synchronous (GUI eventloop driven) tab update.
2529// Can also be used to change the update intervall. If millisec<0
2530// the intervall given in SetUpdateTime is used. If the intervall in
2531// SetUpdateTime is <0 nothing is done. (Call SetUpdateTime(-1) to
2532// disable the automatic update in a MEventloop.
2533//
2534void MStatusDisplay::StartUpdate(Int_t millisec)
2535{
2536    if (fIsLocked>1)
2537        return;
2538
2539    if (fTimer.GetTime()<TTime(0))
2540        return;
2541    fTimer.Start(millisec);
2542}
2543
2544// --------------------------------------------------------------------------
2545//
2546// Stops the automatic GUI update
2547//
2548void MStatusDisplay::StopUpdate()
2549{
2550    if (fIsLocked>1)
2551        return;
2552
2553    fTimer.Stop();
2554}
2555
2556// --------------------------------------------------------------------------
2557//
2558// Set the update interval for the GUI update, see StartUpdate.
2559//
2560void MStatusDisplay::SetUpdateTime(Long_t t)
2561{
2562    fTimer.SetTime(t);
2563}
2564
2565// --------------------------------------------------------------------------
2566//
2567// If the filename name doesn't end with ext, ext is added to the end.
2568// If name.IsNull() "status" is assumed and the a number (>0) is added
2569// as "status-6".
2570// The extension is returned.
2571//
2572const TString &MStatusDisplay::AddExtension(TString &name, const TString &ext, Int_t num) const
2573{
2574    if (name.IsNull())
2575    {
2576        name = gROOT->GetName();
2577        if (num>0)
2578        {
2579            name += "-";
2580            name += num;
2581        }
2582    }
2583
2584    if (name.EndsWith("."+ext))
2585        return ext;
2586
2587    name += ".";
2588    name += ext;
2589
2590    return ext;
2591}
2592
2593Bool_t MStatusDisplay::CheckTabForCanvas(int num) const
2594{
2595    if (gROOT->IsBatch())
2596        return (num>0 && num<=fBatch->GetSize()) || num<0;
2597
2598    if (num>=fTab->GetNumberOfTabs())
2599    {
2600        *fLog << warn << "Tab #" << num << " doesn't exist..." << endl;
2601        return kFALSE;
2602    }
2603    if (num==0)
2604    {
2605        *fLog << warn << "Tab #" << num << " doesn't contain an embedded canvas..." << endl;
2606        return kFALSE;
2607    }
2608    if (fTab->GetNumberOfTabs()<2 || !gPad)
2609    {
2610        *fLog << warn << "Sorry, you must have at least one existing canvas (gPad!=NULL)" << endl;
2611        return kFALSE;
2612    }
2613    return kTRUE;
2614}
2615
2616// --------------------------------------------------------------------------
2617//
2618// Insert the following two lines into the postscript header:
2619//
2620//   %%DocumentPaperSizes: a4
2621//   %%Orientation: Landscape
2622//
2623void MStatusDisplay::UpdatePSHeader(const TString &name) const
2624{
2625    const TString newstr("%%DocumentPaperSizes: a4\n%%Orientation: Landscape\n");
2626
2627    TString tmp(name+"XXXXXX");
2628
2629    // FIXME: Use mkstemp instead
2630    if (!mkstemp(const_cast<char*>(tmp.Data())))
2631    {
2632        *fLog << err << "ERROR - MStatusDisplay::UpdatePSHeader: mktemp failed." << endl;
2633        return;
2634    }
2635
2636    ifstream fin(name);
2637    ofstream fout(tmp);
2638    if (!fout)
2639    {
2640        *fLog << err << "Cannot open file " << name << ": " << strerror(errno) << endl;
2641        return;
2642    }
2643
2644    char c;
2645
2646    TString str;
2647    fin >> str >> c;                // Read "%!PS-Adobe-2.0\n"
2648    fout << str << endl << newstr;
2649
2650    // Doing it in blocks seems not to gain much for small (MB) files
2651    while (fin)
2652    {
2653        fin.read(&c, 1);
2654        fout.write(&c, 1);
2655    }
2656
2657    gSystem->Unlink(name);
2658    gSystem->Rename(tmp, name);
2659}
2660
2661// --------------------------------------------------------------------------
2662//
2663void MStatusDisplay::PSToolsRange(TVirtualPS &vps, Float_t psw, Float_t psh) const
2664{
2665    if (vps.InheritsFrom(TPostScript::Class()))
2666        static_cast<TPostScript&>(vps).Range(psw, psh);
2667    // if (vps.InheritsFrom(TPDF::Class()))
2668    //     static_cast<TPDF&>(vps).Range(psw*0.69, psh*0.69);
2669    // if (vps.InheritsFrom(TSVG::Class()))
2670    //     static_cast<TSVG&>(vps).Range(psw, psh);
2671}
2672
2673// --------------------------------------------------------------------------
2674//
2675void MStatusDisplay::PSToolsTextNDC(TVirtualPS &vps, Double_t u, Double_t v, const char *string) const
2676{
2677    if (vps.InheritsFrom(TPostScript::Class()))
2678        static_cast<TPostScript&>(vps).TextNDC(u, v, string);
2679    if (vps.InheritsFrom(TPDF::Class()))
2680        static_cast<TPDF&>(vps).TextNDC(u, v, string);
2681    // if (vps.InheritsFrom(TSVG::Class()))
2682    //    static_cast<TSVG&>(vps).TextNDC(u, v, string);
2683}
2684
2685// --------------------------------------------------------------------------
2686//
2687Int_t MStatusDisplay::InitWriteDisplay(Int_t num, TString &name, const TString &ext)
2688{
2689    SetStatusLine1(MString::Format("Writing %s file...",ext.Data()));
2690    SetStatusLine2("Please be patient!");
2691
2692    if (!CheckTabForCanvas(num))
2693    {
2694        SetStatusLine2("Failed!");
2695        return 0;
2696    }
2697
2698    AddExtension(name, ext, num);
2699
2700    if (num<0)
2701        *fLog << inf << "Open " << ext << "-File: " << name << endl;
2702
2703    return num;
2704}
2705
2706// --------------------------------------------------------------------------
2707//
2708TCanvas *MStatusDisplay::InitWriteTab(Int_t num, TString &name)
2709{
2710    const Int_t i = TMath::Abs(num);
2711
2712    TCanvas *c = GetCanvas(i);
2713    if (!c)
2714    {
2715        if (num<0)
2716            *fLog << inf << " - ";
2717        *fLog << "Tab #" << i << " doesn't contain an embedded Canvas... skipped." << endl;
2718        return 0;
2719    }
2720
2721    SetStatusLine2(MString::Format("Tab #%d", i));
2722
2723    //
2724    // Paint canvas into root file
2725    //
2726    if (num<0 && !name.IsNull())
2727    {
2728        Bool_t found = kFALSE;
2729        if (name.Index("%%%%name%%%%")>=0)
2730        {
2731            name.ReplaceAll("%%name%%", c->GetName());
2732            found = kTRUE;
2733        }
2734
2735        if (name.Index("%%%%title%%%%")>=0)
2736        {
2737            name.ReplaceAll("%%title%%", c->GetTitle());
2738            found = kTRUE;
2739        }
2740
2741        if (name.Index("%%%%tab%%%%")>=0)
2742        {
2743            name.ReplaceAll("%%tab%%", MString::Format("%d", i));
2744            found = kTRUE;
2745        }
2746
2747        if (!found)
2748            name.Insert(name.Last('.'), MString::Format("-%d", i));
2749    }
2750
2751    if (num<0)
2752        *fLog << inf << " - ";
2753    *fLog << inf << "Writing Tab #" << i;
2754
2755    if (!name.IsNull())
2756        *fLog << " to " << name;
2757
2758    *fLog << ": " << c->GetName() << "... " << flush;
2759
2760    return c;
2761}
2762
2763// This is a stupid workaround to get rid of the damned clipping
2764// of the text. Who the hell needs clipping?
2765class MyCanvas : public TCanvas
2766{
2767public:
2768    void Scale(Double_t val)
2769    {
2770        fAbsXlowNDC = -val;
2771        fAbsYlowNDC = -val;
2772        fAbsWNDC    = 1+2*val;
2773        fAbsHNDC    = 1+2*val;
2774    }
2775};
2776
2777// --------------------------------------------------------------------------
2778//
2779// Write some VGF (vector graphics format). Currently PS, PDF and SVG
2780// is available. Specified by ext.
2781//
2782// In case of num<0 all tabs are written into the VGF file. If num>0
2783// the canvas in the corresponding tab is written to the file.
2784// Name is the name of the file (with or without extension).
2785//
2786// Returns the number of pages written.
2787//
2788// To write all tabs you can also use SaveAsVGF(name, ext)
2789//
2790// If the third argument is given a bottom line is drawn with the text
2791// under it. If no argument is given a bottom line is drawn if
2792// fTitle (SetTitle) is not empty.
2793//
2794Int_t MStatusDisplay::SaveAsVGF(Int_t num, TString name, const TString addon, const TString ext)
2795{
2796    num = InitWriteDisplay(num, name, ext);
2797    if (num==0)
2798        return 0;
2799
2800    TPad       *padsav = (TPad*)gPad;
2801    TVirtualPS *psave  = gVirtualPS;
2802
2803    TDatime d;
2804
2805    Int_t type = -1;
2806
2807    TVirtualPS *ps =0;
2808    if (!ext.CompareTo("ps", TString::kIgnoreCase))
2809    {
2810        gStyle->SetColorModelPS(1);
2811        ps = new TPostScript(name, 112);
2812        type = 1;
2813    }
2814    if (!ext.CompareTo("pdf", TString::kIgnoreCase))
2815    {
2816        ps = new TPDF(name, 112);
2817        type = 2;
2818    }
2819    if (!ext.CompareTo("svg", TString::kIgnoreCase))
2820    {
2821        ps = new TSVG(name, 112);
2822        type = 3;
2823    }
2824
2825    if (!ps)
2826    {
2827        *fLog << err << "Extension " << ext << " unknown..." << endl;
2828        SetStatusLine2("Failed!");
2829        return 0;
2830    }
2831
2832    ps->SetBit(TPad::kPrintingPS);
2833    if (type==1)
2834        ps->PrintFast(13, "/nan {1} def ");
2835
2836    gVirtualPS = ps;
2837
2838    //
2839    // Create some GUI elements for a page legend
2840    //
2841    TLine line;
2842
2843    int page = 1;
2844
2845    //
2846    // Maintain tab numbers
2847    //
2848    Int_t from, to;
2849    GetCanvasRange(from, to, num);
2850
2851    for (int i=from; i<to; i++)
2852    {
2853        TCanvas *c = InitWriteTab(num<0?-i:i);
2854        if (c==0)
2855            continue;
2856
2857        //
2858        // Init page and page size, make sure, that the canvas in the file
2859        // has the same Aspect Ratio than on the screen.
2860        //
2861        if (type==1 || i>from)
2862            ps->NewPage();
2863
2864        //
2865        // 28 is used here to scale the canvas into a height of 28,
2866        // such that the page title can be set above the canvas...
2867        //
2868        Float_t psw = 28.0; // A4 - width  (29.7)
2869        Float_t psh = 21.0; // A4 - height (21.0)
2870
2871        const Float_t cw = c->GetWw();
2872        const Float_t ch = c->GetWh();
2873
2874        if (psw/psh>cw/ch)
2875            psw = cw/ch*psh;
2876        else
2877            psh = ch/cw*psw;
2878
2879        PSToolsRange(*ps, psw, psh);
2880
2881        //
2882        // Clone canvas and change background color and schedule for
2883        // deletion
2884        //
2885
2886        //const Bool_t store = c->IsBatch();
2887        //c->SetBatch(kTRUE);
2888        c->Paint();
2889        //c->SetBatch(store);
2890
2891        //
2892        // Change/fix the canvas coordinate system for the overlaying text.
2893        // This is necessary because root clip everything away which is
2894        // outside a predefined area, which is (0,0)/(1,1)
2895        //
2896        const Double_t height = 0.015;  // Text height
2897        const Double_t off    = 0.005;  // Line offset from text
2898
2899        const Double_t bot = height+off;
2900        const Double_t top = 1-bot;
2901
2902        static_cast<MyCanvas*>(c)->Scale(bot);
2903
2904        // If gPad is not set to c all follwing commands will
2905        // get the wrong numbers for alignment
2906        gPad = c;
2907
2908        // Separator Lines
2909        line.PaintLineNDC(0.01, top, 0.99, top);
2910        line.PaintLineNDC(0.01, bot, 0.99, bot);
2911
2912        //
2913        // Print overlaying text (NDC = %)
2914        //
2915        // align phi col font size (11=left top)
2916        const TString txt(addon.IsNull() ? fTitle : addon);
2917
2918        // Text Attributes
2919        TAttText(11, 0, kBlack, 22, height).Copy(*ps);
2920
2921        // Text on top
2922        ps->SetTextAlign(11); // left bottom
2923        PSToolsTextNDC(*ps, 0.01, top+off, c->GetName());
2924
2925        ps->SetTextAlign(21); // cent bottom
2926        PSToolsTextNDC(*ps, 0.50, top+off, TString("MARS V" MARSVER " - Modular Analysis and Reconstruction Software - ")+d.AsString());
2927
2928        ps->SetTextAlign(31); // right bottom
2929        PSToolsTextNDC(*ps, 0.99, top+off, MString::Format("Page No.%i (%i)", page++, i));
2930
2931        // Text on bottom
2932        ps->SetTextAlign(13); // left top
2933        PSToolsTextNDC(*ps, 0.01, bot-off, c->GetTitle());
2934
2935        ps->SetTextAlign(23); // cent top
2936        PSToolsTextNDC(*ps, 0.50, bot-off, txt);
2937
2938        ps->SetTextAlign(33); // right top
2939        PSToolsTextNDC(*ps, 0.99, bot-off, MString::Format("(c) 2000-%d, Thomas Bretz", TDatime().GetYear()));
2940
2941        static_cast<MyCanvas*>(c)->Scale(0);
2942
2943        //
2944        // Finish drawing page
2945        //
2946        *fLog << "done." << endl;
2947    }
2948
2949    gPad = NULL; // Important!
2950
2951    ps->Close();
2952    delete ps;
2953
2954#if ROOT_VERSION_CODE < ROOT_VERSION(5,12,00)
2955    if (type==1)
2956    {
2957        SetStatusLine2("Updating header of PS file...");
2958
2959        if (num<0)
2960            *fLog << inf3 << " - Updating header of PS file... " << flush;
2961        UpdatePSHeader(name);
2962        if (num<0)
2963            *fLog << inf3 << "done." << endl;
2964    }
2965#endif
2966
2967    gVirtualPS = psave;
2968    if (padsav)
2969        padsav->cd();
2970
2971    if (num<0)
2972        *fLog << inf << "done." << endl;
2973
2974    SetStatusLine2(MString::Format("Done (%dpages)", page-1));
2975
2976    return page-1;
2977}
2978
2979// --------------------------------------------------------------------------
2980//
2981Bool_t MStatusDisplay::SaveAsImage(Int_t num, TString name, TImage::EImageFileTypes type)
2982{
2983#if ROOT_VERSION_CODE < ROOT_VERSION(5,12,00)
2984    if (gROOT->IsBatch())
2985    {
2986        *fLog << warn << "Sorry, writing image-files is not available in batch mode." << endl;
2987        return 0;
2988    }
2989#endif
2990
2991    TString ext;
2992    switch (type)
2993    {
2994    case TImage::kXpm:
2995    case TImage::kZCompressedXpm:  ext = "xpm";     break;
2996    case TImage::kPng:             ext = "png";     break;
2997    case TImage::kJpeg:            ext = "jpg";     break;
2998    case TImage::kGif:             ext = "gif";     break;
2999    case TImage::kTiff:            ext = "tiff";    break;
3000    case TImage::kBmp:             ext = "bmp";     break;
3001    case TImage::kXml:             ext = "xml";     break;
3002    //case TImage::kGZCompressedXpm: ext = "xpm.gz";  break;
3003    //case TImage::kPpm:             ext = "ppm";     break;
3004    //case TImage::kPnm:             ext = "pnm";     break;
3005    //case TImage::kIco:             ext = "ico";     break;
3006    //case TImage::kCur:             ext = "cur";     break;
3007    //case TImage::kXcf:             ext = "xcf";     break;
3008    //case TImage::kXbm:             ext = "xbm";     break;
3009    //case TImage::kFits:            ext = "fits";    break;
3010    //case TImage::kTga:             ext = "tga";     break;
3011    default:
3012        *fLog << warn << "Sorry, unknown or unsupported file type..." << endl;
3013        return 0;
3014    }
3015
3016    num = InitWriteDisplay(num, name, ext);
3017    if (num==0)
3018        return 0;
3019
3020    TPad *padsav = (TPad*)gPad;
3021
3022    Int_t counter = 0;
3023
3024    //
3025    // Maintain tab numbers
3026    //
3027    Int_t from, to;
3028    GetCanvasRange(from, to, num);
3029
3030    for (int i=from; i<to; i++)
3031    {
3032        TString writename(name);
3033
3034        TCanvas *c = InitWriteTab(num<0 ? -i : i, writename);
3035        if (!c)
3036            continue;
3037
3038        //
3039        // Paint canvas into root file
3040        //
3041
3042        // TImage *img = TImage::Create();
3043        // img->FromPad(c);
3044        // img->WriteImage(writename, type);
3045        // delete img;
3046
3047        // FIXME: Not all file types are supported by Print()
3048        c->Print(writename);
3049
3050        if (num<0)
3051            *fLog << "done." << endl;
3052
3053        counter++;
3054    }
3055
3056    if (padsav)
3057        padsav->cd();
3058
3059    *fLog << inf << "done." << endl;
3060
3061    SetStatusLine2("Done.");
3062
3063    return counter>0;
3064}
3065
3066// --------------------------------------------------------------------------
3067//
3068Bool_t MStatusDisplay::SaveAsC(Int_t num, TString name)
3069{
3070    num = InitWriteDisplay(num, name, "C");
3071    if (num==0)
3072        return kFALSE;
3073
3074    TPad *padsav = (TPad*)gPad;
3075
3076    Int_t counter = 0;
3077
3078    //
3079    // Maintain tab numbers
3080    //
3081    Int_t from, to;
3082    GetCanvasRange(from, to, num);
3083
3084    for (int i=from; i<to; i++)
3085    {
3086        TString writename(name);
3087
3088        TCanvas *c = InitWriteTab(num<0 ? -i : i, writename);
3089        if (!c)
3090            continue;
3091
3092        //
3093        // Clone canvas and change background color and schedule for
3094        // deletion
3095        //
3096        c->SaveSource(writename, "");
3097
3098        if (num<0)
3099            *fLog << "done." << endl;
3100
3101        counter++;
3102    }
3103
3104    if (padsav)
3105        padsav->cd();
3106
3107    *fLog << inf << "done." << endl;
3108
3109    SetStatusLine2("Done.");
3110
3111    return counter>0;
3112}
3113
3114// --------------------------------------------------------------------------
3115//
3116// In case of num<0 all tabs are written into a root file. As plain
3117// TCanvas objects if plain==kTRUE otherwise in a MStatusArray. If num>0
3118// the canvas in the corresponding tab is written to the file.
3119// Name is the name of the file (with or without extension).
3120//
3121// Returns the number of keys written.
3122//
3123// To write all tabs you can also use SaveAsRoot(name)
3124//
3125Int_t MStatusDisplay::SaveAsRoot(Int_t num, TString name, Bool_t plain)
3126{
3127    num = InitWriteDisplay(num, name, "root");
3128    if (num==0)
3129        return -1;
3130
3131    TFile *fsave = gFile;
3132    TFile file(name, "RECREATE", GetTitle(), 9);
3133    const Int_t keys = Write(num, "MStatusDisplay", plain ? "plain" : "");
3134    gFile = fsave;
3135
3136    SetStatusLine2("Done.");
3137
3138    return keys;
3139}
3140
3141// --------------------------------------------------------------------------
3142//
3143Bool_t MStatusDisplay::SaveAsCSV(Int_t num, TString name, Char_t delim)
3144{
3145    num = InitWriteDisplay(num, name, "csv");
3146    if (num==0)
3147        return kFALSE;
3148
3149    gSystem->ExpandPathName(name);
3150
3151    ofstream fout(name);
3152    if (!fout)
3153    {
3154        *fLog << err << "Cannot open file " << name << ": " << strerror(errno) << endl;
3155        return kFALSE;
3156    }
3157
3158    fout << 0 << delim << GetName() << delim << GetTitle() << endl;
3159
3160    Int_t from, to;
3161    GetCanvasRange(from, to, num);
3162
3163    for (int i=from; i<to; i++)
3164    {
3165        TCanvas *c;
3166        if (!(c = GetCanvas(i)))
3167        {
3168            if (num<0)
3169                *fLog << inf << " - ";
3170            *fLog << "Tab #" << i << " doesn't contain an embedded Canvas... skipped." << endl;
3171            continue;
3172        }
3173
3174        fout << i << delim << c->GetName() << delim << c->GetTitle() << endl;
3175    }
3176
3177    SetStatusLine2("Done.");
3178
3179    return kTRUE;
3180}
3181
3182/*
3183Bool_t MStatusDisplay::SaveAsCSV(Int_t num, TString name)
3184{
3185    num = InitWriteDisplay(num, name, "csv");
3186    if (num==0)
3187        return kFALSE;
3188
3189    gSystem->ExpandPathName(name);
3190
3191    ofstream fout(name);
3192    if (!fout)
3193    {
3194        *fLog << err << "Cannot open file " << name << ": " << strerror(errno) << endl;
3195        return kFALSE;
3196    }
3197
3198    fout << "<?xml version=\"1.0\"?>" << endl;
3199    fout << "<display name='" << GetName() << "'>" << endl;
3200    fout << "   <file>" << name << "</file>" << endl;
3201    fout << "   <status>" << endl;
3202    fout << "      <name>" << GetName() << "</name>" << endl;
3203    fout << "      <title>" << GetTitle() << "</title>" << endl;
3204    fout << "   </status>" << endl;
3205    fout << "   <tabs>" << endl;
3206
3207    fout << 0 << delim << GetName() << delim << GetTitle() << endl;
3208
3209    Int_t from, to;
3210    GetCanvasRange(from, to, num);
3211
3212    for (int i=from; i<to; i++)
3213    {
3214        TCanvas *c;
3215        if (!(c = GetCanvas(i)))
3216        {
3217            if (num<0)
3218                *fLog << inf << " - ";
3219            *fLog << "Tab #" << i << " doesn't contain an embedded Canvas... skipped." << endl;
3220            continue;
3221        }
3222
3223        fout << "      <tab index='" << i << "'>" << endl;
3224        fout << "         <index>" << i << "</index>" << endl;
3225        fout << "         <name>" << c->GetName()  << "</name>" << endl;
3226        fout << "         <title>" << c->GetName()  << "</title>" << endl;
3227        fout << "      </tab>" << endl;
3228    }
3229
3230    fout << "  </tabs>" << endl;
3231    fout << "</display>" << endl;
3232
3233    SetStatusLine2("Done.");
3234
3235    return kTRUE;
3236}
3237*/
3238
3239// --------------------------------------------------------------------------
3240//
3241void MStatusDisplay::SaveAs(const char *c, const Option_t *o) const
3242{
3243#if ROOT_VERSION_CODE >= ROOT_VERSION(5,18,00)
3244    TGObject::SaveAs(c, o);
3245#endif
3246}
3247
3248// --------------------------------------------------------------------------
3249//
3250// Determin File type to save file as by extension. Allowed extensions are:
3251//   root, ps, pdf, svg, gif, png, jpg, xpm, C
3252//
3253// returns -1 if file type is unknown. Otherwise return value of SaveAs*
3254//
3255Int_t MStatusDisplay::SaveAs(Int_t num, TString name)
3256{
3257    if (name.EndsWith(".root")) return SaveAsRoot(num, name); // kFileSaveAsRoot
3258    if (name.EndsWith(".ps"))   return SaveAsPS(num, name);   // kFileSaveAsPS
3259    if (name.EndsWith(".pdf"))  return SaveAsPDF(num, name);  // kFileSaveAsPDF
3260    if (name.EndsWith(".svg"))  return SaveAsSVG(num, name);  // kFileSaveAsSVG
3261    if (name.EndsWith(".gif"))  return SaveAsGIF(num, name);  // kFileSaveAsGIF
3262    if (name.EndsWith(".png"))  return SaveAsPNG(num, name);  // kFileSaveAsPNG
3263    if (name.EndsWith(".bmp"))  return SaveAsBMP(num, name);  // kFileSaveAsBMP
3264    if (name.EndsWith(".xml"))  return SaveAsXML(num, name);  // kFileSaveAsXML
3265    if (name.EndsWith(".jpg"))  return SaveAsJPG(num, name);  // kFileSaveAsJPG
3266    if (name.EndsWith(".xpm"))  return SaveAsXPM(num, name);  // kFileSaveAsXPM
3267    if (name.EndsWith(".csv"))  return SaveAsCSV(num, name);  // kFileSaveAsCSV
3268    if (name.EndsWith(".tiff")) return SaveAsTIFF(num, name); // kFileSaveAsTIFF
3269    if (name.EndsWith(".C"))    return SaveAsC(num, name);    // kFileSaveAsC
3270    return -1;
3271}
3272
3273// --------------------------------------------------------------------------
3274//
3275//  Opens a save as dialog
3276//
3277Int_t MStatusDisplay::SaveAs(Int_t num)
3278{
3279    static const char *gSaveAsTypes[] =
3280    {
3281        "PostScript",   "*.ps",
3282        "Acrobat pdf",  "*.pdf",
3283        "SVG vector",   "*.svg",
3284        "Gif files",    "*.gif",
3285        "Png files",    "*.png",
3286        "Gif files",    "*.gif",
3287        "Jpeg files",   "*.jpeg",
3288        "Xpm files",    "*.xpm",
3289        "Bmp files",    "*.bmp",
3290        "Xml files",    "*.xml",
3291        "Tiff files",   "*.tiff",
3292        "Csv files",    "*.csv",
3293        "Macro files",  "*.C",
3294        "ROOT files",   "*.root",
3295        "All files",    "*",
3296        NULL,           NULL
3297    };
3298
3299    static TString dir(".");
3300
3301    TGFileInfo fi; // fFileName and fIniDir deleted in ~TGFileInfo
3302
3303    fi.fFileTypes = (const char**)gSaveAsTypes;
3304    fi.fIniDir    = StrDup(dir);
3305
3306    new TGFileDialog(fClient->GetRoot(), this, kFDSave, &fi);
3307
3308    if (!fi.fFilename)
3309        return 0;
3310
3311    dir = fi.fIniDir;
3312
3313    const Int_t rc = SaveAs(num, fi.fFilename);
3314    if (rc>=0)
3315        return rc;
3316
3317    Warning("MStatusDisplay::SaveAs", "Unknown Extension: %s", fi.fFilename);
3318    return 0;
3319}
3320
3321// --------------------------------------------------------------------------
3322//
3323//  Open contents of a MStatusDisplay with key name from file fname.
3324//
3325Int_t MStatusDisplay::Open(TString fname, const char *name)
3326{
3327    TFile file(fname, "READ");
3328    if (file.IsZombie())
3329    {
3330        gLog << warn << "WARNING - Cannot open file " << fname << endl;
3331        return 0;
3332    }
3333
3334    return Read(name);
3335}
3336
3337// --------------------------------------------------------------------------
3338//
3339//  Opens an open dialog
3340//
3341Int_t MStatusDisplay::Open()
3342{
3343    static const char *gOpenTypes[] =
3344    {
3345        "ROOT files", "*.root",
3346        "All files",  "*",
3347        NULL,           NULL
3348    };
3349
3350    static TString dir(".");
3351
3352    TGFileInfo fi; // fFileName and fIniDir deleted in ~TGFileInfo
3353
3354    fi.fFileTypes = (const char**)gOpenTypes;
3355    fi.fIniDir    = StrDup(dir);
3356
3357    new TGFileDialog(fClient->GetRoot(), this, kFDOpen, &fi);
3358
3359    if (!fi.fFilename)
3360        return 0;
3361
3362    dir = fi.fIniDir;
3363
3364    return Open(fi.fFilename);
3365}
3366
3367// --------------------------------------------------------------------------
3368//
3369//  Change width of display. The height is calculated accordingly.
3370//
3371void MStatusDisplay::SetDisplayWidth(UInt_t dw)
3372{
3373    if (gROOT->IsBatch())
3374    {
3375        SetCanvasWidth(dw);
3376        return;
3377    }
3378
3379    // 4 == 2*default border with of canvas
3380    dw -= 4;
3381
3382    // Difference between canvas size and display size
3383    const UInt_t cw = GetWidth() -fTab->GetWidth();
3384    const UInt_t ch = GetHeight()-fTab->GetHeight()+fTab->GetTabHeight();
3385
3386    const UInt_t dh = TMath::Nint((dw - cw)/1.5 + ch);
3387
3388    Resize(dw, dh); // Set display size
3389}
3390
3391// --------------------------------------------------------------------------
3392//
3393//  Change height of display. The width is calculated accordingly.
3394//
3395void MStatusDisplay::SetDisplayHeight(UInt_t dh)
3396{
3397    if (gROOT->IsBatch())
3398    {
3399        SetCanvasHeight(dh);
3400        return;
3401    }
3402
3403    // 4 == 2*default border with of canvas
3404    dh -= 4;
3405
3406    // Difference between canvas size and display size
3407    const UInt_t cw = GetWidth() -fTab->GetWidth();
3408    const UInt_t ch = GetHeight()-fTab->GetHeight()+fTab->GetTabHeight();
3409
3410    const UInt_t dw = TMath::Nint((dh - ch)*1.5 + cw);
3411
3412    Resize(dw, dh); // Set display size
3413}
3414
3415// --------------------------------------------------------------------------
3416//
3417//  Change width of canvas. The height is calculated accordingly.
3418//
3419void MStatusDisplay::SetCanvasWidth(UInt_t w)
3420{
3421    // 4 == 2*default border with of canvas
3422    w += 4;
3423
3424    if (gROOT->IsBatch())
3425    {
3426        Resize(w, 3*w/2);
3427        return;
3428    }
3429
3430    // Difference between canvas size and display size
3431    const UInt_t cw = GetWidth() -fTab->GetWidth();
3432    const UInt_t ch = GetHeight()-fTab->GetHeight()+fTab->GetTabHeight();
3433
3434    const UInt_t h  = TMath::Nint(w/1.5 + ch);
3435
3436    Resize(w + cw, h); // Set display size
3437}
3438
3439// --------------------------------------------------------------------------
3440//
3441//  Change height of canvas. The width is calculated accordingly.
3442//
3443void MStatusDisplay::SetCanvasHeight(UInt_t h)
3444{
3445    // 4 == 2*default border with of canvas
3446    h += 4;
3447
3448    if (gROOT->IsBatch())
3449    {
3450        Resize(2*h/3, h);
3451        return;
3452    }
3453
3454    // Difference between canvas size and display size
3455    const UInt_t cw = GetWidth() -fTab->GetWidth();
3456    const UInt_t ch = GetHeight()-fTab->GetHeight()+fTab->GetTabHeight();
3457
3458    // 4 == 2*default border with of canvas
3459    const UInt_t dw  = TMath::Nint((h+4)*1.5 + cw);
3460
3461    Resize(dw, h + ch); // Set display size
3462}
3463
3464// --------------------------------------------------------------------------
3465//
3466// Calculate width and height of the display such that it fits into the
3467// defined box.
3468//
3469void MStatusDisplay::SetDisplaySize(UInt_t w, UInt_t h)
3470{
3471    if (gROOT->IsBatch())
3472        return;
3473
3474    SetDisplayHeight(h);
3475
3476    if (GetWidth()>w)
3477        SetDisplayWidth(w);
3478}
3479
3480// --------------------------------------------------------------------------
3481//
3482//  Calculate an optimum size for the display from the desktop size
3483//
3484void MStatusDisplay::SetOptimumSize()
3485{
3486    if (gROOT->IsBatch())
3487        return;
3488
3489    const UInt_t w = TMath::Nint(0.95*gClient->GetDisplayWidth());
3490    const UInt_t h = TMath::Nint(0.95*gClient->GetDisplayHeight());
3491
3492    SetDisplaySize(w, h);
3493}
3494
3495
3496Bool_t MStatusDisplay::HandleConfigureNotify(Event_t *evt)
3497{
3498    //
3499    // The initialization of the GUI is not yet enough finished...
3500    //
3501    if (!fTab)
3502        return kTRUE;
3503
3504    UInt_t w = evt->fWidth;
3505    UInt_t h = evt->fHeight;
3506
3507    const Bool_t wchanged = w!=GetWidth()-fTab->GetWidth();
3508    const Bool_t hchanged = h!=GetHeight()-fTab->GetHeight();
3509
3510    if (!wchanged && !hchanged)
3511    {
3512        Layout();
3513        // FIXME: Make sure that this doesn't result in endless loops.
3514        return kTRUE;
3515    }
3516
3517    if (GetWidth()==1 && GetHeight()==1)
3518        return kTRUE;
3519
3520    // calculate the constant part of the window
3521    const UInt_t cw = GetWidth() -fTab->GetWidth();
3522    const UInt_t ch = GetHeight()-fTab->GetHeight()+fTab->GetTabHeight();
3523
3524    // calculate new size of frame (canvas @ 2:3)
3525    if (hchanged)
3526        w = TMath::Nint((h-ch)*1.5+cw);
3527    else
3528        h = TMath::Nint((w-cw)/1.5+ch);
3529
3530    // resize frame
3531    Resize(w, h);
3532
3533    return kTRUE;
3534}
3535
3536Bool_t MStatusDisplay::HandleEvent(Event_t *event)
3537{
3538    // Instead  of doing this in CloseWindow (called from HandleEvent)
3539    // we do it here. This makes sure, that handle event doesn't
3540    // execute code after deleting this.
3541    if (event->fType==kDestroyNotify)
3542    {
3543        if (Close())
3544            delete this;
3545//        Close();
3546        return kTRUE;
3547    }
3548
3549    const Bool_t rc = TGMainFrame::HandleEvent(event);
3550
3551    //
3552    // This fixes a bug in older root versions which makes
3553    // TCanvas crash if gPad==NULL. So we make sure, that
3554    // gPad!=NULL -- be carfull, this may have other side
3555    // effects.
3556    //
3557#if ROOT_VERSION_CODE < ROOT_VERSION(3,10,01)
3558    if (!gPad && fTab)
3559        for (int i=0; i<fTab->GetNumberOfTabs(); i++)
3560        {
3561            TCanvas *c = GetCanvas(i);
3562            if (c)
3563            {
3564                c->cd();
3565                gLog << dbg << "MStatusDisplay::HandleEvent - Workaround: gPad=" << gPad << "." << endl;
3566                break;
3567            }
3568        }
3569#endif
3570
3571    return rc;
3572}
Note: See TracBrowser for help on using the repository browser.