#if ( !defined(lint) && !defined(SABER) )
static char Xrcsid[] = "$XConsortium: SimpleMenu.c,v 1.32 89/12/11 15:01:50 kit Exp $";
#endif 

/*
 * Copyright 1989 Massachusetts Institute of Technology
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of M.I.T. not be used in advertising or
 * publicity pertaining to distribution of the software without specific,
 * written prior permission.  M.I.T. makes no representations about the
 * suitability of this software for any purpose.  It is provided "as is"
 * without express or implied warranty.
 *
 * M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL M.I.T.
 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 
 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/Xos.h>  
#include <X11/Intrinsic.h>
#include <X11/IntrinsicP.h>
#include <X11/StringDefs.h>

#include "EzMenuP.h"
#include <X11/Xaw/SimpleMenu.h>
#include <X11/Xaw/Sme.h>
#include "EzME.h"
#include <X11/Xaw/SmeBSB.h>
#include <X11/Xaw/SmeLine.h>

#include <X11/Xaw/Cardinals.h>

#include <ctype.h>

#define offset(field) XtOffset(EzMenuWidget, ez_menu.field)

static XtResource resources[] = { 
  {XtNmenu,  XtCMenu, XtRString, sizeof(String),
     offset(menu), XtRString, NULL},
};  
#undef offset


/*
 * Semi Public function definitions. 
 */

static void Initialize();
static void Destroy();
static void Realize();
static Boolean SetValues();

#define superclass (&simpleMenuClassRec)
    
EzMenuClassRec ezMenuClassRec = {
  {
    /* superclass         */    (WidgetClass) superclass,
    /* class_name         */    "EzMenu",
    /* size               */    sizeof(EzMenuRec),
    /* class_initialize   */	NULL,
    /* class_part_initialize*/	NULL,
    /* Class init'ed      */	FALSE,
    /* initialize         */    Initialize,
    /* initialize_hook    */	NULL,
    /* realize            */    Realize,
    /* actions            */    NULL,
    /* num_actions        */    0,
    /* resources          */    resources,
    /* resource_count     */	XtNumber(resources),
    /* xrm_class          */    NULLQUARK,
    /* compress_motion    */    TRUE, 
    /* compress_exposure  */    TRUE,
    /* compress_enterleave*/ 	TRUE,
    /* visible_interest   */    FALSE,
    /* destroy            */    Destroy,
    /* resize             */    XtInheritResize,
    /* expose             */    XtInheritExpose,
    /* set_values         */    SetValues,
    /* set_values_hook    */	NULL,
    /* set_values_almost  */	XtInheritSetValuesAlmost,  
    /* get_values_hook    */	NULL,			
    /* accept_focus       */    NULL,
    /* intrinsics version */	XtVersion,
    /* callback offsets   */    NULL,
    /* tm_table		  */    XtInheritTranslations,
    /* query_geometry	  */    XtInheritQueryGeometry,
    /* display_accelerator*/    XtInheritDisplayAccelerator,
    /* extension	  */    NULL
  },{
    /* geometry_manager   */    XtInheritGeometryManager,
    /* change_managed     */    XtInheritChangeManaged,
    /* insert_child	  */	XtInheritInsertChild,
    /* delete_child	  */	XtInheritDeleteChild,
    /* extension	  */    NULL
  },{
    /* Shell extension	  */    NULL
  },{
    /* Override extension */    NULL
  },{
    /* Simple Menu extension*/  NULL
  },{
    /* Ez Menu extension*/  NULL
  }
};

WidgetClass ezMenuWidgetClass = (WidgetClass)&ezMenuClassRec;

/************************************************************
 *
 * Semi-Public Functions.
 *
 ************************************************************/

/*
 * routines to process menus
 */

#define QUOTE '\"'
#define BACKSLASH '\\'

#define SKIPWHITE(cp) while (cp && *cp && isspace(*cp)) cp++
#define FINDPAREN(cp) while (cp && *cp && *cp != '(') cp++
#define FINDQUOTE(cp) while (cp && *cp && *cp != QUOTE) cp++
#define KEY_LEN 32
#define NAME_LEN 256
#define LABEL_LEN 128

/*
 * Parse a main level menu decl. and add all children to that parent
 */

char *
EzMenuParseString(from, to, maxlen)
char *from;
char *to;
int maxlen;
{
#ifdef OLD
  char *oldto = to;
  SKIPWHITE(from);
  if ( from && *from == QUOTE ) {
    maxlen--;
    from++;
    while (*from && *from != QUOTE && maxlen > 1) {
      if ( *from == BACKSLASH) {
	from++;
      }
      *to++ = *from++;
      maxlen--;
    }
    if ( *from == QUOTE ) {
      from++;
    }
  }
  *to = 0;
  return(from);
#else /* NEW: by George Ferguson, ferguson@cs.rochester.edu, 12 Feb 1991.
	 Allows escaped double-quotes (and backslashes) within the string
	 being parsed, since we often need these for action specs. */

  char *oldto = to;
  int escaped = 0;

  SKIPWHITE(from);
  if (from && *from == QUOTE ) {
    maxlen--;
    for (from++; *from && maxlen > 1; from++) {
      if (*from == QUOTE)
        if (escaped) {
          *to++ = QUOTE;
          maxlen--;
          escaped = 0;
        } else
          break;
      else if (*from == BACKSLASH)
        if (escaped) {
          *to++ = BACKSLASH;
          maxlen--;
          escaped = 0;
        } else
          escaped = 1;
      else {
        *to++ = *from;
        maxlen--;
	escaped = 0;
      }
    }
    if ( *from == QUOTE ) {
      from++;
    }
  }
  *to = '\0';
  return(from);
#endif /* OLD/NEW */
}  

char *
EzMenuParseMenu(parent,menu,subMenuOk)
Widget parent;
char *menu;
Bool subMenuOk;
{
  char *thisWidgetName[NAME_LEN];
  char *thisWidgetLabel[LABEL_LEN];

  *thisWidgetName = 0;
  *thisWidgetLabel = 0;
  while(menu && *menu) {
    SKIPWHITE(menu);

    if (!menu || *menu != '(') {
      return( menu );
    }
    else {
      menu++;
    }

    SKIPWHITE(menu);

    if (isalpha(*menu)) {
      char key[KEY_LEN+1];
      char *p = key;
      int keylen = 0;

      while(isalpha(*menu) && keylen < KEY_LEN) {
	*p++ = *menu++;
	keylen++;
      }
      *p = 0;

      if (strcmp(key, "action") == 0 ) {
	char name[NAME_LEN];
	Arg arglist[1];
	Cardinal num_args = 0;

	menu = EzMenuParseString(menu, name, NAME_LEN);

	XtSetArg(arglist[num_args], XtNaction, name); num_args++;
	XtSetValues(parent, arglist, num_args);
      }
      else if (strcmp(key, "blank") == 0 ) {
	char name[NAME_LEN];
	Widget entry;

	*name = 0;
	menu = EzMenuParseString(menu, name, NAME_LEN);
	if ( *name == 0 ) {
	  static int counter = 0;
	  sprintf(name, "blank%d", counter);
	}
	entry = XtCreateManagedWidget(name, smeObjectClass,
				      parent, NULL, ZERO);
      }
      else if (strcmp(key, "line") == 0 ) {
	char name[NAME_LEN];
	Widget entry;

	*name = 0;
	menu = EzMenuParseString(menu, name, NAME_LEN);
	if ( *name == 0 ) {
	  static int counter = 0;
	  sprintf(name, "line%d", counter);
	}
	entry = XtCreateManagedWidget(name, smeLineObjectClass, parent,
				      NULL, ZERO);
      }
      else if (strcmp(key, "item") == 0 || strcmp(key, "menu") == 0 ) {
	char name[NAME_LEN];
	char label[LABEL_LEN];
	Widget entry;
	Bool isMenu;
	extern void MenuSelect();
	Arg arglist[10];
	Cardinal num_args = 0;


	isMenu = (strcmp(key, "menu") == 0);
	*name = 0;

	menu = EzMenuParseString(menu, label, LABEL_LEN);
	menu = EzMenuParseString(menu, name, NAME_LEN);

	if ( *name == 0 ) {
	  static int counter = 0;
	  sprintf(name, "%s%d", key, counter);
	}

	if ( *label == 0 ) {
	  strcpy(label, name);
	}

	if ( isMenu ) {
	  char menu_name[NAME_LEN];
	  Widget menu_entry;

	  sprintf(menu_name,"%s-menu", name);

	  XtSetArg(arglist[num_args], XtNlabel, label); num_args++;

	  menu_entry = XtCreatePopupShell(menu_name, ezMenuWidgetClass,
			     parent, arglist, num_args);

	  /* now, parse their menu entry. This is gross, but beats
	   scanning ahead to find the stupid balanced parens */
	  menu = EzMenuParseMenu(menu_entry, menu, True);

	  num_args = 0;
	  XtSetArg(arglist[num_args], XtNlabel, label); num_args++;
	  XtSetArg(arglist[num_args], XtNmenu, menu_entry); num_args++;
	  entry = XtCreateManagedWidget(name, ezMEObjectClass, parent, 
					arglist, num_args);
	}
	else {
	  num_args = 0;
	  XtSetArg(arglist[num_args], XtNlabel, label); num_args++;
	  XtSetArg(arglist[num_args], XtNmenu, 0); num_args++;
	  entry = XtCreateManagedWidget(name, ezMEObjectClass, parent, 
					arglist, num_args);
	}

	menu = EzMenuParseMenu(entry, menu, isMenu);
      }

      SKIPWHITE(menu);
      if (*menu != ')') {
	char buffer[2048];
	XtAppContext context = 
	  XtWidgetToApplicationContext( parent );

	sprintf(buffer, "unbalanced parens or bogus format: %s\n", menu);

	XtAppError(context, buffer);
	exit(1);
      }
      menu++;
    }
  }
  return(menu);
}

/*      Function Name: Initialize
 *      Description: Initializes the simple menu widget
 *      Arguments: request - the widget requested by the argument list.
 *                 new     - the new widget with both resource and non
 *                           resource values.
 *      Returns: none.
 */

/* ARGSUSED */
static void
Initialize(request, new)
Widget request, new;
{
  EzMenuWidget newez = (EzMenuWidget) new;
  EzMenuWidget requestez = (EzMenuWidget) request;

#ifdef DEBUG
  fprintf(stderr,"Initialize %s\n",
	  XtWidgetName(request);
#endif

  /* just copy the string that will be used to initialize the menu */
  
  newez -> ez_menu.menu = 0;

  if (requestez -> ez_menu.menu ) {
    int len = strlen(requestez -> ez_menu.menu);
    newez -> ez_menu.menu = XtMalloc( len + 1 );
    strcpy( newez -> ez_menu.menu, requestez -> ez_menu.menu);
  }

  if ( newez -> ez_menu.menu ) {
    EzMenuParseMenu( (Widget) newez, newez -> ez_menu.menu, True );
  }
}

/*      Function Name: Realize
 *      Description: Realizes the widget.
 *      Arguments: w - the simple menu widget.
 *                 mask - value mask for the window to create.
 *                 attrs - attributes for the window to create.
 *      Returns: none
 */

static void
Realize(w, mask, attrs)
Widget w;
XtValueMask * mask;
XSetWindowAttributes * attrs;
{
  EzMenuWidget newez = (EzMenuWidget) w;
  (*superclass->core_class.realize) (w, mask, attrs);
}


/*      Function Name: SetValues
 *      Description: Relayout the menu when one of the resources is changed.
 *      Arguments: current - current state of the widget.
 *                 request - what was requested.
 *                 new - what the widget will become.
 *      Returns: none
 */

/* ARGSUSED */
static Boolean
SetValues(current, request, new)
     Widget current, request, new;
{
  EzMenuWidget ez_old = (EzMenuWidget) current;
  EzMenuWidget ez_want = (EzMenuWidget) request;
  EzMenuWidget ez_new = (EzMenuWidget) new;
  Boolean ret_val = FALSE;
  if (

      /* no old menu, want one now */

      ( !ez_old -> ez_menu.menu && ez_want -> ez_menu.menu )
      ||

      /* old menu, new one is different */

      (ez_want -> ez_menu.menu
       && ez_old -> ez_menu.menu
       && ( strcmp( ez_old -> ez_menu.menu,
		   ez_want -> ez_menu.menu) != 0 ))
      ) {

    int len = strlen(ez_old -> ez_menu.menu);

    ez_new -> ez_menu.menu = XtMalloc( len + 1 );
    strcpy( ez_new -> ez_menu.menu,
	   ez_old -> ez_menu.menu);
    
    if ( XtIsRealized((Widget) ez_old) ) {
      SimpleMenuWidget smw = (SimpleMenuWidget) ez_old;
      SmeObject *entry;
      
      ForAllChildren(smw, entry) {
	XtDestroyWidget((Widget) entry);
      }

      EzMenuParseMenu((Widget) ez_new, ez_new -> ez_menu.menu, True);
      ret_val = TRUE;
    }
  }
  return(ret_val);
}

static void
Destroy(who)
EzMenuWidget who;
{
  XtFree( who -> ez_menu.menu );
}

