Contents in release-21-2 at 1999-06-30-19.
[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   /* Common to all label like widgets */
781   if (XtIsSubclass (widget, xmLabelWidgetClass))
782     xm_update_label (instance, widget, val);
783   
784   class = XtClass (widget);
785   /* Class specific things */
786   if (class == xmPushButtonWidgetClass ||
787       class == xmArrowButtonWidgetClass)
788     {
789       xm_update_pushbutton (instance, widget, val);
790     }
791 #ifdef LWLIB_MENUBARS_MOTIF
792   else if (class == xmCascadeButtonWidgetClass)
793     {
794       xm_update_cascadebutton (instance, widget, val);
795     }
796 #endif
797   else if (class == xmToggleButtonWidgetClass
798            || class == xmToggleButtonGadgetClass)
799     {
800       xm_update_toggle (instance, widget, val);
801     }
802   else if (class == xmRowColumnWidgetClass)
803     {
804       Boolean radiobox = 0;
805
806       XtSetArg (al [0], XmNradioBehavior, &radiobox);
807       XtGetValues (widget, al, 1);
808       
809       if (radiobox)
810         xm_update_radiobox (instance, widget, val);
811 #ifdef LWLIB_MENUBARS_MOTIF
812       else
813         xm_update_menu (instance, widget, val, deep_p);
814 #endif
815     }
816   else if (class == xmTextWidgetClass)
817     {
818       xm_update_text (instance, widget, val);
819     }
820   else if (class == xmTextFieldWidgetClass)
821     {
822       xm_update_text_field (instance, widget, val);
823     }
824   else if (class == xmListWidgetClass)
825     {
826       xm_update_list (instance, widget, val);
827     }
828 #ifdef LWLIB_SCROLLBARS_MOTIF
829   else if (class == xmScrollBarWidgetClass)
830     {
831       xm_update_scrollbar (instance, widget, val);
832     }
833 #endif
834 }
835
836 \f/* getting the value back */
837 void
838 xm_update_one_value (widget_instance* instance, Widget widget,
839                      widget_value* val)
840 {
841   WidgetClass class = XtClass (widget);
842   widget_value *old_wv;
843
844   /* copy the call_data slot into the "return" widget_value */
845   for (old_wv = instance->info->val->contents; old_wv; old_wv = old_wv->next)
846     if (!strcmp (val->name, old_wv->name))
847       {
848         val->call_data = old_wv->call_data;
849         break;
850       }
851   
852   if (class == xmToggleButtonWidgetClass || class == xmToggleButtonGadgetClass)
853     {
854       Arg al [1];
855       XtSetArg (al [0], XmNset, &val->selected);
856       XtGetValues (widget, al, 1);
857       val->edited = True;
858     }
859   else if (class == xmTextWidgetClass)
860     {
861       if (val->value)
862         free (val->value);
863       val->value = XmTextGetString (widget);
864       val->edited = True;
865     }
866   else if (class == xmTextFieldWidgetClass)
867     {
868       if (val->value)
869         free (val->value);
870       val->value = XmTextFieldGetString (widget);
871       val->edited = True;
872     }
873   else if (class == xmRowColumnWidgetClass)
874     {
875       Boolean radiobox = 0;
876       {
877         Arg al [1];
878         XtSetArg (al [0], XmNradioBehavior, &radiobox);
879         XtGetValues (widget, al, 1);
880       }
881
882       if (radiobox)
883         {
884           CompositeWidget radio = (CompositeWidget)widget;
885           int i;
886           for (i = 0; i < radio->composite.num_children; i++)
887             {
888               int set = False;
889               Widget toggle = radio->composite.children [i];
890               Arg al [1];
891
892               XtSetArg (al [0], XmNset, &set);
893               XtGetValues (toggle, al, 1);
894               if (set)
895                 {
896                   if (val->value)
897                     free (val->value);
898                   val->value = safe_strdup (XtName (toggle));
899                 }
900             }
901           val->edited = True;
902         }
903     }
904   else if (class == xmListWidgetClass)
905     {
906       int pos_cnt;
907       int* pos_list;
908       if (XmListGetSelectedPos (widget, &pos_list, &pos_cnt))
909         {
910           int i;
911           widget_value* cur;
912           for (cur = val->contents, i = 0; cur; cur = cur->next)
913             if (cur->value)
914               {
915                 int j;
916                 cur->selected = False;
917                 i += 1;
918                 for (j = 0; j < pos_cnt; j++)
919                   if (pos_list [j] == i)
920                     {
921                       cur->selected = True;
922                       val->value = safe_strdup (cur->name);
923                     }
924               }
925           val->edited = 1;
926           XtFree ((char *) pos_list);
927         }
928     }
929 #ifdef LWLIB_SCROLLBARS_MOTIF
930   else if (class == xmScrollBarWidgetClass)
931     {
932       /* This function is not used by the scrollbar. */
933       return;
934     }
935 #endif
936 }
937
938 \f
939 /* This function is for activating a button from a program.  It's wrong because
940    we pass a NULL argument in the call_data which is not Motif compatible.
941    This is used from the XmNdefaultAction callback of the List widgets to
942    have a double-click put down a dialog box like the button would do. 
943    I could not find a way to do that with accelerators.
944  */
945 static void
946 activate_button (Widget widget, XtPointer closure, XtPointer call_data)
947 {
948   Widget button = (Widget)closure;
949   XtCallCallbacks (button, XmNactivateCallback, NULL);
950 }
951
952 /* creation functions */
953
954 #ifdef LWLIB_DIALOGS_MOTIF
955
956 /* dialogs */
957
958 #if (XmVersion >= 1002)
959 # define ARMANDACTIVATE_KLUDGE
960 # define DND_KLUDGE
961 #endif
962
963 #ifdef ARMANDACTIVATE_KLUDGE
964  /* We want typing Return at a dialog box to select the default button; but
965     we're satisfied with having it select the leftmost button instead.
966
967     In Motif 1.1.5 we could do this by putting this resource in the
968     app-defaults file:
969
970         *dialog*button1.accelerators:#override\
971         <KeyPress>Return: ArmAndActivate()\n\
972         <KeyPress>KP_Enter: ArmAndActivate()\n\
973         Ctrl<KeyPress>m: ArmAndActivate()\n
974
975     but that doesn't work with 1.2.1 and I don't understand why. However,
976     doing the equivalent C code does work, with the notable disadvantage that
977     the user can't override it.  So that's what we do until we figure out
978     something better....
979   */
980 static char button_trans[] = "\
981 <KeyPress>Return: ArmAndActivate()\n\
982 <KeyPress>KP_Enter: ArmAndActivate()\n\
983 Ctrl<KeyPress>m: ArmAndActivate()\n";
984
985 #endif /* ARMANDACTIVATE_KLUDGE */
986
987
988 #ifdef DND_KLUDGE
989  /* This is a kludge to disable drag-and-drop in dialog boxes.  The symptom
990     was a segv down in libXm somewhere if you used the middle button on a
991     dialog box to begin a drag; when you released the button to make a drop
992     things would lose if you were not over the button where you started the 
993     drag (canceling the operation).  This was probably due to the fact that
994     the dialog boxes were not set up to handle a drag but were trying to do
995     so anyway for some reason.
996
997     So we disable drag-and-drop in dialog boxes by turning off the binding for
998     Btn2Down which, by default, initiates a drag.  Clearly this is a shitty
999     solution as it only works in default configurations, but...
1000   */
1001 static char disable_dnd_trans[] = "<Btn2Down>: ";
1002 #endif /* DND_KLUDGE */
1003
1004
1005 static Widget
1006 make_dialog (char* name, Widget parent, Boolean pop_up_p,
1007              CONST char* shell_title, CONST char* icon_name,
1008              Boolean text_input_slot, Boolean radio_box, Boolean list,
1009              int left_buttons, int right_buttons)
1010 {
1011   Widget result;
1012   Widget form;
1013   Widget row;
1014   Widget icon;
1015   Widget icon_separator;
1016   Widget message;
1017   Widget value = 0;
1018   Widget separator;
1019   Widget button = 0;
1020   Widget children [16];         /* for the final XtManageChildren */
1021   int   n_children;
1022   Arg   al[64];                 /* Arg List */
1023   int   ac;                     /* Arg Count */
1024   int   i;
1025   
1026 #ifdef DND_KLUDGE
1027   XtTranslations dnd_override = XtParseTranslationTable (disable_dnd_trans);
1028 # define DO_DND_KLUDGE(widget) XtOverrideTranslations ((widget), dnd_override)
1029 #else  /* ! DND_KLUDGE */
1030 # define DO_DND_KLUDGE(widget)
1031 #endif /* ! DND_KLUDGE */
1032
1033   if (pop_up_p)
1034     {
1035       ac = 0;
1036       XtSetArg(al[ac], XmNtitle, shell_title);          ac++;
1037       XtSetArg(al[ac], XtNallowShellResize, True);      ac++;
1038       XtSetArg(al[ac], XmNdeleteResponse, XmUNMAP);     ac++;
1039       result = XmCreateDialogShell (parent, "dialog", al, ac);
1040
1041       XtSetArg(al[ac], XmNautoUnmanage, FALSE);         ac++;
1042 /*      XtSetArg(al[ac], XmNautoUnmanage, TRUE); ac++; */ /* ####is this ok? */
1043       XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1044       form = XmCreateForm (result, (char *) shell_title, al, ac);
1045     }
1046   else
1047     {
1048       ac = 0;
1049       XtSetArg(al[ac], XmNautoUnmanage, FALSE);         ac++;
1050       XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1051       form = XmCreateForm (parent, (char *) shell_title, al, ac);
1052       result = form;
1053     }
1054
1055   ac = 0;
1056   XtSetArg(al[ac], XmNpacking, XmPACK_COLUMN);          ac++;
1057   XtSetArg(al[ac], XmNorientation, XmVERTICAL);         ac++;
1058   XtSetArg(al[ac], XmNnumColumns, left_buttons + right_buttons + 1); ac++;
1059   XtSetArg(al[ac], XmNmarginWidth, 0);                  ac++;
1060   XtSetArg(al[ac], XmNmarginHeight, 0);                 ac++;
1061   XtSetArg(al[ac], XmNspacing, 13);                     ac++;
1062   XtSetArg(al[ac], XmNadjustLast, False);               ac++;
1063   XtSetArg(al[ac], XmNalignment, XmALIGNMENT_CENTER);   ac++;
1064   XtSetArg(al[ac], XmNisAligned, True);                 ac++;
1065   XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE);    ac++;
1066   XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_FORM); ac++;
1067   XtSetArg(al[ac], XmNbottomOffset, 13);                ac++;
1068   XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM);   ac++;
1069   XtSetArg(al[ac], XmNleftOffset, 13);                  ac++;
1070   XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM);  ac++;
1071   XtSetArg(al[ac], XmNrightOffset, 13);                 ac++;
1072   row = XmCreateRowColumn (form, "row", al, ac);
1073   
1074   n_children = 0;
1075   for (i = 0; i < left_buttons; i++)
1076     {
1077       char button_name [16];
1078       sprintf (button_name, "button%d", i + 1);
1079       ac = 0;
1080       if (i == 0)
1081         {
1082           XtSetArg(al[ac], XmNhighlightThickness, 1); ac++;
1083           XtSetArg(al[ac], XmNshowAsDefault, TRUE);   ac++;
1084         }
1085       XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1086       children [n_children] = XmCreatePushButton (row, button_name, al, ac);
1087       DO_DND_KLUDGE (children [n_children]);
1088
1089       if (i == 0)
1090         {
1091           button = children [n_children];
1092           ac = 0;
1093           XtSetArg(al[ac], XmNdefaultButton, button); ac++;
1094           XtSetValues (row, al, ac);
1095
1096 #ifdef ARMANDACTIVATE_KLUDGE    /* See comment above */
1097           {
1098             XtTranslations losers = XtParseTranslationTable (button_trans);
1099             XtOverrideTranslations (button, losers);
1100             XtFree ((char *) losers);
1101           }
1102 #endif /* ARMANDACTIVATE_KLUDGE */
1103         }
1104
1105       n_children++;
1106     }
1107
1108   /* invisible seperator button */
1109   ac = 0;
1110   XtSetArg (al[ac], XmNmappedWhenManaged, FALSE); ac++;
1111   children [n_children] = XmCreateLabel (row, "separator_button",
1112                                          al, ac);
1113   DO_DND_KLUDGE (children [n_children]);
1114   n_children++;
1115   
1116   for (i = 0; i < right_buttons; i++)
1117     {
1118       char button_name [16];
1119       sprintf (button_name, "button%d", left_buttons + i + 1);
1120       ac = 0;
1121       XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1122       children [n_children] = XmCreatePushButton (row, button_name, al, ac);
1123       DO_DND_KLUDGE (children [n_children]);
1124       if (! button) button = children [n_children];
1125       n_children++;
1126     }
1127   
1128   XtManageChildren (children, n_children);
1129   
1130   ac = 0;
1131   XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE);            ac++;
1132   XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET);       ac++;
1133   XtSetArg(al[ac], XmNbottomOffset, 13);                        ac++;
1134   XtSetArg(al[ac], XmNbottomWidget, row);                       ac++;
1135   XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM);           ac++;
1136   XtSetArg(al[ac], XmNleftOffset, 0);                           ac++;
1137   XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM);          ac++;
1138   XtSetArg(al[ac], XmNrightOffset, 0);                          ac++;
1139   separator = XmCreateSeparator (form, "", al, ac);
1140
1141   ac = 0;
1142   XtSetArg(al[ac], XmNlabelType, XmPIXMAP);                     ac++;
1143   XtSetArg(al[ac], XmNtopAttachment, XmATTACH_FORM);            ac++;
1144   XtSetArg(al[ac], XmNtopOffset, 13);                           ac++;
1145   XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_NONE);         ac++;
1146   XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM);           ac++;
1147   XtSetArg(al[ac], XmNleftOffset, 13);                          ac++;
1148   XtSetArg(al[ac], XmNrightAttachment, XmATTACH_NONE);          ac++;
1149   icon = XmCreateLabel (form, (char *) icon_name, al, ac);
1150   DO_DND_KLUDGE (icon);
1151
1152   ac = 0;
1153   XtSetArg(al[ac], XmNmappedWhenManaged, FALSE);                ac++;
1154   XtSetArg(al[ac], XmNtopAttachment, XmATTACH_WIDGET);          ac++;
1155   XtSetArg(al[ac], XmNtopOffset, 6);                            ac++;
1156   XtSetArg(al[ac], XmNtopWidget, icon);                         ac++;
1157   XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET);       ac++;
1158   XtSetArg(al[ac], XmNbottomOffset, 6);                         ac++;
1159   XtSetArg(al[ac], XmNbottomWidget, separator);                 ac++;
1160   XtSetArg(al[ac], XmNleftAttachment, XmATTACH_NONE);           ac++;
1161   XtSetArg(al[ac], XmNrightAttachment, XmATTACH_NONE);          ac++;
1162   icon_separator = XmCreateLabel (form, "", al, ac);
1163   DO_DND_KLUDGE (icon_separator);
1164
1165   if (text_input_slot)
1166     {
1167       ac = 0;
1168       XtSetArg(al[ac], XmNcolumns, 50);                         ac++;
1169       XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE);        ac++;
1170       XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET);   ac++;
1171       XtSetArg(al[ac], XmNbottomOffset, 13);                    ac++;
1172       XtSetArg(al[ac], XmNbottomWidget, separator);             ac++;
1173       XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET);     ac++;
1174       XtSetArg(al[ac], XmNleftOffset, 13);                      ac++;
1175       XtSetArg(al[ac], XmNleftWidget, icon);                    ac++;
1176       XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM);      ac++;
1177       XtSetArg(al[ac], XmNrightOffset, 13);                     ac++;
1178       value = XmCreateTextField (form, "value", al, ac);
1179       DO_DND_KLUDGE (value);
1180     }
1181   else if (radio_box)
1182     {
1183       Widget radio_butt;
1184       ac = 0;
1185       XtSetArg(al[ac], XmNmarginWidth, 0);                      ac++;
1186       XtSetArg(al[ac], XmNmarginHeight, 0);                     ac++;
1187       XtSetArg(al[ac], XmNspacing, 13);                         ac++;
1188       XtSetArg(al[ac], XmNalignment, XmALIGNMENT_CENTER);       ac++;
1189       XtSetArg(al[ac], XmNorientation, XmHORIZONTAL);           ac++;
1190       XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET);   ac++;
1191       XtSetArg(al[ac], XmNbottomOffset, 13);                    ac++;
1192       XtSetArg(al[ac], XmNbottomWidget, separator);             ac++;
1193       XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET);     ac++;
1194       XtSetArg(al[ac], XmNleftOffset, 13);                      ac++;
1195       XtSetArg(al[ac], XmNleftWidget, icon);                    ac++;
1196       XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM);      ac++;
1197       XtSetArg(al[ac], XmNrightOffset, 13);                     ac++;
1198       value = XmCreateRadioBox (form, "radiobutton1", al, ac);
1199       ac = 0;
1200       i = 0;
1201       radio_butt = XmCreateToggleButtonGadget (value, "radio1", al, ac);
1202       children [i++] = radio_butt;
1203       radio_butt = XmCreateToggleButtonGadget (value, "radio2", al, ac);
1204       children [i++] = radio_butt;
1205       radio_butt = XmCreateToggleButtonGadget (value, "radio3", al, ac);
1206       children [i++] = radio_butt;
1207       XtManageChildren (children, i);
1208     }
1209   else if (list)
1210     {
1211       ac = 0;
1212       XtSetArg(al[ac], XmNvisibleItemCount, 5);                 ac++;
1213       XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE);        ac++;
1214       XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET);   ac++;
1215       XtSetArg(al[ac], XmNbottomOffset, 13);                    ac++;
1216       XtSetArg(al[ac], XmNbottomWidget, separator);             ac++;
1217       XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET);     ac++;
1218       XtSetArg(al[ac], XmNleftOffset, 13);                      ac++;
1219       XtSetArg(al[ac], XmNleftWidget, icon);                    ac++;
1220       XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM);      ac++;
1221       XtSetArg(al[ac], XmNrightOffset, 13);                     ac++;
1222       value = XmCreateScrolledList (form, "list", al, ac);
1223
1224       /* this is the easiest way I found to have the dble click in the
1225          list activate the default button */
1226       XtAddCallback (value, XmNdefaultActionCallback, activate_button, button);
1227     }
1228   
1229   ac = 0;
1230   XtSetArg(al[ac], XmNalignment, XmALIGNMENT_BEGINNING);        ac++;
1231   XtSetArg(al[ac], XmNtopAttachment, XmATTACH_FORM);            ac++;
1232   XtSetArg(al[ac], XmNtopOffset, 13);                           ac++;
1233   XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET);       ac++;
1234   XtSetArg(al[ac], XmNbottomOffset, 13);                        ac++;
1235   XtSetArg(al[ac], XmNbottomWidget,
1236            text_input_slot || radio_box || list ? value : separator); ac++;
1237   XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET);         ac++;
1238   XtSetArg(al[ac], XmNleftOffset, 13);                          ac++;
1239   XtSetArg(al[ac], XmNleftWidget, icon);                        ac++;
1240   XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM);          ac++;
1241   XtSetArg(al[ac], XmNrightOffset, 13);                         ac++;
1242   message = XmCreateLabel (form, "message", al, ac);
1243   DO_DND_KLUDGE (message);
1244   
1245   if (list)
1246     XtManageChild (value);
1247
1248   i = 0;
1249   children [i] = row; i++;
1250   children [i] = separator; i++;
1251   if (text_input_slot || radio_box)
1252     {
1253       children [i] = value; i++;
1254     }
1255   children [i] = message; i++;
1256   children [i] = icon; i++;
1257   children [i] = icon_separator; i++;
1258   XtManageChildren (children, i);
1259   
1260   if (text_input_slot || list)
1261     {
1262       XtInstallAccelerators (value, button);
1263       XmProcessTraversal(value, XmTRAVERSE_CURRENT);
1264     }
1265   else
1266     {
1267       XtInstallAccelerators (form, button);
1268       XmProcessTraversal(value, XmTRAVERSE_CURRENT);
1269     }
1270   
1271 #ifdef DND_KLUDGE
1272   XtFree ((char *) dnd_override);
1273 #endif
1274 #undef DO_DND_KLUDGE
1275
1276   return result;
1277 }
1278
1279 static destroyed_instance*
1280 find_matching_instance (widget_instance* instance)
1281 {
1282   destroyed_instance*   cur;
1283   destroyed_instance*   prev;
1284   char* type = instance->info->type;
1285   char* name = instance->info->name;
1286
1287   for (prev = NULL, cur = all_destroyed_instances;
1288        cur;
1289        prev = cur, cur = cur->next)
1290     {
1291       if (!strcmp (cur->name, name)
1292           && !strcmp (cur->type, type)
1293           && cur->parent == instance->parent
1294           && cur->pop_up_p == instance->pop_up_p)
1295         {
1296           if (prev)
1297             prev->next = cur->next;
1298           else
1299             all_destroyed_instances = cur->next;
1300           return cur;
1301         }
1302       /* do some cleanup */
1303       else if (!cur->widget)
1304         {
1305           if (prev)
1306             prev->next = cur->next;
1307           else
1308             all_destroyed_instances = cur->next;
1309           free_destroyed_instance (cur);
1310           cur = prev ? prev : all_destroyed_instances;
1311         }
1312     }
1313   return NULL;
1314 }
1315
1316 static void
1317 mark_dead_instance_destroyed (Widget widget, XtPointer closure,
1318                               XtPointer call_data)
1319 {
1320   destroyed_instance* instance = (destroyed_instance*)closure;
1321   instance->widget = NULL;
1322 }
1323
1324 static void
1325 recenter_widget (Widget widget)
1326 {
1327   Widget parent = XtParent (widget);
1328   Screen* screen = XtScreen (widget);
1329   Dimension screen_width = WidthOfScreen (screen);
1330   Dimension screen_height = HeightOfScreen (screen);
1331   Dimension parent_width = 0;
1332   Dimension parent_height = 0;
1333   Dimension child_width = 0;
1334   Dimension child_height = 0;
1335   Position x;
1336   Position y;
1337   Arg al [2];
1338
1339   XtSetArg (al [0], XtNwidth,  &child_width);
1340   XtSetArg (al [1], XtNheight, &child_height);
1341   XtGetValues (widget, al, 2);
1342
1343   XtSetArg (al [0], XtNwidth,  &parent_width);
1344   XtSetArg (al [1], XtNheight, &parent_height);
1345   XtGetValues (parent, al, 2);
1346
1347   x = (Position) ((parent_width  - child_width)  / 2);
1348   y = (Position) ((parent_height - child_height) / 2);
1349   
1350   XtTranslateCoords (parent, x, y, &x, &y);
1351
1352   if ((Dimension) (x + child_width) > screen_width)
1353     x = screen_width - child_width;
1354   if (x < 0)
1355     x = 0;
1356
1357   if ((Dimension) (y + child_height) > screen_height)
1358     y = screen_height - child_height;
1359   if (y < 0)
1360     y = 0;
1361
1362   XtSetArg (al [0], XtNx, x);
1363   XtSetArg (al [1], XtNy, y);
1364   XtSetValues (widget, al, 2);
1365 }
1366
1367 static Widget
1368 recycle_instance (destroyed_instance* instance)
1369 {
1370   Widget widget = instance->widget;
1371
1372   /* widget is NULL if the parent was destroyed. */
1373   if (widget)
1374     {
1375       Widget focus;
1376       Widget separator;
1377
1378       /* Remove the destroy callback as the instance is not in the list
1379          anymore */
1380       XtRemoveCallback (instance->parent, XtNdestroyCallback,
1381                         mark_dead_instance_destroyed,
1382                         (XtPointer)instance);
1383
1384       /* Give the focus to the initial item */
1385       focus = XtNameToWidget (widget, "*value");
1386       if (!focus)
1387         focus = XtNameToWidget (widget, "*button1");
1388       if (focus)
1389         XmProcessTraversal(focus, XmTRAVERSE_CURRENT);
1390
1391       /* shrink the separator label back to their original size */
1392       separator = XtNameToWidget (widget, "*separator_button");
1393       if (separator)
1394         {
1395           Arg al [2];
1396           XtSetArg (al [0], XtNwidth,  5);
1397           XtSetArg (al [1], XtNheight, 5);
1398           XtSetValues (separator, al, 2);
1399         }
1400
1401       /* Center the dialog in its parent */
1402       recenter_widget (widget);
1403     }
1404   free_destroyed_instance (instance);
1405   return widget;
1406 }
1407
1408 Widget
1409 xm_create_dialog (widget_instance* instance)
1410 {
1411   char*         name = instance->info->type;
1412   Widget        parent = instance->parent;
1413   Widget        widget;
1414   Boolean       pop_up_p = instance->pop_up_p;
1415   CONST char*   shell_name = 0;
1416   CONST char*   icon_name = 0;
1417   Boolean       text_input_slot = False;
1418   Boolean       radio_box = False;
1419   Boolean       list = False;
1420   int           total_buttons;
1421   int           left_buttons = 0;
1422   int           right_buttons = 1;
1423   destroyed_instance*   dead_one;
1424
1425   /* try to find a widget to recycle */
1426   dead_one = find_matching_instance (instance);
1427   if (dead_one)
1428     {
1429       Widget recycled_widget = recycle_instance (dead_one);
1430       if (recycled_widget)
1431         return recycled_widget;
1432     }
1433
1434   switch (name [0]){
1435   case 'E': case 'e':
1436     icon_name = "dbox-error";
1437     shell_name = "Error";
1438     break;
1439
1440   case 'I': case 'i':
1441     icon_name = "dbox-info";
1442     shell_name = "Information";
1443     break;
1444
1445   case 'L': case 'l':
1446     list = True;
1447     icon_name = "dbox-question";
1448     shell_name = "Prompt";
1449     break;
1450
1451   case 'P': case 'p':
1452     text_input_slot = True;
1453     icon_name = "dbox-question";
1454     shell_name = "Prompt";
1455     break;
1456
1457   case 'Q': case 'q':
1458     icon_name = "dbox-question";
1459     shell_name = "Question";
1460     break;
1461   }
1462   
1463   total_buttons = name [1] - '0';
1464
1465   if (name [3] == 'T' || name [3] == 't')
1466     {
1467       text_input_slot = False;
1468       radio_box = True;
1469     }
1470   else if (name [3])
1471     right_buttons = name [4] - '0';
1472   
1473   left_buttons = total_buttons - right_buttons;
1474   
1475   widget = make_dialog (name, parent, pop_up_p,
1476                         shell_name, icon_name, text_input_slot, radio_box,
1477                         list, left_buttons, right_buttons);
1478
1479   XtAddCallback (widget, XmNpopdownCallback, xm_nosel_callback,
1480                  (XtPointer) instance);
1481   return widget;
1482 }
1483
1484 #endif /* LWLIB_DIALOGS_MOTIF */
1485
1486 #ifdef LWLIB_MENUBARS_MOTIF
1487 static Widget
1488 make_menubar (widget_instance* instance)
1489 {
1490   Arg al[10];
1491   int ac = 0;
1492
1493   XtSetArg(al[ac], XmNmarginHeight, 0);    ac++;
1494   XtSetArg(al[ac], XmNshadowThickness, 3); ac++;
1495
1496   return XmCreateMenuBar (instance->parent, instance->info->name, al, ac);
1497 }
1498
1499 static void
1500 remove_grabs (Widget shell, XtPointer closure, XtPointer call_data)
1501 {
1502   Widget menu = (Widget) closure;
1503   XmRemoveFromPostFromList (menu, XtParent (XtParent ((Widget) menu)));
1504 }
1505
1506 static Widget
1507 make_popup_menu (widget_instance* instance)
1508 {
1509   Widget parent = instance->parent;
1510   Window parent_window = parent->core.window;
1511   Widget result;
1512
1513   /* sets the parent window to 0 to fool Motif into not generating a grab */
1514   parent->core.window = 0;
1515   result = XmCreatePopupMenu (parent, instance->info->name, NULL, 0);
1516   XtAddCallback (XtParent (result), XmNpopdownCallback, remove_grabs,
1517                  (XtPointer)result);
1518   parent->core.window = parent_window;
1519   return result;
1520 }
1521 #endif /* LWLIB_MENUBARS_MOTIF */
1522
1523 #ifdef LWLIB_SCROLLBARS_MOTIF
1524 static Widget
1525 make_scrollbar (widget_instance *instance, int vertical)
1526 {
1527   Arg al[20];
1528   int ac = 0;
1529   static XtCallbackRec callbacks[2] =
1530   { {xm_scrollbar_callback, NULL}, {NULL, NULL} };
1531
1532   callbacks[0].closure  = (XtPointer) instance;
1533   
1534   XtSetArg (al[ac], XmNminimum,       1); ac++;
1535   XtSetArg (al[ac], XmNmaximum, INT_MAX); ac++;
1536   XtSetArg (al[ac], XmNincrement,     1); ac++;
1537   XtSetArg (al[ac], XmNpageIncrement, 1); ac++;
1538   XtSetArg (al[ac], XmNborderWidth,   0); ac++;
1539   XtSetArg (al[ac], XmNorientation, vertical ? XmVERTICAL : XmHORIZONTAL); ac++;
1540
1541   XtSetArg (al[ac], XmNdecrementCallback,       callbacks); ac++;
1542   XtSetArg (al[ac], XmNdragCallback,            callbacks); ac++;
1543   XtSetArg (al[ac], XmNincrementCallback,       callbacks); ac++;
1544   XtSetArg (al[ac], XmNpageDecrementCallback,   callbacks); ac++;
1545   XtSetArg (al[ac], XmNpageIncrementCallback,   callbacks); ac++;
1546   XtSetArg (al[ac], XmNtoBottomCallback,        callbacks); ac++;
1547   XtSetArg (al[ac], XmNtoTopCallback,           callbacks); ac++;
1548   XtSetArg (al[ac], XmNvalueChangedCallback,    callbacks); ac++;
1549
1550   return XmCreateScrollBar (instance->parent, instance->info->name, al, ac);
1551 }
1552
1553 static Widget
1554 make_vertical_scrollbar (widget_instance *instance)
1555 {
1556   return make_scrollbar (instance, 1);
1557 }
1558
1559 static Widget
1560 make_horizontal_scrollbar (widget_instance *instance)
1561 {
1562   return make_scrollbar (instance, 0);
1563 }
1564
1565 #endif /* LWLIB_SCROLLBARS_MOTIF */
1566
1567 /* glyph widgets */
1568 static Widget
1569 make_button (widget_instance *instance)
1570 {
1571   Arg al[20];
1572   int ac = 0;
1573   Widget button = 0;
1574   widget_value* val = instance->info->val;
1575
1576   XtSetArg (al [ac], XmNsensitive, val->enabled);               ac++;
1577   XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING);      ac++;
1578   XtSetArg (al [ac], XmNuserData, val->call_data);              ac++;
1579   XtSetArg (al [ac], XmNmappedWhenManaged, FALSE);      ac++;
1580   /* The highlight doesn't appear to be dynamically set which makes it
1581      look ugly.  I think this may be a LessTif bug but for now we just
1582      get rid of it. */
1583   XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;
1584
1585   /* add any args the user supplied for creation time */
1586   lw_add_value_args_to_args (val, al, &ac);
1587
1588   if (!val->call_data)
1589     button = XmCreateLabel (instance->parent, val->name, al, ac);
1590   
1591   else if (val->type == TOGGLE_TYPE || val->type == RADIO_TYPE)
1592     {
1593       XtSetArg (al [ac], XmNset, val->selected);        ac++;
1594       XtSetArg (al [ac], XmNindicatorType,
1595                 (val->type == TOGGLE_TYPE ?
1596                  XmN_OF_MANY : XmONE_OF_MANY));    ac++;
1597       XtSetArg (al [ac], XmNvisibleWhenOff, True); ac++;
1598       button = XmCreateToggleButton (instance->parent, val->name, al, ac);
1599       XtRemoveAllCallbacks (button, XmNvalueChangedCallback);
1600       XtAddCallback (button, XmNvalueChangedCallback, xm_generic_callback,
1601                      (XtPointer)instance);
1602     }
1603   else
1604     {
1605       button = XmCreatePushButton (instance->parent, val->name, al, ac);
1606       XtAddCallback (button, XmNactivateCallback, xm_generic_callback,
1607                      (XtPointer)instance);
1608     }
1609
1610   XtManageChild (button);
1611
1612   return button;
1613 }
1614
1615 static Widget
1616 make_progress (widget_instance *instance)
1617 {
1618   Arg al[20];
1619   int ac = 0;
1620   Widget scale = 0;
1621   widget_value* val = instance->info->val;
1622
1623   XtSetArg (al [ac], XmNsensitive, val->enabled);               ac++;
1624   XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING);      ac++;
1625   XtSetArg (al [ac], XmNuserData, val->call_data);              ac++;
1626   XtSetArg (al [ac], XmNmappedWhenManaged, FALSE);      ac++;
1627   XtSetArg (al [ac], XmNorientation, XmHORIZONTAL);     ac++;
1628   /* The highlight doesn't appear to be dynamically set which makes it
1629      look ugly.  I think this may be a LessTif bug but for now we just
1630      get rid of it. */
1631   XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;
1632   if (!val->call_data)
1633     XtSetArg (al [ac], XmNsensitive, False);            ac++;
1634
1635   /* add any args the user supplied for creation time */
1636   lw_add_value_args_to_args (val, al, &ac);
1637
1638   scale = XmCreateScale (instance->parent, val->name, al, ac);
1639   if (val->call_data)
1640     XtAddCallback (scale, XmNvalueChangedCallback, xm_generic_callback,
1641                    (XtPointer)instance);
1642
1643   XtManageChild (scale);
1644
1645   return scale;
1646 }
1647
1648 static Widget
1649 make_text_field (widget_instance *instance)
1650 {
1651   Arg al[20];
1652   int ac = 0;
1653   Widget text = 0;
1654   widget_value* val = instance->info->val;
1655
1656   XtSetArg (al [ac], XmNsensitive, val->enabled && val->call_data);             ac++;
1657   XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING);      ac++;
1658   XtSetArg (al [ac], XmNuserData, val->call_data);              ac++;
1659   XtSetArg (al [ac], XmNmappedWhenManaged, FALSE);      ac++;
1660   /* The highlight doesn't appear to be dynamically set which makes it
1661      look ugly.  I think this may be a LessTif bug but for now we just
1662      get rid of it. */
1663   XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;
1664
1665   /* add any args the user supplied for creation time */
1666   lw_add_value_args_to_args (val, al, &ac);
1667
1668   text = XmCreateTextField (instance->parent, val->name, al, ac);
1669   if (val->call_data)
1670     XtAddCallback (text, XmNvalueChangedCallback, xm_generic_callback,
1671                    (XtPointer)instance);
1672
1673   XtManageChild (text);
1674
1675   return text;
1676 }
1677
1678 #if XmVERSION > 1
1679 static Widget
1680 make_combo_box (widget_instance *instance)
1681 {
1682   Arg al[20];
1683   int ac = 0;
1684   Widget combo = 0;
1685   widget_value* val = instance->info->val;
1686
1687   XtSetArg (al [ac], XmNsensitive, val->enabled);               ac++;
1688   XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING);      ac++;
1689   XtSetArg (al [ac], XmNuserData, val->call_data);              ac++;
1690   XtSetArg (al [ac], XmNmappedWhenManaged, FALSE);      ac++;
1691   /* The highlight doesn't appear to be dynamically set which makes it
1692      look ugly.  I think this may be a LessTif bug but for now we just
1693      get rid of it. */
1694   XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++;
1695
1696   /* add any args the user supplied for creation time */
1697   lw_add_value_args_to_args (val, al, &ac);
1698
1699   combo = XmCreateComboBox (instance->parent, val->name, al, ac);
1700   if (val->call_data)
1701     XtAddCallback (combo, XmNselectionCallback, xm_generic_callback,
1702                    (XtPointer)instance);
1703
1704   XtManageChild (combo);
1705
1706   return combo;
1707 }
1708 #endif
1709
1710 \f
1711 /* Table of functions to create widgets */
1712
1713 widget_creation_entry
1714 xm_creation_table [] = 
1715 {
1716 #ifdef LWLIB_MENUBARS_MOTIF
1717   {"menubar",                   make_menubar},
1718   {"popup",                     make_popup_menu},
1719 #endif
1720 #ifdef LWLIB_SCROLLBARS_MOTIF
1721   {"vertical-scrollbar",        make_vertical_scrollbar},
1722   {"horizontal-scrollbar",      make_horizontal_scrollbar},
1723 #endif
1724   {"button",            make_button},
1725   {"progress",          make_progress},
1726   {"text-field",                make_text_field},
1727 #if XmVERSION > 1
1728   {"combo-box",         make_combo_box},
1729 #endif
1730   {NULL, NULL}
1731 };
1732
1733 \f/* Destruction of instances */
1734 void
1735 xm_destroy_instance (widget_instance* instance)
1736 {
1737 #ifdef LWLIB_DIALOGS_MOTIF
1738   /* It appears that this is used only for dialog boxes. */
1739   Widget widget = instance->widget;
1740   /* recycle the dialog boxes */
1741   /* Disable the recycling until we can find a way to have the dialog box
1742      get reasonable layout after we modify its contents. */
1743   if (0
1744       && XtClass (widget) == xmDialogShellWidgetClass)
1745     {
1746       destroyed_instance* dead_instance =
1747         make_destroyed_instance (instance->info->name,
1748                                  instance->info->type,
1749                                  instance->widget,
1750                                  instance->parent,
1751                                  instance->pop_up_p);
1752       dead_instance->next = all_destroyed_instances;
1753       all_destroyed_instances = dead_instance;
1754       XtUnmanageChild (first_child (instance->widget));
1755       XFlush (XtDisplay (instance->widget));
1756       XtAddCallback (instance->parent, XtNdestroyCallback,
1757                      mark_dead_instance_destroyed, (XtPointer)dead_instance);
1758     }
1759   else
1760     {
1761       /* This might not be necessary now that the nosel is attached to
1762          popdown instead of destroy, but it can't hurt. */
1763       XtRemoveCallback (instance->widget, XtNdestroyCallback,
1764                         xm_nosel_callback, (XtPointer)instance);
1765
1766       XtDestroyWidget (instance->widget);
1767     }
1768 #endif /* LWLIB_DIALOGS_MOTIF */
1769 }
1770
1771 \f/* popup utility */
1772 #ifdef LWLIB_MENUBARS_MOTIF
1773
1774 void
1775 xm_popup_menu (Widget widget, XEvent *event)
1776 {
1777   if (event->type == ButtonPress || event->type == ButtonRelease)
1778     {
1779       /* This is so totally ridiculous: there's NO WAY to tell Motif
1780          that *any* button can select a menu item.  Only one button
1781          can have that honor.
1782        */
1783       char *trans = 0;
1784       if      (event->xbutton.state & Button5Mask) trans = "<Btn5Down>";
1785       else if (event->xbutton.state & Button4Mask) trans = "<Btn4Down>";
1786       else if (event->xbutton.state & Button3Mask) trans = "<Btn3Down>";
1787       else if (event->xbutton.state & Button2Mask) trans = "<Btn2Down>";
1788       else if (event->xbutton.state & Button1Mask) trans = "<Btn1Down>";
1789       if (trans)
1790         {
1791           Arg al [1];
1792           XtSetArg (al [0], XmNmenuPost, trans);
1793           XtSetValues (widget, al, 1);
1794         }
1795       XmMenuPosition (widget, (XButtonPressedEvent *) event);
1796     }
1797   XtManageChild (widget);
1798 }
1799
1800 #endif
1801
1802 #ifdef LWLIB_DIALOGS_MOTIF
1803
1804 static void
1805 set_min_dialog_size (Widget w)
1806 {
1807   short width;
1808   short height;
1809   Arg al [2];
1810
1811   XtSetArg (al [0], XmNwidth,  &width);
1812   XtSetArg (al [1], XmNheight, &height);
1813   XtGetValues (w, al, 2);
1814
1815   XtSetArg (al [0], XmNminWidth,  width);
1816   XtSetArg (al [1], XmNminHeight, height);
1817   XtSetValues (w, al, 2);
1818 }
1819
1820 #endif
1821
1822 void
1823 xm_pop_instance (widget_instance* instance, Boolean up)
1824 {
1825   Widget widget = instance->widget;
1826
1827 #ifdef LWLIB_DIALOGS_MOTIF
1828   if (XtClass (widget) == xmDialogShellWidgetClass)
1829     {
1830       Widget widget_to_manage = first_child (widget);
1831       if (up)
1832         {
1833           XtManageChild (widget_to_manage);
1834           set_min_dialog_size (widget);
1835           XmProcessTraversal(widget, XmTRAVERSE_CURRENT);
1836         }
1837       else
1838         XtUnmanageChild (widget_to_manage);
1839     }
1840   else
1841 #endif
1842     {
1843       if (up)
1844         XtManageChild (widget);
1845       else
1846         XtUnmanageChild (widget);       
1847     }
1848 }
1849
1850 \f
1851 /* motif callback */ 
1852
1853 enum do_call_type { pre_activate, selection, no_selection, post_activate };
1854
1855 static void
1856 do_call (Widget widget, XtPointer closure, enum do_call_type type)
1857 {
1858   XtPointer user_data;
1859   widget_instance* instance = (widget_instance*)closure;
1860   Widget instance_widget;
1861   LWLIB_ID id;
1862   Arg al [1];
1863
1864   if (!instance)
1865     return;
1866   if (widget->core.being_destroyed)
1867     return;
1868
1869   instance_widget = instance->widget;
1870   if (!instance_widget)
1871     return;
1872
1873   id = instance->info->id;
1874   user_data = NULL;
1875   XtSetArg(al [0], XmNuserData, &user_data);
1876   XtGetValues (widget, al, 1);
1877   switch (type)
1878     {
1879     case pre_activate:
1880       if (instance->info->pre_activate_cb)
1881         instance->info->pre_activate_cb (widget, id, user_data);
1882       break;
1883     case selection:
1884       if (instance->info->selection_cb)
1885         instance->info->selection_cb (widget, id, user_data);
1886       break;
1887     case no_selection:
1888       if (instance->info->selection_cb)
1889         instance->info->selection_cb (widget, id, (XtPointer) -1);
1890       break;
1891     case post_activate:
1892       if (instance->info->post_activate_cb)
1893         instance->info->post_activate_cb (widget, id, user_data);
1894       break;
1895     default:
1896       abort ();
1897     }
1898 }
1899
1900 /* Like lw_internal_update_other_instances except that it does not do
1901    anything if its shell parent is not managed.  This is to protect 
1902    lw_internal_update_other_instances to dereference freed memory
1903    if the widget was ``destroyed'' by caching it in the all_destroyed_instances
1904    list */
1905 static void
1906 xm_internal_update_other_instances (Widget widget, XtPointer closure,
1907                                     XtPointer call_data)
1908 {
1909   Widget parent;
1910   for (parent = widget; parent; parent = XtParent (parent))
1911     if (XtIsShell (parent))
1912       break;
1913     else if (!XtIsManaged (parent))
1914       return;
1915    lw_internal_update_other_instances (widget, closure, call_data);
1916 }
1917
1918 static void
1919 xm_generic_callback (Widget widget, XtPointer closure, XtPointer call_data)
1920 {
1921 #if (defined (LWLIB_MENUBARS_MOTIF) || defined (LWLIB_DIALOGS_MOTIF))
1922   /* We want the selected status to change only when we decide it
1923      should change.  Yuck but correct. */
1924   if (XtClass (widget) == xmToggleButtonWidgetClass
1925       || XtClass (widget) == xmToggleButtonGadgetClass)
1926     {
1927       Boolean check;
1928       Arg al [1];
1929
1930       XtSetArg (al [0], XmNset, &check);
1931       XtGetValues (widget, al, 1);
1932
1933       XtSetArg (al [0], XmNset, !check);
1934       XtSetValues (widget, al, 1);
1935     }
1936 #endif 
1937   lw_internal_update_other_instances (widget, closure, call_data);
1938   do_call (widget, closure, selection);
1939 }
1940
1941 static void
1942 xm_pop_down_callback (Widget widget, XtPointer closure, XtPointer call_data)
1943 {
1944   do_call (widget, closure, post_activate);
1945 }
1946
1947 #ifdef LWLIB_DIALOGS_MOTIF
1948
1949 static void
1950 xm_nosel_callback (Widget widget, XtPointer closure, XtPointer call_data)
1951 {
1952   /* This callback is only called when a dialog box is dismissed with the wm's
1953      destroy button (WM_DELETE_WINDOW.)  We want the dialog box to be destroyed
1954      in that case, not just unmapped, so that it releases its keyboard grabs.
1955      But there are problems with running our callbacks while the widget is in
1956      the process of being destroyed, so we set XmNdeleteResponse to XmUNMAP
1957      instead of XmDESTROY and then destroy it ourself after having run the
1958      callback.
1959    */
1960   do_call (widget, closure, no_selection);
1961   XtDestroyWidget (widget);
1962 }
1963
1964 #endif
1965
1966 #ifdef LWLIB_MENUBARS_MOTIF
1967
1968 static void
1969 xm_pull_down_callback (Widget widget, XtPointer closure, XtPointer call_data)
1970 {
1971 #if 0
1972   if (call_data)
1973     {
1974       /* new behavior for incremental menu construction */
1975       
1976     }
1977   else
1978 #endif 
1979     do_call (widget, closure, pre_activate);
1980 }
1981
1982 #endif /* LWLIB_MENUBARS_MOTIF */
1983
1984 #ifdef LWLIB_SCROLLBARS_MOTIF
1985 static void
1986 xm_scrollbar_callback (Widget widget, XtPointer closure, XtPointer call_data)
1987 {
1988   widget_instance *instance = (widget_instance *) closure;
1989   LWLIB_ID id;
1990   XmScrollBarCallbackStruct *data =
1991     (XmScrollBarCallbackStruct *) call_data;
1992   scroll_event event_data;
1993   scrollbar_values *val =
1994     (scrollbar_values *) instance->info->val->scrollbar_data;
1995   double percent;
1996
1997   if (!instance || widget->core.being_destroyed)
1998     return;
1999
2000   id = instance->info->id;
2001
2002   percent = (double) (data->value - 1) / (double) (INT_MAX - 1);
2003   event_data.slider_value =
2004     (int) (percent * (double) (val->maximum - val->minimum)) + val->minimum;
2005
2006   if (event_data.slider_value > (val->maximum - val->slider_size))
2007     event_data.slider_value = val->maximum - val->slider_size;
2008   else if (event_data.slider_value < 1)
2009     event_data.slider_value = 1;
2010
2011   if (data->event)
2012     {
2013       switch (data->event->xany.type)
2014         {
2015         case KeyPress:
2016         case KeyRelease:
2017           event_data.time = data->event->xkey.time;
2018           break;
2019         case ButtonPress:
2020         case ButtonRelease:
2021           event_data.time = data->event->xbutton.time;
2022           break;
2023         case MotionNotify:
2024           event_data.time = data->event->xmotion.time;
2025           break;
2026         case EnterNotify:
2027         case LeaveNotify:
2028           event_data.time = data->event->xcrossing.time;
2029           break;
2030         default:
2031           event_data.time = 0;
2032           break;
2033         }
2034     }
2035   else
2036     event_data.time = 0;
2037
2038   switch (data->reason)
2039     {
2040     case XmCR_DECREMENT:
2041       event_data.action = SCROLLBAR_LINE_UP;
2042       break;
2043     case XmCR_INCREMENT:
2044       event_data.action = SCROLLBAR_LINE_DOWN;
2045       break;
2046     case XmCR_PAGE_DECREMENT:
2047       event_data.action = SCROLLBAR_PAGE_UP;
2048       break;
2049     case XmCR_PAGE_INCREMENT:
2050       event_data.action = SCROLLBAR_PAGE_DOWN;
2051       break;
2052     case XmCR_TO_TOP:
2053       event_data.action = SCROLLBAR_TOP;
2054       break;
2055     case XmCR_TO_BOTTOM:
2056       event_data.action = SCROLLBAR_BOTTOM;
2057       break;
2058     case XmCR_DRAG:
2059       event_data.action = SCROLLBAR_DRAG;
2060       break;
2061     case XmCR_VALUE_CHANGED:
2062       event_data.action = SCROLLBAR_CHANGE;
2063       break;
2064     default:
2065       event_data.action = SCROLLBAR_CHANGE;
2066       break;
2067     }
2068
2069   if (instance->info->pre_activate_cb)
2070     instance->info->pre_activate_cb (widget, id, (XtPointer) &event_data);
2071 }
2072 #endif /* LWLIB_SCROLLBARS_MOTIF */
2073
2074 \f
2075 /* set the keyboard focus */
2076 void
2077 xm_set_keyboard_focus (Widget parent, Widget w)
2078 {
2079   XmProcessTraversal (w, XmTRAVERSE_CURRENT);
2080   /* At some point we believed that it was necessary to use XtSetKeyboardFocus
2081      instead of XmProcessTraversal when using Motif >= 1.2.1, but that's bogus.
2082      Presumably the problem was elsewhere, and is now gone...
2083    */
2084 }