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