//===============================================================
// vspinc.cxx   - vSpinnerCmd - Windows
//
// Copyright (C) 1995,1996,1997,1998  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/vspinc.h>   // our definitions
#include <v/vcmdprnt.h> // a command parent
#include <v/vapp.h>
#include <v/vutil.h>

//=================>>> vSpinnerCmd::vSpinnerCmd <<<=======================
  vSpinnerCmd::vSpinnerCmd(vCmdParent* dp, CommandObject* co) :
	    vCmd(dp, co)
  {
    initialize();                       // and initialize
  }
//=======================>>> vSpinnerCmd::~vSpinnerCmd <<<=====================
  vSpinnerCmd::~vSpinnerCmd()
  {
    SysDebug(Constructor,"vSpinnerCmd::~vSpinnerCmd() Destructor\n")
    if (_TextList == 2)
      DosFreeMem ((PPVOID) &_fullList);
  }
//=====================>>> vSpinnerCmd::initialize <<<=======================
  void vSpinnerCmd::initialize(void)
  {
    long style;
    SysDebug(Constructor,"vSpinnerCmd::vSpinnerCmd() constructor\n")
    CopyToLocal();                      // Make local copies of CmdObject

    // First, build the list parameters
    _fullList = 0;
    SetupList();

    style = SPBS_READONLY | SPBS_MASTER | SPBS_FASTSPIN; // readonly master control

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

    if (_TextList==1)   // array of strings
    {
      strncpy(_spinTitle, _fullList[_curSelection], 255);
    }
    else if (_TextList==2)   // array of non-consecutive integers
    {
      IntToStr(_curSelection, _spinTitle);
    }
    else             // numeric list
    {
      style |= SPBS_NUMERICONLY;
      IntToStr(_curSelection, _spinTitle);
      _scd.lLowerLimit = _minVal;
      _scd.lUpperLimit = _maxVal;
    }
    _title = _spinTitle;

    _scd.cbSize = sizeof(SPBCDATA);

    _maxWidth = _maxWidth + 14;        // change chars to dialog pixels
    _maxWidth = (dlgCmd->attrs & CA_Large) ? (_maxWidth * 3)/2
					      : _maxWidth;
    if (dlgCmd->size > 0 && dlgCmd->size < 2048)
    {
	_maxWidth = dlgCmd->size;
        if (_maxWidth < 24 )  // trap ridiculous size
          _maxWidth = 24;
    }

    _w = _maxWidth;
    _h = 11;

    _parentWin->SetPosition(_x, _y, _w, _h, dlgCmd->cFrame, dlgCmd->cRightOf,
	dlgCmd->cBelow);

    _CtrlOffset = _parentWin->AddDlgControl(_x, _y , _w, _h, _cmdId,
	   style, WC_SPINBUTTON, _title, NULL, _scd.cbSize, &_scd);
  }

//====================>>> vSpinnerCmd::SetupList <<<=======================
  void vSpinnerCmd::SetupList(void)
  {
    // Set up the list for use
    // Scans list and determines if text or numeric
    // If text, sets _TextList=1, _step=1, _numItems, computes max
    // field width (_maxWidth) and sets _curSelection to default value
    //
    // If numeric, sets _TextList=0, _minVal, _maxVal, _step,
    // computes field width (_maxWidth), and sets _curSelection
    // to default value

    int width;
    _maxWidth = 0;                      // no items yet

    if (dlgCmd->attrs & CA_Text)        // A text list of values
    {
      _fullList = (char**)_itemList;              // list
      _TextList = 1;                  // This is a text list
      _step = 1;
      for ( _numItems = 0 ; _fullList[_numItems] != 0 ; ++_numItems)
      {
	width = LabelWidth(_fullList[_numItems]) + 2;   // text width
	if (width > _maxWidth)
	  _maxWidth = width;              // track largest so far
      }
      if (_numItems <= 0)
      {
	SysDebug(BadVals,"Bad list provided for ValueBox\n");
      }
      // Set to default value
      if (_retVal >= 0 && _retVal < _numItems)
	_curSelection = _retVal;
      else
	_curSelection = 0;          // item 0 by default
    }
    else       // an integer list with arbitrary step
    {
      _TextList = 0;                  // This is an integer list
      int* minMaxList = (int *) _itemList;            // list
      _minVal = 0; _maxVal = 0x7fff; _step = 1;

      if (minMaxList != 0)            // provided a range list
      {
	_minVal = minMaxList[0];
	_maxVal = minMaxList[1];
	_step = minMaxList[2];
	_TextList = 2;                  // This is a non-consecutive integer list

	// Try to make the width pretty
	char buff[20];
	IntToStr(_minVal,buff);
	int maxChars = strlen(buff);
	_maxWidth = LabelWidth(buff) + 2;
	IntToStr(_maxVal,buff);
	int maxC = strlen(buff);
	int maxW = LabelWidth(buff) + 2;
	if (maxW > _maxWidth)
	  _maxWidth = maxW;
	if (maxC > maxChars)
	  maxChars = maxC;

	// now build up an array of strings of each value
	int i;

	// compute number of items and store in _numItems
	for(_numItems=0, i=_minVal; i<=_maxVal; i+=_step, _numItems++);

	// build a character array of values if step!=1
	if (_fullList != 0)   // check if re-allocation!
	  DosFreeMem ((PPVOID) &_fullList);
	DosAllocMem ((PPVOID) &_fullList, _numItems*(maxChars+5), fALLOC);

	char *item;
	item = (CHAR *) _fullList;  //set to first item
        // reserve sapce for list of pointers to item strings
	item += (_numItems)*4;
	for(i=0; i<_numItems; i++)
	{
	  IntToStr(_minVal + i*_step, item);
	  _fullList[i]=item;
	  item += maxChars+1;
	}
      }
      else    // an integer list from 0 to 32767 with step=1
      {       // (initial value _retVal)
        _minVal = 0; _maxVal = 0x7fff; _step = 1;
	// Try to make the width pretty
	char buff[20];
	IntToStr(_minVal,buff);
	_maxWidth = LabelWidth(buff) + 2;
	IntToStr(_maxVal,buff);
	int maxW = LabelWidth(buff) + 2;
	if (maxW > _maxWidth)
	  _maxWidth = maxW;
      }
      _curSelection = _retVal;
    }
  }

//==================>>> vSpinnerCmd::GetCmdValue <<<=========================
  int vSpinnerCmd::GetCmdValue(ItemVal id) VCONST
  {
    if (id != _cmdId)
	return -1;
    return _curSelection;
  }
//=====================>>> vSpinnerCmd::SetCmdVal <<<=========================
  void vSpinnerCmd::SetCmdVal(ItemVal val, ItemSetType st)
  {
    SysDebug2(Misc,"vSpinnerCmd::SetCmdVal(val:%d, type:%d)\n",val,st)
    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 ChangeList:
      case ChangeListPtr:
      {
	if (st == ChangeListPtr)
	   _itemList = dlgCmd->itemList;

	int oldMax = _maxWidth;         // track current max width

	SetupList();                    // reset the list variables

	if (oldMax > _maxWidth)
	    _maxWidth = oldMax;         // don't let it get narrower

	// redisplay text
	// populate the listbox
	if (_TextList)
	  WinSendMsg(myHwnd, SPBM_SETARRAY, _fullList, (MPARAM) _numItems);
	else
	  WinSendMsg(myHwnd, SPBM_SETLIMITS, (MPARAM) _maxVal, (MPARAM) _minVal);

	WinEnableWindow (myHwnd, _Sensitive);

	if (dlgCmd->attrs & CA_Hidden)
	  WinShowWindow(myHwnd,FALSE);
	else
	  WinShowWindow(myHwnd,TRUE);

	_curSelection = val;
	SetCmdVal(_curSelection,Value);
	return;
      }

      case Value:       // select a given item
      {
	_curSelection = val;    // change the current value
	// See if the currently selected item is in the list
	if (_TextList == 1)          // handle like this for text list
	{
	  if (_itemList)
	  {
	    if (_curSelection < 0)
	      _curSelection = 0;
	    if (_curSelection >= _numItems)
	      _curSelection = _numItems - 1;
	  }
	  // Now set appropriate _curSelection
	  WinSendMsg(myHwnd, SPBM_SETCURRENTVALUE,
	    MPFROMLONG(_curSelection), (MPARAM) NULL);
	}
	else if (_TextList == 2)       // numeric non-contiguous values
	{
	  if (_curSelection < _minVal)
	    _curSelection = _minVal;
	  if (_curSelection >= _maxVal)
	    _curSelection = _maxVal;

	  int index = (_curSelection - _minVal)/_step;
	  WinSendMsg(myHwnd, SPBM_SETCURRENTVALUE,
	      MPFROMLONG(index), (MPARAM) NULL);
	}

	else                    // numeric contiguous values
	{
	  if (_curSelection < _minVal)
	    _curSelection = _minVal;
	  if (_curSelection >= _maxVal)
	    _curSelection = _maxVal;
	  WinSendMsg(myHwnd, SPBM_SETCURRENTVALUE,
	      MPFROMLONG(_curSelection), (MPARAM) NULL);
	}
      }
    }
  }
//===================>>> vSpinnerCmd::vCmdCallback <<<=======================
  void vSpinnerCmd::CmdCallback(UINT uMsg, MPARAM mp1, MPARAM mp2)
  {
    // See if we are getting a message we care about
    if (uMsg == WM_CONTROL)
    {
//	SysDebug1(OS2Dev,"vSpinnerCmd::CmdCallBack \n")
      char buf[255];

      switch (SHORT2FROMMP(mp1))
      {
	case SPBN_DOWNARROW:		// Down one item
	case SPBN_UPARROW:		// Up one item
	{
	  // Retrieve the current selection
	  if (_TextList == 1)
	  {
	    // find out the spin button value and associate with its array index
	    WinSendDlgItemMsg(_parentWin->getParent(),_cmdId,
	      SPBM_QUERYVALUE, buf, MPFROM2SHORT(sizeof(buf), SPBQ_DONOTUPDATE));
	    for ( int i = 0 ; _fullList[i] != 0 ; ++i)
	    {
	      if(strcmp(buf, _fullList[i])==0)
		_curSelection = i;
	    }
	    if (_curSelection >= _numItems)  // sanity check
	      _curSelection = _numItems -1;
	  }
	  else if (_TextList == 2)  // non-consecutive integers
	  {
	    // find out the spin button value and convert to integer
	    WinSendDlgItemMsg(_parentWin->getParent(),_cmdId,
	      SPBM_QUERYVALUE, buf, MPFROM2SHORT(sizeof(buf), SPBQ_DONOTUPDATE));
	    for ( int i = 0 ; i < _numItems ; ++i)
	    {
	      if(strcmp(buf, _fullList[i])==0)
	      {
//		_curSelection = StrToLong(_fullList[i]);
		_curSelection = atol(_fullList[i]);
	      }
	    }
	    if (_curSelection > _maxVal)  // sanity check
	      _curSelection = _maxVal;
	    if (_curSelection < _minVal)
	      _curSelection = _minVal;
	  }
	  else  // numeric
	  {
	    WinSendDlgItemMsg(_parentWin->getParent(),_cmdId, SPBM_QUERYVALUE,
	      (MPARAM) &_curSelection, MPFROM2SHORT(0, SPBQ_DONOTUPDATE));
	    if (_curSelection > _maxVal)  // sanity check
	      _curSelection = _maxVal;
	    if (_curSelection < _minVal)
	      _curSelection = _minVal;
	  }
	  if (!(dlgCmd->attrs & CA_NoNotify)) // Notify on each selection?
	    _parentWin->ProcessCmd(_cmdId, _curSelection, dlgCmd->cmdType);
	break;
	} // SPBM

	default:
	  return;                      // punt on other messages
      }
    } //uMsg
  }


