ftp.nice.ch/pub/next/unix/editor/vim-5.0f.s.tar.gz#/vim-5.0f/src/gui_athena.c

This is gui_athena.c in view mode; [Download] [Up]

/* vi:set ts=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.
 */

#include <X11/StringDefs.h>
#include <X11/Intrinsic.h>
#include <X11/Xaw/Form.h>
#include <X11/Xaw/SimpleMenu.h>
#include <X11/Xaw/MenuButton.h>
#include <X11/Xaw/SmeBSB.h>
#include <X11/Xaw/Box.h>

#include "vim.h"
#include "globals.h"
#include "proto.h"
#include "option.h"
#include "gui_at_sb.h"

#define puller_width	19
#define puller_height	19

static char_u puller_bits[] =
{
	0x00,0x00,0xf8,0x00,0x00,0xf8,0xf8,0x7f,0xf8,0x04,0x80,0xf8,0x04,0x80,0xf9,
	0x84,0x81,0xf9,0x84,0x83,0xf9,0x84,0x87,0xf9,0x84,0x8f,0xf9,0x84,0x8f,0xf9,
	0x84,0x87,0xf9,0x84,0x83,0xf9,0x84,0x81,0xf9,0x04,0x80,0xf9,0x04,0x80,0xf9,
	0xf8,0xff,0xf9,0xf0,0x7f,0xf8,0x00,0x00,0xf8,0x00,0x00,0xf8
};

extern Widget vimShell;

static Widget vimForm = (Widget)NULL;
static Widget textArea;
static Widget menuBar;

static void gui_athena_scroll_cb_jump   __ARGS((Widget, XtPointer, XtPointer));
static void gui_athena_scroll_cb_scroll __ARGS((Widget, XtPointer, XtPointer));
static void gui_athena_reorder_menus    __ARGS((void));
static void	gui_athena_pullright_action __ARGS((Widget, XEvent *, String *,
												Cardinal *));
static void	gui_athena_pullleft_action __ARGS((Widget, XEvent *, String *,
												Cardinal *));
static Widget get_popup_entry __ARGS((Widget w));

static XtActionsRec		pullAction[2] = {{ "menu-pullright",
								(XtActionProc)gui_athena_pullright_action},
										 { "menu-pullleft",
								(XtActionProc)gui_athena_pullleft_action}};
static XtTranslations	parentTrans, menuTrans, supermenuTrans;
static Pixmap			pullerBitmap;

/*
 * Scrollbar callback (XtNjumpProc) for when the scrollbar is dragged with the
 * left or middle mouse button.
 */
	static void
gui_athena_scroll_cb_jump(w, client_data, call_data)
	Widget		w;
	XtPointer	client_data, call_data;
{
	GuiScrollbar *sb, *sb_info;
	long_u		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_u)(*((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.
 */
	static void
gui_athena_scroll_cb_scroll(w, client_data, call_data)
	Widget		w;
	XtPointer	client_data, call_data;
{
	GuiScrollbar *sb, *sb_info;
	long		value;
	int			data = (int)call_data;
	int			page;

	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];

		if (sb_info->size > 5)
			page = sb_info->size - 2;		/* use two lines of context */
		else
			page = sb_info->size;
		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;
		}
	}
	else			/* Bottom scrollbar */
	{
		sb_info = sb;
		if (data < -1)
			data = -(Columns - 5);
		else if (data > 1)
			data = (Columns - 5);
	}

	value = sb_info->value + data;
	if (value > sb_info->max)
		value = sb_info->max;
	else if (value < 0)
		value = 0;

	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;

	XtInitializeWidgetClass(formWidgetClass);
	XtInitializeWidgetClass(boxWidgetClass);
	XtInitializeWidgetClass(coreWidgetClass);
	XtInitializeWidgetClass(menuButtonWidgetClass);
	XtInitializeWidgetClass(simpleMenuWidgetClass);
	XtInitializeWidgetClass(vim_scrollbarWidgetClass);

	/* The form containing all the other widgets */
	vimForm = XtVaCreateManagedWidget("vimForm",
		formWidgetClass,		vimShell,
		XtNborderWidth,			0,
		XtNforeground,			gui.menu_fg_pixel,
		XtNbackground,			gui.menu_bg_pixel,
		NULL);

	/* The top menu bar */
	menuBar = XtVaCreateManagedWidget("menuBar",
		boxWidgetClass,			vimForm,
		XtNresizable,			True,
		XtNtop,					XawChainTop,
		XtNbottom,				XawChainTop,
		XtNleft,				XawChainLeft,
		XtNright,				XawChainRight,
		XtNforeground,			gui.menu_fg_pixel,
		XtNbackground,			gui.menu_bg_pixel,
		XtNborderColor,			gui.menu_fg_pixel,
		NULL);

	/* The text area. */
	textArea = XtVaCreateManagedWidget("textArea",
		coreWidgetClass,		vimForm,
		XtNresizable,			True,
		XtNtop,					XawChainTop,
		XtNbottom,				XawChainTop,
		XtNleft,				XawChainLeft,
		XtNright,				XawChainLeft,
		XtNbackground,			gui.back_pixel,
		XtNborderWidth,			0,
		NULL);

	/*
	 * Callbacks for textArea, vimForm, and vimShell.
	 */
	XtAddEventHandler(textArea, VisibilityChangeMask, FALSE,
		gui_x11_visibility_cb, (XtPointer)0);

	XtAddEventHandler(textArea, ExposureMask, FALSE, gui_x11_expose_cb,
		(XtPointer)0);

	XtAddEventHandler(vimForm, StructureNotifyMask, FALSE,
		gui_x11_resize_window_cb, (XtPointer)0);

	XtAddEventHandler(vimShell, FocusChangeMask, FALSE, gui_x11_focus_change_cb,
		(XtPointer)0);

	XtAddEventHandler(vimForm, KeyPressMask, FALSE, gui_x11_key_hit_cb,
		(XtPointer)0);

	XtAddEventHandler(textArea, ButtonPressMask | ButtonReleaseMask |
		ButtonMotionMask, FALSE, gui_x11_mouse_cb, (XtPointer)0);

	parentTrans = XtParseTranslationTable("<EnterWindow>: highlight()\n<LeaveWindow>:\n<BtnUp>: notify() unhighlight() MenuPopdown()\n<BtnMotion>: highlight() menu-pullright()");
	menuTrans = XtParseTranslationTable("<EnterWindow>: highlight()\n<LeaveWindow>: unhighlight() MenuPopdown()\n<BtnUp>: notify() unhighlight() MenuPopdown()\n<BtnMotion>: highlight() menu-pullright()");
	supermenuTrans = XtParseTranslationTable("<EnterWindow>: highlight() menu-pullleft()\n<LeaveWindow>:\n<BtnUp>: notify() unhighlight() MenuPopdown()\n<BtnMotion>:");

	XtAppAddActions(XtWidgetToApplicationContext(vimForm), pullAction, 2);

	pullerBitmap = XCreateBitmapFromData(gui.dpy, DefaultRootWindow(gui.dpy),
							(char *)puller_bits, puller_width, puller_height);
}

	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);
}

/*
 * Menu stuff.
 */

	void
gui_mch_enable_menu(flag)
	int		flag;
{
	if (flag)
		XtManageChild(menuBar);
	else
		XtUnmanageChild(menuBar);
}

	void
gui_mch_set_menu_pos(x, y, w, h)
	int		x;
	int		y;
	int		w;
	int		h;
{
	Dimension	border;

	XtUnmanageChild(menuBar);
	XtVaGetValues(menuBar,
				XtNborderWidth, &border,
				NULL);
	XtVaSetValues(menuBar,
				XtNhorizDistance, x,
				XtNvertDistance, y,
				XtNwidth, w - 2 * border,
				XtNheight, h - 2 * border,
				NULL);
	XtManageChild(menuBar);
}

	void
gui_mch_add_menu(menu, parent)
	GuiMenu	*menu;
	GuiMenu	*parent;
{
	char_u		*pullright_name;
	Dimension	height, space, border;

	if (parent == NULL)
	{
		menu->id = XtVaCreateManagedWidget(menu->name,
			menuButtonWidgetClass, menuBar,
			XtNmenuName, menu->name,
			XtNforeground, gui.menu_fg_pixel,
			XtNbackground, gui.menu_bg_pixel,
			NULL);

		menu->submenu_id = XtVaCreatePopupShell(menu->name,
			simpleMenuWidgetClass, menu->id,
			XtNforeground, gui.menu_fg_pixel,
			XtNbackground, gui.menu_bg_pixel,
			NULL);

		/*
		 * 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);

		gui_athena_reorder_menus();
	}
	else
	{
		menu->id = XtVaCreateManagedWidget(menu->name,
			smeBSBObjectClass, parent->submenu_id,
			XtNforeground, gui.menu_fg_pixel,
			XtNbackground, gui.menu_bg_pixel,
			XtNrightMargin, puller_width,
			XtNrightBitmap, pullerBitmap,
			NULL);
		XtAddCallback(menu->id, XtNcallback, gui_x11_menu_cb,
			(XtPointer)menu);

		pullright_name = vim_strnsave(menu->name,
								   STRLEN(menu->name) + strlen("-pullright"));
		strcat((char *)pullright_name, "-pullright");
		menu->submenu_id = XtVaCreatePopupShell(pullright_name,
			simpleMenuWidgetClass, parent->submenu_id,
			XtNforeground, gui.menu_fg_pixel,
			XtNbackground, gui.menu_bg_pixel,
			XtNtranslations, menuTrans,
			NULL);
		vim_free(pullright_name);

		XtOverrideTranslations(parent->submenu_id, parentTrans);
	}
}

	void
gui_mch_add_menu_item(menu, parent)
	GuiMenu	*menu;
	GuiMenu	*parent;
{
	menu->submenu_id = (Widget)0;
	menu->id = XtVaCreateManagedWidget(menu->name,
		smeBSBObjectClass, parent->submenu_id,
		XtNforeground, gui.menu_fg_pixel,
		XtNbackground, gui.menu_bg_pixel,
		NULL);
	XtAddCallback(menu->id, XtNcallback, gui_x11_menu_cb,
		(XtPointer)menu);
}

/*
 * Destroy the machine specific menu widget.
 */
	void
gui_mch_destroy_menu(menu)
	GuiMenu	*menu;
{
	Widget	parent;

	if (menu->submenu_id != (Widget)0)
	{
		XtDestroyWidget(menu->submenu_id);
		menu->submenu_id = (Widget)0;
	}
	if (menu->id != (Widget)0)
	{
		/*
		 * 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.
		 */
		parent = XtParent(menu->id);
		if (parent != menuBar)
		{
			int num_children;

			XtVaGetValues(parent, XtNnumChildren, &num_children, NULL);
			if (num_children > 1)
				XtDestroyWidget(menu->id);
		}
		else
			XtDestroyWidget(menu->id);
		menu->id = (Widget)0;
	}
}

/*
 * Reorder the menus so "Help" is the rightmost item on the menu.
 */
	static void
gui_athena_reorder_menus()
{
	Widget	*children;
	Widget  help_widget = (Widget)NULL;
	int		num_children;
	int		i;

	XtVaGetValues(menuBar,
			XtNchildren,    &children,
			XtNnumChildren, &num_children,
			NULL);

	XtUnmanageChildren(children, num_children);

	for (i = 0; i < num_children - 1; i++)
		if (help_widget == (Widget)NULL)
		{
			if (strcmp((char *)XtName(children[i]), "Help") == 0)
			{
				help_widget = children[i];
				children[i] = children[i + 1];
			}
		}
		else
			children[i] = children[i + 1];

	if (help_widget != (Widget)NULL)
		children[num_children - 1] = help_widget;

	XtManageChildren(children, num_children);
}


/*
 * Scrollbar stuff.
 */

	void
gui_mch_set_scrollbar_thumb(sb, val, size, max)
	GuiScrollbar	*sb;
	int				val;
	int				size;
	int				max;
{
	float			v, s;

	/*
	 * 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) */
		vim_XawScrollbarSetThumb(sb->id, 0.0, 1.0, 0.0);
	}
	else
	{
		v = (float)val / (float)(max + 1);
		s = (float)size / (float)(max + 1);
		vim_XawScrollbarSetThumb(sb->id, v, s, 1.0);
	}
}

	void
gui_mch_set_scrollbar_pos(sb, x, y, w, h)
	GuiScrollbar	*sb;
	int				x;
	int				y;
	int				w;
	int				h;
{
	XtUnmanageChild(sb->id);
	XtVaSetValues(sb->id,
				  XtNhorizDistance, x,
				  XtNvertDistance, y,
				  XtNwidth, w,
				  XtNheight, h,
				  NULL);
	XtManageChild(sb->id);
}

	void
gui_mch_create_scrollbar(sb, orient)
	GuiScrollbar	*sb;
	int				orient;		/* SBAR_VERT or SBAR_HORIZ */
{
	sb->id = XtVaCreateWidget("scrollBar",
			vim_scrollbarWidgetClass, vimForm,
			XtNresizable,	True,
			XtNtop,			XawChainTop,
			XtNbottom,		XawChainTop,
			XtNleft,		XawChainLeft,
			XtNright,		XawChainLeft,
			XtNborderWidth, 0,
			XtNorientation, (orient == SBAR_VERT) ? XtorientVertical
												  : XtorientHorizontal,
			XtNforeground, gui.scroll_fg_pixel,
			XtNbackground, gui.scroll_bg_pixel,
			NULL);
	XtAddCallback(sb->id, XtNjumpProc,
				  gui_athena_scroll_cb_jump, (XtPointer)sb->ident);
	XtAddCallback(sb->id, XtNscrollProc,
				  gui_athena_scroll_cb_scroll, (XtPointer)sb->ident);

	vim_XawScrollbarSetThumb(sb->id, 0.0, 1.0, 0.0);
}

	void
gui_mch_destroy_scrollbar(sb)
	GuiScrollbar	*sb;
{
	XtDestroyWidget(sb->id);
}


/*
 * Miscellaneous stuff:
 */
	Window
gui_x11_get_wid()
{
	return( XtWindow(textArea) );
}

	static void
gui_athena_pullright_action(w, event, args, nargs)
	Widget		w;
	XEvent		*event;
	String		*args;
	Cardinal	*nargs;
{
	Dimension	width, height;
	Widget		popup;

	if (event->type != MotionNotify)
		return;

	XtVaGetValues(w,
		XtNwidth,	&width,
		XtNheight,	&height,
		NULL);

	if (event->xmotion.x >= (int)width || event->xmotion.y >= (int)height)
		return;

	/* We do the pull-off when the pointer is in the rightmost 1/4th */
	if (event->xmotion.x < (int)(width * 3) / 4)
		return;

	popup = get_popup_entry(w);
	if (popup == (Widget)NULL)
		return;

	/* Don't Popdown the previous submenu now */
	XtOverrideTranslations(w, supermenuTrans);

	XtVaSetValues(popup,
		XtNx, event->xmotion.x_root,
		XtNy, event->xmotion.y_root - 7,
		NULL);

	XtOverrideTranslations(popup, menuTrans);

	XtPopup(popup, XtGrabNonexclusive);
}

/*
 * Called when a submenu with another submenu gets focus again.
 */
	static void
gui_athena_pullleft_action(w, event, args, nargs)
	Widget		w;
	XEvent		*event;
	String		*args;
	Cardinal	*nargs;
{
	Widget		popup;
	Widget		parent;

	if (event->type != EnterNotify)
		return;

	/* Do Popdown the submenu now */
	popup = get_popup_entry(w);
	if (popup != (Widget)NULL)
		XtPopdown(popup);

	/* If this is the toplevel menu item, set parentTrans */
	if ((parent = XtParent(w)) != (Widget)NULL && XtParent(parent) == menuBar)
		XtOverrideTranslations(w, parentTrans);
	else
		XtOverrideTranslations(w, menuTrans);
}

	static Widget
get_popup_entry(w)
	Widget	w;
{
	Widget		menuw;
	char_u		*pullright_name;
	Widget		popup;

	/* Get the active entry for the current menu */
	if ((menuw = XawSimpleMenuGetActiveEntry(w)) == (Widget)NULL)
		return NULL;

	pullright_name = vim_strnsave((char_u *)XtName(menuw),
								strlen(XtName(menuw)) + strlen("-pullright"));
	strcat((char *)pullright_name, "-pullright");
	popup = XtNameToWidget(w, pullright_name);
	vim_free(pullright_name);

	return popup;
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.