//===============================================================
// vPopMenu.cpp - vPopupMenu class functions - OS/2
//
// 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/vpopmenu.h>         // our header
#include <v/vcmdwin.h>          // we need access to vcmdwin

#include <stdlib.h>

// Define static data of the class

  static char curLbl[100];     // for fixed labels
  static vThisList _thisList;  // save menu window handles in this file


//==================>>> vPopupMenu::vPopupMenu <<<===========================
  vPopupMenu::vPopupMenu(VCONST vMenu* menu, VCONST vWindow* pWin)
  {
    int i;
    char *ch;

    // set up menus -----------------------------------------------
    SysDebug(Constructor,"vPopupMenu::vPopupMenu() constructor\n")

    _pWin = pWin;
    _nextMenuButton = 0;        // no menus defined so far

    for (i = 0 ; i < MAX_MENU_BUTTONS ; ++i)
    {
      _mb[i].label[0] = 0;         // null out menu button array
      _mb[i].menuId = 0;
      _mb[i].hPullDown = 0;
      _mb[i].SubMenu = 0;
      _mb[i].mInfo = 0;
    }
    // we treat a popup as starting from what would be
    // a pulldown on the regular menu bar. This seems a bit
    // convoluted to me... why waste the space?
    strcpy(_mb[0].label, "");
    _mb[0].SubMenu = menu;      // this is the pointer to the static defn
    _nextSubMenu = 1;           // we will start submenus here
    _nextMenuButton = 1;        // this many menus defined
    _topLevelMenu = 0;          // window handle of main popup menu

    _origin = menu[0].menuId;   // index of top menu item
  }

//==================>>> vPopupMenu::vPopupMenu <<<===========================
  vPopupMenu::vPopupMenu(const vPopupMenu& m)
  {

    vSysError("V Semantics do not allow copy constructors.");
  }

//====================>>> vPopupMenu::~vPopupMenu <<<=======================
  vPopupMenu::~vPopupMenu()                     // destructor
  {
    SysDebug(Destructor,"vPopupMenu::~vPopupMenu() destructor\n")
    for (int i = _nextSubMenu - 1 ; i >= 0 ; --i)        // all menus
    {
      // we don't want to destroy the popup menu until the user
      // has finished using it.  Unfortunately, V popmenu code when
      // placed in the message handler loop tends to call the
      // popmenu destructor immediately after displaying the
      // popup menu, and the menu disappears before it can be seen!
      // On the other hand, if we don't call WinDestroyWindow during the
      // destructor then the window stays around forever, which is also
      // not good.  To get around this, we store menu windows in _thisList
      // indexed by vmenu pointers.  Before we create a new menu, we check
      // the list to see if it already exists.  If it does, we just reuse
      // it without creating e new one. In this way we avoid creating
      // the same menus over and over and the need to destroy windows goes
      // away until the app terminates.

//      WinDestroyWindow (_mb[i].hPullDown);
      if (_mb[i].mInfo != 0)
        delete _mb[i].mInfo;                // free the space
    }
  }

//======================>>> vPopupMenu::initialize <<<=======================
  void vPopupMenu::initialize()
  {
    if (_topLevelMenu != 0)             // already initialized?
      return;

    // check to see if menu window already created earlier
    _topLevelMenu = (HWND)_thisList.GetThis((ThisId) _mb[0].SubMenu);

    if (_topLevelMenu != 0)
    {
      // now check to make sure its still good since sometimes menu
      // windows can get destroyed by the system when frame windows
      // are closed
      if ( WinIsWindow(theApp->AppHab(), _topLevelMenu) )
      {
        return;
      }
      else
      {
        // window has been destroyed... delete the bad handle reference
        // and recreate
        _thisList.Delete((ThisId) _mb[0].SubMenu);
      }
    }

    // Create a popup menu
    _topLevelMenu = WinCreateWindow (HWND_OBJECT, WC_MENU, NULL,
      WS_CLIPSIBLINGS | WS_SAVEBITS, 0, 0, 0, 0, HWND_OBJECT,
      HWND_BOTTOM, FID_MENU, NULL, NULL);

    // save the new menu in our list
    _thisList.Add((ThisId) _mb[0].SubMenu, (void*) _topLevelMenu);

    // Now we have to add our menus
    doAddMenu(0, _topLevelMenu);
   }

//======================>>> vPopupMenu::fixLabel <<<========================
  void vPopupMenu::fixLabel(VCONST char* lbl, VCONST char* key) VCONST
  {
    // copy label to global curLbl and append any hot key mnemonics
    VCONST char* cp;
    int ix = 0;
    for (cp = lbl ; *cp && ix < 99 ; ++cp)      // Scan label
    {
      curLbl[ix++] = *cp;
    }

    if (key && *key)
    {
      curLbl[ix++] = ' '; curLbl[ix++] = ' '; curLbl[ix++] = '\t';
      for (cp = key ; *cp && ix < 99 ; ++cp)
      {
         curLbl[ix++] = *cp;
      }
    }
    curLbl[ix] = 0;             // finish off

    // translate V '&' for OS/2 '~' in labels for menu accelerators
    char *ch;
    for (ch  = curLbl; *ch != 0; ch++)
    {
      if (*ch == '&')
      *ch = '~';
    }
  }

//======================>>> vPopupMenu::doAddMenu <<<========================
  void vPopupMenu::doAddMenu(int id, HWND parent)
  {
    MENUITEM mi;

    // point to popup window on which buttons will be attached
    _mb[id].hPullDown = parent;

    // loop through the list
    _mb[id].mInfo = 0;                  // empty list so far
    vMenu* item = _mb[id].SubMenu;      // The first item in list
    PMenuInfo* info;                    // for current info
    for (int ix = 0 ; item[ix].label != 0 ; ++ix)
    {
      info = new PMenuInfo;           // new space for current item
      info->NxtInfo = _mb[id].mInfo;  // add to front of list
      _mb[id].mInfo = info;           // fix front pointer
      info->ItemIndex = ix;           // index to item list
      info->SubMenuIndex = 0;         // no submenu normally

      if (item[ix].menuId == M_Line)
      {
        mi.iPosition = MIT_END;
        mi.afStyle = MIS_SEPARATOR;
        mi.afAttribute = 0;
        mi.id = 0;
        mi.hwndSubMenu = NULLHANDLE;
        mi.hItem = 0;
        WinSendMsg (_mb[id].hPullDown, MM_INSERTITEM, (MPARAM) &mi, NULL);
      }
      else if (item[ix].SubMenu != 0) // a submenu
      {
        if(_nextSubMenu >= MAX_MENU_BUTTONS)
        {
          SysDebug(BadVals,"Too many submenus!\n");
          continue;
        }
        // we will create a submenu
        // copy the definitions, track the new index
        int sub = _nextSubMenu++;   // get our index, bump for next time

        strcpy(_mb[sub].label,  item[ix].label);
        // translate V '&' for OS/2 '~' in labels for menu accelerators
        char *ch;
        for (ch  = _mb[sub].label; *ch != 0; ch++)
        {
          if (*ch == '&')
            *ch = '~';
        }
        _mb[sub].menuId = item[ix].menuId;
        _mb[sub].SubMenu = item[ix].SubMenu;
        info->SubMenuIndex = sub;           // no submenu normally

        // create a sub-popup-menu from the main popup-menu
        HWND subParent = WinCreateWindow (HWND_OBJECT, WC_MENU, NULL,
                            WS_CLIPSIBLINGS, 0, 0, 0, 0, HWND_OBJECT,
                            HWND_BOTTOM, _mb[sub].menuId, NULL, NULL);

        // insert submenu into the current menu
        mi.iPosition = MIT_END;
        mi.afStyle = MIS_SUBMENU;
        mi.afAttribute = 0;
        mi.id = _mb[sub].menuId;
        mi.hwndSubMenu = subParent;
        mi.hItem = 0;
        WinSendMsg(parent, MM_INSERTITEM, (MPARAM) &mi, (MPARAM) _mb[sub].label);

        // Now, recursively call doAddMenu to populate the submenu
        doAddMenu(sub, subParent);
      }
      else
      {
        // create the item
        fixLabel(item[ix].label,item[ix].keyLabel);
        mi.iPosition = MIT_END;
        mi.afStyle = MIS_TEXT;
        mi.afAttribute = 0;
        mi.id = item[ix].menuId;
        mi.hwndSubMenu = NULLHANDLE;
        mi.hItem = 0;
        WinSendMsg(_mb[id].hPullDown, MM_INSERTITEM,
                  (MPARAM) &mi, (MPARAM) curLbl);

        WinCheckMenuItem(_mb[id].hPullDown,
                         item[ix].menuId,
                         (item[ix].checked ? TRUE : FALSE));

        WinEnableMenuItem (_mb[id].hPullDown,
                           item[ix].menuId,
                           item[ix].sensitive ? TRUE : FALSE);
      }
    }
  }

//====================>>> vPopupMenu::ShowMenu <<<======================
  void vPopupMenu::ShowMenu(int x, int y)
  {
    // get frame handle
    HWND hFrame = _pWin->winHwnd();
    // get client handle
    HWND hClient = WinWindowFromID(hFrame, FID_CLIENT);

    // initialize creates the menu windows needed.  It is smart
    // enough to recycle menu windows created earlier from the
    // same vmenu definitions.
    initialize();

    POINTL pt;
    pt.x = x; pt.y = y;

    // this pointer nightmare attempts to map the V coords to the OS/2 coords
    ((_pWin->getCanvasPane() )->GetDC() )->MapToOS2(&pt);

    // V anchors menu to top left corner (OS/2 by default maps to bottom left
    // corner), so we need to do a bit of fixing up here

    // force window to update its size by calling WinSetWindowPos
    // If we don't do this the returned size will not include any
    // menu items inserted with MM_INSERTITEM until after the menu
    // is displayed for the first time.
    WinSetWindowPos(_topLevelMenu, 0L, 0L, 0L, 1L, 1L, SWP_SIZE);
    // now query the updated size and adjust
    RECTL rcl;
    WinQueryWindowRect(_topLevelMenu, &rcl);
    pt.y -= (rcl.yTop - rcl.yBottom);
    WinPopupMenu(hClient,
                 hFrame,
                 _topLevelMenu,
                 pt.x, pt.y,
                 0L,
                 PU_VCONSTRAIN | PU_HCONSTRAIN |
                 PU_MOUSEBUTTON2DOWN | PU_MOUSEBUTTON1 | PU_MOUSEBUTTON2);
  }

//====================>>> vPopupMenu::GetValue <<<======================
  int vPopupMenu::GetValue(ItemVal id, int& val) VCONST
  {
    // scan all menus in this window to retrieve the what value
    // then scan button bar if not found

    vMenu* item;

    // Search all menus in this list
    for (int ix = 0 ; ix < _nextSubMenu ; ++ix)
    {
      PMenuInfo* info;                        // for current info
      // scan the list of info for each menu entry

      for (info = _mb[ix].mInfo ; info != 0 ; info = info->NxtInfo)
      {
        item = _mb[ix].SubMenu;     // The current item in list
        // see if menuId is the same as the one we are setting
        if (item[info->ItemIndex].menuId == id)
        {
          // Ah Ha! We found the value we want
          return item[info->ItemIndex].checked;
        }
      }
    }

    return 0;           // assume 0 if not found
  }

//==================>>> vPopupMenu::SetValue <<<========================
  void vPopupMenu::SetValue(ItemVal id, int val, ItemSetType setType)
  {
    // Set the given item on or off
    vMenu* item;
    // Search all menus in this list
    for (int ix = 0 ; ix < _nextSubMenu ; ++ix)
    {
      PMenuInfo* info;                        // for current info
      // scan the list of info for each menu entry
      for (info = _mb[ix].mInfo ; info != 0 ; info = info->NxtInfo)
      {
        item = _mb[ix].SubMenu;     // The current item in list
        // see if its menuId is the same as the one we are setting
        if (item[info->ItemIndex].menuId == id)
        {
          // Ah Ha! We found the value we want
          switch (setType)
          {
            case Value:                 // check box
            case Checked:               // check box
            {
              item[info->ItemIndex].checked = val;
              WinCheckMenuItem (_mb[ix].hPullDown,
	        item[info->ItemIndex].menuId,
		val ? TRUE : FALSE);
              break;
            }

            case Sensitive:                     // sensitive
            {
              item[info->ItemIndex].sensitive = val;

              WinEnableMenuItem (_mb[ix].hPullDown,
	        item[info->ItemIndex].menuId,
		val ? TRUE : FALSE);
              break;
            }
          }     // end switch
        }
      }
    }
  }

//================>>> vPopupMenu::SetString <<<========================
  void vPopupMenu::SetString(ItemVal id, VCONST char* str)
  {
    HWND parent = _pWin->winHwnd();

    // Set the given item on or off
    vMenu* item;

    // Search all menus in this window
    for (int ix = 0 ; ix < _nextSubMenu ; ++ix)
    {
      PMenuInfo* info;                        // for current info

      // scan the list of info for each menu entry
      for (info = _mb[ix].mInfo ; info != 0 ; info = info->NxtInfo)
      {
        item = _mb[ix].SubMenu;     // The current item in list

        if (item[info->ItemIndex].menuId == id)
        {
          // Ah Ha! We found the value we want
          item[info->ItemIndex].label = str;
	  WinSetMenuItemText (_mb[ix].hPullDown,	     // the menu
	  		      item[info->ItemIndex].menuId,  // same id
	  		      item[info->ItemIndex].label);  // new label

//	  WinSendMsg (theApp->_Frame, WM_UPDATEFRAME, MPFROMLONG(FCF_MENU), MPVOID);
	  WinSendMsg (parent, WM_UPDATEFRAME, MPFROMLONG(FCF_MENU), MPVOID);
        }
      }
    }
  }

