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