view lwlib/lwlib-Xm.c @ 814:a634e3b7acc8

[xemacs-hg @ 2002-04-14 12:41:59 by ben] latest changes TODO.ben-mule-21-5: Update. make-docfile.c: Add basic support for handling ISO 2022 doc strings -- we parse the basic charset designation sequences so we know whether we're in ASCII and have to pay attention to end quotes and such. Reformat code according to coding standards. abbrev.el: Add `global-abbrev-mode', which turns on or off abbrev-mode in all buffers. Added `defining-abbrev-turns-on-abbrev-mode' -- if non-nil, defining an abbrev through an interactive function will automatically turn on abbrev-mode, either globally or locally depending on the command. This is the "what you'd expect" behavior. indent.el: general function for indenting a balanced expression in a mode-correct way. Works similar to indent-region in that a mode can specify a specific command to do the whole operation; if not, figure out the region using forward-sexp and indent each line using indent-according-to-mode. keydefs.el: Removed. Modify M-C-backslash to do indent-region-or-balanced-expression. Make S-Tab just insert a TAB char, like it's meant to do. make-docfile.el: Now that we're using the call-process-in-lisp, we need to load an extra file win32-native.el because we're running a bare temacs. menubar-items.el: Totally redo the Cmds menu so that most used commands appear directly on the menu and less used commands appear in submenus. The old way may have been very pretty, but rather impractical. process.el: Under Windows, don't ever use old-call-process-internal, even in batch mode. We can do processes in batch mode. subr.el: Someone recoded truncate-string-to-width, saying "the FSF version is too complicated and does lots of hard-to-understand stuff" but the resulting recoded version was *totally* wrong! it misunderstood the basic point of this function, which is work in *columns* not chars. i dumped ours and copied the version from FSF 21.1. Also added truncate-string-with-continuation-dots, since this idiom is used often. config.inc.samp, xemacs.mak: Separate out debug and optimize flags. Remove all vestiges of USE_MINIMAL_TAGBITS, USE_INDEXED_LRECORD_IMPLEMENTATION, and GUNG_HO, since those ifdefs have long been removed. Make error-checking support actually work. Some rearrangement of config.inc.samp to make it more logical. Remove callproc.c and ntproc.c from xemacs.mak, no longer used. Make pdump the default. lisp.h: Add support for strong type-checking of Bytecount, Bytebpos, Charcount, Charbpos, and others, by making them classes, overloading the operators to provide integer-like operation and carefully controlling what operations are allowed. Not currently enabled in C++ builds because there are still a number of compile errors, and it won't really work till we merge in my "8-bit-Mule" workspace, in which I make use of the new types Charxpos, Bytexpos, Memxpos, representing a "position" either in a buffer or a string. (This is especially important in the extent code.) abbrev.c, alloc.c, eval.c, buffer.c, buffer.h, editfns.c, fns.c, text.h: Warning fixes, some of them related to new C++ strict type checking of Bytecount, Charbpos, etc. dired.c: Caught an actual error due to strong type checking -- char len being passed when should be byte len. alloc.c, backtrace.h, bytecode.c, bytecode.h, eval.c, sysdep.c: Further optimize Ffuncall: -- process arg list at compiled-function creation time, converting into an array for extra-quick access at funcall time. -- rewrite funcall_compiled_function to use it, and inline this function. -- change the order of check for magic stuff in SPECBIND_FAST_UNSAFE to be faster. -- move the check for need to garbage collect into the allocation code, so only a single flag needs to be checked in funcall. buffer.c, symbols.c: add debug funs to check on mule optimization info in buffers and strings. eval.c, emacs.c, text.c, regex.c, scrollbar-msw.c, search.c: Fix evil crashes due to eistrings not properly reinitialized under pdump. Redo a bit some of the init routines; convert some complex_vars_of() into simple vars_of(), because they didn't need complex processing. callproc.c, emacs.c, event-stream.c, nt.c, process.c, process.h, sysdep.c, sysdep.h, syssignal.h, syswindows.h, ntproc.c: Delete. Hallelujah, praise the Lord, there is no god but Allah!!! fix so that processes can be invoked in bare temacs -- thereby eliminating any need for callproc.c. (currently only eliminated under NT.) remove all crufty and unnecessary old process code in ntproc.c and elsewhere. move non-callproc-specific stuff (mostly environment) into process.c, so callproc.c can be left out under NT. console-tty.c, doc.c, file-coding.c, file-coding.h, lstream.c, lstream.h: fix doc string handling so it works with Japanese, etc docs. change handling of "character mode" so callers don't have to manually set it (quite error-prone). event-msw.c: spacing fixes. lread.c: eliminate unused crufty vintage-19 "FSF defun hack" code. lrecord.h: improve pdump description docs. buffer.c, ntheap.c, unexnt.c, win32.c, emacs.c: Mule-ize some unexec and startup code. It was pseudo-Mule-ized before by simply always calling the ...A versions of functions, but that won't cut it -- eventually we want to be able to run properly even if XEmacs has been installed in a Japanese directory. (The current problem is the timing of the loading of the Unicode tables; this will eventually be fixed.) Go through and fix various other places where the code was not Mule-clean. Provide a function mswindows_get_module_file_name() to get our own name without resort to PATH_MAX and such. Add a big comment in main() about the problem with Unicode table load timing that I just alluded to. emacs.c: When error-checking is enabled (interpreted as "user is developing XEmacs"), don't ask user to "pause to read messages" when a fatal error has occurred, because it will wedge if we are in an inner modal loop (typically when a menu is popped up) and make us unable to get a useful stack trace in the debugger. text.c: Correct update_entirely_ascii_p_flag to actually work. lisp.h, symsinit.h: declarations for above changes.
author ben
date Sun, 14 Apr 2002 12:43:31 +0000
parents e5083672c894
children 4a27df428c73
line wrap: on
line source

/* The lwlib interface to Motif widgets.
   Copyright (C) 1992, 1993, 1994 Lucid, Inc.
   Copyright (C) 1995 Tinker Systems and INS Engineering Corp.

This file is part of the Lucid Widget Library.

The Lucid Widget Library is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.

The Lucid Widget Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with XEmacs; see the file COPYING.  If not, write to
the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.  */

#include <config.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <limits.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#include <X11/StringDefs.h>
#include <X11/IntrinsicP.h>
#include <X11/ObjectP.h>
#include <X11/CoreP.h>
#include <X11/CompositeP.h>

#include "lwlib-Xm.h"
#include "lwlib-utils.h"

#include <Xm/Xm.h>
#include <Xm/BulletinB.h>
#include <Xm/CascadeB.h>
#include <Xm/DrawingA.h>
#include <Xm/FileSB.h>
#include <Xm/Label.h>
#include <Xm/List.h>
#include <Xm/MenuShell.h>
#include <Xm/MessageB.h>
#include <Xm/PushB.h>
#include <Xm/PushBG.h>
#include <Xm/ArrowB.h>
#include <Xm/ScrollBar.h>
#include <Xm/SelectioB.h>
#include <Xm/Text.h>
#include <Xm/TextF.h>
#include <Xm/ToggleB.h>
#include <Xm/ToggleBG.h>
#include <Xm/RowColumn.h>
#include <Xm/ScrolledW.h>
#include <Xm/Separator.h>
#include <Xm/DialogS.h>
#include <Xm/Form.h>
#ifdef LWLIB_WIDGETS_MOTIF
#include <Xm/Scale.h>
#if XmVERSION > 1
#include <Xm/ComboBoxP.h>
#endif
#endif

#ifdef LWLIB_MENUBARS_MOTIF
static void xm_pull_down_callback (Widget, XtPointer, XtPointer);
#endif
static void xm_internal_update_other_instances (Widget, XtPointer,
						XtPointer);
static void xm_pop_down_callback (Widget, XtPointer, XtPointer);
static void xm_generic_callback (Widget, XtPointer, XtPointer);
static void mark_dead_instance_destroyed (Widget widget, XtPointer closure,
					  XtPointer call_data);
#if defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF)
static void xm_nosel_callback (Widget, XtPointer, XtPointer);
#endif
#ifdef LWLIB_SCROLLBARS_MOTIF
static void xm_scrollbar_callback (Widget, XtPointer, XtPointer);
#endif

#ifdef LWLIB_MENUBARS_MOTIF
static void
xm_update_menu (widget_instance* instance, Widget widget, widget_value* val,
		Boolean deep_p);
#endif

/* Structures to keep destroyed instances */
typedef struct _destroyed_instance
{
  char*		name;
  char*		type;
  Widget 	widget;
  Widget	parent;
  Boolean	pop_up_p;
  struct _destroyed_instance*	next;
} destroyed_instance;

static destroyed_instance*
all_destroyed_instances = NULL;

/* Utility function. */
static char *
safe_strdup (char* s)
{
  char *result;
  if (! s) return 0;
  result = (char *) malloc (strlen (s) + 1);
  if (! result)
    return 0;
  strcpy (result, s);
  return result;
}

static destroyed_instance*
make_destroyed_instance (char* name, char* type, Widget widget, Widget parent,
			 Boolean pop_up_p)
{
  destroyed_instance* instance =
    (destroyed_instance*) malloc (sizeof (destroyed_instance));
  instance->name = safe_strdup (name);
  instance->type = safe_strdup (type);
  instance->widget = widget;
  instance->parent = parent;
  instance->pop_up_p = pop_up_p;
  instance->next = NULL;
  return instance;
}

static void
free_destroyed_instance (destroyed_instance* instance)
{
  free (instance->name);
  free (instance->type);
  free (instance);
}

/* motif utility functions */
Widget
first_child (Widget widget)
{
  return ((CompositeWidget)widget)->composite.children [0];
}

Boolean
lw_motif_widget_p (Widget widget)
{
  return
#ifdef LWLIB_DIALOGS_MOTIF
    XtClass (widget) == xmDialogShellWidgetClass ||
#endif
      XmIsPrimitive (widget) || XmIsManager (widget) || XmIsGadget (widget);
}

static char *
resource_string (Widget widget, char *name)
{
  XtResource resource;
  char *result = NULL;

  resource.resource_name  = "labelString";
  resource.resource_class = "LabelString"; /* #### should be Xmsomething... */
  resource.resource_type = XtRString;
  resource.resource_size = sizeof (String);
  resource.resource_offset = 0;
  resource.default_type = XtRImmediate;
  resource.default_addr = 0;

  XtGetSubresources (widget, (XtPointer)&result, name,
		     name, &resource, 1, NULL, 0);
  return result;
}



#ifdef LWLIB_DIALOGS_MOTIF

static Boolean
is_in_dialog_box (Widget w)
{
  Widget wmshell;

  wmshell = XtParent (w);
  while (wmshell && (XtClass (wmshell) != xmDialogShellWidgetClass))
    wmshell = XtParent (wmshell);

  if (wmshell && XtClass (wmshell) == xmDialogShellWidgetClass)
    return True;
  else
    return False;
}

#endif /* LWLIB_DIALOGS_MOTIF */

#if defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_MENUBARS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF)

/* update the label of anything subclass of a label */
static void
xm_update_label (widget_instance* instance, Widget widget, widget_value* val)
{
  XmString built_string = NULL;
  XmString key_string   = NULL;
  XmString val_string   = NULL;
  XmString name_string  = NULL;
  Arg al [20];
  int ac = 0;
  int type;

  /* Don't clobber pixmap types. */
  XtSetArg (al [0], XmNlabelType, &type);
  XtGetValues (widget, al, 1);

  if (type == XmPIXMAP)
    return;

  if (val->value)
    {
      /* #### Temporary fix. I though Motif was supposed to grok %_
         type things. */
      lw_remove_accelerator_spec (val->value);
  
#ifdef LWLIB_DIALOGS_MOTIF
      /*
       * Sigh.  The main text of a label is the name field for menubar
       * entries.  The value field is a possible additional field to be
       * concatenated on to the name field.  HOWEVER, with dialog boxes
       * the value field is the complete text which is supposed to be
       * displayed as the label.  Yuck.
       */
      if (is_in_dialog_box (widget))
	{
	  char *value_name = NULL;

	  value_name = resource_string (widget, val->value);
	  if (!value_name)
	    value_name = val->value;

	  built_string =
	    XmStringCreateLtoR (value_name, XmSTRING_DEFAULT_CHARSET);
	}
      else
#endif /* LWLIB_DIALOGS_MOTIF */
	{
	  char *value_name = NULL;
	  char *res_name = NULL;

	  res_name = resource_string (widget, val->name);
	  /* Concatenating the value with itself seems just plain daft. */
	  if (!res_name)
	    {
	      built_string =
		XmStringCreateLtoR (val->value, XmSTRING_DEFAULT_CHARSET);
	    }
	  else
	    {
	      name_string =
		XmStringCreateLtoR (res_name, XmSTRING_DEFAULT_CHARSET);

	      value_name = XtMalloc (strlen (val->value) + 2);
	      *value_name = 0;
	      strcat (value_name, " ");
	      strcat (value_name, val->value);

	      val_string =
		XmStringCreateLtoR (value_name, XmSTRING_DEFAULT_CHARSET);

	      built_string =
		XmStringConcat (name_string, val_string);

	      XtFree (value_name);
	    }
	}

      XtSetArg (al [ac], XmNlabelString, built_string); ac++;
      XtSetArg (al [ac], XmNlabelType, XmSTRING);	ac++;
    }

  if (val->key)
    {
      key_string = XmStringCreateLtoR (val->key, XmSTRING_DEFAULT_CHARSET);
      XtSetArg (al [ac], XmNacceleratorText, key_string); ac++;
    }

  if (ac)
    XtSetValues (widget, al, ac);

  if (built_string)
    XmStringFree (built_string);

  if (key_string)
    XmStringFree (key_string);

  if (name_string)
    XmStringFree (name_string);

  if (val_string)
    XmStringFree (val_string);
}

#endif /* defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_MENUBARS_MOTIF) */

/* update of list */
static void
xm_update_list (widget_instance* instance, Widget widget, widget_value* val)
{
  widget_value* cur;
  int i;
  XtRemoveAllCallbacks (widget, XmNsingleSelectionCallback);
  XtAddCallback (widget, XmNsingleSelectionCallback, xm_generic_callback,
		 instance);
  for (cur = val->contents, i = 0; cur; cur = cur->next)
    if (cur->value)
      {
	XmString xmstr = XmStringCreate (cur->value, XmSTRING_DEFAULT_CHARSET);
	i += 1;
	XmListAddItem (widget, xmstr, 0);
	if (cur->selected)
	  XmListSelectPos (widget, i, False);
	XmStringFree (xmstr);
      }
}

/* update of buttons */
static void
xm_update_pushbutton (widget_instance* instance, Widget widget,
		      widget_value* val)
{
  Arg al [1];
  XtSetArg (al [0], XmNalignment, XmALIGNMENT_CENTER);
  XtSetValues (widget, al, 1);
  XtRemoveAllCallbacks (widget, XmNactivateCallback);
  XtAddCallback (widget, XmNactivateCallback, xm_generic_callback, instance);
}

static void
xm_update_progress (widget_instance* instance, Widget scale,
		      widget_value* val)
{
  Arg al[20];
  int ac = 0;
  Dimension height = 0;
  Dimension width = 0;
  if (!val->call_data)
    {
      XtSetArg (al [ac], XmNeditable, False);		ac++;
    }
  else
    {
      XtSetArg (al [ac], XmNeditable, val->enabled);		ac++;
    }
  height = (Dimension)lw_get_value_arg (val, XtNheight);
  width = (Dimension)lw_get_value_arg (val, XtNwidth);
  if (height > 0)
    {
      XtSetArg (al [ac], XmNscaleHeight, height); ac++;
    }
  if (width > 0)
    {
      XtSetArg (al [ac], XmNscaleWidth, width); ac++;
    }

  XtSetValues (scale, al, 1);
}

#ifdef LWLIB_MENUBARS_MOTIF

static void
xm_update_cascadebutton (widget_instance* instance, Widget widget,
			 widget_value* val)
{
  /* Should also rebuild the menu by calling ...update_menu... */
  if (val
      && val->type == CASCADE_TYPE
      && val->contents
      && val->contents->type == INCREMENTAL_TYPE)
    {
      /* okay, we're now doing a lisp callback to incrementally generate
	 more of the menu. */
      XtRemoveAllCallbacks (widget, XmNcascadingCallback);
      XtAddCallback (widget, XmNcascadingCallback, xm_pull_down_callback,
		     instance);
      XtCallCallbacks ((Widget)widget,
		      XmNcascadingCallback,
		      (XtPointer)val->contents);

    } else {
      XtRemoveAllCallbacks (widget, XmNcascadingCallback);
      XtAddCallback (widget, XmNcascadingCallback, xm_pull_down_callback,
		     instance);
    }
}

#endif /* LWLIB_MENUBARS_MOTIF */

/* update toggle and radiobox */
static void
xm_update_toggle (widget_instance* instance, Widget widget, widget_value* val)
{
  Arg al [2];
  XtRemoveAllCallbacks (widget, XmNvalueChangedCallback);
  XtAddCallback (widget, XmNvalueChangedCallback, xm_generic_callback,
		 instance);
  XtSetArg (al [0], XmNset, val->selected);
  XtSetArg (al [1], XmNalignment, XmALIGNMENT_BEGINNING);
  XtSetValues (widget, al, 1);
}

static void
xm_update_radiobox (widget_instance* instance, Widget widget,
		    widget_value* val)
{
  Widget toggle;
  widget_value* cur;

  /* update the callback */
  XtRemoveAllCallbacks (widget, XmNentryCallback);
  XtAddCallback (widget, XmNentryCallback, xm_generic_callback, instance);

  /* first update all the toggles */
  /* Energize kernel interface is currently bad.  It sets the selected widget
     with the selected flag but returns it by its name.  So we currently
     have to support both setting the selection with the selected slot
     of val contents and setting it with the "value" slot of val.  The latter
     has a higher priority.  This to be removed when the kernel is fixed. */
  for (cur = val->contents; cur; cur = cur->next)
    {
      toggle = XtNameToWidget (widget, cur->value);
      if (toggle)
	{
	  Arg al [2];
	  XtSetArg (al [0], XmNsensitive, cur->enabled);
	  XtSetArg (al [1], XmNset, (!val->value && cur->selected ? cur->selected : False));
	  XtSetValues (toggle, al, 2);
	}
    }

  /* The selected was specified by the value slot */
  if (val->value)
    {
      toggle = XtNameToWidget (widget, val->value);
      if (toggle)
	{
	  Arg al [1];
	  XtSetArg (al [0], XmNset, True);
	  XtSetValues (toggle, al, 1);
	}
    }
}

#if defined (LWLIB_WIDGETS_MOTIF) && XmVERSION > 1
/* update of combo box */
static void
xm_update_combo_box (widget_instance* instance, Widget widget, widget_value* val)
{
  widget_value* cur;
  int i;
  XtRemoveAllCallbacks (widget, XmNselectionCallback);
  XtAddCallback (widget, XmNselectionCallback, xm_generic_callback,
		 instance);
  for (cur = val->contents, i = 0; cur; cur = cur->next)
    if (cur->value)
      {
	XmString xmstr = XmStringCreate (cur->value, XmSTRING_DEFAULT_CHARSET);
	i += 1;
	XmListAddItem (CB_List (widget), xmstr, 0);
	if (cur->selected)
	  XmListSelectPos (CB_List (widget), i, False);
	XmStringFree (xmstr);
      }
}
#endif

#ifdef LWLIB_MENUBARS_MOTIF

/* update a popup menu, pulldown menu or a menubar */
static void
make_menu_in_widget (widget_instance* instance, Widget widget,
		     widget_value* val)
{
  Widget* children = 0;
  int num_children;
  int child_index;
  widget_value* cur;
  Widget button = 0;
  Widget menu;
  Arg al [256];
  int ac;
  Boolean menubar_p = False;

  /* Allocate the children array */
  for (num_children = 0, cur = val; cur; num_children++, cur = cur->next);
  children = (Widget*)XtMalloc (num_children * sizeof (Widget));

  /* tricky way to know if this RowColumn is a menubar or a pulldown... */
  XtSetArg (al [0], XmNisHomogeneous, &menubar_p);
  XtGetValues (widget, al, 1);

  /* add the unmap callback for popups and pulldowns */
  /*** this sounds bogus ***/
  /* probably because it is -- cet */
/*
  if (!menubar_p)
    XtAddCallback (XtParent (widget), XmNpopdownCallback,
		   xm_pop_down_callback, (XtPointer)instance);
*/

  num_children = 0;
  for (child_index = 0, cur = val; cur; child_index++, cur = cur->next)
    {
      ac = 0;
      button = 0;
      XtSetArg (al [ac], XmNsensitive, cur->enabled);		ac++;
      XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING);	ac++;
      XtSetArg (al [ac], XmNuserData, cur->call_data);		ac++;

      switch (cur->type)
	{
	case PUSHRIGHT_TYPE:
	  /* A pushright marker which is not needed for the real Motif
             menubar. */
	  break;
	case SEPARATOR_TYPE:
	  ac = 0;
	  if (cur->value)
	    {
	      /* #### - xlwmenu.h supports several types that motif does
		 not.  Also, motif supports pixmaps w/ type NO_LINE and
		 lwlib provides no way to access that functionality. --Stig */
	      XtSetArg (al [ac], XmNseparatorType, cur->value), ac++;
	    }
	  button = XmCreateSeparator (widget, "separator", al, ac);
	  break;
	case CASCADE_TYPE:
	  menu = XmCreatePulldownMenu (widget, "pulldown", NULL, 0);
	  make_menu_in_widget (instance, menu, cur->contents);
	  XtSetArg (al [ac], XmNsubMenuId, menu); ac++;
	  button = XmCreateCascadeButton (widget, cur->name, al, ac);

	  xm_update_label (instance, button, cur);

	  XtAddCallback (button, XmNcascadingCallback, xm_pull_down_callback,
			 (XtPointer)instance);
	  break;
	default:
	  if (menubar_p)
	    button = XmCreateCascadeButton (widget, cur->name, al, ac);
	  else if (!cur->call_data)
	    button = XmCreateLabel (widget, cur->name, al, ac);
	  else if (cur->type == TOGGLE_TYPE || cur->type == RADIO_TYPE)
	    {
	      XtSetArg (al [ac], XmNindicatorType,
			(cur->type == TOGGLE_TYPE ?
			 XmN_OF_MANY : XmONE_OF_MANY));    ac++;
	      XtSetArg (al [ac], XmNvisibleWhenOff, True); ac++;
	      button = XmCreateToggleButtonGadget (widget, cur->name, al, ac);
	    }
	  else
	    button = XmCreatePushButtonGadget (widget, cur->name, al, ac);

	  xm_update_label (instance, button, cur);

	  /* don't add a callback to a simple label */
	  if (cur->type == TOGGLE_TYPE || cur->type == RADIO_TYPE)
	    xm_update_toggle (instance, button, cur);
	  else if (cur->call_data)
	    XtAddCallback (button, XmNactivateCallback, xm_generic_callback,
			   (XtPointer)instance);
	} /* switch (cur->type) */

      if (button)
	children [num_children++] = button;
    }

  /* Last entry is the help button.  This used be done after managing
     the buttons.  The comment claimed that it had to be done this way
     otherwise the menubar ended up only 4 pixels high.  That must
     have been in the Old World.  In the New World it stays the proper
     height if you don't manage them until after you set this and as a
     bonus the Help menu ends up where it is supposed to. */
  if (button)
    {
      ac = 0;
      XtSetArg (al [ac], XmNmenuHelpWidget, button); ac++;
      XtSetValues (widget, al, ac);
    }

  if (num_children)
    XtManageChildren (children, num_children);

  XtFree ((char *) children);
}

static void
update_one_menu_entry (widget_instance* instance, Widget widget,
		       widget_value* val, Boolean deep_p)
{
  Arg al [2];
  int ac;
  Widget menu;
  widget_value* contents;

  if (val->change == NO_CHANGE)
    return;

  /* update the sensitivity and userdata */
  /* Common to all widget types */
  XtSetArg (al [0], XmNsensitive, val->enabled);
  XtSetArg (al [1], XmNuserData,  val->call_data);
  XtSetValues (widget, al, 2);

  /* update the menu button as a label. */
  if (val->change >= VISIBLE_CHANGE)
    {
      xm_update_label (instance, widget, val);
      if (XtClass (widget) == xmToggleButtonWidgetClass
	  || XtClass (widget) == xmToggleButtonGadgetClass)
	{
	  xm_update_toggle (instance, widget, val);
	}
    }


  /* update the pulldown/pullaside as needed */
  menu = NULL;
  XtSetArg (al [0], XmNsubMenuId, &menu);
  XtGetValues (widget, al, 1);

  contents = val->contents;

  if (!menu)
    {
      if (contents)
	{
	  menu = XmCreatePulldownMenu (widget, "pulldown", NULL, 0);
	  make_menu_in_widget (instance, menu, contents);
	  ac = 0;
	  XtSetArg (al [ac], XmNsubMenuId, menu); ac++;
	  XtSetValues (widget, al, ac);
	}
    }
  else if (!contents)
    {
      ac = 0;
      XtSetArg (al [ac], XmNsubMenuId, NULL); ac++;
      XtSetValues (widget, al, ac);
      XtDestroyWidget (menu);
    }
  else if (deep_p && contents->change != NO_CHANGE)
    xm_update_menu (instance, menu, val, 1);
}

static void
xm_update_menu (widget_instance* instance, Widget widget, widget_value* val,
		Boolean deep_p)
{
  /* Widget is a RowColumn widget whose contents have to be updated
   * to reflect the list of items in val->contents */
  if (val->contents->change == STRUCTURAL_CHANGE)
    {
      destroy_all_children (widget);
      make_menu_in_widget (instance, widget, val->contents);
    }
  else
    {
      /* Update all the buttons of the RowColumn in order. */
      Widget* children;
      unsigned int num_children;
      int i;
      widget_value *cur = 0;

      children = XtCompositeChildren (widget, &num_children);
      if (children)
	{
	  for (i = 0, cur = val->contents; i < num_children; i++)
	    {
	      if (!cur)
		abort ();
	      /* skip if this is a pushright marker or a separator */
	      if (cur->type == PUSHRIGHT_TYPE || cur->type == SEPARATOR_TYPE)
		{
		  cur = cur->next;
#if 0
		  /* #### - this could puke if you have a separator as the
		     last item on a pullright menu. */
		  if (!cur)
		    abort ();
#else
		  if (!cur)
		    continue;
#endif
		}
	      if (children [i]->core.being_destroyed
		  || strcmp (XtName (children [i]), cur->name))
		continue;
	      update_one_menu_entry (instance, children [i], cur, deep_p);
	      cur = cur->next;
	    }
	  XtFree ((char *) children);
	}
      if (cur)
	abort ();
    }
}

#endif /* LWLIB_MENUBARS_MOTIF */


/* update text widgets */

static void
xm_update_text (widget_instance* instance, Widget widget, widget_value* val)
{
  XmTextSetString (widget, val->value ? val->value : (char *) "");
  XtRemoveAllCallbacks (widget, XmNactivateCallback);
  XtAddCallback (widget, XmNactivateCallback, xm_generic_callback, instance);
  XtRemoveAllCallbacks (widget, XmNvalueChangedCallback);
  XtAddCallback (widget, XmNvalueChangedCallback,
		 xm_internal_update_other_instances, instance);
}

static void
xm_update_text_field (widget_instance* instance, Widget widget,
		      widget_value* val)
{
  XmTextFieldSetString (widget, val->value ? val->value : (char *) "");
  XtRemoveAllCallbacks (widget, XmNactivateCallback);
  XtAddCallback (widget, XmNactivateCallback, xm_generic_callback, instance);
  XtRemoveAllCallbacks (widget, XmNvalueChangedCallback);
  XtAddCallback (widget, XmNvalueChangedCallback,
		 xm_internal_update_other_instances, instance);
}


#ifdef LWLIB_SCROLLBARS_MOTIF

/*
 * If this function looks like it does a lot more work than it needs to,
 * you're right.  Blame the Motif scrollbar for not being smart about
 * updating its appearance.
 */
static void
xm_update_scrollbar (widget_instance *instance, Widget widget,
		     widget_value *val)
{
  if (val->scrollbar_data)
    {
      scrollbar_values *data = val->scrollbar_data;
      int widget_sliderSize, widget_val;
      int new_sliderSize, new_value;
      double percent;
      double h_water, l_water;
      Arg al [4];

      /* First size and position the scrollbar widget. */
      XtSetArg (al [0], XtNx,      data->scrollbar_x);
      XtSetArg (al [1], XtNy,      data->scrollbar_y);
      XtSetArg (al [2], XtNwidth,  data->scrollbar_width);
      XtSetArg (al [3], XtNheight, data->scrollbar_height);
      XtSetValues (widget, al, 4);

      /* Now size the scrollbar's slider. */
      XtSetArg (al [0], XmNsliderSize, &widget_sliderSize);
      XtSetArg (al [1], XmNvalue,      &widget_val);
      XtGetValues (widget, al, 2);

      percent = (double) data->slider_size /
	(double) (data->maximum - data->minimum);
      new_sliderSize = (int) ((double) (INT_MAX - 1) * percent);

      percent = (double) (data->slider_position - data->minimum) /
	(double) (data->maximum - data->minimum);
      new_value = (int) ((double) (INT_MAX - 1) * percent);

      if (new_sliderSize > (INT_MAX - 1))
	new_sliderSize = INT_MAX - 1;
      else if (new_sliderSize < 1)
	new_sliderSize = 1;

      if (new_value > (INT_MAX - new_sliderSize))
	new_value = INT_MAX - new_sliderSize;
      else if (new_value < 1)
	new_value = 1;

      h_water = 1.05;
      l_water = 0.95;
      if (new_sliderSize != widget_sliderSize || new_value != widget_val)
	{
	  int force = ((INT_MAX - widget_sliderSize - widget_val)
		       ? 0
		       : (INT_MAX - new_sliderSize - new_value));

	  if (force
	      || (double)new_sliderSize < (l_water * (double)widget_sliderSize)
	      || (double)new_sliderSize > (h_water * (double)widget_sliderSize)
	      || (double)new_value < (l_water * (double)widget_val)
	      || (double)new_value > (h_water * (double)widget_val))
	    {
	      XmScrollBarSetValues (widget, new_value, new_sliderSize, 1, 1,
				    False);
	    }
	}
    }
}

#endif /* LWLIB_SCROLLBARS_MOTIF */


/* update a motif widget */

void
xm_update_one_widget (widget_instance* instance, Widget widget,
		      widget_value* val, Boolean deep_p)
{
  WidgetClass class;
  Arg al [20];
  int ac = 0;

  /* Mark as not edited */
  val->edited = False;

  /* Common to all widget types */
  XtSetArg (al [ac], XmNsensitive, val->enabled);		ac++;
  XtSetArg (al [ac], XmNuserData,  val->call_data);	ac++;
  XtSetValues (widget, al, ac);

#if defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_MENUBARS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF)
  /* Common to all label like widgets */
  if (XtIsSubclass (widget, xmLabelWidgetClass))
    xm_update_label (instance, widget, val);
#endif
  class = XtClass (widget);
  /* Class specific things */
  if (class == xmPushButtonWidgetClass ||
      class == xmArrowButtonWidgetClass)
    {
      xm_update_pushbutton (instance, widget, val);
    }
#ifdef LWLIB_MENUBARS_MOTIF
  else if (class == xmCascadeButtonWidgetClass)
    {
      xm_update_cascadebutton (instance, widget, val);
    }
#endif
  else if (class == xmToggleButtonWidgetClass
	   || class == xmToggleButtonGadgetClass)
    {
      xm_update_toggle (instance, widget, val);
    }
  else if (class == xmRowColumnWidgetClass)
    {
      Boolean radiobox = 0;

      XtSetArg (al [0], XmNradioBehavior, &radiobox);
      XtGetValues (widget, al, 1);

      if (radiobox)
	xm_update_radiobox (instance, widget, val);
#ifdef LWLIB_MENUBARS_MOTIF
      else
	xm_update_menu (instance, widget, val, deep_p);
#endif
    }
  else if (class == xmTextWidgetClass)
    {
      xm_update_text (instance, widget, val);
    }
  else if (class == xmTextFieldWidgetClass)
    {
      xm_update_text_field (instance, widget, val);
    }
  else if (class == xmListWidgetClass)
    {
      xm_update_list (instance, widget, val);
    }
#if defined (LWLIB_WIDGETS_MOTIF) && XmVERSION > 1
  else if (class == xmComboBoxWidgetClass)
    {
      xm_update_combo_box (instance, widget, val);
    }
#endif
#ifdef LWLIB_SCROLLBARS_MOTIF
  else if (class == xmScrollBarWidgetClass)
    {
      xm_update_scrollbar (instance, widget, val);
    }
#endif
  else if (class == xmScaleWidgetClass)
    {
      xm_update_progress (instance, widget, val);
    }
  /* Lastly update our global arg values. */
  if (val->args && val->args->nargs)
    XtSetValues (widget, val->args->args, val->args->nargs);
}

/* getting the value back */
void
xm_update_one_value (widget_instance* instance, Widget widget,
		     widget_value* val)
{
  WidgetClass class = XtClass (widget);
  widget_value *old_wv;

  /* copy the call_data slot into the "return" widget_value */
  for (old_wv = instance->info->val->contents; old_wv; old_wv = old_wv->next)
    if (!strcmp (val->name, old_wv->name))
      {
	val->call_data = old_wv->call_data;
	break;
      }

  if (class == xmToggleButtonWidgetClass || class == xmToggleButtonGadgetClass)
    {
      Arg al [1];
      XtSetArg (al [0], XmNset, &val->selected);
      XtGetValues (widget, al, 1);
      val->edited = True;
    }
  else if (class == xmTextWidgetClass)
    {
      if (val->value)
	XtFree (val->value);
      val->value = XmTextGetString (widget);
      val->edited = True;
    }
  else if (class == xmTextFieldWidgetClass)
    {
      if (val->value)
	XtFree (val->value);
      val->value = XmTextFieldGetString (widget);
      val->edited = True;
    }
  else if (class == xmRowColumnWidgetClass)
    {
      Boolean radiobox = 0;
      {
	Arg al [1];
	XtSetArg (al [0], XmNradioBehavior, &radiobox);
	XtGetValues (widget, al, 1);
      }

      if (radiobox)
	{
	  CompositeWidget radio = (CompositeWidget)widget;
	  unsigned int i;
	  for (i = 0; i < radio->composite.num_children; i++)
	    {
	      int set = False;
	      Widget toggle = radio->composite.children [i];
	      Arg al [1];

	      XtSetArg (al [0], XmNset, &set);
	      XtGetValues (toggle, al, 1);
	      if (set)
		{
		  if (val->value)
		    free (val->value);
		  val->value = safe_strdup (XtName (toggle));
		}
	    }
	  val->edited = True;
	}
    }
  else if (class == xmListWidgetClass
#if defined (LWLIB_WIDGETS_MOTIF) && XmVERSION > 1
	   || class == xmComboBoxWidgetClass
#endif
	   )
    {
      int pos_cnt;
      int* pos_list;
      Widget list = widget;
#if defined (LWLIB_WIDGETS_MOTIF) && XmVERSION > 1
      if (class == xmComboBoxWidgetClass)
	list = CB_List (widget);
#endif
      if (XmListGetSelectedPos (list, &pos_list, &pos_cnt))
	{
	  int i;
	  widget_value* cur;
	  for (cur = val->contents, i = 0; cur; cur = cur->next)
	    if (cur->value)
	      {
		int j;
		cur->selected = False;
		i += 1;
		for (j = 0; j < pos_cnt; j++)
		  if (pos_list [j] == i)
		    {
		      cur->selected = True;
		      val->value = safe_strdup (cur->name);
		    }
	      }
	  val->edited = 1;
	  XtFree ((char *) pos_list);
	}
    }
#ifdef LWLIB_SCROLLBARS_MOTIF
  else if (class == xmScrollBarWidgetClass)
    {
      /* This function is not used by the scrollbar. */
      return;
    }
#endif
}


/* This function is for activating a button from a program.  It's wrong because
   we pass a NULL argument in the call_data which is not Motif compatible.
   This is used from the XmNdefaultAction callback of the List widgets to
   have a double-click put down a dialog box like the button would do.
   I could not find a way to do that with accelerators.
 */
static void
activate_button (Widget widget, XtPointer closure, XtPointer call_data)
{
  Widget button = (Widget)closure;
  XtCallCallbacks (button, XmNactivateCallback, NULL);
}

/* creation functions */

#ifdef LWLIB_DIALOGS_MOTIF

/* dialogs */

#if (XmVersion >= 1002)
# define ARMANDACTIVATE_KLUDGE
# define DND_KLUDGE
#endif

#ifdef ARMANDACTIVATE_KLUDGE
 /* We want typing Return at a dialog box to select the default button; but
    we're satisfied with having it select the leftmost button instead.

    In Motif 1.1.5 we could do this by putting this resource in the
    app-defaults file:

	*dialog*button1.accelerators:#override\
	<KeyPress>Return: ArmAndActivate()\n\
	<KeyPress>KP_Enter: ArmAndActivate()\n\
	Ctrl<KeyPress>m: ArmAndActivate()\n

    but that doesn't work with 1.2.1 and I don't understand why. However,
    doing the equivalent C code does work, with the notable disadvantage that
    the user can't override it.  So that's what we do until we figure out
    something better....
  */
static char button_trans[] = "\
<KeyPress>Return: ArmAndActivate()\n\
<KeyPress>KP_Enter: ArmAndActivate()\n\
Ctrl<KeyPress>m: ArmAndActivate()\n";

#endif /* ARMANDACTIVATE_KLUDGE */


#ifdef DND_KLUDGE
 /* This is a kludge to disable drag-and-drop in dialog boxes.  The symptom
    was a segv down in libXm somewhere if you used the middle button on a
    dialog box to begin a drag; when you released the button to make a drop
    things would lose if you were not over the button where you started the
    drag (canceling the operation).  This was probably due to the fact that
    the dialog boxes were not set up to handle a drag but were trying to do
    so anyway for some reason.

    So we disable drag-and-drop in dialog boxes by turning off the binding for
    Btn2Down which, by default, initiates a drag.  Clearly this is a shitty
    solution as it only works in default configurations, but...
  */
static char disable_dnd_trans[] = "<Btn2Down>: ";
#endif /* DND_KLUDGE */


static Widget
make_dialog (char* name, Widget parent, Boolean pop_up_p,
	     const char* shell_title, const char* icon_name,
	     Boolean text_input_slot, Boolean radio_box, Boolean list,
	     int left_buttons, int right_buttons)
{
  Widget result;
  Widget form;
  Widget row;
  Widget icon;
  Widget icon_separator;
  Widget message;
  Widget value = 0;
  Widget separator;
  Widget button = 0;
  Widget children [16];		/* for the final XtManageChildren */
  int	n_children;
  Arg 	al[64];			/* Arg List */
  int 	ac;			/* Arg Count */
  int 	i;

#ifdef DND_KLUDGE
  XtTranslations dnd_override = XtParseTranslationTable (disable_dnd_trans);
# define DO_DND_KLUDGE(widget) XtOverrideTranslations ((widget), dnd_override)
#else  /* ! DND_KLUDGE */
# define DO_DND_KLUDGE(widget)
#endif /* ! DND_KLUDGE */

  if (pop_up_p)
    {
      ac = 0;
      XtSetArg(al[ac], XmNtitle, shell_title);		ac++;
      XtSetArg(al[ac], XtNallowShellResize, True);	ac++;
      XtSetArg(al[ac], XmNdeleteResponse, XmUNMAP);	ac++;
      result = XmCreateDialogShell (parent, "dialog", al, ac);

      XtSetArg(al[ac], XmNautoUnmanage, FALSE);		ac++;
/*      XtSetArg(al[ac], XmNautoUnmanage, TRUE); ac++; */ /* ####is this ok? */
      XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
      form = XmCreateForm (result, (char *) shell_title, al, ac);
    }
  else
    {
      ac = 0;
      XtSetArg(al[ac], XmNautoUnmanage, FALSE);		ac++;
      XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
      form = XmCreateForm (parent, (char *) shell_title, al, ac);
      result = form;
    }

  ac = 0;
  XtSetArg(al[ac], XmNpacking, XmPACK_COLUMN);		ac++;
  XtSetArg(al[ac], XmNorientation, XmVERTICAL);		ac++;
  XtSetArg(al[ac], XmNnumColumns, left_buttons + right_buttons + 1); ac++;
  XtSetArg(al[ac], XmNmarginWidth, 0);			ac++;
  XtSetArg(al[ac], XmNmarginHeight, 0);			ac++;
  XtSetArg(al[ac], XmNspacing, 13);			ac++;
  XtSetArg(al[ac], XmNadjustLast, False);		ac++;
  XtSetArg(al[ac], XmNalignment, XmALIGNMENT_CENTER);	ac++;
  XtSetArg(al[ac], XmNisAligned, True);			ac++;
  XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE);	ac++;
  XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_FORM);	ac++;
  XtSetArg(al[ac], XmNbottomOffset, 13);		ac++;
  XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM);	ac++;
  XtSetArg(al[ac], XmNleftOffset, 13);			ac++;
  XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM);	ac++;
  XtSetArg(al[ac], XmNrightOffset, 13);			ac++;
  row = XmCreateRowColumn (form, "row", al, ac);

  n_children = 0;
  for (i = 0; i < left_buttons; i++)
    {
      char button_name [16];
      sprintf (button_name, "button%d", i + 1);
      ac = 0;
      if (i == 0)
	{
	  XtSetArg(al[ac], XmNhighlightThickness, 1); ac++;
	  XtSetArg(al[ac], XmNshowAsDefault, TRUE);   ac++;
	}
      XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
      children [n_children] = XmCreatePushButton (row, button_name, al, ac);
      DO_DND_KLUDGE (children [n_children]);

      if (i == 0)
	{
	  button = children [n_children];
	  ac = 0;
	  XtSetArg(al[ac], XmNdefaultButton, button); ac++;
	  XtSetValues (row, al, ac);

#ifdef ARMANDACTIVATE_KLUDGE	/* See comment above */
	  {
	    XtTranslations losers = XtParseTranslationTable (button_trans);
	    XtOverrideTranslations (button, losers);
	    XtFree ((char *) losers);
	  }
#endif /* ARMANDACTIVATE_KLUDGE */
	}

      n_children++;
    }

  /* invisible separator button */
  ac = 0;
  XtSetArg (al[ac], XmNmappedWhenManaged, FALSE); ac++;
  children [n_children] = XmCreateLabel (row, "separator_button",
					 al, ac);
  DO_DND_KLUDGE (children [n_children]);
  n_children++;

  for (i = 0; i < right_buttons; i++)
    {
      char button_name [16];
      sprintf (button_name, "button%d", left_buttons + i + 1);
      ac = 0;
      XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
      children [n_children] = XmCreatePushButton (row, button_name, al, ac);
      DO_DND_KLUDGE (children [n_children]);
      if (! button) button = children [n_children];
      n_children++;
    }

  XtManageChildren (children, n_children);

  ac = 0;
  XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE);		ac++;
  XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET);	ac++;
  XtSetArg(al[ac], XmNbottomOffset, 13);			ac++;
  XtSetArg(al[ac], XmNbottomWidget, row);			ac++;
  XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM);		ac++;
  XtSetArg(al[ac], XmNleftOffset, 0);				ac++;
  XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM);		ac++;
  XtSetArg(al[ac], XmNrightOffset, 0);				ac++;
  separator = XmCreateSeparator (form, "", al, ac);

  ac = 0;
  XtSetArg(al[ac], XmNlabelType, XmPIXMAP);			ac++;
  XtSetArg(al[ac], XmNtopAttachment, XmATTACH_FORM);		ac++;
  XtSetArg(al[ac], XmNtopOffset, 13);				ac++;
  XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_NONE);		ac++;
  XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM);		ac++;
  XtSetArg(al[ac], XmNleftOffset, 13);				ac++;
  XtSetArg(al[ac], XmNrightAttachment, XmATTACH_NONE);		ac++;
  icon = XmCreateLabel (form, (char *) icon_name, al, ac);
  DO_DND_KLUDGE (icon);

  ac = 0;
  XtSetArg(al[ac], XmNmappedWhenManaged, FALSE);		ac++;
  XtSetArg(al[ac], XmNtopAttachment, XmATTACH_WIDGET);		ac++;
  XtSetArg(al[ac], XmNtopOffset, 6);				ac++;
  XtSetArg(al[ac], XmNtopWidget, icon);				ac++;
  XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET);	ac++;
  XtSetArg(al[ac], XmNbottomOffset, 6);				ac++;
  XtSetArg(al[ac], XmNbottomWidget, separator);			ac++;
  XtSetArg(al[ac], XmNleftAttachment, XmATTACH_NONE);		ac++;
  XtSetArg(al[ac], XmNrightAttachment, XmATTACH_NONE);		ac++;
  icon_separator = XmCreateLabel (form, "", al, ac);
  DO_DND_KLUDGE (icon_separator);

  if (text_input_slot)
    {
      ac = 0;
      XtSetArg(al[ac], XmNcolumns, 50);				ac++;
      XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE);	ac++;
      XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET);	ac++;
      XtSetArg(al[ac], XmNbottomOffset, 13);			ac++;
      XtSetArg(al[ac], XmNbottomWidget, separator);		ac++;
      XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET);	ac++;
      XtSetArg(al[ac], XmNleftOffset, 13);			ac++;
      XtSetArg(al[ac], XmNleftWidget, icon); 			ac++;
      XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM);	ac++;
      XtSetArg(al[ac], XmNrightOffset, 13);			ac++;
      value = XmCreateTextField (form, "value", al, ac);
      DO_DND_KLUDGE (value);
    }
  else if (radio_box)
    {
      Widget radio_butt;
      ac = 0;
      XtSetArg(al[ac], XmNmarginWidth, 0);			ac++;
      XtSetArg(al[ac], XmNmarginHeight, 0);			ac++;
      XtSetArg(al[ac], XmNspacing, 13);				ac++;
      XtSetArg(al[ac], XmNalignment, XmALIGNMENT_CENTER);	ac++;
      XtSetArg(al[ac], XmNorientation, XmHORIZONTAL);		ac++;
      XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET);	ac++;
      XtSetArg(al[ac], XmNbottomOffset, 13);			ac++;
      XtSetArg(al[ac], XmNbottomWidget, separator);		ac++;
      XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET);	ac++;
      XtSetArg(al[ac], XmNleftOffset, 13);			ac++;
      XtSetArg(al[ac], XmNleftWidget, icon);			ac++;
      XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM);	ac++;
      XtSetArg(al[ac], XmNrightOffset, 13);			ac++;
      value = XmCreateRadioBox (form, "radiobutton1", al, ac);
      ac = 0;
      i = 0;
      radio_butt = XmCreateToggleButtonGadget (value, "radio1", al, ac);
      children [i++] = radio_butt;
      radio_butt = XmCreateToggleButtonGadget (value, "radio2", al, ac);
      children [i++] = radio_butt;
      radio_butt = XmCreateToggleButtonGadget (value, "radio3", al, ac);
      children [i++] = radio_butt;
      XtManageChildren (children, i);
    }
  else if (list)
    {
      ac = 0;
      XtSetArg(al[ac], XmNvisibleItemCount, 5);			ac++;
      XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE);	ac++;
      XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET);	ac++;
      XtSetArg(al[ac], XmNbottomOffset, 13);			ac++;
      XtSetArg(al[ac], XmNbottomWidget, separator);		ac++;
      XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET);	ac++;
      XtSetArg(al[ac], XmNleftOffset, 13);			ac++;
      XtSetArg(al[ac], XmNleftWidget, icon);			ac++;
      XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM);	ac++;
      XtSetArg(al[ac], XmNrightOffset, 13);			ac++;
      value = XmCreateScrolledList (form, "list", al, ac);

      /* this is the easiest way I found to have the double click in the
	 list activate the default button */
      XtAddCallback (value, XmNdefaultActionCallback, activate_button, button);
    }
  /* else add nothing; it's a separator */

  ac = 0;
  XtSetArg(al[ac], XmNalignment, XmALIGNMENT_BEGINNING);	ac++;
  XtSetArg(al[ac], XmNtopAttachment, XmATTACH_FORM);		ac++;
  XtSetArg(al[ac], XmNtopOffset, 13);				ac++;
  XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET);	ac++;
  XtSetArg(al[ac], XmNbottomOffset, 13);			ac++;
  XtSetArg(al[ac], XmNbottomWidget,
	   text_input_slot || radio_box || list ? value : separator); ac++;
  XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET);		ac++;
  XtSetArg(al[ac], XmNleftOffset, 13);				ac++;
  XtSetArg(al[ac], XmNleftWidget, icon);			ac++;
  XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM);		ac++;
  XtSetArg(al[ac], XmNrightOffset, 13);				ac++;
  message = XmCreateLabel (form, "message", al, ac);
  DO_DND_KLUDGE (message);

  if (list)
    XtManageChild (value);

  i = 0;
  children [i] = row; i++;
  children [i] = separator; i++;
  if (text_input_slot || radio_box)
    {
      children [i] = value; i++;
    }
  children [i] = message; i++;
  children [i] = icon; i++;
  children [i] = icon_separator; i++;
  XtManageChildren (children, i);

  if (text_input_slot || list)
    {
      XtInstallAccelerators (value, button);
      XmProcessTraversal(value, XmTRAVERSE_CURRENT);
    }
  else if (radio_box)
    {
      XtInstallAccelerators (form, button);
      XmProcessTraversal(value, XmTRAVERSE_CURRENT);
    }
  /* else we don' need no STEENKIN' assellerators. */

#ifdef DND_KLUDGE
  XtFree ((char *) dnd_override);
#endif
#undef DO_DND_KLUDGE

  return result;
}

static destroyed_instance*
find_matching_instance (widget_instance* instance)
{
  destroyed_instance*	cur;
  destroyed_instance*	prev;
  char*	type = instance->info->type;
  char*	name = instance->info->name;

  for (prev = NULL, cur = all_destroyed_instances;
       cur;
       prev = cur, cur = cur->next)
    {
      if (!strcmp (cur->name, name)
	  && !strcmp (cur->type, type)
	  && cur->parent == instance->parent
	  && cur->pop_up_p == instance->pop_up_p)
	{
	  if (prev)
	    prev->next = cur->next;
	  else
	    all_destroyed_instances = cur->next;
	  return cur;
	}
      /* do some cleanup */
      else if (!cur->widget)
	{
	  if (prev)
	    prev->next = cur->next;
	  else
	    all_destroyed_instances = cur->next;
	  free_destroyed_instance (cur);
	  cur = prev ? prev : all_destroyed_instances;
	}
    }
  return NULL;
}

static void
recenter_widget (Widget widget)
{
  Widget parent = XtParent (widget);
  Screen* screen = XtScreen (widget);
  Dimension screen_width = WidthOfScreen (screen);
  Dimension screen_height = HeightOfScreen (screen);
  Dimension parent_width = 0;
  Dimension parent_height = 0;
  Dimension child_width = 0;
  Dimension child_height = 0;
  Position x;
  Position y;
  Arg al [2];

  XtSetArg (al [0], XtNwidth,  &child_width);
  XtSetArg (al [1], XtNheight, &child_height);
  XtGetValues (widget, al, 2);

  XtSetArg (al [0], XtNwidth,  &parent_width);
  XtSetArg (al [1], XtNheight, &parent_height);
  XtGetValues (parent, al, 2);

  x = (Position) ((parent_width  - child_width)  / 2);
  y = (Position) ((parent_height - child_height) / 2);

  XtTranslateCoords (parent, x, y, &x, &y);

  if ((Dimension) (x + child_width) > screen_width)
    x = screen_width - child_width;
  if (x < 0)
    x = 0;

  if ((Dimension) (y + child_height) > screen_height)
    y = screen_height - child_height;
  if (y < 0)
    y = 0;

  XtSetArg (al [0], XtNx, x);
  XtSetArg (al [1], XtNy, y);
  XtSetValues (widget, al, 2);
}

static Widget
recycle_instance (destroyed_instance* instance)
{
  Widget widget = instance->widget;

  /* widget is NULL if the parent was destroyed. */
  if (widget)
    {
      Widget focus;
      Widget separator;

      /* Remove the destroy callback as the instance is not in the list
	 anymore */
      XtRemoveCallback (instance->parent, XtNdestroyCallback,
			mark_dead_instance_destroyed,
			(XtPointer)instance);

      /* Give the focus to the initial item */
      focus = XtNameToWidget (widget, "*value");
      if (!focus)
	focus = XtNameToWidget (widget, "*button1");
      if (focus)
	XmProcessTraversal(focus, XmTRAVERSE_CURRENT);

      /* shrink the separator label back to their original size */
      separator = XtNameToWidget (widget, "*separator_button");
      if (separator)
	{
	  Arg al [2];
	  XtSetArg (al [0], XtNwidth,  5);
	  XtSetArg (al [1], XtNheight, 5);
	  XtSetValues (separator, al, 2);
	}

      /* Center the dialog in its parent */
      recenter_widget (widget);
    }
  free_destroyed_instance (instance);
  return widget;
}

Widget
xm_create_dialog (widget_instance* instance)
{
  char* 	name = instance->info->type;
  Widget 	parent = instance->parent;
  Widget	widget;
  Boolean 	pop_up_p = instance->pop_up_p;
  const char*	shell_name = 0;
  const char* 	icon_name = 0;
  Boolean	text_input_slot = False;
  Boolean	radio_box = False;
  Boolean	list = False;
  int		total_buttons;
  int		left_buttons = 0;
  int		right_buttons = 1;
  destroyed_instance*	dead_one;

  /* try to find a widget to recycle */
  dead_one = find_matching_instance (instance);
  if (dead_one)
    {
      Widget recycled_widget = recycle_instance (dead_one);
      if (recycled_widget)
	return recycled_widget;
    }

  switch (name [0]){
  case 'E': case 'e':
    icon_name = "dbox-error";
    shell_name = "Error";
    break;

  case 'I': case 'i':
    icon_name = "dbox-info";
    shell_name = "Information";
    break;

  case 'L': case 'l':
    list = True;
    icon_name = "dbox-question";
    shell_name = "Prompt";
    break;

  case 'P': case 'p':
    text_input_slot = True;
    icon_name = "dbox-question";
    shell_name = "Prompt";
    break;

  case 'Q': case 'q':
    icon_name = "dbox-question";
    shell_name = "Question";
    break;
  }

  total_buttons = name [1] - '0';

  if (name [3] == 'T' || name [3] == 't')
    {
      text_input_slot = False;
      radio_box = True;
    }
  else if (name [3])
    right_buttons = name [4] - '0';

  left_buttons = total_buttons - right_buttons;

  widget = make_dialog (name, parent, pop_up_p,
			shell_name, icon_name, text_input_slot, radio_box,
			list, left_buttons, right_buttons);

  XtAddCallback (widget, XmNpopdownCallback, xm_nosel_callback,
		 (XtPointer) instance);
  return widget;
}

#endif /* LWLIB_DIALOGS_MOTIF */

#ifdef LWLIB_MENUBARS_MOTIF
static Widget
make_menubar (widget_instance* instance)
{
  Arg al[10];
  int ac = 0;

  XtSetArg(al[ac], XmNmarginHeight, 0);	   ac++;
  XtSetArg(al[ac], XmNshadowThickness, 3); ac++;

  return XmCreateMenuBar (instance->parent, instance->info->name, al, ac);
}

static void
remove_grabs (Widget shell, XtPointer closure, XtPointer call_data)
{
  Widget menu = (Widget) closure;
  XmRemoveFromPostFromList (menu, XtParent (XtParent ((Widget) menu)));
}

static Widget
make_popup_menu (widget_instance* instance)
{
  Widget parent = instance->parent;
  Window parent_window = parent->core.window;
  Widget result;

  /* sets the parent window to 0 to fool Motif into not generating a grab */
  parent->core.window = 0;
  result = XmCreatePopupMenu (parent, instance->info->name, NULL, 0);
  XtAddCallback (XtParent (result), XmNpopdownCallback, remove_grabs,
		 (XtPointer)result);
  parent->core.window = parent_window;
  return result;
}
#endif /* LWLIB_MENUBARS_MOTIF */

#ifdef LWLIB_SCROLLBARS_MOTIF
static Widget
make_scrollbar (widget_instance *instance, int vertical)
{
  Arg al[20];
  int ac = 0;
  static XtCallbackRec callbacks[2] =
  { {xm_scrollbar_callback, NULL}, {NULL, NULL} };

  callbacks[0].closure  = (XtPointer) instance;

  XtSetArg (al[ac], XmNminimum,       1); ac++;
  XtSetArg (al[ac], XmNmaximum, INT_MAX); ac++;
  XtSetArg (al[ac], XmNincrement,     1); ac++;
  XtSetArg (al[ac], XmNpageIncrement, 1); ac++;
  XtSetArg (al[ac], XmNborderWidth,   0); ac++;
  XtSetArg (al[ac], XmNorientation, vertical ? XmVERTICAL : XmHORIZONTAL); ac++;

  XtSetArg (al[ac], XmNdecrementCallback,	callbacks); ac++;
  XtSetArg (al[ac], XmNdragCallback,		callbacks); ac++;
  XtSetArg (al[ac], XmNincrementCallback,	callbacks); ac++;
  XtSetArg (al[ac], XmNpageDecrementCallback,	callbacks); ac++;
  XtSetArg (al[ac], XmNpageIncrementCallback,	callbacks); ac++;
  XtSetArg (al[ac], XmNtoBottomCallback,	callbacks); ac++;
  XtSetArg (al[ac], XmNtoTopCallback,		callbacks); ac++;
  XtSetArg (al[ac], XmNvalueChangedCallback,	callbacks); ac++;

  return XmCreateScrollBar (instance->parent, instance->info->name, al, ac);
}

static Widget
make_vertical_scrollbar (widget_instance *instance)
{
  return make_scrollbar (instance, 1);
}

static Widget
make_horizontal_scrollbar (widget_instance *instance)
{
  return make_scrollbar (instance, 0);
}

#endif /* LWLIB_SCROLLBARS_MOTIF */

#ifdef LWLIB_WIDGETS_MOTIF
/* glyph widgets */
static Widget
xm_create_button (widget_instance *instance)
{
  Arg al[20];
  int ac = 0;
  Widget button = 0;
  widget_value* val = instance->info->val;

  XtSetArg (al [ac], XmNsensitive, val->enabled);		ac++;
  XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING);	ac++;
  XtSetArg (al [ac], XmNuserData, val->call_data);		ac++;
  XtSetArg (al [ac], XmNmappedWhenManaged, FALSE);	ac++;
  /* The highlight doesn't appear to be dynamically set which makes it
     look ugly.  I think this may be a LessTif bug but for now we just
     get rid of it. */
  XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;

  /* add any args the user supplied for creation time */
  lw_add_value_args_to_args (val, al, &ac);

  if (!val->call_data)
    button = XmCreateLabel (instance->parent, val->name, al, ac);

  else if (val->type == TOGGLE_TYPE || val->type == RADIO_TYPE)
    {
      XtSetArg (al [ac], XmNset, val->selected);	ac++;
      XtSetArg (al [ac], XmNindicatorType,
		(val->type == TOGGLE_TYPE ?
		 XmN_OF_MANY : XmONE_OF_MANY));    ac++;
      XtSetArg (al [ac], XmNvisibleWhenOff, True); ac++;
      button = XmCreateToggleButton (instance->parent, val->name, al, ac);
      XtRemoveAllCallbacks (button, XmNvalueChangedCallback);
      XtAddCallback (button, XmNvalueChangedCallback, xm_generic_callback,
		     (XtPointer)instance);
    }
  else
    {
      button = XmCreatePushButton (instance->parent, val->name, al, ac);
      XtAddCallback (button, XmNactivateCallback, xm_generic_callback,
		     (XtPointer)instance);
    }

  XtManageChild (button);

  return button;
}

static Widget
xm_create_progress (widget_instance *instance)
{
  Arg al[20];
  int ac = 0;
  Dimension height = 0;
  Dimension width = 0;
  Widget scale = 0;
  widget_value* val = instance->info->val;
  if (!val->call_data)
    {
      XtSetArg (al [ac], XmNeditable, False);		ac++;
    }
  else
    {
      XtSetArg (al [ac], XmNeditable, val->enabled);		ac++;
    }
  XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING);	ac++;
  XtSetArg (al [ac], XmNuserData, val->call_data);		ac++;
  XtSetArg (al [ac], XmNmappedWhenManaged, FALSE);	ac++;
  XtSetArg (al [ac], XmNorientation, XmHORIZONTAL);	ac++;
  /* The highlight doesn't appear to be dynamically set which makes it
     look ugly.  I think this may be a LessTif bug but for now we just
     get rid of it. */
  XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;
  
  height = (Dimension)lw_get_value_arg (val, XtNheight);
  width = (Dimension)lw_get_value_arg (val, XtNwidth);
  if (height > 0)
    {
      XtSetArg (al [ac], XmNscaleHeight, height); ac++;
    }
  if (width > 0)
    {
      XtSetArg (al [ac], XmNscaleWidth, width); ac++;
    }

  /* add any args the user supplied for creation time */
  lw_add_value_args_to_args (val, al, &ac);

  scale = XmCreateScale (instance->parent, val->name, al, ac);
  if (val->call_data)
    XtAddCallback (scale, XmNvalueChangedCallback, xm_generic_callback,
		   (XtPointer)instance);

  XtManageChild (scale);

  return scale;
}

static Widget
xm_create_text_field (widget_instance *instance)
{
  Arg al[20];
  int ac = 0;
  Widget text = 0;
  widget_value* val = instance->info->val;

  XtSetArg (al [ac], XmNsensitive, val->enabled);		ac++;
  XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING);	ac++;
  XtSetArg (al [ac], XmNuserData, val->call_data);		ac++;
  XtSetArg (al [ac], XmNmappedWhenManaged, FALSE);	ac++;
  /* The highlight doesn't appear to be dynamically set which makes it
     look ugly.  I think this may be a LessTif bug but for now we just
     get rid of it. */
  XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;

  /* add any args the user supplied for creation time */
  lw_add_value_args_to_args (val, al, &ac);

  text = XmCreateTextField (instance->parent, val->name, al, ac);
  if (val->call_data)
    XtAddCallback (text, XmNvalueChangedCallback, xm_generic_callback,
		   (XtPointer)instance);

  XtManageChild (text);

  return text;
}

static Widget
xm_create_label_field (widget_instance *instance)
{
  return xm_create_label (instance->parent, instance->info->val);
}

Widget
xm_create_label (Widget parent, widget_value* val)
{
  Arg al[20];
  int ac = 0;
  Widget label = 0;

  XtSetArg (al [ac], XmNsensitive, val->enabled);		ac++;
  XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING);	ac++;
  XtSetArg (al [ac], XmNmappedWhenManaged, FALSE);	ac++;
  /* The highlight doesn't appear to be dynamically set which makes it
     look ugly.  I think this may be a LessTif bug but for now we just
     get rid of it. */
  XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;

  /* add any args the user supplied for creation time */
  lw_add_value_args_to_args (val, al, &ac);

  label = XmCreateLabel (parent, val->name, al, ac);

  XtManageChild (label);

  /* Do it again for arguments that have no effect until the widget is realized. */
  ac = 0;
  lw_add_value_args_to_args (val, al, &ac);
  XtSetValues (label, al, ac);

  return label;
}

#if XmVERSION > 1
static Widget
xm_create_combo_box (widget_instance *instance)
{
  Arg al[20];
  int ac = 0;
  Widget combo = 0;
  widget_value* val = instance->info->val;

  XtSetArg (al [ac], XmNsensitive, val->enabled);		ac++;
  XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING);	ac++;
  XtSetArg (al [ac], XmNuserData, val->call_data);		ac++;
  XtSetArg (al [ac], XmNmappedWhenManaged, FALSE);	ac++;
  /* The highlight doesn't appear to be dynamically set which makes it
     look ugly.  I think this may be a LessTif bug but for now we just
     get rid of it. */
  XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;

  /* add any args the user supplied for creation time */
  lw_add_value_args_to_args (val, al, &ac);

  combo = XmCreateDropDownComboBox (instance->parent, val->name, al, ac);
  if (val->call_data)
    XtAddCallback (combo, XmNselectionCallback, xm_generic_callback,
		   (XtPointer)instance);

  XtManageChild (combo);

  return combo;
}
#endif
#endif /* LWLIB_WIDGETS_MOTIF */


/* Table of functions to create widgets */

const widget_creation_entry
xm_creation_table [] =
{
#ifdef LWLIB_MENUBARS_MOTIF
  {"menubar", 			make_menubar},
  {"popup",			make_popup_menu},
#endif
#ifdef LWLIB_SCROLLBARS_MOTIF
  {"vertical-scrollbar",	make_vertical_scrollbar},
  {"horizontal-scrollbar",	make_horizontal_scrollbar},
#endif
#ifdef LWLIB_WIDGETS_MOTIF
  {"button",		xm_create_button},
  {"progress",		xm_create_progress},
  {"text-field",		xm_create_text_field},
  {"label",		xm_create_label_field},
#if XmVERSION > 1
  {"combo-box",		xm_create_combo_box},
#endif
#endif
  {NULL, NULL}
};

/* Destruction of instances */
void
xm_destroy_instance (widget_instance* instance)
{
#if defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF)
  /* It appears that this is used only for dialog boxes. */
  Widget widget = instance->widget;
  /* recycle the dialog boxes */
  /* Disable the recycling until we can find a way to have the dialog box
     get reasonable layout after we modify its contents. */
  if (0
      && XtClass (widget) == xmDialogShellWidgetClass)
    {
      destroyed_instance* dead_instance =
	make_destroyed_instance (instance->info->name,
				 instance->info->type,
				 instance->widget,
				 instance->parent,
				 instance->pop_up_p);
      dead_instance->next = all_destroyed_instances;
      all_destroyed_instances = dead_instance;
      XtUnmanageChild (first_child (instance->widget));
      XFlush (XtDisplay (instance->widget));
      XtAddCallback (instance->parent, XtNdestroyCallback,
		     mark_dead_instance_destroyed, (XtPointer)dead_instance);
    }
  else
    {
      /* This might not be necessary now that the nosel is attached to
	 popdown instead of destroy, but it can't hurt. */
      XtRemoveCallback (instance->widget, XtNdestroyCallback,
			xm_nosel_callback, (XtPointer)instance);

      XtDestroyWidget (instance->widget);
    }
#endif /* LWLIB_DIALOGS_MOTIF || LWLIB_WIDGETS_MOTIF */
}

/* popup utility */
#ifdef LWLIB_MENUBARS_MOTIF

void
xm_popup_menu (Widget widget, XEvent *event)
{
  if (event->type == ButtonPress || event->type == ButtonRelease)
    {
      /* This is so totally ridiculous: there's NO WAY to tell Motif
	 that *any* button can select a menu item.  Only one button
	 can have that honor.
       */
      char *trans = 0;
      if      (event->xbutton.state & Button5Mask) trans = "<Btn5Down>";
      else if (event->xbutton.state & Button4Mask) trans = "<Btn4Down>";
      else if (event->xbutton.state & Button3Mask) trans = "<Btn3Down>";
      else if (event->xbutton.state & Button2Mask) trans = "<Btn2Down>";
      else if (event->xbutton.state & Button1Mask) trans = "<Btn1Down>";
      if (trans)
	{
	  Arg al [1];
	  XtSetArg (al [0], XmNmenuPost, trans);
	  XtSetValues (widget, al, 1);
	}
      XmMenuPosition (widget, (XButtonPressedEvent *) event);
    }
  XtManageChild (widget);
}

#endif

#ifdef LWLIB_DIALOGS_MOTIF

static void
set_min_dialog_size (Widget w)
{
  short width;
  short height;
  Arg al [2];

  XtSetArg (al [0], XmNwidth,  &width);
  XtSetArg (al [1], XmNheight, &height);
  XtGetValues (w, al, 2);

  XtSetArg (al [0], XmNminWidth,  width);
  XtSetArg (al [1], XmNminHeight, height);
  XtSetValues (w, al, 2);
}

#endif

void
xm_pop_instance (widget_instance* instance, Boolean up)
{
  Widget widget = instance->widget;

#ifdef LWLIB_DIALOGS_MOTIF
  if (XtClass (widget) == xmDialogShellWidgetClass)
    {
      Widget widget_to_manage = first_child (widget);
      if (up)
	{
	  XtManageChild (widget_to_manage);
	  set_min_dialog_size (widget);
	  XmProcessTraversal(widget, XmTRAVERSE_CURRENT);
	}
      else
	XtUnmanageChild (widget_to_manage);
    }
  else
#endif
    {
      if (up)
	XtManageChild (widget);
      else
	XtUnmanageChild (widget);
    }
}


/* motif callback */

enum do_call_type { pre_activate, selection, no_selection, post_activate };

static void
do_call (Widget widget, XtPointer closure, enum do_call_type type)
{
  XtPointer user_data;
  widget_instance* instance = (widget_instance*)closure;
  Widget instance_widget;
  LWLIB_ID id;
  Arg al [1];

  if (!instance)
    return;
  if (widget->core.being_destroyed)
    return;

  instance_widget = instance->widget;
  if (!instance_widget)
    return;

  id = instance->info->id;
  user_data = NULL;
  XtSetArg(al [0], XmNuserData, &user_data);
  XtGetValues (widget, al, 1);
  switch (type)
    {
    case pre_activate:
      if (instance->info->pre_activate_cb)
	instance->info->pre_activate_cb (widget, id, user_data);
      break;
    case selection:
      if (instance->info->selection_cb)
	instance->info->selection_cb (widget, id, user_data);
      break;
    case no_selection:
      if (instance->info->selection_cb)
	instance->info->selection_cb (widget, id, (XtPointer) -1);
      break;
    case post_activate:
      if (instance->info->post_activate_cb)
	instance->info->post_activate_cb (widget, id, user_data);
      break;
    default:
      abort ();
    }
}

/* Like lw_internal_update_other_instances except that it does not do
   anything if its shell parent is not managed.  This is to protect
   lw_internal_update_other_instances to dereference freed memory
   if the widget was ``destroyed'' by caching it in the all_destroyed_instances
   list */
static void
xm_internal_update_other_instances (Widget widget, XtPointer closure,
				    XtPointer call_data)
{
  Widget parent;
  for (parent = widget; parent; parent = XtParent (parent))
    if (XtIsShell (parent))
      break;
    else if (!XtIsManaged (parent))
      return;
   lw_internal_update_other_instances (widget, closure, call_data);
}

static void
xm_generic_callback (Widget widget, XtPointer closure, XtPointer call_data)
{
#if (defined (LWLIB_MENUBARS_MOTIF) || defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF))
  /* We want the selected status to change only when we decide it
     should change.  Yuck but correct. */
  if (XtClass (widget) == xmToggleButtonWidgetClass
      || XtClass (widget) == xmToggleButtonGadgetClass)
    {
      Boolean check;
      Arg al [1];

      XtSetArg (al [0], XmNset, &check);
      XtGetValues (widget, al, 1);

      XtSetArg (al [0], XmNset, !check);
      XtSetValues (widget, al, 1);
    }
#endif
  lw_internal_update_other_instances (widget, closure, call_data);
  do_call (widget, closure, selection);
}

static void
xm_pop_down_callback (Widget widget, XtPointer closure, XtPointer call_data)
{
  do_call (widget, closure, post_activate);
}

#ifdef LWLIB_MENUBARS_MOTIF

static void
xm_pull_down_callback (Widget widget, XtPointer closure, XtPointer call_data)
{
#if 0
  if (call_data)
    {
      /* new behavior for incremental menu construction */

    }
  else
#endif
    do_call (widget, closure, pre_activate);
}

#endif /* LWLIB_MENUBARS_MOTIF */

#ifdef LWLIB_SCROLLBARS_MOTIF
static void
xm_scrollbar_callback (Widget widget, XtPointer closure, XtPointer call_data)
{
  widget_instance *instance = (widget_instance *) closure;
  LWLIB_ID id;
  XmScrollBarCallbackStruct *data =
    (XmScrollBarCallbackStruct *) call_data;
  scroll_event event_data;
  scrollbar_values *val =
    (scrollbar_values *) instance->info->val->scrollbar_data;
  double percent;

  if (!instance || widget->core.being_destroyed)
    return;

  id = instance->info->id;

  percent = (double) (data->value - 1) / (double) (INT_MAX - 1);
  event_data.slider_value =
    (int) (percent * (double) (val->maximum - val->minimum)) + val->minimum;

  if (event_data.slider_value > (val->maximum - val->slider_size))
    event_data.slider_value = val->maximum - val->slider_size;
  else if (event_data.slider_value < 1)
    event_data.slider_value = 1;

  if (data->event)
    {
      switch (data->event->xany.type)
	{
	case KeyPress:
	case KeyRelease:
	  event_data.time = data->event->xkey.time;
	  break;
	case ButtonPress:
	case ButtonRelease:
	  event_data.time = data->event->xbutton.time;
	  break;
	case MotionNotify:
	  event_data.time = data->event->xmotion.time;
	  break;
	case EnterNotify:
	case LeaveNotify:
	  event_data.time = data->event->xcrossing.time;
	  break;
	default:
	  event_data.time = 0;
	  break;
	}
    }
  else
    event_data.time = 0;

  switch (data->reason)
    {
    case XmCR_DECREMENT:
      event_data.action = SCROLLBAR_LINE_UP;
      break;
    case XmCR_INCREMENT:
      event_data.action = SCROLLBAR_LINE_DOWN;
      break;
    case XmCR_PAGE_DECREMENT:
      event_data.action = SCROLLBAR_PAGE_UP;
      break;
    case XmCR_PAGE_INCREMENT:
      event_data.action = SCROLLBAR_PAGE_DOWN;
      break;
    case XmCR_TO_TOP:
      event_data.action = SCROLLBAR_TOP;
      break;
    case XmCR_TO_BOTTOM:
      event_data.action = SCROLLBAR_BOTTOM;
      break;
    case XmCR_DRAG:
      event_data.action = SCROLLBAR_DRAG;
      break;
    case XmCR_VALUE_CHANGED:
      event_data.action = SCROLLBAR_CHANGE;
      break;
    default:
      event_data.action = SCROLLBAR_CHANGE;
      break;
    }

  if (instance->info->pre_activate_cb)
    instance->info->pre_activate_cb (widget, id, (XtPointer) &event_data);
}
#endif /* LWLIB_SCROLLBARS_MOTIF */

#if defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF)
static void
mark_dead_instance_destroyed (Widget widget, XtPointer closure,
			      XtPointer call_data)
{
  destroyed_instance* instance = (destroyed_instance*)closure;
  instance->widget = NULL;
}

static void
xm_nosel_callback (Widget widget, XtPointer closure, XtPointer call_data)
{
  /* This callback is only called when a dialog box is dismissed with the wm's
     destroy button (WM_DELETE_WINDOW.)  We want the dialog box to be destroyed
     in that case, not just unmapped, so that it releases its keyboard grabs.
     But there are problems with running our callbacks while the widget is in
     the process of being destroyed, so we set XmNdeleteResponse to XmUNMAP
     instead of XmDESTROY and then destroy it ourself after having run the
     callback.
   */
  do_call (widget, closure, no_selection);
  XtDestroyWidget (widget);
}
#endif


/* set the keyboard focus */
void
xm_set_keyboard_focus (Widget parent, Widget w)
{
  XmProcessTraversal (w, XmTRAVERSE_CURRENT);
  /* At some point we believed that it was necessary to use XtSetKeyboardFocus
     instead of XmProcessTraversal when using Motif >= 1.2.1, but that's bogus.
     Presumably the problem was elsewhere, and is now gone...
   */
}