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