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