//===============================================================
// vCanvas - a scrolling window canvas for drawing
//
// Copyright (C) 1995-1999  Bruce E. Wampler
//
// This file is part of the V C++ GUI Framework, and is covered
// under the terms of the GNU Library General Public License,
// Version 2. This library has NO WARRANTY. See the source file
// vapp.cxx for more complete information about license terms.
//===============================================================
#include <v/vos2.h>            // for OS/2 stuff
#include <v/vcanvas.h>         // our header
#include <v/vwindow.h>         // need to use some vwindow stuff

  VCursor vCanvasPane::_currentCursor = -1;  // for cursor

//================>>> vCanvasPane::vCanvasPane <<<========================
  vCanvasPane::vCanvasPane(PaneType pt) : vPane(pt)
  {
    SysDebug(Constructor,"vCanvasPane::vCanvasPane() constructor\n")
    _cpDC = 0;         // no pane DC
    _currentCursor = VC_Arrow;
  }
//================>>> vCanvasPane::~vCanvasPane <<<========================
  vCanvasPane::~vCanvasPane()
  {
    SysDebug(Destructor,"vCanvasPane::~vCanvasPane() destructor\n")
    if (_cpDC != 0)
      delete _cpDC;   // free the DC
  }

//==================>>> vCanvasPane::initialize <<<==========================
// pWidget is client window handle
//
  void vCanvasPane::initialize(vWindow* pWindow, HWND pWidget)
  {
    _HScrlHwnd = 0;   // no scroll bar to start
    _HScrlShown = 1;
    _HScrlTop =  0;
    _VScrlHwnd = 0;   // no scroll bar to start
    _VScrlShown = 1;
    _VScrlTop =  0;
    // now, build the items in the widget provided
    vPane::initialize(pWindow, pWidget);       // initialize base class
    pWindow->_canvasPane = this;               // easy access to/from window

    _hasFocus = 0;     // we don't start out with focus
    _drawWindow = pWindow->winHwnd();           // frame window handle
    _drawCanvas = pWidget;                      // client window handle
    // set the height and width of our window
    RECTL rc;
    // CAUTION: returned rc coords are inclusive/exclusive
    WinQueryWindowRect(_drawCanvas, &rc);
    _height = rc.yTop - rc.yBottom;    // update the local stuff
    _width = rc.xRight - rc.xLeft;

    // After the drawing widget has been created, we can create
    // the V DC for it to use.
    CreateDC();
    _parentWin->_WinHeight = _height;
    _parentWin->_WinWidth = _width;
    SetHScroll(_HScrlShown, 0);        // initial values for scroll bar
    SetVScroll(_VScrlShown, 0);
    _HOn = _VOn = 0;                   // both start out off
    _parentWin->SetWinCursor(_currentCursor);

    // Now need to fake out enter focus so the first mouse
    // click is ignored.
    if (!_hasFocus)
    {
      _hasFocus = 1;
      EnterFocus();           // call the virtual function
    }
  }

//==================>>> vCanvasPane::CreateDC <<<========================
  void vCanvasPane::CreateDC(void)
  {
    if (!_cpDC)
       _cpDC = new vCanvasPaneDC(this);        // create a CanvasPaneDC
  }

//====================>>> vCanvasPane::ShowPane <<<======================
  void vCanvasPane::ShowPane(int OnOrOff) VCONST
  {
    if (OnOrOff)
    {
      WinSetWindowPos (_drawWindow, HWND_TOP,
        0,0,0,0,
        SWP_RESTORE | SWP_SHOW | SWP_ACTIVATE);
    }
    else
    {
      WinSetWindowPos (_drawWindow, HWND_TOP,
        0,0,0,0,
        SWP_MINIMIZE | SWP_ACTIVATE);
    }
  }

//=====================>>> vCanvasPane::SetHeightWidth <<<=========================
  void vCanvasPane::SetWidthHeight(int width, int height)
  {
    if (height <= 0 || width <= 0)
       return;

    // we need to compute the total height of all command and status bars
    RECTL oldFrameRcl, oldClientRcl;
    WinQueryWindowRect(_drawWindow, &oldFrameRcl);
    WinQueryWindowRect(_drawCanvas, &oldClientRcl);
    WinCalcFrameRect(_drawWindow, &oldClientRcl, FALSE);
    int barHeight = (oldFrameRcl.yTop - oldFrameRcl.yBottom) -
      (oldClientRcl.yTop - oldClientRcl.yBottom);

    _parentWin->_WinHeight = _height = height;
    _parentWin->_WinWidth = _width = width;

    RECTL rcl;
    rcl.xLeft = 0;
    rcl.yBottom = 0;
    rcl.xRight = width;   // client width
    rcl.yTop = height;    // client height

    // compute the frame size to hold desired client
    WinCalcFrameRect(_drawWindow, &rcl, FALSE);

    // resize the frame and subclassed procedure will fix everything else
    WinSetWindowPos(_drawWindow, 0,
      0, 0,
      rcl.xRight - rcl.xLeft,          // width
      rcl.yTop - rcl.yBottom + barHeight,  // height
      SWP_SIZE);
  }

// ************************************************************************
//---------------------- Mouse Pointer Stuff -----------------------------
// ************************************************************************
//==================>>> vCanvasPane::SetCursor <<<============================
  void vCanvasPane::SetCursor(VCursor id)
  {
    // Set to current cursor
    if (id < VC_None || id > VC_LAST)
    {
      SysDebug1(BadVals,"vCanvasPane::SetCursor(id=%d)\n",id)
      return;
    }
    if (id == VC_None)              // special case
    UnSetCursor();
    SysDebug1(WindowEvents,"vCanvasPane::SetCursor(id=%d)\n",id)
    _currentCursor = id;
    _parentWin->SetWinCursor(_currentCursor);
  }

//===================>>> vCanvasPane::UnSetCursor <<<=========================
  void vCanvasPane::UnSetCursor(void)
  {
    _currentCursor = CA_None;
  }

// ************************************************************************
// ---------------------- Scrolling Stuff ---------------------------------
// ************************************************************************

//=======================>>> vCanvasPane::HPage <<<============================
  void vCanvasPane::HPage(int Shown, int Top)
  {
    // This is the main way a user app gets scrolling events, and
    // will usually override this guy to handle what is actually
    // drawn in the canvas window.
    // This should then be called to actually change the display.
    SysDebug2(WindowEvents,"vCanvasPane::HPage(Shown: %d, Top: %d)\n",Shown, Top);
    SetHScroll(Shown,Top);
  }
//=======================>>> vCanvasPane::HScroll <<<==========================
  void vCanvasPane::HScroll(int step)
  {
    // This is the way the user app gets step events - either page or
    // single step events. The default doesn't have to do anything.
    SysDebug1(WindowEvents,"vCanvasPane::HScroll(%d)\n",step);
    if (step < 0)
    {
      if (_HScrlTop - 1 >= 0)
        SetHScroll(_HScrlShown, --_HScrlTop);
    }
    else
    {
      if (_HScrlTop + 1 <= 100)
        SetHScroll(_HScrlShown, ++_HScrlTop);
    }
  }

//=====================>>> vCanvasPane::ShowHScroll <<<=========================
  void vCanvasPane::ShowHScroll(int OnOff)
  {
    if (OnOff)                 // Turn on
    {
      if (_HOn)               // Already on
        return;
      _HOn = 1;
      // create the scroll bars
      SBCDATA sbcd;
      sbcd.cb = sizeof(SBCDATA);
      sbcd.sHilite = 0;
      sbcd.posFirst = 0;
      sbcd.posLast  = 100;
      sbcd.posThumb = 0;
      sbcd.cVisible = 1;
      sbcd.cTotal = 100;

      _HScrlHwnd = WinCreateWindow(_drawWindow,    // parent
                     WC_SCROLLBAR,                // window class
                     NULL,                        // window text
                     SBS_HORZ,                    // horizontal
                     0,0,                         // position
                     0,0,                         // size
                     _drawWindow,                 // owner
                     HWND_TOP,                    // bottom Z-order
                     FID_HORZSCROLL,              // ID
                     &sbcd,                       // class data
                     NULL);                       // no presentation params

      // notify frame of changes so can resize client
      WinSendMsg(_drawWindow, WM_UPDATEFRAME,
        MPFROMSHORT(FCF_HORZSCROLL ), 0);

      // set range to 0-100
//      WinSendMsg (ScrollBar, SBM_SETSCROLLBAR,
//        MPFROMSHORT(0),MPFROM2SHORT(0,100));
    }
    else                       // Turn off
    {
      if (!_HOn)              // Already off
        return;
      _HOn = 0;
      WinDestroyWindow (WinWindowFromID(_drawWindow, FID_HORZSCROLL));
      // notify frame of changes so can resize client
      WinSendMsg(_drawWindow, WM_UPDATEFRAME,
        MPFROMSHORT(FCF_HORZSCROLL ), 0);
    }
  }

//=====================>>> vCanvasPane::ShowVScroll <<<=========================
  void vCanvasPane::ShowVScroll(int OnOff)
  {
    if (OnOff)                 // Turn on
    {
      if (_VOn)               // Already on
        return;
      _VOn = 1;
      SBCDATA sbcd;

      // set range to 0-100
      sbcd.cb = sizeof(SBCDATA);
      sbcd.sHilite = 0;
      sbcd.posFirst = 0;
      sbcd.posLast  = 100;
      sbcd.posThumb = 0;
      sbcd.cVisible = 1;
      sbcd.cTotal = 100;

      _VScrlHwnd = WinCreateWindow(_drawWindow,    // parent
                     WC_SCROLLBAR,                // window class
                     NULL,                        // window text
                     SBS_VERT,                    // vertical
                     0,0,                         // position
                     0,0,                         // size
                     _drawWindow,                 // owner
                     HWND_TOP,                    // bottom Z-order
                     FID_VERTSCROLL,              // ID
                     &sbcd,                       // class data
                     NULL);                       // no presentation params

      // notify frame of changes so can resize client
      WinSendMsg(_drawWindow, WM_UPDATEFRAME,
        MPFROMSHORT(FCF_VERTSCROLL ), 0);

      // set range to 0-100
//      WinSendMsg (ScrollBar, SBM_SETSCROLLBAR,
//        MPFROMSHORT(0),MPFROM2SHORT(0,100));
    }
    else                       // Turn off
    {
      if (!_VOn)              // Already off
        return;
      _VOn = 0;
      WinDestroyWindow (WinWindowFromID(_drawWindow, FID_VERTSCROLL));
      // notify frame of changes so can resize client
      WinSendMsg(_drawWindow, WM_UPDATEFRAME,
        MPFROMSHORT(FCF_VERTSCROLL ), 0);
    }
  }


//=====================>>> vCanvasPane::GetHScroll <<<=========================
  int vCanvasPane::GetHScroll(int& Shown, int& Top) VCONST // V:1.13
  {
    Shown = _HScrlShown; Top = _HScrlTop;
    return _HOn;
  }


//=====================>>> vCanvasPane::SetHScroll <<<=========================
  void vCanvasPane::SetHScroll(int Shown, int Top)
  {
    // Make sure we are setting to valid values, and are changing things!
    if (Shown < 0 || Shown > 100 || Top < 0 || Top > 100)
      return;
    _HScrlShown = Shown;
    _HScrlTop = Top;

    WinSendMsg (WinWindowFromID(_drawWindow, FID_HORZSCROLL),
      SBM_SETPOS, MPFROMSHORT(Top), 0);

    WinSendMsg (WinWindowFromID(_drawWindow, FID_HORZSCROLL),
      SBM_SETTHUMBSIZE, MPFROM2SHORT(Shown, 100), 0);
  }

//========================>>> vCanvasPane::VPage <<<===========================
  void vCanvasPane::VPage(int Shown, int Top)
  {
    SysDebug2(WindowEvents,"vCanvasPane::VPage(Shown: %d, Top: %d)\n",Shown, Top);
    SetVScroll(Shown,Top);
  }

//========================>>> vCanvasPane::VScroll <<<=========================
  void vCanvasPane::VScroll(int step)
  {
    // This is the way the user app gets step events - either page or
    // single step events. Default updates the display
    SysDebug1(WindowEvents,"vCanvasPane::VScroll(%d)\n",step);
    if (step < 0)
    {
      if (_VScrlTop - 1 >= 0)
        SetVScroll(_VScrlShown, --_VScrlTop);
    }
    else
    {
      if (_VScrlTop + 1 <= 100)
        SetVScroll(_VScrlShown, ++_VScrlTop);
    }
  }

//=====================>>> vCanvasPane::GetVScroll <<<=========================
  int vCanvasPane::GetVScroll(int& Shown, int& Top) VCONST  // V:1.13
  {
    Shown = _VScrlShown; Top = _VScrlTop;
    return _VOn;
  }

//=====================>>> vCanvasPane::SetVScroll <<<=========================
  void vCanvasPane::SetVScroll(int Shown, int Top)
  {
    // Make sure we are setting to valid values, and are changing things!
    if (Shown < 0 || Shown > 100 || Top < 0 || Top > 100)
       return;
    _VScrlShown = Shown;
    _VScrlTop = Top;

    WinSendMsg (WinWindowFromID(_drawWindow, FID_VERTSCROLL),
      SBM_SETPOS, MPFROMSHORT(Top), 0);

    WinSendMsg (WinWindowFromID(_drawWindow, FID_VERTSCROLL),
      SBM_SETTHUMBSIZE, MPFROM2SHORT(Shown, 100), 0);
  }

//====================>>> vCanvasPane::HScrollEV <<<=======================
  void vCanvasPane::HScrollEV(int code, int pos, HWND control)
  {
    switch (code)
    {
      case SB_LINERIGHT:              // Right one unit
        HScroll(1);
        break;

      case SB_LINELEFT:               // Left one unit
        HScroll(-1);
        break;

      case SB_PAGERIGHT:              // Page Right - use 10
        if (_HScrlTop <= 90)
        {
          _HScrlTop += 10;
          HPage(_HScrlShown, _HScrlTop);
        }
        else
        {
          _HScrlTop = 100;
          HPage(_HScrlShown, 100);
        }
        break;

      case SB_PAGELEFT:               // Page Left - use -10
        if (_HScrlTop >= 10)
        {
          _HScrlTop -= 10;
          HPage(_HScrlShown, _HScrlTop);
        }
        else
        {
          _HScrlTop = 0;
          HPage(_HScrlShown, 0);
        }
        break;

      case SB_SLIDERPOSITION:         // move to position given
        _HScrlTop = pos;
        HPage(_HScrlShown, pos);
        break;

      case SB_SLIDERTRACK:            // for continuous motion
        _HScrlTop = pos;
        HPage(_HScrlShown, pos);
        break;
      }
  }

//====================>>> vCanvasPane::VScrollEV <<<=======================
  void vCanvasPane::VScrollEV(int code, int pos, HWND control)
  {
    switch (code)
    {
      case SB_LINEDOWN:               // Down one unit
        VScroll(1);
        break;

      case SB_LINEUP:                 // Up one unit
        VScroll(-1);
        break;

      case SB_PAGEDOWN:               // Page Down - use 10
        if (_VScrlTop <= 90)
        {
          _VScrlTop += 10;
          VPage(_VScrlShown, _VScrlTop);
        }
        else
        {
          _VScrlTop = 100;
          VPage(_VScrlShown, 100);
        }
        break;

      case SB_PAGEUP:                 // Page Up - use -10
        if (_VScrlTop >= 10)
        {
          _VScrlTop -= 10;
          VPage(_VScrlShown, _VScrlTop);
        }
        else
        {
          _VScrlTop = 0;
          VPage(_VScrlShown, 0);
        }
        break;

      case SB_SLIDERPOSITION:         // move to position given
        _VScrlTop = pos;
        VPage(_VScrlShown, pos);
        break;

      case SB_SLIDERTRACK:            // continuous motion
        _VScrlTop = pos;
        VPage(_VScrlShown, pos);
        break;

      default:
        return;
      }
  }

// ************************************************************************
//
// Redraw/resize/focus
//
// ************************************************************************

//=====================>>> vCanvasPane::ExposeEV <<<==========================
  void vCanvasPane::ExposeEV(void)
  {
    SysDebug(WindowEvents,"vCanvasPane::ExposeEV()\n")
//    ERRORID errorCode;
//    errorCode = WinGetLastError(theApp->AppHab());
//    SysDebug1(WindowEvents,"1) ExposeEV() ErrorCode = %8x\n", errorCode)

    // we need to bracket the WM_PAINT with WinBeginPaint/WinEndPAint
    // this will also update _cpDC->_rcl with the update Rectl
    RECTL rc;
    WinBeginPaint(_drawCanvas, _cpDC->handleDC(), &rc);
//    _cpDC->BeginPaint();

    int x = 0, y = 0, width = 0, height = 0;

    // convert from OS/2 to V coord space
    // CAUTION: WinQuery return rc coords are inclusive/exclusive
    x = rc.xLeft ;
    y = (_height -1) - rc.yTop;

    height = rc.yTop - rc.yBottom;
    width = rc.xRight - rc.xLeft;

    _cpDC->ClearRect(x, y, width, height);

    Redraw(x, y , width, height);

    WinEndPaint(_cpDC->handleDC());
//    _cpDC->EndPaint();
  }

//=====================>>> vCanvasPane::EnterFocus <<<========================
  void vCanvasPane::EnterFocus(void)
  {
    SysDebug(WindowEvents,"vCanvasPane::EnterFocus()\n")
  }

//=====================>>> vCanvasPane::EnterEV <<<==========================
  void vCanvasPane::EnterEV(void)
  {
    if (_hasFocus)             // don't double enter focus
      return;
    _hasFocus = 1;
    EnterFocus();              // call the virtual function
  }

//======================>>> vCanvasPane::FontChanged <<<===========================
  void vCanvasPane::FontChanged(VCONST vFont& vf)
  {
    // Called when the font is changed.
    SysDebug1(WindowEvents,"vCanvasPane::FontChanged(%d)\n",vf)
  }

//=====================>>> vCanvasPane::LeaveFocus <<<=======================
  void vCanvasPane::LeaveFocus()
  {
    SysDebug(WindowEvents,"vCanvasPane::LeaveFocus()\n")
  }

//=====================>>> vCanvasPane::LeaveEV <<<==========================
  void vCanvasPane::LeaveEV()
  {

    if (!_hasFocus)            // don't double leave focus
      return;
    _hasFocus = 0;

    LeaveFocus();            // call the virtual function
  }

//=====================>>> vCanvasPane::MouseDown <<<==========================
  void vCanvasPane::MouseDown(int x, int y, int button)
  {
    // the mouse coords are in World Space since V interface is in World Space
    SysDebug3(MouseEvents,"vCanvasPane::MouseDown(x:%d,y:%d,btn:%d)\n",x,y,button)
  }

//=====================>>> vCanvasPane::MouseUp <<<============================
  void vCanvasPane::MouseUp(int x, int y, int button)
  {
    // the mouse coords are in World Space since V interface is in World Space
    SysDebug3(MouseEvents,"vCanvasPane::MouseUp(x:%d,y:%d,btn:%d)\n",x,y,button)
  }

//=====================>>> vCanvasPane::MouseMove <<<==========================
  void vCanvasPane::MouseMove(int x, int y, int button)
  {
    // the mouse coords are in World Space since V interface is in World Space
    SysDebug3(MouseEvents,"vCanvasPane::MouseMove(x:%d,y:%d,btn:%d)\n",x,y,button)
  }

//=======================>>> vCanvasPane::Redraw <<<==========================
  void vCanvasPane::Redraw(int x, int y, int width, int height)
  {
    // Redraw in vCanvasPane is a no-op.
#ifdef vDEBUG          // Don't have a SysDebug4 macro, so do it by hand
    if (DebugState.WindowEvents && DebugState.System)
      printf("vCanvasPane::Redraw(x=%d, y=%d, w=%d, h=%d)\n",x,y,width,height);
#endif
  }

//=====================>>> vCanvasPane::Resize <<<============================
  void vCanvasPane::Resize(int newW, int newH)
  {
    // This is the routine the user will override to intercept size changes
    SysDebug2(WindowEvents,"vCanvasPane::Resize(newW:%d, newH:%d)\n",newW,newH)
  }

//==================>>> vCanvasPane::ResizeEV <<<========================
  void vCanvasPane::ResizeEV(int w, int h)
  {
    _parentWin->_WinHeight = _height = h;
    _parentWin->_WinWidth = _width = w;
    Resize((int)w, (int)h);    // Call the virtual function
  }

