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