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