diff src/gui_athena.c @ 7:3fc0f57ecb91 v7.0001

updated for version 7.0001
author vimboss
date Sun, 13 Jun 2004 20:20:40 +0000
parents
children eff3887963cc
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/src/gui_athena.c
@@ -0,0 +1,2230 @@
+/* vi:set ts=8 sts=4 sw=4:
+ *
+ * VIM - Vi IMproved		by Bram Moolenaar
+ *				GUI/Motif support by Robert Webb
+ *				Athena port by Bill Foster
+ *
+ * Do ":help uganda"  in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ * See README.txt for an overview of the Vim source code.
+ */
+
+#include <X11/StringDefs.h>
+#include <X11/Intrinsic.h>
+#ifdef FEAT_GUI_NEXTAW
+# include <X11/neXtaw/Form.h>
+# include <X11/neXtaw/SimpleMenu.h>
+# include <X11/neXtaw/MenuButton.h>
+# include <X11/neXtaw/SmeBSB.h>
+# include <X11/neXtaw/SmeLine.h>
+# include <X11/neXtaw/Box.h>
+# include <X11/neXtaw/Dialog.h>
+# include <X11/neXtaw/Text.h>
+# include <X11/neXtaw/AsciiText.h>
+# include <X11/neXtaw/Scrollbar.h>
+#else
+# include <X11/Xaw/Form.h>
+# include <X11/Xaw/SimpleMenu.h>
+# include <X11/Xaw/MenuButton.h>
+# include <X11/Xaw/SmeBSB.h>
+# include <X11/Xaw/SmeLine.h>
+# include <X11/Xaw/Box.h>
+# include <X11/Xaw/Dialog.h>
+# include <X11/Xaw/Text.h>
+# include <X11/Xaw/AsciiText.h>
+#endif /* FEAT_GUI_NEXTAW */
+
+#include "vim.h"
+#ifndef FEAT_GUI_NEXTAW
+# include "gui_at_sb.h"
+#endif
+
+extern Widget vimShell;
+
+static Widget vimForm = (Widget)0;
+static Widget textArea = (Widget)0;
+#ifdef FEAT_MENU
+static Widget menuBar = (Widget)0;
+static XtIntervalId timer = 0;	    /* 0 = expired, otherwise active */
+
+/* Used to figure out menu ordering */
+static vimmenu_T *a_cur_menu = NULL;
+static Cardinal	athena_calculate_ins_pos __ARGS((Widget));
+
+static Pixmap gui_athena_create_pullright_pixmap __ARGS((Widget));
+static void gui_athena_menu_timeout __ARGS((XtPointer, XtIntervalId *));
+static void gui_athena_popup_callback __ARGS((Widget, XtPointer, XtPointer));
+static void gui_athena_delayed_arm_action __ARGS((Widget, XEvent *, String *,
+						 Cardinal *));
+static void gui_athena_popdown_submenus_action __ARGS((Widget, XEvent *,
+						      String *, Cardinal *));
+static XtActionsRec	pullAction[2] = {
+    { "menu-delayedpopup", (XtActionProc)gui_athena_delayed_arm_action},
+    { "menu-popdownsubmenus", (XtActionProc)gui_athena_popdown_submenus_action}
+};
+#endif
+
+#ifdef FEAT_TOOLBAR
+static void gui_mch_reset_focus __ARGS((void));
+static Widget toolBar = (Widget)0;
+#endif
+
+static void gui_athena_scroll_cb_jump	__ARGS((Widget, XtPointer, XtPointer));
+static void gui_athena_scroll_cb_scroll __ARGS((Widget, XtPointer, XtPointer));
+#if defined(FEAT_GUI_DIALOG) || defined(FEAT_MENU)
+static void gui_athena_menu_colors __ARGS((Widget id));
+#endif
+static void gui_athena_scroll_colors __ARGS((Widget id));
+
+#ifdef FEAT_MENU
+static XtTranslations	popupTrans, parentTrans, menuTrans, supermenuTrans;
+static Pixmap		pullerBitmap = None;
+static int		puller_width = 0;
+#endif
+
+/*
+ * Scrollbar callback (XtNjumpProc) for when the scrollbar is dragged with the
+ * left or middle mouse button.
+ */
+/* ARGSUSED */
+    static void
+gui_athena_scroll_cb_jump(w, client_data, call_data)
+    Widget	w;
+    XtPointer	client_data, call_data;
+{
+    scrollbar_T *sb, *sb_info;
+    long	value;
+
+    sb = gui_find_scrollbar((long)client_data);
+
+    if (sb == NULL)
+	return;
+    else if (sb->wp != NULL)	    /* Left or right scrollbar */
+    {
+	/*
+	 * Careful: need to get scrollbar info out of first (left) scrollbar
+	 * for window, but keep real scrollbar too because we must pass it to
+	 * gui_drag_scrollbar().
+	 */
+	sb_info = &sb->wp->w_scrollbars[0];
+    }
+    else	    /* Bottom scrollbar */
+	sb_info = sb;
+
+    value = (long)(*((float *)call_data) * (float)(sb_info->max + 1) + 0.001);
+    if (value > sb_info->max)
+	value = sb_info->max;
+
+    gui_drag_scrollbar(sb, value, TRUE);
+}
+
+/*
+ * Scrollbar callback (XtNscrollProc) for paging up or down with the left or
+ * right mouse buttons.
+ */
+/* ARGSUSED */
+    static void
+gui_athena_scroll_cb_scroll(w, client_data, call_data)
+    Widget	w;
+    XtPointer	client_data, call_data;
+{
+    scrollbar_T *sb, *sb_info;
+    long	value;
+    int		data = (int)(long)call_data;
+    int		page;
+
+    sb = gui_find_scrollbar((long)client_data);
+
+    if (sb == NULL)
+	return;
+    if (sb->wp != NULL)		/* Left or right scrollbar */
+    {
+	/*
+	 * Careful: need to get scrollbar info out of first (left) scrollbar
+	 * for window, but keep real scrollbar too because we must pass it to
+	 * gui_drag_scrollbar().
+	 */
+	sb_info = &sb->wp->w_scrollbars[0];
+
+	if (sb_info->size > 5)
+	    page = sb_info->size - 2;	    /* use two lines of context */
+	else
+	    page = sb_info->size;
+#ifdef FEAT_GUI_NEXTAW
+	if (data < 0)
+	{
+	    data = (data - gui.char_height + 1) / gui.char_height;
+	    if (data > -sb_info->size)
+		data = -1;
+	    else
+		data = -page;
+	}
+	else if (data > 0)
+	{
+	    data = (data + gui.char_height - 1) / gui.char_height;
+	    if (data < sb_info->size)
+		data = 1;
+	    else
+		data = page;
+	}
+#else
+	switch (data)
+	{
+	    case  ONE_LINE_DATA: data = 1; break;
+	    case -ONE_LINE_DATA: data = -1; break;
+	    case  ONE_PAGE_DATA: data = page; break;
+	    case -ONE_PAGE_DATA: data = -page; break;
+	    case  END_PAGE_DATA: data = sb_info->max; break;
+	    case -END_PAGE_DATA: data = -sb_info->max; break;
+			default: data = 0; break;
+	}
+#endif
+    }
+    else			/* Bottom scrollbar */
+    {
+	sb_info = sb;
+#ifdef FEAT_GUI_NEXTAW
+	if (data < 0)
+	{
+	    data = (data - gui.char_width + 1) / gui.char_width;
+	    if (data > -sb->size)
+		data = -1;
+	}
+	else if (data > 0)
+	{
+	    data = (data + gui.char_width - 1) / gui.char_width;
+	    if (data < sb->size)
+		data = 1;
+	}
+#endif
+	if (data < -1)		/* page-width left */
+	{
+	    if (sb->size > 8)
+		data = -(sb->size - 5);
+	    else
+		data = -sb->size;
+	}
+	else if (data > 1)	/* page-width right */
+	{
+	    if (sb->size > 8)
+		data = (sb->size - 5);
+	    else
+		data = sb->size;
+	}
+    }
+
+    value = sb_info->value + data;
+    if (value > sb_info->max)
+	value = sb_info->max;
+    else if (value < 0)
+	value = 0;
+
+    /* Update the bottom scrollbar an extra time (why is this needed?? */
+    if (sb->wp == NULL)		/* Bottom scrollbar */
+	gui_mch_set_scrollbar_thumb(sb, value, sb->size, sb->max);
+
+    gui_drag_scrollbar(sb, value, FALSE);
+}
+
+/*
+ * Create all the Athena widgets necessary.
+ */
+    void
+gui_x11_create_widgets()
+{
+    /*
+     * We don't have any borders handled internally by the textArea to worry
+     * about so only skip over the configured border width.
+     */
+    gui.border_offset = gui.border_width;
+
+#if 0 /* not needed? */
+    XtInitializeWidgetClass(formWidgetClass);
+    XtInitializeWidgetClass(boxWidgetClass);
+    XtInitializeWidgetClass(coreWidgetClass);
+#ifdef FEAT_MENU
+    XtInitializeWidgetClass(menuButtonWidgetClass);
+#endif
+    XtInitializeWidgetClass(simpleMenuWidgetClass);
+#ifdef FEAT_GUI_NEXTAW
+    XtInitializeWidgetClass(scrollbarWidgetClass);
+#else
+    XtInitializeWidgetClass(vim_scrollbarWidgetClass);
+#endif
+#endif
+
+    /* The form containing all the other widgets */
+    vimForm = XtVaCreateManagedWidget("vimForm",
+	formWidgetClass,	vimShell,
+	XtNborderWidth,		0,
+	NULL);
+    gui_athena_scroll_colors(vimForm);
+
+#ifdef FEAT_MENU
+    /* The top menu bar */
+    menuBar = XtVaCreateManagedWidget("menuBar",
+	boxWidgetClass,		vimForm,
+	XtNresizable,		True,
+	XtNtop,			XtChainTop,
+	XtNbottom,		XtChainTop,
+	XtNleft,		XtChainLeft,
+	XtNright,		XtChainRight,
+	XtNinsertPosition,	athena_calculate_ins_pos,
+	NULL);
+    gui_athena_menu_colors(menuBar);
+    if (gui.menu_fg_pixel != INVALCOLOR)
+	XtVaSetValues(menuBar, XtNborderColor, gui.menu_fg_pixel, NULL);
+#endif
+
+#ifdef FEAT_TOOLBAR
+    /* Don't create it Managed, it will be managed when creating the first
+     * item.  Otherwise an empty toolbar shows up. */
+    toolBar = XtVaCreateWidget("toolBar",
+	boxWidgetClass,		vimForm,
+	XtNresizable,		True,
+	XtNtop,			XtChainTop,
+	XtNbottom,		XtChainTop,
+	XtNleft,		XtChainLeft,
+	XtNright,		XtChainRight,
+	XtNorientation,		XtorientHorizontal,
+	XtNhSpace,		1,
+	XtNvSpace,		3,
+	XtNinsertPosition,	athena_calculate_ins_pos,
+	NULL);
+    gui_athena_menu_colors(toolBar);
+#endif
+
+    /* The text area. */
+    textArea = XtVaCreateManagedWidget("textArea",
+	coreWidgetClass,	vimForm,
+	XtNresizable,		True,
+	XtNtop,			XtChainTop,
+	XtNbottom,		XtChainTop,
+	XtNleft,		XtChainLeft,
+	XtNright,		XtChainLeft,
+	XtNbackground,		gui.back_pixel,
+	XtNborderWidth,		0,
+	NULL);
+
+    /*
+     * Install the callbacks.
+     */
+    gui_x11_callbacks(textArea, vimForm);
+
+#ifdef FEAT_MENU
+    popupTrans = XtParseTranslationTable(
+	    "<EnterWindow>: menu-popdownsubmenus() highlight() menu-delayedpopup()\n"
+	    "<LeaveWindow>: unhighlight()\n"
+	    "<BtnUp>: menu-popdownsubmenus() XtMenuPopdown() notify() unhighlight()\n"
+	    "<Motion>: highlight() menu-delayedpopup()");
+    parentTrans = XtParseTranslationTable("<LeaveWindow>: unhighlight()");
+    menuTrans = XtParseTranslationTable(
+	    "<EnterWindow>: menu-popdownsubmenus() highlight() menu-delayedpopup()\n"
+	    "<LeaveWindow>: menu-popdownsubmenus() XtMenuPopdown() unhighlight()\n"
+	    "<BtnUp>: notify() unhighlight()\n"
+	    "<BtnMotion>: highlight() menu-delayedpopup()");
+    supermenuTrans = XtParseTranslationTable(
+	    "<EnterWindow>: menu-popdownsubmenus() highlight() menu-delayedpopup()\n"
+	    "<LeaveWindow>: unhighlight()\n"
+	    "<BtnUp>: menu-popdownsubmenus() XtMenuPopdown() notify() unhighlight()\n"
+	    "<BtnMotion>: highlight() menu-delayedpopup()");
+
+    XtAppAddActions(XtWidgetToApplicationContext(vimForm), pullAction,
+		    XtNumber(pullAction));
+#endif
+
+    /* Pretend we don't have input focus, we will get an event if we do. */
+    gui.in_focus = FALSE;
+}
+
+#ifdef FEAT_MENU
+/*
+ * Calculates the Pixmap based on the size of the current menu font.
+ */
+    static Pixmap
+gui_athena_create_pullright_pixmap(w)
+    Widget  w;
+{
+    Pixmap  retval;
+#ifdef FONTSET_ALWAYS
+    XFontSet	font = None;
+#else
+    XFontStruct	*font = NULL;
+#endif
+
+#ifdef FONTSET_ALWAYS
+    if (gui.menu_fontset == NOFONTSET)
+#else
+    if (gui.menu_font == NOFONT)
+#endif
+    {
+	XrmValue from, to;
+	WidgetList  children;
+	Cardinal    num_children;
+
+#ifdef FONTSET_ALWAYS
+	from.size = strlen(from.addr = XtDefaultFontSet);
+	to.addr = (XtPointer)&font;
+	to.size = sizeof(XFontSet);
+#else
+	from.size = strlen(from.addr = XtDefaultFont);
+	to.addr = (XtPointer)&font;
+	to.size = sizeof(XFontStruct *);
+#endif
+	/* Assumption: The menuBar children will use the same font as the
+	 *	       pulldown menu items AND they will all be of type
+	 *	       XtNfont.
+	 */
+	XtVaGetValues(menuBar, XtNchildren, &children,
+			       XtNnumChildren, &num_children,
+			       NULL);
+	if (XtConvertAndStore(w ? w :
+				(num_children > 0) ? children[0] : menuBar,
+			      XtRString, &from,
+#ifdef FONTSET_ALWAYS
+			      XtRFontSet, &to
+#else
+			      XtRFontStruct, &to
+#endif
+		    ) == False)
+	    return None;
+	/* "font" should now contain data */
+    }
+    else
+#ifdef FONTSET_ALWAYS
+	font = (XFontSet)gui.menu_fontset;
+#else
+	font = (XFontStruct *)gui.menu_font;
+#endif
+
+    {
+	int	    width, height;
+	GC	    draw_gc, undraw_gc;
+	XGCValues   gc_values;
+	XPoint	    points[3];
+
+#ifdef FONTSET_ALWAYS
+	height = fontset_height2(font);
+#else
+	height = font->max_bounds.ascent + font->max_bounds.descent;
+#endif
+	width = height - 2;
+	puller_width = width + 4;
+	retval = XCreatePixmap(gui.dpy,DefaultRootWindow(gui.dpy),width,
+			       height, 1);
+	gc_values.foreground = 1;
+	gc_values.background = 0;
+	draw_gc = XCreateGC(gui.dpy, retval,
+		    GCForeground | GCBackground,
+		    &gc_values);
+	gc_values.foreground = 0;
+	gc_values.background = 1;
+	undraw_gc = XCreateGC(gui.dpy, retval,
+			      GCForeground | GCBackground,
+			      &gc_values);
+	points[0].x = 0;
+	points[0].y = 0;
+	points[1].x = width - 1;
+	points[1].y = (height - 1) / 2;
+	points[2].x = 0;
+	points[2].y = height - 1;
+	XFillRectangle(gui.dpy, retval, undraw_gc, 0, 0, height, height);
+	XFillPolygon(gui.dpy, retval, draw_gc, points, XtNumber(points),
+		     Convex, CoordModeOrigin);
+	XFreeGC(gui.dpy, draw_gc);
+	XFreeGC(gui.dpy, undraw_gc);
+    }
+    return retval;
+}
+#endif
+
+/*
+ * Called when the GUI is not going to start after all.
+ */
+    void
+gui_x11_destroy_widgets()
+{
+    textArea = NULL;
+#ifdef FEAT_MENU
+    menuBar = NULL;
+#endif
+#ifdef FEAT_TOOLBAR
+    toolBar = NULL;
+#endif
+}
+
+#if defined(FEAT_TOOLBAR) || defined(PROTO)
+    void
+gui_mch_set_toolbar_pos(x, y, w, h)
+    int	    x;
+    int	    y;
+    int	    w;
+    int	    h;
+{
+    Dimension	border;
+    int		height;
+
+    if (!XtIsManaged(toolBar))	/* nothing to do */
+	return;
+    XtUnmanageChild(toolBar);
+    XtVaGetValues(toolBar,
+		XtNborderWidth, &border,
+		NULL);
+    height = h - 2 * border;
+    if (height < 0)
+	height = 1;
+    XtVaSetValues(toolBar,
+		  XtNhorizDistance, x,
+		  XtNvertDistance, y,
+		  XtNwidth, w - 2 * border,
+		  XtNheight,	height,
+		  NULL);
+    XtManageChild(toolBar);
+}
+#endif
+
+    void
+gui_mch_set_text_area_pos(x, y, w, h)
+    int	    x;
+    int	    y;
+    int	    w;
+    int	    h;
+{
+    XtUnmanageChild(textArea);
+    XtVaSetValues(textArea,
+		  XtNhorizDistance, x,
+		  XtNvertDistance, y,
+		  XtNwidth, w,
+		  XtNheight, h,
+		  NULL);
+    XtManageChild(textArea);
+#ifdef FEAT_TOOLBAR
+    /* Give keyboard focus to the textArea instead of the toolbar. */
+    gui_mch_reset_focus();
+#endif
+}
+
+#ifdef FEAT_TOOLBAR
+/*
+ * A toolbar button has been pushed; now reset the input focus
+ * such that the user can type page up/down etc. and have the
+ * input go to the editor window, not the button
+ */
+    static void
+gui_mch_reset_focus()
+{
+    XtSetKeyboardFocus(vimForm, textArea);
+}
+#endif
+
+
+    void
+gui_x11_set_back_color()
+{
+    if (textArea != NULL)
+	XtVaSetValues(textArea,
+		  XtNbackground, gui.back_pixel,
+		  NULL);
+}
+
+#if defined(FEAT_MENU) || defined(PROTO)
+/*
+ * Menu stuff.
+ */
+
+static char_u	*make_pull_name __ARGS((char_u * name));
+static Widget	get_popup_entry __ARGS((Widget w));
+static Widget	submenu_widget __ARGS((Widget));
+static Boolean	has_submenu __ARGS((Widget));
+static void gui_mch_submenu_change __ARGS((vimmenu_T *mp, int colors));
+static void gui_athena_menu_font __ARGS((Widget id));
+static Boolean	gui_athena_menu_has_submenus __ARGS((Widget, Widget));
+
+    void
+gui_mch_enable_menu(flag)
+    int	    flag;
+{
+    if (flag)
+    {
+	XtManageChild(menuBar);
+# ifdef FEAT_TOOLBAR
+	if (XtIsManaged(toolBar))
+	{
+	    XtVaSetValues(toolBar,
+		XtNvertDistance,    gui.menu_height,
+		NULL);
+	    XtVaSetValues(textArea,
+		XtNvertDistance,    gui.menu_height + gui.toolbar_height,
+		NULL);
+	}
+# endif
+    }
+    else
+    {
+	XtUnmanageChild(menuBar);
+# ifdef FEAT_TOOLBAR
+	if (XtIsManaged(toolBar))
+	{
+	    XtVaSetValues(toolBar,
+		XtNvertDistance,    0,
+		NULL);
+	}
+# endif
+    }
+}
+
+    void
+gui_mch_set_menu_pos(x, y, w, h)
+    int	    x;
+    int	    y;
+    int	    w;
+    int	    h;
+{
+    Dimension	border;
+    int		height;
+
+    XtUnmanageChild(menuBar);
+    XtVaGetValues(menuBar, XtNborderWidth, &border, NULL);
+    /* avoid trouble when there are no menu items, and h is 1 */
+    height = h - 2 * border;
+    if (height < 0)
+	height = 1;
+    XtVaSetValues(menuBar,
+		XtNhorizDistance, x,
+		XtNvertDistance, y,
+		XtNwidth, w - 2 * border,
+		XtNheight, height,
+		NULL);
+    XtManageChild(menuBar);
+}
+
+/*
+ * Used to calculate the insertion position of a widget with respect to its
+ * neighbors.
+ *
+ * Valid range of return values is: 0 (beginning of children) to
+ *				    numChildren (end of children).
+ */
+    static Cardinal
+athena_calculate_ins_pos(widget)
+    Widget	widget;
+{
+    /* Assume that if the parent of the vimmenu_T is NULL, then we can get
+     * to this menu by traversing "next", starting at "root_menu".
+     *
+     * This holds true for popup menus, toolbar, and toplevel menu items.
+     */
+
+    /* Popup menus:  "id" is NULL. Only submenu_id is valid */
+
+    /* Menus that are not toplevel: "parent" will be non-NULL, "id" &
+     * "submenu_id" will be non-NULL.
+     */
+
+    /* Toplevel menus: "parent" is NULL, id is the widget of the menu item */
+
+    WidgetList	children;
+    Cardinal	num_children = 0;
+    int		retval;
+    Arg		args[2];
+    int		n = 0;
+    int		i;
+
+    XtSetArg(args[n], XtNchildren, &children); n++;
+    XtSetArg(args[n], XtNnumChildren, &num_children); n++;
+    XtGetValues(XtParent(widget), args, n);
+
+    retval = num_children;
+    for (i = 0; i < num_children; ++i)
+    {
+	Widget	current = children[i];
+	vimmenu_T	*menu = NULL;
+
+	for (menu = (a_cur_menu->parent == NULL)
+			       ? root_menu : a_cur_menu->parent->children;
+			       menu != NULL;
+			       menu = menu->next)
+	    if (current == menu->id
+		    && a_cur_menu->priority < menu->priority
+		    && i < retval)
+		retval = i;
+    }
+    return retval;
+}
+
+/* ARGSUSED */
+    void
+gui_mch_add_menu(menu, idx)
+    vimmenu_T	*menu;
+    int		idx;
+{
+    char_u	*pullright_name;
+    Dimension	height, space, border;
+    vimmenu_T	*parent = menu->parent;
+
+    a_cur_menu = menu;
+    if (parent == NULL)
+    {
+	if (menu_is_popup(menu->dname))
+	{
+	    menu->submenu_id = XtVaCreatePopupShell((char *)menu->dname,
+		simpleMenuWidgetClass,	vimShell,
+		XtNinsertPosition,	athena_calculate_ins_pos,
+		XtNtranslations,	popupTrans,
+		NULL);
+	    gui_athena_menu_colors(menu->submenu_id);
+	}
+	else if (menu_is_menubar(menu->dname))
+	{
+	    menu->id = XtVaCreateManagedWidget((char *)menu->dname,
+		menuButtonWidgetClass, menuBar,
+		XtNmenuName,	    menu->dname,
+#ifdef FONTSET_ALWAYS
+		XtNinternational,   True,
+#endif
+		NULL);
+	    if (menu->id == (Widget)0)
+		return;
+	    gui_athena_menu_colors(menu->id);
+	    gui_athena_menu_font(menu->id);
+
+	    menu->submenu_id = XtVaCreatePopupShell((char *)menu->dname,
+		simpleMenuWidgetClass, menu->id,
+		XtNinsertPosition,	athena_calculate_ins_pos,
+		XtNtranslations,	supermenuTrans,
+		NULL);
+	    gui_athena_menu_colors(menu->submenu_id);
+	    gui_athena_menu_font(menu->submenu_id);
+
+	    /* Don't update the menu height when it was set at a fixed value */
+	    if (!gui.menu_height_fixed)
+	    {
+		/*
+		 * When we add a top-level item to the menu bar, we can figure
+		 * out how high the menu bar should be.
+		 */
+		XtVaGetValues(menuBar,
+			XtNvSpace,	&space,
+			XtNborderWidth, &border,
+			NULL);
+		XtVaGetValues(menu->id,
+			XtNheight,	&height,
+			NULL);
+		gui.menu_height = height + 2 * (space + border);
+	    }
+	}
+    }
+    else if (parent->submenu_id != (Widget)0)
+    {
+	menu->id = XtVaCreateManagedWidget((char *)menu->dname,
+	    smeBSBObjectClass, parent->submenu_id,
+	    XtNlabel, menu->dname,
+#ifdef FONTSET_ALWAYS
+	    XtNinternational,	True,
+#endif
+	    NULL);
+	if (menu->id == (Widget)0)
+	    return;
+	if (pullerBitmap == None)
+	    pullerBitmap = gui_athena_create_pullright_pixmap(menu->id);
+
+	XtVaSetValues(menu->id, XtNrightBitmap, pullerBitmap,
+				NULL);
+	/* If there are other menu items that are not pulldown menus,
+	 * we need to adjust the right margins of those, too.
+	 */
+	{
+	    WidgetList	children;
+	    Cardinal	num_children;
+	    int		i;
+
+	    XtVaGetValues(parent->submenu_id, XtNchildren, &children,
+					      XtNnumChildren, &num_children,
+					      NULL);
+	    for (i = 0; i < num_children; ++i)
+	    {
+		XtVaSetValues(children[i],
+			      XtNrightMargin, puller_width,
+			      NULL);
+	    }
+	}
+	gui_athena_menu_colors(menu->id);
+	gui_athena_menu_font(menu->id);
+
+	pullright_name = make_pull_name(menu->dname);
+	menu->submenu_id = XtVaCreatePopupShell((char *)pullright_name,
+	    simpleMenuWidgetClass, parent->submenu_id,
+	    XtNtranslations, menuTrans,
+	    NULL);
+	gui_athena_menu_colors(menu->submenu_id);
+	gui_athena_menu_font(menu->submenu_id);
+	vim_free(pullright_name);
+	XtAddCallback(menu->submenu_id, XtNpopupCallback,
+		      gui_athena_popup_callback, (XtPointer)menu);
+
+	if (parent->parent != NULL)
+	    XtOverrideTranslations(parent->submenu_id, parentTrans);
+    }
+    a_cur_menu = NULL;
+}
+
+/* Used to determine whether a SimpleMenu has pulldown entries.
+ *
+ * "id" is the parent of the menu items.
+ * Ignore widget "ignore" in the pane.
+ */
+    static Boolean
+gui_athena_menu_has_submenus(id, ignore)
+    Widget	id;
+    Widget	ignore;
+{
+    WidgetList	children;
+    Cardinal	num_children;
+    int		i;
+
+    XtVaGetValues(id, XtNchildren, &children,
+		      XtNnumChildren, &num_children,
+		      NULL);
+    for (i = 0; i < num_children; ++i)
+    {
+	if (children[i] == ignore)
+	    continue;
+	if (has_submenu(children[i]))
+	    return True;
+    }
+    return False;
+}
+
+    static void
+gui_athena_menu_font(id)
+    Widget	id;
+{
+#ifdef FONTSET_ALWAYS
+    if (gui.menu_fontset != NOFONTSET)
+    {
+	if (XtIsManaged(id))
+	{
+	    XtUnmanageChild(id);
+	    XtVaSetValues(id, XtNfontSet, gui.menu_fontset, NULL);
+	    /* We should force the widget to recalculate it's
+	     * geometry now. */
+	    XtManageChild(id);
+	}
+	else
+	    XtVaSetValues(id, XtNfontSet, gui.menu_fontset, NULL);
+	if (has_submenu(id))
+	    XtVaSetValues(id, XtNrightBitmap, pullerBitmap, NULL);
+    }
+#else
+    int		managed = FALSE;
+
+    if (gui.menu_font != NOFONT)
+    {
+	if (XtIsManaged(id))
+	{
+	    XtUnmanageChild(id);
+	    managed = TRUE;
+	}
+
+# ifdef FEAT_XFONTSET
+	if (gui.fontset != NOFONTSET)
+	    XtVaSetValues(id, XtNfontSet, gui.menu_font, NULL);
+	else
+# endif
+	    XtVaSetValues(id, XtNfont, gui.menu_font, NULL);
+	if (has_submenu(id))
+	    XtVaSetValues(id, XtNrightBitmap, pullerBitmap, NULL);
+
+	/* Force the widget to recalculate it's geometry now. */
+	if (managed)
+	    XtManageChild(id);
+    }
+#endif
+}
+
+
+    void
+gui_mch_new_menu_font()
+{
+    Pixmap oldpuller = None;
+
+    if (menuBar == (Widget)0)
+	return;
+
+    if (pullerBitmap != None)
+    {
+	oldpuller = pullerBitmap;
+	pullerBitmap = gui_athena_create_pullright_pixmap(NULL);
+    }
+    gui_mch_submenu_change(root_menu, FALSE);
+
+    {
+	/* Iterate through the menubar menu items and get the height of
+	 * each one.  The menu bar height is set to the maximum of all
+	 * the heights.
+	 */
+	vimmenu_T *mp;
+	int max_height = 9999;
+
+	for (mp = root_menu; mp != NULL; mp = mp->next)
+	{
+	    if (menu_is_menubar(mp->dname))
+	    {
+		Dimension height;
+
+		XtVaGetValues(mp->id,
+			XtNheight,(XtArgVal *)&height,
+			NULL);
+		if (height < max_height)
+		    max_height = height;
+	    }
+	}
+	if (max_height != 9999)
+	{
+	    /* Don't update the menu height when it was set at a fixed value */
+	    if (!gui.menu_height_fixed)
+	    {
+		Dimension   space, border;
+
+		XtVaGetValues(menuBar,
+			XtNvSpace,	&space,
+			XtNborderWidth, &border,
+			NULL);
+		gui.menu_height = max_height + 2 * (space + border);
+	    }
+	}
+    }
+    /* Now, to simulate the window being resized.  Only, this
+     * will resize the window to it's current state.
+     *
+     * There has to be a better way, but I do not see one at this time.
+     * (David Harrison)
+     */
+    {
+	Position w, h;
+
+	XtVaGetValues(vimShell,
+		XtNwidth, &w,
+		XtNheight, &h,
+		NULL);
+	gui_resize_shell(w, h
+#ifdef FEAT_XIM
+						- xim_get_status_area_height()
+#endif
+		     );
+    }
+    gui_set_shellsize(FALSE, TRUE);
+    ui_new_shellsize();
+    if (oldpuller != None)
+	XFreePixmap(gui.dpy, oldpuller);
+}
+
+#if defined(FEAT_BEVAL) || defined(PROTO)
+    void
+gui_mch_new_tooltip_font()
+{
+#  ifdef FEAT_TOOLBAR
+    vimmenu_T   *menu;
+
+    if (toolBar == (Widget)0)
+	return;
+
+    menu = gui_find_menu((char_u *)"ToolBar");
+    if (menu != NULL)
+	gui_mch_submenu_change(menu, FALSE);
+#  endif
+}
+
+    void
+gui_mch_new_tooltip_colors()
+{
+# ifdef FEAT_TOOLBAR
+    vimmenu_T   *menu;
+
+    if (toolBar == (Widget)0)
+	return;
+
+    menu = gui_find_menu((char_u *)"ToolBar");
+    if (menu != NULL)
+	gui_mch_submenu_change(menu, TRUE);
+# endif
+}
+#endif
+
+    static void
+gui_mch_submenu_change(menu, colors)
+    vimmenu_T	*menu;
+    int		colors;		/* TRUE for colors, FALSE for font */
+{
+    vimmenu_T	*mp;
+
+    for (mp = menu; mp != NULL; mp = mp->next)
+    {
+	if (mp->id != (Widget)0)
+	{
+	    if (colors)
+	    {
+		gui_athena_menu_colors(mp->id);
+#ifdef FEAT_TOOLBAR
+		/* For a toolbar item: Free the pixmap and allocate a new one,
+		 * so that the background color is right. */
+		if (mp->image != (Pixmap)0)
+		{
+		    XFreePixmap(gui.dpy, mp->image);
+		    get_toolbar_pixmap(mp, &mp->image, NULL);
+		    if (mp->image != (Pixmap)0)
+			XtVaSetValues(mp->id, XtNbitmap, mp->image, NULL);
+		}
+
+# ifdef FEAT_BEVAL
+		/* If we have a tooltip, then we need to change it's colors */
+		if (mp->tip != NULL)
+		{
+		    Arg args[2];
+
+		    args[0].name = XtNbackground;
+		    args[0].value = gui.tooltip_bg_pixel;
+		    args[1].name = XtNforeground;
+		    args[1].value = gui.tooltip_fg_pixel;
+		    XtSetValues(mp->tip->balloonLabel, &args[0], XtNumber(args));
+		}
+# endif
+#endif
+	    }
+	    else
+	    {
+		gui_athena_menu_font(mp->id);
+#ifdef FEAT_BEVAL
+		/* If we have a tooltip, then we need to change it's font */
+		/* Assume XtNinternational == True (in createBalloonEvalWindow)
+		 */
+		if (mp->tip != NULL)
+		{
+		    Arg args[1];
+
+		    args[0].name = XtNfontSet;
+		    args[0].value = (XtArgVal)gui.tooltip_fontset;
+		    XtSetValues(mp->tip->balloonLabel, &args[0], XtNumber(args));
+		}
+#endif
+	    }
+	}
+
+	if (mp->children != NULL)
+	{
+	    /* Set the colors/font for the tear off widget */
+	    if (mp->submenu_id != (Widget)0)
+	    {
+		if (colors)
+		    gui_athena_menu_colors(mp->submenu_id);
+		else
+		    gui_athena_menu_font(mp->submenu_id);
+	    }
+	    /* Set the colors for the children */
+	    gui_mch_submenu_change(mp->children, colors);
+	}
+    }
+}
+
+/*
+ * Make a submenu name into a pullright name.
+ * Replace '.' by '_', can't include '.' in the submenu name.
+ */
+    static char_u *
+make_pull_name(name)
+    char_u * name;
+{
+    char_u  *pname;
+    char_u  *p;
+
+    pname = vim_strnsave(name, STRLEN(name) + strlen("-pullright"));
+    if (pname != NULL)
+    {
+	strcat((char *)pname, "-pullright");
+	while ((p = vim_strchr(pname, '.')) != NULL)
+	    *p = '_';
+    }
+    return pname;
+}
+
+/* ARGSUSED */
+    void
+gui_mch_add_menu_item(menu, idx)
+    vimmenu_T	*menu;
+    int		idx;
+{
+    vimmenu_T	*parent = menu->parent;
+
+    a_cur_menu = menu;
+# ifdef FEAT_TOOLBAR
+    if (menu_is_toolbar(parent->name))
+    {
+	WidgetClass	type;
+	int		n;
+	Arg		args[21];
+
+	n = 0;
+	if (menu_is_separator(menu->name))
+	{
+	    XtSetArg(args[n], XtNlabel, ""); n++;
+	    XtSetArg(args[n], XtNborderWidth, 0); n++;
+	}
+	else
+	{
+	    get_toolbar_pixmap(menu, &menu->image, NULL);
+	    XtSetArg(args[n], XtNlabel, menu->dname); n++;
+	    XtSetArg(args[n], XtNinternalHeight, 1); n++;
+	    XtSetArg(args[n], XtNinternalWidth, 1); n++;
+	    XtSetArg(args[n], XtNborderWidth, 1); n++;
+	    if (menu->image != 0)
+		XtSetArg(args[n], XtNbitmap, menu->image); n++;
+	}
+	XtSetArg(args[n], XtNhighlightThickness, 0); n++;
+	type = commandWidgetClass;
+	/* TODO: figure out the position in the toolbar?
+	 *       This currently works fine for the default toolbar, but
+	 *       what if we add/remove items during later runtime?
+	 */
+
+	/* NOTE: "idx" isn't used here.  The position is calculated by
+	 *       athena_calculate_ins_pos().  The position it calculates
+	 *       should be equal to "idx".
+	 */
+	/* TODO: Could we just store "idx" and use that as the child
+	 * placement?
+	 */
+
+	if (menu->id == NULL)
+	{
+	    menu->id = XtCreateManagedWidget((char *)menu->dname,
+			type, toolBar, args, n);
+	    XtAddCallback(menu->id,
+		    XtNcallback, gui_x11_menu_cb, menu);
+	}
+	else
+	    XtSetValues(menu->id, args, n);
+	gui_athena_menu_colors(menu->id);
+
+#ifdef FEAT_BEVAL
+	gui_mch_menu_set_tip(menu);
+#endif
+
+	menu->parent = parent;
+	menu->submenu_id = NULL;
+	if (!XtIsManaged(toolBar)
+		    && vim_strchr(p_go, GO_TOOLBAR) != NULL)
+	    gui_mch_show_toolbar(TRUE);
+	gui.toolbar_height = gui_mch_compute_toolbar_height();
+	return;
+    } /* toolbar menu item */
+# endif
+
+    /* Add menu separator */
+    if (menu_is_separator(menu->name))
+    {
+	menu->submenu_id = (Widget)0;
+	menu->id = XtVaCreateManagedWidget((char *)menu->dname,
+		smeLineObjectClass, parent->submenu_id,
+		NULL);
+	if (menu->id == (Widget)0)
+	    return;
+	gui_athena_menu_colors(menu->id);
+    }
+    else
+    {
+	if (parent != NULL && parent->submenu_id != (Widget)0)
+	{
+	    menu->submenu_id = (Widget)0;
+	    menu->id = XtVaCreateManagedWidget((char *)menu->dname,
+		    smeBSBObjectClass, parent->submenu_id,
+		    XtNlabel, menu->dname,
+#ifdef FONTSET_ALWAYS
+		    XtNinternational,	True,
+#endif
+		    NULL);
+	    if (menu->id == (Widget)0)
+		return;
+
+	    /* If there are other "pulldown" items in this pane, then adjust
+	     * the right margin to accomodate the arrow pixmap, otherwise
+	     * the right margin will be the same as the left margin.
+	     */
+	    {
+		Dimension   left_margin;
+
+		XtVaGetValues(menu->id, XtNleftMargin, &left_margin, NULL);
+		XtVaSetValues(menu->id, XtNrightMargin,
+			gui_athena_menu_has_submenus(parent->submenu_id, NULL) ?
+			    puller_width :
+			    left_margin,
+			NULL);
+	    }
+
+	    gui_athena_menu_colors(menu->id);
+	    gui_athena_menu_font(menu->id);
+	    XtAddCallback(menu->id, XtNcallback, gui_x11_menu_cb,
+		    (XtPointer)menu);
+	}
+    }
+    a_cur_menu = NULL;
+}
+
+#if defined(FEAT_TOOLBAR) || defined(PROTO)
+    void
+gui_mch_show_toolbar(int showit)
+{
+    Cardinal	numChildren;	    /* how many children toolBar has */
+
+    if (toolBar == (Widget)0)
+	return;
+    XtVaGetValues(toolBar, XtNnumChildren, &numChildren, NULL);
+    if (showit && numChildren > 0)
+    {
+	/* Assume that we want to show the toolbar if p_toolbar contains valid
+	 * option settings, therefore p_toolbar must not be NULL.
+	 */
+	WidgetList  children;
+
+	XtVaGetValues(toolBar, XtNchildren, &children, NULL);
+	{
+	    void    (*action)(BalloonEval *);
+	    int	    text = 0;
+
+	    if (strstr((const char *)p_toolbar, "tooltips"))
+		action = &gui_mch_enable_beval_area;
+	    else
+		action = &gui_mch_disable_beval_area;
+	    if (strstr((const char *)p_toolbar, "text"))
+		text = 1;
+	    else if (strstr((const char *)p_toolbar, "icons"))
+		text = -1;
+	    if (text != 0)
+	    {
+		vimmenu_T   *toolbar;
+		vimmenu_T   *cur;
+
+		for (toolbar = root_menu; toolbar; toolbar = toolbar->next)
+		    if (menu_is_toolbar(toolbar->dname))
+			break;
+		/* Assumption: toolbar is NULL if there is no toolbar,
+		 *	       otherwise it contains the toolbar menu structure.
+		 *
+		 * Assumption: "numChildren" == the number of items in the list
+		 *	       of items beginning with toolbar->children.
+		 */
+		if (toolbar)
+		{
+		    for (cur = toolbar->children; cur; cur = cur->next)
+		    {
+			Arg	    args[2];
+			int	    n = 0;
+
+			/* Enable/Disable tooltip (OK to enable while currently
+			 * enabled)
+			 */
+			if (cur->tip != NULL)
+			    (*action)(cur->tip);
+			if (text == 1)
+			{
+			    XtSetArg(args[n], XtNbitmap, None);
+			    n++;
+			    XtSetArg(args[n], XtNlabel,
+				    menu_is_separator(cur->name) ? "" :
+					(char *)cur->dname);
+			    n++;
+			}
+			else
+			{
+			    XtSetArg(args[n], XtNbitmap, cur->image);
+			    n++;
+			    XtSetArg(args[n], XtNlabel, (cur->image == None) ?
+				    menu_is_separator(cur->name) ?
+					"" :
+					(char *)cur->dname
+				    :
+				    (char *)None);
+			    n++;
+			}
+			if (cur->id != NULL)
+			{
+			    XtUnmanageChild(cur->id);
+			    XtSetValues(cur->id, args, n);
+			    XtManageChild(cur->id);
+			}
+		    }
+		}
+	    }
+	}
+	gui.toolbar_height = gui_mch_compute_toolbar_height();
+	XtManageChild(toolBar);
+	if (XtIsManaged(menuBar))
+	{
+	    XtVaSetValues(textArea,
+		    XtNvertDistance,    gui.toolbar_height + gui.menu_height,
+		    NULL);
+	    XtVaSetValues(toolBar,
+		    XtNvertDistance,    gui.menu_height,
+		    NULL);
+	}
+	else
+	{
+	    XtVaSetValues(textArea,
+		    XtNvertDistance,    gui.toolbar_height,
+		    NULL);
+	    XtVaSetValues(toolBar,
+		    XtNvertDistance,    0,
+		    NULL);
+	}
+    }
+    else
+    {
+	gui.toolbar_height = 0;
+	if (XtIsManaged(menuBar))
+	    XtVaSetValues(textArea,
+		XtNvertDistance,    gui.menu_height,
+		NULL);
+	else
+	    XtVaSetValues(textArea,
+		XtNvertDistance,    0,
+		NULL);
+
+	XtUnmanageChild(toolBar);
+    }
+    gui_set_shellsize(FALSE, FALSE);
+}
+
+
+    int
+gui_mch_compute_toolbar_height()
+{
+    Dimension	height;		    /* total Toolbar height */
+    Dimension	whgt;		    /* height of each widget */
+    Dimension	marginHeight;	    /* XmNmarginHeight of toolBar */
+    Dimension	shadowThickness;    /* thickness of Xtparent(toolBar) */
+    WidgetList	children;	    /* list of toolBar's children */
+    Cardinal	numChildren;	    /* how many children toolBar has */
+    int		i;
+
+    height = 0;
+    shadowThickness = 0;
+    marginHeight = 0;
+    if (toolBar != (Widget)0)
+    {
+	XtVaGetValues(toolBar,
+		XtNborderWidth,	    &shadowThickness,
+		XtNvSpace,	    &marginHeight,
+		XtNchildren,	    &children,
+		XtNnumChildren,	    &numChildren,
+		NULL);
+	for (i = 0; i < numChildren; i++)
+	{
+	    whgt = 0;
+
+	    XtVaGetValues(children[i], XtNheight, &whgt, NULL);
+	    if (height < whgt)
+		height = whgt;
+	}
+    }
+
+    return (int)(height + (marginHeight << 1) + (shadowThickness << 1));
+}
+
+    void
+gui_mch_get_toolbar_colors(bgp, fgp, bsp, tsp, hsp)
+    Pixel	*bgp;
+    Pixel	*fgp;
+    Pixel       *bsp;
+    Pixel	*tsp;
+    Pixel	*hsp;
+{
+    XtVaGetValues(toolBar, XtNbackground, bgp, XtNborderColor, fgp, NULL);
+    *bsp = *bgp;
+    *tsp = *fgp;
+    *hsp = *tsp;
+}
+#endif
+
+
+/* ARGSUSED */
+    void
+gui_mch_toggle_tearoffs(enable)
+    int		enable;
+{
+    /* no tearoff menus */
+}
+
+    void
+gui_mch_new_menu_colors()
+{
+    if (menuBar == (Widget)0)
+	return;
+    if (gui.menu_fg_pixel != INVALCOLOR)
+	XtVaSetValues(menuBar, XtNborderColor,	gui.menu_fg_pixel, NULL);
+    gui_athena_menu_colors(menuBar);
+#ifdef FEAT_TOOLBAR
+    gui_athena_menu_colors(toolBar);
+#endif
+
+    gui_mch_submenu_change(root_menu, TRUE);
+}
+
+/*
+ * Destroy the machine specific menu widget.
+ */
+    void
+gui_mch_destroy_menu(menu)
+    vimmenu_T *menu;
+{
+    Widget	parent;
+
+    /* There is no item for the toolbar. */
+    if (menu->id == (Widget)0)
+	return;
+
+    parent = XtParent(menu->id);
+
+    /* When removing the last "pulldown" menu item from a pane, adjust the
+     * right margins of the remaining widgets.
+     */
+    if (menu->submenu_id != (Widget)0)
+    {
+	/* Go through the menu items in the parent of this item and
+	 * adjust their margins, if necessary.
+	 * This takes care of the case when we delete the last menu item in a
+	 * pane that has a submenu.  In this case, there will be no arrow
+	 * pixmaps shown anymore.
+	 */
+	{
+	    WidgetList  children;
+	    Cardinal    num_children;
+	    int		i;
+	    Dimension	right_margin = 0;
+	    Boolean	get_left_margin = False;
+
+	    XtVaGetValues(parent, XtNchildren, &children,
+				  XtNnumChildren, &num_children,
+				  NULL);
+	    if (gui_athena_menu_has_submenus(parent, menu->id))
+		right_margin = puller_width;
+	    else
+		get_left_margin = True;
+
+	    for (i = 0; i < num_children; ++i)
+	    {
+		if (children[i] == menu->id)
+		    continue;
+		if (get_left_margin == True)
+		{
+		    Dimension left_margin;
+
+		    XtVaGetValues(children[i], XtNleftMargin, &left_margin,
+				  NULL);
+		    XtVaSetValues(children[i], XtNrightMargin, left_margin,
+				  NULL);
+		}
+		else
+		    XtVaSetValues(children[i], XtNrightMargin, right_margin,
+				  NULL);
+	    }
+	}
+    }
+    /* Please be sure to destroy the parent widget first (i.e. menu->id).
+     *
+     * This code should be basically identical to that in the file gui_motif.c
+     * because they are both Xt based.
+     */
+    if (menu->id != (Widget)0)
+    {
+	Cardinal    num_children;
+	Dimension   height, space, border;
+
+	XtVaGetValues(menuBar,
+		XtNvSpace,	&space,
+		XtNborderWidth, &border,
+		NULL);
+	XtVaGetValues(menu->id,
+		XtNheight,	&height,
+		NULL);
+#if defined(FEAT_TOOLBAR) && defined(FEAT_BEVAL)
+	if (parent == toolBar && menu->tip != NULL)
+	{
+	    /* We try to destroy this before the actual menu, because there are
+	     * callbacks, etc. that will be unregistered during the tooltip
+	     * destruction.
+	     *
+	     * If you call "gui_mch_destroy_beval_area()" after destroying
+	     * menu->id, then the tooltip's window will have already been
+	     * deallocated by Xt, and unknown behaviour will ensue (probably
+	     * a core dump).
+	     */
+	    gui_mch_destroy_beval_area(menu->tip);
+	    menu->tip = NULL;
+	}
+#endif
+	/*
+	 * This is a hack to stop the Athena simpleMenuWidget from getting a
+	 * BadValue error when a menu's last child is destroyed. We check to
+	 * see if this is the last child and if so, don't delete it. The parent
+	 * will be deleted soon anyway, and it will delete it's children like
+	 * all good widgets do.
+	 */
+	/* NOTE: The cause of the BadValue X Protocol Error is because when the
+	 * last child is destroyed, it is first unmanaged, thus causing a
+	 * geometry resize request from the parent Shell widget.
+	 * Since the Shell widget has no more children, it is resized to have
+	 * width/height of 0.  XConfigureWindow() is then called with the
+	 * width/height of 0, which generates the BadValue.
+	 *
+	 * This happens in phase two of the widget destruction process.
+	 */
+	{
+	    if (parent != menuBar
+#ifdef FEAT_TOOLBAR
+		    && parent != toolBar
+#endif
+		    )
+	    {
+		XtVaGetValues(parent, XtNnumChildren, &num_children, NULL);
+		if (num_children > 1)
+		    XtDestroyWidget(menu->id);
+	    }
+	    else
+		XtDestroyWidget(menu->id);
+	    menu->id = (Widget)0;
+	}
+
+	if (parent == menuBar)
+	{
+	    if (!gui.menu_height_fixed)
+		gui.menu_height = height + 2 * (space + border);
+	}
+#ifdef FEAT_TOOLBAR
+	else if (parent == toolBar)
+	{
+	    /* When removing last toolbar item, don't display the toolbar. */
+	    XtVaGetValues(toolBar, XtNnumChildren, &num_children, NULL);
+	    if (num_children == 0)
+		gui_mch_show_toolbar(FALSE);
+	    else
+		gui.toolbar_height = gui_mch_compute_toolbar_height();
+	}
+#endif
+    }
+    if (menu->submenu_id != (Widget)0)
+    {
+	XtDestroyWidget(menu->submenu_id);
+	menu->submenu_id = (Widget)0;
+    }
+}
+
+/*ARGSUSED*/
+    static void
+gui_athena_menu_timeout(client_data, id)
+    XtPointer	    client_data;
+    XtIntervalId    *id;
+{
+    Widget  w = (Widget)client_data;
+    Widget  popup;
+
+    timer = 0;
+    if (XtIsSubclass(w,smeBSBObjectClass))
+    {
+	Pixmap p;
+
+	XtVaGetValues(w, XtNrightBitmap, &p, NULL);
+	if ((p != None) && (p != XtUnspecifiedPixmap))
+	{
+	    /* We are dealing with an item that has a submenu */
+	    popup = get_popup_entry(XtParent(w));
+	    if (popup == (Widget)0)
+		return;
+	    XtPopup(popup, XtGrabNonexclusive);
+	}
+    }
+}
+
+/* This routine is used to calculate the position (in screen coordinates)
+ * where a submenu should appear relative to the menu entry that popped it
+ * up.  It should appear even with and just slightly to the left of the
+ * rightmost end of the menu entry that caused the popup.
+ *
+ * This is called when XtPopup() is called.
+ */
+/*ARGSUSED*/
+    static void
+gui_athena_popup_callback(w, client_data, call_data)
+    Widget	w;
+    XtPointer	client_data;
+    XtPointer	call_data;
+{
+    /* Assumption: XtIsSubclass(XtParent(w),simpleMenuWidgetClass) */
+    vimmenu_T	*menu = (vimmenu_T *)client_data;
+    Dimension	width;
+    Position	root_x, root_y;
+
+    /* First, popdown any siblings that may have menus popped up */
+    {
+	vimmenu_T *i;
+
+	for (i = menu->parent->children; i != NULL; i = i->next)
+	{
+	    if (i->submenu_id != NULL && XtIsManaged(i->submenu_id))
+		XtPopdown(i->submenu_id);
+	}
+    }
+    XtVaGetValues(XtParent(w),
+		  XtNwidth,   &width,
+		  NULL);
+    /* Assumption: XawSimpleMenuGetActiveEntry(XtParent(w)) == menu->id */
+    /* i.e. This IS the active entry */
+    XtTranslateCoords(menu->id,width - 5, 0, &root_x, &root_y);
+    XtVaSetValues(w, XtNx, root_x,
+		     XtNy, root_y,
+		     NULL);
+}
+
+/* ARGSUSED */
+    static void
+gui_athena_popdown_submenus_action(w, event, args, nargs)
+    Widget	w;
+    XEvent	*event;
+    String	*args;
+    Cardinal	*nargs;
+{
+    WidgetList	children;
+    Cardinal	num_children;
+
+    XtVaGetValues(w, XtNchildren, &children,
+		     XtNnumChildren, &num_children,
+		     NULL);
+    for (; num_children > 0; --num_children)
+    {
+	Widget child = children[num_children - 1];
+
+	if (has_submenu(child))
+	{
+	    Widget temp_w;
+
+	    temp_w = submenu_widget(child);
+	    gui_athena_popdown_submenus_action(temp_w,event,args,nargs);
+	    XtPopdown(temp_w);
+	}
+    }
+}
+
+/* Used to determine if the given widget has a submenu that can be popped up. */
+    static Boolean
+has_submenu(widget)
+    Widget  widget;
+{
+    if ((widget != NULL) && XtIsSubclass(widget,smeBSBObjectClass))
+    {
+	Pixmap p;
+
+	XtVaGetValues(widget, XtNrightBitmap, &p, NULL);
+	if ((p != None) && (p != XtUnspecifiedPixmap))
+	    return True;
+    }
+    return False;
+}
+
+/* ARGSUSED */
+    static void
+gui_athena_delayed_arm_action(w, event, args, nargs)
+    Widget	w;
+    XEvent	*event;
+    String	*args;
+    Cardinal	*nargs;
+{
+    Dimension	width, height;
+
+    if (event->type != MotionNotify)
+	return;
+
+    XtVaGetValues(w,
+	XtNwidth,   &width,
+	XtNheight,  &height,
+	NULL);
+
+    if (event->xmotion.x >= (int)width || event->xmotion.y >= (int)height)
+	return;
+
+    {
+	static Widget	    previous_active_widget = NULL;
+	Widget		    current;
+
+	current = XawSimpleMenuGetActiveEntry(w);
+	if (current != previous_active_widget)
+	{
+	    if (timer)
+	    {
+		/* If the timeout hasn't been triggered, remove it */
+		XtRemoveTimeOut(timer);
+	    }
+	    gui_athena_popdown_submenus_action(w,event,args,nargs);
+	    if (has_submenu(current))
+	    {
+		XtAppAddTimeOut(XtWidgetToApplicationContext(w), 600L,
+				gui_athena_menu_timeout,
+				(XtPointer)current);
+	    }
+	    previous_active_widget = current;
+	}
+    }
+}
+
+    static Widget
+get_popup_entry(w)
+    Widget  w;
+{
+    Widget	menuw;
+
+    /* Get the active entry for the current menu */
+    if ((menuw = XawSimpleMenuGetActiveEntry(w)) == (Widget)0)
+	return NULL;
+
+    return submenu_widget(menuw);
+}
+
+/* Given the widget that has been determined to have a submenu, return the submenu widget
+ * that is to be popped up.
+ */
+    static Widget
+submenu_widget(widget)
+    Widget  widget;
+{
+    /* Precondition: has_submenu(widget) == True
+     *	    XtIsSubclass(XtParent(widget),simpleMenuWidgetClass) == True
+     */
+
+    char_u	*pullright_name;
+    Widget	popup;
+
+    pullright_name = make_pull_name((char_u *)XtName(widget));
+    popup = XtNameToWidget(XtParent(widget), (char *)pullright_name);
+    vim_free(pullright_name);
+
+    return popup;
+    /* Postcondition: (popup != NULL) implies
+     * (XtIsSubclass(popup,simpleMenuWidgetClass) == True) */
+}
+
+/* ARGSUSED */
+    void
+gui_mch_show_popupmenu(menu)
+    vimmenu_T *menu;
+{
+    int		rootx, rooty, winx, winy;
+    Window	root, child;
+    unsigned int mask;
+
+    if (menu->submenu_id == (Widget)0)
+	return;
+
+    /* Position the popup menu at the pointer */
+    if (XQueryPointer(gui.dpy, XtWindow(vimShell), &root, &child,
+		&rootx, &rooty, &winx, &winy, &mask))
+    {
+	rootx -= 30;
+	if (rootx < 0)
+	    rootx = 0;
+	rooty -= 5;
+	if (rooty < 0)
+	    rooty = 0;
+	XtVaSetValues(menu->submenu_id,
+		XtNx, rootx,
+		XtNy, rooty,
+		NULL);
+    }
+
+    XtOverrideTranslations(menu->submenu_id, popupTrans);
+    XtPopupSpringLoaded(menu->submenu_id);
+}
+
+#endif /* FEAT_MENU */
+
+/*
+ * Set the menu and scrollbar colors to their default values.
+ */
+    void
+gui_mch_def_colors()
+{
+    /*
+     * Get the colors ourselves.  Using the automatic conversion doesn't
+     * handle looking for approximate colors.
+     */
+    if (gui.in_use)
+    {
+	gui.menu_fg_pixel = gui_get_color((char_u *)gui.rsrc_menu_fg_name);
+	gui.menu_bg_pixel = gui_get_color((char_u *)gui.rsrc_menu_bg_name);
+	gui.scroll_fg_pixel = gui_get_color((char_u *)gui.rsrc_scroll_fg_name);
+	gui.scroll_bg_pixel = gui_get_color((char_u *)gui.rsrc_scroll_bg_name);
+#ifdef FEAT_BEVAL
+	gui.tooltip_fg_pixel = gui_get_color((char_u *)gui.rsrc_tooltip_fg_name);
+	gui.tooltip_bg_pixel = gui_get_color((char_u *)gui.rsrc_tooltip_bg_name);
+#endif
+    }
+}
+
+
+/*
+ * Scrollbar stuff.
+ */
+
+    void
+gui_mch_set_scrollbar_thumb(sb, val, size, max)
+    scrollbar_T	*sb;
+    long	val;
+    long	size;
+    long	max;
+{
+    double	v, s;
+
+    if (sb->id == (Widget)0)
+	return;
+
+    /*
+     * Athena scrollbar must go from 0.0 to 1.0.
+     */
+    if (max == 0)
+    {
+	/* So you can't scroll it at all (normally it scrolls past end) */
+#ifdef FEAT_GUI_NEXTAW
+	XawScrollbarSetThumb(sb->id, 0.0, 1.0);
+#else
+	vim_XawScrollbarSetThumb(sb->id, 0.0, 1.0, 0.0);
+#endif
+    }
+    else
+    {
+	v = (double)val / (double)(max + 1);
+	s = (double)size / (double)(max + 1);
+#ifdef FEAT_GUI_NEXTAW
+	XawScrollbarSetThumb(sb->id, v, s);
+#else
+	vim_XawScrollbarSetThumb(sb->id, v, s, 1.0);
+#endif
+    }
+}
+
+    void
+gui_mch_set_scrollbar_pos(sb, x, y, w, h)
+    scrollbar_T *sb;
+    int		x;
+    int		y;
+    int		w;
+    int		h;
+{
+    if (sb->id == (Widget)0)
+	return;
+
+    XtUnmanageChild(sb->id);
+    XtVaSetValues(sb->id,
+		  XtNhorizDistance, x,
+		  XtNvertDistance, y,
+		  XtNwidth, w,
+		  XtNheight, h,
+		  NULL);
+    XtManageChild(sb->id);
+}
+
+    void
+gui_mch_enable_scrollbar(sb, flag)
+    scrollbar_T	*sb;
+    int		flag;
+{
+    if (sb->id != (Widget)0)
+    {
+	if (flag)
+	    XtManageChild(sb->id);
+	else
+	    XtUnmanageChild(sb->id);
+    }
+}
+
+    void
+gui_mch_create_scrollbar(sb, orient)
+    scrollbar_T *sb;
+    int		orient;	/* SBAR_VERT or SBAR_HORIZ */
+{
+    sb->id = XtVaCreateWidget("scrollBar",
+#ifdef FEAT_GUI_NEXTAW
+	    scrollbarWidgetClass, vimForm,
+#else
+	    vim_scrollbarWidgetClass, vimForm,
+#endif
+	    XtNresizable,   True,
+	    XtNtop,	    XtChainTop,
+	    XtNbottom,	    XtChainTop,
+	    XtNleft,	    XtChainLeft,
+	    XtNright,	    XtChainLeft,
+	    XtNborderWidth, 0,
+	    XtNorientation, (orient == SBAR_VERT) ? XtorientVertical
+						  : XtorientHorizontal,
+	    XtNforeground, gui.scroll_fg_pixel,
+	    XtNbackground, gui.scroll_bg_pixel,
+	    NULL);
+    if (sb->id == (Widget)0)
+	return;
+
+    XtAddCallback(sb->id, XtNjumpProc,
+		  gui_athena_scroll_cb_jump, (XtPointer)sb->ident);
+    XtAddCallback(sb->id, XtNscrollProc,
+		  gui_athena_scroll_cb_scroll, (XtPointer)sb->ident);
+
+#ifdef FEAT_GUI_NEXTAW
+    XawScrollbarSetThumb(sb->id, 0.0, 1.0);
+#else
+    vim_XawScrollbarSetThumb(sb->id, 0.0, 1.0, 0.0);
+#endif
+}
+
+#if defined(FEAT_WINDOWS) || defined(PROTO)
+    void
+gui_mch_destroy_scrollbar(sb)
+    scrollbar_T *sb;
+{
+    if (sb->id != (Widget)0)
+	XtDestroyWidget(sb->id);
+}
+#endif
+
+    void
+gui_mch_set_scrollbar_colors(sb)
+    scrollbar_T *sb;
+{
+    if (sb->id != (Widget)0)
+	XtVaSetValues(sb->id,
+	    XtNforeground, gui.scroll_fg_pixel,
+	    XtNbackground, gui.scroll_bg_pixel,
+	    NULL);
+
+    /* This is needed for the rectangle below the vertical scrollbars. */
+    if (sb == &gui.bottom_sbar && vimForm != (Widget)0)
+	gui_athena_scroll_colors(vimForm);
+}
+
+/*
+ * Miscellaneous stuff:
+ */
+    Window
+gui_x11_get_wid()
+{
+    return XtWindow(textArea);
+}
+
+#if defined(FEAT_BROWSE) || defined(PROTO)
+/*
+ * Put up a file requester.
+ * Returns the selected name in allocated memory, or NULL for Cancel.
+ */
+/* ARGSUSED */
+    char_u *
+gui_mch_browse(saving, title, dflt, ext, initdir, filter)
+    int		saving;		/* select file to write */
+    char_u	*title;		/* not used (title for the window) */
+    char_u	*dflt;		/* not used (default name) */
+    char_u	*ext;		/* not used (extension added) */
+    char_u	*initdir;	/* initial directory, NULL for current dir */
+    char_u	*filter;	/* not used (file name filter) */
+{
+    Position x, y;
+    char_u	dirbuf[MAXPATHL];
+
+    /* Concatenate "initdir" and "dflt". */
+    if (initdir == NULL || *initdir == NUL)
+	mch_dirname(dirbuf, MAXPATHL);
+    else if (STRLEN(initdir) + 2 < MAXPATHL)
+	STRCPY(dirbuf, initdir);
+    else
+	dirbuf[0] = NUL;
+    if (dflt != NULL && *dflt != NUL
+			      && STRLEN(dirbuf) + 2 + STRLEN(dflt) < MAXPATHL)
+    {
+	add_pathsep(dirbuf);
+	STRCAT(dirbuf, dflt);
+    }
+
+    /* Position the file selector just below the menubar */
+    XtTranslateCoords(vimShell, (Position)0, (Position)
+#ifdef FEAT_MENU
+	    gui.menu_height
+#else
+	    0
+#endif
+	    , &x, &y);
+    return (char_u *)vim_SelFile(vimShell, (char *)title, (char *)dirbuf,
+		  NULL, (int)x, (int)y, gui.menu_fg_pixel, gui.menu_bg_pixel,
+		  gui.scroll_fg_pixel, gui.scroll_bg_pixel);
+}
+#endif
+
+#if defined(FEAT_GUI_DIALOG) || defined(PROTO)
+
+static int	dialogStatus;
+static Atom	dialogatom;
+
+static void keyhit_callback __ARGS((Widget w, XtPointer client_data, XEvent *event, Boolean *cont));
+static void butproc __ARGS((Widget w, XtPointer client_data, XtPointer call_data));
+static void dialog_wm_handler __ARGS((Widget w, XtPointer client_data, XEvent *event, Boolean *dum));
+
+/*
+ * Callback function for the textfield.  When CR is hit this works like
+ * hitting the "OK" button, ESC like "Cancel".
+ */
+/* ARGSUSED */
+    static void
+keyhit_callback(w, client_data, event, cont)
+    Widget		w;
+    XtPointer		client_data;
+    XEvent		*event;
+    Boolean		*cont;
+{
+    char	buf[2];
+
+    if (XLookupString(&(event->xkey), buf, 2, NULL, NULL) == 1)
+    {
+	if (*buf == CAR)
+	    dialogStatus = 1;
+	else if (*buf == ESC)
+	    dialogStatus = 0;
+    }
+}
+
+/* ARGSUSED */
+    static void
+butproc(w, client_data, call_data)
+    Widget	w;
+    XtPointer	client_data;
+    XtPointer	call_data;
+{
+    dialogStatus = (int)(long)client_data + 1;
+}
+
+/*
+ * Function called when dialog window closed.
+ */
+/*ARGSUSED*/
+    static void
+dialog_wm_handler(w, client_data, event, dum)
+    Widget	w;
+    XtPointer	client_data;
+    XEvent	*event;
+    Boolean	*dum;
+{
+    if (event->type == ClientMessage
+	    && ((XClientMessageEvent *)event)->data.l[0] == dialogatom)
+	dialogStatus = 0;
+}
+
+/* ARGSUSED */
+    int
+gui_mch_dialog(type, title, message, buttons, dfltbutton, textfield)
+    int		type;
+    char_u	*title;
+    char_u	*message;
+    char_u	*buttons;
+    int		dfltbutton;
+    char_u	*textfield;
+{
+    char_u		*buts;
+    char_u		*p, *next;
+    XtAppContext	app;
+    XEvent		event;
+    Position		wd, hd;
+    Position		wv, hv;
+    Position		x, y;
+    Widget		dialog;
+    Widget		dialogshell;
+    Widget		dialogmessage;
+    Widget		dialogtextfield = 0;
+    Widget		dialogButton;
+    Widget		prev_dialogButton = NULL;
+    int			butcount;
+    int			vertical;
+
+    if (title == NULL)
+	title = (char_u *)_("Vim dialog");
+    dialogStatus = -1;
+
+    /* if our pointer is currently hidden, then we should show it. */
+    gui_mch_mousehide(FALSE);
+
+    /* Check 'v' flag in 'guioptions': vertical button placement. */
+    vertical = (vim_strchr(p_go, GO_VERTICAL) != NULL);
+
+    /* The shell is created each time, to make sure it is resized properly */
+    dialogshell = XtVaCreatePopupShell("dialogShell",
+	    transientShellWidgetClass, vimShell,
+	    XtNtitle, title,
+	    NULL);
+    if (dialogshell == (Widget)0)
+	goto error;
+
+    dialog = XtVaCreateManagedWidget("dialog",
+	    formWidgetClass, dialogshell,
+	    XtNdefaultDistance, 20,
+	    NULL);
+    if (dialog == (Widget)0)
+	goto error;
+    gui_athena_menu_colors(dialog);
+    dialogmessage = XtVaCreateManagedWidget("dialogMessage",
+	    labelWidgetClass, dialog,
+	    XtNlabel, message,
+	    XtNtop, XtChainTop,
+	    XtNbottom, XtChainTop,
+	    XtNleft, XtChainLeft,
+	    XtNright, XtChainLeft,
+	    XtNresizable, True,
+	    XtNborderWidth, 0,
+	    NULL);
+    gui_athena_menu_colors(dialogmessage);
+
+    if (textfield != NULL)
+    {
+	dialogtextfield = XtVaCreateManagedWidget("textfield",
+		asciiTextWidgetClass, dialog,
+		XtNwidth, 400,
+		XtNtop, XtChainTop,
+		XtNbottom, XtChainTop,
+		XtNleft, XtChainLeft,
+		XtNright, XtChainRight,
+		XtNfromVert, dialogmessage,
+		XtNresizable, True,
+		XtNstring, textfield,
+		XtNlength, IOSIZE,
+		XtNuseStringInPlace, True,
+		XtNeditType, XawtextEdit,
+		XtNwrap, XawtextWrapNever,
+		XtNresize, XawtextResizeHeight,
+		NULL);
+	XtManageChild(dialogtextfield);
+	XtAddEventHandler(dialogtextfield, KeyPressMask, False,
+			    (XtEventHandler)keyhit_callback, (XtPointer)NULL);
+	XawTextSetInsertionPoint(dialogtextfield,
+					  (XawTextPosition)STRLEN(textfield));
+	XtSetKeyboardFocus(dialog, dialogtextfield);
+    }
+
+    /* make a copy, so that we can insert NULs */
+    buts = vim_strsave(buttons);
+    if (buts == NULL)
+	return -1;
+
+    p = buts;
+    for (butcount = 0; *p; ++butcount)
+    {
+	for (next = p; *next; ++next)
+	{
+	    if (*next == DLG_HOTKEY_CHAR)
+		mch_memmove(next, next + 1, STRLEN(next));
+	    if (*next == DLG_BUTTON_SEP)
+	    {
+		*next++ = NUL;
+		break;
+	    }
+	}
+	dialogButton = XtVaCreateManagedWidget("button",
+		commandWidgetClass, dialog,
+		XtNlabel, p,
+		XtNtop, XtChainBottom,
+		XtNbottom, XtChainBottom,
+		XtNleft, XtChainLeft,
+		XtNright, XtChainLeft,
+		XtNfromVert, textfield == NULL ? dialogmessage : dialogtextfield,
+		XtNvertDistance, vertical ? 4 : 20,
+		XtNresizable, False,
+		NULL);
+	gui_athena_menu_colors(dialogButton);
+	if (butcount > 0)
+	    XtVaSetValues(dialogButton,
+		    vertical ? XtNfromVert : XtNfromHoriz, prev_dialogButton,
+		    NULL);
+
+	XtAddCallback(dialogButton, XtNcallback, butproc, (XtPointer)butcount);
+	p = next;
+	prev_dialogButton = dialogButton;
+    }
+    vim_free(buts);
+
+    XtRealizeWidget(dialogshell);
+
+    /* Setup for catching the close-window event, don't let it close Vim! */
+    dialogatom = XInternAtom(gui.dpy, "WM_DELETE_WINDOW", False);
+    XSetWMProtocols(gui.dpy, XtWindow(dialogshell), &dialogatom, 1);
+    XtAddEventHandler(dialogshell, NoEventMask, True, dialog_wm_handler, NULL);
+
+    XtVaGetValues(dialogshell,
+	    XtNwidth, &wd,
+	    XtNheight, &hd,
+	    NULL);
+    XtVaGetValues(vimShell,
+	    XtNwidth, &wv,
+	    XtNheight, &hv,
+	    NULL);
+    XtTranslateCoords(vimShell,
+	    (Position)((wv - wd) / 2),
+	    (Position)((hv - hd) / 2),
+	    &x, &y);
+    if (x < 0)
+	x = 0;
+    if (y < 0)
+	y = 0;
+    XtVaSetValues(dialogshell, XtNx, x, XtNy, y, NULL);
+
+    /* Position the mouse pointer in the dialog, required for when focus
+     * follows mouse. */
+    XWarpPointer(gui.dpy, (Window)0, XtWindow(dialogshell), 0, 0, 0, 0, 20, 40);
+
+
+    app = XtWidgetToApplicationContext(dialogshell);
+
+    XtPopup(dialogshell, XtGrabNonexclusive);
+
+    while (1)
+    {
+	XtAppNextEvent(app, &event);
+	XtDispatchEvent(&event);
+	if (dialogStatus >= 0)
+	    break;
+    }
+
+    XtPopdown(dialogshell);
+
+    if (textfield != NULL && dialogStatus < 0)
+	*textfield = NUL;
+
+error:
+    XtDestroyWidget(dialogshell);
+
+    return dialogStatus;
+}
+#endif
+
+#if defined(FEAT_GUI_DIALOG) || defined(FEAT_MENU)
+/*
+ * Set the colors of Widget "id" to the menu colors.
+ */
+    static void
+gui_athena_menu_colors(id)
+    Widget  id;
+{
+    if (gui.menu_bg_pixel != INVALCOLOR)
+	XtVaSetValues(id, XtNbackground, gui.menu_bg_pixel, NULL);
+    if (gui.menu_fg_pixel != INVALCOLOR)
+	XtVaSetValues(id, XtNforeground, gui.menu_fg_pixel, NULL);
+}
+#endif
+
+/*
+ * Set the colors of Widget "id" to the scroll colors.
+ */
+    static void
+gui_athena_scroll_colors(id)
+    Widget  id;
+{
+    if (gui.scroll_bg_pixel != INVALCOLOR)
+	XtVaSetValues(id, XtNbackground, gui.scroll_bg_pixel, NULL);
+    if (gui.scroll_fg_pixel != INVALCOLOR)
+	XtVaSetValues(id, XtNforeground, gui.scroll_fg_pixel, NULL);
+}