/****************************************************************************
 * NCSA Mosaic for the X Window System                                      *
 * Software Development Group                                               *
 * National Center for Supercomputing Applications                          *
 * University of Illinois at Urbana-Champaign                               *
 * 605 E. Springfield, Champaign IL 61820                                   *
 * mosaic@ncsa.uiuc.edu                                                     *
 *                                                                          *
 * Copyright (C) 1993, Board of Trustees of the University of Illinois      *
 *                                                                          *
 * NCSA Mosaic software, both binary and source (hereafter, Software) is    *
 * copyrighted by The Board of Trustees of the University of Illinois       *
 * (UI), and ownership remains with the UI.                                 *
 *                                                                          *
 * The UI grants you (hereafter, Licensee) a license to use the Software    *
 * for academic, research and internal business purposes only, without a    *
 * fee.  Licensee may distribute the binary and source code (if released)   *
 * to third parties provided that the copyright notice and this statement   *
 * appears on all copies and that no charge is associated with such         *
 * copies.                                                                  *
 *                                                                          *
 * Licensee may make derivative works.  However, if Licensee distributes    *
 * any derivative work based on or derived from the Software, then          *
 * Licensee will (1) notify NCSA regarding its distribution of the          *
 * derivative work, and (2) clearly notify users that such derivative       *
 * work is a modified version and not the original NCSA Mosaic              *
 * distributed by the UI.                                                   *
 *                                                                          *
 * Any Licensee wishing to make commercial use of the Software should       *
 * contact the UI, c/o NCSA, to negotiate an appropriate license for such   *
 * commercial use.  Commercial use includes (1) integration of all or       *
 * part of the source code into a product for sale or license by or on      *
 * behalf of Licensee to third parties, or (2) distribution of the binary   *
 * code or source code to third parties that need it to utilize a           *
 * commercial product sold or licensed by or on behalf of Licensee.         *
 *                                                                          *
 * UI MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THIS SOFTWARE FOR   *
 * ANY PURPOSE.  IT IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED          *
 * WARRANTY.  THE UI SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY THE    *
 * USERS OF THIS SOFTWARE.                                                  *
 *                                                                          *
 * By using or copying this Software, Licensee agrees to abide by the       *
 * copyright law and all other applicable laws of the U.S. including, but   *
 * not limited to, export control laws, and the terms of this license.      *
 * UI shall have the right to terminate this license immediately by         *
 * written notice upon Licensee's breach of, or non-compliance with, any    *
 * of its terms.  Licensee may be held legally responsible for any          *
 * copyright infringement that is caused or encouraged by Licensee's        *
 * failure to abide by the terms of this license.                           *
 *                                                                          *
 * Comments and questions are welcome and can be sent to                    *
 * mosaic-x@ncsa.uiuc.edu.                                                  *
 ****************************************************************************/

#include "XmxP.h"

/* ------------------------------------------------------------------------ */
/* --------------------------- PRIVATE ROUTINES --------------------------- */
/* ------------------------------------------------------------------------ */

/* ----------------------- _XmxMenuAddEntryToRecord ----------------------- */

/* Create a new MenuEntry and add it to the head of a MenuRecord list. */
private void 
_XmxMenuAddEntryToRecord (XmxMenuRecord *rec, Widget w, int token)
{
  XmxMenuEntry *_ent;

  /* Create new menu entry. */
  _ent = (XmxMenuEntry *)malloc (sizeof (XmxMenuEntry));
  _ent->w = w;
  _ent->token = token;

  /* Add rest of list to tail of this entry. */
  _ent->next = rec->first_entry;

  /* Make this entry head of list. */
  rec->first_entry = _ent;

  return;
}

/* ---------------------- _XmxMenuGetEntryFromRecord ---------------------- */

private XmxMenuEntry *
_XmxMenuGetEntryFromRecord (XmxMenuRecord *rec, int token)
{
  /* Given token, fetch the corresponding entry. */
  XmxMenuEntry *_ent = NULL;
  int _done;

  /* Search the linked list. */
  _ent = rec->first_entry;
  _done = 0;
  while (_ent != NULL && !_done)
    {
      if (_ent->token == token)
        _done = 1;
      else
        _ent = _ent->next;
    }

  /* Punish the application for asking for a nonexistent entry. */
  /* assert (_done); */
  return _ent;
}

/* ------------------------- _XmxMenuCreateRecord ------------------------- */

/* Create a new MenuRecord and clear out its list. */
private XmxMenuRecord *
_XmxMenuCreateRecord (Widget base)
{
  XmxMenuRecord *_rec;

  /* Create the new XmxMenuRecord. */
  _rec = (XmxMenuRecord *)malloc (sizeof (XmxMenuRecord));
  _rec->base = base;
  _rec->first_entry = NULL;

  return _rec;
}

/* ------------------------------------------------------------------------ */
/* --------------------------- PUBLIC ROUTINES ---------------------------- */
/* ------------------------------------------------------------------------ */

/* --------------------------- XmxRSetSensitive --------------------------- */

/* args NOT used on Widget */
public void
XmxRSetSensitive (XmxMenuRecord *rec, int token, int state)
{
  XmxMenuEntry *_entry;

  assert (state == XmxSensitive || state == XmxUnsensitive);
  _entry = _XmxMenuGetEntryFromRecord (rec, XmxExtractToken (token));
  /* XtSetSensitive propagates down Widget hierarchy. */
  if (_entry)
    XtSetSensitive (_entry->w, (state == XmxSensitive) ? True : False);
  
  return;
}

/* -------------------------- XmxRSetToggleState -------------------------- */

/* args not used */
public void
XmxRSetToggleState (XmxMenuRecord *rec, int token, int state)
{
  XmxMenuEntry *_entry;

  assert (state == XmxSet || state == XmxUnset);
  _entry = _XmxMenuGetEntryFromRecord (rec, XmxExtractToken (token));
  if (_entry)
    XmToggleButtonGadgetSetState 
      (_entry->w, (state == XmxSet) ? True : False, False);
  
  return;
}

/* ------------------------- XmxRUnsetAllToggles -------------------------- */

/* args not used */
public void
XmxRUnsetAllToggles (XmxMenuRecord *rec)
{
  XmxMenuEntry *_ent;

  for (_ent = rec->first_entry; _ent != NULL; _ent = _ent->next)
    XmToggleButtonGadgetSetState
      (_ent->w, False, False);

  return;
}

/* ----------------------- XmxRSetOptionMenuHistory ----------------------- */

/* args used on Widget */
public void
XmxRSetOptionMenuHistory (XmxMenuRecord *rec, int token)
{
  XmxMenuEntry *_entry;

  _entry = _XmxMenuGetEntryFromRecord (rec, XmxExtractToken (token));
  if (_entry)
    {
      XmxSetArg (XmNmenuHistory, (XtArgVal)(_entry->w));
      XtSetValues (rec->base, Xmx_wargs, Xmx_n);
    }

  Xmx_n = 0;
  return;
}

/* ---------------------------- XmxRSetValues ----------------------------- */

/* args used on Widget */
public void
XmxRSetValues (XmxMenuRecord *rec, int token)
{
  XmxMenuEntry *_entry;

  /* Strip out uniqid, if present. */
  _entry = _XmxMenuGetEntryFromRecord (rec, XmxExtractToken (token));
  if (_entry)
    XtSetValues (_entry->w, Xmx_wargs, Xmx_n);

  Xmx_n = 0;
  return;
}

/* ---------------------------- XmxRGetWidget ----------------------------- */

/* args irrelevant */
public Widget
XmxRGetWidget (XmxMenuRecord *rec, int token)
{
  XmxMenuEntry *_entry;

  /* Strip out uniqid, if present. */
  _entry = _XmxMenuGetEntryFromRecord (rec, XmxExtractToken (token));

  if (_entry)
    return _entry->w;
  else
    return NULL;
}

/* -------------------------- XmxRMakeOptionMenu -------------------------- */

/* args apply to pulldown menu */
public XmxMenuRecord *
XmxRMakeOptionMenu (Widget parent, String name, XtCallbackProc cb,
                    XmxOptionMenuStruct *opts)
{
  XmxMenuRecord *_rec;
  Widget _pulldown, _button, _menuhist = (Widget)NULL;
  int _i;

  /* Create a pulldown menupane to attach to the option menu;
     preloaded wargs affect this. */
  _pulldown = XmCreatePulldownMenu (parent, "pulldownmenu", Xmx_wargs, Xmx_n);

  /* menuHistory will not be applied to _pulldown, so we'll modify
     _rec directly after creating the option menu. */
  _rec = _XmxMenuCreateRecord (_pulldown);

  /* Create pushbutton gadgets as childen of the pulldown menu. */
  _i = 0;
  while (opts[_i].namestr)
    {
      Xmx_n = 0;
      XmxSetArg (XmNlabelString,
                 (XtArgVal)XmStringCreateLtoR (opts[_i].namestr,
                                               XmSTRING_DEFAULT_CHARSET));
      _button = XmCreatePushButtonGadget (_pulldown, "pushbutton",
                                          Xmx_wargs, Xmx_n);
      XtManageChild (_button);
      XtAddCallback (_button, XmNactivateCallback, cb,
                     (XtPointer)_XmxMakeClientData (opts[_i].data));
      if (opts[_i].set_state == XmxSet)
        _menuhist = _button;

      _XmxMenuAddEntryToRecord (_rec, _button, opts[_i].data);

      _i++;
    }

  /* Create the option menu itself; tie in the pulldown menu. */
  Xmx_n = 0;
  XmxSetArg (XmNsubMenuId, (XtArgVal)_pulldown);
  if (_menuhist != (Widget)NULL)
    XmxSetArg (XmNmenuHistory, (XtArgVal)_menuhist);
  Xmx_w = XmCreateOptionMenu (parent, "optionmenu", Xmx_wargs, Xmx_n);
  XtManageChild (Xmx_w);

  XmxSetArg (XmNalignment, (XtArgVal)XmALIGNMENT_BEGINNING);
  XmxSetValues (XmOptionButtonGadget (Xmx_w));

  if (name)
    {
      XmxSetArg (XmNlabelString,
                 (XtArgVal)XmStringCreateLtoR (name, XmSTRING_DEFAULT_CHARSET));
      XmxSetValues (XmOptionLabelGadget (Xmx_w));
    }
  else
    {
      XmxSetArg (XmNspacing, (XtArgVal)0);
      XmxSetArg (XmNmarginWidth, (XtArgVal)0);
      XmxSetValues (Xmx_w);
      XmxSetArg (XmNlabelString, NULL);
      XmxSetValues (XmOptionLabelGadget (Xmx_w));
    }

  /* Explicitly set base Widget of record. */
  _rec->base = Xmx_w;

  Xmx_n = 0;
  return _rec;
}

/* -------------------------- XmxRMakeToggleMenu -------------------------- */

/* args apply to radiobox or optionbox */
public XmxMenuRecord *
XmxRMakeToggleMenu (Widget parent, int behavior, XtCallbackProc cb,
                    XmxToggleMenuStruct *opts)
{
  XmxMenuRecord *_rec;
  Widget _box;
  int _i;

  assert (behavior == XmxOneOfMany || behavior == XmxNOfMany);
  switch (behavior)
    {
    case XmxOneOfMany:
      _box = XmxMakeRadioBox (parent);
      break;
    case XmxNOfMany:
      _box = XmxMakeOptionBox (parent);
      break;
    }

  _rec = _XmxMenuCreateRecord (_box);

  _i = 0;
  while (opts[_i].namestr)
    {
      XmxMakeToggleButton (_box, opts[_i].namestr, cb, opts[_i].data);
      XmxSetToggleButton (Xmx_w, opts[_i].set_state);
      _XmxMenuAddEntryToRecord (_rec, Xmx_w, opts[_i].data);

      _i++;
    }

  Xmx_w = _box;

  Xmx_n = 0;
  return _rec;
}

/* -------------------------- _XmxRCreateMenubar -------------------------- */

/* Possible deficiency: will not be able to grey out a submenu
   (cascade button). */
private void 
_XmxRCreateMenubar (Widget menu, XmxMenubarStruct *menulist, 
                    XmxMenuRecord *rec)
{
  int _i;
  Widget *_buttons;
  int _separators = 0, _nitems;

  _nitems = 0;
  while (menulist[_nitems].namestr)
    _nitems++;

  _buttons = (Widget *)XtMalloc (_nitems * sizeof (Widget));

  for (_i = 0; _i < _nitems; _i++)
    {
      /* Name of "----" means make a separator. */
      if (strcmp(menulist[_i].namestr, "----") == 0)
        {
          XtCreateManagedWidget ("separator", xmSeparatorGadgetClass,
                                 menu, NULL, 0);
          _separators++;
        }
      /* A function means it's an ordinary entry with callback. */
      else if (menulist[_i].func)
        {
          Xmx_n = 0;
          if (menulist[_i].mnemonic)
            XmxSetArg (XmNmnemonic, (XtArgVal)(menulist[_i].mnemonic));
          if (menulist[_i].namestr[0] == '#' ||
              menulist[_i].namestr[0] == '<') /* option/toggle button */
            {
              XmString xmstr;

              /* A toggle button is diamond-shaped. */
              if (menulist[_i].namestr[0] == '<')
                XmxSetArg (XmNindicatorType, (XtArgVal)XmONE_OF_MANY);

	      /* Make sure the button shows up even when toggled off. */
              if (menulist[_i].namestr[0] == '#')
                XmxSetArg (XmNvisibleWhenOff, (XtArgVal)True);

              /* Ignore first character of label. */
              xmstr = XmxMakeXmstrFromString (&(menulist[_i].namestr[1]));
              XmxSetArg (XmNlabelString, (XtArgVal)xmstr);
              _buttons[_i - _separators] = XtCreateManagedWidget
                ("togglebutton", xmToggleButtonGadgetClass,
                 menu, Xmx_wargs, Xmx_n);
              XmStringFree (xmstr);

              XtAddCallback
                (_buttons[_i - _separators], XmNvalueChangedCallback,
                 menulist[_i].func, 
                 (XtPointer)_XmxMakeClientData (menulist[_i].data));

              /* Add thie button to the menu record. */
              _XmxMenuAddEntryToRecord 
                (rec, _buttons[_i - _separators], menulist[_i].data);
            }
          else /* regular button */
            {
              XmString xmstr = 
                XmStringCreateLtoR
                  (menulist[_i].namestr, XmSTRING_DEFAULT_CHARSET);
              XmxSetArg (XmNlabelString, (XtArgVal)xmstr);
              _buttons[_i - _separators] = XtCreateManagedWidget
                ("pushbutton", xmPushButtonGadgetClass,
                 menu, Xmx_wargs, Xmx_n);
              XmStringFree (xmstr);
              XtAddCallback 
                (_buttons[_i - _separators], XmNactivateCallback,
                 menulist[_i].func, 
                 (XtPointer)_XmxMakeClientData (menulist[_i].data));

              /* Add thie button to the menu record. */
              _XmxMenuAddEntryToRecord 
                (rec, _buttons[_i - _separators], menulist[_i].data);
            }
        }

      /* No function and no submenu entry means it's just a label. */
      else if (menulist[_i].sub_menu == (XmxMenubarStruct *)NULL)
        {
          Xmx_n = 0;
          XmxSetArg (XmNlabelString, (XtArgVal)XmStringCreateLtoR
                     (menulist[_i].namestr, XmSTRING_DEFAULT_CHARSET));
          _buttons[_i - _separators] = XtCreateManagedWidget
            ("label", xmLabelGadgetClass, menu, Xmx_wargs, Xmx_n);
        }

      /* If all else fails, it's a submenu. */
      else
        {
          XmString xmstr;
          Widget _sub_menu;
          _sub_menu = XmCreatePulldownMenu (menu, "pulldownmenu", NULL, 0);

          Xmx_n = 0;
          XmxSetArg (XmNsubMenuId, (XtArgVal)_sub_menu);
          if (menulist[_i].mnemonic)
            XmxSetArg (XmNmnemonic, (XtArgVal)(menulist[_i].mnemonic));
          xmstr = XmStringCreateLtoR
            (menulist[_i].namestr, XmSTRING_DEFAULT_CHARSET);
          XmxSetArg (XmNlabelString, (XtArgVal)xmstr);
          _buttons[_i - _separators] = XtCreateWidget
            ("cascadebutton", xmCascadeButtonGadgetClass,
             menu, Xmx_wargs, Xmx_n);
          XmStringFree (xmstr);

          /* If name is "Help", put on far right. */
          if (strcmp (menulist[_i].namestr, "Help") == 0)
            {
              Xmx_n = 0;
              XmxSetArg (XmNmenuHelpWidget, (XtArgVal)_buttons[_i - _separators]);
              XtSetValues (menu, Xmx_wargs, Xmx_n);
            }
          /* Recursively create new submenu. */
          _XmxRCreateMenubar (_sub_menu, menulist[_i].sub_menu, rec);
        }
    }

  XtManageChildren (_buttons, _nitems - _separators);

  XtFree ((char *)_buttons);

  return;
}

/* --------------------------- XmxRMakeMenubar ---------------------------- */

/* args apply to menubar */
public XmxMenuRecord *
XmxRMakeMenubar (Widget parent, XmxMenubarStruct *mainmenu)
{
  Widget _menubar;
  XmxMenuRecord *_rec;

  /* Preset resources applied to main menubar only. */
  _menubar = XmCreateMenuBar (parent, "menubar", Xmx_wargs, Xmx_n);
  XtManageChild (_menubar);

  /* Create the new XmxMenuRecord. */
  _rec = _XmxMenuCreateRecord (_menubar);

  Xmx_n = 0;
  _XmxRCreateMenubar (_menubar, mainmenu, _rec);

  Xmx_n = 0;
  Xmx_w = _menubar;
  return _rec;
}
