DataMuseum.dk

Presents historical artifacts from the history of:

Rational R1000/400 Tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about Rational R1000/400 Tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - download
Index: ┃ T r

⟦70c1d0280⟧ TextFile

    Length: 52445 (0xccdd)
    Types: TextFile
    Names: »ratmenu.c«

Derivation

└─⟦5f3412b64⟧ Bits:30000745 8mm tape, Rational 1000, ENVIRONMENT 12_6_5 TOOLS 
    └─ ⟦91c658230⟧ »DATA« 
        └─⟦5d656759a⟧ 
            └─⟦144d629ab⟧ 
                └─ ⟦this⟧ »./ratmenu.c« 
└─⟦5f3412b64⟧ Bits:30000745 8mm tape, Rational 1000, ENVIRONMENT 12_6_5 TOOLS 
    └─ ⟦91c658230⟧ »DATA« 
        └─⟦5d656759a⟧ 
            └─⟦5cb9d2efd⟧ 
                └─ ⟦this⟧ »./ratmenu.c« 
└─⟦5f3412b64⟧ Bits:30000745 8mm tape, Rational 1000, ENVIRONMENT 12_6_5 TOOLS 
    └─ ⟦91c658230⟧ »DATA« 
        └─⟦5d656759a⟧ 
            └─⟦7d9cab9a9⟧ 
└─⟦d10a02448⟧ Bits:30000409 8mm tape, Rational 1000, ENVIRONMENT, D_12_7_3
    └─ ⟦fc9b38f02⟧ »DATA« 
        └─⟦8e9e227a9⟧ 
            └─⟦7d9cab9a9⟧ 
                └─ ⟦this⟧ »./ratmenu.c« 
└─⟦5f3412b64⟧ Bits:30000745 8mm tape, Rational 1000, ENVIRONMENT 12_6_5 TOOLS 
    └─ ⟦91c658230⟧ »DATA« 
        └─⟦5d656759a⟧ 
            └─⟦85ff0a957⟧ 
└─⟦d10a02448⟧ Bits:30000409 8mm tape, Rational 1000, ENVIRONMENT, D_12_7_3
    └─ ⟦fc9b38f02⟧ »DATA« 
        └─⟦8e9e227a9⟧ 
            └─⟦85ff0a957⟧ 
                └─ ⟦this⟧ »./ratmenu.c« 

TextFile

#include <stdio.h>
#include <ctype.h>
#include <setjmp.h>
#include <X11/Xlib.h>
#include <X11/StringDefs.h>
#include <X11/IntrinsicP.h>
#include "RMCommand.h"
#include "RMForm.h"
#include <X11/Shell.h>
#include <X11/Xos.h>
#include "ratmenu.h"
#include "RMShellP.h"
#include "RMLabelP.h"
#include "RMSubP.h"
#include <X11/cursorfont.h>
#include <X11/Xatom.h>
#define XK_MISCELLANY
#define XK_LATIN1
#include <X11/keysymdef.h>
#include <sys/param.h>
#include "ptyx.h"
#include "data.h"
#include "menu.h"


\f


/******************************************************************************
* Package State
******************************************************************************/

#ifdef RIOS
#undef strcpy
#endif
extern char	*strcpy();

extern int	default_menuBorder;
extern int	default_menuPad;
static int	defZero       = 0;
static String	defNullString = NULL;
static Boolean	defTrue       = TRUE;
static Boolean	defFalse      = FALSE;

#define MAXMENUDEPTH	4

static Cursor	MainCursor;
static int	MenuDepth = -1;
static Widget	DaddyMenu[MAXMENUDEPTH];
static Widget	ChildMenu[MAXMENUDEPTH];
static KeySym	MenuPick [MAXMENUDEPTH];

static Boolean	ActionsRegistered = FALSE;

Widget  		RatMenu = NULL;
RatMenuDefaultsType 	RatMenuDefaults;
Window			RatMenuTransientFor = 0;

char   	RatMenuDontWarp[] = "DontWarp";
char   	RatMenuDontMove[] = "DontMove";
char   *RatMenuDontMoveWarp[] = { RatMenuDontMove, RatMenuDontWarp };

#define offset(fld)  XtOffset(RatMenuDefaultsType*,fld)

XtResource RatMenuResourceList[] = {
  {"envMenuFile", "EnvMenuFile", XtRString, sizeof(char*),
     offset(env_menu_file), XtRString, (caddr_t)NULL},
  {"itemBorder", "ItemBorder", XtRInt, sizeof(int),
     offset(item_border_width), XtRInt, (caddr_t)&defZero},
  {"menuLabels", "MenuLabels", XtRBoolean, sizeof(Boolean),
     offset(want_menu_labels), XtRBoolean, (caddr_t)&defTrue},
  {"menuSpace", "MenuSpace", XtRInt, sizeof(int),
     offset(item_pad), XtRInt, (caddr_t)&default_menuPad},
  {"permanent", "Permanent", XtRBoolean, sizeof(Boolean),
     offset(permanent), XtRBoolean, (caddr_t)&defFalse},
  {"subBorder", "SubBorder", XtRInt, sizeof(int),
     offset(sub_border_width), XtRInt, (caddr_t)&defZero},
  {"subDistance", "SubDistance", XtRInt, sizeof(int),
     offset(sub_horiz_distance), XtRInt, (caddr_t)&default_menuPad},
  {"submenuLabels", "SubmenuLabels", XtRBoolean, sizeof(Boolean),
     offset(want_submenu_labels), XtRBoolean, (caddr_t)&defTrue},
  {"subSlide", "SubSlide", XtRBoolean, sizeof(Boolean),
     offset(slide_subs), XtRBoolean, (caddr_t)&defFalse},
  {"warp", "Warp", XtRBoolean, sizeof(Boolean),
     offset(warp), XtRBoolean, (caddr_t)&defTrue},
};

int Size_RatMenuResourceList = XtNumber(RatMenuResourceList);

#undef offset

extern void AddRatItem();
extern void RationalMenuInit();
static void NotifyRatMenu();
extern void SubupRatMenu();
extern void SubdownRatMenu();
static PickRatMenuCallback();
extern void PopupRatMenu();
extern void PopdownRatMenu();
static void NoOp();
static void KeyRatMenu();
extern void PopdownRatMenuChain();
static void DropSubsRatMenu();
static void UnmappedRatMenu();
       void spit();

#define SETARG( XtN, XtV )   XtSetArg( args[argi], XtN, XtV ); argi++
#define CALLBACK( XtN, XtV ) Callback[++cbi].callback = (XtCallbackProc)XtN; \
			     Callback[  cbi].closure =(caddr_t)XtV

/*--When we pop up a menu we try to put it's x/y coordinates at this offset
 *  back from the event's coordinates. */

#define XPOPUP	5
#define YPOPUP	5

\f


void InitRatCursor()
/******************************************************************************
* InitRatCursor - This started out simply making sure that the menu cursor
* was properly defined before we created the Rational menu(s); now however we
* make sure that we've retrieved the full set of resources specific to the
* Rational menu(s).
******************************************************************************/
{
  static char	DidThisOnce = FALSE;
  extern Menu   Menu_Default;
  extern Cursor Menu_DefaultCursor;
  extern void   InitMenu();

/*--If the menu cursor hasn't been given, make a default one. */

  if (!DidThisOnce) { InitMenu( xterm_name ); }
  DidThisOnce = TRUE;
  if(!Menu_Default.menuCursor) {
    if(!Menu_DefaultCursor) {
      Menu_DefaultCursor =
	XCreateFontCursor(term->screen.display, XC_left_ptr);
    }
    Menu_Default.menuCursor = Menu_DefaultCursor;
  }

/*--Get the Rational menu resources. */
/* Now done in main.c at startup.
*
*  XtGetSubresources( (Widget)term, (caddr_t)&RatMenuDefaults, "menu", "Menu",
*		     RatMenuResourceList, XtNumber(RatMenuResourceList),
*		     NULL, 0 );
*/

/*--Fill in stuff from the term. */

  RatMenuDefaults.background        = term->core.background_pixel;
  RatMenuDefaults.border_pixel      = term->core.border_pixel;
  RatMenuDefaults.foreground        = term->screen.foreground;
  RatMenuDefaults.font              = Menu_Default.menuFontInfo;
  RatMenuDefaults.menu_border_width = Menu_Default.menuBorderWidth;
  RatMenuDefaults.menu_whitespace   = Menu_Default.menuItemPad;

} /* InitRatCursor */

\f


Boolean RatMenuMapped( w )
     Widget	w;
/******************************************************************************
* RatMenuMapped - Returns true if a shell exists and thinks it is mapped.
******************************************************************************/
{

  if (w == NULL) { return FALSE; }
  if (!XtIsSubclass( w, ratMenuShellWidgetClass )) {
    fprintf( stderr, "RatMenuMapped: widget not RatMenuShell [%s]\n",
	     w->core.widget_class->core_class.class_name );
    exit(1);
  }
  return ((RatMenuShellWidget)w)->ratmenu.mapped;

} /* RatMenuMapped */

\f


void RatMenuPermanent( move )
     Boolean	move;
/******************************************************************************
* RatMenuPermanent - Control for permanent menus.  Map or unmap the menu
* as appropriate to the current state of the world.  Our parameter tells us
* whether or not a remap is allowed to move the menu.  A creation always
* moves the menu.
******************************************************************************/
{
    char	*what[1];
    Cardinal	 count;

/*--Make sure the menu exists.  If we have to create it then we are free to
 *  move it. */

    if (RatMenu == NULL) {
	RationalMenuInit( (Widget)term );
	move = TRUE;

/*--If menu already exists then change the override flag to correspond to the
 *  permanency.  Ie. We want window managers to be able (or not to be able)
 *  to play with the placement of the window. */

    } else {
	Arg	args[5];
	int	argi = 0;
	SETARG( XtNoverrideRedirect, !RatMenuDefaults.permanent );
	SETARG( XtNinput,	     !RatMenuDefaults.permanent );
	XtSetValues( RatMenu, args, (Cardinal)argi );
	if (RatMenuDefaults.permanent && XtIsRealized(RatMenu)) {
	    XSetTransientForHint( XtDisplay(RatMenu), XtWindow(RatMenu),
				  RatMenuTransientFor );
	}
    }

/*--Make sure that the menu is up or down as appropriate.  If it is a permanent
 *  menu then it must be/go up.  Otherwise it must be/go down. */

  if (RatMenuDefaults.permanent && term->misc.rational) {
      DropSubsRatMenu( (Widget)RatMenu, (XEvent*)NULL,
		       (char**)NULL, (Cardinal*)NULL );
      if (move || !XtIsRealized(RatMenu)) {
	  what[0] = RatMenuDontWarp;
	  count = 1;
	  PopupRatMenu( RatMenu, (XEvent*)NULL, &what[0], &count );
      } else {
	  count = 2;
	  PopupRatMenu( RatMenu, (XEvent*)NULL, RatMenuDontMoveWarp, &count );
      }
  } else if (RatMenuMapped(RatMenu)) {
      count = 0;
      PopdownRatMenuChain( RatMenu, (XEvent*)NULL, (char**)NULL, &count );
  }

} /* RatMenuPermanent */

\f


Widget StartAnyRatMenu( rmname, label, toplevel, cursor,
		        menupick, cmdpick, submenu )
     String	rmname;		/* resource name of the menu to create */
     String	label;		/* label for the menu when visible to user */
     Widget	toplevel;	/* the true top-level shell of application */
     Cursor	cursor;		/* None or the cursor to use within the menu */
     KeySym	menupick;	/* KeySym to use when picking submenu items */
     KeySym	cmdpick;	/* KeySym to use when submenu picked itself */
     Boolean	submenu;	/* TRUE if this is a sub-menu */
/******************************************************************************
* StartAnyRatMenu - Begin a new Rational Menu.  Create a Popup shell
* and set the menu label.  Set up for adding items to the menu.
******************************************************************************/
{
  int	  argi;
  Arg	  args[20];
  Widget  TheMenu;
  static char shellactions[] = "<Unmap>: RatMenuUnmapped()";
  static char actions[] = "<Btn2Up>:     RatMenuPopdownChain()\n\
			   <Btn2Down>:	 noop()\n\
			   <Key>:        RatMenuKey()\n\
			   <EnterWindow>:RatMenuDropSubs()\n";


/*--This is a new menu.  Create his outermost DaddyMenu. */

  if (++MenuDepth == 0) { MainCursor = cursor; }
  argi = 0;
  SETARG( XtNtitle,		label );
  SETARG( XtNcursor,		cursor );
  SETARG( XtNmenuDepth,		MenuDepth );
  SETARG( XtNsubMenu,		submenu );
  SETARG( XtNmenuPick,		menupick );
  SETARG( XtNcmdPick,		cmdpick );
  SETARG( XtNoverrideRedirect,	submenu | !RatMenuDefaults.permanent );
  SETARG( XtNinput,		TRUE );
  SETARG( XtNborderPixmap,	XtGrayPixmap(XtScreen(toplevel)) );
  SETARG( XtNborderWidth,	RatMenuDefaults.menu_border_width );
  SETARG( XtNbackground,	RatMenuDefaults.background );
  SETARG( XtNborderColor,	RatMenuDefaults.border_pixel );
  SETARG( XtNgeometry,		NULL ); /* Under no circumstances let user set
					 * the geometry value for menus! */
  SETARG( XtNtranslations,	XtParseTranslationTable( shellactions ) );
  TheMenu = XtCreatePopupShell( rmname, ratMenuShellWidgetClass,
			        toplevel, args, argi );
  
/*--Create the form that occupies the Shell. */

  argi = 0;
  SETARG( XtNdefaultDistance,	RatMenuDefaults.menu_whitespace );
  SETARG( XtNbackground,	RatMenuDefaults.background );
  SETARG( XtNborderColor,	RatMenuDefaults.border_pixel );
  SETARG( XtNtranslations,	XtParseTranslationTable( actions ) );
  DaddyMenu[MenuDepth] = XtCreateManagedWidget( "Form", RMformWidgetClass,
					        TheMenu, args, argi );
  XtSetKeyboardFocus( TheMenu, DaddyMenu[MenuDepth] );

/*--Create the label that goes at the top of the menu; he is inverse video
 *  of whatever video we are using; he may be reverse-reverse-vide. */

  if (((!submenu && RatMenuDefaults.want_menu_labels) ||
       ( submenu && RatMenuDefaults.want_submenu_labels)) &&
      label != NULL && label[0] != '\000') {
    argi = 0;
    SETARG( XtNlabel,		label );
    SETARG( XtNcursor,		None );
    SETARG( XtNborderWidth,	RatMenuDefaults.item_border_width );
    SETARG( XtNbackground,	term->screen.foreground );
    SETARG( XtNborderColor,	(RatMenuDefaults.border_pixel ==
				   RatMenuDefaults.background
				 ? RatMenuDefaults.foreground
				 : RatMenuDefaults.border_pixel ) );
    SETARG( XtNforeground,	RatMenuDefaults.background );
    SETARG( XtNfont,		RatMenuDefaults.font );
    SETARG( XtNtranslations,	XtParseTranslationTable( actions ) );
    ChildMenu[MenuDepth] =
      XtCreateManagedWidget( "Label", ratMenuLabelWidgetClass,
			     DaddyMenu[MenuDepth], args, argi );
  } else {
    ChildMenu[MenuDepth] = NULL;
  }

  return TheMenu;

} /* StartAnyRatMenu */

\f


Widget StartRatMenu( rmname, label, toplevel, cursor )
     String	rmname;		/* resource name of the menu to create */
     String	label;		/* label for menu when visible to the user */
     Widget	toplevel;	/* the true top-level shell of  application */
     Cursor	cursor;		/* None or the cursor to use within the menu */
/******************************************************************************
* StartRatMenu - Begin a new Rational Menu.  Create the outermost Popup shell
* and set the menu label.  Set up for adding items to the menu.
******************************************************************************/
{

/*--Check MenuDepth */

  if (MenuDepth >= 0) { fprintf( stderr, "ratmenu: Menu already started?\n" );
			exit(1); }

/*--Set up action tables we will need. */

  if (!ActionsRegistered) {
    XtActionsRec RatActions[20];
    int		 rai;
    ActionsRegistered = TRUE;
    rai = 0;
    RatActions[rai].string = "RatMenuPopdown";
    RatActions[rai].proc   = (XtActionProc)PopdownRatMenu;
    rai++;
    RatActions[rai].string = "RatMenuPopup";
    RatActions[rai].proc   = (XtActionProc)PopupRatMenu;
    rai++;
    RatActions[rai].string = "RatMenuPopdownChain";
    RatActions[rai].proc   = (XtActionProc)PopdownRatMenuChain;
    rai++;
    RatActions[rai].string = "RatMenuUnmapped";
    RatActions[rai].proc   = (XtActionProc)UnmappedRatMenu;
    rai++;
    RatActions[rai].string = "noop";
    RatActions[rai].proc   = (XtActionProc)NoOp;
    rai++;
    RatActions[rai].string = "RatMenuNotify";
    RatActions[rai].proc   = (XtActionProc)NotifyRatMenu;
    rai++;
    RatActions[rai].string = "RatMenuDropSubs";
    RatActions[rai].proc   = (XtActionProc)DropSubsRatMenu;
    rai++;
    RatActions[rai].string = "RatMenuSubup";
    RatActions[rai].proc   = (XtActionProc)SubupRatMenu;
    rai++;
    RatActions[rai].string = "RatMenuSubdown";
    RatActions[rai].proc   = (XtActionProc)SubdownRatMenu;
    rai++;
    RatActions[rai].string = "RatMenuKey";
    RatActions[rai].proc   = (XtActionProc)KeyRatMenu;
    rai++;
    XtAddActions( &RatActions[0], rai );
  }

/*--Set up the menu. */

  return StartAnyRatMenu( rmname, label, toplevel, cursor,
			  (KeySym)0, (KeySym)0, FALSE);

} /* StartRatMenu */

\f


Widget StartRatSubMenu( itemname, menurmname, menulabel,
		        menupick, cmdpick, cursor )
     String	itemname;   /* name of the item to go in outer menu */
     String	menurmname; /* resource name of the menu to create */
     String	menulabel;  /* label to go at top of the menu */
     KeySym	menupick;   /* KeySym used when a sub-item is picked */
     KeySym	cmdpick;    /* KeySym used when we are picked; or NoSymbol */
     Cursor	cursor;	    /* None or the cursor to use within the menu */
/******************************************************************************
* StartRatSubMenu - Begin a new Rational SubMenu.  Create the Popup shell
* and set the menu label.  Set up for adding items to the menu.
******************************************************************************/
{
  Widget	Daddy = DaddyMenu[MenuDepth];
  Widget	Child = ChildMenu[MenuDepth];
  Widget	SubMenu;
  int		argi;
  Arg		args[20];
  Widget        item;
  static char item_translat[] =
    "<EnterWindow>:highlight() RatMenuDropSubs()\n\
     <LeaveWindow>:unhighlight()";

/*--Check MenuDepth */

  if (MenuDepth < 0) { fprintf( stderr, "ratmenu: No menu started?\n" );
		       exit(1); }

/*--If we are a command pickable submenu then create a RatItem; otherwise
 *  just create a more or less normal label. */

  if (cmdpick != NoSymbol) {
    AddRatItem( menurmname, itemname, cmdpick );
    item = ChildMenu[MenuDepth];

/*--Set up the textual menu entry; it just has text and it has few actions
 *  which completely replaces the standard set (not just overrides a few). */

  } else {
    argi = 0;
    if (Child) {
      SETARG( XtNfromVert,	Child );
      SETARG( XtNvertDistance,	RatMenuDefaults.item_pad );
    }
    SETARG( XtNcursor,		None );
    SETARG( XtNlabel,		itemname );
    SETARG( XtNtranslations,	XtParseTranslationTable( item_translat ) );
    SETARG( XtNborderWidth,	RatMenuDefaults.item_border_width );
    SETARG( XtNbackground,	RatMenuDefaults.background );
    SETARG( XtNborderColor,	RatMenuDefaults.border_pixel );
    SETARG( XtNforeground,	RatMenuDefaults.foreground );
    SETARG( XtNfont,		RatMenuDefaults.font );
    SETARG( XtNjustify,		XtJustifyLeft );
    item = XtCreateManagedWidget( menurmname, RMcommandWidgetClass,
				  Daddy, args, argi );
    ChildMenu[MenuDepth] = item;
  }

/*--Create the submenu. */

  if (cursor == None) {
    SubMenu =
      StartAnyRatMenu( menurmname, menulabel, Daddy->core.parent, MainCursor,
		       menupick, cmdpick, TRUE );
  } else {
    SubMenu =
      StartAnyRatMenu( menurmname, menulabel, Daddy->core.parent, cursor,
		       menupick, cmdpick, TRUE );
  }

/*--Set up the arrow menu entry; it pops up the SubMenu when it is entered.
 *  Once again we completely replace the translation table for this widget. */

  argi = 0;
  if (Child) {
    SETARG( XtNfromVert,	Child );
    SETARG( XtNvertDistance,	RatMenuDefaults.item_pad );
  }
  SETARG(   XtNfromHoriz,	item );
  SETARG(   XtNhorizDistance,	RatMenuDefaults.sub_horiz_distance );
  SETARG(   XtNsubMenu,		SubMenu );
  SETARG(   XtNcursor,		None );
  SETARG(   XtNlabel,		"=>" );
  SETARG(   XtNborderWidth,	RatMenuDefaults.sub_border_width );
  SETARG(   XtNbackground,	RatMenuDefaults.background );
  SETARG(   XtNborderColor,	RatMenuDefaults.border_pixel );
  SETARG(   XtNforeground,	RatMenuDefaults.foreground );
  SETARG(   XtNfont,		RatMenuDefaults.font );
  (void) XtCreateManagedWidget( itemname, ratMenuSubWidgetClass,
			        Daddy, args, argi );

/*--Return the SubMenu to our caller. */

  return SubMenu;

} /* StartRatSubMenu */

\f


void AddRatItem( rmname, label, menupick )
     String	rmname;		/* resource name of the item to add */
     String	label;		/* Label to put on the menu */
     KeySym	menupick;	/* MenuPick character when item picked */
/******************************************************************************
* AddRatItem - Called to add a selectable item to a menu or a submenu.
* The item will be labelled with Name and it will send a menupick character
* when selected by any Btn up.
******************************************************************************/
{
  int		argi;
  Arg		args[20];
  int		cbi;
  XtCallbackRec Callback[2];
  Widget        item;
  static char PickTransl[] =
    "<EnterWindow>:highlight() RatMenuDropSubs()\n\
     <Btn1Down>:   noop()\n\
     <Btn1Up>:     RatMenuNotify()\n\
     <Btn3Down>:   noop()\n\
     <Btn3Up>:     RatMenuNotify()\n\
     <Btn2Down>:   noop()\n\
     <Btn2Up>:     RatMenuNotify() RatMenuPopdownChain()";


  cbi = -1;
  CALLBACK( PickRatMenuCallback, menupick );
  CALLBACK( NULL,		 NULL );
  argi = 0;
  if (ChildMenu[MenuDepth]) {
    SETARG( XtNfromVert,	ChildMenu[MenuDepth] );
    SETARG( XtNvertDistance,	RatMenuDefaults.item_pad );
  }
  SETARG(   XtNcursor,		None );
  SETARG(   XtNcallback,	&Callback[0] );
  SETARG(   XtNlabel,		label );
  SETARG(   XtNborderWidth,	RatMenuDefaults.item_border_width );
  SETARG(   XtNbackground,	RatMenuDefaults.background );
  SETARG(   XtNborderColor,	RatMenuDefaults.border_pixel );
  SETARG(   XtNforeground,	RatMenuDefaults.foreground );
  SETARG(   XtNfont,		RatMenuDefaults.font );
  SETARG(   XtNjustify,		XtJustifyLeft );
  item = XtCreateManagedWidget( rmname, RMcommandWidgetClass,
			        DaddyMenu[MenuDepth], args, argi );
  ChildMenu[MenuDepth] = item;
  XtOverrideTranslations( item, XtParseTranslationTable( PickTransl ) );

} /* AddRatItem */

\f


void FinishAnyRatMenu()
/******************************************************************************
* FinishRatMenu - Finish up a Rational Menu.  Tidy up.
******************************************************************************/
{

/*--We are now one less in MenuDepth. */

  MenuDepth--;

} /* FinishAnyRatMenu */

\f


void FinishRatMenu()
/******************************************************************************
* FinishRatMenu - Finish up the outermost Rational Menu.  Tidy up.
******************************************************************************/
{

/*--Check MenuDepth */

  if (MenuDepth != 0) { fprintf( stderr, "ratmenu: MenuDepth != 0?\n" );
			exit(1); }
  for (MenuDepth = MAXMENUDEPTH; MenuDepth > 0; ) {
      DaddyMenu[--MenuDepth] = NULL;
  }

/*--Finish up the menu. */

  FinishAnyRatMenu();

} /* FinishRatMenu */

\f


void FinishRatSubMenu()
/******************************************************************************
* FinishRatSubMenu - Finish up any Rational SubMenu.  Tidy up.
******************************************************************************/
{

/*--Check MenuDepth */

  if (MenuDepth <= 0) { fprintf( stderr, "ratmenu: MenuDepth <= 0?\n" );
			exit(1); }

/*--Finish up the menu. */

  FinishAnyRatMenu();

} /* FinishRatSubMenu */

\f


void PopupRatMenu( w, event, params, num_params )
     Widget		w;
     XEvent         *event;
     String	       *params;
     Cardinal       *num_params;
/******************************************************************************
* PopupRatMenu - Called to popup any RatMenuShell.
* Allowed params:
*	"DontMove" - don't move the menu from whereever it is
*	"DontWarp" - don't warp pointer to the menu
* Menus now have permanence.
* - event != NULL - we are doing a normal button loaded popup; however, if
*		a main menu is mapped then don't move it, just raise it up.
* - event == NULL - we are forcing the menu to be mapped; check params for
*		RatMenuDontWarp/Move, they tell us to move/not-move the menu
*		and to warp/not-warp the pointer to it afterwards.
******************************************************************************/
{
    RatMenuShellWidget rw = (RatMenuShellWidget)w;
    Boolean	    just_realized;
    Position          x,y;
    Dimension	    wid,hei;
    Position	    dx,dy;
    Position	    x_root, y_root;
    Boolean	    warp = RatMenuDefaults.warp;
    Boolean	    move = TRUE;
    Boolean	    permanent = FALSE;
    int		    i;

/*--Are we really what we think we are? */

    if (!XtIsSubclass( w, ratMenuShellWidgetClass )) {
	fprintf( stderr, "PopupRatMenu: widget not RatMenuShell [%s]\n",
		 XtClass(w)->core_class.class_name );
	exit(1);
    }

/*--Make sure that we are realized and have a window and such. */

    if (!XtIsRealized(w)) {
	XtRealizeWidget( w );
	just_realized = TRUE;
	if (!rw->ratmenu.submenu) {
	    (void)XGetTransientForHint( XtDisplay(rw), XtWindow(rw),
				       &RatMenuTransientFor );
	    if (RatMenuDefaults.permanent) {
		XDeleteProperty( XtDisplay(rw), XtWindow(rw),
				XA_WM_TRANSIENT_FOR );
	    }
	}
    } else {
	just_realized = FALSE;
    }

/*--Check our params.  Don't warp if told not to.  Don't move if told not to
 *  or if we are mapped and a main menu. */

    if (params != NULL) {
	for (i = (int)*num_params-1; i >= 0; i--) {
	    if        (strcmp( params[i], RatMenuDontMove ) == 0) {
		move = FALSE;
	    } else if (strcmp( params[i], RatMenuDontWarp ) == 0) {
		warp = FALSE;
	    }
	}
    }
    if (!rw->ratmenu.submenu && RatMenuDefaults.permanent) {
	permanent = TRUE;
    }
    if (!rw->ratmenu.submenu && rw->ratmenu.mapped) {
	move = FALSE;
    }

/*--Figure the x/y coords for popup menu.  We don't want the cursor to move
 *  if it doesn't have to but we also don't want the menu to be half on/off
 *  the screen.  Try to place the menu so that the cursor is "just" inside
 *  the upper left corner. */

    if (permanent) {
#ifndef XTTRANSLATECOORDS_FIXED
	/* XtTranslateCoords has bugs when the user uses uwm to move
	 * menus; use the XLib function. */
	int	tmpx,tmpy;
	Window	tmpchild;
	/*--Pop him up at the right side of the VT100 widget. */
	XTranslateCoordinates( XtDisplay(term), XtWindow(XtParent(term)),
			      RootWindowOfScreen(XtScreen(term)),
			      0, 0, &tmpx, &tmpy, &tmpchild );
	/*--The term has no border width but the shell surrounding the term
	 *  may well have non-zero border width; adjust for that. */
	x_root = tmpx + term->core.parent->core.width +
	                term->core.parent->core.border_width;
	y_root = tmpy - term->core.parent->core.border_width;
	x_root += XPOPUP;
	y_root += YPOPUP;
#else
	/*--Pop him up at the right side of the VT100 widget. */
	XtTranslateCoords( (Widget)term, (Position)0, (Position)0,
			   &x_root, &y_root );
	/*--The term has no border width but the shell surrounding the term
	 *  may well have non-zero border width; adjust for that. */
	x_root += term->core.parent->core.width +
	          term->core.parent->core.border_width;
	y_root += -term->core.parent->core.border_width;
	x_root += XPOPUP;
	y_root += YPOPUP;
#endif
    } else {
	switch (event->type) {
	  case ButtonPress :
	  case ButtonRelease :
	    x_root = event->xbutton.x_root;
	    y_root = event->xbutton.y_root;
	    break;
	  case EnterNotify :
	  case LeaveNotify :
	    {
		int	tmpx,tmpy;
		Window	tmpchild;
		Window    win;
		Widget	sm;
		win = event->xcrossing.window;
		sm = XtWindowToWidget( XtDisplay(term), win );
		/* XtTranslateCoords has bugs when the user uses uwm to move
		 * menus; use the XLib function. */
		XTranslateCoordinates( XtDisplay(term), win,
				       RootWindowOfScreen(XtScreen(term)),
				       (int) -sm->core.border_width,
				       (int) -sm->core.border_width,
				       &tmpx, &tmpy, &tmpchild );
		x_root = tmpx + XPOPUP;
		y_root = tmpy + YPOPUP;
		if (!rw->ratmenu.submenu) { move = TRUE; }
	    }
	    break;
	  default:
	    fprintf( stderr,
		     "PopupRatMenu: event not Button or Enter/Leave?\n" );
	    exit(1);
	}
    }
    x = x_root;
    dx = 0;
    if (x > XPOPUP) { x -= XPOPUP; dx = XPOPUP; }
    y = y_root;
    dy = 0;
    if (y > YPOPUP) { y -= YPOPUP; dy = YPOPUP; }

/*--Don't let popup fall off screen */

    wid = rw->core.width  + (rw->core.border_width << 1);
    hei = rw->core.height + (rw->core.border_width << 1); 
    if (x+wid > XtScreen(rw)->width)  { x = XtScreen(rw)->width  - wid; }
    if (y+hei > XtScreen(rw)->height) { y = XtScreen(rw)->height - hei; }

/*--Get the menu positioned correctly. */

    if (just_realized) {
	XSizeHints	hints;
	if (XGetNormalHints( XtDisplay(w), XtWindow(w), &hints )) {
	    hints.flags |= USPosition;
	} else {
	    hints.flags  = USPosition;
	}
	hints.x = x;
	hints.y = y;
	XSetNormalHints( XtDisplay(w), XtWindow(w), &hints );
    }
    if (move) {
	XtMoveWidget( rw, x, y );
    }

/*--Pop the menu and record the fact. */

    XtPopup( rw, XtGrabNone );
    {   XWindowAttributes wa;
	do {
	    XGetWindowAttributes( XtDisplay(rw), XtWindow(rw), &wa );
	} while (wa.map_state == IsUnmapped);
    }
    XUngrabPointer( XtDisplay(rw), CurrentTime );
    rw->ratmenu.mapped = TRUE;		/* In our opinion; he is mapped. */
    rw->ratmenu.permanent = permanent;

/*--Set up the MenuDepth and the DaddyMenu entry so that we know who's up
 *  and thus we know what to transmit when the menu pick is done. */

    if (!rw->ratmenu.submenu) {
	for (wid = MAXMENUDEPTH-1; wid > 0; wid--) { DaddyMenu[wid] = NULL; }
    }
    MenuDepth = rw->ratmenu.menu_depth;
    DaddyMenu[MenuDepth] = w;
    MenuPick[MenuDepth] = rw->ratmenu.menu_pick;

/*--Move pointer to be at start of menu. */

    x += dx;
    y += dy;
    if (warp) {
	XWarpPointer( XtDisplay(rw), None,
		      DefaultRootWindow(XtDisplay(rw)), 0, 0, 0, 0, x, y );
    }

} /* PopupRatMenu */

\f


/*ARGSUSED*/
void PopdownRatMenu( w, event, params, num_params )
     Widget		w;
     XEvent	       *event;
     String	       *params;
     Cardinal       *num_params;
/******************************************************************************
* PopdownRatMenu - Called to popdown any RatMenuShell.
******************************************************************************/
{
  RatMenuShellWidget rw  = (RatMenuShellWidget)w;

/*--Are we really what we think we are? */

  if (!XtIsSubclass( w, ratMenuShellWidgetClass )) {
    fprintf( stderr, "PopdownRatMenu: widget not RatMenuShell [%s]\n",
	     XtClass(w)->core_class.class_name );
    exit(1);
  }

/*--Popdown the menu. */

  if (rw->ratmenu.submenu ||		/* sumenus always go down */
      !rw->ratmenu.permanent ||		/* we weren't permanent anyway */
      !RatMenuDefaults.permanent ||	/* permanent mode is off now */
      !term->misc.rational) {		/* rational mode is off now */
    XtPopdown( w );
    DaddyMenu[rw->ratmenu.menu_depth] = NULL;
    rw->ratmenu.mapped = FALSE;
    rw->ratmenu.permanent = FALSE;
  }

} /* PopdownRatMenu */

\f


Boolean FoundALeave;

Boolean LookingForLeaves( dpy, event, window )
     Display	*dpy;
     XEvent	*event;
     char	*window;
/******************************************************************************
* Search the entire event queue for a Leave that leaves the window in question.
* Never actually remove anything; we don't want to change the order of events
* we just want to know "Am I about to Leave?".
******************************************************************************/
{
    if (FoundALeave) { /* optimize a speed-up */ }
    else if (event->xany.type == LeaveNotify &&
	     event->xcrossing.window == *(Window*)window) {
	/*--The answer is yes. */
	FoundALeave = TRUE;
    }
    return FALSE;
} /* LookingForLeaves */

\f


Boolean SimultaneousLeave( window )
     Window	window;
/******************************************************************************
* Try to reduce submenu flicker when the user drags the mouse quickly over
* a bunch of RatMenuSub's (ie. drags faster than we can respond; too bad
* there isn't a way to do a passive grab on the pointer on EnterNotify).
* Sync with the server.  If there is now a Leave that corresponds to our
* Enter (same window) then do nothing.
******************************************************************************/
{
    XEvent	ev;

    XSync( XtDisplay(term), FALSE );
    FoundALeave = FALSE;
    (void)XCheckIfEvent( XtDisplay(term), &ev,
			 LookingForLeaves, (char*)&window );
    return FoundALeave;

} /* SimultaneousLeave */

\f


/*ARGSUSED*/
void SubupRatMenu( w, event, params, num_params )
     Widget		w;
     XEvent	       *event;
     String	       *params;
     Cardinal       *num_params;
/******************************************************************************
* SubupRatMenu - Called by the translation manager to pop up a submenu.
******************************************************************************/
{
    register Widget  SubMenu;

/*--Are we really what we think we are? */

    if (!XtIsSubclass( w, ratMenuSubWidgetClass )) {
	fprintf( stderr, "SubupRatMenu: widget not RatMenuSub [%s]\n",
		 XtClass(w)->core_class.class_name );
	exit(1);
    }

/*--For all parents up to the main menu; do they all think that they are
 *  mapped?  (The server may think they are mapped (and thus give them an
 *  enter event) even though we think they are unmapped.) */

    SubMenu = w;
    while ((SubMenu = XtParent(SubMenu)) != NULL) {
	if (XtIsSubclass( SubMenu, ratMenuShellWidgetClass )) {
	    if (!((RatMenuShellWidget)SubMenu)->ratmenu.mapped) {
		return;
	    }
	    if (!((RatMenuShellWidget)SubMenu)->ratmenu.submenu) { break; }
	}
    }

/*--Ignore Enters where we are just somebody's ancestor getting a Virtual
 *  entry. */

    if (event && event->xany.type == EnterNotify) {
	if (event->xcrossing.detail == NotifyVirtual ||
	    event->xcrossing.detail == NotifyNonlinearVirtual) {
	    return;
	}

/*--If we are an Enter event then see if there is already a Leave event
 *  around.  The Toolkit doesn't compress Enter/Leave pairs unless they
 *  are exactly adjacent in the queue.  Not much chance of that when either
 *  a) there are multiple ancestors/inferiors getting Virtual/Nonlinear
 *  enter/leaves along with us or b) when pointer motion is also being
 *  reported. */


	if (SimultaneousLeave( event->xcrossing.window )) {
	    return;
	}
    }

/*--Redo the coordinates as we enter menu arrows. */

    if (event->type == EnterNotify || event->type == LeaveNotify) {
	Position x,y;
	XtTranslateCoords( w,
			   XPOPUP-w->core.border_width,
			   YPOPUP-w->core.border_width,
			   &x,
			   &y );
	((XCrossingEvent*)event)->x_root = x;
	((XCrossingEvent*)event)->y_root = y;
    }

/*--Get the SubMenu and drop anything that is at or below its level. */

    SubMenu = ((RatMenuSubWidget)w)->ratmenusub.submenu;
    DropSubsRatMenu( SubMenu, (XEvent*)NULL, (char**)NULL, (Cardinal*)NULL );

/*--Pop up the sub menu. */

    if (!((RatMenuShellWidget)SubMenu)->ratmenu.mapped) {
	PopupRatMenu( SubMenu, event, params, num_params );
    }

} /* SubupRatMenu */

\f


#ifdef RIOS

/*ARGSUSED*/
static void AixBugFix( w )
     Widget		w;
/******************************************************************************
* AixBugFix - Called when a submenu (or a main menu) is <Unmap>ed.  We go
* through all managed children and we do an Unhighlight on them if they are
* an RMCommand type of object.  This fixes a bug in the AIX X server where that
* server forgets to send <Leave> events to subwindows of a window that gets
* unmapped.
******************************************************************************/
{
    WidgetList	children;
    Cardinal	num_children;
    Cardinal	i;

    if (!XtIsComposite(w)) { return; }
    children = ((CompositeWidget)w)->composite.children;
    num_children = ((CompositeWidget)w)->composite.num_children;
    for (i = 0; i < num_children; ++i) {
	if (XtIsSubclass( children[i], RMformWidgetClass )) {
	    AixBugFix( children[i] );
	} else if (XtIsSubclass( children[i], RMcommandWidgetClass )) {
	    extern void AixBugUnhighlight();
	    AixBugUnhighlight( children[i] );
	}
    }

} /* AixBugFix */

#endif /* RIOS */

\f


/*ARGSUSED*/
void SubdownRatMenu( w, event, params, num_params )
     Widget		w;
     XEvent	       *event;
     String	       *params;
     Cardinal       *num_params;
/******************************************************************************
* SubdownRatMenu - Called by the translation manager to pop down a submenu.
******************************************************************************/
{
  PopdownRatMenu( ((RatMenuSubWidget)w)->ratmenusub.submenu,
		  event, params, num_params );
} /* SubdownRatMenu */

\f


/*ARGSUSED*/
static void DropSubsRatMenu( w, event, params, num_params )
     Widget		w;
     XEvent         *event;
     String	       *params;
     Cardinal       *num_params;
/******************************************************************************
* DropSubsRatMenu - Called when we enter a main menu.  Remove any mapped
* submenus.
******************************************************************************/
{
    XEvent	 ev;
    int		 i;
    int		 menu_depth;

/*--Are we really what we think we are? */

  if (!XtIsSubclass( w, ratMenuShellWidgetClass )) {
    Widget w2 = XtParent(w);
    while (w2 != NULL && !XtIsSubclass( w2, ratMenuShellWidgetClass )) {
      w2 = XtParent(w2);
    }
    if (w2 == NULL) {
      fprintf( stderr, "DropSubsRatMenu: widget not RatMenuShell [%s]\n",
	       XtClass(w)->core_class.class_name );
      exit(1);
    }
    w = w2;
  }

/*--Drop any submenus that are at or below our level and which aren't us. */

  menu_depth = ((RatMenuShellWidget)w)->ratmenu.menu_depth;
  MenuDepth = menu_depth;
  for ( i = MAXMENUDEPTH-1; i > menu_depth; i--) {
    if (DaddyMenu[i] != NULL) {
      PopdownRatMenu( DaddyMenu[i], event, params, num_params );
    }
  }
  if (DaddyMenu[menu_depth] != NULL &&
      DaddyMenu[menu_depth] != w) {
    PopdownRatMenu( DaddyMenu[menu_depth], event, params, num_params );
    MenuDepth--;
  }

} /* DropSubsRatMenu */

\f


/*ARGSUSED*/
void PopdownRatMenuChain( w, event, params, num_params )
     Widget	     w;
     XEvent         *event;
     String	    *params;
     Cardinal       *num_params;
/******************************************************************************
* PopdownRatMenuChain - Called to popdown any RatMenuShell and all of its
* parents.
******************************************************************************/
{
  int	menu_depth;

/*--If we are permanent and if the event is a button release then igore it. */

  if (RatMenuDefaults.permanent && term->misc.rational &&
      (event == NULL || event->xany.type == ButtonRelease)) {
    return;
  }

/*--Recursively Popdown every shell until we get to a non-submenu. */

  for (menu_depth = 0; menu_depth < MAXMENUDEPTH; menu_depth++){
    if (DaddyMenu[menu_depth] != NULL) {
      PopdownRatMenu( DaddyMenu[menu_depth], event, params, num_params );
    }
  }

} /* PopdownRatMenuChain */

\f


/* ARGSUSED */
static void NotifyRatMenu( w, event, params, num_params )
     Widget    w;
     XEvent   *event;
     String   *params;		/* unused */
     Cardinal *num_params;	/* unused */
{

  if (event->type == EnterNotify ||
      ((event->type == ButtonPress || event->type == ButtonRelease) &&
       ((XButtonEvent*)event)->button == Button3)) {
    XtCallCallbacks( w, XtNcallback, (caddr_t)TRUE );
  } else {
    XtCallCallbacks( w, XtNcallback, (caddr_t) FALSE );
  }
  if (event->xany.type == ButtonPress) {
      XAllowEvents( XtDisplay(w), SyncPointer, event->xbutton.time );
  } else {
      XAllowEvents( XtDisplay(w), AsyncPointer, event->xbutton.time );
  }

} /* NotifyRatMenu */

\f


/* ARGSUSED */
static void UnmappedRatMenu( w, event, params, num_params )
     Widget    w;
     XEvent   *event;
     String   *params;		/* unused */
     Cardinal *num_params;	/* unused */
/******************************************************************************
* If we get an UNMAP notificiation then either the menu was in its popup mode
* (!permanent) or else the user must have had his window mananager unmap the
* Rational Menu (permanent).  In the latter case we will reset our permanent
* flag.
******************************************************************************/
{

#ifdef RIOS
    AixBugFix( w );
#endif
    if (w != RatMenu || !RatMenuDefaults.permanent) { return; }
    RatMenuDefaults.permanent = FALSE;
    RatMenuPermanent( FALSE );

} /* UnmappedRatMenu */

\f


/*ARGSUSED*/
static void KeyRatMenu( w, event, params, num_params )
     Widget		w;
     XEvent         *event;
     String	       *params;
     Cardinal       *num_params;
/******************************************************************************
* Send keys back to RXI when they are struck over a menu.
******************************************************************************/
{

  extern void HandleKeyPressed();
  HandleKeyPressed( w, event, params, num_params );
  XAllowEvents( XtDisplay(w), AsyncKeyboard, event->xkey.time );

} /* KeyRatMenu */

\f


/*ARGSUSED*/
static void NoOp( w, event, params, num_params )
     Widget		w;
     XEvent         *event;
     String	       *params;
     Cardinal       *num_params;
/******************************************************************************
* No-Operation for Translation lists.
******************************************************************************/
{

  /* translation manager has a bug whereby you can't override some action with
   * an empty action so we must have a noop action to help us out. */
  if (params != NULL && *num_params > 0) {
    fprintf( stderr, "NoOp: [%s]\n", params[0] );
  }
  if (event->xany.type == ButtonPress) {
      XAllowEvents( XtDisplay(w), SyncPointer, event->xbutton.time );
  }

} /* NoOp */

\f


/*ARGSUSED*/
static PickRatMenuCallback( w, client_data, call_data )
     Widget	 w;
     char	*client_data;
     caddr_t	 call_data;
/******************************************************************************
* PickRatMenuCallback - Called when Button2 goes up over a menu item or when
* Button1 goes down over a menu item.  Transmit the menu pick to the pty.
******************************************************************************/
{
    char		 buf[MAXMENUDEPTH*4+16];
    register char	*bp = &buf[0];
    register KeySym	 ks;
    register int	 i;

/*--Stuff the current menu_pick into the MenuPick list and then transmit. */

    MenuDepth++;
    MenuPick[MenuDepth] = (KeySym)client_data;

/*--See if we are Button3; we do a Prompt_For in that case */

    if (call_data) {			/* Button3 - Do MenuPick MenuPick */
	*bp++ = MOUSE_PREFIX;
	*bp++ = MENU_PICK;
	*bp++ = MOUSE_PREFIX;
	*bp++ = MENU_PICK;
    }

/*--Check the last MenuPick key.  See if it is a special key. */

    switch (MenuPick[MenuDepth]) {

      case MENU_JOB_KILL :
      case MENU_JOB_DISABLE :
      case MENU_DEBUG_STOP :
	ks = MenuPick[MenuDepth];
	DoRChar( bp, ks );
	break;

/*--Normal picks come here. */

      default :
	*bp++ = (char)MOUSE_PREFIX;	/* Any Button - Do MenuPick <btns> */
	*bp++ = (char)MENU_PICK;
	for (i = 1; i <= MenuDepth; i++) {
	    ks = MenuPick[i];
	    DoRChar( bp, ks );
	}
	break;
    }

    MenuDepth--;
    (void)v_write( term->screen.respond, buf, bp-buf );

} /* PickRatMenuCallback */

\f


/* print out the name of a widget; NOTE: no \n printed so printout is not
 * flushed until our caller does one. */

void spit(w)
     Widget	w;
{
    if (w == NULL) return;
    spit( w->core.parent );
    fprintf( stderr, ".%s", w->core.name );
}


\f


void RatMenuUserItems( user )
     SubMenuRes	*user;
/******************************************************************************
* Starting with these user items and submenus; and they might be empty; add
* item0..item9 to the current (sub)menu.  Then for each menu0..menu9 add a new
* submenu and recurse on that submenu.ratmenu.user as the new user.  This
* instantiates the user menu items.
******************************************************************************/
{
    char 		       *buf;
    int				i;
    RatMenuShellWidget		SubMenu;
    char			SubMenuName[6];
    KeySym			ks;

/*--Loop over all items and sub-menus for this widget.  We install the items
 *  and the sub-menus in order 0..9. */

    strcpy( SubMenuName, "menuN" );
    for (i = 0; i < XtNumber(user->item); i++) {

/*--Check each of the itemN's and see if it gets added.  We add this one if
 *  it exists and if the corresponding menuN entry is empty. */

	if (user->menu[i] == NULL ||
	    user->menu[i][0] == '\000') {		/* no menu entry */

	    if (user->item[i] != NULL &&		/* enter an item */
		user->item[i][0] != '\000') {
		buf = (char*)XtMalloc( strlen(user->item[i] ) + 2 );
		buf[0] = '*';	/* All user items are pickable; mark with * */
		strcpy( &buf[1], user->item[i] );
		AddRatItem( buf, buf, (KeySym)(XK_0+i) );
	    }

/*--We have a menuN entry.  See if it is itself an item or whether it is just
 *  a sub-menu. */

	} else {
	    SubMenuName[4] = '0' + i;

/*--Selectable sub-menus have names that start with '*' and non-selectable 
 *  names start with ' '. */

	    if (user->item[i] != NULL &&
		user->item[i][0] != '\000') {
		buf = (char*)XtMalloc( strlen(user->item[i] ) + 2 );
		buf[0] = '*';
		strcpy( &buf[1], user->item[i] );
		ks = XStringToKeysym(user->menu[i]);
	    } else {
		buf = (char*)XtMalloc( strlen(user->menu[i] ) + 2 );
		buf[0] = ' ';
		strcpy( &buf[1], user->menu[i] );
		ks = NoSymbol;
	    }

/*--Create the sub-menu and all of it's sub-entries. */

	    SubMenu = (RatMenuShellWidget)
	      StartRatSubMenu( buf, SubMenuName, buf,
			       (KeySym)(XK_0+i), ks, None );
	    RatMenuUserItems( &SubMenu->ratmenu.user );
	    FinishRatSubMenu();
	}
    }

} /* RatMenuUserItems */

\f


KeySym OurStrToKeysym( str )
     char	*str;
/******************************************************************************
* Convert from a string, eg. "XK_A", to a KeySym value.
******************************************************************************/
{

    if (strncmp( str, "XK_", 3 ) == 0) { str += 3; }
    else if (strncmp( str, "MENU_", 5 ) == 0) {
	if (strcmp( str, "MENU_JOB_KILL" ) == 0)    return MENU_JOB_KILL;
	if (strcmp( str, "MENU_JOB_DISABLE" ) == 0) return MENU_JOB_DISABLE;
	if (strcmp( str, "MENU_DEBUG_STOP" ) == 0)  return MENU_DEBUG_STOP;
	if (strcmp( str, "MENU_RETURN" ) == 0)      return MENU_RETURN;
	return NoSymbol;
    }
    return XStringToKeysym( str );

} /* OurStrToKeysym */

\f



typedef struct _Define_Name {
    struct _Define_Name	*Next;
    Boolean		 On;
    char		 Name[1];
} Define_Name_Rec, *Define_Name;

#define Define_Name_NULL	((Define_Name)0)

#define new_Define_Name(Name_Length) \
  ((Define_Name)XtMalloc( (Cardinal)(sizeof(Define_Name_Rec) + Name_Length) ))

\f


void RationalMenuInit( toplevel )
Widget	toplevel;
/******************************************************************************
* Called when the user has requested the Rational Environment menu for the
* first time.  We read the descriptor file, build the menu structure, and
* return to our caller who will do the actual window mapping and such.
******************************************************************************/
{
    Define_Name		 Defines = Define_Name_NULL;
    FILE		*file;
    int			 menu_depth = 0;
    char		 buff[128];
    char		 name[128];
    char		 pick1[128];
    char		 pick2[128];
    int			 val;
    int			 cnt;
    int			 eol;
    char		*ptr;
    int			 On_Depth = 0;
    int			 Off_Depth = 0;

/*--Locate and open the RXI_Env_Menu file.  If we can't find it or can't
 *  open it then create an "empty" menu containing only user menu items. */

    InitRatCursor();
    if (RatMenuDefaults.env_menu_file == NULL) {
	static char Error_Null[] = "No RXI_Env_Menu file specified.";
	fprintf( stderr, "No Rational Environment menu filename resource.\n");
	ptr = Error_Null;
	goto Make_Fake_Menu;
    }

/*--If the Env_Menu file name starts with $ then we look up an environment
 *  variable and stick that in front of the rest of the string.  eg.
 *  $OPENWINHOME/etc/RXI_Env_Menu for instance. */

    if (RatMenuDefaults.env_menu_file[0] == '$') {
	char	 real[MAXPATHLEN];
	char	*openwinhome;
	int	 offset;
	extern char *getenv();

/*--Find the end of the $foo variable name, copy the name to the real
 *  buffer, terminate it in null, and look it up in the environment. */

	openwinhome = strpbrk( RatMenuDefaults.env_menu_file + 1,
			       "/." );
	if (openwinhome != NULL) {
	    offset = (int)(openwinhome - (RatMenuDefaults.env_menu_file + 1));
	} else {
	    offset = strlen( RatMenuDefaults.env_menu_file + 1 );
	}
	(void) strncpy( real, RatMenuDefaults.env_menu_file + 1, offset );
	real[offset] = '\000';
	openwinhome = getenv( real );
	if (openwinhome == NULL) {
	    fprintf( stderr,
		     "Environment variable %s is not defined?\n", real );
	    openwinhome = "/usr/openwin";
	}

/*--Concatenate the $foo variable and the remainder of the file name.  Then 
 *  try to open the file. */

	(void) strcpy( real, openwinhome );
	(void) strcat( real, RatMenuDefaults.env_menu_file + 1 + offset);
	file = fopen( real, "r" );
    } else {
	file = fopen( RatMenuDefaults.env_menu_file, "r" );
    }
    if (file == (FILE*)NULL) {
	static char Error_Item[] = "Could not find RXI_Env_Menu?";

/*--Open failed.  Tell the user and create an empty menu. */

	fprintf( stderr, "Could not open the RXI_Env_Menu file: %s\n",
		 RatMenuDefaults.env_menu_file );
	fprintf( stderr, "No Rational Environment menus will be available.\n");
	perror( "fopen error was" );
	ptr = Error_Item;
	goto Make_Fake_Menu;
    }

/*--Loop reading from the file until we hit EOF or until we decide to give
 *  up for some reason. */

    while (fgets( buff, sizeof(buff), file ) != NULL && buff[0] != '\000') {

/*--Skip any leading whitespace on the line (so we can tell the difference
 *  between empty lines and lines that have uninterpretable garbage.
 *  Lines beginning with / or * are comment lines. */

	ptr = &buff[0];
	while (*ptr != '\000' && *ptr <= ' ') { ++ptr; }
	if (*ptr == '\000' || *ptr == '*' || *ptr == '/') { continue; }

/*--Watch out for #if FOO lines. */

	if (*ptr == '#' || Off_Depth > 0) {
	    cnt = sscanf( ptr, "#if %s", name );
	    if (cnt == 1) {
		Define_Name		N;
		if (Off_Depth > 0) { ++Off_Depth; continue; }
		for (N = Defines; N != Define_Name_NULL; N = N->Next) {
		    if (strcmp( &N->Name[0], &name[0] ) == 0) { break; }
		}
		if (N == Define_Name_NULL) {
		    fprintf( stderr, "Don't recognize #if %s\n", name );
		    goto Bad_Line;
		}
		if (N->On) { ++On_Depth; continue; }
		++Off_Depth;
		continue;
	    }

/*--Watch out for #endif lines. */

	    cnt = sscanf( ptr, "#endi%[f]", name );
	    if (cnt == 1) {
		if (Off_Depth) { --Off_Depth; continue; }
		if (On_Depth)  { --On_Depth;  continue; }
		fprintf( stderr, "Found #endif with no matching #if?\n" );
		goto Bad_Line;
	    }

/*--If we are in a turned-off #if then don't check any further. */

	    if (Off_Depth > 0) { continue; }

/*--Watch out for #define lines. */

	    cnt = sscanf( ptr, "#define %s %d", &name[0], &val );
	    if (cnt == 2) {
		Define_Name		N;
		N = new_Define_Name( strlen(name) );
		strcpy( &N->Name[0], &name[0] );
		N->On = val != 0;
		N->Next = Defines;
		Defines = N;
		continue;
	    }
	}

/*--Watch for the line that begins the whole menu. */

	if (*ptr == 'B') {
	    cnt = sscanf( ptr, "BEGIN_MAIN_MENU(\"%[^\"]", name );
	    if (cnt == 1) {
		ptr = XtNewString( name );
		MenuDepth = -1;
		RatMenu = StartRatMenu( "env", ptr, toplevel,
				        Menu_Default.menuCursor );
		RatMenuUserItems(&((RatMenuShellWidget)RatMenu)->ratmenu.user);
		++menu_depth;
		continue;
	    }
	}

/*--Watch for the line that begins a simple sub-menu. */

	if (*ptr == 'S') {
	    cnt = sscanf( ptr,
"SUBM ( \"%[^\"]\",\
 %[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_]",
			  name, pick1 );
	    if (cnt == 2) {
		val = OurStrToKeysym( pick1 );
		if (val == NoSymbol) {
		    fprintf( stderr, "XK_ key symbol name invalid: %s", buff );
		    goto Bad_Line;
		}
		ptr = XtNewString( name );
		(void)StartRatSubMenu( ptr, ptr, ptr, (KeySym)val,
				       NoSymbol, None );
		++menu_depth;
		continue;
	    }

/*--Watch for the line that begins an executable sub-menu. */

	    cnt = sscanf( ptr,
"SUBB ( \"%[^\"]\",\
 %[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_],\
 %[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_]",
			  name, pick1, pick2 );
	    if (cnt == 3) {
		val = OurStrToKeysym( pick1 );
		if (val == NoSymbol) {
		    fprintf( stderr, "XK_%s key symbol name invalid: %s",
			     pick1, buff );
		    goto Bad_Line;
		}
		cnt = OurStrToKeysym( pick2 );
		if (val == NoSymbol) {
		    fprintf( stderr, "XK_%s key symbol name invalid: %s",
			     pick2, buff );
		    goto Bad_Line;
		}
		ptr = XtNewString( name );
		(void)StartRatSubMenu( ptr, ptr, ptr,
				       (KeySym)val, (KeySym)cnt, None );
		++menu_depth;
		continue;
	    }
	}

/*--Watch for ITEM lines that indicate items in menus. */

	if (*ptr == 'I') {
	    cnt = sscanf( ptr,
"ITEM ( \"%[^\"]\",\
 %[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_]",
			  name, pick1 );
	    if (cnt == 2) {
		val = OurStrToKeysym( pick1 );
		if (val == NoSymbol) {
		    fprintf( stderr, "XK_%s key symbol name invalid: %s",
			     pick1, buff );
		    goto Bad_Line;
		}
		ptr = XtNewString( name );
		AddRatItem( ptr, ptr, (KeySym)val );
		continue;
	    }
	}

/*--Watch for lines that end sub-menus. */

	if (*ptr == 'E') {
	    cnt = sscanf( ptr, "ENDM %[(] %[)]", pick1, pick2 );
	    if (cnt == 2) {
		if (menu_depth <= 0) {
		    fprintf( stderr,
			     "Found ENDM with no SUBM or SUBB preceeding?\n" );
		    goto Bad_Line;
		}
		--menu_depth;
		FinishRatSubMenu();
		continue;
	    }

/*--Watch for the line that ends the whole menu. */

	    cnt = sscanf( ptr, "END_MAIN_MENU %[(] %[)]", pick1, pick2 );
	    if (cnt == 2) {
		if (menu_depth > 1) { break; }
		if (menu_depth == 0) {
		    fprintf( stderr,
		 "Found END_MAIN_MENU with no BEGIN_MAIN_MENU preceeding?\n" );
		    goto Bad_Line;
		}
		FinishRatMenu();
		menu_depth = 0;
		break;
	    }
	}

/*--Report any lines that don't make sense. */

	fprintf( stderr, "Do not know how to do: %s", buff );
      Bad_Line :
	fprintf( stderr, "In menu file: %s\n", RatMenuDefaults.env_menu_file );
    }

/*--Close the file.  We are done with it. */

    if (menu_depth > 1) {
	fprintf( stderr,  "%d ENDM's are missing?\n", menu_depth-1 );
	fprintf( stderr, "In menu file: %s\n", RatMenuDefaults.env_menu_file );
	for ( ; menu_depth > 1; --menu_depth) { FinishRatSubMenu(); }
    }
    if (menu_depth > 0) {
	FinishRatMenu();
	fprintf( stderr, "Missing END_MAIN_MENU?\n" );
	fprintf( stderr, "In menu file: %s\n", RatMenuDefaults.env_menu_file );
    }
    fclose( file );
    {   Define_Name	N;
	while ((N = Defines) != Define_Name_NULL) {
	    Defines = N->Next;
	    XtFree( N );
	}
    }
    if (RatMenu != 0) {
	return;
    }

/*--We get here if there is some catastrophic error; like the file was empty.*/

    ptr = "RXI_Env_Menu file was empty?";
  Make_Fake_Menu:
    RatMenu = StartRatMenu( "env", "Environment", toplevel,
			    Menu_Default.menuCursor );
    RatMenuUserItems( &((RatMenuShellWidget)RatMenu)->ratmenu.user );
    AddRatItem( ptr, ptr, XK_equal );
    FinishRatMenu();
    return;

} /* RationalMenuInit */