xref: /vim-8.2.3635/src/gui_w32.c (revision 3fdfa4a9)
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 #ifdef FEAT_MBYTE
1713 /*
1714  * Convert latin9 text to ucs-2.
1715  */
1716     static void
1717 latin9_to_ucs(char_u *text, int len, WCHAR *unicodebuf)
1718 {
1719     int		c;
1720 
1721     while (len-- >= 0)
1722     {
1723 	c = *text++;
1724 	switch (c)
1725 	{
1726 	    case 0xa4: c = 0x20ac; break;   /* euro */
1727 	    case 0xa6: c = 0x0160; break;   /* S hat */
1728 	    case 0xa8: c = 0x0161; break;   /* S -hat */
1729 	    case 0xb4: c = 0x017d; break;   /* Z hat */
1730 	    case 0xb8: c = 0x017e; break;   /* Z -hat */
1731 	    case 0xbc: c = 0x0152; break;   /* OE */
1732 	    case 0xbd: c = 0x0153; break;   /* oe */
1733 	    case 0xbe: c = 0x0178; break;   /* Y */
1734 	}
1735 	*unicodebuf++ = c;
1736     }
1737 }
1738 #endif
1739 
1740 #ifdef FEAT_RIGHTLEFT
1741 /*
1742  * What is this for?  In the case where you are using Win98 or Win2K or later,
1743  * and you are using a Hebrew font (or Arabic!), Windows does you a favor and
1744  * reverses the string sent to the TextOut... family.  This sucks, because we
1745  * go to a lot of effort to do the right thing, and there doesn't seem to be a
1746  * way to tell Windblows not to do this!
1747  *
1748  * The short of it is that this 'RevOut' only gets called if you are running
1749  * one of the new, "improved" MS OSes, and only if you are running in
1750  * 'rightleft' mode.  It makes display take *slightly* longer, but not
1751  * noticeably so.
1752  */
1753     static void
1754 RevOut( HDC s_hdc,
1755 	int col,
1756 	int row,
1757 	UINT foptions,
1758 	CONST RECT *pcliprect,
1759 	LPCTSTR text,
1760 	UINT len,
1761 	CONST INT *padding)
1762 {
1763     int		ix;
1764     static int	special = -1;
1765 
1766     if (special == -1)
1767     {
1768 	/* Check windows version: special treatment is needed if it is NT 5 or
1769 	 * Win98 or higher. */
1770 	if  ((os_version.dwPlatformId == VER_PLATFORM_WIN32_NT
1771 		    && os_version.dwMajorVersion >= 5)
1772 		|| (os_version.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS
1773 		    && (os_version.dwMajorVersion > 4
1774 			|| (os_version.dwMajorVersion == 4
1775 			    && os_version.dwMinorVersion > 0))))
1776 	    special = 1;
1777 	else
1778 	    special = 0;
1779     }
1780 
1781     if (special)
1782 	for (ix = 0; ix < (int)len; ++ix)
1783 	    ExtTextOut(s_hdc, col + TEXT_X(ix), row, foptions,
1784 					    pcliprect, text + ix, 1, padding);
1785     else
1786 	ExtTextOut(s_hdc, col, row, foptions, pcliprect, text, len, padding);
1787 }
1788 #endif
1789 
1790     void
1791 gui_mch_draw_string(
1792     int		row,
1793     int		col,
1794     char_u	*text,
1795     int		len,
1796     int		flags)
1797 {
1798     static int	*padding = NULL;
1799     static int	pad_size = 0;
1800     int		i;
1801     const RECT	*pcliprect = NULL;
1802     UINT	foptions = 0;
1803 #ifdef FEAT_MBYTE
1804     static WCHAR *unicodebuf = NULL;
1805     static int   *unicodepdy = NULL;
1806     int		unibuflen = 0;
1807     int		n = 0;
1808 #endif
1809     HPEN	hpen, old_pen;
1810     int		y;
1811 
1812 #ifndef MSWIN16_FASTTEXT
1813     /*
1814      * Italic and bold text seems to have an extra row of pixels at the bottom
1815      * (below where the bottom of the character should be).  If we draw the
1816      * characters with a solid background, the top row of pixels in the
1817      * character below will be overwritten.  We can fix this by filling in the
1818      * background ourselves, to the correct character proportions, and then
1819      * writing the character in transparent mode.  Still have a problem when
1820      * the character is "_", which gets written on to the character below.
1821      * New fix: set gui.char_ascent to -1.  This shifts all characters up one
1822      * pixel in their slots, which fixes the problem with the bottom row of
1823      * pixels.	We still need this code because otherwise the top row of pixels
1824      * becomes a problem. - webb.
1825      */
1826     static HBRUSH	hbr_cache[2] = {NULL, NULL};
1827     static guicolor_T	brush_color[2] = {INVALCOLOR, INVALCOLOR};
1828     static int		brush_lru = 0;
1829     HBRUSH		hbr;
1830     RECT		rc;
1831 
1832     if (!(flags & DRAW_TRANSP))
1833     {
1834 	/*
1835 	 * Clear background first.
1836 	 * Note: FillRect() excludes right and bottom of rectangle.
1837 	 */
1838 	rc.left = FILL_X(col);
1839 	rc.top = FILL_Y(row);
1840 #ifdef FEAT_MBYTE
1841 	if (has_mbyte)
1842 	{
1843 	    int cell_len = 0;
1844 
1845 	    /* Compute the length in display cells. */
1846 	    for (n = 0; n < len; n += MB_BYTE2LEN(text[n]))
1847 		cell_len += (*mb_ptr2cells)(text + n);
1848 	    rc.right = FILL_X(col + cell_len);
1849 	}
1850 	else
1851 #endif
1852 	    rc.right = FILL_X(col + len);
1853 	rc.bottom = FILL_Y(row + 1);
1854 
1855 	/* Cache the created brush, that saves a lot of time.  We need two:
1856 	 * one for cursor background and one for the normal background. */
1857 	if (gui.currBgColor == brush_color[0])
1858 	{
1859 	    hbr = hbr_cache[0];
1860 	    brush_lru = 1;
1861 	}
1862 	else if (gui.currBgColor == brush_color[1])
1863 	{
1864 	    hbr = hbr_cache[1];
1865 	    brush_lru = 0;
1866 	}
1867 	else
1868 	{
1869 	    if (hbr_cache[brush_lru] != NULL)
1870 		DeleteBrush(hbr_cache[brush_lru]);
1871 	    hbr_cache[brush_lru] = CreateSolidBrush(gui.currBgColor);
1872 	    brush_color[brush_lru] = gui.currBgColor;
1873 	    hbr = hbr_cache[brush_lru];
1874 	    brush_lru = !brush_lru;
1875 	}
1876 	FillRect(s_hdc, &rc, hbr);
1877 
1878 	SetBkMode(s_hdc, TRANSPARENT);
1879 
1880 	/*
1881 	 * When drawing block cursor, prevent inverted character spilling
1882 	 * over character cell (can happen with bold/italic)
1883 	 */
1884 	if (flags & DRAW_CURSOR)
1885 	{
1886 	    pcliprect = &rc;
1887 	    foptions = ETO_CLIPPED;
1888 	}
1889     }
1890 #else
1891     /*
1892      * The alternative would be to write the characters in opaque mode, but
1893      * when the text is not exactly the same proportions as normal text, too
1894      * big or too little a rectangle gets drawn for the background.
1895      */
1896     SetBkMode(s_hdc, OPAQUE);
1897     SetBkColor(s_hdc, gui.currBgColor);
1898 #endif
1899     SetTextColor(s_hdc, gui.currFgColor);
1900     SelectFont(s_hdc, gui.currFont);
1901 
1902     if (pad_size != Columns || padding == NULL || padding[0] != gui.char_width)
1903     {
1904 	vim_free(padding);
1905 	pad_size = Columns;
1906 
1907 	padding = (int *)alloc(pad_size * sizeof(int));
1908 	if (padding != NULL)
1909 	    for (i = 0; i < pad_size; i++)
1910 		padding[i] = gui.char_width;
1911     }
1912 
1913     /* On NT, tell the font renderer not to "help" us with Hebrew and Arabic
1914      * text.  This doesn't work in 9x, so we have to deal with it manually on
1915      * those systems. */
1916     if (os_version.dwPlatformId == VER_PLATFORM_WIN32_NT)
1917 	foptions |= ETO_IGNORELANGUAGE;
1918 
1919     /*
1920      * We have to provide the padding argument because italic and bold versions
1921      * of fixed-width fonts are often one pixel or so wider than their normal
1922      * versions.
1923      * No check for DRAW_BOLD, Windows will have done it already.
1924      */
1925 
1926 #ifdef FEAT_MBYTE
1927     /* Check if there are any UTF-8 characters.  If not, use normal text
1928      * output to speed up output. */
1929     if (enc_utf8)
1930 	for (n = 0; n < len; ++n)
1931 	    if (text[n] >= 0x80)
1932 		break;
1933 
1934     /* Check if the Unicode buffer exists and is big enough.  Create it
1935      * with the same lengt as the multi-byte string, the number of wide
1936      * characters is always equal or smaller. */
1937     if ((enc_utf8
1938 		|| (enc_codepage > 0 && (int)GetACP() != enc_codepage)
1939 		|| enc_latin9)
1940 	    && (unicodebuf == NULL || len > unibuflen))
1941     {
1942 	vim_free(unicodebuf);
1943 	unicodebuf = (WCHAR *)alloc(len * sizeof(WCHAR));
1944 
1945 	vim_free(unicodepdy);
1946 	unicodepdy = (int *)alloc(len * sizeof(int));
1947 
1948 	unibuflen = len;
1949     }
1950 
1951     if (enc_utf8 && n < len && unicodebuf != NULL)
1952     {
1953 	/* Output UTF-8 characters.  Caller has already separated
1954 	 * composing characters. */
1955 	int		i = 0;
1956 	int		clen;	/* string length up to composing char */
1957 	int		cells;	/* cell width of string up to composing char */
1958 	int		cw;	/* width of current cell */
1959 
1960 	cells = 0;
1961 	for (clen = 0; i < len; )
1962 	{
1963 	    unicodebuf[clen] = utf_ptr2char(text + i);
1964 	    cw = utf_char2cells(unicodebuf[clen]);
1965 	    if (cw > 2)		/* don't use 4 for unprintable char */
1966 		cw = 1;
1967 	    if (unicodepdy != NULL)
1968 	    {
1969 		/* Use unicodepdy to make characters fit as we expect, even
1970 		 * when the font uses different widths (e.g., bold character
1971 		 * is wider).  */
1972 		unicodepdy[clen] = cw * gui.char_width;
1973 	    }
1974 	    cells += cw;
1975 	    i += utfc_ptr2len_check_len(text + i, len - i);
1976 	    ++clen;
1977 	}
1978 	ExtTextOutW(s_hdc, TEXT_X(col), TEXT_Y(row),
1979 			     foptions, pcliprect, unicodebuf, clen, unicodepdy);
1980 	len = cells;	/* used for underlining */
1981     }
1982     else if ((enc_codepage > 0 && (int)GetACP() != enc_codepage) || enc_latin9)
1983     {
1984 	/* If we want to display codepage data, and the current CP is not the
1985 	 * ANSI one, we need to go via Unicode. */
1986 	if (unicodebuf != NULL)
1987 	{
1988 	    if (enc_latin9)
1989 		latin9_to_ucs(text, len, unicodebuf);
1990 	    else
1991 		len = MultiByteToWideChar(enc_codepage,
1992 			MB_PRECOMPOSED,
1993 			(char *)text, len,
1994 			(LPWSTR)unicodebuf, unibuflen);
1995 	    if (len != 0)
1996 		ExtTextOutW(s_hdc, TEXT_X(col), TEXT_Y(row),
1997 			      foptions, pcliprect, unicodebuf, len, NULL);
1998 	}
1999     }
2000     else
2001 #endif
2002     {
2003 #ifdef FEAT_RIGHTLEFT
2004 	/* If we can't use ETO_IGNORELANGUAGE, we can't tell Windows not to
2005 	 * mess up RL text, so we have to draw it character-by-character.
2006 	 * Only do this if RL is on, since it's slow. */
2007 	if (curwin->w_p_rl && !(foptions & ETO_IGNORELANGUAGE))
2008 	    RevOut(s_hdc, TEXT_X(col), TEXT_Y(row),
2009 			 foptions, pcliprect, (char *)text, len, padding);
2010 	else
2011 #endif
2012 	    ExtTextOut(s_hdc, TEXT_X(col), TEXT_Y(row),
2013 			 foptions, pcliprect, (char *)text, len, padding);
2014     }
2015 
2016     if (flags & DRAW_UNDERL)
2017     {
2018 	hpen = CreatePen(PS_SOLID, 1, gui.currFgColor);
2019 	old_pen = SelectObject(s_hdc, hpen);
2020 	/* When p_linespace is 0, overwrite the bottom row of pixels.
2021 	 * Otherwise put the line just below the character. */
2022 	y = FILL_Y(row + 1) - 1;
2023 #ifndef MSWIN16_FASTTEXT
2024 	if (p_linespace > 1)
2025 	    y -= p_linespace - 1;
2026 #endif
2027 	MoveToEx(s_hdc, FILL_X(col), y, NULL);
2028 	/* Note: LineTo() excludes the last pixel in the line. */
2029 	LineTo(s_hdc, FILL_X(col + len), y);
2030 	DeleteObject(SelectObject(s_hdc, old_pen));
2031     }
2032 }
2033 
2034 
2035 /*
2036  * Output routines.
2037  */
2038 
2039 /* Flush any output to the screen */
2040     void
2041 gui_mch_flush(void)
2042 {
2043 #   if defined(__BORLANDC__)
2044     /*
2045      * The GdiFlush declaration (in Borland C 5.01 <wingdi.h>) is not a
2046      * prototype declaration.
2047      * The compiler complains if __stdcall is not used in both declarations.
2048      */
2049     BOOL  __stdcall GdiFlush(void);
2050 #   endif
2051 
2052     GdiFlush();
2053 }
2054 
2055     static void
2056 clear_rect(RECT *rcp)
2057 {
2058     HBRUSH  hbr;
2059 
2060     hbr = CreateSolidBrush(gui.back_pixel);
2061     FillRect(s_hdc, rcp, hbr);
2062     DeleteBrush(hbr);
2063 }
2064 
2065 
2066 #if defined(FEAT_MENU) || defined(PROTO)
2067 /*
2068  * Add a sub menu to the menu bar.
2069  */
2070     void
2071 gui_mch_add_menu(
2072     vimmenu_T	*menu,
2073     int		pos)
2074 {
2075     vimmenu_T	*parent = menu->parent;
2076 
2077     menu->submenu_id = CreatePopupMenu();
2078     menu->id = s_menu_id++;
2079 
2080     if (menu_is_menubar(menu->name))
2081     {
2082 	if (is_winnt_3())
2083 	{
2084 	    InsertMenu((parent == NULL) ? s_menuBar : parent->submenu_id,
2085 		    (UINT)pos, MF_POPUP | MF_STRING | MF_BYPOSITION,
2086 		    (UINT)menu->submenu_id, (LPCTSTR) menu->name);
2087 	}
2088 	else
2089 	{
2090 #ifdef FEAT_MBYTE
2091 	    WCHAR	*wn = NULL;
2092 	    int		n;
2093 
2094 	    if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
2095 	    {
2096 		/* 'encoding' differs from active codepage: convert menu name
2097 		 * and use wide function */
2098 		wn = enc_to_ucs2(menu->name, NULL);
2099 		if (wn != NULL)
2100 		{
2101 		    MENUITEMINFOW	infow;
2102 
2103 		    infow.cbSize = sizeof(infow);
2104 		    infow.fMask = MIIM_DATA | MIIM_TYPE | MIIM_ID
2105 							       | MIIM_SUBMENU;
2106 		    infow.dwItemData = (DWORD)menu;
2107 		    infow.wID = menu->id;
2108 		    infow.fType = MFT_STRING;
2109 		    infow.dwTypeData = wn;
2110 		    infow.cch = wcslen(wn);
2111 		    infow.hSubMenu = menu->submenu_id;
2112 		    n = InsertMenuItemW((parent == NULL)
2113 					    ? s_menuBar : parent->submenu_id,
2114 					    (UINT)pos, TRUE, &infow);
2115 		    vim_free(wn);
2116 		    if (n == 0 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
2117 			/* Failed, try using non-wide function. */
2118 			wn = NULL;
2119 		}
2120 	    }
2121 
2122 	    if (wn == NULL)
2123 #endif
2124 	    {
2125 		MENUITEMINFO	info;
2126 
2127 		info.cbSize = sizeof(info);
2128 		info.fMask = MIIM_DATA | MIIM_TYPE | MIIM_ID | MIIM_SUBMENU;
2129 		info.dwItemData = (DWORD)menu;
2130 		info.wID = menu->id;
2131 		info.fType = MFT_STRING;
2132 		info.dwTypeData = (LPTSTR)menu->name;
2133 		info.cch = (UINT)STRLEN(menu->name);
2134 		info.hSubMenu = menu->submenu_id;
2135 		InsertMenuItem((parent == NULL)
2136 					? s_menuBar : parent->submenu_id,
2137 					(UINT)pos, TRUE, &info);
2138 	    }
2139 	}
2140     }
2141 
2142     /* Fix window size if menu may have wrapped */
2143     if (parent == NULL)
2144 	gui_mswin_get_menu_height(!gui.starting);
2145 #ifdef FEAT_TEAROFF
2146     else if (IsWindow(parent->tearoff_handle))
2147 	rebuild_tearoff(parent);
2148 #endif
2149 }
2150 
2151     void
2152 gui_mch_show_popupmenu(vimmenu_T *menu)
2153 {
2154     POINT mp;
2155 
2156     (void)GetCursorPos((LPPOINT)&mp);
2157     gui_mch_show_popupmenu_at(menu, (int)mp.x, (int)mp.y);
2158 }
2159 
2160     void
2161 gui_make_popup(char_u *path_name)
2162 {
2163     vimmenu_T	*menu = gui_find_menu(path_name);
2164 
2165     if (menu != NULL)
2166     {
2167 	POINT	p;
2168 
2169 	/* Find the position of the current cursor */
2170 	GetDCOrgEx(s_hdc, &p);
2171 	if (curwin != NULL)
2172 	{
2173 	    p.x += TEXT_X(W_WINCOL(curwin) + curwin->w_wcol + 1);
2174 	    p.y += TEXT_Y(W_WINROW(curwin) + curwin->w_wrow + 1);
2175 	}
2176 	msg_scroll = FALSE;
2177 	gui_mch_show_popupmenu_at(menu, (int)p.x, (int)p.y);
2178     }
2179 }
2180 
2181 #if defined(FEAT_TEAROFF) || defined(PROTO)
2182 /*
2183  * Given a menu descriptor, e.g. "File.New", find it in the menu hierarchy and
2184  * create it as a pseudo-"tearoff menu".
2185  */
2186     void
2187 gui_make_tearoff(char_u *path_name)
2188 {
2189     vimmenu_T	*menu = gui_find_menu(path_name);
2190 
2191     /* Found the menu, so tear it off. */
2192     if (menu != NULL)
2193 	gui_mch_tearoff(menu->dname, menu, 0xffffL, 0xffffL);
2194 }
2195 #endif
2196 
2197 /*
2198  * Add a menu item to a menu
2199  */
2200     void
2201 gui_mch_add_menu_item(
2202     vimmenu_T	*menu,
2203     int		idx)
2204 {
2205     vimmenu_T	*parent = menu->parent;
2206 
2207     menu->id = s_menu_id++;
2208     menu->submenu_id = NULL;
2209 
2210 #ifdef FEAT_TEAROFF
2211     if (STRNCMP(menu->name, TEAR_STRING, TEAR_LEN) == 0)
2212     {
2213 	InsertMenu(parent->submenu_id, (UINT)idx, MF_BITMAP|MF_BYPOSITION,
2214 		(UINT)menu->id, (LPCTSTR) s_htearbitmap);
2215     }
2216     else
2217 #endif
2218 #ifdef FEAT_TOOLBAR
2219     if (menu_is_toolbar(parent->name))
2220     {
2221 	TBBUTTON newtb;
2222 
2223 	vim_memset(&newtb, 0, sizeof(newtb));
2224 	if (menu_is_separator(menu->name))
2225 	{
2226 	    newtb.iBitmap = 0;
2227 	    newtb.fsStyle = TBSTYLE_SEP;
2228 	}
2229 	else
2230 	{
2231 	    newtb.iBitmap = get_toolbar_bitmap(menu);
2232 	    newtb.fsStyle = TBSTYLE_BUTTON;
2233 	}
2234 	newtb.idCommand = menu->id;
2235 	newtb.fsState = TBSTATE_ENABLED;
2236 	newtb.iString = 0;
2237 	SendMessage(s_toolbarhwnd, TB_INSERTBUTTON, (WPARAM)idx,
2238 							     (LPARAM)&newtb);
2239 	menu->submenu_id = (HMENU)-1;
2240     }
2241     else
2242 #endif
2243     {
2244 #ifdef FEAT_MBYTE
2245 	WCHAR	*wn = NULL;
2246 	int	n;
2247 
2248 	if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
2249 	{
2250 	    /* 'encoding' differs from active codepage: convert menu item name
2251 	     * and use wide function */
2252 	    wn = enc_to_ucs2(menu->name, NULL);
2253 	    if (wn != NULL)
2254 	    {
2255 		n = InsertMenuW(parent->submenu_id, (UINT)idx,
2256 			(menu_is_separator(menu->name)
2257 				 ? MF_SEPARATOR : MF_STRING) | MF_BYPOSITION,
2258 			(UINT)menu->id, wn);
2259 		vim_free(wn);
2260 		if (n == 0 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
2261 		    /* Failed, try using non-wide function. */
2262 		    wn = NULL;
2263 	    }
2264 	}
2265 	if (wn == NULL)
2266 #endif
2267 	    InsertMenu(parent->submenu_id, (UINT)idx,
2268 		(menu_is_separator(menu->name) ? MF_SEPARATOR : MF_STRING)
2269 							      | MF_BYPOSITION,
2270 		(UINT)menu->id, (LPCTSTR)menu->name);
2271 #ifdef FEAT_TEAROFF
2272 	if (IsWindow(parent->tearoff_handle))
2273 	    rebuild_tearoff(parent);
2274 #endif
2275     }
2276 }
2277 
2278 /*
2279  * Destroy the machine specific menu widget.
2280  */
2281     void
2282 gui_mch_destroy_menu(vimmenu_T *menu)
2283 {
2284 #ifdef FEAT_TOOLBAR
2285     /*
2286      * is this a toolbar button?
2287      */
2288     if (menu->submenu_id == (HMENU)-1)
2289     {
2290 	int iButton;
2291 
2292 	iButton = (int)SendMessage(s_toolbarhwnd, TB_COMMANDTOINDEX,
2293 							 (WPARAM)menu->id, 0);
2294 	SendMessage(s_toolbarhwnd, TB_DELETEBUTTON, (WPARAM)iButton, 0);
2295     }
2296     else
2297 #endif
2298     {
2299 	if (menu->parent != NULL
2300 		&& menu_is_popup(menu->parent->dname)
2301 		&& menu->parent->submenu_id != NULL)
2302 	    RemoveMenu(menu->parent->submenu_id, menu->id, MF_BYCOMMAND);
2303 	else
2304 	    RemoveMenu(s_menuBar, menu->id, MF_BYCOMMAND);
2305 	if (menu->submenu_id != NULL)
2306 	    DestroyMenu(menu->submenu_id);
2307 #ifdef FEAT_TEAROFF
2308 	if (IsWindow(menu->tearoff_handle))
2309 	    DestroyWindow(menu->tearoff_handle);
2310 	if (menu->parent != NULL
2311 		&& menu->parent->children != NULL
2312 		&& IsWindow(menu->parent->tearoff_handle))
2313 	{
2314 	    /* This menu must not show up when rebuilding the tearoff window. */
2315 	    menu->modes = 0;
2316 	    rebuild_tearoff(menu->parent);
2317 	}
2318 #endif
2319     }
2320 }
2321 
2322 #ifdef FEAT_TEAROFF
2323     static void
2324 rebuild_tearoff(vimmenu_T *menu)
2325 {
2326     /*hackish*/
2327     char_u	tbuf[128];
2328     RECT	trect;
2329     RECT	rct;
2330     RECT	roct;
2331     int		x, y;
2332 
2333     HWND thwnd = menu->tearoff_handle;
2334 
2335     GetWindowText(thwnd, tbuf, 127);
2336     if (GetWindowRect(thwnd, &trect)
2337 	    && GetWindowRect(s_hwnd, &rct)
2338 	    && GetClientRect(s_hwnd, &roct))
2339     {
2340 	x = trect.left - rct.left;
2341 	y = (trect.top -  rct.bottom  + roct.bottom);
2342     }
2343     else
2344     {
2345 	x = y = 0xffffL;
2346     }
2347     DestroyWindow(thwnd);
2348     if (menu->children != NULL)
2349     {
2350 	gui_mch_tearoff(tbuf, menu, x, y);
2351 	if (IsWindow(menu->tearoff_handle))
2352 	    (void) SetWindowPos(menu->tearoff_handle,
2353 				NULL,
2354 				(int)trect.left,
2355 				(int)trect.top,
2356 				0, 0,
2357 				SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
2358     }
2359 }
2360 #endif /* FEAT_TEAROFF */
2361 
2362 /*
2363  * Make a menu either grey or not grey.
2364  */
2365     void
2366 gui_mch_menu_grey(
2367     vimmenu_T	*menu,
2368     int	    grey)
2369 {
2370 #ifdef FEAT_TOOLBAR
2371     /*
2372      * is this a toolbar button?
2373      */
2374     if (menu->submenu_id == (HMENU)-1)
2375     {
2376 	SendMessage(s_toolbarhwnd, TB_ENABLEBUTTON,
2377 	    (WPARAM)menu->id, (LPARAM) MAKELONG((grey ? FALSE : TRUE), 0) );
2378     }
2379     else
2380 #endif
2381     if (grey)
2382 	EnableMenuItem(s_menuBar, menu->id, MF_BYCOMMAND | MF_GRAYED);
2383     else
2384 	EnableMenuItem(s_menuBar, menu->id, MF_BYCOMMAND | MF_ENABLED);
2385 
2386 #ifdef FEAT_TEAROFF
2387     if ((menu->parent != NULL) && (IsWindow(menu->parent->tearoff_handle)))
2388     {
2389 	WORD menuID;
2390 	HWND menuHandle;
2391 
2392 	/*
2393 	 * A tearoff button has changed state.
2394 	 */
2395 	if (menu->children == NULL)
2396 	    menuID = (WORD)(menu->id);
2397 	else
2398 	    menuID = (WORD)((DWORD)(menu->submenu_id) | (DWORD)0x8000);
2399 	menuHandle = GetDlgItem(menu->parent->tearoff_handle, menuID);
2400 	if (menuHandle)
2401 	    EnableWindow(menuHandle, !grey);
2402 
2403     }
2404 #endif
2405 }
2406 
2407 #endif /* FEAT_MENU */
2408 
2409 
2410 /* define some macros used to make the dialogue creation more readable */
2411 
2412 #define add_string(s) strcpy((LPSTR)p, s); (LPSTR)p += (strlen((LPSTR)p) + 1)
2413 #define add_word(x)		*p++ = (x)
2414 #define add_byte(x)		*((LPSTR)p)++ = (x)
2415 #define add_long(x)		*((LPDWORD)p)++ = (x)
2416 
2417 #if defined(FEAT_GUI_DIALOG) || defined(PROTO)
2418 /*
2419  * stuff for dialogs
2420  */
2421 
2422 /*
2423  * The callback routine used by all the dialogs.  Very simple.  First,
2424  * acknowledges the INITDIALOG message so that Windows knows to do standard
2425  * dialog stuff (Return = default, Esc = cancel....) Second, if a button is
2426  * pressed, return that button's ID - IDCANCEL (2), which is the button's
2427  * number.
2428  */
2429     static LRESULT CALLBACK
2430 dialog_callback(
2431     HWND hwnd,
2432     UINT message,
2433     WPARAM wParam,
2434     LPARAM lParam)
2435 {
2436     if (message == WM_INITDIALOG)
2437     {
2438 	CenterWindow(hwnd, GetWindow(hwnd, GW_OWNER));
2439 	/* Set focus to the dialog.  Set the default button, if specified. */
2440 	(void)SetFocus(hwnd);
2441 	if (dialog_default_button > IDCANCEL)
2442 	    (void)SetFocus(GetDlgItem(hwnd, dialog_default_button));
2443 	return FALSE;
2444     }
2445 
2446     if (message == WM_COMMAND)
2447     {
2448 	int	button = LOWORD(wParam);
2449 
2450 	/* Don't end the dialog if something was selected that was
2451 	 * not a button.
2452 	 */
2453 	if (button >= DLG_NONBUTTON_CONTROL)
2454 	    return TRUE;
2455 
2456 	/* If the edit box exists, copy the string. */
2457 	if (s_textfield != NULL)
2458 	    GetDlgItemText(hwnd, DLG_NONBUTTON_CONTROL + 2,
2459 							 s_textfield, IOSIZE);
2460 
2461 	/*
2462 	 * Need to check for IDOK because if the user just hits Return to
2463 	 * accept the default value, some reason this is what we get.
2464 	 */
2465 	if (button == IDOK)
2466 	{
2467 	    if (dialog_default_button > IDCANCEL)
2468 		EndDialog(hwnd, dialog_default_button);
2469 	}
2470 	else
2471 	    EndDialog(hwnd, button - IDCANCEL);
2472 	return TRUE;
2473     }
2474 
2475     if ((message == WM_SYSCOMMAND) && (wParam == SC_CLOSE))
2476     {
2477 	EndDialog(hwnd, 0);
2478 	return TRUE;
2479     }
2480     return FALSE;
2481 }
2482 
2483 /*
2484  * Create a dialog dynamically from the parameter strings.
2485  * type		= type of dialog (question, alert, etc.)
2486  * title	= dialog title. may be NULL for default title.
2487  * message	= text to display. Dialog sizes to accommodate it.
2488  * buttons	= '\n' separated list of button captions, default first.
2489  * dfltbutton	= number of default button.
2490  *
2491  * This routine returns 1 if the first button is pressed,
2492  *			2 for the second, etc.
2493  *
2494  *			0 indicates Esc was pressed.
2495  *			-1 for unexpected error
2496  *
2497  * If stubbing out this fn, return 1.
2498  */
2499 
2500 static const char_u *dlg_icons[] = /* must match names in resource file */
2501 {
2502     "IDR_VIM",
2503     "IDR_VIM_ERROR",
2504     "IDR_VIM_ALERT",
2505     "IDR_VIM_INFO",
2506     "IDR_VIM_QUESTION"
2507 };
2508 
2509 #ifdef USE_SYSMENU_FONT
2510 /*
2511  * Get Menu Font.
2512  * Return OK or FAIL.
2513  */
2514     static int
2515 gui_w32_get_menu_font(LOGFONT *lf)
2516 {
2517     NONCLIENTMETRICS nm;
2518 
2519     nm.cbSize = sizeof(NONCLIENTMETRICS);
2520     if (!SystemParametersInfo(
2521 	    SPI_GETNONCLIENTMETRICS,
2522 	    sizeof(NONCLIENTMETRICS),
2523 	    &nm,
2524 	    0))
2525 	return FAIL;
2526     *lf = nm.lfMenuFont;
2527     return OK;
2528 }
2529 #endif
2530 
2531     int
2532 gui_mch_dialog(
2533     int		 type,
2534     char_u	*title,
2535     char_u	*message,
2536     char_u	*buttons,
2537     int		 dfltbutton,
2538     char_u	*textfield)
2539 {
2540     WORD	*p, *pdlgtemplate, *pnumitems;
2541     int		numButtons;
2542     int		*buttonWidths, *buttonPositions;
2543     int		buttonYpos;
2544     int		nchar, i;
2545     DWORD	lStyle;
2546     int		dlgwidth = 0;
2547     int		dlgheight;
2548     int		editboxheight;
2549     int		horizWidth = 0;
2550     int		msgheight;
2551     char_u	*pstart;
2552     char_u	*pend;
2553     char_u	*tbuffer;
2554     RECT	rect;
2555     HWND	hwnd;
2556     HDC		hdc;
2557     HFONT	font, oldFont;
2558     TEXTMETRIC	fontInfo;
2559     int		fontHeight;
2560     int		textWidth, minButtonWidth, messageWidth;
2561     int		maxDialogWidth;
2562     int		vertical;
2563     int		dlgPaddingX;
2564     int		dlgPaddingY;
2565 #ifdef USE_SYSMENU_FONT
2566     LOGFONT	lfSysmenu;
2567     int		use_lfSysmenu = FALSE;
2568 #endif
2569 
2570 #ifndef NO_CONSOLE
2571     /* Don't output anything in silent mode ("ex -s") */
2572     if (silent_mode)
2573 	return dfltbutton;   /* return default option */
2574 #endif
2575 
2576     /* If there is no window yet, open it. */
2577     if (s_hwnd == NULL && gui_mch_init() == FAIL)
2578 	return dfltbutton;
2579 
2580     if ((type < 0) || (type > VIM_LAST_TYPE))
2581 	type = 0;
2582 
2583     /* allocate some memory for dialog template */
2584     /* TODO should compute this really*/
2585     pdlgtemplate = p = (PWORD)LocalAlloc(LPTR, DLG_ALLOC_SIZE);
2586 
2587     if (p == NULL)
2588 	return -1;
2589 
2590     /*
2591      * make a copy of 'buttons' to fiddle with it.  complier grizzles because
2592      * vim_strsave() doesn't take a const arg (why not?), so cast away the
2593      * const.
2594      */
2595     tbuffer = vim_strsave(buttons);
2596     if (tbuffer == NULL)
2597 	return -1;
2598 
2599     --dfltbutton;   /* Change from one-based to zero-based */
2600 
2601     /* Count buttons */
2602     numButtons = 1;
2603     for (i = 0; tbuffer[i] != '\0'; i++)
2604     {
2605 	if (tbuffer[i] == DLG_BUTTON_SEP)
2606 	    numButtons++;
2607     }
2608     if (dfltbutton >= numButtons)
2609 	dfltbutton = -1;
2610 
2611     /* Allocate array to hold the width of each button */
2612     buttonWidths = (int *) lalloc(numButtons * sizeof(int), TRUE);
2613     if (buttonWidths == NULL)
2614 	return -1;
2615 
2616     /* Allocate array to hold the X position of each button */
2617     buttonPositions = (int *) lalloc(numButtons * sizeof(int), TRUE);
2618     if (buttonPositions == NULL)
2619 	return -1;
2620 
2621     /*
2622      * Calculate how big the dialog must be.
2623      */
2624     hwnd = GetDesktopWindow();
2625     hdc = GetWindowDC(hwnd);
2626 #ifdef USE_SYSMENU_FONT
2627     if (gui_w32_get_menu_font(&lfSysmenu) == OK)
2628     {
2629 	font = CreateFontIndirect(&lfSysmenu);
2630 	use_lfSysmenu = TRUE;
2631     }
2632     else
2633 #endif
2634     font = CreateFont(-DLG_FONT_POINT_SIZE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2635 		      VARIABLE_PITCH , DLG_FONT_NAME);
2636     if (s_usenewlook)
2637     {
2638 	oldFont = SelectFont(hdc, font);
2639 	dlgPaddingX = DLG_PADDING_X;
2640 	dlgPaddingY = DLG_PADDING_Y;
2641     }
2642     else
2643     {
2644 	oldFont = SelectFont(hdc, GetStockObject(SYSTEM_FONT));
2645 	dlgPaddingX = DLG_OLD_STYLE_PADDING_X;
2646 	dlgPaddingY = DLG_OLD_STYLE_PADDING_Y;
2647     }
2648     GetTextMetrics(hdc, &fontInfo);
2649     fontHeight = fontInfo.tmHeight;
2650 
2651     /* Minimum width for horizontal button */
2652     minButtonWidth = GetTextWidth(hdc, "Cancel", 6);
2653 
2654     /* Maximum width of a dialog, if possible */
2655     GetWindowRect(s_hwnd, &rect);
2656     maxDialogWidth = rect.right - rect.left
2657 		     - GetSystemMetrics(SM_CXFRAME) * 2;
2658     if (maxDialogWidth < DLG_MIN_MAX_WIDTH)
2659 	maxDialogWidth = DLG_MIN_MAX_WIDTH;
2660 
2661     /* Set dlgwidth to width of message */
2662     pstart = message;
2663     messageWidth = 0;
2664     msgheight = 0;
2665     do
2666     {
2667 	pend = vim_strchr(pstart, DLG_BUTTON_SEP);
2668 	if (pend == NULL)
2669 	    pend = pstart + STRLEN(pstart);	/* Last line of message. */
2670 	msgheight += fontHeight;
2671 	textWidth = GetTextWidth(hdc, pstart, (int)(pend - pstart));
2672 	if (textWidth > messageWidth)
2673 	    messageWidth = textWidth;
2674 	pstart = pend + 1;
2675     } while (*pend != NUL);
2676     dlgwidth = messageWidth;
2677 
2678     /* Add width of icon to dlgwidth, and some space */
2679     dlgwidth += DLG_ICON_WIDTH + 3 * dlgPaddingX;
2680 
2681     if (msgheight < DLG_ICON_HEIGHT)
2682 	msgheight = DLG_ICON_HEIGHT;
2683 
2684     /*
2685      * Check button names.  A long one will make the dialog wider.
2686      */
2687     vertical = (vim_strchr(p_go, GO_VERTICAL) != NULL);
2688     if (!vertical)
2689     {
2690 	// Place buttons horizontally if they fit.
2691 	horizWidth = dlgPaddingX;
2692 	pstart = tbuffer;
2693 	i = 0;
2694 	do
2695 	{
2696 	    pend = vim_strchr(pstart, DLG_BUTTON_SEP);
2697 	    if (pend == NULL)
2698 		pend = pstart + STRLEN(pstart);	// Last button name.
2699 	    textWidth = GetTextWidth(hdc, pstart, (int)(pend - pstart));
2700 	    if (textWidth < minButtonWidth)
2701 		textWidth = minButtonWidth;
2702 	    textWidth += dlgPaddingX;	    /* Padding within button */
2703 	    buttonWidths[i] = textWidth;
2704 	    buttonPositions[i++] = horizWidth;
2705 	    horizWidth += textWidth + dlgPaddingX; /* Pad between buttons */
2706 	    pstart = pend + 1;
2707 	} while (*pend != NUL);
2708 
2709 	if (horizWidth > maxDialogWidth)
2710 	    vertical = TRUE;	// Too wide to fit on the screen.
2711 	else if (horizWidth > dlgwidth)
2712 	    dlgwidth = horizWidth;
2713     }
2714 
2715     if (vertical)
2716     {
2717 	// Stack buttons vertically.
2718 	pstart = tbuffer;
2719 	do
2720 	{
2721 	    pend = vim_strchr(pstart, DLG_BUTTON_SEP);
2722 	    if (pend == NULL)
2723 		pend = pstart + STRLEN(pstart);	// Last button name.
2724 	    textWidth = GetTextWidth(hdc, pstart, (int)(pend - pstart));
2725 	    textWidth += dlgPaddingX;		/* Padding within button */
2726 	    textWidth += DLG_VERT_PADDING_X * 2; /* Padding around button */
2727 	    if (textWidth > dlgwidth)
2728 		dlgwidth = textWidth;
2729 	    pstart = pend + 1;
2730 	} while (*pend != NUL);
2731     }
2732 
2733     if (dlgwidth < DLG_MIN_WIDTH)
2734 	dlgwidth = DLG_MIN_WIDTH;	/* Don't allow a really thin dialog!*/
2735 
2736     /* start to fill in the dlgtemplate information.  addressing by WORDs */
2737     if (s_usenewlook)
2738 	lStyle = DS_MODALFRAME | WS_CAPTION |DS_3DLOOK| WS_VISIBLE |DS_SETFONT;
2739     else
2740 	lStyle = DS_MODALFRAME | WS_CAPTION |DS_3DLOOK| WS_VISIBLE;
2741 
2742     add_long(lStyle);
2743     add_long(0);	// (lExtendedStyle)
2744     pnumitems = p;	/*save where the number of items must be stored*/
2745     add_word(0);	// NumberOfItems(will change later)
2746     add_word(10);	// x
2747     add_word(10);	// y
2748     add_word(PixelToDialogX(dlgwidth));	// cx
2749 
2750     // Dialog height.
2751     if (vertical)
2752 	dlgheight = msgheight + 2 * dlgPaddingY +
2753 			      DLG_VERT_PADDING_Y + 2 * fontHeight * numButtons;
2754     else
2755 	dlgheight = msgheight + 3 * dlgPaddingY + 2 * fontHeight;
2756 
2757     // Dialog needs to be taller if contains an edit box.
2758     editboxheight = fontHeight + dlgPaddingY + 4 * DLG_VERT_PADDING_Y;
2759     if (textfield != NULL)
2760 	dlgheight += editboxheight;
2761 
2762     add_word(PixelToDialogY(dlgheight));
2763 
2764     add_word(0);	// Menu
2765     add_word(0);	// Class
2766 
2767     /* copy the title of the dialog */
2768     nchar = nCopyAnsiToWideChar(p, (title ?
2769 				    (LPSTR)title :
2770 				    (LPSTR)("Vim "VIM_VERSION_MEDIUM)));
2771     p += nchar;
2772 
2773     if (s_usenewlook)
2774     {
2775 	/* do the font, since DS_3DLOOK doesn't work properly */
2776 #ifdef USE_SYSMENU_FONT
2777 	if (use_lfSysmenu)
2778 	{
2779 	    /* point size */
2780 	    *p++ = -MulDiv(lfSysmenu.lfHeight, 72,
2781 		    GetDeviceCaps(hdc, LOGPIXELSY));
2782 	    nchar = nCopyAnsiToWideChar(p, TEXT(lfSysmenu.lfFaceName));
2783 	}
2784 	else
2785 #endif
2786 	{
2787 	    *p++ = DLG_FONT_POINT_SIZE;		// point size
2788 	    nchar = nCopyAnsiToWideChar(p, TEXT(DLG_FONT_NAME));
2789 	}
2790 	p += nchar;
2791     }
2792 
2793     buttonYpos = msgheight + 2 * dlgPaddingY;
2794 
2795     if (textfield != NULL)
2796 	buttonYpos += editboxheight;
2797 
2798     pstart = tbuffer;
2799     if (!vertical)
2800 	horizWidth = (dlgwidth - horizWidth) / 2;	/* Now it's X offset */
2801     for (i = 0; i < numButtons; i++)
2802     {
2803 	/* get end of this button. */
2804 	for (	pend = pstart;
2805 		*pend && (*pend != DLG_BUTTON_SEP);
2806 		pend++)
2807 	    ;
2808 
2809 	if (*pend)
2810 	    *pend = '\0';
2811 
2812 	/*
2813 	 * old NOTE:
2814 	 * setting the BS_DEFPUSHBUTTON style doesn't work because Windows sets
2815 	 * the focus to the first tab-able button and in so doing makes that
2816 	 * the default!! Grrr.  Workaround: Make the default button the only
2817 	 * one with WS_TABSTOP style. Means user can't tab between buttons, but
2818 	 * he/she can use arrow keys.
2819 	 *
2820 	 * new NOTE: BS_DEFPUSHBUTTON is required to be able to select the
2821 	 * right buttun when hitting <Enter>.  E.g., for the ":confirm quit"
2822 	 * dialog.  Also needed for when the textfield is the default control.
2823 	 * It appears to work now (perhaps not on Win95?).
2824 	 */
2825 	if (vertical)
2826 	{
2827 	    p = add_dialog_element(p,
2828 		    (i == dfltbutton
2829 			    ? BS_DEFPUSHBUTTON : BS_PUSHBUTTON) | WS_TABSTOP,
2830 		    PixelToDialogX(DLG_VERT_PADDING_X),
2831 		    PixelToDialogY(buttonYpos /* TBK */
2832 				   + 2 * fontHeight * i),
2833 		    PixelToDialogX(dlgwidth - 2 * DLG_VERT_PADDING_X),
2834 		    (WORD)(PixelToDialogY(2 * fontHeight) - 1),
2835 		    (WORD)(IDCANCEL + 1 + i), (WORD)0x0080, pstart);
2836 	}
2837 	else
2838 	{
2839 	    p = add_dialog_element(p,
2840 		    (i == dfltbutton
2841 			    ? BS_DEFPUSHBUTTON : BS_PUSHBUTTON) | WS_TABSTOP,
2842 		    PixelToDialogX(horizWidth + buttonPositions[i]),
2843 		    PixelToDialogY(buttonYpos), /* TBK */
2844 		    PixelToDialogX(buttonWidths[i]),
2845 		    (WORD)(PixelToDialogY(2 * fontHeight) - 1),
2846 		    (WORD)(IDCANCEL + 1 + i), (WORD)0x0080, pstart);
2847 	}
2848 	pstart = pend + 1;	/*next button*/
2849     }
2850     *pnumitems += numButtons;
2851 
2852     /* Vim icon */
2853     p = add_dialog_element(p, SS_ICON,
2854 	    PixelToDialogX(dlgPaddingX),
2855 	    PixelToDialogY(dlgPaddingY),
2856 	    PixelToDialogX(DLG_ICON_WIDTH),
2857 	    PixelToDialogY(DLG_ICON_HEIGHT),
2858 	    DLG_NONBUTTON_CONTROL + 0, (WORD)0x0082,
2859 	    dlg_icons[type]);
2860 
2861     /* Dialog message */
2862     p = add_dialog_element(p, SS_LEFT,
2863 	    PixelToDialogX(2 * dlgPaddingX + DLG_ICON_WIDTH),
2864 	    PixelToDialogY(dlgPaddingY),
2865 	    (WORD)(PixelToDialogX(messageWidth) + 1),
2866 	    PixelToDialogY(msgheight),
2867 	    DLG_NONBUTTON_CONTROL + 1, (WORD)0x0082, message);
2868 
2869     /* Edit box */
2870     if (textfield != NULL)
2871     {
2872 	p = add_dialog_element(p, ES_LEFT|ES_AUTOHSCROLL|WS_TABSTOP|WS_BORDER,
2873 		PixelToDialogX(2 * dlgPaddingX),
2874 		PixelToDialogY(2 * dlgPaddingY + msgheight),
2875 		PixelToDialogX(dlgwidth - 4 * dlgPaddingX),
2876 		PixelToDialogY(fontHeight + dlgPaddingY),
2877 		DLG_NONBUTTON_CONTROL + 2, (WORD)0x0081, textfield);
2878 	*pnumitems += 1;
2879     }
2880 
2881     *pnumitems += 2;
2882 
2883     SelectFont(hdc, oldFont);
2884     DeleteObject(font);
2885     ReleaseDC(hwnd, hdc);
2886 
2887     /* Let the dialog_callback() function know which button to make default
2888      * If we have an edit box, make that the default. We also need to tell
2889      * dialog_callback() if this dialog contains an edit box or not. We do
2890      * this by setting s_textfield if it does.
2891      */
2892     if (textfield != NULL)
2893     {
2894 	dialog_default_button = DLG_NONBUTTON_CONTROL + 2;
2895 	s_textfield = textfield;
2896     }
2897     else
2898     {
2899 	dialog_default_button = IDCANCEL + 1 + dfltbutton;
2900 	s_textfield = NULL;
2901     }
2902 
2903     /* show the dialog box modally and get a return value */
2904     nchar = (int)DialogBoxIndirect(
2905 	    s_hinst,
2906 	    (LPDLGTEMPLATE)pdlgtemplate,
2907 	    s_hwnd,
2908 	    (DLGPROC)dialog_callback);
2909 
2910     LocalFree(LocalHandle(pdlgtemplate));
2911     vim_free(tbuffer);
2912     vim_free(buttonWidths);
2913     vim_free(buttonPositions);
2914 
2915     /* Focus back to our window (for when MDI is used). */
2916     (void)SetFocus(s_hwnd);
2917 
2918     return nchar;
2919 }
2920 
2921 #endif /* FEAT_GUI_DIALOG */
2922 /*
2923  * Put a simple element (basic class) onto a dialog template in memory.
2924  * return a pointer to where the next item should be added.
2925  *
2926  * parameters:
2927  *  lStyle = additional style flags
2928  *		(be careful, NT3.51 & Win32s will ignore the new ones)
2929  *  x,y = x & y positions IN DIALOG UNITS
2930  *  w,h = width and height IN DIALOG UNITS
2931  *  Id  = ID used in messages
2932  *  clss  = class ID, e.g 0x0080 for a button, 0x0082 for a static
2933  *  caption = usually text or resource name
2934  *
2935  *  TODO: use the length information noted here to enable the dialog creation
2936  *  routines to work out more exactly how much memory they need to alloc.
2937  */
2938     static PWORD
2939 add_dialog_element(
2940     PWORD p,
2941     DWORD lStyle,
2942     WORD x,
2943     WORD y,
2944     WORD w,
2945     WORD h,
2946     WORD Id,
2947     WORD clss,
2948     const char *caption)
2949 {
2950     int nchar;
2951 
2952     p = lpwAlign(p);	/* Align to dword boundary*/
2953     lStyle = lStyle | WS_VISIBLE | WS_CHILD;
2954     *p++ = LOWORD(lStyle);
2955     *p++ = HIWORD(lStyle);
2956     *p++ = 0;		// LOWORD (lExtendedStyle)
2957     *p++ = 0;		// HIWORD (lExtendedStyle)
2958     *p++ = x;
2959     *p++ = y;
2960     *p++ = w;
2961     *p++ = h;
2962     *p++ = Id;		//9 or 10 words in all
2963 
2964     *p++ = (WORD)0xffff;
2965     *p++ = clss;			//2 more here
2966 
2967     nchar = nCopyAnsiToWideChar(p, (LPSTR)caption); //strlen(caption)+1
2968     p += nchar;
2969 
2970     *p++ = 0;  // advance pointer over nExtraStuff WORD   - 2 more
2971 
2972     return p;	//total = 15+ (strlen(caption)) words
2973 		//	   = 30 + 2(strlen(caption) bytes reqd
2974 }
2975 
2976 
2977 /*
2978  * Helper routine.  Take an input pointer, return closest pointer that is
2979  * aligned on a DWORD (4 byte) boundary.  Taken from the Win32SDK samples.
2980  */
2981     static LPWORD
2982 lpwAlign(
2983     LPWORD lpIn)
2984 {
2985     ULONG ul;
2986 
2987     ul = (ULONG)lpIn;
2988     ul += 3;
2989     ul >>= 2;
2990     ul <<= 2;
2991     return (LPWORD)ul;
2992 }
2993 
2994 /*
2995  * Helper routine.  Takes second parameter as Ansi string, copies it to first
2996  * parameter as wide character (16-bits / char) string, and returns integer
2997  * number of wide characters (words) in string (including the trailing wide
2998  * char NULL).  Partly taken from the Win32SDK samples.
2999  */
3000     static int
3001 nCopyAnsiToWideChar(
3002     LPWORD lpWCStr,
3003     LPSTR lpAnsiIn)
3004 {
3005     int		nChar = 0;
3006 #ifdef FEAT_MBYTE
3007     int		len = lstrlen(lpAnsiIn) + 1;	/* include NUL character */
3008     int		i;
3009     WCHAR	*wn;
3010 
3011     if (enc_codepage == 0 && (int)GetACP() != enc_codepage)
3012     {
3013 	/* Not a codepage, use our own conversion function. */
3014 	wn = enc_to_ucs2(lpAnsiIn, NULL);
3015 	if (wn != NULL)
3016 	{
3017 	    wcscpy(lpWCStr, wn);
3018 	    nChar = wcslen(wn) + 1;
3019 	    vim_free(wn);
3020 	}
3021     }
3022     if (nChar == 0)
3023 	/* Use Win32 conversion function. */
3024 	nChar = MultiByteToWideChar(
3025 		enc_codepage > 0 ? enc_codepage : CP_ACP,
3026 		MB_PRECOMPOSED,
3027 		lpAnsiIn, len,
3028 		lpWCStr, len);
3029     for (i = 0; i < nChar; ++i)
3030 	if (lpWCStr[i] == (WORD)'\t')	/* replace tabs with spaces */
3031 	    lpWCStr[i] = (WORD)' ';
3032 #else
3033     do
3034     {
3035 	if (*lpAnsiIn == '\t')
3036 	    *lpWCStr++ = (WORD)' ';
3037 	else
3038 	    *lpWCStr++ = (WORD)*lpAnsiIn;
3039 	nChar++;
3040     } while (*lpAnsiIn++);
3041 #endif
3042 
3043     return nChar;
3044 }
3045 
3046 
3047 #ifdef FEAT_TEAROFF
3048 /*
3049  * The callback function for all the modeless dialogs that make up the
3050  * "tearoff menus" Very simple - forward button presses (to fool Vim into
3051  * thinking its menus have been clicked), and go away when closed.
3052  */
3053     static LRESULT CALLBACK
3054 tearoff_callback(
3055     HWND hwnd,
3056     UINT message,
3057     WPARAM wParam,
3058     LPARAM lParam)
3059 {
3060     if (message == WM_INITDIALOG)
3061 	return (TRUE);
3062 
3063     /* May show the mouse pointer again. */
3064     HandleMouseHide(message, lParam);
3065 
3066     if (message == WM_COMMAND)
3067     {
3068 	if ((WORD)(LOWORD(wParam)) & 0x8000)
3069 	{
3070 	    POINT   mp;
3071 	    RECT    rect;
3072 
3073 	    if (GetCursorPos(&mp) && GetWindowRect(hwnd, &rect))
3074 	    {
3075 		(void)TrackPopupMenu(
3076 			 (HMENU)(LOWORD(wParam) ^ 0x8000),
3077 			 TPM_LEFTALIGN | TPM_LEFTBUTTON,
3078 			 (int)rect.right - 8,
3079 			 (int)mp.y,
3080 			 (int)0,	    /*reserved param*/
3081 			 s_hwnd,
3082 			 NULL);
3083 		/*
3084 		 * NOTE: The pop-up menu can eat the mouse up event.
3085 		 * We deal with this in normal.c.
3086 		 */
3087 	    }
3088 	}
3089 	else
3090 	    /* Pass on messages to the main Vim window */
3091 	    PostMessage(s_hwnd, WM_COMMAND, LOWORD(wParam), 0);
3092 	/*
3093 	 * Give main window the focus back: this is so after
3094 	 * choosing a tearoff button you can start typing again
3095 	 * straight away.
3096 	 */
3097 	(void)SetFocus(s_hwnd);
3098 	return TRUE;
3099     }
3100     if ((message == WM_SYSCOMMAND) && (wParam == SC_CLOSE))
3101     {
3102 	DestroyWindow(hwnd);
3103 	return TRUE;
3104     }
3105 
3106     /* When moved around, give main window the focus back. */
3107     if (message == WM_EXITSIZEMOVE)
3108 	(void)SetActiveWindow(s_hwnd);
3109 
3110     return FALSE;
3111 }
3112 #endif
3113 
3114 
3115 /*
3116  * Decide whether to use the "new look" (small, non-bold font) or the "old
3117  * look" (big, clanky font) for dialogs, and work out a few values for use
3118  * later accordingly.
3119  */
3120     static void
3121 get_dialog_font_metrics(void)
3122 {
3123     HDC		    hdc;
3124     HFONT	    hfontTools = 0;
3125     DWORD	    dlgFontSize;
3126     SIZE	    size;
3127 #ifdef USE_SYSMENU_FONT
3128     LOGFONT	    lfSysmenu;
3129 #endif
3130 
3131     s_usenewlook = FALSE;
3132 
3133     /*
3134      * For NT3.51 and Win32s, we stick with the old look
3135      * because it matches everything else.
3136      */
3137     if (!is_winnt_3())
3138     {
3139 #ifdef USE_SYSMENU_FONT
3140 	if (gui_w32_get_menu_font(&lfSysmenu) == OK)
3141 	    hfontTools = CreateFontIndirect(&lfSysmenu);
3142 	else
3143 #endif
3144 	hfontTools = CreateFont(-DLG_FONT_POINT_SIZE, 0, 0, 0, 0, 0, 0, 0,
3145 				0, 0, 0, 0, VARIABLE_PITCH , DLG_FONT_NAME);
3146 
3147 	if (hfontTools)
3148 	{
3149 	    hdc = GetDC(s_hwnd);
3150 	    SelectObject(hdc, hfontTools);
3151 	    /*
3152 	     * GetTextMetrics() doesn't return the right value in
3153 	     * tmAveCharWidth, so we have to figure out the dialog base units
3154 	     * ourselves.
3155 	     */
3156 	    GetTextExtentPoint(hdc,
3157 		    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
3158 		    52, &size);
3159 	    ReleaseDC(s_hwnd, hdc);
3160 
3161 	    s_dlgfntwidth = (WORD)((size.cx / 26 + 1) / 2);
3162 	    s_dlgfntheight = (WORD)size.cy;
3163 	    s_usenewlook = TRUE;
3164 	}
3165     }
3166 
3167     if (!s_usenewlook)
3168     {
3169 	dlgFontSize = GetDialogBaseUnits();	/* fall back to big old system*/
3170 	s_dlgfntwidth = LOWORD(dlgFontSize);
3171 	s_dlgfntheight = HIWORD(dlgFontSize);
3172     }
3173 }
3174 
3175 #if defined(FEAT_MENU) && defined(FEAT_TEAROFF)
3176 /*
3177  * Create a pseudo-"tearoff menu" based on the child
3178  * items of a given menu pointer.
3179  */
3180     static void
3181 gui_mch_tearoff(
3182     char_u	*title,
3183     vimmenu_T	*menu,
3184     int		initX,
3185     int		initY)
3186 {
3187     WORD	*p, *pdlgtemplate, *pnumitems, *ptrueheight;
3188     int		template_len;
3189     int		nchar, textWidth, submenuWidth;
3190     DWORD	lStyle;
3191     DWORD	lExtendedStyle;
3192     WORD	dlgwidth;
3193     WORD	menuID;
3194     vimmenu_T	*pmenu;
3195     vimmenu_T	*the_menu = menu;
3196     HWND	hwnd;
3197     HDC		hdc;
3198     HFONT	font, oldFont;
3199     int		col, spaceWidth, len;
3200     int		columnWidths[2];
3201     char_u	*label, *text;
3202     int		acLen = 0;
3203     int		nameLen;
3204     int		padding0, padding1, padding2 = 0;
3205     int		sepPadding=0;
3206 #ifdef USE_SYSMENU_FONT
3207     LOGFONT	lfSysmenu;
3208     int		use_lfSysmenu = FALSE;
3209 #endif
3210 
3211     /*
3212      * If this menu is already torn off, move it to the mouse position.
3213      */
3214     if (IsWindow(menu->tearoff_handle))
3215     {
3216 	POINT mp;
3217 	if (GetCursorPos((LPPOINT)&mp))
3218 	{
3219 	    SetWindowPos(menu->tearoff_handle, NULL, mp.x, mp.y, 0, 0,
3220 		    SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
3221 	}
3222 	return;
3223     }
3224 
3225     /*
3226      * Create a new tearoff.
3227      */
3228     if (*title == MNU_HIDDEN_CHAR)
3229 	title++;
3230 
3231     /* Allocate memory to store the dialog template.  It's made bigger when
3232      * needed. */
3233     template_len = DLG_ALLOC_SIZE;
3234     pdlgtemplate = p = (WORD *)LocalAlloc(LPTR, template_len);
3235     if (p == NULL)
3236 	return;
3237 
3238     hwnd = GetDesktopWindow();
3239     hdc = GetWindowDC(hwnd);
3240 #ifdef USE_SYSMENU_FONT
3241     if (gui_w32_get_menu_font(&lfSysmenu) == OK)
3242     {
3243 	font = CreateFontIndirect(&lfSysmenu);
3244 	use_lfSysmenu = TRUE;
3245     }
3246     else
3247 #endif
3248     font = CreateFont(-DLG_FONT_POINT_SIZE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3249 		      VARIABLE_PITCH , DLG_FONT_NAME);
3250     if (s_usenewlook)
3251 	oldFont = SelectFont(hdc, font);
3252     else
3253 	oldFont = SelectFont(hdc, GetStockObject(SYSTEM_FONT));
3254 
3255     /* Calculate width of a single space.  Used for padding columns to the
3256      * right width. */
3257     spaceWidth = GetTextWidth(hdc, " ", 1);
3258 
3259     /* Figure out max width of the text column, the accelerator column and the
3260      * optional submenu column. */
3261     submenuWidth = 0;
3262     for (col = 0; col < 2; col++)
3263     {
3264 	columnWidths[col] = 0;
3265 	for (pmenu = menu->children; pmenu != NULL; pmenu = pmenu->next)
3266 	{
3267 	    /* Use "dname" here to compute the width of the visible text. */
3268 	    text = (col == 0) ? pmenu->dname : pmenu->actext;
3269 	    if (text != NULL && *text != NUL)
3270 	    {
3271 		textWidth = GetTextWidthEnc(hdc, text, (int)STRLEN(text));
3272 		if (textWidth > columnWidths[col])
3273 		    columnWidths[col] = textWidth;
3274 	    }
3275 	    if (pmenu->children != NULL)
3276 		submenuWidth = TEAROFF_COLUMN_PADDING * spaceWidth;
3277 	}
3278     }
3279     if (columnWidths[1] == 0)
3280     {
3281 	/* no accelerators */
3282 	if (submenuWidth != 0)
3283 	    columnWidths[0] += submenuWidth;
3284 	else
3285 	    columnWidths[0] += spaceWidth;
3286     }
3287     else
3288     {
3289 	/* there is an accelerator column */
3290 	columnWidths[0] += TEAROFF_COLUMN_PADDING * spaceWidth;
3291 	columnWidths[1] += submenuWidth;
3292     }
3293 
3294     /*
3295      * Now find the total width of our 'menu'.
3296      */
3297     textWidth = columnWidths[0] + columnWidths[1];
3298     if (submenuWidth != 0)
3299     {
3300 	submenuWidth = GetTextWidth(hdc, TEAROFF_SUBMENU_LABEL,
3301 					  (int)STRLEN(TEAROFF_SUBMENU_LABEL));
3302 	textWidth += submenuWidth;
3303     }
3304     dlgwidth = GetTextWidthEnc(hdc, title, (int)STRLEN(title));
3305     if (textWidth > dlgwidth)
3306 	dlgwidth = textWidth;
3307     dlgwidth += 2 * TEAROFF_PADDING_X + TEAROFF_BUTTON_PAD_X;
3308 
3309     /* W95 can't do thin dialogs, they look v. weird! */
3310     if (mch_windows95() && dlgwidth < TEAROFF_MIN_WIDTH)
3311 	dlgwidth = TEAROFF_MIN_WIDTH;
3312 
3313     /* start to fill in the dlgtemplate information.  addressing by WORDs */
3314     if (s_usenewlook)
3315 	lStyle = DS_MODALFRAME | WS_CAPTION| WS_SYSMENU |DS_SETFONT| WS_VISIBLE;
3316     else
3317 	lStyle = DS_MODALFRAME | WS_CAPTION| WS_SYSMENU | WS_VISIBLE;
3318 
3319     lExtendedStyle = WS_EX_TOOLWINDOW|WS_EX_STATICEDGE;
3320     *p++ = LOWORD(lStyle);
3321     *p++ = HIWORD(lStyle);
3322     *p++ = LOWORD(lExtendedStyle);
3323     *p++ = HIWORD(lExtendedStyle);
3324     pnumitems = p;	/* save where the number of items must be stored */
3325     *p++ = 0;		// NumberOfItems(will change later)
3326     if (initX == 0xffffL)
3327 	*p++ = PixelToDialogX(gui_mch_get_mouse_x()); // x
3328     else
3329 	*p++ = PixelToDialogX(initX); // x
3330     if (initY == 0xffffL)
3331 	*p++ = PixelToDialogY(gui_mch_get_mouse_y()); // y
3332     else
3333 	*p++ = PixelToDialogY(initY); // y
3334     *p++ = PixelToDialogX(dlgwidth);    // cx
3335     ptrueheight = p;
3336     *p++ = 0;		// dialog height: changed later anyway
3337     *p++ = 0;		// Menu
3338     *p++ = 0;		// Class
3339 
3340     /* copy the title of the dialog */
3341     nchar = nCopyAnsiToWideChar(p, ((*title)
3342 				    ? (LPSTR)title
3343 				    : (LPSTR)("Vim "VIM_VERSION_MEDIUM)));
3344     p += nchar;
3345 
3346     if (s_usenewlook)
3347     {
3348 	/* do the font, since DS_3DLOOK doesn't work properly */
3349 #ifdef USE_SYSMENU_FONT
3350 	if (use_lfSysmenu)
3351 	{
3352 	    /* point size */
3353 	    *p++ = -MulDiv(lfSysmenu.lfHeight, 72,
3354 		    GetDeviceCaps(hdc, LOGPIXELSY));
3355 	    nchar = nCopyAnsiToWideChar(p, TEXT(lfSysmenu.lfFaceName));
3356 	}
3357 	else
3358 #endif
3359 	{
3360 	    *p++ = DLG_FONT_POINT_SIZE;		// point size
3361 	    nchar = nCopyAnsiToWideChar (p, TEXT(DLG_FONT_NAME));
3362 	}
3363 	p += nchar;
3364     }
3365 
3366     /*
3367      * Loop over all the items in the menu.
3368      * But skip over the tearbar.
3369      */
3370     if (STRCMP(menu->children->name, TEAR_STRING) == 0)
3371 	menu = menu->children->next;
3372     else
3373 	menu = menu->children;
3374     for ( ; menu != NULL; menu = menu->next)
3375     {
3376 	if (menu->modes == 0)	/* this menu has just been deleted */
3377 	    continue;
3378 	if (menu_is_separator(menu->dname))
3379 	{
3380 	    sepPadding += 3;
3381 	    continue;
3382 	}
3383 
3384 	/* Check if there still is plenty of room in the template.  Make it
3385 	 * larger when needed. */
3386 	if (((char *)p - (char *)pdlgtemplate) + 1000 > template_len)
3387 	{
3388 	    WORD    *newp;
3389 
3390 	    newp = (WORD *)LocalAlloc(LPTR, template_len + 4096);
3391 	    if (newp != NULL)
3392 	    {
3393 		template_len += 4096;
3394 		mch_memmove(newp, pdlgtemplate,
3395 					    (char *)p - (char *)pdlgtemplate);
3396 		p = newp + (p - pdlgtemplate);
3397 		pnumitems = newp + (pnumitems - pdlgtemplate);
3398 		ptrueheight = newp + (ptrueheight - pdlgtemplate);
3399 		LocalFree(LocalHandle(pdlgtemplate));
3400 		pdlgtemplate = newp;
3401 	    }
3402 	}
3403 
3404 	/* Figure out minimal length of this menu label.  Use "name" for the
3405 	 * actual text, "dname" for estimating the displayed size.  "name"
3406 	 * has "&a" for mnemonic and includes the accelerator. */
3407 	len = nameLen = (int)STRLEN(menu->name);
3408 	padding0 = (columnWidths[0] - GetTextWidthEnc(hdc, menu->dname,
3409 				      (int)STRLEN(menu->dname))) / spaceWidth;
3410 	len += padding0;
3411 
3412 	if (menu->actext != NULL)
3413 	{
3414 	    acLen = (int)STRLEN(menu->actext);
3415 	    len += acLen;
3416 	    textWidth = GetTextWidthEnc(hdc, menu->actext, acLen);
3417 	}
3418 	else
3419 	    textWidth = 0;
3420 	padding1 = (columnWidths[1] - textWidth) / spaceWidth;
3421 	len += padding1;
3422 
3423 	if (menu->children == NULL)
3424 	{
3425 	    padding2 = submenuWidth / spaceWidth;
3426 	    len += padding2;
3427 	    menuID = (WORD)(menu->id);
3428 	}
3429 	else
3430 	{
3431 	    len += (int)STRLEN(TEAROFF_SUBMENU_LABEL);
3432 	    menuID = (WORD)((DWORD)(menu->submenu_id) | (DWORD)0x8000);
3433 	}
3434 
3435 	/* Allocate menu label and fill it in */
3436 	text = label = alloc((unsigned)len + 1);
3437 	if (label == NULL)
3438 	    break;
3439 
3440 	STRNCPY(text, menu->name, nameLen);
3441 	text[nameLen] = NUL;
3442 	text = vim_strchr(text, TAB);	    /* stop at TAB before actext */
3443 	if (text == NULL)
3444 	    text = label + nameLen;	    /* no actext, use whole name */
3445 	while (padding0-- > 0)
3446 	    *text++ = ' ';
3447 	if (menu->actext != NULL)
3448 	{
3449 	    STRNCPY(text, menu->actext, acLen);
3450 	    text += acLen;
3451 	}
3452 	while (padding1-- > 0)
3453 	    *text++ = ' ';
3454 	if (menu->children != NULL)
3455 	{
3456 	    STRCPY(text, TEAROFF_SUBMENU_LABEL);
3457 	    text += STRLEN(TEAROFF_SUBMENU_LABEL);
3458 	}
3459 	else
3460 	{
3461 	    while (padding2-- > 0)
3462 		*text++ = ' ';
3463 	}
3464 	*text = NUL;
3465 
3466 	/*
3467 	 * BS_LEFT will just be ignored on Win32s/NT3.5x - on
3468 	 * W95/NT4 it makes the tear-off look more like a menu.
3469 	 */
3470 	p = add_dialog_element(p,
3471 		BS_PUSHBUTTON|BS_LEFT,
3472 		(WORD)PixelToDialogX(TEAROFF_PADDING_X),
3473 		(WORD)(sepPadding + 1 + 13 * (*pnumitems)),
3474 		(WORD)PixelToDialogX(dlgwidth - 2 * TEAROFF_PADDING_X),
3475 		(WORD)12,
3476 		menuID, (WORD)0x0080, label);
3477 	vim_free(label);
3478 	(*pnumitems)++;
3479     }
3480 
3481     *ptrueheight = (WORD)(sepPadding + 1 + 13 * (*pnumitems));
3482 
3483 
3484     /* show modelessly */
3485     the_menu->tearoff_handle = CreateDialogIndirect(
3486 	    s_hinst,
3487 	    (LPDLGTEMPLATE)pdlgtemplate,
3488 	    s_hwnd,
3489 	    (DLGPROC)tearoff_callback);
3490 
3491     LocalFree(LocalHandle(pdlgtemplate));
3492     SelectFont(hdc, oldFont);
3493     DeleteObject(font);
3494     ReleaseDC(hwnd, hdc);
3495 
3496     /*
3497      * Reassert ourselves as the active window.  This is so that after creating
3498      * a tearoff, the user doesn't have to click with the mouse just to start
3499      * typing again!
3500      */
3501     (void)SetActiveWindow(s_hwnd);
3502 
3503     /* make sure the right buttons are enabled */
3504     force_menu_update = TRUE;
3505 }
3506 #endif
3507 
3508 #if defined(FEAT_TOOLBAR) || defined(PROTO)
3509 #include "gui_w32_rc.h"
3510 
3511 /* This not defined in older SDKs */
3512 # ifndef TBSTYLE_FLAT
3513 #  define TBSTYLE_FLAT		0x0800
3514 # endif
3515 
3516 /*
3517  * Create the toolbar, initially unpopulated.
3518  *  (just like the menu, there are no defaults, it's all
3519  *  set up through menu.vim)
3520  */
3521     static void
3522 initialise_toolbar(void)
3523 {
3524     InitCommonControls();
3525     s_toolbarhwnd = CreateToolbarEx(
3526 		    s_hwnd,
3527 		    WS_CHILD | TBSTYLE_TOOLTIPS | TBSTYLE_FLAT,
3528 		    4000,		//any old big number
3529 		    31,			//number of images in inital bitmap
3530 		    s_hinst,
3531 		    IDR_TOOLBAR1,	// id of initial bitmap
3532 		    NULL,
3533 		    0,			// initial number of buttons
3534 		    TOOLBAR_BUTTON_WIDTH, //api guide is wrong!
3535 		    TOOLBAR_BUTTON_HEIGHT,
3536 		    TOOLBAR_BUTTON_WIDTH,
3537 		    TOOLBAR_BUTTON_HEIGHT,
3538 		    sizeof(TBBUTTON)
3539 		    );
3540 
3541     gui_mch_show_toolbar(vim_strchr(p_go, GO_TOOLBAR) != NULL);
3542 }
3543 
3544     static int
3545 get_toolbar_bitmap(vimmenu_T *menu)
3546 {
3547     int i = -1;
3548 
3549     /*
3550      * Check user bitmaps first, unless builtin is specified.
3551      */
3552     if (!is_winnt_3() && !menu->icon_builtin)
3553     {
3554 	char_u fname[MAXPATHL];
3555 	HANDLE hbitmap = NULL;
3556 
3557 	if (menu->iconfile != NULL)
3558 	{
3559 	    gui_find_iconfile(menu->iconfile, fname, "bmp");
3560 	    hbitmap = LoadImage(
3561 			NULL,
3562 			fname,
3563 			IMAGE_BITMAP,
3564 			TOOLBAR_BUTTON_WIDTH,
3565 			TOOLBAR_BUTTON_HEIGHT,
3566 			LR_LOADFROMFILE |
3567 			LR_LOADMAP3DCOLORS
3568 			);
3569 	}
3570 
3571 	/*
3572 	 * If the LoadImage call failed, or the "icon=" file
3573 	 * didn't exist or wasn't specified, try the menu name
3574 	 */
3575 	if (hbitmap == NULL
3576 		&& (gui_find_bitmap(menu->name, fname, "bmp") == OK))
3577 	    hbitmap = LoadImage(
3578 		    NULL,
3579 		    fname,
3580 		    IMAGE_BITMAP,
3581 		    TOOLBAR_BUTTON_WIDTH,
3582 		    TOOLBAR_BUTTON_HEIGHT,
3583 		    LR_LOADFROMFILE |
3584 		    LR_LOADMAP3DCOLORS
3585 		);
3586 
3587 	if (hbitmap != NULL)
3588 	{
3589 	    TBADDBITMAP tbAddBitmap;
3590 
3591 	    tbAddBitmap.hInst = NULL;
3592 	    tbAddBitmap.nID = (UINT)hbitmap;
3593 
3594 	    i = (int)SendMessage(s_toolbarhwnd, TB_ADDBITMAP,
3595 			    (WPARAM)1, (LPARAM)&tbAddBitmap);
3596 	    /* i will be set to -1 if it fails */
3597 	}
3598     }
3599     if (i == -1 && menu->iconidx >= 0 && menu->iconidx < TOOLBAR_BITMAP_COUNT)
3600 	i = menu->iconidx;
3601 
3602     return i;
3603 }
3604 #endif
3605 
3606 #if defined(FEAT_OLE) || defined(FEAT_EVAL) || defined(PROTO)
3607 /*
3608  * Make the GUI window come to the foreground.
3609  */
3610     void
3611 gui_mch_set_foreground(void)
3612 {
3613     if (IsIconic(s_hwnd))
3614 	 SendMessage(s_hwnd, WM_SYSCOMMAND, SC_RESTORE, 0);
3615     SetForegroundWindow(s_hwnd);
3616 }
3617 #endif
3618 
3619 #if defined(FEAT_MBYTE_IME) && defined(DYNAMIC_IME)
3620     static void
3621 dyn_imm_load(void)
3622 {
3623     hLibImm = LoadLibrary("imm32.dll");
3624     if (hLibImm == NULL)
3625 	return;
3626 
3627     pImmGetCompositionStringA
3628 	    = (void *)GetProcAddress(hLibImm, "ImmGetCompositionStringA");
3629     pImmGetCompositionStringW
3630 	    = (void *)GetProcAddress(hLibImm, "ImmGetCompositionStringW");
3631     pImmGetContext
3632 	    = (void *)GetProcAddress(hLibImm, "ImmGetContext");
3633     pImmAssociateContext
3634 	    = (void *)GetProcAddress(hLibImm, "ImmAssociateContext");
3635     pImmReleaseContext
3636 	    = (void *)GetProcAddress(hLibImm, "ImmReleaseContext");
3637     pImmGetOpenStatus
3638 	    = (void *)GetProcAddress(hLibImm, "ImmGetOpenStatus");
3639     pImmSetOpenStatus
3640 	    = (void *)GetProcAddress(hLibImm, "ImmSetOpenStatus");
3641     pImmGetCompositionFont
3642 	    = (void *)GetProcAddress(hLibImm, "ImmGetCompositionFontA");
3643     pImmSetCompositionFont
3644 	    = (void *)GetProcAddress(hLibImm, "ImmSetCompositionFontA");
3645     pImmSetCompositionWindow
3646 	    = (void *)GetProcAddress(hLibImm, "ImmSetCompositionWindow");
3647     pImmGetConversionStatus
3648 	    = (void *)GetProcAddress(hLibImm, "ImmGetConversionStatus");
3649 
3650     if (       pImmGetCompositionStringA == NULL
3651 	    || pImmGetCompositionStringW == NULL
3652 	    || pImmGetContext == NULL
3653 	    || pImmAssociateContext == NULL
3654 	    || pImmReleaseContext == NULL
3655 	    || pImmGetOpenStatus == NULL
3656 	    || pImmSetOpenStatus == NULL
3657 	    || pImmGetCompositionFont == NULL
3658 	    || pImmSetCompositionFont == NULL
3659 	    || pImmSetCompositionWindow == NULL
3660 	    || pImmGetConversionStatus == NULL)
3661     {
3662 	FreeLibrary(hLibImm);
3663 	hLibImm = NULL;
3664 	pImmGetContext = NULL;
3665 	return;
3666     }
3667 
3668     return;
3669 }
3670 
3671 # if 0	/* not used */
3672     int
3673 dyn_imm_unload(void)
3674 {
3675     if (!hLibImm)
3676 	return FALSE;
3677     FreeLibrary(hLibImm);
3678     hLibImm = NULL;
3679     return TRUE;
3680 }
3681 # endif
3682 
3683 #endif
3684 
3685 #if defined(FEAT_SIGN_ICONS) || defined(PROTO)
3686 
3687 # ifdef FEAT_XPM_W32
3688 #  define IMAGE_XPM   100
3689 # endif
3690 
3691 typedef struct _signicon_t
3692 {
3693     HANDLE	hImage;
3694     UINT	uType;
3695 #ifdef FEAT_XPM_W32
3696     HANDLE	hShape;	/* Mask bitmap handle */
3697 #endif
3698 } signicon_t;
3699 
3700     void
3701 gui_mch_drawsign(row, col, typenr)
3702     int		row;
3703     int		col;
3704     int		typenr;
3705 {
3706     signicon_t *sign;
3707     int x, y, w, h;
3708 
3709     if (!gui.in_use || (sign = (signicon_t *)sign_get_image(typenr)) == NULL)
3710 	return;
3711 
3712     x = TEXT_X(col);
3713     y = TEXT_Y(row);
3714     w = gui.char_width * 2;
3715     h = gui.char_height;
3716     switch (sign->uType)
3717     {
3718 	case IMAGE_BITMAP:
3719 	    {
3720 		HDC hdcMem;
3721 		HBITMAP hbmpOld;
3722 
3723 		hdcMem = CreateCompatibleDC(s_hdc);
3724 		hbmpOld = (HBITMAP)SelectObject(hdcMem, sign->hImage);
3725 		BitBlt(s_hdc, x, y, w, h, hdcMem, 0, 0, SRCCOPY);
3726 		SelectObject(hdcMem, hbmpOld);
3727 		DeleteDC(hdcMem);
3728 	    }
3729 	    break;
3730 	case IMAGE_ICON:
3731 	case IMAGE_CURSOR:
3732 	    DrawIconEx(s_hdc, x, y, (HICON)sign->hImage, w, h, 0, NULL, DI_NORMAL);
3733 	    break;
3734 #ifdef FEAT_XPM_W32
3735 	case IMAGE_XPM:
3736 	    {
3737 		HDC hdcMem;
3738 		HBITMAP hbmpOld;
3739 
3740 		hdcMem = CreateCompatibleDC(s_hdc);
3741 		hbmpOld = (HBITMAP)SelectObject(hdcMem, sign->hShape);
3742 		/* Make hole */
3743 		BitBlt(s_hdc, x, y, w, h, hdcMem, 0, 0, SRCAND);
3744 
3745 		SelectObject(hdcMem, sign->hImage);
3746 		/* Paint sign */
3747 		BitBlt(s_hdc, x, y, w, h, hdcMem, 0, 0, SRCPAINT);
3748 		SelectObject(hdcMem, hbmpOld);
3749 		DeleteDC(hdcMem);
3750 	    }
3751 	    break;
3752 #endif
3753     }
3754 }
3755 
3756     static void
3757 close_signicon_image(signicon_t *sign)
3758 {
3759     if (sign)
3760 	switch (sign->uType)
3761 	{
3762 	    case IMAGE_BITMAP:
3763 		DeleteObject((HGDIOBJ)sign->hImage);
3764 		break;
3765 	    case IMAGE_CURSOR:
3766 		DestroyCursor((HCURSOR)sign->hImage);
3767 		break;
3768 	    case IMAGE_ICON:
3769 		DestroyIcon((HICON)sign->hImage);
3770 		break;
3771 #ifdef FEAT_XPM_W32
3772 	    case IMAGE_XPM:
3773 		DeleteObject((HBITMAP)sign->hImage);
3774 		DeleteObject((HBITMAP)sign->hShape);
3775 		break;
3776 #endif
3777 	}
3778 }
3779 
3780     void *
3781 gui_mch_register_sign(signfile)
3782     char_u	*signfile;
3783 {
3784     signicon_t	sign, *psign;
3785     char_u	*ext;
3786 
3787     if (is_winnt_3())
3788     {
3789 	EMSG(_(e_signdata));
3790 	return NULL;
3791     }
3792 
3793     sign.hImage = NULL;
3794     ext = signfile + STRLEN(signfile) - 4; /* get extention */
3795     if (ext > signfile)
3796     {
3797 	int do_load = 1;
3798 
3799 	if (!STRICMP(ext, ".bmp"))
3800 	    sign.uType =  IMAGE_BITMAP;
3801 	else if (!STRICMP(ext, ".ico"))
3802 	    sign.uType =  IMAGE_ICON;
3803 	else if (!STRICMP(ext, ".cur") || !STRICMP(ext, ".ani"))
3804 	    sign.uType =  IMAGE_CURSOR;
3805 	else
3806 	    do_load = 0;
3807 
3808 	if (do_load)
3809 	    sign.hImage = (HANDLE)LoadImage(NULL, signfile, sign.uType,
3810 		    gui.char_width * 2, gui.char_height,
3811 		    LR_LOADFROMFILE | LR_CREATEDIBSECTION);
3812 #ifdef FEAT_XPM_W32
3813 	if (!STRICMP(ext, ".xpm"))
3814 	{
3815 	    sign.uType = IMAGE_XPM;
3816 	    LoadXpmImage(signfile, (HBITMAP *)&sign.hImage, (HBITMAP *)&sign.hShape);
3817 	}
3818 #endif
3819     }
3820 
3821     psign = NULL;
3822     if (sign.hImage && (psign = (signicon_t *)alloc(sizeof(signicon_t)))
3823 								      != NULL)
3824 	*psign = sign;
3825 
3826     if (!psign)
3827     {
3828 	if (sign.hImage)
3829 	    close_signicon_image(&sign);
3830 	EMSG(_(e_signdata));
3831     }
3832     return (void *)psign;
3833 
3834 }
3835 
3836     void
3837 gui_mch_destroy_sign(sign)
3838     void *sign;
3839 {
3840     if (sign)
3841     {
3842 	close_signicon_image((signicon_t *)sign);
3843 	vim_free(sign);
3844     }
3845 }
3846 
3847 #if defined(FEAT_BEVAL) || defined(PROTO)
3848 
3849 /* BALLOON-EVAL IMPLEMENTATION FOR WINDOWS.
3850  *  Added by Sergey Khorev
3851  *
3852  * The only reused thing is gui_beval.h and gui_mch_get_beval_info()
3853  * from gui_beval.c (note it uses x and y of the BalloonEval struct
3854  * to get current mouse position).
3855  *
3856  * Trying to use as more Windows services as possible, and as less
3857  * IE version as possible :)).
3858  *
3859  * 1) Don't create ToolTip in gui_mch_create_beval_area, only initialize
3860  * BalloonEval struct.
3861  * 2) Enable/Disable simply create/kill BalloonEval Timer
3862  * 3) When there was enough inactivity, timer procedure posts
3863  * async request to debugger
3864  * 4) gui_mch_post_balloon (invoked from netbeans.c) creates tooltip control
3865  * and performs some actions to show it ASAP
3866  * 5) WM_NOTOFY:TTN_POP destroys created tooltip
3867  */
3868 
3869     static void
3870 make_tooltip(beval, text, pt)
3871     BalloonEval *beval;
3872     char *text;
3873     POINT pt;
3874 {
3875     TOOLINFO	ti;
3876 
3877     beval->balloon = CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS,
3878 	    NULL, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,
3879 	    CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
3880 	    beval->target, NULL, s_hinst, NULL);
3881 
3882     SetWindowPos(beval->balloon, HWND_TOPMOST, 0, 0, 0, 0,
3883 	    SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
3884 
3885     ti.cbSize = sizeof(TOOLINFO);
3886     ti.uFlags = TTF_SUBCLASS;
3887     ti.hwnd = beval->target;
3888     ti.hinst = 0; /* Don't use string resources */
3889     ti.uId = ID_BEVAL_TOOLTIP;
3890     ti.lpszText = text;
3891 
3892     /* Limit ballooneval bounding rect to CursorPos neighbourhood */
3893     ti.rect.left = pt.x - 3;
3894     ti.rect.top = pt.y - 3;
3895     ti.rect.right = pt.x + 3;
3896     ti.rect.bottom = pt.y + 3;
3897 
3898     SendMessage(beval->balloon, TTM_ADDTOOL, 0, (LPARAM)&ti);
3899     /* Make tooltip appear sooner */
3900     SendMessage(beval->balloon, TTM_SETDELAYTIME, TTDT_INITIAL, 10);
3901     /*
3902      * HACK: force tooltip to appear, because it'll not appear until
3903      * first mouse move. D*mn M$
3904      */
3905     mouse_event(MOUSEEVENTF_MOVE, 1, 1, 0, 0);
3906     mouse_event(MOUSEEVENTF_MOVE, (DWORD)-1, (DWORD)-1, 0, 0);
3907 }
3908 
3909     static void
3910 delete_tooltip(beval)
3911     BalloonEval	*beval;
3912 {
3913     DestroyWindow(beval->balloon);
3914 }
3915 
3916     static VOID CALLBACK
3917 BevalTimerProc(hwnd, uMsg, idEvent, dwTime)
3918     HWND    hwnd;
3919     UINT    uMsg;
3920     UINT    idEvent;
3921     DWORD   dwTime;
3922 {
3923     POINT	pt;
3924     RECT	rect;
3925 
3926     if (cur_beval == NULL || cur_beval->showState == ShS_SHOWING || !p_beval)
3927 	return;
3928 
3929     GetCursorPos(&pt);
3930     if (WindowFromPoint(pt) != s_textArea)
3931 	return;
3932 
3933     ScreenToClient(s_textArea, &pt);
3934     GetClientRect(s_textArea, &rect);
3935     if (!PtInRect(&rect, pt))
3936 	return;
3937 
3938     if (LastActivity > 0
3939 	    && (dwTime - LastActivity) >= (DWORD)p_bdlay
3940 	    && (cur_beval->showState != ShS_PENDING
3941 		|| abs(cur_beval->x - pt.x) > 3
3942 		|| abs(cur_beval->y - pt.y) > 3))
3943     {
3944 	/* Pointer resting in one place long enough, it's time to show
3945 	 * the tooltip. */
3946 	cur_beval->showState = ShS_PENDING;
3947 	cur_beval->x = pt.x;
3948 	cur_beval->y = pt.y;
3949 
3950 	TRACE0("BevalTimerProc: sending request");
3951 
3952 	if (cur_beval->msgCB != NULL)
3953 	    (*cur_beval->msgCB)(cur_beval, 0);
3954     }
3955 }
3956 
3957     void
3958 gui_mch_disable_beval_area(beval)
3959     BalloonEval	*beval;
3960 {
3961     TRACE0("gui_mch_disable_beval_area {{{");
3962     KillTimer(s_textArea, BevalTimerId);
3963     TRACE0("gui_mch_disable_beval_area }}}");
3964 }
3965 
3966     void
3967 gui_mch_enable_beval_area(beval)
3968     BalloonEval	*beval;
3969 {
3970     TRACE0("gui_mch_enable_beval_area |||");
3971     if (beval == NULL)
3972 	return;
3973     TRACE0("gui_mch_enable_beval_area {{{");
3974     BevalTimerId = SetTimer(s_textArea, 0, p_bdlay / 2, (TIMERPROC)BevalTimerProc);
3975     TRACE0("gui_mch_enable_beval_area }}}");
3976 }
3977 
3978     void
3979 gui_mch_post_balloon(beval, mesg)
3980     BalloonEval	*beval;
3981     char_u	*mesg;
3982 {
3983     POINT   pt;
3984     TRACE0("gui_mch_post_balloon {{{");
3985     if (beval->showState == ShS_SHOWING)
3986 	return;
3987     GetCursorPos(&pt);
3988     ScreenToClient(s_textArea, &pt);
3989 
3990     if (abs(beval->x - pt.x) < 3 && abs(beval->y - pt.y) < 3)
3991 	/* cursor is still here */
3992     {
3993 	gui_mch_disable_beval_area(cur_beval);
3994 	beval->showState = ShS_SHOWING;
3995 	make_tooltip(beval, mesg, pt);
3996     }
3997     TRACE0("gui_mch_post_balloon }}}");
3998 }
3999 
4000     BalloonEval *
4001 gui_mch_create_beval_area(target, mesg, mesgCB, clientData)
4002     void	*target;	/* ignored, always use s_textArea */
4003     char_u	*mesg;
4004     void	(*mesgCB)__ARGS((BalloonEval *, int));
4005     void	*clientData;
4006 {
4007     /* partially stolen from gui_beval.c */
4008     BalloonEval	*beval;
4009 
4010     if (mesg != NULL && mesgCB != NULL)
4011     {
4012 	EMSG(_("E232: Cannot create BalloonEval with both message and callback"));
4013 	return NULL;
4014     }
4015 
4016     beval = (BalloonEval *)alloc(sizeof(BalloonEval));
4017     if (beval != NULL)
4018     {
4019 	beval->target = s_textArea;
4020 	beval->balloon = NULL;
4021 
4022 	beval->showState = ShS_NEUTRAL;
4023 	beval->x = 0;
4024 	beval->y = 0;
4025 	beval->msg = mesg;
4026 	beval->msgCB = mesgCB;
4027 	beval->clientData = clientData;
4028 
4029 	InitCommonControls();
4030 
4031 	cur_beval = beval;
4032 
4033 	if (p_beval)
4034 	    gui_mch_enable_beval_area(beval);
4035 
4036     }
4037     return beval;
4038 }
4039 
4040     static void
4041 Handle_WM_Notify(hwnd, pnmh)
4042     HWND hwnd;
4043     LPNMHDR pnmh;
4044 {
4045     if (pnmh->idFrom != ID_BEVAL_TOOLTIP) /* it is not our tooltip */
4046 	return;
4047 
4048     if (cur_beval != NULL)
4049     {
4050 	if (pnmh->code == TTN_SHOW)
4051 	{
4052 	    TRACE0("TTN_SHOW {{{");
4053 	    TRACE0("TTN_SHOW }}}");
4054 	}
4055 	else if (pnmh->code == TTN_POP) /* Before tooltip disappear */
4056 	{
4057 	    TRACE0("TTN_POP {{{");
4058 	    delete_tooltip(cur_beval);
4059 	    gui_mch_enable_beval_area(cur_beval);
4060 	    TRACE0("TTN_POP }}}");
4061 
4062 	    cur_beval->showState = ShS_NEUTRAL;
4063 	}
4064     }
4065 }
4066 
4067     static void
4068 TrackUserActivity(UINT uMsg)
4069 {
4070     if ((uMsg >= WM_MOUSEFIRST && uMsg <= WM_MOUSELAST)
4071 	    || (uMsg >= WM_KEYFIRST && uMsg <= WM_KEYLAST))
4072 	LastActivity = GetTickCount();
4073 }
4074 
4075     void
4076 gui_mch_destroy_beval_area(beval)
4077     BalloonEval	*beval;
4078 {
4079     vim_free(beval);
4080 }
4081 #endif /* FEAT_BEVAL */
4082 
4083 #if defined(FEAT_NETBEANS_INTG) || defined(PROTO)
4084 /*
4085  * We have multiple signs to draw at the same location. Draw the
4086  * multi-sign indicator (down-arrow) instead. This is the Win32 version.
4087  */
4088     void
4089 netbeans_draw_multisign_indicator(int row)
4090 {
4091     int i;
4092     int y;
4093     int x;
4094 
4095     x = 0;
4096     y = TEXT_Y(row);
4097 
4098     for (i = 0; i < gui.char_height - 3; i++)
4099 	SetPixel(s_hdc, x+2, y++, gui.currFgColor);
4100 
4101     SetPixel(s_hdc, x+0, y, gui.currFgColor);
4102     SetPixel(s_hdc, x+2, y, gui.currFgColor);
4103     SetPixel(s_hdc, x+4, y++, gui.currFgColor);
4104     SetPixel(s_hdc, x+1, y, gui.currFgColor);
4105     SetPixel(s_hdc, x+2, y, gui.currFgColor);
4106     SetPixel(s_hdc, x+3, y++, gui.currFgColor);
4107     SetPixel(s_hdc, x+2, y, gui.currFgColor);
4108 }
4109 #endif
4110 
4111 #endif
4112