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