// @(#)root/gpad:$Id$
// Author: Rene Brun   23/11/96

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

#include "Riostream.h"
#include "TROOT.h"
#include "TSlider.h"
#include "TSliderBox.h"

#include <string.h>

ClassImp(TSliderBox)

//______________________________________________________________________________
//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TSliderBox                                                           //
//                                                                      //
//  The moving box in a TSlider                                         //
//                                                                      //
//////////////////////////////////////////////////////////////////////////


//______________________________________________________________________________
TSliderBox::TSliderBox(): TWbox()
{
   // SliderBox default constructor.

   fSlider = 0;
}


//______________________________________________________________________________
TSliderBox::TSliderBox(Double_t x1, Double_t y1,Double_t x2, Double_t  y2, Color_t color, Short_t bordersize, Short_t bordermode)
           :TWbox(x1,y1,x2,y2,color,bordersize,bordermode)
{
   // SliderBox normal constructor.

   fSlider = 0;
}


//______________________________________________________________________________
TSliderBox::~TSliderBox()
{
   // SliderBox default destructor.
}


//______________________________________________________________________________
void TSliderBox::ExecuteEvent(Int_t event, Int_t px, Int_t py)
{
   // Interaction with a slider.

   const Int_t kMaxDiff = 5;
   const Int_t kMinSize = 20;

   static Int_t px1, px2, py1, py2, pxl, pyl, pxt, pyt, pxold, pyold;
   static Int_t px1p, px2p, py1p, py2p;
   static Bool_t  pL, pR, pTop, pBot, pINSIDE;
   Int_t  wx, wy;
   Bool_t doing_again = kFALSE;
   Bool_t opaque  = gPad->OpaqueMoving();
   Bool_t ropaque = gPad->OpaqueResizing();

   TVirtualPad  *parent = gPad;

   Int_t border   = parent->GetBorderSize();
   Int_t pxpadmin = parent->XtoAbsPixel(parent->GetX1()) + border;
   Int_t pxpadmax = parent->XtoAbsPixel(parent->GetX2()) - border;
   Int_t pypadmin = parent->YtoAbsPixel(parent->GetY1()) - border;
   Int_t pypadmax = parent->YtoAbsPixel(parent->GetY2()) + border;

   Bool_t vertical = kTRUE;
   if (pxpadmax-pxpadmin > pypadmin-pypadmax) vertical = kFALSE;

again:

   switch (event) {

   case kButton1Down:

      gVirtualX->SetLineColor(-1);
      TAttLine::Modify();  //Change line attributes only if necessary
      if (GetFillColor())
         gVirtualX->SetLineColor(GetFillColor());
      else
         gVirtualX->SetLineColor(1);
      gVirtualX->SetLineWidth(2);

      // No break !!!

   case kMouseMotion:

      px1 = gPad->XtoAbsPixel(GetX1());
      py1 = gPad->YtoAbsPixel(GetY1());
      px2 = gPad->XtoAbsPixel(GetX2());
      py2 = gPad->YtoAbsPixel(GetY2());

      if (px1 < px2) {
         pxl = px1;
         pxt = px2;
      } else {
         pxl = px2;
         pxt = px1;
      }
      if (py1 < py2) {
         pyl = py1;
         pyt = py2;
      } else {
         pyl = py2;
         pyt = py1;
      }

      px1p = pxpadmin;
      py1p = pypadmin;
      px2p = pxpadmax;
      py2p = pypadmax;

      pL = pR  = pTop = pBot = pINSIDE = kFALSE;

      if (vertical && (px > pxl+kMaxDiff && px < pxt-kMaxDiff) &&
          TMath::Abs(py - pyl) < kMaxDiff) {             // top edge
         pxold = pxl; pyold = pyl; pTop = kTRUE;
         gPad->SetCursor(kTopSide);
      }

      if (vertical && (px > pxl+kMaxDiff && px < pxt-kMaxDiff) &&
          TMath::Abs(py - pyt) < kMaxDiff) {             // bottom edge
         pxold = pxt; pyold = pyt; pBot = kTRUE;
         gPad->SetCursor(kBottomSide);
      }

      if (!vertical && (py > pyl+kMaxDiff && py < pyt-kMaxDiff) &&
          TMath::Abs(px - pxl) < kMaxDiff) {             // left edge
         pxold = pxl; pyold = pyl; pL = kTRUE;
         gPad->SetCursor(kLeftSide);
      }

      if (!vertical && (py > pyl+kMaxDiff && py < pyt-kMaxDiff) &&
          TMath::Abs(px - pxt) < kMaxDiff) {             // right edge
         pxold = pxt; pyold = pyt; pR = kTRUE;
         gPad->SetCursor(kRightSide);
      }

      if ((px > pxl+kMaxDiff && px < pxt-kMaxDiff) &&
          (py > pyl+kMaxDiff && py < pyt-kMaxDiff)) {    // inside box
         pxold = px; pyold = py; pINSIDE = kTRUE;
         if (event == kButton1Down)
            gPad->SetCursor(kMove);
         else
            gPad->SetCursor(kCross);
      }

      fResizing = kFALSE;
      if ( pL || pR || pTop || pBot)
         fResizing = kTRUE;

      if ( !pL && !pR && !pTop && !pBot && !pINSIDE)
         gPad->SetCursor(kCross);

      break;

   case kButton1Motion:

      wx = wy = 0;

      if (pTop) {
         if (!ropaque) gVirtualX->DrawBox(px1, py1, px2, py2, TVirtualX::kHollow);
         py2 += py - pyold;
         if (py2 > py1-kMinSize) { py2 = py1-kMinSize; wy = py2; }
         if (py2 < py2p) { py2 = py2p; wy = py2; }
         if (!ropaque) gVirtualX->DrawBox(px1, py1, px2, py2, TVirtualX::kHollow);
      }
      if (pBot) {
         if (!ropaque) gVirtualX->DrawBox(px1, py1, px2, py2, TVirtualX::kHollow);
         py1 += py - pyold;
         if (py1 < py2+kMinSize) { py1 = py2+kMinSize; wy = py1; }
         if (py1 > py1p) { py1 = py1p; wy = py1; }
         if (!ropaque) gVirtualX->DrawBox(px1, py1, px2, py2, TVirtualX::kHollow);
      }
      if (pL) {
         if (!ropaque) gVirtualX->DrawBox(px1, py1, px2, py2, TVirtualX::kHollow);
         px1 += px - pxold;
         if (px1 > px2-kMinSize) { px1 = px2-kMinSize; wx = px1; }
         if (px1 < px1p) { px1 = px1p; wx = px1; }
         if (!ropaque) gVirtualX->DrawBox(px1, py1, px2, py2, TVirtualX::kHollow);
      }
      if (pR) {
         if (!ropaque) gVirtualX->DrawBox(px1, py1, px2, py2, TVirtualX::kHollow);
         px2 += px - pxold;
         if (px2 < px1+kMinSize) { px2 = px1+kMinSize; wx = px2; }
         if (px2 > px2p) { px2 = px2p; wx = px2; }
         if (!ropaque) gVirtualX->DrawBox(px1, py1, px2, py2, TVirtualX::kHollow);
      }
      if (pINSIDE) {
         if (!opaque) gVirtualX->DrawBox(px1, py1, px2, py2, TVirtualX::kHollow);  // draw the old box
         Int_t dx = px - pxold;
         Int_t dy = py - pyold;
         px1 += dx; py1 += dy; px2 += dx; py2 += dy;
         if (px1 < px1p) { dx = px1p - px1; px1 += dx; px2 += dx; wx = px+dx; }
         if (px2 > px2p) { dx = px2 - px2p; px1 -= dx; px2 -= dx; wx = px-dx; }
         if (py1 > py1p) { dy = py1 - py1p; py1 -= dy; py2 -= dy; wy = py-dy; }
         if (py2 < py2p) { dy = py2p - py2; py1 += dy; py2 += dy; wy = py+dy; }
         if (!opaque) gVirtualX->DrawBox(px1, py1, px2, py2, TVirtualX::kHollow);  // draw the new box
      }

      if (wx || wy) {
         if (wx) px = wx;
         if (wy) py = wy;
         gVirtualX->Warp(px, py);
      }

      pxold = px;
      pyold = py;

      if ((pINSIDE && opaque) || (fResizing && ropaque)) {
         event = kButton1Up;
         doing_again = kTRUE;
         goto again;
      }

      break;

   case kButton1Up:

      if (pTop || pBot || pL || pR || pINSIDE) {
         fX1 = gPad->AbsPixeltoX(px1);
         fY1 = gPad->AbsPixeltoY(py1);
         fX2 = gPad->AbsPixeltoX(px2);
         fY2 = gPad->AbsPixeltoY(py2);
      }

      if (pINSIDE) {
         // if it was not a pad that was moved then it must have been
         // a box or something like that so we have to redraw the pad
         if (parent == gPad) gPad->Modified(kTRUE);
         if (!doing_again) gPad->SetCursor(kCross);
      }

      if (pTop || pBot ||  pL || pR )
         gPad->Modified(kTRUE);

      // Restore original event type
      if (doing_again)
         event = kButton1Motion;
      else {
         gVirtualX->SetLineColor(-1);
         gVirtualX->SetLineWidth(-1);
      }

      break;
   }


   // Give control to object using the slider

   Float_t xpmin,xpmax;
   if (vertical) {
      xpmin = Float_t(pypadmin-py1)/Float_t(pypadmin-pypadmax);
      xpmax = Float_t(pypadmin-py2)/Float_t(pypadmin-pypadmax);
   } else {  //vertical slider
      xpmin = Float_t(px1-pxpadmin)/Float_t(pxpadmax-pxpadmin);
      xpmax = Float_t(px2-pxpadmin)/Float_t(pxpadmax-pxpadmin);
   }
   fSlider->SetMinimum(xpmin);
   fSlider->SetMaximum(xpmax);

   //A user method to execute?
   Int_t lenMethod = strlen(fSlider->GetMethod());
   if (event == kButton1Up && lenMethod > 0 ) {
      gPad->SetCursor(kWatch);
      gROOT->ProcessLine(fSlider->GetMethod());
      return;
   }

   //An object connected to this slider?
   TObject *obj = fSlider->GetObject();
   if (obj) {
      obj->ExecuteEvent(event,0,0);
   }
}


//______________________________________________________________________________
void TSliderBox::SavePrimitive(ostream &, Option_t * /*= ""*/)
{
    // Save primitive as a C++ statement(s) on output stream out
}
