xref: /vim-8.2.3635/src/gui_w32.c (revision 69a7cb47)
1 /* vi:set ts=8 sts=4 sw=4:
2  *
3  * VIM - Vi IMproved		by Bram Moolenaar
4  *				GUI support by Robert Webb
5  *
6  * Do ":help uganda"  in Vim to read copying and usage conditions.
7  * Do ":help credits" in Vim to see a list of people who contributed.
8  * See README.txt for an overview of the Vim source code.
9  */
10 /*
11  * Windows GUI.
12  *
13  * GUI support for Microsoft Windows.  Win32 initially; maybe Win16 later
14  *
15  * George V. Reilly <[email protected]> wrote the original Win32 GUI.
16  * Robert Webb reworked it to use the existing GUI stuff and added menu,
17  * scrollbars, etc.
18  *
19  * Note: Clipboard stuff, for cutting and pasting text to other windows, is in
20  * os_win32.c.	(It can also be done from the terminal version).
21  *
22  * TODO: Some of the function signatures ought to be updated for Win64;
23  * e.g., replace LONG with LONG_PTR, etc.
24  */
25 
26 /*
27  * These are new in Windows ME/XP, only defined in recent compilers.
28  */
29 #ifndef HANDLE_WM_XBUTTONUP
30 # define HANDLE_WM_XBUTTONUP(hwnd, wParam, lParam, fn) \
31    ((fn)((hwnd), (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
32 #endif
33 #ifndef HANDLE_WM_XBUTTONDOWN
34 # define HANDLE_WM_XBUTTONDOWN(hwnd, wParam, lParam, fn) \
35    ((fn)((hwnd), FALSE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
36 #endif
37 #ifndef HANDLE_WM_XBUTTONDBLCLK
38 # define HANDLE_WM_XBUTTONDBLCLK(hwnd, wParam, lParam, fn) \
39    ((fn)((hwnd), TRUE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
40 #endif
41 
42 /*
43  * Include the common stuff for MS-Windows GUI.
44  */
45 #include "gui_w48.c"
46 
47 #ifdef FEAT_XPM_W32
48 # include "xpm_w32.h"
49 #endif
50 
51 #ifdef PROTO
52 # define WINAPI
53 #endif
54 
55 #ifdef __MINGW32__
56 /*
57  * Add a lot of missing defines.
58  * They are not always missing, we need the #ifndef's.
59  */
60 # ifndef _cdecl
61 #  define _cdecl
62 # endif
63 # ifndef IsMinimized
64 #  define     IsMinimized(hwnd)		IsIconic(hwnd)
65 # endif
66 # ifndef IsMaximized
67 #  define     IsMaximized(hwnd)		IsZoomed(hwnd)
68 # endif
69 # ifndef SelectFont
70 #  define     SelectFont(hdc, hfont)  ((HFONT)SelectObject((hdc), (HGDIOBJ)(HFONT)(hfont)))
71 # endif
72 # ifndef GetStockBrush
73 #  define     GetStockBrush(i)     ((HBRUSH)GetStockObject(i))
74 # endif
75 # ifndef DeleteBrush
76 #  define     DeleteBrush(hbr)     DeleteObject((HGDIOBJ)(HBRUSH)(hbr))
77 # endif
78 
79 # ifndef HANDLE_WM_RBUTTONDBLCLK
80 #  define HANDLE_WM_RBUTTONDBLCLK(hwnd, wParam, lParam, fn) \
81     ((fn)((hwnd), TRUE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
82 # endif
83 # ifndef HANDLE_WM_MBUTTONUP
84 #  define HANDLE_WM_MBUTTONUP(hwnd, wParam, lParam, fn) \
85     ((fn)((hwnd), (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
86 # endif
87 # ifndef HANDLE_WM_MBUTTONDBLCLK
88 #  define HANDLE_WM_MBUTTONDBLCLK(hwnd, wParam, lParam, fn) \
89     ((fn)((hwnd), TRUE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
90 # endif
91 # ifndef HANDLE_WM_LBUTTONDBLCLK
92 #  define HANDLE_WM_LBUTTONDBLCLK(hwnd, wParam, lParam, fn) \
93     ((fn)((hwnd), TRUE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
94 # endif
95 # ifndef HANDLE_WM_RBUTTONDOWN
96 #  define HANDLE_WM_RBUTTONDOWN(hwnd, wParam, lParam, fn) \
97     ((fn)((hwnd), FALSE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
98 # endif
99 # ifndef HANDLE_WM_MOUSEMOVE
100 #  define HANDLE_WM_MOUSEMOVE(hwnd, wParam, lParam, fn) \
101     ((fn)((hwnd), (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
102 # endif
103 # ifndef HANDLE_WM_RBUTTONUP
104 #  define HANDLE_WM_RBUTTONUP(hwnd, wParam, lParam, fn) \
105     ((fn)((hwnd), (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
106 # endif
107 # ifndef HANDLE_WM_MBUTTONDOWN
108 #  define HANDLE_WM_MBUTTONDOWN(hwnd, wParam, lParam, fn) \
109     ((fn)((hwnd), FALSE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
110 # endif
111 # ifndef HANDLE_WM_LBUTTONUP
112 #  define HANDLE_WM_LBUTTONUP(hwnd, wParam, lParam, fn) \
113     ((fn)((hwnd), (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
114 # endif
115 # ifndef HANDLE_WM_LBUTTONDOWN
116 #  define HANDLE_WM_LBUTTONDOWN(hwnd, wParam, lParam, fn) \
117     ((fn)((hwnd), FALSE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
118 # endif
119 # ifndef HANDLE_WM_SYSCHAR
120 #  define HANDLE_WM_SYSCHAR(hwnd, wParam, lParam, fn) \
121     ((fn)((hwnd), (TCHAR)(wParam), (int)(short)LOWORD(lParam)), 0L)
122 # endif
123 # ifndef HANDLE_WM_ACTIVATEAPP
124 #  define HANDLE_WM_ACTIVATEAPP(hwnd, wParam, lParam, fn) \
125     ((fn)((hwnd), (BOOL)(wParam), (DWORD)(lParam)), 0L)
126 # endif
127 # ifndef HANDLE_WM_WINDOWPOSCHANGING
128 #  define HANDLE_WM_WINDOWPOSCHANGING(hwnd, wParam, lParam, fn) \
129     (LRESULT)(DWORD)(BOOL)(fn)((hwnd), (LPWINDOWPOS)(lParam))
130 # endif
131 # ifndef HANDLE_WM_VSCROLL
132 #  define HANDLE_WM_VSCROLL(hwnd, wParam, lParam, fn) \
133     ((fn)((hwnd), (HWND)(lParam), (UINT)(LOWORD(wParam)),  (int)(short)HIWORD(wParam)), 0L)
134 # endif
135 # ifndef HANDLE_WM_SETFOCUS
136 #  define HANDLE_WM_SETFOCUS(hwnd, wParam, lParam, fn) \
137     ((fn)((hwnd), (HWND)(wParam)), 0L)
138 # endif
139 # ifndef HANDLE_WM_KILLFOCUS
140 #  define HANDLE_WM_KILLFOCUS(hwnd, wParam, lParam, fn) \
141     ((fn)((hwnd), (HWND)(wParam)), 0L)
142 # endif
143 # ifndef HANDLE_WM_HSCROLL
144 #  define HANDLE_WM_HSCROLL(hwnd, wParam, lParam, fn) \
145     ((fn)((hwnd), (HWND)(lParam), (UINT)(LOWORD(wParam)), (int)(short)HIWORD(wParam)), 0L)
146 # endif
147 # ifndef HANDLE_WM_DROPFILES
148 #  define HANDLE_WM_DROPFILES(hwnd, wParam, lParam, fn) \
149     ((fn)((hwnd), (HDROP)(wParam)), 0L)
150 # endif
151 # ifndef HANDLE_WM_CHAR
152 #  define HANDLE_WM_CHAR(hwnd, wParam, lParam, fn) \
153     ((fn)((hwnd), (TCHAR)(wParam), (int)(short)LOWORD(lParam)), 0L)
154 # endif
155 # ifndef HANDLE_WM_SYSDEADCHAR
156 #  define HANDLE_WM_SYSDEADCHAR(hwnd, wParam, lParam, fn) \
157     ((fn)((hwnd), (TCHAR)(wParam), (int)(short)LOWORD(lParam)), 0L)
158 # endif
159 # ifndef HANDLE_WM_DEADCHAR
160 #  define HANDLE_WM_DEADCHAR(hwnd, wParam, lParam, fn) \
161     ((fn)((hwnd), (TCHAR)(wParam), (int)(short)LOWORD(lParam)), 0L)
162 # endif
163 #endif /* __MINGW32__ */
164 
165 
166 /* Some parameters for tearoff menus.  All in pixels. */
167 #define TEAROFF_PADDING_X	2
168 #define TEAROFF_BUTTON_PAD_X	8
169 #define TEAROFF_MIN_WIDTH	200
170 #define TEAROFF_SUBMENU_LABEL	">>"
171 #define TEAROFF_COLUMN_PADDING	3	// # spaces to pad column with.
172 
173 
174 /* For the Intellimouse: */
175 #ifndef WM_MOUSEWHEEL
176 #define WM_MOUSEWHEEL	0x20a
177 #endif
178 
179 
180 #ifdef FEAT_BEVAL
181 # define ID_BEVAL_TOOLTIP   200
182 # define BEVAL_TEXT_LEN	    MAXPATHL
183 
184 static void make_tooltip __ARGS((BalloonEval *beval, char *text, POINT pt));
185 static void delete_tooltip __ARGS((BalloonEval *beval));
186 static VOID CALLBACK BevalTimerProc __ARGS((HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime));
187 
188 static BalloonEval  *cur_beval = NULL;
189 static UINT	    BevalTimerId = 0;
190 static DWORD	    LastActivity = 0;
191 #endif
192 
193 /* Local variables: */
194 
195 #ifdef FEAT_MENU
196 static UINT	s_menu_id = 100;
197 
198 /*
199  * Use the system font for dialogs and tear-off menus.  Remove this line to
200  * use DLG_FONT_NAME.
201  */
202 # define USE_SYSMENU_FONT
203 #endif
204 
205 #define VIM_NAME	"vim"
206 #define VIM_CLASS	"Vim"
207 #define VIM_CLASSW	L"Vim"
208 
209 /* Initial size for the dialog template.  For gui_mch_dialog() it's fixed,
210  * thus there should be room for every dialog.  For tearoffs it's made bigger
211  * when needed. */
212 #define DLG_ALLOC_SIZE 16 * 1024
213 
214 /*
215  * stuff for dialogs, menus, tearoffs etc.
216  */
217 static LRESULT APIENTRY dialog_callback(HWND, UINT, WPARAM, LPARAM);
218 static LRESULT APIENTRY tearoff_callback(HWND, UINT, WPARAM, LPARAM);
219 static PWORD
220 add_dialog_element(
221 	PWORD p,
222 	DWORD lStyle,
223 	WORD x,
224 	WORD y,
225 	WORD w,
226 	WORD h,
227 	WORD Id,
228 	WORD clss,
229 	const char *caption);
230 static LPWORD lpwAlign(LPWORD);
231 static int nCopyAnsiToWideChar(LPWORD, LPSTR);
232 static void gui_mch_tearoff(char_u *title, vimmenu_T *menu, int initX, int initY);
233 static void get_dialog_font_metrics(void);
234 
235 static int dialog_default_button = -1;
236 
237 /* Intellimouse support */
238 static int mouse_scroll_lines = 0;
239 static UINT msh_msgmousewheel = 0;
240 
241 static int	s_usenewlook;	    /* emulate W95/NT4 non-bold dialogs */
242 #ifdef FEAT_TOOLBAR
243 static void initialise_toolbar(void);
244 static int get_toolbar_bitmap(vimmenu_T *menu);
245 #endif
246 
247 #ifdef FEAT_MBYTE_IME
248 static LRESULT _OnImeComposition(HWND hwnd, WPARAM dbcs, LPARAM param);
249 static char_u *GetResultStr(HWND hwnd, int GCS, int *lenp);
250 #endif
251 #if defined(FEAT_MBYTE_IME) && defined(DYNAMIC_IME)
252 # ifdef NOIME
253 typedef struct tagCOMPOSITIONFORM {
254     DWORD dwStyle;
255     POINT ptCurrentPos;
256     RECT  rcArea;
257 } COMPOSITIONFORM, *PCOMPOSITIONFORM, NEAR *NPCOMPOSITIONFORM, FAR *LPCOMPOSITIONFORM;
258 typedef HANDLE HIMC;
259 # endif
260 
261 HINSTANCE hLibImm = NULL;
262 LONG (WINAPI *pImmGetCompositionStringA)(HIMC, DWORD, LPVOID, DWORD);
263 LONG (WINAPI *pImmGetCompositionStringW)(HIMC, DWORD, LPVOID, DWORD);
264 HIMC (WINAPI *pImmGetContext)(HWND);
265 HIMC (WINAPI *pImmAssociateContext)(HWND, HIMC);
266 BOOL (WINAPI *pImmReleaseContext)(HWND, HIMC);
267 BOOL (WINAPI *pImmGetOpenStatus)(HIMC);
268 BOOL (WINAPI *pImmSetOpenStatus)(HIMC, BOOL);
269 BOOL (WINAPI *pImmGetCompositionFont)(HIMC, LPLOGFONTA);
270 BOOL (WINAPI *pImmSetCompositionFont)(HIMC, LPLOGFONTA);
271 BOOL (WINAPI *pImmSetCompositionWindow)(HIMC, LPCOMPOSITIONFORM);
272 BOOL (WINAPI *pImmGetConversionStatus)(HIMC, LPDWORD, LPDWORD);
273 static void dyn_imm_load(void);
274 #else
275 # define pImmGetCompositionStringA ImmGetCompositionStringA
276 # define pImmGetCompositionStringW ImmGetCompositionStringW
277 # define pImmGetContext		  ImmGetContext
278 # define pImmAssociateContext	  ImmAssociateContext
279 # define pImmReleaseContext	  ImmReleaseContext
280 # define pImmGetOpenStatus	  ImmGetOpenStatus
281 # define pImmSetOpenStatus	  ImmSetOpenStatus
282 # define pImmGetCompositionFont   ImmGetCompositionFontA
283 # define pImmSetCompositionFont   ImmSetCompositionFontA
284 # define pImmSetCompositionWindow ImmSetCompositionWindow
285 # define pImmGetConversionStatus  ImmGetConversionStatus
286 #endif
287 
288 #ifndef ETO_IGNORELANGUAGE
289 # define ETO_IGNORELANGUAGE  0x1000
290 #endif
291 
292 /* multi monitor support */
293 typedef struct _MONITORINFOstruct
294 {
295     DWORD cbSize;
296     RECT rcMonitor;
297     RECT rcWork;
298     DWORD dwFlags;
299 } _MONITORINFO;
300 
301 typedef HANDLE _HMONITOR;
302 typedef _HMONITOR (WINAPI *TMonitorFromWindow)(HWND, DWORD);
303 typedef BOOL (WINAPI *TGetMonitorInfo)(_HMONITOR, _MONITORINFO *);
304 
305 static TMonitorFromWindow   pMonitorFromWindow = NULL;
306 static TGetMonitorInfo	    pGetMonitorInfo = NULL;
307 static HANDLE		    user32_lib = NULL;
308 #ifdef FEAT_NETBEANS_INTG
309 int WSInitialized = FALSE; /* WinSock is initialized */
310 #endif
311 /*
312  * Return TRUE when running under Windows NT 3.x or Win32s, both of which have
313  * less fancy GUI APIs.
314  */
315     static int
316 is_winnt_3(void)
317 {
318     return ((os_version.dwPlatformId == VER_PLATFORM_WIN32_NT
319 		&& os_version.dwMajorVersion == 3)
320 	    || (os_version.dwPlatformId == VER_PLATFORM_WIN32s));
321 }
322 
323 /*
324  * Return TRUE when running under Win32s.
325  */
326     int
327 gui_is_win32s(void)
328 {
329     return (os_version.dwPlatformId == VER_PLATFORM_WIN32s);
330 }
331 
332 #ifdef FEAT_MENU
333 /*
334  * Figure out how high the menu bar is at the moment.
335  */
336     static int
337 gui_mswin_get_menu_height(
338     int	    fix_window)	    /* If TRUE, resize window if menu height changed */
339 {
340     static int	old_menu_height = -1;
341 
342     RECT    rc1, rc2;
343     int	    num;
344     int	    menu_height;
345 
346     if (gui.menu_is_active)
347 	num = GetMenuItemCount(s_menuBar);
348     else
349 	num = 0;
350 
351     if (num == 0)
352 	menu_height = 0;
353     else
354     {
355 	if (is_winnt_3())	/* for NT 3.xx */
356 	{
357 	    if (gui.starting)
358 		menu_height = GetSystemMetrics(SM_CYMENU);
359 	    else
360 	    {
361 		RECT r1, r2;
362 		int frameht = GetSystemMetrics(SM_CYFRAME);
363 		int capht = GetSystemMetrics(SM_CYCAPTION);
364 
365 		/* get window rect of s_hwnd
366 		 * get client rect of s_hwnd
367 		 * get cap height
368 		 * subtract from window rect, the sum of client height,
369 		 * (if not maximized)frame thickness, and caption height.
370 		 */
371 		GetWindowRect(s_hwnd, &r1);
372 		GetClientRect(s_hwnd, &r2);
373 		menu_height = r1.bottom - r1.top - (r2.bottom - r2.top
374 				 + 2 * frameht * (!IsZoomed(s_hwnd)) + capht);
375 	    }
376 	}
377 	else			/* win95 and variants (NT 4.0, I guess) */
378 	{
379 	    /*
380 	     * In case 'lines' is set in _vimrc/_gvimrc window width doesn't
381 	     * seem to have been set yet, so menu wraps in default window
382 	     * width which is very narrow.  Instead just return height of a
383 	     * single menu item.  Will still be wrong when the menu really
384 	     * should wrap over more than one line.
385 	     */
386 	    GetMenuItemRect(s_hwnd, s_menuBar, 0, &rc1);
387 	    if (gui.starting)
388 		menu_height = rc1.bottom - rc1.top + 1;
389 	    else
390 	    {
391 		GetMenuItemRect(s_hwnd, s_menuBar, num - 1, &rc2);
392 		menu_height = rc2.bottom - rc1.top + 1;
393 	    }
394 	}
395     }
396 
397     if (fix_window && menu_height != old_menu_height)
398     {
399 	old_menu_height = menu_height;
400 	gui_set_shellsize(FALSE, FALSE);
401     }
402 
403     return menu_height;
404 }
405 #endif /*FEAT_MENU*/
406 
407 
408 /*
409  * Setup for the Intellimouse
410  */
411     static void
412 init_mouse_wheel(void)
413 {
414 
415 #ifndef SPI_GETWHEELSCROLLLINES
416 # define SPI_GETWHEELSCROLLLINES    104
417 #endif
418 
419 #define VMOUSEZ_CLASSNAME  "MouseZ"		/* hidden wheel window class */
420 #define VMOUSEZ_TITLE      "Magellan MSWHEEL"	/* hidden wheel window title */
421 #define VMSH_MOUSEWHEEL    "MSWHEEL_ROLLMSG"
422 #define VMSH_SCROLL_LINES  "MSH_SCROLL_LINES_MSG"
423 
424     HWND hdl_mswheel;
425     UINT msh_msgscrolllines;
426 
427     msh_msgmousewheel = 0;
428     mouse_scroll_lines = 3;	/* reasonable default */
429 
430     if ((os_version.dwPlatformId == VER_PLATFORM_WIN32_NT
431 		&& os_version.dwMajorVersion >= 4)
432 	    || (os_version.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS
433 		&& ((os_version.dwMajorVersion == 4
434 			&& os_version.dwMinorVersion >= 10)
435 		    || os_version.dwMajorVersion >= 5)))
436     {
437 	/* if NT 4.0+ (or Win98) get scroll lines directly from system */
438 	SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0,
439 		&mouse_scroll_lines, 0);
440     }
441     else if (os_version.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS
442 	    || (os_version.dwPlatformId == VER_PLATFORM_WIN32_NT
443 		&& os_version.dwMajorVersion < 4))
444     {	/*
445 	 * If Win95 or NT 3.51,
446 	 * try to find the hidden point32 window.
447 	 */
448 	hdl_mswheel = FindWindow(VMOUSEZ_CLASSNAME, VMOUSEZ_TITLE);
449 	if (hdl_mswheel)
450 	{
451 	    msh_msgscrolllines = RegisterWindowMessage(VMSH_SCROLL_LINES);
452 	    if (msh_msgscrolllines)
453 	    {
454 		mouse_scroll_lines = (int)SendMessage(hdl_mswheel,
455 			msh_msgscrolllines, 0, 0);
456 		msh_msgmousewheel  = RegisterWindowMessage(VMSH_MOUSEWHEEL);
457 	    }
458 	}
459     }
460 }
461 
462 
463 /* Intellimouse wheel handler */
464     static void
465 _OnMouseWheel(
466     HWND hwnd,
467     short zDelta)
468 {
469 /* Treat a mouse wheel event as if it were a scroll request */
470     int i;
471     int size;
472     HWND hwndCtl;
473 
474     if (curwin->w_scrollbars[SBAR_RIGHT].id != 0)
475     {
476 	hwndCtl = curwin->w_scrollbars[SBAR_RIGHT].id;
477 	size = curwin->w_scrollbars[SBAR_RIGHT].size;
478     }
479     else if (curwin->w_scrollbars[SBAR_LEFT].id != 0)
480     {
481 	hwndCtl = curwin->w_scrollbars[SBAR_LEFT].id;
482 	size = curwin->w_scrollbars[SBAR_LEFT].size;
483     }
484     else
485 	return;
486 
487     size = curwin->w_height;
488     if (mouse_scroll_lines == 0)
489 	init_mouse_wheel();
490 
491     if (mouse_scroll_lines > 0
492 	    && mouse_scroll_lines < (size > 2 ? size - 2 : 1))
493     {
494 	for (i = mouse_scroll_lines; i > 0; --i)
495 	    _OnScroll(hwnd, hwndCtl, zDelta >= 0 ? SB_LINEUP : SB_LINEDOWN, 0);
496     }
497     else
498 	_OnScroll(hwnd, hwndCtl, zDelta >= 0 ? SB_PAGEUP : SB_PAGEDOWN, 0);
499 }
500 
501 #if 0	/* disabled, a gap appears below and beside the window, and the window
502 	   can be moved (in a strange way) */
503 /*
504  * Even though we have _DuringSizing() which makes the rubber band a valid
505  * size, we need this for when the user maximises the window.
506  * TODO: Doesn't seem to adjust the width though for some reason.
507  */
508     static BOOL
509 _OnWindowPosChanging(
510     HWND hwnd,
511     LPWINDOWPOS lpwpos)
512 {
513     RECT    workarea_rect;
514 
515     if (!(lpwpos->flags & SWP_NOSIZE))
516     {
517 	if (IsMaximized(hwnd)
518 		&& (os_version.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS
519 		    || (os_version.dwPlatformId == VER_PLATFORM_WIN32_NT
520 			&& os_version.dwMajorVersion >= 4)))
521 	{
522 	    SystemParametersInfo(SPI_GETWORKAREA, 0, &workarea_rect, 0);
523 	    lpwpos->x = workarea_rect.left;
524 	    lpwpos->y = workarea_rect.top;
525 	    lpwpos->cx = workarea_rect.right - workarea_rect.left;
526 	    lpwpos->cy = workarea_rect.bottom - workarea_rect.top;
527 	}
528 	gui_mswin_get_valid_dimensions(lpwpos->cx, lpwpos->cy,
529 				     &lpwpos->cx, &lpwpos->cy);
530     }
531     return 0;
532 }
533 #endif
534 
535 #ifdef FEAT_NETBEANS_INTG
536     static void
537 _OnWindowPosChanged(
538     HWND hwnd,
539     const LPWINDOWPOS lpwpos)
540 {
541     static int x = 0, y = 0, cx = 0, cy = 0;
542 
543     if (WSInitialized && (lpwpos->x != x || lpwpos->y != y
544 				     || lpwpos->cx != cx || lpwpos->cy != cy))
545     {
546 	x = lpwpos->x;
547 	y = lpwpos->y;
548 	cx = lpwpos->cx;
549 	cy = lpwpos->cy;
550 	netbeans_frame_moved(x, y);
551     }
552     /* Allow to send WM_SIZE and WM_MOVE */
553     FORWARD_WM_WINDOWPOSCHANGED(hwnd, lpwpos, MyWindowProc);
554 }
555 #endif
556 
557     static int
558 _DuringSizing(
559     HWND hwnd,
560     UINT fwSide,
561     LPRECT lprc)
562 {
563     int	    w, h;
564     int	    valid_w, valid_h;
565     int	    w_offset, h_offset;
566 
567     w = lprc->right - lprc->left;
568     h = lprc->bottom - lprc->top;
569     gui_mswin_get_valid_dimensions(w, h, &valid_w, &valid_h);
570     w_offset = w - valid_w;
571     h_offset = h - valid_h;
572 
573     if (fwSide == WMSZ_LEFT || fwSide == WMSZ_TOPLEFT
574 			    || fwSide == WMSZ_BOTTOMLEFT)
575 	lprc->left += w_offset;
576     else if (fwSide == WMSZ_RIGHT || fwSide == WMSZ_TOPRIGHT
577 			    || fwSide == WMSZ_BOTTOMRIGHT)
578 	lprc->right -= w_offset;
579 
580     if (fwSide == WMSZ_TOP || fwSide == WMSZ_TOPLEFT
581 			    || fwSide == WMSZ_TOPRIGHT)
582 	lprc->top += h_offset;
583     else if (fwSide == WMSZ_BOTTOM || fwSide == WMSZ_BOTTOMLEFT
584 			    || fwSide == WMSZ_BOTTOMRIGHT)
585 	lprc->bottom -= h_offset;
586     return TRUE;
587 }
588 
589 
590 
591     static LRESULT CALLBACK
592 _WndProc(
593     HWND hwnd,
594     UINT uMsg,
595     WPARAM wParam,
596     LPARAM lParam)
597 {
598     /*
599     TRACE("WndProc: hwnd = %08x, msg = %x, wParam = %x, lParam = %x\n",
600 	  hwnd, uMsg, wParam, lParam);
601     */
602 
603     HandleMouseHide(uMsg, lParam);
604 
605     s_uMsg = uMsg;
606     s_wParam = wParam;
607     s_lParam = lParam;
608 
609     switch (uMsg)
610     {
611 	HANDLE_MSG(hwnd, WM_DEADCHAR,	_OnDeadChar);
612 	HANDLE_MSG(hwnd, WM_SYSDEADCHAR, _OnDeadChar);
613 	/* HANDLE_MSG(hwnd, WM_ACTIVATE,    _OnActivate); */
614 	HANDLE_MSG(hwnd, WM_CLOSE,	_OnClose);
615 	/* HANDLE_MSG(hwnd, WM_COMMAND,	_OnCommand); */
616 	HANDLE_MSG(hwnd, WM_DESTROY,	_OnDestroy);
617 	HANDLE_MSG(hwnd, WM_DROPFILES,	_OnDropFiles);
618 	HANDLE_MSG(hwnd, WM_HSCROLL,	_OnScroll);
619 	HANDLE_MSG(hwnd, WM_KILLFOCUS,	_OnKillFocus);
620 #ifdef FEAT_MENU
621 	HANDLE_MSG(hwnd, WM_COMMAND,	_OnMenu);
622 #endif
623 	/* HANDLE_MSG(hwnd, WM_MOVE,	    _OnMove); */
624 	/* HANDLE_MSG(hwnd, WM_NCACTIVATE,  _OnNCActivate); */
625 	HANDLE_MSG(hwnd, WM_SETFOCUS,	_OnSetFocus);
626 	HANDLE_MSG(hwnd, WM_SIZE,	_OnSize);
627 	/* HANDLE_MSG(hwnd, WM_SYSCOMMAND,  _OnSysCommand); */
628 	/* HANDLE_MSG(hwnd, WM_SYSKEYDOWN,  _OnAltKey); */
629 	HANDLE_MSG(hwnd, WM_VSCROLL,	_OnScroll);
630 	// HANDLE_MSG(hwnd, WM_WINDOWPOSCHANGING,	_OnWindowPosChanging);
631 	HANDLE_MSG(hwnd, WM_ACTIVATEAPP, _OnActivateApp);
632 #ifdef FEAT_NETBEANS_INTG
633 	HANDLE_MSG(hwnd, WM_WINDOWPOSCHANGED, _OnWindowPosChanged);
634 #endif
635 
636     case WM_QUERYENDSESSION:	/* System wants to go down. */
637 	gui_shell_closed();	/* Will exit when no changed buffers. */
638 	return FALSE;		/* Do NOT allow system to go down. */
639 
640     case WM_ENDSESSION:
641 	if (wParam)	/* system only really goes down when wParam is TRUE */
642 	    _OnEndSession();
643 	break;
644 
645     case WM_CHAR:
646 	/* Don't use HANDLE_MSG() for WM_CHAR, it truncates wParam to a single
647 	 * byte while we want the UTF-16 character value. */
648 	_OnChar(hwnd, wParam, (int)(short)LOWORD(lParam));
649 	return 0L;
650 
651     case WM_SYSCHAR:
652 	/*
653 	 * if 'winaltkeys' is "no", or it's "menu" and it's not a menu
654 	 * shortcut key, handle like a typed ALT key, otherwise call Windows
655 	 * ALT key handling.
656 	 */
657 #ifdef FEAT_MENU
658 	if (	!gui.menu_is_active
659 		|| p_wak[0] == 'n'
660 		|| (p_wak[0] == 'm' && !gui_is_menu_shortcut((int)wParam))
661 		)
662 #endif
663 	{
664 	    _OnSysChar(hwnd, wParam, (int)(short)LOWORD(lParam));
665 	    return 0L;
666 	}
667 #ifdef FEAT_MENU
668 	else
669 	    return MyWindowProc(hwnd, uMsg, wParam, lParam);
670 #endif
671 
672     case WM_SYSKEYUP:
673 #ifdef FEAT_MENU
674 	/* This used to be done only when menu is active: ALT key is used for
675 	 * that.  But that caused problems when menu is disabled and using
676 	 * Alt-Tab-Esc: get into a strange state where no mouse-moved events
677 	 * are received, mouse pointer remains hidden. */
678 	return MyWindowProc(hwnd, uMsg, wParam, lParam);
679 #else
680 	return 0;
681 #endif
682 
683     case WM_SIZING:	/* HANDLE_MSG doesn't seem to handle this one */
684 	return _DuringSizing(hwnd, (UINT)wParam, (LPRECT)lParam);
685 
686     case WM_MOUSEWHEEL:
687 	_OnMouseWheel(hwnd, HIWORD(wParam));
688 	break;
689 
690 #ifdef FEAT_TOOLBAR
691     case WM_NOTIFY:
692 	switch (((LPNMHDR) lParam)->code)
693 	{
694 	    case TTN_NEEDTEXT:
695 		{
696 		    LPTOOLTIPTEXT	lpttt;
697 		    UINT		idButton;
698 		    int			idx;
699 		    vimmenu_T		*pMenu;
700 
701 		    lpttt = (LPTOOLTIPTEXT)lParam;
702 		    idButton = (UINT) lpttt->hdr.idFrom;
703 		    pMenu = gui_mswin_find_menu(root_menu, idButton);
704 		    if (pMenu)
705 		    {
706 			idx = MENU_INDEX_TIP;
707 			if (pMenu->strings[idx])
708 			{
709 			    lpttt->hinst = NULL;  /* string, not resource */
710 			    lpttt->lpszText = pMenu->strings[idx];
711 			}
712 		    }
713 		}
714 		break;
715 	    default:
716 		break;
717 	}
718 	break;
719 #endif
720 #if defined(MENUHINTS) && defined(FEAT_MENU)
721     case WM_MENUSELECT:
722 	if (((UINT) HIWORD(wParam)
723 		    & (0xffff ^ (MF_MOUSESELECT + MF_BITMAP + MF_POPUP)))
724 		== MF_HILITE
725 		&& (State & CMDLINE) == 0)
726 	{
727 	    UINT	idButton;
728 	    vimmenu_T	*pMenu;
729 	    static int	did_menu_tip = FALSE;
730 
731 	    if (did_menu_tip)
732 	    {
733 		msg_clr_cmdline();
734 		setcursor();
735 		out_flush();
736 		did_menu_tip = FALSE;
737 	    }
738 
739 	    idButton = (UINT)LOWORD(wParam);
740 	    pMenu = gui_mswin_find_menu(root_menu, idButton);
741 	    if (pMenu != NULL && pMenu->strings[MENU_INDEX_TIP] != 0
742 		    && GetMenuState(s_menuBar, pMenu->id, MF_BYCOMMAND) != -1)
743 	    {
744 		msg(pMenu->strings[MENU_INDEX_TIP]);
745 		setcursor();
746 		out_flush();
747 		did_menu_tip = TRUE;
748 	    }
749 	}
750 	break;
751 #endif
752     case WM_NCHITTEST:
753 	{
754 	    LRESULT	result;
755 	    int		x, y;
756 	    int		xPos = GET_X_LPARAM(lParam);
757 
758 	    result = MyWindowProc(hwnd, uMsg, wParam, lParam);
759 	    if (result == HTCLIENT)
760 	    {
761 		gui_mch_get_winpos(&x, &y);
762 		xPos -= x;
763 
764 		if (xPos < 48) /* <VN> TODO should use system metric? */
765 		    return HTBOTTOMLEFT;
766 		else
767 		    return HTBOTTOMRIGHT;
768 	    }
769 	    else
770 		return result;
771 	}
772 	/* break; notreached */
773 
774 #ifdef FEAT_MBYTE_IME
775     case WM_IME_NOTIFY:
776 	if (!_OnImeNotify(hwnd, (DWORD)wParam, (DWORD)lParam))
777 	    return MyWindowProc(hwnd, uMsg, wParam, lParam);
778 	break;
779     case WM_IME_COMPOSITION:
780 	if (!_OnImeComposition(hwnd, wParam, lParam))
781 	    return MyWindowProc(hwnd, uMsg, wParam, lParam);
782 	break;
783 #endif
784 
785     default:
786 	if (uMsg == msh_msgmousewheel && msh_msgmousewheel != 0)
787 	{   /* handle MSH_MOUSEWHEEL messages for Intellimouse */
788 	    _OnMouseWheel(hwnd, HIWORD(wParam));
789 	    break;
790 	}
791 #ifdef MSWIN_FIND_REPLACE
792 	else
793 	if (uMsg == s_findrep_msg && s_findrep_msg != 0)
794 	{
795 	    _OnFindRepl();
796 	}
797 #endif
798 	return MyWindowProc(hwnd, uMsg, wParam, lParam);
799     }
800 
801     return 1;
802 }
803 
804 /*
805  * End of call-back routines
806  */
807 
808 /* parent window, if specified with -P */
809 HWND vim_parent_hwnd = NULL;
810 
811     static BOOL CALLBACK
812 FindWindowTitle(HWND hwnd, LPARAM lParam)
813 {
814     char	buf[2048];
815     char	*title = (char *)lParam;
816 
817     if (GetWindowText(hwnd, buf, sizeof(buf)))
818     {
819 	if (strstr(buf, title) != NULL)
820 	{
821 	    /* Found it.  Store the window ref. and quit searching if MDI
822 	     * works. */
823 	    vim_parent_hwnd = FindWindowEx(hwnd, NULL, "MDIClient", NULL);
824 	    if (vim_parent_hwnd != NULL)
825 		return FALSE;
826 	}
827     }
828     return TRUE;	/* continue searching */
829 }
830 
831 /*
832  * Invoked for '-P "title"' argument: search for parent application to open
833  * our window in.
834  */
835     void
836 gui_mch_set_parent(char *title)
837 {
838     EnumWindows(FindWindowTitle, (LPARAM)title);
839     if (vim_parent_hwnd == NULL)
840     {
841 	EMSG2(_("E671: Cannot find window title \"%s\""), title);
842 	mch_exit(2);
843     }
844 }
845 
846     static void
847 ole_error(char *arg)
848 {
849     EMSG2(_("E243: Argument not supported: \"-%s\"; Use the OLE version."),
850 									 arg);
851 }
852 
853 /*
854  * Parse the GUI related command-line arguments.  Any arguments used are
855  * deleted from argv, and *argc is decremented accordingly.  This is called
856  * when vim is started, whether or not the GUI has been started.
857  */
858     void
859 gui_mch_prepare(int *argc, char **argv)
860 {
861     int		silent = FALSE;
862     int		idx;
863 
864     /* Check for special OLE command line parameters */
865     if ((*argc == 2 || *argc == 3) && (argv[1][0] == '-' || argv[1][0] == '/'))
866     {
867 	/* Check for a "-silent" argument first. */
868 	if (*argc == 3 && STRICMP(argv[1] + 1, "silent") == 0
869 		&& (argv[2][0] == '-' || argv[2][0] == '/'))
870 	{
871 	    silent = TRUE;
872 	    idx = 2;
873 	}
874 	else
875 	    idx = 1;
876 
877 	/* Register Vim as an OLE Automation server */
878 	if (STRICMP(argv[idx] + 1, "register") == 0)
879 	{
880 #ifdef FEAT_OLE
881 	    RegisterMe(silent);
882 	    mch_exit(0);
883 #else
884 	    if (!silent)
885 		ole_error("register");
886 	    mch_exit(2);
887 #endif
888 	}
889 
890 	/* Unregister Vim as an OLE Automation server */
891 	if (STRICMP(argv[idx] + 1, "unregister") == 0)
892 	{
893 #ifdef FEAT_OLE
894 	    UnregisterMe(!silent);
895 	    mch_exit(0);
896 #else
897 	    if (!silent)
898 		ole_error("unregister");
899 	    mch_exit(2);
900 #endif
901 	}
902 
903 	/* Ignore an -embedding argument. It is only relevant if the
904 	 * application wants to treat the case when it is started manually
905 	 * differently from the case where it is started via automation (and
906 	 * we don't).
907 	 */
908 	if (STRICMP(argv[idx] + 1, "embedding") == 0)
909 	{
910 #ifdef FEAT_OLE
911 	    *argc = 1;
912 #else
913 	    ole_error("embedding");
914 	    mch_exit(2);
915 #endif
916 	}
917     }
918 
919 #ifdef FEAT_OLE
920     {
921 	int	bDoRestart = FALSE;
922 
923 	InitOLE(&bDoRestart);
924 	/* automatically exit after registering */
925 	if (bDoRestart)
926 	    mch_exit(0);
927     }
928 #endif
929 
930 #ifdef FEAT_NETBEANS_INTG
931     {
932 	/* stolen from gui_x11.x */
933 	int arg;
934 
935 	for (arg = 1; arg < *argc; arg++)
936 	    if (strncmp("-nb", argv[arg], 3) == 0)
937 	    {
938 		usingNetbeans++;
939 		netbeansArg = argv[arg];
940 		mch_memmove(&argv[arg], &argv[arg + 1],
941 					    (--*argc - arg) * sizeof(char *));
942 		argv[*argc] = NULL;
943 		break;	/* enough? */
944 	    }
945 
946 	if (usingNetbeans)
947 	{
948 	    WSADATA wsaData;
949 	    int wsaerr;
950 
951 	    /* Init WinSock */
952 	    wsaerr = WSAStartup(MAKEWORD(2, 2), &wsaData);
953 	    if (wsaerr == 0)
954 		WSInitialized = TRUE;
955 	}
956     }
957 #endif
958 
959     /* get the OS version info */
960     os_version.dwOSVersionInfoSize = sizeof(os_version);
961     GetVersionEx(&os_version); /* this call works on Win32s, Win95 and WinNT */
962 
963     /* try and load the user32.dll library and get the entry points for
964      * multi-monitor-support. */
965     if ((user32_lib = LoadLibrary("User32.dll")) != NULL)
966     {
967 	pMonitorFromWindow = (TMonitorFromWindow)GetProcAddress(user32_lib,
968 							 "MonitorFromWindow");
969 
970 	/* there are ...A and ...W version of GetMonitorInfo - looking at
971 	 * winuser.h, they have exactly the same declaration. */
972 	pGetMonitorInfo = (TGetMonitorInfo)GetProcAddress(user32_lib,
973 							  "GetMonitorInfoA");
974     }
975 }
976 
977 /*
978  * Initialise the GUI.	Create all the windows, set up all the call-backs
979  * etc.
980  */
981     int
982 gui_mch_init(void)
983 {
984     const char szVimWndClass[] = VIM_CLASS;
985     const char szTextAreaClass[] = "VimTextArea";
986     WNDCLASS wndclass;
987 #ifdef FEAT_MBYTE
988     const WCHAR szVimWndClassW[] = VIM_CLASSW;
989     WNDCLASSW wndclassw;
990 #endif
991 #ifdef GLOBAL_IME
992     ATOM	atom;
993 #endif
994 
995     /* Display any pending error messages */
996     display_errors();
997 
998     /* Return here if the window was already opened (happens when
999      * gui_mch_dialog() is called early). */
1000     if (s_hwnd != NULL)
1001 	return OK;
1002 
1003     /*
1004      * Load the tearoff bitmap
1005      */
1006 #ifdef FEAT_TEAROFF
1007     s_htearbitmap = LoadBitmap(s_hinst, "IDB_TEAROFF");
1008 #endif
1009 
1010     gui.scrollbar_width = GetSystemMetrics(SM_CXVSCROLL);
1011     gui.scrollbar_height = GetSystemMetrics(SM_CYHSCROLL);
1012 #ifdef FEAT_MENU
1013     gui.menu_height = 0;	/* Windows takes care of this */
1014 #endif
1015     gui.border_width = 0;
1016 
1017     s_brush = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
1018 
1019 #ifdef FEAT_MBYTE
1020     /* First try using the wide version, so that we can use any title.
1021      * Otherwise only characters in the active codepage will work. */
1022     if (GetClassInfoW(s_hinst, szVimWndClassW, &wndclassw) == 0)
1023     {
1024 	wndclassw.style = 0;
1025 	wndclassw.lpfnWndProc = _WndProc;
1026 	wndclassw.cbClsExtra = 0;
1027 	wndclassw.cbWndExtra = 0;
1028 	wndclassw.hInstance = s_hinst;
1029 	wndclassw.hIcon = LoadIcon(wndclassw.hInstance, "IDR_VIM");
1030 	wndclassw.hCursor = LoadCursor(NULL, IDC_ARROW);
1031 	wndclassw.hbrBackground = s_brush;
1032 	wndclassw.lpszMenuName = NULL;
1033 	wndclassw.lpszClassName = szVimWndClassW;
1034 
1035 	if ((
1036 #ifdef GLOBAL_IME
1037 		    atom =
1038 #endif
1039 		    RegisterClassW(&wndclassw)) == 0)
1040 	{
1041 	    if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
1042 		return FAIL;
1043 
1044 	    /* Must be Windows 98, fall back to non-wide function. */
1045 	}
1046 	else
1047 	    wide_WindowProc = TRUE;
1048     }
1049 
1050     if (!wide_WindowProc)
1051 #endif
1052 
1053     if (GetClassInfo(s_hinst, szVimWndClass, &wndclass) == 0)
1054     {
1055 	wndclass.style = 0;
1056 	wndclass.lpfnWndProc = _WndProc;
1057 	wndclass.cbClsExtra = 0;
1058 	wndclass.cbWndExtra = 0;
1059 	wndclass.hInstance = s_hinst;
1060 	wndclass.hIcon = LoadIcon(wndclass.hInstance, "IDR_VIM");
1061 	wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
1062 	wndclass.hbrBackground = s_brush;
1063 	wndclass.lpszMenuName = NULL;
1064 	wndclass.lpszClassName = szVimWndClass;
1065 
1066 	if ((
1067 #ifdef GLOBAL_IME
1068 		    atom =
1069 #endif
1070 		    RegisterClass(&wndclass)) == 0)
1071 	    return FAIL;
1072     }
1073 
1074     if (vim_parent_hwnd != NULL)
1075     {
1076 #ifdef HAVE_TRY_EXCEPT
1077 	__try
1078 	{
1079 #endif
1080 	    /* Open inside the specified parent window.
1081 	     * TODO: last argument should point to a CLIENTCREATESTRUCT
1082 	     * structure. */
1083 	    s_hwnd = CreateWindowEx(
1084 		WS_EX_MDICHILD,
1085 		szVimWndClass, "Vim MSWindows GUI",
1086 		WS_OVERLAPPEDWINDOW | WS_CHILD | WS_CLIPSIBLINGS | 0xC000,
1087 		gui_win_x == -1 ? CW_USEDEFAULT : gui_win_x,
1088 		gui_win_y == -1 ? CW_USEDEFAULT : gui_win_y,
1089 		100,				/* Any value will do */
1090 		100,				/* Any value will do */
1091 		vim_parent_hwnd, NULL,
1092 		s_hinst, NULL);
1093 #ifdef HAVE_TRY_EXCEPT
1094 	}
1095 	__except(EXCEPTION_EXECUTE_HANDLER)
1096 	{
1097 	    /* NOP */
1098 	}
1099 #endif
1100 	if (s_hwnd == NULL)
1101 	{
1102 	    EMSG(_("E672: Unable to open window inside MDI application"));
1103 	    mch_exit(2);
1104 	}
1105     }
1106     else
1107 	/* Open toplevel window. */
1108 	s_hwnd = CreateWindow(
1109 	    szVimWndClass, "Vim MSWindows GUI",
1110 	    WS_OVERLAPPEDWINDOW,
1111 	    gui_win_x == -1 ? CW_USEDEFAULT : gui_win_x,
1112 	    gui_win_y == -1 ? CW_USEDEFAULT : gui_win_y,
1113 	    100,				/* Any value will do */
1114 	    100,				/* Any value will do */
1115 	    NULL, NULL,
1116 	    s_hinst, NULL);
1117 
1118     if (s_hwnd == NULL)
1119 	return FAIL;
1120 
1121 #ifdef GLOBAL_IME
1122     global_ime_init(atom, s_hwnd);
1123 #endif
1124 #if defined(FEAT_MBYTE_IME) && defined(DYNAMIC_IME)
1125     dyn_imm_load();
1126 #endif
1127 
1128     /* Create the text area window */
1129     if (GetClassInfo(s_hinst, szTextAreaClass, &wndclass) == 0)
1130     {
1131 	wndclass.style = CS_OWNDC;
1132 	wndclass.lpfnWndProc = _TextAreaWndProc;
1133 	wndclass.cbClsExtra = 0;
1134 	wndclass.cbWndExtra = 0;
1135 	wndclass.hInstance = s_hinst;
1136 	wndclass.hIcon = NULL;
1137 	wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
1138 	wndclass.hbrBackground = NULL;
1139 	wndclass.lpszMenuName = NULL;
1140 	wndclass.lpszClassName = szTextAreaClass;
1141 
1142 	if (RegisterClass(&wndclass) == 0)
1143 	    return FAIL;
1144     }
1145     s_textArea = CreateWindowEx(
1146 	WS_EX_CLIENTEDGE,
1147 	szTextAreaClass, "Vim text area",
1148 	WS_CHILD | WS_VISIBLE, 0, 0,
1149 	100,				/* Any value will do for now */
1150 	100,				/* Any value will do for now */
1151 	s_hwnd, NULL,
1152 	s_hinst, NULL);
1153 
1154     if (s_textArea == NULL)
1155 	return FAIL;
1156 
1157 #ifdef FEAT_MENU
1158     s_menuBar = CreateMenu();
1159 #endif
1160     s_hdc = GetDC(s_textArea);
1161 
1162 #ifdef MSWIN16_FASTTEXT
1163     SetBkMode(s_hdc, OPAQUE);
1164 #endif
1165 
1166 #ifdef FEAT_WINDOWS
1167     DragAcceptFiles(s_hwnd, TRUE);
1168 #endif
1169 
1170     /* Do we need to bother with this? */
1171     /* m_fMouseAvail = GetSystemMetrics(SM_MOUSEPRESENT); */
1172 
1173     /* Get background/foreground colors from the system */
1174     gui_mch_def_colors();
1175 
1176     /* Get the colors from the "Normal" group (set in syntax.c or in a vimrc
1177      * file) */
1178     set_normal_colors();
1179 
1180     /*
1181      * Check that none of the colors are the same as the background color.
1182      * Then store the current values as the defaults.
1183      */
1184     gui_check_colors();
1185     gui.def_norm_pixel = gui.norm_pixel;
1186     gui.def_back_pixel = gui.back_pixel;
1187 
1188     /* Get the colors for the highlight groups (gui_check_colors() might have
1189      * changed them) */
1190     highlight_gui_started();
1191 
1192     /*
1193      * Start out by adding the configured border width into the border offset
1194      */
1195     gui.border_offset = gui.border_width + 2;	/*CLIENT EDGE*/
1196 
1197     /*
1198      * Set up for Intellimouse processing
1199      */
1200     init_mouse_wheel();
1201 
1202     /*
1203      * compute a couple of metrics used for the dialogs
1204      */
1205     get_dialog_font_metrics();
1206 #ifdef FEAT_TOOLBAR
1207     /*
1208      * Create the toolbar
1209      */
1210     initialise_toolbar();
1211 #endif
1212 #ifdef MSWIN_FIND_REPLACE
1213     /*
1214      * Initialise the dialog box stuff
1215      */
1216     s_findrep_msg = RegisterWindowMessage(FINDMSGSTRING);
1217 
1218     /* Initialise the struct */
1219     s_findrep_struct.lStructSize = sizeof(s_findrep_struct);
1220     s_findrep_struct.lpstrFindWhat = alloc(MSWIN_FR_BUFSIZE);
1221     s_findrep_struct.lpstrFindWhat[0] = NUL;
1222     s_findrep_struct.lpstrReplaceWith = alloc(MSWIN_FR_BUFSIZE);
1223     s_findrep_struct.lpstrReplaceWith[0] = NUL;
1224     s_findrep_struct.wFindWhatLen = MSWIN_FR_BUFSIZE;
1225     s_findrep_struct.wReplaceWithLen = MSWIN_FR_BUFSIZE;
1226 #endif
1227 #ifdef FEAT_NETBEANS_INTG
1228     if (usingNetbeans)
1229 	netbeans_w32_connect();
1230 #endif
1231 
1232     return OK;
1233 }
1234 
1235 /*
1236  * Get the size of the screen, taking position on multiple monitors into
1237  * account (if supported).
1238  */
1239     static void
1240 get_work_area(RECT *spi_rect)
1241 {
1242     _HMONITOR	    mon;
1243     _MONITORINFO    moninfo;
1244 
1245     /* use these functions only if available */
1246     if (pMonitorFromWindow != NULL && pGetMonitorInfo != NULL)
1247     {
1248 	/* work out which monitor the window is on, and get *it's* work area */
1249 	mon = pMonitorFromWindow(s_hwnd, 1 /*MONITOR_DEFAULTTOPRIMARY*/);
1250 	if (mon != NULL)
1251 	{
1252 	    moninfo.cbSize = sizeof(_MONITORINFO);
1253 	    if (pGetMonitorInfo(mon, &moninfo))
1254 	    {
1255 		*spi_rect = moninfo.rcWork;
1256 		return;
1257 	    }
1258 	}
1259     }
1260     /* this is the old method... */
1261     SystemParametersInfo(SPI_GETWORKAREA, 0, spi_rect, 0);
1262 }
1263 
1264 /*
1265  * Set the size of the window to the given width and height in pixels.
1266  */
1267     void
1268 gui_mch_set_shellsize(int width, int height,
1269 	int min_width, int min_height, int base_width, int base_height)
1270 {
1271     RECT	workarea_rect;
1272     int		win_width, win_height;
1273     int		win_xpos, win_ypos;
1274     WINDOWPLACEMENT wndpl;
1275 
1276     /* try to keep window completely on screen */
1277     /* get size of the screen work area (excludes taskbar, appbars) */
1278     get_work_area(&workarea_rect);
1279 
1280     /* get current posision of our window */
1281     wndpl.length = sizeof(WINDOWPLACEMENT);
1282     GetWindowPlacement(s_hwnd, &wndpl);
1283 
1284     /* Resizing a maximized window looks very strange, unzoom it first.
1285      * But don't do it when still starting up, it may have been requested in
1286      * the shortcut. */
1287     if (wndpl.showCmd == SW_SHOWMAXIMIZED && starting == 0)
1288     {
1289 	ShowWindow(s_hwnd, SW_SHOWNORMAL);
1290 	/* Need to get the settings of the normal window. */
1291 	GetWindowPlacement(s_hwnd, &wndpl);
1292     }
1293 
1294     win_xpos = wndpl.rcNormalPosition.left;
1295     win_ypos = wndpl.rcNormalPosition.top;
1296 
1297     /* compute the size of the outside of the window */
1298     win_width = width + GetSystemMetrics(SM_CXFRAME) * 2;
1299     win_height = height + GetSystemMetrics(SM_CYFRAME) * 2
1300 			+ GetSystemMetrics(SM_CYCAPTION)
1301 #ifdef FEAT_MENU
1302 			+ gui_mswin_get_menu_height(FALSE)
1303 #endif
1304 			;
1305 
1306     /* if the window is going off the screen, move it on to the screen */
1307     if (win_xpos + win_width > workarea_rect.right)
1308 	win_xpos = workarea_rect.right - win_width;
1309 
1310     if (win_xpos < workarea_rect.left)
1311 	win_xpos = workarea_rect.left;
1312 
1313     if (win_ypos + win_height > workarea_rect.bottom)
1314 	win_ypos = workarea_rect.bottom - win_height;
1315 
1316     if (win_ypos < workarea_rect.top)
1317 	win_ypos = workarea_rect.top;
1318 
1319     /* When the taskbar is placed on the left or top of the screen,
1320      * SetWindowPlacement() adds its width or height automatically, compensate
1321      * for that.  When the offset is over 400 it's probably something else,
1322      * skip it then (just in case). */
1323     if (workarea_rect.left > 0 && workarea_rect.left < 400)
1324 	win_xpos -= workarea_rect.left;
1325     if (workarea_rect.top > 0 && workarea_rect.top < 400)
1326 	win_ypos -= workarea_rect.top;
1327 
1328     wndpl.rcNormalPosition.left = win_xpos;
1329     wndpl.rcNormalPosition.right = win_xpos + win_width;
1330     wndpl.rcNormalPosition.top = win_ypos;
1331     wndpl.rcNormalPosition.bottom = win_ypos + win_height;
1332 
1333     /* set window position - we should use SetWindowPlacement rather than
1334      * SetWindowPos as the MSDN docs say the coord systems returned by
1335      * these two are not compatible. */
1336     SetWindowPlacement(s_hwnd, &wndpl);
1337 
1338     SetActiveWindow(s_hwnd);
1339     SetFocus(s_hwnd);
1340 
1341 #ifdef FEAT_MENU
1342     /* Menu may wrap differently now */
1343     gui_mswin_get_menu_height(!gui.starting);
1344 #endif
1345 }
1346 
1347 
1348     void
1349 gui_mch_set_scrollbar_thumb(
1350     scrollbar_T *sb,
1351     long	val,
1352     long	size,
1353     long	max)
1354 {
1355     SCROLLINFO	info;
1356 
1357     sb->scroll_shift = 0;
1358     while (max > 32767)
1359     {
1360 	max = (max + 1) >> 1;
1361 	val  >>= 1;
1362 	size >>= 1;
1363 	++sb->scroll_shift;
1364     }
1365 
1366     if (sb->scroll_shift > 0)
1367 	++size;
1368 
1369     info.cbSize = sizeof(info);
1370     info.fMask = SIF_POS | SIF_RANGE | SIF_PAGE;
1371     info.nPos = val;
1372     info.nMin = 0;
1373     info.nMax = max;
1374     info.nPage = size;
1375     SetScrollInfo(sb->id, SB_CTL, &info, TRUE);
1376 }
1377 
1378 
1379 /*
1380  * Set the current text font.
1381  */
1382     void
1383 gui_mch_set_font(GuiFont font)
1384 {
1385     gui.currFont = font;
1386 }
1387 
1388 
1389 /*
1390  * Set the current text foreground color.
1391  */
1392     void
1393 gui_mch_set_fg_color(guicolor_T color)
1394 {
1395     gui.currFgColor = color;
1396 }
1397 
1398 /*
1399  * Set the current text background color.
1400  */
1401     void
1402 gui_mch_set_bg_color(guicolor_T color)
1403 {
1404     gui.currBgColor = color;
1405 }
1406 
1407 #if defined(FEAT_MBYTE) && defined(FEAT_MBYTE_IME)
1408 /*
1409  * Multi-byte handling, originally by Sung-Hoon Baek.
1410  * First static functions (no prototypes generated).
1411  */
1412 #ifdef _MSC_VER
1413 # include <ime.h>   /* Apparently not needed for Cygwin, MingW or Borland. */
1414 #endif
1415 #include <imm.h>
1416 
1417 /*
1418  * display composition string(korean)
1419  */
1420     static void
1421 DisplayCompStringOpaque(char_u *s, int len)
1422 {
1423     int OldBkMode = GetBkMode(s_hdc);
1424 
1425     SetBkMode(s_hdc, OPAQUE);
1426     gui_outstr_nowrap(s, len, GUI_MON_TRS_CURSOR,
1427 					     (guicolor_T)0, (guicolor_T)0, 0);
1428     SetBkMode(s_hdc, OldBkMode);
1429 }
1430 
1431 /*
1432  * handle WM_IME_NOTIFY message
1433  */
1434     static LRESULT
1435 _OnImeNotify(HWND hWnd, DWORD dwCommand, DWORD dwData)
1436 {
1437     LRESULT lResult = 0;
1438     HIMC hImc;
1439 
1440     if (!pImmGetContext || (hImc = pImmGetContext(hWnd)) == (HIMC)0)
1441 	return lResult;
1442     switch (dwCommand)
1443     {
1444 	case IMN_SETOPENSTATUS:
1445 	    if (pImmGetOpenStatus(hImc))
1446 	    {
1447 		pImmSetCompositionFont(hImc, &norm_logfont);
1448 		im_set_position(gui.row, gui.col);
1449 
1450 		/* Disable langmap */
1451 		State &= ~LANGMAP;
1452 		if (State & INSERT)
1453 		{
1454 #if defined(FEAT_WINDOWS) && defined(FEAT_KEYMAP)
1455 		    /* Unshown 'keymap' in status lines */
1456 		    if (curbuf->b_p_iminsert == B_IMODE_LMAP)
1457 		    {
1458 			/* Save cursor position */
1459 			int old_row = gui.row;
1460 			int old_col = gui.col;
1461 
1462 			// This must be called here before
1463 			// status_redraw_curbuf(), otherwise the mode
1464 			// message may appear in the wrong position.
1465 			showmode();
1466 			status_redraw_curbuf();
1467 			update_screen(0);
1468 			/* Restore cursor position */
1469 			gui.row = old_row;
1470 			gui.col = old_col;
1471 		    }
1472 #endif
1473 		}
1474 	    }
1475 	    gui_update_cursor(TRUE, FALSE);
1476 	    lResult = 0;
1477 	    break;
1478     }
1479     pImmReleaseContext(hWnd, hImc);
1480     return lResult;
1481 }
1482 
1483     static LRESULT
1484 _OnImeComposition(HWND hwnd, WPARAM dbcs, LPARAM param)
1485 {
1486     char_u	*ret;
1487     int		len;
1488 
1489     if ((param & GCS_RESULTSTR) == 0) /* Composition unfinished. */
1490 	return 0;
1491 
1492     ret = GetResultStr(hwnd, GCS_RESULTSTR, &len);
1493     if (ret != NULL)
1494     {
1495 	add_to_input_buf_csi(ret, len);
1496 	vim_free(ret);
1497 	return 1;
1498     }
1499     return 0;
1500 }
1501 
1502 /*
1503  * get the current composition string, in UCS-2; *lenp is the number of
1504  * *lenp is the number of Unicode characters.
1505  */
1506     static short_u *
1507 GetCompositionString_inUCS2(HIMC hIMC, DWORD GCS, int *lenp)
1508 {
1509     LONG	    ret;
1510     LPWSTR	    wbuf = NULL;
1511     char_u	    *buf;
1512 
1513     if (!pImmGetContext)
1514 	return NULL; /* no imm32.dll */
1515 
1516     /* Try Unicode; this'll always work on NT regardless of codepage. */
1517     ret = pImmGetCompositionStringW(hIMC, GCS, NULL, 0);
1518     if (ret == 0)
1519 	return NULL; /* empty */
1520 
1521     if (ret > 0)
1522     {
1523 	/* Allocate the requested buffer plus space for the NUL character. */
1524 	wbuf = (LPWSTR)alloc(ret + sizeof(WCHAR));
1525 	if (wbuf != NULL)
1526 	{
1527 	    pImmGetCompositionStringW(hIMC, GCS, wbuf, ret);
1528 	    *lenp = ret / sizeof(WCHAR);
1529 	}
1530 	return (short_u *)wbuf;
1531     }
1532 
1533     /* ret < 0; we got an error, so try the ANSI version.  This'll work
1534      * on 9x/ME, but only if the codepage happens to be set to whatever
1535      * we're inputting. */
1536     ret = pImmGetCompositionStringA(hIMC, GCS, NULL, 0);
1537     if (ret <= 0)
1538 	return NULL; /* empty or error */
1539 
1540     buf = alloc(ret);
1541     if (buf == NULL)
1542 	return NULL;
1543     pImmGetCompositionStringA(hIMC, GCS, buf, ret);
1544 
1545     /* convert from codepage to UCS-2 */
1546     MultiByteToWideChar_alloc(GetACP(), 0, buf, ret, &wbuf, lenp);
1547     vim_free(buf);
1548 
1549     return (short_u *)wbuf;
1550 }
1551 
1552 /*
1553  * void GetResultStr()
1554  *
1555  * This handles WM_IME_COMPOSITION with GCS_RESULTSTR flag on.
1556  * get complete composition string
1557  */
1558     static char_u *
1559 GetResultStr(HWND hwnd, int GCS, int *lenp)
1560 {
1561     HIMC	hIMC;		/* Input context handle. */
1562     short_u	*buf = NULL;
1563     char_u	*convbuf = NULL;
1564 
1565     if (!pImmGetContext || (hIMC = pImmGetContext(hwnd)) == (HIMC)0)
1566 	return NULL;
1567 
1568     /* Reads in the composition string. */
1569     buf = GetCompositionString_inUCS2(hIMC, GCS, lenp);
1570     if (buf == NULL)
1571 	return NULL;
1572 
1573     convbuf = ucs2_to_enc(buf, lenp);
1574     pImmReleaseContext(hwnd, hIMC);
1575     vim_free(buf);
1576     return convbuf;
1577 }
1578 #endif
1579 
1580 /* For global functions we need prototypes. */
1581 #if (defined(FEAT_MBYTE) && defined(FEAT_MBYTE_IME)) || defined(PROTO)
1582 
1583 /*
1584  * set font to IM.
1585  */
1586     void
1587 im_set_font(LOGFONT *lf)
1588 {
1589     HIMC hImc;
1590 
1591     if (pImmGetContext && (hImc = pImmGetContext(s_hwnd)) != (HIMC)0)
1592     {
1593 	pImmSetCompositionFont(hImc, lf);
1594 	pImmReleaseContext(s_hwnd, hImc);
1595     }
1596 }
1597 
1598 /*
1599  * Notify cursor position to IM.
1600  */
1601     void
1602 im_set_position(int row, int col)
1603 {
1604     HIMC hImc;
1605 
1606     if (pImmGetContext && (hImc = pImmGetContext(s_hwnd)) != (HIMC)0)
1607     {
1608 	COMPOSITIONFORM	cfs;
1609 
1610 	cfs.dwStyle = CFS_POINT;
1611 	cfs.ptCurrentPos.x = FILL_X(col);
1612 	cfs.ptCurrentPos.y = FILL_Y(row);
1613 	MapWindowPoints(s_textArea, s_hwnd, &cfs.ptCurrentPos, 1);
1614 	pImmSetCompositionWindow(hImc, &cfs);
1615 
1616 	pImmReleaseContext(s_hwnd, hImc);
1617     }
1618 }
1619 
1620 /*
1621  * Set IM status on ("active" is TRUE) or off ("active" is FALSE).
1622  */
1623     void
1624 im_set_active(int active)
1625 {
1626     HIMC	hImc;
1627     static HIMC	hImcOld = (HIMC)0;
1628 
1629     if (pImmGetContext)	    /* if NULL imm32.dll wasn't loaded (yet) */
1630     {
1631 	if (p_imdisable)
1632 	{
1633 	    if (hImcOld == (HIMC)0)
1634 	    {
1635 		hImcOld = pImmGetContext(s_hwnd);
1636 		if (hImcOld)
1637 		    pImmAssociateContext(s_hwnd, (HIMC)0);
1638 	    }
1639 	    active = FALSE;
1640 	}
1641 	else if (hImcOld != (HIMC)0)
1642 	{
1643 	    pImmAssociateContext(s_hwnd, hImcOld);
1644 	    hImcOld = (HIMC)0;
1645 	}
1646 
1647 	hImc = pImmGetContext(s_hwnd);
1648 	if (hImc)
1649 	{
1650 	    pImmSetOpenStatus(hImc, active);
1651 	    pImmReleaseContext(s_hwnd, hImc);
1652 	}
1653     }
1654 }
1655 
1656 /*
1657  * Get IM status.  When IM is on, return not 0.  Else return 0.
1658  */
1659     int
1660 im_get_status()
1661 {
1662     int		status = 0;
1663     HIMC	hImc;
1664 
1665     if (pImmGetContext && (hImc = pImmGetContext(s_hwnd)) != (HIMC)0)
1666     {
1667 	status = pImmGetOpenStatus(hImc) ? 1 : 0;
1668 	pImmReleaseContext(s_hwnd, hImc);
1669     }
1670     return status;
1671 }
1672 
1673 #endif /* FEAT_MBYTE && FEAT_MBYTE_IME */
1674 
1675 #if defined(FEAT_MBYTE) && !defined(FEAT_MBYTE_IME) && defined(GLOBAL_IME)
1676 /* Win32 with GLOBAL IME */
1677 
1678 /*
1679  * Notify cursor position to IM.
1680  */
1681     void
1682 im_set_position(int row, int col)
1683 {
1684     /* Win32 with GLOBAL IME */
1685     POINT p;
1686 
1687     p.x = FILL_X(col);
1688     p.y = FILL_Y(row);
1689     MapWindowPoints(s_textArea, s_hwnd, &p, 1);
1690     global_ime_set_position(&p);
1691 }
1692 
1693 /*
1694  * Set IM status on ("active" is TRUE) or off ("active" is FALSE).
1695  */
1696     void
1697 im_set_active(int active)
1698 {
1699     global_ime_set_status(active);
1700 }
1701 
1702 /*
1703  * Get IM status.  When IM is on, return not 0.  Else return 0.
1704  */
1705     int
1706 im_get_status()
1707 {
1708     return global_ime_get_status();
1709 }
1710 #endif
1711 
1712 
1713 #ifdef FEAT_RIGHTLEFT
1714 /*
1715  * What is this for?  In the case where you are using Win98 or Win2K or later,
1716  * and you are using a Hebrew font (or Arabic!), Windows does you a favor and
1717  * reverses the string sent to the TextOut... family.  This sucks, because we
1718  * go to a lot of effort to do the right thing, and there doesn't seem to be a
1719  * way to tell Windblows not to do this!
1720  *
1721  * The short of it is that this 'RevOut' only gets called if you are running
1722  * one of the new, "improved" MS OSes, and only if you are running in
1723  * 'rightleft' mode.  It makes display take *slightly* longer, but not
1724  * noticeably so.
1725  */
1726     static void
1727 RevOut( HDC s_hdc,
1728 	int col,
1729 	int row,
1730 	UINT foptions,
1731 	CONST RECT *pcliprect,
1732 	LPCTSTR text,
1733 	UINT len,
1734 	CONST INT *padding)
1735 {
1736     int		ix;
1737     static int	special = -1;
1738 
1739     if (special == -1)
1740     {
1741 	/* Check windows version: special treatment is needed if it is NT 5 or
1742 	 * Win98 or higher. */
1743 	if  ((os_version.dwPlatformId == VER_PLATFORM_WIN32_NT
1744 		    && os_version.dwMajorVersion >= 5)
1745 		|| (os_version.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS
1746 		    && (os_version.dwMajorVersion > 4
1747 			|| (os_version.dwMajorVersion == 4
1748 			    && os_version.dwMinorVersion > 0))))
1749 	    special = 1;
1750 	else
1751 	    special = 0;
1752     }
1753 
1754     if (special)
1755 	for (ix = 0; ix < (int)len; ++ix)
1756 	    ExtTextOut(s_hdc, col + TEXT_X(ix), row, foptions,
1757 					    pcliprect, text + ix, 1, padding);
1758     else
1759 	ExtTextOut(s_hdc, col, row, foptions, pcliprect, text, len, padding);
1760 }
1761 #endif
1762 
1763     void
1764 gui_mch_draw_string(
1765     int		row,
1766     int		col,
1767     char_u	*text,
1768     int		len,
1769     int		flags)
1770 {
1771     static int	*padding = NULL;
1772     static int	pad_size = 0;
1773     int		i;
1774     const RECT	*pcliprect = NULL;
1775     UINT	foptions = 0;
1776 #ifdef FEAT_MBYTE
1777     static WCHAR *unicodebuf = NULL;
1778     static int   *unicodepdy = NULL;
1779     int		unibuflen = 0;
1780     int		n = 0;
1781 #endif
1782     HPEN	hpen, old_pen;
1783     int		y;
1784 
1785 #ifndef MSWIN16_FASTTEXT
1786     /*
1787      * Italic and bold text seems to have an extra row of pixels at the bottom
1788      * (below where the bottom of the character should be).  If we draw the
1789      * characters with a solid background, the top row of pixels in the
1790      * character below will be overwritten.  We can fix this by filling in the
1791      * background ourselves, to the correct character proportions, and then
1792      * writing the character in transparent mode.  Still have a problem when
1793      * the character is "_", which gets written on to the character below.
1794      * New fix: set gui.char_ascent to -1.  This shifts all characters up one
1795      * pixel in their slots, which fixes the problem with the bottom row of
1796      * pixels.	We still need this code because otherwise the top row of pixels
1797      * becomes a problem. - webb.
1798      */
1799     static HBRUSH	hbr_cache[2] = {NULL, NULL};
1800     static guicolor_T	brush_color[2] = {INVALCOLOR, INVALCOLOR};
1801     static int		brush_lru = 0;
1802     HBRUSH		hbr;
1803     RECT		rc;
1804 
1805     if (!(flags & DRAW_TRANSP))
1806     {
1807 	/*
1808 	 * Clear background first.
1809 	 * Note: FillRect() excludes right and bottom of rectangle.
1810 	 */
1811 	rc.left = FILL_X(col);
1812 	rc.top = FILL_Y(row);
1813 #ifdef FEAT_MBYTE
1814 	if (has_mbyte)
1815 	{
1816 	    int cell_len = 0;
1817 
1818 	    /* Compute the length in display cells. */
1819 	    for (n = 0; n < len; n += MB_BYTE2LEN(text[n]))
1820 		cell_len += (*mb_ptr2cells)(text + n);
1821 	    rc.right = FILL_X(col + cell_len);
1822 	}
1823 	else
1824 #endif
1825 	    rc.right = FILL_X(col + len);
1826 	rc.bottom = FILL_Y(row + 1);
1827 
1828 	/* Cache the created brush, that saves a lot of time.  We need two:
1829 	 * one for cursor background and one for the normal background. */
1830 	if (gui.currBgColor == brush_color[0])
1831 	{
1832 	    hbr = hbr_cache[0];
1833 	    brush_lru = 1;
1834 	}
1835 	else if (gui.currBgColor == brush_color[1])
1836 	{
1837 	    hbr = hbr_cache[1];
1838 	    brush_lru = 0;
1839 	}
1840 	else
1841 	{
1842 	    if (hbr_cache[brush_lru] != NULL)
1843 		DeleteBrush(hbr_cache[brush_lru]);
1844 	    hbr_cache[brush_lru] = CreateSolidBrush(gui.currBgColor);
1845 	    brush_color[brush_lru] = gui.currBgColor;
1846 	    hbr = hbr_cache[brush_lru];
1847 	    brush_lru = !brush_lru;
1848 	}
1849 	FillRect(s_hdc, &rc, hbr);
1850 
1851 	SetBkMode(s_hdc, TRANSPARENT);
1852 
1853 	/*
1854 	 * When drawing block cursor, prevent inverted character spilling
1855 	 * over character cell (can happen with bold/italic)
1856 	 */
1857 	if (flags & DRAW_CURSOR)
1858 	{
1859 	    pcliprect = &rc;
1860 	    foptions = ETO_CLIPPED;
1861 	}
1862     }
1863 #else
1864     /*
1865      * The alternative would be to write the characters in opaque mode, but
1866      * when the text is not exactly the same proportions as normal text, too
1867      * big or too little a rectangle gets drawn for the background.
1868      */
1869     SetBkMode(s_hdc, OPAQUE);
1870     SetBkColor(s_hdc, gui.currBgColor);
1871 #endif
1872     SetTextColor(s_hdc, gui.currFgColor);
1873     SelectFont(s_hdc, gui.currFont);
1874 
1875     if (pad_size != Columns || padding == NULL || padding[0] != gui.char_width)
1876     {
1877 	vim_free(padding);
1878 	pad_size = Columns;
1879 
1880 	padding = (int *)alloc(pad_size * sizeof(int));
1881 	if (padding != NULL)
1882 	    for (i = 0; i < pad_size; i++)
1883 		padding[i] = gui.char_width;
1884     }
1885 
1886     /* On NT, tell the font renderer not to "help" us with Hebrew and Arabic
1887      * text.  This doesn't work in 9x, so we have to deal with it manually on
1888      * those systems. */
1889     if (os_version.dwPlatformId == VER_PLATFORM_WIN32_NT)
1890 	foptions |= ETO_IGNORELANGUAGE;
1891 
1892     /*
1893      * We have to provide the padding argument because italic and bold versions
1894      * of fixed-width fonts are often one pixel or so wider than their normal
1895      * versions.
1896      * No check for DRAW_BOLD, Windows will have done it already.
1897      */
1898 
1899 #ifdef FEAT_MBYTE
1900     /* Check if there are any UTF-8 characters.  If not, use normal text
1901      * output to speed up output. */
1902     if (enc_utf8)
1903 	for (n = 0; n < len; ++n)
1904 	    if (text[n] >= 0x80)
1905 		break;
1906 
1907     /* Check if the Unicode buffer exists and is big enough.  Create it
1908      * with the same lengt as the multi-byte string, the number of wide
1909      * characters is always equal or smaller. */
1910     if ((enc_utf8 || (enc_codepage > 0 && (int)GetACP() != enc_codepage))
1911 	    && (unicodebuf == NULL || len > unibuflen))
1912     {
1913 	vim_free(unicodebuf);
1914 	unicodebuf = (WCHAR *)alloc(len * sizeof(WCHAR));
1915 
1916 	vim_free(unicodepdy);
1917 	unicodepdy = (int *)alloc(len * sizeof(int));
1918 
1919 	unibuflen = len;
1920     }
1921 
1922     if (enc_utf8 && n < len && unicodebuf != NULL)
1923     {
1924 	/* Output UTF-8 characters.  Caller has already separated
1925 	 * composing characters. */
1926 	int		i = 0;
1927 	int		clen;	/* string length up to composing char */
1928 	int		cells;	/* cell width of string up to composing char */
1929 	int		cw;	/* width of current cell */
1930 
1931 	cells = 0;
1932 	for (clen = 0; i < len; )
1933 	{
1934 	    unicodebuf[clen] = utf_ptr2char(text + i);
1935 	    cw = utf_char2cells(unicodebuf[clen]);
1936 	    if (cw > 2)		/* don't use 4 for unprintable char */
1937 		cw = 1;
1938 	    if (unicodepdy != NULL)
1939 	    {
1940 		/* Use unicodepdy to make characters fit as we expect, even
1941 		 * when the font uses different widths (e.g., bold character
1942 		 * is wider).  */
1943 		unicodepdy[clen] = cw * gui.char_width;
1944 	    }
1945 	    cells += cw;
1946 	    i += utfc_ptr2len_check_len(text + i, len - i);
1947 	    ++clen;
1948 	}
1949 	ExtTextOutW(s_hdc, TEXT_X(col), TEXT_Y(row),
1950 			     foptions, pcliprect, unicodebuf, clen, unicodepdy);
1951 	len = cells;	/* used for underlining */
1952     }
1953     else if (enc_codepage > 0 && (int)GetACP() != enc_codepage)
1954     {
1955 	/* If we want to display codepage data, and the current CP is not the
1956 	 * ANSI one, we need to go via Unicode. */
1957 	if (unicodebuf != NULL)
1958 	{
1959 	    len = MultiByteToWideChar(enc_codepage,
1960 			MB_PRECOMPOSED,
1961 			(char *)text, len,
1962 			(LPWSTR)unicodebuf, unibuflen);
1963 	    if (len != 0)
1964 		ExtTextOutW(s_hdc, TEXT_X(col), TEXT_Y(row),
1965 			      foptions, pcliprect, unicodebuf, len, NULL);
1966 	}
1967     }
1968     else
1969 #endif
1970     {
1971 #ifdef FEAT_RIGHTLEFT
1972 	/* If we can't use ETO_IGNORELANGUAGE, we can't tell Windows not to
1973 	 * mess up RL text, so we have to draw it character-by-character.
1974 	 * Only do this if RL is on, since it's slow. */
1975 	if (curwin->w_p_rl && !(foptions & ETO_IGNORELANGUAGE))
1976 	    RevOut(s_hdc, TEXT_X(col), TEXT_Y(row),
1977 			 foptions, pcliprect, (char *)text, len, padding);
1978 	else
1979 #endif
1980 	    ExtTextOut(s_hdc, TEXT_X(col), TEXT_Y(row),
1981 			 foptions, pcliprect, (char *)text, len, padding);
1982     }
1983 
1984     if (flags & DRAW_UNDERL)
1985     {
1986 	hpen = CreatePen(PS_SOLID, 1, gui.currFgColor);
1987 	old_pen = SelectObject(s_hdc, hpen);
1988 	/* When p_linespace is 0, overwrite the bottom row of pixels.
1989 	 * Otherwise put the line just below the character. */
1990 	y = FILL_Y(row + 1) - 1;
1991 #ifndef MSWIN16_FASTTEXT
1992 	if (p_linespace > 1)
1993 	    y -= p_linespace - 1;
1994 #endif
1995 	MoveToEx(s_hdc, FILL_X(col), y, NULL);
1996 	/* Note: LineTo() excludes the last pixel in the line. */
1997 	LineTo(s_hdc, FILL_X(col + len), y);
1998 	DeleteObject(SelectObject(s_hdc, old_pen));
1999     }
2000 }
2001 
2002 
2003 /*
2004  * Output routines.
2005  */
2006 
2007 /* Flush any output to the screen */
2008     void
2009 gui_mch_flush(void)
2010 {
2011 #   if defined(__BORLANDC__)
2012     /*
2013      * The GdiFlush declaration (in Borland C 5.01 <wingdi.h>) is not a
2014      * prototype declaration.
2015      * The compiler complains if __stdcall is not used in both declarations.
2016      */
2017     BOOL  __stdcall GdiFlush(void);
2018 #   endif
2019 
2020     GdiFlush();
2021 }
2022 
2023     static void
2024 clear_rect(RECT *rcp)
2025 {
2026     HBRUSH  hbr;
2027 
2028     hbr = CreateSolidBrush(gui.back_pixel);
2029     FillRect(s_hdc, rcp, hbr);
2030     DeleteBrush(hbr);
2031 }
2032 
2033 
2034 #if defined(FEAT_MENU) || defined(PROTO)
2035 /*
2036  * Add a sub menu to the menu bar.
2037  */
2038     void
2039 gui_mch_add_menu(
2040     vimmenu_T	*menu,
2041     int		pos)
2042 {
2043     vimmenu_T	*parent = menu->parent;
2044 
2045     menu->submenu_id = CreatePopupMenu();
2046     menu->id = s_menu_id++;
2047 
2048     if (menu_is_menubar(menu->name))
2049     {
2050 	if (is_winnt_3())
2051 	{
2052 	    InsertMenu((parent == NULL) ? s_menuBar : parent->submenu_id,
2053 		    (UINT)pos, MF_POPUP | MF_STRING | MF_BYPOSITION,
2054 		    (UINT)menu->submenu_id, (LPCTSTR) menu->name);
2055 	}
2056 	else
2057 	{
2058 #ifdef FEAT_MBYTE
2059 	    WCHAR	*wn = NULL;
2060 	    int		n;
2061 
2062 	    if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
2063 	    {
2064 		/* 'encoding' differs from active codepage: convert menu name
2065 		 * and use wide function */
2066 		wn = enc_to_ucs2(menu->name, NULL);
2067 		if (wn != NULL)
2068 		{
2069 		    MENUITEMINFOW	infow;
2070 
2071 		    infow.cbSize = sizeof(infow);
2072 		    infow.fMask = MIIM_DATA | MIIM_TYPE | MIIM_ID
2073 							       | MIIM_SUBMENU;
2074 		    infow.dwItemData = (DWORD)menu;
2075 		    infow.wID = menu->id;
2076 		    infow.fType = MFT_STRING;
2077 		    infow.dwTypeData = wn;
2078 		    infow.cch = wcslen(wn);
2079 		    infow.hSubMenu = menu->submenu_id;
2080 		    n = InsertMenuItemW((parent == NULL)
2081 					    ? s_menuBar : parent->submenu_id,
2082 					    (UINT)pos, TRUE, &infow);
2083 		    vim_free(wn);
2084 		    if (n == 0 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
2085 			/* Failed, try using non-wide function. */
2086 			wn = NULL;
2087 		}
2088 	    }
2089 
2090 	    if (wn == NULL)
2091 #endif
2092 	    {
2093 		MENUITEMINFO	info;
2094 
2095 		info.cbSize = sizeof(info);
2096 		info.fMask = MIIM_DATA | MIIM_TYPE | MIIM_ID | MIIM_SUBMENU;
2097 		info.dwItemData = (DWORD)menu;
2098 		info.wID = menu->id;
2099 		info.fType = MFT_STRING;
2100 		info.dwTypeData = (LPTSTR)menu->name;
2101 		info.cch = (UINT)STRLEN(menu->name);
2102 		info.hSubMenu = menu->submenu_id;
2103 		InsertMenuItem((parent == NULL)
2104 					? s_menuBar : parent->submenu_id,
2105 					(UINT)pos, TRUE, &info);
2106 	    }
2107 	}
2108     }
2109 
2110     /* Fix window size if menu may have wrapped */
2111     if (parent == NULL)
2112 	gui_mswin_get_menu_height(!gui.starting);
2113 #ifdef FEAT_TEAROFF
2114     else if (IsWindow(parent->tearoff_handle))
2115 	rebuild_tearoff(parent);
2116 #endif
2117 }
2118 
2119     void
2120 gui_mch_show_popupmenu(vimmenu_T *menu)
2121 {
2122     POINT mp;
2123 
2124     (void)GetCursorPos((LPPOINT)&mp);
2125     gui_mch_show_popupmenu_at(menu, (int)mp.x, (int)mp.y);
2126 }
2127 
2128     void
2129 gui_make_popup(char_u *path_name)
2130 {
2131     vimmenu_T	*menu = gui_find_menu(path_name);
2132 
2133     if (menu != NULL)
2134     {
2135 	POINT	p;
2136 
2137 	/* Find the position of the current cursor */
2138 	GetDCOrgEx(s_hdc, &p);
2139 	if (curwin != NULL)
2140 	{
2141 	    p.x += TEXT_X(W_WINCOL(curwin) + curwin->w_wcol + 1);
2142 	    p.y += TEXT_Y(W_WINROW(curwin) + curwin->w_wrow + 1);
2143 	}
2144 	msg_scroll = FALSE;
2145 	gui_mch_show_popupmenu_at(menu, (int)p.x, (int)p.y);
2146     }
2147 }
2148 
2149 #if defined(FEAT_TEAROFF) || defined(PROTO)
2150 /*
2151  * Given a menu descriptor, e.g. "File.New", find it in the menu hierarchy and
2152  * create it as a pseudo-"tearoff menu".
2153  */
2154     void
2155 gui_make_tearoff(char_u *path_name)
2156 {
2157     vimmenu_T	*menu = gui_find_menu(path_name);
2158 
2159     /* Found the menu, so tear it off. */
2160     if (menu != NULL)
2161 	gui_mch_tearoff(menu->dname, menu, 0xffffL, 0xffffL);
2162 }
2163 #endif
2164 
2165 /*
2166  * Add a menu item to a menu
2167  */
2168     void
2169 gui_mch_add_menu_item(
2170     vimmenu_T	*menu,
2171     int		idx)
2172 {
2173     vimmenu_T	*parent = menu->parent;
2174 
2175     menu->id = s_menu_id++;
2176     menu->submenu_id = NULL;
2177 
2178 #ifdef FEAT_TEAROFF
2179     if (STRNCMP(menu->name, TEAR_STRING, TEAR_LEN) == 0)
2180     {
2181 	InsertMenu(parent->submenu_id, (UINT)idx, MF_BITMAP|MF_BYPOSITION,
2182 		(UINT)menu->id, (LPCTSTR) s_htearbitmap);
2183     }
2184     else
2185 #endif
2186 #ifdef FEAT_TOOLBAR
2187     if (menu_is_toolbar(parent->name))
2188     {
2189 	TBBUTTON newtb;
2190 
2191 	vim_memset(&newtb, 0, sizeof(newtb));
2192 	if (menu_is_separator(menu->name))
2193 	{
2194 	    newtb.iBitmap = 0;
2195 	    newtb.fsStyle = TBSTYLE_SEP;
2196 	}
2197 	else
2198 	{
2199 	    newtb.iBitmap = get_toolbar_bitmap(menu);
2200 	    newtb.fsStyle = TBSTYLE_BUTTON;
2201 	}
2202 	newtb.idCommand = menu->id;
2203 	newtb.fsState = TBSTATE_ENABLED;
2204 	newtb.iString = 0;
2205 	SendMessage(s_toolbarhwnd, TB_INSERTBUTTON, (WPARAM)idx,
2206 							     (LPARAM)&newtb);
2207 	menu->submenu_id = (HMENU)-1;
2208     }
2209     else
2210 #endif
2211     {
2212 #ifdef FEAT_MBYTE
2213 	WCHAR	*wn = NULL;
2214 	int	n;
2215 
2216 	if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
2217 	{
2218 	    /* 'encoding' differs from active codepage: convert menu item name
2219 	     * and use wide function */
2220 	    wn = enc_to_ucs2(menu->name, NULL);
2221 	    if (wn != NULL)
2222 	    {
2223 		n = InsertMenuW(parent->submenu_id, (UINT)idx,
2224 			(menu_is_separator(menu->name)
2225 				 ? MF_SEPARATOR : MF_STRING) | MF_BYPOSITION,
2226 			(UINT)menu->id, wn);
2227 		vim_free(wn);
2228 		if (n == 0 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
2229 		    /* Failed, try using non-wide function. */
2230 		    wn = NULL;
2231 	    }
2232 	}
2233 	if (wn == NULL)
2234 #endif
2235 	    InsertMenu(parent->submenu_id, (UINT)idx,
2236 		(menu_is_separator(menu->name) ? MF_SEPARATOR : MF_STRING)
2237 							      | MF_BYPOSITION,
2238 		(UINT)menu->id, (LPCTSTR)menu->name);
2239 #ifdef FEAT_TEAROFF
2240 	if (IsWindow(parent->tearoff_handle))
2241 	    rebuild_tearoff(parent);
2242 #endif
2243     }
2244 }
2245 
2246 /*
2247  * Destroy the machine specific menu widget.
2248  */
2249     void
2250 gui_mch_destroy_menu(vimmenu_T *menu)
2251 {
2252 #ifdef FEAT_TOOLBAR
2253     /*
2254      * is this a toolbar button?
2255      */
2256     if (menu->submenu_id == (HMENU)-1)
2257     {
2258 	int iButton;
2259 
2260 	iButton = (int)SendMessage(s_toolbarhwnd, TB_COMMANDTOINDEX,
2261 							 (WPARAM)menu->id, 0);
2262 	SendMessage(s_toolbarhwnd, TB_DELETEBUTTON, (WPARAM)iButton, 0);
2263     }
2264     else
2265 #endif
2266     {
2267 	if (menu->parent != NULL
2268 		&& menu_is_popup(menu->parent->dname)
2269 		&& menu->parent->submenu_id != NULL)
2270 	    RemoveMenu(menu->parent->submenu_id, menu->id, MF_BYCOMMAND);
2271 	else
2272 	    RemoveMenu(s_menuBar, menu->id, MF_BYCOMMAND);
2273 	if (menu->submenu_id != NULL)
2274 	    DestroyMenu(menu->submenu_id);
2275 #ifdef FEAT_TEAROFF
2276 	if (IsWindow(menu->tearoff_handle))
2277 	    DestroyWindow(menu->tearoff_handle);
2278 	if (menu->parent != NULL
2279 		&& menu->parent->children != NULL
2280 		&& IsWindow(menu->parent->tearoff_handle))
2281 	{
2282 	    /* This menu must not show up when rebuilding the tearoff window. */
2283 	    menu->modes = 0;
2284 	    rebuild_tearoff(menu->parent);
2285 	}
2286 #endif
2287     }
2288 }
2289 
2290 #ifdef FEAT_TEAROFF
2291     static void
2292 rebuild_tearoff(vimmenu_T *menu)
2293 {
2294     /*hackish*/
2295     char_u	tbuf[128];
2296     RECT	trect;
2297     RECT	rct;
2298     RECT	roct;
2299     int		x, y;
2300 
2301     HWND thwnd = menu->tearoff_handle;
2302 
2303     GetWindowText(thwnd, tbuf, 127);
2304     if (GetWindowRect(thwnd, &trect)
2305 	    && GetWindowRect(s_hwnd, &rct)
2306 	    && GetClientRect(s_hwnd, &roct))
2307     {
2308 	x = trect.left - rct.left;
2309 	y = (trect.top -  rct.bottom  + roct.bottom);
2310     }
2311     else
2312     {
2313 	x = y = 0xffffL;
2314     }
2315     DestroyWindow(thwnd);
2316     if (menu->children != NULL)
2317     {
2318 	gui_mch_tearoff(tbuf, menu, x, y);
2319 	if (IsWindow(menu->tearoff_handle))
2320 	    (void) SetWindowPos(menu->tearoff_handle,
2321 				NULL,
2322 				(int)trect.left,
2323 				(int)trect.top,
2324 				0, 0,
2325 				SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
2326     }
2327 }
2328 #endif /* FEAT_TEAROFF */
2329 
2330 /*
2331  * Make a menu either grey or not grey.
2332  */
2333     void
2334 gui_mch_menu_grey(
2335     vimmenu_T	*menu,
2336     int	    grey)
2337 {
2338 #ifdef FEAT_TOOLBAR
2339     /*
2340      * is this a toolbar button?
2341      */
2342     if (menu->submenu_id == (HMENU)-1)
2343     {
2344 	SendMessage(s_toolbarhwnd, TB_ENABLEBUTTON,
2345 	    (WPARAM)menu->id, (LPARAM) MAKELONG((grey ? FALSE : TRUE), 0) );
2346     }
2347     else
2348 #endif
2349     if (grey)
2350 	EnableMenuItem(s_menuBar, menu->id, MF_BYCOMMAND | MF_GRAYED);
2351     else
2352 	EnableMenuItem(s_menuBar, menu->id, MF_BYCOMMAND | MF_ENABLED);
2353 
2354 #ifdef FEAT_TEAROFF
2355     if ((menu->parent != NULL) && (IsWindow(menu->parent->tearoff_handle)))
2356     {
2357 	WORD menuID;
2358 	HWND menuHandle;
2359 
2360 	/*
2361 	 * A tearoff button has changed state.
2362 	 */
2363 	if (menu->children == NULL)
2364 	    menuID = (WORD)(menu->id);
2365 	else
2366 	    menuID = (WORD)((DWORD)(menu->submenu_id) | (DWORD)0x8000);
2367 	menuHandle = GetDlgItem(menu->parent->tearoff_handle, menuID);
2368 	if (menuHandle)
2369 	    EnableWindow(menuHandle, !grey);
2370 
2371     }
2372 #endif
2373 }
2374 
2375 #endif /* FEAT_MENU */
2376 
2377 
2378 /* define some macros used to make the dialogue creation more readable */
2379 
2380 #define add_string(s) strcpy((LPSTR)p, s); (LPSTR)p += (strlen((LPSTR)p) + 1)
2381 #define add_word(x)		*p++ = (x)
2382 #define add_byte(x)		*((LPSTR)p)++ = (x)
2383 #define add_long(x)		*((LPDWORD)p)++ = (x)
2384 
2385 #if defined(FEAT_GUI_DIALOG) || defined(PROTO)
2386 /*
2387  * stuff for dialogs
2388  */
2389 
2390 /*
2391  * The callback routine used by all the dialogs.  Very simple.  First,
2392  * acknowledges the INITDIALOG message so that Windows knows to do standard
2393  * dialog stuff (Return = default, Esc = cancel....) Second, if a button is
2394  * pressed, return that button's ID - IDCANCEL (2), which is the button's
2395  * number.
2396  */
2397     static LRESULT CALLBACK
2398 dialog_callback(
2399     HWND hwnd,
2400     UINT message,
2401     WPARAM wParam,
2402     LPARAM lParam)
2403 {
2404     if (message == WM_INITDIALOG)
2405     {
2406 	CenterWindow(hwnd, GetWindow(hwnd, GW_OWNER));
2407 	/* Set focus to the dialog.  Set the default button, if specified. */
2408 	(void)SetFocus(hwnd);
2409 	if (dialog_default_button > IDCANCEL)
2410 	    (void)SetFocus(GetDlgItem(hwnd, dialog_default_button));
2411 	return FALSE;
2412     }
2413 
2414     if (message == WM_COMMAND)
2415     {
2416 	int	button = LOWORD(wParam);
2417 
2418 	/* Don't end the dialog if something was selected that was
2419 	 * not a button.
2420 	 */
2421 	if (button >= DLG_NONBUTTON_CONTROL)
2422 	    return TRUE;
2423 
2424 	/* If the edit box exists, copy the string. */
2425 	if (s_textfield != NULL)
2426 	    GetDlgItemText(hwnd, DLG_NONBUTTON_CONTROL + 2,
2427 							 s_textfield, IOSIZE);
2428 
2429 	/*
2430 	 * Need to check for IDOK because if the user just hits Return to
2431 	 * accept the default value, some reason this is what we get.
2432 	 */
2433 	if (button == IDOK)
2434 	{
2435 	    if (dialog_default_button > IDCANCEL)
2436 		EndDialog(hwnd, dialog_default_button);
2437 	}
2438 	else
2439 	    EndDialog(hwnd, button - IDCANCEL);
2440 	return TRUE;
2441     }
2442 
2443     if ((message == WM_SYSCOMMAND) && (wParam == SC_CLOSE))
2444     {
2445 	EndDialog(hwnd, 0);
2446 	return TRUE;
2447     }
2448     return FALSE;
2449 }
2450 
2451 /*
2452  * Create a dialog dynamically from the parameter strings.
2453  * type		= type of dialog (question, alert, etc.)
2454  * title	= dialog title. may be NULL for default title.
2455  * message	= text to display. Dialog sizes to accommodate it.
2456  * buttons	= '\n' separated list of button captions, default first.
2457  * dfltbutton	= number of default button.
2458  *
2459  * This routine returns 1 if the first button is pressed,
2460  *			2 for the second, etc.
2461  *
2462  *			0 indicates Esc was pressed.
2463  *			-1 for unexpected error
2464  *
2465  * If stubbing out this fn, return 1.
2466  */
2467 
2468 static const char_u *dlg_icons[] = /* must match names in resource file */
2469 {
2470     "IDR_VIM",
2471     "IDR_VIM_ERROR",
2472     "IDR_VIM_ALERT",
2473     "IDR_VIM_INFO",
2474     "IDR_VIM_QUESTION"
2475 };
2476 
2477 #ifdef USE_SYSMENU_FONT
2478 /*
2479  * Get Menu Font.
2480  * Return OK or FAIL.
2481  */
2482     static int
2483 gui_w32_get_menu_font(LOGFONT *lf)
2484 {
2485     NONCLIENTMETRICS nm;
2486 
2487     nm.cbSize = sizeof(NONCLIENTMETRICS);
2488     if (!SystemParametersInfo(
2489 	    SPI_GETNONCLIENTMETRICS,
2490 	    sizeof(NONCLIENTMETRICS),
2491 	    &nm,
2492 	    0))
2493 	return FAIL;
2494     *lf = nm.lfMenuFont;
2495     return OK;
2496 }
2497 #endif
2498 
2499     int
2500 gui_mch_dialog(
2501     int		 type,
2502     char_u	*title,
2503     char_u	*message,
2504     char_u	*buttons,
2505     int		 dfltbutton,
2506     char_u	*textfield)
2507 {
2508     WORD	*p, *pdlgtemplate, *pnumitems;
2509     int		numButtons;
2510     int		*buttonWidths, *buttonPositions;
2511     int		buttonYpos;
2512     int		nchar, i;
2513     DWORD	lStyle;
2514     int		dlgwidth = 0;
2515     int		dlgheight;
2516     int		editboxheight;
2517     int		horizWidth = 0;
2518     int		msgheight;
2519     char_u	*pstart;
2520     char_u	*pend;
2521     char_u	*tbuffer;
2522     RECT	rect;
2523     HWND	hwnd;
2524     HDC		hdc;
2525     HFONT	font, oldFont;
2526     TEXTMETRIC	fontInfo;
2527     int		fontHeight;
2528     int		textWidth, minButtonWidth, messageWidth;
2529     int		maxDialogWidth;
2530     int		vertical;
2531     int		dlgPaddingX;
2532     int		dlgPaddingY;
2533 #ifdef USE_SYSMENU_FONT
2534     LOGFONT	lfSysmenu;
2535     int		use_lfSysmenu = FALSE;
2536 #endif
2537 
2538 #ifndef NO_CONSOLE
2539     /* Don't output anything in silent mode ("ex -s") */
2540     if (silent_mode)
2541 	return dfltbutton;   /* return default option */
2542 #endif
2543 
2544     /* If there is no window yet, open it. */
2545     if (s_hwnd == NULL && gui_mch_init() == FAIL)
2546 	return dfltbutton;
2547 
2548     if ((type < 0) || (type > VIM_LAST_TYPE))
2549 	type = 0;
2550 
2551     /* allocate some memory for dialog template */
2552     /* TODO should compute this really*/
2553     pdlgtemplate = p = (PWORD)LocalAlloc(LPTR, DLG_ALLOC_SIZE);
2554 
2555     if (p == NULL)
2556 	return -1;
2557 
2558     /*
2559      * make a copy of 'buttons' to fiddle with it.  complier grizzles because
2560      * vim_strsave() doesn't take a const arg (why not?), so cast away the
2561      * const.
2562      */
2563     tbuffer = vim_strsave(buttons);
2564     if (tbuffer == NULL)
2565 	return -1;
2566 
2567     --dfltbutton;   /* Change from one-based to zero-based */
2568 
2569     /* Count buttons */
2570     numButtons = 1;
2571     for (i = 0; tbuffer[i] != '\0'; i++)
2572     {
2573 	if (tbuffer[i] == DLG_BUTTON_SEP)
2574 	    numButtons++;
2575     }
2576     if (dfltbutton >= numButtons)
2577 	dfltbutton = -1;
2578 
2579     /* Allocate array to hold the width of each button */
2580     buttonWidths = (int *) lalloc(numButtons * sizeof(int), TRUE);
2581     if (buttonWidths == NULL)
2582 	return -1;
2583 
2584     /* Allocate array to hold the X position of each button */
2585     buttonPositions = (int *) lalloc(numButtons * sizeof(int), TRUE);
2586     if (buttonPositions == NULL)
2587 	return -1;
2588 
2589     /*
2590      * Calculate how big the dialog must be.
2591      */
2592     hwnd = GetDesktopWindow();
2593     hdc = GetWindowDC(hwnd);
2594 #ifdef USE_SYSMENU_FONT
2595     if (gui_w32_get_menu_font(&lfSysmenu) == OK)
2596     {
2597 	font = CreateFontIndirect(&lfSysmenu);
2598 	use_lfSysmenu = TRUE;
2599     }
2600     else
2601 #endif
2602     font = CreateFont(-DLG_FONT_POINT_SIZE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2603 		      VARIABLE_PITCH , DLG_FONT_NAME);
2604     if (s_usenewlook)
2605     {
2606 	oldFont = SelectFont(hdc, font);
2607 	dlgPaddingX = DLG_PADDING_X;
2608 	dlgPaddingY = DLG_PADDING_Y;
2609     }
2610     else
2611     {
2612 	oldFont = SelectFont(hdc, GetStockObject(SYSTEM_FONT));
2613 	dlgPaddingX = DLG_OLD_STYLE_PADDING_X;
2614 	dlgPaddingY = DLG_OLD_STYLE_PADDING_Y;
2615     }
2616     GetTextMetrics(hdc, &fontInfo);
2617     fontHeight = fontInfo.tmHeight;
2618 
2619     /* Minimum width for horizontal button */
2620     minButtonWidth = GetTextWidth(hdc, "Cancel", 6);
2621 
2622     /* Maximum width of a dialog, if possible */
2623     GetWindowRect(s_hwnd, &rect);
2624     maxDialogWidth = rect.right - rect.left
2625 		     - GetSystemMetrics(SM_CXFRAME) * 2;
2626     if (maxDialogWidth < DLG_MIN_MAX_WIDTH)
2627 	maxDialogWidth = DLG_MIN_MAX_WIDTH;
2628 
2629     /* Set dlgwidth to width of message */
2630     pstart = message;
2631     messageWidth = 0;
2632     msgheight = 0;
2633     do
2634     {
2635 	pend = vim_strchr(pstart, DLG_BUTTON_SEP);
2636 	if (pend == NULL)
2637 	    pend = pstart + STRLEN(pstart);	/* Last line of message. */
2638 	msgheight += fontHeight;
2639 	textWidth = GetTextWidth(hdc, pstart, (int)(pend - pstart));
2640 	if (textWidth > messageWidth)
2641 	    messageWidth = textWidth;
2642 	pstart = pend + 1;
2643     } while (*pend != NUL);
2644     dlgwidth = messageWidth;
2645 
2646     /* Add width of icon to dlgwidth, and some space */
2647     dlgwidth += DLG_ICON_WIDTH + 3 * dlgPaddingX;
2648 
2649     if (msgheight < DLG_ICON_HEIGHT)
2650 	msgheight = DLG_ICON_HEIGHT;
2651 
2652     /*
2653      * Check button names.  A long one will make the dialog wider.
2654      */
2655     vertical = (vim_strchr(p_go, GO_VERTICAL) != NULL);
2656     if (!vertical)
2657     {
2658 	// Place buttons horizontally if they fit.
2659 	horizWidth = dlgPaddingX;
2660 	pstart = tbuffer;
2661 	i = 0;
2662 	do
2663 	{
2664 	    pend = vim_strchr(pstart, DLG_BUTTON_SEP);
2665 	    if (pend == NULL)
2666 		pend = pstart + STRLEN(pstart);	// Last button name.
2667 	    textWidth = GetTextWidth(hdc, pstart, (int)(pend - pstart));
2668 	    if (textWidth < minButtonWidth)
2669 		textWidth = minButtonWidth;
2670 	    textWidth += dlgPaddingX;	    /* Padding within button */
2671 	    buttonWidths[i] = textWidth;
2672 	    buttonPositions[i++] = horizWidth;
2673 	    horizWidth += textWidth + dlgPaddingX; /* Pad between buttons */
2674 	    pstart = pend + 1;
2675 	} while (*pend != NUL);
2676 
2677 	if (horizWidth > maxDialogWidth)
2678 	    vertical = TRUE;	// Too wide to fit on the screen.
2679 	else if (horizWidth > dlgwidth)
2680 	    dlgwidth = horizWidth;
2681     }
2682 
2683     if (vertical)
2684     {
2685 	// Stack buttons vertically.
2686 	pstart = tbuffer;
2687 	do
2688 	{
2689 	    pend = vim_strchr(pstart, DLG_BUTTON_SEP);
2690 	    if (pend == NULL)
2691 		pend = pstart + STRLEN(pstart);	// Last button name.
2692 	    textWidth = GetTextWidth(hdc, pstart, (int)(pend - pstart));
2693 	    textWidth += dlgPaddingX;		/* Padding within button */
2694 	    textWidth += DLG_VERT_PADDING_X * 2; /* Padding around button */
2695 	    if (textWidth > dlgwidth)
2696 		dlgwidth = textWidth;
2697 	    pstart = pend + 1;
2698 	} while (*pend != NUL);
2699     }
2700 
2701     if (dlgwidth < DLG_MIN_WIDTH)
2702 	dlgwidth = DLG_MIN_WIDTH;	/* Don't allow a really thin dialog!*/
2703 
2704     /* start to fill in the dlgtemplate information.  addressing by WORDs */
2705     if (s_usenewlook)
2706 	lStyle = DS_MODALFRAME | WS_CAPTION |DS_3DLOOK| WS_VISIBLE |DS_SETFONT;
2707     else
2708 	lStyle = DS_MODALFRAME | WS_CAPTION |DS_3DLOOK| WS_VISIBLE;
2709 
2710     add_long(lStyle);
2711     add_long(0);	// (lExtendedStyle)
2712     pnumitems = p;	/*save where the number of items must be stored*/
2713     add_word(0);	// NumberOfItems(will change later)
2714     add_word(10);	// x
2715     add_word(10);	// y
2716     add_word(PixelToDialogX(dlgwidth));	// cx
2717 
2718     // Dialog height.
2719     if (vertical)
2720 	dlgheight = msgheight + 2 * dlgPaddingY +
2721 			      DLG_VERT_PADDING_Y + 2 * fontHeight * numButtons;
2722     else
2723 	dlgheight = msgheight + 3 * dlgPaddingY + 2 * fontHeight;
2724 
2725     // Dialog needs to be taller if contains an edit box.
2726     editboxheight = fontHeight + dlgPaddingY + 4 * DLG_VERT_PADDING_Y;
2727     if (textfield != NULL)
2728 	dlgheight += editboxheight;
2729 
2730     add_word(PixelToDialogY(dlgheight));
2731 
2732     add_word(0);	// Menu
2733     add_word(0);	// Class
2734 
2735     /* copy the title of the dialog */
2736     nchar = nCopyAnsiToWideChar(p, (title ?
2737 				    (LPSTR)title :
2738 				    (LPSTR)("Vim "VIM_VERSION_MEDIUM)));
2739     p += nchar;
2740 
2741     if (s_usenewlook)
2742     {
2743 	/* do the font, since DS_3DLOOK doesn't work properly */
2744 #ifdef USE_SYSMENU_FONT
2745 	if (use_lfSysmenu)
2746 	{
2747 	    /* point size */
2748 	    *p++ = -MulDiv(lfSysmenu.lfHeight, 72,
2749 		    GetDeviceCaps(hdc, LOGPIXELSY));
2750 	    nchar = nCopyAnsiToWideChar(p, TEXT(lfSysmenu.lfFaceName));
2751 	}
2752 	else
2753 #endif
2754 	{
2755 	    *p++ = DLG_FONT_POINT_SIZE;		// point size
2756 	    nchar = nCopyAnsiToWideChar(p, TEXT(DLG_FONT_NAME));
2757 	}
2758 	p += nchar;
2759     }
2760 
2761     buttonYpos = msgheight + 2 * dlgPaddingY;
2762 
2763     if (textfield != NULL)
2764 	buttonYpos += editboxheight;
2765 
2766     pstart = tbuffer;
2767     if (!vertical)
2768 	horizWidth = (dlgwidth - horizWidth) / 2;	/* Now it's X offset */
2769     for (i = 0; i < numButtons; i++)
2770     {
2771 	/* get end of this button. */
2772 	for (	pend = pstart;
2773 		*pend && (*pend != DLG_BUTTON_SEP);
2774 		pend++)
2775 	    ;
2776 
2777 	if (*pend)
2778 	    *pend = '\0';
2779 
2780 	/*
2781 	 * old NOTE:
2782 	 * setting the BS_DEFPUSHBUTTON style doesn't work because Windows sets
2783 	 * the focus to the first tab-able button and in so doing makes that
2784 	 * the default!! Grrr.  Workaround: Make the default button the only
2785 	 * one with WS_TABSTOP style. Means user can't tab between buttons, but
2786 	 * he/she can use arrow keys.
2787 	 *
2788 	 * new NOTE: BS_DEFPUSHBUTTON is required to be able to select the
2789 	 * right buttun when hitting <Enter>.  E.g., for the ":confirm quit"
2790 	 * dialog.  Also needed for when the textfield is the default control.
2791 	 * It appears to work now (perhaps not on Win95?).
2792 	 */
2793 	if (vertical)
2794 	{
2795 	    p = add_dialog_element(p,
2796 		    (i == dfltbutton
2797 			    ? BS_DEFPUSHBUTTON : BS_PUSHBUTTON) | WS_TABSTOP,
2798 		    PixelToDialogX(DLG_VERT_PADDING_X),
2799 		    PixelToDialogY(buttonYpos /* TBK */
2800 				   + 2 * fontHeight * i),
2801 		    PixelToDialogX(dlgwidth - 2 * DLG_VERT_PADDING_X),
2802 		    (WORD)(PixelToDialogY(2 * fontHeight) - 1),
2803 		    (WORD)(IDCANCEL + 1 + i), (WORD)0x0080, pstart);
2804 	}
2805 	else
2806 	{
2807 	    p = add_dialog_element(p,
2808 		    (i == dfltbutton
2809 			    ? BS_DEFPUSHBUTTON : BS_PUSHBUTTON) | WS_TABSTOP,
2810 		    PixelToDialogX(horizWidth + buttonPositions[i]),
2811 		    PixelToDialogY(buttonYpos), /* TBK */
2812 		    PixelToDialogX(buttonWidths[i]),
2813 		    (WORD)(PixelToDialogY(2 * fontHeight) - 1),
2814 		    (WORD)(IDCANCEL + 1 + i), (WORD)0x0080, pstart);
2815 	}
2816 	pstart = pend + 1;	/*next button*/
2817     }
2818     *pnumitems += numButtons;
2819 
2820     /* Vim icon */
2821     p = add_dialog_element(p, SS_ICON,
2822 	    PixelToDialogX(dlgPaddingX),
2823 	    PixelToDialogY(dlgPaddingY),
2824 	    PixelToDialogX(DLG_ICON_WIDTH),
2825 	    PixelToDialogY(DLG_ICON_HEIGHT),
2826 	    DLG_NONBUTTON_CONTROL + 0, (WORD)0x0082,
2827 	    dlg_icons[type]);
2828 
2829     /* Dialog message */
2830     p = add_dialog_element(p, SS_LEFT,
2831 	    PixelToDialogX(2 * dlgPaddingX + DLG_ICON_WIDTH),
2832 	    PixelToDialogY(dlgPaddingY),
2833 	    (WORD)(PixelToDialogX(messageWidth) + 1),
2834 	    PixelToDialogY(msgheight),
2835 	    DLG_NONBUTTON_CONTROL + 1, (WORD)0x0082, message);
2836 
2837     /* Edit box */
2838     if (textfield != NULL)
2839     {
2840 	p = add_dialog_element(p, ES_LEFT|ES_AUTOHSCROLL|WS_TABSTOP|WS_BORDER,
2841 		PixelToDialogX(2 * dlgPaddingX),
2842 		PixelToDialogY(2 * dlgPaddingY + msgheight),
2843 		PixelToDialogX(dlgwidth - 4 * dlgPaddingX),
2844 		PixelToDialogY(fontHeight + dlgPaddingY),
2845 		DLG_NONBUTTON_CONTROL + 2, (WORD)0x0081, textfield);
2846 	*pnumitems += 1;
2847     }
2848 
2849     *pnumitems += 2;
2850 
2851     SelectFont(hdc, oldFont);
2852     DeleteObject(font);
2853     ReleaseDC(hwnd, hdc);
2854 
2855     /* Let the dialog_callback() function know which button to make default
2856      * If we have an edit box, make that the default. We also need to tell
2857      * dialog_callback() if this dialog contains an edit box or not. We do
2858      * this by setting s_textfield if it does.
2859      */
2860     if (textfield != NULL)
2861     {
2862 	dialog_default_button = DLG_NONBUTTON_CONTROL + 2;
2863 	s_textfield = textfield;
2864     }
2865     else
2866     {
2867 	dialog_default_button = IDCANCEL + 1 + dfltbutton;
2868 	s_textfield = NULL;
2869     }
2870 
2871     /* show the dialog box modally and get a return value */
2872     nchar = (int)DialogBoxIndirect(
2873 	    s_hinst,
2874 	    (LPDLGTEMPLATE)pdlgtemplate,
2875 	    s_hwnd,
2876 	    (DLGPROC)dialog_callback);
2877 
2878     LocalFree(LocalHandle(pdlgtemplate));
2879     vim_free(tbuffer);
2880     vim_free(buttonWidths);
2881     vim_free(buttonPositions);
2882 
2883     /* Focus back to our window (for when MDI is used). */
2884     (void)SetFocus(s_hwnd);
2885 
2886     return nchar;
2887 }
2888 
2889 #endif /* FEAT_GUI_DIALOG */
2890 /*
2891  * Put a simple element (basic class) onto a dialog template in memory.
2892  * return a pointer to where the next item should be added.
2893  *
2894  * parameters:
2895  *  lStyle = additional style flags
2896  *		(be careful, NT3.51 & Win32s will ignore the new ones)
2897  *  x,y = x & y positions IN DIALOG UNITS
2898  *  w,h = width and height IN DIALOG UNITS
2899  *  Id  = ID used in messages
2900  *  clss  = class ID, e.g 0x0080 for a button, 0x0082 for a static
2901  *  caption = usually text or resource name
2902  *
2903  *  TODO: use the length information noted here to enable the dialog creation
2904  *  routines to work out more exactly how much memory they need to alloc.
2905  */
2906     static PWORD
2907 add_dialog_element(
2908     PWORD p,
2909     DWORD lStyle,
2910     WORD x,
2911     WORD y,
2912     WORD w,
2913     WORD h,
2914     WORD Id,
2915     WORD clss,
2916     const char *caption)
2917 {
2918     int nchar;
2919 
2920     p = lpwAlign(p);	/* Align to dword boundary*/
2921     lStyle = lStyle | WS_VISIBLE | WS_CHILD;
2922     *p++ = LOWORD(lStyle);
2923     *p++ = HIWORD(lStyle);
2924     *p++ = 0;		// LOWORD (lExtendedStyle)
2925     *p++ = 0;		// HIWORD (lExtendedStyle)
2926     *p++ = x;
2927     *p++ = y;
2928     *p++ = w;
2929     *p++ = h;
2930     *p++ = Id;		//9 or 10 words in all
2931 
2932     *p++ = (WORD)0xffff;
2933     *p++ = clss;			//2 more here
2934 
2935     nchar = nCopyAnsiToWideChar(p, (LPSTR)caption); //strlen(caption)+1
2936     p += nchar;
2937 
2938     *p++ = 0;  // advance pointer over nExtraStuff WORD   - 2 more
2939 
2940     return p;	//total = 15+ (strlen(caption)) words
2941 		//	   = 30 + 2(strlen(caption) bytes reqd
2942 }
2943 
2944 
2945 /*
2946  * Helper routine.  Take an input pointer, return closest pointer that is
2947  * aligned on a DWORD (4 byte) boundary.  Taken from the Win32SDK samples.
2948  */
2949     static LPWORD
2950 lpwAlign(
2951     LPWORD lpIn)
2952 {
2953     ULONG ul;
2954 
2955     ul = (ULONG)lpIn;
2956     ul += 3;
2957     ul >>= 2;
2958     ul <<= 2;
2959     return (LPWORD)ul;
2960 }
2961 
2962 /*
2963  * Helper routine.  Takes second parameter as Ansi string, copies it to first
2964  * parameter as wide character (16-bits / char) string, and returns integer
2965  * number of wide characters (words) in string (including the trailing wide
2966  * char NULL).  Partly taken from the Win32SDK samples.
2967  */
2968     static int
2969 nCopyAnsiToWideChar(
2970     LPWORD lpWCStr,
2971     LPSTR lpAnsiIn)
2972 {
2973     int		nChar = 0;
2974 #ifdef FEAT_MBYTE
2975     int		len = lstrlen(lpAnsiIn) + 1;	/* include NUL character */
2976     int		i;
2977     WCHAR	*wn;
2978 
2979     if (enc_codepage == 0 && (int)GetACP() != enc_codepage)
2980     {
2981 	/* Not a codepage, use our own conversion function. */
2982 	wn = enc_to_ucs2(lpAnsiIn, NULL);
2983 	if (wn != NULL)
2984 	{
2985 	    wcscpy(lpWCStr, wn);
2986 	    nChar = wcslen(wn) + 1;
2987 	    vim_free(wn);
2988 	}
2989     }
2990     if (nChar == 0)
2991 	/* Use Win32 conversion function. */
2992 	nChar = MultiByteToWideChar(
2993 		enc_codepage > 0 ? enc_codepage : CP_ACP,
2994 		MB_PRECOMPOSED,
2995 		lpAnsiIn, len,
2996 		lpWCStr, len);
2997     for (i = 0; i < nChar; ++i)
2998 	if (lpWCStr[i] == (WORD)'\t')	/* replace tabs with spaces */
2999 	    lpWCStr[i] = (WORD)' ';
3000 #else
3001     do
3002     {
3003 	if (*lpAnsiIn == '\t')
3004 	    *lpWCStr++ = (WORD)' ';
3005 	else
3006 	    *lpWCStr++ = (WORD)*lpAnsiIn;
3007 	nChar++;
3008     } while (*lpAnsiIn++);
3009 #endif
3010 
3011     return nChar;
3012 }
3013 
3014 
3015 #ifdef FEAT_TEAROFF
3016 /*
3017  * The callback function for all the modeless dialogs that make up the
3018  * "tearoff menus" Very simple - forward button presses (to fool Vim into
3019  * thinking its menus have been clicked), and go away when closed.
3020  */
3021     static LRESULT CALLBACK
3022 tearoff_callback(
3023     HWND hwnd,
3024     UINT message,
3025     WPARAM wParam,
3026     LPARAM lParam)
3027 {
3028     if (message == WM_INITDIALOG)
3029 	return (TRUE);
3030 
3031     /* May show the mouse pointer again. */
3032     HandleMouseHide(message, lParam);
3033 
3034     if (message == WM_COMMAND)
3035     {
3036 	if ((WORD)(LOWORD(wParam)) & 0x8000)
3037 	{
3038 	    POINT   mp;
3039 	    RECT    rect;
3040 
3041 	    if (GetCursorPos(&mp) && GetWindowRect(hwnd, &rect))
3042 	    {
3043 		(void)TrackPopupMenu(
3044 			 (HMENU)(LOWORD(wParam) ^ 0x8000),
3045 			 TPM_LEFTALIGN | TPM_LEFTBUTTON,
3046 			 (int)rect.right - 8,
3047 			 (int)mp.y,
3048 			 (int)0,	    /*reserved param*/
3049 			 s_hwnd,
3050 			 NULL);
3051 		/*
3052 		 * NOTE: The pop-up menu can eat the mouse up event.
3053 		 * We deal with this in normal.c.
3054 		 */
3055 	    }
3056 	}
3057 	else
3058 	    /* Pass on messages to the main Vim window */
3059 	    PostMessage(s_hwnd, WM_COMMAND, LOWORD(wParam), 0);
3060 	/*
3061 	 * Give main window the focus back: this is so after
3062 	 * choosing a tearoff button you can start typing again
3063 	 * straight away.
3064 	 */
3065 	(void)SetFocus(s_hwnd);
3066 	return TRUE;
3067     }
3068     if ((message == WM_SYSCOMMAND) && (wParam == SC_CLOSE))
3069     {
3070 	DestroyWindow(hwnd);
3071 	return TRUE;
3072     }
3073 
3074     /* When moved around, give main window the focus back. */
3075     if (message == WM_EXITSIZEMOVE)
3076 	(void)SetActiveWindow(s_hwnd);
3077 
3078     return FALSE;
3079 }
3080 #endif
3081 
3082 
3083 /*
3084  * Decide whether to use the "new look" (small, non-bold font) or the "old
3085  * look" (big, clanky font) for dialogs, and work out a few values for use
3086  * later accordingly.
3087  */
3088     static void
3089 get_dialog_font_metrics(void)
3090 {
3091     HDC		    hdc;
3092     HFONT	    hfontTools = 0;
3093     DWORD	    dlgFontSize;
3094     SIZE	    size;
3095 #ifdef USE_SYSMENU_FONT
3096     LOGFONT	    lfSysmenu;
3097 #endif
3098 
3099     s_usenewlook = FALSE;
3100 
3101     /*
3102      * For NT3.51 and Win32s, we stick with the old look
3103      * because it matches everything else.
3104      */
3105     if (!is_winnt_3())
3106     {
3107 #ifdef USE_SYSMENU_FONT
3108 	if (gui_w32_get_menu_font(&lfSysmenu) == OK)
3109 	    hfontTools = CreateFontIndirect(&lfSysmenu);
3110 	else
3111 #endif
3112 	hfontTools = CreateFont(-DLG_FONT_POINT_SIZE, 0, 0, 0, 0, 0, 0, 0,
3113 				0, 0, 0, 0, VARIABLE_PITCH , DLG_FONT_NAME);
3114 
3115 	if (hfontTools)
3116 	{
3117 	    hdc = GetDC(s_hwnd);
3118 	    SelectObject(hdc, hfontTools);
3119 	    /*
3120 	     * GetTextMetrics() doesn't return the right value in
3121 	     * tmAveCharWidth, so we have to figure out the dialog base units
3122 	     * ourselves.
3123 	     */
3124 	    GetTextExtentPoint(hdc,
3125 		    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
3126 		    52, &size);
3127 	    ReleaseDC(s_hwnd, hdc);
3128 
3129 	    s_dlgfntwidth = (WORD)((size.cx / 26 + 1) / 2);
3130 	    s_dlgfntheight = (WORD)size.cy;
3131 	    s_usenewlook = TRUE;
3132 	}
3133     }
3134 
3135     if (!s_usenewlook)
3136     {
3137 	dlgFontSize = GetDialogBaseUnits();	/* fall back to big old system*/
3138 	s_dlgfntwidth = LOWORD(dlgFontSize);
3139 	s_dlgfntheight = HIWORD(dlgFontSize);
3140     }
3141 }
3142 
3143 #if defined(FEAT_MENU) && defined(FEAT_TEAROFF)
3144 /*
3145  * Create a pseudo-"tearoff menu" based on the child
3146  * items of a given menu pointer.
3147  */
3148     static void
3149 gui_mch_tearoff(
3150     char_u	*title,
3151     vimmenu_T	*menu,
3152     int		initX,
3153     int		initY)
3154 {
3155     WORD	*p, *pdlgtemplate, *pnumitems, *ptrueheight;
3156     int		template_len;
3157     int		nchar, textWidth, submenuWidth;
3158     DWORD	lStyle;
3159     DWORD	lExtendedStyle;
3160     WORD	dlgwidth;
3161     WORD	menuID;
3162     vimmenu_T	*pmenu;
3163     vimmenu_T	*the_menu = menu;
3164     HWND	hwnd;
3165     HDC		hdc;
3166     HFONT	font, oldFont;
3167     int		col, spaceWidth, len;
3168     int		columnWidths[2];
3169     char_u	*label, *text;
3170     int		acLen = 0;
3171     int		nameLen;
3172     int		padding0, padding1, padding2 = 0;
3173     int		sepPadding=0;
3174 #ifdef USE_SYSMENU_FONT
3175     LOGFONT	lfSysmenu;
3176     int		use_lfSysmenu = FALSE;
3177 #endif
3178 
3179     /*
3180      * If this menu is already torn off, move it to the mouse position.
3181      */
3182     if (IsWindow(menu->tearoff_handle))
3183     {
3184 	POINT mp;
3185 	if (GetCursorPos((LPPOINT)&mp))
3186 	{
3187 	    SetWindowPos(menu->tearoff_handle, NULL, mp.x, mp.y, 0, 0,
3188 		    SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
3189 	}
3190 	return;
3191     }
3192 
3193     /*
3194      * Create a new tearoff.
3195      */
3196     if (*title == MNU_HIDDEN_CHAR)
3197 	title++;
3198 
3199     /* Allocate memory to store the dialog template.  It's made bigger when
3200      * needed. */
3201     template_len = DLG_ALLOC_SIZE;
3202     pdlgtemplate = p = (WORD *)LocalAlloc(LPTR, template_len);
3203     if (p == NULL)
3204 	return;
3205 
3206     hwnd = GetDesktopWindow();
3207     hdc = GetWindowDC(hwnd);
3208 #ifdef USE_SYSMENU_FONT
3209     if (gui_w32_get_menu_font(&lfSysmenu) == OK)
3210     {
3211 	font = CreateFontIndirect(&lfSysmenu);
3212 	use_lfSysmenu = TRUE;
3213     }
3214     else
3215 #endif
3216     font = CreateFont(-DLG_FONT_POINT_SIZE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3217 		      VARIABLE_PITCH , DLG_FONT_NAME);
3218     if (s_usenewlook)
3219 	oldFont = SelectFont(hdc, font);
3220     else
3221 	oldFont = SelectFont(hdc, GetStockObject(SYSTEM_FONT));
3222 
3223     /* Calculate width of a single space.  Used for padding columns to the
3224      * right width. */
3225     spaceWidth = GetTextWidth(hdc, " ", 1);
3226 
3227     /* Figure out max width of the text column, the accelerator column and the
3228      * optional submenu column. */
3229     submenuWidth = 0;
3230     for (col = 0; col < 2; col++)
3231     {
3232 	columnWidths[col] = 0;
3233 	for (pmenu = menu->children; pmenu != NULL; pmenu = pmenu->next)
3234 	{
3235 	    /* Use "dname" here to compute the width of the visible text. */
3236 	    text = (col == 0) ? pmenu->dname : pmenu->actext;
3237 	    if (text != NULL && *text != NUL)
3238 	    {
3239 		textWidth = GetTextWidthEnc(hdc, text, (int)STRLEN(text));
3240 		if (textWidth > columnWidths[col])
3241 		    columnWidths[col] = textWidth;
3242 	    }
3243 	    if (pmenu->children != NULL)
3244 		submenuWidth = TEAROFF_COLUMN_PADDING * spaceWidth;
3245 	}
3246     }
3247     if (columnWidths[1] == 0)
3248     {
3249 	/* no accelerators */
3250 	if (submenuWidth != 0)
3251 	    columnWidths[0] += submenuWidth;
3252 	else
3253 	    columnWidths[0] += spaceWidth;
3254     }
3255     else
3256     {
3257 	/* there is an accelerator column */
3258 	columnWidths[0] += TEAROFF_COLUMN_PADDING * spaceWidth;
3259 	columnWidths[1] += submenuWidth;
3260     }
3261 
3262     /*
3263      * Now find the total width of our 'menu'.
3264      */
3265     textWidth = columnWidths[0] + columnWidths[1];
3266     if (submenuWidth != 0)
3267     {
3268 	submenuWidth = GetTextWidth(hdc, TEAROFF_SUBMENU_LABEL,
3269 					  (int)STRLEN(TEAROFF_SUBMENU_LABEL));
3270 	textWidth += submenuWidth;
3271     }
3272     dlgwidth = GetTextWidthEnc(hdc, title, (int)STRLEN(title));
3273     if (textWidth > dlgwidth)
3274 	dlgwidth = textWidth;
3275     dlgwidth += 2 * TEAROFF_PADDING_X + TEAROFF_BUTTON_PAD_X;
3276 
3277     /* W95 can't do thin dialogs, they look v. weird! */
3278     if (mch_windows95() && dlgwidth < TEAROFF_MIN_WIDTH)
3279 	dlgwidth = TEAROFF_MIN_WIDTH;
3280 
3281     /* start to fill in the dlgtemplate information.  addressing by WORDs */
3282     if (s_usenewlook)
3283 	lStyle = DS_MODALFRAME | WS_CAPTION| WS_SYSMENU |DS_SETFONT| WS_VISIBLE;
3284     else
3285 	lStyle = DS_MODALFRAME | WS_CAPTION| WS_SYSMENU | WS_VISIBLE;
3286 
3287     lExtendedStyle = WS_EX_TOOLWINDOW|WS_EX_STATICEDGE;
3288     *p++ = LOWORD(lStyle);
3289     *p++ = HIWORD(lStyle);
3290     *p++ = LOWORD(lExtendedStyle);
3291     *p++ = HIWORD(lExtendedStyle);
3292     pnumitems = p;	/* save where the number of items must be stored */
3293     *p++ = 0;		// NumberOfItems(will change later)
3294     if (initX == 0xffffL)
3295 	*p++ = PixelToDialogX(gui_mch_get_mouse_x()); // x
3296     else
3297 	*p++ = PixelToDialogX(initX); // x
3298     if (initY == 0xffffL)
3299 	*p++ = PixelToDialogY(gui_mch_get_mouse_y()); // y
3300     else
3301 	*p++ = PixelToDialogY(initY); // y
3302     *p++ = PixelToDialogX(dlgwidth);    // cx
3303     ptrueheight = p;
3304     *p++ = 0;		// dialog height: changed later anyway
3305     *p++ = 0;		// Menu
3306     *p++ = 0;		// Class
3307 
3308     /* copy the title of the dialog */
3309     nchar = nCopyAnsiToWideChar(p, ((*title)
3310 				    ? (LPSTR)title
3311 				    : (LPSTR)("Vim "VIM_VERSION_MEDIUM)));
3312     p += nchar;
3313 
3314     if (s_usenewlook)
3315     {
3316 	/* do the font, since DS_3DLOOK doesn't work properly */
3317 #ifdef USE_SYSMENU_FONT
3318 	if (use_lfSysmenu)
3319 	{
3320 	    /* point size */
3321 	    *p++ = -MulDiv(lfSysmenu.lfHeight, 72,
3322 		    GetDeviceCaps(hdc, LOGPIXELSY));
3323 	    nchar = nCopyAnsiToWideChar(p, TEXT(lfSysmenu.lfFaceName));
3324 	}
3325 	else
3326 #endif
3327 	{
3328 	    *p++ = DLG_FONT_POINT_SIZE;		// point size
3329 	    nchar = nCopyAnsiToWideChar (p, TEXT(DLG_FONT_NAME));
3330 	}
3331 	p += nchar;
3332     }
3333 
3334     /*
3335      * Loop over all the items in the menu.
3336      * But skip over the tearbar.
3337      */
3338     if (STRCMP(menu->children->name, TEAR_STRING) == 0)
3339 	menu = menu->children->next;
3340     else
3341 	menu = menu->children;
3342     for ( ; menu != NULL; menu = menu->next)
3343     {
3344 	if (menu->modes == 0)	/* this menu has just been deleted */
3345 	    continue;
3346 	if (menu_is_separator(menu->dname))
3347 	{
3348 	    sepPadding += 3;
3349 	    continue;
3350 	}
3351 
3352 	/* Check if there still is plenty of room in the template.  Make it
3353 	 * larger when needed. */
3354 	if (((char *)p - (char *)pdlgtemplate) + 1000 > template_len)
3355 	{
3356 	    WORD    *newp;
3357 
3358 	    newp = (WORD *)LocalAlloc(LPTR, template_len + 4096);
3359 	    if (newp != NULL)
3360 	    {
3361 		template_len += 4096;
3362 		mch_memmove(newp, pdlgtemplate,
3363 					    (char *)p - (char *)pdlgtemplate);
3364 		p = newp + (p - pdlgtemplate);
3365 		pnumitems = newp + (pnumitems - pdlgtemplate);
3366 		ptrueheight = newp + (ptrueheight - pdlgtemplate);
3367 		LocalFree(LocalHandle(pdlgtemplate));
3368 		pdlgtemplate = newp;
3369 	    }
3370 	}
3371 
3372 	/* Figure out minimal length of this menu label.  Use "name" for the
3373 	 * actual text, "dname" for estimating the displayed size.  "name"
3374 	 * has "&a" for mnemonic and includes the accelerator. */
3375 	len = nameLen = (int)STRLEN(menu->name);
3376 	padding0 = (columnWidths[0] - GetTextWidthEnc(hdc, menu->dname,
3377 				      (int)STRLEN(menu->dname))) / spaceWidth;
3378 	len += padding0;
3379 
3380 	if (menu->actext != NULL)
3381 	{
3382 	    acLen = (int)STRLEN(menu->actext);
3383 	    len += acLen;
3384 	    textWidth = GetTextWidthEnc(hdc, menu->actext, acLen);
3385 	}
3386 	else
3387 	    textWidth = 0;
3388 	padding1 = (columnWidths[1] - textWidth) / spaceWidth;
3389 	len += padding1;
3390 
3391 	if (menu->children == NULL)
3392 	{
3393 	    padding2 = submenuWidth / spaceWidth;
3394 	    len += padding2;
3395 	    menuID = (WORD)(menu->id);
3396 	}
3397 	else
3398 	{
3399 	    len += (int)STRLEN(TEAROFF_SUBMENU_LABEL);
3400 	    menuID = (WORD)((DWORD)(menu->submenu_id) | (DWORD)0x8000);
3401 	}
3402 
3403 	/* Allocate menu label and fill it in */
3404 	text = label = alloc((unsigned)len + 1);
3405 	if (label == NULL)
3406 	    break;
3407 
3408 	STRNCPY(text, menu->name, nameLen);
3409 	text[nameLen] = NUL;
3410 	text = vim_strchr(text, TAB);	    /* stop at TAB before actext */
3411 	if (text == NULL)
3412 	    text = label + nameLen;	    /* no actext, use whole name */
3413 	while (padding0-- > 0)
3414 	    *text++ = ' ';
3415 	if (menu->actext != NULL)
3416 	{
3417 	    STRNCPY(text, menu->actext, acLen);
3418 	    text += acLen;
3419 	}
3420 	while (padding1-- > 0)
3421 	    *text++ = ' ';
3422 	if (menu->children != NULL)
3423 	{
3424 	    STRCPY(text, TEAROFF_SUBMENU_LABEL);
3425 	    text += STRLEN(TEAROFF_SUBMENU_LABEL);
3426 	}
3427 	else
3428 	{
3429 	    while (padding2-- > 0)
3430 		*text++ = ' ';
3431 	}
3432 	*text = NUL;
3433 
3434 	/*
3435 	 * BS_LEFT will just be ignored on Win32s/NT3.5x - on
3436 	 * W95/NT4 it makes the tear-off look more like a menu.
3437 	 */
3438 	p = add_dialog_element(p,
3439 		BS_PUSHBUTTON|BS_LEFT,
3440 		(WORD)PixelToDialogX(TEAROFF_PADDING_X),
3441 		(WORD)(sepPadding + 1 + 13 * (*pnumitems)),
3442 		(WORD)PixelToDialogX(dlgwidth - 2 * TEAROFF_PADDING_X),
3443 		(WORD)12,
3444 		menuID, (WORD)0x0080, label);
3445 	vim_free(label);
3446 	(*pnumitems)++;
3447     }
3448 
3449     *ptrueheight = (WORD)(sepPadding + 1 + 13 * (*pnumitems));
3450 
3451 
3452     /* show modelessly */
3453     the_menu->tearoff_handle = CreateDialogIndirect(
3454 	    s_hinst,
3455 	    (LPDLGTEMPLATE)pdlgtemplate,
3456 	    s_hwnd,
3457 	    (DLGPROC)tearoff_callback);
3458 
3459     LocalFree(LocalHandle(pdlgtemplate));
3460     SelectFont(hdc, oldFont);
3461     DeleteObject(font);
3462     ReleaseDC(hwnd, hdc);
3463 
3464     /*
3465      * Reassert ourselves as the active window.  This is so that after creating
3466      * a tearoff, the user doesn't have to click with the mouse just to start
3467      * typing again!
3468      */
3469     (void)SetActiveWindow(s_hwnd);
3470 
3471     /* make sure the right buttons are enabled */
3472     force_menu_update = TRUE;
3473 }
3474 #endif
3475 
3476 #if defined(FEAT_TOOLBAR) || defined(PROTO)
3477 #include "gui_w32_rc.h"
3478 
3479 /* This not defined in older SDKs */
3480 # ifndef TBSTYLE_FLAT
3481 #  define TBSTYLE_FLAT		0x0800
3482 # endif
3483 
3484 /*
3485  * Create the toolbar, initially unpopulated.
3486  *  (just like the menu, there are no defaults, it's all
3487  *  set up through menu.vim)
3488  */
3489     static void
3490 initialise_toolbar(void)
3491 {
3492     InitCommonControls();
3493     s_toolbarhwnd = CreateToolbarEx(
3494 		    s_hwnd,
3495 		    WS_CHILD | TBSTYLE_TOOLTIPS | TBSTYLE_FLAT,
3496 		    4000,		//any old big number
3497 		    31,			//number of images in inital bitmap
3498 		    s_hinst,
3499 		    IDR_TOOLBAR1,	// id of initial bitmap
3500 		    NULL,
3501 		    0,			// initial number of buttons
3502 		    TOOLBAR_BUTTON_WIDTH, //api guide is wrong!
3503 		    TOOLBAR_BUTTON_HEIGHT,
3504 		    TOOLBAR_BUTTON_WIDTH,
3505 		    TOOLBAR_BUTTON_HEIGHT,
3506 		    sizeof(TBBUTTON)
3507 		    );
3508 
3509     gui_mch_show_toolbar(vim_strchr(p_go, GO_TOOLBAR) != NULL);
3510 }
3511 
3512     static int
3513 get_toolbar_bitmap(vimmenu_T *menu)
3514 {
3515     int i = -1;
3516 
3517     /*
3518      * Check user bitmaps first, unless builtin is specified.
3519      */
3520     if (!is_winnt_3() && !menu->icon_builtin)
3521     {
3522 	char_u fname[MAXPATHL];
3523 	HANDLE hbitmap = NULL;
3524 
3525 	if (menu->iconfile != NULL)
3526 	{
3527 	    gui_find_iconfile(menu->iconfile, fname, "bmp");
3528 	    hbitmap = LoadImage(
3529 			NULL,
3530 			fname,
3531 			IMAGE_BITMAP,
3532 			TOOLBAR_BUTTON_WIDTH,
3533 			TOOLBAR_BUTTON_HEIGHT,
3534 			LR_LOADFROMFILE |
3535 			LR_LOADMAP3DCOLORS
3536 			);
3537 	}
3538 
3539 	/*
3540 	 * If the LoadImage call failed, or the "icon=" file
3541 	 * didn't exist or wasn't specified, try the menu name
3542 	 */
3543 	if (hbitmap == NULL
3544 		&& (gui_find_bitmap(menu->name, fname, "bmp") == OK))
3545 	    hbitmap = LoadImage(
3546 		    NULL,
3547 		    fname,
3548 		    IMAGE_BITMAP,
3549 		    TOOLBAR_BUTTON_WIDTH,
3550 		    TOOLBAR_BUTTON_HEIGHT,
3551 		    LR_LOADFROMFILE |
3552 		    LR_LOADMAP3DCOLORS
3553 		);
3554 
3555 	if (hbitmap != NULL)
3556 	{
3557 	    TBADDBITMAP tbAddBitmap;
3558 
3559 	    tbAddBitmap.hInst = NULL;
3560 	    tbAddBitmap.nID = (UINT)hbitmap;
3561 
3562 	    i = (int)SendMessage(s_toolbarhwnd, TB_ADDBITMAP,
3563 			    (WPARAM)1, (LPARAM)&tbAddBitmap);
3564 	    /* i will be set to -1 if it fails */
3565 	}
3566     }
3567     if (i == -1 && menu->iconidx >= 0 && menu->iconidx < TOOLBAR_BITMAP_COUNT)
3568 	i = menu->iconidx;
3569 
3570     return i;
3571 }
3572 #endif
3573 
3574 #if defined(FEAT_OLE) || defined(FEAT_EVAL) || defined(PROTO)
3575 /*
3576  * Make the GUI window come to the foreground.
3577  */
3578     void
3579 gui_mch_set_foreground(void)
3580 {
3581     if (IsIconic(s_hwnd))
3582 	 SendMessage(s_hwnd, WM_SYSCOMMAND, SC_RESTORE, 0);
3583     SetForegroundWindow(s_hwnd);
3584 }
3585 #endif
3586 
3587 #if defined(FEAT_MBYTE_IME) && defined(DYNAMIC_IME)
3588     static void
3589 dyn_imm_load(void)
3590 {
3591     hLibImm = LoadLibrary("imm32.dll");
3592     if (hLibImm == NULL)
3593 	return;
3594 
3595     pImmGetCompositionStringA
3596 	    = (void *)GetProcAddress(hLibImm, "ImmGetCompositionStringA");
3597     pImmGetCompositionStringW
3598 	    = (void *)GetProcAddress(hLibImm, "ImmGetCompositionStringW");
3599     pImmGetContext
3600 	    = (void *)GetProcAddress(hLibImm, "ImmGetContext");
3601     pImmAssociateContext
3602 	    = (void *)GetProcAddress(hLibImm, "ImmAssociateContext");
3603     pImmReleaseContext
3604 	    = (void *)GetProcAddress(hLibImm, "ImmReleaseContext");
3605     pImmGetOpenStatus
3606 	    = (void *)GetProcAddress(hLibImm, "ImmGetOpenStatus");
3607     pImmSetOpenStatus
3608 	    = (void *)GetProcAddress(hLibImm, "ImmSetOpenStatus");
3609     pImmGetCompositionFont
3610 	    = (void *)GetProcAddress(hLibImm, "ImmGetCompositionFontA");
3611     pImmSetCompositionFont
3612 	    = (void *)GetProcAddress(hLibImm, "ImmSetCompositionFontA");
3613     pImmSetCompositionWindow
3614 	    = (void *)GetProcAddress(hLibImm, "ImmSetCompositionWindow");
3615     pImmGetConversionStatus
3616 	    = (void *)GetProcAddress(hLibImm, "ImmGetConversionStatus");
3617 
3618     if (       pImmGetCompositionStringA == NULL
3619 	    || pImmGetCompositionStringW == NULL
3620 	    || pImmGetContext == NULL
3621 	    || pImmAssociateContext == NULL
3622 	    || pImmReleaseContext == NULL
3623 	    || pImmGetOpenStatus == NULL
3624 	    || pImmSetOpenStatus == NULL
3625 	    || pImmGetCompositionFont == NULL
3626 	    || pImmSetCompositionFont == NULL
3627 	    || pImmSetCompositionWindow == NULL
3628 	    || pImmGetConversionStatus == NULL)
3629     {
3630 	FreeLibrary(hLibImm);
3631 	hLibImm = NULL;
3632 	pImmGetContext = NULL;
3633 	return;
3634     }
3635 
3636     return;
3637 }
3638 
3639 # if 0	/* not used */
3640     int
3641 dyn_imm_unload(void)
3642 {
3643     if (!hLibImm)
3644 	return FALSE;
3645     FreeLibrary(hLibImm);
3646     hLibImm = NULL;
3647     return TRUE;
3648 }
3649 # endif
3650 
3651 #endif
3652 
3653 #if defined(FEAT_SIGN_ICONS) || defined(PROTO)
3654 
3655 # ifdef FEAT_XPM_W32
3656 #  define IMAGE_XPM   100
3657 # endif
3658 
3659 typedef struct _signicon_t
3660 {
3661     HANDLE	hImage;
3662     UINT	uType;
3663 #ifdef FEAT_XPM_W32
3664     HANDLE	hShape;	/* Mask bitmap handle */
3665 #endif
3666 } signicon_t;
3667 
3668     void
3669 gui_mch_drawsign(row, col, typenr)
3670     int		row;
3671     int		col;
3672     int		typenr;
3673 {
3674     signicon_t *sign;
3675     int x, y, w, h;
3676 
3677     if (!gui.in_use || (sign = (signicon_t *)sign_get_image(typenr)) == NULL)
3678 	return;
3679 
3680     x = TEXT_X(col);
3681     y = TEXT_Y(row);
3682     w = gui.char_width * 2;
3683     h = gui.char_height;
3684     switch (sign->uType)
3685     {
3686 	case IMAGE_BITMAP:
3687 	    {
3688 		HDC hdcMem;
3689 		HBITMAP hbmpOld;
3690 
3691 		hdcMem = CreateCompatibleDC(s_hdc);
3692 		hbmpOld = (HBITMAP)SelectObject(hdcMem, sign->hImage);
3693 		BitBlt(s_hdc, x, y, w, h, hdcMem, 0, 0, SRCCOPY);
3694 		SelectObject(hdcMem, hbmpOld);
3695 		DeleteDC(hdcMem);
3696 	    }
3697 	    break;
3698 	case IMAGE_ICON:
3699 	case IMAGE_CURSOR:
3700 	    DrawIconEx(s_hdc, x, y, (HICON)sign->hImage, w, h, 0, NULL, DI_NORMAL);
3701 	    break;
3702 #ifdef FEAT_XPM_W32
3703 	case IMAGE_XPM:
3704 	    {
3705 		HDC hdcMem;
3706 		HBITMAP hbmpOld;
3707 
3708 		hdcMem = CreateCompatibleDC(s_hdc);
3709 		hbmpOld = (HBITMAP)SelectObject(hdcMem, sign->hShape);
3710 		/* Make hole */
3711 		BitBlt(s_hdc, x, y, w, h, hdcMem, 0, 0, SRCAND);
3712 
3713 		SelectObject(hdcMem, sign->hImage);
3714 		/* Paint sign */
3715 		BitBlt(s_hdc, x, y, w, h, hdcMem, 0, 0, SRCPAINT);
3716 		SelectObject(hdcMem, hbmpOld);
3717 		DeleteDC(hdcMem);
3718 	    }
3719 	    break;
3720 #endif
3721     }
3722 }
3723 
3724     static void
3725 close_signicon_image(signicon_t *sign)
3726 {
3727     if (sign)
3728 	switch (sign->uType)
3729 	{
3730 	    case IMAGE_BITMAP:
3731 		DeleteObject((HGDIOBJ)sign->hImage);
3732 		break;
3733 	    case IMAGE_CURSOR:
3734 		DestroyCursor((HCURSOR)sign->hImage);
3735 		break;
3736 	    case IMAGE_ICON:
3737 		DestroyIcon((HICON)sign->hImage);
3738 		break;
3739 #ifdef FEAT_XPM_W32
3740 	    case IMAGE_XPM:
3741 		DeleteObject((HBITMAP)sign->hImage);
3742 		DeleteObject((HBITMAP)sign->hShape);
3743 		break;
3744 #endif
3745 	}
3746 }
3747 
3748     void *
3749 gui_mch_register_sign(signfile)
3750     char_u	*signfile;
3751 {
3752     signicon_t	sign, *psign;
3753     char_u	*ext;
3754 
3755     if (is_winnt_3())
3756     {
3757 	EMSG(_(e_signdata));
3758 	return NULL;
3759     }
3760 
3761     sign.hImage = NULL;
3762     ext = signfile + STRLEN(signfile) - 4; /* get extention */
3763     if (ext > signfile)
3764     {
3765 	int do_load = 1;
3766 
3767 	if (!STRICMP(ext, ".bmp"))
3768 	    sign.uType =  IMAGE_BITMAP;
3769 	else if (!STRICMP(ext, ".ico"))
3770 	    sign.uType =  IMAGE_ICON;
3771 	else if (!STRICMP(ext, ".cur") || !STRICMP(ext, ".ani"))
3772 	    sign.uType =  IMAGE_CURSOR;
3773 	else
3774 	    do_load = 0;
3775 
3776 	if (do_load)
3777 	    sign.hImage = (HANDLE)LoadImage(NULL, signfile, sign.uType,
3778 		    gui.char_width * 2, gui.char_height,
3779 		    LR_LOADFROMFILE | LR_CREATEDIBSECTION);
3780 #ifdef FEAT_XPM_W32
3781 	if (!STRICMP(ext, ".xpm"))
3782 	{
3783 	    sign.uType = IMAGE_XPM;
3784 	    LoadXpmImage(signfile, (HBITMAP *)&sign.hImage, (HBITMAP *)&sign.hShape);
3785 	}
3786 #endif
3787     }
3788 
3789     psign = NULL;
3790     if (sign.hImage && (psign = (signicon_t *)alloc(sizeof(signicon_t)))
3791 								      != NULL)
3792 	*psign = sign;
3793 
3794     if (!psign)
3795     {
3796 	if (sign.hImage)
3797 	    close_signicon_image(&sign);
3798 	EMSG(_(e_signdata));
3799     }
3800     return (void *)psign;
3801 
3802 }
3803 
3804     void
3805 gui_mch_destroy_sign(sign)
3806     void *sign;
3807 {
3808     if (sign)
3809     {
3810 	close_signicon_image((signicon_t *)sign);
3811 	vim_free(sign);
3812     }
3813 }
3814 
3815 #if defined(FEAT_BEVAL) || defined(PROTO)
3816 
3817 /* BALLOON-EVAL IMPLEMENTATION FOR WINDOWS.
3818  *  Added by Sergey Khorev
3819  *
3820  * The only reused thing is gui_beval.h and gui_mch_get_beval_info()
3821  * from gui_beval.c (note it uses x and y of the BalloonEval struct
3822  * to get current mouse position).
3823  *
3824  * Trying to use as more Windows services as possible, and as less
3825  * IE version as possible :)).
3826  *
3827  * 1) Don't create ToolTip in gui_mch_create_beval_area, only initialize
3828  * BalloonEval struct.
3829  * 2) Enable/Disable simply create/kill BalloonEval Timer
3830  * 3) When there was enough inactivity, timer procedure posts
3831  * async request to debugger
3832  * 4) gui_mch_post_balloon (invoked from netbeans.c) creates tooltip control
3833  * and performs some actions to show it ASAP
3834  * 5) WM_NOTOFY:TTN_POP destroys created tooltip
3835  */
3836 
3837     static void
3838 make_tooltip(beval, text, pt)
3839     BalloonEval *beval;
3840     char *text;
3841     POINT pt;
3842 {
3843     TOOLINFO	ti;
3844 
3845     beval->balloon = CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS,
3846 	    NULL, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,
3847 	    CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
3848 	    beval->target, NULL, s_hinst, NULL);
3849 
3850     SetWindowPos(beval->balloon, HWND_TOPMOST, 0, 0, 0, 0,
3851 	    SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
3852 
3853     ti.cbSize = sizeof(TOOLINFO);
3854     ti.uFlags = TTF_SUBCLASS;
3855     ti.hwnd = beval->target;
3856     ti.hinst = 0; /* Don't use string resources */
3857     ti.uId = ID_BEVAL_TOOLTIP;
3858     ti.lpszText = text;
3859 
3860     /* Limit ballooneval bounding rect to CursorPos neighbourhood */
3861     ti.rect.left = pt.x - 3;
3862     ti.rect.top = pt.y - 3;
3863     ti.rect.right = pt.x + 3;
3864     ti.rect.bottom = pt.y + 3;
3865 
3866     SendMessage(beval->balloon, TTM_ADDTOOL, 0, (LPARAM)&ti);
3867     /* Make tooltip appear sooner */
3868     SendMessage(beval->balloon, TTM_SETDELAYTIME, TTDT_INITIAL, 10);
3869     /*
3870      * HACK: force tooltip to appear, because it'll not appear until
3871      * first mouse move. D*mn M$
3872      */
3873     mouse_event(MOUSEEVENTF_MOVE, 1, 1, 0, 0);
3874     mouse_event(MOUSEEVENTF_MOVE, (DWORD)-1, (DWORD)-1, 0, 0);
3875 }
3876 
3877     static void
3878 delete_tooltip(beval)
3879     BalloonEval	*beval;
3880 {
3881     DestroyWindow(beval->balloon);
3882 }
3883 
3884     static VOID CALLBACK
3885 BevalTimerProc(hwnd, uMsg, idEvent, dwTime)
3886     HWND    hwnd;
3887     UINT    uMsg;
3888     UINT    idEvent;
3889     DWORD   dwTime;
3890 {
3891     POINT	pt;
3892     RECT	rect;
3893 
3894     if (cur_beval == NULL || cur_beval->showState == ShS_SHOWING || !p_beval)
3895 	return;
3896 
3897     GetCursorPos(&pt);
3898     if (WindowFromPoint(pt) != s_textArea)
3899 	return;
3900 
3901     ScreenToClient(s_textArea, &pt);
3902     GetClientRect(s_textArea, &rect);
3903     if (!PtInRect(&rect, pt))
3904 	return;
3905 
3906     if (LastActivity > 0
3907 	    && (dwTime - LastActivity) >= (DWORD)p_bdlay
3908 	    && (cur_beval->showState != ShS_PENDING
3909 		|| abs(cur_beval->x - pt.x) > 3
3910 		|| abs(cur_beval->y - pt.y) > 3))
3911     {
3912 	/* Pointer resting in one place long enough, it's time to show
3913 	 * the tooltip. */
3914 	cur_beval->showState = ShS_PENDING;
3915 	cur_beval->x = pt.x;
3916 	cur_beval->y = pt.y;
3917 
3918 	TRACE0("BevalTimerProc: sending request");
3919 
3920 	if (cur_beval->msgCB != NULL)
3921 	    (*cur_beval->msgCB)(cur_beval, 0);
3922     }
3923 }
3924 
3925     void
3926 gui_mch_disable_beval_area(beval)
3927     BalloonEval	*beval;
3928 {
3929     TRACE0("gui_mch_disable_beval_area {{{");
3930     KillTimer(s_textArea, BevalTimerId);
3931     TRACE0("gui_mch_disable_beval_area }}}");
3932 }
3933 
3934     void
3935 gui_mch_enable_beval_area(beval)
3936     BalloonEval	*beval;
3937 {
3938     TRACE0("gui_mch_enable_beval_area |||");
3939     if (beval == NULL)
3940 	return;
3941     TRACE0("gui_mch_enable_beval_area {{{");
3942     BevalTimerId = SetTimer(s_textArea, 0, p_bdlay / 2, (TIMERPROC)BevalTimerProc);
3943     TRACE0("gui_mch_enable_beval_area }}}");
3944 }
3945 
3946     void
3947 gui_mch_post_balloon(beval, mesg)
3948     BalloonEval	*beval;
3949     char_u	*mesg;
3950 {
3951     POINT   pt;
3952     TRACE0("gui_mch_post_balloon {{{");
3953     if (beval->showState == ShS_SHOWING)
3954 	return;
3955     GetCursorPos(&pt);
3956     ScreenToClient(s_textArea, &pt);
3957 
3958     if (abs(beval->x - pt.x) < 3 && abs(beval->y - pt.y) < 3)
3959 	/* cursor is still here */
3960     {
3961 	gui_mch_disable_beval_area(cur_beval);
3962 	beval->showState = ShS_SHOWING;
3963 	make_tooltip(beval, mesg, pt);
3964     }
3965     TRACE0("gui_mch_post_balloon }}}");
3966 }
3967 
3968     BalloonEval *
3969 gui_mch_create_beval_area(target, mesg, mesgCB, clientData)
3970     void	*target;	/* ignored, always use s_textArea */
3971     char_u	*mesg;
3972     void	(*mesgCB)__ARGS((BalloonEval *, int));
3973     void	*clientData;
3974 {
3975     /* partially stolen from gui_beval.c */
3976     BalloonEval	*beval;
3977 
3978     if (mesg != NULL && mesgCB != NULL)
3979     {
3980 	EMSG(_("E232: Cannot create BalloonEval with both message and callback"));
3981 	return NULL;
3982     }
3983 
3984     beval = (BalloonEval *)alloc(sizeof(BalloonEval));
3985     if (beval != NULL)
3986     {
3987 	beval->target = s_textArea;
3988 	beval->balloon = NULL;
3989 
3990 	beval->showState = ShS_NEUTRAL;
3991 	beval->x = 0;
3992 	beval->y = 0;
3993 	beval->msg = mesg;
3994 	beval->msgCB = mesgCB;
3995 	beval->clientData = clientData;
3996 
3997 	InitCommonControls();
3998 
3999 	cur_beval = beval;
4000 
4001 	if (p_beval)
4002 	    gui_mch_enable_beval_area(beval);
4003 
4004     }
4005     return beval;
4006 }
4007 
4008     static void
4009 Handle_WM_Notify(hwnd, pnmh)
4010     HWND hwnd;
4011     LPNMHDR pnmh;
4012 {
4013     if (pnmh->idFrom != ID_BEVAL_TOOLTIP) /* it is not our tooltip */
4014 	return;
4015 
4016     if (cur_beval != NULL)
4017     {
4018 	if (pnmh->code == TTN_SHOW)
4019 	{
4020 	    TRACE0("TTN_SHOW {{{");
4021 	    TRACE0("TTN_SHOW }}}");
4022 	}
4023 	else if (pnmh->code == TTN_POP) /* Before tooltip disappear */
4024 	{
4025 	    TRACE0("TTN_POP {{{");
4026 	    delete_tooltip(cur_beval);
4027 	    gui_mch_enable_beval_area(cur_beval);
4028 	    TRACE0("TTN_POP }}}");
4029 
4030 	    cur_beval->showState = ShS_NEUTRAL;
4031 	}
4032     }
4033 }
4034 
4035     static void
4036 TrackUserActivity(UINT uMsg)
4037 {
4038     if ((uMsg >= WM_MOUSEFIRST && uMsg <= WM_MOUSELAST)
4039 	    || (uMsg >= WM_KEYFIRST && uMsg <= WM_KEYLAST))
4040 	LastActivity = GetTickCount();
4041 }
4042 
4043     void
4044 gui_mch_destroy_beval_area(beval)
4045     BalloonEval	*beval;
4046 {
4047     vim_free(beval);
4048 }
4049 #endif /* FEAT_BEVAL */
4050 
4051 #if defined(FEAT_NETBEANS_INTG) || defined(PROTO)
4052 /*
4053  * We have multiple signs to draw at the same location. Draw the
4054  * multi-sign indicator (down-arrow) instead. This is the Win32 version.
4055  */
4056     void
4057 netbeans_draw_multisign_indicator(int row)
4058 {
4059     int i;
4060     int y;
4061     int x;
4062 
4063     x = 0;
4064     y = TEXT_Y(row);
4065 
4066     for (i = 0; i < gui.char_height - 3; i++)
4067 	SetPixel(s_hdc, x+2, y++, gui.currFgColor);
4068 
4069     SetPixel(s_hdc, x+0, y, gui.currFgColor);
4070     SetPixel(s_hdc, x+2, y, gui.currFgColor);
4071     SetPixel(s_hdc, x+4, y++, gui.currFgColor);
4072     SetPixel(s_hdc, x+1, y, gui.currFgColor);
4073     SetPixel(s_hdc, x+2, y, gui.currFgColor);
4074     SetPixel(s_hdc, x+3, y++, gui.currFgColor);
4075     SetPixel(s_hdc, x+2, y, gui.currFgColor);
4076 }
4077 #endif
4078 
4079 #endif
4080