//////////////////////////////////////////////////////////////////////
// coreSplitter.CC                                                  //
// dBASE Inc. R&D Group - Jim Sare                                  //
// 04/05/2002                                                       //
// Copyright dataBased Intelligence, Inc. 2002-2006                 //
//////////////////////////////////////////////////////////////////////
// Rights:                                                          //
// You have the right to freely include this code in any            //
// compiled dBASE application. You do not have the right to         //
// reproduce or publish this source code without the explicit       //
// written permission of dataBased Intelligence, Inc., Vestal, NY   //
//////////////////////////////////////////////////////////////////////
// DESCRIPTION:                                                     //
// This is a visual horizontal/vertical splitter bar that allows    //
// dragging/moving across its container surface. As the splitter is //
// moved, it can optionally notify the form that it is moving so    //
// that the form can update its controls accordingly. When the move //
// is complete, the Splitter can notify the form that it is done    //
// moving so that the form can update its controls accordingly.     //
// By default, the Splitter is also configured such that when it is //
// doubleclicked, it will restore its position to a preconfigured   //
// default position.                                                //
//////////////////////////////////////////////////////////////////////
// USAGE:                                                           //
// - Place HSplitter (horizontal splitter) or VSplitter (vertical   //
//   splitter) on the form in the Form Designer.                    //
// - If the Splitter is to notify the form while it is moving,      //
//   assign a function pointer of a form procedure or function to   //
//   xSplitter.onMoving. The assigned function or procedure will be //
//   called while the Splitter is being dragged across the form or  //
//   container.                                                     //
// - If the Splitter is to notify the form when a move is complete, //
//   assign a function pointer to a form procedure or function to   //
//   xSplitter.onMoved. The assigned function or procedure will be  //
//   called when the Splitter move operation is complete.           //
// - If you want to change the default positioning of the Splitter  //
//   when it is double-clicked, change the value of the             //
//   xSplitter.doubleClickPosition property (default is 50). The    //
//   xSplitter.doubleClickPosition property is a numeric that is    //
//   is expressed as a percentage from the left side of the         //
//   Splitter's container (HSplitter) or the top side of the        //
//   Splitter's container (VSplitter). The valid range is 0 - 100.  //
// - If you do not want the splitter to use default positioning     //
//   when it is double-clicked, set xSplitter.allowDoubleClickMove  //
//   to False.                                                      //
//////////////////////////////////////////////////////////////////////

// #define dB_VERSION 1.12  // Last release version.
#define dB_VERSION 2.00  // 12/17/2003, 04/05/2002
                         // Defaulted width in HSplitter and height in VSplitter
                         //   for better sizing when the splitter is dropped on
                         //   a form in the Form Designer.
                         // Defaulted metric to 6 (Pixels) for correct sizing
                         //   when defaulting width and height in the class
                         //   constructor.
                         // Defaulted mousePointer to 7 in HSplitter and to
                         //   9 in VSplitter.
                         // Corrected firing of onMoved event in onLeftMouseUp
                         //   to only fire if splitter was actually in a move
                         //   sequence. In the previous version, onMoved fired
                         //   from onLeftMouseUp whether the splitter was
                         //   actually in a move sequence or not.
                         // Added a new anchorPosition property that anchors
                         //   the left for HSplitter or top for VSplitter to a
                         //   fixed position, and defaulted anchorPosition in
                         //   the onOpen event to the designed left for HSplitter
                         //   or top for VSplitter.
                         // Added a new version property member so that the
                         //   splitter version can be checked from dBL code.
                         //   The version property will be set from dB_VERSION.
                         //   dB_VERSION will be changed on subsequent change
                         //   releases.
                         // Changed 'orientation' property member which was an
                         //   enumerated numeric property to a logical
                         //   'vertical' property member for consistency with
                         //   the ScrollBar's vertical property member.
                         // Minor code optimization in onMouseMove.

                         // QAID: 4539 - M.Kay, dBASE Inc.
                         // Made Splitter easier to grab by following changes:
                         //  Changed base class from Rectangle to Container
                         //  Turned off borders
                         //  Added built in hightlight lines to give 3D appearance
                         //  Added method adjustLength()

CLASS coreHSplitter(o, n) Of Container(o, n) Custom

   if type("dB_GetCapture") # "FP"
      extern CHANDLE dB_GetCapture() User32;
                from "GetCapture"
   endIf
   if type("dB_LockWindowUpdate") # "FP"
      extern CLOGICAL dB_LockWindowUpdate(CHANDLE) User32;
                from "LockWindowUpdate"
   endIf
   if type("dB_ReleaseCapture") # "FP"
      extern CLOGICAL dB_ReleaseCapture() User32;
                from "ReleaseCapture"
   endIf
   if type("dB_SetCapture") # "FP"
      extern CHANDLE dB_SetCapture(CHANDLE) User32;
                from "SetCapture"
   endIf
   if type("dB_UpdateWindow") # "FP"
      extern CLOGICAL dB_UpdateWindow(CHANDLE) User32;
                from "UpdateWindow"
   endIf

   // M.Kay, 09/06/2002, Test fix for splitter moving under
   // subforms.
   If Type("dB_BringWindowToTop") # "FP"
      extern CLOGICAL dB_BringWindowToTop(CHANDLE) User32;
                From "BringWindowToTop"
   EndIf
   // End 

   // Set stock property members
   this.metric :=                6              // Pixels

   // QAID: 4539 - M.Kay, dBASE Inc. - 12/18/2003
   this.borderStyle :=           3              // Turn off border
   // QAID End

   this.width :=                 o.width        // Default to width of parent
   this.height :=                6
   this.mousePointer :=          15     // QAID: 5109 - Changed from 7, M.Kay
   //this.text :=                  ""

   // Set custom property members
   this.vertical =               false          // False = Horizontal, True = Vertical
   this.allowDoubleClickMove =   True           // DoubleClick to default position
   this.doubleClickPosition =    50             // Percentage from Left or Top
   this.anchorPosition =         0              // Left or Top anchor
   this.version =                dB_VERSION     // Release version

   // Set custom event members
   this.onMoving =               Null           // Called while splitter is moving
   this.onMoved =                Null           // Called when splitter is done moving

   // PROTECTed custom members
   this.moving =                 False
   this.offsetX =                0              // Mouse offset at start of move
   this.offsetY =                0              // Mouse offset at start of move

   // PROTECT custom properties
   PROTECT moving, offsetX, offsetY

   PROCEDURE onOpen
      this.anchorPosition := iIf(this.vertical, this.top, this.left)

      // QAID: 4539- M.Kay, dBASE Inc. - 12/18/2003
      this.highLightLine = new Line(this)
      this.shadowLine    = new Line(this)
      this.endHighLight  = new Line(this)
      this.endShadow     = new Line(this)
      
      if this.vertical
         // setup vertical highlight
         this.highLightLine.left = 0
         this.highLightLine.right = 0
         this.highLightLine.top = 0
         this.highLightLine.bottom = this.height

         // setup vertical shadow
         this.shadowLine.left = this.width-1
         this.shadowLine.right = this.width-1
         this.shadowLine.top = 0
         this.shadowLine.bottom = this.height

         // setup top highlight
         this.endHighLight.left   = 0
         this.endHighLight.right  = this.width-1
         this.endHighLight.top    = 0
         this.endHighLight.bottom = 0

         // setup bottom shadow
         this.endShadow.left      = 1
         this.endShadow.right     = this.width-1
         this.endShadow.top       = this.height
         this.endShadow.bottom    = this.height
      else
         // setup horizontal highlight
         this.highLightLine.left = 0
         this.highLightLine.right = this.width
         this.highLightLine.top = 0
         this.highLightLine.bottom = 0

         // setup horizontal shadow
         this.shadowLine.left = 0
         this.shadowLine.right = this.width
         this.shadowLine.top = this.height-1
         this.shadowLine.bottom = this.height-1

         // setup top highlight
         this.endHighLight.left   = 0
         this.endHighLight.right  = 0
         this.endHighLight.top    = 0
         this.endHighLight.bottom = this.height-1

         // setup bottom shadow
         this.endShadow.left      = this.width-1
         this.endShadow.right     = this.width-1
         this.endShadow.top       = 1
         this.endShadow.bottom    = this.height-1
      endif

/*      this.highLightLine.colorNormal = "BtnHighlight"
      this.shadowLine.colorNormal    = "BtnShadow"
      this.endHighLight.colorNormal  = "BtnHighlight"
      this.endShadow.colorNormal     = "BtnShadow"
*/

      this.highLightLine.colorNormal = "White"
      this.shadowLine.colorNormal    = "Gray"
      this.endHighLight.colorNormal  = "Silver"
      this.endShadow.colorNormal     = "White"

      // End QAID: 4539

      dB_BringWindowToTop(this.hWND)   // M.Kay

   PROCEDURE onLeftDblClick
      LOCAL nLeft, nTop, nTemp

      if not this.allowDoubleClickMove
         RETURN
      endIf
      if this.doubleClickPosition < 0           // Force minimum position
         this.doubleClickPosition = 0
      endIf
      if this.doubleClickPosition > 100         // Force maximum position
         this.doubleClickPosition = 100
      endIf
      dB_LockWindowUpdate(FORM.hWND)            // Reduce form repaints
      nTop = this.top
      nLeft = this.left
      if not this.vertical                      // Horizontal
         nTemp = (this.parent.height * (this.doubleClickPosition / 100)) -;
                                                               (this.height / 2)
         this.left := this.anchorPosition
         this.top := iIf(nTemp < 0, 0, nTemp)
      endIf
      if this.vertical                          // Vertical
         nTemp = (this.parent.width * (this.doubleClickPosition / 100)) -;
                                                                (this.width / 2)
         this.left := iIf(nTemp < 0, 0, nTemp)
         this.top := this.anchorPosition
      endIf
      if ((this.top # nTop) or (this.left # nLeft)) and;
                  (type("this.onMoved") $ "CB|FP") and (not empty(this.onMoved))
         this.onMoved(this.left, this.top)
      endIf
      dB_LockWindowUpdate(0)                    // Allow the form to repaint
      nTemp = this.mousePointer
      this.mousePointer := 0
      this.mousePointer := nTemp                // Force mouse pointer restore

   PROCEDURE onLeftMouseDown(flags, col, row)

      if this.hWND = dB_GetCapture()
         dB_ReleaseCapture()
      endIf

      this.offsetX := col
      this.offsetY := row
      this.moving := True

      dB_BringWindowToTop(this.hWND) // M.Kay

      dB_SetCapture(this.hWND)

   PROCEDURE onMouseMove(flags, col, row)

      if this.moving
         this.doRelativeMove(col, row)
      elseIf this.hWND = dB_GetCapture()
         dB_ReleaseCapture()
      endIf

   PROCEDURE onLeftMouseUp(flags, col, row)
      LOCAL nSave

      if this.hWND = dB_GetCapture()
         dB_ReleaseCapture()
      endIf
      if this.moving
         this.doRelativeMove(col, row)
         this.moving := False
         if (type("this.onMoved") $ "CB|FP") and (not empty(this.onMoved))
            this.onMoved(this.left, this.top)
         endIf
      endIf
      nSave = this.mousePointer
      this.mousePointer := 0
      this.mousePointer := nSave                 // Force mouse pointer restore

   PROCEDURE doRelativeMove(nX, nY)

      if nX > 32767
         nX := -bitXOr(0xFFFF, nX)
      endIf
      if nY > 32767
         nY := -bitXOr(0xFFFF, nY)
      endIf
      this.doMove(iIf(this.left + nX - this.offsetX => 0,;
                          this.left + nX - this.offsetX, 0),;
                  iIf(this.top + nY - this.offsetY => 0,;
                         this.top + nY - this.offsetY, 0))

   PROCEDURE doMove(nX, nY)
      LOCAL nLeft, nTop

      if (this.left = nX) and (this.top = nY)
         RETURN
      endIf
      dB_LockWindowUpdate(FORM.hWND)
      nLeft = this.left
      nTop = this.top
      if this.vertical
         this.left := iIf(nX + this.width > this.parent.width,;
                          this.parent.width - this.width,;
                          iIf(nX < 0, 0, nX))
         this.top := this.anchorPosition
      else
         this.left := this.anchorPosition
         this.top  := iIf(nY + this.height > this.parent.height,;
                          this.parent.height - this.height,;
                          iIf(nY < 0, 0, nY))
      endIf
      if ((this.left # nLeft) or (this.top # nTop)) and;
                (type("this.onMoving") $ "CB|FP") and (not empty(this.onMoving))
         this.onMoving(this.left, this.top)
      endIf
      dB_LockWindowUpdate(0)
      dB_UpdateWindow(FORM.hWND)


      // QAID: 4539 - M.Kay, dBASE Inc. - 12/17/2003
      PROCEDURE adjustLength( nLength )

      if this.vertical
         this.height = nLength

         if type("this.highLightLine") = "O"
            this.highLightLine.bottom = nLength
         endif

         if type("this.shadowLine") = "O"
            this.shadowLine.bottom = nLength
         endif

         if type("this.endShadow") = "O"
            this.endShadow.top    = nLength-1
            this.endShadow.bottom = nLength-1
         endif
      else
         this.width = nLength

         if type("this.highLightLine") = "O"
            this.highLightLine.right = nLength
         endif

         if type("this.shadowLine") = "O"
            this.shadowLine.right = nLength
         endif

         if type("this.endShadow") = "O"
            this.endShadow.left  = nLength-1
            this.endShadow.right = nLength-1
         endif
      endif
      // End QAID: 4539

ENDCLASS  // coreHSplitter Of Rectangle


CLASS coreVSplitter(o, n) Of coreHSplitter(o, n) Custom

   // Set stock property members
   this.metric :=                6              // Pixels
   this.width :=                 6
   this.height :=                o.height       // Default to parent height
   this.mousePointer :=          14             // QAID: 5109 - M.Kay - Changed from 9 - 12/15/2004

   // Set custom property members
   this.vertical =               true           // False = Horizontal, True = Vertical


ENDCLASS  // coreVSplitter Of coreHSplitter

// EOF: coreSplitter.CC
