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