//===============================================================
// vCommandPane - the command bar pane class used by the vWindow class
//
// 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/vcmdpane.h>
#include <v/vwindow.h>          // we are part of a window
#include <v/vthislst.h>         // For this list

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

  static vThisList _thisList;   // hide this in this file

//===================>>> vCommandPane::vCommandPane <<<====================
  vCommandPane::vCommandPane(CommandObject* cList) :
       vPane(P_Commands), vCmdParent(P_Commands)
  {

    SysDebug(Constructor,"vCommandPane::vCommandPane() constructor\n")
    _origList = cList;          // handle to list of commands
    _dialogType = aCmdBar;      // this is a command pane
    _isDisplayed = 1;           // Command bars visible by default
    _cmdObj = 0;                // no list
    _cmdObjCount = 0;
    _TextInActive = 0;
  }

//===================>>> vCommandPane::vCommandPane <<<====================
// Constructor for vStatus type status bars
// the list of elements are assumed to be just text labels
// implemented here as vBoxedLabels

  vCommandPane::vCommandPane(vStatus* s_bar) :
        vPane(P_Status), vCmdParent(P_Status)
  {
    _dialogType = aStatBar;     // this is really a command pane
    _isDisplayed = 1;           // Status bars visible by default
    char* cp;
    SysDebug(Constructor,"vStatusPane::vStatusPane() constructor\n")

    // get number of objects in pane
    for (_cmdObjCount = 1; s_bar[_cmdObjCount].label; ++_cmdObjCount);

    _cmdObj = new CommandObject[_cmdObjCount+1];
    for (int ix = 0; ix <= _cmdObjCount; ++ix)
    {
      if (!s_bar[ix].label)           // end of list
      {
        _cmdObj[ix].cmdType = C_EndOfList;
        _cmdObj[ix].title = 0;
        break;
      }
      _cmdObj[ix].cmdType = C_BoxedLabel;

      _cmdObj[ix].cmdId = s_bar[ix].statId;
      _cmdObj[ix].Sensitive = s_bar[ix].sensitive;
      _cmdObj[ix].retVal = 0;
      cp = new char[strlen(s_bar[ix].label)+1];       // copy string
      strcpy(cp, s_bar[ix].label);
      _cmdObj[ix].title = cp;
      _cmdObj[ix].itemList = 0;
      _cmdObj[ix].attrs = s_bar[ix].attrs;
      _cmdObj[ix].cFrame = 0;
      _cmdObj[ix].cRightOf = 0;
      _cmdObj[ix].cBelow = 0;
      _cmdObj[ix].size = s_bar[ix].width;
      _cmdObj[ix].tip = 0;
    }
    _origList = _cmdObj;                // handle to list of commands
  }

//===================>>> vCommandPane::~vCommandPane <<<====================
  vCommandPane::~vCommandPane()
  {
    SysDebug(Destructor,"vCommandPane::~vCommandPane() destructor\n")
//  theApp->unregisterCmdPane(this);
    _parentWin->unregisterCmdPane(this);  // vWindow is tracking pane

    if (_hTemplate !=0)
    {
      DosFreeMem(_hTemplate);
      _hTemplate = 0;
    }
    if (_cmdObj != 0)
    {
      for (int ix = 0 ; ix < _cmdObjCount ; ++ix)
      {
        if (_cmdObj[ix].title)
          delete [] _cmdObj[ix].title;    // free strings
      }
      delete [] _cmdObj;                              // free list
    }
    _thisList.Delete((ThisId)_barHwnd); // free the this ptr
  }

//==================>>> vCommandPane::ProcessCmd <<<=======================
  void vCommandPane::ProcessCmd(ItemVal id, ItemVal rv, CmdType ct)
  {
    // simple interface between command objects and the
    // inherited vCmdParent ProcessCmd to the CommandObject which does the work
    _parentWin->WindowCommand(id, rv, ct);
  }
//====================>>> vCommandPane::GetPaneValue <<<======================
  int vCommandPane::GetPaneValue(ItemVal id, int& val) VCONST
  {
    if (HasId(id))              // make sure the id is in the pane
      {
        val = GetValue(id);
        return 1;               // assume 0 if not found
      }
    else
        return 0;       // can't find this id
  }
//===================>>> vCommandPane::SetPaneValue <<<========================
  void vCommandPane::SetPaneValue(ItemVal id, int val, ItemSetType setType)
  {
    SetValue(id,val,setType);
  }

//====================>>> vCommandPane::SetPaneString <<<======================
  void vCommandPane::SetPaneString(ItemVal id, VCONST char* str)
  {
    SetString(id,str);
  }
//====================>>> vCommandPane::ShowPane <<<======================
  void vCommandPane::ShowPane(int OnOrOff) VCONST
  {
    if (OnOrOff != _isDisplayed)        // only need to do something if changed
     {
       _isDisplayed = OnOrOff;

       if (_isDisplayed)
       {
         WinShowWindow (_barHwnd, TRUE);
         _parentWin->_numPanes++;
       }
       else
       {
         WinShowWindow (_barHwnd, FALSE);
         _parentWin->_numPanes--;
       }
       _parentWin->AdjustSize(this);
     }
  }
//====================>>> vCommandPane::initialize <<<=======================
  void vCommandPane::initialize(vWindow* pWin, HWND hOwner)
  {
    // now, build the menu items in the widget provided
    DlgCmdList* curCmd;

    vPane::initialize(pWin, hOwner);       // initialize base class
    _hOwner = hOwner;
    // Now, build the initial in-memory dialog template using
    // OS/2 Dialog template handle _hTemplate.

    // Using 8,helv makes it ok to use 8 in calculating control sizes!
    PPElement PPel[] = { {PP_FONTNAMESIZE,(ULONG) "8.Helv"}, {0}};

//    int buildOK = CreateDlgTemplate(FCF_NOBYTEALIGN, WS_VISIBLE | WS_CLIPSIBLINGS,
    int buildOK = CreateDlgTemplate(FCF_NOBYTEALIGN, WS_CLIPSIBLINGS,
      0,0, 0,0, 0, "", "", "", AssyPresParams(PPel) );

    if (!buildOK)
      vSysError("vDialog - Unable to build dialog template");

    int ix;
    // Add each control to the template via AddCmd
    for (ix = 0 ; _origList && (_origList[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(&_origList[ix]);
    }
    DoneAddingControls();               // All finished adding controls
    // Now ready to create the dialog

    _barHwnd = WinCreateDlg(WinQueryWindow(hOwner, QW_PARENT), hOwner,
      (PFNWP) &CPProcCB, _hTemplate, (PVOID) this);

    SysDebug2(OS2Dev,"vCommandPane::initialize hOwner:%u _barHwnd:%u \n", hOwner, _barHwnd)

    if( _barHwnd)
    {
      _wDialog = _barHwnd;
      pWin->registerCmdPane(this);  // if created ok, then register with vWindow
      pWin->AdjustSize(this);   // resize the frame to accomodate pane
    }

    DosFreeMem(_hTemplate);
    _hTemplate = 0;

  }
// ---------------------------------------------------------------------
//====================>>> DlgProc <<<=======================
  MRESULT EXPENTRY CPProcCB(HWND hDlg, ULONG uMsg,
                              MPARAM mp1, MPARAM mp2)
  {
    vCommandPane* useThis;
    if (uMsg == WM_INITDLG)  // first time!
    {
      useThis = (vCommandPane*)mp2;
      _thisList.Add((ThisId)hDlg, (void*)useThis);
    }
    else
      useThis = (vCommandPane*)_thisList.GetThis((ThisId)hDlg);

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

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

//====================>>> vCommandPane::OnInitDialog <<<==================
  int vCommandPane::OnInitDialog(HWND hwnd, MPARAM mp1, MPARAM mp2)
  {
    // Changes here must have equivlalents in the vDialog.cpp code
    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_BoxedLabel)
        cp->DRAWITEM((cp->dlgCmd)->cmdId, (OWNERITEM*) _barHwnd);

      if ((cp->dlgCmd)->cmdType == C_Text)
        cp->DRAWITEM((cp->dlgCmd)->cmdId, (OWNERITEM*) _barHwnd);

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

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

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

      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)->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_ComboBox
        || (cp->dlgCmd)->cmdType == C_SComboBox
        || (cp->dlgCmd)->cmdType == C_Spinner
        || (cp->dlgCmd)->cmdType == C_SList )
      {
        int curval = cp->GetCmdValue((cp->dlgCmd)->cmdId);
        cp->SetCmdVal(curval,ChangeList);
      }

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

      // Now add ToolTip if it has a message
      if ((cp->dlgCmd)->tip && *(cp->dlgCmd)->tip)
      {
/*  ToolTips code not implemented in OS/2 yet.  More effort needed!!!
	TOOLINFO ti;
	char *cc = (char*) &ti;
	int lim = sizeof (ti);
	for (int i = 0 ; i++ < lim ; *cc++ = 0)
	  ;
	ti.cbSize = sizeof(ti);
	ti.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
	ti.hwnd = hwnd;
	ti.uId = (UINT)GetDlgItem(hwnd,(cp->dlgCmd)->cmdId);
	ti.lpszText = TEXT((cp->dlgCmd)->tip);
	SendMessage(hwndToolTip, TTM_ADDTOOL, 0, (LPARAM)&ti);
*/
      }
    }

//    _isShown = 1;                               // We will be shown now
    RECTL rc;                                   // to get our size
    WinQueryWindowRect (_barHwnd, &rc);         // the rect of the command bar

    _windowY = 0;                               // put pane at bottom for now
    _windowH =  rc.yTop - rc.yBottom;           // compute height

//    WinEnableWindow(_barHwnd, TRUE);

    return 0;
  }
//====================>>> vCommandPane::OnEnable <<<==================
  void vCommandPane::OnEnable(HWND hwnd, BOOL fEnable)
  {
    WinEnableWindow (hwnd, fEnable);
/*
    // Make all child windows have the same status as the dialog box.
    for (HWND hwndCtl = (HWND) WinQueryWindow (hwnd, QW_TOP); hwndCtl != NULLHANDLE ;
      hwndCtl = (HWND) WinQueryWindow (hwnd, QW_NEXT))
    {
      WinEnableWindow (hwndCtl, fEnable);
    }
*/
  }

//====================>>> vCommandPane::OnPaint <<<==================
  int vCommandPane::OnPaint(HWND hwnd)
  {
    // Paint dividing lines around command/status bar
    RECTL rc, rcInner, rcGrip;
    POINTL  pnt = {0,0};
    LINEBUNDLE ShadowPen, LightPen;
    int borderH, borderV;
    int gripPitch, frameV, frameH;

    borderH = WinQuerySysValue (HWND_DESKTOP, SV_CYBORDER);
    borderV = WinQuerySysValue (HWND_DESKTOP, SV_CXBORDER);
    frameV = 0;  // space from edge of bar to frame boundary
    frameH = 1;
    gripPitch = 2;   // pitch of grip ribs

    ShadowPen.lColor = SYSCLR_BUTTONDARK;
    ShadowPen.usMixMode = FM_OVERPAINT;
    ShadowPen.usType = LINETYPE_SOLID;

    LightPen.lColor = SYSCLR_BUTTONLIGHT;
    LightPen.usMixMode = FM_OVERPAINT;
    LightPen.usType = LINETYPE_SOLID;

    // begin painting here
    HPS hdc = WinBeginPaint (_barHwnd, NULLHANDLE, &rc);
    WinQueryWindowRect(_barHwnd, &rc);
    WinFillRect(hdc, &rc, SYSCLR_BUTTONMIDDLE);

    if (paneType() == P_Status)         // Paint a status bar
    {

      rcInner.xLeft = rc.xLeft + frameH;
      rcInner.xRight = rc.xRight - frameH +1;
      rcInner.yBottom = rc.yBottom + frameV;
      // to make it look centered, we need to add the border thickness to the top
      rcInner.yTop = rc.yTop - frameV - 2*borderV;
      WinDrawBorder(hdc,
               &rcInner,
               borderV,
               borderH,
               SYSCLR_BUTTONMIDDLE,
               SYSCLR_BUTTONMIDDLE,
               DB_DEPRESSED);

      // Paint diagonal grip ribs on status pane
      // draw grip area rectangle
      rcGrip.xLeft = rcInner.xRight - 6*gripPitch;
      rcGrip.yBottom = rcInner.yBottom;
      rcGrip.xRight = rcInner.xRight;
      rcGrip.yTop = rcInner.yBottom + 6*gripPitch;
      WinFillRect(hdc, &rcGrip, SYSCLR_BUTTONMIDDLE);
//      WinFillRect(hdc, &rcGrip, CLR_GREEN);

      rcInner.xRight -= 1;  // the drawing commands are inclusive
      // draw bottom hash
      pnt.x = rcInner.xRight -gripPitch;
      pnt.y = rcInner.yBottom;
      GpiSetAttrs(hdc, PRIM_LINE, LBB_COLOR | LBB_TYPE, 0, &ShadowPen);
      GpiSetCurrentPosition (hdc, &pnt);
      pnt.x = rcInner.xRight;
      pnt.y = rcInner.yBottom +gripPitch;
      GpiLine (hdc, &pnt);

      GpiSetAttrs(hdc, PRIM_LINE, LBB_COLOR | LBB_TYPE, 0, &LightPen);
      pnt.x = rcInner.xRight -2*gripPitch;
      pnt.y = rcInner.yBottom;
      GpiSetCurrentPosition (hdc, &pnt);
      pnt.x = rcInner.xRight;
      pnt.y = rcInner.yBottom +2*gripPitch;
      GpiLine (hdc, &pnt);

      // draw middle hash
      pnt.x = rcInner.xRight -3*gripPitch;
      pnt.y = rcInner.yBottom;
      GpiSetAttrs(hdc, PRIM_LINE, LBB_COLOR | LBB_TYPE, 0, &ShadowPen);
      GpiSetCurrentPosition (hdc, &pnt);
      pnt.x = rcInner.xRight;
      pnt.y = rcInner.yBottom +3*gripPitch;
      GpiLine (hdc, &pnt);

      GpiSetAttrs(hdc, PRIM_LINE, LBB_COLOR | LBB_TYPE, 0, &LightPen);
      pnt.x = rcInner.xRight -4*gripPitch;
      pnt.y = rcInner.yBottom;
      GpiSetCurrentPosition (hdc, &pnt);
      pnt.x = rcInner.xRight;
      pnt.y = rcInner.yBottom +4*gripPitch;
      GpiLine (hdc, &pnt);

      // draw top hash
      pnt.x = rcInner.xRight -5*gripPitch;
      pnt.y = rcInner.yBottom;
      GpiSetAttrs(hdc, PRIM_LINE, LBB_COLOR | LBB_TYPE, 0, &ShadowPen);
      GpiSetCurrentPosition (hdc, &pnt);
      pnt.x = rcInner.xRight;
      pnt.y = rcInner.yBottom +5*gripPitch;
      GpiLine (hdc, &pnt);

      GpiSetAttrs(hdc, PRIM_LINE, LBB_COLOR | LBB_TYPE, 0, &LightPen);
      pnt.x = rcInner.xRight -6*gripPitch;
      pnt.y = rcInner.yBottom;
      GpiSetCurrentPosition (hdc, &pnt);
      pnt.x = rcInner.xRight;
      pnt.y = rcInner.yBottom +6*gripPitch;
      GpiLine (hdc, &pnt);
    }
    else                                // Paint a command bar
    {
      GpiSetCurrentPosition (hdc, &pnt);

      GpiSetAttrs(hdc, PRIM_LINE, LBB_COLOR | LBB_TYPE, 0, &ShadowPen);

      pnt.x = rc.xRight;
      pnt.y = 0;
      GpiLine (hdc, &pnt);

      GpiSetAttrs(hdc, PRIM_LINE, LBB_COLOR | LBB_TYPE, 0, &LightPen);

      pnt.x = 0;
      pnt.y = _windowH - borderH;
      GpiSetCurrentPosition (hdc, &pnt);

      pnt.x = rc.xRight;
      pnt.y = _windowH - borderH;
      GpiLine (hdc, &pnt);

    }
    WinEndPaint (hdc);

    return 0;
  }

//====================>>> vCommandPane::OnCommand <<<==================
  void vCommandPane::OnCommand (vCmd *UseThis, int uMsg, MPARAM mp1,
    MPARAM mp2)
  {
    // A very dangerous thing can happen here if the button causes
    // vApp::Exit to be called. This can cause things to be destructed,
    // which means that the command object goes away, which means
    // that UseThis no longer points to something valid, which is of
    // course a problem. Exit can get called in CmdCallback, so we
    // will remember the command type first for the test after the call.

    CmdType ct = (UseThis->dlgCmd)->cmdType;    // will be ok still
    UseThis->CmdCallback(uMsg, mp1, mp2);

    // We need a SetFocus to get the focus back to the
    // canvas so that cursor will work, but for
    // lists and combos, SetFocus breaks them.  Also, we don't
    // want timer updates to text controls to cause focus grabbing
    // so we have to check for them too.
    if ((ct == C_ComboBox || ct == C_SComboBox) && SHORT2FROMMP(mp1) != CBN_ENTER)
      return;

    if ((ct == C_List || ct == C_SList)
      && SHORT2FROMMP(mp1) != LN_ENTER && SHORT2FROMMP(mp1) != LN_SELECT)
      return;

    if (ct == C_BoxedLabel || ct == C_ColorLabel || ct == C_Label || ct == C_ProgressBar
      || ct == C_Text || ct == C_Icon)
    {
      return;
    }

    // we need to flag when the user is inputing data into a text in box
    // so that we can know not to mess with the focus when the mouse
    // buttons are being clicked
    if (ct == C_TextIn)
    {
      if (SHORT2FROMMP(mp1) == EN_SETFOCUS)
        _TextInActive = 1;
      else if(SHORT2FROMMP(mp1) == EN_KILLFOCUS)
        _TextInActive = 0;
      return;
    }

    // The idea here is that we change the focus back to the client after
    // any WM_CONTROL/WM_COMMAND message occurs, but we only do this if the
    // app is currently active.  If it is not, then we do nothing which prevents
    // automatic timer updates of things like text boxes from yanking away focus
    // from another app that is curently active.
    // note: parent of cmdpane should be our frame
    if ( WinQueryActiveWindow(HWND_DESKTOP) == WinQueryWindow(_barHwnd, QW_PARENT) )
    {
      WinFocusChange(HWND_DESKTOP, WinQueryWindow(_barHwnd, QW_OWNER),
        FC_NOSETACTIVE | FC_NOSETSELECTION);
    }
  }

//====================>>> vCommandPane::DynamicDlgProc <<<==================
  int vCommandPane::DynamicDlgProc(HWND hDlg, UINT uMsg,MPARAM mp1, MPARAM mp2)
  {
    switch (uMsg)
    {
      case WM_INITDLG:
        _barHwnd = hDlg;
        _wDialog = hDlg;  // used by vCmdParent
        return OnInitDialog(hDlg, mp1, mp2);

/*
      case WM_ENABLE:
        OnEnable(hDlg, (BOOL) SHORT1FROMMP(mp1));
        break;
*/
      case WM_PAINT:
        return OnPaint(hDlg);

      case WM_CONTROL:
      case WM_COMMAND:
      {
        vCmd* UseThis = (vCmd *) getThisFromId((ItemVal)SHORT1FROMMP(mp1));
        if (!UseThis)
        {
          return 0;
        }
        OnCommand(UseThis, uMsg, mp1, mp2);
        return 0;
      }

      // we need to monitor if the cmdpane gets focus and then send the
      // focus right back to the client
      case WM_BUTTON1UP:
      case WM_BUTTON2UP:
      case WM_BUTTON3UP:
      case WM_CONTEXTMENU:
      case WM_BEGINSELECT:
      case WM_SINGLESELECT:
      {
        // if the user is inputing text into a textin box, then we
        // don't want to mess with the focus when user clicks the mouse
        // in the command bar (yes, it's a kludge!!!)
        if (_TextInActive == 1)
          return 0;

        WinFocusChange(HWND_DESKTOP, WinQueryWindow(_barHwnd ,QW_OWNER),
          FC_NOSETACTIVE | FC_NOSETSELECTION);
        break;
      }

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

