// @(#)root/gpad:$Id: TCreatePrimitives.cxx,v 1.0

/*************************************************************************
 * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TCreatePrimitives                                                    //
//                                                                      //
// Creates new primitives.                                              //
//                                                                      //
// The functions in this static class are called by TPad::ExecuteEvent  //
// to create new primitives in gPad from the TPad toolbar.              //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#include "TCanvas.h"
#include "TStyle.h"
#include "TGraph.h"
#include "TArrow.h"
#include "TPavesText.h"
#include "TPaveLabel.h"
#include "TCurlyArc.h"
#include "TArc.h"
#include "TLatex.h"
#include "TMarker.h"
#include "TDiamond.h"
#include "TGroupButton.h"
#include "TVirtualPad.h"
#include "TCreatePrimitives.h"
#include "TROOT.h"
#include "TSystem.h"
#include "TMath.h"
#include "KeySymbols.h"

//______________________________________________________________________________
TCreatePrimitives::TCreatePrimitives()
{
   // TCreatePrimitives default constructor
}


//______________________________________________________________________________
TCreatePrimitives::~TCreatePrimitives()
{
   // TCreatePrimitives destructor
}


//______________________________________________________________________________
void TCreatePrimitives::Ellipse(Int_t event, Int_t px, Int_t py, Int_t mode)
{
   //  Create a new arc/ellipse in this gPad
   //
   //  Click left button to indicate arrow starting position.
   //  Release left button to terminate the arrow.
   //

   static Double_t x0, y0, x1, y1;

   static Int_t pxold, pyold;
   static Int_t px0, py0;
   static Int_t linedrawn;
   Double_t xc,yc,r1,r2;
   TEllipse *el = 0;

   switch (event) {

   case kButton1Down:
      gVirtualX->SetLineColor(-1);
      x0 = gPad->AbsPixeltoX(px);
      y0 = gPad->AbsPixeltoY(py);
      px0   = px; py0   = py;
      pxold = px; pyold = py;
      linedrawn = 0;
      break;

   case kButton1Motion:
      if (linedrawn) gVirtualX->DrawBox(px0, py0, pxold, pyold, TVirtualX::kHollow);
      pxold = px;
      pyold = py;
      linedrawn = 1;
      gVirtualX->DrawBox(px0, py0, pxold, pyold, TVirtualX::kHollow);
      break;

   case kButton1Up:
      x1 = gPad->AbsPixeltoX(px);
      y1 = gPad->AbsPixeltoY(py);
      if (gPad->GetLogx()) {
         x0 = TMath::Power(10,x0);
         x1 = TMath::Power(10,x1);
      }
      if (gPad->GetLogy()) {
         y0 = TMath::Power(10,y0);
         y1 = TMath::Power(10,y1);
      }
      xc = 0.5*(x0+x1);
      yc = 0.5*(y0+y1);
      if (mode == kArc) {
         r1 = 0.5*TMath::Abs(x1-x0);
         el = new TArc(xc, yc, r1);
      }
      if (mode == kEllipse) {
         r1 = 0.5*TMath::Abs(x1-x0);
         r2 = 0.5*TMath::Abs(y1-y0);
         el = new TEllipse(xc, yc, r1, r2);
      }
      TCanvas *canvas = gPad->GetCanvas();
      if (canvas) canvas->FeedbackMode(kFALSE);
      gPad->Modified(kTRUE);
      if (el) el->Draw();
      if (canvas) canvas->Selected((TPad*)gPad, el, event);
      gROOT->SetEditorMode();
      break;
   }
}


//______________________________________________________________________________
void TCreatePrimitives::Line(Int_t event, Int_t px, Int_t py, Int_t mode)
{
   // Create a new line/arrow in this gPad
   //
   //  Click left button to indicate arrow starting position.
   //  Release left button to terminate the arrow.
   //

   static Double_t x0, y0, x1, y1;

   static Int_t pxold, pyold;
   static Int_t px0, py0;
   static Int_t linedrawn;
   TLine *line;
   TArrow *arrow;
   TCurlyLine *cline;
   Double_t radius, phimin,phimax;

   switch (event) {

   case kButton1Down:
      gVirtualX->SetLineColor(-1);
      x0 = gPad->AbsPixeltoX(px);
      y0 = gPad->AbsPixeltoY(py);
      px0   = px; py0   = py;
      pxold = px; pyold = py;
      linedrawn = 0;
      break;

   case kButton1Motion:
      if (linedrawn) gVirtualX->DrawLine(px0, py0, pxold, pyold);
      pxold = px;
      pyold = py;
      linedrawn = 1;
      gVirtualX->DrawLine(px0, py0, pxold, pyold);
      break;

   case kButton1Up:
      if (px == px0 && py == py0) break;
      x1 = gPad->AbsPixeltoX(px);
      y1 = gPad->AbsPixeltoY(py);
      gPad->Modified(kTRUE);
      TCanvas *canvas = gPad->GetCanvas();
      if (gPad->GetLogx()) {
         x0 = TMath::Power(10,x0);
         x1 = TMath::Power(10,x1);
      }
      if (gPad->GetLogy()) {
         y0 = TMath::Power(10,y0);
         y1 = TMath::Power(10,y1);
      }
      if (mode == kLine) {
         line = new TLine(x0,y0,x1,y1);
         line->Draw();
         if (canvas) canvas->Selected((TPad*)gPad, line, event);
      }
      if (mode == kArrow) {
         arrow = new TArrow(x0,y0,x1,y1
                            , TArrow::GetDefaultArrowSize()
                            , TArrow::GetDefaultOption());
         arrow->Draw();
         if (canvas) canvas->Selected((TPad*)gPad, arrow, event);
      }
      if (mode == kCurlyLine) {
         cline = new TCurlyLine(x0,y0,x1,y1
                                , TCurlyLine::GetDefaultWaveLength()
                                , TCurlyLine::GetDefaultAmplitude());
         cline->Draw();
         if (canvas) canvas->Selected((TPad*)gPad, cline, event);
      }
      if (mode == kCurlyArc) {
         //calculate radius in pixels and convert to users x
         radius = gPad->PixeltoX((Int_t)(TMath::Sqrt((Double_t)((px-px0)*(px-px0) + (py-py0)*(py-py0)))))
                 - gPad->PixeltoX(0);
         phimin = 0;
         phimax = 360;
         cline = new TCurlyArc(x0,y0,radius,phimin,phimax
                                , TCurlyArc::GetDefaultWaveLength()
                                , TCurlyArc::GetDefaultAmplitude());
         cline->Draw();
         if (canvas) canvas->Selected((TPad*)gPad, cline, event);
      }
      gROOT->SetEditorMode();
      break;
   }
}


//______________________________________________________________________________
void TCreatePrimitives::Pad(Int_t event, Int_t px, Int_t py, Int_t)
{
   // Create a new pad in gPad
   //
   //  Click left button to indicate one corner of the pad
   //  Click left button to indicate the opposite corner
   //
   //  The new pad is inserted in the pad where the first point is selected.
   //

   static Int_t px1old, py1old, px2old, py2old;
   static Int_t px1, py1, px2, py2, pxl, pyl, pxt, pyt;
   static Bool_t boxdrawn;
   static TPad *padsav;
   Double_t xlow, ylow, xup, yup;
   TPad * newpad;

   Int_t  n = 0;
   TObject *obj;
   TIter next(gPad->GetListOfPrimitives());

   while ((obj = next())) {
      if (obj->InheritsFrom(TPad::Class())) {
         n++;
      }
   }

   switch (event) {

   case kButton1Down:
      padsav = (TPad*)gPad;
      gPad->cd();
      gVirtualX->SetLineColor(-1);
      px1 = gPad->XtoAbsPixel(gPad->GetX1());
      py1 = gPad->YtoAbsPixel(gPad->GetY1());
      px2 = gPad->XtoAbsPixel(gPad->GetX2());
      py2 = gPad->YtoAbsPixel(gPad->GetY2());
      px1old = px; py1old = py;
      boxdrawn = 0;
      break;

   case kButton1Motion:
      if (boxdrawn) gVirtualX->DrawBox(pxl, pyl, pxt, pyt, TVirtualX::kHollow);
      px2old = px;
      px2old = TMath::Max(px2old, px1);
      px2old = TMath::Min(px2old, px2);
      py2old = py;
      py2old = TMath::Max(py2old, py2);
      py2old = TMath::Min(py2old, py1);
      pxl = TMath::Min(px1old, px2old);
      pxt = TMath::Max(px1old, px2old);
      pyl = TMath::Max(py1old, py2old);
      pyt = TMath::Min(py1old, py2old);
      boxdrawn = 1;
      gVirtualX->DrawBox(pxl, pyl, pxt, pyt, TVirtualX::kHollow);
      break;

   case kButton1Up:
      gPad->Modified(kTRUE);
      gPad->SetDoubleBuffer(1);   // Turn on double buffer mode
      gVirtualX->SetDrawMode(TVirtualX::kCopy);       // set drawing mode back to normal (copy) mode
      xlow = (Double_t(pxl) - Double_t(px1))/(Double_t(px2) - Double_t(px1));
      ylow = (Double_t(py1) - Double_t(pyl))/(Double_t(py1) - Double_t(py2));
      xup  = (Double_t(pxt) - Double_t(px1))/(Double_t(px2) - Double_t(px1));
      yup  = (Double_t(py1) - Double_t(pyt))/(Double_t(py1) - Double_t(py2));
      gROOT->SetEditorMode();
      boxdrawn = 0;
      if (xup <= xlow || yup <= ylow) return;
      newpad = new TPad(Form("%s_%d",gPad->GetName(),n+1),"newpad",xlow, ylow, xup, yup);
      if (newpad->IsZombie()) break;
      newpad->SetFillColor(gStyle->GetPadColor());
      newpad->Draw();
      TCanvas *canvas = gPad->GetCanvas();
      if (canvas) canvas->Selected((TPad*)gPad, newpad, event);
      padsav->cd();
      break;
   }
}


//______________________________________________________________________________
void TCreatePrimitives::Pave(Int_t event, Int_t px, Int_t py, Int_t mode)
{
   // Create a new pavetext in gPad
   //
   //  Click left button to indicate one corner of the pavelabel.
   //  Release left button at the opposite corner.
   //

   static Double_t x0, y0, x1, y1;

   static Int_t pxold, pyold;
   static Int_t px0, py0;
   static Int_t linedrawn;
   Double_t temp;
   Double_t xp0,xp1,yp0,yp1;
   static TObject *pave = 0;

   if (mode == kPaveLabel)
      ((TPad *)gPad)->EventPave();

   switch (event) {

   case kKeyPress:
      if (mode == kPaveLabel) {
         if ((py == kKey_Return) || (py == kKey_Enter)) {
            TString s(pave->GetTitle());
            Int_t l = s.Length();
            s.Remove(l-1);
            ((TPaveLabel*)pave)->SetLabel(s.Data());
            gSystem->ProcessEvents();
            gPad->Modified(kTRUE);
            gROOT->SetEditorMode();
            gPad->Update();
            pave = 0;
         } else if (py == kKey_Backspace) {
            TString s(pave->GetTitle());
            Int_t l = s.Length();
            if (l>1) {
               s.Replace(l-2, 2, "<");
               ((TPaveLabel*)pave)->SetLabel(s.Data());
               gPad->Modified(kTRUE);
               gPad->Update();
            }
         } else if (isprint(py)) {
            TString s(pave->GetTitle());
            Int_t l = s.Length();
            s.Insert(l-1,(char)py);
            ((TPaveLabel*)pave)->SetLabel(s.Data());
            gPad->Modified(kTRUE);
            gPad->Update();
         }
      }
      break;

   case kButton1Down:
      gVirtualX->SetLineColor(-1);
      x0 = gPad->AbsPixeltoX(px);
      y0 = gPad->AbsPixeltoY(py);
      px0   = px; py0   = py;
      pxold = px; pyold = py;
      linedrawn = 0;
      break;

   case kButton1Motion:
      if (linedrawn) gVirtualX->DrawBox(px0, py0, pxold, pyold, TVirtualX::kHollow);
      pxold = px;
      pyold = py;
      linedrawn = 1;
      gVirtualX->DrawBox(px0, py0, pxold, pyold, TVirtualX::kHollow);
      break;

   case kButton1Up:
      if (px == px0) px = px0+10;
      if (py == py0) py = py0-10;
      x1 = gPad->AbsPixeltoX(px);
      y1 = gPad->AbsPixeltoY(py);

      if (x1 < x0) {temp = x0; x0 = x1; x1 = temp;}
      if (y1 < y0) {temp = y0; y0 = y1; y1 = temp;}
      xp0 = gPad->PadtoX(x0);
      xp1 = gPad->PadtoX(x1);
      yp0 = gPad->PadtoY(y0);
      yp1 = gPad->PadtoY(y1);
      if (mode == kPave)      pave = new TPave(xp0,yp0,xp1,yp1);
      if (mode == kPaveText ) pave = new TPaveText(xp0,yp0,xp1,yp1);
      if (mode == kPavesText) pave = new TPavesText(xp0,yp0,xp1,yp1);
      if (mode == kDiamond)   pave = new TDiamond(x0,y0,x1,y1);
      if (mode == kPaveLabel) {
         ((TPad *)gPad)->StartEditing();
         gSystem->ProcessEvents();
         if (mode == kPaveLabel) {
            pave = new TPaveLabel(xp0,yp0,xp1,yp1,"<");
            pave->Draw();
            gPad->Modified(kTRUE);
            gPad->Update();
            break;
         }
      }
      TCanvas *canvas = gPad->GetCanvas();
      if (canvas) canvas->FeedbackMode(kFALSE);
      gPad->Modified(kTRUE);
      if (pave) pave->Draw();
      if (canvas) canvas->Selected((TPad*)gPad, pave, event);
      gROOT->SetEditorMode();
      gPad->Update();
      break;
   }
}


//______________________________________________________________________________
void TCreatePrimitives::PolyLine(Int_t event, Int_t px, Int_t py, Int_t mode)
{
   // Create a new PolyLine in gPad
   //
   //  Click left button to indicate a new point
   //  Click left button at same place or double click to close the polyline
   //

   static Int_t pxold, pyold, px1old, py1old;
   static Int_t npoints = 0;
   static Int_t linedrawn = 0;
   Int_t dp;
   static TGraph *gr = 0;
   TCanvas *canvas = gPad->GetCanvas();

   switch (event) {

   case kButton1Double:
   case kButton1Down:
      if (npoints == 0) {
         gVirtualX->SetLineColor(-1);
      } else {
         if (canvas) canvas->FeedbackMode(kFALSE);
         gVirtualX->DrawLine(px1old, py1old, pxold, pyold);
      }
      // stop collecting new points if new point is close ( < 5 pixels) of previous point
      if (event == kButton1Double) {
         px = px1old;
         py = py1old;
      }
      dp = TMath::Abs(px-px1old) +TMath::Abs(py-py1old);
      if (npoints && dp < 5) {
         gPad->Modified(kTRUE);
         if (mode == kCutG && gr) {
            gr->Set(gr->GetN() + 1);
            Double_t x0 = 0., y0 = 0.;
            gr->GetPoint(0, x0, y0);
            gr->SetPoint(npoints, x0, y0);
         }
         npoints = 0;
         linedrawn = 0;
         gr = 0;
         gROOT->SetEditorMode();
         break;
      }
      if (npoints == 1 && gr == 0) {
         if (mode == kPolyLine) {
            gr = new TGraph(2);
            gr->ResetBit(TGraph::kClipFrame);

         } else {
            gr = (TGraph*)gROOT->ProcessLineFast(
                 Form("new %s(\"CUTG\",%d",
                      gROOT->GetCutClassName(),2));
         }
         gr->SetPoint(0, gPad->PadtoX(gPad->AbsPixeltoX(px1old)),
                         gPad->PadtoY(gPad->AbsPixeltoY(py1old)));
         gr->SetPoint(1, gPad->PadtoX(gPad->AbsPixeltoX(px)),
                         gPad->PadtoY(gPad->AbsPixeltoY(py)));
         npoints = 2;
         gr->Draw("L");
         if (canvas) canvas->Selected((TPad*)gPad, gr, event);
      } else if (npoints > 1) {
         gr->Set(gr->GetN() + 1);
         gr->SetPoint(npoints, gPad->PadtoX(gPad->AbsPixeltoX(px)),
                         gPad->PadtoY(gPad->AbsPixeltoY(py)));
         npoints ++;
         gPad->Modified();
         gPad->Update();
      } else {
         npoints = 1;
      }
      px1old = px; py1old = py;
      pxold  = px; pyold  = py;
      linedrawn = 0;
      break;

   case kMouseMotion:
   case kButton1Motion:
   case kButton1Up:
      if (npoints < 1) return;
      if (canvas) canvas->FeedbackMode(kTRUE);
      if (linedrawn) {
         gVirtualX->DrawLine(px1old, py1old, pxold, pyold);
      }
      pxold = px;
      pyold = py;
      linedrawn = 1;
      gVirtualX->DrawLine(px1old, py1old, pxold, pyold);
      break;
   }
}


//______________________________________________________________________________
void TCreatePrimitives::Text(Int_t event, Int_t px, Int_t py, Int_t mode)
{
   // Create a new TLatex at the cursor position in gPad
   //
   // Click left button to indicate the text position
   //

   static TLatex *text = 0;
   static Double_t x, y;

   switch (event) {

   case kKeyPress:
      if ((py == kKey_Return) || (py == kKey_Enter)) {
         TString s(text->GetTitle());
         Int_t l = s.Length();
         s.Remove(l-1);
         text->SetText(x,y,s.Data());
         gSystem->ProcessEvents();
         gPad->Modified(kTRUE);
         gROOT->SetEditorMode();
         gPad->Update();
         text = 0;
      } else if (py == kKey_Backspace) {
         TString s(text->GetTitle());
         Int_t l = s.Length();
         if (l>1) {
            s.Replace(l-2, 2, "<");
            text->SetText(x,y,s.Data());
            gPad->Modified(kTRUE);
            gPad->Update();
         }
      } else if (isprint(py)) {
         TString s(text->GetTitle());
         Int_t l = s.Length();
         s.Insert(l-1,(char)py);
         text->SetText(x,y,s.Data());
         gPad->Modified(kTRUE);
         gPad->Update();
      }
      break;

   case kButton1Down:
      if (text) {
         TString s(text->GetTitle());
         Int_t l = s.Length();
         s.Remove(l-1);
         text->SetText(x,y,s.Data());
      }

      x = gPad->AbsPixeltoX(px);
      y = gPad->AbsPixeltoY(py);
      if (gPad->GetLogx()) x = TMath::Power(10,x);
      if (gPad->GetLogy()) y = TMath::Power(10,y);

      if (mode == kMarker) {
         TMarker *marker;
         marker = new TMarker(x,y,gStyle->GetMarkerStyle());
         marker->Draw();
         gROOT->SetEditorMode();
         break;
      }

      ((TPad *)gPad)->StartEditing();
      gSystem->ProcessEvents();

      text = new TLatex(x,y,"<");
      text->Draw();
      gPad->Modified(kTRUE);
      gPad->Update();

      break;
   }
}