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