Merge remote-tracking branch 'origin/master' into for-steve
[sxemacs] / src / ui / lwlib / lwlib-Xlw.c
1 /* The lwlib interface to "xlwmenu" menus.
2    Copyright (C) 1992, 1994 Lucid, Inc.
3
4 This file is part of the Lucid Widget Library.
5
6 The Lucid Widget Library is free software: you can redistribute it
7 and/or modify it under the terms of the GNU General Public License as
8 published by the Free Software Foundation, either version 3 of the
9 License, or (at your option) any later version.
10
11 The Lucid Widget Library is distributed in the hope that it will be
12 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program.  If not, see <http://www.gnu.org/licenses/>. */
18
19 #include <config.h>
20 #include <stdlib.h>             /* for abort () */
21 #include <stdio.h>              /* for abort () */
22 #include <limits.h>
23
24 #include "lwlib-Xlw.h"
25 #include "lwlib-utils.h"
26 #include <X11/StringDefs.h>
27 #include <X11/IntrinsicP.h>
28 #include <X11/ObjectP.h>
29 #include <X11/CompositeP.h>
30 #include <X11/Shell.h>
31 #ifdef HAVE_WIDGETS
32 #include "ui/X11/EmacsManager.h"
33 #endif
34 #ifdef LWLIB_MENUBARS_LUCID
35 #include "xlwmenu.h"
36 #endif
37 #ifdef LWLIB_SCROLLBARS_LUCID
38 #include "xlwscrollbar.h"
39 #endif
40 #ifdef LWLIB_TABS_LUCID
41 #ifdef NEED_MOTIF
42 #include "lwlib-Xm.h"
43 #endif
44 #ifdef NEED_ATHENA
45 #include "lwlib-Xaw.h"
46 #endif
47 #include "ui/X11/xmu.h"
48 #include "xlwtabs.h"
49 #endif
50 \f
51 #include <X11/Intrinsic.h>
52 #include <X11/StringDefs.h>
53 #include <X11/Shell.h>
54 #include <X11/Xaw/Form.h>
55 #include <X11/Xaw/Command.h>
56
57 #ifdef LWLIB_MENUBARS_LUCID
58
59 /* Menu callbacks */
60
61 static void pre_hook(Widget w, XtPointer client_data, XtPointer call_data)
62 {
63         widget_instance *instance = (widget_instance *) client_data;
64         widget_value *val;
65
66         if (w->core.being_destroyed)
67                 return;
68
69         val = lw_get_widget_value_for_widget(instance, w);
70 #if 0
71         /* #### - this code used to (for some random back_asswards reason) pass
72            the expression below in the call_data slot.  For incremental menu
73            construction, this needs to go.  I can't even figure out why it was done
74            this way in the first place...it's just a historical weirdism. --Stig */
75         call_data = (val ? val->call_data : NULL);
76 #endif
77         if (val && val->call_data)
78                 abort();        /* #### - the call_data for the top_level
79                                    "menubar" widget_value used to be passed
80                                    back to the pre_hook. */
81
82         if (instance->info->pre_activate_cb)
83                 instance->info->pre_activate_cb(w, instance->info->id,
84                                                 call_data);
85 }
86
87 static void pick_hook(Widget w, XtPointer client_data, XtPointer call_data)
88 {
89         widget_instance *instance = (widget_instance *) client_data;
90         widget_value *contents_val = (widget_value *) call_data;
91         widget_value *widget_val;
92         XtPointer widget_arg;
93         LWLIB_ID id;
94         lw_callback post_activate_cb;
95
96         if (w->core.being_destroyed)
97                 return;
98
99         /* Grab these values before running any functions, in case running
100            the selection_cb causes the widget to be destroyed. */
101         id = instance->info->id;
102         post_activate_cb = instance->info->post_activate_cb;
103
104         widget_val = lw_get_widget_value_for_widget(instance, w);
105         widget_arg = widget_val ? widget_val->call_data : NULL;
106
107         if (instance->info->selection_cb &&
108             contents_val && contents_val->enabled && !contents_val->contents)
109                 instance->info->selection_cb(w, id, contents_val->call_data);
110
111         if (post_activate_cb)
112                 post_activate_cb(w, id, widget_arg);
113 }
114 \f
115 /* creation functions */
116 static Widget xlw_create_menubar(widget_instance * instance)
117 {
118         Arg al[1];
119         Widget widget;
120
121         XtSetArg(al[0], XtNmenu, instance->info->val);
122         widget = XtCreateWidget(instance->info->name, xlwMenuWidgetClass,
123                                 instance->parent, al, 1);
124         XtAddCallback(widget, XtNopen, pre_hook, (XtPointer) instance);
125         XtAddCallback(widget, XtNselect, pick_hook, (XtPointer) instance);
126         return widget;
127 }
128
129 static Widget xlw_create_popup_menu(widget_instance * instance)
130 {
131         Arg al[2];
132         Widget popup_shell, widget;
133
134         popup_shell = XtCreatePopupShell(instance->info->name,
135                                          overrideShellWidgetClass,
136                                          instance->parent, NULL, 0);
137         XtSetArg(al[0], XtNmenu, instance->info->val);
138         XtSetArg(al[1], XtNhorizontal, False);
139         widget = XtCreateManagedWidget("popup", xlwMenuWidgetClass,
140                                        popup_shell, al, 2);
141         XtAddCallback(widget, XtNselect, pick_hook, (XtPointer) instance);
142         return popup_shell;
143 }
144 #endif                          /* LWLIB_MENUBARS_LUCID */
145
146 #ifdef LWLIB_SCROLLBARS_LUCID
147 static void
148 xlw_scrollbar_callback(Widget widget, XtPointer closure, XtPointer call_data)
149 {
150         widget_instance *instance = (widget_instance *) closure;
151         LWLIB_ID id;
152         XlwScrollBarCallbackStruct *data =
153             (XlwScrollBarCallbackStruct *) call_data;
154         scroll_event event_data;
155         scrollbar_values *val;
156         double percent;
157
158         if (!instance || widget->core.being_destroyed)
159                 return;
160
161         val = (scrollbar_values *) instance->info->val->scrollbar_data;
162
163         id = instance->info->id;
164
165         percent = (double)(data->value - 1) / (double)(INT_MAX - 1);
166         event_data.slider_value =
167             (int)(percent * (double)(val->maximum - val->minimum)) +
168             val->minimum;
169
170         if (event_data.slider_value > val->maximum - val->slider_size)
171                 event_data.slider_value = val->maximum - val->slider_size;
172         else if (event_data.slider_value < val->minimum)
173                 event_data.slider_value = val->minimum;
174
175         if (data->event) {
176                 switch (data->event->type) {
177                 case KeyPress:
178                 case KeyRelease:
179                         event_data.time = data->event->xkey.time;
180                         break;
181                 case ButtonPress:
182                 case ButtonRelease:
183                         event_data.time = data->event->xbutton.time;
184                         break;
185                 case MotionNotify:
186                         event_data.time = data->event->xmotion.time;
187                         break;
188                 case EnterNotify:
189                 case LeaveNotify:
190                         event_data.time = data->event->xcrossing.time;
191                         break;
192                 default:
193                         event_data.time = 0;
194                         break;
195                 }
196         } else
197                 event_data.time = 0;
198
199         switch (data->reason) {
200         case XmCR_DECREMENT:
201                 event_data.action = SCROLLBAR_LINE_UP;
202                 break;
203         case XmCR_INCREMENT:
204                 event_data.action = SCROLLBAR_LINE_DOWN;
205                 break;
206         case XmCR_PAGE_DECREMENT:
207                 event_data.action = SCROLLBAR_PAGE_UP;
208                 break;
209         case XmCR_PAGE_INCREMENT:
210                 event_data.action = SCROLLBAR_PAGE_DOWN;
211                 break;
212         case XmCR_TO_TOP:
213                 event_data.action = SCROLLBAR_TOP;
214                 break;
215         case XmCR_TO_BOTTOM:
216                 event_data.action = SCROLLBAR_BOTTOM;
217                 break;
218         case XmCR_DRAG:
219                 event_data.action = SCROLLBAR_DRAG;
220                 break;
221         case XmCR_VALUE_CHANGED:
222                 event_data.action = SCROLLBAR_CHANGE;
223                 break;
224         default:
225                 event_data.action = SCROLLBAR_CHANGE;
226                 break;
227         }
228
229         if (instance->info->pre_activate_cb)
230                 instance->info->pre_activate_cb(widget, id,
231                                                 (XtPointer) & event_data);
232 }
233
234 #define add_scrollbar_callback(resource) \
235 XtAddCallback (scrollbar, resource, xlw_scrollbar_callback, (XtPointer) instance)
236
237 /* #### Does not yet support horizontal scrollbars. */
238 static Widget xlw_create_scrollbar(widget_instance * instance, int vertical)
239 {
240         Arg al[20];
241         int ac = 0;
242         static XtCallbackRec callbacks[2] =
243             { {xlw_scrollbar_callback, NULL}, {NULL, NULL} };
244
245         callbacks[0].closure = (XtPointer) instance;
246
247         XtSetArg(al[ac], XmNminimum, 1);
248         ac++;
249         XtSetArg(al[ac], XmNmaximum, INT_MAX);
250         ac++;
251         XtSetArg(al[ac], XmNincrement, 1);
252         ac++;
253         XtSetArg(al[ac], XmNpageIncrement, 1);
254         ac++;
255         XtSetArg(al[ac], XmNorientation,
256                  (vertical ? XmVERTICAL : XmHORIZONTAL));
257         ac++;
258
259         XtSetArg(al[ac], XmNdecrementCallback, callbacks);
260         ac++;
261         XtSetArg(al[ac], XmNdragCallback, callbacks);
262         ac++;
263         XtSetArg(al[ac], XmNincrementCallback, callbacks);
264         ac++;
265         XtSetArg(al[ac], XmNpageDecrementCallback, callbacks);
266         ac++;
267         XtSetArg(al[ac], XmNpageIncrementCallback, callbacks);
268         ac++;
269         XtSetArg(al[ac], XmNtoBottomCallback, callbacks);
270         ac++;
271         XtSetArg(al[ac], XmNtoTopCallback, callbacks);
272         ac++;
273         XtSetArg(al[ac], XmNvalueChangedCallback, callbacks);
274         ac++;
275
276         return XtCreateWidget(instance->info->name, xlwScrollBarWidgetClass,
277                               instance->parent, al, ac);
278 }
279
280 static Widget xlw_create_vertical_scrollbar(widget_instance * instance)
281 {
282         return xlw_create_scrollbar(instance, 1);
283 }
284
285 static Widget xlw_create_horizontal_scrollbar(widget_instance * instance)
286 {
287         return xlw_create_scrollbar(instance, 0);
288 }
289
290 static void
291 xlw_update_scrollbar(widget_instance * instance, Widget widget,
292                      widget_value * val)
293 {
294         if (val->scrollbar_data) {
295                 scrollbar_values *data = val->scrollbar_data;
296                 int widget_sliderSize, widget_val;
297                 int new_sliderSize, new_value;
298                 double percent;
299                 Arg al[4];
300
301                 /* First size and position the scrollbar widget. */
302                 XtSetArg(al[0], XtNx, data->scrollbar_x);
303                 XtSetArg(al[1], XtNy, data->scrollbar_y);
304                 XtSetArg(al[2], XtNwidth, data->scrollbar_width);
305                 XtSetArg(al[3], XtNheight, data->scrollbar_height);
306                 XtSetValues(widget, al, 4);
307
308                 /* Now size the scrollbar's slider. */
309                 XtSetArg(al[0], XmNsliderSize, &widget_sliderSize);
310                 XtSetArg(al[1], XmNvalue, &widget_val);
311                 XtGetValues(widget, al, 2);
312
313                 percent = (double)data->slider_size /
314                     (double)(data->maximum - data->minimum);
315                 percent = (percent > 1.0 ? 1.0 : percent);
316                 new_sliderSize = (int)((double)(INT_MAX - 1) * percent);
317
318                 percent = (double)(data->slider_position - data->minimum) /
319                     (double)(data->maximum - data->minimum);
320                 percent = (percent > 1.0 ? 1.0 : percent);
321                 new_value = (int)((double)(INT_MAX - 1) * percent);
322
323                 if (new_sliderSize > INT_MAX - 1)
324                         new_sliderSize = INT_MAX - 1;
325                 else if (new_sliderSize < 1)
326                         new_sliderSize = 1;
327
328                 if (new_value > (INT_MAX - new_sliderSize))
329                         new_value = INT_MAX - new_sliderSize;
330                 else if (new_value < 1)
331                         new_value = 1;
332
333                 if (new_sliderSize != widget_sliderSize
334                     || new_value != widget_val)
335                         XlwScrollBarSetValues(widget, new_value, new_sliderSize,
336                                               1, 1, False);
337         }
338 }
339
340 #endif                          /* LWLIB_SCROLLBARS_LUCID */
341
342 #ifdef LWLIB_TABS_LUCID
343 /* tab control
344
345    lwlib is such an incredible hairy crock. I just cannot believe
346    it! There are random dependencies between functions, there is a
347    total lack of genericity, even though it initially appears to be
348    generic. It should all be junked and begun again. Building tabs are
349    an example - in theory we should be able to reuse a lot of the
350    general stuff because we want to put labels of whatever toolkit we
351    are using in the tab. Instead we have to hack it by hand. */
352 static void
353 xlw_tab_control_callback(Widget w, XtPointer client_data, XtPointer call_data)
354 {
355         /* call data is the topmost widget */
356         widget_instance *instance = (widget_instance *) client_data;
357         Widget top = (Widget) call_data;
358         char *name = XtName(top);
359         widget_value *widget_val;
360         XtPointer widget_arg;
361         LWLIB_ID id;
362         lw_callback post_activate_cb;
363
364         if (w->core.being_destroyed)
365                 return;
366
367         /* Grab these values before running any functions, in case running
368            the selection_cb causes the widget to be destroyed. */
369         id = instance->info->id;
370         post_activate_cb = instance->info->post_activate_cb;
371
372         /* search for the widget_val for the selected tab */
373         for (widget_val = instance->info->val->contents; widget_val;
374              widget_val = widget_val->next) {
375                 if (!strcmp(widget_val->name, name))
376                         break;
377         }
378
379         widget_arg = widget_val ? widget_val->call_data : NULL;
380
381         if (instance->info->selection_cb &&
382             widget_val && widget_val->enabled && !widget_val->contents)
383                 instance->info->selection_cb(w, id, widget_arg);
384
385         if (post_activate_cb)
386                 post_activate_cb(w, id, widget_arg);
387 }
388
389 static Widget xlw_create_tab_control(widget_instance * instance)
390 {
391         Arg al[20];
392         int ac = 0;
393         Widget tab = 0;
394         widget_value *val = instance->info->val;
395
396         XtSetArg(al[ac], XtNsensitive, val->enabled);
397         ac++;
398         XtSetArg(al[ac], XtNmappedWhenManaged, FALSE);
399         ac++;
400         XtSetArg(al[ac], XtNorientation, XtorientHorizontal);
401         ac++;
402         XtSetArg(al[ac], XtNresizable, False);
403         ac++;
404
405         /* add any args the user supplied for creation time */
406         lw_add_value_args_to_args(val, al, &ac);
407
408         tab = XtCreateManagedWidget(val->name, tabsWidgetClass,
409                                     instance->parent, al, ac);
410         XtRemoveAllCallbacks(tab, XtNcallback);
411         XtAddCallback(tab, XtNcallback, xlw_tab_control_callback,
412                       (XtPointer) instance);
413
414         XtManageChild(tab);
415
416         return tab;
417 }
418
419 static void build_tabs_in_widget(widget_instance * instance, Widget widget,
420                                  widget_value * val)
421 {
422         widget_value *cur = val;
423         for (cur = val; cur; cur = cur->next) {
424                 if (cur->value) {
425 #ifdef LWLIB_WIDGETS_MOTIF
426                         xm_create_label(widget, cur);
427 #else
428                         xaw_create_label(widget, cur);
429 #endif
430                 }
431                 cur->change = NO_CHANGE;
432         }
433 }
434
435 static void
436 xlw_update_tab_control(widget_instance * instance, Widget widget,
437                        widget_value * val)
438 {
439         Widget *children;
440         unsigned int num_children;
441         unsigned int i;
442         widget_value *cur = 0;
443
444         XtRemoveAllCallbacks(widget, XtNcallback);
445         XtAddCallback(widget, XtNcallback, xlw_tab_control_callback,
446                       (XtPointer) instance);
447
448         if (val->change == STRUCTURAL_CHANGE
449             || (val->contents && val->contents->change == STRUCTURAL_CHANGE)) {
450                 destroy_all_children(widget);
451                 build_tabs_in_widget(instance, widget, val->contents);
452         }
453
454         children = XtCompositeChildren(widget, &num_children);
455         if (children) {
456                 for (i = 0, cur = val->contents; i < num_children; i++) {
457                         if (cur) {
458                                 if (children[i]->core.being_destroyed
459                                     || strcmp(XtName(children[i]), cur->name))
460                                         continue;
461 #ifdef NEED_MOTIF
462                                 if (lw_motif_widget_p(children[i]))
463                                         xm_update_one_widget(instance, children[i], cur,
464                                                              False);
465 #endif
466 #ifdef NEED_ATHENA
467                                 if (lw_xaw_widget_p(children[i]))
468                                         xaw_update_one_widget(instance, children[i],
469                                                               cur, False);
470 #endif
471                                 cur = cur->next;
472                         } else
473                                 abort();
474                 }
475                 XtFree((char *)children);
476         }
477         if (cur)
478                 abort();
479 }
480 #endif                          /* LWLIB_TABS_LUCID */
481
482 #ifdef HAVE_WIDGETS
483 static Widget xlw_create_clip_window(widget_instance * instance)
484 {
485         Arg al[20];
486         int ac = 0;
487         Widget clip = 0;
488         widget_value *val = instance->info->val;
489
490         XtSetArg(al[ac], XtNmappedWhenManaged, FALSE);
491         ac++;
492         XtSetArg(al[ac], XtNsensitive, TRUE);
493         ac++;
494         /* add any args the user supplied for creation time */
495         lw_add_value_args_to_args(val, al, &ac);
496
497         /* Create a clip window to contain the subwidget. Incredibly the
498            XEmacs manager seems to be the most appropriate widget for
499            this. Nothing else is simple enough and yet does what is
500            required. */
501         clip = XtCreateManagedWidget(val->name,
502                                      emacsManagerWidgetClass,
503                                      instance->parent, al, ac);
504
505         return clip;
506 }
507 #endif
508
509 const widget_creation_entry xlw_creation_table[] = {
510 #ifdef LWLIB_MENUBARS_LUCID
511         {"menubar", xlw_create_menubar},
512         {"popup", xlw_create_popup_menu},
513 #endif
514 #ifdef LWLIB_SCROLLBARS_LUCID
515         {"vertical-scrollbar", xlw_create_vertical_scrollbar},
516         {"horizontal-scrollbar", xlw_create_horizontal_scrollbar},
517 #endif
518 #ifdef LWLIB_TABS_LUCID
519         {"tab-control", xlw_create_tab_control},
520 #endif
521 #ifdef HAVE_WIDGETS
522         {"clip-window", xlw_create_clip_window},
523 #endif
524         {NULL, NULL}
525 };
526
527 Boolean lw_lucid_widget_p(Widget widget)
528 {
529         WidgetClass the_class = XtClass(widget);
530 #ifdef LWLIB_MENUBARS_LUCID
531         if (the_class == xlwMenuWidgetClass)
532                 return True;
533 #endif
534 #ifdef LWLIB_SCROLLBARS_LUCID
535         if (the_class == xlwScrollBarWidgetClass)
536                 return True;
537 #endif
538 #ifdef LWLIB_TABS_LUCID
539         if (the_class == tabsWidgetClass)
540                 return True;
541 #endif
542 #ifdef LWLIB_MENUBARS_LUCID
543         if (the_class == overrideShellWidgetClass)
544                 return
545                     XtClass(((CompositeWidget) widget)->composite.children[0])
546                     == xlwMenuWidgetClass;
547 #endif
548 #ifdef HAVE_WIDGETS
549         if (the_class == emacsManagerWidgetClass)
550                 return True;
551 #endif
552         return False;
553 }
554
555 void
556 xlw_update_one_widget(widget_instance * instance, Widget widget,
557                       widget_value * val, Boolean deep_p)
558 {
559         WidgetClass class = XtClass(widget);
560
561         if (0) ;
562 #ifdef LWLIB_MENUBARS_LUCID
563         else if (class == xlwMenuWidgetClass) {
564                 XlwMenuWidget mw;
565                 Arg al[1];
566                 if (XtIsShell(widget))
567                         mw = (XlwMenuWidget) ((CompositeWidget) widget)->
568                             composite.children[0];
569                 else
570                         mw = (XlwMenuWidget) widget;
571                 XtSetArg(al[0], XtNmenu, val);
572                 XtSetValues(widget, al, 1);     /* #### mw unused! */
573         }
574 #endif
575 #ifdef LWLIB_SCROLLBARS_LUCID
576         else if (class == xlwScrollBarWidgetClass) {
577                 xlw_update_scrollbar(instance, widget, val);
578         }
579 #endif
580 #ifdef LWLIB_TABS_LUCID
581         else if (class == tabsWidgetClass) {
582                 xlw_update_tab_control(instance, widget, val);
583         }
584 #endif
585         /* Lastly update our global arg values. */
586         if (val->args && val->args->nargs)
587                 XtSetValues(widget, val->args->args, val->args->nargs);
588 }
589
590 void
591 xlw_update_one_value(widget_instance * instance, Widget widget,
592                      widget_value * val)
593 {
594         return;
595 }
596
597 void xlw_pop_instance(widget_instance * instance, Boolean up)
598 {
599 }
600
601 #ifdef LWLIB_MENUBARS_LUCID
602 void xlw_popup_menu(Widget widget, XEvent * event)
603 {
604         XlwMenuWidget mw;
605
606         if (!XtIsShell(widget))
607                 return;
608
609         if (event->type == ButtonPress || event->type == ButtonRelease) {
610                 mw = (XlwMenuWidget) ((CompositeWidget) widget)->composite.
611                     children[0];
612                 xlw_pop_up_menu(mw, (XButtonPressedEvent *) event);
613         } else
614                 abort();
615 }
616 #endif                          /* LWLIB_MENUBARS_LUCID */
617 \f
618 /* Destruction of instances */
619 void xlw_destroy_instance(widget_instance * instance)
620 {
621         if (instance->widget)
622                 XtDestroyWidget(instance->widget);
623 }