//////////////////////////////////////////////////////////////////////
// Splitter.CC                                                      //
// dBASE Inc. R&D Group - Jim Sare                                  //
// 04/05/2002                                                       //
//////////////////////////////////////////////////////////////////////
// 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:                                                           //
// - Make sure that the form's Metric property is set to 6 - pixels.//
//   The Splitter will not function correctly with any other value. //
// - 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.                                                      //
//////////////////////////////////////////////////////////////////////
// Versioning and History comments:
// #define SPLITTER_VERSION 1.12  // Last release version.
//#define SPLITTER_VERSION 1.20  // 04/19/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 SPLITTER_VERSION.
                               //   SPLITTER_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.
#define SPLITTER_VERSION 1.21  // 10/26/2002
                               // Set borderStyle = 1 (Raised).
                               // Corrected assignment of Version member to SPLITTER_VERSION.
                               // Picked up Marty Kay's fix to prevent splitter from
                               // going under SubForms.

CLASS HSplitter(o, n) Of Rectangle(o, n) Custom

   if type("dB_BringWindowToTop") # "FP"
      extern CLOGICAL dB_BringWindowToTop(CHANDLE) User32;
                from "BringWindowToTop"
   endIf
   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

   // Set stock property members
   this.metric :=                6                // Pixels
   this.borderStyle :=           1                // Raised
   this.width :=                 o.width          // Default to width of parent
   this.height :=                6
   this.mousePointer :=          7
   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 =                SPLITTER_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)
      dB_BringWindowToTop(this.hWND)

   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)
      else                                        // 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)
      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)

ENDCLASS  // HSplitter Of Rectangle


CLASS VSplitter(o, n) Of HSplitter(o, n) Custom

   // Set stock property members
   this.metric :=                6                // Pixels
   this.width :=                 6
   this.height :=                o.height         // Default to parent height
   this.mousePointer :=          9

   // Set custom property members
   this.vertical =               true             // False = Horizontal, True = Vertical

ENDCLASS  // VSplitter Of HSplitter

// EOF: Splitter.CC

