1/* The lwlib interface to Motif widgets.
2   Copyright (C) 1994, 1995, 1996, 1997, 1999, 2000, 2001, 2002, 2003,
3                 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
4   Copyright (C) 1992 Lucid, Inc.
5
6This file is part of the Lucid Widget Library.
7
8The Lucid Widget Library is free software; you can redistribute it and/or
9modify it under the terms of the GNU General Public License as published by
10the Free Software Foundation; either version 1, or (at your option)
11any later version.
12
13The Lucid Widget Library is distributed in the hope that it will be useful,
14but WITHOUT ANY WARRANTY; without even the implied warranty of
15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16GNU General Public License for more details.
17
18You should have received a copy of the GNU General Public License
19along with GNU Emacs; see the file COPYING.  If not, write to
20the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21Boston, MA 02110-1301, USA.  */
22
23#ifdef HAVE_CONFIG_H
24#include <config.h>
25#endif
26
27#include <unistd.h>
28#include <stdio.h>
29
30#include <X11/StringDefs.h>
31#include <X11/IntrinsicP.h>
32#include <X11/ObjectP.h>
33#include <X11/CoreP.h>
34#include <X11/CompositeP.h>
35
36#include "../src/lisp.h"
37
38#include "lwlib-Xm.h"
39#include "lwlib-utils.h"
40
41#include <Xm/BulletinB.h>
42#include <Xm/CascadeB.h>
43#include <Xm/CascadeBG.h>
44#include <Xm/DrawingA.h>
45#include <Xm/FileSB.h>
46#include <Xm/Label.h>
47#include <Xm/List.h>
48#include <Xm/MainW.h>
49#include <Xm/MenuShell.h>
50#include <Xm/MessageB.h>
51#include <Xm/PanedW.h>
52#include <Xm/PushB.h>
53#include <Xm/PushBG.h>
54#include <Xm/ArrowB.h>
55#include <Xm/SelectioB.h>
56#include <Xm/Text.h>
57#include <Xm/TextF.h>
58#include <Xm/ToggleB.h>
59#include <Xm/ToggleBG.h>
60#include <Xm/RowColumn.h>
61#include <Xm/ScrolledW.h>
62#include <Xm/Separator.h>
63#include <Xm/DialogS.h>
64#include <Xm/Form.h>
65
66#undef P_
67#if defined __STDC__ || defined PROTOTYPES
68#define P_(X) X
69#else
70#define P_(X) ()
71#endif
72
73enum do_call_type { pre_activate, selection, no_selection, post_activate };
74
75
76/* Structures to keep destroyed instances */
77typedef struct _destroyed_instance
78{
79  char*		name;
80  char*		type;
81  Widget 	widget;
82  Widget	parent;
83  Boolean	pop_up_p;
84  struct _destroyed_instance*	next;
85} destroyed_instance;
86
87static destroyed_instance *make_destroyed_instance P_ ((char *, char *,
88							Widget, Widget,
89							Boolean));
90static void free_destroyed_instance P_ ((destroyed_instance*));
91Widget first_child P_ ((Widget));
92Boolean lw_motif_widget_p P_ ((Widget));
93static XmString resource_motif_string P_ ((Widget, char *));
94static void destroy_all_children P_ ((Widget, int));
95static void xm_update_label P_ ((widget_instance *, Widget, widget_value *));
96static void xm_update_list P_ ((widget_instance *, Widget, widget_value *));
97static void xm_update_pushbutton P_ ((widget_instance *, Widget,
98				      widget_value *));
99static void xm_update_cascadebutton P_ ((widget_instance *, Widget,
100					 widget_value *));
101static void xm_update_toggle P_ ((widget_instance *, Widget, widget_value *));
102static void xm_update_radiobox P_ ((widget_instance *, Widget, widget_value *));
103static void make_menu_in_widget P_ ((widget_instance *, Widget,
104				     widget_value *, int));
105static void update_one_menu_entry P_ ((widget_instance *, Widget,
106				       widget_value *, Boolean));
107static void xm_update_menu P_ ((widget_instance *, Widget, widget_value *,
108				Boolean));
109static void xm_update_text P_ ((widget_instance *, Widget, widget_value *));
110static void xm_update_text_field P_ ((widget_instance *, Widget,
111				      widget_value *));
112void xm_update_one_value P_ ((widget_instance *, Widget, widget_value *));
113static void activate_button P_ ((Widget, XtPointer, XtPointer));
114static Widget make_dialog P_ ((char *, Widget, Boolean, char *, char *,
115			       Boolean, Boolean, Boolean, int, int));
116static destroyed_instance* find_matching_instance P_ ((widget_instance*));
117static void mark_dead_instance_destroyed P_ ((Widget, XtPointer, XtPointer));
118static void recenter_widget P_ ((Widget));
119static Widget recycle_instance P_ ((destroyed_instance*));
120Widget xm_create_dialog P_ ((widget_instance*));
121static Widget make_menubar P_ ((widget_instance*));
122static void remove_grabs P_ ((Widget, XtPointer, XtPointer));
123static Widget make_popup_menu P_ ((widget_instance*));
124static Widget make_main P_ ((widget_instance*));
125void xm_destroy_instance P_ ((widget_instance*));
126void xm_popup_menu P_ ((Widget, XEvent *));
127static void set_min_dialog_size P_ ((Widget));
128static void do_call P_ ((Widget, XtPointer, enum do_call_type));
129static void xm_generic_callback P_ ((Widget, XtPointer, XtPointer));
130static void xm_nosel_callback P_ ((Widget, XtPointer, XtPointer));
131static void xm_pull_down_callback P_ ((Widget, XtPointer, XtPointer));
132static void xm_pop_down_callback P_ ((Widget, XtPointer, XtPointer));
133void xm_set_keyboard_focus P_ ((Widget, Widget));
134void xm_set_main_areas P_ ((Widget, Widget, Widget));
135static void xm_internal_update_other_instances P_ ((Widget, XtPointer,
136						    XtPointer));
137static void xm_arm_callback P_ ((Widget, XtPointer, XtPointer));
138
139#if 0
140void xm_update_one_widget P_ ((widget_instance *, Widget, widget_value *,
141			       Boolean));
142void xm_pop_instance P_ ((widget_instance*, Boolean));
143void xm_manage_resizing P_ ((Widget, Boolean));
144#endif
145
146
147#if 0
148
149/* Print the complete X resource name of widget WIDGET to stderr.
150   This is sometimes handy to have available.  */
151
152void
153x_print_complete_resource_name (widget)
154     Widget widget;
155{
156  int i;
157  String names[100];
158
159  for (i = 0; i < 100 && widget != NULL; ++i)
160    {
161      names[i] = XtName (widget);
162      widget = XtParent (widget);
163    }
164
165  for (--i; i >= 1; --i)
166    fprintf (stderr, "%s.", names[i]);
167  fprintf (stderr, "%s\n", names[0]);
168}
169
170#endif /* 0 */
171
172
173static destroyed_instance *all_destroyed_instances = NULL;
174
175static destroyed_instance*
176make_destroyed_instance (name, type, widget, parent, pop_up_p)
177     char* name;
178     char* type;
179     Widget widget;
180     Widget parent;
181     Boolean pop_up_p;
182{
183  destroyed_instance* instance =
184    (destroyed_instance*)malloc (sizeof (destroyed_instance));
185  instance->name = safe_strdup (name);
186  instance->type = safe_strdup (type);
187  instance->widget = widget;
188  instance->parent = parent;
189  instance->pop_up_p = pop_up_p;
190  instance->next = NULL;
191  return instance;
192}
193
194static void
195free_destroyed_instance (instance)
196     destroyed_instance* instance;
197{
198  free (instance->name);
199  free (instance->type);
200  free (instance);
201}
202
203/* motif utility functions */
204Widget
205first_child (widget)
206     Widget widget;
207{
208  return ((CompositeWidget)widget)->composite.children [0];
209}
210
211Boolean
212lw_motif_widget_p (widget)
213     Widget widget;
214{
215  return
216    XtClass (widget) == xmDialogShellWidgetClass
217      || XmIsPrimitive (widget) || XmIsManager (widget) || XmIsGadget (widget);
218}
219
220static XmString
221resource_motif_string (widget, name)
222     Widget widget;
223     char* name;
224{
225  XtResource resource;
226  XmString result = 0;
227
228  resource.resource_name = name;
229  resource.resource_class = XmCXmString;
230  resource.resource_type = XmRXmString;
231  resource.resource_size = sizeof (XmString);
232  resource.resource_offset = 0;
233  resource.default_type = XtRImmediate;
234  resource.default_addr = 0;
235
236  XtGetSubresources (widget, (XtPointer)&result, "dialogString",
237		     "DialogString", &resource, 1, NULL, 0);
238  return result;
239}
240
241/* Destroy all of the children of WIDGET
242   starting with number FIRST_CHILD_TO_DESTROY.  */
243
244static void
245destroy_all_children (widget, first_child_to_destroy)
246     Widget widget;
247     int first_child_to_destroy;
248{
249  Widget* children;
250  unsigned int number;
251  int i;
252
253  children = XtCompositeChildren (widget, &number);
254  if (children)
255    {
256      XtUnmanageChildren (children + first_child_to_destroy,
257			  number - first_child_to_destroy);
258
259      /* Unmanage all children and destroy them.  They will only be
260	 really destroyed when we get out of DispatchEvent.  */
261      for (i = first_child_to_destroy; i < number; i++)
262	{
263	  Arg al[2];
264	  Widget submenu = 0;
265	  /* Cascade buttons have submenus,and these submenus
266	     need to be freed.  But they are not included in
267	     XtCompositeChildren.  So get it out of the cascade button
268	     and free it.  If this child is not a cascade button,
269	     then submenu should remain unchanged.  */
270	  XtSetArg (al[0], XmNsubMenuId, &submenu);
271  	  XtGetValues (children[i], al, 1);
272	  if (submenu)
273            {
274              destroy_all_children (submenu, 0);
275              XtDestroyWidget (submenu);
276            }
277	  XtDestroyWidget (children[i]);
278	}
279
280      XtFree ((char *) children);
281    }
282}
283
284
285
286/* Callback XmNarmCallback and XmNdisarmCallback for buttons in a
287   menu.  CLIENT_DATA contains a pointer to the widget_value
288   corresponding to widget W.  CALL_DATA contains a
289   XmPushButtonCallbackStruct containing the reason why the callback
290   is called.  */
291
292static void
293xm_arm_callback (w, client_data, call_data)
294     Widget w;
295     XtPointer client_data, call_data;
296{
297  XmPushButtonCallbackStruct *cbs = (XmPushButtonCallbackStruct *) call_data;
298  widget_value *wv = (widget_value *) client_data;
299  widget_instance *instance;
300
301  /* Get the id of the menu bar or popup menu this widget is in.  */
302  while (w != NULL)
303    {
304      if (XmIsRowColumn (w))
305	{
306	  unsigned char type = 0xff;
307
308	  XtVaGetValues (w, XmNrowColumnType, &type, NULL);
309	  if (type == XmMENU_BAR || type == XmMENU_POPUP)
310	    break;
311	}
312
313      w = XtParent (w);
314    }
315
316  if (w != NULL)
317    {
318      instance = lw_get_widget_instance (w);
319      if (instance && instance->info->highlight_cb)
320	{
321	  call_data = cbs->reason == XmCR_DISARM ? NULL : wv;
322	  instance->info->highlight_cb (w, instance->info->id, call_data);
323	}
324    }
325}
326
327
328
329/* Update the label of widget WIDGET.  WIDGET must be a Label widget
330   or a subclass of Label.  WIDGET_INSTANCE is unused.  VAL contains
331   the value to update.
332
333   Menus:
334
335   Emacs fills VAL->name with the text to display in the menu, and
336   sets VAL->value to null.  Function make_menu_in_widget creates
337   widgets with VAL->name as resource name.  This works because the
338   Label widget uses its resource name for display if no
339   XmNlabelString is set.
340
341   Dialogs:
342
343   VAL->name is again set to the resource name, but VAL->value is
344   not null, and contains the label string to display.  */
345
346static void
347xm_update_label (instance, widget, val)
348     widget_instance* instance;
349     Widget widget;
350     widget_value* val;
351{
352  XmString res_string = 0;
353  XmString built_string = 0;
354  XmString key_string = 0;
355  Arg al [256];
356  int ac;
357
358  ac = 0;
359
360  if (val->value)
361    {
362      /* A label string is specified, i.e. we are in a dialog.  First
363	 see if it is overridden by something from the resource file.  */
364      res_string = resource_motif_string (widget, val->value);
365
366      if (res_string)
367	{
368	  XtSetArg (al [ac], XmNlabelString, res_string); ac++;
369	}
370      else
371	{
372	  built_string =
373	    XmStringCreateLocalized (val->value);
374	  XtSetArg (al [ac], XmNlabelString, built_string); ac++;
375	}
376
377      XtSetArg (al [ac], XmNlabelType, XmSTRING); ac++;
378    }
379
380  if (val->key)
381    {
382      key_string = XmStringCreateLocalized (val->key);
383      XtSetArg (al [ac], XmNacceleratorText, key_string); ac++;
384    }
385
386  if (ac)
387    XtSetValues (widget, al, ac);
388
389  if (built_string)
390    XmStringFree (built_string);
391
392  if (key_string)
393    XmStringFree (key_string);
394}
395
396/* update of list */
397static void
398xm_update_list (instance, widget, val)
399     widget_instance* instance;
400     Widget widget;
401     widget_value* val;
402{
403  widget_value* cur;
404  int i;
405  XtRemoveAllCallbacks (widget, XmNsingleSelectionCallback);
406  XtAddCallback (widget, XmNsingleSelectionCallback, xm_generic_callback,
407		 instance);
408  for (cur = val->contents, i = 0; cur; cur = cur->next)
409    if (cur->value)
410      {
411	XmString xmstr = XmStringCreateLocalized (cur->value);
412	i += 1;
413	XmListAddItem (widget, xmstr, 0);
414	if (cur->selected)
415	  XmListSelectPos (widget, i, False);
416	XmStringFree (xmstr);
417      }
418}
419
420/* update of buttons */
421static void
422xm_update_pushbutton (instance, widget, val)
423     widget_instance* instance;
424     Widget widget;
425     widget_value* val;
426{
427  XtVaSetValues (widget, XmNalignment, XmALIGNMENT_CENTER, NULL);
428  XtRemoveAllCallbacks (widget, XmNactivateCallback);
429  XtAddCallback (widget, XmNactivateCallback, xm_generic_callback, instance);
430}
431
432static void
433xm_update_cascadebutton (instance, widget, val)
434     widget_instance* instance;
435     Widget widget;
436     widget_value* val;
437{
438  /* Should also rebuild the menu by calling ...update_menu... */
439  XtRemoveAllCallbacks (widget, XmNcascadingCallback);
440  XtAddCallback (widget, XmNcascadingCallback, xm_pull_down_callback,
441		 instance);
442}
443
444/* update toggle and radiobox */
445static void
446xm_update_toggle (instance, widget, val)
447     widget_instance* instance;
448     Widget widget;
449     widget_value* val;
450{
451  XtRemoveAllCallbacks (widget, XmNvalueChangedCallback);
452  XtAddCallback (widget, XmNvalueChangedCallback,
453		 xm_generic_callback, instance);
454  XtVaSetValues (widget, XmNset, val->selected,
455		 XmNalignment, XmALIGNMENT_BEGINNING, NULL);
456}
457
458static void
459xm_update_radiobox (instance, widget, val)
460     widget_instance* instance;
461     Widget widget;
462     widget_value* val;
463
464{
465  Widget toggle;
466  widget_value* cur;
467
468  /* update the callback */
469  XtRemoveAllCallbacks (widget, XmNentryCallback);
470  XtAddCallback (widget, XmNentryCallback, xm_generic_callback, instance);
471
472  /* first update all the toggles */
473  /* Energize kernel interface is currently bad.  It sets the selected widget
474     with the selected flag but returns it by its name.  So we currently
475     have to support both setting the selection with the selected slot
476     of val contents and setting it with the "value" slot of val.  The latter
477     has a higher priority.  This to be removed when the kernel is fixed. */
478  for (cur = val->contents; cur; cur = cur->next)
479    {
480      toggle = XtNameToWidget (widget, cur->value);
481      if (toggle)
482	{
483	  XtSetSensitive (toggle, cur->enabled);
484	  if (!val->value && cur->selected)
485	    XtVaSetValues (toggle, XmNset, cur->selected, NULL);
486	  if (val->value && strcmp (val->value, cur->value))
487	    XtVaSetValues (toggle, XmNset, False, NULL);
488	}
489    }
490
491  /* The selected was specified by the value slot */
492  if (val->value)
493    {
494      toggle = XtNameToWidget (widget, val->value);
495      if (toggle)
496	XtVaSetValues (toggle, XmNset, True, NULL);
497    }
498}
499
500
501/* update a popup menu, pulldown menu or a menubar */
502
503/* KEEP_FIRST_CHILDREN gives the number of initial children to keep.  */
504
505static void
506make_menu_in_widget (instance, widget, val, keep_first_children)
507     widget_instance* instance;
508     Widget widget;
509     widget_value* val;
510     int keep_first_children;
511{
512  Widget* children = 0;
513  int num_children;
514  int child_index;
515  widget_value* cur;
516  Widget button = 0;
517  Widget title = 0;
518  Widget menu;
519  Arg al [256];
520  int ac;
521  Boolean menubar_p;
522  unsigned char type;
523
524  Widget* old_children;
525  unsigned int old_num_children;
526
527  /* Disable drag and drop for labels in menu bar.  */
528  static char overrideTrans[] = "<Btn2Down>: Noop()";
529  XtTranslations override = XtParseTranslationTable (overrideTrans);
530
531  old_children = XtCompositeChildren (widget, &old_num_children);
532
533  /* Allocate the children array */
534  for (num_children = 0, cur = val; cur; num_children++, cur = cur->next)
535    ;
536  children = (Widget*)XtMalloc (num_children * sizeof (Widget));
537
538  /* WIDGET should be a RowColumn.  */
539  if (!XmIsRowColumn (widget))
540    abort ();
541
542  /* Determine whether WIDGET is a menu bar.  */
543  type = -1;
544  XtSetArg (al[0], XmNrowColumnType, &type);
545  XtGetValues (widget, al, 1);
546  if (type != XmMENU_BAR && type != XmMENU_PULLDOWN && type != XmMENU_POPUP)
547    abort ();
548  menubar_p = type == XmMENU_BAR;
549
550  /* Add a callback to popups and pulldowns that is called when
551     it is made invisible again.  */
552  if (!menubar_p)
553    XtAddCallback (XtParent (widget), XmNpopdownCallback,
554		   xm_pop_down_callback, (XtPointer)instance);
555
556  /* Preserve the first KEEP_FIRST_CHILDREN old children.  */
557  for (child_index = 0, cur = val; child_index < keep_first_children;
558       child_index++, cur = cur->next)
559    children[child_index] = old_children[child_index];
560
561  /* Check that those are all we have
562     (the caller should have deleted the rest).  */
563  if (old_num_children != keep_first_children)
564    abort ();
565
566  /* Create the rest.  */
567  for (child_index = keep_first_children; cur; child_index++, cur = cur->next)
568    {
569      enum menu_separator separator;
570
571      ac = 0;
572      XtSetArg (al[ac], XmNsensitive, cur->enabled); ac++;
573      XtSetArg (al[ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
574      XtSetArg (al[ac], XmNuserData, cur->call_data); ac++;
575
576      if (instance->pop_up_p && !cur->contents && !cur->call_data
577	  && !lw_separator_p (cur->name, &separator, 1))
578	{
579	  ac = 0;
580	  XtSetArg (al[ac], XmNalignment, XmALIGNMENT_CENTER); ac++;
581	  title = button = XmCreateLabel (widget, cur->name, al, ac);
582	}
583      else if (lw_separator_p (cur->name, &separator, 1))
584	{
585	  ac = 0;
586	  XtSetArg (al[ac], XmNseparatorType, separator); ++ac;
587	  button = XmCreateSeparator (widget, cur->name, al, ac);
588	}
589      else if (!cur->contents)
590	{
591	  if (menubar_p)
592	    button = XmCreateCascadeButton (widget, cur->name, al, ac);
593	  else if (!cur->call_data)
594	    button = XmCreateLabel (widget, cur->name, al, ac);
595	  else if (cur->button_type == BUTTON_TYPE_TOGGLE
596		   || cur->button_type == BUTTON_TYPE_RADIO)
597	    {
598	      XtSetArg (al[ac], XmNset, cur->selected); ++ac;
599	      XtSetArg (al[ac], XmNvisibleWhenOff, True); ++ac;
600	      XtSetArg (al[ac], XmNindicatorType,
601			(cur->button_type == BUTTON_TYPE_TOGGLE
602			 ? XmN_OF_MANY : XmONE_OF_MANY));
603	      ++ac;
604	      button = XmCreateToggleButton (widget, cur->name, al, ac);
605	      XtAddCallback (button, XmNarmCallback, xm_arm_callback, cur);
606	      XtAddCallback (button, XmNdisarmCallback, xm_arm_callback, cur);
607	    }
608	  else
609	    {
610	      button = XmCreatePushButton (widget, cur->name, al, ac);
611	      XtAddCallback (button, XmNarmCallback, xm_arm_callback, cur);
612	      XtAddCallback (button, XmNdisarmCallback, xm_arm_callback, cur);
613	    }
614
615	  xm_update_label (instance, button, cur);
616
617	  /* Add a callback that is called when the button is
618	     selected.  Toggle buttons don't support
619	     XmNactivateCallback, we use XmNvalueChangedCallback in
620	     that case.  Don't add a callback to a simple label.  */
621	  if (cur->button_type)
622	    xm_update_toggle (instance, button, cur);
623	  else if (cur->call_data)
624	    XtAddCallback (button, XmNactivateCallback, xm_generic_callback,
625			   (XtPointer)instance);
626	}
627      else
628	{
629	  menu = XmCreatePulldownMenu (widget, cur->name, NULL, 0);
630
631	  make_menu_in_widget (instance, menu, cur->contents, 0);
632          XtSetArg (al[ac], XmNsubMenuId, menu); ac++;
633	  button = XmCreateCascadeButton (widget, cur->name, al, ac);
634
635	  xm_update_label (instance, button, cur);
636
637	  XtAddCallback (button, XmNcascadingCallback, xm_pull_down_callback,
638			 (XtPointer)instance);
639          XtOverrideTranslations (button, override);
640
641	}
642
643      children[child_index] = button;
644    }
645
646  /* Last entry is the help button.  The original comment read "Has to
647     be done after managing the buttons otherwise the menubar is only
648     4 pixels high."  This is no longer true, and to make
649     XmNmenuHelpWidget work, we need to set it before managing the
650     children.. --gerd.  */
651  if (button)
652    XtVaSetValues (widget, XmNmenuHelpWidget, button, NULL);
653
654  if (num_children)
655    XtManageChildren (children, num_children);
656
657  XtFree ((char *) children);
658  if (old_children)
659    XtFree ((char *) old_children);
660}
661
662static void
663update_one_menu_entry (instance, widget, val, deep_p)
664     widget_instance* instance;
665     Widget widget;
666     widget_value* val;
667     Boolean deep_p;
668{
669  Arg al [256];
670  int ac;
671  Widget menu;
672  widget_value* contents;
673
674  if (val->this_one_change == NO_CHANGE)
675    return;
676
677  /* update the sensitivity and userdata */
678  /* Common to all widget types */
679  XtSetSensitive (widget, val->enabled);
680  XtVaSetValues (widget, XmNuserData, val->call_data, NULL);
681
682  /* update the menu button as a label. */
683  if (val->this_one_change >= VISIBLE_CHANGE)
684    {
685      xm_update_label (instance, widget, val);
686      if (val->button_type)
687	xm_update_toggle (instance, widget, val);
688    }
689
690  /* update the pulldown/pullaside as needed */
691  ac = 0;
692  menu = NULL;
693  XtSetArg (al [ac], XmNsubMenuId, &menu); ac++;
694  XtGetValues (widget, al, ac);
695
696  contents = val->contents;
697
698  if (!menu)
699    {
700      if (contents)
701	{
702	  unsigned int old_num_children, i;
703	  Widget parent;
704	  Widget *widget_list;
705
706	  parent = XtParent (widget);
707	  widget_list = XtCompositeChildren (parent, &old_num_children);
708
709	  /* Find the widget position within the parent's widget list.  */
710	  for (i = 0; i < old_num_children; i++)
711	    if (strcmp (XtName (widget_list[i]), XtName (widget)) == 0)
712	      break;
713	  if (i == old_num_children)
714	    abort ();
715	  if (XmIsCascadeButton (widget_list[i]))
716	    {
717	      menu = XmCreatePulldownMenu (parent, XtName(widget), NULL, 0);
718	      make_menu_in_widget (instance, menu, contents, 0);
719	      ac = 0;
720	      XtSetArg (al [ac], XmNsubMenuId, menu); ac++;
721	      XtSetValues (widget, al, ac);
722	    }
723	  else
724	    {
725	      Widget button;
726
727	      /* The current menuitem is a XmPushButtonGadget, it
728		 needs to be replaced by a CascadeButtonGadget */
729	      XtDestroyWidget (widget_list[i]);
730	      menu = XmCreatePulldownMenu (parent, val->name, NULL, 0);
731	      make_menu_in_widget (instance, menu, contents, 0);
732	      ac = 0;
733	      XtSetArg (al [ac], XmNsubMenuId, menu); ac++;
734	      /* Non-zero values don't work reliably in
735		 conjunction with Emacs' event loop */
736	      XtSetArg (al [ac], XmNmappingDelay, 0); ac++;
737#ifdef XmNpositionIndex /* This is undefined on SCO ODT 2.0.  */
738	      /* Tell Motif to put it in the right place */
739	      XtSetArg (al [ac], XmNpositionIndex , i); ac++;
740#endif
741	      button = XmCreateCascadeButton (parent, val->name, al, ac);
742	      xm_update_label (instance, button, val);
743
744	      XtAddCallback (button, XmNcascadingCallback, xm_pull_down_callback,
745			     (XtPointer)instance);
746	      XtManageChild (button);
747	    }
748
749          if (widget_list)
750            XtFree ((char*) widget_list);
751	}
752    }
753  else if (!contents)
754    {
755      ac = 0;
756      XtSetArg (al [ac], XmNsubMenuId, NULL); ac++;
757      XtSetValues (widget, al, ac);
758      XtDestroyWidget (menu);
759    }
760  else if (deep_p && contents->change != NO_CHANGE)
761    xm_update_menu (instance, menu, val, 1);
762}
763
764static void
765xm_update_menu (instance, widget, val, deep_p)
766     widget_instance* instance;
767     Widget widget;
768     widget_value* val;
769     Boolean deep_p;
770{
771  Widget* children;
772  unsigned int num_children;
773  int num_children_to_keep = 0;
774  int i;
775  widget_value* cur;
776
777  children = XtCompositeChildren (widget, &num_children);
778
779  /* Widget is a RowColumn widget whose contents have to be updated
780   * to reflect the list of items in val->contents */
781
782  /* See how many buttons we can keep, and how many we
783     must completely replace.  */
784  if (val->contents == 0)
785    num_children_to_keep = 0;
786  else if (val->contents->change == STRUCTURAL_CHANGE)
787    {
788      if (children)
789	{
790	  for (i = 0, cur = val->contents;
791               (i < num_children
792		&& cur); /* how else to ditch unwanted children ?? - mgd */
793	       i++, cur = cur->next)
794	    {
795	      if (cur->this_one_change == STRUCTURAL_CHANGE)
796		break;
797	    }
798
799	  num_children_to_keep = i;
800	}
801    }
802  else
803    num_children_to_keep = num_children;
804
805  /* Update all the buttons of the RowColumn, in order,
806     except for those we are going to replace entirely.  */
807  if (children)
808    {
809      for (i = 0, cur = val->contents; i < num_children_to_keep; i++)
810	{
811	  if (!cur)
812	    {
813	      num_children_to_keep = i;
814	      break;
815	    }
816	  if (children [i]->core.being_destroyed
817	      || strcmp (XtName (children [i]), cur->name))
818	    continue;
819	  update_one_menu_entry (instance, children [i], cur, deep_p);
820	  cur = cur->next;
821	}
822    }
823
824  /* Now replace from scratch all the buttons after the last
825     place that the top-level structure changed.  */
826  if (val->contents->change == STRUCTURAL_CHANGE)
827    {
828      destroy_all_children (widget, num_children_to_keep);
829      make_menu_in_widget (instance, widget, val->contents,
830                           num_children_to_keep);
831    }
832
833  XtFree ((char *) children);
834}
835
836
837/* update text widgets */
838
839static void
840xm_update_text (instance, widget, val)
841     widget_instance* instance;
842     Widget widget;
843     widget_value* val;
844{
845  XmTextSetString (widget, val->value ? val->value : "");
846  XtRemoveAllCallbacks (widget, XmNactivateCallback);
847  XtAddCallback (widget, XmNactivateCallback, xm_generic_callback, instance);
848  XtRemoveAllCallbacks (widget, XmNvalueChangedCallback);
849  XtAddCallback (widget, XmNvalueChangedCallback,
850		 xm_internal_update_other_instances, instance);
851}
852
853static void
854xm_update_text_field (instance, widget, val)
855     widget_instance* instance;
856     Widget widget;
857     widget_value* val;
858{
859  XmTextFieldSetString (widget, val->value ? val->value : "");
860  XtRemoveAllCallbacks (widget, XmNactivateCallback);
861  XtAddCallback (widget, XmNactivateCallback, xm_generic_callback, instance);
862  XtRemoveAllCallbacks (widget, XmNvalueChangedCallback);
863  XtAddCallback (widget, XmNvalueChangedCallback,
864		 xm_internal_update_other_instances, instance);
865}
866
867
868/* update a motif widget */
869
870void
871xm_update_one_widget (instance, widget, val, deep_p)
872     widget_instance* instance;
873     Widget widget;
874     widget_value* val;
875     Boolean deep_p;
876{
877  WidgetClass class;
878
879  /* Mark as not edited */
880  val->edited = False;
881
882  /* Common to all widget types */
883  XtSetSensitive (widget, val->enabled);
884  XtVaSetValues (widget, XmNuserData, val->call_data, NULL);
885
886  /* Common to all label like widgets */
887  if (XtIsSubclass (widget, xmLabelWidgetClass))
888    xm_update_label (instance, widget, val);
889
890  class = XtClass (widget);
891  /* Class specific things */
892  if (class == xmPushButtonWidgetClass ||
893      class == xmArrowButtonWidgetClass)
894    {
895      xm_update_pushbutton (instance, widget, val);
896    }
897  else if (class == xmCascadeButtonWidgetClass)
898    {
899      xm_update_cascadebutton (instance, widget, val);
900    }
901  else if (class == xmToggleButtonWidgetClass
902	   || class == xmToggleButtonGadgetClass)
903    {
904      xm_update_toggle (instance, widget, val);
905    }
906  else if (class == xmRowColumnWidgetClass)
907    {
908      Boolean radiobox = 0;
909      int ac = 0;
910      Arg al [1];
911
912      XtSetArg (al [ac], XmNradioBehavior, &radiobox); ac++;
913      XtGetValues (widget, al, ac);
914
915      if (radiobox)
916	xm_update_radiobox (instance, widget, val);
917      else
918	xm_update_menu (instance, widget, val, deep_p);
919    }
920  else if (class == xmTextWidgetClass)
921    {
922      xm_update_text (instance, widget, val);
923    }
924  else if (class == xmTextFieldWidgetClass)
925    {
926      xm_update_text_field (instance, widget, val);
927    }
928  else if (class == xmListWidgetClass)
929    {
930      xm_update_list (instance, widget, val);
931    }
932}
933
934/* getting the value back */
935void
936xm_update_one_value (instance, widget, val)
937     widget_instance* instance;
938     Widget widget;
939     widget_value* val;
940{
941  WidgetClass class = XtClass (widget);
942  widget_value *old_wv;
943
944  /* copy the call_data slot into the "return" widget_value */
945  for (old_wv = instance->info->val->contents; old_wv; old_wv = old_wv->next)
946    if (!strcmp (val->name, old_wv->name))
947      {
948	val->call_data = old_wv->call_data;
949	break;
950      }
951
952  if (class == xmToggleButtonWidgetClass || class == xmToggleButtonGadgetClass)
953    {
954      XtVaGetValues (widget, XmNset, &val->selected, NULL);
955      val->edited = True;
956    }
957  else if (class == xmTextWidgetClass)
958    {
959      if (val->value)
960	free (val->value);
961      val->value = XmTextGetString (widget);
962      val->edited = True;
963    }
964  else if (class == xmTextFieldWidgetClass)
965    {
966      if (val->value)
967	free (val->value);
968      val->value = XmTextFieldGetString (widget);
969      val->edited = True;
970    }
971  else if (class == xmRowColumnWidgetClass)
972    {
973      Boolean radiobox = 0;
974      int ac = 0;
975      Arg al [1];
976
977      XtSetArg (al [ac], XmNradioBehavior, &radiobox); ac++;
978      XtGetValues (widget, al, ac);
979
980      if (radiobox)
981	{
982	  CompositeWidget radio = (CompositeWidget)widget;
983	  int i;
984	  for (i = 0; i < radio->composite.num_children; i++)
985	    {
986	      int set = False;
987	      Widget toggle = radio->composite.children [i];
988
989	      XtVaGetValues (toggle, XmNset, &set, NULL);
990	      if (set)
991		{
992		  if (val->value)
993		    free (val->value);
994		  val->value = safe_strdup (XtName (toggle));
995		}
996	    }
997	  val->edited = True;
998	}
999    }
1000  else if (class == xmListWidgetClass)
1001    {
1002      int pos_cnt;
1003      int* pos_list;
1004      if (XmListGetSelectedPos (widget, &pos_list, &pos_cnt))
1005	{
1006	  int i;
1007	  widget_value* cur;
1008	  for (cur = val->contents, i = 0; cur; cur = cur->next)
1009	    if (cur->value)
1010	      {
1011		int j;
1012		cur->selected = False;
1013		i += 1;
1014		for (j = 0; j < pos_cnt; j++)
1015		  if (pos_list [j] == i)
1016		    {
1017		      cur->selected = True;
1018		      val->value = safe_strdup (cur->name);
1019		    }
1020	      }
1021	  val->edited = 1;
1022	  XtFree ((char *) pos_list);
1023	}
1024    }
1025}
1026
1027
1028/* This function is for activating a button from a program.  It's wrong because
1029   we pass a NULL argument in the call_data which is not Motif compatible.
1030   This is used from the XmNdefaultAction callback of the List widgets to
1031   have a double-click put down a dialog box like the button would do.
1032   I could not find a way to do that with accelerators.
1033 */
1034static void
1035activate_button (widget, closure, call_data)
1036     Widget widget;
1037     XtPointer closure;
1038     XtPointer call_data;
1039{
1040  Widget button = (Widget)closure;
1041  XtCallCallbacks (button, XmNactivateCallback, NULL);
1042}
1043
1044/* creation functions */
1045
1046/* Called for key press in dialogs.  Used to pop down dialog on ESC.  */
1047static void
1048dialog_key_cb (widget, closure, event, continue_to_dispatch)
1049     Widget widget;
1050     XtPointer closure;
1051     XEvent *event;
1052     Boolean *continue_to_dispatch;
1053{
1054  KeySym sym = 0;
1055  Modifiers modif_ret;
1056
1057  XtTranslateKeycode (event->xkey.display, event->xkey.keycode, 0,
1058                      &modif_ret, &sym);
1059
1060  if (sym == osfXK_Cancel)
1061    {
1062      Widget w = *((Widget *) closure);
1063
1064      while (w && ! XtIsShell (w))
1065        w = XtParent (w);
1066
1067      if (XtIsShell (w)) XtPopdown (w);
1068    }
1069
1070  *continue_to_dispatch = TRUE;
1071}
1072
1073/* dialogs */
1074static Widget
1075make_dialog (name, parent, pop_up_p, shell_title, icon_name, text_input_slot,
1076	     radio_box, list, left_buttons, right_buttons)
1077     char* name;
1078     Widget parent;
1079     Boolean pop_up_p;
1080     char* shell_title;
1081     char* icon_name;
1082     Boolean text_input_slot;
1083     Boolean radio_box;
1084     Boolean list;
1085     int left_buttons;
1086     int right_buttons;
1087{
1088  Widget result;
1089  Widget form;
1090  Widget row;
1091  Widget icon;
1092  Widget icon_separator;
1093  Widget message;
1094  Widget value = 0;
1095  Widget separator;
1096  Widget button = 0;
1097  Widget children [16];		/* for the final XtManageChildren */
1098  int	 n_children;
1099  Arg 	al[64];			/* Arg List */
1100  int 	ac;			/* Arg Count */
1101  int 	i;
1102
1103  if (pop_up_p)
1104    {
1105      ac = 0;
1106      XtSetArg(al[ac], XmNtitle, shell_title); ac++;
1107      XtSetArg(al[ac], XtNallowShellResize, True); ac++;
1108      XtSetArg(al[ac], XmNdeleteResponse, XmUNMAP); ac++;
1109      result = XmCreateDialogShell (parent, "dialog", al, ac);
1110      ac = 0;
1111      XtSetArg(al[ac], XmNautoUnmanage, FALSE); ac++;
1112/*      XtSetArg(al[ac], XmNautoUnmanage, TRUE); ac++; */ /* ####is this ok? */
1113      XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1114      form = XmCreateForm (result, shell_title, al, ac);
1115    }
1116  else
1117    {
1118      ac = 0;
1119      XtSetArg(al[ac], XmNautoUnmanage, FALSE); ac++;
1120      XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1121      form = XmCreateForm (parent, shell_title, al, ac);
1122      result = form;
1123    }
1124
1125  n_children = left_buttons + right_buttons + 1;
1126  ac = 0;
1127  XtSetArg(al[ac], XmNpacking, n_children == 3?
1128	   XmPACK_COLUMN: XmPACK_TIGHT); ac++;
1129  XtSetArg(al[ac], XmNorientation, n_children == 3?
1130	   XmVERTICAL: XmHORIZONTAL); ac++;
1131  XtSetArg(al[ac], XmNnumColumns, left_buttons + right_buttons + 1); ac++;
1132  XtSetArg(al[ac], XmNmarginWidth, 0); ac++;
1133  XtSetArg(al[ac], XmNmarginHeight, 0); ac++;
1134  XtSetArg(al[ac], XmNspacing, 13); ac++;
1135  XtSetArg(al[ac], XmNadjustLast, False); ac++;
1136  XtSetArg(al[ac], XmNalignment, XmALIGNMENT_CENTER); ac++;
1137  XtSetArg(al[ac], XmNisAligned, True); ac++;
1138  XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++;
1139  XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_FORM); ac++;
1140  XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1141  XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM); ac++;
1142  XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1143  XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1144  XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1145  row = XmCreateRowColumn (form, "row", al, ac);
1146
1147  n_children = 0;
1148  for (i = 0; i < left_buttons; i++)
1149    {
1150      char button_name [16];
1151      sprintf (button_name, "button%d", i + 1);
1152      ac = 0;
1153      if (i == 0)
1154	{
1155	  XtSetArg(al[ac], XmNhighlightThickness, 1); ac++;
1156	  XtSetArg(al[ac], XmNshowAsDefault, TRUE); ac++;
1157	}
1158      XtSetArg(al[ac], XmNmarginWidth, 10); ac++;
1159      XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1160      children [n_children] = XmCreatePushButton (row, button_name, al, ac);
1161      XtAddEventHandler (children [n_children],
1162                         KeyPressMask, False, dialog_key_cb, result);
1163
1164      if (i == 0)
1165	{
1166	  button = children [n_children];
1167	  ac = 0;
1168	  XtSetArg(al[ac], XmNdefaultButton, button); ac++;
1169	  XtSetValues (row, al, ac);
1170	}
1171
1172      n_children++;
1173    }
1174
1175  /* invisible separator button */
1176  ac = 0;
1177  XtSetArg (al[ac], XmNmappedWhenManaged, FALSE); ac++;
1178  children [n_children] = XmCreateLabel (row, "separator_button", al, ac);
1179  n_children++;
1180
1181  for (i = 0; i < right_buttons; i++)
1182    {
1183      char button_name [16];
1184      sprintf (button_name, "button%d", left_buttons + i + 1);
1185      ac = 0;
1186      XtSetArg(al[ac], XmNmarginWidth, 10); ac++;
1187      XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1188      children [n_children] = XmCreatePushButton (row, button_name, al, ac);
1189      XtAddEventHandler (children [n_children],
1190                         KeyPressMask, False, dialog_key_cb, result);
1191
1192      if (! button) button = children [n_children];
1193      n_children++;
1194    }
1195
1196  XtManageChildren (children, n_children);
1197
1198  ac = 0;
1199  XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++;
1200  XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1201  XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1202  XtSetArg(al[ac], XmNbottomWidget, row); ac++;
1203  XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM); ac++;
1204  XtSetArg(al[ac], XmNleftOffset, 0); ac++;
1205  XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1206  XtSetArg(al[ac], XmNrightOffset, 0); ac++;
1207  separator = XmCreateSeparator (form, "", al, ac);
1208
1209  ac = 0;
1210  XtSetArg(al[ac], XmNlabelType, XmPIXMAP); ac++;
1211  XtSetArg(al[ac], XmNtopAttachment, XmATTACH_FORM); ac++;
1212  XtSetArg(al[ac], XmNtopOffset, 13); ac++;
1213  XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_NONE); ac++;
1214  XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM); ac++;
1215  XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1216  XtSetArg(al[ac], XmNrightAttachment, XmATTACH_NONE); ac++;
1217  icon = XmCreateLabel (form, icon_name, al, ac);
1218
1219  ac = 0;
1220  XtSetArg(al[ac], XmNmappedWhenManaged, FALSE); ac++;
1221  XtSetArg(al[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++;
1222  XtSetArg(al[ac], XmNtopOffset, 6); ac++;
1223  XtSetArg(al[ac], XmNtopWidget, icon); ac++;
1224  XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1225  XtSetArg(al[ac], XmNbottomOffset, 6); ac++;
1226  XtSetArg(al[ac], XmNbottomWidget, separator); ac++;
1227  XtSetArg(al[ac], XmNleftAttachment, XmATTACH_NONE); ac++;
1228  XtSetArg(al[ac], XmNrightAttachment, XmATTACH_NONE); ac++;
1229  icon_separator = XmCreateLabel (form, "", al, ac);
1230
1231  if (text_input_slot)
1232    {
1233      ac = 0;
1234      XtSetArg(al[ac], XmNcolumns, 50); ac++;
1235      XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++;
1236      XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1237      XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1238      XtSetArg(al[ac], XmNbottomWidget, separator); ac++;
1239      XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++;
1240      XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1241      XtSetArg(al[ac], XmNleftWidget, icon); ac++;
1242      XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1243      XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1244      value = XmCreateTextField (form, "value", al, ac);
1245    }
1246  else if (radio_box)
1247    {
1248      Widget radio_butt;
1249      ac = 0;
1250      XtSetArg(al[ac], XmNmarginWidth, 0); ac++;
1251      XtSetArg(al[ac], XmNmarginHeight, 0); ac++;
1252      XtSetArg(al[ac], XmNspacing, 13); ac++;
1253      XtSetArg(al[ac], XmNalignment, XmALIGNMENT_CENTER); ac++;
1254      XtSetArg(al[ac], XmNorientation, XmHORIZONTAL); ac++;
1255      XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1256      XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1257      XtSetArg(al[ac], XmNbottomWidget, separator); ac++;
1258      XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++;
1259      XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1260      XtSetArg(al[ac], XmNleftWidget, icon); ac++;
1261      XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1262      XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1263      value = XmCreateRadioBox (form, "radiobutton1", al, ac);
1264      ac = 0;
1265      i = 0;
1266      radio_butt = XmCreateToggleButtonGadget (value, "radio1", al, ac);
1267      children [i++] = radio_butt;
1268      radio_butt = XmCreateToggleButtonGadget (value, "radio2", al, ac);
1269      children [i++] = radio_butt;
1270      radio_butt = XmCreateToggleButtonGadget (value, "radio3", al, ac);
1271      children [i++] = radio_butt;
1272      XtManageChildren (children, i);
1273    }
1274  else if (list)
1275    {
1276      ac = 0;
1277      XtSetArg(al[ac], XmNvisibleItemCount, 5); ac++;
1278      XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++;
1279      XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1280      XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1281      XtSetArg(al[ac], XmNbottomWidget, separator); ac++;
1282      XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++;
1283      XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1284      XtSetArg(al[ac], XmNleftWidget, icon); ac++;
1285      XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1286      XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1287      value = XmCreateScrolledList (form, "list", al, ac);
1288
1289      /* this is the easiest way I found to have the dble click in the
1290	 list activate the default button */
1291      XtAddCallback (value, XmNdefaultActionCallback, activate_button, button);
1292    }
1293
1294  ac = 0;
1295  XtSetArg(al[ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
1296  XtSetArg(al[ac], XmNtopAttachment, XmATTACH_FORM); ac++;
1297  XtSetArg(al[ac], XmNtopOffset, 13); ac++;
1298  XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1299  XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1300  XtSetArg(al[ac], XmNbottomWidget,
1301	   text_input_slot || radio_box || list ? value : separator); ac++;
1302  XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++;
1303  XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1304  XtSetArg(al[ac], XmNleftWidget, icon); ac++;
1305  XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1306  XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1307  message = XmCreateLabel (form, "message", al, ac);
1308
1309  if (list)
1310    XtManageChild (value);
1311
1312  i = 0;
1313  children [i] = row; i++;
1314  children [i] = separator; i++;
1315  if (text_input_slot || radio_box)
1316    {
1317      children [i] = value; i++;
1318    }
1319  children [i] = message; i++;
1320  children [i] = icon; i++;
1321  children [i] = icon_separator; i++;
1322  XtManageChildren (children, i);
1323
1324  if (text_input_slot || list)
1325    {
1326      XtInstallAccelerators (value, button);
1327      XtSetKeyboardFocus (result, value);
1328    }
1329  else
1330    {
1331      XtInstallAccelerators (form, button);
1332      XtSetKeyboardFocus (result, button);
1333    }
1334
1335  return result;
1336}
1337
1338static destroyed_instance*
1339find_matching_instance (instance)
1340     widget_instance* instance;
1341{
1342  destroyed_instance*	cur;
1343  destroyed_instance*	prev;
1344  char*	type = instance->info->type;
1345  char*	name = instance->info->name;
1346
1347  for (prev = NULL, cur = all_destroyed_instances;
1348       cur;
1349       prev = cur, cur = cur->next)
1350    {
1351      if (!strcmp (cur->name, name)
1352	  && !strcmp (cur->type, type)
1353	  && cur->parent == instance->parent
1354	  && cur->pop_up_p == instance->pop_up_p)
1355	{
1356	  if (prev)
1357	    prev->next = cur->next;
1358	  else
1359	    all_destroyed_instances = cur->next;
1360	  return cur;
1361	}
1362      /* do some cleanup */
1363      else if (!cur->widget)
1364	{
1365	  if (prev)
1366	    prev->next = cur->next;
1367	  else
1368	    all_destroyed_instances = cur->next;
1369	  free_destroyed_instance (cur);
1370	  cur = prev ? prev : all_destroyed_instances;
1371	}
1372    }
1373  return NULL;
1374}
1375
1376static void
1377mark_dead_instance_destroyed (widget, closure, call_data)
1378     Widget widget;
1379     XtPointer closure;
1380     XtPointer call_data;
1381{
1382  destroyed_instance* instance = (destroyed_instance*)closure;
1383  instance->widget = NULL;
1384}
1385
1386static void
1387recenter_widget (widget)
1388     Widget widget;
1389{
1390  Widget parent = XtParent (widget);
1391  Screen* screen = XtScreen (widget);
1392  Dimension screen_width = WidthOfScreen (screen);
1393  Dimension screen_height = HeightOfScreen (screen);
1394  Dimension parent_width = 0;
1395  Dimension parent_height = 0;
1396  Dimension child_width = 0;
1397  Dimension child_height = 0;
1398  Position x;
1399  Position y;
1400
1401  XtVaGetValues (widget, XtNwidth, &child_width, XtNheight, &child_height, NULL);
1402  XtVaGetValues (parent, XtNwidth, &parent_width, XtNheight, &parent_height,
1403		 NULL);
1404
1405  x = (((Position)parent_width) - ((Position)child_width)) / 2;
1406  y = (((Position)parent_height) - ((Position)child_height)) / 2;
1407
1408  XtTranslateCoords (parent, x, y, &x, &y);
1409
1410  if (x + child_width > screen_width)
1411    x = screen_width - child_width;
1412  if (x < 0)
1413    x = 0;
1414
1415  if (y + child_height > screen_height)
1416    y = screen_height - child_height;
1417  if (y < 0)
1418    y = 0;
1419
1420  XtVaSetValues (widget, XtNx, x, XtNy, y, NULL);
1421}
1422
1423static Widget
1424recycle_instance (instance)
1425     destroyed_instance* instance;
1426{
1427  Widget widget = instance->widget;
1428
1429  /* widget is NULL if the parent was destroyed. */
1430  if (widget)
1431    {
1432      Widget focus;
1433      Widget separator;
1434
1435      /* Remove the destroy callback as the instance is not in the list
1436	 anymore */
1437      XtRemoveCallback (instance->parent, XtNdestroyCallback,
1438			mark_dead_instance_destroyed,
1439			(XtPointer)instance);
1440
1441      /* Give the focus to the initial item */
1442      focus = XtNameToWidget (widget, "*value");
1443      if (!focus)
1444	focus = XtNameToWidget (widget, "*button1");
1445      if (focus)
1446	XtSetKeyboardFocus (widget, focus);
1447
1448      /* shrink the separator label back to their original size */
1449      separator = XtNameToWidget (widget, "*separator_button");
1450      if (separator)
1451	XtVaSetValues (separator, XtNwidth, 5, XtNheight, 5, NULL);
1452
1453      /* Center the dialog in its parent */
1454      recenter_widget (widget);
1455    }
1456  free_destroyed_instance (instance);
1457  return widget;
1458}
1459
1460Widget
1461xm_create_dialog (instance)
1462     widget_instance* instance;
1463{
1464  char* 	name = instance->info->type;
1465  Widget 	parent = instance->parent;
1466  Widget	widget;
1467  Boolean 	pop_up_p = instance->pop_up_p;
1468  char*		shell_name = 0;
1469  char* 	icon_name = 0;
1470  Boolean	text_input_slot = False;
1471  Boolean	radio_box = False;
1472  Boolean	list = False;
1473  int		total_buttons;
1474  int		left_buttons = 0;
1475  int		right_buttons = 1;
1476  destroyed_instance*	dead_one;
1477
1478  /* try to find a widget to recycle */
1479  dead_one = find_matching_instance (instance);
1480  if (dead_one)
1481    {
1482      Widget recycled_widget = recycle_instance (dead_one);
1483      if (recycled_widget)
1484	return recycled_widget;
1485    }
1486
1487  switch (name [0]){
1488  case 'E': case 'e':
1489    icon_name = "dbox-error";
1490    shell_name = "Error";
1491    break;
1492
1493  case 'I': case 'i':
1494    icon_name = "dbox-info";
1495    shell_name = "Information";
1496    break;
1497
1498  case 'L': case 'l':
1499    list = True;
1500    icon_name = "dbox-question";
1501    shell_name = "Prompt";
1502    break;
1503
1504  case 'P': case 'p':
1505    text_input_slot = True;
1506    icon_name = "dbox-question";
1507    shell_name = "Prompt";
1508    break;
1509
1510  case 'Q': case 'q':
1511    icon_name = "dbox-question";
1512    shell_name = "Question";
1513    break;
1514  }
1515
1516  total_buttons = name [1] - '0';
1517
1518  if (name [3] == 'T' || name [3] == 't')
1519    {
1520      text_input_slot = False;
1521      radio_box = True;
1522    }
1523  else if (name [3])
1524    right_buttons = name [4] - '0';
1525
1526  left_buttons = total_buttons - right_buttons;
1527
1528  widget = make_dialog (name, parent, pop_up_p,
1529			shell_name, icon_name, text_input_slot, radio_box,
1530			list, left_buttons, right_buttons);
1531
1532  XtAddCallback (widget, XmNpopdownCallback, xm_nosel_callback,
1533		 (XtPointer) instance);
1534
1535  return widget;
1536}
1537
1538/* Create a menu bar.  We turn off the f10 key
1539   because we have not yet managed to make it work right in Motif.  */
1540
1541static Widget
1542make_menubar (instance)
1543     widget_instance* instance;
1544{
1545  Arg al[3];
1546  int ac;
1547
1548  ac = 0;
1549  XtSetArg(al[ac], XmNmenuAccelerator, 0); ++ac;
1550  return XmCreateMenuBar (instance->parent, instance->info->name, al, ac);
1551}
1552
1553static void
1554remove_grabs (shell, closure, call_data)
1555     Widget shell;
1556     XtPointer closure;
1557     XtPointer call_data;
1558{
1559  Widget menu = (Widget) closure;
1560  XmRemoveFromPostFromList (menu, XtParent (XtParent (menu)));
1561}
1562
1563static Widget
1564make_popup_menu (instance)
1565     widget_instance* instance;
1566{
1567  Widget parent = instance->parent;
1568  Window parent_window = parent->core.window;
1569  Widget result;
1570
1571  /* sets the parent window to 0 to fool Motif into not generating a grab */
1572  parent->core.window = 0;
1573  result = XmCreatePopupMenu (parent, instance->info->name, NULL, 0);
1574  XtAddCallback (XtParent (result), XmNpopdownCallback, remove_grabs,
1575		 (XtPointer)result);
1576  parent->core.window = parent_window;
1577  return result;
1578}
1579
1580static Widget
1581make_main (instance)
1582     widget_instance* instance;
1583{
1584  Widget parent = instance->parent;
1585  Widget result;
1586  Arg al[2];
1587  int ac;
1588
1589  ac = 0;
1590  XtSetArg (al[ac], XtNborderWidth, 0); ac++;
1591  XtSetArg (al[ac], XmNspacing, 0); ac++;
1592  result = XmCreateMainWindow (parent, instance->info->name, al, ac);
1593  return result;
1594}
1595
1596/* Table of functions to create widgets */
1597
1598#ifdef ENERGIZE
1599
1600/* interface with the XDesigner generated functions */
1601typedef Widget (*widget_maker) (Widget);
1602extern Widget create_project_p_sheet (Widget parent);
1603extern Widget create_debugger_p_sheet (Widget parent);
1604extern Widget create_breaklist_p_sheet (Widget parent);
1605extern Widget create_le_browser_p_sheet (Widget parent);
1606extern Widget create_class_browser_p_sheet (Widget parent);
1607extern Widget create_call_browser_p_sheet (Widget parent);
1608extern Widget create_build_dialog (Widget parent);
1609extern Widget create_editmode_dialog (Widget parent);
1610extern Widget create_search_dialog (Widget parent);
1611extern Widget create_project_display_dialog (Widget parent);
1612
1613static Widget
1614make_one (widget_instance* instance, widget_maker fn)
1615{
1616  Widget result;
1617  Arg 	al [64];
1618  int 	ac = 0;
1619
1620  if (instance->pop_up_p)
1621    {
1622      XtSetArg (al [ac], XmNallowShellResize, TRUE); ac++;
1623      result = XmCreateDialogShell (instance->parent, "dialog", NULL, 0);
1624      XtAddCallback (result, XmNpopdownCallback, &xm_nosel_callback,
1625		     (XtPointer) instance);
1626      (*fn) (result);
1627    }
1628  else
1629    {
1630      result = (*fn) (instance->parent);
1631      XtRealizeWidget (result);
1632    }
1633  return result;
1634}
1635
1636static Widget
1637make_project_p_sheet (widget_instance* instance)
1638{
1639  return make_one (instance, create_project_p_sheet);
1640}
1641
1642static Widget
1643make_debugger_p_sheet (widget_instance* instance)
1644{
1645  return make_one (instance, create_debugger_p_sheet);
1646}
1647
1648static Widget
1649make_breaklist_p_sheet (widget_instance* instance)
1650{
1651  return make_one (instance, create_breaklist_p_sheet);
1652}
1653
1654static Widget
1655make_le_browser_p_sheet (widget_instance* instance)
1656{
1657  return make_one (instance, create_le_browser_p_sheet);
1658}
1659
1660static Widget
1661make_class_browser_p_sheet (widget_instance* instance)
1662{
1663  return make_one (instance, create_class_browser_p_sheet);
1664}
1665
1666static Widget
1667make_call_browser_p_sheet (widget_instance* instance)
1668{
1669  return make_one (instance, create_call_browser_p_sheet);
1670}
1671
1672static Widget
1673make_build_dialog (widget_instance* instance)
1674{
1675  return make_one (instance, create_build_dialog);
1676}
1677
1678static Widget
1679make_editmode_dialog (widget_instance* instance)
1680{
1681  return make_one (instance, create_editmode_dialog);
1682}
1683
1684static Widget
1685make_search_dialog (widget_instance* instance)
1686{
1687  return make_one (instance, create_search_dialog);
1688}
1689
1690static Widget
1691make_project_display_dialog (widget_instance* instance)
1692{
1693  return make_one (instance, create_project_display_dialog);
1694}
1695
1696#endif /* ENERGIZE */
1697
1698widget_creation_entry
1699xm_creation_table [] =
1700{
1701  {"menubar", 			make_menubar},
1702  {"popup",			make_popup_menu},
1703  {"main",			make_main},
1704#ifdef ENERGIZE
1705  {"project_p_sheet",		make_project_p_sheet},
1706  {"debugger_p_sheet",		make_debugger_p_sheet},
1707  {"breaklist_psheet",		make_breaklist_p_sheet},
1708  {"leb_psheet",       		make_le_browser_p_sheet},
1709  {"class_browser_psheet",	make_class_browser_p_sheet},
1710  {"ctree_browser_psheet",	make_call_browser_p_sheet},
1711  {"build",			make_build_dialog},
1712  {"editmode",			make_editmode_dialog},
1713  {"search",			make_search_dialog},
1714  {"project_display",		make_project_display_dialog},
1715#endif /* ENERGIZE */
1716  {NULL, NULL}
1717};
1718
1719/* Destruction of instances */
1720void
1721xm_destroy_instance (instance)
1722     widget_instance* instance;
1723{
1724  Widget widget = instance->widget;
1725  /* recycle the dialog boxes */
1726  /* Disable the recycling until we can find a way to have the dialog box
1727     get reasonable layout after we modify its contents. */
1728  if (0
1729      && XtClass (widget) == xmDialogShellWidgetClass)
1730    {
1731      destroyed_instance* dead_instance =
1732	make_destroyed_instance (instance->info->name,
1733				 instance->info->type,
1734				 instance->widget,
1735				 instance->parent,
1736				 instance->pop_up_p);
1737      dead_instance->next = all_destroyed_instances;
1738      all_destroyed_instances = dead_instance;
1739      XtUnmanageChild (first_child (instance->widget));
1740      XFlush (XtDisplay (instance->widget));
1741      XtAddCallback (instance->parent, XtNdestroyCallback,
1742		     mark_dead_instance_destroyed, (XtPointer)dead_instance);
1743    }
1744  else
1745    {
1746      /* This might not be necessary now that the nosel is attached to
1747	 popdown instead of destroy, but it can't hurt. */
1748      XtRemoveCallback (instance->widget, XtNdestroyCallback,
1749			xm_nosel_callback, (XtPointer)instance);
1750      XtDestroyWidget (instance->widget);
1751    }
1752}
1753
1754/* popup utility */
1755void
1756xm_popup_menu (widget, event)
1757     Widget widget;
1758     XEvent *event;
1759{
1760  XButtonPressedEvent dummy;
1761
1762  if (event == 0)
1763    {
1764      dummy.type = ButtonPress;
1765      dummy.serial = 0;
1766      dummy.send_event = 0;
1767      dummy.display = XtDisplay (widget);
1768      dummy.window = XtWindow (XtParent (widget));
1769      dummy.time = 0;
1770      dummy.button = 0;
1771      XQueryPointer (dummy.display, dummy.window, &dummy.root,
1772		     &dummy.subwindow, &dummy.x_root, &dummy.y_root,
1773		     &dummy.x, &dummy.y, &dummy.state);
1774      event = (XEvent *) &dummy;
1775    }
1776
1777  if (event->type == ButtonPress || event->type == ButtonRelease)
1778    {
1779      /* Setting the menuPost resource only required by Motif 1.1 and
1780	 LessTif 0.84 and earlier.  With later versions of LessTif,
1781	 setting menuPost is unnecessary and may cause problems, so
1782	 don't do it.  */
1783#if XmVersion < 1002 || (defined LESSTIF_VERSION && LESSTIF_VERSION < 84)
1784	{
1785	  /* This is so totally ridiculous: there's NO WAY to tell Motif
1786	     that *any* button can select a menu item.  Only one button
1787	     can have that honor.  */
1788
1789	  char *trans = 0;
1790	  if      (event->xbutton.state & Button5Mask) trans = "<Btn5Down>";
1791	  else if (event->xbutton.state & Button4Mask) trans = "<Btn4Down>";
1792	  else if (event->xbutton.state & Button3Mask) trans = "<Btn3Down>";
1793	  else if (event->xbutton.state & Button2Mask) trans = "<Btn2Down>";
1794	  else if (event->xbutton.state & Button1Mask) trans = "<Btn1Down>";
1795	  if (trans) XtVaSetValues (widget, XmNmenuPost, trans, NULL);
1796	}
1797#endif
1798
1799      XmMenuPosition (widget, (XButtonPressedEvent *) event);
1800    }
1801
1802  XtManageChild (widget);
1803}
1804
1805static void
1806set_min_dialog_size (w)
1807     Widget w;
1808{
1809  short width;
1810  short height;
1811  XtVaGetValues (w, XmNwidth, &width, XmNheight, &height, NULL);
1812  XtVaSetValues (w, XmNminWidth, width, XmNminHeight, height, NULL);
1813}
1814
1815void
1816xm_pop_instance (instance, up)
1817     widget_instance* instance;
1818     Boolean up;
1819{
1820  Widget widget = instance->widget;
1821
1822  if (XtClass (widget) == xmDialogShellWidgetClass)
1823    {
1824      Widget widget_to_manage = first_child (widget);
1825      if (up)
1826	{
1827	  XtManageChild (widget_to_manage);
1828	  set_min_dialog_size (widget);
1829	  XtSetKeyboardFocus (instance->parent, widget);
1830	}
1831      else
1832	XtUnmanageChild (widget_to_manage);
1833    }
1834  else
1835    {
1836      if (up)
1837	XtManageChild (widget);
1838      else
1839	XtUnmanageChild (widget);
1840    }
1841}
1842
1843
1844/* motif callback */
1845
1846static void
1847do_call (widget, closure, type)
1848     Widget widget;
1849     XtPointer closure;
1850     enum do_call_type type;
1851{
1852  Arg al [256];
1853  int ac;
1854  XtPointer user_data;
1855  widget_instance* instance = (widget_instance*)closure;
1856  Widget instance_widget;
1857  LWLIB_ID id;
1858
1859  if (!instance)
1860    return;
1861  if (widget->core.being_destroyed)
1862    return;
1863
1864  instance_widget = instance->widget;
1865  if (!instance_widget)
1866    return;
1867
1868  id = instance->info->id;
1869  ac = 0;
1870  user_data = NULL;
1871  XtSetArg (al [ac], XmNuserData, &user_data); ac++;
1872  XtGetValues (widget, al, ac);
1873
1874  switch (type)
1875    {
1876    case pre_activate:
1877      if (instance->info->pre_activate_cb)
1878	instance->info->pre_activate_cb (widget, id, user_data);
1879      break;
1880
1881    case selection:
1882      if (instance->info->selection_cb)
1883	instance->info->selection_cb (widget, id, user_data);
1884      break;
1885
1886    case no_selection:
1887      if (instance->info->selection_cb)
1888	instance->info->selection_cb (widget, id, (XtPointer) -1);
1889      break;
1890
1891    case post_activate:
1892      if (instance->info->post_activate_cb)
1893	instance->info->post_activate_cb (widget, id, user_data);
1894      break;
1895
1896    default:
1897      abort ();
1898    }
1899}
1900
1901/* Like lw_internal_update_other_instances except that it does not do
1902   anything if its shell parent is not managed.  This is to protect
1903   lw_internal_update_other_instances to dereference freed memory
1904   if the widget was ``destroyed'' by caching it in the all_destroyed_instances
1905   list */
1906static void
1907xm_internal_update_other_instances (widget, closure, call_data)
1908     Widget widget;
1909     XtPointer closure;
1910     XtPointer call_data;
1911{
1912  Widget parent;
1913  for (parent = widget; parent; parent = XtParent (parent))
1914    if (XtIsShell (parent))
1915      break;
1916    else if (!XtIsManaged (parent))
1917      return;
1918   lw_internal_update_other_instances (widget, closure, call_data);
1919}
1920
1921static void
1922xm_generic_callback (widget, closure, call_data)
1923     Widget widget;
1924     XtPointer closure;
1925     XtPointer call_data;
1926{
1927  lw_internal_update_other_instances (widget, closure, call_data);
1928  do_call (widget, closure, selection);
1929}
1930
1931static void
1932xm_nosel_callback (widget, closure, call_data)
1933     Widget widget;
1934     XtPointer closure;
1935     XtPointer call_data;
1936{
1937  /* This callback is only called when a dialog box is dismissed with
1938     the wm's destroy button (WM_DELETE_WINDOW.)  We want the dialog
1939     box to be destroyed in that case, not just unmapped, so that it
1940     releases its keyboard grabs.  But there are problems with running
1941     our callbacks while the widget is in the process of being
1942     destroyed, so we set XmNdeleteResponse to XmUNMAP instead of
1943     XmDESTROY and then destroy it ourself after having run the
1944     callback.  */
1945  do_call (widget, closure, no_selection);
1946  XtDestroyWidget (widget);
1947}
1948
1949static void
1950xm_pull_down_callback (widget, closure, call_data)
1951     Widget widget;
1952     XtPointer closure;
1953     XtPointer call_data;
1954{
1955  Widget parent = XtParent (widget);
1956
1957  if (XmIsRowColumn (parent))
1958    {
1959      unsigned char type = 0xff;
1960      XtVaGetValues (parent, XmNrowColumnType, &type, NULL);
1961      if (type == XmMENU_BAR)
1962	do_call (widget, closure, pre_activate);
1963    }
1964}
1965
1966
1967/* XmNpopdownCallback for MenuShell widgets.  WIDGET is the MenuShell,
1968   CLOSURE is a pointer to the widget_instance of the shell,
1969
1970   Note that this callback is called for each cascade button in a
1971   menu, whether or not its submenu is visible.  */
1972
1973static void
1974xm_pop_down_callback (widget, closure, call_data)
1975     Widget widget;
1976     XtPointer closure;
1977     XtPointer call_data;
1978{
1979  widget_instance *instance = (widget_instance *) closure;
1980
1981  if ((!instance->pop_up_p && XtParent (widget) == instance->widget)
1982      || XtParent (widget) == instance->parent)
1983    do_call (widget, closure, post_activate);
1984}
1985
1986
1987/* set the keyboard focus */
1988void
1989xm_set_keyboard_focus (parent, w)
1990     Widget parent;
1991     Widget w;
1992{
1993  XmProcessTraversal (w, 0);
1994  XtSetKeyboardFocus (parent, w);
1995}
1996
1997/* Motif hack to set the main window areas. */
1998void
1999xm_set_main_areas (parent, menubar, work_area)
2000     Widget parent;
2001     Widget menubar;
2002     Widget work_area;
2003{
2004  XmMainWindowSetAreas (parent,
2005			menubar,	/* menubar (maybe 0) */
2006			0,		/* command area (psheets) */
2007			0,		/* horizontal scroll */
2008			0,              /* vertical scroll */
2009			work_area);	/* work area */
2010}
2011
2012/* Motif hack to control resizing on the menubar. */
2013void
2014xm_manage_resizing (w, flag)
2015     Widget w;
2016     Boolean flag;
2017{
2018  XtVaSetValues (w, XtNallowShellResize, flag, NULL);
2019}
2020
2021/* arch-tag: 73976f64-73b2-4600-aa13-d9ede20ee965
2022   (do not change this comment) */
2023