    // @(#)root/ged:$Name:  $:$Id: TGedDrawPanel.cxx,v 1.5 2004/02/19 15:36:45 brun Exp $
    // Author: Marek Biskup, Ilka Antcheva 15/08/2003

    /*************************************************************************
     * 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.             *
     *************************************************************************/

    //////////////////////////////////////////////////////////////////////////
    //                                                                      //
    // TGedDrawPanel                                                        //
    //                                                                      //
    //////////////////////////////////////////////////////////////////////////

    #include "TGedDrawPanel.h"
    #include "TCanvas.h"
    #include "TGButton.h"
    #include "TH1.h"
    #include "TF1.h"
    #include "TGDoubleSlider.h"
    #include <ctype.h>
    #include <snprintf.h>

    ClassImp(TGedDrawPanel)

    enum {
      kType = 1,
         kTypeHist        = 0,
         kTypeLego1       = 1,
         kTypeLego2       = 2,
         kTypeLego3       = 3,
         kTypeSurf        = 4,
         kTypeSurfColors  = 5,
         kTypeSurfContour = 6,
         kTypeGouraud     = 7,
         kTypeCont0       = 8,
         kTypeCont1       = 9,
         kTypeCont2       = 10,
         kTypeCont3       = 11,
      kCoords = 2,
         kCoordsCartesian = 0,
         kCoordsPolar     = 1,
         kCoordsCylindric = 2,
         kCoordsSpheric   = 3,
      kErrors = 3,
         kErrorsNone = 0,
         kErrorsEdges      = 1,
         kErrorsRectangles = 2,
         kErrorsFill       = 3,
         kErrorsContour    = 4,

      kDraw     = 1000,
      kDefaults = 1001,
      kClose    = 1002
    };

    static const char * const TypeData[] = {
       "hist",  "lego1", "lego2", "lego3",
       "surf1", "surf2", "surf3", "surf4",
       "cont1", "cont2", "cont3", "cont4"
    };

    static const char * const CoordsData[] = {
       "", "pol", "cyl", "sph"
    };

    static const char * const ErrorsData[] = {
       "", "e1", "e2", "e3", "e4"
    };

    TGedDrawPanel* TGedDrawPanel::fDrawPanel = 0;

    //______________________________________________________________________________
     void TGedDrawPanel::ShowPanel(TObject* histogram, TPad* pad)
    {
       if (!fDrawPanel)
          fDrawPanel = new TGedDrawPanel();
       fDrawPanel->SetHistogram(histogram, pad);
       fDrawPanel->MapRaised();
    }

    //______________________________________________________________________________
     TGedDrawPanel::TGedDrawPanel() :
       TGMainFrame(gClient->GetRoot(), 300, 500, kVerticalFrame)
    {
       fHistogram =0;
       fRefPad = 0;
       Build();
       MapWindow();
    }

    //______________________________________________________________________________
     TGedDrawPanel::~TGedDrawPanel()
    {
       fDrawPanel = 0;
       Cleanup();
    }

    //______________________________________________________________________________
     void TGedDrawPanel::Build()
    {
       Int_t i;
       TGGroupFrame *type = new TGGroupFrame(this, "type", kHorizontalFrame);
       AddFrame(type, new TGLayoutHints(kLHintsExpandX));

       TGCompositeFrame *t1 = new TGCompositeFrame(type, 10, 10);
       type->AddFrame(t1, new TGLayoutHints(kLHintsExpandY));
       TGCompositeFrame *t2 = new TGCompositeFrame(type, 10, 10);
       type->AddFrame(t2, new TGLayoutHints(kLHintsExpandY));
       TGCompositeFrame *t3 = new TGCompositeFrame(type, 10, 10);
       type->AddFrame(t3, new TGLayoutHints(kLHintsExpandY));
       TGCompositeFrame *t4 = new TGCompositeFrame(type, 10, 10);
       type->AddFrame(t4, new TGLayoutHints(kLHintsExpandY));

       fType[0] = new TGRadioButton(t1, "hist", kTypeHist);
       t1->AddFrame(fType[0], new TGLayoutHints(kLHintsTop));

       fType[1] = new TGRadioButton(t2, "lego1", kTypeLego1);
       t2->AddFrame(fType[1], new TGLayoutHints(kLHintsTop));

       fType[2] = new TGRadioButton(t2, "lego2", kTypeLego2);
       t2->AddFrame(fType[2], new TGLayoutHints(kLHintsTop));

       fType[3] = new TGRadioButton(t2, "lego3", kTypeLego3);
       t2->AddFrame(fType[3], new TGLayoutHints(kLHintsTop));

       fType[4] = new TGRadioButton(t3, "surf", kTypeSurf);
       t3->AddFrame(fType[4], new TGLayoutHints(kLHintsTop));

       fType[5] = new TGRadioButton(t3, "surf/colors", kTypeSurfColors);
       t3->AddFrame(fType[5], new TGLayoutHints(kLHintsTop));

       fType[6] = new TGRadioButton(t3, "surf/contour", kTypeSurfContour);
       t3->AddFrame(fType[6], new TGLayoutHints(kLHintsTop));

       fType[7] = new TGRadioButton(t3, "gouraud", kTypeGouraud);
       t3->AddFrame(fType[7], new TGLayoutHints(kLHintsTop));

       fType[8] = new TGRadioButton(t4, "cont0", kTypeCont0);
       t4->AddFrame(fType[8], new TGLayoutHints(kLHintsTop));

       fType[9] = new TGRadioButton(t4, "cont1", kTypeCont1);
       t4->AddFrame(fType[9], new TGLayoutHints(kLHintsTop));

       fType[10] = new TGRadioButton(t4, "cont2", kTypeCont2);
       t4->AddFrame(fType[10], new TGLayoutHints(kLHintsTop));

       fType[11] = new TGRadioButton(t4, "cont3", kTypeCont3);
       t4->AddFrame(fType[11], new TGLayoutHints(kLHintsTop));

       for (i = 0; i < 12; i++) {
          fType[i]->SetUserData( (void*) kType);
          fType[i]->Associate(this);
       }

       TGCompositeFrame *row2 = new TGCompositeFrame(this, 10, 10, kHorizontalFrame);
       AddFrame(row2, new TGLayoutHints(kLHintsExpandX));

       TGGroupFrame *coords = new TGGroupFrame(row2, "coords", kVerticalFrame);
       row2->AddFrame(coords, new TGLayoutHints(kLHintsExpandX | kLHintsExpandY));

       fCoords[0] = new TGRadioButton(coords, "cartesian", kCoordsCartesian);
       coords->AddFrame(fCoords[0], new TGLayoutHints(kLHintsTop));

       fCoords[1] = new TGRadioButton(coords, "polar", kCoordsPolar);
       coords->AddFrame(fCoords[1], new TGLayoutHints(kLHintsTop));

       fCoords[2] = new TGRadioButton(coords, "cylindric", kCoordsCylindric);
       coords->AddFrame(fCoords[2], new TGLayoutHints(kLHintsTop));

       fCoords[3] = new TGRadioButton(coords, "spheric", kCoordsSpheric);
       coords->AddFrame(fCoords[3], new TGLayoutHints(kLHintsTop));

       for (i = 0; i < 4; i++) {
          fCoords[i]->SetUserData( (void*) kCoords);
          fCoords[i]->Associate(this);
       }

       TGGroupFrame *errors = new TGGroupFrame(row2, "errors", kVerticalFrame);
       row2->AddFrame(errors, new TGLayoutHints(kLHintsExpandX));

       fErrors[0] = new TGRadioButton(errors, "No errors", kErrorsNone);
       errors->AddFrame(fErrors[0], new TGLayoutHints(kLHintsTop));

       fErrors[1] = new TGRadioButton(errors, "E1: edges", kErrorsEdges);
       errors->AddFrame(fErrors[1], new TGLayoutHints(kLHintsTop));

       fErrors[2] = new TGRadioButton(errors, "E2: rectangles", kErrorsRectangles);
       errors->AddFrame(fErrors[2], new TGLayoutHints(kLHintsTop));

       fErrors[3] = new TGRadioButton(errors, "E3: fill", kErrorsFill);
       errors->AddFrame(fErrors[3], new TGLayoutHints(kLHintsTop));

       fErrors[4] = new TGRadioButton(errors, "E4: contour", kErrorsContour);
       errors->AddFrame(fErrors[4], new TGLayoutHints(kLHintsTop));

       for (i = 0; i < 5; i++) {
          fErrors[i]->SetUserData( (void*) kErrors);
          fErrors[i]->Associate(this);
       }

       fSlider = new TGDoubleHSlider(this, 1, 1);
       AddFrame(fSlider, new TGLayoutHints(kLHintsExpandX));

       TGCompositeFrame *row3 = new TGCompositeFrame(this, 10, 10, kHorizontalFrame);
       AddFrame(row3, new TGLayoutHints(kLHintsExpandX));
       TGTextButton *draw = new TGTextButton(row3, "Draw", kDraw);
       row3->AddFrame(draw, new TGLayoutHints(kLHintsLeft));
       draw->Associate(this);

       TGTextButton *defaults = new TGTextButton(row3, "Defaults", kDefaults);
       row3->AddFrame(defaults, new TGLayoutHints(kLHintsLeft));
       defaults->Associate(this);

       TGTextButton *close = new TGTextButton(row3, "Close", kClose);
       row3->AddFrame(close, new TGLayoutHints(kLHintsLeft));
       close->Associate(this);

       MapSubwindows();
       Resize(GetDefaultSize());
       Reset();
    }

    //______________________________________________________________________________
     void TGedDrawPanel::SetRadio(TGRadioButton **group, Int_t count, Int_t index)
    {
       for (int i = 0; i < count; i++) {
          if (i == index)
             group[i]->SetState(kButtonDown);
          else
             group[i]->SetState(kButtonUp);
       }
    }

    //______________________________________________________________________________
     Bool_t TGedDrawPanel::ProcessMessage(Long_t msg, Long_t parm1, Long_t parm2)
    {
       switch (GET_MSG(msg)) {
          case kC_COMMAND:
             switch (GET_SUBMSG(msg)) {
                case kCM_RADIOBUTTON:
                   switch (parm2) {
                      case kType:
                         SetRadio(fType, 12, parm1);
                         break;
                      case kCoords:
                         SetRadio(fCoords, 4, parm1);
                         break;
                      case kErrors:
                         SetRadio(fErrors, 5, parm1);
                         break;
                      default:
                         break;
                   }
                   break;
                case kCM_BUTTON:
                   switch (parm1) {
                      case kDraw:
                         DrawHistogram();
                         break;
                      case kDefaults:
                         Reset();
                         break;
                      case kClose:
                         CloseWindow();
                         break;
                      default:
                         break;
                   }
             }
             break;
          case kC_HSLIDER:
             ProcessSlider(GET_SUBMSG(msg));
             break;
       }
       return kTRUE;
    }

    //______________________________________________________________________________
     void TGedDrawPanel::CloseWindow()
    {
       DeleteWindow();
    }

    //______________________________________________________________________________
     void TGedDrawPanel::ProcessSlider(Long_t submsg)
    {
       // Control mousse events when slider is used in a drawpanel

       Float_t xpmin = fSlider->GetMinPosition();
       Float_t xpmax = fSlider->GetMaxPosition();

       static TH1 *h1;
       static Bool_t done = kFALSE;
       static Int_t px1,py1,px2,py2,nbins;
       static Int_t pxmin,pxmax;
       static Float_t xmin,xmax,ymin,ymax,xleft,xright;
       Int_t first,last;

       if (!fRefPad) return;
       fRefPad->cd();

       fRefPad->GetCanvas()->FeedbackMode(kTRUE);
       gVirtualX->SetLineWidth(2);
       gVirtualX->SetLineColor(-1);

       switch (submsg) {
          case kSL_PRESS:      // button 1 down
             if (done) gVirtualX->DrawBox(px1, py1, px2, py2, TVirtualX::kHollow);
             done = kTRUE;
             if (fHistogram->InheritsFrom("TF1")) {
                h1 = (TH1*)((TF1*)fHistogram)->GetHistogram();
             } else if (fHistogram->InheritsFrom("TH1")) {
                h1 = (TH1*)fHistogram;
             } else {
                h1 = 0;
                break;
             }
             nbins = h1->GetXaxis()->GetNbins();
             xmin  = fRefPad->GetUxmin();
             xmax  = fRefPad->GetUxmax();
             xleft = xmin+(xmax-xmin)*xpmin;
             xright= xmin+(xmax-xmin)*xpmax;
             ymin  = fRefPad->GetUymin();
             ymax  = fRefPad->GetUymax();
             px1   = fRefPad->XtoAbsPixel(xleft);
             py1   = fRefPad->YtoAbsPixel(ymin);
             px2   = fRefPad->XtoAbsPixel(xright);
             py2   = fRefPad->YtoAbsPixel(ymax);
             pxmin = fRefPad->XtoAbsPixel(xmin);
             pxmax = fRefPad->XtoAbsPixel(xmax);
             gVirtualX->DrawBox(px1, py1, px2, py2, TVirtualX::kHollow);
             break;
          case kSL_POS:        //kButton1Motion:
             if (h1 == 0) break;
             gVirtualX->DrawBox(px1, py1, px2, py2, TVirtualX::kHollow);
             first  = 1 + Int_t(nbins*xpmin);
             last   =     Int_t(nbins*xpmax);
             xleft  = fRefPad->XtoPad(h1->GetXaxis()->GetBinLowEdge(first));
             xright = fRefPad->XtoPad(h1->GetXaxis()->GetBinLowEdge(last)+h1->GetXaxis()->GetBinWidth(last));
             px1 = fRefPad->XtoAbsPixel(xleft);
             px2 = fRefPad->XtoAbsPixel(xright);
             if (px1 < pxmin) px1 = pxmin;
             if (px2 > pxmax) px2 = pxmax;
             gVirtualX->DrawBox(px1, py1, px2, py2, TVirtualX::kHollow);
             break;
          case kSL_RELEASE:
             gVirtualX->DrawBox(px1, py1, px2, py2, TVirtualX::kHollow);
             done = kFALSE;
             fRefPad->GetCanvas()->FeedbackMode(kFALSE);
             gVirtualX->SetLineWidth(-1);
             break;
       }
    }

    //______________________________________________________________________________
     void TGedDrawPanel::AddRadioOption(TGRadioButton **group,
                                       const char * const * const data, Int_t count)
    {
       for (int i = 0; i < count; i++)
          if (group[i]->GetState() == kButtonDown)
             fOption += data[i];
    }

    //______________________________________________________________________________
     void TGedDrawPanel::ReadOption()
    {
       fOption.Resize(0);
       AddRadioOption(fType, TypeData, 12);
       AddRadioOption(fCoords, CoordsData, 4);
       AddRadioOption(fErrors, ErrorsData, 5);
    }

    //______________________________________________________________________________
     void TGedDrawPanel::ParseRadio(const char * const option, TGRadioButton **group,
                                   const char * const * const data, Int_t count)
    {
       for (int i = 0; i < count; i++) {
          if (data[i][0] != '0')
             if (strstr(option, data[i]))
                group[i]->SetState(kButtonDown);
       }
    }

    //______________________________________________________________________________
     void TGedDrawPanel::ParseOption(const char * const option)
    {
       char o[128];
       int i;

       for (i = 0; i < 128 - 1 && option[i] != '0'; i++)
          o[i] = tolower(option[i]);

       o[i] = 0;
       ParseRadio(o, fType, TypeData, 12);
       ParseRadio(o, fCoords, CoordsData, 4);
       ParseRadio(o, fErrors, ErrorsData, 5);
    }

    //______________________________________________________________________________
     void TGedDrawPanel::DrawHistogram()
    {
       if (!fHistogram || !fRefPad) return;

       ReadOption();
       fRefPad->cd();
       TH1 *h1;

       if (fHistogram->InheritsFrom("TF1")) {
          h1 = (TH1*)((TF1*)fHistogram)->GetHistogram();
       } else if (fHistogram->InheritsFrom("TH1")) {
          h1 = (TH1*)fHistogram;
       } else {
          h1 = 0;
       }

       if (h1) {
          Int_t nbins   = h1->GetXaxis()->GetNbins();
          Int_t first   = 1 + Int_t(nbins*fSlider->GetMinPosition());
          Int_t last    =     Int_t(nbins*fSlider->GetMaxPosition());
          h1->GetXaxis()->SetRange(first, last);
       }

       Int_t keep = fHistogram->TestBit(kCanDelete);
       fHistogram->SetBit(kCanDelete, 0);
       fHistogram->Draw((char*)fOption.Data());
       fHistogram->SetBit(kCanDelete, keep);
       fRefPad->Update();
    }

    //______________________________________________________________________________
     void TGedDrawPanel::Reset()
    {
       SetRadio(fType, 12, 0);
       SetRadio(fCoords, 4, 0);
       SetRadio(fErrors, 5, 0);
       fSlider->SetPosition(0.0, 1.0);
    }

    //______________________________________________________________________________
     void TGedDrawPanel::Clean()
    {
       SetRadio(fType, 12, -1);
       SetRadio(fCoords, 4, -1);
       SetRadio(fErrors, 5, -1);
       fSlider->SetPosition(0.0, 1.0);
    }

    //______________________________________________________________________________
     void TGedDrawPanel::SetHistogram(TObject* histogram, TPad* pad)
    {
      // Set default draw panel options.

       ParseOption(histogram->GetDrawOption());
       fRefPad    = pad;
       fHistogram = histogram;

       Clean();

       char cmd[64];
       if (fHistogram) {
          snprintf(cmd, 64, "drawpanel: %s",fHistogram->GetName());
          SetWindowName(cmd);
          ParseOption(histogram->GetDrawOption());
       }
    }