[BACK]Return to gui.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / vim

File: [local] / src / usr.bin / vim / Attic / gui.c (download)

Revision 1.1.1.1 (vendor branch), Sat Sep 7 21:40:28 1996 UTC (27 years, 9 months ago) by downsj
Branch: VIM
CVS Tags: VIM42
Changes since 1.1: +0 -0 lines

Initial import of vim 4.2.

This is meant to replace nvi in the tree.  Vim, in general, works better,
provides more features, and does not suffer from the license problems
being imposed upon nvi.

On the other hand, vim lacks a non-visual ex mode, in addition to open mode.

This includes the GUI (X11) code, but doesn't try to compile it.


/*	$OpenBSD: gui.c,v 1.1.1.1 1996/09/07 21:40:28 downsj Exp $	*/
/* vi:set ts=4 sw=4:
 *
 * VIM - Vi IMproved			by Bram Moolenaar
 *								GUI/Motif support by Robert Webb
 *
 * 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 "vim.h"
#include "globals.h"
#include "proto.h"
#include "option.h"

/* Structure containing all the GUI information */
Gui gui;

/* Set to TRUE after adding/removing menus to ensure they are updated */
int force_menu_update = FALSE;


static void gui_check_screen __ARGS((void));
static void gui_outstr __ARGS((char_u *, int));
static void gui_update_selection __ARGS((void));
static int gui_get_menu_cmd_modes __ARGS((char_u *, int, int *, int *));
static int gui_add_menu_path __ARGS((char_u *, int, void (*)(), char_u *, int));
static int gui_remove_menu __ARGS((GuiMenu **, char_u *, int));
static void gui_free_menu __ARGS((GuiMenu *));
static void gui_free_menu_string __ARGS((GuiMenu *, int));
static int gui_show_menus __ARGS((char_u *, int));
static void gui_show_menus_recursive __ARGS((GuiMenu *, int, int));
static char_u *gui_menu_name_skip __ARGS((char_u *name));
static void gui_create_initial_menus __ARGS((GuiMenu *, GuiMenu *));
static void gui_update_scrollbars __ARGS((void));
static void gui_update_horiz_scrollbar __ARGS((void));

/*
 * The Athena scrollbars can move the thumb to after the end of the scrollbar,
 * this makes the thumb indicate the part of the text that is shown.  Motif
 * can't do this.
 */
#ifdef USE_GUI_ATHENA
# define SCROLL_PAST_END
#endif

/*
 * gui_start -- Called when user wants to start the GUI.
 */
	void
gui_start()
{
	char_u	*old_term;

	old_term = strsave(term_strings[KS_NAME]);
	mch_setmouse(FALSE);					/* first switch mouse off */

	/* set_termname() will call gui_init() to start the GUI */
	termcapinit((char_u *)"builtin_gui");

	if (!gui.in_use)						/* failed to start GUI */
		termcapinit(old_term);

	vim_free(old_term);

	/*
	 * Quit the current process and continue in the child.
	 * Makes "gvim file" disconnect from the shell it was started in.
	 * Don't do this when Vim was started with "-f" or the 'f' flag is present
	 * in 'guioptions'.
	 */
	if (gui.in_use && gui.dofork &&
					  vim_strchr(p_guioptions, GO_FORG) == NULL && fork() > 0)
		exit(0);
}

/*
 * Call this when vim starts up, whether or not the GUI is started
 */
	void
gui_prepare(argc, argv)
	int		*argc;
	char	**argv;
{
	/* Menu items may be added before the GUI is started, so set this now */
	gui.root_menu = NULL;
	gui.in_use = FALSE;				/* No GUI yet (maybe later) */
	gui.starting = FALSE;			/* No GUI yet (maybe later) */
	gui.dofork = TRUE;				/* default is to use fork() */
	gui_mch_prepare(argc, argv);
}

static struct default_menu
{
	char	*name;			/* name of menu item */
	int		mode;			/* mode where menu is valid */
	char	*command;		/* resulting command */
} default_menus[] = 
{
	/* Help menu.  Some reason Motif is happier if this is added first. */
	{"Help.Overview  <F1>",	MENU_NORMAL_MODE,	":help\r"},
	{"Help.Overview  <F1>",	MENU_VISUAL_MODE|MENU_INSERT_MODE|MENU_CMDLINE_MODE,
												"\033:help\r"},
	{"Help.How to\\.\\.\\.",MENU_NORMAL_MODE,	":help how_to\r"},
	{"Help.How to\\.\\.\\.",MENU_VISUAL_MODE|MENU_INSERT_MODE|MENU_CMDLINE_MODE,
												"\033:help how_to\r"},
	{"Help.GUI",			MENU_NORMAL_MODE,	":help gui\r"},
	{"Help.GUI",			MENU_VISUAL_MODE|MENU_INSERT_MODE|MENU_CMDLINE_MODE,
												"\033:help gui\r"},
	{"Help.Version",		MENU_NORMAL_MODE,	":version\r"},
	{"Help.Version",		MENU_VISUAL_MODE|MENU_INSERT_MODE|MENU_CMDLINE_MODE,
												"\033:version\r"},
	{"Help.Credits",		MENU_NORMAL_MODE,	":help credits\r"},
	{"Help.Credits",		MENU_VISUAL_MODE|MENU_INSERT_MODE|MENU_CMDLINE_MODE,
												"\033:help credits\r"},
	{"Help.Copying",		MENU_NORMAL_MODE,	":help uganda\r"},
	{"Help.Copying",		MENU_VISUAL_MODE|MENU_INSERT_MODE|MENU_CMDLINE_MODE,
												"\033:help uganda\r"},

	/* File menu */
	{"File.Save       :w",	MENU_NORMAL_MODE,	":w\r"},
	{"File.Save       :w",	MENU_INSERT_MODE,	"\017:w\r"},

	{"File.Close      :q",	MENU_NORMAL_MODE,	":q\r"},
	{"File.Close      :q",	MENU_VISUAL_MODE|MENU_INSERT_MODE|MENU_CMDLINE_MODE,
												"\033:q\r"},

	{"File.Quit       :qa",	MENU_NORMAL_MODE,	":qa\r"},
	{"File.Quit       :qa",	MENU_VISUAL_MODE|MENU_INSERT_MODE|MENU_CMDLINE_MODE,
												"\033:qa\r"},

	{"File.Save-Quit  :wqa",MENU_NORMAL_MODE,	":wqa\r"},
	{"File.Save-Quit  :wqa",MENU_VISUAL_MODE|MENU_INSERT_MODE|MENU_CMDLINE_MODE,
												"\033:wqa\r"},

	/* Edit menu */
	{"Edit.Undo",			MENU_NORMAL_MODE,	"u"},
	{"Edit.Redo",			MENU_NORMAL_MODE,	"\022"},

	{"Edit.Cut",			MENU_VISUAL_MODE,	"x"},
	{"Edit.Copy",			MENU_VISUAL_MODE,	"y"},
	{"Edit.Put Before",		MENU_NORMAL_MODE,	"[p"},
	{"Edit.Put Before",		MENU_INSERT_MODE,	"\017[p"},
	{"Edit.Put After",		MENU_NORMAL_MODE,	"]p"},
	{"Edit.Put After",		MENU_INSERT_MODE,	"\017]p"},
	{"Edit.Paste",			MENU_NORMAL_MODE,	"i\022*\033"},	/* CTRL-R * */
	{"Edit.Paste",			MENU_INSERT_MODE|MENU_CMDLINE_MODE,
												"\022*"},	/* CTRL-R * */
	{NULL,					0,					NULL}
};

/*
 * This is the call which starts the GUI.
 */
	void
gui_init()
{
	char_u	*env_str;
	int		i;

	gui.dying = FALSE;
	gui.in_focus = FALSE;
	gui.dragged_sb = SB_NONE;
	gui.dragged_wp = NULL;
	gui.col = gui.num_cols = 0;
	gui.row = gui.num_rows = 0;

	/* Initialise gui.cursor_row: */
	INVALIDATE_CURSOR();
	gui.scroll_region_top = 0;
	gui.scroll_region_bot = Rows - 1;
	gui.highlight_mask = HL_NORMAL;
	gui.char_width = 0;
	gui.char_height = 0;
	gui.char_ascent = 0;
	gui.border_width = 0;

	gui.selection.owned = FALSE;
	gui.selection.start.lnum = 0;
	gui.selection.start.col = 0;
	gui.selection.end.lnum = 0;
	gui.selection.end.col = 0;
	gui.selection.state = SELECT_CLEARED;

	gui.root_menu = NULL;
	gui.menu_is_active = TRUE;		/* default: include menu */

	gui.scrollbar_width = SB_DEFAULT_WIDTH;
	gui.menu_height = MENU_DEFAULT_HEIGHT;
	for (i = 0; i < 3; i++)
		gui.new_sb[i] = FALSE;

	gui.prev_wrap = -1;

	for (i = 0; default_menus[i].name != NULL; ++i)
		gui_add_menu_path((char_u *)default_menus[i].name,
									default_menus[i].mode, gui_menu_cb,
						  (char_u *)default_menus[i].command, TRUE);

	/*
	 * Switch on the mouse by default.
	 * This can be changed in the .gvimrc.
	 */
	set_string_option((char_u *)"mouse", -1, (char_u *)"a", TRUE);

	/*
	 * Get system wide defaults for gvim (Unix only)
	 */
#ifdef HAVE_CONFIG_H
	do_source(sys_gvimrc_fname, FALSE);
#endif

	/*
	 * Try to read GUI initialization commands from the following places:
	 * - environment variable GVIMINIT
	 * - the user gvimrc file (~/.gvimrc for Unix)
	 * The first that exists is used, the rest is ignored.
	 */
	if ((env_str = vim_getenv((char_u *)"GVIMINIT")) != NULL && *env_str != NUL)
	{
		sourcing_name = (char_u *)"GVIMINIT";
		do_cmdline(env_str, TRUE, TRUE);
		sourcing_name = NULL;
	}
	else
		do_source((char_u *)USR_GVIMRC_FILE, FALSE);

	/*
	 * Read initialization commands from ".gvimrc" in current directory.  This
	 * is only done if the 'exrc' option is set.  Because of security reasons
	 * we disallow shell and write commands now, except for unix if the file is
	 * owned by the user or 'secure' option has been reset in environment of
	 * global ".gvimrc".  Only do this if GVIMRC_FILE is not the same as
	 * USR_GVIMRC_FILE or sys_gvimrc_fname.
	 */
	if (p_exrc)
	{
#ifdef UNIX
		{
			struct stat s;

			/* if ".gvimrc" file is not owned by user, set 'secure' mode */
			if (stat(GVIMRC_FILE, &s) || s.st_uid != getuid())
				secure = p_secure;
		}
#else
		secure = p_secure;
#endif

		i = FAIL;
		if (fullpathcmp((char_u *)USR_GVIMRC_FILE,
											(char_u *)GVIMRC_FILE) != FPC_SAME
#ifdef HAVE_CONFIG_H
				&& fullpathcmp(sys_gvimrc_fname,
											(char_u *)GVIMRC_FILE) != FPC_SAME
#endif
				)
			i = do_source((char_u *)GVIMRC_FILE, FALSE);
	}

	/*
	 * Actually start the GUI itself.
	 */
	gui.in_use = TRUE;		/* Must be set after menus have been set up */
	if (gui_mch_init() == FAIL)
	{
		gui.in_use = FALSE;
		return;
	}

	maketitle();

	gui_create_initial_menus(gui.root_menu, NULL);
}

	void
gui_exit()
{
	gui.in_use = FALSE;
	gui_mch_exit();
}

/*
 * Set the font. Uses the 'font' option. The first font name that works is
 * used. If none is found, use the default font.
 */
	int
gui_init_font()
{
#define FONTLEN 100
	char_u	*font_list;
	char_u	font_name[FONTLEN];

	if (!gui.in_use)
		return FAIL;

	for (font_list = p_guifont; *font_list != NUL; )
	{
		/* Isolate one font name */
		(void)copy_option_part(&font_list, font_name, FONTLEN, ",");
		if (gui_mch_init_font(font_name) == OK)
			return OK;
	}

	/*
	 * Couldn't load any font in 'font', tell gui_mch_init_font() to try and
	 * find a font we can load.
	 */
  	return gui_mch_init_font(NULL);
}

	void
gui_set_cursor(row, col)
	int		row;
	int		col;
{
	gui.row = row;
	gui.col = col;
}

/*
 * gui_check_screen - check if the cursor is on the screen.
 */
	static void
gui_check_screen()
{
	if (gui.row >= Rows)
		gui.row = Rows - 1;
	if (gui.col >= Columns)
		gui.col = Columns - 1;
	if (gui.cursor_row >= Rows || gui.cursor_col >= Columns)
		INVALIDATE_CURSOR();
}

	void
gui_update_cursor()
{
	gui_check_screen();
	if (gui.row != gui.cursor_row || gui.col != gui.cursor_col)
	{
		gui_undraw_cursor();
		gui.cursor_row = gui.row;
		gui.cursor_col = gui.col;
		gui_mch_draw_cursor();
	}
}

/*
 * Should be called after the GUI window has been resized.  Its arguments are
 * the new width and height of the window in pixels.
 */
	void
gui_resize_window(pixel_width, pixel_height)
	int		pixel_width;
	int		pixel_height;
{
	gui.num_cols = (pixel_width - 2 * gui.border_offset) / gui.char_width;
	gui.num_rows = (pixel_height - 2 * gui.border_offset) / gui.char_height;

	gui_reset_scroll_region();
	/*
	 * At the "more" prompt there is no redraw, put the cursor at the last
	 * line here (why does it have to be one row too low???).
	 */
	if (State == ASKMORE)
		gui.row = gui.num_rows;

	if (gui.num_rows != screen_Rows || gui.num_cols != screen_Columns)
		set_winsize(0, 0, FALSE);
	gui_update_cursor();
}

/*
 * Make scroll region cover whole screen.
 */
	void
gui_reset_scroll_region()
{
	gui.scroll_region_top = 0;
	gui.scroll_region_bot = gui.num_rows - 1;
}

	void
gui_start_highlight(mask)
	long_u	mask;
{
	gui.highlight_mask |= mask;
}

	void
gui_stop_highlight(mask)
    long_u	mask;
{
	gui.highlight_mask &= ~mask;
}

	void
gui_write(s, len)
	char_u	*s;
	int		len;
{
	char_u	*p;
	int		arg1 = 0, arg2 = 0;

/* #define DEBUG_GUI_WRITE */
#ifdef DEBUG_GUI_WRITE
	{
		int i;
		char_u *str;

		printf("gui_write(%d):\n    ", len);
		for (i = 0; i < len; i++)
			if (s[i] == ESC)
			{
				if (i != 0)
					printf("\n    ");
				printf("<ESC>");
			}
			else
			{
				str = transchar(s[i]);
				if (str[0] && str[1])
					printf("<%s>", (char *)str);
				else
					printf("%s", (char *)str);
			}
		printf("\n");
	}
#endif
	while (len)
	{
		if (s[0] == '\n')
		{
			len--;
			s++;
			gui.col = 0;
			if (gui.row < gui.scroll_region_bot)
				gui.row++;
			else
				gui_mch_delete_lines(gui.scroll_region_top, 1);
		}
		else if (s[0] == '\r')
		{
			len--;
			s++;
			gui.col = 0;
		}
		else if (s[0] == Ctrl('G'))		/* Beep */
		{
			gui_mch_beep();
			len--;
			s++;
		}
		else if (s[0] == ESC && s[1] == '|')
		{
			p = s + 2;
			if (isdigit(*p))
			{
				arg1 = getdigits(&p);
				if (p > s + len)
					break;
				if (*p == ';')
				{
					++p;
					arg2 = getdigits(&p);
					if (p > s + len)
						break;
				}
			}
			switch (*p)
			{
				case 'C':		/* Clear screen */
					gui_mch_clear_block(0, 0, Rows - 1, Columns - 1);
					break;
				case 'M':		/* Move cursor */
					gui_set_cursor(arg1, arg2);
					break;
				case 'R':		/* Set scroll region */
					if (arg1 < arg2)
					{
						gui.scroll_region_top = arg1;
						gui.scroll_region_bot = arg2;
					}
					else
					{
						gui.scroll_region_top = arg2;
						gui.scroll_region_bot = arg1;
					}
					break;
				case 'd':		/* Delete line */
					gui_mch_delete_lines(gui.row, 1);
					break;
				case 'D':		/* Delete lines */
					gui_mch_delete_lines(gui.row, arg1);
					break;
				case 'i':		/* Insert line */
					gui_mch_insert_lines(gui.row, 1);
					break;
				case 'I':		/* Insert lines */
					gui_mch_insert_lines(gui.row, arg1);
					break;
				case '$':		/* Clear to end-of-line */
					gui_mch_clear_block(gui.row, gui.col, gui.row, Columns - 1);
					break;
				case 'h':		/* Turn on highlighting */
					gui_start_highlight(arg1);
					break;
				case 'H':		/* Turn off highlighting */
					gui_stop_highlight(arg1);
					break;
				case 'f':		/* flash the window (visual bell) */
					gui_mch_flash();
					break;
				default:
					p = s + 1;	/* Skip the ESC */
					break;
			}
			len -= ++p - s;
			s = p;
		}
		else if (s[0] < 0x20)			/* Ctrl character, shouldn't happen */
		{
			/*
			 * For some reason vim sends me a ^M after hitting return on the
			 * ':' line.  Make sure we ignore this here.
			 */
			len--;		/* Skip this char */
			s++;
		}
		else
		{
			p = s;
			while (len && *p >= 0x20)
			{
				len--;
				p++;
			}
			gui_outstr(s, p - s);
			s = p;
		}
	}
	gui_update_cursor();
	gui_update_scrollbars();
	gui_update_horiz_scrollbar();

	/* 
	 * We need to make sure this is cleared since Athena doesn't tell us when
	 * he is done dragging.
	 */
	gui.dragged_sb = SB_NONE;

	if (vim_strchr(p_guioptions, GO_ASEL) != NULL)
		gui_update_selection();
	gui_mch_flush();				/* In case vim decides to take a nap */
}

	static void
gui_outstr(s, len)
	char_u	*s;
	int		len;
{
	int		this_len;

	if (len == 0)
		return;

	if (len < 0)
		len = STRLEN(s);

	while (gui.col + len > Columns)
	{
		this_len = Columns - gui.col;
		gui_mch_outstr_nowrap(s, this_len, TRUE, FALSE);
		s += this_len;
		len -= this_len;
	}
	gui_mch_outstr_nowrap(s, len, TRUE, FALSE);
}

/*
 * Un-draw the cursor.  Actually this just redraws the character at the given
 * position.
 */
	void
gui_undraw_cursor()
{
	if (IS_CURSOR_VALID())
		gui_redraw_block(gui.cursor_row, gui.cursor_col, gui.cursor_row,
															gui.cursor_col);
}

	void
gui_redraw(x, y, w, h)
	int		x;
	int		y;
	int		w;
	int		h;
{
	int		row1, col1, row2, col2;

	row1 = Y_2_ROW(y);
	col1 = X_2_COL(x);
	row2 = Y_2_ROW(y + h - 1);
	col2 = X_2_COL(x + w - 1);

	gui_redraw_block(row1, col1, row2, col2);

	/* We may need to redraw the cursor */
	gui_update_cursor();
}

	void
gui_redraw_block(row1, col1, row2, col2)
	int		row1;
	int		col1;
	int		row2;
	int		col2;
{
	int		old_row, old_col;
	long_u	old_hl_mask;
	char_u	*screenp, *attrp, first_attr;
	int		idx, len;

	/* Don't try to draw outside the window! */
	/* Check everything, strange values may be caused by big border width */
	col1 = check_col(col1);
	col2 = check_col(col2);
	row1 = check_row(row1);
	row2 = check_row(row2);

	/* Don't try to update when NextScreen is not valid */
	if (!screen_cleared || NextScreen == NULL)
		return;

	/* Remember where our cursor was */
	old_row = gui.row;
	old_col = gui.col;
	old_hl_mask = gui.highlight_mask;

	for (gui.row = row1; gui.row <= row2; gui.row++)
	{
		gui.col = col1;
		screenp = LinePointers[gui.row] + gui.col;
		attrp = screenp + Columns;
		len = col2 - col1 + 1;
		while (len > 0)
		{
			switch (attrp[0])
			{
				case CHAR_INVERT:
					gui.highlight_mask = HL_INVERSE;
					break;
				case CHAR_UNDERL:
					gui.highlight_mask = HL_UNDERLINE;
					break;
				case CHAR_BOLD:
					gui.highlight_mask = HL_BOLD;
					break;
				case CHAR_STDOUT:
					gui.highlight_mask = HL_STANDOUT;
					break;
				case CHAR_ITALIC:
					gui.highlight_mask = HL_ITAL;
					break;
				case CHAR_NORMAL:
				default:
					gui.highlight_mask = HL_NORMAL;
					break;
			}
			first_attr = attrp[0];
			for (idx = 0; len > 0 && attrp[idx] == first_attr; idx++)
				--len;
			gui_mch_outstr_nowrap(screenp, idx, FALSE, FALSE);
			screenp += idx;
			attrp += idx;
		}
	}

	/* Put the cursor back where it was */
	gui.row = old_row;
	gui.col = old_col;
	gui.highlight_mask = old_hl_mask;
}

/*
 * Check bounds for column number
 */
	int
check_col(col)
	int		col;
{
	if (col < 0)
		return 0;
	if (col >= (int)Columns)
		return (int)Columns - 1;
	return col;
}

/*
 * Check bounds for row number
 */
	int
check_row(row)
	int		row;
{
	if (row < 0)
		return 0;
	if (row >= (int)Rows)
		return (int)Rows - 1;
	return row;
}

/*
 * Generic mouse support function.  Add a mouse event to the input buffer with
 * the given properties.
 *	button			--- may be any of MOUSE_LEFT, MOUSE_MIDDLE, MOUSE_RIGHT,
 *						MOUSE_DRAG, or MOUSE_RELEASE.
 *	x, y			--- Coordinates of mouse in pixels.
 *	repeated_click	--- TRUE if this click comes only a short time after a
 *						previous click.
 *	modifiers		--- Bit field which may be any of the following modifiers
 *						or'ed together: MOUSE_SHIFT | MOUSE_CTRL | MOUSE_ALT.
 * This function will ignore drag events where the mouse has not moved to a new
 * character.
 */
	void
gui_send_mouse_event(button, x, y, repeated_click, modifiers)
	int		button;
	int		x;
	int		y;
	int		repeated_click;
	int_u	modifiers;
{
	static int		prev_row = 0, prev_col = 0;
	static int		prev_button = -1;
	static linenr_t prev_topline = 0;
	static int		num_clicks = 1;
	char_u			string[6];
	int				row, col;

	row = Y_2_ROW(y);
	col = X_2_COL(x);

	/*
	 * If we are dragging and the mouse hasn't moved far enough to be on a
	 * different character, then don't send an event to vim.
	 */
	if (button == MOUSE_DRAG && row == prev_row && col == prev_col)
		return;

	/*
	 * If topline has changed (window scrolled) since the last click, reset
	 * repeated_click, because we don't want starting Visual mode when
	 * clicking on a different character in the text.
	 */
	if (curwin->w_topline != prev_topline)
		repeated_click = FALSE;

	string[0] = CSI;	/* this sequence is recognized by check_termcode() */
	string[1] = KS_MOUSE;
	string[2] = K_FILLER;
	if (button != MOUSE_DRAG && button != MOUSE_RELEASE)
	{
		if (repeated_click)
		{
			/*
			 * Handle multiple clicks.  They only count if the mouse is still
			 * pointing at the same character.
			 */
			if (button != prev_button || row != prev_row || col != prev_col)
				num_clicks = 1;
			else if (++num_clicks > 4)
				num_clicks = 1;
		}
		else
			num_clicks = 1;
		prev_button = button;
		prev_topline = curwin->w_topline;

		string[3] = (char_u)(button | 0x20);
		SET_NUM_MOUSE_CLICKS(string[3], num_clicks);
	}
	else
		string[3] = (char_u)button;

	string[3] |= modifiers;
	string[4] = (char_u)(col + ' ' + 1);
	string[5] = (char_u)(row + ' ' + 1);
	add_to_input_buf(string, 6);

	prev_row = row;
	prev_col = col;
}

/*
 * Selection stuff, for cutting and pasting text to other windows.
 */

/*
 * Check whether the VIsual area has changed, and if so try to become the owner
 * of the selection, and free any old converted selection we may still have
 * lying around.  If the VIsual mode has ended, make a copy of what was
 * selected so we can still give it to others.  Will probably have to make sure
 * this is called whenever VIsual mode is ended.
 */
	static void
gui_update_selection()
{
	/* If visual mode is only due to a redo command ("."), then ignore it */
	if (redo_VIsual_busy)
		return;
	if (!VIsual_active)
	{
		gui_mch_clear_selection();
		gui.selection.start = gui.selection.end = VIsual;
	}
	else if (lt(VIsual, curwin->w_cursor))
	{
		if (!equal(gui.selection.start, VIsual) ||
			!equal(gui.selection.end, curwin->w_cursor))
		{
			gui_mch_clear_selection();
			gui.selection.start = VIsual;
			gui.selection.end = curwin->w_cursor;
			gui_free_selection();
			gui_own_selection();
		}
	}
	else
	{
		if (!equal(gui.selection.start, curwin->w_cursor) ||
			!equal(gui.selection.end, VIsual))
		{
			gui_mch_clear_selection();
			gui.selection.start = curwin->w_cursor;
			gui.selection.end = VIsual;
			gui_free_selection();
			gui_own_selection();
		}
	}
}

	void
gui_own_selection()
{
	/*
	 * Also want to check somehow that we are reading from the keyboard rather
	 * than a mapping etc.
	 */
	if (!gui.selection.owned && gui_mch_own_selection())
	{
		gui_free_selection();
		gui.selection.owned = TRUE;
	}
}

	void
gui_lose_selection()
{
	gui_free_selection();
	gui.selection.owned = FALSE;
	gui_mch_lose_selection();
}

	void
gui_copy_selection()
{
	if (VIsual_active)
	{
		if (vim_strchr(p_guioptions, GO_ASEL) == NULL)
			gui_update_selection();
		gui_own_selection();
		if (gui.selection.owned)
			gui_get_selection();
	}
}

	void
gui_auto_select()
{
	if (vim_strchr(p_guioptions, GO_ASEL) != NULL)
		gui_copy_selection();
}

/*
 * Menu stuff.
 */

	void
gui_menu_cb(menu)
	GuiMenu	*menu;
{
	char_u	bytes[3 + sizeof(long_u)];

	bytes[0] = CSI;
	bytes[1] = KS_MENU;
	bytes[2] = K_FILLER;
	add_long_to_buf((long_u)menu, bytes + 3);
	add_to_input_buf(bytes, 3 + sizeof(long_u));
}

/*
 * Return the index into the menu->strings or menu->noremap arrays for the
 * current state.  Returns MENU_INDEX_INVALID if there is no mapping for the
 * given menu in the current mode.
 */
	int
gui_get_menu_index(menu, state)
	GuiMenu	*menu;
	int		state;
{
	int		idx;

	if (VIsual_active)
		idx = MENU_INDEX_VISUAL;
	else if ((state & NORMAL))
		idx = MENU_INDEX_NORMAL;
	else if ((state & INSERT))
		idx = MENU_INDEX_INSERT;
	else if ((state & CMDLINE))
		idx = MENU_INDEX_CMDLINE;
	else
		idx = MENU_INDEX_INVALID;

	if (idx != MENU_INDEX_INVALID && menu->strings[idx] == NULL)
		idx = MENU_INDEX_INVALID;
	return idx;
}

/*
 * Return the modes specified by the given menu command (eg :menu! returns
 * MENU_CMDLINE_MODE | MENU_INSERT_MODE).  If noremap is not NULL, then the
 * flag it points to is set according to whether the command is a "nore"
 * command.  If unmenu is not NULL, then the flag it points to is set
 * according to whether the command is an "unmenu" command.
 */
	static int
gui_get_menu_cmd_modes(cmd, force, noremap, unmenu)
	char_u	*cmd;
	int		force;		/* Was there a "!" after the command? */
	int		*noremap;
	int		*unmenu;
{
	int		modes = 0x0;

	if (*cmd == 'n' && cmd[1] != 'o')	/* nmenu, nnoremenu */
	{
		modes |= MENU_NORMAL_MODE;
		cmd++;
	}
	else if (*cmd == 'v')				/* vmenu, vnoremenu */
	{
		modes |= MENU_VISUAL_MODE;
		cmd++;
	}
	else if (*cmd == 'i')				/* imenu, inoremenu */
	{
		modes |= MENU_INSERT_MODE;
		cmd++;
	}
	else if (*cmd == 'c')				/* cmenu, cnoremenu */
	{
		modes |= MENU_CMDLINE_MODE;
		cmd++;
	}
	else if (force)					/* menu!, noremenu! */
		modes |= MENU_INSERT_MODE | MENU_CMDLINE_MODE;
	else							/* menu, noremenu */
		modes |= MENU_NORMAL_MODE | MENU_VISUAL_MODE;

	if (noremap != NULL)
		*noremap = (*cmd == 'n');
	if (unmenu != NULL)
		*unmenu = (*cmd == 'u');
	return modes;
}

/*
 * Do the :menu commands.
 */
	void
gui_do_menu(cmd, arg, force)
	char_u	*cmd;
	char_u	*arg;
	int		force;
{
	char_u	*menu_path;
	int		modes;
	char_u	*map_to;
	int		noremap;
	int		unmenu;
	char_u	*map_buf;

	modes = gui_get_menu_cmd_modes(cmd, force, &noremap, &unmenu);
	menu_path = arg;
	if (*menu_path == NUL)
	{
		gui_show_menus(menu_path, modes);
		return;
	}
	while (*arg && !vim_iswhite(*arg))
	{
		if ((*arg == '\\' || *arg == Ctrl('V')) && arg[1] != NUL)
			arg++;
		arg++;
	}
	if (*arg != NUL)
		*arg++ = NUL;
	arg = skipwhite(arg);
	map_to = arg;
	if (*map_to == NUL && !unmenu)
	{
		gui_show_menus(menu_path, modes);
		return;
	}
	else if (*map_to != NUL && unmenu)
	{
		EMSG("Trailing characters");
		return;
	}
	if (unmenu)
	{
		if (STRCMP(menu_path, "*") == 0)		/* meaning: remove all menus */
			menu_path = (char_u *)"";
		gui_remove_menu(&gui.root_menu, menu_path, modes);
	}
	else
	{
		/* Replace special key codes */
		map_to = replace_termcodes(map_to, &map_buf, FALSE);
		gui_add_menu_path(menu_path, modes, gui_menu_cb, map_to, noremap);
		vim_free(map_buf);
	}
}

/*
 * Add the menu with the given name to the menu hierarchy
 */
	static int
gui_add_menu_path(path_name, modes, call_back, call_data, noremap)
	char_u	*path_name;
	int		modes;
	void	(*call_back)();
	char_u	*call_data;
	int		noremap;
{
	GuiMenu	**menup;
	GuiMenu	*menu = NULL;
	GuiMenu	*parent;
	char_u	*p;
	char_u	*name;
	int		i;

	/* Make a copy so we can stuff around with it, since it could be const */
	path_name = strsave(path_name);
	if (path_name == NULL)
		return FAIL;
	menup = &gui.root_menu;
	parent = NULL;
	name = path_name;
	while (*name)
	{
		/* Get name of this element in the menu hierarchy */
		p = gui_menu_name_skip(name);

		/* See if it's already there */
		menu = *menup;
		while (menu != NULL)
		{
			if (STRCMP(name, menu->name) == 0)
			{
				if (*p == NUL && menu->children != NULL)
				{
					EMSG("Menu path must not lead to a sub-menu");
					vim_free(path_name);
					return FAIL;
				}
				else if (*p != NUL && menu->children == NULL)
				{
					EMSG("Part of menu-item path is not sub-menu");
					vim_free(path_name);
					return FAIL;
				}
				break;
			}
			menup = &menu->next;
			menu = menu->next;
		}
		if (menu == NULL)
		{
			if (*p == NUL && parent == NULL)
			{
				EMSG("Must not add menu items directly to menu bar");
				vim_free(path_name);
				return FAIL;
			}

			/* Not already there, so lets add it */
			menu = (GuiMenu *)alloc(sizeof(GuiMenu));
			if (menu == NULL)
			{
				vim_free(path_name);
				return FAIL;
			}
			menu->modes = modes;
			menu->name = strsave(name);
			menu->cb = NULL;
			for (i = 0; i < 4; i++)
			{
				menu->strings[i] = NULL;
				menu->noremap[i] = FALSE;
			}
			menu->children = NULL;
			menu->next = NULL;
			if (gui.in_use)	 /* Otherwise it will be added when GUI starts */
			{
				if (*p == NUL)
				{
					/* Real menu item, not sub-menu */
					gui_mch_add_menu_item(menu, parent);

					/* Want to update menus now even if mode not changed */
					force_menu_update = TRUE;
				}
				else
				{
					/* Sub-menu (not at end of path yet) */
					gui_mch_add_menu(menu, parent);
				}
			}
			*menup = menu;
		}
		else
		{
			/*
			 * If this menu option was previously only available in other
			 * modes, then make sure it's available for this one now
			 */
			menu->modes |= modes;
		}

		menup = &menu->children;
		parent = menu;
		name = p;
	}
	vim_free(path_name);

	if (menu != NULL)
	{
		menu->cb = call_back;
		p = (call_data == NULL) ? NULL : strsave(call_data);

		/* May match more than one of these */
		if (modes & MENU_NORMAL_MODE)
		{
			gui_free_menu_string(menu, MENU_INDEX_NORMAL);
			menu->strings[MENU_INDEX_NORMAL] = p;
			menu->noremap[MENU_INDEX_NORMAL] = noremap;
		}
		if (modes & MENU_VISUAL_MODE)
		{
			gui_free_menu_string(menu, MENU_INDEX_VISUAL);
			menu->strings[MENU_INDEX_VISUAL] = p;
			menu->noremap[MENU_INDEX_VISUAL] = noremap;
		}
		if (modes & MENU_INSERT_MODE)
		{
			gui_free_menu_string(menu, MENU_INDEX_INSERT);
			menu->strings[MENU_INDEX_INSERT] = p;
			menu->noremap[MENU_INDEX_INSERT] = noremap;
		}
		if (modes & MENU_CMDLINE_MODE)
		{
			gui_free_menu_string(menu, MENU_INDEX_CMDLINE);
			menu->strings[MENU_INDEX_CMDLINE] = p;
			menu->noremap[MENU_INDEX_CMDLINE] = noremap;
		}
	}
	return OK;
}

/*
 * Remove the (sub)menu with the given name from the menu hierarchy
 * Called recursively.
 */
	static int
gui_remove_menu(menup, name, modes)
	GuiMenu	**menup;
	char_u	*name;
	int		modes;
{
	GuiMenu	*menu;
	GuiMenu	*child;
	char_u	*p;

	if (*menup == NULL)
		return OK;			/* Got to bottom of hierarchy */

	/* Get name of this element in the menu hierarchy */
	p = gui_menu_name_skip(name);

	/* Find the menu */
	menu = *menup;
	while (menu != NULL)
	{
		if (*name == NUL || STRCMP(name, menu->name) == 0)
		{
			if (*p != NUL && menu->children == NULL)
			{
				EMSG("Part of menu-item path is not sub-menu");
				return FAIL;
			}
			if ((menu->modes & modes) != 0x0)
			{
				if (gui_remove_menu(&menu->children, p, modes) == FAIL)
					return FAIL;
			}
			else if (*name != NUL)
			{
				EMSG("Menu only exists in another mode");
				return FAIL;
			}

			/*
			 * When name is empty, we are removing all menu items for the given
			 * modes, so keep looping, otherwise we are just removing the named
			 * menu item (which has been found) so break here.
			 */
			if (*name != NUL)
				break;

			/* Remove the menu item for the given mode[s] */
			menu->modes &= ~modes;

			if (menu->modes == 0x0)
			{
				/* The menu item is no longer valid in ANY mode, so delete it */
				*menup = menu->next;
				gui_free_menu(menu);
			}
			else
				menup = &menu->next;
		}
		else
			menup = &menu->next;
		menu = *menup;
	}
	if (*name != NUL)
	{
		if (menu == NULL)
		{
			EMSG("No menu of that name");
			return FAIL;
		}

		/* Recalculate modes for menu based on the new updated children */
		menu->modes = 0x0;
		for (child = menu->children; child != NULL; child = child->next)
			menu->modes |= child->modes;
		if (menu->modes == 0x0)
		{
			/* The menu item is no longer valid in ANY mode, so delete it */
			*menup = menu->next;
			gui_free_menu(menu);
		}
	}

	return OK;
}

/*
 * Free the given menu structure
 */
	static void
gui_free_menu(menu)
	GuiMenu	*menu;
{
	int		i;

	gui_mch_destroy_menu(menu);		/* Free machine specific menu structures */
	vim_free(menu->name);
	for (i = 0; i < 4; i++)
		gui_free_menu_string(menu, i);
	vim_free(menu);

	/* Want to update menus now even if mode not changed */
	force_menu_update = TRUE;
}

/*
 * Free the menu->string with the given index.
 */
	static void
gui_free_menu_string(menu, idx)
	GuiMenu	*menu;
	int		idx;
{
	int		count = 0;
	int		i;

	for (i = 0; i < 4; i++)
		if (menu->strings[i] == menu->strings[idx])
			count++;
	if (count == 1)
		vim_free(menu->strings[idx]);
	menu->strings[idx] = NULL;
}

/*
 * Show the mapping associated with a menu item or hierarchy in a sub-menu.
 */
	static int
gui_show_menus(path_name, modes)
	char_u	*path_name;
	int		modes;
{
	char_u	*p;
	char_u	*name;
	GuiMenu	*menu;
	GuiMenu	*parent = NULL;

	menu = gui.root_menu;
	name = path_name = strsave(path_name);
	if (path_name == NULL)
		return FAIL;

	/* First, find the (sub)menu with the given name */
	while (*name)
	{
		p = gui_menu_name_skip(name);
		while (menu != NULL)
		{
			if (STRCMP(name, menu->name) == 0)
			{
				/* Found menu */
				if (*p != NUL && menu->children == NULL)
				{
					EMSG("Part of menu-item path is not sub-menu");
					vim_free(path_name);
					return FAIL;
				}
				else if ((menu->modes & modes) == 0x0)
				{
					EMSG("Menu only exists in another mode");
					vim_free(path_name);
					return FAIL;
				}
				break;
			}
			menu = menu->next;
		}
		if (menu == NULL)
		{
			EMSG("No menu of that name");
			vim_free(path_name);
			return FAIL;
		}
		name = p;
		parent = menu;
		menu = menu->children;
	}

	/* Now we have found the matching menu, and we list the mappings */
	set_highlight('t');		/* Highlight title */
	start_highlight();
	MSG_OUTSTR("\n--- Menus ---");
	stop_highlight();

	gui_show_menus_recursive(parent, modes, 0);
	return OK;
}

/*
 * Recursively show the mappings associated with the menus under the given one
 */
	static void
gui_show_menus_recursive(menu, modes, depth)
	GuiMenu	*menu;
	int		modes;
	int		depth;
{
	int		i;
	int		bit;

	if (menu != NULL && (menu->modes & modes) == 0x0)
		return;

	if (menu != NULL)
	{
		msg_outchar('\n');
		if (got_int)			/* "q" hit for "--more--" */
			return;
		for (i = 0; i < depth; i++)
			MSG_OUTSTR("  ");
		set_highlight('d');			/* Same as for directories!? */
		start_highlight();
		msg_outstr(menu->name);
		stop_highlight();
	}

	if (menu != NULL && menu->children == NULL)
	{
		for (bit = 0; bit < 4; bit++)
			if ((menu->modes & modes & (1 << bit)) != 0)
			{
				msg_outchar('\n');
				if (got_int)			/* "q" hit for "--more--" */
					return;
				for (i = 0; i < depth + 2; i++)
					MSG_OUTSTR("  ");
				msg_outchar("nvic"[bit]);
				if (menu->noremap[bit])
					msg_outchar('*');
				else
					msg_outchar(' ');
				MSG_OUTSTR("  ");
				msg_outtrans_special(menu->strings[bit], TRUE);
			}
	}
	else
	{
		if (menu == NULL)
		{
			menu = gui.root_menu;
			depth--;
		}
		else
			menu = menu->children;
		for (; menu != NULL; menu = menu->next)
			gui_show_menus_recursive(menu, modes, depth + 1);
	}
}

/*
 * Used when expanding menu names.
 */
static GuiMenu	*expand_menu = NULL;
static int		expand_modes = 0x0;

/*
 * Work out what to complete when doing command line completion of menu names.
 */
	char_u *
gui_set_context_in_menu_cmd(cmd, arg, force)
	char_u	*cmd;
	char_u	*arg;
	int		force;
{
	char_u	*after_dot;
	char_u	*p;
	char_u	*path_name = NULL;
	char_u	*name;
	int		unmenu;
	GuiMenu	*menu;

	expand_context = EXPAND_UNSUCCESSFUL;

	after_dot = arg;
	for (p = arg; *p && !vim_iswhite(*p); ++p)
	{
		if ((*p == '\\' || *p == Ctrl('V')) && p[1] != NUL)
			p++;
		else if (*p == '.')
			after_dot = p + 1;
	}
	if (*p == NUL)				/* Complete the menu name */
	{
		/*
		 * With :unmenu, you only want to match menus for the appropriate mode.
		 * With :menu though you might want to add a menu with the same name as
		 * one in another mode, so match menus fom other modes too.
		 */
		expand_modes = gui_get_menu_cmd_modes(cmd, force, NULL, &unmenu);
		if (!unmenu)
			expand_modes = MENU_ALL_MODES;

		menu = gui.root_menu;
		if (after_dot != arg)
		{
			path_name = alloc(after_dot - arg);
			if (path_name == NULL)
				return NULL;
			STRNCPY(path_name, arg, after_dot - arg - 1);
			path_name[after_dot - arg - 1] = NUL;
		}
		name = path_name;
		while (name != NULL && *name)
		{
			p = gui_menu_name_skip(name);
			while (menu != NULL)
			{
				if (STRCMP(name, menu->name) == 0)
				{
					/* Found menu */
					if ((*p != NUL && menu->children == NULL)
						|| ((menu->modes & expand_modes) == 0x0))
					{
						/*
						 * Menu path continues, but we have reached a leaf.
						 * Or menu exists only in another mode.
						 */
						vim_free(path_name);
						return NULL;
					}
					break;
				}
				menu = menu->next;
			}
			if (menu == NULL)
			{
				/* No menu found with the name we were looking for */
				vim_free(path_name);
				return NULL;
			}
			name = p;
			menu = menu->children;
		}

		expand_context = EXPAND_MENUS;
		expand_pattern = after_dot;
		expand_menu = menu;
	}
	else						/* We're in the mapping part */
		expand_context = EXPAND_NOTHING;
	return NULL;
}

/*
 * Expand the menu names.
 */
	int
gui_ExpandMenuNames(prog, num_file, file)
	regexp	*prog;
	int		*num_file;
	char_u	***file;
{
	GuiMenu	*menu;
	int		round;
	int		count;

	/*
	 * round == 1: Count the matches.
	 * round == 2: Save the matches into the array.
	 */
	for (round = 1; round <= 2; ++round)
	{
		count = 0;
		for (menu = expand_menu; menu != NULL; menu = menu->next)
			if ((menu->modes & expand_modes) != 0x0
				&& vim_regexec(prog, menu->name, TRUE))
			{
				if (round == 1)
					count++;
				else
					(*file)[count++] = strsave_escaped(menu->name,
													   (char_u *)" \t\\.");
			}
		if (round == 1)
		{
			*num_file = count;
			if (count == 0 || (*file = (char_u **)
						 alloc((unsigned)(count * sizeof(char_u *)))) == NULL)
				return FAIL;
		}
	}
	return OK;
}

/*
 * Skip over this element of the menu path and return the start of the next
 * element.  Any \ and ^Vs are removed from the current element.
 */
	static char_u *
gui_menu_name_skip(name)
	char_u	*name;
{
	char_u	*p;

	for (p = name; *p && *p != '.'; p++)
		if (*p == '\\' || *p == Ctrl('V'))
		{
			STRCPY(p, p + 1);
			if (*p == NUL)
				break;
		}
	if (*p)
		*p++ = NUL;
	return p;
}

/*
 * After we have started the GUI, then we can create any menus that have been
 * defined.  This is done once here.  gui_add_menu_path() may have already been
 * called to define these menus, and may be called again.  This function calls
 * itself recursively.	Should be called at the top level with:
 * gui_create_initial_menus(gui.root_menu, NULL);
 */
	static void
gui_create_initial_menus(menu, parent)
	GuiMenu	*menu;
	GuiMenu	*parent;
{
	while (menu)
	{
		if (menu->children != NULL)
		{
			gui_mch_add_menu(menu, parent);
			gui_create_initial_menus(menu->children, menu);
		}
		else
			gui_mch_add_menu_item(menu, parent);
		menu = menu->next;
	}
}


/*
 * Set which components are present.
 * If "oldval" is not NULL, "oldval" is the previous value, the new * value is
 * in p_guioptions.
 */
	void
gui_init_which_components(oldval)
	char_u	*oldval;
{
	char_u	*p;
	int		i;
	int		grey_old, grey_new;
	char_u	*temp;

	if (oldval != NULL)
	{
		/*
		 * Check if the menu's go from grey to non-grey or vise versa.
		 */
		grey_old = (vim_strchr(oldval, GO_GREY) != NULL);
		grey_new = (vim_strchr(p_guioptions, GO_GREY) != NULL);
		if (grey_old != grey_new)
		{
			temp = p_guioptions;
			p_guioptions = oldval;
			gui_x11_update_menus(MENU_ALL_MODES);
			p_guioptions = temp;
		}
	}

	gui.menu_is_active = FALSE;
	for (i = 0; i < 3; i++)
		gui.which_scrollbars[i] = FALSE;
	for (p = p_guioptions; *p; p++)
		switch (*p)
		{
			case GO_LEFT:
				gui.which_scrollbars[SB_LEFT] = TRUE;
				break;
			case GO_RIGHT:
				gui.which_scrollbars[SB_RIGHT] = TRUE;
				break;
			case GO_BOT:
				gui.which_scrollbars[SB_BOTTOM] = TRUE;
				break;
			case GO_MENUS:
				gui.menu_is_active = TRUE;
				break;
			case GO_GREY:
				/* make menu's have grey items, ignored here */
				break;
			default:
				/* Should give error message for internal error */
				break;
		}
	if (gui.in_use)
		gui_mch_create_which_components();
}


/*
 * Vertical scrollbar stuff:
 */

	static void
gui_update_scrollbars()
{
	WIN		*wp;
	int		worst_update = SB_UPDATE_NOTHING;
	int		val, size, max;
	int		which_sb;
	int		cmdline_height;

	/*
	 * Don't want to update a scrollbar while we're dragging it.  But if we
	 * have both a left and right scrollbar, and we drag one of them, we still
	 * need to update the other one.
	 */
	if ((gui.dragged_sb == SB_LEFT || gui.dragged_sb == SB_RIGHT) &&
			(!gui.which_scrollbars[SB_LEFT] || !gui.which_scrollbars[SB_RIGHT]))
		return;

	if (gui.dragged_sb == SB_LEFT || gui.dragged_sb == SB_RIGHT)
	{
		/*
		 * If we have two scrollbars and one of them is being dragged, just
		 * copy the scrollbar position from the dragged one to the other one.
		 */
		which_sb = SB_LEFT + SB_RIGHT - gui.dragged_sb;
		if (gui.dragged_wp != NULL)
			gui.dragged_wp->w_scrollbar.update[which_sb] = SB_UPDATE_VALUE;
		else
			gui.cmdline_sb.update[which_sb] = SB_UPDATE_VALUE;

		gui_mch_update_scrollbars(SB_UPDATE_VALUE, which_sb);
		return;
	}

	/* Return straight away if there is neither a left nor right scrollbar */
	if (!gui.which_scrollbars[SB_LEFT] && !gui.which_scrollbars[SB_RIGHT])
		return;

	cmdline_height = Rows;
	for (wp = firstwin; wp; wp = wp->w_next)
	{
		cmdline_height -= wp->w_height + wp->w_status_height;
		if (wp->w_buffer == NULL)		/* just in case */
			continue;
#ifdef SCROLL_PAST_END
		max = wp->w_buffer->b_ml.ml_line_count;
#else
		max = wp->w_buffer->b_ml.ml_line_count + wp->w_height - 1;
#endif
		if (max < 1)					/* empty buffer */
			max = 1;
		val = wp->w_topline;
		size = wp->w_height;
#ifdef SCROLL_PAST_END
		if (val > max)					/* just in case */
			val = max;
#else
		if (size > max)					/* just in case */
			size = max;
		if (val > max - size + 1)
		{
			val = max - size + 1;
			if (val < 1)				/* minimal value is 1 */
				val = 1;
		}
#endif
		if (size < 1 || wp->w_botline - 1 > max)
		{
			/*
			 * This can happen during changing files.  Just don't update the
			 * scrollbar for now.
			 */
		}
		else if (wp->w_scrollbar.height == 0)
		{
			/* Must be a new window */
			wp->w_scrollbar.update[SB_LEFT] = SB_UPDATE_CREATE;
			wp->w_scrollbar.update[SB_RIGHT] = SB_UPDATE_CREATE;
			wp->w_scrollbar.value = val;
			wp->w_scrollbar.size = size;
			wp->w_scrollbar.max = max;
			wp->w_scrollbar.top = wp->w_winpos;
			wp->w_scrollbar.height = wp->w_height;
			wp->w_scrollbar.status_height = wp->w_status_height;
			gui.num_scrollbars++;
			worst_update = SB_UPDATE_CREATE;
		}
		else if (wp->w_scrollbar.top != wp->w_winpos
			|| wp->w_scrollbar.height != wp->w_height
			|| wp->w_scrollbar.status_height != wp->w_status_height)
		{
			/* Height of scrollbar has changed */
			wp->w_scrollbar.update[SB_LEFT] = SB_UPDATE_HEIGHT;
			wp->w_scrollbar.update[SB_RIGHT] = SB_UPDATE_HEIGHT;
			wp->w_scrollbar.value = val;
			wp->w_scrollbar.size = size;
			wp->w_scrollbar.max = max;
			wp->w_scrollbar.top = wp->w_winpos;
			wp->w_scrollbar.height = wp->w_height;
			wp->w_scrollbar.status_height = wp->w_status_height;
			if (worst_update < SB_UPDATE_HEIGHT)
				worst_update = SB_UPDATE_HEIGHT;
		}
		else if (wp->w_scrollbar.value != val
			|| wp->w_scrollbar.size != size
			|| wp->w_scrollbar.max != max)
		{
			/* Thumb of scrollbar has moved */
			wp->w_scrollbar.update[SB_LEFT] = SB_UPDATE_VALUE;
			wp->w_scrollbar.update[SB_RIGHT] = SB_UPDATE_VALUE;
			wp->w_scrollbar.value = val;
			wp->w_scrollbar.size = size;
			wp->w_scrollbar.max = max;
			if (worst_update < SB_UPDATE_VALUE)
				worst_update = SB_UPDATE_VALUE;
		}

		/*
		 * We may have just created the left scrollbar say, when we already had
		 * the right one.
		 */
		if (gui.new_sb[SB_LEFT])
			wp->w_scrollbar.update[SB_LEFT] = SB_UPDATE_CREATE;
		if (gui.new_sb[SB_RIGHT])
			wp->w_scrollbar.update[SB_RIGHT] = SB_UPDATE_CREATE;
	}
	if (cmdline_height < 1)
		cmdline_height = 1;             /* Shouldn't happen, but just in case */

	/* Check the command line scrollbar */
	if (gui.cmdline_sb.height != cmdline_height
		|| gui.cmdline_sb.status_height != lastwin->w_status_height)
	{
		/* Height of scrollbar has changed */
		gui.cmdline_sb.update[SB_LEFT] = SB_UPDATE_HEIGHT;
		gui.cmdline_sb.update[SB_RIGHT] = SB_UPDATE_HEIGHT;
		gui.cmdline_sb.value = 0;
		gui.cmdline_sb.size = 1;			/* No thumb */
		gui.cmdline_sb.max = 0;
		gui.cmdline_sb.top = Rows - cmdline_height;
		gui.cmdline_sb.height = cmdline_height;
		gui.cmdline_sb.status_height = lastwin->w_status_height;
		if (worst_update < SB_UPDATE_HEIGHT)
			worst_update = SB_UPDATE_HEIGHT;
	}

	/*
	 * If we have just created the left or right scrollbar-box, then we need to
	 * update the height of the command line scrollbar (it will already be
	 * created).
	 */
	if (gui.new_sb[SB_LEFT])
	{
		gui.cmdline_sb.update[SB_LEFT] = SB_UPDATE_HEIGHT;
		worst_update = SB_UPDATE_CREATE;
		gui.new_sb[SB_LEFT] = FALSE;
	}
	if (gui.new_sb[SB_RIGHT])
	{
		gui.cmdline_sb.update[SB_RIGHT] = SB_UPDATE_HEIGHT;
		worst_update = SB_UPDATE_CREATE;
		gui.new_sb[SB_RIGHT] = FALSE;
	}

	if (worst_update != SB_UPDATE_NOTHING)
	{
		if (gui.which_scrollbars[SB_LEFT] && gui.dragged_sb != SB_LEFT)
			gui_mch_update_scrollbars(worst_update, SB_LEFT);
		if (gui.which_scrollbars[SB_RIGHT] && gui.dragged_sb != SB_RIGHT)
			gui_mch_update_scrollbars(worst_update, SB_RIGHT);
	}
}

/*
 * Scroll a window according to the values set in the globals current_scrollbar
 * and scrollbar_value.  Return TRUE if the cursor in the current window moved
 * or FALSE otherwise.
 */
	int
gui_do_scroll()
{
	WIN		*wp, *old_wp;
	int		i;
	FPOS	old_cursor;

	for (wp = firstwin, i = 0; i < current_scrollbar; i++)
	{
		if (wp == NULL)
			break;
		wp = wp->w_next;
	}
	if (wp != NULL)
	{
		old_cursor = curwin->w_cursor;
		old_wp = curwin;
		curwin = wp;
		curbuf = wp->w_buffer;
		i = (long)scrollbar_value - (long)wp->w_topline;
		if (i < 0)
			scrolldown(-i);
		else if (i > 0)
			scrollup(i);
		if (p_so)
			cursor_correct();
		coladvance(curwin->w_curswant);

		curwin = old_wp;
		curbuf = old_wp->w_buffer;

		if (wp == curwin)
			cursupdate();			/* fix window for 'so' */
		wp->w_redr_type = VALID;
		updateWindow(wp);		/* update window, status line, and cmdline */

		return !equal(curwin->w_cursor, old_cursor);
	}
	else
	{
		/* Command-line scrollbar, unimplemented */
		return FALSE;
	}
}


/*
 * Horizontal scrollbar stuff:
 */

	static void
gui_update_horiz_scrollbar()
{
	int		value, size, max;

	if (!gui.which_scrollbars[SB_BOTTOM])
		return;

	if (gui.dragged_sb == SB_BOTTOM)
		return;

	if (curwin->w_p_wrap && gui.prev_wrap)
		return;
			
	/*
	 * It is possible for the cursor to be invalid if we're in the middle of
	 * something (like changing files).  If so, don't do anything for now.
	 */
	if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
		return;

	if (curwin->w_p_wrap)
	{
		value = 0;
		size = Columns;
#ifdef SCROLL_PAST_END
		max = 0;
#else
		max = Columns - 1;
#endif
	}
	else
	{
		value = curwin->w_leftcol;
		size = Columns;
#ifdef SCROLL_PAST_END
		max = gui_get_max_horiz_scroll();
#else
		max = gui_get_max_horiz_scroll() + Columns - 1;
#endif
	}
	gui_mch_update_horiz_scrollbar(value, size, max + 1);
	gui.prev_wrap = curwin->w_p_wrap;
}

/*
 * Determine the maximum value for scrolling right.
 */
	int
gui_get_max_horiz_scroll()
{
	int		max = 0;
	char_u	*p;

	p = ml_get_curline();
	if (p[0] != NUL)
		while (p[1] != NUL)				/* Don't count last character */
			max += chartabsize(*p++, (colnr_t)max);
	return max;
}

/*
 * Do a horizontal scroll.  Return TRUE if the cursor moved, or FALSE otherwise
 */
	int
gui_do_horiz_scroll()
{
	char_u	*p;
	int		i;
	int		vcol;
	int		ret_val = FALSE;

	/* no wrapping, no scrolling */
	if (curwin->w_p_wrap)
		return FALSE;

	curwin->w_leftcol = scrollbar_value;

	i = 0;
	vcol = 0;
	p = ml_get_curline();
	while (p[i] && i < curwin->w_cursor.col && vcol < curwin->w_leftcol)
		vcol += chartabsize(p[i++], (colnr_t)vcol);
	if (vcol < curwin->w_leftcol)
	{
		/*
		 * Cursor is on a character that is at least partly off the left hand
		 * side of the screen.
		 */
		while (p[i] && vcol < curwin->w_leftcol)
			vcol += chartabsize(p[i++], (colnr_t)vcol);
		curwin->w_cursor.col = i;
		curwin->w_set_curswant = TRUE;
		ret_val = TRUE;
	}

	while (p[i] && i <= curwin->w_cursor.col
									&& vcol <= curwin->w_leftcol + Columns)
		vcol += chartabsize(p[i++], (colnr_t)vcol);
	if (vcol > curwin->w_leftcol + Columns)
	{
		/*
		 * Cursor is on a character that is at least partly off the right hand
		 * side of the screen.
		 */
		if (i < 2)
			i = 0;
		else
			i -= 2;
		curwin->w_cursor.col = i;
		curwin->w_set_curswant = TRUE;
		ret_val = TRUE;
	}
	updateScreen(NOT_VALID);
	return ret_val;
}