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