/* General-purpose Menu  class. An instance of this class
can be placed in a Window's menu ivar. Facilitates the
dynamic creation of menus. Hierarchical menus can be created
through use of the addMenuItems method. */!!

inherit(Object, #Menu,
#(handle      /* the Windows menu handle */
actionDict  /* contains message syms */), 2, nil)!!

now(class(Menu))!!

now(Menu)!!

/* Allow menu item to be selected. */
Def enableMenuItemByPosition(self, position)
{ ^Call EnableMenuItem(handle, position, MF_ENABLED bitOr MF_BYPOSITION);
}!!

/* Disable (but not gray) the specified menu item. */
Def disableMenuItemByPosition(self, position)
{ ^Call EnableMenuItem(handle, position, MF_DISABLED bitOr MF_BYPOSITION);
}!!

/* Disable and gray this menu item by position. */
Def grayMenuItemByPosition(self, position)
{ ^Call EnableMenuItem(handle, position, MF_GRAYED bitOr MF_BYPOSITION);
}!!

/* Return an action value for this menu item ID. */
Def action(self, ID)
{ ^at(actionDict, ID);
}!!

/* Add the memu Item to the building menu. */
Def addItem(self, item)
{ ^appendItem(self, item, handle);
}!!

/* Append this MenuItem object to the forming menu
 whose handle is supplied. If the item is a popup
 type, create a popup menu for the item and add the
 sub-items. */
Def appendItem(self, menuItem, hM | pC, hPopup)
{ if menuItem
  then
    if pC := popupColl(menuItem)
    then hPopup := Call CreateMenu();
      do(pC,
      {using(mi) appendItem(self, mi, hPopup);
      });
      Call AppendMenu(hM, MF_POPUP, hPopup, asciiz(text(menuItem)));
    else addToMenu(menuItem, hM);
      setAction(self, id(menuItem), actionSym(menuItem));
    endif;
  else Call AppendMenu(hM, MF_SEPARATOR, 0, 0);
  endif;
}!!

/* Check the specified menu item. */
Def checkMenuItem(self, item)
{ ^Call CheckMenuItem(handle, item, MF_CHECKED);
}!!

/* Create a new menu in Windows for the given window,
 and set the handle instance Variable. */
Def create(self, window)
{ init(self);
  createMenu(window);
  handle := menuHandle(window);
}!!

/* Disable (but not gray) the specified menu item. */
Def disableMenuItem(self, item)
{ ^Call EnableMenuItem(handle, item, MF_DISABLED);
}!!

/* Allow menu item to be selected. */
Def enableMenuItem(self, item)
{ ^Call EnableMenuItem(handle, item, MF_ENABLED);
}!!

/* Return a string for this menu item. */
Def getString(self, id | str)
{ Call GetMenuString(handle, id, str := new(String, 40), 39, MF_BYCOMMAND);
  ^removeNulls(str);
}!!

/* Disable and gray this menu item. */
Def grayMenuItem(self, item)
{ ^Call EnableMenuItem(handle, item, MF_GRAYED);
}!!

/* Return the menu handle. */
Def handle(self)
{ ^handle;
}!!

/* Initialize self by creating a new action
 dictionary.  */
Def init(self)
{ actionDict := new(Dictionary, 4);
}!!

/* Load the menu resource if possible and obtain a handle
  to a menu to place in handle (if menuName is not nil). */
Def loadMenu(self, menuName)
{ if menuName
    cand (handle := Call LoadMenu(HInstance, asciiz(menuName))) = 0
  then handle := nil;
    alert(System, menuName, #menuError)
  endif;
}!!

/* Create a popup menu in self. This method does not
  support hierarchical menus.
  name  is the name of the popup menu.
  items is an array of strings for the popup menu.
  ids   is an array of integer values for menu IDs.
  msg   is an array of action symbols for each
          menu item, or an action symbol for the
          entire popup.
  e.g.,
    popupMenu(self, "top".
        #("sub1", "sub2"),
        #(101, 102),
        #(#act1, #act2)); */
Def popupMenu(self, name, items, ids, msgs | popup aSym)
{ if class(msgs) == Symbol
  then aSym := msgs;
  endif;
  if size(items) <> size(ids) /* check if the size is ok */
     or (not(aSym) and (size(items) <> size(msgs)))
   alert(System, self, #wrongSizeArguments);
  endif;
  popup := Call CreateMenu();
  do (size(ids),
  {using(i)
    if items[i]
    then Call AppendMenu(popup, MF_STRING, ids[i], asciiz(items[i]));
      setAction(self, ids[i], aSym cor msgs[i]);
    else Call AppendMenu(popup, MF_SEPARATOR, 0, 0);
    endif;
  });
  Call AppendMenu(handle, MF_POPUP, popup, asciiz(name));
}!!

/* Set an action value for this menu item ID. */
Def setAction(self, ID, msg)
{ ^add(actionDict, ID, msg);
}!!

/* Set the menu handle. */
Def setHandle(self, hnd)
{ ^handle := hnd;
}!!

/* Set window's menu to be self in Windows.
  Return the handle to the menu if successful.
  Otherwise return nil. */
Def setMenu(self, window)
{ if Call SetMenu(handle(window), handle) = 0
  then ^nil;
  endif;
  ^handle;
}!!

/* Create a top level menu item.
  string is the text for the menu.
  id     is an integer value for the menu ID.
  msg    is the "action" for this item.
  e.g.
    topMenu(self, "Help!", 100, #help);
*/
Def topMenu(self, string, id, msg)
{ Call AppendMenu(handle, MF_STRING, id, asciiz(string));
  setAction(self, id, msg);
}!!

/* Uncheck the specified menu item. */
Def unCheckMenuItem(self, item)
{ ^Call CheckMenuItem(handle, item, MF_UNCHECKED);
}!!
