/*
 *	$Source: /u1/Xr/src/Xrlib/Dialog/RCS/MenuMgr.c,v $
 *	$Header: MenuMgr.c,v 1.1 86/12/17 08:58:20 swick Exp $
 */

#ifndef lint
static char *rcsid_MenuMgr_c = "$Header: MenuMgr.c,v 1.1 86/12/17 08:58:20 swick Exp $";
#endif	lint

#include <Xr/xr-copyright.h>

/* $Header: MenuMgr.c,v 1.1 86/12/17 08:58:20 swick Exp $ */
/* Copyright 1986, Hewlett-Packard Company */
/* Copyright 1986, Massachussetts Institute of Technology */
static char rcsid[] = "$Header: MenuMgr.c,v 1.1 86/12/17 08:58:20 swick Exp $";

/*************************************<+>*************************************
 *****************************************************************************
 **
 **   File:        MenuMgr.c
 **
 **   Project:     X-ray Toolbox
 **
 **   Description: X-ray Toolbox menu manager.
 **
 **
 **
 **   ------------------------ MODIFICATION RECORD   ------------------------
 *
 * $Log:	MenuMgr.c,v $
 * Revision 1.1  86/12/17  08:58:20  swick
 * Initial revision
 * 
 * Revision 7.0  86/11/13  08:26:50  08:26:50  ed ()
 * Final QA release
 * 
 * Revision 6.1  86/11/11  19:50:33  19:50:33  ed ()
 * Fixed: strcmp not sent NULL any longer (SUN fix).
 * 
 * Revision 6.0  86/11/10  15:35:50  15:35:50  ed ()
 * QA #2 release
 * 
 * Revision 5.5  86/11/10  15:07:04  15:07:04  ed ()
 * removed ref to string.h
 * 
 * Revision 5.4  86/11/07  14:08:17  14:08:17  ed ()
 * Fixed:  Borderwidth menu posting bug.
 * Added procedure headers, new copyright message.
 * 
 * Revision 5.2  86/11/06  11:29:04  11:29:04  ed ()
 * Fixed:  Submenu remains posted even if cursor is in another menu.
 *         Activate Menu now deactivates current menu (if any).
 *         Cannot activate and deactivate non text items.
 *         Menu language parser default case no works.
 * 
 * Revision 5.1  86/10/30  09:53:35  09:53:35  ed ()
 * Freeze server before function call, check ADDWINDOWFUNCT return value.
 * 
 * Revision 5.0  86/10/28  08:20:19  08:20:19  ed ()
 * QA #1.1 release
 * 
 * Revision 4.2  86/10/27  13:45:23  13:45:23  ed ()
 * Changed to use new XrInputMap().
 * 
 * Revision 4.1  86/10/26  16:28:34  16:28:34  ed ()
 * Now uses cursor from menu context.
 * 
 * Revision 4.0  86/10/20  12:09:09  12:09:09  ed ()
 * QA #1 release
 * 
 * Revision 3.11  86/10/19  21:28:48  21:28:48  ed ()
 * Linted, register variables, prepared for Beta release.
 * 
 * Revision 3.10  86/10/18  16:57:44  16:57:44  ed ()
 * Changed MSG_FREE to remove WINDOWFUNCTS.
 * 
 * 
 * Revision 3.9  86/10/17  12:36:44  12:36:44  ed ()
 * Adjusted menu positioning heuristic.
 * 
 * Revision 3.8  86/10/16  19:59:57  19:59:57  ed ()
 * Currentpath and Currentwindow work.  Modified item language parser.
 * 
 * Revision 3.7  86/10/15  21:43:44  21:43:44  ed ()
 * Memory management changes
 * 
 * Revision 3.6  86/10/14  16:44:44  16:44:44  ed ()
 * Added item functs and item events
 * 
 * Revision 3.5  86/10/13  20:44:35  20:44:35  ed ()
 * Added Keyboard equivalent functionality.
 * 
 * Revision 3.4  86/10/10  08:57:43  08:57:43  ed ()
 * More drawing changes.
 * 
 * Revision 3.3  86/10/09  22:04:13  22:04:13  ed ()
 * Moved popup array to editor data structure.
 * (code changes for this move)
 * 
 * Revision 3.2  86/10/09  08:08:07  08:08:07  ed ()
 * Added color stuff, changed menu cursor to default cursor.
 * 
 * Revision 3.1  86/10/08  09:35:43  09:35:43  ed ()
 * Modified to use new XrMapButton.
 * 
 * Revision 3.0  86/10/02  16:02:47  16:02:47  ed ()
 * Alpha Release set to 3.0
 * 
 * Revision 1.4  86/09/30  10:26:28  10:26:28  ed ()
 * Missed a printf.
 * 
 * Revision 1.3  86/09/29  15:38:16  15:38:16  ed ()
 * Added grabmouse code, Added code to adjust menu if it would
 * have gone off the display.
 * 
 * 
 * Revision 1.2  86/09/28  19:23:35  19:23:35  ed ()
 * Removed printf's for alpha release.
 * 
 * Revision 1.1  86/09/28  19:12:56  19:12:56  ed ()
 * Initial revision
 * 
 *****************************************************************************
 *************************************<+>*************************************/




#include <X/Xlib.h>
#include <Xr/defs.h>
#include <Xr/types.h>
#include <Xr/in_types.h>
#include <ctype.h>

static INT32  xrDisplayWidth;
static INT32  xrDisplayHeight;

extern void addKeybdEquiv();


/*************************************<->*************************************
 *
 *  XrMenu(menuInstance, message, data)
 *
 *     xrMenu   * menuInstance;
 *     INT32      message;
 *     INT8     * data;
 *
 *   Description:
 *   -----------
 *     This is the menu manager. It provides a pop-up, cascading
 *     menuing system for the X-ray toolbox.  The most important
 *     message for the programmer are MSG_NEW and MSG_ACTIVATEMENU.
 *     MSG_NEW creates the menu, MSG_ACTIVATEMENU associates it 
 *     with a registered X-ray window.  Output form the menu
 *     manager is returned transparently to the program by the
 *     XrInput() module.  The programmer will not normally call
 *     the menu manager for input directly.
 *
 *     The other messages support the modification and query of
 *     the menu structure including sub-menu support.  For a complete
 *     description of the menu manager, refer to the X-ray toolbox
 *     manual.
 *
 *
 *   Inputs:
 *   ------
 *     menuInstance = This value indicates to the menu manager,
 *                    which menu should be dealt with.
 *
 *     message = Contains the command to be executed.
 *
 *     data    = Varies depending on the message, But is generally
 *               a pointer to a structure.
 *
 * 
 *   Outputs:
 *   -------
 *     menuInstance = Returned from MSG_NEW.
 *
 *     TRUE    = Returned upon successful completion of a message.
 *
 *     NULL    = Returned if something went wrong.
 *
 *     xrError = The error variable is set to one of several values
 *               upon failure of this routine.
 *
 *   Procedures Called
 *   -----------------
 *   MSG_NEW:
 *     (* xrMalloc)()
 *     (* xrFree)()
 *     DisplayHeight()      - XLib
 *     DisplayWidth()       - XLib
 *     strcmp()
 *     XrMenuEdit()         - MenuEdit.c
 *     XCreateWindow()      - XLib
 *     XQueryWindow()       - XLib
 *     XSelectInput()       - XLib
 *     XrCopyRect()         - calc.c
 *     XrInput()            - input.c
 *
 *  MSG_EDIT:
 *    XrInput()             - input.c
 *    XGrabServer()         - XLib
 *    XGrabMouse()          - XLib
 *    XQueryMouseButtons()  - XLib
 *    XQueryMouse()         - XLib
 *    XPixmapSave()         - XLib
 *    XMoveWindow()         - XLib
 *    XMapWindow()          - XLib
 *    XrMenuEdit()          - MenuEdit.c
 *    XUnmapTransparent()   - XLib
 *    XPixmapPut()          - XLib
 *    XFreePixmap()         - XLib
 *    XrMapButton()         - XLib
 *    XSync()               - XLib
 *    XUngrabMouse()        - XLib
 *    XUngrabServer()       - XLib
 *    XFlush()              - XLib
 *
 *  MSG_KEYBDEQUIV:
 *    XrInput()             - input.c
 *    XrInputMap()          - inputlib.c
 *    XSync()               - XLib
 *
 *  MSG_FREE:
 *    (* xrFree)()
 *    XrInput()             - input.c
 *    XrMenuEdit()          - MenuEdit.c
 *    XDestroyWindow()      - XLib
 *
 *  MSG_ACTIVATEMENU:
 *    XrGetWindowEvent()    - input.c
 *    XrInput()             - input.c
 *    addKeybdEquiv()
 *
 *  MSG_DEACTIVATEMENU:
 *    XrInput()             - input.c
 *
 *
 *
 *************************************<->***********************************/


xrMenu * 
XrMenu (menuInstance, message, data)
register xrMenu    * menuInstance;
INT32       message;
INT8      * data;
 
{

  /******************************************************************
   *
   *   Process the messages sent to the menu manager.
   *
   ******************************************************************/


   switch (message) {

   case MSG_NEW:
   {
      register xrMenu           * mnuInstance;
      register INT32               i;
      register xrMenuInfo        * mnuInfo;
               xrMenuEditor      * mnuEditor;
               xrWindowData        mnuWindowData;
               WindowInfo          winInfo;
      register INT32               currentChar;
      register INT32               parsing;
               INT32               disabled;

      /*  coerce data to proper type for MSG_NEW  */
      mnuInfo = (xrMenuInfo *) data; 


      /*  validate menu infomation pointer      */
      if ( mnuInfo == NULL ) {
         xrErrno = XrINVALIDPTR;
         return ((xrMenu *) NULL);
       }


      /* malloc space for the menu structures   */
      if ((mnuInstance = (xrMenu *) (*xrMalloc)(sizeof(xrMenu))) == NULL) {
         xrErrno = XrOUTOFMEM;
         return ((xrMenu *) NULL);
      }

      if ((mnuEditor = 
          (xrMenuEditor *) (*xrMalloc)(sizeof(xrMenuEditor))) == NULL) {
         xrErrno = XrOUTOFMEM;
         (* xrFree)(mnuInstance);
         return ((xrMenu *) NULL);
      }



      if ((mnuEditor -> menuStrings = 
        (INT8 **) (*xrMalloc)(sizeof(INT8 *) * (mnuInfo -> numItems))) 
        == NULL) {
        xrErrno = XrOUTOFMEM;
        (* xrFree)(mnuEditor);
        (* xrFree)(mnuInstance);
        return ((xrMenu *) NULL);
      }


      if ((mnuEditor -> itemTypes = 
          (INT32 *) (*xrMalloc)(sizeof(INT32) * (mnuInfo -> numItems)))
           == NULL)
      {
         xrErrno = XrOUTOFMEM;
         (* xrFree)(mnuEditor -> menuStrings);
         (* xrFree)(mnuEditor);
         (* xrFree)(mnuInstance);
         return ((xrMenu *) NULL);
      }

      if ((mnuEditor -> stringLengths = 
          (INT32 *) (*xrMalloc)(sizeof(INT32) * (mnuInfo -> numItems)))
           == NULL)
      {
         xrErrno = XrOUTOFMEM;
         (* xrFree) (mnuEditor -> itemTypes);
         (* xrFree)(mnuEditor -> menuStrings);
         (* xrFree)(mnuEditor);
         (* xrFree)(mnuInstance);
         return ((xrMenu *) NULL);
      }

      if ((mnuEditor -> keybdEquiv  = 
          (INT16 *) (*xrMalloc)(sizeof(INT16) * (mnuInfo -> numItems)))
           == NULL)
      {
         xrErrno = XrOUTOFMEM;
         (* xrFree) (mnuEditor -> stringLengths);
         (* xrFree) (mnuEditor -> itemTypes);
         (* xrFree)(mnuEditor -> menuStrings);
         (* xrFree)(mnuEditor);
         (* xrFree)(mnuInstance);
         return ((xrMenu *) NULL);
      }


      


      /***************************************************************
       *
       *   Fill out the menu and menu editor structures created 
       *   above by transferring the info structure information.
       *
       ****************************************************************/      

      xrDisplayHeight = DisplayHeight();
      xrDisplayWidth = DisplayWidth();

      if (mnuInfo -> menuTitle)
      {
         if (! strcmp(mnuInfo -> menuTitle,  ""))
            mnuEditor -> menuTitle = NULL;
         else
            mnuEditor -> menuTitle = mnuInfo -> menuTitle;
      }
      else
      {
         mnuEditor -> menuTitle = NULL;
      }

      mnuEditor -> numItems = mnuInfo -> numItems;


      if (!(mnuInfo -> menuContext))
         mnuInfo -> menuContext = &xrPanelContextDefaults;

      mnuEditor -> editorFont = mnuInfo -> menuContext -> fontInfo;
      mnuEditor -> editorFGColor = mnuInfo -> menuContext -> foregroundColor;
      mnuEditor -> editorBGColor = mnuInfo -> menuContext -> backgroundColor;

      for (i=0; i < mnuEditor -> numItems; i++)
      {
         mnuEditor -> menuStrings[i] = mnuInfo -> menuItems[i];
         mnuEditor -> keybdEquiv[i] = NULL;


         disabled = 0;
         currentChar = 0;
         parsing = 1;
         while(parsing)
         {
            if (mnuEditor -> menuStrings[i][0] == '\\')
            {
               switch (mnuEditor -> menuStrings[i][1])
               {
               case '-':
                  if(currentChar == 0)
                     mnuEditor -> itemTypes[i] = XrLINE;
                  parsing = 0;
                  break;
               case '=':
                  if(currentChar == 0)
                     mnuEditor -> itemTypes[i] = XrDBLINE;
                  parsing = 0;
                  break;
               case 'D':
                  mnuEditor -> menuStrings[i] += 3;
                  currentChar += 3;
                  mnuEditor -> itemTypes[i] = XrUSTRING;
                  mnuEditor -> stringLengths[i] = 
                               strlen(mnuEditor -> menuStrings[i]);
                  disabled = 1;
                  break;
               case 'K':
                  if(isalpha(mnuEditor ->menuStrings[i][3]))
                  {
                     mnuEditor -> keybdEquiv[i] = 
                                  mnuEditor -> menuStrings[i][3];
                  }
                  if(! disabled)
                     mnuEditor -> itemTypes[i] = XrSTRING;
                  mnuEditor -> menuStrings[i] += 4;
                  currentChar +=4;
                  mnuEditor -> stringLengths[i] = 
                               strlen(mnuEditor -> menuStrings[i]);
                  break;
               default:
                  mnuEditor -> itemTypes[i] = XrSTRING;
                  mnuEditor -> stringLengths[i] = 
                               strlen(mnuEditor -> menuStrings[i]);
                  parsing = 0;
                  break;
               }
            }
            else
            {
               if (! disabled)
                  mnuEditor -> itemTypes[i] = XrSTRING;
               parsing = 0;
            }
         }

         mnuEditor -> stringLengths[i] = strlen(mnuEditor -> menuStrings[i]);
      }

      mnuInstance -> pathLength = 0;
      mnuInstance -> numWindows = 0;
      mnuInstance -> menuId = mnuInfo -> menuId;
      mnuInstance -> menuCursor = mnuInfo -> menuContext -> cursor;
      mnuInstance -> borderWidth = mnuInfo -> menuContext -> borderWidth;

      for (i=0; i < XrMAXMENUWINDOWS - 1; i++)
      {
         mnuInstance -> currentWindows[i] = NULL;
      }

      /* Information for menu editor is ready, get the size */
     /*  and create the editor */

      if (XrMenuEdit(NULL, MSG_SIZE, mnuEditor) == NULL)
      {
         /* error out and return */
         xrErrno = XrOUTOFMEM;
         (* xrFree) (mnuEditor -> keybdEquiv);
         (* xrFree) (mnuEditor -> stringLengths);
         (* xrFree) (mnuEditor -> itemTypes);
         (* xrFree)(mnuEditor -> menuStrings);
         (* xrFree)(mnuEditor);
         (* xrFree)(mnuInstance);
         return ((xrMenu *) NULL);

      }

 

      if ( (mnuInstance -> menuWindow = 
                           XCreateWindow( RootWindow, 0, 0,
                                  mnuEditor -> editorRect.width,
                                  mnuEditor -> editorRect.height,
                                  mnuInfo -> menuContext -> borderWidth,
                                  mnuInfo -> menuContext -> winForeground,
                                  mnuInfo -> menuContext -> winBackground))
                                          == NULL)
      {
        /* error out and return NULL */
         xrErrno = XrOUTOFMEM;
         (* xrFree) (mnuEditor -> keybdEquiv);
         (* xrFree) (mnuEditor -> stringLengths);
         (* xrFree) (mnuEditor -> itemTypes);
         (* xrFree)(mnuEditor -> menuStrings);
         (* xrFree)(mnuEditor);
         (* xrFree)(mnuInstance);
         return ((xrMenu *) NULL);

      }

      XQueryWindow(mnuInstance -> menuWindow, &winInfo);
      mnuInstance -> totalWidth = winInfo.width + winInfo.bdrwidth * 2;
      mnuInstance -> totalHeight = winInfo.height + winInfo.bdrwidth * 2;

      if (mnuInstance -> totalWidth > xrDisplayWidth ||
          mnuInstance -> totalHeight > xrDisplayHeight)
      {
         xrErrno = XrINVALIDPARM;
         (* xrFree) (mnuEditor -> keybdEquiv);
         (* xrFree) (mnuEditor -> stringLengths);
         (* xrFree) (mnuEditor -> itemTypes);
         (* xrFree)(mnuEditor -> menuStrings);
         (* xrFree)(mnuEditor);
         (* xrFree)(mnuInstance);
         return ((xrMenu *) NULL);
       }

      mnuEditor -> editorWindowId = mnuInstance -> menuWindow;
      XSelectInput (mnuInstance -> menuWindow, ButtonPressed | ButtonReleased
                                               | EnterWindow | LeaveWindow);
      XrCopyRect(&xrZeroRect, &mnuWindowData.windowRect);
      mnuWindowData.foreTile = mnuInfo -> menuContext -> winForeground;
      mnuWindowData.backTile = mnuInfo -> menuContext -> winBackground;
      XrInput(mnuInstance -> menuWindow, MSG_ADDWINDOW, &mnuWindowData);

      mnuInstance -> menuEditor = XrMenuEdit(NULL, MSG_NEW, mnuEditor);
      mnuInstance -> menuEditor -> editorState = XrVISIBLE | XrSENSITIVE;
      (* xrFree) (mnuEditor);

      return (mnuInstance);
      break;
   } /* MSG_NEW end */




    case MSG_EDIT:
    {

      register xrMenu  * currentMenu;
      register xrMenu  * newMenu;
      XButtonEvent     * menuKey;
      xrMenu           * mainMenu;
      xrEvent            menuEditKey;
      XEnterWindowEvent  enterKey;
      static xrEvent     buildKey = {XrXRAY, 0, 0, XrMENU, 0, 0, 0,
                                     {0, 0}, 0};
      static xrEvent     nullKey = {0, 0, 0, 0, 0, 0, 0,
                                     {0, 0}, 0};
      xrEvent          * returnKey;
      INT32              originX, originY, cursorX, cursorY, outsideMenu;
      register INT32     menuing, numMenus;
      register INT32     menuIndex;
      Window             subw;
      xrMenuData       * menuData;
      INT16              state;


      if ((menuKey = (XButtonEvent *) data) == NULL ||
           (menuKey -> type != ButtonPressed && 
           menuKey -> type != ButtonReleased)) 
      {
         XrInput(NULL, MSG_PUSHEVENT, menuKey);
         xrErrno = XrINVALIDPTR;
         return ((xrMenu *) NULL);
      }

      /*  validate menu  pointer      */
      if ( menuInstance == NULL ) {
         XrInput(NULL, MSG_PUSHEVENT, menuKey);
         xrErrno = XrINVALIDPTR;
         return ((xrMenu *) NULL);
       }



      /* Check to make sure the user hasn't released the button */

      XGrabMouse(RootWindow, menuInstance -> menuCursor, 
                 ButtonPressed | ButtonReleased | EnterWindow | LeaveWindow);

      XQueryMouseButtons(RootWindow, &originX, &originY, &subw, &state);
      if((state == 0) && ((xrMenuItemSelect == XrRIGHTBUTTONUP) ||
                          (xrMenuItemSelect == XrLEFTBUTTONUP) ||
                          (xrMenuItemSelect == XrMIDDLEBUTTONUP)))
      {
       XUngrabMouse();
       XrInput(NULL, MSG_PUSHEVENT, &nullKey);
       return((xrMenu *) TRUE);
      }

      XGrabServer();


      buildKey.source = menuKey -> window;
      numMenus = 0;
      currentMenu = (xrMenu *) NULL;
      newMenu = mainMenu = menuInstance;
      mainMenu -> currentPath[numMenus].menuInstance = newMenu;
      mainMenu -> currentPath[numMenus].itemIndex = -1;
      menuing = TRUE;

      while (menuing)
      {
         if (currentMenu != newMenu)
         {
            currentMenu = newMenu;
            menuData = (xrMenuData *) currentMenu -> menuEditor -> editorData;
            numMenus++;
            XQueryMouse(RootWindow, &originX, &originY, &subw);
            cursorX = originX;
            cursorY = originY;

            originX -= (menuData -> itemPosx + currentMenu -> borderWidth);
            originY -= (menuData -> itemPosy[0] + currentMenu -> borderWidth);

            /* figure the origin of the menu */

            if(originX < 1)
            {
               originX = 1;
               cursorX = 1;
            }

            if(originY < 1)
            {
               originY = 1;
               cursorY = 1;
            }

            if ((originX + currentMenu -> totalWidth) > xrDisplayWidth)
            {
             originX = xrDisplayWidth - (currentMenu -> totalWidth + 1);
             cursorX = originX + menuData -> itemPosx + 
                                 currentMenu -> borderWidth;
            }

            if ((originY + currentMenu -> totalHeight) > xrDisplayHeight)
            {
             originY = xrDisplayHeight - (currentMenu -> totalHeight + 1);
             cursorY = originY + menuData -> itemPosy[0] + 
                                 currentMenu -> borderWidth;
            }


            /* figuring origin complete      */

            currentMenu -> menuOrigin.x = originX;
            currentMenu -> menuOrigin.y = originY;

            /* code here to keep window within display bounds */
            currentMenu -> menuPixmap = XPixmapSave(RootWindow, 
                        currentMenu -> menuOrigin.x,
                        currentMenu -> menuOrigin.y,
                        currentMenu -> totalWidth,
                        currentMenu -> totalHeight);
            XMoveWindow(currentMenu -> menuWindow, 
                        currentMenu -> menuOrigin.x,
                        currentMenu -> menuOrigin.y);
            XMapWindow(currentMenu -> menuWindow);
            if(currentMenu != mainMenu)
               XWarpMouse(RootWindow, cursorX, cursorY);

            XrMenuEdit(currentMenu -> menuEditor, MSG_REDRAW, XrREDRAW_ALL);
          }
 
          XrMenuEdit(currentMenu -> menuEditor, MSG_EDIT, &menuEditKey);
          XrInput(NULL, MSG_NONBLKREAD, &menuEditKey);
          if ((menuEditKey.type & XrXRAY) && 
              (menuEditKey.inputCode == XrMENUEDIT))
          {

          switch(menuEditKey.value1)
          {
          case XrITEMAREA:
          {
             if (menuEditKey.value3 == XrMENUITEMSELECT)
             {
                buildKey.value2 = currentMenu -> menuId;
                buildKey.value3 = menuEditKey.value2;
                buildKey.valuePtr = (INT32) currentMenu;
                menuing = FALSE;
                returnKey = &buildKey;
             }

          
             break;
          }

          case XrUITEMAREA:
          {
             if (menuEditKey.value3 == XrMENUITEMSELECT)
             {
               returnKey = &nullKey;
               menuing = FALSE;
             }

             break;
          }


          case XrPOPUPAREA:
          {
             if(menuData -> popupMenu[menuEditKey.value2])
             {
                mainMenu -> currentPath[numMenus - 1].menuInstance = 
                                                       currentMenu;
                mainMenu -> currentPath[numMenus - 1].itemIndex = 
                                                       menuEditKey.value2;
                newMenu = menuData -> popupMenu[menuEditKey.value2];

                mainMenu -> currentPath[numMenus].menuInstance = newMenu;
                mainMenu -> currentPath[numMenus].itemIndex = -1;
             }
             else 
             {
                if (menuEditKey.value3 == XrMENUITEMSELECT)
                {
                   buildKey.value2 = currentMenu -> menuId;
                   buildKey.value3 = menuEditKey.value2;
                   buildKey.valuePtr = (INT32) currentMenu;
                   menuing = FALSE;
                   returnKey = &buildKey;
                }

              }

             break;
          }


          case XrEXITPOPUP:
          {
             break;
          }


          case XrOUTSIDEMENU:
          {
             outsideMenu = TRUE;
             while(outsideMenu)
             {
                XrInput(NULL, MSG_BLKREAD, &enterKey);

                if (enterKey.type == EnterWindow)
                {
                   for (menuIndex = 0; menuIndex < numMenus; menuIndex++)
                   {
                      if (mainMenu -> 
                          currentPath[menuIndex].menuInstance -> menuWindow ==
                          enterKey.window)
                      {
                         outsideMenu = FALSE;

                         while (numMenus-- != menuIndex + 1)
                         {

                            XUnmapTransparent(currentMenu -> menuWindow);
                            XPixmapPut(RootWindow, 
                            0,0,
                            currentMenu -> menuOrigin.x, currentMenu -> menuOrigin.y, 
                            currentMenu -> totalWidth, currentMenu -> totalHeight,
                            currentMenu -> menuPixmap, GXcopy, AllPlanes);
                            XFreePixmap(currentMenu -> menuPixmap);

                            XrMenuEdit(currentMenu -> menuEditor, MSG_RESET, NULL);
                            currentMenu = mainMenu -> currentPath[numMenus - 1].menuInstance;
                            newMenu = currentMenu;
                         }
                         numMenus++;
                         break;
                      }

                   }  /* for loop */

            menuData = (xrMenuData *) currentMenu -> menuEditor -> editorData;
                }  /* if statement */
                else
                {
                   if(XrMapButton(XrMENUITEMSELECT,&enterKey))
                   {
                      menuing = FALSE;
                      returnKey = &nullKey;
                      outsideMenu = FALSE;
                      break;
                   }
                }


             }   /* while loop */

             break;
          }


          case XrNULLAREA:
          {
             if (menuEditKey.value3 == XrMENUITEMSELECT)
             {
               returnKey = &nullKey;
               menuing = FALSE;
             }

             break;
          }
          default:
             break;

          } /* end switch */


         } /* end if */

      } /* end while */

      mainMenu -> pathLength = numMenus;

      while (numMenus--)
      {

         XUnmapTransparent(currentMenu -> menuWindow);
         XPixmapPut(RootWindow, 
                    0,0,
                    currentMenu -> menuOrigin.x, currentMenu -> menuOrigin.y, 
                    currentMenu -> totalWidth, currentMenu -> totalHeight,
                    currentMenu -> menuPixmap, GXcopy, AllPlanes);
         XFreePixmap(currentMenu -> menuPixmap);


         XrMenuEdit(currentMenu -> menuEditor, MSG_RESET, NULL);
         currentMenu = mainMenu -> currentPath[numMenus - 1].menuInstance;

      }

      
      XSync(1);

      XUngrabMouse();
      XUngrabServer();

      XFlush();


      if (returnKey -> type == 0)
      {
         XrInput(NULL, MSG_PUSHEVENT, returnKey);
         return((xrMenu *) TRUE);
      }
      else
      {
         if(menuData -> itemEvent[returnKey -> value3])
            XrInput(NULL, MSG_PUSHEVENT, 
                          menuData -> itemEvent[returnKey -> value3]);
         else
            XrInput(NULL, MSG_PUSHEVENT, returnKey);
       }

      if (menuData -> itemFunct[returnKey -> value3])
         (* menuData -> itemFunct[returnKey -> value3])();

      return((xrMenu*) TRUE);

 
     break;

    }

    case MSG_KEYBDEQUIV:
    {

      xrEvent             returnEvent;
      XKeyEvent         * keybdEvent;
      register xrMenuData * menuData;
      INT8                * asciiCode;
      static INT16        charMask = 0x1f;
      register INT32      i;
      INT32               nBytes;
      static xrEvent      nullKey = {0, 0, 0, 0, 0, 0, 0,
                                     {0, 0}, 0};


      if ((keybdEvent = (XKeyEvent *) data) == NULL ||
           (keybdEvent -> type != KeyPressed)) 
      {
         XrInput(NULL, MSG_PUSHEVENT, keybdEvent);
         xrErrno = XrINVALIDPTR;
         return ((xrMenu *) NULL);
      }

      /*  validate menu  pointer      */
      if ( menuInstance == NULL ) {
         XrInput(NULL, MSG_PUSHEVENT, keybdEvent);
         xrErrno = XrINVALIDPTR;
         return ((xrMenu *) NULL);
       }

       asciiCode = XrInputMap(keybdEvent, &nBytes);
       menuData = (xrMenuData *) menuInstance -> menuEditor -> editorData;


       returnEvent.value3 = -1;
       
       for(i=0; i < menuData -> numItems; i++)
       {
          if((menuData -> keybdEquiv[i] & charMask) == * asciiCode)
          {
              returnEvent.value3 = i;
              break;
          }
       }

       if (menuData -> itemTypes[returnEvent.value3] == XrUSTRING)
       {
          returnEvent = nullKey;
       }
       else
       {
          returnEvent.type = XrXRAY;
          returnEvent.source = keybdEvent -> window;
          returnEvent.inputCode = 0;
          returnEvent.inputType = XrMENU;
          returnEvent.value1 = 0;
          returnEvent.value2 = menuInstance -> menuId;
          returnEvent.valuePtr = (INT32) menuInstance;
        }

      XSync(1);

      if (returnEvent.type == 0)
      {
         XrInput(NULL, MSG_PUSHEVENT, &returnEvent);
         return((xrMenu *) TRUE);
      }
      else
      {
         if(menuData -> itemEvent[returnEvent.value3])
            XrInput(NULL, MSG_PUSHEVENT, 
                          menuData -> itemEvent[returnEvent.value3]);
         else
            XrInput(NULL, MSG_PUSHEVENT, &returnEvent);
       }

      if (menuData -> itemFunct[returnEvent.value3])
         (* menuData -> itemFunct[returnEvent.value3])();

      return((xrMenu*) TRUE);

      break;

   }



    case MSG_FREE:
    {

      register xrMenuData * menuData;
      register INT32 i;

      /*  validate menu infomation pointer      */
      if (menuInstance == NULL) {
         xrErrno = XrINVALIDPTR;
         return ((xrMenu *) NULL);
       }

       menuData = (xrMenuData *) menuInstance -> menuEditor -> editorData;

         /* Remove menu display functions for given menu and windows */

         for (i=0; i < XrMAXMENUWINDOWS; i++)
         {
            if (menuInstance -> currentWindows[i])
            {
               while (XrInput(menuInstance -> currentWindows[i], 
                              MSG_REMOVEWINDOWFUNCT, 
                              (INT8 *) XrMenu))
                {
                }
            }
         }

         /* free various arrays */

         (* xrFree) (menuData -> menuStrings);
         (* xrFree) (menuData -> itemTypes);
         (* xrFree) (menuData -> stringLengths);
         (* xrFree) (menuData -> keybdEquiv);

         XrMenuEdit(menuInstance -> menuEditor, MSG_FREE, NULL);
         XrInput(menuInstance -> menuWindow, MSG_REMOVEWINDOW, NULL);
         XDestroyWindow(menuInstance -> menuWindow);
         (* xrFree) (menuInstance);
         return ((xrMenu * ) TRUE);

       break;
    }


    case MSG_ACTIVATEMENU:
    {
      Window  menuWindow;
      xrWindowEvent   eventSet[1];
      static  xrWindowFunctInfo menuFunct = {TRUE, (xrPFI)XrMenu,
                                             NULL,
                                             (INT32)MSG_EDIT,1,
                                             NULL};
      register INT32 i;

      if ((menuWindow = (Window) data) == NULL) {
         xrErrno = XrINVALIDPTR;
         return ((xrMenu *) NULL);
      }

      /*  validate menu  pointer      */
      if ( menuInstance == NULL ) {
         xrErrno = XrINVALIDPTR;
         return ((xrMenu *) NULL);
       }

       if (menuInstance -> numWindows + 1 > XrMAXMENUWINDOWS)
         if ( menuInstance == NULL ) 
         {
            xrErrno = XrOUTOFMEM;
            return ((xrMenu *) NULL);
         }

       /*  Remove any menu functions associated with this window */
       while (XrInput(menuWindow, MSG_REMOVEWINDOWFUNCT, (INT8 *) XrMenu))
       {
       }

       menuFunct.instance = (INT32) menuInstance;
       menuFunct.eventList = &eventSet[0];
       XrGetWindowEvent(XrMENUPOST, &eventSet[0]);

       if (!(XrInput(menuWindow, MSG_ADDWINDOWFUNCT, &menuFunct)))
       {
          /* errno set by XrInput */
          return((xrMenu *) NULL);
       }

       addKeybdEquiv(menuInstance, menuWindow, TRUE);

       for (i=0; i <= XrMAXMENUWINDOWS - 1; i++)
       {
          if (! menuInstance -> currentWindows[i])
          {
             menuInstance -> currentWindows[i] = menuWindow;
             menuInstance -> numWindows++;
             break;
          }
       }

       return((xrMenu *) TRUE);


       break;

     }

    case MSG_DEACTIVATEMENU:
    {
      register Window   menuWindow;
      register INT32    i, thisWindow;

      /*  validate menu  pointer      */
      if ( menuInstance == NULL ) {
         xrErrno = XrINVALIDPTR;
         return ((xrMenu *) NULL);
       }

      if ((menuWindow = (Window) data) == NULL) {
         xrErrno = XrINVALIDPTR;
         return ((xrMenu *) NULL);
      }

       /*****************************************************/
       /* check to make sure menu is active on given window */
       /*****************************************************/


       thisWindow = FALSE;

       for (i=0; i < menuInstance -> numWindows; i++)
       {
          if (menuInstance -> currentWindows[i] == menuWindow)
          {
             thisWindow = TRUE;
             menuInstance -> currentWindows[i] = NULL;
             break;
          }
        }

       if (thisWindow)
       {
          while (XrInput(menuWindow, MSG_REMOVEWINDOWFUNCT, (INT8 *) XrMenu))
          {
          }
          return((xrMenu *) TRUE);
       }

       xrErrno = XrINVALIDPARM;
       return ((xrMenu *) NULL);
       break;

    }

    case MSG_ADDSUBMENU:
    {

      register xrMenuIndex   * subMenu;
      register xrMenuData    * menuData;

      subMenu = (xrMenuIndex *) data;

      if (subMenu == NULL) {
         xrErrno = XrINVALIDPTR;
         return ((xrMenu *) NULL);
      }


      /*  validate menu  pointer      */
      if ( menuInstance == NULL ) {
         xrErrno = XrINVALIDPTR;
         return ((xrMenu *) NULL);
       }

       menuData = (xrMenuData *) menuInstance -> menuEditor -> editorData;

       menuData -> popupMenu[subMenu -> itemIndex] = 
                                               subMenu -> menuInstance;
       return((xrMenu *) TRUE);
       break;

    }

    case MSG_REMOVESUBMENU:
    {

      register xrMenuIndex   * subMenu;
      register xrMenuData    * menuData;

      subMenu = (xrMenuIndex *) data;

      if (subMenu == NULL) {
         xrErrno = XrINVALIDPTR;
         return ((xrMenu *) NULL);
      }


      /*  validate menu  pointer      */
      if ( menuInstance == NULL ) {
         xrErrno = XrINVALIDPTR;
         return ((xrMenu *) NULL);
       }

       menuData = (xrMenuData *) menuInstance -> menuEditor -> editorData;

       menuData -> popupMenu[subMenu -> itemIndex] = (xrMenu *) NULL;
       return((xrMenu *) TRUE);

       break;
     }



    case MSG_ACTIVATEITEM:
    {
      register INT32 itemIndex;
      register xrMenuData  * dataPtr;

      if ((itemIndex = (INT32) data) < 0) {
         xrErrno = XrINVALIDPARM;
         return ((xrMenu *) NULL);
      }

      /*  validate menu  pointer      */
      if ( menuInstance == NULL ) {
         xrErrno = XrINVALIDPTR;
         return ((xrMenu *) NULL);
       }

       dataPtr = (xrMenuData *) menuInstance -> menuEditor -> editorData;

       if ((dataPtr -> itemTypes[itemIndex] == XrSTRING) ||
           (dataPtr -> itemTypes[itemIndex] == XrUSTRING))
       {
          dataPtr -> itemTypes[itemIndex] = XrSTRING;
       }
       else
       {
          xrErrno = XrINVALIDPARM;
          return((xrMenu *) NULL);
       }


       return((xrMenu *) TRUE);
       break;
     }

    case MSG_DEACTIVATEITEM:
    {
      register INT32 itemIndex;
      register xrMenuData  * dataPtr;

      if ((itemIndex = (INT32) data) < 0) {
         xrErrno = XrINVALIDPARM;
         return ((xrMenu *) NULL);
      }

      /*  validate menu  pointer      */
      if ( menuInstance == NULL ) {
         xrErrno = XrINVALIDPTR;
         return ((xrMenu *) NULL);
       }

       dataPtr = (xrMenuData *) menuInstance -> menuEditor -> editorData;

       if ((dataPtr -> itemTypes[itemIndex] == XrSTRING) ||
           (dataPtr -> itemTypes[itemIndex] == XrUSTRING))
       {
          dataPtr -> itemTypes[itemIndex] = XrUSTRING;
       }
       else
       {
          xrErrno = XrINVALIDPARM;
          return((xrMenu *) NULL);
       }

      return((xrMenu *) TRUE);
      break;
     }

    case MSG_SETITEMFUNCT:
    {
      register xrMenuIndex * indexStruct;
      register xrMenuData  * dataPtr;

      if ((indexStruct = (xrMenuIndex *) data) < 0) {
         xrErrno = XrINVALIDPARM;
         return ((xrMenu *) NULL);
      }

      /*  validate menu  pointer      */
      if ( menuInstance == NULL ) {
         xrErrno = XrINVALIDPTR;
         return ((xrMenu *) NULL);
       }

       dataPtr = (xrMenuData *) menuInstance -> menuEditor -> editorData;

       dataPtr -> itemFunct[indexStruct -> itemIndex] = 
                  (xrPFI) indexStruct -> itemData;

       return((xrMenu *) TRUE);

       break;

    }

    case MSG_SETITEMEVENT:
    {
      register xrMenuIndex * indexStruct;
      register xrMenuData  * dataPtr;

      if ((indexStruct = (xrMenuIndex *) data) < 0) {
         xrErrno = XrINVALIDPARM;
         return ((xrMenu *) NULL);
      }

      /*  validate menu  pointer      */
      if ( menuInstance == NULL ) {
         xrErrno = XrINVALIDPTR;
         return ((xrMenu *) NULL);
       }

       dataPtr = (xrMenuData *) menuInstance -> menuEditor -> editorData;

       dataPtr -> itemEvent[indexStruct -> itemIndex] = 
                  (xrEvent *) indexStruct -> itemData;

       return((xrMenu *) TRUE);

    }


    default:
      xrErrno = XrINVALIDMSG;
      return((xrMenu *) NULL);
   }
   return((xrMenu*) TRUE);
}


/*************************************<->*************************************
 *
 *  addKeybdEquiv(instance, menuWindow, recursive)
 *    xrMenu  * instance;
 *    Window    menuWindow;
 *    INT32     recursive;
 *
 *   Description:
 *   -----------
 *     This routine recursively goes through a menu tree and adds
 *     keyboard equivalents to the window given.
 *
 *
 *   Inputs:
 *   ------
 *     instance = The menu instance to start recursion on.
 *
 *     menuWindow = the window to add the keyboard equivalents to.
 *
 *     recursive = A boolean that determines if the sub-menus
 *                 should be considered.
 * 
 *   Outputs:
 *   -------
 *     void function -- Adds WINDOWFUNCTS as a side effect.
 *
 *   Procedures Called
 *   -----------------
 *     isalpha()
 *     XrInput()         - input.c
 *     addKeybdEquiv()
 *
 *
 *************************************<->***********************************/


static
void
addKeybdEquiv(instance, menuWindow, recursive)
xrMenu * instance;
Window   menuWindow;
INT32    recursive;

{
   xrWindowFunctInfo keybdFunct;
   static INT16 charMask = 0x1f;
   register INT32 i;
   register xrMenuData  * menuData;
   xrWindowEvent keybdEvent;


   menuData = (xrMenuData *) instance -> menuEditor -> editorData;

   for (i=0; i < menuData -> numItems; i++)
   {
      if (menuData -> keybdEquiv[i])
      {
          if (isalpha(menuData -> keybdEquiv[i]))
          {
             keybdFunct.processFlag = TRUE;
             keybdFunct.funct = (xrPFI) XrMenu;
             keybdFunct.instance = (INT32) instance;
             keybdFunct.message = MSG_KEYBDEQUIV;
             keybdFunct.eventCount = 1;
             keybdEvent.inputType = KeyPressed;
             keybdEvent.inputCode = charMask & menuData -> keybdEquiv[i];
             keybdFunct.eventList = &keybdEvent;
     
             XrInput(menuWindow, MSG_ADDWINDOWFUNCT, &keybdFunct);
             
          }

       }

      if ((menuData -> popupMenu[i]) && recursive)
      {
         addKeybdEquiv(menuData -> popupMenu[i], menuWindow, TRUE);
      }

   }

   return;
}
