//===============================================================
// vcmdprnt.cxx - vCmdParent Class - 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/22 stuff
#include <v/vapp.h>             // need the codepage
#include <v/vcmdprnt.h>         // our include
#include <v/vbtncmd.h>          // ButtonCmd
#include <v/vlabelc.h>          // LabelCmd
#include <v/vframec.h>          // FrameCmd
#include <v/vchkboxc.h>         // CheckBoxCmd
#include <v/vradioc.h>          // RadioButtonCmd
#include <v/vtextinc.h>         // TextInCmd
#include <v/vtextc.h>           // TextCmd
#include <v/vlistc.h>           // ListCmd
#include <v/vcomboc.h>          // ComboBoxCmd
#include <v/vspinc.h>           // SpinnerCmd
#include <v/vsliderc.h>         // SliderCmd
#include <v/vprogrsc.h>         // ProgressCmd
#include <v/vboxlblc.h>         // BoxedLabel

// ---------------------------------------------------------------------
// Dynamic Dialog Building Support
// ---------------------------------------------------------------------

  // this is an attempt to consolidate all the different margin
  // variables that are lying around, but alas all seem to be doing pretty
  // much the same thing. We will stick with StdMargin in vcmd.h as the
  // master margin variable.
  const int _CtrlSpacing = StdMargin;

  //======================>>> vCmdParent::vCmdParent <<<========================
  // In this tricky little function, the address of the pointer variable used
  // in the calling function to track the next free byte at the end of the
  // dialog template variable data region is accessed (*inp).
  // The function automatically increments this pointer so that after copying
  // the *src  string to the end of the data region, the original pointer in
  // the calling function now points to the new end of the data region.
  static void CopyStringToDialog (PSZ *inp, PCSZ src)
  {
    char *in = (char  * )(*inp);  // in assigned the address of *inp

    do
    {
      *in++ = (char) *src;
    } while (*src++);
    *inp = (char *)in;    // *inp reset to the new end of data
  }

//======================>>> vCmdParent::vCmdParent <<<==========================
  vCmdParent::vCmdParent(PaneType pt)
  {
    _cmdList = 0;
    maxX = 0;           // minimum size for a dialog
    maxY = 0;
    if (pt == P_Status)
    {
      maxX = 4;       // give some room at left for drawing box
      maxY = 4;
    }

    _callbackID = -1;   // no call back id
    _pt = pt;
  }

//======================>>> vCmdParent::~vCmdParent <<<============================
  vCmdParent::~vCmdParent()
  {
    SysDebug(Destructor,"vCmdParent::~vCmdParent() destructor\n")
    // need to delete everything we added
    DlgCmdList* tmp;
    if (_hTemplate != 0)
    {
      DosFreeMem(_hTemplate);

      _hTemplate = 0;
    }
    for (DlgCmdList* cc = _cmdList ; cc != 0  ; )
    {
      delete cc->cmdP;        // delete the cmd object
      tmp = cc;               // don't forget where we were
      cc = cc->nextDCL;       // for next time
      delete tmp;             // delete the list cell
    }
  }

//======================>>> vCmdParent::AddCmd <<<============================
  vCmd* vCmdParent::AddCmd(CommandObject* cmd)
  {
    // Private method to add a single command to a dialog object.
    SysDebug2(Build,"vCmdParent::AddCmd() - %s(id:%d)\n",cmd->title,cmd->cmdId)
    vCmd* cmdPtr;
    switch (cmd->cmdType)               // depends on type
    {
      case C_IconButton:              // a command button Icon
      case C_ToggleButton:            // a toggle icon button
      case C_ToggleIconButton:        // a toggle icon button
      case C_Button:                  // Button
      case C_ColorButton:             // ColorButton
      {
        cmdPtr = new vButtonCmd(this, cmd);
        break;
      }
      case C_CheckBox:                // Checked Item
      {
        cmdPtr = new vCheckBoxCmd(this, cmd);
        break;
      }
      case C_ComboBox:                // Popup combo list
      {
        cmdPtr = new vComboBoxCmd(this, cmd);
        break;
      }
      case C_Frame:                   // General purpose frame
      case C_ToggleFrame:             // A toggle frame
      {
        cmdPtr = new vFrameCmd(this, cmd);
        break;
      }
      case C_Icon:                    // a static icon
      case C_Blank:                   // to help RightOfs, Belows work
      case C_Label:                   // Regular text label
      case C_ColorLabel:              // Color text label
      {
        cmdPtr = new vLabelCmd(this, cmd);
        break;
      }
      case C_List:                    // List of items (scrollable)
      {
        cmdPtr = new vListCmd(this, cmd);
        break;
      }
      case C_ProgressBar:             // Progress bar
      {
        cmdPtr = new vProgressCmd(this, cmd);
        break;
      }
      case C_RadioButton:             // radio button
      {
        cmdPtr = new vRadioButtonCmd(this, cmd);
        break;
      }
      case C_Slider:                  // slider
      {
        cmdPtr = new vSliderCmd(this, cmd);
        break;
      }
      case C_BoxedLabel:
      {
        cmdPtr = new vBoxedLabelCmd(this, cmd);
        break;
      }
      case C_Text:                    // Text output field
      {
        cmdPtr = new vTextCmd(this, cmd);
        break;
      }
      case C_TextIn:                  // Text input field
      {
        cmdPtr = new vTextInCmd(this, cmd);
        break;
      }
      case C_Spinner:         // Value Box list
      {
        cmdPtr = new vSpinnerCmd(this, cmd);
        break;
      }
      default:                        // unknown!
      {
        SysDebug2(BadVals,"vCmdParent::AddCmd(id:%d, title:%s) ** Unknown DialogCommand\n",
          cmd->cmdId,cmd->title);
        cmd->title = "?? Bad CommandObject ??";
        cmdPtr = new vLabelCmd(this, cmd);
        break;
      }
    }
    return cmdPtr;
  }

//====================>>> vCmdParent::HasId <<<=========================
  int vCmdParent::HasId(ItemVal id) VCONST
  {
    // We need this one for panes to work correctly
    for (DlgCmdList* cc = _cmdList ; cc != 0  ; cc = cc->nextDCL)
    {
      if (((cc->cmdP)->dlgCmd)->cmdId == id)
      {
        return 1;
      }
    }
    return 0;
  }

//====================>>> vCmdParent::GetValue <<<=========================
  int vCmdParent::GetValue(ItemVal id) VCONST
  {
    for (DlgCmdList* cc = _cmdList ; cc != 0  ; cc = cc->nextDCL)
    {
      if (((cc->cmdP)->dlgCmd)->cmdId == id)
      {
        return (cc->cmdP)->GetCmdValue(id);  // return the value
      }
    }
    return 0;
  }

//====================>>> vCmdParent::SetFrameChildren <<<======================
  void vCmdParent::SetFrameChildren(ItemVal frameId, int frameVal)
  {
    // Set all children of given frame to hidden or not hidden
    // Do this recursively to be sure to get them all.
    // Scan list, setting all children that use this frame
    for (DlgCmdList* cc = _cmdList ; cc != 0  ; cc = cc->nextDCL)  // Find the frame
    {
      CommandObject* cop = (cc->cmdP)->dlgCmd;
      if (cop->cFrame == frameId)                     // A child of ours
      {
        // Hide control if not turning on (frameVal is opposite of Hidden)
        SetValue(cop->cmdId, !frameVal, Hidden);
        if (cop->cmdType == C_Frame || cop->cmdType == C_ToggleFrame)
        	SetFrameChildren(cop->cmdId, frameVal); // Get other chilren
      }
    }
  }

//====================>>> vCmdParent::GetTextIn <<<=========================
  int vCmdParent::GetTextIn(ItemVal id, char* strout, int maxlen)
  {
    // recover the string from TextIn id, up to maxlen
    for (DlgCmdList* cc = _cmdList ; cc != 0  ; cc = cc->nextDCL)
    {
      if (((cc->cmdP)->dlgCmd)->cmdId == id)
      {
        // need to cast to a vTextInCmd so we can use getstr
        vTextInCmd* tip = (vTextInCmd*) cc->cmdP;
        return tip->GetTextIn(id, strout, maxlen);  // return the value
      }
    }
    *strout = 0;
    return 0;
  }

//====================>>> vCmdParent::getParent <<<=======================
  HWND vCmdParent::getParent(void) VCONST
  {
    //  Return parent's HWND
    return _wDialog;
 }

//==================>>> vCmdParent::getThisFromId <<<=======================
  vCmd* vCmdParent::getThisFromId(ItemVal find_id)
  {
    // search the list of cmds for id, return its this
    DlgCmdList* cc;
    // search the list

    for (cc = _cmdList; cc != NULL ; cc = cc->nextDCL)
    {
      if (cc->cmdP == NULL || (cc->cmdP)->dlgCmd == NULL)
        return 0;

      if (((cc->cmdP)->dlgCmd)->cmdId == find_id)
      {
        return cc->cmdP;
      }
    }
    return 0;                           // failed to find it
  }

//====================>>> vCmdParent::SetPosition <<<=========================
  void vCmdParent::SetPosition(int& x, int& y, int w, int h, ItemVal frame,
    ItemVal rightOf, ItemVal below, int isSpinner)
  {
    // This method is responsible for controlling the layout of controls in
    // a dialog. All positions of controls are set relative to other controls,
    // which must have been previously defined (i.e., you start with the upper
    // left control and work to the lower right corner). It must dynamically
    // set the size of frames, and track the maximum x and y of the whole
    // dialog.
    //
    // note: As far as I can tell, the isSpinner variable is never used
    //       and is always 0 so I deleted all the isSpinner code

    int margin = _CtrlSpacing;  // spacing between controls
    int FrameMargin = _CtrlSpacing*2;  // spacing between controls and frame edge

    vCmd* frameCmd = 0;

    x = y = margin;         // start out with standard spacing

    if (frame != 0)             // in a frame?
    {
      // refresher:  DlgCmdList is a linked list of pointers to vCmd's
      //             _cmdList is the list of vCmd's (controls) for this dialog
      //             dlgCmd is a pointer to the vCmd static definition
      //
      for (DlgCmdList* cc = _cmdList ; cc != 0  ; cc = cc->nextDCL)
      {
        if ((cc->cmdP) == 0 || (cc->cmdP)->dlgCmd == 0) // not completely added yet!
          continue;

        // search for the vCmd of our frame
	if (((cc->cmdP)->dlgCmd)->cmdId == frame)
	{
	  frameCmd = cc->cmdP;            // remember our frame
	  if ((frameCmd->dlgCmd)->attrs & CA_NoSpace)
	    FrameMargin = 0;

          // position the control relative to the frame based on the specified
          // position data
	  if (rightOf == 0)
          {
	    x = (cc->cmdP)->_x + FrameMargin; // add to right of frame
            if ((frameCmd->dlgCmd)->attrs & CA_NoSpace)
              ++x;  // adjust for right side BEW 1.21
          }

	  else
	    x = FrameMargin;

	  if (below == 0)
          {
	    y = (cc->cmdP)->_y + FrameMargin;
            if ((frameCmd->dlgCmd)->attrs & CA_NoSpace)
            ++y;  // adjust for bottom  BEW 1.21
          }

	  else
	    y = FrameMargin;
	}
      }
    }  // end of frame procedure

    // check if its a command bar and not in a frame
    if (_dialogType == aCmdBar && frame == 0)
    {
      x = maxX + margin; // put the control at the end of the bar + margin
      y = margin;        // bars are linear in x (no y control allowed)
    }

    // check if its a status bar and not in a frame
    else if (_dialogType == aStatBar && frame == 0)
    {
      x = maxX + margin;  // put the control at the end of the bar + margin
//      y = margin + 1;   // adjust in y to center better
      y = margin;         // bars are linear in x (no y control allowed)
    }

    // else, its a regular dialog with full 2d positioning
    else
    {
      // refresher:  DlgCmdList is a linked list of pointers to vCmd's
      //             _cmdList is the list of vCmd's (controls) for this dialog
      //             dlgCmd is a pointer to the vCmd static definition
      //
      // cycle through all the controls in the dialog

      for (DlgCmdList* cc = _cmdList ; cc != 0  ; cc = cc->nextDCL)
      {
	// we skip the partially added current control
        if ((cc->cmdP) == 0 || (cc->cmdP)->dlgCmd == 0) // not completely added yet!
          continue;
        // set the control's x posn right of the 'rightof' control
        if (((cc->cmdP)->dlgCmd)->cmdId == rightOf)
        {
          x += ((cc->cmdP)->_x) + ((cc->cmdP)->_w); // position to right of the 'rightOf'
        }
        // set the control's y posn below the 'below' control
        if (((cc->cmdP)->dlgCmd)->cmdId == below)
        {
          y += ((cc->cmdP)->_y) + ((cc->cmdP)->_h); // position below the 'below'
        }
      }
    }

    // now we adjust the overall dialog size to enclose the
    // control we just added
    int cxr = x + w;                            // right x of control
    int cyb = y + h;                            // bottom y of control

    if (maxX < cxr)                             // track size of the dialog
      maxX = cxr;

    if (maxY < cyb)
      maxY = cyb;

    // if a frame was used, then we need to also adjust the
    // overall frame size to enclose the control we just added
    if (frameCmd)                               // need to fixup frame
    {
      FixFrame(cxr, cyb, FrameMargin, frameCmd);           // fixup frames
    }
  }

//====================>>> vCmdParent::FixFrame <<<=======================
// After a vCmd (control) is added to a frame, we need to readjust the
// overall frame size to make sure it encloses the control we just added
//
//  cxr is the controls rightmost extent
//  cyb is the controls bottommost extent
  void vCmdParent::FixFrame(int cxr, int cyb, int FrameMargin, vCmd* frameCmd)
  {
    int fxr = frameCmd->_x + frameCmd->_w;      // right x of frame
    int fyb = frameCmd->_y + frameCmd->_h;      // bottom y of frame

    if (fxr < cxr + FrameMargin)                     // need to make frame wider
    {
      frameCmd->_w = (cxr + FrameMargin) - frameCmd->_x;
      if (FrameMargin == 0)
        ++(frameCmd->_w);  // make nospace look right BEW 1.21
    }

    if (fyb < cyb + FrameMargin)                     // need to make frame taller
    {
      frameCmd->_h = (cyb + FrameMargin) - frameCmd->_y;
      if (FrameMargin == 0)
        ++(frameCmd->_h);  // make nospace look right BEW 1.21
    }

    // Check if we need fixup the overall dialog size
    if (maxX < frameCmd->_x + frameCmd->_w)
      maxX = frameCmd->_x + frameCmd->_w;
    if (maxY < frameCmd->_y + frameCmd->_h)
      maxY = frameCmd->_y + frameCmd->_h;

    // Now fixup the dialog template
    // The dialog template item for the frame control is already created
    // We need to update the template item size parameters with the new
    // values for the frame size
    if (frameCmd->_CtrlOffset != 0)
    {
      ULONG  *wNumBytes;

      PDLGTITEM pDlgTItem;

      // wNumBytes points to beginning of DLGTEMPLATE memory block.
      wNumBytes = (ULONG *) _hTemplate;
      // pDlgTItem points to start of new DLGTITEM.
      pDlgTItem = (PDLGTITEM) (((PSZ) wNumBytes) + frameCmd->_CtrlOffset);

      // Fix the width
      pDlgTItem->cx = frameCmd->_w;
      pDlgTItem->cy = frameCmd->_h;
    }

    // Now, fixup surrounding frames!
    int frame = (frameCmd->dlgCmd)->cFrame;
    if (frame != 0)     // Have to fix frame's frame
    {
      // refresher:  DlgCmdList is a linked list of pointers to vCmd's
      //             _cmdList is the list of vCmd's (controls) for this dialog
      //             dlgCmd is a pointer to the vCmd static definition
      //
      // cycle through all the controls in the dialog
      for (DlgCmdList* cc = _cmdList ; cc != 0  ; cc = cc->nextDCL)
      {
        if ((cc->cmdP) == 0 || (cc->cmdP)->dlgCmd == 0)  // not completely added yet!
          continue;
        if (((cc->cmdP)->dlgCmd)->cmdId == frame)
        {
          vCmd* fCmd = cc->cmdP;          // remember frame's frame
          fxr = frameCmd->_x + frameCmd->_w;      // right x of frame
          fyb = frameCmd->_y + frameCmd->_h;      // bottom y of frame
          FixFrame(fxr, fyb, FrameMargin, fCmd);
          break;
        }
      }
    }
  }

//====================>>> vCmdParent::SetValue <<<=======================
  void vCmdParent::SetValue(ItemVal id, ItemVal val, ItemSetType setType)
  {
    SysDebug2(Misc,"vCmdParent::SetValue(id:%d, val:%d)\n",id,val)
    for (DlgCmdList* cc = _cmdList ; cc != 0  ; cc = cc->nextDCL)
    {
      if (((cc->cmdP)->dlgCmd)->cmdId == id)
      {
        (cc->cmdP)->SetCmdVal(val,setType);  // return the value
        return;
      }
    }
  }

//====================>>> vCmdParent::SetString <<<=======================
  void vCmdParent::SetString(ItemVal id, char* str) VCONST
  {
    char trunc[80];
    SysDebug2(Misc,"vCmdParent::SetString(id:%d, str:%s)\n",id, strncpy(trunc, str, 60) )
    for (DlgCmdList* cc = _cmdList ; cc != 0  ; cc = cc->nextDCL)
    {
      if (((cc->cmdP)->dlgCmd)->cmdId == id)
      {
        (cc->cmdP)->SetCmdStr(str);  // return the value
        return;
      }
    }
  }

//====================>>> vCmdParent::CreateDlgTemplate <<<==================
// Creates a Dialog Template in memory.  Avoids needing a resource file
// to build up the dialog controls.

  int vCmdParent::CreateDlgTemplate(
    ULONG dtCtlData,		// dialog control data (FCF_*, etc)
    ULONG dtStyle,		// dialog style (WS_* and FS_*)
    SHORT dtX,      	        // location, In dialog-box units
    SHORT dtY,
    SHORT dtCX,       	        // size, in dialog-box units
    SHORT dtCY,
    SHORT dtID,	                // V doesn't assign an ID to the frame
    PSZ dtMenuName,		// "" if no menu  (not Implemented)
    PSZ dtClassName,		// "" if standard dialog box class  (not implemented)
    PSZ dtCaption,        	// caption text for dialog
    PPRESPARAMS pPP)		// Presentation Parameters
  {
    ULONG wBlockLen;
    ULONG wBaseMem, wItemMem;
    ULONG wCaptionLen, PPLen;
    PSZ szDlgTemplate, szDlgTypeFace;
    PDLGTEMPLATE pDlgTemplate;

    // Calculate number of bytes required by following fields:
    // Block must be large enough to contain the following:
    // # bytes: fixed part DLGTEMPLATE and one DLGTITEM
    // # bytes: dialog box caption.
    // # bytes: presentation parameters.

    wCaptionLen  = (1 + strlen(dtCaption));
    wBlockLen = sizeof(DLGTEMPLATE) + sizeof(dtCtlData) + wCaptionLen;

    if (pPP)
    {
      // Dialog box has Presentation Parameters
      // Calculate # of bytes required for PRESPARAM structure
      // cb gives the byte count of the aparam[] array, need to add
      // extra 4 bytes for pPP->cb count
      PPLen = pPP->cb + sizeof(ULONG);
      wBlockLen += PPLen;	// # bytes for PRESPARAM value (font typeface)
    }
    else
    {
      // Dialog box uses the System font.
      // Block length does not change.
      PPLen = 0;
    }

    // Allocate global block of memory for Dialog template.
    DosAllocMem ((PPVOID) &_hTemplate, wBlockLen, fALLOC);

    if (_hTemplate == NULL)
      return 0;            // memory alloc failed!

    // pDlgTemplate points to start of DLGTEMPLATE in block.
    pDlgTemplate = _hTemplate;

    // Set the members of the DLGTEMPLATE structure.
    pDlgTemplate->cbTemplate = wBlockLen;
    pDlgTemplate->type = 0;                // not used
    pDlgTemplate->codepage = theApp->AppCP();
    wBaseMem = (ULONG) pDlgTemplate;
    wItemMem = (ULONG) pDlgTemplate->adlgti;
    pDlgTemplate->offadlgti = (SHORT) wItemMem - wBaseMem;
    pDlgTemplate->fsTemplateStatus = 1;    // not used (docs say 0 but debug says 1)
    pDlgTemplate->iItemFocus = (USHORT) -1;         // no focus specified
    pDlgTemplate->coffPresParams = 0;      // never used!

    // Set the members of the frame DLGTITEM structure.
    // the first item is always the frame window class (WC_FRAME)
    pDlgTemplate->adlgti[0].fsItemStatus = 0;    // not used
    // no controls yet, Incremented with calls to AddDlgControl.
    pDlgTemplate->adlgti[0].cChildren = 0;
    pDlgTemplate->adlgti[0].cchClassName = 0;      	// use PM classes
    pDlgTemplate->adlgti[0].offClassName = SHORT1FROMMP((MPARAM) WC_FRAME);    // PM class
    pDlgTemplate->adlgti[0].cchText = wCaptionLen-1;     // strip null from string
    pDlgTemplate->adlgti[0].flStyle = dtStyle;
    pDlgTemplate->adlgti[0].x = dtX;
    pDlgTemplate->adlgti[0].y = dtY;
    pDlgTemplate->adlgti[0].cx = dtCX;
    pDlgTemplate->adlgti[0].cy = dtCY;
    pDlgTemplate->adlgti[0].id = dtID;       		// V doesn't seem to need this

    // szDlgTemplate points to start of variable part of DLGTEMPLATE.
    szDlgTemplate = (PSZ) (pDlgTemplate + 1);

    // store variable length caption
    // we compute the offset from here to start of DLGTEMPLATE and save
    wItemMem = (ULONG) szDlgTemplate;

    pDlgTemplate->adlgti[0].offText =  (SHORT) (wItemMem - wBaseMem);
    CopyStringToDialog(&szDlgTemplate, dtCaption);

    // store ULONG control data here
    wItemMem = (ULONG) szDlgTemplate;
    pDlgTemplate->adlgti[0].offCtlData =  (SHORT) wItemMem - wBaseMem;
    memcpy(szDlgTemplate, &dtCtlData, 4);
    szDlgTemplate += 4;

    // add the PRESPARAMS info
    if (pPP)
    {
      wItemMem = (ULONG) szDlgTemplate;
      pDlgTemplate->adlgti[0].offPresParams =  (SHORT) wItemMem - wBaseMem;
      memcpy(szDlgTemplate, pPP, PPLen);
      szDlgTemplate += PPLen;
      DosFreeMem (pPP);          // destroy pPP structure after use
    }
    else  // no PPs!
      pDlgTemplate->adlgti[0].offPresParams = (USHORT) -1;

    return 1;                   // success
}

//====================>>> vCmdParent::AddDlgControl <<<=====================
  int AddDlgControl (SHORT dtilX, SHORT dtilY, SHORT dtilCX, SHORT dtilCY,
        USHORT dtilID, ULONG dtilStyle, PCSZ dtilClass, PCSZ dtilText,
        PPRESPARAMS dtipPP, ULONG dtilCtlLen, PVOID dtilCtlData);

  int vCmdParent::AddDlgControl (
    SHORT dtilX, SHORT dtilY,	// In dialog-box units.
    SHORT dtilCX, SHORT dtilCY,	// In dialog-box units.
    USHORT dtilID,
    ULONG dtilStyle,		// WS_* BS_* etc.
    PCSZ dtilClass,		// Class name WC_*
    PCSZ dtilText,		// label
    PPRESPARAMS dtipPP,         // Pointer to presentation parameters
    ULONG dtilCtlLen,	        // Control Data length (bytes)
    PVOID dtilCtlData)          // Pointer to control data
  {
    ULONG wBlockLen, wTextLen,  wBaseMem, wItemMem;
    ULONG PPLen;
    USHORT *wCtlLen;
    PDLGTEMPLATE pOldDlgTemplate, pDlgTemplate;
    PDLGTITEM pDlgTItem;

    PSZ pszOldData, pszData;

    int i, numControls = 0;	// number of controls in dialog

    // Calculate number of bytes required by following fields:
    wTextLen  = (1 + strlen(dtilText)) * sizeof (char);

    // Block must be increased by the following amount:
    wBlockLen =
      sizeof(DLGTITEM) +      // # bytes for fixed part of DLGTITEM.
      wTextLen +		// # bytes for control text.
      dtilCtlLen;	        // # bytes for extra CTRL data.

    if (dtipPP)
    {
      // Dialog box has Presentation Parameters
      // Calculate # of bytes required for PRESPARAM structure
      // cb gives the byte count of the aparam[] array, need to add
      // extra (ULONG) bytes for dtipPP->cb member
      PPLen = dtipPP->cb + sizeof(ULONG);

      // Block must be large enough to include PP information.
      wBlockLen += PPLen;	// # bytes for PRESPARAM value (font typeface)
    }
    else
    {
      // Dialog box uses the System font.
      // Block length does not change.
      PPLen = 0;
    }

    // Add to number of bytes currently in the memory block.
    wBlockLen += _hTemplate->cbTemplate;

    // pOldDlgTemplate points to start of old DLGTEMPLATE in block.
    pOldDlgTemplate = _hTemplate;

    // Allocate new larger global block of memory for Dialog template.
    // this is probably really inefficient but what else is there to do?
    DosAllocMem ((PPVOID) &pDlgTemplate, wBlockLen, fALLOC);

    if (pDlgTemplate == NULL)
      return 0;            // memory alloc failed!

    _hTemplate = pDlgTemplate;
    wBaseMem = (ULONG) pDlgTemplate;  // used for offset calcs

    // copy over the old template data to the new structure
    // we will fix up the offsets and variable data area later
    memcpy(pDlgTemplate, pOldDlgTemplate, pOldDlgTemplate->cbTemplate);

    // save the total size of the new template
    pDlgTemplate->cbTemplate = wBlockLen;

    // Increment the number of controls in the WC_FRAME template item.
    numControls = ++pDlgTemplate->adlgti[0].cChildren;

    // set pDlgTItem to point to the start of new DLGTITEM.
    // This is near the end of the memory block.
    pDlgTItem = &pDlgTemplate->adlgti[numControls];

    // Set the members of the DLGTITEM structure.
    pDlgTItem->fsItemStatus = 0;         // not used
    pDlgTItem->cChildren = 0;            // only WC_FRAME has children
    pDlgTItem->cchClassName = 0;      	 // use PM classes
    pDlgTItem->offClassName = SHORT1FROMMP((MPARAM) dtilClass); // WC_*
    pDlgTItem->cchText = wTextLen-1;     // label length (drop null at end)
    pDlgTItem->flStyle = dtilStyle;      // WS_* BS_*, MLE_* etc.
    pDlgTItem->x = dtilX;
    pDlgTItem->y = dtilY;
    pDlgTItem->cx = dtilCX;

    // We have to handle comboboxes specially because the
    // height needed here is the combobox PLUS its list, while
    // the height we want to use internally is the height WITHOUT the list
    if (dtilClass == WC_COMBOBOX )	// Combo?
      pDlgTItem->cy = dtilCY + 8*6;
    else
      pDlgTItem->cy = dtilCY;

    pDlgTItem->id = dtilID;
    pDlgTItem->offPresParams = (USHORT) -1;       	// default

    // now we need to fix up the variable data area
    // pszData points to start of variable data part of the template
    pszData = (PSZ) (pDlgTItem + 1);

    for (i=0; i<numControls; i++)
    {
      // store text label and compute offset to start of DLGTEMPLATE
      wItemMem = (ULONG) pszData;
      pDlgTemplate->adlgti[i].offText =  (USHORT) (wItemMem - wBaseMem);

      pszOldData = (PSZ) pOldDlgTemplate;               // base of old template
      pszOldData += pOldDlgTemplate->adlgti[i].offText;  // pointer to old text
      CopyStringToDialog(&pszData, pszOldData);

      // store PRESPARAMS and compute offset to start of DLGTEMPLATE
      // if offset = -1 then no data
      if (pOldDlgTemplate->adlgti[i].offPresParams == (USHORT) -1)
	pDlgTemplate->adlgti[i].offPresParams = (USHORT) -1;

      else  // else copy over data
      {
	wItemMem = (ULONG) pszData;
	pDlgTemplate->adlgti[i].offPresParams =  (USHORT) wItemMem - wBaseMem;

	pszOldData = (PSZ) pOldDlgTemplate;                      // base of old template
	pszOldData += pOldDlgTemplate->adlgti[i].offPresParams;  // pointer to old CtlData
	// get the data length in bytes (first 4 bytes of data)
	PPLen = (ULONG) *pszOldData;
	memcpy(pszData, pszOldData, PPLen + sizeof(ULONG));
	pszData += PPLen + sizeof(ULONG);
      }

      // store CTLDATA and compute offset to start of DLGTEMPLATE
      // if offset = -1 then no data
      if (pOldDlgTemplate->adlgti[i].offCtlData == (USHORT) -1)
	pDlgTemplate->adlgti[i].offCtlData = (USHORT) -1;

      else  // else copy over data
      {
	wItemMem = (ULONG) pszData;
	pDlgTemplate->adlgti[i].offCtlData =  (USHORT) wItemMem - wBaseMem;

	pszOldData = (PSZ) pOldDlgTemplate;                   // base of old template
	pszOldData += pOldDlgTemplate->adlgti[i].offCtlData;  // pointer to old CtlData
	// get the data length in bytes (first 4 bytes of data)
	// Note: WC_FRAME class is non-conforming.  It expects a 4 byte
	//       ULONG in the data area with no size info
	if ( pOldDlgTemplate->adlgti[i].offClassName == SHORT1FROMMP((MPARAM) WC_FRAME))
	{
	  memcpy(pszData, pszOldData, 4);  // non-conforming
	  pszData += 4;
	}
	else
	{
	  wCtlLen = (USHORT*) pszOldData;         // conforming
	  memcpy(pszData, pszOldData, *wCtlLen);
	  pszData += *wCtlLen;
	}
      }
    }

    // now we finish off by storing the current control's data
    // store control label and compute offset to start of DLGTEMPLATE
    wItemMem = (ULONG) pszData;
    pDlgTemplate->adlgti[i].offText =  (USHORT) (wItemMem - wBaseMem);
    CopyStringToDialog(&pszData, dtilText);

    // store PRESPARAMS and compute offset to start of DLGTEMPLATE
    if (dtipPP == 0)   // if no data set offset to -1
      pDlgTemplate->adlgti[i].offPresParams = (USHORT) -1;
    else
    {
      wItemMem = (ULONG) pszData;
      pDlgTemplate->adlgti[i].offPresParams =  (USHORT) wItemMem - wBaseMem;
      memcpy(pszData, dtipPP, dtipPP->cb + sizeof(ULONG));
      pszData += dtipPP->cb + sizeof(ULONG);
      DosFreeMem (dtipPP);   // don't forget to clean up afterwards
    }

    // store CTLDATA and compute offset to start of DLGTEMPLATE
    if (dtilCtlLen == 0)   // if no data set offset to -1
      pDlgTemplate->adlgti[i].offCtlData = (USHORT) -1;
    else
    {
      wItemMem = (ULONG) pszData;
      pDlgTemplate->adlgti[i].offCtlData =  (USHORT) wItemMem - wBaseMem;
      memcpy(pszData, dtilCtlData, dtilCtlLen);
      pszData += dtilCtlLen;
    }

    // now destroy the old template
    DosFreeMem(pOldDlgTemplate);

    // offset to start of last control
    return (int) &pDlgTemplate->adlgti[i] - wBaseMem;
  }


//==================>>> vCmdParent::DoneAddingControls <<<===================
  void vCmdParent::DoneAddingControls(void)
  {
    PDLGTEMPLATE pDlgTemplate;

    // set pDlgTemplate to start of DLGTEMPLATE in block.
    pDlgTemplate = _hTemplate;

    // Fixup size
    maxX = pDlgTemplate->adlgti[0].cx = maxX + _CtrlSpacing;
    maxY = pDlgTemplate->adlgti[0].cy = maxY + _CtrlSpacing;

    // we need to transform all control y-dimensions since V uses upper left as
    // origin and OS/2 assumes lower left

    ULONG  *wNumBytes;
    PDLGTITEM pDlgTItem;

    // set wNumBytes to beginning of DLGTEMPLATE memory block.
    wNumBytes = (ULONG *) _hTemplate;

    for (DlgCmdList* cc = _cmdList ; cc != 0  ; cc = cc->nextDCL)
    {
      // transform the y coord in the dialog template
      if ( (cc->cmdP)->_CtrlOffset != 0)
      {
	// set pDlgTItem to start of current control DLGTITEM.
	pDlgTItem = (PDLGTITEM) (((PSZ) wNumBytes) + (cc->cmdP)->_CtrlOffset);

	// invert and shift y coord
	if ( (((cc->cmdP)->dlgCmd)->cmdType) == C_ComboBox )	// Combo?
	  pDlgTItem->y = maxY - (cc->cmdP)->_y - (cc->cmdP)->_h - 8*6;
	else
	  pDlgTItem->y = maxY - (cc->cmdP)->_y - (cc->cmdP)->_h;
      }
    }
  }


//================>>> vCmdParent::ChangeDlgTButtonColor <<<=====================
//  routine to change the color button color in the dialog template
//  after the template is assembled in memory
//
void vCmdParent::ChangeDlgTButtonColor(int CtrlOffset, USHORT cmdId, ULONG newColor )
{
  PDLGTITEM pDlgTItem;

  // set pDlgTItem to start of current control DLGTITEM.
  pDlgTItem = (PDLGTITEM) (((PSZ) _hTemplate) + CtrlOffset);

  // a sanity check
  if (pDlgTItem->id != cmdId)
  {
    // something's gone wrong, better punt!
    return;
  }
  // find the button color pres params word in the template...
  // this is a bit dangerous since we assume that the button
  // color pres param order is fixed, but since we create the template
  // we do have control over this so it shouldn't break later
  // and it makes life simpler this way
  ULONG *buttonColor;
  buttonColor = (ULONG *) (((PSZ) _hTemplate) + pDlgTItem->offPresParams);

  // button color is 3rd ULONG in from start of Pres Params
  buttonColor += 3;
  *buttonColor = newColor;
}

//====================>>> vCmdParent::AssyPresParams <<<=====================
//----- routine to assemble an arbitrary size PP structure
  PPRESPARAMS vCmdParent::AssyPresParams(PPElement *PPel)
  {
    ULONG PPlen;
    int i;
    PPARAM ixPP;
    PPRESPARAMS pPP;

    // first compute necessary storage space for PPs
    PPlen = sizeof(ULONG);    // pp.cb
    for (i=0; PPel[i].PPtype; i++)
    {
      if (PPel[i].PPtype == PP_FONTNAMESIZE)
      // its a font PP
	PPlen += sizeof(PARAM)-1 + strlen(PPel[i].PPval.font)+1;
      else
	// its a color PP
	PPlen += sizeof(PARAM)-1 + sizeof(ULONG);
    }

    // now we build up the PRESPARAM structure
    DosAllocMem ((PPVOID) &pPP, PPlen, fALLOC);

    // Danger: PRESPARAMS is so braindead that it is unworkable
    //         as designed.  It uses a PARAM as an indexed array
    //         internally, but PARAM is variable length so that
    //         the indexing fails after the first element!
    pPP->cb = PPlen - sizeof(ULONG);
    ixPP = pPP->aparam;
    for (i=0; PPel[i].PPtype; i++)
    {
      ixPP->id = PPel[i].PPtype;
      if (PPel[i].PPtype == PP_FONTNAMESIZE)
      { // its a font PP
	ixPP->cb = strlen(PPel[i].PPval.font)+1;
	strcpy(ixPP->ab, PPel[i].PPval.font);
      }
      else
      {	// its a color PP
	ixPP->cb = sizeof(ULONG);
	memcpy(ixPP->ab, &PPel[i].PPval.color, sizeof(ULONG));
      }
      // now we set the index to the next PARAM element in the PRESPARAM structure
      // Here, we temporarily set ixPP to a PSZ so we can do byte size pointer
      // arithmetic
      ixPP = (PPARAM) (((PSZ) ixPP) + sizeof(PARAM)-1 + ixPP->cb);
    }
    return(pPP);
  }

// ---------------------------------------------------------------------



