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