xref: /vim-8.2.3635/src/gui_athena.c (revision 94688b8a)
1 /* vi:set ts=8 sts=4 sw=4 noet:
2  *
3  * VIM - Vi IMproved		by Bram Moolenaar
4  *				GUI/Motif support by Robert Webb
5  *				Athena port by Bill Foster
6  *
7  * Do ":help uganda"  in Vim to read copying and usage conditions.
8  * Do ":help credits" in Vim to see a list of people who contributed.
9  * See README.txt for an overview of the Vim source code.
10  */
11 
12 #include "vim.h"
13 
14 #include <X11/StringDefs.h>
15 #include <X11/Intrinsic.h>
16 #ifdef FEAT_GUI_NEXTAW
17 # include <X11/neXtaw/Form.h>
18 # include <X11/neXtaw/SimpleMenu.h>
19 # include <X11/neXtaw/MenuButton.h>
20 # include <X11/neXtaw/SmeBSB.h>
21 # include <X11/neXtaw/SmeLine.h>
22 # include <X11/neXtaw/Box.h>
23 # include <X11/neXtaw/Dialog.h>
24 # include <X11/neXtaw/Text.h>
25 # include <X11/neXtaw/AsciiText.h>
26 # include <X11/neXtaw/Scrollbar.h>
27 #else
28 # include <X11/Xaw/Form.h>
29 # include <X11/Xaw/SimpleMenu.h>
30 # include <X11/Xaw/MenuButton.h>
31 # include <X11/Xaw/SmeBSB.h>
32 # include <X11/Xaw/SmeLine.h>
33 # include <X11/Xaw/Box.h>
34 # include <X11/Xaw/Dialog.h>
35 # include <X11/Xaw/Text.h>
36 # include <X11/Xaw/AsciiText.h>
37 #endif /* FEAT_GUI_NEXTAW */
38 
39 #ifndef FEAT_GUI_NEXTAW
40 # include "gui_at_sb.h"
41 #endif
42 
43 extern Widget vimShell;
44 
45 static Widget vimForm = (Widget)0;
46 Widget textArea = (Widget)0;
47 #ifdef FEAT_MENU
48 static Widget menuBar = (Widget)0;
49 static XtIntervalId timer = 0;	    /* 0 = expired, otherwise active */
50 
51 /* Used to figure out menu ordering */
52 static vimmenu_T *a_cur_menu = NULL;
53 static Cardinal	athena_calculate_ins_pos(Widget);
54 
55 static void gui_athena_popup_callback(Widget, XtPointer, XtPointer);
56 static void gui_athena_delayed_arm_action(Widget, XEvent *, String *,
57 						 Cardinal *);
58 static void gui_athena_popdown_submenus_action(Widget, XEvent *,
59 						      String *, Cardinal *);
60 static XtActionsRec	pullAction[2] = {
61     { "menu-delayedpopup", (XtActionProc)gui_athena_delayed_arm_action},
62     { "menu-popdownsubmenus", (XtActionProc)gui_athena_popdown_submenus_action}
63 };
64 #endif
65 
66 #ifdef FEAT_TOOLBAR
67 static void gui_mch_reset_focus(void);
68 static Widget toolBar = (Widget)0;
69 #endif
70 
71 #if defined(FEAT_GUI_DIALOG) || defined(FEAT_MENU)
72 static void gui_athena_menu_colors(Widget id);
73 #endif
74 static void gui_athena_scroll_colors(Widget id);
75 
76 #ifdef FEAT_MENU
77 static XtTranslations	popupTrans, parentTrans, menuTrans, supermenuTrans;
78 static Pixmap		pullerBitmap = None;
79 static int		puller_width = 0;
80 #endif
81 
82 /*
83  * Scrollbar callback (XtNjumpProc) for when the scrollbar is dragged with the
84  * left or middle mouse button.
85  */
86     static void
87 gui_athena_scroll_cb_jump(
88     Widget	w UNUSED,
89     XtPointer	client_data,
90     XtPointer	call_data)
91 {
92     scrollbar_T *sb, *sb_info;
93     long	value;
94 
95     sb = gui_find_scrollbar((long)client_data);
96 
97     if (sb == NULL)
98 	return;
99     else if (sb->wp != NULL)	    /* Left or right scrollbar */
100     {
101 	/*
102 	 * Careful: need to get scrollbar info out of first (left) scrollbar
103 	 * for window, but keep real scrollbar too because we must pass it to
104 	 * gui_drag_scrollbar().
105 	 */
106 	sb_info = &sb->wp->w_scrollbars[0];
107     }
108     else	    /* Bottom scrollbar */
109 	sb_info = sb;
110 
111     value = (long)(*((float *)call_data) * (float)(sb_info->max + 1) + 0.001);
112     if (value > sb_info->max)
113 	value = sb_info->max;
114 
115     gui_drag_scrollbar(sb, value, TRUE);
116 }
117 
118 /*
119  * Scrollbar callback (XtNscrollProc) for paging up or down with the left or
120  * right mouse buttons.
121  */
122     static void
123 gui_athena_scroll_cb_scroll(
124     Widget	w UNUSED,
125     XtPointer	client_data,
126     XtPointer	call_data)
127 {
128     scrollbar_T *sb, *sb_info;
129     long	value;
130     int		data = (int)(long)call_data;
131     int		page;
132 
133     sb = gui_find_scrollbar((long)client_data);
134 
135     if (sb == NULL)
136 	return;
137     if (sb->wp != NULL)		/* Left or right scrollbar */
138     {
139 	/*
140 	 * Careful: need to get scrollbar info out of first (left) scrollbar
141 	 * for window, but keep real scrollbar too because we must pass it to
142 	 * gui_drag_scrollbar().
143 	 */
144 	sb_info = &sb->wp->w_scrollbars[0];
145 
146 	if (sb_info->size > 5)
147 	    page = sb_info->size - 2;	    /* use two lines of context */
148 	else
149 	    page = sb_info->size;
150 #ifdef FEAT_GUI_NEXTAW
151 	if (data < 0)
152 	{
153 	    data = (data - gui.char_height + 1) / gui.char_height;
154 	    if (data > -sb_info->size)
155 		data = -1;
156 	    else
157 		data = -page;
158 	}
159 	else if (data > 0)
160 	{
161 	    data = (data + gui.char_height - 1) / gui.char_height;
162 	    if (data < sb_info->size)
163 		data = 1;
164 	    else
165 		data = page;
166 	}
167 #else
168 	switch (data)
169 	{
170 	    case  ONE_LINE_DATA: data = 1; break;
171 	    case -ONE_LINE_DATA: data = -1; break;
172 	    case  ONE_PAGE_DATA: data = page; break;
173 	    case -ONE_PAGE_DATA: data = -page; break;
174 	    case  END_PAGE_DATA: data = sb_info->max; break;
175 	    case -END_PAGE_DATA: data = -sb_info->max; break;
176 			default: data = 0; break;
177 	}
178 #endif
179     }
180     else			/* Bottom scrollbar */
181     {
182 	sb_info = sb;
183 #ifdef FEAT_GUI_NEXTAW
184 	if (data < 0)
185 	{
186 	    data = (data - gui.char_width + 1) / gui.char_width;
187 	    if (data > -sb->size)
188 		data = -1;
189 	}
190 	else if (data > 0)
191 	{
192 	    data = (data + gui.char_width - 1) / gui.char_width;
193 	    if (data < sb->size)
194 		data = 1;
195 	}
196 #endif
197 	if (data < -1)		/* page-width left */
198 	{
199 	    if (sb->size > 8)
200 		data = -(sb->size - 5);
201 	    else
202 		data = -sb->size;
203 	}
204 	else if (data > 1)	/* page-width right */
205 	{
206 	    if (sb->size > 8)
207 		data = (sb->size - 5);
208 	    else
209 		data = sb->size;
210 	}
211     }
212 
213     value = sb_info->value + data;
214     if (value > sb_info->max)
215 	value = sb_info->max;
216     else if (value < 0)
217 	value = 0;
218 
219     /* Update the bottom scrollbar an extra time (why is this needed?? */
220     if (sb->wp == NULL)		/* Bottom scrollbar */
221 	gui_mch_set_scrollbar_thumb(sb, value, sb->size, sb->max);
222 
223     gui_drag_scrollbar(sb, value, FALSE);
224 }
225 
226 /*
227  * Create all the Athena widgets necessary.
228  */
229     void
230 gui_x11_create_widgets(void)
231 {
232     /*
233      * We don't have any borders handled internally by the textArea to worry
234      * about so only skip over the configured border width.
235      */
236     gui.border_offset = gui.border_width;
237 
238     /* The form containing all the other widgets */
239     vimForm = XtVaCreateManagedWidget("vimForm",
240 	formWidgetClass,	vimShell,
241 	XtNborderWidth,		0,
242 	NULL);
243     gui_athena_scroll_colors(vimForm);
244 
245 #ifdef FEAT_MENU
246     /* The top menu bar */
247     menuBar = XtVaCreateManagedWidget("menuBar",
248 	boxWidgetClass,		vimForm,
249 	XtNresizable,		True,
250 	XtNtop,			XtChainTop,
251 	XtNbottom,		XtChainTop,
252 	XtNleft,		XtChainLeft,
253 	XtNright,		XtChainRight,
254 	XtNinsertPosition,	athena_calculate_ins_pos,
255 	NULL);
256     gui_athena_menu_colors(menuBar);
257     if (gui.menu_fg_pixel != INVALCOLOR)
258 	XtVaSetValues(menuBar, XtNborderColor, gui.menu_fg_pixel, NULL);
259 #endif
260 
261 #ifdef FEAT_TOOLBAR
262     /* Don't create it Managed, it will be managed when creating the first
263      * item.  Otherwise an empty toolbar shows up. */
264     toolBar = XtVaCreateWidget("toolBar",
265 	boxWidgetClass,		vimForm,
266 	XtNresizable,		True,
267 	XtNtop,			XtChainTop,
268 	XtNbottom,		XtChainTop,
269 	XtNleft,		XtChainLeft,
270 	XtNright,		XtChainRight,
271 	XtNorientation,		XtorientHorizontal,
272 	XtNhSpace,		1,
273 	XtNvSpace,		3,
274 	XtNinsertPosition,	athena_calculate_ins_pos,
275 	NULL);
276     gui_athena_menu_colors(toolBar);
277 #endif
278 
279     /* The text area. */
280     textArea = XtVaCreateManagedWidget("textArea",
281 	coreWidgetClass,	vimForm,
282 	XtNresizable,		True,
283 	XtNtop,			XtChainTop,
284 	XtNbottom,		XtChainTop,
285 	XtNleft,		XtChainLeft,
286 	XtNright,		XtChainLeft,
287 	XtNbackground,		gui.back_pixel,
288 	XtNborderWidth,		0,
289 	NULL);
290 
291     /*
292      * Install the callbacks.
293      */
294     gui_x11_callbacks(textArea, vimForm);
295 
296 #ifdef FEAT_MENU
297     popupTrans = XtParseTranslationTable(
298 	    "<EnterWindow>: menu-popdownsubmenus() highlight() menu-delayedpopup()\n"
299 	    "<LeaveWindow>: unhighlight()\n"
300 	    "<BtnUp>: menu-popdownsubmenus() XtMenuPopdown() notify() unhighlight()\n"
301 	    "<Motion>: highlight() menu-delayedpopup()");
302     parentTrans = XtParseTranslationTable("<LeaveWindow>: unhighlight()");
303     menuTrans = XtParseTranslationTable(
304 	    "<EnterWindow>: menu-popdownsubmenus() highlight() menu-delayedpopup()\n"
305 	    "<LeaveWindow>: menu-popdownsubmenus() XtMenuPopdown() unhighlight()\n"
306 	    "<BtnUp>: notify() unhighlight()\n"
307 	    "<BtnMotion>: highlight() menu-delayedpopup()");
308     supermenuTrans = XtParseTranslationTable(
309 	    "<EnterWindow>: menu-popdownsubmenus() highlight() menu-delayedpopup()\n"
310 	    "<LeaveWindow>: unhighlight()\n"
311 	    "<BtnUp>: menu-popdownsubmenus() XtMenuPopdown() notify() unhighlight()\n"
312 	    "<BtnMotion>: highlight() menu-delayedpopup()");
313 
314     XtAppAddActions(XtWidgetToApplicationContext(vimForm), pullAction,
315 		    XtNumber(pullAction));
316 #endif
317 
318     /* Pretend we don't have input focus, we will get an event if we do. */
319     gui.in_focus = FALSE;
320 }
321 
322 #ifdef FEAT_MENU
323 /*
324  * Calculates the Pixmap based on the size of the current menu font.
325  */
326     static Pixmap
327 gui_athena_create_pullright_pixmap(Widget w)
328 {
329     Pixmap  retval;
330 #ifdef FONTSET_ALWAYS
331     XFontSet	font = None;
332 #else
333     XFontStruct	*font = NULL;
334 #endif
335 
336 #ifdef FONTSET_ALWAYS
337     if (gui.menu_fontset == NOFONTSET)
338 #else
339     if (gui.menu_font == NOFONT)
340 #endif
341     {
342 	XrmValue from, to;
343 	WidgetList  children;
344 	Cardinal    num_children;
345 
346 #ifdef FONTSET_ALWAYS
347 	from.size = strlen(from.addr = XtDefaultFontSet);
348 	to.addr = (XtPointer)&font;
349 	to.size = sizeof(XFontSet);
350 #else
351 	from.size = strlen(from.addr = XtDefaultFont);
352 	to.addr = (XtPointer)&font;
353 	to.size = sizeof(XFontStruct *);
354 #endif
355 	/* Assumption: The menuBar children will use the same font as the
356 	 *	       pulldown menu items AND they will all be of type
357 	 *	       XtNfont.
358 	 */
359 	XtVaGetValues(menuBar, XtNchildren, &children,
360 			       XtNnumChildren, &num_children,
361 			       NULL);
362 	if (XtConvertAndStore(w ? w :
363 				(num_children > 0) ? children[0] : menuBar,
364 			      XtRString, &from,
365 #ifdef FONTSET_ALWAYS
366 			      XtRFontSet, &to
367 #else
368 			      XtRFontStruct, &to
369 #endif
370 		    ) == False)
371 	    return None;
372 	/* "font" should now contain data */
373     }
374     else
375 #ifdef FONTSET_ALWAYS
376 	font = (XFontSet)gui.menu_fontset;
377 #else
378 	font = (XFontStruct *)gui.menu_font;
379 #endif
380 
381     {
382 	int	    width, height;
383 	GC	    draw_gc, undraw_gc;
384 	XGCValues   gc_values;
385 	XPoint	    points[3];
386 
387 #ifdef FONTSET_ALWAYS
388 	height = fontset_height2(font);
389 #else
390 	height = font->max_bounds.ascent + font->max_bounds.descent;
391 #endif
392 	width = height - 2;
393 	puller_width = width + 4;
394 	retval = XCreatePixmap(gui.dpy,DefaultRootWindow(gui.dpy),width,
395 			       height, 1);
396 	gc_values.foreground = 1;
397 	gc_values.background = 0;
398 	draw_gc = XCreateGC(gui.dpy, retval,
399 		    GCForeground | GCBackground,
400 		    &gc_values);
401 	gc_values.foreground = 0;
402 	gc_values.background = 1;
403 	undraw_gc = XCreateGC(gui.dpy, retval,
404 			      GCForeground | GCBackground,
405 			      &gc_values);
406 	points[0].x = 0;
407 	points[0].y = 0;
408 	points[1].x = width - 1;
409 	points[1].y = (height - 1) / 2;
410 	points[2].x = 0;
411 	points[2].y = height - 1;
412 	XFillRectangle(gui.dpy, retval, undraw_gc, 0, 0, height, height);
413 	XFillPolygon(gui.dpy, retval, draw_gc, points, XtNumber(points),
414 		     Convex, CoordModeOrigin);
415 	XFreeGC(gui.dpy, draw_gc);
416 	XFreeGC(gui.dpy, undraw_gc);
417     }
418     return retval;
419 }
420 #endif
421 
422 /*
423  * Called when the GUI is not going to start after all.
424  */
425     void
426 gui_x11_destroy_widgets(void)
427 {
428     textArea = NULL;
429 #ifdef FEAT_MENU
430     menuBar = NULL;
431 #endif
432 #ifdef FEAT_TOOLBAR
433     toolBar = NULL;
434 #endif
435 }
436 
437 #if defined(FEAT_TOOLBAR) || defined(PROTO)
438 # include "gui_x11_pm.h"
439 # ifdef HAVE_X11_XPM_H
440 #  include <X11/xpm.h>
441 # endif
442 
443 static void createXpmImages(char_u *path, char **xpm, Pixmap *sen);
444 
445 /*
446  * Allocated a pixmap for toolbar menu "menu".
447  * Return in "sen".
448  */
449     static void
450 get_toolbar_pixmap(vimmenu_T *menu, Pixmap *sen)
451 {
452     char_u	buf[MAXPATHL];		/* buffer storing expanded pathname */
453     char	**xpm = NULL;		/* xpm array */
454 
455     buf[0] = NUL;			/* start with NULL path */
456 
457     if (menu->iconfile != NULL)
458     {
459 	/* Use the "icon="  argument. */
460 	gui_find_iconfile(menu->iconfile, buf, "xpm");
461 	createXpmImages(buf, NULL, sen);
462 
463 	/* If it failed, try using the menu name. */
464 	if (*sen == (Pixmap)0 && gui_find_bitmap(menu->name, buf, "xpm") == OK)
465 	    createXpmImages(buf, NULL, sen);
466 	if (*sen != (Pixmap)0)
467 	    return;
468     }
469 
470     if (menu->icon_builtin || gui_find_bitmap(menu->name, buf, "xpm") == FAIL)
471     {
472 	if (menu->iconidx >= 0 && menu->iconidx
473 	      < (int)(sizeof(built_in_pixmaps) / sizeof(built_in_pixmaps[0])))
474 	    xpm = built_in_pixmaps[menu->iconidx];
475 	else
476 	    xpm = tb_blank_xpm;
477     }
478 
479     if (xpm != NULL || buf[0] != NUL)
480 	createXpmImages(buf, xpm, sen);
481 }
482 
483 /*
484  * Read an Xpm file, doing color substitutions for the foreground and
485  * background colors. If there is an error reading a color xpm file,
486  * drop back and read the monochrome file. If successful, create the
487  * insensitive Pixmap too.
488  */
489     static void
490 createXpmImages(char_u *path, char **xpm, Pixmap *sen)
491 {
492     Window	rootWindow;
493     XpmAttributes attrs;
494     XpmColorSymbol color[5] =
495     {
496 	{"none", "none", 0},
497 	{"iconColor1", NULL, 0},
498 	{"bottomShadowColor", NULL, 0},
499 	{"topShadowColor", NULL, 0},
500 	{"selectColor", NULL, 0}
501     };
502     int		screenNum;
503     int		status;
504     Pixmap	mask;
505     Pixmap	map;
506 
507     gui_mch_get_toolbar_colors(
508 	    &color[BACKGROUND].pixel,
509 	    &color[FOREGROUND].pixel,
510 	    &color[BOTTOM_SHADOW].pixel,
511 	    &color[TOP_SHADOW].pixel,
512 	    &color[HIGHLIGHT].pixel);
513 
514     /* Setup the color substitution table */
515     attrs.valuemask = XpmColorSymbols;
516     attrs.colorsymbols = color;
517     attrs.numsymbols = 5;
518 
519     screenNum = DefaultScreen(gui.dpy);
520     rootWindow = RootWindow(gui.dpy, screenNum);
521 
522     /* Create the "sensitive" pixmap */
523     if (xpm != NULL)
524 	status = XpmCreatePixmapFromData(gui.dpy, rootWindow, xpm,
525 							 &map, &mask, &attrs);
526     else
527 	status = XpmReadFileToPixmap(gui.dpy, rootWindow, (char *)path,
528 							 &map, &mask, &attrs);
529     if (status == XpmSuccess && map != 0)
530     {
531 	XGCValues   gcvalues;
532 	GC	    back_gc;
533 	GC	    mask_gc;
534 
535 	/* Need to create new Pixmaps with the mask applied. */
536 	gcvalues.foreground = color[BACKGROUND].pixel;
537 	back_gc = XCreateGC(gui.dpy, map, GCForeground, &gcvalues);
538 	mask_gc = XCreateGC(gui.dpy, map, GCForeground, &gcvalues);
539 	XSetClipMask(gui.dpy, mask_gc, mask);
540 
541 	/* Create the "sensitive" pixmap. */
542 	*sen = XCreatePixmap(gui.dpy, rootWindow,
543 		 attrs.width, attrs.height,
544 		 DefaultDepth(gui.dpy, screenNum));
545 	XFillRectangle(gui.dpy, *sen, back_gc, 0, 0,
546 		attrs.width, attrs.height);
547 	XCopyArea(gui.dpy, map, *sen, mask_gc, 0, 0,
548 		attrs.width, attrs.height, 0, 0);
549 
550 	XFreeGC(gui.dpy, back_gc);
551 	XFreeGC(gui.dpy, mask_gc);
552 	XFreePixmap(gui.dpy, map);
553     }
554     else
555 	*sen = 0;
556 
557     XpmFreeAttributes(&attrs);
558 }
559 
560     void
561 gui_mch_set_toolbar_pos(
562     int	    x,
563     int	    y,
564     int	    w,
565     int	    h)
566 {
567     Dimension	border;
568     int		height;
569 
570     if (!XtIsManaged(toolBar))	/* nothing to do */
571 	return;
572     XtUnmanageChild(toolBar);
573     XtVaGetValues(toolBar,
574 		XtNborderWidth, &border,
575 		NULL);
576     height = h - 2 * border;
577     if (height < 0)
578 	height = 1;
579     XtVaSetValues(toolBar,
580 		  XtNhorizDistance, x,
581 		  XtNvertDistance, y,
582 		  XtNwidth, w - 2 * border,
583 		  XtNheight,	height,
584 		  NULL);
585     XtManageChild(toolBar);
586 }
587 #endif
588 
589     void
590 gui_mch_set_text_area_pos(
591     int	    x,
592     int	    y,
593     int	    w,
594     int	    h)
595 {
596     XtUnmanageChild(textArea);
597     XtVaSetValues(textArea,
598 		  XtNhorizDistance, x,
599 		  XtNvertDistance, y,
600 		  XtNwidth, w,
601 		  XtNheight, h,
602 		  NULL);
603     XtManageChild(textArea);
604 #ifdef FEAT_TOOLBAR
605     /* Give keyboard focus to the textArea instead of the toolbar. */
606     gui_mch_reset_focus();
607 #endif
608 }
609 
610 #ifdef FEAT_TOOLBAR
611 /*
612  * A toolbar button has been pushed; now reset the input focus
613  * such that the user can type page up/down etc. and have the
614  * input go to the editor window, not the button
615  */
616     static void
617 gui_mch_reset_focus(void)
618 {
619     XtSetKeyboardFocus(vimForm, textArea);
620 }
621 #endif
622 
623 
624     void
625 gui_x11_set_back_color(void)
626 {
627     if (textArea != NULL)
628 	XtVaSetValues(textArea,
629 		  XtNbackground, gui.back_pixel,
630 		  NULL);
631 }
632 
633 #if defined(FEAT_MENU) || defined(PROTO)
634 /*
635  * Menu stuff.
636  */
637 
638 static char_u	*make_pull_name(char_u * name);
639 static Widget	get_popup_entry(Widget w);
640 static Widget	submenu_widget(Widget);
641 static Boolean	has_submenu(Widget);
642 static void gui_mch_submenu_change(vimmenu_T *mp, int colors);
643 static void gui_athena_menu_font(Widget id);
644 
645     void
646 gui_mch_enable_menu(int flag)
647 {
648     if (flag)
649     {
650 	XtManageChild(menuBar);
651 # ifdef FEAT_TOOLBAR
652 	if (XtIsManaged(toolBar))
653 	{
654 	    XtVaSetValues(toolBar,
655 		XtNvertDistance,    gui.menu_height,
656 		NULL);
657 	    XtVaSetValues(textArea,
658 		XtNvertDistance,    gui.menu_height + gui.toolbar_height,
659 		NULL);
660 	}
661 # endif
662     }
663     else
664     {
665 	XtUnmanageChild(menuBar);
666 # ifdef FEAT_TOOLBAR
667 	if (XtIsManaged(toolBar))
668 	{
669 	    XtVaSetValues(toolBar,
670 		XtNvertDistance,    0,
671 		NULL);
672 	}
673 # endif
674     }
675 }
676 
677     void
678 gui_mch_set_menu_pos(
679     int	    x,
680     int	    y,
681     int	    w,
682     int	    h)
683 {
684     Dimension	border;
685     int		height;
686 
687     XtUnmanageChild(menuBar);
688     XtVaGetValues(menuBar, XtNborderWidth, &border, NULL);
689     /* avoid trouble when there are no menu items, and h is 1 */
690     height = h - 2 * border;
691     if (height < 0)
692 	height = 1;
693     XtVaSetValues(menuBar,
694 		XtNhorizDistance, x,
695 		XtNvertDistance, y,
696 		XtNwidth, w - 2 * border,
697 		XtNheight, height,
698 		NULL);
699     XtManageChild(menuBar);
700 }
701 
702 /*
703  * Used to calculate the insertion position of a widget with respect to its
704  * neighbors.
705  *
706  * Valid range of return values is: 0 (beginning of children) to
707  *				    numChildren (end of children).
708  */
709     static Cardinal
710 athena_calculate_ins_pos(Widget widget)
711 {
712     /* Assume that if the parent of the vimmenu_T is NULL, then we can get
713      * to this menu by traversing "next", starting at "root_menu".
714      *
715      * This holds true for popup menus, toolbar, and toplevel menu items.
716      */
717 
718     /* Popup menus:  "id" is NULL. Only submenu_id is valid */
719 
720     /* Menus that are not toplevel: "parent" will be non-NULL, "id" &
721      * "submenu_id" will be non-NULL.
722      */
723 
724     /* Toplevel menus: "parent" is NULL, id is the widget of the menu item */
725 
726     WidgetList	children;
727     Cardinal	num_children = 0;
728     int		retval;
729     Arg		args[2];
730     int		n = 0;
731     int		i;
732 
733     XtSetArg(args[n], XtNchildren, &children); n++;
734     XtSetArg(args[n], XtNnumChildren, &num_children); n++;
735     XtGetValues(XtParent(widget), args, n);
736 
737     retval = num_children;
738     for (i = 0; i < (int)num_children; ++i)
739     {
740 	Widget	current = children[i];
741 	vimmenu_T	*menu = NULL;
742 
743 	for (menu = (a_cur_menu->parent == NULL)
744 			       ? root_menu : a_cur_menu->parent->children;
745 			       menu != NULL;
746 			       menu = menu->next)
747 	    if (current == menu->id
748 		    && a_cur_menu->priority < menu->priority
749 		    && i < retval)
750 		retval = i;
751     }
752     return retval;
753 }
754 
755     void
756 gui_mch_add_menu(vimmenu_T *menu, int idx UNUSED)
757 {
758     char_u	*pullright_name;
759     Dimension	height, space, border;
760     vimmenu_T	*parent = menu->parent;
761 
762     a_cur_menu = menu;
763     if (parent == NULL)
764     {
765 	if (menu_is_popup(menu->dname))
766 	{
767 	    menu->submenu_id = XtVaCreatePopupShell((char *)menu->dname,
768 		simpleMenuWidgetClass,	vimShell,
769 		XtNinsertPosition,	athena_calculate_ins_pos,
770 		XtNtranslations,	popupTrans,
771 		NULL);
772 	    gui_athena_menu_colors(menu->submenu_id);
773 	}
774 	else if (menu_is_menubar(menu->dname))
775 	{
776 	    menu->id = XtVaCreateManagedWidget((char *)menu->dname,
777 		menuButtonWidgetClass, menuBar,
778 		XtNmenuName,	    menu->dname,
779 #ifdef FONTSET_ALWAYS
780 		XtNinternational,   True,
781 #endif
782 		NULL);
783 	    if (menu->id == (Widget)0)
784 		return;
785 	    gui_athena_menu_colors(menu->id);
786 	    gui_athena_menu_font(menu->id);
787 
788 	    menu->submenu_id = XtVaCreatePopupShell((char *)menu->dname,
789 		simpleMenuWidgetClass, menu->id,
790 		XtNinsertPosition,	athena_calculate_ins_pos,
791 		XtNtranslations,	supermenuTrans,
792 		NULL);
793 	    gui_athena_menu_colors(menu->submenu_id);
794 	    gui_athena_menu_font(menu->submenu_id);
795 
796 	    /* Don't update the menu height when it was set at a fixed value */
797 	    if (!gui.menu_height_fixed)
798 	    {
799 		/*
800 		 * When we add a top-level item to the menu bar, we can figure
801 		 * out how high the menu bar should be.
802 		 */
803 		XtVaGetValues(menuBar,
804 			XtNvSpace,	&space,
805 			XtNborderWidth, &border,
806 			NULL);
807 		XtVaGetValues(menu->id,
808 			XtNheight,	&height,
809 			NULL);
810 		gui.menu_height = height + 2 * (space + border);
811 	    }
812 	}
813     }
814     else if (parent->submenu_id != (Widget)0)
815     {
816 	menu->id = XtVaCreateManagedWidget((char *)menu->dname,
817 	    smeBSBObjectClass, parent->submenu_id,
818 	    XtNlabel, menu->dname,
819 #ifdef FONTSET_ALWAYS
820 	    XtNinternational,	True,
821 #endif
822 	    NULL);
823 	if (menu->id == (Widget)0)
824 	    return;
825 	if (pullerBitmap == None)
826 	    pullerBitmap = gui_athena_create_pullright_pixmap(menu->id);
827 
828 	XtVaSetValues(menu->id, XtNrightBitmap, pullerBitmap,
829 				NULL);
830 	/* If there are other menu items that are not pulldown menus,
831 	 * we need to adjust the right margins of those, too.
832 	 */
833 	{
834 	    WidgetList	children;
835 	    Cardinal	num_children;
836 	    int		i;
837 
838 	    XtVaGetValues(parent->submenu_id, XtNchildren, &children,
839 					      XtNnumChildren, &num_children,
840 					      NULL);
841 	    for (i = 0; i < (int)num_children; ++i)
842 	    {
843 		XtVaSetValues(children[i],
844 			      XtNrightMargin, puller_width,
845 			      NULL);
846 	    }
847 	}
848 	gui_athena_menu_colors(menu->id);
849 	gui_athena_menu_font(menu->id);
850 
851 	pullright_name = make_pull_name(menu->dname);
852 	menu->submenu_id = XtVaCreatePopupShell((char *)pullright_name,
853 	    simpleMenuWidgetClass, parent->submenu_id,
854 	    XtNtranslations, menuTrans,
855 	    NULL);
856 	gui_athena_menu_colors(menu->submenu_id);
857 	gui_athena_menu_font(menu->submenu_id);
858 	vim_free(pullright_name);
859 	XtAddCallback(menu->submenu_id, XtNpopupCallback,
860 		      gui_athena_popup_callback, (XtPointer)menu);
861 
862 	if (parent->parent != NULL)
863 	    XtOverrideTranslations(parent->submenu_id, parentTrans);
864     }
865     a_cur_menu = NULL;
866 }
867 
868 /* Used to determine whether a SimpleMenu has pulldown entries.
869  *
870  * "id" is the parent of the menu items.
871  * Ignore widget "ignore" in the pane.
872  */
873     static Boolean
874 gui_athena_menu_has_submenus(Widget id, Widget ignore)
875 {
876     WidgetList	children;
877     Cardinal	num_children;
878     int		i;
879 
880     XtVaGetValues(id, XtNchildren, &children,
881 		      XtNnumChildren, &num_children,
882 		      NULL);
883     for (i = 0; i < (int)num_children; ++i)
884     {
885 	if (children[i] == ignore)
886 	    continue;
887 	if (has_submenu(children[i]))
888 	    return True;
889     }
890     return False;
891 }
892 
893     static void
894 gui_athena_menu_font(Widget id)
895 {
896 #ifdef FONTSET_ALWAYS
897     if (gui.menu_fontset != NOFONTSET)
898     {
899 	if (XtIsManaged(id))
900 	{
901 	    XtUnmanageChild(id);
902 	    XtVaSetValues(id, XtNfontSet, gui.menu_fontset, NULL);
903 	    /* We should force the widget to recalculate its
904 	     * geometry now. */
905 	    XtManageChild(id);
906 	}
907 	else
908 	    XtVaSetValues(id, XtNfontSet, gui.menu_fontset, NULL);
909 	if (has_submenu(id))
910 	    XtVaSetValues(id, XtNrightBitmap, pullerBitmap, NULL);
911     }
912 #else
913     int		managed = FALSE;
914 
915     if (gui.menu_font != NOFONT)
916     {
917 	if (XtIsManaged(id))
918 	{
919 	    XtUnmanageChild(id);
920 	    managed = TRUE;
921 	}
922 
923 # ifdef FEAT_XFONTSET
924 	if (gui.fontset != NOFONTSET)
925 	    XtVaSetValues(id, XtNfontSet, gui.menu_font, NULL);
926 	else
927 # endif
928 	    XtVaSetValues(id, XtNfont, gui.menu_font, NULL);
929 	if (has_submenu(id))
930 	    XtVaSetValues(id, XtNrightBitmap, pullerBitmap, NULL);
931 
932 	/* Force the widget to recalculate its geometry now. */
933 	if (managed)
934 	    XtManageChild(id);
935     }
936 #endif
937 }
938 
939 
940     void
941 gui_mch_new_menu_font(void)
942 {
943     Pixmap oldpuller = None;
944 
945     if (menuBar == (Widget)0)
946 	return;
947 
948     if (pullerBitmap != None)
949     {
950 	oldpuller = pullerBitmap;
951 	pullerBitmap = gui_athena_create_pullright_pixmap(NULL);
952     }
953     gui_mch_submenu_change(root_menu, FALSE);
954 
955     {
956 	/* Iterate through the menubar menu items and get the height of
957 	 * each one.  The menu bar height is set to the maximum of all
958 	 * the heights.
959 	 */
960 	vimmenu_T *mp;
961 	int max_height = 9999;
962 
963 	for (mp = root_menu; mp != NULL; mp = mp->next)
964 	{
965 	    if (menu_is_menubar(mp->dname))
966 	    {
967 		Dimension height;
968 
969 		XtVaGetValues(mp->id,
970 			XtNheight, &height,
971 			NULL);
972 		if (height < max_height)
973 		    max_height = height;
974 	    }
975 	}
976 	if (max_height != 9999)
977 	{
978 	    /* Don't update the menu height when it was set at a fixed value */
979 	    if (!gui.menu_height_fixed)
980 	    {
981 		Dimension   space, border;
982 
983 		XtVaGetValues(menuBar,
984 			XtNvSpace,	&space,
985 			XtNborderWidth, &border,
986 			NULL);
987 		gui.menu_height = max_height + 2 * (space + border);
988 	    }
989 	}
990     }
991     /* Now, to simulate the window being resized.  Only, this
992      * will resize the window to its current state.
993      *
994      * There has to be a better way, but I do not see one at this time.
995      * (David Harrison)
996      */
997     {
998 	Position w, h;
999 
1000 	XtVaGetValues(vimShell,
1001 		XtNwidth, &w,
1002 		XtNheight, &h,
1003 		NULL);
1004 	gui_resize_shell(w, h
1005 #ifdef FEAT_XIM
1006 						- xim_get_status_area_height()
1007 #endif
1008 		     );
1009     }
1010     gui_set_shellsize(FALSE, TRUE, RESIZE_VERT);
1011     ui_new_shellsize();
1012     if (oldpuller != None)
1013 	XFreePixmap(gui.dpy, oldpuller);
1014 }
1015 
1016 #if defined(FEAT_BEVAL_GUI) || defined(PROTO)
1017     void
1018 gui_mch_new_tooltip_font(void)
1019 {
1020 #  ifdef FEAT_TOOLBAR
1021     vimmenu_T   *menu;
1022 
1023     if (toolBar == (Widget)0)
1024 	return;
1025 
1026     menu = gui_find_menu((char_u *)"ToolBar");
1027     if (menu != NULL)
1028 	gui_mch_submenu_change(menu, FALSE);
1029 #  endif
1030 }
1031 
1032     void
1033 gui_mch_new_tooltip_colors(void)
1034 {
1035 # ifdef FEAT_TOOLBAR
1036     vimmenu_T   *menu;
1037 
1038     if (toolBar == (Widget)0)
1039 	return;
1040 
1041     menu = gui_find_menu((char_u *)"ToolBar");
1042     if (menu != NULL)
1043 	gui_mch_submenu_change(menu, TRUE);
1044 # endif
1045 }
1046 #endif
1047 
1048     static void
1049 gui_mch_submenu_change(
1050     vimmenu_T	*menu,
1051     int		colors)		/* TRUE for colors, FALSE for font */
1052 {
1053     vimmenu_T	*mp;
1054 
1055     for (mp = menu; mp != NULL; mp = mp->next)
1056     {
1057 	if (mp->id != (Widget)0)
1058 	{
1059 	    if (colors)
1060 	    {
1061 		gui_athena_menu_colors(mp->id);
1062 #ifdef FEAT_TOOLBAR
1063 		/* For a toolbar item: Free the pixmap and allocate a new one,
1064 		 * so that the background color is right. */
1065 		if (mp->image != (Pixmap)0)
1066 		{
1067 		    XFreePixmap(gui.dpy, mp->image);
1068 		    get_toolbar_pixmap(mp, &mp->image);
1069 		    if (mp->image != (Pixmap)0)
1070 			XtVaSetValues(mp->id, XtNbitmap, mp->image, NULL);
1071 		}
1072 
1073 # ifdef FEAT_BEVAL_GUI
1074 		/* If we have a tooltip, then we need to change its colors */
1075 		if (mp->tip != NULL)
1076 		{
1077 		    Arg args[2];
1078 
1079 		    args[0].name = XtNbackground;
1080 		    args[0].value = gui.tooltip_bg_pixel;
1081 		    args[1].name = XtNforeground;
1082 		    args[1].value = gui.tooltip_fg_pixel;
1083 		    XtSetValues(mp->tip->balloonLabel, &args[0], XtNumber(args));
1084 		}
1085 # endif
1086 #endif
1087 	    }
1088 	    else
1089 	    {
1090 		gui_athena_menu_font(mp->id);
1091 #ifdef FEAT_BEVAL_GUI
1092 		/* If we have a tooltip, then we need to change its font */
1093 		/* Assume XtNinternational == True (in createBalloonEvalWindow)
1094 		 */
1095 		if (mp->tip != NULL)
1096 		{
1097 		    Arg args[1];
1098 
1099 		    args[0].name = XtNfontSet;
1100 		    args[0].value = (XtArgVal)gui.tooltip_fontset;
1101 		    XtSetValues(mp->tip->balloonLabel, &args[0], XtNumber(args));
1102 		}
1103 #endif
1104 	    }
1105 	}
1106 
1107 	if (mp->children != NULL)
1108 	{
1109 	    /* Set the colors/font for the tear off widget */
1110 	    if (mp->submenu_id != (Widget)0)
1111 	    {
1112 		if (colors)
1113 		    gui_athena_menu_colors(mp->submenu_id);
1114 		else
1115 		    gui_athena_menu_font(mp->submenu_id);
1116 	    }
1117 	    /* Set the colors for the children */
1118 	    gui_mch_submenu_change(mp->children, colors);
1119 	}
1120     }
1121 }
1122 
1123 /*
1124  * Make a submenu name into a pullright name.
1125  * Replace '.' by '_', can't include '.' in the submenu name.
1126  */
1127     static char_u *
1128 make_pull_name(char_u * name)
1129 {
1130     char_u  *pname;
1131     char_u  *p;
1132 
1133     pname = vim_strnsave(name, STRLEN(name) + strlen("-pullright"));
1134     if (pname != NULL)
1135     {
1136 	strcat((char *)pname, "-pullright");
1137 	while ((p = vim_strchr(pname, '.')) != NULL)
1138 	    *p = '_';
1139     }
1140     return pname;
1141 }
1142 
1143     void
1144 gui_mch_add_menu_item(vimmenu_T *menu, int idx UNUSED)
1145 {
1146     vimmenu_T	*parent = menu->parent;
1147 
1148     a_cur_menu = menu;
1149 # ifdef FEAT_TOOLBAR
1150     if (menu_is_toolbar(parent->name))
1151     {
1152 	WidgetClass	type;
1153 	int		n;
1154 	Arg		args[21];
1155 
1156 	n = 0;
1157 	if (menu_is_separator(menu->name))
1158 	{
1159 	    XtSetArg(args[n], XtNlabel, ""); n++;
1160 	    XtSetArg(args[n], XtNborderWidth, 0); n++;
1161 	}
1162 	else
1163 	{
1164 	    get_toolbar_pixmap(menu, &menu->image);
1165 	    XtSetArg(args[n], XtNlabel, menu->dname); n++;
1166 	    XtSetArg(args[n], XtNinternalHeight, 1); n++;
1167 	    XtSetArg(args[n], XtNinternalWidth, 1); n++;
1168 	    XtSetArg(args[n], XtNborderWidth, 1); n++;
1169 	    if (menu->image != 0)
1170 		XtSetArg(args[n], XtNbitmap, menu->image); n++;
1171 	}
1172 	XtSetArg(args[n], XtNhighlightThickness, 0); n++;
1173 	type = commandWidgetClass;
1174 	/* TODO: figure out the position in the toolbar?
1175 	 *       This currently works fine for the default toolbar, but
1176 	 *       what if we add/remove items during later runtime?
1177 	 */
1178 
1179 	/* NOTE: "idx" isn't used here.  The position is calculated by
1180 	 *       athena_calculate_ins_pos().  The position it calculates
1181 	 *       should be equal to "idx".
1182 	 */
1183 	/* TODO: Could we just store "idx" and use that as the child
1184 	 * placement?
1185 	 */
1186 
1187 	if (menu->id == NULL)
1188 	{
1189 	    menu->id = XtCreateManagedWidget((char *)menu->dname,
1190 			type, toolBar, args, n);
1191 	    XtAddCallback(menu->id,
1192 		    XtNcallback, gui_x11_menu_cb, menu);
1193 	}
1194 	else
1195 	    XtSetValues(menu->id, args, n);
1196 	gui_athena_menu_colors(menu->id);
1197 
1198 #ifdef FEAT_BEVAL_GUI
1199 	gui_mch_menu_set_tip(menu);
1200 #endif
1201 
1202 	menu->parent = parent;
1203 	menu->submenu_id = NULL;
1204 	if (!XtIsManaged(toolBar)
1205 		    && vim_strchr(p_go, GO_TOOLBAR) != NULL)
1206 	    gui_mch_show_toolbar(TRUE);
1207 	gui.toolbar_height = gui_mch_compute_toolbar_height();
1208 	return;
1209     } /* toolbar menu item */
1210 # endif
1211 
1212     /* Add menu separator */
1213     if (menu_is_separator(menu->name))
1214     {
1215 	menu->submenu_id = (Widget)0;
1216 	menu->id = XtVaCreateManagedWidget((char *)menu->dname,
1217 		smeLineObjectClass, parent->submenu_id,
1218 		NULL);
1219 	if (menu->id == (Widget)0)
1220 	    return;
1221 	gui_athena_menu_colors(menu->id);
1222     }
1223     else
1224     {
1225 	if (parent != NULL && parent->submenu_id != (Widget)0)
1226 	{
1227 	    menu->submenu_id = (Widget)0;
1228 	    menu->id = XtVaCreateManagedWidget((char *)menu->dname,
1229 		    smeBSBObjectClass, parent->submenu_id,
1230 		    XtNlabel, menu->dname,
1231 #ifdef FONTSET_ALWAYS
1232 		    XtNinternational,	True,
1233 #endif
1234 		    NULL);
1235 	    if (menu->id == (Widget)0)
1236 		return;
1237 
1238 	    /* If there are other "pulldown" items in this pane, then adjust
1239 	     * the right margin to accommodate the arrow pixmap, otherwise
1240 	     * the right margin will be the same as the left margin.
1241 	     */
1242 	    {
1243 		Dimension   left_margin;
1244 
1245 		XtVaGetValues(menu->id, XtNleftMargin, &left_margin, NULL);
1246 		XtVaSetValues(menu->id, XtNrightMargin,
1247 			gui_athena_menu_has_submenus(parent->submenu_id, NULL) ?
1248 			    puller_width :
1249 			    left_margin,
1250 			NULL);
1251 	    }
1252 
1253 	    gui_athena_menu_colors(menu->id);
1254 	    gui_athena_menu_font(menu->id);
1255 	    XtAddCallback(menu->id, XtNcallback, gui_x11_menu_cb,
1256 		    (XtPointer)menu);
1257 	}
1258     }
1259     a_cur_menu = NULL;
1260 }
1261 
1262 #if defined(FEAT_TOOLBAR) || defined(PROTO)
1263     void
1264 gui_mch_show_toolbar(int showit)
1265 {
1266     Cardinal	numChildren;	    /* how many children toolBar has */
1267 
1268     if (toolBar == (Widget)0)
1269 	return;
1270     XtVaGetValues(toolBar, XtNnumChildren, &numChildren, NULL);
1271     if (showit && numChildren > 0)
1272     {
1273 	/* Assume that we want to show the toolbar if p_toolbar contains valid
1274 	 * option settings, therefore p_toolbar must not be NULL.
1275 	 */
1276 	WidgetList  children;
1277 
1278 	XtVaGetValues(toolBar, XtNchildren, &children, NULL);
1279 	{
1280 	    void    (*action)(BalloonEval *);
1281 	    int	    text = 0;
1282 
1283 	    if (strstr((const char *)p_toolbar, "tooltips"))
1284 		action = &gui_mch_enable_beval_area;
1285 	    else
1286 		action = &gui_mch_disable_beval_area;
1287 	    if (strstr((const char *)p_toolbar, "text"))
1288 		text = 1;
1289 	    else if (strstr((const char *)p_toolbar, "icons"))
1290 		text = -1;
1291 	    if (text != 0)
1292 	    {
1293 		vimmenu_T   *toolbar;
1294 		vimmenu_T   *cur;
1295 
1296 		for (toolbar = root_menu; toolbar; toolbar = toolbar->next)
1297 		    if (menu_is_toolbar(toolbar->dname))
1298 			break;
1299 		/* Assumption: toolbar is NULL if there is no toolbar,
1300 		 *	       otherwise it contains the toolbar menu structure.
1301 		 *
1302 		 * Assumption: "numChildren" == the number of items in the list
1303 		 *	       of items beginning with toolbar->children.
1304 		 */
1305 		if (toolbar)
1306 		{
1307 		    for (cur = toolbar->children; cur; cur = cur->next)
1308 		    {
1309 			Arg	    args[2];
1310 			int	    n = 0;
1311 
1312 			/* Enable/Disable tooltip (OK to enable while currently
1313 			 * enabled)
1314 			 */
1315 			if (cur->tip != NULL)
1316 			    (*action)(cur->tip);
1317 			if (text == 1)
1318 			{
1319 			    XtSetArg(args[n], XtNbitmap, None);
1320 			    n++;
1321 			    XtSetArg(args[n], XtNlabel,
1322 				    menu_is_separator(cur->name) ? "" :
1323 					(char *)cur->dname);
1324 			    n++;
1325 			}
1326 			else
1327 			{
1328 			    XtSetArg(args[n], XtNbitmap, cur->image);
1329 			    n++;
1330 			    XtSetArg(args[n], XtNlabel, (cur->image == None) ?
1331 				    menu_is_separator(cur->name) ?
1332 					"" :
1333 					(char *)cur->dname
1334 				    :
1335 				    (char *)None);
1336 			    n++;
1337 			}
1338 			if (cur->id != NULL)
1339 			{
1340 			    XtUnmanageChild(cur->id);
1341 			    XtSetValues(cur->id, args, n);
1342 			    XtManageChild(cur->id);
1343 			}
1344 		    }
1345 		}
1346 	    }
1347 	}
1348 	gui.toolbar_height = gui_mch_compute_toolbar_height();
1349 	XtManageChild(toolBar);
1350 	if (XtIsManaged(menuBar))
1351 	{
1352 	    XtVaSetValues(textArea,
1353 		    XtNvertDistance,    gui.toolbar_height + gui.menu_height,
1354 		    NULL);
1355 	    XtVaSetValues(toolBar,
1356 		    XtNvertDistance,    gui.menu_height,
1357 		    NULL);
1358 	}
1359 	else
1360 	{
1361 	    XtVaSetValues(textArea,
1362 		    XtNvertDistance,    gui.toolbar_height,
1363 		    NULL);
1364 	    XtVaSetValues(toolBar,
1365 		    XtNvertDistance,    0,
1366 		    NULL);
1367 	}
1368     }
1369     else
1370     {
1371 	gui.toolbar_height = 0;
1372 	if (XtIsManaged(menuBar))
1373 	    XtVaSetValues(textArea,
1374 		XtNvertDistance,    gui.menu_height,
1375 		NULL);
1376 	else
1377 	    XtVaSetValues(textArea,
1378 		XtNvertDistance,    0,
1379 		NULL);
1380 
1381 	XtUnmanageChild(toolBar);
1382     }
1383     gui_set_shellsize(FALSE, FALSE, RESIZE_VERT);
1384 }
1385 
1386 
1387     int
1388 gui_mch_compute_toolbar_height(void)
1389 {
1390     Dimension	height;		    /* total Toolbar height */
1391     Dimension	whgt;		    /* height of each widget */
1392     Dimension	marginHeight;	    /* XmNmarginHeight of toolBar */
1393     Dimension	shadowThickness;    /* thickness of Xtparent(toolBar) */
1394     WidgetList	children;	    /* list of toolBar's children */
1395     Cardinal	numChildren;	    /* how many children toolBar has */
1396     int		i;
1397 
1398     height = 0;
1399     shadowThickness = 0;
1400     marginHeight = 0;
1401     if (toolBar != (Widget)0)
1402     {
1403 	XtVaGetValues(toolBar,
1404 		XtNborderWidth,	    &shadowThickness,
1405 		XtNvSpace,	    &marginHeight,
1406 		XtNchildren,	    &children,
1407 		XtNnumChildren,	    &numChildren,
1408 		NULL);
1409 	for (i = 0; i < (int)numChildren; i++)
1410 	{
1411 	    whgt = 0;
1412 
1413 	    XtVaGetValues(children[i], XtNheight, &whgt, NULL);
1414 	    if (height < whgt)
1415 		height = whgt;
1416 	}
1417     }
1418 
1419     return (int)(height + (marginHeight << 1) + (shadowThickness << 1));
1420 }
1421 
1422     void
1423 gui_mch_get_toolbar_colors(
1424     Pixel	*bgp,
1425     Pixel	*fgp,
1426     Pixel       *bsp,
1427     Pixel	*tsp,
1428     Pixel	*hsp)
1429 {
1430     XtVaGetValues(toolBar, XtNbackground, bgp, XtNborderColor, fgp, NULL);
1431     *bsp = *bgp;
1432     *tsp = *fgp;
1433     *hsp = *tsp;
1434 }
1435 #endif
1436 
1437 
1438     void
1439 gui_mch_toggle_tearoffs(int enable UNUSED)
1440 {
1441     /* no tearoff menus */
1442 }
1443 
1444     void
1445 gui_mch_new_menu_colors(void)
1446 {
1447     if (menuBar == (Widget)0)
1448 	return;
1449     if (gui.menu_fg_pixel != INVALCOLOR)
1450 	XtVaSetValues(menuBar, XtNborderColor,	gui.menu_fg_pixel, NULL);
1451     gui_athena_menu_colors(menuBar);
1452 #ifdef FEAT_TOOLBAR
1453     gui_athena_menu_colors(toolBar);
1454 #endif
1455 
1456     gui_mch_submenu_change(root_menu, TRUE);
1457 }
1458 
1459 /*
1460  * Destroy the machine specific menu widget.
1461  */
1462     void
1463 gui_mch_destroy_menu(vimmenu_T *menu)
1464 {
1465     Widget	parent;
1466 
1467     /* There is no item for the toolbar. */
1468     if (menu->id == (Widget)0)
1469 	return;
1470 
1471     parent = XtParent(menu->id);
1472 
1473     /* When removing the last "pulldown" menu item from a pane, adjust the
1474      * right margins of the remaining widgets.
1475      */
1476     if (menu->submenu_id != (Widget)0)
1477     {
1478 	/* Go through the menu items in the parent of this item and
1479 	 * adjust their margins, if necessary.
1480 	 * This takes care of the case when we delete the last menu item in a
1481 	 * pane that has a submenu.  In this case, there will be no arrow
1482 	 * pixmaps shown anymore.
1483 	 */
1484 	{
1485 	    WidgetList  children;
1486 	    Cardinal    num_children;
1487 	    int		i;
1488 	    Dimension	right_margin = 0;
1489 	    Boolean	get_left_margin = False;
1490 
1491 	    XtVaGetValues(parent, XtNchildren, &children,
1492 				  XtNnumChildren, &num_children,
1493 				  NULL);
1494 	    if (gui_athena_menu_has_submenus(parent, menu->id))
1495 		right_margin = puller_width;
1496 	    else
1497 		get_left_margin = True;
1498 
1499 	    for (i = 0; i < (int)num_children; ++i)
1500 	    {
1501 		if (children[i] == menu->id)
1502 		    continue;
1503 		if (get_left_margin == True)
1504 		{
1505 		    Dimension left_margin;
1506 
1507 		    XtVaGetValues(children[i], XtNleftMargin, &left_margin,
1508 				  NULL);
1509 		    XtVaSetValues(children[i], XtNrightMargin, left_margin,
1510 				  NULL);
1511 		}
1512 		else
1513 		    XtVaSetValues(children[i], XtNrightMargin, right_margin,
1514 				  NULL);
1515 	    }
1516 	}
1517     }
1518     /* Please be sure to destroy the parent widget first (i.e. menu->id).
1519      *
1520      * This code should be basically identical to that in the file gui_motif.c
1521      * because they are both Xt based.
1522      */
1523     if (menu->id != (Widget)0)
1524     {
1525 	Cardinal    num_children;
1526 	Dimension   height, space, border;
1527 
1528 	XtVaGetValues(menuBar,
1529 		XtNvSpace,	&space,
1530 		XtNborderWidth, &border,
1531 		NULL);
1532 	XtVaGetValues(menu->id,
1533 		XtNheight,	&height,
1534 		NULL);
1535 #if defined(FEAT_TOOLBAR) && defined(FEAT_BEVAL_GUI)
1536 	if (parent == toolBar && menu->tip != NULL)
1537 	{
1538 	    /* We try to destroy this before the actual menu, because there are
1539 	     * callbacks, etc. that will be unregistered during the tooltip
1540 	     * destruction.
1541 	     *
1542 	     * If you call "gui_mch_destroy_beval_area()" after destroying
1543 	     * menu->id, then the tooltip's window will have already been
1544 	     * deallocated by Xt, and unknown behaviour will ensue (probably
1545 	     * a core dump).
1546 	     */
1547 	    gui_mch_destroy_beval_area(menu->tip);
1548 	    menu->tip = NULL;
1549 	}
1550 #endif
1551 	/*
1552 	 * This is a hack to stop the Athena simpleMenuWidget from getting a
1553 	 * BadValue error when a menu's last child is destroyed. We check to
1554 	 * see if this is the last child and if so, don't delete it. The parent
1555 	 * will be deleted soon anyway, and it will delete its children like
1556 	 * all good widgets do.
1557 	 */
1558 	/* NOTE: The cause of the BadValue X Protocol Error is because when the
1559 	 * last child is destroyed, it is first unmanaged, thus causing a
1560 	 * geometry resize request from the parent Shell widget.
1561 	 * Since the Shell widget has no more children, it is resized to have
1562 	 * width/height of 0.  XConfigureWindow() is then called with the
1563 	 * width/height of 0, which generates the BadValue.
1564 	 *
1565 	 * This happens in phase two of the widget destruction process.
1566 	 */
1567 	{
1568 	    if (parent != menuBar
1569 #ifdef FEAT_TOOLBAR
1570 		    && parent != toolBar
1571 #endif
1572 		    )
1573 	    {
1574 		XtVaGetValues(parent, XtNnumChildren, &num_children, NULL);
1575 		if (num_children > 1)
1576 		    XtDestroyWidget(menu->id);
1577 	    }
1578 	    else
1579 		XtDestroyWidget(menu->id);
1580 	    menu->id = (Widget)0;
1581 	}
1582 
1583 	if (parent == menuBar)
1584 	{
1585 	    if (!gui.menu_height_fixed)
1586 		gui.menu_height = height + 2 * (space + border);
1587 	}
1588 #ifdef FEAT_TOOLBAR
1589 	else if (parent == toolBar)
1590 	{
1591 	    /* When removing last toolbar item, don't display the toolbar. */
1592 	    XtVaGetValues(toolBar, XtNnumChildren, &num_children, NULL);
1593 	    if (num_children == 0)
1594 		gui_mch_show_toolbar(FALSE);
1595 	    else
1596 		gui.toolbar_height = gui_mch_compute_toolbar_height();
1597 	}
1598 #endif
1599     }
1600     if (menu->submenu_id != (Widget)0)
1601     {
1602 	XtDestroyWidget(menu->submenu_id);
1603 	menu->submenu_id = (Widget)0;
1604     }
1605 }
1606 
1607     static void
1608 gui_athena_menu_timeout(
1609     XtPointer	    client_data,
1610     XtIntervalId    *id UNUSED)
1611 {
1612     Widget  w = (Widget)client_data;
1613     Widget  popup;
1614 
1615     timer = 0;
1616     if (XtIsSubclass(w,smeBSBObjectClass))
1617     {
1618 	Pixmap p;
1619 
1620 	XtVaGetValues(w, XtNrightBitmap, &p, NULL);
1621 	if ((p != None) && (p != XtUnspecifiedPixmap))
1622 	{
1623 	    /* We are dealing with an item that has a submenu */
1624 	    popup = get_popup_entry(XtParent(w));
1625 	    if (popup == (Widget)0)
1626 		return;
1627 	    XtPopup(popup, XtGrabNonexclusive);
1628 	}
1629     }
1630 }
1631 
1632 /* This routine is used to calculate the position (in screen coordinates)
1633  * where a submenu should appear relative to the menu entry that popped it
1634  * up.  It should appear even with and just slightly to the left of the
1635  * rightmost end of the menu entry that caused the popup.
1636  *
1637  * This is called when XtPopup() is called.
1638  */
1639     static void
1640 gui_athena_popup_callback(
1641     Widget	w,
1642     XtPointer	client_data,
1643     XtPointer	call_data UNUSED)
1644 {
1645     /* Assumption: XtIsSubclass(XtParent(w),simpleMenuWidgetClass) */
1646     vimmenu_T	*menu = (vimmenu_T *)client_data;
1647     Dimension	width;
1648     Position	root_x, root_y;
1649 
1650     /* First, popdown any siblings that may have menus popped up */
1651     {
1652 	vimmenu_T *i;
1653 
1654 	for (i = menu->parent->children; i != NULL; i = i->next)
1655 	{
1656 	    if (i->submenu_id != NULL && XtIsManaged(i->submenu_id))
1657 		XtPopdown(i->submenu_id);
1658 	}
1659     }
1660     XtVaGetValues(XtParent(w),
1661 		  XtNwidth,   &width,
1662 		  NULL);
1663     /* Assumption: XawSimpleMenuGetActiveEntry(XtParent(w)) == menu->id */
1664     /* i.e. This IS the active entry */
1665     XtTranslateCoords(menu->id,width - 5, 0, &root_x, &root_y);
1666     XtVaSetValues(w, XtNx, root_x,
1667 		     XtNy, root_y,
1668 		     NULL);
1669 }
1670 
1671     static void
1672 gui_athena_popdown_submenus_action(
1673     Widget	w,
1674     XEvent	*event,
1675     String	*args,
1676     Cardinal	*nargs)
1677 {
1678     WidgetList	children;
1679     Cardinal	num_children;
1680 
1681     XtVaGetValues(w, XtNchildren, &children,
1682 		     XtNnumChildren, &num_children,
1683 		     NULL);
1684     for (; num_children > 0; --num_children)
1685     {
1686 	Widget child = children[num_children - 1];
1687 
1688 	if (has_submenu(child))
1689 	{
1690 	    Widget temp_w;
1691 
1692 	    temp_w = submenu_widget(child);
1693 	    gui_athena_popdown_submenus_action(temp_w,event,args,nargs);
1694 	    XtPopdown(temp_w);
1695 	}
1696     }
1697 }
1698 
1699 /* Used to determine if the given widget has a submenu that can be popped up. */
1700     static Boolean
1701 has_submenu(Widget widget)
1702 {
1703     if ((widget != NULL) && XtIsSubclass(widget,smeBSBObjectClass))
1704     {
1705 	Pixmap p;
1706 
1707 	XtVaGetValues(widget, XtNrightBitmap, &p, NULL);
1708 	if ((p != None) && (p != XtUnspecifiedPixmap))
1709 	    return True;
1710     }
1711     return False;
1712 }
1713 
1714     static void
1715 gui_athena_delayed_arm_action(
1716     Widget	w,
1717     XEvent	*event,
1718     String	*args,
1719     Cardinal	*nargs)
1720 {
1721     Dimension	width, height;
1722 
1723     if (event->type != MotionNotify)
1724 	return;
1725 
1726     XtVaGetValues(w,
1727 	XtNwidth,   &width,
1728 	XtNheight,  &height,
1729 	NULL);
1730 
1731     if (event->xmotion.x >= (int)width || event->xmotion.y >= (int)height)
1732 	return;
1733 
1734     {
1735 	static Widget	    previous_active_widget = NULL;
1736 	Widget		    current;
1737 
1738 	current = XawSimpleMenuGetActiveEntry(w);
1739 	if (current != previous_active_widget)
1740 	{
1741 	    if (timer)
1742 	    {
1743 		/* If the timeout hasn't been triggered, remove it */
1744 		XtRemoveTimeOut(timer);
1745 	    }
1746 	    gui_athena_popdown_submenus_action(w,event,args,nargs);
1747 	    if (has_submenu(current))
1748 	    {
1749 		XtAppAddTimeOut(XtWidgetToApplicationContext(w), 600L,
1750 				gui_athena_menu_timeout,
1751 				(XtPointer)current);
1752 	    }
1753 	    previous_active_widget = current;
1754 	}
1755     }
1756 }
1757 
1758     static Widget
1759 get_popup_entry(Widget w)
1760 {
1761     Widget	menuw;
1762 
1763     /* Get the active entry for the current menu */
1764     if ((menuw = XawSimpleMenuGetActiveEntry(w)) == (Widget)0)
1765 	return NULL;
1766 
1767     return submenu_widget(menuw);
1768 }
1769 
1770 /* Given the widget that has been determined to have a submenu, return the submenu widget
1771  * that is to be popped up.
1772  */
1773     static Widget
1774 submenu_widget(Widget widget)
1775 {
1776     /* Precondition: has_submenu(widget) == True
1777      *	    XtIsSubclass(XtParent(widget),simpleMenuWidgetClass) == True
1778      */
1779 
1780     char_u	*pullright_name;
1781     Widget	popup;
1782 
1783     pullright_name = make_pull_name((char_u *)XtName(widget));
1784     popup = XtNameToWidget(XtParent(widget), (char *)pullright_name);
1785     vim_free(pullright_name);
1786 
1787     return popup;
1788     /* Postcondition: (popup != NULL) implies
1789      * (XtIsSubclass(popup,simpleMenuWidgetClass) == True) */
1790 }
1791 
1792     void
1793 gui_mch_show_popupmenu(vimmenu_T *menu)
1794 {
1795     int		rootx, rooty, winx, winy;
1796     Window	root, child;
1797     unsigned int mask;
1798 
1799     if (menu->submenu_id == (Widget)0)
1800 	return;
1801 
1802     /* Position the popup menu at the pointer */
1803     if (XQueryPointer(gui.dpy, XtWindow(vimShell), &root, &child,
1804 		&rootx, &rooty, &winx, &winy, &mask))
1805     {
1806 	rootx -= 30;
1807 	if (rootx < 0)
1808 	    rootx = 0;
1809 	rooty -= 5;
1810 	if (rooty < 0)
1811 	    rooty = 0;
1812 	XtVaSetValues(menu->submenu_id,
1813 		XtNx, rootx,
1814 		XtNy, rooty,
1815 		NULL);
1816     }
1817 
1818     XtOverrideTranslations(menu->submenu_id, popupTrans);
1819     XtPopupSpringLoaded(menu->submenu_id);
1820 }
1821 
1822 #endif /* FEAT_MENU */
1823 
1824 /*
1825  * Set the menu and scrollbar colors to their default values.
1826  */
1827     void
1828 gui_mch_def_colors(void)
1829 {
1830     /*
1831      * Get the colors ourselves.  Using the automatic conversion doesn't
1832      * handle looking for approximate colors.
1833      */
1834     if (gui.in_use)
1835     {
1836 	gui.menu_fg_pixel = gui_get_color((char_u *)gui.rsrc_menu_fg_name);
1837 	gui.menu_bg_pixel = gui_get_color((char_u *)gui.rsrc_menu_bg_name);
1838 	gui.scroll_fg_pixel = gui_get_color((char_u *)gui.rsrc_scroll_fg_name);
1839 	gui.scroll_bg_pixel = gui_get_color((char_u *)gui.rsrc_scroll_bg_name);
1840 #ifdef FEAT_BEVAL_GUI
1841 	gui.tooltip_fg_pixel = gui_get_color((char_u *)gui.rsrc_tooltip_fg_name);
1842 	gui.tooltip_bg_pixel = gui_get_color((char_u *)gui.rsrc_tooltip_bg_name);
1843 #endif
1844     }
1845 }
1846 
1847 
1848 /*
1849  * Scrollbar stuff.
1850  */
1851 
1852     void
1853 gui_mch_set_scrollbar_thumb(
1854     scrollbar_T	*sb,
1855     long	val,
1856     long	size,
1857     long	max)
1858 {
1859     double	v, s;
1860 
1861     if (sb->id == (Widget)0)
1862 	return;
1863 
1864     /*
1865      * Athena scrollbar must go from 0.0 to 1.0.
1866      */
1867     if (max == 0)
1868     {
1869 	/* So you can't scroll it at all (normally it scrolls past end) */
1870 #ifdef FEAT_GUI_NEXTAW
1871 	XawScrollbarSetThumb(sb->id, 0.0, 1.0);
1872 #else
1873 	vim_XawScrollbarSetThumb(sb->id, 0.0, 1.0, 0.0);
1874 #endif
1875     }
1876     else
1877     {
1878 	v = (double)val / (double)(max + 1);
1879 	s = (double)size / (double)(max + 1);
1880 #ifdef FEAT_GUI_NEXTAW
1881 	XawScrollbarSetThumb(sb->id, v, s);
1882 #else
1883 	vim_XawScrollbarSetThumb(sb->id, v, s, 1.0);
1884 #endif
1885     }
1886 }
1887 
1888     void
1889 gui_mch_set_scrollbar_pos(
1890     scrollbar_T *sb,
1891     int		x,
1892     int		y,
1893     int		w,
1894     int		h)
1895 {
1896     if (sb->id == (Widget)0)
1897 	return;
1898 
1899     XtUnmanageChild(sb->id);
1900     XtVaSetValues(sb->id,
1901 		  XtNhorizDistance, x,
1902 		  XtNvertDistance, y,
1903 		  XtNwidth, w,
1904 		  XtNheight, h,
1905 		  NULL);
1906     XtManageChild(sb->id);
1907 }
1908 
1909     void
1910 gui_mch_enable_scrollbar(scrollbar_T *sb, int flag)
1911 {
1912     if (sb->id != (Widget)0)
1913     {
1914 	if (flag)
1915 	    XtManageChild(sb->id);
1916 	else
1917 	    XtUnmanageChild(sb->id);
1918     }
1919 }
1920 
1921     void
1922 gui_mch_create_scrollbar(
1923     scrollbar_T *sb,
1924     int		orient)	/* SBAR_VERT or SBAR_HORIZ */
1925 {
1926     sb->id = XtVaCreateWidget("scrollBar",
1927 #ifdef FEAT_GUI_NEXTAW
1928 	    scrollbarWidgetClass, vimForm,
1929 #else
1930 	    vim_scrollbarWidgetClass, vimForm,
1931 #endif
1932 	    XtNresizable,   True,
1933 	    XtNtop,	    XtChainTop,
1934 	    XtNbottom,	    XtChainTop,
1935 	    XtNleft,	    XtChainLeft,
1936 	    XtNright,	    XtChainLeft,
1937 	    XtNborderWidth, 0,
1938 	    XtNorientation, (orient == SBAR_VERT) ? XtorientVertical
1939 						  : XtorientHorizontal,
1940 	    XtNforeground, gui.scroll_fg_pixel,
1941 	    XtNbackground, gui.scroll_bg_pixel,
1942 	    NULL);
1943     if (sb->id == (Widget)0)
1944 	return;
1945 
1946     XtAddCallback(sb->id, XtNjumpProc,
1947 		  gui_athena_scroll_cb_jump, (XtPointer)sb->ident);
1948     XtAddCallback(sb->id, XtNscrollProc,
1949 		  gui_athena_scroll_cb_scroll, (XtPointer)sb->ident);
1950 
1951 #ifdef FEAT_GUI_NEXTAW
1952     XawScrollbarSetThumb(sb->id, 0.0, 1.0);
1953 #else
1954     vim_XawScrollbarSetThumb(sb->id, 0.0, 1.0, 0.0);
1955 #endif
1956 }
1957 
1958     void
1959 gui_mch_destroy_scrollbar(scrollbar_T *sb)
1960 {
1961     if (sb->id != (Widget)0)
1962 	XtDestroyWidget(sb->id);
1963 }
1964 
1965     void
1966 gui_mch_set_scrollbar_colors(scrollbar_T *sb)
1967 {
1968     if (sb->id != (Widget)0)
1969 	XtVaSetValues(sb->id,
1970 	    XtNforeground, gui.scroll_fg_pixel,
1971 	    XtNbackground, gui.scroll_bg_pixel,
1972 	    NULL);
1973 
1974     /* This is needed for the rectangle below the vertical scrollbars. */
1975     if (sb == &gui.bottom_sbar && vimForm != (Widget)0)
1976 	gui_athena_scroll_colors(vimForm);
1977 }
1978 
1979 /*
1980  * Miscellaneous stuff:
1981  */
1982     Window
1983 gui_x11_get_wid(void)
1984 {
1985     return XtWindow(textArea);
1986 }
1987 
1988 #if defined(FEAT_BROWSE) || defined(PROTO)
1989 /*
1990  * Put up a file requester.
1991  * Returns the selected name in allocated memory, or NULL for Cancel.
1992  */
1993     char_u *
1994 gui_mch_browse(
1995     int		saving UNUSED,	/* select file to write */
1996     char_u	*title,		/* title for the window */
1997     char_u	*dflt,		/* default name */
1998     char_u	*ext UNUSED,	/* extension added */
1999     char_u	*initdir,	/* initial directory, NULL for current dir */
2000     char_u	*filter UNUSED)	/* file name filter */
2001 {
2002     Position x, y;
2003     char_u	dirbuf[MAXPATHL];
2004 
2005     /* Concatenate "initdir" and "dflt". */
2006     if (initdir == NULL || *initdir == NUL)
2007 	mch_dirname(dirbuf, MAXPATHL);
2008     else if (STRLEN(initdir) + 2 < MAXPATHL)
2009 	STRCPY(dirbuf, initdir);
2010     else
2011 	dirbuf[0] = NUL;
2012     if (dflt != NULL && *dflt != NUL
2013 			      && STRLEN(dirbuf) + 2 + STRLEN(dflt) < MAXPATHL)
2014     {
2015 	add_pathsep(dirbuf);
2016 	STRCAT(dirbuf, dflt);
2017     }
2018 
2019     /* Position the file selector just below the menubar */
2020     XtTranslateCoords(vimShell, (Position)0, (Position)
2021 #ifdef FEAT_MENU
2022 	    gui.menu_height
2023 #else
2024 	    0
2025 #endif
2026 	    , &x, &y);
2027     return (char_u *)vim_SelFile(vimShell, (char *)title, (char *)dirbuf,
2028 		  NULL, (int)x, (int)y, gui.menu_fg_pixel, gui.menu_bg_pixel,
2029 		  gui.scroll_fg_pixel, gui.scroll_bg_pixel);
2030 }
2031 #endif
2032 
2033 #if defined(FEAT_GUI_DIALOG) || defined(PROTO)
2034 
2035 static int	dialogStatus;
2036 static Atom	dialogatom;
2037 
2038 /*
2039  * Callback function for the textfield.  When CR is hit this works like
2040  * hitting the "OK" button, ESC like "Cancel".
2041  */
2042     static void
2043 keyhit_callback(
2044     Widget		w UNUSED,
2045     XtPointer		client_data UNUSED,
2046     XEvent		*event,
2047     Boolean		*cont UNUSED)
2048 {
2049     char	buf[2];
2050 
2051     if (XLookupString(&(event->xkey), buf, 2, NULL, NULL) == 1)
2052     {
2053 	if (*buf == CAR)
2054 	    dialogStatus = 1;
2055 	else if (*buf == ESC)
2056 	    dialogStatus = 0;
2057     }
2058 }
2059 
2060     static void
2061 butproc(
2062     Widget	w UNUSED,
2063     XtPointer	client_data,
2064     XtPointer	call_data UNUSED)
2065 {
2066     dialogStatus = (int)(long)client_data + 1;
2067 }
2068 
2069 /*
2070  * Function called when dialog window closed.
2071  */
2072     static void
2073 dialog_wm_handler(
2074     Widget	w UNUSED,
2075     XtPointer	client_data UNUSED,
2076     XEvent	*event,
2077     Boolean	*dum UNUSED)
2078 {
2079     if (event->type == ClientMessage
2080 	    && (Atom)((XClientMessageEvent *)event)->data.l[0] == dialogatom)
2081 	dialogStatus = 0;
2082 }
2083 
2084     int
2085 gui_mch_dialog(
2086     int		type UNUSED,
2087     char_u	*title,
2088     char_u	*message,
2089     char_u	*buttons,
2090     int		dfltbutton UNUSED,
2091     char_u	*textfield,
2092     int		ex_cmd UNUSED)
2093 {
2094     char_u		*buts;
2095     char_u		*p, *next;
2096     XtAppContext	app;
2097     XEvent		event;
2098     Position		wd, hd;
2099     Position		wv, hv;
2100     Position		x, y;
2101     Widget		dialog;
2102     Widget		dialogshell;
2103     Widget		dialogmessage;
2104     Widget		dialogtextfield = 0;
2105     Widget		dialogButton;
2106     Widget		prev_dialogButton = NULL;
2107     int			butcount;
2108     int			vertical;
2109 
2110     if (title == NULL)
2111 	title = (char_u *)_("Vim dialog");
2112     dialogStatus = -1;
2113 
2114     /* if our pointer is currently hidden, then we should show it. */
2115     gui_mch_mousehide(FALSE);
2116 
2117     /* Check 'v' flag in 'guioptions': vertical button placement. */
2118     vertical = (vim_strchr(p_go, GO_VERTICAL) != NULL);
2119 
2120     /* The shell is created each time, to make sure it is resized properly */
2121     dialogshell = XtVaCreatePopupShell("dialogShell",
2122 	    transientShellWidgetClass, vimShell,
2123 	    XtNtitle, title,
2124 	    NULL);
2125     if (dialogshell == (Widget)0)
2126 	goto error;
2127 
2128     dialog = XtVaCreateManagedWidget("dialog",
2129 	    formWidgetClass, dialogshell,
2130 	    XtNdefaultDistance, 20,
2131 	    NULL);
2132     if (dialog == (Widget)0)
2133 	goto error;
2134     gui_athena_menu_colors(dialog);
2135     dialogmessage = XtVaCreateManagedWidget("dialogMessage",
2136 	    labelWidgetClass, dialog,
2137 	    XtNlabel, message,
2138 	    XtNtop, XtChainTop,
2139 	    XtNbottom, XtChainTop,
2140 	    XtNleft, XtChainLeft,
2141 	    XtNright, XtChainLeft,
2142 	    XtNresizable, True,
2143 	    XtNborderWidth, 0,
2144 	    NULL);
2145     gui_athena_menu_colors(dialogmessage);
2146 
2147     if (textfield != NULL)
2148     {
2149 	dialogtextfield = XtVaCreateManagedWidget("textfield",
2150 		asciiTextWidgetClass, dialog,
2151 		XtNwidth, 400,
2152 		XtNtop, XtChainTop,
2153 		XtNbottom, XtChainTop,
2154 		XtNleft, XtChainLeft,
2155 		XtNright, XtChainRight,
2156 		XtNfromVert, dialogmessage,
2157 		XtNresizable, True,
2158 		XtNstring, textfield,
2159 		XtNlength, IOSIZE,
2160 		XtNuseStringInPlace, True,
2161 		XtNeditType, XawtextEdit,
2162 		XtNwrap, XawtextWrapNever,
2163 		XtNresize, XawtextResizeHeight,
2164 		NULL);
2165 	XtManageChild(dialogtextfield);
2166 	XtAddEventHandler(dialogtextfield, KeyPressMask, False,
2167 			    (XtEventHandler)keyhit_callback, (XtPointer)NULL);
2168 	XawTextSetInsertionPoint(dialogtextfield,
2169 					  (XawTextPosition)STRLEN(textfield));
2170 	XtSetKeyboardFocus(dialog, dialogtextfield);
2171     }
2172 
2173     /* make a copy, so that we can insert NULs */
2174     buts = vim_strsave(buttons);
2175     if (buts == NULL)
2176 	return -1;
2177 
2178     p = buts;
2179     for (butcount = 0; *p; ++butcount)
2180     {
2181 	for (next = p; *next; ++next)
2182 	{
2183 	    if (*next == DLG_HOTKEY_CHAR)
2184 		STRMOVE(next, next + 1);
2185 	    if (*next == DLG_BUTTON_SEP)
2186 	    {
2187 		*next++ = NUL;
2188 		break;
2189 	    }
2190 	}
2191 	dialogButton = XtVaCreateManagedWidget("button",
2192 		commandWidgetClass, dialog,
2193 		XtNlabel, p,
2194 		XtNtop, XtChainBottom,
2195 		XtNbottom, XtChainBottom,
2196 		XtNleft, XtChainLeft,
2197 		XtNright, XtChainLeft,
2198 		XtNfromVert, textfield == NULL ? dialogmessage : dialogtextfield,
2199 		XtNvertDistance, vertical ? 4 : 20,
2200 		XtNresizable, False,
2201 		NULL);
2202 	gui_athena_menu_colors(dialogButton);
2203 	if (butcount > 0)
2204 	    XtVaSetValues(dialogButton,
2205 		    vertical ? XtNfromVert : XtNfromHoriz, prev_dialogButton,
2206 		    NULL);
2207 
2208 	XtAddCallback(dialogButton, XtNcallback, butproc, (XtPointer)(long_u)butcount);
2209 	p = next;
2210 	prev_dialogButton = dialogButton;
2211     }
2212     vim_free(buts);
2213 
2214     XtRealizeWidget(dialogshell);
2215 
2216     /* Setup for catching the close-window event, don't let it close Vim! */
2217     dialogatom = XInternAtom(gui.dpy, "WM_DELETE_WINDOW", False);
2218     XSetWMProtocols(gui.dpy, XtWindow(dialogshell), &dialogatom, 1);
2219     XtAddEventHandler(dialogshell, NoEventMask, True, dialog_wm_handler, NULL);
2220 
2221     XtVaGetValues(dialogshell,
2222 	    XtNwidth, &wd,
2223 	    XtNheight, &hd,
2224 	    NULL);
2225     XtVaGetValues(vimShell,
2226 	    XtNwidth, &wv,
2227 	    XtNheight, &hv,
2228 	    NULL);
2229     XtTranslateCoords(vimShell,
2230 	    (Position)((wv - wd) / 2),
2231 	    (Position)((hv - hd) / 2),
2232 	    &x, &y);
2233     if (x < 0)
2234 	x = 0;
2235     if (y < 0)
2236 	y = 0;
2237     XtVaSetValues(dialogshell, XtNx, x, XtNy, y, NULL);
2238 
2239     /* Position the mouse pointer in the dialog, required for when focus
2240      * follows mouse. */
2241     XWarpPointer(gui.dpy, (Window)0, XtWindow(dialogshell), 0, 0, 0, 0, 20, 40);
2242 
2243 
2244     app = XtWidgetToApplicationContext(dialogshell);
2245 
2246     XtPopup(dialogshell, XtGrabNonexclusive);
2247 
2248     for (;;)
2249     {
2250 	XtAppNextEvent(app, &event);
2251 	XtDispatchEvent(&event);
2252 	if (dialogStatus >= 0)
2253 	    break;
2254     }
2255 
2256     XtPopdown(dialogshell);
2257 
2258     if (textfield != NULL && dialogStatus < 0)
2259 	*textfield = NUL;
2260 
2261 error:
2262     XtDestroyWidget(dialogshell);
2263 
2264     return dialogStatus;
2265 }
2266 #endif
2267 
2268 #if defined(FEAT_GUI_DIALOG) || defined(FEAT_MENU)
2269 /*
2270  * Set the colors of Widget "id" to the menu colors.
2271  */
2272     static void
2273 gui_athena_menu_colors(Widget id)
2274 {
2275     if (gui.menu_bg_pixel != INVALCOLOR)
2276 	XtVaSetValues(id, XtNbackground, gui.menu_bg_pixel, NULL);
2277     if (gui.menu_fg_pixel != INVALCOLOR)
2278 	XtVaSetValues(id, XtNforeground, gui.menu_fg_pixel, NULL);
2279 }
2280 #endif
2281 
2282 /*
2283  * Set the colors of Widget "id" to the scroll colors.
2284  */
2285     static void
2286 gui_athena_scroll_colors(Widget id)
2287 {
2288     if (gui.scroll_bg_pixel != INVALCOLOR)
2289 	XtVaSetValues(id, XtNbackground, gui.scroll_bg_pixel, NULL);
2290     if (gui.scroll_fg_pixel != INVALCOLOR)
2291 	XtVaSetValues(id, XtNforeground, gui.scroll_fg_pixel, NULL);
2292 }
2293