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