text: /* 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 && enc_utf8)
  static int directx_enabled(void);
  static void directx_binddc(void);
  #endif
  
  #ifdef FEAT_MENU
  static int gui_mswin_get_menu_height(int fix_window);
  #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 if (STRCMP(name, "scrlines") == 0)
  	{
  	    /* Deprecated.  Simply ignore it. */
  	}
  	else
  	    return FAIL;
      }
  
      if (!gui.in_use)
  	return OK;  /* only checking the syntax of the value */
  
      /* 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_GUI) || 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 LOGFONTW[];
  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;
  typedef int COLORREF;
  typedef int HCURSOR;
  #endif
  
  #ifndef GET_X_LPARAM
  # define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp))
  #endif
  
  static void _OnPaint( HWND hwnd);
  static void fill_rect(const RECT *rcp, HBRUSH hbr, COLORREF color);
  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 FINDREPLACEW	s_findrep_struct;
  static HWND		s_findrep_hwnd = NULL;
  static int		s_findrep_is_find;	// TRUE for find dialog, FALSE
  						// for find/replace dialog
  #endif
  
  #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(FEAT_DIRECTX)
      static 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();
      directx_binddc();
      return s_dwc != NULL ? 1 : 0;
  }
  
      static void
  directx_binddc(void)
  {
      if (s_textArea != NULL)
      {
  	RECT	rect;
  	GetClientRect(s_textArea, &rect);
  	DWriteContext_BindDC(s_dwc, s_hdc, &rect);
      }
  }
  #endif
  
  /* use of WindowProc depends on Global IME */
  #define MyWindowProc vim_WindowProc
  
  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
  static UINT	surrogate_pending_ch = 0; // 0: no surrogate pending,
  					  // else a high surrogate
  
  #ifdef FEAT_BEVAL_GUI
  /* balloon-eval WM_NOTIFY_HANDLER */
  static void Handle_WM_Notify(HWND hwnd, LPNMHDR pnmh);
  static void TrackUserActivity(UINT uMsg);
  #endif
  
  /*
   * For control IME.
   *
   * These LOGFONTW used for IME.
   */
  #if defined(FEAT_MBYTE_IME) || defined(GLOBAL_IME)
  /* holds LOGFONTW for 'guifontwide' if available, otherwise 'guifont' */
  static LOGFONTW norm_logfont;
  #endif
  #ifdef FEAT_MBYTE_IME
  /* holds LOGFONTW for 'guifont' always. */
  static LOGFONTW sub_logfont;
  #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;
  }
  
      static VOID CALLBACK
  _OnBlinkTimer(
      HWND hwnd,
      UINT uMsg UNUSED,
      UINT idEvent,
      DWORD dwTime UNUSED)
  {
      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);
      }
      gui_mch_flush();
  }
  
      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(int may_call_gui_update_cursor)
  {
      gui_mswin_rm_blink_timer();
      if (blink_state == BLINK_OFF && may_call_gui_update_cursor)
      {
  	gui_update_cursor(TRUE, FALSE);
  	gui_mch_flush();
      }
      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);
  	gui_mch_flush();
      }
  }
  
  /*
   * Call-back routines.
   */
  
      static VOID CALLBACK
  _OnTimer(
      HWND hwnd,
      UINT uMsg UNUSED,
      UINT idEvent,
      DWORD dwTime UNUSED)
  {
      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;
  }
  
      static void
  _OnDeadChar(
      HWND hwnd UNUSED,
      UINT ch UNUSED,
      int cRepeat UNUSED)
  {
      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.
   * Because the Windows API uses UTF-16, we have to deal with surrogate
   * pairs; this is where we choose to deal with them: if "ch" is a high
   * surrogate, it will be stored, and the length returned will be zero; the next
   * char_to_string call will then include the high surrogate, decoding the pair
   * of UTF-16 code units to a single Unicode code point, presuming it is the
   * matching low surrogate.
   */
      static int
  char_to_string(int ch, char_u *string, int slen, int had_alt)
  {
      int		len;
      int		i;
      WCHAR	wstring[2];
      char_u	*ws = NULL;
  
      if (surrogate_pending_ch != 0)
      {
  	/* We don't guarantee ch is a low surrogate to match the high surrogate
  	 * we already have; it should be, but if it isn't, tough luck. */
  	wstring[0] = surrogate_pending_ch;
  	wstring[1] = ch;
  	surrogate_pending_ch = 0;
  	len = 2;
      }
      else if (ch >= 0xD800 && ch <= 0xDBFF)	/* high surrogate */
      {
  	/* We don't have the entire code point yet, only the first UTF-16 code
  	 * unit; so just remember it and use it in the next call. */
  	surrogate_pending_ch = ch;
  	return 0;
      }
      else
      {
  	wstring[0] = ch;
  	len = 1;
      }
  
      /* "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
      {
  	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)
      {
  	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.
   */
      static void
  _OnChar(
      HWND hwnd UNUSED,
      UINT ch,
      int cRepeat UNUSED)
  {
      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.
   */
      static void
  _OnSysChar(
      HWND hwnd UNUSED,
      UINT cch,
      int cRepeat UNUSED)
  {
      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, TRUE, NULL);
      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);
  }
  
      static void
  _OnMouseButtonDown(
      HWND hwnd UNUSED,
      BOOL fDoubleClick UNUSED,
      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;
      }
  }
  
      static void
  _OnMouseMoveOrRelease(
      HWND hwnd UNUSED,
      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);
  }
  
      static void
  _OnSizeTextArea(
      HWND hwnd UNUSED,
      UINT state UNUSED,
      int cx UNUSED,
      int cy UNUSED)
  {
  #if defined(FEAT_DIRECTX)
      if (IS_ENABLE_DIRECTX())
  	directx_binddc();
  #endif
  }
  
  #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;
  }
  
      static void
  _OnMenu(
      HWND	hwnd UNUSED,
      int		id,
      HWND	hwndCtl UNUSED,
      UINT	codeNotify UNUSED)
  {
      vimmenu_T	*pMenu;
  
      pMenu = gui_mswin_find_menu(root_menu, id);
      if (pMenu)
  	gui_menu_cb(pMenu);
  }
  #endif
  
  #ifdef MSWIN_FIND_REPLACE
  /*
   * Handle a Find/Replace window message.
   */
      static void
  _OnFindRepl(void)
  {
      int	    flags = 0;
      int	    down;
  
      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)
      {
  	char_u	*p, *q;
  
  	/* 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;
  	p = utf16_to_enc(s_findrep_struct.lpstrFindWhat, NULL);
  	q = utf16_to_enc(s_findrep_struct.lpstrReplaceWith, NULL);
  	if (p != NULL && q != NULL)
  	    gui_do_findrepl(flags, p, q, down);
  	vim_free(p);
  	vim_free(q);
      }
  }
  #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_GUI
      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);
  	HANDLE_MSG(hwnd, WM_SIZE,	_OnSizeTextArea);
  
  #ifdef FEAT_BEVAL_GUI
  	case WM_NOTIFY: Handle_WM_Notify(hwnd, (LPNMHDR)lParam);
  	    return TRUE;
  #endif
  	default:
  	    return MyWindowProc(hwnd, uMsg, wParam, lParam);
      }
  }
  
  #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
      return DefWindowProcW(hwnd, message, wParam, lParam);
  #endif
  }
  
  /*
   * Called when the foreground or background color has been changed.
   */
      void
  gui_mch_new_colors(void)
  {
      HBRUSH prevBrush;
  
      s_brush = CreateSolidBrush(gui.back_pixel);
  #ifdef SetClassLongPtr
      prevBrush = (HBRUSH)SetClassLongPtr(
  				s_hwnd, GCLP_HBRBACKGROUND, (LONG_PTR)s_brush);
  #else
      prevBrush = (HBRUSH)SetClassLong(
  				   s_hwnd, GCL_HBRBACKGROUND, (long_u)s_brush);
  #endif
      InvalidateRect(s_hwnd, NULL, TRUE);
      DeleteObject(prevBrush);
  }
  
  /*
   * 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)
  {
      /* 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,
  	g_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);
      SIZE    size;
      TEXTMETRIC tm;
  
      GetTextMetrics(hdc, &tm);
      // GetTextMetrics() may not return the right value in tmAveCharWidth
      // for some fonts.  Do our own average computation.
      GetTextExtentPoint(hdc,
  	    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
  	    52, &size);
      gui.char_width = (size.cx / 26 + 1) / 2 + 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(LOGFONTW *lf)
  {
      HFONT   font = NULL;
  
      /* Load the font */
      font = CreateFontIndirectW(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)
  {
      LOGFONTW	lf;
      GuiFont	font = NOFONT;
  
      if (get_logfont(&lf, name, NULL, giveErrorIfMissing) == OK)
  	font = get_font_handle(&lf);
      if (font == NOFONT && giveErrorIfMissing)
  	semsg(_(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.
   */
      char_u *
  gui_mch_get_fontname(GuiFont font UNUSED, 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[] =
      {
  	{"SYS_3DDKSHADOW", COLOR_3DDKSHADOW},
  	{"SYS_3DHILIGHT", COLOR_3DHILIGHT},
  #ifdef COLOR_3DHIGHLIGHT
  	{"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},
  	{"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);
  }
  
      guicolor_T
  gui_mch_get_rgb_color(int r, int g, int b)
  {
      return gui_get_rgb_color_cmn(r, g, b);
  }
  
  /*
   * 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;
  
  #if defined(FEAT_DIRECTX)
      if (IS_ENABLE_DIRECTX())
  	DWriteContext_Flush(s_dwc);
  #endif
  
      /*
       * 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;
  
  #if defined(FEAT_DIRECTX)
      if (IS_ENABLE_DIRECTX())
  	DWriteContext_Flush(s_dwc);
  #endif
  
      /*
       * 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;
      if (mb_lefthalve(gui.row, gui.col))
  	rc.right += gui.char_width;
      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)
  {
      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;
  
      fill_rect(&rc, NULL, color);
  }
  
  
  /*
   * 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;
  	    ctrl_break_was_pressed = 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;
  
  	// When called with "wtime" zero, just want one msec.
  	s_wait_timer = (UINT)SetTimer(NULL, 0, (UINT)(wtime == 0 ? 1 : 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(TRUE);
  	    focus = gui.in_focus;
  	}
  
  	if (s_need_activate)
  	{
  	    (void)SetForegroundWindow(s_hwnd);
  	    s_need_activate = FALSE;
  	}
  
  #ifdef FEAT_TIMERS
  	did_add_timer = FALSE;
  #endif
  #ifdef MESSAGE_QUEUE
  	// Check channel I/O while waiting for a message.
  	for (;;)
  	{
  	    MSG msg;
  
  	    parse_queued_messages();
  #ifdef FEAT_TIMERS
  	    if (did_add_timer)
  		break;
  #endif
  	    if (pPeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
  	    {
  		process_message();
  		break;
  	    }
  	    else if (input_available()
  		    || MsgWaitForMultipleObjects(0, NULL, FALSE, 100,
  						  QS_ALLINPUT) != WAIT_TIMEOUT)
  		break;
  	}
  #else
  	// 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();
  #endif
  
  	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
  }
  
      void
  gui_mch_set_menu_pos(
      int	    x UNUSED,
      int	    y UNUSED,
      int	    w UNUSED,
      int	    h UNUSED)
  {
      /* 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*/
  
  /*
   * 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;
  }
  
  /*
   * 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;
  
      wstr = enc_to_utf16(str, &wlen);
      if (wstr == NULL)
  	return 0;
  
      n = GetTextExtentPointW(hdc, wstr, wlen, &size);
      vim_free(wstr);
      if (n)
  	return size.cx;
      return 0;
  }
  
  static void get_work_area(RECT *spi_rect);
  
  /*
   * A quick little routine that will center one window over another, handy for
   * dialog boxes.  Taken from the Win32SDK samples and modified for multiple
   * monitors.
   */
      static BOOL
  CenterWindow(
      HWND hwndChild,
      HWND hwndParent)
  {
      HMONITOR	    mon;
      MONITORINFO	    moninfo;
      RECT	    rChild, rParent, rScreen;
      int		    wChild, hChild, wParent, hParent;
      int		    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))
  	get_work_area(&rParent);
      else
  	GetWindowRect(hwndParent, &rParent);
      wParent = rParent.right - rParent.left;
      hParent = rParent.bottom - rParent.top;
  
      moninfo.cbSize = sizeof(MONITORINFO);
      mon = MonitorFromWindow(hwndChild, MONITOR_DEFAULTTOPRIMARY);
      if (mon != NULL && GetMonitorInfo(mon, &moninfo))
      {
  	rScreen = moninfo.rcWork;
      }
      else
      {
  	hdc = GetDC(hwndChild);
  	rScreen.left = 0;
  	rScreen.top = 0;
  	rScreen.right = GetDeviceCaps(hdc, HORZRES);
  	rScreen.bottom = GetDeviceCaps(hdc, VERTRES);
  	ReleaseDC(hwndChild, hdc);
      }
  
      xNew = rParent.left + ((wParent - wChild) / 2);
      if (xNew < rScreen.left)
  	xNew = rScreen.left;
      else if ((xNew + wChild) > rScreen.right)
  	xNew = rScreen.right - wChild;
  
      yNew = rParent.top + ((hParent - hChild) / 2);
      if (yNew < rScreen.top)
  	yNew = rScreen.top;
      else if ((yNew + hChild) > rScreen.bottom)
  	yNew = rScreen.bottom - hChild;
  
      return SetWindowPos(hwndChild, NULL, xNew, yNew, 0, 0,
  						   SWP_NOSIZE | SWP_NOZORDER);
  }
  #endif /* FEAT_GUI_DIALOG */
  
  #if defined(FEAT_TOOLBAR) || defined(PROTO)
      void
  gui_mch_show_toolbar(int showit)
  {
      if (s_toolbarhwnd == NULL)
  	return;
  
      if (showit)
      {
  # ifndef TB_SETUNICODEFORMAT
      // For older compilers.  We assume this never changes.
  #  define TB_SETUNICODEFORMAT 0x2005
  # endif
  	// Enable unicode support
  	SendMessage(s_toolbarhwnd, TB_SETUNICODEFORMAT, (WPARAM)TRUE,
  								(LPARAM)0);
  	ShowWindow(s_toolbarhwnd, SW_SHOW);
      }
      else
  	ShowWindow(s_toolbarhwnd, SW_HIDE);
  }
  
  /* The 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)
  {
      WCHAR	    *wn;
      MENUITEMINFOW   infow;
  
      wn = enc_to_utf16(item_text, NULL);
      if (wn == NULL)
  	return;
  
      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);
      InsertMenuItemW(pmenu, item_id, FALSE, &infow);
      vim_free(wn);
  }
  
      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;
      WCHAR	*wstr = NULL;
  
      if (s_tabhwnd == NULL)
  	return;
  
  #ifndef CCM_SETUNICODEFORMAT
      /* For older compilers.  We assume this never changes. */
  # define CCM_SETUNICODEFORMAT 0x2005
  #endif
      // Enable unicode support
      SendMessage(s_tabhwnd, CCM_SETUNICODEFORMAT, (WPARAM)TRUE, (LPARAM)0);
  
      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;
  
  	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);
  	}
      }
  
      /* 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;
      int		fill_typebuf = FALSE;
      char_u	key_name[4];
  
      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++;
  	fill_typebuf = TRUE;
      }
      if (fill_typebuf)
      {
  	/* Put a NOP in the typeahead buffer so that the message will get
  	 * processed. */
  	key_name[0] = K_SPECIAL;
  	key_name[1] = KS_EXTRA;
  	key_name[2] = KE_NOP;
  	key_name[3] = NUL;
  #if defined(FEAT_CLIENTSERVER) || defined(FEAT_EVAL)
  	typebuf_was_filled = TRUE;
  #endif
  	(void)ins_typebuf(key_name, REMAP_NONE, 0, TRUE, FALSE);
      }
  }
  
  /*
   * 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)
      {
  	WCHAR *p = enc_to_utf16(entry_text, NULL);
  	if (p != NULL)
  	{
  	    int len = s_findrep_struct.wFindWhatLen - 1;
  
  	    wcsncpy(s_findrep_struct.lpstrFindWhat, p, len);
  	    s_findrep_struct.lpstrFindWhat[len] = NUL;
  	    vim_free(p);
  	}
      }
      vim_free(entry_text);
  }
  #endif
  
      static void
  set_window_title(HWND hwnd, char *title)
  {
      if (title != NULL)
      {
  	WCHAR	*wbuf;
  
  	/* Convert the title from 'encoding' to UTF-16. */
  	wbuf = (WCHAR *)enc_to_utf16((char_u *)title, NULL);
  	if (wbuf != NULL)
  	{
  	    SetWindowTextW(hwnd, wbuf);
  	    vim_free(wbuf);
  	}
      }
      else
  	(void)SetWindowTextW(hwnd, NULL);
  }
  
      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);
  	    s_findrep_hwnd = FindTextW((LPFINDREPLACEW) &s_findrep_struct);
  	}
  
  	set_window_title(s_findrep_hwnd, _("Find string"));
  	(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);
  	    s_findrep_hwnd = ReplaceTextW((LPFINDREPLACEW) &s_findrep_struct);
  	}
  
  	set_window_title(s_findrep_hwnd, _("Find & Replace"));
  	(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.
   */
      static void
  _OnClose(HWND hwnd UNUSED)
  {
      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);
  
  	/* 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;
  	}
  
  	if (!IsRectEmpty(&ps.rcPaint))
  	{
  	    gui_redraw(ps.rcPaint.left, ps.rcPaint.top,
  		    ps.rcPaint.right - ps.rcPaint.left + 1,
  		    ps.rcPaint.bottom - ps.rcPaint.top + 1);
  	}
  
  	EndPaint(hwnd, &ps);
      }
  }
  
      static void
  _OnSize(
      HWND hwnd,
      UINT state UNUSED,
      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);
  }
  
      void
  gui_mch_destroy_scrollbar(scrollbar_T *sb)
  {
      DestroyWindow(sb->id);
  }
  
  /*
   * 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;
  
  #if defined(FEAT_DIRECTX)
      if (IS_ENABLE_DIRECTX())
  	DWriteContext_Flush(s_dwc);
  #endif
  
      /*
       * 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;
  
      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);
  
  #if defined(FEAT_DIRECTX)
      if (IS_ENABLE_DIRECTX())
      {
  	DWriteContext_Scroll(s_dwc, 0, -num_lines * gui.char_height, &rc);
  	DWriteContext_Flush(s_dwc);
      }
      else
  #endif
      {
  	intel_gpu_workaround();
  	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;
  
      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);
  
  #if defined(FEAT_DIRECTX)
      if (IS_ENABLE_DIRECTX())
      {
  	DWriteContext_Scroll(s_dwc, 0, num_lines * gui.char_height, &rc);
  	DWriteContext_Flush(s_dwc);
      }
      else
  #endif
      {
  	intel_gpu_workaround();
  	/* 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);
  }
  
  
      void
  gui_mch_exit(int rc UNUSED)
  {
  #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(LOGFONTW lf)
  {
      char	*p;
      char	*res;
      char	*charset_name;
      char	*quality_name;
      char	*font_name;
      int		points;
  
      font_name = (char *)utf16_to_enc(lf.lfFaceName, NULL);
      if (font_name == NULL)
  	return NULL;
      charset_name = charset_id2name((int)lf.lfCharSet);
      quality_name = quality_id2name((int)lf.lfQuality);
  
      res = alloc(strlen(font_name) + 30
  		    + (charset_name == NULL ? 0 : strlen(charset_name) + 2)
  		    + (quality_name == NULL ? 0 : strlen(quality_name) + 2));
      if (res != NULL)
      {
  	p = res;
  	// make a normal font string out of the lf thing:
  	points = pixels_to_points(
  			 lf.lfHeight < 0 ? -lf.lfHeight : lf.lfHeight, TRUE);
  	if (lf.lfWeight == FW_NORMAL || lf.lfWeight == FW_BOLD)
  	    sprintf((char *)p, "%s:h%d", font_name, points);
  	else
  	    sprintf((char *)p, "%s:h%d:W%ld", font_name, points, lf.lfWeight);
  	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);
  	}
      }
  
      vim_free(font_name);
      return (char_u *)res;
  }
  
  
  #ifdef FEAT_MBYTE_IME
  /*
   * Set correct LOGFONTW to IME.  Use 'guifontwide' if available, otherwise use
   * 'guifont'
   */
      static void
  update_im_font(void)
  {
      LOGFONTW	lf_wide;
  
      if (p_guifontwide != NULL && *p_guifontwide != NUL
  	    && gui.wide_font != NOFONT
  	    && GetObjectW((HFONT)gui.wide_font, sizeof(lf_wide), &lf_wide))
  	norm_logfont = lf_wide;
      else
  	norm_logfont = sub_logfont;
      im_set_font(&norm_logfont);
  }
  #endif
  
  /*
   * Handler of gui.wide_font (p_guifontwide) changed notification.
   */
      void
  gui_mch_wide_font_changed(void)
  {
      LOGFONTW 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
  	&& GetObjectW((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);
  	    }
  	}
      }
  }
  
  /*
   * Initialise vim to use the font with the given name.
   * Return FAIL if the font could not be loaded, OK otherwise.
   */
      int
  gui_mch_init_font(char_u *font_name, int fontset UNUSED)
  {
      LOGFONTW	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;
  #endif
  #ifdef FEAT_MBYTE_IME
      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 or GO_KEEPWINSIZE
   * is set.  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
   */
      void
  gui_mch_settitle(
      char_u  *title,
      char_u  *icon UNUSED)
  {
      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 */
      IDC_NO,			/* no */
      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
  	SetClassLong(s_textArea, GCL_HCURSOR, (long_u)LoadCursor(NULL, idc));
  #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)
  /*
   * 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;
  }
  
  /*
   * 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.
   */
      char_u *
  gui_mch_browse(
  	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, *q;
  
      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)
  	return NULL;
  
      /* Give focus back to main window (when using MDI). */
      SetFocus(s_hwnd);
  
      /* Shorten the file name if possible */
      q = vim_strsave(shorten_fname1(p));
      vim_free(p);
      return q;
  }
  
  
  /*
   * 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"));
  }
  #endif /* FEAT_BROWSE */
  
      static void
  _OnDropFiles(
      HWND hwnd UNUSED,
      HDROP hDrop)
  {
  #define BUFPATHLEN _MAX_PATH
  #define DRAGQVAL 0xFFFFFFFF
      WCHAR   wszFile[BUFPATHLEN];
      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 = ALLOC_MULT(char_u *, cFiles);
  
      if (fnames != NULL)
  	for (i = 0; i < cFiles; ++i)
  	{
  	    if (DragQueryFileW(hDrop, i, wszFile, BUFPATHLEN) > 0)
  		fnames[i] = utf16_to_enc(wszFile, NULL);
  	    else
  	    {
  		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;
      }
  }
  
      static int
  _OnScroll(
      HWND hwnd UNUSED,
      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;
      SCROLLINFO	si;
  
      si.cbSize = sizeof(si);
      si.fMask = SIF_POS;
  
      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;
  
      si.nPos = (sb->scroll_shift > 0) ? val >> sb->scroll_shift : val;
      SetScrollInfo(hwndCtl, SB_CTL, &si, TRUE);
  
      /*
       * 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;
  
  	SetScrollInfo(id, SB_CTL, &si, TRUE);
      }
  
      /* 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;
  
      mch_disable_flush();
      gui_drag_scrollbar(sb, val, dragging);
      mch_enable_flush();
      gui_may_flush();
  
      s_busy_processing = FALSE;
      dont_scroll = dont_scroll_save;
  
      return 0;
  }
  
  
  #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 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_GUI
  # 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 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 struct tagTOOLINFOW_NEW
  {
      UINT       cbSize;
      UINT       uFlags;
      HWND       hwnd;
      UINT_PTR   uId;
      RECT       rect;
      HINSTANCE  hinst;
      LPWSTR     lpszText;
      LPARAM     lParam;
      void       *lpReserved;
  } TOOLINFOW_NEW;
  
  typedef struct tagNMTTDISPINFOW_NEW
  {
      NMHDR      hdr;
      LPWSTR     lpszText;
      WCHAR      szText[80];
      HINSTANCE  hinst;
      UINT       uFlags;
      LPARAM     lParam;
  } NMTTDISPINFOW_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_GUI) */
  
  #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
  
  typedef struct tagNMTTDISPINFOW {
      NMHDR	hdr;
      LPWSTR	lpszText;
      WCHAR	szText[80];
      HINSTANCE	hinst;
      UINT	uFlags;
      LPARAM	lParam;
  } NMTTDISPINFOW, *LPNMTTDISPINFOW;
  # 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_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 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, BOOL);
  #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 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 *pImmGetCompositionFontW)(HIMC, LPLOGFONTW);
  static BOOL (WINAPI *pImmSetCompositionFontW)(HIMC, LPLOGFONTW);
  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 pImmGetCompositionFontW  ImmGetCompositionFontW
  # define pImmSetCompositionFontW  ImmSetCompositionFontW
  # define pImmSetCompositionWindow ImmSetCompositionWindow
  # define pImmGetConversionStatus  ImmGetConversionStatus
  # define pImmSetConversionStatus  ImmSetConversionStatus
  #endif
  
  #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
      {
  	/*
  	 * 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"
  
      mouse_scroll_lines = 3;	/* reasonable default */
  
      /* if NT 4.0+ (or Win98) get scroll lines directly from system */
      SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0,
  	    &mouse_scroll_lines, 0);
  }
  
  
  /*
   * Intellimouse wheel handler.
   * Treat a mouse wheel event as if it were a scroll request.
   */
      static void
  _OnMouseWheel(
      HWND hwnd,
      short zDelta)
  {
      int i;
      int size;
      HWND hwndCtl;
      win_T *wp;
  
      if (mouse_scroll_lines == 0)
  	init_mouse_wheel();
  
      wp = gui_mouse_window(FIND_POPUP);
  
  #ifdef FEAT_TEXT_PROP
      if (wp != NULL && popup_is_popup(wp))
      {
  	cmdarg_T cap;
  	oparg_T	oa;
  
  	// Mouse hovers over popup window, scroll it if possible.
  	mouse_row = wp->w_winrow;
  	mouse_col = wp->w_wincol;
  	vim_memset(&cap, 0, sizeof(cap));
  	cap.arg = zDelta < 0 ? MSCR_UP : MSCR_DOWN;
  	cap.cmdchar = zDelta < 0 ? K_MOUSEUP : K_MOUSEDOWN;
  	clear_oparg(&oa);
  	cap.oap = &oa;
  	nv_mousescroll(&cap);
  	update_screen(0);
  	setcursor();
  	out_flush();
  	return;
      }
  #endif
  
      if (wp == NULL || !p_scf)
  	wp = curwin;
  
      if (wp->w_scrollbars[SBAR_RIGHT].id != 0)
  	hwndCtl = wp->w_scrollbars[SBAR_RIGHT].id;
      else if (wp->w_scrollbars[SBAR_LEFT].id != 0)
  	hwndCtl = wp->w_scrollbars[SBAR_LEFT].id;
      else
  	return;
      size = wp->w_height;
  
      mch_disable_flush();
      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);
      mch_enable_flush();
      gui_may_flush();
  }
  
  #ifdef USE_SYSMENU_FONT
  /*
   * Get Menu Font.
   * Return OK or FAIL.
   */
      static int
  gui_w32_get_menu_font(LOGFONTW *lf)
  {
      NONCLIENTMETRICSW nm;
  
      nm.cbSize = sizeof(NONCLIENTMETRICSW);
      if (!SystemParametersInfoW(
  	    SPI_GETNONCLIENTMETRICS,
  	    sizeof(NONCLIENTMETRICSW),
  	    &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)
  {
      LOGFONTW	lfSysmenu;
      HFONT	font;
      HWND	hwnd;
      HDC		hdc;
      HFONT	hfntOld;
      TEXTMETRIC	tm;
  
      if (gui_w32_get_menu_font(&lfSysmenu) != OK)
  	return;
  
      font = CreateFontIndirectW(&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)
  	{
  	    case TTN_GETDISPINFOW:
  	    case TTN_GETDISPINFO:
  		{
  		    LPNMHDR		hdr = (LPNMHDR)lParam;
  		    char_u		*str = NULL;
  		    static void		*tt_text = NULL;
  
  		    VIM_CLEAR(tt_text);
  
  # 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)
  		    {
  			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
  			{
  			    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((char *)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:
  #ifdef MSWIN_FIND_REPLACE
  	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)
      {
  	semsg(_("E671: Cannot find window title \"%s\""), title);
  	mch_exit(2);
      }
  }
  
  #ifndef FEAT_OLE
      static void
  ole_error(char *arg)
  {
      char buf[IOSIZE];
  
  # ifdef VIMDLL
      gui.in_use = mch_is_gui_executable();
  # endif
  
      /* 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
  
  #if defined(GUI_MAY_SPAWN) || defined(PROTO)
      static char *
  gvim_error(void)
  {
      char *msg = _("E988: GUI cannot be used. Cannot execute gvim.exe.");
  
      if (starting)
      {
  	mch_errmsg(msg);
  	mch_errmsg("\n");
  	mch_exit(2);
      }
      return msg;
  }
  
      char *
  gui_mch_do_spawn(char_u *arg)
  {
      int			len;
  # if defined(FEAT_SESSION) && defined(EXPERIMENTAL_GUI_CMD)
      char_u		*session = NULL;
      LPWSTR		tofree1 = NULL;
  # endif
      WCHAR		name[MAX_PATH];
      LPWSTR		cmd, newcmd = NULL, p, warg, tofree2 = NULL;
      STARTUPINFOW	si = {sizeof(si)};
      PROCESS_INFORMATION pi;
  
      if (!GetModuleFileNameW(g_hinst, name, MAX_PATH))
  	goto error;
      p = wcsrchr(name, L'\\');
      if (p == NULL)
  	goto error;
      // Replace the executable name from vim(d).exe to gvim(d).exe.
  # ifdef DEBUG
      wcscpy(p + 1, L"gvimd.exe");
  # else
      wcscpy(p + 1, L"gvim.exe");
  # endif
  
  # if defined(FEAT_SESSION) && defined(EXPERIMENTAL_GUI_CMD)
      if (starting)
  # endif
      {
  	// Pass the command line to the new process.
  	p = GetCommandLineW();
  	// Skip 1st argument.
  	while (*p && *p != L' ' && *p != L'\t')
  	{
  	    if (*p == L'"')
  	    {
  		while (*p && *p != L'"')
  		    ++p;
  		if (*p)
  		    ++p;
  	    }
  	    else
  		++p;
  	}
  	cmd = p;
      }
  # if defined(FEAT_SESSION) && defined(EXPERIMENTAL_GUI_CMD)
      else
      {
  	// Create a session file and pass it to the new process.
  	LPWSTR	wsession;
  	char_u	*savebg;
  	int	ret;
  
  	session = vim_tempname('s', FALSE);
  	if (session == NULL)
  	    goto error;
  	savebg = p_bg;
  	p_bg = vim_strsave((char_u *)"light");	// Set 'bg' to "light".
  	ret = write_session_file(session);
  	vim_free(p_bg);
  	p_bg = savebg;
  	if (!ret)
  	    goto error;
  	wsession = enc_to_utf16(session, NULL);
  	if (wsession == NULL)
  	    goto error;
  	len = (int)wcslen(wsession) * 2 + 27 + 1;
  	cmd = ALLOC_MULT(WCHAR, len);
  	if (cmd == NULL)
  	{
  	    vim_free(wsession);
  	    goto error;
  	}
  	tofree1 = cmd;
  	_snwprintf(cmd, len, L" -S \"%s\" -c \"call delete('%s')\"",
  		wsession, wsession);
  	vim_free(wsession);
      }
  # endif
  
      // Check additional arguments to the `:gui` command.
      if (arg != NULL)
      {
  	warg = enc_to_utf16(arg, NULL);
  	if (warg == NULL)
  	    goto error;
  	tofree2 = warg;
      }
      else
  	warg = L"";
  
      // Set up the new command line.
      len = (int)wcslen(name) + (int)wcslen(cmd) + (int)wcslen(warg) + 4;
      newcmd = ALLOC_MULT(WCHAR, len);
      if (newcmd == NULL)
  	goto error;
      _snwprintf(newcmd, len, L"\"%s\"%s %s", name, cmd, warg);
  
      // Spawn a new GUI process.
      if (!CreateProcessW(NULL, newcmd, NULL, NULL, TRUE, 0,
  		NULL, NULL, &si, &pi))
  	goto error;
      CloseHandle(pi.hProcess);
      CloseHandle(pi.hThread);
      mch_exit(0);
  
  error:
  # if defined(FEAT_SESSION) && defined(EXPERIMENTAL_GUI_CMD)
      if (session)
  	mch_remove(session);
      vim_free(session);
      vim_free(tofree1);
  # endif
      vim_free(newcmd);
      vim_free(tofree2);
      return gvim_error();
  }
  #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
  }
  
  /*
   * Initialise the GUI.	Create all the windows, set up all the call-backs
   * etc.
   */
      int
  gui_mch_init(void)
  {
      const WCHAR szVimWndClassW[] = VIM_CLASSW;
      const WCHAR szTextAreaClassW[] = L"VimTextArea";
      WNDCLASSW wndclassw;
  #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(g_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));
  
      /* First try using the wide version, so that we can use any title.
       * Otherwise only characters in the active codepage will work. */
      if (GetClassInfoW(g_hinst, szVimWndClassW, &wndclassw) == 0)
      {
  	wndclassw.style = CS_DBLCLKS;
  	wndclassw.lpfnWndProc = _WndProc;
  	wndclassw.cbClsExtra = 0;
  	wndclassw.cbWndExtra = 0;
  	wndclassw.hInstance = g_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)
  	    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 = CreateWindowExW(
  		WS_EX_MDICHILD,
  		szVimWndClassW, L"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,
  		g_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 = CreateWindowW(
  		szVimWndClassW, L"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,
  		g_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 */
      if (GetClassInfoW(g_hinst, szTextAreaClassW, &wndclassw) == 0)
      {
  	wndclassw.style = CS_OWNDC;
  	wndclassw.lpfnWndProc = _TextAreaWndProc;
  	wndclassw.cbClsExtra = 0;
  	wndclassw.cbWndExtra = 0;
  	wndclassw.hInstance = g_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;
      }
  
      s_textArea = CreateWindowExW(
  	0,
  	szTextAreaClassW, L"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,
  	g_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);
  
      DragAcceptFiles(s_hwnd, TRUE);
  
      /* 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 = ALLOC_MULT(WCHAR, MSWIN_FR_BUFSIZE);
      s_findrep_struct.lpstrFindWhat[0] = NUL;
      s_findrep_struct.lpstrReplaceWith = ALLOC_MULT(WCHAR, MSWIN_FR_BUFSIZE);
      s_findrep_struct.lpstrReplaceWith[0] = NUL;
      s_findrep_struct.wFindWhatLen = MSWIN_FR_BUFSIZE;
      s_findrep_struct.wReplaceWithLen = MSWIN_FR_BUFSIZE;
  #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;
  
      /* work out which monitor the window is on, and get *its* work area */
      mon = MonitorFromWindow(s_hwnd, MONITOR_DEFAULTTOPRIMARY);
      if (mon != NULL)
      {
  	moninfo.cbSize = sizeof(MONITORINFO);
  	if (GetMonitorInfo(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.
   */
      void
  gui_mch_set_shellsize(
  	int width,
  	int height,
  	int min_width UNUSED,
  	int min_height UNUSED,
  	int base_width UNUSED,
  	int base_height UNUSED,
  	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;
  }
  
  #ifdef 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 or MinGW. */
  # endif
  # include <imm.h>
  
  /*
   * handle WM_IME_NOTIFY message
   */
      static LRESULT
  _OnImeNotify(HWND hWnd, DWORD dwCommand, DWORD dwData UNUSED)
  {
      LRESULT lResult = 0;
      HIMC hImc;
  
      if (!pImmGetContext || (hImc = pImmGetContext(hWnd)) == (HIMC)0)
  	return lResult;
      switch (dwCommand)
      {
  	case IMN_SETOPENSTATUS:
  	    if (pImmGetOpenStatus(hImc))
  	    {
  		pImmSetCompositionFontW(hImc, &norm_logfont);
  		im_set_position(gui.row, gui.col);
  
  		/* Disable langmap */
  		State &= ~LANGMAP;
  		if (State & INSERT)
  		{
  #if 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);
  	    gui_mch_flush();
  	    lResult = 0;
  	    break;
      }
      pImmReleaseContext(hWnd, hImc);
      return lResult;
  }
  
      static LRESULT
  _OnImeComposition(HWND hwnd, WPARAM dbcs UNUSED, 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 = 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_IME) || defined(PROTO)
  
  /*
   * set font to IM.
   */
      void
  im_set_font(LOGFONTW *lf)
  {
      HIMC hImc;
  
      if (pImmGetContext && (hImc = pImmGetContext(s_hwnd)) != (HIMC)0)
      {
  	pImmSetCompositionFontW(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_IME */
  
  #if !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
  
  /*
   * 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;
      }
  }
  
  #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;
  
      for (ix = 0; ix < (int)len; ++ix)
  	ExtTextOut(s_hdc, col + TEXT_X(ix), row, foptions,
  					pcliprect, text + ix, 1, padding);
  }
  #endif
  
      static void
  draw_line(
      int		x1,
      int		y1,
      int		x2,
      int		y2,
      COLORREF	color)
  {
  #if defined(FEAT_DIRECTX)
      if (IS_ENABLE_DIRECTX())
  	DWriteContext_DrawLine(s_dwc, x1, y1, x2, y2, color);
      else
  #endif
      {
  	HPEN	hpen = CreatePen(PS_SOLID, 1, color);
  	HPEN	old_pen = SelectObject(s_hdc, hpen);
  	MoveToEx(s_hdc, x1, y1, NULL);
  	/* Note: LineTo() excludes the last pixel in the line. */
  	LineTo(s_hdc, x2, y2);
  	DeleteObject(SelectObject(s_hdc, old_pen));
      }
  }
  
      static void
  set_pixel(
      int		x,
      int		y,
      COLORREF	color)
  {
  #if defined(FEAT_DIRECTX)
      if (IS_ENABLE_DIRECTX())
  	DWriteContext_SetPixel(s_dwc, x, y, color);
      else
  #endif
  	SetPixel(s_hdc, x, y, color);
  }
  
      static void
  fill_rect(
      const RECT	*rcp,
      HBRUSH	hbr,
      COLORREF	color)
  {
  #if defined(FEAT_DIRECTX)
      if (IS_ENABLE_DIRECTX())
  	DWriteContext_FillRect(s_dwc, rcp, color);
      else
  #endif
      {
  	HBRUSH	hbr2;
  
  	if (hbr == NULL)
  	    hbr2 = CreateSolidBrush(color);
  	else
  	    hbr2 = hbr;
  	FillRect(s_hdc, rcp, hbr2);
  	if (hbr == NULL)
  	    DeleteBrush(hbr2);
      }
  }
  
      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;
      static WCHAR *unicodebuf = NULL;
      static int   *unicodepdy = NULL;
      static int	unibuflen = 0;
      int		n = 0;
      int		y;
  
      /*
       * 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);
  	if (has_mbyte)
  	{
  	    /* Compute the length in display cells. */
  	    rc.right = FILL_X(col + mb_string2cells(text, len));
  	}
  	else
  	    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;
  	}
  
  	fill_rect(&rc, hbr, gui.currBgColor);
  
  	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())
  	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 = LALLOC_MULT(int, pad_size);
  	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.
       */
  
      /* 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 (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 = LALLOC_MULT(WCHAR, len);
  
  	vim_free(unicodepdy);
  	unicodepdy = LALLOC_MULT(int, len);
  
  	unibuflen = len;
      }
  
      if (enc_utf8 && n < len && unicodebuf != NULL)
      {
  	/* Output UTF-8 characters.  Composing characters should be
  	 * handled here. */
  	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;
  	    }
  
  	    if (utf_iscomposing(c))
  		cw = 0;
  	    else
  	    {
  		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 += utf_ptr2len_len(text + i, len - i);
  	    ++clen;
  	}
  #if defined(FEAT_DIRECTX)
  	if (IS_ENABLE_DIRECTX())
  	{
  	    /* Add one to "cells" for italics. */
  	    DWriteContext_DrawText(s_dwc, unicodebuf, wlen,
  		    TEXT_X(col), TEXT_Y(row),
  		    FILL_X(cells + 1), FILL_Y(1) - p_linespace,
  		    gui.char_width, gui.currFgColor,
  		    foptions, pcliprect, unicodepdy);
  	}
  	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
      {
  #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)
      {
  	/* 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;
  	draw_line(FILL_X(col), y, FILL_X(col + len), y, gui.currFgColor);
      }
  
      /* Strikethrough */
      if (flags & DRAW_STRIKE)
      {
  	y = FILL_Y(row + 1) - gui.char_height/2;
  	draw_line(FILL_X(col), y, FILL_X(col + len), y, gui.currSpColor);
      }
  
      /* 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];
  	    set_pixel(x, y - offset, gui.currSpColor);
  	}
      }
  }
  
  
  /*
   * Output routines.
   */
  
  /* Flush any output to the screen */
      void
  gui_mch_flush(void)
  {
  #if defined(FEAT_DIRECTX)
      if (IS_ENABLE_DIRECTX())
  	DWriteContext_Flush(s_dwc);
  #endif
  
      GdiFlush();
  }
  
      static void
  clear_rect(RECT *rcp)
  {
      fill_rect(rcp, NULL, gui.back_pixel);
  }
  
  
      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))
      {
  	WCHAR	*wn;
  	MENUITEMINFOW	infow;
  
  	wn = enc_to_utf16(menu->name, NULL);
  	if (wn == NULL)
  	    return;
  
  	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;
  	InsertMenuItemW((parent == NULL)
  		? s_menuBar : parent->submenu_id,
  		(UINT)pos, TRUE, &infow);
  	vim_free(wn);
      }
  
      /* 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(curwin->w_wincol + 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
      {
  	WCHAR	*wn;
  
  	wn = enc_to_utf16(menu->name, NULL);
  	if (wn != NULL)
  	{
  	    InsertMenuW(parent->submenu_id, (UINT)idx,
  		    (menu_is_separator(menu->name)
  		     ? MF_SEPARATOR : MF_STRING) | MF_BYPOSITION,
  		    (UINT)menu->id, wn);
  	    vim_free(wn);
  	}
  #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.
   */
      static LRESULT CALLBACK
  dialog_callback(
      HWND hwnd,
      UINT message,
      WPARAM wParam,
      LPARAM lParam UNUSED)
  {
      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)
  	{
  	    WCHAR  *wp = ALLOC_MULT(WCHAR, IOSIZE);
  	    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);
  	}
  
  	/*
  	 * 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 UNUSED)
  {
      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
      LOGFONTW	lfSysmenu;
      int		use_lfSysmenu = FALSE;
  #endif
      garray_T	ga;
      int		l;
  
  #ifndef NO_CONSOLE
      /* Don't output anything in silent mode ("ex -s") */
  # ifdef VIMDLL
      if (!(gui.in_use || gui.starting))
  # endif
  	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 = ALLOC_MULT(int, numButtons);
      if (buttonWidths == NULL)
  	return -1;
  
      /* Allocate array to hold the X position of each button */
      buttonPositions = ALLOC_MULT(int, numButtons);
      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 = CreateFontIndirectW(&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'; )
  	{
  	    l = (*mb_ptr2len)(pend);
  	    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)), TRUE);
      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));
  	    wcscpy(p, lfSysmenu.lfFaceName);
  	    nchar = (int)wcslen(lfSysmenu.lfFaceName) + 1;
  	}
  	else
  #endif
  	{
  	    *p++ = DLG_FONT_POINT_SIZE;		// point size
  	    nchar = nCopyAnsiToWideChar(p, DLG_FONT_NAME, FALSE);
  	}
  	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(
  	    g_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, TRUE); //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.
   * If "use_enc" is TRUE, 'encoding' is used for "lpAnsiIn". If FALSE, current
   * ACP is used for "lpAnsiIn". */
      static int
  nCopyAnsiToWideChar(
      LPWORD lpWCStr,
      LPSTR lpAnsiIn,
      BOOL use_enc)
  {
      int		nChar = 0;
      int		len = lstrlen(lpAnsiIn) + 1;	/* include NUL character */
      int		i;
      WCHAR	*wn;
  
      if (use_enc && 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)' ';
  
      return nChar;
  }
  
  
  #ifdef FEAT_TEAROFF
  /*
   * Lookup menu handle from "menu_id".
   */
      static HMENU
  tearoff_lookup_menuhandle(
      vimmenu_T *menu,
      WORD menu_id)
  {
      for ( ; menu != NULL; menu = menu->next)
      {
  	if (menu->modes == 0)	/* this menu has just been deleted */
  	    continue;
  	if (menu_is_separator(menu->dname))
  	    continue;
  	if ((WORD)((long_u)(menu->submenu_id) | (DWORD)0x8000) == menu_id)
  	    return menu->submenu_id;
      }
      return NULL;
  }
  
  /*
   * 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)
      {
  	SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR)lParam);
  	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))
  	    {
  		vimmenu_T *menu;
  
  		menu = (vimmenu_T*)GetWindowLongPtr(hwnd, DWLP_USER);
  		(void)TrackPopupMenu(
  			 tearoff_lookup_menuhandle(menu, LOWORD(wParam)),
  			 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
      LOGFONTW	    lfSysmenu;
  #endif
  
      s_usenewlook = FALSE;
  
  #ifdef USE_SYSMENU_FONT
      if (gui_w32_get_menu_font(&lfSysmenu) == OK)
  	hfontTools = CreateFontIndirectW(&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	*top_menu;
      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
      LOGFONTW	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 = CreateFontIndirectW(&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;
  
      /* 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)), TRUE);
      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));
  	    wcscpy(p, lfSysmenu.lfFaceName);
  	    nchar = (int)wcslen(lfSysmenu.lfFaceName) + 1;
  	}
  	else
  #endif
  	{
  	    *p++ = DLG_FONT_POINT_SIZE;		// point size
  	    nchar = nCopyAnsiToWideChar(p, DLG_FONT_NAME, FALSE);
  	}
  	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;
      top_menu = menu;
      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(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 = CreateDialogIndirectParam(
  	    g_hinst,
  	    (LPDLGTEMPLATE)pdlgtemplate,
  	    s_hwnd,
  	    (DLGPROC)tearoff_callback,
  	    (LPARAM)top_menu);
  
      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
  		    g_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)
  		    );
  
      // Remove transparency from the toolbar to prevent the main window
      // background colour showing through
      SendMessage(s_toolbarhwnd, TB_SETSTYLE, 0,
  	SendMessage(s_toolbarhwnd, TB_GETSTYLE, 0, 0) & ~TBSTYLE_TRANSPARENT);
  
      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 (!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, g_hinst, NULL);
      s_tabline_wndproc = SubclassWindow(s_tabhwnd, tabline_wndproc);
  
      gui.tabline_height = TABLINE_HEIGHT;
  
  # ifdef USE_SYSMENU_FONT
      set_tabline_font();
  # endif
  }
  
  /*
   * Get tabpage_T from POINT.
   */
      static tabpage_T *
  GetTabFromPoint(
      HWND    hWnd,
      POINT   pt)
  {
      tabpage_T	*ptp = NULL;
  
      if (gui_mch_showing_tabline())
      {
  	TCHITTESTINFO htinfo;
  	htinfo.pt = pt;
  	/* ignore if a window under cusor is not tabcontrol. */
  	if (s_tabhwnd == hWnd)
  	{
  	    int idx = TabCtrl_HitTest(s_tabhwnd, &htinfo);
  	    if (idx != -1)
  		ptp = find_tabpage(idx + 1);
  	}
      }
      return ptp;
  }
  
  static POINT	    s_pt = {0, 0};
  static HCURSOR      s_hCursor = NULL;
  
      static LRESULT CALLBACK
  tabline_wndproc(
      HWND hwnd,
      UINT uMsg,
      WPARAM wParam,
      LPARAM lParam)
  {
      POINT	pt;
      tabpage_T	*tp;
      RECT	rect;
      int		nCenter;
      int		idx0;
      int		idx1;
  
      HandleMouseHide(uMsg, lParam);
  
      switch (uMsg)
      {
  	case WM_LBUTTONDOWN:
  	    {
  		s_pt.x = GET_X_LPARAM(lParam);
  		s_pt.y = GET_Y_LPARAM(lParam);
  		SetCapture(hwnd);
  		s_hCursor = GetCursor(); /* backup default cursor */
  		break;
  	    }
  	case WM_MOUSEMOVE:
  	    if (GetCapture() == hwnd
  		    && ((wParam & MK_LBUTTON)) != 0)
  	    {
  		pt.x = GET_X_LPARAM(lParam);
  		pt.y = s_pt.y;
  		if (abs(pt.x - s_pt.x) > GetSystemMetrics(SM_CXDRAG))
  		{
  		    SetCursor(LoadCursor(NULL, IDC_SIZEWE));
  
  		    tp = GetTabFromPoint(hwnd, pt);
  		    if (tp != NULL)
  		    {
  			idx0 = tabpage_index(curtab) - 1;
  			idx1 = tabpage_index(tp) - 1;
  
  			TabCtrl_GetItemRect(hwnd, idx1, &rect);
  			nCenter = rect.left + (rect.right - rect.left) / 2;
  
  			/* Check if the mouse cursor goes over the center of
  			 * the next tab to prevent "flickering". */
  			if ((idx0 < idx1) && (nCenter < pt.x))
  			{
  			    tabpage_move(idx1 + 1);
  			    update_screen(0);
  			}
  			else if ((idx1 < idx0) && (pt.x < nCenter))
  			{
  			    tabpage_move(idx1);
  			    update_screen(0);
  			}
  		    }
  		}
  	    }
  	    break;
  	case WM_LBUTTONUP:
  	    {
  		if (GetCapture() == hwnd)
  		{
  		    SetCursor(s_hCursor);
  		    ReleaseCapture();
  		}
  		break;
  	    }
  	default:
  	    break;
      }
  
      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");
      pImmGetCompositionFontW
  	    = (void *)GetProcAddress(hLibImm, "ImmGetCompositionFontW");
      pImmSetCompositionFontW
  	    = (void *)GetProcAddress(hLibImm, "ImmSetCompositionFontW");
      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
  	    || pImmGetCompositionFontW == NULL
  	    || pImmSetCompositionFontW == 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;
  
  #if defined(FEAT_DIRECTX)
      if (IS_ENABLE_DIRECTX())
  	DWriteContext_Flush(s_dwc);
  #endif
  
      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;
  
      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 = ALLOC_ONE(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_GUI) || defined(PROTO)
  
  /* BALLOON-EVAL IMPLEMENTATION FOR WINDOWS.
   *  Added by Sergey Khorev <sergey.khorev@gmail.com>
   *
   * The only reused thing is 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)
  {
      TOOLINFOW	*pti;
      int		ToolInfoSize;
  
      if (multiline_balloon_available() == TRUE)
  	ToolInfoSize = sizeof(TOOLINFOW_NEW);
      else
  	ToolInfoSize = sizeof(TOOLINFOW);
  
      pti = alloc(ToolInfoSize);
      if (pti == NULL)
  	return;
  
      beval->balloon = CreateWindowExW(WS_EX_TOPMOST, TOOLTIPS_CLASSW,
  	    NULL, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,
  	    CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
  	    beval->target, NULL, g_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;
  	TOOLINFOW_NEW *ptin = (TOOLINFOW_NEW *)pti;
  	pti->lpszText = LPSTR_TEXTCALLBACKW;
  	beval->tofree = enc_to_utf16((char_u*)text, NULL);
  	ptin->lParam = (LPARAM)beval->tofree;
  	// switch multiline tooltips on
  	if (GetClientRect(s_textArea, &rect))
  	    SendMessageW(beval->balloon, TTM_SETMAXTIPWIDTH, 0,
  		    (LPARAM)rect.right);
      }
      else
      {
  	// do this old way
  	beval->tofree = enc_to_utf16((char_u*)text, NULL);
  	pti->lpszText = (LPWSTR)beval->tofree;
      }
  
      // 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;
  
      SendMessageW(beval->balloon, TTM_ADDTOOLW, 0, (LPARAM)pti);
      // Make tooltip appear sooner.
      SendMessageW(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.
      SendMessageW(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);
  }
  
      static VOID CALLBACK
  BevalTimerProc(
      HWND	hwnd UNUSED,
      UINT	uMsg UNUSED,
      UINT_PTR    idEvent UNUSED,
      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);
      }
  }
  
      void
  gui_mch_disable_beval_area(BalloonEval *beval UNUSED)
  {
      // TRACE0("gui_mch_disable_beval_area {{{");
      KillTimer(s_textArea, BevalTimerId);
      // TRACE0("gui_mch_disable_beval_area }}}");
  }
  
      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;
  
      vim_free(beval->msg);
      beval->msg = mesg == NULL ? NULL : vim_strsave(mesg);
      if (beval->msg == NULL)
      {
  	delete_tooltip(beval);
  	beval->showState = ShS_NEUTRAL;
  	return;
      }
  
      // 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 }}}");
  }
  
      BalloonEval *
  gui_mch_create_beval_area(
      void	*target UNUSED,	/* 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)
      {
  	iemsg(_("E232: Cannot create BalloonEval with both message and callback"));
  	return NULL;
      }
  
      beval = ALLOC_CLEAR_ONE(BalloonEval);
      if (beval != NULL)
      {
  	beval->target = s_textArea;
  
  	beval->showState = ShS_NEUTRAL;
  	beval->msg = mesg;
  	beval->msgCB = mesgCB;
  	beval->clientData = clientData;
  
  	InitCommonControls();
  	cur_beval = beval;
  
  	if (p_beval)
  	    gui_mch_enable_beval_area(beval);
      }
      return beval;
  }
  
      static void
  Handle_WM_Notify(HWND hwnd UNUSED, 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;
  	case TTN_GETDISPINFOW:
  	    {
  		// if we get here then we have new common controls
  		NMTTDISPINFOW_NEW *info = (NMTTDISPINFOW_NEW *)pnmh;
  		info->lpszText = (LPWSTR)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)
  {
  #ifdef FEAT_VARTABS
      vim_free(beval->vts);
  #endif
      vim_free(beval->tofree);
      vim_free(beval);
  }
  #endif /* FEAT_BEVAL_GUI */
  
  #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);
  
  #if defined(FEAT_DIRECTX)
      if (IS_ENABLE_DIRECTX())
  	DWriteContext_Flush(s_dwc);
  #endif
  
      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
  