xref: /vim-8.2.3635/src/gui_w32.c (revision 94688b8a)
1 /* vi:set ts=8 sts=4 sw=4 noet:
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, aka Win32.  Also for Win64.
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  * winclip.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 #include "vim.h"
27 
28 #if defined(FEAT_DIRECTX)
29 # include "gui_dwrite.h"
30 #endif
31 
32 #if defined(FEAT_DIRECTX)
33 static DWriteContext *s_dwc = NULL;
34 static int s_directx_enabled = 0;
35 static int s_directx_load_attempted = 0;
36 # define IS_ENABLE_DIRECTX() (s_directx_enabled && s_dwc != NULL && enc_utf8)
37 static int directx_enabled(void);
38 static void directx_binddc(void);
39 #endif
40 
41 #ifdef FEAT_MENU
42 static int gui_mswin_get_menu_height(int fix_window);
43 #endif
44 
45 #if defined(FEAT_RENDER_OPTIONS) || defined(PROTO)
46     int
47 gui_mch_set_rendering_options(char_u *s)
48 {
49 # ifdef FEAT_DIRECTX
50     char_u  *p, *q;
51 
52     int	    dx_enable = 0;
53     int	    dx_flags = 0;
54     float   dx_gamma = 0.0f;
55     float   dx_contrast = 0.0f;
56     float   dx_level = 0.0f;
57     int	    dx_geom = 0;
58     int	    dx_renmode = 0;
59     int	    dx_taamode = 0;
60 
61     /* parse string as rendering options. */
62     for (p = s; p != NULL && *p != NUL; )
63     {
64 	char_u  item[256];
65 	char_u  name[128];
66 	char_u  value[128];
67 
68 	copy_option_part(&p, item, sizeof(item), ",");
69 	if (p == NULL)
70 	    break;
71 	q = &item[0];
72 	copy_option_part(&q, name, sizeof(name), ":");
73 	if (q == NULL)
74 	    return FAIL;
75 	copy_option_part(&q, value, sizeof(value), ":");
76 
77 	if (STRCMP(name, "type") == 0)
78 	{
79 	    if (STRCMP(value, "directx") == 0)
80 		dx_enable = 1;
81 	    else
82 		return FAIL;
83 	}
84 	else if (STRCMP(name, "gamma") == 0)
85 	{
86 	    dx_flags |= 1 << 0;
87 	    dx_gamma = (float)atof((char *)value);
88 	}
89 	else if (STRCMP(name, "contrast") == 0)
90 	{
91 	    dx_flags |= 1 << 1;
92 	    dx_contrast = (float)atof((char *)value);
93 	}
94 	else if (STRCMP(name, "level") == 0)
95 	{
96 	    dx_flags |= 1 << 2;
97 	    dx_level = (float)atof((char *)value);
98 	}
99 	else if (STRCMP(name, "geom") == 0)
100 	{
101 	    dx_flags |= 1 << 3;
102 	    dx_geom = atoi((char *)value);
103 	    if (dx_geom < 0 || dx_geom > 2)
104 		return FAIL;
105 	}
106 	else if (STRCMP(name, "renmode") == 0)
107 	{
108 	    dx_flags |= 1 << 4;
109 	    dx_renmode = atoi((char *)value);
110 	    if (dx_renmode < 0 || dx_renmode > 6)
111 		return FAIL;
112 	}
113 	else if (STRCMP(name, "taamode") == 0)
114 	{
115 	    dx_flags |= 1 << 5;
116 	    dx_taamode = atoi((char *)value);
117 	    if (dx_taamode < 0 || dx_taamode > 3)
118 		return FAIL;
119 	}
120 	else if (STRCMP(name, "scrlines") == 0)
121 	{
122 	    /* Deprecated.  Simply ignore it. */
123 	}
124 	else
125 	    return FAIL;
126     }
127 
128     if (!gui.in_use)
129 	return OK;  /* only checking the syntax of the value */
130 
131     /* Enable DirectX/DirectWrite */
132     if (dx_enable)
133     {
134 	if (!directx_enabled())
135 	    return FAIL;
136 	DWriteContext_SetRenderingParams(s_dwc, NULL);
137 	if (dx_flags)
138 	{
139 	    DWriteRenderingParams param;
140 	    DWriteContext_GetRenderingParams(s_dwc, &param);
141 	    if (dx_flags & (1 << 0))
142 		param.gamma = dx_gamma;
143 	    if (dx_flags & (1 << 1))
144 		param.enhancedContrast = dx_contrast;
145 	    if (dx_flags & (1 << 2))
146 		param.clearTypeLevel = dx_level;
147 	    if (dx_flags & (1 << 3))
148 		param.pixelGeometry = dx_geom;
149 	    if (dx_flags & (1 << 4))
150 		param.renderingMode = dx_renmode;
151 	    if (dx_flags & (1 << 5))
152 		param.textAntialiasMode = dx_taamode;
153 	    DWriteContext_SetRenderingParams(s_dwc, &param);
154 	}
155     }
156     s_directx_enabled = dx_enable;
157 
158     return OK;
159 # else
160     return FAIL;
161 # endif
162 }
163 #endif
164 
165 /*
166  * These are new in Windows ME/XP, only defined in recent compilers.
167  */
168 #ifndef HANDLE_WM_XBUTTONUP
169 # define HANDLE_WM_XBUTTONUP(hwnd, wParam, lParam, fn) \
170    ((fn)((hwnd), (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
171 #endif
172 #ifndef HANDLE_WM_XBUTTONDOWN
173 # define HANDLE_WM_XBUTTONDOWN(hwnd, wParam, lParam, fn) \
174    ((fn)((hwnd), FALSE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
175 #endif
176 #ifndef HANDLE_WM_XBUTTONDBLCLK
177 # define HANDLE_WM_XBUTTONDBLCLK(hwnd, wParam, lParam, fn) \
178    ((fn)((hwnd), TRUE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
179 #endif
180 
181 
182 #include "version.h"	/* used by dialog box routine for default title */
183 #ifdef DEBUG
184 # include <tchar.h>
185 #endif
186 
187 /* cproto fails on missing include files */
188 #ifndef PROTO
189 
190 #ifndef __MINGW32__
191 # include <shellapi.h>
192 #endif
193 #if defined(FEAT_TOOLBAR) || defined(FEAT_BEVAL_GUI) || defined(FEAT_GUI_TABLINE)
194 # include <commctrl.h>
195 #endif
196 #include <windowsx.h>
197 
198 #ifdef GLOBAL_IME
199 # include "glbl_ime.h"
200 #endif
201 
202 #endif /* PROTO */
203 
204 #ifdef FEAT_MENU
205 # define MENUHINTS		/* show menu hints in command line */
206 #endif
207 
208 /* Some parameters for dialog boxes.  All in pixels. */
209 #define DLG_PADDING_X		10
210 #define DLG_PADDING_Y		10
211 #define DLG_OLD_STYLE_PADDING_X	5
212 #define DLG_OLD_STYLE_PADDING_Y	5
213 #define DLG_VERT_PADDING_X	4	/* For vertical buttons */
214 #define DLG_VERT_PADDING_Y	4
215 #define DLG_ICON_WIDTH		34
216 #define DLG_ICON_HEIGHT		34
217 #define DLG_MIN_WIDTH		150
218 #define DLG_FONT_NAME		"MS Sans Serif"
219 #define DLG_FONT_POINT_SIZE	8
220 #define DLG_MIN_MAX_WIDTH	400
221 #define DLG_MIN_MAX_HEIGHT	400
222 
223 #define DLG_NONBUTTON_CONTROL	5000	/* First ID of non-button controls */
224 
225 #ifndef WM_XBUTTONDOWN /* For Win2K / winME ONLY */
226 # define WM_XBUTTONDOWN		0x020B
227 # define WM_XBUTTONUP		0x020C
228 # define WM_XBUTTONDBLCLK	0x020D
229 # define MK_XBUTTON1		0x0020
230 # define MK_XBUTTON2		0x0040
231 #endif
232 
233 #ifdef PROTO
234 /*
235  * Define a few things for generating prototypes.  This is just to avoid
236  * syntax errors, the defines do not need to be correct.
237  */
238 # define APIENTRY
239 # define CALLBACK
240 # define CONST
241 # define FAR
242 # define NEAR
243 # undef _cdecl
244 # define _cdecl
245 typedef int BOOL;
246 typedef int BYTE;
247 typedef int DWORD;
248 typedef int WCHAR;
249 typedef int ENUMLOGFONT;
250 typedef int FINDREPLACE;
251 typedef int HANDLE;
252 typedef int HBITMAP;
253 typedef int HBRUSH;
254 typedef int HDROP;
255 typedef int INT;
256 typedef int LOGFONT[];
257 typedef int LPARAM;
258 typedef int LPCREATESTRUCT;
259 typedef int LPCSTR;
260 typedef int LPCTSTR;
261 typedef int LPRECT;
262 typedef int LPSTR;
263 typedef int LPWINDOWPOS;
264 typedef int LPWORD;
265 typedef int LRESULT;
266 typedef int HRESULT;
267 # undef MSG
268 typedef int MSG;
269 typedef int NEWTEXTMETRIC;
270 typedef int OSVERSIONINFO;
271 typedef int PWORD;
272 typedef int RECT;
273 typedef int UINT;
274 typedef int WORD;
275 typedef int WPARAM;
276 typedef int POINT;
277 typedef void *HINSTANCE;
278 typedef void *HMENU;
279 typedef void *HWND;
280 typedef void *HDC;
281 typedef void VOID;
282 typedef int LPNMHDR;
283 typedef int LONG;
284 typedef int WNDPROC;
285 typedef int UINT_PTR;
286 typedef int COLORREF;
287 typedef int HCURSOR;
288 #endif
289 
290 #ifndef GET_X_LPARAM
291 # define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp))
292 #endif
293 
294 static void _OnPaint( HWND hwnd);
295 static void fill_rect(const RECT *rcp, HBRUSH hbr, COLORREF color);
296 static void clear_rect(RECT *rcp);
297 
298 static WORD		s_dlgfntheight;		/* height of the dialog font */
299 static WORD		s_dlgfntwidth;		/* width of the dialog font */
300 
301 #ifdef FEAT_MENU
302 static HMENU		s_menuBar = NULL;
303 #endif
304 #ifdef FEAT_TEAROFF
305 static void rebuild_tearoff(vimmenu_T *menu);
306 static HBITMAP	s_htearbitmap;	    /* bitmap used to indicate tearoff */
307 #endif
308 
309 /* Flag that is set while processing a message that must not be interrupted by
310  * processing another message. */
311 static int		s_busy_processing = FALSE;
312 
313 static int		destroying = FALSE;	/* call DestroyWindow() ourselves */
314 
315 #ifdef MSWIN_FIND_REPLACE
316 static UINT		s_findrep_msg = 0;	/* set in gui_w[16/32].c */
317 static FINDREPLACE	s_findrep_struct;
318 static FINDREPLACEW	s_findrep_struct_w;
319 static HWND		s_findrep_hwnd = NULL;
320 static int		s_findrep_is_find;	/* TRUE for find dialog, FALSE
321 						   for find/replace dialog */
322 #endif
323 
324 static HINSTANCE	s_hinst = NULL;
325 #if !defined(FEAT_GUI)
326 static
327 #endif
328 HWND			s_hwnd = NULL;
329 static HDC		s_hdc = NULL;
330 static HBRUSH	s_brush = NULL;
331 
332 #ifdef FEAT_TOOLBAR
333 static HWND		s_toolbarhwnd = NULL;
334 static WNDPROC		s_toolbar_wndproc = NULL;
335 #endif
336 
337 #ifdef FEAT_GUI_TABLINE
338 static HWND		s_tabhwnd = NULL;
339 static WNDPROC		s_tabline_wndproc = NULL;
340 static int		showing_tabline = 0;
341 #endif
342 
343 static WPARAM		s_wParam = 0;
344 static LPARAM		s_lParam = 0;
345 
346 static HWND		s_textArea = NULL;
347 static UINT		s_uMsg = 0;
348 
349 static char_u		*s_textfield; /* Used by dialogs to pass back strings */
350 
351 static int		s_need_activate = FALSE;
352 
353 /* This variable is set when waiting for an event, which is the only moment
354  * scrollbar dragging can be done directly.  It's not allowed while commands
355  * are executed, because it may move the cursor and that may cause unexpected
356  * problems (e.g., while ":s" is working).
357  */
358 static int allow_scrollbar = FALSE;
359 
360 #ifdef GLOBAL_IME
361 # define MyTranslateMessage(x) global_ime_TranslateMessage(x)
362 #else
363 # define MyTranslateMessage(x) TranslateMessage(x)
364 #endif
365 
366 #if defined(FEAT_DIRECTX)
367     static int
368 directx_enabled(void)
369 {
370     if (s_dwc != NULL)
371 	return 1;
372     else if (s_directx_load_attempted)
373 	return 0;
374     /* load DirectX */
375     DWrite_Init();
376     s_directx_load_attempted = 1;
377     s_dwc = DWriteContext_Open();
378     directx_binddc();
379     return s_dwc != NULL ? 1 : 0;
380 }
381 
382     static void
383 directx_binddc(void)
384 {
385     if (s_textArea != NULL)
386     {
387 	RECT	rect;
388 	GetClientRect(s_textArea, &rect);
389 	DWriteContext_BindDC(s_dwc, s_hdc, &rect);
390     }
391 }
392 #endif
393 
394 /* use of WindowProc depends on wide_WindowProc */
395 #define MyWindowProc vim_WindowProc
396 
397 extern int current_font_height;	    /* this is in os_mswin.c */
398 
399 static struct
400 {
401     UINT    key_sym;
402     char_u  vim_code0;
403     char_u  vim_code1;
404 } special_keys[] =
405 {
406     {VK_UP,		'k', 'u'},
407     {VK_DOWN,		'k', 'd'},
408     {VK_LEFT,		'k', 'l'},
409     {VK_RIGHT,		'k', 'r'},
410 
411     {VK_F1,		'k', '1'},
412     {VK_F2,		'k', '2'},
413     {VK_F3,		'k', '3'},
414     {VK_F4,		'k', '4'},
415     {VK_F5,		'k', '5'},
416     {VK_F6,		'k', '6'},
417     {VK_F7,		'k', '7'},
418     {VK_F8,		'k', '8'},
419     {VK_F9,		'k', '9'},
420     {VK_F10,		'k', ';'},
421 
422     {VK_F11,		'F', '1'},
423     {VK_F12,		'F', '2'},
424     {VK_F13,		'F', '3'},
425     {VK_F14,		'F', '4'},
426     {VK_F15,		'F', '5'},
427     {VK_F16,		'F', '6'},
428     {VK_F17,		'F', '7'},
429     {VK_F18,		'F', '8'},
430     {VK_F19,		'F', '9'},
431     {VK_F20,		'F', 'A'},
432 
433     {VK_F21,		'F', 'B'},
434 #ifdef FEAT_NETBEANS_INTG
435     {VK_PAUSE,		'F', 'B'},	/* Pause == F21 (see gui_gtk_x11.c) */
436 #endif
437     {VK_F22,		'F', 'C'},
438     {VK_F23,		'F', 'D'},
439     {VK_F24,		'F', 'E'},	/* winuser.h defines up to F24 */
440 
441     {VK_HELP,		'%', '1'},
442     {VK_BACK,		'k', 'b'},
443     {VK_INSERT,		'k', 'I'},
444     {VK_DELETE,		'k', 'D'},
445     {VK_HOME,		'k', 'h'},
446     {VK_END,		'@', '7'},
447     {VK_PRIOR,		'k', 'P'},
448     {VK_NEXT,		'k', 'N'},
449     {VK_PRINT,		'%', '9'},
450     {VK_ADD,		'K', '6'},
451     {VK_SUBTRACT,	'K', '7'},
452     {VK_DIVIDE,		'K', '8'},
453     {VK_MULTIPLY,	'K', '9'},
454     {VK_SEPARATOR,	'K', 'A'},	/* Keypad Enter */
455     {VK_DECIMAL,	'K', 'B'},
456 
457     {VK_NUMPAD0,	'K', 'C'},
458     {VK_NUMPAD1,	'K', 'D'},
459     {VK_NUMPAD2,	'K', 'E'},
460     {VK_NUMPAD3,	'K', 'F'},
461     {VK_NUMPAD4,	'K', 'G'},
462     {VK_NUMPAD5,	'K', 'H'},
463     {VK_NUMPAD6,	'K', 'I'},
464     {VK_NUMPAD7,	'K', 'J'},
465     {VK_NUMPAD8,	'K', 'K'},
466     {VK_NUMPAD9,	'K', 'L'},
467 
468     /* Keys that we want to be able to use any modifier with: */
469     {VK_SPACE,		' ', NUL},
470     {VK_TAB,		TAB, NUL},
471     {VK_ESCAPE,		ESC, NUL},
472     {NL,		NL, NUL},
473     {CAR,		CAR, NUL},
474 
475     /* End of list marker: */
476     {0,			0, 0}
477 };
478 
479 /* Local variables */
480 static int	s_button_pending = -1;
481 
482 /* s_getting_focus is set when we got focus but didn't see mouse-up event yet,
483  * so don't reset s_button_pending. */
484 static int	s_getting_focus = FALSE;
485 
486 static int	s_x_pending;
487 static int	s_y_pending;
488 static UINT	s_kFlags_pending;
489 static UINT	s_wait_timer = 0;	  // Timer for get char from user
490 static int	s_timed_out = FALSE;
491 static int	dead_key = 0;		  // 0: no dead key, 1: dead key pressed
492 static UINT	surrogate_pending_ch = 0; // 0: no surrogate pending,
493 					  // else a high surrogate
494 
495 #ifdef FEAT_BEVAL_GUI
496 /* balloon-eval WM_NOTIFY_HANDLER */
497 static void Handle_WM_Notify(HWND hwnd, LPNMHDR pnmh);
498 static void TrackUserActivity(UINT uMsg);
499 #endif
500 
501 /*
502  * For control IME.
503  *
504  * These LOGFONT used for IME.
505  */
506 #if defined(FEAT_MBYTE_IME) || defined(GLOBAL_IME)
507 /* holds LOGFONT for 'guifontwide' if available, otherwise 'guifont' */
508 static LOGFONT norm_logfont;
509 #endif
510 #ifdef FEAT_MBYTE_IME
511 /* holds LOGFONT for 'guifont' always. */
512 static LOGFONT sub_logfont;
513 #endif
514 
515 #ifdef FEAT_MBYTE_IME
516 static LRESULT _OnImeNotify(HWND hWnd, DWORD dwCommand, DWORD dwData);
517 #endif
518 
519 #if defined(FEAT_BROWSE)
520 static char_u *convert_filter(char_u *s);
521 #endif
522 
523 #ifdef DEBUG_PRINT_ERROR
524 /*
525  * Print out the last Windows error message
526  */
527     static void
528 print_windows_error(void)
529 {
530     LPVOID  lpMsgBuf;
531 
532     FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
533 		  NULL, GetLastError(),
534 		  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
535 		  (LPTSTR) &lpMsgBuf, 0, NULL);
536     TRACE1("Error: %s\n", lpMsgBuf);
537     LocalFree(lpMsgBuf);
538 }
539 #endif
540 
541 /*
542  * Cursor blink functions.
543  *
544  * This is a simple state machine:
545  * BLINK_NONE	not blinking at all
546  * BLINK_OFF	blinking, cursor is not shown
547  * BLINK_ON	blinking, cursor is shown
548  */
549 
550 #define BLINK_NONE  0
551 #define BLINK_OFF   1
552 #define BLINK_ON    2
553 
554 static int		blink_state = BLINK_NONE;
555 static long_u		blink_waittime = 700;
556 static long_u		blink_ontime = 400;
557 static long_u		blink_offtime = 250;
558 static UINT		blink_timer = 0;
559 
560     int
561 gui_mch_is_blinking(void)
562 {
563     return blink_state != BLINK_NONE;
564 }
565 
566     int
567 gui_mch_is_blink_off(void)
568 {
569     return blink_state == BLINK_OFF;
570 }
571 
572     void
573 gui_mch_set_blinking(long wait, long on, long off)
574 {
575     blink_waittime = wait;
576     blink_ontime = on;
577     blink_offtime = off;
578 }
579 
580     static VOID CALLBACK
581 _OnBlinkTimer(
582     HWND hwnd,
583     UINT uMsg UNUSED,
584     UINT idEvent,
585     DWORD dwTime UNUSED)
586 {
587     MSG msg;
588 
589     /*
590     TRACE2("Got timer event, id %d, blink_timer %d\n", idEvent, blink_timer);
591     */
592 
593     KillTimer(NULL, idEvent);
594 
595     /* Eat spurious WM_TIMER messages */
596     while (pPeekMessage(&msg, hwnd, WM_TIMER, WM_TIMER, PM_REMOVE))
597 	;
598 
599     if (blink_state == BLINK_ON)
600     {
601 	gui_undraw_cursor();
602 	blink_state = BLINK_OFF;
603 	blink_timer = (UINT) SetTimer(NULL, 0, (UINT)blink_offtime,
604 						    (TIMERPROC)_OnBlinkTimer);
605     }
606     else
607     {
608 	gui_update_cursor(TRUE, FALSE);
609 	blink_state = BLINK_ON;
610 	blink_timer = (UINT) SetTimer(NULL, 0, (UINT)blink_ontime,
611 						    (TIMERPROC)_OnBlinkTimer);
612     }
613     gui_mch_flush();
614 }
615 
616     static void
617 gui_mswin_rm_blink_timer(void)
618 {
619     MSG msg;
620 
621     if (blink_timer != 0)
622     {
623 	KillTimer(NULL, blink_timer);
624 	/* Eat spurious WM_TIMER messages */
625 	while (pPeekMessage(&msg, s_hwnd, WM_TIMER, WM_TIMER, PM_REMOVE))
626 	    ;
627 	blink_timer = 0;
628     }
629 }
630 
631 /*
632  * Stop the cursor blinking.  Show the cursor if it wasn't shown.
633  */
634     void
635 gui_mch_stop_blink(int may_call_gui_update_cursor)
636 {
637     gui_mswin_rm_blink_timer();
638     if (blink_state == BLINK_OFF && may_call_gui_update_cursor)
639     {
640 	gui_update_cursor(TRUE, FALSE);
641 	gui_mch_flush();
642     }
643     blink_state = BLINK_NONE;
644 }
645 
646 /*
647  * Start the cursor blinking.  If it was already blinking, this restarts the
648  * waiting time and shows the cursor.
649  */
650     void
651 gui_mch_start_blink(void)
652 {
653     gui_mswin_rm_blink_timer();
654 
655     /* Only switch blinking on if none of the times is zero */
656     if (blink_waittime && blink_ontime && blink_offtime && gui.in_focus)
657     {
658 	blink_timer = (UINT)SetTimer(NULL, 0, (UINT)blink_waittime,
659 						    (TIMERPROC)_OnBlinkTimer);
660 	blink_state = BLINK_ON;
661 	gui_update_cursor(TRUE, FALSE);
662 	gui_mch_flush();
663     }
664 }
665 
666 /*
667  * Call-back routines.
668  */
669 
670     static VOID CALLBACK
671 _OnTimer(
672     HWND hwnd,
673     UINT uMsg UNUSED,
674     UINT idEvent,
675     DWORD dwTime UNUSED)
676 {
677     MSG msg;
678 
679     /*
680     TRACE2("Got timer event, id %d, s_wait_timer %d\n", idEvent, s_wait_timer);
681     */
682     KillTimer(NULL, idEvent);
683     s_timed_out = TRUE;
684 
685     /* Eat spurious WM_TIMER messages */
686     while (pPeekMessage(&msg, hwnd, WM_TIMER, WM_TIMER, PM_REMOVE))
687 	;
688     if (idEvent == s_wait_timer)
689 	s_wait_timer = 0;
690 }
691 
692     static void
693 _OnDeadChar(
694     HWND hwnd UNUSED,
695     UINT ch UNUSED,
696     int cRepeat UNUSED)
697 {
698     dead_key = 1;
699 }
700 
701 /*
702  * Convert Unicode character "ch" to bytes in "string[slen]".
703  * When "had_alt" is TRUE the ALT key was included in "ch".
704  * Return the length.
705  * Because the Windows API uses UTF-16, we have to deal with surrogate
706  * pairs; this is where we choose to deal with them: if "ch" is a high
707  * surrogate, it will be stored, and the length returned will be zero; the next
708  * char_to_string call will then include the high surrogate, decoding the pair
709  * of UTF-16 code units to a single Unicode code point, presuming it is the
710  * matching low surrogate.
711  */
712     static int
713 char_to_string(int ch, char_u *string, int slen, int had_alt)
714 {
715     int		len;
716     int		i;
717     WCHAR	wstring[2];
718     char_u	*ws = NULL;
719 
720     if (surrogate_pending_ch != 0)
721     {
722 	/* We don't guarantee ch is a low surrogate to match the high surrogate
723 	 * we already have; it should be, but if it isn't, tough luck. */
724 	wstring[0] = surrogate_pending_ch;
725 	wstring[1] = ch;
726 	surrogate_pending_ch = 0;
727 	len = 2;
728     }
729     else if (ch >= 0xD800 && ch <= 0xDBFF)	/* high surrogate */
730     {
731 	/* We don't have the entire code point yet, only the first UTF-16 code
732 	 * unit; so just remember it and use it in the next call. */
733 	surrogate_pending_ch = ch;
734 	return 0;
735     }
736     else
737     {
738 	wstring[0] = ch;
739 	len = 1;
740     }
741 
742     /* "ch" is a UTF-16 character.  Convert it to a string of bytes.  When
743      * "enc_codepage" is non-zero use the standard Win32 function,
744      * otherwise use our own conversion function (e.g., for UTF-8). */
745     if (enc_codepage > 0)
746     {
747 	len = WideCharToMultiByte(enc_codepage, 0, wstring, len,
748 		(LPSTR)string, slen, 0, NULL);
749 	/* If we had included the ALT key into the character but now the
750 	 * upper bit is no longer set, that probably means the conversion
751 	 * failed.  Convert the original character and set the upper bit
752 	 * afterwards. */
753 	if (had_alt && len == 1 && ch >= 0x80 && string[0] < 0x80)
754 	{
755 	    wstring[0] = ch & 0x7f;
756 	    len = WideCharToMultiByte(enc_codepage, 0, wstring, len,
757 		    (LPSTR)string, slen, 0, NULL);
758 	    if (len == 1) /* safety check */
759 		string[0] |= 0x80;
760 	}
761     }
762     else
763     {
764 	ws = utf16_to_enc(wstring, &len);
765 	if (ws == NULL)
766 	    len = 0;
767 	else
768 	{
769 	    if (len > slen)	/* just in case */
770 		len = slen;
771 	    mch_memmove(string, ws, len);
772 	    vim_free(ws);
773 	}
774     }
775 
776     if (len == 0)
777     {
778 	string[0] = ch;
779 	len = 1;
780     }
781 
782     for (i = 0; i < len; ++i)
783 	if (string[i] == CSI && len <= slen - 2)
784 	{
785 	    /* Insert CSI as K_CSI. */
786 	    mch_memmove(string + i + 3, string + i + 1, len - i - 1);
787 	    string[++i] = KS_EXTRA;
788 	    string[++i] = (int)KE_CSI;
789 	    len += 2;
790 	}
791 
792     return len;
793 }
794 
795 /*
796  * Key hit, add it to the input buffer.
797  */
798     static void
799 _OnChar(
800     HWND hwnd UNUSED,
801     UINT ch,
802     int cRepeat UNUSED)
803 {
804     char_u	string[40];
805     int		len = 0;
806 
807     dead_key = 0;
808 
809     len = char_to_string(ch, string, 40, FALSE);
810     if (len == 1 && string[0] == Ctrl_C && ctrl_c_interrupts)
811     {
812 	trash_input_buf();
813 	got_int = TRUE;
814     }
815 
816     add_to_input_buf(string, len);
817 }
818 
819 /*
820  * Alt-Key hit, add it to the input buffer.
821  */
822     static void
823 _OnSysChar(
824     HWND hwnd UNUSED,
825     UINT cch,
826     int cRepeat UNUSED)
827 {
828     char_u	string[40]; /* Enough for multibyte character */
829     int		len;
830     int		modifiers;
831     int		ch = cch;   /* special keys are negative */
832 
833     dead_key = 0;
834 
835     /* TRACE("OnSysChar(%d, %c)\n", ch, ch); */
836 
837     /* OK, we have a character key (given by ch) which was entered with the
838      * ALT key pressed. Eg, if the user presses Alt-A, then ch == 'A'. Note
839      * that the system distinguishes Alt-a and Alt-A (Alt-Shift-a unless
840      * CAPSLOCK is pressed) at this point.
841      */
842     modifiers = MOD_MASK_ALT;
843     if (GetKeyState(VK_SHIFT) & 0x8000)
844 	modifiers |= MOD_MASK_SHIFT;
845     if (GetKeyState(VK_CONTROL) & 0x8000)
846 	modifiers |= MOD_MASK_CTRL;
847 
848     ch = simplify_key(ch, &modifiers);
849     /* remove the SHIFT modifier for keys where it's already included, e.g.,
850      * '(' and '*' */
851     if (ch < 0x100 && !isalpha(ch) && isprint(ch))
852 	modifiers &= ~MOD_MASK_SHIFT;
853 
854     /* Interpret the ALT key as making the key META, include SHIFT, etc. */
855     ch = extract_modifiers(ch, &modifiers);
856     if (ch == CSI)
857 	ch = K_CSI;
858 
859     len = 0;
860     if (modifiers)
861     {
862 	string[len++] = CSI;
863 	string[len++] = KS_MODIFIER;
864 	string[len++] = modifiers;
865     }
866 
867     if (IS_SPECIAL((int)ch))
868     {
869 	string[len++] = CSI;
870 	string[len++] = K_SECOND((int)ch);
871 	string[len++] = K_THIRD((int)ch);
872     }
873     else
874     {
875 	/* Although the documentation isn't clear about it, we assume "ch" is
876 	 * a Unicode character. */
877 	len += char_to_string(ch, string + len, 40 - len, TRUE);
878     }
879 
880     add_to_input_buf(string, len);
881 }
882 
883     static void
884 _OnMouseEvent(
885     int button,
886     int x,
887     int y,
888     int repeated_click,
889     UINT keyFlags)
890 {
891     int vim_modifiers = 0x0;
892 
893     s_getting_focus = FALSE;
894 
895     if (keyFlags & MK_SHIFT)
896 	vim_modifiers |= MOUSE_SHIFT;
897     if (keyFlags & MK_CONTROL)
898 	vim_modifiers |= MOUSE_CTRL;
899     if (GetKeyState(VK_MENU) & 0x8000)
900 	vim_modifiers |= MOUSE_ALT;
901 
902     gui_send_mouse_event(button, x, y, repeated_click, vim_modifiers);
903 }
904 
905     static void
906 _OnMouseButtonDown(
907     HWND hwnd UNUSED,
908     BOOL fDoubleClick UNUSED,
909     int x,
910     int y,
911     UINT keyFlags)
912 {
913     static LONG	s_prevTime = 0;
914 
915     LONG    currentTime = GetMessageTime();
916     int	    button = -1;
917     int	    repeated_click;
918 
919     /* Give main window the focus: this is so the cursor isn't hollow. */
920     (void)SetFocus(s_hwnd);
921 
922     if (s_uMsg == WM_LBUTTONDOWN || s_uMsg == WM_LBUTTONDBLCLK)
923 	button = MOUSE_LEFT;
924     else if (s_uMsg == WM_MBUTTONDOWN || s_uMsg == WM_MBUTTONDBLCLK)
925 	button = MOUSE_MIDDLE;
926     else if (s_uMsg == WM_RBUTTONDOWN || s_uMsg == WM_RBUTTONDBLCLK)
927 	button = MOUSE_RIGHT;
928     else if (s_uMsg == WM_XBUTTONDOWN || s_uMsg == WM_XBUTTONDBLCLK)
929     {
930 #ifndef GET_XBUTTON_WPARAM
931 # define GET_XBUTTON_WPARAM(wParam)	(HIWORD(wParam))
932 #endif
933 	button = ((GET_XBUTTON_WPARAM(s_wParam) == 1) ? MOUSE_X1 : MOUSE_X2);
934     }
935     else if (s_uMsg == WM_CAPTURECHANGED)
936     {
937 	/* on W95/NT4, somehow you get in here with an odd Msg
938 	 * if you press one button while holding down the other..*/
939 	if (s_button_pending == MOUSE_LEFT)
940 	    button = MOUSE_RIGHT;
941 	else
942 	    button = MOUSE_LEFT;
943     }
944     if (button >= 0)
945     {
946 	repeated_click = ((int)(currentTime - s_prevTime) < p_mouset);
947 
948 	/*
949 	 * Holding down the left and right buttons simulates pushing the middle
950 	 * button.
951 	 */
952 	if (repeated_click
953 		&& ((button == MOUSE_LEFT && s_button_pending == MOUSE_RIGHT)
954 		    || (button == MOUSE_RIGHT
955 					  && s_button_pending == MOUSE_LEFT)))
956 	{
957 	    /*
958 	     * Hmm, gui.c will ignore more than one button down at a time, so
959 	     * pretend we let go of it first.
960 	     */
961 	    gui_send_mouse_event(MOUSE_RELEASE, x, y, FALSE, 0x0);
962 	    button = MOUSE_MIDDLE;
963 	    repeated_click = FALSE;
964 	    s_button_pending = -1;
965 	    _OnMouseEvent(button, x, y, repeated_click, keyFlags);
966 	}
967 	else if ((repeated_click)
968 		|| (mouse_model_popup() && (button == MOUSE_RIGHT)))
969 	{
970 	    if (s_button_pending > -1)
971 	    {
972 		    _OnMouseEvent(s_button_pending, x, y, FALSE, keyFlags);
973 		    s_button_pending = -1;
974 	    }
975 	    /* TRACE("Button down at x %d, y %d\n", x, y); */
976 	    _OnMouseEvent(button, x, y, repeated_click, keyFlags);
977 	}
978 	else
979 	{
980 	    /*
981 	     * If this is the first press (i.e. not a multiple click) don't
982 	     * action immediately, but store and wait for:
983 	     * i) button-up
984 	     * ii) mouse move
985 	     * iii) another button press
986 	     * before using it.
987 	     * This enables us to make left+right simulate middle button,
988 	     * without left or right being actioned first.  The side-effect is
989 	     * that if you click and hold the mouse without dragging, the
990 	     * cursor doesn't move until you release the button. In practice
991 	     * this is hardly a problem.
992 	     */
993 	    s_button_pending = button;
994 	    s_x_pending = x;
995 	    s_y_pending = y;
996 	    s_kFlags_pending = keyFlags;
997 	}
998 
999 	s_prevTime = currentTime;
1000     }
1001 }
1002 
1003     static void
1004 _OnMouseMoveOrRelease(
1005     HWND hwnd UNUSED,
1006     int x,
1007     int y,
1008     UINT keyFlags)
1009 {
1010     int button;
1011 
1012     s_getting_focus = FALSE;
1013     if (s_button_pending > -1)
1014     {
1015 	/* Delayed action for mouse down event */
1016 	_OnMouseEvent(s_button_pending, s_x_pending,
1017 					s_y_pending, FALSE, s_kFlags_pending);
1018 	s_button_pending = -1;
1019     }
1020     if (s_uMsg == WM_MOUSEMOVE)
1021     {
1022 	/*
1023 	 * It's only a MOUSE_DRAG if one or more mouse buttons are being held
1024 	 * down.
1025 	 */
1026 	if (!(keyFlags & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON
1027 						| MK_XBUTTON1 | MK_XBUTTON2)))
1028 	{
1029 	    gui_mouse_moved(x, y);
1030 	    return;
1031 	}
1032 
1033 	/*
1034 	 * While button is down, keep grabbing mouse move events when
1035 	 * the mouse goes outside the window
1036 	 */
1037 	SetCapture(s_textArea);
1038 	button = MOUSE_DRAG;
1039 	/* TRACE("  move at x %d, y %d\n", x, y); */
1040     }
1041     else
1042     {
1043 	ReleaseCapture();
1044 	button = MOUSE_RELEASE;
1045 	/* TRACE("  up at x %d, y %d\n", x, y); */
1046     }
1047 
1048     _OnMouseEvent(button, x, y, FALSE, keyFlags);
1049 }
1050 
1051     static void
1052 _OnSizeTextArea(
1053     HWND hwnd UNUSED,
1054     UINT state UNUSED,
1055     int cx UNUSED,
1056     int cy UNUSED)
1057 {
1058 #if defined(FEAT_DIRECTX)
1059     if (IS_ENABLE_DIRECTX())
1060 	directx_binddc();
1061 #endif
1062 }
1063 
1064 #ifdef FEAT_MENU
1065 /*
1066  * Find the vimmenu_T with the given id
1067  */
1068     static vimmenu_T *
1069 gui_mswin_find_menu(
1070     vimmenu_T	*pMenu,
1071     int		id)
1072 {
1073     vimmenu_T	*pChildMenu;
1074 
1075     while (pMenu)
1076     {
1077 	if (pMenu->id == (UINT)id)
1078 	    break;
1079 	if (pMenu->children != NULL)
1080 	{
1081 	    pChildMenu = gui_mswin_find_menu(pMenu->children, id);
1082 	    if (pChildMenu)
1083 	    {
1084 		pMenu = pChildMenu;
1085 		break;
1086 	    }
1087 	}
1088 	pMenu = pMenu->next;
1089     }
1090     return pMenu;
1091 }
1092 
1093     static void
1094 _OnMenu(
1095     HWND	hwnd UNUSED,
1096     int		id,
1097     HWND	hwndCtl UNUSED,
1098     UINT	codeNotify UNUSED)
1099 {
1100     vimmenu_T	*pMenu;
1101 
1102     pMenu = gui_mswin_find_menu(root_menu, id);
1103     if (pMenu)
1104 	gui_menu_cb(pMenu);
1105 }
1106 #endif
1107 
1108 #ifdef MSWIN_FIND_REPLACE
1109 /*
1110  * copy useful data from structure LPFINDREPLACE to structure LPFINDREPLACEW
1111  */
1112     static void
1113 findrep_atow(LPFINDREPLACEW lpfrw, LPFINDREPLACE lpfr)
1114 {
1115     WCHAR *wp;
1116 
1117     lpfrw->hwndOwner = lpfr->hwndOwner;
1118     lpfrw->Flags = lpfr->Flags;
1119 
1120     wp = enc_to_utf16((char_u *)lpfr->lpstrFindWhat, NULL);
1121     wcsncpy(lpfrw->lpstrFindWhat, wp, lpfrw->wFindWhatLen - 1);
1122     vim_free(wp);
1123 
1124     /* the field "lpstrReplaceWith" doesn't need to be copied */
1125 }
1126 
1127 /*
1128  * copy useful data from structure LPFINDREPLACEW to structure LPFINDREPLACE
1129  */
1130     static void
1131 findrep_wtoa(LPFINDREPLACE lpfr, LPFINDREPLACEW lpfrw)
1132 {
1133     char_u *p;
1134 
1135     lpfr->Flags = lpfrw->Flags;
1136 
1137     p = utf16_to_enc((short_u*)lpfrw->lpstrFindWhat, NULL);
1138     vim_strncpy((char_u *)lpfr->lpstrFindWhat, p, lpfr->wFindWhatLen - 1);
1139     vim_free(p);
1140 
1141     p = utf16_to_enc((short_u*)lpfrw->lpstrReplaceWith, NULL);
1142     vim_strncpy((char_u *)lpfr->lpstrReplaceWith, p, lpfr->wReplaceWithLen - 1);
1143     vim_free(p);
1144 }
1145 
1146 /*
1147  * Handle a Find/Replace window message.
1148  */
1149     static void
1150 _OnFindRepl(void)
1151 {
1152     int	    flags = 0;
1153     int	    down;
1154 
1155     /* If the OS is Windows NT, and 'encoding' differs from active codepage:
1156      * convert text from wide string. */
1157     if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
1158     {
1159 	findrep_wtoa(&s_findrep_struct, &s_findrep_struct_w);
1160     }
1161 
1162     if (s_findrep_struct.Flags & FR_DIALOGTERM)
1163 	/* Give main window the focus back. */
1164 	(void)SetFocus(s_hwnd);
1165 
1166     if (s_findrep_struct.Flags & FR_FINDNEXT)
1167     {
1168 	flags = FRD_FINDNEXT;
1169 
1170 	/* Give main window the focus back: this is so the cursor isn't
1171 	 * hollow. */
1172 	(void)SetFocus(s_hwnd);
1173     }
1174     else if (s_findrep_struct.Flags & FR_REPLACE)
1175     {
1176 	flags = FRD_REPLACE;
1177 
1178 	/* Give main window the focus back: this is so the cursor isn't
1179 	 * hollow. */
1180 	(void)SetFocus(s_hwnd);
1181     }
1182     else if (s_findrep_struct.Flags & FR_REPLACEALL)
1183     {
1184 	flags = FRD_REPLACEALL;
1185     }
1186 
1187     if (flags != 0)
1188     {
1189 	/* Call the generic GUI function to do the actual work. */
1190 	if (s_findrep_struct.Flags & FR_WHOLEWORD)
1191 	    flags |= FRD_WHOLE_WORD;
1192 	if (s_findrep_struct.Flags & FR_MATCHCASE)
1193 	    flags |= FRD_MATCH_CASE;
1194 	down = (s_findrep_struct.Flags & FR_DOWN) != 0;
1195 	gui_do_findrepl(flags, (char_u *)s_findrep_struct.lpstrFindWhat,
1196 			     (char_u *)s_findrep_struct.lpstrReplaceWith, down);
1197     }
1198 }
1199 #endif
1200 
1201     static void
1202 HandleMouseHide(UINT uMsg, LPARAM lParam)
1203 {
1204     static LPARAM last_lParam = 0L;
1205 
1206     /* We sometimes get a mousemove when the mouse didn't move... */
1207     if (uMsg == WM_MOUSEMOVE || uMsg == WM_NCMOUSEMOVE)
1208     {
1209 	if (lParam == last_lParam)
1210 	    return;
1211 	last_lParam = lParam;
1212     }
1213 
1214     /* Handle specially, to centralise coding. We need to be sure we catch all
1215      * possible events which should cause us to restore the cursor (as it is a
1216      * shared resource, we take full responsibility for it).
1217      */
1218     switch (uMsg)
1219     {
1220     case WM_KEYUP:
1221     case WM_CHAR:
1222 	/*
1223 	 * blank out the pointer if necessary
1224 	 */
1225 	if (p_mh)
1226 	    gui_mch_mousehide(TRUE);
1227 	break;
1228 
1229     case WM_SYSKEYUP:	 /* show the pointer when a system-key is pressed */
1230     case WM_SYSCHAR:
1231     case WM_MOUSEMOVE:	 /* show the pointer on any mouse action */
1232     case WM_LBUTTONDOWN:
1233     case WM_LBUTTONUP:
1234     case WM_MBUTTONDOWN:
1235     case WM_MBUTTONUP:
1236     case WM_RBUTTONDOWN:
1237     case WM_RBUTTONUP:
1238     case WM_XBUTTONDOWN:
1239     case WM_XBUTTONUP:
1240     case WM_NCMOUSEMOVE:
1241     case WM_NCLBUTTONDOWN:
1242     case WM_NCLBUTTONUP:
1243     case WM_NCMBUTTONDOWN:
1244     case WM_NCMBUTTONUP:
1245     case WM_NCRBUTTONDOWN:
1246     case WM_NCRBUTTONUP:
1247     case WM_KILLFOCUS:
1248 	/*
1249 	 * if the pointer is currently hidden, then we should show it.
1250 	 */
1251 	gui_mch_mousehide(FALSE);
1252 	break;
1253     }
1254 }
1255 
1256     static LRESULT CALLBACK
1257 _TextAreaWndProc(
1258     HWND hwnd,
1259     UINT uMsg,
1260     WPARAM wParam,
1261     LPARAM lParam)
1262 {
1263     /*
1264     TRACE("TextAreaWndProc: hwnd = %08x, msg = %x, wParam = %x, lParam = %x\n",
1265 	  hwnd, uMsg, wParam, lParam);
1266     */
1267 
1268     HandleMouseHide(uMsg, lParam);
1269 
1270     s_uMsg = uMsg;
1271     s_wParam = wParam;
1272     s_lParam = lParam;
1273 
1274 #ifdef FEAT_BEVAL_GUI
1275     TrackUserActivity(uMsg);
1276 #endif
1277 
1278     switch (uMsg)
1279     {
1280 	HANDLE_MSG(hwnd, WM_LBUTTONDBLCLK,_OnMouseButtonDown);
1281 	HANDLE_MSG(hwnd, WM_LBUTTONDOWN,_OnMouseButtonDown);
1282 	HANDLE_MSG(hwnd, WM_LBUTTONUP,	_OnMouseMoveOrRelease);
1283 	HANDLE_MSG(hwnd, WM_MBUTTONDBLCLK,_OnMouseButtonDown);
1284 	HANDLE_MSG(hwnd, WM_MBUTTONDOWN,_OnMouseButtonDown);
1285 	HANDLE_MSG(hwnd, WM_MBUTTONUP,	_OnMouseMoveOrRelease);
1286 	HANDLE_MSG(hwnd, WM_MOUSEMOVE,	_OnMouseMoveOrRelease);
1287 	HANDLE_MSG(hwnd, WM_PAINT,	_OnPaint);
1288 	HANDLE_MSG(hwnd, WM_RBUTTONDBLCLK,_OnMouseButtonDown);
1289 	HANDLE_MSG(hwnd, WM_RBUTTONDOWN,_OnMouseButtonDown);
1290 	HANDLE_MSG(hwnd, WM_RBUTTONUP,	_OnMouseMoveOrRelease);
1291 	HANDLE_MSG(hwnd, WM_XBUTTONDBLCLK,_OnMouseButtonDown);
1292 	HANDLE_MSG(hwnd, WM_XBUTTONDOWN,_OnMouseButtonDown);
1293 	HANDLE_MSG(hwnd, WM_XBUTTONUP,	_OnMouseMoveOrRelease);
1294 	HANDLE_MSG(hwnd, WM_SIZE,	_OnSizeTextArea);
1295 
1296 #ifdef FEAT_BEVAL_GUI
1297 	case WM_NOTIFY: Handle_WM_Notify(hwnd, (LPNMHDR)lParam);
1298 	    return TRUE;
1299 #endif
1300 	default:
1301 	    return MyWindowProc(hwnd, uMsg, wParam, lParam);
1302     }
1303 }
1304 
1305 #ifdef PROTO
1306 typedef int WINAPI;
1307 #endif
1308 
1309     LRESULT WINAPI
1310 vim_WindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
1311 {
1312 #ifdef GLOBAL_IME
1313     return global_ime_DefWindowProc(hwnd, message, wParam, lParam);
1314 #else
1315     if (wide_WindowProc)
1316 	return DefWindowProcW(hwnd, message, wParam, lParam);
1317     return DefWindowProc(hwnd, message, wParam, lParam);
1318 #endif
1319 }
1320 
1321 /*
1322  * Called when the foreground or background color has been changed.
1323  */
1324     void
1325 gui_mch_new_colors(void)
1326 {
1327     /* nothing to do? */
1328 }
1329 
1330 /*
1331  * Set the colors to their default values.
1332  */
1333     void
1334 gui_mch_def_colors(void)
1335 {
1336     gui.norm_pixel = GetSysColor(COLOR_WINDOWTEXT);
1337     gui.back_pixel = GetSysColor(COLOR_WINDOW);
1338     gui.def_norm_pixel = gui.norm_pixel;
1339     gui.def_back_pixel = gui.back_pixel;
1340 }
1341 
1342 /*
1343  * Open the GUI window which was created by a call to gui_mch_init().
1344  */
1345     int
1346 gui_mch_open(void)
1347 {
1348 #ifndef SW_SHOWDEFAULT
1349 # define SW_SHOWDEFAULT 10	/* Borland 5.0 doesn't have it */
1350 #endif
1351     /* Actually open the window, if not already visible
1352      * (may be done already in gui_mch_set_shellsize) */
1353     if (!IsWindowVisible(s_hwnd))
1354 	ShowWindow(s_hwnd, SW_SHOWDEFAULT);
1355 
1356 #ifdef MSWIN_FIND_REPLACE
1357     /* Init replace string here, so that we keep it when re-opening the
1358      * dialog. */
1359     s_findrep_struct.lpstrReplaceWith[0] = NUL;
1360 #endif
1361 
1362     return OK;
1363 }
1364 
1365 /*
1366  * Get the position of the top left corner of the window.
1367  */
1368     int
1369 gui_mch_get_winpos(int *x, int *y)
1370 {
1371     RECT    rect;
1372 
1373     GetWindowRect(s_hwnd, &rect);
1374     *x = rect.left;
1375     *y = rect.top;
1376     return OK;
1377 }
1378 
1379 /*
1380  * Set the position of the top left corner of the window to the given
1381  * coordinates.
1382  */
1383     void
1384 gui_mch_set_winpos(int x, int y)
1385 {
1386     SetWindowPos(s_hwnd, NULL, x, y, 0, 0,
1387 		 SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
1388 }
1389     void
1390 gui_mch_set_text_area_pos(int x, int y, int w, int h)
1391 {
1392     static int oldx = 0;
1393     static int oldy = 0;
1394 
1395     SetWindowPos(s_textArea, NULL, x, y, w, h, SWP_NOZORDER | SWP_NOACTIVATE);
1396 
1397 #ifdef FEAT_TOOLBAR
1398     if (vim_strchr(p_go, GO_TOOLBAR) != NULL)
1399 	SendMessage(s_toolbarhwnd, WM_SIZE,
1400 	      (WPARAM)0, (LPARAM)(w + ((long)(TOOLBAR_BUTTON_HEIGHT+8)<<16)));
1401 #endif
1402 #if defined(FEAT_GUI_TABLINE)
1403     if (showing_tabline)
1404     {
1405 	int	top = 0;
1406 	RECT	rect;
1407 
1408 # ifdef FEAT_TOOLBAR
1409 	if (vim_strchr(p_go, GO_TOOLBAR) != NULL)
1410 	    top = TOOLBAR_BUTTON_HEIGHT + TOOLBAR_BORDER_HEIGHT;
1411 # endif
1412 	GetClientRect(s_hwnd, &rect);
1413 	MoveWindow(s_tabhwnd, 0, top, rect.right, gui.tabline_height, TRUE);
1414     }
1415 #endif
1416 
1417     /* When side scroll bar is unshown, the size of window will change.
1418      * then, the text area move left or right. thus client rect should be
1419      * forcedly redrawn. (Yasuhiro Matsumoto) */
1420     if (oldx != x || oldy != y)
1421     {
1422 	InvalidateRect(s_hwnd, NULL, FALSE);
1423 	oldx = x;
1424 	oldy = y;
1425     }
1426 }
1427 
1428 
1429 /*
1430  * Scrollbar stuff:
1431  */
1432 
1433     void
1434 gui_mch_enable_scrollbar(
1435     scrollbar_T     *sb,
1436     int		    flag)
1437 {
1438     ShowScrollBar(sb->id, SB_CTL, flag);
1439 
1440     /* TODO: When the window is maximized, the size of the window stays the
1441      * same, thus the size of the text area changes.  On Win98 it's OK, on Win
1442      * NT 4.0 it's not... */
1443 }
1444 
1445     void
1446 gui_mch_set_scrollbar_pos(
1447     scrollbar_T *sb,
1448     int		x,
1449     int		y,
1450     int		w,
1451     int		h)
1452 {
1453     SetWindowPos(sb->id, NULL, x, y, w, h,
1454 			      SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW);
1455 }
1456 
1457     void
1458 gui_mch_create_scrollbar(
1459     scrollbar_T *sb,
1460     int		orient)	/* SBAR_VERT or SBAR_HORIZ */
1461 {
1462     sb->id = CreateWindow(
1463 	"SCROLLBAR", "Scrollbar",
1464 	WS_CHILD | ((orient == SBAR_VERT) ? SBS_VERT : SBS_HORZ), 0, 0,
1465 	10,				/* Any value will do for now */
1466 	10,				/* Any value will do for now */
1467 	s_hwnd, NULL,
1468 	s_hinst, NULL);
1469 }
1470 
1471 /*
1472  * Find the scrollbar with the given hwnd.
1473  */
1474 	 static scrollbar_T *
1475 gui_mswin_find_scrollbar(HWND hwnd)
1476 {
1477     win_T	*wp;
1478 
1479     if (gui.bottom_sbar.id == hwnd)
1480 	return &gui.bottom_sbar;
1481     FOR_ALL_WINDOWS(wp)
1482     {
1483 	if (wp->w_scrollbars[SBAR_LEFT].id == hwnd)
1484 	    return &wp->w_scrollbars[SBAR_LEFT];
1485 	if (wp->w_scrollbars[SBAR_RIGHT].id == hwnd)
1486 	    return &wp->w_scrollbars[SBAR_RIGHT];
1487     }
1488     return NULL;
1489 }
1490 
1491 /*
1492  * Get the character size of a font.
1493  */
1494     static void
1495 GetFontSize(GuiFont font)
1496 {
1497     HWND    hwnd = GetDesktopWindow();
1498     HDC	    hdc = GetWindowDC(hwnd);
1499     HFONT   hfntOld = SelectFont(hdc, (HFONT)font);
1500     TEXTMETRIC tm;
1501 
1502     GetTextMetrics(hdc, &tm);
1503     gui.char_width = tm.tmAveCharWidth + tm.tmOverhang;
1504 
1505     gui.char_height = tm.tmHeight + p_linespace;
1506 
1507     SelectFont(hdc, hfntOld);
1508 
1509     ReleaseDC(hwnd, hdc);
1510 }
1511 
1512 /*
1513  * Adjust gui.char_height (after 'linespace' was changed).
1514  */
1515     int
1516 gui_mch_adjust_charheight(void)
1517 {
1518     GetFontSize(gui.norm_font);
1519     return OK;
1520 }
1521 
1522     static GuiFont
1523 get_font_handle(LOGFONT *lf)
1524 {
1525     HFONT   font = NULL;
1526 
1527     /* Load the font */
1528     font = CreateFontIndirect(lf);
1529 
1530     if (font == NULL)
1531 	return NOFONT;
1532 
1533     return (GuiFont)font;
1534 }
1535 
1536     static int
1537 pixels_to_points(int pixels, int vertical)
1538 {
1539     int		points;
1540     HWND	hwnd;
1541     HDC		hdc;
1542 
1543     hwnd = GetDesktopWindow();
1544     hdc = GetWindowDC(hwnd);
1545 
1546     points = MulDiv(pixels, 72,
1547 		    GetDeviceCaps(hdc, vertical ? LOGPIXELSY : LOGPIXELSX));
1548 
1549     ReleaseDC(hwnd, hdc);
1550 
1551     return points;
1552 }
1553 
1554     GuiFont
1555 gui_mch_get_font(
1556     char_u	*name,
1557     int		giveErrorIfMissing)
1558 {
1559     LOGFONT	lf;
1560     GuiFont	font = NOFONT;
1561 
1562     if (get_logfont(&lf, name, NULL, giveErrorIfMissing) == OK)
1563 	font = get_font_handle(&lf);
1564     if (font == NOFONT && giveErrorIfMissing)
1565 	semsg(_(e_font), name);
1566     return font;
1567 }
1568 
1569 #if defined(FEAT_EVAL) || defined(PROTO)
1570 /*
1571  * Return the name of font "font" in allocated memory.
1572  * Don't know how to get the actual name, thus use the provided name.
1573  */
1574     char_u *
1575 gui_mch_get_fontname(GuiFont font UNUSED, char_u *name)
1576 {
1577     if (name == NULL)
1578 	return NULL;
1579     return vim_strsave(name);
1580 }
1581 #endif
1582 
1583     void
1584 gui_mch_free_font(GuiFont font)
1585 {
1586     if (font)
1587 	DeleteObject((HFONT)font);
1588 }
1589 
1590 /*
1591  * Return the Pixel value (color) for the given color name.
1592  * Return INVALCOLOR for error.
1593  */
1594     guicolor_T
1595 gui_mch_get_color(char_u *name)
1596 {
1597     int i;
1598 
1599     typedef struct SysColorTable
1600     {
1601 	char	    *name;
1602 	int	    color;
1603     } SysColorTable;
1604 
1605     static SysColorTable sys_table[] =
1606     {
1607 	{"SYS_3DDKSHADOW", COLOR_3DDKSHADOW},
1608 	{"SYS_3DHILIGHT", COLOR_3DHILIGHT},
1609 #ifdef COLOR_3DHIGHLIGHT
1610 	{"SYS_3DHIGHLIGHT", COLOR_3DHIGHLIGHT},
1611 #endif
1612 	{"SYS_BTNHILIGHT", COLOR_BTNHILIGHT},
1613 	{"SYS_BTNHIGHLIGHT", COLOR_BTNHIGHLIGHT},
1614 	{"SYS_3DLIGHT", COLOR_3DLIGHT},
1615 	{"SYS_3DSHADOW", COLOR_3DSHADOW},
1616 	{"SYS_DESKTOP", COLOR_DESKTOP},
1617 	{"SYS_INFOBK", COLOR_INFOBK},
1618 	{"SYS_INFOTEXT", COLOR_INFOTEXT},
1619 	{"SYS_3DFACE", COLOR_3DFACE},
1620 	{"SYS_BTNFACE", COLOR_BTNFACE},
1621 	{"SYS_BTNSHADOW", COLOR_BTNSHADOW},
1622 	{"SYS_ACTIVEBORDER", COLOR_ACTIVEBORDER},
1623 	{"SYS_ACTIVECAPTION", COLOR_ACTIVECAPTION},
1624 	{"SYS_APPWORKSPACE", COLOR_APPWORKSPACE},
1625 	{"SYS_BACKGROUND", COLOR_BACKGROUND},
1626 	{"SYS_BTNTEXT", COLOR_BTNTEXT},
1627 	{"SYS_CAPTIONTEXT", COLOR_CAPTIONTEXT},
1628 	{"SYS_GRAYTEXT", COLOR_GRAYTEXT},
1629 	{"SYS_HIGHLIGHT", COLOR_HIGHLIGHT},
1630 	{"SYS_HIGHLIGHTTEXT", COLOR_HIGHLIGHTTEXT},
1631 	{"SYS_INACTIVEBORDER", COLOR_INACTIVEBORDER},
1632 	{"SYS_INACTIVECAPTION", COLOR_INACTIVECAPTION},
1633 	{"SYS_INACTIVECAPTIONTEXT", COLOR_INACTIVECAPTIONTEXT},
1634 	{"SYS_MENU", COLOR_MENU},
1635 	{"SYS_MENUTEXT", COLOR_MENUTEXT},
1636 	{"SYS_SCROLLBAR", COLOR_SCROLLBAR},
1637 	{"SYS_WINDOW", COLOR_WINDOW},
1638 	{"SYS_WINDOWFRAME", COLOR_WINDOWFRAME},
1639 	{"SYS_WINDOWTEXT", COLOR_WINDOWTEXT}
1640     };
1641 
1642     /*
1643      * Try to look up a system colour.
1644      */
1645     for (i = 0; i < sizeof(sys_table) / sizeof(sys_table[0]); i++)
1646 	if (STRICMP(name, sys_table[i].name) == 0)
1647 	    return GetSysColor(sys_table[i].color);
1648 
1649     return gui_get_color_cmn(name);
1650 }
1651 
1652     guicolor_T
1653 gui_mch_get_rgb_color(int r, int g, int b)
1654 {
1655     return gui_get_rgb_color_cmn(r, g, b);
1656 }
1657 
1658 /*
1659  * Return OK if the key with the termcap name "name" is supported.
1660  */
1661     int
1662 gui_mch_haskey(char_u *name)
1663 {
1664     int i;
1665 
1666     for (i = 0; special_keys[i].vim_code1 != NUL; i++)
1667 	if (name[0] == special_keys[i].vim_code0 &&
1668 					 name[1] == special_keys[i].vim_code1)
1669 	    return OK;
1670     return FAIL;
1671 }
1672 
1673     void
1674 gui_mch_beep(void)
1675 {
1676     MessageBeep(MB_OK);
1677 }
1678 /*
1679  * Invert a rectangle from row r, column c, for nr rows and nc columns.
1680  */
1681     void
1682 gui_mch_invert_rectangle(
1683     int	    r,
1684     int	    c,
1685     int	    nr,
1686     int	    nc)
1687 {
1688     RECT    rc;
1689 
1690 #if defined(FEAT_DIRECTX)
1691     if (IS_ENABLE_DIRECTX())
1692 	DWriteContext_Flush(s_dwc);
1693 #endif
1694 
1695     /*
1696      * Note: InvertRect() excludes right and bottom of rectangle.
1697      */
1698     rc.left = FILL_X(c);
1699     rc.top = FILL_Y(r);
1700     rc.right = rc.left + nc * gui.char_width;
1701     rc.bottom = rc.top + nr * gui.char_height;
1702     InvertRect(s_hdc, &rc);
1703 }
1704 
1705 /*
1706  * Iconify the GUI window.
1707  */
1708     void
1709 gui_mch_iconify(void)
1710 {
1711     ShowWindow(s_hwnd, SW_MINIMIZE);
1712 }
1713 
1714 /*
1715  * Draw a cursor without focus.
1716  */
1717     void
1718 gui_mch_draw_hollow_cursor(guicolor_T color)
1719 {
1720     HBRUSH  hbr;
1721     RECT    rc;
1722 
1723 #if defined(FEAT_DIRECTX)
1724     if (IS_ENABLE_DIRECTX())
1725 	DWriteContext_Flush(s_dwc);
1726 #endif
1727 
1728     /*
1729      * Note: FrameRect() excludes right and bottom of rectangle.
1730      */
1731     rc.left = FILL_X(gui.col);
1732     rc.top = FILL_Y(gui.row);
1733     rc.right = rc.left + gui.char_width;
1734     if (mb_lefthalve(gui.row, gui.col))
1735 	rc.right += gui.char_width;
1736     rc.bottom = rc.top + gui.char_height;
1737     hbr = CreateSolidBrush(color);
1738     FrameRect(s_hdc, &rc, hbr);
1739     DeleteBrush(hbr);
1740 }
1741 /*
1742  * Draw part of a cursor, "w" pixels wide, and "h" pixels high, using
1743  * color "color".
1744  */
1745     void
1746 gui_mch_draw_part_cursor(
1747     int		w,
1748     int		h,
1749     guicolor_T	color)
1750 {
1751     RECT	rc;
1752 
1753     /*
1754      * Note: FillRect() excludes right and bottom of rectangle.
1755      */
1756     rc.left =
1757 #ifdef FEAT_RIGHTLEFT
1758 		/* vertical line should be on the right of current point */
1759 		CURSOR_BAR_RIGHT ? FILL_X(gui.col + 1) - w :
1760 #endif
1761 		    FILL_X(gui.col);
1762     rc.top = FILL_Y(gui.row) + gui.char_height - h;
1763     rc.right = rc.left + w;
1764     rc.bottom = rc.top + h;
1765 
1766     fill_rect(&rc, NULL, color);
1767 }
1768 
1769 
1770 /*
1771  * Generates a VK_SPACE when the internal dead_key flag is set to output the
1772  * dead key's nominal character and re-post the original message.
1773  */
1774     static void
1775 outputDeadKey_rePost(MSG originalMsg)
1776 {
1777     static MSG deadCharExpel;
1778 
1779     if (!dead_key)
1780 	return;
1781 
1782     dead_key = 0;
1783 
1784     /* Make Windows generate the dead key's character */
1785     deadCharExpel.message = originalMsg.message;
1786     deadCharExpel.hwnd    = originalMsg.hwnd;
1787     deadCharExpel.wParam  = VK_SPACE;
1788 
1789     MyTranslateMessage(&deadCharExpel);
1790 
1791     /* re-generate the current character free of the dead char influence */
1792     PostMessage(originalMsg.hwnd, originalMsg.message, originalMsg.wParam,
1793 							  originalMsg.lParam);
1794 }
1795 
1796 
1797 /*
1798  * Process a single Windows message.
1799  * If one is not available we hang until one is.
1800  */
1801     static void
1802 process_message(void)
1803 {
1804     MSG		msg;
1805     UINT	vk = 0;		/* Virtual key */
1806     char_u	string[40];
1807     int		i;
1808     int		modifiers = 0;
1809     int		key;
1810 #ifdef FEAT_MENU
1811     static char_u k10[] = {K_SPECIAL, 'k', ';', 0};
1812 #endif
1813 
1814     pGetMessage(&msg, NULL, 0, 0);
1815 
1816 #ifdef FEAT_OLE
1817     /* Look after OLE Automation commands */
1818     if (msg.message == WM_OLE)
1819     {
1820 	char_u *str = (char_u *)msg.lParam;
1821 	if (str == NULL || *str == NUL)
1822 	{
1823 	    /* Message can't be ours, forward it.  Fixes problem with Ultramon
1824 	     * 3.0.4 */
1825 	    pDispatchMessage(&msg);
1826 	}
1827 	else
1828 	{
1829 	    add_to_input_buf(str, (int)STRLEN(str));
1830 	    vim_free(str);  /* was allocated in CVim::SendKeys() */
1831 	}
1832 	return;
1833     }
1834 #endif
1835 
1836 #ifdef MSWIN_FIND_REPLACE
1837     /* Don't process messages used by the dialog */
1838     if (s_findrep_hwnd != NULL && pIsDialogMessage(s_findrep_hwnd, &msg))
1839     {
1840 	HandleMouseHide(msg.message, msg.lParam);
1841 	return;
1842     }
1843 #endif
1844 
1845     /*
1846      * Check if it's a special key that we recognise.  If not, call
1847      * TranslateMessage().
1848      */
1849     if (msg.message == WM_KEYDOWN || msg.message == WM_SYSKEYDOWN)
1850     {
1851 	vk = (int) msg.wParam;
1852 
1853 	/*
1854 	 * Handle dead keys in special conditions in other cases we let Windows
1855 	 * handle them and do not interfere.
1856 	 *
1857 	 * The dead_key flag must be reset on several occasions:
1858 	 * - in _OnChar() (or _OnSysChar()) as any dead key was necessarily
1859 	 *   consumed at that point (This is when we let Windows combine the
1860 	 *   dead character on its own)
1861 	 *
1862 	 * - Before doing something special such as regenerating keypresses to
1863 	 *   expel the dead character as this could trigger an infinite loop if
1864 	 *   for some reason MyTranslateMessage() do not trigger a call
1865 	 *   immediately to _OnChar() (or _OnSysChar()).
1866 	 */
1867 	if (dead_key)
1868 	{
1869 	    /*
1870 	     * If a dead key was pressed and the user presses VK_SPACE,
1871 	     * VK_BACK, or VK_ESCAPE it means that he actually wants to deal
1872 	     * with the dead char now, so do nothing special and let Windows
1873 	     * handle it.
1874 	     *
1875 	     * Note that VK_SPACE combines with the dead_key's character and
1876 	     * only one WM_CHAR will be generated by TranslateMessage(), in
1877 	     * the two other cases two WM_CHAR will be generated: the dead
1878 	     * char and VK_BACK or VK_ESCAPE. That is most likely what the
1879 	     * user expects.
1880 	     */
1881 	    if ((vk == VK_SPACE || vk == VK_BACK || vk == VK_ESCAPE))
1882 	    {
1883 		dead_key = 0;
1884 		MyTranslateMessage(&msg);
1885 		return;
1886 	    }
1887 	    /* In modes where we are not typing, dead keys should behave
1888 	     * normally */
1889 	    else if (!(get_real_state() & (INSERT | CMDLINE | SELECTMODE)))
1890 	    {
1891 		outputDeadKey_rePost(msg);
1892 		return;
1893 	    }
1894 	}
1895 
1896 	/* Check for CTRL-BREAK */
1897 	if (vk == VK_CANCEL)
1898 	{
1899 	    trash_input_buf();
1900 	    got_int = TRUE;
1901 	    ctrl_break_was_pressed = TRUE;
1902 	    string[0] = Ctrl_C;
1903 	    add_to_input_buf(string, 1);
1904 	}
1905 
1906 	for (i = 0; special_keys[i].key_sym != 0; i++)
1907 	{
1908 	    /* ignore VK_SPACE when ALT key pressed: system menu */
1909 	    if (special_keys[i].key_sym == vk
1910 		    && (vk != VK_SPACE || !(GetKeyState(VK_MENU) & 0x8000)))
1911 	    {
1912 		/*
1913 		 * Behave as expected if we have a dead key and the special key
1914 		 * is a key that would normally trigger the dead key nominal
1915 		 * character output (such as a NUMPAD printable character or
1916 		 * the TAB key, etc...).
1917 		 */
1918 		if (dead_key && (special_keys[i].vim_code0 == 'K'
1919 						|| vk == VK_TAB || vk == CAR))
1920 		{
1921 		    outputDeadKey_rePost(msg);
1922 		    return;
1923 		}
1924 
1925 #ifdef FEAT_MENU
1926 		/* Check for <F10>: Windows selects the menu.  When <F10> is
1927 		 * mapped we want to use the mapping instead. */
1928 		if (vk == VK_F10
1929 			&& gui.menu_is_active
1930 			&& check_map(k10, State, FALSE, TRUE, FALSE,
1931 							  NULL, NULL) == NULL)
1932 		    break;
1933 #endif
1934 		if (GetKeyState(VK_SHIFT) & 0x8000)
1935 		    modifiers |= MOD_MASK_SHIFT;
1936 		/*
1937 		 * Don't use caps-lock as shift, because these are special keys
1938 		 * being considered here, and we only want letters to get
1939 		 * shifted -- webb
1940 		 */
1941 		/*
1942 		if (GetKeyState(VK_CAPITAL) & 0x0001)
1943 		    modifiers ^= MOD_MASK_SHIFT;
1944 		*/
1945 		if (GetKeyState(VK_CONTROL) & 0x8000)
1946 		    modifiers |= MOD_MASK_CTRL;
1947 		if (GetKeyState(VK_MENU) & 0x8000)
1948 		    modifiers |= MOD_MASK_ALT;
1949 
1950 		if (special_keys[i].vim_code1 == NUL)
1951 		    key = special_keys[i].vim_code0;
1952 		else
1953 		    key = TO_SPECIAL(special_keys[i].vim_code0,
1954 						   special_keys[i].vim_code1);
1955 		key = simplify_key(key, &modifiers);
1956 		if (key == CSI)
1957 		    key = K_CSI;
1958 
1959 		if (modifiers)
1960 		{
1961 		    string[0] = CSI;
1962 		    string[1] = KS_MODIFIER;
1963 		    string[2] = modifiers;
1964 		    add_to_input_buf(string, 3);
1965 		}
1966 
1967 		if (IS_SPECIAL(key))
1968 		{
1969 		    string[0] = CSI;
1970 		    string[1] = K_SECOND(key);
1971 		    string[2] = K_THIRD(key);
1972 		    add_to_input_buf(string, 3);
1973 		}
1974 		else
1975 		{
1976 		    int	len;
1977 
1978 		    /* Handle "key" as a Unicode character. */
1979 		    len = char_to_string(key, string, 40, FALSE);
1980 		    add_to_input_buf(string, len);
1981 		}
1982 		break;
1983 	    }
1984 	}
1985 	if (special_keys[i].key_sym == 0)
1986 	{
1987 	    /* Some keys need C-S- where they should only need C-.
1988 	     * Ignore 0xff, Windows XP sends it when NUMLOCK has changed since
1989 	     * system startup (Helmut Stiegler, 2003 Oct 3). */
1990 	    if (vk != 0xff
1991 		    && (GetKeyState(VK_CONTROL) & 0x8000)
1992 		    && !(GetKeyState(VK_SHIFT) & 0x8000)
1993 		    && !(GetKeyState(VK_MENU) & 0x8000))
1994 	    {
1995 		/* CTRL-6 is '^'; Japanese keyboard maps '^' to vk == 0xDE */
1996 		if (vk == '6' || MapVirtualKey(vk, 2) == (UINT)'^')
1997 		{
1998 		    string[0] = Ctrl_HAT;
1999 		    add_to_input_buf(string, 1);
2000 		}
2001 		/* vk == 0xBD AZERTY for CTRL-'-', but CTRL-[ for * QWERTY! */
2002 		else if (vk == 0xBD)	/* QWERTY for CTRL-'-' */
2003 		{
2004 		    string[0] = Ctrl__;
2005 		    add_to_input_buf(string, 1);
2006 		}
2007 		/* CTRL-2 is '@'; Japanese keyboard maps '@' to vk == 0xC0 */
2008 		else if (vk == '2' || MapVirtualKey(vk, 2) == (UINT)'@')
2009 		{
2010 		    string[0] = Ctrl_AT;
2011 		    add_to_input_buf(string, 1);
2012 		}
2013 		else
2014 		    MyTranslateMessage(&msg);
2015 	    }
2016 	    else
2017 		MyTranslateMessage(&msg);
2018 	}
2019     }
2020 #ifdef FEAT_MBYTE_IME
2021     else if (msg.message == WM_IME_NOTIFY)
2022 	_OnImeNotify(msg.hwnd, (DWORD)msg.wParam, (DWORD)msg.lParam);
2023     else if (msg.message == WM_KEYUP && im_get_status())
2024 	/* added for non-MS IME (Yasuhiro Matsumoto) */
2025 	MyTranslateMessage(&msg);
2026 #endif
2027 #if !defined(FEAT_MBYTE_IME) && defined(GLOBAL_IME)
2028 /* GIME_TEST */
2029     else if (msg.message == WM_IME_STARTCOMPOSITION)
2030     {
2031 	POINT point;
2032 
2033 	global_ime_set_font(&norm_logfont);
2034 	point.x = FILL_X(gui.col);
2035 	point.y = FILL_Y(gui.row);
2036 	MapWindowPoints(s_textArea, s_hwnd, &point, 1);
2037 	global_ime_set_position(&point);
2038     }
2039 #endif
2040 
2041 #ifdef FEAT_MENU
2042     /* Check for <F10>: Default effect is to select the menu.  When <F10> is
2043      * mapped we need to stop it here to avoid strange effects (e.g., for the
2044      * key-up event) */
2045     if (vk != VK_F10 || check_map(k10, State, FALSE, TRUE, FALSE,
2046 							  NULL, NULL) == NULL)
2047 #endif
2048 	pDispatchMessage(&msg);
2049 }
2050 
2051 /*
2052  * Catch up with any queued events.  This may put keyboard input into the
2053  * input buffer, call resize call-backs, trigger timers etc.  If there is
2054  * nothing in the event queue (& no timers pending), then we return
2055  * immediately.
2056  */
2057     void
2058 gui_mch_update(void)
2059 {
2060     MSG	    msg;
2061 
2062     if (!s_busy_processing)
2063 	while (pPeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)
2064 						  && !vim_is_input_buf_full())
2065 	    process_message();
2066 }
2067 
2068     static void
2069 remove_any_timer(void)
2070 {
2071     MSG		msg;
2072 
2073     if (s_wait_timer != 0 && !s_timed_out)
2074     {
2075 	KillTimer(NULL, s_wait_timer);
2076 
2077 	/* Eat spurious WM_TIMER messages */
2078 	while (pPeekMessage(&msg, s_hwnd, WM_TIMER, WM_TIMER, PM_REMOVE))
2079 	    ;
2080 	s_wait_timer = 0;
2081     }
2082 }
2083 
2084 /*
2085  * GUI input routine called by gui_wait_for_chars().  Waits for a character
2086  * from the keyboard.
2087  *  wtime == -1	    Wait forever.
2088  *  wtime == 0	    This should never happen.
2089  *  wtime > 0	    Wait wtime milliseconds for a character.
2090  * Returns OK if a character was found to be available within the given time,
2091  * or FAIL otherwise.
2092  */
2093     int
2094 gui_mch_wait_for_chars(int wtime)
2095 {
2096     int		focus;
2097 
2098     s_timed_out = FALSE;
2099 
2100     if (wtime >= 0)
2101     {
2102 	// Don't do anything while processing a (scroll) message.
2103 	if (s_busy_processing)
2104 	    return FAIL;
2105 
2106 	// When called with "wtime" zero, just want one msec.
2107 	s_wait_timer = (UINT)SetTimer(NULL, 0, (UINT)(wtime == 0 ? 1 : wtime),
2108 							 (TIMERPROC)_OnTimer);
2109     }
2110 
2111     allow_scrollbar = TRUE;
2112 
2113     focus = gui.in_focus;
2114     while (!s_timed_out)
2115     {
2116 	/* Stop or start blinking when focus changes */
2117 	if (gui.in_focus != focus)
2118 	{
2119 	    if (gui.in_focus)
2120 		gui_mch_start_blink();
2121 	    else
2122 		gui_mch_stop_blink(TRUE);
2123 	    focus = gui.in_focus;
2124 	}
2125 
2126 	if (s_need_activate)
2127 	{
2128 	    (void)SetForegroundWindow(s_hwnd);
2129 	    s_need_activate = FALSE;
2130 	}
2131 
2132 #ifdef FEAT_TIMERS
2133 	did_add_timer = FALSE;
2134 #endif
2135 #ifdef MESSAGE_QUEUE
2136 	/* Check channel I/O while waiting for a message. */
2137 	for (;;)
2138 	{
2139 	    MSG msg;
2140 
2141 	    parse_queued_messages();
2142 
2143 	    if (pPeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
2144 	    {
2145 		process_message();
2146 		break;
2147 	    }
2148 	    else if (MsgWaitForMultipleObjects(0, NULL, FALSE, 100, QS_ALLINPUT)
2149 							       != WAIT_TIMEOUT)
2150 		break;
2151 	}
2152 #else
2153 	/*
2154 	 * Don't use gui_mch_update() because then we will spin-lock until a
2155 	 * char arrives, instead we use GetMessage() to hang until an
2156 	 * event arrives.  No need to check for input_buf_full because we are
2157 	 * returning as soon as it contains a single char -- webb
2158 	 */
2159 	process_message();
2160 #endif
2161 
2162 	if (input_available())
2163 	{
2164 	    remove_any_timer();
2165 	    allow_scrollbar = FALSE;
2166 
2167 	    /* Clear pending mouse button, the release event may have been
2168 	     * taken by the dialog window.  But don't do this when getting
2169 	     * focus, we need the mouse-up event then. */
2170 	    if (!s_getting_focus)
2171 		s_button_pending = -1;
2172 
2173 	    return OK;
2174 	}
2175 
2176 #ifdef FEAT_TIMERS
2177 	if (did_add_timer)
2178 	{
2179 	    /* Need to recompute the waiting time. */
2180 	    remove_any_timer();
2181 	    break;
2182 	}
2183 #endif
2184     }
2185     allow_scrollbar = FALSE;
2186     return FAIL;
2187 }
2188 
2189 /*
2190  * Clear a rectangular region of the screen from text pos (row1, col1) to
2191  * (row2, col2) inclusive.
2192  */
2193     void
2194 gui_mch_clear_block(
2195     int		row1,
2196     int		col1,
2197     int		row2,
2198     int		col2)
2199 {
2200     RECT	rc;
2201 
2202     /*
2203      * Clear one extra pixel at the far right, for when bold characters have
2204      * spilled over to the window border.
2205      * Note: FillRect() excludes right and bottom of rectangle.
2206      */
2207     rc.left = FILL_X(col1);
2208     rc.top = FILL_Y(row1);
2209     rc.right = FILL_X(col2 + 1) + (col2 == Columns - 1);
2210     rc.bottom = FILL_Y(row2 + 1);
2211     clear_rect(&rc);
2212 }
2213 
2214 /*
2215  * Clear the whole text window.
2216  */
2217     void
2218 gui_mch_clear_all(void)
2219 {
2220     RECT    rc;
2221 
2222     rc.left = 0;
2223     rc.top = 0;
2224     rc.right = Columns * gui.char_width + 2 * gui.border_width;
2225     rc.bottom = Rows * gui.char_height + 2 * gui.border_width;
2226     clear_rect(&rc);
2227 }
2228 /*
2229  * Menu stuff.
2230  */
2231 
2232     void
2233 gui_mch_enable_menu(int flag)
2234 {
2235 #ifdef FEAT_MENU
2236     SetMenu(s_hwnd, flag ? s_menuBar : NULL);
2237 #endif
2238 }
2239 
2240     void
2241 gui_mch_set_menu_pos(
2242     int	    x UNUSED,
2243     int	    y UNUSED,
2244     int	    w UNUSED,
2245     int	    h UNUSED)
2246 {
2247     /* It will be in the right place anyway */
2248 }
2249 
2250 #if defined(FEAT_MENU) || defined(PROTO)
2251 /*
2252  * Make menu item hidden or not hidden
2253  */
2254     void
2255 gui_mch_menu_hidden(
2256     vimmenu_T	*menu,
2257     int		hidden)
2258 {
2259     /*
2260      * This doesn't do what we want.  Hmm, just grey the menu items for now.
2261      */
2262     /*
2263     if (hidden)
2264 	EnableMenuItem(s_menuBar, menu->id, MF_BYCOMMAND | MF_DISABLED);
2265     else
2266 	EnableMenuItem(s_menuBar, menu->id, MF_BYCOMMAND | MF_ENABLED);
2267     */
2268     gui_mch_menu_grey(menu, hidden);
2269 }
2270 
2271 /*
2272  * This is called after setting all the menus to grey/hidden or not.
2273  */
2274     void
2275 gui_mch_draw_menubar(void)
2276 {
2277     DrawMenuBar(s_hwnd);
2278 }
2279 #endif /*FEAT_MENU*/
2280 
2281 #ifndef PROTO
2282 void
2283 #ifdef VIMDLL
2284 _export
2285 #endif
2286 _cdecl
2287 SaveInst(HINSTANCE hInst)
2288 {
2289     s_hinst = hInst;
2290 }
2291 #endif
2292 
2293 /*
2294  * Return the RGB value of a pixel as a long.
2295  */
2296     guicolor_T
2297 gui_mch_get_rgb(guicolor_T pixel)
2298 {
2299     return (guicolor_T)((GetRValue(pixel) << 16) + (GetGValue(pixel) << 8)
2300 							   + GetBValue(pixel));
2301 }
2302 
2303 #if defined(FEAT_GUI_DIALOG) || defined(PROTO)
2304 /* Convert pixels in X to dialog units */
2305     static WORD
2306 PixelToDialogX(int numPixels)
2307 {
2308     return (WORD)((numPixels * 4) / s_dlgfntwidth);
2309 }
2310 
2311 /* Convert pixels in Y to dialog units */
2312     static WORD
2313 PixelToDialogY(int numPixels)
2314 {
2315     return (WORD)((numPixels * 8) / s_dlgfntheight);
2316 }
2317 
2318 /* Return the width in pixels of the given text in the given DC. */
2319     static int
2320 GetTextWidth(HDC hdc, char_u *str, int len)
2321 {
2322     SIZE    size;
2323 
2324     GetTextExtentPoint(hdc, (LPCSTR)str, len, &size);
2325     return size.cx;
2326 }
2327 
2328 /*
2329  * Return the width in pixels of the given text in the given DC, taking care
2330  * of 'encoding' to active codepage conversion.
2331  */
2332     static int
2333 GetTextWidthEnc(HDC hdc, char_u *str, int len)
2334 {
2335     SIZE	size;
2336     WCHAR	*wstr;
2337     int		n;
2338     int		wlen = len;
2339 
2340     if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
2341     {
2342 	/* 'encoding' differs from active codepage: convert text and use wide
2343 	 * function */
2344 	wstr = enc_to_utf16(str, &wlen);
2345 	if (wstr != NULL)
2346 	{
2347 	    n = GetTextExtentPointW(hdc, wstr, wlen, &size);
2348 	    vim_free(wstr);
2349 	    if (n)
2350 		return size.cx;
2351 	}
2352     }
2353 
2354     return GetTextWidth(hdc, str, len);
2355 }
2356 
2357 static void get_work_area(RECT *spi_rect);
2358 
2359 /*
2360  * A quick little routine that will center one window over another, handy for
2361  * dialog boxes.  Taken from the Win32SDK samples and modified for multiple
2362  * monitors.
2363  */
2364     static BOOL
2365 CenterWindow(
2366     HWND hwndChild,
2367     HWND hwndParent)
2368 {
2369     HMONITOR	    mon;
2370     MONITORINFO	    moninfo;
2371     RECT	    rChild, rParent, rScreen;
2372     int		    wChild, hChild, wParent, hParent;
2373     int		    xNew, yNew;
2374     HDC		    hdc;
2375 
2376     GetWindowRect(hwndChild, &rChild);
2377     wChild = rChild.right - rChild.left;
2378     hChild = rChild.bottom - rChild.top;
2379 
2380     /* If Vim is minimized put the window in the middle of the screen. */
2381     if (hwndParent == NULL || IsMinimized(hwndParent))
2382 	get_work_area(&rParent);
2383     else
2384 	GetWindowRect(hwndParent, &rParent);
2385     wParent = rParent.right - rParent.left;
2386     hParent = rParent.bottom - rParent.top;
2387 
2388     moninfo.cbSize = sizeof(MONITORINFO);
2389     mon = MonitorFromWindow(hwndChild, MONITOR_DEFAULTTOPRIMARY);
2390     if (mon != NULL && GetMonitorInfo(mon, &moninfo))
2391     {
2392 	rScreen = moninfo.rcWork;
2393     }
2394     else
2395     {
2396 	hdc = GetDC(hwndChild);
2397 	rScreen.left = 0;
2398 	rScreen.top = 0;
2399 	rScreen.right = GetDeviceCaps(hdc, HORZRES);
2400 	rScreen.bottom = GetDeviceCaps(hdc, VERTRES);
2401 	ReleaseDC(hwndChild, hdc);
2402     }
2403 
2404     xNew = rParent.left + ((wParent - wChild) / 2);
2405     if (xNew < rScreen.left)
2406 	xNew = rScreen.left;
2407     else if ((xNew + wChild) > rScreen.right)
2408 	xNew = rScreen.right - wChild;
2409 
2410     yNew = rParent.top + ((hParent - hChild) / 2);
2411     if (yNew < rScreen.top)
2412 	yNew = rScreen.top;
2413     else if ((yNew + hChild) > rScreen.bottom)
2414 	yNew = rScreen.bottom - hChild;
2415 
2416     return SetWindowPos(hwndChild, NULL, xNew, yNew, 0, 0,
2417 						   SWP_NOSIZE | SWP_NOZORDER);
2418 }
2419 #endif /* FEAT_GUI_DIALOG */
2420 
2421 #if defined(FEAT_TOOLBAR) || defined(PROTO)
2422     void
2423 gui_mch_show_toolbar(int showit)
2424 {
2425     if (s_toolbarhwnd == NULL)
2426 	return;
2427 
2428     if (showit)
2429     {
2430 # ifndef TB_SETUNICODEFORMAT
2431     /* For older compilers.  We assume this never changes. */
2432 #  define TB_SETUNICODEFORMAT 0x2005
2433 # endif
2434 	/* Enable/disable unicode support */
2435 	int uu = (enc_codepage >= 0 && (int)GetACP() != enc_codepage);
2436 	SendMessage(s_toolbarhwnd, TB_SETUNICODEFORMAT, (WPARAM)uu, (LPARAM)0);
2437 	ShowWindow(s_toolbarhwnd, SW_SHOW);
2438     }
2439     else
2440 	ShowWindow(s_toolbarhwnd, SW_HIDE);
2441 }
2442 
2443 /* Then number of bitmaps is fixed.  Exit is missing! */
2444 #define TOOLBAR_BITMAP_COUNT 31
2445 
2446 #endif
2447 
2448 #if defined(FEAT_GUI_TABLINE) || defined(PROTO)
2449     static void
2450 add_tabline_popup_menu_entry(HMENU pmenu, UINT item_id, char_u *item_text)
2451 {
2452     WCHAR	*wn = NULL;
2453 
2454     if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
2455     {
2456 	/* 'encoding' differs from active codepage: convert menu name
2457 	 * and use wide function */
2458 	wn = enc_to_utf16(item_text, NULL);
2459 	if (wn != NULL)
2460 	{
2461 	    MENUITEMINFOW	infow;
2462 
2463 	    infow.cbSize = sizeof(infow);
2464 	    infow.fMask = MIIM_TYPE | MIIM_ID;
2465 	    infow.wID = item_id;
2466 	    infow.fType = MFT_STRING;
2467 	    infow.dwTypeData = wn;
2468 	    infow.cch = (UINT)wcslen(wn);
2469 	    InsertMenuItemW(pmenu, item_id, FALSE, &infow);
2470 	    vim_free(wn);
2471 	}
2472     }
2473 
2474     if (wn == NULL)
2475     {
2476 	MENUITEMINFO	info;
2477 
2478 	info.cbSize = sizeof(info);
2479 	info.fMask = MIIM_TYPE | MIIM_ID;
2480 	info.wID = item_id;
2481 	info.fType = MFT_STRING;
2482 	info.dwTypeData = (LPTSTR)item_text;
2483 	info.cch = (UINT)STRLEN(item_text);
2484 	InsertMenuItem(pmenu, item_id, FALSE, &info);
2485     }
2486 }
2487 
2488     static void
2489 show_tabline_popup_menu(void)
2490 {
2491     HMENU	    tab_pmenu;
2492     long	    rval;
2493     POINT	    pt;
2494 
2495     /* When ignoring events don't show the menu. */
2496     if (hold_gui_events
2497 # ifdef FEAT_CMDWIN
2498 	    || cmdwin_type != 0
2499 # endif
2500        )
2501 	return;
2502 
2503     tab_pmenu = CreatePopupMenu();
2504     if (tab_pmenu == NULL)
2505 	return;
2506 
2507     if (first_tabpage->tp_next != NULL)
2508 	add_tabline_popup_menu_entry(tab_pmenu,
2509 				TABLINE_MENU_CLOSE, (char_u *)_("Close tab"));
2510     add_tabline_popup_menu_entry(tab_pmenu,
2511 				TABLINE_MENU_NEW, (char_u *)_("New tab"));
2512     add_tabline_popup_menu_entry(tab_pmenu,
2513 				TABLINE_MENU_OPEN, (char_u *)_("Open tab..."));
2514 
2515     GetCursorPos(&pt);
2516     rval = TrackPopupMenuEx(tab_pmenu, TPM_RETURNCMD, pt.x, pt.y, s_tabhwnd,
2517 									NULL);
2518 
2519     DestroyMenu(tab_pmenu);
2520 
2521     /* Add the string cmd into input buffer */
2522     if (rval > 0)
2523     {
2524 	TCHITTESTINFO htinfo;
2525 	int idx;
2526 
2527 	if (ScreenToClient(s_tabhwnd, &pt) == 0)
2528 	    return;
2529 
2530 	htinfo.pt.x = pt.x;
2531 	htinfo.pt.y = pt.y;
2532 	idx = TabCtrl_HitTest(s_tabhwnd, &htinfo);
2533 	if (idx == -1)
2534 	    idx = 0;
2535 	else
2536 	    idx += 1;
2537 
2538 	send_tabline_menu_event(idx, (int)rval);
2539     }
2540 }
2541 
2542 /*
2543  * Show or hide the tabline.
2544  */
2545     void
2546 gui_mch_show_tabline(int showit)
2547 {
2548     if (s_tabhwnd == NULL)
2549 	return;
2550 
2551     if (!showit != !showing_tabline)
2552     {
2553 	if (showit)
2554 	    ShowWindow(s_tabhwnd, SW_SHOW);
2555 	else
2556 	    ShowWindow(s_tabhwnd, SW_HIDE);
2557 	showing_tabline = showit;
2558     }
2559 }
2560 
2561 /*
2562  * Return TRUE when tabline is displayed.
2563  */
2564     int
2565 gui_mch_showing_tabline(void)
2566 {
2567     return s_tabhwnd != NULL && showing_tabline;
2568 }
2569 
2570 /*
2571  * Update the labels of the tabline.
2572  */
2573     void
2574 gui_mch_update_tabline(void)
2575 {
2576     tabpage_T	*tp;
2577     TCITEM	tie;
2578     int		nr = 0;
2579     int		curtabidx = 0;
2580     int		tabadded = 0;
2581     static int	use_unicode = FALSE;
2582     int		uu;
2583     WCHAR	*wstr = NULL;
2584 
2585     if (s_tabhwnd == NULL)
2586 	return;
2587 
2588 #ifndef CCM_SETUNICODEFORMAT
2589     /* For older compilers.  We assume this never changes. */
2590 # define CCM_SETUNICODEFORMAT 0x2005
2591 #endif
2592     uu = (enc_codepage >= 0 && (int)GetACP() != enc_codepage);
2593     if (uu != use_unicode)
2594     {
2595 	/* Enable/disable unicode support */
2596 	SendMessage(s_tabhwnd, CCM_SETUNICODEFORMAT, (WPARAM)uu, (LPARAM)0);
2597 	use_unicode = uu;
2598     }
2599 
2600     tie.mask = TCIF_TEXT;
2601     tie.iImage = -1;
2602 
2603     /* Disable redraw for tab updates to eliminate O(N^2) draws. */
2604     SendMessage(s_tabhwnd, WM_SETREDRAW, (WPARAM)FALSE, 0);
2605 
2606     /* Add a label for each tab page.  They all contain the same text area. */
2607     for (tp = first_tabpage; tp != NULL; tp = tp->tp_next, ++nr)
2608     {
2609 	if (tp == curtab)
2610 	    curtabidx = nr;
2611 
2612 	if (nr >= TabCtrl_GetItemCount(s_tabhwnd))
2613 	{
2614 	    /* Add the tab */
2615 	    tie.pszText = "-Empty-";
2616 	    TabCtrl_InsertItem(s_tabhwnd, nr, &tie);
2617 	    tabadded = 1;
2618 	}
2619 
2620 	get_tabline_label(tp, FALSE);
2621 	tie.pszText = (LPSTR)NameBuff;
2622 	wstr = NULL;
2623 	if (use_unicode)
2624 	{
2625 	    /* Need to go through Unicode. */
2626 	    wstr = enc_to_utf16(NameBuff, NULL);
2627 	    if (wstr != NULL)
2628 	    {
2629 		TCITEMW		tiw;
2630 
2631 		tiw.mask = TCIF_TEXT;
2632 		tiw.iImage = -1;
2633 		tiw.pszText = wstr;
2634 		SendMessage(s_tabhwnd, TCM_SETITEMW, (WPARAM)nr, (LPARAM)&tiw);
2635 		vim_free(wstr);
2636 	    }
2637 	}
2638 	if (wstr == NULL)
2639 	{
2640 	    TabCtrl_SetItem(s_tabhwnd, nr, &tie);
2641 	}
2642     }
2643 
2644     /* Remove any old labels. */
2645     while (nr < TabCtrl_GetItemCount(s_tabhwnd))
2646 	TabCtrl_DeleteItem(s_tabhwnd, nr);
2647 
2648     if (!tabadded && TabCtrl_GetCurSel(s_tabhwnd) != curtabidx)
2649 	TabCtrl_SetCurSel(s_tabhwnd, curtabidx);
2650 
2651     /* Re-enable redraw and redraw. */
2652     SendMessage(s_tabhwnd, WM_SETREDRAW, (WPARAM)TRUE, 0);
2653     RedrawWindow(s_tabhwnd, NULL, NULL,
2654 		    RDW_ERASE | RDW_FRAME | RDW_INVALIDATE | RDW_ALLCHILDREN);
2655 
2656     if (tabadded && TabCtrl_GetCurSel(s_tabhwnd) != curtabidx)
2657 	TabCtrl_SetCurSel(s_tabhwnd, curtabidx);
2658 }
2659 
2660 /*
2661  * Set the current tab to "nr".  First tab is 1.
2662  */
2663     void
2664 gui_mch_set_curtab(int nr)
2665 {
2666     if (s_tabhwnd == NULL)
2667 	return;
2668 
2669     if (TabCtrl_GetCurSel(s_tabhwnd) != nr - 1)
2670 	TabCtrl_SetCurSel(s_tabhwnd, nr - 1);
2671 }
2672 
2673 #endif
2674 
2675 /*
2676  * ":simalt" command.
2677  */
2678     void
2679 ex_simalt(exarg_T *eap)
2680 {
2681     char_u	*keys = eap->arg;
2682     int		fill_typebuf = FALSE;
2683     char_u	key_name[4];
2684 
2685     PostMessage(s_hwnd, WM_SYSCOMMAND, (WPARAM)SC_KEYMENU, (LPARAM)0);
2686     while (*keys)
2687     {
2688 	if (*keys == '~')
2689 	    *keys = ' ';	    /* for showing system menu */
2690 	PostMessage(s_hwnd, WM_CHAR, (WPARAM)*keys, (LPARAM)0);
2691 	keys++;
2692 	fill_typebuf = TRUE;
2693     }
2694     if (fill_typebuf)
2695     {
2696 	/* Put a NOP in the typeahead buffer so that the message will get
2697 	 * processed. */
2698 	key_name[0] = K_SPECIAL;
2699 	key_name[1] = KS_EXTRA;
2700 	key_name[2] = KE_NOP;
2701 	key_name[3] = NUL;
2702 	typebuf_was_filled = TRUE;
2703 	(void)ins_typebuf(key_name, REMAP_NONE, 0, TRUE, FALSE);
2704     }
2705 }
2706 
2707 /*
2708  * Create the find & replace dialogs.
2709  * You can't have both at once: ":find" when replace is showing, destroys
2710  * the replace dialog first, and the other way around.
2711  */
2712 #ifdef MSWIN_FIND_REPLACE
2713     static void
2714 initialise_findrep(char_u *initial_string)
2715 {
2716     int		wword = FALSE;
2717     int		mcase = !p_ic;
2718     char_u	*entry_text;
2719 
2720     /* Get the search string to use. */
2721     entry_text = get_find_dialog_text(initial_string, &wword, &mcase);
2722 
2723     s_findrep_struct.hwndOwner = s_hwnd;
2724     s_findrep_struct.Flags = FR_DOWN;
2725     if (mcase)
2726 	s_findrep_struct.Flags |= FR_MATCHCASE;
2727     if (wword)
2728 	s_findrep_struct.Flags |= FR_WHOLEWORD;
2729     if (entry_text != NULL && *entry_text != NUL)
2730 	vim_strncpy((char_u *)s_findrep_struct.lpstrFindWhat, entry_text,
2731 					   s_findrep_struct.wFindWhatLen - 1);
2732     vim_free(entry_text);
2733 }
2734 #endif
2735 
2736     static void
2737 set_window_title(HWND hwnd, char *title)
2738 {
2739     if (title != NULL && enc_codepage >= 0 && enc_codepage != (int)GetACP())
2740     {
2741 	WCHAR	*wbuf;
2742 
2743 	/* Convert the title from 'encoding' to UTF-16. */
2744 	wbuf = (WCHAR *)enc_to_utf16((char_u *)title, NULL);
2745 	if (wbuf != NULL)
2746 	{
2747 	    SetWindowTextW(hwnd, wbuf);
2748 	    vim_free(wbuf);
2749 	}
2750 	return;
2751     }
2752     (void)SetWindowText(hwnd, (LPCSTR)title);
2753 }
2754 
2755     void
2756 gui_mch_find_dialog(exarg_T *eap)
2757 {
2758 #ifdef MSWIN_FIND_REPLACE
2759     if (s_findrep_msg != 0)
2760     {
2761 	if (IsWindow(s_findrep_hwnd) && !s_findrep_is_find)
2762 	    DestroyWindow(s_findrep_hwnd);
2763 
2764 	if (!IsWindow(s_findrep_hwnd))
2765 	{
2766 	    initialise_findrep(eap->arg);
2767 	    /* If the OS is Windows NT, and 'encoding' differs from active
2768 	     * codepage: convert text and use wide function. */
2769 	    if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
2770 	    {
2771 		findrep_atow(&s_findrep_struct_w, &s_findrep_struct);
2772 		s_findrep_hwnd = FindTextW(
2773 					(LPFINDREPLACEW) &s_findrep_struct_w);
2774 	    }
2775 	    else
2776 		s_findrep_hwnd = FindText((LPFINDREPLACE) &s_findrep_struct);
2777 	}
2778 
2779 	set_window_title(s_findrep_hwnd, _("Find string"));
2780 	(void)SetFocus(s_findrep_hwnd);
2781 
2782 	s_findrep_is_find = TRUE;
2783     }
2784 #endif
2785 }
2786 
2787 
2788     void
2789 gui_mch_replace_dialog(exarg_T *eap)
2790 {
2791 #ifdef MSWIN_FIND_REPLACE
2792     if (s_findrep_msg != 0)
2793     {
2794 	if (IsWindow(s_findrep_hwnd) && s_findrep_is_find)
2795 	    DestroyWindow(s_findrep_hwnd);
2796 
2797 	if (!IsWindow(s_findrep_hwnd))
2798 	{
2799 	    initialise_findrep(eap->arg);
2800 	    if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
2801 	    {
2802 		findrep_atow(&s_findrep_struct_w, &s_findrep_struct);
2803 		s_findrep_hwnd = ReplaceTextW(
2804 					(LPFINDREPLACEW) &s_findrep_struct_w);
2805 	    }
2806 	    else
2807 		s_findrep_hwnd = ReplaceText(
2808 					   (LPFINDREPLACE) &s_findrep_struct);
2809 	}
2810 
2811 	set_window_title(s_findrep_hwnd, _("Find & Replace"));
2812 	(void)SetFocus(s_findrep_hwnd);
2813 
2814 	s_findrep_is_find = FALSE;
2815     }
2816 #endif
2817 }
2818 
2819 
2820 /*
2821  * Set visibility of the pointer.
2822  */
2823     void
2824 gui_mch_mousehide(int hide)
2825 {
2826     if (hide != gui.pointer_hidden)
2827     {
2828 	ShowCursor(!hide);
2829 	gui.pointer_hidden = hide;
2830     }
2831 }
2832 
2833 #ifdef FEAT_MENU
2834     static void
2835 gui_mch_show_popupmenu_at(vimmenu_T *menu, int x, int y)
2836 {
2837     /* Unhide the mouse, we don't get move events here. */
2838     gui_mch_mousehide(FALSE);
2839 
2840     (void)TrackPopupMenu(
2841 	(HMENU)menu->submenu_id,
2842 	TPM_LEFTALIGN | TPM_LEFTBUTTON,
2843 	x, y,
2844 	(int)0,	    /*reserved param*/
2845 	s_hwnd,
2846 	NULL);
2847     /*
2848      * NOTE: The pop-up menu can eat the mouse up event.
2849      * We deal with this in normal.c.
2850      */
2851 }
2852 #endif
2853 
2854 /*
2855  * Got a message when the system will go down.
2856  */
2857     static void
2858 _OnEndSession(void)
2859 {
2860     getout_preserve_modified(1);
2861 }
2862 
2863 /*
2864  * Get this message when the user clicks on the cross in the top right corner
2865  * of a Windows95 window.
2866  */
2867     static void
2868 _OnClose(HWND hwnd UNUSED)
2869 {
2870     gui_shell_closed();
2871 }
2872 
2873 /*
2874  * Get a message when the window is being destroyed.
2875  */
2876     static void
2877 _OnDestroy(HWND hwnd)
2878 {
2879     if (!destroying)
2880 	_OnClose(hwnd);
2881 }
2882 
2883     static void
2884 _OnPaint(
2885     HWND hwnd)
2886 {
2887     if (!IsMinimized(hwnd))
2888     {
2889 	PAINTSTRUCT ps;
2890 
2891 	out_flush();	    /* make sure all output has been processed */
2892 	(void)BeginPaint(hwnd, &ps);
2893 
2894 	/* prevent multi-byte characters from misprinting on an invalid
2895 	 * rectangle */
2896 	if (has_mbyte)
2897 	{
2898 	    RECT rect;
2899 
2900 	    GetClientRect(hwnd, &rect);
2901 	    ps.rcPaint.left = rect.left;
2902 	    ps.rcPaint.right = rect.right;
2903 	}
2904 
2905 	if (!IsRectEmpty(&ps.rcPaint))
2906 	{
2907 	    gui_redraw(ps.rcPaint.left, ps.rcPaint.top,
2908 		    ps.rcPaint.right - ps.rcPaint.left + 1,
2909 		    ps.rcPaint.bottom - ps.rcPaint.top + 1);
2910 	}
2911 
2912 	EndPaint(hwnd, &ps);
2913     }
2914 }
2915 
2916     static void
2917 _OnSize(
2918     HWND hwnd,
2919     UINT state UNUSED,
2920     int cx,
2921     int cy)
2922 {
2923     if (!IsMinimized(hwnd))
2924     {
2925 	gui_resize_shell(cx, cy);
2926 
2927 #ifdef FEAT_MENU
2928 	/* Menu bar may wrap differently now */
2929 	gui_mswin_get_menu_height(TRUE);
2930 #endif
2931     }
2932 }
2933 
2934     static void
2935 _OnSetFocus(
2936     HWND hwnd,
2937     HWND hwndOldFocus)
2938 {
2939     gui_focus_change(TRUE);
2940     s_getting_focus = TRUE;
2941     (void)MyWindowProc(hwnd, WM_SETFOCUS, (WPARAM)hwndOldFocus, 0);
2942 }
2943 
2944     static void
2945 _OnKillFocus(
2946     HWND hwnd,
2947     HWND hwndNewFocus)
2948 {
2949     gui_focus_change(FALSE);
2950     s_getting_focus = FALSE;
2951     (void)MyWindowProc(hwnd, WM_KILLFOCUS, (WPARAM)hwndNewFocus, 0);
2952 }
2953 
2954 /*
2955  * Get a message when the user switches back to vim
2956  */
2957     static LRESULT
2958 _OnActivateApp(
2959     HWND hwnd,
2960     BOOL fActivate,
2961     DWORD dwThreadId)
2962 {
2963     /* we call gui_focus_change() in _OnSetFocus() */
2964     /* gui_focus_change((int)fActivate); */
2965     return MyWindowProc(hwnd, WM_ACTIVATEAPP, fActivate, (DWORD)dwThreadId);
2966 }
2967 
2968     void
2969 gui_mch_destroy_scrollbar(scrollbar_T *sb)
2970 {
2971     DestroyWindow(sb->id);
2972 }
2973 
2974 /*
2975  * Get current mouse coordinates in text window.
2976  */
2977     void
2978 gui_mch_getmouse(int *x, int *y)
2979 {
2980     RECT rct;
2981     POINT mp;
2982 
2983     (void)GetWindowRect(s_textArea, &rct);
2984     (void)GetCursorPos((LPPOINT)&mp);
2985     *x = (int)(mp.x - rct.left);
2986     *y = (int)(mp.y - rct.top);
2987 }
2988 
2989 /*
2990  * Move mouse pointer to character at (x, y).
2991  */
2992     void
2993 gui_mch_setmouse(int x, int y)
2994 {
2995     RECT rct;
2996 
2997     (void)GetWindowRect(s_textArea, &rct);
2998     (void)SetCursorPos(x + gui.border_offset + rct.left,
2999 		       y + gui.border_offset + rct.top);
3000 }
3001 
3002     static void
3003 gui_mswin_get_valid_dimensions(
3004     int w,
3005     int h,
3006     int *valid_w,
3007     int *valid_h)
3008 {
3009     int	    base_width, base_height;
3010 
3011     base_width = gui_get_base_width()
3012 	+ (GetSystemMetrics(SM_CXFRAME) +
3013 	   GetSystemMetrics(SM_CXPADDEDBORDER)) * 2;
3014     base_height = gui_get_base_height()
3015 	+ (GetSystemMetrics(SM_CYFRAME) +
3016 	   GetSystemMetrics(SM_CXPADDEDBORDER)) * 2
3017 	+ GetSystemMetrics(SM_CYCAPTION)
3018 #ifdef FEAT_MENU
3019 	+ gui_mswin_get_menu_height(FALSE)
3020 #endif
3021 	;
3022     *valid_w = base_width +
3023 		    ((w - base_width) / gui.char_width) * gui.char_width;
3024     *valid_h = base_height +
3025 		    ((h - base_height) / gui.char_height) * gui.char_height;
3026 }
3027 
3028     void
3029 gui_mch_flash(int msec)
3030 {
3031     RECT    rc;
3032 
3033 #if defined(FEAT_DIRECTX)
3034     if (IS_ENABLE_DIRECTX())
3035 	DWriteContext_Flush(s_dwc);
3036 #endif
3037 
3038     /*
3039      * Note: InvertRect() excludes right and bottom of rectangle.
3040      */
3041     rc.left = 0;
3042     rc.top = 0;
3043     rc.right = gui.num_cols * gui.char_width;
3044     rc.bottom = gui.num_rows * gui.char_height;
3045     InvertRect(s_hdc, &rc);
3046     gui_mch_flush();			/* make sure it's displayed */
3047 
3048     ui_delay((long)msec, TRUE);	/* wait for a few msec */
3049 
3050     InvertRect(s_hdc, &rc);
3051 }
3052 
3053 /*
3054  * Return flags used for scrolling.
3055  * The SW_INVALIDATE is required when part of the window is covered or
3056  * off-screen. Refer to MS KB Q75236.
3057  */
3058     static int
3059 get_scroll_flags(void)
3060 {
3061     HWND	hwnd;
3062     RECT	rcVim, rcOther, rcDest;
3063 
3064     GetWindowRect(s_hwnd, &rcVim);
3065 
3066     /* Check if the window is partly above or below the screen.  We don't care
3067      * about partly left or right of the screen, it is not relevant when
3068      * scrolling up or down. */
3069     if (rcVim.top < 0 || rcVim.bottom > GetSystemMetrics(SM_CYFULLSCREEN))
3070 	return SW_INVALIDATE;
3071 
3072     /* Check if there is an window (partly) on top of us. */
3073     for (hwnd = s_hwnd; (hwnd = GetWindow(hwnd, GW_HWNDPREV)) != (HWND)0; )
3074 	if (IsWindowVisible(hwnd))
3075 	{
3076 	    GetWindowRect(hwnd, &rcOther);
3077 	    if (IntersectRect(&rcDest, &rcVim, &rcOther))
3078 		return SW_INVALIDATE;
3079 	}
3080     return 0;
3081 }
3082 
3083 /*
3084  * On some Intel GPUs, the regions drawn just prior to ScrollWindowEx()
3085  * may not be scrolled out properly.
3086  * For gVim, when _OnScroll() is repeated, the character at the
3087  * previous cursor position may be left drawn after scroll.
3088  * The problem can be avoided by calling GetPixel() to get a pixel in
3089  * the region before ScrollWindowEx().
3090  */
3091     static void
3092 intel_gpu_workaround(void)
3093 {
3094     GetPixel(s_hdc, FILL_X(gui.col), FILL_Y(gui.row));
3095 }
3096 
3097 /*
3098  * Delete the given number of lines from the given row, scrolling up any
3099  * text further down within the scroll region.
3100  */
3101     void
3102 gui_mch_delete_lines(
3103     int	    row,
3104     int	    num_lines)
3105 {
3106     RECT	rc;
3107 
3108     rc.left = FILL_X(gui.scroll_region_left);
3109     rc.right = FILL_X(gui.scroll_region_right + 1);
3110     rc.top = FILL_Y(row);
3111     rc.bottom = FILL_Y(gui.scroll_region_bot + 1);
3112 
3113 #if defined(FEAT_DIRECTX)
3114     if (IS_ENABLE_DIRECTX())
3115     {
3116 	DWriteContext_Scroll(s_dwc, 0, -num_lines * gui.char_height, &rc);
3117 	DWriteContext_Flush(s_dwc);
3118     }
3119     else
3120 #endif
3121     {
3122 	intel_gpu_workaround();
3123 	ScrollWindowEx(s_textArea, 0, -num_lines * gui.char_height,
3124 				    &rc, &rc, NULL, NULL, get_scroll_flags());
3125 	UpdateWindow(s_textArea);
3126     }
3127 
3128     /* This seems to be required to avoid the cursor disappearing when
3129      * scrolling such that the cursor ends up in the top-left character on
3130      * the screen...   But why?  (Webb) */
3131     /* It's probably fixed by disabling drawing the cursor while scrolling. */
3132     /* gui.cursor_is_valid = FALSE; */
3133 
3134     gui_clear_block(gui.scroll_region_bot - num_lines + 1,
3135 						       gui.scroll_region_left,
3136 	gui.scroll_region_bot, gui.scroll_region_right);
3137 }
3138 
3139 /*
3140  * Insert the given number of lines before the given row, scrolling down any
3141  * following text within the scroll region.
3142  */
3143     void
3144 gui_mch_insert_lines(
3145     int		row,
3146     int		num_lines)
3147 {
3148     RECT	rc;
3149 
3150     rc.left = FILL_X(gui.scroll_region_left);
3151     rc.right = FILL_X(gui.scroll_region_right + 1);
3152     rc.top = FILL_Y(row);
3153     rc.bottom = FILL_Y(gui.scroll_region_bot + 1);
3154 
3155 #if defined(FEAT_DIRECTX)
3156     if (IS_ENABLE_DIRECTX())
3157     {
3158 	DWriteContext_Scroll(s_dwc, 0, num_lines * gui.char_height, &rc);
3159 	DWriteContext_Flush(s_dwc);
3160     }
3161     else
3162 #endif
3163     {
3164 	intel_gpu_workaround();
3165 	/* The SW_INVALIDATE is required when part of the window is covered or
3166 	 * off-screen.  How do we avoid it when it's not needed? */
3167 	ScrollWindowEx(s_textArea, 0, num_lines * gui.char_height,
3168 				    &rc, &rc, NULL, NULL, get_scroll_flags());
3169 	UpdateWindow(s_textArea);
3170     }
3171 
3172     gui_clear_block(row, gui.scroll_region_left,
3173 				row + num_lines - 1, gui.scroll_region_right);
3174 }
3175 
3176 
3177     void
3178 gui_mch_exit(int rc UNUSED)
3179 {
3180 #if defined(FEAT_DIRECTX)
3181     DWriteContext_Close(s_dwc);
3182     DWrite_Final();
3183     s_dwc = NULL;
3184 #endif
3185 
3186     ReleaseDC(s_textArea, s_hdc);
3187     DeleteObject(s_brush);
3188 
3189 #ifdef FEAT_TEAROFF
3190     /* Unload the tearoff bitmap */
3191     (void)DeleteObject((HGDIOBJ)s_htearbitmap);
3192 #endif
3193 
3194     /* Destroy our window (if we have one). */
3195     if (s_hwnd != NULL)
3196     {
3197 	destroying = TRUE;	/* ignore WM_DESTROY message now */
3198 	DestroyWindow(s_hwnd);
3199     }
3200 
3201 #ifdef GLOBAL_IME
3202     global_ime_end();
3203 #endif
3204 }
3205 
3206     static char_u *
3207 logfont2name(LOGFONT lf)
3208 {
3209     char	*p;
3210     char	*res;
3211     char	*charset_name;
3212     char	*quality_name;
3213     char	*font_name = lf.lfFaceName;
3214 
3215     charset_name = charset_id2name((int)lf.lfCharSet);
3216     /* Convert a font name from the current codepage to 'encoding'.
3217      * TODO: Use Wide APIs (including LOGFONTW) instead of ANSI APIs. */
3218     if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
3219     {
3220 	int	len;
3221 	acp_to_enc((char_u *)lf.lfFaceName, (int)strlen(lf.lfFaceName),
3222 						(char_u **)&font_name, &len);
3223     }
3224     quality_name = quality_id2name((int)lf.lfQuality);
3225 
3226     res = (char *)alloc((unsigned)(strlen(font_name) + 20
3227 		    + (charset_name == NULL ? 0 : strlen(charset_name) + 2)));
3228     if (res != NULL)
3229     {
3230 	p = res;
3231 	/* make a normal font string out of the lf thing:*/
3232 	sprintf((char *)p, "%s:h%d", font_name, pixels_to_points(
3233 			 lf.lfHeight < 0 ? -lf.lfHeight : lf.lfHeight, TRUE));
3234 	while (*p)
3235 	{
3236 	    if (*p == ' ')
3237 		*p = '_';
3238 	    ++p;
3239 	}
3240 	if (lf.lfItalic)
3241 	    STRCAT(p, ":i");
3242 	if (lf.lfWeight >= FW_BOLD)
3243 	    STRCAT(p, ":b");
3244 	if (lf.lfUnderline)
3245 	    STRCAT(p, ":u");
3246 	if (lf.lfStrikeOut)
3247 	    STRCAT(p, ":s");
3248 	if (charset_name != NULL)
3249 	{
3250 	    STRCAT(p, ":c");
3251 	    STRCAT(p, charset_name);
3252 	}
3253 	if (quality_name != NULL)
3254 	{
3255 	    STRCAT(p, ":q");
3256 	    STRCAT(p, quality_name);
3257 	}
3258     }
3259 
3260     if (font_name != lf.lfFaceName)
3261 	vim_free(font_name);
3262     return (char_u *)res;
3263 }
3264 
3265 
3266 #ifdef FEAT_MBYTE_IME
3267 /*
3268  * Set correct LOGFONT to IME.  Use 'guifontwide' if available, otherwise use
3269  * 'guifont'
3270  */
3271     static void
3272 update_im_font(void)
3273 {
3274     LOGFONT	lf_wide;
3275 
3276     if (p_guifontwide != NULL && *p_guifontwide != NUL
3277 	    && gui.wide_font != NOFONT
3278 	    && GetObject((HFONT)gui.wide_font, sizeof(lf_wide), &lf_wide))
3279 	norm_logfont = lf_wide;
3280     else
3281 	norm_logfont = sub_logfont;
3282     im_set_font(&norm_logfont);
3283 }
3284 #endif
3285 
3286 /*
3287  * Handler of gui.wide_font (p_guifontwide) changed notification.
3288  */
3289     void
3290 gui_mch_wide_font_changed(void)
3291 {
3292     LOGFONT lf;
3293 
3294 #ifdef FEAT_MBYTE_IME
3295     update_im_font();
3296 #endif
3297 
3298     gui_mch_free_font(gui.wide_ital_font);
3299     gui.wide_ital_font = NOFONT;
3300     gui_mch_free_font(gui.wide_bold_font);
3301     gui.wide_bold_font = NOFONT;
3302     gui_mch_free_font(gui.wide_boldital_font);
3303     gui.wide_boldital_font = NOFONT;
3304 
3305     if (gui.wide_font
3306 	&& GetObject((HFONT)gui.wide_font, sizeof(lf), &lf))
3307     {
3308 	if (!lf.lfItalic)
3309 	{
3310 	    lf.lfItalic = TRUE;
3311 	    gui.wide_ital_font = get_font_handle(&lf);
3312 	    lf.lfItalic = FALSE;
3313 	}
3314 	if (lf.lfWeight < FW_BOLD)
3315 	{
3316 	    lf.lfWeight = FW_BOLD;
3317 	    gui.wide_bold_font = get_font_handle(&lf);
3318 	    if (!lf.lfItalic)
3319 	    {
3320 		lf.lfItalic = TRUE;
3321 		gui.wide_boldital_font = get_font_handle(&lf);
3322 	    }
3323 	}
3324     }
3325 }
3326 
3327 /*
3328  * Initialise vim to use the font with the given name.
3329  * Return FAIL if the font could not be loaded, OK otherwise.
3330  */
3331     int
3332 gui_mch_init_font(char_u *font_name, int fontset UNUSED)
3333 {
3334     LOGFONT	lf;
3335     GuiFont	font = NOFONT;
3336     char_u	*p;
3337 
3338     /* Load the font */
3339     if (get_logfont(&lf, font_name, NULL, TRUE) == OK)
3340 	font = get_font_handle(&lf);
3341     if (font == NOFONT)
3342 	return FAIL;
3343 
3344     if (font_name == NULL)
3345 	font_name = (char_u *)lf.lfFaceName;
3346 #if defined(FEAT_MBYTE_IME) || defined(GLOBAL_IME)
3347     norm_logfont = lf;
3348 #endif
3349 #ifdef FEAT_MBYTE_IME
3350     sub_logfont = lf;
3351 #endif
3352 #ifdef FEAT_MBYTE_IME
3353     update_im_font();
3354 #endif
3355     gui_mch_free_font(gui.norm_font);
3356     gui.norm_font = font;
3357     current_font_height = lf.lfHeight;
3358     GetFontSize(font);
3359 
3360     p = logfont2name(lf);
3361     if (p != NULL)
3362     {
3363 	hl_set_font_name(p);
3364 
3365 	/* When setting 'guifont' to "*" replace it with the actual font name.
3366 	 * */
3367 	if (STRCMP(font_name, "*") == 0 && STRCMP(p_guifont, "*") == 0)
3368 	{
3369 	    vim_free(p_guifont);
3370 	    p_guifont = p;
3371 	}
3372 	else
3373 	    vim_free(p);
3374     }
3375 
3376     gui_mch_free_font(gui.ital_font);
3377     gui.ital_font = NOFONT;
3378     gui_mch_free_font(gui.bold_font);
3379     gui.bold_font = NOFONT;
3380     gui_mch_free_font(gui.boldital_font);
3381     gui.boldital_font = NOFONT;
3382 
3383     if (!lf.lfItalic)
3384     {
3385 	lf.lfItalic = TRUE;
3386 	gui.ital_font = get_font_handle(&lf);
3387 	lf.lfItalic = FALSE;
3388     }
3389     if (lf.lfWeight < FW_BOLD)
3390     {
3391 	lf.lfWeight = FW_BOLD;
3392 	gui.bold_font = get_font_handle(&lf);
3393 	if (!lf.lfItalic)
3394 	{
3395 	    lf.lfItalic = TRUE;
3396 	    gui.boldital_font = get_font_handle(&lf);
3397 	}
3398     }
3399 
3400     return OK;
3401 }
3402 
3403 #ifndef WPF_RESTORETOMAXIMIZED
3404 # define WPF_RESTORETOMAXIMIZED 2   /* just in case someone doesn't have it */
3405 #endif
3406 
3407 /*
3408  * Return TRUE if the GUI window is maximized, filling the whole screen.
3409  */
3410     int
3411 gui_mch_maximized(void)
3412 {
3413     WINDOWPLACEMENT wp;
3414 
3415     wp.length = sizeof(WINDOWPLACEMENT);
3416     if (GetWindowPlacement(s_hwnd, &wp))
3417 	return wp.showCmd == SW_SHOWMAXIMIZED
3418 	    || (wp.showCmd == SW_SHOWMINIMIZED
3419 		    && wp.flags == WPF_RESTORETOMAXIMIZED);
3420 
3421     return 0;
3422 }
3423 
3424 /*
3425  * Called when the font changed while the window is maximized or GO_KEEPWINSIZE
3426  * is set.  Compute the new Rows and Columns.  This is like resizing the
3427  * window.
3428  */
3429     void
3430 gui_mch_newfont(void)
3431 {
3432     RECT	rect;
3433 
3434     GetWindowRect(s_hwnd, &rect);
3435     if (win_socket_id == 0)
3436     {
3437 	gui_resize_shell(rect.right - rect.left
3438 	    - (GetSystemMetrics(SM_CXFRAME) +
3439 	       GetSystemMetrics(SM_CXPADDEDBORDER)) * 2,
3440 	    rect.bottom - rect.top
3441 	    - (GetSystemMetrics(SM_CYFRAME) +
3442 	       GetSystemMetrics(SM_CXPADDEDBORDER)) * 2
3443 	    - GetSystemMetrics(SM_CYCAPTION)
3444 #ifdef FEAT_MENU
3445 	    - gui_mswin_get_menu_height(FALSE)
3446 #endif
3447 	);
3448     }
3449     else
3450     {
3451 	/* Inside another window, don't use the frame and border. */
3452 	gui_resize_shell(rect.right - rect.left,
3453 	    rect.bottom - rect.top
3454 #ifdef FEAT_MENU
3455 			- gui_mswin_get_menu_height(FALSE)
3456 #endif
3457 	);
3458     }
3459 }
3460 
3461 /*
3462  * Set the window title
3463  */
3464     void
3465 gui_mch_settitle(
3466     char_u  *title,
3467     char_u  *icon UNUSED)
3468 {
3469     set_window_title(s_hwnd, (title == NULL ? "VIM" : (char *)title));
3470 }
3471 
3472 #if defined(FEAT_MOUSESHAPE) || defined(PROTO)
3473 /* Table for shape IDCs.  Keep in sync with the mshape_names[] table in
3474  * misc2.c! */
3475 static LPCSTR mshape_idcs[] =
3476 {
3477     IDC_ARROW,			/* arrow */
3478     MAKEINTRESOURCE(0),		/* blank */
3479     IDC_IBEAM,			/* beam */
3480     IDC_SIZENS,			/* updown */
3481     IDC_SIZENS,			/* udsizing */
3482     IDC_SIZEWE,			/* leftright */
3483     IDC_SIZEWE,			/* lrsizing */
3484     IDC_WAIT,			/* busy */
3485     IDC_NO,			/* no */
3486     IDC_ARROW,			/* crosshair */
3487     IDC_ARROW,			/* hand1 */
3488     IDC_ARROW,			/* hand2 */
3489     IDC_ARROW,			/* pencil */
3490     IDC_ARROW,			/* question */
3491     IDC_ARROW,			/* right-arrow */
3492     IDC_UPARROW,		/* up-arrow */
3493     IDC_ARROW			/* last one */
3494 };
3495 
3496     void
3497 mch_set_mouse_shape(int shape)
3498 {
3499     LPCSTR idc;
3500 
3501     if (shape == MSHAPE_HIDE)
3502 	ShowCursor(FALSE);
3503     else
3504     {
3505 	if (shape >= MSHAPE_NUMBERED)
3506 	    idc = IDC_ARROW;
3507 	else
3508 	    idc = mshape_idcs[shape];
3509 #ifdef SetClassLongPtr
3510 	SetClassLongPtr(s_textArea, GCLP_HCURSOR, (__int3264)(LONG_PTR)LoadCursor(NULL, idc));
3511 #else
3512 	SetClassLong(s_textArea, GCL_HCURSOR, (long_u)LoadCursor(NULL, idc));
3513 #endif
3514 	if (!p_mh)
3515 	{
3516 	    POINT mp;
3517 
3518 	    /* Set the position to make it redrawn with the new shape. */
3519 	    (void)GetCursorPos((LPPOINT)&mp);
3520 	    (void)SetCursorPos(mp.x, mp.y);
3521 	    ShowCursor(TRUE);
3522 	}
3523     }
3524 }
3525 #endif
3526 
3527 #if defined(FEAT_BROWSE) || defined(PROTO)
3528 /*
3529  * Wide version of convert_filter().
3530  */
3531     static WCHAR *
3532 convert_filterW(char_u *s)
3533 {
3534     char_u *tmp;
3535     int len;
3536     WCHAR *res;
3537 
3538     tmp = convert_filter(s);
3539     if (tmp == NULL)
3540 	return NULL;
3541     len = (int)STRLEN(s) + 3;
3542     res = enc_to_utf16(tmp, &len);
3543     vim_free(tmp);
3544     return res;
3545 }
3546 
3547 /*
3548  * Pop open a file browser and return the file selected, in allocated memory,
3549  * or NULL if Cancel is hit.
3550  *  saving  - TRUE if the file will be saved to, FALSE if it will be opened.
3551  *  title   - Title message for the file browser dialog.
3552  *  dflt    - Default name of file.
3553  *  ext     - Default extension to be added to files without extensions.
3554  *  initdir - directory in which to open the browser (NULL = current dir)
3555  *  filter  - Filter for matched files to choose from.
3556  */
3557     char_u *
3558 gui_mch_browse(
3559 	int saving,
3560 	char_u *title,
3561 	char_u *dflt,
3562 	char_u *ext,
3563 	char_u *initdir,
3564 	char_u *filter)
3565 {
3566     /* We always use the wide function.  This means enc_to_utf16() must work,
3567      * otherwise it fails miserably! */
3568     OPENFILENAMEW	fileStruct;
3569     WCHAR		fileBuf[MAXPATHL];
3570     WCHAR		*wp;
3571     int			i;
3572     WCHAR		*titlep = NULL;
3573     WCHAR		*extp = NULL;
3574     WCHAR		*initdirp = NULL;
3575     WCHAR		*filterp;
3576     char_u		*p, *q;
3577 
3578     if (dflt == NULL)
3579 	fileBuf[0] = NUL;
3580     else
3581     {
3582 	wp = enc_to_utf16(dflt, NULL);
3583 	if (wp == NULL)
3584 	    fileBuf[0] = NUL;
3585 	else
3586 	{
3587 	    for (i = 0; wp[i] != NUL && i < MAXPATHL - 1; ++i)
3588 		fileBuf[i] = wp[i];
3589 	    fileBuf[i] = NUL;
3590 	    vim_free(wp);
3591 	}
3592     }
3593 
3594     /* Convert the filter to Windows format. */
3595     filterp = convert_filterW(filter);
3596 
3597     vim_memset(&fileStruct, 0, sizeof(OPENFILENAMEW));
3598 #  ifdef OPENFILENAME_SIZE_VERSION_400W
3599     /* be compatible with Windows NT 4.0 */
3600     fileStruct.lStructSize = OPENFILENAME_SIZE_VERSION_400W;
3601 #  else
3602     fileStruct.lStructSize = sizeof(fileStruct);
3603 #  endif
3604 
3605     if (title != NULL)
3606 	titlep = enc_to_utf16(title, NULL);
3607     fileStruct.lpstrTitle = titlep;
3608 
3609     if (ext != NULL)
3610 	extp = enc_to_utf16(ext, NULL);
3611     fileStruct.lpstrDefExt = extp;
3612 
3613     fileStruct.lpstrFile = fileBuf;
3614     fileStruct.nMaxFile = MAXPATHL;
3615     fileStruct.lpstrFilter = filterp;
3616     fileStruct.hwndOwner = s_hwnd;		/* main Vim window is owner*/
3617     /* has an initial dir been specified? */
3618     if (initdir != NULL && *initdir != NUL)
3619     {
3620 	/* Must have backslashes here, no matter what 'shellslash' says */
3621 	initdirp = enc_to_utf16(initdir, NULL);
3622 	if (initdirp != NULL)
3623 	{
3624 	    for (wp = initdirp; *wp != NUL; ++wp)
3625 		if (*wp == '/')
3626 		    *wp = '\\';
3627 	}
3628 	fileStruct.lpstrInitialDir = initdirp;
3629     }
3630 
3631     /*
3632      * TODO: Allow selection of multiple files.  Needs another arg to this
3633      * function to ask for it, and need to use OFN_ALLOWMULTISELECT below.
3634      * Also, should we use OFN_FILEMUSTEXIST when opening?  Vim can edit on
3635      * files that don't exist yet, so I haven't put it in.  What about
3636      * OFN_PATHMUSTEXIST?
3637      * Don't use OFN_OVERWRITEPROMPT, Vim has its own ":confirm" dialog.
3638      */
3639     fileStruct.Flags = (OFN_NOCHANGEDIR | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY);
3640 #  ifdef FEAT_SHORTCUT
3641     if (curbuf->b_p_bin)
3642 	fileStruct.Flags |= OFN_NODEREFERENCELINKS;
3643 #  endif
3644     if (saving)
3645     {
3646 	if (!GetSaveFileNameW(&fileStruct))
3647 	    return NULL;
3648     }
3649     else
3650     {
3651 	if (!GetOpenFileNameW(&fileStruct))
3652 	    return NULL;
3653     }
3654 
3655     vim_free(filterp);
3656     vim_free(initdirp);
3657     vim_free(titlep);
3658     vim_free(extp);
3659 
3660     /* Convert from UCS2 to 'encoding'. */
3661     p = utf16_to_enc(fileBuf, NULL);
3662     if (p == NULL)
3663 	return NULL;
3664 
3665     /* Give focus back to main window (when using MDI). */
3666     SetFocus(s_hwnd);
3667 
3668     /* Shorten the file name if possible */
3669     q = vim_strsave(shorten_fname1(p));
3670     vim_free(p);
3671     return q;
3672 }
3673 
3674 
3675 /*
3676  * Convert the string s to the proper format for a filter string by replacing
3677  * the \t and \n delimiters with \0.
3678  * Returns the converted string in allocated memory.
3679  *
3680  * Keep in sync with convert_filterW() above!
3681  */
3682     static char_u *
3683 convert_filter(char_u *s)
3684 {
3685     char_u	*res;
3686     unsigned	s_len = (unsigned)STRLEN(s);
3687     unsigned	i;
3688 
3689     res = alloc(s_len + 3);
3690     if (res != NULL)
3691     {
3692 	for (i = 0; i < s_len; ++i)
3693 	    if (s[i] == '\t' || s[i] == '\n')
3694 		res[i] = '\0';
3695 	    else
3696 		res[i] = s[i];
3697 	res[s_len] = NUL;
3698 	/* Add two extra NULs to make sure it's properly terminated. */
3699 	res[s_len + 1] = NUL;
3700 	res[s_len + 2] = NUL;
3701     }
3702     return res;
3703 }
3704 
3705 /*
3706  * Select a directory.
3707  */
3708     char_u *
3709 gui_mch_browsedir(char_u *title, char_u *initdir)
3710 {
3711     /* We fake this: Use a filter that doesn't select anything and a default
3712      * file name that won't be used. */
3713     return gui_mch_browse(0, title, (char_u *)_("Not Used"), NULL,
3714 			      initdir, (char_u *)_("Directory\t*.nothing\n"));
3715 }
3716 #endif /* FEAT_BROWSE */
3717 
3718     static void
3719 _OnDropFiles(
3720     HWND hwnd UNUSED,
3721     HDROP hDrop)
3722 {
3723 #define BUFPATHLEN _MAX_PATH
3724 #define DRAGQVAL 0xFFFFFFFF
3725     WCHAR   wszFile[BUFPATHLEN];
3726     char    szFile[BUFPATHLEN];
3727     UINT    cFiles = DragQueryFile(hDrop, DRAGQVAL, NULL, 0);
3728     UINT    i;
3729     char_u  **fnames;
3730     POINT   pt;
3731     int_u   modifiers = 0;
3732 
3733     /* TRACE("_OnDropFiles: %d files dropped\n", cFiles); */
3734 
3735     /* Obtain dropped position */
3736     DragQueryPoint(hDrop, &pt);
3737     MapWindowPoints(s_hwnd, s_textArea, &pt, 1);
3738 
3739     reset_VIsual();
3740 
3741     fnames = (char_u **)alloc(cFiles * sizeof(char_u *));
3742 
3743     if (fnames != NULL)
3744 	for (i = 0; i < cFiles; ++i)
3745 	{
3746 	    if (DragQueryFileW(hDrop, i, wszFile, BUFPATHLEN) > 0)
3747 		fnames[i] = utf16_to_enc(wszFile, NULL);
3748 	    else
3749 	    {
3750 		DragQueryFile(hDrop, i, szFile, BUFPATHLEN);
3751 		fnames[i] = vim_strsave((char_u *)szFile);
3752 	    }
3753 	}
3754 
3755     DragFinish(hDrop);
3756 
3757     if (fnames != NULL)
3758     {
3759 	if ((GetKeyState(VK_SHIFT) & 0x8000) != 0)
3760 	    modifiers |= MOUSE_SHIFT;
3761 	if ((GetKeyState(VK_CONTROL) & 0x8000) != 0)
3762 	    modifiers |= MOUSE_CTRL;
3763 	if ((GetKeyState(VK_MENU) & 0x8000) != 0)
3764 	    modifiers |= MOUSE_ALT;
3765 
3766 	gui_handle_drop(pt.x, pt.y, modifiers, fnames, cFiles);
3767 
3768 	s_need_activate = TRUE;
3769     }
3770 }
3771 
3772     static int
3773 _OnScroll(
3774     HWND hwnd UNUSED,
3775     HWND hwndCtl,
3776     UINT code,
3777     int pos)
3778 {
3779     static UINT	prev_code = 0;   /* code of previous call */
3780     scrollbar_T *sb, *sb_info;
3781     long	val;
3782     int		dragging = FALSE;
3783     int		dont_scroll_save = dont_scroll;
3784     SCROLLINFO	si;
3785 
3786     si.cbSize = sizeof(si);
3787     si.fMask = SIF_POS;
3788 
3789     sb = gui_mswin_find_scrollbar(hwndCtl);
3790     if (sb == NULL)
3791 	return 0;
3792 
3793     if (sb->wp != NULL)		/* Left or right scrollbar */
3794     {
3795 	/*
3796 	 * Careful: need to get scrollbar info out of first (left) scrollbar
3797 	 * for window, but keep real scrollbar too because we must pass it to
3798 	 * gui_drag_scrollbar().
3799 	 */
3800 	sb_info = &sb->wp->w_scrollbars[0];
3801     }
3802     else	    /* Bottom scrollbar */
3803 	sb_info = sb;
3804     val = sb_info->value;
3805 
3806     switch (code)
3807     {
3808 	case SB_THUMBTRACK:
3809 	    val = pos;
3810 	    dragging = TRUE;
3811 	    if (sb->scroll_shift > 0)
3812 		val <<= sb->scroll_shift;
3813 	    break;
3814 	case SB_LINEDOWN:
3815 	    val++;
3816 	    break;
3817 	case SB_LINEUP:
3818 	    val--;
3819 	    break;
3820 	case SB_PAGEDOWN:
3821 	    val += (sb_info->size > 2 ? sb_info->size - 2 : 1);
3822 	    break;
3823 	case SB_PAGEUP:
3824 	    val -= (sb_info->size > 2 ? sb_info->size - 2 : 1);
3825 	    break;
3826 	case SB_TOP:
3827 	    val = 0;
3828 	    break;
3829 	case SB_BOTTOM:
3830 	    val = sb_info->max;
3831 	    break;
3832 	case SB_ENDSCROLL:
3833 	    if (prev_code == SB_THUMBTRACK)
3834 	    {
3835 		/*
3836 		 * "pos" only gives us 16-bit data.  In case of large file,
3837 		 * use GetScrollPos() which returns 32-bit.  Unfortunately it
3838 		 * is not valid while the scrollbar is being dragged.
3839 		 */
3840 		val = GetScrollPos(hwndCtl, SB_CTL);
3841 		if (sb->scroll_shift > 0)
3842 		    val <<= sb->scroll_shift;
3843 	    }
3844 	    break;
3845 
3846 	default:
3847 	    /* TRACE("Unknown scrollbar event %d\n", code); */
3848 	    return 0;
3849     }
3850     prev_code = code;
3851 
3852     si.nPos = (sb->scroll_shift > 0) ? val >> sb->scroll_shift : val;
3853     SetScrollInfo(hwndCtl, SB_CTL, &si, TRUE);
3854 
3855     /*
3856      * When moving a vertical scrollbar, move the other vertical scrollbar too.
3857      */
3858     if (sb->wp != NULL)
3859     {
3860 	scrollbar_T *sba = sb->wp->w_scrollbars;
3861 	HWND    id = sba[ (sb == sba + SBAR_LEFT) ? SBAR_RIGHT : SBAR_LEFT].id;
3862 
3863 	SetScrollInfo(id, SB_CTL, &si, TRUE);
3864     }
3865 
3866     /* Don't let us be interrupted here by another message. */
3867     s_busy_processing = TRUE;
3868 
3869     /* When "allow_scrollbar" is FALSE still need to remember the new
3870      * position, but don't actually scroll by setting "dont_scroll". */
3871     dont_scroll = !allow_scrollbar;
3872 
3873     mch_disable_flush();
3874     gui_drag_scrollbar(sb, val, dragging);
3875     mch_enable_flush();
3876     gui_may_flush();
3877 
3878     s_busy_processing = FALSE;
3879     dont_scroll = dont_scroll_save;
3880 
3881     return 0;
3882 }
3883 
3884 
3885 /*
3886  * Get command line arguments.
3887  * Use "prog" as the name of the program and "cmdline" as the arguments.
3888  * Copy the arguments to allocated memory.
3889  * Return the number of arguments (including program name).
3890  * Return pointers to the arguments in "argvp".  Memory is allocated with
3891  * malloc(), use free() instead of vim_free().
3892  * Return pointer to buffer in "tofree".
3893  * Returns zero when out of memory.
3894  */
3895     int
3896 get_cmd_args(char *prog, char *cmdline, char ***argvp, char **tofree)
3897 {
3898     int		i;
3899     char	*p;
3900     char	*progp;
3901     char	*pnew = NULL;
3902     char	*newcmdline;
3903     int		inquote;
3904     int		argc;
3905     char	**argv = NULL;
3906     int		round;
3907 
3908     *tofree = NULL;
3909 
3910     /* Try using the Unicode version first, it takes care of conversion when
3911      * 'encoding' is changed. */
3912     argc = get_cmd_argsW(&argv);
3913     if (argc != 0)
3914 	goto done;
3915 
3916     /* Handle the program name.  Remove the ".exe" extension, and find the 1st
3917      * non-space. */
3918     p = strrchr(prog, '.');
3919     if (p != NULL)
3920 	*p = NUL;
3921     for (progp = prog; *progp == ' '; ++progp)
3922 	;
3923 
3924     /* The command line is copied to allocated memory, so that we can change
3925      * it.  Add the size of the string, the separating NUL and a terminating
3926      * NUL. */
3927     newcmdline = malloc(STRLEN(cmdline) + STRLEN(progp) + 2);
3928     if (newcmdline == NULL)
3929 	return 0;
3930 
3931     /*
3932      * First round: count the number of arguments ("pnew" == NULL).
3933      * Second round: produce the arguments.
3934      */
3935     for (round = 1; round <= 2; ++round)
3936     {
3937 	/* First argument is the program name. */
3938 	if (pnew != NULL)
3939 	{
3940 	    argv[0] = pnew;
3941 	    strcpy(pnew, progp);
3942 	    pnew += strlen(pnew);
3943 	    *pnew++ = NUL;
3944 	}
3945 
3946 	/*
3947 	 * Isolate each argument and put it in argv[].
3948 	 */
3949 	p = cmdline;
3950 	argc = 1;
3951 	while (*p != NUL)
3952 	{
3953 	    inquote = FALSE;
3954 	    if (pnew != NULL)
3955 		argv[argc] = pnew;
3956 	    ++argc;
3957 	    while (*p != NUL && (inquote || (*p != ' ' && *p != '\t')))
3958 	    {
3959 		/* Backslashes are only special when followed by a double
3960 		 * quote. */
3961 		i = (int)strspn(p, "\\");
3962 		if (p[i] == '"')
3963 		{
3964 		    /* Halve the number of backslashes. */
3965 		    if (i > 1 && pnew != NULL)
3966 		    {
3967 			vim_memset(pnew, '\\', i / 2);
3968 			pnew += i / 2;
3969 		    }
3970 
3971 		    /* Even nr of backslashes toggles quoting, uneven copies
3972 		     * the double quote. */
3973 		    if ((i & 1) == 0)
3974 			inquote = !inquote;
3975 		    else if (pnew != NULL)
3976 			*pnew++ = '"';
3977 		    p += i + 1;
3978 		}
3979 		else if (i > 0)
3980 		{
3981 		    /* Copy span of backslashes unmodified. */
3982 		    if (pnew != NULL)
3983 		    {
3984 			vim_memset(pnew, '\\', i);
3985 			pnew += i;
3986 		    }
3987 		    p += i;
3988 		}
3989 		else
3990 		{
3991 		    if (pnew != NULL)
3992 			*pnew++ = *p;
3993 		    /* Can't use mb_* functions, because 'encoding' is not
3994 		     * initialized yet here. */
3995 		    if (IsDBCSLeadByte(*p))
3996 		    {
3997 			++p;
3998 			if (pnew != NULL)
3999 			    *pnew++ = *p;
4000 		    }
4001 		    ++p;
4002 		}
4003 	    }
4004 
4005 	    if (pnew != NULL)
4006 		*pnew++ = NUL;
4007 	    while (*p == ' ' || *p == '\t')
4008 		++p;		    /* advance until a non-space */
4009 	}
4010 
4011 	if (round == 1)
4012 	{
4013 	    argv = (char **)malloc((argc + 1) * sizeof(char *));
4014 	    if (argv == NULL )
4015 	    {
4016 		free(newcmdline);
4017 		return 0;		   /* malloc error */
4018 	    }
4019 	    pnew = newcmdline;
4020 	    *tofree = newcmdline;
4021 	}
4022     }
4023 
4024 done:
4025     argv[argc] = NULL;		/* NULL-terminated list */
4026     *argvp = argv;
4027     return argc;
4028 }
4029 
4030 #ifdef FEAT_XPM_W32
4031 # include "xpm_w32.h"
4032 #endif
4033 
4034 #ifdef PROTO
4035 # define WINAPI
4036 #endif
4037 
4038 #ifdef __MINGW32__
4039 /*
4040  * Add a lot of missing defines.
4041  * They are not always missing, we need the #ifndef's.
4042  */
4043 # ifndef _cdecl
4044 #  define _cdecl
4045 # endif
4046 # ifndef IsMinimized
4047 #  define     IsMinimized(hwnd)		IsIconic(hwnd)
4048 # endif
4049 # ifndef IsMaximized
4050 #  define     IsMaximized(hwnd)		IsZoomed(hwnd)
4051 # endif
4052 # ifndef SelectFont
4053 #  define     SelectFont(hdc, hfont)  ((HFONT)SelectObject((hdc), (HGDIOBJ)(HFONT)(hfont)))
4054 # endif
4055 # ifndef GetStockBrush
4056 #  define     GetStockBrush(i)     ((HBRUSH)GetStockObject(i))
4057 # endif
4058 # ifndef DeleteBrush
4059 #  define     DeleteBrush(hbr)     DeleteObject((HGDIOBJ)(HBRUSH)(hbr))
4060 # endif
4061 
4062 # ifndef HANDLE_WM_RBUTTONDBLCLK
4063 #  define HANDLE_WM_RBUTTONDBLCLK(hwnd, wParam, lParam, fn) \
4064     ((fn)((hwnd), TRUE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
4065 # endif
4066 # ifndef HANDLE_WM_MBUTTONUP
4067 #  define HANDLE_WM_MBUTTONUP(hwnd, wParam, lParam, fn) \
4068     ((fn)((hwnd), (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
4069 # endif
4070 # ifndef HANDLE_WM_MBUTTONDBLCLK
4071 #  define HANDLE_WM_MBUTTONDBLCLK(hwnd, wParam, lParam, fn) \
4072     ((fn)((hwnd), TRUE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
4073 # endif
4074 # ifndef HANDLE_WM_LBUTTONDBLCLK
4075 #  define HANDLE_WM_LBUTTONDBLCLK(hwnd, wParam, lParam, fn) \
4076     ((fn)((hwnd), TRUE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
4077 # endif
4078 # ifndef HANDLE_WM_RBUTTONDOWN
4079 #  define HANDLE_WM_RBUTTONDOWN(hwnd, wParam, lParam, fn) \
4080     ((fn)((hwnd), FALSE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
4081 # endif
4082 # ifndef HANDLE_WM_MOUSEMOVE
4083 #  define HANDLE_WM_MOUSEMOVE(hwnd, wParam, lParam, fn) \
4084     ((fn)((hwnd), (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
4085 # endif
4086 # ifndef HANDLE_WM_RBUTTONUP
4087 #  define HANDLE_WM_RBUTTONUP(hwnd, wParam, lParam, fn) \
4088     ((fn)((hwnd), (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
4089 # endif
4090 # ifndef HANDLE_WM_MBUTTONDOWN
4091 #  define HANDLE_WM_MBUTTONDOWN(hwnd, wParam, lParam, fn) \
4092     ((fn)((hwnd), FALSE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
4093 # endif
4094 # ifndef HANDLE_WM_LBUTTONUP
4095 #  define HANDLE_WM_LBUTTONUP(hwnd, wParam, lParam, fn) \
4096     ((fn)((hwnd), (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
4097 # endif
4098 # ifndef HANDLE_WM_LBUTTONDOWN
4099 #  define HANDLE_WM_LBUTTONDOWN(hwnd, wParam, lParam, fn) \
4100     ((fn)((hwnd), FALSE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
4101 # endif
4102 # ifndef HANDLE_WM_SYSCHAR
4103 #  define HANDLE_WM_SYSCHAR(hwnd, wParam, lParam, fn) \
4104     ((fn)((hwnd), (TCHAR)(wParam), (int)(short)LOWORD(lParam)), 0L)
4105 # endif
4106 # ifndef HANDLE_WM_ACTIVATEAPP
4107 #  define HANDLE_WM_ACTIVATEAPP(hwnd, wParam, lParam, fn) \
4108     ((fn)((hwnd), (BOOL)(wParam), (DWORD)(lParam)), 0L)
4109 # endif
4110 # ifndef HANDLE_WM_WINDOWPOSCHANGING
4111 #  define HANDLE_WM_WINDOWPOSCHANGING(hwnd, wParam, lParam, fn) \
4112     (LRESULT)(DWORD)(BOOL)(fn)((hwnd), (LPWINDOWPOS)(lParam))
4113 # endif
4114 # ifndef HANDLE_WM_VSCROLL
4115 #  define HANDLE_WM_VSCROLL(hwnd, wParam, lParam, fn) \
4116     ((fn)((hwnd), (HWND)(lParam), (UINT)(LOWORD(wParam)),  (int)(short)HIWORD(wParam)), 0L)
4117 # endif
4118 # ifndef HANDLE_WM_SETFOCUS
4119 #  define HANDLE_WM_SETFOCUS(hwnd, wParam, lParam, fn) \
4120     ((fn)((hwnd), (HWND)(wParam)), 0L)
4121 # endif
4122 # ifndef HANDLE_WM_KILLFOCUS
4123 #  define HANDLE_WM_KILLFOCUS(hwnd, wParam, lParam, fn) \
4124     ((fn)((hwnd), (HWND)(wParam)), 0L)
4125 # endif
4126 # ifndef HANDLE_WM_HSCROLL
4127 #  define HANDLE_WM_HSCROLL(hwnd, wParam, lParam, fn) \
4128     ((fn)((hwnd), (HWND)(lParam), (UINT)(LOWORD(wParam)), (int)(short)HIWORD(wParam)), 0L)
4129 # endif
4130 # ifndef HANDLE_WM_DROPFILES
4131 #  define HANDLE_WM_DROPFILES(hwnd, wParam, lParam, fn) \
4132     ((fn)((hwnd), (HDROP)(wParam)), 0L)
4133 # endif
4134 # ifndef HANDLE_WM_CHAR
4135 #  define HANDLE_WM_CHAR(hwnd, wParam, lParam, fn) \
4136     ((fn)((hwnd), (TCHAR)(wParam), (int)(short)LOWORD(lParam)), 0L)
4137 # endif
4138 # ifndef HANDLE_WM_SYSDEADCHAR
4139 #  define HANDLE_WM_SYSDEADCHAR(hwnd, wParam, lParam, fn) \
4140     ((fn)((hwnd), (TCHAR)(wParam), (int)(short)LOWORD(lParam)), 0L)
4141 # endif
4142 # ifndef HANDLE_WM_DEADCHAR
4143 #  define HANDLE_WM_DEADCHAR(hwnd, wParam, lParam, fn) \
4144     ((fn)((hwnd), (TCHAR)(wParam), (int)(short)LOWORD(lParam)), 0L)
4145 # endif
4146 #endif /* __MINGW32__ */
4147 
4148 
4149 /* Some parameters for tearoff menus.  All in pixels. */
4150 #define TEAROFF_PADDING_X	2
4151 #define TEAROFF_BUTTON_PAD_X	8
4152 #define TEAROFF_MIN_WIDTH	200
4153 #define TEAROFF_SUBMENU_LABEL	">>"
4154 #define TEAROFF_COLUMN_PADDING	3	// # spaces to pad column with.
4155 
4156 
4157 /* For the Intellimouse: */
4158 #ifndef WM_MOUSEWHEEL
4159 #define WM_MOUSEWHEEL	0x20a
4160 #endif
4161 
4162 
4163 #ifdef FEAT_BEVAL_GUI
4164 # define ID_BEVAL_TOOLTIP   200
4165 # define BEVAL_TEXT_LEN	    MAXPATHL
4166 
4167 #if (defined(_MSC_VER) && _MSC_VER < 1300) || !defined(MAXULONG_PTR)
4168 /* Work around old versions of basetsd.h which wrongly declares
4169  * UINT_PTR as unsigned long. */
4170 # undef  UINT_PTR
4171 # define UINT_PTR UINT
4172 #endif
4173 
4174 static BalloonEval  *cur_beval = NULL;
4175 static UINT_PTR	    BevalTimerId = 0;
4176 static DWORD	    LastActivity = 0;
4177 
4178 
4179 /* cproto fails on missing include files */
4180 #ifndef PROTO
4181 
4182 /*
4183  * excerpts from headers since this may not be presented
4184  * in the extremely old compilers
4185  */
4186 # include <pshpack1.h>
4187 
4188 #endif
4189 
4190 typedef struct _DllVersionInfo
4191 {
4192     DWORD cbSize;
4193     DWORD dwMajorVersion;
4194     DWORD dwMinorVersion;
4195     DWORD dwBuildNumber;
4196     DWORD dwPlatformID;
4197 } DLLVERSIONINFO;
4198 
4199 #ifndef PROTO
4200 # include <poppack.h>
4201 #endif
4202 
4203 typedef struct tagTOOLINFOA_NEW
4204 {
4205     UINT       cbSize;
4206     UINT       uFlags;
4207     HWND       hwnd;
4208     UINT_PTR   uId;
4209     RECT       rect;
4210     HINSTANCE  hinst;
4211     LPSTR      lpszText;
4212     LPARAM     lParam;
4213 } TOOLINFO_NEW;
4214 
4215 typedef struct tagNMTTDISPINFO_NEW
4216 {
4217     NMHDR      hdr;
4218     LPSTR      lpszText;
4219     char       szText[80];
4220     HINSTANCE  hinst;
4221     UINT       uFlags;
4222     LPARAM     lParam;
4223 } NMTTDISPINFO_NEW;
4224 
4225 typedef struct tagTOOLINFOW_NEW
4226 {
4227     UINT       cbSize;
4228     UINT       uFlags;
4229     HWND       hwnd;
4230     UINT_PTR   uId;
4231     RECT       rect;
4232     HINSTANCE  hinst;
4233     LPWSTR     lpszText;
4234     LPARAM     lParam;
4235     void       *lpReserved;
4236 } TOOLINFOW_NEW;
4237 
4238 typedef struct tagNMTTDISPINFOW_NEW
4239 {
4240     NMHDR      hdr;
4241     LPWSTR     lpszText;
4242     WCHAR      szText[80];
4243     HINSTANCE  hinst;
4244     UINT       uFlags;
4245     LPARAM     lParam;
4246 } NMTTDISPINFOW_NEW;
4247 
4248 
4249 typedef HRESULT (WINAPI* DLLGETVERSIONPROC)(DLLVERSIONINFO *);
4250 #ifndef TTM_SETMAXTIPWIDTH
4251 # define TTM_SETMAXTIPWIDTH	(WM_USER+24)
4252 #endif
4253 
4254 #ifndef TTF_DI_SETITEM
4255 # define TTF_DI_SETITEM		0x8000
4256 #endif
4257 
4258 #ifndef TTN_GETDISPINFO
4259 # define TTN_GETDISPINFO	(TTN_FIRST - 0)
4260 #endif
4261 
4262 #endif /* defined(FEAT_BEVAL_GUI) */
4263 
4264 #if defined(FEAT_TOOLBAR) || defined(FEAT_GUI_TABLINE)
4265 /* Older MSVC compilers don't have LPNMTTDISPINFO[AW] thus we need to define
4266  * it here if LPNMTTDISPINFO isn't defined.
4267  * MingW doesn't define LPNMTTDISPINFO but typedefs it.  Thus we need to check
4268  * _MSC_VER. */
4269 # if !defined(LPNMTTDISPINFO) && defined(_MSC_VER)
4270 typedef struct tagNMTTDISPINFOA {
4271     NMHDR	hdr;
4272     LPSTR	lpszText;
4273     char	szText[80];
4274     HINSTANCE	hinst;
4275     UINT	uFlags;
4276     LPARAM	lParam;
4277 } NMTTDISPINFOA, *LPNMTTDISPINFOA;
4278 #  define LPNMTTDISPINFO LPNMTTDISPINFOA
4279 
4280 typedef struct tagNMTTDISPINFOW {
4281     NMHDR	hdr;
4282     LPWSTR	lpszText;
4283     WCHAR	szText[80];
4284     HINSTANCE	hinst;
4285     UINT	uFlags;
4286     LPARAM	lParam;
4287 } NMTTDISPINFOW, *LPNMTTDISPINFOW;
4288 # endif
4289 #endif
4290 
4291 #ifndef TTN_GETDISPINFOW
4292 # define TTN_GETDISPINFOW	(TTN_FIRST - 10)
4293 #endif
4294 
4295 /* Local variables: */
4296 
4297 #ifdef FEAT_MENU
4298 static UINT	s_menu_id = 100;
4299 #endif
4300 
4301 /*
4302  * Use the system font for dialogs and tear-off menus.  Remove this line to
4303  * use DLG_FONT_NAME.
4304  */
4305 #define USE_SYSMENU_FONT
4306 
4307 #define VIM_NAME	"vim"
4308 #define VIM_CLASS	"Vim"
4309 #define VIM_CLASSW	L"Vim"
4310 
4311 /* Initial size for the dialog template.  For gui_mch_dialog() it's fixed,
4312  * thus there should be room for every dialog.  For tearoffs it's made bigger
4313  * when needed. */
4314 #define DLG_ALLOC_SIZE 16 * 1024
4315 
4316 /*
4317  * stuff for dialogs, menus, tearoffs etc.
4318  */
4319 static PWORD
4320 add_dialog_element(
4321 	PWORD p,
4322 	DWORD lStyle,
4323 	WORD x,
4324 	WORD y,
4325 	WORD w,
4326 	WORD h,
4327 	WORD Id,
4328 	WORD clss,
4329 	const char *caption);
4330 static LPWORD lpwAlign(LPWORD);
4331 static int nCopyAnsiToWideChar(LPWORD, LPSTR, BOOL);
4332 #if defined(FEAT_MENU) && defined(FEAT_TEAROFF)
4333 static void gui_mch_tearoff(char_u *title, vimmenu_T *menu, int initX, int initY);
4334 #endif
4335 static void get_dialog_font_metrics(void);
4336 
4337 static int dialog_default_button = -1;
4338 
4339 /* Intellimouse support */
4340 static int mouse_scroll_lines = 0;
4341 
4342 static int	s_usenewlook;	    /* emulate W95/NT4 non-bold dialogs */
4343 #ifdef FEAT_TOOLBAR
4344 static void initialise_toolbar(void);
4345 static LRESULT CALLBACK toolbar_wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
4346 static int get_toolbar_bitmap(vimmenu_T *menu);
4347 #endif
4348 
4349 #ifdef FEAT_GUI_TABLINE
4350 static void initialise_tabline(void);
4351 static LRESULT CALLBACK tabline_wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
4352 #endif
4353 
4354 #ifdef FEAT_MBYTE_IME
4355 static LRESULT _OnImeComposition(HWND hwnd, WPARAM dbcs, LPARAM param);
4356 static char_u *GetResultStr(HWND hwnd, int GCS, int *lenp);
4357 #endif
4358 #if defined(FEAT_MBYTE_IME) && defined(DYNAMIC_IME)
4359 # ifdef NOIME
4360 typedef struct tagCOMPOSITIONFORM {
4361     DWORD dwStyle;
4362     POINT ptCurrentPos;
4363     RECT  rcArea;
4364 } COMPOSITIONFORM, *PCOMPOSITIONFORM, NEAR *NPCOMPOSITIONFORM, FAR *LPCOMPOSITIONFORM;
4365 typedef HANDLE HIMC;
4366 # endif
4367 
4368 static HINSTANCE hLibImm = NULL;
4369 static LONG (WINAPI *pImmGetCompositionStringA)(HIMC, DWORD, LPVOID, DWORD);
4370 static LONG (WINAPI *pImmGetCompositionStringW)(HIMC, DWORD, LPVOID, DWORD);
4371 static HIMC (WINAPI *pImmGetContext)(HWND);
4372 static HIMC (WINAPI *pImmAssociateContext)(HWND, HIMC);
4373 static BOOL (WINAPI *pImmReleaseContext)(HWND, HIMC);
4374 static BOOL (WINAPI *pImmGetOpenStatus)(HIMC);
4375 static BOOL (WINAPI *pImmSetOpenStatus)(HIMC, BOOL);
4376 static BOOL (WINAPI *pImmGetCompositionFont)(HIMC, LPLOGFONTA);
4377 static BOOL (WINAPI *pImmSetCompositionFont)(HIMC, LPLOGFONTA);
4378 static BOOL (WINAPI *pImmSetCompositionWindow)(HIMC, LPCOMPOSITIONFORM);
4379 static BOOL (WINAPI *pImmGetConversionStatus)(HIMC, LPDWORD, LPDWORD);
4380 static BOOL (WINAPI *pImmSetConversionStatus)(HIMC, DWORD, DWORD);
4381 static void dyn_imm_load(void);
4382 #else
4383 # define pImmGetCompositionStringA ImmGetCompositionStringA
4384 # define pImmGetCompositionStringW ImmGetCompositionStringW
4385 # define pImmGetContext		  ImmGetContext
4386 # define pImmAssociateContext	  ImmAssociateContext
4387 # define pImmReleaseContext	  ImmReleaseContext
4388 # define pImmGetOpenStatus	  ImmGetOpenStatus
4389 # define pImmSetOpenStatus	  ImmSetOpenStatus
4390 # define pImmGetCompositionFont   ImmGetCompositionFontA
4391 # define pImmSetCompositionFont   ImmSetCompositionFontA
4392 # define pImmSetCompositionWindow ImmSetCompositionWindow
4393 # define pImmGetConversionStatus  ImmGetConversionStatus
4394 # define pImmSetConversionStatus  ImmSetConversionStatus
4395 #endif
4396 
4397 #ifdef FEAT_MENU
4398 /*
4399  * Figure out how high the menu bar is at the moment.
4400  */
4401     static int
4402 gui_mswin_get_menu_height(
4403     int	    fix_window)	    /* If TRUE, resize window if menu height changed */
4404 {
4405     static int	old_menu_height = -1;
4406 
4407     RECT    rc1, rc2;
4408     int	    num;
4409     int	    menu_height;
4410 
4411     if (gui.menu_is_active)
4412 	num = GetMenuItemCount(s_menuBar);
4413     else
4414 	num = 0;
4415 
4416     if (num == 0)
4417 	menu_height = 0;
4418     else if (IsMinimized(s_hwnd))
4419     {
4420 	/* The height of the menu cannot be determined while the window is
4421 	 * minimized.  Take the previous height if the menu is changed in that
4422 	 * state, to avoid that Vim's vertical window size accidentally
4423 	 * increases due to the unaccounted-for menu height. */
4424 	menu_height = old_menu_height == -1 ? 0 : old_menu_height;
4425     }
4426     else
4427     {
4428 	/*
4429 	 * In case 'lines' is set in _vimrc/_gvimrc window width doesn't
4430 	 * seem to have been set yet, so menu wraps in default window
4431 	 * width which is very narrow.  Instead just return height of a
4432 	 * single menu item.  Will still be wrong when the menu really
4433 	 * should wrap over more than one line.
4434 	 */
4435 	GetMenuItemRect(s_hwnd, s_menuBar, 0, &rc1);
4436 	if (gui.starting)
4437 	    menu_height = rc1.bottom - rc1.top + 1;
4438 	else
4439 	{
4440 	    GetMenuItemRect(s_hwnd, s_menuBar, num - 1, &rc2);
4441 	    menu_height = rc2.bottom - rc1.top + 1;
4442 	}
4443     }
4444 
4445     if (fix_window && menu_height != old_menu_height)
4446     {
4447 	gui_set_shellsize(FALSE, FALSE, RESIZE_VERT);
4448     }
4449     old_menu_height = menu_height;
4450 
4451     return menu_height;
4452 }
4453 #endif /*FEAT_MENU*/
4454 
4455 
4456 /*
4457  * Setup for the Intellimouse
4458  */
4459     static void
4460 init_mouse_wheel(void)
4461 {
4462 
4463 #ifndef SPI_GETWHEELSCROLLLINES
4464 # define SPI_GETWHEELSCROLLLINES    104
4465 #endif
4466 #ifndef SPI_SETWHEELSCROLLLINES
4467 # define SPI_SETWHEELSCROLLLINES    105
4468 #endif
4469 
4470 #define VMOUSEZ_CLASSNAME  "MouseZ"		/* hidden wheel window class */
4471 #define VMOUSEZ_TITLE      "Magellan MSWHEEL"	/* hidden wheel window title */
4472 #define VMSH_MOUSEWHEEL    "MSWHEEL_ROLLMSG"
4473 #define VMSH_SCROLL_LINES  "MSH_SCROLL_LINES_MSG"
4474 
4475     mouse_scroll_lines = 3;	/* reasonable default */
4476 
4477     /* if NT 4.0+ (or Win98) get scroll lines directly from system */
4478     SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0,
4479 	    &mouse_scroll_lines, 0);
4480 }
4481 
4482 
4483 /* Intellimouse wheel handler */
4484     static void
4485 _OnMouseWheel(
4486     HWND hwnd,
4487     short zDelta)
4488 {
4489 /* Treat a mouse wheel event as if it were a scroll request */
4490     int i;
4491     int size;
4492     HWND hwndCtl;
4493 
4494     if (curwin->w_scrollbars[SBAR_RIGHT].id != 0)
4495     {
4496 	hwndCtl = curwin->w_scrollbars[SBAR_RIGHT].id;
4497 	size = curwin->w_scrollbars[SBAR_RIGHT].size;
4498     }
4499     else if (curwin->w_scrollbars[SBAR_LEFT].id != 0)
4500     {
4501 	hwndCtl = curwin->w_scrollbars[SBAR_LEFT].id;
4502 	size = curwin->w_scrollbars[SBAR_LEFT].size;
4503     }
4504     else
4505 	return;
4506 
4507     size = curwin->w_height;
4508     if (mouse_scroll_lines == 0)
4509 	init_mouse_wheel();
4510 
4511     mch_disable_flush();
4512     if (mouse_scroll_lines > 0
4513 	    && mouse_scroll_lines < (size > 2 ? size - 2 : 1))
4514     {
4515 	for (i = mouse_scroll_lines; i > 0; --i)
4516 	    _OnScroll(hwnd, hwndCtl, zDelta >= 0 ? SB_LINEUP : SB_LINEDOWN, 0);
4517     }
4518     else
4519 	_OnScroll(hwnd, hwndCtl, zDelta >= 0 ? SB_PAGEUP : SB_PAGEDOWN, 0);
4520     mch_enable_flush();
4521     gui_may_flush();
4522 }
4523 
4524 #ifdef USE_SYSMENU_FONT
4525 /*
4526  * Get Menu Font.
4527  * Return OK or FAIL.
4528  */
4529     static int
4530 gui_w32_get_menu_font(LOGFONT *lf)
4531 {
4532     NONCLIENTMETRICS nm;
4533 
4534     nm.cbSize = sizeof(NONCLIENTMETRICS);
4535     if (!SystemParametersInfo(
4536 	    SPI_GETNONCLIENTMETRICS,
4537 	    sizeof(NONCLIENTMETRICS),
4538 	    &nm,
4539 	    0))
4540 	return FAIL;
4541     *lf = nm.lfMenuFont;
4542     return OK;
4543 }
4544 #endif
4545 
4546 
4547 #if defined(FEAT_GUI_TABLINE) && defined(USE_SYSMENU_FONT)
4548 /*
4549  * Set the GUI tabline font to the system menu font
4550  */
4551     static void
4552 set_tabline_font(void)
4553 {
4554     LOGFONT	lfSysmenu;
4555     HFONT	font;
4556     HWND	hwnd;
4557     HDC		hdc;
4558     HFONT	hfntOld;
4559     TEXTMETRIC	tm;
4560 
4561     if (gui_w32_get_menu_font(&lfSysmenu) != OK)
4562 	return;
4563 
4564     font = CreateFontIndirect(&lfSysmenu);
4565 
4566     SendMessage(s_tabhwnd, WM_SETFONT, (WPARAM)font, TRUE);
4567 
4568     /*
4569      * Compute the height of the font used for the tab text
4570      */
4571     hwnd = GetDesktopWindow();
4572     hdc = GetWindowDC(hwnd);
4573     hfntOld = SelectFont(hdc, font);
4574 
4575     GetTextMetrics(hdc, &tm);
4576 
4577     SelectFont(hdc, hfntOld);
4578     ReleaseDC(hwnd, hdc);
4579 
4580     /*
4581      * The space used by the tab border and the space between the tab label
4582      * and the tab border is included as 7.
4583      */
4584     gui.tabline_height = tm.tmHeight + tm.tmInternalLeading + 7;
4585 }
4586 #endif
4587 
4588 /*
4589  * Invoked when a setting was changed.
4590  */
4591     static LRESULT CALLBACK
4592 _OnSettingChange(UINT n)
4593 {
4594     if (n == SPI_SETWHEELSCROLLLINES)
4595 	SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0,
4596 		&mouse_scroll_lines, 0);
4597 #if defined(FEAT_GUI_TABLINE) && defined(USE_SYSMENU_FONT)
4598     if (n == SPI_SETNONCLIENTMETRICS)
4599 	set_tabline_font();
4600 #endif
4601     return 0;
4602 }
4603 
4604 #ifdef FEAT_NETBEANS_INTG
4605     static void
4606 _OnWindowPosChanged(
4607     HWND hwnd,
4608     const LPWINDOWPOS lpwpos)
4609 {
4610     static int x = 0, y = 0, cx = 0, cy = 0;
4611     extern int WSInitialized;
4612 
4613     if (WSInitialized && (lpwpos->x != x || lpwpos->y != y
4614 				     || lpwpos->cx != cx || lpwpos->cy != cy))
4615     {
4616 	x = lpwpos->x;
4617 	y = lpwpos->y;
4618 	cx = lpwpos->cx;
4619 	cy = lpwpos->cy;
4620 	netbeans_frame_moved(x, y);
4621     }
4622     /* Allow to send WM_SIZE and WM_MOVE */
4623     FORWARD_WM_WINDOWPOSCHANGED(hwnd, lpwpos, MyWindowProc);
4624 }
4625 #endif
4626 
4627     static int
4628 _DuringSizing(
4629     UINT fwSide,
4630     LPRECT lprc)
4631 {
4632     int	    w, h;
4633     int	    valid_w, valid_h;
4634     int	    w_offset, h_offset;
4635 
4636     w = lprc->right - lprc->left;
4637     h = lprc->bottom - lprc->top;
4638     gui_mswin_get_valid_dimensions(w, h, &valid_w, &valid_h);
4639     w_offset = w - valid_w;
4640     h_offset = h - valid_h;
4641 
4642     if (fwSide == WMSZ_LEFT || fwSide == WMSZ_TOPLEFT
4643 			    || fwSide == WMSZ_BOTTOMLEFT)
4644 	lprc->left += w_offset;
4645     else if (fwSide == WMSZ_RIGHT || fwSide == WMSZ_TOPRIGHT
4646 			    || fwSide == WMSZ_BOTTOMRIGHT)
4647 	lprc->right -= w_offset;
4648 
4649     if (fwSide == WMSZ_TOP || fwSide == WMSZ_TOPLEFT
4650 			    || fwSide == WMSZ_TOPRIGHT)
4651 	lprc->top += h_offset;
4652     else if (fwSide == WMSZ_BOTTOM || fwSide == WMSZ_BOTTOMLEFT
4653 			    || fwSide == WMSZ_BOTTOMRIGHT)
4654 	lprc->bottom -= h_offset;
4655     return TRUE;
4656 }
4657 
4658 
4659 
4660     static LRESULT CALLBACK
4661 _WndProc(
4662     HWND hwnd,
4663     UINT uMsg,
4664     WPARAM wParam,
4665     LPARAM lParam)
4666 {
4667     /*
4668     TRACE("WndProc: hwnd = %08x, msg = %x, wParam = %x, lParam = %x\n",
4669 	  hwnd, uMsg, wParam, lParam);
4670     */
4671 
4672     HandleMouseHide(uMsg, lParam);
4673 
4674     s_uMsg = uMsg;
4675     s_wParam = wParam;
4676     s_lParam = lParam;
4677 
4678     switch (uMsg)
4679     {
4680 	HANDLE_MSG(hwnd, WM_DEADCHAR,	_OnDeadChar);
4681 	HANDLE_MSG(hwnd, WM_SYSDEADCHAR, _OnDeadChar);
4682 	/* HANDLE_MSG(hwnd, WM_ACTIVATE,    _OnActivate); */
4683 	HANDLE_MSG(hwnd, WM_CLOSE,	_OnClose);
4684 	/* HANDLE_MSG(hwnd, WM_COMMAND,	_OnCommand); */
4685 	HANDLE_MSG(hwnd, WM_DESTROY,	_OnDestroy);
4686 	HANDLE_MSG(hwnd, WM_DROPFILES,	_OnDropFiles);
4687 	HANDLE_MSG(hwnd, WM_HSCROLL,	_OnScroll);
4688 	HANDLE_MSG(hwnd, WM_KILLFOCUS,	_OnKillFocus);
4689 #ifdef FEAT_MENU
4690 	HANDLE_MSG(hwnd, WM_COMMAND,	_OnMenu);
4691 #endif
4692 	/* HANDLE_MSG(hwnd, WM_MOVE,	    _OnMove); */
4693 	/* HANDLE_MSG(hwnd, WM_NCACTIVATE,  _OnNCActivate); */
4694 	HANDLE_MSG(hwnd, WM_SETFOCUS,	_OnSetFocus);
4695 	HANDLE_MSG(hwnd, WM_SIZE,	_OnSize);
4696 	/* HANDLE_MSG(hwnd, WM_SYSCOMMAND,  _OnSysCommand); */
4697 	/* HANDLE_MSG(hwnd, WM_SYSKEYDOWN,  _OnAltKey); */
4698 	HANDLE_MSG(hwnd, WM_VSCROLL,	_OnScroll);
4699 	// HANDLE_MSG(hwnd, WM_WINDOWPOSCHANGING,	_OnWindowPosChanging);
4700 	HANDLE_MSG(hwnd, WM_ACTIVATEAPP, _OnActivateApp);
4701 #ifdef FEAT_NETBEANS_INTG
4702 	HANDLE_MSG(hwnd, WM_WINDOWPOSCHANGED, _OnWindowPosChanged);
4703 #endif
4704 
4705 #ifdef FEAT_GUI_TABLINE
4706 	case WM_RBUTTONUP:
4707 	{
4708 	    if (gui_mch_showing_tabline())
4709 	    {
4710 		POINT pt;
4711 		RECT rect;
4712 
4713 		/*
4714 		 * If the cursor is on the tabline, display the tab menu
4715 		 */
4716 		GetCursorPos((LPPOINT)&pt);
4717 		GetWindowRect(s_textArea, &rect);
4718 		if (pt.y < rect.top)
4719 		{
4720 		    show_tabline_popup_menu();
4721 		    return 0L;
4722 		}
4723 	    }
4724 	    return MyWindowProc(hwnd, uMsg, wParam, lParam);
4725 	}
4726 	case WM_LBUTTONDBLCLK:
4727 	{
4728 	    /*
4729 	     * If the user double clicked the tabline, create a new tab
4730 	     */
4731 	    if (gui_mch_showing_tabline())
4732 	    {
4733 		POINT pt;
4734 		RECT rect;
4735 
4736 		GetCursorPos((LPPOINT)&pt);
4737 		GetWindowRect(s_textArea, &rect);
4738 		if (pt.y < rect.top)
4739 		    send_tabline_menu_event(0, TABLINE_MENU_NEW);
4740 	    }
4741 	    return MyWindowProc(hwnd, uMsg, wParam, lParam);
4742 	}
4743 #endif
4744 
4745     case WM_QUERYENDSESSION:	/* System wants to go down. */
4746 	gui_shell_closed();	/* Will exit when no changed buffers. */
4747 	return FALSE;		/* Do NOT allow system to go down. */
4748 
4749     case WM_ENDSESSION:
4750 	if (wParam)	/* system only really goes down when wParam is TRUE */
4751 	{
4752 	    _OnEndSession();
4753 	    return 0L;
4754 	}
4755 	break;
4756 
4757     case WM_CHAR:
4758 	/* Don't use HANDLE_MSG() for WM_CHAR, it truncates wParam to a single
4759 	 * byte while we want the UTF-16 character value. */
4760 	_OnChar(hwnd, (UINT)wParam, (int)(short)LOWORD(lParam));
4761 	return 0L;
4762 
4763     case WM_SYSCHAR:
4764 	/*
4765 	 * if 'winaltkeys' is "no", or it's "menu" and it's not a menu
4766 	 * shortcut key, handle like a typed ALT key, otherwise call Windows
4767 	 * ALT key handling.
4768 	 */
4769 #ifdef FEAT_MENU
4770 	if (	!gui.menu_is_active
4771 		|| p_wak[0] == 'n'
4772 		|| (p_wak[0] == 'm' && !gui_is_menu_shortcut((int)wParam))
4773 		)
4774 #endif
4775 	{
4776 	    _OnSysChar(hwnd, (UINT)wParam, (int)(short)LOWORD(lParam));
4777 	    return 0L;
4778 	}
4779 #ifdef FEAT_MENU
4780 	else
4781 	    return MyWindowProc(hwnd, uMsg, wParam, lParam);
4782 #endif
4783 
4784     case WM_SYSKEYUP:
4785 #ifdef FEAT_MENU
4786 	/* This used to be done only when menu is active: ALT key is used for
4787 	 * that.  But that caused problems when menu is disabled and using
4788 	 * Alt-Tab-Esc: get into a strange state where no mouse-moved events
4789 	 * are received, mouse pointer remains hidden. */
4790 	return MyWindowProc(hwnd, uMsg, wParam, lParam);
4791 #else
4792 	return 0L;
4793 #endif
4794 
4795     case WM_SIZING:	/* HANDLE_MSG doesn't seem to handle this one */
4796 	return _DuringSizing((UINT)wParam, (LPRECT)lParam);
4797 
4798     case WM_MOUSEWHEEL:
4799 	_OnMouseWheel(hwnd, HIWORD(wParam));
4800 	return 0L;
4801 
4802 	/* Notification for change in SystemParametersInfo() */
4803     case WM_SETTINGCHANGE:
4804 	return _OnSettingChange((UINT)wParam);
4805 
4806 #if defined(FEAT_TOOLBAR) || defined(FEAT_GUI_TABLINE)
4807     case WM_NOTIFY:
4808 	switch (((LPNMHDR) lParam)->code)
4809 	{
4810 	    case TTN_GETDISPINFOW:
4811 	    case TTN_GETDISPINFO:
4812 		{
4813 		    LPNMHDR		hdr = (LPNMHDR)lParam;
4814 		    char_u		*str = NULL;
4815 		    static void		*tt_text = NULL;
4816 
4817 		    VIM_CLEAR(tt_text);
4818 
4819 # ifdef FEAT_GUI_TABLINE
4820 		    if (gui_mch_showing_tabline()
4821 			   && hdr->hwndFrom == TabCtrl_GetToolTips(s_tabhwnd))
4822 		    {
4823 			POINT		pt;
4824 			/*
4825 			 * Mouse is over the GUI tabline. Display the
4826 			 * tooltip for the tab under the cursor
4827 			 *
4828 			 * Get the cursor position within the tab control
4829 			 */
4830 			GetCursorPos(&pt);
4831 			if (ScreenToClient(s_tabhwnd, &pt) != 0)
4832 			{
4833 			    TCHITTESTINFO htinfo;
4834 			    int idx;
4835 
4836 			    /*
4837 			     * Get the tab under the cursor
4838 			     */
4839 			    htinfo.pt.x = pt.x;
4840 			    htinfo.pt.y = pt.y;
4841 			    idx = TabCtrl_HitTest(s_tabhwnd, &htinfo);
4842 			    if (idx != -1)
4843 			    {
4844 				tabpage_T *tp;
4845 
4846 				tp = find_tabpage(idx + 1);
4847 				if (tp != NULL)
4848 				{
4849 				    get_tabline_label(tp, TRUE);
4850 				    str = NameBuff;
4851 				}
4852 			    }
4853 			}
4854 		    }
4855 # endif
4856 # ifdef FEAT_TOOLBAR
4857 #  ifdef FEAT_GUI_TABLINE
4858 		    else
4859 #  endif
4860 		    {
4861 			UINT		idButton;
4862 			vimmenu_T	*pMenu;
4863 
4864 			idButton = (UINT) hdr->idFrom;
4865 			pMenu = gui_mswin_find_menu(root_menu, idButton);
4866 			if (pMenu)
4867 			    str = pMenu->strings[MENU_INDEX_TIP];
4868 		    }
4869 # endif
4870 		    if (str != NULL)
4871 		    {
4872 			if (hdr->code == TTN_GETDISPINFOW)
4873 			{
4874 			    LPNMTTDISPINFOW	lpdi = (LPNMTTDISPINFOW)lParam;
4875 
4876 			    /* Set the maximum width, this also enables using
4877 			     * \n for line break. */
4878 			    SendMessage(lpdi->hdr.hwndFrom, TTM_SETMAXTIPWIDTH,
4879 								      0, 500);
4880 
4881 			    tt_text = enc_to_utf16(str, NULL);
4882 			    lpdi->lpszText = tt_text;
4883 			    /* can't show tooltip if failed */
4884 			}
4885 			else
4886 			{
4887 			    LPNMTTDISPINFO	lpdi = (LPNMTTDISPINFO)lParam;
4888 
4889 			    /* Set the maximum width, this also enables using
4890 			     * \n for line break. */
4891 			    SendMessage(lpdi->hdr.hwndFrom, TTM_SETMAXTIPWIDTH,
4892 								      0, 500);
4893 
4894 			    if (STRLEN(str) < sizeof(lpdi->szText)
4895 				    || ((tt_text = vim_strsave(str)) == NULL))
4896 				vim_strncpy((char_u *)lpdi->szText, str,
4897 						sizeof(lpdi->szText) - 1);
4898 			    else
4899 				lpdi->lpszText = tt_text;
4900 			}
4901 		    }
4902 		}
4903 		break;
4904 # ifdef FEAT_GUI_TABLINE
4905 	    case TCN_SELCHANGE:
4906 		if (gui_mch_showing_tabline()
4907 				  && ((LPNMHDR)lParam)->hwndFrom == s_tabhwnd)
4908 		{
4909 		    send_tabline_event(TabCtrl_GetCurSel(s_tabhwnd) + 1);
4910 		    return 0L;
4911 		}
4912 		break;
4913 
4914 	    case NM_RCLICK:
4915 		if (gui_mch_showing_tabline()
4916 			&& ((LPNMHDR)lParam)->hwndFrom == s_tabhwnd)
4917 		{
4918 		    show_tabline_popup_menu();
4919 		    return 0L;
4920 		}
4921 		break;
4922 # endif
4923 	    default:
4924 # ifdef FEAT_GUI_TABLINE
4925 		if (gui_mch_showing_tabline()
4926 				  && ((LPNMHDR)lParam)->hwndFrom == s_tabhwnd)
4927 		    return MyWindowProc(hwnd, uMsg, wParam, lParam);
4928 # endif
4929 		break;
4930 	}
4931 	break;
4932 #endif
4933 #if defined(MENUHINTS) && defined(FEAT_MENU)
4934     case WM_MENUSELECT:
4935 	if (((UINT) HIWORD(wParam)
4936 		    & (0xffff ^ (MF_MOUSESELECT + MF_BITMAP + MF_POPUP)))
4937 		== MF_HILITE
4938 		&& (State & CMDLINE) == 0)
4939 	{
4940 	    UINT	idButton;
4941 	    vimmenu_T	*pMenu;
4942 	    static int	did_menu_tip = FALSE;
4943 
4944 	    if (did_menu_tip)
4945 	    {
4946 		msg_clr_cmdline();
4947 		setcursor();
4948 		out_flush();
4949 		did_menu_tip = FALSE;
4950 	    }
4951 
4952 	    idButton = (UINT)LOWORD(wParam);
4953 	    pMenu = gui_mswin_find_menu(root_menu, idButton);
4954 	    if (pMenu != NULL && pMenu->strings[MENU_INDEX_TIP] != 0
4955 		    && GetMenuState(s_menuBar, pMenu->id, MF_BYCOMMAND) != -1)
4956 	    {
4957 		++msg_hist_off;
4958 		msg((char *)pMenu->strings[MENU_INDEX_TIP]);
4959 		--msg_hist_off;
4960 		setcursor();
4961 		out_flush();
4962 		did_menu_tip = TRUE;
4963 	    }
4964 	    return 0L;
4965 	}
4966 	break;
4967 #endif
4968     case WM_NCHITTEST:
4969 	{
4970 	    LRESULT	result;
4971 	    int		x, y;
4972 	    int		xPos = GET_X_LPARAM(lParam);
4973 
4974 	    result = MyWindowProc(hwnd, uMsg, wParam, lParam);
4975 	    if (result == HTCLIENT)
4976 	    {
4977 #ifdef FEAT_GUI_TABLINE
4978 		if (gui_mch_showing_tabline())
4979 		{
4980 		    int  yPos = GET_Y_LPARAM(lParam);
4981 		    RECT rct;
4982 
4983 		    /* If the cursor is on the GUI tabline, don't process this
4984 		     * event */
4985 		    GetWindowRect(s_textArea, &rct);
4986 		    if (yPos < rct.top)
4987 			return result;
4988 		}
4989 #endif
4990 		(void)gui_mch_get_winpos(&x, &y);
4991 		xPos -= x;
4992 
4993 		if (xPos < 48) /* <VN> TODO should use system metric? */
4994 		    return HTBOTTOMLEFT;
4995 		else
4996 		    return HTBOTTOMRIGHT;
4997 	    }
4998 	    else
4999 		return result;
5000 	}
5001 	/* break; notreached */
5002 
5003 #ifdef FEAT_MBYTE_IME
5004     case WM_IME_NOTIFY:
5005 	if (!_OnImeNotify(hwnd, (DWORD)wParam, (DWORD)lParam))
5006 	    return MyWindowProc(hwnd, uMsg, wParam, lParam);
5007 	return 1L;
5008 
5009     case WM_IME_COMPOSITION:
5010 	if (!_OnImeComposition(hwnd, wParam, lParam))
5011 	    return MyWindowProc(hwnd, uMsg, wParam, lParam);
5012 	return 1L;
5013 #endif
5014 
5015     default:
5016 #ifdef MSWIN_FIND_REPLACE
5017 	if (uMsg == s_findrep_msg && s_findrep_msg != 0)
5018 	{
5019 	    _OnFindRepl();
5020 	}
5021 #endif
5022 	return MyWindowProc(hwnd, uMsg, wParam, lParam);
5023     }
5024 
5025     return DefWindowProc(hwnd, uMsg, wParam, lParam);
5026 }
5027 
5028 /*
5029  * End of call-back routines
5030  */
5031 
5032 /* parent window, if specified with -P */
5033 HWND vim_parent_hwnd = NULL;
5034 
5035     static BOOL CALLBACK
5036 FindWindowTitle(HWND hwnd, LPARAM lParam)
5037 {
5038     char	buf[2048];
5039     char	*title = (char *)lParam;
5040 
5041     if (GetWindowText(hwnd, buf, sizeof(buf)))
5042     {
5043 	if (strstr(buf, title) != NULL)
5044 	{
5045 	    /* Found it.  Store the window ref. and quit searching if MDI
5046 	     * works. */
5047 	    vim_parent_hwnd = FindWindowEx(hwnd, NULL, "MDIClient", NULL);
5048 	    if (vim_parent_hwnd != NULL)
5049 		return FALSE;
5050 	}
5051     }
5052     return TRUE;	/* continue searching */
5053 }
5054 
5055 /*
5056  * Invoked for '-P "title"' argument: search for parent application to open
5057  * our window in.
5058  */
5059     void
5060 gui_mch_set_parent(char *title)
5061 {
5062     EnumWindows(FindWindowTitle, (LPARAM)title);
5063     if (vim_parent_hwnd == NULL)
5064     {
5065 	semsg(_("E671: Cannot find window title \"%s\""), title);
5066 	mch_exit(2);
5067     }
5068 }
5069 
5070 #ifndef FEAT_OLE
5071     static void
5072 ole_error(char *arg)
5073 {
5074     char buf[IOSIZE];
5075 
5076     /* Can't use emsg() here, we have not finished initialisation yet. */
5077     vim_snprintf(buf, IOSIZE,
5078 	    _("E243: Argument not supported: \"-%s\"; Use the OLE version."),
5079 	    arg);
5080     mch_errmsg(buf);
5081 }
5082 #endif
5083 
5084 /*
5085  * Parse the GUI related command-line arguments.  Any arguments used are
5086  * deleted from argv, and *argc is decremented accordingly.  This is called
5087  * when vim is started, whether or not the GUI has been started.
5088  */
5089     void
5090 gui_mch_prepare(int *argc, char **argv)
5091 {
5092     int		silent = FALSE;
5093     int		idx;
5094 
5095     /* Check for special OLE command line parameters */
5096     if ((*argc == 2 || *argc == 3) && (argv[1][0] == '-' || argv[1][0] == '/'))
5097     {
5098 	/* Check for a "-silent" argument first. */
5099 	if (*argc == 3 && STRICMP(argv[1] + 1, "silent") == 0
5100 		&& (argv[2][0] == '-' || argv[2][0] == '/'))
5101 	{
5102 	    silent = TRUE;
5103 	    idx = 2;
5104 	}
5105 	else
5106 	    idx = 1;
5107 
5108 	/* Register Vim as an OLE Automation server */
5109 	if (STRICMP(argv[idx] + 1, "register") == 0)
5110 	{
5111 #ifdef FEAT_OLE
5112 	    RegisterMe(silent);
5113 	    mch_exit(0);
5114 #else
5115 	    if (!silent)
5116 		ole_error("register");
5117 	    mch_exit(2);
5118 #endif
5119 	}
5120 
5121 	/* Unregister Vim as an OLE Automation server */
5122 	if (STRICMP(argv[idx] + 1, "unregister") == 0)
5123 	{
5124 #ifdef FEAT_OLE
5125 	    UnregisterMe(!silent);
5126 	    mch_exit(0);
5127 #else
5128 	    if (!silent)
5129 		ole_error("unregister");
5130 	    mch_exit(2);
5131 #endif
5132 	}
5133 
5134 	/* Ignore an -embedding argument. It is only relevant if the
5135 	 * application wants to treat the case when it is started manually
5136 	 * differently from the case where it is started via automation (and
5137 	 * we don't).
5138 	 */
5139 	if (STRICMP(argv[idx] + 1, "embedding") == 0)
5140 	{
5141 #ifdef FEAT_OLE
5142 	    *argc = 1;
5143 #else
5144 	    ole_error("embedding");
5145 	    mch_exit(2);
5146 #endif
5147 	}
5148     }
5149 
5150 #ifdef FEAT_OLE
5151     {
5152 	int	bDoRestart = FALSE;
5153 
5154 	InitOLE(&bDoRestart);
5155 	/* automatically exit after registering */
5156 	if (bDoRestart)
5157 	    mch_exit(0);
5158     }
5159 #endif
5160 
5161 #ifdef FEAT_NETBEANS_INTG
5162     {
5163 	/* stolen from gui_x11.c */
5164 	int arg;
5165 
5166 	for (arg = 1; arg < *argc; arg++)
5167 	    if (strncmp("-nb", argv[arg], 3) == 0)
5168 	    {
5169 		netbeansArg = argv[arg];
5170 		mch_memmove(&argv[arg], &argv[arg + 1],
5171 					    (--*argc - arg) * sizeof(char *));
5172 		argv[*argc] = NULL;
5173 		break;	/* enough? */
5174 	    }
5175     }
5176 #endif
5177 }
5178 
5179 /*
5180  * Initialise the GUI.	Create all the windows, set up all the call-backs
5181  * etc.
5182  */
5183     int
5184 gui_mch_init(void)
5185 {
5186     const char szVimWndClass[] = VIM_CLASS;
5187     const char szTextAreaClass[] = "VimTextArea";
5188     WNDCLASS wndclass;
5189     const WCHAR szVimWndClassW[] = VIM_CLASSW;
5190     const WCHAR szTextAreaClassW[] = L"VimTextArea";
5191     WNDCLASSW wndclassw;
5192 #ifdef GLOBAL_IME
5193     ATOM	atom;
5194 #endif
5195 
5196     /* Return here if the window was already opened (happens when
5197      * gui_mch_dialog() is called early). */
5198     if (s_hwnd != NULL)
5199 	goto theend;
5200 
5201     /*
5202      * Load the tearoff bitmap
5203      */
5204 #ifdef FEAT_TEAROFF
5205     s_htearbitmap = LoadBitmap(s_hinst, "IDB_TEAROFF");
5206 #endif
5207 
5208     gui.scrollbar_width = GetSystemMetrics(SM_CXVSCROLL);
5209     gui.scrollbar_height = GetSystemMetrics(SM_CYHSCROLL);
5210 #ifdef FEAT_MENU
5211     gui.menu_height = 0;	/* Windows takes care of this */
5212 #endif
5213     gui.border_width = 0;
5214 
5215     s_brush = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
5216 
5217     /* First try using the wide version, so that we can use any title.
5218      * Otherwise only characters in the active codepage will work. */
5219     if (GetClassInfoW(s_hinst, szVimWndClassW, &wndclassw) == 0)
5220     {
5221 	wndclassw.style = CS_DBLCLKS;
5222 	wndclassw.lpfnWndProc = _WndProc;
5223 	wndclassw.cbClsExtra = 0;
5224 	wndclassw.cbWndExtra = 0;
5225 	wndclassw.hInstance = s_hinst;
5226 	wndclassw.hIcon = LoadIcon(wndclassw.hInstance, "IDR_VIM");
5227 	wndclassw.hCursor = LoadCursor(NULL, IDC_ARROW);
5228 	wndclassw.hbrBackground = s_brush;
5229 	wndclassw.lpszMenuName = NULL;
5230 	wndclassw.lpszClassName = szVimWndClassW;
5231 
5232 	if ((
5233 #ifdef GLOBAL_IME
5234 		    atom =
5235 #endif
5236 		    RegisterClassW(&wndclassw)) == 0)
5237 	    return FAIL;
5238 	else
5239 	    wide_WindowProc = TRUE;
5240     }
5241 
5242     if (!wide_WindowProc)
5243 	if (GetClassInfo(s_hinst, szVimWndClass, &wndclass) == 0)
5244 	{
5245 	    wndclass.style = CS_DBLCLKS;
5246 	    wndclass.lpfnWndProc = _WndProc;
5247 	    wndclass.cbClsExtra = 0;
5248 	    wndclass.cbWndExtra = 0;
5249 	    wndclass.hInstance = s_hinst;
5250 	    wndclass.hIcon = LoadIcon(wndclass.hInstance, "IDR_VIM");
5251 	    wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
5252 	    wndclass.hbrBackground = s_brush;
5253 	    wndclass.lpszMenuName = NULL;
5254 	    wndclass.lpszClassName = szVimWndClass;
5255 
5256 	    if ((
5257 #ifdef GLOBAL_IME
5258 			atom =
5259 #endif
5260 			RegisterClass(&wndclass)) == 0)
5261 		return FAIL;
5262 	}
5263 
5264     if (vim_parent_hwnd != NULL)
5265     {
5266 #ifdef HAVE_TRY_EXCEPT
5267 	__try
5268 	{
5269 #endif
5270 	    /* Open inside the specified parent window.
5271 	     * TODO: last argument should point to a CLIENTCREATESTRUCT
5272 	     * structure. */
5273 	    s_hwnd = CreateWindowEx(
5274 		WS_EX_MDICHILD,
5275 		szVimWndClass, "Vim MSWindows GUI",
5276 		WS_OVERLAPPEDWINDOW | WS_CHILD
5277 				 | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | 0xC000,
5278 		gui_win_x == -1 ? CW_USEDEFAULT : gui_win_x,
5279 		gui_win_y == -1 ? CW_USEDEFAULT : gui_win_y,
5280 		100,				/* Any value will do */
5281 		100,				/* Any value will do */
5282 		vim_parent_hwnd, NULL,
5283 		s_hinst, NULL);
5284 #ifdef HAVE_TRY_EXCEPT
5285 	}
5286 	__except(EXCEPTION_EXECUTE_HANDLER)
5287 	{
5288 	    /* NOP */
5289 	}
5290 #endif
5291 	if (s_hwnd == NULL)
5292 	{
5293 	    emsg(_("E672: Unable to open window inside MDI application"));
5294 	    mch_exit(2);
5295 	}
5296     }
5297     else
5298     {
5299 	/* If the provided windowid is not valid reset it to zero, so that it
5300 	 * is ignored and we open our own window. */
5301 	if (IsWindow((HWND)win_socket_id) <= 0)
5302 	    win_socket_id = 0;
5303 
5304 	/* Create a window.  If win_socket_id is not zero without border and
5305 	 * titlebar, it will be reparented below. */
5306 	s_hwnd = CreateWindow(
5307 		szVimWndClass, "Vim MSWindows GUI",
5308 		(win_socket_id == 0 ? WS_OVERLAPPEDWINDOW : WS_POPUP)
5309 					  | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
5310 		gui_win_x == -1 ? CW_USEDEFAULT : gui_win_x,
5311 		gui_win_y == -1 ? CW_USEDEFAULT : gui_win_y,
5312 		100,				/* Any value will do */
5313 		100,				/* Any value will do */
5314 		NULL, NULL,
5315 		s_hinst, NULL);
5316 	if (s_hwnd != NULL && win_socket_id != 0)
5317 	{
5318 	    SetParent(s_hwnd, (HWND)win_socket_id);
5319 	    ShowWindow(s_hwnd, SW_SHOWMAXIMIZED);
5320 	}
5321     }
5322 
5323     if (s_hwnd == NULL)
5324 	return FAIL;
5325 
5326 #ifdef GLOBAL_IME
5327     global_ime_init(atom, s_hwnd);
5328 #endif
5329 #if defined(FEAT_MBYTE_IME) && defined(DYNAMIC_IME)
5330     dyn_imm_load();
5331 #endif
5332 
5333     /* Create the text area window */
5334     if (wide_WindowProc)
5335     {
5336 	if (GetClassInfoW(s_hinst, szTextAreaClassW, &wndclassw) == 0)
5337 	{
5338 	    wndclassw.style = CS_OWNDC;
5339 	    wndclassw.lpfnWndProc = _TextAreaWndProc;
5340 	    wndclassw.cbClsExtra = 0;
5341 	    wndclassw.cbWndExtra = 0;
5342 	    wndclassw.hInstance = s_hinst;
5343 	    wndclassw.hIcon = NULL;
5344 	    wndclassw.hCursor = LoadCursor(NULL, IDC_ARROW);
5345 	    wndclassw.hbrBackground = NULL;
5346 	    wndclassw.lpszMenuName = NULL;
5347 	    wndclassw.lpszClassName = szTextAreaClassW;
5348 
5349 	    if (RegisterClassW(&wndclassw) == 0)
5350 		return FAIL;
5351 	}
5352 
5353 	s_textArea = CreateWindowExW(
5354 	    0,
5355 	    szTextAreaClassW, L"Vim text area",
5356 	    WS_CHILD | WS_VISIBLE, 0, 0,
5357 	    100,				// Any value will do for now
5358 	    100,				// Any value will do for now
5359 	    s_hwnd, NULL,
5360 	    s_hinst, NULL);
5361     }
5362     else if (GetClassInfo(s_hinst, szTextAreaClass, &wndclass) == 0)
5363     {
5364 	wndclass.style = CS_OWNDC;
5365 	wndclass.lpfnWndProc = _TextAreaWndProc;
5366 	wndclass.cbClsExtra = 0;
5367 	wndclass.cbWndExtra = 0;
5368 	wndclass.hInstance = s_hinst;
5369 	wndclass.hIcon = NULL;
5370 	wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
5371 	wndclass.hbrBackground = NULL;
5372 	wndclass.lpszMenuName = NULL;
5373 	wndclass.lpszClassName = szTextAreaClass;
5374 
5375 	if (RegisterClass(&wndclass) == 0)
5376 	    return FAIL;
5377 
5378 	s_textArea = CreateWindowEx(
5379 	    0,
5380 	    szTextAreaClass, "Vim text area",
5381 	    WS_CHILD | WS_VISIBLE, 0, 0,
5382 	    100,				// Any value will do for now
5383 	    100,				// Any value will do for now
5384 	    s_hwnd, NULL,
5385 	    s_hinst, NULL);
5386     }
5387 
5388     if (s_textArea == NULL)
5389 	return FAIL;
5390 
5391 #ifdef FEAT_LIBCALL
5392     /* Try loading an icon from $RUNTIMEPATH/bitmaps/vim.ico. */
5393     {
5394 	HANDLE	hIcon = NULL;
5395 
5396 	if (mch_icon_load(&hIcon) == OK && hIcon != NULL)
5397 	    SendMessage(s_hwnd, WM_SETICON, ICON_SMALL, (LPARAM)hIcon);
5398     }
5399 #endif
5400 
5401 #ifdef FEAT_MENU
5402     s_menuBar = CreateMenu();
5403 #endif
5404     s_hdc = GetDC(s_textArea);
5405 
5406     DragAcceptFiles(s_hwnd, TRUE);
5407 
5408     /* Do we need to bother with this? */
5409     /* m_fMouseAvail = GetSystemMetrics(SM_MOUSEPRESENT); */
5410 
5411     /* Get background/foreground colors from the system */
5412     gui_mch_def_colors();
5413 
5414     /* Get the colors from the "Normal" group (set in syntax.c or in a vimrc
5415      * file) */
5416     set_normal_colors();
5417 
5418     /*
5419      * Check that none of the colors are the same as the background color.
5420      * Then store the current values as the defaults.
5421      */
5422     gui_check_colors();
5423     gui.def_norm_pixel = gui.norm_pixel;
5424     gui.def_back_pixel = gui.back_pixel;
5425 
5426     /* Get the colors for the highlight groups (gui_check_colors() might have
5427      * changed them) */
5428     highlight_gui_started();
5429 
5430     /*
5431      * Start out by adding the configured border width into the border offset.
5432      */
5433     gui.border_offset = gui.border_width;
5434 
5435     /*
5436      * Set up for Intellimouse processing
5437      */
5438     init_mouse_wheel();
5439 
5440     /*
5441      * compute a couple of metrics used for the dialogs
5442      */
5443     get_dialog_font_metrics();
5444 #ifdef FEAT_TOOLBAR
5445     /*
5446      * Create the toolbar
5447      */
5448     initialise_toolbar();
5449 #endif
5450 #ifdef FEAT_GUI_TABLINE
5451     /*
5452      * Create the tabline
5453      */
5454     initialise_tabline();
5455 #endif
5456 #ifdef MSWIN_FIND_REPLACE
5457     /*
5458      * Initialise the dialog box stuff
5459      */
5460     s_findrep_msg = RegisterWindowMessage(FINDMSGSTRING);
5461 
5462     /* Initialise the struct */
5463     s_findrep_struct.lStructSize = sizeof(s_findrep_struct);
5464     s_findrep_struct.lpstrFindWhat = (LPSTR)alloc(MSWIN_FR_BUFSIZE);
5465     s_findrep_struct.lpstrFindWhat[0] = NUL;
5466     s_findrep_struct.lpstrReplaceWith = (LPSTR)alloc(MSWIN_FR_BUFSIZE);
5467     s_findrep_struct.lpstrReplaceWith[0] = NUL;
5468     s_findrep_struct.wFindWhatLen = MSWIN_FR_BUFSIZE;
5469     s_findrep_struct.wReplaceWithLen = MSWIN_FR_BUFSIZE;
5470     s_findrep_struct_w.lStructSize = sizeof(s_findrep_struct_w);
5471     s_findrep_struct_w.lpstrFindWhat =
5472 			      (LPWSTR)alloc(MSWIN_FR_BUFSIZE * sizeof(WCHAR));
5473     s_findrep_struct_w.lpstrFindWhat[0] = NUL;
5474     s_findrep_struct_w.lpstrReplaceWith =
5475 			      (LPWSTR)alloc(MSWIN_FR_BUFSIZE * sizeof(WCHAR));
5476     s_findrep_struct_w.lpstrReplaceWith[0] = NUL;
5477     s_findrep_struct_w.wFindWhatLen = MSWIN_FR_BUFSIZE;
5478     s_findrep_struct_w.wReplaceWithLen = MSWIN_FR_BUFSIZE;
5479 #endif
5480 
5481 #ifdef FEAT_EVAL
5482 # if !defined(_MSC_VER) || (_MSC_VER < 1400)
5483 /* Define HandleToLong for old MS and non-MS compilers if not defined. */
5484 #  ifndef HandleToLong
5485 #   define HandleToLong(h) ((long)(intptr_t)(h))
5486 #  endif
5487 # endif
5488     /* set the v:windowid variable */
5489     set_vim_var_nr(VV_WINDOWID, HandleToLong(s_hwnd));
5490 #endif
5491 
5492 #ifdef FEAT_RENDER_OPTIONS
5493     if (p_rop)
5494 	(void)gui_mch_set_rendering_options(p_rop);
5495 #endif
5496 
5497 theend:
5498     /* Display any pending error messages */
5499     display_errors();
5500 
5501     return OK;
5502 }
5503 
5504 /*
5505  * Get the size of the screen, taking position on multiple monitors into
5506  * account (if supported).
5507  */
5508     static void
5509 get_work_area(RECT *spi_rect)
5510 {
5511     HMONITOR	    mon;
5512     MONITORINFO	    moninfo;
5513 
5514     /* work out which monitor the window is on, and get *its* work area */
5515     mon = MonitorFromWindow(s_hwnd, MONITOR_DEFAULTTOPRIMARY);
5516     if (mon != NULL)
5517     {
5518 	moninfo.cbSize = sizeof(MONITORINFO);
5519 	if (GetMonitorInfo(mon, &moninfo))
5520 	{
5521 	    *spi_rect = moninfo.rcWork;
5522 	    return;
5523 	}
5524     }
5525     /* this is the old method... */
5526     SystemParametersInfo(SPI_GETWORKAREA, 0, spi_rect, 0);
5527 }
5528 
5529 /*
5530  * Set the size of the window to the given width and height in pixels.
5531  */
5532     void
5533 gui_mch_set_shellsize(
5534 	int width,
5535 	int height,
5536 	int min_width UNUSED,
5537 	int min_height UNUSED,
5538 	int base_width UNUSED,
5539 	int base_height UNUSED,
5540 	int direction)
5541 {
5542     RECT	workarea_rect;
5543     int		win_width, win_height;
5544     WINDOWPLACEMENT wndpl;
5545 
5546     /* Try to keep window completely on screen. */
5547     /* Get position of the screen work area.  This is the part that is not
5548      * used by the taskbar or appbars. */
5549     get_work_area(&workarea_rect);
5550 
5551     /* Get current position of our window.  Note that the .left and .top are
5552      * relative to the work area.  */
5553     wndpl.length = sizeof(WINDOWPLACEMENT);
5554     GetWindowPlacement(s_hwnd, &wndpl);
5555 
5556     /* Resizing a maximized window looks very strange, unzoom it first.
5557      * But don't do it when still starting up, it may have been requested in
5558      * the shortcut. */
5559     if (wndpl.showCmd == SW_SHOWMAXIMIZED && starting == 0)
5560     {
5561 	ShowWindow(s_hwnd, SW_SHOWNORMAL);
5562 	/* Need to get the settings of the normal window. */
5563 	GetWindowPlacement(s_hwnd, &wndpl);
5564     }
5565 
5566     /* compute the size of the outside of the window */
5567     win_width = width + (GetSystemMetrics(SM_CXFRAME) +
5568 			 GetSystemMetrics(SM_CXPADDEDBORDER)) * 2;
5569     win_height = height + (GetSystemMetrics(SM_CYFRAME) +
5570 			   GetSystemMetrics(SM_CXPADDEDBORDER)) * 2
5571 			+ GetSystemMetrics(SM_CYCAPTION)
5572 #ifdef FEAT_MENU
5573 			+ gui_mswin_get_menu_height(FALSE)
5574 #endif
5575 			;
5576 
5577     /* The following should take care of keeping Vim on the same monitor, no
5578      * matter if the secondary monitor is left or right of the primary
5579      * monitor. */
5580     wndpl.rcNormalPosition.right = wndpl.rcNormalPosition.left + win_width;
5581     wndpl.rcNormalPosition.bottom = wndpl.rcNormalPosition.top + win_height;
5582 
5583     /* If the window is going off the screen, move it on to the screen. */
5584     if ((direction & RESIZE_HOR)
5585 	    && wndpl.rcNormalPosition.right > workarea_rect.right)
5586 	OffsetRect(&wndpl.rcNormalPosition,
5587 		workarea_rect.right - wndpl.rcNormalPosition.right, 0);
5588 
5589     if ((direction & RESIZE_HOR)
5590 	    && wndpl.rcNormalPosition.left < workarea_rect.left)
5591 	OffsetRect(&wndpl.rcNormalPosition,
5592 		workarea_rect.left - wndpl.rcNormalPosition.left, 0);
5593 
5594     if ((direction & RESIZE_VERT)
5595 	    && wndpl.rcNormalPosition.bottom > workarea_rect.bottom)
5596 	OffsetRect(&wndpl.rcNormalPosition,
5597 		0, workarea_rect.bottom - wndpl.rcNormalPosition.bottom);
5598 
5599     if ((direction & RESIZE_VERT)
5600 	    && wndpl.rcNormalPosition.top < workarea_rect.top)
5601 	OffsetRect(&wndpl.rcNormalPosition,
5602 		0, workarea_rect.top - wndpl.rcNormalPosition.top);
5603 
5604     /* set window position - we should use SetWindowPlacement rather than
5605      * SetWindowPos as the MSDN docs say the coord systems returned by
5606      * these two are not compatible. */
5607     SetWindowPlacement(s_hwnd, &wndpl);
5608 
5609     SetActiveWindow(s_hwnd);
5610     SetFocus(s_hwnd);
5611 
5612 #ifdef FEAT_MENU
5613     /* Menu may wrap differently now */
5614     gui_mswin_get_menu_height(!gui.starting);
5615 #endif
5616 }
5617 
5618 
5619     void
5620 gui_mch_set_scrollbar_thumb(
5621     scrollbar_T *sb,
5622     long	val,
5623     long	size,
5624     long	max)
5625 {
5626     SCROLLINFO	info;
5627 
5628     sb->scroll_shift = 0;
5629     while (max > 32767)
5630     {
5631 	max = (max + 1) >> 1;
5632 	val  >>= 1;
5633 	size >>= 1;
5634 	++sb->scroll_shift;
5635     }
5636 
5637     if (sb->scroll_shift > 0)
5638 	++size;
5639 
5640     info.cbSize = sizeof(info);
5641     info.fMask = SIF_POS | SIF_RANGE | SIF_PAGE;
5642     info.nPos = val;
5643     info.nMin = 0;
5644     info.nMax = max;
5645     info.nPage = size;
5646     SetScrollInfo(sb->id, SB_CTL, &info, TRUE);
5647 }
5648 
5649 
5650 /*
5651  * Set the current text font.
5652  */
5653     void
5654 gui_mch_set_font(GuiFont font)
5655 {
5656     gui.currFont = font;
5657 }
5658 
5659 
5660 /*
5661  * Set the current text foreground color.
5662  */
5663     void
5664 gui_mch_set_fg_color(guicolor_T color)
5665 {
5666     gui.currFgColor = color;
5667 }
5668 
5669 /*
5670  * Set the current text background color.
5671  */
5672     void
5673 gui_mch_set_bg_color(guicolor_T color)
5674 {
5675     gui.currBgColor = color;
5676 }
5677 
5678 /*
5679  * Set the current text special color.
5680  */
5681     void
5682 gui_mch_set_sp_color(guicolor_T color)
5683 {
5684     gui.currSpColor = color;
5685 }
5686 
5687 #ifdef FEAT_MBYTE_IME
5688 /*
5689  * Multi-byte handling, originally by Sung-Hoon Baek.
5690  * First static functions (no prototypes generated).
5691  */
5692 # ifdef _MSC_VER
5693 #  include <ime.h>   /* Apparently not needed for Cygwin, MingW or Borland. */
5694 # endif
5695 # include <imm.h>
5696 
5697 /*
5698  * handle WM_IME_NOTIFY message
5699  */
5700     static LRESULT
5701 _OnImeNotify(HWND hWnd, DWORD dwCommand, DWORD dwData UNUSED)
5702 {
5703     LRESULT lResult = 0;
5704     HIMC hImc;
5705 
5706     if (!pImmGetContext || (hImc = pImmGetContext(hWnd)) == (HIMC)0)
5707 	return lResult;
5708     switch (dwCommand)
5709     {
5710 	case IMN_SETOPENSTATUS:
5711 	    if (pImmGetOpenStatus(hImc))
5712 	    {
5713 		pImmSetCompositionFont(hImc, &norm_logfont);
5714 		im_set_position(gui.row, gui.col);
5715 
5716 		/* Disable langmap */
5717 		State &= ~LANGMAP;
5718 		if (State & INSERT)
5719 		{
5720 #if defined(FEAT_KEYMAP)
5721 		    /* Unshown 'keymap' in status lines */
5722 		    if (curbuf->b_p_iminsert == B_IMODE_LMAP)
5723 		    {
5724 			/* Save cursor position */
5725 			int old_row = gui.row;
5726 			int old_col = gui.col;
5727 
5728 			// This must be called here before
5729 			// status_redraw_curbuf(), otherwise the mode
5730 			// message may appear in the wrong position.
5731 			showmode();
5732 			status_redraw_curbuf();
5733 			update_screen(0);
5734 			/* Restore cursor position */
5735 			gui.row = old_row;
5736 			gui.col = old_col;
5737 		    }
5738 #endif
5739 		}
5740 	    }
5741 	    gui_update_cursor(TRUE, FALSE);
5742 	    gui_mch_flush();
5743 	    lResult = 0;
5744 	    break;
5745     }
5746     pImmReleaseContext(hWnd, hImc);
5747     return lResult;
5748 }
5749 
5750     static LRESULT
5751 _OnImeComposition(HWND hwnd, WPARAM dbcs UNUSED, LPARAM param)
5752 {
5753     char_u	*ret;
5754     int		len;
5755 
5756     if ((param & GCS_RESULTSTR) == 0) /* Composition unfinished. */
5757 	return 0;
5758 
5759     ret = GetResultStr(hwnd, GCS_RESULTSTR, &len);
5760     if (ret != NULL)
5761     {
5762 	add_to_input_buf_csi(ret, len);
5763 	vim_free(ret);
5764 	return 1;
5765     }
5766     return 0;
5767 }
5768 
5769 /*
5770  * get the current composition string, in UCS-2; *lenp is the number of
5771  * *lenp is the number of Unicode characters.
5772  */
5773     static short_u *
5774 GetCompositionString_inUCS2(HIMC hIMC, DWORD GCS, int *lenp)
5775 {
5776     LONG	    ret;
5777     LPWSTR	    wbuf = NULL;
5778     char_u	    *buf;
5779 
5780     if (!pImmGetContext)
5781 	return NULL; /* no imm32.dll */
5782 
5783     /* Try Unicode; this'll always work on NT regardless of codepage. */
5784     ret = pImmGetCompositionStringW(hIMC, GCS, NULL, 0);
5785     if (ret == 0)
5786 	return NULL; /* empty */
5787 
5788     if (ret > 0)
5789     {
5790 	/* Allocate the requested buffer plus space for the NUL character. */
5791 	wbuf = (LPWSTR)alloc(ret + sizeof(WCHAR));
5792 	if (wbuf != NULL)
5793 	{
5794 	    pImmGetCompositionStringW(hIMC, GCS, wbuf, ret);
5795 	    *lenp = ret / sizeof(WCHAR);
5796 	}
5797 	return (short_u *)wbuf;
5798     }
5799 
5800     /* ret < 0; we got an error, so try the ANSI version.  This'll work
5801      * on 9x/ME, but only if the codepage happens to be set to whatever
5802      * we're inputting. */
5803     ret = pImmGetCompositionStringA(hIMC, GCS, NULL, 0);
5804     if (ret <= 0)
5805 	return NULL; /* empty or error */
5806 
5807     buf = alloc(ret);
5808     if (buf == NULL)
5809 	return NULL;
5810     pImmGetCompositionStringA(hIMC, GCS, buf, ret);
5811 
5812     /* convert from codepage to UCS-2 */
5813     MultiByteToWideChar_alloc(GetACP(), 0, (LPCSTR)buf, ret, &wbuf, lenp);
5814     vim_free(buf);
5815 
5816     return (short_u *)wbuf;
5817 }
5818 
5819 /*
5820  * void GetResultStr()
5821  *
5822  * This handles WM_IME_COMPOSITION with GCS_RESULTSTR flag on.
5823  * get complete composition string
5824  */
5825     static char_u *
5826 GetResultStr(HWND hwnd, int GCS, int *lenp)
5827 {
5828     HIMC	hIMC;		/* Input context handle. */
5829     short_u	*buf = NULL;
5830     char_u	*convbuf = NULL;
5831 
5832     if (!pImmGetContext || (hIMC = pImmGetContext(hwnd)) == (HIMC)0)
5833 	return NULL;
5834 
5835     /* Reads in the composition string. */
5836     buf = GetCompositionString_inUCS2(hIMC, GCS, lenp);
5837     if (buf == NULL)
5838 	return NULL;
5839 
5840     convbuf = utf16_to_enc(buf, lenp);
5841     pImmReleaseContext(hwnd, hIMC);
5842     vim_free(buf);
5843     return convbuf;
5844 }
5845 #endif
5846 
5847 /* For global functions we need prototypes. */
5848 #if defined(FEAT_MBYTE_IME) || defined(PROTO)
5849 
5850 /*
5851  * set font to IM.
5852  */
5853     void
5854 im_set_font(LOGFONT *lf)
5855 {
5856     HIMC hImc;
5857 
5858     if (pImmGetContext && (hImc = pImmGetContext(s_hwnd)) != (HIMC)0)
5859     {
5860 	pImmSetCompositionFont(hImc, lf);
5861 	pImmReleaseContext(s_hwnd, hImc);
5862     }
5863 }
5864 
5865 /*
5866  * Notify cursor position to IM.
5867  */
5868     void
5869 im_set_position(int row, int col)
5870 {
5871     HIMC hImc;
5872 
5873     if (pImmGetContext && (hImc = pImmGetContext(s_hwnd)) != (HIMC)0)
5874     {
5875 	COMPOSITIONFORM	cfs;
5876 
5877 	cfs.dwStyle = CFS_POINT;
5878 	cfs.ptCurrentPos.x = FILL_X(col);
5879 	cfs.ptCurrentPos.y = FILL_Y(row);
5880 	MapWindowPoints(s_textArea, s_hwnd, &cfs.ptCurrentPos, 1);
5881 	pImmSetCompositionWindow(hImc, &cfs);
5882 
5883 	pImmReleaseContext(s_hwnd, hImc);
5884     }
5885 }
5886 
5887 /*
5888  * Set IM status on ("active" is TRUE) or off ("active" is FALSE).
5889  */
5890     void
5891 im_set_active(int active)
5892 {
5893     HIMC	hImc;
5894     static HIMC	hImcOld = (HIMC)0;
5895 
5896     if (pImmGetContext)	    /* if NULL imm32.dll wasn't loaded (yet) */
5897     {
5898 	if (p_imdisable)
5899 	{
5900 	    if (hImcOld == (HIMC)0)
5901 	    {
5902 		hImcOld = pImmGetContext(s_hwnd);
5903 		if (hImcOld)
5904 		    pImmAssociateContext(s_hwnd, (HIMC)0);
5905 	    }
5906 	    active = FALSE;
5907 	}
5908 	else if (hImcOld != (HIMC)0)
5909 	{
5910 	    pImmAssociateContext(s_hwnd, hImcOld);
5911 	    hImcOld = (HIMC)0;
5912 	}
5913 
5914 	hImc = pImmGetContext(s_hwnd);
5915 	if (hImc)
5916 	{
5917 	    /*
5918 	     * for Korean ime
5919 	     */
5920 	    HKL hKL = GetKeyboardLayout(0);
5921 
5922 	    if (LOWORD(hKL) == MAKELANGID(LANG_KOREAN, SUBLANG_KOREAN))
5923 	    {
5924 		static DWORD dwConversionSaved = 0, dwSentenceSaved = 0;
5925 		static BOOL bSaved = FALSE;
5926 
5927 		if (active)
5928 		{
5929 		    /* if we have a saved conversion status, restore it */
5930 		    if (bSaved)
5931 			pImmSetConversionStatus(hImc, dwConversionSaved,
5932 							     dwSentenceSaved);
5933 		    bSaved = FALSE;
5934 		}
5935 		else
5936 		{
5937 		    /* save conversion status and disable korean */
5938 		    if (pImmGetConversionStatus(hImc, &dwConversionSaved,
5939 							    &dwSentenceSaved))
5940 		    {
5941 			bSaved = TRUE;
5942 			pImmSetConversionStatus(hImc,
5943 				dwConversionSaved & ~(IME_CMODE_NATIVE
5944 						       | IME_CMODE_FULLSHAPE),
5945 				dwSentenceSaved);
5946 		    }
5947 		}
5948 	    }
5949 
5950 	    pImmSetOpenStatus(hImc, active);
5951 	    pImmReleaseContext(s_hwnd, hImc);
5952 	}
5953     }
5954 }
5955 
5956 /*
5957  * Get IM status.  When IM is on, return not 0.  Else return 0.
5958  */
5959     int
5960 im_get_status(void)
5961 {
5962     int		status = 0;
5963     HIMC	hImc;
5964 
5965     if (pImmGetContext && (hImc = pImmGetContext(s_hwnd)) != (HIMC)0)
5966     {
5967 	status = pImmGetOpenStatus(hImc) ? 1 : 0;
5968 	pImmReleaseContext(s_hwnd, hImc);
5969     }
5970     return status;
5971 }
5972 
5973 #endif /* FEAT_MBYTE_IME */
5974 
5975 #if !defined(FEAT_MBYTE_IME) && defined(GLOBAL_IME)
5976 /* Win32 with GLOBAL IME */
5977 
5978 /*
5979  * Notify cursor position to IM.
5980  */
5981     void
5982 im_set_position(int row, int col)
5983 {
5984     /* Win32 with GLOBAL IME */
5985     POINT p;
5986 
5987     p.x = FILL_X(col);
5988     p.y = FILL_Y(row);
5989     MapWindowPoints(s_textArea, s_hwnd, &p, 1);
5990     global_ime_set_position(&p);
5991 }
5992 
5993 /*
5994  * Set IM status on ("active" is TRUE) or off ("active" is FALSE).
5995  */
5996     void
5997 im_set_active(int active)
5998 {
5999     global_ime_set_status(active);
6000 }
6001 
6002 /*
6003  * Get IM status.  When IM is on, return not 0.  Else return 0.
6004  */
6005     int
6006 im_get_status(void)
6007 {
6008     return global_ime_get_status();
6009 }
6010 #endif
6011 
6012 /*
6013  * Convert latin9 text "text[len]" to ucs-2 in "unicodebuf".
6014  */
6015     static void
6016 latin9_to_ucs(char_u *text, int len, WCHAR *unicodebuf)
6017 {
6018     int		c;
6019 
6020     while (--len >= 0)
6021     {
6022 	c = *text++;
6023 	switch (c)
6024 	{
6025 	    case 0xa4: c = 0x20ac; break;   /* euro */
6026 	    case 0xa6: c = 0x0160; break;   /* S hat */
6027 	    case 0xa8: c = 0x0161; break;   /* S -hat */
6028 	    case 0xb4: c = 0x017d; break;   /* Z hat */
6029 	    case 0xb8: c = 0x017e; break;   /* Z -hat */
6030 	    case 0xbc: c = 0x0152; break;   /* OE */
6031 	    case 0xbd: c = 0x0153; break;   /* oe */
6032 	    case 0xbe: c = 0x0178; break;   /* Y */
6033 	}
6034 	*unicodebuf++ = c;
6035     }
6036 }
6037 
6038 #ifdef FEAT_RIGHTLEFT
6039 /*
6040  * What is this for?  In the case where you are using Win98 or Win2K or later,
6041  * and you are using a Hebrew font (or Arabic!), Windows does you a favor and
6042  * reverses the string sent to the TextOut... family.  This sucks, because we
6043  * go to a lot of effort to do the right thing, and there doesn't seem to be a
6044  * way to tell Windblows not to do this!
6045  *
6046  * The short of it is that this 'RevOut' only gets called if you are running
6047  * one of the new, "improved" MS OSes, and only if you are running in
6048  * 'rightleft' mode.  It makes display take *slightly* longer, but not
6049  * noticeably so.
6050  */
6051     static void
6052 RevOut( HDC s_hdc,
6053 	int col,
6054 	int row,
6055 	UINT foptions,
6056 	CONST RECT *pcliprect,
6057 	LPCTSTR text,
6058 	UINT len,
6059 	CONST INT *padding)
6060 {
6061     int		ix;
6062 
6063     for (ix = 0; ix < (int)len; ++ix)
6064 	ExtTextOut(s_hdc, col + TEXT_X(ix), row, foptions,
6065 					pcliprect, text + ix, 1, padding);
6066 }
6067 #endif
6068 
6069     static void
6070 draw_line(
6071     int		x1,
6072     int		y1,
6073     int		x2,
6074     int		y2,
6075     COLORREF	color)
6076 {
6077 #if defined(FEAT_DIRECTX)
6078     if (IS_ENABLE_DIRECTX())
6079 	DWriteContext_DrawLine(s_dwc, x1, y1, x2, y2, color);
6080     else
6081 #endif
6082     {
6083 	HPEN	hpen = CreatePen(PS_SOLID, 1, color);
6084 	HPEN	old_pen = SelectObject(s_hdc, hpen);
6085 	MoveToEx(s_hdc, x1, y1, NULL);
6086 	/* Note: LineTo() excludes the last pixel in the line. */
6087 	LineTo(s_hdc, x2, y2);
6088 	DeleteObject(SelectObject(s_hdc, old_pen));
6089     }
6090 }
6091 
6092     static void
6093 set_pixel(
6094     int		x,
6095     int		y,
6096     COLORREF	color)
6097 {
6098 #if defined(FEAT_DIRECTX)
6099     if (IS_ENABLE_DIRECTX())
6100 	DWriteContext_SetPixel(s_dwc, x, y, color);
6101     else
6102 #endif
6103 	SetPixel(s_hdc, x, y, color);
6104 }
6105 
6106     static void
6107 fill_rect(
6108     const RECT	*rcp,
6109     HBRUSH	hbr,
6110     COLORREF	color)
6111 {
6112 #if defined(FEAT_DIRECTX)
6113     if (IS_ENABLE_DIRECTX())
6114 	DWriteContext_FillRect(s_dwc, rcp, color);
6115     else
6116 #endif
6117     {
6118 	HBRUSH	hbr2;
6119 
6120 	if (hbr == NULL)
6121 	    hbr2 = CreateSolidBrush(color);
6122 	else
6123 	    hbr2 = hbr;
6124 	FillRect(s_hdc, rcp, hbr2);
6125 	if (hbr == NULL)
6126 	    DeleteBrush(hbr2);
6127     }
6128 }
6129 
6130     void
6131 gui_mch_draw_string(
6132     int		row,
6133     int		col,
6134     char_u	*text,
6135     int		len,
6136     int		flags)
6137 {
6138     static int	*padding = NULL;
6139     static int	pad_size = 0;
6140     int		i;
6141     const RECT	*pcliprect = NULL;
6142     UINT	foptions = 0;
6143     static WCHAR *unicodebuf = NULL;
6144     static int   *unicodepdy = NULL;
6145     static int	unibuflen = 0;
6146     int		n = 0;
6147     int		y;
6148 
6149     /*
6150      * Italic and bold text seems to have an extra row of pixels at the bottom
6151      * (below where the bottom of the character should be).  If we draw the
6152      * characters with a solid background, the top row of pixels in the
6153      * character below will be overwritten.  We can fix this by filling in the
6154      * background ourselves, to the correct character proportions, and then
6155      * writing the character in transparent mode.  Still have a problem when
6156      * the character is "_", which gets written on to the character below.
6157      * New fix: set gui.char_ascent to -1.  This shifts all characters up one
6158      * pixel in their slots, which fixes the problem with the bottom row of
6159      * pixels.	We still need this code because otherwise the top row of pixels
6160      * becomes a problem. - webb.
6161      */
6162     static HBRUSH	hbr_cache[2] = {NULL, NULL};
6163     static guicolor_T	brush_color[2] = {INVALCOLOR, INVALCOLOR};
6164     static int		brush_lru = 0;
6165     HBRUSH		hbr;
6166     RECT		rc;
6167 
6168     if (!(flags & DRAW_TRANSP))
6169     {
6170 	/*
6171 	 * Clear background first.
6172 	 * Note: FillRect() excludes right and bottom of rectangle.
6173 	 */
6174 	rc.left = FILL_X(col);
6175 	rc.top = FILL_Y(row);
6176 	if (has_mbyte)
6177 	{
6178 	    /* Compute the length in display cells. */
6179 	    rc.right = FILL_X(col + mb_string2cells(text, len));
6180 	}
6181 	else
6182 	    rc.right = FILL_X(col + len);
6183 	rc.bottom = FILL_Y(row + 1);
6184 
6185 	/* Cache the created brush, that saves a lot of time.  We need two:
6186 	 * one for cursor background and one for the normal background. */
6187 	if (gui.currBgColor == brush_color[0])
6188 	{
6189 	    hbr = hbr_cache[0];
6190 	    brush_lru = 1;
6191 	}
6192 	else if (gui.currBgColor == brush_color[1])
6193 	{
6194 	    hbr = hbr_cache[1];
6195 	    brush_lru = 0;
6196 	}
6197 	else
6198 	{
6199 	    if (hbr_cache[brush_lru] != NULL)
6200 		DeleteBrush(hbr_cache[brush_lru]);
6201 	    hbr_cache[brush_lru] = CreateSolidBrush(gui.currBgColor);
6202 	    brush_color[brush_lru] = gui.currBgColor;
6203 	    hbr = hbr_cache[brush_lru];
6204 	    brush_lru = !brush_lru;
6205 	}
6206 
6207 	fill_rect(&rc, hbr, gui.currBgColor);
6208 
6209 	SetBkMode(s_hdc, TRANSPARENT);
6210 
6211 	/*
6212 	 * When drawing block cursor, prevent inverted character spilling
6213 	 * over character cell (can happen with bold/italic)
6214 	 */
6215 	if (flags & DRAW_CURSOR)
6216 	{
6217 	    pcliprect = &rc;
6218 	    foptions = ETO_CLIPPED;
6219 	}
6220     }
6221     SetTextColor(s_hdc, gui.currFgColor);
6222     SelectFont(s_hdc, gui.currFont);
6223 
6224 #ifdef FEAT_DIRECTX
6225     if (IS_ENABLE_DIRECTX())
6226 	DWriteContext_SetFont(s_dwc, (HFONT)gui.currFont);
6227 #endif
6228 
6229     if (pad_size != Columns || padding == NULL || padding[0] != gui.char_width)
6230     {
6231 	vim_free(padding);
6232 	pad_size = Columns;
6233 
6234 	/* Don't give an out-of-memory message here, it would call us
6235 	 * recursively. */
6236 	padding = (int *)lalloc(pad_size * sizeof(int), FALSE);
6237 	if (padding != NULL)
6238 	    for (i = 0; i < pad_size; i++)
6239 		padding[i] = gui.char_width;
6240     }
6241 
6242     /*
6243      * We have to provide the padding argument because italic and bold versions
6244      * of fixed-width fonts are often one pixel or so wider than their normal
6245      * versions.
6246      * No check for DRAW_BOLD, Windows will have done it already.
6247      */
6248 
6249     /* Check if there are any UTF-8 characters.  If not, use normal text
6250      * output to speed up output. */
6251     if (enc_utf8)
6252 	for (n = 0; n < len; ++n)
6253 	    if (text[n] >= 0x80)
6254 		break;
6255 
6256 #if defined(FEAT_DIRECTX)
6257     /* Quick hack to enable DirectWrite.  To use DirectWrite (antialias), it is
6258      * required that unicode drawing routine, currently.  So this forces it
6259      * enabled. */
6260     if (IS_ENABLE_DIRECTX())
6261 	n = 0; /* Keep n < len, to enter block for unicode. */
6262 #endif
6263 
6264     /* Check if the Unicode buffer exists and is big enough.  Create it
6265      * with the same length as the multi-byte string, the number of wide
6266      * characters is always equal or smaller. */
6267     if ((enc_utf8
6268 		|| (enc_codepage > 0 && (int)GetACP() != enc_codepage)
6269 		|| enc_latin9)
6270 	    && (unicodebuf == NULL || len > unibuflen))
6271     {
6272 	vim_free(unicodebuf);
6273 	unicodebuf = (WCHAR *)lalloc(len * sizeof(WCHAR), FALSE);
6274 
6275 	vim_free(unicodepdy);
6276 	unicodepdy = (int *)lalloc(len * sizeof(int), FALSE);
6277 
6278 	unibuflen = len;
6279     }
6280 
6281     if (enc_utf8 && n < len && unicodebuf != NULL)
6282     {
6283 	/* Output UTF-8 characters.  Composing characters should be
6284 	 * handled here. */
6285 	int		i;
6286 	int		wlen;	/* string length in words */
6287 	int		clen;	/* string length in characters */
6288 	int		cells;	/* cell width of string up to composing char */
6289 	int		cw;	/* width of current cell */
6290 	int		c;
6291 
6292 	wlen = 0;
6293 	clen = 0;
6294 	cells = 0;
6295 	for (i = 0; i < len; )
6296 	{
6297 	    c = utf_ptr2char(text + i);
6298 	    if (c >= 0x10000)
6299 	    {
6300 		/* Turn into UTF-16 encoding. */
6301 		unicodebuf[wlen++] = ((c - 0x10000) >> 10) + 0xD800;
6302 		unicodebuf[wlen++] = ((c - 0x10000) & 0x3ff) + 0xDC00;
6303 	    }
6304 	    else
6305 	    {
6306 		unicodebuf[wlen++] = c;
6307 	    }
6308 
6309 	    if (utf_iscomposing(c))
6310 		cw = 0;
6311 	    else
6312 	    {
6313 		cw = utf_char2cells(c);
6314 		if (cw > 2)		/* don't use 4 for unprintable char */
6315 		    cw = 1;
6316 	    }
6317 
6318 	    if (unicodepdy != NULL)
6319 	    {
6320 		/* Use unicodepdy to make characters fit as we expect, even
6321 		 * when the font uses different widths (e.g., bold character
6322 		 * is wider).  */
6323 		if (c >= 0x10000)
6324 		{
6325 		    unicodepdy[wlen - 2] = cw * gui.char_width;
6326 		    unicodepdy[wlen - 1] = 0;
6327 		}
6328 		else
6329 		    unicodepdy[wlen - 1] = cw * gui.char_width;
6330 	    }
6331 	    cells += cw;
6332 	    i += utf_ptr2len_len(text + i, len - i);
6333 	    ++clen;
6334 	}
6335 #if defined(FEAT_DIRECTX)
6336 	if (IS_ENABLE_DIRECTX())
6337 	{
6338 	    /* Add one to "cells" for italics. */
6339 	    DWriteContext_DrawText(s_dwc, unicodebuf, wlen,
6340 		    TEXT_X(col), TEXT_Y(row), FILL_X(cells + 1), FILL_Y(1),
6341 		    gui.char_width, gui.currFgColor,
6342 		    foptions, pcliprect, unicodepdy);
6343 	}
6344 	else
6345 #endif
6346 	    ExtTextOutW(s_hdc, TEXT_X(col), TEXT_Y(row),
6347 		    foptions, pcliprect, unicodebuf, wlen, unicodepdy);
6348 	len = cells;	/* used for underlining */
6349     }
6350     else if ((enc_codepage > 0 && (int)GetACP() != enc_codepage) || enc_latin9)
6351     {
6352 	/* If we want to display codepage data, and the current CP is not the
6353 	 * ANSI one, we need to go via Unicode. */
6354 	if (unicodebuf != NULL)
6355 	{
6356 	    if (enc_latin9)
6357 		latin9_to_ucs(text, len, unicodebuf);
6358 	    else
6359 		len = MultiByteToWideChar(enc_codepage,
6360 			MB_PRECOMPOSED,
6361 			(char *)text, len,
6362 			(LPWSTR)unicodebuf, unibuflen);
6363 	    if (len != 0)
6364 	    {
6365 		/* Use unicodepdy to make characters fit as we expect, even
6366 		 * when the font uses different widths (e.g., bold character
6367 		 * is wider). */
6368 		if (unicodepdy != NULL)
6369 		{
6370 		    int i;
6371 		    int cw;
6372 
6373 		    for (i = 0; i < len; ++i)
6374 		    {
6375 			cw = utf_char2cells(unicodebuf[i]);
6376 			if (cw > 2)
6377 			    cw = 1;
6378 			unicodepdy[i] = cw * gui.char_width;
6379 		    }
6380 		}
6381 		ExtTextOutW(s_hdc, TEXT_X(col), TEXT_Y(row),
6382 			    foptions, pcliprect, unicodebuf, len, unicodepdy);
6383 	    }
6384 	}
6385     }
6386     else
6387     {
6388 #ifdef FEAT_RIGHTLEFT
6389 	/* Windows will mess up RL text, so we have to draw it character by
6390 	 * character.  Only do this if RL is on, since it's slow. */
6391 	if (curwin->w_p_rl)
6392 	    RevOut(s_hdc, TEXT_X(col), TEXT_Y(row),
6393 			 foptions, pcliprect, (char *)text, len, padding);
6394 	else
6395 #endif
6396 	    ExtTextOut(s_hdc, TEXT_X(col), TEXT_Y(row),
6397 			 foptions, pcliprect, (char *)text, len, padding);
6398     }
6399 
6400     /* Underline */
6401     if (flags & DRAW_UNDERL)
6402     {
6403 	/* When p_linespace is 0, overwrite the bottom row of pixels.
6404 	 * Otherwise put the line just below the character. */
6405 	y = FILL_Y(row + 1) - 1;
6406 	if (p_linespace > 1)
6407 	    y -= p_linespace - 1;
6408 	draw_line(FILL_X(col), y, FILL_X(col + len), y, gui.currFgColor);
6409     }
6410 
6411     /* Strikethrough */
6412     if (flags & DRAW_STRIKE)
6413     {
6414 	y = FILL_Y(row + 1) - gui.char_height/2;
6415 	draw_line(FILL_X(col), y, FILL_X(col + len), y, gui.currSpColor);
6416     }
6417 
6418     /* Undercurl */
6419     if (flags & DRAW_UNDERC)
6420     {
6421 	int			x;
6422 	int			offset;
6423 	static const int	val[8] = {1, 0, 0, 0, 1, 2, 2, 2 };
6424 
6425 	y = FILL_Y(row + 1) - 1;
6426 	for (x = FILL_X(col); x < FILL_X(col + len); ++x)
6427 	{
6428 	    offset = val[x % 8];
6429 	    set_pixel(x, y - offset, gui.currSpColor);
6430 	}
6431     }
6432 }
6433 
6434 
6435 /*
6436  * Output routines.
6437  */
6438 
6439 /* Flush any output to the screen */
6440     void
6441 gui_mch_flush(void)
6442 {
6443 #   if defined(__BORLANDC__)
6444     /*
6445      * The GdiFlush declaration (in Borland C 5.01 <wingdi.h>) is not a
6446      * prototype declaration.
6447      * The compiler complains if __stdcall is not used in both declarations.
6448      */
6449     BOOL  __stdcall GdiFlush(void);
6450 #   endif
6451 
6452 #if defined(FEAT_DIRECTX)
6453     if (IS_ENABLE_DIRECTX())
6454 	DWriteContext_Flush(s_dwc);
6455 #endif
6456 
6457     GdiFlush();
6458 }
6459 
6460     static void
6461 clear_rect(RECT *rcp)
6462 {
6463     fill_rect(rcp, NULL, gui.back_pixel);
6464 }
6465 
6466 
6467     void
6468 gui_mch_get_screen_dimensions(int *screen_w, int *screen_h)
6469 {
6470     RECT	workarea_rect;
6471 
6472     get_work_area(&workarea_rect);
6473 
6474     *screen_w = workarea_rect.right - workarea_rect.left
6475 		- (GetSystemMetrics(SM_CXFRAME) +
6476 		   GetSystemMetrics(SM_CXPADDEDBORDER)) * 2;
6477 
6478     /* FIXME: dirty trick: Because the gui_get_base_height() doesn't include
6479      * the menubar for MSwin, we subtract it from the screen height, so that
6480      * the window size can be made to fit on the screen. */
6481     *screen_h = workarea_rect.bottom - workarea_rect.top
6482 		- (GetSystemMetrics(SM_CYFRAME) +
6483 		   GetSystemMetrics(SM_CXPADDEDBORDER)) * 2
6484 		- GetSystemMetrics(SM_CYCAPTION)
6485 #ifdef FEAT_MENU
6486 		- gui_mswin_get_menu_height(FALSE)
6487 #endif
6488 		;
6489 }
6490 
6491 
6492 #if defined(FEAT_MENU) || defined(PROTO)
6493 /*
6494  * Add a sub menu to the menu bar.
6495  */
6496     void
6497 gui_mch_add_menu(
6498     vimmenu_T	*menu,
6499     int		pos)
6500 {
6501     vimmenu_T	*parent = menu->parent;
6502 
6503     menu->submenu_id = CreatePopupMenu();
6504     menu->id = s_menu_id++;
6505 
6506     if (menu_is_menubar(menu->name))
6507     {
6508 	WCHAR	*wn = NULL;
6509 
6510 	if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
6511 	{
6512 	    /* 'encoding' differs from active codepage: convert menu name
6513 	     * and use wide function */
6514 	    wn = enc_to_utf16(menu->name, NULL);
6515 	    if (wn != NULL)
6516 	    {
6517 		MENUITEMINFOW	infow;
6518 
6519 		infow.cbSize = sizeof(infow);
6520 		infow.fMask = MIIM_DATA | MIIM_TYPE | MIIM_ID
6521 		    | MIIM_SUBMENU;
6522 		infow.dwItemData = (long_u)menu;
6523 		infow.wID = menu->id;
6524 		infow.fType = MFT_STRING;
6525 		infow.dwTypeData = wn;
6526 		infow.cch = (UINT)wcslen(wn);
6527 		infow.hSubMenu = menu->submenu_id;
6528 		InsertMenuItemW((parent == NULL)
6529 			? s_menuBar : parent->submenu_id,
6530 			(UINT)pos, TRUE, &infow);
6531 		vim_free(wn);
6532 	    }
6533 	}
6534 
6535 	if (wn == NULL)
6536 	{
6537 	    MENUITEMINFO	info;
6538 
6539 	    info.cbSize = sizeof(info);
6540 	    info.fMask = MIIM_DATA | MIIM_TYPE | MIIM_ID | MIIM_SUBMENU;
6541 	    info.dwItemData = (long_u)menu;
6542 	    info.wID = menu->id;
6543 	    info.fType = MFT_STRING;
6544 	    info.dwTypeData = (LPTSTR)menu->name;
6545 	    info.cch = (UINT)STRLEN(menu->name);
6546 	    info.hSubMenu = menu->submenu_id;
6547 	    InsertMenuItem((parent == NULL)
6548 		    ? s_menuBar : parent->submenu_id,
6549 		    (UINT)pos, TRUE, &info);
6550 	}
6551     }
6552 
6553     /* Fix window size if menu may have wrapped */
6554     if (parent == NULL)
6555 	gui_mswin_get_menu_height(!gui.starting);
6556 #ifdef FEAT_TEAROFF
6557     else if (IsWindow(parent->tearoff_handle))
6558 	rebuild_tearoff(parent);
6559 #endif
6560 }
6561 
6562     void
6563 gui_mch_show_popupmenu(vimmenu_T *menu)
6564 {
6565     POINT mp;
6566 
6567     (void)GetCursorPos((LPPOINT)&mp);
6568     gui_mch_show_popupmenu_at(menu, (int)mp.x, (int)mp.y);
6569 }
6570 
6571     void
6572 gui_make_popup(char_u *path_name, int mouse_pos)
6573 {
6574     vimmenu_T	*menu = gui_find_menu(path_name);
6575 
6576     if (menu != NULL)
6577     {
6578 	POINT	p;
6579 
6580 	/* Find the position of the current cursor */
6581 	GetDCOrgEx(s_hdc, &p);
6582 	if (mouse_pos)
6583 	{
6584 	    int	mx, my;
6585 
6586 	    gui_mch_getmouse(&mx, &my);
6587 	    p.x += mx;
6588 	    p.y += my;
6589 	}
6590 	else if (curwin != NULL)
6591 	{
6592 	    p.x += TEXT_X(curwin->w_wincol + curwin->w_wcol + 1);
6593 	    p.y += TEXT_Y(W_WINROW(curwin) + curwin->w_wrow + 1);
6594 	}
6595 	msg_scroll = FALSE;
6596 	gui_mch_show_popupmenu_at(menu, (int)p.x, (int)p.y);
6597     }
6598 }
6599 
6600 #if defined(FEAT_TEAROFF) || defined(PROTO)
6601 /*
6602  * Given a menu descriptor, e.g. "File.New", find it in the menu hierarchy and
6603  * create it as a pseudo-"tearoff menu".
6604  */
6605     void
6606 gui_make_tearoff(char_u *path_name)
6607 {
6608     vimmenu_T	*menu = gui_find_menu(path_name);
6609 
6610     /* Found the menu, so tear it off. */
6611     if (menu != NULL)
6612 	gui_mch_tearoff(menu->dname, menu, 0xffffL, 0xffffL);
6613 }
6614 #endif
6615 
6616 /*
6617  * Add a menu item to a menu
6618  */
6619     void
6620 gui_mch_add_menu_item(
6621     vimmenu_T	*menu,
6622     int		idx)
6623 {
6624     vimmenu_T	*parent = menu->parent;
6625 
6626     menu->id = s_menu_id++;
6627     menu->submenu_id = NULL;
6628 
6629 #ifdef FEAT_TEAROFF
6630     if (STRNCMP(menu->name, TEAR_STRING, TEAR_LEN) == 0)
6631     {
6632 	InsertMenu(parent->submenu_id, (UINT)idx, MF_BITMAP|MF_BYPOSITION,
6633 		(UINT)menu->id, (LPCTSTR) s_htearbitmap);
6634     }
6635     else
6636 #endif
6637 #ifdef FEAT_TOOLBAR
6638     if (menu_is_toolbar(parent->name))
6639     {
6640 	TBBUTTON newtb;
6641 
6642 	vim_memset(&newtb, 0, sizeof(newtb));
6643 	if (menu_is_separator(menu->name))
6644 	{
6645 	    newtb.iBitmap = 0;
6646 	    newtb.fsStyle = TBSTYLE_SEP;
6647 	}
6648 	else
6649 	{
6650 	    newtb.iBitmap = get_toolbar_bitmap(menu);
6651 	    newtb.fsStyle = TBSTYLE_BUTTON;
6652 	}
6653 	newtb.idCommand = menu->id;
6654 	newtb.fsState = TBSTATE_ENABLED;
6655 	newtb.iString = 0;
6656 	SendMessage(s_toolbarhwnd, TB_INSERTBUTTON, (WPARAM)idx,
6657 							     (LPARAM)&newtb);
6658 	menu->submenu_id = (HMENU)-1;
6659     }
6660     else
6661 #endif
6662     {
6663 	WCHAR	*wn = NULL;
6664 
6665 	if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
6666 	{
6667 	    /* 'encoding' differs from active codepage: convert menu item name
6668 	     * and use wide function */
6669 	    wn = enc_to_utf16(menu->name, NULL);
6670 	    if (wn != NULL)
6671 	    {
6672 		InsertMenuW(parent->submenu_id, (UINT)idx,
6673 			(menu_is_separator(menu->name)
6674 				 ? MF_SEPARATOR : MF_STRING) | MF_BYPOSITION,
6675 			(UINT)menu->id, wn);
6676 		vim_free(wn);
6677 	    }
6678 	}
6679 	if (wn == NULL)
6680 	    InsertMenu(parent->submenu_id, (UINT)idx,
6681 		(menu_is_separator(menu->name) ? MF_SEPARATOR : MF_STRING)
6682 							      | MF_BYPOSITION,
6683 		(UINT)menu->id, (LPCTSTR)menu->name);
6684 #ifdef FEAT_TEAROFF
6685 	if (IsWindow(parent->tearoff_handle))
6686 	    rebuild_tearoff(parent);
6687 #endif
6688     }
6689 }
6690 
6691 /*
6692  * Destroy the machine specific menu widget.
6693  */
6694     void
6695 gui_mch_destroy_menu(vimmenu_T *menu)
6696 {
6697 #ifdef FEAT_TOOLBAR
6698     /*
6699      * is this a toolbar button?
6700      */
6701     if (menu->submenu_id == (HMENU)-1)
6702     {
6703 	int iButton;
6704 
6705 	iButton = (int)SendMessage(s_toolbarhwnd, TB_COMMANDTOINDEX,
6706 							 (WPARAM)menu->id, 0);
6707 	SendMessage(s_toolbarhwnd, TB_DELETEBUTTON, (WPARAM)iButton, 0);
6708     }
6709     else
6710 #endif
6711     {
6712 	if (menu->parent != NULL
6713 		&& menu_is_popup(menu->parent->dname)
6714 		&& menu->parent->submenu_id != NULL)
6715 	    RemoveMenu(menu->parent->submenu_id, menu->id, MF_BYCOMMAND);
6716 	else
6717 	    RemoveMenu(s_menuBar, menu->id, MF_BYCOMMAND);
6718 	if (menu->submenu_id != NULL)
6719 	    DestroyMenu(menu->submenu_id);
6720 #ifdef FEAT_TEAROFF
6721 	if (IsWindow(menu->tearoff_handle))
6722 	    DestroyWindow(menu->tearoff_handle);
6723 	if (menu->parent != NULL
6724 		&& menu->parent->children != NULL
6725 		&& IsWindow(menu->parent->tearoff_handle))
6726 	{
6727 	    /* This menu must not show up when rebuilding the tearoff window. */
6728 	    menu->modes = 0;
6729 	    rebuild_tearoff(menu->parent);
6730 	}
6731 #endif
6732     }
6733 }
6734 
6735 #ifdef FEAT_TEAROFF
6736     static void
6737 rebuild_tearoff(vimmenu_T *menu)
6738 {
6739     /*hackish*/
6740     char_u	tbuf[128];
6741     RECT	trect;
6742     RECT	rct;
6743     RECT	roct;
6744     int		x, y;
6745 
6746     HWND thwnd = menu->tearoff_handle;
6747 
6748     GetWindowText(thwnd, (LPSTR)tbuf, 127);
6749     if (GetWindowRect(thwnd, &trect)
6750 	    && GetWindowRect(s_hwnd, &rct)
6751 	    && GetClientRect(s_hwnd, &roct))
6752     {
6753 	x = trect.left - rct.left;
6754 	y = (trect.top -  rct.bottom  + roct.bottom);
6755     }
6756     else
6757     {
6758 	x = y = 0xffffL;
6759     }
6760     DestroyWindow(thwnd);
6761     if (menu->children != NULL)
6762     {
6763 	gui_mch_tearoff(tbuf, menu, x, y);
6764 	if (IsWindow(menu->tearoff_handle))
6765 	    (void) SetWindowPos(menu->tearoff_handle,
6766 				NULL,
6767 				(int)trect.left,
6768 				(int)trect.top,
6769 				0, 0,
6770 				SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
6771     }
6772 }
6773 #endif /* FEAT_TEAROFF */
6774 
6775 /*
6776  * Make a menu either grey or not grey.
6777  */
6778     void
6779 gui_mch_menu_grey(
6780     vimmenu_T	*menu,
6781     int	    grey)
6782 {
6783 #ifdef FEAT_TOOLBAR
6784     /*
6785      * is this a toolbar button?
6786      */
6787     if (menu->submenu_id == (HMENU)-1)
6788     {
6789 	SendMessage(s_toolbarhwnd, TB_ENABLEBUTTON,
6790 	    (WPARAM)menu->id, (LPARAM) MAKELONG((grey ? FALSE : TRUE), 0) );
6791     }
6792     else
6793 #endif
6794     (void)EnableMenuItem(menu->parent ? menu->parent->submenu_id : s_menuBar,
6795 		    menu->id, MF_BYCOMMAND | (grey ? MF_GRAYED : MF_ENABLED));
6796 
6797 #ifdef FEAT_TEAROFF
6798     if ((menu->parent != NULL) && (IsWindow(menu->parent->tearoff_handle)))
6799     {
6800 	WORD menuID;
6801 	HWND menuHandle;
6802 
6803 	/*
6804 	 * A tearoff button has changed state.
6805 	 */
6806 	if (menu->children == NULL)
6807 	    menuID = (WORD)(menu->id);
6808 	else
6809 	    menuID = (WORD)((long_u)(menu->submenu_id) | (DWORD)0x8000);
6810 	menuHandle = GetDlgItem(menu->parent->tearoff_handle, menuID);
6811 	if (menuHandle)
6812 	    EnableWindow(menuHandle, !grey);
6813 
6814     }
6815 #endif
6816 }
6817 
6818 #endif /* FEAT_MENU */
6819 
6820 
6821 /* define some macros used to make the dialogue creation more readable */
6822 
6823 #define add_string(s) strcpy((LPSTR)p, s); (LPSTR)p += (strlen((LPSTR)p) + 1)
6824 #define add_word(x)		*p++ = (x)
6825 #define add_long(x)		dwp = (DWORD *)p; *dwp++ = (x); p = (WORD *)dwp
6826 
6827 #if defined(FEAT_GUI_DIALOG) || defined(PROTO)
6828 /*
6829  * stuff for dialogs
6830  */
6831 
6832 /*
6833  * The callback routine used by all the dialogs.  Very simple.  First,
6834  * acknowledges the INITDIALOG message so that Windows knows to do standard
6835  * dialog stuff (Return = default, Esc = cancel....) Second, if a button is
6836  * pressed, return that button's ID - IDCANCEL (2), which is the button's
6837  * number.
6838  */
6839     static LRESULT CALLBACK
6840 dialog_callback(
6841     HWND hwnd,
6842     UINT message,
6843     WPARAM wParam,
6844     LPARAM lParam UNUSED)
6845 {
6846     if (message == WM_INITDIALOG)
6847     {
6848 	CenterWindow(hwnd, GetWindow(hwnd, GW_OWNER));
6849 	/* Set focus to the dialog.  Set the default button, if specified. */
6850 	(void)SetFocus(hwnd);
6851 	if (dialog_default_button > IDCANCEL)
6852 	    (void)SetFocus(GetDlgItem(hwnd, dialog_default_button));
6853 	else
6854 	    /* We don't have a default, set focus on another element of the
6855 	     * dialog window, probably the icon */
6856 	    (void)SetFocus(GetDlgItem(hwnd, DLG_NONBUTTON_CONTROL));
6857 	return FALSE;
6858     }
6859 
6860     if (message == WM_COMMAND)
6861     {
6862 	int	button = LOWORD(wParam);
6863 
6864 	/* Don't end the dialog if something was selected that was
6865 	 * not a button.
6866 	 */
6867 	if (button >= DLG_NONBUTTON_CONTROL)
6868 	    return TRUE;
6869 
6870 	/* If the edit box exists, copy the string. */
6871 	if (s_textfield != NULL)
6872 	{
6873 	    /* If the OS is Windows NT, and 'encoding' differs from active
6874 	     * codepage: use wide function and convert text. */
6875 	    if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
6876 	    {
6877 	       WCHAR  *wp = (WCHAR *)alloc(IOSIZE * sizeof(WCHAR));
6878 	       char_u *p;
6879 
6880 	       GetDlgItemTextW(hwnd, DLG_NONBUTTON_CONTROL + 2, wp, IOSIZE);
6881 	       p = utf16_to_enc(wp, NULL);
6882 	       vim_strncpy(s_textfield, p, IOSIZE);
6883 	       vim_free(p);
6884 	       vim_free(wp);
6885 	    }
6886 	    else
6887 		GetDlgItemText(hwnd, DLG_NONBUTTON_CONTROL + 2,
6888 						(LPSTR)s_textfield, IOSIZE);
6889 	}
6890 
6891 	/*
6892 	 * Need to check for IDOK because if the user just hits Return to
6893 	 * accept the default value, some reason this is what we get.
6894 	 */
6895 	if (button == IDOK)
6896 	{
6897 	    if (dialog_default_button > IDCANCEL)
6898 		EndDialog(hwnd, dialog_default_button);
6899 	}
6900 	else
6901 	    EndDialog(hwnd, button - IDCANCEL);
6902 	return TRUE;
6903     }
6904 
6905     if ((message == WM_SYSCOMMAND) && (wParam == SC_CLOSE))
6906     {
6907 	EndDialog(hwnd, 0);
6908 	return TRUE;
6909     }
6910     return FALSE;
6911 }
6912 
6913 /*
6914  * Create a dialog dynamically from the parameter strings.
6915  * type		= type of dialog (question, alert, etc.)
6916  * title	= dialog title. may be NULL for default title.
6917  * message	= text to display. Dialog sizes to accommodate it.
6918  * buttons	= '\n' separated list of button captions, default first.
6919  * dfltbutton	= number of default button.
6920  *
6921  * This routine returns 1 if the first button is pressed,
6922  *			2 for the second, etc.
6923  *
6924  *			0 indicates Esc was pressed.
6925  *			-1 for unexpected error
6926  *
6927  * If stubbing out this fn, return 1.
6928  */
6929 
6930 static const char *dlg_icons[] = /* must match names in resource file */
6931 {
6932     "IDR_VIM",
6933     "IDR_VIM_ERROR",
6934     "IDR_VIM_ALERT",
6935     "IDR_VIM_INFO",
6936     "IDR_VIM_QUESTION"
6937 };
6938 
6939     int
6940 gui_mch_dialog(
6941     int		 type,
6942     char_u	*title,
6943     char_u	*message,
6944     char_u	*buttons,
6945     int		 dfltbutton,
6946     char_u	*textfield,
6947     int		ex_cmd)
6948 {
6949     WORD	*p, *pdlgtemplate, *pnumitems;
6950     DWORD	*dwp;
6951     int		numButtons;
6952     int		*buttonWidths, *buttonPositions;
6953     int		buttonYpos;
6954     int		nchar, i;
6955     DWORD	lStyle;
6956     int		dlgwidth = 0;
6957     int		dlgheight;
6958     int		editboxheight;
6959     int		horizWidth = 0;
6960     int		msgheight;
6961     char_u	*pstart;
6962     char_u	*pend;
6963     char_u	*last_white;
6964     char_u	*tbuffer;
6965     RECT	rect;
6966     HWND	hwnd;
6967     HDC		hdc;
6968     HFONT	font, oldFont;
6969     TEXTMETRIC	fontInfo;
6970     int		fontHeight;
6971     int		textWidth, minButtonWidth, messageWidth;
6972     int		maxDialogWidth;
6973     int		maxDialogHeight;
6974     int		scroll_flag = 0;
6975     int		vertical;
6976     int		dlgPaddingX;
6977     int		dlgPaddingY;
6978 #ifdef USE_SYSMENU_FONT
6979     LOGFONT	lfSysmenu;
6980     int		use_lfSysmenu = FALSE;
6981 #endif
6982     garray_T	ga;
6983     int		l;
6984 
6985 #ifndef NO_CONSOLE
6986     /* Don't output anything in silent mode ("ex -s") */
6987     if (silent_mode)
6988 	return dfltbutton;   /* return default option */
6989 #endif
6990 
6991     if (s_hwnd == NULL)
6992 	get_dialog_font_metrics();
6993 
6994     if ((type < 0) || (type > VIM_LAST_TYPE))
6995 	type = 0;
6996 
6997     /* allocate some memory for dialog template */
6998     /* TODO should compute this really */
6999     pdlgtemplate = p = (PWORD)LocalAlloc(LPTR,
7000 					DLG_ALLOC_SIZE + STRLEN(message) * 2);
7001 
7002     if (p == NULL)
7003 	return -1;
7004 
7005     /*
7006      * make a copy of 'buttons' to fiddle with it.  compiler grizzles because
7007      * vim_strsave() doesn't take a const arg (why not?), so cast away the
7008      * const.
7009      */
7010     tbuffer = vim_strsave(buttons);
7011     if (tbuffer == NULL)
7012 	return -1;
7013 
7014     --dfltbutton;   /* Change from one-based to zero-based */
7015 
7016     /* Count buttons */
7017     numButtons = 1;
7018     for (i = 0; tbuffer[i] != '\0'; i++)
7019     {
7020 	if (tbuffer[i] == DLG_BUTTON_SEP)
7021 	    numButtons++;
7022     }
7023     if (dfltbutton >= numButtons)
7024 	dfltbutton = -1;
7025 
7026     /* Allocate array to hold the width of each button */
7027     buttonWidths = (int *)lalloc(numButtons * sizeof(int), TRUE);
7028     if (buttonWidths == NULL)
7029 	return -1;
7030 
7031     /* Allocate array to hold the X position of each button */
7032     buttonPositions = (int *)lalloc(numButtons * sizeof(int), TRUE);
7033     if (buttonPositions == NULL)
7034 	return -1;
7035 
7036     /*
7037      * Calculate how big the dialog must be.
7038      */
7039     hwnd = GetDesktopWindow();
7040     hdc = GetWindowDC(hwnd);
7041 #ifdef USE_SYSMENU_FONT
7042     if (gui_w32_get_menu_font(&lfSysmenu) == OK)
7043     {
7044 	font = CreateFontIndirect(&lfSysmenu);
7045 	use_lfSysmenu = TRUE;
7046     }
7047     else
7048 #endif
7049     font = CreateFont(-DLG_FONT_POINT_SIZE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
7050 		      VARIABLE_PITCH , DLG_FONT_NAME);
7051     if (s_usenewlook)
7052     {
7053 	oldFont = SelectFont(hdc, font);
7054 	dlgPaddingX = DLG_PADDING_X;
7055 	dlgPaddingY = DLG_PADDING_Y;
7056     }
7057     else
7058     {
7059 	oldFont = SelectFont(hdc, GetStockObject(SYSTEM_FONT));
7060 	dlgPaddingX = DLG_OLD_STYLE_PADDING_X;
7061 	dlgPaddingY = DLG_OLD_STYLE_PADDING_Y;
7062     }
7063     GetTextMetrics(hdc, &fontInfo);
7064     fontHeight = fontInfo.tmHeight;
7065 
7066     /* Minimum width for horizontal button */
7067     minButtonWidth = GetTextWidth(hdc, (char_u *)"Cancel", 6);
7068 
7069     /* Maximum width of a dialog, if possible */
7070     if (s_hwnd == NULL)
7071     {
7072 	RECT	workarea_rect;
7073 
7074 	/* We don't have a window, use the desktop area. */
7075 	get_work_area(&workarea_rect);
7076 	maxDialogWidth = workarea_rect.right - workarea_rect.left - 100;
7077 	if (maxDialogWidth > 600)
7078 	    maxDialogWidth = 600;
7079 	/* Leave some room for the taskbar. */
7080 	maxDialogHeight = workarea_rect.bottom - workarea_rect.top - 150;
7081     }
7082     else
7083     {
7084 	/* Use our own window for the size, unless it's very small. */
7085 	GetWindowRect(s_hwnd, &rect);
7086 	maxDialogWidth = rect.right - rect.left
7087 				   - (GetSystemMetrics(SM_CXFRAME) +
7088 				      GetSystemMetrics(SM_CXPADDEDBORDER)) * 2;
7089 	if (maxDialogWidth < DLG_MIN_MAX_WIDTH)
7090 	    maxDialogWidth = DLG_MIN_MAX_WIDTH;
7091 
7092 	maxDialogHeight = rect.bottom - rect.top
7093 				   - (GetSystemMetrics(SM_CYFRAME) +
7094 				      GetSystemMetrics(SM_CXPADDEDBORDER)) * 4
7095 				   - GetSystemMetrics(SM_CYCAPTION);
7096 	if (maxDialogHeight < DLG_MIN_MAX_HEIGHT)
7097 	    maxDialogHeight = DLG_MIN_MAX_HEIGHT;
7098     }
7099 
7100     /* Set dlgwidth to width of message.
7101      * Copy the message into "ga", changing NL to CR-NL and inserting line
7102      * breaks where needed. */
7103     pstart = message;
7104     messageWidth = 0;
7105     msgheight = 0;
7106     ga_init2(&ga, sizeof(char), 500);
7107     do
7108     {
7109 	msgheight += fontHeight;    /* at least one line */
7110 
7111 	/* Need to figure out where to break the string.  The system does it
7112 	 * at a word boundary, which would mean we can't compute the number of
7113 	 * wrapped lines. */
7114 	textWidth = 0;
7115 	last_white = NULL;
7116 	for (pend = pstart; *pend != NUL && *pend != '\n'; )
7117 	{
7118 	    l = (*mb_ptr2len)(pend);
7119 	    if (l == 1 && VIM_ISWHITE(*pend)
7120 					&& textWidth > maxDialogWidth * 3 / 4)
7121 		last_white = pend;
7122 	    textWidth += GetTextWidthEnc(hdc, pend, l);
7123 	    if (textWidth >= maxDialogWidth)
7124 	    {
7125 		/* Line will wrap. */
7126 		messageWidth = maxDialogWidth;
7127 		msgheight += fontHeight;
7128 		textWidth = 0;
7129 
7130 		if (last_white != NULL)
7131 		{
7132 		    /* break the line just after a space */
7133 		    ga.ga_len -= (int)(pend - (last_white + 1));
7134 		    pend = last_white + 1;
7135 		    last_white = NULL;
7136 		}
7137 		ga_append(&ga, '\r');
7138 		ga_append(&ga, '\n');
7139 		continue;
7140 	    }
7141 
7142 	    while (--l >= 0)
7143 		ga_append(&ga, *pend++);
7144 	}
7145 	if (textWidth > messageWidth)
7146 	    messageWidth = textWidth;
7147 
7148 	ga_append(&ga, '\r');
7149 	ga_append(&ga, '\n');
7150 	pstart = pend + 1;
7151     } while (*pend != NUL);
7152 
7153     if (ga.ga_data != NULL)
7154 	message = ga.ga_data;
7155 
7156     messageWidth += 10;		/* roundoff space */
7157 
7158     /* Add width of icon to dlgwidth, and some space */
7159     dlgwidth = messageWidth + DLG_ICON_WIDTH + 3 * dlgPaddingX
7160 					     + GetSystemMetrics(SM_CXVSCROLL);
7161 
7162     if (msgheight < DLG_ICON_HEIGHT)
7163 	msgheight = DLG_ICON_HEIGHT;
7164 
7165     /*
7166      * Check button names.  A long one will make the dialog wider.
7167      * When called early (-register error message) p_go isn't initialized.
7168      */
7169     vertical = (p_go != NULL && vim_strchr(p_go, GO_VERTICAL) != NULL);
7170     if (!vertical)
7171     {
7172 	// Place buttons horizontally if they fit.
7173 	horizWidth = dlgPaddingX;
7174 	pstart = tbuffer;
7175 	i = 0;
7176 	do
7177 	{
7178 	    pend = vim_strchr(pstart, DLG_BUTTON_SEP);
7179 	    if (pend == NULL)
7180 		pend = pstart + STRLEN(pstart);	// Last button name.
7181 	    textWidth = GetTextWidthEnc(hdc, pstart, (int)(pend - pstart));
7182 	    if (textWidth < minButtonWidth)
7183 		textWidth = minButtonWidth;
7184 	    textWidth += dlgPaddingX;	    /* Padding within button */
7185 	    buttonWidths[i] = textWidth;
7186 	    buttonPositions[i++] = horizWidth;
7187 	    horizWidth += textWidth + dlgPaddingX; /* Pad between buttons */
7188 	    pstart = pend + 1;
7189 	} while (*pend != NUL);
7190 
7191 	if (horizWidth > maxDialogWidth)
7192 	    vertical = TRUE;	// Too wide to fit on the screen.
7193 	else if (horizWidth > dlgwidth)
7194 	    dlgwidth = horizWidth;
7195     }
7196 
7197     if (vertical)
7198     {
7199 	// Stack buttons vertically.
7200 	pstart = tbuffer;
7201 	do
7202 	{
7203 	    pend = vim_strchr(pstart, DLG_BUTTON_SEP);
7204 	    if (pend == NULL)
7205 		pend = pstart + STRLEN(pstart);	// Last button name.
7206 	    textWidth = GetTextWidthEnc(hdc, pstart, (int)(pend - pstart));
7207 	    textWidth += dlgPaddingX;		/* Padding within button */
7208 	    textWidth += DLG_VERT_PADDING_X * 2; /* Padding around button */
7209 	    if (textWidth > dlgwidth)
7210 		dlgwidth = textWidth;
7211 	    pstart = pend + 1;
7212 	} while (*pend != NUL);
7213     }
7214 
7215     if (dlgwidth < DLG_MIN_WIDTH)
7216 	dlgwidth = DLG_MIN_WIDTH;	/* Don't allow a really thin dialog!*/
7217 
7218     /* start to fill in the dlgtemplate information.  addressing by WORDs */
7219     if (s_usenewlook)
7220 	lStyle = DS_MODALFRAME | WS_CAPTION |DS_3DLOOK| WS_VISIBLE |DS_SETFONT;
7221     else
7222 	lStyle = DS_MODALFRAME | WS_CAPTION |DS_3DLOOK| WS_VISIBLE;
7223 
7224     add_long(lStyle);
7225     add_long(0);	// (lExtendedStyle)
7226     pnumitems = p;	/*save where the number of items must be stored*/
7227     add_word(0);	// NumberOfItems(will change later)
7228     add_word(10);	// x
7229     add_word(10);	// y
7230     add_word(PixelToDialogX(dlgwidth));	// cx
7231 
7232     // Dialog height.
7233     if (vertical)
7234 	dlgheight = msgheight + 2 * dlgPaddingY
7235 			   + DLG_VERT_PADDING_Y + 2 * fontHeight * numButtons;
7236     else
7237 	dlgheight = msgheight + 3 * dlgPaddingY + 2 * fontHeight;
7238 
7239     // Dialog needs to be taller if contains an edit box.
7240     editboxheight = fontHeight + dlgPaddingY + 4 * DLG_VERT_PADDING_Y;
7241     if (textfield != NULL)
7242 	dlgheight += editboxheight;
7243 
7244     /* Restrict the size to a maximum.  Causes a scrollbar to show up. */
7245     if (dlgheight > maxDialogHeight)
7246     {
7247 	msgheight = msgheight - (dlgheight - maxDialogHeight);
7248 	dlgheight = maxDialogHeight;
7249 	scroll_flag = WS_VSCROLL;
7250 	/* Make sure scrollbar doesn't appear in the middle of the dialog */
7251 	messageWidth = dlgwidth - DLG_ICON_WIDTH - 3 * dlgPaddingX;
7252     }
7253 
7254     add_word(PixelToDialogY(dlgheight));
7255 
7256     add_word(0);	// Menu
7257     add_word(0);	// Class
7258 
7259     /* copy the title of the dialog */
7260     nchar = nCopyAnsiToWideChar(p, (title ? (LPSTR)title
7261 				   : (LPSTR)("Vim "VIM_VERSION_MEDIUM)), TRUE);
7262     p += nchar;
7263 
7264     if (s_usenewlook)
7265     {
7266 	/* do the font, since DS_3DLOOK doesn't work properly */
7267 #ifdef USE_SYSMENU_FONT
7268 	if (use_lfSysmenu)
7269 	{
7270 	    /* point size */
7271 	    *p++ = -MulDiv(lfSysmenu.lfHeight, 72,
7272 		    GetDeviceCaps(hdc, LOGPIXELSY));
7273 	    nchar = nCopyAnsiToWideChar(p, lfSysmenu.lfFaceName, FALSE);
7274 	}
7275 	else
7276 #endif
7277 	{
7278 	    *p++ = DLG_FONT_POINT_SIZE;		// point size
7279 	    nchar = nCopyAnsiToWideChar(p, DLG_FONT_NAME, FALSE);
7280 	}
7281 	p += nchar;
7282     }
7283 
7284     buttonYpos = msgheight + 2 * dlgPaddingY;
7285 
7286     if (textfield != NULL)
7287 	buttonYpos += editboxheight;
7288 
7289     pstart = tbuffer;
7290     if (!vertical)
7291 	horizWidth = (dlgwidth - horizWidth) / 2;	/* Now it's X offset */
7292     for (i = 0; i < numButtons; i++)
7293     {
7294 	/* get end of this button. */
7295 	for (	pend = pstart;
7296 		*pend && (*pend != DLG_BUTTON_SEP);
7297 		pend++)
7298 	    ;
7299 
7300 	if (*pend)
7301 	    *pend = '\0';
7302 
7303 	/*
7304 	 * old NOTE:
7305 	 * setting the BS_DEFPUSHBUTTON style doesn't work because Windows sets
7306 	 * the focus to the first tab-able button and in so doing makes that
7307 	 * the default!! Grrr.  Workaround: Make the default button the only
7308 	 * one with WS_TABSTOP style. Means user can't tab between buttons, but
7309 	 * he/she can use arrow keys.
7310 	 *
7311 	 * new NOTE: BS_DEFPUSHBUTTON is required to be able to select the
7312 	 * right button when hitting <Enter>.  E.g., for the ":confirm quit"
7313 	 * dialog.  Also needed for when the textfield is the default control.
7314 	 * It appears to work now (perhaps not on Win95?).
7315 	 */
7316 	if (vertical)
7317 	{
7318 	    p = add_dialog_element(p,
7319 		    (i == dfltbutton
7320 			    ? BS_DEFPUSHBUTTON : BS_PUSHBUTTON) | WS_TABSTOP,
7321 		    PixelToDialogX(DLG_VERT_PADDING_X),
7322 		    PixelToDialogY(buttonYpos /* TBK */
7323 				   + 2 * fontHeight * i),
7324 		    PixelToDialogX(dlgwidth - 2 * DLG_VERT_PADDING_X),
7325 		    (WORD)(PixelToDialogY(2 * fontHeight) - 1),
7326 		    (WORD)(IDCANCEL + 1 + i), (WORD)0x0080, (char *)pstart);
7327 	}
7328 	else
7329 	{
7330 	    p = add_dialog_element(p,
7331 		    (i == dfltbutton
7332 			    ? BS_DEFPUSHBUTTON : BS_PUSHBUTTON) | WS_TABSTOP,
7333 		    PixelToDialogX(horizWidth + buttonPositions[i]),
7334 		    PixelToDialogY(buttonYpos), /* TBK */
7335 		    PixelToDialogX(buttonWidths[i]),
7336 		    (WORD)(PixelToDialogY(2 * fontHeight) - 1),
7337 		    (WORD)(IDCANCEL + 1 + i), (WORD)0x0080, (char *)pstart);
7338 	}
7339 	pstart = pend + 1;	/*next button*/
7340     }
7341     *pnumitems += numButtons;
7342 
7343     /* Vim icon */
7344     p = add_dialog_element(p, SS_ICON,
7345 	    PixelToDialogX(dlgPaddingX),
7346 	    PixelToDialogY(dlgPaddingY),
7347 	    PixelToDialogX(DLG_ICON_WIDTH),
7348 	    PixelToDialogY(DLG_ICON_HEIGHT),
7349 	    DLG_NONBUTTON_CONTROL + 0, (WORD)0x0082,
7350 	    dlg_icons[type]);
7351 
7352     /* Dialog message */
7353     p = add_dialog_element(p, ES_LEFT|scroll_flag|ES_MULTILINE|ES_READONLY,
7354 	    PixelToDialogX(2 * dlgPaddingX + DLG_ICON_WIDTH),
7355 	    PixelToDialogY(dlgPaddingY),
7356 	    (WORD)(PixelToDialogX(messageWidth) + 1),
7357 	    PixelToDialogY(msgheight),
7358 	    DLG_NONBUTTON_CONTROL + 1, (WORD)0x0081, (char *)message);
7359 
7360     /* Edit box */
7361     if (textfield != NULL)
7362     {
7363 	p = add_dialog_element(p, ES_LEFT|ES_AUTOHSCROLL|WS_TABSTOP|WS_BORDER,
7364 		PixelToDialogX(2 * dlgPaddingX),
7365 		PixelToDialogY(2 * dlgPaddingY + msgheight),
7366 		PixelToDialogX(dlgwidth - 4 * dlgPaddingX),
7367 		PixelToDialogY(fontHeight + dlgPaddingY),
7368 		DLG_NONBUTTON_CONTROL + 2, (WORD)0x0081, (char *)textfield);
7369 	*pnumitems += 1;
7370     }
7371 
7372     *pnumitems += 2;
7373 
7374     SelectFont(hdc, oldFont);
7375     DeleteObject(font);
7376     ReleaseDC(hwnd, hdc);
7377 
7378     /* Let the dialog_callback() function know which button to make default
7379      * If we have an edit box, make that the default. We also need to tell
7380      * dialog_callback() if this dialog contains an edit box or not. We do
7381      * this by setting s_textfield if it does.
7382      */
7383     if (textfield != NULL)
7384     {
7385 	dialog_default_button = DLG_NONBUTTON_CONTROL + 2;
7386 	s_textfield = textfield;
7387     }
7388     else
7389     {
7390 	dialog_default_button = IDCANCEL + 1 + dfltbutton;
7391 	s_textfield = NULL;
7392     }
7393 
7394     /* show the dialog box modally and get a return value */
7395     nchar = (int)DialogBoxIndirect(
7396 	    s_hinst,
7397 	    (LPDLGTEMPLATE)pdlgtemplate,
7398 	    s_hwnd,
7399 	    (DLGPROC)dialog_callback);
7400 
7401     LocalFree(LocalHandle(pdlgtemplate));
7402     vim_free(tbuffer);
7403     vim_free(buttonWidths);
7404     vim_free(buttonPositions);
7405     vim_free(ga.ga_data);
7406 
7407     /* Focus back to our window (for when MDI is used). */
7408     (void)SetFocus(s_hwnd);
7409 
7410     return nchar;
7411 }
7412 
7413 #endif /* FEAT_GUI_DIALOG */
7414 
7415 /*
7416  * Put a simple element (basic class) onto a dialog template in memory.
7417  * return a pointer to where the next item should be added.
7418  *
7419  * parameters:
7420  *  lStyle = additional style flags
7421  *		(be careful, NT3.51 & Win32s will ignore the new ones)
7422  *  x,y = x & y positions IN DIALOG UNITS
7423  *  w,h = width and height IN DIALOG UNITS
7424  *  Id  = ID used in messages
7425  *  clss  = class ID, e.g 0x0080 for a button, 0x0082 for a static
7426  *  caption = usually text or resource name
7427  *
7428  *  TODO: use the length information noted here to enable the dialog creation
7429  *  routines to work out more exactly how much memory they need to alloc.
7430  */
7431     static PWORD
7432 add_dialog_element(
7433     PWORD p,
7434     DWORD lStyle,
7435     WORD x,
7436     WORD y,
7437     WORD w,
7438     WORD h,
7439     WORD Id,
7440     WORD clss,
7441     const char *caption)
7442 {
7443     int nchar;
7444 
7445     p = lpwAlign(p);	/* Align to dword boundary*/
7446     lStyle = lStyle | WS_VISIBLE | WS_CHILD;
7447     *p++ = LOWORD(lStyle);
7448     *p++ = HIWORD(lStyle);
7449     *p++ = 0;		// LOWORD (lExtendedStyle)
7450     *p++ = 0;		// HIWORD (lExtendedStyle)
7451     *p++ = x;
7452     *p++ = y;
7453     *p++ = w;
7454     *p++ = h;
7455     *p++ = Id;		//9 or 10 words in all
7456 
7457     *p++ = (WORD)0xffff;
7458     *p++ = clss;			//2 more here
7459 
7460     nchar = nCopyAnsiToWideChar(p, (LPSTR)caption, TRUE); //strlen(caption)+1
7461     p += nchar;
7462 
7463     *p++ = 0;  // advance pointer over nExtraStuff WORD   - 2 more
7464 
7465     return p;	//total = 15+ (strlen(caption)) words
7466 		//	   = 30 + 2(strlen(caption) bytes reqd
7467 }
7468 
7469 
7470 /*
7471  * Helper routine.  Take an input pointer, return closest pointer that is
7472  * aligned on a DWORD (4 byte) boundary.  Taken from the Win32SDK samples.
7473  */
7474     static LPWORD
7475 lpwAlign(
7476     LPWORD lpIn)
7477 {
7478     long_u ul;
7479 
7480     ul = (long_u)lpIn;
7481     ul += 3;
7482     ul >>= 2;
7483     ul <<= 2;
7484     return (LPWORD)ul;
7485 }
7486 
7487 /*
7488  * Helper routine.  Takes second parameter as Ansi string, copies it to first
7489  * parameter as wide character (16-bits / char) string, and returns integer
7490  * number of wide characters (words) in string (including the trailing wide
7491  * char NULL).  Partly taken from the Win32SDK samples.
7492  * If "use_enc" is TRUE, 'encoding' is used for "lpAnsiIn". If FALSE, current
7493  * ACP is used for "lpAnsiIn". */
7494     static int
7495 nCopyAnsiToWideChar(
7496     LPWORD lpWCStr,
7497     LPSTR lpAnsiIn,
7498     BOOL use_enc)
7499 {
7500     int		nChar = 0;
7501     int		len = lstrlen(lpAnsiIn) + 1;	/* include NUL character */
7502     int		i;
7503     WCHAR	*wn;
7504 
7505     if (use_enc && enc_codepage >= 0 && (int)GetACP() != enc_codepage)
7506     {
7507 	/* Not a codepage, use our own conversion function. */
7508 	wn = enc_to_utf16((char_u *)lpAnsiIn, NULL);
7509 	if (wn != NULL)
7510 	{
7511 	    wcscpy(lpWCStr, wn);
7512 	    nChar = (int)wcslen(wn) + 1;
7513 	    vim_free(wn);
7514 	}
7515     }
7516     if (nChar == 0)
7517 	/* Use Win32 conversion function. */
7518 	nChar = MultiByteToWideChar(
7519 		enc_codepage > 0 ? enc_codepage : CP_ACP,
7520 		MB_PRECOMPOSED,
7521 		lpAnsiIn, len,
7522 		lpWCStr, len);
7523     for (i = 0; i < nChar; ++i)
7524 	if (lpWCStr[i] == (WORD)'\t')	/* replace tabs with spaces */
7525 	    lpWCStr[i] = (WORD)' ';
7526 
7527     return nChar;
7528 }
7529 
7530 
7531 #ifdef FEAT_TEAROFF
7532 /*
7533  * Lookup menu handle from "menu_id".
7534  */
7535     static HMENU
7536 tearoff_lookup_menuhandle(
7537     vimmenu_T *menu,
7538     WORD menu_id)
7539 {
7540     for ( ; menu != NULL; menu = menu->next)
7541     {
7542 	if (menu->modes == 0)	/* this menu has just been deleted */
7543 	    continue;
7544 	if (menu_is_separator(menu->dname))
7545 	    continue;
7546 	if ((WORD)((long_u)(menu->submenu_id) | (DWORD)0x8000) == menu_id)
7547 	    return menu->submenu_id;
7548     }
7549     return NULL;
7550 }
7551 
7552 /*
7553  * The callback function for all the modeless dialogs that make up the
7554  * "tearoff menus" Very simple - forward button presses (to fool Vim into
7555  * thinking its menus have been clicked), and go away when closed.
7556  */
7557     static LRESULT CALLBACK
7558 tearoff_callback(
7559     HWND hwnd,
7560     UINT message,
7561     WPARAM wParam,
7562     LPARAM lParam)
7563 {
7564     if (message == WM_INITDIALOG)
7565     {
7566 	SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR)lParam);
7567 	return (TRUE);
7568     }
7569 
7570     /* May show the mouse pointer again. */
7571     HandleMouseHide(message, lParam);
7572 
7573     if (message == WM_COMMAND)
7574     {
7575 	if ((WORD)(LOWORD(wParam)) & 0x8000)
7576 	{
7577 	    POINT   mp;
7578 	    RECT    rect;
7579 
7580 	    if (GetCursorPos(&mp) && GetWindowRect(hwnd, &rect))
7581 	    {
7582 		vimmenu_T *menu;
7583 
7584 		menu = (vimmenu_T*)GetWindowLongPtr(hwnd, DWLP_USER);
7585 		(void)TrackPopupMenu(
7586 			 tearoff_lookup_menuhandle(menu, LOWORD(wParam)),
7587 			 TPM_LEFTALIGN | TPM_LEFTBUTTON,
7588 			 (int)rect.right - 8,
7589 			 (int)mp.y,
7590 			 (int)0,	    /*reserved param*/
7591 			 s_hwnd,
7592 			 NULL);
7593 		/*
7594 		 * NOTE: The pop-up menu can eat the mouse up event.
7595 		 * We deal with this in normal.c.
7596 		 */
7597 	    }
7598 	}
7599 	else
7600 	    /* Pass on messages to the main Vim window */
7601 	    PostMessage(s_hwnd, WM_COMMAND, LOWORD(wParam), 0);
7602 	/*
7603 	 * Give main window the focus back: this is so after
7604 	 * choosing a tearoff button you can start typing again
7605 	 * straight away.
7606 	 */
7607 	(void)SetFocus(s_hwnd);
7608 	return TRUE;
7609     }
7610     if ((message == WM_SYSCOMMAND) && (wParam == SC_CLOSE))
7611     {
7612 	DestroyWindow(hwnd);
7613 	return TRUE;
7614     }
7615 
7616     /* When moved around, give main window the focus back. */
7617     if (message == WM_EXITSIZEMOVE)
7618 	(void)SetActiveWindow(s_hwnd);
7619 
7620     return FALSE;
7621 }
7622 #endif
7623 
7624 
7625 /*
7626  * Decide whether to use the "new look" (small, non-bold font) or the "old
7627  * look" (big, clanky font) for dialogs, and work out a few values for use
7628  * later accordingly.
7629  */
7630     static void
7631 get_dialog_font_metrics(void)
7632 {
7633     HDC		    hdc;
7634     HFONT	    hfontTools = 0;
7635     DWORD	    dlgFontSize;
7636     SIZE	    size;
7637 #ifdef USE_SYSMENU_FONT
7638     LOGFONT	    lfSysmenu;
7639 #endif
7640 
7641     s_usenewlook = FALSE;
7642 
7643 #ifdef USE_SYSMENU_FONT
7644     if (gui_w32_get_menu_font(&lfSysmenu) == OK)
7645 	hfontTools = CreateFontIndirect(&lfSysmenu);
7646     else
7647 #endif
7648 	hfontTools = CreateFont(-DLG_FONT_POINT_SIZE, 0, 0, 0, 0, 0, 0, 0,
7649 				0, 0, 0, 0, VARIABLE_PITCH , DLG_FONT_NAME);
7650 
7651     if (hfontTools)
7652     {
7653 	hdc = GetDC(s_hwnd);
7654 	SelectObject(hdc, hfontTools);
7655 	/*
7656 	 * GetTextMetrics() doesn't return the right value in
7657 	 * tmAveCharWidth, so we have to figure out the dialog base units
7658 	 * ourselves.
7659 	 */
7660 	GetTextExtentPoint(hdc,
7661 		"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
7662 		52, &size);
7663 	ReleaseDC(s_hwnd, hdc);
7664 
7665 	s_dlgfntwidth = (WORD)((size.cx / 26 + 1) / 2);
7666 	s_dlgfntheight = (WORD)size.cy;
7667 	s_usenewlook = TRUE;
7668     }
7669 
7670     if (!s_usenewlook)
7671     {
7672 	dlgFontSize = GetDialogBaseUnits();	/* fall back to big old system*/
7673 	s_dlgfntwidth = LOWORD(dlgFontSize);
7674 	s_dlgfntheight = HIWORD(dlgFontSize);
7675     }
7676 }
7677 
7678 #if defined(FEAT_MENU) && defined(FEAT_TEAROFF)
7679 /*
7680  * Create a pseudo-"tearoff menu" based on the child
7681  * items of a given menu pointer.
7682  */
7683     static void
7684 gui_mch_tearoff(
7685     char_u	*title,
7686     vimmenu_T	*menu,
7687     int		initX,
7688     int		initY)
7689 {
7690     WORD	*p, *pdlgtemplate, *pnumitems, *ptrueheight;
7691     int		template_len;
7692     int		nchar, textWidth, submenuWidth;
7693     DWORD	lStyle;
7694     DWORD	lExtendedStyle;
7695     WORD	dlgwidth;
7696     WORD	menuID;
7697     vimmenu_T	*pmenu;
7698     vimmenu_T	*top_menu;
7699     vimmenu_T	*the_menu = menu;
7700     HWND	hwnd;
7701     HDC		hdc;
7702     HFONT	font, oldFont;
7703     int		col, spaceWidth, len;
7704     int		columnWidths[2];
7705     char_u	*label, *text;
7706     int		acLen = 0;
7707     int		nameLen;
7708     int		padding0, padding1, padding2 = 0;
7709     int		sepPadding=0;
7710     int		x;
7711     int		y;
7712 #ifdef USE_SYSMENU_FONT
7713     LOGFONT	lfSysmenu;
7714     int		use_lfSysmenu = FALSE;
7715 #endif
7716 
7717     /*
7718      * If this menu is already torn off, move it to the mouse position.
7719      */
7720     if (IsWindow(menu->tearoff_handle))
7721     {
7722 	POINT mp;
7723 	if (GetCursorPos((LPPOINT)&mp))
7724 	{
7725 	    SetWindowPos(menu->tearoff_handle, NULL, mp.x, mp.y, 0, 0,
7726 		    SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
7727 	}
7728 	return;
7729     }
7730 
7731     /*
7732      * Create a new tearoff.
7733      */
7734     if (*title == MNU_HIDDEN_CHAR)
7735 	title++;
7736 
7737     /* Allocate memory to store the dialog template.  It's made bigger when
7738      * needed. */
7739     template_len = DLG_ALLOC_SIZE;
7740     pdlgtemplate = p = (WORD *)LocalAlloc(LPTR, template_len);
7741     if (p == NULL)
7742 	return;
7743 
7744     hwnd = GetDesktopWindow();
7745     hdc = GetWindowDC(hwnd);
7746 #ifdef USE_SYSMENU_FONT
7747     if (gui_w32_get_menu_font(&lfSysmenu) == OK)
7748     {
7749 	font = CreateFontIndirect(&lfSysmenu);
7750 	use_lfSysmenu = TRUE;
7751     }
7752     else
7753 #endif
7754     font = CreateFont(-DLG_FONT_POINT_SIZE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
7755 		      VARIABLE_PITCH , DLG_FONT_NAME);
7756     if (s_usenewlook)
7757 	oldFont = SelectFont(hdc, font);
7758     else
7759 	oldFont = SelectFont(hdc, GetStockObject(SYSTEM_FONT));
7760 
7761     /* Calculate width of a single space.  Used for padding columns to the
7762      * right width. */
7763     spaceWidth = GetTextWidth(hdc, (char_u *)" ", 1);
7764 
7765     /* Figure out max width of the text column, the accelerator column and the
7766      * optional submenu column. */
7767     submenuWidth = 0;
7768     for (col = 0; col < 2; col++)
7769     {
7770 	columnWidths[col] = 0;
7771 	for (pmenu = menu->children; pmenu != NULL; pmenu = pmenu->next)
7772 	{
7773 	    /* Use "dname" here to compute the width of the visible text. */
7774 	    text = (col == 0) ? pmenu->dname : pmenu->actext;
7775 	    if (text != NULL && *text != NUL)
7776 	    {
7777 		textWidth = GetTextWidthEnc(hdc, text, (int)STRLEN(text));
7778 		if (textWidth > columnWidths[col])
7779 		    columnWidths[col] = textWidth;
7780 	    }
7781 	    if (pmenu->children != NULL)
7782 		submenuWidth = TEAROFF_COLUMN_PADDING * spaceWidth;
7783 	}
7784     }
7785     if (columnWidths[1] == 0)
7786     {
7787 	/* no accelerators */
7788 	if (submenuWidth != 0)
7789 	    columnWidths[0] += submenuWidth;
7790 	else
7791 	    columnWidths[0] += spaceWidth;
7792     }
7793     else
7794     {
7795 	/* there is an accelerator column */
7796 	columnWidths[0] += TEAROFF_COLUMN_PADDING * spaceWidth;
7797 	columnWidths[1] += submenuWidth;
7798     }
7799 
7800     /*
7801      * Now find the total width of our 'menu'.
7802      */
7803     textWidth = columnWidths[0] + columnWidths[1];
7804     if (submenuWidth != 0)
7805     {
7806 	submenuWidth = GetTextWidth(hdc, (char_u *)TEAROFF_SUBMENU_LABEL,
7807 					  (int)STRLEN(TEAROFF_SUBMENU_LABEL));
7808 	textWidth += submenuWidth;
7809     }
7810     dlgwidth = GetTextWidthEnc(hdc, title, (int)STRLEN(title));
7811     if (textWidth > dlgwidth)
7812 	dlgwidth = textWidth;
7813     dlgwidth += 2 * TEAROFF_PADDING_X + TEAROFF_BUTTON_PAD_X;
7814 
7815     /* start to fill in the dlgtemplate information.  addressing by WORDs */
7816     if (s_usenewlook)
7817 	lStyle = DS_MODALFRAME | WS_CAPTION| WS_SYSMENU |DS_SETFONT| WS_VISIBLE;
7818     else
7819 	lStyle = DS_MODALFRAME | WS_CAPTION| WS_SYSMENU | WS_VISIBLE;
7820 
7821     lExtendedStyle = WS_EX_TOOLWINDOW|WS_EX_STATICEDGE;
7822     *p++ = LOWORD(lStyle);
7823     *p++ = HIWORD(lStyle);
7824     *p++ = LOWORD(lExtendedStyle);
7825     *p++ = HIWORD(lExtendedStyle);
7826     pnumitems = p;	/* save where the number of items must be stored */
7827     *p++ = 0;		// NumberOfItems(will change later)
7828     gui_mch_getmouse(&x, &y);
7829     if (initX == 0xffffL)
7830 	*p++ = PixelToDialogX(x); // x
7831     else
7832 	*p++ = PixelToDialogX(initX); // x
7833     if (initY == 0xffffL)
7834 	*p++ = PixelToDialogY(y); // y
7835     else
7836 	*p++ = PixelToDialogY(initY); // y
7837     *p++ = PixelToDialogX(dlgwidth);    // cx
7838     ptrueheight = p;
7839     *p++ = 0;		// dialog height: changed later anyway
7840     *p++ = 0;		// Menu
7841     *p++ = 0;		// Class
7842 
7843     /* copy the title of the dialog */
7844     nchar = nCopyAnsiToWideChar(p, ((*title)
7845 			    ? (LPSTR)title
7846 			    : (LPSTR)("Vim "VIM_VERSION_MEDIUM)), TRUE);
7847     p += nchar;
7848 
7849     if (s_usenewlook)
7850     {
7851 	/* do the font, since DS_3DLOOK doesn't work properly */
7852 #ifdef USE_SYSMENU_FONT
7853 	if (use_lfSysmenu)
7854 	{
7855 	    /* point size */
7856 	    *p++ = -MulDiv(lfSysmenu.lfHeight, 72,
7857 		    GetDeviceCaps(hdc, LOGPIXELSY));
7858 	    nchar = nCopyAnsiToWideChar(p, lfSysmenu.lfFaceName, FALSE);
7859 	}
7860 	else
7861 #endif
7862 	{
7863 	    *p++ = DLG_FONT_POINT_SIZE;		// point size
7864 	    nchar = nCopyAnsiToWideChar(p, DLG_FONT_NAME, FALSE);
7865 	}
7866 	p += nchar;
7867     }
7868 
7869     /*
7870      * Loop over all the items in the menu.
7871      * But skip over the tearbar.
7872      */
7873     if (STRCMP(menu->children->name, TEAR_STRING) == 0)
7874 	menu = menu->children->next;
7875     else
7876 	menu = menu->children;
7877     top_menu = menu;
7878     for ( ; menu != NULL; menu = menu->next)
7879     {
7880 	if (menu->modes == 0)	/* this menu has just been deleted */
7881 	    continue;
7882 	if (menu_is_separator(menu->dname))
7883 	{
7884 	    sepPadding += 3;
7885 	    continue;
7886 	}
7887 
7888 	/* Check if there still is plenty of room in the template.  Make it
7889 	 * larger when needed. */
7890 	if (((char *)p - (char *)pdlgtemplate) + 1000 > template_len)
7891 	{
7892 	    WORD    *newp;
7893 
7894 	    newp = (WORD *)LocalAlloc(LPTR, template_len + 4096);
7895 	    if (newp != NULL)
7896 	    {
7897 		template_len += 4096;
7898 		mch_memmove(newp, pdlgtemplate,
7899 					    (char *)p - (char *)pdlgtemplate);
7900 		p = newp + (p - pdlgtemplate);
7901 		pnumitems = newp + (pnumitems - pdlgtemplate);
7902 		ptrueheight = newp + (ptrueheight - pdlgtemplate);
7903 		LocalFree(LocalHandle(pdlgtemplate));
7904 		pdlgtemplate = newp;
7905 	    }
7906 	}
7907 
7908 	/* Figure out minimal length of this menu label.  Use "name" for the
7909 	 * actual text, "dname" for estimating the displayed size.  "name"
7910 	 * has "&a" for mnemonic and includes the accelerator. */
7911 	len = nameLen = (int)STRLEN(menu->name);
7912 	padding0 = (columnWidths[0] - GetTextWidthEnc(hdc, menu->dname,
7913 				      (int)STRLEN(menu->dname))) / spaceWidth;
7914 	len += padding0;
7915 
7916 	if (menu->actext != NULL)
7917 	{
7918 	    acLen = (int)STRLEN(menu->actext);
7919 	    len += acLen;
7920 	    textWidth = GetTextWidthEnc(hdc, menu->actext, acLen);
7921 	}
7922 	else
7923 	    textWidth = 0;
7924 	padding1 = (columnWidths[1] - textWidth) / spaceWidth;
7925 	len += padding1;
7926 
7927 	if (menu->children == NULL)
7928 	{
7929 	    padding2 = submenuWidth / spaceWidth;
7930 	    len += padding2;
7931 	    menuID = (WORD)(menu->id);
7932 	}
7933 	else
7934 	{
7935 	    len += (int)STRLEN(TEAROFF_SUBMENU_LABEL);
7936 	    menuID = (WORD)((long_u)(menu->submenu_id) | (DWORD)0x8000);
7937 	}
7938 
7939 	/* Allocate menu label and fill it in */
7940 	text = label = alloc((unsigned)len + 1);
7941 	if (label == NULL)
7942 	    break;
7943 
7944 	vim_strncpy(text, menu->name, nameLen);
7945 	text = vim_strchr(text, TAB);	    /* stop at TAB before actext */
7946 	if (text == NULL)
7947 	    text = label + nameLen;	    /* no actext, use whole name */
7948 	while (padding0-- > 0)
7949 	    *text++ = ' ';
7950 	if (menu->actext != NULL)
7951 	{
7952 	    STRNCPY(text, menu->actext, acLen);
7953 	    text += acLen;
7954 	}
7955 	while (padding1-- > 0)
7956 	    *text++ = ' ';
7957 	if (menu->children != NULL)
7958 	{
7959 	    STRCPY(text, TEAROFF_SUBMENU_LABEL);
7960 	    text += STRLEN(TEAROFF_SUBMENU_LABEL);
7961 	}
7962 	else
7963 	{
7964 	    while (padding2-- > 0)
7965 		*text++ = ' ';
7966 	}
7967 	*text = NUL;
7968 
7969 	/*
7970 	 * BS_LEFT will just be ignored on Win32s/NT3.5x - on
7971 	 * W95/NT4 it makes the tear-off look more like a menu.
7972 	 */
7973 	p = add_dialog_element(p,
7974 		BS_PUSHBUTTON|BS_LEFT,
7975 		(WORD)PixelToDialogX(TEAROFF_PADDING_X),
7976 		(WORD)(sepPadding + 1 + 13 * (*pnumitems)),
7977 		(WORD)PixelToDialogX(dlgwidth - 2 * TEAROFF_PADDING_X),
7978 		(WORD)12,
7979 		menuID, (WORD)0x0080, (char *)label);
7980 	vim_free(label);
7981 	(*pnumitems)++;
7982     }
7983 
7984     *ptrueheight = (WORD)(sepPadding + 1 + 13 * (*pnumitems));
7985 
7986 
7987     /* show modelessly */
7988     the_menu->tearoff_handle = CreateDialogIndirectParam(
7989 	    s_hinst,
7990 	    (LPDLGTEMPLATE)pdlgtemplate,
7991 	    s_hwnd,
7992 	    (DLGPROC)tearoff_callback,
7993 	    (LPARAM)top_menu);
7994 
7995     LocalFree(LocalHandle(pdlgtemplate));
7996     SelectFont(hdc, oldFont);
7997     DeleteObject(font);
7998     ReleaseDC(hwnd, hdc);
7999 
8000     /*
8001      * Reassert ourselves as the active window.  This is so that after creating
8002      * a tearoff, the user doesn't have to click with the mouse just to start
8003      * typing again!
8004      */
8005     (void)SetActiveWindow(s_hwnd);
8006 
8007     /* make sure the right buttons are enabled */
8008     force_menu_update = TRUE;
8009 }
8010 #endif
8011 
8012 #if defined(FEAT_TOOLBAR) || defined(PROTO)
8013 #include "gui_w32_rc.h"
8014 
8015 /* This not defined in older SDKs */
8016 # ifndef TBSTYLE_FLAT
8017 #  define TBSTYLE_FLAT		0x0800
8018 # endif
8019 
8020 /*
8021  * Create the toolbar, initially unpopulated.
8022  *  (just like the menu, there are no defaults, it's all
8023  *  set up through menu.vim)
8024  */
8025     static void
8026 initialise_toolbar(void)
8027 {
8028     InitCommonControls();
8029     s_toolbarhwnd = CreateToolbarEx(
8030 		    s_hwnd,
8031 		    WS_CHILD | TBSTYLE_TOOLTIPS | TBSTYLE_FLAT,
8032 		    4000,		//any old big number
8033 		    31,			//number of images in initial bitmap
8034 		    s_hinst,
8035 		    IDR_TOOLBAR1,	// id of initial bitmap
8036 		    NULL,
8037 		    0,			// initial number of buttons
8038 		    TOOLBAR_BUTTON_WIDTH, //api guide is wrong!
8039 		    TOOLBAR_BUTTON_HEIGHT,
8040 		    TOOLBAR_BUTTON_WIDTH,
8041 		    TOOLBAR_BUTTON_HEIGHT,
8042 		    sizeof(TBBUTTON)
8043 		    );
8044     s_toolbar_wndproc = SubclassWindow(s_toolbarhwnd, toolbar_wndproc);
8045 
8046     gui_mch_show_toolbar(vim_strchr(p_go, GO_TOOLBAR) != NULL);
8047 }
8048 
8049     static LRESULT CALLBACK
8050 toolbar_wndproc(
8051     HWND hwnd,
8052     UINT uMsg,
8053     WPARAM wParam,
8054     LPARAM lParam)
8055 {
8056     HandleMouseHide(uMsg, lParam);
8057     return CallWindowProc(s_toolbar_wndproc, hwnd, uMsg, wParam, lParam);
8058 }
8059 
8060     static int
8061 get_toolbar_bitmap(vimmenu_T *menu)
8062 {
8063     int i = -1;
8064 
8065     /*
8066      * Check user bitmaps first, unless builtin is specified.
8067      */
8068     if (!menu->icon_builtin)
8069     {
8070 	char_u fname[MAXPATHL];
8071 	HANDLE hbitmap = NULL;
8072 
8073 	if (menu->iconfile != NULL)
8074 	{
8075 	    gui_find_iconfile(menu->iconfile, fname, "bmp");
8076 	    hbitmap = LoadImage(
8077 			NULL,
8078 			(LPCSTR)fname,
8079 			IMAGE_BITMAP,
8080 			TOOLBAR_BUTTON_WIDTH,
8081 			TOOLBAR_BUTTON_HEIGHT,
8082 			LR_LOADFROMFILE |
8083 			LR_LOADMAP3DCOLORS
8084 			);
8085 	}
8086 
8087 	/*
8088 	 * If the LoadImage call failed, or the "icon=" file
8089 	 * didn't exist or wasn't specified, try the menu name
8090 	 */
8091 	if (hbitmap == NULL
8092 		&& (gui_find_bitmap(
8093 #ifdef FEAT_MULTI_LANG
8094 			    menu->en_dname != NULL ? menu->en_dname :
8095 #endif
8096 					menu->dname, fname, "bmp") == OK))
8097 	    hbitmap = LoadImage(
8098 		    NULL,
8099 		    (LPCSTR)fname,
8100 		    IMAGE_BITMAP,
8101 		    TOOLBAR_BUTTON_WIDTH,
8102 		    TOOLBAR_BUTTON_HEIGHT,
8103 		    LR_LOADFROMFILE |
8104 		    LR_LOADMAP3DCOLORS
8105 		);
8106 
8107 	if (hbitmap != NULL)
8108 	{
8109 	    TBADDBITMAP tbAddBitmap;
8110 
8111 	    tbAddBitmap.hInst = NULL;
8112 	    tbAddBitmap.nID = (long_u)hbitmap;
8113 
8114 	    i = (int)SendMessage(s_toolbarhwnd, TB_ADDBITMAP,
8115 			    (WPARAM)1, (LPARAM)&tbAddBitmap);
8116 	    /* i will be set to -1 if it fails */
8117 	}
8118     }
8119     if (i == -1 && menu->iconidx >= 0 && menu->iconidx < TOOLBAR_BITMAP_COUNT)
8120 	i = menu->iconidx;
8121 
8122     return i;
8123 }
8124 #endif
8125 
8126 #if defined(FEAT_GUI_TABLINE) || defined(PROTO)
8127     static void
8128 initialise_tabline(void)
8129 {
8130     InitCommonControls();
8131 
8132     s_tabhwnd = CreateWindow(WC_TABCONTROL, "Vim tabline",
8133 	    WS_CHILD|TCS_FOCUSNEVER|TCS_TOOLTIPS,
8134 	    CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
8135 	    CW_USEDEFAULT, s_hwnd, NULL, s_hinst, NULL);
8136     s_tabline_wndproc = SubclassWindow(s_tabhwnd, tabline_wndproc);
8137 
8138     gui.tabline_height = TABLINE_HEIGHT;
8139 
8140 # ifdef USE_SYSMENU_FONT
8141     set_tabline_font();
8142 # endif
8143 }
8144 
8145 /*
8146  * Get tabpage_T from POINT.
8147  */
8148     static tabpage_T *
8149 GetTabFromPoint(
8150     HWND    hWnd,
8151     POINT   pt)
8152 {
8153     tabpage_T	*ptp = NULL;
8154 
8155     if (gui_mch_showing_tabline())
8156     {
8157 	TCHITTESTINFO htinfo;
8158 	htinfo.pt = pt;
8159 	/* ignore if a window under cusor is not tabcontrol. */
8160 	if (s_tabhwnd == hWnd)
8161 	{
8162 	    int idx = TabCtrl_HitTest(s_tabhwnd, &htinfo);
8163 	    if (idx != -1)
8164 		ptp = find_tabpage(idx + 1);
8165 	}
8166     }
8167     return ptp;
8168 }
8169 
8170 static POINT	    s_pt = {0, 0};
8171 static HCURSOR      s_hCursor = NULL;
8172 
8173     static LRESULT CALLBACK
8174 tabline_wndproc(
8175     HWND hwnd,
8176     UINT uMsg,
8177     WPARAM wParam,
8178     LPARAM lParam)
8179 {
8180     POINT	pt;
8181     tabpage_T	*tp;
8182     RECT	rect;
8183     int		nCenter;
8184     int		idx0;
8185     int		idx1;
8186 
8187     HandleMouseHide(uMsg, lParam);
8188 
8189     switch (uMsg)
8190     {
8191 	case WM_LBUTTONDOWN:
8192 	    {
8193 		s_pt.x = GET_X_LPARAM(lParam);
8194 		s_pt.y = GET_Y_LPARAM(lParam);
8195 		SetCapture(hwnd);
8196 		s_hCursor = GetCursor(); /* backup default cursor */
8197 		break;
8198 	    }
8199 	case WM_MOUSEMOVE:
8200 	    if (GetCapture() == hwnd
8201 		    && ((wParam & MK_LBUTTON)) != 0)
8202 	    {
8203 		pt.x = GET_X_LPARAM(lParam);
8204 		pt.y = s_pt.y;
8205 		if (abs(pt.x - s_pt.x) > GetSystemMetrics(SM_CXDRAG))
8206 		{
8207 		    SetCursor(LoadCursor(NULL, IDC_SIZEWE));
8208 
8209 		    tp = GetTabFromPoint(hwnd, pt);
8210 		    if (tp != NULL)
8211 		    {
8212 			idx0 = tabpage_index(curtab) - 1;
8213 			idx1 = tabpage_index(tp) - 1;
8214 
8215 			TabCtrl_GetItemRect(hwnd, idx1, &rect);
8216 			nCenter = rect.left + (rect.right - rect.left) / 2;
8217 
8218 			/* Check if the mouse cursor goes over the center of
8219 			 * the next tab to prevent "flickering". */
8220 			if ((idx0 < idx1) && (nCenter < pt.x))
8221 			{
8222 			    tabpage_move(idx1 + 1);
8223 			    update_screen(0);
8224 			}
8225 			else if ((idx1 < idx0) && (pt.x < nCenter))
8226 			{
8227 			    tabpage_move(idx1);
8228 			    update_screen(0);
8229 			}
8230 		    }
8231 		}
8232 	    }
8233 	    break;
8234 	case WM_LBUTTONUP:
8235 	    {
8236 		if (GetCapture() == hwnd)
8237 		{
8238 		    SetCursor(s_hCursor);
8239 		    ReleaseCapture();
8240 		}
8241 		break;
8242 	    }
8243 	default:
8244 	    break;
8245     }
8246 
8247     return CallWindowProc(s_tabline_wndproc, hwnd, uMsg, wParam, lParam);
8248 }
8249 #endif
8250 
8251 #if defined(FEAT_OLE) || defined(FEAT_EVAL) || defined(PROTO)
8252 /*
8253  * Make the GUI window come to the foreground.
8254  */
8255     void
8256 gui_mch_set_foreground(void)
8257 {
8258     if (IsIconic(s_hwnd))
8259 	 SendMessage(s_hwnd, WM_SYSCOMMAND, SC_RESTORE, 0);
8260     SetForegroundWindow(s_hwnd);
8261 }
8262 #endif
8263 
8264 #if defined(FEAT_MBYTE_IME) && defined(DYNAMIC_IME)
8265     static void
8266 dyn_imm_load(void)
8267 {
8268     hLibImm = vimLoadLib("imm32.dll");
8269     if (hLibImm == NULL)
8270 	return;
8271 
8272     pImmGetCompositionStringA
8273 	    = (void *)GetProcAddress(hLibImm, "ImmGetCompositionStringA");
8274     pImmGetCompositionStringW
8275 	    = (void *)GetProcAddress(hLibImm, "ImmGetCompositionStringW");
8276     pImmGetContext
8277 	    = (void *)GetProcAddress(hLibImm, "ImmGetContext");
8278     pImmAssociateContext
8279 	    = (void *)GetProcAddress(hLibImm, "ImmAssociateContext");
8280     pImmReleaseContext
8281 	    = (void *)GetProcAddress(hLibImm, "ImmReleaseContext");
8282     pImmGetOpenStatus
8283 	    = (void *)GetProcAddress(hLibImm, "ImmGetOpenStatus");
8284     pImmSetOpenStatus
8285 	    = (void *)GetProcAddress(hLibImm, "ImmSetOpenStatus");
8286     pImmGetCompositionFont
8287 	    = (void *)GetProcAddress(hLibImm, "ImmGetCompositionFontA");
8288     pImmSetCompositionFont
8289 	    = (void *)GetProcAddress(hLibImm, "ImmSetCompositionFontA");
8290     pImmSetCompositionWindow
8291 	    = (void *)GetProcAddress(hLibImm, "ImmSetCompositionWindow");
8292     pImmGetConversionStatus
8293 	    = (void *)GetProcAddress(hLibImm, "ImmGetConversionStatus");
8294     pImmSetConversionStatus
8295 	    = (void *)GetProcAddress(hLibImm, "ImmSetConversionStatus");
8296 
8297     if (       pImmGetCompositionStringA == NULL
8298 	    || pImmGetCompositionStringW == NULL
8299 	    || pImmGetContext == NULL
8300 	    || pImmAssociateContext == NULL
8301 	    || pImmReleaseContext == NULL
8302 	    || pImmGetOpenStatus == NULL
8303 	    || pImmSetOpenStatus == NULL
8304 	    || pImmGetCompositionFont == NULL
8305 	    || pImmSetCompositionFont == NULL
8306 	    || pImmSetCompositionWindow == NULL
8307 	    || pImmGetConversionStatus == NULL
8308 	    || pImmSetConversionStatus == NULL)
8309     {
8310 	FreeLibrary(hLibImm);
8311 	hLibImm = NULL;
8312 	pImmGetContext = NULL;
8313 	return;
8314     }
8315 
8316     return;
8317 }
8318 
8319 #endif
8320 
8321 #if defined(FEAT_SIGN_ICONS) || defined(PROTO)
8322 
8323 # ifdef FEAT_XPM_W32
8324 #  define IMAGE_XPM   100
8325 # endif
8326 
8327 typedef struct _signicon_t
8328 {
8329     HANDLE	hImage;
8330     UINT	uType;
8331 #ifdef FEAT_XPM_W32
8332     HANDLE	hShape;	/* Mask bitmap handle */
8333 #endif
8334 } signicon_t;
8335 
8336     void
8337 gui_mch_drawsign(int row, int col, int typenr)
8338 {
8339     signicon_t *sign;
8340     int x, y, w, h;
8341 
8342     if (!gui.in_use || (sign = (signicon_t *)sign_get_image(typenr)) == NULL)
8343 	return;
8344 
8345 #if defined(FEAT_DIRECTX)
8346     if (IS_ENABLE_DIRECTX())
8347 	DWriteContext_Flush(s_dwc);
8348 #endif
8349 
8350     x = TEXT_X(col);
8351     y = TEXT_Y(row);
8352     w = gui.char_width * 2;
8353     h = gui.char_height;
8354     switch (sign->uType)
8355     {
8356 	case IMAGE_BITMAP:
8357 	    {
8358 		HDC hdcMem;
8359 		HBITMAP hbmpOld;
8360 
8361 		hdcMem = CreateCompatibleDC(s_hdc);
8362 		hbmpOld = (HBITMAP)SelectObject(hdcMem, sign->hImage);
8363 		BitBlt(s_hdc, x, y, w, h, hdcMem, 0, 0, SRCCOPY);
8364 		SelectObject(hdcMem, hbmpOld);
8365 		DeleteDC(hdcMem);
8366 	    }
8367 	    break;
8368 	case IMAGE_ICON:
8369 	case IMAGE_CURSOR:
8370 	    DrawIconEx(s_hdc, x, y, (HICON)sign->hImage, w, h, 0, NULL, DI_NORMAL);
8371 	    break;
8372 #ifdef FEAT_XPM_W32
8373 	case IMAGE_XPM:
8374 	    {
8375 		HDC hdcMem;
8376 		HBITMAP hbmpOld;
8377 
8378 		hdcMem = CreateCompatibleDC(s_hdc);
8379 		hbmpOld = (HBITMAP)SelectObject(hdcMem, sign->hShape);
8380 		/* Make hole */
8381 		BitBlt(s_hdc, x, y, w, h, hdcMem, 0, 0, SRCAND);
8382 
8383 		SelectObject(hdcMem, sign->hImage);
8384 		/* Paint sign */
8385 		BitBlt(s_hdc, x, y, w, h, hdcMem, 0, 0, SRCPAINT);
8386 		SelectObject(hdcMem, hbmpOld);
8387 		DeleteDC(hdcMem);
8388 	    }
8389 	    break;
8390 #endif
8391     }
8392 }
8393 
8394     static void
8395 close_signicon_image(signicon_t *sign)
8396 {
8397     if (sign)
8398 	switch (sign->uType)
8399 	{
8400 	    case IMAGE_BITMAP:
8401 		DeleteObject((HGDIOBJ)sign->hImage);
8402 		break;
8403 	    case IMAGE_CURSOR:
8404 		DestroyCursor((HCURSOR)sign->hImage);
8405 		break;
8406 	    case IMAGE_ICON:
8407 		DestroyIcon((HICON)sign->hImage);
8408 		break;
8409 #ifdef FEAT_XPM_W32
8410 	    case IMAGE_XPM:
8411 		DeleteObject((HBITMAP)sign->hImage);
8412 		DeleteObject((HBITMAP)sign->hShape);
8413 		break;
8414 #endif
8415 	}
8416 }
8417 
8418     void *
8419 gui_mch_register_sign(char_u *signfile)
8420 {
8421     signicon_t	sign, *psign;
8422     char_u	*ext;
8423 
8424     sign.hImage = NULL;
8425     ext = signfile + STRLEN(signfile) - 4; /* get extension */
8426     if (ext > signfile)
8427     {
8428 	int do_load = 1;
8429 
8430 	if (!STRICMP(ext, ".bmp"))
8431 	    sign.uType =  IMAGE_BITMAP;
8432 	else if (!STRICMP(ext, ".ico"))
8433 	    sign.uType =  IMAGE_ICON;
8434 	else if (!STRICMP(ext, ".cur") || !STRICMP(ext, ".ani"))
8435 	    sign.uType =  IMAGE_CURSOR;
8436 	else
8437 	    do_load = 0;
8438 
8439 	if (do_load)
8440 	    sign.hImage = (HANDLE)LoadImage(NULL, (LPCSTR)signfile, sign.uType,
8441 		    gui.char_width * 2, gui.char_height,
8442 		    LR_LOADFROMFILE | LR_CREATEDIBSECTION);
8443 #ifdef FEAT_XPM_W32
8444 	if (!STRICMP(ext, ".xpm"))
8445 	{
8446 	    sign.uType = IMAGE_XPM;
8447 	    LoadXpmImage((char *)signfile, (HBITMAP *)&sign.hImage,
8448 		    (HBITMAP *)&sign.hShape);
8449 	}
8450 #endif
8451     }
8452 
8453     psign = NULL;
8454     if (sign.hImage && (psign = (signicon_t *)alloc(sizeof(signicon_t)))
8455 								      != NULL)
8456 	*psign = sign;
8457 
8458     if (!psign)
8459     {
8460 	if (sign.hImage)
8461 	    close_signicon_image(&sign);
8462 	emsg(_(e_signdata));
8463     }
8464     return (void *)psign;
8465 
8466 }
8467 
8468     void
8469 gui_mch_destroy_sign(void *sign)
8470 {
8471     if (sign)
8472     {
8473 	close_signicon_image((signicon_t *)sign);
8474 	vim_free(sign);
8475     }
8476 }
8477 #endif
8478 
8479 #if defined(FEAT_BEVAL_GUI) || defined(PROTO)
8480 
8481 /* BALLOON-EVAL IMPLEMENTATION FOR WINDOWS.
8482  *  Added by Sergey Khorev <[email protected]>
8483  *
8484  * The only reused thing is beval.h and get_beval_info()
8485  * from gui_beval.c (note it uses x and y of the BalloonEval struct
8486  * to get current mouse position).
8487  *
8488  * Trying to use as more Windows services as possible, and as less
8489  * IE version as possible :)).
8490  *
8491  * 1) Don't create ToolTip in gui_mch_create_beval_area, only initialize
8492  * BalloonEval struct.
8493  * 2) Enable/Disable simply create/kill BalloonEval Timer
8494  * 3) When there was enough inactivity, timer procedure posts
8495  * async request to debugger
8496  * 4) gui_mch_post_balloon (invoked from netbeans.c) creates tooltip control
8497  * and performs some actions to show it ASAP
8498  * 5) WM_NOTIFY:TTN_POP destroys created tooltip
8499  */
8500 
8501 /*
8502  * determine whether installed Common Controls support multiline tooltips
8503  * (i.e. their version is >= 4.70
8504  */
8505     int
8506 multiline_balloon_available(void)
8507 {
8508     HINSTANCE hDll;
8509     static char comctl_dll[] = "comctl32.dll";
8510     static int multiline_tip = MAYBE;
8511 
8512     if (multiline_tip != MAYBE)
8513 	return multiline_tip;
8514 
8515     hDll = GetModuleHandle(comctl_dll);
8516     if (hDll != NULL)
8517     {
8518 	DLLGETVERSIONPROC pGetVer;
8519 	pGetVer = (DLLGETVERSIONPROC)GetProcAddress(hDll, "DllGetVersion");
8520 
8521 	if (pGetVer != NULL)
8522 	{
8523 	    DLLVERSIONINFO dvi;
8524 	    HRESULT hr;
8525 
8526 	    ZeroMemory(&dvi, sizeof(dvi));
8527 	    dvi.cbSize = sizeof(dvi);
8528 
8529 	    hr = (*pGetVer)(&dvi);
8530 
8531 	    if (SUCCEEDED(hr)
8532 		    && (dvi.dwMajorVersion > 4
8533 			|| (dvi.dwMajorVersion == 4
8534 						&& dvi.dwMinorVersion >= 70)))
8535 	    {
8536 		multiline_tip = TRUE;
8537 		return multiline_tip;
8538 	    }
8539 	}
8540 	else
8541 	{
8542 	    /* there is chance we have ancient CommCtl 4.70
8543 	       which doesn't export DllGetVersion */
8544 	    DWORD dwHandle = 0;
8545 	    DWORD len = GetFileVersionInfoSize(comctl_dll, &dwHandle);
8546 	    if (len > 0)
8547 	    {
8548 		VS_FIXEDFILEINFO *ver;
8549 		UINT vlen = 0;
8550 		void *data = alloc(len);
8551 
8552 		if ((data != NULL
8553 			&& GetFileVersionInfo(comctl_dll, 0, len, data)
8554 			&& VerQueryValue(data, "\\", (void **)&ver, &vlen)
8555 			&& vlen
8556 			&& HIWORD(ver->dwFileVersionMS) > 4)
8557 			|| ((HIWORD(ver->dwFileVersionMS) == 4
8558 			    && LOWORD(ver->dwFileVersionMS) >= 70)))
8559 		{
8560 		    vim_free(data);
8561 		    multiline_tip = TRUE;
8562 		    return multiline_tip;
8563 		}
8564 		vim_free(data);
8565 	    }
8566 	}
8567     }
8568     multiline_tip = FALSE;
8569     return multiline_tip;
8570 }
8571 
8572     static void
8573 make_tooltipw(BalloonEval *beval, char *text, POINT pt)
8574 {
8575     TOOLINFOW	*pti;
8576     int		ToolInfoSize;
8577 
8578     if (multiline_balloon_available() == TRUE)
8579 	ToolInfoSize = sizeof(TOOLINFOW_NEW);
8580     else
8581 	ToolInfoSize = sizeof(TOOLINFOW);
8582 
8583     pti = (TOOLINFOW *)alloc(ToolInfoSize);
8584     if (pti == NULL)
8585 	return;
8586 
8587     beval->balloon = CreateWindowExW(WS_EX_TOPMOST, TOOLTIPS_CLASSW,
8588 	    NULL, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,
8589 	    CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
8590 	    beval->target, NULL, s_hinst, NULL);
8591 
8592     SetWindowPos(beval->balloon, HWND_TOPMOST, 0, 0, 0, 0,
8593 	    SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
8594 
8595     pti->cbSize = ToolInfoSize;
8596     pti->uFlags = TTF_SUBCLASS;
8597     pti->hwnd = beval->target;
8598     pti->hinst = 0; // Don't use string resources
8599     pti->uId = ID_BEVAL_TOOLTIP;
8600 
8601     if (multiline_balloon_available() == TRUE)
8602     {
8603 	RECT rect;
8604 	TOOLINFOW_NEW *ptin = (TOOLINFOW_NEW *)pti;
8605 	pti->lpszText = LPSTR_TEXTCALLBACKW;
8606 	beval->tofree = enc_to_utf16((char_u*)text, NULL);
8607 	ptin->lParam = (LPARAM)beval->tofree;
8608 	// switch multiline tooltips on
8609 	if (GetClientRect(s_textArea, &rect))
8610 	    SendMessageW(beval->balloon, TTM_SETMAXTIPWIDTH, 0,
8611 		    (LPARAM)rect.right);
8612     }
8613     else
8614     {
8615 	// do this old way
8616 	beval->tofree = enc_to_utf16((char_u*)text, NULL);
8617 	pti->lpszText = (LPWSTR)beval->tofree;
8618     }
8619 
8620     // Limit ballooneval bounding rect to CursorPos neighbourhood.
8621     pti->rect.left = pt.x - 3;
8622     pti->rect.top = pt.y - 3;
8623     pti->rect.right = pt.x + 3;
8624     pti->rect.bottom = pt.y + 3;
8625 
8626     SendMessageW(beval->balloon, TTM_ADDTOOLW, 0, (LPARAM)pti);
8627     // Make tooltip appear sooner.
8628     SendMessageW(beval->balloon, TTM_SETDELAYTIME, TTDT_INITIAL, 10);
8629     // I've performed some tests and it seems the longest possible life time
8630     // of tooltip is 30 seconds.
8631     SendMessageW(beval->balloon, TTM_SETDELAYTIME, TTDT_AUTOPOP, 30000);
8632     /*
8633      * HACK: force tooltip to appear, because it'll not appear until
8634      * first mouse move. D*mn M$
8635      * Amazingly moving (2, 2) and then (-1, -1) the mouse doesn't move.
8636      */
8637     mouse_event(MOUSEEVENTF_MOVE, 2, 2, 0, 0);
8638     mouse_event(MOUSEEVENTF_MOVE, (DWORD)-1, (DWORD)-1, 0, 0);
8639     vim_free(pti);
8640 }
8641 
8642     static void
8643 make_tooltip(BalloonEval *beval, char *text, POINT pt)
8644 {
8645     TOOLINFO	*pti;
8646     int		ToolInfoSize;
8647 
8648     if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
8649     {
8650 	make_tooltipw(beval, text, pt);
8651 	return;
8652     }
8653 
8654     if (multiline_balloon_available() == TRUE)
8655 	ToolInfoSize = sizeof(TOOLINFO_NEW);
8656     else
8657 	ToolInfoSize = sizeof(TOOLINFO);
8658 
8659     pti = (TOOLINFO *)alloc(ToolInfoSize);
8660     if (pti == NULL)
8661 	return;
8662 
8663     beval->balloon = CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS,
8664 	    NULL, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,
8665 	    CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
8666 	    beval->target, NULL, s_hinst, NULL);
8667 
8668     SetWindowPos(beval->balloon, HWND_TOPMOST, 0, 0, 0, 0,
8669 	    SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
8670 
8671     pti->cbSize = ToolInfoSize;
8672     pti->uFlags = TTF_SUBCLASS;
8673     pti->hwnd = beval->target;
8674     pti->hinst = 0; /* Don't use string resources */
8675     pti->uId = ID_BEVAL_TOOLTIP;
8676 
8677     if (multiline_balloon_available() == TRUE)
8678     {
8679 	RECT rect;
8680 	TOOLINFO_NEW *ptin = (TOOLINFO_NEW *)pti;
8681 	pti->lpszText = LPSTR_TEXTCALLBACK;
8682 	beval->tofree = vim_strsave((char_u*)text);
8683 	ptin->lParam = (LPARAM)beval->tofree;
8684 	if (GetClientRect(s_textArea, &rect)) /* switch multiline tooltips on */
8685 	    SendMessage(beval->balloon, TTM_SETMAXTIPWIDTH, 0,
8686 		    (LPARAM)rect.right);
8687     }
8688     else
8689 	pti->lpszText = text; /* do this old way */
8690 
8691     /* Limit ballooneval bounding rect to CursorPos neighbourhood */
8692     pti->rect.left = pt.x - 3;
8693     pti->rect.top = pt.y - 3;
8694     pti->rect.right = pt.x + 3;
8695     pti->rect.bottom = pt.y + 3;
8696 
8697     SendMessage(beval->balloon, TTM_ADDTOOL, 0, (LPARAM)pti);
8698     /* Make tooltip appear sooner */
8699     SendMessage(beval->balloon, TTM_SETDELAYTIME, TTDT_INITIAL, 10);
8700     /* I've performed some tests and it seems the longest possible life time
8701      * of tooltip is 30 seconds */
8702     SendMessage(beval->balloon, TTM_SETDELAYTIME, TTDT_AUTOPOP, 30000);
8703     /*
8704      * HACK: force tooltip to appear, because it'll not appear until
8705      * first mouse move. D*mn M$
8706      * Amazingly moving (2, 2) and then (-1, -1) the mouse doesn't move.
8707      */
8708     mouse_event(MOUSEEVENTF_MOVE, 2, 2, 0, 0);
8709     mouse_event(MOUSEEVENTF_MOVE, (DWORD)-1, (DWORD)-1, 0, 0);
8710     vim_free(pti);
8711 }
8712 
8713     static void
8714 delete_tooltip(BalloonEval *beval)
8715 {
8716     PostMessage(beval->balloon, WM_CLOSE, 0, 0);
8717 }
8718 
8719     static VOID CALLBACK
8720 BevalTimerProc(
8721     HWND	hwnd UNUSED,
8722     UINT	uMsg UNUSED,
8723     UINT_PTR    idEvent UNUSED,
8724     DWORD	dwTime)
8725 {
8726     POINT	pt;
8727     RECT	rect;
8728 
8729     if (cur_beval == NULL || cur_beval->showState == ShS_SHOWING || !p_beval)
8730 	return;
8731 
8732     GetCursorPos(&pt);
8733     if (WindowFromPoint(pt) != s_textArea)
8734 	return;
8735 
8736     ScreenToClient(s_textArea, &pt);
8737     GetClientRect(s_textArea, &rect);
8738     if (!PtInRect(&rect, pt))
8739 	return;
8740 
8741     if (LastActivity > 0
8742 	    && (dwTime - LastActivity) >= (DWORD)p_bdlay
8743 	    && (cur_beval->showState != ShS_PENDING
8744 		|| abs(cur_beval->x - pt.x) > 3
8745 		|| abs(cur_beval->y - pt.y) > 3))
8746     {
8747 	/* Pointer resting in one place long enough, it's time to show
8748 	 * the tooltip. */
8749 	cur_beval->showState = ShS_PENDING;
8750 	cur_beval->x = pt.x;
8751 	cur_beval->y = pt.y;
8752 
8753 	// TRACE0("BevalTimerProc: sending request");
8754 
8755 	if (cur_beval->msgCB != NULL)
8756 	    (*cur_beval->msgCB)(cur_beval, 0);
8757     }
8758 }
8759 
8760     void
8761 gui_mch_disable_beval_area(BalloonEval *beval UNUSED)
8762 {
8763     // TRACE0("gui_mch_disable_beval_area {{{");
8764     KillTimer(s_textArea, BevalTimerId);
8765     // TRACE0("gui_mch_disable_beval_area }}}");
8766 }
8767 
8768     void
8769 gui_mch_enable_beval_area(BalloonEval *beval)
8770 {
8771     // TRACE0("gui_mch_enable_beval_area |||");
8772     if (beval == NULL)
8773 	return;
8774     // TRACE0("gui_mch_enable_beval_area {{{");
8775     BevalTimerId = SetTimer(s_textArea, 0, (UINT)(p_bdlay / 2), BevalTimerProc);
8776     // TRACE0("gui_mch_enable_beval_area }}}");
8777 }
8778 
8779     void
8780 gui_mch_post_balloon(BalloonEval *beval, char_u *mesg)
8781 {
8782     POINT   pt;
8783 
8784     // TRACE0("gui_mch_post_balloon {{{");
8785     if (beval->showState == ShS_SHOWING)
8786 	return;
8787     GetCursorPos(&pt);
8788     ScreenToClient(s_textArea, &pt);
8789 
8790     if (abs(beval->x - pt.x) < 3 && abs(beval->y - pt.y) < 3)
8791     {
8792 	/* cursor is still here */
8793 	gui_mch_disable_beval_area(cur_beval);
8794 	beval->showState = ShS_SHOWING;
8795 	make_tooltip(beval, (char *)mesg, pt);
8796     }
8797     // TRACE0("gui_mch_post_balloon }}}");
8798 }
8799 
8800     BalloonEval *
8801 gui_mch_create_beval_area(
8802     void	*target,	/* ignored, always use s_textArea */
8803     char_u	*mesg,
8804     void	(*mesgCB)(BalloonEval *, int),
8805     void	*clientData)
8806 {
8807     /* partially stolen from gui_beval.c */
8808     BalloonEval	*beval;
8809 
8810     if (mesg != NULL && mesgCB != NULL)
8811     {
8812 	iemsg(_("E232: Cannot create BalloonEval with both message and callback"));
8813 	return NULL;
8814     }
8815 
8816     beval = (BalloonEval *)alloc_clear(sizeof(BalloonEval));
8817     if (beval != NULL)
8818     {
8819 	beval->target = s_textArea;
8820 
8821 	beval->showState = ShS_NEUTRAL;
8822 	beval->msg = mesg;
8823 	beval->msgCB = mesgCB;
8824 	beval->clientData = clientData;
8825 
8826 	InitCommonControls();
8827 	cur_beval = beval;
8828 
8829 	if (p_beval)
8830 	    gui_mch_enable_beval_area(beval);
8831     }
8832     return beval;
8833 }
8834 
8835     static void
8836 Handle_WM_Notify(HWND hwnd UNUSED, LPNMHDR pnmh)
8837 {
8838     if (pnmh->idFrom != ID_BEVAL_TOOLTIP) /* it is not our tooltip */
8839 	return;
8840 
8841     if (cur_beval != NULL)
8842     {
8843 	switch (pnmh->code)
8844 	{
8845 	case TTN_SHOW:
8846 	    // TRACE0("TTN_SHOW {{{");
8847 	    // TRACE0("TTN_SHOW }}}");
8848 	    break;
8849 	case TTN_POP: /* Before tooltip disappear */
8850 	    // TRACE0("TTN_POP {{{");
8851 	    delete_tooltip(cur_beval);
8852 	    gui_mch_enable_beval_area(cur_beval);
8853 	    // TRACE0("TTN_POP }}}");
8854 
8855 	    cur_beval->showState = ShS_NEUTRAL;
8856 	    break;
8857 	case TTN_GETDISPINFO:
8858 	    {
8859 		/* if you get there then we have new common controls */
8860 		NMTTDISPINFO_NEW *info = (NMTTDISPINFO_NEW *)pnmh;
8861 		info->lpszText = (LPSTR)info->lParam;
8862 		info->uFlags |= TTF_DI_SETITEM;
8863 	    }
8864 	    break;
8865 	case TTN_GETDISPINFOW:
8866 	    {
8867 		// if we get here then we have new common controls
8868 		NMTTDISPINFOW_NEW *info = (NMTTDISPINFOW_NEW *)pnmh;
8869 		info->lpszText = (LPWSTR)info->lParam;
8870 		info->uFlags |= TTF_DI_SETITEM;
8871 	    }
8872 	    break;
8873 	}
8874     }
8875 }
8876 
8877     static void
8878 TrackUserActivity(UINT uMsg)
8879 {
8880     if ((uMsg >= WM_MOUSEFIRST && uMsg <= WM_MOUSELAST)
8881 	    || (uMsg >= WM_KEYFIRST && uMsg <= WM_KEYLAST))
8882 	LastActivity = GetTickCount();
8883 }
8884 
8885     void
8886 gui_mch_destroy_beval_area(BalloonEval *beval)
8887 {
8888 #ifdef FEAT_VARTABS
8889     vim_free(beval->vts);
8890 #endif
8891     vim_free(beval->tofree);
8892     vim_free(beval);
8893 }
8894 #endif /* FEAT_BEVAL_GUI */
8895 
8896 #if defined(FEAT_NETBEANS_INTG) || defined(PROTO)
8897 /*
8898  * We have multiple signs to draw at the same location. Draw the
8899  * multi-sign indicator (down-arrow) instead. This is the Win32 version.
8900  */
8901     void
8902 netbeans_draw_multisign_indicator(int row)
8903 {
8904     int i;
8905     int y;
8906     int x;
8907 
8908     if (!netbeans_active())
8909 	return;
8910 
8911     x = 0;
8912     y = TEXT_Y(row);
8913 
8914 #if defined(FEAT_DIRECTX)
8915     if (IS_ENABLE_DIRECTX())
8916 	DWriteContext_Flush(s_dwc);
8917 #endif
8918 
8919     for (i = 0; i < gui.char_height - 3; i++)
8920 	SetPixel(s_hdc, x+2, y++, gui.currFgColor);
8921 
8922     SetPixel(s_hdc, x+0, y, gui.currFgColor);
8923     SetPixel(s_hdc, x+2, y, gui.currFgColor);
8924     SetPixel(s_hdc, x+4, y++, gui.currFgColor);
8925     SetPixel(s_hdc, x+1, y, gui.currFgColor);
8926     SetPixel(s_hdc, x+2, y, gui.currFgColor);
8927     SetPixel(s_hdc, x+3, y++, gui.currFgColor);
8928     SetPixel(s_hdc, x+2, y, gui.currFgColor);
8929 }
8930 #endif
8931