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