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

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

/* vi:set ts=4 sw=4:
 *
 * VIM - Vi IMproved			by Bram Moolenaar
 *								GUI 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.
 *
 * Windows GUI.
 *
 * GUI support for Microsoft Windows.  Win32 initially; maybe Win16 later 
 *
 * George V. Reilly <gvr@halcyon.com> wrote the original Win32 GUI.
 * Robert Webb reworked it to use the existing GUI stuff and added menu,
 * scrollbars, etc.
 *
 * Note: Clipboard stuff, for cutting and pasting text to other windows, is in
 * os_win32.c.  (It can also be done from the terminal version).
 */

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

#include <windows.h>
#include <shellapi.h>

#ifdef WIN32
# include <windowsx.h>
#else /* WIN16 */
# include <windowsx.h16>
#endif

#ifdef PROTO
/*
 * Define a few things for generating prototypes.  This is just to avoid
 * syntax errors, the defines do not need to be correct.
 */
# define HINSTANCE	void *
# define HWND		void *
# define HMENU		void *
# define UINT		int	
# define WPARAM		int
# define LPARAM		int
typedef int LOGFONT[];
# define VOID		void
# define CALLBACK
# define DWORD 		int
# define HDROP 		int
# define BOOL		int
# define LPRECT 	int
# define LRESULT 	int
# define WINAPI
# define LPSTR 		int
# define LPWINDOWPOS int
#endif

/* Local variables: */
static HINSTANCE	s_hinst = NULL;
static HWND			s_hwnd = NULL;
static HWND			s_textArea = NULL;
static HMENU		s_menuBar = NULL;
static UINT			s_menu_id = 0;
static UINT			s_wait_timer = 0;	/* Timer for get char from user */

static int			s_alt_key_down = FALSE;

static UINT			s_uMsg = 0;
static WPARAM		s_wParam = 0;
static LPARAM		s_lParam = 0;

static int			s_timed_out = FALSE;

static const LOGFONT s_lfDefault =
{
	-12, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET,
	OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
	PROOF_QUALITY, FIXED_PITCH | FF_DONTCARE, 
	"Fixedsys"	/* see _ReadVimIni */
};

static LOGFONT s_lfNormal;

static struct
{
	UINT	key_sym;
	char_u	vim_code0;
	char_u	vim_code1;
} special_keys[] =
{
	{VK_UP,			'k', 'u'},
	{VK_DOWN,		'k', 'd'},
	{VK_LEFT,		'k', 'l'},
	{VK_RIGHT,		'k', 'r'},

	{VK_F1,			'k', '1'},
	{VK_F2,			'k', '2'},
	{VK_F3,			'k', '3'},
	{VK_F4,			'k', '4'},
	{VK_F5,			'k', '5'},
	{VK_F6,			'k', '6'},
	{VK_F7,			'k', '7'},
	{VK_F8,			'k', '8'},
	{VK_F9,			'k', '9'},
	{VK_F10,		'k', ';'},

	{VK_F11,		'F', '1'},
	{VK_F12,		'F', '2'},
	{VK_F13,		'F', '3'},
	{VK_F14,		'F', '4'},
	{VK_F15,		'F', '5'},
	{VK_F16,		'F', '6'},
	{VK_F17,		'F', '7'},
	{VK_F18,		'F', '8'},
	{VK_F19,		'F', '9'},
	{VK_F20,		'F', 'A'},

	{VK_F21,		'F', 'B'},
	{VK_F22,		'F', 'C'},
	{VK_F23,		'F', 'D'},
	{VK_F24,		'F', 'E'},		/* winuser.h defines up to F24 */

	{VK_HELP,		'%', '1'},
	{VK_BACK,		'k', 'b'},
	{VK_INSERT,		'k', 'I'},
	{VK_DELETE,		'k', 'D'},
	{VK_HOME,		'k', 'h'},
	{VK_END,		'@', '7'},
	{VK_PRIOR,		'k', 'P'},
	{VK_NEXT,		'k', 'N'},
	{VK_PRINT,		'%', '9'},

	/* Keys that we want to be able to use any modifier with: */
	{VK_SPACE,		' ', NUL},
	{VK_TAB,		TAB, NUL},
	{VK_ESCAPE,		ESC, NUL},
	{NL,			NL, NUL},
	{CR,			CR, NUL},

	/* End of list marker: */
	{0,				0, 0}
};


#define VIM_NAME		"vim"
#define VIM_CLASS		"Vim"


#ifdef DEBUG
/* Print out the last Windows error message */
	static void
print_windows_error()
{
	LPVOID	lpMsgBuf;

	FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
				  NULL, GetLastError(),
				  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
				  (LPTSTR) &lpMsgBuf, 0, NULL);
	TRACE("Error: %s\n", lpMsgBuf);
	LocalFree(lpMsgBuf);
}
#endif /* DEBUG */

/*
 * Figure out how high the menu bar is at the moment.
 */
	static int
gui_w32_get_menu_height(fix_window)
	int		fix_window;		/* If TRUE, resize window if menu height changed */
{
	static int	old_menu_height = -1;

	RECT	rc1, rc2;
	int		num;
	int		menu_height;

	num = GetMenuItemCount(s_menuBar);
	if (num == 0)
		menu_height = 0;
	else
	{
		GetMenuItemRect(s_hwnd, s_menuBar, 0, &rc1);
		GetMenuItemRect(s_hwnd, s_menuBar, num - 1, &rc2);
		menu_height = rc2.bottom - rc1.top + 1;
	}

	if (fix_window && menu_height != old_menu_height)
		gui_set_winsize(FALSE);

	old_menu_height = menu_height;
	return menu_height;
}

/*
 * Call-back routines.
 */

	static VOID CALLBACK
_OnTimer(
	HWND hwnd,
	UINT uMsg,
	UINT idEvent,
	DWORD dwTime)
{
	MSG msg;

	/*
	TRACE("Got timer event, id %d, s_wait_timer %d\n", idEvent, s_wait_timer);
	*/
	KillTimer(NULL, idEvent);
	s_timed_out = TRUE;

	/* Eat spurious WM_TIMER messages */
	while(PeekMessage(&msg, hwnd, WM_TIMER, WM_TIMER, PM_REMOVE));
	if (idEvent == s_wait_timer)
		s_wait_timer = 0;
}

/*
 * Get this message when the user clicks on the cross in the top right corner
 * of a Windows95 window.
 */
	static void
_OnDestroy(
	HWND hwnd)
{
	preserve_exit();		/* output IObuff, preserve files and exit */

	FORWARD_WM_DESTROY(hwnd, DefWindowProc);
}

	static void
_OnDropFiles(
	HWND hwnd,
	HDROP hDrop)
{
	char	szFile[_MAX_PATH]; 
	UINT	cFiles = DragQueryFile(hDrop, 0xFFFFFFFF, szFile, _MAX_PATH);
	UINT	i;
	char_u	*fname;
	char_u	string[1];

    /* TRACE("_OnDropFiles: %d files dropped\n", cFiles); */

	/* reset_VIsual(); */
	if (VIsual_active)
	{
		end_visual_mode();
		update_curbuf(NOT_VALID);		/* delete the inversion */
	}

	for (i = 0; i < cFiles; ++i)
	{
		DragQueryFile(hDrop, i, szFile, _MAX_PATH);
        /* TRACE("  dropped %2u: '%s'\n", i, szFile); */

		mch_dirname(IObuff, IOSIZE);
		fname = shorten_fname(szFile, IObuff);
		if (fname == NULL)
			fname = szFile;
        win_split(0, TRUE);
		(void)do_ecmd(0, fname, NULL, NULL, 0, ECMD_FORCEIT);
	}

	DragFinish(hDrop);

	/* This makes it redraw properly */
	string[0] = Ctrl('L');
	add_to_input_buf(string, 1);
}

	static void
_OnChar(
	HWND hwnd,
	UINT ch,
	int cRepeat)
{
	char_u		string[1];

	/* TRACE("OnChar(%d, %c)\n", ch, ch); */

	string[0] = ch;
	if (string[0] == Ctrl('C'))
	{
		trash_input_buf();
		got_int = TRUE;
	}
	add_to_input_buf(string, 1);
}

	static void
_OnAltKey(
	HWND hwnd,
	UINT vk,
	BOOL fDown,
	int cRepeat,
	UINT flags)
{
	/* This doesn't work */
	/*
	if (s_uMsg == WM_SYSKEYDOWN)
		s_alt_key_down = TRUE;
	else if (s_uMsg == WM_SYSKEYUP)
		s_alt_key_down = FALSE;
	TRACE("Alt key %d\n", s_alt_key_down);
	*/
	/* FORWARD_WM_KEYDOWN(hwnd, vk, cRepeat, flags, DefWindowProc); */
}

	static void
_OnMouseEvent(
	int button,
	int x,
	int y,
	int repeated_click,
	UINT keyFlags)
{
	int vim_modifiers = 0x0;

	if (keyFlags & MK_SHIFT)
		vim_modifiers |= MOUSE_SHIFT;
	if (keyFlags & MK_CONTROL)
		vim_modifiers |= MOUSE_CTRL;
	/*
	 * When we find M- in a mapping, use either Alt or the Menu key, since we
	 * haven't got Alt working very well.  Hey, it seems the Menu key IS the
	 * Alt key??
	 */
	if ((GetKeyState(VK_MENU) & 0x8000) || s_alt_key_down)
		vim_modifiers |= MOUSE_ALT;

	gui_send_mouse_event(button, x, y, repeated_click, vim_modifiers);
}

	static void
_OnMouseButtonDown(
	HWND hwnd,
	BOOL fDoubleClick,
	int x,
	int y,
	UINT keyFlags)
{
	static LONG	s_prevTime = 0;

	LONG	currentTime = GetMessageTime();
	int		button = -1;
	int		repeated_click;

	/*
	 * While button is down, keep grabbing mouse move events when the mouse
	 * goes outside the window
	 */
	SetCapture(s_textArea);

	if (s_uMsg == WM_LBUTTONDOWN || s_uMsg == WM_LBUTTONDBLCLK)
		button = MOUSE_LEFT;
	else if (s_uMsg == WM_MBUTTONDOWN || s_uMsg == WM_MBUTTONDBLCLK)
		button = MOUSE_MIDDLE;
	else if (s_uMsg == WM_RBUTTONDOWN || s_uMsg == WM_RBUTTONDBLCLK)
		button = MOUSE_RIGHT;

	if (button >= 0)
	{
		repeated_click = ((int)(currentTime - s_prevTime) < p_mouset);

		/*
		 * TODO: Fix this, it doesn't work:
		 */
		/*
		 * Holding down the left and right buttons simulates pushing the middle
		 * button.  Hmm, this doesn't really work because we have already
		 * passed through the first click as a left or right button click.
		 */
		if (repeated_click &&
			((button == MOUSE_LEFT && (keyFlags & MK_RBUTTON)) ||
			 (button == MOUSE_RIGHT && (keyFlags & MK_LBUTTON))))
		{
			/*
			 * Hmm, gui.c will ignore more than one button down at a time, so
			 * pretend we let go of it first.
			 */
			gui_send_mouse_event(MOUSE_RELEASE, x, y, FALSE, 0x0);
			button = MOUSE_MIDDLE;
			repeated_click = FALSE;
		}

		s_prevTime = currentTime;
		/* TRACE("Button down at x %d, y %d\n", x, y); */
		_OnMouseEvent(button, x, y, repeated_click, keyFlags);
	}
}

	static void
_OnMouseMoveOrRelease(
	HWND hwnd,
	int x,
	int y,
	UINT keyFlags)
{
	int button;

	if (s_uMsg == WM_MOUSEMOVE)
	{
		/*
		 * It's only a MOUSE_DRAG if one or more mouse buttons are being held
		 * down.
		 */
		if (!(keyFlags & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)))
			return;

		button = MOUSE_DRAG;
		/* TRACE("       move at x %d, y %d\n", x, y); */
	}
	else
	{
		ReleaseCapture();
		button = MOUSE_RELEASE;
		/* TRACE("         up at x %d, y %d\n", x, y); */
	}

	_OnMouseEvent(button, x, y, FALSE, keyFlags);
}

	static void
_OnPaint(
	HWND hwnd)
{
	if (!IsMinimized(hwnd))
	{
		PAINTSTRUCT ps;
		HDC hdc;

		hdc = BeginPaint(hwnd, &ps);
		if (!IsRectEmpty(&ps.rcPaint))
			gui_redraw(ps.rcPaint.left, ps.rcPaint.top,
					   ps.rcPaint.right - ps.rcPaint.left + 1,
					   ps.rcPaint.bottom - ps.rcPaint.top + 1);
		EndPaint(hwnd, &ps);
	}
}

	static void
_OnSize(
	HWND hwnd,
	UINT state,
	int cx,
	int cy)
{
	if (!IsMinimized(hwnd))
	{
		gui_resize_window(cx, cy);
	
		/* Menu bar may wrap differently now */
		gui_w32_get_menu_height(TRUE);
	}
}

	static void
_OnKillFocus(
	HWND hwnd,
	HWND hwndNewFocus)
{
	clip_mch_lose_focus();
	gui.in_focus = FALSE;
	gui_update_cursor(TRUE);
	s_alt_key_down = FALSE;
}

	static void
_OnMenu(
	HWND hwnd,
	int id,
	HWND hwndCtl,
	UINT codeNotify)
{
	MENUITEMINFO	info;

	info.cbSize = sizeof(info);
	info.fMask = MIIM_DATA;
	
	/*
	 * The menu id is only 16-bit, so we store the menu pointer in the data
	 * field instead.
	 */
	GetMenuItemInfo(s_menuBar, id, FALSE, &info);
	gui_menu_cb((GuiMenu *)info.dwItemData);
}

	static void
_OnSetFocus(
	HWND hwnd,
	HWND hwndOldFocus)
{
	gui.in_focus = TRUE;
	gui_update_cursor(TRUE);

	/* reset_modifiers(); */
}

/*
 * Find the scrollbar with the given hwnd.
 */
	static GuiScrollbar *
gui_w32_find_scrollbar(hwnd)
	HWND		hwnd;
{
	WIN			*wp;

	if (gui.bottom_sbar.id == hwnd)
		return &gui.bottom_sbar;
	for (wp = firstwin; wp != NULL; wp = wp->w_next)
	{
		if (wp->w_scrollbars[SBAR_LEFT].id == hwnd)
			return &wp->w_scrollbars[SBAR_LEFT];
		if (wp->w_scrollbars[SBAR_RIGHT].id == hwnd)
			return &wp->w_scrollbars[SBAR_RIGHT];
	}
	return NULL;
}

	static void
_OnScroll(
	HWND hwnd,
	HWND hwndCtl,
	UINT code,
	int pos)
{
	GuiScrollbar *sb, *sb_info;
	int			val;
	int			dragging = FALSE;
	SCROLLINFO	si;
	int			c;

	sb = gui_w32_find_scrollbar(hwndCtl);
	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;
	val = sb_info->value;

	switch (code)
	{
		case SB_THUMBTRACK:
			/* TRACE("SB_THUMBTRACK, %d\n", pos); */
			val = pos;
			dragging = TRUE;
			break;
		case SB_LINEDOWN:
			/* TRACE("SB_LINEDOWN\n"); */
			val++;
			break;
		case SB_LINEUP:
			/* TRACE("SB_LINEUP\n"); */
			val--;
			break;
		case SB_PAGEDOWN:
			/* TRACE("SB_PAGEDOWN\n"); */
			val += (sb_info->size > 2 ? sb_info->size - 2 : 1);
			break;
		case SB_PAGEUP:
			/* TRACE("SB_PAGEUP\n"); */
			val -= (sb_info->size > 2 ? sb_info->size - 2 : 1);
			break;
		case SB_TOP:
			/* TRACE("SB_TOP\n"); */
			val = 0;
			break;
		case SB_BOTTOM:
			/* TRACE("SB_BOTTOM\n"); */
			val = sb_info->max;
			break;
		case SB_ENDSCROLL:
			/*
			 * "pos" only gives us 16-bit data.  In case of large file, use
			 * GetScrollPos() which returns 32-bit.  Unfortunately it is not
			 * valid while the scrollbar is being dragged.
			 */
			/* TRACE("SB_ENDSCROLL\n"); */
			val = GetScrollPos(hwndCtl, SB_CTL);
			break;
		default:
			/* TRACE("Unknown scrollbar event %d\n", code); */
			return;
	}

	si.cbSize = sizeof(si);
	si.fMask = SIF_POS;
	si.nPos = val;
	SetScrollInfo(hwndCtl, SB_CTL, &si, TRUE);
	
	/*
	 * Scrollbars seem to grab focus and vim doesn't read the input queue until
	 * you stop dragging the scrollbar.  We get here each time the scrollbar is
	 * dragged another pixel, but as far as the rest of vim goes, it thinks
	 * we're just hanging in the call to DispatchMessage() in
	 * process_message().  The DispatchMessage() call that hangs was passed a
	 * mouse button click event in the scrollbar window. -- webb.
	 */
	gui_drag_scrollbar(sb, val, dragging);
	if (code == SB_THUMBTRACK)
	{
		/*
		 * We need to process the messages we just stuck in the queue; if we
		 * don't, we will end up with nothing happening until we release the
		 * thumb, then the screen will scroll wildly.  Really ugly!
		 * This is a hack though, is there no way to stop the scrollbar from
		 * taking over in the first place?
		 */
		c = vgetc();
		if (c == K_SCROLLBAR)
		{
			if (State & NORMAL)
			{
				gui_do_scroll();
				setcursor();
			}
			else if (State & INSERT)
			{
				/*
				 * Get some random text inserted sometimes after scrolling in
				 * insert mode, why?  Sometimes you get text that you've
				 * already typed elsewhere! -- webb
				 */
				ins_scroll();
				setcursor();
			}
			else if (State & CMDLINE)
			{
				if (!msg_scrolled)
				{
					gui_do_scroll();
					redrawcmdline();
				}
			}
			flushbuf();
		}
		else if (c == K_HORIZ_SCROLLBAR)
		{
			if (State & NORMAL)
			{
				gui_do_horiz_scroll();
				setcursor();
			}
			else if (State & INSERT)
			{
				ins_horscroll();
				setcursor();
			}
			else if (State & CMDLINE)
			{
				if (!msg_scrolled)
				{
					gui_do_horiz_scroll();
					redrawcmdline();
				}
			}
			flushbuf();
		}
		else
		{
			/* Woops!  Shouldn't have got this */
			vungetc(c);
		}
	}
}

	static void
gui_w32_get_valid_dimensions(
	int w,
	int h,
	int *valid_w,
	int *valid_h)
{
	int		base_width, base_height;

	base_width = gui_get_base_width()
		+ GetSystemMetrics(SM_CXSIZEFRAME) * 2;
	base_height = gui_get_base_height()
		+ GetSystemMetrics(SM_CYSIZEFRAME) * 2
		+ GetSystemMetrics(SM_CYCAPTION)
		+ gui_w32_get_menu_height(FALSE);
	*valid_w = base_width +
					((w - base_width) / gui.char_width) * gui.char_width;
	*valid_h = base_height +
					((h - base_height) / gui.char_height) * gui.char_height;
}

/*
 * Even though we have _DuringSizing() which makes the rubber band a valid
 * size, we need this for when the user maximises the window.
 * TODO: Doesn't seem to adjust the width though for some reason.
 */
	static BOOL
_OnWindowPosChanging(
	HWND hwnd,
	LPWINDOWPOS lpwpos)
{
	if (!(lpwpos->flags & SWP_NOSIZE))
	{
		gui_w32_get_valid_dimensions(lpwpos->cx, lpwpos->cy,
									 &lpwpos->cx, &lpwpos->cy);
	}
	return 0;
}

	static int
_DuringSizing(
	HWND hwnd,
	UINT fwSide,
	LPRECT lprc)
{
	int		w, h;
	int		valid_w, valid_h;
	int		w_offset, h_offset;

	w = lprc->right - lprc->left;
	h = lprc->bottom - lprc->top;
	gui_w32_get_valid_dimensions(w, h, &valid_w, &valid_h);
	w_offset = w - valid_w;
	h_offset = h - valid_h;

	if (fwSide == WMSZ_LEFT || fwSide == WMSZ_TOPLEFT
							|| fwSide == WMSZ_BOTTOMLEFT)
		lprc->left += w_offset;
	else if (fwSide == WMSZ_RIGHT || fwSide == WMSZ_TOPRIGHT
							|| fwSide == WMSZ_BOTTOMRIGHT)
		lprc->right -= w_offset;

	if (fwSide == WMSZ_TOP || fwSide == WMSZ_TOPLEFT
							|| fwSide == WMSZ_TOPRIGHT)
		lprc->top += h_offset;
	else if (fwSide == WMSZ_BOTTOM || fwSide == WMSZ_BOTTOMLEFT
							|| fwSide == WMSZ_BOTTOMRIGHT)
		lprc->bottom -= h_offset;
	return TRUE;
}

	static LRESULT CALLBACK
_WndProc(
	HWND hwnd,
	UINT uMsg,
	WPARAM wParam,
	LPARAM lParam)
{
	/*
	TRACE("WndProc: hwnd = %08x, msg = %x, wParam = %x, lParam = %x\n",
		  hwnd, uMsg, wParam, lParam);
	*/

	s_uMsg = uMsg;
	s_wParam = wParam;
	s_lParam = lParam;

	switch (uMsg)
	{
		/* HANDLE_MSG(hwnd, WM_ACTIVATE,	_OnActivate); */
		HANDLE_MSG(hwnd, WM_CHAR,		_OnChar);
		/* HANDLE_MSG(hwnd, WM_CLOSE,		_OnClose); */
		/* HANDLE_MSG(hwnd, WM_COMMAND,	_OnCommand); */
		/* HANDLE_MSG(hwnd, WM_CREATE,		_OnCreate); */
		HANDLE_MSG(hwnd, WM_DESTROY,	_OnDestroy);
		HANDLE_MSG(hwnd, WM_DROPFILES,	_OnDropFiles);
		HANDLE_MSG(hwnd, WM_HSCROLL,	_OnScroll);
		HANDLE_MSG(hwnd, WM_KILLFOCUS,	_OnKillFocus);
		HANDLE_MSG(hwnd, WM_COMMAND,	_OnMenu);
		/* HANDLE_MSG(hwnd, WM_MOVE,		_OnMove); */
		/* HANDLE_MSG(hwnd, WM_NCACTIVATE,	_OnNCActivate); */
		HANDLE_MSG(hwnd, WM_SETFOCUS,	_OnSetFocus);
		HANDLE_MSG(hwnd, WM_SIZE,		_OnSize);
		/* HANDLE_MSG(hwnd, WM_SYSCOMMAND,	_OnSysCommand); */
		HANDLE_MSG(hwnd, WM_SYSKEYDOWN,	_OnAltKey);
		HANDLE_MSG(hwnd, WM_SYSKEYUP,	_OnAltKey);
		HANDLE_MSG(hwnd, WM_VSCROLL,	_OnScroll);
		HANDLE_MSG(hwnd, WM_WINDOWPOSCHANGING,	_OnWindowPosChanging);

	case WM_SIZING:		/* HANDLE_MSG doesn't seem to handle this one */
		return _DuringSizing(hwnd, wParam, (LPRECT)lParam);

	default:
		return DefWindowProc(hwnd, uMsg, wParam, lParam);
	}
}

	static LRESULT CALLBACK
_TextAreaWndProc(
	HWND hwnd,
	UINT uMsg,
	WPARAM wParam,
	LPARAM lParam)
{
	/*
	TRACE("TextAreaWndProc: hwnd = %08x, msg = %x, wParam = %x, lParam = %x\n",
		  hwnd, uMsg, wParam, lParam);
	*/

	s_uMsg = uMsg;
	s_wParam = wParam;
	s_lParam = lParam;

	switch (uMsg)
	{
		HANDLE_MSG(hwnd, WM_LBUTTONDBLCLK,_OnMouseButtonDown);
		HANDLE_MSG(hwnd, WM_LBUTTONDOWN,_OnMouseButtonDown);
		HANDLE_MSG(hwnd, WM_LBUTTONUP,	_OnMouseMoveOrRelease);
		HANDLE_MSG(hwnd, WM_MBUTTONDBLCLK,_OnMouseButtonDown);
		HANDLE_MSG(hwnd, WM_MBUTTONDOWN,_OnMouseButtonDown);
		HANDLE_MSG(hwnd, WM_MBUTTONUP,	_OnMouseMoveOrRelease);
		HANDLE_MSG(hwnd, WM_MOUSEMOVE,	_OnMouseMoveOrRelease);
		HANDLE_MSG(hwnd, WM_PAINT,		_OnPaint);
		HANDLE_MSG(hwnd, WM_RBUTTONDBLCLK,_OnMouseButtonDown);
		HANDLE_MSG(hwnd, WM_RBUTTONDOWN,_OnMouseButtonDown);
		HANDLE_MSG(hwnd, WM_RBUTTONUP,	_OnMouseMoveOrRelease);

	default:
		return DefWindowProc(hwnd, uMsg, wParam, lParam);
	}
}


/*
 * End of call-back routines
 */

/*
 * Parse the GUI related command-line arguments.  Any arguments used are
 * deleted from argv, and *argc is decremented accordingly.  This is called
 * when vim is started, whether or not the GUI has been started.
 */
	void
gui_mch_prepare(argc, argv)
	int		*argc;
	char	**argv;
{
	/* We don't have any command line arguments for the Windows GUI yet */
}

/*
 * Initialise the GUI.  Create all the windows, set up all the call-backs
 * etc.
 */
	int
gui_mch_init()
{
	const char szVimWndClass[] = VIM_CLASS;
	const char szTextAreaClass[] = "VimTextArea";
	WNDCLASS wndclass;
	
	gui.scrollbar_width = GetSystemMetrics(SM_CXVSCROLL);
	gui.scrollbar_height = GetSystemMetrics(SM_CYHSCROLL);
	gui.menu_height = 0;		/* Windows takes care of this */
	gui.border_width = 0;

	wndclass.style = CS_HREDRAW | CS_VREDRAW;
	wndclass.lpfnWndProc = _WndProc;
	wndclass.cbClsExtra = 0;
	wndclass.cbWndExtra = 0;
	wndclass.hInstance = s_hinst;
	wndclass.hIcon = LoadIcon(wndclass.hInstance, "IDR_VIM");
	wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndclass.hbrBackground = GetStockBrush(LTGRAY_BRUSH);
	wndclass.lpszMenuName = NULL;
	wndclass.lpszClassName = szVimWndClass;

	if (RegisterClass(&wndclass) == 0)
		return FAIL;

	s_hwnd = CreateWindow(
		szVimWndClass, "Vim W32 GUI",
		WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT, CW_USEDEFAULT,
		100,							/* Any value will do */
		100,							/* Any value will do */
		NULL, NULL,
		s_hinst, NULL);

	if (s_hwnd == NULL)
		return FAIL;

	/* Create the text area window */
	wndclass.style = CS_HREDRAW | CS_VREDRAW;
	wndclass.lpfnWndProc = _TextAreaWndProc;
	wndclass.cbClsExtra = 0;
	wndclass.cbWndExtra = 0;
	wndclass.hInstance = s_hinst;
	wndclass.hIcon = NULL;
	wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndclass.hbrBackground = NULL;
	wndclass.lpszMenuName = NULL;
	wndclass.lpszClassName = szTextAreaClass;

	if (RegisterClass(&wndclass) == 0)
		return FAIL;

	s_textArea = CreateWindow(
		szTextAreaClass, "Vim text area",
		WS_CHILD | WS_VISIBLE, 0, 0,
		100,							/* Any value will do for now */
		100,							/* Any value will do for now */
		s_hwnd, NULL,
		s_hinst, NULL);

	if (s_textArea == NULL)
		return FAIL;

	s_menuBar = CreateMenu();

    DragAcceptFiles(s_hwnd, TRUE);

	/* Do we need to bother with this? */
	/* m_fMouseAvail = GetSystemMetrics(SM_MOUSEPRESENT); */


	/*
	 * Load resources.
	 * Reads from vim.ini in the Windows directory.
	 * Default to 9pt Fixedsys (yuk), which everyone should have.
	 */
	GetPrivateProfileString("Font", "FaceName", "Fixedsys",
							gui.dflt_font_name, LF_FACESIZE, "vim.ini");

	/* Height should be (-4 * point size) / 3; e.g., 9pt = -12 */
	gui.dflt_font_height = GetPrivateProfileInt("Font", "Height", -12,
												"vim.ini");

	gui.back_pixel = gui_mch_get_color((char_u *)"Black");
	gui.norm_pixel = gui_mch_get_color((char_u *)"LightGray");
	gui.bold_pixel = gui_mch_get_color((char_u *)"Yellow");
	gui.ital_pixel = gui.norm_pixel;
	gui.underline_pixel = gui.norm_pixel;
	gui.cursor_pixel = gui_mch_get_color((char_u *)"LightGreen");
	
	/*
	 * Check that none of the colors are the same as the background color
	 */
	if (gui.norm_pixel == gui.back_pixel)
	{
		gui.norm_pixel = gui_mch_get_color((char_u *)"White");
		if (gui.norm_pixel == gui.back_pixel)
			gui.norm_pixel = gui_mch_get_color((char_u *)"Black");
	}
	if (gui.bold_pixel == gui.back_pixel)
		gui.bold_pixel = gui.norm_pixel;
	if (gui.ital_pixel == gui.back_pixel)
		gui.ital_pixel = gui.norm_pixel;
	if (gui.underline_pixel == gui.back_pixel)
		gui.underline_pixel = gui.norm_pixel;
	if (gui.cursor_pixel == gui.back_pixel)
		gui.cursor_pixel = gui.norm_pixel;

	/*
	 * Start out by adding the configured border width into the border offset
	 */
	gui.border_offset = gui.border_width;

	return OK;
}

/*
 * Open the GUI window which was created by a call to gui_mch_init().
 */
	int
gui_mch_open()
{
	/* Actually open the window */
	ShowWindow(s_hwnd, SW_SHOWNORMAL);

	return OK;
}

	void
gui_mch_exit()
{
	/* Can't think of anything to do here */
}

/*
 * Set the size of the window to the given width and height in pixels.
 */
	void
gui_mch_set_winsize(width, height, min_width, min_height,
					base_width, base_height)
	int		width;
	int		height;
	int		min_width;
	int		min_height;
	int		base_width;
	int		base_height;
{
	SetWindowPos(s_hwnd, NULL, 0, 0,
				 width + GetSystemMetrics(SM_CXSIZEFRAME) * 2,
				 height + GetSystemMetrics(SM_CYSIZEFRAME) * 2
						+ GetSystemMetrics(SM_CYCAPTION)
						+ gui_w32_get_menu_height(FALSE),
				 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
	
	/* Menu may wrap differently now */
	gui_w32_get_menu_height(TRUE);
}

	void
gui_mch_get_screen_dimensions(screen_w, screen_h)
	int		*screen_w;
	int		*screen_h;
{
	*screen_w = GetSystemMetrics(SM_CXSCREEN)
			  - GetSystemMetrics(SM_CXSIZEFRAME) * 2;
	*screen_h = GetSystemMetrics(SM_CYSCREEN)
			  - GetSystemMetrics(SM_CYSIZEFRAME) * 2
			  - GetSystemMetrics(SM_CYCAPTION)
			  - gui_w32_get_menu_height();
}

	void
gui_mch_set_text_area_pos(x, y, w, h)
	int		x;
	int		y;
	int		w;
	int		h;
{
	SetWindowPos(s_textArea, NULL, x, y, w, h, SWP_NOZORDER | SWP_NOACTIVATE);
}


/*
 * Scrollbar stuff:
 */

	void
gui_mch_enable_scrollbar(sb, flag)
	GuiScrollbar	*sb;
	int				flag;
{
	ShowScrollBar(sb->id, SB_CTL, flag);
}

	void
gui_mch_set_scrollbar_thumb(sb, val, size, max)
	GuiScrollbar	*sb;
	int				val;
	int				size;
	int				max;
{
	SCROLLINFO	info;

	info.cbSize = sizeof(info);
	info.fMask = SIF_POS | SIF_RANGE | SIF_PAGE;
	info.nPos = val;
	info.nMin = 0;
	info.nMax = max;
	info.nPage = size;
	SetScrollInfo(sb->id, SB_CTL, &info, TRUE);
}

	void
gui_mch_set_scrollbar_pos(sb, x, y, w, h)
	GuiScrollbar	*sb;
	int				x;
	int				y;
	int				w;
	int				h;
{
	SetWindowPos(sb->id, NULL, x, y, w, h, SWP_NOZORDER | SWP_NOACTIVATE);
}

	void
gui_mch_create_scrollbar(sb, orient)
	GuiScrollbar	*sb;
	int				orient;		/* SBAR_VERT or SBAR_HORIZ */
{
	sb->id = CreateWindow(
		"SCROLLBAR", "Scrollbar",
		WS_CHILD | ((orient == SBAR_VERT) ? SBS_VERT : SBS_HORZ), 0, 0,
		10,								/* Any value will do for now */
		10,								/* Any value will do for now */
		s_hwnd, NULL,
		s_hinst, NULL);
}

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

	static void
GetFontSize(GuiFont font)
{
	static const char ach[] = {'W', 'f', 'g', 'M'};

	HWND	hwnd = GetDesktopWindow();
	HDC		hdc = GetWindowDC(hwnd);
	HFONT	hfntOld = SelectFont(hdc, (HFONT)font);
	SIZE	siz;
	
	GetTextExtentPoint(hdc, ach, sizeof(ach), &siz);

	gui.char_width = siz.cx / sizeof(ach);
	gui.char_height = siz.cy;

	/* TRACE("GetFontSize: h %d, w %d\n", gui.char_height, gui.char_width); */

	SelectFont(hdc, hfntOld);

	ReleaseDC(hwnd, hdc);
}

	static GuiFont
get_font_handle(lf)
	LOGFONT	*lf;
{
	HFONT	*font = NULL;

	/* Load the font */
	font = CreateFontIndirect(lf);

	if (font == NULL)
		return (GuiFont)0;

	return (GuiFont)font;
}

	static void
get_logfont(lf, name)
	LOGFONT	*lf;
	char_u	*name;
{
	char_u	*p;

	if (name == NULL)
	{
		*lf = s_lfDefault;
		return;
	}
	*lf = s_lfNormal;
	if (name != NULL && *name != NUL)
	{
		/*
		 * Split name up, it could be <name>_h<height>
		 */
		for (p = name; *p && *p != '_'; p++)
			;
		if (p - name + 1 > LF_FACESIZE)
			return;						/* Name too long */
		if (p != name)
			strncpy(lf->lfFaceName, name, p - name);
		while (*p == '_')
			p++;
		while (*p)
		{
			switch (*p++)
			{
				case 'h':
					lf->lfHeight = atoi(p);
					if (*p == '-')
						p++;
					p = skipdigits(p);
					break;
				case 'w':
					lf->lfWidth = atoi(p);
					if (*p == '-')
						p++;
					p = skipdigits(p);
					break;
				case 'b':
					lf->lfWeight = FW_BOLD;
					break;
				case 'i':
					lf->lfItalic = TRUE;
					break;
				case 'u':
					lf->lfUnderline = TRUE;
					break;
				case 's':
					lf->lfStrikeOut = TRUE;
					break;
				default:
					sprintf((char *)IObuff,
							"Illegal char '%c' in font name \"%s\"",
							p[-1], name);
					EMSG(IObuff);
					break;
			}
			while (*p == '_')
				p++;
		}
	}
}

/*
 * Initialise vim to use the font with the given name.	Return FAIL if the font
 * could not be loaded, OK otherwise.
 */
	int
gui_mch_init_font(font_name)
	char_u		*font_name;
{
	LOGFONT	lf;
	GuiFont	font = (GuiFont)0;
	char_u	str[80];

	/* Load the font */
	if (font_name == NULL)
	{
		/*
		 * If none of the fonts in 'font' could be loaded, try the one set in
		 * the vim.ini resource file, and finally just try using s_lfDefault,
		 * which will hopefully always be there.
		 */
		sprintf(str, "%s_h%d", gui.dflt_font_name, gui.dflt_font_height);
		get_logfont(&lf, str);
		font = get_font_handle(&lf);
		if (font == (GuiFont)0)
		{
			get_logfont(&lf, NULL);
			font = get_font_handle(&lf);
		}
	}
	else
	{
		get_logfont(&lf, font_name);
		font = get_font_handle(&lf);
	}
	if (font == (GuiFont)0)
		return FAIL;
	if (gui.norm_font != (GuiFont)0)
		gui_mch_free_font(gui.norm_font);
	gui.norm_font = font;
	s_lfNormal = lf;
	GetFontSize(font);

	/*
	 * Move characters up one pixel so that italic and bold fonts don't draw
	 * off the bottom of their character space.  Also means that we can
	 * underline an underscore for normal text.
	 */
	gui.char_ascent = -1;

	if (!lf.lfItalic)
	{
		lf.lfItalic = TRUE;
		gui.ital_font = get_font_handle(&lf);
		lf.lfItalic = FALSE;
	}
	if (lf.lfWeight < FW_BOLD)
	{
		lf.lfWeight = FW_BOLD;
		gui.bold_font = get_font_handle(&lf);
		if (!lf.lfItalic)
		{
			lf.lfItalic = TRUE;
			gui.boldital_font = get_font_handle(&lf);
		}
	}

	return OK;
}

	GuiFont
gui_mch_get_font(name, giveErrorIfMissing)
	char_u		*name;
	int			giveErrorIfMissing;
{
	LOGFONT		lf;
	GuiFont		font;

	get_logfont(&lf, name);
	font = get_font_handle(&lf);
	if (font == (GuiFont)0 && giveErrorIfMissing)
		EMSG2("Unknown font: %s", name);
	return font;
}

/*
 * Set the current text font.
 */
	void
gui_mch_set_font(font)
	GuiFont	font;
{
	gui.currFont = font;
}

/*
 * Return TRUE if the two fonts given are equivalent.
 */
	int
gui_mch_same_font(f1, f2)
	GuiFont	f1;
	GuiFont	f2;
{
	return f1 == f2;
}

	void
gui_mch_free_font(font)
	GuiFont	font;
{
	DeleteObject((HFONT)font);
}

	static int
hex_digit(c)
	int		c;
{
	if (isdigit(c))
		return c - '0';
	c = TO_LOWER(c);
	if (c >= 'a' && c <= 'f')
		return c - 'a' + 10;
	return -1000;
}

	GuiColor
gui_mch_get_color(name)
	char_u	*name;
{
	typedef struct GuiColorTable
	{
		char		*name;
		COLORREF	color;
	} GuiColorTable;

	static GuiColorTable table[] =
	{
		{"black",			RGB(0x00, 0x00, 0x00)},
		{"blue",			RGB(0x00, 0x00, 0x80)},
		{"green",			RGB(0x00, 0x80, 0x00)},
		{"cyan",			RGB(0x00, 0x80, 0x80)},
		{"red",				RGB(0x80, 0x00, 0x00)},
		{"magenta",			RGB(0x80, 0x00, 0x80)},
		{"brown",			RGB(0x80, 0x40, 0x40)},
		{"lightgray",		RGB(0xC0, 0xC0, 0xC0)},
		{"darkgray",		RGB(0x80, 0x80, 0x80)},
		{"lightblue",		RGB(0x00, 0x00, 0xFF)},
		{"lightgreen",		RGB(0x00, 0xFF, 0x00)},
		{"lightcyan",		RGB(0x00, 0xFF, 0xFF)},
		{"lightred",		RGB(0xFF, 0x00, 0x00)},
		{"lightmagenta",	RGB(0xFF, 0x00, 0xFF)},
		{"yellow",			RGB(0xFF, 0xFF, 0x00)},
		{"white",			RGB(0xFF, 0xFF, 0xFF)},
	};

	int				r, g, b;
	int				i;

	if (!gui.in_use)				/* can't do this when GUI not running */
		return (GuiColor)0;

	if (name[0] == '#' && strlen(name) == 7)
	{
		/* Name is in "#rrggbb" format */
		r = hex_digit(name[1]) * 16 + hex_digit(name[2]);
		g = hex_digit(name[3]) * 16 + hex_digit(name[4]);
		b = hex_digit(name[5]) * 16 + hex_digit(name[6]);
		if (r < 0 || g < 0 || b < 0)
			return (GuiColor)0;
		return RGB(r, g, b);
	}
	else
	{
		/* Check if the name is one of the colors we know */
		for (i = 0; i < sizeof(table) / sizeof(table[0]); i++)
			if (STRICMP(name, table[i].name) == 0)
				return table[i].color;
	}

	return (GuiColor)0;
}

/*
 * Set the current text foreground color.
 */
	void
gui_mch_set_fg_color(color)
	GuiColor	color;
{
	gui.currFgColor = color;
}

/*
 * Set the current text background color.
 */
	void
gui_mch_set_bg_color(color)
	GuiColor	color;
{
	gui.currBgColor = color;
}

	void
gui_mch_draw_string(row, col, s, len, fake_bold, underline)
	int		row;
	int		col;
	char_u	*s;
	int		len;
	int		fake_bold;
	int		underline;
{
	static int *padding = NULL;
	static int pad_size = 0;

	HDC		hdc = GetWindowDC(s_textArea);
	HPEN	hpen, old_pen;
	HFONT	old_font;
	int		y;
	int		i;

#if 1
	/*
	 * Italic and bold text seems to have an extra row of pixels at the bottom
	 * (below where the bottom of the character should be).  If we draw the
	 * characters with a solid background, the top row of pixels in the
	 * character below will be overwritten.  We can fix this by filling in the
	 * background ourselves, to the correct character proportions, and then
	 * writing the character in transparent mode.  Still have a problem when
	 * the character is "_", which gets written on to the character below.
	 * New fix: set gui.char_ascent to -1.  This shifts all characters up one
	 * pixel in their slots, which fixes the problem with the bottom row of
	 * pixels.  We still need this code because otherwise the top row of pixels
	 * becomes a problem. - webb.
	 */
	HBRUSH	hbr;
	RECT	rc;

	/*
	 * Clear background first.
	 * Note: FillRect() excludes right and bottom of rectangle.
	 */
	rc.left = FILL_X(col);
	rc.top = FILL_Y(row);
	rc.right = FILL_X(col + len);		/* Add +1 to erase fake bold? */
	rc.bottom = FILL_Y(row + 1);
	hbr = CreateSolidBrush(gui.currBgColor);
	FillRect(hdc, &rc, hbr);
	DeleteBrush(hbr);

	SetBkMode(hdc, TRANSPARENT);
#else
	/*
	 * The alternative would be to write the characters in opaque mode, but
	 * when the text is not exactly the same proportions as normal text, too
	 * big or too little a rectangle gets drawn for the background.
	 */
	SetBkMode(hdc, OPAQUE);
	SetBkColor(hdc, gui.currBgColor);
#endif
	SetTextColor(hdc, gui.currFgColor);
	old_font = SelectFont(hdc, gui.currFont);

	if (pad_size != Columns || padding == NULL || padding[0] != gui.char_width)
	{
		vim_free(padding);
		pad_size = Columns;
		padding = (int *)alloc(pad_size);
		if (padding != NULL)
			for (i = 0; i < pad_size; i++)
				padding[i] = gui.char_width;
	}

	/*
	 * What am I doing wrong here?  If I use "padding" as the last argument to
	 * the two calls to ExtTextOut() below instead of "NULL", all the spacing
	 * goes strange, and some text doesn't get displayed even after ^L, but it
	 * does when you move the cursor over it. -- webb.
	 */
	ExtTextOut(hdc, TEXT_X(col), TEXT_Y(row), 0, NULL, (char *)s, len, NULL);

	if (fake_bold)
		ExtTextOut(hdc, TEXT_X(col), TEXT_Y(row), 0, NULL, (char *)s, len,
				   NULL);

	if (underline)
	{
		/*
		 * Note: LineTo() excludes the last pixel in the line.
		 */
		hpen = CreatePen(PS_SOLID, 1, gui.currFgColor);
		old_pen = SelectObject(hdc, hpen);
		y = FILL_Y(row + 1) - 1;
		MoveToEx(hdc, FILL_X(col), y, NULL);
		LineTo(hdc, FILL_X(col + len), y);
		DeleteObject(hpen);
		SelectObject(hdc, old_pen);
	}
	SelectFont(hdc, old_font);
	ReleaseDC(s_textArea, hdc);
}

/*
 * Return OK if the key with the termcap name "name" is supported.
 */
	int
gui_mch_haskey(name)
	char_u	*name;
{
	int i;

	for (i = 0; special_keys[i].vim_code1 != NUL; i++)
		if (name[0] == special_keys[i].vim_code0 &&
										 name[1] == special_keys[i].vim_code1)
			return OK;
	return FAIL;
}

	void
gui_mch_beep()
{
	/*
	 * TODO: Seems to wait or not beep at all.  Can we use Beep()?  What args?
	 */
	MessageBeep(0xFFFFFFFF);
}

	void
gui_mch_flash()
{
	HDC   	hdc = GetWindowDC(s_textArea);
	RECT	rc;

	/*
	 * Note: InvertRect() excludes right and bottom of rectangle.
	 */
	rc.left = 0;
	rc.top = 0;
	rc.right = gui.num_cols * gui.char_width;
	rc.bottom = gui.num_rows * gui.char_height;
	InvertRect(hdc, &rc);

	ui_delay(20L, TRUE);		/* wait 1/50 of a second */

	InvertRect(hdc, &rc);
	ReleaseDC(s_textArea, hdc);
}

/*
 * Invert a rectangle from row r, column c, for nr rows and nc columns.
 */
	void
gui_mch_invert_rectangle(r, c, nr, nc)
	int		r;
	int		c;
	int		nr;
	int		nc;
{
	HDC   	hdc = GetWindowDC(s_textArea);
	RECT	rc;

	/*
	 * Note: InvertRect() excludes right and bottom of rectangle.
	 */
	rc.left = FILL_X(c);
	rc.top = FILL_Y(r);
	rc.right = rc.left + nc * gui.char_width;
	rc.bottom = rc.top + nr * gui.char_height;
	InvertRect(hdc, &rc);
	ReleaseDC(s_textArea, hdc);
}

/*
 * Iconify the GUI window.
 */
	void
gui_mch_iconify()
{
	/* TODO */
}

/*
 * Set the window title
 */
	void
gui_mch_settitle(title, icon)
	char_u	*title;
	char_u	*icon;
{
	SetWindowText(s_hwnd, (LPCSTR)title);
}

/*
 * Draw a cursor without focus.
 */
	void
gui_mch_draw_hollow_cursor()
{
	HDC   	hdc = GetWindowDC(s_textArea);
	HBRUSH	hbr;
	RECT	rc;

	/*
	 * Note: FrameRect() excludes right and bottom of rectangle.
	 */
	rc.left = FILL_X(gui.col);
	rc.top = FILL_Y(gui.row);
	rc.right = rc.left + gui.char_width;
	rc.bottom = rc.top + gui.char_height;
	hbr = CreateSolidBrush(gui.cursor_pixel);
	FrameRect(hdc, &rc, hbr);
	DeleteBrush(hbr);
	ReleaseDC(s_textArea, hdc);
}

/*
 * Draw part of a cursor, only w pixels wide, and h pixels high.
 */
	void
gui_mch_draw_part_cursor(w, h)
	int		w;
	int		h;
{
	HDC   	hdc = GetWindowDC(s_textArea);
	HBRUSH	hbr;
	RECT	rc;

	/*
	 * Note: FillRect() excludes right and bottom of rectangle.
	 */
	rc.left = FILL_X(gui.col);
	rc.top = FILL_Y(gui.row) + gui.char_height - h;
	rc.right = rc.left + w;
	rc.bottom = rc.top + h;
	hbr = CreateSolidBrush(gui.cursor_pixel);
	FillRect(hdc, &rc, hbr);
	DeleteBrush(hbr);
	ReleaseDC(s_textArea, hdc);
}

/*
 * Process a single Windows message.  If one is not available we hang until one
 * is.
 */
	static void
process_message()
{
	MSG		msg;
	UINT	vk;			/* Virtual key */
	char_u	string[3];
	int		num;
	int		i;

	GetMessage(&msg, NULL, 0, 0);

	/*
	 * Check if it's a special key that we recognise.  If not, call
	 * TranslateMessage().
	 */
	if (msg.message == WM_KEYDOWN)
	{
		vk = (int) msg.wParam;
		for (i = 0; special_keys[i].key_sym != 0; i++)
		{
			if (special_keys[i].key_sym == vk)
			{
				string[0] = CSI;
				string[1] = KS_MODIFIER;
				string[2] = 0;
				if (GetKeyState(VK_SHIFT) & 0x8000)
					string[2] ^= MOD_MASK_SHIFT;
				/*
				 * Don't use caps-lock as shift, because these are special keys
				 * being considered here, and we only want letters to get
				 * shifted -- webb
				 */
				/*
				if (GetKeyState(VK_CAPITAL) & 0x0001)
					string[2] ^= MOD_MASK_SHIFT;
				*/
				if (GetKeyState(VK_CONTROL) & 0x8000)
					string[2] |= MOD_MASK_CTRL;
				/*
				 * When we find M- in a mapping, use either Alt or the Menu
				 * key, since we haven't got Alt working very well.  Hey, it
				 * seems the Menu key IS the Alt key??
				 */
				if ((GetKeyState(VK_MENU) & 0x8000) || s_alt_key_down)
					string[2] |= MOD_MASK_ALT;
				if (string[2] != 0)
					add_to_input_buf(string, 3);

				num = 0;
				if (special_keys[i].vim_code1 == NUL)
					string[num++] = special_keys[i].vim_code0;
				else
				{
					string[num++] = (char_u)CSI;
					string[num++] = special_keys[i].vim_code0;
					string[num++] = special_keys[i].vim_code1;
				}
				add_to_input_buf(string, num);
				break;
			}
		}
		if (special_keys[i].key_sym == 0)
		{
			/* Some keys need C-S- where they should only need C- */
			if (GetKeyState(VK_CONTROL) & 0x8000)
			{
				if (vk == '6')
				{
					string[0] = Ctrl('^');
					add_to_input_buf(string, 1);
				}
				else if (vk == '-')		/* Why doesn't this work? */
				{
					string[0] = Ctrl('_');
					add_to_input_buf(string, 1);
				}
				else
					TranslateMessage(&msg);
			}
			else
				TranslateMessage(&msg);
		}
	}

	DispatchMessage(&msg);
}

/*
 * Catch up with any queued events.  This may put keyboard input into the
 * input buffer, call resize call-backs, trigger timers etc.  If there is
 * nothing in the event queue (& no timers pending), then we return
 * immediately.
 */
	void
gui_mch_update()
{
	MSG		msg;

	while (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE) && !is_input_buf_full())
		process_message();
}

/*
 * GUI input routine called by gui_wait_for_chars().  Waits for a character
 * from the keyboard.
 *	wtime == -1		Wait forever.
 *	wtime == 0		This should never happen.
 *	wtime > 0		Wait wtime milliseconds for a character.
 * Returns OK if a character was found to be available within the given time,
 * or FAIL otherwise.
 */
	int
gui_mch_wait_for_chars(wtime)
	int		wtime;
{
	MSG			msg;

	s_timed_out = FALSE;

	if (wtime > 0)
		s_wait_timer = SetTimer(NULL, 0, (UINT)wtime, (TIMERPROC)_OnTimer);

	while (!s_timed_out)
	{
		/*
		 * Don't use gui_mch_update() because then we will spin-lock until a
		 * char arrives, instead we use GetMessage() to hang until an
		 * event arrives.  No need to check for input_buf_full because we are
		 * returning as soon as it contains a single char -- webb
		 */
		process_message();

		if (!is_input_buf_empty())
		{
			if (s_wait_timer != 0 && !s_timed_out)
			{
				KillTimer(NULL, s_wait_timer);

				/* Eat spurious WM_TIMER messages */
				while(PeekMessage(&msg, s_hwnd, WM_TIMER, WM_TIMER, PM_REMOVE));
				s_wait_timer = 0;
			}
			return OK;
		}
	}
	return FAIL;
}

/*
 * Output routines.
 */

/* Flush any output to the screen */
	void
gui_mch_flush()
{
	/* Is anything needed here? */
}

/*
 * Clear a rectangular region of the screen from text pos (row1, col1) to
 * (row2, col2) inclusive.
 */
	void
gui_mch_clear_block(row1, col1, row2, col2)
	int		row1;
	int		col1;
	int		row2;
	int		col2;
{
	HDC   	hdc = GetWindowDC(s_textArea);
	HBRUSH	hbr;
	RECT	rc;

	/*
	 * Clear one extra pixel at the right, for when bold characters have
	 * spilled over to the next column.  Note: FillRect() excludes right and
	 * bottom of rectangle.
	 * Can this ever erase part of the next character? - webb
	 */
	rc.left = FILL_X(col1);
	rc.top = FILL_Y(row1);
	rc.right = FILL_X(col2 + 1) + 1;
	rc.bottom = FILL_Y(row2 + 1);
	hbr = CreateSolidBrush(gui.back_pixel);
	FillRect(hdc, &rc, hbr);
	DeleteBrush(hbr);
	ReleaseDC(s_textArea, hdc);
}

/*
 * Delete the given number of lines from the given row, scrolling up any
 * text further down within the scroll region.
 */
	void
gui_mch_delete_lines(row, num_lines)
	int		row;
	int		num_lines;
{
	if (num_lines <= 0)
		return;

	if (row + num_lines > gui.scroll_region_bot)
	{
		/* Scrolled out of region, just blank the lines out */
		gui_clear_block(row, 0, gui.scroll_region_bot, Columns - 1);
	}
	else
	{
#if 0
		/* Doesn't work when part of the window is off the screen */
		RECT	rc;

		rc.left = FILL_X(0);
		rc.right = FILL_X(Columns);
		rc.top = FILL_Y(row);
		rc.bottom = FILL_Y(gui.scroll_region_bot + 1);
		ScrollWindowEx(s_textArea, 0, -num_lines * gui.char_height,
					   &rc, &rc, NULL, NULL, 0x0);
#else
		/* HACK: */
		gui_redraw_block(gui.scroll_region_top, 0,
						 gui.scroll_region_bot, Columns - 1);
#endif

		/* Update gui.cursor_row if the cursor scrolled or copied over */
		if (gui.cursor_row >= row)
		{
			if (gui.cursor_row < row + num_lines)
				INVALIDATE_CURSOR();
			else if (gui.cursor_row <= gui.scroll_region_bot)
				gui.cursor_row -= num_lines;
		}

		gui_clear_block(gui.scroll_region_bot - num_lines + 1, 0,
			gui.scroll_region_bot, Columns - 1);
	}
}

/*
 * Insert the given number of lines before the given row, scrolling down any
 * following text within the scroll region.
 */
	void
gui_mch_insert_lines(row, num_lines)
	int		row;
	int		num_lines;
{
	if (num_lines <= 0)
		return;

	if (row + num_lines > gui.scroll_region_bot)
	{
		/* Scrolled out of region, just blank the lines out */
		gui_clear_block(row, 0, gui.scroll_region_bot, Columns - 1);
	}
	else
	{
#if 0
		/* Doesn't work when part of the window is off the screen */
		RECT	rc;

		rc.left = FILL_X(0);
		rc.right = FILL_X(Columns);
		rc.top = FILL_Y(row);
		rc.bottom = FILL_Y(gui.scroll_region_bot + 1);
		ScrollWindowEx(s_textArea, 0, num_lines * gui.char_height,
					   &rc, &rc, NULL, NULL, 0x0);
#else
		/* HACK: */
		gui_redraw_block(gui.scroll_region_top, 0,
						 gui.scroll_region_bot, Columns - 1);
#endif

		/* Update gui.cursor_row if the cursor scrolled or copied over */
		if (gui.cursor_row >= gui.row)
		{
			if (gui.cursor_row <= gui.scroll_region_bot - num_lines)
				gui.cursor_row += num_lines;
			else if (gui.cursor_row <= gui.scroll_region_bot)
				INVALIDATE_CURSOR();
		}

		gui_clear_block(row, 0, row + num_lines - 1, Columns - 1);
	}
}


/*
 * Menu stuff.
 */

	void
gui_mch_enable_menu(flag)
	int		flag;
{
	SetMenu(s_hwnd, flag ? s_menuBar : NULL);
}

	void
gui_mch_set_menu_pos(x, y, w, h)
	int		x;
	int		y;
	int		w;
	int		h;
{
	/* It will be in the right place anyway */
}

/*
 * Add a sub menu to the menu bar.
 */
	void
gui_mch_add_menu(menu, parent)
	GuiMenu	*menu;
	GuiMenu	*parent;
{
	MENUITEMINFO	info;

	menu->submenu_id = CreatePopupMenu();

	info.cbSize = sizeof(info);
	info.fMask = MIIM_DATA | MIIM_TYPE | MIIM_ID | MIIM_SUBMENU;
	info.dwItemData = (DWORD)menu;
	info.wID = menu->id = s_menu_id++;
	info.fType = MFT_STRING;
	info.dwTypeData = (LPTSTR)menu->name;
	info.cch = STRLEN(menu->name);
	info.hSubMenu = menu->submenu_id;
	InsertMenuItem((parent == NULL) ? s_menuBar : parent->submenu_id,
				   (UINT)99999, TRUE, &info);

	/*
	 * The "Help" menu is a special case, and should be placed at the far right
	 * hand side of the menu-bar.
	 */
	if (parent == NULL && STRCMP((char *)menu->name, "Help") == 0)
	{
		/* TODO: Make this the help menu */
	}

	/* Fix window size if menu wrapped */
	if (parent == NULL)
		gui_w32_get_menu_height(TRUE);

	DrawMenuBar(s_hwnd);
}

/*
 * Add a menu item to a menu
 */
	void
gui_mch_add_menu_item(menu, parent)
	GuiMenu	*menu;
	GuiMenu	*parent;
{
	MENUITEMINFO	info;

	info.cbSize = sizeof(info);
	info.fMask = MIIM_DATA | MIIM_TYPE | MIIM_ID;
	info.dwItemData = (DWORD)menu;
	info.wID = menu->id = s_menu_id++;
	info.fType = MFT_STRING;
	info.dwTypeData = (LPTSTR)menu->name;
	info.cch = STRLEN(menu->name);
	InsertMenuItem(parent->submenu_id, (UINT)99999, TRUE, &info);

	menu->submenu_id = NULL;

	DrawMenuBar(s_hwnd);
}

/*
 * Destroy the machine specific menu widget.
 */
	void
gui_mch_destroy_menu(menu)
	GuiMenu	*menu;
{
	RemoveMenu(s_menuBar, menu->id, MF_BYCOMMAND);
	if (menu->submenu_id != NULL)
		DestroyMenu(menu->submenu_id);
	DrawMenuBar(s_hwnd);
}

/*
 * Make a menu either grey or not grey.
 */
	void
gui_mch_menu_grey(menu, grey)
	GuiMenu	*menu;
	int		grey;
{
	if (grey)
		EnableMenuItem(s_menuBar, menu->id, MF_BYCOMMAND | MF_GRAYED);
	else
		EnableMenuItem(s_menuBar, menu->id, MF_BYCOMMAND | MF_ENABLED);
}

/*
 * Make menu item hidden or not hidden
 */
	void
gui_mch_menu_hidden(menu, hidden)
	GuiMenu	*menu;
	int		hidden;
{
	/*
	 * This doesn't do what we want.  Hmm, just grey the menu items for now.
	 */
	/*
	if (hidden)
		EnableMenuItem(s_menuBar, menu->id, MF_BYCOMMAND | MF_DISABLED);
	else
		EnableMenuItem(s_menuBar, menu->id, MF_BYCOMMAND | MF_ENABLED);
	*/
	gui_mch_menu_grey(menu, hidden);
}

/*
 * This is called after setting all the menus to grey/hidden or not.
 */
	void
gui_mch_draw_menubar()
{
	DrawMenuBar(s_hwnd);
}

/* cproto doesn't create a prototype for main() */
int main __PARMS((int argc, char **argv));

#ifndef PROTO
int WINAPI
WinMain(
	HINSTANCE hInstance,
	HINSTANCE hPrevInst,
	LPSTR lpszCmdLine,
	int nCmdShow)
{
#if 0
	TRACE("vim WinMain, hInst = %x, hPrev = %x, cmd = `%s', show = %d\n",
		  hInstance, hPrevInst, lpszCmdLine, nCmdShow);
#endif
	int argc;
	char **argv;

#ifdef __BORLANDC__ 
	int i;
	char* pch;
	char* pszNewCmdLine;
	char prog[256];
	char *p;
	int fIsQuote;

	/*
	 * Ron: added full path name so that the $VIM variable will get set to our
	 * startup path (so the .vimrc file can be found w/o a VIM env. var.)
	 */
	GetModuleFileName(NULL, prog, 255);
	p = strrchr(prog, '.');
	if (p != NULL)
		*p = '\0';

	/*
	 * Add the size of the string, the separating space, and a terminating '\0'
	 */   
	pszNewCmdLine = (char *) malloc(STRLEN(lpszCmdLine) + STRLEN(prog) + 2);
	if (pszNewCmdLine == NULL)
		return 0;
	
	STRCPY(pszNewCmdLine, prog);
	STRCAT(pszNewCmdLine, " ");
	STRCAT(pszNewCmdLine, lpszCmdLine);
	
	while ((*pszNewCmdLine != '\0') && (*pszNewCmdLine == ' '))
		pszNewCmdLine++;		   /* start on 1st non-space */
	
	pch = pszNewCmdLine;
	argc = 0;

	while ( *pch != '\0' )
	{
		/* Ron: Handle quoted strings in args list */
		fIsQuote = (*pch == '\"');
		if (fIsQuote)
			++pch;

		argc++;						/* this is an argument */
		if (fIsQuote) 
		{
			while ((*pch != '\0') && (*pch != '\"'))
				pch++;				/* advance until a closing quote */
			if (*pch)
				pch++;
		}
		else
		{
			while ((*pch != '\0') && (*pch != ' '))
				pch++;				/* advance until a space */
		}
		while (*pch && *pch == ' ' )
			pch++;					/* advance until a non-space */
	}
	
	argv = (char**) malloc((argc+1) * sizeof(char*));
	if (argv == NULL )
		return 0;			       /* malloc error */
	
	i = 0;
	pch = pszNewCmdLine;

	while ((i < argc) && (*pch != '\0'))
	{
		fIsQuote = (*pch == '\"');
		if (fIsQuote)
			++pch;
		
		argv[i++] = pch;
		if (fIsQuote)
		{
			while (*pch != '\0' && *pch != '\"')
				pch++;				/* advance until the closing quote */
		}
		else
		{
			while (*pch != '\0' && *pch != ' ')
				pch++;				/* advance until a space */
		}
		if (*pch != '\0')
			*(pch++) = '\0';		/* parse argument here */
		while (*pch && *pch == ' ')
			pch++;					/* advance until a non-space */
	}

	ASSERT(i == argc);
	
	argv[argc] = (char *) NULL;    /* NULL-terminated list */
#else
	
	/*
	 * Are __argc and __argv portable?  Because the above doesn't handle
	 * something like `gvim "line func.c"'.
	 */
	 argc = __argc;
	 argv = __argv;
#endif
	
	s_hinst = hInstance;

	main (argc, argv);
#ifdef __BORLANDC__
	free(argv);
	free(pszNewCmdLine);
#endif

	return 0;
}
#endif

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