view src/gui_w32.c @ 10098:72e4b7f90465 v7.4.2320

commit https://github.com/vim/vim/commit/dda933d06c06c2792bd686d059f6ad19191ad30b Author: Bram Moolenaar <Bram@vim.org> Date: Sat Sep 3 21:04:58 2016 +0200 patch 7.4.2320 Problem: Redraw problem when using 'incsearch'. Solution: Save the current view when deleting characters. (Christian Brabandt) Fix that the '" mark is set in the wrong position. Don't change the search start when using BS.
author Christian Brabandt <cb@256bit.org>
date Sat, 03 Sep 2016 21:15:06 +0200
parents 4aead6a9b7a9
children c036c0f636d5
line wrap: on
line source

/* vi:set ts=8 sts=4 sw=4 noet:
 *
 * 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.
 * See README.txt for an overview of the Vim source code.
 */
/*
 * Windows GUI.
 *
 * GUI support for Microsoft Windows, aka Win32.  Also for Win64.
 *
 * George V. Reilly <george@reilly.org> 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
 * winclip.c.	(It can also be done from the terminal version).
 *
 * TODO: Some of the function signatures ought to be updated for Win64;
 * e.g., replace LONG with LONG_PTR, etc.
 */

#include "vim.h"

#if defined(FEAT_DIRECTX)
# include "gui_dwrite.h"
#endif

#if defined(FEAT_DIRECTX)
static DWriteContext *s_dwc = NULL;
static int s_directx_enabled = 0;
static int s_directx_load_attempted = 0;
# define IS_ENABLE_DIRECTX() (s_directx_enabled && s_dwc != NULL)
#endif

#ifdef FEAT_MENU
static int gui_mswin_get_menu_height(int fix_window);
#endif

#if defined(FEAT_DIRECTX) || defined(PROTO)
    int
directx_enabled(void)
{
    if (s_dwc != NULL)
	return 1;
    else if (s_directx_load_attempted)
	return 0;
    /* load DirectX */
    DWrite_Init();
    s_directx_load_attempted = 1;
    s_dwc = DWriteContext_Open();
    return s_dwc != NULL ? 1 : 0;
}
#endif

#if defined(FEAT_RENDER_OPTIONS) || defined(PROTO)
    int
gui_mch_set_rendering_options(char_u *s)
{
#ifdef FEAT_DIRECTX
    char_u  *p, *q;

    int	    dx_enable = 0;
    int	    dx_flags = 0;
    float   dx_gamma = 0.0f;
    float   dx_contrast = 0.0f;
    float   dx_level = 0.0f;
    int	    dx_geom = 0;
    int	    dx_renmode = 0;
    int	    dx_taamode = 0;

    /* parse string as rendering options. */
    for (p = s; p != NULL && *p != NUL; )
    {
	char_u  item[256];
	char_u  name[128];
	char_u  value[128];

	copy_option_part(&p, item, sizeof(item), ",");
	if (p == NULL)
	    break;
	q = &item[0];
	copy_option_part(&q, name, sizeof(name), ":");
	if (q == NULL)
	    return FAIL;
	copy_option_part(&q, value, sizeof(value), ":");

	if (STRCMP(name, "type") == 0)
	{
	    if (STRCMP(value, "directx") == 0)
		dx_enable = 1;
	    else
		return FAIL;
	}
	else if (STRCMP(name, "gamma") == 0)
	{
	    dx_flags |= 1 << 0;
	    dx_gamma = (float)atof((char *)value);
	}
	else if (STRCMP(name, "contrast") == 0)
	{
	    dx_flags |= 1 << 1;
	    dx_contrast = (float)atof((char *)value);
	}
	else if (STRCMP(name, "level") == 0)
	{
	    dx_flags |= 1 << 2;
	    dx_level = (float)atof((char *)value);
	}
	else if (STRCMP(name, "geom") == 0)
	{
	    dx_flags |= 1 << 3;
	    dx_geom = atoi((char *)value);
	    if (dx_geom < 0 || dx_geom > 2)
		return FAIL;
	}
	else if (STRCMP(name, "renmode") == 0)
	{
	    dx_flags |= 1 << 4;
	    dx_renmode = atoi((char *)value);
	    if (dx_renmode < 0 || dx_renmode > 6)
		return FAIL;
	}
	else if (STRCMP(name, "taamode") == 0)
	{
	    dx_flags |= 1 << 5;
	    dx_taamode = atoi((char *)value);
	    if (dx_taamode < 0 || dx_taamode > 3)
		return FAIL;
	}
	else
	    return FAIL;
    }

    /* Enable DirectX/DirectWrite */
    if (dx_enable)
    {
	if (!directx_enabled())
	    return FAIL;
	DWriteContext_SetRenderingParams(s_dwc, NULL);
	if (dx_flags)
	{
	    DWriteRenderingParams param;
	    DWriteContext_GetRenderingParams(s_dwc, &param);
	    if (dx_flags & (1 << 0))
		param.gamma = dx_gamma;
	    if (dx_flags & (1 << 1))
		param.enhancedContrast = dx_contrast;
	    if (dx_flags & (1 << 2))
		param.clearTypeLevel = dx_level;
	    if (dx_flags & (1 << 3))
		param.pixelGeometry = dx_geom;
	    if (dx_flags & (1 << 4))
		param.renderingMode = dx_renmode;
	    if (dx_flags & (1 << 5))
		param.textAntialiasMode = dx_taamode;
	    DWriteContext_SetRenderingParams(s_dwc, &param);
	}
    }
    s_directx_enabled = dx_enable;

    return OK;
#else
    return FAIL;
#endif
}
#endif

/*
 * These are new in Windows ME/XP, only defined in recent compilers.
 */
#ifndef HANDLE_WM_XBUTTONUP
# define HANDLE_WM_XBUTTONUP(hwnd, wParam, lParam, fn) \
   ((fn)((hwnd), (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
#endif
#ifndef HANDLE_WM_XBUTTONDOWN
# define HANDLE_WM_XBUTTONDOWN(hwnd, wParam, lParam, fn) \
   ((fn)((hwnd), FALSE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
#endif
#ifndef HANDLE_WM_XBUTTONDBLCLK
# define HANDLE_WM_XBUTTONDBLCLK(hwnd, wParam, lParam, fn) \
   ((fn)((hwnd), TRUE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
#endif


#include "version.h"	/* used by dialog box routine for default title */
#ifdef DEBUG
# include <tchar.h>
#endif

/* cproto fails on missing include files */
#ifndef PROTO

#ifndef __MINGW32__
# include <shellapi.h>
#endif
#if defined(FEAT_TOOLBAR) || defined(FEAT_BEVAL) || defined(FEAT_GUI_TABLINE)
# include <commctrl.h>
#endif
#include <windowsx.h>

#ifdef GLOBAL_IME
# include "glbl_ime.h"
#endif

#endif /* PROTO */

#ifdef FEAT_MENU
# define MENUHINTS		/* show menu hints in command line */
#endif

/* Some parameters for dialog boxes.  All in pixels. */
#define DLG_PADDING_X		10
#define DLG_PADDING_Y		10
#define DLG_OLD_STYLE_PADDING_X	5
#define DLG_OLD_STYLE_PADDING_Y	5
#define DLG_VERT_PADDING_X	4	/* For vertical buttons */
#define DLG_VERT_PADDING_Y	4
#define DLG_ICON_WIDTH		34
#define DLG_ICON_HEIGHT		34
#define DLG_MIN_WIDTH		150
#define DLG_FONT_NAME		"MS Sans Serif"
#define DLG_FONT_POINT_SIZE	8
#define DLG_MIN_MAX_WIDTH	400
#define DLG_MIN_MAX_HEIGHT	400

#define DLG_NONBUTTON_CONTROL	5000	/* First ID of non-button controls */

#ifndef WM_XBUTTONDOWN /* For Win2K / winME ONLY */
# define WM_XBUTTONDOWN		0x020B
# define WM_XBUTTONUP		0x020C
# define WM_XBUTTONDBLCLK	0x020D
# define MK_XBUTTON1		0x0020
# define MK_XBUTTON2		0x0040
#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 APIENTRY
# define CALLBACK
# define CONST
# define FAR
# define NEAR
# undef _cdecl
# define _cdecl
typedef int BOOL;
typedef int BYTE;
typedef int DWORD;
typedef int WCHAR;
typedef int ENUMLOGFONT;
typedef int FINDREPLACE;
typedef int HANDLE;
typedef int HBITMAP;
typedef int HBRUSH;
typedef int HDROP;
typedef int INT;
typedef int LOGFONT[];
typedef int LPARAM;
typedef int LPCREATESTRUCT;
typedef int LPCSTR;
typedef int LPCTSTR;
typedef int LPRECT;
typedef int LPSTR;
typedef int LPWINDOWPOS;
typedef int LPWORD;
typedef int LRESULT;
typedef int HRESULT;
# undef MSG
typedef int MSG;
typedef int NEWTEXTMETRIC;
typedef int OSVERSIONINFO;
typedef int PWORD;
typedef int RECT;
typedef int UINT;
typedef int WORD;
typedef int WPARAM;
typedef int POINT;
typedef void *HINSTANCE;
typedef void *HMENU;
typedef void *HWND;
typedef void *HDC;
typedef void VOID;
typedef int LPNMHDR;
typedef int LONG;
typedef int WNDPROC;
typedef int UINT_PTR;
#endif

#ifndef GET_X_LPARAM
# define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp))
#endif

static void _OnPaint( HWND hwnd);
static void clear_rect(RECT *rcp);

static WORD		s_dlgfntheight;		/* height of the dialog font */
static WORD		s_dlgfntwidth;		/* width of the dialog font */

#ifdef FEAT_MENU
static HMENU		s_menuBar = NULL;
#endif
#ifdef FEAT_TEAROFF
static void rebuild_tearoff(vimmenu_T *menu);
static HBITMAP	s_htearbitmap;	    /* bitmap used to indicate tearoff */
#endif

/* Flag that is set while processing a message that must not be interrupted by
 * processing another message. */
static int		s_busy_processing = FALSE;

static int		destroying = FALSE;	/* call DestroyWindow() ourselves */

#ifdef MSWIN_FIND_REPLACE
static UINT		s_findrep_msg = 0;	/* set in gui_w[16/32].c */
static FINDREPLACE	s_findrep_struct;
# if defined(FEAT_MBYTE) && defined(WIN3264)
static FINDREPLACEW	s_findrep_struct_w;
# endif
static HWND		s_findrep_hwnd = NULL;
static int		s_findrep_is_find;	/* TRUE for find dialog, FALSE
						   for find/replace dialog */
#endif

static HINSTANCE	s_hinst = NULL;
#if !defined(FEAT_GUI)
static
#endif
HWND			s_hwnd = NULL;
static HDC		s_hdc = NULL;
static HBRUSH	s_brush = NULL;

#ifdef FEAT_TOOLBAR
static HWND		s_toolbarhwnd = NULL;
static WNDPROC		s_toolbar_wndproc = NULL;
#endif

#ifdef FEAT_GUI_TABLINE
static HWND		s_tabhwnd = NULL;
static WNDPROC		s_tabline_wndproc = NULL;
static int		showing_tabline = 0;
#endif

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

static HWND		s_textArea = NULL;
static UINT		s_uMsg = 0;

static char_u		*s_textfield; /* Used by dialogs to pass back strings */

static int		s_need_activate = FALSE;

/* This variable is set when waiting for an event, which is the only moment
 * scrollbar dragging can be done directly.  It's not allowed while commands
 * are executed, because it may move the cursor and that may cause unexpected
 * problems (e.g., while ":s" is working).
 */
static int allow_scrollbar = FALSE;

#ifdef GLOBAL_IME
# define MyTranslateMessage(x) global_ime_TranslateMessage(x)
#else
# define MyTranslateMessage(x) TranslateMessage(x)
#endif

#if (defined(WIN3264) && defined(FEAT_MBYTE)) || defined(GLOBAL_IME)
  /* use of WindowProc depends on wide_WindowProc */
# define MyWindowProc vim_WindowProc
#else
  /* use ordinary WindowProc */
# define MyWindowProc DefWindowProc
#endif

extern int current_font_height;	    /* this is in os_mswin.c */

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'},
#ifdef FEAT_NETBEANS_INTG
    {VK_PAUSE,		'F', 'B'},	/* Pause == F21 (see gui_gtk_x11.c) */
#endif
    {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'},
    {VK_ADD,		'K', '6'},
    {VK_SUBTRACT,	'K', '7'},
    {VK_DIVIDE,		'K', '8'},
    {VK_MULTIPLY,	'K', '9'},
    {VK_SEPARATOR,	'K', 'A'},	/* Keypad Enter */
    {VK_DECIMAL,	'K', 'B'},

    {VK_NUMPAD0,	'K', 'C'},
    {VK_NUMPAD1,	'K', 'D'},
    {VK_NUMPAD2,	'K', 'E'},
    {VK_NUMPAD3,	'K', 'F'},
    {VK_NUMPAD4,	'K', 'G'},
    {VK_NUMPAD5,	'K', 'H'},
    {VK_NUMPAD6,	'K', 'I'},
    {VK_NUMPAD7,	'K', 'J'},
    {VK_NUMPAD8,	'K', 'K'},
    {VK_NUMPAD9,	'K', 'L'},

    /* 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},
    {CAR,		CAR, NUL},

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

/* Local variables */
static int	s_button_pending = -1;

/* s_getting_focus is set when we got focus but didn't see mouse-up event yet,
 * so don't reset s_button_pending. */
static int	s_getting_focus = FALSE;

static int	s_x_pending;
static int	s_y_pending;
static UINT	s_kFlags_pending;
static UINT	s_wait_timer = 0;   /* Timer for get char from user */
static int	s_timed_out = FALSE;
static int	dead_key = 0;	/* 0: no dead key, 1: dead key pressed */

#ifdef WIN3264
static OSVERSIONINFO os_version;    /* like it says.  Init in gui_mch_init() */
#endif

#ifdef FEAT_BEVAL
/* balloon-eval WM_NOTIFY_HANDLER */
static void Handle_WM_Notify(HWND hwnd, LPNMHDR pnmh);
static void TrackUserActivity(UINT uMsg);
#endif

/*
 * For control IME.
 *
 * These LOGFONT used for IME.
 */
#ifdef FEAT_MBYTE
# ifdef USE_IM_CONTROL
/* holds LOGFONT for 'guifontwide' if available, otherwise 'guifont' */
static LOGFONT norm_logfont;
/* holds LOGFONT for 'guifont' always. */
static LOGFONT sub_logfont;
# endif
#endif

#ifdef FEAT_MBYTE_IME
static LRESULT _OnImeNotify(HWND hWnd, DWORD dwCommand, DWORD dwData);
#endif

#if defined(FEAT_BROWSE)
static char_u *convert_filter(char_u *s);
#endif

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

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

/*
 * Cursor blink functions.
 *
 * This is a simple state machine:
 * BLINK_NONE	not blinking at all
 * BLINK_OFF	blinking, cursor is not shown
 * BLINK_ON	blinking, cursor is shown
 */

#define BLINK_NONE  0
#define BLINK_OFF   1
#define BLINK_ON    2

static int		blink_state = BLINK_NONE;
static long_u		blink_waittime = 700;
static long_u		blink_ontime = 400;
static long_u		blink_offtime = 250;
static UINT		blink_timer = 0;

    int
gui_mch_is_blinking(void)
{
    return blink_state != BLINK_NONE;
}

    int
gui_mch_is_blink_off(void)
{
    return blink_state == BLINK_OFF;
}

    void
gui_mch_set_blinking(long wait, long on, long off)
{
    blink_waittime = wait;
    blink_ontime = on;
    blink_offtime = off;
}

/* ARGSUSED */
    static VOID CALLBACK
_OnBlinkTimer(
    HWND hwnd,
    UINT uMsg,
    UINT idEvent,
    DWORD dwTime)
{
    MSG msg;

    /*
    TRACE2("Got timer event, id %d, blink_timer %d\n", idEvent, blink_timer);
    */

    KillTimer(NULL, idEvent);

    /* Eat spurious WM_TIMER messages */
    while (pPeekMessage(&msg, hwnd, WM_TIMER, WM_TIMER, PM_REMOVE))
	;

    if (blink_state == BLINK_ON)
    {
	gui_undraw_cursor();
	blink_state = BLINK_OFF;
	blink_timer = (UINT) SetTimer(NULL, 0, (UINT)blink_offtime,
						    (TIMERPROC)_OnBlinkTimer);
    }
    else
    {
	gui_update_cursor(TRUE, FALSE);
	blink_state = BLINK_ON;
	blink_timer = (UINT) SetTimer(NULL, 0, (UINT)blink_ontime,
							 (TIMERPROC)_OnBlinkTimer);
    }
}

    static void
gui_mswin_rm_blink_timer(void)
{
    MSG msg;

    if (blink_timer != 0)
    {
	KillTimer(NULL, blink_timer);
	/* Eat spurious WM_TIMER messages */
	while (pPeekMessage(&msg, s_hwnd, WM_TIMER, WM_TIMER, PM_REMOVE))
	    ;
	blink_timer = 0;
    }
}

/*
 * Stop the cursor blinking.  Show the cursor if it wasn't shown.
 */
    void
gui_mch_stop_blink(void)
{
    gui_mswin_rm_blink_timer();
    if (blink_state == BLINK_OFF)
	gui_update_cursor(TRUE, FALSE);
    blink_state = BLINK_NONE;
}

/*
 * Start the cursor blinking.  If it was already blinking, this restarts the
 * waiting time and shows the cursor.
 */
    void
gui_mch_start_blink(void)
{
    gui_mswin_rm_blink_timer();

    /* Only switch blinking on if none of the times is zero */
    if (blink_waittime && blink_ontime && blink_offtime && gui.in_focus)
    {
	blink_timer = (UINT)SetTimer(NULL, 0, (UINT)blink_waittime,
						    (TIMERPROC)_OnBlinkTimer);
	blink_state = BLINK_ON;
	gui_update_cursor(TRUE, FALSE);
    }
}

/*
 * Call-back routines.
 */

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

    /*
    TRACE2("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 (pPeekMessage(&msg, hwnd, WM_TIMER, WM_TIMER, PM_REMOVE))
	;
    if (idEvent == s_wait_timer)
	s_wait_timer = 0;
}

/*ARGSUSED*/
    static void
_OnDeadChar(
    HWND hwnd,
    UINT ch,
    int cRepeat)
{
    dead_key = 1;
}

/*
 * Convert Unicode character "ch" to bytes in "string[slen]".
 * When "had_alt" is TRUE the ALT key was included in "ch".
 * Return the length.
 */
    static int
char_to_string(int ch, char_u *string, int slen, int had_alt)
{
    int		len;
    int		i;
#ifdef FEAT_MBYTE
    WCHAR	wstring[2];
    char_u	*ws = NULL;

    if (os_version.dwPlatformId != VER_PLATFORM_WIN32_NT)
    {
	/* On Windows 95/98 we apparently get the character in the active
	 * codepage, not in UCS-2.  If conversion is needed convert it to
	 * UCS-2 first. */
	if ((int)GetACP() == enc_codepage)
	    len = 0;	    /* no conversion required */
	else
	{
	    string[0] = ch;
	    len = MultiByteToWideChar(GetACP(), 0, (LPCSTR)string,
		    1, wstring, 2);
	}
    }
    else
    {
	wstring[0] = ch;
	len = 1;
    }

    if (len > 0)
    {
	/* "ch" is a UTF-16 character.  Convert it to a string of bytes.  When
	 * "enc_codepage" is non-zero use the standard Win32 function,
	 * otherwise use our own conversion function (e.g., for UTF-8). */
	if (enc_codepage > 0)
	{
	    len = WideCharToMultiByte(enc_codepage, 0, wstring, len,
					       (LPSTR)string, slen, 0, NULL);
	    /* If we had included the ALT key into the character but now the
	     * upper bit is no longer set, that probably means the conversion
	     * failed.  Convert the original character and set the upper bit
	     * afterwards. */
	    if (had_alt && len == 1 && ch >= 0x80 && string[0] < 0x80)
	    {
		wstring[0] = ch & 0x7f;
		len = WideCharToMultiByte(enc_codepage, 0, wstring, len,
					       (LPSTR)string, slen, 0, NULL);
		if (len == 1) /* safety check */
		    string[0] |= 0x80;
	    }
	}
	else
	{
	    len = 1;
	    ws = utf16_to_enc(wstring, &len);
	    if (ws == NULL)
		len = 0;
	    else
	    {
		if (len > slen)	/* just in case */
		    len = slen;
		mch_memmove(string, ws, len);
		vim_free(ws);
	    }
	}
    }

    if (len == 0)
#endif
    {
	string[0] = ch;
	len = 1;
    }

    for (i = 0; i < len; ++i)
	if (string[i] == CSI && len <= slen - 2)
	{
	    /* Insert CSI as K_CSI. */
	    mch_memmove(string + i + 3, string + i + 1, len - i - 1);
	    string[++i] = KS_EXTRA;
	    string[++i] = (int)KE_CSI;
	    len += 2;
	}

    return len;
}

/*
 * Key hit, add it to the input buffer.
 */
/*ARGSUSED*/
    static void
_OnChar(
    HWND hwnd,
    UINT ch,
    int cRepeat)
{
    char_u	string[40];
    int		len = 0;

    dead_key = 0;

    len = char_to_string(ch, string, 40, FALSE);
    if (len == 1 && string[0] == Ctrl_C && ctrl_c_interrupts)
    {
	trash_input_buf();
	got_int = TRUE;
    }

    add_to_input_buf(string, len);
}

/*
 * Alt-Key hit, add it to the input buffer.
 */
/*ARGSUSED*/
    static void
_OnSysChar(
    HWND hwnd,
    UINT cch,
    int cRepeat)
{
    char_u	string[40]; /* Enough for multibyte character */
    int		len;
    int		modifiers;
    int		ch = cch;   /* special keys are negative */

    dead_key = 0;

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

    /* OK, we have a character key (given by ch) which was entered with the
     * ALT key pressed. Eg, if the user presses Alt-A, then ch == 'A'. Note
     * that the system distinguishes Alt-a and Alt-A (Alt-Shift-a unless
     * CAPSLOCK is pressed) at this point.
     */
    modifiers = MOD_MASK_ALT;
    if (GetKeyState(VK_SHIFT) & 0x8000)
	modifiers |= MOD_MASK_SHIFT;
    if (GetKeyState(VK_CONTROL) & 0x8000)
	modifiers |= MOD_MASK_CTRL;

    ch = simplify_key(ch, &modifiers);
    /* remove the SHIFT modifier for keys where it's already included, e.g.,
     * '(' and '*' */
    if (ch < 0x100 && !isalpha(ch) && isprint(ch))
	modifiers &= ~MOD_MASK_SHIFT;

    /* Interpret the ALT key as making the key META, include SHIFT, etc. */
    ch = extract_modifiers(ch, &modifiers);
    if (ch == CSI)
	ch = K_CSI;

    len = 0;
    if (modifiers)
    {
	string[len++] = CSI;
	string[len++] = KS_MODIFIER;
	string[len++] = modifiers;
    }

    if (IS_SPECIAL((int)ch))
    {
	string[len++] = CSI;
	string[len++] = K_SECOND((int)ch);
	string[len++] = K_THIRD((int)ch);
    }
    else
    {
	/* Although the documentation isn't clear about it, we assume "ch" is
	 * a Unicode character. */
	len += char_to_string(ch, string + len, 40 - len, TRUE);
    }

    add_to_input_buf(string, len);
}

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

    s_getting_focus = FALSE;

    if (keyFlags & MK_SHIFT)
	vim_modifiers |= MOUSE_SHIFT;
    if (keyFlags & MK_CONTROL)
	vim_modifiers |= MOUSE_CTRL;
    if (GetKeyState(VK_MENU) & 0x8000)
	vim_modifiers |= MOUSE_ALT;

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

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

    /* Give main window the focus: this is so the cursor isn't hollow. */
    (void)SetFocus(s_hwnd);

    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;
    else if (s_uMsg == WM_XBUTTONDOWN || s_uMsg == WM_XBUTTONDBLCLK)
    {
#ifndef GET_XBUTTON_WPARAM
# define GET_XBUTTON_WPARAM(wParam)	(HIWORD(wParam))
#endif
	button = ((GET_XBUTTON_WPARAM(s_wParam) == 1) ? MOUSE_X1 : MOUSE_X2);
    }
    else if (s_uMsg == WM_CAPTURECHANGED)
    {
	/* on W95/NT4, somehow you get in here with an odd Msg
	 * if you press one button while holding down the other..*/
	if (s_button_pending == MOUSE_LEFT)
	    button = MOUSE_RIGHT;
	else
	    button = MOUSE_LEFT;
    }
    if (button >= 0)
    {
	repeated_click = ((int)(currentTime - s_prevTime) < p_mouset);

	/*
	 * Holding down the left and right buttons simulates pushing the middle
	 * button.
	 */
	if (repeated_click
		&& ((button == MOUSE_LEFT && s_button_pending == MOUSE_RIGHT)
		    || (button == MOUSE_RIGHT
					  && s_button_pending == MOUSE_LEFT)))
	{
	    /*
	     * 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_button_pending = -1;
	    _OnMouseEvent(button, x, y, repeated_click, keyFlags);
	}
	else if ((repeated_click)
		|| (mouse_model_popup() && (button == MOUSE_RIGHT)))
	{
	    if (s_button_pending > -1)
	    {
		    _OnMouseEvent(s_button_pending, x, y, FALSE, keyFlags);
		    s_button_pending = -1;
	    }
	    /* TRACE("Button down at x %d, y %d\n", x, y); */
	    _OnMouseEvent(button, x, y, repeated_click, keyFlags);
	}
	else
	{
	    /*
	     * If this is the first press (i.e. not a multiple click) don't
	     * action immediately, but store and wait for:
	     * i) button-up
	     * ii) mouse move
	     * iii) another button press
	     * before using it.
	     * This enables us to make left+right simulate middle button,
	     * without left or right being actioned first.  The side-effect is
	     * that if you click and hold the mouse without dragging, the
	     * cursor doesn't move until you release the button. In practice
	     * this is hardly a problem.
	     */
	    s_button_pending = button;
	    s_x_pending = x;
	    s_y_pending = y;
	    s_kFlags_pending = keyFlags;
	}

	s_prevTime = currentTime;
    }
}

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

    s_getting_focus = FALSE;
    if (s_button_pending > -1)
    {
	/* Delayed action for mouse down event */
	_OnMouseEvent(s_button_pending, s_x_pending,
					s_y_pending, FALSE, s_kFlags_pending);
	s_button_pending = -1;
    }
    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
						| MK_XBUTTON1 | MK_XBUTTON2)))
	{
	    gui_mouse_moved(x, y);
	    return;
	}

	/*
	 * While button is down, keep grabbing mouse move events when
	 * the mouse goes outside the window
	 */
	SetCapture(s_textArea);
	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);
}

#ifdef FEAT_MENU
/*
 * Find the vimmenu_T with the given id
 */
    static vimmenu_T *
gui_mswin_find_menu(
    vimmenu_T	*pMenu,
    int		id)
{
    vimmenu_T	*pChildMenu;

    while (pMenu)
    {
	if (pMenu->id == (UINT)id)
	    break;
	if (pMenu->children != NULL)
	{
	    pChildMenu = gui_mswin_find_menu(pMenu->children, id);
	    if (pChildMenu)
	    {
		pMenu = pChildMenu;
		break;
	    }
	}
	pMenu = pMenu->next;
    }
    return pMenu;
}

/*ARGSUSED*/
    static void
_OnMenu(
    HWND	hwnd,
    int		id,
    HWND	hwndCtl,
    UINT	codeNotify)
{
    vimmenu_T	*pMenu;

    pMenu = gui_mswin_find_menu(root_menu, id);
    if (pMenu)
	gui_menu_cb(pMenu);
}
#endif

#ifdef MSWIN_FIND_REPLACE
# if defined(FEAT_MBYTE) && defined(WIN3264)
/*
 * copy useful data from structure LPFINDREPLACE to structure LPFINDREPLACEW
 */
    static void
findrep_atow(LPFINDREPLACEW lpfrw, LPFINDREPLACE lpfr)
{
    WCHAR *wp;

    lpfrw->hwndOwner = lpfr->hwndOwner;
    lpfrw->Flags = lpfr->Flags;

    wp = enc_to_utf16((char_u *)lpfr->lpstrFindWhat, NULL);
    wcsncpy(lpfrw->lpstrFindWhat, wp, lpfrw->wFindWhatLen - 1);
    vim_free(wp);

    /* the field "lpstrReplaceWith" doesn't need to be copied */
}

/*
 * copy useful data from structure LPFINDREPLACEW to structure LPFINDREPLACE
 */
    static void
findrep_wtoa(LPFINDREPLACE lpfr, LPFINDREPLACEW lpfrw)
{
    char_u *p;

    lpfr->Flags = lpfrw->Flags;

    p = utf16_to_enc((short_u*)lpfrw->lpstrFindWhat, NULL);
    vim_strncpy((char_u *)lpfr->lpstrFindWhat, p, lpfr->wFindWhatLen - 1);
    vim_free(p);

    p = utf16_to_enc((short_u*)lpfrw->lpstrReplaceWith, NULL);
    vim_strncpy((char_u *)lpfr->lpstrReplaceWith, p, lpfr->wReplaceWithLen - 1);
    vim_free(p);
}
# endif

/*
 * Handle a Find/Replace window message.
 */
    static void
_OnFindRepl(void)
{
    int	    flags = 0;
    int	    down;

# if defined(FEAT_MBYTE) && defined(WIN3264)
    /* If the OS is Windows NT, and 'encoding' differs from active codepage:
     * convert text from wide string. */
    if (os_version.dwPlatformId == VER_PLATFORM_WIN32_NT
			&& enc_codepage >= 0 && (int)GetACP() != enc_codepage)
    {
	findrep_wtoa(&s_findrep_struct, &s_findrep_struct_w);
    }
# endif

    if (s_findrep_struct.Flags & FR_DIALOGTERM)
	/* Give main window the focus back. */
	(void)SetFocus(s_hwnd);

    if (s_findrep_struct.Flags & FR_FINDNEXT)
    {
	flags = FRD_FINDNEXT;

	/* Give main window the focus back: this is so the cursor isn't
	 * hollow. */
	(void)SetFocus(s_hwnd);
    }
    else if (s_findrep_struct.Flags & FR_REPLACE)
    {
	flags = FRD_REPLACE;

	/* Give main window the focus back: this is so the cursor isn't
	 * hollow. */
	(void)SetFocus(s_hwnd);
    }
    else if (s_findrep_struct.Flags & FR_REPLACEALL)
    {
	flags = FRD_REPLACEALL;
    }

    if (flags != 0)
    {
	/* Call the generic GUI function to do the actual work. */
	if (s_findrep_struct.Flags & FR_WHOLEWORD)
	    flags |= FRD_WHOLE_WORD;
	if (s_findrep_struct.Flags & FR_MATCHCASE)
	    flags |= FRD_MATCH_CASE;
	down = (s_findrep_struct.Flags & FR_DOWN) != 0;
	gui_do_findrepl(flags, (char_u *)s_findrep_struct.lpstrFindWhat,
			     (char_u *)s_findrep_struct.lpstrReplaceWith, down);
    }
}
#endif

    static void
HandleMouseHide(UINT uMsg, LPARAM lParam)
{
    static LPARAM last_lParam = 0L;

    /* We sometimes get a mousemove when the mouse didn't move... */
    if (uMsg == WM_MOUSEMOVE || uMsg == WM_NCMOUSEMOVE)
    {
	if (lParam == last_lParam)
	    return;
	last_lParam = lParam;
    }

    /* Handle specially, to centralise coding. We need to be sure we catch all
     * possible events which should cause us to restore the cursor (as it is a
     * shared resource, we take full responsibility for it).
     */
    switch (uMsg)
    {
    case WM_KEYUP:
    case WM_CHAR:
	/*
	 * blank out the pointer if necessary
	 */
	if (p_mh)
	    gui_mch_mousehide(TRUE);
	break;

    case WM_SYSKEYUP:	 /* show the pointer when a system-key is pressed */
    case WM_SYSCHAR:
    case WM_MOUSEMOVE:	 /* show the pointer on any mouse action */
    case WM_LBUTTONDOWN:
    case WM_LBUTTONUP:
    case WM_MBUTTONDOWN:
    case WM_MBUTTONUP:
    case WM_RBUTTONDOWN:
    case WM_RBUTTONUP:
    case WM_XBUTTONDOWN:
    case WM_XBUTTONUP:
    case WM_NCMOUSEMOVE:
    case WM_NCLBUTTONDOWN:
    case WM_NCLBUTTONUP:
    case WM_NCMBUTTONDOWN:
    case WM_NCMBUTTONUP:
    case WM_NCRBUTTONDOWN:
    case WM_NCRBUTTONUP:
    case WM_KILLFOCUS:
	/*
	 * if the pointer is currently hidden, then we should show it.
	 */
	gui_mch_mousehide(FALSE);
	break;
    }
}

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

    HandleMouseHide(uMsg, lParam);

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

#ifdef FEAT_BEVAL
    TrackUserActivity(uMsg);
#endif

    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);
	HANDLE_MSG(hwnd, WM_XBUTTONDBLCLK,_OnMouseButtonDown);
	HANDLE_MSG(hwnd, WM_XBUTTONDOWN,_OnMouseButtonDown);
	HANDLE_MSG(hwnd, WM_XBUTTONUP,	_OnMouseMoveOrRelease);

#ifdef FEAT_BEVAL
	case WM_NOTIFY: Handle_WM_Notify(hwnd, (LPNMHDR)lParam);
	    return TRUE;
#endif
	default:
	    return MyWindowProc(hwnd, uMsg, wParam, lParam);
    }
}

#if (defined(WIN3264) && defined(FEAT_MBYTE)) \
	|| defined(GLOBAL_IME) \
	|| defined(PROTO)
# ifdef PROTO
typedef int WINAPI;
# endif

    LRESULT WINAPI
vim_WindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
# ifdef GLOBAL_IME
    return global_ime_DefWindowProc(hwnd, message, wParam, lParam);
# else
    if (wide_WindowProc)
	return DefWindowProcW(hwnd, message, wParam, lParam);
    return DefWindowProc(hwnd, message, wParam, lParam);
#endif
}
#endif

/*
 * Called when the foreground or background color has been changed.
 */
    void
gui_mch_new_colors(void)
{
    /* nothing to do? */
}

/*
 * Set the colors to their default values.
 */
    void
gui_mch_def_colors(void)
{
    gui.norm_pixel = GetSysColor(COLOR_WINDOWTEXT);
    gui.back_pixel = GetSysColor(COLOR_WINDOW);
    gui.def_norm_pixel = gui.norm_pixel;
    gui.def_back_pixel = gui.back_pixel;
}

/*
 * Open the GUI window which was created by a call to gui_mch_init().
 */
    int
gui_mch_open(void)
{
#ifndef SW_SHOWDEFAULT
# define SW_SHOWDEFAULT 10	/* Borland 5.0 doesn't have it */
#endif
    /* Actually open the window, if not already visible
     * (may be done already in gui_mch_set_shellsize) */
    if (!IsWindowVisible(s_hwnd))
	ShowWindow(s_hwnd, SW_SHOWDEFAULT);

#ifdef MSWIN_FIND_REPLACE
    /* Init replace string here, so that we keep it when re-opening the
     * dialog. */
    s_findrep_struct.lpstrReplaceWith[0] = NUL;
#endif

    return OK;
}

/*
 * Get the position of the top left corner of the window.
 */
    int
gui_mch_get_winpos(int *x, int *y)
{
    RECT    rect;

    GetWindowRect(s_hwnd, &rect);
    *x = rect.left;
    *y = rect.top;
    return OK;
}

/*
 * Set the position of the top left corner of the window to the given
 * coordinates.
 */
    void
gui_mch_set_winpos(int x, int y)
{
    SetWindowPos(s_hwnd, NULL, x, y, 0, 0,
		 SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
}
    void
gui_mch_set_text_area_pos(int x, int y, int w, int h)
{
    static int oldx = 0;
    static int oldy = 0;

    SetWindowPos(s_textArea, NULL, x, y, w, h, SWP_NOZORDER | SWP_NOACTIVATE);

#ifdef FEAT_TOOLBAR
    if (vim_strchr(p_go, GO_TOOLBAR) != NULL)
	SendMessage(s_toolbarhwnd, WM_SIZE,
	      (WPARAM)0, (LPARAM)(w + ((long)(TOOLBAR_BUTTON_HEIGHT+8)<<16)));
#endif
#if defined(FEAT_GUI_TABLINE)
    if (showing_tabline)
    {
	int	top = 0;
	RECT	rect;

# ifdef FEAT_TOOLBAR
	if (vim_strchr(p_go, GO_TOOLBAR) != NULL)
	    top = TOOLBAR_BUTTON_HEIGHT + TOOLBAR_BORDER_HEIGHT;
# endif
	GetClientRect(s_hwnd, &rect);
	MoveWindow(s_tabhwnd, 0, top, rect.right, gui.tabline_height, TRUE);
    }
#endif

    /* When side scroll bar is unshown, the size of window will change.
     * then, the text area move left or right. thus client rect should be
     * forcedly redrawn. (Yasuhiro Matsumoto) */
    if (oldx != x || oldy != y)
    {
	InvalidateRect(s_hwnd, NULL, FALSE);
	oldx = x;
	oldy = y;
    }
}


/*
 * Scrollbar stuff:
 */

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

    /* TODO: When the window is maximized, the size of the window stays the
     * same, thus the size of the text area changes.  On Win98 it's OK, on Win
     * NT 4.0 it's not... */
}

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

    void
gui_mch_create_scrollbar(
    scrollbar_T *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);
}

/*
 * Find the scrollbar with the given hwnd.
 */
	 static scrollbar_T *
gui_mswin_find_scrollbar(HWND hwnd)
{
    win_T	*wp;

    if (gui.bottom_sbar.id == hwnd)
	return &gui.bottom_sbar;
    FOR_ALL_WINDOWS(wp)
    {
	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;
}

/*
 * Get the character size of a font.
 */
    static void
GetFontSize(GuiFont font)
{
    HWND    hwnd = GetDesktopWindow();
    HDC	    hdc = GetWindowDC(hwnd);
    HFONT   hfntOld = SelectFont(hdc, (HFONT)font);
    TEXTMETRIC tm;

    GetTextMetrics(hdc, &tm);
    gui.char_width = tm.tmAveCharWidth + tm.tmOverhang;

    gui.char_height = tm.tmHeight + p_linespace;

    SelectFont(hdc, hfntOld);

    ReleaseDC(hwnd, hdc);
}

/*
 * Adjust gui.char_height (after 'linespace' was changed).
 */
    int
gui_mch_adjust_charheight(void)
{
    GetFontSize(gui.norm_font);
    return OK;
}

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

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

    if (font == NULL)
	return NOFONT;

    return (GuiFont)font;
}

    static int
pixels_to_points(int pixels, int vertical)
{
    int		points;
    HWND	hwnd;
    HDC		hdc;

    hwnd = GetDesktopWindow();
    hdc = GetWindowDC(hwnd);

    points = MulDiv(pixels, 72,
		    GetDeviceCaps(hdc, vertical ? LOGPIXELSY : LOGPIXELSX));

    ReleaseDC(hwnd, hdc);

    return points;
}

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

    if (get_logfont(&lf, name, NULL, giveErrorIfMissing) == OK)
	font = get_font_handle(&lf);
    if (font == NOFONT && giveErrorIfMissing)
	EMSG2(_(e_font), name);
    return font;
}

#if defined(FEAT_EVAL) || defined(PROTO)
/*
 * Return the name of font "font" in allocated memory.
 * Don't know how to get the actual name, thus use the provided name.
 */
/*ARGSUSED*/
    char_u *
gui_mch_get_fontname(GuiFont font, char_u *name)
{
    if (name == NULL)
	return NULL;
    return vim_strsave(name);
}
#endif

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

/*
 * Return the Pixel value (color) for the given color name.
 * Return INVALCOLOR for error.
 */
    guicolor_T
gui_mch_get_color(char_u *name)
{
    int i;

    typedef struct SysColorTable
    {
	char	    *name;
	int	    color;
    } SysColorTable;

    static SysColorTable sys_table[] =
    {
#ifdef WIN3264
	{"SYS_3DDKSHADOW", COLOR_3DDKSHADOW},
	{"SYS_3DHILIGHT", COLOR_3DHILIGHT},
#ifndef __MINGW32__
	{"SYS_3DHIGHLIGHT", COLOR_3DHIGHLIGHT},
#endif
	{"SYS_BTNHILIGHT", COLOR_BTNHILIGHT},
	{"SYS_BTNHIGHLIGHT", COLOR_BTNHIGHLIGHT},
	{"SYS_3DLIGHT", COLOR_3DLIGHT},
	{"SYS_3DSHADOW", COLOR_3DSHADOW},
	{"SYS_DESKTOP", COLOR_DESKTOP},
	{"SYS_INFOBK", COLOR_INFOBK},
	{"SYS_INFOTEXT", COLOR_INFOTEXT},
	{"SYS_3DFACE", COLOR_3DFACE},
#endif
	{"SYS_BTNFACE", COLOR_BTNFACE},
	{"SYS_BTNSHADOW", COLOR_BTNSHADOW},
	{"SYS_ACTIVEBORDER", COLOR_ACTIVEBORDER},
	{"SYS_ACTIVECAPTION", COLOR_ACTIVECAPTION},
	{"SYS_APPWORKSPACE", COLOR_APPWORKSPACE},
	{"SYS_BACKGROUND", COLOR_BACKGROUND},
	{"SYS_BTNTEXT", COLOR_BTNTEXT},
	{"SYS_CAPTIONTEXT", COLOR_CAPTIONTEXT},
	{"SYS_GRAYTEXT", COLOR_GRAYTEXT},
	{"SYS_HIGHLIGHT", COLOR_HIGHLIGHT},
	{"SYS_HIGHLIGHTTEXT", COLOR_HIGHLIGHTTEXT},
	{"SYS_INACTIVEBORDER", COLOR_INACTIVEBORDER},
	{"SYS_INACTIVECAPTION", COLOR_INACTIVECAPTION},
	{"SYS_INACTIVECAPTIONTEXT", COLOR_INACTIVECAPTIONTEXT},
	{"SYS_MENU", COLOR_MENU},
	{"SYS_MENUTEXT", COLOR_MENUTEXT},
	{"SYS_SCROLLBAR", COLOR_SCROLLBAR},
	{"SYS_WINDOW", COLOR_WINDOW},
	{"SYS_WINDOWFRAME", COLOR_WINDOWFRAME},
	{"SYS_WINDOWTEXT", COLOR_WINDOWTEXT}
    };

    /*
     * Try to look up a system colour.
     */
    for (i = 0; i < sizeof(sys_table) / sizeof(sys_table[0]); i++)
	if (STRICMP(name, sys_table[i].name) == 0)
	    return GetSysColor(sys_table[i].color);

    return gui_get_color_cmn(name);
}

/*
 * Return OK if the key with the termcap name "name" is supported.
 */
    int
gui_mch_haskey(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(void)
{
    MessageBeep(MB_OK);
}
/*
 * Invert a rectangle from row r, column c, for nr rows and nc columns.
 */
    void
gui_mch_invert_rectangle(
    int	    r,
    int	    c,
    int	    nr,
    int	    nc)
{
    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(s_hdc, &rc);
}

/*
 * Iconify the GUI window.
 */
    void
gui_mch_iconify(void)
{
    ShowWindow(s_hwnd, SW_MINIMIZE);
}

/*
 * Draw a cursor without focus.
 */
    void
gui_mch_draw_hollow_cursor(guicolor_T color)
{
    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;
#ifdef FEAT_MBYTE
    if (mb_lefthalve(gui.row, gui.col))
	rc.right += gui.char_width;
#endif
    rc.bottom = rc.top + gui.char_height;
    hbr = CreateSolidBrush(color);
    FrameRect(s_hdc, &rc, hbr);
    DeleteBrush(hbr);
}
/*
 * Draw part of a cursor, "w" pixels wide, and "h" pixels high, using
 * color "color".
 */
    void
gui_mch_draw_part_cursor(
    int		w,
    int		h,
    guicolor_T	color)
{
    HBRUSH	hbr;
    RECT	rc;

    /*
     * Note: FillRect() excludes right and bottom of rectangle.
     */
    rc.left =
#ifdef FEAT_RIGHTLEFT
		/* vertical line should be on the right of current point */
		CURSOR_BAR_RIGHT ? FILL_X(gui.col + 1) - w :
#endif
		    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(color);
    FillRect(s_hdc, &rc, hbr);
    DeleteBrush(hbr);
}


/*
 * Generates a VK_SPACE when the internal dead_key flag is set to output the
 * dead key's nominal character and re-post the original message.
 */
    static void
outputDeadKey_rePost(MSG originalMsg)
{
    static MSG deadCharExpel;

    if (!dead_key)
	return;

    dead_key = 0;

    /* Make Windows generate the dead key's character */
    deadCharExpel.message = originalMsg.message;
    deadCharExpel.hwnd    = originalMsg.hwnd;
    deadCharExpel.wParam  = VK_SPACE;

    MyTranslateMessage(&deadCharExpel);

    /* re-generate the current character free of the dead char influence */
    PostMessage(originalMsg.hwnd, originalMsg.message, originalMsg.wParam,
							  originalMsg.lParam);
}


/*
 * Process a single Windows message.
 * If one is not available we hang until one is.
 */
    static void
process_message(void)
{
    MSG		msg;
    UINT	vk = 0;		/* Virtual key */
    char_u	string[40];
    int		i;
    int		modifiers = 0;
    int		key;
#ifdef FEAT_MENU
    static char_u k10[] = {K_SPECIAL, 'k', ';', 0};
#endif

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

#ifdef FEAT_OLE
    /* Look after OLE Automation commands */
    if (msg.message == WM_OLE)
    {
	char_u *str = (char_u *)msg.lParam;
	if (str == NULL || *str == NUL)
	{
	    /* Message can't be ours, forward it.  Fixes problem with Ultramon
	     * 3.0.4 */
	    pDispatchMessage(&msg);
	}
	else
	{
	    add_to_input_buf(str, (int)STRLEN(str));
	    vim_free(str);  /* was allocated in CVim::SendKeys() */
	}
	return;
    }
#endif

#ifdef MSWIN_FIND_REPLACE
    /* Don't process messages used by the dialog */
    if (s_findrep_hwnd != NULL && pIsDialogMessage(s_findrep_hwnd, &msg))
    {
	HandleMouseHide(msg.message, msg.lParam);
	return;
    }
#endif

    /*
     * Check if it's a special key that we recognise.  If not, call
     * TranslateMessage().
     */
    if (msg.message == WM_KEYDOWN || msg.message == WM_SYSKEYDOWN)
    {
	vk = (int) msg.wParam;

	/*
	 * Handle dead keys in special conditions in other cases we let Windows
	 * handle them and do not interfere.
	 *
	 * The dead_key flag must be reset on several occasions:
	 * - in _OnChar() (or _OnSysChar()) as any dead key was necessarily
	 *   consumed at that point (This is when we let Windows combine the
	 *   dead character on its own)
	 *
	 * - Before doing something special such as regenerating keypresses to
	 *   expel the dead character as this could trigger an infinite loop if
	 *   for some reason MyTranslateMessage() do not trigger a call
	 *   immediately to _OnChar() (or _OnSysChar()).
	 */
	if (dead_key)
	{
	    /*
	     * If a dead key was pressed and the user presses VK_SPACE,
	     * VK_BACK, or VK_ESCAPE it means that he actually wants to deal
	     * with the dead char now, so do nothing special and let Windows
	     * handle it.
	     *
	     * Note that VK_SPACE combines with the dead_key's character and
	     * only one WM_CHAR will be generated by TranslateMessage(), in
	     * the two other cases two WM_CHAR will be generated: the dead
	     * char and VK_BACK or VK_ESCAPE. That is most likely what the
	     * user expects.
	     */
	    if ((vk == VK_SPACE || vk == VK_BACK || vk == VK_ESCAPE))
	    {
		dead_key = 0;
		MyTranslateMessage(&msg);
		return;
	    }
	    /* In modes where we are not typing, dead keys should behave
	     * normally */
	    else if (!(get_real_state() & (INSERT | CMDLINE | SELECTMODE)))
	    {
		outputDeadKey_rePost(msg);
		return;
	    }
	}

	/* Check for CTRL-BREAK */
	if (vk == VK_CANCEL)
	{
	    trash_input_buf();
	    got_int = TRUE;
	    string[0] = Ctrl_C;
	    add_to_input_buf(string, 1);
	}

	for (i = 0; special_keys[i].key_sym != 0; i++)
	{
	    /* ignore VK_SPACE when ALT key pressed: system menu */
	    if (special_keys[i].key_sym == vk
		    && (vk != VK_SPACE || !(GetKeyState(VK_MENU) & 0x8000)))
	    {
		/*
		 * Behave as expected if we have a dead key and the special key
		 * is a key that would normally trigger the dead key nominal
		 * character output (such as a NUMPAD printable character or
		 * the TAB key, etc...).
		 */
		if (dead_key && (special_keys[i].vim_code0 == 'K'
						|| vk == VK_TAB || vk == CAR))
		{
		    outputDeadKey_rePost(msg);
		    return;
		}

#ifdef FEAT_MENU
		/* Check for <F10>: Windows selects the menu.  When <F10> is
		 * mapped we want to use the mapping instead. */
		if (vk == VK_F10
			&& gui.menu_is_active
			&& check_map(k10, State, FALSE, TRUE, FALSE,
							  NULL, NULL) == NULL)
		    break;
#endif
		if (GetKeyState(VK_SHIFT) & 0x8000)
		    modifiers |= 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)
		    modifiers ^= MOD_MASK_SHIFT;
		*/
		if (GetKeyState(VK_CONTROL) & 0x8000)
		    modifiers |= MOD_MASK_CTRL;
		if (GetKeyState(VK_MENU) & 0x8000)
		    modifiers |= MOD_MASK_ALT;

		if (special_keys[i].vim_code1 == NUL)
		    key = special_keys[i].vim_code0;
		else
		    key = TO_SPECIAL(special_keys[i].vim_code0,
						   special_keys[i].vim_code1);
		key = simplify_key(key, &modifiers);
		if (key == CSI)
		    key = K_CSI;

		if (modifiers)
		{
		    string[0] = CSI;
		    string[1] = KS_MODIFIER;
		    string[2] = modifiers;
		    add_to_input_buf(string, 3);
		}

		if (IS_SPECIAL(key))
		{
		    string[0] = CSI;
		    string[1] = K_SECOND(key);
		    string[2] = K_THIRD(key);
		    add_to_input_buf(string, 3);
		}
		else
		{
		    int	len;

		    /* Handle "key" as a Unicode character. */
		    len = char_to_string(key, string, 40, FALSE);
		    add_to_input_buf(string, len);
		}
		break;
	    }
	}
	if (special_keys[i].key_sym == 0)
	{
	    /* Some keys need C-S- where they should only need C-.
	     * Ignore 0xff, Windows XP sends it when NUMLOCK has changed since
	     * system startup (Helmut Stiegler, 2003 Oct 3). */
	    if (vk != 0xff
		    && (GetKeyState(VK_CONTROL) & 0x8000)
		    && !(GetKeyState(VK_SHIFT) & 0x8000)
		    && !(GetKeyState(VK_MENU) & 0x8000))
	    {
		/* CTRL-6 is '^'; Japanese keyboard maps '^' to vk == 0xDE */
		if (vk == '6' || MapVirtualKey(vk, 2) == (UINT)'^')
		{
		    string[0] = Ctrl_HAT;
		    add_to_input_buf(string, 1);
		}
		/* vk == 0xBD AZERTY for CTRL-'-', but CTRL-[ for * QWERTY! */
		else if (vk == 0xBD)	/* QWERTY for CTRL-'-' */
		{
		    string[0] = Ctrl__;
		    add_to_input_buf(string, 1);
		}
		/* CTRL-2 is '@'; Japanese keyboard maps '@' to vk == 0xC0 */
		else if (vk == '2' || MapVirtualKey(vk, 2) == (UINT)'@')
		{
		    string[0] = Ctrl_AT;
		    add_to_input_buf(string, 1);
		}
		else
		    MyTranslateMessage(&msg);
	    }
	    else
		MyTranslateMessage(&msg);
	}
    }
#ifdef FEAT_MBYTE_IME
    else if (msg.message == WM_IME_NOTIFY)
	_OnImeNotify(msg.hwnd, (DWORD)msg.wParam, (DWORD)msg.lParam);
    else if (msg.message == WM_KEYUP && im_get_status())
	/* added for non-MS IME (Yasuhiro Matsumoto) */
	MyTranslateMessage(&msg);
#endif
#if !defined(FEAT_MBYTE_IME) && defined(GLOBAL_IME)
/* GIME_TEST */
    else if (msg.message == WM_IME_STARTCOMPOSITION)
    {
	POINT point;

	global_ime_set_font(&norm_logfont);
	point.x = FILL_X(gui.col);
	point.y = FILL_Y(gui.row);
	MapWindowPoints(s_textArea, s_hwnd, &point, 1);
	global_ime_set_position(&point);
    }
#endif

#ifdef FEAT_MENU
    /* Check for <F10>: Default effect is to select the menu.  When <F10> is
     * mapped we need to stop it here to avoid strange effects (e.g., for the
     * key-up event) */
    if (vk != VK_F10 || check_map(k10, State, FALSE, TRUE, FALSE,
							  NULL, NULL) == NULL)
#endif
	pDispatchMessage(&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(void)
{
    MSG	    msg;

    if (!s_busy_processing)
	while (pPeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)
						  && !vim_is_input_buf_full())
	    process_message();
}

    static void
remove_any_timer(void)
{
    MSG		msg;

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

	/* Eat spurious WM_TIMER messages */
	while (pPeekMessage(&msg, s_hwnd, WM_TIMER, WM_TIMER, PM_REMOVE))
	    ;
	s_wait_timer = 0;
    }
}

/*
 * 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(int wtime)
{
    int		focus;

    s_timed_out = FALSE;

    if (wtime > 0)
    {
	/* Don't do anything while processing a (scroll) message. */
	if (s_busy_processing)
	    return FAIL;
	s_wait_timer = (UINT)SetTimer(NULL, 0, (UINT)wtime,
							 (TIMERPROC)_OnTimer);
    }

    allow_scrollbar = TRUE;

    focus = gui.in_focus;
    while (!s_timed_out)
    {
	/* Stop or start blinking when focus changes */
	if (gui.in_focus != focus)
	{
	    if (gui.in_focus)
		gui_mch_start_blink();
	    else
		gui_mch_stop_blink();
	    focus = gui.in_focus;
	}

	if (s_need_activate)
	{
#ifdef WIN32
	    (void)SetForegroundWindow(s_hwnd);
#else
	    (void)SetActiveWindow(s_hwnd);
#endif
	    s_need_activate = FALSE;
	}

#ifdef FEAT_TIMERS
	did_add_timer = FALSE;
#endif
#ifdef MESSAGE_QUEUE
	/* Check channel while waiting message. */
	for (;;)
	{
	    MSG msg;

	    parse_queued_messages();

	    if (pPeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)
		|| MsgWaitForMultipleObjects(0, NULL, FALSE, 100, QS_ALLINPUT)
								!= WAIT_TIMEOUT)
		break;
	}
#endif

	/*
	 * 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 (input_available())
	{
	    remove_any_timer();
	    allow_scrollbar = FALSE;

	    /* Clear pending mouse button, the release event may have been
	     * taken by the dialog window.  But don't do this when getting
	     * focus, we need the mouse-up event then. */
	    if (!s_getting_focus)
		s_button_pending = -1;

	    return OK;
	}

#ifdef FEAT_TIMERS
	if (did_add_timer)
	{
	    /* Need to recompute the waiting time. */
	    remove_any_timer();
	    break;
	}
#endif
    }
    allow_scrollbar = FALSE;
    return FAIL;
}

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

    /*
     * Clear one extra pixel at the far right, for when bold characters have
     * spilled over to the window border.
     * Note: FillRect() excludes right and bottom of rectangle.
     */
    rc.left = FILL_X(col1);
    rc.top = FILL_Y(row1);
    rc.right = FILL_X(col2 + 1) + (col2 == Columns - 1);
    rc.bottom = FILL_Y(row2 + 1);
    clear_rect(&rc);
}

/*
 * Clear the whole text window.
 */
    void
gui_mch_clear_all(void)
{
    RECT    rc;

    rc.left = 0;
    rc.top = 0;
    rc.right = Columns * gui.char_width + 2 * gui.border_width;
    rc.bottom = Rows * gui.char_height + 2 * gui.border_width;
    clear_rect(&rc);
}
/*
 * Menu stuff.
 */

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

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

#if defined(FEAT_MENU) || defined(PROTO)
/*
 * Make menu item hidden or not hidden
 */
    void
gui_mch_menu_hidden(
    vimmenu_T	*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(void)
{
    DrawMenuBar(s_hwnd);
}
#endif /*FEAT_MENU*/

#ifndef PROTO
void
#ifdef VIMDLL
_export
#endif
_cdecl
SaveInst(HINSTANCE hInst)
{
    s_hinst = hInst;
}
#endif

/*
 * Return the RGB value of a pixel as a long.
 */
    guicolor_T
gui_mch_get_rgb(guicolor_T pixel)
{
    return (guicolor_T)((GetRValue(pixel) << 16) + (GetGValue(pixel) << 8)
							   + GetBValue(pixel));
}

#if defined(FEAT_GUI_DIALOG) || defined(PROTO)
/* Convert pixels in X to dialog units */
    static WORD
PixelToDialogX(int numPixels)
{
    return (WORD)((numPixels * 4) / s_dlgfntwidth);
}

/* Convert pixels in Y to dialog units */
    static WORD
PixelToDialogY(int numPixels)
{
    return (WORD)((numPixels * 8) / s_dlgfntheight);
}

/* Return the width in pixels of the given text in the given DC. */
    static int
GetTextWidth(HDC hdc, char_u *str, int len)
{
    SIZE    size;

    GetTextExtentPoint(hdc, (LPCSTR)str, len, &size);
    return size.cx;
}

#ifdef FEAT_MBYTE
/*
 * Return the width in pixels of the given text in the given DC, taking care
 * of 'encoding' to active codepage conversion.
 */
    static int
GetTextWidthEnc(HDC hdc, char_u *str, int len)
{
    SIZE	size;
    WCHAR	*wstr;
    int		n;
    int		wlen = len;

    if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
    {
	/* 'encoding' differs from active codepage: convert text and use wide
	 * function */
	wstr = enc_to_utf16(str, &wlen);
	if (wstr != NULL)
	{
	    n = GetTextExtentPointW(hdc, wstr, wlen, &size);
	    vim_free(wstr);
	    if (n)
		return size.cx;
	}
    }

    return GetTextWidth(hdc, str, len);
}
#else
# define GetTextWidthEnc(h, s, l) GetTextWidth((h), (s), (l))
#endif

/*
 * A quick little routine that will center one window over another, handy for
 * dialog boxes.  Taken from the Win32SDK samples.
 */
    static BOOL
CenterWindow(
    HWND hwndChild,
    HWND hwndParent)
{
    RECT    rChild, rParent;
    int     wChild, hChild, wParent, hParent;
    int     wScreen, hScreen, xNew, yNew;
    HDC     hdc;

    GetWindowRect(hwndChild, &rChild);
    wChild = rChild.right - rChild.left;
    hChild = rChild.bottom - rChild.top;

    /* If Vim is minimized put the window in the middle of the screen. */
    if (hwndParent == NULL || IsMinimized(hwndParent))
	SystemParametersInfo(SPI_GETWORKAREA, 0, &rParent, 0);
    else
	GetWindowRect(hwndParent, &rParent);
    wParent = rParent.right - rParent.left;
    hParent = rParent.bottom - rParent.top;

    hdc = GetDC(hwndChild);
    wScreen = GetDeviceCaps (hdc, HORZRES);
    hScreen = GetDeviceCaps (hdc, VERTRES);
    ReleaseDC(hwndChild, hdc);

    xNew = rParent.left + ((wParent - wChild) /2);
    if (xNew < 0)
    {
	xNew = 0;
    }
    else if ((xNew+wChild) > wScreen)
    {
	xNew = wScreen - wChild;
    }

    yNew = rParent.top	+ ((hParent - hChild) /2);
    if (yNew < 0)
	yNew = 0;
    else if ((yNew+hChild) > hScreen)
	yNew = hScreen - hChild;

    return SetWindowPos(hwndChild, NULL, xNew, yNew, 0, 0,
						   SWP_NOSIZE | SWP_NOZORDER);
}
#endif /* FEAT_GUI_DIALOG */

void
gui_mch_activate_window(void)
{
    (void)SetActiveWindow(s_hwnd);
}

#if defined(FEAT_TOOLBAR) || defined(PROTO)
    void
gui_mch_show_toolbar(int showit)
{
    if (s_toolbarhwnd == NULL)
	return;

    if (showit)
    {
# ifdef FEAT_MBYTE
#  ifndef TB_SETUNICODEFORMAT
    /* For older compilers.  We assume this never changes. */
#   define TB_SETUNICODEFORMAT 0x2005
#  endif
	/* Enable/disable unicode support */
	int uu = (enc_codepage >= 0 && (int)GetACP() != enc_codepage);
	SendMessage(s_toolbarhwnd, TB_SETUNICODEFORMAT, (WPARAM)uu, (LPARAM)0);
# endif
	ShowWindow(s_toolbarhwnd, SW_SHOW);
    }
    else
	ShowWindow(s_toolbarhwnd, SW_HIDE);
}

/* Then number of bitmaps is fixed.  Exit is missing! */
#define TOOLBAR_BITMAP_COUNT 31

#endif

#if defined(FEAT_GUI_TABLINE) || defined(PROTO)
    static void
add_tabline_popup_menu_entry(HMENU pmenu, UINT item_id, char_u *item_text)
{
#ifdef FEAT_MBYTE
    WCHAR	*wn = NULL;
    int		n;

    if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
    {
	/* 'encoding' differs from active codepage: convert menu name
	 * and use wide function */
	wn = enc_to_utf16(item_text, NULL);
	if (wn != NULL)
	{
	    MENUITEMINFOW	infow;

	    infow.cbSize = sizeof(infow);
	    infow.fMask = MIIM_TYPE | MIIM_ID;
	    infow.wID = item_id;
	    infow.fType = MFT_STRING;
	    infow.dwTypeData = wn;
	    infow.cch = (UINT)wcslen(wn);
	    n = InsertMenuItemW(pmenu, item_id, FALSE, &infow);
	    vim_free(wn);
	    if (n == 0 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
		/* Failed, try using non-wide function. */
		wn = NULL;
	}
    }

    if (wn == NULL)
#endif
    {
	MENUITEMINFO	info;

	info.cbSize = sizeof(info);
	info.fMask = MIIM_TYPE | MIIM_ID;
	info.wID = item_id;
	info.fType = MFT_STRING;
	info.dwTypeData = (LPTSTR)item_text;
	info.cch = (UINT)STRLEN(item_text);
	InsertMenuItem(pmenu, item_id, FALSE, &info);
    }
}

    static void
show_tabline_popup_menu(void)
{
    HMENU	    tab_pmenu;
    long	    rval;
    POINT	    pt;

    /* When ignoring events don't show the menu. */
    if (hold_gui_events
# ifdef FEAT_CMDWIN
	    || cmdwin_type != 0
# endif
       )
	return;

    tab_pmenu = CreatePopupMenu();
    if (tab_pmenu == NULL)
	return;

    if (first_tabpage->tp_next != NULL)
	add_tabline_popup_menu_entry(tab_pmenu,
				TABLINE_MENU_CLOSE, (char_u *)_("Close tab"));
    add_tabline_popup_menu_entry(tab_pmenu,
				TABLINE_MENU_NEW, (char_u *)_("New tab"));
    add_tabline_popup_menu_entry(tab_pmenu,
				TABLINE_MENU_OPEN, (char_u *)_("Open tab..."));

    GetCursorPos(&pt);
    rval = TrackPopupMenuEx(tab_pmenu, TPM_RETURNCMD, pt.x, pt.y, s_tabhwnd,
									NULL);

    DestroyMenu(tab_pmenu);

    /* Add the string cmd into input buffer */
    if (rval > 0)
    {
	TCHITTESTINFO htinfo;
	int idx;

	if (ScreenToClient(s_tabhwnd, &pt) == 0)
	    return;

	htinfo.pt.x = pt.x;
	htinfo.pt.y = pt.y;
	idx = TabCtrl_HitTest(s_tabhwnd, &htinfo);
	if (idx == -1)
	    idx = 0;
	else
	    idx += 1;

	send_tabline_menu_event(idx, (int)rval);
    }
}

/*
 * Show or hide the tabline.
 */
    void
gui_mch_show_tabline(int showit)
{
    if (s_tabhwnd == NULL)
	return;

    if (!showit != !showing_tabline)
    {
	if (showit)
	    ShowWindow(s_tabhwnd, SW_SHOW);
	else
	    ShowWindow(s_tabhwnd, SW_HIDE);
	showing_tabline = showit;
    }
}

/*
 * Return TRUE when tabline is displayed.
 */
    int
gui_mch_showing_tabline(void)
{
    return s_tabhwnd != NULL && showing_tabline;
}

/*
 * Update the labels of the tabline.
 */
    void
gui_mch_update_tabline(void)
{
    tabpage_T	*tp;
    TCITEM	tie;
    int		nr = 0;
    int		curtabidx = 0;
    int		tabadded = 0;
#ifdef FEAT_MBYTE
    static int	use_unicode = FALSE;
    int		uu;
    WCHAR	*wstr = NULL;
#endif

    if (s_tabhwnd == NULL)
	return;

#if defined(FEAT_MBYTE)
# ifndef CCM_SETUNICODEFORMAT
    /* For older compilers.  We assume this never changes. */
#  define CCM_SETUNICODEFORMAT 0x2005
# endif
    uu = (enc_codepage >= 0 && (int)GetACP() != enc_codepage);
    if (uu != use_unicode)
    {
	/* Enable/disable unicode support */
	SendMessage(s_tabhwnd, CCM_SETUNICODEFORMAT, (WPARAM)uu, (LPARAM)0);
	use_unicode = uu;
    }
#endif

    tie.mask = TCIF_TEXT;
    tie.iImage = -1;

    /* Disable redraw for tab updates to eliminate O(N^2) draws. */
    SendMessage(s_tabhwnd, WM_SETREDRAW, (WPARAM)FALSE, 0);

    /* Add a label for each tab page.  They all contain the same text area. */
    for (tp = first_tabpage; tp != NULL; tp = tp->tp_next, ++nr)
    {
	if (tp == curtab)
	    curtabidx = nr;

	if (nr >= TabCtrl_GetItemCount(s_tabhwnd))
	{
	    /* Add the tab */
	    tie.pszText = "-Empty-";
	    TabCtrl_InsertItem(s_tabhwnd, nr, &tie);
	    tabadded = 1;
	}

	get_tabline_label(tp, FALSE);
	tie.pszText = (LPSTR)NameBuff;
#ifdef FEAT_MBYTE
	wstr = NULL;
	if (use_unicode)
	{
	    /* Need to go through Unicode. */
	    wstr = enc_to_utf16(NameBuff, NULL);
	    if (wstr != NULL)
	    {
		TCITEMW		tiw;

		tiw.mask = TCIF_TEXT;
		tiw.iImage = -1;
		tiw.pszText = wstr;
		SendMessage(s_tabhwnd, TCM_SETITEMW, (WPARAM)nr, (LPARAM)&tiw);
		vim_free(wstr);
	    }
	}
	if (wstr == NULL)
#endif
	{
	    TabCtrl_SetItem(s_tabhwnd, nr, &tie);
	}
    }

    /* Remove any old labels. */
    while (nr < TabCtrl_GetItemCount(s_tabhwnd))
	TabCtrl_DeleteItem(s_tabhwnd, nr);

    if (!tabadded && TabCtrl_GetCurSel(s_tabhwnd) != curtabidx)
	TabCtrl_SetCurSel(s_tabhwnd, curtabidx);

    /* Re-enable redraw and redraw. */
    SendMessage(s_tabhwnd, WM_SETREDRAW, (WPARAM)TRUE, 0);
    RedrawWindow(s_tabhwnd, NULL, NULL,
		    RDW_ERASE | RDW_FRAME | RDW_INVALIDATE | RDW_ALLCHILDREN);

    if (tabadded && TabCtrl_GetCurSel(s_tabhwnd) != curtabidx)
	TabCtrl_SetCurSel(s_tabhwnd, curtabidx);
}

/*
 * Set the current tab to "nr".  First tab is 1.
 */
    void
gui_mch_set_curtab(int nr)
{
    if (s_tabhwnd == NULL)
	return;

    if (TabCtrl_GetCurSel(s_tabhwnd) != nr - 1)
	TabCtrl_SetCurSel(s_tabhwnd, nr - 1);
}

#endif

/*
 * ":simalt" command.
 */
    void
ex_simalt(exarg_T *eap)
{
    char_u *keys = eap->arg;

    PostMessage(s_hwnd, WM_SYSCOMMAND, (WPARAM)SC_KEYMENU, (LPARAM)0);
    while (*keys)
    {
	if (*keys == '~')
	    *keys = ' ';	    /* for showing system menu */
	PostMessage(s_hwnd, WM_CHAR, (WPARAM)*keys, (LPARAM)0);
	keys++;
    }
}

/*
 * Create the find & replace dialogs.
 * You can't have both at once: ":find" when replace is showing, destroys
 * the replace dialog first, and the other way around.
 */
#ifdef MSWIN_FIND_REPLACE
    static void
initialise_findrep(char_u *initial_string)
{
    int		wword = FALSE;
    int		mcase = !p_ic;
    char_u	*entry_text;

    /* Get the search string to use. */
    entry_text = get_find_dialog_text(initial_string, &wword, &mcase);

    s_findrep_struct.hwndOwner = s_hwnd;
    s_findrep_struct.Flags = FR_DOWN;
    if (mcase)
	s_findrep_struct.Flags |= FR_MATCHCASE;
    if (wword)
	s_findrep_struct.Flags |= FR_WHOLEWORD;
    if (entry_text != NULL && *entry_text != NUL)
	vim_strncpy((char_u *)s_findrep_struct.lpstrFindWhat, entry_text,
					   s_findrep_struct.wFindWhatLen - 1);
    vim_free(entry_text);
}
#endif

    static void
set_window_title(HWND hwnd, char *title)
{
#ifdef FEAT_MBYTE
    if (title != NULL && enc_codepage >= 0 && enc_codepage != (int)GetACP())
    {
	WCHAR	*wbuf;
	int	n;

	/* Convert the title from 'encoding' to UTF-16. */
	wbuf = (WCHAR *)enc_to_utf16((char_u *)title, NULL);
	if (wbuf != NULL)
	{
	    n = SetWindowTextW(hwnd, wbuf);
	    vim_free(wbuf);
	    if (n != 0 || GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
		return;
	    /* Retry with non-wide function (for Windows 98). */
	}
    }
#endif
    (void)SetWindowText(hwnd, (LPCSTR)title);
}

    void
gui_mch_find_dialog(exarg_T *eap)
{
#ifdef MSWIN_FIND_REPLACE
    if (s_findrep_msg != 0)
    {
	if (IsWindow(s_findrep_hwnd) && !s_findrep_is_find)
	    DestroyWindow(s_findrep_hwnd);

	if (!IsWindow(s_findrep_hwnd))
	{
	    initialise_findrep(eap->arg);
# if defined(FEAT_MBYTE) && defined(WIN3264)
	    /* If the OS is Windows NT, and 'encoding' differs from active
	     * codepage: convert text and use wide function. */
	    if (os_version.dwPlatformId == VER_PLATFORM_WIN32_NT
		    && enc_codepage >= 0 && (int)GetACP() != enc_codepage)
	    {
		findrep_atow(&s_findrep_struct_w, &s_findrep_struct);
		s_findrep_hwnd = FindTextW(
					(LPFINDREPLACEW) &s_findrep_struct_w);
	    }
	    else
# endif
		s_findrep_hwnd = FindText((LPFINDREPLACE) &s_findrep_struct);
	}

	set_window_title(s_findrep_hwnd,
			       _("Find string (use '\\\\' to find  a '\\')"));
	(void)SetFocus(s_findrep_hwnd);

	s_findrep_is_find = TRUE;
    }
#endif
}


    void
gui_mch_replace_dialog(exarg_T *eap)
{
#ifdef MSWIN_FIND_REPLACE
    if (s_findrep_msg != 0)
    {
	if (IsWindow(s_findrep_hwnd) && s_findrep_is_find)
	    DestroyWindow(s_findrep_hwnd);

	if (!IsWindow(s_findrep_hwnd))
	{
	    initialise_findrep(eap->arg);
# if defined(FEAT_MBYTE) && defined(WIN3264)
	    if (os_version.dwPlatformId == VER_PLATFORM_WIN32_NT
		    && enc_codepage >= 0 && (int)GetACP() != enc_codepage)
	    {
		findrep_atow(&s_findrep_struct_w, &s_findrep_struct);
		s_findrep_hwnd = ReplaceTextW(
					(LPFINDREPLACEW) &s_findrep_struct_w);
	    }
	    else
# endif
		s_findrep_hwnd = ReplaceText(
					   (LPFINDREPLACE) &s_findrep_struct);
	}

	set_window_title(s_findrep_hwnd,
			    _("Find & Replace (use '\\\\' to find  a '\\')"));
	(void)SetFocus(s_findrep_hwnd);

	s_findrep_is_find = FALSE;
    }
#endif
}


/*
 * Set visibility of the pointer.
 */
    void
gui_mch_mousehide(int hide)
{
    if (hide != gui.pointer_hidden)
    {
	ShowCursor(!hide);
	gui.pointer_hidden = hide;
    }
}

#ifdef FEAT_MENU
    static void
gui_mch_show_popupmenu_at(vimmenu_T *menu, int x, int y)
{
    /* Unhide the mouse, we don't get move events here. */
    gui_mch_mousehide(FALSE);

    (void)TrackPopupMenu(
	(HMENU)menu->submenu_id,
	TPM_LEFTALIGN | TPM_LEFTBUTTON,
	x, y,
	(int)0,	    /*reserved param*/
	s_hwnd,
	NULL);
    /*
     * NOTE: The pop-up menu can eat the mouse up event.
     * We deal with this in normal.c.
     */
}
#endif

/*
 * Got a message when the system will go down.
 */
    static void
_OnEndSession(void)
{
    getout_preserve_modified(1);
}

/*
 * Get this message when the user clicks on the cross in the top right corner
 * of a Windows95 window.
 */
/*ARGSUSED*/
    static void
_OnClose(
    HWND hwnd)
{
    gui_shell_closed();
}

/*
 * Get a message when the window is being destroyed.
 */
    static void
_OnDestroy(
    HWND hwnd)
{
    if (!destroying)
	_OnClose(hwnd);
}

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

	out_flush();	    /* make sure all output has been processed */
	(void)BeginPaint(hwnd, &ps);
#if defined(FEAT_DIRECTX)
	if (IS_ENABLE_DIRECTX())
	    DWriteContext_BeginDraw(s_dwc);
#endif

#ifdef FEAT_MBYTE
	/* prevent multi-byte characters from misprinting on an invalid
	 * rectangle */
	if (has_mbyte)
	{
	    RECT rect;

	    GetClientRect(hwnd, &rect);
	    ps.rcPaint.left = rect.left;
	    ps.rcPaint.right = rect.right;
	}
#endif

	if (!IsRectEmpty(&ps.rcPaint))
	{
#if defined(FEAT_DIRECTX)
	    if (IS_ENABLE_DIRECTX())
		DWriteContext_BindDC(s_dwc, s_hdc, &ps.rcPaint);
#endif
	    gui_redraw(ps.rcPaint.left, ps.rcPaint.top,
		    ps.rcPaint.right - ps.rcPaint.left + 1,
		    ps.rcPaint.bottom - ps.rcPaint.top + 1);
	}

#if defined(FEAT_DIRECTX)
	if (IS_ENABLE_DIRECTX())
	    DWriteContext_EndDraw(s_dwc);
#endif
	EndPaint(hwnd, &ps);
    }
}

/*ARGSUSED*/
    static void
_OnSize(
    HWND hwnd,
    UINT state,
    int cx,
    int cy)
{
    if (!IsMinimized(hwnd))
    {
	gui_resize_shell(cx, cy);

#ifdef FEAT_MENU
	/* Menu bar may wrap differently now */
	gui_mswin_get_menu_height(TRUE);
#endif
    }
}

    static void
_OnSetFocus(
    HWND hwnd,
    HWND hwndOldFocus)
{
    gui_focus_change(TRUE);
    s_getting_focus = TRUE;
    (void)MyWindowProc(hwnd, WM_SETFOCUS, (WPARAM)hwndOldFocus, 0);
}

    static void
_OnKillFocus(
    HWND hwnd,
    HWND hwndNewFocus)
{
    gui_focus_change(FALSE);
    s_getting_focus = FALSE;
    (void)MyWindowProc(hwnd, WM_KILLFOCUS, (WPARAM)hwndNewFocus, 0);
}

/*
 * Get a message when the user switches back to vim
 */
    static LRESULT
_OnActivateApp(
    HWND hwnd,
    BOOL fActivate,
    DWORD dwThreadId)
{
    /* we call gui_focus_change() in _OnSetFocus() */
    /* gui_focus_change((int)fActivate); */
    return MyWindowProc(hwnd, WM_ACTIVATEAPP, fActivate, (DWORD)dwThreadId);
}

#if defined(FEAT_WINDOWS) || defined(PROTO)
    void
gui_mch_destroy_scrollbar(scrollbar_T *sb)
{
    DestroyWindow(sb->id);
}
#endif

/*
 * Get current mouse coordinates in text window.
 */
    void
gui_mch_getmouse(int *x, int *y)
{
    RECT rct;
    POINT mp;

    (void)GetWindowRect(s_textArea, &rct);
    (void)GetCursorPos((LPPOINT)&mp);
    *x = (int)(mp.x - rct.left);
    *y = (int)(mp.y - rct.top);
}

/*
 * Move mouse pointer to character at (x, y).
 */
    void
gui_mch_setmouse(int x, int y)
{
    RECT rct;

    (void)GetWindowRect(s_textArea, &rct);
    (void)SetCursorPos(x + gui.border_offset + rct.left,
		       y + gui.border_offset + rct.top);
}

    static void
gui_mswin_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_CXFRAME) +
	   GetSystemMetrics(SM_CXPADDEDBORDER)) * 2;
    base_height = gui_get_base_height()
	+ (GetSystemMetrics(SM_CYFRAME) +
	   GetSystemMetrics(SM_CXPADDEDBORDER)) * 2
	+ GetSystemMetrics(SM_CYCAPTION)
#ifdef FEAT_MENU
	+ gui_mswin_get_menu_height(FALSE)
#endif
	;
    *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;
}

    void
gui_mch_flash(int msec)
{
    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(s_hdc, &rc);
    gui_mch_flush();			/* make sure it's displayed */

    ui_delay((long)msec, TRUE);	/* wait for a few msec */

    InvertRect(s_hdc, &rc);
}

/*
 * Return flags used for scrolling.
 * The SW_INVALIDATE is required when part of the window is covered or
 * off-screen. Refer to MS KB Q75236.
 */
    static int
get_scroll_flags(void)
{
    HWND	hwnd;
    RECT	rcVim, rcOther, rcDest;

    GetWindowRect(s_hwnd, &rcVim);

    /* Check if the window is partly above or below the screen.  We don't care
     * about partly left or right of the screen, it is not relevant when
     * scrolling up or down. */
    if (rcVim.top < 0 || rcVim.bottom > GetSystemMetrics(SM_CYFULLSCREEN))
	return SW_INVALIDATE;

    /* Check if there is an window (partly) on top of us. */
    for (hwnd = s_hwnd; (hwnd = GetWindow(hwnd, GW_HWNDPREV)) != (HWND)0; )
	if (IsWindowVisible(hwnd))
	{
	    GetWindowRect(hwnd, &rcOther);
	    if (IntersectRect(&rcDest, &rcVim, &rcOther))
		return SW_INVALIDATE;
	}
    return 0;
}

/*
 * On some Intel GPUs, the regions drawn just prior to ScrollWindowEx()
 * may not be scrolled out properly.
 * For gVim, when _OnScroll() is repeated, the character at the
 * previous cursor position may be left drawn after scroll.
 * The problem can be avoided by calling GetPixel() to get a pixel in
 * the region before ScrollWindowEx().
 */
    static void
intel_gpu_workaround(void)
{
    GetPixel(s_hdc, FILL_X(gui.col), FILL_Y(gui.row));
}

/*
 * 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(
    int	    row,
    int	    num_lines)
{
    RECT	rc;

    intel_gpu_workaround();

    rc.left = FILL_X(gui.scroll_region_left);
    rc.right = FILL_X(gui.scroll_region_right + 1);
    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, get_scroll_flags());

    UpdateWindow(s_textArea);
    /* This seems to be required to avoid the cursor disappearing when
     * scrolling such that the cursor ends up in the top-left character on
     * the screen...   But why?  (Webb) */
    /* It's probably fixed by disabling drawing the cursor while scrolling. */
    /* gui.cursor_is_valid = FALSE; */

    gui_clear_block(gui.scroll_region_bot - num_lines + 1,
						       gui.scroll_region_left,
	gui.scroll_region_bot, gui.scroll_region_right);
}

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

    intel_gpu_workaround();

    rc.left = FILL_X(gui.scroll_region_left);
    rc.right = FILL_X(gui.scroll_region_right + 1);
    rc.top = FILL_Y(row);
    rc.bottom = FILL_Y(gui.scroll_region_bot + 1);
    /* The SW_INVALIDATE is required when part of the window is covered or
     * off-screen.  How do we avoid it when it's not needed? */
    ScrollWindowEx(s_textArea, 0, num_lines * gui.char_height,
				    &rc, &rc, NULL, NULL, get_scroll_flags());

    UpdateWindow(s_textArea);

    gui_clear_block(row, gui.scroll_region_left,
				row + num_lines - 1, gui.scroll_region_right);
}


/*ARGSUSED*/
    void
gui_mch_exit(int rc)
{
#if defined(FEAT_DIRECTX)
    DWriteContext_Close(s_dwc);
    DWrite_Final();
    s_dwc = NULL;
#endif

    ReleaseDC(s_textArea, s_hdc);
    DeleteObject(s_brush);

#ifdef FEAT_TEAROFF
    /* Unload the tearoff bitmap */
    (void)DeleteObject((HGDIOBJ)s_htearbitmap);
#endif

    /* Destroy our window (if we have one). */
    if (s_hwnd != NULL)
    {
	destroying = TRUE;	/* ignore WM_DESTROY message now */
	DestroyWindow(s_hwnd);
    }

#ifdef GLOBAL_IME
    global_ime_end();
#endif
}

    static char_u *
logfont2name(LOGFONT lf)
{
    char	*p;
    char	*res;
    char	*charset_name;
    char	*quality_name;
    char	*font_name = lf.lfFaceName;

    charset_name = charset_id2name((int)lf.lfCharSet);
#ifdef FEAT_MBYTE
    /* Convert a font name from the current codepage to 'encoding'.
     * TODO: Use Wide APIs (including LOGFONTW) instead of ANSI APIs. */
    if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
    {
	int	len;
	acp_to_enc((char_u *)lf.lfFaceName, (int)strlen(lf.lfFaceName),
						(char_u **)&font_name, &len);
    }
#endif
    quality_name = quality_id2name((int)lf.lfQuality);

    res = (char *)alloc((unsigned)(strlen(font_name) + 20
		    + (charset_name == NULL ? 0 : strlen(charset_name) + 2)));
    if (res != NULL)
    {
	p = res;
	/* make a normal font string out of the lf thing:*/
	sprintf((char *)p, "%s:h%d", font_name, pixels_to_points(
			 lf.lfHeight < 0 ? -lf.lfHeight : lf.lfHeight, TRUE));
	while (*p)
	{
	    if (*p == ' ')
		*p = '_';
	    ++p;
	}
	if (lf.lfItalic)
	    STRCAT(p, ":i");
	if (lf.lfWeight >= FW_BOLD)
	    STRCAT(p, ":b");
	if (lf.lfUnderline)
	    STRCAT(p, ":u");
	if (lf.lfStrikeOut)
	    STRCAT(p, ":s");
	if (charset_name != NULL)
	{
	    STRCAT(p, ":c");
	    STRCAT(p, charset_name);
	}
	if (quality_name != NULL)
	{
	    STRCAT(p, ":q");
	    STRCAT(p, quality_name);
	}
    }

#ifdef FEAT_MBYTE
    if (font_name != lf.lfFaceName)
	vim_free(font_name);
#endif
    return (char_u *)res;
}


#ifdef FEAT_MBYTE_IME
/*
 * Set correct LOGFONT to IME.  Use 'guifontwide' if available, otherwise use
 * 'guifont'
 */
    static void
update_im_font(void)
{
    LOGFONT	lf_wide;

    if (p_guifontwide != NULL && *p_guifontwide != NUL
	    && gui.wide_font != NOFONT
	    && GetObject((HFONT)gui.wide_font, sizeof(lf_wide), &lf_wide))
	norm_logfont = lf_wide;
    else
	norm_logfont = sub_logfont;
    im_set_font(&norm_logfont);
}
#endif

#ifdef FEAT_MBYTE
/*
 * Handler of gui.wide_font (p_guifontwide) changed notification.
 */
    void
gui_mch_wide_font_changed(void)
{
    LOGFONT lf;

# ifdef FEAT_MBYTE_IME
    update_im_font();
# endif

    gui_mch_free_font(gui.wide_ital_font);
    gui.wide_ital_font = NOFONT;
    gui_mch_free_font(gui.wide_bold_font);
    gui.wide_bold_font = NOFONT;
    gui_mch_free_font(gui.wide_boldital_font);
    gui.wide_boldital_font = NOFONT;

    if (gui.wide_font
	&& GetObject((HFONT)gui.wide_font, sizeof(lf), &lf))
    {
	if (!lf.lfItalic)
	{
	    lf.lfItalic = TRUE;
	    gui.wide_ital_font = get_font_handle(&lf);
	    lf.lfItalic = FALSE;
	}
	if (lf.lfWeight < FW_BOLD)
	{
	    lf.lfWeight = FW_BOLD;
	    gui.wide_bold_font = get_font_handle(&lf);
	    if (!lf.lfItalic)
	    {
		lf.lfItalic = TRUE;
		gui.wide_boldital_font = get_font_handle(&lf);
	    }
	}
    }
}
#endif

/*
 * Initialise vim to use the font with the given name.
 * Return FAIL if the font could not be loaded, OK otherwise.
 */
/*ARGSUSED*/
    int
gui_mch_init_font(char_u *font_name, int fontset)
{
    LOGFONT	lf;
    GuiFont	font = NOFONT;
    char_u	*p;

    /* Load the font */
    if (get_logfont(&lf, font_name, NULL, TRUE) == OK)
	font = get_font_handle(&lf);
    if (font == NOFONT)
	return FAIL;

    if (font_name == NULL)
	font_name = (char_u *)lf.lfFaceName;
#if defined(FEAT_MBYTE_IME) || defined(GLOBAL_IME)
    norm_logfont = lf;
    sub_logfont = lf;
#endif
#ifdef FEAT_MBYTE_IME
    update_im_font();
#endif
    gui_mch_free_font(gui.norm_font);
    gui.norm_font = font;
    current_font_height = lf.lfHeight;
    GetFontSize(font);

    p = logfont2name(lf);
    if (p != NULL)
    {
	hl_set_font_name(p);

	/* When setting 'guifont' to "*" replace it with the actual font name.
	 * */
	if (STRCMP(font_name, "*") == 0 && STRCMP(p_guifont, "*") == 0)
	{
	    vim_free(p_guifont);
	    p_guifont = p;
	}
	else
	    vim_free(p);
    }

    gui_mch_free_font(gui.ital_font);
    gui.ital_font = NOFONT;
    gui_mch_free_font(gui.bold_font);
    gui.bold_font = NOFONT;
    gui_mch_free_font(gui.boldital_font);
    gui.boldital_font = NOFONT;

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

#ifndef WPF_RESTORETOMAXIMIZED
# define WPF_RESTORETOMAXIMIZED 2   /* just in case someone doesn't have it */
#endif

/*
 * Return TRUE if the GUI window is maximized, filling the whole screen.
 */
    int
gui_mch_maximized(void)
{
    WINDOWPLACEMENT wp;

    wp.length = sizeof(WINDOWPLACEMENT);
    if (GetWindowPlacement(s_hwnd, &wp))
	return wp.showCmd == SW_SHOWMAXIMIZED
	    || (wp.showCmd == SW_SHOWMINIMIZED
		    && wp.flags == WPF_RESTORETOMAXIMIZED);

    return 0;
}

/*
 * Called when the font changed while the window is maximized.  Compute the
 * new Rows and Columns.  This is like resizing the window.
 */
    void
gui_mch_newfont(void)
{
    RECT	rect;

    GetWindowRect(s_hwnd, &rect);
    if (win_socket_id == 0)
    {
	gui_resize_shell(rect.right - rect.left
	    - (GetSystemMetrics(SM_CXFRAME) +
	       GetSystemMetrics(SM_CXPADDEDBORDER)) * 2,
	    rect.bottom - rect.top
	    - (GetSystemMetrics(SM_CYFRAME) +
	       GetSystemMetrics(SM_CXPADDEDBORDER)) * 2
	    - GetSystemMetrics(SM_CYCAPTION)
#ifdef FEAT_MENU
	    - gui_mswin_get_menu_height(FALSE)
#endif
	);
    }
    else
    {
	/* Inside another window, don't use the frame and border. */
	gui_resize_shell(rect.right - rect.left,
	    rect.bottom - rect.top
#ifdef FEAT_MENU
			- gui_mswin_get_menu_height(FALSE)
#endif
	);
    }
}

/*
 * Set the window title
 */
/*ARGSUSED*/
    void
gui_mch_settitle(
    char_u  *title,
    char_u  *icon)
{
    set_window_title(s_hwnd, (title == NULL ? "VIM" : (char *)title));
}

#if defined(FEAT_MOUSESHAPE) || defined(PROTO)
/* Table for shape IDCs.  Keep in sync with the mshape_names[] table in
 * misc2.c! */
static LPCSTR mshape_idcs[] =
{
    IDC_ARROW,			/* arrow */
    MAKEINTRESOURCE(0),		/* blank */
    IDC_IBEAM,			/* beam */
    IDC_SIZENS,			/* updown */
    IDC_SIZENS,			/* udsizing */
    IDC_SIZEWE,			/* leftright */
    IDC_SIZEWE,			/* lrsizing */
    IDC_WAIT,			/* busy */
#ifdef WIN3264
    IDC_NO,			/* no */
#else
    IDC_ICON,			/* no */
#endif
    IDC_ARROW,			/* crosshair */
    IDC_ARROW,			/* hand1 */
    IDC_ARROW,			/* hand2 */
    IDC_ARROW,			/* pencil */
    IDC_ARROW,			/* question */
    IDC_ARROW,			/* right-arrow */
    IDC_UPARROW,		/* up-arrow */
    IDC_ARROW			/* last one */
};

    void
mch_set_mouse_shape(int shape)
{
    LPCSTR idc;

    if (shape == MSHAPE_HIDE)
	ShowCursor(FALSE);
    else
    {
	if (shape >= MSHAPE_NUMBERED)
	    idc = IDC_ARROW;
	else
	    idc = mshape_idcs[shape];
#ifdef SetClassLongPtr
	SetClassLongPtr(s_textArea, GCLP_HCURSOR, (__int3264)(LONG_PTR)LoadCursor(NULL, idc));
#else
# ifdef WIN32
	SetClassLong(s_textArea, GCL_HCURSOR, (long_u)LoadCursor(NULL, idc));
# else /* Win16 */
	SetClassWord(s_textArea, GCW_HCURSOR, (WORD)LoadCursor(NULL, idc));
# endif
#endif
	if (!p_mh)
	{
	    POINT mp;

	    /* Set the position to make it redrawn with the new shape. */
	    (void)GetCursorPos((LPPOINT)&mp);
	    (void)SetCursorPos(mp.x, mp.y);
	    ShowCursor(TRUE);
	}
    }
}
#endif

#if defined(FEAT_BROWSE) || defined(PROTO)
/*
 * The file browser exists in two versions: with "W" uses wide characters,
 * without "W" the current codepage.  When FEAT_MBYTE is defined and on
 * Windows NT/2000/XP the "W" functions are used.
 */

# if defined(FEAT_MBYTE) && defined(WIN3264)
/*
 * Wide version of convert_filter().
 */
    static WCHAR *
convert_filterW(char_u *s)
{
    char_u *tmp;
    int len;
    WCHAR *res;

    tmp = convert_filter(s);
    if (tmp == NULL)
	return NULL;
    len = (int)STRLEN(s) + 3;
    res = enc_to_utf16(tmp, &len);
    vim_free(tmp);
    return res;
}

/*
 * Wide version of gui_mch_browse().  Keep in sync!
 */
    static char_u *
gui_mch_browseW(
	int saving,
	char_u *title,
	char_u *dflt,
	char_u *ext,
	char_u *initdir,
	char_u *filter)
{
    /* We always use the wide function.  This means enc_to_utf16() must work,
     * otherwise it fails miserably! */
    OPENFILENAMEW	fileStruct;
    WCHAR		fileBuf[MAXPATHL];
    WCHAR		*wp;
    int			i;
    WCHAR		*titlep = NULL;
    WCHAR		*extp = NULL;
    WCHAR		*initdirp = NULL;
    WCHAR		*filterp;
    char_u		*p;

    if (dflt == NULL)
	fileBuf[0] = NUL;
    else
    {
	wp = enc_to_utf16(dflt, NULL);
	if (wp == NULL)
	    fileBuf[0] = NUL;
	else
	{
	    for (i = 0; wp[i] != NUL && i < MAXPATHL - 1; ++i)
		fileBuf[i] = wp[i];
	    fileBuf[i] = NUL;
	    vim_free(wp);
	}
    }

    /* Convert the filter to Windows format. */
    filterp = convert_filterW(filter);

    vim_memset(&fileStruct, 0, sizeof(OPENFILENAMEW));
#ifdef OPENFILENAME_SIZE_VERSION_400W
    /* be compatible with Windows NT 4.0 */
    fileStruct.lStructSize = OPENFILENAME_SIZE_VERSION_400W;
#else
    fileStruct.lStructSize = sizeof(fileStruct);
#endif

    if (title != NULL)
	titlep = enc_to_utf16(title, NULL);
    fileStruct.lpstrTitle = titlep;

    if (ext != NULL)
	extp = enc_to_utf16(ext, NULL);
    fileStruct.lpstrDefExt = extp;

    fileStruct.lpstrFile = fileBuf;
    fileStruct.nMaxFile = MAXPATHL;
    fileStruct.lpstrFilter = filterp;
    fileStruct.hwndOwner = s_hwnd;		/* main Vim window is owner*/
    /* has an initial dir been specified? */
    if (initdir != NULL && *initdir != NUL)
    {
	/* Must have backslashes here, no matter what 'shellslash' says */
	initdirp = enc_to_utf16(initdir, NULL);
	if (initdirp != NULL)
	{
	    for (wp = initdirp; *wp != NUL; ++wp)
		if (*wp == '/')
		    *wp = '\\';
	}
	fileStruct.lpstrInitialDir = initdirp;
    }

    /*
     * TODO: Allow selection of multiple files.  Needs another arg to this
     * function to ask for it, and need to use OFN_ALLOWMULTISELECT below.
     * Also, should we use OFN_FILEMUSTEXIST when opening?  Vim can edit on
     * files that don't exist yet, so I haven't put it in.  What about
     * OFN_PATHMUSTEXIST?
     * Don't use OFN_OVERWRITEPROMPT, Vim has its own ":confirm" dialog.
     */
    fileStruct.Flags = (OFN_NOCHANGEDIR | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY);
#ifdef FEAT_SHORTCUT
    if (curbuf->b_p_bin)
	fileStruct.Flags |= OFN_NODEREFERENCELINKS;
#endif
    if (saving)
    {
	if (!GetSaveFileNameW(&fileStruct))
	    return NULL;
    }
    else
    {
	if (!GetOpenFileNameW(&fileStruct))
	    return NULL;
    }

    vim_free(filterp);
    vim_free(initdirp);
    vim_free(titlep);
    vim_free(extp);

    /* Convert from UCS2 to 'encoding'. */
    p = utf16_to_enc(fileBuf, NULL);
    if (p != NULL)
	/* when out of memory we get garbage for non-ASCII chars */
	STRCPY(fileBuf, p);
    vim_free(p);

    /* Give focus back to main window (when using MDI). */
    SetFocus(s_hwnd);

    /* Shorten the file name if possible */
    return vim_strsave(shorten_fname1((char_u *)fileBuf));
}
# endif /* FEAT_MBYTE */


/*
 * Convert the string s to the proper format for a filter string by replacing
 * the \t and \n delimiters with \0.
 * Returns the converted string in allocated memory.
 *
 * Keep in sync with convert_filterW() above!
 */
    static char_u *
convert_filter(char_u *s)
{
    char_u	*res;
    unsigned	s_len = (unsigned)STRLEN(s);
    unsigned	i;

    res = alloc(s_len + 3);
    if (res != NULL)
    {
	for (i = 0; i < s_len; ++i)
	    if (s[i] == '\t' || s[i] == '\n')
		res[i] = '\0';
	    else
		res[i] = s[i];
	res[s_len] = NUL;
	/* Add two extra NULs to make sure it's properly terminated. */
	res[s_len + 1] = NUL;
	res[s_len + 2] = NUL;
    }
    return res;
}

/*
 * Select a directory.
 */
    char_u *
gui_mch_browsedir(char_u *title, char_u *initdir)
{
    /* We fake this: Use a filter that doesn't select anything and a default
     * file name that won't be used. */
    return gui_mch_browse(0, title, (char_u *)_("Not Used"), NULL,
			      initdir, (char_u *)_("Directory\t*.nothing\n"));
}

/*
 * Pop open a file browser and return the file selected, in allocated memory,
 * or NULL if Cancel is hit.
 *  saving  - TRUE if the file will be saved to, FALSE if it will be opened.
 *  title   - Title message for the file browser dialog.
 *  dflt    - Default name of file.
 *  ext     - Default extension to be added to files without extensions.
 *  initdir - directory in which to open the browser (NULL = current dir)
 *  filter  - Filter for matched files to choose from.
 *
 * Keep in sync with gui_mch_browseW() above!
 */
    char_u *
gui_mch_browse(
	int saving,
	char_u *title,
	char_u *dflt,
	char_u *ext,
	char_u *initdir,
	char_u *filter)
{
    OPENFILENAME	fileStruct;
    char_u		fileBuf[MAXPATHL];
    char_u		*initdirp = NULL;
    char_u		*filterp;
    char_u		*p;

# if defined(FEAT_MBYTE) && defined(WIN3264)
    if (os_version.dwPlatformId == VER_PLATFORM_WIN32_NT)
	return gui_mch_browseW(saving, title, dflt, ext, initdir, filter);
# endif

    if (dflt == NULL)
	fileBuf[0] = NUL;
    else
	vim_strncpy(fileBuf, dflt, MAXPATHL - 1);

    /* Convert the filter to Windows format. */
    filterp = convert_filter(filter);

    vim_memset(&fileStruct, 0, sizeof(OPENFILENAME));
#ifdef OPENFILENAME_SIZE_VERSION_400
    /* be compatible with Windows NT 4.0 */
    fileStruct.lStructSize = OPENFILENAME_SIZE_VERSION_400;
#else
    fileStruct.lStructSize = sizeof(fileStruct);
#endif

    fileStruct.lpstrTitle = (LPSTR)title;
    fileStruct.lpstrDefExt = (LPSTR)ext;

    fileStruct.lpstrFile = (LPSTR)fileBuf;
    fileStruct.nMaxFile = MAXPATHL;
    fileStruct.lpstrFilter = (LPSTR)filterp;
    fileStruct.hwndOwner = s_hwnd;		/* main Vim window is owner*/
    /* has an initial dir been specified? */
    if (initdir != NULL && *initdir != NUL)
    {
	/* Must have backslashes here, no matter what 'shellslash' says */
	initdirp = vim_strsave(initdir);
	if (initdirp != NULL)
	    for (p = initdirp; *p != NUL; ++p)
		if (*p == '/')
		    *p = '\\';
	fileStruct.lpstrInitialDir = (LPSTR)initdirp;
    }

    /*
     * TODO: Allow selection of multiple files.  Needs another arg to this
     * function to ask for it, and need to use OFN_ALLOWMULTISELECT below.
     * Also, should we use OFN_FILEMUSTEXIST when opening?  Vim can edit on
     * files that don't exist yet, so I haven't put it in.  What about
     * OFN_PATHMUSTEXIST?
     * Don't use OFN_OVERWRITEPROMPT, Vim has its own ":confirm" dialog.
     */
    fileStruct.Flags = (OFN_NOCHANGEDIR | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY);
#ifdef FEAT_SHORTCUT
    if (curbuf->b_p_bin)
	fileStruct.Flags |= OFN_NODEREFERENCELINKS;
#endif
    if (saving)
    {
	if (!GetSaveFileName(&fileStruct))
	    return NULL;
    }
    else
    {
	if (!GetOpenFileName(&fileStruct))
	    return NULL;
    }

    vim_free(filterp);
    vim_free(initdirp);

    /* Give focus back to main window (when using MDI). */
    SetFocus(s_hwnd);

    /* Shorten the file name if possible */
    return vim_strsave(shorten_fname1((char_u *)fileBuf));
}
#endif /* FEAT_BROWSE */

/*ARGSUSED*/
    static void
_OnDropFiles(
    HWND hwnd,
    HDROP hDrop)
{
#ifdef FEAT_WINDOWS
#ifdef WIN3264
# define BUFPATHLEN _MAX_PATH
# define DRAGQVAL 0xFFFFFFFF
#else
# define BUFPATHLEN MAXPATHL
# define DRAGQVAL 0xFFFF
#endif
#ifdef FEAT_MBYTE
    WCHAR   wszFile[BUFPATHLEN];
#endif
    char    szFile[BUFPATHLEN];
    UINT    cFiles = DragQueryFile(hDrop, DRAGQVAL, NULL, 0);
    UINT    i;
    char_u  **fnames;
    POINT   pt;
    int_u   modifiers = 0;

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

    /* Obtain dropped position */
    DragQueryPoint(hDrop, &pt);
    MapWindowPoints(s_hwnd, s_textArea, &pt, 1);

    reset_VIsual();

    fnames = (char_u **)alloc(cFiles * sizeof(char_u *));

    if (fnames != NULL)
	for (i = 0; i < cFiles; ++i)
	{
#ifdef FEAT_MBYTE
	    if (DragQueryFileW(hDrop, i, wszFile, BUFPATHLEN) > 0)
		fnames[i] = utf16_to_enc(wszFile, NULL);
	    else
#endif
	    {
		DragQueryFile(hDrop, i, szFile, BUFPATHLEN);
		fnames[i] = vim_strsave((char_u *)szFile);
	    }
	}

    DragFinish(hDrop);

    if (fnames != NULL)
    {
	if ((GetKeyState(VK_SHIFT) & 0x8000) != 0)
	    modifiers |= MOUSE_SHIFT;
	if ((GetKeyState(VK_CONTROL) & 0x8000) != 0)
	    modifiers |= MOUSE_CTRL;
	if ((GetKeyState(VK_MENU) & 0x8000) != 0)
	    modifiers |= MOUSE_ALT;

	gui_handle_drop(pt.x, pt.y, modifiers, fnames, cFiles);

	s_need_activate = TRUE;
    }
#endif
}

/*ARGSUSED*/
    static int
_OnScroll(
    HWND hwnd,
    HWND hwndCtl,
    UINT code,
    int pos)
{
    static UINT	prev_code = 0;   /* code of previous call */
    scrollbar_T *sb, *sb_info;
    long	val;
    int		dragging = FALSE;
    int		dont_scroll_save = dont_scroll;
#ifndef WIN3264
    int		nPos;
#else
    SCROLLINFO	si;

    si.cbSize = sizeof(si);
    si.fMask = SIF_POS;
#endif

    sb = gui_mswin_find_scrollbar(hwndCtl);
    if (sb == NULL)
	return 0;

    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:
	    val = pos;
	    dragging = TRUE;
	    if (sb->scroll_shift > 0)
		val <<= sb->scroll_shift;
	    break;
	case SB_LINEDOWN:
	    val++;
	    break;
	case SB_LINEUP:
	    val--;
	    break;
	case SB_PAGEDOWN:
	    val += (sb_info->size > 2 ? sb_info->size - 2 : 1);
	    break;
	case SB_PAGEUP:
	    val -= (sb_info->size > 2 ? sb_info->size - 2 : 1);
	    break;
	case SB_TOP:
	    val = 0;
	    break;
	case SB_BOTTOM:
	    val = sb_info->max;
	    break;
	case SB_ENDSCROLL:
	    if (prev_code == SB_THUMBTRACK)
	    {
		/*
		 * "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.
		 */
		val = GetScrollPos(hwndCtl, SB_CTL);
		if (sb->scroll_shift > 0)
		    val <<= sb->scroll_shift;
	    }
	    break;

	default:
	    /* TRACE("Unknown scrollbar event %d\n", code); */
	    return 0;
    }
    prev_code = code;

#ifdef WIN3264
    si.nPos = (sb->scroll_shift > 0) ? val >> sb->scroll_shift : val;
    SetScrollInfo(hwndCtl, SB_CTL, &si, TRUE);
#else
    nPos = (sb->scroll_shift > 0) ? val >> sb->scroll_shift : val;
    SetScrollPos(hwndCtl, SB_CTL, nPos, TRUE);
#endif

    /*
     * When moving a vertical scrollbar, move the other vertical scrollbar too.
     */
    if (sb->wp != NULL)
    {
	scrollbar_T *sba = sb->wp->w_scrollbars;
	HWND    id = sba[ (sb == sba + SBAR_LEFT) ? SBAR_RIGHT : SBAR_LEFT].id;

#ifdef WIN3264
	SetScrollInfo(id, SB_CTL, &si, TRUE);
#else
	SetScrollPos(id, SB_CTL, nPos, TRUE);
#endif
    }

    /* Don't let us be interrupted here by another message. */
    s_busy_processing = TRUE;

    /* When "allow_scrollbar" is FALSE still need to remember the new
     * position, but don't actually scroll by setting "dont_scroll". */
    dont_scroll = !allow_scrollbar;

    gui_drag_scrollbar(sb, val, dragging);

    s_busy_processing = FALSE;
    dont_scroll = dont_scroll_save;

    return 0;
}


/*
 * Get command line arguments.
 * Use "prog" as the name of the program and "cmdline" as the arguments.
 * Copy the arguments to allocated memory.
 * Return the number of arguments (including program name).
 * Return pointers to the arguments in "argvp".  Memory is allocated with
 * malloc(), use free() instead of vim_free().
 * Return pointer to buffer in "tofree".
 * Returns zero when out of memory.
 */
/*ARGSUSED*/
    int
get_cmd_args(char *prog, char *cmdline, char ***argvp, char **tofree)
{
    int		i;
    char	*p;
    char	*progp;
    char	*pnew = NULL;
    char	*newcmdline;
    int		inquote;
    int		argc;
    char	**argv = NULL;
    int		round;

    *tofree = NULL;

#ifdef FEAT_MBYTE
    /* Try using the Unicode version first, it takes care of conversion when
     * 'encoding' is changed. */
    argc = get_cmd_argsW(&argv);
    if (argc != 0)
	goto done;
#endif

    /* Handle the program name.  Remove the ".exe" extension, and find the 1st
     * non-space. */
    p = strrchr(prog, '.');
    if (p != NULL)
	*p = NUL;
    for (progp = prog; *progp == ' '; ++progp)
	;

    /* The command line is copied to allocated memory, so that we can change
     * it.  Add the size of the string, the separating NUL and a terminating
     * NUL. */
    newcmdline = malloc(STRLEN(cmdline) + STRLEN(progp) + 2);
    if (newcmdline == NULL)
	return 0;

    /*
     * First round: count the number of arguments ("pnew" == NULL).
     * Second round: produce the arguments.
     */
    for (round = 1; round <= 2; ++round)
    {
	/* First argument is the program name. */
	if (pnew != NULL)
	{
	    argv[0] = pnew;
	    strcpy(pnew, progp);
	    pnew += strlen(pnew);
	    *pnew++ = NUL;
	}

	/*
	 * Isolate each argument and put it in argv[].
	 */
	p = cmdline;
	argc = 1;
	while (*p != NUL)
	{
	    inquote = FALSE;
	    if (pnew != NULL)
		argv[argc] = pnew;
	    ++argc;
	    while (*p != NUL && (inquote || (*p != ' ' && *p != '\t')))
	    {
		/* Backslashes are only special when followed by a double
		 * quote. */
		i = (int)strspn(p, "\\");
		if (p[i] == '"')
		{
		    /* Halve the number of backslashes. */
		    if (i > 1 && pnew != NULL)
		    {
			vim_memset(pnew, '\\', i / 2);
			pnew += i / 2;
		    }

		    /* Even nr of backslashes toggles quoting, uneven copies
		     * the double quote. */
		    if ((i & 1) == 0)
			inquote = !inquote;
		    else if (pnew != NULL)
			*pnew++ = '"';
		    p += i + 1;
		}
		else if (i > 0)
		{
		    /* Copy span of backslashes unmodified. */
		    if (pnew != NULL)
		    {
			vim_memset(pnew, '\\', i);
			pnew += i;
		    }
		    p += i;
		}
		else
		{
		    if (pnew != NULL)
			*pnew++ = *p;
#ifdef FEAT_MBYTE
		    /* Can't use mb_* functions, because 'encoding' is not
		     * initialized yet here. */
		    if (IsDBCSLeadByte(*p))
		    {
			++p;
			if (pnew != NULL)
			    *pnew++ = *p;
		    }
#endif
		    ++p;
		}
	    }

	    if (pnew != NULL)
		*pnew++ = NUL;
	    while (*p == ' ' || *p == '\t')
		++p;		    /* advance until a non-space */
	}

	if (round == 1)
	{
	    argv = (char **)malloc((argc + 1) * sizeof(char *));
	    if (argv == NULL )
	    {
		free(newcmdline);
		return 0;		   /* malloc error */
	    }
	    pnew = newcmdline;
	    *tofree = newcmdline;
	}
    }

#ifdef FEAT_MBYTE
done:
#endif
    argv[argc] = NULL;		/* NULL-terminated list */
    *argvp = argv;
    return argc;
}

#ifdef FEAT_XPM_W32
# include "xpm_w32.h"
#endif

#ifdef PROTO
# define WINAPI
#endif

#ifdef __MINGW32__
/*
 * Add a lot of missing defines.
 * They are not always missing, we need the #ifndef's.
 */
# ifndef _cdecl
#  define _cdecl
# endif
# ifndef IsMinimized
#  define     IsMinimized(hwnd)		IsIconic(hwnd)
# endif
# ifndef IsMaximized
#  define     IsMaximized(hwnd)		IsZoomed(hwnd)
# endif
# ifndef SelectFont
#  define     SelectFont(hdc, hfont)  ((HFONT)SelectObject((hdc), (HGDIOBJ)(HFONT)(hfont)))
# endif
# ifndef GetStockBrush
#  define     GetStockBrush(i)     ((HBRUSH)GetStockObject(i))
# endif
# ifndef DeleteBrush
#  define     DeleteBrush(hbr)     DeleteObject((HGDIOBJ)(HBRUSH)(hbr))
# endif

# ifndef HANDLE_WM_RBUTTONDBLCLK
#  define HANDLE_WM_RBUTTONDBLCLK(hwnd, wParam, lParam, fn) \
    ((fn)((hwnd), TRUE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
# endif
# ifndef HANDLE_WM_MBUTTONUP
#  define HANDLE_WM_MBUTTONUP(hwnd, wParam, lParam, fn) \
    ((fn)((hwnd), (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
# endif
# ifndef HANDLE_WM_MBUTTONDBLCLK
#  define HANDLE_WM_MBUTTONDBLCLK(hwnd, wParam, lParam, fn) \
    ((fn)((hwnd), TRUE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
# endif
# ifndef HANDLE_WM_LBUTTONDBLCLK
#  define HANDLE_WM_LBUTTONDBLCLK(hwnd, wParam, lParam, fn) \
    ((fn)((hwnd), TRUE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
# endif
# ifndef HANDLE_WM_RBUTTONDOWN
#  define HANDLE_WM_RBUTTONDOWN(hwnd, wParam, lParam, fn) \
    ((fn)((hwnd), FALSE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
# endif
# ifndef HANDLE_WM_MOUSEMOVE
#  define HANDLE_WM_MOUSEMOVE(hwnd, wParam, lParam, fn) \
    ((fn)((hwnd), (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
# endif
# ifndef HANDLE_WM_RBUTTONUP
#  define HANDLE_WM_RBUTTONUP(hwnd, wParam, lParam, fn) \
    ((fn)((hwnd), (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
# endif
# ifndef HANDLE_WM_MBUTTONDOWN
#  define HANDLE_WM_MBUTTONDOWN(hwnd, wParam, lParam, fn) \
    ((fn)((hwnd), FALSE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
# endif
# ifndef HANDLE_WM_LBUTTONUP
#  define HANDLE_WM_LBUTTONUP(hwnd, wParam, lParam, fn) \
    ((fn)((hwnd), (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
# endif
# ifndef HANDLE_WM_LBUTTONDOWN
#  define HANDLE_WM_LBUTTONDOWN(hwnd, wParam, lParam, fn) \
    ((fn)((hwnd), FALSE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
# endif
# ifndef HANDLE_WM_SYSCHAR
#  define HANDLE_WM_SYSCHAR(hwnd, wParam, lParam, fn) \
    ((fn)((hwnd), (TCHAR)(wParam), (int)(short)LOWORD(lParam)), 0L)
# endif
# ifndef HANDLE_WM_ACTIVATEAPP
#  define HANDLE_WM_ACTIVATEAPP(hwnd, wParam, lParam, fn) \
    ((fn)((hwnd), (BOOL)(wParam), (DWORD)(lParam)), 0L)
# endif
# ifndef HANDLE_WM_WINDOWPOSCHANGING
#  define HANDLE_WM_WINDOWPOSCHANGING(hwnd, wParam, lParam, fn) \
    (LRESULT)(DWORD)(BOOL)(fn)((hwnd), (LPWINDOWPOS)(lParam))
# endif
# ifndef HANDLE_WM_VSCROLL
#  define HANDLE_WM_VSCROLL(hwnd, wParam, lParam, fn) \
    ((fn)((hwnd), (HWND)(lParam), (UINT)(LOWORD(wParam)),  (int)(short)HIWORD(wParam)), 0L)
# endif
# ifndef HANDLE_WM_SETFOCUS
#  define HANDLE_WM_SETFOCUS(hwnd, wParam, lParam, fn) \
    ((fn)((hwnd), (HWND)(wParam)), 0L)
# endif
# ifndef HANDLE_WM_KILLFOCUS
#  define HANDLE_WM_KILLFOCUS(hwnd, wParam, lParam, fn) \
    ((fn)((hwnd), (HWND)(wParam)), 0L)
# endif
# ifndef HANDLE_WM_HSCROLL
#  define HANDLE_WM_HSCROLL(hwnd, wParam, lParam, fn) \
    ((fn)((hwnd), (HWND)(lParam), (UINT)(LOWORD(wParam)), (int)(short)HIWORD(wParam)), 0L)
# endif
# ifndef HANDLE_WM_DROPFILES
#  define HANDLE_WM_DROPFILES(hwnd, wParam, lParam, fn) \
    ((fn)((hwnd), (HDROP)(wParam)), 0L)
# endif
# ifndef HANDLE_WM_CHAR
#  define HANDLE_WM_CHAR(hwnd, wParam, lParam, fn) \
    ((fn)((hwnd), (TCHAR)(wParam), (int)(short)LOWORD(lParam)), 0L)
# endif
# ifndef HANDLE_WM_SYSDEADCHAR
#  define HANDLE_WM_SYSDEADCHAR(hwnd, wParam, lParam, fn) \
    ((fn)((hwnd), (TCHAR)(wParam), (int)(short)LOWORD(lParam)), 0L)
# endif
# ifndef HANDLE_WM_DEADCHAR
#  define HANDLE_WM_DEADCHAR(hwnd, wParam, lParam, fn) \
    ((fn)((hwnd), (TCHAR)(wParam), (int)(short)LOWORD(lParam)), 0L)
# endif
#endif /* __MINGW32__ */


/* Some parameters for tearoff menus.  All in pixels. */
#define TEAROFF_PADDING_X	2
#define TEAROFF_BUTTON_PAD_X	8
#define TEAROFF_MIN_WIDTH	200
#define TEAROFF_SUBMENU_LABEL	">>"
#define TEAROFF_COLUMN_PADDING	3	// # spaces to pad column with.


/* For the Intellimouse: */
#ifndef WM_MOUSEWHEEL
#define WM_MOUSEWHEEL	0x20a
#endif


#ifdef FEAT_BEVAL
# define ID_BEVAL_TOOLTIP   200
# define BEVAL_TEXT_LEN	    MAXPATHL

#if (defined(_MSC_VER) && _MSC_VER < 1300) || !defined(MAXULONG_PTR)
/* Work around old versions of basetsd.h which wrongly declares
 * UINT_PTR as unsigned long. */
# undef  UINT_PTR
# define UINT_PTR UINT
#endif

static void make_tooltip(BalloonEval *beval, char *text, POINT pt);
static void delete_tooltip(BalloonEval *beval);
static VOID CALLBACK BevalTimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime);

static BalloonEval  *cur_beval = NULL;
static UINT_PTR	    BevalTimerId = 0;
static DWORD	    LastActivity = 0;


/* cproto fails on missing include files */
#ifndef PROTO

/*
 * excerpts from headers since this may not be presented
 * in the extremely old compilers
 */
# include <pshpack1.h>

#endif

typedef struct _DllVersionInfo
{
    DWORD cbSize;
    DWORD dwMajorVersion;
    DWORD dwMinorVersion;
    DWORD dwBuildNumber;
    DWORD dwPlatformID;
} DLLVERSIONINFO;

#ifndef PROTO
# include <poppack.h>
#endif

typedef struct tagTOOLINFOA_NEW
{
	UINT cbSize;
	UINT uFlags;
	HWND hwnd;
	UINT_PTR uId;
	RECT rect;
	HINSTANCE hinst;
	LPSTR lpszText;
	LPARAM lParam;
} TOOLINFO_NEW;

typedef struct tagNMTTDISPINFO_NEW
{
    NMHDR      hdr;
    LPSTR      lpszText;
    char       szText[80];
    HINSTANCE  hinst;
    UINT       uFlags;
    LPARAM     lParam;
} NMTTDISPINFO_NEW;

typedef HRESULT (WINAPI* DLLGETVERSIONPROC)(DLLVERSIONINFO *);
#ifndef TTM_SETMAXTIPWIDTH
# define TTM_SETMAXTIPWIDTH	(WM_USER+24)
#endif

#ifndef TTF_DI_SETITEM
# define TTF_DI_SETITEM		0x8000
#endif

#ifndef TTN_GETDISPINFO
# define TTN_GETDISPINFO	(TTN_FIRST - 0)
#endif

#endif /* defined(FEAT_BEVAL) */

#if defined(FEAT_TOOLBAR) || defined(FEAT_GUI_TABLINE)
/* Older MSVC compilers don't have LPNMTTDISPINFO[AW] thus we need to define
 * it here if LPNMTTDISPINFO isn't defined.
 * MingW doesn't define LPNMTTDISPINFO but typedefs it.  Thus we need to check
 * _MSC_VER. */
# if !defined(LPNMTTDISPINFO) && defined(_MSC_VER)
typedef struct tagNMTTDISPINFOA {
    NMHDR	hdr;
    LPSTR	lpszText;
    char	szText[80];
    HINSTANCE	hinst;
    UINT	uFlags;
    LPARAM	lParam;
} NMTTDISPINFOA, *LPNMTTDISPINFOA;
#  define LPNMTTDISPINFO LPNMTTDISPINFOA

#  ifdef FEAT_MBYTE
typedef struct tagNMTTDISPINFOW {
    NMHDR	hdr;
    LPWSTR	lpszText;
    WCHAR	szText[80];
    HINSTANCE	hinst;
    UINT	uFlags;
    LPARAM	lParam;
} NMTTDISPINFOW, *LPNMTTDISPINFOW;
#  endif
# endif
#endif

#ifndef TTN_GETDISPINFOW
# define TTN_GETDISPINFOW	(TTN_FIRST - 10)
#endif

/* Local variables: */

#ifdef FEAT_MENU
static UINT	s_menu_id = 100;
#endif

/*
 * Use the system font for dialogs and tear-off menus.  Remove this line to
 * use DLG_FONT_NAME.
 */
#define USE_SYSMENU_FONT

#define VIM_NAME	"vim"
#define VIM_CLASS	"Vim"
#define VIM_CLASSW	L"Vim"

/* Initial size for the dialog template.  For gui_mch_dialog() it's fixed,
 * thus there should be room for every dialog.  For tearoffs it's made bigger
 * when needed. */
#define DLG_ALLOC_SIZE 16 * 1024

/*
 * stuff for dialogs, menus, tearoffs etc.
 */
static LRESULT APIENTRY dialog_callback(HWND, UINT, WPARAM, LPARAM);
#ifdef FEAT_TEAROFF
static LRESULT APIENTRY tearoff_callback(HWND, UINT, WPARAM, LPARAM);
#endif
static PWORD
add_dialog_element(
	PWORD p,
	DWORD lStyle,
	WORD x,
	WORD y,
	WORD w,
	WORD h,
	WORD Id,
	WORD clss,
	const char *caption);
static LPWORD lpwAlign(LPWORD);
static int nCopyAnsiToWideChar(LPWORD, LPSTR);
#if defined(FEAT_MENU) && defined(FEAT_TEAROFF)
static void gui_mch_tearoff(char_u *title, vimmenu_T *menu, int initX, int initY);
#endif
static void get_dialog_font_metrics(void);

static int dialog_default_button = -1;

/* Intellimouse support */
static int mouse_scroll_lines = 0;
static UINT msh_msgmousewheel = 0;

static int	s_usenewlook;	    /* emulate W95/NT4 non-bold dialogs */
#ifdef FEAT_TOOLBAR
static void initialise_toolbar(void);
static LRESULT CALLBACK toolbar_wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
static int get_toolbar_bitmap(vimmenu_T *menu);
#endif

#ifdef FEAT_GUI_TABLINE
static void initialise_tabline(void);
static LRESULT CALLBACK tabline_wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
#endif

#ifdef FEAT_MBYTE_IME
static LRESULT _OnImeComposition(HWND hwnd, WPARAM dbcs, LPARAM param);
static char_u *GetResultStr(HWND hwnd, int GCS, int *lenp);
#endif
#if defined(FEAT_MBYTE_IME) && defined(DYNAMIC_IME)
# ifdef NOIME
typedef struct tagCOMPOSITIONFORM {
    DWORD dwStyle;
    POINT ptCurrentPos;
    RECT  rcArea;
} COMPOSITIONFORM, *PCOMPOSITIONFORM, NEAR *NPCOMPOSITIONFORM, FAR *LPCOMPOSITIONFORM;
typedef HANDLE HIMC;
# endif

static HINSTANCE hLibImm = NULL;
static LONG (WINAPI *pImmGetCompositionStringA)(HIMC, DWORD, LPVOID, DWORD);
static LONG (WINAPI *pImmGetCompositionStringW)(HIMC, DWORD, LPVOID, DWORD);
static HIMC (WINAPI *pImmGetContext)(HWND);
static HIMC (WINAPI *pImmAssociateContext)(HWND, HIMC);
static BOOL (WINAPI *pImmReleaseContext)(HWND, HIMC);
static BOOL (WINAPI *pImmGetOpenStatus)(HIMC);
static BOOL (WINAPI *pImmSetOpenStatus)(HIMC, BOOL);
static BOOL (WINAPI *pImmGetCompositionFont)(HIMC, LPLOGFONTA);
static BOOL (WINAPI *pImmSetCompositionFont)(HIMC, LPLOGFONTA);
static BOOL (WINAPI *pImmSetCompositionWindow)(HIMC, LPCOMPOSITIONFORM);
static BOOL (WINAPI *pImmGetConversionStatus)(HIMC, LPDWORD, LPDWORD);
static BOOL (WINAPI *pImmSetConversionStatus)(HIMC, DWORD, DWORD);
static void dyn_imm_load(void);
#else
# define pImmGetCompositionStringA ImmGetCompositionStringA
# define pImmGetCompositionStringW ImmGetCompositionStringW
# define pImmGetContext		  ImmGetContext
# define pImmAssociateContext	  ImmAssociateContext
# define pImmReleaseContext	  ImmReleaseContext
# define pImmGetOpenStatus	  ImmGetOpenStatus
# define pImmSetOpenStatus	  ImmSetOpenStatus
# define pImmGetCompositionFont   ImmGetCompositionFontA
# define pImmSetCompositionFont   ImmSetCompositionFontA
# define pImmSetCompositionWindow ImmSetCompositionWindow
# define pImmGetConversionStatus  ImmGetConversionStatus
# define pImmSetConversionStatus  ImmSetConversionStatus
#endif

/* multi monitor support */
typedef struct _MONITORINFOstruct
{
    DWORD cbSize;
    RECT rcMonitor;
    RECT rcWork;
    DWORD dwFlags;
} _MONITORINFO;

typedef HANDLE _HMONITOR;
typedef _HMONITOR (WINAPI *TMonitorFromWindow)(HWND, DWORD);
typedef BOOL (WINAPI *TGetMonitorInfo)(_HMONITOR, _MONITORINFO *);

static TMonitorFromWindow   pMonitorFromWindow = NULL;
static TGetMonitorInfo	    pGetMonitorInfo = NULL;
static HANDLE		    user32_lib = NULL;
/*
 * Return TRUE when running under Windows NT 3.x or Win32s, both of which have
 * less fancy GUI APIs.
 */
    static int
is_winnt_3(void)
{
    return ((os_version.dwPlatformId == VER_PLATFORM_WIN32_NT
		&& os_version.dwMajorVersion == 3)
	    || (os_version.dwPlatformId == VER_PLATFORM_WIN32s));
}

#ifdef FEAT_MENU
/*
 * Figure out how high the menu bar is at the moment.
 */
    static int
gui_mswin_get_menu_height(
    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;

    if (gui.menu_is_active)
	num = GetMenuItemCount(s_menuBar);
    else
	num = 0;

    if (num == 0)
	menu_height = 0;
    else if (IsMinimized(s_hwnd))
    {
	/* The height of the menu cannot be determined while the window is
	 * minimized.  Take the previous height if the menu is changed in that
	 * state, to avoid that Vim's vertical window size accidentally
	 * increases due to the unaccounted-for menu height. */
	menu_height = old_menu_height == -1 ? 0 : old_menu_height;
    }
    else
    {
	if (is_winnt_3())	/* for NT 3.xx */
	{
	    if (gui.starting)
		menu_height = GetSystemMetrics(SM_CYMENU);
	    else
	    {
		RECT r1, r2;
		int frameht = GetSystemMetrics(SM_CYFRAME);
		int capht = GetSystemMetrics(SM_CYCAPTION);

		/* get window rect of s_hwnd
		 * get client rect of s_hwnd
		 * get cap height
		 * subtract from window rect, the sum of client height,
		 * (if not maximized)frame thickness, and caption height.
		 */
		GetWindowRect(s_hwnd, &r1);
		GetClientRect(s_hwnd, &r2);
		menu_height = r1.bottom - r1.top - (r2.bottom - r2.top
				 + 2 * frameht * (!IsZoomed(s_hwnd)) + capht);
	    }
	}
	else			/* win95 and variants (NT 4.0, I guess) */
	{
	    /*
	     * In case 'lines' is set in _vimrc/_gvimrc window width doesn't
	     * seem to have been set yet, so menu wraps in default window
	     * width which is very narrow.  Instead just return height of a
	     * single menu item.  Will still be wrong when the menu really
	     * should wrap over more than one line.
	     */
	    GetMenuItemRect(s_hwnd, s_menuBar, 0, &rc1);
	    if (gui.starting)
		menu_height = rc1.bottom - rc1.top + 1;
	    else
	    {
		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_shellsize(FALSE, FALSE, RESIZE_VERT);
    }
    old_menu_height = menu_height;

    return menu_height;
}
#endif /*FEAT_MENU*/


/*
 * Setup for the Intellimouse
 */
    static void
init_mouse_wheel(void)
{

#ifndef SPI_GETWHEELSCROLLLINES
# define SPI_GETWHEELSCROLLLINES    104
#endif
#ifndef SPI_SETWHEELSCROLLLINES
# define SPI_SETWHEELSCROLLLINES    105
#endif

#define VMOUSEZ_CLASSNAME  "MouseZ"		/* hidden wheel window class */
#define VMOUSEZ_TITLE      "Magellan MSWHEEL"	/* hidden wheel window title */
#define VMSH_MOUSEWHEEL    "MSWHEEL_ROLLMSG"
#define VMSH_SCROLL_LINES  "MSH_SCROLL_LINES_MSG"

    HWND hdl_mswheel;
    UINT msh_msgscrolllines;

    msh_msgmousewheel = 0;
    mouse_scroll_lines = 3;	/* reasonable default */

    if ((os_version.dwPlatformId == VER_PLATFORM_WIN32_NT
		&& os_version.dwMajorVersion >= 4)
	    || (os_version.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS
		&& ((os_version.dwMajorVersion == 4
			&& os_version.dwMinorVersion >= 10)
		    || os_version.dwMajorVersion >= 5)))
    {
	/* if NT 4.0+ (or Win98) get scroll lines directly from system */
	SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0,
		&mouse_scroll_lines, 0);
    }
    else if (os_version.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS
	    || (os_version.dwPlatformId == VER_PLATFORM_WIN32_NT
		&& os_version.dwMajorVersion < 4))
    {	/*
	 * If Win95 or NT 3.51,
	 * try to find the hidden point32 window.
	 */
	hdl_mswheel = FindWindow(VMOUSEZ_CLASSNAME, VMOUSEZ_TITLE);
	if (hdl_mswheel)
	{
	    msh_msgscrolllines = RegisterWindowMessage(VMSH_SCROLL_LINES);
	    if (msh_msgscrolllines)
	    {
		mouse_scroll_lines = (int)SendMessage(hdl_mswheel,
			msh_msgscrolllines, 0, 0);
		msh_msgmousewheel  = RegisterWindowMessage(VMSH_MOUSEWHEEL);
	    }
	}
    }
}


/* Intellimouse wheel handler */
    static void
_OnMouseWheel(
    HWND hwnd,
    short zDelta)
{
/* Treat a mouse wheel event as if it were a scroll request */
    int i;
    int size;
    HWND hwndCtl;

    if (curwin->w_scrollbars[SBAR_RIGHT].id != 0)
    {
	hwndCtl = curwin->w_scrollbars[SBAR_RIGHT].id;
	size = curwin->w_scrollbars[SBAR_RIGHT].size;
    }
    else if (curwin->w_scrollbars[SBAR_LEFT].id != 0)
    {
	hwndCtl = curwin->w_scrollbars[SBAR_LEFT].id;
	size = curwin->w_scrollbars[SBAR_LEFT].size;
    }
    else
	return;

    size = curwin->w_height;
    if (mouse_scroll_lines == 0)
	init_mouse_wheel();

    if (mouse_scroll_lines > 0
	    && mouse_scroll_lines < (size > 2 ? size - 2 : 1))
    {
	for (i = mouse_scroll_lines; i > 0; --i)
	    _OnScroll(hwnd, hwndCtl, zDelta >= 0 ? SB_LINEUP : SB_LINEDOWN, 0);
    }
    else
	_OnScroll(hwnd, hwndCtl, zDelta >= 0 ? SB_PAGEUP : SB_PAGEDOWN, 0);
}

#ifdef USE_SYSMENU_FONT
/*
 * Get Menu Font.
 * Return OK or FAIL.
 */
    static int
gui_w32_get_menu_font(LOGFONT *lf)
{
    NONCLIENTMETRICS nm;

    nm.cbSize = sizeof(NONCLIENTMETRICS);
    if (!SystemParametersInfo(
	    SPI_GETNONCLIENTMETRICS,
	    sizeof(NONCLIENTMETRICS),
	    &nm,
	    0))
	return FAIL;
    *lf = nm.lfMenuFont;
    return OK;
}
#endif


#if defined(FEAT_GUI_TABLINE) && defined(USE_SYSMENU_FONT)
/*
 * Set the GUI tabline font to the system menu font
 */
    static void
set_tabline_font(void)
{
    LOGFONT	lfSysmenu;
    HFONT	font;
    HWND	hwnd;
    HDC		hdc;
    HFONT	hfntOld;
    TEXTMETRIC	tm;

    if (gui_w32_get_menu_font(&lfSysmenu) != OK)
	return;

    font = CreateFontIndirect(&lfSysmenu);

    SendMessage(s_tabhwnd, WM_SETFONT, (WPARAM)font, TRUE);

    /*
     * Compute the height of the font used for the tab text
     */
    hwnd = GetDesktopWindow();
    hdc = GetWindowDC(hwnd);
    hfntOld = SelectFont(hdc, font);

    GetTextMetrics(hdc, &tm);

    SelectFont(hdc, hfntOld);
    ReleaseDC(hwnd, hdc);

    /*
     * The space used by the tab border and the space between the tab label
     * and the tab border is included as 7.
     */
    gui.tabline_height = tm.tmHeight + tm.tmInternalLeading + 7;
}
#endif

/*
 * Invoked when a setting was changed.
 */
    static LRESULT CALLBACK
_OnSettingChange(UINT n)
{
    if (n == SPI_SETWHEELSCROLLLINES)
	SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0,
		&mouse_scroll_lines, 0);
#if defined(FEAT_GUI_TABLINE) && defined(USE_SYSMENU_FONT)
    if (n == SPI_SETNONCLIENTMETRICS)
	set_tabline_font();
#endif
    return 0;
}

#ifdef FEAT_NETBEANS_INTG
    static void
_OnWindowPosChanged(
    HWND hwnd,
    const LPWINDOWPOS lpwpos)
{
    static int x = 0, y = 0, cx = 0, cy = 0;
    extern int WSInitialized;

    if (WSInitialized && (lpwpos->x != x || lpwpos->y != y
				     || lpwpos->cx != cx || lpwpos->cy != cy))
    {
	x = lpwpos->x;
	y = lpwpos->y;
	cx = lpwpos->cx;
	cy = lpwpos->cy;
	netbeans_frame_moved(x, y);
    }
    /* Allow to send WM_SIZE and WM_MOVE */
    FORWARD_WM_WINDOWPOSCHANGED(hwnd, lpwpos, MyWindowProc);
}
#endif

    static int
_DuringSizing(
    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_mswin_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);
    */

    HandleMouseHide(uMsg, lParam);

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

    switch (uMsg)
    {
	HANDLE_MSG(hwnd, WM_DEADCHAR,	_OnDeadChar);
	HANDLE_MSG(hwnd, WM_SYSDEADCHAR, _OnDeadChar);
	/* HANDLE_MSG(hwnd, WM_ACTIVATE,    _OnActivate); */
	HANDLE_MSG(hwnd, WM_CLOSE,	_OnClose);
	/* HANDLE_MSG(hwnd, WM_COMMAND,	_OnCommand); */
	HANDLE_MSG(hwnd, WM_DESTROY,	_OnDestroy);
	HANDLE_MSG(hwnd, WM_DROPFILES,	_OnDropFiles);
	HANDLE_MSG(hwnd, WM_HSCROLL,	_OnScroll);
	HANDLE_MSG(hwnd, WM_KILLFOCUS,	_OnKillFocus);
#ifdef FEAT_MENU
	HANDLE_MSG(hwnd, WM_COMMAND,	_OnMenu);
#endif
	/* 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_VSCROLL,	_OnScroll);
	// HANDLE_MSG(hwnd, WM_WINDOWPOSCHANGING,	_OnWindowPosChanging);
	HANDLE_MSG(hwnd, WM_ACTIVATEAPP, _OnActivateApp);
#ifdef FEAT_NETBEANS_INTG
	HANDLE_MSG(hwnd, WM_WINDOWPOSCHANGED, _OnWindowPosChanged);
#endif

#ifdef FEAT_GUI_TABLINE
	case WM_RBUTTONUP:
	{
	    if (gui_mch_showing_tabline())
	    {
		POINT pt;
		RECT rect;

		/*
		 * If the cursor is on the tabline, display the tab menu
		 */
		GetCursorPos((LPPOINT)&pt);
		GetWindowRect(s_textArea, &rect);
		if (pt.y < rect.top)
		{
		    show_tabline_popup_menu();
		    return 0L;
		}
	    }
	    return MyWindowProc(hwnd, uMsg, wParam, lParam);
	}
	case WM_LBUTTONDBLCLK:
	{
	    /*
	     * If the user double clicked the tabline, create a new tab
	     */
	    if (gui_mch_showing_tabline())
	    {
		POINT pt;
		RECT rect;

		GetCursorPos((LPPOINT)&pt);
		GetWindowRect(s_textArea, &rect);
		if (pt.y < rect.top)
		    send_tabline_menu_event(0, TABLINE_MENU_NEW);
	    }
	    return MyWindowProc(hwnd, uMsg, wParam, lParam);
	}
#endif

    case WM_QUERYENDSESSION:	/* System wants to go down. */
	gui_shell_closed();	/* Will exit when no changed buffers. */
	return FALSE;		/* Do NOT allow system to go down. */

    case WM_ENDSESSION:
	if (wParam)	/* system only really goes down when wParam is TRUE */
	{
	    _OnEndSession();
	    return 0L;
	}
	break;

    case WM_CHAR:
	/* Don't use HANDLE_MSG() for WM_CHAR, it truncates wParam to a single
	 * byte while we want the UTF-16 character value. */
	_OnChar(hwnd, (UINT)wParam, (int)(short)LOWORD(lParam));
	return 0L;

    case WM_SYSCHAR:
	/*
	 * if 'winaltkeys' is "no", or it's "menu" and it's not a menu
	 * shortcut key, handle like a typed ALT key, otherwise call Windows
	 * ALT key handling.
	 */
#ifdef FEAT_MENU
	if (	!gui.menu_is_active
		|| p_wak[0] == 'n'
		|| (p_wak[0] == 'm' && !gui_is_menu_shortcut((int)wParam))
		)
#endif
	{
	    _OnSysChar(hwnd, (UINT)wParam, (int)(short)LOWORD(lParam));
	    return 0L;
	}
#ifdef FEAT_MENU
	else
	    return MyWindowProc(hwnd, uMsg, wParam, lParam);
#endif

    case WM_SYSKEYUP:
#ifdef FEAT_MENU
	/* This used to be done only when menu is active: ALT key is used for
	 * that.  But that caused problems when menu is disabled and using
	 * Alt-Tab-Esc: get into a strange state where no mouse-moved events
	 * are received, mouse pointer remains hidden. */
	return MyWindowProc(hwnd, uMsg, wParam, lParam);
#else
	return 0L;
#endif

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

    case WM_MOUSEWHEEL:
	_OnMouseWheel(hwnd, HIWORD(wParam));
	return 0L;

	/* Notification for change in SystemParametersInfo() */
    case WM_SETTINGCHANGE:
	return _OnSettingChange((UINT)wParam);

#if defined(FEAT_TOOLBAR) || defined(FEAT_GUI_TABLINE)
    case WM_NOTIFY:
	switch (((LPNMHDR) lParam)->code)
	{
# ifdef FEAT_MBYTE
	    case TTN_GETDISPINFOW:
# endif
	    case TTN_GETDISPINFO:
		{
		    LPNMHDR		hdr = (LPNMHDR)lParam;
		    char_u		*str = NULL;
		    static void		*tt_text = NULL;

		    vim_free(tt_text);
		    tt_text = NULL;

# ifdef FEAT_GUI_TABLINE
		    if (gui_mch_showing_tabline()
			   && hdr->hwndFrom == TabCtrl_GetToolTips(s_tabhwnd))
		    {
			POINT		pt;
			/*
			 * Mouse is over the GUI tabline. Display the
			 * tooltip for the tab under the cursor
			 *
			 * Get the cursor position within the tab control
			 */
			GetCursorPos(&pt);
			if (ScreenToClient(s_tabhwnd, &pt) != 0)
			{
			    TCHITTESTINFO htinfo;
			    int idx;

			    /*
			     * Get the tab under the cursor
			     */
			    htinfo.pt.x = pt.x;
			    htinfo.pt.y = pt.y;
			    idx = TabCtrl_HitTest(s_tabhwnd, &htinfo);
			    if (idx != -1)
			    {
				tabpage_T *tp;

				tp = find_tabpage(idx + 1);
				if (tp != NULL)
				{
				    get_tabline_label(tp, TRUE);
				    str = NameBuff;
				}
			    }
			}
		    }
# endif
# ifdef FEAT_TOOLBAR
#  ifdef FEAT_GUI_TABLINE
		    else
#  endif
		    {
			UINT		idButton;
			vimmenu_T	*pMenu;

			idButton = (UINT) hdr->idFrom;
			pMenu = gui_mswin_find_menu(root_menu, idButton);
			if (pMenu)
			    str = pMenu->strings[MENU_INDEX_TIP];
		    }
# endif
		    if (str != NULL)
		    {
# ifdef FEAT_MBYTE
			if (hdr->code == TTN_GETDISPINFOW)
			{
			    LPNMTTDISPINFOW	lpdi = (LPNMTTDISPINFOW)lParam;

			    /* Set the maximum width, this also enables using
			     * \n for line break. */
			    SendMessage(lpdi->hdr.hwndFrom, TTM_SETMAXTIPWIDTH,
								      0, 500);

			    tt_text = enc_to_utf16(str, NULL);
			    lpdi->lpszText = tt_text;
			    /* can't show tooltip if failed */
			}
			else
# endif
			{
			    LPNMTTDISPINFO	lpdi = (LPNMTTDISPINFO)lParam;

			    /* Set the maximum width, this also enables using
			     * \n for line break. */
			    SendMessage(lpdi->hdr.hwndFrom, TTM_SETMAXTIPWIDTH,
								      0, 500);

			    if (STRLEN(str) < sizeof(lpdi->szText)
				    || ((tt_text = vim_strsave(str)) == NULL))
				vim_strncpy((char_u *)lpdi->szText, str,
						sizeof(lpdi->szText) - 1);
			    else
				lpdi->lpszText = tt_text;
			}
		    }
		}
		break;
# ifdef FEAT_GUI_TABLINE
	    case TCN_SELCHANGE:
		if (gui_mch_showing_tabline()
				  && ((LPNMHDR)lParam)->hwndFrom == s_tabhwnd)
		{
		    send_tabline_event(TabCtrl_GetCurSel(s_tabhwnd) + 1);
		    return 0L;
		}
		break;

	    case NM_RCLICK:
		if (gui_mch_showing_tabline()
			&& ((LPNMHDR)lParam)->hwndFrom == s_tabhwnd)
		{
		    show_tabline_popup_menu();
		    return 0L;
		}
		break;
# endif
	    default:
# ifdef FEAT_GUI_TABLINE
		if (gui_mch_showing_tabline()
				  && ((LPNMHDR)lParam)->hwndFrom == s_tabhwnd)
		    return MyWindowProc(hwnd, uMsg, wParam, lParam);
# endif
		break;
	}
	break;
#endif
#if defined(MENUHINTS) && defined(FEAT_MENU)
    case WM_MENUSELECT:
	if (((UINT) HIWORD(wParam)
		    & (0xffff ^ (MF_MOUSESELECT + MF_BITMAP + MF_POPUP)))
		== MF_HILITE
		&& (State & CMDLINE) == 0)
	{
	    UINT	idButton;
	    vimmenu_T	*pMenu;
	    static int	did_menu_tip = FALSE;

	    if (did_menu_tip)
	    {
		msg_clr_cmdline();
		setcursor();
		out_flush();
		did_menu_tip = FALSE;
	    }

	    idButton = (UINT)LOWORD(wParam);
	    pMenu = gui_mswin_find_menu(root_menu, idButton);
	    if (pMenu != NULL && pMenu->strings[MENU_INDEX_TIP] != 0
		    && GetMenuState(s_menuBar, pMenu->id, MF_BYCOMMAND) != -1)
	    {
		++msg_hist_off;
		msg(pMenu->strings[MENU_INDEX_TIP]);
		--msg_hist_off;
		setcursor();
		out_flush();
		did_menu_tip = TRUE;
	    }
	    return 0L;
	}
	break;
#endif
    case WM_NCHITTEST:
	{
	    LRESULT	result;
	    int		x, y;
	    int		xPos = GET_X_LPARAM(lParam);

	    result = MyWindowProc(hwnd, uMsg, wParam, lParam);
	    if (result == HTCLIENT)
	    {
#ifdef FEAT_GUI_TABLINE
		if (gui_mch_showing_tabline())
		{
		    int  yPos = GET_Y_LPARAM(lParam);
		    RECT rct;

		    /* If the cursor is on the GUI tabline, don't process this
		     * event */
		    GetWindowRect(s_textArea, &rct);
		    if (yPos < rct.top)
			return result;
		}
#endif
		(void)gui_mch_get_winpos(&x, &y);
		xPos -= x;

		if (xPos < 48) /* <VN> TODO should use system metric? */
		    return HTBOTTOMLEFT;
		else
		    return HTBOTTOMRIGHT;
	    }
	    else
		return result;
	}
	/* break; notreached */

#ifdef FEAT_MBYTE_IME
    case WM_IME_NOTIFY:
	if (!_OnImeNotify(hwnd, (DWORD)wParam, (DWORD)lParam))
	    return MyWindowProc(hwnd, uMsg, wParam, lParam);
	return 1L;

    case WM_IME_COMPOSITION:
	if (!_OnImeComposition(hwnd, wParam, lParam))
	    return MyWindowProc(hwnd, uMsg, wParam, lParam);
	return 1L;
#endif

    default:
	if (uMsg == msh_msgmousewheel && msh_msgmousewheel != 0)
	{   /* handle MSH_MOUSEWHEEL messages for Intellimouse */
	    _OnMouseWheel(hwnd, HIWORD(wParam));
	    return 0L;
	}
#ifdef MSWIN_FIND_REPLACE
	else if (uMsg == s_findrep_msg && s_findrep_msg != 0)
	{
	    _OnFindRepl();
	}
#endif
	return MyWindowProc(hwnd, uMsg, wParam, lParam);
    }

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

/*
 * End of call-back routines
 */

/* parent window, if specified with -P */
HWND vim_parent_hwnd = NULL;

    static BOOL CALLBACK
FindWindowTitle(HWND hwnd, LPARAM lParam)
{
    char	buf[2048];
    char	*title = (char *)lParam;

    if (GetWindowText(hwnd, buf, sizeof(buf)))
    {
	if (strstr(buf, title) != NULL)
	{
	    /* Found it.  Store the window ref. and quit searching if MDI
	     * works. */
	    vim_parent_hwnd = FindWindowEx(hwnd, NULL, "MDIClient", NULL);
	    if (vim_parent_hwnd != NULL)
		return FALSE;
	}
    }
    return TRUE;	/* continue searching */
}

/*
 * Invoked for '-P "title"' argument: search for parent application to open
 * our window in.
 */
    void
gui_mch_set_parent(char *title)
{
    EnumWindows(FindWindowTitle, (LPARAM)title);
    if (vim_parent_hwnd == NULL)
    {
	EMSG2(_("E671: Cannot find window title \"%s\""), title);
	mch_exit(2);
    }
}

#ifndef FEAT_OLE
    static void
ole_error(char *arg)
{
    char buf[IOSIZE];

    /* Can't use EMSG() here, we have not finished initialisation yet. */
    vim_snprintf(buf, IOSIZE,
	    _("E243: Argument not supported: \"-%s\"; Use the OLE version."),
	    arg);
    mch_errmsg(buf);
}
#endif

/*
 * 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(int *argc, char **argv)
{
    int		silent = FALSE;
    int		idx;

    /* Check for special OLE command line parameters */
    if ((*argc == 2 || *argc == 3) && (argv[1][0] == '-' || argv[1][0] == '/'))
    {
	/* Check for a "-silent" argument first. */
	if (*argc == 3 && STRICMP(argv[1] + 1, "silent") == 0
		&& (argv[2][0] == '-' || argv[2][0] == '/'))
	{
	    silent = TRUE;
	    idx = 2;
	}
	else
	    idx = 1;

	/* Register Vim as an OLE Automation server */
	if (STRICMP(argv[idx] + 1, "register") == 0)
	{
#ifdef FEAT_OLE
	    RegisterMe(silent);
	    mch_exit(0);
#else
	    if (!silent)
		ole_error("register");
	    mch_exit(2);
#endif
	}

	/* Unregister Vim as an OLE Automation server */
	if (STRICMP(argv[idx] + 1, "unregister") == 0)
	{
#ifdef FEAT_OLE
	    UnregisterMe(!silent);
	    mch_exit(0);
#else
	    if (!silent)
		ole_error("unregister");
	    mch_exit(2);
#endif
	}

	/* Ignore an -embedding argument. It is only relevant if the
	 * application wants to treat the case when it is started manually
	 * differently from the case where it is started via automation (and
	 * we don't).
	 */
	if (STRICMP(argv[idx] + 1, "embedding") == 0)
	{
#ifdef FEAT_OLE
	    *argc = 1;
#else
	    ole_error("embedding");
	    mch_exit(2);
#endif
	}
    }

#ifdef FEAT_OLE
    {
	int	bDoRestart = FALSE;

	InitOLE(&bDoRestart);
	/* automatically exit after registering */
	if (bDoRestart)
	    mch_exit(0);
    }
#endif

#ifdef FEAT_NETBEANS_INTG
    {
	/* stolen from gui_x11.c */
	int arg;

	for (arg = 1; arg < *argc; arg++)
	    if (strncmp("-nb", argv[arg], 3) == 0)
	    {
		netbeansArg = argv[arg];
		mch_memmove(&argv[arg], &argv[arg + 1],
					    (--*argc - arg) * sizeof(char *));
		argv[*argc] = NULL;
		break;	/* enough? */
	    }
    }
#endif

    /* get the OS version info */
    os_version.dwOSVersionInfoSize = sizeof(os_version);
    GetVersionEx(&os_version); /* this call works on Win32s, Win95 and WinNT */

    /* try and load the user32.dll library and get the entry points for
     * multi-monitor-support. */
    if ((user32_lib = vimLoadLib("User32.dll")) != NULL)
    {
	pMonitorFromWindow = (TMonitorFromWindow)GetProcAddress(user32_lib,
							 "MonitorFromWindow");

	/* there are ...A and ...W version of GetMonitorInfo - looking at
	 * winuser.h, they have exactly the same declaration. */
	pGetMonitorInfo = (TGetMonitorInfo)GetProcAddress(user32_lib,
							  "GetMonitorInfoA");
    }

#ifdef FEAT_MBYTE
    /* If the OS is Windows NT, use wide functions;
     * this enables common dialogs input unicode from IME. */
    if (os_version.dwPlatformId == VER_PLATFORM_WIN32_NT)
    {
	pDispatchMessage = DispatchMessageW;
	pGetMessage = GetMessageW;
	pIsDialogMessage = IsDialogMessageW;
	pPeekMessage = PeekMessageW;
    }
    else
    {
	pDispatchMessage = DispatchMessageA;
	pGetMessage = GetMessageA;
	pIsDialogMessage = IsDialogMessageA;
	pPeekMessage = PeekMessageA;
    }
#endif
}

/*
 * Initialise the GUI.	Create all the windows, set up all the call-backs
 * etc.
 */
    int
gui_mch_init(void)
{
    const char szVimWndClass[] = VIM_CLASS;
    const char szTextAreaClass[] = "VimTextArea";
    WNDCLASS wndclass;
#ifdef FEAT_MBYTE
    const WCHAR szVimWndClassW[] = VIM_CLASSW;
    const WCHAR szTextAreaClassW[] = L"VimTextArea";
    WNDCLASSW wndclassw;
#endif
#ifdef GLOBAL_IME
    ATOM	atom;
#endif

    /* Return here if the window was already opened (happens when
     * gui_mch_dialog() is called early). */
    if (s_hwnd != NULL)
	goto theend;

    /*
     * Load the tearoff bitmap
     */
#ifdef FEAT_TEAROFF
    s_htearbitmap = LoadBitmap(s_hinst, "IDB_TEAROFF");
#endif

    gui.scrollbar_width = GetSystemMetrics(SM_CXVSCROLL);
    gui.scrollbar_height = GetSystemMetrics(SM_CYHSCROLL);
#ifdef FEAT_MENU
    gui.menu_height = 0;	/* Windows takes care of this */
#endif
    gui.border_width = 0;

    s_brush = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));

#ifdef FEAT_MBYTE
    /* First try using the wide version, so that we can use any title.
     * Otherwise only characters in the active codepage will work. */
    if (GetClassInfoW(s_hinst, szVimWndClassW, &wndclassw) == 0)
    {
	wndclassw.style = CS_DBLCLKS;
	wndclassw.lpfnWndProc = _WndProc;
	wndclassw.cbClsExtra = 0;
	wndclassw.cbWndExtra = 0;
	wndclassw.hInstance = s_hinst;
	wndclassw.hIcon = LoadIcon(wndclassw.hInstance, "IDR_VIM");
	wndclassw.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndclassw.hbrBackground = s_brush;
	wndclassw.lpszMenuName = NULL;
	wndclassw.lpszClassName = szVimWndClassW;

	if ((
#ifdef GLOBAL_IME
		    atom =
#endif
		    RegisterClassW(&wndclassw)) == 0)
	{
	    if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
		return FAIL;

	    /* Must be Windows 98, fall back to non-wide function. */
	}
	else
	    wide_WindowProc = TRUE;
    }

    if (!wide_WindowProc)
#endif

    if (GetClassInfo(s_hinst, szVimWndClass, &wndclass) == 0)
    {
	wndclass.style = CS_DBLCLKS;
	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 = s_brush;
	wndclass.lpszMenuName = NULL;
	wndclass.lpszClassName = szVimWndClass;

	if ((
#ifdef GLOBAL_IME
		    atom =
#endif
		    RegisterClass(&wndclass)) == 0)
	    return FAIL;
    }

    if (vim_parent_hwnd != NULL)
    {
#ifdef HAVE_TRY_EXCEPT
	__try
	{
#endif
	    /* Open inside the specified parent window.
	     * TODO: last argument should point to a CLIENTCREATESTRUCT
	     * structure. */
	    s_hwnd = CreateWindowEx(
		WS_EX_MDICHILD,
		szVimWndClass, "Vim MSWindows GUI",
		WS_OVERLAPPEDWINDOW | WS_CHILD
				 | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | 0xC000,
		gui_win_x == -1 ? CW_USEDEFAULT : gui_win_x,
		gui_win_y == -1 ? CW_USEDEFAULT : gui_win_y,
		100,				/* Any value will do */
		100,				/* Any value will do */
		vim_parent_hwnd, NULL,
		s_hinst, NULL);
#ifdef HAVE_TRY_EXCEPT
	}
	__except(EXCEPTION_EXECUTE_HANDLER)
	{
	    /* NOP */
	}
#endif
	if (s_hwnd == NULL)
	{
	    EMSG(_("E672: Unable to open window inside MDI application"));
	    mch_exit(2);
	}
    }
    else
    {
	/* If the provided windowid is not valid reset it to zero, so that it
	 * is ignored and we open our own window. */
	if (IsWindow((HWND)win_socket_id) <= 0)
	    win_socket_id = 0;

	/* Create a window.  If win_socket_id is not zero without border and
	 * titlebar, it will be reparented below. */
	s_hwnd = CreateWindow(
		szVimWndClass, "Vim MSWindows GUI",
		(win_socket_id == 0 ? WS_OVERLAPPEDWINDOW : WS_POPUP)
					  | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
		gui_win_x == -1 ? CW_USEDEFAULT : gui_win_x,
		gui_win_y == -1 ? CW_USEDEFAULT : gui_win_y,
		100,				/* Any value will do */
		100,				/* Any value will do */
		NULL, NULL,
		s_hinst, NULL);
	if (s_hwnd != NULL && win_socket_id != 0)
	{
	    SetParent(s_hwnd, (HWND)win_socket_id);
	    ShowWindow(s_hwnd, SW_SHOWMAXIMIZED);
	}
    }

    if (s_hwnd == NULL)
	return FAIL;

#ifdef GLOBAL_IME
    global_ime_init(atom, s_hwnd);
#endif
#if defined(FEAT_MBYTE_IME) && defined(DYNAMIC_IME)
    dyn_imm_load();
#endif

    /* Create the text area window */
#ifdef FEAT_MBYTE
    if (wide_WindowProc)
    {
	if (GetClassInfoW(s_hinst, szTextAreaClassW, &wndclassw) == 0)
	{
	    wndclassw.style = CS_OWNDC;
	    wndclassw.lpfnWndProc = _TextAreaWndProc;
	    wndclassw.cbClsExtra = 0;
	    wndclassw.cbWndExtra = 0;
	    wndclassw.hInstance = s_hinst;
	    wndclassw.hIcon = NULL;
	    wndclassw.hCursor = LoadCursor(NULL, IDC_ARROW);
	    wndclassw.hbrBackground = NULL;
	    wndclassw.lpszMenuName = NULL;
	    wndclassw.lpszClassName = szTextAreaClassW;

	    if (RegisterClassW(&wndclassw) == 0)
		return FAIL;
	}
    }
    else
#endif
    if (GetClassInfo(s_hinst, szTextAreaClass, &wndclass) == 0)
    {
	wndclass.style = CS_OWNDC;
	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 = CreateWindowEx(
	0,
	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;

#ifdef FEAT_LIBCALL
    /* Try loading an icon from $RUNTIMEPATH/bitmaps/vim.ico. */
    {
	HANDLE	hIcon = NULL;

	if (mch_icon_load(&hIcon) == OK && hIcon != NULL)
	    SendMessage(s_hwnd, WM_SETICON, ICON_SMALL, (LPARAM)hIcon);
    }
#endif

#ifdef FEAT_MENU
    s_menuBar = CreateMenu();
#endif
    s_hdc = GetDC(s_textArea);

#ifdef FEAT_WINDOWS
    DragAcceptFiles(s_hwnd, TRUE);
#endif

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

    /* Get background/foreground colors from the system */
    gui_mch_def_colors();

    /* Get the colors from the "Normal" group (set in syntax.c or in a vimrc
     * file) */
    set_normal_colors();

    /*
     * Check that none of the colors are the same as the background color.
     * Then store the current values as the defaults.
     */
    gui_check_colors();
    gui.def_norm_pixel = gui.norm_pixel;
    gui.def_back_pixel = gui.back_pixel;

    /* Get the colors for the highlight groups (gui_check_colors() might have
     * changed them) */
    highlight_gui_started();

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

    /*
     * Set up for Intellimouse processing
     */
    init_mouse_wheel();

    /*
     * compute a couple of metrics used for the dialogs
     */
    get_dialog_font_metrics();
#ifdef FEAT_TOOLBAR
    /*
     * Create the toolbar
     */
    initialise_toolbar();
#endif
#ifdef FEAT_GUI_TABLINE
    /*
     * Create the tabline
     */
    initialise_tabline();
#endif
#ifdef MSWIN_FIND_REPLACE
    /*
     * Initialise the dialog box stuff
     */
    s_findrep_msg = RegisterWindowMessage(FINDMSGSTRING);

    /* Initialise the struct */
    s_findrep_struct.lStructSize = sizeof(s_findrep_struct);
    s_findrep_struct.lpstrFindWhat = (LPSTR)alloc(MSWIN_FR_BUFSIZE);
    s_findrep_struct.lpstrFindWhat[0] = NUL;
    s_findrep_struct.lpstrReplaceWith = (LPSTR)alloc(MSWIN_FR_BUFSIZE);
    s_findrep_struct.lpstrReplaceWith[0] = NUL;
    s_findrep_struct.wFindWhatLen = MSWIN_FR_BUFSIZE;
    s_findrep_struct.wReplaceWithLen = MSWIN_FR_BUFSIZE;
# if defined(FEAT_MBYTE) && defined(WIN3264)
    s_findrep_struct_w.lStructSize = sizeof(s_findrep_struct_w);
    s_findrep_struct_w.lpstrFindWhat =
			      (LPWSTR)alloc(MSWIN_FR_BUFSIZE * sizeof(WCHAR));
    s_findrep_struct_w.lpstrFindWhat[0] = NUL;
    s_findrep_struct_w.lpstrReplaceWith =
			      (LPWSTR)alloc(MSWIN_FR_BUFSIZE * sizeof(WCHAR));
    s_findrep_struct_w.lpstrReplaceWith[0] = NUL;
    s_findrep_struct_w.wFindWhatLen = MSWIN_FR_BUFSIZE;
    s_findrep_struct_w.wReplaceWithLen = MSWIN_FR_BUFSIZE;
# endif
#endif

#ifdef FEAT_EVAL
# if !defined(_MSC_VER) || (_MSC_VER < 1400)
/* Define HandleToLong for old MS and non-MS compilers if not defined. */
#  ifndef HandleToLong
#   define HandleToLong(h) ((long)(intptr_t)(h))
#  endif
# endif
    /* set the v:windowid variable */
    set_vim_var_nr(VV_WINDOWID, HandleToLong(s_hwnd));
#endif

#ifdef FEAT_RENDER_OPTIONS
    if (p_rop)
	(void)gui_mch_set_rendering_options(p_rop);
#endif

theend:
    /* Display any pending error messages */
    display_errors();

    return OK;
}

/*
 * Get the size of the screen, taking position on multiple monitors into
 * account (if supported).
 */
    static void
get_work_area(RECT *spi_rect)
{
    _HMONITOR	    mon;
    _MONITORINFO    moninfo;

    /* use these functions only if available */
    if (pMonitorFromWindow != NULL && pGetMonitorInfo != NULL)
    {
	/* work out which monitor the window is on, and get *it's* work area */
	mon = pMonitorFromWindow(s_hwnd, 1 /*MONITOR_DEFAULTTOPRIMARY*/);
	if (mon != NULL)
	{
	    moninfo.cbSize = sizeof(_MONITORINFO);
	    if (pGetMonitorInfo(mon, &moninfo))
	    {
		*spi_rect = moninfo.rcWork;
		return;
	    }
	}
    }
    /* this is the old method... */
    SystemParametersInfo(SPI_GETWORKAREA, 0, spi_rect, 0);
}

/*
 * Set the size of the window to the given width and height in pixels.
 */
/*ARGSUSED*/
    void
gui_mch_set_shellsize(int width, int height,
	int min_width, int min_height, int base_width, int base_height,
	int direction)
{
    RECT	workarea_rect;
    int		win_width, win_height;
    WINDOWPLACEMENT wndpl;

    /* Try to keep window completely on screen. */
    /* Get position of the screen work area.  This is the part that is not
     * used by the taskbar or appbars. */
    get_work_area(&workarea_rect);

    /* Get current position of our window.  Note that the .left and .top are
     * relative to the work area.  */
    wndpl.length = sizeof(WINDOWPLACEMENT);
    GetWindowPlacement(s_hwnd, &wndpl);

    /* Resizing a maximized window looks very strange, unzoom it first.
     * But don't do it when still starting up, it may have been requested in
     * the shortcut. */
    if (wndpl.showCmd == SW_SHOWMAXIMIZED && starting == 0)
    {
	ShowWindow(s_hwnd, SW_SHOWNORMAL);
	/* Need to get the settings of the normal window. */
	GetWindowPlacement(s_hwnd, &wndpl);
    }

    /* compute the size of the outside of the window */
    win_width = width + (GetSystemMetrics(SM_CXFRAME) +
			 GetSystemMetrics(SM_CXPADDEDBORDER)) * 2;
    win_height = height + (GetSystemMetrics(SM_CYFRAME) +
			   GetSystemMetrics(SM_CXPADDEDBORDER)) * 2
			+ GetSystemMetrics(SM_CYCAPTION)
#ifdef FEAT_MENU
			+ gui_mswin_get_menu_height(FALSE)
#endif
			;

    /* The following should take care of keeping Vim on the same monitor, no
     * matter if the secondary monitor is left or right of the primary
     * monitor. */
    wndpl.rcNormalPosition.right = wndpl.rcNormalPosition.left + win_width;
    wndpl.rcNormalPosition.bottom = wndpl.rcNormalPosition.top + win_height;

    /* If the window is going off the screen, move it on to the screen. */
    if ((direction & RESIZE_HOR)
	    && wndpl.rcNormalPosition.right > workarea_rect.right)
	OffsetRect(&wndpl.rcNormalPosition,
		workarea_rect.right - wndpl.rcNormalPosition.right, 0);

    if ((direction & RESIZE_HOR)
	    && wndpl.rcNormalPosition.left < workarea_rect.left)
	OffsetRect(&wndpl.rcNormalPosition,
		workarea_rect.left - wndpl.rcNormalPosition.left, 0);

    if ((direction & RESIZE_VERT)
	    && wndpl.rcNormalPosition.bottom > workarea_rect.bottom)
	OffsetRect(&wndpl.rcNormalPosition,
		0, workarea_rect.bottom - wndpl.rcNormalPosition.bottom);

    if ((direction & RESIZE_VERT)
	    && wndpl.rcNormalPosition.top < workarea_rect.top)
	OffsetRect(&wndpl.rcNormalPosition,
		0, workarea_rect.top - wndpl.rcNormalPosition.top);

    /* set window position - we should use SetWindowPlacement rather than
     * SetWindowPos as the MSDN docs say the coord systems returned by
     * these two are not compatible. */
    SetWindowPlacement(s_hwnd, &wndpl);

    SetActiveWindow(s_hwnd);
    SetFocus(s_hwnd);

#ifdef FEAT_MENU
    /* Menu may wrap differently now */
    gui_mswin_get_menu_height(!gui.starting);
#endif
}


    void
gui_mch_set_scrollbar_thumb(
    scrollbar_T *sb,
    long	val,
    long	size,
    long	max)
{
    SCROLLINFO	info;

    sb->scroll_shift = 0;
    while (max > 32767)
    {
	max = (max + 1) >> 1;
	val  >>= 1;
	size >>= 1;
	++sb->scroll_shift;
    }

    if (sb->scroll_shift > 0)
	++size;

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


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


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

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

/*
 * Set the current text special color.
 */
    void
gui_mch_set_sp_color(guicolor_T color)
{
    gui.currSpColor = color;
}

#if defined(FEAT_MBYTE) && defined(FEAT_MBYTE_IME)
/*
 * Multi-byte handling, originally by Sung-Hoon Baek.
 * First static functions (no prototypes generated).
 */
#ifdef _MSC_VER
# include <ime.h>   /* Apparently not needed for Cygwin, MingW or Borland. */
#endif
#include <imm.h>

/*
 * handle WM_IME_NOTIFY message
 */
/*ARGSUSED*/
    static LRESULT
_OnImeNotify(HWND hWnd, DWORD dwCommand, DWORD dwData)
{
    LRESULT lResult = 0;
    HIMC hImc;

    if (!pImmGetContext || (hImc = pImmGetContext(hWnd)) == (HIMC)0)
	return lResult;
    switch (dwCommand)
    {
	case IMN_SETOPENSTATUS:
	    if (pImmGetOpenStatus(hImc))
	    {
		pImmSetCompositionFont(hImc, &norm_logfont);
		im_set_position(gui.row, gui.col);

		/* Disable langmap */
		State &= ~LANGMAP;
		if (State & INSERT)
		{
#if defined(FEAT_WINDOWS) && defined(FEAT_KEYMAP)
		    /* Unshown 'keymap' in status lines */
		    if (curbuf->b_p_iminsert == B_IMODE_LMAP)
		    {
			/* Save cursor position */
			int old_row = gui.row;
			int old_col = gui.col;

			// This must be called here before
			// status_redraw_curbuf(), otherwise the mode
			// message may appear in the wrong position.
			showmode();
			status_redraw_curbuf();
			update_screen(0);
			/* Restore cursor position */
			gui.row = old_row;
			gui.col = old_col;
		    }
#endif
		}
	    }
	    gui_update_cursor(TRUE, FALSE);
	    lResult = 0;
	    break;
    }
    pImmReleaseContext(hWnd, hImc);
    return lResult;
}

/*ARGSUSED*/
    static LRESULT
_OnImeComposition(HWND hwnd, WPARAM dbcs, LPARAM param)
{
    char_u	*ret;
    int		len;

    if ((param & GCS_RESULTSTR) == 0) /* Composition unfinished. */
	return 0;

    ret = GetResultStr(hwnd, GCS_RESULTSTR, &len);
    if (ret != NULL)
    {
	add_to_input_buf_csi(ret, len);
	vim_free(ret);
	return 1;
    }
    return 0;
}

/*
 * get the current composition string, in UCS-2; *lenp is the number of
 * *lenp is the number of Unicode characters.
 */
    static short_u *
GetCompositionString_inUCS2(HIMC hIMC, DWORD GCS, int *lenp)
{
    LONG	    ret;
    LPWSTR	    wbuf = NULL;
    char_u	    *buf;

    if (!pImmGetContext)
	return NULL; /* no imm32.dll */

    /* Try Unicode; this'll always work on NT regardless of codepage. */
    ret = pImmGetCompositionStringW(hIMC, GCS, NULL, 0);
    if (ret == 0)
	return NULL; /* empty */

    if (ret > 0)
    {
	/* Allocate the requested buffer plus space for the NUL character. */
	wbuf = (LPWSTR)alloc(ret + sizeof(WCHAR));
	if (wbuf != NULL)
	{
	    pImmGetCompositionStringW(hIMC, GCS, wbuf, ret);
	    *lenp = ret / sizeof(WCHAR);
	}
	return (short_u *)wbuf;
    }

    /* ret < 0; we got an error, so try the ANSI version.  This'll work
     * on 9x/ME, but only if the codepage happens to be set to whatever
     * we're inputting. */
    ret = pImmGetCompositionStringA(hIMC, GCS, NULL, 0);
    if (ret <= 0)
	return NULL; /* empty or error */

    buf = alloc(ret);
    if (buf == NULL)
	return NULL;
    pImmGetCompositionStringA(hIMC, GCS, buf, ret);

    /* convert from codepage to UCS-2 */
    MultiByteToWideChar_alloc(GetACP(), 0, (LPCSTR)buf, ret, &wbuf, lenp);
    vim_free(buf);

    return (short_u *)wbuf;
}

/*
 * void GetResultStr()
 *
 * This handles WM_IME_COMPOSITION with GCS_RESULTSTR flag on.
 * get complete composition string
 */
    static char_u *
GetResultStr(HWND hwnd, int GCS, int *lenp)
{
    HIMC	hIMC;		/* Input context handle. */
    short_u	*buf = NULL;
    char_u	*convbuf = NULL;

    if (!pImmGetContext || (hIMC = pImmGetContext(hwnd)) == (HIMC)0)
	return NULL;

    /* Reads in the composition string. */
    buf = GetCompositionString_inUCS2(hIMC, GCS, lenp);
    if (buf == NULL)
	return NULL;

    convbuf = utf16_to_enc(buf, lenp);
    pImmReleaseContext(hwnd, hIMC);
    vim_free(buf);
    return convbuf;
}
#endif

/* For global functions we need prototypes. */
#if (defined(FEAT_MBYTE) && defined(FEAT_MBYTE_IME)) || defined(PROTO)

/*
 * set font to IM.
 */
    void
im_set_font(LOGFONT *lf)
{
    HIMC hImc;

    if (pImmGetContext && (hImc = pImmGetContext(s_hwnd)) != (HIMC)0)
    {
	pImmSetCompositionFont(hImc, lf);
	pImmReleaseContext(s_hwnd, hImc);
    }
}

/*
 * Notify cursor position to IM.
 */
    void
im_set_position(int row, int col)
{
    HIMC hImc;

    if (pImmGetContext && (hImc = pImmGetContext(s_hwnd)) != (HIMC)0)
    {
	COMPOSITIONFORM	cfs;

	cfs.dwStyle = CFS_POINT;
	cfs.ptCurrentPos.x = FILL_X(col);
	cfs.ptCurrentPos.y = FILL_Y(row);
	MapWindowPoints(s_textArea, s_hwnd, &cfs.ptCurrentPos, 1);
	pImmSetCompositionWindow(hImc, &cfs);

	pImmReleaseContext(s_hwnd, hImc);
    }
}

/*
 * Set IM status on ("active" is TRUE) or off ("active" is FALSE).
 */
    void
im_set_active(int active)
{
    HIMC	hImc;
    static HIMC	hImcOld = (HIMC)0;

    if (pImmGetContext)	    /* if NULL imm32.dll wasn't loaded (yet) */
    {
	if (p_imdisable)
	{
	    if (hImcOld == (HIMC)0)
	    {
		hImcOld = pImmGetContext(s_hwnd);
		if (hImcOld)
		    pImmAssociateContext(s_hwnd, (HIMC)0);
	    }
	    active = FALSE;
	}
	else if (hImcOld != (HIMC)0)
	{
	    pImmAssociateContext(s_hwnd, hImcOld);
	    hImcOld = (HIMC)0;
	}

	hImc = pImmGetContext(s_hwnd);
	if (hImc)
	{
	    /*
	     * for Korean ime
	     */
	    HKL hKL = GetKeyboardLayout(0);

	    if (LOWORD(hKL) == MAKELANGID(LANG_KOREAN, SUBLANG_KOREAN))
	    {
		static DWORD dwConversionSaved = 0, dwSentenceSaved = 0;
		static BOOL bSaved = FALSE;

		if (active)
		{
		    /* if we have a saved conversion status, restore it */
		    if (bSaved)
			pImmSetConversionStatus(hImc, dwConversionSaved,
							     dwSentenceSaved);
		    bSaved = FALSE;
		}
		else
		{
		    /* save conversion status and disable korean */
		    if (pImmGetConversionStatus(hImc, &dwConversionSaved,
							    &dwSentenceSaved))
		    {
			bSaved = TRUE;
			pImmSetConversionStatus(hImc,
				dwConversionSaved & ~(IME_CMODE_NATIVE
						       | IME_CMODE_FULLSHAPE),
				dwSentenceSaved);
		    }
		}
	    }

	    pImmSetOpenStatus(hImc, active);
	    pImmReleaseContext(s_hwnd, hImc);
	}
    }
}

/*
 * Get IM status.  When IM is on, return not 0.  Else return 0.
 */
    int
im_get_status(void)
{
    int		status = 0;
    HIMC	hImc;

    if (pImmGetContext && (hImc = pImmGetContext(s_hwnd)) != (HIMC)0)
    {
	status = pImmGetOpenStatus(hImc) ? 1 : 0;
	pImmReleaseContext(s_hwnd, hImc);
    }
    return status;
}

#endif /* FEAT_MBYTE && FEAT_MBYTE_IME */

#if defined(FEAT_MBYTE) && !defined(FEAT_MBYTE_IME) && defined(GLOBAL_IME)
/* Win32 with GLOBAL IME */

/*
 * Notify cursor position to IM.
 */
    void
im_set_position(int row, int col)
{
    /* Win32 with GLOBAL IME */
    POINT p;

    p.x = FILL_X(col);
    p.y = FILL_Y(row);
    MapWindowPoints(s_textArea, s_hwnd, &p, 1);
    global_ime_set_position(&p);
}

/*
 * Set IM status on ("active" is TRUE) or off ("active" is FALSE).
 */
    void
im_set_active(int active)
{
    global_ime_set_status(active);
}

/*
 * Get IM status.  When IM is on, return not 0.  Else return 0.
 */
    int
im_get_status(void)
{
    return global_ime_get_status();
}
#endif

#ifdef FEAT_MBYTE
/*
 * Convert latin9 text "text[len]" to ucs-2 in "unicodebuf".
 */
    static void
latin9_to_ucs(char_u *text, int len, WCHAR *unicodebuf)
{
    int		c;

    while (--len >= 0)
    {
	c = *text++;
	switch (c)
	{
	    case 0xa4: c = 0x20ac; break;   /* euro */
	    case 0xa6: c = 0x0160; break;   /* S hat */
	    case 0xa8: c = 0x0161; break;   /* S -hat */
	    case 0xb4: c = 0x017d; break;   /* Z hat */
	    case 0xb8: c = 0x017e; break;   /* Z -hat */
	    case 0xbc: c = 0x0152; break;   /* OE */
	    case 0xbd: c = 0x0153; break;   /* oe */
	    case 0xbe: c = 0x0178; break;   /* Y */
	}
	*unicodebuf++ = c;
    }
}
#endif

#ifdef FEAT_RIGHTLEFT
/*
 * What is this for?  In the case where you are using Win98 or Win2K or later,
 * and you are using a Hebrew font (or Arabic!), Windows does you a favor and
 * reverses the string sent to the TextOut... family.  This sucks, because we
 * go to a lot of effort to do the right thing, and there doesn't seem to be a
 * way to tell Windblows not to do this!
 *
 * The short of it is that this 'RevOut' only gets called if you are running
 * one of the new, "improved" MS OSes, and only if you are running in
 * 'rightleft' mode.  It makes display take *slightly* longer, but not
 * noticeably so.
 */
    static void
RevOut( HDC s_hdc,
	int col,
	int row,
	UINT foptions,
	CONST RECT *pcliprect,
	LPCTSTR text,
	UINT len,
	CONST INT *padding)
{
    int		ix;
    static int	special = -1;

    if (special == -1)
    {
	/* Check windows version: special treatment is needed if it is NT 5 or
	 * Win98 or higher. */
	if  ((os_version.dwPlatformId == VER_PLATFORM_WIN32_NT
		    && os_version.dwMajorVersion >= 5)
		|| (os_version.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS
		    && (os_version.dwMajorVersion > 4
			|| (os_version.dwMajorVersion == 4
			    && os_version.dwMinorVersion > 0))))
	    special = 1;
	else
	    special = 0;
    }

    if (special)
	for (ix = 0; ix < (int)len; ++ix)
	    ExtTextOut(s_hdc, col + TEXT_X(ix), row, foptions,
					    pcliprect, text + ix, 1, padding);
    else
	ExtTextOut(s_hdc, col, row, foptions, pcliprect, text, len, padding);
}
#endif

    void
gui_mch_draw_string(
    int		row,
    int		col,
    char_u	*text,
    int		len,
    int		flags)
{
    static int	*padding = NULL;
    static int	pad_size = 0;
    int		i;
    const RECT	*pcliprect = NULL;
    UINT	foptions = 0;
#ifdef FEAT_MBYTE
    static WCHAR *unicodebuf = NULL;
    static int   *unicodepdy = NULL;
    static int	unibuflen = 0;
    int		n = 0;
#endif
    HPEN	hpen, old_pen;
    int		y;
#ifdef FEAT_DIRECTX
    int		font_is_ttf_or_vector = 0;
#endif

    /*
     * 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.
     */
    static HBRUSH	hbr_cache[2] = {NULL, NULL};
    static guicolor_T	brush_color[2] = {INVALCOLOR, INVALCOLOR};
    static int		brush_lru = 0;
    HBRUSH		hbr;
    RECT		rc;

    if (!(flags & DRAW_TRANSP))
    {
	/*
	 * Clear background first.
	 * Note: FillRect() excludes right and bottom of rectangle.
	 */
	rc.left = FILL_X(col);
	rc.top = FILL_Y(row);
#ifdef FEAT_MBYTE
	if (has_mbyte)
	{
	    /* Compute the length in display cells. */
	    rc.right = FILL_X(col + mb_string2cells(text, len));
	}
	else
#endif
	    rc.right = FILL_X(col + len);
	rc.bottom = FILL_Y(row + 1);

	/* Cache the created brush, that saves a lot of time.  We need two:
	 * one for cursor background and one for the normal background. */
	if (gui.currBgColor == brush_color[0])
	{
	    hbr = hbr_cache[0];
	    brush_lru = 1;
	}
	else if (gui.currBgColor == brush_color[1])
	{
	    hbr = hbr_cache[1];
	    brush_lru = 0;
	}
	else
	{
	    if (hbr_cache[brush_lru] != NULL)
		DeleteBrush(hbr_cache[brush_lru]);
	    hbr_cache[brush_lru] = CreateSolidBrush(gui.currBgColor);
	    brush_color[brush_lru] = gui.currBgColor;
	    hbr = hbr_cache[brush_lru];
	    brush_lru = !brush_lru;
	}
	FillRect(s_hdc, &rc, hbr);

	SetBkMode(s_hdc, TRANSPARENT);

	/*
	 * When drawing block cursor, prevent inverted character spilling
	 * over character cell (can happen with bold/italic)
	 */
	if (flags & DRAW_CURSOR)
	{
	    pcliprect = &rc;
	    foptions = ETO_CLIPPED;
	}
    }
    SetTextColor(s_hdc, gui.currFgColor);
    SelectFont(s_hdc, gui.currFont);

#ifdef FEAT_DIRECTX
    if (IS_ENABLE_DIRECTX())
    {
	TEXTMETRIC tm;

	GetTextMetrics(s_hdc, &tm);
	if (tm.tmPitchAndFamily & (TMPF_TRUETYPE | TMPF_VECTOR))
	{
	    font_is_ttf_or_vector = 1;
	    DWriteContext_SetFont(s_dwc, (HFONT)gui.currFont);
	}
    }
#endif

    if (pad_size != Columns || padding == NULL || padding[0] != gui.char_width)
    {
	vim_free(padding);
	pad_size = Columns;

	/* Don't give an out-of-memory message here, it would call us
	 * recursively. */
	padding = (int *)lalloc(pad_size * sizeof(int), FALSE);
	if (padding != NULL)
	    for (i = 0; i < pad_size; i++)
		padding[i] = gui.char_width;
    }

    /*
     * We have to provide the padding argument because italic and bold versions
     * of fixed-width fonts are often one pixel or so wider than their normal
     * versions.
     * No check for DRAW_BOLD, Windows will have done it already.
     */

#ifdef FEAT_MBYTE
    /* Check if there are any UTF-8 characters.  If not, use normal text
     * output to speed up output. */
    if (enc_utf8)
	for (n = 0; n < len; ++n)
	    if (text[n] >= 0x80)
		break;

#if defined(FEAT_DIRECTX)
    /* Quick hack to enable DirectWrite.  To use DirectWrite (antialias), it is
     * required that unicode drawing routine, currently.  So this forces it
     * enabled. */
    if (enc_utf8 && IS_ENABLE_DIRECTX())
	n = 0; /* Keep n < len, to enter block for unicode. */
#endif

    /* Check if the Unicode buffer exists and is big enough.  Create it
     * with the same length as the multi-byte string, the number of wide
     * characters is always equal or smaller. */
    if ((enc_utf8
		|| (enc_codepage > 0 && (int)GetACP() != enc_codepage)
		|| enc_latin9)
	    && (unicodebuf == NULL || len > unibuflen))
    {
	vim_free(unicodebuf);
	unicodebuf = (WCHAR *)lalloc(len * sizeof(WCHAR), FALSE);

	vim_free(unicodepdy);
	unicodepdy = (int *)lalloc(len * sizeof(int), FALSE);

	unibuflen = len;
    }

    if (enc_utf8 && n < len && unicodebuf != NULL)
    {
	/* Output UTF-8 characters.  Caller has already separated
	 * composing characters. */
	int		i;
	int		wlen;	/* string length in words */
	int		clen;	/* string length in characters */
	int		cells;	/* cell width of string up to composing char */
	int		cw;	/* width of current cell */
	int		c;

	wlen = 0;
	clen = 0;
	cells = 0;
	for (i = 0; i < len; )
	{
	    c = utf_ptr2char(text + i);
	    if (c >= 0x10000)
	    {
		/* Turn into UTF-16 encoding. */
		unicodebuf[wlen++] = ((c - 0x10000) >> 10) + 0xD800;
		unicodebuf[wlen++] = ((c - 0x10000) & 0x3ff) + 0xDC00;
	    }
	    else
	    {
		unicodebuf[wlen++] = c;
	    }
	    cw = utf_char2cells(c);
	    if (cw > 2)		/* don't use 4 for unprintable char */
		cw = 1;
	    if (unicodepdy != NULL)
	    {
		/* Use unicodepdy to make characters fit as we expect, even
		 * when the font uses different widths (e.g., bold character
		 * is wider).  */
		if (c >= 0x10000)
		{
		    unicodepdy[wlen - 2] = cw * gui.char_width;
		    unicodepdy[wlen - 1] = 0;
		}
		else
		    unicodepdy[wlen - 1] = cw * gui.char_width;
	    }
	    cells += cw;
	    i += utfc_ptr2len_len(text + i, len - i);
	    ++clen;
	}
#if defined(FEAT_DIRECTX)
	if (IS_ENABLE_DIRECTX() && font_is_ttf_or_vector)
	{
	    /* Add one to "cells" for italics. */
	    DWriteContext_DrawText(s_dwc, s_hdc, unicodebuf, wlen,
		    TEXT_X(col), TEXT_Y(row), FILL_X(cells + 1), FILL_Y(1),
		    gui.char_width, gui.currFgColor);
	}
	else
#endif
	    ExtTextOutW(s_hdc, TEXT_X(col), TEXT_Y(row),
		    foptions, pcliprect, unicodebuf, wlen, unicodepdy);
	len = cells;	/* used for underlining */
    }
    else if ((enc_codepage > 0 && (int)GetACP() != enc_codepage) || enc_latin9)
    {
	/* If we want to display codepage data, and the current CP is not the
	 * ANSI one, we need to go via Unicode. */
	if (unicodebuf != NULL)
	{
	    if (enc_latin9)
		latin9_to_ucs(text, len, unicodebuf);
	    else
		len = MultiByteToWideChar(enc_codepage,
			MB_PRECOMPOSED,
			(char *)text, len,
			(LPWSTR)unicodebuf, unibuflen);
	    if (len != 0)
	    {
		/* Use unicodepdy to make characters fit as we expect, even
		 * when the font uses different widths (e.g., bold character
		 * is wider). */
		if (unicodepdy != NULL)
		{
		    int i;
		    int cw;

		    for (i = 0; i < len; ++i)
		    {
			cw = utf_char2cells(unicodebuf[i]);
			if (cw > 2)
			    cw = 1;
			unicodepdy[i] = cw * gui.char_width;
		    }
		}
		ExtTextOutW(s_hdc, TEXT_X(col), TEXT_Y(row),
			    foptions, pcliprect, unicodebuf, len, unicodepdy);
	    }
	}
    }
    else
#endif
    {
#ifdef FEAT_RIGHTLEFT
	/* Windows will mess up RL text, so we have to draw it character by
	 * character.  Only do this if RL is on, since it's slow. */
	if (curwin->w_p_rl)
	    RevOut(s_hdc, TEXT_X(col), TEXT_Y(row),
			 foptions, pcliprect, (char *)text, len, padding);
	else
#endif
	    ExtTextOut(s_hdc, TEXT_X(col), TEXT_Y(row),
			 foptions, pcliprect, (char *)text, len, padding);
    }

    /* Underline */
    if (flags & DRAW_UNDERL)
    {
	hpen = CreatePen(PS_SOLID, 1, gui.currFgColor);
	old_pen = SelectObject(s_hdc, hpen);
	/* When p_linespace is 0, overwrite the bottom row of pixels.
	 * Otherwise put the line just below the character. */
	y = FILL_Y(row + 1) - 1;
	if (p_linespace > 1)
	    y -= p_linespace - 1;
	MoveToEx(s_hdc, FILL_X(col), y, NULL);
	/* Note: LineTo() excludes the last pixel in the line. */
	LineTo(s_hdc, FILL_X(col + len), y);
	DeleteObject(SelectObject(s_hdc, old_pen));
    }

    /* Undercurl */
    if (flags & DRAW_UNDERC)
    {
	int			x;
	int			offset;
	static const int	val[8] = {1, 0, 0, 0, 1, 2, 2, 2 };

	y = FILL_Y(row + 1) - 1;
	for (x = FILL_X(col); x < FILL_X(col + len); ++x)
	{
	    offset = val[x % 8];
	    SetPixel(s_hdc, x, y - offset, gui.currSpColor);
	}
    }
}


/*
 * Output routines.
 */

/* Flush any output to the screen */
    void
gui_mch_flush(void)
{
#   if defined(__BORLANDC__)
    /*
     * The GdiFlush declaration (in Borland C 5.01 <wingdi.h>) is not a
     * prototype declaration.
     * The compiler complains if __stdcall is not used in both declarations.
     */
    BOOL  __stdcall GdiFlush(void);
#   endif

    GdiFlush();
}

    static void
clear_rect(RECT *rcp)
{
    HBRUSH  hbr;

    hbr = CreateSolidBrush(gui.back_pixel);
    FillRect(s_hdc, rcp, hbr);
    DeleteBrush(hbr);
}


    void
gui_mch_get_screen_dimensions(int *screen_w, int *screen_h)
{
    RECT	workarea_rect;

    get_work_area(&workarea_rect);

    *screen_w = workarea_rect.right - workarea_rect.left
		- (GetSystemMetrics(SM_CXFRAME) +
		   GetSystemMetrics(SM_CXPADDEDBORDER)) * 2;

    /* FIXME: dirty trick: Because the gui_get_base_height() doesn't include
     * the menubar for MSwin, we subtract it from the screen height, so that
     * the window size can be made to fit on the screen. */
    *screen_h = workarea_rect.bottom - workarea_rect.top
		- (GetSystemMetrics(SM_CYFRAME) +
		   GetSystemMetrics(SM_CXPADDEDBORDER)) * 2
		- GetSystemMetrics(SM_CYCAPTION)
#ifdef FEAT_MENU
		- gui_mswin_get_menu_height(FALSE)
#endif
		;
}


#if defined(FEAT_MENU) || defined(PROTO)
/*
 * Add a sub menu to the menu bar.
 */
    void
gui_mch_add_menu(
    vimmenu_T	*menu,
    int		pos)
{
    vimmenu_T	*parent = menu->parent;

    menu->submenu_id = CreatePopupMenu();
    menu->id = s_menu_id++;

    if (menu_is_menubar(menu->name))
    {
	if (is_winnt_3())
	{
	    InsertMenu((parent == NULL) ? s_menuBar : parent->submenu_id,
		    (UINT)pos, MF_POPUP | MF_STRING | MF_BYPOSITION,
		    (long_u)menu->submenu_id, (LPCTSTR) menu->name);
	}
	else
	{
#ifdef FEAT_MBYTE
	    WCHAR	*wn = NULL;
	    int		n;

	    if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
	    {
		/* 'encoding' differs from active codepage: convert menu name
		 * and use wide function */
		wn = enc_to_utf16(menu->name, NULL);
		if (wn != NULL)
		{
		    MENUITEMINFOW	infow;

		    infow.cbSize = sizeof(infow);
		    infow.fMask = MIIM_DATA | MIIM_TYPE | MIIM_ID
							       | MIIM_SUBMENU;
		    infow.dwItemData = (long_u)menu;
		    infow.wID = menu->id;
		    infow.fType = MFT_STRING;
		    infow.dwTypeData = wn;
		    infow.cch = (UINT)wcslen(wn);
		    infow.hSubMenu = menu->submenu_id;
		    n = InsertMenuItemW((parent == NULL)
					    ? s_menuBar : parent->submenu_id,
					    (UINT)pos, TRUE, &infow);
		    vim_free(wn);
		    if (n == 0 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
			/* Failed, try using non-wide function. */
			wn = NULL;
		}
	    }

	    if (wn == NULL)
#endif
	    {
		MENUITEMINFO	info;

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

    /* Fix window size if menu may have wrapped */
    if (parent == NULL)
	gui_mswin_get_menu_height(!gui.starting);
#ifdef FEAT_TEAROFF
    else if (IsWindow(parent->tearoff_handle))
	rebuild_tearoff(parent);
#endif
}

    void
gui_mch_show_popupmenu(vimmenu_T *menu)
{
    POINT mp;

    (void)GetCursorPos((LPPOINT)&mp);
    gui_mch_show_popupmenu_at(menu, (int)mp.x, (int)mp.y);
}

    void
gui_make_popup(char_u *path_name, int mouse_pos)
{
    vimmenu_T	*menu = gui_find_menu(path_name);

    if (menu != NULL)
    {
	POINT	p;

	/* Find the position of the current cursor */
	GetDCOrgEx(s_hdc, &p);
	if (mouse_pos)
	{
	    int	mx, my;

	    gui_mch_getmouse(&mx, &my);
	    p.x += mx;
	    p.y += my;
	}
	else if (curwin != NULL)
	{
	    p.x += TEXT_X(W_WINCOL(curwin) + curwin->w_wcol + 1);
	    p.y += TEXT_Y(W_WINROW(curwin) + curwin->w_wrow + 1);
	}
	msg_scroll = FALSE;
	gui_mch_show_popupmenu_at(menu, (int)p.x, (int)p.y);
    }
}

#if defined(FEAT_TEAROFF) || defined(PROTO)
/*
 * Given a menu descriptor, e.g. "File.New", find it in the menu hierarchy and
 * create it as a pseudo-"tearoff menu".
 */
    void
gui_make_tearoff(char_u *path_name)
{
    vimmenu_T	*menu = gui_find_menu(path_name);

    /* Found the menu, so tear it off. */
    if (menu != NULL)
	gui_mch_tearoff(menu->dname, menu, 0xffffL, 0xffffL);
}
#endif

/*
 * Add a menu item to a menu
 */
    void
gui_mch_add_menu_item(
    vimmenu_T	*menu,
    int		idx)
{
    vimmenu_T	*parent = menu->parent;

    menu->id = s_menu_id++;
    menu->submenu_id = NULL;

#ifdef FEAT_TEAROFF
    if (STRNCMP(menu->name, TEAR_STRING, TEAR_LEN) == 0)
    {
	InsertMenu(parent->submenu_id, (UINT)idx, MF_BITMAP|MF_BYPOSITION,
		(UINT)menu->id, (LPCTSTR) s_htearbitmap);
    }
    else
#endif
#ifdef FEAT_TOOLBAR
    if (menu_is_toolbar(parent->name))
    {
	TBBUTTON newtb;

	vim_memset(&newtb, 0, sizeof(newtb));
	if (menu_is_separator(menu->name))
	{
	    newtb.iBitmap = 0;
	    newtb.fsStyle = TBSTYLE_SEP;
	}
	else
	{
	    newtb.iBitmap = get_toolbar_bitmap(menu);
	    newtb.fsStyle = TBSTYLE_BUTTON;
	}
	newtb.idCommand = menu->id;
	newtb.fsState = TBSTATE_ENABLED;
	newtb.iString = 0;
	SendMessage(s_toolbarhwnd, TB_INSERTBUTTON, (WPARAM)idx,
							     (LPARAM)&newtb);
	menu->submenu_id = (HMENU)-1;
    }
    else
#endif
    {
#ifdef FEAT_MBYTE
	WCHAR	*wn = NULL;
	int	n;

	if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
	{
	    /* 'encoding' differs from active codepage: convert menu item name
	     * and use wide function */
	    wn = enc_to_utf16(menu->name, NULL);
	    if (wn != NULL)
	    {
		n = InsertMenuW(parent->submenu_id, (UINT)idx,
			(menu_is_separator(menu->name)
				 ? MF_SEPARATOR : MF_STRING) | MF_BYPOSITION,
			(UINT)menu->id, wn);
		vim_free(wn);
		if (n == 0 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
		    /* Failed, try using non-wide function. */
		    wn = NULL;
	    }
	}
	if (wn == NULL)
#endif
	    InsertMenu(parent->submenu_id, (UINT)idx,
		(menu_is_separator(menu->name) ? MF_SEPARATOR : MF_STRING)
							      | MF_BYPOSITION,
		(UINT)menu->id, (LPCTSTR)menu->name);
#ifdef FEAT_TEAROFF
	if (IsWindow(parent->tearoff_handle))
	    rebuild_tearoff(parent);
#endif
    }
}

/*
 * Destroy the machine specific menu widget.
 */
    void
gui_mch_destroy_menu(vimmenu_T *menu)
{
#ifdef FEAT_TOOLBAR
    /*
     * is this a toolbar button?
     */
    if (menu->submenu_id == (HMENU)-1)
    {
	int iButton;

	iButton = (int)SendMessage(s_toolbarhwnd, TB_COMMANDTOINDEX,
							 (WPARAM)menu->id, 0);
	SendMessage(s_toolbarhwnd, TB_DELETEBUTTON, (WPARAM)iButton, 0);
    }
    else
#endif
    {
	if (menu->parent != NULL
		&& menu_is_popup(menu->parent->dname)
		&& menu->parent->submenu_id != NULL)
	    RemoveMenu(menu->parent->submenu_id, menu->id, MF_BYCOMMAND);
	else
	    RemoveMenu(s_menuBar, menu->id, MF_BYCOMMAND);
	if (menu->submenu_id != NULL)
	    DestroyMenu(menu->submenu_id);
#ifdef FEAT_TEAROFF
	if (IsWindow(menu->tearoff_handle))
	    DestroyWindow(menu->tearoff_handle);
	if (menu->parent != NULL
		&& menu->parent->children != NULL
		&& IsWindow(menu->parent->tearoff_handle))
	{
	    /* This menu must not show up when rebuilding the tearoff window. */
	    menu->modes = 0;
	    rebuild_tearoff(menu->parent);
	}
#endif
    }
}

#ifdef FEAT_TEAROFF
    static void
rebuild_tearoff(vimmenu_T *menu)
{
    /*hackish*/
    char_u	tbuf[128];
    RECT	trect;
    RECT	rct;
    RECT	roct;
    int		x, y;

    HWND thwnd = menu->tearoff_handle;

    GetWindowText(thwnd, (LPSTR)tbuf, 127);
    if (GetWindowRect(thwnd, &trect)
	    && GetWindowRect(s_hwnd, &rct)
	    && GetClientRect(s_hwnd, &roct))
    {
	x = trect.left - rct.left;
	y = (trect.top -  rct.bottom  + roct.bottom);
    }
    else
    {
	x = y = 0xffffL;
    }
    DestroyWindow(thwnd);
    if (menu->children != NULL)
    {
	gui_mch_tearoff(tbuf, menu, x, y);
	if (IsWindow(menu->tearoff_handle))
	    (void) SetWindowPos(menu->tearoff_handle,
				NULL,
				(int)trect.left,
				(int)trect.top,
				0, 0,
				SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
    }
}
#endif /* FEAT_TEAROFF */

/*
 * Make a menu either grey or not grey.
 */
    void
gui_mch_menu_grey(
    vimmenu_T	*menu,
    int	    grey)
{
#ifdef FEAT_TOOLBAR
    /*
     * is this a toolbar button?
     */
    if (menu->submenu_id == (HMENU)-1)
    {
	SendMessage(s_toolbarhwnd, TB_ENABLEBUTTON,
	    (WPARAM)menu->id, (LPARAM) MAKELONG((grey ? FALSE : TRUE), 0) );
    }
    else
#endif
    (void)EnableMenuItem(menu->parent ? menu->parent->submenu_id : s_menuBar,
		    menu->id, MF_BYCOMMAND | (grey ? MF_GRAYED : MF_ENABLED));

#ifdef FEAT_TEAROFF
    if ((menu->parent != NULL) && (IsWindow(menu->parent->tearoff_handle)))
    {
	WORD menuID;
	HWND menuHandle;

	/*
	 * A tearoff button has changed state.
	 */
	if (menu->children == NULL)
	    menuID = (WORD)(menu->id);
	else
	    menuID = (WORD)((long_u)(menu->submenu_id) | (DWORD)0x8000);
	menuHandle = GetDlgItem(menu->parent->tearoff_handle, menuID);
	if (menuHandle)
	    EnableWindow(menuHandle, !grey);

    }
#endif
}

#endif /* FEAT_MENU */


/* define some macros used to make the dialogue creation more readable */

#define add_string(s) strcpy((LPSTR)p, s); (LPSTR)p += (strlen((LPSTR)p) + 1)
#define add_word(x)		*p++ = (x)
#define add_long(x)		dwp = (DWORD *)p; *dwp++ = (x); p = (WORD *)dwp

#if defined(FEAT_GUI_DIALOG) || defined(PROTO)
/*
 * stuff for dialogs
 */

/*
 * The callback routine used by all the dialogs.  Very simple.  First,
 * acknowledges the INITDIALOG message so that Windows knows to do standard
 * dialog stuff (Return = default, Esc = cancel....) Second, if a button is
 * pressed, return that button's ID - IDCANCEL (2), which is the button's
 * number.
 */
/*ARGSUSED*/
    static LRESULT CALLBACK
dialog_callback(
    HWND hwnd,
    UINT message,
    WPARAM wParam,
    LPARAM lParam)
{
    if (message == WM_INITDIALOG)
    {
	CenterWindow(hwnd, GetWindow(hwnd, GW_OWNER));
	/* Set focus to the dialog.  Set the default button, if specified. */
	(void)SetFocus(hwnd);
	if (dialog_default_button > IDCANCEL)
	    (void)SetFocus(GetDlgItem(hwnd, dialog_default_button));
	else
	    /* We don't have a default, set focus on another element of the
	     * dialog window, probably the icon */
	    (void)SetFocus(GetDlgItem(hwnd, DLG_NONBUTTON_CONTROL));
	return FALSE;
    }

    if (message == WM_COMMAND)
    {
	int	button = LOWORD(wParam);

	/* Don't end the dialog if something was selected that was
	 * not a button.
	 */
	if (button >= DLG_NONBUTTON_CONTROL)
	    return TRUE;

	/* If the edit box exists, copy the string. */
	if (s_textfield != NULL)
	{
# if defined(FEAT_MBYTE) && defined(WIN3264)
	    /* If the OS is Windows NT, and 'encoding' differs from active
	     * codepage: use wide function and convert text. */
	    if (os_version.dwPlatformId == VER_PLATFORM_WIN32_NT
		    && enc_codepage >= 0 && (int)GetACP() != enc_codepage)
	    {
	       WCHAR  *wp = (WCHAR *)alloc(IOSIZE * sizeof(WCHAR));
	       char_u *p;

	       GetDlgItemTextW(hwnd, DLG_NONBUTTON_CONTROL + 2, wp, IOSIZE);
	       p = utf16_to_enc(wp, NULL);
	       vim_strncpy(s_textfield, p, IOSIZE);
	       vim_free(p);
	       vim_free(wp);
	    }
	    else
# endif
		GetDlgItemText(hwnd, DLG_NONBUTTON_CONTROL + 2,
						(LPSTR)s_textfield, IOSIZE);
	}

	/*
	 * Need to check for IDOK because if the user just hits Return to
	 * accept the default value, some reason this is what we get.
	 */
	if (button == IDOK)
	{
	    if (dialog_default_button > IDCANCEL)
		EndDialog(hwnd, dialog_default_button);
	}
	else
	    EndDialog(hwnd, button - IDCANCEL);
	return TRUE;
    }

    if ((message == WM_SYSCOMMAND) && (wParam == SC_CLOSE))
    {
	EndDialog(hwnd, 0);
	return TRUE;
    }
    return FALSE;
}

/*
 * Create a dialog dynamically from the parameter strings.
 * type		= type of dialog (question, alert, etc.)
 * title	= dialog title. may be NULL for default title.
 * message	= text to display. Dialog sizes to accommodate it.
 * buttons	= '\n' separated list of button captions, default first.
 * dfltbutton	= number of default button.
 *
 * This routine returns 1 if the first button is pressed,
 *			2 for the second, etc.
 *
 *			0 indicates Esc was pressed.
 *			-1 for unexpected error
 *
 * If stubbing out this fn, return 1.
 */

static const char *dlg_icons[] = /* must match names in resource file */
{
    "IDR_VIM",
    "IDR_VIM_ERROR",
    "IDR_VIM_ALERT",
    "IDR_VIM_INFO",
    "IDR_VIM_QUESTION"
};

    int
gui_mch_dialog(
    int		 type,
    char_u	*title,
    char_u	*message,
    char_u	*buttons,
    int		 dfltbutton,
    char_u	*textfield,
    int		ex_cmd)
{
    WORD	*p, *pdlgtemplate, *pnumitems;
    DWORD	*dwp;
    int		numButtons;
    int		*buttonWidths, *buttonPositions;
    int		buttonYpos;
    int		nchar, i;
    DWORD	lStyle;
    int		dlgwidth = 0;
    int		dlgheight;
    int		editboxheight;
    int		horizWidth = 0;
    int		msgheight;
    char_u	*pstart;
    char_u	*pend;
    char_u	*last_white;
    char_u	*tbuffer;
    RECT	rect;
    HWND	hwnd;
    HDC		hdc;
    HFONT	font, oldFont;
    TEXTMETRIC	fontInfo;
    int		fontHeight;
    int		textWidth, minButtonWidth, messageWidth;
    int		maxDialogWidth;
    int		maxDialogHeight;
    int		scroll_flag = 0;
    int		vertical;
    int		dlgPaddingX;
    int		dlgPaddingY;
#ifdef USE_SYSMENU_FONT
    LOGFONT	lfSysmenu;
    int		use_lfSysmenu = FALSE;
#endif
    garray_T	ga;
    int		l;

#ifndef NO_CONSOLE
    /* Don't output anything in silent mode ("ex -s") */
    if (silent_mode)
	return dfltbutton;   /* return default option */
#endif

    if (s_hwnd == NULL)
	get_dialog_font_metrics();

    if ((type < 0) || (type > VIM_LAST_TYPE))
	type = 0;

    /* allocate some memory for dialog template */
    /* TODO should compute this really */
    pdlgtemplate = p = (PWORD)LocalAlloc(LPTR,
					DLG_ALLOC_SIZE + STRLEN(message) * 2);

    if (p == NULL)
	return -1;

    /*
     * make a copy of 'buttons' to fiddle with it.  compiler grizzles because
     * vim_strsave() doesn't take a const arg (why not?), so cast away the
     * const.
     */
    tbuffer = vim_strsave(buttons);
    if (tbuffer == NULL)
	return -1;

    --dfltbutton;   /* Change from one-based to zero-based */

    /* Count buttons */
    numButtons = 1;
    for (i = 0; tbuffer[i] != '\0'; i++)
    {
	if (tbuffer[i] == DLG_BUTTON_SEP)
	    numButtons++;
    }
    if (dfltbutton >= numButtons)
	dfltbutton = -1;

    /* Allocate array to hold the width of each button */
    buttonWidths = (int *)lalloc(numButtons * sizeof(int), TRUE);
    if (buttonWidths == NULL)
	return -1;

    /* Allocate array to hold the X position of each button */
    buttonPositions = (int *)lalloc(numButtons * sizeof(int), TRUE);
    if (buttonPositions == NULL)
	return -1;

    /*
     * Calculate how big the dialog must be.
     */
    hwnd = GetDesktopWindow();
    hdc = GetWindowDC(hwnd);
#ifdef USE_SYSMENU_FONT
    if (gui_w32_get_menu_font(&lfSysmenu) == OK)
    {
	font = CreateFontIndirect(&lfSysmenu);
	use_lfSysmenu = TRUE;
    }
    else
#endif
    font = CreateFont(-DLG_FONT_POINT_SIZE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
		      VARIABLE_PITCH , DLG_FONT_NAME);
    if (s_usenewlook)
    {
	oldFont = SelectFont(hdc, font);
	dlgPaddingX = DLG_PADDING_X;
	dlgPaddingY = DLG_PADDING_Y;
    }
    else
    {
	oldFont = SelectFont(hdc, GetStockObject(SYSTEM_FONT));
	dlgPaddingX = DLG_OLD_STYLE_PADDING_X;
	dlgPaddingY = DLG_OLD_STYLE_PADDING_Y;
    }
    GetTextMetrics(hdc, &fontInfo);
    fontHeight = fontInfo.tmHeight;

    /* Minimum width for horizontal button */
    minButtonWidth = GetTextWidth(hdc, (char_u *)"Cancel", 6);

    /* Maximum width of a dialog, if possible */
    if (s_hwnd == NULL)
    {
	RECT	workarea_rect;

	/* We don't have a window, use the desktop area. */
	get_work_area(&workarea_rect);
	maxDialogWidth = workarea_rect.right - workarea_rect.left - 100;
	if (maxDialogWidth > 600)
	    maxDialogWidth = 600;
	/* Leave some room for the taskbar. */
	maxDialogHeight = workarea_rect.bottom - workarea_rect.top - 150;
    }
    else
    {
	/* Use our own window for the size, unless it's very small. */
	GetWindowRect(s_hwnd, &rect);
	maxDialogWidth = rect.right - rect.left
				   - (GetSystemMetrics(SM_CXFRAME) +
				      GetSystemMetrics(SM_CXPADDEDBORDER)) * 2;
	if (maxDialogWidth < DLG_MIN_MAX_WIDTH)
	    maxDialogWidth = DLG_MIN_MAX_WIDTH;

	maxDialogHeight = rect.bottom - rect.top
				   - (GetSystemMetrics(SM_CYFRAME) +
				      GetSystemMetrics(SM_CXPADDEDBORDER)) * 4
				   - GetSystemMetrics(SM_CYCAPTION);
	if (maxDialogHeight < DLG_MIN_MAX_HEIGHT)
	    maxDialogHeight = DLG_MIN_MAX_HEIGHT;
    }

    /* Set dlgwidth to width of message.
     * Copy the message into "ga", changing NL to CR-NL and inserting line
     * breaks where needed. */
    pstart = message;
    messageWidth = 0;
    msgheight = 0;
    ga_init2(&ga, sizeof(char), 500);
    do
    {
	msgheight += fontHeight;    /* at least one line */

	/* Need to figure out where to break the string.  The system does it
	 * at a word boundary, which would mean we can't compute the number of
	 * wrapped lines. */
	textWidth = 0;
	last_white = NULL;
	for (pend = pstart; *pend != NUL && *pend != '\n'; )
	{
#ifdef FEAT_MBYTE
	    l = (*mb_ptr2len)(pend);
#else
	    l = 1;
#endif
	    if (l == 1 && vim_iswhite(*pend)
					&& textWidth > maxDialogWidth * 3 / 4)
		last_white = pend;
	    textWidth += GetTextWidthEnc(hdc, pend, l);
	    if (textWidth >= maxDialogWidth)
	    {
		/* Line will wrap. */
		messageWidth = maxDialogWidth;
		msgheight += fontHeight;
		textWidth = 0;

		if (last_white != NULL)
		{
		    /* break the line just after a space */
		    ga.ga_len -= (int)(pend - (last_white + 1));
		    pend = last_white + 1;
		    last_white = NULL;
		}
		ga_append(&ga, '\r');
		ga_append(&ga, '\n');
		continue;
	    }

	    while (--l >= 0)
		ga_append(&ga, *pend++);
	}
	if (textWidth > messageWidth)
	    messageWidth = textWidth;

	ga_append(&ga, '\r');
	ga_append(&ga, '\n');
	pstart = pend + 1;
    } while (*pend != NUL);

    if (ga.ga_data != NULL)
	message = ga.ga_data;

    messageWidth += 10;		/* roundoff space */

    /* Add width of icon to dlgwidth, and some space */
    dlgwidth = messageWidth + DLG_ICON_WIDTH + 3 * dlgPaddingX
					     + GetSystemMetrics(SM_CXVSCROLL);

    if (msgheight < DLG_ICON_HEIGHT)
	msgheight = DLG_ICON_HEIGHT;

    /*
     * Check button names.  A long one will make the dialog wider.
     * When called early (-register error message) p_go isn't initialized.
     */
    vertical = (p_go != NULL && vim_strchr(p_go, GO_VERTICAL) != NULL);
    if (!vertical)
    {
	// Place buttons horizontally if they fit.
	horizWidth = dlgPaddingX;
	pstart = tbuffer;
	i = 0;
	do
	{
	    pend = vim_strchr(pstart, DLG_BUTTON_SEP);
	    if (pend == NULL)
		pend = pstart + STRLEN(pstart);	// Last button name.
	    textWidth = GetTextWidthEnc(hdc, pstart, (int)(pend - pstart));
	    if (textWidth < minButtonWidth)
		textWidth = minButtonWidth;
	    textWidth += dlgPaddingX;	    /* Padding within button */
	    buttonWidths[i] = textWidth;
	    buttonPositions[i++] = horizWidth;
	    horizWidth += textWidth + dlgPaddingX; /* Pad between buttons */
	    pstart = pend + 1;
	} while (*pend != NUL);

	if (horizWidth > maxDialogWidth)
	    vertical = TRUE;	// Too wide to fit on the screen.
	else if (horizWidth > dlgwidth)
	    dlgwidth = horizWidth;
    }

    if (vertical)
    {
	// Stack buttons vertically.
	pstart = tbuffer;
	do
	{
	    pend = vim_strchr(pstart, DLG_BUTTON_SEP);
	    if (pend == NULL)
		pend = pstart + STRLEN(pstart);	// Last button name.
	    textWidth = GetTextWidthEnc(hdc, pstart, (int)(pend - pstart));
	    textWidth += dlgPaddingX;		/* Padding within button */
	    textWidth += DLG_VERT_PADDING_X * 2; /* Padding around button */
	    if (textWidth > dlgwidth)
		dlgwidth = textWidth;
	    pstart = pend + 1;
	} while (*pend != NUL);
    }

    if (dlgwidth < DLG_MIN_WIDTH)
	dlgwidth = DLG_MIN_WIDTH;	/* Don't allow a really thin dialog!*/

    /* start to fill in the dlgtemplate information.  addressing by WORDs */
    if (s_usenewlook)
	lStyle = DS_MODALFRAME | WS_CAPTION |DS_3DLOOK| WS_VISIBLE |DS_SETFONT;
    else
	lStyle = DS_MODALFRAME | WS_CAPTION |DS_3DLOOK| WS_VISIBLE;

    add_long(lStyle);
    add_long(0);	// (lExtendedStyle)
    pnumitems = p;	/*save where the number of items must be stored*/
    add_word(0);	// NumberOfItems(will change later)
    add_word(10);	// x
    add_word(10);	// y
    add_word(PixelToDialogX(dlgwidth));	// cx

    // Dialog height.
    if (vertical)
	dlgheight = msgheight + 2 * dlgPaddingY
			   + DLG_VERT_PADDING_Y + 2 * fontHeight * numButtons;
    else
	dlgheight = msgheight + 3 * dlgPaddingY + 2 * fontHeight;

    // Dialog needs to be taller if contains an edit box.
    editboxheight = fontHeight + dlgPaddingY + 4 * DLG_VERT_PADDING_Y;
    if (textfield != NULL)
	dlgheight += editboxheight;

    /* Restrict the size to a maximum.  Causes a scrollbar to show up. */
    if (dlgheight > maxDialogHeight)
    {
	msgheight = msgheight - (dlgheight - maxDialogHeight);
	dlgheight = maxDialogHeight;
	scroll_flag = WS_VSCROLL;
	/* Make sure scrollbar doesn't appear in the middle of the dialog */
	messageWidth = dlgwidth - DLG_ICON_WIDTH - 3 * dlgPaddingX;
    }

    add_word(PixelToDialogY(dlgheight));

    add_word(0);	// Menu
    add_word(0);	// Class

    /* copy the title of the dialog */
    nchar = nCopyAnsiToWideChar(p, (title ?
				    (LPSTR)title :
				    (LPSTR)("Vim "VIM_VERSION_MEDIUM)));
    p += nchar;

    if (s_usenewlook)
    {
	/* do the font, since DS_3DLOOK doesn't work properly */
#ifdef USE_SYSMENU_FONT
	if (use_lfSysmenu)
	{
	    /* point size */
	    *p++ = -MulDiv(lfSysmenu.lfHeight, 72,
		    GetDeviceCaps(hdc, LOGPIXELSY));
	    nchar = nCopyAnsiToWideChar(p, TEXT(lfSysmenu.lfFaceName));
	}
	else
#endif
	{
	    *p++ = DLG_FONT_POINT_SIZE;		// point size
	    nchar = nCopyAnsiToWideChar(p, TEXT(DLG_FONT_NAME));
	}
	p += nchar;
    }

    buttonYpos = msgheight + 2 * dlgPaddingY;

    if (textfield != NULL)
	buttonYpos += editboxheight;

    pstart = tbuffer;
    if (!vertical)
	horizWidth = (dlgwidth - horizWidth) / 2;	/* Now it's X offset */
    for (i = 0; i < numButtons; i++)
    {
	/* get end of this button. */
	for (	pend = pstart;
		*pend && (*pend != DLG_BUTTON_SEP);
		pend++)
	    ;

	if (*pend)
	    *pend = '\0';

	/*
	 * old NOTE:
	 * setting the BS_DEFPUSHBUTTON style doesn't work because Windows sets
	 * the focus to the first tab-able button and in so doing makes that
	 * the default!! Grrr.  Workaround: Make the default button the only
	 * one with WS_TABSTOP style. Means user can't tab between buttons, but
	 * he/she can use arrow keys.
	 *
	 * new NOTE: BS_DEFPUSHBUTTON is required to be able to select the
	 * right button when hitting <Enter>.  E.g., for the ":confirm quit"
	 * dialog.  Also needed for when the textfield is the default control.
	 * It appears to work now (perhaps not on Win95?).
	 */
	if (vertical)
	{
	    p = add_dialog_element(p,
		    (i == dfltbutton
			    ? BS_DEFPUSHBUTTON : BS_PUSHBUTTON) | WS_TABSTOP,
		    PixelToDialogX(DLG_VERT_PADDING_X),
		    PixelToDialogY(buttonYpos /* TBK */
				   + 2 * fontHeight * i),
		    PixelToDialogX(dlgwidth - 2 * DLG_VERT_PADDING_X),
		    (WORD)(PixelToDialogY(2 * fontHeight) - 1),
		    (WORD)(IDCANCEL + 1 + i), (WORD)0x0080, (char *)pstart);
	}
	else
	{
	    p = add_dialog_element(p,
		    (i == dfltbutton
			    ? BS_DEFPUSHBUTTON : BS_PUSHBUTTON) | WS_TABSTOP,
		    PixelToDialogX(horizWidth + buttonPositions[i]),
		    PixelToDialogY(buttonYpos), /* TBK */
		    PixelToDialogX(buttonWidths[i]),
		    (WORD)(PixelToDialogY(2 * fontHeight) - 1),
		    (WORD)(IDCANCEL + 1 + i), (WORD)0x0080, (char *)pstart);
	}
	pstart = pend + 1;	/*next button*/
    }
    *pnumitems += numButtons;

    /* Vim icon */
    p = add_dialog_element(p, SS_ICON,
	    PixelToDialogX(dlgPaddingX),
	    PixelToDialogY(dlgPaddingY),
	    PixelToDialogX(DLG_ICON_WIDTH),
	    PixelToDialogY(DLG_ICON_HEIGHT),
	    DLG_NONBUTTON_CONTROL + 0, (WORD)0x0082,
	    dlg_icons[type]);

    /* Dialog message */
    p = add_dialog_element(p, ES_LEFT|scroll_flag|ES_MULTILINE|ES_READONLY,
	    PixelToDialogX(2 * dlgPaddingX + DLG_ICON_WIDTH),
	    PixelToDialogY(dlgPaddingY),
	    (WORD)(PixelToDialogX(messageWidth) + 1),
	    PixelToDialogY(msgheight),
	    DLG_NONBUTTON_CONTROL + 1, (WORD)0x0081, (char *)message);

    /* Edit box */
    if (textfield != NULL)
    {
	p = add_dialog_element(p, ES_LEFT|ES_AUTOHSCROLL|WS_TABSTOP|WS_BORDER,
		PixelToDialogX(2 * dlgPaddingX),
		PixelToDialogY(2 * dlgPaddingY + msgheight),
		PixelToDialogX(dlgwidth - 4 * dlgPaddingX),
		PixelToDialogY(fontHeight + dlgPaddingY),
		DLG_NONBUTTON_CONTROL + 2, (WORD)0x0081, (char *)textfield);
	*pnumitems += 1;
    }

    *pnumitems += 2;

    SelectFont(hdc, oldFont);
    DeleteObject(font);
    ReleaseDC(hwnd, hdc);

    /* Let the dialog_callback() function know which button to make default
     * If we have an edit box, make that the default. We also need to tell
     * dialog_callback() if this dialog contains an edit box or not. We do
     * this by setting s_textfield if it does.
     */
    if (textfield != NULL)
    {
	dialog_default_button = DLG_NONBUTTON_CONTROL + 2;
	s_textfield = textfield;
    }
    else
    {
	dialog_default_button = IDCANCEL + 1 + dfltbutton;
	s_textfield = NULL;
    }

    /* show the dialog box modally and get a return value */
    nchar = (int)DialogBoxIndirect(
	    s_hinst,
	    (LPDLGTEMPLATE)pdlgtemplate,
	    s_hwnd,
	    (DLGPROC)dialog_callback);

    LocalFree(LocalHandle(pdlgtemplate));
    vim_free(tbuffer);
    vim_free(buttonWidths);
    vim_free(buttonPositions);
    vim_free(ga.ga_data);

    /* Focus back to our window (for when MDI is used). */
    (void)SetFocus(s_hwnd);

    return nchar;
}

#endif /* FEAT_GUI_DIALOG */

/*
 * Put a simple element (basic class) onto a dialog template in memory.
 * return a pointer to where the next item should be added.
 *
 * parameters:
 *  lStyle = additional style flags
 *		(be careful, NT3.51 & Win32s will ignore the new ones)
 *  x,y = x & y positions IN DIALOG UNITS
 *  w,h = width and height IN DIALOG UNITS
 *  Id  = ID used in messages
 *  clss  = class ID, e.g 0x0080 for a button, 0x0082 for a static
 *  caption = usually text or resource name
 *
 *  TODO: use the length information noted here to enable the dialog creation
 *  routines to work out more exactly how much memory they need to alloc.
 */
    static PWORD
add_dialog_element(
    PWORD p,
    DWORD lStyle,
    WORD x,
    WORD y,
    WORD w,
    WORD h,
    WORD Id,
    WORD clss,
    const char *caption)
{
    int nchar;

    p = lpwAlign(p);	/* Align to dword boundary*/
    lStyle = lStyle | WS_VISIBLE | WS_CHILD;
    *p++ = LOWORD(lStyle);
    *p++ = HIWORD(lStyle);
    *p++ = 0;		// LOWORD (lExtendedStyle)
    *p++ = 0;		// HIWORD (lExtendedStyle)
    *p++ = x;
    *p++ = y;
    *p++ = w;
    *p++ = h;
    *p++ = Id;		//9 or 10 words in all

    *p++ = (WORD)0xffff;
    *p++ = clss;			//2 more here

    nchar = nCopyAnsiToWideChar(p, (LPSTR)caption); //strlen(caption)+1
    p += nchar;

    *p++ = 0;  // advance pointer over nExtraStuff WORD   - 2 more

    return p;	//total = 15+ (strlen(caption)) words
		//	   = 30 + 2(strlen(caption) bytes reqd
}


/*
 * Helper routine.  Take an input pointer, return closest pointer that is
 * aligned on a DWORD (4 byte) boundary.  Taken from the Win32SDK samples.
 */
    static LPWORD
lpwAlign(
    LPWORD lpIn)
{
    long_u ul;

    ul = (long_u)lpIn;
    ul += 3;
    ul >>= 2;
    ul <<= 2;
    return (LPWORD)ul;
}

/*
 * Helper routine.  Takes second parameter as Ansi string, copies it to first
 * parameter as wide character (16-bits / char) string, and returns integer
 * number of wide characters (words) in string (including the trailing wide
 * char NULL).  Partly taken from the Win32SDK samples.
 */
    static int
nCopyAnsiToWideChar(
    LPWORD lpWCStr,
    LPSTR lpAnsiIn)
{
    int		nChar = 0;
#ifdef FEAT_MBYTE
    int		len = lstrlen(lpAnsiIn) + 1;	/* include NUL character */
    int		i;
    WCHAR	*wn;

    if (enc_codepage == 0 && (int)GetACP() != enc_codepage)
    {
	/* Not a codepage, use our own conversion function. */
	wn = enc_to_utf16((char_u *)lpAnsiIn, NULL);
	if (wn != NULL)
	{
	    wcscpy(lpWCStr, wn);
	    nChar = (int)wcslen(wn) + 1;
	    vim_free(wn);
	}
    }
    if (nChar == 0)
	/* Use Win32 conversion function. */
	nChar = MultiByteToWideChar(
		enc_codepage > 0 ? enc_codepage : CP_ACP,
		MB_PRECOMPOSED,
		lpAnsiIn, len,
		lpWCStr, len);
    for (i = 0; i < nChar; ++i)
	if (lpWCStr[i] == (WORD)'\t')	/* replace tabs with spaces */
	    lpWCStr[i] = (WORD)' ';
#else
    do
    {
	if (*lpAnsiIn == '\t')
	    *lpWCStr++ = (WORD)' ';
	else
	    *lpWCStr++ = (WORD)*lpAnsiIn;
	nChar++;
    } while (*lpAnsiIn++);
#endif

    return nChar;
}


#ifdef FEAT_TEAROFF
/*
 * The callback function for all the modeless dialogs that make up the
 * "tearoff menus" Very simple - forward button presses (to fool Vim into
 * thinking its menus have been clicked), and go away when closed.
 */
    static LRESULT CALLBACK
tearoff_callback(
    HWND hwnd,
    UINT message,
    WPARAM wParam,
    LPARAM lParam)
{
    if (message == WM_INITDIALOG)
	return (TRUE);

    /* May show the mouse pointer again. */
    HandleMouseHide(message, lParam);

    if (message == WM_COMMAND)
    {
	if ((WORD)(LOWORD(wParam)) & 0x8000)
	{
	    POINT   mp;
	    RECT    rect;

	    if (GetCursorPos(&mp) && GetWindowRect(hwnd, &rect))
	    {
		(void)TrackPopupMenu(
			 (HMENU)(long_u)(LOWORD(wParam) ^ 0x8000),
			 TPM_LEFTALIGN | TPM_LEFTBUTTON,
			 (int)rect.right - 8,
			 (int)mp.y,
			 (int)0,	    /*reserved param*/
			 s_hwnd,
			 NULL);
		/*
		 * NOTE: The pop-up menu can eat the mouse up event.
		 * We deal with this in normal.c.
		 */
	    }
	}
	else
	    /* Pass on messages to the main Vim window */
	    PostMessage(s_hwnd, WM_COMMAND, LOWORD(wParam), 0);
	/*
	 * Give main window the focus back: this is so after
	 * choosing a tearoff button you can start typing again
	 * straight away.
	 */
	(void)SetFocus(s_hwnd);
	return TRUE;
    }
    if ((message == WM_SYSCOMMAND) && (wParam == SC_CLOSE))
    {
	DestroyWindow(hwnd);
	return TRUE;
    }

    /* When moved around, give main window the focus back. */
    if (message == WM_EXITSIZEMOVE)
	(void)SetActiveWindow(s_hwnd);

    return FALSE;
}
#endif


/*
 * Decide whether to use the "new look" (small, non-bold font) or the "old
 * look" (big, clanky font) for dialogs, and work out a few values for use
 * later accordingly.
 */
    static void
get_dialog_font_metrics(void)
{
    HDC		    hdc;
    HFONT	    hfontTools = 0;
    DWORD	    dlgFontSize;
    SIZE	    size;
#ifdef USE_SYSMENU_FONT
    LOGFONT	    lfSysmenu;
#endif

    s_usenewlook = FALSE;

    /*
     * For NT3.51 and Win32s, we stick with the old look
     * because it matches everything else.
     */
    if (!is_winnt_3())
    {
#ifdef USE_SYSMENU_FONT
	if (gui_w32_get_menu_font(&lfSysmenu) == OK)
	    hfontTools = CreateFontIndirect(&lfSysmenu);
	else
#endif
	hfontTools = CreateFont(-DLG_FONT_POINT_SIZE, 0, 0, 0, 0, 0, 0, 0,
				0, 0, 0, 0, VARIABLE_PITCH , DLG_FONT_NAME);

	if (hfontTools)
	{
	    hdc = GetDC(s_hwnd);
	    SelectObject(hdc, hfontTools);
	    /*
	     * GetTextMetrics() doesn't return the right value in
	     * tmAveCharWidth, so we have to figure out the dialog base units
	     * ourselves.
	     */
	    GetTextExtentPoint(hdc,
		    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
		    52, &size);
	    ReleaseDC(s_hwnd, hdc);

	    s_dlgfntwidth = (WORD)((size.cx / 26 + 1) / 2);
	    s_dlgfntheight = (WORD)size.cy;
	    s_usenewlook = TRUE;
	}
    }

    if (!s_usenewlook)
    {
	dlgFontSize = GetDialogBaseUnits();	/* fall back to big old system*/
	s_dlgfntwidth = LOWORD(dlgFontSize);
	s_dlgfntheight = HIWORD(dlgFontSize);
    }
}

#if defined(FEAT_MENU) && defined(FEAT_TEAROFF)
/*
 * Create a pseudo-"tearoff menu" based on the child
 * items of a given menu pointer.
 */
    static void
gui_mch_tearoff(
    char_u	*title,
    vimmenu_T	*menu,
    int		initX,
    int		initY)
{
    WORD	*p, *pdlgtemplate, *pnumitems, *ptrueheight;
    int		template_len;
    int		nchar, textWidth, submenuWidth;
    DWORD	lStyle;
    DWORD	lExtendedStyle;
    WORD	dlgwidth;
    WORD	menuID;
    vimmenu_T	*pmenu;
    vimmenu_T	*the_menu = menu;
    HWND	hwnd;
    HDC		hdc;
    HFONT	font, oldFont;
    int		col, spaceWidth, len;
    int		columnWidths[2];
    char_u	*label, *text;
    int		acLen = 0;
    int		nameLen;
    int		padding0, padding1, padding2 = 0;
    int		sepPadding=0;
    int		x;
    int		y;
#ifdef USE_SYSMENU_FONT
    LOGFONT	lfSysmenu;
    int		use_lfSysmenu = FALSE;
#endif

    /*
     * If this menu is already torn off, move it to the mouse position.
     */
    if (IsWindow(menu->tearoff_handle))
    {
	POINT mp;
	if (GetCursorPos((LPPOINT)&mp))
	{
	    SetWindowPos(menu->tearoff_handle, NULL, mp.x, mp.y, 0, 0,
		    SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
	}
	return;
    }

    /*
     * Create a new tearoff.
     */
    if (*title == MNU_HIDDEN_CHAR)
	title++;

    /* Allocate memory to store the dialog template.  It's made bigger when
     * needed. */
    template_len = DLG_ALLOC_SIZE;
    pdlgtemplate = p = (WORD *)LocalAlloc(LPTR, template_len);
    if (p == NULL)
	return;

    hwnd = GetDesktopWindow();
    hdc = GetWindowDC(hwnd);
#ifdef USE_SYSMENU_FONT
    if (gui_w32_get_menu_font(&lfSysmenu) == OK)
    {
	font = CreateFontIndirect(&lfSysmenu);
	use_lfSysmenu = TRUE;
    }
    else
#endif
    font = CreateFont(-DLG_FONT_POINT_SIZE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
		      VARIABLE_PITCH , DLG_FONT_NAME);
    if (s_usenewlook)
	oldFont = SelectFont(hdc, font);
    else
	oldFont = SelectFont(hdc, GetStockObject(SYSTEM_FONT));

    /* Calculate width of a single space.  Used for padding columns to the
     * right width. */
    spaceWidth = GetTextWidth(hdc, (char_u *)" ", 1);

    /* Figure out max width of the text column, the accelerator column and the
     * optional submenu column. */
    submenuWidth = 0;
    for (col = 0; col < 2; col++)
    {
	columnWidths[col] = 0;
	for (pmenu = menu->children; pmenu != NULL; pmenu = pmenu->next)
	{
	    /* Use "dname" here to compute the width of the visible text. */
	    text = (col == 0) ? pmenu->dname : pmenu->actext;
	    if (text != NULL && *text != NUL)
	    {
		textWidth = GetTextWidthEnc(hdc, text, (int)STRLEN(text));
		if (textWidth > columnWidths[col])
		    columnWidths[col] = textWidth;
	    }
	    if (pmenu->children != NULL)
		submenuWidth = TEAROFF_COLUMN_PADDING * spaceWidth;
	}
    }
    if (columnWidths[1] == 0)
    {
	/* no accelerators */
	if (submenuWidth != 0)
	    columnWidths[0] += submenuWidth;
	else
	    columnWidths[0] += spaceWidth;
    }
    else
    {
	/* there is an accelerator column */
	columnWidths[0] += TEAROFF_COLUMN_PADDING * spaceWidth;
	columnWidths[1] += submenuWidth;
    }

    /*
     * Now find the total width of our 'menu'.
     */
    textWidth = columnWidths[0] + columnWidths[1];
    if (submenuWidth != 0)
    {
	submenuWidth = GetTextWidth(hdc, (char_u *)TEAROFF_SUBMENU_LABEL,
					  (int)STRLEN(TEAROFF_SUBMENU_LABEL));
	textWidth += submenuWidth;
    }
    dlgwidth = GetTextWidthEnc(hdc, title, (int)STRLEN(title));
    if (textWidth > dlgwidth)
	dlgwidth = textWidth;
    dlgwidth += 2 * TEAROFF_PADDING_X + TEAROFF_BUTTON_PAD_X;

    /* W95 can't do thin dialogs, they look v. weird! */
    if (mch_windows95() && dlgwidth < TEAROFF_MIN_WIDTH)
	dlgwidth = TEAROFF_MIN_WIDTH;

    /* start to fill in the dlgtemplate information.  addressing by WORDs */
    if (s_usenewlook)
	lStyle = DS_MODALFRAME | WS_CAPTION| WS_SYSMENU |DS_SETFONT| WS_VISIBLE;
    else
	lStyle = DS_MODALFRAME | WS_CAPTION| WS_SYSMENU | WS_VISIBLE;

    lExtendedStyle = WS_EX_TOOLWINDOW|WS_EX_STATICEDGE;
    *p++ = LOWORD(lStyle);
    *p++ = HIWORD(lStyle);
    *p++ = LOWORD(lExtendedStyle);
    *p++ = HIWORD(lExtendedStyle);
    pnumitems = p;	/* save where the number of items must be stored */
    *p++ = 0;		// NumberOfItems(will change later)
    gui_mch_getmouse(&x, &y);
    if (initX == 0xffffL)
	*p++ = PixelToDialogX(x); // x
    else
	*p++ = PixelToDialogX(initX); // x
    if (initY == 0xffffL)
	*p++ = PixelToDialogY(y); // y
    else
	*p++ = PixelToDialogY(initY); // y
    *p++ = PixelToDialogX(dlgwidth);    // cx
    ptrueheight = p;
    *p++ = 0;		// dialog height: changed later anyway
    *p++ = 0;		// Menu
    *p++ = 0;		// Class

    /* copy the title of the dialog */
    nchar = nCopyAnsiToWideChar(p, ((*title)
				    ? (LPSTR)title
				    : (LPSTR)("Vim "VIM_VERSION_MEDIUM)));
    p += nchar;

    if (s_usenewlook)
    {
	/* do the font, since DS_3DLOOK doesn't work properly */
#ifdef USE_SYSMENU_FONT
	if (use_lfSysmenu)
	{
	    /* point size */
	    *p++ = -MulDiv(lfSysmenu.lfHeight, 72,
		    GetDeviceCaps(hdc, LOGPIXELSY));
	    nchar = nCopyAnsiToWideChar(p, TEXT(lfSysmenu.lfFaceName));
	}
	else
#endif
	{
	    *p++ = DLG_FONT_POINT_SIZE;		// point size
	    nchar = nCopyAnsiToWideChar (p, TEXT(DLG_FONT_NAME));
	}
	p += nchar;
    }

    /*
     * Loop over all the items in the menu.
     * But skip over the tearbar.
     */
    if (STRCMP(menu->children->name, TEAR_STRING) == 0)
	menu = menu->children->next;
    else
	menu = menu->children;
    for ( ; menu != NULL; menu = menu->next)
    {
	if (menu->modes == 0)	/* this menu has just been deleted */
	    continue;
	if (menu_is_separator(menu->dname))
	{
	    sepPadding += 3;
	    continue;
	}

	/* Check if there still is plenty of room in the template.  Make it
	 * larger when needed. */
	if (((char *)p - (char *)pdlgtemplate) + 1000 > template_len)
	{
	    WORD    *newp;

	    newp = (WORD *)LocalAlloc(LPTR, template_len + 4096);
	    if (newp != NULL)
	    {
		template_len += 4096;
		mch_memmove(newp, pdlgtemplate,
					    (char *)p - (char *)pdlgtemplate);
		p = newp + (p - pdlgtemplate);
		pnumitems = newp + (pnumitems - pdlgtemplate);
		ptrueheight = newp + (ptrueheight - pdlgtemplate);
		LocalFree(LocalHandle(pdlgtemplate));
		pdlgtemplate = newp;
	    }
	}

	/* Figure out minimal length of this menu label.  Use "name" for the
	 * actual text, "dname" for estimating the displayed size.  "name"
	 * has "&a" for mnemonic and includes the accelerator. */
	len = nameLen = (int)STRLEN(menu->name);
	padding0 = (columnWidths[0] - GetTextWidthEnc(hdc, menu->dname,
				      (int)STRLEN(menu->dname))) / spaceWidth;
	len += padding0;

	if (menu->actext != NULL)
	{
	    acLen = (int)STRLEN(menu->actext);
	    len += acLen;
	    textWidth = GetTextWidthEnc(hdc, menu->actext, acLen);
	}
	else
	    textWidth = 0;
	padding1 = (columnWidths[1] - textWidth) / spaceWidth;
	len += padding1;

	if (menu->children == NULL)
	{
	    padding2 = submenuWidth / spaceWidth;
	    len += padding2;
	    menuID = (WORD)(menu->id);
	}
	else
	{
	    len += (int)STRLEN(TEAROFF_SUBMENU_LABEL);
	    menuID = (WORD)((long_u)(menu->submenu_id) | (DWORD)0x8000);
	}

	/* Allocate menu label and fill it in */
	text = label = alloc((unsigned)len + 1);
	if (label == NULL)
	    break;

	vim_strncpy(text, menu->name, nameLen);
	text = vim_strchr(text, TAB);	    /* stop at TAB before actext */
	if (text == NULL)
	    text = label + nameLen;	    /* no actext, use whole name */
	while (padding0-- > 0)
	    *text++ = ' ';
	if (menu->actext != NULL)
	{
	    STRNCPY(text, menu->actext, acLen);
	    text += acLen;
	}
	while (padding1-- > 0)
	    *text++ = ' ';
	if (menu->children != NULL)
	{
	    STRCPY(text, TEAROFF_SUBMENU_LABEL);
	    text += STRLEN(TEAROFF_SUBMENU_LABEL);
	}
	else
	{
	    while (padding2-- > 0)
		*text++ = ' ';
	}
	*text = NUL;

	/*
	 * BS_LEFT will just be ignored on Win32s/NT3.5x - on
	 * W95/NT4 it makes the tear-off look more like a menu.
	 */
	p = add_dialog_element(p,
		BS_PUSHBUTTON|BS_LEFT,
		(WORD)PixelToDialogX(TEAROFF_PADDING_X),
		(WORD)(sepPadding + 1 + 13 * (*pnumitems)),
		(WORD)PixelToDialogX(dlgwidth - 2 * TEAROFF_PADDING_X),
		(WORD)12,
		menuID, (WORD)0x0080, (char *)label);
	vim_free(label);
	(*pnumitems)++;
    }

    *ptrueheight = (WORD)(sepPadding + 1 + 13 * (*pnumitems));


    /* show modelessly */
    the_menu->tearoff_handle = CreateDialogIndirect(
	    s_hinst,
	    (LPDLGTEMPLATE)pdlgtemplate,
	    s_hwnd,
	    (DLGPROC)tearoff_callback);

    LocalFree(LocalHandle(pdlgtemplate));
    SelectFont(hdc, oldFont);
    DeleteObject(font);
    ReleaseDC(hwnd, hdc);

    /*
     * Reassert ourselves as the active window.  This is so that after creating
     * a tearoff, the user doesn't have to click with the mouse just to start
     * typing again!
     */
    (void)SetActiveWindow(s_hwnd);

    /* make sure the right buttons are enabled */
    force_menu_update = TRUE;
}
#endif

#if defined(FEAT_TOOLBAR) || defined(PROTO)
#include "gui_w32_rc.h"

/* This not defined in older SDKs */
# ifndef TBSTYLE_FLAT
#  define TBSTYLE_FLAT		0x0800
# endif

/*
 * Create the toolbar, initially unpopulated.
 *  (just like the menu, there are no defaults, it's all
 *  set up through menu.vim)
 */
    static void
initialise_toolbar(void)
{
    InitCommonControls();
    s_toolbarhwnd = CreateToolbarEx(
		    s_hwnd,
		    WS_CHILD | TBSTYLE_TOOLTIPS | TBSTYLE_FLAT,
		    4000,		//any old big number
		    31,			//number of images in initial bitmap
		    s_hinst,
		    IDR_TOOLBAR1,	// id of initial bitmap
		    NULL,
		    0,			// initial number of buttons
		    TOOLBAR_BUTTON_WIDTH, //api guide is wrong!
		    TOOLBAR_BUTTON_HEIGHT,
		    TOOLBAR_BUTTON_WIDTH,
		    TOOLBAR_BUTTON_HEIGHT,
		    sizeof(TBBUTTON)
		    );
    s_toolbar_wndproc = SubclassWindow(s_toolbarhwnd, toolbar_wndproc);

    gui_mch_show_toolbar(vim_strchr(p_go, GO_TOOLBAR) != NULL);
}

    static LRESULT CALLBACK
toolbar_wndproc(
    HWND hwnd,
    UINT uMsg,
    WPARAM wParam,
    LPARAM lParam)
{
    HandleMouseHide(uMsg, lParam);
    return CallWindowProc(s_toolbar_wndproc, hwnd, uMsg, wParam, lParam);
}

    static int
get_toolbar_bitmap(vimmenu_T *menu)
{
    int i = -1;

    /*
     * Check user bitmaps first, unless builtin is specified.
     */
    if (!is_winnt_3() && !menu->icon_builtin)
    {
	char_u fname[MAXPATHL];
	HANDLE hbitmap = NULL;

	if (menu->iconfile != NULL)
	{
	    gui_find_iconfile(menu->iconfile, fname, "bmp");
	    hbitmap = LoadImage(
			NULL,
			(LPCSTR)fname,
			IMAGE_BITMAP,
			TOOLBAR_BUTTON_WIDTH,
			TOOLBAR_BUTTON_HEIGHT,
			LR_LOADFROMFILE |
			LR_LOADMAP3DCOLORS
			);
	}

	/*
	 * If the LoadImage call failed, or the "icon=" file
	 * didn't exist or wasn't specified, try the menu name
	 */
	if (hbitmap == NULL
		&& (gui_find_bitmap(
#ifdef FEAT_MULTI_LANG
			    menu->en_dname != NULL ? menu->en_dname :
#endif
					menu->dname, fname, "bmp") == OK))
	    hbitmap = LoadImage(
		    NULL,
		    (LPCSTR)fname,
		    IMAGE_BITMAP,
		    TOOLBAR_BUTTON_WIDTH,
		    TOOLBAR_BUTTON_HEIGHT,
		    LR_LOADFROMFILE |
		    LR_LOADMAP3DCOLORS
		);

	if (hbitmap != NULL)
	{
	    TBADDBITMAP tbAddBitmap;

	    tbAddBitmap.hInst = NULL;
	    tbAddBitmap.nID = (long_u)hbitmap;

	    i = (int)SendMessage(s_toolbarhwnd, TB_ADDBITMAP,
			    (WPARAM)1, (LPARAM)&tbAddBitmap);
	    /* i will be set to -1 if it fails */
	}
    }
    if (i == -1 && menu->iconidx >= 0 && menu->iconidx < TOOLBAR_BITMAP_COUNT)
	i = menu->iconidx;

    return i;
}
#endif

#if defined(FEAT_GUI_TABLINE) || defined(PROTO)
    static void
initialise_tabline(void)
{
    InitCommonControls();

    s_tabhwnd = CreateWindow(WC_TABCONTROL, "Vim tabline",
	    WS_CHILD|TCS_FOCUSNEVER|TCS_TOOLTIPS,
	    CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
	    CW_USEDEFAULT, s_hwnd, NULL, s_hinst, NULL);
    s_tabline_wndproc = SubclassWindow(s_tabhwnd, tabline_wndproc);

    gui.tabline_height = TABLINE_HEIGHT;

# ifdef USE_SYSMENU_FONT
    set_tabline_font();
# endif
}

    static LRESULT CALLBACK
tabline_wndproc(
    HWND hwnd,
    UINT uMsg,
    WPARAM wParam,
    LPARAM lParam)
{
    HandleMouseHide(uMsg, lParam);
    return CallWindowProc(s_tabline_wndproc, hwnd, uMsg, wParam, lParam);
}
#endif

#if defined(FEAT_OLE) || defined(FEAT_EVAL) || defined(PROTO)
/*
 * Make the GUI window come to the foreground.
 */
    void
gui_mch_set_foreground(void)
{
    if (IsIconic(s_hwnd))
	 SendMessage(s_hwnd, WM_SYSCOMMAND, SC_RESTORE, 0);
    SetForegroundWindow(s_hwnd);
}
#endif

#if defined(FEAT_MBYTE_IME) && defined(DYNAMIC_IME)
    static void
dyn_imm_load(void)
{
    hLibImm = vimLoadLib("imm32.dll");
    if (hLibImm == NULL)
	return;

    pImmGetCompositionStringA
	    = (void *)GetProcAddress(hLibImm, "ImmGetCompositionStringA");
    pImmGetCompositionStringW
	    = (void *)GetProcAddress(hLibImm, "ImmGetCompositionStringW");
    pImmGetContext
	    = (void *)GetProcAddress(hLibImm, "ImmGetContext");
    pImmAssociateContext
	    = (void *)GetProcAddress(hLibImm, "ImmAssociateContext");
    pImmReleaseContext
	    = (void *)GetProcAddress(hLibImm, "ImmReleaseContext");
    pImmGetOpenStatus
	    = (void *)GetProcAddress(hLibImm, "ImmGetOpenStatus");
    pImmSetOpenStatus
	    = (void *)GetProcAddress(hLibImm, "ImmSetOpenStatus");
    pImmGetCompositionFont
	    = (void *)GetProcAddress(hLibImm, "ImmGetCompositionFontA");
    pImmSetCompositionFont
	    = (void *)GetProcAddress(hLibImm, "ImmSetCompositionFontA");
    pImmSetCompositionWindow
	    = (void *)GetProcAddress(hLibImm, "ImmSetCompositionWindow");
    pImmGetConversionStatus
	    = (void *)GetProcAddress(hLibImm, "ImmGetConversionStatus");
    pImmSetConversionStatus
	    = (void *)GetProcAddress(hLibImm, "ImmSetConversionStatus");

    if (       pImmGetCompositionStringA == NULL
	    || pImmGetCompositionStringW == NULL
	    || pImmGetContext == NULL
	    || pImmAssociateContext == NULL
	    || pImmReleaseContext == NULL
	    || pImmGetOpenStatus == NULL
	    || pImmSetOpenStatus == NULL
	    || pImmGetCompositionFont == NULL
	    || pImmSetCompositionFont == NULL
	    || pImmSetCompositionWindow == NULL
	    || pImmGetConversionStatus == NULL
	    || pImmSetConversionStatus == NULL)
    {
	FreeLibrary(hLibImm);
	hLibImm = NULL;
	pImmGetContext = NULL;
	return;
    }

    return;
}

#endif

#if defined(FEAT_SIGN_ICONS) || defined(PROTO)

# ifdef FEAT_XPM_W32
#  define IMAGE_XPM   100
# endif

typedef struct _signicon_t
{
    HANDLE	hImage;
    UINT	uType;
#ifdef FEAT_XPM_W32
    HANDLE	hShape;	/* Mask bitmap handle */
#endif
} signicon_t;

    void
gui_mch_drawsign(int row, int col, int typenr)
{
    signicon_t *sign;
    int x, y, w, h;

    if (!gui.in_use || (sign = (signicon_t *)sign_get_image(typenr)) == NULL)
	return;

    x = TEXT_X(col);
    y = TEXT_Y(row);
    w = gui.char_width * 2;
    h = gui.char_height;
    switch (sign->uType)
    {
	case IMAGE_BITMAP:
	    {
		HDC hdcMem;
		HBITMAP hbmpOld;

		hdcMem = CreateCompatibleDC(s_hdc);
		hbmpOld = (HBITMAP)SelectObject(hdcMem, sign->hImage);
		BitBlt(s_hdc, x, y, w, h, hdcMem, 0, 0, SRCCOPY);
		SelectObject(hdcMem, hbmpOld);
		DeleteDC(hdcMem);
	    }
	    break;
	case IMAGE_ICON:
	case IMAGE_CURSOR:
	    DrawIconEx(s_hdc, x, y, (HICON)sign->hImage, w, h, 0, NULL, DI_NORMAL);
	    break;
#ifdef FEAT_XPM_W32
	case IMAGE_XPM:
	    {
		HDC hdcMem;
		HBITMAP hbmpOld;

		hdcMem = CreateCompatibleDC(s_hdc);
		hbmpOld = (HBITMAP)SelectObject(hdcMem, sign->hShape);
		/* Make hole */
		BitBlt(s_hdc, x, y, w, h, hdcMem, 0, 0, SRCAND);

		SelectObject(hdcMem, sign->hImage);
		/* Paint sign */
		BitBlt(s_hdc, x, y, w, h, hdcMem, 0, 0, SRCPAINT);
		SelectObject(hdcMem, hbmpOld);
		DeleteDC(hdcMem);
	    }
	    break;
#endif
    }
}

    static void
close_signicon_image(signicon_t *sign)
{
    if (sign)
	switch (sign->uType)
	{
	    case IMAGE_BITMAP:
		DeleteObject((HGDIOBJ)sign->hImage);
		break;
	    case IMAGE_CURSOR:
		DestroyCursor((HCURSOR)sign->hImage);
		break;
	    case IMAGE_ICON:
		DestroyIcon((HICON)sign->hImage);
		break;
#ifdef FEAT_XPM_W32
	    case IMAGE_XPM:
		DeleteObject((HBITMAP)sign->hImage);
		DeleteObject((HBITMAP)sign->hShape);
		break;
#endif
	}
}

    void *
gui_mch_register_sign(char_u *signfile)
{
    signicon_t	sign, *psign;
    char_u	*ext;

    if (is_winnt_3())
    {
	EMSG(_(e_signdata));
	return NULL;
    }

    sign.hImage = NULL;
    ext = signfile + STRLEN(signfile) - 4; /* get extension */
    if (ext > signfile)
    {
	int do_load = 1;

	if (!STRICMP(ext, ".bmp"))
	    sign.uType =  IMAGE_BITMAP;
	else if (!STRICMP(ext, ".ico"))
	    sign.uType =  IMAGE_ICON;
	else if (!STRICMP(ext, ".cur") || !STRICMP(ext, ".ani"))
	    sign.uType =  IMAGE_CURSOR;
	else
	    do_load = 0;

	if (do_load)
	    sign.hImage = (HANDLE)LoadImage(NULL, (LPCSTR)signfile, sign.uType,
		    gui.char_width * 2, gui.char_height,
		    LR_LOADFROMFILE | LR_CREATEDIBSECTION);
#ifdef FEAT_XPM_W32
	if (!STRICMP(ext, ".xpm"))
	{
	    sign.uType = IMAGE_XPM;
	    LoadXpmImage((char *)signfile, (HBITMAP *)&sign.hImage,
		    (HBITMAP *)&sign.hShape);
	}
#endif
    }

    psign = NULL;
    if (sign.hImage && (psign = (signicon_t *)alloc(sizeof(signicon_t)))
								      != NULL)
	*psign = sign;

    if (!psign)
    {
	if (sign.hImage)
	    close_signicon_image(&sign);
	EMSG(_(e_signdata));
    }
    return (void *)psign;

}

    void
gui_mch_destroy_sign(void *sign)
{
    if (sign)
    {
	close_signicon_image((signicon_t *)sign);
	vim_free(sign);
    }
}
#endif

#if defined(FEAT_BEVAL) || defined(PROTO)

/* BALLOON-EVAL IMPLEMENTATION FOR WINDOWS.
 *  Added by Sergey Khorev <sergey.khorev@gmail.com>
 *
 * The only reused thing is gui_beval.h and get_beval_info()
 * from gui_beval.c (note it uses x and y of the BalloonEval struct
 * to get current mouse position).
 *
 * Trying to use as more Windows services as possible, and as less
 * IE version as possible :)).
 *
 * 1) Don't create ToolTip in gui_mch_create_beval_area, only initialize
 * BalloonEval struct.
 * 2) Enable/Disable simply create/kill BalloonEval Timer
 * 3) When there was enough inactivity, timer procedure posts
 * async request to debugger
 * 4) gui_mch_post_balloon (invoked from netbeans.c) creates tooltip control
 * and performs some actions to show it ASAP
 * 5) WM_NOTIFY:TTN_POP destroys created tooltip
 */

/*
 * determine whether installed Common Controls support multiline tooltips
 * (i.e. their version is >= 4.70
 */
    int
multiline_balloon_available(void)
{
    HINSTANCE hDll;
    static char comctl_dll[] = "comctl32.dll";
    static int multiline_tip = MAYBE;

    if (multiline_tip != MAYBE)
	return multiline_tip;

    hDll = GetModuleHandle(comctl_dll);
    if (hDll != NULL)
    {
	DLLGETVERSIONPROC pGetVer;
	pGetVer = (DLLGETVERSIONPROC)GetProcAddress(hDll, "DllGetVersion");

	if (pGetVer != NULL)
	{
	    DLLVERSIONINFO dvi;
	    HRESULT hr;

	    ZeroMemory(&dvi, sizeof(dvi));
	    dvi.cbSize = sizeof(dvi);

	    hr = (*pGetVer)(&dvi);

	    if (SUCCEEDED(hr)
		    && (dvi.dwMajorVersion > 4
			|| (dvi.dwMajorVersion == 4
						&& dvi.dwMinorVersion >= 70)))
	    {
		multiline_tip = TRUE;
		return multiline_tip;
	    }
	}
	else
	{
	    /* there is chance we have ancient CommCtl 4.70
	       which doesn't export DllGetVersion */
	    DWORD dwHandle = 0;
	    DWORD len = GetFileVersionInfoSize(comctl_dll, &dwHandle);
	    if (len > 0)
	    {
		VS_FIXEDFILEINFO *ver;
		UINT vlen = 0;
		void *data = alloc(len);

		if ((data != NULL
			&& GetFileVersionInfo(comctl_dll, 0, len, data)
			&& VerQueryValue(data, "\\", (void **)&ver, &vlen)
			&& vlen
			&& HIWORD(ver->dwFileVersionMS) > 4)
			|| ((HIWORD(ver->dwFileVersionMS) == 4
			    && LOWORD(ver->dwFileVersionMS) >= 70)))
		{
		    vim_free(data);
		    multiline_tip = TRUE;
		    return multiline_tip;
		}
		vim_free(data);
	    }
	}
    }
    multiline_tip = FALSE;
    return multiline_tip;
}

    static void
make_tooltip(BalloonEval *beval, char *text, POINT pt)
{
    TOOLINFO	*pti;
    int		ToolInfoSize;

    if (multiline_balloon_available() == TRUE)
	ToolInfoSize = sizeof(TOOLINFO_NEW);
    else
	ToolInfoSize = sizeof(TOOLINFO);

    pti = (TOOLINFO *)alloc(ToolInfoSize);
    if (pti == NULL)
	return;

    beval->balloon = CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS,
	    NULL, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,
	    CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
	    beval->target, NULL, s_hinst, NULL);

    SetWindowPos(beval->balloon, HWND_TOPMOST, 0, 0, 0, 0,
	    SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);

    pti->cbSize = ToolInfoSize;
    pti->uFlags = TTF_SUBCLASS;
    pti->hwnd = beval->target;
    pti->hinst = 0; /* Don't use string resources */
    pti->uId = ID_BEVAL_TOOLTIP;

    if (multiline_balloon_available() == TRUE)
    {
	RECT rect;
	TOOLINFO_NEW *ptin = (TOOLINFO_NEW *)pti;
	pti->lpszText = LPSTR_TEXTCALLBACK;
	ptin->lParam = (LPARAM)text;
	if (GetClientRect(s_textArea, &rect)) /* switch multiline tooltips on */
	    SendMessage(beval->balloon, TTM_SETMAXTIPWIDTH, 0,
		    (LPARAM)rect.right);
    }
    else
	pti->lpszText = text; /* do this old way */

    /* Limit ballooneval bounding rect to CursorPos neighbourhood */
    pti->rect.left = pt.x - 3;
    pti->rect.top = pt.y - 3;
    pti->rect.right = pt.x + 3;
    pti->rect.bottom = pt.y + 3;

    SendMessage(beval->balloon, TTM_ADDTOOL, 0, (LPARAM)pti);
    /* Make tooltip appear sooner */
    SendMessage(beval->balloon, TTM_SETDELAYTIME, TTDT_INITIAL, 10);
    /* I've performed some tests and it seems the longest possible life time
     * of tooltip is 30 seconds */
    SendMessage(beval->balloon, TTM_SETDELAYTIME, TTDT_AUTOPOP, 30000);
    /*
     * HACK: force tooltip to appear, because it'll not appear until
     * first mouse move. D*mn M$
     * Amazingly moving (2, 2) and then (-1, -1) the mouse doesn't move.
     */
    mouse_event(MOUSEEVENTF_MOVE, 2, 2, 0, 0);
    mouse_event(MOUSEEVENTF_MOVE, (DWORD)-1, (DWORD)-1, 0, 0);
    vim_free(pti);
}

    static void
delete_tooltip(BalloonEval *beval)
{
    PostMessage(beval->balloon, WM_CLOSE, 0, 0);
}

/*ARGSUSED*/
    static VOID CALLBACK
BevalTimerProc(
    HWND    hwnd,
    UINT    uMsg,
    UINT_PTR    idEvent,
    DWORD   dwTime)
{
    POINT	pt;
    RECT	rect;

    if (cur_beval == NULL || cur_beval->showState == ShS_SHOWING || !p_beval)
	return;

    GetCursorPos(&pt);
    if (WindowFromPoint(pt) != s_textArea)
	return;

    ScreenToClient(s_textArea, &pt);
    GetClientRect(s_textArea, &rect);
    if (!PtInRect(&rect, pt))
	return;

    if (LastActivity > 0
	    && (dwTime - LastActivity) >= (DWORD)p_bdlay
	    && (cur_beval->showState != ShS_PENDING
		|| abs(cur_beval->x - pt.x) > 3
		|| abs(cur_beval->y - pt.y) > 3))
    {
	/* Pointer resting in one place long enough, it's time to show
	 * the tooltip. */
	cur_beval->showState = ShS_PENDING;
	cur_beval->x = pt.x;
	cur_beval->y = pt.y;

	// TRACE0("BevalTimerProc: sending request");

	if (cur_beval->msgCB != NULL)
	    (*cur_beval->msgCB)(cur_beval, 0);
    }
}

/*ARGSUSED*/
    void
gui_mch_disable_beval_area(BalloonEval *beval)
{
    // TRACE0("gui_mch_disable_beval_area {{{");
    KillTimer(s_textArea, BevalTimerId);
    // TRACE0("gui_mch_disable_beval_area }}}");
}

/*ARGSUSED*/
    void
gui_mch_enable_beval_area(BalloonEval *beval)
{
    // TRACE0("gui_mch_enable_beval_area |||");
    if (beval == NULL)
	return;
    // TRACE0("gui_mch_enable_beval_area {{{");
    BevalTimerId = SetTimer(s_textArea, 0, (UINT)(p_bdlay / 2), BevalTimerProc);
    // TRACE0("gui_mch_enable_beval_area }}}");
}

    void
gui_mch_post_balloon(BalloonEval *beval, char_u *mesg)
{
    POINT   pt;
    // TRACE0("gui_mch_post_balloon {{{");
    if (beval->showState == ShS_SHOWING)
	return;
    GetCursorPos(&pt);
    ScreenToClient(s_textArea, &pt);

    if (abs(beval->x - pt.x) < 3 && abs(beval->y - pt.y) < 3)
	/* cursor is still here */
    {
	gui_mch_disable_beval_area(cur_beval);
	beval->showState = ShS_SHOWING;
	make_tooltip(beval, (char *)mesg, pt);
    }
    // TRACE0("gui_mch_post_balloon }}}");
}

/*ARGSUSED*/
    BalloonEval *
gui_mch_create_beval_area(
    void	*target,	/* ignored, always use s_textArea */
    char_u	*mesg,
    void	(*mesgCB)(BalloonEval *, int),
    void	*clientData)
{
    /* partially stolen from gui_beval.c */
    BalloonEval	*beval;

    if (mesg != NULL && mesgCB != NULL)
    {
	EMSG(_("E232: Cannot create BalloonEval with both message and callback"));
	return NULL;
    }

    beval = (BalloonEval *)alloc(sizeof(BalloonEval));
    if (beval != NULL)
    {
	beval->target = s_textArea;
	beval->balloon = NULL;

	beval->showState = ShS_NEUTRAL;
	beval->x = 0;
	beval->y = 0;
	beval->msg = mesg;
	beval->msgCB = mesgCB;
	beval->clientData = clientData;

	InitCommonControls();
	cur_beval = beval;

	if (p_beval)
	    gui_mch_enable_beval_area(beval);

    }
    return beval;
}

/*ARGSUSED*/
    static void
Handle_WM_Notify(HWND hwnd, LPNMHDR pnmh)
{
    if (pnmh->idFrom != ID_BEVAL_TOOLTIP) /* it is not our tooltip */
	return;

    if (cur_beval != NULL)
    {
	switch (pnmh->code)
	{
	case TTN_SHOW:
	    // TRACE0("TTN_SHOW {{{");
	    // TRACE0("TTN_SHOW }}}");
	    break;
	case TTN_POP: /* Before tooltip disappear */
	    // TRACE0("TTN_POP {{{");
	    delete_tooltip(cur_beval);
	    gui_mch_enable_beval_area(cur_beval);
	    // TRACE0("TTN_POP }}}");

	    cur_beval->showState = ShS_NEUTRAL;
	    break;
	case TTN_GETDISPINFO:
	    {
		/* if you get there then we have new common controls */
		NMTTDISPINFO_NEW *info = (NMTTDISPINFO_NEW *)pnmh;
		info->lpszText = (LPSTR)info->lParam;
		info->uFlags |= TTF_DI_SETITEM;
	    }
	    break;
	}
    }
}

    static void
TrackUserActivity(UINT uMsg)
{
    if ((uMsg >= WM_MOUSEFIRST && uMsg <= WM_MOUSELAST)
	    || (uMsg >= WM_KEYFIRST && uMsg <= WM_KEYLAST))
	LastActivity = GetTickCount();
}

    void
gui_mch_destroy_beval_area(BalloonEval *beval)
{
    vim_free(beval);
}
#endif /* FEAT_BEVAL */

#if defined(FEAT_NETBEANS_INTG) || defined(PROTO)
/*
 * We have multiple signs to draw at the same location. Draw the
 * multi-sign indicator (down-arrow) instead. This is the Win32 version.
 */
    void
netbeans_draw_multisign_indicator(int row)
{
    int i;
    int y;
    int x;

    if (!netbeans_active())
	return;

    x = 0;
    y = TEXT_Y(row);

    for (i = 0; i < gui.char_height - 3; i++)
	SetPixel(s_hdc, x+2, y++, gui.currFgColor);

    SetPixel(s_hdc, x+0, y, gui.currFgColor);
    SetPixel(s_hdc, x+2, y, gui.currFgColor);
    SetPixel(s_hdc, x+4, y++, gui.currFgColor);
    SetPixel(s_hdc, x+1, y, gui.currFgColor);
    SetPixel(s_hdc, x+2, y, gui.currFgColor);
    SetPixel(s_hdc, x+3, y++, gui.currFgColor);
    SetPixel(s_hdc, x+2, y, gui.currFgColor);
}
#endif