//===============================================================
// vbtncmd.cpp - button Cmd - Windows
//
// 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/vbtncmd.h>  // our definitions
#include <v/vcmdprnt.h> // a command parent
#include <v/vutil.h>
#include <v/vicon.h>
#include <v/vapp.h>

  // Toggle buttons are subclassed to this procedure
  MRESULT EXPENTRY BtnToggleProc(HWND hDlg, ULONG uMsg, MPARAM mp1, MPARAM mp2);
  // this is a pointer to the original button procedure that is subclassed
  PFNWP orgBtnProc;

//====================>>> vButtonCmd::vButtonCmd <<<=======================
  vButtonCmd::vButtonCmd(vCmdParent* dp, CommandObject* dc) :
    vCmd(dp, dc)
  {
    initialize();
  }
//====================>>> vButtonCmd::~vButtonCmd <<<=======================
  vButtonCmd::~vButtonCmd()
  {
    SysDebug(Destructor,"vButtonCmd::~vButtonCmd() destructor\n")
  }
//=====================>>> vButtonCmd::initialize <<<=======================
  void vButtonCmd::initialize()
  {
    // build a button command for use in a parent window
    SysDebug(Constructor,"vButtonCmd::vButtonCmd() constructor\n")
    long style;

    // set the Pres Params to the system default color
    _PPColor[0].PPtype = PP_BACKGROUNDCOLORINDEX;
    _PPColor[0].PPval.color = SYSCLR_BUTTONMIDDLE;
    _PPColor[1].PPtype = PP_FOREGROUNDCOLORINDEX;
    _PPColor[1].PPval.color = SYSCLR_MENUTEXT;
    _PPColor[2].PPtype = 0;    // end of array marker

    if (_parentWin->_dialogType != aCmdBar)
	style = WS_TABSTOP | WS_GROUP;  // default for a dialog button
    else
	style = WS_GROUP | BS_NOPOINTERFOCUS;

    CopyToLocal();                      // Make local copies of CmdObject

    if (!(dlgCmd->attrs & CA_Hidden))   // Check for Hidden
	style |= WS_VISIBLE;

    if ((dlgCmd->cmdType == C_IconButton || dlgCmd->cmdType == C_ToggleIconButton)
	&& _itemList != 0) // icon
    {
      initIconButton(style);          // Special init if icon
      return;
    }

    if (dlgCmd->cmdType == C_ColorButton) // just set PPs to get a color button
    {
      style |= BS_PUSHBUTTON;
      initColorButton();
    }
    else if (dlgCmd->cmdType == C_ToggleButton) // noop for toggle
	style |= BS_PUSHBUTTON;

    else if ((dlgCmd->attrs & CA_DefaultButton))   // default button?
	style |= BS_DEFAULT;

    else
	style |= BS_PUSHBUTTON;         // Plain old button

    _w = LabelWidth(_title)+14;         // set my width
    _h = LabelHeight() + 5;             // was _h = 14;

    if ((dlgCmd->cmdType == C_Button || dlgCmd->cmdType == C_ToggleButton)
	  && dlgCmd->size > 0)
    {
      // make sure button is big enough to hold text
      int w = LabelWidth(_title)+14;         // set my width
      if (w < dlgCmd->size)
      {
        _w = dlgCmd->size;
      }
    }
    else if ( dlgCmd->cmdType == C_ColorButton && dlgCmd->size > 0)
    {
      // each y dialog unit is 1/8 the max system font height and
      // each x dialog unit is 1/4 max system font width.  The system
      // proportional font is 8x20 so in pixels one x dialog unit = 2 pixels
      // and one y dialog unit = 2.5 pixels.  Since we want square
      // buttons we need to multiply y by 4/5 to get same size as
      // x on the screen. We then multiply by 1/2 to convert to
      // pixels as desired.
      _w = dlgCmd->size*5/4;          // size - square (color buttons)
      _h = dlgCmd->size;      // simulate pixels using dialog units
    }
    else if (dlgCmd->size > 0)  // size - square (icons)
    {
      _w = dlgCmd->size*5/4;
      _h = dlgCmd->size;
    }

    _parentWin->SetPosition(_x, _y, _w, _h, dlgCmd->cFrame, dlgCmd->cRightOf,
      dlgCmd->cBelow);
/*
    if (dlgCmd->cmdType == C_Button || dlgCmd->cmdType == C_ToggleButton)
    {
      _y += 1;     // center better
    }
*/
    _CtrlOffset = _parentWin->AddDlgControl(_x, _y, _w, _h, _cmdId,
      style, WC_BUTTON, _title, _parentWin->AssyPresParams(_PPColor), 0, NULL);
  }

//=====================>>> vButtonCmd::initColorButton <<<=======================
  void vButtonCmd::initColorButton(void)
  {
    // wCmd is the widget of this button
    _origColor = 0;
    if (_itemList != 0)                         // an RGB provided
    {
      _origColor = (vColor*)_itemList;        // point to the rgb
      _color = *_origColor;
    }
    // set the Pres Params to the specified color
    // we use the RGB version for the background
    _PPColor[0].PPtype = PP_BACKGROUNDCOLOR;
    _PPColor[0].PPval.color = (LONG) _color.pixel();
    _PPColor[1].PPtype = PP_FOREGROUNDCOLORINDEX;
    _PPColor[1].PPval.color = SYSCLR_MENUTEXT;
    _PPColor[2].PPtype = 0;    // end of array marker
  }


//=====================>>> vButtonCmd::initIconButton <<<====================
  void vButtonCmd::initIconButton(long style)
  {
    style |= BS_BITMAP;

    _ip = (vIcon *) _itemList;  // to access bitmap
    _h = LONG( (_ip->height / 2.5) + 6 );
    _w = LONG( (_ip->width / 2) + 6 );

    // build up the button control data to point to the desired bitmap
    _btncData.cb = sizeof(BTNCDATA);
    _btncData.fsCheckState = 0;
    _btncData.fsHiliteState = 0;

    // get icon HBITMAP
    _btncData.hImage = _ip->GetIconDLG(dlgCmd->cmdType, SYSCLR_BUTTONMIDDLE);

    _parentWin->SetPosition(_x, _y, _w, _h, dlgCmd->cFrame,
	     dlgCmd->cRightOf, dlgCmd->cBelow);
/*
    // set the Pres Params to the specified color
    // we use the RGB version for the background
    _PPColor[0].PPtype = 13;
    _PPColor[0].PPval.color = SETRGB(0,0,0);
    _PPColor[1].PPtype = 1;
    _PPColor[1].PPval.color = SETRGB(0,0,0);
    _PPColor[2].PPtype = 0;    // end of array marker

    _CtrlOffset = _parentWin->AddDlgControl(_x, _y , _w, _h, _cmdId,
	   style, WC_BUTTON, "", _parentWin->AssyPresParams(_PPColor),
	   _btncData.cb, &_btncData);
*/
    _CtrlOffset = _parentWin->AddDlgControl(_x, _y , _w, _h, _cmdId,
	   style, WC_BUTTON, "", NULL, _btncData.cb, &_btncData);

  }
//==================>>> vButtonCmd::GetCmdValue <<<=========================
  int vButtonCmd::GetCmdValue(ItemVal id) VCONST
  {
    if (id != _cmdId)
      return -1;
    return _retVal;
  }
//==================>>> vButtonCmd::GetCmdValue <<<=========================
  int vButtonCmd::GetOwnCmdValue(void)
  {
    // subclassed toggle buttons call this method to determine
    // the buttons current state. Unfortunately it cannot be
    // private, so... security through obscurity.
    return _retVal;
  }
//================>>> vButtonCmd::ResetItemValue <<<======================
  void vButtonCmd::ResetItemValue(void)
  {
    // We have to toggle things
    if (dlgCmd->cmdType != C_ToggleIconButton &&
      dlgCmd->cmdType != C_ToggleButton)
	return;
    if (_retVal == _origVal)    // No op if no change
	return;

    _retVal = _origVal;         // restore

    if (_retVal)                        // depends on state
      {
	SetCmdVal(1, Checked);
      }
    else
      {
	SetCmdVal(0, Checked);
      }

//    HWND myHwnd = GetMyHwnd(_cmdId);
//    WinShowWindow (myHwnd, FALSE);  // trick to repaint button
//    WinShowWindow (myHwnd, TRUE);

    // let parent window handle now
    _parentWin->ProcessCmd(_cmdId, _retVal, dlgCmd->cmdType);
  }

//================>>> vButtonCmd::SetCmdVal <<<============================
  void vButtonCmd::SetCmdVal(ItemVal val, ItemSetType st)
  {
    SysDebug1(Misc,"vButtonCmd::SetCmdVal(val:%d)\n",val)
    HWND myHwnd = GetMyHwnd(_cmdId);

    switch (st)
    {
      case Sensitive:
	_Sensitive = val;               // set
	WinEnableWindow (myHwnd, val);
	break;

      case Hidden:
	if (val)
	  {
	    WinShowWindow (myHwnd, FALSE);
	  }
	else
	  {
	    WinShowWindow (myHwnd, TRUE);
	  }
	break;

      case Value:
      case Checked:
	_retVal = val;                  // set
	if (dlgCmd->cmdType != C_ToggleIconButton &&   // only toggles need attention
	    dlgCmd->cmdType != C_ToggleButton)
	  return;

//	WinCheckButton(_parentWin->getParent(),_cmdId, val);
	SysDebug2(OS2Dev,"--> val:%d _retVal:%d \n",val, _retVal)
	WinSendDlgItemMsg(_parentWin->getParent(), _cmdId,
	  BM_SETHILITE, MPFROMSHORT(val), (MPARAM)NULL);

//	HWND myHwnd = GetMyHwnd(_cmdId);
	break;

      case ChangeColor:               // changed original vColor
      {
	_color = *_origColor;       // recopy color

	// set the Pres Params to the specified color
	// we use the RGB version of the PP_
	_PPColor[0].PPtype = PP_BACKGROUNDCOLOR;
	_PPColor[0].PPval.color = (LONG) _color.pixel();

        // if the user calls ChangeColor before the dialog is
        // displayed then the control won't exist and WinSetPresParams
        // will fail.  We need to check for this and change the dialog
        // template PresParams instead if the dialog has not been created.
        if (myHwnd)
        {
	  WinSetPresParam(myHwnd, _PPColor[0].PPtype, (ULONG) sizeof(RGB),
	    (PVOID) &_PPColor[0].PPval.color);
        }
        else
        {
          _parentWin->ChangeDlgTButtonColor(_CtrlOffset, _cmdId,
            _PPColor[0].PPval.color );
        }
	break;
      }

      case Red:
	_color.SetR(val);
	break;

      case Green:
	_color.SetG(val);
	break;

      case Blue:
      {
	_color.SetB(val);
	_PPColor[0].PPtype = PP_BACKGROUNDCOLOR;
	_PPColor[0].PPval.color = (LONG) _color.pixel();
	WinSetPresParam(myHwnd, _PPColor[0].PPtype, (ULONG) sizeof(RGB),
	  (PVOID) &_PPColor[0].PPval.color);

	break;
      }
    }
  }

//==================>>> vButtonCmd::ToggleCmdValue <<<=========================
  int vButtonCmd::ToggleCmdValue(void)
  {
    SysDebug1(OS2Dev,"ToggleCmdValue org _retVal:%d \n",_retVal)
    SetCmdVal(!_retVal, Checked);
    return _retVal;
  }

//==================>>> vButtonCmd::userClick <<<=========================
  int vButtonCmd::userClick(int clicked)
  {
    // This routine lets the first BM_SETHILITE message sent
    // after a user clicks on the button to be passed
    // through to the button control unaltered.  This is
    // necessary so that the button can change state and issue
    // the required WM_COMMAND.  All BM_SETHILITE messages after
    // the first one are filtered and set to _retVal
    SysDebug1(OS2Dev,"Userclick click:%d \n",_click)
    if (clicked)
    {
      _click = 1;
      return _click;
    }
    else
    {
      if (_click)
      {
	_click = 0;
	return 1;
      }
      return 0;
    }
  }

//================>>> vButtonCmd::SetCmdStr <<<============================
  void vButtonCmd::SetCmdStr(char* str)
  {
    // Set the string value of button
    SysDebug1(Misc,"vButtonCmd::SetCmdStr(str:%s)\n",str)
    WinSetDlgItemText(_parentWin->getParent(),_cmdId, str);
    _title = str;
  }


//================>>> vButtonCmd::DRAWITEM <<<============================
  int vButtonCmd::DRAWITEM(int id, OWNERITEM* poi)
  {
    // This really has nothing to do with DRAWITEM, but its
    // an unused procedure that we can hijak and use to subclass
    // toggle buttons during the WM_INITDLG setup

    if (dlgCmd->cmdType == C_ToggleIconButton ||
	   dlgCmd->cmdType == C_ToggleButton)
    {
      // This is called during WM_INITDLG for toggle buttons
      // so we can subclass the button procedure
//      HWND hBtn = WinWindowFromID((HWND)poi, id);
      HWND hBtn = GetMyHwnd(id);
      WinSetWindowULong(hBtn, QWL_USER, (LONG) this);
      WinSendMsg(hBtn, BM_SETHILITE, MPFROMSHORT(_retVal), (MPARAM)NULL);
      _orgBtnProc = WinSubclassWindow(hBtn, BtnToggleProc);
      SysDebug2(OS2Dev,"DRAWITEM this:%x hBtn:%x \n", this, hBtn)
//	     HWND myHwnd = GetMyHwnd(_cmdId);
//	     if (SHORT1FROMMP((MPARAM) poi) == FALSE)  // losing focus
//	       WinPostMsg(myHwnd, BM_SETHILITE, MPFROMSHORT(_retVal), (MPARAM)NULL);
    }
    return (0);
  }

//====================>>> BtnToggleProc <<<=======================
// we subclass the C_ToggleButton so we can control its
// hilited state and stop the WC_FRAME class from interfering
  MRESULT EXPENTRY BtnToggleProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
  {
    vButtonCmd* thisCmd = (vButtonCmd*) WinQueryWindowULong( hwnd, QWL_USER ); // Pointer from window word
    if (!thisCmd)
    {
      return (MRESULT) WinDefDlgProc(hwnd, msg, mp1, mp2);
    }
    else
      return (MRESULT) thisCmd->vBtnToggleProc(hwnd, msg, mp1, mp2);
  }



//====================>>> vBtnToggleProc <<<=======================
// we subclass the C_ToggleButton so we can control its
// hilited state and stop the WC_FRAME class from interfering
  MRESULT vButtonCmd::vBtnToggleProc(HWND hBtn, ULONG uMsg, MPARAM mp1, MPARAM mp2)
  {
    // set the original frame procedure
    orgBtnProc = (PFNWP) _orgBtnProc;

    switch (uMsg)
    {
      case BM_SETHILITE:
      {
        // prevent frame from altering toggle button state
        // by trapping any attempt to alter buttons state
        // with its private BM_SETHILITE messages and replacing
        // it with the desired state
        if (!userClick(0))  // check if pending click
	  mp1 = MPFROMSHORT(GetOwnCmdValue());
        SysDebug3(OS2Dev,"BtnToggleProc this:%x hBtn:%x mp1:%x \n", this, hBtn, mp1)
      }
      break;

      case WM_BUTTON1DOWN:
      {
        // user has clicked on the button, so toggle the button state
        // so that _retVal gets updated befor the BM_SETHILITE is sent
        userClick(1);     // button is clicked so pass through next
                          // BM_SETHILITE
      }
      break;
  }
  return orgBtnProc(hBtn, uMsg, mp1, mp2);
}

//====================>>> vButtondCmd::CmdCallback <<<=======================
  void vButtonCmd::CmdCallback(UINT uMsg, MPARAM mp1, MPARAM mp2)
  {
    if (uMsg == WM_COMMAND)
    {
      if (dlgCmd->cmdType == C_ToggleIconButton ||        // depends on state
	  dlgCmd->cmdType == C_ToggleButton)
      {
        SysDebug3(OS2Dev,"CmdCallBack uMsg:%x mp1:%x mp2:%x \n", uMsg, mp1, mp2)
        SetCmdVal(!_retVal, Checked);
      }
    }
    // let parent window handle now
    _parentWin->ProcessCmd(_cmdId, _retVal, dlgCmd->cmdType);
  }
