//===============================================================
// vdialog.cxx - vDialog class - OS/2
//
// 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/vdialog.h>          // our header
#include <v/vapp.h>
#include <v/vthislst.h>         // For this list

  // Define static data of the class
  MRESULT EXPENTRY DlgProcCB(HWND hDlg, ULONG uMsg, MPARAM mp1, MPARAM mp2);

  static vThisList _thisList;           // hide this in this file
  vDialog* vDialog::_curModal = 0;      // V:1.13

//===================>>> vDialog::vDialog <<<=======================
  vDialog::vDialog(const vDialog& d) : vBaseWindow(d),
    vCmdParent(P_Dialog)
  {
    vSysError("vDialog - V semantics do not support copy constructors!");
    _oldModal = 0;              // V:1.13
  }

//=================>>> vDialog::~vDialog <<<=======================
  vDialog::~vDialog()
  {
    SysDebug(Destructor,"vDialog::~vDialog() destructor\n")
    _IsDisplayed = 0;           // The dialog is not up
    if (_hTemplate != 0)
      {
	::DosFreeMem(_hTemplate);
	_hTemplate = 0;
      }
    closeBaseWindow();          // close this window
    _thisList.Delete((ThisId)_wDialog); // free the this ptr
  }

//===================>>> vDialog::vDialog <<<=======================
  vDialog::vDialog(vBaseWindow* creator, int modal, VCONST char* title) :
    vBaseWindow(title), vCmdParent(P_Dialog)            // constructor
  {
     _parentHWND = creator->winHwnd();  // track parent's HWND
     init(modal,title);                 // finish construction
  }

//===================>>> vDialog::vDialog <<<=======================
  vDialog::vDialog(vApp* creator, int modal, VCONST char* title) :
    vBaseWindow(title), vCmdParent(P_Dialog)            // constructor
  {
     _parentHWND = creator->winHwnd();  // track parent's HWND
     init(modal,title);                 // finish construction
  }

//===================>>> vDialog::vDialog <<<=======================
  void vDialog::init(int modal, VCONST char* title)
  {
    // Initialize dialog box
    _modal = modal;             // settable modal
    SysDebug(Constructor,"vDialog::vDialog() constructor\n")
    _wType = DIALOG;            // a dialog
    _dialogType = aDialog;      // This is a dialog
    _IsDisplayed = 0;           // The dialog is not up
    _wDialog = 0;               // haven't created the dialog yet...
    hwndToolTip = 0;

    // Now, build the initial in-memory dialog template using
    // DLGTEMPLATE  pointer _hTemplate.
    int buildOK;
    // Using 8,helv makes it ok to use 8 in calculating control sizes!
#ifdef ALLOW_BOLD_FONT
    // get screen width in pels
    int horRes;
    HDC hDevCxt = GpiQueryDevice(WinGetScreenPS(HWND_DESKTOP));
    DevQueryCaps(hDevCxt, CAPS_WIDTH, 1L, &horRes);
    char *fntname = (horRes <= 800) ? "8.Helv" : "8.HelvBold";
#else
    char *fntname = "8.Helv";
#endif

//    PPElement PPel[] = { {PP_FONTNAMESIZE,(ULONG) "8.Helv"}, {0}};
    PPElement PPel[] = { {PP_FONTNAMESIZE,(ULONG) fntname}, {0}};

    if (_modal)
	buildOK = CreateDlgTemplate( FCF_TITLEBAR | FCF_DLGBORDER |
//	   FCF_NOMOVEWITHOWNER, WS_VISIBLE | WS_CLIPSIBLINGS | WS_SAVEBITS,
	   FCF_NOMOVEWITHOWNER, WS_VISIBLE | WS_CLIPSIBLINGS,
	   10,25, 1,1, 0, "", "", _name, AssyPresParams(PPel) );

    else
	buildOK = CreateDlgTemplate( FCF_TITLEBAR | FCF_DLGBORDER |
//	   FCF_NOMOVEWITHOWNER, WS_VISIBLE | WS_CLIPSIBLINGS | WS_SAVEBITS,
	   FCF_NOMOVEWITHOWNER, WS_VISIBLE | WS_CLIPSIBLINGS,
	   10,25, 1,1, 0, "", "", _name, AssyPresParams(PPel) );

    if (!buildOK)
	vSysError("vDialog - Unable to build dialog template");
    _DefaultButton = 0; // No default button described
    _FirstTextIn = 0;   // No text in field
  }

//===================>>> vDialog::AddDialogCmds <<<========================
  void vDialog::AddDialogCmds(CommandObject* cList)     // add commands
  {
    // This is called to add commands from the supplied
    // CommandObject list.
    vCmd* defButton = 0;
    DlgCmdList* curCmd;
    // scan the entire list
    for (int ix = 0 ; cList && (cList[ix].cmdType != C_EndOfList) ; ++ix)
      {
	curCmd = new DlgCmdList;                // get a new cell
	curCmd->nextDCL = _cmdList;             // add in at front
	_cmdList = curCmd;
        curCmd->cmdP = 0;                       // not added yet
        curCmd->cmdP = AddCmd(&cList[ix]);
        // Track default button
        if (cList[ix].attrs & CA_DefaultButton)
          {
             defButton = curCmd->cmdP;
          }
        if (cList[ix].cmdType == C_TextIn)
          {
            if (_FirstTextIn == 0)
                _FirstTextIn = curCmd->cmdP;
          }
      }
    if (defButton)                      // we have a default
      {
        _DefaultButton = defButton;     // Track the default button
      }
    DoneAddingControls();               // all finished with controls
  }

//===================>>> vDialog::AddDialogCmdObj <<<========================
  void vDialog::AddDialogCmdObj(CommandObject* Cmd, vCmd* CmdInstance)
  {
    // This is called to add an already instantiated command object to the
    // CommandObject list.
    DlgCmdList* curCmd = new DlgCmdList;                // get a new cell
    curCmd->nextDCL = _cmdList;         // add in at front of list
    _cmdList = curCmd;
    curCmd->cmdP = CmdInstance;         // point to the object
    // Track default button
    if (Cmd->attrs & CA_DefaultButton)
      {
        _DefaultButton = CmdInstance;   // This is the default button
        // This lets us use Enter to activate the default button.
      }
  }

//====================>>> vDialog::CancelDialog <<<=======================
  void vDialog::CancelDialog(void)
  {
    // Cancel selected - reset all values to original values

    SysDebug(CmdEvents,"vDialog::CancelDialog()\n")
    for (DlgCmdList* cc = _cmdList ; cc != 0  ; cc = cc->nextDCL)
      {
        (cc->cmdP)->ResetItemValue();
      }
    // And close the dialog
    CloseDialog();
  }

//====================>>> vDialog::CloseDialog <<<=======================
  void vDialog::CloseDialog(void)
  {
    SysDebug(Build,"vDialog::CloseDialog()\n")
    _IsDisplayed = 0;           // The dialog is not up
    if (!_modal)                // destroy if not modal
      WinDestroyWindow(_wDialog);

    _thisList.Delete((ThisId)_wDialog); // free the this ptr
  }

//====================>>> vDialog::DialogCommand <<<=======================
  void vDialog::DialogCommand(ItemVal id, ItemVal retval, CmdType ctype)
  {
    // After the user has selected a command from the dialog,
    // this routine is called with the value

    SysDebug2(CmdEvents,"vDialog::DialogCommand(id:%d, val:%d)\n",id, retval)
    switch (id)                 // We will do some things depending on value
      {
        case M_Cancel:
            CancelDialog();
            break;
	case M_Done:            // default, close dialog
        case M_OK:
          {
            CloseDialog();
            break;
          }
      }
  }

//====================>>> vDialog::DialogDisplayed <<<=======================
  void vDialog::DialogDisplayed()
  {
  }

//==================>>> vDialog::ProcessCmd <<<=======================
  void vDialog::ProcessCmd(ItemVal id, ItemVal rv, CmdType ct)
  {
    // simple interface between command objects and the
    // inherited vCmdParent ProcessCmd to the CommandObject which does the work
    DialogCommand(id, rv, ct);
  }

//====================>>> vDialog::SetDialogTitle <<<=======================
  void vDialog::SetDialogTitle(VCONST char* title)
  {
    WinSetWindowText(_wDialog, title);
  }

//================>>> vDialog::GetDialogPosition <<<========================
  void vDialog::GetDialogPosition(int& left, int& top, int& width, int& height) VCONST
  {
    RECTL rct;

    WinQueryWindowRect (_wDialog, &rct);
    WinMapWindowPoints (_wDialog ,HWND_DESKTOP, (PPOINTL) &rct, 2);

    LONG DisplayHeight = WinQuerySysValue(HWND_DESKTOP, SV_CYSCREEN);

    // in OS/2 the origin is at the bottom left corner whereas V has the
    // origin in the top left corner.  We need to transform coords from
    // os/2 to V space to get the right results
    // CAUTION: WinQuery return rc coords are inclusive/exclusive
    top = (DisplayHeight-1) - rct.yTop;
    left = rct.xLeft;
    width = rct.xRight - rct.xLeft;
    height = rct.yTop - rct.yBottom;
  }

//====================>>> vDialog::SetDialogPosition <<<=======================
//
//  position is relative to the screen, not the application
//
  void vDialog::SetDialogPosition(int left, int top)
  {
    RECTL ar;
    WinQueryWindowRect (_wDialog, &ar);
    int height =  ar.yTop - ar.yBottom;
    LONG DisplayHeight = WinQuerySysValue(HWND_DESKTOP, SV_CYSCREEN);

    // in OS/2 the origin is at the bottom left corner whereas V has the
    // origin in the top left corner.  We need to transform coords from
    // V to os/2 space to get the right results
    top = (DisplayHeight-1) - top;

    // CAUTION: WinSetWindowPos rc coords are inclusive/exclusive
    //          and use the bottom left corner
    int bottom = top - height;

    WinSetWindowPos (_wDialog, HWND_TOP,
      left, bottom,
      0,0,
      SWP_MOVE);
  }

//====================>>> vDialog::ShowDialog <<<=======================
  void vDialog::ShowDialog(VCONST char* msg)
  {
    //  Show the dialog with the default message
    //  We will control the position of the dialog box based on
    //  the parent's location. The user can move the dialog later.
    SysDebug1(Build,"vDialog::ShowDialog(%s) \n",msg)

    if (_IsDisplayed)
      return;                         // make this a no-op

    // First, get the values from our parent
    // set the message field -- have to move to the dlgproc init case

    if (msg && *msg)
    {
      for (DlgCmdList* cc = _cmdList ; cc != 0  ; cc = cc->nextDCL)
      {
	if (((cc->cmdP)->dlgCmd)->attrs & CA_MainMsg)
	{
	  ((cc->cmdP)->dlgCmd)->title = msg;     // repoint to the message
	}
      }
    }

    // Since the dialog isn't necessarily re-created each time it is
    // popped up, we need to loop through now to set the original values
    for (DlgCmdList* cc = _cmdList ; cc != 0  ; cc = cc->nextDCL)
    {
      (cc->cmdP)->SaveItemValue();
    }

    // Now ready to popup the dialog
    _IsDisplayed = 1;                   // The dialog is up

    if (_modal)
    {
      _wDialog = WinCreateDlg(HWND_DESKTOP, _parentHWND, (PFNWP) &DlgProcCB,
	_hTemplate, (PVOID) this);
      _myHWND = _wDialog;
      WinProcessDlg (_wDialog);
      WinDestroyWindow (_wDialog);

      _IsDisplayed = 0;
    }
    else
    {
      _wDialog = WinCreateDlg(HWND_DESKTOP, _parentHWND, (PFNWP) &DlgProcCB,
	_hTemplate, (PVOID) this);
      _myHWND = _wDialog;
      SysDebug1(OS2Dev,"    Modeless dialog HWND = %x \n", _wDialog)
    }
  }

// ---------------------------------------------------------------------
//====================>>> DlgProc <<<=======================
MRESULT EXPENTRY DlgProcCB(HWND hDlg, ULONG uMsg,
			       MPARAM mp1, MPARAM mp2)
{
  vDialog* useThis;
  if (uMsg == WM_INITDLG) // first time!
  {
    useThis = (vDialog*)mp2;

    _thisList.Add((ThisId)hDlg, (void*)useThis);
  }
  else
    useThis = (vDialog*)_thisList.GetThis((ThisId)hDlg);

  if (!useThis)
    return WinDefDlgProc(hDlg, uMsg, mp1, mp2);

  return (MRESULT) useThis->DynamicDlgProc(hDlg, uMsg, mp1, mp2);
}



//====================>>> vDialog::DynamicDlgProc <<<=======================
  int vDialog::DynamicDlgProc(HWND hDlg, UINT uMsg,
			    MPARAM mp1, MPARAM mp2)
  {
    int fProcessed = 0;
    // This code handles nested modals, a no no....   (V:1.13)
    if (_curModal != 0 && _curModal != this)             // V:1.13
    {
      return 0;
    }

    switch (uMsg)
    {
      case WM_INITDLG:
      {
	// Changes here must have equivalents in the vcmdpane.cpp code

	// although we can set these later in ShowDialog we need to set them now
	// during creation for those controls that need to set their
	// initial values during window creation
	_wDialog = hDlg;
	_myHWND = hDlg;

	WinSetWindowText(_wDialog, _name);

	for (DlgCmdList* cc = _cmdList ; cc != 0  ; cc = cc->nextDCL)
	{
	  vCmd* cp = cc->cmdP;

	  // BEW - V 1.17 - Shouldn't have to do this, but the dynamic
	  // dialog creation somehow replaces characters > 128 with _.
	  // This fixes that problem.
	  if ((cp->dlgCmd)->cmdType == C_BoxedLabel ||
	    (cp->dlgCmd)->cmdType == C_Button ||
	    (cp->dlgCmd)->cmdType == C_CheckBox ||
	    (cp->dlgCmd)->cmdType == C_Label ||
	    (cp->dlgCmd)->cmdType == C_RadioButton ||
	    (cp->dlgCmd)->cmdType == C_TextIn )
	  {
	    cp->SetCmdStr((cp->dlgCmd)->title);
	  }
	  else if ((cp->dlgCmd)->cmdType == C_Text)
          {
	    if (!(cp->dlgCmd)->itemList || (*(char *)(cp->dlgCmd)->itemList == 0))
	      cp->SetCmdStr((cp->dlgCmd)->title);
	    else
	      cp->SetCmdStr((char*)(cp->dlgCmd)->itemList);
	  }

	  if ((cp->dlgCmd)->cmdType == C_CheckBox && (cp->dlgCmd)->retVal)
	    cp->SetCmdVal(1,Checked);

	  if ((cp->dlgCmd)->cmdType == C_RadioButton && (cp->dlgCmd)->retVal)
	    cp->SetCmdVal(1,Checked);

	  if ((cp->dlgCmd)->cmdType == C_ToggleButton)
	    cp->DRAWITEM((cp->dlgCmd)->cmdId, (OWNERITEM*) _wDialog);

	  if ((cp->dlgCmd)->cmdType == C_ToggleIconButton)
	    cp->DRAWITEM((cp->dlgCmd)->cmdId, (OWNERITEM*) _wDialog);

	  if ((cp->dlgCmd)->cmdType == C_Icon)
	    cp->DRAWITEM((cp->dlgCmd)->cmdId, (OWNERITEM*) _wDialog);

	  if ((cp->dlgCmd)->cmdType == C_Slider || (cp->dlgCmd)->cmdType == C_Spinner)
	    cp->SetCmdVal(cp->GetCmdValue((cp->dlgCmd)->cmdId),Value);

	  if ((cp->dlgCmd)->cmdType == C_ProgressBar)
	    cp->SetCmdVal(cp->GetCmdValue((cp->dlgCmd)->cmdId),Value);

	  if ((cp->dlgCmd)->attrs & CA_MainMsg)
	  {
	    cp->SetCmdStr((cp->dlgCmd)->title);
	  }

	  if ((cp->dlgCmd)->attrs & CA_DefaultButton)
	  {
	    WinFocusChange(HWND_DESKTOP,
	      WinWindowFromID(_wDialog, (cp->dlgCmd)->cmdId),
	      0L);            // Set to default

	    fProcessed = 1;                  // return 0 if we call SetFocus
	  }

	  if ((cp->dlgCmd)->cmdType == C_TextIn)
	  {
	    WinFocusChange(HWND_DESKTOP,
	      WinWindowFromID(_wDialog, (cp->dlgCmd)->cmdId),
	      0L);            // Set to default

	    fProcessed = 1;           // return 0 if we call SetFocus
	  }

	  if (!(cp->dlgCmd)->Sensitive) // Make insensitive if it was
	  {
	    cp->SetCmdVal(0,Sensitive);
	  }

	  if ((cp->dlgCmd)->attrs & CA_Hidden) // Hide it!
	  {
	    cp->SetCmdVal(1,Hidden);
	  }

	  if ((cp->dlgCmd)->cmdType == C_List
	    || (cp->dlgCmd)->cmdType == C_SList
	    || (cp->dlgCmd)->cmdType == C_ComboBox
	    || (cp->dlgCmd)->cmdType == C_SComboBox
	    || (cp->dlgCmd)->cmdType == C_Spinner)
	  {
	    int curval = cp->GetCmdValue((cp->dlgCmd)->cmdId);
	    cp->SetCmdVal(curval,ChangeList);
	  }

	  if ((cp->dlgCmd)->cmdType == C_Icon || (cp->dlgCmd)->cmdType == C_Frame
	    || (cp->dlgCmd)->cmdType == C_ToggleFrame)
	  {
//	    cp->SetCmdVal(0,Sensitive); // Trick to avoid getting focus
	  }

// TOOLTIPS NOT IMPLEMENTED YET!!!!
	  if ((cp->dlgCmd)->tip && *(cp->dlgCmd)->tip)
	  {
	    /* add the control to the tooltip. TTF_SUBCLASS causes the
	    tooltip to automatically subclass the window and look for the
	    messages it is interested in. */
	    if ((cp->dlgCmd)->tip && *(cp->dlgCmd)->tip)
	    {
	    }
	  }
	}
	DialogDisplayed();

	return fProcessed;
      }

      case WM_CONTROL:
      {
	vCmd* UseThis = (vCmd *) getThisFromId((ItemVal)SHORT1FROMMP(mp1));
	if (!UseThis)
	{
	  fProcessed = 0;
	  break;
	}
	UseThis->CmdCallback(uMsg, mp1, mp2);
      }
      return fProcessed;

      case WM_COMMAND:
      {
	vCmd* UseThis = (vCmd *) getThisFromId((ItemVal) mp1);
	if (!UseThis)
	{
	  fProcessed = 0;
	  break;
	}
	UseThis->CmdCallback(uMsg, mp1, mp2);
      }
      return fProcessed;


      case WM_FOCUSCHANGE:
      {
	vCmd* UseThis = (vCmd *) getThisFromId((ItemVal) mp1);
	if (!UseThis)
	{
	  fProcessed = 0;
	  break;
	}
	UseThis->CmdCallback(uMsg, mp1, mp2);
      }
      return fProcessed;

/*
    case WM_HSCROLL:
    case WM_VSCROLL:
    {
      vCmd* UseThis = (vCmd *) getThisFromId(SHORT1FROMMP(mp1));

      int code = SHORT2FROMMP(mp2);
      if (!UseThis || code == SB_ENDSCROLL)
      {
	fProcessed = 0; break;
      }

      UseThis->CmdCallback(uMsg, mp1, mp2);
    }
    break;
*/
    default:
      fProcessed = FALSE;
      break;
    }

  return (int) WinDefDlgProc(hDlg, uMsg, mp1, mp2);
}
