1 /* The lwlib interface to Motif widgets.
2
3 Copyright (C) 1994-1997, 1999-2021 Free Software Foundation, Inc.
4 Copyright (C) 1992 Lucid, Inc.
5
6 This file is part of the Lucid Widget Library.
7
8 The Lucid Widget Library is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 1, or (at your option)
11 any later version.
12
13 The Lucid Widget Library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
20
21 #include <config.h>
22
23 #include <unistd.h>
24 #include <stdio.h>
25 #include <setjmp.h>
26
27 #include <X11/StringDefs.h>
28 #include <X11/IntrinsicP.h>
29 #include <X11/ObjectP.h>
30 #include <X11/CoreP.h>
31 #include <X11/CompositeP.h>
32
33 #include <lisp.h>
34
35 #include "lwlib-Xm.h"
36 #include "lwlib-utils.h"
37
38 #include <Xm/BulletinB.h>
39 #include <Xm/CascadeB.h>
40 #include <Xm/CascadeBG.h>
41 #include <Xm/DrawingA.h>
42 #include <Xm/FileSB.h>
43 #include <Xm/Label.h>
44 #include <Xm/List.h>
45 #include <Xm/MainW.h>
46 #include <Xm/MenuShell.h>
47 #include <Xm/MessageB.h>
48 #include <Xm/PanedW.h>
49 #include <Xm/PushB.h>
50 #include <Xm/PushBG.h>
51 #include <Xm/ArrowB.h>
52 #include <Xm/SelectioB.h>
53 #include <Xm/Text.h>
54 #include <Xm/TextF.h>
55 #include <Xm/ToggleB.h>
56 #include <Xm/ToggleBG.h>
57 #include <Xm/RowColumn.h>
58 #include <Xm/ScrolledW.h>
59 #include <Xm/Separator.h>
60 #include <Xm/DialogS.h>
61 #include <Xm/Form.h>
62
63 enum do_call_type { pre_activate, selection, no_selection, post_activate };
64
65
66 /* Structures to keep destroyed instances */
67 typedef struct _destroyed_instance
68 {
69 char* name;
70 char* type;
71 Widget widget;
72 Widget parent;
73 Boolean pop_up_p;
74 struct _destroyed_instance* next;
75 } destroyed_instance;
76
77 static destroyed_instance *make_destroyed_instance (char *, char *,
78 Widget, Widget,
79 Boolean);
80 static void free_destroyed_instance (destroyed_instance*);
81 Widget first_child (Widget);
82 static XmString resource_motif_string (Widget, char *);
83 static void destroy_all_children (Widget, int);
84 static void xm_update_label (widget_instance *, Widget, widget_value *);
85 static void xm_update_list (widget_instance *, Widget, widget_value *);
86 static void xm_update_pushbutton (widget_instance *, Widget,
87 widget_value *);
88 static void xm_update_cascadebutton (widget_instance *, Widget,
89 widget_value *);
90 static void xm_update_toggle (widget_instance *, Widget, widget_value *);
91 static void xm_update_radiobox (widget_instance *, Widget, widget_value *);
92 static void make_menu_in_widget (widget_instance *, Widget,
93 widget_value *, int);
94 static void update_one_menu_entry (widget_instance *, Widget,
95 widget_value *, Boolean);
96 static void xm_update_menu (widget_instance *, Widget, widget_value *,
97 Boolean);
98 static void xm_update_text (widget_instance *, Widget, widget_value *);
99 static void xm_update_text_field (widget_instance *, Widget,
100 widget_value *);
101 static void activate_button (Widget, XtPointer, XtPointer);
102 static Widget make_dialog (char *, Widget, Boolean, char *, char *,
103 Boolean, Boolean, Boolean, int, int);
104 static destroyed_instance* find_matching_instance (widget_instance*);
105 static void mark_dead_instance_destroyed (Widget, XtPointer, XtPointer);
106 static void recenter_widget (Widget);
107 static Widget recycle_instance (destroyed_instance*);
108 static Widget make_menubar (widget_instance*);
109 static void remove_grabs (Widget, XtPointer, XtPointer);
110 static Widget make_popup_menu (widget_instance*);
111 static Widget make_main (widget_instance*);
112 static void set_min_dialog_size (Widget);
113 static void do_call (Widget, XtPointer, enum do_call_type);
114 static void xm_generic_callback (Widget, XtPointer, XtPointer);
115 static void xm_nosel_callback (Widget, XtPointer, XtPointer);
116 static void xm_pull_down_callback (Widget, XtPointer, XtPointer);
117 static void xm_pop_down_callback (Widget, XtPointer, XtPointer);
118 static void xm_internal_update_other_instances (Widget, XtPointer,
119 XtPointer);
120 static void xm_arm_callback (Widget, XtPointer, XtPointer);
121
122 #if 0
123 void xm_update_one_widget (widget_instance *, Widget, widget_value *,
124 Boolean);
125 void xm_pop_instance (widget_instance*, Boolean);
126 void xm_manage_resizing (Widget, Boolean);
127 #endif
128
129
130 #if 0
131
132 /* Print the complete X resource name of widget WIDGET to stderr.
133 This is sometimes handy to have available. */
134
135 void
136 x_print_complete_resource_name (Widget widget)
137 {
138 int i;
139 String names[100];
140
141 for (i = 0; i < 100 && widget != NULL; ++i)
142 {
143 names[i] = XtName (widget);
144 widget = XtParent (widget);
145 }
146
147 for (--i; i >= 1; --i)
148 fprintf (stderr, "%s.", names[i]);
149 fprintf (stderr, "%s\n", names[0]);
150 }
151
152 #endif /* 0 */
153
154
155 static destroyed_instance *all_destroyed_instances = NULL;
156
157 static destroyed_instance*
make_destroyed_instance(char * name,char * type,Widget widget,Widget parent,Boolean pop_up_p)158 make_destroyed_instance (char* name,
159 char* type,
160 Widget widget,
161 Widget parent,
162 Boolean pop_up_p)
163 {
164 destroyed_instance* instance =
165 (destroyed_instance*) xmalloc (sizeof (destroyed_instance));
166 instance->name = xstrdup (name);
167 instance->type = xstrdup (type);
168 instance->widget = widget;
169 instance->parent = parent;
170 instance->pop_up_p = pop_up_p;
171 instance->next = NULL;
172 return instance;
173 }
174
175 static void
free_destroyed_instance(destroyed_instance * instance)176 free_destroyed_instance (destroyed_instance* instance)
177 {
178 xfree (instance->name);
179 xfree (instance->type);
180 xfree (instance);
181 }
182
183 /* motif utility functions */
184 Widget
first_child(Widget widget)185 first_child (Widget widget)
186 {
187 return ((CompositeWidget)widget)->composite.children [0];
188 }
189
190 Boolean
lw_motif_widget_p(Widget widget)191 lw_motif_widget_p (Widget widget)
192 {
193 return
194 XtClass (widget) == xmDialogShellWidgetClass
195 || XmIsPrimitive (widget) || XmIsManager (widget) || XmIsGadget (widget);
196 }
197
198 static XmString
resource_motif_string(Widget widget,char * name)199 resource_motif_string (Widget widget,
200 char* name)
201 {
202 XtResource resource;
203 XmString result = 0;
204
205 resource.resource_name = name;
206 resource.resource_class = XmCXmString;
207 resource.resource_type = XmRXmString;
208 resource.resource_size = sizeof (XmString);
209 resource.resource_offset = 0;
210 resource.default_type = XtRImmediate;
211 resource.default_addr = 0;
212
213 XtGetSubresources (widget, (XtPointer)&result, "dialogString",
214 "DialogString", &resource, 1, NULL, 0);
215 return result;
216 }
217
218 /* Destroy all of the children of WIDGET
219 starting with number FIRST_CHILD_TO_DESTROY. */
220
221 static void
destroy_all_children(Widget widget,int first_child_to_destroy)222 destroy_all_children (Widget widget,
223 int first_child_to_destroy)
224 {
225 Widget* children;
226 unsigned int number;
227 int i;
228
229 children = XtCompositeChildren (widget, &number);
230 if (children)
231 {
232 XtUnmanageChildren (children + first_child_to_destroy,
233 number - first_child_to_destroy);
234
235 /* Unmanage all children and destroy them. They will only be
236 really destroyed when we get out of DispatchEvent. */
237 for (i = first_child_to_destroy; i < number; i++)
238 {
239 Arg al[2];
240 Widget submenu = 0;
241 /* Cascade buttons have submenus,and these submenus
242 need to be freed. But they are not included in
243 XtCompositeChildren. So get it out of the cascade button
244 and free it. If this child is not a cascade button,
245 then submenu should remain unchanged. */
246 XtSetArg (al[0], XmNsubMenuId, &submenu);
247 XtGetValues (children[i], al, 1);
248 if (submenu)
249 {
250 destroy_all_children (submenu, 0);
251 XtDestroyWidget (submenu);
252 }
253 XtDestroyWidget (children[i]);
254 }
255
256 XtFree ((char *) children);
257 }
258 }
259
260
261
262 /* Callback XmNarmCallback and XmNdisarmCallback for buttons in a
263 menu. CLIENT_DATA contains a pointer to the widget_value
264 corresponding to widget W. CALL_DATA contains a
265 XmPushButtonCallbackStruct containing the reason why the callback
266 is called. */
267
268 static void
xm_arm_callback(Widget w,XtPointer client_data,XtPointer call_data)269 xm_arm_callback (Widget w, XtPointer client_data, XtPointer call_data)
270 {
271 XmPushButtonCallbackStruct *cbs = (XmPushButtonCallbackStruct *) call_data;
272 widget_value *wv = (widget_value *) client_data;
273 widget_instance *instance;
274
275 /* Get the id of the menu bar or popup menu this widget is in. */
276 while (w != NULL)
277 {
278 if (XmIsRowColumn (w))
279 {
280 unsigned char type = 0xff;
281
282 XtVaGetValues (w, XmNrowColumnType, &type, NULL);
283 if (type == XmMENU_BAR || type == XmMENU_POPUP)
284 break;
285 }
286
287 w = XtParent (w);
288 }
289
290 if (w != NULL)
291 {
292 instance = lw_get_widget_instance (w);
293 if (instance && instance->info->highlight_cb)
294 {
295 call_data = cbs->reason == XmCR_DISARM ? NULL : wv;
296 instance->info->highlight_cb (w, instance->info->id, call_data);
297 }
298 }
299 }
300
301
302
303 /* Update the label of widget WIDGET. WIDGET must be a Label widget
304 or a subclass of Label. WIDGET_INSTANCE is unused. VAL contains
305 the value to update.
306
307 Menus:
308
309 Emacs fills VAL->name with the text to display in the menu, and
310 sets VAL->value to null. Function make_menu_in_widget creates
311 widgets with VAL->name as resource name. This works because the
312 Label widget uses its resource name for display if no
313 XmNlabelString is set.
314
315 Dialogs:
316
317 VAL->name is again set to the resource name, but VAL->value is
318 not null, and contains the label string to display. */
319
320 static void
xm_update_label(widget_instance * instance,Widget widget,widget_value * val)321 xm_update_label (widget_instance* instance,
322 Widget widget,
323 widget_value* val)
324 {
325 XmString res_string = 0;
326 XmString built_string = 0;
327 XmString key_string = 0;
328 Arg al [256];
329 int ac;
330
331 ac = 0;
332
333 if (val->value)
334 {
335 /* A label string is specified, i.e. we are in a dialog. First
336 see if it is overridden by something from the resource file. */
337 res_string = resource_motif_string (widget, val->value);
338
339 if (res_string)
340 {
341 XtSetArg (al [ac], XmNlabelString, res_string); ac++;
342 }
343 else
344 {
345 built_string =
346 XmStringCreateLocalized (val->value);
347 XtSetArg (al [ac], XmNlabelString, built_string); ac++;
348 }
349
350 XtSetArg (al [ac], XmNlabelType, XmSTRING); ac++;
351 }
352
353 if (val->key)
354 {
355 key_string = XmStringCreateLocalized (val->key);
356 XtSetArg (al [ac], XmNacceleratorText, key_string); ac++;
357 }
358
359 if (ac)
360 XtSetValues (widget, al, ac);
361
362 if (built_string)
363 XmStringFree (built_string);
364
365 if (key_string)
366 XmStringFree (key_string);
367 }
368
369 /* update of list */
370 static void
xm_update_list(widget_instance * instance,Widget widget,widget_value * val)371 xm_update_list (widget_instance* instance,
372 Widget widget,
373 widget_value* val)
374 {
375 widget_value* cur;
376 int i;
377 XtRemoveAllCallbacks (widget, XmNsingleSelectionCallback);
378 XtAddCallback (widget, XmNsingleSelectionCallback, xm_generic_callback,
379 instance);
380 for (cur = val->contents, i = 0; cur; cur = cur->next)
381 if (cur->value)
382 {
383 XmString xmstr = XmStringCreateLocalized (cur->value);
384 i += 1;
385 XmListAddItem (widget, xmstr, 0);
386 if (cur->selected)
387 XmListSelectPos (widget, i, False);
388 XmStringFree (xmstr);
389 }
390 }
391
392 /* update of buttons */
393 static void
xm_update_pushbutton(widget_instance * instance,Widget widget,widget_value * val)394 xm_update_pushbutton (widget_instance* instance,
395 Widget widget,
396 widget_value* val)
397 {
398 XtVaSetValues (widget, XmNalignment, XmALIGNMENT_CENTER, NULL);
399 XtRemoveAllCallbacks (widget, XmNactivateCallback);
400 XtAddCallback (widget, XmNactivateCallback, xm_generic_callback, instance);
401 }
402
403 static void
xm_update_cascadebutton(widget_instance * instance,Widget widget,widget_value * val)404 xm_update_cascadebutton (widget_instance* instance,
405 Widget widget,
406 widget_value* val)
407 {
408 /* Should also rebuild the menu by calling ...update_menu... */
409 XtRemoveAllCallbacks (widget, XmNcascadingCallback);
410 XtAddCallback (widget, XmNcascadingCallback, xm_pull_down_callback,
411 instance);
412 }
413
414 /* update toggle and radiobox */
415 static void
xm_update_toggle(widget_instance * instance,Widget widget,widget_value * val)416 xm_update_toggle (widget_instance* instance,
417 Widget widget,
418 widget_value* val)
419 {
420 XtRemoveAllCallbacks (widget, XmNvalueChangedCallback);
421 XtAddCallback (widget, XmNvalueChangedCallback,
422 xm_generic_callback, instance);
423 XtVaSetValues (widget, XmNset, val->selected,
424 XmNalignment, XmALIGNMENT_BEGINNING, NULL);
425 }
426
427 static void
xm_update_radiobox(widget_instance * instance,Widget widget,widget_value * val)428 xm_update_radiobox (widget_instance* instance,
429 Widget widget,
430 widget_value* val)
431
432 {
433 Widget toggle;
434 widget_value* cur;
435
436 /* update the callback */
437 XtRemoveAllCallbacks (widget, XmNentryCallback);
438 XtAddCallback (widget, XmNentryCallback, xm_generic_callback, instance);
439
440 /* first update all the toggles */
441 /* Energize kernel interface is currently bad. It sets the selected widget
442 with the selected flag but returns it by its name. So we currently
443 have to support both setting the selection with the selected slot
444 of val contents and setting it with the "value" slot of val. The latter
445 has a higher priority. This to be removed when the kernel is fixed. */
446 for (cur = val->contents; cur; cur = cur->next)
447 {
448 toggle = XtNameToWidget (widget, cur->value);
449 if (toggle)
450 {
451 XtSetSensitive (toggle, cur->enabled);
452 if (!val->value && cur->selected)
453 XtVaSetValues (toggle, XmNset, cur->selected, NULL);
454 if (val->value && strcmp (val->value, cur->value))
455 XtVaSetValues (toggle, XmNset, False, NULL);
456 }
457 }
458
459 /* The selected was specified by the value slot */
460 if (val->value)
461 {
462 toggle = XtNameToWidget (widget, val->value);
463 if (toggle)
464 XtVaSetValues (toggle, XmNset, True, NULL);
465 }
466 }
467
468
469 /* update a popup menu, pulldown menu or a menubar */
470
471 /* KEEP_FIRST_CHILDREN gives the number of initial children to keep. */
472
473 static void
make_menu_in_widget(widget_instance * instance,Widget widget,widget_value * val,int keep_first_children)474 make_menu_in_widget (widget_instance* instance,
475 Widget widget,
476 widget_value* val,
477 int keep_first_children)
478 {
479 Widget* children = 0;
480 int num_children;
481 int child_index;
482 widget_value* cur;
483 Widget button = 0;
484 Widget menu;
485 Arg al [256];
486 int ac;
487 Boolean menubar_p;
488 unsigned char type;
489
490 Widget* old_children;
491 unsigned int old_num_children;
492
493 /* Disable drag and drop for labels in menu bar. */
494 static char overrideTrans[] = "<Btn2Down>: Noop()";
495 XtTranslations override = XtParseTranslationTable (overrideTrans);
496
497 old_children = XtCompositeChildren (widget, &old_num_children);
498
499 /* Allocate the children array */
500 for (num_children = 0, cur = val; cur; num_children++, cur = cur->next)
501 ;
502 children = (Widget*)(void*)XtMalloc (num_children * sizeof (Widget));
503
504 /* WIDGET should be a RowColumn. */
505 if (!XmIsRowColumn (widget))
506 emacs_abort ();
507
508 /* Determine whether WIDGET is a menu bar. */
509 type = -1;
510 XtSetArg (al[0], XmNrowColumnType, &type);
511 XtGetValues (widget, al, 1);
512 if (type != XmMENU_BAR && type != XmMENU_PULLDOWN && type != XmMENU_POPUP)
513 emacs_abort ();
514 menubar_p = type == XmMENU_BAR;
515
516 /* Add a callback to popups and pulldowns that is called when
517 it is made invisible again. */
518 if (!menubar_p)
519 XtAddCallback (XtParent (widget), XmNpopdownCallback,
520 xm_pop_down_callback, (XtPointer)instance);
521
522 /* Preserve the first KEEP_FIRST_CHILDREN old children. */
523 for (child_index = 0, cur = val; child_index < keep_first_children;
524 child_index++, cur = cur->next)
525 children[child_index] = old_children[child_index];
526
527 /* Check that those are all we have
528 (the caller should have deleted the rest). */
529 if (old_num_children != keep_first_children)
530 emacs_abort ();
531
532 /* Create the rest. */
533 for (child_index = keep_first_children; cur; child_index++, cur = cur->next)
534 {
535 enum menu_separator separator;
536
537 ac = 0;
538 XtSetArg (al[ac], XmNsensitive, cur->enabled); ac++;
539 XtSetArg (al[ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
540 XtSetArg (al[ac], XmNuserData, cur->call_data); ac++;
541
542 if (instance->pop_up_p && !cur->contents && !cur->call_data
543 && !lw_separator_p (cur->name, &separator, 1))
544 {
545 ac = 0;
546 XtSetArg (al[ac], XmNalignment, XmALIGNMENT_CENTER); ac++;
547 button = XmCreateLabel (widget, cur->name, al, ac);
548 }
549 else if (lw_separator_p (cur->name, &separator, 1))
550 {
551 ac = 0;
552 XtSetArg (al[ac], XmNseparatorType, separator); ++ac;
553 button = XmCreateSeparator (widget, cur->name, al, ac);
554 }
555 else if (!cur->contents)
556 {
557 if (menubar_p)
558 button = XmCreateCascadeButton (widget, cur->name, al, ac);
559 else if (!cur->call_data)
560 button = XmCreateLabel (widget, cur->name, al, ac);
561 else if (cur->button_type == BUTTON_TYPE_TOGGLE
562 || cur->button_type == BUTTON_TYPE_RADIO)
563 {
564 XtSetArg (al[ac], XmNset, cur->selected); ++ac;
565 XtSetArg (al[ac], XmNvisibleWhenOff, True); ++ac;
566 XtSetArg (al[ac], XmNindicatorType,
567 (cur->button_type == BUTTON_TYPE_TOGGLE
568 ? XmN_OF_MANY : XmONE_OF_MANY));
569 ++ac;
570 button = XmCreateToggleButton (widget, cur->name, al, ac);
571 XtAddCallback (button, XmNarmCallback, xm_arm_callback, cur);
572 XtAddCallback (button, XmNdisarmCallback, xm_arm_callback, cur);
573 }
574 else
575 {
576 button = XmCreatePushButton (widget, cur->name, al, ac);
577 XtAddCallback (button, XmNarmCallback, xm_arm_callback, cur);
578 XtAddCallback (button, XmNdisarmCallback, xm_arm_callback, cur);
579 }
580
581 xm_update_label (instance, button, cur);
582
583 /* Add a callback that is called when the button is
584 selected. Toggle buttons don't support
585 XmNactivateCallback, we use XmNvalueChangedCallback in
586 that case. Don't add a callback to a simple label. */
587 if (cur->button_type)
588 xm_update_toggle (instance, button, cur);
589 else if (cur->call_data)
590 XtAddCallback (button, XmNactivateCallback, xm_generic_callback,
591 (XtPointer)instance);
592 }
593 else
594 {
595 menu = XmCreatePulldownMenu (widget, cur->name, NULL, 0);
596
597 make_menu_in_widget (instance, menu, cur->contents, 0);
598 XtSetArg (al[ac], XmNsubMenuId, menu); ac++;
599 button = XmCreateCascadeButton (widget, cur->name, al, ac);
600
601 xm_update_label (instance, button, cur);
602
603 XtAddCallback (button, XmNcascadingCallback, xm_pull_down_callback,
604 (XtPointer)instance);
605 XtOverrideTranslations (button, override);
606
607 }
608
609 children[child_index] = button;
610 }
611
612 /* Last entry is the help button. The original comment read "Has to
613 be done after managing the buttons otherwise the menubar is only
614 4 pixels high." This is no longer true, and to make
615 XmNmenuHelpWidget work, we need to set it before managing the
616 children.. --gerd. */
617 if (button)
618 XtVaSetValues (widget, XmNmenuHelpWidget, button, NULL);
619
620 if (num_children)
621 XtManageChildren (children, num_children);
622
623 XtFree ((char *) children);
624 if (old_children)
625 XtFree ((char *) old_children);
626 }
627
628 static void
update_one_menu_entry(widget_instance * instance,Widget widget,widget_value * val,Boolean deep_p)629 update_one_menu_entry (widget_instance* instance,
630 Widget widget,
631 widget_value* val,
632 Boolean deep_p)
633 {
634 Arg al [256];
635 int ac;
636 Widget menu;
637 widget_value* contents;
638
639 if (val->this_one_change == NO_CHANGE)
640 return;
641
642 /* update the sensitivity and userdata */
643 /* Common to all widget types */
644 XtSetSensitive (widget, val->enabled);
645 XtVaSetValues (widget, XmNuserData, val->call_data, NULL);
646
647 /* update the menu button as a label. */
648 if (val->this_one_change >= VISIBLE_CHANGE)
649 {
650 xm_update_label (instance, widget, val);
651 if (val->button_type)
652 xm_update_toggle (instance, widget, val);
653 }
654
655 /* update the pulldown/pullaside as needed */
656 ac = 0;
657 menu = NULL;
658 XtSetArg (al [ac], XmNsubMenuId, &menu); ac++;
659 XtGetValues (widget, al, ac);
660
661 contents = val->contents;
662
663 if (!menu)
664 {
665 if (contents)
666 {
667 unsigned int old_num_children, i;
668 Widget parent;
669 Widget *widget_list;
670
671 parent = XtParent (widget);
672 widget_list = XtCompositeChildren (parent, &old_num_children);
673
674 /* Find the widget position within the parent's widget list. */
675 for (i = 0; i < old_num_children; i++)
676 if (strcmp (XtName (widget_list[i]), XtName (widget)) == 0)
677 break;
678 if (i == old_num_children)
679 emacs_abort ();
680 if (XmIsCascadeButton (widget_list[i]))
681 {
682 menu = XmCreatePulldownMenu (parent, XtName(widget), NULL, 0);
683 make_menu_in_widget (instance, menu, contents, 0);
684 ac = 0;
685 XtSetArg (al [ac], XmNsubMenuId, menu); ac++;
686 XtSetValues (widget, al, ac);
687 }
688 else
689 {
690 Widget button;
691
692 /* The current menuitem is a XmPushButtonGadget, it
693 needs to be replaced by a CascadeButtonGadget */
694 XtDestroyWidget (widget_list[i]);
695 menu = XmCreatePulldownMenu (parent, val->name, NULL, 0);
696 make_menu_in_widget (instance, menu, contents, 0);
697 ac = 0;
698 XtSetArg (al [ac], XmNsubMenuId, menu); ac++;
699 /* Non-zero values don't work reliably in
700 conjunction with Emacs' event loop */
701 XtSetArg (al [ac], XmNmappingDelay, 0); ac++;
702 #ifdef XmNpositionIndex /* This is undefined on SCO ODT 2.0. */
703 /* Tell Motif to put it in the right place */
704 XtSetArg (al [ac], XmNpositionIndex , i); ac++;
705 #endif
706 button = XmCreateCascadeButton (parent, val->name, al, ac);
707 xm_update_label (instance, button, val);
708
709 XtAddCallback (button, XmNcascadingCallback, xm_pull_down_callback,
710 (XtPointer)instance);
711 XtManageChild (button);
712 }
713
714 if (widget_list)
715 XtFree ((char*) widget_list);
716 }
717 }
718 else if (!contents)
719 {
720 ac = 0;
721 XtSetArg (al [ac], XmNsubMenuId, NULL); ac++;
722 XtSetValues (widget, al, ac);
723 XtDestroyWidget (menu);
724 }
725 else if (deep_p && contents->change != NO_CHANGE)
726 xm_update_menu (instance, menu, val, 1);
727 }
728
729 static void
xm_update_menu(widget_instance * instance,Widget widget,widget_value * val,Boolean deep_p)730 xm_update_menu (widget_instance* instance,
731 Widget widget,
732 widget_value* val,
733 Boolean deep_p)
734 {
735 Widget* children;
736 unsigned int num_children;
737 int num_children_to_keep = 0;
738 int i;
739 widget_value* cur;
740
741 children = XtCompositeChildren (widget, &num_children);
742
743 /* Widget is a RowColumn widget whose contents have to be updated
744 * to reflect the list of items in val->contents */
745
746 /* See how many buttons we can keep, and how many we
747 must completely replace. */
748 if (val->contents == 0)
749 num_children_to_keep = 0;
750 else if (val->contents->change == STRUCTURAL_CHANGE)
751 {
752 if (children)
753 {
754 for (i = 0, cur = val->contents;
755 (i < num_children
756 && cur); /* how else to ditch unwanted children ?? - mgd */
757 i++, cur = cur->next)
758 {
759 if (cur->this_one_change == STRUCTURAL_CHANGE)
760 break;
761 }
762
763 num_children_to_keep = i;
764 }
765 }
766 else
767 num_children_to_keep = num_children;
768
769 /* Update all the buttons of the RowColumn, in order,
770 except for those we are going to replace entirely. */
771 if (children)
772 {
773 for (i = 0, cur = val->contents; i < num_children_to_keep; i++)
774 {
775 if (!cur)
776 {
777 num_children_to_keep = i;
778 break;
779 }
780 if (children [i]->core.being_destroyed
781 || strcmp (XtName (children [i]), cur->name))
782 continue;
783 update_one_menu_entry (instance, children [i], cur, deep_p);
784 cur = cur->next;
785 }
786 }
787
788 /* Now replace from scratch all the buttons after the last
789 place that the top-level structure changed. */
790 if (val->contents && val->contents->change == STRUCTURAL_CHANGE)
791 {
792 destroy_all_children (widget, num_children_to_keep);
793 make_menu_in_widget (instance, widget, val->contents,
794 num_children_to_keep);
795 }
796
797 XtFree ((char *) children);
798 }
799
800
801 /* update text widgets */
802
803 static void
xm_update_text(widget_instance * instance,Widget widget,widget_value * val)804 xm_update_text (widget_instance* instance,
805 Widget widget,
806 widget_value* val)
807 {
808 XmTextSetString (widget, val->value ? val->value : "");
809 XtRemoveAllCallbacks (widget, XmNactivateCallback);
810 XtAddCallback (widget, XmNactivateCallback, xm_generic_callback, instance);
811 XtRemoveAllCallbacks (widget, XmNvalueChangedCallback);
812 XtAddCallback (widget, XmNvalueChangedCallback,
813 xm_internal_update_other_instances, instance);
814 }
815
816 static void
xm_update_text_field(widget_instance * instance,Widget widget,widget_value * val)817 xm_update_text_field (widget_instance* instance,
818 Widget widget,
819 widget_value* val)
820 {
821 XmTextFieldSetString (widget, val->value ? val->value : "");
822 XtRemoveAllCallbacks (widget, XmNactivateCallback);
823 XtAddCallback (widget, XmNactivateCallback, xm_generic_callback, instance);
824 XtRemoveAllCallbacks (widget, XmNvalueChangedCallback);
825 XtAddCallback (widget, XmNvalueChangedCallback,
826 xm_internal_update_other_instances, instance);
827 }
828
829
830 /* update a motif widget */
831
832 void
xm_update_one_widget(widget_instance * instance,Widget widget,widget_value * val,Boolean deep_p)833 xm_update_one_widget (widget_instance* instance,
834 Widget widget,
835 widget_value* val,
836 Boolean deep_p)
837 {
838 WidgetClass class;
839
840 /* Mark as not edited */
841 val->edited = False;
842
843 /* Common to all widget types */
844 XtSetSensitive (widget, val->enabled);
845 XtVaSetValues (widget, XmNuserData, val->call_data, NULL);
846
847 /* Common to all label like widgets */
848 if (XtIsSubclass (widget, xmLabelWidgetClass))
849 xm_update_label (instance, widget, val);
850
851 class = XtClass (widget);
852 /* Class specific things */
853 if (class == xmPushButtonWidgetClass ||
854 class == xmArrowButtonWidgetClass)
855 {
856 xm_update_pushbutton (instance, widget, val);
857 }
858 else if (class == xmCascadeButtonWidgetClass)
859 {
860 xm_update_cascadebutton (instance, widget, val);
861 }
862 else if (class == xmToggleButtonWidgetClass
863 || class == xmToggleButtonGadgetClass)
864 {
865 xm_update_toggle (instance, widget, val);
866 }
867 else if (class == xmRowColumnWidgetClass)
868 {
869 Boolean radiobox = 0;
870 int ac = 0;
871 Arg al [1];
872
873 XtSetArg (al [ac], XmNradioBehavior, &radiobox); ac++;
874 XtGetValues (widget, al, ac);
875
876 if (radiobox)
877 xm_update_radiobox (instance, widget, val);
878 else
879 xm_update_menu (instance, widget, val, deep_p);
880 }
881 else if (class == xmTextWidgetClass)
882 {
883 xm_update_text (instance, widget, val);
884 }
885 else if (class == xmTextFieldWidgetClass)
886 {
887 xm_update_text_field (instance, widget, val);
888 }
889 else if (class == xmListWidgetClass)
890 {
891 xm_update_list (instance, widget, val);
892 }
893 }
894
895 /* getting the value back */
896 void
xm_update_one_value(widget_instance * instance,Widget widget,widget_value * val)897 xm_update_one_value (widget_instance* instance,
898 Widget widget,
899 widget_value* val)
900 {
901 WidgetClass class = XtClass (widget);
902 widget_value *old_wv;
903
904 /* copy the call_data slot into the "return" widget_value */
905 for (old_wv = instance->info->val->contents; old_wv; old_wv = old_wv->next)
906 if (!strcmp (val->name, old_wv->name))
907 {
908 val->call_data = old_wv->call_data;
909 break;
910 }
911
912 if (class == xmToggleButtonWidgetClass || class == xmToggleButtonGadgetClass)
913 {
914 XtVaGetValues (widget, XmNset, &val->selected, NULL);
915 val->edited = True;
916 }
917 else if (class == xmTextWidgetClass)
918 {
919 xfree (val->value);
920 val->value = XmTextGetString (widget);
921 val->edited = True;
922 }
923 else if (class == xmTextFieldWidgetClass)
924 {
925 xfree (val->value);
926 val->value = XmTextFieldGetString (widget);
927 val->edited = True;
928 }
929 else if (class == xmRowColumnWidgetClass)
930 {
931 Boolean radiobox = 0;
932 int ac = 0;
933 Arg al [1];
934
935 XtSetArg (al [ac], XmNradioBehavior, &radiobox); ac++;
936 XtGetValues (widget, al, ac);
937
938 if (radiobox)
939 {
940 CompositeWidget radio = (CompositeWidget)widget;
941 int i;
942 for (i = 0; i < radio->composite.num_children; i++)
943 {
944 int set = False;
945 Widget toggle = radio->composite.children [i];
946
947 XtVaGetValues (toggle, XmNset, &set, NULL);
948 if (set)
949 dupstring (&val->value, XtName (toggle));
950 }
951 val->edited = True;
952 }
953 }
954 else if (class == xmListWidgetClass)
955 {
956 int pos_cnt;
957 int* pos_list;
958 if (XmListGetSelectedPos (widget, &pos_list, &pos_cnt))
959 {
960 int i;
961 widget_value* cur;
962 for (cur = val->contents, i = 0; cur; cur = cur->next)
963 if (cur->value)
964 {
965 int j;
966 cur->selected = False;
967 i += 1;
968 for (j = 0; j < pos_cnt; j++)
969 if (pos_list [j] == i)
970 {
971 cur->selected = True;
972 val->value = xstrdup (cur->name);
973 }
974 }
975 val->edited = 1;
976 XtFree ((char *) pos_list);
977 }
978 }
979 }
980
981
982 /* This function is for activating a button from a program. It's wrong because
983 we pass a NULL argument in the call_data which is not Motif compatible.
984 This is used from the XmNdefaultAction callback of the List widgets to
985 have a double-click put down a dialog box like the button would do.
986 I could not find a way to do that with accelerators.
987 */
988 static void
activate_button(Widget widget,XtPointer closure,XtPointer call_data)989 activate_button (Widget widget,
990 XtPointer closure,
991 XtPointer call_data)
992 {
993 Widget button = (Widget)closure;
994 XtCallCallbacks (button, XmNactivateCallback, NULL);
995 }
996
997 /* creation functions */
998
999 /* Called for key press in dialogs. Used to pop down dialog on ESC. */
1000 static void
dialog_key_cb(Widget widget,XtPointer closure,XEvent * event,Boolean * continue_to_dispatch)1001 dialog_key_cb (Widget widget,
1002 XtPointer closure,
1003 XEvent *event,
1004 Boolean *continue_to_dispatch)
1005 {
1006 KeySym sym = 0;
1007 Modifiers modif_ret;
1008
1009 XtTranslateKeycode (event->xkey.display, event->xkey.keycode, 0,
1010 &modif_ret, &sym);
1011
1012 if (sym == osfXK_Cancel)
1013 {
1014 Widget w = *((Widget *) closure);
1015
1016 while (w && ! XtIsShell (w))
1017 w = XtParent (w);
1018
1019 if (XtIsShell (w)) XtPopdown (w);
1020 }
1021
1022 *continue_to_dispatch = TRUE;
1023 }
1024
1025 /* dialogs */
1026 static Widget
make_dialog(char * name,Widget parent,Boolean pop_up_p,char * shell_title,char * icon_name,Boolean text_input_slot,Boolean radio_box,Boolean list,int left_buttons,int right_buttons)1027 make_dialog (char* name,
1028 Widget parent,
1029 Boolean pop_up_p,
1030 char* shell_title,
1031 char* icon_name,
1032 Boolean text_input_slot,
1033 Boolean radio_box,
1034 Boolean list,
1035 int left_buttons,
1036 int right_buttons)
1037 {
1038 Widget result;
1039 Widget form;
1040 Widget row;
1041 Widget icon;
1042 Widget icon_separator;
1043 Widget message_label;
1044 Widget value = 0;
1045 Widget separator;
1046 Widget button = 0;
1047 Widget children [16]; /* for the final XtManageChildren */
1048 int n_children;
1049 Arg al[64]; /* Arg List */
1050 int ac; /* Arg Count */
1051 int i;
1052
1053 if (pop_up_p)
1054 {
1055 ac = 0;
1056 XtSetArg(al[ac], XmNtitle, shell_title); ac++;
1057 XtSetArg(al[ac], XtNallowShellResize, True); ac++;
1058 XtSetArg(al[ac], XmNdeleteResponse, XmUNMAP); ac++;
1059 result = XmCreateDialogShell (parent, "dialog", al, ac);
1060 ac = 0;
1061 XtSetArg(al[ac], XmNautoUnmanage, FALSE); ac++;
1062 /* XtSetArg(al[ac], XmNautoUnmanage, TRUE); ac++; */ /* ####is this ok? */
1063 XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1064 form = XmCreateForm (result, shell_title, al, ac);
1065 }
1066 else
1067 {
1068 ac = 0;
1069 XtSetArg(al[ac], XmNautoUnmanage, FALSE); ac++;
1070 XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1071 form = XmCreateForm (parent, shell_title, al, ac);
1072 result = form;
1073 }
1074
1075 n_children = left_buttons + right_buttons + 1;
1076 ac = 0;
1077 XtSetArg(al[ac], XmNpacking, n_children == 3?
1078 XmPACK_COLUMN: XmPACK_TIGHT); ac++;
1079 XtSetArg(al[ac], XmNorientation, n_children == 3?
1080 XmVERTICAL: XmHORIZONTAL); ac++;
1081 XtSetArg(al[ac], XmNnumColumns, left_buttons + right_buttons + 1); ac++;
1082 XtSetArg(al[ac], XmNmarginWidth, 0); ac++;
1083 XtSetArg(al[ac], XmNmarginHeight, 0); ac++;
1084 XtSetArg(al[ac], XmNspacing, 13); ac++;
1085 XtSetArg(al[ac], XmNadjustLast, False); ac++;
1086 XtSetArg(al[ac], XmNalignment, XmALIGNMENT_CENTER); ac++;
1087 XtSetArg(al[ac], XmNisAligned, True); ac++;
1088 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++;
1089 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_FORM); ac++;
1090 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1091 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM); ac++;
1092 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1093 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1094 XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1095 row = XmCreateRowColumn (form, "row", al, ac);
1096
1097 n_children = 0;
1098 for (i = 0; i < left_buttons; i++)
1099 {
1100 char button_name [16];
1101 sprintf (button_name, "button%d", i + 1);
1102 ac = 0;
1103 if (i == 0)
1104 {
1105 XtSetArg(al[ac], XmNhighlightThickness, 1); ac++;
1106 XtSetArg(al[ac], XmNshowAsDefault, TRUE); ac++;
1107 }
1108 XtSetArg(al[ac], XmNmarginWidth, 10); ac++;
1109 XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1110 children [n_children] = XmCreatePushButton (row, button_name, al, ac);
1111 XtAddEventHandler (children [n_children],
1112 KeyPressMask, False, dialog_key_cb, result);
1113
1114 if (i == 0)
1115 {
1116 button = children [n_children];
1117 ac = 0;
1118 XtSetArg(al[ac], XmNdefaultButton, button); ac++;
1119 XtSetValues (row, al, ac);
1120 }
1121
1122 n_children++;
1123 }
1124
1125 /* invisible separator button */
1126 ac = 0;
1127 XtSetArg (al[ac], XmNmappedWhenManaged, FALSE); ac++;
1128 children [n_children] = XmCreateLabel (row, "separator_button", al, ac);
1129 n_children++;
1130
1131 for (i = 0; i < right_buttons; i++)
1132 {
1133 char button_name [16];
1134 sprintf (button_name, "button%d", left_buttons + i + 1);
1135 ac = 0;
1136 XtSetArg(al[ac], XmNmarginWidth, 10); ac++;
1137 XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++;
1138 children [n_children] = XmCreatePushButton (row, button_name, al, ac);
1139 XtAddEventHandler (children [n_children],
1140 KeyPressMask, False, dialog_key_cb, result);
1141
1142 if (! button) button = children [n_children];
1143 n_children++;
1144 }
1145
1146 XtManageChildren (children, n_children);
1147
1148 ac = 0;
1149 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++;
1150 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1151 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1152 XtSetArg(al[ac], XmNbottomWidget, row); ac++;
1153 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM); ac++;
1154 XtSetArg(al[ac], XmNleftOffset, 0); ac++;
1155 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1156 XtSetArg(al[ac], XmNrightOffset, 0); ac++;
1157 separator = XmCreateSeparator (form, "", al, ac);
1158
1159 ac = 0;
1160 XtSetArg(al[ac], XmNlabelType, XmPIXMAP); ac++;
1161 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_FORM); ac++;
1162 XtSetArg(al[ac], XmNtopOffset, 13); ac++;
1163 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_NONE); ac++;
1164 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM); ac++;
1165 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1166 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_NONE); ac++;
1167 icon = XmCreateLabel (form, icon_name, al, ac);
1168
1169 ac = 0;
1170 XtSetArg(al[ac], XmNmappedWhenManaged, FALSE); ac++;
1171 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++;
1172 XtSetArg(al[ac], XmNtopOffset, 6); ac++;
1173 XtSetArg(al[ac], XmNtopWidget, icon); ac++;
1174 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1175 XtSetArg(al[ac], XmNbottomOffset, 6); ac++;
1176 XtSetArg(al[ac], XmNbottomWidget, separator); ac++;
1177 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_NONE); ac++;
1178 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_NONE); ac++;
1179 icon_separator = XmCreateLabel (form, "", al, ac);
1180
1181 if (text_input_slot)
1182 {
1183 ac = 0;
1184 XtSetArg(al[ac], XmNcolumns, 50); ac++;
1185 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++;
1186 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1187 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1188 XtSetArg(al[ac], XmNbottomWidget, separator); ac++;
1189 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++;
1190 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1191 XtSetArg(al[ac], XmNleftWidget, icon); ac++;
1192 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1193 XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1194 value = XmCreateTextField (form, "value", al, ac);
1195 }
1196 else if (radio_box)
1197 {
1198 Widget radio_butt;
1199 ac = 0;
1200 XtSetArg(al[ac], XmNmarginWidth, 0); ac++;
1201 XtSetArg(al[ac], XmNmarginHeight, 0); ac++;
1202 XtSetArg(al[ac], XmNspacing, 13); ac++;
1203 XtSetArg(al[ac], XmNalignment, XmALIGNMENT_CENTER); ac++;
1204 XtSetArg(al[ac], XmNorientation, XmHORIZONTAL); ac++;
1205 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1206 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1207 XtSetArg(al[ac], XmNbottomWidget, separator); ac++;
1208 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++;
1209 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1210 XtSetArg(al[ac], XmNleftWidget, icon); ac++;
1211 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1212 XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1213 value = XmCreateRadioBox (form, "radiobutton1", al, ac);
1214 ac = 0;
1215 i = 0;
1216 radio_butt = XmCreateToggleButtonGadget (value, "radio1", al, ac);
1217 children [i++] = radio_butt;
1218 radio_butt = XmCreateToggleButtonGadget (value, "radio2", al, ac);
1219 children [i++] = radio_butt;
1220 radio_butt = XmCreateToggleButtonGadget (value, "radio3", al, ac);
1221 children [i++] = radio_butt;
1222 XtManageChildren (children, i);
1223 }
1224 else if (list)
1225 {
1226 ac = 0;
1227 XtSetArg(al[ac], XmNvisibleItemCount, 5); ac++;
1228 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++;
1229 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1230 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1231 XtSetArg(al[ac], XmNbottomWidget, separator); ac++;
1232 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++;
1233 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1234 XtSetArg(al[ac], XmNleftWidget, icon); ac++;
1235 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1236 XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1237 value = XmCreateScrolledList (form, "list", al, ac);
1238
1239 /* this is the easiest way I found to have the dble click in the
1240 list activate the default button */
1241 XtAddCallback (value, XmNdefaultActionCallback, activate_button, button);
1242 }
1243
1244 ac = 0;
1245 XtSetArg(al[ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
1246 XtSetArg(al[ac], XmNtopAttachment, XmATTACH_FORM); ac++;
1247 XtSetArg(al[ac], XmNtopOffset, 13); ac++;
1248 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++;
1249 XtSetArg(al[ac], XmNbottomOffset, 13); ac++;
1250 XtSetArg(al[ac], XmNbottomWidget,
1251 text_input_slot || radio_box || list ? value : separator); ac++;
1252 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++;
1253 XtSetArg(al[ac], XmNleftOffset, 13); ac++;
1254 XtSetArg(al[ac], XmNleftWidget, icon); ac++;
1255 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
1256 XtSetArg(al[ac], XmNrightOffset, 13); ac++;
1257 message_label = XmCreateLabel (form, "message", al, ac);
1258
1259 if (list)
1260 XtManageChild (value);
1261
1262 i = 0;
1263 children [i] = row; i++;
1264 children [i] = separator; i++;
1265 if (text_input_slot || radio_box)
1266 {
1267 children [i] = value; i++;
1268 }
1269 children [i] = message_label; i++;
1270 children [i] = icon; i++;
1271 children [i] = icon_separator; i++;
1272 XtManageChildren (children, i);
1273
1274 if (text_input_slot || list)
1275 {
1276 XtInstallAccelerators (value, button);
1277 XtSetKeyboardFocus (result, value);
1278 }
1279 else
1280 {
1281 XtInstallAccelerators (form, button);
1282 XtSetKeyboardFocus (result, button);
1283 }
1284
1285 return result;
1286 }
1287
1288 static destroyed_instance*
find_matching_instance(widget_instance * instance)1289 find_matching_instance (widget_instance* instance)
1290 {
1291 destroyed_instance* cur;
1292 destroyed_instance* prev;
1293 char* type = instance->info->type;
1294 char* name = instance->info->name;
1295
1296 for (prev = NULL, cur = all_destroyed_instances;
1297 cur;
1298 prev = cur, cur = cur->next)
1299 {
1300 if (!strcmp (cur->name, name)
1301 && !strcmp (cur->type, type)
1302 && cur->parent == instance->parent
1303 && cur->pop_up_p == instance->pop_up_p)
1304 {
1305 if (prev)
1306 prev->next = cur->next;
1307 else
1308 all_destroyed_instances = cur->next;
1309 return cur;
1310 }
1311 /* do some cleanup */
1312 else if (!cur->widget)
1313 {
1314 if (prev)
1315 prev->next = cur->next;
1316 else
1317 all_destroyed_instances = cur->next;
1318 free_destroyed_instance (cur);
1319 cur = prev ? prev : all_destroyed_instances;
1320 }
1321 }
1322 return NULL;
1323 }
1324
1325 static void
mark_dead_instance_destroyed(Widget widget,XtPointer closure,XtPointer call_data)1326 mark_dead_instance_destroyed (Widget widget,
1327 XtPointer closure,
1328 XtPointer call_data)
1329 {
1330 destroyed_instance* instance = (destroyed_instance*)closure;
1331 instance->widget = NULL;
1332 }
1333
1334 static void
recenter_widget(Widget widget)1335 recenter_widget (Widget widget)
1336 {
1337 Widget parent = XtParent (widget);
1338 Screen* screen = XtScreen (widget);
1339 Dimension screen_width = WidthOfScreen (screen);
1340 Dimension screen_height = HeightOfScreen (screen);
1341 Dimension parent_width = 0;
1342 Dimension parent_height = 0;
1343 Dimension child_width = 0;
1344 Dimension child_height = 0;
1345 Position x;
1346 Position y;
1347
1348 XtVaGetValues (widget, XtNwidth, &child_width, XtNheight, &child_height, NULL);
1349 XtVaGetValues (parent, XtNwidth, &parent_width, XtNheight, &parent_height,
1350 NULL);
1351
1352 x = (((Position)parent_width) - ((Position)child_width)) / 2;
1353 y = (((Position)parent_height) - ((Position)child_height)) / 2;
1354
1355 XtTranslateCoords (parent, x, y, &x, &y);
1356
1357 if (x + child_width > screen_width)
1358 x = screen_width - child_width;
1359 if (x < 0)
1360 x = 0;
1361
1362 if (y + child_height > screen_height)
1363 y = screen_height - child_height;
1364 if (y < 0)
1365 y = 0;
1366
1367 XtVaSetValues (widget, XtNx, x, XtNy, y, NULL);
1368 }
1369
1370 static Widget
recycle_instance(destroyed_instance * instance)1371 recycle_instance (destroyed_instance* instance)
1372 {
1373 Widget widget = instance->widget;
1374
1375 /* widget is NULL if the parent was destroyed. */
1376 if (widget)
1377 {
1378 Widget focus;
1379 Widget separator;
1380
1381 /* Remove the destroy callback as the instance is not in the list
1382 anymore */
1383 XtRemoveCallback (instance->parent, XtNdestroyCallback,
1384 mark_dead_instance_destroyed,
1385 (XtPointer)instance);
1386
1387 /* Give the focus to the initial item */
1388 focus = XtNameToWidget (widget, "*value");
1389 if (!focus)
1390 focus = XtNameToWidget (widget, "*button1");
1391 if (focus)
1392 XtSetKeyboardFocus (widget, focus);
1393
1394 /* shrink the separator label back to their original size */
1395 separator = XtNameToWidget (widget, "*separator_button");
1396 if (separator)
1397 XtVaSetValues (separator, XtNwidth, 5, XtNheight, 5, NULL);
1398
1399 /* Center the dialog in its parent */
1400 recenter_widget (widget);
1401 }
1402 free_destroyed_instance (instance);
1403 return widget;
1404 }
1405
1406 Widget
xm_create_dialog(widget_instance * instance)1407 xm_create_dialog (widget_instance* instance)
1408 {
1409 char* name = instance->info->type;
1410 Widget parent = instance->parent;
1411 Widget widget;
1412 Boolean pop_up_p = instance->pop_up_p;
1413 char* shell_name = 0;
1414 char* icon_name = 0;
1415 Boolean text_input_slot = False;
1416 Boolean radio_box = False;
1417 Boolean list = False;
1418 int total_buttons;
1419 int left_buttons = 0;
1420 int right_buttons = 1;
1421 destroyed_instance* dead_one;
1422
1423 /* try to find a widget to recycle */
1424 dead_one = find_matching_instance (instance);
1425 if (dead_one)
1426 {
1427 Widget recycled_widget = recycle_instance (dead_one);
1428 if (recycled_widget)
1429 return recycled_widget;
1430 }
1431
1432 switch (name [0]){
1433 case 'E': case 'e':
1434 icon_name = "dbox-error";
1435 shell_name = "Error";
1436 break;
1437
1438 case 'I': case 'i':
1439 icon_name = "dbox-info";
1440 shell_name = "Information";
1441 break;
1442
1443 case 'L': case 'l':
1444 list = True;
1445 icon_name = "dbox-question";
1446 shell_name = "Prompt";
1447 break;
1448
1449 case 'P': case 'p':
1450 text_input_slot = True;
1451 icon_name = "dbox-question";
1452 shell_name = "Prompt";
1453 break;
1454
1455 case 'Q': case 'q':
1456 icon_name = "dbox-question";
1457 shell_name = "Question";
1458 break;
1459 }
1460
1461 total_buttons = name [1] - '0';
1462
1463 if (name [3] == 'T' || name [3] == 't')
1464 {
1465 text_input_slot = False;
1466 radio_box = True;
1467 }
1468 else if (name [3])
1469 right_buttons = name [4] - '0';
1470
1471 left_buttons = total_buttons - right_buttons;
1472
1473 widget = make_dialog (name, parent, pop_up_p,
1474 shell_name, icon_name, text_input_slot, radio_box,
1475 list, left_buttons, right_buttons);
1476
1477 XtAddCallback (widget, XmNpopdownCallback, xm_nosel_callback,
1478 (XtPointer) instance);
1479
1480 return widget;
1481 }
1482
1483 /* Create a menu bar. We turn off the f10 key
1484 because we have not yet managed to make it work right in Motif. */
1485
1486 static Widget
make_menubar(widget_instance * instance)1487 make_menubar (widget_instance* instance)
1488 {
1489 Arg al[3];
1490 int ac;
1491
1492 ac = 0;
1493 XtSetArg(al[ac], XmNmenuAccelerator, 0); ++ac;
1494 return XmCreateMenuBar (instance->parent, instance->info->name, al, ac);
1495 }
1496
1497 static void
remove_grabs(Widget shell,XtPointer closure,XtPointer call_data)1498 remove_grabs (Widget shell,
1499 XtPointer closure,
1500 XtPointer call_data)
1501 {
1502 Widget menu = (Widget) closure;
1503 XmRemoveFromPostFromList (menu, XtParent (XtParent (menu)));
1504 }
1505
1506 static Widget
make_popup_menu(widget_instance * instance)1507 make_popup_menu (widget_instance* instance)
1508 {
1509 Widget parent = instance->parent;
1510 Window parent_window = parent->core.window;
1511 Widget result;
1512
1513 /* sets the parent window to 0 to fool Motif into not generating a grab */
1514 parent->core.window = 0;
1515 result = XmCreatePopupMenu (parent, instance->info->name, NULL, 0);
1516 XtAddCallback (XtParent (result), XmNpopdownCallback, remove_grabs,
1517 (XtPointer)result);
1518 parent->core.window = parent_window;
1519 return result;
1520 }
1521
1522 static Widget
make_main(widget_instance * instance)1523 make_main (widget_instance* instance)
1524 {
1525 Widget parent = instance->parent;
1526 Widget result;
1527 Arg al[2];
1528 int ac;
1529
1530 ac = 0;
1531 XtSetArg (al[ac], XtNborderWidth, 0); ac++;
1532 XtSetArg (al[ac], XmNspacing, 0); ac++;
1533 result = XmCreateMainWindow (parent, instance->info->name, al, ac);
1534 return result;
1535 }
1536
1537 /* Table of functions to create widgets */
1538
1539 #ifdef ENERGIZE
1540
1541 /* interface with the XDesigner generated functions */
1542 typedef Widget (*widget_maker) (Widget);
1543 extern Widget create_project_p_sheet (Widget parent);
1544 extern Widget create_debugger_p_sheet (Widget parent);
1545 extern Widget create_breaklist_p_sheet (Widget parent);
1546 extern Widget create_le_browser_p_sheet (Widget parent);
1547 extern Widget create_class_browser_p_sheet (Widget parent);
1548 extern Widget create_call_browser_p_sheet (Widget parent);
1549 extern Widget create_build_dialog (Widget parent);
1550 extern Widget create_editmode_dialog (Widget parent);
1551 extern Widget create_search_dialog (Widget parent);
1552 extern Widget create_project_display_dialog (Widget parent);
1553
1554 static Widget
make_one(widget_instance * instance,widget_maker fn)1555 make_one (widget_instance* instance, widget_maker fn)
1556 {
1557 Widget result;
1558 Arg al [64];
1559 int ac = 0;
1560
1561 if (instance->pop_up_p)
1562 {
1563 XtSetArg (al [ac], XmNallowShellResize, TRUE); ac++;
1564 result = XmCreateDialogShell (instance->parent, "dialog", NULL, 0);
1565 XtAddCallback (result, XmNpopdownCallback, &xm_nosel_callback,
1566 (XtPointer) instance);
1567 (*fn) (result);
1568 }
1569 else
1570 {
1571 result = (*fn) (instance->parent);
1572 XtRealizeWidget (result);
1573 }
1574 return result;
1575 }
1576
1577 static Widget
make_project_p_sheet(widget_instance * instance)1578 make_project_p_sheet (widget_instance* instance)
1579 {
1580 return make_one (instance, create_project_p_sheet);
1581 }
1582
1583 static Widget
make_debugger_p_sheet(widget_instance * instance)1584 make_debugger_p_sheet (widget_instance* instance)
1585 {
1586 return make_one (instance, create_debugger_p_sheet);
1587 }
1588
1589 static Widget
make_breaklist_p_sheet(widget_instance * instance)1590 make_breaklist_p_sheet (widget_instance* instance)
1591 {
1592 return make_one (instance, create_breaklist_p_sheet);
1593 }
1594
1595 static Widget
make_le_browser_p_sheet(widget_instance * instance)1596 make_le_browser_p_sheet (widget_instance* instance)
1597 {
1598 return make_one (instance, create_le_browser_p_sheet);
1599 }
1600
1601 static Widget
make_class_browser_p_sheet(widget_instance * instance)1602 make_class_browser_p_sheet (widget_instance* instance)
1603 {
1604 return make_one (instance, create_class_browser_p_sheet);
1605 }
1606
1607 static Widget
make_call_browser_p_sheet(widget_instance * instance)1608 make_call_browser_p_sheet (widget_instance* instance)
1609 {
1610 return make_one (instance, create_call_browser_p_sheet);
1611 }
1612
1613 static Widget
make_build_dialog(widget_instance * instance)1614 make_build_dialog (widget_instance* instance)
1615 {
1616 return make_one (instance, create_build_dialog);
1617 }
1618
1619 static Widget
make_editmode_dialog(widget_instance * instance)1620 make_editmode_dialog (widget_instance* instance)
1621 {
1622 return make_one (instance, create_editmode_dialog);
1623 }
1624
1625 static Widget
make_search_dialog(widget_instance * instance)1626 make_search_dialog (widget_instance* instance)
1627 {
1628 return make_one (instance, create_search_dialog);
1629 }
1630
1631 static Widget
make_project_display_dialog(widget_instance * instance)1632 make_project_display_dialog (widget_instance* instance)
1633 {
1634 return make_one (instance, create_project_display_dialog);
1635 }
1636
1637 #endif /* ENERGIZE */
1638
1639 widget_creation_entry
1640 xm_creation_table [] =
1641 {
1642 {"menubar", make_menubar},
1643 {"popup", make_popup_menu},
1644 {"main", make_main},
1645 #ifdef ENERGIZE
1646 {"project_p_sheet", make_project_p_sheet},
1647 {"debugger_p_sheet", make_debugger_p_sheet},
1648 {"breaklist_psheet", make_breaklist_p_sheet},
1649 {"leb_psheet", make_le_browser_p_sheet},
1650 {"class_browser_psheet", make_class_browser_p_sheet},
1651 {"ctree_browser_psheet", make_call_browser_p_sheet},
1652 {"build", make_build_dialog},
1653 {"editmode", make_editmode_dialog},
1654 {"search", make_search_dialog},
1655 {"project_display", make_project_display_dialog},
1656 #endif /* ENERGIZE */
1657 {NULL, NULL}
1658 };
1659
1660 /* Destruction of instances */
1661 void
xm_destroy_instance(widget_instance * instance)1662 xm_destroy_instance ( widget_instance* instance)
1663 {
1664 Widget widget = instance->widget;
1665 /* recycle the dialog boxes */
1666 /* Disable the recycling until we can find a way to have the dialog box
1667 get reasonable layout after we modify its contents. */
1668 if (0
1669 && XtClass (widget) == xmDialogShellWidgetClass)
1670 {
1671 destroyed_instance* dead_instance =
1672 make_destroyed_instance (instance->info->name,
1673 instance->info->type,
1674 instance->widget,
1675 instance->parent,
1676 instance->pop_up_p);
1677 dead_instance->next = all_destroyed_instances;
1678 all_destroyed_instances = dead_instance;
1679 XtUnmanageChild (first_child (instance->widget));
1680 XFlush (XtDisplay (instance->widget));
1681 XtAddCallback (instance->parent, XtNdestroyCallback,
1682 mark_dead_instance_destroyed, (XtPointer)dead_instance);
1683 }
1684 else
1685 {
1686 /* This might not be necessary now that the nosel is attached to
1687 popdown instead of destroy, but it can't hurt. */
1688 XtRemoveCallback (instance->widget, XtNdestroyCallback,
1689 xm_nosel_callback, (XtPointer)instance);
1690 XtDestroyWidget (instance->widget);
1691 }
1692 }
1693
1694 /* popup utility */
1695 void
xm_popup_menu(Widget widget,XEvent * event)1696 xm_popup_menu (Widget widget, XEvent *event)
1697 {
1698 XButtonPressedEvent dummy;
1699
1700 if (event == 0)
1701 {
1702 dummy.type = ButtonPress;
1703 dummy.serial = 0;
1704 dummy.send_event = 0;
1705 dummy.display = XtDisplay (widget);
1706 dummy.window = XtWindow (XtParent (widget));
1707 dummy.time = 0;
1708 dummy.button = 0;
1709 XQueryPointer (dummy.display, dummy.window, &dummy.root,
1710 &dummy.subwindow, &dummy.x_root, &dummy.y_root,
1711 &dummy.x, &dummy.y, &dummy.state);
1712 event = (XEvent *) &dummy;
1713 }
1714
1715 if (event->type == ButtonPress || event->type == ButtonRelease)
1716 {
1717 /* Setting the menuPost resource only required by Motif 1.1 and
1718 LessTif 0.84 and earlier. With later versions of LessTif,
1719 setting menuPost is unnecessary and may cause problems, so
1720 don't do it. */
1721 #if XmVersion < 1002 || (defined LESSTIF_VERSION && LESSTIF_VERSION < 84)
1722 {
1723 /* This is so totally ridiculous: there's NO WAY to tell Motif
1724 that *any* button can select a menu item. Only one button
1725 can have that honor. */
1726
1727 char *trans = 0;
1728 if (event->xbutton.state & Button5Mask) trans = "<Btn5Down>";
1729 else if (event->xbutton.state & Button4Mask) trans = "<Btn4Down>";
1730 else if (event->xbutton.state & Button3Mask) trans = "<Btn3Down>";
1731 else if (event->xbutton.state & Button2Mask) trans = "<Btn2Down>";
1732 else if (event->xbutton.state & Button1Mask) trans = "<Btn1Down>";
1733 if (trans) XtVaSetValues (widget, XmNmenuPost, trans, NULL);
1734 }
1735 #endif
1736
1737 XmMenuPosition (widget, (XButtonPressedEvent *) event);
1738 }
1739
1740 XtManageChild (widget);
1741 }
1742
1743 static void
set_min_dialog_size(Widget w)1744 set_min_dialog_size (Widget w)
1745 {
1746 short width;
1747 short height;
1748 XtVaGetValues (w, XmNwidth, &width, XmNheight, &height, NULL);
1749 XtVaSetValues (w, XmNminWidth, width, XmNminHeight, height, NULL);
1750 }
1751
1752 void
xm_pop_instance(widget_instance * instance,Boolean up)1753 xm_pop_instance (widget_instance* instance, Boolean up)
1754 {
1755 Widget widget = instance->widget;
1756
1757 if (XtClass (widget) == xmDialogShellWidgetClass)
1758 {
1759 Widget widget_to_manage = first_child (widget);
1760 if (up)
1761 {
1762 XtManageChild (widget_to_manage);
1763 set_min_dialog_size (widget);
1764 XtSetKeyboardFocus (instance->parent, widget);
1765 }
1766 else
1767 XtUnmanageChild (widget_to_manage);
1768 }
1769 else
1770 {
1771 if (up)
1772 XtManageChild (widget);
1773 else
1774 XtUnmanageChild (widget);
1775 }
1776 }
1777
1778
1779 /* motif callback */
1780
1781 static void
do_call(Widget widget,XtPointer closure,enum do_call_type type)1782 do_call (Widget widget,
1783 XtPointer closure,
1784 enum do_call_type type)
1785 {
1786 Arg al [256];
1787 int ac;
1788 XtPointer user_data;
1789 widget_instance* instance = (widget_instance*)closure;
1790 Widget instance_widget;
1791 LWLIB_ID id;
1792
1793 if (!instance)
1794 return;
1795 if (widget->core.being_destroyed)
1796 return;
1797
1798 instance_widget = instance->widget;
1799 if (!instance_widget)
1800 return;
1801
1802 id = instance->info->id;
1803 ac = 0;
1804 user_data = NULL;
1805 XtSetArg (al [ac], XmNuserData, &user_data); ac++;
1806 XtGetValues (widget, al, ac);
1807
1808 switch (type)
1809 {
1810 case pre_activate:
1811 if (instance->info->pre_activate_cb)
1812 instance->info->pre_activate_cb (widget, id, user_data);
1813 break;
1814
1815 case selection:
1816 if (instance->info->selection_cb)
1817 instance->info->selection_cb (widget, id, user_data);
1818 break;
1819
1820 case no_selection:
1821 if (instance->info->selection_cb)
1822 instance->info->selection_cb (widget, id, (XtPointer) -1);
1823 break;
1824
1825 case post_activate:
1826 if (instance->info->post_activate_cb)
1827 instance->info->post_activate_cb (widget, id, user_data);
1828 break;
1829
1830 default:
1831 emacs_abort ();
1832 }
1833 }
1834
1835 /* Like lw_internal_update_other_instances except that it does not do
1836 anything if its shell parent is not managed. This is to protect
1837 lw_internal_update_other_instances to dereference freed memory
1838 if the widget was ``destroyed'' by caching it in the all_destroyed_instances
1839 list */
1840 static void
xm_internal_update_other_instances(Widget widget,XtPointer closure,XtPointer call_data)1841 xm_internal_update_other_instances (Widget widget,
1842 XtPointer closure,
1843 XtPointer call_data)
1844 {
1845 Widget parent;
1846 for (parent = widget; parent; parent = XtParent (parent))
1847 if (XtIsShell (parent))
1848 break;
1849 else if (!XtIsManaged (parent))
1850 return;
1851 lw_internal_update_other_instances (widget, closure, call_data);
1852 }
1853
1854 static void
xm_generic_callback(Widget widget,XtPointer closure,XtPointer call_data)1855 xm_generic_callback (Widget widget,
1856 XtPointer closure,
1857 XtPointer call_data)
1858 {
1859 lw_internal_update_other_instances (widget, closure, call_data);
1860 do_call (widget, closure, selection);
1861 }
1862
1863 static void
xm_nosel_callback(Widget widget,XtPointer closure,XtPointer call_data)1864 xm_nosel_callback (Widget widget,
1865 XtPointer closure,
1866 XtPointer call_data)
1867 {
1868 /* This callback is only called when a dialog box is dismissed with
1869 the wm's destroy button (WM_DELETE_WINDOW.) We want the dialog
1870 box to be destroyed in that case, not just unmapped, so that it
1871 releases its keyboard grabs. But there are problems with running
1872 our callbacks while the widget is in the process of being
1873 destroyed, so we set XmNdeleteResponse to XmUNMAP instead of
1874 XmDESTROY and then destroy it ourself after having run the
1875 callback. */
1876 do_call (widget, closure, no_selection);
1877 XtDestroyWidget (widget);
1878 }
1879
1880 static void
xm_pull_down_callback(Widget widget,XtPointer closure,XtPointer call_data)1881 xm_pull_down_callback (Widget widget,
1882 XtPointer closure,
1883 XtPointer call_data)
1884 {
1885 Widget parent = XtParent (widget);
1886
1887 if (XmIsRowColumn (parent))
1888 {
1889 unsigned char type = 0xff;
1890 XtVaGetValues (parent, XmNrowColumnType, &type, NULL);
1891 if (type == XmMENU_BAR)
1892 do_call (widget, closure, pre_activate);
1893 }
1894 }
1895
1896
1897 /* XmNpopdownCallback for MenuShell widgets. WIDGET is the MenuShell,
1898 CLOSURE is a pointer to the widget_instance of the shell,
1899
1900 Note that this callback is called for each cascade button in a
1901 menu, whether or not its submenu is visible. */
1902
1903 static void
xm_pop_down_callback(Widget widget,XtPointer closure,XtPointer call_data)1904 xm_pop_down_callback (Widget widget,
1905 XtPointer closure,
1906 XtPointer call_data)
1907 {
1908 widget_instance *instance = (widget_instance *) closure;
1909
1910 if ((!instance->pop_up_p && XtParent (widget) == instance->widget)
1911 || XtParent (widget) == instance->parent)
1912 do_call (widget, closure, post_activate);
1913 }
1914
1915
1916 /* set the keyboard focus */
1917 void
xm_set_keyboard_focus(Widget parent,Widget w)1918 xm_set_keyboard_focus (Widget parent, Widget w)
1919 {
1920 XmProcessTraversal (w, 0);
1921 XtSetKeyboardFocus (parent, w);
1922 }
1923
1924 /* Motif hack to set the main window areas. */
1925 void
xm_set_main_areas(Widget parent,Widget menubar,Widget work_area)1926 xm_set_main_areas (Widget parent,
1927 Widget menubar,
1928 Widget work_area)
1929 {
1930 XmMainWindowSetAreas (parent,
1931 menubar, /* menubar (maybe 0) */
1932 0, /* command area (psheets) */
1933 0, /* horizontal scroll */
1934 0, /* vertical scroll */
1935 work_area); /* work area */
1936 }
1937
1938 /* Motif hack to control resizing on the menubar. */
1939 void
xm_manage_resizing(Widget w,Boolean flag)1940 xm_manage_resizing (Widget w, Boolean flag)
1941 {
1942 XtVaSetValues (w, XtNallowShellResize, flag, NULL);
1943 }
1944