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 * gui_w48.c:  This file is included in gui_w16.c and gui_w32.c.
12 *
13 * GUI support for Microsoft Windows (Win16 + Win32 = Win48 :-)
14 *
15 * The combined efforts of:
16 * George V. Reilly <george@reilly.org>
17 * Robert Webb
18 * Vince Negri
19 * ...and contributions from many others
20 *
21 */
22
23#include "vim.h"
24#include "version.h"	/* used by dialog box routine for default title */
25#ifdef DEBUG
26# include <tchar.h>
27#endif
28#ifndef __MINGW32__
29# include <shellapi.h>
30#endif
31#if defined(FEAT_TOOLBAR) || defined(FEAT_BEVAL) || defined(FEAT_GUI_TABLINE)
32# include <commctrl.h>
33#endif
34#ifdef WIN16
35# include <commdlg.h>
36# include <shellapi.h>
37# ifdef WIN16_3DLOOK
38#  include <ctl3d.h>
39# endif
40#endif
41#include <windowsx.h>
42
43#ifdef GLOBAL_IME
44# include "glbl_ime.h"
45#endif
46
47#ifdef FEAT_MENU
48# define MENUHINTS		/* show menu hints in command line */
49#endif
50
51/* Some parameters for dialog boxes.  All in pixels. */
52#define DLG_PADDING_X		10
53#define DLG_PADDING_Y		10
54#define DLG_OLD_STYLE_PADDING_X	5
55#define DLG_OLD_STYLE_PADDING_Y	5
56#define DLG_VERT_PADDING_X	4	/* For vertical buttons */
57#define DLG_VERT_PADDING_Y	4
58#define DLG_ICON_WIDTH		34
59#define DLG_ICON_HEIGHT		34
60#define DLG_MIN_WIDTH		150
61#define DLG_FONT_NAME		"MS Sans Serif"
62#define DLG_FONT_POINT_SIZE	8
63#define DLG_MIN_MAX_WIDTH	400
64#define DLG_MIN_MAX_HEIGHT	400
65
66#define DLG_NONBUTTON_CONTROL	5000	/* First ID of non-button controls */
67
68#ifndef WM_XBUTTONDOWN /* For Win2K / winME ONLY */
69# define WM_XBUTTONDOWN		0x020B
70# define WM_XBUTTONUP		0x020C
71# define WM_XBUTTONDBLCLK	0x020D
72# define MK_XBUTTON1		0x0020
73# define MK_XBUTTON2		0x0040
74#endif
75
76#ifdef PROTO
77/*
78 * Define a few things for generating prototypes.  This is just to avoid
79 * syntax errors, the defines do not need to be correct.
80 */
81# define APIENTRY
82# define CALLBACK
83# define CONST
84# define FAR
85# define NEAR
86# define _cdecl
87typedef int BOOL;
88typedef int BYTE;
89typedef int DWORD;
90typedef int WCHAR;
91typedef int ENUMLOGFONT;
92typedef int FINDREPLACE;
93typedef int HANDLE;
94typedef int HBITMAP;
95typedef int HBRUSH;
96typedef int HDROP;
97typedef int INT;
98typedef int LOGFONT[];
99typedef int LPARAM;
100typedef int LPCREATESTRUCT;
101typedef int LPCSTR;
102typedef int LPCTSTR;
103typedef int LPRECT;
104typedef int LPSTR;
105typedef int LPWINDOWPOS;
106typedef int LPWORD;
107typedef int LRESULT;
108typedef int HRESULT;
109# undef MSG
110typedef int MSG;
111typedef int NEWTEXTMETRIC;
112typedef int OSVERSIONINFO;
113typedef int PWORD;
114typedef int RECT;
115typedef int UINT;
116typedef int WORD;
117typedef int WPARAM;
118typedef int POINT;
119typedef void *HINSTANCE;
120typedef void *HMENU;
121typedef void *HWND;
122typedef void *HDC;
123typedef void VOID;
124typedef int LPNMHDR;
125typedef int LONG;
126#endif
127
128#ifndef GET_X_LPARAM
129# define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp))
130#endif
131
132static void _OnPaint( HWND hwnd);
133static void clear_rect(RECT *rcp);
134static int gui_mswin_get_menu_height(int fix_window);
135
136static WORD		s_dlgfntheight;		/* height of the dialog font */
137static WORD		s_dlgfntwidth;		/* width of the dialog font */
138
139#ifdef FEAT_MENU
140static HMENU		s_menuBar = NULL;
141#endif
142#ifdef FEAT_TEAROFF
143static void rebuild_tearoff(vimmenu_T *menu);
144static HBITMAP	s_htearbitmap;	    /* bitmap used to indicate tearoff */
145#endif
146
147/* Flag that is set while processing a message that must not be interrupted by
148 * processing another message. */
149static int		s_busy_processing = FALSE;
150
151static int		destroying = FALSE;	/* call DestroyWindow() ourselves */
152
153#ifdef MSWIN_FIND_REPLACE
154static UINT		s_findrep_msg = 0;	/* set in gui_w[16/32].c */
155static FINDREPLACE	s_findrep_struct;
156# if defined(FEAT_MBYTE) && defined(WIN3264)
157static FINDREPLACEW	s_findrep_struct_w;
158# endif
159static HWND		s_findrep_hwnd = NULL;
160static int		s_findrep_is_find;	/* TRUE for find dialog, FALSE
161						   for find/replace dialog */
162#endif
163
164static HINSTANCE	s_hinst = NULL;
165#if !defined(FEAT_SNIFF) && !defined(FEAT_GUI)
166static
167#endif
168HWND			s_hwnd = NULL;
169static HDC		s_hdc = NULL;
170static HBRUSH	s_brush = NULL;
171
172#ifdef FEAT_TOOLBAR
173static HWND		s_toolbarhwnd = NULL;
174#endif
175
176#ifdef FEAT_GUI_TABLINE
177static HWND		s_tabhwnd = NULL;
178static int		showing_tabline = 0;
179#endif
180
181static WPARAM		s_wParam = 0;
182static LPARAM		s_lParam = 0;
183
184static HWND		s_textArea = NULL;
185static UINT		s_uMsg = 0;
186
187static char_u		*s_textfield; /* Used by dialogs to pass back strings */
188
189static int		s_need_activate = FALSE;
190
191/* This variable is set when waiting for an event, which is the only moment
192 * scrollbar dragging can be done directly.  It's not allowed while commands
193 * are executed, because it may move the cursor and that may cause unexpected
194 * problems (e.g., while ":s" is working).
195 */
196static int allow_scrollbar = FALSE;
197
198#ifdef GLOBAL_IME
199# define MyTranslateMessage(x) global_ime_TranslateMessage(x)
200#else
201# define MyTranslateMessage(x) TranslateMessage(x)
202#endif
203
204#if (defined(WIN3264) && defined(FEAT_MBYTE)) || defined(GLOBAL_IME)
205  /* use of WindowProc depends on wide_WindowProc */
206# define MyWindowProc vim_WindowProc
207#else
208  /* use ordinary WindowProc */
209# define MyWindowProc DefWindowProc
210#endif
211
212extern int current_font_height;	    /* this is in os_mswin.c */
213
214static struct
215{
216    UINT    key_sym;
217    char_u  vim_code0;
218    char_u  vim_code1;
219} special_keys[] =
220{
221    {VK_UP,		'k', 'u'},
222    {VK_DOWN,		'k', 'd'},
223    {VK_LEFT,		'k', 'l'},
224    {VK_RIGHT,		'k', 'r'},
225
226    {VK_F1,		'k', '1'},
227    {VK_F2,		'k', '2'},
228    {VK_F3,		'k', '3'},
229    {VK_F4,		'k', '4'},
230    {VK_F5,		'k', '5'},
231    {VK_F6,		'k', '6'},
232    {VK_F7,		'k', '7'},
233    {VK_F8,		'k', '8'},
234    {VK_F9,		'k', '9'},
235    {VK_F10,		'k', ';'},
236
237    {VK_F11,		'F', '1'},
238    {VK_F12,		'F', '2'},
239    {VK_F13,		'F', '3'},
240    {VK_F14,		'F', '4'},
241    {VK_F15,		'F', '5'},
242    {VK_F16,		'F', '6'},
243    {VK_F17,		'F', '7'},
244    {VK_F18,		'F', '8'},
245    {VK_F19,		'F', '9'},
246    {VK_F20,		'F', 'A'},
247
248    {VK_F21,		'F', 'B'},
249#ifdef FEAT_NETBEANS_INTG
250    {VK_PAUSE,		'F', 'B'},	/* Pause == F21 (see gui_gtk_x11.c) */
251#endif
252    {VK_F22,		'F', 'C'},
253    {VK_F23,		'F', 'D'},
254    {VK_F24,		'F', 'E'},	/* winuser.h defines up to F24 */
255
256    {VK_HELP,		'%', '1'},
257    {VK_BACK,		'k', 'b'},
258    {VK_INSERT,		'k', 'I'},
259    {VK_DELETE,		'k', 'D'},
260    {VK_HOME,		'k', 'h'},
261    {VK_END,		'@', '7'},
262    {VK_PRIOR,		'k', 'P'},
263    {VK_NEXT,		'k', 'N'},
264    {VK_PRINT,		'%', '9'},
265    {VK_ADD,		'K', '6'},
266    {VK_SUBTRACT,	'K', '7'},
267    {VK_DIVIDE,		'K', '8'},
268    {VK_MULTIPLY,	'K', '9'},
269    {VK_SEPARATOR,	'K', 'A'},	/* Keypad Enter */
270    {VK_DECIMAL,	'K', 'B'},
271
272    {VK_NUMPAD0,	'K', 'C'},
273    {VK_NUMPAD1,	'K', 'D'},
274    {VK_NUMPAD2,	'K', 'E'},
275    {VK_NUMPAD3,	'K', 'F'},
276    {VK_NUMPAD4,	'K', 'G'},
277    {VK_NUMPAD5,	'K', 'H'},
278    {VK_NUMPAD6,	'K', 'I'},
279    {VK_NUMPAD7,	'K', 'J'},
280    {VK_NUMPAD8,	'K', 'K'},
281    {VK_NUMPAD9,	'K', 'L'},
282
283    /* Keys that we want to be able to use any modifier with: */
284    {VK_SPACE,		' ', NUL},
285    {VK_TAB,		TAB, NUL},
286    {VK_ESCAPE,		ESC, NUL},
287    {NL,		NL, NUL},
288    {CAR,		CAR, NUL},
289
290    /* End of list marker: */
291    {0,			0, 0}
292};
293
294/* Local variables */
295static int		s_button_pending = -1;
296
297/* s_getting_focus is set when we got focus but didn't see mouse-up event yet,
298 * so don't reset s_button_pending. */
299static int		s_getting_focus = FALSE;
300
301static int		s_x_pending;
302static int		s_y_pending;
303static UINT		s_kFlags_pending;
304static UINT		s_wait_timer = 0;   /* Timer for get char from user */
305static int		s_timed_out = FALSE;
306static int		dead_key = 0;	/* 0 - no dead key, 1 - dead key pressed */
307
308#ifdef WIN3264
309static OSVERSIONINFO os_version;    /* like it says.  Init in gui_mch_init() */
310#endif
311
312#ifdef FEAT_BEVAL
313/* balloon-eval WM_NOTIFY_HANDLER */
314static void Handle_WM_Notify __ARGS((HWND hwnd, LPNMHDR pnmh));
315static void TrackUserActivity __ARGS((UINT uMsg));
316#endif
317
318/*
319 * For control IME.
320 */
321#ifdef FEAT_MBYTE
322# ifdef USE_IM_CONTROL
323static LOGFONT norm_logfont;
324# endif
325#endif
326
327#ifdef FEAT_MBYTE_IME
328static LRESULT _OnImeNotify(HWND hWnd, DWORD dwCommand, DWORD dwData);
329#endif
330
331#ifdef DEBUG_PRINT_ERROR
332/*
333 * Print out the last Windows error message
334 */
335    static void
336print_windows_error(void)
337{
338    LPVOID  lpMsgBuf;
339
340    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
341		  NULL, GetLastError(),
342		  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
343		  (LPTSTR) &lpMsgBuf, 0, NULL);
344    TRACE1("Error: %s\n", lpMsgBuf);
345    LocalFree(lpMsgBuf);
346}
347#endif
348
349/*
350 * Cursor blink functions.
351 *
352 * This is a simple state machine:
353 * BLINK_NONE	not blinking at all
354 * BLINK_OFF	blinking, cursor is not shown
355 * BLINK_ON	blinking, cursor is shown
356 */
357
358#define BLINK_NONE  0
359#define BLINK_OFF   1
360#define BLINK_ON    2
361
362static int		blink_state = BLINK_NONE;
363static long_u		blink_waittime = 700;
364static long_u		blink_ontime = 400;
365static long_u		blink_offtime = 250;
366static UINT		blink_timer = 0;
367
368    void
369gui_mch_set_blinking(long wait, long on, long off)
370{
371    blink_waittime = wait;
372    blink_ontime = on;
373    blink_offtime = off;
374}
375
376/* ARGSUSED */
377    static VOID CALLBACK
378_OnBlinkTimer(
379    HWND hwnd,
380    UINT uMsg,
381    UINT idEvent,
382    DWORD dwTime)
383{
384    MSG msg;
385
386    /*
387    TRACE2("Got timer event, id %d, blink_timer %d\n", idEvent, blink_timer);
388    */
389
390    KillTimer(NULL, idEvent);
391
392    /* Eat spurious WM_TIMER messages */
393    while (PeekMessage(&msg, hwnd, WM_TIMER, WM_TIMER, PM_REMOVE))
394	;
395
396    if (blink_state == BLINK_ON)
397    {
398	gui_undraw_cursor();
399	blink_state = BLINK_OFF;
400	blink_timer = (UINT) SetTimer(NULL, 0, (UINT)blink_offtime,
401						    (TIMERPROC)_OnBlinkTimer);
402    }
403    else
404    {
405	gui_update_cursor(TRUE, FALSE);
406	blink_state = BLINK_ON;
407	blink_timer = (UINT) SetTimer(NULL, 0, (UINT)blink_ontime,
408							 (TIMERPROC)_OnBlinkTimer);
409    }
410}
411
412    static void
413gui_mswin_rm_blink_timer(void)
414{
415    MSG msg;
416
417    if (blink_timer != 0)
418    {
419	KillTimer(NULL, blink_timer);
420	/* Eat spurious WM_TIMER messages */
421	while (PeekMessage(&msg, s_hwnd, WM_TIMER, WM_TIMER, PM_REMOVE))
422	    ;
423	blink_timer = 0;
424    }
425}
426
427/*
428 * Stop the cursor blinking.  Show the cursor if it wasn't shown.
429 */
430    void
431gui_mch_stop_blink(void)
432{
433    gui_mswin_rm_blink_timer();
434    if (blink_state == BLINK_OFF)
435	gui_update_cursor(TRUE, FALSE);
436    blink_state = BLINK_NONE;
437}
438
439/*
440 * Start the cursor blinking.  If it was already blinking, this restarts the
441 * waiting time and shows the cursor.
442 */
443    void
444gui_mch_start_blink(void)
445{
446    gui_mswin_rm_blink_timer();
447
448    /* Only switch blinking on if none of the times is zero */
449    if (blink_waittime && blink_ontime && blink_offtime && gui.in_focus)
450    {
451	blink_timer = (UINT)SetTimer(NULL, 0, (UINT)blink_waittime,
452						    (TIMERPROC)_OnBlinkTimer);
453	blink_state = BLINK_ON;
454	gui_update_cursor(TRUE, FALSE);
455    }
456}
457
458/*
459 * Call-back routines.
460 */
461
462/*ARGSUSED*/
463    static VOID CALLBACK
464_OnTimer(
465    HWND hwnd,
466    UINT uMsg,
467    UINT idEvent,
468    DWORD dwTime)
469{
470    MSG msg;
471
472    /*
473    TRACE2("Got timer event, id %d, s_wait_timer %d\n", idEvent, s_wait_timer);
474    */
475    KillTimer(NULL, idEvent);
476    s_timed_out = TRUE;
477
478    /* Eat spurious WM_TIMER messages */
479    while (PeekMessage(&msg, hwnd, WM_TIMER, WM_TIMER, PM_REMOVE))
480	;
481    if (idEvent == s_wait_timer)
482	s_wait_timer = 0;
483}
484
485/*ARGSUSED*/
486    static void
487_OnDeadChar(
488    HWND hwnd,
489    UINT ch,
490    int cRepeat)
491{
492    dead_key = 1;
493}
494
495/*
496 * Convert Unicode character "ch" to bytes in "string[slen]".
497 * When "had_alt" is TRUE the ALT key was included in "ch".
498 * Return the length.
499 */
500    static int
501char_to_string(int ch, char_u *string, int slen, int had_alt)
502{
503    int		len;
504    int		i;
505#ifdef FEAT_MBYTE
506    WCHAR	wstring[2];
507    char_u	*ws = NULL;;
508
509    if (os_version.dwPlatformId != VER_PLATFORM_WIN32_NT)
510    {
511	/* On Windows 95/98 we apparently get the character in the active
512	 * codepage, not in UCS-2.  If conversion is needed convert it to
513	 * UCS-2 first. */
514	if ((int)GetACP() == enc_codepage)
515	    len = 0;	    /* no conversion required */
516	else
517	{
518	    string[0] = ch;
519	    len = MultiByteToWideChar(GetACP(), 0, string, 1, wstring, 2);
520	}
521    }
522    else
523    {
524	wstring[0] = ch;
525	len = 1;
526    }
527
528    if (len > 0)
529    {
530	/* "ch" is a UTF-16 character.  Convert it to a string of bytes.  When
531	 * "enc_codepage" is non-zero use the standard Win32 function,
532	 * otherwise use our own conversion function (e.g., for UTF-8). */
533	if (enc_codepage > 0)
534	{
535	    len = WideCharToMultiByte(enc_codepage, 0, wstring, len,
536						       string, slen, 0, NULL);
537	    /* If we had included the ALT key into the character but now the
538	     * upper bit is no longer set, that probably means the conversion
539	     * failed.  Convert the original character and set the upper bit
540	     * afterwards. */
541	    if (had_alt && len == 1 && ch >= 0x80 && string[0] < 0x80)
542	    {
543		wstring[0] = ch & 0x7f;
544		len = WideCharToMultiByte(enc_codepage, 0, wstring, len,
545						       string, slen, 0, NULL);
546		if (len == 1) /* safety check */
547		    string[0] |= 0x80;
548	    }
549	}
550	else
551	{
552	    len = 1;
553	    ws = utf16_to_enc(wstring, &len);
554	    if (ws == NULL)
555		len = 0;
556	    else
557	    {
558		if (len > slen)	/* just in case */
559		    len = slen;
560		mch_memmove(string, ws, len);
561		vim_free(ws);
562	    }
563	}
564    }
565
566    if (len == 0)
567#endif
568    {
569	string[0] = ch;
570	len = 1;
571    }
572
573    for (i = 0; i < len; ++i)
574	if (string[i] == CSI && len <= slen - 2)
575	{
576	    /* Insert CSI as K_CSI. */
577	    mch_memmove(string + i + 3, string + i + 1, len - i - 1);
578	    string[++i] = KS_EXTRA;
579	    string[++i] = (int)KE_CSI;
580	    len += 2;
581	}
582
583    return len;
584}
585
586/*
587 * Key hit, add it to the input buffer.
588 */
589/*ARGSUSED*/
590    static void
591_OnChar(
592    HWND hwnd,
593    UINT ch,
594    int cRepeat)
595{
596    char_u	string[40];
597    int		len = 0;
598
599    len = char_to_string(ch, string, 40, FALSE);
600    if (len == 1 && string[0] == Ctrl_C && ctrl_c_interrupts)
601    {
602	trash_input_buf();
603	got_int = TRUE;
604    }
605
606    add_to_input_buf(string, len);
607}
608
609/*
610 * Alt-Key hit, add it to the input buffer.
611 */
612/*ARGSUSED*/
613    static void
614_OnSysChar(
615    HWND hwnd,
616    UINT cch,
617    int cRepeat)
618{
619    char_u	string[40]; /* Enough for multibyte character */
620    int		len;
621    int		modifiers;
622    int		ch = cch;   /* special keys are negative */
623
624    /* TRACE("OnSysChar(%d, %c)\n", ch, ch); */
625
626    /* OK, we have a character key (given by ch) which was entered with the
627     * ALT key pressed. Eg, if the user presses Alt-A, then ch == 'A'. Note
628     * that the system distinguishes Alt-a and Alt-A (Alt-Shift-a unless
629     * CAPSLOCK is pressed) at this point.
630     */
631    modifiers = MOD_MASK_ALT;
632    if (GetKeyState(VK_SHIFT) & 0x8000)
633	modifiers |= MOD_MASK_SHIFT;
634    if (GetKeyState(VK_CONTROL) & 0x8000)
635	modifiers |= MOD_MASK_CTRL;
636
637    ch = simplify_key(ch, &modifiers);
638    /* remove the SHIFT modifier for keys where it's already included, e.g.,
639     * '(' and '*' */
640    if (ch < 0x100 && !isalpha(ch) && isprint(ch))
641	modifiers &= ~MOD_MASK_SHIFT;
642
643    /* Interpret the ALT key as making the key META, include SHIFT, etc. */
644    ch = extract_modifiers(ch, &modifiers);
645    if (ch == CSI)
646	ch = K_CSI;
647
648    len = 0;
649    if (modifiers)
650    {
651	string[len++] = CSI;
652	string[len++] = KS_MODIFIER;
653	string[len++] = modifiers;
654    }
655
656    if (IS_SPECIAL((int)ch))
657    {
658	string[len++] = CSI;
659	string[len++] = K_SECOND((int)ch);
660	string[len++] = K_THIRD((int)ch);
661    }
662    else
663    {
664	/* Although the documentation isn't clear about it, we assume "ch" is
665	 * a Unicode character. */
666	len += char_to_string(ch, string + len, 40 - len, TRUE);
667    }
668
669    add_to_input_buf(string, len);
670}
671
672    static void
673_OnMouseEvent(
674    int button,
675    int x,
676    int y,
677    int repeated_click,
678    UINT keyFlags)
679{
680    int vim_modifiers = 0x0;
681
682    s_getting_focus = FALSE;
683
684    if (keyFlags & MK_SHIFT)
685	vim_modifiers |= MOUSE_SHIFT;
686    if (keyFlags & MK_CONTROL)
687	vim_modifiers |= MOUSE_CTRL;
688    if (GetKeyState(VK_MENU) & 0x8000)
689	vim_modifiers |= MOUSE_ALT;
690
691    gui_send_mouse_event(button, x, y, repeated_click, vim_modifiers);
692}
693
694/*ARGSUSED*/
695    static void
696_OnMouseButtonDown(
697    HWND hwnd,
698    BOOL fDoubleClick,
699    int x,
700    int y,
701    UINT keyFlags)
702{
703    static LONG	s_prevTime = 0;
704
705    LONG    currentTime = GetMessageTime();
706    int	    button = -1;
707    int	    repeated_click;
708
709    /* Give main window the focus: this is so the cursor isn't hollow. */
710    (void)SetFocus(s_hwnd);
711
712    if (s_uMsg == WM_LBUTTONDOWN || s_uMsg == WM_LBUTTONDBLCLK)
713	button = MOUSE_LEFT;
714    else if (s_uMsg == WM_MBUTTONDOWN || s_uMsg == WM_MBUTTONDBLCLK)
715	button = MOUSE_MIDDLE;
716    else if (s_uMsg == WM_RBUTTONDOWN || s_uMsg == WM_RBUTTONDBLCLK)
717	button = MOUSE_RIGHT;
718#ifndef WIN16 /*<VN>*/
719    else if (s_uMsg == WM_XBUTTONDOWN || s_uMsg == WM_XBUTTONDBLCLK)
720    {
721#ifndef GET_XBUTTON_WPARAM
722# define GET_XBUTTON_WPARAM(wParam)	(HIWORD(wParam))
723#endif
724	button = ((GET_XBUTTON_WPARAM(s_wParam) == 1) ? MOUSE_X1 : MOUSE_X2);
725    }
726    else if (s_uMsg == WM_CAPTURECHANGED)
727    {
728	/* on W95/NT4, somehow you get in here with an odd Msg
729	 * if you press one button while holding down the other..*/
730	if (s_button_pending == MOUSE_LEFT)
731	    button = MOUSE_RIGHT;
732	else
733	    button = MOUSE_LEFT;
734    }
735#endif
736    if (button >= 0)
737    {
738	repeated_click = ((int)(currentTime - s_prevTime) < p_mouset);
739
740	/*
741	 * Holding down the left and right buttons simulates pushing the middle
742	 * button.
743	 */
744	if (repeated_click
745		&& ((button == MOUSE_LEFT && s_button_pending == MOUSE_RIGHT)
746		    || (button == MOUSE_RIGHT
747					  && s_button_pending == MOUSE_LEFT)))
748	{
749	    /*
750	     * Hmm, gui.c will ignore more than one button down at a time, so
751	     * pretend we let go of it first.
752	     */
753	    gui_send_mouse_event(MOUSE_RELEASE, x, y, FALSE, 0x0);
754	    button = MOUSE_MIDDLE;
755	    repeated_click = FALSE;
756	    s_button_pending = -1;
757	    _OnMouseEvent(button, x, y, repeated_click, keyFlags);
758	}
759	else if ((repeated_click)
760		|| (mouse_model_popup() && (button == MOUSE_RIGHT)))
761	{
762	    if (s_button_pending > -1)
763	    {
764		    _OnMouseEvent(s_button_pending, x, y, FALSE, keyFlags);
765		    s_button_pending = -1;
766	    }
767	    /* TRACE("Button down at x %d, y %d\n", x, y); */
768	    _OnMouseEvent(button, x, y, repeated_click, keyFlags);
769	}
770	else
771	{
772	    /*
773	     * If this is the first press (i.e. not a multiple click) don't
774	     * action immediately, but store and wait for:
775	     * i) button-up
776	     * ii) mouse move
777	     * iii) another button press
778	     * before using it.
779	     * This enables us to make left+right simulate middle button,
780	     * without left or right being actioned first.  The side-effect is
781	     * that if you click and hold the mouse without dragging, the
782	     * cursor doesn't move until you release the button. In practice
783	     * this is hardly a problem.
784	     */
785	    s_button_pending = button;
786	    s_x_pending = x;
787	    s_y_pending = y;
788	    s_kFlags_pending = keyFlags;
789	}
790
791	s_prevTime = currentTime;
792    }
793}
794
795/*ARGSUSED*/
796    static void
797_OnMouseMoveOrRelease(
798    HWND hwnd,
799    int x,
800    int y,
801    UINT keyFlags)
802{
803    int button;
804
805    s_getting_focus = FALSE;
806    if (s_button_pending > -1)
807    {
808	/* Delayed action for mouse down event */
809	_OnMouseEvent(s_button_pending, s_x_pending,
810					s_y_pending, FALSE, s_kFlags_pending);
811	s_button_pending = -1;
812    }
813    if (s_uMsg == WM_MOUSEMOVE)
814    {
815	/*
816	 * It's only a MOUSE_DRAG if one or more mouse buttons are being held
817	 * down.
818	 */
819	if (!(keyFlags & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON
820						| MK_XBUTTON1 | MK_XBUTTON2)))
821	{
822	    gui_mouse_moved(x, y);
823	    return;
824	}
825
826	/*
827	 * While button is down, keep grabbing mouse move events when
828	 * the mouse goes outside the window
829	 */
830	SetCapture(s_textArea);
831	button = MOUSE_DRAG;
832	/* TRACE("  move at x %d, y %d\n", x, y); */
833    }
834    else
835    {
836	ReleaseCapture();
837	button = MOUSE_RELEASE;
838	/* TRACE("  up at x %d, y %d\n", x, y); */
839    }
840
841    _OnMouseEvent(button, x, y, FALSE, keyFlags);
842}
843
844#ifdef FEAT_MENU
845/*
846 * Find the vimmenu_T with the given id
847 */
848    static vimmenu_T *
849gui_mswin_find_menu(
850    vimmenu_T	*pMenu,
851    int		id)
852{
853    vimmenu_T	*pChildMenu;
854
855    while (pMenu)
856    {
857	if (pMenu->id == (UINT)id)
858	    break;
859	if (pMenu->children != NULL)
860	{
861	    pChildMenu = gui_mswin_find_menu(pMenu->children, id);
862	    if (pChildMenu)
863	    {
864		pMenu = pChildMenu;
865		break;
866	    }
867	}
868	pMenu = pMenu->next;
869    }
870    return pMenu;
871}
872
873/*ARGSUSED*/
874    static void
875_OnMenu(
876    HWND	hwnd,
877    int		id,
878    HWND	hwndCtl,
879    UINT	codeNotify)
880{
881    vimmenu_T	*pMenu;
882
883    pMenu = gui_mswin_find_menu(root_menu, id);
884    if (pMenu)
885	gui_menu_cb(pMenu);
886}
887#endif
888
889#ifdef MSWIN_FIND_REPLACE
890# if defined(FEAT_MBYTE) && defined(WIN3264)
891/*
892 * copy useful data from structure LPFINDREPLACE to structure LPFINDREPLACEW
893 */
894    static void
895findrep_atow(LPFINDREPLACEW lpfrw, LPFINDREPLACE lpfr)
896{
897    WCHAR *wp;
898
899    lpfrw->hwndOwner = lpfr->hwndOwner;
900    lpfrw->Flags = lpfr->Flags;
901
902    wp = enc_to_utf16(lpfr->lpstrFindWhat, NULL);
903    wcsncpy(lpfrw->lpstrFindWhat, wp, lpfrw->wFindWhatLen - 1);
904    vim_free(wp);
905
906    /* the field "lpstrReplaceWith" doesn't need to be copied */
907}
908
909/*
910 * copy useful data from structure LPFINDREPLACEW to structure LPFINDREPLACE
911 */
912    static void
913findrep_wtoa(LPFINDREPLACE lpfr, LPFINDREPLACEW lpfrw)
914{
915    char_u *p;
916
917    lpfr->Flags = lpfrw->Flags;
918
919    p = utf16_to_enc(lpfrw->lpstrFindWhat, NULL);
920    vim_strncpy(lpfr->lpstrFindWhat, p, lpfr->wFindWhatLen - 1);
921    vim_free(p);
922
923    p = utf16_to_enc(lpfrw->lpstrReplaceWith, NULL);
924    vim_strncpy(lpfr->lpstrReplaceWith, p, lpfr->wReplaceWithLen - 1);
925    vim_free(p);
926}
927# endif
928
929/*
930 * Handle a Find/Replace window message.
931 */
932    static void
933_OnFindRepl(void)
934{
935    int	    flags = 0;
936    int	    down;
937
938# if defined(FEAT_MBYTE) && defined(WIN3264)
939    /* If the OS is Windows NT, and 'encoding' differs from active codepage:
940     * convert text from wide string. */
941    if (os_version.dwPlatformId == VER_PLATFORM_WIN32_NT
942			&& enc_codepage >= 0 && (int)GetACP() != enc_codepage)
943    {
944	findrep_wtoa(&s_findrep_struct, &s_findrep_struct_w);
945    }
946# endif
947
948    if (s_findrep_struct.Flags & FR_DIALOGTERM)
949	/* Give main window the focus back. */
950	(void)SetFocus(s_hwnd);
951
952    if (s_findrep_struct.Flags & FR_FINDNEXT)
953    {
954	flags = FRD_FINDNEXT;
955
956	/* Give main window the focus back: this is so the cursor isn't
957	 * hollow. */
958	(void)SetFocus(s_hwnd);
959    }
960    else if (s_findrep_struct.Flags & FR_REPLACE)
961    {
962	flags = FRD_REPLACE;
963
964	/* Give main window the focus back: this is so the cursor isn't
965	 * hollow. */
966	(void)SetFocus(s_hwnd);
967    }
968    else if (s_findrep_struct.Flags & FR_REPLACEALL)
969    {
970	flags = FRD_REPLACEALL;
971    }
972
973    if (flags != 0)
974    {
975	/* Call the generic GUI function to do the actual work. */
976	if (s_findrep_struct.Flags & FR_WHOLEWORD)
977	    flags |= FRD_WHOLE_WORD;
978	if (s_findrep_struct.Flags & FR_MATCHCASE)
979	    flags |= FRD_MATCH_CASE;
980	down = (s_findrep_struct.Flags & FR_DOWN) != 0;
981	gui_do_findrepl(flags, s_findrep_struct.lpstrFindWhat,
982				     s_findrep_struct.lpstrReplaceWith, down);
983    }
984}
985#endif
986
987    static void
988HandleMouseHide(UINT uMsg, LPARAM lParam)
989{
990    static LPARAM last_lParam = 0L;
991
992    /* We sometimes get a mousemove when the mouse didn't move... */
993    if (uMsg == WM_MOUSEMOVE)
994    {
995	if (lParam == last_lParam)
996	    return;
997	last_lParam = lParam;
998    }
999
1000    /* Handle specially, to centralise coding. We need to be sure we catch all
1001     * possible events which should cause us to restore the cursor (as it is a
1002     * shared resource, we take full responsibility for it).
1003     */
1004    switch (uMsg)
1005    {
1006    case WM_KEYUP:
1007    case WM_CHAR:
1008	/*
1009	 * blank out the pointer if necessary
1010	 */
1011	if (p_mh)
1012	    gui_mch_mousehide(TRUE);
1013	break;
1014
1015    case WM_SYSKEYUP:	 /* show the pointer when a system-key is pressed */
1016    case WM_SYSCHAR:
1017    case WM_MOUSEMOVE:	 /* show the pointer on any mouse action */
1018    case WM_LBUTTONDOWN:
1019    case WM_LBUTTONUP:
1020    case WM_MBUTTONDOWN:
1021    case WM_MBUTTONUP:
1022    case WM_RBUTTONDOWN:
1023    case WM_RBUTTONUP:
1024    case WM_XBUTTONDOWN:
1025    case WM_XBUTTONUP:
1026    case WM_NCMOUSEMOVE:
1027    case WM_NCLBUTTONDOWN:
1028    case WM_NCLBUTTONUP:
1029    case WM_NCMBUTTONDOWN:
1030    case WM_NCMBUTTONUP:
1031    case WM_NCRBUTTONDOWN:
1032    case WM_NCRBUTTONUP:
1033    case WM_KILLFOCUS:
1034	/*
1035	 * if the pointer is currently hidden, then we should show it.
1036	 */
1037	gui_mch_mousehide(FALSE);
1038	break;
1039    }
1040}
1041
1042    static LRESULT CALLBACK
1043_TextAreaWndProc(
1044    HWND hwnd,
1045    UINT uMsg,
1046    WPARAM wParam,
1047    LPARAM lParam)
1048{
1049    /*
1050    TRACE("TextAreaWndProc: hwnd = %08x, msg = %x, wParam = %x, lParam = %x\n",
1051	  hwnd, uMsg, wParam, lParam);
1052    */
1053
1054    HandleMouseHide(uMsg, lParam);
1055
1056    s_uMsg = uMsg;
1057    s_wParam = wParam;
1058    s_lParam = lParam;
1059
1060#ifdef FEAT_BEVAL
1061    TrackUserActivity(uMsg);
1062#endif
1063
1064    switch (uMsg)
1065    {
1066	HANDLE_MSG(hwnd, WM_LBUTTONDBLCLK,_OnMouseButtonDown);
1067	HANDLE_MSG(hwnd, WM_LBUTTONDOWN,_OnMouseButtonDown);
1068	HANDLE_MSG(hwnd, WM_LBUTTONUP,	_OnMouseMoveOrRelease);
1069	HANDLE_MSG(hwnd, WM_MBUTTONDBLCLK,_OnMouseButtonDown);
1070	HANDLE_MSG(hwnd, WM_MBUTTONDOWN,_OnMouseButtonDown);
1071	HANDLE_MSG(hwnd, WM_MBUTTONUP,	_OnMouseMoveOrRelease);
1072	HANDLE_MSG(hwnd, WM_MOUSEMOVE,	_OnMouseMoveOrRelease);
1073	HANDLE_MSG(hwnd, WM_PAINT,	_OnPaint);
1074	HANDLE_MSG(hwnd, WM_RBUTTONDBLCLK,_OnMouseButtonDown);
1075	HANDLE_MSG(hwnd, WM_RBUTTONDOWN,_OnMouseButtonDown);
1076	HANDLE_MSG(hwnd, WM_RBUTTONUP,	_OnMouseMoveOrRelease);
1077#ifndef WIN16 /*<VN>*/
1078	HANDLE_MSG(hwnd, WM_XBUTTONDBLCLK,_OnMouseButtonDown);
1079	HANDLE_MSG(hwnd, WM_XBUTTONDOWN,_OnMouseButtonDown);
1080	HANDLE_MSG(hwnd, WM_XBUTTONUP,	_OnMouseMoveOrRelease);
1081#endif
1082
1083#ifdef FEAT_BEVAL
1084	case WM_NOTIFY: Handle_WM_Notify(hwnd, (LPNMHDR)lParam);
1085	    return TRUE;
1086#endif
1087	default:
1088	    return MyWindowProc(hwnd, uMsg, wParam, lParam);
1089    }
1090}
1091
1092#if (defined(WIN3264) && defined(FEAT_MBYTE)) \
1093	|| defined(GLOBAL_IME) \
1094	|| defined(PROTO)
1095# ifdef PROTO
1096typedef int WINAPI;
1097# endif
1098
1099    LRESULT WINAPI
1100vim_WindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
1101{
1102# ifdef GLOBAL_IME
1103    return global_ime_DefWindowProc(hwnd, message, wParam, lParam);
1104# else
1105    if (wide_WindowProc)
1106	return DefWindowProcW(hwnd, message, wParam, lParam);
1107    return DefWindowProc(hwnd, message, wParam, lParam);
1108#endif
1109}
1110#endif
1111
1112/*
1113 * Called when the foreground or background color has been changed.
1114 */
1115    void
1116gui_mch_new_colors(void)
1117{
1118    /* nothing to do? */
1119}
1120
1121/*
1122 * Set the colors to their default values.
1123 */
1124    void
1125gui_mch_def_colors()
1126{
1127    gui.norm_pixel = GetSysColor(COLOR_WINDOWTEXT);
1128    gui.back_pixel = GetSysColor(COLOR_WINDOW);
1129    gui.def_norm_pixel = gui.norm_pixel;
1130    gui.def_back_pixel = gui.back_pixel;
1131}
1132
1133/*
1134 * Open the GUI window which was created by a call to gui_mch_init().
1135 */
1136    int
1137gui_mch_open(void)
1138{
1139#ifndef SW_SHOWDEFAULT
1140# define SW_SHOWDEFAULT 10	/* Borland 5.0 doesn't have it */
1141#endif
1142    /* Actually open the window, if not already visible
1143     * (may be done already in gui_mch_set_shellsize) */
1144    if (!IsWindowVisible(s_hwnd))
1145	ShowWindow(s_hwnd, SW_SHOWDEFAULT);
1146
1147#ifdef MSWIN_FIND_REPLACE
1148    /* Init replace string here, so that we keep it when re-opening the
1149     * dialog. */
1150    s_findrep_struct.lpstrReplaceWith[0] = NUL;
1151#endif
1152
1153    return OK;
1154}
1155
1156/*
1157 * Get the position of the top left corner of the window.
1158 */
1159    int
1160gui_mch_get_winpos(int *x, int *y)
1161{
1162    RECT    rect;
1163
1164    GetWindowRect(s_hwnd, &rect);
1165    *x = rect.left;
1166    *y = rect.top;
1167    return OK;
1168}
1169
1170/*
1171 * Set the position of the top left corner of the window to the given
1172 * coordinates.
1173 */
1174    void
1175gui_mch_set_winpos(int x, int y)
1176{
1177    SetWindowPos(s_hwnd, NULL, x, y, 0, 0,
1178		 SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
1179}
1180    void
1181gui_mch_set_text_area_pos(int x, int y, int w, int h)
1182{
1183    static int oldx = 0;
1184    static int oldy = 0;
1185
1186    SetWindowPos(s_textArea, NULL, x, y, w, h, SWP_NOZORDER | SWP_NOACTIVATE);
1187
1188#ifdef FEAT_TOOLBAR
1189    if (vim_strchr(p_go, GO_TOOLBAR) != NULL)
1190	SendMessage(s_toolbarhwnd, WM_SIZE,
1191	      (WPARAM)0, (LPARAM)(w + ((long)(TOOLBAR_BUTTON_HEIGHT+8)<<16)));
1192#endif
1193#if defined(FEAT_GUI_TABLINE)
1194    if (showing_tabline)
1195    {
1196	int	top = 0;
1197	RECT	rect;
1198
1199# ifdef FEAT_TOOLBAR
1200	if (vim_strchr(p_go, GO_TOOLBAR) != NULL)
1201	    top = TOOLBAR_BUTTON_HEIGHT + TOOLBAR_BORDER_HEIGHT;
1202# endif
1203	GetClientRect(s_hwnd, &rect);
1204	MoveWindow(s_tabhwnd, 0, top, rect.right, gui.tabline_height, TRUE);
1205    }
1206#endif
1207
1208    /* When side scroll bar is unshown, the size of window will change.
1209     * then, the text area move left or right. thus client rect should be
1210     * forcely redraw. (Yasuhiro Matsumoto) */
1211    if (oldx != x || oldy != y)
1212    {
1213	InvalidateRect(s_hwnd, NULL, FALSE);
1214	oldx = x;
1215	oldy = y;
1216    }
1217}
1218
1219
1220/*
1221 * Scrollbar stuff:
1222 */
1223
1224    void
1225gui_mch_enable_scrollbar(
1226    scrollbar_T     *sb,
1227    int		    flag)
1228{
1229    ShowScrollBar(sb->id, SB_CTL, flag);
1230
1231    /* TODO: When the window is maximized, the size of the window stays the
1232     * same, thus the size of the text area changes.  On Win98 it's OK, on Win
1233     * NT 4.0 it's not... */
1234}
1235
1236    void
1237gui_mch_set_scrollbar_pos(
1238    scrollbar_T *sb,
1239    int		x,
1240    int		y,
1241    int		w,
1242    int		h)
1243{
1244    SetWindowPos(sb->id, NULL, x, y, w, h,
1245			      SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW);
1246}
1247
1248    void
1249gui_mch_create_scrollbar(
1250    scrollbar_T *sb,
1251    int		orient)	/* SBAR_VERT or SBAR_HORIZ */
1252{
1253    sb->id = CreateWindow(
1254	"SCROLLBAR", "Scrollbar",
1255	WS_CHILD | ((orient == SBAR_VERT) ? SBS_VERT : SBS_HORZ), 0, 0,
1256	10,				/* Any value will do for now */
1257	10,				/* Any value will do for now */
1258	s_hwnd, NULL,
1259	s_hinst, NULL);
1260}
1261
1262/*
1263 * Find the scrollbar with the given hwnd.
1264 */
1265	 static scrollbar_T *
1266gui_mswin_find_scrollbar(HWND hwnd)
1267{
1268    win_T	*wp;
1269
1270    if (gui.bottom_sbar.id == hwnd)
1271	return &gui.bottom_sbar;
1272    FOR_ALL_WINDOWS(wp)
1273    {
1274	if (wp->w_scrollbars[SBAR_LEFT].id == hwnd)
1275	    return &wp->w_scrollbars[SBAR_LEFT];
1276	if (wp->w_scrollbars[SBAR_RIGHT].id == hwnd)
1277	    return &wp->w_scrollbars[SBAR_RIGHT];
1278    }
1279    return NULL;
1280}
1281
1282/*
1283 * Get the character size of a font.
1284 */
1285    static void
1286GetFontSize(GuiFont font)
1287{
1288    HWND    hwnd = GetDesktopWindow();
1289    HDC	    hdc = GetWindowDC(hwnd);
1290    HFONT   hfntOld = SelectFont(hdc, (HFONT)font);
1291    TEXTMETRIC tm;
1292
1293    GetTextMetrics(hdc, &tm);
1294    gui.char_width = tm.tmAveCharWidth + tm.tmOverhang;
1295
1296    gui.char_height = tm.tmHeight
1297#ifndef MSWIN16_FASTTEXT
1298				+ p_linespace
1299#endif
1300				;
1301
1302    SelectFont(hdc, hfntOld);
1303
1304    ReleaseDC(hwnd, hdc);
1305}
1306
1307/*
1308 * Adjust gui.char_height (after 'linespace' was changed).
1309 */
1310    int
1311gui_mch_adjust_charheight(void)
1312{
1313    GetFontSize(gui.norm_font);
1314    return OK;
1315}
1316
1317    static GuiFont
1318get_font_handle(LOGFONT *lf)
1319{
1320    HFONT   font = NULL;
1321
1322    /* Load the font */
1323    font = CreateFontIndirect(lf);
1324
1325    if (font == NULL)
1326	return NOFONT;
1327
1328    return (GuiFont)font;
1329}
1330
1331    static int
1332pixels_to_points(int pixels, int vertical)
1333{
1334    int		points;
1335    HWND	hwnd;
1336    HDC		hdc;
1337
1338    hwnd = GetDesktopWindow();
1339    hdc = GetWindowDC(hwnd);
1340
1341    points = MulDiv(pixels, 72,
1342		    GetDeviceCaps(hdc, vertical ? LOGPIXELSY : LOGPIXELSX));
1343
1344    ReleaseDC(hwnd, hdc);
1345
1346    return points;
1347}
1348
1349    GuiFont
1350gui_mch_get_font(
1351    char_u	*name,
1352    int		giveErrorIfMissing)
1353{
1354    LOGFONT	lf;
1355    GuiFont	font = NOFONT;
1356
1357    if (get_logfont(&lf, name, NULL, giveErrorIfMissing) == OK)
1358	font = get_font_handle(&lf);
1359    if (font == NOFONT && giveErrorIfMissing)
1360	EMSG2(_(e_font), name);
1361    return font;
1362}
1363
1364#if defined(FEAT_EVAL) || defined(PROTO)
1365/*
1366 * Return the name of font "font" in allocated memory.
1367 * Don't know how to get the actual name, thus use the provided name.
1368 */
1369/*ARGSUSED*/
1370    char_u *
1371gui_mch_get_fontname(font, name)
1372    GuiFont font;
1373    char_u  *name;
1374{
1375    if (name == NULL)
1376	return NULL;
1377    return vim_strsave(name);
1378}
1379#endif
1380
1381    void
1382gui_mch_free_font(GuiFont font)
1383{
1384    if (font)
1385	DeleteObject((HFONT)font);
1386}
1387
1388    static int
1389hex_digit(int c)
1390{
1391    if (VIM_ISDIGIT(c))
1392	return c - '0';
1393    c = TOLOWER_ASC(c);
1394    if (c >= 'a' && c <= 'f')
1395	return c - 'a' + 10;
1396    return -1000;
1397}
1398/*
1399 * Return the Pixel value (color) for the given color name.
1400 * Return INVALCOLOR for error.
1401 */
1402    guicolor_T
1403gui_mch_get_color(char_u *name)
1404{
1405    typedef struct guicolor_tTable
1406    {
1407	char	    *name;
1408	COLORREF    color;
1409    } guicolor_tTable;
1410
1411    static guicolor_tTable table[] =
1412    {
1413	{"Black",		RGB(0x00, 0x00, 0x00)},
1414	{"DarkGray",		RGB(0x80, 0x80, 0x80)},
1415	{"DarkGrey",		RGB(0x80, 0x80, 0x80)},
1416	{"Gray",		RGB(0xC0, 0xC0, 0xC0)},
1417	{"Grey",		RGB(0xC0, 0xC0, 0xC0)},
1418	{"LightGray",		RGB(0xE0, 0xE0, 0xE0)},
1419	{"LightGrey",		RGB(0xE0, 0xE0, 0xE0)},
1420	{"Gray10",		RGB(0x1A, 0x1A, 0x1A)},
1421	{"Grey10",		RGB(0x1A, 0x1A, 0x1A)},
1422	{"Gray20",		RGB(0x33, 0x33, 0x33)},
1423	{"Grey20",		RGB(0x33, 0x33, 0x33)},
1424	{"Gray30",		RGB(0x4D, 0x4D, 0x4D)},
1425	{"Grey30",		RGB(0x4D, 0x4D, 0x4D)},
1426	{"Gray40",		RGB(0x66, 0x66, 0x66)},
1427	{"Grey40",		RGB(0x66, 0x66, 0x66)},
1428	{"Gray50",		RGB(0x7F, 0x7F, 0x7F)},
1429	{"Grey50",		RGB(0x7F, 0x7F, 0x7F)},
1430	{"Gray60",		RGB(0x99, 0x99, 0x99)},
1431	{"Grey60",		RGB(0x99, 0x99, 0x99)},
1432	{"Gray70",		RGB(0xB3, 0xB3, 0xB3)},
1433	{"Grey70",		RGB(0xB3, 0xB3, 0xB3)},
1434	{"Gray80",		RGB(0xCC, 0xCC, 0xCC)},
1435	{"Grey80",		RGB(0xCC, 0xCC, 0xCC)},
1436	{"Gray90",		RGB(0xE5, 0xE5, 0xE5)},
1437	{"Grey90",		RGB(0xE5, 0xE5, 0xE5)},
1438	{"White",		RGB(0xFF, 0xFF, 0xFF)},
1439	{"DarkRed",		RGB(0x80, 0x00, 0x00)},
1440	{"Red",			RGB(0xFF, 0x00, 0x00)},
1441	{"LightRed",		RGB(0xFF, 0xA0, 0xA0)},
1442	{"DarkBlue",		RGB(0x00, 0x00, 0x80)},
1443	{"Blue",		RGB(0x00, 0x00, 0xFF)},
1444	{"LightBlue",		RGB(0xA0, 0xA0, 0xFF)},
1445	{"DarkGreen",		RGB(0x00, 0x80, 0x00)},
1446	{"Green",		RGB(0x00, 0xFF, 0x00)},
1447	{"LightGreen",		RGB(0xA0, 0xFF, 0xA0)},
1448	{"DarkCyan",		RGB(0x00, 0x80, 0x80)},
1449	{"Cyan",		RGB(0x00, 0xFF, 0xFF)},
1450	{"LightCyan",		RGB(0xA0, 0xFF, 0xFF)},
1451	{"DarkMagenta",		RGB(0x80, 0x00, 0x80)},
1452	{"Magenta",		RGB(0xFF, 0x00, 0xFF)},
1453	{"LightMagenta",	RGB(0xFF, 0xA0, 0xFF)},
1454	{"Brown",		RGB(0x80, 0x40, 0x40)},
1455	{"Yellow",		RGB(0xFF, 0xFF, 0x00)},
1456	{"LightYellow",		RGB(0xFF, 0xFF, 0xA0)},
1457	{"DarkYellow",		RGB(0xBB, 0xBB, 0x00)},
1458	{"SeaGreen",		RGB(0x2E, 0x8B, 0x57)},
1459	{"Orange",		RGB(0xFF, 0xA5, 0x00)},
1460	{"Purple",		RGB(0xA0, 0x20, 0xF0)},
1461	{"SlateBlue",		RGB(0x6A, 0x5A, 0xCD)},
1462	{"Violet",		RGB(0xEE, 0x82, 0xEE)},
1463    };
1464
1465    typedef struct SysColorTable
1466    {
1467	char	    *name;
1468	int	    color;
1469    } SysColorTable;
1470
1471    static SysColorTable sys_table[] =
1472    {
1473#ifdef WIN3264
1474	{"SYS_3DDKSHADOW", COLOR_3DDKSHADOW},
1475	{"SYS_3DHILIGHT", COLOR_3DHILIGHT},
1476#ifndef __MINGW32__
1477	{"SYS_3DHIGHLIGHT", COLOR_3DHIGHLIGHT},
1478#endif
1479	{"SYS_BTNHILIGHT", COLOR_BTNHILIGHT},
1480	{"SYS_BTNHIGHLIGHT", COLOR_BTNHIGHLIGHT},
1481	{"SYS_3DLIGHT", COLOR_3DLIGHT},
1482	{"SYS_3DSHADOW", COLOR_3DSHADOW},
1483	{"SYS_DESKTOP", COLOR_DESKTOP},
1484	{"SYS_INFOBK", COLOR_INFOBK},
1485	{"SYS_INFOTEXT", COLOR_INFOTEXT},
1486	{"SYS_3DFACE", COLOR_3DFACE},
1487#endif
1488	{"SYS_BTNFACE", COLOR_BTNFACE},
1489	{"SYS_BTNSHADOW", COLOR_BTNSHADOW},
1490	{"SYS_ACTIVEBORDER", COLOR_ACTIVEBORDER},
1491	{"SYS_ACTIVECAPTION", COLOR_ACTIVECAPTION},
1492	{"SYS_APPWORKSPACE", COLOR_APPWORKSPACE},
1493	{"SYS_BACKGROUND", COLOR_BACKGROUND},
1494	{"SYS_BTNTEXT", COLOR_BTNTEXT},
1495	{"SYS_CAPTIONTEXT", COLOR_CAPTIONTEXT},
1496	{"SYS_GRAYTEXT", COLOR_GRAYTEXT},
1497	{"SYS_HIGHLIGHT", COLOR_HIGHLIGHT},
1498	{"SYS_HIGHLIGHTTEXT", COLOR_HIGHLIGHTTEXT},
1499	{"SYS_INACTIVEBORDER", COLOR_INACTIVEBORDER},
1500	{"SYS_INACTIVECAPTION", COLOR_INACTIVECAPTION},
1501	{"SYS_INACTIVECAPTIONTEXT", COLOR_INACTIVECAPTIONTEXT},
1502	{"SYS_MENU", COLOR_MENU},
1503	{"SYS_MENUTEXT", COLOR_MENUTEXT},
1504	{"SYS_SCROLLBAR", COLOR_SCROLLBAR},
1505	{"SYS_WINDOW", COLOR_WINDOW},
1506	{"SYS_WINDOWFRAME", COLOR_WINDOWFRAME},
1507	{"SYS_WINDOWTEXT", COLOR_WINDOWTEXT}
1508    };
1509
1510    int		    r, g, b;
1511    int		    i;
1512
1513    if (name[0] == '#' && strlen(name) == 7)
1514    {
1515	/* Name is in "#rrggbb" format */
1516	r = hex_digit(name[1]) * 16 + hex_digit(name[2]);
1517	g = hex_digit(name[3]) * 16 + hex_digit(name[4]);
1518	b = hex_digit(name[5]) * 16 + hex_digit(name[6]);
1519	if (r < 0 || g < 0 || b < 0)
1520	    return INVALCOLOR;
1521	return RGB(r, g, b);
1522    }
1523    else
1524    {
1525	/* Check if the name is one of the colors we know */
1526	for (i = 0; i < sizeof(table) / sizeof(table[0]); i++)
1527	    if (STRICMP(name, table[i].name) == 0)
1528		return table[i].color;
1529    }
1530
1531    /*
1532     * Try to look up a system colour.
1533     */
1534    for (i = 0; i < sizeof(sys_table) / sizeof(sys_table[0]); i++)
1535	if (STRICMP(name, sys_table[i].name) == 0)
1536	    return GetSysColor(sys_table[i].color);
1537
1538    /*
1539     * Last attempt. Look in the file "$VIMRUNTIME/rgb.txt".
1540     */
1541    {
1542#define LINE_LEN 100
1543	FILE	*fd;
1544	char	line[LINE_LEN];
1545	char_u	*fname;
1546
1547	fname = expand_env_save((char_u *)"$VIMRUNTIME/rgb.txt");
1548	if (fname == NULL)
1549	    return INVALCOLOR;
1550
1551	fd = mch_fopen((char *)fname, "rt");
1552	vim_free(fname);
1553	if (fd == NULL)
1554	    return INVALCOLOR;
1555
1556	while (!feof(fd))
1557	{
1558	    int	    len;
1559	    int	    pos;
1560	    char    *color;
1561
1562	    fgets(line, LINE_LEN, fd);
1563	    len = (int)STRLEN(line);
1564
1565	    if (len <= 1 || line[len-1] != '\n')
1566		continue;
1567
1568	    line[len-1] = '\0';
1569
1570	    i = sscanf(line, "%d %d %d %n", &r, &g, &b, &pos);
1571	    if (i != 3)
1572		continue;
1573
1574	    color = line + pos;
1575
1576	    if (STRICMP(color, name) == 0)
1577	    {
1578		fclose(fd);
1579		return (guicolor_T) RGB(r, g, b);
1580	    }
1581	}
1582
1583	fclose(fd);
1584    }
1585
1586    return INVALCOLOR;
1587}
1588/*
1589 * Return OK if the key with the termcap name "name" is supported.
1590 */
1591    int
1592gui_mch_haskey(char_u *name)
1593{
1594    int i;
1595
1596    for (i = 0; special_keys[i].vim_code1 != NUL; i++)
1597	if (name[0] == special_keys[i].vim_code0 &&
1598					 name[1] == special_keys[i].vim_code1)
1599	    return OK;
1600    return FAIL;
1601}
1602
1603    void
1604gui_mch_beep(void)
1605{
1606    MessageBeep(MB_OK);
1607}
1608/*
1609 * Invert a rectangle from row r, column c, for nr rows and nc columns.
1610 */
1611    void
1612gui_mch_invert_rectangle(
1613    int	    r,
1614    int	    c,
1615    int	    nr,
1616    int	    nc)
1617{
1618    RECT    rc;
1619
1620    /*
1621     * Note: InvertRect() excludes right and bottom of rectangle.
1622     */
1623    rc.left = FILL_X(c);
1624    rc.top = FILL_Y(r);
1625    rc.right = rc.left + nc * gui.char_width;
1626    rc.bottom = rc.top + nr * gui.char_height;
1627    InvertRect(s_hdc, &rc);
1628}
1629
1630/*
1631 * Iconify the GUI window.
1632 */
1633    void
1634gui_mch_iconify(void)
1635{
1636    ShowWindow(s_hwnd, SW_MINIMIZE);
1637}
1638
1639/*
1640 * Draw a cursor without focus.
1641 */
1642    void
1643gui_mch_draw_hollow_cursor(guicolor_T color)
1644{
1645    HBRUSH  hbr;
1646    RECT    rc;
1647
1648    /*
1649     * Note: FrameRect() excludes right and bottom of rectangle.
1650     */
1651    rc.left = FILL_X(gui.col);
1652    rc.top = FILL_Y(gui.row);
1653    rc.right = rc.left + gui.char_width;
1654#ifdef FEAT_MBYTE
1655    if (mb_lefthalve(gui.row, gui.col))
1656	rc.right += gui.char_width;
1657#endif
1658    rc.bottom = rc.top + gui.char_height;
1659    hbr = CreateSolidBrush(color);
1660    FrameRect(s_hdc, &rc, hbr);
1661    DeleteBrush(hbr);
1662}
1663/*
1664 * Draw part of a cursor, "w" pixels wide, and "h" pixels high, using
1665 * color "color".
1666 */
1667    void
1668gui_mch_draw_part_cursor(
1669    int		w,
1670    int		h,
1671    guicolor_T	color)
1672{
1673    HBRUSH	hbr;
1674    RECT	rc;
1675
1676    /*
1677     * Note: FillRect() excludes right and bottom of rectangle.
1678     */
1679    rc.left =
1680#ifdef FEAT_RIGHTLEFT
1681		/* vertical line should be on the right of current point */
1682		CURSOR_BAR_RIGHT ? FILL_X(gui.col + 1) - w :
1683#endif
1684		    FILL_X(gui.col);
1685    rc.top = FILL_Y(gui.row) + gui.char_height - h;
1686    rc.right = rc.left + w;
1687    rc.bottom = rc.top + h;
1688    hbr = CreateSolidBrush(color);
1689    FillRect(s_hdc, &rc, hbr);
1690    DeleteBrush(hbr);
1691}
1692
1693/*
1694 * Process a single Windows message.
1695 * If one is not available we hang until one is.
1696 */
1697    static void
1698process_message(void)
1699{
1700    MSG		msg;
1701    UINT	vk = 0;		/* Virtual key */
1702    char_u	string[40];
1703    int		i;
1704    int		modifiers = 0;
1705    int		key;
1706#ifdef FEAT_MENU
1707    static char_u k10[] = {K_SPECIAL, 'k', ';', 0};
1708#endif
1709
1710    GetMessage(&msg, NULL, 0, 0);
1711
1712#ifdef FEAT_OLE
1713    /* Look after OLE Automation commands */
1714    if (msg.message == WM_OLE)
1715    {
1716	char_u *str = (char_u *)msg.lParam;
1717	if (str == NULL || *str == NUL)
1718	{
1719	    /* Message can't be ours, forward it.  Fixes problem with Ultramon
1720	     * 3.0.4 */
1721	    DispatchMessage(&msg);
1722	}
1723	else
1724	{
1725	    add_to_input_buf(str, (int)STRLEN(str));
1726	    vim_free(str);  /* was allocated in CVim::SendKeys() */
1727	}
1728	return;
1729    }
1730#endif
1731
1732#ifdef FEAT_NETBEANS_INTG
1733    if (msg.message == WM_NETBEANS)
1734    {
1735	netbeans_read();
1736	return;
1737    }
1738#endif
1739
1740#ifdef FEAT_SNIFF
1741    if (sniff_request_waiting && want_sniff_request)
1742    {
1743	static char_u bytes[3] = {CSI, (char_u)KS_EXTRA, (char_u)KE_SNIFF};
1744	add_to_input_buf(bytes, 3); /* K_SNIFF */
1745	sniff_request_waiting = 0;
1746	want_sniff_request = 0;
1747	/* request is handled in normal.c */
1748    }
1749    if (msg.message == WM_USER)
1750    {
1751	MyTranslateMessage(&msg);
1752	DispatchMessage(&msg);
1753	return;
1754    }
1755#endif
1756
1757#ifdef MSWIN_FIND_REPLACE
1758    /* Don't process messages used by the dialog */
1759    if (s_findrep_hwnd != NULL && IsDialogMessage(s_findrep_hwnd, &msg))
1760    {
1761	HandleMouseHide(msg.message, msg.lParam);
1762	return;
1763    }
1764#endif
1765
1766    /*
1767     * Check if it's a special key that we recognise.  If not, call
1768     * TranslateMessage().
1769     */
1770    if (msg.message == WM_KEYDOWN || msg.message == WM_SYSKEYDOWN)
1771    {
1772	vk = (int) msg.wParam;
1773	/* handle key after dead key, but ignore shift, alt and control */
1774	if (dead_key && vk != VK_SHIFT && vk != VK_MENU && vk != VK_CONTROL)
1775	{
1776	    dead_key = 0;
1777	    /* handle non-alphabetic keys (ones that hopefully cannot generate
1778	     * umlaut-characters), unless when control is down */
1779	    if (vk < 'A' || vk > 'Z' || (GetKeyState(VK_CONTROL) & 0x8000))
1780	    {
1781		MSG dm;
1782
1783		dm.message = msg.message;
1784		dm.hwnd = msg.hwnd;
1785		dm.wParam = VK_SPACE;
1786		MyTranslateMessage(&dm);	/* generate dead character */
1787		if (vk != VK_SPACE) /* and send current character once more */
1788		    PostMessage(msg.hwnd, msg.message, msg.wParam, msg.lParam);
1789		return;
1790	    }
1791	}
1792
1793	/* Check for CTRL-BREAK */
1794	if (vk == VK_CANCEL)
1795	{
1796	    trash_input_buf();
1797	    got_int = TRUE;
1798	    string[0] = Ctrl_C;
1799	    add_to_input_buf(string, 1);
1800	}
1801
1802	for (i = 0; special_keys[i].key_sym != 0; i++)
1803	{
1804	    /* ignore VK_SPACE when ALT key pressed: system menu */
1805	    if (special_keys[i].key_sym == vk
1806		    && (vk != VK_SPACE || !(GetKeyState(VK_MENU) & 0x8000)))
1807	    {
1808#ifdef FEAT_MENU
1809		/* Check for <F10>: Windows selects the menu.  When <F10> is
1810		 * mapped we want to use the mapping instead. */
1811		if (vk == VK_F10
1812			&& gui.menu_is_active
1813			&& check_map(k10, State, FALSE, TRUE, FALSE) == NULL)
1814		    break;
1815#endif
1816		if (GetKeyState(VK_SHIFT) & 0x8000)
1817		    modifiers |= MOD_MASK_SHIFT;
1818		/*
1819		 * Don't use caps-lock as shift, because these are special keys
1820		 * being considered here, and we only want letters to get
1821		 * shifted -- webb
1822		 */
1823		/*
1824		if (GetKeyState(VK_CAPITAL) & 0x0001)
1825		    modifiers ^= MOD_MASK_SHIFT;
1826		*/
1827		if (GetKeyState(VK_CONTROL) & 0x8000)
1828		    modifiers |= MOD_MASK_CTRL;
1829		if (GetKeyState(VK_MENU) & 0x8000)
1830		    modifiers |= MOD_MASK_ALT;
1831
1832		if (special_keys[i].vim_code1 == NUL)
1833		    key = special_keys[i].vim_code0;
1834		else
1835		    key = TO_SPECIAL(special_keys[i].vim_code0,
1836						   special_keys[i].vim_code1);
1837		key = simplify_key(key, &modifiers);
1838		if (key == CSI)
1839		    key = K_CSI;
1840
1841		if (modifiers)
1842		{
1843		    string[0] = CSI;
1844		    string[1] = KS_MODIFIER;
1845		    string[2] = modifiers;
1846		    add_to_input_buf(string, 3);
1847		}
1848
1849		if (IS_SPECIAL(key))
1850		{
1851		    string[0] = CSI;
1852		    string[1] = K_SECOND(key);
1853		    string[2] = K_THIRD(key);
1854		    add_to_input_buf(string, 3);
1855		}
1856		else
1857		{
1858		    int	len;
1859
1860		    /* Handle "key" as a Unicode character. */
1861		    len = char_to_string(key, string, 40, FALSE);
1862		    add_to_input_buf(string, len);
1863		}
1864		break;
1865	    }
1866	}
1867	if (special_keys[i].key_sym == 0)
1868	{
1869	    /* Some keys need C-S- where they should only need C-.
1870	     * Ignore 0xff, Windows XP sends it when NUMLOCK has changed since
1871	     * system startup (Helmut Stiegler, 2003 Oct 3). */
1872	    if (vk != 0xff
1873		    && (GetKeyState(VK_CONTROL) & 0x8000)
1874		    && !(GetKeyState(VK_SHIFT) & 0x8000)
1875		    && !(GetKeyState(VK_MENU) & 0x8000))
1876	    {
1877		/* CTRL-6 is '^'; Japanese keyboard maps '^' to vk == 0xDE */
1878		if (vk == '6' || MapVirtualKey(vk, 2) == (UINT)'^')
1879		{
1880		    string[0] = Ctrl_HAT;
1881		    add_to_input_buf(string, 1);
1882		}
1883		/* vk == 0xBD AZERTY for CTRL-'-', but CTRL-[ for * QWERTY! */
1884		else if (vk == 0xBD)	/* QWERTY for CTRL-'-' */
1885		{
1886		    string[0] = Ctrl__;
1887		    add_to_input_buf(string, 1);
1888		}
1889		/* CTRL-2 is '@'; Japanese keyboard maps '@' to vk == 0xC0 */
1890		else if (vk == '2' || MapVirtualKey(vk, 2) == (UINT)'@')
1891		{
1892		    string[0] = Ctrl_AT;
1893		    add_to_input_buf(string, 1);
1894		}
1895		else
1896		    MyTranslateMessage(&msg);
1897	    }
1898	    else
1899		MyTranslateMessage(&msg);
1900	}
1901    }
1902#ifdef FEAT_MBYTE_IME
1903    else if (msg.message == WM_IME_NOTIFY)
1904	_OnImeNotify(msg.hwnd, (DWORD)msg.wParam, (DWORD)msg.lParam);
1905    else if (msg.message == WM_KEYUP && im_get_status())
1906	/* added for non-MS IME (Yasuhiro Matsumoto) */
1907	MyTranslateMessage(&msg);
1908#endif
1909#if !defined(FEAT_MBYTE_IME) && defined(GLOBAL_IME)
1910/* GIME_TEST */
1911    else if (msg.message == WM_IME_STARTCOMPOSITION)
1912    {
1913	POINT point;
1914
1915	global_ime_set_font(&norm_logfont);
1916	point.x = FILL_X(gui.col);
1917	point.y = FILL_Y(gui.row);
1918	MapWindowPoints(s_textArea, s_hwnd, &point, 1);
1919	global_ime_set_position(&point);
1920    }
1921#endif
1922
1923#ifdef FEAT_MENU
1924    /* Check for <F10>: Default effect is to select the menu.  When <F10> is
1925     * mapped we need to stop it here to avoid strange effects (e.g., for the
1926     * key-up event) */
1927    if (vk != VK_F10 || check_map(k10, State, FALSE, TRUE, FALSE) == NULL)
1928#endif
1929	DispatchMessage(&msg);
1930}
1931
1932/*
1933 * Catch up with any queued events.  This may put keyboard input into the
1934 * input buffer, call resize call-backs, trigger timers etc.  If there is
1935 * nothing in the event queue (& no timers pending), then we return
1936 * immediately.
1937 */
1938    void
1939gui_mch_update(void)
1940{
1941    MSG	    msg;
1942
1943    if (!s_busy_processing)
1944	while (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)
1945						  && !vim_is_input_buf_full())
1946	    process_message();
1947}
1948
1949/*
1950 * GUI input routine called by gui_wait_for_chars().  Waits for a character
1951 * from the keyboard.
1952 *  wtime == -1	    Wait forever.
1953 *  wtime == 0	    This should never happen.
1954 *  wtime > 0	    Wait wtime milliseconds for a character.
1955 * Returns OK if a character was found to be available within the given time,
1956 * or FAIL otherwise.
1957 */
1958    int
1959gui_mch_wait_for_chars(int wtime)
1960{
1961    MSG		msg;
1962    int		focus;
1963
1964    s_timed_out = FALSE;
1965
1966    if (wtime > 0)
1967    {
1968	/* Don't do anything while processing a (scroll) message. */
1969	if (s_busy_processing)
1970	    return FAIL;
1971	s_wait_timer = (UINT)SetTimer(NULL, 0, (UINT)wtime,
1972							 (TIMERPROC)_OnTimer);
1973    }
1974
1975    allow_scrollbar = TRUE;
1976
1977    focus = gui.in_focus;
1978    while (!s_timed_out)
1979    {
1980	/* Stop or start blinking when focus changes */
1981	if (gui.in_focus != focus)
1982	{
1983	    if (gui.in_focus)
1984		gui_mch_start_blink();
1985	    else
1986		gui_mch_stop_blink();
1987	    focus = gui.in_focus;
1988	}
1989
1990	if (s_need_activate)
1991	{
1992#ifdef WIN32
1993	    (void)SetForegroundWindow(s_hwnd);
1994#else
1995	    (void)SetActiveWindow(s_hwnd);
1996#endif
1997	    s_need_activate = FALSE;
1998	}
1999
2000#ifdef FEAT_NETBEANS_INTG
2001	/* Process the queued netbeans messages. */
2002	netbeans_parse_messages();
2003#endif
2004
2005	/*
2006	 * Don't use gui_mch_update() because then we will spin-lock until a
2007	 * char arrives, instead we use GetMessage() to hang until an
2008	 * event arrives.  No need to check for input_buf_full because we are
2009	 * returning as soon as it contains a single char -- webb
2010	 */
2011	process_message();
2012
2013	if (input_available())
2014	{
2015	    if (s_wait_timer != 0 && !s_timed_out)
2016	    {
2017		KillTimer(NULL, s_wait_timer);
2018
2019		/* Eat spurious WM_TIMER messages */
2020		while (PeekMessage(&msg, s_hwnd, WM_TIMER, WM_TIMER, PM_REMOVE))
2021		    ;
2022		s_wait_timer = 0;
2023	    }
2024	    allow_scrollbar = FALSE;
2025
2026	    /* Clear pending mouse button, the release event may have been
2027	     * taken by the dialog window.  But don't do this when getting
2028	     * focus, we need the mouse-up event then. */
2029	    if (!s_getting_focus)
2030		s_button_pending = -1;
2031
2032	    return OK;
2033	}
2034    }
2035    allow_scrollbar = FALSE;
2036    return FAIL;
2037}
2038
2039/*
2040 * Clear a rectangular region of the screen from text pos (row1, col1) to
2041 * (row2, col2) inclusive.
2042 */
2043    void
2044gui_mch_clear_block(
2045    int		row1,
2046    int		col1,
2047    int		row2,
2048    int		col2)
2049{
2050    RECT	rc;
2051
2052    /*
2053     * Clear one extra pixel at the far right, for when bold characters have
2054     * spilled over to the window border.
2055     * Note: FillRect() excludes right and bottom of rectangle.
2056     */
2057    rc.left = FILL_X(col1);
2058    rc.top = FILL_Y(row1);
2059    rc.right = FILL_X(col2 + 1) + (col2 == Columns - 1);
2060    rc.bottom = FILL_Y(row2 + 1);
2061    clear_rect(&rc);
2062}
2063
2064/*
2065 * Clear the whole text window.
2066 */
2067    void
2068gui_mch_clear_all(void)
2069{
2070    RECT    rc;
2071
2072    rc.left = 0;
2073    rc.top = 0;
2074    rc.right = Columns * gui.char_width + 2 * gui.border_width;
2075    rc.bottom = Rows * gui.char_height + 2 * gui.border_width;
2076    clear_rect(&rc);
2077}
2078/*
2079 * Menu stuff.
2080 */
2081
2082    void
2083gui_mch_enable_menu(int flag)
2084{
2085#ifdef FEAT_MENU
2086    SetMenu(s_hwnd, flag ? s_menuBar : NULL);
2087#endif
2088}
2089
2090/*ARGSUSED*/
2091    void
2092gui_mch_set_menu_pos(
2093    int	    x,
2094    int	    y,
2095    int	    w,
2096    int	    h)
2097{
2098    /* It will be in the right place anyway */
2099}
2100
2101#if defined(FEAT_MENU) || defined(PROTO)
2102/*
2103 * Make menu item hidden or not hidden
2104 */
2105    void
2106gui_mch_menu_hidden(
2107    vimmenu_T	*menu,
2108    int		hidden)
2109{
2110    /*
2111     * This doesn't do what we want.  Hmm, just grey the menu items for now.
2112     */
2113    /*
2114    if (hidden)
2115	EnableMenuItem(s_menuBar, menu->id, MF_BYCOMMAND | MF_DISABLED);
2116    else
2117	EnableMenuItem(s_menuBar, menu->id, MF_BYCOMMAND | MF_ENABLED);
2118    */
2119    gui_mch_menu_grey(menu, hidden);
2120}
2121
2122/*
2123 * This is called after setting all the menus to grey/hidden or not.
2124 */
2125    void
2126gui_mch_draw_menubar(void)
2127{
2128    DrawMenuBar(s_hwnd);
2129}
2130#endif /*FEAT_MENU*/
2131
2132#ifndef PROTO
2133void
2134#ifdef VIMDLL
2135_export
2136#endif
2137_cdecl
2138SaveInst(HINSTANCE hInst)
2139{
2140    s_hinst = hInst;
2141}
2142#endif
2143
2144/*
2145 * Return the RGB value of a pixel as a long.
2146 */
2147    long_u
2148gui_mch_get_rgb(guicolor_T pixel)
2149{
2150    return (GetRValue(pixel) << 16) + (GetGValue(pixel) << 8)
2151							   + GetBValue(pixel);
2152}
2153
2154#if defined(FEAT_GUI_DIALOG) || defined(PROTO)
2155/* Convert pixels in X to dialog units */
2156    static WORD
2157PixelToDialogX(int numPixels)
2158{
2159    return (WORD)((numPixels * 4) / s_dlgfntwidth);
2160}
2161
2162/* Convert pixels in Y to dialog units */
2163    static WORD
2164PixelToDialogY(int numPixels)
2165{
2166    return (WORD)((numPixels * 8) / s_dlgfntheight);
2167}
2168
2169/* Return the width in pixels of the given text in the given DC. */
2170    static int
2171GetTextWidth(HDC hdc, char_u *str, int len)
2172{
2173    SIZE    size;
2174
2175    GetTextExtentPoint(hdc, str, len, &size);
2176    return size.cx;
2177}
2178
2179#ifdef FEAT_MBYTE
2180/*
2181 * Return the width in pixels of the given text in the given DC, taking care
2182 * of 'encoding' to active codepage conversion.
2183 */
2184    static int
2185GetTextWidthEnc(HDC hdc, char_u *str, int len)
2186{
2187    SIZE	size;
2188    WCHAR	*wstr;
2189    int		n;
2190    int		wlen = len;
2191
2192    if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
2193    {
2194	/* 'encoding' differs from active codepage: convert text and use wide
2195	 * function */
2196	wstr = enc_to_utf16(str, &wlen);
2197	if (wstr != NULL)
2198	{
2199	    n = GetTextExtentPointW(hdc, wstr, wlen, &size);
2200	    vim_free(wstr);
2201	    if (n)
2202		return size.cx;
2203	}
2204    }
2205
2206    return GetTextWidth(hdc, str, len);
2207}
2208#else
2209# define GetTextWidthEnc(h, s, l) GetTextWidth((h), (s), (l))
2210#endif
2211
2212/*
2213 * A quick little routine that will center one window over another, handy for
2214 * dialog boxes.  Taken from the Win32SDK samples.
2215 */
2216    static BOOL
2217CenterWindow(
2218    HWND hwndChild,
2219    HWND hwndParent)
2220{
2221    RECT    rChild, rParent;
2222    int     wChild, hChild, wParent, hParent;
2223    int     wScreen, hScreen, xNew, yNew;
2224    HDC     hdc;
2225
2226    GetWindowRect(hwndChild, &rChild);
2227    wChild = rChild.right - rChild.left;
2228    hChild = rChild.bottom - rChild.top;
2229
2230    /* If Vim is minimized put the window in the middle of the screen. */
2231    if (hwndParent == NULL || IsMinimized(hwndParent))
2232    {
2233#ifdef WIN16
2234	rParent.left = 0;
2235	rParent.top = 0;
2236	rParent.right = GetSystemMetrics(SM_CXSCREEN);
2237	rParent.bottom = GetSystemMetrics(SM_CYFULLSCREEN);
2238#else
2239	SystemParametersInfo(SPI_GETWORKAREA, 0, &rParent, 0);
2240#endif
2241    }
2242    else
2243	GetWindowRect(hwndParent, &rParent);
2244    wParent = rParent.right - rParent.left;
2245    hParent = rParent.bottom - rParent.top;
2246
2247    hdc = GetDC(hwndChild);
2248    wScreen = GetDeviceCaps (hdc, HORZRES);
2249    hScreen = GetDeviceCaps (hdc, VERTRES);
2250    ReleaseDC(hwndChild, hdc);
2251
2252    xNew = rParent.left + ((wParent - wChild) /2);
2253    if (xNew < 0)
2254    {
2255	xNew = 0;
2256    }
2257    else if ((xNew+wChild) > wScreen)
2258    {
2259	xNew = wScreen - wChild;
2260    }
2261
2262    yNew = rParent.top	+ ((hParent - hChild) /2);
2263    if (yNew < 0)
2264	yNew = 0;
2265    else if ((yNew+hChild) > hScreen)
2266	yNew = hScreen - hChild;
2267
2268    return SetWindowPos(hwndChild, NULL, xNew, yNew, 0, 0,
2269						   SWP_NOSIZE | SWP_NOZORDER);
2270}
2271#endif /* FEAT_GUI_DIALOG */
2272
2273void
2274gui_mch_activate_window(void)
2275{
2276    (void)SetActiveWindow(s_hwnd);
2277}
2278
2279#if defined(FEAT_TOOLBAR) || defined(PROTO)
2280    void
2281gui_mch_show_toolbar(int showit)
2282{
2283    if (s_toolbarhwnd == NULL)
2284	return;
2285
2286    if (showit)
2287    {
2288# ifdef FEAT_MBYTE
2289#  ifndef TB_SETUNICODEFORMAT
2290    /* For older compilers.  We assume this never changes. */
2291#   define TB_SETUNICODEFORMAT 0x2005
2292#  endif
2293	/* Enable/disable unicode support */
2294	int uu = (enc_codepage >= 0 && (int)GetACP() != enc_codepage);
2295	SendMessage(s_toolbarhwnd, TB_SETUNICODEFORMAT, (WPARAM)uu, (LPARAM)0);
2296# endif
2297	ShowWindow(s_toolbarhwnd, SW_SHOW);
2298    }
2299    else
2300	ShowWindow(s_toolbarhwnd, SW_HIDE);
2301}
2302
2303/* Then number of bitmaps is fixed.  Exit is missing! */
2304#define TOOLBAR_BITMAP_COUNT 31
2305
2306#endif
2307
2308#if defined(FEAT_GUI_TABLINE) || defined(PROTO)
2309    static void
2310add_tabline_popup_menu_entry(HMENU pmenu, UINT item_id, char_u *item_text)
2311{
2312#ifdef FEAT_MBYTE
2313    WCHAR	*wn = NULL;
2314    int		n;
2315
2316    if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
2317    {
2318	/* 'encoding' differs from active codepage: convert menu name
2319	 * and use wide function */
2320	wn = enc_to_utf16(item_text, NULL);
2321	if (wn != NULL)
2322	{
2323	    MENUITEMINFOW	infow;
2324
2325	    infow.cbSize = sizeof(infow);
2326	    infow.fMask = MIIM_TYPE | MIIM_ID;
2327	    infow.wID = item_id;
2328	    infow.fType = MFT_STRING;
2329	    infow.dwTypeData = wn;
2330	    infow.cch = (UINT)wcslen(wn);
2331	    n = InsertMenuItemW(pmenu, item_id, FALSE, &infow);
2332	    vim_free(wn);
2333	    if (n == 0 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
2334		/* Failed, try using non-wide function. */
2335		wn = NULL;
2336	}
2337    }
2338
2339    if (wn == NULL)
2340#endif
2341    {
2342	MENUITEMINFO	info;
2343
2344	info.cbSize = sizeof(info);
2345	info.fMask = MIIM_TYPE | MIIM_ID;
2346	info.wID = item_id;
2347	info.fType = MFT_STRING;
2348	info.dwTypeData = item_text;
2349	info.cch = (UINT)STRLEN(item_text);
2350	InsertMenuItem(pmenu, item_id, FALSE, &info);
2351    }
2352}
2353
2354    static void
2355show_tabline_popup_menu(void)
2356{
2357    HMENU	    tab_pmenu;
2358    long	    rval;
2359    POINT	    pt;
2360
2361    /* When ignoring events don't show the menu. */
2362    if (hold_gui_events
2363# ifdef FEAT_CMDWIN
2364	    || cmdwin_type != 0
2365# endif
2366       )
2367	return;
2368
2369    tab_pmenu = CreatePopupMenu();
2370    if (tab_pmenu == NULL)
2371	return;
2372
2373    add_tabline_popup_menu_entry(tab_pmenu, TABLINE_MENU_CLOSE, _("Close tab"));
2374    add_tabline_popup_menu_entry(tab_pmenu, TABLINE_MENU_NEW, _("New tab"));
2375    add_tabline_popup_menu_entry(tab_pmenu, TABLINE_MENU_OPEN,
2376				 _("Open tab..."));
2377
2378    GetCursorPos(&pt);
2379    rval = TrackPopupMenuEx(tab_pmenu, TPM_RETURNCMD, pt.x, pt.y, s_tabhwnd,
2380									NULL);
2381
2382    DestroyMenu(tab_pmenu);
2383
2384    /* Add the string cmd into input buffer */
2385    if (rval > 0)
2386    {
2387	TCHITTESTINFO htinfo;
2388	int idx;
2389
2390	if (ScreenToClient(s_tabhwnd, &pt) == 0)
2391	    return;
2392
2393	htinfo.pt.x = pt.x;
2394	htinfo.pt.y = pt.y;
2395	idx = TabCtrl_HitTest(s_tabhwnd, &htinfo);
2396	if (idx == -1)
2397	    idx = 0;
2398	else
2399	    idx += 1;
2400
2401	send_tabline_menu_event(idx, (int)rval);
2402    }
2403}
2404
2405/*
2406 * Show or hide the tabline.
2407 */
2408    void
2409gui_mch_show_tabline(int showit)
2410{
2411    if (s_tabhwnd == NULL)
2412	return;
2413
2414    if (!showit != !showing_tabline)
2415    {
2416	if (showit)
2417	    ShowWindow(s_tabhwnd, SW_SHOW);
2418	else
2419	    ShowWindow(s_tabhwnd, SW_HIDE);
2420	showing_tabline = showit;
2421    }
2422}
2423
2424/*
2425 * Return TRUE when tabline is displayed.
2426 */
2427    int
2428gui_mch_showing_tabline(void)
2429{
2430    return s_tabhwnd != NULL && showing_tabline;
2431}
2432
2433/*
2434 * Update the labels of the tabline.
2435 */
2436    void
2437gui_mch_update_tabline(void)
2438{
2439    tabpage_T	*tp;
2440    TCITEM	tie;
2441    int		nr = 0;
2442    int		curtabidx = 0;
2443    RECT	rc;
2444#ifdef FEAT_MBYTE
2445    static int	use_unicode = FALSE;
2446    int		uu;
2447    WCHAR	*wstr = NULL;
2448#endif
2449
2450    if (s_tabhwnd == NULL)
2451	return;
2452
2453#if defined(FEAT_MBYTE)
2454# ifndef CCM_SETUNICODEFORMAT
2455    /* For older compilers.  We assume this never changes. */
2456#  define CCM_SETUNICODEFORMAT 0x2005
2457# endif
2458    uu = (enc_codepage >= 0 && (int)GetACP() != enc_codepage);
2459    if (uu != use_unicode)
2460    {
2461	/* Enable/disable unicode support */
2462	SendMessage(s_tabhwnd, CCM_SETUNICODEFORMAT, (WPARAM)uu, (LPARAM)0);
2463	use_unicode = uu;
2464    }
2465#endif
2466
2467    tie.mask = TCIF_TEXT;
2468    tie.iImage = -1;
2469
2470    /* Add a label for each tab page.  They all contain the same text area. */
2471    for (tp = first_tabpage; tp != NULL; tp = tp->tp_next, ++nr)
2472    {
2473	if (tp == curtab)
2474	    curtabidx = nr;
2475
2476	if (!TabCtrl_GetItemRect(s_tabhwnd, nr, &rc))
2477	{
2478	    /* Add the tab */
2479	    tie.pszText = "-Empty-";
2480	    TabCtrl_InsertItem(s_tabhwnd, nr, &tie);
2481	}
2482
2483	get_tabline_label(tp, FALSE);
2484	tie.pszText = NameBuff;
2485#ifdef FEAT_MBYTE
2486	wstr = NULL;
2487	if (use_unicode)
2488	{
2489	    /* Need to go through Unicode. */
2490	    wstr = enc_to_utf16(NameBuff, NULL);
2491	    if (wstr != NULL)
2492	    {
2493		TCITEMW		tiw;
2494
2495		tiw.mask = TCIF_TEXT;
2496		tiw.iImage = -1;
2497		tiw.pszText = wstr;
2498		SendMessage(s_tabhwnd, TCM_SETITEMW, (WPARAM)nr, (LPARAM)&tiw);
2499		vim_free(wstr);
2500	    }
2501	}
2502	if (wstr == NULL)
2503#endif
2504	{
2505	    TabCtrl_SetItem(s_tabhwnd, nr, &tie);
2506	}
2507    }
2508
2509    /* Remove any old labels. */
2510    while (TabCtrl_GetItemRect(s_tabhwnd, nr, &rc))
2511	TabCtrl_DeleteItem(s_tabhwnd, nr);
2512
2513    if (TabCtrl_GetCurSel(s_tabhwnd) != curtabidx)
2514	TabCtrl_SetCurSel(s_tabhwnd, curtabidx);
2515}
2516
2517/*
2518 * Set the current tab to "nr".  First tab is 1.
2519 */
2520    void
2521gui_mch_set_curtab(nr)
2522    int		nr;
2523{
2524    if (s_tabhwnd == NULL)
2525	return;
2526
2527    if (TabCtrl_GetCurSel(s_tabhwnd) != nr -1)
2528	TabCtrl_SetCurSel(s_tabhwnd, nr -1);
2529}
2530
2531#endif
2532
2533/*
2534 * ":simalt" command.
2535 */
2536    void
2537ex_simalt(exarg_T *eap)
2538{
2539    char_u *keys = eap->arg;
2540
2541    PostMessage(s_hwnd, WM_SYSCOMMAND, (WPARAM)SC_KEYMENU, (LPARAM)0);
2542    while (*keys)
2543    {
2544	if (*keys == '~')
2545	    *keys = ' ';	    /* for showing system menu */
2546	PostMessage(s_hwnd, WM_CHAR, (WPARAM)*keys, (LPARAM)0);
2547	keys++;
2548    }
2549}
2550
2551/*
2552 * Create the find & replace dialogs.
2553 * You can't have both at once: ":find" when replace is showing, destroys
2554 * the replace dialog first, and the other way around.
2555 */
2556#ifdef MSWIN_FIND_REPLACE
2557    static void
2558initialise_findrep(char_u *initial_string)
2559{
2560    int		wword = FALSE;
2561    int		mcase = !p_ic;
2562    char_u	*entry_text;
2563
2564    /* Get the search string to use. */
2565    entry_text = get_find_dialog_text(initial_string, &wword, &mcase);
2566
2567    s_findrep_struct.hwndOwner = s_hwnd;
2568    s_findrep_struct.Flags = FR_DOWN;
2569    if (mcase)
2570	s_findrep_struct.Flags |= FR_MATCHCASE;
2571    if (wword)
2572	s_findrep_struct.Flags |= FR_WHOLEWORD;
2573    if (entry_text != NULL && *entry_text != NUL)
2574	vim_strncpy(s_findrep_struct.lpstrFindWhat, entry_text,
2575					   s_findrep_struct.wFindWhatLen - 1);
2576    vim_free(entry_text);
2577}
2578#endif
2579
2580    static void
2581set_window_title(HWND hwnd, char *title)
2582{
2583#ifdef FEAT_MBYTE
2584    if (title != NULL && enc_codepage >= 0 && enc_codepage != (int)GetACP())
2585    {
2586	WCHAR	*wbuf;
2587	int	n;
2588
2589	/* Convert the title from 'encoding' to UTF-16. */
2590	wbuf = (WCHAR *)enc_to_utf16((char_u *)title, NULL);
2591	if (wbuf != NULL)
2592	{
2593	    n = SetWindowTextW(hwnd, wbuf);
2594	    vim_free(wbuf);
2595	    if (n != 0 || GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
2596		return;
2597	    /* Retry with non-wide function (for Windows 98). */
2598	}
2599    }
2600#endif
2601    (void)SetWindowText(hwnd, (LPCSTR)title);
2602}
2603
2604    void
2605gui_mch_find_dialog(exarg_T *eap)
2606{
2607#ifdef MSWIN_FIND_REPLACE
2608    if (s_findrep_msg != 0)
2609    {
2610	if (IsWindow(s_findrep_hwnd) && !s_findrep_is_find)
2611	    DestroyWindow(s_findrep_hwnd);
2612
2613	if (!IsWindow(s_findrep_hwnd))
2614	{
2615	    initialise_findrep(eap->arg);
2616# if defined(FEAT_MBYTE) && defined(WIN3264)
2617	    /* If the OS is Windows NT, and 'encoding' differs from active
2618	     * codepage: convert text and use wide function. */
2619	    if (os_version.dwPlatformId == VER_PLATFORM_WIN32_NT
2620		    && enc_codepage >= 0 && (int)GetACP() != enc_codepage)
2621	    {
2622		findrep_atow(&s_findrep_struct_w, &s_findrep_struct);
2623		s_findrep_hwnd = FindTextW(
2624					(LPFINDREPLACEW) &s_findrep_struct_w);
2625	    }
2626	    else
2627# endif
2628		s_findrep_hwnd = FindText((LPFINDREPLACE) &s_findrep_struct);
2629	}
2630
2631	set_window_title(s_findrep_hwnd,
2632			       _("Find string (use '\\\\' to find  a '\\')"));
2633	(void)SetFocus(s_findrep_hwnd);
2634
2635	s_findrep_is_find = TRUE;
2636    }
2637#endif
2638}
2639
2640
2641    void
2642gui_mch_replace_dialog(exarg_T *eap)
2643{
2644#ifdef MSWIN_FIND_REPLACE
2645    if (s_findrep_msg != 0)
2646    {
2647	if (IsWindow(s_findrep_hwnd) && s_findrep_is_find)
2648	    DestroyWindow(s_findrep_hwnd);
2649
2650	if (!IsWindow(s_findrep_hwnd))
2651	{
2652	    initialise_findrep(eap->arg);
2653# if defined(FEAT_MBYTE) && defined(WIN3264)
2654	    if (os_version.dwPlatformId == VER_PLATFORM_WIN32_NT
2655		    && enc_codepage >= 0 && (int)GetACP() != enc_codepage)
2656	    {
2657		findrep_atow(&s_findrep_struct_w, &s_findrep_struct);
2658		s_findrep_hwnd = ReplaceTextW(
2659					(LPFINDREPLACEW) &s_findrep_struct_w);
2660	    }
2661	    else
2662# endif
2663		s_findrep_hwnd = ReplaceText(
2664					   (LPFINDREPLACE) &s_findrep_struct);
2665	}
2666
2667	set_window_title(s_findrep_hwnd,
2668			    _("Find & Replace (use '\\\\' to find  a '\\')"));
2669	(void)SetFocus(s_findrep_hwnd);
2670
2671	s_findrep_is_find = FALSE;
2672    }
2673#endif
2674}
2675
2676
2677/*
2678 * Set visibility of the pointer.
2679 */
2680    void
2681gui_mch_mousehide(int hide)
2682{
2683    if (hide != gui.pointer_hidden)
2684    {
2685	ShowCursor(!hide);
2686	gui.pointer_hidden = hide;
2687    }
2688}
2689
2690#ifdef FEAT_MENU
2691    static void
2692gui_mch_show_popupmenu_at(vimmenu_T *menu, int x, int y)
2693{
2694    /* Unhide the mouse, we don't get move events here. */
2695    gui_mch_mousehide(FALSE);
2696
2697    (void)TrackPopupMenu(
2698	(HMENU)menu->submenu_id,
2699	TPM_LEFTALIGN | TPM_LEFTBUTTON,
2700	x, y,
2701	(int)0,	    /*reserved param*/
2702	s_hwnd,
2703	NULL);
2704    /*
2705     * NOTE: The pop-up menu can eat the mouse up event.
2706     * We deal with this in normal.c.
2707     */
2708}
2709#endif
2710
2711/*
2712 * Got a message when the system will go down.
2713 */
2714    static void
2715_OnEndSession(void)
2716{
2717    getout_preserve_modified(1);
2718}
2719
2720/*
2721 * Get this message when the user clicks on the cross in the top right corner
2722 * of a Windows95 window.
2723 */
2724/*ARGSUSED*/
2725    static void
2726_OnClose(
2727    HWND hwnd)
2728{
2729    gui_shell_closed();
2730}
2731
2732/*
2733 * Get a message when the window is being destroyed.
2734 */
2735    static void
2736_OnDestroy(
2737    HWND hwnd)
2738{
2739#ifdef WIN16_3DLOOK
2740    Ctl3dUnregister(s_hinst);
2741#endif
2742    if (!destroying)
2743	_OnClose(hwnd);
2744}
2745
2746    static void
2747_OnPaint(
2748    HWND hwnd)
2749{
2750    if (!IsMinimized(hwnd))
2751    {
2752	PAINTSTRUCT ps;
2753
2754	out_flush();	    /* make sure all output has been processed */
2755	(void)BeginPaint(hwnd, &ps);
2756
2757#ifdef FEAT_MBYTE
2758	/* prevent multi-byte characters from misprinting on an invalid
2759	 * rectangle */
2760	if (has_mbyte)
2761	{
2762	    RECT rect;
2763
2764	    GetClientRect(hwnd, &rect);
2765	    ps.rcPaint.left = rect.left;
2766	    ps.rcPaint.right = rect.right;
2767	}
2768#endif
2769
2770	if (!IsRectEmpty(&ps.rcPaint))
2771	    gui_redraw(ps.rcPaint.left, ps.rcPaint.top,
2772		    ps.rcPaint.right - ps.rcPaint.left + 1,
2773		    ps.rcPaint.bottom - ps.rcPaint.top + 1);
2774	EndPaint(hwnd, &ps);
2775    }
2776}
2777
2778/*ARGSUSED*/
2779    static void
2780_OnSize(
2781    HWND hwnd,
2782    UINT state,
2783    int cx,
2784    int cy)
2785{
2786    if (!IsMinimized(hwnd))
2787    {
2788	gui_resize_shell(cx, cy);
2789
2790#ifdef FEAT_MENU
2791	/* Menu bar may wrap differently now */
2792	gui_mswin_get_menu_height(TRUE);
2793#endif
2794    }
2795}
2796
2797    static void
2798_OnSetFocus(
2799    HWND hwnd,
2800    HWND hwndOldFocus)
2801{
2802    gui_focus_change(TRUE);
2803    s_getting_focus = TRUE;
2804    (void)MyWindowProc(hwnd, WM_SETFOCUS, (WPARAM)hwndOldFocus, 0);
2805}
2806
2807    static void
2808_OnKillFocus(
2809    HWND hwnd,
2810    HWND hwndNewFocus)
2811{
2812    gui_focus_change(FALSE);
2813    s_getting_focus = FALSE;
2814    (void)MyWindowProc(hwnd, WM_KILLFOCUS, (WPARAM)hwndNewFocus, 0);
2815}
2816
2817/*
2818 * Get a message when the user switches back to vim
2819 */
2820#ifdef WIN16
2821    static BOOL
2822#else
2823    static LRESULT
2824#endif
2825_OnActivateApp(
2826    HWND hwnd,
2827    BOOL fActivate,
2828#ifdef WIN16
2829    HTASK dwThreadId
2830#else
2831    DWORD dwThreadId
2832#endif
2833	)
2834{
2835    /* we call gui_focus_change() in _OnSetFocus() */
2836    /* gui_focus_change((int)fActivate); */
2837    return MyWindowProc(hwnd, WM_ACTIVATEAPP, fActivate, (DWORD)dwThreadId);
2838}
2839
2840#if defined(FEAT_WINDOWS) || defined(PROTO)
2841    void
2842gui_mch_destroy_scrollbar(scrollbar_T *sb)
2843{
2844    DestroyWindow(sb->id);
2845}
2846#endif
2847
2848/*
2849 * Get current mouse coordinates in text window.
2850 */
2851    void
2852gui_mch_getmouse(int *x, int *y)
2853{
2854    RECT rct;
2855    POINT mp;
2856
2857    (void)GetWindowRect(s_textArea, &rct);
2858    (void)GetCursorPos((LPPOINT)&mp);
2859    *x = (int)(mp.x - rct.left);
2860    *y = (int)(mp.y - rct.top);
2861}
2862
2863/*
2864 * Move mouse pointer to character at (x, y).
2865 */
2866    void
2867gui_mch_setmouse(int x, int y)
2868{
2869    RECT rct;
2870
2871    (void)GetWindowRect(s_textArea, &rct);
2872    (void)SetCursorPos(x + gui.border_offset + rct.left,
2873		       y + gui.border_offset + rct.top);
2874}
2875
2876    static void
2877gui_mswin_get_valid_dimensions(
2878    int w,
2879    int h,
2880    int *valid_w,
2881    int *valid_h)
2882{
2883    int	    base_width, base_height;
2884
2885    base_width = gui_get_base_width()
2886	+ GetSystemMetrics(SM_CXFRAME) * 2;
2887    base_height = gui_get_base_height()
2888	+ GetSystemMetrics(SM_CYFRAME) * 2
2889	+ GetSystemMetrics(SM_CYCAPTION)
2890#ifdef FEAT_MENU
2891	+ gui_mswin_get_menu_height(FALSE)
2892#endif
2893	;
2894    *valid_w = base_width +
2895		    ((w - base_width) / gui.char_width) * gui.char_width;
2896    *valid_h = base_height +
2897		    ((h - base_height) / gui.char_height) * gui.char_height;
2898}
2899
2900    void
2901gui_mch_flash(int msec)
2902{
2903    RECT    rc;
2904
2905    /*
2906     * Note: InvertRect() excludes right and bottom of rectangle.
2907     */
2908    rc.left = 0;
2909    rc.top = 0;
2910    rc.right = gui.num_cols * gui.char_width;
2911    rc.bottom = gui.num_rows * gui.char_height;
2912    InvertRect(s_hdc, &rc);
2913    gui_mch_flush();			/* make sure it's displayed */
2914
2915    ui_delay((long)msec, TRUE);	/* wait for a few msec */
2916
2917    InvertRect(s_hdc, &rc);
2918}
2919
2920/*
2921 * Return flags used for scrolling.
2922 * The SW_INVALIDATE is required when part of the window is covered or
2923 * off-screen. Refer to MS KB Q75236.
2924 */
2925    static int
2926get_scroll_flags(void)
2927{
2928    HWND	hwnd;
2929    RECT	rcVim, rcOther, rcDest;
2930
2931    GetWindowRect(s_hwnd, &rcVim);
2932
2933    /* Check if the window is partly above or below the screen.  We don't care
2934     * about partly left or right of the screen, it is not relevant when
2935     * scrolling up or down. */
2936    if (rcVim.top < 0 || rcVim.bottom > GetSystemMetrics(SM_CYFULLSCREEN))
2937	return SW_INVALIDATE;
2938
2939    /* Check if there is an window (partly) on top of us. */
2940    for (hwnd = s_hwnd; (hwnd = GetWindow(hwnd, GW_HWNDPREV)) != (HWND)0; )
2941	if (IsWindowVisible(hwnd))
2942	{
2943	    GetWindowRect(hwnd, &rcOther);
2944	    if (IntersectRect(&rcDest, &rcVim, &rcOther))
2945		return SW_INVALIDATE;
2946	}
2947    return 0;
2948}
2949
2950/*
2951 * Delete the given number of lines from the given row, scrolling up any
2952 * text further down within the scroll region.
2953 */
2954    void
2955gui_mch_delete_lines(
2956    int	    row,
2957    int	    num_lines)
2958{
2959    RECT	rc;
2960
2961    rc.left = FILL_X(gui.scroll_region_left);
2962    rc.right = FILL_X(gui.scroll_region_right + 1);
2963    rc.top = FILL_Y(row);
2964    rc.bottom = FILL_Y(gui.scroll_region_bot + 1);
2965
2966    ScrollWindowEx(s_textArea, 0, -num_lines * gui.char_height,
2967				    &rc, &rc, NULL, NULL, get_scroll_flags());
2968
2969    UpdateWindow(s_textArea);
2970    /* This seems to be required to avoid the cursor disappearing when
2971     * scrolling such that the cursor ends up in the top-left character on
2972     * the screen...   But why?  (Webb) */
2973    /* It's probably fixed by disabling drawing the cursor while scrolling. */
2974    /* gui.cursor_is_valid = FALSE; */
2975
2976    gui_clear_block(gui.scroll_region_bot - num_lines + 1,
2977						       gui.scroll_region_left,
2978	gui.scroll_region_bot, gui.scroll_region_right);
2979}
2980
2981/*
2982 * Insert the given number of lines before the given row, scrolling down any
2983 * following text within the scroll region.
2984 */
2985    void
2986gui_mch_insert_lines(
2987    int		row,
2988    int		num_lines)
2989{
2990    RECT	rc;
2991
2992    rc.left = FILL_X(gui.scroll_region_left);
2993    rc.right = FILL_X(gui.scroll_region_right + 1);
2994    rc.top = FILL_Y(row);
2995    rc.bottom = FILL_Y(gui.scroll_region_bot + 1);
2996    /* The SW_INVALIDATE is required when part of the window is covered or
2997     * off-screen.  How do we avoid it when it's not needed? */
2998    ScrollWindowEx(s_textArea, 0, num_lines * gui.char_height,
2999				    &rc, &rc, NULL, NULL, get_scroll_flags());
3000
3001    UpdateWindow(s_textArea);
3002
3003    gui_clear_block(row, gui.scroll_region_left,
3004				row + num_lines - 1, gui.scroll_region_right);
3005}
3006
3007
3008/*ARGSUSED*/
3009    void
3010gui_mch_exit(int rc)
3011{
3012    ReleaseDC(s_textArea, s_hdc);
3013    DeleteObject(s_brush);
3014
3015#ifdef FEAT_TEAROFF
3016    /* Unload the tearoff bitmap */
3017    (void)DeleteObject((HGDIOBJ)s_htearbitmap);
3018#endif
3019
3020    /* Destroy our window (if we have one). */
3021    if (s_hwnd != NULL)
3022    {
3023	destroying = TRUE;	/* ignore WM_DESTROY message now */
3024	DestroyWindow(s_hwnd);
3025    }
3026
3027#ifdef GLOBAL_IME
3028    global_ime_end();
3029#endif
3030}
3031
3032    static char_u *
3033logfont2name(LOGFONT lf)
3034{
3035    char	*p;
3036    char	*res;
3037    char	*charset_name;
3038
3039    charset_name = charset_id2name((int)lf.lfCharSet);
3040    res = alloc((unsigned)(strlen(lf.lfFaceName) + 20
3041		    + (charset_name == NULL ? 0 : strlen(charset_name) + 2)));
3042    if (res != NULL)
3043    {
3044	p = res;
3045	/* make a normal font string out of the lf thing:*/
3046	sprintf((char *)p, "%s:h%d", lf.lfFaceName, pixels_to_points(
3047			 lf.lfHeight < 0 ? -lf.lfHeight : lf.lfHeight, TRUE));
3048	while (*p)
3049	{
3050	    if (*p == ' ')
3051		*p = '_';
3052	    ++p;
3053	}
3054#ifndef MSWIN16_FASTTEXT
3055	if (lf.lfItalic)
3056	    STRCAT(p, ":i");
3057	if (lf.lfWeight >= FW_BOLD)
3058	    STRCAT(p, ":b");
3059#endif
3060	if (lf.lfUnderline)
3061	    STRCAT(p, ":u");
3062	if (lf.lfStrikeOut)
3063	    STRCAT(p, ":s");
3064	if (charset_name != NULL)
3065	{
3066	    STRCAT(p, ":c");
3067	    STRCAT(p, charset_name);
3068	}
3069    }
3070
3071    return res;
3072}
3073
3074/*
3075 * Initialise vim to use the font with the given name.
3076 * Return FAIL if the font could not be loaded, OK otherwise.
3077 */
3078/*ARGSUSED*/
3079    int
3080gui_mch_init_font(char_u *font_name, int fontset)
3081{
3082    LOGFONT	lf;
3083    GuiFont	font = NOFONT;
3084    char_u	*p;
3085
3086    /* Load the font */
3087    if (get_logfont(&lf, font_name, NULL, TRUE) == OK)
3088	font = get_font_handle(&lf);
3089    if (font == NOFONT)
3090	return FAIL;
3091
3092    if (font_name == NULL)
3093	font_name = lf.lfFaceName;
3094#if defined(FEAT_MBYTE_IME) || defined(GLOBAL_IME)
3095    norm_logfont = lf;
3096#endif
3097#ifdef FEAT_MBYTE_IME
3098    im_set_font(&lf);
3099#endif
3100    gui_mch_free_font(gui.norm_font);
3101    gui.norm_font = font;
3102    current_font_height = lf.lfHeight;
3103    GetFontSize(font);
3104
3105    p = logfont2name(lf);
3106    if (p != NULL)
3107    {
3108	hl_set_font_name(p);
3109
3110	/* When setting 'guifont' to "*" replace it with the actual font name.
3111	 * */
3112	if (STRCMP(font_name, "*") == 0 && STRCMP(p_guifont, "*") == 0)
3113	{
3114	    vim_free(p_guifont);
3115	    p_guifont = p;
3116	}
3117	else
3118	    vim_free(p);
3119    }
3120
3121#ifndef MSWIN16_FASTTEXT
3122    gui_mch_free_font(gui.ital_font);
3123    gui.ital_font = NOFONT;
3124    gui_mch_free_font(gui.bold_font);
3125    gui.bold_font = NOFONT;
3126    gui_mch_free_font(gui.boldital_font);
3127    gui.boldital_font = NOFONT;
3128
3129    if (!lf.lfItalic)
3130    {
3131	lf.lfItalic = TRUE;
3132	gui.ital_font = get_font_handle(&lf);
3133	lf.lfItalic = FALSE;
3134    }
3135    if (lf.lfWeight < FW_BOLD)
3136    {
3137	lf.lfWeight = FW_BOLD;
3138	gui.bold_font = get_font_handle(&lf);
3139	if (!lf.lfItalic)
3140	{
3141	    lf.lfItalic = TRUE;
3142	    gui.boldital_font = get_font_handle(&lf);
3143	}
3144    }
3145#endif
3146
3147    return OK;
3148}
3149
3150#ifndef WPF_RESTORETOMAXIMIZED
3151# define WPF_RESTORETOMAXIMIZED 2   /* just in case someone doesn't have it */
3152#endif
3153
3154/*
3155 * Return TRUE if the GUI window is maximized, filling the whole screen.
3156 */
3157    int
3158gui_mch_maximized()
3159{
3160    WINDOWPLACEMENT wp;
3161
3162    wp.length = sizeof(WINDOWPLACEMENT);
3163    if (GetWindowPlacement(s_hwnd, &wp))
3164	return wp.showCmd == SW_SHOWMAXIMIZED
3165	    || (wp.showCmd == SW_SHOWMINIMIZED
3166		    && wp.flags == WPF_RESTORETOMAXIMIZED);
3167
3168    return 0;
3169}
3170
3171/*
3172 * Called when the font changed while the window is maximized.  Compute the
3173 * new Rows and Columns.  This is like resizing the window.
3174 */
3175    void
3176gui_mch_newfont()
3177{
3178    RECT	rect;
3179
3180    GetWindowRect(s_hwnd, &rect);
3181    gui_resize_shell(rect.right - rect.left
3182			- GetSystemMetrics(SM_CXFRAME) * 2,
3183		     rect.bottom - rect.top
3184			- GetSystemMetrics(SM_CYFRAME) * 2
3185			- GetSystemMetrics(SM_CYCAPTION)
3186#ifdef FEAT_MENU
3187			- gui_mswin_get_menu_height(FALSE)
3188#endif
3189	    );
3190}
3191
3192/*
3193 * Set the window title
3194 */
3195/*ARGSUSED*/
3196    void
3197gui_mch_settitle(
3198    char_u  *title,
3199    char_u  *icon)
3200{
3201    set_window_title(s_hwnd, (title == NULL ? "VIM" : (char *)title));
3202}
3203
3204#ifdef FEAT_MOUSESHAPE
3205/* Table for shape IDCs.  Keep in sync with the mshape_names[] table in
3206 * misc2.c! */
3207static LPCSTR mshape_idcs[] =
3208{
3209    MAKEINTRESOURCE(IDC_ARROW),		/* arrow */
3210    MAKEINTRESOURCE(0),			/* blank */
3211    MAKEINTRESOURCE(IDC_IBEAM),		/* beam */
3212    MAKEINTRESOURCE(IDC_SIZENS),	/* updown */
3213    MAKEINTRESOURCE(IDC_SIZENS),	/* udsizing */
3214    MAKEINTRESOURCE(IDC_SIZEWE),	/* leftright */
3215    MAKEINTRESOURCE(IDC_SIZEWE),	/* lrsizing */
3216    MAKEINTRESOURCE(IDC_WAIT),		/* busy */
3217#ifdef WIN3264
3218    MAKEINTRESOURCE(IDC_NO),		/* no */
3219#else
3220    MAKEINTRESOURCE(IDC_ICON),		/* no */
3221#endif
3222    MAKEINTRESOURCE(IDC_ARROW),		/* crosshair */
3223    MAKEINTRESOURCE(IDC_ARROW),		/* hand1 */
3224    MAKEINTRESOURCE(IDC_ARROW),		/* hand2 */
3225    MAKEINTRESOURCE(IDC_ARROW),		/* pencil */
3226    MAKEINTRESOURCE(IDC_ARROW),		/* question */
3227    MAKEINTRESOURCE(IDC_ARROW),		/* right-arrow */
3228    MAKEINTRESOURCE(IDC_UPARROW),	/* up-arrow */
3229    MAKEINTRESOURCE(IDC_ARROW)		/* last one */
3230};
3231
3232    void
3233mch_set_mouse_shape(int shape)
3234{
3235    LPCSTR idc;
3236
3237    if (shape == MSHAPE_HIDE)
3238	ShowCursor(FALSE);
3239    else
3240    {
3241	if (shape >= MSHAPE_NUMBERED)
3242	    idc = MAKEINTRESOURCE(IDC_ARROW);
3243	else
3244	    idc = mshape_idcs[shape];
3245#ifdef SetClassLongPtr
3246	SetClassLongPtr(s_textArea, GCLP_HCURSOR, (__int3264)(LONG_PTR)LoadCursor(NULL, idc));
3247#else
3248# ifdef WIN32
3249	SetClassLong(s_textArea, GCL_HCURSOR, (long_u)LoadCursor(NULL, idc));
3250# else /* Win16 */
3251	SetClassWord(s_textArea, GCW_HCURSOR, (WORD)LoadCursor(NULL, idc));
3252# endif
3253#endif
3254	if (!p_mh)
3255	{
3256	    POINT mp;
3257
3258	    /* Set the position to make it redrawn with the new shape. */
3259	    (void)GetCursorPos((LPPOINT)&mp);
3260	    (void)SetCursorPos(mp.x, mp.y);
3261	    ShowCursor(TRUE);
3262	}
3263    }
3264}
3265#endif
3266
3267#ifdef FEAT_BROWSE
3268/*
3269 * The file browser exists in two versions: with "W" uses wide characters,
3270 * without "W" the current codepage.  When FEAT_MBYTE is defined and on
3271 * Windows NT/2000/XP the "W" functions are used.
3272 */
3273
3274# if defined(FEAT_MBYTE) && defined(WIN3264)
3275/*
3276 * Wide version of convert_filter().  Keep in sync!
3277 */
3278    static WCHAR *
3279convert_filterW(char_u *s)
3280{
3281    WCHAR	*res;
3282    unsigned	s_len = (unsigned)STRLEN(s);
3283    unsigned	i;
3284
3285    res = (WCHAR *)alloc((s_len + 3) * sizeof(WCHAR));
3286    if (res != NULL)
3287    {
3288	for (i = 0; i < s_len; ++i)
3289	    if (s[i] == '\t' || s[i] == '\n')
3290		res[i] = '\0';
3291	    else
3292		res[i] = s[i];
3293	res[s_len] = NUL;
3294	/* Add two extra NULs to make sure it's properly terminated. */
3295	res[s_len + 1] = NUL;
3296	res[s_len + 2] = NUL;
3297    }
3298    return res;
3299}
3300
3301/*
3302 * Wide version of gui_mch_browse().  Keep in sync!
3303 */
3304    static char_u *
3305gui_mch_browseW(
3306	int saving,
3307	char_u *title,
3308	char_u *dflt,
3309	char_u *ext,
3310	char_u *initdir,
3311	char_u *filter)
3312{
3313    /* We always use the wide function.  This means enc_to_utf16() must work,
3314     * otherwise it fails miserably! */
3315    OPENFILENAMEW	fileStruct;
3316    WCHAR		fileBuf[MAXPATHL];
3317    WCHAR		*wp;
3318    int			i;
3319    WCHAR		*titlep = NULL;
3320    WCHAR		*extp = NULL;
3321    WCHAR		*initdirp = NULL;
3322    WCHAR		*filterp;
3323    char_u		*p;
3324
3325    if (dflt == NULL)
3326	fileBuf[0] = NUL;
3327    else
3328    {
3329	wp = enc_to_utf16(dflt, NULL);
3330	if (wp == NULL)
3331	    fileBuf[0] = NUL;
3332	else
3333	{
3334	    for (i = 0; wp[i] != NUL && i < MAXPATHL - 1; ++i)
3335		fileBuf[i] = wp[i];
3336	    fileBuf[i] = NUL;
3337	    vim_free(wp);
3338	}
3339    }
3340
3341    /* Convert the filter to Windows format. */
3342    filterp = convert_filterW(filter);
3343
3344    vim_memset(&fileStruct, 0, sizeof(OPENFILENAMEW));
3345#ifdef OPENFILENAME_SIZE_VERSION_400
3346    /* be compatible with Windows NT 4.0 */
3347    /* TODO: what to use for OPENFILENAMEW??? */
3348    fileStruct.lStructSize = OPENFILENAME_SIZE_VERSION_400;
3349#else
3350    fileStruct.lStructSize = sizeof(fileStruct);
3351#endif
3352
3353    if (title != NULL)
3354	titlep = enc_to_utf16(title, NULL);
3355    fileStruct.lpstrTitle = titlep;
3356
3357    if (ext != NULL)
3358	extp = enc_to_utf16(ext, NULL);
3359    fileStruct.lpstrDefExt = extp;
3360
3361    fileStruct.lpstrFile = fileBuf;
3362    fileStruct.nMaxFile = MAXPATHL;
3363    fileStruct.lpstrFilter = filterp;
3364    fileStruct.hwndOwner = s_hwnd;		/* main Vim window is owner*/
3365    /* has an initial dir been specified? */
3366    if (initdir != NULL && *initdir != NUL)
3367    {
3368	/* Must have backslashes here, no matter what 'shellslash' says */
3369	initdirp = enc_to_utf16(initdir, NULL);
3370	if (initdirp != NULL)
3371	{
3372	    for (wp = initdirp; *wp != NUL; ++wp)
3373		if (*wp == '/')
3374		    *wp = '\\';
3375	}
3376	fileStruct.lpstrInitialDir = initdirp;
3377    }
3378
3379    /*
3380     * TODO: Allow selection of multiple files.  Needs another arg to this
3381     * function to ask for it, and need to use OFN_ALLOWMULTISELECT below.
3382     * Also, should we use OFN_FILEMUSTEXIST when opening?  Vim can edit on
3383     * files that don't exist yet, so I haven't put it in.  What about
3384     * OFN_PATHMUSTEXIST?
3385     * Don't use OFN_OVERWRITEPROMPT, Vim has its own ":confirm" dialog.
3386     */
3387    fileStruct.Flags = (OFN_NOCHANGEDIR | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY);
3388#ifdef FEAT_SHORTCUT
3389    if (curbuf->b_p_bin)
3390	fileStruct.Flags |= OFN_NODEREFERENCELINKS;
3391#endif
3392    if (saving)
3393    {
3394	if (!GetSaveFileNameW(&fileStruct))
3395	    return NULL;
3396    }
3397    else
3398    {
3399	if (!GetOpenFileNameW(&fileStruct))
3400	    return NULL;
3401    }
3402
3403    vim_free(filterp);
3404    vim_free(initdirp);
3405    vim_free(titlep);
3406    vim_free(extp);
3407
3408    /* Convert from UCS2 to 'encoding'. */
3409    p = utf16_to_enc(fileBuf, NULL);
3410    if (p != NULL)
3411	/* when out of memory we get garbage for non-ASCII chars */
3412	STRCPY(fileBuf, p);
3413    vim_free(p);
3414
3415    /* Give focus back to main window (when using MDI). */
3416    SetFocus(s_hwnd);
3417
3418    /* Shorten the file name if possible */
3419    return vim_strsave(shorten_fname1((char_u *)fileBuf));
3420}
3421# endif /* FEAT_MBYTE */
3422
3423
3424/*
3425 * Convert the string s to the proper format for a filter string by replacing
3426 * the \t and \n delimiters with \0.
3427 * Returns the converted string in allocated memory.
3428 *
3429 * Keep in sync with convert_filterW() above!
3430 */
3431    static char_u *
3432convert_filter(char_u *s)
3433{
3434    char_u	*res;
3435    unsigned	s_len = (unsigned)STRLEN(s);
3436    unsigned	i;
3437
3438    res = alloc(s_len + 3);
3439    if (res != NULL)
3440    {
3441	for (i = 0; i < s_len; ++i)
3442	    if (s[i] == '\t' || s[i] == '\n')
3443		res[i] = '\0';
3444	    else
3445		res[i] = s[i];
3446	res[s_len] = NUL;
3447	/* Add two extra NULs to make sure it's properly terminated. */
3448	res[s_len + 1] = NUL;
3449	res[s_len + 2] = NUL;
3450    }
3451    return res;
3452}
3453
3454/*
3455 * Select a directory.
3456 */
3457    char_u *
3458gui_mch_browsedir(char_u *title, char_u *initdir)
3459{
3460    /* We fake this: Use a filter that doesn't select anything and a default
3461     * file name that won't be used. */
3462    return gui_mch_browse(0, title, (char_u *)_("Not Used"), NULL,
3463			      initdir, (char_u *)_("Directory\t*.nothing\n"));
3464}
3465
3466/*
3467 * Pop open a file browser and return the file selected, in allocated memory,
3468 * or NULL if Cancel is hit.
3469 *  saving  - TRUE if the file will be saved to, FALSE if it will be opened.
3470 *  title   - Title message for the file browser dialog.
3471 *  dflt    - Default name of file.
3472 *  ext     - Default extension to be added to files without extensions.
3473 *  initdir - directory in which to open the browser (NULL = current dir)
3474 *  filter  - Filter for matched files to choose from.
3475 *
3476 * Keep in sync with gui_mch_browseW() above!
3477 */
3478    char_u *
3479gui_mch_browse(
3480	int saving,
3481	char_u *title,
3482	char_u *dflt,
3483	char_u *ext,
3484	char_u *initdir,
3485	char_u *filter)
3486{
3487    OPENFILENAME	fileStruct;
3488    char_u		fileBuf[MAXPATHL];
3489    char_u		*initdirp = NULL;
3490    char_u		*filterp;
3491    char_u		*p;
3492
3493# if defined(FEAT_MBYTE) && defined(WIN3264)
3494    if (os_version.dwPlatformId == VER_PLATFORM_WIN32_NT)
3495	return gui_mch_browseW(saving, title, dflt, ext, initdir, filter);
3496# endif
3497
3498    if (dflt == NULL)
3499	fileBuf[0] = NUL;
3500    else
3501	vim_strncpy(fileBuf, dflt, MAXPATHL - 1);
3502
3503    /* Convert the filter to Windows format. */
3504    filterp = convert_filter(filter);
3505
3506    vim_memset(&fileStruct, 0, sizeof(OPENFILENAME));
3507#ifdef OPENFILENAME_SIZE_VERSION_400
3508    /* be compatible with Windows NT 4.0 */
3509    fileStruct.lStructSize = OPENFILENAME_SIZE_VERSION_400;
3510#else
3511    fileStruct.lStructSize = sizeof(fileStruct);
3512#endif
3513
3514    fileStruct.lpstrTitle = title;
3515    fileStruct.lpstrDefExt = ext;
3516
3517    fileStruct.lpstrFile = fileBuf;
3518    fileStruct.nMaxFile = MAXPATHL;
3519    fileStruct.lpstrFilter = filterp;
3520    fileStruct.hwndOwner = s_hwnd;		/* main Vim window is owner*/
3521    /* has an initial dir been specified? */
3522    if (initdir != NULL && *initdir != NUL)
3523    {
3524	/* Must have backslashes here, no matter what 'shellslash' says */
3525	initdirp = vim_strsave(initdir);
3526	if (initdirp != NULL)
3527	    for (p = initdirp; *p != NUL; ++p)
3528		if (*p == '/')
3529		    *p = '\\';
3530	fileStruct.lpstrInitialDir = initdirp;
3531    }
3532
3533    /*
3534     * TODO: Allow selection of multiple files.  Needs another arg to this
3535     * function to ask for it, and need to use OFN_ALLOWMULTISELECT below.
3536     * Also, should we use OFN_FILEMUSTEXIST when opening?  Vim can edit on
3537     * files that don't exist yet, so I haven't put it in.  What about
3538     * OFN_PATHMUSTEXIST?
3539     * Don't use OFN_OVERWRITEPROMPT, Vim has its own ":confirm" dialog.
3540     */
3541    fileStruct.Flags = (OFN_NOCHANGEDIR | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY);
3542#ifdef FEAT_SHORTCUT
3543    if (curbuf->b_p_bin)
3544	fileStruct.Flags |= OFN_NODEREFERENCELINKS;
3545#endif
3546    if (saving)
3547    {
3548	if (!GetSaveFileName(&fileStruct))
3549	    return NULL;
3550    }
3551    else
3552    {
3553	if (!GetOpenFileName(&fileStruct))
3554	    return NULL;
3555    }
3556
3557    vim_free(filterp);
3558    vim_free(initdirp);
3559
3560    /* Give focus back to main window (when using MDI). */
3561    SetFocus(s_hwnd);
3562
3563    /* Shorten the file name if possible */
3564    return vim_strsave(shorten_fname1((char_u *)fileBuf));
3565}
3566#endif /* FEAT_BROWSE */
3567
3568/*ARGSUSED*/
3569    static void
3570_OnDropFiles(
3571    HWND hwnd,
3572    HDROP hDrop)
3573{
3574#ifdef FEAT_WINDOWS
3575#ifdef WIN3264
3576# define BUFPATHLEN _MAX_PATH
3577# define DRAGQVAL 0xFFFFFFFF
3578#else
3579# define BUFPATHLEN MAXPATHL
3580# define DRAGQVAL 0xFFFF
3581#endif
3582#ifdef FEAT_MBYTE
3583    WCHAR   wszFile[BUFPATHLEN];
3584#endif
3585    char    szFile[BUFPATHLEN];
3586    UINT    cFiles = DragQueryFile(hDrop, DRAGQVAL, NULL, 0);
3587    UINT    i;
3588    char_u  **fnames;
3589    POINT   pt;
3590    int_u   modifiers = 0;
3591
3592    /* TRACE("_OnDropFiles: %d files dropped\n", cFiles); */
3593
3594    /* Obtain dropped position */
3595    DragQueryPoint(hDrop, &pt);
3596    MapWindowPoints(s_hwnd, s_textArea, &pt, 1);
3597
3598# ifdef FEAT_VISUAL
3599    reset_VIsual();
3600# endif
3601
3602    fnames = (char_u **)alloc(cFiles * sizeof(char_u *));
3603
3604    if (fnames != NULL)
3605	for (i = 0; i < cFiles; ++i)
3606	{
3607#ifdef FEAT_MBYTE
3608	    if (DragQueryFileW(hDrop, i, wszFile, BUFPATHLEN) > 0)
3609		fnames[i] = utf16_to_enc(wszFile, NULL);
3610	    else
3611#endif
3612	    {
3613		DragQueryFile(hDrop, i, szFile, BUFPATHLEN);
3614		fnames[i] = vim_strsave(szFile);
3615	    }
3616	}
3617
3618    DragFinish(hDrop);
3619
3620    if (fnames != NULL)
3621    {
3622	if ((GetKeyState(VK_SHIFT) & 0x8000) != 0)
3623	    modifiers |= MOUSE_SHIFT;
3624	if ((GetKeyState(VK_CONTROL) & 0x8000) != 0)
3625	    modifiers |= MOUSE_CTRL;
3626	if ((GetKeyState(VK_MENU) & 0x8000) != 0)
3627	    modifiers |= MOUSE_ALT;
3628
3629	gui_handle_drop(pt.x, pt.y, modifiers, fnames, cFiles);
3630
3631	s_need_activate = TRUE;
3632    }
3633#endif
3634}
3635
3636/*ARGSUSED*/
3637    static int
3638_OnScroll(
3639    HWND hwnd,
3640    HWND hwndCtl,
3641    UINT code,
3642    int pos)
3643{
3644    static UINT	prev_code = 0;   /* code of previous call */
3645    scrollbar_T *sb, *sb_info;
3646    long	val;
3647    int		dragging = FALSE;
3648    int		dont_scroll_save = dont_scroll;
3649#ifndef WIN3264
3650    int		nPos;
3651#else
3652    SCROLLINFO	si;
3653
3654    si.cbSize = sizeof(si);
3655    si.fMask = SIF_POS;
3656#endif
3657
3658    sb = gui_mswin_find_scrollbar(hwndCtl);
3659    if (sb == NULL)
3660	return 0;
3661
3662    if (sb->wp != NULL)		/* Left or right scrollbar */
3663    {
3664	/*
3665	 * Careful: need to get scrollbar info out of first (left) scrollbar
3666	 * for window, but keep real scrollbar too because we must pass it to
3667	 * gui_drag_scrollbar().
3668	 */
3669	sb_info = &sb->wp->w_scrollbars[0];
3670    }
3671    else	    /* Bottom scrollbar */
3672	sb_info = sb;
3673    val = sb_info->value;
3674
3675    switch (code)
3676    {
3677	case SB_THUMBTRACK:
3678	    val = pos;
3679	    dragging = TRUE;
3680	    if (sb->scroll_shift > 0)
3681		val <<= sb->scroll_shift;
3682	    break;
3683	case SB_LINEDOWN:
3684	    val++;
3685	    break;
3686	case SB_LINEUP:
3687	    val--;
3688	    break;
3689	case SB_PAGEDOWN:
3690	    val += (sb_info->size > 2 ? sb_info->size - 2 : 1);
3691	    break;
3692	case SB_PAGEUP:
3693	    val -= (sb_info->size > 2 ? sb_info->size - 2 : 1);
3694	    break;
3695	case SB_TOP:
3696	    val = 0;
3697	    break;
3698	case SB_BOTTOM:
3699	    val = sb_info->max;
3700	    break;
3701	case SB_ENDSCROLL:
3702	    if (prev_code == SB_THUMBTRACK)
3703	    {
3704		/*
3705		 * "pos" only gives us 16-bit data.  In case of large file,
3706		 * use GetScrollPos() which returns 32-bit.  Unfortunately it
3707		 * is not valid while the scrollbar is being dragged.
3708		 */
3709		val = GetScrollPos(hwndCtl, SB_CTL);
3710		if (sb->scroll_shift > 0)
3711		    val <<= sb->scroll_shift;
3712	    }
3713	    break;
3714
3715	default:
3716	    /* TRACE("Unknown scrollbar event %d\n", code); */
3717	    return 0;
3718    }
3719    prev_code = code;
3720
3721#ifdef WIN3264
3722    si.nPos = (sb->scroll_shift > 0) ? val >> sb->scroll_shift : val;
3723    SetScrollInfo(hwndCtl, SB_CTL, &si, TRUE);
3724#else
3725    nPos = (sb->scroll_shift > 0) ? val >> sb->scroll_shift : val;
3726    SetScrollPos(hwndCtl, SB_CTL, nPos, TRUE);
3727#endif
3728
3729    /*
3730     * When moving a vertical scrollbar, move the other vertical scrollbar too.
3731     */
3732    if (sb->wp != NULL)
3733    {
3734	scrollbar_T *sba = sb->wp->w_scrollbars;
3735	HWND    id = sba[ (sb == sba + SBAR_LEFT) ? SBAR_RIGHT : SBAR_LEFT].id;
3736
3737#ifdef WIN3264
3738	SetScrollInfo(id, SB_CTL, &si, TRUE);
3739#else
3740	SetScrollPos(id, SB_CTL, nPos, TRUE);
3741#endif
3742    }
3743
3744    /* Don't let us be interrupted here by another message. */
3745    s_busy_processing = TRUE;
3746
3747    /* When "allow_scrollbar" is FALSE still need to remember the new
3748     * position, but don't actually scroll by setting "dont_scroll". */
3749    dont_scroll = !allow_scrollbar;
3750
3751    gui_drag_scrollbar(sb, val, dragging);
3752
3753    s_busy_processing = FALSE;
3754    dont_scroll = dont_scroll_save;
3755
3756    return 0;
3757}
3758
3759
3760/*
3761 * Get command line arguments.
3762 * Use "prog" as the name of the program and "cmdline" as the arguments.
3763 * Copy the arguments to allocated memory.
3764 * Return the number of arguments (including program name).
3765 * Return pointers to the arguments in "argvp".  Memory is allocated with
3766 * malloc(), use free() instead of vim_free().
3767 * Return pointer to buffer in "tofree".
3768 * Returns zero when out of memory.
3769 */
3770/*ARGSUSED*/
3771    int
3772get_cmd_args(char *prog, char *cmdline, char ***argvp, char **tofree)
3773{
3774    int		i;
3775    char	*p;
3776    char	*progp;
3777    char	*pnew = NULL;
3778    char	*newcmdline;
3779    int		inquote;
3780    int		argc;
3781    char	**argv = NULL;
3782    int		round;
3783
3784    *tofree = NULL;
3785
3786#ifdef FEAT_MBYTE
3787    /* Try using the Unicode version first, it takes care of conversion when
3788     * 'encoding' is changed. */
3789    argc = get_cmd_argsW(&argv);
3790    if (argc != 0)
3791	goto done;
3792#endif
3793
3794    /* Handle the program name.  Remove the ".exe" extension, and find the 1st
3795     * non-space. */
3796    p = strrchr(prog, '.');
3797    if (p != NULL)
3798	*p = NUL;
3799    for (progp = prog; *progp == ' '; ++progp)
3800	;
3801
3802    /* The command line is copied to allocated memory, so that we can change
3803     * it.  Add the size of the string, the separating NUL and a terminating
3804     * NUL. */
3805    newcmdline = malloc(STRLEN(cmdline) + STRLEN(progp) + 2);
3806    if (newcmdline == NULL)
3807	return 0;
3808
3809    /*
3810     * First round: count the number of arguments ("pnew" == NULL).
3811     * Second round: produce the arguments.
3812     */
3813    for (round = 1; round <= 2; ++round)
3814    {
3815	/* First argument is the program name. */
3816	if (pnew != NULL)
3817	{
3818	    argv[0] = pnew;
3819	    strcpy(pnew, progp);
3820	    pnew += strlen(pnew);
3821	    *pnew++ = NUL;
3822	}
3823
3824	/*
3825	 * Isolate each argument and put it in argv[].
3826	 */
3827	p = cmdline;
3828	argc = 1;
3829	while (*p != NUL)
3830	{
3831	    inquote = FALSE;
3832	    if (pnew != NULL)
3833		argv[argc] = pnew;
3834	    ++argc;
3835	    while (*p != NUL && (inquote || (*p != ' ' && *p != '\t')))
3836	    {
3837		/* Backslashes are only special when followed by a double
3838		 * quote. */
3839		i = (int)strspn(p, "\\");
3840		if (p[i] == '"')
3841		{
3842		    /* Halve the number of backslashes. */
3843		    if (i > 1 && pnew != NULL)
3844		    {
3845			vim_memset(pnew, '\\', i / 2);
3846			pnew += i / 2;
3847		    }
3848
3849		    /* Even nr of backslashes toggles quoting, uneven copies
3850		     * the double quote. */
3851		    if ((i & 1) == 0)
3852			inquote = !inquote;
3853		    else if (pnew != NULL)
3854			*pnew++ = '"';
3855		    p += i + 1;
3856		}
3857		else if (i > 0)
3858		{
3859		    /* Copy span of backslashes unmodified. */
3860		    if (pnew != NULL)
3861		    {
3862			vim_memset(pnew, '\\', i);
3863			pnew += i;
3864		    }
3865		    p += i;
3866		}
3867		else
3868		{
3869		    if (pnew != NULL)
3870			*pnew++ = *p;
3871#ifdef FEAT_MBYTE
3872		    /* Can't use mb_* functions, because 'encoding' is not
3873		     * initialized yet here. */
3874		    if (IsDBCSLeadByte(*p))
3875		    {
3876			++p;
3877			if (pnew != NULL)
3878			    *pnew++ = *p;
3879		    }
3880#endif
3881		    ++p;
3882		}
3883	    }
3884
3885	    if (pnew != NULL)
3886		*pnew++ = NUL;
3887	    while (*p == ' ' || *p == '\t')
3888		++p;		    /* advance until a non-space */
3889	}
3890
3891	if (round == 1)
3892	{
3893	    argv = (char **)malloc((argc + 1) * sizeof(char *));
3894	    if (argv == NULL )
3895	    {
3896		free(newcmdline);
3897		return 0;		   /* malloc error */
3898	    }
3899	    pnew = newcmdline;
3900	    *tofree = newcmdline;
3901	}
3902    }
3903
3904#ifdef FEAT_MBYTE
3905done:
3906#endif
3907    argv[argc] = NULL;		/* NULL-terminated list */
3908    *argvp = argv;
3909    return argc;
3910}
3911