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