blob: 061319177b222d0f00bfc7d4b6aa788c647753ff [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 Moolenaarb5a7a8b2014-08-06 14:52:30 +020033static DWriteContext *s_dwc = NULL;
34static int s_directx_enabled = 0;
35static int s_directx_load_attempted = 0;
Bram Moolenaar7f88b652017-12-14 13:15:19 +010036# define IS_ENABLE_DIRECTX() (s_directx_enabled && s_dwc != NULL && enc_utf8)
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +010037static int directx_enabled(void);
38static void directx_binddc(void);
Bram Moolenaarb8e0bdb2014-11-12 16:10:48 +010039#endif
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +020040
Bram Moolenaar065bbac2016-02-20 13:08:46 +010041#ifdef FEAT_MENU
42static int gui_mswin_get_menu_height(int fix_window);
K.Takatac81e9bf2022-01-16 14:15:49 +000043#else
44# define gui_mswin_get_menu_height(fix_window) 0
Bram Moolenaar065bbac2016-02-20 13:08:46 +010045#endif
46
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +020047#if defined(FEAT_RENDER_OPTIONS) || defined(PROTO)
48 int
49gui_mch_set_rendering_options(char_u *s)
50{
Bram Moolenaar7f88b652017-12-14 13:15:19 +010051# ifdef FEAT_DIRECTX
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +020052 char_u *p, *q;
53
54 int dx_enable = 0;
55 int dx_flags = 0;
56 float dx_gamma = 0.0f;
57 float dx_contrast = 0.0f;
58 float dx_level = 0.0f;
59 int dx_geom = 0;
60 int dx_renmode = 0;
61 int dx_taamode = 0;
62
Bram Moolenaar734a8672019-12-02 22:49:38 +010063 // parse string as rendering options.
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +020064 for (p = s; p != NULL && *p != NUL; )
65 {
66 char_u item[256];
67 char_u name[128];
68 char_u value[128];
69
Bram Moolenaarcde88542015-08-11 19:14:00 +020070 copy_option_part(&p, item, sizeof(item), ",");
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +020071 if (p == NULL)
72 break;
73 q = &item[0];
74 copy_option_part(&q, name, sizeof(name), ":");
75 if (q == NULL)
76 return FAIL;
77 copy_option_part(&q, value, sizeof(value), ":");
78
79 if (STRCMP(name, "type") == 0)
80 {
81 if (STRCMP(value, "directx") == 0)
82 dx_enable = 1;
83 else
84 return FAIL;
85 }
86 else if (STRCMP(name, "gamma") == 0)
87 {
88 dx_flags |= 1 << 0;
Bram Moolenaar7f0608f2016-02-18 20:46:39 +010089 dx_gamma = (float)atof((char *)value);
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +020090 }
91 else if (STRCMP(name, "contrast") == 0)
92 {
93 dx_flags |= 1 << 1;
Bram Moolenaar7f0608f2016-02-18 20:46:39 +010094 dx_contrast = (float)atof((char *)value);
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +020095 }
96 else if (STRCMP(name, "level") == 0)
97 {
98 dx_flags |= 1 << 2;
Bram Moolenaar7f0608f2016-02-18 20:46:39 +010099 dx_level = (float)atof((char *)value);
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200100 }
101 else if (STRCMP(name, "geom") == 0)
102 {
103 dx_flags |= 1 << 3;
Bram Moolenaar7f0608f2016-02-18 20:46:39 +0100104 dx_geom = atoi((char *)value);
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200105 if (dx_geom < 0 || dx_geom > 2)
106 return FAIL;
107 }
108 else if (STRCMP(name, "renmode") == 0)
109 {
110 dx_flags |= 1 << 4;
Bram Moolenaar7f0608f2016-02-18 20:46:39 +0100111 dx_renmode = atoi((char *)value);
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200112 if (dx_renmode < 0 || dx_renmode > 6)
113 return FAIL;
114 }
115 else if (STRCMP(name, "taamode") == 0)
116 {
117 dx_flags |= 1 << 5;
Bram Moolenaar7f0608f2016-02-18 20:46:39 +0100118 dx_taamode = atoi((char *)value);
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200119 if (dx_taamode < 0 || dx_taamode > 3)
120 return FAIL;
121 }
Bram Moolenaar92467d32017-12-05 13:22:16 +0100122 else if (STRCMP(name, "scrlines") == 0)
123 {
Bram Moolenaar734a8672019-12-02 22:49:38 +0100124 // Deprecated. Simply ignore it.
Bram Moolenaar92467d32017-12-05 13:22:16 +0100125 }
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200126 else
127 return FAIL;
128 }
129
Bram Moolenaar3767c6e2017-12-05 16:57:56 +0100130 if (!gui.in_use)
Bram Moolenaar734a8672019-12-02 22:49:38 +0100131 return OK; // only checking the syntax of the value
Bram Moolenaar3767c6e2017-12-05 16:57:56 +0100132
Bram Moolenaar734a8672019-12-02 22:49:38 +0100133 // Enable DirectX/DirectWrite
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200134 if (dx_enable)
135 {
136 if (!directx_enabled())
137 return FAIL;
138 DWriteContext_SetRenderingParams(s_dwc, NULL);
139 if (dx_flags)
140 {
141 DWriteRenderingParams param;
142 DWriteContext_GetRenderingParams(s_dwc, &param);
143 if (dx_flags & (1 << 0))
144 param.gamma = dx_gamma;
145 if (dx_flags & (1 << 1))
146 param.enhancedContrast = dx_contrast;
147 if (dx_flags & (1 << 2))
148 param.clearTypeLevel = dx_level;
149 if (dx_flags & (1 << 3))
150 param.pixelGeometry = dx_geom;
151 if (dx_flags & (1 << 4))
152 param.renderingMode = dx_renmode;
153 if (dx_flags & (1 << 5))
154 param.textAntialiasMode = dx_taamode;
155 DWriteContext_SetRenderingParams(s_dwc, &param);
156 }
157 }
158 s_directx_enabled = dx_enable;
159
160 return OK;
Bram Moolenaar7f88b652017-12-14 13:15:19 +0100161# else
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200162 return FAIL;
Bram Moolenaar7f88b652017-12-14 13:15:19 +0100163# endif
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200164}
165#endif
166
Bram Moolenaar071d4272004-06-13 20:20:40 +0000167/*
168 * These are new in Windows ME/XP, only defined in recent compilers.
169 */
170#ifndef HANDLE_WM_XBUTTONUP
171# define HANDLE_WM_XBUTTONUP(hwnd, wParam, lParam, fn) \
172 ((fn)((hwnd), (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
173#endif
174#ifndef HANDLE_WM_XBUTTONDOWN
175# define HANDLE_WM_XBUTTONDOWN(hwnd, wParam, lParam, fn) \
176 ((fn)((hwnd), FALSE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
177#endif
178#ifndef HANDLE_WM_XBUTTONDBLCLK
179# define HANDLE_WM_XBUTTONDBLCLK(hwnd, wParam, lParam, fn) \
180 ((fn)((hwnd), TRUE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
181#endif
182
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100183
Bram Moolenaar734a8672019-12-02 22:49:38 +0100184#include "version.h" // used by dialog box routine for default title
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100185#ifdef DEBUG
186# include <tchar.h>
187#endif
188
Bram Moolenaar734a8672019-12-02 22:49:38 +0100189// cproto fails on missing include files
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100190#ifndef PROTO
191
Bram Moolenaar912bc4a2019-12-01 18:58:11 +0100192# ifndef __MINGW32__
193# include <shellapi.h>
194# endif
195# if defined(FEAT_TOOLBAR) || defined(FEAT_BEVAL_GUI) || defined(FEAT_GUI_TABLINE)
196# include <commctrl.h>
197# endif
198# include <windowsx.h>
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100199
Bram Moolenaar734a8672019-12-02 22:49:38 +0100200#endif // PROTO
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100201
202#ifdef FEAT_MENU
Bram Moolenaar734a8672019-12-02 22:49:38 +0100203# define MENUHINTS // show menu hints in command line
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100204#endif
205
Bram Moolenaar734a8672019-12-02 22:49:38 +0100206// Some parameters for dialog boxes. All in pixels.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100207#define DLG_PADDING_X 10
208#define DLG_PADDING_Y 10
Bram Moolenaar734a8672019-12-02 22:49:38 +0100209#define DLG_VERT_PADDING_X 4 // For vertical buttons
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100210#define DLG_VERT_PADDING_Y 4
211#define DLG_ICON_WIDTH 34
212#define DLG_ICON_HEIGHT 34
213#define DLG_MIN_WIDTH 150
K.Takatad1c58992022-01-23 12:31:57 +0000214#define DLG_FONT_NAME "MS Shell Dlg"
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100215#define DLG_FONT_POINT_SIZE 8
216#define DLG_MIN_MAX_WIDTH 400
217#define DLG_MIN_MAX_HEIGHT 400
218
Bram Moolenaar734a8672019-12-02 22:49:38 +0100219#define DLG_NONBUTTON_CONTROL 5000 // First ID of non-button controls
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100220
Bram Moolenaar734a8672019-12-02 22:49:38 +0100221#ifndef WM_XBUTTONDOWN // For Win2K / winME ONLY
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100222# define WM_XBUTTONDOWN 0x020B
223# define WM_XBUTTONUP 0x020C
224# define WM_XBUTTONDBLCLK 0x020D
225# define MK_XBUTTON1 0x0020
226# define MK_XBUTTON2 0x0040
227#endif
228
K.Takatac81e9bf2022-01-16 14:15:49 +0000229#ifndef WM_DPICHANGED
230# define WM_DPICHANGED 0x02E0
231#endif
232
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100233#ifdef PROTO
Bram Moolenaar071d4272004-06-13 20:20:40 +0000234/*
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100235 * Define a few things for generating prototypes. This is just to avoid
236 * syntax errors, the defines do not need to be correct.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000237 */
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100238# define APIENTRY
239# define CALLBACK
240# define CONST
241# define FAR
242# define NEAR
Bram Moolenaar945c8572020-07-17 22:17:03 +0200243# define WINAPI
Bram Moolenaara6b7a082016-08-10 20:53:05 +0200244# undef _cdecl
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100245# define _cdecl
246typedef int BOOL;
247typedef int BYTE;
248typedef int DWORD;
249typedef int WCHAR;
250typedef int ENUMLOGFONT;
251typedef int FINDREPLACE;
252typedef int HANDLE;
253typedef int HBITMAP;
254typedef int HBRUSH;
255typedef int HDROP;
256typedef int INT;
Bram Moolenaar433a5eb2019-03-30 16:24:16 +0100257typedef int LOGFONTW[];
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100258typedef int LPARAM;
259typedef int LPCREATESTRUCT;
260typedef int LPCSTR;
261typedef int LPCTSTR;
262typedef int LPRECT;
263typedef int LPSTR;
264typedef int LPWINDOWPOS;
265typedef int LPWORD;
266typedef int LRESULT;
267typedef int HRESULT;
268# undef MSG
269typedef int MSG;
270typedef int NEWTEXTMETRIC;
271typedef int OSVERSIONINFO;
272typedef int PWORD;
273typedef int RECT;
274typedef int UINT;
275typedef int WORD;
276typedef int WPARAM;
277typedef int POINT;
278typedef void *HINSTANCE;
279typedef void *HMENU;
280typedef void *HWND;
281typedef void *HDC;
282typedef void VOID;
283typedef int LPNMHDR;
284typedef int LONG;
285typedef int WNDPROC;
Bram Moolenaara6b7a082016-08-10 20:53:05 +0200286typedef int UINT_PTR;
Bram Moolenaarb1c91982018-05-17 17:04:55 +0200287typedef int COLORREF;
288typedef int HCURSOR;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100289#endif
290
291#ifndef GET_X_LPARAM
292# define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp))
293#endif
294
K.Takata45f9cfb2022-01-21 11:11:00 +0000295static void _OnPaint(HWND hwnd);
Bram Moolenaar92467d32017-12-05 13:22:16 +0100296static void fill_rect(const RECT *rcp, HBRUSH hbr, COLORREF color);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100297static void clear_rect(RECT *rcp);
298
Bram Moolenaar734a8672019-12-02 22:49:38 +0100299static WORD s_dlgfntheight; // height of the dialog font
300static WORD s_dlgfntwidth; // width of the dialog font
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100301
302#ifdef FEAT_MENU
303static HMENU s_menuBar = NULL;
304#endif
305#ifdef FEAT_TEAROFF
306static void rebuild_tearoff(vimmenu_T *menu);
Bram Moolenaar734a8672019-12-02 22:49:38 +0100307static HBITMAP s_htearbitmap; // bitmap used to indicate tearoff
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100308#endif
309
Bram Moolenaar734a8672019-12-02 22:49:38 +0100310// Flag that is set while processing a message that must not be interrupted by
311// processing another message.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100312static int s_busy_processing = FALSE;
313
Bram Moolenaar734a8672019-12-02 22:49:38 +0100314static int destroying = FALSE; // call DestroyWindow() ourselves
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100315
316#ifdef MSWIN_FIND_REPLACE
K.Takatac81e9bf2022-01-16 14:15:49 +0000317static UINT s_findrep_msg = 0;
Bram Moolenaar0eb035c2019-04-02 22:15:55 +0200318static FINDREPLACEW s_findrep_struct;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100319static HWND s_findrep_hwnd = NULL;
Bram Moolenaar0eb035c2019-04-02 22:15:55 +0200320static int s_findrep_is_find; // TRUE for find dialog, FALSE
321 // for find/replace dialog
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100322#endif
323
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100324HWND s_hwnd = NULL;
325static HDC s_hdc = NULL;
Bram Moolenaarab85ca42019-11-15 22:41:14 +0100326static HBRUSH s_brush = NULL;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100327
328#ifdef FEAT_TOOLBAR
329static HWND s_toolbarhwnd = NULL;
330static WNDPROC s_toolbar_wndproc = NULL;
331#endif
332
333#ifdef FEAT_GUI_TABLINE
334static HWND s_tabhwnd = NULL;
335static WNDPROC s_tabline_wndproc = NULL;
336static int showing_tabline = 0;
337#endif
338
339static WPARAM s_wParam = 0;
340static LPARAM s_lParam = 0;
341
342static HWND s_textArea = NULL;
343static UINT s_uMsg = 0;
344
Bram Moolenaar734a8672019-12-02 22:49:38 +0100345static char_u *s_textfield; // Used by dialogs to pass back strings
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100346
347static int s_need_activate = FALSE;
348
Bram Moolenaar734a8672019-12-02 22:49:38 +0100349// This variable is set when waiting for an event, which is the only moment
350// scrollbar dragging can be done directly. It's not allowed while commands
351// are executed, because it may move the cursor and that may cause unexpected
352// problems (e.g., while ":s" is working).
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100353static int allow_scrollbar = FALSE;
354
K.Takatac81e9bf2022-01-16 14:15:49 +0000355#ifndef _DPI_AWARENESS_CONTEXTS_
356typedef HANDLE DPI_AWARENESS_CONTEXT;
357
358typedef enum DPI_AWARENESS {
K.Takatab0b2b732022-01-19 12:59:21 +0000359 DPI_AWARENESS_INVALID = -1,
360 DPI_AWARENESS_UNAWARE = 0,
361 DPI_AWARENESS_SYSTEM_AWARE = 1,
K.Takatac81e9bf2022-01-16 14:15:49 +0000362 DPI_AWARENESS_PER_MONITOR_AWARE = 2
363} DPI_AWARENESS;
364
K.Takatab0b2b732022-01-19 12:59:21 +0000365# define DPI_AWARENESS_CONTEXT_UNAWARE ((DPI_AWARENESS_CONTEXT)-1)
366# define DPI_AWARENESS_CONTEXT_SYSTEM_AWARE ((DPI_AWARENESS_CONTEXT)-2)
K.Takatac81e9bf2022-01-16 14:15:49 +0000367# define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE ((DPI_AWARENESS_CONTEXT)-3)
368# define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 ((DPI_AWARENESS_CONTEXT)-4)
369# define DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED ((DPI_AWARENESS_CONTEXT)-5)
370#endif
371
372#define DEFAULT_DPI 96
373static int s_dpi = DEFAULT_DPI;
374static BOOL s_in_dpichanged = FALSE;
375static DPI_AWARENESS s_process_dpi_aware = DPI_AWARENESS_INVALID;
376
377static UINT (WINAPI *pGetDpiForSystem)(void) = NULL;
378static UINT (WINAPI *pGetDpiForWindow)(HWND hwnd) = NULL;
379static int (WINAPI *pGetSystemMetricsForDpi)(int, UINT) = NULL;
380//static INT (WINAPI *pGetWindowDpiAwarenessContext)(HWND hwnd) = NULL;
381static DPI_AWARENESS_CONTEXT (WINAPI *pSetThreadDpiAwarenessContext)(DPI_AWARENESS_CONTEXT dpiContext) = NULL;
382static DPI_AWARENESS (WINAPI *pGetAwarenessFromDpiAwarenessContext)(DPI_AWARENESS_CONTEXT) = NULL;
383
384 static UINT WINAPI
385stubGetDpiForSystem(void)
386{
387 HWND hwnd = GetDesktopWindow();
388 HDC hdc = GetWindowDC(hwnd);
389 UINT dpi = GetDeviceCaps(hdc, LOGPIXELSY);
390 ReleaseDC(hwnd, hdc);
391 return dpi;
392}
393
394 static int WINAPI
395stubGetSystemMetricsForDpi(int nIndex, UINT dpi)
396{
397 return GetSystemMetrics(nIndex);
398}
399
400 static int
401adjust_fontsize_by_dpi(int size)
402{
403 return size * s_dpi / (int)pGetDpiForSystem();
404}
405
406 static int
407adjust_by_system_dpi(int size)
408{
409 return size * (int)pGetDpiForSystem() / DEFAULT_DPI;
410}
411
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100412#if defined(FEAT_DIRECTX)
413 static int
414directx_enabled(void)
415{
416 if (s_dwc != NULL)
417 return 1;
418 else if (s_directx_load_attempted)
419 return 0;
Bram Moolenaar734a8672019-12-02 22:49:38 +0100420 // load DirectX
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100421 DWrite_Init();
422 s_directx_load_attempted = 1;
423 s_dwc = DWriteContext_Open();
424 directx_binddc();
425 return s_dwc != NULL ? 1 : 0;
426}
427
428 static void
429directx_binddc(void)
430{
431 if (s_textArea != NULL)
432 {
433 RECT rect;
434 GetClientRect(s_textArea, &rect);
435 DWriteContext_BindDC(s_dwc, s_hdc, &rect);
436 }
437}
438#endif
439
Bram Moolenaar734a8672019-12-02 22:49:38 +0100440extern int current_font_height; // this is in os_mswin.c
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100441
442static struct
443{
444 UINT key_sym;
445 char_u vim_code0;
446 char_u vim_code1;
447} special_keys[] =
448{
449 {VK_UP, 'k', 'u'},
450 {VK_DOWN, 'k', 'd'},
451 {VK_LEFT, 'k', 'l'},
452 {VK_RIGHT, 'k', 'r'},
453
454 {VK_F1, 'k', '1'},
455 {VK_F2, 'k', '2'},
456 {VK_F3, 'k', '3'},
457 {VK_F4, 'k', '4'},
458 {VK_F5, 'k', '5'},
459 {VK_F6, 'k', '6'},
460 {VK_F7, 'k', '7'},
461 {VK_F8, 'k', '8'},
462 {VK_F9, 'k', '9'},
463 {VK_F10, 'k', ';'},
464
465 {VK_F11, 'F', '1'},
466 {VK_F12, 'F', '2'},
467 {VK_F13, 'F', '3'},
468 {VK_F14, 'F', '4'},
469 {VK_F15, 'F', '5'},
470 {VK_F16, 'F', '6'},
471 {VK_F17, 'F', '7'},
472 {VK_F18, 'F', '8'},
473 {VK_F19, 'F', '9'},
474 {VK_F20, 'F', 'A'},
475
476 {VK_F21, 'F', 'B'},
477#ifdef FEAT_NETBEANS_INTG
Bram Moolenaar734a8672019-12-02 22:49:38 +0100478 {VK_PAUSE, 'F', 'B'}, // Pause == F21 (see gui_gtk_x11.c)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100479#endif
480 {VK_F22, 'F', 'C'},
481 {VK_F23, 'F', 'D'},
Bram Moolenaar734a8672019-12-02 22:49:38 +0100482 {VK_F24, 'F', 'E'}, // winuser.h defines up to F24
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100483
484 {VK_HELP, '%', '1'},
485 {VK_BACK, 'k', 'b'},
486 {VK_INSERT, 'k', 'I'},
487 {VK_DELETE, 'k', 'D'},
488 {VK_HOME, 'k', 'h'},
489 {VK_END, '@', '7'},
490 {VK_PRIOR, 'k', 'P'},
491 {VK_NEXT, 'k', 'N'},
492 {VK_PRINT, '%', '9'},
493 {VK_ADD, 'K', '6'},
494 {VK_SUBTRACT, 'K', '7'},
495 {VK_DIVIDE, 'K', '8'},
496 {VK_MULTIPLY, 'K', '9'},
Bram Moolenaar734a8672019-12-02 22:49:38 +0100497 {VK_SEPARATOR, 'K', 'A'}, // Keypad Enter
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100498 {VK_DECIMAL, 'K', 'B'},
499
500 {VK_NUMPAD0, 'K', 'C'},
501 {VK_NUMPAD1, 'K', 'D'},
502 {VK_NUMPAD2, 'K', 'E'},
503 {VK_NUMPAD3, 'K', 'F'},
504 {VK_NUMPAD4, 'K', 'G'},
505 {VK_NUMPAD5, 'K', 'H'},
506 {VK_NUMPAD6, 'K', 'I'},
507 {VK_NUMPAD7, 'K', 'J'},
508 {VK_NUMPAD8, 'K', 'K'},
509 {VK_NUMPAD9, 'K', 'L'},
510
Bram Moolenaar734a8672019-12-02 22:49:38 +0100511 // Keys that we want to be able to use any modifier with:
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100512 {VK_SPACE, ' ', NUL},
513 {VK_TAB, TAB, NUL},
514 {VK_ESCAPE, ESC, NUL},
515 {NL, NL, NUL},
516 {CAR, CAR, NUL},
517
Bram Moolenaar734a8672019-12-02 22:49:38 +0100518 // End of list marker:
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100519 {0, 0, 0}
520};
521
Bram Moolenaar734a8672019-12-02 22:49:38 +0100522// Local variables
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100523static int s_button_pending = -1;
524
Bram Moolenaar734a8672019-12-02 22:49:38 +0100525// s_getting_focus is set when we got focus but didn't see mouse-up event yet,
526// so don't reset s_button_pending.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100527static int s_getting_focus = FALSE;
528
529static int s_x_pending;
530static int s_y_pending;
531static UINT s_kFlags_pending;
Bram Moolenaarf1f2f832018-04-24 16:04:57 +0200532static UINT s_wait_timer = 0; // Timer for get char from user
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100533static int s_timed_out = FALSE;
Bram Moolenaarf1f2f832018-04-24 16:04:57 +0200534static int dead_key = 0; // 0: no dead key, 1: dead key pressed
535static UINT surrogate_pending_ch = 0; // 0: no surrogate pending,
536 // else a high surrogate
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100537
Bram Moolenaarc3719bd2017-11-18 22:13:31 +0100538#ifdef FEAT_BEVAL_GUI
Bram Moolenaar734a8672019-12-02 22:49:38 +0100539// balloon-eval WM_NOTIFY_HANDLER
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100540static void Handle_WM_Notify(HWND hwnd, LPNMHDR pnmh);
541static void TrackUserActivity(UINT uMsg);
542#endif
543
544/*
545 * For control IME.
546 *
Bram Moolenaar433a5eb2019-03-30 16:24:16 +0100547 * These LOGFONTW used for IME.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100548 */
K.Takata4ac893f2022-01-20 12:44:28 +0000549#ifdef FEAT_MBYTE_IME
Bram Moolenaar734a8672019-12-02 22:49:38 +0100550// holds LOGFONTW for 'guifontwide' if available, otherwise 'guifont'
Bram Moolenaar433a5eb2019-03-30 16:24:16 +0100551static LOGFONTW norm_logfont;
Bram Moolenaar734a8672019-12-02 22:49:38 +0100552// holds LOGFONTW for 'guifont' always.
Bram Moolenaar433a5eb2019-03-30 16:24:16 +0100553static LOGFONTW sub_logfont;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100554#endif
555
556#ifdef FEAT_MBYTE_IME
557static LRESULT _OnImeNotify(HWND hWnd, DWORD dwCommand, DWORD dwData);
558#endif
559
560#if defined(FEAT_BROWSE)
561static char_u *convert_filter(char_u *s);
562#endif
563
564#ifdef DEBUG_PRINT_ERROR
565/*
566 * Print out the last Windows error message
567 */
568 static void
569print_windows_error(void)
570{
571 LPVOID lpMsgBuf;
572
573 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
574 NULL, GetLastError(),
575 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
576 (LPTSTR) &lpMsgBuf, 0, NULL);
577 TRACE1("Error: %s\n", lpMsgBuf);
578 LocalFree(lpMsgBuf);
579}
580#endif
581
582/*
583 * Cursor blink functions.
584 *
585 * This is a simple state machine:
586 * BLINK_NONE not blinking at all
587 * BLINK_OFF blinking, cursor is not shown
588 * BLINK_ON blinking, cursor is shown
589 */
590
591#define BLINK_NONE 0
592#define BLINK_OFF 1
593#define BLINK_ON 2
594
595static int blink_state = BLINK_NONE;
596static long_u blink_waittime = 700;
597static long_u blink_ontime = 400;
598static long_u blink_offtime = 250;
599static UINT blink_timer = 0;
600
Bram Moolenaar703a8042016-06-04 16:24:32 +0200601 int
602gui_mch_is_blinking(void)
603{
604 return blink_state != BLINK_NONE;
605}
606
Bram Moolenaar9d5d3c92016-07-07 16:43:02 +0200607 int
608gui_mch_is_blink_off(void)
609{
610 return blink_state == BLINK_OFF;
611}
612
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100613 void
614gui_mch_set_blinking(long wait, long on, long off)
615{
616 blink_waittime = wait;
617 blink_ontime = on;
618 blink_offtime = off;
619}
620
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100621 static VOID CALLBACK
622_OnBlinkTimer(
623 HWND hwnd,
Bram Moolenaar1266d672017-02-01 13:43:36 +0100624 UINT uMsg UNUSED,
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100625 UINT idEvent,
Bram Moolenaar1266d672017-02-01 13:43:36 +0100626 DWORD dwTime UNUSED)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100627{
628 MSG msg;
629
630 /*
631 TRACE2("Got timer event, id %d, blink_timer %d\n", idEvent, blink_timer);
632 */
633
634 KillTimer(NULL, idEvent);
635
Bram Moolenaar734a8672019-12-02 22:49:38 +0100636 // Eat spurious WM_TIMER messages
K.Takatab7057bd2022-01-21 11:37:07 +0000637 while (PeekMessageW(&msg, hwnd, WM_TIMER, WM_TIMER, PM_REMOVE))
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100638 ;
639
640 if (blink_state == BLINK_ON)
641 {
642 gui_undraw_cursor();
643 blink_state = BLINK_OFF;
644 blink_timer = (UINT) SetTimer(NULL, 0, (UINT)blink_offtime,
645 (TIMERPROC)_OnBlinkTimer);
646 }
647 else
648 {
649 gui_update_cursor(TRUE, FALSE);
650 blink_state = BLINK_ON;
651 blink_timer = (UINT) SetTimer(NULL, 0, (UINT)blink_ontime,
Bram Moolenaar1266d672017-02-01 13:43:36 +0100652 (TIMERPROC)_OnBlinkTimer);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100653 }
Bram Moolenaar92467d32017-12-05 13:22:16 +0100654 gui_mch_flush();
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100655}
656
657 static void
658gui_mswin_rm_blink_timer(void)
659{
660 MSG msg;
661
662 if (blink_timer != 0)
663 {
664 KillTimer(NULL, blink_timer);
Bram Moolenaar734a8672019-12-02 22:49:38 +0100665 // Eat spurious WM_TIMER messages
K.Takatab7057bd2022-01-21 11:37:07 +0000666 while (PeekMessageW(&msg, s_hwnd, WM_TIMER, WM_TIMER, PM_REMOVE))
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100667 ;
668 blink_timer = 0;
669 }
670}
671
672/*
673 * Stop the cursor blinking. Show the cursor if it wasn't shown.
674 */
675 void
Bram Moolenaar1dd45fb2018-01-31 21:10:01 +0100676gui_mch_stop_blink(int may_call_gui_update_cursor)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100677{
678 gui_mswin_rm_blink_timer();
Bram Moolenaar1dd45fb2018-01-31 21:10:01 +0100679 if (blink_state == BLINK_OFF && may_call_gui_update_cursor)
Bram Moolenaar92467d32017-12-05 13:22:16 +0100680 {
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100681 gui_update_cursor(TRUE, FALSE);
Bram Moolenaar92467d32017-12-05 13:22:16 +0100682 gui_mch_flush();
683 }
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100684 blink_state = BLINK_NONE;
685}
686
687/*
688 * Start the cursor blinking. If it was already blinking, this restarts the
689 * waiting time and shows the cursor.
690 */
691 void
692gui_mch_start_blink(void)
693{
694 gui_mswin_rm_blink_timer();
695
Bram Moolenaar734a8672019-12-02 22:49:38 +0100696 // Only switch blinking on if none of the times is zero
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100697 if (blink_waittime && blink_ontime && blink_offtime && gui.in_focus)
698 {
699 blink_timer = (UINT)SetTimer(NULL, 0, (UINT)blink_waittime,
700 (TIMERPROC)_OnBlinkTimer);
701 blink_state = BLINK_ON;
702 gui_update_cursor(TRUE, FALSE);
Bram Moolenaar92467d32017-12-05 13:22:16 +0100703 gui_mch_flush();
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100704 }
705}
706
707/*
708 * Call-back routines.
709 */
710
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100711 static VOID CALLBACK
712_OnTimer(
713 HWND hwnd,
Bram Moolenaar1266d672017-02-01 13:43:36 +0100714 UINT uMsg UNUSED,
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100715 UINT idEvent,
Bram Moolenaar1266d672017-02-01 13:43:36 +0100716 DWORD dwTime UNUSED)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100717{
718 MSG msg;
719
720 /*
721 TRACE2("Got timer event, id %d, s_wait_timer %d\n", idEvent, s_wait_timer);
722 */
723 KillTimer(NULL, idEvent);
724 s_timed_out = TRUE;
725
Bram Moolenaar734a8672019-12-02 22:49:38 +0100726 // Eat spurious WM_TIMER messages
K.Takatab7057bd2022-01-21 11:37:07 +0000727 while (PeekMessageW(&msg, hwnd, WM_TIMER, WM_TIMER, PM_REMOVE))
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100728 ;
729 if (idEvent == s_wait_timer)
730 s_wait_timer = 0;
731}
732
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100733 static void
734_OnDeadChar(
Bram Moolenaar1266d672017-02-01 13:43:36 +0100735 HWND hwnd UNUSED,
736 UINT ch UNUSED,
737 int cRepeat UNUSED)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100738{
739 dead_key = 1;
740}
741
742/*
743 * Convert Unicode character "ch" to bytes in "string[slen]".
744 * When "had_alt" is TRUE the ALT key was included in "ch".
745 * Return the length.
Bram Moolenaarf1f2f832018-04-24 16:04:57 +0200746 * Because the Windows API uses UTF-16, we have to deal with surrogate
747 * pairs; this is where we choose to deal with them: if "ch" is a high
748 * surrogate, it will be stored, and the length returned will be zero; the next
749 * char_to_string call will then include the high surrogate, decoding the pair
750 * of UTF-16 code units to a single Unicode code point, presuming it is the
751 * matching low surrogate.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100752 */
753 static int
754char_to_string(int ch, char_u *string, int slen, int had_alt)
755{
756 int len;
757 int i;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100758 WCHAR wstring[2];
Bram Moolenaar945ec092016-06-08 21:17:43 +0200759 char_u *ws = NULL;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100760
Bram Moolenaarf1f2f832018-04-24 16:04:57 +0200761 if (surrogate_pending_ch != 0)
762 {
Bram Moolenaar734a8672019-12-02 22:49:38 +0100763 // We don't guarantee ch is a low surrogate to match the high surrogate
764 // we already have; it should be, but if it isn't, tough luck.
Bram Moolenaarf1f2f832018-04-24 16:04:57 +0200765 wstring[0] = surrogate_pending_ch;
766 wstring[1] = ch;
767 surrogate_pending_ch = 0;
768 len = 2;
769 }
Bram Moolenaar734a8672019-12-02 22:49:38 +0100770 else if (ch >= 0xD800 && ch <= 0xDBFF) // high surrogate
Bram Moolenaarf1f2f832018-04-24 16:04:57 +0200771 {
Bram Moolenaar734a8672019-12-02 22:49:38 +0100772 // We don't have the entire code point yet, only the first UTF-16 code
773 // unit; so just remember it and use it in the next call.
Bram Moolenaarf1f2f832018-04-24 16:04:57 +0200774 surrogate_pending_ch = ch;
775 return 0;
776 }
777 else
778 {
779 wstring[0] = ch;
780 len = 1;
781 }
Bram Moolenaarcea912a2016-10-12 14:20:24 +0200782
Bram Moolenaar734a8672019-12-02 22:49:38 +0100783 // "ch" is a UTF-16 character. Convert it to a string of bytes. When
784 // "enc_codepage" is non-zero use the standard Win32 function,
785 // otherwise use our own conversion function (e.g., for UTF-8).
Bram Moolenaarcea912a2016-10-12 14:20:24 +0200786 if (enc_codepage > 0)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100787 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +0200788 len = WideCharToMultiByte(enc_codepage, 0, wstring, len,
789 (LPSTR)string, slen, 0, NULL);
Bram Moolenaar734a8672019-12-02 22:49:38 +0100790 // If we had included the ALT key into the character but now the
791 // upper bit is no longer set, that probably means the conversion
792 // failed. Convert the original character and set the upper bit
793 // afterwards.
Bram Moolenaarcea912a2016-10-12 14:20:24 +0200794 if (had_alt && len == 1 && ch >= 0x80 && string[0] < 0x80)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100795 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +0200796 wstring[0] = ch & 0x7f;
797 len = WideCharToMultiByte(enc_codepage, 0, wstring, len,
798 (LPSTR)string, slen, 0, NULL);
Bram Moolenaar734a8672019-12-02 22:49:38 +0100799 if (len == 1) // safety check
Bram Moolenaarcea912a2016-10-12 14:20:24 +0200800 string[0] |= 0x80;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100801 }
802 }
803 else
804 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +0200805 ws = utf16_to_enc(wstring, &len);
806 if (ws == NULL)
807 len = 0;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100808 else
809 {
Bram Moolenaar734a8672019-12-02 22:49:38 +0100810 if (len > slen) // just in case
Bram Moolenaarcea912a2016-10-12 14:20:24 +0200811 len = slen;
812 mch_memmove(string, ws, len);
813 vim_free(ws);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100814 }
815 }
816
817 if (len == 0)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100818 {
819 string[0] = ch;
820 len = 1;
821 }
822
823 for (i = 0; i < len; ++i)
824 if (string[i] == CSI && len <= slen - 2)
825 {
Bram Moolenaar734a8672019-12-02 22:49:38 +0100826 // Insert CSI as K_CSI.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100827 mch_memmove(string + i + 3, string + i + 1, len - i - 1);
828 string[++i] = KS_EXTRA;
829 string[++i] = (int)KE_CSI;
830 len += 2;
831 }
832
833 return len;
834}
835
836/*
837 * Key hit, add it to the input buffer.
838 */
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100839 static void
840_OnChar(
Bram Moolenaar1266d672017-02-01 13:43:36 +0100841 HWND hwnd UNUSED,
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100842 UINT ch,
Bram Moolenaar1266d672017-02-01 13:43:36 +0100843 int cRepeat UNUSED)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100844{
845 char_u string[40];
846 int len = 0;
847
848 dead_key = 0;
849
850 len = char_to_string(ch, string, 40, FALSE);
851 if (len == 1 && string[0] == Ctrl_C && ctrl_c_interrupts)
852 {
853 trash_input_buf();
854 got_int = TRUE;
855 }
856
857 add_to_input_buf(string, len);
858}
859
860/*
861 * Alt-Key hit, add it to the input buffer.
862 */
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100863 static void
864_OnSysChar(
Bram Moolenaar1266d672017-02-01 13:43:36 +0100865 HWND hwnd UNUSED,
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100866 UINT cch,
Bram Moolenaar1266d672017-02-01 13:43:36 +0100867 int cRepeat UNUSED)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100868{
Bram Moolenaar734a8672019-12-02 22:49:38 +0100869 char_u string[40]; // Enough for multibyte character
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100870 int len;
871 int modifiers;
Bram Moolenaar734a8672019-12-02 22:49:38 +0100872 int ch = cch; // special keys are negative
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100873
874 dead_key = 0;
875
Bram Moolenaar734a8672019-12-02 22:49:38 +0100876 // TRACE("OnSysChar(%d, %c)\n", ch, ch);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100877
Bram Moolenaar734a8672019-12-02 22:49:38 +0100878 // OK, we have a character key (given by ch) which was entered with the
879 // ALT key pressed. Eg, if the user presses Alt-A, then ch == 'A'. Note
880 // that the system distinguishes Alt-a and Alt-A (Alt-Shift-a unless
881 // CAPSLOCK is pressed) at this point.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100882 modifiers = MOD_MASK_ALT;
883 if (GetKeyState(VK_SHIFT) & 0x8000)
884 modifiers |= MOD_MASK_SHIFT;
885 if (GetKeyState(VK_CONTROL) & 0x8000)
886 modifiers |= MOD_MASK_CTRL;
887
888 ch = simplify_key(ch, &modifiers);
Bram Moolenaar734a8672019-12-02 22:49:38 +0100889 // remove the SHIFT modifier for keys where it's already included, e.g.,
890 // '(' and '*'
Bram Moolenaardaff0fb2020-09-27 13:16:46 +0200891 modifiers = may_remove_shift_modifier(modifiers, ch);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100892
Bram Moolenaarfd615a32020-05-16 14:01:51 +0200893 // Unify modifiers somewhat. No longer use ALT to set the 8th bit.
894 ch = extract_modifiers(ch, &modifiers, FALSE, NULL);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100895 if (ch == CSI)
896 ch = K_CSI;
897
898 len = 0;
899 if (modifiers)
900 {
901 string[len++] = CSI;
902 string[len++] = KS_MODIFIER;
903 string[len++] = modifiers;
904 }
905
906 if (IS_SPECIAL((int)ch))
907 {
908 string[len++] = CSI;
909 string[len++] = K_SECOND((int)ch);
910 string[len++] = K_THIRD((int)ch);
911 }
912 else
913 {
Bram Moolenaar734a8672019-12-02 22:49:38 +0100914 // Although the documentation isn't clear about it, we assume "ch" is
915 // a Unicode character.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100916 len += char_to_string(ch, string + len, 40 - len, TRUE);
917 }
918
919 add_to_input_buf(string, len);
920}
921
922 static void
923_OnMouseEvent(
924 int button,
925 int x,
926 int y,
927 int repeated_click,
928 UINT keyFlags)
929{
930 int vim_modifiers = 0x0;
931
932 s_getting_focus = FALSE;
933
934 if (keyFlags & MK_SHIFT)
935 vim_modifiers |= MOUSE_SHIFT;
936 if (keyFlags & MK_CONTROL)
937 vim_modifiers |= MOUSE_CTRL;
938 if (GetKeyState(VK_MENU) & 0x8000)
939 vim_modifiers |= MOUSE_ALT;
940
941 gui_send_mouse_event(button, x, y, repeated_click, vim_modifiers);
942}
943
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100944 static void
945_OnMouseButtonDown(
Bram Moolenaar1266d672017-02-01 13:43:36 +0100946 HWND hwnd UNUSED,
947 BOOL fDoubleClick UNUSED,
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100948 int x,
949 int y,
950 UINT keyFlags)
951{
952 static LONG s_prevTime = 0;
953
954 LONG currentTime = GetMessageTime();
955 int button = -1;
956 int repeated_click;
957
Bram Moolenaar734a8672019-12-02 22:49:38 +0100958 // Give main window the focus: this is so the cursor isn't hollow.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100959 (void)SetFocus(s_hwnd);
960
961 if (s_uMsg == WM_LBUTTONDOWN || s_uMsg == WM_LBUTTONDBLCLK)
962 button = MOUSE_LEFT;
963 else if (s_uMsg == WM_MBUTTONDOWN || s_uMsg == WM_MBUTTONDBLCLK)
964 button = MOUSE_MIDDLE;
965 else if (s_uMsg == WM_RBUTTONDOWN || s_uMsg == WM_RBUTTONDBLCLK)
966 button = MOUSE_RIGHT;
967 else if (s_uMsg == WM_XBUTTONDOWN || s_uMsg == WM_XBUTTONDBLCLK)
968 {
969#ifndef GET_XBUTTON_WPARAM
970# define GET_XBUTTON_WPARAM(wParam) (HIWORD(wParam))
971#endif
972 button = ((GET_XBUTTON_WPARAM(s_wParam) == 1) ? MOUSE_X1 : MOUSE_X2);
973 }
974 else if (s_uMsg == WM_CAPTURECHANGED)
975 {
Bram Moolenaar734a8672019-12-02 22:49:38 +0100976 // on W95/NT4, somehow you get in here with an odd Msg
977 // if you press one button while holding down the other..
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100978 if (s_button_pending == MOUSE_LEFT)
979 button = MOUSE_RIGHT;
980 else
981 button = MOUSE_LEFT;
982 }
983 if (button >= 0)
984 {
985 repeated_click = ((int)(currentTime - s_prevTime) < p_mouset);
986
987 /*
988 * Holding down the left and right buttons simulates pushing the middle
989 * button.
990 */
991 if (repeated_click
992 && ((button == MOUSE_LEFT && s_button_pending == MOUSE_RIGHT)
993 || (button == MOUSE_RIGHT
994 && s_button_pending == MOUSE_LEFT)))
995 {
996 /*
997 * Hmm, gui.c will ignore more than one button down at a time, so
998 * pretend we let go of it first.
999 */
1000 gui_send_mouse_event(MOUSE_RELEASE, x, y, FALSE, 0x0);
1001 button = MOUSE_MIDDLE;
1002 repeated_click = FALSE;
1003 s_button_pending = -1;
1004 _OnMouseEvent(button, x, y, repeated_click, keyFlags);
1005 }
1006 else if ((repeated_click)
1007 || (mouse_model_popup() && (button == MOUSE_RIGHT)))
1008 {
1009 if (s_button_pending > -1)
1010 {
K.Takata45f9cfb2022-01-21 11:11:00 +00001011 _OnMouseEvent(s_button_pending, x, y, FALSE, keyFlags);
1012 s_button_pending = -1;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001013 }
Bram Moolenaar734a8672019-12-02 22:49:38 +01001014 // TRACE("Button down at x %d, y %d\n", x, y);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001015 _OnMouseEvent(button, x, y, repeated_click, keyFlags);
1016 }
1017 else
1018 {
1019 /*
1020 * If this is the first press (i.e. not a multiple click) don't
1021 * action immediately, but store and wait for:
1022 * i) button-up
1023 * ii) mouse move
1024 * iii) another button press
1025 * before using it.
1026 * This enables us to make left+right simulate middle button,
1027 * without left or right being actioned first. The side-effect is
1028 * that if you click and hold the mouse without dragging, the
1029 * cursor doesn't move until you release the button. In practice
1030 * this is hardly a problem.
1031 */
1032 s_button_pending = button;
1033 s_x_pending = x;
1034 s_y_pending = y;
1035 s_kFlags_pending = keyFlags;
1036 }
1037
1038 s_prevTime = currentTime;
1039 }
1040}
1041
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001042 static void
1043_OnMouseMoveOrRelease(
Bram Moolenaar1266d672017-02-01 13:43:36 +01001044 HWND hwnd UNUSED,
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001045 int x,
1046 int y,
1047 UINT keyFlags)
1048{
1049 int button;
1050
1051 s_getting_focus = FALSE;
1052 if (s_button_pending > -1)
1053 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01001054 // Delayed action for mouse down event
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001055 _OnMouseEvent(s_button_pending, s_x_pending,
1056 s_y_pending, FALSE, s_kFlags_pending);
1057 s_button_pending = -1;
1058 }
1059 if (s_uMsg == WM_MOUSEMOVE)
1060 {
1061 /*
1062 * It's only a MOUSE_DRAG if one or more mouse buttons are being held
1063 * down.
1064 */
1065 if (!(keyFlags & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON
1066 | MK_XBUTTON1 | MK_XBUTTON2)))
1067 {
1068 gui_mouse_moved(x, y);
1069 return;
1070 }
1071
1072 /*
1073 * While button is down, keep grabbing mouse move events when
1074 * the mouse goes outside the window
1075 */
1076 SetCapture(s_textArea);
1077 button = MOUSE_DRAG;
Bram Moolenaar734a8672019-12-02 22:49:38 +01001078 // TRACE(" move at x %d, y %d\n", x, y);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001079 }
1080 else
1081 {
1082 ReleaseCapture();
1083 button = MOUSE_RELEASE;
Bram Moolenaar734a8672019-12-02 22:49:38 +01001084 // TRACE(" up at x %d, y %d\n", x, y);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001085 }
1086
1087 _OnMouseEvent(button, x, y, FALSE, keyFlags);
1088}
1089
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01001090 static void
1091_OnSizeTextArea(
1092 HWND hwnd UNUSED,
1093 UINT state UNUSED,
1094 int cx UNUSED,
1095 int cy UNUSED)
1096{
1097#if defined(FEAT_DIRECTX)
1098 if (IS_ENABLE_DIRECTX())
1099 directx_binddc();
1100#endif
1101}
1102
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001103#ifdef FEAT_MENU
1104/*
1105 * Find the vimmenu_T with the given id
1106 */
1107 static vimmenu_T *
1108gui_mswin_find_menu(
1109 vimmenu_T *pMenu,
1110 int id)
1111{
1112 vimmenu_T *pChildMenu;
1113
1114 while (pMenu)
1115 {
1116 if (pMenu->id == (UINT)id)
1117 break;
1118 if (pMenu->children != NULL)
1119 {
1120 pChildMenu = gui_mswin_find_menu(pMenu->children, id);
1121 if (pChildMenu)
1122 {
1123 pMenu = pChildMenu;
1124 break;
1125 }
1126 }
1127 pMenu = pMenu->next;
1128 }
1129 return pMenu;
1130}
1131
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001132 static void
1133_OnMenu(
Bram Moolenaar1266d672017-02-01 13:43:36 +01001134 HWND hwnd UNUSED,
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001135 int id,
Bram Moolenaar1266d672017-02-01 13:43:36 +01001136 HWND hwndCtl UNUSED,
1137 UINT codeNotify UNUSED)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001138{
1139 vimmenu_T *pMenu;
1140
1141 pMenu = gui_mswin_find_menu(root_menu, id);
1142 if (pMenu)
1143 gui_menu_cb(pMenu);
1144}
1145#endif
1146
1147#ifdef MSWIN_FIND_REPLACE
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001148/*
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001149 * Handle a Find/Replace window message.
1150 */
1151 static void
1152_OnFindRepl(void)
1153{
1154 int flags = 0;
1155 int down;
1156
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001157 if (s_findrep_struct.Flags & FR_DIALOGTERM)
Bram Moolenaar734a8672019-12-02 22:49:38 +01001158 // Give main window the focus back.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001159 (void)SetFocus(s_hwnd);
1160
1161 if (s_findrep_struct.Flags & FR_FINDNEXT)
1162 {
1163 flags = FRD_FINDNEXT;
1164
Bram Moolenaar734a8672019-12-02 22:49:38 +01001165 // Give main window the focus back: this is so the cursor isn't
1166 // hollow.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001167 (void)SetFocus(s_hwnd);
1168 }
1169 else if (s_findrep_struct.Flags & FR_REPLACE)
1170 {
1171 flags = FRD_REPLACE;
1172
Bram Moolenaar734a8672019-12-02 22:49:38 +01001173 // Give main window the focus back: this is so the cursor isn't
1174 // hollow.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001175 (void)SetFocus(s_hwnd);
1176 }
1177 else if (s_findrep_struct.Flags & FR_REPLACEALL)
1178 {
1179 flags = FRD_REPLACEALL;
1180 }
1181
1182 if (flags != 0)
1183 {
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001184 char_u *p, *q;
1185
Bram Moolenaar734a8672019-12-02 22:49:38 +01001186 // Call the generic GUI function to do the actual work.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001187 if (s_findrep_struct.Flags & FR_WHOLEWORD)
1188 flags |= FRD_WHOLE_WORD;
1189 if (s_findrep_struct.Flags & FR_MATCHCASE)
1190 flags |= FRD_MATCH_CASE;
1191 down = (s_findrep_struct.Flags & FR_DOWN) != 0;
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001192 p = utf16_to_enc(s_findrep_struct.lpstrFindWhat, NULL);
1193 q = utf16_to_enc(s_findrep_struct.lpstrReplaceWith, NULL);
1194 if (p != NULL && q != NULL)
1195 gui_do_findrepl(flags, p, q, down);
1196 vim_free(p);
1197 vim_free(q);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001198 }
1199}
1200#endif
1201
1202 static void
1203HandleMouseHide(UINT uMsg, LPARAM lParam)
1204{
1205 static LPARAM last_lParam = 0L;
1206
Bram Moolenaar734a8672019-12-02 22:49:38 +01001207 // We sometimes get a mousemove when the mouse didn't move...
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001208 if (uMsg == WM_MOUSEMOVE || uMsg == WM_NCMOUSEMOVE)
1209 {
1210 if (lParam == last_lParam)
1211 return;
1212 last_lParam = lParam;
1213 }
1214
Bram Moolenaar734a8672019-12-02 22:49:38 +01001215 // Handle specially, to centralise coding. We need to be sure we catch all
1216 // possible events which should cause us to restore the cursor (as it is a
1217 // shared resource, we take full responsibility for it).
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001218 switch (uMsg)
1219 {
1220 case WM_KEYUP:
1221 case WM_CHAR:
1222 /*
1223 * blank out the pointer if necessary
1224 */
1225 if (p_mh)
1226 gui_mch_mousehide(TRUE);
1227 break;
1228
Bram Moolenaar734a8672019-12-02 22:49:38 +01001229 case WM_SYSKEYUP: // show the pointer when a system-key is pressed
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001230 case WM_SYSCHAR:
Bram Moolenaar734a8672019-12-02 22:49:38 +01001231 case WM_MOUSEMOVE: // show the pointer on any mouse action
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001232 case WM_LBUTTONDOWN:
1233 case WM_LBUTTONUP:
1234 case WM_MBUTTONDOWN:
1235 case WM_MBUTTONUP:
1236 case WM_RBUTTONDOWN:
1237 case WM_RBUTTONUP:
1238 case WM_XBUTTONDOWN:
1239 case WM_XBUTTONUP:
1240 case WM_NCMOUSEMOVE:
1241 case WM_NCLBUTTONDOWN:
1242 case WM_NCLBUTTONUP:
1243 case WM_NCMBUTTONDOWN:
1244 case WM_NCMBUTTONUP:
1245 case WM_NCRBUTTONDOWN:
1246 case WM_NCRBUTTONUP:
1247 case WM_KILLFOCUS:
1248 /*
1249 * if the pointer is currently hidden, then we should show it.
1250 */
1251 gui_mch_mousehide(FALSE);
1252 break;
1253 }
1254}
1255
1256 static LRESULT CALLBACK
1257_TextAreaWndProc(
1258 HWND hwnd,
1259 UINT uMsg,
1260 WPARAM wParam,
1261 LPARAM lParam)
1262{
1263 /*
1264 TRACE("TextAreaWndProc: hwnd = %08x, msg = %x, wParam = %x, lParam = %x\n",
1265 hwnd, uMsg, wParam, lParam);
1266 */
1267
1268 HandleMouseHide(uMsg, lParam);
1269
1270 s_uMsg = uMsg;
1271 s_wParam = wParam;
1272 s_lParam = lParam;
1273
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01001274#ifdef FEAT_BEVAL_GUI
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001275 TrackUserActivity(uMsg);
1276#endif
1277
1278 switch (uMsg)
1279 {
1280 HANDLE_MSG(hwnd, WM_LBUTTONDBLCLK,_OnMouseButtonDown);
1281 HANDLE_MSG(hwnd, WM_LBUTTONDOWN,_OnMouseButtonDown);
1282 HANDLE_MSG(hwnd, WM_LBUTTONUP, _OnMouseMoveOrRelease);
1283 HANDLE_MSG(hwnd, WM_MBUTTONDBLCLK,_OnMouseButtonDown);
1284 HANDLE_MSG(hwnd, WM_MBUTTONDOWN,_OnMouseButtonDown);
1285 HANDLE_MSG(hwnd, WM_MBUTTONUP, _OnMouseMoveOrRelease);
1286 HANDLE_MSG(hwnd, WM_MOUSEMOVE, _OnMouseMoveOrRelease);
1287 HANDLE_MSG(hwnd, WM_PAINT, _OnPaint);
1288 HANDLE_MSG(hwnd, WM_RBUTTONDBLCLK,_OnMouseButtonDown);
1289 HANDLE_MSG(hwnd, WM_RBUTTONDOWN,_OnMouseButtonDown);
1290 HANDLE_MSG(hwnd, WM_RBUTTONUP, _OnMouseMoveOrRelease);
1291 HANDLE_MSG(hwnd, WM_XBUTTONDBLCLK,_OnMouseButtonDown);
1292 HANDLE_MSG(hwnd, WM_XBUTTONDOWN,_OnMouseButtonDown);
1293 HANDLE_MSG(hwnd, WM_XBUTTONUP, _OnMouseMoveOrRelease);
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01001294 HANDLE_MSG(hwnd, WM_SIZE, _OnSizeTextArea);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001295
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01001296#ifdef FEAT_BEVAL_GUI
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001297 case WM_NOTIFY: Handle_WM_Notify(hwnd, (LPNMHDR)lParam);
1298 return TRUE;
1299#endif
1300 default:
K.Takata4ac893f2022-01-20 12:44:28 +00001301 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001302 }
1303}
1304
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001305/*
1306 * Called when the foreground or background color has been changed.
1307 */
1308 void
1309gui_mch_new_colors(void)
1310{
Bram Moolenaarab85ca42019-11-15 22:41:14 +01001311 HBRUSH prevBrush;
1312
1313 s_brush = CreateSolidBrush(gui.back_pixel);
Bram Moolenaarab85ca42019-11-15 22:41:14 +01001314 prevBrush = (HBRUSH)SetClassLongPtr(
1315 s_hwnd, GCLP_HBRBACKGROUND, (LONG_PTR)s_brush);
Bram Moolenaarab85ca42019-11-15 22:41:14 +01001316 InvalidateRect(s_hwnd, NULL, TRUE);
1317 DeleteObject(prevBrush);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001318}
1319
1320/*
1321 * Set the colors to their default values.
1322 */
1323 void
1324gui_mch_def_colors(void)
1325{
1326 gui.norm_pixel = GetSysColor(COLOR_WINDOWTEXT);
1327 gui.back_pixel = GetSysColor(COLOR_WINDOW);
1328 gui.def_norm_pixel = gui.norm_pixel;
1329 gui.def_back_pixel = gui.back_pixel;
1330}
1331
1332/*
1333 * Open the GUI window which was created by a call to gui_mch_init().
1334 */
1335 int
1336gui_mch_open(void)
1337{
Bram Moolenaar734a8672019-12-02 22:49:38 +01001338 // Actually open the window, if not already visible
1339 // (may be done already in gui_mch_set_shellsize)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001340 if (!IsWindowVisible(s_hwnd))
1341 ShowWindow(s_hwnd, SW_SHOWDEFAULT);
1342
1343#ifdef MSWIN_FIND_REPLACE
Bram Moolenaar734a8672019-12-02 22:49:38 +01001344 // Init replace string here, so that we keep it when re-opening the
1345 // dialog.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001346 s_findrep_struct.lpstrReplaceWith[0] = NUL;
1347#endif
1348
1349 return OK;
1350}
1351
1352/*
1353 * Get the position of the top left corner of the window.
1354 */
1355 int
1356gui_mch_get_winpos(int *x, int *y)
1357{
1358 RECT rect;
1359
1360 GetWindowRect(s_hwnd, &rect);
1361 *x = rect.left;
1362 *y = rect.top;
1363 return OK;
1364}
1365
1366/*
1367 * Set the position of the top left corner of the window to the given
1368 * coordinates.
1369 */
1370 void
1371gui_mch_set_winpos(int x, int y)
1372{
1373 SetWindowPos(s_hwnd, NULL, x, y, 0, 0,
1374 SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
1375}
1376 void
1377gui_mch_set_text_area_pos(int x, int y, int w, int h)
1378{
1379 static int oldx = 0;
1380 static int oldy = 0;
1381
1382 SetWindowPos(s_textArea, NULL, x, y, w, h, SWP_NOZORDER | SWP_NOACTIVATE);
1383
1384#ifdef FEAT_TOOLBAR
1385 if (vim_strchr(p_go, GO_TOOLBAR) != NULL)
1386 SendMessage(s_toolbarhwnd, WM_SIZE,
K.Takatac81e9bf2022-01-16 14:15:49 +00001387 (WPARAM)0, MAKELPARAM(w, gui.toolbar_height));
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001388#endif
1389#if defined(FEAT_GUI_TABLINE)
1390 if (showing_tabline)
1391 {
1392 int top = 0;
1393 RECT rect;
1394
1395# ifdef FEAT_TOOLBAR
1396 if (vim_strchr(p_go, GO_TOOLBAR) != NULL)
K.Takatac81e9bf2022-01-16 14:15:49 +00001397 top = gui.toolbar_height;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001398# endif
1399 GetClientRect(s_hwnd, &rect);
1400 MoveWindow(s_tabhwnd, 0, top, rect.right, gui.tabline_height, TRUE);
1401 }
1402#endif
1403
Bram Moolenaar734a8672019-12-02 22:49:38 +01001404 // When side scroll bar is unshown, the size of window will change.
1405 // then, the text area move left or right. thus client rect should be
1406 // forcedly redrawn. (Yasuhiro Matsumoto)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001407 if (oldx != x || oldy != y)
1408 {
1409 InvalidateRect(s_hwnd, NULL, FALSE);
1410 oldx = x;
1411 oldy = y;
1412 }
1413}
1414
1415
1416/*
1417 * Scrollbar stuff:
1418 */
1419
1420 void
1421gui_mch_enable_scrollbar(
1422 scrollbar_T *sb,
1423 int flag)
1424{
1425 ShowScrollBar(sb->id, SB_CTL, flag);
1426
Bram Moolenaar734a8672019-12-02 22:49:38 +01001427 // TODO: When the window is maximized, the size of the window stays the
1428 // same, thus the size of the text area changes. On Win98 it's OK, on Win
1429 // NT 4.0 it's not...
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001430}
1431
1432 void
1433gui_mch_set_scrollbar_pos(
1434 scrollbar_T *sb,
1435 int x,
1436 int y,
1437 int w,
1438 int h)
1439{
1440 SetWindowPos(sb->id, NULL, x, y, w, h,
1441 SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW);
1442}
1443
Bram Moolenaar203ec772020-07-17 20:43:43 +02001444 int
1445gui_mch_get_scrollbar_xpadding(void)
1446{
1447 RECT rcTxt, rcWnd;
1448 int xpad;
1449
1450 GetWindowRect(s_textArea, &rcTxt);
1451 GetWindowRect(s_hwnd, &rcWnd);
1452 xpad = rcWnd.right - rcTxt.right - gui.scrollbar_width
K.Takatac81e9bf2022-01-16 14:15:49 +00001453 - pGetSystemMetricsForDpi(SM_CXFRAME, s_dpi)
1454 - pGetSystemMetricsForDpi(SM_CXPADDEDBORDER, s_dpi);
Bram Moolenaar203ec772020-07-17 20:43:43 +02001455 return (xpad < 0) ? 0 : xpad;
1456}
1457
1458 int
1459gui_mch_get_scrollbar_ypadding(void)
1460{
1461 RECT rcTxt, rcWnd;
1462 int ypad;
1463
1464 GetWindowRect(s_textArea, &rcTxt);
1465 GetWindowRect(s_hwnd, &rcWnd);
1466 ypad = rcWnd.bottom - rcTxt.bottom - gui.scrollbar_height
K.Takatac81e9bf2022-01-16 14:15:49 +00001467 - pGetSystemMetricsForDpi(SM_CYFRAME, s_dpi)
1468 - pGetSystemMetricsForDpi(SM_CXPADDEDBORDER, s_dpi);
Bram Moolenaar203ec772020-07-17 20:43:43 +02001469 return (ypad < 0) ? 0 : ypad;
1470}
1471
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001472 void
1473gui_mch_create_scrollbar(
1474 scrollbar_T *sb,
Bram Moolenaar734a8672019-12-02 22:49:38 +01001475 int orient) // SBAR_VERT or SBAR_HORIZ
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001476{
1477 sb->id = CreateWindow(
1478 "SCROLLBAR", "Scrollbar",
1479 WS_CHILD | ((orient == SBAR_VERT) ? SBS_VERT : SBS_HORZ), 0, 0,
Bram Moolenaar734a8672019-12-02 22:49:38 +01001480 10, // Any value will do for now
1481 10, // Any value will do for now
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001482 s_hwnd, NULL,
Bram Moolenaarafde13b2019-04-28 19:46:49 +02001483 g_hinst, NULL);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001484}
1485
1486/*
1487 * Find the scrollbar with the given hwnd.
1488 */
Bram Moolenaar98af99f2020-07-16 22:30:31 +02001489 static scrollbar_T *
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001490gui_mswin_find_scrollbar(HWND hwnd)
1491{
1492 win_T *wp;
1493
1494 if (gui.bottom_sbar.id == hwnd)
1495 return &gui.bottom_sbar;
1496 FOR_ALL_WINDOWS(wp)
1497 {
1498 if (wp->w_scrollbars[SBAR_LEFT].id == hwnd)
1499 return &wp->w_scrollbars[SBAR_LEFT];
1500 if (wp->w_scrollbars[SBAR_RIGHT].id == hwnd)
1501 return &wp->w_scrollbars[SBAR_RIGHT];
1502 }
1503 return NULL;
1504}
1505
K.Takatac81e9bf2022-01-16 14:15:49 +00001506 static void
1507update_scrollbar_size(void)
1508{
1509 gui.scrollbar_width = pGetSystemMetricsForDpi(SM_CXVSCROLL, s_dpi);
1510 gui.scrollbar_height = pGetSystemMetricsForDpi(SM_CYHSCROLL, s_dpi);
1511}
1512
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001513/*
1514 * Get the character size of a font.
1515 */
1516 static void
1517GetFontSize(GuiFont font)
1518{
1519 HWND hwnd = GetDesktopWindow();
1520 HDC hdc = GetWindowDC(hwnd);
1521 HFONT hfntOld = SelectFont(hdc, (HFONT)font);
Bram Moolenaar93d77b22019-05-07 22:52:50 +02001522 SIZE size;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001523 TEXTMETRIC tm;
1524
1525 GetTextMetrics(hdc, &tm);
Bram Moolenaar93d77b22019-05-07 22:52:50 +02001526 // GetTextMetrics() may not return the right value in tmAveCharWidth
1527 // for some fonts. Do our own average computation.
1528 GetTextExtentPoint(hdc,
1529 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
1530 52, &size);
1531 gui.char_width = (size.cx / 26 + 1) / 2 + tm.tmOverhang;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001532
1533 gui.char_height = tm.tmHeight + p_linespace;
1534
1535 SelectFont(hdc, hfntOld);
1536
1537 ReleaseDC(hwnd, hdc);
1538}
1539
1540/*
1541 * Adjust gui.char_height (after 'linespace' was changed).
1542 */
1543 int
1544gui_mch_adjust_charheight(void)
1545{
1546 GetFontSize(gui.norm_font);
1547 return OK;
1548}
1549
1550 static GuiFont
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01001551get_font_handle(LOGFONTW *lf)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001552{
1553 HFONT font = NULL;
1554
Bram Moolenaar734a8672019-12-02 22:49:38 +01001555 // Load the font
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01001556 font = CreateFontIndirectW(lf);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001557
1558 if (font == NULL)
1559 return NOFONT;
1560
1561 return (GuiFont)font;
1562}
1563
1564 static int
1565pixels_to_points(int pixels, int vertical)
1566{
1567 int points;
1568 HWND hwnd;
1569 HDC hdc;
1570
1571 hwnd = GetDesktopWindow();
1572 hdc = GetWindowDC(hwnd);
1573
1574 points = MulDiv(pixels, 72,
1575 GetDeviceCaps(hdc, vertical ? LOGPIXELSY : LOGPIXELSX));
1576
1577 ReleaseDC(hwnd, hdc);
1578
1579 return points;
1580}
1581
1582 GuiFont
1583gui_mch_get_font(
1584 char_u *name,
1585 int giveErrorIfMissing)
1586{
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01001587 LOGFONTW lf;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001588 GuiFont font = NOFONT;
1589
1590 if (get_logfont(&lf, name, NULL, giveErrorIfMissing) == OK)
K.Takatac81e9bf2022-01-16 14:15:49 +00001591 {
1592 lf.lfHeight = adjust_fontsize_by_dpi(lf.lfHeight);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001593 font = get_font_handle(&lf);
K.Takatac81e9bf2022-01-16 14:15:49 +00001594 }
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001595 if (font == NOFONT && giveErrorIfMissing)
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001596 semsg(_(e_unknown_font_str), name);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001597 return font;
1598}
1599
1600#if defined(FEAT_EVAL) || defined(PROTO)
1601/*
1602 * Return the name of font "font" in allocated memory.
1603 * Don't know how to get the actual name, thus use the provided name.
1604 */
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001605 char_u *
Bram Moolenaar1266d672017-02-01 13:43:36 +01001606gui_mch_get_fontname(GuiFont font UNUSED, char_u *name)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001607{
1608 if (name == NULL)
1609 return NULL;
1610 return vim_strsave(name);
1611}
1612#endif
1613
1614 void
1615gui_mch_free_font(GuiFont font)
1616{
1617 if (font)
1618 DeleteObject((HFONT)font);
1619}
1620
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001621/*
1622 * Return the Pixel value (color) for the given color name.
1623 * Return INVALCOLOR for error.
1624 */
1625 guicolor_T
1626gui_mch_get_color(char_u *name)
1627{
Bram Moolenaarc285fe72016-04-26 21:51:48 +02001628 int i;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001629
1630 typedef struct SysColorTable
1631 {
1632 char *name;
1633 int color;
1634 } SysColorTable;
1635
1636 static SysColorTable sys_table[] =
1637 {
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001638 {"SYS_3DDKSHADOW", COLOR_3DDKSHADOW},
1639 {"SYS_3DHILIGHT", COLOR_3DHILIGHT},
Bram Moolenaarcea912a2016-10-12 14:20:24 +02001640#ifdef COLOR_3DHIGHLIGHT
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001641 {"SYS_3DHIGHLIGHT", COLOR_3DHIGHLIGHT},
1642#endif
1643 {"SYS_BTNHILIGHT", COLOR_BTNHILIGHT},
1644 {"SYS_BTNHIGHLIGHT", COLOR_BTNHIGHLIGHT},
1645 {"SYS_3DLIGHT", COLOR_3DLIGHT},
1646 {"SYS_3DSHADOW", COLOR_3DSHADOW},
1647 {"SYS_DESKTOP", COLOR_DESKTOP},
1648 {"SYS_INFOBK", COLOR_INFOBK},
1649 {"SYS_INFOTEXT", COLOR_INFOTEXT},
1650 {"SYS_3DFACE", COLOR_3DFACE},
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001651 {"SYS_BTNFACE", COLOR_BTNFACE},
1652 {"SYS_BTNSHADOW", COLOR_BTNSHADOW},
1653 {"SYS_ACTIVEBORDER", COLOR_ACTIVEBORDER},
1654 {"SYS_ACTIVECAPTION", COLOR_ACTIVECAPTION},
1655 {"SYS_APPWORKSPACE", COLOR_APPWORKSPACE},
1656 {"SYS_BACKGROUND", COLOR_BACKGROUND},
1657 {"SYS_BTNTEXT", COLOR_BTNTEXT},
1658 {"SYS_CAPTIONTEXT", COLOR_CAPTIONTEXT},
1659 {"SYS_GRAYTEXT", COLOR_GRAYTEXT},
1660 {"SYS_HIGHLIGHT", COLOR_HIGHLIGHT},
1661 {"SYS_HIGHLIGHTTEXT", COLOR_HIGHLIGHTTEXT},
1662 {"SYS_INACTIVEBORDER", COLOR_INACTIVEBORDER},
1663 {"SYS_INACTIVECAPTION", COLOR_INACTIVECAPTION},
1664 {"SYS_INACTIVECAPTIONTEXT", COLOR_INACTIVECAPTIONTEXT},
1665 {"SYS_MENU", COLOR_MENU},
1666 {"SYS_MENUTEXT", COLOR_MENUTEXT},
1667 {"SYS_SCROLLBAR", COLOR_SCROLLBAR},
1668 {"SYS_WINDOW", COLOR_WINDOW},
1669 {"SYS_WINDOWFRAME", COLOR_WINDOWFRAME},
1670 {"SYS_WINDOWTEXT", COLOR_WINDOWTEXT}
1671 };
1672
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001673 /*
1674 * Try to look up a system colour.
1675 */
K.Takataeeec2542021-06-02 13:28:16 +02001676 for (i = 0; i < ARRAY_LENGTH(sys_table); i++)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001677 if (STRICMP(name, sys_table[i].name) == 0)
1678 return GetSysColor(sys_table[i].color);
1679
Bram Moolenaarab302212016-04-26 20:59:29 +02001680 return gui_get_color_cmn(name);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001681}
Bram Moolenaarc285fe72016-04-26 21:51:48 +02001682
Bram Moolenaar26af85d2017-07-23 16:45:10 +02001683 guicolor_T
1684gui_mch_get_rgb_color(int r, int g, int b)
1685{
1686 return gui_get_rgb_color_cmn(r, g, b);
1687}
1688
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001689/*
1690 * Return OK if the key with the termcap name "name" is supported.
1691 */
1692 int
1693gui_mch_haskey(char_u *name)
1694{
1695 int i;
1696
1697 for (i = 0; special_keys[i].vim_code1 != NUL; i++)
1698 if (name[0] == special_keys[i].vim_code0 &&
1699 name[1] == special_keys[i].vim_code1)
1700 return OK;
1701 return FAIL;
1702}
1703
1704 void
1705gui_mch_beep(void)
1706{
1707 MessageBeep(MB_OK);
1708}
1709/*
1710 * Invert a rectangle from row r, column c, for nr rows and nc columns.
1711 */
1712 void
1713gui_mch_invert_rectangle(
1714 int r,
1715 int c,
1716 int nr,
1717 int nc)
1718{
1719 RECT rc;
1720
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01001721#if defined(FEAT_DIRECTX)
1722 if (IS_ENABLE_DIRECTX())
1723 DWriteContext_Flush(s_dwc);
1724#endif
1725
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001726 /*
1727 * Note: InvertRect() excludes right and bottom of rectangle.
1728 */
1729 rc.left = FILL_X(c);
1730 rc.top = FILL_Y(r);
1731 rc.right = rc.left + nc * gui.char_width;
1732 rc.bottom = rc.top + nr * gui.char_height;
1733 InvertRect(s_hdc, &rc);
1734}
1735
1736/*
1737 * Iconify the GUI window.
1738 */
1739 void
1740gui_mch_iconify(void)
1741{
1742 ShowWindow(s_hwnd, SW_MINIMIZE);
1743}
1744
1745/*
1746 * Draw a cursor without focus.
1747 */
1748 void
1749gui_mch_draw_hollow_cursor(guicolor_T color)
1750{
1751 HBRUSH hbr;
1752 RECT rc;
1753
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01001754#if defined(FEAT_DIRECTX)
1755 if (IS_ENABLE_DIRECTX())
1756 DWriteContext_Flush(s_dwc);
1757#endif
1758
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001759 /*
1760 * Note: FrameRect() excludes right and bottom of rectangle.
1761 */
1762 rc.left = FILL_X(gui.col);
1763 rc.top = FILL_Y(gui.row);
1764 rc.right = rc.left + gui.char_width;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001765 if (mb_lefthalve(gui.row, gui.col))
1766 rc.right += gui.char_width;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001767 rc.bottom = rc.top + gui.char_height;
1768 hbr = CreateSolidBrush(color);
1769 FrameRect(s_hdc, &rc, hbr);
1770 DeleteBrush(hbr);
1771}
1772/*
1773 * Draw part of a cursor, "w" pixels wide, and "h" pixels high, using
1774 * color "color".
1775 */
1776 void
1777gui_mch_draw_part_cursor(
1778 int w,
1779 int h,
1780 guicolor_T color)
1781{
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001782 RECT rc;
1783
1784 /*
1785 * Note: FillRect() excludes right and bottom of rectangle.
1786 */
1787 rc.left =
1788#ifdef FEAT_RIGHTLEFT
Bram Moolenaar734a8672019-12-02 22:49:38 +01001789 // vertical line should be on the right of current point
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001790 CURSOR_BAR_RIGHT ? FILL_X(gui.col + 1) - w :
1791#endif
1792 FILL_X(gui.col);
1793 rc.top = FILL_Y(gui.row) + gui.char_height - h;
1794 rc.right = rc.left + w;
1795 rc.bottom = rc.top + h;
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01001796
Bram Moolenaar92467d32017-12-05 13:22:16 +01001797 fill_rect(&rc, NULL, color);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001798}
1799
1800
1801/*
1802 * Generates a VK_SPACE when the internal dead_key flag is set to output the
1803 * dead key's nominal character and re-post the original message.
1804 */
1805 static void
1806outputDeadKey_rePost(MSG originalMsg)
1807{
1808 static MSG deadCharExpel;
1809
1810 if (!dead_key)
1811 return;
1812
1813 dead_key = 0;
1814
Bram Moolenaar734a8672019-12-02 22:49:38 +01001815 // Make Windows generate the dead key's character
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001816 deadCharExpel.message = originalMsg.message;
1817 deadCharExpel.hwnd = originalMsg.hwnd;
1818 deadCharExpel.wParam = VK_SPACE;
1819
K.Takata4ac893f2022-01-20 12:44:28 +00001820 TranslateMessage(&deadCharExpel);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001821
Bram Moolenaar734a8672019-12-02 22:49:38 +01001822 // re-generate the current character free of the dead char influence
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001823 PostMessage(originalMsg.hwnd, originalMsg.message, originalMsg.wParam,
1824 originalMsg.lParam);
1825}
1826
1827
1828/*
1829 * Process a single Windows message.
1830 * If one is not available we hang until one is.
1831 */
1832 static void
1833process_message(void)
1834{
1835 MSG msg;
Bram Moolenaar734a8672019-12-02 22:49:38 +01001836 UINT vk = 0; // Virtual key
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001837 char_u string[40];
1838 int i;
1839 int modifiers = 0;
1840 int key;
1841#ifdef FEAT_MENU
1842 static char_u k10[] = {K_SPECIAL, 'k', ';', 0};
1843#endif
1844
K.Takatab7057bd2022-01-21 11:37:07 +00001845 GetMessageW(&msg, NULL, 0, 0);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001846
1847#ifdef FEAT_OLE
Bram Moolenaar734a8672019-12-02 22:49:38 +01001848 // Look after OLE Automation commands
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001849 if (msg.message == WM_OLE)
1850 {
1851 char_u *str = (char_u *)msg.lParam;
1852 if (str == NULL || *str == NUL)
1853 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01001854 // Message can't be ours, forward it. Fixes problem with Ultramon
1855 // 3.0.4
K.Takatab7057bd2022-01-21 11:37:07 +00001856 DispatchMessageW(&msg);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001857 }
1858 else
1859 {
1860 add_to_input_buf(str, (int)STRLEN(str));
Bram Moolenaar734a8672019-12-02 22:49:38 +01001861 vim_free(str); // was allocated in CVim::SendKeys()
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001862 }
1863 return;
1864 }
1865#endif
1866
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001867#ifdef MSWIN_FIND_REPLACE
Bram Moolenaar734a8672019-12-02 22:49:38 +01001868 // Don't process messages used by the dialog
K.Takatab7057bd2022-01-21 11:37:07 +00001869 if (s_findrep_hwnd != NULL && IsDialogMessageW(s_findrep_hwnd, &msg))
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001870 {
1871 HandleMouseHide(msg.message, msg.lParam);
1872 return;
1873 }
1874#endif
1875
1876 /*
1877 * Check if it's a special key that we recognise. If not, call
1878 * TranslateMessage().
1879 */
1880 if (msg.message == WM_KEYDOWN || msg.message == WM_SYSKEYDOWN)
1881 {
1882 vk = (int) msg.wParam;
1883
1884 /*
1885 * Handle dead keys in special conditions in other cases we let Windows
1886 * handle them and do not interfere.
1887 *
1888 * The dead_key flag must be reset on several occasions:
1889 * - in _OnChar() (or _OnSysChar()) as any dead key was necessarily
1890 * consumed at that point (This is when we let Windows combine the
1891 * dead character on its own)
1892 *
1893 * - Before doing something special such as regenerating keypresses to
1894 * expel the dead character as this could trigger an infinite loop if
K.Takata4ac893f2022-01-20 12:44:28 +00001895 * for some reason TranslateMessage() do not trigger a call
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001896 * immediately to _OnChar() (or _OnSysChar()).
1897 */
1898 if (dead_key)
1899 {
1900 /*
1901 * If a dead key was pressed and the user presses VK_SPACE,
1902 * VK_BACK, or VK_ESCAPE it means that he actually wants to deal
1903 * with the dead char now, so do nothing special and let Windows
1904 * handle it.
1905 *
1906 * Note that VK_SPACE combines with the dead_key's character and
1907 * only one WM_CHAR will be generated by TranslateMessage(), in
1908 * the two other cases two WM_CHAR will be generated: the dead
1909 * char and VK_BACK or VK_ESCAPE. That is most likely what the
1910 * user expects.
1911 */
1912 if ((vk == VK_SPACE || vk == VK_BACK || vk == VK_ESCAPE))
1913 {
1914 dead_key = 0;
K.Takata4ac893f2022-01-20 12:44:28 +00001915 TranslateMessage(&msg);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001916 return;
1917 }
Bram Moolenaar734a8672019-12-02 22:49:38 +01001918 // In modes where we are not typing, dead keys should behave
1919 // normally
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001920 else if (!(get_real_state() & (INSERT | CMDLINE | SELECTMODE)))
1921 {
1922 outputDeadKey_rePost(msg);
1923 return;
1924 }
1925 }
1926
Bram Moolenaar734a8672019-12-02 22:49:38 +01001927 // Check for CTRL-BREAK
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001928 if (vk == VK_CANCEL)
1929 {
1930 trash_input_buf();
1931 got_int = TRUE;
Bram Moolenaar9698ad72017-08-12 14:52:15 +02001932 ctrl_break_was_pressed = TRUE;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001933 string[0] = Ctrl_C;
1934 add_to_input_buf(string, 1);
1935 }
1936
1937 for (i = 0; special_keys[i].key_sym != 0; i++)
1938 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01001939 // ignore VK_SPACE when ALT key pressed: system menu
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001940 if (special_keys[i].key_sym == vk
1941 && (vk != VK_SPACE || !(GetKeyState(VK_MENU) & 0x8000)))
1942 {
1943 /*
Bram Moolenaar945ec092016-06-08 21:17:43 +02001944 * Behave as expected if we have a dead key and the special key
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001945 * is a key that would normally trigger the dead key nominal
1946 * character output (such as a NUMPAD printable character or
1947 * the TAB key, etc...).
1948 */
1949 if (dead_key && (special_keys[i].vim_code0 == 'K'
1950 || vk == VK_TAB || vk == CAR))
1951 {
1952 outputDeadKey_rePost(msg);
1953 return;
1954 }
1955
1956#ifdef FEAT_MENU
Bram Moolenaar734a8672019-12-02 22:49:38 +01001957 // Check for <F10>: Windows selects the menu. When <F10> is
1958 // mapped we want to use the mapping instead.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001959 if (vk == VK_F10
1960 && gui.menu_is_active
1961 && check_map(k10, State, FALSE, TRUE, FALSE,
1962 NULL, NULL) == NULL)
1963 break;
1964#endif
1965 if (GetKeyState(VK_SHIFT) & 0x8000)
1966 modifiers |= MOD_MASK_SHIFT;
1967 /*
1968 * Don't use caps-lock as shift, because these are special keys
1969 * being considered here, and we only want letters to get
1970 * shifted -- webb
1971 */
1972 /*
1973 if (GetKeyState(VK_CAPITAL) & 0x0001)
1974 modifiers ^= MOD_MASK_SHIFT;
1975 */
1976 if (GetKeyState(VK_CONTROL) & 0x8000)
1977 modifiers |= MOD_MASK_CTRL;
1978 if (GetKeyState(VK_MENU) & 0x8000)
1979 modifiers |= MOD_MASK_ALT;
1980
1981 if (special_keys[i].vim_code1 == NUL)
1982 key = special_keys[i].vim_code0;
1983 else
1984 key = TO_SPECIAL(special_keys[i].vim_code0,
1985 special_keys[i].vim_code1);
1986 key = simplify_key(key, &modifiers);
1987 if (key == CSI)
1988 key = K_CSI;
1989
1990 if (modifiers)
1991 {
1992 string[0] = CSI;
1993 string[1] = KS_MODIFIER;
1994 string[2] = modifiers;
1995 add_to_input_buf(string, 3);
1996 }
1997
1998 if (IS_SPECIAL(key))
1999 {
2000 string[0] = CSI;
2001 string[1] = K_SECOND(key);
2002 string[2] = K_THIRD(key);
2003 add_to_input_buf(string, 3);
2004 }
2005 else
2006 {
2007 int len;
2008
Bram Moolenaar734a8672019-12-02 22:49:38 +01002009 // Handle "key" as a Unicode character.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002010 len = char_to_string(key, string, 40, FALSE);
2011 add_to_input_buf(string, len);
2012 }
2013 break;
2014 }
2015 }
2016 if (special_keys[i].key_sym == 0)
2017 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01002018 // Some keys need C-S- where they should only need C-.
2019 // Ignore 0xff, Windows XP sends it when NUMLOCK has changed since
2020 // system startup (Helmut Stiegler, 2003 Oct 3).
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002021 if (vk != 0xff
2022 && (GetKeyState(VK_CONTROL) & 0x8000)
2023 && !(GetKeyState(VK_SHIFT) & 0x8000)
2024 && !(GetKeyState(VK_MENU) & 0x8000))
2025 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01002026 // CTRL-6 is '^'; Japanese keyboard maps '^' to vk == 0xDE
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002027 if (vk == '6' || MapVirtualKey(vk, 2) == (UINT)'^')
2028 {
2029 string[0] = Ctrl_HAT;
2030 add_to_input_buf(string, 1);
2031 }
Bram Moolenaar734a8672019-12-02 22:49:38 +01002032 // vk == 0xBD AZERTY for CTRL-'-', but CTRL-[ for * QWERTY!
2033 else if (vk == 0xBD) // QWERTY for CTRL-'-'
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002034 {
2035 string[0] = Ctrl__;
2036 add_to_input_buf(string, 1);
2037 }
Bram Moolenaar734a8672019-12-02 22:49:38 +01002038 // CTRL-2 is '@'; Japanese keyboard maps '@' to vk == 0xC0
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002039 else if (vk == '2' || MapVirtualKey(vk, 2) == (UINT)'@')
2040 {
2041 string[0] = Ctrl_AT;
2042 add_to_input_buf(string, 1);
2043 }
2044 else
K.Takata4ac893f2022-01-20 12:44:28 +00002045 TranslateMessage(&msg);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002046 }
2047 else
K.Takata4ac893f2022-01-20 12:44:28 +00002048 TranslateMessage(&msg);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002049 }
2050 }
2051#ifdef FEAT_MBYTE_IME
2052 else if (msg.message == WM_IME_NOTIFY)
2053 _OnImeNotify(msg.hwnd, (DWORD)msg.wParam, (DWORD)msg.lParam);
2054 else if (msg.message == WM_KEYUP && im_get_status())
Bram Moolenaar734a8672019-12-02 22:49:38 +01002055 // added for non-MS IME (Yasuhiro Matsumoto)
K.Takata4ac893f2022-01-20 12:44:28 +00002056 TranslateMessage(&msg);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002057#endif
2058
2059#ifdef FEAT_MENU
Bram Moolenaar734a8672019-12-02 22:49:38 +01002060 // Check for <F10>: Default effect is to select the menu. When <F10> is
2061 // mapped we need to stop it here to avoid strange effects (e.g., for the
2062 // key-up event)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002063 if (vk != VK_F10 || check_map(k10, State, FALSE, TRUE, FALSE,
2064 NULL, NULL) == NULL)
2065#endif
K.Takatab7057bd2022-01-21 11:37:07 +00002066 DispatchMessageW(&msg);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002067}
2068
2069/*
2070 * Catch up with any queued events. This may put keyboard input into the
2071 * input buffer, call resize call-backs, trigger timers etc. If there is
2072 * nothing in the event queue (& no timers pending), then we return
2073 * immediately.
2074 */
2075 void
2076gui_mch_update(void)
2077{
2078 MSG msg;
2079
2080 if (!s_busy_processing)
K.Takatab7057bd2022-01-21 11:37:07 +00002081 while (PeekMessageW(&msg, NULL, 0, 0, PM_NOREMOVE)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002082 && !vim_is_input_buf_full())
2083 process_message();
2084}
2085
Bram Moolenaar4231da42016-06-02 14:30:04 +02002086 static void
2087remove_any_timer(void)
2088{
2089 MSG msg;
2090
2091 if (s_wait_timer != 0 && !s_timed_out)
2092 {
2093 KillTimer(NULL, s_wait_timer);
2094
Bram Moolenaar734a8672019-12-02 22:49:38 +01002095 // Eat spurious WM_TIMER messages
K.Takatab7057bd2022-01-21 11:37:07 +00002096 while (PeekMessageW(&msg, s_hwnd, WM_TIMER, WM_TIMER, PM_REMOVE))
Bram Moolenaar4231da42016-06-02 14:30:04 +02002097 ;
2098 s_wait_timer = 0;
2099 }
2100}
2101
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002102/*
2103 * GUI input routine called by gui_wait_for_chars(). Waits for a character
2104 * from the keyboard.
2105 * wtime == -1 Wait forever.
2106 * wtime == 0 This should never happen.
2107 * wtime > 0 Wait wtime milliseconds for a character.
2108 * Returns OK if a character was found to be available within the given time,
2109 * or FAIL otherwise.
2110 */
2111 int
2112gui_mch_wait_for_chars(int wtime)
2113{
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002114 int focus;
2115
2116 s_timed_out = FALSE;
2117
Bram Moolenaar12dfc9e2019-01-28 22:32:58 +01002118 if (wtime >= 0)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002119 {
Bram Moolenaar12dfc9e2019-01-28 22:32:58 +01002120 // Don't do anything while processing a (scroll) message.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002121 if (s_busy_processing)
2122 return FAIL;
Bram Moolenaar12dfc9e2019-01-28 22:32:58 +01002123
2124 // When called with "wtime" zero, just want one msec.
2125 s_wait_timer = (UINT)SetTimer(NULL, 0, (UINT)(wtime == 0 ? 1 : wtime),
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002126 (TIMERPROC)_OnTimer);
2127 }
2128
2129 allow_scrollbar = TRUE;
2130
2131 focus = gui.in_focus;
2132 while (!s_timed_out)
2133 {
Bram Moolenaar89c00032019-09-04 13:53:21 +02002134 // Stop or start blinking when focus changes
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002135 if (gui.in_focus != focus)
2136 {
2137 if (gui.in_focus)
2138 gui_mch_start_blink();
2139 else
Bram Moolenaar1dd45fb2018-01-31 21:10:01 +01002140 gui_mch_stop_blink(TRUE);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002141 focus = gui.in_focus;
2142 }
2143
2144 if (s_need_activate)
2145 {
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002146 (void)SetForegroundWindow(s_hwnd);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002147 s_need_activate = FALSE;
2148 }
2149
Bram Moolenaar4231da42016-06-02 14:30:04 +02002150#ifdef FEAT_TIMERS
2151 did_add_timer = FALSE;
2152#endif
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002153#ifdef MESSAGE_QUEUE
Bram Moolenaar89c00032019-09-04 13:53:21 +02002154 // Check channel I/O while waiting for a message.
Bram Moolenaar9186a272016-02-23 19:34:01 +01002155 for (;;)
2156 {
2157 MSG msg;
2158
2159 parse_queued_messages();
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002160# ifdef FEAT_TIMERS
Bram Moolenaar89c00032019-09-04 13:53:21 +02002161 if (did_add_timer)
2162 break;
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002163# endif
K.Takatab7057bd2022-01-21 11:37:07 +00002164 if (PeekMessageW(&msg, NULL, 0, 0, PM_NOREMOVE))
Bram Moolenaar62426e12017-08-13 15:37:58 +02002165 {
2166 process_message();
2167 break;
2168 }
Bram Moolenaar89c00032019-09-04 13:53:21 +02002169 else if (input_available()
Bram Moolenaar032f40a2020-11-18 15:21:50 +01002170 // TODO: The 10 msec is a compromise between laggy response
2171 // and consuming more CPU time. Better would be to handle
2172 // channel messages when they arrive.
2173 || MsgWaitForMultipleObjects(0, NULL, FALSE, 10,
Bram Moolenaar89c00032019-09-04 13:53:21 +02002174 QS_ALLINPUT) != WAIT_TIMEOUT)
Bram Moolenaar9186a272016-02-23 19:34:01 +01002175 break;
2176 }
Bram Moolenaar62426e12017-08-13 15:37:58 +02002177#else
Bram Moolenaar89c00032019-09-04 13:53:21 +02002178 // Don't use gui_mch_update() because then we will spin-lock until a
2179 // char arrives, instead we use GetMessage() to hang until an
2180 // event arrives. No need to check for input_buf_full because we are
2181 // returning as soon as it contains a single char -- webb
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002182 process_message();
Bram Moolenaar62426e12017-08-13 15:37:58 +02002183#endif
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002184
2185 if (input_available())
2186 {
Bram Moolenaar4231da42016-06-02 14:30:04 +02002187 remove_any_timer();
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002188 allow_scrollbar = FALSE;
2189
Bram Moolenaar89c00032019-09-04 13:53:21 +02002190 // Clear pending mouse button, the release event may have been
2191 // taken by the dialog window. But don't do this when getting
2192 // focus, we need the mouse-up event then.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002193 if (!s_getting_focus)
2194 s_button_pending = -1;
2195
2196 return OK;
2197 }
Bram Moolenaar4231da42016-06-02 14:30:04 +02002198
2199#ifdef FEAT_TIMERS
2200 if (did_add_timer)
2201 {
Bram Moolenaar89c00032019-09-04 13:53:21 +02002202 // Need to recompute the waiting time.
Bram Moolenaar4231da42016-06-02 14:30:04 +02002203 remove_any_timer();
2204 break;
2205 }
2206#endif
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002207 }
2208 allow_scrollbar = FALSE;
2209 return FAIL;
2210}
2211
2212/*
2213 * Clear a rectangular region of the screen from text pos (row1, col1) to
2214 * (row2, col2) inclusive.
2215 */
2216 void
2217gui_mch_clear_block(
2218 int row1,
2219 int col1,
2220 int row2,
2221 int col2)
2222{
2223 RECT rc;
2224
2225 /*
2226 * Clear one extra pixel at the far right, for when bold characters have
2227 * spilled over to the window border.
2228 * Note: FillRect() excludes right and bottom of rectangle.
2229 */
2230 rc.left = FILL_X(col1);
2231 rc.top = FILL_Y(row1);
2232 rc.right = FILL_X(col2 + 1) + (col2 == Columns - 1);
2233 rc.bottom = FILL_Y(row2 + 1);
2234 clear_rect(&rc);
2235}
2236
2237/*
2238 * Clear the whole text window.
2239 */
2240 void
2241gui_mch_clear_all(void)
2242{
2243 RECT rc;
2244
2245 rc.left = 0;
2246 rc.top = 0;
2247 rc.right = Columns * gui.char_width + 2 * gui.border_width;
2248 rc.bottom = Rows * gui.char_height + 2 * gui.border_width;
2249 clear_rect(&rc);
2250}
2251/*
2252 * Menu stuff.
2253 */
2254
2255 void
2256gui_mch_enable_menu(int flag)
2257{
2258#ifdef FEAT_MENU
2259 SetMenu(s_hwnd, flag ? s_menuBar : NULL);
2260#endif
2261}
2262
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002263 void
2264gui_mch_set_menu_pos(
Bram Moolenaar1266d672017-02-01 13:43:36 +01002265 int x UNUSED,
2266 int y UNUSED,
2267 int w UNUSED,
2268 int h UNUSED)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002269{
Bram Moolenaar734a8672019-12-02 22:49:38 +01002270 // It will be in the right place anyway
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002271}
2272
2273#if defined(FEAT_MENU) || defined(PROTO)
2274/*
2275 * Make menu item hidden or not hidden
2276 */
2277 void
2278gui_mch_menu_hidden(
2279 vimmenu_T *menu,
2280 int hidden)
2281{
2282 /*
2283 * This doesn't do what we want. Hmm, just grey the menu items for now.
2284 */
2285 /*
2286 if (hidden)
2287 EnableMenuItem(s_menuBar, menu->id, MF_BYCOMMAND | MF_DISABLED);
2288 else
2289 EnableMenuItem(s_menuBar, menu->id, MF_BYCOMMAND | MF_ENABLED);
2290 */
2291 gui_mch_menu_grey(menu, hidden);
2292}
2293
2294/*
2295 * This is called after setting all the menus to grey/hidden or not.
2296 */
2297 void
2298gui_mch_draw_menubar(void)
2299{
2300 DrawMenuBar(s_hwnd);
2301}
Bram Moolenaar734a8672019-12-02 22:49:38 +01002302#endif // FEAT_MENU
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002303
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002304/*
2305 * Return the RGB value of a pixel as a long.
2306 */
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02002307 guicolor_T
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002308gui_mch_get_rgb(guicolor_T pixel)
2309{
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02002310 return (guicolor_T)((GetRValue(pixel) << 16) + (GetGValue(pixel) << 8)
2311 + GetBValue(pixel));
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002312}
2313
2314#if defined(FEAT_GUI_DIALOG) || defined(PROTO)
Bram Moolenaar734a8672019-12-02 22:49:38 +01002315/*
2316 * Convert pixels in X to dialog units
2317 */
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002318 static WORD
2319PixelToDialogX(int numPixels)
2320{
2321 return (WORD)((numPixels * 4) / s_dlgfntwidth);
2322}
2323
Bram Moolenaar734a8672019-12-02 22:49:38 +01002324/*
2325 * Convert pixels in Y to dialog units
2326 */
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002327 static WORD
2328PixelToDialogY(int numPixels)
2329{
2330 return (WORD)((numPixels * 8) / s_dlgfntheight);
2331}
2332
Bram Moolenaar734a8672019-12-02 22:49:38 +01002333/*
2334 * Return the width in pixels of the given text in the given DC.
2335 */
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002336 static int
2337GetTextWidth(HDC hdc, char_u *str, int len)
2338{
2339 SIZE size;
2340
2341 GetTextExtentPoint(hdc, (LPCSTR)str, len, &size);
2342 return size.cx;
2343}
2344
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002345/*
2346 * Return the width in pixels of the given text in the given DC, taking care
2347 * of 'encoding' to active codepage conversion.
2348 */
2349 static int
2350GetTextWidthEnc(HDC hdc, char_u *str, int len)
2351{
2352 SIZE size;
2353 WCHAR *wstr;
2354 int n;
2355 int wlen = len;
2356
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02002357 wstr = enc_to_utf16(str, &wlen);
2358 if (wstr == NULL)
2359 return 0;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002360
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02002361 n = GetTextExtentPointW(hdc, wstr, wlen, &size);
2362 vim_free(wstr);
2363 if (n)
2364 return size.cx;
2365 return 0;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002366}
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002367
Bram Moolenaar87f3d202016-12-01 20:18:50 +01002368static void get_work_area(RECT *spi_rect);
2369
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002370/*
2371 * A quick little routine that will center one window over another, handy for
Bram Moolenaar87f3d202016-12-01 20:18:50 +01002372 * dialog boxes. Taken from the Win32SDK samples and modified for multiple
2373 * monitors.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002374 */
2375 static BOOL
2376CenterWindow(
2377 HWND hwndChild,
2378 HWND hwndParent)
2379{
Bram Moolenaar87f3d202016-12-01 20:18:50 +01002380 HMONITOR mon;
2381 MONITORINFO moninfo;
2382 RECT rChild, rParent, rScreen;
2383 int wChild, hChild, wParent, hParent;
2384 int xNew, yNew;
2385 HDC hdc;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002386
2387 GetWindowRect(hwndChild, &rChild);
2388 wChild = rChild.right - rChild.left;
2389 hChild = rChild.bottom - rChild.top;
2390
Bram Moolenaar734a8672019-12-02 22:49:38 +01002391 // If Vim is minimized put the window in the middle of the screen.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002392 if (hwndParent == NULL || IsMinimized(hwndParent))
Bram Moolenaar87f3d202016-12-01 20:18:50 +01002393 get_work_area(&rParent);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002394 else
2395 GetWindowRect(hwndParent, &rParent);
2396 wParent = rParent.right - rParent.left;
2397 hParent = rParent.bottom - rParent.top;
2398
Bram Moolenaar87f3d202016-12-01 20:18:50 +01002399 moninfo.cbSize = sizeof(MONITORINFO);
2400 mon = MonitorFromWindow(hwndChild, MONITOR_DEFAULTTOPRIMARY);
2401 if (mon != NULL && GetMonitorInfo(mon, &moninfo))
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002402 {
Bram Moolenaar87f3d202016-12-01 20:18:50 +01002403 rScreen = moninfo.rcWork;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002404 }
Bram Moolenaar87f3d202016-12-01 20:18:50 +01002405 else
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002406 {
Bram Moolenaar87f3d202016-12-01 20:18:50 +01002407 hdc = GetDC(hwndChild);
2408 rScreen.left = 0;
2409 rScreen.top = 0;
2410 rScreen.right = GetDeviceCaps(hdc, HORZRES);
2411 rScreen.bottom = GetDeviceCaps(hdc, VERTRES);
2412 ReleaseDC(hwndChild, hdc);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002413 }
2414
Bram Moolenaar87f3d202016-12-01 20:18:50 +01002415 xNew = rParent.left + ((wParent - wChild) / 2);
2416 if (xNew < rScreen.left)
2417 xNew = rScreen.left;
2418 else if ((xNew + wChild) > rScreen.right)
2419 xNew = rScreen.right - wChild;
2420
2421 yNew = rParent.top + ((hParent - hChild) / 2);
2422 if (yNew < rScreen.top)
2423 yNew = rScreen.top;
2424 else if ((yNew + hChild) > rScreen.bottom)
2425 yNew = rScreen.bottom - hChild;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002426
2427 return SetWindowPos(hwndChild, NULL, xNew, yNew, 0, 0,
2428 SWP_NOSIZE | SWP_NOZORDER);
2429}
Bram Moolenaar734a8672019-12-02 22:49:38 +01002430#endif // FEAT_GUI_DIALOG
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002431
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002432#if defined(FEAT_TOOLBAR) || defined(PROTO)
2433 void
2434gui_mch_show_toolbar(int showit)
2435{
2436 if (s_toolbarhwnd == NULL)
2437 return;
2438
2439 if (showit)
2440 {
Bram Moolenaarfc3abf42019-01-24 15:54:21 +01002441# ifndef TB_SETUNICODEFORMAT
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02002442 // For older compilers. We assume this never changes.
Bram Moolenaarfc3abf42019-01-24 15:54:21 +01002443# define TB_SETUNICODEFORMAT 0x2005
2444# endif
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02002445 // Enable unicode support
2446 SendMessage(s_toolbarhwnd, TB_SETUNICODEFORMAT, (WPARAM)TRUE,
2447 (LPARAM)0);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002448 ShowWindow(s_toolbarhwnd, SW_SHOW);
2449 }
2450 else
2451 ShowWindow(s_toolbarhwnd, SW_HIDE);
2452}
2453
Bram Moolenaar734a8672019-12-02 22:49:38 +01002454// The number of bitmaps is fixed. Exit is missing!
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002455# define TOOLBAR_BITMAP_COUNT 31
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002456
2457#endif
2458
2459#if defined(FEAT_GUI_TABLINE) || defined(PROTO)
2460 static void
2461add_tabline_popup_menu_entry(HMENU pmenu, UINT item_id, char_u *item_text)
2462{
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02002463 WCHAR *wn;
2464 MENUITEMINFOW infow;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002465
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02002466 wn = enc_to_utf16(item_text, NULL);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002467 if (wn == NULL)
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02002468 return;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002469
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02002470 infow.cbSize = sizeof(infow);
2471 infow.fMask = MIIM_TYPE | MIIM_ID;
2472 infow.wID = item_id;
2473 infow.fType = MFT_STRING;
2474 infow.dwTypeData = wn;
2475 infow.cch = (UINT)wcslen(wn);
2476 InsertMenuItemW(pmenu, item_id, FALSE, &infow);
2477 vim_free(wn);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002478}
2479
2480 static void
2481show_tabline_popup_menu(void)
2482{
2483 HMENU tab_pmenu;
2484 long rval;
2485 POINT pt;
2486
Bram Moolenaar734a8672019-12-02 22:49:38 +01002487 // When ignoring events don't show the menu.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002488 if (hold_gui_events
2489# ifdef FEAT_CMDWIN
2490 || cmdwin_type != 0
2491# endif
2492 )
2493 return;
2494
2495 tab_pmenu = CreatePopupMenu();
2496 if (tab_pmenu == NULL)
2497 return;
2498
2499 if (first_tabpage->tp_next != NULL)
2500 add_tabline_popup_menu_entry(tab_pmenu,
2501 TABLINE_MENU_CLOSE, (char_u *)_("Close tab"));
2502 add_tabline_popup_menu_entry(tab_pmenu,
2503 TABLINE_MENU_NEW, (char_u *)_("New tab"));
2504 add_tabline_popup_menu_entry(tab_pmenu,
2505 TABLINE_MENU_OPEN, (char_u *)_("Open tab..."));
2506
2507 GetCursorPos(&pt);
2508 rval = TrackPopupMenuEx(tab_pmenu, TPM_RETURNCMD, pt.x, pt.y, s_tabhwnd,
2509 NULL);
2510
2511 DestroyMenu(tab_pmenu);
2512
Bram Moolenaar734a8672019-12-02 22:49:38 +01002513 // Add the string cmd into input buffer
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002514 if (rval > 0)
2515 {
2516 TCHITTESTINFO htinfo;
2517 int idx;
2518
2519 if (ScreenToClient(s_tabhwnd, &pt) == 0)
2520 return;
2521
2522 htinfo.pt.x = pt.x;
2523 htinfo.pt.y = pt.y;
2524 idx = TabCtrl_HitTest(s_tabhwnd, &htinfo);
2525 if (idx == -1)
2526 idx = 0;
2527 else
2528 idx += 1;
2529
2530 send_tabline_menu_event(idx, (int)rval);
2531 }
2532}
2533
2534/*
2535 * Show or hide the tabline.
2536 */
2537 void
2538gui_mch_show_tabline(int showit)
2539{
2540 if (s_tabhwnd == NULL)
2541 return;
2542
2543 if (!showit != !showing_tabline)
2544 {
2545 if (showit)
2546 ShowWindow(s_tabhwnd, SW_SHOW);
2547 else
2548 ShowWindow(s_tabhwnd, SW_HIDE);
2549 showing_tabline = showit;
2550 }
2551}
2552
2553/*
2554 * Return TRUE when tabline is displayed.
2555 */
2556 int
2557gui_mch_showing_tabline(void)
2558{
2559 return s_tabhwnd != NULL && showing_tabline;
2560}
2561
2562/*
2563 * Update the labels of the tabline.
2564 */
2565 void
2566gui_mch_update_tabline(void)
2567{
2568 tabpage_T *tp;
2569 TCITEM tie;
2570 int nr = 0;
2571 int curtabidx = 0;
2572 int tabadded = 0;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002573 WCHAR *wstr = NULL;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002574
2575 if (s_tabhwnd == NULL)
2576 return;
2577
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002578# ifndef CCM_SETUNICODEFORMAT
Bram Moolenaar734a8672019-12-02 22:49:38 +01002579 // For older compilers. We assume this never changes.
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002580# define CCM_SETUNICODEFORMAT 0x2005
2581# endif
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02002582 // Enable unicode support
2583 SendMessage(s_tabhwnd, CCM_SETUNICODEFORMAT, (WPARAM)TRUE, (LPARAM)0);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002584
2585 tie.mask = TCIF_TEXT;
2586 tie.iImage = -1;
2587
Bram Moolenaar734a8672019-12-02 22:49:38 +01002588 // Disable redraw for tab updates to eliminate O(N^2) draws.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002589 SendMessage(s_tabhwnd, WM_SETREDRAW, (WPARAM)FALSE, 0);
2590
Bram Moolenaar734a8672019-12-02 22:49:38 +01002591 // Add a label for each tab page. They all contain the same text area.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002592 for (tp = first_tabpage; tp != NULL; tp = tp->tp_next, ++nr)
2593 {
2594 if (tp == curtab)
2595 curtabidx = nr;
2596
2597 if (nr >= TabCtrl_GetItemCount(s_tabhwnd))
2598 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01002599 // Add the tab
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002600 tie.pszText = "-Empty-";
2601 TabCtrl_InsertItem(s_tabhwnd, nr, &tie);
2602 tabadded = 1;
2603 }
2604
2605 get_tabline_label(tp, FALSE);
2606 tie.pszText = (LPSTR)NameBuff;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002607
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02002608 wstr = enc_to_utf16(NameBuff, NULL);
2609 if (wstr != NULL)
2610 {
2611 TCITEMW tiw;
2612
2613 tiw.mask = TCIF_TEXT;
2614 tiw.iImage = -1;
2615 tiw.pszText = wstr;
2616 SendMessage(s_tabhwnd, TCM_SETITEMW, (WPARAM)nr, (LPARAM)&tiw);
2617 vim_free(wstr);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002618 }
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002619 }
2620
Bram Moolenaar734a8672019-12-02 22:49:38 +01002621 // Remove any old labels.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002622 while (nr < TabCtrl_GetItemCount(s_tabhwnd))
2623 TabCtrl_DeleteItem(s_tabhwnd, nr);
2624
2625 if (!tabadded && TabCtrl_GetCurSel(s_tabhwnd) != curtabidx)
2626 TabCtrl_SetCurSel(s_tabhwnd, curtabidx);
2627
Bram Moolenaar734a8672019-12-02 22:49:38 +01002628 // Re-enable redraw and redraw.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002629 SendMessage(s_tabhwnd, WM_SETREDRAW, (WPARAM)TRUE, 0);
2630 RedrawWindow(s_tabhwnd, NULL, NULL,
2631 RDW_ERASE | RDW_FRAME | RDW_INVALIDATE | RDW_ALLCHILDREN);
2632
2633 if (tabadded && TabCtrl_GetCurSel(s_tabhwnd) != curtabidx)
2634 TabCtrl_SetCurSel(s_tabhwnd, curtabidx);
2635}
2636
2637/*
2638 * Set the current tab to "nr". First tab is 1.
2639 */
2640 void
2641gui_mch_set_curtab(int nr)
2642{
2643 if (s_tabhwnd == NULL)
2644 return;
2645
2646 if (TabCtrl_GetCurSel(s_tabhwnd) != nr - 1)
2647 TabCtrl_SetCurSel(s_tabhwnd, nr - 1);
2648}
2649
2650#endif
2651
2652/*
2653 * ":simalt" command.
2654 */
2655 void
2656ex_simalt(exarg_T *eap)
2657{
Bram Moolenaar7a85b0f2017-04-22 15:17:40 +02002658 char_u *keys = eap->arg;
2659 int fill_typebuf = FALSE;
2660 char_u key_name[4];
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002661
2662 PostMessage(s_hwnd, WM_SYSCOMMAND, (WPARAM)SC_KEYMENU, (LPARAM)0);
2663 while (*keys)
2664 {
2665 if (*keys == '~')
Bram Moolenaar734a8672019-12-02 22:49:38 +01002666 *keys = ' '; // for showing system menu
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002667 PostMessage(s_hwnd, WM_CHAR, (WPARAM)*keys, (LPARAM)0);
2668 keys++;
Bram Moolenaar7a85b0f2017-04-22 15:17:40 +02002669 fill_typebuf = TRUE;
2670 }
2671 if (fill_typebuf)
2672 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01002673 // Put a NOP in the typeahead buffer so that the message will get
2674 // processed.
Bram Moolenaar7a85b0f2017-04-22 15:17:40 +02002675 key_name[0] = K_SPECIAL;
2676 key_name[1] = KS_EXTRA;
Bram Moolenaara21ccb72017-04-29 17:40:22 +02002677 key_name[2] = KE_NOP;
Bram Moolenaar7a85b0f2017-04-22 15:17:40 +02002678 key_name[3] = NUL;
Bram Moolenaar93bbf332019-10-23 21:43:16 +02002679#if defined(FEAT_CLIENTSERVER) || defined(FEAT_EVAL)
Bram Moolenaar7a85b0f2017-04-22 15:17:40 +02002680 typebuf_was_filled = TRUE;
Bram Moolenaar93bbf332019-10-23 21:43:16 +02002681#endif
Bram Moolenaar7a85b0f2017-04-22 15:17:40 +02002682 (void)ins_typebuf(key_name, REMAP_NONE, 0, TRUE, FALSE);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002683 }
2684}
2685
2686/*
2687 * Create the find & replace dialogs.
2688 * You can't have both at once: ":find" when replace is showing, destroys
2689 * the replace dialog first, and the other way around.
2690 */
2691#ifdef MSWIN_FIND_REPLACE
2692 static void
2693initialise_findrep(char_u *initial_string)
2694{
2695 int wword = FALSE;
2696 int mcase = !p_ic;
2697 char_u *entry_text;
2698
Bram Moolenaar734a8672019-12-02 22:49:38 +01002699 // Get the search string to use.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002700 entry_text = get_find_dialog_text(initial_string, &wword, &mcase);
2701
2702 s_findrep_struct.hwndOwner = s_hwnd;
2703 s_findrep_struct.Flags = FR_DOWN;
2704 if (mcase)
2705 s_findrep_struct.Flags |= FR_MATCHCASE;
2706 if (wword)
2707 s_findrep_struct.Flags |= FR_WHOLEWORD;
2708 if (entry_text != NULL && *entry_text != NUL)
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02002709 {
2710 WCHAR *p = enc_to_utf16(entry_text, NULL);
2711 if (p != NULL)
2712 {
2713 int len = s_findrep_struct.wFindWhatLen - 1;
2714
2715 wcsncpy(s_findrep_struct.lpstrFindWhat, p, len);
2716 s_findrep_struct.lpstrFindWhat[len] = NUL;
2717 vim_free(p);
2718 }
2719 }
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002720 vim_free(entry_text);
2721}
2722#endif
2723
2724 static void
2725set_window_title(HWND hwnd, char *title)
2726{
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02002727 if (title != NULL)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002728 {
2729 WCHAR *wbuf;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002730
Bram Moolenaar734a8672019-12-02 22:49:38 +01002731 // Convert the title from 'encoding' to UTF-16.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002732 wbuf = (WCHAR *)enc_to_utf16((char_u *)title, NULL);
2733 if (wbuf != NULL)
2734 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +02002735 SetWindowTextW(hwnd, wbuf);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002736 vim_free(wbuf);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002737 }
2738 }
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02002739 else
2740 (void)SetWindowTextW(hwnd, NULL);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002741}
2742
2743 void
2744gui_mch_find_dialog(exarg_T *eap)
2745{
2746#ifdef MSWIN_FIND_REPLACE
2747 if (s_findrep_msg != 0)
2748 {
2749 if (IsWindow(s_findrep_hwnd) && !s_findrep_is_find)
2750 DestroyWindow(s_findrep_hwnd);
2751
2752 if (!IsWindow(s_findrep_hwnd))
2753 {
2754 initialise_findrep(eap->arg);
K.Takata45f9cfb2022-01-21 11:11:00 +00002755 s_findrep_hwnd = FindTextW(&s_findrep_struct);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002756 }
2757
Bram Moolenaar9e42c862018-07-20 05:03:16 +02002758 set_window_title(s_findrep_hwnd, _("Find string"));
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002759 (void)SetFocus(s_findrep_hwnd);
2760
2761 s_findrep_is_find = TRUE;
2762 }
2763#endif
2764}
2765
2766
2767 void
2768gui_mch_replace_dialog(exarg_T *eap)
2769{
2770#ifdef MSWIN_FIND_REPLACE
2771 if (s_findrep_msg != 0)
2772 {
2773 if (IsWindow(s_findrep_hwnd) && s_findrep_is_find)
2774 DestroyWindow(s_findrep_hwnd);
2775
2776 if (!IsWindow(s_findrep_hwnd))
2777 {
2778 initialise_findrep(eap->arg);
K.Takata45f9cfb2022-01-21 11:11:00 +00002779 s_findrep_hwnd = ReplaceTextW(&s_findrep_struct);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002780 }
2781
Bram Moolenaar9e42c862018-07-20 05:03:16 +02002782 set_window_title(s_findrep_hwnd, _("Find & Replace"));
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002783 (void)SetFocus(s_findrep_hwnd);
2784
2785 s_findrep_is_find = FALSE;
2786 }
2787#endif
2788}
2789
2790
2791/*
2792 * Set visibility of the pointer.
2793 */
2794 void
2795gui_mch_mousehide(int hide)
2796{
2797 if (hide != gui.pointer_hidden)
2798 {
2799 ShowCursor(!hide);
2800 gui.pointer_hidden = hide;
2801 }
2802}
2803
2804#ifdef FEAT_MENU
2805 static void
2806gui_mch_show_popupmenu_at(vimmenu_T *menu, int x, int y)
2807{
Bram Moolenaar734a8672019-12-02 22:49:38 +01002808 // Unhide the mouse, we don't get move events here.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002809 gui_mch_mousehide(FALSE);
2810
2811 (void)TrackPopupMenu(
2812 (HMENU)menu->submenu_id,
2813 TPM_LEFTALIGN | TPM_LEFTBUTTON,
2814 x, y,
Bram Moolenaar734a8672019-12-02 22:49:38 +01002815 (int)0, //reserved param
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002816 s_hwnd,
2817 NULL);
2818 /*
2819 * NOTE: The pop-up menu can eat the mouse up event.
2820 * We deal with this in normal.c.
2821 */
2822}
2823#endif
2824
2825/*
2826 * Got a message when the system will go down.
2827 */
2828 static void
2829_OnEndSession(void)
2830{
2831 getout_preserve_modified(1);
2832}
2833
2834/*
2835 * Get this message when the user clicks on the cross in the top right corner
2836 * of a Windows95 window.
2837 */
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002838 static void
Bram Moolenaar1266d672017-02-01 13:43:36 +01002839_OnClose(HWND hwnd UNUSED)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002840{
2841 gui_shell_closed();
2842}
2843
2844/*
2845 * Get a message when the window is being destroyed.
2846 */
2847 static void
Bram Moolenaar1266d672017-02-01 13:43:36 +01002848_OnDestroy(HWND hwnd)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002849{
2850 if (!destroying)
2851 _OnClose(hwnd);
2852}
2853
2854 static void
2855_OnPaint(
2856 HWND hwnd)
2857{
2858 if (!IsMinimized(hwnd))
2859 {
2860 PAINTSTRUCT ps;
2861
Bram Moolenaar734a8672019-12-02 22:49:38 +01002862 out_flush(); // make sure all output has been processed
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002863 (void)BeginPaint(hwnd, &ps);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002864
Bram Moolenaar734a8672019-12-02 22:49:38 +01002865 // prevent multi-byte characters from misprinting on an invalid
2866 // rectangle
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002867 if (has_mbyte)
2868 {
2869 RECT rect;
2870
2871 GetClientRect(hwnd, &rect);
2872 ps.rcPaint.left = rect.left;
2873 ps.rcPaint.right = rect.right;
2874 }
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002875
2876 if (!IsRectEmpty(&ps.rcPaint))
2877 {
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002878 gui_redraw(ps.rcPaint.left, ps.rcPaint.top,
2879 ps.rcPaint.right - ps.rcPaint.left + 1,
2880 ps.rcPaint.bottom - ps.rcPaint.top + 1);
2881 }
2882
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002883 EndPaint(hwnd, &ps);
2884 }
2885}
2886
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002887 static void
2888_OnSize(
2889 HWND hwnd,
Bram Moolenaar1266d672017-02-01 13:43:36 +01002890 UINT state UNUSED,
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002891 int cx,
2892 int cy)
2893{
K.Takatac81e9bf2022-01-16 14:15:49 +00002894 if (!IsMinimized(hwnd) && !s_in_dpichanged)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002895 {
2896 gui_resize_shell(cx, cy);
2897
Bram Moolenaar734a8672019-12-02 22:49:38 +01002898 // Menu bar may wrap differently now
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002899 gui_mswin_get_menu_height(TRUE);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002900 }
2901}
2902
2903 static void
2904_OnSetFocus(
2905 HWND hwnd,
2906 HWND hwndOldFocus)
2907{
2908 gui_focus_change(TRUE);
2909 s_getting_focus = TRUE;
K.Takata4ac893f2022-01-20 12:44:28 +00002910 (void)DefWindowProcW(hwnd, WM_SETFOCUS, (WPARAM)hwndOldFocus, 0);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002911}
2912
2913 static void
2914_OnKillFocus(
2915 HWND hwnd,
2916 HWND hwndNewFocus)
2917{
2918 gui_focus_change(FALSE);
2919 s_getting_focus = FALSE;
K.Takata4ac893f2022-01-20 12:44:28 +00002920 (void)DefWindowProcW(hwnd, WM_KILLFOCUS, (WPARAM)hwndNewFocus, 0);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002921}
2922
2923/*
2924 * Get a message when the user switches back to vim
2925 */
2926 static LRESULT
2927_OnActivateApp(
2928 HWND hwnd,
2929 BOOL fActivate,
2930 DWORD dwThreadId)
2931{
Bram Moolenaar734a8672019-12-02 22:49:38 +01002932 // we call gui_focus_change() in _OnSetFocus()
2933 // gui_focus_change((int)fActivate);
K.Takata4ac893f2022-01-20 12:44:28 +00002934 return DefWindowProcW(hwnd, WM_ACTIVATEAPP, fActivate, (DWORD)dwThreadId);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002935}
2936
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002937 void
2938gui_mch_destroy_scrollbar(scrollbar_T *sb)
2939{
2940 DestroyWindow(sb->id);
2941}
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002942
2943/*
2944 * Get current mouse coordinates in text window.
2945 */
2946 void
2947gui_mch_getmouse(int *x, int *y)
2948{
2949 RECT rct;
2950 POINT mp;
2951
2952 (void)GetWindowRect(s_textArea, &rct);
K.Takata45f9cfb2022-01-21 11:11:00 +00002953 (void)GetCursorPos(&mp);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002954 *x = (int)(mp.x - rct.left);
2955 *y = (int)(mp.y - rct.top);
2956}
2957
2958/*
2959 * Move mouse pointer to character at (x, y).
2960 */
2961 void
2962gui_mch_setmouse(int x, int y)
2963{
2964 RECT rct;
2965
2966 (void)GetWindowRect(s_textArea, &rct);
2967 (void)SetCursorPos(x + gui.border_offset + rct.left,
2968 y + gui.border_offset + rct.top);
2969}
2970
2971 static void
2972gui_mswin_get_valid_dimensions(
2973 int w,
2974 int h,
2975 int *valid_w,
K.Takata4f2417f2021-06-05 16:25:32 +02002976 int *valid_h,
2977 int *cols,
2978 int *rows)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002979{
2980 int base_width, base_height;
2981
2982 base_width = gui_get_base_width()
K.Takatac81e9bf2022-01-16 14:15:49 +00002983 + (pGetSystemMetricsForDpi(SM_CXFRAME, s_dpi) +
2984 pGetSystemMetricsForDpi(SM_CXPADDEDBORDER, s_dpi)) * 2;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002985 base_height = gui_get_base_height()
K.Takatac81e9bf2022-01-16 14:15:49 +00002986 + (pGetSystemMetricsForDpi(SM_CYFRAME, s_dpi) +
2987 pGetSystemMetricsForDpi(SM_CXPADDEDBORDER, s_dpi)) * 2
2988 + pGetSystemMetricsForDpi(SM_CYCAPTION, s_dpi)
2989 + gui_mswin_get_menu_height(FALSE);
K.Takata4f2417f2021-06-05 16:25:32 +02002990 *cols = (w - base_width) / gui.char_width;
2991 *rows = (h - base_height) / gui.char_height;
2992 *valid_w = base_width + *cols * gui.char_width;
2993 *valid_h = base_height + *rows * gui.char_height;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002994}
2995
2996 void
2997gui_mch_flash(int msec)
2998{
2999 RECT rc;
3000
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01003001#if defined(FEAT_DIRECTX)
3002 if (IS_ENABLE_DIRECTX())
3003 DWriteContext_Flush(s_dwc);
3004#endif
3005
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003006 /*
3007 * Note: InvertRect() excludes right and bottom of rectangle.
3008 */
3009 rc.left = 0;
3010 rc.top = 0;
3011 rc.right = gui.num_cols * gui.char_width;
3012 rc.bottom = gui.num_rows * gui.char_height;
3013 InvertRect(s_hdc, &rc);
Bram Moolenaar734a8672019-12-02 22:49:38 +01003014 gui_mch_flush(); // make sure it's displayed
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003015
Bram Moolenaar734a8672019-12-02 22:49:38 +01003016 ui_delay((long)msec, TRUE); // wait for a few msec
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003017
3018 InvertRect(s_hdc, &rc);
3019}
3020
3021/*
Bram Moolenaar185577e2020-10-29 20:08:21 +01003022 * Check if the specified point is on-screen. (multi-monitor aware)
3023 */
3024 static BOOL
3025is_point_onscreen(int x, int y)
3026{
3027 POINT pt = {x, y};
3028
3029 return MonitorFromPoint(pt, MONITOR_DEFAULTTONULL) != NULL;
3030}
3031
3032/*
3033 * Check if the whole area of the specified window is on-screen.
3034 *
3035 * Note about DirectX: Windows 10 1809 or above no longer maintains image of
3036 * the window portion that is off-screen. Scrolling by DWriteContext_Scroll()
3037 * only works when the whole window is on-screen.
3038 */
3039 static BOOL
3040is_window_onscreen(HWND hwnd)
3041{
3042 RECT rc;
3043
3044 GetWindowRect(hwnd, &rc);
3045
3046 if (!is_point_onscreen(rc.left, rc.top))
3047 return FALSE;
3048 if (!is_point_onscreen(rc.left, rc.bottom))
3049 return FALSE;
3050 if (!is_point_onscreen(rc.right, rc.top))
3051 return FALSE;
3052 if (!is_point_onscreen(rc.right, rc.bottom))
3053 return FALSE;
3054 return TRUE;
3055}
3056
3057/*
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003058 * Return flags used for scrolling.
3059 * The SW_INVALIDATE is required when part of the window is covered or
3060 * off-screen. Refer to MS KB Q75236.
3061 */
3062 static int
3063get_scroll_flags(void)
3064{
3065 HWND hwnd;
3066 RECT rcVim, rcOther, rcDest;
3067
Bram Moolenaar185577e2020-10-29 20:08:21 +01003068 // Check if the window is (partly) off-screen.
3069 if (!is_window_onscreen(s_hwnd))
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003070 return SW_INVALIDATE;
3071
Dominique Pelleaf4a61a2021-12-27 17:21:41 +00003072 // Check if there is a window (partly) on top of us.
Bram Moolenaar185577e2020-10-29 20:08:21 +01003073 GetWindowRect(s_hwnd, &rcVim);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003074 for (hwnd = s_hwnd; (hwnd = GetWindow(hwnd, GW_HWNDPREV)) != (HWND)0; )
3075 if (IsWindowVisible(hwnd))
3076 {
3077 GetWindowRect(hwnd, &rcOther);
3078 if (IntersectRect(&rcDest, &rcVim, &rcOther))
3079 return SW_INVALIDATE;
3080 }
3081 return 0;
3082}
3083
3084/*
3085 * On some Intel GPUs, the regions drawn just prior to ScrollWindowEx()
3086 * may not be scrolled out properly.
3087 * For gVim, when _OnScroll() is repeated, the character at the
3088 * previous cursor position may be left drawn after scroll.
3089 * The problem can be avoided by calling GetPixel() to get a pixel in
3090 * the region before ScrollWindowEx().
3091 */
3092 static void
3093intel_gpu_workaround(void)
3094{
3095 GetPixel(s_hdc, FILL_X(gui.col), FILL_Y(gui.row));
3096}
3097
3098/*
3099 * Delete the given number of lines from the given row, scrolling up any
3100 * text further down within the scroll region.
3101 */
3102 void
3103gui_mch_delete_lines(
3104 int row,
3105 int num_lines)
3106{
3107 RECT rc;
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01003108
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003109 rc.left = FILL_X(gui.scroll_region_left);
3110 rc.right = FILL_X(gui.scroll_region_right + 1);
3111 rc.top = FILL_Y(row);
3112 rc.bottom = FILL_Y(gui.scroll_region_bot + 1);
3113
Bram Moolenaar92467d32017-12-05 13:22:16 +01003114#if defined(FEAT_DIRECTX)
Bram Moolenaar185577e2020-10-29 20:08:21 +01003115 if (IS_ENABLE_DIRECTX() && is_window_onscreen(s_hwnd))
Bram Moolenaar92467d32017-12-05 13:22:16 +01003116 {
Bram Moolenaara338adc2018-01-31 20:51:47 +01003117 DWriteContext_Scroll(s_dwc, 0, -num_lines * gui.char_height, &rc);
Bram Moolenaar92467d32017-12-05 13:22:16 +01003118 }
Bram Moolenaara338adc2018-01-31 20:51:47 +01003119 else
Bram Moolenaar92467d32017-12-05 13:22:16 +01003120#endif
3121 {
Bram Moolenaar185577e2020-10-29 20:08:21 +01003122#if defined(FEAT_DIRECTX)
3123 if (IS_ENABLE_DIRECTX())
3124 DWriteContext_Flush(s_dwc);
3125#endif
Bram Moolenaar92467d32017-12-05 13:22:16 +01003126 intel_gpu_workaround();
3127 ScrollWindowEx(s_textArea, 0, -num_lines * gui.char_height,
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003128 &rc, &rc, NULL, NULL, get_scroll_flags());
Bram Moolenaar7f88b652017-12-14 13:15:19 +01003129 UpdateWindow(s_textArea);
Bram Moolenaar92467d32017-12-05 13:22:16 +01003130 }
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003131
Bram Moolenaar734a8672019-12-02 22:49:38 +01003132 // This seems to be required to avoid the cursor disappearing when
3133 // scrolling such that the cursor ends up in the top-left character on
3134 // the screen... But why? (Webb)
3135 // It's probably fixed by disabling drawing the cursor while scrolling.
3136 // gui.cursor_is_valid = FALSE;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003137
3138 gui_clear_block(gui.scroll_region_bot - num_lines + 1,
3139 gui.scroll_region_left,
3140 gui.scroll_region_bot, gui.scroll_region_right);
3141}
3142
3143/*
3144 * Insert the given number of lines before the given row, scrolling down any
3145 * following text within the scroll region.
3146 */
3147 void
3148gui_mch_insert_lines(
3149 int row,
3150 int num_lines)
3151{
3152 RECT rc;
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01003153
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003154 rc.left = FILL_X(gui.scroll_region_left);
3155 rc.right = FILL_X(gui.scroll_region_right + 1);
3156 rc.top = FILL_Y(row);
3157 rc.bottom = FILL_Y(gui.scroll_region_bot + 1);
Bram Moolenaar92467d32017-12-05 13:22:16 +01003158
3159#if defined(FEAT_DIRECTX)
Bram Moolenaar185577e2020-10-29 20:08:21 +01003160 if (IS_ENABLE_DIRECTX() && is_window_onscreen(s_hwnd))
Bram Moolenaar92467d32017-12-05 13:22:16 +01003161 {
Bram Moolenaara338adc2018-01-31 20:51:47 +01003162 DWriteContext_Scroll(s_dwc, 0, num_lines * gui.char_height, &rc);
Bram Moolenaar92467d32017-12-05 13:22:16 +01003163 }
Bram Moolenaara338adc2018-01-31 20:51:47 +01003164 else
Bram Moolenaar92467d32017-12-05 13:22:16 +01003165#endif
3166 {
Bram Moolenaar185577e2020-10-29 20:08:21 +01003167#if defined(FEAT_DIRECTX)
3168 if (IS_ENABLE_DIRECTX())
3169 DWriteContext_Flush(s_dwc);
3170#endif
Bram Moolenaar92467d32017-12-05 13:22:16 +01003171 intel_gpu_workaround();
Bram Moolenaar734a8672019-12-02 22:49:38 +01003172 // The SW_INVALIDATE is required when part of the window is covered or
3173 // off-screen. How do we avoid it when it's not needed?
Bram Moolenaar92467d32017-12-05 13:22:16 +01003174 ScrollWindowEx(s_textArea, 0, num_lines * gui.char_height,
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003175 &rc, &rc, NULL, NULL, get_scroll_flags());
Bram Moolenaar7f88b652017-12-14 13:15:19 +01003176 UpdateWindow(s_textArea);
Bram Moolenaar92467d32017-12-05 13:22:16 +01003177 }
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003178
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003179 gui_clear_block(row, gui.scroll_region_left,
3180 row + num_lines - 1, gui.scroll_region_right);
3181}
3182
3183
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003184 void
Bram Moolenaar1266d672017-02-01 13:43:36 +01003185gui_mch_exit(int rc UNUSED)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003186{
3187#if defined(FEAT_DIRECTX)
3188 DWriteContext_Close(s_dwc);
3189 DWrite_Final();
3190 s_dwc = NULL;
3191#endif
3192
3193 ReleaseDC(s_textArea, s_hdc);
3194 DeleteObject(s_brush);
3195
3196#ifdef FEAT_TEAROFF
Bram Moolenaar734a8672019-12-02 22:49:38 +01003197 // Unload the tearoff bitmap
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003198 (void)DeleteObject((HGDIOBJ)s_htearbitmap);
3199#endif
3200
Bram Moolenaar734a8672019-12-02 22:49:38 +01003201 // Destroy our window (if we have one).
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003202 if (s_hwnd != NULL)
3203 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01003204 destroying = TRUE; // ignore WM_DESTROY message now
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003205 DestroyWindow(s_hwnd);
3206 }
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003207}
3208
3209 static char_u *
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01003210logfont2name(LOGFONTW lf)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003211{
3212 char *p;
3213 char *res;
3214 char *charset_name;
Bram Moolenaar7c1c6db2016-04-03 22:08:05 +02003215 char *quality_name;
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01003216 char *font_name;
Bram Moolenaarf720d0a2019-04-28 14:02:47 +02003217 int points;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003218
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01003219 font_name = (char *)utf16_to_enc(lf.lfFaceName, NULL);
3220 if (font_name == NULL)
3221 return NULL;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003222 charset_name = charset_id2name((int)lf.lfCharSet);
Bram Moolenaar7c1c6db2016-04-03 22:08:05 +02003223 quality_name = quality_id2name((int)lf.lfQuality);
3224
Bram Moolenaarc799fe22019-05-28 23:08:19 +02003225 res = alloc(strlen(font_name) + 30
Bram Moolenaar2155a6a2019-04-27 19:15:45 +02003226 + (charset_name == NULL ? 0 : strlen(charset_name) + 2)
Bram Moolenaar964b3742019-05-24 18:54:09 +02003227 + (quality_name == NULL ? 0 : strlen(quality_name) + 2));
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003228 if (res != NULL)
3229 {
3230 p = res;
Bram Moolenaarf720d0a2019-04-28 14:02:47 +02003231 // make a normal font string out of the lf thing:
3232 points = pixels_to_points(
3233 lf.lfHeight < 0 ? -lf.lfHeight : lf.lfHeight, TRUE);
3234 if (lf.lfWeight == FW_NORMAL || lf.lfWeight == FW_BOLD)
3235 sprintf((char *)p, "%s:h%d", font_name, points);
3236 else
Bram Moolenaara0e67fc2019-04-29 21:46:26 +02003237 sprintf((char *)p, "%s:h%d:W%ld", font_name, points, lf.lfWeight);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003238 while (*p)
3239 {
3240 if (*p == ' ')
3241 *p = '_';
3242 ++p;
3243 }
3244 if (lf.lfItalic)
3245 STRCAT(p, ":i");
Bram Moolenaarf720d0a2019-04-28 14:02:47 +02003246 if (lf.lfWeight == FW_BOLD)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003247 STRCAT(p, ":b");
3248 if (lf.lfUnderline)
3249 STRCAT(p, ":u");
3250 if (lf.lfStrikeOut)
3251 STRCAT(p, ":s");
3252 if (charset_name != NULL)
3253 {
3254 STRCAT(p, ":c");
3255 STRCAT(p, charset_name);
3256 }
Bram Moolenaar7c1c6db2016-04-03 22:08:05 +02003257 if (quality_name != NULL)
3258 {
3259 STRCAT(p, ":q");
3260 STRCAT(p, quality_name);
3261 }
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003262 }
3263
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01003264 vim_free(font_name);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003265 return (char_u *)res;
3266}
3267
3268
3269#ifdef FEAT_MBYTE_IME
3270/*
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01003271 * Set correct LOGFONTW to IME. Use 'guifontwide' if available, otherwise use
K.Takatac81e9bf2022-01-16 14:15:49 +00003272 * 'guifont'.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003273 */
3274 static void
3275update_im_font(void)
3276{
K.Takatac81e9bf2022-01-16 14:15:49 +00003277 LOGFONTW lf_wide, lf;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003278
3279 if (p_guifontwide != NULL && *p_guifontwide != NUL
3280 && gui.wide_font != NOFONT
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01003281 && GetObjectW((HFONT)gui.wide_font, sizeof(lf_wide), &lf_wide))
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003282 norm_logfont = lf_wide;
3283 else
3284 norm_logfont = sub_logfont;
K.Takatac81e9bf2022-01-16 14:15:49 +00003285
3286 lf = norm_logfont;
3287 if (s_process_dpi_aware == DPI_AWARENESS_UNAWARE)
3288 // Work around when PerMonitorV2 is not enabled in the process level.
3289 lf.lfHeight = lf.lfHeight * DEFAULT_DPI / s_dpi;
3290 im_set_font(&lf);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003291}
3292#endif
3293
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003294/*
3295 * Handler of gui.wide_font (p_guifontwide) changed notification.
3296 */
3297 void
3298gui_mch_wide_font_changed(void)
3299{
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01003300 LOGFONTW lf;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003301
Bram Moolenaarfc3abf42019-01-24 15:54:21 +01003302#ifdef FEAT_MBYTE_IME
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003303 update_im_font();
Bram Moolenaarfc3abf42019-01-24 15:54:21 +01003304#endif
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003305
3306 gui_mch_free_font(gui.wide_ital_font);
3307 gui.wide_ital_font = NOFONT;
3308 gui_mch_free_font(gui.wide_bold_font);
3309 gui.wide_bold_font = NOFONT;
3310 gui_mch_free_font(gui.wide_boldital_font);
3311 gui.wide_boldital_font = NOFONT;
3312
3313 if (gui.wide_font
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01003314 && GetObjectW((HFONT)gui.wide_font, sizeof(lf), &lf))
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003315 {
3316 if (!lf.lfItalic)
3317 {
3318 lf.lfItalic = TRUE;
3319 gui.wide_ital_font = get_font_handle(&lf);
3320 lf.lfItalic = FALSE;
3321 }
3322 if (lf.lfWeight < FW_BOLD)
3323 {
3324 lf.lfWeight = FW_BOLD;
3325 gui.wide_bold_font = get_font_handle(&lf);
3326 if (!lf.lfItalic)
3327 {
3328 lf.lfItalic = TRUE;
3329 gui.wide_boldital_font = get_font_handle(&lf);
3330 }
3331 }
3332 }
3333}
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003334
3335/*
3336 * Initialise vim to use the font with the given name.
3337 * Return FAIL if the font could not be loaded, OK otherwise.
3338 */
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003339 int
Bram Moolenaar1266d672017-02-01 13:43:36 +01003340gui_mch_init_font(char_u *font_name, int fontset UNUSED)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003341{
K.Takatac81e9bf2022-01-16 14:15:49 +00003342 LOGFONTW lf, lfOrig;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003343 GuiFont font = NOFONT;
3344 char_u *p;
3345
Bram Moolenaar734a8672019-12-02 22:49:38 +01003346 // Load the font
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003347 if (get_logfont(&lf, font_name, NULL, TRUE) == OK)
K.Takatac81e9bf2022-01-16 14:15:49 +00003348 {
3349 lfOrig = lf;
3350 lf.lfHeight = adjust_fontsize_by_dpi(lf.lfHeight);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003351 font = get_font_handle(&lf);
K.Takatac81e9bf2022-01-16 14:15:49 +00003352 }
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003353 if (font == NOFONT)
3354 return FAIL;
3355
3356 if (font_name == NULL)
K.Takata45f9cfb2022-01-21 11:11:00 +00003357 font_name = (char_u *)"";
K.Takata4ac893f2022-01-20 12:44:28 +00003358#ifdef FEAT_MBYTE_IME
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003359 norm_logfont = lf;
3360 sub_logfont = lf;
K.Takatac81e9bf2022-01-16 14:15:49 +00003361 if (!s_in_dpichanged)
3362 update_im_font();
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003363#endif
3364 gui_mch_free_font(gui.norm_font);
3365 gui.norm_font = font;
K.Takatac81e9bf2022-01-16 14:15:49 +00003366 current_font_height = lfOrig.lfHeight;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003367 GetFontSize(font);
3368
K.Takatac81e9bf2022-01-16 14:15:49 +00003369 p = logfont2name(lfOrig);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003370 if (p != NULL)
3371 {
3372 hl_set_font_name(p);
3373
Bram Moolenaar734a8672019-12-02 22:49:38 +01003374 // When setting 'guifont' to "*" replace it with the actual font name.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003375 if (STRCMP(font_name, "*") == 0 && STRCMP(p_guifont, "*") == 0)
3376 {
3377 vim_free(p_guifont);
3378 p_guifont = p;
3379 }
3380 else
3381 vim_free(p);
3382 }
3383
3384 gui_mch_free_font(gui.ital_font);
3385 gui.ital_font = NOFONT;
3386 gui_mch_free_font(gui.bold_font);
3387 gui.bold_font = NOFONT;
3388 gui_mch_free_font(gui.boldital_font);
3389 gui.boldital_font = NOFONT;
3390
3391 if (!lf.lfItalic)
3392 {
3393 lf.lfItalic = TRUE;
3394 gui.ital_font = get_font_handle(&lf);
3395 lf.lfItalic = FALSE;
3396 }
3397 if (lf.lfWeight < FW_BOLD)
3398 {
3399 lf.lfWeight = FW_BOLD;
3400 gui.bold_font = get_font_handle(&lf);
3401 if (!lf.lfItalic)
3402 {
3403 lf.lfItalic = TRUE;
3404 gui.boldital_font = get_font_handle(&lf);
3405 }
3406 }
3407
3408 return OK;
3409}
3410
3411#ifndef WPF_RESTORETOMAXIMIZED
Bram Moolenaar734a8672019-12-02 22:49:38 +01003412# define WPF_RESTORETOMAXIMIZED 2 // just in case someone doesn't have it
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003413#endif
3414
3415/*
3416 * Return TRUE if the GUI window is maximized, filling the whole screen.
Bram Moolenaarb68ced52020-07-17 22:26:53 +02003417 * Also return TRUE if the window is snapped.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003418 */
3419 int
3420gui_mch_maximized(void)
3421{
3422 WINDOWPLACEMENT wp;
Bram Moolenaarb68ced52020-07-17 22:26:53 +02003423 RECT rc;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003424
3425 wp.length = sizeof(WINDOWPLACEMENT);
3426 if (GetWindowPlacement(s_hwnd, &wp))
Bram Moolenaarb68ced52020-07-17 22:26:53 +02003427 {
3428 if (wp.showCmd == SW_SHOWMAXIMIZED
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003429 || (wp.showCmd == SW_SHOWMINIMIZED
Bram Moolenaarb68ced52020-07-17 22:26:53 +02003430 && wp.flags == WPF_RESTORETOMAXIMIZED))
3431 return TRUE;
3432 if (wp.showCmd == SW_SHOWMINIMIZED)
3433 return FALSE;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003434
Bram Moolenaarb68ced52020-07-17 22:26:53 +02003435 // Assume the window is snapped when the sizes from two APIs differ.
3436 GetWindowRect(s_hwnd, &rc);
3437 if ((rc.right - rc.left !=
3438 wp.rcNormalPosition.right - wp.rcNormalPosition.left)
3439 || (rc.bottom - rc.top !=
3440 wp.rcNormalPosition.bottom - wp.rcNormalPosition.top))
3441 return TRUE;
3442 }
3443 return FALSE;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003444}
3445
3446/*
Bram Moolenaar8ac44152017-11-09 18:33:29 +01003447 * Called when the font changed while the window is maximized or GO_KEEPWINSIZE
3448 * is set. Compute the new Rows and Columns. This is like resizing the
3449 * window.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003450 */
3451 void
3452gui_mch_newfont(void)
3453{
3454 RECT rect;
3455
3456 GetWindowRect(s_hwnd, &rect);
3457 if (win_socket_id == 0)
3458 {
3459 gui_resize_shell(rect.right - rect.left
K.Takatac81e9bf2022-01-16 14:15:49 +00003460 - (pGetSystemMetricsForDpi(SM_CXFRAME, s_dpi) +
3461 pGetSystemMetricsForDpi(SM_CXPADDEDBORDER, s_dpi)) * 2,
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003462 rect.bottom - rect.top
K.Takatac81e9bf2022-01-16 14:15:49 +00003463 - (pGetSystemMetricsForDpi(SM_CYFRAME, s_dpi) +
3464 pGetSystemMetricsForDpi(SM_CXPADDEDBORDER, s_dpi)) * 2
3465 - pGetSystemMetricsForDpi(SM_CYCAPTION, s_dpi)
3466 - gui_mswin_get_menu_height(FALSE));
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003467 }
3468 else
3469 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01003470 // Inside another window, don't use the frame and border.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003471 gui_resize_shell(rect.right - rect.left,
K.Takatac81e9bf2022-01-16 14:15:49 +00003472 rect.bottom - rect.top - gui_mswin_get_menu_height(FALSE));
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003473 }
3474}
3475
3476/*
3477 * Set the window title
3478 */
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003479 void
3480gui_mch_settitle(
3481 char_u *title,
Bram Moolenaar1266d672017-02-01 13:43:36 +01003482 char_u *icon UNUSED)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003483{
3484 set_window_title(s_hwnd, (title == NULL ? "VIM" : (char *)title));
3485}
3486
Bram Moolenaara6b7a082016-08-10 20:53:05 +02003487#if defined(FEAT_MOUSESHAPE) || defined(PROTO)
Bram Moolenaar734a8672019-12-02 22:49:38 +01003488// Table for shape IDCs. Keep in sync with the mshape_names[] table in
3489// misc2.c!
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003490static LPCSTR mshape_idcs[] =
3491{
Bram Moolenaar734a8672019-12-02 22:49:38 +01003492 IDC_ARROW, // arrow
3493 MAKEINTRESOURCE(0), // blank
3494 IDC_IBEAM, // beam
3495 IDC_SIZENS, // updown
3496 IDC_SIZENS, // udsizing
3497 IDC_SIZEWE, // leftright
3498 IDC_SIZEWE, // lrsizing
3499 IDC_WAIT, // busy
3500 IDC_NO, // no
3501 IDC_ARROW, // crosshair
3502 IDC_ARROW, // hand1
3503 IDC_ARROW, // hand2
3504 IDC_ARROW, // pencil
3505 IDC_ARROW, // question
3506 IDC_ARROW, // right-arrow
3507 IDC_UPARROW, // up-arrow
3508 IDC_ARROW // last one
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003509};
3510
3511 void
3512mch_set_mouse_shape(int shape)
3513{
3514 LPCSTR idc;
3515
3516 if (shape == MSHAPE_HIDE)
3517 ShowCursor(FALSE);
3518 else
3519 {
3520 if (shape >= MSHAPE_NUMBERED)
3521 idc = IDC_ARROW;
3522 else
3523 idc = mshape_idcs[shape];
Bram Moolenaara0754902019-11-19 23:01:28 +01003524 SetClassLongPtr(s_textArea, GCLP_HCURSOR, (LONG_PTR)LoadCursor(NULL, idc));
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003525 if (!p_mh)
3526 {
3527 POINT mp;
3528
Bram Moolenaar734a8672019-12-02 22:49:38 +01003529 // Set the position to make it redrawn with the new shape.
K.Takata45f9cfb2022-01-21 11:11:00 +00003530 (void)GetCursorPos(&mp);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003531 (void)SetCursorPos(mp.x, mp.y);
3532 ShowCursor(TRUE);
3533 }
3534 }
3535}
3536#endif
3537
Bram Moolenaara6b7a082016-08-10 20:53:05 +02003538#if defined(FEAT_BROWSE) || defined(PROTO)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003539/*
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003540 * Wide version of convert_filter().
3541 */
3542 static WCHAR *
3543convert_filterW(char_u *s)
3544{
3545 char_u *tmp;
3546 int len;
3547 WCHAR *res;
3548
3549 tmp = convert_filter(s);
3550 if (tmp == NULL)
3551 return NULL;
3552 len = (int)STRLEN(s) + 3;
3553 res = enc_to_utf16(tmp, &len);
3554 vim_free(tmp);
3555 return res;
3556}
3557
3558/*
Bram Moolenaarfc3abf42019-01-24 15:54:21 +01003559 * Pop open a file browser and return the file selected, in allocated memory,
3560 * or NULL if Cancel is hit.
3561 * saving - TRUE if the file will be saved to, FALSE if it will be opened.
3562 * title - Title message for the file browser dialog.
3563 * dflt - Default name of file.
3564 * ext - Default extension to be added to files without extensions.
3565 * initdir - directory in which to open the browser (NULL = current dir)
3566 * filter - Filter for matched files to choose from.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003567 */
Bram Moolenaar091806d2019-01-24 16:27:46 +01003568 char_u *
Bram Moolenaarfc3abf42019-01-24 15:54:21 +01003569gui_mch_browse(
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003570 int saving,
3571 char_u *title,
3572 char_u *dflt,
3573 char_u *ext,
3574 char_u *initdir,
3575 char_u *filter)
3576{
Bram Moolenaar734a8672019-12-02 22:49:38 +01003577 // We always use the wide function. This means enc_to_utf16() must work,
3578 // otherwise it fails miserably!
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003579 OPENFILENAMEW fileStruct;
3580 WCHAR fileBuf[MAXPATHL];
3581 WCHAR *wp;
3582 int i;
3583 WCHAR *titlep = NULL;
3584 WCHAR *extp = NULL;
3585 WCHAR *initdirp = NULL;
3586 WCHAR *filterp;
Bram Moolenaar7ff8a3c2018-09-22 14:39:15 +02003587 char_u *p, *q;
K.Takata14b8d6a2022-01-20 15:05:22 +00003588 BOOL ret;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003589
3590 if (dflt == NULL)
3591 fileBuf[0] = NUL;
3592 else
3593 {
3594 wp = enc_to_utf16(dflt, NULL);
3595 if (wp == NULL)
3596 fileBuf[0] = NUL;
3597 else
3598 {
3599 for (i = 0; wp[i] != NUL && i < MAXPATHL - 1; ++i)
3600 fileBuf[i] = wp[i];
3601 fileBuf[i] = NUL;
3602 vim_free(wp);
3603 }
3604 }
3605
Bram Moolenaar734a8672019-12-02 22:49:38 +01003606 // Convert the filter to Windows format.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003607 filterp = convert_filterW(filter);
3608
Bram Moolenaara80faa82020-04-12 19:37:17 +02003609 CLEAR_FIELD(fileStruct);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01003610# ifdef OPENFILENAME_SIZE_VERSION_400W
Bram Moolenaar734a8672019-12-02 22:49:38 +01003611 // be compatible with Windows NT 4.0
Bram Moolenaar89e375a2016-03-15 18:09:57 +01003612 fileStruct.lStructSize = OPENFILENAME_SIZE_VERSION_400W;
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01003613# else
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003614 fileStruct.lStructSize = sizeof(fileStruct);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01003615# endif
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003616
3617 if (title != NULL)
3618 titlep = enc_to_utf16(title, NULL);
3619 fileStruct.lpstrTitle = titlep;
3620
3621 if (ext != NULL)
3622 extp = enc_to_utf16(ext, NULL);
3623 fileStruct.lpstrDefExt = extp;
3624
3625 fileStruct.lpstrFile = fileBuf;
3626 fileStruct.nMaxFile = MAXPATHL;
3627 fileStruct.lpstrFilter = filterp;
Bram Moolenaar734a8672019-12-02 22:49:38 +01003628 fileStruct.hwndOwner = s_hwnd; // main Vim window is owner
3629 // has an initial dir been specified?
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003630 if (initdir != NULL && *initdir != NUL)
3631 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01003632 // Must have backslashes here, no matter what 'shellslash' says
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003633 initdirp = enc_to_utf16(initdir, NULL);
3634 if (initdirp != NULL)
3635 {
3636 for (wp = initdirp; *wp != NUL; ++wp)
3637 if (*wp == '/')
3638 *wp = '\\';
3639 }
3640 fileStruct.lpstrInitialDir = initdirp;
3641 }
3642
3643 /*
3644 * TODO: Allow selection of multiple files. Needs another arg to this
3645 * function to ask for it, and need to use OFN_ALLOWMULTISELECT below.
3646 * Also, should we use OFN_FILEMUSTEXIST when opening? Vim can edit on
3647 * files that don't exist yet, so I haven't put it in. What about
3648 * OFN_PATHMUSTEXIST?
3649 * Don't use OFN_OVERWRITEPROMPT, Vim has its own ":confirm" dialog.
3650 */
3651 fileStruct.Flags = (OFN_NOCHANGEDIR | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01003652# ifdef FEAT_SHORTCUT
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003653 if (curbuf->b_p_bin)
3654 fileStruct.Flags |= OFN_NODEREFERENCELINKS;
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01003655# endif
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003656 if (saving)
K.Takata14b8d6a2022-01-20 15:05:22 +00003657 ret = GetSaveFileNameW(&fileStruct);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003658 else
K.Takata14b8d6a2022-01-20 15:05:22 +00003659 ret = GetOpenFileNameW(&fileStruct);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003660
3661 vim_free(filterp);
3662 vim_free(initdirp);
3663 vim_free(titlep);
3664 vim_free(extp);
3665
K.Takata14b8d6a2022-01-20 15:05:22 +00003666 if (!ret)
3667 return NULL;
3668
3669 // Convert from UTF-16 to 'encoding'.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003670 p = utf16_to_enc(fileBuf, NULL);
Bram Moolenaar7ff8a3c2018-09-22 14:39:15 +02003671 if (p == NULL)
3672 return NULL;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003673
Bram Moolenaar734a8672019-12-02 22:49:38 +01003674 // Give focus back to main window (when using MDI).
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003675 SetFocus(s_hwnd);
3676
Bram Moolenaar734a8672019-12-02 22:49:38 +01003677 // Shorten the file name if possible
Bram Moolenaar7ff8a3c2018-09-22 14:39:15 +02003678 q = vim_strsave(shorten_fname1(p));
3679 vim_free(p);
3680 return q;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003681}
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003682
3683
3684/*
3685 * Convert the string s to the proper format for a filter string by replacing
3686 * the \t and \n delimiters with \0.
3687 * Returns the converted string in allocated memory.
3688 *
3689 * Keep in sync with convert_filterW() above!
3690 */
3691 static char_u *
3692convert_filter(char_u *s)
3693{
3694 char_u *res;
3695 unsigned s_len = (unsigned)STRLEN(s);
3696 unsigned i;
3697
3698 res = alloc(s_len + 3);
3699 if (res != NULL)
3700 {
3701 for (i = 0; i < s_len; ++i)
3702 if (s[i] == '\t' || s[i] == '\n')
3703 res[i] = '\0';
3704 else
3705 res[i] = s[i];
3706 res[s_len] = NUL;
Bram Moolenaar734a8672019-12-02 22:49:38 +01003707 // Add two extra NULs to make sure it's properly terminated.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003708 res[s_len + 1] = NUL;
3709 res[s_len + 2] = NUL;
3710 }
3711 return res;
3712}
3713
3714/*
3715 * Select a directory.
3716 */
3717 char_u *
3718gui_mch_browsedir(char_u *title, char_u *initdir)
3719{
Bram Moolenaar734a8672019-12-02 22:49:38 +01003720 // We fake this: Use a filter that doesn't select anything and a default
3721 // file name that won't be used.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003722 return gui_mch_browse(0, title, (char_u *)_("Not Used"), NULL,
3723 initdir, (char_u *)_("Directory\t*.nothing\n"));
3724}
Bram Moolenaar734a8672019-12-02 22:49:38 +01003725#endif // FEAT_BROWSE
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003726
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003727 static void
3728_OnDropFiles(
Bram Moolenaar1266d672017-02-01 13:43:36 +01003729 HWND hwnd UNUSED,
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003730 HDROP hDrop)
3731{
Bram Moolenaar4033c552017-09-16 20:54:51 +02003732#define BUFPATHLEN _MAX_PATH
3733#define DRAGQVAL 0xFFFFFFFF
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003734 WCHAR wszFile[BUFPATHLEN];
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003735 char szFile[BUFPATHLEN];
3736 UINT cFiles = DragQueryFile(hDrop, DRAGQVAL, NULL, 0);
3737 UINT i;
3738 char_u **fnames;
3739 POINT pt;
3740 int_u modifiers = 0;
3741
Bram Moolenaar734a8672019-12-02 22:49:38 +01003742 // TRACE("_OnDropFiles: %d files dropped\n", cFiles);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003743
Bram Moolenaar734a8672019-12-02 22:49:38 +01003744 // Obtain dropped position
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003745 DragQueryPoint(hDrop, &pt);
3746 MapWindowPoints(s_hwnd, s_textArea, &pt, 1);
3747
3748 reset_VIsual();
3749
Bram Moolenaarc799fe22019-05-28 23:08:19 +02003750 fnames = ALLOC_MULT(char_u *, cFiles);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003751
3752 if (fnames != NULL)
3753 for (i = 0; i < cFiles; ++i)
3754 {
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003755 if (DragQueryFileW(hDrop, i, wszFile, BUFPATHLEN) > 0)
3756 fnames[i] = utf16_to_enc(wszFile, NULL);
3757 else
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003758 {
3759 DragQueryFile(hDrop, i, szFile, BUFPATHLEN);
3760 fnames[i] = vim_strsave((char_u *)szFile);
3761 }
3762 }
3763
3764 DragFinish(hDrop);
3765
3766 if (fnames != NULL)
3767 {
3768 if ((GetKeyState(VK_SHIFT) & 0x8000) != 0)
3769 modifiers |= MOUSE_SHIFT;
3770 if ((GetKeyState(VK_CONTROL) & 0x8000) != 0)
3771 modifiers |= MOUSE_CTRL;
3772 if ((GetKeyState(VK_MENU) & 0x8000) != 0)
3773 modifiers |= MOUSE_ALT;
3774
3775 gui_handle_drop(pt.x, pt.y, modifiers, fnames, cFiles);
3776
3777 s_need_activate = TRUE;
3778 }
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003779}
3780
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003781 static int
3782_OnScroll(
Bram Moolenaar1266d672017-02-01 13:43:36 +01003783 HWND hwnd UNUSED,
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003784 HWND hwndCtl,
3785 UINT code,
3786 int pos)
3787{
Bram Moolenaar734a8672019-12-02 22:49:38 +01003788 static UINT prev_code = 0; // code of previous call
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003789 scrollbar_T *sb, *sb_info;
3790 long val;
3791 int dragging = FALSE;
3792 int dont_scroll_save = dont_scroll;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003793 SCROLLINFO si;
3794
3795 si.cbSize = sizeof(si);
3796 si.fMask = SIF_POS;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003797
3798 sb = gui_mswin_find_scrollbar(hwndCtl);
3799 if (sb == NULL)
3800 return 0;
3801
Bram Moolenaar734a8672019-12-02 22:49:38 +01003802 if (sb->wp != NULL) // Left or right scrollbar
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003803 {
3804 /*
3805 * Careful: need to get scrollbar info out of first (left) scrollbar
3806 * for window, but keep real scrollbar too because we must pass it to
3807 * gui_drag_scrollbar().
3808 */
3809 sb_info = &sb->wp->w_scrollbars[0];
3810 }
Bram Moolenaar734a8672019-12-02 22:49:38 +01003811 else // Bottom scrollbar
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003812 sb_info = sb;
3813 val = sb_info->value;
3814
3815 switch (code)
3816 {
3817 case SB_THUMBTRACK:
3818 val = pos;
3819 dragging = TRUE;
3820 if (sb->scroll_shift > 0)
3821 val <<= sb->scroll_shift;
3822 break;
3823 case SB_LINEDOWN:
3824 val++;
3825 break;
3826 case SB_LINEUP:
3827 val--;
3828 break;
3829 case SB_PAGEDOWN:
3830 val += (sb_info->size > 2 ? sb_info->size - 2 : 1);
3831 break;
3832 case SB_PAGEUP:
3833 val -= (sb_info->size > 2 ? sb_info->size - 2 : 1);
3834 break;
3835 case SB_TOP:
3836 val = 0;
3837 break;
3838 case SB_BOTTOM:
3839 val = sb_info->max;
3840 break;
3841 case SB_ENDSCROLL:
3842 if (prev_code == SB_THUMBTRACK)
3843 {
3844 /*
3845 * "pos" only gives us 16-bit data. In case of large file,
3846 * use GetScrollPos() which returns 32-bit. Unfortunately it
3847 * is not valid while the scrollbar is being dragged.
3848 */
3849 val = GetScrollPos(hwndCtl, SB_CTL);
3850 if (sb->scroll_shift > 0)
3851 val <<= sb->scroll_shift;
3852 }
3853 break;
3854
3855 default:
Bram Moolenaar734a8672019-12-02 22:49:38 +01003856 // TRACE("Unknown scrollbar event %d\n", code);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003857 return 0;
3858 }
3859 prev_code = code;
3860
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003861 si.nPos = (sb->scroll_shift > 0) ? val >> sb->scroll_shift : val;
3862 SetScrollInfo(hwndCtl, SB_CTL, &si, TRUE);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003863
3864 /*
3865 * When moving a vertical scrollbar, move the other vertical scrollbar too.
3866 */
3867 if (sb->wp != NULL)
3868 {
3869 scrollbar_T *sba = sb->wp->w_scrollbars;
3870 HWND id = sba[ (sb == sba + SBAR_LEFT) ? SBAR_RIGHT : SBAR_LEFT].id;
3871
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003872 SetScrollInfo(id, SB_CTL, &si, TRUE);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003873 }
3874
Bram Moolenaar734a8672019-12-02 22:49:38 +01003875 // Don't let us be interrupted here by another message.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003876 s_busy_processing = TRUE;
3877
Bram Moolenaar734a8672019-12-02 22:49:38 +01003878 // When "allow_scrollbar" is FALSE still need to remember the new
3879 // position, but don't actually scroll by setting "dont_scroll".
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003880 dont_scroll = !allow_scrollbar;
3881
Bram Moolenaara338adc2018-01-31 20:51:47 +01003882 mch_disable_flush();
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003883 gui_drag_scrollbar(sb, val, dragging);
Bram Moolenaara338adc2018-01-31 20:51:47 +01003884 mch_enable_flush();
3885 gui_may_flush();
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003886
3887 s_busy_processing = FALSE;
3888 dont_scroll = dont_scroll_save;
3889
3890 return 0;
3891}
3892
3893
Bram Moolenaar071d4272004-06-13 20:20:40 +00003894#ifdef FEAT_XPM_W32
3895# include "xpm_w32.h"
3896#endif
3897
Bram Moolenaar071d4272004-06-13 20:20:40 +00003898#ifdef __MINGW32__
3899/*
3900 * Add a lot of missing defines.
3901 * They are not always missing, we need the #ifndef's.
3902 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003903# ifndef IsMinimized
3904# define IsMinimized(hwnd) IsIconic(hwnd)
3905# endif
3906# ifndef IsMaximized
3907# define IsMaximized(hwnd) IsZoomed(hwnd)
3908# endif
3909# ifndef SelectFont
3910# define SelectFont(hdc, hfont) ((HFONT)SelectObject((hdc), (HGDIOBJ)(HFONT)(hfont)))
3911# endif
3912# ifndef GetStockBrush
3913# define GetStockBrush(i) ((HBRUSH)GetStockObject(i))
3914# endif
3915# ifndef DeleteBrush
3916# define DeleteBrush(hbr) DeleteObject((HGDIOBJ)(HBRUSH)(hbr))
3917# endif
3918
3919# ifndef HANDLE_WM_RBUTTONDBLCLK
3920# define HANDLE_WM_RBUTTONDBLCLK(hwnd, wParam, lParam, fn) \
3921 ((fn)((hwnd), TRUE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
3922# endif
3923# ifndef HANDLE_WM_MBUTTONUP
3924# define HANDLE_WM_MBUTTONUP(hwnd, wParam, lParam, fn) \
3925 ((fn)((hwnd), (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
3926# endif
3927# ifndef HANDLE_WM_MBUTTONDBLCLK
3928# define HANDLE_WM_MBUTTONDBLCLK(hwnd, wParam, lParam, fn) \
3929 ((fn)((hwnd), TRUE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
3930# endif
3931# ifndef HANDLE_WM_LBUTTONDBLCLK
3932# define HANDLE_WM_LBUTTONDBLCLK(hwnd, wParam, lParam, fn) \
3933 ((fn)((hwnd), TRUE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
3934# endif
3935# ifndef HANDLE_WM_RBUTTONDOWN
3936# define HANDLE_WM_RBUTTONDOWN(hwnd, wParam, lParam, fn) \
3937 ((fn)((hwnd), FALSE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
3938# endif
3939# ifndef HANDLE_WM_MOUSEMOVE
3940# define HANDLE_WM_MOUSEMOVE(hwnd, wParam, lParam, fn) \
3941 ((fn)((hwnd), (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
3942# endif
3943# ifndef HANDLE_WM_RBUTTONUP
3944# define HANDLE_WM_RBUTTONUP(hwnd, wParam, lParam, fn) \
3945 ((fn)((hwnd), (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
3946# endif
3947# ifndef HANDLE_WM_MBUTTONDOWN
3948# define HANDLE_WM_MBUTTONDOWN(hwnd, wParam, lParam, fn) \
3949 ((fn)((hwnd), FALSE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
3950# endif
3951# ifndef HANDLE_WM_LBUTTONUP
3952# define HANDLE_WM_LBUTTONUP(hwnd, wParam, lParam, fn) \
3953 ((fn)((hwnd), (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
3954# endif
3955# ifndef HANDLE_WM_LBUTTONDOWN
3956# define HANDLE_WM_LBUTTONDOWN(hwnd, wParam, lParam, fn) \
3957 ((fn)((hwnd), FALSE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
3958# endif
3959# ifndef HANDLE_WM_SYSCHAR
3960# define HANDLE_WM_SYSCHAR(hwnd, wParam, lParam, fn) \
3961 ((fn)((hwnd), (TCHAR)(wParam), (int)(short)LOWORD(lParam)), 0L)
3962# endif
3963# ifndef HANDLE_WM_ACTIVATEAPP
3964# define HANDLE_WM_ACTIVATEAPP(hwnd, wParam, lParam, fn) \
3965 ((fn)((hwnd), (BOOL)(wParam), (DWORD)(lParam)), 0L)
3966# endif
3967# ifndef HANDLE_WM_WINDOWPOSCHANGING
3968# define HANDLE_WM_WINDOWPOSCHANGING(hwnd, wParam, lParam, fn) \
3969 (LRESULT)(DWORD)(BOOL)(fn)((hwnd), (LPWINDOWPOS)(lParam))
3970# endif
3971# ifndef HANDLE_WM_VSCROLL
3972# define HANDLE_WM_VSCROLL(hwnd, wParam, lParam, fn) \
3973 ((fn)((hwnd), (HWND)(lParam), (UINT)(LOWORD(wParam)), (int)(short)HIWORD(wParam)), 0L)
3974# endif
3975# ifndef HANDLE_WM_SETFOCUS
3976# define HANDLE_WM_SETFOCUS(hwnd, wParam, lParam, fn) \
3977 ((fn)((hwnd), (HWND)(wParam)), 0L)
3978# endif
3979# ifndef HANDLE_WM_KILLFOCUS
3980# define HANDLE_WM_KILLFOCUS(hwnd, wParam, lParam, fn) \
3981 ((fn)((hwnd), (HWND)(wParam)), 0L)
3982# endif
3983# ifndef HANDLE_WM_HSCROLL
3984# define HANDLE_WM_HSCROLL(hwnd, wParam, lParam, fn) \
3985 ((fn)((hwnd), (HWND)(lParam), (UINT)(LOWORD(wParam)), (int)(short)HIWORD(wParam)), 0L)
3986# endif
3987# ifndef HANDLE_WM_DROPFILES
3988# define HANDLE_WM_DROPFILES(hwnd, wParam, lParam, fn) \
3989 ((fn)((hwnd), (HDROP)(wParam)), 0L)
3990# endif
3991# ifndef HANDLE_WM_CHAR
3992# define HANDLE_WM_CHAR(hwnd, wParam, lParam, fn) \
3993 ((fn)((hwnd), (TCHAR)(wParam), (int)(short)LOWORD(lParam)), 0L)
3994# endif
3995# ifndef HANDLE_WM_SYSDEADCHAR
3996# define HANDLE_WM_SYSDEADCHAR(hwnd, wParam, lParam, fn) \
3997 ((fn)((hwnd), (TCHAR)(wParam), (int)(short)LOWORD(lParam)), 0L)
3998# endif
3999# ifndef HANDLE_WM_DEADCHAR
4000# define HANDLE_WM_DEADCHAR(hwnd, wParam, lParam, fn) \
4001 ((fn)((hwnd), (TCHAR)(wParam), (int)(short)LOWORD(lParam)), 0L)
4002# endif
Bram Moolenaar734a8672019-12-02 22:49:38 +01004003#endif // __MINGW32__
Bram Moolenaar071d4272004-06-13 20:20:40 +00004004
4005
Bram Moolenaar734a8672019-12-02 22:49:38 +01004006// Some parameters for tearoff menus. All in pixels.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004007#define TEAROFF_PADDING_X 2
4008#define TEAROFF_BUTTON_PAD_X 8
4009#define TEAROFF_MIN_WIDTH 200
4010#define TEAROFF_SUBMENU_LABEL ">>"
4011#define TEAROFF_COLUMN_PADDING 3 // # spaces to pad column with.
4012
4013
Bram Moolenaar734a8672019-12-02 22:49:38 +01004014// For the Intellimouse:
Bram Moolenaar071d4272004-06-13 20:20:40 +00004015#ifndef WM_MOUSEWHEEL
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01004016# define WM_MOUSEWHEEL 0x20a
Bram Moolenaar071d4272004-06-13 20:20:40 +00004017#endif
4018
4019
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01004020#ifdef FEAT_BEVAL_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00004021# define ID_BEVAL_TOOLTIP 200
4022# define BEVAL_TEXT_LEN MAXPATHL
4023
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01004024# if (defined(_MSC_VER) && _MSC_VER < 1300) || !defined(MAXULONG_PTR)
Bram Moolenaar734a8672019-12-02 22:49:38 +01004025// Work around old versions of basetsd.h which wrongly declares
4026// UINT_PTR as unsigned long.
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01004027# undef UINT_PTR
4028# define UINT_PTR UINT
4029# endif
Bram Moolenaareb3593b2006-04-22 22:33:57 +00004030
Bram Moolenaar071d4272004-06-13 20:20:40 +00004031static BalloonEval *cur_beval = NULL;
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004032static UINT_PTR BevalTimerId = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004033static DWORD LastActivity = 0;
Bram Moolenaar45360022005-07-21 21:08:21 +00004034
Bram Moolenaar82881492012-11-20 16:53:39 +01004035
Bram Moolenaar45360022005-07-21 21:08:21 +00004036typedef struct tagNMTTDISPINFO_NEW
4037{
4038 NMHDR hdr;
Bram Moolenaar281daf62009-12-24 15:11:40 +00004039 LPSTR lpszText;
Bram Moolenaar45360022005-07-21 21:08:21 +00004040 char szText[80];
4041 HINSTANCE hinst;
4042 UINT uFlags;
4043 LPARAM lParam;
4044} NMTTDISPINFO_NEW;
4045
Bram Moolenaard385b5d2018-12-27 22:43:08 +01004046typedef struct tagTOOLINFOW_NEW
4047{
4048 UINT cbSize;
4049 UINT uFlags;
4050 HWND hwnd;
4051 UINT_PTR uId;
4052 RECT rect;
4053 HINSTANCE hinst;
4054 LPWSTR lpszText;
4055 LPARAM lParam;
4056 void *lpReserved;
4057} TOOLINFOW_NEW;
4058
4059typedef struct tagNMTTDISPINFOW_NEW
4060{
4061 NMHDR hdr;
4062 LPWSTR lpszText;
4063 WCHAR szText[80];
4064 HINSTANCE hinst;
4065 UINT uFlags;
4066 LPARAM lParam;
4067} NMTTDISPINFOW_NEW;
4068
Bram Moolenaard385b5d2018-12-27 22:43:08 +01004069
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01004070# ifndef TTM_SETMAXTIPWIDTH
4071# define TTM_SETMAXTIPWIDTH (WM_USER+24)
4072# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004073
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01004074# ifndef TTF_DI_SETITEM
4075# define TTF_DI_SETITEM 0x8000
4076# endif
Bram Moolenaar45360022005-07-21 21:08:21 +00004077
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01004078# ifndef TTN_GETDISPINFO
4079# define TTN_GETDISPINFO (TTN_FIRST - 0)
4080# endif
Bram Moolenaar45360022005-07-21 21:08:21 +00004081
Bram Moolenaar734a8672019-12-02 22:49:38 +01004082#endif // defined(FEAT_BEVAL_GUI)
Bram Moolenaar45360022005-07-21 21:08:21 +00004083
Bram Moolenaar2c7a7632007-05-10 18:19:11 +00004084#if defined(FEAT_TOOLBAR) || defined(FEAT_GUI_TABLINE)
Bram Moolenaar734a8672019-12-02 22:49:38 +01004085// Older MSVC compilers don't have LPNMTTDISPINFO[AW] thus we need to define
4086// it here if LPNMTTDISPINFO isn't defined.
K.Takatac81e9bf2022-01-16 14:15:49 +00004087// MinGW doesn't define LPNMTTDISPINFO but typedefs it. Thus we need to check
Bram Moolenaar734a8672019-12-02 22:49:38 +01004088// _MSC_VER.
Bram Moolenaar2c7a7632007-05-10 18:19:11 +00004089# if !defined(LPNMTTDISPINFO) && defined(_MSC_VER)
4090typedef struct tagNMTTDISPINFOA {
4091 NMHDR hdr;
4092 LPSTR lpszText;
4093 char szText[80];
4094 HINSTANCE hinst;
4095 UINT uFlags;
4096 LPARAM lParam;
4097} NMTTDISPINFOA, *LPNMTTDISPINFOA;
4098# define LPNMTTDISPINFO LPNMTTDISPINFOA
4099
Bram Moolenaar2c7a7632007-05-10 18:19:11 +00004100typedef struct tagNMTTDISPINFOW {
4101 NMHDR hdr;
4102 LPWSTR lpszText;
4103 WCHAR szText[80];
4104 HINSTANCE hinst;
4105 UINT uFlags;
4106 LPARAM lParam;
4107} NMTTDISPINFOW, *LPNMTTDISPINFOW;
Bram Moolenaar2c7a7632007-05-10 18:19:11 +00004108# endif
4109#endif
4110
Bram Moolenaarf9393ef2006-04-24 19:47:27 +00004111#ifndef TTN_GETDISPINFOW
4112# define TTN_GETDISPINFOW (TTN_FIRST - 10)
4113#endif
4114
Bram Moolenaar734a8672019-12-02 22:49:38 +01004115// Local variables:
Bram Moolenaar071d4272004-06-13 20:20:40 +00004116
4117#ifdef FEAT_MENU
4118static UINT s_menu_id = 100;
Bram Moolenaar786989b2010-10-27 12:15:33 +02004119#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004120
4121/*
4122 * Use the system font for dialogs and tear-off menus. Remove this line to
4123 * use DLG_FONT_NAME.
4124 */
Bram Moolenaar786989b2010-10-27 12:15:33 +02004125#define USE_SYSMENU_FONT
Bram Moolenaar071d4272004-06-13 20:20:40 +00004126
4127#define VIM_NAME "vim"
Bram Moolenaar071d4272004-06-13 20:20:40 +00004128#define VIM_CLASSW L"Vim"
4129
Bram Moolenaar734a8672019-12-02 22:49:38 +01004130// Initial size for the dialog template. For gui_mch_dialog() it's fixed,
4131// thus there should be room for every dialog. For tearoffs it's made bigger
4132// when needed.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004133#define DLG_ALLOC_SIZE 16 * 1024
4134
4135/*
4136 * stuff for dialogs, menus, tearoffs etc.
4137 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004138static PWORD
4139add_dialog_element(
4140 PWORD p,
4141 DWORD lStyle,
4142 WORD x,
4143 WORD y,
4144 WORD w,
4145 WORD h,
4146 WORD Id,
4147 WORD clss,
4148 const char *caption);
4149static LPWORD lpwAlign(LPWORD);
Bram Moolenaar6edeaf32017-09-26 14:46:04 +02004150static int nCopyAnsiToWideChar(LPWORD, LPSTR, BOOL);
Bram Moolenaar065bbac2016-02-20 13:08:46 +01004151#if defined(FEAT_MENU) && defined(FEAT_TEAROFF)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004152static void gui_mch_tearoff(char_u *title, vimmenu_T *menu, int initX, int initY);
Bram Moolenaar065bbac2016-02-20 13:08:46 +01004153#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004154static void get_dialog_font_metrics(void);
4155
4156static int dialog_default_button = -1;
4157
Bram Moolenaar734a8672019-12-02 22:49:38 +01004158// Intellimouse support
Bram Moolenaar071d4272004-06-13 20:20:40 +00004159static int mouse_scroll_lines = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004160
Bram Moolenaar071d4272004-06-13 20:20:40 +00004161#ifdef FEAT_TOOLBAR
4162static void initialise_toolbar(void);
K.Takatac81e9bf2022-01-16 14:15:49 +00004163static void update_toolbar_size(void);
Bram Moolenaar5f919ee2013-07-21 17:46:43 +02004164static LRESULT CALLBACK toolbar_wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004165static int get_toolbar_bitmap(vimmenu_T *menu);
K.Takatac81e9bf2022-01-16 14:15:49 +00004166#else
4167# define update_toolbar_size()
Bram Moolenaar071d4272004-06-13 20:20:40 +00004168#endif
4169
Bram Moolenaar3991dab2006-03-27 17:01:56 +00004170#ifdef FEAT_GUI_TABLINE
4171static void initialise_tabline(void);
Bram Moolenaar5f919ee2013-07-21 17:46:43 +02004172static LRESULT CALLBACK tabline_wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
Bram Moolenaar3991dab2006-03-27 17:01:56 +00004173#endif
4174
Bram Moolenaar071d4272004-06-13 20:20:40 +00004175#ifdef FEAT_MBYTE_IME
4176static LRESULT _OnImeComposition(HWND hwnd, WPARAM dbcs, LPARAM param);
4177static char_u *GetResultStr(HWND hwnd, int GCS, int *lenp);
4178#endif
4179#if defined(FEAT_MBYTE_IME) && defined(DYNAMIC_IME)
4180# ifdef NOIME
4181typedef struct tagCOMPOSITIONFORM {
4182 DWORD dwStyle;
4183 POINT ptCurrentPos;
4184 RECT rcArea;
4185} COMPOSITIONFORM, *PCOMPOSITIONFORM, NEAR *NPCOMPOSITIONFORM, FAR *LPCOMPOSITIONFORM;
4186typedef HANDLE HIMC;
4187# endif
4188
Bram Moolenaard857f0e2005-06-21 22:37:39 +00004189static HINSTANCE hLibImm = NULL;
Bram Moolenaard857f0e2005-06-21 22:37:39 +00004190static LONG (WINAPI *pImmGetCompositionStringW)(HIMC, DWORD, LPVOID, DWORD);
4191static HIMC (WINAPI *pImmGetContext)(HWND);
4192static HIMC (WINAPI *pImmAssociateContext)(HWND, HIMC);
4193static BOOL (WINAPI *pImmReleaseContext)(HWND, HIMC);
4194static BOOL (WINAPI *pImmGetOpenStatus)(HIMC);
4195static BOOL (WINAPI *pImmSetOpenStatus)(HIMC, BOOL);
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01004196static BOOL (WINAPI *pImmGetCompositionFontW)(HIMC, LPLOGFONTW);
4197static BOOL (WINAPI *pImmSetCompositionFontW)(HIMC, LPLOGFONTW);
Bram Moolenaard857f0e2005-06-21 22:37:39 +00004198static BOOL (WINAPI *pImmSetCompositionWindow)(HIMC, LPCOMPOSITIONFORM);
4199static BOOL (WINAPI *pImmGetConversionStatus)(HIMC, LPDWORD, LPDWORD);
Bram Moolenaarca003e12006-03-17 23:19:38 +00004200static BOOL (WINAPI *pImmSetConversionStatus)(HIMC, DWORD, DWORD);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004201static void dyn_imm_load(void);
4202#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004203# define pImmGetCompositionStringW ImmGetCompositionStringW
4204# define pImmGetContext ImmGetContext
4205# define pImmAssociateContext ImmAssociateContext
4206# define pImmReleaseContext ImmReleaseContext
4207# define pImmGetOpenStatus ImmGetOpenStatus
4208# define pImmSetOpenStatus ImmSetOpenStatus
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01004209# define pImmGetCompositionFontW ImmGetCompositionFontW
4210# define pImmSetCompositionFontW ImmSetCompositionFontW
Bram Moolenaar071d4272004-06-13 20:20:40 +00004211# define pImmSetCompositionWindow ImmSetCompositionWindow
4212# define pImmGetConversionStatus ImmGetConversionStatus
Bram Moolenaarca003e12006-03-17 23:19:38 +00004213# define pImmSetConversionStatus ImmSetConversionStatus
Bram Moolenaar071d4272004-06-13 20:20:40 +00004214#endif
4215
Bram Moolenaar071d4272004-06-13 20:20:40 +00004216#ifdef FEAT_MENU
4217/*
4218 * Figure out how high the menu bar is at the moment.
4219 */
4220 static int
4221gui_mswin_get_menu_height(
Bram Moolenaar734a8672019-12-02 22:49:38 +01004222 int fix_window) // If TRUE, resize window if menu height changed
Bram Moolenaar071d4272004-06-13 20:20:40 +00004223{
4224 static int old_menu_height = -1;
4225
4226 RECT rc1, rc2;
4227 int num;
4228 int menu_height;
4229
4230 if (gui.menu_is_active)
4231 num = GetMenuItemCount(s_menuBar);
4232 else
4233 num = 0;
4234
4235 if (num == 0)
4236 menu_height = 0;
Bram Moolenaar71371b12015-03-24 17:57:12 +01004237 else if (IsMinimized(s_hwnd))
4238 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01004239 // The height of the menu cannot be determined while the window is
4240 // minimized. Take the previous height if the menu is changed in that
4241 // state, to avoid that Vim's vertical window size accidentally
4242 // increases due to the unaccounted-for menu height.
Bram Moolenaar71371b12015-03-24 17:57:12 +01004243 menu_height = old_menu_height == -1 ? 0 : old_menu_height;
4244 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004245 else
4246 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +02004247 /*
4248 * In case 'lines' is set in _vimrc/_gvimrc window width doesn't
4249 * seem to have been set yet, so menu wraps in default window
4250 * width which is very narrow. Instead just return height of a
4251 * single menu item. Will still be wrong when the menu really
4252 * should wrap over more than one line.
4253 */
4254 GetMenuItemRect(s_hwnd, s_menuBar, 0, &rc1);
4255 if (gui.starting)
4256 menu_height = rc1.bottom - rc1.top + 1;
4257 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004258 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +02004259 GetMenuItemRect(s_hwnd, s_menuBar, num - 1, &rc2);
4260 menu_height = rc2.bottom - rc1.top + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004261 }
4262 }
4263
4264 if (fix_window && menu_height != old_menu_height)
Bram Moolenaarafa24992006-03-27 20:58:26 +00004265 gui_set_shellsize(FALSE, FALSE, RESIZE_VERT);
Bram Moolenaar71371b12015-03-24 17:57:12 +01004266 old_menu_height = menu_height;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004267
4268 return menu_height;
4269}
Bram Moolenaar734a8672019-12-02 22:49:38 +01004270#endif // FEAT_MENU
Bram Moolenaar071d4272004-06-13 20:20:40 +00004271
4272
4273/*
4274 * Setup for the Intellimouse
4275 */
4276 static void
4277init_mouse_wheel(void)
4278{
4279
4280#ifndef SPI_GETWHEELSCROLLLINES
4281# define SPI_GETWHEELSCROLLLINES 104
4282#endif
Bram Moolenaare7566042005-06-17 22:00:15 +00004283#ifndef SPI_SETWHEELSCROLLLINES
4284# define SPI_SETWHEELSCROLLLINES 105
4285#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004286
Bram Moolenaar734a8672019-12-02 22:49:38 +01004287#define VMOUSEZ_CLASSNAME "MouseZ" // hidden wheel window class
4288#define VMOUSEZ_TITLE "Magellan MSWHEEL" // hidden wheel window title
Bram Moolenaar071d4272004-06-13 20:20:40 +00004289#define VMSH_MOUSEWHEEL "MSWHEEL_ROLLMSG"
4290#define VMSH_SCROLL_LINES "MSH_SCROLL_LINES_MSG"
4291
Bram Moolenaar734a8672019-12-02 22:49:38 +01004292 mouse_scroll_lines = 3; // reasonable default
Bram Moolenaar071d4272004-06-13 20:20:40 +00004293
Bram Moolenaar734a8672019-12-02 22:49:38 +01004294 // if NT 4.0+ (or Win98) get scroll lines directly from system
Bram Moolenaarcea912a2016-10-12 14:20:24 +02004295 SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0,
4296 &mouse_scroll_lines, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004297}
4298
4299
Bram Moolenaarae20f342019-11-05 21:09:23 +01004300/*
4301 * Intellimouse wheel handler.
4302 * Treat a mouse wheel event as if it were a scroll request.
4303 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004304 static void
4305_OnMouseWheel(
4306 HWND hwnd,
4307 short zDelta)
4308{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004309 int i;
4310 int size;
4311 HWND hwndCtl;
Bram Moolenaarae20f342019-11-05 21:09:23 +01004312 win_T *wp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004313
Bram Moolenaar071d4272004-06-13 20:20:40 +00004314 if (mouse_scroll_lines == 0)
4315 init_mouse_wheel();
4316
Bram Moolenaarae20f342019-11-05 21:09:23 +01004317 wp = gui_mouse_window(FIND_POPUP);
4318
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01004319#ifdef FEAT_PROP_POPUP
Bram Moolenaarae20f342019-11-05 21:09:23 +01004320 if (wp != NULL && popup_is_popup(wp))
Bram Moolenaar0630bb62019-11-04 22:52:12 +01004321 {
Bram Moolenaarae20f342019-11-05 21:09:23 +01004322 cmdarg_T cap;
4323 oparg_T oa;
Bram Moolenaar0630bb62019-11-04 22:52:12 +01004324
Bram Moolenaarae20f342019-11-05 21:09:23 +01004325 // Mouse hovers over popup window, scroll it if possible.
4326 mouse_row = wp->w_winrow;
4327 mouse_col = wp->w_wincol;
Bram Moolenaara80faa82020-04-12 19:37:17 +02004328 CLEAR_FIELD(cap);
Bram Moolenaarae20f342019-11-05 21:09:23 +01004329 cap.arg = zDelta < 0 ? MSCR_UP : MSCR_DOWN;
4330 cap.cmdchar = zDelta < 0 ? K_MOUSEUP : K_MOUSEDOWN;
4331 clear_oparg(&oa);
4332 cap.oap = &oa;
4333 nv_mousescroll(&cap);
4334 update_screen(0);
4335 setcursor();
4336 out_flush();
4337 return;
Bram Moolenaar0630bb62019-11-04 22:52:12 +01004338 }
4339#endif
4340
Bram Moolenaarae20f342019-11-05 21:09:23 +01004341 if (wp == NULL || !p_scf)
4342 wp = curwin;
4343
4344 if (wp->w_scrollbars[SBAR_RIGHT].id != 0)
4345 hwndCtl = wp->w_scrollbars[SBAR_RIGHT].id;
4346 else if (wp->w_scrollbars[SBAR_LEFT].id != 0)
4347 hwndCtl = wp->w_scrollbars[SBAR_LEFT].id;
4348 else
4349 return;
4350 size = wp->w_height;
4351
Bram Moolenaara338adc2018-01-31 20:51:47 +01004352 mch_disable_flush();
Bram Moolenaar071d4272004-06-13 20:20:40 +00004353 if (mouse_scroll_lines > 0
4354 && mouse_scroll_lines < (size > 2 ? size - 2 : 1))
4355 {
4356 for (i = mouse_scroll_lines; i > 0; --i)
4357 _OnScroll(hwnd, hwndCtl, zDelta >= 0 ? SB_LINEUP : SB_LINEDOWN, 0);
4358 }
4359 else
4360 _OnScroll(hwnd, hwndCtl, zDelta >= 0 ? SB_PAGEUP : SB_PAGEDOWN, 0);
Bram Moolenaara338adc2018-01-31 20:51:47 +01004361 mch_enable_flush();
4362 gui_may_flush();
Bram Moolenaar071d4272004-06-13 20:20:40 +00004363}
4364
Bram Moolenaar551dbcc2006-04-25 22:13:59 +00004365#ifdef USE_SYSMENU_FONT
4366/*
4367 * Get Menu Font.
4368 * Return OK or FAIL.
4369 */
4370 static int
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01004371gui_w32_get_menu_font(LOGFONTW *lf)
Bram Moolenaar551dbcc2006-04-25 22:13:59 +00004372{
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01004373 NONCLIENTMETRICSW nm;
Bram Moolenaar551dbcc2006-04-25 22:13:59 +00004374
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01004375 nm.cbSize = sizeof(NONCLIENTMETRICSW);
4376 if (!SystemParametersInfoW(
Bram Moolenaar551dbcc2006-04-25 22:13:59 +00004377 SPI_GETNONCLIENTMETRICS,
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01004378 sizeof(NONCLIENTMETRICSW),
Bram Moolenaar551dbcc2006-04-25 22:13:59 +00004379 &nm,
4380 0))
4381 return FAIL;
4382 *lf = nm.lfMenuFont;
4383 return OK;
4384}
4385#endif
4386
4387
4388#if defined(FEAT_GUI_TABLINE) && defined(USE_SYSMENU_FONT)
4389/*
4390 * Set the GUI tabline font to the system menu font
4391 */
4392 static void
4393set_tabline_font(void)
4394{
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01004395 LOGFONTW lfSysmenu;
Bram Moolenaar551dbcc2006-04-25 22:13:59 +00004396 HFONT font;
4397 HWND hwnd;
4398 HDC hdc;
4399 HFONT hfntOld;
4400 TEXTMETRIC tm;
4401
4402 if (gui_w32_get_menu_font(&lfSysmenu) != OK)
4403 return;
4404
K.Takatac81e9bf2022-01-16 14:15:49 +00004405 lfSysmenu.lfHeight = adjust_fontsize_by_dpi(lfSysmenu.lfHeight);
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01004406 font = CreateFontIndirectW(&lfSysmenu);
Bram Moolenaar551dbcc2006-04-25 22:13:59 +00004407
4408 SendMessage(s_tabhwnd, WM_SETFONT, (WPARAM)font, TRUE);
4409
4410 /*
4411 * Compute the height of the font used for the tab text
4412 */
4413 hwnd = GetDesktopWindow();
4414 hdc = GetWindowDC(hwnd);
4415 hfntOld = SelectFont(hdc, font);
4416
4417 GetTextMetrics(hdc, &tm);
4418
4419 SelectFont(hdc, hfntOld);
4420 ReleaseDC(hwnd, hdc);
4421
4422 /*
4423 * The space used by the tab border and the space between the tab label
4424 * and the tab border is included as 7.
4425 */
4426 gui.tabline_height = tm.tmHeight + tm.tmInternalLeading + 7;
4427}
K.Takatac81e9bf2022-01-16 14:15:49 +00004428#else
4429# define set_tabline_font()
Bram Moolenaar551dbcc2006-04-25 22:13:59 +00004430#endif
4431
Bram Moolenaar520470a2005-06-16 21:59:56 +00004432/*
4433 * Invoked when a setting was changed.
4434 */
4435 static LRESULT CALLBACK
4436_OnSettingChange(UINT n)
4437{
4438 if (n == SPI_SETWHEELSCROLLLINES)
4439 SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0,
4440 &mouse_scroll_lines, 0);
Bram Moolenaar551dbcc2006-04-25 22:13:59 +00004441 if (n == SPI_SETNONCLIENTMETRICS)
4442 set_tabline_font();
Bram Moolenaar520470a2005-06-16 21:59:56 +00004443 return 0;
4444}
4445
Bram Moolenaar071d4272004-06-13 20:20:40 +00004446#ifdef FEAT_NETBEANS_INTG
4447 static void
4448_OnWindowPosChanged(
4449 HWND hwnd,
4450 const LPWINDOWPOS lpwpos)
4451{
4452 static int x = 0, y = 0, cx = 0, cy = 0;
Bram Moolenaarf12d9832016-01-29 21:11:25 +01004453 extern int WSInitialized;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004454
4455 if (WSInitialized && (lpwpos->x != x || lpwpos->y != y
4456 || lpwpos->cx != cx || lpwpos->cy != cy))
4457 {
4458 x = lpwpos->x;
4459 y = lpwpos->y;
4460 cx = lpwpos->cx;
4461 cy = lpwpos->cy;
4462 netbeans_frame_moved(x, y);
4463 }
Bram Moolenaar734a8672019-12-02 22:49:38 +01004464 // Allow to send WM_SIZE and WM_MOVE
K.Takata4ac893f2022-01-20 12:44:28 +00004465 FORWARD_WM_WINDOWPOSCHANGED(hwnd, lpwpos, DefWindowProcW);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004466}
4467#endif
4468
K.Takata4f2417f2021-06-05 16:25:32 +02004469
4470static HWND hwndTip = NULL;
4471
4472 static void
4473show_sizing_tip(int cols, int rows)
4474{
4475 TOOLINFOA ti = {sizeof(ti)};
4476 char buf[32];
4477
4478 ti.hwnd = s_hwnd;
4479 ti.uId = (UINT_PTR)s_hwnd;
4480 ti.uFlags = TTF_SUBCLASS | TTF_IDISHWND;
4481 ti.lpszText = buf;
4482 sprintf(buf, "%dx%d", cols, rows);
4483 if (hwndTip == NULL)
4484 {
4485 hwndTip = CreateWindowExA(0, TOOLTIPS_CLASSA, NULL,
4486 WS_POPUP | TTS_ALWAYSTIP | TTS_NOPREFIX,
4487 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
4488 s_hwnd, NULL, GetModuleHandle(NULL), NULL);
4489 SendMessage(hwndTip, TTM_ADDTOOL, 0, (LPARAM)&ti);
4490 SendMessage(hwndTip, TTM_TRACKACTIVATE, TRUE, (LPARAM)&ti);
4491 }
4492 else
4493 {
4494 SendMessage(hwndTip, TTM_UPDATETIPTEXT, 0, (LPARAM)&ti);
4495 }
4496 SendMessage(hwndTip, TTM_POPUP, 0, 0);
4497}
4498
4499 static void
4500destroy_sizing_tip(void)
4501{
4502 if (hwndTip != NULL)
4503 {
4504 DestroyWindow(hwndTip);
4505 hwndTip = NULL;
4506 }
4507}
4508
Bram Moolenaar071d4272004-06-13 20:20:40 +00004509 static int
4510_DuringSizing(
Bram Moolenaar071d4272004-06-13 20:20:40 +00004511 UINT fwSide,
4512 LPRECT lprc)
4513{
4514 int w, h;
4515 int valid_w, valid_h;
4516 int w_offset, h_offset;
K.Takata4f2417f2021-06-05 16:25:32 +02004517 int cols, rows;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004518
4519 w = lprc->right - lprc->left;
4520 h = lprc->bottom - lprc->top;
K.Takata4f2417f2021-06-05 16:25:32 +02004521 gui_mswin_get_valid_dimensions(w, h, &valid_w, &valid_h, &cols, &rows);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004522 w_offset = w - valid_w;
4523 h_offset = h - valid_h;
4524
4525 if (fwSide == WMSZ_LEFT || fwSide == WMSZ_TOPLEFT
4526 || fwSide == WMSZ_BOTTOMLEFT)
4527 lprc->left += w_offset;
4528 else if (fwSide == WMSZ_RIGHT || fwSide == WMSZ_TOPRIGHT
4529 || fwSide == WMSZ_BOTTOMRIGHT)
4530 lprc->right -= w_offset;
4531
4532 if (fwSide == WMSZ_TOP || fwSide == WMSZ_TOPLEFT
4533 || fwSide == WMSZ_TOPRIGHT)
4534 lprc->top += h_offset;
4535 else if (fwSide == WMSZ_BOTTOM || fwSide == WMSZ_BOTTOMLEFT
4536 || fwSide == WMSZ_BOTTOMRIGHT)
4537 lprc->bottom -= h_offset;
K.Takata4f2417f2021-06-05 16:25:32 +02004538
4539 show_sizing_tip(cols, rows);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004540 return TRUE;
4541}
4542
K.Takata92000e22022-01-20 15:10:57 +00004543#ifdef FEAT_GUI_TABLINE
4544 static void
4545_OnRButtonUp(HWND hwnd, int x, int y, UINT keyFlags)
4546{
4547 if (gui_mch_showing_tabline())
4548 {
4549 POINT pt;
4550 RECT rect;
4551
4552 /*
4553 * If the cursor is on the tabline, display the tab menu
4554 */
4555 GetCursorPos(&pt);
4556 GetWindowRect(s_textArea, &rect);
4557 if (pt.y < rect.top)
4558 {
4559 show_tabline_popup_menu();
4560 return;
4561 }
4562 }
4563 FORWARD_WM_RBUTTONUP(hwnd, x, y, keyFlags, DefWindowProcW);
4564}
4565
4566 static void
4567_OnLButtonDown(HWND hwnd, BOOL fDoubleClick, int x, int y, UINT keyFlags)
4568{
4569 /*
4570 * If the user double clicked the tabline, create a new tab
4571 */
4572 if (gui_mch_showing_tabline())
4573 {
4574 POINT pt;
4575 RECT rect;
4576
4577 GetCursorPos(&pt);
4578 GetWindowRect(s_textArea, &rect);
4579 if (pt.y < rect.top)
4580 send_tabline_menu_event(0, TABLINE_MENU_NEW);
4581 }
4582 FORWARD_WM_LBUTTONDOWN(hwnd, fDoubleClick, x, y, keyFlags, DefWindowProcW);
4583}
4584#endif
4585
4586 static UINT
4587_OnNCHitTest(HWND hwnd, int xPos, int yPos)
4588{
4589 UINT result;
4590 int x, y;
4591
4592 result = FORWARD_WM_NCHITTEST(hwnd, xPos, yPos, DefWindowProcW);
4593 if (result != HTCLIENT)
4594 return result;
4595
4596#ifdef FEAT_GUI_TABLINE
4597 if (gui_mch_showing_tabline())
4598 {
4599 RECT rct;
4600
4601 // If the cursor is on the GUI tabline, don't process this event
4602 GetWindowRect(s_textArea, &rct);
4603 if (yPos < rct.top)
4604 return result;
4605 }
4606#endif
4607 (void)gui_mch_get_winpos(&x, &y);
4608 xPos -= x;
4609
4610 if (xPos < 48) // <VN> TODO should use system metric?
4611 return HTBOTTOMLEFT;
4612 else
4613 return HTBOTTOMRIGHT;
4614}
4615
4616#if defined(FEAT_TOOLBAR) || defined(FEAT_GUI_TABLINE)
4617 static LRESULT
4618_OnNotify(HWND hwnd, UINT id, NMHDR *hdr)
4619{
4620 switch (hdr->code)
4621 {
4622 case TTN_GETDISPINFOW:
4623 case TTN_GETDISPINFO:
4624 {
4625 char_u *str = NULL;
4626 static void *tt_text = NULL;
4627
4628 VIM_CLEAR(tt_text);
4629
4630# ifdef FEAT_GUI_TABLINE
4631 if (gui_mch_showing_tabline()
4632 && hdr->hwndFrom == TabCtrl_GetToolTips(s_tabhwnd))
4633 {
4634 POINT pt;
4635 /*
4636 * Mouse is over the GUI tabline. Display the
4637 * tooltip for the tab under the cursor
4638 *
4639 * Get the cursor position within the tab control
4640 */
4641 GetCursorPos(&pt);
4642 if (ScreenToClient(s_tabhwnd, &pt) != 0)
4643 {
4644 TCHITTESTINFO htinfo;
4645 int idx;
4646
4647 /*
4648 * Get the tab under the cursor
4649 */
4650 htinfo.pt.x = pt.x;
4651 htinfo.pt.y = pt.y;
4652 idx = TabCtrl_HitTest(s_tabhwnd, &htinfo);
4653 if (idx != -1)
4654 {
4655 tabpage_T *tp;
4656
4657 tp = find_tabpage(idx + 1);
4658 if (tp != NULL)
4659 {
4660 get_tabline_label(tp, TRUE);
4661 str = NameBuff;
4662 }
4663 }
4664 }
4665 }
4666# endif
4667# ifdef FEAT_TOOLBAR
4668# ifdef FEAT_GUI_TABLINE
4669 else
4670# endif
4671 {
4672 UINT idButton;
4673 vimmenu_T *pMenu;
4674
4675 idButton = (UINT) hdr->idFrom;
4676 pMenu = gui_mswin_find_menu(root_menu, idButton);
4677 if (pMenu)
4678 str = pMenu->strings[MENU_INDEX_TIP];
4679 }
4680# endif
4681 if (str == NULL)
4682 break;
4683
4684 // Set the maximum width, this also enables using \n for
4685 // line break.
4686 SendMessage(hdr->hwndFrom, TTM_SETMAXTIPWIDTH, 0, 500);
4687
4688 if (hdr->code == TTN_GETDISPINFOW)
4689 {
4690 LPNMTTDISPINFOW lpdi = (LPNMTTDISPINFOW)hdr;
4691
4692 tt_text = enc_to_utf16(str, NULL);
4693 lpdi->lpszText = tt_text;
4694 // can't show tooltip if failed
4695 }
4696 else
4697 {
4698 LPNMTTDISPINFO lpdi = (LPNMTTDISPINFO)hdr;
4699
4700 if (STRLEN(str) < sizeof(lpdi->szText)
4701 || ((tt_text = vim_strsave(str)) == NULL))
4702 vim_strncpy((char_u *)lpdi->szText, str,
4703 sizeof(lpdi->szText) - 1);
4704 else
4705 lpdi->lpszText = tt_text;
4706 }
4707 }
4708 break;
4709
4710# ifdef FEAT_GUI_TABLINE
4711 case TCN_SELCHANGE:
4712 if (gui_mch_showing_tabline() && (hdr->hwndFrom == s_tabhwnd))
4713 {
4714 send_tabline_event(TabCtrl_GetCurSel(s_tabhwnd) + 1);
4715 return 0L;
4716 }
4717 break;
4718
4719 case NM_RCLICK:
4720 if (gui_mch_showing_tabline() && (hdr->hwndFrom == s_tabhwnd))
4721 {
4722 show_tabline_popup_menu();
4723 return 0L;
4724 }
4725 break;
4726# endif
4727
4728 default:
4729 break;
4730 }
4731 return DefWindowProcW(hwnd, WM_NOTIFY, (WPARAM)id, (LPARAM)hdr);
4732}
4733#endif
4734
4735#if defined(MENUHINTS) && defined(FEAT_MENU)
4736 static LRESULT
4737_OnMenuSelect(HWND hwnd, WPARAM wParam, LPARAM lParam)
4738{
4739 if (((UINT) HIWORD(wParam)
4740 & (0xffff ^ (MF_MOUSESELECT + MF_BITMAP + MF_POPUP)))
4741 == MF_HILITE
4742 && (State & CMDLINE) == 0)
4743 {
4744 UINT idButton;
4745 vimmenu_T *pMenu;
4746 static int did_menu_tip = FALSE;
4747
4748 if (did_menu_tip)
4749 {
4750 msg_clr_cmdline();
4751 setcursor();
4752 out_flush();
4753 did_menu_tip = FALSE;
4754 }
4755
4756 idButton = (UINT)LOWORD(wParam);
4757 pMenu = gui_mswin_find_menu(root_menu, idButton);
4758 if (pMenu != NULL && pMenu->strings[MENU_INDEX_TIP] != 0
4759 && GetMenuState(s_menuBar, pMenu->id, MF_BYCOMMAND) != -1)
4760 {
4761 ++msg_hist_off;
4762 msg((char *)pMenu->strings[MENU_INDEX_TIP]);
4763 --msg_hist_off;
4764 setcursor();
4765 out_flush();
4766 did_menu_tip = TRUE;
4767 }
4768 return 0L;
4769 }
4770 return DefWindowProcW(hwnd, WM_MENUSELECT, wParam, lParam);
4771}
4772#endif
4773
K.Takatac81e9bf2022-01-16 14:15:49 +00004774 static LRESULT
4775_OnDpiChanged(HWND hwnd, UINT xdpi, UINT ydpi, RECT *rc)
4776{
4777 s_dpi = ydpi;
4778 s_in_dpichanged = TRUE;
4779 //TRACE("DPI: %d", ydpi);
4780
4781 update_scrollbar_size();
4782 update_toolbar_size();
4783 set_tabline_font();
4784
4785 gui_init_font(*p_guifont == NUL ? hl_get_font_name() : p_guifont, FALSE);
4786 gui_get_wide_font();
4787 gui_mswin_get_menu_height(FALSE);
4788#ifdef FEAT_MBYTE_IME
4789 im_set_position(gui.row, gui.col);
4790#endif
4791 InvalidateRect(hwnd, NULL, TRUE);
4792
4793 s_in_dpichanged = FALSE;
4794 return 0L;
4795}
Bram Moolenaar071d4272004-06-13 20:20:40 +00004796
4797
4798 static LRESULT CALLBACK
4799_WndProc(
4800 HWND hwnd,
4801 UINT uMsg,
4802 WPARAM wParam,
4803 LPARAM lParam)
4804{
4805 /*
4806 TRACE("WndProc: hwnd = %08x, msg = %x, wParam = %x, lParam = %x\n",
4807 hwnd, uMsg, wParam, lParam);
4808 */
4809
4810 HandleMouseHide(uMsg, lParam);
4811
4812 s_uMsg = uMsg;
4813 s_wParam = wParam;
4814 s_lParam = lParam;
4815
4816 switch (uMsg)
4817 {
4818 HANDLE_MSG(hwnd, WM_DEADCHAR, _OnDeadChar);
4819 HANDLE_MSG(hwnd, WM_SYSDEADCHAR, _OnDeadChar);
Bram Moolenaar734a8672019-12-02 22:49:38 +01004820 // HANDLE_MSG(hwnd, WM_ACTIVATE, _OnActivate);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004821 HANDLE_MSG(hwnd, WM_CLOSE, _OnClose);
Bram Moolenaar734a8672019-12-02 22:49:38 +01004822 // HANDLE_MSG(hwnd, WM_COMMAND, _OnCommand);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004823 HANDLE_MSG(hwnd, WM_DESTROY, _OnDestroy);
4824 HANDLE_MSG(hwnd, WM_DROPFILES, _OnDropFiles);
4825 HANDLE_MSG(hwnd, WM_HSCROLL, _OnScroll);
4826 HANDLE_MSG(hwnd, WM_KILLFOCUS, _OnKillFocus);
4827#ifdef FEAT_MENU
4828 HANDLE_MSG(hwnd, WM_COMMAND, _OnMenu);
4829#endif
Bram Moolenaar734a8672019-12-02 22:49:38 +01004830 // HANDLE_MSG(hwnd, WM_MOVE, _OnMove);
4831 // HANDLE_MSG(hwnd, WM_NCACTIVATE, _OnNCActivate);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004832 HANDLE_MSG(hwnd, WM_SETFOCUS, _OnSetFocus);
4833 HANDLE_MSG(hwnd, WM_SIZE, _OnSize);
Bram Moolenaar734a8672019-12-02 22:49:38 +01004834 // HANDLE_MSG(hwnd, WM_SYSCOMMAND, _OnSysCommand);
4835 // HANDLE_MSG(hwnd, WM_SYSKEYDOWN, _OnAltKey);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004836 HANDLE_MSG(hwnd, WM_VSCROLL, _OnScroll);
4837 // HANDLE_MSG(hwnd, WM_WINDOWPOSCHANGING, _OnWindowPosChanging);
4838 HANDLE_MSG(hwnd, WM_ACTIVATEAPP, _OnActivateApp);
4839#ifdef FEAT_NETBEANS_INTG
4840 HANDLE_MSG(hwnd, WM_WINDOWPOSCHANGED, _OnWindowPosChanged);
4841#endif
Bram Moolenaarafa24992006-03-27 20:58:26 +00004842#ifdef FEAT_GUI_TABLINE
K.Takata92000e22022-01-20 15:10:57 +00004843 HANDLE_MSG(hwnd, WM_RBUTTONUP, _OnRButtonUp);
4844 HANDLE_MSG(hwnd, WM_LBUTTONDBLCLK, _OnLButtonDown);
Bram Moolenaarafa24992006-03-27 20:58:26 +00004845#endif
K.Takata92000e22022-01-20 15:10:57 +00004846 HANDLE_MSG(hwnd, WM_NCHITTEST, _OnNCHitTest);
Bram Moolenaarafa24992006-03-27 20:58:26 +00004847
Bram Moolenaar734a8672019-12-02 22:49:38 +01004848 case WM_QUERYENDSESSION: // System wants to go down.
4849 gui_shell_closed(); // Will exit when no changed buffers.
4850 return FALSE; // Do NOT allow system to go down.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004851
4852 case WM_ENDSESSION:
Bram Moolenaar734a8672019-12-02 22:49:38 +01004853 if (wParam) // system only really goes down when wParam is TRUE
Bram Moolenaar213ae482011-12-15 21:51:36 +01004854 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00004855 _OnEndSession();
Bram Moolenaar213ae482011-12-15 21:51:36 +01004856 return 0L;
4857 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004858 break;
4859
4860 case WM_CHAR:
Bram Moolenaar734a8672019-12-02 22:49:38 +01004861 // Don't use HANDLE_MSG() for WM_CHAR, it truncates wParam to a single
4862 // byte while we want the UTF-16 character value.
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004863 _OnChar(hwnd, (UINT)wParam, (int)(short)LOWORD(lParam));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004864 return 0L;
4865
4866 case WM_SYSCHAR:
4867 /*
4868 * if 'winaltkeys' is "no", or it's "menu" and it's not a menu
4869 * shortcut key, handle like a typed ALT key, otherwise call Windows
4870 * ALT key handling.
4871 */
4872#ifdef FEAT_MENU
4873 if ( !gui.menu_is_active
4874 || p_wak[0] == 'n'
4875 || (p_wak[0] == 'm' && !gui_is_menu_shortcut((int)wParam))
4876 )
4877#endif
4878 {
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004879 _OnSysChar(hwnd, (UINT)wParam, (int)(short)LOWORD(lParam));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004880 return 0L;
4881 }
4882#ifdef FEAT_MENU
4883 else
K.Takata4ac893f2022-01-20 12:44:28 +00004884 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004885#endif
4886
4887 case WM_SYSKEYUP:
4888#ifdef FEAT_MENU
Bram Moolenaar734a8672019-12-02 22:49:38 +01004889 // This used to be done only when menu is active: ALT key is used for
4890 // that. But that caused problems when menu is disabled and using
4891 // Alt-Tab-Esc: get into a strange state where no mouse-moved events
4892 // are received, mouse pointer remains hidden.
K.Takata4ac893f2022-01-20 12:44:28 +00004893 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004894#else
Bram Moolenaar213ae482011-12-15 21:51:36 +01004895 return 0L;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004896#endif
4897
K.Takata4f2417f2021-06-05 16:25:32 +02004898 case WM_EXITSIZEMOVE:
4899 destroy_sizing_tip();
4900 break;
4901
Bram Moolenaar734a8672019-12-02 22:49:38 +01004902 case WM_SIZING: // HANDLE_MSG doesn't seem to handle this one
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +00004903 return _DuringSizing((UINT)wParam, (LPRECT)lParam);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004904
4905 case WM_MOUSEWHEEL:
4906 _OnMouseWheel(hwnd, HIWORD(wParam));
Bram Moolenaar213ae482011-12-15 21:51:36 +01004907 return 0L;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004908
Bram Moolenaar734a8672019-12-02 22:49:38 +01004909 // Notification for change in SystemParametersInfo()
Bram Moolenaar520470a2005-06-16 21:59:56 +00004910 case WM_SETTINGCHANGE:
4911 return _OnSettingChange((UINT)wParam);
4912
Bram Moolenaar3991dab2006-03-27 17:01:56 +00004913#if defined(FEAT_TOOLBAR) || defined(FEAT_GUI_TABLINE)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004914 case WM_NOTIFY:
K.Takata92000e22022-01-20 15:10:57 +00004915 return _OnNotify(hwnd, (UINT)wParam, (NMHDR*)lParam);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004916#endif
K.Takata92000e22022-01-20 15:10:57 +00004917
Bram Moolenaar071d4272004-06-13 20:20:40 +00004918#if defined(MENUHINTS) && defined(FEAT_MENU)
4919 case WM_MENUSELECT:
K.Takata92000e22022-01-20 15:10:57 +00004920 return _OnMenuSelect(hwnd, wParam, lParam);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004921#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004922
4923#ifdef FEAT_MBYTE_IME
4924 case WM_IME_NOTIFY:
4925 if (!_OnImeNotify(hwnd, (DWORD)wParam, (DWORD)lParam))
K.Takata4ac893f2022-01-20 12:44:28 +00004926 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
Bram Moolenaar213ae482011-12-15 21:51:36 +01004927 return 1L;
4928
Bram Moolenaar071d4272004-06-13 20:20:40 +00004929 case WM_IME_COMPOSITION:
4930 if (!_OnImeComposition(hwnd, wParam, lParam))
K.Takata4ac893f2022-01-20 12:44:28 +00004931 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
Bram Moolenaar213ae482011-12-15 21:51:36 +01004932 return 1L;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004933#endif
K.Takatac81e9bf2022-01-16 14:15:49 +00004934 case WM_DPICHANGED:
4935 return _OnDpiChanged(hwnd, (UINT)LOWORD(wParam), (UINT)HIWORD(wParam),
4936 (RECT*)lParam);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004937
4938 default:
Bram Moolenaar071d4272004-06-13 20:20:40 +00004939#ifdef MSWIN_FIND_REPLACE
Bram Moolenaarcea912a2016-10-12 14:20:24 +02004940 if (uMsg == s_findrep_msg && s_findrep_msg != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004941 _OnFindRepl();
Bram Moolenaar071d4272004-06-13 20:20:40 +00004942#endif
K.Takata92000e22022-01-20 15:10:57 +00004943 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004944 }
4945
K.Takata4ac893f2022-01-20 12:44:28 +00004946 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004947}
4948
4949/*
4950 * End of call-back routines
4951 */
4952
Bram Moolenaar734a8672019-12-02 22:49:38 +01004953// parent window, if specified with -P
Bram Moolenaar071d4272004-06-13 20:20:40 +00004954HWND vim_parent_hwnd = NULL;
4955
4956 static BOOL CALLBACK
4957FindWindowTitle(HWND hwnd, LPARAM lParam)
4958{
4959 char buf[2048];
4960 char *title = (char *)lParam;
4961
4962 if (GetWindowText(hwnd, buf, sizeof(buf)))
4963 {
4964 if (strstr(buf, title) != NULL)
4965 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01004966 // Found it. Store the window ref. and quit searching if MDI
4967 // works.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004968 vim_parent_hwnd = FindWindowEx(hwnd, NULL, "MDIClient", NULL);
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00004969 if (vim_parent_hwnd != NULL)
4970 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004971 }
4972 }
Bram Moolenaar734a8672019-12-02 22:49:38 +01004973 return TRUE; // continue searching
Bram Moolenaar071d4272004-06-13 20:20:40 +00004974}
4975
4976/*
4977 * Invoked for '-P "title"' argument: search for parent application to open
4978 * our window in.
4979 */
4980 void
4981gui_mch_set_parent(char *title)
4982{
4983 EnumWindows(FindWindowTitle, (LPARAM)title);
4984 if (vim_parent_hwnd == NULL)
4985 {
Bram Moolenaara6f79292022-01-04 21:30:47 +00004986 semsg(_(e_cannot_find_window_title_str), title);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004987 mch_exit(2);
4988 }
4989}
4990
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +00004991#ifndef FEAT_OLE
Bram Moolenaar071d4272004-06-13 20:20:40 +00004992 static void
4993ole_error(char *arg)
4994{
Bram Moolenaaraea02fa2007-05-04 20:29:09 +00004995 char buf[IOSIZE];
4996
Bram Moolenaar0b75f7c2019-05-08 22:28:46 +02004997# ifdef VIMDLL
4998 gui.in_use = mch_is_gui_executable();
4999# endif
5000
Bram Moolenaar734a8672019-12-02 22:49:38 +01005001 // Can't use emsg() here, we have not finished initialisation yet.
Bram Moolenaaraea02fa2007-05-04 20:29:09 +00005002 vim_snprintf(buf, IOSIZE,
Bram Moolenaarcbadefe2022-01-01 19:33:50 +00005003 _(e_argument_not_supported_str_use_ole_version), arg);
Bram Moolenaaraea02fa2007-05-04 20:29:09 +00005004 mch_errmsg(buf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005005}
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +00005006#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005007
Bram Moolenaarafde13b2019-04-28 19:46:49 +02005008#if defined(GUI_MAY_SPAWN) || defined(PROTO)
5009 static char *
5010gvim_error(void)
5011{
Bram Moolenaard82a47d2022-01-05 20:24:39 +00005012 char *msg = _(e_gui_cannot_be_used_cannot_execute_gvim_exe);
Bram Moolenaarafde13b2019-04-28 19:46:49 +02005013
5014 if (starting)
5015 {
5016 mch_errmsg(msg);
5017 mch_errmsg("\n");
5018 mch_exit(2);
5019 }
5020 return msg;
5021}
5022
5023 char *
5024gui_mch_do_spawn(char_u *arg)
5025{
5026 int len;
5027# if defined(FEAT_SESSION) && defined(EXPERIMENTAL_GUI_CMD)
5028 char_u *session = NULL;
5029 LPWSTR tofree1 = NULL;
5030# endif
5031 WCHAR name[MAX_PATH];
5032 LPWSTR cmd, newcmd = NULL, p, warg, tofree2 = NULL;
5033 STARTUPINFOW si = {sizeof(si)};
5034 PROCESS_INFORMATION pi;
5035
5036 if (!GetModuleFileNameW(g_hinst, name, MAX_PATH))
5037 goto error;
5038 p = wcsrchr(name, L'\\');
5039 if (p == NULL)
5040 goto error;
5041 // Replace the executable name from vim(d).exe to gvim(d).exe.
5042# ifdef DEBUG
5043 wcscpy(p + 1, L"gvimd.exe");
5044# else
5045 wcscpy(p + 1, L"gvim.exe");
5046# endif
5047
5048# if defined(FEAT_SESSION) && defined(EXPERIMENTAL_GUI_CMD)
5049 if (starting)
5050# endif
5051 {
5052 // Pass the command line to the new process.
5053 p = GetCommandLineW();
5054 // Skip 1st argument.
5055 while (*p && *p != L' ' && *p != L'\t')
5056 {
5057 if (*p == L'"')
5058 {
5059 while (*p && *p != L'"')
5060 ++p;
5061 if (*p)
5062 ++p;
5063 }
5064 else
5065 ++p;
5066 }
5067 cmd = p;
5068 }
5069# if defined(FEAT_SESSION) && defined(EXPERIMENTAL_GUI_CMD)
5070 else
5071 {
5072 // Create a session file and pass it to the new process.
5073 LPWSTR wsession;
5074 char_u *savebg;
5075 int ret;
5076
5077 session = vim_tempname('s', FALSE);
5078 if (session == NULL)
5079 goto error;
5080 savebg = p_bg;
5081 p_bg = vim_strsave((char_u *)"light"); // Set 'bg' to "light".
5082 ret = write_session_file(session);
5083 vim_free(p_bg);
5084 p_bg = savebg;
5085 if (!ret)
5086 goto error;
5087 wsession = enc_to_utf16(session, NULL);
5088 if (wsession == NULL)
5089 goto error;
5090 len = (int)wcslen(wsession) * 2 + 27 + 1;
Bram Moolenaarc799fe22019-05-28 23:08:19 +02005091 cmd = ALLOC_MULT(WCHAR, len);
Bram Moolenaarafde13b2019-04-28 19:46:49 +02005092 if (cmd == NULL)
5093 {
5094 vim_free(wsession);
5095 goto error;
5096 }
5097 tofree1 = cmd;
5098 _snwprintf(cmd, len, L" -S \"%s\" -c \"call delete('%s')\"",
5099 wsession, wsession);
5100 vim_free(wsession);
5101 }
5102# endif
5103
5104 // Check additional arguments to the `:gui` command.
5105 if (arg != NULL)
5106 {
5107 warg = enc_to_utf16(arg, NULL);
5108 if (warg == NULL)
5109 goto error;
5110 tofree2 = warg;
5111 }
5112 else
5113 warg = L"";
5114
5115 // Set up the new command line.
5116 len = (int)wcslen(name) + (int)wcslen(cmd) + (int)wcslen(warg) + 4;
Bram Moolenaarc799fe22019-05-28 23:08:19 +02005117 newcmd = ALLOC_MULT(WCHAR, len);
Bram Moolenaarafde13b2019-04-28 19:46:49 +02005118 if (newcmd == NULL)
5119 goto error;
5120 _snwprintf(newcmd, len, L"\"%s\"%s %s", name, cmd, warg);
5121
5122 // Spawn a new GUI process.
5123 if (!CreateProcessW(NULL, newcmd, NULL, NULL, TRUE, 0,
5124 NULL, NULL, &si, &pi))
5125 goto error;
5126 CloseHandle(pi.hProcess);
5127 CloseHandle(pi.hThread);
5128 mch_exit(0);
5129
5130error:
5131# if defined(FEAT_SESSION) && defined(EXPERIMENTAL_GUI_CMD)
5132 if (session)
5133 mch_remove(session);
5134 vim_free(session);
5135 vim_free(tofree1);
5136# endif
5137 vim_free(newcmd);
5138 vim_free(tofree2);
5139 return gvim_error();
5140}
5141#endif
5142
Bram Moolenaar071d4272004-06-13 20:20:40 +00005143/*
5144 * Parse the GUI related command-line arguments. Any arguments used are
5145 * deleted from argv, and *argc is decremented accordingly. This is called
K.Takataeeec2542021-06-02 13:28:16 +02005146 * when Vim is started, whether or not the GUI has been started.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005147 */
5148 void
5149gui_mch_prepare(int *argc, char **argv)
5150{
5151 int silent = FALSE;
5152 int idx;
5153
Bram Moolenaar734a8672019-12-02 22:49:38 +01005154 // Check for special OLE command line parameters
Bram Moolenaar071d4272004-06-13 20:20:40 +00005155 if ((*argc == 2 || *argc == 3) && (argv[1][0] == '-' || argv[1][0] == '/'))
5156 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01005157 // Check for a "-silent" argument first.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005158 if (*argc == 3 && STRICMP(argv[1] + 1, "silent") == 0
5159 && (argv[2][0] == '-' || argv[2][0] == '/'))
5160 {
5161 silent = TRUE;
5162 idx = 2;
5163 }
5164 else
5165 idx = 1;
5166
Bram Moolenaar734a8672019-12-02 22:49:38 +01005167 // Register Vim as an OLE Automation server
Bram Moolenaar071d4272004-06-13 20:20:40 +00005168 if (STRICMP(argv[idx] + 1, "register") == 0)
5169 {
5170#ifdef FEAT_OLE
5171 RegisterMe(silent);
5172 mch_exit(0);
5173#else
5174 if (!silent)
5175 ole_error("register");
5176 mch_exit(2);
5177#endif
5178 }
5179
Bram Moolenaar734a8672019-12-02 22:49:38 +01005180 // Unregister Vim as an OLE Automation server
Bram Moolenaar071d4272004-06-13 20:20:40 +00005181 if (STRICMP(argv[idx] + 1, "unregister") == 0)
5182 {
5183#ifdef FEAT_OLE
5184 UnregisterMe(!silent);
5185 mch_exit(0);
5186#else
5187 if (!silent)
5188 ole_error("unregister");
5189 mch_exit(2);
5190#endif
5191 }
5192
Bram Moolenaar734a8672019-12-02 22:49:38 +01005193 // Ignore an -embedding argument. It is only relevant if the
5194 // application wants to treat the case when it is started manually
5195 // differently from the case where it is started via automation (and
5196 // we don't).
Bram Moolenaar071d4272004-06-13 20:20:40 +00005197 if (STRICMP(argv[idx] + 1, "embedding") == 0)
5198 {
5199#ifdef FEAT_OLE
5200 *argc = 1;
5201#else
5202 ole_error("embedding");
5203 mch_exit(2);
5204#endif
5205 }
5206 }
5207
5208#ifdef FEAT_OLE
5209 {
5210 int bDoRestart = FALSE;
5211
5212 InitOLE(&bDoRestart);
Bram Moolenaar734a8672019-12-02 22:49:38 +01005213 // automatically exit after registering
Bram Moolenaar071d4272004-06-13 20:20:40 +00005214 if (bDoRestart)
5215 mch_exit(0);
5216 }
5217#endif
5218
5219#ifdef FEAT_NETBEANS_INTG
5220 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01005221 // stolen from gui_x11.c
Bram Moolenaar071d4272004-06-13 20:20:40 +00005222 int arg;
5223
5224 for (arg = 1; arg < *argc; arg++)
5225 if (strncmp("-nb", argv[arg], 3) == 0)
5226 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00005227 netbeansArg = argv[arg];
5228 mch_memmove(&argv[arg], &argv[arg + 1],
5229 (--*argc - arg) * sizeof(char *));
5230 argv[*argc] = NULL;
Bram Moolenaar734a8672019-12-02 22:49:38 +01005231 break; // enough?
Bram Moolenaar071d4272004-06-13 20:20:40 +00005232 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005233 }
5234#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005235}
5236
K.Takatac81e9bf2022-01-16 14:15:49 +00005237 static void
5238load_dpi_func(void)
5239{
5240 HMODULE hUser32;
5241
5242 hUser32 = GetModuleHandle("user32.dll");
5243 if (hUser32 == NULL)
5244 goto fail;
5245
5246 pGetDpiForSystem = (void*)GetProcAddress(hUser32, "GetDpiForSystem");
5247 pGetDpiForWindow = (void*)GetProcAddress(hUser32, "GetDpiForWindow");
5248 pGetSystemMetricsForDpi = (void*)GetProcAddress(hUser32, "GetSystemMetricsForDpi");
5249 //pGetWindowDpiAwarenessContext = (void*)GetProcAddress(hUser32, "GetWindowDpiAwarenessContext");
5250 pSetThreadDpiAwarenessContext = (void*)GetProcAddress(hUser32, "SetThreadDpiAwarenessContext");
5251 pGetAwarenessFromDpiAwarenessContext = (void*)GetProcAddress(hUser32, "GetAwarenessFromDpiAwarenessContext");
5252
5253 if (pSetThreadDpiAwarenessContext != NULL)
5254 {
5255 DPI_AWARENESS_CONTEXT oldctx = pSetThreadDpiAwarenessContext(
5256 DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
5257 if (oldctx != NULL)
5258 {
5259 TRACE("DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 enabled");
5260 s_process_dpi_aware = pGetAwarenessFromDpiAwarenessContext(oldctx);
5261#ifdef DEBUG
5262 if (s_process_dpi_aware == DPI_AWARENESS_UNAWARE)
5263 {
5264 TRACE("WARNING: PerMonitorV2 is not enabled in the process level for some reasons. IME window may not shown correctly.");
5265 }
5266#endif
5267 return;
5268 }
5269 }
5270
5271fail:
5272 // Disable PerMonitorV2 APIs.
5273 pGetDpiForSystem = stubGetDpiForSystem;
5274 pGetDpiForWindow = NULL;
5275 pGetSystemMetricsForDpi = stubGetSystemMetricsForDpi;
5276 pSetThreadDpiAwarenessContext = NULL;
5277 pGetAwarenessFromDpiAwarenessContext = NULL;
5278}
5279
Bram Moolenaar071d4272004-06-13 20:20:40 +00005280/*
5281 * Initialise the GUI. Create all the windows, set up all the call-backs
5282 * etc.
5283 */
5284 int
5285gui_mch_init(void)
5286{
Bram Moolenaar071d4272004-06-13 20:20:40 +00005287 const WCHAR szVimWndClassW[] = VIM_CLASSW;
Bram Moolenaar33d0b692010-02-17 16:31:32 +01005288 const WCHAR szTextAreaClassW[] = L"VimTextArea";
Bram Moolenaar071d4272004-06-13 20:20:40 +00005289 WNDCLASSW wndclassw;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005290
Bram Moolenaar734a8672019-12-02 22:49:38 +01005291 // Return here if the window was already opened (happens when
5292 // gui_mch_dialog() is called early).
Bram Moolenaar071d4272004-06-13 20:20:40 +00005293 if (s_hwnd != NULL)
Bram Moolenaar748bf032005-02-02 23:04:36 +00005294 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005295
5296 /*
5297 * Load the tearoff bitmap
5298 */
5299#ifdef FEAT_TEAROFF
Bram Moolenaarafde13b2019-04-28 19:46:49 +02005300 s_htearbitmap = LoadBitmap(g_hinst, "IDB_TEAROFF");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005301#endif
5302
K.Takatac81e9bf2022-01-16 14:15:49 +00005303 load_dpi_func();
5304
5305 s_dpi = pGetDpiForSystem();
5306 update_scrollbar_size();
5307
Bram Moolenaar071d4272004-06-13 20:20:40 +00005308#ifdef FEAT_MENU
Bram Moolenaar734a8672019-12-02 22:49:38 +01005309 gui.menu_height = 0; // Windows takes care of this
Bram Moolenaar071d4272004-06-13 20:20:40 +00005310#endif
5311 gui.border_width = 0;
K.Takatac81e9bf2022-01-16 14:15:49 +00005312#ifdef FEAT_TOOLBAR
5313 gui.toolbar_height = TOOLBAR_BUTTON_HEIGHT + TOOLBAR_BORDER_HEIGHT;
5314#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005315
5316 s_brush = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
5317
Bram Moolenaar734a8672019-12-02 22:49:38 +01005318 // First try using the wide version, so that we can use any title.
5319 // Otherwise only characters in the active codepage will work.
Bram Moolenaarafde13b2019-04-28 19:46:49 +02005320 if (GetClassInfoW(g_hinst, szVimWndClassW, &wndclassw) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005321 {
Bram Moolenaar910f66f2006-04-05 20:41:53 +00005322 wndclassw.style = CS_DBLCLKS;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005323 wndclassw.lpfnWndProc = _WndProc;
5324 wndclassw.cbClsExtra = 0;
5325 wndclassw.cbWndExtra = 0;
Bram Moolenaarafde13b2019-04-28 19:46:49 +02005326 wndclassw.hInstance = g_hinst;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005327 wndclassw.hIcon = LoadIcon(wndclassw.hInstance, "IDR_VIM");
5328 wndclassw.hCursor = LoadCursor(NULL, IDC_ARROW);
5329 wndclassw.hbrBackground = s_brush;
5330 wndclassw.lpszMenuName = NULL;
5331 wndclassw.lpszClassName = szVimWndClassW;
5332
K.Takata4ac893f2022-01-20 12:44:28 +00005333 if (RegisterClassW(&wndclassw) == 0)
Bram Moolenaarcea912a2016-10-12 14:20:24 +02005334 return FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005335 }
5336
Bram Moolenaar071d4272004-06-13 20:20:40 +00005337 if (vim_parent_hwnd != NULL)
5338 {
5339#ifdef HAVE_TRY_EXCEPT
5340 __try
5341 {
5342#endif
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02005343 // Open inside the specified parent window.
5344 // TODO: last argument should point to a CLIENTCREATESTRUCT
5345 // structure.
5346 s_hwnd = CreateWindowExW(
Bram Moolenaar071d4272004-06-13 20:20:40 +00005347 WS_EX_MDICHILD,
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02005348 szVimWndClassW, L"Vim MSWindows GUI",
Bram Moolenaare78c2062011-08-10 15:56:27 +02005349 WS_OVERLAPPEDWINDOW | WS_CHILD
5350 | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | 0xC000,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005351 gui_win_x == -1 ? CW_USEDEFAULT : gui_win_x,
5352 gui_win_y == -1 ? CW_USEDEFAULT : gui_win_y,
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02005353 100, // Any value will do
5354 100, // Any value will do
Bram Moolenaar071d4272004-06-13 20:20:40 +00005355 vim_parent_hwnd, NULL,
Bram Moolenaarafde13b2019-04-28 19:46:49 +02005356 g_hinst, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005357#ifdef HAVE_TRY_EXCEPT
5358 }
5359 __except(EXCEPTION_EXECUTE_HANDLER)
5360 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01005361 // NOP
Bram Moolenaar071d4272004-06-13 20:20:40 +00005362 }
5363#endif
5364 if (s_hwnd == NULL)
5365 {
Bram Moolenaara6f79292022-01-04 21:30:47 +00005366 emsg(_(e_unable_to_open_window_inside_mdi_application));
Bram Moolenaar071d4272004-06-13 20:20:40 +00005367 mch_exit(2);
5368 }
5369 }
5370 else
Bram Moolenaar78e17622007-08-30 10:26:19 +00005371 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01005372 // If the provided windowid is not valid reset it to zero, so that it
5373 // is ignored and we open our own window.
Bram Moolenaar78e17622007-08-30 10:26:19 +00005374 if (IsWindow((HWND)win_socket_id) <= 0)
5375 win_socket_id = 0;
5376
Bram Moolenaar734a8672019-12-02 22:49:38 +01005377 // Create a window. If win_socket_id is not zero without border and
5378 // titlebar, it will be reparented below.
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02005379 s_hwnd = CreateWindowW(
5380 szVimWndClassW, L"Vim MSWindows GUI",
Bram Moolenaare78c2062011-08-10 15:56:27 +02005381 (win_socket_id == 0 ? WS_OVERLAPPEDWINDOW : WS_POPUP)
5382 | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
Bram Moolenaar78e17622007-08-30 10:26:19 +00005383 gui_win_x == -1 ? CW_USEDEFAULT : gui_win_x,
5384 gui_win_y == -1 ? CW_USEDEFAULT : gui_win_y,
Bram Moolenaar734a8672019-12-02 22:49:38 +01005385 100, // Any value will do
5386 100, // Any value will do
Bram Moolenaar78e17622007-08-30 10:26:19 +00005387 NULL, NULL,
Bram Moolenaarafde13b2019-04-28 19:46:49 +02005388 g_hinst, NULL);
Bram Moolenaar78e17622007-08-30 10:26:19 +00005389 if (s_hwnd != NULL && win_socket_id != 0)
5390 {
5391 SetParent(s_hwnd, (HWND)win_socket_id);
5392 ShowWindow(s_hwnd, SW_SHOWMAXIMIZED);
5393 }
5394 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005395
5396 if (s_hwnd == NULL)
5397 return FAIL;
5398
K.Takatac81e9bf2022-01-16 14:15:49 +00005399 if (pGetDpiForWindow != NULL)
5400 {
5401 s_dpi = pGetDpiForWindow(s_hwnd);
5402 update_scrollbar_size();
5403 //TRACE("System DPI: %d, DPI: %d", pGetDpiForSystem(), s_dpi);
5404 }
5405
Bram Moolenaar071d4272004-06-13 20:20:40 +00005406#if defined(FEAT_MBYTE_IME) && defined(DYNAMIC_IME)
5407 dyn_imm_load();
5408#endif
5409
Bram Moolenaar734a8672019-12-02 22:49:38 +01005410 // Create the text area window
Bram Moolenaarafde13b2019-04-28 19:46:49 +02005411 if (GetClassInfoW(g_hinst, szTextAreaClassW, &wndclassw) == 0)
Bram Moolenaar33d0b692010-02-17 16:31:32 +01005412 {
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02005413 wndclassw.style = CS_OWNDC;
5414 wndclassw.lpfnWndProc = _TextAreaWndProc;
5415 wndclassw.cbClsExtra = 0;
5416 wndclassw.cbWndExtra = 0;
Bram Moolenaarafde13b2019-04-28 19:46:49 +02005417 wndclassw.hInstance = g_hinst;
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02005418 wndclassw.hIcon = NULL;
5419 wndclassw.hCursor = LoadCursor(NULL, IDC_ARROW);
5420 wndclassw.hbrBackground = NULL;
5421 wndclassw.lpszMenuName = NULL;
5422 wndclassw.lpszClassName = szTextAreaClassW;
Bram Moolenaar33d0b692010-02-17 16:31:32 +01005423
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02005424 if (RegisterClassW(&wndclassw) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005425 return FAIL;
5426 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005427
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02005428 s_textArea = CreateWindowExW(
5429 0,
5430 szTextAreaClassW, L"Vim text area",
5431 WS_CHILD | WS_VISIBLE, 0, 0,
5432 100, // Any value will do for now
5433 100, // Any value will do for now
5434 s_hwnd, NULL,
Bram Moolenaarafde13b2019-04-28 19:46:49 +02005435 g_hinst, NULL);
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02005436
Bram Moolenaar071d4272004-06-13 20:20:40 +00005437 if (s_textArea == NULL)
5438 return FAIL;
5439
Bram Moolenaar20321902016-02-17 12:30:17 +01005440#ifdef FEAT_LIBCALL
Bram Moolenaar734a8672019-12-02 22:49:38 +01005441 // Try loading an icon from $RUNTIMEPATH/bitmaps/vim.ico.
Bram Moolenaarcddc91c2014-09-23 21:53:41 +02005442 {
5443 HANDLE hIcon = NULL;
5444
5445 if (mch_icon_load(&hIcon) == OK && hIcon != NULL)
Bram Moolenaar0f519a02014-10-06 18:10:09 +02005446 SendMessage(s_hwnd, WM_SETICON, ICON_SMALL, (LPARAM)hIcon);
Bram Moolenaarcddc91c2014-09-23 21:53:41 +02005447 }
Bram Moolenaar20321902016-02-17 12:30:17 +01005448#endif
Bram Moolenaarcddc91c2014-09-23 21:53:41 +02005449
Bram Moolenaar071d4272004-06-13 20:20:40 +00005450#ifdef FEAT_MENU
5451 s_menuBar = CreateMenu();
5452#endif
5453 s_hdc = GetDC(s_textArea);
5454
Bram Moolenaar071d4272004-06-13 20:20:40 +00005455 DragAcceptFiles(s_hwnd, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005456
Bram Moolenaar734a8672019-12-02 22:49:38 +01005457 // Do we need to bother with this?
K.Takatac81e9bf2022-01-16 14:15:49 +00005458 // m_fMouseAvail = pGetSystemMetricsForDpi(SM_MOUSEPRESENT, s_dpi);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005459
Bram Moolenaar734a8672019-12-02 22:49:38 +01005460 // Get background/foreground colors from the system
Bram Moolenaar071d4272004-06-13 20:20:40 +00005461 gui_mch_def_colors();
5462
Bram Moolenaar734a8672019-12-02 22:49:38 +01005463 // Get the colors from the "Normal" group (set in syntax.c or in a vimrc
5464 // file)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005465 set_normal_colors();
5466
5467 /*
5468 * Check that none of the colors are the same as the background color.
5469 * Then store the current values as the defaults.
5470 */
5471 gui_check_colors();
5472 gui.def_norm_pixel = gui.norm_pixel;
5473 gui.def_back_pixel = gui.back_pixel;
5474
Bram Moolenaar734a8672019-12-02 22:49:38 +01005475 // Get the colors for the highlight groups (gui_check_colors() might have
5476 // changed them)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005477 highlight_gui_started();
5478
5479 /*
Bram Moolenaar97b0b0e2015-11-19 20:23:37 +01005480 * Start out by adding the configured border width into the border offset.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005481 */
Bram Moolenaar97b0b0e2015-11-19 20:23:37 +01005482 gui.border_offset = gui.border_width;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005483
5484 /*
5485 * Set up for Intellimouse processing
5486 */
5487 init_mouse_wheel();
5488
5489 /*
5490 * compute a couple of metrics used for the dialogs
5491 */
5492 get_dialog_font_metrics();
5493#ifdef FEAT_TOOLBAR
5494 /*
5495 * Create the toolbar
5496 */
5497 initialise_toolbar();
5498#endif
Bram Moolenaar3991dab2006-03-27 17:01:56 +00005499#ifdef FEAT_GUI_TABLINE
5500 /*
5501 * Create the tabline
5502 */
5503 initialise_tabline();
5504#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005505#ifdef MSWIN_FIND_REPLACE
5506 /*
5507 * Initialise the dialog box stuff
5508 */
5509 s_findrep_msg = RegisterWindowMessage(FINDMSGSTRING);
5510
Bram Moolenaar734a8672019-12-02 22:49:38 +01005511 // Initialise the struct
Bram Moolenaar071d4272004-06-13 20:20:40 +00005512 s_findrep_struct.lStructSize = sizeof(s_findrep_struct);
Bram Moolenaarc799fe22019-05-28 23:08:19 +02005513 s_findrep_struct.lpstrFindWhat = ALLOC_MULT(WCHAR, MSWIN_FR_BUFSIZE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005514 s_findrep_struct.lpstrFindWhat[0] = NUL;
Bram Moolenaarc799fe22019-05-28 23:08:19 +02005515 s_findrep_struct.lpstrReplaceWith = ALLOC_MULT(WCHAR, MSWIN_FR_BUFSIZE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005516 s_findrep_struct.lpstrReplaceWith[0] = NUL;
5517 s_findrep_struct.wFindWhatLen = MSWIN_FR_BUFSIZE;
5518 s_findrep_struct.wReplaceWithLen = MSWIN_FR_BUFSIZE;
5519#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005520
Bram Moolenaar264e9fd2010-10-27 12:33:17 +02005521#ifdef FEAT_EVAL
Bram Moolenaarf32c5cd2016-01-10 16:07:44 +01005522# if !defined(_MSC_VER) || (_MSC_VER < 1400)
Bram Moolenaar734a8672019-12-02 22:49:38 +01005523// Define HandleToLong for old MS and non-MS compilers if not defined.
Bram Moolenaarf32c5cd2016-01-10 16:07:44 +01005524# ifndef HandleToLong
Bram Moolenaara87e2c22016-02-17 20:48:19 +01005525# define HandleToLong(h) ((long)(intptr_t)(h))
Bram Moolenaarf32c5cd2016-01-10 16:07:44 +01005526# endif
Bram Moolenaar4da95d32011-07-07 17:43:41 +02005527# endif
Bram Moolenaar734a8672019-12-02 22:49:38 +01005528 // set the v:windowid variable
Bram Moolenaar7154b322011-05-25 21:18:06 +02005529 set_vim_var_nr(VV_WINDOWID, HandleToLong(s_hwnd));
Bram Moolenaar264e9fd2010-10-27 12:33:17 +02005530#endif
5531
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02005532#ifdef FEAT_RENDER_OPTIONS
5533 if (p_rop)
5534 (void)gui_mch_set_rendering_options(p_rop);
5535#endif
5536
Bram Moolenaar748bf032005-02-02 23:04:36 +00005537theend:
Bram Moolenaar734a8672019-12-02 22:49:38 +01005538 // Display any pending error messages
Bram Moolenaar748bf032005-02-02 23:04:36 +00005539 display_errors();
5540
Bram Moolenaar071d4272004-06-13 20:20:40 +00005541 return OK;
5542}
5543
5544/*
5545 * Get the size of the screen, taking position on multiple monitors into
5546 * account (if supported).
5547 */
5548 static void
5549get_work_area(RECT *spi_rect)
5550{
Bram Moolenaarcea912a2016-10-12 14:20:24 +02005551 HMONITOR mon;
5552 MONITORINFO moninfo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005553
Bram Moolenaar734a8672019-12-02 22:49:38 +01005554 // work out which monitor the window is on, and get *its* work area
Bram Moolenaar87f3d202016-12-01 20:18:50 +01005555 mon = MonitorFromWindow(s_hwnd, MONITOR_DEFAULTTOPRIMARY);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02005556 if (mon != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005557 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +02005558 moninfo.cbSize = sizeof(MONITORINFO);
5559 if (GetMonitorInfo(mon, &moninfo))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005560 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +02005561 *spi_rect = moninfo.rcWork;
5562 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005563 }
5564 }
Bram Moolenaar734a8672019-12-02 22:49:38 +01005565 // this is the old method...
Bram Moolenaar071d4272004-06-13 20:20:40 +00005566 SystemParametersInfo(SPI_GETWORKAREA, 0, spi_rect, 0);
5567}
5568
5569/*
5570 * Set the size of the window to the given width and height in pixels.
5571 */
5572 void
Bram Moolenaar1266d672017-02-01 13:43:36 +01005573gui_mch_set_shellsize(
5574 int width,
5575 int height,
5576 int min_width UNUSED,
5577 int min_height UNUSED,
5578 int base_width UNUSED,
5579 int base_height UNUSED,
Bram Moolenaarafa24992006-03-27 20:58:26 +00005580 int direction)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005581{
5582 RECT workarea_rect;
Bram Moolenaar98af99f2020-07-16 22:30:31 +02005583 RECT window_rect;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005584 int win_width, win_height;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005585
Bram Moolenaar734a8672019-12-02 22:49:38 +01005586 // Try to keep window completely on screen.
5587 // Get position of the screen work area. This is the part that is not
5588 // used by the taskbar or appbars.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005589 get_work_area(&workarea_rect);
5590
Bram Moolenaar734a8672019-12-02 22:49:38 +01005591 // Resizing a maximized window looks very strange, unzoom it first.
5592 // But don't do it when still starting up, it may have been requested in
5593 // the shortcut.
Bram Moolenaar98af99f2020-07-16 22:30:31 +02005594 if (IsZoomed(s_hwnd) && starting == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005595 ShowWindow(s_hwnd, SW_SHOWNORMAL);
Bram Moolenaar98af99f2020-07-16 22:30:31 +02005596
5597 GetWindowRect(s_hwnd, &window_rect);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005598
Bram Moolenaar734a8672019-12-02 22:49:38 +01005599 // compute the size of the outside of the window
K.Takatac81e9bf2022-01-16 14:15:49 +00005600 win_width = width + (pGetSystemMetricsForDpi(SM_CXFRAME, s_dpi) +
5601 pGetSystemMetricsForDpi(SM_CXPADDEDBORDER, s_dpi)) * 2;
5602 win_height = height + (pGetSystemMetricsForDpi(SM_CYFRAME, s_dpi) +
5603 pGetSystemMetricsForDpi(SM_CXPADDEDBORDER, s_dpi)) * 2
5604 + pGetSystemMetricsForDpi(SM_CYCAPTION, s_dpi)
5605 + gui_mswin_get_menu_height(FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005606
Bram Moolenaar734a8672019-12-02 22:49:38 +01005607 // The following should take care of keeping Vim on the same monitor, no
5608 // matter if the secondary monitor is left or right of the primary
5609 // monitor.
Bram Moolenaar98af99f2020-07-16 22:30:31 +02005610 window_rect.right = window_rect.left + win_width;
5611 window_rect.bottom = window_rect.top + win_height;
Bram Moolenaar56a907a2006-05-06 21:44:30 +00005612
Bram Moolenaar734a8672019-12-02 22:49:38 +01005613 // If the window is going off the screen, move it on to the screen.
Bram Moolenaar98af99f2020-07-16 22:30:31 +02005614 if ((direction & RESIZE_HOR) && window_rect.right > workarea_rect.right)
5615 OffsetRect(&window_rect, workarea_rect.right - window_rect.right, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005616
Bram Moolenaar98af99f2020-07-16 22:30:31 +02005617 if ((direction & RESIZE_HOR) && window_rect.left < workarea_rect.left)
5618 OffsetRect(&window_rect, workarea_rect.left - window_rect.left, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005619
Bram Moolenaar98af99f2020-07-16 22:30:31 +02005620 if ((direction & RESIZE_VERT) && window_rect.bottom > workarea_rect.bottom)
5621 OffsetRect(&window_rect, 0, workarea_rect.bottom - window_rect.bottom);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005622
Bram Moolenaar98af99f2020-07-16 22:30:31 +02005623 if ((direction & RESIZE_VERT) && window_rect.top < workarea_rect.top)
5624 OffsetRect(&window_rect, 0, workarea_rect.top - window_rect.top);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005625
Bram Moolenaar98af99f2020-07-16 22:30:31 +02005626 MoveWindow(s_hwnd, window_rect.left, window_rect.top,
5627 win_width, win_height, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005628
5629 SetActiveWindow(s_hwnd);
5630 SetFocus(s_hwnd);
5631
Bram Moolenaar734a8672019-12-02 22:49:38 +01005632 // Menu may wrap differently now
Bram Moolenaar071d4272004-06-13 20:20:40 +00005633 gui_mswin_get_menu_height(!gui.starting);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005634}
5635
5636
5637 void
5638gui_mch_set_scrollbar_thumb(
5639 scrollbar_T *sb,
5640 long val,
5641 long size,
5642 long max)
5643{
5644 SCROLLINFO info;
5645
5646 sb->scroll_shift = 0;
5647 while (max > 32767)
5648 {
5649 max = (max + 1) >> 1;
5650 val >>= 1;
5651 size >>= 1;
5652 ++sb->scroll_shift;
5653 }
5654
5655 if (sb->scroll_shift > 0)
5656 ++size;
5657
5658 info.cbSize = sizeof(info);
5659 info.fMask = SIF_POS | SIF_RANGE | SIF_PAGE;
5660 info.nPos = val;
5661 info.nMin = 0;
5662 info.nMax = max;
5663 info.nPage = size;
5664 SetScrollInfo(sb->id, SB_CTL, &info, TRUE);
5665}
5666
5667
5668/*
5669 * Set the current text font.
5670 */
5671 void
5672gui_mch_set_font(GuiFont font)
5673{
5674 gui.currFont = font;
5675}
5676
5677
5678/*
5679 * Set the current text foreground color.
5680 */
5681 void
5682gui_mch_set_fg_color(guicolor_T color)
5683{
5684 gui.currFgColor = color;
5685}
5686
5687/*
5688 * Set the current text background color.
5689 */
5690 void
5691gui_mch_set_bg_color(guicolor_T color)
5692{
5693 gui.currBgColor = color;
5694}
5695
Bram Moolenaare2cc9702005-03-15 22:43:58 +00005696/*
5697 * Set the current text special color.
5698 */
5699 void
5700gui_mch_set_sp_color(guicolor_T color)
5701{
5702 gui.currSpColor = color;
5703}
5704
Bram Moolenaarbdb81392017-11-27 23:24:08 +01005705#ifdef FEAT_MBYTE_IME
Bram Moolenaar071d4272004-06-13 20:20:40 +00005706/*
5707 * Multi-byte handling, originally by Sung-Hoon Baek.
5708 * First static functions (no prototypes generated).
5709 */
Bram Moolenaarbdb81392017-11-27 23:24:08 +01005710# ifdef _MSC_VER
Bram Moolenaar734a8672019-12-02 22:49:38 +01005711# include <ime.h> // Apparently not needed for Cygwin or MinGW.
Bram Moolenaarbdb81392017-11-27 23:24:08 +01005712# endif
5713# include <imm.h>
Bram Moolenaar071d4272004-06-13 20:20:40 +00005714
5715/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00005716 * handle WM_IME_NOTIFY message
5717 */
5718 static LRESULT
Bram Moolenaar1266d672017-02-01 13:43:36 +01005719_OnImeNotify(HWND hWnd, DWORD dwCommand, DWORD dwData UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005720{
5721 LRESULT lResult = 0;
5722 HIMC hImc;
5723
5724 if (!pImmGetContext || (hImc = pImmGetContext(hWnd)) == (HIMC)0)
5725 return lResult;
5726 switch (dwCommand)
5727 {
5728 case IMN_SETOPENSTATUS:
5729 if (pImmGetOpenStatus(hImc))
5730 {
K.Takatac81e9bf2022-01-16 14:15:49 +00005731 LOGFONTW lf = norm_logfont;
5732 if (s_process_dpi_aware == DPI_AWARENESS_UNAWARE)
5733 // Work around when PerMonitorV2 is not enabled in the process level.
5734 lf.lfHeight = lf.lfHeight * DEFAULT_DPI / s_dpi;
5735 pImmSetCompositionFontW(hImc, &lf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005736 im_set_position(gui.row, gui.col);
5737
Bram Moolenaar734a8672019-12-02 22:49:38 +01005738 // Disable langmap
Bram Moolenaar071d4272004-06-13 20:20:40 +00005739 State &= ~LANGMAP;
5740 if (State & INSERT)
5741 {
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01005742# if defined(FEAT_KEYMAP)
Bram Moolenaar734a8672019-12-02 22:49:38 +01005743 // Unshown 'keymap' in status lines
Bram Moolenaar071d4272004-06-13 20:20:40 +00005744 if (curbuf->b_p_iminsert == B_IMODE_LMAP)
5745 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01005746 // Save cursor position
Bram Moolenaar071d4272004-06-13 20:20:40 +00005747 int old_row = gui.row;
5748 int old_col = gui.col;
5749
5750 // This must be called here before
5751 // status_redraw_curbuf(), otherwise the mode
5752 // message may appear in the wrong position.
5753 showmode();
5754 status_redraw_curbuf();
5755 update_screen(0);
Bram Moolenaar734a8672019-12-02 22:49:38 +01005756 // Restore cursor position
Bram Moolenaar071d4272004-06-13 20:20:40 +00005757 gui.row = old_row;
5758 gui.col = old_col;
5759 }
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01005760# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005761 }
5762 }
5763 gui_update_cursor(TRUE, FALSE);
Bram Moolenaar92467d32017-12-05 13:22:16 +01005764 gui_mch_flush();
Bram Moolenaar071d4272004-06-13 20:20:40 +00005765 lResult = 0;
5766 break;
5767 }
5768 pImmReleaseContext(hWnd, hImc);
5769 return lResult;
5770}
5771
5772 static LRESULT
Bram Moolenaar1266d672017-02-01 13:43:36 +01005773_OnImeComposition(HWND hwnd, WPARAM dbcs UNUSED, LPARAM param)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005774{
5775 char_u *ret;
5776 int len;
5777
Bram Moolenaar734a8672019-12-02 22:49:38 +01005778 if ((param & GCS_RESULTSTR) == 0) // Composition unfinished.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005779 return 0;
5780
5781 ret = GetResultStr(hwnd, GCS_RESULTSTR, &len);
5782 if (ret != NULL)
5783 {
5784 add_to_input_buf_csi(ret, len);
5785 vim_free(ret);
5786 return 1;
5787 }
5788 return 0;
5789}
5790
5791/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00005792 * void GetResultStr()
5793 *
5794 * This handles WM_IME_COMPOSITION with GCS_RESULTSTR flag on.
5795 * get complete composition string
5796 */
5797 static char_u *
5798GetResultStr(HWND hwnd, int GCS, int *lenp)
5799{
Bram Moolenaar734a8672019-12-02 22:49:38 +01005800 HIMC hIMC; // Input context handle.
K.Takatab0b2b732022-01-19 12:59:21 +00005801 LONG ret;
5802 WCHAR *buf = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005803 char_u *convbuf = NULL;
5804
5805 if (!pImmGetContext || (hIMC = pImmGetContext(hwnd)) == (HIMC)0)
5806 return NULL;
5807
K.Takatab0b2b732022-01-19 12:59:21 +00005808 // Get the length of the composition string.
5809 ret = pImmGetCompositionStringW(hIMC, GCS, NULL, 0);
5810 if (ret <= 0)
5811 return NULL;
5812
5813 // Allocate the requested buffer plus space for the NUL character.
5814 buf = alloc(ret + sizeof(WCHAR));
Bram Moolenaar071d4272004-06-13 20:20:40 +00005815 if (buf == NULL)
5816 return NULL;
5817
K.Takatab0b2b732022-01-19 12:59:21 +00005818 // Reads in the composition string.
5819 pImmGetCompositionStringW(hIMC, GCS, buf, ret);
5820 *lenp = ret / sizeof(WCHAR);
5821
Bram Moolenaar36f692d2008-11-20 16:10:17 +00005822 convbuf = utf16_to_enc(buf, lenp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005823 pImmReleaseContext(hwnd, hIMC);
5824 vim_free(buf);
5825 return convbuf;
5826}
5827#endif
5828
Bram Moolenaar734a8672019-12-02 22:49:38 +01005829// For global functions we need prototypes.
Bram Moolenaarbdb81392017-11-27 23:24:08 +01005830#if defined(FEAT_MBYTE_IME) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005831
5832/*
5833 * set font to IM.
5834 */
5835 void
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01005836im_set_font(LOGFONTW *lf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005837{
5838 HIMC hImc;
5839
5840 if (pImmGetContext && (hImc = pImmGetContext(s_hwnd)) != (HIMC)0)
5841 {
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01005842 pImmSetCompositionFontW(hImc, lf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005843 pImmReleaseContext(s_hwnd, hImc);
5844 }
5845}
5846
5847/*
5848 * Notify cursor position to IM.
5849 */
5850 void
5851im_set_position(int row, int col)
5852{
5853 HIMC hImc;
5854
5855 if (pImmGetContext && (hImc = pImmGetContext(s_hwnd)) != (HIMC)0)
5856 {
5857 COMPOSITIONFORM cfs;
5858
5859 cfs.dwStyle = CFS_POINT;
5860 cfs.ptCurrentPos.x = FILL_X(col);
5861 cfs.ptCurrentPos.y = FILL_Y(row);
5862 MapWindowPoints(s_textArea, s_hwnd, &cfs.ptCurrentPos, 1);
K.Takatac81e9bf2022-01-16 14:15:49 +00005863 if (s_process_dpi_aware == DPI_AWARENESS_UNAWARE)
5864 {
5865 // Work around when PerMonitorV2 is not enabled in the process level.
5866 cfs.ptCurrentPos.x = cfs.ptCurrentPos.x * DEFAULT_DPI / s_dpi;
5867 cfs.ptCurrentPos.y = cfs.ptCurrentPos.y * DEFAULT_DPI / s_dpi;
5868 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005869 pImmSetCompositionWindow(hImc, &cfs);
5870
5871 pImmReleaseContext(s_hwnd, hImc);
5872 }
5873}
5874
5875/*
5876 * Set IM status on ("active" is TRUE) or off ("active" is FALSE).
5877 */
5878 void
5879im_set_active(int active)
5880{
5881 HIMC hImc;
5882 static HIMC hImcOld = (HIMC)0;
5883
Bram Moolenaar310c32e2019-11-29 23:15:25 +01005884# ifdef VIMDLL
5885 if (!gui.in_use && !gui.starting)
5886 {
5887 mbyte_im_set_active(active);
5888 return;
5889 }
5890# endif
5891
Bram Moolenaar734a8672019-12-02 22:49:38 +01005892 if (pImmGetContext) // if NULL imm32.dll wasn't loaded (yet)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005893 {
5894 if (p_imdisable)
5895 {
5896 if (hImcOld == (HIMC)0)
5897 {
5898 hImcOld = pImmGetContext(s_hwnd);
5899 if (hImcOld)
5900 pImmAssociateContext(s_hwnd, (HIMC)0);
5901 }
5902 active = FALSE;
5903 }
5904 else if (hImcOld != (HIMC)0)
5905 {
5906 pImmAssociateContext(s_hwnd, hImcOld);
5907 hImcOld = (HIMC)0;
5908 }
5909
5910 hImc = pImmGetContext(s_hwnd);
5911 if (hImc)
5912 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00005913 /*
5914 * for Korean ime
5915 */
5916 HKL hKL = GetKeyboardLayout(0);
5917
5918 if (LOWORD(hKL) == MAKELANGID(LANG_KOREAN, SUBLANG_KOREAN))
5919 {
5920 static DWORD dwConversionSaved = 0, dwSentenceSaved = 0;
5921 static BOOL bSaved = FALSE;
5922
5923 if (active)
5924 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01005925 // if we have a saved conversion status, restore it
Bram Moolenaarca003e12006-03-17 23:19:38 +00005926 if (bSaved)
5927 pImmSetConversionStatus(hImc, dwConversionSaved,
5928 dwSentenceSaved);
5929 bSaved = FALSE;
5930 }
5931 else
5932 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01005933 // save conversion status and disable korean
Bram Moolenaarca003e12006-03-17 23:19:38 +00005934 if (pImmGetConversionStatus(hImc, &dwConversionSaved,
5935 &dwSentenceSaved))
5936 {
5937 bSaved = TRUE;
5938 pImmSetConversionStatus(hImc,
5939 dwConversionSaved & ~(IME_CMODE_NATIVE
5940 | IME_CMODE_FULLSHAPE),
5941 dwSentenceSaved);
5942 }
5943 }
5944 }
5945
Bram Moolenaar071d4272004-06-13 20:20:40 +00005946 pImmSetOpenStatus(hImc, active);
5947 pImmReleaseContext(s_hwnd, hImc);
5948 }
5949 }
5950}
5951
5952/*
5953 * Get IM status. When IM is on, return not 0. Else return 0.
5954 */
5955 int
Bram Moolenaar68c2f632016-01-30 17:24:07 +01005956im_get_status(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005957{
5958 int status = 0;
5959 HIMC hImc;
5960
Bram Moolenaar310c32e2019-11-29 23:15:25 +01005961# ifdef VIMDLL
5962 if (!gui.in_use && !gui.starting)
5963 return mbyte_im_get_status();
5964# endif
5965
Bram Moolenaar071d4272004-06-13 20:20:40 +00005966 if (pImmGetContext && (hImc = pImmGetContext(s_hwnd)) != (HIMC)0)
5967 {
5968 status = pImmGetOpenStatus(hImc) ? 1 : 0;
5969 pImmReleaseContext(s_hwnd, hImc);
5970 }
5971 return status;
5972}
5973
Bram Moolenaar734a8672019-12-02 22:49:38 +01005974#endif // FEAT_MBYTE_IME
Bram Moolenaar071d4272004-06-13 20:20:40 +00005975
Bram Moolenaar071d4272004-06-13 20:20:40 +00005976
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005977/*
Bram Moolenaar39f05632006-03-19 22:15:26 +00005978 * Convert latin9 text "text[len]" to ucs-2 in "unicodebuf".
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005979 */
5980 static void
5981latin9_to_ucs(char_u *text, int len, WCHAR *unicodebuf)
5982{
5983 int c;
5984
Bram Moolenaarca003e12006-03-17 23:19:38 +00005985 while (--len >= 0)
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005986 {
5987 c = *text++;
5988 switch (c)
5989 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01005990 case 0xa4: c = 0x20ac; break; // euro
5991 case 0xa6: c = 0x0160; break; // S hat
5992 case 0xa8: c = 0x0161; break; // S -hat
5993 case 0xb4: c = 0x017d; break; // Z hat
5994 case 0xb8: c = 0x017e; break; // Z -hat
5995 case 0xbc: c = 0x0152; break; // OE
5996 case 0xbd: c = 0x0153; break; // oe
5997 case 0xbe: c = 0x0178; break; // Y
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005998 }
5999 *unicodebuf++ = c;
6000 }
6001}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006002
6003#ifdef FEAT_RIGHTLEFT
6004/*
6005 * What is this for? In the case where you are using Win98 or Win2K or later,
6006 * and you are using a Hebrew font (or Arabic!), Windows does you a favor and
6007 * reverses the string sent to the TextOut... family. This sucks, because we
6008 * go to a lot of effort to do the right thing, and there doesn't seem to be a
6009 * way to tell Windblows not to do this!
6010 *
6011 * The short of it is that this 'RevOut' only gets called if you are running
6012 * one of the new, "improved" MS OSes, and only if you are running in
6013 * 'rightleft' mode. It makes display take *slightly* longer, but not
6014 * noticeably so.
6015 */
6016 static void
6017RevOut( HDC s_hdc,
6018 int col,
6019 int row,
6020 UINT foptions,
6021 CONST RECT *pcliprect,
6022 LPCTSTR text,
6023 UINT len,
6024 CONST INT *padding)
6025{
6026 int ix;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006027
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006028 for (ix = 0; ix < (int)len; ++ix)
6029 ExtTextOut(s_hdc, col + TEXT_X(ix), row, foptions,
6030 pcliprect, text + ix, 1, padding);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006031}
6032#endif
6033
Bram Moolenaar92467d32017-12-05 13:22:16 +01006034 static void
6035draw_line(
6036 int x1,
Bram Moolenaard385b5d2018-12-27 22:43:08 +01006037 int y1,
6038 int x2,
6039 int y2,
Bram Moolenaar92467d32017-12-05 13:22:16 +01006040 COLORREF color)
6041{
6042#if defined(FEAT_DIRECTX)
6043 if (IS_ENABLE_DIRECTX())
6044 DWriteContext_DrawLine(s_dwc, x1, y1, x2, y2, color);
6045 else
6046#endif
6047 {
6048 HPEN hpen = CreatePen(PS_SOLID, 1, color);
6049 HPEN old_pen = SelectObject(s_hdc, hpen);
6050 MoveToEx(s_hdc, x1, y1, NULL);
Bram Moolenaar734a8672019-12-02 22:49:38 +01006051 // Note: LineTo() excludes the last pixel in the line.
Bram Moolenaar92467d32017-12-05 13:22:16 +01006052 LineTo(s_hdc, x2, y2);
6053 DeleteObject(SelectObject(s_hdc, old_pen));
6054 }
6055}
6056
6057 static void
6058set_pixel(
6059 int x,
Bram Moolenaard385b5d2018-12-27 22:43:08 +01006060 int y,
Bram Moolenaar92467d32017-12-05 13:22:16 +01006061 COLORREF color)
6062{
6063#if defined(FEAT_DIRECTX)
6064 if (IS_ENABLE_DIRECTX())
6065 DWriteContext_SetPixel(s_dwc, x, y, color);
6066 else
6067#endif
6068 SetPixel(s_hdc, x, y, color);
6069}
6070
6071 static void
6072fill_rect(
6073 const RECT *rcp,
Bram Moolenaard385b5d2018-12-27 22:43:08 +01006074 HBRUSH hbr,
Bram Moolenaar92467d32017-12-05 13:22:16 +01006075 COLORREF color)
6076{
6077#if defined(FEAT_DIRECTX)
6078 if (IS_ENABLE_DIRECTX())
6079 DWriteContext_FillRect(s_dwc, rcp, color);
6080 else
6081#endif
6082 {
6083 HBRUSH hbr2;
6084
6085 if (hbr == NULL)
6086 hbr2 = CreateSolidBrush(color);
6087 else
6088 hbr2 = hbr;
6089 FillRect(s_hdc, rcp, hbr2);
6090 if (hbr == NULL)
6091 DeleteBrush(hbr2);
6092 }
6093}
6094
Bram Moolenaar071d4272004-06-13 20:20:40 +00006095 void
6096gui_mch_draw_string(
6097 int row,
6098 int col,
6099 char_u *text,
6100 int len,
6101 int flags)
6102{
6103 static int *padding = NULL;
6104 static int pad_size = 0;
6105 int i;
6106 const RECT *pcliprect = NULL;
6107 UINT foptions = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006108 static WCHAR *unicodebuf = NULL;
6109 static int *unicodepdy = NULL;
Bram Moolenaar402d2fe2005-04-15 21:00:38 +00006110 static int unibuflen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006111 int n = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006112 int y;
6113
Bram Moolenaar071d4272004-06-13 20:20:40 +00006114 /*
6115 * Italic and bold text seems to have an extra row of pixels at the bottom
6116 * (below where the bottom of the character should be). If we draw the
6117 * characters with a solid background, the top row of pixels in the
6118 * character below will be overwritten. We can fix this by filling in the
6119 * background ourselves, to the correct character proportions, and then
6120 * writing the character in transparent mode. Still have a problem when
6121 * the character is "_", which gets written on to the character below.
6122 * New fix: set gui.char_ascent to -1. This shifts all characters up one
6123 * pixel in their slots, which fixes the problem with the bottom row of
6124 * pixels. We still need this code because otherwise the top row of pixels
6125 * becomes a problem. - webb.
6126 */
6127 static HBRUSH hbr_cache[2] = {NULL, NULL};
6128 static guicolor_T brush_color[2] = {INVALCOLOR, INVALCOLOR};
6129 static int brush_lru = 0;
6130 HBRUSH hbr;
6131 RECT rc;
6132
6133 if (!(flags & DRAW_TRANSP))
6134 {
6135 /*
6136 * Clear background first.
6137 * Note: FillRect() excludes right and bottom of rectangle.
6138 */
6139 rc.left = FILL_X(col);
6140 rc.top = FILL_Y(row);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006141 if (has_mbyte)
6142 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01006143 // Compute the length in display cells.
Bram Moolenaar72597a52010-07-18 15:31:08 +02006144 rc.right = FILL_X(col + mb_string2cells(text, len));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006145 }
6146 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00006147 rc.right = FILL_X(col + len);
6148 rc.bottom = FILL_Y(row + 1);
6149
Bram Moolenaar734a8672019-12-02 22:49:38 +01006150 // Cache the created brush, that saves a lot of time. We need two:
6151 // one for cursor background and one for the normal background.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006152 if (gui.currBgColor == brush_color[0])
6153 {
6154 hbr = hbr_cache[0];
6155 brush_lru = 1;
6156 }
6157 else if (gui.currBgColor == brush_color[1])
6158 {
6159 hbr = hbr_cache[1];
6160 brush_lru = 0;
6161 }
6162 else
6163 {
6164 if (hbr_cache[brush_lru] != NULL)
6165 DeleteBrush(hbr_cache[brush_lru]);
6166 hbr_cache[brush_lru] = CreateSolidBrush(gui.currBgColor);
6167 brush_color[brush_lru] = gui.currBgColor;
6168 hbr = hbr_cache[brush_lru];
6169 brush_lru = !brush_lru;
6170 }
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01006171
Bram Moolenaar92467d32017-12-05 13:22:16 +01006172 fill_rect(&rc, hbr, gui.currBgColor);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006173
6174 SetBkMode(s_hdc, TRANSPARENT);
6175
6176 /*
6177 * When drawing block cursor, prevent inverted character spilling
6178 * over character cell (can happen with bold/italic)
6179 */
6180 if (flags & DRAW_CURSOR)
6181 {
6182 pcliprect = &rc;
6183 foptions = ETO_CLIPPED;
6184 }
6185 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006186 SetTextColor(s_hdc, gui.currFgColor);
6187 SelectFont(s_hdc, gui.currFont);
6188
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02006189#ifdef FEAT_DIRECTX
6190 if (IS_ENABLE_DIRECTX())
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01006191 DWriteContext_SetFont(s_dwc, (HFONT)gui.currFont);
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02006192#endif
6193
Bram Moolenaar071d4272004-06-13 20:20:40 +00006194 if (pad_size != Columns || padding == NULL || padding[0] != gui.char_width)
6195 {
6196 vim_free(padding);
6197 pad_size = Columns;
6198
Bram Moolenaar734a8672019-12-02 22:49:38 +01006199 // Don't give an out-of-memory message here, it would call us
6200 // recursively.
Bram Moolenaar59edb002019-05-28 23:32:47 +02006201 padding = LALLOC_MULT(int, pad_size);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006202 if (padding != NULL)
6203 for (i = 0; i < pad_size; i++)
6204 padding[i] = gui.char_width;
6205 }
6206
Bram Moolenaar071d4272004-06-13 20:20:40 +00006207 /*
6208 * We have to provide the padding argument because italic and bold versions
6209 * of fixed-width fonts are often one pixel or so wider than their normal
6210 * versions.
6211 * No check for DRAW_BOLD, Windows will have done it already.
6212 */
6213
Bram Moolenaar734a8672019-12-02 22:49:38 +01006214 // Check if there are any UTF-8 characters. If not, use normal text
6215 // output to speed up output.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006216 if (enc_utf8)
6217 for (n = 0; n < len; ++n)
6218 if (text[n] >= 0x80)
6219 break;
6220
Bram Moolenaarfc3abf42019-01-24 15:54:21 +01006221#if defined(FEAT_DIRECTX)
Bram Moolenaar734a8672019-12-02 22:49:38 +01006222 // Quick hack to enable DirectWrite. To use DirectWrite (antialias), it is
6223 // required that unicode drawing routine, currently. So this forces it
6224 // enabled.
Bram Moolenaar7f88b652017-12-14 13:15:19 +01006225 if (IS_ENABLE_DIRECTX())
Bram Moolenaar734a8672019-12-02 22:49:38 +01006226 n = 0; // Keep n < len, to enter block for unicode.
Bram Moolenaarfc3abf42019-01-24 15:54:21 +01006227#endif
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02006228
Bram Moolenaar734a8672019-12-02 22:49:38 +01006229 // Check if the Unicode buffer exists and is big enough. Create it
6230 // with the same length as the multi-byte string, the number of wide
6231 // characters is always equal or smaller.
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006232 if ((enc_utf8
6233 || (enc_codepage > 0 && (int)GetACP() != enc_codepage)
6234 || enc_latin9)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006235 && (unicodebuf == NULL || len > unibuflen))
6236 {
6237 vim_free(unicodebuf);
Bram Moolenaarc799fe22019-05-28 23:08:19 +02006238 unicodebuf = LALLOC_MULT(WCHAR, len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006239
6240 vim_free(unicodepdy);
Bram Moolenaarc799fe22019-05-28 23:08:19 +02006241 unicodepdy = LALLOC_MULT(int, len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006242
6243 unibuflen = len;
6244 }
6245
6246 if (enc_utf8 && n < len && unicodebuf != NULL)
6247 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01006248 // Output UTF-8 characters. Composing characters should be
6249 // handled here.
Bram Moolenaarca003e12006-03-17 23:19:38 +00006250 int i;
Bram Moolenaar734a8672019-12-02 22:49:38 +01006251 int wlen; // string length in words
6252 int clen; // string length in characters
6253 int cells; // cell width of string up to composing char
6254 int cw; // width of current cell
Bram Moolenaar362e1a32006-03-06 23:29:24 +00006255 int c;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006256
Bram Moolenaar97b2ad32006-03-18 21:40:56 +00006257 wlen = 0;
Bram Moolenaarca003e12006-03-17 23:19:38 +00006258 clen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006259 cells = 0;
Bram Moolenaarca003e12006-03-17 23:19:38 +00006260 for (i = 0; i < len; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00006261 {
Bram Moolenaar362e1a32006-03-06 23:29:24 +00006262 c = utf_ptr2char(text + i);
6263 if (c >= 0x10000)
6264 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01006265 // Turn into UTF-16 encoding.
Bram Moolenaarca003e12006-03-17 23:19:38 +00006266 unicodebuf[wlen++] = ((c - 0x10000) >> 10) + 0xD800;
6267 unicodebuf[wlen++] = ((c - 0x10000) & 0x3ff) + 0xDC00;
Bram Moolenaar362e1a32006-03-06 23:29:24 +00006268 }
6269 else
6270 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00006271 unicodebuf[wlen++] = c;
Bram Moolenaar362e1a32006-03-06 23:29:24 +00006272 }
Bram Moolenaara6ce1cc2017-10-28 19:23:11 +02006273
6274 if (utf_iscomposing(c))
6275 cw = 0;
6276 else
6277 {
6278 cw = utf_char2cells(c);
Bram Moolenaar734a8672019-12-02 22:49:38 +01006279 if (cw > 2) // don't use 4 for unprintable char
Bram Moolenaara6ce1cc2017-10-28 19:23:11 +02006280 cw = 1;
6281 }
6282
Bram Moolenaar071d4272004-06-13 20:20:40 +00006283 if (unicodepdy != NULL)
6284 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01006285 // Use unicodepdy to make characters fit as we expect, even
6286 // when the font uses different widths (e.g., bold character
6287 // is wider).
Bram Moolenaard804fdf2016-02-27 16:04:58 +01006288 if (c >= 0x10000)
6289 {
6290 unicodepdy[wlen - 2] = cw * gui.char_width;
6291 unicodepdy[wlen - 1] = 0;
6292 }
6293 else
6294 unicodepdy[wlen - 1] = cw * gui.char_width;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006295 }
6296 cells += cw;
Bram Moolenaara6ce1cc2017-10-28 19:23:11 +02006297 i += utf_ptr2len_len(text + i, len - i);
Bram Moolenaarca003e12006-03-17 23:19:38 +00006298 ++clen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006299 }
Bram Moolenaarfc3abf42019-01-24 15:54:21 +01006300#if defined(FEAT_DIRECTX)
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01006301 if (IS_ENABLE_DIRECTX())
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02006302 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01006303 // Add one to "cells" for italics.
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01006304 DWriteContext_DrawText(s_dwc, unicodebuf, wlen,
Bram Moolenaar60ebd522019-03-21 20:50:12 +01006305 TEXT_X(col), TEXT_Y(row),
6306 FILL_X(cells + 1), FILL_Y(1) - p_linespace,
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01006307 gui.char_width, gui.currFgColor,
6308 foptions, pcliprect, unicodepdy);
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02006309 }
6310 else
Bram Moolenaarfc3abf42019-01-24 15:54:21 +01006311#endif
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02006312 ExtTextOutW(s_hdc, TEXT_X(col), TEXT_Y(row),
6313 foptions, pcliprect, unicodebuf, wlen, unicodepdy);
Bram Moolenaar734a8672019-12-02 22:49:38 +01006314 len = cells; // used for underlining
Bram Moolenaar071d4272004-06-13 20:20:40 +00006315 }
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006316 else if ((enc_codepage > 0 && (int)GetACP() != enc_codepage) || enc_latin9)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006317 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01006318 // If we want to display codepage data, and the current CP is not the
6319 // ANSI one, we need to go via Unicode.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006320 if (unicodebuf != NULL)
6321 {
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006322 if (enc_latin9)
6323 latin9_to_ucs(text, len, unicodebuf);
6324 else
6325 len = MultiByteToWideChar(enc_codepage,
Bram Moolenaar071d4272004-06-13 20:20:40 +00006326 MB_PRECOMPOSED,
6327 (char *)text, len,
6328 (LPWSTR)unicodebuf, unibuflen);
6329 if (len != 0)
Bram Moolenaar19a09a12005-03-04 23:39:37 +00006330 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01006331 // Use unicodepdy to make characters fit as we expect, even
6332 // when the font uses different widths (e.g., bold character
6333 // is wider).
Bram Moolenaar19a09a12005-03-04 23:39:37 +00006334 if (unicodepdy != NULL)
6335 {
6336 int i;
6337 int cw;
6338
6339 for (i = 0; i < len; ++i)
6340 {
6341 cw = utf_char2cells(unicodebuf[i]);
6342 if (cw > 2)
6343 cw = 1;
6344 unicodepdy[i] = cw * gui.char_width;
6345 }
6346 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006347 ExtTextOutW(s_hdc, TEXT_X(col), TEXT_Y(row),
Bram Moolenaar19a09a12005-03-04 23:39:37 +00006348 foptions, pcliprect, unicodebuf, len, unicodepdy);
6349 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006350 }
6351 }
6352 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00006353 {
6354#ifdef FEAT_RIGHTLEFT
Bram Moolenaar734a8672019-12-02 22:49:38 +01006355 // Windows will mess up RL text, so we have to draw it character by
6356 // character. Only do this if RL is on, since it's slow.
Bram Moolenaar4ee40b02014-09-19 16:13:53 +02006357 if (curwin->w_p_rl)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006358 RevOut(s_hdc, TEXT_X(col), TEXT_Y(row),
6359 foptions, pcliprect, (char *)text, len, padding);
6360 else
6361#endif
6362 ExtTextOut(s_hdc, TEXT_X(col), TEXT_Y(row),
6363 foptions, pcliprect, (char *)text, len, padding);
6364 }
6365
Bram Moolenaar734a8672019-12-02 22:49:38 +01006366 // Underline
Bram Moolenaar071d4272004-06-13 20:20:40 +00006367 if (flags & DRAW_UNDERL)
6368 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01006369 // When p_linespace is 0, overwrite the bottom row of pixels.
6370 // Otherwise put the line just below the character.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006371 y = FILL_Y(row + 1) - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006372 if (p_linespace > 1)
6373 y -= p_linespace - 1;
Bram Moolenaar92467d32017-12-05 13:22:16 +01006374 draw_line(FILL_X(col), y, FILL_X(col + len), y, gui.currFgColor);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006375 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006376
Bram Moolenaar734a8672019-12-02 22:49:38 +01006377 // Strikethrough
Bram Moolenaarcf4b00c2017-09-02 18:33:56 +02006378 if (flags & DRAW_STRIKE)
6379 {
Bram Moolenaarcf4b00c2017-09-02 18:33:56 +02006380 y = FILL_Y(row + 1) - gui.char_height/2;
Bram Moolenaar92467d32017-12-05 13:22:16 +01006381 draw_line(FILL_X(col), y, FILL_X(col + len), y, gui.currSpColor);
Bram Moolenaarcf4b00c2017-09-02 18:33:56 +02006382 }
6383
Bram Moolenaar734a8672019-12-02 22:49:38 +01006384 // Undercurl
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006385 if (flags & DRAW_UNDERC)
6386 {
6387 int x;
6388 int offset;
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +00006389 static const int val[8] = {1, 0, 0, 0, 1, 2, 2, 2 };
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006390
6391 y = FILL_Y(row + 1) - 1;
6392 for (x = FILL_X(col); x < FILL_X(col + len); ++x)
6393 {
6394 offset = val[x % 8];
Bram Moolenaar92467d32017-12-05 13:22:16 +01006395 set_pixel(x, y - offset, gui.currSpColor);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006396 }
6397 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006398}
6399
6400
6401/*
6402 * Output routines.
6403 */
6404
Bram Moolenaar734a8672019-12-02 22:49:38 +01006405/*
6406 * Flush any output to the screen
6407 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006408 void
6409gui_mch_flush(void)
6410{
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01006411#if defined(FEAT_DIRECTX)
6412 if (IS_ENABLE_DIRECTX())
6413 DWriteContext_Flush(s_dwc);
6414#endif
6415
Bram Moolenaar071d4272004-06-13 20:20:40 +00006416 GdiFlush();
6417}
6418
6419 static void
6420clear_rect(RECT *rcp)
6421{
Bram Moolenaar92467d32017-12-05 13:22:16 +01006422 fill_rect(rcp, NULL, gui.back_pixel);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006423}
6424
6425
Bram Moolenaarc716c302006-01-21 22:12:51 +00006426 void
6427gui_mch_get_screen_dimensions(int *screen_w, int *screen_h)
6428{
6429 RECT workarea_rect;
6430
6431 get_work_area(&workarea_rect);
6432
Bram Moolenaar910f66f2006-04-05 20:41:53 +00006433 *screen_w = workarea_rect.right - workarea_rect.left
K.Takatac81e9bf2022-01-16 14:15:49 +00006434 - (pGetSystemMetricsForDpi(SM_CXFRAME, s_dpi) +
6435 pGetSystemMetricsForDpi(SM_CXPADDEDBORDER, s_dpi)) * 2;
Bram Moolenaarc716c302006-01-21 22:12:51 +00006436
Bram Moolenaar734a8672019-12-02 22:49:38 +01006437 // FIXME: dirty trick: Because the gui_get_base_height() doesn't include
6438 // the menubar for MSwin, we subtract it from the screen height, so that
6439 // the window size can be made to fit on the screen.
Bram Moolenaar910f66f2006-04-05 20:41:53 +00006440 *screen_h = workarea_rect.bottom - workarea_rect.top
K.Takatac81e9bf2022-01-16 14:15:49 +00006441 - (pGetSystemMetricsForDpi(SM_CYFRAME, s_dpi) +
6442 pGetSystemMetricsForDpi(SM_CXPADDEDBORDER, s_dpi)) * 2
6443 - pGetSystemMetricsForDpi(SM_CYCAPTION, s_dpi)
6444 - gui_mswin_get_menu_height(FALSE);
Bram Moolenaarc716c302006-01-21 22:12:51 +00006445}
6446
6447
Bram Moolenaar071d4272004-06-13 20:20:40 +00006448#if defined(FEAT_MENU) || defined(PROTO)
6449/*
6450 * Add a sub menu to the menu bar.
6451 */
6452 void
6453gui_mch_add_menu(
6454 vimmenu_T *menu,
6455 int pos)
6456{
6457 vimmenu_T *parent = menu->parent;
6458
6459 menu->submenu_id = CreatePopupMenu();
6460 menu->id = s_menu_id++;
6461
6462 if (menu_is_menubar(menu->name))
6463 {
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006464 WCHAR *wn;
6465 MENUITEMINFOW infow;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006466
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006467 wn = enc_to_utf16(menu->name, NULL);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006468 if (wn == NULL)
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006469 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006470
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006471 infow.cbSize = sizeof(infow);
6472 infow.fMask = MIIM_DATA | MIIM_TYPE | MIIM_ID
6473 | MIIM_SUBMENU;
6474 infow.dwItemData = (long_u)menu;
6475 infow.wID = menu->id;
6476 infow.fType = MFT_STRING;
6477 infow.dwTypeData = wn;
6478 infow.cch = (UINT)wcslen(wn);
6479 infow.hSubMenu = menu->submenu_id;
6480 InsertMenuItemW((parent == NULL)
6481 ? s_menuBar : parent->submenu_id,
6482 (UINT)pos, TRUE, &infow);
6483 vim_free(wn);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006484 }
6485
Bram Moolenaar734a8672019-12-02 22:49:38 +01006486 // Fix window size if menu may have wrapped
Bram Moolenaar071d4272004-06-13 20:20:40 +00006487 if (parent == NULL)
6488 gui_mswin_get_menu_height(!gui.starting);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006489# ifdef FEAT_TEAROFF
Bram Moolenaar071d4272004-06-13 20:20:40 +00006490 else if (IsWindow(parent->tearoff_handle))
6491 rebuild_tearoff(parent);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006492# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006493}
6494
6495 void
6496gui_mch_show_popupmenu(vimmenu_T *menu)
6497{
6498 POINT mp;
6499
K.Takata45f9cfb2022-01-21 11:11:00 +00006500 (void)GetCursorPos(&mp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006501 gui_mch_show_popupmenu_at(menu, (int)mp.x, (int)mp.y);
6502}
6503
6504 void
Bram Moolenaar045e82d2005-07-08 22:25:33 +00006505gui_make_popup(char_u *path_name, int mouse_pos)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006506{
6507 vimmenu_T *menu = gui_find_menu(path_name);
6508
6509 if (menu != NULL)
6510 {
6511 POINT p;
6512
Bram Moolenaar734a8672019-12-02 22:49:38 +01006513 // Find the position of the current cursor
Bram Moolenaar071d4272004-06-13 20:20:40 +00006514 GetDCOrgEx(s_hdc, &p);
Bram Moolenaar045e82d2005-07-08 22:25:33 +00006515 if (mouse_pos)
6516 {
6517 int mx, my;
6518
6519 gui_mch_getmouse(&mx, &my);
6520 p.x += mx;
6521 p.y += my;
6522 }
6523 else if (curwin != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006524 {
Bram Moolenaar53f81742017-09-22 14:35:51 +02006525 p.x += TEXT_X(curwin->w_wincol + curwin->w_wcol + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006526 p.y += TEXT_Y(W_WINROW(curwin) + curwin->w_wrow + 1);
6527 }
6528 msg_scroll = FALSE;
6529 gui_mch_show_popupmenu_at(menu, (int)p.x, (int)p.y);
6530 }
6531}
6532
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006533# if defined(FEAT_TEAROFF) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006534/*
6535 * Given a menu descriptor, e.g. "File.New", find it in the menu hierarchy and
6536 * create it as a pseudo-"tearoff menu".
6537 */
6538 void
6539gui_make_tearoff(char_u *path_name)
6540{
6541 vimmenu_T *menu = gui_find_menu(path_name);
6542
Bram Moolenaar734a8672019-12-02 22:49:38 +01006543 // Found the menu, so tear it off.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006544 if (menu != NULL)
6545 gui_mch_tearoff(menu->dname, menu, 0xffffL, 0xffffL);
6546}
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006547# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006548
6549/*
6550 * Add a menu item to a menu
6551 */
6552 void
6553gui_mch_add_menu_item(
6554 vimmenu_T *menu,
6555 int idx)
6556{
6557 vimmenu_T *parent = menu->parent;
6558
6559 menu->id = s_menu_id++;
6560 menu->submenu_id = NULL;
6561
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006562# ifdef FEAT_TEAROFF
Bram Moolenaar071d4272004-06-13 20:20:40 +00006563 if (STRNCMP(menu->name, TEAR_STRING, TEAR_LEN) == 0)
6564 {
6565 InsertMenu(parent->submenu_id, (UINT)idx, MF_BITMAP|MF_BYPOSITION,
6566 (UINT)menu->id, (LPCTSTR) s_htearbitmap);
6567 }
6568 else
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006569# endif
6570# ifdef FEAT_TOOLBAR
Bram Moolenaar071d4272004-06-13 20:20:40 +00006571 if (menu_is_toolbar(parent->name))
6572 {
6573 TBBUTTON newtb;
6574
Bram Moolenaara80faa82020-04-12 19:37:17 +02006575 CLEAR_FIELD(newtb);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006576 if (menu_is_separator(menu->name))
6577 {
6578 newtb.iBitmap = 0;
6579 newtb.fsStyle = TBSTYLE_SEP;
6580 }
6581 else
6582 {
6583 newtb.iBitmap = get_toolbar_bitmap(menu);
6584 newtb.fsStyle = TBSTYLE_BUTTON;
6585 }
6586 newtb.idCommand = menu->id;
6587 newtb.fsState = TBSTATE_ENABLED;
6588 newtb.iString = 0;
6589 SendMessage(s_toolbarhwnd, TB_INSERTBUTTON, (WPARAM)idx,
6590 (LPARAM)&newtb);
6591 menu->submenu_id = (HMENU)-1;
6592 }
6593 else
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006594# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006595 {
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006596 WCHAR *wn;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006597
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006598 wn = enc_to_utf16(menu->name, NULL);
6599 if (wn != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006600 {
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006601 InsertMenuW(parent->submenu_id, (UINT)idx,
6602 (menu_is_separator(menu->name)
6603 ? MF_SEPARATOR : MF_STRING) | MF_BYPOSITION,
6604 (UINT)menu->id, wn);
6605 vim_free(wn);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006606 }
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006607# ifdef FEAT_TEAROFF
Bram Moolenaar071d4272004-06-13 20:20:40 +00006608 if (IsWindow(parent->tearoff_handle))
6609 rebuild_tearoff(parent);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006610# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006611 }
6612}
6613
6614/*
6615 * Destroy the machine specific menu widget.
6616 */
6617 void
6618gui_mch_destroy_menu(vimmenu_T *menu)
6619{
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006620# ifdef FEAT_TOOLBAR
Bram Moolenaar071d4272004-06-13 20:20:40 +00006621 /*
6622 * is this a toolbar button?
6623 */
6624 if (menu->submenu_id == (HMENU)-1)
6625 {
6626 int iButton;
6627
6628 iButton = (int)SendMessage(s_toolbarhwnd, TB_COMMANDTOINDEX,
6629 (WPARAM)menu->id, 0);
6630 SendMessage(s_toolbarhwnd, TB_DELETEBUTTON, (WPARAM)iButton, 0);
6631 }
6632 else
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006633# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006634 {
6635 if (menu->parent != NULL
6636 && menu_is_popup(menu->parent->dname)
6637 && menu->parent->submenu_id != NULL)
6638 RemoveMenu(menu->parent->submenu_id, menu->id, MF_BYCOMMAND);
6639 else
6640 RemoveMenu(s_menuBar, menu->id, MF_BYCOMMAND);
6641 if (menu->submenu_id != NULL)
6642 DestroyMenu(menu->submenu_id);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006643# ifdef FEAT_TEAROFF
Bram Moolenaar071d4272004-06-13 20:20:40 +00006644 if (IsWindow(menu->tearoff_handle))
6645 DestroyWindow(menu->tearoff_handle);
6646 if (menu->parent != NULL
6647 && menu->parent->children != NULL
6648 && IsWindow(menu->parent->tearoff_handle))
6649 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01006650 // This menu must not show up when rebuilding the tearoff window.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006651 menu->modes = 0;
6652 rebuild_tearoff(menu->parent);
6653 }
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006654# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006655 }
6656}
6657
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006658# ifdef FEAT_TEAROFF
Bram Moolenaar071d4272004-06-13 20:20:40 +00006659 static void
6660rebuild_tearoff(vimmenu_T *menu)
6661{
Bram Moolenaar734a8672019-12-02 22:49:38 +01006662 //hackish
Bram Moolenaar071d4272004-06-13 20:20:40 +00006663 char_u tbuf[128];
6664 RECT trect;
6665 RECT rct;
6666 RECT roct;
6667 int x, y;
6668
6669 HWND thwnd = menu->tearoff_handle;
6670
Bram Moolenaar418f81b2016-02-16 20:12:02 +01006671 GetWindowText(thwnd, (LPSTR)tbuf, 127);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006672 if (GetWindowRect(thwnd, &trect)
6673 && GetWindowRect(s_hwnd, &rct)
6674 && GetClientRect(s_hwnd, &roct))
6675 {
6676 x = trect.left - rct.left;
6677 y = (trect.top - rct.bottom + roct.bottom);
6678 }
6679 else
6680 {
6681 x = y = 0xffffL;
6682 }
6683 DestroyWindow(thwnd);
6684 if (menu->children != NULL)
6685 {
6686 gui_mch_tearoff(tbuf, menu, x, y);
6687 if (IsWindow(menu->tearoff_handle))
6688 (void) SetWindowPos(menu->tearoff_handle,
6689 NULL,
6690 (int)trect.left,
6691 (int)trect.top,
6692 0, 0,
6693 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
6694 }
6695}
Bram Moolenaar734a8672019-12-02 22:49:38 +01006696# endif // FEAT_TEAROFF
Bram Moolenaar071d4272004-06-13 20:20:40 +00006697
6698/*
6699 * Make a menu either grey or not grey.
6700 */
6701 void
6702gui_mch_menu_grey(
6703 vimmenu_T *menu,
6704 int grey)
6705{
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006706# ifdef FEAT_TOOLBAR
Bram Moolenaar071d4272004-06-13 20:20:40 +00006707 /*
6708 * is this a toolbar button?
6709 */
6710 if (menu->submenu_id == (HMENU)-1)
6711 {
6712 SendMessage(s_toolbarhwnd, TB_ENABLEBUTTON,
K.Takata45f9cfb2022-01-21 11:11:00 +00006713 (WPARAM)menu->id, (LPARAM) MAKELONG((grey ? FALSE : TRUE), 0));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006714 }
6715 else
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006716# endif
Bram Moolenaar762f1752016-06-04 22:36:17 +02006717 (void)EnableMenuItem(menu->parent ? menu->parent->submenu_id : s_menuBar,
6718 menu->id, MF_BYCOMMAND | (grey ? MF_GRAYED : MF_ENABLED));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006719
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006720# ifdef FEAT_TEAROFF
Bram Moolenaar071d4272004-06-13 20:20:40 +00006721 if ((menu->parent != NULL) && (IsWindow(menu->parent->tearoff_handle)))
6722 {
6723 WORD menuID;
6724 HWND menuHandle;
6725
6726 /*
6727 * A tearoff button has changed state.
6728 */
6729 if (menu->children == NULL)
6730 menuID = (WORD)(menu->id);
6731 else
Bram Moolenaareb3593b2006-04-22 22:33:57 +00006732 menuID = (WORD)((long_u)(menu->submenu_id) | (DWORD)0x8000);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006733 menuHandle = GetDlgItem(menu->parent->tearoff_handle, menuID);
6734 if (menuHandle)
6735 EnableWindow(menuHandle, !grey);
6736
6737 }
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006738# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006739}
6740
Bram Moolenaar734a8672019-12-02 22:49:38 +01006741#endif // FEAT_MENU
Bram Moolenaar071d4272004-06-13 20:20:40 +00006742
6743
Bram Moolenaar734a8672019-12-02 22:49:38 +01006744// define some macros used to make the dialogue creation more readable
Bram Moolenaar071d4272004-06-13 20:20:40 +00006745
Bram Moolenaar071d4272004-06-13 20:20:40 +00006746#define add_word(x) *p++ = (x)
Bram Moolenaarf4cd3e82005-12-22 22:47:02 +00006747#define add_long(x) dwp = (DWORD *)p; *dwp++ = (x); p = (WORD *)dwp
Bram Moolenaar071d4272004-06-13 20:20:40 +00006748
6749#if defined(FEAT_GUI_DIALOG) || defined(PROTO)
6750/*
6751 * stuff for dialogs
6752 */
6753
6754/*
6755 * The callback routine used by all the dialogs. Very simple. First,
6756 * acknowledges the INITDIALOG message so that Windows knows to do standard
6757 * dialog stuff (Return = default, Esc = cancel....) Second, if a button is
6758 * pressed, return that button's ID - IDCANCEL (2), which is the button's
6759 * number.
6760 */
6761 static LRESULT CALLBACK
6762dialog_callback(
6763 HWND hwnd,
6764 UINT message,
6765 WPARAM wParam,
Bram Moolenaar1266d672017-02-01 13:43:36 +01006766 LPARAM lParam UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006767{
6768 if (message == WM_INITDIALOG)
6769 {
6770 CenterWindow(hwnd, GetWindow(hwnd, GW_OWNER));
Bram Moolenaar734a8672019-12-02 22:49:38 +01006771 // Set focus to the dialog. Set the default button, if specified.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006772 (void)SetFocus(hwnd);
6773 if (dialog_default_button > IDCANCEL)
6774 (void)SetFocus(GetDlgItem(hwnd, dialog_default_button));
Bram Moolenaar2b80e652007-08-14 14:57:55 +00006775 else
Bram Moolenaar734a8672019-12-02 22:49:38 +01006776 // We don't have a default, set focus on another element of the
6777 // dialog window, probably the icon
Bram Moolenaar2b80e652007-08-14 14:57:55 +00006778 (void)SetFocus(GetDlgItem(hwnd, DLG_NONBUTTON_CONTROL));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006779 return FALSE;
6780 }
6781
6782 if (message == WM_COMMAND)
6783 {
6784 int button = LOWORD(wParam);
6785
Bram Moolenaar734a8672019-12-02 22:49:38 +01006786 // Don't end the dialog if something was selected that was
6787 // not a button.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006788 if (button >= DLG_NONBUTTON_CONTROL)
6789 return TRUE;
6790
Bram Moolenaar734a8672019-12-02 22:49:38 +01006791 // If the edit box exists, copy the string.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006792 if (s_textfield != NULL)
Bram Moolenaar3ca9a8a2009-01-28 20:23:17 +00006793 {
Bram Moolenaarc799fe22019-05-28 23:08:19 +02006794 WCHAR *wp = ALLOC_MULT(WCHAR, IOSIZE);
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006795 char_u *p;
Bram Moolenaar3ca9a8a2009-01-28 20:23:17 +00006796
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006797 GetDlgItemTextW(hwnd, DLG_NONBUTTON_CONTROL + 2, wp, IOSIZE);
6798 p = utf16_to_enc(wp, NULL);
6799 vim_strncpy(s_textfield, p, IOSIZE);
6800 vim_free(p);
6801 vim_free(wp);
Bram Moolenaar3ca9a8a2009-01-28 20:23:17 +00006802 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006803
6804 /*
6805 * Need to check for IDOK because if the user just hits Return to
6806 * accept the default value, some reason this is what we get.
6807 */
6808 if (button == IDOK)
6809 {
6810 if (dialog_default_button > IDCANCEL)
6811 EndDialog(hwnd, dialog_default_button);
6812 }
6813 else
6814 EndDialog(hwnd, button - IDCANCEL);
6815 return TRUE;
6816 }
6817
6818 if ((message == WM_SYSCOMMAND) && (wParam == SC_CLOSE))
6819 {
6820 EndDialog(hwnd, 0);
6821 return TRUE;
6822 }
6823 return FALSE;
6824}
6825
6826/*
6827 * Create a dialog dynamically from the parameter strings.
6828 * type = type of dialog (question, alert, etc.)
6829 * title = dialog title. may be NULL for default title.
6830 * message = text to display. Dialog sizes to accommodate it.
6831 * buttons = '\n' separated list of button captions, default first.
6832 * dfltbutton = number of default button.
6833 *
6834 * This routine returns 1 if the first button is pressed,
6835 * 2 for the second, etc.
6836 *
6837 * 0 indicates Esc was pressed.
6838 * -1 for unexpected error
6839 *
6840 * If stubbing out this fn, return 1.
6841 */
6842
Bram Moolenaar734a8672019-12-02 22:49:38 +01006843static const char *dlg_icons[] = // must match names in resource file
Bram Moolenaar071d4272004-06-13 20:20:40 +00006844{
6845 "IDR_VIM",
6846 "IDR_VIM_ERROR",
6847 "IDR_VIM_ALERT",
6848 "IDR_VIM_INFO",
6849 "IDR_VIM_QUESTION"
6850};
6851
Bram Moolenaar071d4272004-06-13 20:20:40 +00006852 int
6853gui_mch_dialog(
6854 int type,
6855 char_u *title,
6856 char_u *message,
6857 char_u *buttons,
6858 int dfltbutton,
Bram Moolenaard2c340a2011-01-17 20:08:11 +01006859 char_u *textfield,
Bram Moolenaarbd67aac2019-09-21 23:09:04 +02006860 int ex_cmd UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006861{
6862 WORD *p, *pdlgtemplate, *pnumitems;
Bram Moolenaarf4cd3e82005-12-22 22:47:02 +00006863 DWORD *dwp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006864 int numButtons;
6865 int *buttonWidths, *buttonPositions;
6866 int buttonYpos;
6867 int nchar, i;
6868 DWORD lStyle;
6869 int dlgwidth = 0;
6870 int dlgheight;
6871 int editboxheight;
6872 int horizWidth = 0;
6873 int msgheight;
6874 char_u *pstart;
6875 char_u *pend;
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00006876 char_u *last_white;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006877 char_u *tbuffer;
6878 RECT rect;
6879 HWND hwnd;
6880 HDC hdc;
6881 HFONT font, oldFont;
6882 TEXTMETRIC fontInfo;
6883 int fontHeight;
6884 int textWidth, minButtonWidth, messageWidth;
6885 int maxDialogWidth;
Bram Moolenaar748bf032005-02-02 23:04:36 +00006886 int maxDialogHeight;
6887 int scroll_flag = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006888 int vertical;
6889 int dlgPaddingX;
6890 int dlgPaddingY;
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006891# ifdef USE_SYSMENU_FONT
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01006892 LOGFONTW lfSysmenu;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006893 int use_lfSysmenu = FALSE;
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006894# endif
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00006895 garray_T ga;
6896 int l;
K.Takatac81e9bf2022-01-16 14:15:49 +00006897 int dlg_icon_width;
6898 int dlg_icon_height;
6899 int dpi;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006900
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006901# ifndef NO_CONSOLE
Bram Moolenaar734a8672019-12-02 22:49:38 +01006902 // Don't output anything in silent mode ("ex -s")
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006903# ifdef VIMDLL
Bram Moolenaarafde13b2019-04-28 19:46:49 +02006904 if (!(gui.in_use || gui.starting))
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006905# endif
Bram Moolenaarafde13b2019-04-28 19:46:49 +02006906 if (silent_mode)
Bram Moolenaar734a8672019-12-02 22:49:38 +01006907 return dfltbutton; // return default option
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006908# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006909
Bram Moolenaar748bf032005-02-02 23:04:36 +00006910 if (s_hwnd == NULL)
K.Takatac81e9bf2022-01-16 14:15:49 +00006911 {
6912 load_dpi_func();
6913 s_dpi = dpi = pGetDpiForSystem();
Bram Moolenaar748bf032005-02-02 23:04:36 +00006914 get_dialog_font_metrics();
K.Takatac81e9bf2022-01-16 14:15:49 +00006915 }
6916 else
6917 dpi = pGetDpiForSystem();
Bram Moolenaar071d4272004-06-13 20:20:40 +00006918
6919 if ((type < 0) || (type > VIM_LAST_TYPE))
6920 type = 0;
6921
Bram Moolenaar734a8672019-12-02 22:49:38 +01006922 // allocate some memory for dialog template
6923 // TODO should compute this really
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00006924 pdlgtemplate = p = (PWORD)LocalAlloc(LPTR,
Bram Moolenaareb3593b2006-04-22 22:33:57 +00006925 DLG_ALLOC_SIZE + STRLEN(message) * 2);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006926
6927 if (p == NULL)
6928 return -1;
6929
6930 /*
Bram Moolenaar84a05ac2013-05-06 04:24:17 +02006931 * make a copy of 'buttons' to fiddle with it. compiler grizzles because
Bram Moolenaar071d4272004-06-13 20:20:40 +00006932 * vim_strsave() doesn't take a const arg (why not?), so cast away the
6933 * const.
6934 */
6935 tbuffer = vim_strsave(buttons);
6936 if (tbuffer == NULL)
6937 return -1;
6938
Bram Moolenaar734a8672019-12-02 22:49:38 +01006939 --dfltbutton; // Change from one-based to zero-based
Bram Moolenaar071d4272004-06-13 20:20:40 +00006940
Bram Moolenaar734a8672019-12-02 22:49:38 +01006941 // Count buttons
Bram Moolenaar071d4272004-06-13 20:20:40 +00006942 numButtons = 1;
6943 for (i = 0; tbuffer[i] != '\0'; i++)
6944 {
6945 if (tbuffer[i] == DLG_BUTTON_SEP)
6946 numButtons++;
6947 }
6948 if (dfltbutton >= numButtons)
6949 dfltbutton = -1;
6950
Bram Moolenaar734a8672019-12-02 22:49:38 +01006951 // Allocate array to hold the width of each button
Bram Moolenaarc799fe22019-05-28 23:08:19 +02006952 buttonWidths = ALLOC_MULT(int, numButtons);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006953 if (buttonWidths == NULL)
6954 return -1;
6955
Bram Moolenaar734a8672019-12-02 22:49:38 +01006956 // Allocate array to hold the X position of each button
Bram Moolenaarc799fe22019-05-28 23:08:19 +02006957 buttonPositions = ALLOC_MULT(int, numButtons);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006958 if (buttonPositions == NULL)
6959 return -1;
6960
6961 /*
6962 * Calculate how big the dialog must be.
6963 */
6964 hwnd = GetDesktopWindow();
6965 hdc = GetWindowDC(hwnd);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006966# ifdef USE_SYSMENU_FONT
Bram Moolenaar071d4272004-06-13 20:20:40 +00006967 if (gui_w32_get_menu_font(&lfSysmenu) == OK)
6968 {
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01006969 font = CreateFontIndirectW(&lfSysmenu);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006970 use_lfSysmenu = TRUE;
6971 }
6972 else
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006973# endif
K.Takatad1c58992022-01-23 12:31:57 +00006974 font = CreateFont(-DLG_FONT_POINT_SIZE, 0, 0, 0, 0, 0, 0, 0,
6975 0, 0, 0, 0, VARIABLE_PITCH, DLG_FONT_NAME);
6976
6977 oldFont = SelectFont(hdc, font);
6978 dlgPaddingX = DLG_PADDING_X;
6979 dlgPaddingY = DLG_PADDING_Y;
6980
Bram Moolenaar071d4272004-06-13 20:20:40 +00006981 GetTextMetrics(hdc, &fontInfo);
6982 fontHeight = fontInfo.tmHeight;
6983
Bram Moolenaar734a8672019-12-02 22:49:38 +01006984 // Minimum width for horizontal button
Bram Moolenaar418f81b2016-02-16 20:12:02 +01006985 minButtonWidth = GetTextWidth(hdc, (char_u *)"Cancel", 6);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006986
Bram Moolenaar734a8672019-12-02 22:49:38 +01006987 // Maximum width of a dialog, if possible
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00006988 if (s_hwnd == NULL)
6989 {
6990 RECT workarea_rect;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006991
Bram Moolenaar734a8672019-12-02 22:49:38 +01006992 // We don't have a window, use the desktop area.
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00006993 get_work_area(&workarea_rect);
6994 maxDialogWidth = workarea_rect.right - workarea_rect.left - 100;
K.Takatac81e9bf2022-01-16 14:15:49 +00006995 if (maxDialogWidth > adjust_by_system_dpi(600))
6996 maxDialogWidth = adjust_by_system_dpi(600);
Bram Moolenaar734a8672019-12-02 22:49:38 +01006997 // Leave some room for the taskbar.
Bram Moolenaar1b1b0942013-08-01 13:20:42 +02006998 maxDialogHeight = workarea_rect.bottom - workarea_rect.top - 150;
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00006999 }
7000 else
7001 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01007002 // Use our own window for the size, unless it's very small.
Bram Moolenaara95d8232013-08-07 15:27:11 +02007003 GetWindowRect(s_hwnd, &rect);
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007004 maxDialogWidth = rect.right - rect.left
K.Takatac81e9bf2022-01-16 14:15:49 +00007005 - (pGetSystemMetricsForDpi(SM_CXFRAME, dpi) +
7006 pGetSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi)) * 2;
7007 if (maxDialogWidth < adjust_by_system_dpi(DLG_MIN_MAX_WIDTH))
7008 maxDialogWidth = adjust_by_system_dpi(DLG_MIN_MAX_WIDTH);
Bram Moolenaar748bf032005-02-02 23:04:36 +00007009
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007010 maxDialogHeight = rect.bottom - rect.top
K.Takatac81e9bf2022-01-16 14:15:49 +00007011 - (pGetSystemMetricsForDpi(SM_CYFRAME, dpi) +
7012 pGetSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi)) * 4
7013 - pGetSystemMetricsForDpi(SM_CYCAPTION, dpi);
7014 if (maxDialogHeight < adjust_by_system_dpi(DLG_MIN_MAX_HEIGHT))
7015 maxDialogHeight = adjust_by_system_dpi(DLG_MIN_MAX_HEIGHT);
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007016 }
7017
Bram Moolenaar734a8672019-12-02 22:49:38 +01007018 // Set dlgwidth to width of message.
7019 // Copy the message into "ga", changing NL to CR-NL and inserting line
7020 // breaks where needed.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007021 pstart = message;
7022 messageWidth = 0;
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007023 msgheight = 0;
7024 ga_init2(&ga, sizeof(char), 500);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007025 do
7026 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01007027 msgheight += fontHeight; // at least one line
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007028
Bram Moolenaar734a8672019-12-02 22:49:38 +01007029 // Need to figure out where to break the string. The system does it
7030 // at a word boundary, which would mean we can't compute the number of
7031 // wrapped lines.
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007032 textWidth = 0;
7033 last_white = NULL;
7034 for (pend = pstart; *pend != NUL && *pend != '\n'; )
Bram Moolenaar748bf032005-02-02 23:04:36 +00007035 {
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00007036 l = (*mb_ptr2len)(pend);
Bram Moolenaar1c465442017-03-12 20:10:05 +01007037 if (l == 1 && VIM_ISWHITE(*pend)
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007038 && textWidth > maxDialogWidth * 3 / 4)
7039 last_white = pend;
Bram Moolenaarf05d8112013-06-26 12:58:32 +02007040 textWidth += GetTextWidthEnc(hdc, pend, l);
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007041 if (textWidth >= maxDialogWidth)
Bram Moolenaar748bf032005-02-02 23:04:36 +00007042 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01007043 // Line will wrap.
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007044 messageWidth = maxDialogWidth;
Bram Moolenaar748bf032005-02-02 23:04:36 +00007045 msgheight += fontHeight;
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007046 textWidth = 0;
7047
7048 if (last_white != NULL)
7049 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01007050 // break the line just after a space
K.Takatac81e9bf2022-01-16 14:15:49 +00007051 if (pend > last_white)
7052 ga.ga_len -= (int)(pend - (last_white + 1));
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007053 pend = last_white + 1;
7054 last_white = NULL;
7055 }
7056 ga_append(&ga, '\r');
7057 ga_append(&ga, '\n');
7058 continue;
Bram Moolenaar748bf032005-02-02 23:04:36 +00007059 }
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007060
7061 while (--l >= 0)
7062 ga_append(&ga, *pend++);
Bram Moolenaar748bf032005-02-02 23:04:36 +00007063 }
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007064 if (textWidth > messageWidth)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007065 messageWidth = textWidth;
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007066
7067 ga_append(&ga, '\r');
7068 ga_append(&ga, '\n');
Bram Moolenaar071d4272004-06-13 20:20:40 +00007069 pstart = pend + 1;
7070 } while (*pend != NUL);
Bram Moolenaar748bf032005-02-02 23:04:36 +00007071
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007072 if (ga.ga_data != NULL)
7073 message = ga.ga_data;
7074
Bram Moolenaar734a8672019-12-02 22:49:38 +01007075 messageWidth += 10; // roundoff space
Bram Moolenaar748bf032005-02-02 23:04:36 +00007076
K.Takatac81e9bf2022-01-16 14:15:49 +00007077 dlg_icon_width = adjust_by_system_dpi(DLG_ICON_WIDTH);
7078 dlg_icon_height = adjust_by_system_dpi(DLG_ICON_HEIGHT);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007079
K.Takatac81e9bf2022-01-16 14:15:49 +00007080 // Add width of icon to dlgwidth, and some space
7081 dlgwidth = messageWidth + dlg_icon_width + 3 * dlgPaddingX
7082 + pGetSystemMetricsForDpi(SM_CXVSCROLL, dpi);
7083
7084 if (msgheight < dlg_icon_height)
7085 msgheight = dlg_icon_height;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007086
7087 /*
7088 * Check button names. A long one will make the dialog wider.
Bram Moolenaaraea02fa2007-05-04 20:29:09 +00007089 * When called early (-register error message) p_go isn't initialized.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007090 */
Bram Moolenaaraea02fa2007-05-04 20:29:09 +00007091 vertical = (p_go != NULL && vim_strchr(p_go, GO_VERTICAL) != NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007092 if (!vertical)
7093 {
7094 // Place buttons horizontally if they fit.
7095 horizWidth = dlgPaddingX;
7096 pstart = tbuffer;
7097 i = 0;
7098 do
7099 {
7100 pend = vim_strchr(pstart, DLG_BUTTON_SEP);
7101 if (pend == NULL)
7102 pend = pstart + STRLEN(pstart); // Last button name.
Bram Moolenaarb052fe02013-06-26 13:16:20 +02007103 textWidth = GetTextWidthEnc(hdc, pstart, (int)(pend - pstart));
Bram Moolenaar071d4272004-06-13 20:20:40 +00007104 if (textWidth < minButtonWidth)
7105 textWidth = minButtonWidth;
Bram Moolenaar734a8672019-12-02 22:49:38 +01007106 textWidth += dlgPaddingX; // Padding within button
Bram Moolenaar071d4272004-06-13 20:20:40 +00007107 buttonWidths[i] = textWidth;
7108 buttonPositions[i++] = horizWidth;
Bram Moolenaar734a8672019-12-02 22:49:38 +01007109 horizWidth += textWidth + dlgPaddingX; // Pad between buttons
Bram Moolenaar071d4272004-06-13 20:20:40 +00007110 pstart = pend + 1;
7111 } while (*pend != NUL);
7112
7113 if (horizWidth > maxDialogWidth)
7114 vertical = TRUE; // Too wide to fit on the screen.
7115 else if (horizWidth > dlgwidth)
7116 dlgwidth = horizWidth;
7117 }
7118
7119 if (vertical)
7120 {
7121 // Stack buttons vertically.
7122 pstart = tbuffer;
7123 do
7124 {
7125 pend = vim_strchr(pstart, DLG_BUTTON_SEP);
7126 if (pend == NULL)
7127 pend = pstart + STRLEN(pstart); // Last button name.
Bram Moolenaarb052fe02013-06-26 13:16:20 +02007128 textWidth = GetTextWidthEnc(hdc, pstart, (int)(pend - pstart));
Bram Moolenaar734a8672019-12-02 22:49:38 +01007129 textWidth += dlgPaddingX; // Padding within button
7130 textWidth += DLG_VERT_PADDING_X * 2; // Padding around button
Bram Moolenaar071d4272004-06-13 20:20:40 +00007131 if (textWidth > dlgwidth)
7132 dlgwidth = textWidth;
7133 pstart = pend + 1;
7134 } while (*pend != NUL);
7135 }
7136
7137 if (dlgwidth < DLG_MIN_WIDTH)
Bram Moolenaar734a8672019-12-02 22:49:38 +01007138 dlgwidth = DLG_MIN_WIDTH; // Don't allow a really thin dialog!
Bram Moolenaar071d4272004-06-13 20:20:40 +00007139
Bram Moolenaar734a8672019-12-02 22:49:38 +01007140 // start to fill in the dlgtemplate information. addressing by WORDs
K.Takatad1c58992022-01-23 12:31:57 +00007141 lStyle = DS_MODALFRAME | WS_CAPTION | DS_3DLOOK | WS_VISIBLE | DS_SETFONT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007142
7143 add_long(lStyle);
7144 add_long(0); // (lExtendedStyle)
Bram Moolenaar734a8672019-12-02 22:49:38 +01007145 pnumitems = p; //save where the number of items must be stored
Bram Moolenaar071d4272004-06-13 20:20:40 +00007146 add_word(0); // NumberOfItems(will change later)
7147 add_word(10); // x
7148 add_word(10); // y
7149 add_word(PixelToDialogX(dlgwidth)); // cx
7150
7151 // Dialog height.
7152 if (vertical)
Bram Moolenaara95d8232013-08-07 15:27:11 +02007153 dlgheight = msgheight + 2 * dlgPaddingY
7154 + DLG_VERT_PADDING_Y + 2 * fontHeight * numButtons;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007155 else
7156 dlgheight = msgheight + 3 * dlgPaddingY + 2 * fontHeight;
7157
7158 // Dialog needs to be taller if contains an edit box.
7159 editboxheight = fontHeight + dlgPaddingY + 4 * DLG_VERT_PADDING_Y;
7160 if (textfield != NULL)
7161 dlgheight += editboxheight;
7162
Bram Moolenaar734a8672019-12-02 22:49:38 +01007163 // Restrict the size to a maximum. Causes a scrollbar to show up.
Bram Moolenaara95d8232013-08-07 15:27:11 +02007164 if (dlgheight > maxDialogHeight)
7165 {
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02007166 msgheight = msgheight - (dlgheight - maxDialogHeight);
7167 dlgheight = maxDialogHeight;
7168 scroll_flag = WS_VSCROLL;
Bram Moolenaar734a8672019-12-02 22:49:38 +01007169 // Make sure scrollbar doesn't appear in the middle of the dialog
K.Takatac81e9bf2022-01-16 14:15:49 +00007170 messageWidth = dlgwidth - dlg_icon_width - 3 * dlgPaddingX;
Bram Moolenaara95d8232013-08-07 15:27:11 +02007171 }
7172
Bram Moolenaar071d4272004-06-13 20:20:40 +00007173 add_word(PixelToDialogY(dlgheight));
7174
7175 add_word(0); // Menu
7176 add_word(0); // Class
7177
Bram Moolenaar734a8672019-12-02 22:49:38 +01007178 // copy the title of the dialog
Bram Moolenaar6edeaf32017-09-26 14:46:04 +02007179 nchar = nCopyAnsiToWideChar(p, (title ? (LPSTR)title
7180 : (LPSTR)("Vim "VIM_VERSION_MEDIUM)), TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007181 p += nchar;
7182
K.Takatad1c58992022-01-23 12:31:57 +00007183 // do the font, since DS_3DLOOK doesn't work properly
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01007184# ifdef USE_SYSMENU_FONT
K.Takatad1c58992022-01-23 12:31:57 +00007185 if (use_lfSysmenu)
7186 {
7187 // point size
7188 *p++ = -MulDiv(lfSysmenu.lfHeight, 72,
7189 GetDeviceCaps(hdc, LOGPIXELSY));
7190 wcscpy(p, lfSysmenu.lfFaceName);
7191 nchar = (int)wcslen(lfSysmenu.lfFaceName) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007192 }
K.Takatad1c58992022-01-23 12:31:57 +00007193 else
7194# endif
7195 {
7196 *p++ = DLG_FONT_POINT_SIZE; // point size
7197 nchar = nCopyAnsiToWideChar(p, DLG_FONT_NAME, FALSE);
7198 }
7199 p += nchar;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007200
7201 buttonYpos = msgheight + 2 * dlgPaddingY;
7202
7203 if (textfield != NULL)
7204 buttonYpos += editboxheight;
7205
7206 pstart = tbuffer;
7207 if (!vertical)
Bram Moolenaar734a8672019-12-02 22:49:38 +01007208 horizWidth = (dlgwidth - horizWidth) / 2; // Now it's X offset
Bram Moolenaar071d4272004-06-13 20:20:40 +00007209 for (i = 0; i < numButtons; i++)
7210 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01007211 // get end of this button.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007212 for ( pend = pstart;
7213 *pend && (*pend != DLG_BUTTON_SEP);
7214 pend++)
7215 ;
7216
7217 if (*pend)
7218 *pend = '\0';
7219
7220 /*
7221 * old NOTE:
7222 * setting the BS_DEFPUSHBUTTON style doesn't work because Windows sets
7223 * the focus to the first tab-able button and in so doing makes that
7224 * the default!! Grrr. Workaround: Make the default button the only
7225 * one with WS_TABSTOP style. Means user can't tab between buttons, but
7226 * he/she can use arrow keys.
7227 *
7228 * new NOTE: BS_DEFPUSHBUTTON is required to be able to select the
Bram Moolenaar2c7a7632007-05-10 18:19:11 +00007229 * right button when hitting <Enter>. E.g., for the ":confirm quit"
Bram Moolenaar071d4272004-06-13 20:20:40 +00007230 * dialog. Also needed for when the textfield is the default control.
7231 * It appears to work now (perhaps not on Win95?).
7232 */
7233 if (vertical)
7234 {
7235 p = add_dialog_element(p,
7236 (i == dfltbutton
7237 ? BS_DEFPUSHBUTTON : BS_PUSHBUTTON) | WS_TABSTOP,
7238 PixelToDialogX(DLG_VERT_PADDING_X),
Bram Moolenaar734a8672019-12-02 22:49:38 +01007239 PixelToDialogY(buttonYpos // TBK
Bram Moolenaar071d4272004-06-13 20:20:40 +00007240 + 2 * fontHeight * i),
7241 PixelToDialogX(dlgwidth - 2 * DLG_VERT_PADDING_X),
7242 (WORD)(PixelToDialogY(2 * fontHeight) - 1),
Bram Moolenaar418f81b2016-02-16 20:12:02 +01007243 (WORD)(IDCANCEL + 1 + i), (WORD)0x0080, (char *)pstart);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007244 }
7245 else
7246 {
7247 p = add_dialog_element(p,
7248 (i == dfltbutton
7249 ? BS_DEFPUSHBUTTON : BS_PUSHBUTTON) | WS_TABSTOP,
7250 PixelToDialogX(horizWidth + buttonPositions[i]),
Bram Moolenaar734a8672019-12-02 22:49:38 +01007251 PixelToDialogY(buttonYpos), // TBK
Bram Moolenaar071d4272004-06-13 20:20:40 +00007252 PixelToDialogX(buttonWidths[i]),
7253 (WORD)(PixelToDialogY(2 * fontHeight) - 1),
Bram Moolenaar418f81b2016-02-16 20:12:02 +01007254 (WORD)(IDCANCEL + 1 + i), (WORD)0x0080, (char *)pstart);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007255 }
Bram Moolenaar734a8672019-12-02 22:49:38 +01007256 pstart = pend + 1; //next button
Bram Moolenaar071d4272004-06-13 20:20:40 +00007257 }
7258 *pnumitems += numButtons;
7259
Bram Moolenaar734a8672019-12-02 22:49:38 +01007260 // Vim icon
Bram Moolenaar071d4272004-06-13 20:20:40 +00007261 p = add_dialog_element(p, SS_ICON,
7262 PixelToDialogX(dlgPaddingX),
7263 PixelToDialogY(dlgPaddingY),
K.Takatac81e9bf2022-01-16 14:15:49 +00007264 PixelToDialogX(dlg_icon_width),
7265 PixelToDialogY(dlg_icon_height),
Bram Moolenaar071d4272004-06-13 20:20:40 +00007266 DLG_NONBUTTON_CONTROL + 0, (WORD)0x0082,
7267 dlg_icons[type]);
7268
Bram Moolenaar734a8672019-12-02 22:49:38 +01007269 // Dialog message
Bram Moolenaar748bf032005-02-02 23:04:36 +00007270 p = add_dialog_element(p, ES_LEFT|scroll_flag|ES_MULTILINE|ES_READONLY,
K.Takatac81e9bf2022-01-16 14:15:49 +00007271 PixelToDialogX(2 * dlgPaddingX + dlg_icon_width),
Bram Moolenaar748bf032005-02-02 23:04:36 +00007272 PixelToDialogY(dlgPaddingY),
7273 (WORD)(PixelToDialogX(messageWidth) + 1),
7274 PixelToDialogY(msgheight),
Bram Moolenaar418f81b2016-02-16 20:12:02 +01007275 DLG_NONBUTTON_CONTROL + 1, (WORD)0x0081, (char *)message);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007276
Bram Moolenaar734a8672019-12-02 22:49:38 +01007277 // Edit box
Bram Moolenaar071d4272004-06-13 20:20:40 +00007278 if (textfield != NULL)
7279 {
7280 p = add_dialog_element(p, ES_LEFT|ES_AUTOHSCROLL|WS_TABSTOP|WS_BORDER,
7281 PixelToDialogX(2 * dlgPaddingX),
7282 PixelToDialogY(2 * dlgPaddingY + msgheight),
7283 PixelToDialogX(dlgwidth - 4 * dlgPaddingX),
7284 PixelToDialogY(fontHeight + dlgPaddingY),
Bram Moolenaar418f81b2016-02-16 20:12:02 +01007285 DLG_NONBUTTON_CONTROL + 2, (WORD)0x0081, (char *)textfield);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007286 *pnumitems += 1;
7287 }
7288
7289 *pnumitems += 2;
7290
7291 SelectFont(hdc, oldFont);
7292 DeleteObject(font);
7293 ReleaseDC(hwnd, hdc);
7294
Bram Moolenaar734a8672019-12-02 22:49:38 +01007295 // Let the dialog_callback() function know which button to make default
7296 // If we have an edit box, make that the default. We also need to tell
7297 // dialog_callback() if this dialog contains an edit box or not. We do
7298 // this by setting s_textfield if it does.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007299 if (textfield != NULL)
7300 {
7301 dialog_default_button = DLG_NONBUTTON_CONTROL + 2;
7302 s_textfield = textfield;
7303 }
7304 else
7305 {
7306 dialog_default_button = IDCANCEL + 1 + dfltbutton;
7307 s_textfield = NULL;
7308 }
7309
Bram Moolenaar734a8672019-12-02 22:49:38 +01007310 // show the dialog box modally and get a return value
Bram Moolenaar071d4272004-06-13 20:20:40 +00007311 nchar = (int)DialogBoxIndirect(
Bram Moolenaarafde13b2019-04-28 19:46:49 +02007312 g_hinst,
Bram Moolenaar071d4272004-06-13 20:20:40 +00007313 (LPDLGTEMPLATE)pdlgtemplate,
7314 s_hwnd,
7315 (DLGPROC)dialog_callback);
7316
7317 LocalFree(LocalHandle(pdlgtemplate));
7318 vim_free(tbuffer);
7319 vim_free(buttonWidths);
7320 vim_free(buttonPositions);
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007321 vim_free(ga.ga_data);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007322
Bram Moolenaar734a8672019-12-02 22:49:38 +01007323 // Focus back to our window (for when MDI is used).
Bram Moolenaar071d4272004-06-13 20:20:40 +00007324 (void)SetFocus(s_hwnd);
7325
7326 return nchar;
7327}
7328
Bram Moolenaar734a8672019-12-02 22:49:38 +01007329#endif // FEAT_GUI_DIALOG
Bram Moolenaareb3593b2006-04-22 22:33:57 +00007330
Bram Moolenaar071d4272004-06-13 20:20:40 +00007331/*
7332 * Put a simple element (basic class) onto a dialog template in memory.
7333 * return a pointer to where the next item should be added.
7334 *
7335 * parameters:
7336 * lStyle = additional style flags
7337 * (be careful, NT3.51 & Win32s will ignore the new ones)
7338 * x,y = x & y positions IN DIALOG UNITS
7339 * w,h = width and height IN DIALOG UNITS
7340 * Id = ID used in messages
7341 * clss = class ID, e.g 0x0080 for a button, 0x0082 for a static
7342 * caption = usually text or resource name
7343 *
7344 * TODO: use the length information noted here to enable the dialog creation
7345 * routines to work out more exactly how much memory they need to alloc.
7346 */
7347 static PWORD
7348add_dialog_element(
7349 PWORD p,
7350 DWORD lStyle,
7351 WORD x,
7352 WORD y,
7353 WORD w,
7354 WORD h,
7355 WORD Id,
7356 WORD clss,
7357 const char *caption)
7358{
7359 int nchar;
7360
Bram Moolenaar734a8672019-12-02 22:49:38 +01007361 p = lpwAlign(p); // Align to dword boundary
Bram Moolenaar071d4272004-06-13 20:20:40 +00007362 lStyle = lStyle | WS_VISIBLE | WS_CHILD;
7363 *p++ = LOWORD(lStyle);
7364 *p++ = HIWORD(lStyle);
7365 *p++ = 0; // LOWORD (lExtendedStyle)
7366 *p++ = 0; // HIWORD (lExtendedStyle)
7367 *p++ = x;
7368 *p++ = y;
7369 *p++ = w;
7370 *p++ = h;
7371 *p++ = Id; //9 or 10 words in all
7372
7373 *p++ = (WORD)0xffff;
7374 *p++ = clss; //2 more here
7375
Bram Moolenaar6edeaf32017-09-26 14:46:04 +02007376 nchar = nCopyAnsiToWideChar(p, (LPSTR)caption, TRUE); //strlen(caption)+1
Bram Moolenaar071d4272004-06-13 20:20:40 +00007377 p += nchar;
7378
7379 *p++ = 0; // advance pointer over nExtraStuff WORD - 2 more
7380
Dominique Pelleaf4a61a2021-12-27 17:21:41 +00007381 return p; // total = 15 + strlen(caption) words
7382 // bytes read = 2 * total
Bram Moolenaar071d4272004-06-13 20:20:40 +00007383}
7384
7385
7386/*
7387 * Helper routine. Take an input pointer, return closest pointer that is
7388 * aligned on a DWORD (4 byte) boundary. Taken from the Win32SDK samples.
7389 */
7390 static LPWORD
7391lpwAlign(
7392 LPWORD lpIn)
7393{
Bram Moolenaareb3593b2006-04-22 22:33:57 +00007394 long_u ul;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007395
Bram Moolenaareb3593b2006-04-22 22:33:57 +00007396 ul = (long_u)lpIn;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007397 ul += 3;
7398 ul >>= 2;
7399 ul <<= 2;
7400 return (LPWORD)ul;
7401}
7402
7403/*
7404 * Helper routine. Takes second parameter as Ansi string, copies it to first
7405 * parameter as wide character (16-bits / char) string, and returns integer
7406 * number of wide characters (words) in string (including the trailing wide
7407 * char NULL). Partly taken from the Win32SDK samples.
Bram Moolenaar6edeaf32017-09-26 14:46:04 +02007408 * If "use_enc" is TRUE, 'encoding' is used for "lpAnsiIn". If FALSE, current
7409 * ACP is used for "lpAnsiIn". */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007410 static int
7411nCopyAnsiToWideChar(
7412 LPWORD lpWCStr,
Bram Moolenaar6edeaf32017-09-26 14:46:04 +02007413 LPSTR lpAnsiIn,
7414 BOOL use_enc)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007415{
7416 int nChar = 0;
Bram Moolenaar734a8672019-12-02 22:49:38 +01007417 int len = lstrlen(lpAnsiIn) + 1; // include NUL character
Bram Moolenaar071d4272004-06-13 20:20:40 +00007418 int i;
7419 WCHAR *wn;
7420
Bram Moolenaar6edeaf32017-09-26 14:46:04 +02007421 if (use_enc && enc_codepage >= 0 && (int)GetACP() != enc_codepage)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007422 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01007423 // Not a codepage, use our own conversion function.
Bram Moolenaar418f81b2016-02-16 20:12:02 +01007424 wn = enc_to_utf16((char_u *)lpAnsiIn, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007425 if (wn != NULL)
7426 {
7427 wcscpy(lpWCStr, wn);
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00007428 nChar = (int)wcslen(wn) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007429 vim_free(wn);
7430 }
7431 }
7432 if (nChar == 0)
Bram Moolenaar734a8672019-12-02 22:49:38 +01007433 // Use Win32 conversion function.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007434 nChar = MultiByteToWideChar(
7435 enc_codepage > 0 ? enc_codepage : CP_ACP,
7436 MB_PRECOMPOSED,
7437 lpAnsiIn, len,
7438 lpWCStr, len);
7439 for (i = 0; i < nChar; ++i)
Bram Moolenaar734a8672019-12-02 22:49:38 +01007440 if (lpWCStr[i] == (WORD)'\t') // replace tabs with spaces
Bram Moolenaar071d4272004-06-13 20:20:40 +00007441 lpWCStr[i] = (WORD)' ';
Bram Moolenaar071d4272004-06-13 20:20:40 +00007442
7443 return nChar;
7444}
7445
7446
7447#ifdef FEAT_TEAROFF
7448/*
Bram Moolenaar66857f42017-10-22 16:43:20 +02007449 * Lookup menu handle from "menu_id".
7450 */
7451 static HMENU
7452tearoff_lookup_menuhandle(
7453 vimmenu_T *menu,
7454 WORD menu_id)
7455{
7456 for ( ; menu != NULL; menu = menu->next)
7457 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01007458 if (menu->modes == 0) // this menu has just been deleted
Bram Moolenaar66857f42017-10-22 16:43:20 +02007459 continue;
7460 if (menu_is_separator(menu->dname))
7461 continue;
7462 if ((WORD)((long_u)(menu->submenu_id) | (DWORD)0x8000) == menu_id)
7463 return menu->submenu_id;
7464 }
7465 return NULL;
7466}
7467
7468/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00007469 * The callback function for all the modeless dialogs that make up the
7470 * "tearoff menus" Very simple - forward button presses (to fool Vim into
7471 * thinking its menus have been clicked), and go away when closed.
7472 */
7473 static LRESULT CALLBACK
7474tearoff_callback(
7475 HWND hwnd,
7476 UINT message,
7477 WPARAM wParam,
7478 LPARAM lParam)
7479{
7480 if (message == WM_INITDIALOG)
Bram Moolenaar66857f42017-10-22 16:43:20 +02007481 {
7482 SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR)lParam);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007483 return (TRUE);
Bram Moolenaar66857f42017-10-22 16:43:20 +02007484 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007485
Bram Moolenaar734a8672019-12-02 22:49:38 +01007486 // May show the mouse pointer again.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007487 HandleMouseHide(message, lParam);
7488
7489 if (message == WM_COMMAND)
7490 {
7491 if ((WORD)(LOWORD(wParam)) & 0x8000)
7492 {
7493 POINT mp;
7494 RECT rect;
7495
7496 if (GetCursorPos(&mp) && GetWindowRect(hwnd, &rect))
7497 {
Bram Moolenaar66857f42017-10-22 16:43:20 +02007498 vimmenu_T *menu;
7499
7500 menu = (vimmenu_T*)GetWindowLongPtr(hwnd, DWLP_USER);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007501 (void)TrackPopupMenu(
Bram Moolenaar66857f42017-10-22 16:43:20 +02007502 tearoff_lookup_menuhandle(menu, LOWORD(wParam)),
Bram Moolenaar071d4272004-06-13 20:20:40 +00007503 TPM_LEFTALIGN | TPM_LEFTBUTTON,
7504 (int)rect.right - 8,
7505 (int)mp.y,
Bram Moolenaar734a8672019-12-02 22:49:38 +01007506 (int)0, // reserved param
Bram Moolenaar071d4272004-06-13 20:20:40 +00007507 s_hwnd,
7508 NULL);
7509 /*
7510 * NOTE: The pop-up menu can eat the mouse up event.
7511 * We deal with this in normal.c.
7512 */
7513 }
7514 }
7515 else
Bram Moolenaar734a8672019-12-02 22:49:38 +01007516 // Pass on messages to the main Vim window
Bram Moolenaar071d4272004-06-13 20:20:40 +00007517 PostMessage(s_hwnd, WM_COMMAND, LOWORD(wParam), 0);
7518 /*
7519 * Give main window the focus back: this is so after
7520 * choosing a tearoff button you can start typing again
7521 * straight away.
7522 */
7523 (void)SetFocus(s_hwnd);
7524 return TRUE;
7525 }
7526 if ((message == WM_SYSCOMMAND) && (wParam == SC_CLOSE))
7527 {
7528 DestroyWindow(hwnd);
7529 return TRUE;
7530 }
7531
Bram Moolenaar734a8672019-12-02 22:49:38 +01007532 // When moved around, give main window the focus back.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007533 if (message == WM_EXITSIZEMOVE)
7534 (void)SetActiveWindow(s_hwnd);
7535
7536 return FALSE;
7537}
7538#endif
7539
7540
7541/*
K.Takatad1c58992022-01-23 12:31:57 +00007542 * Computes the dialog base units based on the current dialog font.
7543 * We don't use the GetDialogBaseUnits() API, because we don't use the
7544 * (old-style) system font.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007545 */
7546 static void
7547get_dialog_font_metrics(void)
7548{
7549 HDC hdc;
7550 HFONT hfontTools = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007551 SIZE size;
7552#ifdef USE_SYSMENU_FONT
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01007553 LOGFONTW lfSysmenu;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007554#endif
7555
Bram Moolenaar071d4272004-06-13 20:20:40 +00007556#ifdef USE_SYSMENU_FONT
Bram Moolenaarcea912a2016-10-12 14:20:24 +02007557 if (gui_w32_get_menu_font(&lfSysmenu) == OK)
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01007558 hfontTools = CreateFontIndirectW(&lfSysmenu);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02007559 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00007560#endif
7561 hfontTools = CreateFont(-DLG_FONT_POINT_SIZE, 0, 0, 0, 0, 0, 0, 0,
K.Takatac81e9bf2022-01-16 14:15:49 +00007562 0, 0, 0, 0, VARIABLE_PITCH, DLG_FONT_NAME);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007563
K.Takatad1c58992022-01-23 12:31:57 +00007564 hdc = GetDC(s_hwnd);
7565 SelectObject(hdc, hfontTools);
7566 /*
7567 * GetTextMetrics() doesn't return the right value in
7568 * tmAveCharWidth, so we have to figure out the dialog base units
7569 * ourselves.
7570 */
7571 GetTextExtentPoint(hdc,
7572 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
7573 52, &size);
7574 ReleaseDC(s_hwnd, hdc);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007575
K.Takatad1c58992022-01-23 12:31:57 +00007576 s_dlgfntwidth = (WORD)((size.cx / 26 + 1) / 2);
7577 s_dlgfntheight = (WORD)size.cy;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007578}
7579
7580#if defined(FEAT_MENU) && defined(FEAT_TEAROFF)
7581/*
7582 * Create a pseudo-"tearoff menu" based on the child
7583 * items of a given menu pointer.
7584 */
7585 static void
7586gui_mch_tearoff(
7587 char_u *title,
7588 vimmenu_T *menu,
7589 int initX,
7590 int initY)
7591{
7592 WORD *p, *pdlgtemplate, *pnumitems, *ptrueheight;
7593 int template_len;
7594 int nchar, textWidth, submenuWidth;
7595 DWORD lStyle;
7596 DWORD lExtendedStyle;
7597 WORD dlgwidth;
7598 WORD menuID;
7599 vimmenu_T *pmenu;
Bram Moolenaar66857f42017-10-22 16:43:20 +02007600 vimmenu_T *top_menu;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007601 vimmenu_T *the_menu = menu;
7602 HWND hwnd;
7603 HDC hdc;
7604 HFONT font, oldFont;
7605 int col, spaceWidth, len;
7606 int columnWidths[2];
7607 char_u *label, *text;
7608 int acLen = 0;
7609 int nameLen;
7610 int padding0, padding1, padding2 = 0;
7611 int sepPadding=0;
Bram Moolenaar9588a0f2005-01-08 21:45:39 +00007612 int x;
7613 int y;
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01007614# ifdef USE_SYSMENU_FONT
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01007615 LOGFONTW lfSysmenu;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007616 int use_lfSysmenu = FALSE;
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01007617# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007618
7619 /*
7620 * If this menu is already torn off, move it to the mouse position.
7621 */
7622 if (IsWindow(menu->tearoff_handle))
7623 {
7624 POINT mp;
K.Takata45f9cfb2022-01-21 11:11:00 +00007625 if (GetCursorPos(&mp))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007626 {
7627 SetWindowPos(menu->tearoff_handle, NULL, mp.x, mp.y, 0, 0,
7628 SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
7629 }
7630 return;
7631 }
7632
7633 /*
7634 * Create a new tearoff.
7635 */
7636 if (*title == MNU_HIDDEN_CHAR)
7637 title++;
7638
Bram Moolenaar734a8672019-12-02 22:49:38 +01007639 // Allocate memory to store the dialog template. It's made bigger when
7640 // needed.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007641 template_len = DLG_ALLOC_SIZE;
7642 pdlgtemplate = p = (WORD *)LocalAlloc(LPTR, template_len);
7643 if (p == NULL)
7644 return;
7645
7646 hwnd = GetDesktopWindow();
7647 hdc = GetWindowDC(hwnd);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01007648# ifdef USE_SYSMENU_FONT
Bram Moolenaar071d4272004-06-13 20:20:40 +00007649 if (gui_w32_get_menu_font(&lfSysmenu) == OK)
7650 {
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01007651 font = CreateFontIndirectW(&lfSysmenu);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007652 use_lfSysmenu = TRUE;
7653 }
7654 else
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01007655# endif
K.Takatad1c58992022-01-23 12:31:57 +00007656 font = CreateFont(-DLG_FONT_POINT_SIZE, 0, 0, 0, 0, 0, 0, 0,
7657 0, 0, 0, 0, VARIABLE_PITCH, DLG_FONT_NAME);
7658
7659 oldFont = SelectFont(hdc, font);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007660
Bram Moolenaar734a8672019-12-02 22:49:38 +01007661 // Calculate width of a single space. Used for padding columns to the
7662 // right width.
Bram Moolenaar418f81b2016-02-16 20:12:02 +01007663 spaceWidth = GetTextWidth(hdc, (char_u *)" ", 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007664
Bram Moolenaar734a8672019-12-02 22:49:38 +01007665 // Figure out max width of the text column, the accelerator column and the
7666 // optional submenu column.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007667 submenuWidth = 0;
7668 for (col = 0; col < 2; col++)
7669 {
7670 columnWidths[col] = 0;
Bram Moolenaar00d253e2020-04-06 22:13:01 +02007671 FOR_ALL_CHILD_MENUS(menu, pmenu)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007672 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01007673 // Use "dname" here to compute the width of the visible text.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007674 text = (col == 0) ? pmenu->dname : pmenu->actext;
7675 if (text != NULL && *text != NUL)
7676 {
7677 textWidth = GetTextWidthEnc(hdc, text, (int)STRLEN(text));
7678 if (textWidth > columnWidths[col])
7679 columnWidths[col] = textWidth;
7680 }
7681 if (pmenu->children != NULL)
7682 submenuWidth = TEAROFF_COLUMN_PADDING * spaceWidth;
7683 }
7684 }
7685 if (columnWidths[1] == 0)
7686 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01007687 // no accelerators
Bram Moolenaar071d4272004-06-13 20:20:40 +00007688 if (submenuWidth != 0)
7689 columnWidths[0] += submenuWidth;
7690 else
7691 columnWidths[0] += spaceWidth;
7692 }
7693 else
7694 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01007695 // there is an accelerator column
Bram Moolenaar071d4272004-06-13 20:20:40 +00007696 columnWidths[0] += TEAROFF_COLUMN_PADDING * spaceWidth;
7697 columnWidths[1] += submenuWidth;
7698 }
7699
7700 /*
7701 * Now find the total width of our 'menu'.
7702 */
7703 textWidth = columnWidths[0] + columnWidths[1];
7704 if (submenuWidth != 0)
7705 {
Bram Moolenaar418f81b2016-02-16 20:12:02 +01007706 submenuWidth = GetTextWidth(hdc, (char_u *)TEAROFF_SUBMENU_LABEL,
Bram Moolenaar071d4272004-06-13 20:20:40 +00007707 (int)STRLEN(TEAROFF_SUBMENU_LABEL));
7708 textWidth += submenuWidth;
7709 }
7710 dlgwidth = GetTextWidthEnc(hdc, title, (int)STRLEN(title));
7711 if (textWidth > dlgwidth)
7712 dlgwidth = textWidth;
7713 dlgwidth += 2 * TEAROFF_PADDING_X + TEAROFF_BUTTON_PAD_X;
7714
Bram Moolenaar734a8672019-12-02 22:49:38 +01007715 // start to fill in the dlgtemplate information. addressing by WORDs
K.Takatad1c58992022-01-23 12:31:57 +00007716 lStyle = DS_MODALFRAME | WS_CAPTION | WS_SYSMENU | DS_SETFONT | WS_VISIBLE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007717
7718 lExtendedStyle = WS_EX_TOOLWINDOW|WS_EX_STATICEDGE;
7719 *p++ = LOWORD(lStyle);
7720 *p++ = HIWORD(lStyle);
7721 *p++ = LOWORD(lExtendedStyle);
7722 *p++ = HIWORD(lExtendedStyle);
Bram Moolenaar734a8672019-12-02 22:49:38 +01007723 pnumitems = p; // save where the number of items must be stored
Bram Moolenaar071d4272004-06-13 20:20:40 +00007724 *p++ = 0; // NumberOfItems(will change later)
Bram Moolenaar9588a0f2005-01-08 21:45:39 +00007725 gui_mch_getmouse(&x, &y);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007726 if (initX == 0xffffL)
Bram Moolenaar9588a0f2005-01-08 21:45:39 +00007727 *p++ = PixelToDialogX(x); // x
Bram Moolenaar071d4272004-06-13 20:20:40 +00007728 else
7729 *p++ = PixelToDialogX(initX); // x
7730 if (initY == 0xffffL)
Bram Moolenaar9588a0f2005-01-08 21:45:39 +00007731 *p++ = PixelToDialogY(y); // y
Bram Moolenaar071d4272004-06-13 20:20:40 +00007732 else
7733 *p++ = PixelToDialogY(initY); // y
7734 *p++ = PixelToDialogX(dlgwidth); // cx
7735 ptrueheight = p;
7736 *p++ = 0; // dialog height: changed later anyway
7737 *p++ = 0; // Menu
7738 *p++ = 0; // Class
7739
Bram Moolenaar734a8672019-12-02 22:49:38 +01007740 // copy the title of the dialog
Bram Moolenaar071d4272004-06-13 20:20:40 +00007741 nchar = nCopyAnsiToWideChar(p, ((*title)
Bram Moolenaar6edeaf32017-09-26 14:46:04 +02007742 ? (LPSTR)title
7743 : (LPSTR)("Vim "VIM_VERSION_MEDIUM)), TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007744 p += nchar;
7745
K.Takatad1c58992022-01-23 12:31:57 +00007746 // do the font, since DS_3DLOOK doesn't work properly
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01007747# ifdef USE_SYSMENU_FONT
K.Takatad1c58992022-01-23 12:31:57 +00007748 if (use_lfSysmenu)
7749 {
7750 // point size
7751 *p++ = -MulDiv(lfSysmenu.lfHeight, 72,
7752 GetDeviceCaps(hdc, LOGPIXELSY));
7753 wcscpy(p, lfSysmenu.lfFaceName);
7754 nchar = (int)wcslen(lfSysmenu.lfFaceName) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007755 }
K.Takatad1c58992022-01-23 12:31:57 +00007756 else
7757# endif
7758 {
7759 *p++ = DLG_FONT_POINT_SIZE; // point size
7760 nchar = nCopyAnsiToWideChar(p, DLG_FONT_NAME, FALSE);
7761 }
7762 p += nchar;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007763
7764 /*
7765 * Loop over all the items in the menu.
7766 * But skip over the tearbar.
7767 */
7768 if (STRCMP(menu->children->name, TEAR_STRING) == 0)
7769 menu = menu->children->next;
7770 else
7771 menu = menu->children;
Bram Moolenaar66857f42017-10-22 16:43:20 +02007772 top_menu = menu;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007773 for ( ; menu != NULL; menu = menu->next)
7774 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01007775 if (menu->modes == 0) // this menu has just been deleted
Bram Moolenaar071d4272004-06-13 20:20:40 +00007776 continue;
7777 if (menu_is_separator(menu->dname))
7778 {
7779 sepPadding += 3;
7780 continue;
7781 }
7782
Bram Moolenaar734a8672019-12-02 22:49:38 +01007783 // Check if there still is plenty of room in the template. Make it
7784 // larger when needed.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007785 if (((char *)p - (char *)pdlgtemplate) + 1000 > template_len)
7786 {
7787 WORD *newp;
7788
7789 newp = (WORD *)LocalAlloc(LPTR, template_len + 4096);
7790 if (newp != NULL)
7791 {
7792 template_len += 4096;
7793 mch_memmove(newp, pdlgtemplate,
7794 (char *)p - (char *)pdlgtemplate);
7795 p = newp + (p - pdlgtemplate);
7796 pnumitems = newp + (pnumitems - pdlgtemplate);
7797 ptrueheight = newp + (ptrueheight - pdlgtemplate);
7798 LocalFree(LocalHandle(pdlgtemplate));
7799 pdlgtemplate = newp;
7800 }
7801 }
7802
Bram Moolenaar734a8672019-12-02 22:49:38 +01007803 // Figure out minimal length of this menu label. Use "name" for the
7804 // actual text, "dname" for estimating the displayed size. "name"
7805 // has "&a" for mnemonic and includes the accelerator.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007806 len = nameLen = (int)STRLEN(menu->name);
7807 padding0 = (columnWidths[0] - GetTextWidthEnc(hdc, menu->dname,
7808 (int)STRLEN(menu->dname))) / spaceWidth;
7809 len += padding0;
7810
7811 if (menu->actext != NULL)
7812 {
7813 acLen = (int)STRLEN(menu->actext);
7814 len += acLen;
7815 textWidth = GetTextWidthEnc(hdc, menu->actext, acLen);
7816 }
7817 else
7818 textWidth = 0;
7819 padding1 = (columnWidths[1] - textWidth) / spaceWidth;
7820 len += padding1;
7821
7822 if (menu->children == NULL)
7823 {
7824 padding2 = submenuWidth / spaceWidth;
7825 len += padding2;
7826 menuID = (WORD)(menu->id);
7827 }
7828 else
7829 {
7830 len += (int)STRLEN(TEAROFF_SUBMENU_LABEL);
Bram Moolenaareb3593b2006-04-22 22:33:57 +00007831 menuID = (WORD)((long_u)(menu->submenu_id) | (DWORD)0x8000);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007832 }
7833
Bram Moolenaar734a8672019-12-02 22:49:38 +01007834 // Allocate menu label and fill it in
Bram Moolenaar964b3742019-05-24 18:54:09 +02007835 text = label = alloc(len + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007836 if (label == NULL)
7837 break;
7838
Bram Moolenaarce0842a2005-07-18 21:58:11 +00007839 vim_strncpy(text, menu->name, nameLen);
Bram Moolenaar734a8672019-12-02 22:49:38 +01007840 text = vim_strchr(text, TAB); // stop at TAB before actext
Bram Moolenaar071d4272004-06-13 20:20:40 +00007841 if (text == NULL)
Bram Moolenaar734a8672019-12-02 22:49:38 +01007842 text = label + nameLen; // no actext, use whole name
Bram Moolenaar071d4272004-06-13 20:20:40 +00007843 while (padding0-- > 0)
7844 *text++ = ' ';
7845 if (menu->actext != NULL)
7846 {
7847 STRNCPY(text, menu->actext, acLen);
7848 text += acLen;
7849 }
7850 while (padding1-- > 0)
7851 *text++ = ' ';
7852 if (menu->children != NULL)
7853 {
7854 STRCPY(text, TEAROFF_SUBMENU_LABEL);
7855 text += STRLEN(TEAROFF_SUBMENU_LABEL);
7856 }
7857 else
7858 {
7859 while (padding2-- > 0)
7860 *text++ = ' ';
7861 }
7862 *text = NUL;
7863
7864 /*
7865 * BS_LEFT will just be ignored on Win32s/NT3.5x - on
7866 * W95/NT4 it makes the tear-off look more like a menu.
7867 */
7868 p = add_dialog_element(p,
7869 BS_PUSHBUTTON|BS_LEFT,
7870 (WORD)PixelToDialogX(TEAROFF_PADDING_X),
7871 (WORD)(sepPadding + 1 + 13 * (*pnumitems)),
7872 (WORD)PixelToDialogX(dlgwidth - 2 * TEAROFF_PADDING_X),
7873 (WORD)12,
Bram Moolenaar418f81b2016-02-16 20:12:02 +01007874 menuID, (WORD)0x0080, (char *)label);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007875 vim_free(label);
7876 (*pnumitems)++;
7877 }
7878
7879 *ptrueheight = (WORD)(sepPadding + 1 + 13 * (*pnumitems));
7880
7881
Bram Moolenaar734a8672019-12-02 22:49:38 +01007882 // show modelessly
Bram Moolenaar66857f42017-10-22 16:43:20 +02007883 the_menu->tearoff_handle = CreateDialogIndirectParam(
Bram Moolenaarafde13b2019-04-28 19:46:49 +02007884 g_hinst,
Bram Moolenaar071d4272004-06-13 20:20:40 +00007885 (LPDLGTEMPLATE)pdlgtemplate,
7886 s_hwnd,
Bram Moolenaar66857f42017-10-22 16:43:20 +02007887 (DLGPROC)tearoff_callback,
7888 (LPARAM)top_menu);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007889
7890 LocalFree(LocalHandle(pdlgtemplate));
7891 SelectFont(hdc, oldFont);
7892 DeleteObject(font);
7893 ReleaseDC(hwnd, hdc);
7894
7895 /*
7896 * Reassert ourselves as the active window. This is so that after creating
7897 * a tearoff, the user doesn't have to click with the mouse just to start
7898 * typing again!
7899 */
7900 (void)SetActiveWindow(s_hwnd);
7901
Bram Moolenaar734a8672019-12-02 22:49:38 +01007902 // make sure the right buttons are enabled
Bram Moolenaar071d4272004-06-13 20:20:40 +00007903 force_menu_update = TRUE;
7904}
7905#endif
7906
7907#if defined(FEAT_TOOLBAR) || defined(PROTO)
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01007908# include "gui_w32_rc.h"
Bram Moolenaar071d4272004-06-13 20:20:40 +00007909
Bram Moolenaar734a8672019-12-02 22:49:38 +01007910// This not defined in older SDKs
Bram Moolenaar071d4272004-06-13 20:20:40 +00007911# ifndef TBSTYLE_FLAT
7912# define TBSTYLE_FLAT 0x0800
7913# endif
7914
7915/*
7916 * Create the toolbar, initially unpopulated.
7917 * (just like the menu, there are no defaults, it's all
7918 * set up through menu.vim)
7919 */
7920 static void
7921initialise_toolbar(void)
7922{
7923 InitCommonControls();
7924 s_toolbarhwnd = CreateToolbarEx(
7925 s_hwnd,
7926 WS_CHILD | TBSTYLE_TOOLTIPS | TBSTYLE_FLAT,
7927 4000, //any old big number
Bram Moolenaar2c7a7632007-05-10 18:19:11 +00007928 31, //number of images in initial bitmap
Bram Moolenaarafde13b2019-04-28 19:46:49 +02007929 g_hinst,
Bram Moolenaar071d4272004-06-13 20:20:40 +00007930 IDR_TOOLBAR1, // id of initial bitmap
7931 NULL,
7932 0, // initial number of buttons
7933 TOOLBAR_BUTTON_WIDTH, //api guide is wrong!
7934 TOOLBAR_BUTTON_HEIGHT,
7935 TOOLBAR_BUTTON_WIDTH,
7936 TOOLBAR_BUTTON_HEIGHT,
7937 sizeof(TBBUTTON)
7938 );
Bram Moolenaar5f763342019-11-17 22:54:10 +01007939
7940 // Remove transparency from the toolbar to prevent the main window
7941 // background colour showing through
7942 SendMessage(s_toolbarhwnd, TB_SETSTYLE, 0,
7943 SendMessage(s_toolbarhwnd, TB_GETSTYLE, 0, 0) & ~TBSTYLE_TRANSPARENT);
7944
Bram Moolenaar5f919ee2013-07-21 17:46:43 +02007945 s_toolbar_wndproc = SubclassWindow(s_toolbarhwnd, toolbar_wndproc);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007946
7947 gui_mch_show_toolbar(vim_strchr(p_go, GO_TOOLBAR) != NULL);
K.Takatac81e9bf2022-01-16 14:15:49 +00007948
7949 update_toolbar_size();
7950}
7951
7952 static void
7953update_toolbar_size(void)
7954{
7955 int w, h;
7956 TBMETRICS tbm = {sizeof(TBMETRICS)};
7957
7958 tbm.dwMask = TBMF_PAD | TBMF_BUTTONSPACING;
7959 SendMessage(s_toolbarhwnd, TB_GETMETRICS, 0, (LPARAM)&tbm);
7960 //TRACE("Pad: %d, %d", tbm.cxPad, tbm.cyPad);
7961 //TRACE("ButtonSpacing: %d, %d", tbm.cxButtonSpacing, tbm.cyButtonSpacing);
7962
7963 w = (TOOLBAR_BUTTON_WIDTH + tbm.cxPad) * s_dpi / DEFAULT_DPI;
7964 h = (TOOLBAR_BUTTON_HEIGHT + tbm.cyPad) * s_dpi / DEFAULT_DPI;
7965 //TRACE("button size: %d, %d", w, h);
7966 SendMessage(s_toolbarhwnd, TB_SETBUTTONSIZE, 0, MAKELPARAM(w, h));
7967 gui.toolbar_height = h + 6;
7968
7969 //DWORD s = SendMessage(s_toolbarhwnd, TB_GETBUTTONSIZE, 0, 0);
7970 //TRACE("actual button size: %d, %d", LOWORD(s), HIWORD(s));
7971
7972 // TODO:
7973 // Currently, this function only updates the size of toolbar buttons.
7974 // It would be nice if the toolbar images are resized based on DPI.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007975}
7976
Bram Moolenaar5f919ee2013-07-21 17:46:43 +02007977 static LRESULT CALLBACK
7978toolbar_wndproc(
7979 HWND hwnd,
7980 UINT uMsg,
7981 WPARAM wParam,
7982 LPARAM lParam)
7983{
7984 HandleMouseHide(uMsg, lParam);
7985 return CallWindowProc(s_toolbar_wndproc, hwnd, uMsg, wParam, lParam);
7986}
7987
Bram Moolenaar071d4272004-06-13 20:20:40 +00007988 static int
7989get_toolbar_bitmap(vimmenu_T *menu)
7990{
7991 int i = -1;
7992
7993 /*
7994 * Check user bitmaps first, unless builtin is specified.
7995 */
Bram Moolenaarcea912a2016-10-12 14:20:24 +02007996 if (!menu->icon_builtin)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007997 {
7998 char_u fname[MAXPATHL];
7999 HANDLE hbitmap = NULL;
8000
8001 if (menu->iconfile != NULL)
8002 {
8003 gui_find_iconfile(menu->iconfile, fname, "bmp");
8004 hbitmap = LoadImage(
8005 NULL,
Bram Moolenaar418f81b2016-02-16 20:12:02 +01008006 (LPCSTR)fname,
Bram Moolenaar071d4272004-06-13 20:20:40 +00008007 IMAGE_BITMAP,
8008 TOOLBAR_BUTTON_WIDTH,
8009 TOOLBAR_BUTTON_HEIGHT,
8010 LR_LOADFROMFILE |
8011 LR_LOADMAP3DCOLORS
8012 );
8013 }
8014
8015 /*
8016 * If the LoadImage call failed, or the "icon=" file
8017 * didn't exist or wasn't specified, try the menu name
8018 */
8019 if (hbitmap == NULL
Bram Moolenaara5f5c8b2013-06-27 22:29:38 +02008020 && (gui_find_bitmap(
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01008021# ifdef FEAT_MULTI_LANG
Bram Moolenaara5f5c8b2013-06-27 22:29:38 +02008022 menu->en_dname != NULL ? menu->en_dname :
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01008023# endif
Bram Moolenaara5f5c8b2013-06-27 22:29:38 +02008024 menu->dname, fname, "bmp") == OK))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008025 hbitmap = LoadImage(
8026 NULL,
Bram Moolenaar418f81b2016-02-16 20:12:02 +01008027 (LPCSTR)fname,
Bram Moolenaar071d4272004-06-13 20:20:40 +00008028 IMAGE_BITMAP,
8029 TOOLBAR_BUTTON_WIDTH,
8030 TOOLBAR_BUTTON_HEIGHT,
8031 LR_LOADFROMFILE |
8032 LR_LOADMAP3DCOLORS
8033 );
8034
8035 if (hbitmap != NULL)
8036 {
8037 TBADDBITMAP tbAddBitmap;
8038
8039 tbAddBitmap.hInst = NULL;
Bram Moolenaareb3593b2006-04-22 22:33:57 +00008040 tbAddBitmap.nID = (long_u)hbitmap;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008041
8042 i = (int)SendMessage(s_toolbarhwnd, TB_ADDBITMAP,
8043 (WPARAM)1, (LPARAM)&tbAddBitmap);
Bram Moolenaar734a8672019-12-02 22:49:38 +01008044 // i will be set to -1 if it fails
Bram Moolenaar071d4272004-06-13 20:20:40 +00008045 }
8046 }
8047 if (i == -1 && menu->iconidx >= 0 && menu->iconidx < TOOLBAR_BITMAP_COUNT)
8048 i = menu->iconidx;
8049
8050 return i;
8051}
8052#endif
8053
Bram Moolenaar3991dab2006-03-27 17:01:56 +00008054#if defined(FEAT_GUI_TABLINE) || defined(PROTO)
8055 static void
8056initialise_tabline(void)
8057{
8058 InitCommonControls();
8059
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008060 s_tabhwnd = CreateWindow(WC_TABCONTROL, "Vim tabline",
Bram Moolenaareb3593b2006-04-22 22:33:57 +00008061 WS_CHILD|TCS_FOCUSNEVER|TCS_TOOLTIPS,
Bram Moolenaar3991dab2006-03-27 17:01:56 +00008062 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
Bram Moolenaarafde13b2019-04-28 19:46:49 +02008063 CW_USEDEFAULT, s_hwnd, NULL, g_hinst, NULL);
Bram Moolenaar5f919ee2013-07-21 17:46:43 +02008064 s_tabline_wndproc = SubclassWindow(s_tabhwnd, tabline_wndproc);
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008065
Bram Moolenaar551dbcc2006-04-25 22:13:59 +00008066 gui.tabline_height = TABLINE_HEIGHT;
8067
Bram Moolenaar551dbcc2006-04-25 22:13:59 +00008068 set_tabline_font();
Bram Moolenaar3991dab2006-03-27 17:01:56 +00008069}
Bram Moolenaar5f919ee2013-07-21 17:46:43 +02008070
Bram Moolenaarca05aa22017-10-22 15:36:14 +02008071/*
8072 * Get tabpage_T from POINT.
8073 */
8074 static tabpage_T *
8075GetTabFromPoint(
8076 HWND hWnd,
8077 POINT pt)
8078{
8079 tabpage_T *ptp = NULL;
8080
8081 if (gui_mch_showing_tabline())
8082 {
8083 TCHITTESTINFO htinfo;
8084 htinfo.pt = pt;
K.Takatac81e9bf2022-01-16 14:15:49 +00008085 // ignore if a window under cursor is not tabcontrol.
Bram Moolenaarca05aa22017-10-22 15:36:14 +02008086 if (s_tabhwnd == hWnd)
8087 {
8088 int idx = TabCtrl_HitTest(s_tabhwnd, &htinfo);
8089 if (idx != -1)
8090 ptp = find_tabpage(idx + 1);
8091 }
8092 }
8093 return ptp;
8094}
8095
8096static POINT s_pt = {0, 0};
8097static HCURSOR s_hCursor = NULL;
8098
Bram Moolenaar5f919ee2013-07-21 17:46:43 +02008099 static LRESULT CALLBACK
8100tabline_wndproc(
8101 HWND hwnd,
8102 UINT uMsg,
8103 WPARAM wParam,
8104 LPARAM lParam)
8105{
Bram Moolenaarca05aa22017-10-22 15:36:14 +02008106 POINT pt;
8107 tabpage_T *tp;
8108 RECT rect;
8109 int nCenter;
8110 int idx0;
8111 int idx1;
8112
Bram Moolenaar5f919ee2013-07-21 17:46:43 +02008113 HandleMouseHide(uMsg, lParam);
Bram Moolenaarca05aa22017-10-22 15:36:14 +02008114
8115 switch (uMsg)
8116 {
8117 case WM_LBUTTONDOWN:
8118 {
8119 s_pt.x = GET_X_LPARAM(lParam);
8120 s_pt.y = GET_Y_LPARAM(lParam);
8121 SetCapture(hwnd);
Bram Moolenaar734a8672019-12-02 22:49:38 +01008122 s_hCursor = GetCursor(); // backup default cursor
Bram Moolenaarca05aa22017-10-22 15:36:14 +02008123 break;
8124 }
8125 case WM_MOUSEMOVE:
8126 if (GetCapture() == hwnd
8127 && ((wParam & MK_LBUTTON)) != 0)
8128 {
8129 pt.x = GET_X_LPARAM(lParam);
8130 pt.y = s_pt.y;
K.Takatac81e9bf2022-01-16 14:15:49 +00008131 if (abs(pt.x - s_pt.x) >
8132 pGetSystemMetricsForDpi(SM_CXDRAG, s_dpi))
Bram Moolenaarca05aa22017-10-22 15:36:14 +02008133 {
8134 SetCursor(LoadCursor(NULL, IDC_SIZEWE));
8135
8136 tp = GetTabFromPoint(hwnd, pt);
8137 if (tp != NULL)
8138 {
8139 idx0 = tabpage_index(curtab) - 1;
8140 idx1 = tabpage_index(tp) - 1;
8141
8142 TabCtrl_GetItemRect(hwnd, idx1, &rect);
8143 nCenter = rect.left + (rect.right - rect.left) / 2;
8144
Bram Moolenaar734a8672019-12-02 22:49:38 +01008145 // Check if the mouse cursor goes over the center of
8146 // the next tab to prevent "flickering".
Bram Moolenaarca05aa22017-10-22 15:36:14 +02008147 if ((idx0 < idx1) && (nCenter < pt.x))
8148 {
8149 tabpage_move(idx1 + 1);
8150 update_screen(0);
8151 }
8152 else if ((idx1 < idx0) && (pt.x < nCenter))
8153 {
8154 tabpage_move(idx1);
8155 update_screen(0);
8156 }
8157 }
8158 }
8159 }
8160 break;
8161 case WM_LBUTTONUP:
8162 {
8163 if (GetCapture() == hwnd)
8164 {
8165 SetCursor(s_hCursor);
8166 ReleaseCapture();
8167 }
8168 break;
8169 }
8170 default:
8171 break;
8172 }
8173
Bram Moolenaar5f919ee2013-07-21 17:46:43 +02008174 return CallWindowProc(s_tabline_wndproc, hwnd, uMsg, wParam, lParam);
8175}
Bram Moolenaar3991dab2006-03-27 17:01:56 +00008176#endif
8177
Bram Moolenaar071d4272004-06-13 20:20:40 +00008178#if defined(FEAT_OLE) || defined(FEAT_EVAL) || defined(PROTO)
8179/*
8180 * Make the GUI window come to the foreground.
8181 */
8182 void
8183gui_mch_set_foreground(void)
8184{
8185 if (IsIconic(s_hwnd))
8186 SendMessage(s_hwnd, WM_SYSCOMMAND, SC_RESTORE, 0);
8187 SetForegroundWindow(s_hwnd);
8188}
8189#endif
8190
8191#if defined(FEAT_MBYTE_IME) && defined(DYNAMIC_IME)
8192 static void
8193dyn_imm_load(void)
8194{
Bram Moolenaarebbcb822010-10-23 14:02:54 +02008195 hLibImm = vimLoadLib("imm32.dll");
Bram Moolenaar071d4272004-06-13 20:20:40 +00008196 if (hLibImm == NULL)
8197 return;
8198
Bram Moolenaar071d4272004-06-13 20:20:40 +00008199 pImmGetCompositionStringW
8200 = (void *)GetProcAddress(hLibImm, "ImmGetCompositionStringW");
8201 pImmGetContext
8202 = (void *)GetProcAddress(hLibImm, "ImmGetContext");
8203 pImmAssociateContext
8204 = (void *)GetProcAddress(hLibImm, "ImmAssociateContext");
8205 pImmReleaseContext
8206 = (void *)GetProcAddress(hLibImm, "ImmReleaseContext");
8207 pImmGetOpenStatus
8208 = (void *)GetProcAddress(hLibImm, "ImmGetOpenStatus");
8209 pImmSetOpenStatus
8210 = (void *)GetProcAddress(hLibImm, "ImmSetOpenStatus");
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01008211 pImmGetCompositionFontW
8212 = (void *)GetProcAddress(hLibImm, "ImmGetCompositionFontW");
8213 pImmSetCompositionFontW
8214 = (void *)GetProcAddress(hLibImm, "ImmSetCompositionFontW");
Bram Moolenaar071d4272004-06-13 20:20:40 +00008215 pImmSetCompositionWindow
8216 = (void *)GetProcAddress(hLibImm, "ImmSetCompositionWindow");
8217 pImmGetConversionStatus
8218 = (void *)GetProcAddress(hLibImm, "ImmGetConversionStatus");
Bram Moolenaarca003e12006-03-17 23:19:38 +00008219 pImmSetConversionStatus
8220 = (void *)GetProcAddress(hLibImm, "ImmSetConversionStatus");
Bram Moolenaar071d4272004-06-13 20:20:40 +00008221
K.Takatab0b2b732022-01-19 12:59:21 +00008222 if ( pImmGetCompositionStringW == NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00008223 || pImmGetContext == NULL
8224 || pImmAssociateContext == NULL
8225 || pImmReleaseContext == NULL
8226 || pImmGetOpenStatus == NULL
8227 || pImmSetOpenStatus == NULL
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01008228 || pImmGetCompositionFontW == NULL
8229 || pImmSetCompositionFontW == NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00008230 || pImmSetCompositionWindow == NULL
Bram Moolenaarca003e12006-03-17 23:19:38 +00008231 || pImmGetConversionStatus == NULL
8232 || pImmSetConversionStatus == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008233 {
8234 FreeLibrary(hLibImm);
8235 hLibImm = NULL;
8236 pImmGetContext = NULL;
8237 return;
8238 }
8239
8240 return;
8241}
8242
Bram Moolenaar071d4272004-06-13 20:20:40 +00008243#endif
8244
8245#if defined(FEAT_SIGN_ICONS) || defined(PROTO)
8246
8247# ifdef FEAT_XPM_W32
8248# define IMAGE_XPM 100
8249# endif
8250
8251typedef struct _signicon_t
8252{
8253 HANDLE hImage;
8254 UINT uType;
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01008255# ifdef FEAT_XPM_W32
Bram Moolenaar734a8672019-12-02 22:49:38 +01008256 HANDLE hShape; // Mask bitmap handle
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01008257# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008258} signicon_t;
8259
8260 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01008261gui_mch_drawsign(int row, int col, int typenr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008262{
8263 signicon_t *sign;
8264 int x, y, w, h;
8265
8266 if (!gui.in_use || (sign = (signicon_t *)sign_get_image(typenr)) == NULL)
8267 return;
8268
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01008269# if defined(FEAT_DIRECTX)
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01008270 if (IS_ENABLE_DIRECTX())
8271 DWriteContext_Flush(s_dwc);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01008272# endif
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01008273
Bram Moolenaar071d4272004-06-13 20:20:40 +00008274 x = TEXT_X(col);
8275 y = TEXT_Y(row);
8276 w = gui.char_width * 2;
8277 h = gui.char_height;
8278 switch (sign->uType)
8279 {
8280 case IMAGE_BITMAP:
8281 {
8282 HDC hdcMem;
8283 HBITMAP hbmpOld;
8284
8285 hdcMem = CreateCompatibleDC(s_hdc);
8286 hbmpOld = (HBITMAP)SelectObject(hdcMem, sign->hImage);
8287 BitBlt(s_hdc, x, y, w, h, hdcMem, 0, 0, SRCCOPY);
8288 SelectObject(hdcMem, hbmpOld);
8289 DeleteDC(hdcMem);
8290 }
8291 break;
8292 case IMAGE_ICON:
8293 case IMAGE_CURSOR:
8294 DrawIconEx(s_hdc, x, y, (HICON)sign->hImage, w, h, 0, NULL, DI_NORMAL);
8295 break;
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01008296# ifdef FEAT_XPM_W32
Bram Moolenaar071d4272004-06-13 20:20:40 +00008297 case IMAGE_XPM:
8298 {
8299 HDC hdcMem;
8300 HBITMAP hbmpOld;
8301
8302 hdcMem = CreateCompatibleDC(s_hdc);
8303 hbmpOld = (HBITMAP)SelectObject(hdcMem, sign->hShape);
Bram Moolenaar734a8672019-12-02 22:49:38 +01008304 // Make hole
Bram Moolenaar071d4272004-06-13 20:20:40 +00008305 BitBlt(s_hdc, x, y, w, h, hdcMem, 0, 0, SRCAND);
8306
8307 SelectObject(hdcMem, sign->hImage);
Bram Moolenaar734a8672019-12-02 22:49:38 +01008308 // Paint sign
Bram Moolenaar071d4272004-06-13 20:20:40 +00008309 BitBlt(s_hdc, x, y, w, h, hdcMem, 0, 0, SRCPAINT);
8310 SelectObject(hdcMem, hbmpOld);
8311 DeleteDC(hdcMem);
8312 }
8313 break;
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01008314# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008315 }
8316}
8317
8318 static void
8319close_signicon_image(signicon_t *sign)
8320{
8321 if (sign)
8322 switch (sign->uType)
8323 {
8324 case IMAGE_BITMAP:
8325 DeleteObject((HGDIOBJ)sign->hImage);
8326 break;
8327 case IMAGE_CURSOR:
8328 DestroyCursor((HCURSOR)sign->hImage);
8329 break;
8330 case IMAGE_ICON:
8331 DestroyIcon((HICON)sign->hImage);
8332 break;
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01008333# ifdef FEAT_XPM_W32
Bram Moolenaar071d4272004-06-13 20:20:40 +00008334 case IMAGE_XPM:
8335 DeleteObject((HBITMAP)sign->hImage);
8336 DeleteObject((HBITMAP)sign->hShape);
8337 break;
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01008338# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008339 }
8340}
8341
8342 void *
Bram Moolenaar68c2f632016-01-30 17:24:07 +01008343gui_mch_register_sign(char_u *signfile)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008344{
8345 signicon_t sign, *psign;
8346 char_u *ext;
8347
Bram Moolenaar071d4272004-06-13 20:20:40 +00008348 sign.hImage = NULL;
Bram Moolenaar734a8672019-12-02 22:49:38 +01008349 ext = signfile + STRLEN(signfile) - 4; // get extension
Bram Moolenaar071d4272004-06-13 20:20:40 +00008350 if (ext > signfile)
8351 {
8352 int do_load = 1;
8353
8354 if (!STRICMP(ext, ".bmp"))
8355 sign.uType = IMAGE_BITMAP;
8356 else if (!STRICMP(ext, ".ico"))
8357 sign.uType = IMAGE_ICON;
8358 else if (!STRICMP(ext, ".cur") || !STRICMP(ext, ".ani"))
8359 sign.uType = IMAGE_CURSOR;
8360 else
8361 do_load = 0;
8362
8363 if (do_load)
Bram Moolenaar418f81b2016-02-16 20:12:02 +01008364 sign.hImage = (HANDLE)LoadImage(NULL, (LPCSTR)signfile, sign.uType,
Bram Moolenaar071d4272004-06-13 20:20:40 +00008365 gui.char_width * 2, gui.char_height,
8366 LR_LOADFROMFILE | LR_CREATEDIBSECTION);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01008367# ifdef FEAT_XPM_W32
Bram Moolenaar071d4272004-06-13 20:20:40 +00008368 if (!STRICMP(ext, ".xpm"))
8369 {
8370 sign.uType = IMAGE_XPM;
Bram Moolenaar418f81b2016-02-16 20:12:02 +01008371 LoadXpmImage((char *)signfile, (HBITMAP *)&sign.hImage,
8372 (HBITMAP *)&sign.hShape);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008373 }
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01008374# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008375 }
8376
8377 psign = NULL;
Bram Moolenaarc799fe22019-05-28 23:08:19 +02008378 if (sign.hImage && (psign = ALLOC_ONE(signicon_t)) != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008379 *psign = sign;
8380
8381 if (!psign)
8382 {
8383 if (sign.hImage)
8384 close_signicon_image(&sign);
Bram Moolenaar74409f62022-01-01 15:58:22 +00008385 emsg(_(e_couldnt_read_in_sign_data));
Bram Moolenaar071d4272004-06-13 20:20:40 +00008386 }
8387 return (void *)psign;
8388
8389}
8390
8391 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01008392gui_mch_destroy_sign(void *sign)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008393{
8394 if (sign)
8395 {
8396 close_signicon_image((signicon_t *)sign);
8397 vim_free(sign);
8398 }
8399}
Bram Moolenaar5c06f8b2005-05-31 22:14:58 +00008400#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008401
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01008402#if defined(FEAT_BEVAL_GUI) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008403
Bram Moolenaar734a8672019-12-02 22:49:38 +01008404/*
8405 * BALLOON-EVAL IMPLEMENTATION FOR WINDOWS.
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00008406 * Added by Sergey Khorev <sergey.khorev@gmail.com>
Bram Moolenaar071d4272004-06-13 20:20:40 +00008407 *
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01008408 * The only reused thing is beval.h and get_beval_info()
Bram Moolenaar071d4272004-06-13 20:20:40 +00008409 * from gui_beval.c (note it uses x and y of the BalloonEval struct
8410 * to get current mouse position).
8411 *
8412 * Trying to use as more Windows services as possible, and as less
8413 * IE version as possible :)).
8414 *
8415 * 1) Don't create ToolTip in gui_mch_create_beval_area, only initialize
8416 * BalloonEval struct.
8417 * 2) Enable/Disable simply create/kill BalloonEval Timer
8418 * 3) When there was enough inactivity, timer procedure posts
8419 * async request to debugger
8420 * 4) gui_mch_post_balloon (invoked from netbeans.c) creates tooltip control
8421 * and performs some actions to show it ASAP
Bram Moolenaar446cb832008-06-24 21:56:24 +00008422 * 5) WM_NOTIFY:TTN_POP destroys created tooltip
Bram Moolenaar071d4272004-06-13 20:20:40 +00008423 */
8424
Bram Moolenaard385b5d2018-12-27 22:43:08 +01008425 static void
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02008426make_tooltip(BalloonEval *beval, char *text, POINT pt)
Bram Moolenaard385b5d2018-12-27 22:43:08 +01008427{
Bram Moolenaar0bd663a2022-01-22 10:24:47 +00008428 TOOLINFOW_NEW *pti;
8429 RECT rect;
Bram Moolenaard385b5d2018-12-27 22:43:08 +01008430
Bram Moolenaar0bd663a2022-01-22 10:24:47 +00008431 pti = alloc(sizeof(TOOLINFOW_NEW));
Bram Moolenaard385b5d2018-12-27 22:43:08 +01008432 if (pti == NULL)
8433 return;
8434
8435 beval->balloon = CreateWindowExW(WS_EX_TOPMOST, TOOLTIPS_CLASSW,
8436 NULL, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,
8437 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
Bram Moolenaarafde13b2019-04-28 19:46:49 +02008438 beval->target, NULL, g_hinst, NULL);
Bram Moolenaard385b5d2018-12-27 22:43:08 +01008439
8440 SetWindowPos(beval->balloon, HWND_TOPMOST, 0, 0, 0, 0,
8441 SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
8442
Bram Moolenaar0bd663a2022-01-22 10:24:47 +00008443 pti->cbSize = sizeof(TOOLINFOW_NEW);
Bram Moolenaard385b5d2018-12-27 22:43:08 +01008444 pti->uFlags = TTF_SUBCLASS;
8445 pti->hwnd = beval->target;
8446 pti->hinst = 0; // Don't use string resources
8447 pti->uId = ID_BEVAL_TOOLTIP;
8448
Bram Moolenaar0bd663a2022-01-22 10:24:47 +00008449 pti->lpszText = LPSTR_TEXTCALLBACKW;
8450 beval->tofree = enc_to_utf16((char_u*)text, NULL);
8451 pti->lParam = (LPARAM)beval->tofree;
8452 // switch multiline tooltips on
8453 if (GetClientRect(s_textArea, &rect))
8454 SendMessageW(beval->balloon, TTM_SETMAXTIPWIDTH, 0,
8455 (LPARAM)rect.right);
Bram Moolenaard385b5d2018-12-27 22:43:08 +01008456
8457 // Limit ballooneval bounding rect to CursorPos neighbourhood.
8458 pti->rect.left = pt.x - 3;
8459 pti->rect.top = pt.y - 3;
8460 pti->rect.right = pt.x + 3;
8461 pti->rect.bottom = pt.y + 3;
8462
8463 SendMessageW(beval->balloon, TTM_ADDTOOLW, 0, (LPARAM)pti);
8464 // Make tooltip appear sooner.
8465 SendMessageW(beval->balloon, TTM_SETDELAYTIME, TTDT_INITIAL, 10);
8466 // I've performed some tests and it seems the longest possible life time
8467 // of tooltip is 30 seconds.
8468 SendMessageW(beval->balloon, TTM_SETDELAYTIME, TTDT_AUTOPOP, 30000);
8469 /*
8470 * HACK: force tooltip to appear, because it'll not appear until
8471 * first mouse move. D*mn M$
8472 * Amazingly moving (2, 2) and then (-1, -1) the mouse doesn't move.
8473 */
8474 mouse_event(MOUSEEVENTF_MOVE, 2, 2, 0, 0);
8475 mouse_event(MOUSEEVENTF_MOVE, (DWORD)-1, (DWORD)-1, 0, 0);
8476 vim_free(pti);
Bram Moolenaard385b5d2018-12-27 22:43:08 +01008477}
Bram Moolenaard385b5d2018-12-27 22:43:08 +01008478
Bram Moolenaar071d4272004-06-13 20:20:40 +00008479 static void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01008480delete_tooltip(BalloonEval *beval)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008481{
Bram Moolenaar8e5f5b42015-08-26 23:12:38 +02008482 PostMessage(beval->balloon, WM_CLOSE, 0, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008483}
8484
8485 static VOID CALLBACK
Bram Moolenaar68c2f632016-01-30 17:24:07 +01008486BevalTimerProc(
Bram Moolenaar1266d672017-02-01 13:43:36 +01008487 HWND hwnd UNUSED,
8488 UINT uMsg UNUSED,
8489 UINT_PTR idEvent UNUSED,
8490 DWORD dwTime)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008491{
8492 POINT pt;
8493 RECT rect;
8494
8495 if (cur_beval == NULL || cur_beval->showState == ShS_SHOWING || !p_beval)
8496 return;
8497
8498 GetCursorPos(&pt);
8499 if (WindowFromPoint(pt) != s_textArea)
8500 return;
8501
8502 ScreenToClient(s_textArea, &pt);
8503 GetClientRect(s_textArea, &rect);
8504 if (!PtInRect(&rect, pt))
8505 return;
8506
8507 if (LastActivity > 0
8508 && (dwTime - LastActivity) >= (DWORD)p_bdlay
8509 && (cur_beval->showState != ShS_PENDING
8510 || abs(cur_beval->x - pt.x) > 3
8511 || abs(cur_beval->y - pt.y) > 3))
8512 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01008513 // Pointer resting in one place long enough, it's time to show
8514 // the tooltip.
Bram Moolenaar071d4272004-06-13 20:20:40 +00008515 cur_beval->showState = ShS_PENDING;
8516 cur_beval->x = pt.x;
8517 cur_beval->y = pt.y;
8518
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008519 // TRACE0("BevalTimerProc: sending request");
Bram Moolenaar071d4272004-06-13 20:20:40 +00008520
8521 if (cur_beval->msgCB != NULL)
8522 (*cur_beval->msgCB)(cur_beval, 0);
8523 }
8524}
8525
8526 void
Bram Moolenaar1266d672017-02-01 13:43:36 +01008527gui_mch_disable_beval_area(BalloonEval *beval UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008528{
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008529 // TRACE0("gui_mch_disable_beval_area {{{");
Bram Moolenaar071d4272004-06-13 20:20:40 +00008530 KillTimer(s_textArea, BevalTimerId);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008531 // TRACE0("gui_mch_disable_beval_area }}}");
Bram Moolenaar071d4272004-06-13 20:20:40 +00008532}
8533
8534 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01008535gui_mch_enable_beval_area(BalloonEval *beval)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008536{
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008537 // TRACE0("gui_mch_enable_beval_area |||");
Bram Moolenaar071d4272004-06-13 20:20:40 +00008538 if (beval == NULL)
8539 return;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008540 // TRACE0("gui_mch_enable_beval_area {{{");
Bram Moolenaar167632f2010-05-26 21:42:54 +02008541 BevalTimerId = SetTimer(s_textArea, 0, (UINT)(p_bdlay / 2), BevalTimerProc);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008542 // TRACE0("gui_mch_enable_beval_area }}}");
Bram Moolenaar071d4272004-06-13 20:20:40 +00008543}
8544
8545 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01008546gui_mch_post_balloon(BalloonEval *beval, char_u *mesg)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008547{
8548 POINT pt;
Bram Moolenaar1c465442017-03-12 20:10:05 +01008549
Bram Moolenaarbe0a2592019-05-09 13:50:16 +02008550 vim_free(beval->msg);
8551 beval->msg = mesg == NULL ? NULL : vim_strsave(mesg);
8552 if (beval->msg == NULL)
8553 {
8554 delete_tooltip(beval);
8555 beval->showState = ShS_NEUTRAL;
8556 return;
8557 }
8558
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008559 // TRACE0("gui_mch_post_balloon {{{");
Bram Moolenaar071d4272004-06-13 20:20:40 +00008560 if (beval->showState == ShS_SHOWING)
8561 return;
8562 GetCursorPos(&pt);
8563 ScreenToClient(s_textArea, &pt);
8564
8565 if (abs(beval->x - pt.x) < 3 && abs(beval->y - pt.y) < 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008566 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01008567 // cursor is still here
Bram Moolenaar071d4272004-06-13 20:20:40 +00008568 gui_mch_disable_beval_area(cur_beval);
8569 beval->showState = ShS_SHOWING;
Bram Moolenaar418f81b2016-02-16 20:12:02 +01008570 make_tooltip(beval, (char *)mesg, pt);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008571 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008572 // TRACE0("gui_mch_post_balloon }}}");
Bram Moolenaar071d4272004-06-13 20:20:40 +00008573}
8574
8575 BalloonEval *
Bram Moolenaar68c2f632016-01-30 17:24:07 +01008576gui_mch_create_beval_area(
Bram Moolenaar734a8672019-12-02 22:49:38 +01008577 void *target UNUSED, // ignored, always use s_textArea
Bram Moolenaar68c2f632016-01-30 17:24:07 +01008578 char_u *mesg,
8579 void (*mesgCB)(BalloonEval *, int),
8580 void *clientData)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008581{
Bram Moolenaar734a8672019-12-02 22:49:38 +01008582 // partially stolen from gui_beval.c
Bram Moolenaar071d4272004-06-13 20:20:40 +00008583 BalloonEval *beval;
8584
8585 if (mesg != NULL && mesgCB != NULL)
8586 {
Bram Moolenaarcbadefe2022-01-01 19:33:50 +00008587 iemsg(_(e_cannot_create_ballooneval_with_both_message_and_callback));
Bram Moolenaar071d4272004-06-13 20:20:40 +00008588 return NULL;
8589 }
8590
Bram Moolenaarc799fe22019-05-28 23:08:19 +02008591 beval = ALLOC_CLEAR_ONE(BalloonEval);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008592 if (beval != NULL)
8593 {
8594 beval->target = s_textArea;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008595
8596 beval->showState = ShS_NEUTRAL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008597 beval->msg = mesg;
8598 beval->msgCB = mesgCB;
8599 beval->clientData = clientData;
8600
8601 InitCommonControls();
Bram Moolenaar071d4272004-06-13 20:20:40 +00008602 cur_beval = beval;
8603
8604 if (p_beval)
8605 gui_mch_enable_beval_area(beval);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008606 }
8607 return beval;
8608}
8609
8610 static void
Bram Moolenaar1266d672017-02-01 13:43:36 +01008611Handle_WM_Notify(HWND hwnd UNUSED, LPNMHDR pnmh)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008612{
Bram Moolenaar734a8672019-12-02 22:49:38 +01008613 if (pnmh->idFrom != ID_BEVAL_TOOLTIP) // it is not our tooltip
Bram Moolenaar071d4272004-06-13 20:20:40 +00008614 return;
8615
8616 if (cur_beval != NULL)
8617 {
Bram Moolenaar45360022005-07-21 21:08:21 +00008618 switch (pnmh->code)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008619 {
Bram Moolenaar45360022005-07-21 21:08:21 +00008620 case TTN_SHOW:
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008621 // TRACE0("TTN_SHOW {{{");
8622 // TRACE0("TTN_SHOW }}}");
Bram Moolenaar45360022005-07-21 21:08:21 +00008623 break;
Bram Moolenaar734a8672019-12-02 22:49:38 +01008624 case TTN_POP: // Before tooltip disappear
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008625 // TRACE0("TTN_POP {{{");
Bram Moolenaar071d4272004-06-13 20:20:40 +00008626 delete_tooltip(cur_beval);
8627 gui_mch_enable_beval_area(cur_beval);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008628 // TRACE0("TTN_POP }}}");
Bram Moolenaar071d4272004-06-13 20:20:40 +00008629
8630 cur_beval->showState = ShS_NEUTRAL;
Bram Moolenaar45360022005-07-21 21:08:21 +00008631 break;
8632 case TTN_GETDISPINFO:
Bram Moolenaar6c9176d2008-01-03 19:45:15 +00008633 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01008634 // if you get there then we have new common controls
Bram Moolenaar6c9176d2008-01-03 19:45:15 +00008635 NMTTDISPINFO_NEW *info = (NMTTDISPINFO_NEW *)pnmh;
8636 info->lpszText = (LPSTR)info->lParam;
8637 info->uFlags |= TTF_DI_SETITEM;
8638 }
Bram Moolenaar45360022005-07-21 21:08:21 +00008639 break;
Bram Moolenaard385b5d2018-12-27 22:43:08 +01008640 case TTN_GETDISPINFOW:
8641 {
8642 // if we get here then we have new common controls
8643 NMTTDISPINFOW_NEW *info = (NMTTDISPINFOW_NEW *)pnmh;
8644 info->lpszText = (LPWSTR)info->lParam;
8645 info->uFlags |= TTF_DI_SETITEM;
8646 }
8647 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008648 }
8649 }
8650}
8651
8652 static void
8653TrackUserActivity(UINT uMsg)
8654{
8655 if ((uMsg >= WM_MOUSEFIRST && uMsg <= WM_MOUSELAST)
8656 || (uMsg >= WM_KEYFIRST && uMsg <= WM_KEYLAST))
8657 LastActivity = GetTickCount();
8658}
8659
8660 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01008661gui_mch_destroy_beval_area(BalloonEval *beval)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008662{
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01008663# ifdef FEAT_VARTABS
Bram Moolenaar6d9e71a2018-12-28 19:13:34 +01008664 vim_free(beval->vts);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01008665# endif
Bram Moolenaar6d9e71a2018-12-28 19:13:34 +01008666 vim_free(beval->tofree);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008667 vim_free(beval);
8668}
Bram Moolenaar734a8672019-12-02 22:49:38 +01008669#endif // FEAT_BEVAL_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00008670
8671#if defined(FEAT_NETBEANS_INTG) || defined(PROTO)
8672/*
8673 * We have multiple signs to draw at the same location. Draw the
8674 * multi-sign indicator (down-arrow) instead. This is the Win32 version.
8675 */
8676 void
8677netbeans_draw_multisign_indicator(int row)
8678{
8679 int i;
8680 int y;
8681 int x;
8682
Bram Moolenaarb26e6322010-05-22 21:34:09 +02008683 if (!netbeans_active())
Bram Moolenaarcc448b32010-07-14 16:52:17 +02008684 return;
Bram Moolenaarb26e6322010-05-22 21:34:09 +02008685
Bram Moolenaar071d4272004-06-13 20:20:40 +00008686 x = 0;
8687 y = TEXT_Y(row);
8688
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01008689# if defined(FEAT_DIRECTX)
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01008690 if (IS_ENABLE_DIRECTX())
8691 DWriteContext_Flush(s_dwc);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01008692# endif
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01008693
Bram Moolenaar071d4272004-06-13 20:20:40 +00008694 for (i = 0; i < gui.char_height - 3; i++)
8695 SetPixel(s_hdc, x+2, y++, gui.currFgColor);
8696
8697 SetPixel(s_hdc, x+0, y, gui.currFgColor);
8698 SetPixel(s_hdc, x+2, y, gui.currFgColor);
8699 SetPixel(s_hdc, x+4, y++, gui.currFgColor);
8700 SetPixel(s_hdc, x+1, y, gui.currFgColor);
8701 SetPixel(s_hdc, x+2, y, gui.currFgColor);
8702 SetPixel(s_hdc, x+3, y++, gui.currFgColor);
8703 SetPixel(s_hdc, x+2, y, gui.currFgColor);
8704}
Bram Moolenaare0874f82016-01-24 20:36:41 +01008705#endif