Logo ROOT  
Reference Guide
TPad.cxx
Go to the documentation of this file.
1 // @(#)root/gpad:$Id$
2 // Author: Rene Brun 12/12/94
3 
4 /*************************************************************************
5  * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers. *
6  * All rights reserved. *
7  * *
8  * For the licensing terms see $ROOTSYS/LICENSE. *
9  * For the list of contributors see $ROOTSYS/README/CREDITS. *
10  *************************************************************************/
11 
12 #include <cstring>
13 #include <cstdlib>
14 #include <iostream>
15 
16 #include "TROOT.h"
17 #include "TBuffer.h"
18 #include "TError.h"
19 #include "TMath.h"
20 #include "TSystem.h"
21 #include "TStyle.h"
22 #include "TH1.h"
23 #include "TH2.h"
24 #include "TH3.h"
25 #include "TClass.h"
26 #include "TBaseClass.h"
27 #include "TClassTable.h"
28 #include "TVirtualPS.h"
29 #include "TVirtualX.h"
30 #include "TVirtualViewer3D.h"
31 #include "TView.h"
32 #include "TPoint.h"
33 #include "TGraph.h"
34 #include "TMultiGraph.h"
35 #include "THStack.h"
36 #include "TPaveText.h"
37 #include "TPaveStats.h"
38 #include "TGroupButton.h"
39 #include "TBrowser.h"
40 #include "TVirtualGL.h"
41 #include "TString.h"
42 #include "TDataMember.h"
43 #include "TMethod.h"
44 #include "TDataType.h"
45 #include "TFrame.h"
46 #include "TExec.h"
47 #include "TDatime.h"
48 #include "TColor.h"
49 #include "TCanvas.h"
50 #include "TPluginManager.h"
51 #include "TEnv.h"
52 #include "TImage.h"
53 #include "TViewer3DPad.h"
54 #include "TCreatePrimitives.h"
55 #include "TLegend.h"
56 #include "TAtt3D.h"
57 #include "TVirtualPadPainter.h"
58 #include "strlcpy.h"
59 #include "snprintf.h"
60 
61 #include "TVirtualMutex.h"
62 
63 static Int_t gReadLevel = 0;
64 
66 
68 
69 /** \class TPad
70 \ingroup gpad
71 
72 The most important graphics class in the ROOT system.
73 
74 A Pad is contained in a Canvas.
75 
76 A Pad may contain other pads (unlimited pad hierarchy).
77 
78 A pad is a linked list of primitives of any type (graphics objects,
79 histograms, detectors, tracks, etc.).
80 
81 Adding a new element into a pad is in general performed by the Draw
82 member function of the object classes.
83 
84 It is important to realize that the pad is a linked list of references
85 to the original object.
86 For example, in case of a histogram, the histogram.Draw() operation
87 only stores a reference to the histogram object and not a graphical
88 representation of this histogram.
89 When the mouse is used to change (say the bin content), the bin content
90 of the original histogram is changed.
91 
92 The convention used in ROOT is that a Draw operation only adds
93 a reference to the object. The effective drawing is performed
94 when the canvas receives a signal to be painted.
95 
96 \image html gpad_pad1.png
97 
98 This signal is generally sent when typing carriage return in the
99 command input or when a graphical operation has been performed on one
100 of the pads of this canvas.
101 When a Canvas/Pad is repainted, the member function Paint for all
102 objects in the Pad linked list is invoked.
103 
104 \image html gpad_pad2.png
105 
106 When the mouse is moved on the Pad, The member function DistancetoPrimitive
107 is called for all the elements in the pad. DistancetoPrimitive returns
108 the distance in pixels to this object.
109 
110 When the object is within the distance window, the member function
111 ExecuteEvent is called for this object.
112 
113 In ExecuteEvent, move, changes can be performed on the object.
114 
115 For examples of DistancetoPrimitive and ExecuteEvent functions,
116 see classes
117 ~~~ {.cpp}
118  TLine::DistancetoPrimitive, TLine::ExecuteEvent
119  TBox::DistancetoPrimitive, TBox::ExecuteEvent
120  TH1::DistancetoPrimitive, TH1::ExecuteEvent
121 ~~~
122 A Pad supports linear and log scales coordinate systems.
123 The transformation coefficients are explained in TPad::ResizePad.
124 */
125 
126 ////////////////////////////////////////////////////////////////////////////////
127 /// Pad default constructor.
128 
130 {
131  fModified = kTRUE;
132  fTip = nullptr;
133  fPadPointer = nullptr;
134  fPrimitives = nullptr;
135  fExecs = nullptr;
136  fCanvas = nullptr;
137  fPadPaint = 0;
138  fPixmapID = -1;
139  fGLDevice = -1;
140  fCopyGLDevice = kFALSE;
141  fEmbeddedGL = kFALSE;
142  fTheta = 30;
143  fPhi = 30;
144  fNumber = 0;
145  fAbsCoord = kFALSE;
146  fEditable = kTRUE;
147  fCrosshair = 0;
148  fCrosshairPos = 0;
149  fPadView3D = nullptr;
150  fMother = (TPad*)gPad;
151 
152  fAbsHNDC = 0.;
153  fAbsPixeltoXk = 0.;
154  fAbsPixeltoYk = 0.;
155  fAbsWNDC = 0.;
156  fAbsXlowNDC = 0.;
157  fAbsYlowNDC = 0.;
158  fBorderMode = 0;
159  fBorderSize = 0;
160  fPixeltoX = 0;
161  fPixeltoXk = 0.;
162  fPixeltoY = 0.;
163  fPixeltoYk = 0.;
164  fUtoAbsPixelk = 0.;
165  fUtoPixel = 0.;
166  fUtoPixelk = 0.;
167  fVtoAbsPixelk = 0.;
168  fVtoPixel = 0.;
169  fVtoPixelk = 0.;
170  fXtoAbsPixelk = 0.;
171  fXtoPixel = 0.;
172  fXtoPixelk = 0.;
173  fYtoAbsPixelk = 0.;
174  fYtoPixel = 0.;
175  fYtoPixelk = 0.;
176  fXUpNDC = 0.;
177  fYUpNDC = 0.;
178 
179  fFixedAspectRatio = kFALSE;
180  fAspectRatio = 0.;
181 
182  fNumPaletteColor = 0;
183  fNextPaletteColor = 0;
184  fCollideGrid = nullptr;
185  fCGnx = 0;
186  fCGny = 0;
187 
188  fLogx = 0;
189  fLogy = 0;
190  fLogz = 0;
191  fGridx = 0;
192  fGridy = 0;
193  fTickx = 0;
194  fTicky = 0;
195  fFrame = nullptr;
196  fView = nullptr;
197 
198  fUxmin = fUymin = fUxmax = fUymax = 0;
199 
200  // Set default world coordinates to NDC [0,1]
201  fX1 = 0;
202  fX2 = 1;
203  fY1 = 0;
204  fY2 = 1;
205 
206  // Set default pad range
207  fXlowNDC = 0;
208  fYlowNDC = 0;
209  fWNDC = 1;
210  fHNDC = 1;
211 
212  fViewer3D = nullptr;
213  SetBit(kMustCleanup);
214 
215  // the following line is temporarily disabled. It has side effects
216  // when the pad is a TDrawPanelHist or a TFitPanel.
217  // the line was supposed to fix a problem with DrawClonePad
218  // gROOT->SetSelectedPad(this);
219 }
220 
221 ////////////////////////////////////////////////////////////////////////////////
222 /// Pad constructor.
223 ///
224 /// A pad is a linked list of primitives.
225 /// A pad is contained in a canvas. It may contain other pads.
226 /// A pad has attributes. When a pad is created, the attributes
227 /// defined in the current style are copied to the pad attributes.
228 ///
229 /// \param[in] name pad name
230 /// \param[in] title pad title
231 /// \param[in] xlow [0,1] is the position of the bottom left point of the pad
232 /// expressed in the mother pad reference system
233 /// \param[in] ylow [0,1] is the Y position of this point.
234 /// \param[in] xup [0,1] is the x position of the top right point of the pad
235 /// expressed in the mother pad reference system
236 /// \param[in] yup [0,1] is the Y position of this point.
237 /// \param[in] color pad color
238 /// \param[in] bordersize border size in pixels
239 /// \param[in] bordermode border mode
240 /// - bordermode = -1 box looks as it is behind the screen
241 /// - bordermode = 0 no special effects
242 /// - bordermode = 1 box looks as it is in front of the screen
243 
244 TPad::TPad(const char *name, const char *title, Double_t xlow,
245  Double_t ylow, Double_t xup, Double_t yup,
246  Color_t color, Short_t bordersize, Short_t bordermode)
247  : TVirtualPad(name,title,xlow,ylow,xup,yup,color,bordersize,bordermode)
248 {
249  fModified = kTRUE;
250  fTip = nullptr;
251  fBorderSize = bordersize;
252  fBorderMode = bordermode;
253  if (gPad) fCanvas = gPad->GetCanvas();
254  else fCanvas = (TCanvas*)this;
255  fMother = (TPad*)gPad;
256  fPrimitives = new TList;
257  fExecs = new TList;
258  fPadPointer = nullptr;
259  fTheta = 30;
260  fPhi = 30;
265  fFrame = nullptr;
266  fView = nullptr;
267  fPadPaint = 0;
268  fPadView3D = nullptr;
269  fPixmapID = -1; // -1 means pixmap will be created by ResizePad()
272  fNumber = 0;
273  fAbsCoord = kFALSE;
274  fEditable = kTRUE;
275  fCrosshair = 0;
276  fCrosshairPos = 0;
277 
278  fVtoAbsPixelk = 0.;
279  fVtoPixelk = 0.;
280  fVtoPixel = 0.;
281  fAbsPixeltoXk = 0.;
282  fPixeltoXk = 0.;
283  fPixeltoX = 0;
284  fAbsPixeltoYk = 0.;
285  fPixeltoYk = 0.;
286  fPixeltoY = 0.;
287  fXlowNDC = 0;
288  fYlowNDC = 0;
289  fWNDC = 1;
290  fHNDC = 1;
291  fXUpNDC = 0.;
292  fYUpNDC = 0.;
293  fAbsXlowNDC = 0.;
294  fAbsYlowNDC = 0.;
295  fAbsWNDC = 0.;
296  fAbsHNDC = 0.;
297  fXtoAbsPixelk = 0.;
298  fXtoPixelk = 0.;
299  fXtoPixel = 0.;
300  fYtoAbsPixelk = 0.;
301  fYtoPixelk = 0.;
302  fYtoPixel = 0.;
303  fUtoAbsPixelk = 0.;
304  fUtoPixelk = 0.;
305  fUtoPixel = 0.;
306 
307  fUxmin = fUymin = fUxmax = fUymax = 0;
308  fLogx = gStyle->GetOptLogx();
309  fLogy = gStyle->GetOptLogy();
310  fLogz = gStyle->GetOptLogz();
311 
313  fAspectRatio = 0.;
314 
315  fNumPaletteColor = 0;
316  fNextPaletteColor = 0;
317  fCollideGrid = nullptr;
318  fCGnx = 0;
319  fCGny = 0;
320 
321  fViewer3D = nullptr;
322 
324  // Set default world coordinates to NDC [0,1]
325  fX1 = 0;
326  fX2 = 1;
327  fY1 = 0;
328  fY2 = 1;
329 
330  if (!gPad) {
331  Error("TPad", "You must create a TCanvas before creating a TPad");
332  MakeZombie();
333  return;
334  }
335 
336  TPad *padsav = (TPad*)gPad;
337 
338  if ((xlow < 0) || (xlow > 1) || (ylow < 0) || (ylow > 1)) {
339  Error("TPad", "illegal bottom left position: x=%f, y=%f", xlow, ylow);
340  goto zombie;
341  }
342  if ((xup < 0) || (xup > 1) || (yup < 0) || (yup > 1)) {
343  Error("TPad", "illegal top right position: x=%f, y=%f", xup, yup);
344  goto zombie;
345  }
346  if (xup-xlow <= 0) {
347  Error("TPad", "illegal width: %f", xup-xlow);
348  goto zombie;
349  }
350  if (yup-ylow <= 0) {
351  Error("TPad", "illegal height: %f", yup-ylow);
352  goto zombie;
353  }
354 
355  fLogx = gStyle->GetOptLogx();
356  fLogy = gStyle->GetOptLogy();
357  fLogz = gStyle->GetOptLogz();
358 
359  fUxmin = fUymin = fUxmax = fUymax = 0;
360 
361  // Set pad parameters and Compute conversion coefficients
362  SetPad(name, title, xlow, ylow, xup, yup, color, bordersize, bordermode);
363  Range(0, 0, 1, 1);
366 
367  padsav->cd();
368  return;
369 
370 zombie:
371  // error in creating pad occurred, make this pad a zombie
372  MakeZombie();
373  padsav->cd();
374 }
375 
376 
377 ////////////////////////////////////////////////////////////////////////////////
378 /// Pad destructor.
379 
381 {
382  if (!TestBit(kNotDeleted)) return;
383  Close();
386  auto primitives = fPrimitives;
387  // In some cases, fPrimitives has the kMustCleanup bit set which will lead
388  // its destructor to call RecursiveRemove and since this pad is still
389  // likely to be (indirectly) in the list of cleanups, we must set
390  // fPrimitives to nullptr to avoid TPad::RecursiveRemove from calling
391  // a member function of a partially destructed object.
392  fPrimitives = nullptr;
393  delete primitives;
395  delete fViewer3D;
396  if (fCollideGrid) delete [] fCollideGrid;
397 
398  // Required since we overload TObject::Hash.
400  if (this == gPad) gPad=nullptr;
401 }
402 
403 ////////////////////////////////////////////////////////////////////////////////
404 /// Add a new TExec object to the list of Execs.
405 ///
406 /// When an event occurs in the pad (mouse click, etc) the list of C++ commands
407 /// in the list of Execs are executed via TPad::AutoExec.
408 ///
409 /// When a pad event occurs (mouse move, click, etc) all the commands
410 /// contained in the fExecs list are executed in the order found in the list.
411 ///
412 /// This facility is activated by default. It can be deactivated by using
413 /// the canvas "Option" menu.
414 ///
415 /// The following examples of TExec commands are provided in the tutorials:
416 /// macros exec1.C and exec2.C.
417 ///
418 /// ### Example1 of use of exec1.C
419 ///
420 /// ~~~ {.cpp}
421 /// Root > TFile f("hsimple.root")
422 /// Root > hpx.Draw()
423 /// Root > c1.AddExec("ex1",".x exec1.C")
424 /// ~~~
425 ///
426 /// At this point you can use the mouse to click on the contour of
427 /// the histogram hpx. When the mouse is clicked, the bin number and its
428 /// contents are printed.
429 ///
430 /// ### Example2 of use of exec1.C
431 ///
432 /// ~~~ {.cpp}
433 /// Root > TFile f("hsimple.root")
434 /// Root > hpxpy.Draw()
435 /// Root > c1.AddExec("ex2",".x exec2.C")
436 /// ~~~
437 ///
438 /// When moving the mouse in the canvas, a second canvas shows the
439 /// projection along X of the bin corresponding to the Y position
440 /// of the mouse. The resulting histogram is fitted with a gaussian.
441 /// A "dynamic" line shows the current bin position in Y.
442 /// This more elaborated example can be used as a starting point
443 /// to develop more powerful interactive applications exploiting the C++
444 /// interpreter as a development engine.
445 
446 void TPad::AddExec(const char *name, const char*command)
447 {
448  if (!fExecs) fExecs = new TList;
449  TExec *ex = new TExec(name,command);
450  fExecs->Add(ex);
451 }
452 
453 ////////////////////////////////////////////////////////////////////////////////
454 /// Execute the list of Execs when a pad event occurs.
455 
457 {
458  if (GetCrosshair()) DrawCrosshair();
459 
460  if (!fExecs) fExecs = new TList;
461  TIter next(fExecs);
462  TExec *exec;
463  while ((exec = (TExec*)next())) {
464  exec->Exec();
465  }
466 }
467 
468 ////////////////////////////////////////////////////////////////////////////////
469 /// Browse pad.
470 
472 {
473  cd();
475 }
476 
477 ////////////////////////////////////////////////////////////////////////////////
478 /// Build a legend from the graphical objects in the pad.
479 ///
480 /// A simple method to build automatically a TLegend from the primitives in a TPad.
481 ///
482 /// Only those deriving from TAttLine, TAttMarker and TAttFill are added, excluding
483 /// TPave and TFrame derived classes.
484 ///
485 /// \return The built TLegend
486 ///
487 /// \param[in] x1, y1, x2, y2 The TLegend coordinates
488 /// \param[in] title The legend title. By default it is " "
489 /// \param[in] option The TLegend option
490 ///
491 /// The caller program owns the returned TLegend.
492 ///
493 /// If the pad contains some TMultiGraph or THStack the individual
494 /// graphs or histograms in them are added to the TLegend.
495 ///
496 /// ### Automatic placement of the legend
497 /// If `x1` is equal to `x2` and `y1` is equal to `y2` the legend will be automatically
498 /// placed to avoid overlapping with the existing primitives already displayed.
499 /// `x1` is considered as the width of the legend and `y1` the height. By default
500 /// the legend is automatically placed with width = `x1`= `x2` = 0.3 and
501 /// height = `y1`= `y2` = 0.21.
502 
504  const char* title, Option_t *option)
505 {
506  TList *lop=GetListOfPrimitives();
507  if (!lop) return 0;
508  TLegend *leg=0;
509  TIter next(lop);
510  TString mes;
511  TObject *o=0;
512  TString opt("");
513  while( (o=next()) ) {
515  o->InheritsFrom(TAttFill::Class())) &&
516  ( !(o->InheritsFrom(TFrame::Class())) && !(o->InheritsFrom(TPave::Class())) )) {
517  if (!leg) leg = new TLegend(x1, y1, x2, y2, title);
518  if (o->InheritsFrom(TNamed::Class()) && strlen(((TNamed *)o)->GetTitle()))
519  mes = ((TNamed *)o)->GetTitle();
520  else if (strlen(o->GetName()))
521  mes = o->GetName();
522  else
523  mes = o->ClassName();
524  if (strlen(option)) {
525  opt = option;
526  } else {
527  if (o->InheritsFrom(TAttLine::Class())) opt += "l";
528  if (o->InheritsFrom(TAttMarker::Class())) opt += "p";
529  if (o->InheritsFrom(TAttFill::Class())) opt += "f";
530  }
531  leg->AddEntry(o,mes.Data(),opt.Data());
532  } else if ( o->InheritsFrom(TMultiGraph::Class() ) ) {
533  if (!leg) leg = new TLegend(x1, y1, x2, y2, title);
534  TList * grlist = ((TMultiGraph *)o)->GetListOfGraphs();
535  TIter nextgraph(grlist);
536  TGraph * gr;
537  TObject * obj;
538  while ((obj = nextgraph())) {
539  gr = (TGraph*) obj;
540  if (strlen(gr->GetTitle())) mes = gr->GetTitle();
541  else if (strlen(gr->GetName())) mes = gr->GetName();
542  else mes = gr->ClassName();
543  if (strlen(option)) opt = option;
544  else opt = "lpf";
545  leg->AddEntry( obj, mes.Data(), opt );
546  }
547  } else if ( o->InheritsFrom(THStack::Class() ) ) {
548  if (!leg) leg = new TLegend(x1, y1, x2, y2, title);
549  TList * hlist = ((THStack *)o)->GetHists();
550  TIter nexthist(hlist);
551  TH1 * hist;
552  TObject * obj;
553  while ((obj = nexthist())) {
554  hist = (TH1*) obj;
555  if (strlen(hist->GetTitle())) mes = hist->GetTitle();
556  else if (strlen(hist->GetName())) mes = hist->GetName();
557  else mes = hist->ClassName();
558  if (strlen(option)) opt = option;
559  else opt = "lpf";
560  leg->AddEntry( obj, mes.Data(), opt );
561  }
562  }
563  }
564  if (leg) {
565  TVirtualPad *gpadsave;
566  gpadsave = gPad;
567  this->cd();
568  leg->Draw();
569  gpadsave->cd();
570  } else {
571  Info("BuildLegend(void)","No object to build a TLegend.");
572  }
573  return leg;
574 }
575 
576 ////////////////////////////////////////////////////////////////////////////////
577 /// Set Current pad.
578 ///
579 /// When a canvas/pad is divided via TPad::Divide, one can directly
580 /// set the current path to one of the subdivisions.
581 /// See TPad::Divide for the convention to number sub-pads.
582 ///
583 /// Returns the new current pad, or 0 in case of failure.
584 ///
585 /// For example:
586 /// ~~~ {.cpp}
587 /// c1.Divide(2,3); // create 6 pads (2 divisions along x, 3 along y).
588 /// ~~~
589 /// To set the current pad to the bottom right pad, do
590 /// ~~~ {.cpp}
591 /// c1.cd(6);
592 /// ~~~
593 /// Note1: c1.cd() is equivalent to c1.cd(0) and sets the current pad
594 /// to c1 itself.
595 ///
596 /// Note2: after a statement like c1.cd(6), the global variable gPad
597 /// points to the current pad. One can use gPad to set attributes
598 /// of the current pad.
599 ///
600 /// Note3: One can get a pointer to one of the sub-pads of pad with:
601 /// TPad *subpad = (TPad*)pad->GetPad(subpadnumber);
602 
603 TVirtualPad *TPad::cd(Int_t subpadnumber)
604 {
605  if (!subpadnumber) {
606  gPad = this;
607  if (!gPad->IsBatch() && GetPainter()) GetPainter()->SelectDrawable(fPixmapID);
608  if (!fPrimitives) fPrimitives = new TList;
609  return gPad;
610  }
611 
612  TObject *obj;
613  if (!fPrimitives) fPrimitives = new TList;
614  TIter next(fPrimitives);
615  while ((obj = next())) {
616  if (obj->InheritsFrom(TPad::Class())) {
617  Int_t n = ((TPad*)obj)->GetNumber();
618  if (n == subpadnumber) {
619  return ((TPad*)obj)->cd();
620  }
621  }
622  }
623  return 0;
624 }
625 
626 ////////////////////////////////////////////////////////////////////////////////
627 /// Delete all pad primitives.
628 ///
629 /// If the bit kClearAfterCR has been set for this pad, the Clear function
630 /// will execute only after having pressed a CarriageReturn
631 /// Set the bit with `mypad->SetBit(TPad::kClearAfterCR)`
632 
633 void TPad::Clear(Option_t *option)
634 {
635  if (!IsEditable()) return;
636 
638 
639  if (!fPadPaint) {
640  SafeDelete(fView);
641  if (fPrimitives) fPrimitives->Clear(option);
642  if (fFrame) {
643  if (fFrame->TestBit(kNotDeleted)) delete fFrame;
644  fFrame = nullptr;
645  }
646  }
647  if (fCanvas) fCanvas->Cleared(this);
648 
649  cd();
650 
651  if (TestBit(kClearAfterCR)) {
652  // Intentional do not use the return value of getchar,
653  // we just want to get it and forget it
654  getchar();
655  }
656 
657  if (!gPad->IsBatch() && GetPainter()) GetPainter()->ClearDrawable();
658  if (gVirtualPS && gPad == gPad->GetCanvas()) gVirtualPS->NewPage();
659 
661  fCrosshairPos = 0;
662  fNumPaletteColor = 0;
663  if (fCollideGrid) {
664  delete [] fCollideGrid;
665  fCollideGrid = nullptr;
666  fCGnx = 0;
667  fCGny = 0;
668  }
670 }
671 
672 ////////////////////////////////////////////////////////////////////////////////
673 /// Clipping routine: Cohen Sutherland algorithm.
674 ///
675 /// - If Clip ==2 the segment is outside the boundary.
676 /// - If Clip ==1 the segment has one point outside the boundary.
677 /// - If Clip ==0 the segment is inside the boundary.
678 ///
679 /// \param[in] x[],y[] Segment coordinates (2 points)
680 /// \param[in] xclipl,yclipb,xclipr,yclipt Clipping boundary
681 /// \param[out] x[],y[] New segment coordinates( 2 points)
682 
683 Int_t TPad::Clip(Float_t *x, Float_t *y, Float_t xclipl, Float_t yclipb, Float_t xclipr, Float_t yclipt)
684 {
685  const Float_t kP=10000;
686  Int_t clip = 0;
687 
688  for (Int_t i=0;i<2;i++) {
689  if (TMath::Abs(xclipl-x[i]) <= TMath::Abs(xclipr-xclipl)/kP) x[i] = xclipl;
690  if (TMath::Abs(xclipr-x[i]) <= TMath::Abs(xclipr-xclipl)/kP) x[i] = xclipr;
691  if (TMath::Abs(yclipb-y[i]) <= TMath::Abs(yclipt-yclipb)/kP) y[i] = yclipb;
692  if (TMath::Abs(yclipt-y[i]) <= TMath::Abs(yclipt-yclipb)/kP) y[i] = yclipt;
693  }
694 
695  // Compute the first endpoint codes.
696  Int_t code1 = ClippingCode(x[0],y[0],xclipl,yclipb,xclipr,yclipt);
697  Int_t code2 = ClippingCode(x[1],y[1],xclipl,yclipb,xclipr,yclipt);
698 
699  Double_t xt=0, yt=0;
700  Int_t clipped = 0; //this variable could be used in a future version
701  while(code1 + code2) {
702  clipped = 1;
703 
704  // The line lies entirely outside the clipping boundary
705  if (code1&code2) {
706  clip = 2;
707  return clip;
708  }
709 
710  // The line is subdivided into several parts
711  Int_t ic = code1;
712  if (ic == 0) ic = code2;
713  if (ic & 0x1) {
714  yt = y[0] + (y[1]-y[0])*(xclipl-x[0])/(x[1]-x[0]);
715  xt = xclipl;
716  }
717  if (ic & 0x2) {
718  yt = y[0] + (y[1]-y[0])*(xclipr-x[0])/(x[1]-x[0]);
719  xt = xclipr;
720  }
721  if (ic & 0x4) {
722  xt = x[0] + (x[1]-x[0])*(yclipb-y[0])/(y[1]-y[0]);
723  yt = yclipb;
724  }
725  if (ic & 0x8) {
726  xt = x[0] + (x[1]-x[0])*(yclipt-y[0])/(y[1]-y[0]);
727  yt = yclipt;
728  }
729  if (ic == code1) {
730  x[0] = xt;
731  y[0] = yt;
732  code1 = ClippingCode(xt,yt,xclipl,yclipb,xclipr,yclipt);
733  } else {
734  x[1] = xt;
735  y[1] = yt;
736  code2 = ClippingCode(xt,yt,xclipl,yclipb,xclipr,yclipt);
737  }
738  }
739  clip = clipped;
740  return clip;
741 }
742 
743 ////////////////////////////////////////////////////////////////////////////////
744 /// Clipping routine: Cohen Sutherland algorithm.
745 ///
746 /// - If Clip ==2 the segment is outside the boundary.
747 /// - If Clip ==1 the segment has one point outside the boundary.
748 /// - If Clip ==0 the segment is inside the boundary.
749 ///
750 /// \param[in] x[],y[] Segment coordinates (2 points)
751 /// \param[in] xclipl,yclipb,xclipr,yclipt Clipping boundary
752 /// \param[out] x[],y[] New segment coordinates(2 points)
753 
754 Int_t TPad::Clip(Double_t *x, Double_t *y, Double_t xclipl, Double_t yclipb, Double_t xclipr, Double_t yclipt)
755 {
756  const Double_t kP=10000;
757  Int_t clip = 0;
758 
759  for (Int_t i=0;i<2;i++) {
760  if (TMath::Abs(xclipl-x[i]) <= TMath::Abs(xclipr-xclipl)/kP) x[i] = xclipl;
761  if (TMath::Abs(xclipr-x[i]) <= TMath::Abs(xclipr-xclipl)/kP) x[i] = xclipr;
762  if (TMath::Abs(yclipb-y[i]) <= TMath::Abs(yclipt-yclipb)/kP) y[i] = yclipb;
763  if (TMath::Abs(yclipt-y[i]) <= TMath::Abs(yclipt-yclipb)/kP) y[i] = yclipt;
764  }
765 
766  // Compute the first endpoint codes.
767  Int_t code1 = 0;
768  if (x[0] < xclipl) code1 = code1 | 0x1;
769  if (x[0] > xclipr) code1 = code1 | 0x2;
770  if (y[0] < yclipb) code1 = code1 | 0x4;
771  if (y[0] > yclipt) code1 = code1 | 0x8;
772  Int_t code2 = 0;
773  if (x[1] < xclipl) code2 = code2 | 0x1;
774  if (x[1] > xclipr) code2 = code2 | 0x2;
775  if (y[1] < yclipb) code2 = code2 | 0x4;
776  if (y[1] > yclipt) code2 = code2 | 0x8;
777 
778  Double_t xt=0, yt=0;
779  Int_t clipped = 0; //this variable could be used in a future version
780  while(code1 + code2) {
781  clipped = 1;
782 
783  // The line lies entirely outside the clipping boundary
784  if (code1&code2) {
785  clip = 2;
786  return clip;
787  }
788 
789  // The line is subdivided into several parts
790  Int_t ic = code1;
791  if (ic == 0) ic = code2;
792  if (ic & 0x1) {
793  yt = y[0] + (y[1]-y[0])*(xclipl-x[0])/(x[1]-x[0]);
794  xt = xclipl;
795  }
796  if (ic & 0x2) {
797  yt = y[0] + (y[1]-y[0])*(xclipr-x[0])/(x[1]-x[0]);
798  xt = xclipr;
799  }
800  if (ic & 0x4) {
801  xt = x[0] + (x[1]-x[0])*(yclipb-y[0])/(y[1]-y[0]);
802  yt = yclipb;
803  }
804  if (ic & 0x8) {
805  xt = x[0] + (x[1]-x[0])*(yclipt-y[0])/(y[1]-y[0]);
806  yt = yclipt;
807  }
808  if (ic == code1) {
809  x[0] = xt;
810  y[0] = yt;
811  code1 = ClippingCode(xt,yt,xclipl,yclipb,xclipr,yclipt);
812  } else {
813  x[1] = xt;
814  y[1] = yt;
815  code2 = ClippingCode(xt,yt,xclipl,yclipb,xclipr,yclipt);
816  }
817  }
818  clip = clipped;
819  return clip;
820 }
821 
822 ////////////////////////////////////////////////////////////////////////////////
823 /// Compute the endpoint codes for TPad::Clip.
824 
826 {
827  Int_t code = 0;
828  if (x < xcl1) code = code | 0x1;
829  if (x > xcl2) code = code | 0x2;
830  if (y < ycl1) code = code | 0x4;
831  if (y > ycl2) code = code | 0x8;
832  return code;
833 }
834 
835 ////////////////////////////////////////////////////////////////////////////////
836 /// Clip polygon using the Sutherland-Hodgman algorithm.
837 ///
838 /// \param[in] n Number of points in the polygon to
839 /// be clipped
840 /// \param[in] x[n],y[n] Polygon do be clipped vertices
841 /// \param[in] xclipl,yclipb,xclipr,yclipt Clipping boundary
842 /// \param[out] nn Number of points in xc and yc
843 /// \param[out] xc,yc Clipped polygon vertices. The Int_t
844 /// returned by this function is
845 /// the number of points in the clipped
846 /// polygon. These vectors must
847 /// be allocated by the calling function.
848 /// A size of 2*n for each is
849 /// enough.
850 ///
851 /// Sutherland and Hodgman's polygon-clipping algorithm uses a divide-and-conquer
852 /// strategy: It solves a series of simple and identical problems that, when
853 /// combined, solve the overall problem. The simple problem is to clip a polygon
854 /// against a single infinite clip edge. Four clip edges, each defining one boundary
855 /// of the clip rectangle, successively clip a polygon against a clip rectangle.
856 ///
857 /// Steps of Sutherland-Hodgman's polygon-clipping algorithm:
858 ///
859 /// * Polygons can be clipped against each edge of the window one at a time.
860 /// Windows/edge intersections, if any, are easy to find since the X or Y coordinates
861 /// are already known.
862 /// * Vertices which are kept after clipping against one window edge are saved for
863 /// clipping against the remaining edges.
864 /// * Note that the number of vertices usually changes and will often increases.
865 ///
866 /// The clip boundary determines a visible and invisible region. The edges from
867 /// vertex i to vertex i+1 can be one of four types:
868 ///
869 /// * Case 1 : Wholly inside visible region - save endpoint
870 /// * Case 2 : Exit visible region - save the intersection
871 /// * Case 3 : Wholly outside visible region - save nothing
872 /// * Case 4 : Enter visible region - save intersection and endpoint
873 
875 {
876  Int_t nc, nc2;
877  Double_t x1, y1, x2, y2, slope; // Segment to be clipped
878 
879  Double_t *xc2 = new Double_t[nn];
880  Double_t *yc2 = new Double_t[nn];
881 
882  // Clip against the left boundary
883  x1 = x[n-1]; y1 = y[n-1];
884  nc2 = 0;
885  Int_t i;
886  for (i=0; i<n; i++) {
887  x2 = x[i]; y2 = y[i];
888  if (x1 == x2) {
889  slope = 0;
890  } else {
891  slope = (y2-y1)/(x2-x1);
892  }
893  if (x1 >= xclipl) {
894  if (x2 < xclipl) {
895  xc2[nc2] = xclipl; yc2[nc2++] = slope*(xclipl-x1)+y1;
896  } else {
897  xc2[nc2] = x2; yc2[nc2++] = y2;
898  }
899  } else {
900  if (x2 >= xclipl) {
901  xc2[nc2] = xclipl; yc2[nc2++] = slope*(xclipl-x1)+y1;
902  xc2[nc2] = x2; yc2[nc2++] = y2;
903  }
904  }
905  x1 = x2; y1 = y2;
906  }
907 
908  // Clip against the top boundary
909  x1 = xc2[nc2-1]; y1 = yc2[nc2-1];
910  nc = 0;
911  for (i=0; i<nc2; i++) {
912  x2 = xc2[i]; y2 = yc2[i];
913  if (y1 == y2) {
914  slope = 0;
915  } else {
916  slope = (x2-x1)/(y2-y1);
917  }
918  if (y1 <= yclipt) {
919  if (y2 > yclipt) {
920  xc[nc] = x1+(yclipt-y1)*slope; yc[nc++] = yclipt;
921  } else {
922  xc[nc] = x2; yc[nc++] = y2;
923  }
924  } else {
925  if (y2 <= yclipt) {
926  xc[nc] = x1+(yclipt-y1)*slope; yc[nc++] = yclipt;
927  xc[nc] = x2; yc[nc++] = y2;
928  }
929  }
930  x1 = x2; y1 = y2;
931  }
932 
933  if (nc>0) {
934 
935  // Clip against the right boundary
936  x1 = xc[nc-1]; y1 = yc[nc-1];
937  nc2 = 0;
938  for (i=0; i<nc; i++) {
939  x2 = xc[i]; y2 = yc[i];
940  if (x1 == x2) {
941  slope = 0;
942  } else {
943  slope = (y2-y1)/(x2-x1);
944  }
945  if (x1 <= xclipr) {
946  if (x2 > xclipr) {
947  xc2[nc2] = xclipr; yc2[nc2++] = slope*(xclipr-x1)+y1;
948  } else {
949  xc2[nc2] = x2; yc2[nc2++] = y2;
950  }
951  } else {
952  if (x2 <= xclipr) {
953  xc2[nc2] = xclipr; yc2[nc2++] = slope*(xclipr-x1)+y1;
954  xc2[nc2] = x2; yc2[nc2++] = y2;
955  }
956  }
957  x1 = x2; y1 = y2;
958  }
959 
960  // Clip against the bottom boundary
961  x1 = xc2[nc2-1]; y1 = yc2[nc2-1];
962  nc = 0;
963  for (i=0; i<nc2; i++) {
964  x2 = xc2[i]; y2 = yc2[i];
965  if (y1 == y2) {
966  slope = 0;
967  } else {
968  slope = (x2-x1)/(y2-y1);
969  }
970  if (y1 >= yclipb) {
971  if (y2 < yclipb) {
972  xc[nc] = x1+(yclipb-y1)*slope; yc[nc++] = yclipb;
973  } else {
974  xc[nc] = x2; yc[nc++] = y2;
975  }
976  } else {
977  if (y2 >= yclipb) {
978  xc[nc] = x1+(yclipb-y1)*slope; yc[nc++] = yclipb;
979  xc[nc] = x2; yc[nc++] = y2;
980  }
981  }
982  x1 = x2; y1 = y2;
983  }
984  }
985 
986  delete [] xc2;
987  delete [] yc2;
988 
989  if (nc < 3) nc =0;
990  return nc;
991 }
992 
993 ////////////////////////////////////////////////////////////////////////////////
994 /// Delete all primitives in pad and pad itself.
995 /// Pad cannot be used anymore after this call.
996 /// Emits signal "Closed()".
997 
999 {
1000  if (!TestBit(kNotDeleted)) return;
1001  if (!fMother) return;
1002  if (!fMother->TestBit(kNotDeleted)) return;
1003 
1004  if (fPrimitives)
1005  fPrimitives->Clear();
1006  if (fView) {
1007  if (fView->TestBit(kNotDeleted)) delete fView;
1008  fView = nullptr;
1009  }
1010  if (fFrame) {
1011  if (fFrame->TestBit(kNotDeleted)) delete fFrame;
1012  fFrame = nullptr;
1013  }
1014 
1015  // emit signal
1016  if (IsA() != TCanvas::Class())
1017  Closed();
1018 
1019  if (fPixmapID != -1) {
1020  if (gPad) {
1021  if (!gPad->IsBatch() && GetPainter())
1023  }
1024  fPixmapID = -1;
1025 
1026  if (!gROOT->GetListOfCanvases()) return;
1027  if (fMother == this) {
1028  gROOT->GetListOfCanvases()->Remove(this);
1029  return; // in case of TCanvas
1030  }
1031 
1032  // remove from the mother's list of primitives
1033  if (fMother) {
1036 
1037  if (gPad == this) fMother->cd();
1038  }
1039  if (fCanvas) {
1040  if (fCanvas->GetPadSave() == this)
1041  fCanvas->ClearPadSave();
1042  if (fCanvas->GetSelectedPad() == this)
1043  fCanvas->SetSelectedPad(0);
1044  if (fCanvas->GetClickSelectedPad() == this)
1046  }
1047  }
1048 
1049  fMother = nullptr;
1050  if (gROOT->GetSelectedPad() == this) gROOT->SetSelectedPad(nullptr);
1051 }
1052 
1053 ////////////////////////////////////////////////////////////////////////////////
1054 /// Copy the pixmap of the pad to the canvas.
1055 
1057 {
1058  int px, py;
1059  XYtoAbsPixel(fX1, fY2, px, py);
1060 
1061  if (fPixmapID != -1 && GetPainter())
1062  GetPainter()->CopyDrawable(fPixmapID, px, py);
1063 
1064  if (this == gPad) HighLight(gPad->GetHighLightColor());
1065 }
1066 
1067 ////////////////////////////////////////////////////////////////////////////////
1068 /// Copy the sub-pixmaps of the pad to the canvas.
1069 
1071 {
1072  TObject *obj;
1073  if (!fPrimitives) fPrimitives = new TList;
1074  TIter next(GetListOfPrimitives());
1075  while ((obj = next())) {
1076  if (obj->InheritsFrom(TPad::Class())) {
1077  ((TPad*)obj)->CopyPixmap();
1078  ((TPad*)obj)->CopyPixmaps();
1079  }
1080  }
1081 }
1082 
1083 ////////////////////////////////////////////////////////////////////////////////
1084 /// Remove TExec name from the list of Execs.
1085 
1086 void TPad::DeleteExec(const char *name)
1087 {
1088  if (!fExecs) fExecs = new TList;
1090  if (!ex) return;
1091  fExecs->Remove(ex);
1092  delete ex;
1093 }
1094 
1095 ////////////////////////////////////////////////////////////////////////////////
1096 /// Compute distance from point px,py to a box.
1097 ///
1098 /// Compute the closest distance of approach from point px,py to the
1099 /// edges of this pad.
1100 /// The distance is computed in pixels units.
1101 
1103 {
1104  Int_t pxl, pyl, pxt, pyt;
1105  Int_t px1 = gPad->XtoAbsPixel(fX1);
1106  Int_t py1 = gPad->YtoAbsPixel(fY1);
1107  Int_t px2 = gPad->XtoAbsPixel(fX2);
1108  Int_t py2 = gPad->YtoAbsPixel(fY2);
1109  if (px1 < px2) {pxl = px1; pxt = px2;}
1110  else {pxl = px2; pxt = px1;}
1111  if (py1 < py2) {pyl = py1; pyt = py2;}
1112  else {pyl = py2; pyt = py1;}
1113 
1114  // Are we inside the box?
1115  // ======================
1116  if ( (px > pxl && px < pxt) && (py > pyl && py < pyt) ) {
1117  if (GetFillStyle()) return 0; //*-* if pad is filled
1118  }
1119 
1120  // Are we on the edges?
1121  // ====================
1122  Int_t dxl = TMath::Abs(px - pxl);
1123  if (py < pyl) dxl += pyl - py;
1124  if (py > pyt) dxl += py - pyt;
1125  Int_t dxt = TMath::Abs(px - pxt);
1126  if (py < pyl) dxt += pyl - py;
1127  if (py > pyt) dxt += py - pyt;
1128  Int_t dyl = TMath::Abs(py - pyl);
1129  if (px < pxl) dyl += pxl - px;
1130  if (px > pxt) dyl += px - pxt;
1131  Int_t dyt = TMath::Abs(py - pyt);
1132  if (px < pxl) dyt += pxl - px;
1133  if (px > pxt) dyt += px - pxt;
1134 
1135  Int_t distance = dxl;
1136  if (dxt < distance) distance = dxt;
1137  if (dyl < distance) distance = dyl;
1138  if (dyt < distance) distance = dyt;
1139 
1140  return distance - Int_t(0.5*fLineWidth);
1141 }
1142 
1143 ////////////////////////////////////////////////////////////////////////////////
1144 /// Automatic pad generation by division.
1145 ///
1146 /// - The current canvas is divided in nx by ny equal divisions (pads).
1147 /// - xmargin is the space along x between pads in percent of canvas.
1148 /// - ymargin is the space along y between pads in percent of canvas.
1149 /// - color is the color of the new pads. If 0, color is the canvas color.
1150 ///
1151 /// Pads are automatically named `canvasname_n` where `n` is the division number
1152 /// starting from top left pad.
1153 ///
1154 /// Example if canvasname=c1 , nx=2, ny=3:
1155 ///
1156 /// \image html gpad_pad3.png
1157 ///
1158 /// Once a pad is divided into sub-pads, one can set the current pad
1159 /// to a subpad with a given division number as illustrated above
1160 /// with TPad::cd(subpad_number).
1161 ///
1162 /// For example, to set the current pad to c1_4, one can do:
1163 /// ~~~ {.cpp}
1164 /// c1->cd(4)
1165 /// ~~~
1166 /// __Note1:__ c1.cd() is equivalent to c1.cd(0) and sets the current pad
1167 /// to c1 itself.
1168 ///
1169 /// __Note2:__ after a statement like c1.cd(6), the global variable gPad
1170 /// points to the current pad. One can use gPad to set attributes
1171 /// of the current pad.
1172 ///
1173 /// __Note3:__ in case xmargin <=0 and ymargin <= 0, there is no space
1174 /// between pads. The current pad margins are recomputed to
1175 /// optimize the layout.
1176 
1177 void TPad::Divide(Int_t nx, Int_t ny, Float_t xmargin, Float_t ymargin, Int_t color)
1178 {
1179  if (!IsEditable()) return;
1180 
1181 
1182  if (gThreadXAR) {
1183  void *arr[7];
1184  arr[1] = this; arr[2] = (void*)&nx;arr[3] = (void*)& ny;
1185  arr[4] = (void*)&xmargin; arr[5] = (void *)& ymargin; arr[6] = (void *)&color;
1186  if ((*gThreadXAR)("PDCD", 7, arr, 0)) return;
1187  }
1188 
1189  TPad *padsav = (TPad*)gPad;
1190  cd();
1191  if (nx <= 0) nx = 1;
1192  if (ny <= 0) ny = 1;
1193  Int_t ix,iy;
1194  Double_t x1,y1,x2,y2;
1195  Double_t dx,dy;
1196  TPad *pad;
1197  Int_t nchname = strlen(GetName())+6;
1198  Int_t nchtitle = strlen(GetTitle())+6;
1199  char *name = new char [nchname];
1200  char *title = new char [nchtitle];
1201  Int_t n = 0;
1202  if (color == 0) color = GetFillColor();
1203  if (xmargin > 0 && ymargin > 0) {
1204  //general case
1205  dy = 1/Double_t(ny);
1206  dx = 1/Double_t(nx);
1207  for (iy=0;iy<ny;iy++) {
1208  y2 = 1 - iy*dy - ymargin;
1209  y1 = y2 - dy + 2*ymargin;
1210  if (y1 < 0) y1 = 0;
1211  if (y1 > y2) continue;
1212  for (ix=0;ix<nx;ix++) {
1213  x1 = ix*dx + xmargin;
1214  x2 = x1 +dx -2*xmargin;
1215  if (x1 > x2) continue;
1216  n++;
1217  snprintf(name,nchname,"%s_%d",GetName(),n);
1218  pad = new TPad(name,name,x1,y1,x2,y2,color);
1219  pad->SetNumber(n);
1220  pad->Draw();
1221  }
1222  }
1223  } else {
1224  // special case when xmargin <= 0 && ymargin <= 0
1225  Double_t xl = GetLeftMargin();
1226  Double_t xr = GetRightMargin();
1227  Double_t yb = GetBottomMargin();
1228  Double_t yt = GetTopMargin();
1229  xl /= (1-xl+xr)*nx;
1230  xr /= (1-xl+xr)*nx;
1231  yb /= (1-yb+yt)*ny;
1232  yt /= (1-yb+yt)*ny;
1233  SetLeftMargin(xl);
1234  SetRightMargin(xr);
1235  SetBottomMargin(yb);
1236  SetTopMargin(yt);
1237  dx = (1-xl-xr)/nx;
1238  dy = (1-yb-yt)/ny;
1239  Int_t number = 0;
1240  for (Int_t i=0;i<nx;i++) {
1241  x1 = i*dx+xl;
1242  x2 = x1 + dx;
1243  if (i == 0) x1 = 0;
1244  if (i == nx-1) x2 = 1-xr;
1245  for (Int_t j=0;j<ny;j++) {
1246  number = j*nx + i +1;
1247  y2 = 1 -j*dy -yt;
1248  y1 = y2 - dy;
1249  if (j == 0) y2 = 1-yt;
1250  if (j == ny-1) y1 = 0;
1251  snprintf(name,nchname,"%s_%d",GetName(),number);
1252  snprintf(title,nchtitle,"%s_%d",GetTitle(),number);
1253  pad = new TPad(name,title,x1,y1,x2,y2);
1254  pad->SetNumber(number);
1255  pad->SetBorderMode(0);
1256  if (i == 0) pad->SetLeftMargin(xl*nx);
1257  else pad->SetLeftMargin(0);
1258  pad->SetRightMargin(0);
1259  pad->SetTopMargin(0);
1260  if (j == ny-1) pad->SetBottomMargin(yb*ny);
1261  else pad->SetBottomMargin(0);
1262  pad->Draw();
1263  }
1264  }
1265  }
1266  delete [] name;
1267  delete [] title;
1268  Modified();
1269  if (padsav) padsav->cd();
1270 }
1271 
1272 ////////////////////////////////////////////////////////////////////////////////
1273 /// "n" is the total number of sub-pads. The number of sub-pads along the X
1274 /// and Y axis are computed according to the square root of n.
1275 
1276 void TPad::DivideSquare(Int_t n, Float_t xmargin, Float_t ymargin, Int_t color)
1277 {
1278  Int_t w = 1, h = 1;
1279  if (!fCanvas) {
1280  Error("DivideSquare", "No canvas associated with this pad.");
1281  return;
1282  }
1284  w = TMath::Ceil(TMath::Sqrt(n));
1286  if (w*h < n) w++;
1287  } else {
1288  h = TMath::Ceil(TMath::Sqrt(n));
1289  w = TMath::Floor(TMath::Sqrt(n));
1290  if (w*h < n) h++;
1291  }
1292 
1293  Divide( w, h, xmargin, ymargin, color);
1294 }
1295 
1296 ////////////////////////////////////////////////////////////////////////////////
1297 /// Draw Pad in Current pad (re-parent pad if necessary).
1298 
1299 void TPad::Draw(Option_t *option)
1300 {
1301  // if no canvas opened yet create a default canvas
1302  if (!gPad) {
1303  gROOT->MakeDefCanvas();
1304  }
1305 
1306  // pad cannot be in itself and it can only be in one other pad at a time
1307  if (!fPrimitives) fPrimitives = new TList;
1308  if (gPad != this) {
1309  if (fMother && fMother->TestBit(kNotDeleted))
1311  TPad *oldMother = fMother;
1312  fCanvas = gPad->GetCanvas();
1313  //
1314  fMother = (TPad*)gPad;
1315  if (oldMother != fMother || fPixmapID == -1) ResizePad();
1316  }
1317 
1318  Paint();
1319 
1320  if (gPad->IsRetained() && gPad != this && fMother)
1321  if (fMother->GetListOfPrimitives()) fMother->GetListOfPrimitives()->Add(this, option);
1322 }
1323 
1324 ////////////////////////////////////////////////////////////////////////////////
1325 /// Draw class inheritance tree of the class to which obj belongs.
1326 ///
1327 /// If a class B inherits from a class A, description of B is drawn
1328 /// on the right side of description of A.
1329 ///
1330 /// Member functions overridden by B are shown in class A with a blue line
1331 /// crossing-out the corresponding member function.
1332 
1333 void TPad::DrawClassObject(const TObject *classobj, Option_t *option)
1334 {
1335  if (!classobj) return;
1336  char dname[256];
1337  const Int_t kMAXLEVELS = 10;
1338  TClass *clevel[kMAXLEVELS], *cl, *cll;
1339  TBaseClass *base, *cinherit;
1340  TText *ptext = 0;
1341  TString opt=option;
1342  Double_t x,y,dy,y1,v1,v2,dv;
1343  Int_t nd,nf,nc,nkd,nkf,i,j;
1344  TPaveText *pt;
1345  Int_t maxlev = 4;
1346  if (opt.Contains("2")) maxlev = 2;
1347  if (opt.Contains("3")) maxlev = 3;
1348  if (opt.Contains("5")) maxlev = 5;
1349  if (opt.Contains("6")) maxlev = 6;
1350  if (opt.Contains("7")) maxlev = 7;
1351 
1352  // Clear and Set Pad range
1353  Double_t xpad = 20.5;
1354  Double_t ypad = 27.5;
1355  Clear();
1356  Range(0,0,xpad,ypad);
1357 
1358  // Find number of levels
1359  Int_t nlevel = 0;
1360  TClass *obj = (TClass*)classobj;
1361  clevel[nlevel] = obj;
1362  TList *lbase = obj->GetListOfBases();
1363  while(lbase) {
1364  base = (TBaseClass*)lbase->First();
1365  if (!base) break;
1366  if ( base->GetClassPointer() == 0) break;
1367  nlevel++;
1368  clevel[nlevel] = base->GetClassPointer();
1369  lbase = clevel[nlevel]->GetListOfBases();
1370  if (nlevel >= maxlev-1) break;
1371  }
1372  Int_t maxelem = 0;
1373  Int_t ncdraw = 0;
1374  Int_t ilevel, nelem;
1375  for (ilevel=nlevel;ilevel>=0;ilevel--) {
1376  cl = clevel[ilevel];
1377  nelem = cl->GetNdata() + cl->GetNmethods();
1378  if (nelem > maxelem) maxelem = nelem;
1379  nc = (nelem/50) + 1;
1380  ncdraw += nc;
1381  }
1382 
1383  Double_t tsizcm = 0.40;
1384  Double_t x1 = 0.25;
1385  Double_t x2 = 0;
1386  Double_t dx = 3.5;
1387  if (ncdraw > 4) {
1388  dx = dx - 0.42*Double_t(ncdraw-5);
1389  if (dx < 1.3) dx = 1.3;
1390  tsizcm = tsizcm - 0.03*Double_t(ncdraw-5);
1391  if (tsizcm < 0.27) tsizcm = 0.27;
1392  }
1393  Double_t tsiz = 1.2*tsizcm/ypad;
1394 
1395  // Now loop on levels
1396  for (ilevel=nlevel;ilevel>=0;ilevel--) {
1397  cl = clevel[ilevel];
1398  nelem = cl->GetNdata() + cl->GetNmethods();
1399  if (nelem > maxelem) maxelem = nelem;
1400  nc = (nelem/50) + 1;
1401  dy = 0.45;
1402  if (ilevel < nlevel) x1 = x2 + 0.5;
1403  x2 = x1 + nc*dx;
1404  v2 = ypad - 0.5;
1405  lbase = cl->GetListOfBases();
1406  cinherit = 0;
1407  if (lbase) cinherit = (TBaseClass*)lbase->First();
1408 
1409  do {
1410  nd = cl->GetNdata();
1411  nf = cl->GetNmethods() - 2; //do not show default constructor and destructor
1412  if (cl->GetListOfMethods()->FindObject("Dictionary")) {
1413  nf -= 6; // do not count the Dictionary/ClassDef functions
1414  }
1415  nkf= nf/nc +1;
1416  nkd= nd/nc +1;
1417  if (nd == 0) nkd=0;
1418  if (nf == 0) nkf=0;
1419  y1 = v2 - 0.7;
1420  v1 = y1 - Double_t(nkf+nkd+nc-1)*dy;
1421  dv = v2 - v1;
1422 
1423  // Create a new PaveText
1424  pt = new TPaveText(x1,v1,x2,v2);
1425  pt->SetBit(kCanDelete);
1426  pt->SetFillColor(19);
1427  pt->Draw();
1428  pt->SetTextColor(4);
1429  pt->SetTextFont(61);
1430  pt->SetTextAlign(12);
1431  pt->SetTextSize(tsiz);
1432  TBox *box = pt->AddBox(0,(y1+0.01-v1)/dv,0,(v2-0.01-v1)/dv);
1433  if (box) box->SetFillColor(17);
1434  pt->AddLine(0,(y1-v1)/dv,0,(y1-v1)/dv);
1435  TText *title = pt->AddText(0.5,(0.5*(y1+v2)-v1)/dv,(char*)cl->GetName());
1436  title->SetTextAlign(22);
1437  title->SetTextSize(0.6*(v2-y1)/ypad);
1438 
1439  // Draw data Members
1440  i = 0;
1441  x = 0.03;
1442  y = y1 + 0.5*dy;
1443  TDataMember *d;
1444  TIter nextd(cl->GetListOfDataMembers());
1445  while ((d = (TDataMember *) nextd())) {
1446  if (i >= nkd) { i = 1; y = y1 - 0.5*dy; x += 1/Double_t(nc); }
1447  else { i++; y -= dy; }
1448 
1449  // Take in account the room the array index will occupy
1450 
1451  Int_t dim = d->GetArrayDim();
1452  Int_t indx = 0;
1453  snprintf(dname,256,"%s",d->GetName());
1454  Int_t ldname = 0;
1455  while (indx < dim ){
1456  ldname = strlen(dname);
1457  snprintf(&dname[ldname],256-ldname,"[%d]",d->GetMaxIndex(indx));
1458  indx++;
1459  }
1460  pt->AddText(x,(y-v1)/dv,dname);
1461  }
1462 
1463  // Draw a separator line
1464  Double_t ysep;
1465  if (nd) {
1466  ysep = y1 - Double_t(nkd)*dy;
1467  pt->AddLine(0,(ysep-v1)/dv,0,(ysep-v1)/dv);
1468  ysep -= 0.5*dy;
1469  } else ysep = y1;
1470 
1471  // Draw Member Functions
1472  Int_t fcount = 0;
1473  i = 0;
1474  x = 0.03;
1475  y = ysep + 0.5*dy;
1476  TMethod *m;
1477  TIter nextm(cl->GetListOfMethods());
1478  while ((m = (TMethod *) nextm())) {
1479  if (
1480  !strcmp( m->GetName(), "Dictionary" ) ||
1481  !strcmp( m->GetName(), "Class_Version" ) ||
1482  !strcmp( m->GetName(), "DeclFileName" ) ||
1483  !strcmp( m->GetName(), "DeclFileLine" ) ||
1484  !strcmp( m->GetName(), "ImplFileName" ) ||
1485  !strcmp( m->GetName(), "ImplFileLine" )
1486  ) continue;
1487  fcount++;
1488  if (fcount > nf) break;
1489  if (i >= nkf) { i = 1; y = ysep - 0.5*dy; x += 1/Double_t(nc); }
1490  else { i++; y -= dy; }
1491 
1492  ptext = pt->AddText(x,(y-v1)/dv,m->GetName());
1493  // Check if method is overloaded in a derived class
1494  // If yes, Change the color of the text to blue
1495  for (j=ilevel-1;j>=0;j--) {
1496  if (cl == clevel[ilevel]) {
1497  if (clevel[j]->GetMethodAny((char*)m->GetName())) {
1498  ptext->SetTextColor(15);
1499  break;
1500  }
1501  }
1502  }
1503  }
1504 
1505  // Draw second inheritance classes for this class
1506  cll = 0;
1507  if (cinherit) {
1508  cinherit = (TBaseClass*)lbase->After(cinherit);
1509  if (cinherit) {
1510  cl = cinherit->GetClassPointer();
1511  cll = cl;
1512  v2 = v1 -0.4;
1513  dy = 0.35;
1514  }
1515  }
1516  } while (cll);
1517  }
1518  Update();
1519 }
1520 
1521 ////////////////////////////////////////////////////////////////////////////////
1522 /// Function called to draw a crosshair in the canvas
1523 ///
1524 /// Example:
1525 /// ~~~ {.cpp}
1526 /// Root > TFile f("hsimple.root");
1527 /// Root > hpxpy.Draw();
1528 /// Root > c1.SetCrosshair();
1529 /// ~~~
1530 /// When moving the mouse in the canvas, a crosshair is drawn
1531 ///
1532 /// - if the canvas fCrosshair = 1 , the crosshair spans the full canvas
1533 /// - if the canvas fCrosshair > 1 , the crosshair spans only the pad
1534 
1536 {
1537  if (gPad->GetEvent() == kMouseEnter) return;
1538 
1539  TPad *cpad = (TPad*)gPad;
1540  TCanvas *canvas = cpad->GetCanvas();
1541  canvas->FeedbackMode(kTRUE);
1542 
1543  //erase old position and draw a line at current position
1544  Int_t pxmin,pxmax,pymin,pymax,pxold,pyold,px,py;
1545  pxold = fCrosshairPos%10000;
1546  pyold = fCrosshairPos/10000;
1547  px = cpad->GetEventX();
1548  py = cpad->GetEventY()+1;
1549  if (canvas->GetCrosshair() > 1) { //crosshair only in the current pad
1550  pxmin = cpad->XtoAbsPixel(fX1);
1551  pxmax = cpad->XtoAbsPixel(fX2);
1552  pymin = cpad->YtoAbsPixel(fY1);
1553  pymax = cpad->YtoAbsPixel(fY2);
1554  } else { //default; crosshair spans the full canvas
1555  pxmin = 0;
1556  pxmax = canvas->GetWw();
1557  pymin = 0;
1558  pymax = cpad->GetWh();
1559  }
1560 #ifndef R__HAS_COCOA
1561  // Not needed, no XOR with Cocoa.
1562  if(pxold) gVirtualX->DrawLine(pxold,pymin,pxold,pymax);
1563  if(pyold) gVirtualX->DrawLine(pxmin,pyold,pxmax,pyold);
1564 #endif // R__HAS_COCOA
1565  if (cpad->GetEvent() == kButton1Down ||
1566  cpad->GetEvent() == kButton1Up ||
1567  cpad->GetEvent() == kMouseLeave) {
1568  fCrosshairPos = 0;
1569  return;
1570  }
1571  gVirtualX->DrawLine(px,pymin,px,pymax);
1572  gVirtualX->DrawLine(pxmin,py,pxmax,py);
1573  fCrosshairPos = px + 10000*py;
1574 }
1575 
1576 ////////////////////////////////////////////////////////////////////////////////
1577 /// Draw an empty pad frame with X and Y axis.
1578 ///
1579 /// \return The pointer to the histogram used to draw the frame.
1580 ///
1581 /// \param[in] xmin X axis lower limit
1582 /// \param[in] xmax X axis upper limit
1583 /// \param[in] ymin Y axis lower limit
1584 /// \param[in] ymax Y axis upper limit
1585 /// \param[in] title Pad title.If title is of the form "stringt;stringx;stringy"
1586 /// the pad title is set to stringt, the x axis title to
1587 /// stringx, the y axis title to stringy.
1588 ///
1589 /// #### Example:
1590 ///
1591 /// Begin_Macro(source)
1592 /// {
1593 /// auto c = new TCanvas("c","c",200,10,500,300);
1594 ///
1595 /// const Int_t n = 50;
1596 /// auto g = new TGraph();
1597 /// for (Int_t i=0;i<n;i++) g->SetPoint(i,i*0.1,100*sin(i*0.1+0.2));
1598 ///
1599 /// auto frame = c->DrawFrame(0, -110, 2, 110);
1600 /// frame->GetXaxis()->SetTitle("X axis");
1601 ///
1602 /// g->Draw("L*");
1603 /// }
1604 /// End_Macro
1605 
1607 {
1608  if (!IsEditable()) return 0;
1609  TPad *padsav = (TPad*)gPad;
1610  if (this != padsav) {
1611  Warning("DrawFrame","Must be called for the current pad only");
1612  return padsav->DrawFrame(xmin,ymin,xmax,ymax,title);
1613  }
1614 
1615  cd();
1616 
1617  TH1F *hframe = (TH1F*)FindObject("hframe");
1618  if (hframe) delete hframe;
1619  Int_t nbins = 1000;
1620  //if log scale in X, use variable bin size linear with log(x)
1621  //this gives a better precision when zooming on the axis
1622  if (fLogx && xmin > 0 && xmax > xmin) {
1623  Double_t xminl = TMath::Log(xmin);
1624  Double_t xmaxl = TMath::Log(xmax);
1625  Double_t dx = (xmaxl-xminl)/nbins;
1626  Double_t *xbins = new Double_t[nbins+1];
1627  xbins[0] = xmin;
1628  for (Int_t i=1;i<=nbins;i++) {
1629  xbins[i] = TMath::Exp(xminl+i*dx);
1630  }
1631  hframe = new TH1F("hframe",title,nbins,xbins);
1632  delete [] xbins;
1633  } else {
1634  hframe = new TH1F("hframe",title,nbins,xmin,xmax);
1635  }
1636  hframe->SetBit(TH1::kNoStats);
1637  hframe->SetBit(kCanDelete);
1638  hframe->SetMinimum(ymin);
1639  hframe->SetMaximum(ymax);
1640  hframe->GetYaxis()->SetLimits(ymin,ymax);
1641  hframe->SetDirectory(0);
1642  hframe->Draw(" ");
1643  Update();
1644  if (padsav) padsav->cd();
1645  return hframe;
1646 }
1647 
1648 ////////////////////////////////////////////////////////////////////////////////
1649 /// Static function to Display Color Table in a pad.
1650 
1652 {
1653  Int_t i, j;
1654  Int_t color;
1655  Double_t xlow, ylow, xup, yup, hs, ws;
1656  Double_t x1, y1, x2, y2;
1657  x1 = y1 = 0;
1658  x2 = y2 = 20;
1659 
1660  gPad->SetFillColor(0);
1661  gPad->Clear();
1662  gPad->Range(x1,y1,x2,y2);
1663 
1664  TText *text = new TText(0,0,"");
1665  text->SetTextFont(61);
1666  text->SetTextSize(0.07);
1667  text->SetTextAlign(22);
1668 
1669  TBox *box = new TBox();
1670 
1671  // Draw color table boxes.
1672  hs = (y2-y1)/Double_t(5);
1673  ws = (x2-x1)/Double_t(10);
1674  for (i=0;i<10;i++) {
1675  xlow = x1 + ws*(Double_t(i)+0.1);
1676  xup = x1 + ws*(Double_t(i)+0.9);
1677  for (j=0;j<5;j++) {
1678  ylow = y1 + hs*(Double_t(j)+0.1);
1679  yup = y1 + hs*(Double_t(j)+0.9);
1680  color = 10*j + i;
1681  box->SetFillStyle(1001);
1682  box->SetFillColor(color);
1683  box->DrawBox(xlow, ylow, xup, yup);
1684  box->SetFillStyle(0);
1685  box->SetLineColor(1);
1686  box->DrawBox(xlow, ylow, xup, yup);
1687  if (color == 1) text->SetTextColor(0);
1688  else text->SetTextColor(1);
1689  text->DrawText(0.5*(xlow+xup), 0.5*(ylow+yup), Form("%d",color));
1690  }
1691  }
1692 }
1693 
1694 ////////////////////////////////////////////////////////////////////////////////
1695 /// Execute action corresponding to one event.
1696 ///
1697 /// This member function is called when a TPad object is clicked.
1698 ///
1699 /// If the mouse is clicked in one of the 4 corners of the pad (pA,pB,pC,pD)
1700 /// the pad is resized with the rubber rectangle.
1701 ///
1702 /// If the mouse is clicked inside the pad, the pad is moved.
1703 ///
1704 /// If the mouse is clicked on the 4 edges (pL,pR,pTop,pBot), the pad is scaled
1705 /// parallel to this edge.
1706 ///
1707 /// \image html gpad_pad4.png
1708 ///
1709 /// Note that this function duplicates on purpose the functionality
1710 /// already implemented in TBox::ExecuteEvent.
1711 /// If somebody modifies this function, may be similar changes should also
1712 /// be applied to TBox::ExecuteEvent.
1713 
1715 {
1716  const Int_t kMaxDiff = 5;
1717  const Int_t kMinSize = 20;
1718  static Int_t pxorg, pyorg;
1719  static Int_t px1, px2, py1, py2, pxl, pyl, pxt, pyt, pxold, pyold;
1720  static Int_t px1p, px2p, py1p, py2p, pxlp, pylp, pxtp, pytp;
1721  static Bool_t pA, pB, pC, pD, pTop, pL, pR, pBot, pINSIDE;
1722  Int_t wx, wy;
1723  Bool_t opaque = OpaqueMoving();
1724  Bool_t ropaque = OpaqueResizing();
1725  Bool_t fixedr = HasFixedAspectRatio();
1726 
1727  if (!IsEditable() && event != kMouseEnter) return;
1728  TVirtualPad *parent = GetMother();
1729  if (!parent->IsEditable()) return;
1730 
1731  HideToolTip(event);
1732 
1733  if (fXlowNDC < 0 && event != kButton1Down) return;
1734  if (fYlowNDC < 0 && event != kButton1Down) return;
1735 
1736  // keep old mouse position
1737  if (event == kButton1Down) {
1738  pxorg = px;
1739  pyorg = py;
1740  }
1741 
1742  Int_t newcode = gROOT->GetEditorMode();
1743  if (newcode)
1744  pA = pB = pC = pD = pTop = pL = pR = pBot = pINSIDE = kFALSE;
1745  switch (newcode) {
1746  case kPad:
1747  TCreatePrimitives::Pad(event,px,py,0);
1748  break;
1749  case kMarker:
1750  case kText:
1751  TCreatePrimitives::Text(event,px,py,newcode);
1752  break;
1753  case kLine:
1754  TCreatePrimitives::Line(event,px,py,kLine);
1755  break;
1756  case kArrow:
1757  TCreatePrimitives::Line(event,px,py,kArrow);
1758  break;
1759  case kCurlyLine:
1760  TCreatePrimitives::Line(event,px,py,kCurlyLine);
1761  break;
1762  case kCurlyArc:
1763  TCreatePrimitives::Line(event,px,py,kCurlyArc);
1764  break;
1765  case kPolyLine:
1767  break;
1768  case kCutG:
1769  TCreatePrimitives::PolyLine(event,px,py,kCutG);
1770  break;
1771  case kArc:
1772  TCreatePrimitives::Ellipse(event,px,py,kArc);
1773  break;
1774  case kEllipse:
1775  TCreatePrimitives::Ellipse(event,px,py,kEllipse);
1776  break;
1777  case kButton:
1778  case kPave:
1779  case kPaveLabel:
1780  case kPaveText:
1781  case kPavesText:
1782  case kDiamond:
1783  TCreatePrimitives::Pave(event,px,py,newcode);
1784  return;
1785  default:
1786  break;
1787  }
1788  if (newcode) return;
1789 
1790  switch (event) {
1791 
1792  case kMouseEnter:
1793  if (fTip)
1794  ResetToolTip(fTip);
1795  break;
1796 
1797  case kArrowKeyPress:
1798  case kButton1Down:
1799 
1800  fXUpNDC = fXlowNDC + fWNDC;
1801  fYUpNDC = fYlowNDC + fHNDC;
1802 
1803  GetPainter()->SetLineColor(-1);
1804  TAttLine::Modify(); //Change line attributes only if necessary
1805  if (GetFillColor())
1807  else
1808  GetPainter()->SetLineColor(1);
1809  GetPainter()->SetLineWidth(2);
1810 
1811  // No break !!!
1812 
1813  case kMouseMotion:
1814 
1815  px1 = XtoAbsPixel(fX1);
1816  py1 = YtoAbsPixel(fY1);
1817  px2 = XtoAbsPixel(fX2);
1818  py2 = YtoAbsPixel(fY2);
1819 
1820  if (px1 < px2) {
1821  pxl = px1;
1822  pxt = px2;
1823  } else {
1824  pxl = px2;
1825  pxt = px1;
1826  }
1827  if (py1 < py2) {
1828  pyl = py1;
1829  pyt = py2;
1830  } else {
1831  pyl = py2;
1832  pyt = py1;
1833  }
1834 
1835  px1p = parent->XtoAbsPixel(parent->GetX1()) + parent->GetBorderSize();
1836  py1p = parent->YtoAbsPixel(parent->GetY1()) - parent->GetBorderSize();
1837  px2p = parent->XtoAbsPixel(parent->GetX2()) - parent->GetBorderSize();
1838  py2p = parent->YtoAbsPixel(parent->GetY2()) + parent->GetBorderSize();
1839 
1840  if (px1p < px2p) {
1841  pxlp = px1p;
1842  pxtp = px2p;
1843  } else {
1844  pxlp = px2p;
1845  pxtp = px1p;
1846  }
1847  if (py1p < py2p) {
1848  pylp = py1p;
1849  pytp = py2p;
1850  } else {
1851  pylp = py2p;
1852  pytp = py1p;
1853  }
1854 
1855  pA = pB = pC = pD = pTop = pL = pR = pBot = pINSIDE = kFALSE;
1856 
1857  // case pA
1858  if (TMath::Abs(px - pxl) <= kMaxDiff && TMath::Abs(py - pyl) <= kMaxDiff) {
1859  pxold = pxl; pyold = pyl; pA = kTRUE;
1861  }
1862  // case pB
1863  if (TMath::Abs(px - pxt) <= kMaxDiff && TMath::Abs(py - pyl) <= kMaxDiff) {
1864  pxold = pxt; pyold = pyl; pB = kTRUE;
1866  }
1867  // case pC
1868  if (TMath::Abs(px - pxt) <= kMaxDiff && TMath::Abs(py - pyt) <= kMaxDiff) {
1869  pxold = pxt; pyold = pyt; pC = kTRUE;
1871  }
1872  // case pD
1873  if (TMath::Abs(px - pxl) <= kMaxDiff && TMath::Abs(py - pyt) <= kMaxDiff) {
1874  pxold = pxl; pyold = pyt; pD = kTRUE;
1876  }
1877 
1878  if ((px > pxl+kMaxDiff && px < pxt-kMaxDiff) &&
1879  TMath::Abs(py - pyl) < kMaxDiff) { // top edge
1880  pxold = pxl; pyold = pyl; pTop = kTRUE;
1882  }
1883 
1884  if ((px > pxl+kMaxDiff && px < pxt-kMaxDiff) &&
1885  TMath::Abs(py - pyt) < kMaxDiff) { // bottom edge
1886  pxold = pxt; pyold = pyt; pBot = kTRUE;
1888  }
1889 
1890  if ((py > pyl+kMaxDiff && py < pyt-kMaxDiff) &&
1891  TMath::Abs(px - pxl) < kMaxDiff) { // left edge
1892  pxold = pxl; pyold = pyl; pL = kTRUE;
1894  }
1895 
1896  if ((py > pyl+kMaxDiff && py < pyt-kMaxDiff) &&
1897  TMath::Abs(px - pxt) < kMaxDiff) { // right edge
1898  pxold = pxt; pyold = pyt; pR = kTRUE;
1900  }
1901 
1902  if ((px > pxl+kMaxDiff && px < pxt-kMaxDiff) &&
1903  (py > pyl+kMaxDiff && py < pyt-kMaxDiff)) { // inside box
1904  pxold = px; pyold = py; pINSIDE = kTRUE;
1905  if (event == kButton1Down)
1906  SetCursor(kMove);
1907  else
1908  SetCursor(kCross);
1909  }
1910 
1911  fResizing = kFALSE;
1912  if (pA || pB || pC || pD || pTop || pL || pR || pBot)
1913  fResizing = kTRUE;
1914 
1915  if (!pA && !pB && !pC && !pD && !pTop && !pL && !pR && !pBot && !pINSIDE)
1916  SetCursor(kCross);
1917 
1918  break;
1919 
1920  case kArrowKeyRelease:
1921  case kButton1Motion:
1922 
1923  if (TestBit(kCannotMove)) break;
1924  wx = wy = 0;
1925 
1926  if (pA) {
1927  if (!ropaque) gVirtualX->DrawBox(pxold, pyt, pxt, pyold, TVirtualX::kHollow);
1928  if (px > pxt-kMinSize) { px = pxt-kMinSize; wx = px; }
1929  if (py > pyt-kMinSize) { py = pyt-kMinSize; wy = py; }
1930  if (px < pxlp) { px = pxlp; wx = px; }
1931  if (py < pylp) { py = pylp; wy = py; }
1932  if (fixedr) {
1933  Double_t dy = Double_t(TMath::Abs(pxt-px))/parent->UtoPixel(1.) /
1934  fAspectRatio;
1935  Int_t npy2 = pyt - TMath::Abs(parent->VtoAbsPixel(dy) -
1936  parent->VtoAbsPixel(0));
1937  if (npy2 < pylp) {
1938  px = pxold;
1939  py = pyold;
1940  } else
1941  py = npy2;
1942 
1943  wx = wy = 0;
1944  }
1945  if (!ropaque) gVirtualX->DrawBox(px, pyt, pxt, py, TVirtualX::kHollow);
1946  }
1947  if (pB) {
1948  if (!ropaque) gVirtualX->DrawBox(pxl , pyt, pxold, pyold, TVirtualX::kHollow);
1949  if (px < pxl+kMinSize) { px = pxl+kMinSize; wx = px; }
1950  if (py > pyt-kMinSize) { py = pyt-kMinSize; wy = py; }
1951  if (px > pxtp) { px = pxtp; wx = px; }
1952  if (py < pylp) { py = pylp; wy = py; }
1953  if (fixedr) {
1954  Double_t dy = Double_t(TMath::Abs(pxl-px))/parent->UtoPixel(1.) /
1955  fAspectRatio;
1956  Int_t npy2 = pyt - TMath::Abs(parent->VtoAbsPixel(dy) -
1957  parent->VtoAbsPixel(0));
1958  if (npy2 < pylp) {
1959  px = pxold;
1960  py = pyold;
1961  } else
1962  py = npy2;
1963 
1964  wx = wy = 0;
1965  }
1966  if (!ropaque) gVirtualX->DrawBox(pxl , pyt, px , py, TVirtualX::kHollow);
1967  }
1968  if (pC) {
1969  if (!ropaque) gVirtualX->DrawBox(pxl , pyl, pxold, pyold, TVirtualX::kHollow);
1970  if (px < pxl+kMinSize) { px = pxl+kMinSize; wx = px; }
1971  if (py < pyl+kMinSize) { py = pyl+kMinSize; wy = py; }
1972  if (px > pxtp) { px = pxtp; wx = px; }
1973  if (py > pytp) { py = pytp; wy = py; }
1974  if (fixedr) {
1975  Double_t dy = Double_t(TMath::Abs(pxl-px))/parent->UtoPixel(1.) /
1976  fAspectRatio;
1977  Int_t npy2 = pyl + TMath::Abs(parent->VtoAbsPixel(dy) -
1978  parent->VtoAbsPixel(0));
1979  if (npy2 > pytp) {
1980  px = pxold;
1981  py = pyold;
1982  } else
1983  py = npy2;
1984 
1985  wx = wy = 0;
1986  }
1987  if (!ropaque) gVirtualX->DrawBox(pxl, pyl, px, py, TVirtualX::kHollow);
1988  }
1989  if (pD) {
1990  if (!ropaque) gVirtualX->DrawBox(pxold, pyold, pxt, pyl, TVirtualX::kHollow);
1991  if (px > pxt-kMinSize) { px = pxt-kMinSize; wx = px; }
1992  if (py < pyl+kMinSize) { py = pyl+kMinSize; wy = py; }
1993  if (px < pxlp) { px = pxlp; wx = px; }
1994  if (py > pytp) { py = pytp; wy = py; }
1995  if (fixedr) {
1996  Double_t dy = Double_t(TMath::Abs(pxt-px))/parent->UtoPixel(1.) /
1997  fAspectRatio;
1998  Int_t npy2 = pyl + TMath::Abs(parent->VtoAbsPixel(dy) -
1999  parent->VtoAbsPixel(0));
2000  if (npy2 > pytp) {
2001  px = pxold;
2002  py = pyold;
2003  } else
2004  py = npy2;
2005 
2006  wx = wy = 0;
2007  }
2008  if (!ropaque) gVirtualX->DrawBox(px, py, pxt, pyl, TVirtualX::kHollow);
2009  }
2010  if (pTop) {
2011  if (!ropaque) gVirtualX->DrawBox(px1, py1, px2, py2, TVirtualX::kHollow);
2012  py2 += py - pyold;
2013  if (py2 > py1-kMinSize) { py2 = py1-kMinSize; wy = py2; }
2014  if (py2 < py2p) { py2 = py2p; wy = py2; }
2015  if (fixedr) {
2016  Double_t dx = Double_t(TMath::Abs(py2-py1))/parent->VtoPixel(0) *
2017  fAspectRatio;
2018  Int_t npx2 = px1 + parent->UtoPixel(dx);
2019  if (npx2 > px2p)
2020  py2 -= py - pyold;
2021  else
2022  px2 = npx2;
2023  }
2024  if (!ropaque) gVirtualX->DrawBox(px1, py1, px2, py2, TVirtualX::kHollow);
2025  }
2026  if (pBot) {
2027  if (!ropaque) gVirtualX->DrawBox(px1, py1, px2, py2, TVirtualX::kHollow);
2028  py1 += py - pyold;
2029  if (py1 < py2+kMinSize) { py1 = py2+kMinSize; wy = py1; }
2030  if (py1 > py1p) { py1 = py1p; wy = py1; }
2031  if (fixedr) {
2032  Double_t dx = Double_t(TMath::Abs(py2-py1))/parent->VtoPixel(0) *
2033  fAspectRatio;
2034  Int_t npx2 = px1 + parent->UtoPixel(dx);
2035  if (npx2 > px2p)
2036  py1 -= py - pyold;
2037  else
2038  px2 = npx2;
2039  }
2040  if (!ropaque) gVirtualX->DrawBox(px1, py1, px2, py2, TVirtualX::kHollow);
2041  }
2042  if (pL) {
2043  if (!ropaque) gVirtualX->DrawBox(px1, py1, px2, py2, TVirtualX::kHollow);
2044  px1 += px - pxold;
2045  if (px1 > px2-kMinSize) { px1 = px2-kMinSize; wx = px1; }
2046  if (px1 < px1p) { px1 = px1p; wx = px1; }
2047  if (fixedr) {
2048  Double_t dy = Double_t(TMath::Abs(px2-px1))/parent->UtoPixel(1.) /
2049  fAspectRatio;
2050  Int_t npy2 = py1 - TMath::Abs(parent->VtoAbsPixel(dy) -
2051  parent->VtoAbsPixel(0));
2052  if (npy2 < py2p)
2053  px1 -= px - pxold;
2054  else
2055  py2 = npy2;
2056  }
2057  if (!ropaque) gVirtualX->DrawBox(px1, py1, px2, py2, TVirtualX::kHollow);
2058  }
2059  if (pR) {
2060  if (!ropaque) gVirtualX->DrawBox(px1, py1, px2, py2, TVirtualX::kHollow);
2061  px2 += px - pxold;
2062  if (px2 < px1+kMinSize) { px2 = px1+kMinSize; wx = px2; }
2063  if (px2 > px2p) { px2 = px2p; wx = px2; }
2064  if (fixedr) {
2065  Double_t dy = Double_t(TMath::Abs(px2-px1))/parent->UtoPixel(1.) /
2066  fAspectRatio;
2067  Int_t npy2 = py1 - TMath::Abs(parent->VtoAbsPixel(dy) -
2068  parent->VtoAbsPixel(0));
2069  if (npy2 < py2p)
2070  px2 -= px - pxold;
2071  else
2072  py2 = npy2;
2073  }
2074  if (!ropaque) gVirtualX->DrawBox(px1, py1, px2, py2, TVirtualX::kHollow);
2075  }
2076  if (pINSIDE) {
2077  if (!opaque) gVirtualX->DrawBox(px1, py1, px2, py2, TVirtualX::kHollow); // draw the old box
2078  Int_t dx = px - pxold;
2079  Int_t dy = py - pyold;
2080  px1 += dx; py1 += dy; px2 += dx; py2 += dy;
2081  if (px1 < px1p) { dx = px1p - px1; px1 += dx; px2 += dx; wx = px+dx; }
2082  if (px2 > px2p) { dx = px2 - px2p; px1 -= dx; px2 -= dx; wx = px-dx; }
2083  if (py1 > py1p) { dy = py1 - py1p; py1 -= dy; py2 -= dy; wy = py-dy; }
2084  if (py2 < py2p) { dy = py2p - py2; py1 += dy; py2 += dy; wy = py+dy; }
2085  if (!opaque) gVirtualX->DrawBox(px1, py1, px2, py2, TVirtualX::kHollow); // draw the new box
2086  }
2087 
2088  if (wx || wy) {
2089  if (wx) px = wx;
2090  if (wy) py = wy;
2091  gVirtualX->Warp(px, py);
2092  }
2093 
2094  pxold = px;
2095  pyold = py;
2096 
2097  Double_t x1, y1, x2, y2;
2098  x1 = x2 = y1 = y2 = 0;
2099 
2100  if ((!fResizing && opaque) || (fResizing && ropaque)) {
2101  if (pA) {
2102  x1 = AbsPixeltoX(pxold);
2103  y1 = AbsPixeltoY(pyt);
2104  x2 = AbsPixeltoX(pxt);
2105  y2 = AbsPixeltoY(pyold);
2106  }
2107  if (pB) {
2108  x1 = AbsPixeltoX(pxl);
2109  y1 = AbsPixeltoY(pyt);
2110  x2 = AbsPixeltoX(pxold);
2111  y2 = AbsPixeltoY(pyold);
2112  }
2113  if (pC) {
2114  x1 = AbsPixeltoX(pxl);
2115  y1 = AbsPixeltoY(pyold);
2116  x2 = AbsPixeltoX(pxold);
2117  y2 = AbsPixeltoY(pyl);
2118  }
2119  if (pD) {
2120  x1 = AbsPixeltoX(pxold);
2121  y1 = AbsPixeltoY(pyold);
2122  x2 = AbsPixeltoX(pxt);
2123  y2 = AbsPixeltoY(pyl);
2124  }
2125  if (pTop || pBot || pL || pR || pINSIDE) {
2126  x1 = AbsPixeltoX(px1);
2127  y1 = AbsPixeltoY(py1);
2128  x2 = AbsPixeltoX(px2);
2129  y2 = AbsPixeltoY(py2);
2130  }
2131 
2132  if (px != pxorg || py != pyorg) {
2133 
2134  // Get parent corners pixels coordinates
2135  Int_t parentpx1 = fMother->XtoAbsPixel(parent->GetX1());
2136  Int_t parentpx2 = fMother->XtoAbsPixel(parent->GetX2());
2137  Int_t parentpy1 = fMother->YtoAbsPixel(parent->GetY1());
2138  Int_t parentpy2 = fMother->YtoAbsPixel(parent->GetY2());
2139 
2140  // Get pad new corners pixels coordinates
2141  Int_t apx1 = XtoAbsPixel(x1); if (apx1 < parentpx1) {apx1 = parentpx1; }
2142  Int_t apx2 = XtoAbsPixel(x2); if (apx2 > parentpx2) {apx2 = parentpx2; }
2143  Int_t apy1 = YtoAbsPixel(y1); if (apy1 > parentpy1) {apy1 = parentpy1; }
2144  Int_t apy2 = YtoAbsPixel(y2); if (apy2 < parentpy2) {apy2 = parentpy2; }
2145 
2146  // Compute new pad positions in the NDC space of parent
2147  fXlowNDC = Double_t(apx1 - parentpx1)/Double_t(parentpx2 - parentpx1);
2148  fYlowNDC = Double_t(apy1 - parentpy1)/Double_t(parentpy2 - parentpy1);
2149  fWNDC = Double_t(apx2 - apx1)/Double_t(parentpx2 - parentpx1);
2150  fHNDC = Double_t(apy2 - apy1)/Double_t(parentpy2 - parentpy1);
2151  }
2152 
2153  // Reset pad parameters and recompute conversion coefficients
2154  ResizePad();
2155 
2156  if (pINSIDE) gPad->ShowGuidelines(this, event);
2157  if (pTop) gPad->ShowGuidelines(this, event, 't', true);
2158  if (pBot) gPad->ShowGuidelines(this, event, 'b', true);
2159  if (pL) gPad->ShowGuidelines(this, event, 'l', true);
2160  if (pR) gPad->ShowGuidelines(this, event, 'r', true);
2161  if (pA) gPad->ShowGuidelines(this, event, '1', true);
2162  if (pB) gPad->ShowGuidelines(this, event, '2', true);
2163  if (pC) gPad->ShowGuidelines(this, event, '3', true);
2164  if (pD) gPad->ShowGuidelines(this, event, '4', true);
2165 
2166  Modified(kTRUE);
2167  }
2168 
2169  break;
2170 
2171  case kButton1Up:
2172 
2173  if (gROOT->IsEscaped()) {
2174  gROOT->SetEscape(kFALSE);
2175  break;
2176  }
2177 
2178  if (opaque||ropaque) {
2179  ShowGuidelines(this, event);
2180  } else {
2181  x1 = x2 = y1 = y2 = 0;
2182 
2183  if (pA) {
2184  x1 = AbsPixeltoX(pxold);
2185  y1 = AbsPixeltoY(pyt);
2186  x2 = AbsPixeltoX(pxt);
2187  y2 = AbsPixeltoY(pyold);
2188  }
2189  if (pB) {
2190  x1 = AbsPixeltoX(pxl);
2191  y1 = AbsPixeltoY(pyt);
2192  x2 = AbsPixeltoX(pxold);
2193  y2 = AbsPixeltoY(pyold);
2194  }
2195  if (pC) {
2196  x1 = AbsPixeltoX(pxl);
2197  y1 = AbsPixeltoY(pyold);
2198  x2 = AbsPixeltoX(pxold);
2199  y2 = AbsPixeltoY(pyl);
2200  }
2201  if (pD) {
2202  x1 = AbsPixeltoX(pxold);
2203  y1 = AbsPixeltoY(pyold);
2204  x2 = AbsPixeltoX(pxt);
2205  y2 = AbsPixeltoY(pyl);
2206  }
2207  if (pTop || pBot || pL || pR || pINSIDE) {
2208  x1 = AbsPixeltoX(px1);
2209  y1 = AbsPixeltoY(py1);
2210  x2 = AbsPixeltoX(px2);
2211  y2 = AbsPixeltoY(py2);
2212  }
2213 
2214  if (pA || pB || pC || pD || pTop || pL || pR || pBot)
2215  Modified(kTRUE);
2216 
2217  gVirtualX->SetLineColor(-1);
2218  gVirtualX->SetLineWidth(-1);
2219 
2220  if (px != pxorg || py != pyorg) {
2221 
2222  // Get parent corners pixels coordinates
2223  Int_t parentpx1 = fMother->XtoAbsPixel(parent->GetX1());
2224  Int_t parentpx2 = fMother->XtoAbsPixel(parent->GetX2());
2225  Int_t parentpy1 = fMother->YtoAbsPixel(parent->GetY1());
2226  Int_t parentpy2 = fMother->YtoAbsPixel(parent->GetY2());
2227 
2228  // Get pad new corners pixels coordinates
2229  Int_t apx1 = XtoAbsPixel(x1); if (apx1 < parentpx1) {apx1 = parentpx1; }
2230  Int_t apx2 = XtoAbsPixel(x2); if (apx2 > parentpx2) {apx2 = parentpx2; }
2231  Int_t apy1 = YtoAbsPixel(y1); if (apy1 > parentpy1) {apy1 = parentpy1; }
2232  Int_t apy2 = YtoAbsPixel(y2); if (apy2 < parentpy2) {apy2 = parentpy2; }
2233 
2234  // Compute new pad positions in the NDC space of parent
2235  fXlowNDC = Double_t(apx1 - parentpx1)/Double_t(parentpx2 - parentpx1);
2236  fYlowNDC = Double_t(apy1 - parentpy1)/Double_t(parentpy2 - parentpy1);
2237  fWNDC = Double_t(apx2 - apx1)/Double_t(parentpx2 - parentpx1);
2238  fHNDC = Double_t(apy2 - apy1)/Double_t(parentpy2 - parentpy1);
2239  }
2240 
2241  // Reset pad parameters and recompute conversion coefficients
2242  ResizePad();
2243 
2244 
2245  // emit signal
2246  RangeChanged();
2247  }
2248 
2249  break;
2250 
2251  case kButton1Locate:
2252 
2253  ExecuteEvent(kButton1Down, px, py);
2254 
2255  while (1) {
2256  px = py = 0;
2257  event = gVirtualX->RequestLocator(1, 1, px, py);
2258 
2259  ExecuteEvent(kButton1Motion, px, py);
2260 
2261  if (event != -1) { // button is released
2262  ExecuteEvent(kButton1Up, px, py);
2263  return;
2264  }
2265  }
2266 
2267  case kButton2Down:
2268 
2269  Pop();
2270  break;
2271 
2272  }
2273 }
2274 
2275 ////////////////////////////////////////////////////////////////////////////////
2276 /// Execute action corresponding to one event for a TAxis object
2277 /// (called by TAxis::ExecuteEvent.)
2278 /// This member function is called when an axis is clicked with the locator
2279 ///
2280 /// The axis range is set between the position where the mouse is pressed
2281 /// and the position where it is released.
2282 ///
2283 /// If the mouse position is outside the current axis range when it is released
2284 /// the axis is unzoomed with the corresponding proportions.
2285 ///
2286 /// Note that the mouse does not need to be in the pad or even canvas
2287 /// when it is released.
2288 
2289 void TPad::ExecuteEventAxis(Int_t event, Int_t px, Int_t py, TAxis *axis)
2290 {
2291  if (!IsEditable()) return;
2292  if (!axis) return;
2293  SetCursor(kHand);
2294 
2295  TView *view = GetView();
2296  static Int_t axisNumber;
2297  static Double_t ratio1, ratio2;
2298  static Int_t px1old, py1old, px2old, py2old;
2299  Int_t bin1, bin2, first, last;
2300  Double_t temp, xmin,xmax;
2301  Bool_t opaque = gPad->OpaqueMoving();
2302  static TBox *zoombox;
2303  Double_t zbx1=0,zbx2=0,zby1=0,zby2=0;
2304 
2305  // The CONT4 option, used to paint TH2, is a special case; it uses a 3D
2306  // drawing technique to paint a 2D plot.
2307  TString opt = axis->GetParent()->GetDrawOption();
2308  opt.ToLower();
2309  Bool_t kCont4 = kFALSE;
2310  if (strstr(opt,"cont4")) {
2311  view = 0;
2312  kCont4 = kTRUE;
2313  }
2314 
2315  switch (event) {
2316 
2317  case kButton1Down:
2318  axisNumber = 1;
2319  if (!strcmp(axis->GetName(),"xaxis")) {
2320  axisNumber = 1;
2321  if (!IsVertical()) axisNumber = 2;
2322  }
2323  if (!strcmp(axis->GetName(),"yaxis")) {
2324  axisNumber = 2;
2325  if (!IsVertical()) axisNumber = 1;
2326  }
2327  if (!strcmp(axis->GetName(),"zaxis")) {
2328  axisNumber = 3;
2329  }
2330  if (view) {
2331  view->GetDistancetoAxis(axisNumber, px, py, ratio1);
2332  } else {
2333  if (axisNumber == 1) {
2334  ratio1 = (AbsPixeltoX(px) - GetUxmin())/(GetUxmax() - GetUxmin());
2335  px1old = XtoAbsPixel(GetUxmin()+ratio1*(GetUxmax() - GetUxmin()));
2336  py1old = YtoAbsPixel(GetUymin());
2337  px2old = px1old;
2338  py2old = YtoAbsPixel(GetUymax());
2339  } else if (axisNumber == 2) {
2340  ratio1 = (AbsPixeltoY(py) - GetUymin())/(GetUymax() - GetUymin());
2341  py1old = YtoAbsPixel(GetUymin()+ratio1*(GetUymax() - GetUymin()));
2342  px1old = XtoAbsPixel(GetUxmin());
2343  px2old = XtoAbsPixel(GetUxmax());
2344  py2old = py1old;
2345  } else {
2346  ratio1 = (AbsPixeltoY(py) - GetUymin())/(GetUymax() - GetUymin());
2347  py1old = YtoAbsPixel(GetUymin()+ratio1*(GetUymax() - GetUymin()));
2348  px1old = XtoAbsPixel(GetUxmax());
2349  px2old = XtoAbsPixel(GetX2());
2350  py2old = py1old;
2351  }
2352  if (!opaque) {
2353  gVirtualX->DrawBox(px1old, py1old, px2old, py2old, TVirtualX::kHollow);
2354  } else {
2355  if (axisNumber == 1) {
2356  zbx1 = AbsPixeltoX(px1old);
2357  zbx2 = AbsPixeltoX(px2old);
2358  zby1 = GetUymin();
2359  zby2 = GetUymax();
2360  } else if (axisNumber == 2) {
2361  zbx1 = GetUxmin();
2362  zbx2 = GetUxmax();
2363  zby1 = AbsPixeltoY(py1old);
2364  zby2 = AbsPixeltoY(py2old);
2365  }
2366  if (GetLogx()) {
2367  zbx1 = TMath::Power(10,zbx1);
2368  zbx2 = TMath::Power(10,zbx2);
2369  }
2370  if (GetLogy()) {
2371  zby1 = TMath::Power(10,zby1);
2372  zby2 = TMath::Power(10,zby2);
2373  }
2374  zoombox = new TBox(zbx1, zby1, zbx2, zby2);
2375  Int_t ci = TColor::GetColor("#7d7dff");
2376  TColor *zoomcolor = gROOT->GetColor(ci);
2377  if (!TCanvas::SupportAlpha() || !zoomcolor) zoombox->SetFillStyle(3002);
2378  else zoomcolor->SetAlpha(0.5);
2379  zoombox->SetFillColor(ci);
2380  zoombox->Draw();
2381  gPad->Modified();
2382  gPad->Update();
2383  }
2384  }
2385  if (!opaque) gVirtualX->SetLineColor(-1);
2386  // No break !!!
2387 
2388  case kButton1Motion:
2389  if (view) {
2390  view->GetDistancetoAxis(axisNumber, px, py, ratio2);
2391  } else {
2392  if (!opaque) gVirtualX->DrawBox(px1old, py1old, px2old, py2old, TVirtualX::kHollow);
2393  if (axisNumber == 1) {
2394  ratio2 = (AbsPixeltoX(px) - GetUxmin())/(GetUxmax() - GetUxmin());
2395  px2old = XtoAbsPixel(GetUxmin()+ratio2*(GetUxmax() - GetUxmin()));
2396  } else {
2397  ratio2 = (AbsPixeltoY(py) - GetUymin())/(GetUymax() - GetUymin());
2398  py2old = YtoAbsPixel(GetUymin()+ratio2*(GetUymax() - GetUymin()));
2399  }
2400  if (!opaque) {
2401  gVirtualX->DrawBox(px1old, py1old, px2old, py2old, TVirtualX::kHollow);
2402  } else {
2403  if (axisNumber == 1) {
2404  zbx1 = AbsPixeltoX(px1old);
2405  zbx2 = AbsPixeltoX(px2old);
2406  zby1 = GetUymin();
2407  zby2 = GetUymax();
2408  } else if (axisNumber == 2) {
2409  zbx1 = GetUxmin();
2410  zbx2 = GetUxmax();
2411  zby1 = AbsPixeltoY(py1old);
2412  zby2 = AbsPixeltoY(py2old);
2413  }
2414  if (GetLogx()) {
2415  zbx1 = TMath::Power(10,zbx1);
2416  zbx2 = TMath::Power(10,zbx2);
2417  }
2418  if (GetLogy()) {
2419  zby1 = TMath::Power(10,zby1);
2420  zby2 = TMath::Power(10,zby2);
2421  }
2422  if (zoombox) {
2423  zoombox->SetX1(zbx1);
2424  zoombox->SetY1(zby1);
2425  zoombox->SetX2(zbx2);
2426  zoombox->SetY2(zby2);
2427  }
2428  gPad->Modified();
2429  gPad->Update();
2430  }
2431  }
2432  break;
2433 
2434  case kWheelUp:
2435  bin1 = axis->GetFirst()+1;
2436  bin2 = axis->GetLast()-1;
2437  bin1 = TMath::Max(bin1, 1);
2438  bin2 = TMath::Min(bin2, axis->GetNbins());
2439  if (bin2>bin1) {
2440  axis->SetRange(bin1,bin2);
2441  gPad->Modified();
2442  gPad->Update();
2443  }
2444  break;
2445 
2446  case kWheelDown:
2447  bin1 = axis->GetFirst()-1;
2448  bin2 = axis->GetLast()+1;
2449  bin1 = TMath::Max(bin1, 1);
2450  bin2 = TMath::Min(bin2, axis->GetNbins());
2451  if (bin2>bin1) {
2452  axis->SetRange(bin1,bin2);
2453  gPad->Modified();
2454  gPad->Update();
2455  }
2456  break;
2457 
2458  case kButton1Up:
2459  if (gROOT->IsEscaped()) {
2460  gROOT->SetEscape(kFALSE);
2461  if (opaque && zoombox) {
2462  zoombox->Delete();
2463  zoombox = 0;
2464  }
2465  break;
2466  }
2467 
2468  if (view) {
2469  view->GetDistancetoAxis(axisNumber, px, py, ratio2);
2470  if (ratio1 > ratio2) {
2471  temp = ratio1;
2472  ratio1 = ratio2;
2473  ratio2 = temp;
2474  }
2475  if (ratio2 - ratio1 > 0.05) {
2476  TH1 *hobj = (TH1*)axis->GetParent();
2477  if (axisNumber == 3 && hobj && hobj->GetDimension() != 3) {
2478  Float_t zmin = hobj->GetMinimum();
2479  Float_t zmax = hobj->GetMaximum();
2480  if(GetLogz()){
2481  if (zmin <= 0 && zmax > 0) zmin = TMath::Min((Double_t)1,
2482  (Double_t)0.001*zmax);
2483  zmin = TMath::Log10(zmin);
2484  zmax = TMath::Log10(zmax);
2485  }
2486  Float_t newmin = zmin + (zmax-zmin)*ratio1;
2487  Float_t newmax = zmin + (zmax-zmin)*ratio2;
2488  if (newmin < zmin) newmin = hobj->GetBinContent(hobj->GetMinimumBin());
2489  if (newmax > zmax) newmax = hobj->GetBinContent(hobj->GetMaximumBin());
2490  if (GetLogz()){
2491  newmin = TMath::Exp(2.302585092994*newmin);
2492  newmax = TMath::Exp(2.302585092994*newmax);
2493  }
2494  hobj->SetMinimum(newmin);
2495  hobj->SetMaximum(newmax);
2496  hobj->SetBit(TH1::kIsZoomed);
2497  } else {
2498  first = axis->GetFirst();
2499  last = axis->GetLast();
2500  bin1 = first + Int_t((last-first+1)*ratio1);
2501  bin2 = first + Int_t((last-first+1)*ratio2);
2502  bin1 = TMath::Max(bin1, 1);
2503  bin2 = TMath::Min(bin2, axis->GetNbins());
2504  axis->SetRange(bin1, bin2);
2505  }
2506  delete view;
2507  SetView(0);
2508  Modified(kTRUE);
2509  }
2510  } else {
2511  if (axisNumber == 1) {
2512  ratio2 = (AbsPixeltoX(px) - GetUxmin())/(GetUxmax() - GetUxmin());
2513  xmin = GetUxmin() +ratio1*(GetUxmax() - GetUxmin());
2514  xmax = GetUxmin() +ratio2*(GetUxmax() - GetUxmin());
2515  if (GetLogx() && !kCont4) {
2516  xmin = PadtoX(xmin);
2517  xmax = PadtoX(xmax);
2518  }
2519  } else if (axisNumber == 2) {
2520  ratio2 = (AbsPixeltoY(py) - GetUymin())/(GetUymax() - GetUymin());
2521  xmin = GetUymin() +ratio1*(GetUymax() - GetUymin());
2522  xmax = GetUymin() +ratio2*(GetUymax() - GetUymin());
2523  if (GetLogy() && !kCont4) {
2524  xmin = PadtoY(xmin);
2525  xmax = PadtoY(xmax);
2526  }
2527  } else {
2528  ratio2 = (AbsPixeltoY(py) - GetUymin())/(GetUymax() - GetUymin());
2529  xmin = ratio1;
2530  xmax = ratio2;
2531  }
2532  if (xmin > xmax) {
2533  temp = xmin;
2534  xmin = xmax;
2535  xmax = temp;
2536  temp = ratio1;
2537  ratio1 = ratio2;
2538  ratio2 = temp;
2539  }
2540 
2541  // xmin and xmax need to be adjusted in case of CONT4.
2542  if (kCont4) {
2543  Double_t low = axis->GetBinLowEdge(axis->GetFirst());
2544  Double_t up = axis->GetBinUpEdge(axis->GetLast());
2545  Double_t xmi = GetUxmin();
2546  Double_t xma = GetUxmax();
2547  xmin = ((xmin-xmi)/(xma-xmi))*(up-low)+low;
2548  xmax = ((xmax-xmi)/(xma-xmi))*(up-low)+low;
2549  }
2550 
2551  if (!strcmp(axis->GetName(),"xaxis")) axisNumber = 1;
2552  if (!strcmp(axis->GetName(),"yaxis")) axisNumber = 2;
2553  if (ratio2 - ratio1 > 0.05) {
2554  //update object owning this axis
2555  TH1 *hobj1 = (TH1*)axis->GetParent();
2556  bin1 = axis->FindFixBin(xmin);
2557  bin2 = axis->FindFixBin(xmax);
2558  bin1 = TMath::Max(bin1, 1);
2559  bin2 = TMath::Min(bin2, axis->GetNbins());
2560  if (axisNumber == 1) axis->SetRange(bin1,bin2);
2561  if (axisNumber == 2 && hobj1) {
2562  if (hobj1->GetDimension() == 1) {
2563  if (hobj1->GetNormFactor() != 0) {
2564  Double_t norm = hobj1->GetSumOfWeights()/hobj1->GetNormFactor();
2565  xmin *= norm;
2566  xmax *= norm;
2567  }
2568  hobj1->SetMinimum(xmin);
2569  hobj1->SetMaximum(xmax);
2570  hobj1->SetBit(TH1::kIsZoomed);
2571  } else {
2572  axis->SetRange(bin1,bin2);
2573  }
2574  }
2575  //update all histograms in the pad
2576  TIter next(GetListOfPrimitives());
2577  TObject *obj;
2578  while ((obj= next())) {
2579  if (!obj->InheritsFrom(TH1::Class())) continue;
2580  TH1 *hobj = (TH1*)obj;
2581  if (hobj == hobj1) continue;
2582  bin1 = hobj->GetXaxis()->FindFixBin(xmin);
2583  bin2 = hobj->GetXaxis()->FindFixBin(xmax);
2584  if (axisNumber == 1) {
2585  hobj->GetXaxis()->SetRange(bin1,bin2);
2586  } else if (axisNumber == 2) {
2587  if (hobj->GetDimension() == 1) {
2588  Double_t xxmin = xmin;
2589  Double_t xxmax = xmax;
2590  if (hobj->GetNormFactor() != 0) {
2591  Double_t norm = hobj->GetSumOfWeights()/hobj->GetNormFactor();
2592  xxmin *= norm;
2593  xxmax *= norm;
2594  }
2595  hobj->SetMinimum(xxmin);
2596  hobj->SetMaximum(xxmax);
2597  hobj->SetBit(TH1::kIsZoomed);
2598  } else {
2599  bin1 = hobj->GetYaxis()->FindFixBin(xmin);
2600  bin2 = hobj->GetYaxis()->FindFixBin(xmax);
2601  hobj->GetYaxis()->SetRange(bin1,bin2);
2602  }
2603  }
2604  }
2605  Modified(kTRUE);
2606  }
2607  }
2608  if (!opaque) {
2609  gVirtualX->SetLineColor(-1);
2610  } else {
2611  if (zoombox) {
2612  zoombox->Delete();
2613  zoombox = 0;
2614  }
2615  }
2616  break;
2617  }
2618 }
2619 
2620 ////////////////////////////////////////////////////////////////////////////////
2621 /// Search if object named name is inside this pad or in pads inside this pad.
2622 ///
2623 /// In case name is in several sub-pads the first one is returned.
2624 
2625 TObject *TPad::FindObject(const char *name) const
2626 {
2627  if (!fPrimitives) return nullptr;
2628  TObject *found = fPrimitives->FindObject(name);
2629  if (found) return found;
2630  TObject *cur;
2631  TIter next(GetListOfPrimitives());
2632  while ((cur = next())) {
2633  if (cur->InheritsFrom(TPad::Class())) {
2634  found = ((TPad*)cur)->FindObject(name);
2635  if (found) return found;
2636  }
2637  }
2638  return nullptr;
2639 }
2640 
2641 ////////////////////////////////////////////////////////////////////////////////
2642 /// Search if obj is in pad or in pads inside this pad.
2643 ///
2644 /// In case obj is in several sub-pads the first one is returned.
2645 
2647 {
2648  if (!fPrimitives) return nullptr;
2649  TObject *found = fPrimitives->FindObject(obj);
2650  if (found) return found;
2651  TObject *cur;
2652  TIter next(GetListOfPrimitives());
2653  while ((cur = next())) {
2654  if (cur->InheritsFrom(TPad::Class())) {
2655  found = ((TPad*)cur)->FindObject(obj);
2656  if (found) return found;
2657  }
2658  }
2659  return nullptr;
2660 }
2661 
2662 ////////////////////////////////////////////////////////////////////////////////
2663 /// Get canvas identifier.
2664 
2666 {
2667  return fCanvas ? fCanvas->GetCanvasID() : -1;
2668 }
2669 
2670 ////////////////////////////////////////////////////////////////////////////////
2671 /// Get canvas implementation pointer if any
2672 
2674 {
2675  return fCanvas ? fCanvas->GetCanvasImp() : nullptr;
2676 }
2677 
2678 ////////////////////////////////////////////////////////////////////////////////
2679 /// Get Event.
2680 
2682 {
2683  return fCanvas ? fCanvas->GetEvent() : 0;
2684 }
2685 
2686 ////////////////////////////////////////////////////////////////////////////////
2687 /// Get X event.
2688 
2690 {
2691  return fCanvas ? fCanvas->GetEventX() : 0;
2692 }
2693 
2694 ////////////////////////////////////////////////////////////////////////////////
2695 /// Get Y event.
2696 
2698 {
2699  return fCanvas ? fCanvas->GetEventY() : 0;
2700 }
2701 
2702 ////////////////////////////////////////////////////////////////////////////////
2703 /// Get virtual canvas.
2704 
2706 {
2707  return fCanvas ? (TVirtualPad*) fCanvas : nullptr;
2708 }
2709 
2710 ////////////////////////////////////////////////////////////////////////////////
2711 /// Get highlight color.
2712 
2714 {
2715  return fCanvas ? fCanvas->GetHighLightColor() : 0;
2716 }
2717 
2718 ////////////////////////////////////////////////////////////////////////////////
2719 /// Static function (see also TPad::SetMaxPickDistance)
2720 
2722 {
2723  return fgMaxPickDistance;
2724 }
2725 
2726 ////////////////////////////////////////////////////////////////////////////////
2727 /// Get selected.
2728 
2730 {
2731  if (fCanvas == this) return nullptr;
2732  return fCanvas ? fCanvas->GetSelected() : nullptr;
2733 }
2734 
2735 ////////////////////////////////////////////////////////////////////////////////
2736 /// Get selected pad.
2737 
2739 {
2740  if (fCanvas == this) return nullptr;
2741  return fCanvas ? fCanvas->GetSelectedPad() : nullptr;
2742 }
2743 
2744 ////////////////////////////////////////////////////////////////////////////////
2745 /// Get save pad.
2746 
2748 {
2749  if (fCanvas == this) return nullptr;
2750  return fCanvas ? fCanvas->GetPadSave() : nullptr;
2751 }
2752 
2753 ////////////////////////////////////////////////////////////////////////////////
2754 /// Get Wh.
2755 
2757 {
2758  return fCanvas ? fCanvas->GetWh() : 0;
2759 }
2760 
2761 ////////////////////////////////////////////////////////////////////////////////
2762 /// Get Ww.
2763 
2765 {
2766  return fCanvas ? fCanvas->GetWw() : 0;
2767 }
2768 
2769 ////////////////////////////////////////////////////////////////////////////////
2770 /// Hide tool tip depending on the event type. Typically tool tips
2771 /// are hidden when event is not a kMouseEnter and not a kMouseMotion
2772 /// event.
2773 
2775 {
2776  if (event != kMouseEnter && event != kMouseMotion && fTip)
2777  gPad->CloseToolTip(fTip);
2778 }
2779 
2780 ////////////////////////////////////////////////////////////////////////////////
2781 /// Is pad in batch mode ?
2782 
2784 {
2785  return fCanvas ? fCanvas->IsBatch() : kFALSE;
2786 }
2787 
2788 ////////////////////////////////////////////////////////////////////////////////
2789 /// Is pad retained ?
2790 
2792 {
2793  return fCanvas ? fCanvas->IsRetained() : kFALSE;
2794 }
2795 
2796 ////////////////////////////////////////////////////////////////////////////////
2797 /// Is pad moving in opaque mode ?
2798 
2800 {
2801  return fCanvas ? fCanvas->OpaqueMoving() : kFALSE;
2802 }
2803 
2804 ////////////////////////////////////////////////////////////////////////////////
2805 /// Is pad resizing in opaque mode ?
2806 
2808 {
2809  return fCanvas ? fCanvas->OpaqueResizing() : kFALSE;
2810 }
2811 
2812 ////////////////////////////////////////////////////////////////////////////////
2813 /// Set pad in batch mode.
2814 
2816 {
2817  if (fCanvas) fCanvas->SetBatch(batch);
2818 }
2819 
2820 ////////////////////////////////////////////////////////////////////////////////
2821 /// Set canvas size.
2822 
2824 {
2825  if (fCanvas) fCanvas->SetCanvasSize(ww,wh);
2826 }
2827 
2828 ////////////////////////////////////////////////////////////////////////////////
2829 /// Set cursor type.
2830 
2832 {
2833  if (fCanvas) fCanvas->SetCursor(cursor);
2834 }
2835 
2836 ////////////////////////////////////////////////////////////////////////////////
2837 /// Set double buffer mode ON or OFF.
2838 
2840 {
2841  if (fCanvas) fCanvas->SetDoubleBuffer(mode);
2842 }
2843 
2844 ////////////////////////////////////////////////////////////////////////////////
2845 /// Set selected.
2846 
2848 {
2849  if (fCanvas) fCanvas->SetSelected(obj);
2850 }
2851 
2852 ////////////////////////////////////////////////////////////////////////////////
2853 /// Update pad.
2854 
2856 {
2857  if (fCanvas) fCanvas->Update();
2858 }
2859 
2860 ////////////////////////////////////////////////////////////////////////////////
2861 /// Get frame.
2862 
2864 {
2865  if (!fPrimitives) fPrimitives = new TList;
2867  if (!frame) frame = (TFrame*)GetListOfPrimitives()->FindObject("TFrame");
2868  fFrame = frame;
2869  if (!fFrame) {
2870  if (!frame) fFrame = new TFrame(0,0,1,1);
2871  Int_t framecolor = GetFrameFillColor();
2872  if (!framecolor) framecolor = GetFillColor();
2873  fFrame->SetFillColor(framecolor);
2880  }
2881  return fFrame;
2882 }
2883 
2884 ////////////////////////////////////////////////////////////////////////////////
2885 /// Get primitive.
2886 
2887 TObject *TPad::GetPrimitive(const char *name) const
2888 {
2889  if (!fPrimitives) return nullptr;
2890  TIter next(fPrimitives);
2891  TObject *found, *obj;
2892  while ((obj=next())) {
2893  if (!strcmp(name, obj->GetName())) return obj;
2894  if (obj->InheritsFrom(TPad::Class())) continue;
2895  found = obj->FindObject(name);
2896  if (found) return found;
2897  }
2898  return nullptr;
2899 }
2900 
2901 ////////////////////////////////////////////////////////////////////////////////
2902 /// Get a pointer to subpadnumber of this pad.
2903 
2904 TVirtualPad *TPad::GetPad(Int_t subpadnumber) const
2905 {
2906  if (!subpadnumber) {
2907  return (TVirtualPad*)this;
2908  }
2909 
2910  TObject *obj;
2911  if (!fPrimitives) return nullptr;
2912  TIter next(GetListOfPrimitives());
2913  while ((obj = next())) {
2914  if (obj->InheritsFrom(TVirtualPad::Class())) {
2915  TVirtualPad *pad = (TVirtualPad*)obj;
2916  if (pad->GetNumber() == subpadnumber) return pad;
2917  }
2918  }
2919  return nullptr;
2920 }
2921 
2922 ////////////////////////////////////////////////////////////////////////////////
2923 /// Return lower and upper bounds of the pad in NDC coordinates.
2924 
2925 void TPad::GetPadPar(Double_t &xlow, Double_t &ylow, Double_t &xup, Double_t &yup)
2926 {
2927  xlow = fXlowNDC;
2928  ylow = fYlowNDC;
2929  xup = fXlowNDC+fWNDC;
2930  yup = fYlowNDC+fHNDC;
2931 }
2932 
2933 ////////////////////////////////////////////////////////////////////////////////
2934 /// Return pad world coordinates range.
2935 
2937 {
2938  x1 = fX1;
2939  y1 = fY1;
2940  x2 = fX2;
2941  y2 = fY2;
2942 }
2943 
2944 ////////////////////////////////////////////////////////////////////////////////
2945 /// Return pad axis coordinates range.
2946 
2948 {
2949  xmin = fUxmin;
2950  ymin = fUymin;
2951  xmax = fUxmax;
2952  ymax = fUymax;
2953 }
2954 
2955 ////////////////////////////////////////////////////////////////////////////////
2956 /// Highlight pad.
2957 /// do not highlight when printing on Postscript
2958 
2960 {
2961  if (gVirtualPS && gVirtualPS->TestBit(kPrintingPS)) return;
2962 
2963  if (color <= 0) return;
2964 
2966 
2967  // We do not want to have active(executable) buttons, etc highlighted
2968  // in this manner, unless we want to edit'em
2969  if (GetMother() && GetMother()->IsEditable() && !InheritsFrom(TButton::Class())) {
2970  //When doing a DrawClone from the GUI you would do
2971  // - select an empty pad -
2972  // - right click on object -
2973  // - select DrawClone on menu -
2974  //
2975  // Without the SetSelectedPad(); in the HighLight function, the
2976  // above instruction lead to the clone to be drawn in the
2977  // same canvas as the original object. This is because the
2978  // 'right clicking' (via TCanvas::HandleInput) changes gPad
2979  // momentarily such that when DrawClone is called, it is
2980  // not the right value (for DrawClone). Should be FIXED.
2981  gROOT->SetSelectedPad(this);
2982  if (GetBorderMode()>0) {
2983  if (set) PaintBorder(-color, kFALSE);
2984  else PaintBorder(-GetFillColor(), kFALSE);
2985  }
2986  }
2987 
2989 }
2990 
2991 ////////////////////////////////////////////////////////////////////////////////
2992 /// List all primitives in pad.
2993 
2994 void TPad::ls(Option_t *option) const
2995 {
2997  std::cout <<IsA()->GetName()<<" fXlowNDC=" <<fXlowNDC<<" fYlowNDC="<<fYlowNDC<<" fWNDC="<<GetWNDC()<<" fHNDC="<<GetHNDC()
2998  <<" Name= "<<GetName()<<" Title= "<<GetTitle()<<" Option="<<option<<std::endl;
3000  if (!fPrimitives) return;
3001  fPrimitives->ls(option);
3003 }
3004 
3005 ////////////////////////////////////////////////////////////////////////////////
3006 /// Increment (i==1) or set (i>1) the number of autocolor in the pad.
3007 
3009 {
3010  if (opt.Index("pfc")>=0 || opt.Index("plc")>=0 || opt.Index("pmc")>=0) {
3011  if (i==1) fNumPaletteColor++;
3012  else fNumPaletteColor = i;
3013  return fNumPaletteColor;
3014  } else {
3015  return 0;
3016  }
3017 }
3018 
3019 ////////////////////////////////////////////////////////////////////////////////
3020 /// Get the next autocolor in the pad.
3021 
3023 {
3024  Int_t i = 0;
3025  Int_t ncolors = gStyle->GetNumberOfColors();
3026  if (fNumPaletteColor>1) {
3027  i = fNextPaletteColor*(ncolors/(fNumPaletteColor-1));
3028  if (i>=ncolors) i = ncolors-1;
3029  }
3032  return gStyle->GetColorPalette(i);
3033 }
3034 
3035 ////////////////////////////////////////////////////////////////////////////////
3036 /// Initialise the grid used to find empty space when adding a box (Legend) in a pad
3037 
3039 {
3040  Int_t const cellSize = 10; // Sive of an individual grid cell in pixels.
3041 
3042  if (fCGnx == 0 && fCGny == 0) {
3043  fCGnx = (Int_t)(gPad->GetWw())/cellSize;
3044  fCGny = (Int_t)(gPad->GetWh())/cellSize;
3045  } else {
3046  Int_t CGnx = (Int_t)(gPad->GetWw())/cellSize;
3047  Int_t CGny = (Int_t)(gPad->GetWh())/cellSize;
3048  if (fCGnx != CGnx || fCGny != CGny) {
3049  fCGnx = CGnx;
3050  fCGny = CGny;
3051  delete [] fCollideGrid;
3052  fCollideGrid = nullptr;
3053  }
3054  }
3055 
3056  // Initialise the collide grid
3057  if (!fCollideGrid) {
3058  fCollideGrid = new Bool_t [fCGnx*fCGny];
3059  for (int i = 0; i<fCGnx; i++) {
3060  for (int j = 0; j<fCGny; j++) {
3061  fCollideGrid[i + j*fCGnx] = kTRUE;
3062  }
3063  }
3064  }
3065 
3066  // Fill the collide grid
3068  if (!l) return;
3069  Int_t np = l->GetSize();
3070  TObject *o;
3071 
3072  for (int i=0; i<np; i++) {
3073  o = (TObject *) l->At(i);
3074  if (o!=oi) {
3075  if (o->InheritsFrom(TFrame::Class())) { FillCollideGridTFrame(o); continue;}
3076  if (o->InheritsFrom(TBox::Class())) { FillCollideGridTBox(o); continue;}
3077  if (o->InheritsFrom(TH1::Class())) { FillCollideGridTH1(o); continue;}
3078  if (o->InheritsFrom(TGraph::Class())) { FillCollideGridTGraph(o); continue;}
3079  if (o->InheritsFrom(TMultiGraph::Class())) {
3080  TList * grlist = ((TMultiGraph *)o)->GetListOfGraphs();
3081  TIter nextgraph(grlist);
3082  TObject * og;
3083  while ((og = nextgraph())) FillCollideGridTGraph(og);
3084  }
3085  if (o->InheritsFrom(THStack::Class())) {
3086  TList * hlist = ((THStack *)o)->GetHists();
3087  TIter nexthist(hlist);
3088  TObject * oh;
3089  while ((oh = nexthist())) {
3090  if (oh->InheritsFrom(TH1::Class())) FillCollideGridTH1(oh);
3091  }
3092  }
3093  }
3094  }
3095 }
3096 
3097 ////////////////////////////////////////////////////////////////////////////////
3098 /// Check if a box of size w and h collide some primitives in the pad at
3099 /// position i,j
3100 
3102 {
3103  for (int r=i; r<w+i; r++) {
3104  for (int c=j; c<h+j; c++) {
3105  if (!fCollideGrid[r + c*fCGnx]) return kTRUE;
3106  }
3107  }
3108  return kFALSE;
3109 }
3110 
3111 ////////////////////////////////////////////////////////////////////////////////
3112 /// Place a box in NDC space
3113 ///
3114 /// \return `true` if the box could be placed, `false` if not.
3115 ///
3116 /// \param[in] o pointer to the box to be placed
3117 /// \param[in] w box width to be placed
3118 /// \param[in] h box height to be placed
3119 /// \param[out] xl x position of the bottom left corner of the placed box
3120 /// \param[out] yb y position of the bottom left corner of the placed box
3121 
3123 {
3124  FillCollideGrid(o);
3125 
3126  Int_t iw = (int)(fCGnx*w);
3127  Int_t ih = (int)(fCGny*h);
3128 
3129  Int_t nxmax = fCGnx-iw-1;
3130  Int_t nymax = fCGny-ih-1;
3131 
3132  for (Int_t i = 0; i<nxmax; i++) {
3133  for (Int_t j = 0; j<=nymax; j++) {
3134  if (Collide(i,j,iw,ih)) {
3135  continue;
3136  } else {
3137  xl = (Double_t)(i)/(Double_t)(fCGnx);
3138  yb = (Double_t)(j)/(Double_t)(fCGny);
3139  return kTRUE;
3140  }
3141  }
3142  }
3143  return kFALSE;
3144 }
3145 
3146 #define NotFree(i, j) fCollideGrid[TMath::Max(TMath::Min(i+j*fCGnx,fCGnx*fCGny),0)] = kFALSE;
3147 
3148 ////////////////////////////////////////////////////////////////////////////////
3149 /// Mark as "not free" the cells along a line.
3150 
3152 {
3153  NotFree(x1, y1);
3154  NotFree(x2, y2);
3155  Int_t i, j, xt, yt;
3156 
3157  // horizontal lines
3158  if (y1==y2) {
3159  for (i=x1+1; i<x2; i++) NotFree(i,y1);
3160  return;
3161  }
3162 
3163  // vertical lines
3164  if (x1==x2) {
3165  for (i=y1+1; i<y2; i++) NotFree(x1,i);
3166  return;
3167  }
3168 
3169  // other lines
3170  if (TMath::Abs(x2-x1)>TMath::Abs(y2-y1)) {
3171  if (x1>x2) {
3172  xt = x1; x1 = x2; x2 = xt;
3173  yt = y1; y1 = y2; y2 = yt;
3174  }
3175  for (i=x1+1; i<x2; i++) {
3176  j = (Int_t)((Double_t)(y2-y1)*(Double_t)((i-x1)/(Double_t)(x2-x1))+y1);
3177  NotFree(i,j);
3178  NotFree(i,(j+1));
3179  }
3180  } else {
3181  if (y1>y2) {
3182  yt = y1; y1 = y2; y2 = yt;
3183  xt = x1; x1 = x2; x2 = xt;
3184  }
3185  for (j=y1+1; j<y2; j++) {
3186  i = (Int_t)((Double_t)(x2-x1)*(Double_t)((j-y1)/(Double_t)(y2-y1))+x1);
3187  NotFree(i,j);
3188  NotFree((i+1),j);
3189  }
3190  }
3191 }
3192 
3193 ////////////////////////////////////////////////////////////////////////////////
3195 {
3196  TBox *b = (TBox *)o;
3197  if (fCGnx==0||fCGny==0) return;
3198  Double_t xs = (fX2-fX1)/fCGnx;
3199  Double_t ys = (fY2-fY1)/fCGny;
3200 
3201  Int_t x1 = (Int_t)((b->GetX1()-fX1)/xs);
3202  Int_t x2 = (Int_t)((b->GetX2()-fX1)/xs);
3203  Int_t y1 = (Int_t)((b->GetY1()-fY1)/ys);
3204  Int_t y2 = (Int_t)((b->GetY2()-fY1)/ys);
3205  for (int i = x1; i<=x2; i++) {
3206  for (int j = y1; j<=y2; j++) NotFree(i, j);
3207  }
3208 }
3209 
3210 ////////////////////////////////////////////////////////////////////////////////
3212 {
3213  TFrame *f = (TFrame *)o;
3214  if (fCGnx==0||fCGny==0) return;
3215  Double_t xs = (fX2-fX1)/fCGnx;
3216  Double_t ys = (fY2-fY1)/fCGny;
3217 
3218  Int_t x1 = (Int_t)((f->GetX1()-fX1)/xs);
3219  Int_t x2 = (Int_t)((f->GetX2()-fX1)/xs);
3220  Int_t y1 = (Int_t)((f->GetY1()-fY1)/ys);
3221  Int_t y2 = (Int_t)((f->GetY2()-fY1)/ys);
3222  Int_t i;
3223 
3224  for (i = x1; i<=x2; i++) {
3225  NotFree(i, y1);
3226  NotFree(i, (y1-1));
3227  NotFree(i, (y1-2));
3228  }
3229  for (i = y1; i<=y2; i++) {
3230  NotFree(x1, i);
3231  NotFree((x1-1), i);
3232  NotFree((x1-2), i);
3233  }
3234 }
3235 
3236 ////////////////////////////////////////////////////////////////////////////////
3238 {
3239  TGraph *g = (TGraph *)o;
3240  if (fCGnx==0||fCGny==0) return;
3241  Double_t xs = (fX2-fX1)/fCGnx;
3242  Double_t ys = (fY2-fY1)/fCGny;
3243 
3244  Int_t n = g->GetN();
3245  Double_t x1, x2, y1, y2;
3246 
3247  for (Int_t i=1; i<n; i++) {
3248  g->GetPoint(i-1,x1,y1);
3249  g->GetPoint(i ,x2,y2);
3250  if (fLogx) {
3251  if (x1 > 0) x1 = TMath::Log10(x1);
3252  else x1 = fUxmin;
3253  if (x2 > 0) x2 = TMath::Log10(x2);
3254  else x2 = fUxmin;
3255  }
3256  if (fLogy) {
3257  if (y1 > 0) y1 = TMath::Log10(y1);
3258  else y1 = fUymin;
3259  if (y2 > 0) y2 = TMath::Log10(y2);
3260  else y2 = fUymin;
3261  }
3262  LineNotFree((int)((x1-fX1)/xs), (int)((x2-fX1)/xs),
3263  (int)((y1-fY1)/ys), (int)((y2-fY1)/ys));
3264  }
3265 }
3266 
3267 ////////////////////////////////////////////////////////////////////////////////
3269 {
3270  TH1 *h = (TH1 *)o;
3271  if (fCGnx==0||fCGny==0) return;
3272  if (o->InheritsFrom(TH2::Class())) return;
3273  if (o->InheritsFrom(TH3::Class())) return;
3274 
3275  TString name = h->GetName();
3276  if (name.Index("hframe") >= 0) return;
3277 
3278  Double_t xs = (fX2-fX1)/fCGnx;
3279  Double_t ys = (fY2-fY1)/fCGny;
3280 
3281  bool haserrors = false;
3282  TString drawOption = h->GetDrawOption();
3283  drawOption.ToLower();
3284  drawOption.ReplaceAll("same","");
3285 
3286  if (drawOption.Index("hist") < 0) {
3287  if (drawOption.Index("e") >= 0) haserrors = true;
3288  }
3289 
3290  Int_t nx = h->GetNbinsX();
3291  Int_t x1, y1, y2;
3292  Int_t i, j;
3293  Double_t x1l, y1l, y2l;
3294 
3295  for (i = 1; i<nx; i++) {
3296  if (haserrors) {
3297  x1l = h->GetBinCenter(i);
3298  if (fLogx) {
3299  if (x1l > 0) x1l = TMath::Log10(x1l);
3300  else x1l = fUxmin;
3301  }
3302  x1 = (Int_t)((x1l-fX1)/xs);
3303  y1l = h->GetBinContent(i)-h->GetBinErrorLow(i);
3304  if (fLogy) {
3305  if (y1l > 0) y1l = TMath::Log10(y1l);
3306  else y1l = fUymin;
3307  }
3308  y1 = (Int_t)((y1l-fY1)/ys);
3309  y2l = h->GetBinContent(i)+h->GetBinErrorUp(i);
3310  if (fLogy) {
3311  if (y2l > 0) y2l = TMath::Log10(y2l);
3312  else y2l = fUymin;
3313  }
3314  y2 = (Int_t)((y2l-fY1)/ys);
3315  for (j=y1; j<=y2; j++) {
3316  NotFree(x1, j);
3317  }
3318  }
3319  x1l = h->GetBinLowEdge(i);
3320  if (fLogx) {
3321  if (x1l > 0) x1l = TMath::Log10(x1l);
3322  else x1l = fUxmin;
3323  }
3324  x1 = (Int_t)((x1l-fX1)/xs);
3325  y1l = h->GetBinContent(i);
3326  if (fLogy) {
3327  if (y1l > 0) y1l = TMath::Log10(y1l);
3328  else y1l = fUymin;
3329  }
3330  y1 = (Int_t)((y1l-fY1)/ys);
3331  NotFree(x1, y1);
3332  x1l = h->GetBinLowEdge(i)+h->GetBinWidth(i);
3333  if (fLogx) {
3334  if (x1l > 0) x1l = TMath::Log10(x1l);
3335  else x1l = fUxmin;
3336  }
3337  x1 = (int)((x1l-fX1)/xs);
3338  NotFree(x1, y1);
3339  }
3340 
3341  // Extra objects in the list of function
3342  TPaveStats *ps = (TPaveStats*)h->GetListOfFunctions()->FindObject("stats");
3343  if (ps) FillCollideGridTBox(ps);
3344 }
3345 
3346 ////////////////////////////////////////////////////////////////////////////////
3347 /// This method draws the collide grid on top of the canvas. This is used for
3348 /// debugging only. At some point it will be removed.
3349 
3351 {
3352  if (fCGnx==0||fCGny==0) return;
3353  auto box = new TBox();
3354  box->SetFillColorAlpha(kRed,0.5);
3355 
3356  Double_t xs = (fX2-fX1)/fCGnx;
3357  Double_t ys = (fY2-fY1)/fCGny;
3358 
3359  Double_t X1L, X2L, Y1L, Y2L;
3360  Double_t t = 0.15;
3361  Double_t Y1, Y2;
3362  Double_t X1 = fX1;
3363  Double_t X2 = X1+xs;
3364 
3365  for (int i = 0; i<fCGnx; i++) {
3366  Y1 = fY1;
3367  Y2 = Y1+ys;
3368  for (int j = 0; j<fCGny; j++) {
3369  if (gPad->GetLogx()) {
3370  X1L = TMath::Power(10,X1);
3371  X2L = TMath::Power(10,X2);
3372  } else {
3373  X1L = X1;
3374  X2L = X2;
3375  }
3376  if (gPad->GetLogy()) {
3377  Y1L = TMath::Power(10,Y1);
3378  Y2L = TMath::Power(10,Y2);
3379  } else {
3380  Y1L = Y1;
3381  Y2L = Y2;
3382  }
3383  if (!fCollideGrid[i + j*fCGnx]) {
3384  box->SetFillColorAlpha(kBlack,t);
3385  box->DrawBox(X1L, Y1L, X2L, Y2L);
3386  } else {
3387  box->SetFillColorAlpha(kRed,t);
3388  box->DrawBox(X1L, Y1L, X2L, Y2L);
3389  }
3390  Y1 = Y2;
3391  Y2 = Y1+ys;
3392  if (t==0.15) t = 0.1;
3393  else t = 0.15;
3394  }
3395  X1 = X2;
3396  X2 = X1+xs;
3397  }
3398 }
3399 
3400 
3401 ////////////////////////////////////////////////////////////////////////////////
3402 /// Convert x from pad to X.
3403 
3405 {
3406  if (fLogx && x < 50) return Double_t(TMath::Exp(2.302585092994*x));
3407  return x;
3408 }
3409 
3410 ////////////////////////////////////////////////////////////////////////////////
3411 /// Convert y from pad to Y.
3412 
3414 {
3415  if (fLogy && y < 50) return Double_t(TMath::Exp(2.302585092994*y));
3416  return y;
3417 }
3418 
3419 ////////////////////////////////////////////////////////////////////////////////
3420 /// Convert x from X to pad.
3421 
3423 {
3424  if (fLogx) {
3425  if (x > 0) x = TMath::Log10(x);
3426  else x = fUxmin;
3427  }
3428  return x;
3429 }
3430 
3431 ////////////////////////////////////////////////////////////////////////////////
3432 /// Convert y from Y to pad.
3433 
3435 {
3436  if (fLogy) {
3437  if (y > 0) y = TMath::Log10(y);
3438  else y = fUymin;
3439  }
3440  return y;
3441 }
3442 
3443 ////////////////////////////////////////////////////////////////////////////////
3444 /// Paint all primitives in pad.
3445 
3446 void TPad::Paint(Option_t * /*option*/)
3447 {
3448  if (!fPrimitives) fPrimitives = new TList;
3450  fViewer3D->PadPaint(this);
3451  Modified(kFALSE);
3452  if (GetGLDevice()!=-1 && gVirtualPS) {
3453  TPad *padsav = (TPad*)gPad;
3454  gPad = this;
3455  if (gGLManager) gGLManager->PrintViewer(GetViewer3D());
3456  gPad = padsav;
3457  }
3458  return;
3459  }
3460 
3462 
3463  TPad *padsav = (TPad*)gPad;
3464 
3465  fPadPaint = 1;
3466  cd();
3467 
3469  PaintDate();
3470 
3472  TObject *obj;
3473 
3474  Bool_t began3DScene = kFALSE;
3475  while (lnk) {
3476  obj = lnk->GetObject();
3477 
3478  // Create a pad 3D viewer if none exists and we encounter a 3D shape
3479  if (!fViewer3D && obj->InheritsFrom(TAtt3D::Class())) {
3480  GetViewer3D("pad");
3481  }
3482 
3483  // Open a 3D scene if required
3484  if (fViewer3D && !fViewer3D->BuildingScene()) {
3485  fViewer3D->BeginScene();
3486  began3DScene = kTRUE;
3487  }
3488 
3489  obj->Paint(lnk->GetOption());
3490  lnk = (TObjOptLink*)lnk->Next();
3491  }
3492 
3493  if (padsav) padsav->cd();
3494  fPadPaint = 0;
3495  Modified(kFALSE);
3496 
3497  // Close the 3D scene if we opened it. This must be done after modified
3498  // flag is cleared, as some viewers will invoke another paint by marking pad modified again
3499  if (began3DScene) {
3500  fViewer3D->EndScene();
3501  }
3502 }
3503 
3504 ////////////////////////////////////////////////////////////////////////////////
3505 /// Paint the pad border.
3506 /// Draw first a box as a normal filled box
3507 
3509 {
3510  if (color >= 0) {
3511  TAttLine::Modify(); //Change line attributes only if necessary
3512  TAttFill::Modify(); //Change fill area attributes only if necessary
3513 
3514  //With Cocoa we have a transparency. But we also have
3515  //pixmaps, and if you just paint a new content over the old one
3516  //with alpha < 1., you'll be able to see the old content.
3517  if (!gROOT->IsBatch() && gVirtualX->InheritsFrom("TGCocoa") && GetPainter())
3519 
3520  PaintBox(fX1,fY1,fX2,fY2);
3521  }
3522  if (color < 0) color = -color;
3523  // then paint 3d frame (depending on bordermode)
3524  if (IsTransparent()) return;
3525  // Paint a 3D frame around the pad.
3526 
3527  if (fBorderMode == 0) return;
3528  Int_t bordersize = fBorderSize;
3529  if (bordersize <= 0) bordersize = 2;
3530 
3531  const Double_t realBsX = bordersize / (GetAbsWNDC() * GetWw()) * (fX2 - fX1);
3532  const Double_t realBsY = bordersize / (GetAbsHNDC() * GetWh()) * (fY2 - fY1);
3533 
3534  Short_t px1,py1,px2,py2;
3535  Double_t xl, xt, yl, yt;
3536 
3537  // GetDarkColor() and GetLightColor() use GetFillColor()
3538  Color_t oldcolor = GetFillColor();
3539  SetFillColor(color);
3540  TAttFill::Modify();
3541  Color_t light = 0, dark = 0;
3542  if (color != 0) {
3543  light = TColor::GetColorBright(color);
3544  dark = TColor::GetColorDark(color);
3545  }
3546 
3547  // Compute real left bottom & top right of the box in pixels
3548  px1 = XtoPixel(fX1); py1 = YtoPixel(fY1);
3549  px2 = XtoPixel(fX2); py2 = YtoPixel(fY2);
3550  if (px1 < px2) {xl = fX1; xt = fX2; }
3551  else {xl = fX2; xt = fX1;}
3552  if (py1 > py2) {yl = fY1; yt = fY2;}
3553  else {yl = fY2; yt = fY1;}
3554 
3555  Double_t frameXs[7] = {}, frameYs[7] = {};
3556 
3557  if (!IsBatch() && GetPainter()) {
3558  // Draw top&left part of the box
3559  frameXs[0] = xl; frameYs[0] = yl;
3560  frameXs[1] = xl + realBsX; frameYs[1] = yl + realBsY;
3561  frameXs[2] = frameXs[1]; frameYs[2] = yt - realBsY;
3562  frameXs[3] = xt - realBsX; frameYs[3] = frameYs[2];
3563  frameXs[4] = xt; frameYs[4] = yt;
3564  frameXs[5] = xl; frameYs[5] = yt;
3565  frameXs[6] = xl; frameYs[6] = yl;
3566 
3567  if (fBorderMode == -1) GetPainter()->SetFillColor(dark);
3568  else GetPainter()->SetFillColor(light);
3569  GetPainter()->DrawFillArea(7, frameXs, frameYs);
3570 
3571  // Draw bottom&right part of the box
3572  frameXs[0] = xl; frameYs[0] = yl;
3573  frameXs[1] = xl + realBsX; frameYs[1] = yl + realBsY;
3574  frameXs[2] = xt - realBsX; frameYs[2] = frameYs[1];
3575  frameXs[3] = frameXs[2]; frameYs[3] = yt - realBsY;
3576  frameXs[4] = xt; frameYs[4] = yt;
3577  frameXs[5] = xt; frameYs[5] = yl;
3578  frameXs[6] = xl; frameYs[6] = yl;
3579 
3580  if (fBorderMode == -1) GetPainter()->SetFillColor(light);
3581  else GetPainter()->SetFillColor(dark);
3582  GetPainter()->DrawFillArea(7, frameXs, frameYs);
3583 
3584  // If this pad is a button, highlight it
3585  if (InheritsFrom(TButton::Class()) && fBorderMode == -1) {
3586  if (TestBit(kFraming)) { // bit set in TButton::SetFraming
3587  if (GetFillColor() != 2) GetPainter()->SetLineColor(2);
3588  else GetPainter()->SetLineColor(4);
3589  GetPainter()->DrawBox(xl + realBsX, yl + realBsY, xt - realBsX, yt - realBsY, TVirtualPadPainter::kHollow);
3590  }
3591  }
3592  GetPainter()->SetFillColor(-1);
3593  SetFillColor(oldcolor);
3594  }
3595 
3596  if (!tops) return;
3597 
3598  PaintBorderPS(xl, yl, xt, yt, fBorderMode, bordersize, dark, light);
3599 }
3600 
3601 ////////////////////////////////////////////////////////////////////////////////
3602 /// Paint a frame border with Postscript.
3603 
3604 void TPad::PaintBorderPS(Double_t xl,Double_t yl,Double_t xt,Double_t yt,Int_t bmode,Int_t bsize,Int_t dark,Int_t light)
3605 {
3606  if (!gVirtualPS) return;
3607  gVirtualPS->DrawFrame(xl, yl, xt, yt, bmode,bsize,dark,light);
3608 }
3609 
3610 ////////////////////////////////////////////////////////////////////////////////
3611 /// Paint the current date and time if the option date is on.
3612 
3614 {
3615  if (fCanvas == this && gStyle->GetOptDate()) {
3616  TDatime dt;
3617  const char *dates;
3618  char iso[16];
3619  if (gStyle->GetOptDate() < 10) {
3620  //by default use format like "Wed Sep 25 17:10:35 2002"
3621  dates = dt.AsString();
3622  } else if (gStyle->GetOptDate() < 20) {
3623  //use ISO format like 2002-09-25
3624  strlcpy(iso,dt.AsSQLString(),16);
3625  dates = iso;
3626  } else {
3627  //use ISO format like 2002-09-25 17:10:35
3628  dates = dt.AsSQLString();
3629  }
3630  TText tdate(gStyle->GetDateX(),gStyle->GetDateY(),dates);
3631  tdate.SetTextSize( gStyle->GetAttDate()->GetTextSize());
3632  tdate.SetTextFont( gStyle->GetAttDate()->GetTextFont());
3636  tdate.SetNDC();
3637  tdate.Paint();
3638  }
3639 }
3640 
3641 ////////////////////////////////////////////////////////////////////////////////
3642 /// Paint histogram/graph frame.
3643 
3645 {
3646  if (!fPrimitives) fPrimitives = new TList;
3647  TList *glist = GetListOfPrimitives();
3648  TFrame *frame = GetFrame();
3649  frame->SetX1(xmin);
3650  frame->SetX2(xmax);
3651  frame->SetY1(ymin);
3652  frame->SetY2(ymax);
3653  if (!glist->FindObject(fFrame)) {
3654  glist->AddFirst(frame);
3656  }
3657  frame->Paint();
3658 }
3659 
3660 ////////////////////////////////////////////////////////////////////////////////
3661 /// Traverse pad hierarchy and (re)paint only modified pads.
3662 
3664 {
3666  if (IsModified()) {
3667  fViewer3D->PadPaint(this);
3668  Modified(kFALSE);
3669  }
3670  TList *pList = GetListOfPrimitives();
3671  TObjOptLink *lnk = 0;
3672  if (pList) lnk = (TObjOptLink*)pList->FirstLink();
3673  TObject *obj;
3674  while (lnk) {
3675  obj = lnk->GetObject();
3676  if (obj->InheritsFrom(TPad::Class()))
3677  ((TPad*)obj)->PaintModified();
3678  lnk = (TObjOptLink*)lnk->Next();
3679  }
3680  return;
3681  }
3682 
3684 
3685  TPad *padsav = (TPad*)gPad;
3686  TVirtualPS *saveps = gVirtualPS;
3687  if (gVirtualPS) {
3689  }
3690  fPadPaint = 1;
3691  cd();
3692  if (IsModified() || IsTransparent()) {
3693  if ((fFillStyle < 3026) && (fFillStyle > 3000)) {
3694  if (!gPad->IsBatch() && GetPainter()) GetPainter()->ClearDrawable();
3695  }
3697  }
3698 
3699  PaintDate();
3700 
3701  TList *pList = GetListOfPrimitives();
3702  TObjOptLink *lnk = 0;
3703  if (pList) lnk = (TObjOptLink*)pList->FirstLink();
3704  TObject *obj;
3705 
3706  Bool_t began3DScene = kFALSE;
3707 
3708  while (lnk) {
3709  obj = lnk->GetObject();
3710  if (obj->InheritsFrom(TPad::Class())) {
3711  ((TPad*)obj)->PaintModified();
3712  } else if (IsModified() || IsTransparent()) {
3713 
3714  // Create a pad 3D viewer if none exists and we encounter a
3715  // 3D shape
3716  if (!fViewer3D && obj->InheritsFrom(TAtt3D::Class())) {
3717  GetViewer3D("pad");
3718  }
3719 
3720  // Open a 3D scene if required
3721  if (fViewer3D && !fViewer3D->BuildingScene()) {
3722  fViewer3D->BeginScene();
3723  began3DScene = kTRUE;
3724  }
3725 
3726  obj->Paint(lnk->GetOption());
3727  }
3728  lnk = (TObjOptLink*)lnk->Next();
3729  }
3730 
3731  if (padsav) padsav->cd();
3732  fPadPaint = 0;
3733  Modified(kFALSE);
3734 
3735  // This must be done after modified flag is cleared, as some
3736  // viewers will invoke another paint by marking pad modified again
3737  if (began3DScene) {
3738  if (fViewer3D) fViewer3D->EndScene();
3739  }
3740 
3741  gVirtualPS = saveps;
3742 }
3743 
3744 ////////////////////////////////////////////////////////////////////////////////
3745 /// Paint box in CurrentPad World coordinates.
3746 ///
3747 /// - if option[0] = 's' the box is forced to be paint with style=0
3748 /// - if option[0] = 'l' the box contour is drawn
3749 
3751 {
3752  if (!gPad->IsBatch() && GetPainter()) {
3753  Int_t style0 = GetPainter()->GetFillStyle();
3754  Int_t style = style0;
3755  if (option[0] == 's') {
3756  GetPainter()->SetFillStyle(0);
3757  style = 0;
3758  }
3759  if (style) {
3760  if (style > 3000 && style < 4000) {
3761  if (style < 3026) {
3762  // draw stipples with fFillColor foreground
3764  }
3765 
3766  if (style >= 3100 && style < 4000) {
3767  Double_t xb[4], yb[4];
3768  xb[0] = x1; xb[1] = x1; xb[2] = x2; xb[3] = x2;
3769  yb[0] = y1; yb[1] = y2; yb[2] = y2; yb[3] = y1;
3770  PaintFillAreaHatches(4, xb, yb, style);
3771  return;
3772  }
3773  //special case for TAttFillCanvas
3774  if (GetPainter()->GetFillColor() == 10) {
3775  GetPainter()->SetFillColor(1);
3777  GetPainter()->SetFillColor(10);
3778  }
3779  } else if (style >= 4000 && style <= 4100) {
3780  // For style >=4000 we make the window transparent.
3781  // From 4000 to 4100 the window is 100% transparent to 100% opaque
3782 
3783  //ignore this style option when this is the canvas itself
3784  if (this == fMother) {
3785  //It's clear, that virtual X checks a style (4000) and will render a hollow rect!
3786  const Style_t oldFillStyle = GetPainter()->GetFillStyle();
3787  if (gVirtualX->InheritsFrom("TGCocoa"))
3788  GetPainter()->SetFillStyle(1000);
3790  if (gVirtualX->InheritsFrom("TGCocoa"))
3791  GetPainter()->SetFillStyle(oldFillStyle);
3792  } else {
3793  //draw background by blitting all bottom pads
3794  int px, py;
3795  XYtoAbsPixel(fX1, fY2, px, py);
3796 
3797  if (fMother) {
3798  fMother->CopyBackgroundPixmap(px, py);
3799  CopyBackgroundPixmaps(fMother, this, px, py);
3800  }
3801 
3802  GetPainter()->SetOpacity(style - 4000);
3803  }
3804  } else if (style >= 1000 && style <= 1999) {
3806  } else {
3808  }
3809  if (option[0] == 'l') GetPainter()->DrawBox(x1, y1, x2, y2, TVirtualPadPainter::kHollow);
3810  } else {
3812  if (option[0] == 's') GetPainter()->SetFillStyle(style0);
3813  }
3814  }
3815 
3816  if (gVirtualPS) {
3817  Int_t style0 = gVirtualPS->GetFillStyle();
3818  if (option[0] == 's') {
3820  } else {
3821  if (style0 >= 3100 && style0 < 4000) {
3822  Double_t xb[4], yb[4];
3823  xb[0] = x1; xb[1] = x1; xb[2] = x2; xb[3] = x2;
3824  yb[0] = y1; yb[1] = y2; yb[2] = y2; yb[3] = y1;
3825  PaintFillAreaHatches(4, xb, yb, style0);
3826  return;
3827  }
3828  }
3829  gVirtualPS->DrawBox(x1, y1, x2, y2);
3830  if (option[0] == 'l') {
3832  gVirtualPS->DrawBox(x1, y1, x2, y2);
3833  }
3834  if (option[0] == 's' || option[0] == 'l') gVirtualPS->SetFillStyle(style0);
3835  }
3836 
3837  Modified();
3838 }
3839 
3840 ////////////////////////////////////////////////////////////////////////////////
3841 /// Copy pixmaps of pads laying below pad "stop" into pad "stop". This
3842 /// gives the effect of pad "stop" being transparent.
3843 
3845 {
3846  if (!start) return;
3847  TObject *obj;
3848  if (!fPrimitives) fPrimitives = new TList;
3849  TIter next(start->GetListOfPrimitives());
3850  while ((obj = next())) {
3851  if (obj->InheritsFrom(TPad::Class())) {
3852  if (obj == stop) break;
3853  ((TPad*)obj)->CopyBackgroundPixmap(x, y);
3854  ((TPad*)obj)->CopyBackgroundPixmaps((TPad*)obj, stop, x, y);
3855  }
3856  }
3857 }
3858 
3859 ////////////////////////////////////////////////////////////////////////////////
3860 /// Copy pixmap of this pad as background of the current pad.
3861 
3863 {
3864  int px, py;
3865  XYtoAbsPixel(fX1, fY2, px, py);
3866  if (GetPainter()) GetPainter()->CopyDrawable(GetPixmapID(), px-x, py-y);
3867 }
3868 
3869 ////////////////////////////////////////////////////////////////////////////////
3870 
3872 {
3873  Warning("TPad::PaintFillArea", "Float_t signature is obsolete. Use Double_t signature.");
3874 }
3875 
3876 ////////////////////////////////////////////////////////////////////////////////
3877 /// Paint fill area in CurrentPad World coordinates.
3878 
3880 {
3881  if (nn <3) return;
3882  Int_t n=0;
3884  if (TestBit(TGraph::kClipFrame)) {
3885  xmin = fUxmin; ymin = fUymin; xmax = fUxmax; ymax = fUymax;
3886  } else {
3887  xmin = fX1; ymin = fY1; xmax = fX2; ymax = fY2;
3888  }
3889 
3890  Int_t nc = 2*nn+1;
3891  std::vector<Double_t> x(nc, 0.);
3892  std::vector<Double_t> y(nc, 0.);
3893 
3894  n = ClipPolygon(nn, xx, yy, nc, &x.front(), &y.front(),xmin,ymin,xmax,ymax);
3895  if (!n)
3896  return;
3897 
3898  // Paint the fill area with hatches
3899  Int_t fillstyle = GetPainter()?GetPainter()->GetFillStyle():1;
3900  if (gPad->IsBatch() && GetPainter() && gVirtualPS) fillstyle = gVirtualPS->GetFillStyle();
3901  if (fillstyle >= 3100 && fillstyle < 4000) {
3902  PaintFillAreaHatches(nn, &x.front(), &y.front(), fillstyle);
3903  return;
3904  }
3905 
3906  if (!gPad->IsBatch() && GetPainter())
3907  // invoke the graphics subsystem
3908  GetPainter()->DrawFillArea(n, &x.front(), &y.front());
3909 
3910  if (gVirtualPS)
3911  gVirtualPS->DrawPS(-n, &x.front(), &y.front());
3912 
3913  Modified();
3914 }
3915 
3916 ////////////////////////////////////////////////////////////////////////////////
3917 /// Paint fill area in CurrentPad NDC coordinates.
3918 
3920 {
3921  auto xw = new Double_t[n];
3922  auto yw = new Double_t[n];
3923  for (int i=0; i<n; i++) {
3924  xw[i] = fX1 + x[i]*(fX2 - fX1);
3925  yw[i] = fY1 + y[i]*(fY2 - fY1);
3926  }
3927  PaintFillArea(n, xw, yw, option);
3928  delete [] xw;
3929  delete [] yw;
3930 }
3931 
3932 ////////////////////////////////////////////////////////////////////////////////
3933 /// This function paints hatched fill area according to the FillStyle value
3934 /// The convention for the Hatch is the following:
3935 ///
3936 /// `FillStyle = 3ijk`
3937 ///
3938 /// - i (1-9) : specify the space between each hatch
3939 /// 1 = minimum 9 = maximum
3940 /// the final spacing is i*GetHatchesSpacing(). The hatches spacing
3941 /// is set by SetHatchesSpacing()
3942 /// - j (0-9) : specify angle between 0 and 90 degrees
3943 /// * 0 = 0
3944 /// * 1 = 10
3945 /// * 2 = 20
3946 /// * 3 = 30
3947 /// * 4 = 45
3948 /// * 5 = Not drawn
3949 /// * 6 = 60
3950 /// * 7 = 70
3951 /// * 8 = 80
3952 /// * 9 = 90
3953 /// - k (0-9) : specify angle between 90 and 180 degrees
3954 /// * 0 = 180
3955 /// * 1 = 170
3956 /// * 2 = 160
3957 /// * 3 = 150
3958 /// * 4 = 135
3959 /// * 5 = Not drawn
3960 /// * 6 = 120
3961 /// * 7 = 110
3962 /// * 8 = 100
3963 /// * 9 = 90
3964 
3966 {
3967  static Double_t ang1[10] = { 0., 10., 20., 30., 45.,5., 60., 70., 80., 89.99};
3968  static Double_t ang2[10] = {180.,170.,160.,150.,135.,5.,120.,110.,100., 89.99};
3969 
3970  Int_t fasi = FillStyle%1000;
3971  Int_t idSPA = (Int_t)(fasi/100);
3972  Int_t iAng2 = (Int_t)((fasi-100*idSPA)/10);
3973  Int_t iAng1 = fasi%10;
3974  Double_t dy = 0.003*(Double_t)(idSPA)*gStyle->GetHatchesSpacing();
3976  Short_t lws = 0;
3977  Int_t lss = 0;
3978  Int_t lcs = 0;
3979 
3980  // Save the current line attributes
3981  if (!gPad->IsBatch() && GetPainter()) {
3982  lws = GetPainter()->GetLineWidth();
3983  lss = GetPainter()->GetLineStyle();
3984  lcs = GetPainter()->GetLineColor();
3985  } else {
3986  if (gVirtualPS) {
3987  lws = gVirtualPS->GetLineWidth();
3988  lss = gVirtualPS->GetLineStyle();
3989  lcs = gVirtualPS->GetLineColor();
3990  }
3991  }
3992 
3993  // Change the current line attributes to draw the hatches
3994  if (!gPad->IsBatch() && GetPainter()) {
3995  GetPainter()->SetLineStyle(1);
3998  }
3999  if (gVirtualPS) {
4003  }
4004 
4005  // Draw the hatches
4006  if (ang1[iAng1] != 5.) PaintHatches(dy, ang1[iAng1], nn, xx, yy);
4007  if (ang2[iAng2] != 5.) PaintHatches(dy, ang2[iAng2], nn, xx, yy);
4008 
4009  // Restore the line attributes
4010  if (!gPad->IsBatch() && GetPainter()) {
4011  GetPainter()->SetLineStyle(lss);
4012  GetPainter()->SetLineWidth(lws);
4013  GetPainter()->SetLineColor(lcs);
4014  }
4015  if (gVirtualPS) {
4016  gVirtualPS->SetLineStyle(lss);
4017  gVirtualPS->SetLineWidth(lws);
4018  gVirtualPS->SetLineColor(lcs);
4019  }
4020 }
4021 
4022 ////////////////////////////////////////////////////////////////////////////////
4023 /// This routine draw hatches inclined with the
4024 /// angle "angle" and spaced of "dy" in normalized device
4025 /// coordinates in the surface defined by n,xx,yy.
4026 
4028  Int_t nn, Double_t *xx, Double_t *yy)
4029 {
4030  Int_t i, i1, i2, nbi, m, inv;
4031  Double_t ratiox, ratioy, ymin, ymax, yrot, ycur;
4032  const Double_t angr = TMath::Pi()*(180.-angle)/180.;
4033  const Double_t epsil = 0.0001;
4034  const Int_t maxnbi = 100;
4035  Double_t xli[maxnbi], xlh[2], ylh[2], xt1, xt2, yt1, yt2;
4036  Double_t ll, x, y, x1, x2, y1, y2, a, b, xi, xip, xin, yi, yip;
4037 
4038  Double_t rwxmin = gPad->GetX1();
4039  Double_t rwxmax = gPad->GetX2();
4040  Double_t rwymin = gPad->GetY1();
4041  Double_t rwymax = gPad->GetY2();
4042  ratiox = 1./(rwxmax-rwxmin);
4043  ratioy = 1./(rwymax-rwymin);
4044 
4045  Double_t sina = TMath::Sin(angr), sinb;
4046  Double_t cosa = TMath::Cos(angr), cosb;
4047  if (TMath::Abs(cosa) <= epsil) cosa=0.;
4048  if (TMath::Abs(sina) <= epsil) sina=0.;
4049  sinb = -sina;
4050  cosb = cosa;
4051 
4052  // Values needed to compute the hatches in TRUE normalized space (NDC)
4053  Int_t iw = (Int_t)gPad->GetWw();
4054  Int_t ih = (Int_t)gPad->GetWh();
4055  Double_t x1p,y1p,x2p,y2p;
4056  gPad->GetPadPar(x1p,y1p,x2p,y2p);
4057  iw = (Int_t)(iw*x2p)-(Int_t)(iw*x1p);
4058  ih = (Int_t)(ih*y2p)-(Int_t)(ih*y1p);
4059  Double_t wndc = TMath::Min(1.,(Double_t)iw/(Double_t)ih);
4060  Double_t hndc = TMath::Min(1.,(Double_t)ih/(Double_t)iw);
4061 
4062  // Search ymin and ymax
4063  ymin = 1.;
4064  ymax = 0.;
4065  for (i=1; i<=nn; i++) {
4066  x = wndc*ratiox*(xx[i-1]-rwxmin);
4067  y = hndc*ratioy*(yy[i-1]-rwymin);
4068  yrot = sina*x+cosa*y;
4069  if (yrot > ymax) ymax = yrot;
4070  if (yrot < ymin) ymin = yrot;
4071  }
4072  ymax = (Double_t)((Int_t)(ymax/dy))*dy;
4073 
4074  for (ycur=ymax; ycur>=ymin; ycur=ycur-dy) {
4075  nbi = 0;
4076  for (i=2; i<=nn+1; i++) {
4077  i2 = i;
4078  i1 = i-1;
4079  if (i == nn+1) i2=1;
4080  x1 = wndc*ratiox*(xx[i1-1]-rwxmin);
4081  y1 = hndc*ratioy*(yy[i1-1]-rwymin);
4082  x2 = wndc*ratiox*(xx[i2-1]-rwxmin);
4083  y2 = hndc*ratioy*(yy[i2-1]-rwymin);
4084  xt1 = cosa*x1-sina*y1;
4085  yt1 = sina*x1+cosa*y1;
4086  xt2 = cosa*x2-sina*y2;
4087  yt2 = sina*x2+cosa*y2;
4088 
4089  // Line segment parallel to oy
4090  if (xt1 == xt2) {
4091  if (yt1 < yt2) {
4092  yi = yt1;
4093  yip = yt2;
4094  } else {
4095  yi = yt2;
4096  yip = yt1;
4097  }
4098  if ((yi <= ycur) && (ycur < yip)) {
4099  nbi++;
4100  if (nbi >= maxnbi) return;
4101  xli[nbi-1] = xt1;
4102  }
4103  continue;
4104  }
4105 
4106  // Line segment parallel to ox
4107  if (yt1 == yt2) {
4108  if (yt1 == ycur) {
4109  nbi++;
4110  if (nbi >= maxnbi) return;
4111  xli[nbi-1] = xt1;
4112  nbi++;
4113  if (nbi >= maxnbi) return;
4114  xli[nbi-1] = xt2;
4115  }
4116  continue;
4117  }
4118 
4119  // Other line segment
4120  a = (yt1-yt2)/(xt1-xt2);
4121  b = (yt2*xt1-xt2*yt1)/(xt1-xt2);
4122  if (xt1 < xt2) {
4123  xi = xt1;
4124  xip = xt2;
4125  } else {
4126  xi = xt2;
4127  xip = xt1;
4128  }
4129  xin = (ycur-b)/a;
4130  if ((xi <= xin) && (xin < xip) &&
4131  (TMath::Min(yt1,yt2) <= ycur) &&
4132  (ycur < TMath::Max(yt1,yt2))) {
4133  nbi++;
4134  if (nbi >= maxnbi) return;
4135  xli[nbi-1] = xin;
4136  }
4137  }
4138 
4139  // Sorting of the x coordinates intersections
4140  inv = 0;
4141  m = nbi-1;
4142 L30:
4143  for (i=1; i<=m; i++) {
4144  if (xli[i] < xli[i-1]) {
4145  inv++;
4146  ll = xli[i-1];
4147  xli[i-1] = xli[i];
4148  xli[i] = ll;
4149  }
4150  }
4151  m--;
4152  if (inv == 0) goto L50;
4153  inv = 0;
4154  goto L30;
4155 
4156  // Draw the hatches
4157 L50:
4158  if (nbi%2 != 0) continue;
4159 
4160  for (i=1; i<=nbi; i=i+2) {
4161  // Rotate back the hatches
4162  xlh[0] = cosb*xli[i-1]-sinb*ycur;
4163  ylh[0] = sinb*xli[i-1]+cosb*ycur;
4164  xlh[1] = cosb*xli[i] -sinb*ycur;
4165  ylh[1] = sinb*xli[i] +cosb*ycur;
4166  // Convert hatches' positions from true NDC to WC
4167  xlh[0] = (xlh[0]/wndc)*(rwxmax-rwxmin)+rwxmin;
4168  ylh[0] = (ylh[0]/hndc)*(rwymax-rwymin)+rwymin;
4169  xlh[1] = (xlh[1]/wndc)*(rwxmax-rwxmin)+rwxmin;
4170  ylh[1] = (ylh[1]/hndc)*(rwymax-rwymin)+rwymin;
4171  gPad->PaintLine(xlh[0], ylh[0], xlh[1], ylh[1]);
4172  }
4173  }
4174 }
4175 
4176 ////////////////////////////////////////////////////////////////////////////////
4177 /// Paint line in CurrentPad World coordinates.
4178 
4180 {
4181  Double_t x[2], y[2];
4182  x[0] = x1; x[1] = x2; y[0] = y1; y[1] = y2;
4183 
4184  //If line is totally clipped, return
4185  if (TestBit(TGraph::kClipFrame)) {
4186  if (Clip(x,y,fUxmin,fUymin,fUxmax,fUymax) == 2) return;
4187  } else {
4188  if (Clip(x,y,fX1,fY1,fX2,fY2) == 2) return;
4189  }
4190 
4191  if (!gPad->IsBatch() && GetPainter())
4192  GetPainter()->DrawLine(x[0], y[0], x[1], y[1]);
4193 
4194  if (gVirtualPS) {
4195  gVirtualPS->DrawPS(2, x, y);
4196  }
4197 
4198  Modified();
4199 }
4200 
4201 ////////////////////////////////////////////////////////////////////////////////
4202 /// Paint line in normalized coordinates.
4203 
4205 {
4206  static Double_t xw[2], yw[2];
4207  if (!gPad->IsBatch() && GetPainter())
4208  GetPainter()->DrawLineNDC(u1, v1, u2, v2);
4209 
4210  if (gVirtualPS) {
4211  xw[0] = fX1 + u1*(fX2 - fX1);
4212  xw[1] = fX1 + u2*(fX2 - fX1);
4213  yw[0] = fY1 + v1*(fY2 - fY1);
4214  yw[1] = fY1 + v2*(fY2 - fY1);
4215  gVirtualPS->DrawPS(2, xw, yw);
4216  }
4217 
4218  Modified();
4219 }
4220 
4221 ////////////////////////////////////////////////////////////////////////////////
4222 /// Paint 3-D line in the CurrentPad.
4223 
4225 {
4226  if (!fView) return;
4227 
4228  // convert from 3-D to 2-D pad coordinate system
4229  Double_t xpad[6];
4230  Double_t temp[3];
4231  Int_t i;
4232  for (i=0;i<3;i++) temp[i] = p1[i];
4233  fView->WCtoNDC(temp, &xpad[0]);
4234  for (i=0;i<3;i++) temp[i] = p2[i];
4235  fView->WCtoNDC(temp, &xpad[3]);
4236  PaintLine(xpad[0],xpad[1],xpad[3],xpad[4]);
4237 }
4238 
4239 ////////////////////////////////////////////////////////////////////////////////
4240 /// Paint 3-D line in the CurrentPad.
4241 
4243 {
4244  //take into account perspective view
4245  if (!fView) return;
4246  // convert from 3-D to 2-D pad coordinate system
4247  Double_t xpad[6];
4248  Double_t temp[3];
4249  Int_t i;
4250  for (i=0;i<3;i++) temp[i] = p1[i];
4251  fView->WCtoNDC(temp, &xpad[0]);
4252  for (i=0;i<3;i++) temp[i] = p2[i];
4253  fView->WCtoNDC(temp, &xpad[3]);
4254  PaintLine(xpad[0],xpad[1],xpad[3],xpad[4]);
4255 }
4256 
4257 ////////////////////////////////////////////////////////////////////////////////
4258 /// Paint polyline in CurrentPad World coordinates.
4259 
4261 {
4262  if (n < 2) return;
4263 
4265  if (TestBit(TGraph::kClipFrame)) {
4266  xmin = fUxmin; ymin = fUymin; xmax = fUxmax; ymax = fUymax;
4267  } else {
4268  xmin = fX1; ymin = fY1; xmax = fX2; ymax = fY2;
4269  }
4270  Int_t i, i1=-1,np=1;
4271  for (i=0; i<n-1; i++) {
4272  Double_t x1=x[i];
4273  Double_t y1=y[i];
4274  Double_t x2=x[i+1];
4275  Double_t y2=y[i+1];
4276  Int_t iclip = Clip(&x[i],&y[i],xmin,ymin,xmax,ymax);
4277  if (iclip == 2) {
4278  i1 = -1;
4279  continue;
4280  }
4281  np++;
4282  if (i1 < 0) i1 = i;
4283  if (iclip == 0 && i < n-2) continue;
4284  if (!gPad->IsBatch() && GetPainter())
4285  GetPainter()->DrawPolyLine(np, &x[i1], &y[i1]);
4286  if (gVirtualPS) {
4287  gVirtualPS->DrawPS(np, &x[i1], &y[i1]);
4288  }
4289  if (iclip) {
4290  x[i] = x1;
4291  y[i] = y1;
4292  x[i+1] = x2;
4293  y[i+1] = y2;
4294  }
4295  i1 = -1;
4296  np = 1;
4297  }
4298 
4299  Modified();
4300 }
4301 
4302 ////////////////////////////////////////////////////////////////////////////////
4303 /// Paint polyline in CurrentPad World coordinates.
4304 ///
4305 /// If option[0] == 'C' no clipping
4306 
4308 {
4309  if (n < 2) return;
4310 
4312  Bool_t mustClip = kTRUE;
4313  if (TestBit(TGraph::kClipFrame)) {
4314  xmin = fUxmin; ymin = fUymin; xmax = fUxmax; ymax = fUymax;
4315  } else {
4316  xmin = fX1; ymin = fY1; xmax = fX2; ymax = fY2;
4317  if (option && (option[0] == 'C')) mustClip = kFALSE;
4318  }
4319 
4320  Int_t i, i1=-1, np=1, iclip=0;
4321 
4322  for (i=0; i < n-1; i++) {
4323  Double_t x1=x[i];
4324  Double_t y1=y[i];
4325  Double_t x2=x[i+1];
4326  Double_t y2=y[i+1];
4327  if (mustClip) {
4328  iclip = Clip(&x[i],&y[i],xmin,ymin,xmax,ymax);
4329  if (iclip == 2) {
4330  i1 = -1;
4331  continue;
4332  }
4333  }
4334  np++;
4335  if (i1 < 0) i1 = i;
4336  if (iclip == 0 && i < n-2) continue;
4337  if (!gPad->IsBatch() && GetPainter())
4338  GetPainter()->DrawPolyLine(np, &x[i1], &y[i1]);
4339  if (gVirtualPS) {
4340  gVirtualPS->DrawPS(np, &x[i1], &y[i1]);
4341  }
4342  if (iclip) {
4343  x[i] = x1;
4344  y[i] = y1;
4345  x[i+1] = x2;
4346  y[i+1] = y2;
4347  }
4348  i1 = -1;
4349  np = 1;
4350  }
4351 
4352  Modified();
4353 }
4354 
4355 ////////////////////////////////////////////////////////////////////////////////
4356 /// Paint polyline in CurrentPad NDC coordinates.
4357 
4359 {
4360  if (n <=0) return;
4361 
4362  if (!gPad->IsBatch() && GetPainter())
4363  GetPainter()->DrawPolyLineNDC(n, x, y);
4364 
4365  if (gVirtualPS) {
4366  Double_t *xw = new Double_t[n];
4367  Double_t *yw = new Double_t[n];
4368  for (Int_t i=0; i<n; i++) {
4369  xw[i] = fX1 + x[i]*(fX2 - fX1);
4370  yw[i] = fY1 + y[i]*(fY2 - fY1);
4371  }
4372  gVirtualPS->DrawPS(n, xw, yw);
4373  delete [] xw;
4374  delete [] yw;
4375  }
4376  Modified();
4377 }
4378 
4379 ////////////////////////////////////////////////////////////////////////////////
4380 /// Paint 3-D polyline in the CurrentPad.
4381 
4383 {
4384  if (!fView) return;
4385 
4386  // Loop on each individual line
4387  for (Int_t i = 1; i < n; i++)
4388  PaintLine3D(&p[3*i-3], &p[3*i]);
4389 
4390  Modified();
4391 }
4392 
4393 ////////////////////////////////////////////////////////////////////////////////
4394 /// Paint polymarker in CurrentPad World coordinates.
4395 
4397 {
4398  Int_t n = TMath::Abs(nn);
4400  if (nn > 0 || TestBit(TGraph::kClipFrame)) {
4401  xmin = fUxmin; ymin = fUymin; xmax = fUxmax; ymax = fUymax;
4402  } else {
4403  xmin = fX1; ymin = fY1; xmax = fX2; ymax = fY2;
4404  }
4405  Int_t i,i1=-1,np=0;
4406  for (i=0; i<n; i++) {
4407  if (x[i] >= xmin && x[i] <= xmax && y[i] >= ymin && y[i] <= ymax) {
4408  np++;
4409  if (i1 < 0) i1 = i;
4410  if (i < n-1) continue;
4411  }
4412  if (np == 0) continue;
4413  if (!gPad->IsBatch() && GetPainter())
4414  GetPainter()->DrawPolyMarker(np, &x[i1], &y[i1]);
4415  if (gVirtualPS) {
4416  gVirtualPS->DrawPolyMarker(np, &x[i1], &y[i1]);
4417  }
4418  i1 = -1;
4419  np = 0;
4420  }
4421  Modified();
4422 }
4423 
4424 ////////////////////////////////////////////////////////////////////////////////
4425 /// Paint polymarker in CurrentPad World coordinates.
4426 
4428 {
4429  Int_t n = TMath::Abs(nn);
4431  if (nn > 0 || TestBit(TGraph::kClipFrame)) {
4432  xmin = fUxmin; ymin = fUymin; xmax = fUxmax; ymax = fUymax;
4433  } else {
4434  xmin = fX1; ymin = fY1; xmax = fX2; ymax = fY2;
4435  }
4436  Int_t i,i1=-1,np=0;
4437  for (i=0; i<n; i++) {
4438  if (x[i] >= xmin && x[i] <= xmax && y[i] >= ymin && y[i] <= ymax) {
4439  np++;
4440  if (i1 < 0) i1 = i;
4441  if (i < n-1) continue;
4442  }
4443  if (np == 0) continue;
4444  if (!gPad->IsBatch() && GetPainter())
4445  GetPainter()->DrawPolyMarker(np, &x[i1], &y[i1]);
4446  if (gVirtualPS) {
4447  gVirtualPS->DrawPolyMarker(np, &x[i1], &y[i1]);
4448  }
4449  i1 = -1;
4450  np = 0;
4451  }
4452  Modified();
4453 }
4454 
4455 ////////////////////////////////////////////////////////////////////////////////
4456 /// Paint text in CurrentPad World coordinates.
4457 
4459 {
4460  Modified();
4461 
4462  if (!gPad->IsBatch() && GetPainter())
4464 
4465  if (gVirtualPS) gVirtualPS->Text(x, y, text);
4466 }
4467 
4468 ////////////////////////////////////////////////////////////////////////////////
4469 /// Paint text in CurrentPad World coordinates.
4470 
4471 void TPad::PaintText(Double_t x, Double_t y, const wchar_t *text)
4472 {
4473  Modified();
4474 
4475  if (!gPad->IsBatch() && GetPainter())
4477 
4478  if (gVirtualPS) gVirtualPS->Text(x, y, text);
4479 }
4480 
4481 ////////////////////////////////////////////////////////////////////////////////
4482 /// Paint text in CurrentPad NDC coordinates.
4483 
4485 {
4486  Modified();
4487 
4488  if (!gPad->IsBatch() && GetPainter())
4490 
4491  if (gVirtualPS) {
4492  Double_t x = fX1 + u*(fX2 - fX1);
4493  Double_t y = fY1 + v*(fY2 - fY1);
4494  gVirtualPS->Text(x, y, text);
4495  }
4496 }
4497 
4498 ////////////////////////////////////////////////////////////////////////////////
4499 /// Paint text in CurrentPad NDC coordinates.
4500 
4501 void TPad::PaintTextNDC(Double_t u, Double_t v, const wchar_t *text)
4502 {
4503  Modified();
4504 
4505  if (!gPad->IsBatch() && GetPainter())
4507 
4508  if (gVirtualPS) {
4509  Double_t x = fX1 + u*(fX2 - fX1);
4510  Double_t y = fY1 + v*(fY2 - fY1);
4511  gVirtualPS->Text(x, y, text);
4512  }
4513 }
4514 
4515 ////////////////////////////////////////////////////////////////////////////////
4516 /// Search for an object at pixel position px,py.
4517 ///
4518 /// Check if point is in this pad.
4519 ///
4520 /// If yes, check if it is in one of the sub-pads
4521 ///
4522 /// If found in the pad, compute closest distance of approach
4523 /// to each primitive.
4524 ///
4525 /// If one distance of approach is found to be within the limit Distancemaximum
4526 /// the corresponding primitive is selected and the routine returns.
4527 
4528 TPad *TPad::Pick(Int_t px, Int_t py, TObjLink *&pickobj)
4529 {
4530  //the two following statements are necessary under NT (multithreaded)
4531  //when a TCanvas object is being created and a thread calling TPad::Pick
4532  //before the TPad constructor has completed in the other thread
4533  if (gPad == 0) return 0; //Andy Haas
4534  if (GetListOfPrimitives() == 0) return 0; //Andy Haas
4535 
4536  Int_t dist;
4537  // Search if point is in pad itself
4538  Double_t x = AbsPixeltoX(px);
4539  Double_t y = AbsPixeltoY(py);
4540  if (this != gPad->GetCanvas()) {
4541  if (!((x >= fX1 && x <= fX2) && (y >= fY1 && y <= fY2))) return 0;
4542  }
4543 
4544  // search for a primitive in this pad or its sub-pads
4545  static TObjOptLink dummyLink(0,""); //place holder for when no link available
4546  TPad *padsav = (TPad*)gPad;
4547  gPad = this; // since no drawing will be done, don't use cd() for efficiency reasons
4548  TPad *pick = 0;
4549  TPad *picked = this;
4550  pickobj = 0;
4551  if (DistancetoPrimitive(px,py) < fgMaxPickDistance) {
4552  dummyLink.SetObject(this);
4553  pickobj = &dummyLink;
4554  }
4555 
4556  // Loop backwards over the list of primitives. The first non-pad primitive
4557  // found is the selected one. However, we have to keep going down the
4558  // list to see if there is maybe a pad overlaying the primitive. In that
4559  // case look into the pad for a possible primitive. Once a pad has been
4560  // found we can terminate the loop.
4561  Bool_t gotPrim = kFALSE; // true if found a non pad primitive
4563 
4564  //We can have 3d stuff in pad. If canvas prefers to draw
4565  //such stuff with OpenGL, the selection of 3d objects is
4566  //a gl viewer business so, in first cycle we do not
4567  //call DistancetoPrimitive for TAtt3D descendants.
4568  //In case of gl we first try to select 2d object first.
4569 
4570  while (lnk) {
4571  TObject *obj = lnk->GetObject();
4572 
4573  //If canvas prefers GL, all 3d objects must be drawn/selected by
4574  //gl viewer
4575  if (obj->InheritsFrom(TAtt3D::Class()) && fEmbeddedGL) {
4576  lnk = lnk->Prev();
4577  continue;
4578  }
4579 
4580  fPadPointer = obj;
4581  if (obj->InheritsFrom(TPad::Class())) {
4582  pick = ((TPad*)obj)->Pick(px, py, pickobj);
4583  if (pick) {
4584  picked = pick;
4585  break;
4586  }
4587  } else if (!gROOT->GetEditorMode()) {
4588  if (!gotPrim) {
4589  if (!obj->TestBit(kCannotPick)) {
4590  dist = obj->DistancetoPrimitive(px, py);
4591  if (dist < fgMaxPickDistance) {
4592  pickobj = lnk;
4593  gotPrim = kTRUE;
4594  if (dist == 0) break;
4595  }
4596  }
4597  }
4598  }
4599 
4600  lnk = lnk->Prev();
4601  }
4602 
4603  //if no primitive found, check if we have a TView
4604  //if yes, return the view except if you are in the lower or upper X range
4605  //of the pad.
4606  //In case canvas prefers gl, fView existence
4607  //automatically means viewer3d existence. (?)
4608 
4609  if (fView && !gotPrim) {
4610  Double_t dx = 0.05*(fUxmax-fUxmin);
4611  if ((x > fUxmin + dx) && (x < fUxmax-dx)) {
4612 
4613  if (fEmbeddedGL) {
4614  //No 2d stuff was selected, but we have gl-viewer. Let it select an object in
4615  //scene (or select itself). In any case it'll internally call
4616  //gPad->SetSelected(ptr) as, for example, hist painter does.
4617  py -= Int_t((1 - GetHNDC() - GetYlowNDC()) * GetWh());
4618  px -= Int_t(GetXlowNDC() * GetWw());
4619  fViewer3D->DistancetoPrimitive(px, py);
4620  }
4621  else
4622  dummyLink.SetObject(fView);
4623  }
4624  }
4625 
4626  if (picked->InheritsFrom(TButton::Class())) {
4627  TButton *button = (TButton*)picked;
4628  if (!button->IsEditable()) pickobj = 0;
4629  }
4630 
4631  if (TestBit(kCannotPick)) {
4632 
4633  if (picked == this) {
4634  // cannot pick pad itself!
4635  picked = 0;
4636  }
4637 
4638  }
4639 
4640  gPad = padsav;
4641  return picked;
4642 }
4643 
4644 ////////////////////////////////////////////////////////////////////////////////
4645 /// Pop pad to the top of the stack.
4646 
4648 {
4649  if (!fMother) return;
4650  if (!fMother->TestBit(kNotDeleted)) return;
4651  if (!fPrimitives) fPrimitives = new TList;
4652  if (this == fMother->GetListOfPrimitives()->Last()) return;
4653 
4655  TObject *obj;
4656  while ((obj = next()))
4657  if (obj == this) {
4658  char *opt = StrDup(next.GetOption());
4660  fMother->GetListOfPrimitives()->AddLast(this, opt);
4661  delete [] opt;
4662  return;
4663  }
4664 }
4665 
4666 ////////////////////////////////////////////////////////////////////////////////
4667 /// Save Pad contents in a file in one of various formats.
4668 ///
4669 /// - if filename is "", the file produced is padname.ps
4670 /// - if filename starts with a dot, the padname is added in front
4671 /// - if filename contains .eps, an Encapsulated Postscript file is produced
4672 /// - if filename contains .pdf, a PDF file is produced NOTE: TMathText will be converted to TLatex; q.e.d., symbols only available in TMathText will not render properly.
4673 /// - if filename contains .svg, a SVG file is produced
4674 /// - if filename contains .tex, a TeX file is produced
4675 /// - if filename contains .gif, a GIF file is produced
4676 /// - if filename contains .gif+NN, an animated GIF file is produced See comments in TASImage::WriteImage for meaning of NN and other .gif sufix variants
4677 /// - if filename contains .xpm, a XPM file is produced
4678 /// - if filename contains .png, a PNG file is produced
4679 /// - if filename contains .jpg, a JPEG file is produced NOTE: JPEG's lossy compression will make all sharp edges fuzzy.
4680 /// - if filename contains .tiff, a TIFF file is produced
4681 /// - if filename contains .C or .cxx, a C++ macro file is produced
4682 /// - if filename contains .root, a Root file is produced
4683 /// - if filename contains .xml, a XML file is produced
4684 /// - if filename contains .json, a JSON file is produced
4685 ///
4686 /// See comments in TPad::SaveAs or the TPad::Print function below
4687 
4688 void TPad::Print(const char *filename) const
4689 {
4690  ((TPad*)this)->SaveAs(filename);
4691 }
4692 
4693 ////////////////////////////////////////////////////////////////////////////////
4694 /// Auxiliary function. Returns kTRUE if list contains an object inherited
4695 /// from TImage
4696 
4698 {
4699  TIter next(li);
4700  TObject *obj;
4701 
4702  while ((obj = next())) {
4703  if (obj->InheritsFrom(TImage::Class())) {
4704  return kTRUE;
4705  } else if (obj->InheritsFrom(TPad::Class())) {
4706  if (ContainsTImage(((TPad*)obj)->GetListOfPrimitives())) {
4707  return kTRUE;
4708  }
4709  }
4710  }
4711  return kFALSE;
4712 }
4713 
4714 ////////////////////////////////////////////////////////////////////////////////
4715 /// Save Canvas contents in a file in one of various formats.
4716 ///
4717 /// option can be:
4718 /// - 0 as "ps"
4719 /// - "ps" Postscript file is produced (see special cases below)
4720 /// - "Portrait" Postscript file is produced (Portrait)
4721 /// - "Landscape" Postscript file is produced (Landscape)
4722 /// - "Title:" The character string after "Title:" becomes a table
4723 /// of content entry (for PDF files).
4724 /// - "eps" an Encapsulated Postscript file is produced
4725 /// - "Preview" an Encapsulated Postscript file with preview is produced.
4726 /// - "EmbedFonts" a PDF file with embedded fonts is generated.
4727 /// - "pdf" a PDF file is produced NOTE: TMathText will be converted to TLatex; q.e.d., symbols only available in TMathText will not render properly.
4728 /// - "svg" a SVG file is produced
4729 /// - "tex" a TeX file is produced
4730 /// - "gif" a GIF file is produced
4731 /// - "gif+NN" an animated GIF file is produced, where NN is delay in 10ms units NOTE: See other variants for looping animation in TASImage::WriteImage
4732 /// - "xpm" a XPM file is produced
4733 /// - "png" a PNG file is produced
4734 /// - "jpg" a JPEG file is produced. NOTE: JPEG's lossy compression will make all sharp edges fuzzy.
4735 /// - "tiff" a TIFF file is produced
4736 /// - "cxx" a C++ macro file is produced
4737 /// - "xml" a XML file
4738 /// - "json" a JSON file
4739 /// - "root" a ROOT binary file
4740 ///
4741 /// filename = 0 - filename is defined by the GetName and its
4742 /// extension is defined with the option
4743 ///
4744 /// When Postscript output is selected (ps, eps), the canvas is saved
4745 /// to filename.ps or filename.eps. The aspect ratio of the canvas is preserved
4746 /// on the Postscript file. When the "ps" option is selected, the Postscript
4747 /// page will be landscape format if the canvas is in landscape format, otherwise
4748 /// portrait format is selected.
4749 ///
4750 /// The physical size of the Postscript page is the one selected in the
4751 /// current style. This size can be modified via TStyle::SetPaperSize.
4752 ///
4753 /// Examples:
4754 /// ~~~ {.cpp}
4755 /// gStyle->SetPaperSize(TStyle::kA4); //default
4756 /// gStyle->SetPaperSize(TStyle::kUSLetter);
4757 /// ~~~
4758 /// where TStyle::kA4 and TStyle::kUSLetter are defined in the enum
4759 /// EPaperSize in TStyle.h
4760 ///
4761 /// An alternative is to call:
4762 /// ~~~ {.cpp}
4763 /// gStyle->SetPaperSize(20,26); same as kA4
4764 /// or gStyle->SetPaperSize(20,24); same as kUSLetter
4765 /// ~~~
4766 /// The above numbers take into account some margins and are in centimeters.
4767 ///
4768 /// ### The "Preview" option
4769 ///
4770 /// The "Preview" option allows to generate a preview (in the TIFF format) within
4771 /// the Encapsulated Postscript file. This preview can be used by programs like
4772 /// MSWord to visualize the picture on screen. The "Preview" option relies on the
4773 /// "epstool" command (http://www.cs.wisc.edu/~ghost/gsview/epstool.htm).
4774 ///
4775 /// Example:
4776 /// ~~~ {.cpp}
4777 /// canvas->Print("example.eps","Preview");
4778 /// ~~~
4779 ///
4780 /// ### The "EmbedFonts" option
4781 ///
4782 /// The "EmbedFonts" option allows to embed the fonts used in a PDF file inside
4783 /// that file. This option relies on the "gs" command (https://ghostscript.com).
4784 ///
4785 /// Example:
4786 /// ~~~ {.cpp}
4787 /// canvas->Print("example.pdf","EmbedFonts");
4788 /// ~~~
4789 ///
4790 /// ### Writing several canvases to the same Postscript or PDF file:
4791 ///
4792 /// - if the Postscript or PDF file name finishes with "(", the file is not closed
4793 /// - if the Postscript or PDF file name finishes with ")" and the file has been opened
4794 /// with "(", the file is closed.
4795 ///
4796 /// Example:
4797 /// ~~~ {.cpp}
4798 /// {
4799 /// TCanvas c1("c1");
4800 /// h1.Draw();
4801 /// c1.Print("c1.ps("); //write canvas and keep the ps file open
4802 /// h2.Draw();
4803 /// c1.Print("c1.ps"); canvas is added to "c1.ps"
4804 /// h3.Draw();
4805 /// c1.Print("c1.ps)"); canvas is added to "c1.ps" and ps file is closed
4806 /// }
4807 /// ~~~
4808 /// In the previous example replacing "ps" by "pdf" will create a multi-pages PDF file.
4809 ///
4810 /// Note that the following sequence writes the canvas to "c1.ps" and closes the ps file.:
4811 /// ~~~ {.cpp}
4812 /// TCanvas c1("c1");
4813 /// h1.Draw();
4814 /// c1.Print("c1.ps");
4815 /// ~~~
4816 /// The TCanvas::Print("file.ps(") mechanism is very useful, but it can be
4817 /// a little inconvenient to have the action of opening/closing a file
4818 /// being atomic with printing a page. Particularly if pages are being
4819 /// generated in some loop one needs to detect the special cases of first
4820 /// and last page and then munge the argument to Print() accordingly.
4821 ///
4822 /// The "[" and "]" can be used instead of "(" and ")".
4823 ///
4824 /// Example:
4825 /// ~~~ {.cpp}
4826 /// c1.Print("file.ps["); // No actual print, just open file.ps
4827 /// for (int i=0; i<10; ++i) {
4828 /// // fill canvas for context i
4829 /// // ...
4830 ///
4831 /// c1.Print("file.ps"); // actually print canvas to file
4832 /// }// end loop
4833 /// c1.Print("file.ps]"); // No actual print, just close.
4834 /// ~~~
4835 /// As before, the same macro is valid for PDF files.
4836 ///
4837 /// It is possible to print a canvas into an animated GIF file by specifying the
4838 /// file name as "myfile.gif+" or "myfile.gif+NN", where NN*10ms is delay
4839 /// between the subimages' display. If NN is omitted the delay between
4840 /// subimages is zero. Each picture is added in the animation thanks to a loop
4841 /// similar to the following one:
4842 /// ~~~ {.cpp}
4843 /// for (int i=0; i<10; ++i) {
4844 /// // fill canvas for context i
4845 /// // ...
4846 ///
4847 /// c1.Print("file.gif+5"); // print canvas to GIF file with 50ms delays
4848 /// }// end loop
4849 /// ~~~
4850 /// The delay between each frame must be specified in each Print() statement.
4851 /// If the file "myfile.gif" already exists, the new frame are appended at
4852 /// the end of the file. To avoid this, delete it first with gSystem->Unlink(myfile.gif);
4853 /// If you want the gif file to repeat or loop forever, check TASImage::WriteImage documentation
4854 
4855 void TPad::Print(const char *filenam, Option_t *option)
4856 {
4857  TString psname, fs1 = filenam;
4858 
4859  // "[" and "]" are special characters for ExpandPathName. When they are at the end
4860  // of the file name (see help) they must be removed before doing ExpandPathName.
4861  if (fs1.EndsWith("[")) {
4862  fs1.Replace((fs1.Length()-1),1," ");
4863  gSystem->ExpandPathName(fs1);
4864  fs1.Replace((fs1.Length()-1),1,"[");
4865  } else if (fs1.EndsWith("]")) {
4866  fs1.Replace((fs1.Length()-1),1," ");
4867  gSystem->ExpandPathName(fs1);
4868  fs1.Replace((fs1.Length()-1),1,"]");
4869  } else {
4870  gSystem->ExpandPathName(fs1);
4871  }
4872 
4873  // Set the default option as "Postscript" (Should be a data member of TPad)
4874  const char *opt_default = "ps";
4875 
4876  TString opt = !option ? opt_default : option;
4877  Bool_t image = kFALSE;
4878 
4879  if (!fs1.Length()) {
4880  psname = GetName();
4881  psname += opt;
4882  } else {
4883  psname = fs1;
4884  }
4885 
4886  // lines below protected against case like c1->SaveAs( "../ps/cs.ps" );
4887  if (psname.BeginsWith('.') && (psname.Contains('/') == 0)) {
4888  psname = GetName();
4889  psname.Append(fs1);
4890  psname.Prepend("/");
4891  psname.Prepend(gEnv->GetValue("Canvas.PrintDirectory","."));
4892  }
4893  if (!gPad->IsBatch() && fCanvas && GetPainter())
4895 
4896  // Save pad/canvas in alternative formats
4898  if (strstr(opt, "gif+")) {
4899  gtype = TImage::kAnimGif;
4900  image = kTRUE;
4901  } else if (strstr(opt, "gif")) {
4902  gtype = TImage::kGif;
4903  image = kTRUE;
4904  } else if (strstr(opt, "png")) {
4905  gtype = TImage::kPng;
4906  image = kTRUE;
4907  } else if (strstr(opt, "jpg")) {
4908  gtype = TImage::kJpeg;
4909  image = kTRUE;
4910  } else if (strstr(opt, "tiff")) {
4911  gtype = TImage::kTiff;
4912  image = kTRUE;
4913  } else if (strstr(opt, "xpm")) {
4914  gtype = TImage::kXpm;
4915  image = kTRUE;
4916  } else if (strstr(opt, "bmp")) {
4917  gtype = TImage::kBmp;
4918  image = kTRUE;
4919  }
4920 
4921  Int_t wid = 0;
4922  if (!GetCanvas()) return;
4923  if (!gROOT->IsBatch() && image) {
4924  if ((gtype == TImage::kGif) && !ContainsTImage(fPrimitives)) {
4925  wid = (this == GetCanvas()) ? GetCanvas()->GetCanvasID() : GetPixmapID();
4926  Color_t hc = gPad->GetCanvas()->GetHighLightColor();
4927  gPad->GetCanvas()->SetHighLightColor(-1);
4928  gPad->Modified();
4929  gPad->Update();
4930  if (GetPainter()){
4931  GetPainter()->SelectDrawable(wid);
4932  GetPainter()->SaveImage(this, psname.Data(), gtype);
4933  }
4934  if (!gSystem->AccessPathName(psname.Data())) {
4935  Info("Print", "GIF file %s has been created", psname.Data());
4936  }
4937  gPad->GetCanvas()->SetHighLightColor(hc);
4938  return;
4939  }
4940  if (gtype != TImage::kUnknown) {
4941  Color_t hc = gPad->GetCanvas()->GetHighLightColor();
4942  gPad->GetCanvas()->SetHighLightColor(-1);
4943  gPad->Modified();
4944  gPad->Update();
4945  gVirtualX->Update(1);
4946  gSystem->Sleep(30); // synchronize
4947  if (GetPainter()) GetPainter()->SaveImage(this, psname, gtype);
4948  if (!gSystem->AccessPathName(psname)) {
4949  Info("Print", "file %s has been created", psname.Data());
4950  }
4951  gPad->GetCanvas()->SetHighLightColor(hc);
4952  } else {
4953  Warning("Print", "Unsupported image format %s", psname.Data());
4954  }
4955  return;
4956  }
4957 
4958  //==============Save pad/canvas as a C++ script==============================
4959  if (strstr(opt,"cxx")) {
4960  GetCanvas()->SaveSource(psname, "");
4961  return;
4962  }
4963 
4964  //==============Save pad/canvas as a root file===============================
4965  if (strstr(opt,"root")) {
4966  if (gDirectory) gDirectory->SaveObjectAs(this,psname.Data(),"");
4967  return;
4968  }
4969 
4970  //==============Save pad/canvas as a XML file================================
4971  if (strstr(opt,"xml")) {
4972  // Plugin XML driver
4973  if (gDirectory) gDirectory->SaveObjectAs(this,psname.Data(),"");
4974  return;
4975  }
4976 
4977  //==============Save pad/canvas as a JSON file================================
4978  if (strstr(opt,"json")) {
4979  if (gDirectory) gDirectory->SaveObjectAs(this,psname.Data(),"");
4980  return;
4981  }
4982 
4983  //==============Save pad/canvas as a SVG file================================
4984  if (strstr(opt,"svg")) {
4985  gVirtualPS = (TVirtualPS*)gROOT->GetListOfSpecials()->FindObject(psname);
4986 
4987  Bool_t noScreen = kFALSE;
4988  if (!GetCanvas()->IsBatch() && GetCanvas()->GetCanvasID() == -1) {
4989  noScreen = kTRUE;
4990  GetCanvas()->SetBatch(kTRUE);
4991  }
4992 
4993  TPad *padsav = (TPad*)gPad;
4994  cd();
4995 
4996  if (!gVirtualPS) {
4997  // Plugin Postscript/SVG driver
4998  TPluginHandler *h;
4999  if ((h = gROOT->GetPluginManager()->FindHandler("TVirtualPS", "svg"))) {
5000  if (h->LoadPlugin() == -1)
5001  return;
5002  h->ExecPlugin(0);
5003  }
5004  }
5005 
5006  // Create a new SVG file
5007  if (gVirtualPS) {
5008  gVirtualPS->SetName(psname);
5009  gVirtualPS->Open(psname);
5011  gVirtualPS->NewPage();
5012  }
5013  Paint();
5014  if (noScreen) GetCanvas()->SetBatch(kFALSE);
5015 
5016  if (!gSystem->AccessPathName(psname)) Info("Print", "SVG file %s has been created", psname.Data());
5017 
5018  delete gVirtualPS;
5019  gVirtualPS = 0;
5020  padsav->cd();
5021 
5022  return;
5023  }
5024 
5025  //==============Save pad/canvas as a TeX file================================
5026  if (strstr(opt,"tex")) {
5027  gVirtualPS = (TVirtualPS*)gROOT->GetListOfSpecials()->FindObject(psname);
5028 
5029  Bool_t noScreen = kFALSE;
5030  if (!GetCanvas()->IsBatch() && GetCanvas()->GetCanvasID() == -1) {
5031  noScreen = kTRUE;
5032  GetCanvas()->SetBatch(kTRUE);
5033  }
5034 
5035  TPad *padsav = (TPad*)gPad;
5036  cd();
5037 
5038  if (!gVirtualPS) {
5039  // Plugin Postscript/SVG driver
5040  TPluginHandler *h;
5041  if ((h = gROOT->GetPluginManager()->FindHandler("TVirtualPS", "tex"))) {
5042  if (h->LoadPlugin() == -1)
5043  return;
5044  h->ExecPlugin(0);
5045  }
5046  }
5047 
5048  // Create a new TeX file
5049  if (gVirtualPS) {
5050  gVirtualPS->SetName(psname);
5051  gVirtualPS->Open(psname);
5053  gVirtualPS->NewPage();
5054  }
5055  Paint();
5056  if (noScreen) GetCanvas()->SetBatch(kFALSE);
5057 
5058  if (!gSystem->AccessPathName(psname)) Info("Print", "TeX file %s has been created", psname.Data());
5059 
5060  delete gVirtualPS;
5061  gVirtualPS = 0;
5062  padsav->cd();
5063 
5064  return;
5065  }
5066 
5067  //==============Save pad/canvas as a Postscript file=========================
5068 
5069  // in case we read directly from a Root file and the canvas
5070  // is not on the screen, set batch mode
5071 
5072  Bool_t mustOpen = kTRUE;
5073  Bool_t mustClose = kTRUE;
5074  Bool_t copen=kFALSE, cclose=kFALSE, copenb=kFALSE, ccloseb=kFALSE;
5075  if (!image) {
5076  // The parenthesis mechanism is only valid for PS and PDF files.
5077  copen = psname.EndsWith("("); if (copen) psname[psname.Length()-1] = 0;
5078  cclose = psname.EndsWith(")"); if (cclose) psname[psname.Length()-1] = 0;
5079  copenb = psname.EndsWith("["); if (copenb) psname[psname.Length()-1] = 0;
5080  ccloseb = psname.EndsWith("]"); if (ccloseb) psname[psname.Length()-1] = 0;
5081  }
5082  gVirtualPS = (TVirtualPS*)gROOT->GetListOfSpecials()-