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
42extern Widget vimShell;
43
44static Widget vimForm = (Widget)0;
45Widget textArea = (Widget)0;
46#ifdef FEAT_MENU
47static Widget menuBar = (Widget)0;
48static XtIntervalId timer = 0;	    /* 0 = expired, otherwise active */
49
50/* Used to figure out menu ordering */
51static vimmenu_T *a_cur_menu = NULL;
52static Cardinal	athena_calculate_ins_pos __ARGS((Widget));
53
54static Pixmap gui_athena_create_pullright_pixmap __ARGS((Widget));
55static void gui_athena_menu_timeout __ARGS((XtPointer, XtIntervalId *));
56static void gui_athena_popup_callback __ARGS((Widget, XtPointer, XtPointer));
57static void gui_athena_delayed_arm_action __ARGS((Widget, XEvent *, String *,
58						 Cardinal *));
59static void gui_athena_popdown_submenus_action __ARGS((Widget, XEvent *,
60						      String *, Cardinal *));
61static 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
68static void gui_mch_reset_focus __ARGS((void));
69static Widget toolBar = (Widget)0;
70#endif
71
72static void gui_athena_scroll_cb_jump	__ARGS((Widget, XtPointer, XtPointer));
73static void gui_athena_scroll_cb_scroll __ARGS((Widget, XtPointer, XtPointer));
74#if defined(FEAT_GUI_DIALOG) || defined(FEAT_MENU)
75static void gui_athena_menu_colors __ARGS((Widget id));
76#endif
77static void gui_athena_scroll_colors __ARGS((Widget id));
78
79#ifdef FEAT_MENU
80static XtTranslations	popupTrans, parentTrans, menuTrans, supermenuTrans;
81static Pixmap		pullerBitmap = None;
82static 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
90gui_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
125gui_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
231gui_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
328gui_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
428gui_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
445static void createXpmImages __ARGS((char_u *path, char **xpm, Pixmap *sen));
446static 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
453get_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
495createXpmImages(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 subsititution 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
569gui_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
598gui_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
625gui_mch_reset_focus()
626{
627    XtSetKeyboardFocus(vimForm, textArea);
628}
629#endif
630
631
632    void
633gui_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
646static char_u	*make_pull_name __ARGS((char_u * name));
647static Widget	get_popup_entry __ARGS((Widget w));
648static Widget	submenu_widget __ARGS((Widget));
649static Boolean	has_submenu __ARGS((Widget));
650static void gui_mch_submenu_change __ARGS((vimmenu_T *mp, int colors));
651static void gui_athena_menu_font __ARGS((Widget id));
652static Boolean	gui_athena_menu_has_submenus __ARGS((Widget, Widget));
653
654    void
655gui_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
688gui_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
720athena_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
767gui_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
887gui_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
909gui_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
957gui_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
1034gui_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
1049gui_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
1065gui_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 *
1144make_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
1161gui_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
1283gui_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
1407gui_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
1442gui_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
1458gui_mch_toggle_tearoffs(enable)
1459    int		enable UNUSED;
1460{
1461    /* no tearoff menus */
1462}
1463
1464    void
1465gui_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
1483gui_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
1629gui_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
1661gui_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
1693gui_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
1722has_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
1737gui_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
1781get_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
1797submenu_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
1817gui_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
1853gui_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
1878gui_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
1914gui_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
1935gui_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
1949gui_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
1987gui_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
1996gui_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
2014gui_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 *
2025gui_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
2066static int	dialogStatus;
2067static Atom	dialogatom;
2068
2069static void keyhit_callback __ARGS((Widget w, XtPointer client_data, XEvent *event, Boolean *cont));
2070static void butproc __ARGS((Widget w, XtPointer client_data, XtPointer call_data));
2071static 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
2078keyhit_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
2096butproc(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
2108dialog_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
2120gui_mch_dialog(type, title, message, buttons, dfltbutton, textfield)
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{
2128    char_u		*buts;
2129    char_u		*p, *next;
2130    XtAppContext	app;
2131    XEvent		event;
2132    Position		wd, hd;
2133    Position		wv, hv;
2134    Position		x, y;
2135    Widget		dialog;
2136    Widget		dialogshell;
2137    Widget		dialogmessage;
2138    Widget		dialogtextfield = 0;
2139    Widget		dialogButton;
2140    Widget		prev_dialogButton = NULL;
2141    int			butcount;
2142    int			vertical;
2143
2144    if (title == NULL)
2145	title = (char_u *)_("Vim dialog");
2146    dialogStatus = -1;
2147
2148    /* if our pointer is currently hidden, then we should show it. */
2149    gui_mch_mousehide(FALSE);
2150
2151    /* Check 'v' flag in 'guioptions': vertical button placement. */
2152    vertical = (vim_strchr(p_go, GO_VERTICAL) != NULL);
2153
2154    /* The shell is created each time, to make sure it is resized properly */
2155    dialogshell = XtVaCreatePopupShell("dialogShell",
2156	    transientShellWidgetClass, vimShell,
2157	    XtNtitle, title,
2158	    NULL);
2159    if (dialogshell == (Widget)0)
2160	goto error;
2161
2162    dialog = XtVaCreateManagedWidget("dialog",
2163	    formWidgetClass, dialogshell,
2164	    XtNdefaultDistance, 20,
2165	    NULL);
2166    if (dialog == (Widget)0)
2167	goto error;
2168    gui_athena_menu_colors(dialog);
2169    dialogmessage = XtVaCreateManagedWidget("dialogMessage",
2170	    labelWidgetClass, dialog,
2171	    XtNlabel, message,
2172	    XtNtop, XtChainTop,
2173	    XtNbottom, XtChainTop,
2174	    XtNleft, XtChainLeft,
2175	    XtNright, XtChainLeft,
2176	    XtNresizable, True,
2177	    XtNborderWidth, 0,
2178	    NULL);
2179    gui_athena_menu_colors(dialogmessage);
2180
2181    if (textfield != NULL)
2182    {
2183	dialogtextfield = XtVaCreateManagedWidget("textfield",
2184		asciiTextWidgetClass, dialog,
2185		XtNwidth, 400,
2186		XtNtop, XtChainTop,
2187		XtNbottom, XtChainTop,
2188		XtNleft, XtChainLeft,
2189		XtNright, XtChainRight,
2190		XtNfromVert, dialogmessage,
2191		XtNresizable, True,
2192		XtNstring, textfield,
2193		XtNlength, IOSIZE,
2194		XtNuseStringInPlace, True,
2195		XtNeditType, XawtextEdit,
2196		XtNwrap, XawtextWrapNever,
2197		XtNresize, XawtextResizeHeight,
2198		NULL);
2199	XtManageChild(dialogtextfield);
2200	XtAddEventHandler(dialogtextfield, KeyPressMask, False,
2201			    (XtEventHandler)keyhit_callback, (XtPointer)NULL);
2202	XawTextSetInsertionPoint(dialogtextfield,
2203					  (XawTextPosition)STRLEN(textfield));
2204	XtSetKeyboardFocus(dialog, dialogtextfield);
2205    }
2206
2207    /* make a copy, so that we can insert NULs */
2208    buts = vim_strsave(buttons);
2209    if (buts == NULL)
2210	return -1;
2211
2212    p = buts;
2213    for (butcount = 0; *p; ++butcount)
2214    {
2215	for (next = p; *next; ++next)
2216	{
2217	    if (*next == DLG_HOTKEY_CHAR)
2218		STRMOVE(next, next + 1);
2219	    if (*next == DLG_BUTTON_SEP)
2220	    {
2221		*next++ = NUL;
2222		break;
2223	    }
2224	}
2225	dialogButton = XtVaCreateManagedWidget("button",
2226		commandWidgetClass, dialog,
2227		XtNlabel, p,
2228		XtNtop, XtChainBottom,
2229		XtNbottom, XtChainBottom,
2230		XtNleft, XtChainLeft,
2231		XtNright, XtChainLeft,
2232		XtNfromVert, textfield == NULL ? dialogmessage : dialogtextfield,
2233		XtNvertDistance, vertical ? 4 : 20,
2234		XtNresizable, False,
2235		NULL);
2236	gui_athena_menu_colors(dialogButton);
2237	if (butcount > 0)
2238	    XtVaSetValues(dialogButton,
2239		    vertical ? XtNfromVert : XtNfromHoriz, prev_dialogButton,
2240		    NULL);
2241
2242	XtAddCallback(dialogButton, XtNcallback, butproc, (XtPointer)(long_u)butcount);
2243	p = next;
2244	prev_dialogButton = dialogButton;
2245    }
2246    vim_free(buts);
2247
2248    XtRealizeWidget(dialogshell);
2249
2250    /* Setup for catching the close-window event, don't let it close Vim! */
2251    dialogatom = XInternAtom(gui.dpy, "WM_DELETE_WINDOW", False);
2252    XSetWMProtocols(gui.dpy, XtWindow(dialogshell), &dialogatom, 1);
2253    XtAddEventHandler(dialogshell, NoEventMask, True, dialog_wm_handler, NULL);
2254
2255    XtVaGetValues(dialogshell,
2256	    XtNwidth, &wd,
2257	    XtNheight, &hd,
2258	    NULL);
2259    XtVaGetValues(vimShell,
2260	    XtNwidth, &wv,
2261	    XtNheight, &hv,
2262	    NULL);
2263    XtTranslateCoords(vimShell,
2264	    (Position)((wv - wd) / 2),
2265	    (Position)((hv - hd) / 2),
2266	    &x, &y);
2267    if (x < 0)
2268	x = 0;
2269    if (y < 0)
2270	y = 0;
2271    XtVaSetValues(dialogshell, XtNx, x, XtNy, y, NULL);
2272
2273    /* Position the mouse pointer in the dialog, required for when focus
2274     * follows mouse. */
2275    XWarpPointer(gui.dpy, (Window)0, XtWindow(dialogshell), 0, 0, 0, 0, 20, 40);
2276
2277
2278    app = XtWidgetToApplicationContext(dialogshell);
2279
2280    XtPopup(dialogshell, XtGrabNonexclusive);
2281
2282    for (;;)
2283    {
2284	XtAppNextEvent(app, &event);
2285	XtDispatchEvent(&event);
2286	if (dialogStatus >= 0)
2287	    break;
2288    }
2289
2290    XtPopdown(dialogshell);
2291
2292    if (textfield != NULL && dialogStatus < 0)
2293	*textfield = NUL;
2294
2295error:
2296    XtDestroyWidget(dialogshell);
2297
2298    return dialogStatus;
2299}
2300#endif
2301
2302#if defined(FEAT_GUI_DIALOG) || defined(FEAT_MENU)
2303/*
2304 * Set the colors of Widget "id" to the menu colors.
2305 */
2306    static void
2307gui_athena_menu_colors(id)
2308    Widget  id;
2309{
2310    if (gui.menu_bg_pixel != INVALCOLOR)
2311	XtVaSetValues(id, XtNbackground, gui.menu_bg_pixel, NULL);
2312    if (gui.menu_fg_pixel != INVALCOLOR)
2313	XtVaSetValues(id, XtNforeground, gui.menu_fg_pixel, NULL);
2314}
2315#endif
2316
2317/*
2318 * Set the colors of Widget "id" to the scroll colors.
2319 */
2320    static void
2321gui_athena_scroll_colors(id)
2322    Widget  id;
2323{
2324    if (gui.scroll_bg_pixel != INVALCOLOR)
2325	XtVaSetValues(id, XtNbackground, gui.scroll_bg_pixel, NULL);
2326    if (gui.scroll_fg_pixel != INVALCOLOR)
2327	XtVaSetValues(id, XtNforeground, gui.scroll_fg_pixel, NULL);
2328}
2329