1 /*
2 * Copyright (c) 2001-2004 the xdvik development team
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to
6 * deal in the Software without restriction, including without limitation the
7 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 * sell copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 * PAUL VOJTA OR ANY OTHER AUTHOR OF THIS SOFTWARE BE LIABLE FOR ANY CLAIM,
18 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20 * OTHER DEALINGS IN THE SOFTWARE.
21 *
22 */
23
24 /*
25 * Menu bar implementation for the Athena widget set.
26 */
27 #include "xdvi-config.h"
28 #include "xdvi.h"
29
30 #include "c-openmx.h"
31 #include "events.h"
32 #include "dvi-draw.h"
33 #include "dvi-init.h"
34 #include "statusline.h"
35 #include "pagesel.h"
36 #include "util.h"
37 #include "x_util.h"
38 #include "xaw_menu.h"
39 #include "message-window.h"
40 #include "my-snprintf.h"
41 #include "filehist.h"
42 #include "menu.h"
43
44 #ifndef MOTIF /* entire file */
45
46 #include <ctype.h>
47
48 #include <X11/Intrinsic.h>
49 #include <X11/Xatom.h>
50 #include <X11/StringDefs.h>
51 #include <X11/Shell.h> /* needed for def. of XtNiconX */
52
53 #include <X11/Xaw/Viewport.h>
54 #include <X11/Xaw/SimpleMenu.h>
55 #include <X11/Xaw/MenuButton.h>
56 #include <X11/Xaw/Sme.h>
57 #include <X11/Xaw/SmeBSB.h>
58 #include <X11/Xaw/SmeLine.h>
59 #include <X11/Xaw/Dialog.h>
60 #include <X11/Xaw/Text.h>
61 #include <X11/Xaw/Panner.h>
62 #include <X11/Xaw/Porthole.h>
63 #include <X11/Xaw/Command.h>
64
65 #ifndef MAX
66 # define MAX(i, j) ( (i) > (j) ? (i) : (j) )
67 #endif
68
69
70 static Widget line_widget, panel_widget;
71
72 /* width of button panel */
73 static int m_panel_width = 0;
74
75 /* used for communication with the pagelist in xaw_create_pagelist */
76 static int m_y_pos;
77
78
79 /* access method for panel width */
80 int
get_panel_width(void)81 get_panel_width(void)
82 {
83 /* int retval = 0; */
84 /* if (resource.expert_mode & XPRT_SHOW_BUTTONS) */
85 /* retval = m_panel_width; */
86 /* TRACE_GUI((stderr, "get_panel_width: %d", retval)); */
87 /* return retval; */
88 return m_panel_width;
89 }
90
91 /*
92 ================================================================================
93 Pixmaps indicating the state of menu buttons (radiobutton/checkbox
94 on/off, cascading menu). Inspired by menu.c, `check_bits' in the xterm source.
95 ================================================================================
96 */
97 #include "xaw_bitmaps.h"
98 static Pixmap menu_check_on_bitmap;
99 static Pixmap menu_check_off_bitmap;
100 static Pixmap menu_radio_on_bitmap;
101 static Pixmap menu_radio_off_bitmap;
102 static Pixmap menu_arrow_bitmap;
103
104 /*
105 ============================================================
106 Hack for pullright menus part I: data
107 ============================================================
108 */
109
110 /* There are a few custom widgets for pullright menus out there, but
111 * these are old and potentially buggy, so just do it manually via an
112 * event handler, similar to Motif tooltips.
113 */
114 static XtIntervalId m_timeout = 0;
115 static Widget m_active_submenu = NULL; /* if not NULL, the currently active pullright */
116 static Widget m_submenu = NULL; /* parent of the currently active pullright
117 (i.e. the menu label in the parent window) */
118
119 static void ActPopdownSubmenus(Widget w, XEvent *event, String *params, Cardinal *num_params);
120
121 /* to safely pop down the pullright, this callback is added to its parent menu */
122 static XtActionsRec menu_actions[] = {
123 { "popdown-submenus", ActPopdownSubmenus }
124 };
125
126 struct pullright_position_info {
127 Position y;
128 Dimension w;
129 Dimension h;
130 Dimension border_width;
131 Widget menu;
132 };
133
134
135 /*
136 * Set all pixmaps indicating the state of the wigdet pointed to by `elems'.
137 */
138 void
xaw_set_button_state(struct button_elems * elems,Boolean on)139 xaw_set_button_state(struct button_elems *elems, Boolean on)
140 {
141 static Arg args[] = {
142 { XtNleftBitmap, (XtArgVal)0 },
143 { XtNrightBitmap, (XtArgVal)0 }
144 };
145
146 if (elems->type == BT_CHECK)
147 args[0].value = on ? menu_check_on_bitmap : menu_check_off_bitmap;
148 else if (elems->type == BT_RADIO)
149 args[0].value = on ? menu_radio_on_bitmap : menu_radio_off_bitmap;
150 if (elems->submenu != NULL)
151 args[1].value = menu_arrow_bitmap;
152
153 XtSetValues(elems->widget, args, XtNumber(args));
154 }
155
156 /*
157 * Initialize the bitmaps.
158 */
159 void
xaw_initialize_menu_bitmaps(void)160 xaw_initialize_menu_bitmaps(void)
161 {
162 static Boolean initialized = False;
163 if (!initialized) {
164 initialized = True;
165 menu_check_on_bitmap
166 = XCreateBitmapFromData(XtDisplay(globals.widgets.top_level),
167 RootWindowOfScreen(XtScreen(globals.widgets.top_level)),
168 (char *)menu_check_on_bits, MENU_BITMAP_W, MENU_BITMAP_H);
169 menu_check_off_bitmap
170 = XCreateBitmapFromData(XtDisplay(globals.widgets.top_level),
171 RootWindowOfScreen(XtScreen(globals.widgets.top_level)),
172 (char *)menu_check_off_bits, MENU_BITMAP_W, MENU_BITMAP_H);
173 menu_radio_on_bitmap
174 = XCreateBitmapFromData(XtDisplay(globals.widgets.top_level),
175 RootWindowOfScreen(XtScreen(globals.widgets.top_level)),
176 (char *)menu_radio_on_bits, MENU_BITMAP_W, MENU_BITMAP_H);
177 menu_radio_off_bitmap
178 = XCreateBitmapFromData(XtDisplay(globals.widgets.top_level),
179 RootWindowOfScreen(XtScreen(globals.widgets.top_level)),
180 (char *)menu_radio_off_bits, MENU_BITMAP_W, MENU_BITMAP_H);
181 menu_arrow_bitmap
182 = XCreateBitmapFromData(XtDisplay(globals.widgets.top_level),
183 RootWindowOfScreen(XtScreen(globals.widgets.top_level)),
184 (char *)menu_arrow_bits, MENU_ARROW_W, MENU_ARROW_H);
185 }
186 }
187
188
189 void
xaw_create_pagelist(void)190 xaw_create_pagelist(void)
191 {
192 Dimension height, width;
193
194 XtVaGetValues(globals.widgets.clip_widget, XtNheight, &height, NULL);
195 XtVaGetValues(panel_widget, XtNwidth, &width, NULL);
196
197 width = MAX(width - 2 * (resource.btn_side_spacing + resource.btn_border_width),
198 xaw_get_pagelist_size());
199 height -= resource.btn_top_spacing + resource.btn_border_width + m_y_pos;
200 xaw_create_pagelist_widgets(height, width, m_y_pos, panel_widget);
201 }
202
203
204 static XtCallbackRec command_call[] = {
205 {handle_command, NULL},
206 {NULL, NULL},
207 };
208
209 #ifdef USE_PANNER
210 void
scroll_x_panner(int x)211 scroll_x_panner(int x)
212 {
213 static int old_x = 0;
214 if (panner != 0 && ABS(x - old_x) > 8) {
215 XtVaSetValues(panner, XtNsliderX, x, NULL);
216 old_x = x;
217 }
218 }
219
220 void
scroll_y_panner(int y)221 scroll_y_panner(int y)
222 {
223 static int old_y = 0;
224 if (panner != 0 && ABS(y - old_y) > 8) {
225 XtVaSetValues(panner, XtNsliderY, y, NULL);
226 old_y = y;
227 }
228 }
229
230 static void
panner_cb(Widget widget,XtPointer closure,XtPointer report_ptr)231 panner_cb(Widget widget, XtPointer closure, XtPointer report_ptr)
232 {
233 XawPannerReport *report = (XawPannerReport *)report_ptr;
234 static int orig_x = 0, orig_y = 0;
235 int x = report->slider_x;
236 int y = report->slider_y;
237 static Dimension w, h;
238 static Arg arg_wh_clip[] = {
239 {XtNwidth, (XtArgVal) &w},
240 {XtNheight, (XtArgVal) &h},
241 };
242
243 UNUSED(closure);
244
245 XtGetValues(globals.widgets.clip_widget, arg_wh_clip, XtNumber(arg_wh_clip));
246
247 fprintf(stderr, "w: %d, h: %d, globals.page.w: %d, globals.page.h: %d\n",
248 w, h, globals.page.w, globals.page.h);
249 XtVaSetValues(widget,
250 XtNsliderWidth, w, XtNsliderHeight, h,
251 XtNcanvasWidth, globals.page.w, XtNcanvasHeight, globals.page.h,
252 NULL);
253
254 fprintf(stderr, "panner moved: %d, %d\n", report->slider_x, report->slider_y);
255 if (globals.widgets.x_bar != NULL)
256 XtCallCallbacks(globals.widgets.x_bar, XtNscrollProc, cast_int_to_XtPointer(x - orig_x));
257 if (globals.widgets.y_bar != NULL)
258 XtCallCallbacks(globals.widgets.y_bar, XtNscrollProc, cast_int_to_XtPointer(y - orig_y));
259 orig_x = x;
260 orig_y = y;
261 }
262 #endif /* USE_PANNER */
263
264
265 static void
create_menu_entries(struct button_info * item,Widget parent)266 create_menu_entries(struct button_info *item, Widget parent)
267 {
268 size_t i;
269
270 /* add our own popdown-submenus() action to the default translations of this menu */
271 XtAccelerators menu_accels = XtParseAcceleratorTable("<BtnUp>:MenuPopdown()notify()unhighlight()popdown-submenus()");
272
273 for (i = 0; item != NULL && i < item->size; i++) {
274 Widget w;
275
276 if (item->elems[i].submenu != NULL) { /* another submenu */
277 XDVI_ERROR((stderr, "Xaw menus don't support nested pulldown menus (ignoring)"));
278 continue;
279 }
280
281 if (item->elems[i].type == BT_SEP) { /* separator */
282 w = XtCreateManagedWidget(item->elems[i].title, smeLineObjectClass, parent, NULL, 0);
283 }
284 else if (item->elems[i].action != NULL && item->elems[i].action->proc == Act_recent_files) {
285 /* special case: submenu with recent files */
286 w = XtVaCreateManagedWidget(item->elems[i].title, smeBSBObjectClass, parent,
287 XtNlabel, item->elems[i].title,
288 XtNleftMargin, 20,
289 XtNrightMargin, 16,
290 XtNrightBitmap, menu_arrow_bitmap,
291 NULL);
292 m_submenu = w;
293 XtOverrideTranslations(parent, menu_accels);
294 }
295 else { /* normal menu entry */
296 w = XtVaCreateManagedWidget(item->elems[i].title, smeBSBObjectClass, parent,
297 XtNlabel, item->elems[i].title,
298 XtNleftMargin, 20,
299 NULL);
300 XtAddCallback(w, XtNcallback, handle_command, item->elems[i].action);
301 }
302 item->elems[i].widget = w;
303 }
304 }
305
306 void
xaw_create_menu(struct button_info * item,Widget parent,int * ret_width)307 xaw_create_menu(struct button_info *item, Widget parent, int *ret_width)
308 {
309 Dimension y_pos = resource.btn_top_spacing;
310 size_t i;
311
312 XtAppAddActions(XtWidgetToApplicationContext(globals.widgets.form_widget), menu_actions, XtNumber(menu_actions));
313
314 for (i = 0; item != NULL && i < item->size; i++) {
315 Widget button;
316 Dimension w, h;
317
318 if (item->elems[i].type == BT_SEP) { /* separator */
319 XDVI_ERROR((stderr, "Cannot have a separator on a toplevel Xaw menu (ignoring)"));
320 /* y_pos += resource.btn_between_extra; */
321 continue;
322 }
323 else if (item->elems[i].submenu != NULL) { /* menu button, create a pulldown menu */
324 Widget shell;
325
326 button = XtVaCreateWidget("button", menuButtonWidgetClass, parent,
327 XtNmenuName, item->elems[i].title,
328 XtNlabel, item->elems[i].title,
329 XtNx, resource.btn_side_spacing,
330 XtNy, y_pos,
331 XtNborderWidth, resource.btn_border_width,
332 NULL);
333 shell = XtCreatePopupShell(item->elems[i].title, simpleMenuWidgetClass, button, NULL, 0);
334
335 create_menu_entries(item->elems[i].submenu, shell);
336 }
337 else { /* command button */
338 command_call[0].closure = (XtPointer)item->elems[i].action;
339 button = XtVaCreateWidget(item->elems[i].title, commandWidgetClass, parent,
340 XtNlabel, item->elems[i].title,
341 XtNx, resource.btn_side_spacing,
342 XtNy, y_pos,
343 XtNborderWidth, resource.btn_border_width,
344 XtNcallback, (XtArgVal)command_call,
345 NULL);
346 }
347 XtVaGetValues(button, XtNwidth, &w, XtNheight, &h, NULL);
348 y_pos += h + resource.btn_between_spacing + 2 * resource.btn_border_width;
349 if (w > m_panel_width)
350 m_panel_width = w;
351 item->elems[i].widget = button;
352 }
353
354 /* adjust button sizes, and manage buttons (managing them earlier may result
355 in `parent has no geometry manager' error) */
356 for (i = 0; item != NULL && i < item->size; i++) {
357 XtVaSetValues(item->elems[i].widget, XtNwidth, m_panel_width, NULL);
358 XtManageChild(item->elems[i].widget);
359 }
360
361 m_y_pos = y_pos - resource.btn_between_spacing + resource.btn_top_spacing + 2 * resource.btn_border_width;
362 m_panel_width += 2 * resource.btn_side_spacing + resource.btn_border_width;
363 XtVaSetValues(panel_widget, XtNwidth, m_panel_width, NULL);
364 *ret_width = m_panel_width;
365 }
366
367 Widget
xaw_create_menu_widgets(Widget parent)368 xaw_create_menu_widgets(Widget parent)
369 {
370 /* hack to disable the magnifier in the panel: */
371 XtTranslations panel_translations = XtParseTranslationTable("#augment <ButtonPress>:");
372
373 XtAppAddActions(XtWidgetToApplicationContext(parent), menu_actions, XtNumber(menu_actions));
374
375 line_widget = XtVaCreateWidget("line", widgetClass, parent,
376 XtNbackground, (XtArgVal)resource.fore_Pixel,
377 XtNwidth, (XtArgVal)1,
378 XtNfromHoriz, (XtArgVal)globals.widgets.vport_widget,
379 XtNborderWidth, (XtArgVal)0,
380 XtNtop, (XtArgVal)XtChainTop,
381 XtNbottom, (XtArgVal)XtChainBottom,
382 XtNleft, (XtArgVal)XtChainRight,
383 XtNright, (XtArgVal)XtChainRight,
384 NULL);
385 panel_widget = XtVaCreateWidget("panel", compositeWidgetClass, parent,
386 XtNborderWidth, (XtArgVal)0,
387 XtNfromHoriz, (XtArgVal)line_widget,
388 XtNtranslations, (XtArgVal)panel_translations,
389 XtNtop, (XtArgVal)XtChainTop,
390 XtNbottom, (XtArgVal)XtChainBottom,
391 XtNleft, (XtArgVal)XtChainRight,
392 XtNright, (XtArgVal)XtChainRight,
393 NULL);
394 return panel_widget;
395 }
396
397 static void
filehist_select_cb(Widget w,XtPointer client_data,XtPointer call_data)398 filehist_select_cb(Widget w, XtPointer client_data, XtPointer call_data)
399 {
400 char *label, *ptr;
401 int idx;
402
403 UNUSED(client_data);
404 UNUSED(call_data);
405
406 XtVaGetValues(w, XtNlabel, &label, NULL);
407
408 idx = strtol(label, &ptr, 10) - 1;
409 while (isspace(*ptr))
410 ptr++;
411 TRACE_GUI((stderr, "User selected: %d, `%s'", idx, ptr));
412 if (idx == 0) {
413 globals.ev.flags |= EV_RELOAD;
414 return;
415 }
416 file_history_open(ptr);
417 }
418
419 static void
update_menu_labels(Widget menu)420 update_menu_labels(Widget menu)
421 {
422 WidgetList children;
423 int num_children;
424 int i;
425
426 static char *buf = NULL;
427 static size_t buf_len = 0;
428 size_t new_len;
429
430 XtVaGetValues(menu,
431 XtNnumChildren, &num_children,
432 XtNchildren, &children,
433 NULL);
434 for (i = 0; i < (int)file_history_size(); i++) {
435 int dummy_page;
436 char *filename;
437
438 if ((filename = file_history_get_elem(i, &dummy_page)) == NULL) {
439 XDVI_ERROR((stderr, "Error accessing element %d of file history", i));
440 continue;
441 }
442
443 new_len = LENGTH_OF_INT + strlen(filename) + 1;
444 if (new_len > buf_len) {
445 buf = xrealloc(buf, new_len);
446 buf_len = new_len;
447 }
448
449 sprintf(buf, "%d %s", i + 1, filename);
450 XtVaSetValues(children[i], XtNlabel, buf, NULL);
451 TRACE_GUI((stderr, "child %d: `%s'", i, buf));
452 }
453
454 /* if history size < number of menu entries, destroy excess menu entries */
455 for (; i < num_children; i++) {
456 XtDestroyWidget(children[i]);
457 }
458 }
459
460 void
filehist_menu_add_entry(const char * filename)461 filehist_menu_add_entry(const char *filename)
462 {
463 static char *buf = NULL;
464 static size_t buf_len = 0;
465 size_t new_len = LENGTH_OF_INT + strlen(filename) + 1;
466
467 Widget menu;
468 /* Don't report an error here, since "filehist_pullright" is only created on-demand
469 when user clicks on menu, but this may be called before from the event loop.
470 (The menu will still contain this entry when it's created later.) */
471 if (get_widget_by_name(&menu, globals.widgets.top_level, "filehist_pullright", False)) {
472 int num_children;
473 Widget w;
474
475 if (new_len > buf_len) {
476 buf = xrealloc(buf, new_len);
477 buf_len = new_len;
478 }
479
480 XtVaGetValues(menu, XtNnumChildren, &num_children, NULL);
481 sprintf(buf, "%d %s", num_children + 1, filename);
482
483 w = XtVaCreateManagedWidget("_filehist", smeBSBObjectClass, menu,
484 XtNlabel, buf,
485 XtNleftMargin, 10,
486 NULL);
487 XtAddCallback(w, XtNcallback, filehist_select_cb, NULL);
488 update_menu_labels(menu);
489 }
490 }
491
492 void
filehist_menu_refresh(void)493 filehist_menu_refresh(void)
494 {
495 Widget menu;
496
497 /* Don't report an error here, since "filehist_pullright" is only created on-demand
498 when user clicks on menu, but this may be called before from the event loop.
499 (The menu will still contain this entry when it's created later.) */
500 if (get_widget_by_name(&menu, globals.widgets.top_level, "filehist_pullright", False)) {
501 update_menu_labels(menu);
502 }
503 }
504
505
506 static void
filehist_insert_submenu(int idx,const char * filename,int pageno,void * data)507 filehist_insert_submenu(int idx, const char *filename, int pageno, void *data)
508 {
509 Widget menu = (Widget)data;
510 Widget w;
511 static char *buf = NULL;
512 static size_t buf_len = 0;
513 size_t new_len = LENGTH_OF_INT + strlen(filename) + 1;
514
515 UNUSED(pageno);
516
517 if (new_len > buf_len) {
518 buf = xrealloc(buf, new_len);
519 buf_len = new_len;
520 }
521
522 sprintf(buf, "%d %s", idx + 1, filename);
523 TRACE_GUI((stderr, "Creating menu `%s'", buf));
524 w = XtVaCreateManagedWidget("_filehist", smeBSBObjectClass, menu,
525 XtNlabel, buf,
526 XtNleftMargin, 10,
527 NULL);
528 XtAddCallback(w, XtNcallback, filehist_select_cb, NULL);
529 }
530
531 /*
532 ============================================================
533 Hack for pullright menus part II: callbacks and functions
534 ============================================================
535 */
536
537 /* callback to pop down the currently active pullright */
538 static void
ActPopdownSubmenus(Widget w,XEvent * event,String * params,Cardinal * num_params)539 ActPopdownSubmenus(Widget w, XEvent *event, String *params, Cardinal *num_params)
540 {
541 UNUSED(w);
542 UNUSED(event);
543 UNUSED(params);
544 UNUSED(num_params);
545
546 if (m_timeout != 0)
547 XtRemoveTimeOut(m_timeout);
548 m_timeout = 0;
549 if (m_active_submenu != NULL)
550 XtPopdown(m_active_submenu);
551 }
552 /* create a parent shell for the pullright menu entries */
553 static Widget
create_files_submenu(void)554 create_files_submenu(void)
555 {
556 Widget popup = XtCreatePopupShell("filehist_pullright", simpleMenuWidgetClass, globals.widgets.top_level,
557 NULL, 0);
558 file_history_enumerate(filehist_insert_submenu, popup);
559 return popup;
560 }
561
562 /* Acutally pop up the pullright menu */
563 static void
popup_pullright(XtPointer client_data,XtIntervalId * id)564 popup_pullright(XtPointer client_data, XtIntervalId *id)
565 {
566 int pos_x, pos_y;
567 Dimension w1;
568 Window dummy;
569 static Widget files_submenu = NULL;
570 struct pullright_position_info *info = (struct pullright_position_info *)client_data;
571
572 UNUSED(id);
573
574 if (files_submenu == NULL)
575 files_submenu = create_files_submenu();
576 /* XtManageChild(files_submenu); */
577 XTranslateCoordinates(DISP, XtWindow(XtParent(m_submenu)), RootWindowOfScreen(SCRN),
578 info->w, info->y, &pos_x, &pos_y, &dummy);
579 XtRealizeWidget(files_submenu);
580 XtVaGetValues(files_submenu, XtNwidth, &w1, NULL);
581 TRACE_GUI((stderr, "Popping up at %d, %d, %d, %d", pos_x, pos_y, w1, WidthOfScreen(SCRN)));
582
583 /* if not sufficient place on the right, pop it up on the left */
584 /* fprintf(stderr, "border_width: %d\n", info->border_width); */
585 if (pos_x + w1 > WidthOfScreen(SCRN)) {
586 /* fprintf(stderr, "%d > %d!\n", pos_x + w1, WidthOfScreen(SCRN)); */
587 pos_x -= (w1 + info->w + 3 * info->border_width);
588 /* fprintf(stderr, "new x: %d\n", pos_x); */
589 }
590 else {
591 pos_x += info->border_width;
592 }
593 XtVaSetValues(files_submenu,
594 XtNx, pos_x,
595 XtNy, pos_y,
596 NULL);
597 /* use XtPopupSpringLoaded() instead of XtPopup() since it does a few things
598 that make the pullright behave like a proper menu, like highlighting the
599 current selection, setting the cursor shape etc. */
600 XtPopupSpringLoaded(files_submenu);
601 m_active_submenu = files_submenu;
602 }
603
604 /*
605 * This event handler (to be called by read_events(), the main event handling loop)
606 * creates a timeout for the pullright to pop up.
607 */
608 void
SubMenuHandleEvent(XtAppContext app,XEvent * event)609 SubMenuHandleEvent(XtAppContext app, XEvent *event)
610 {
611 static int flag = 0;
612 static struct pullright_position_info info = { -1, 0, 0, 0, NULL };
613
614 UNUSED(app);
615
616 if (m_submenu == NULL)
617 return;
618
619 if (event->type == EnterNotify
620 || event->type == MotionNotify
621 || event->type == LeaveNotify
622 || event->type == ButtonPress) {
623
624 /* fprintf(stderr, "SubMenuHandleEvent: 0x%lx, 0x%lx\n", event->xany.window, XtWindow(m_submenu)); */
625
626 /* Could also loop through a list of windows here ...
627 We need to check for the parent of the menu item, since smeBSBObject is not
628 a real window, and then manually check whether the pointer is inside the menu
629 item.
630 */
631 if (XtParent(m_submenu) != NULL &&
632 event->xany.window == XtWindow(XtParent(m_submenu))) {
633 /* don't need to check for x coordinates since we already
634 know that pointer is inside the parent */
635 if (info.y == -1) { /* initialize info */
636 XtVaGetValues(m_submenu,
637 XtNy, &(info.y),
638 XtNwidth, &(info.w),
639 XtNheight, &(info.h),
640 NULL);
641 XtVaGetValues(XtParent(m_submenu),
642 XtNborderWidth, &(info.border_width),
643 NULL);
644
645 info.menu = m_submenu;
646 }
647 if (info.y < event->xbutton.y && info.y + info.h > event->xbutton.y) {
648 if (flag == 0) {
649 /* Create a timeout of 200ms to pop up the menu, so that it doesn't
650 pop up always when the cursor is only moved over the pulldown menu.
651 I think this is about the same delay as the one used by Motif.
652 */
653 flag = 1;
654 TRACE_GUI((stderr, "ENTER: %d, %d, %d; %d, %d",
655 info.y, info.w, info.h, event->xbutton.x, event->xbutton.y));
656 m_timeout = XtAppAddTimeOut(app, 200, popup_pullright, &info);
657 return;
658 }
659 }
660 else if (flag == 1) {
661 flag = 0;
662 TRACE_GUI((stderr, "LEAVE!"));
663 if (m_timeout != 0)
664 XtRemoveTimeOut(m_timeout);
665 m_timeout = 0;
666 if (m_active_submenu != NULL)
667 XtPopdown(m_active_submenu);
668 m_active_submenu = NULL;
669 }
670 }
671 }
672 }
673
674 void
realize_button_panel(XtArgVal h)675 realize_button_panel(XtArgVal h)
676 {
677 XtVaSetValues(line_widget, XtNheight, h, NULL);
678 XtVaSetValues(panel_widget, XtNheight, h, NULL);
679
680 if ((resource.expert_mode & XPRT_SHOW_BUTTONS) != 0) {
681 XtManageChild(line_widget);
682 XtManageChild(panel_widget);
683 }
684 }
685
686 static void
reconfig_window(void)687 reconfig_window(void) {
688 Dimension x_top, y_top, h_top, w_top;
689 XWindowChanges sizeconfigure;
690 int sizeconfiguremask;
691
692 /* brute-force method to bring the scrollbars back. Apparently a single XConfigureWindow()
693 isn't enough to get the scrollbars back, we actually need to move the window a bit,
694 and then move it back. */
695 sizeconfiguremask = CWWidth | CWHeight;
696 XtVaGetValues(globals.widgets.top_level, XtNx, &x_top, XtNy, &y_top, XtNheight, &h_top, XtNwidth, &w_top, NULL);
697 sizeconfigure.width = w_top + 1;
698 sizeconfigure.height = h_top + 1;
699 XConfigureWindow(DISP, XtWindow(globals.widgets.top_level), sizeconfiguremask, &sizeconfigure);
700 sizeconfigure.width = w_top;
701 sizeconfigure.height = h_top;
702 XConfigureWindow(DISP, XtWindow(globals.widgets.top_level), sizeconfiguremask, &sizeconfigure);
703 }
704
705 /* toggle scrollbars to state `visible' */
706 void
toggle_scrollbars(void)707 toggle_scrollbars(void)
708 {
709 Widget v_bar = XtNameToWidget(globals.widgets.vport_widget, "vertical");
710 Widget h_bar = XtNameToWidget(globals.widgets.vport_widget, "horizontal");
711 static Dimension bar_thick;
712 static Boolean v_bar_mapped = False, h_bar_mapped = False;
713 static Boolean initialized = False;
714
715 Boolean make_v_bar_visible = False;
716 Boolean make_v_bar_invisible = False;
717
718 Boolean make_h_bar_visible = False;
719 Boolean make_h_bar_invisible = False;
720
721 if (v_bar != 0) {
722 int test_v = 0;
723 XtVaGetValues(v_bar, XtNwidth, &test_v, NULL);
724 if (test_v > 1)
725 v_bar_mapped = True;
726 }
727 if (h_bar != 0) {
728 int test_h = 0;
729 XtVaGetValues(h_bar, XtNheight, &test_h, NULL);
730 if (test_h > 1)
731 h_bar_mapped = True;
732 }
733
734 if (!initialized) {
735 v_bar_mapped = h_bar_mapped = (resource.expert_mode & XPRT_SHOW_SCROLLBARS) != 0;
736 initialized = True;
737 if (v_bar != 0)
738 XtVaGetValues(v_bar, XtNwidth, &bar_thick, NULL);
739 else if (h_bar != 0)
740 XtVaGetValues(h_bar, XtNheight, &bar_thick, NULL);
741 else
742 bar_thick = 15; /* FIXME */
743 }
744
745 if ((resource.expert_mode & XPRT_SHOW_SCROLLBARS) == 0) {
746 if (v_bar_mapped)
747 make_v_bar_invisible = True;
748 if (h_bar_mapped)
749 make_h_bar_invisible = True;
750 }
751 else {
752 if (!v_bar_mapped)
753 make_v_bar_visible = True;
754 if (!h_bar_mapped)
755 make_h_bar_visible = True;
756 }
757
758 if (make_h_bar_visible || make_v_bar_visible) {
759 if (make_h_bar_visible && h_bar != 0) {
760 TRACE_GUI((stderr, "h_bar: h %d", bar_thick));
761 XtVaSetValues(h_bar, XtNheight, bar_thick, XtNx, bar_thick, XtNy, 0, XtNborderWidth, 1, NULL);
762 XtManageChild(h_bar);
763 h_bar_mapped = True;
764 }
765 if (make_v_bar_visible && v_bar != 0) {
766 TRACE_GUI((stderr, "v_bar: w %d", bar_thick));
767 XtVaSetValues(v_bar, XtNwidth, bar_thick, XtNx, 0, XtNy, bar_thick, XtNborderWidth, 1, NULL);
768 XtManageChild(v_bar);
769 v_bar_mapped = True;
770 }
771 if (h_bar != 0 || v_bar != 0) { /* need to reconfigure screen */
772 reconfig_window();
773 }
774 }
775 else if (make_h_bar_invisible || make_v_bar_invisible) {
776 if (make_h_bar_invisible && h_bar != 0) {
777 XtUnmanageChild(h_bar);
778 XtVaSetValues(h_bar, XtNheight, 1, XtNx, 0, XtNy, 0, XtNborderWidth, 0, NULL);
779 h_bar_mapped = False;
780 }
781 if (make_v_bar_invisible && v_bar != 0) {
782 XtUnmanageChild(v_bar);
783 XtVaSetValues(v_bar, XtNwidth, 1, XtNy, 0, XtNy, 0, XtNborderWidth, 0, NULL);
784 v_bar_mapped = False;
785 }
786 if (h_bar != 0 || v_bar != 0) { /* need to reconfigure screen */
787 reconfig_window();
788 }
789 }
790
791 }
792
793 void
toggle_buttons(void)794 toggle_buttons(void)
795 {
796 static Dimension panel_width = 0;
797 Dimension w;
798
799 if (panel_width == 0) /* initialize */
800 XtVaGetValues(panel_widget, XtNwidth, &panel_width, NULL);
801
802 /* need to get current width of form in case user has resized the window */
803 XtVaGetValues(globals.widgets.form_widget, XtNwidth, &w, NULL);
804 XtVaSetValues(globals.widgets.vport_widget, XtNresizable, (XtArgVal)True, NULL);
805
806 if (resource.expert_mode & XPRT_SHOW_BUTTONS) { /* show buttons */
807 XtManageChild(panel_widget);
808 XtManageChild(line_widget);
809 XtVaSetValues(globals.widgets.vport_widget,
810 XtNwidth, w - panel_width - 1, /* -1 for line_widget */
811 NULL);
812 }
813 else { /* hide buttons */
814 XtUnmanageChild(panel_widget);
815 XtUnmanageChild(line_widget);
816 XtVaSetValues(globals.widgets.vport_widget, XtNwidth, w, NULL);
817 }
818 }
819
820 #else
821 /* silence `empty compilation unit' warnings */
bar(void)822 static void bar(void); static void foo(void) { bar(); } static void bar(void) { foo(); }
823 #endif /* ifndef MOTIF */
824