source: trunk/MagicSoft/Mars/mbase/MStatusDisplay.cc@ 9578

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