blob: 7c2c7fce33f427d88f1878d852f8060386d1de1d [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
K.Takatac81e9bf2022-01-16 14:15:49 +0000221#ifndef WM_DPICHANGED
222# define WM_DPICHANGED 0x02E0
223#endif
224
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100225#ifdef PROTO
Bram Moolenaar071d4272004-06-13 20:20:40 +0000226/*
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100227 * Define a few things for generating prototypes. This is just to avoid
228 * syntax errors, the defines do not need to be correct.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000229 */
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100230# define APIENTRY
231# define CALLBACK
232# define CONST
233# define FAR
234# define NEAR
Bram Moolenaar945c8572020-07-17 22:17:03 +0200235# define WINAPI
Bram Moolenaara6b7a082016-08-10 20:53:05 +0200236# undef _cdecl
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100237# define _cdecl
238typedef int BOOL;
239typedef int BYTE;
240typedef int DWORD;
241typedef int WCHAR;
242typedef int ENUMLOGFONT;
243typedef int FINDREPLACE;
244typedef int HANDLE;
245typedef int HBITMAP;
246typedef int HBRUSH;
247typedef int HDROP;
248typedef int INT;
Bram Moolenaar433a5eb2019-03-30 16:24:16 +0100249typedef int LOGFONTW[];
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100250typedef int LPARAM;
251typedef int LPCREATESTRUCT;
252typedef int LPCSTR;
253typedef int LPCTSTR;
254typedef int LPRECT;
255typedef int LPSTR;
256typedef int LPWINDOWPOS;
257typedef int LPWORD;
258typedef int LRESULT;
259typedef int HRESULT;
260# undef MSG
261typedef int MSG;
262typedef int NEWTEXTMETRIC;
263typedef int OSVERSIONINFO;
264typedef int PWORD;
265typedef int RECT;
266typedef int UINT;
267typedef int WORD;
268typedef int WPARAM;
269typedef int POINT;
270typedef void *HINSTANCE;
271typedef void *HMENU;
272typedef void *HWND;
273typedef void *HDC;
274typedef void VOID;
275typedef int LPNMHDR;
276typedef int LONG;
277typedef int WNDPROC;
Bram Moolenaara6b7a082016-08-10 20:53:05 +0200278typedef int UINT_PTR;
Bram Moolenaarb1c91982018-05-17 17:04:55 +0200279typedef int COLORREF;
280typedef int HCURSOR;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100281#endif
282
K.Takata45f9cfb2022-01-21 11:11:00 +0000283static void _OnPaint(HWND hwnd);
Bram Moolenaar92467d32017-12-05 13:22:16 +0100284static void fill_rect(const RECT *rcp, HBRUSH hbr, COLORREF color);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100285static void clear_rect(RECT *rcp);
286
Bram Moolenaar734a8672019-12-02 22:49:38 +0100287static WORD s_dlgfntheight; // height of the dialog font
288static WORD s_dlgfntwidth; // width of the dialog font
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100289
290#ifdef FEAT_MENU
291static HMENU s_menuBar = NULL;
292#endif
293#ifdef FEAT_TEAROFF
294static void rebuild_tearoff(vimmenu_T *menu);
Bram Moolenaar734a8672019-12-02 22:49:38 +0100295static HBITMAP s_htearbitmap; // bitmap used to indicate tearoff
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100296#endif
297
Bram Moolenaar734a8672019-12-02 22:49:38 +0100298// Flag that is set while processing a message that must not be interrupted by
299// processing another message.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100300static int s_busy_processing = FALSE;
301
Bram Moolenaar734a8672019-12-02 22:49:38 +0100302static int destroying = FALSE; // call DestroyWindow() ourselves
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100303
304#ifdef MSWIN_FIND_REPLACE
K.Takatac81e9bf2022-01-16 14:15:49 +0000305static UINT s_findrep_msg = 0;
Bram Moolenaar0eb035c2019-04-02 22:15:55 +0200306static FINDREPLACEW s_findrep_struct;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100307static HWND s_findrep_hwnd = NULL;
Bram Moolenaar0eb035c2019-04-02 22:15:55 +0200308static int s_findrep_is_find; // TRUE for find dialog, FALSE
309 // for find/replace dialog
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100310#endif
311
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100312HWND s_hwnd = NULL;
313static HDC s_hdc = NULL;
Bram Moolenaarab85ca42019-11-15 22:41:14 +0100314static HBRUSH s_brush = NULL;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100315
316#ifdef FEAT_TOOLBAR
317static HWND s_toolbarhwnd = NULL;
318static WNDPROC s_toolbar_wndproc = NULL;
319#endif
320
321#ifdef FEAT_GUI_TABLINE
322static HWND s_tabhwnd = NULL;
323static WNDPROC s_tabline_wndproc = NULL;
324static int showing_tabline = 0;
325#endif
326
327static WPARAM s_wParam = 0;
328static LPARAM s_lParam = 0;
329
330static HWND s_textArea = NULL;
331static UINT s_uMsg = 0;
332
Bram Moolenaar734a8672019-12-02 22:49:38 +0100333static char_u *s_textfield; // Used by dialogs to pass back strings
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100334
335static int s_need_activate = FALSE;
336
Bram Moolenaar734a8672019-12-02 22:49:38 +0100337// This variable is set when waiting for an event, which is the only moment
338// scrollbar dragging can be done directly. It's not allowed while commands
339// are executed, because it may move the cursor and that may cause unexpected
340// problems (e.g., while ":s" is working).
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100341static int allow_scrollbar = FALSE;
342
K.Takatac81e9bf2022-01-16 14:15:49 +0000343#ifndef _DPI_AWARENESS_CONTEXTS_
344typedef HANDLE DPI_AWARENESS_CONTEXT;
345
346typedef enum DPI_AWARENESS {
K.Takatab0b2b732022-01-19 12:59:21 +0000347 DPI_AWARENESS_INVALID = -1,
348 DPI_AWARENESS_UNAWARE = 0,
349 DPI_AWARENESS_SYSTEM_AWARE = 1,
K.Takatac81e9bf2022-01-16 14:15:49 +0000350 DPI_AWARENESS_PER_MONITOR_AWARE = 2
351} DPI_AWARENESS;
352
K.Takatab0b2b732022-01-19 12:59:21 +0000353# define DPI_AWARENESS_CONTEXT_UNAWARE ((DPI_AWARENESS_CONTEXT)-1)
354# define DPI_AWARENESS_CONTEXT_SYSTEM_AWARE ((DPI_AWARENESS_CONTEXT)-2)
K.Takatac81e9bf2022-01-16 14:15:49 +0000355# define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE ((DPI_AWARENESS_CONTEXT)-3)
356# define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 ((DPI_AWARENESS_CONTEXT)-4)
357# define DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED ((DPI_AWARENESS_CONTEXT)-5)
358#endif
359
360#define DEFAULT_DPI 96
361static int s_dpi = DEFAULT_DPI;
362static BOOL s_in_dpichanged = FALSE;
363static DPI_AWARENESS s_process_dpi_aware = DPI_AWARENESS_INVALID;
364
365static UINT (WINAPI *pGetDpiForSystem)(void) = NULL;
366static UINT (WINAPI *pGetDpiForWindow)(HWND hwnd) = NULL;
367static int (WINAPI *pGetSystemMetricsForDpi)(int, UINT) = NULL;
368//static INT (WINAPI *pGetWindowDpiAwarenessContext)(HWND hwnd) = NULL;
369static DPI_AWARENESS_CONTEXT (WINAPI *pSetThreadDpiAwarenessContext)(DPI_AWARENESS_CONTEXT dpiContext) = NULL;
370static DPI_AWARENESS (WINAPI *pGetAwarenessFromDpiAwarenessContext)(DPI_AWARENESS_CONTEXT) = NULL;
371
372 static UINT WINAPI
373stubGetDpiForSystem(void)
374{
375 HWND hwnd = GetDesktopWindow();
376 HDC hdc = GetWindowDC(hwnd);
377 UINT dpi = GetDeviceCaps(hdc, LOGPIXELSY);
378 ReleaseDC(hwnd, hdc);
379 return dpi;
380}
381
382 static int WINAPI
383stubGetSystemMetricsForDpi(int nIndex, UINT dpi)
384{
385 return GetSystemMetrics(nIndex);
386}
387
388 static int
389adjust_fontsize_by_dpi(int size)
390{
391 return size * s_dpi / (int)pGetDpiForSystem();
392}
393
394 static int
395adjust_by_system_dpi(int size)
396{
397 return size * (int)pGetDpiForSystem() / DEFAULT_DPI;
398}
399
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100400#if defined(FEAT_DIRECTX)
401 static int
402directx_enabled(void)
403{
404 if (s_dwc != NULL)
405 return 1;
406 else if (s_directx_load_attempted)
407 return 0;
Bram Moolenaar734a8672019-12-02 22:49:38 +0100408 // load DirectX
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +0100409 DWrite_Init();
410 s_directx_load_attempted = 1;
411 s_dwc = DWriteContext_Open();
412 directx_binddc();
413 return s_dwc != NULL ? 1 : 0;
414}
415
416 static void
417directx_binddc(void)
418{
419 if (s_textArea != NULL)
420 {
421 RECT rect;
422 GetClientRect(s_textArea, &rect);
423 DWriteContext_BindDC(s_dwc, s_hdc, &rect);
424 }
425}
426#endif
427
Bram Moolenaar734a8672019-12-02 22:49:38 +0100428extern int current_font_height; // this is in os_mswin.c
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100429
430static struct
431{
432 UINT key_sym;
433 char_u vim_code0;
434 char_u vim_code1;
435} special_keys[] =
436{
437 {VK_UP, 'k', 'u'},
438 {VK_DOWN, 'k', 'd'},
439 {VK_LEFT, 'k', 'l'},
440 {VK_RIGHT, 'k', 'r'},
441
442 {VK_F1, 'k', '1'},
443 {VK_F2, 'k', '2'},
444 {VK_F3, 'k', '3'},
445 {VK_F4, 'k', '4'},
446 {VK_F5, 'k', '5'},
447 {VK_F6, 'k', '6'},
448 {VK_F7, 'k', '7'},
449 {VK_F8, 'k', '8'},
450 {VK_F9, 'k', '9'},
451 {VK_F10, 'k', ';'},
452
453 {VK_F11, 'F', '1'},
454 {VK_F12, 'F', '2'},
455 {VK_F13, 'F', '3'},
456 {VK_F14, 'F', '4'},
457 {VK_F15, 'F', '5'},
458 {VK_F16, 'F', '6'},
459 {VK_F17, 'F', '7'},
460 {VK_F18, 'F', '8'},
461 {VK_F19, 'F', '9'},
462 {VK_F20, 'F', 'A'},
463
464 {VK_F21, 'F', 'B'},
465#ifdef FEAT_NETBEANS_INTG
Bram Moolenaar734a8672019-12-02 22:49:38 +0100466 {VK_PAUSE, 'F', 'B'}, // Pause == F21 (see gui_gtk_x11.c)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100467#endif
468 {VK_F22, 'F', 'C'},
469 {VK_F23, 'F', 'D'},
Bram Moolenaar734a8672019-12-02 22:49:38 +0100470 {VK_F24, 'F', 'E'}, // winuser.h defines up to F24
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100471
472 {VK_HELP, '%', '1'},
473 {VK_BACK, 'k', 'b'},
474 {VK_INSERT, 'k', 'I'},
475 {VK_DELETE, 'k', 'D'},
476 {VK_HOME, 'k', 'h'},
477 {VK_END, '@', '7'},
478 {VK_PRIOR, 'k', 'P'},
479 {VK_NEXT, 'k', 'N'},
480 {VK_PRINT, '%', '9'},
481 {VK_ADD, 'K', '6'},
482 {VK_SUBTRACT, 'K', '7'},
483 {VK_DIVIDE, 'K', '8'},
484 {VK_MULTIPLY, 'K', '9'},
Bram Moolenaar734a8672019-12-02 22:49:38 +0100485 {VK_SEPARATOR, 'K', 'A'}, // Keypad Enter
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100486 {VK_DECIMAL, 'K', 'B'},
487
488 {VK_NUMPAD0, 'K', 'C'},
489 {VK_NUMPAD1, 'K', 'D'},
490 {VK_NUMPAD2, 'K', 'E'},
491 {VK_NUMPAD3, 'K', 'F'},
492 {VK_NUMPAD4, 'K', 'G'},
493 {VK_NUMPAD5, 'K', 'H'},
494 {VK_NUMPAD6, 'K', 'I'},
495 {VK_NUMPAD7, 'K', 'J'},
496 {VK_NUMPAD8, 'K', 'K'},
497 {VK_NUMPAD9, 'K', 'L'},
498
Bram Moolenaar734a8672019-12-02 22:49:38 +0100499 // Keys that we want to be able to use any modifier with:
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100500 {VK_SPACE, ' ', NUL},
501 {VK_TAB, TAB, NUL},
502 {VK_ESCAPE, ESC, NUL},
503 {NL, NL, NUL},
504 {CAR, CAR, NUL},
505
Bram Moolenaar734a8672019-12-02 22:49:38 +0100506 // End of list marker:
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100507 {0, 0, 0}
508};
509
Bram Moolenaar734a8672019-12-02 22:49:38 +0100510// Local variables
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100511static int s_button_pending = -1;
512
Bram Moolenaar734a8672019-12-02 22:49:38 +0100513// s_getting_focus is set when we got focus but didn't see mouse-up event yet,
514// so don't reset s_button_pending.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100515static int s_getting_focus = FALSE;
516
517static int s_x_pending;
518static int s_y_pending;
519static UINT s_kFlags_pending;
K.Takataa8ec4912022-02-03 14:32:33 +0000520static UINT_PTR s_wait_timer = 0; // Timer for get char from user
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100521static int s_timed_out = FALSE;
Bram Moolenaarf1f2f832018-04-24 16:04:57 +0200522static int dead_key = 0; // 0: no dead key, 1: dead key pressed
523static UINT surrogate_pending_ch = 0; // 0: no surrogate pending,
524 // else a high surrogate
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100525
Bram Moolenaarc3719bd2017-11-18 22:13:31 +0100526#ifdef FEAT_BEVAL_GUI
Bram Moolenaar734a8672019-12-02 22:49:38 +0100527// balloon-eval WM_NOTIFY_HANDLER
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100528static void Handle_WM_Notify(HWND hwnd, LPNMHDR pnmh);
K.Takataa8ec4912022-02-03 14:32:33 +0000529static void track_user_activity(UINT uMsg);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100530#endif
531
532/*
533 * For control IME.
534 *
Bram Moolenaar433a5eb2019-03-30 16:24:16 +0100535 * These LOGFONTW used for IME.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100536 */
K.Takata4ac893f2022-01-20 12:44:28 +0000537#ifdef FEAT_MBYTE_IME
Bram Moolenaar734a8672019-12-02 22:49:38 +0100538// holds LOGFONTW for 'guifontwide' if available, otherwise 'guifont'
Bram Moolenaar433a5eb2019-03-30 16:24:16 +0100539static LOGFONTW norm_logfont;
Bram Moolenaar734a8672019-12-02 22:49:38 +0100540// holds LOGFONTW for 'guifont' always.
Bram Moolenaar433a5eb2019-03-30 16:24:16 +0100541static LOGFONTW sub_logfont;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100542#endif
543
544#ifdef FEAT_MBYTE_IME
545static LRESULT _OnImeNotify(HWND hWnd, DWORD dwCommand, DWORD dwData);
546#endif
547
548#if defined(FEAT_BROWSE)
549static char_u *convert_filter(char_u *s);
550#endif
551
552#ifdef DEBUG_PRINT_ERROR
553/*
554 * Print out the last Windows error message
555 */
556 static void
557print_windows_error(void)
558{
559 LPVOID lpMsgBuf;
560
561 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
562 NULL, GetLastError(),
563 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
564 (LPTSTR) &lpMsgBuf, 0, NULL);
565 TRACE1("Error: %s\n", lpMsgBuf);
566 LocalFree(lpMsgBuf);
567}
568#endif
569
570/*
571 * Cursor blink functions.
572 *
573 * This is a simple state machine:
574 * BLINK_NONE not blinking at all
575 * BLINK_OFF blinking, cursor is not shown
576 * BLINK_ON blinking, cursor is shown
577 */
578
579#define BLINK_NONE 0
580#define BLINK_OFF 1
581#define BLINK_ON 2
582
583static int blink_state = BLINK_NONE;
584static long_u blink_waittime = 700;
585static long_u blink_ontime = 400;
586static long_u blink_offtime = 250;
K.Takataa8ec4912022-02-03 14:32:33 +0000587static UINT_PTR blink_timer = 0;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100588
Bram Moolenaar703a8042016-06-04 16:24:32 +0200589 int
590gui_mch_is_blinking(void)
591{
592 return blink_state != BLINK_NONE;
593}
594
Bram Moolenaar9d5d3c92016-07-07 16:43:02 +0200595 int
596gui_mch_is_blink_off(void)
597{
598 return blink_state == BLINK_OFF;
599}
600
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100601 void
602gui_mch_set_blinking(long wait, long on, long off)
603{
604 blink_waittime = wait;
605 blink_ontime = on;
606 blink_offtime = off;
607}
608
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100609 static VOID CALLBACK
610_OnBlinkTimer(
611 HWND hwnd,
Bram Moolenaar1266d672017-02-01 13:43:36 +0100612 UINT uMsg UNUSED,
K.Takataa8ec4912022-02-03 14:32:33 +0000613 UINT_PTR idEvent,
Bram Moolenaar1266d672017-02-01 13:43:36 +0100614 DWORD dwTime UNUSED)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100615{
616 MSG msg;
617
618 /*
619 TRACE2("Got timer event, id %d, blink_timer %d\n", idEvent, blink_timer);
620 */
621
622 KillTimer(NULL, idEvent);
623
Bram Moolenaar734a8672019-12-02 22:49:38 +0100624 // Eat spurious WM_TIMER messages
K.Takatab7057bd2022-01-21 11:37:07 +0000625 while (PeekMessageW(&msg, hwnd, WM_TIMER, WM_TIMER, PM_REMOVE))
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100626 ;
627
628 if (blink_state == BLINK_ON)
629 {
630 gui_undraw_cursor();
631 blink_state = BLINK_OFF;
K.Takataa8ec4912022-02-03 14:32:33 +0000632 blink_timer = SetTimer(NULL, 0, (UINT)blink_offtime, _OnBlinkTimer);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100633 }
634 else
635 {
636 gui_update_cursor(TRUE, FALSE);
637 blink_state = BLINK_ON;
K.Takataa8ec4912022-02-03 14:32:33 +0000638 blink_timer = SetTimer(NULL, 0, (UINT)blink_ontime, _OnBlinkTimer);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100639 }
Bram Moolenaar92467d32017-12-05 13:22:16 +0100640 gui_mch_flush();
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100641}
642
643 static void
644gui_mswin_rm_blink_timer(void)
645{
646 MSG msg;
647
648 if (blink_timer != 0)
649 {
650 KillTimer(NULL, blink_timer);
Bram Moolenaar734a8672019-12-02 22:49:38 +0100651 // Eat spurious WM_TIMER messages
K.Takatab7057bd2022-01-21 11:37:07 +0000652 while (PeekMessageW(&msg, s_hwnd, WM_TIMER, WM_TIMER, PM_REMOVE))
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100653 ;
654 blink_timer = 0;
655 }
656}
657
658/*
659 * Stop the cursor blinking. Show the cursor if it wasn't shown.
660 */
661 void
Bram Moolenaar1dd45fb2018-01-31 21:10:01 +0100662gui_mch_stop_blink(int may_call_gui_update_cursor)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100663{
664 gui_mswin_rm_blink_timer();
Bram Moolenaar1dd45fb2018-01-31 21:10:01 +0100665 if (blink_state == BLINK_OFF && may_call_gui_update_cursor)
Bram Moolenaar92467d32017-12-05 13:22:16 +0100666 {
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100667 gui_update_cursor(TRUE, FALSE);
Bram Moolenaar92467d32017-12-05 13:22:16 +0100668 gui_mch_flush();
669 }
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100670 blink_state = BLINK_NONE;
671}
672
673/*
674 * Start the cursor blinking. If it was already blinking, this restarts the
675 * waiting time and shows the cursor.
676 */
677 void
678gui_mch_start_blink(void)
679{
680 gui_mswin_rm_blink_timer();
681
Bram Moolenaar734a8672019-12-02 22:49:38 +0100682 // Only switch blinking on if none of the times is zero
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100683 if (blink_waittime && blink_ontime && blink_offtime && gui.in_focus)
684 {
K.Takataa8ec4912022-02-03 14:32:33 +0000685 blink_timer = SetTimer(NULL, 0, (UINT)blink_waittime, _OnBlinkTimer);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100686 blink_state = BLINK_ON;
687 gui_update_cursor(TRUE, FALSE);
Bram Moolenaar92467d32017-12-05 13:22:16 +0100688 gui_mch_flush();
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100689 }
690}
691
692/*
693 * Call-back routines.
694 */
695
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100696 static VOID CALLBACK
697_OnTimer(
698 HWND hwnd,
Bram Moolenaar1266d672017-02-01 13:43:36 +0100699 UINT uMsg UNUSED,
K.Takataa8ec4912022-02-03 14:32:33 +0000700 UINT_PTR idEvent,
Bram Moolenaar1266d672017-02-01 13:43:36 +0100701 DWORD dwTime UNUSED)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100702{
703 MSG msg;
704
705 /*
706 TRACE2("Got timer event, id %d, s_wait_timer %d\n", idEvent, s_wait_timer);
707 */
708 KillTimer(NULL, idEvent);
709 s_timed_out = TRUE;
710
Bram Moolenaar734a8672019-12-02 22:49:38 +0100711 // Eat spurious WM_TIMER messages
K.Takatab7057bd2022-01-21 11:37:07 +0000712 while (PeekMessageW(&msg, hwnd, WM_TIMER, WM_TIMER, PM_REMOVE))
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100713 ;
714 if (idEvent == s_wait_timer)
715 s_wait_timer = 0;
716}
717
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100718 static void
719_OnDeadChar(
Bram Moolenaar1266d672017-02-01 13:43:36 +0100720 HWND hwnd UNUSED,
721 UINT ch UNUSED,
722 int cRepeat UNUSED)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100723{
724 dead_key = 1;
725}
726
727/*
728 * Convert Unicode character "ch" to bytes in "string[slen]".
729 * When "had_alt" is TRUE the ALT key was included in "ch".
730 * Return the length.
Bram Moolenaarf1f2f832018-04-24 16:04:57 +0200731 * Because the Windows API uses UTF-16, we have to deal with surrogate
732 * pairs; this is where we choose to deal with them: if "ch" is a high
733 * surrogate, it will be stored, and the length returned will be zero; the next
734 * char_to_string call will then include the high surrogate, decoding the pair
735 * of UTF-16 code units to a single Unicode code point, presuming it is the
736 * matching low surrogate.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100737 */
738 static int
739char_to_string(int ch, char_u *string, int slen, int had_alt)
740{
741 int len;
742 int i;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100743 WCHAR wstring[2];
Bram Moolenaar945ec092016-06-08 21:17:43 +0200744 char_u *ws = NULL;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100745
Bram Moolenaarf1f2f832018-04-24 16:04:57 +0200746 if (surrogate_pending_ch != 0)
747 {
Bram Moolenaar734a8672019-12-02 22:49:38 +0100748 // We don't guarantee ch is a low surrogate to match the high surrogate
749 // we already have; it should be, but if it isn't, tough luck.
Bram Moolenaarf1f2f832018-04-24 16:04:57 +0200750 wstring[0] = surrogate_pending_ch;
751 wstring[1] = ch;
752 surrogate_pending_ch = 0;
753 len = 2;
754 }
Bram Moolenaar734a8672019-12-02 22:49:38 +0100755 else if (ch >= 0xD800 && ch <= 0xDBFF) // high surrogate
Bram Moolenaarf1f2f832018-04-24 16:04:57 +0200756 {
Bram Moolenaar734a8672019-12-02 22:49:38 +0100757 // We don't have the entire code point yet, only the first UTF-16 code
758 // unit; so just remember it and use it in the next call.
Bram Moolenaarf1f2f832018-04-24 16:04:57 +0200759 surrogate_pending_ch = ch;
760 return 0;
761 }
762 else
763 {
764 wstring[0] = ch;
765 len = 1;
766 }
Bram Moolenaarcea912a2016-10-12 14:20:24 +0200767
Bram Moolenaar734a8672019-12-02 22:49:38 +0100768 // "ch" is a UTF-16 character. Convert it to a string of bytes. When
769 // "enc_codepage" is non-zero use the standard Win32 function,
770 // otherwise use our own conversion function (e.g., for UTF-8).
Bram Moolenaarcea912a2016-10-12 14:20:24 +0200771 if (enc_codepage > 0)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100772 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +0200773 len = WideCharToMultiByte(enc_codepage, 0, wstring, len,
774 (LPSTR)string, slen, 0, NULL);
Bram Moolenaar734a8672019-12-02 22:49:38 +0100775 // If we had included the ALT key into the character but now the
776 // upper bit is no longer set, that probably means the conversion
777 // failed. Convert the original character and set the upper bit
778 // afterwards.
Bram Moolenaarcea912a2016-10-12 14:20:24 +0200779 if (had_alt && len == 1 && ch >= 0x80 && string[0] < 0x80)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100780 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +0200781 wstring[0] = ch & 0x7f;
782 len = WideCharToMultiByte(enc_codepage, 0, wstring, len,
783 (LPSTR)string, slen, 0, NULL);
Bram Moolenaar734a8672019-12-02 22:49:38 +0100784 if (len == 1) // safety check
Bram Moolenaarcea912a2016-10-12 14:20:24 +0200785 string[0] |= 0x80;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100786 }
787 }
788 else
789 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +0200790 ws = utf16_to_enc(wstring, &len);
791 if (ws == NULL)
792 len = 0;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100793 else
794 {
Bram Moolenaar734a8672019-12-02 22:49:38 +0100795 if (len > slen) // just in case
Bram Moolenaarcea912a2016-10-12 14:20:24 +0200796 len = slen;
797 mch_memmove(string, ws, len);
798 vim_free(ws);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100799 }
800 }
801
802 if (len == 0)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100803 {
804 string[0] = ch;
805 len = 1;
806 }
807
808 for (i = 0; i < len; ++i)
809 if (string[i] == CSI && len <= slen - 2)
810 {
Bram Moolenaar734a8672019-12-02 22:49:38 +0100811 // Insert CSI as K_CSI.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100812 mch_memmove(string + i + 3, string + i + 1, len - i - 1);
813 string[++i] = KS_EXTRA;
814 string[++i] = (int)KE_CSI;
815 len += 2;
816 }
817
818 return len;
819}
820
821/*
822 * Key hit, add it to the input buffer.
823 */
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100824 static void
825_OnChar(
Bram Moolenaar1266d672017-02-01 13:43:36 +0100826 HWND hwnd UNUSED,
LemonBoy77fc0b02022-04-22 22:45:52 +0100827 UINT cch,
Bram Moolenaar1266d672017-02-01 13:43:36 +0100828 int cRepeat UNUSED)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100829{
830 char_u string[40];
831 int len = 0;
LemonBoy77fc0b02022-04-22 22:45:52 +0100832 int modifiers = 0;
833 int ch = cch; // special keys are negative
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100834
835 dead_key = 0;
836
LemonBoy77fc0b02022-04-22 22:45:52 +0100837 if (GetKeyState(VK_SHIFT) & 0x8000)
838 modifiers |= MOD_MASK_SHIFT;
839 if (GetKeyState(VK_CONTROL) & 0x8000)
840 modifiers |= MOD_MASK_CTRL;
841
842 ch = simplify_key(ch, &modifiers);
843 // remove the SHIFT modifier for keys where it's already included, e.g.,
844 // '(' and '*'
845 modifiers = may_remove_shift_modifier(modifiers, ch);
846
847 // Unify modifiers somewhat. No longer use ALT to set the 8th bit.
848 ch = extract_modifiers(ch, &modifiers, FALSE, NULL);
849 if (ch == CSI)
850 ch = K_CSI;
851
852 if (modifiers)
853 {
854 string[0] = CSI;
855 string[1] = KS_MODIFIER;
856 string[2] = modifiers;
857 add_to_input_buf(string, 3);
858 }
859
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100860 len = char_to_string(ch, string, 40, FALSE);
861 if (len == 1 && string[0] == Ctrl_C && ctrl_c_interrupts)
862 {
863 trash_input_buf();
864 got_int = TRUE;
865 }
866
867 add_to_input_buf(string, len);
868}
869
870/*
871 * Alt-Key hit, add it to the input buffer.
872 */
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100873 static void
874_OnSysChar(
Bram Moolenaar1266d672017-02-01 13:43:36 +0100875 HWND hwnd UNUSED,
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100876 UINT cch,
Bram Moolenaar1266d672017-02-01 13:43:36 +0100877 int cRepeat UNUSED)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100878{
Bram Moolenaar734a8672019-12-02 22:49:38 +0100879 char_u string[40]; // Enough for multibyte character
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100880 int len;
881 int modifiers;
Bram Moolenaar734a8672019-12-02 22:49:38 +0100882 int ch = cch; // special keys are negative
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100883
884 dead_key = 0;
885
Bram Moolenaar734a8672019-12-02 22:49:38 +0100886 // OK, we have a character key (given by ch) which was entered with the
887 // ALT key pressed. Eg, if the user presses Alt-A, then ch == 'A'. Note
888 // that the system distinguishes Alt-a and Alt-A (Alt-Shift-a unless
889 // CAPSLOCK is pressed) at this point.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100890 modifiers = MOD_MASK_ALT;
891 if (GetKeyState(VK_SHIFT) & 0x8000)
892 modifiers |= MOD_MASK_SHIFT;
893 if (GetKeyState(VK_CONTROL) & 0x8000)
894 modifiers |= MOD_MASK_CTRL;
895
896 ch = simplify_key(ch, &modifiers);
Bram Moolenaar734a8672019-12-02 22:49:38 +0100897 // remove the SHIFT modifier for keys where it's already included, e.g.,
898 // '(' and '*'
Bram Moolenaardaff0fb2020-09-27 13:16:46 +0200899 modifiers = may_remove_shift_modifier(modifiers, ch);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100900
Bram Moolenaarfd615a32020-05-16 14:01:51 +0200901 // Unify modifiers somewhat. No longer use ALT to set the 8th bit.
902 ch = extract_modifiers(ch, &modifiers, FALSE, NULL);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100903 if (ch == CSI)
904 ch = K_CSI;
905
906 len = 0;
907 if (modifiers)
908 {
909 string[len++] = CSI;
910 string[len++] = KS_MODIFIER;
911 string[len++] = modifiers;
912 }
913
914 if (IS_SPECIAL((int)ch))
915 {
916 string[len++] = CSI;
917 string[len++] = K_SECOND((int)ch);
918 string[len++] = K_THIRD((int)ch);
919 }
920 else
921 {
Bram Moolenaar734a8672019-12-02 22:49:38 +0100922 // Although the documentation isn't clear about it, we assume "ch" is
923 // a Unicode character.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100924 len += char_to_string(ch, string + len, 40 - len, TRUE);
925 }
926
927 add_to_input_buf(string, len);
928}
929
930 static void
931_OnMouseEvent(
932 int button,
933 int x,
934 int y,
935 int repeated_click,
936 UINT keyFlags)
937{
938 int vim_modifiers = 0x0;
939
940 s_getting_focus = FALSE;
941
942 if (keyFlags & MK_SHIFT)
943 vim_modifiers |= MOUSE_SHIFT;
944 if (keyFlags & MK_CONTROL)
945 vim_modifiers |= MOUSE_CTRL;
946 if (GetKeyState(VK_MENU) & 0x8000)
947 vim_modifiers |= MOUSE_ALT;
948
949 gui_send_mouse_event(button, x, y, repeated_click, vim_modifiers);
950}
951
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100952 static void
953_OnMouseButtonDown(
Bram Moolenaar1266d672017-02-01 13:43:36 +0100954 HWND hwnd UNUSED,
955 BOOL fDoubleClick UNUSED,
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100956 int x,
957 int y,
958 UINT keyFlags)
959{
960 static LONG s_prevTime = 0;
961
962 LONG currentTime = GetMessageTime();
963 int button = -1;
964 int repeated_click;
965
Bram Moolenaar734a8672019-12-02 22:49:38 +0100966 // Give main window the focus: this is so the cursor isn't hollow.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100967 (void)SetFocus(s_hwnd);
968
969 if (s_uMsg == WM_LBUTTONDOWN || s_uMsg == WM_LBUTTONDBLCLK)
970 button = MOUSE_LEFT;
971 else if (s_uMsg == WM_MBUTTONDOWN || s_uMsg == WM_MBUTTONDBLCLK)
972 button = MOUSE_MIDDLE;
973 else if (s_uMsg == WM_RBUTTONDOWN || s_uMsg == WM_RBUTTONDBLCLK)
974 button = MOUSE_RIGHT;
975 else if (s_uMsg == WM_XBUTTONDOWN || s_uMsg == WM_XBUTTONDBLCLK)
976 {
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100977 button = ((GET_XBUTTON_WPARAM(s_wParam) == 1) ? MOUSE_X1 : MOUSE_X2);
978 }
979 else if (s_uMsg == WM_CAPTURECHANGED)
980 {
Bram Moolenaar734a8672019-12-02 22:49:38 +0100981 // on W95/NT4, somehow you get in here with an odd Msg
982 // if you press one button while holding down the other..
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100983 if (s_button_pending == MOUSE_LEFT)
984 button = MOUSE_RIGHT;
985 else
986 button = MOUSE_LEFT;
987 }
988 if (button >= 0)
989 {
990 repeated_click = ((int)(currentTime - s_prevTime) < p_mouset);
991
992 /*
993 * Holding down the left and right buttons simulates pushing the middle
994 * button.
995 */
996 if (repeated_click
997 && ((button == MOUSE_LEFT && s_button_pending == MOUSE_RIGHT)
998 || (button == MOUSE_RIGHT
999 && s_button_pending == MOUSE_LEFT)))
1000 {
1001 /*
1002 * Hmm, gui.c will ignore more than one button down at a time, so
1003 * pretend we let go of it first.
1004 */
1005 gui_send_mouse_event(MOUSE_RELEASE, x, y, FALSE, 0x0);
1006 button = MOUSE_MIDDLE;
1007 repeated_click = FALSE;
1008 s_button_pending = -1;
1009 _OnMouseEvent(button, x, y, repeated_click, keyFlags);
1010 }
1011 else if ((repeated_click)
1012 || (mouse_model_popup() && (button == MOUSE_RIGHT)))
1013 {
1014 if (s_button_pending > -1)
1015 {
K.Takata45f9cfb2022-01-21 11:11:00 +00001016 _OnMouseEvent(s_button_pending, x, y, FALSE, keyFlags);
1017 s_button_pending = -1;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001018 }
Bram Moolenaar734a8672019-12-02 22:49:38 +01001019 // TRACE("Button down at x %d, y %d\n", x, y);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001020 _OnMouseEvent(button, x, y, repeated_click, keyFlags);
1021 }
1022 else
1023 {
1024 /*
1025 * If this is the first press (i.e. not a multiple click) don't
1026 * action immediately, but store and wait for:
1027 * i) button-up
1028 * ii) mouse move
1029 * iii) another button press
1030 * before using it.
1031 * This enables us to make left+right simulate middle button,
1032 * without left or right being actioned first. The side-effect is
1033 * that if you click and hold the mouse without dragging, the
1034 * cursor doesn't move until you release the button. In practice
1035 * this is hardly a problem.
1036 */
1037 s_button_pending = button;
1038 s_x_pending = x;
1039 s_y_pending = y;
1040 s_kFlags_pending = keyFlags;
1041 }
1042
1043 s_prevTime = currentTime;
1044 }
1045}
1046
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001047 static void
1048_OnMouseMoveOrRelease(
Bram Moolenaar1266d672017-02-01 13:43:36 +01001049 HWND hwnd UNUSED,
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001050 int x,
1051 int y,
1052 UINT keyFlags)
1053{
1054 int button;
1055
1056 s_getting_focus = FALSE;
1057 if (s_button_pending > -1)
1058 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01001059 // Delayed action for mouse down event
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001060 _OnMouseEvent(s_button_pending, s_x_pending,
1061 s_y_pending, FALSE, s_kFlags_pending);
1062 s_button_pending = -1;
1063 }
1064 if (s_uMsg == WM_MOUSEMOVE)
1065 {
1066 /*
1067 * It's only a MOUSE_DRAG if one or more mouse buttons are being held
1068 * down.
1069 */
1070 if (!(keyFlags & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON
1071 | MK_XBUTTON1 | MK_XBUTTON2)))
1072 {
1073 gui_mouse_moved(x, y);
1074 return;
1075 }
1076
1077 /*
1078 * While button is down, keep grabbing mouse move events when
1079 * the mouse goes outside the window
1080 */
1081 SetCapture(s_textArea);
1082 button = MOUSE_DRAG;
Bram Moolenaar734a8672019-12-02 22:49:38 +01001083 // TRACE(" move at x %d, y %d\n", x, y);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001084 }
1085 else
1086 {
1087 ReleaseCapture();
1088 button = MOUSE_RELEASE;
Bram Moolenaar734a8672019-12-02 22:49:38 +01001089 // TRACE(" up at x %d, y %d\n", x, y);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001090 }
1091
1092 _OnMouseEvent(button, x, y, FALSE, keyFlags);
1093}
1094
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01001095 static void
1096_OnSizeTextArea(
1097 HWND hwnd UNUSED,
1098 UINT state UNUSED,
1099 int cx UNUSED,
1100 int cy UNUSED)
1101{
1102#if defined(FEAT_DIRECTX)
1103 if (IS_ENABLE_DIRECTX())
1104 directx_binddc();
1105#endif
1106}
1107
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001108#ifdef FEAT_MENU
1109/*
1110 * Find the vimmenu_T with the given id
1111 */
1112 static vimmenu_T *
1113gui_mswin_find_menu(
1114 vimmenu_T *pMenu,
1115 int id)
1116{
1117 vimmenu_T *pChildMenu;
1118
1119 while (pMenu)
1120 {
1121 if (pMenu->id == (UINT)id)
1122 break;
1123 if (pMenu->children != NULL)
1124 {
1125 pChildMenu = gui_mswin_find_menu(pMenu->children, id);
1126 if (pChildMenu)
1127 {
1128 pMenu = pChildMenu;
1129 break;
1130 }
1131 }
1132 pMenu = pMenu->next;
1133 }
1134 return pMenu;
1135}
1136
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001137 static void
1138_OnMenu(
Bram Moolenaar1266d672017-02-01 13:43:36 +01001139 HWND hwnd UNUSED,
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001140 int id,
Bram Moolenaar1266d672017-02-01 13:43:36 +01001141 HWND hwndCtl UNUSED,
1142 UINT codeNotify UNUSED)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001143{
1144 vimmenu_T *pMenu;
1145
1146 pMenu = gui_mswin_find_menu(root_menu, id);
1147 if (pMenu)
1148 gui_menu_cb(pMenu);
1149}
1150#endif
1151
1152#ifdef MSWIN_FIND_REPLACE
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001153/*
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001154 * Handle a Find/Replace window message.
1155 */
1156 static void
1157_OnFindRepl(void)
1158{
1159 int flags = 0;
1160 int down;
1161
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001162 if (s_findrep_struct.Flags & FR_DIALOGTERM)
Bram Moolenaar734a8672019-12-02 22:49:38 +01001163 // Give main window the focus back.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001164 (void)SetFocus(s_hwnd);
1165
1166 if (s_findrep_struct.Flags & FR_FINDNEXT)
1167 {
1168 flags = FRD_FINDNEXT;
1169
Bram Moolenaar734a8672019-12-02 22:49:38 +01001170 // Give main window the focus back: this is so the cursor isn't
1171 // hollow.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001172 (void)SetFocus(s_hwnd);
1173 }
1174 else if (s_findrep_struct.Flags & FR_REPLACE)
1175 {
1176 flags = FRD_REPLACE;
1177
Bram Moolenaar734a8672019-12-02 22:49:38 +01001178 // Give main window the focus back: this is so the cursor isn't
1179 // hollow.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001180 (void)SetFocus(s_hwnd);
1181 }
1182 else if (s_findrep_struct.Flags & FR_REPLACEALL)
1183 {
1184 flags = FRD_REPLACEALL;
1185 }
1186
1187 if (flags != 0)
1188 {
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001189 char_u *p, *q;
1190
Bram Moolenaar734a8672019-12-02 22:49:38 +01001191 // Call the generic GUI function to do the actual work.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001192 if (s_findrep_struct.Flags & FR_WHOLEWORD)
1193 flags |= FRD_WHOLE_WORD;
1194 if (s_findrep_struct.Flags & FR_MATCHCASE)
1195 flags |= FRD_MATCH_CASE;
1196 down = (s_findrep_struct.Flags & FR_DOWN) != 0;
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001197 p = utf16_to_enc(s_findrep_struct.lpstrFindWhat, NULL);
1198 q = utf16_to_enc(s_findrep_struct.lpstrReplaceWith, NULL);
1199 if (p != NULL && q != NULL)
1200 gui_do_findrepl(flags, p, q, down);
1201 vim_free(p);
1202 vim_free(q);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001203 }
1204}
1205#endif
1206
1207 static void
1208HandleMouseHide(UINT uMsg, LPARAM lParam)
1209{
1210 static LPARAM last_lParam = 0L;
1211
Bram Moolenaar734a8672019-12-02 22:49:38 +01001212 // We sometimes get a mousemove when the mouse didn't move...
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001213 if (uMsg == WM_MOUSEMOVE || uMsg == WM_NCMOUSEMOVE)
1214 {
1215 if (lParam == last_lParam)
1216 return;
1217 last_lParam = lParam;
1218 }
1219
Bram Moolenaar734a8672019-12-02 22:49:38 +01001220 // Handle specially, to centralise coding. We need to be sure we catch all
1221 // possible events which should cause us to restore the cursor (as it is a
1222 // shared resource, we take full responsibility for it).
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001223 switch (uMsg)
1224 {
1225 case WM_KEYUP:
1226 case WM_CHAR:
1227 /*
1228 * blank out the pointer if necessary
1229 */
1230 if (p_mh)
1231 gui_mch_mousehide(TRUE);
1232 break;
1233
Bram Moolenaar734a8672019-12-02 22:49:38 +01001234 case WM_SYSKEYUP: // show the pointer when a system-key is pressed
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001235 case WM_SYSCHAR:
Bram Moolenaar734a8672019-12-02 22:49:38 +01001236 case WM_MOUSEMOVE: // show the pointer on any mouse action
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001237 case WM_LBUTTONDOWN:
1238 case WM_LBUTTONUP:
1239 case WM_MBUTTONDOWN:
1240 case WM_MBUTTONUP:
1241 case WM_RBUTTONDOWN:
1242 case WM_RBUTTONUP:
1243 case WM_XBUTTONDOWN:
1244 case WM_XBUTTONUP:
1245 case WM_NCMOUSEMOVE:
1246 case WM_NCLBUTTONDOWN:
1247 case WM_NCLBUTTONUP:
1248 case WM_NCMBUTTONDOWN:
1249 case WM_NCMBUTTONUP:
1250 case WM_NCRBUTTONDOWN:
1251 case WM_NCRBUTTONUP:
1252 case WM_KILLFOCUS:
1253 /*
1254 * if the pointer is currently hidden, then we should show it.
1255 */
1256 gui_mch_mousehide(FALSE);
1257 break;
1258 }
1259}
1260
1261 static LRESULT CALLBACK
1262_TextAreaWndProc(
1263 HWND hwnd,
1264 UINT uMsg,
1265 WPARAM wParam,
1266 LPARAM lParam)
1267{
1268 /*
1269 TRACE("TextAreaWndProc: hwnd = %08x, msg = %x, wParam = %x, lParam = %x\n",
1270 hwnd, uMsg, wParam, lParam);
1271 */
1272
1273 HandleMouseHide(uMsg, lParam);
1274
1275 s_uMsg = uMsg;
1276 s_wParam = wParam;
1277 s_lParam = lParam;
1278
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01001279#ifdef FEAT_BEVAL_GUI
K.Takataa8ec4912022-02-03 14:32:33 +00001280 track_user_activity(uMsg);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001281#endif
1282
1283 switch (uMsg)
1284 {
1285 HANDLE_MSG(hwnd, WM_LBUTTONDBLCLK,_OnMouseButtonDown);
1286 HANDLE_MSG(hwnd, WM_LBUTTONDOWN,_OnMouseButtonDown);
1287 HANDLE_MSG(hwnd, WM_LBUTTONUP, _OnMouseMoveOrRelease);
1288 HANDLE_MSG(hwnd, WM_MBUTTONDBLCLK,_OnMouseButtonDown);
1289 HANDLE_MSG(hwnd, WM_MBUTTONDOWN,_OnMouseButtonDown);
1290 HANDLE_MSG(hwnd, WM_MBUTTONUP, _OnMouseMoveOrRelease);
1291 HANDLE_MSG(hwnd, WM_MOUSEMOVE, _OnMouseMoveOrRelease);
1292 HANDLE_MSG(hwnd, WM_PAINT, _OnPaint);
1293 HANDLE_MSG(hwnd, WM_RBUTTONDBLCLK,_OnMouseButtonDown);
1294 HANDLE_MSG(hwnd, WM_RBUTTONDOWN,_OnMouseButtonDown);
1295 HANDLE_MSG(hwnd, WM_RBUTTONUP, _OnMouseMoveOrRelease);
1296 HANDLE_MSG(hwnd, WM_XBUTTONDBLCLK,_OnMouseButtonDown);
1297 HANDLE_MSG(hwnd, WM_XBUTTONDOWN,_OnMouseButtonDown);
1298 HANDLE_MSG(hwnd, WM_XBUTTONUP, _OnMouseMoveOrRelease);
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01001299 HANDLE_MSG(hwnd, WM_SIZE, _OnSizeTextArea);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001300
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01001301#ifdef FEAT_BEVAL_GUI
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001302 case WM_NOTIFY: Handle_WM_Notify(hwnd, (LPNMHDR)lParam);
1303 return TRUE;
1304#endif
1305 default:
K.Takata4ac893f2022-01-20 12:44:28 +00001306 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001307 }
1308}
1309
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001310/*
1311 * Called when the foreground or background color has been changed.
1312 */
1313 void
1314gui_mch_new_colors(void)
1315{
Bram Moolenaarab85ca42019-11-15 22:41:14 +01001316 HBRUSH prevBrush;
1317
1318 s_brush = CreateSolidBrush(gui.back_pixel);
Bram Moolenaarab85ca42019-11-15 22:41:14 +01001319 prevBrush = (HBRUSH)SetClassLongPtr(
1320 s_hwnd, GCLP_HBRBACKGROUND, (LONG_PTR)s_brush);
Bram Moolenaarab85ca42019-11-15 22:41:14 +01001321 InvalidateRect(s_hwnd, NULL, TRUE);
1322 DeleteObject(prevBrush);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001323}
1324
1325/*
1326 * Set the colors to their default values.
1327 */
1328 void
1329gui_mch_def_colors(void)
1330{
1331 gui.norm_pixel = GetSysColor(COLOR_WINDOWTEXT);
1332 gui.back_pixel = GetSysColor(COLOR_WINDOW);
1333 gui.def_norm_pixel = gui.norm_pixel;
1334 gui.def_back_pixel = gui.back_pixel;
1335}
1336
1337/*
1338 * Open the GUI window which was created by a call to gui_mch_init().
1339 */
1340 int
1341gui_mch_open(void)
1342{
Bram Moolenaar734a8672019-12-02 22:49:38 +01001343 // Actually open the window, if not already visible
1344 // (may be done already in gui_mch_set_shellsize)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001345 if (!IsWindowVisible(s_hwnd))
1346 ShowWindow(s_hwnd, SW_SHOWDEFAULT);
1347
1348#ifdef MSWIN_FIND_REPLACE
Bram Moolenaar734a8672019-12-02 22:49:38 +01001349 // Init replace string here, so that we keep it when re-opening the
1350 // dialog.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001351 s_findrep_struct.lpstrReplaceWith[0] = NUL;
1352#endif
1353
1354 return OK;
1355}
1356
1357/*
1358 * Get the position of the top left corner of the window.
1359 */
1360 int
1361gui_mch_get_winpos(int *x, int *y)
1362{
1363 RECT rect;
1364
1365 GetWindowRect(s_hwnd, &rect);
1366 *x = rect.left;
1367 *y = rect.top;
1368 return OK;
1369}
1370
1371/*
1372 * Set the position of the top left corner of the window to the given
1373 * coordinates.
1374 */
1375 void
1376gui_mch_set_winpos(int x, int y)
1377{
1378 SetWindowPos(s_hwnd, NULL, x, y, 0, 0,
1379 SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
1380}
1381 void
1382gui_mch_set_text_area_pos(int x, int y, int w, int h)
1383{
1384 static int oldx = 0;
1385 static int oldy = 0;
1386
1387 SetWindowPos(s_textArea, NULL, x, y, w, h, SWP_NOZORDER | SWP_NOACTIVATE);
1388
1389#ifdef FEAT_TOOLBAR
1390 if (vim_strchr(p_go, GO_TOOLBAR) != NULL)
1391 SendMessage(s_toolbarhwnd, WM_SIZE,
K.Takatac81e9bf2022-01-16 14:15:49 +00001392 (WPARAM)0, MAKELPARAM(w, gui.toolbar_height));
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001393#endif
1394#if defined(FEAT_GUI_TABLINE)
1395 if (showing_tabline)
1396 {
1397 int top = 0;
1398 RECT rect;
1399
1400# ifdef FEAT_TOOLBAR
1401 if (vim_strchr(p_go, GO_TOOLBAR) != NULL)
K.Takatac81e9bf2022-01-16 14:15:49 +00001402 top = gui.toolbar_height;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001403# endif
1404 GetClientRect(s_hwnd, &rect);
1405 MoveWindow(s_tabhwnd, 0, top, rect.right, gui.tabline_height, TRUE);
1406 }
1407#endif
1408
Bram Moolenaar734a8672019-12-02 22:49:38 +01001409 // When side scroll bar is unshown, the size of window will change.
1410 // then, the text area move left or right. thus client rect should be
1411 // forcedly redrawn. (Yasuhiro Matsumoto)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001412 if (oldx != x || oldy != y)
1413 {
1414 InvalidateRect(s_hwnd, NULL, FALSE);
1415 oldx = x;
1416 oldy = y;
1417 }
1418}
1419
1420
1421/*
1422 * Scrollbar stuff:
1423 */
1424
1425 void
1426gui_mch_enable_scrollbar(
1427 scrollbar_T *sb,
1428 int flag)
1429{
1430 ShowScrollBar(sb->id, SB_CTL, flag);
1431
Bram Moolenaar734a8672019-12-02 22:49:38 +01001432 // TODO: When the window is maximized, the size of the window stays the
1433 // same, thus the size of the text area changes. On Win98 it's OK, on Win
1434 // NT 4.0 it's not...
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001435}
1436
1437 void
1438gui_mch_set_scrollbar_pos(
1439 scrollbar_T *sb,
1440 int x,
1441 int y,
1442 int w,
1443 int h)
1444{
1445 SetWindowPos(sb->id, NULL, x, y, w, h,
1446 SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW);
1447}
1448
Bram Moolenaar203ec772020-07-17 20:43:43 +02001449 int
1450gui_mch_get_scrollbar_xpadding(void)
1451{
1452 RECT rcTxt, rcWnd;
1453 int xpad;
1454
1455 GetWindowRect(s_textArea, &rcTxt);
1456 GetWindowRect(s_hwnd, &rcWnd);
1457 xpad = rcWnd.right - rcTxt.right - gui.scrollbar_width
K.Takatac81e9bf2022-01-16 14:15:49 +00001458 - pGetSystemMetricsForDpi(SM_CXFRAME, s_dpi)
1459 - pGetSystemMetricsForDpi(SM_CXPADDEDBORDER, s_dpi);
Bram Moolenaar203ec772020-07-17 20:43:43 +02001460 return (xpad < 0) ? 0 : xpad;
1461}
1462
1463 int
1464gui_mch_get_scrollbar_ypadding(void)
1465{
1466 RECT rcTxt, rcWnd;
1467 int ypad;
1468
1469 GetWindowRect(s_textArea, &rcTxt);
1470 GetWindowRect(s_hwnd, &rcWnd);
1471 ypad = rcWnd.bottom - rcTxt.bottom - gui.scrollbar_height
K.Takatac81e9bf2022-01-16 14:15:49 +00001472 - pGetSystemMetricsForDpi(SM_CYFRAME, s_dpi)
1473 - pGetSystemMetricsForDpi(SM_CXPADDEDBORDER, s_dpi);
Bram Moolenaar203ec772020-07-17 20:43:43 +02001474 return (ypad < 0) ? 0 : ypad;
1475}
1476
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001477 void
1478gui_mch_create_scrollbar(
1479 scrollbar_T *sb,
Bram Moolenaar734a8672019-12-02 22:49:38 +01001480 int orient) // SBAR_VERT or SBAR_HORIZ
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001481{
1482 sb->id = CreateWindow(
1483 "SCROLLBAR", "Scrollbar",
1484 WS_CHILD | ((orient == SBAR_VERT) ? SBS_VERT : SBS_HORZ), 0, 0,
Bram Moolenaar734a8672019-12-02 22:49:38 +01001485 10, // Any value will do for now
1486 10, // Any value will do for now
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001487 s_hwnd, NULL,
Bram Moolenaarafde13b2019-04-28 19:46:49 +02001488 g_hinst, NULL);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001489}
1490
1491/*
1492 * Find the scrollbar with the given hwnd.
1493 */
Bram Moolenaar98af99f2020-07-16 22:30:31 +02001494 static scrollbar_T *
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001495gui_mswin_find_scrollbar(HWND hwnd)
1496{
1497 win_T *wp;
1498
1499 if (gui.bottom_sbar.id == hwnd)
1500 return &gui.bottom_sbar;
1501 FOR_ALL_WINDOWS(wp)
1502 {
1503 if (wp->w_scrollbars[SBAR_LEFT].id == hwnd)
1504 return &wp->w_scrollbars[SBAR_LEFT];
1505 if (wp->w_scrollbars[SBAR_RIGHT].id == hwnd)
1506 return &wp->w_scrollbars[SBAR_RIGHT];
1507 }
1508 return NULL;
1509}
1510
K.Takatac81e9bf2022-01-16 14:15:49 +00001511 static void
1512update_scrollbar_size(void)
1513{
1514 gui.scrollbar_width = pGetSystemMetricsForDpi(SM_CXVSCROLL, s_dpi);
1515 gui.scrollbar_height = pGetSystemMetricsForDpi(SM_CYHSCROLL, s_dpi);
1516}
1517
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001518/*
K.Takataabe628e2022-01-23 16:25:17 +00001519 * Get the average character size of a font.
1520 */
1521 static void
1522GetAverageFontSize(HDC hdc, SIZE *size)
1523{
1524 // GetTextMetrics() may not return the right value in tmAveCharWidth
1525 // for some fonts. Do our own average computation.
1526 GetTextExtentPoint(hdc,
1527 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
1528 52, size);
1529 size->cx = (size->cx / 26 + 1) / 2;
1530}
1531
1532/*
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001533 * Get the character size of a font.
1534 */
1535 static void
1536GetFontSize(GuiFont font)
1537{
1538 HWND hwnd = GetDesktopWindow();
1539 HDC hdc = GetWindowDC(hwnd);
1540 HFONT hfntOld = SelectFont(hdc, (HFONT)font);
Bram Moolenaar93d77b22019-05-07 22:52:50 +02001541 SIZE size;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001542 TEXTMETRIC tm;
1543
1544 GetTextMetrics(hdc, &tm);
K.Takataabe628e2022-01-23 16:25:17 +00001545 GetAverageFontSize(hdc, &size);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001546
K.Takataabe628e2022-01-23 16:25:17 +00001547 gui.char_width = size.cx + tm.tmOverhang;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001548 gui.char_height = tm.tmHeight + p_linespace;
1549
1550 SelectFont(hdc, hfntOld);
1551
1552 ReleaseDC(hwnd, hdc);
1553}
1554
1555/*
1556 * Adjust gui.char_height (after 'linespace' was changed).
1557 */
1558 int
1559gui_mch_adjust_charheight(void)
1560{
1561 GetFontSize(gui.norm_font);
1562 return OK;
1563}
1564
1565 static GuiFont
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01001566get_font_handle(LOGFONTW *lf)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001567{
1568 HFONT font = NULL;
1569
Bram Moolenaar734a8672019-12-02 22:49:38 +01001570 // Load the font
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01001571 font = CreateFontIndirectW(lf);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001572
1573 if (font == NULL)
1574 return NOFONT;
1575
1576 return (GuiFont)font;
1577}
1578
1579 static int
1580pixels_to_points(int pixels, int vertical)
1581{
1582 int points;
1583 HWND hwnd;
1584 HDC hdc;
1585
1586 hwnd = GetDesktopWindow();
1587 hdc = GetWindowDC(hwnd);
1588
1589 points = MulDiv(pixels, 72,
1590 GetDeviceCaps(hdc, vertical ? LOGPIXELSY : LOGPIXELSX));
1591
1592 ReleaseDC(hwnd, hdc);
1593
1594 return points;
1595}
1596
1597 GuiFont
1598gui_mch_get_font(
1599 char_u *name,
1600 int giveErrorIfMissing)
1601{
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01001602 LOGFONTW lf;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001603 GuiFont font = NOFONT;
1604
1605 if (get_logfont(&lf, name, NULL, giveErrorIfMissing) == OK)
K.Takatac81e9bf2022-01-16 14:15:49 +00001606 {
1607 lf.lfHeight = adjust_fontsize_by_dpi(lf.lfHeight);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001608 font = get_font_handle(&lf);
K.Takatac81e9bf2022-01-16 14:15:49 +00001609 }
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001610 if (font == NOFONT && giveErrorIfMissing)
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001611 semsg(_(e_unknown_font_str), name);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001612 return font;
1613}
1614
1615#if defined(FEAT_EVAL) || defined(PROTO)
1616/*
1617 * Return the name of font "font" in allocated memory.
1618 * Don't know how to get the actual name, thus use the provided name.
1619 */
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001620 char_u *
Bram Moolenaar1266d672017-02-01 13:43:36 +01001621gui_mch_get_fontname(GuiFont font UNUSED, char_u *name)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001622{
1623 if (name == NULL)
1624 return NULL;
1625 return vim_strsave(name);
1626}
1627#endif
1628
1629 void
1630gui_mch_free_font(GuiFont font)
1631{
1632 if (font)
1633 DeleteObject((HFONT)font);
1634}
1635
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001636/*
1637 * Return the Pixel value (color) for the given color name.
1638 * Return INVALCOLOR for error.
1639 */
1640 guicolor_T
1641gui_mch_get_color(char_u *name)
1642{
Bram Moolenaarc285fe72016-04-26 21:51:48 +02001643 int i;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001644
1645 typedef struct SysColorTable
1646 {
1647 char *name;
1648 int color;
1649 } SysColorTable;
1650
1651 static SysColorTable sys_table[] =
1652 {
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001653 {"SYS_3DDKSHADOW", COLOR_3DDKSHADOW},
1654 {"SYS_3DHILIGHT", COLOR_3DHILIGHT},
Bram Moolenaarcea912a2016-10-12 14:20:24 +02001655#ifdef COLOR_3DHIGHLIGHT
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001656 {"SYS_3DHIGHLIGHT", COLOR_3DHIGHLIGHT},
1657#endif
1658 {"SYS_BTNHILIGHT", COLOR_BTNHILIGHT},
1659 {"SYS_BTNHIGHLIGHT", COLOR_BTNHIGHLIGHT},
1660 {"SYS_3DLIGHT", COLOR_3DLIGHT},
1661 {"SYS_3DSHADOW", COLOR_3DSHADOW},
1662 {"SYS_DESKTOP", COLOR_DESKTOP},
1663 {"SYS_INFOBK", COLOR_INFOBK},
1664 {"SYS_INFOTEXT", COLOR_INFOTEXT},
1665 {"SYS_3DFACE", COLOR_3DFACE},
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001666 {"SYS_BTNFACE", COLOR_BTNFACE},
1667 {"SYS_BTNSHADOW", COLOR_BTNSHADOW},
1668 {"SYS_ACTIVEBORDER", COLOR_ACTIVEBORDER},
1669 {"SYS_ACTIVECAPTION", COLOR_ACTIVECAPTION},
1670 {"SYS_APPWORKSPACE", COLOR_APPWORKSPACE},
1671 {"SYS_BACKGROUND", COLOR_BACKGROUND},
1672 {"SYS_BTNTEXT", COLOR_BTNTEXT},
1673 {"SYS_CAPTIONTEXT", COLOR_CAPTIONTEXT},
1674 {"SYS_GRAYTEXT", COLOR_GRAYTEXT},
1675 {"SYS_HIGHLIGHT", COLOR_HIGHLIGHT},
1676 {"SYS_HIGHLIGHTTEXT", COLOR_HIGHLIGHTTEXT},
1677 {"SYS_INACTIVEBORDER", COLOR_INACTIVEBORDER},
1678 {"SYS_INACTIVECAPTION", COLOR_INACTIVECAPTION},
1679 {"SYS_INACTIVECAPTIONTEXT", COLOR_INACTIVECAPTIONTEXT},
1680 {"SYS_MENU", COLOR_MENU},
1681 {"SYS_MENUTEXT", COLOR_MENUTEXT},
1682 {"SYS_SCROLLBAR", COLOR_SCROLLBAR},
1683 {"SYS_WINDOW", COLOR_WINDOW},
1684 {"SYS_WINDOWFRAME", COLOR_WINDOWFRAME},
1685 {"SYS_WINDOWTEXT", COLOR_WINDOWTEXT}
1686 };
1687
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001688 /*
1689 * Try to look up a system colour.
1690 */
K.Takataeeec2542021-06-02 13:28:16 +02001691 for (i = 0; i < ARRAY_LENGTH(sys_table); i++)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001692 if (STRICMP(name, sys_table[i].name) == 0)
1693 return GetSysColor(sys_table[i].color);
1694
Bram Moolenaarab302212016-04-26 20:59:29 +02001695 return gui_get_color_cmn(name);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001696}
Bram Moolenaarc285fe72016-04-26 21:51:48 +02001697
Bram Moolenaar26af85d2017-07-23 16:45:10 +02001698 guicolor_T
1699gui_mch_get_rgb_color(int r, int g, int b)
1700{
1701 return gui_get_rgb_color_cmn(r, g, b);
1702}
1703
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001704/*
1705 * Return OK if the key with the termcap name "name" is supported.
1706 */
1707 int
1708gui_mch_haskey(char_u *name)
1709{
1710 int i;
1711
1712 for (i = 0; special_keys[i].vim_code1 != NUL; i++)
1713 if (name[0] == special_keys[i].vim_code0 &&
1714 name[1] == special_keys[i].vim_code1)
1715 return OK;
1716 return FAIL;
1717}
1718
1719 void
1720gui_mch_beep(void)
1721{
LemonBoy77771d32022-04-13 11:47:25 +01001722 MessageBeep((UINT)-1);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001723}
1724/*
1725 * Invert a rectangle from row r, column c, for nr rows and nc columns.
1726 */
1727 void
1728gui_mch_invert_rectangle(
1729 int r,
1730 int c,
1731 int nr,
1732 int nc)
1733{
1734 RECT rc;
1735
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01001736#if defined(FEAT_DIRECTX)
1737 if (IS_ENABLE_DIRECTX())
1738 DWriteContext_Flush(s_dwc);
1739#endif
1740
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001741 /*
1742 * Note: InvertRect() excludes right and bottom of rectangle.
1743 */
1744 rc.left = FILL_X(c);
1745 rc.top = FILL_Y(r);
1746 rc.right = rc.left + nc * gui.char_width;
1747 rc.bottom = rc.top + nr * gui.char_height;
1748 InvertRect(s_hdc, &rc);
1749}
1750
1751/*
1752 * Iconify the GUI window.
1753 */
1754 void
1755gui_mch_iconify(void)
1756{
1757 ShowWindow(s_hwnd, SW_MINIMIZE);
1758}
1759
1760/*
1761 * Draw a cursor without focus.
1762 */
1763 void
1764gui_mch_draw_hollow_cursor(guicolor_T color)
1765{
1766 HBRUSH hbr;
1767 RECT rc;
1768
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01001769#if defined(FEAT_DIRECTX)
1770 if (IS_ENABLE_DIRECTX())
1771 DWriteContext_Flush(s_dwc);
1772#endif
1773
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001774 /*
1775 * Note: FrameRect() excludes right and bottom of rectangle.
1776 */
1777 rc.left = FILL_X(gui.col);
1778 rc.top = FILL_Y(gui.row);
1779 rc.right = rc.left + gui.char_width;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001780 if (mb_lefthalve(gui.row, gui.col))
1781 rc.right += gui.char_width;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001782 rc.bottom = rc.top + gui.char_height;
1783 hbr = CreateSolidBrush(color);
1784 FrameRect(s_hdc, &rc, hbr);
1785 DeleteBrush(hbr);
1786}
1787/*
1788 * Draw part of a cursor, "w" pixels wide, and "h" pixels high, using
1789 * color "color".
1790 */
1791 void
1792gui_mch_draw_part_cursor(
1793 int w,
1794 int h,
1795 guicolor_T color)
1796{
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001797 RECT rc;
1798
1799 /*
1800 * Note: FillRect() excludes right and bottom of rectangle.
1801 */
1802 rc.left =
1803#ifdef FEAT_RIGHTLEFT
Bram Moolenaar734a8672019-12-02 22:49:38 +01001804 // vertical line should be on the right of current point
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001805 CURSOR_BAR_RIGHT ? FILL_X(gui.col + 1) - w :
1806#endif
1807 FILL_X(gui.col);
1808 rc.top = FILL_Y(gui.row) + gui.char_height - h;
1809 rc.right = rc.left + w;
1810 rc.bottom = rc.top + h;
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01001811
Bram Moolenaar92467d32017-12-05 13:22:16 +01001812 fill_rect(&rc, NULL, color);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001813}
1814
1815
1816/*
1817 * Generates a VK_SPACE when the internal dead_key flag is set to output the
1818 * dead key's nominal character and re-post the original message.
1819 */
1820 static void
1821outputDeadKey_rePost(MSG originalMsg)
1822{
1823 static MSG deadCharExpel;
1824
1825 if (!dead_key)
1826 return;
1827
1828 dead_key = 0;
1829
Bram Moolenaar734a8672019-12-02 22:49:38 +01001830 // Make Windows generate the dead key's character
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001831 deadCharExpel.message = originalMsg.message;
1832 deadCharExpel.hwnd = originalMsg.hwnd;
1833 deadCharExpel.wParam = VK_SPACE;
1834
K.Takata4ac893f2022-01-20 12:44:28 +00001835 TranslateMessage(&deadCharExpel);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001836
Bram Moolenaar734a8672019-12-02 22:49:38 +01001837 // re-generate the current character free of the dead char influence
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001838 PostMessage(originalMsg.hwnd, originalMsg.message, originalMsg.wParam,
1839 originalMsg.lParam);
1840}
1841
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001842/*
1843 * Process a single Windows message.
1844 * If one is not available we hang until one is.
1845 */
1846 static void
1847process_message(void)
1848{
1849 MSG msg;
Bram Moolenaar734a8672019-12-02 22:49:38 +01001850 UINT vk = 0; // Virtual key
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001851 char_u string[40];
1852 int i;
1853 int modifiers = 0;
1854 int key;
1855#ifdef FEAT_MENU
1856 static char_u k10[] = {K_SPECIAL, 'k', ';', 0};
1857#endif
LemonBoy77fc0b02022-04-22 22:45:52 +01001858 BYTE keyboard_state[256];
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001859
K.Takatab7057bd2022-01-21 11:37:07 +00001860 GetMessageW(&msg, NULL, 0, 0);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001861
1862#ifdef FEAT_OLE
Bram Moolenaar734a8672019-12-02 22:49:38 +01001863 // Look after OLE Automation commands
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001864 if (msg.message == WM_OLE)
1865 {
1866 char_u *str = (char_u *)msg.lParam;
1867 if (str == NULL || *str == NUL)
1868 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01001869 // Message can't be ours, forward it. Fixes problem with Ultramon
1870 // 3.0.4
K.Takatab7057bd2022-01-21 11:37:07 +00001871 DispatchMessageW(&msg);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001872 }
1873 else
1874 {
1875 add_to_input_buf(str, (int)STRLEN(str));
Bram Moolenaar734a8672019-12-02 22:49:38 +01001876 vim_free(str); // was allocated in CVim::SendKeys()
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001877 }
1878 return;
1879 }
1880#endif
1881
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001882#ifdef MSWIN_FIND_REPLACE
Bram Moolenaar734a8672019-12-02 22:49:38 +01001883 // Don't process messages used by the dialog
K.Takatab7057bd2022-01-21 11:37:07 +00001884 if (s_findrep_hwnd != NULL && IsDialogMessageW(s_findrep_hwnd, &msg))
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001885 {
1886 HandleMouseHide(msg.message, msg.lParam);
1887 return;
1888 }
1889#endif
1890
1891 /*
1892 * Check if it's a special key that we recognise. If not, call
1893 * TranslateMessage().
1894 */
1895 if (msg.message == WM_KEYDOWN || msg.message == WM_SYSKEYDOWN)
1896 {
1897 vk = (int) msg.wParam;
1898
1899 /*
1900 * Handle dead keys in special conditions in other cases we let Windows
1901 * handle them and do not interfere.
1902 *
1903 * The dead_key flag must be reset on several occasions:
1904 * - in _OnChar() (or _OnSysChar()) as any dead key was necessarily
1905 * consumed at that point (This is when we let Windows combine the
1906 * dead character on its own)
1907 *
1908 * - Before doing something special such as regenerating keypresses to
1909 * expel the dead character as this could trigger an infinite loop if
K.Takata4ac893f2022-01-20 12:44:28 +00001910 * for some reason TranslateMessage() do not trigger a call
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001911 * immediately to _OnChar() (or _OnSysChar()).
1912 */
1913 if (dead_key)
1914 {
1915 /*
1916 * If a dead key was pressed and the user presses VK_SPACE,
1917 * VK_BACK, or VK_ESCAPE it means that he actually wants to deal
1918 * with the dead char now, so do nothing special and let Windows
1919 * handle it.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001920 */
1921 if ((vk == VK_SPACE || vk == VK_BACK || vk == VK_ESCAPE))
1922 {
1923 dead_key = 0;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001924 }
Bram Moolenaar734a8672019-12-02 22:49:38 +01001925 // In modes where we are not typing, dead keys should behave
1926 // normally
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001927 else if (!(get_real_state() & (INSERT | CMDLINE | SELECTMODE)))
1928 {
1929 outputDeadKey_rePost(msg);
1930 return;
1931 }
1932 }
1933
Bram Moolenaar734a8672019-12-02 22:49:38 +01001934 // Check for CTRL-BREAK
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001935 if (vk == VK_CANCEL)
1936 {
1937 trash_input_buf();
1938 got_int = TRUE;
Bram Moolenaar9698ad72017-08-12 14:52:15 +02001939 ctrl_break_was_pressed = TRUE;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001940 string[0] = Ctrl_C;
1941 add_to_input_buf(string, 1);
1942 }
1943
LemonBoy77fc0b02022-04-22 22:45:52 +01001944 // This is an IME event or a synthetic keystroke, let Windows handle it.
1945 if (vk == VK_PROCESSKEY || vk == VK_PACKET)
1946 {
1947 TranslateMessage(&msg);
1948 return;
1949 }
1950
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001951 for (i = 0; special_keys[i].key_sym != 0; i++)
1952 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01001953 // ignore VK_SPACE when ALT key pressed: system menu
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001954 if (special_keys[i].key_sym == vk
1955 && (vk != VK_SPACE || !(GetKeyState(VK_MENU) & 0x8000)))
1956 {
1957 /*
Bram Moolenaar945ec092016-06-08 21:17:43 +02001958 * Behave as expected if we have a dead key and the special key
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001959 * is a key that would normally trigger the dead key nominal
1960 * character output (such as a NUMPAD printable character or
1961 * the TAB key, etc...).
1962 */
1963 if (dead_key && (special_keys[i].vim_code0 == 'K'
1964 || vk == VK_TAB || vk == CAR))
1965 {
1966 outputDeadKey_rePost(msg);
1967 return;
1968 }
1969
1970#ifdef FEAT_MENU
Bram Moolenaar734a8672019-12-02 22:49:38 +01001971 // Check for <F10>: Windows selects the menu. When <F10> is
1972 // mapped we want to use the mapping instead.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001973 if (vk == VK_F10
1974 && gui.menu_is_active
1975 && check_map(k10, State, FALSE, TRUE, FALSE,
1976 NULL, NULL) == NULL)
1977 break;
1978#endif
1979 if (GetKeyState(VK_SHIFT) & 0x8000)
1980 modifiers |= MOD_MASK_SHIFT;
1981 /*
1982 * Don't use caps-lock as shift, because these are special keys
1983 * being considered here, and we only want letters to get
1984 * shifted -- webb
1985 */
1986 /*
1987 if (GetKeyState(VK_CAPITAL) & 0x0001)
1988 modifiers ^= MOD_MASK_SHIFT;
1989 */
1990 if (GetKeyState(VK_CONTROL) & 0x8000)
1991 modifiers |= MOD_MASK_CTRL;
1992 if (GetKeyState(VK_MENU) & 0x8000)
1993 modifiers |= MOD_MASK_ALT;
1994
1995 if (special_keys[i].vim_code1 == NUL)
1996 key = special_keys[i].vim_code0;
1997 else
1998 key = TO_SPECIAL(special_keys[i].vim_code0,
1999 special_keys[i].vim_code1);
2000 key = simplify_key(key, &modifiers);
2001 if (key == CSI)
2002 key = K_CSI;
2003
2004 if (modifiers)
2005 {
2006 string[0] = CSI;
2007 string[1] = KS_MODIFIER;
2008 string[2] = modifiers;
2009 add_to_input_buf(string, 3);
2010 }
2011
2012 if (IS_SPECIAL(key))
2013 {
2014 string[0] = CSI;
2015 string[1] = K_SECOND(key);
2016 string[2] = K_THIRD(key);
2017 add_to_input_buf(string, 3);
2018 }
2019 else
2020 {
2021 int len;
2022
Bram Moolenaar734a8672019-12-02 22:49:38 +01002023 // Handle "key" as a Unicode character.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002024 len = char_to_string(key, string, 40, FALSE);
2025 add_to_input_buf(string, len);
2026 }
2027 break;
2028 }
2029 }
LemonBoy77fc0b02022-04-22 22:45:52 +01002030
2031 // Not a special key.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002032 if (special_keys[i].key_sym == 0)
2033 {
LemonBoy77fc0b02022-04-22 22:45:52 +01002034 WCHAR ch[8];
2035 int len;
2036 int i;
2037 UINT scan_code;
2038
2039 if (GetKeyState(VK_SHIFT) & 0x8000)
2040 modifiers |= MOD_MASK_SHIFT;
2041 if (GetKeyState(VK_CONTROL) & 0x8000)
2042 modifiers |= MOD_MASK_CTRL;
2043 if (GetKeyState(VK_LMENU) & 0x8000)
2044 modifiers |= MOD_MASK_ALT;
2045
2046 // Construct the state table with only a few modifiers, we don't
2047 // really care about the presence of Ctrl/Alt as those modifiers are
2048 // handled by Vim separately.
2049 memset(keyboard_state, 0, 256);
2050 if (GetKeyState(VK_SHIFT) & 0x8000)
2051 keyboard_state[VK_SHIFT] = 0x80;
LemonBoy0de73692022-04-23 11:08:11 +01002052 if (GetKeyState(VK_CAPITAL) & 0x0001)
2053 keyboard_state[VK_CAPITAL] = 0x01;
LemonBoy77fc0b02022-04-22 22:45:52 +01002054 if (GetKeyState(VK_RMENU) & 0x8000)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002055 {
LemonBoy77fc0b02022-04-22 22:45:52 +01002056 keyboard_state[VK_MENU] = 0x80;
2057 keyboard_state[VK_CONTROL] = 0x80;
2058 }
2059
2060 // Translate the virtual key according to the current keyboard
2061 // layout.
2062 scan_code = MapVirtualKey(vk, MAPVK_VK_TO_VSC);
2063 // Convert the scan-code into a sequence of zero or more unicode
2064 // codepoints.
2065 // If this is a dead key ToUnicode returns a negative value.
2066 len = ToUnicode(vk, scan_code, keyboard_state, ch, ARRAY_LENGTH(ch),
2067 0);
2068 dead_key = len < 0;
2069
2070 if (len <= 0)
2071 return;
2072
2073 // Post the message as TranslateMessage would do.
2074 if (msg.message == WM_KEYDOWN)
2075 {
2076 for (i = 0; i < len; i++)
2077 PostMessageW(msg.hwnd, WM_CHAR, ch[i], msg.lParam);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002078 }
2079 else
LemonBoy77fc0b02022-04-22 22:45:52 +01002080 {
2081 for (i = 0; i < len; i++)
2082 PostMessageW(msg.hwnd, WM_SYSCHAR, ch[i], msg.lParam);
2083 }
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002084 }
2085 }
2086#ifdef FEAT_MBYTE_IME
2087 else if (msg.message == WM_IME_NOTIFY)
2088 _OnImeNotify(msg.hwnd, (DWORD)msg.wParam, (DWORD)msg.lParam);
2089 else if (msg.message == WM_KEYUP && im_get_status())
Bram Moolenaar734a8672019-12-02 22:49:38 +01002090 // added for non-MS IME (Yasuhiro Matsumoto)
K.Takata4ac893f2022-01-20 12:44:28 +00002091 TranslateMessage(&msg);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002092#endif
2093
2094#ifdef FEAT_MENU
Bram Moolenaar734a8672019-12-02 22:49:38 +01002095 // Check for <F10>: Default effect is to select the menu. When <F10> is
2096 // mapped we need to stop it here to avoid strange effects (e.g., for the
2097 // key-up event)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002098 if (vk != VK_F10 || check_map(k10, State, FALSE, TRUE, FALSE,
2099 NULL, NULL) == NULL)
2100#endif
K.Takatab7057bd2022-01-21 11:37:07 +00002101 DispatchMessageW(&msg);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002102}
2103
2104/*
2105 * Catch up with any queued events. This may put keyboard input into the
2106 * input buffer, call resize call-backs, trigger timers etc. If there is
2107 * nothing in the event queue (& no timers pending), then we return
2108 * immediately.
2109 */
2110 void
2111gui_mch_update(void)
2112{
2113 MSG msg;
2114
2115 if (!s_busy_processing)
K.Takatab7057bd2022-01-21 11:37:07 +00002116 while (PeekMessageW(&msg, NULL, 0, 0, PM_NOREMOVE)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002117 && !vim_is_input_buf_full())
2118 process_message();
2119}
2120
Bram Moolenaar4231da42016-06-02 14:30:04 +02002121 static void
2122remove_any_timer(void)
2123{
2124 MSG msg;
2125
2126 if (s_wait_timer != 0 && !s_timed_out)
2127 {
2128 KillTimer(NULL, s_wait_timer);
2129
Bram Moolenaar734a8672019-12-02 22:49:38 +01002130 // Eat spurious WM_TIMER messages
K.Takatab7057bd2022-01-21 11:37:07 +00002131 while (PeekMessageW(&msg, s_hwnd, WM_TIMER, WM_TIMER, PM_REMOVE))
Bram Moolenaar4231da42016-06-02 14:30:04 +02002132 ;
2133 s_wait_timer = 0;
2134 }
2135}
2136
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002137/*
2138 * GUI input routine called by gui_wait_for_chars(). Waits for a character
2139 * from the keyboard.
2140 * wtime == -1 Wait forever.
2141 * wtime == 0 This should never happen.
2142 * wtime > 0 Wait wtime milliseconds for a character.
2143 * Returns OK if a character was found to be available within the given time,
2144 * or FAIL otherwise.
2145 */
2146 int
2147gui_mch_wait_for_chars(int wtime)
2148{
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002149 int focus;
2150
2151 s_timed_out = FALSE;
2152
Bram Moolenaar12dfc9e2019-01-28 22:32:58 +01002153 if (wtime >= 0)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002154 {
Bram Moolenaar12dfc9e2019-01-28 22:32:58 +01002155 // Don't do anything while processing a (scroll) message.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002156 if (s_busy_processing)
2157 return FAIL;
Bram Moolenaar12dfc9e2019-01-28 22:32:58 +01002158
2159 // When called with "wtime" zero, just want one msec.
K.Takataa8ec4912022-02-03 14:32:33 +00002160 s_wait_timer = SetTimer(NULL, 0, (UINT)(wtime == 0 ? 1 : wtime),
2161 _OnTimer);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002162 }
2163
2164 allow_scrollbar = TRUE;
2165
2166 focus = gui.in_focus;
2167 while (!s_timed_out)
2168 {
Bram Moolenaar89c00032019-09-04 13:53:21 +02002169 // Stop or start blinking when focus changes
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002170 if (gui.in_focus != focus)
2171 {
2172 if (gui.in_focus)
2173 gui_mch_start_blink();
2174 else
Bram Moolenaar1dd45fb2018-01-31 21:10:01 +01002175 gui_mch_stop_blink(TRUE);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002176 focus = gui.in_focus;
2177 }
2178
2179 if (s_need_activate)
2180 {
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002181 (void)SetForegroundWindow(s_hwnd);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002182 s_need_activate = FALSE;
2183 }
2184
Bram Moolenaar4231da42016-06-02 14:30:04 +02002185#ifdef FEAT_TIMERS
2186 did_add_timer = FALSE;
2187#endif
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002188#ifdef MESSAGE_QUEUE
Bram Moolenaar89c00032019-09-04 13:53:21 +02002189 // Check channel I/O while waiting for a message.
Bram Moolenaar9186a272016-02-23 19:34:01 +01002190 for (;;)
2191 {
2192 MSG msg;
2193
2194 parse_queued_messages();
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002195# ifdef FEAT_TIMERS
Bram Moolenaar89c00032019-09-04 13:53:21 +02002196 if (did_add_timer)
2197 break;
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002198# endif
K.Takatab7057bd2022-01-21 11:37:07 +00002199 if (PeekMessageW(&msg, NULL, 0, 0, PM_NOREMOVE))
Bram Moolenaar62426e12017-08-13 15:37:58 +02002200 {
2201 process_message();
2202 break;
2203 }
Bram Moolenaar89c00032019-09-04 13:53:21 +02002204 else if (input_available()
Bram Moolenaar032f40a2020-11-18 15:21:50 +01002205 // TODO: The 10 msec is a compromise between laggy response
2206 // and consuming more CPU time. Better would be to handle
2207 // channel messages when they arrive.
2208 || MsgWaitForMultipleObjects(0, NULL, FALSE, 10,
Bram Moolenaar89c00032019-09-04 13:53:21 +02002209 QS_ALLINPUT) != WAIT_TIMEOUT)
Bram Moolenaar9186a272016-02-23 19:34:01 +01002210 break;
2211 }
Bram Moolenaar62426e12017-08-13 15:37:58 +02002212#else
Bram Moolenaar89c00032019-09-04 13:53:21 +02002213 // Don't use gui_mch_update() because then we will spin-lock until a
2214 // char arrives, instead we use GetMessage() to hang until an
2215 // event arrives. No need to check for input_buf_full because we are
2216 // returning as soon as it contains a single char -- webb
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002217 process_message();
Bram Moolenaar62426e12017-08-13 15:37:58 +02002218#endif
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002219
2220 if (input_available())
2221 {
Bram Moolenaar4231da42016-06-02 14:30:04 +02002222 remove_any_timer();
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002223 allow_scrollbar = FALSE;
2224
Bram Moolenaar89c00032019-09-04 13:53:21 +02002225 // Clear pending mouse button, the release event may have been
2226 // taken by the dialog window. But don't do this when getting
2227 // focus, we need the mouse-up event then.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002228 if (!s_getting_focus)
2229 s_button_pending = -1;
2230
2231 return OK;
2232 }
Bram Moolenaar4231da42016-06-02 14:30:04 +02002233
2234#ifdef FEAT_TIMERS
2235 if (did_add_timer)
2236 {
Bram Moolenaar89c00032019-09-04 13:53:21 +02002237 // Need to recompute the waiting time.
Bram Moolenaar4231da42016-06-02 14:30:04 +02002238 remove_any_timer();
2239 break;
2240 }
2241#endif
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002242 }
2243 allow_scrollbar = FALSE;
2244 return FAIL;
2245}
2246
2247/*
2248 * Clear a rectangular region of the screen from text pos (row1, col1) to
2249 * (row2, col2) inclusive.
2250 */
2251 void
2252gui_mch_clear_block(
2253 int row1,
2254 int col1,
2255 int row2,
2256 int col2)
2257{
2258 RECT rc;
2259
2260 /*
2261 * Clear one extra pixel at the far right, for when bold characters have
2262 * spilled over to the window border.
2263 * Note: FillRect() excludes right and bottom of rectangle.
2264 */
2265 rc.left = FILL_X(col1);
2266 rc.top = FILL_Y(row1);
2267 rc.right = FILL_X(col2 + 1) + (col2 == Columns - 1);
2268 rc.bottom = FILL_Y(row2 + 1);
2269 clear_rect(&rc);
2270}
2271
2272/*
2273 * Clear the whole text window.
2274 */
2275 void
2276gui_mch_clear_all(void)
2277{
2278 RECT rc;
2279
2280 rc.left = 0;
2281 rc.top = 0;
2282 rc.right = Columns * gui.char_width + 2 * gui.border_width;
2283 rc.bottom = Rows * gui.char_height + 2 * gui.border_width;
2284 clear_rect(&rc);
2285}
2286/*
2287 * Menu stuff.
2288 */
2289
2290 void
2291gui_mch_enable_menu(int flag)
2292{
2293#ifdef FEAT_MENU
2294 SetMenu(s_hwnd, flag ? s_menuBar : NULL);
2295#endif
2296}
2297
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002298 void
2299gui_mch_set_menu_pos(
Bram Moolenaar1266d672017-02-01 13:43:36 +01002300 int x UNUSED,
2301 int y UNUSED,
2302 int w UNUSED,
2303 int h UNUSED)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002304{
Bram Moolenaar734a8672019-12-02 22:49:38 +01002305 // It will be in the right place anyway
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002306}
2307
2308#if defined(FEAT_MENU) || defined(PROTO)
2309/*
2310 * Make menu item hidden or not hidden
2311 */
2312 void
2313gui_mch_menu_hidden(
2314 vimmenu_T *menu,
2315 int hidden)
2316{
2317 /*
2318 * This doesn't do what we want. Hmm, just grey the menu items for now.
2319 */
2320 /*
2321 if (hidden)
2322 EnableMenuItem(s_menuBar, menu->id, MF_BYCOMMAND | MF_DISABLED);
2323 else
2324 EnableMenuItem(s_menuBar, menu->id, MF_BYCOMMAND | MF_ENABLED);
2325 */
2326 gui_mch_menu_grey(menu, hidden);
2327}
2328
2329/*
2330 * This is called after setting all the menus to grey/hidden or not.
2331 */
2332 void
2333gui_mch_draw_menubar(void)
2334{
2335 DrawMenuBar(s_hwnd);
2336}
Bram Moolenaar734a8672019-12-02 22:49:38 +01002337#endif // FEAT_MENU
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002338
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002339/*
2340 * Return the RGB value of a pixel as a long.
2341 */
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02002342 guicolor_T
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002343gui_mch_get_rgb(guicolor_T pixel)
2344{
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02002345 return (guicolor_T)((GetRValue(pixel) << 16) + (GetGValue(pixel) << 8)
2346 + GetBValue(pixel));
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002347}
2348
2349#if defined(FEAT_GUI_DIALOG) || defined(PROTO)
Bram Moolenaar734a8672019-12-02 22:49:38 +01002350/*
2351 * Convert pixels in X to dialog units
2352 */
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002353 static WORD
2354PixelToDialogX(int numPixels)
2355{
2356 return (WORD)((numPixels * 4) / s_dlgfntwidth);
2357}
2358
Bram Moolenaar734a8672019-12-02 22:49:38 +01002359/*
2360 * Convert pixels in Y to dialog units
2361 */
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002362 static WORD
2363PixelToDialogY(int numPixels)
2364{
2365 return (WORD)((numPixels * 8) / s_dlgfntheight);
2366}
2367
Bram Moolenaar734a8672019-12-02 22:49:38 +01002368/*
2369 * Return the width in pixels of the given text in the given DC.
2370 */
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002371 static int
2372GetTextWidth(HDC hdc, char_u *str, int len)
2373{
2374 SIZE size;
2375
2376 GetTextExtentPoint(hdc, (LPCSTR)str, len, &size);
2377 return size.cx;
2378}
2379
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002380/*
2381 * Return the width in pixels of the given text in the given DC, taking care
2382 * of 'encoding' to active codepage conversion.
2383 */
2384 static int
2385GetTextWidthEnc(HDC hdc, char_u *str, int len)
2386{
2387 SIZE size;
2388 WCHAR *wstr;
2389 int n;
2390 int wlen = len;
2391
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02002392 wstr = enc_to_utf16(str, &wlen);
2393 if (wstr == NULL)
2394 return 0;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002395
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02002396 n = GetTextExtentPointW(hdc, wstr, wlen, &size);
2397 vim_free(wstr);
2398 if (n)
2399 return size.cx;
2400 return 0;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002401}
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002402
Bram Moolenaar87f3d202016-12-01 20:18:50 +01002403static void get_work_area(RECT *spi_rect);
2404
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002405/*
2406 * A quick little routine that will center one window over another, handy for
Bram Moolenaar87f3d202016-12-01 20:18:50 +01002407 * dialog boxes. Taken from the Win32SDK samples and modified for multiple
2408 * monitors.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002409 */
2410 static BOOL
2411CenterWindow(
2412 HWND hwndChild,
2413 HWND hwndParent)
2414{
Bram Moolenaar87f3d202016-12-01 20:18:50 +01002415 HMONITOR mon;
2416 MONITORINFO moninfo;
2417 RECT rChild, rParent, rScreen;
2418 int wChild, hChild, wParent, hParent;
2419 int xNew, yNew;
2420 HDC hdc;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002421
2422 GetWindowRect(hwndChild, &rChild);
2423 wChild = rChild.right - rChild.left;
2424 hChild = rChild.bottom - rChild.top;
2425
Bram Moolenaar734a8672019-12-02 22:49:38 +01002426 // If Vim is minimized put the window in the middle of the screen.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002427 if (hwndParent == NULL || IsMinimized(hwndParent))
Bram Moolenaar87f3d202016-12-01 20:18:50 +01002428 get_work_area(&rParent);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002429 else
2430 GetWindowRect(hwndParent, &rParent);
2431 wParent = rParent.right - rParent.left;
2432 hParent = rParent.bottom - rParent.top;
2433
Bram Moolenaar87f3d202016-12-01 20:18:50 +01002434 moninfo.cbSize = sizeof(MONITORINFO);
2435 mon = MonitorFromWindow(hwndChild, MONITOR_DEFAULTTOPRIMARY);
2436 if (mon != NULL && GetMonitorInfo(mon, &moninfo))
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002437 {
Bram Moolenaar87f3d202016-12-01 20:18:50 +01002438 rScreen = moninfo.rcWork;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002439 }
Bram Moolenaar87f3d202016-12-01 20:18:50 +01002440 else
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002441 {
Bram Moolenaar87f3d202016-12-01 20:18:50 +01002442 hdc = GetDC(hwndChild);
2443 rScreen.left = 0;
2444 rScreen.top = 0;
2445 rScreen.right = GetDeviceCaps(hdc, HORZRES);
2446 rScreen.bottom = GetDeviceCaps(hdc, VERTRES);
2447 ReleaseDC(hwndChild, hdc);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002448 }
2449
Bram Moolenaar87f3d202016-12-01 20:18:50 +01002450 xNew = rParent.left + ((wParent - wChild) / 2);
2451 if (xNew < rScreen.left)
2452 xNew = rScreen.left;
2453 else if ((xNew + wChild) > rScreen.right)
2454 xNew = rScreen.right - wChild;
2455
2456 yNew = rParent.top + ((hParent - hChild) / 2);
2457 if (yNew < rScreen.top)
2458 yNew = rScreen.top;
2459 else if ((yNew + hChild) > rScreen.bottom)
2460 yNew = rScreen.bottom - hChild;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002461
2462 return SetWindowPos(hwndChild, NULL, xNew, yNew, 0, 0,
2463 SWP_NOSIZE | SWP_NOZORDER);
2464}
Bram Moolenaar734a8672019-12-02 22:49:38 +01002465#endif // FEAT_GUI_DIALOG
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002466
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002467#if defined(FEAT_TOOLBAR) || defined(PROTO)
2468 void
2469gui_mch_show_toolbar(int showit)
2470{
2471 if (s_toolbarhwnd == NULL)
2472 return;
2473
2474 if (showit)
2475 {
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02002476 // Enable unicode support
2477 SendMessage(s_toolbarhwnd, TB_SETUNICODEFORMAT, (WPARAM)TRUE,
2478 (LPARAM)0);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002479 ShowWindow(s_toolbarhwnd, SW_SHOW);
2480 }
2481 else
2482 ShowWindow(s_toolbarhwnd, SW_HIDE);
2483}
2484
Bram Moolenaar734a8672019-12-02 22:49:38 +01002485// The number of bitmaps is fixed. Exit is missing!
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002486# define TOOLBAR_BITMAP_COUNT 31
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002487
2488#endif
2489
2490#if defined(FEAT_GUI_TABLINE) || defined(PROTO)
2491 static void
2492add_tabline_popup_menu_entry(HMENU pmenu, UINT item_id, char_u *item_text)
2493{
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02002494 WCHAR *wn;
2495 MENUITEMINFOW infow;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002496
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02002497 wn = enc_to_utf16(item_text, NULL);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002498 if (wn == NULL)
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02002499 return;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002500
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02002501 infow.cbSize = sizeof(infow);
2502 infow.fMask = MIIM_TYPE | MIIM_ID;
2503 infow.wID = item_id;
2504 infow.fType = MFT_STRING;
2505 infow.dwTypeData = wn;
2506 infow.cch = (UINT)wcslen(wn);
2507 InsertMenuItemW(pmenu, item_id, FALSE, &infow);
2508 vim_free(wn);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002509}
2510
2511 static void
2512show_tabline_popup_menu(void)
2513{
2514 HMENU tab_pmenu;
2515 long rval;
2516 POINT pt;
2517
Bram Moolenaar734a8672019-12-02 22:49:38 +01002518 // When ignoring events don't show the menu.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002519 if (hold_gui_events
2520# ifdef FEAT_CMDWIN
2521 || cmdwin_type != 0
2522# endif
2523 )
2524 return;
2525
2526 tab_pmenu = CreatePopupMenu();
2527 if (tab_pmenu == NULL)
2528 return;
2529
2530 if (first_tabpage->tp_next != NULL)
2531 add_tabline_popup_menu_entry(tab_pmenu,
2532 TABLINE_MENU_CLOSE, (char_u *)_("Close tab"));
2533 add_tabline_popup_menu_entry(tab_pmenu,
2534 TABLINE_MENU_NEW, (char_u *)_("New tab"));
2535 add_tabline_popup_menu_entry(tab_pmenu,
2536 TABLINE_MENU_OPEN, (char_u *)_("Open tab..."));
2537
2538 GetCursorPos(&pt);
2539 rval = TrackPopupMenuEx(tab_pmenu, TPM_RETURNCMD, pt.x, pt.y, s_tabhwnd,
2540 NULL);
2541
2542 DestroyMenu(tab_pmenu);
2543
Bram Moolenaar734a8672019-12-02 22:49:38 +01002544 // Add the string cmd into input buffer
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002545 if (rval > 0)
2546 {
2547 TCHITTESTINFO htinfo;
2548 int idx;
2549
2550 if (ScreenToClient(s_tabhwnd, &pt) == 0)
2551 return;
2552
2553 htinfo.pt.x = pt.x;
2554 htinfo.pt.y = pt.y;
2555 idx = TabCtrl_HitTest(s_tabhwnd, &htinfo);
2556 if (idx == -1)
2557 idx = 0;
2558 else
2559 idx += 1;
2560
2561 send_tabline_menu_event(idx, (int)rval);
2562 }
2563}
2564
2565/*
2566 * Show or hide the tabline.
2567 */
2568 void
2569gui_mch_show_tabline(int showit)
2570{
2571 if (s_tabhwnd == NULL)
2572 return;
2573
2574 if (!showit != !showing_tabline)
2575 {
2576 if (showit)
2577 ShowWindow(s_tabhwnd, SW_SHOW);
2578 else
2579 ShowWindow(s_tabhwnd, SW_HIDE);
2580 showing_tabline = showit;
2581 }
2582}
2583
2584/*
2585 * Return TRUE when tabline is displayed.
2586 */
2587 int
2588gui_mch_showing_tabline(void)
2589{
2590 return s_tabhwnd != NULL && showing_tabline;
2591}
2592
2593/*
2594 * Update the labels of the tabline.
2595 */
2596 void
2597gui_mch_update_tabline(void)
2598{
2599 tabpage_T *tp;
2600 TCITEM tie;
2601 int nr = 0;
2602 int curtabidx = 0;
2603 int tabadded = 0;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002604 WCHAR *wstr = NULL;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002605
2606 if (s_tabhwnd == NULL)
2607 return;
2608
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02002609 // Enable unicode support
2610 SendMessage(s_tabhwnd, CCM_SETUNICODEFORMAT, (WPARAM)TRUE, (LPARAM)0);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002611
2612 tie.mask = TCIF_TEXT;
2613 tie.iImage = -1;
2614
Bram Moolenaar734a8672019-12-02 22:49:38 +01002615 // Disable redraw for tab updates to eliminate O(N^2) draws.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002616 SendMessage(s_tabhwnd, WM_SETREDRAW, (WPARAM)FALSE, 0);
2617
Bram Moolenaar734a8672019-12-02 22:49:38 +01002618 // Add a label for each tab page. They all contain the same text area.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002619 for (tp = first_tabpage; tp != NULL; tp = tp->tp_next, ++nr)
2620 {
2621 if (tp == curtab)
2622 curtabidx = nr;
2623
2624 if (nr >= TabCtrl_GetItemCount(s_tabhwnd))
2625 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01002626 // Add the tab
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002627 tie.pszText = "-Empty-";
2628 TabCtrl_InsertItem(s_tabhwnd, nr, &tie);
2629 tabadded = 1;
2630 }
2631
2632 get_tabline_label(tp, FALSE);
2633 tie.pszText = (LPSTR)NameBuff;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002634
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02002635 wstr = enc_to_utf16(NameBuff, NULL);
2636 if (wstr != NULL)
2637 {
2638 TCITEMW tiw;
2639
2640 tiw.mask = TCIF_TEXT;
2641 tiw.iImage = -1;
2642 tiw.pszText = wstr;
2643 SendMessage(s_tabhwnd, TCM_SETITEMW, (WPARAM)nr, (LPARAM)&tiw);
2644 vim_free(wstr);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002645 }
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002646 }
2647
Bram Moolenaar734a8672019-12-02 22:49:38 +01002648 // Remove any old labels.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002649 while (nr < TabCtrl_GetItemCount(s_tabhwnd))
2650 TabCtrl_DeleteItem(s_tabhwnd, nr);
2651
2652 if (!tabadded && TabCtrl_GetCurSel(s_tabhwnd) != curtabidx)
2653 TabCtrl_SetCurSel(s_tabhwnd, curtabidx);
2654
Bram Moolenaar734a8672019-12-02 22:49:38 +01002655 // Re-enable redraw and redraw.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002656 SendMessage(s_tabhwnd, WM_SETREDRAW, (WPARAM)TRUE, 0);
2657 RedrawWindow(s_tabhwnd, NULL, NULL,
2658 RDW_ERASE | RDW_FRAME | RDW_INVALIDATE | RDW_ALLCHILDREN);
2659
2660 if (tabadded && TabCtrl_GetCurSel(s_tabhwnd) != curtabidx)
2661 TabCtrl_SetCurSel(s_tabhwnd, curtabidx);
2662}
2663
2664/*
2665 * Set the current tab to "nr". First tab is 1.
2666 */
2667 void
2668gui_mch_set_curtab(int nr)
2669{
2670 if (s_tabhwnd == NULL)
2671 return;
2672
2673 if (TabCtrl_GetCurSel(s_tabhwnd) != nr - 1)
2674 TabCtrl_SetCurSel(s_tabhwnd, nr - 1);
2675}
2676
2677#endif
2678
2679/*
2680 * ":simalt" command.
2681 */
2682 void
2683ex_simalt(exarg_T *eap)
2684{
Bram Moolenaar7a85b0f2017-04-22 15:17:40 +02002685 char_u *keys = eap->arg;
2686 int fill_typebuf = FALSE;
2687 char_u key_name[4];
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002688
2689 PostMessage(s_hwnd, WM_SYSCOMMAND, (WPARAM)SC_KEYMENU, (LPARAM)0);
2690 while (*keys)
2691 {
2692 if (*keys == '~')
Bram Moolenaar734a8672019-12-02 22:49:38 +01002693 *keys = ' '; // for showing system menu
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002694 PostMessage(s_hwnd, WM_CHAR, (WPARAM)*keys, (LPARAM)0);
2695 keys++;
Bram Moolenaar7a85b0f2017-04-22 15:17:40 +02002696 fill_typebuf = TRUE;
2697 }
2698 if (fill_typebuf)
2699 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01002700 // Put a NOP in the typeahead buffer so that the message will get
2701 // processed.
Bram Moolenaar7a85b0f2017-04-22 15:17:40 +02002702 key_name[0] = K_SPECIAL;
2703 key_name[1] = KS_EXTRA;
Bram Moolenaara21ccb72017-04-29 17:40:22 +02002704 key_name[2] = KE_NOP;
Bram Moolenaar7a85b0f2017-04-22 15:17:40 +02002705 key_name[3] = NUL;
Bram Moolenaar93bbf332019-10-23 21:43:16 +02002706#if defined(FEAT_CLIENTSERVER) || defined(FEAT_EVAL)
Bram Moolenaar7a85b0f2017-04-22 15:17:40 +02002707 typebuf_was_filled = TRUE;
Bram Moolenaar93bbf332019-10-23 21:43:16 +02002708#endif
Bram Moolenaar7a85b0f2017-04-22 15:17:40 +02002709 (void)ins_typebuf(key_name, REMAP_NONE, 0, TRUE, FALSE);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002710 }
2711}
2712
2713/*
2714 * Create the find & replace dialogs.
2715 * You can't have both at once: ":find" when replace is showing, destroys
2716 * the replace dialog first, and the other way around.
2717 */
2718#ifdef MSWIN_FIND_REPLACE
2719 static void
2720initialise_findrep(char_u *initial_string)
2721{
2722 int wword = FALSE;
2723 int mcase = !p_ic;
2724 char_u *entry_text;
2725
Bram Moolenaar734a8672019-12-02 22:49:38 +01002726 // Get the search string to use.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002727 entry_text = get_find_dialog_text(initial_string, &wword, &mcase);
2728
2729 s_findrep_struct.hwndOwner = s_hwnd;
2730 s_findrep_struct.Flags = FR_DOWN;
2731 if (mcase)
2732 s_findrep_struct.Flags |= FR_MATCHCASE;
2733 if (wword)
2734 s_findrep_struct.Flags |= FR_WHOLEWORD;
2735 if (entry_text != NULL && *entry_text != NUL)
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02002736 {
2737 WCHAR *p = enc_to_utf16(entry_text, NULL);
2738 if (p != NULL)
2739 {
2740 int len = s_findrep_struct.wFindWhatLen - 1;
2741
2742 wcsncpy(s_findrep_struct.lpstrFindWhat, p, len);
2743 s_findrep_struct.lpstrFindWhat[len] = NUL;
2744 vim_free(p);
2745 }
2746 }
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002747 vim_free(entry_text);
2748}
2749#endif
2750
2751 static void
2752set_window_title(HWND hwnd, char *title)
2753{
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02002754 if (title != NULL)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002755 {
2756 WCHAR *wbuf;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002757
Bram Moolenaar734a8672019-12-02 22:49:38 +01002758 // Convert the title from 'encoding' to UTF-16.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002759 wbuf = (WCHAR *)enc_to_utf16((char_u *)title, NULL);
2760 if (wbuf != NULL)
2761 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +02002762 SetWindowTextW(hwnd, wbuf);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002763 vim_free(wbuf);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002764 }
2765 }
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02002766 else
2767 (void)SetWindowTextW(hwnd, NULL);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002768}
2769
2770 void
2771gui_mch_find_dialog(exarg_T *eap)
2772{
2773#ifdef MSWIN_FIND_REPLACE
2774 if (s_findrep_msg != 0)
2775 {
2776 if (IsWindow(s_findrep_hwnd) && !s_findrep_is_find)
2777 DestroyWindow(s_findrep_hwnd);
2778
2779 if (!IsWindow(s_findrep_hwnd))
2780 {
2781 initialise_findrep(eap->arg);
K.Takata45f9cfb2022-01-21 11:11:00 +00002782 s_findrep_hwnd = FindTextW(&s_findrep_struct);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002783 }
2784
Bram Moolenaar9e42c862018-07-20 05:03:16 +02002785 set_window_title(s_findrep_hwnd, _("Find string"));
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002786 (void)SetFocus(s_findrep_hwnd);
2787
2788 s_findrep_is_find = TRUE;
2789 }
2790#endif
2791}
2792
2793
2794 void
2795gui_mch_replace_dialog(exarg_T *eap)
2796{
2797#ifdef MSWIN_FIND_REPLACE
2798 if (s_findrep_msg != 0)
2799 {
2800 if (IsWindow(s_findrep_hwnd) && s_findrep_is_find)
2801 DestroyWindow(s_findrep_hwnd);
2802
2803 if (!IsWindow(s_findrep_hwnd))
2804 {
2805 initialise_findrep(eap->arg);
K.Takata45f9cfb2022-01-21 11:11:00 +00002806 s_findrep_hwnd = ReplaceTextW(&s_findrep_struct);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002807 }
2808
Bram Moolenaar9e42c862018-07-20 05:03:16 +02002809 set_window_title(s_findrep_hwnd, _("Find & Replace"));
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002810 (void)SetFocus(s_findrep_hwnd);
2811
2812 s_findrep_is_find = FALSE;
2813 }
2814#endif
2815}
2816
2817
2818/*
2819 * Set visibility of the pointer.
2820 */
2821 void
2822gui_mch_mousehide(int hide)
2823{
2824 if (hide != gui.pointer_hidden)
2825 {
2826 ShowCursor(!hide);
2827 gui.pointer_hidden = hide;
2828 }
2829}
2830
2831#ifdef FEAT_MENU
2832 static void
2833gui_mch_show_popupmenu_at(vimmenu_T *menu, int x, int y)
2834{
Bram Moolenaar734a8672019-12-02 22:49:38 +01002835 // Unhide the mouse, we don't get move events here.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002836 gui_mch_mousehide(FALSE);
2837
2838 (void)TrackPopupMenu(
2839 (HMENU)menu->submenu_id,
2840 TPM_LEFTALIGN | TPM_LEFTBUTTON,
2841 x, y,
Bram Moolenaar734a8672019-12-02 22:49:38 +01002842 (int)0, //reserved param
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002843 s_hwnd,
2844 NULL);
2845 /*
2846 * NOTE: The pop-up menu can eat the mouse up event.
2847 * We deal with this in normal.c.
2848 */
2849}
2850#endif
2851
2852/*
2853 * Got a message when the system will go down.
2854 */
2855 static void
2856_OnEndSession(void)
2857{
2858 getout_preserve_modified(1);
2859}
2860
2861/*
2862 * Get this message when the user clicks on the cross in the top right corner
2863 * of a Windows95 window.
2864 */
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002865 static void
Bram Moolenaar1266d672017-02-01 13:43:36 +01002866_OnClose(HWND hwnd UNUSED)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002867{
2868 gui_shell_closed();
2869}
2870
2871/*
2872 * Get a message when the window is being destroyed.
2873 */
2874 static void
Bram Moolenaar1266d672017-02-01 13:43:36 +01002875_OnDestroy(HWND hwnd)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002876{
2877 if (!destroying)
2878 _OnClose(hwnd);
2879}
2880
2881 static void
2882_OnPaint(
2883 HWND hwnd)
2884{
2885 if (!IsMinimized(hwnd))
2886 {
2887 PAINTSTRUCT ps;
2888
Bram Moolenaar734a8672019-12-02 22:49:38 +01002889 out_flush(); // make sure all output has been processed
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002890 (void)BeginPaint(hwnd, &ps);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002891
Bram Moolenaar734a8672019-12-02 22:49:38 +01002892 // prevent multi-byte characters from misprinting on an invalid
2893 // rectangle
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002894 if (has_mbyte)
2895 {
2896 RECT rect;
2897
2898 GetClientRect(hwnd, &rect);
2899 ps.rcPaint.left = rect.left;
2900 ps.rcPaint.right = rect.right;
2901 }
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002902
2903 if (!IsRectEmpty(&ps.rcPaint))
2904 {
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002905 gui_redraw(ps.rcPaint.left, ps.rcPaint.top,
2906 ps.rcPaint.right - ps.rcPaint.left + 1,
2907 ps.rcPaint.bottom - ps.rcPaint.top + 1);
2908 }
2909
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002910 EndPaint(hwnd, &ps);
2911 }
2912}
2913
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002914 static void
2915_OnSize(
2916 HWND hwnd,
Bram Moolenaar1266d672017-02-01 13:43:36 +01002917 UINT state UNUSED,
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002918 int cx,
2919 int cy)
2920{
K.Takatac81e9bf2022-01-16 14:15:49 +00002921 if (!IsMinimized(hwnd) && !s_in_dpichanged)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002922 {
2923 gui_resize_shell(cx, cy);
2924
Bram Moolenaar734a8672019-12-02 22:49:38 +01002925 // Menu bar may wrap differently now
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002926 gui_mswin_get_menu_height(TRUE);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002927 }
2928}
2929
2930 static void
2931_OnSetFocus(
2932 HWND hwnd,
2933 HWND hwndOldFocus)
2934{
2935 gui_focus_change(TRUE);
2936 s_getting_focus = TRUE;
K.Takata4ac893f2022-01-20 12:44:28 +00002937 (void)DefWindowProcW(hwnd, WM_SETFOCUS, (WPARAM)hwndOldFocus, 0);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002938}
2939
2940 static void
2941_OnKillFocus(
2942 HWND hwnd,
2943 HWND hwndNewFocus)
2944{
Bram Moolenaar3c620b02022-02-24 11:39:43 +00002945 if (destroying)
2946 return;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002947 gui_focus_change(FALSE);
2948 s_getting_focus = FALSE;
K.Takata4ac893f2022-01-20 12:44:28 +00002949 (void)DefWindowProcW(hwnd, WM_KILLFOCUS, (WPARAM)hwndNewFocus, 0);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002950}
2951
2952/*
2953 * Get a message when the user switches back to vim
2954 */
2955 static LRESULT
2956_OnActivateApp(
2957 HWND hwnd,
2958 BOOL fActivate,
2959 DWORD dwThreadId)
2960{
Bram Moolenaar734a8672019-12-02 22:49:38 +01002961 // we call gui_focus_change() in _OnSetFocus()
2962 // gui_focus_change((int)fActivate);
K.Takata4ac893f2022-01-20 12:44:28 +00002963 return DefWindowProcW(hwnd, WM_ACTIVATEAPP, fActivate, (DWORD)dwThreadId);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002964}
2965
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002966 void
2967gui_mch_destroy_scrollbar(scrollbar_T *sb)
2968{
2969 DestroyWindow(sb->id);
2970}
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002971
2972/*
2973 * Get current mouse coordinates in text window.
2974 */
2975 void
2976gui_mch_getmouse(int *x, int *y)
2977{
2978 RECT rct;
2979 POINT mp;
2980
2981 (void)GetWindowRect(s_textArea, &rct);
K.Takata45f9cfb2022-01-21 11:11:00 +00002982 (void)GetCursorPos(&mp);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002983 *x = (int)(mp.x - rct.left);
2984 *y = (int)(mp.y - rct.top);
2985}
2986
2987/*
2988 * Move mouse pointer to character at (x, y).
2989 */
2990 void
2991gui_mch_setmouse(int x, int y)
2992{
2993 RECT rct;
2994
2995 (void)GetWindowRect(s_textArea, &rct);
2996 (void)SetCursorPos(x + gui.border_offset + rct.left,
2997 y + gui.border_offset + rct.top);
2998}
2999
3000 static void
3001gui_mswin_get_valid_dimensions(
3002 int w,
3003 int h,
3004 int *valid_w,
K.Takata4f2417f2021-06-05 16:25:32 +02003005 int *valid_h,
3006 int *cols,
3007 int *rows)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003008{
3009 int base_width, base_height;
3010
3011 base_width = gui_get_base_width()
K.Takatac81e9bf2022-01-16 14:15:49 +00003012 + (pGetSystemMetricsForDpi(SM_CXFRAME, s_dpi) +
3013 pGetSystemMetricsForDpi(SM_CXPADDEDBORDER, s_dpi)) * 2;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003014 base_height = gui_get_base_height()
K.Takatac81e9bf2022-01-16 14:15:49 +00003015 + (pGetSystemMetricsForDpi(SM_CYFRAME, s_dpi) +
3016 pGetSystemMetricsForDpi(SM_CXPADDEDBORDER, s_dpi)) * 2
3017 + pGetSystemMetricsForDpi(SM_CYCAPTION, s_dpi)
3018 + gui_mswin_get_menu_height(FALSE);
K.Takata4f2417f2021-06-05 16:25:32 +02003019 *cols = (w - base_width) / gui.char_width;
3020 *rows = (h - base_height) / gui.char_height;
3021 *valid_w = base_width + *cols * gui.char_width;
3022 *valid_h = base_height + *rows * gui.char_height;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003023}
3024
3025 void
3026gui_mch_flash(int msec)
3027{
3028 RECT rc;
3029
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01003030#if defined(FEAT_DIRECTX)
3031 if (IS_ENABLE_DIRECTX())
3032 DWriteContext_Flush(s_dwc);
3033#endif
3034
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003035 /*
3036 * Note: InvertRect() excludes right and bottom of rectangle.
3037 */
3038 rc.left = 0;
3039 rc.top = 0;
3040 rc.right = gui.num_cols * gui.char_width;
3041 rc.bottom = gui.num_rows * gui.char_height;
3042 InvertRect(s_hdc, &rc);
Bram Moolenaar734a8672019-12-02 22:49:38 +01003043 gui_mch_flush(); // make sure it's displayed
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003044
Bram Moolenaar734a8672019-12-02 22:49:38 +01003045 ui_delay((long)msec, TRUE); // wait for a few msec
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003046
3047 InvertRect(s_hdc, &rc);
3048}
3049
3050/*
Bram Moolenaar185577e2020-10-29 20:08:21 +01003051 * Check if the specified point is on-screen. (multi-monitor aware)
3052 */
3053 static BOOL
3054is_point_onscreen(int x, int y)
3055{
3056 POINT pt = {x, y};
3057
3058 return MonitorFromPoint(pt, MONITOR_DEFAULTTONULL) != NULL;
3059}
3060
3061/*
Bram Moolenaarf01af9c2022-03-01 16:02:26 +00003062 * Check if the whole client area of the specified window is on-screen.
Bram Moolenaar185577e2020-10-29 20:08:21 +01003063 *
3064 * Note about DirectX: Windows 10 1809 or above no longer maintains image of
3065 * the window portion that is off-screen. Scrolling by DWriteContext_Scroll()
3066 * only works when the whole window is on-screen.
3067 */
3068 static BOOL
3069is_window_onscreen(HWND hwnd)
3070{
3071 RECT rc;
Bram Moolenaarf01af9c2022-03-01 16:02:26 +00003072 POINT p1, p2;
Bram Moolenaar185577e2020-10-29 20:08:21 +01003073
Bram Moolenaarf01af9c2022-03-01 16:02:26 +00003074 GetClientRect(hwnd, &rc);
3075 p1.x = rc.left;
3076 p1.y = rc.top;
3077 p2.x = rc.right - 1;
3078 p2.y = rc.bottom - 1;
3079 ClientToScreen(hwnd, &p1);
3080 ClientToScreen(hwnd, &p2);
Bram Moolenaar185577e2020-10-29 20:08:21 +01003081
Bram Moolenaarf01af9c2022-03-01 16:02:26 +00003082 if (!is_point_onscreen(p1.x, p1.y))
Bram Moolenaar185577e2020-10-29 20:08:21 +01003083 return FALSE;
Bram Moolenaarf01af9c2022-03-01 16:02:26 +00003084 if (!is_point_onscreen(p1.x, p2.y))
Bram Moolenaar185577e2020-10-29 20:08:21 +01003085 return FALSE;
Bram Moolenaarf01af9c2022-03-01 16:02:26 +00003086 if (!is_point_onscreen(p2.x, p1.y))
Bram Moolenaar185577e2020-10-29 20:08:21 +01003087 return FALSE;
Bram Moolenaarf01af9c2022-03-01 16:02:26 +00003088 if (!is_point_onscreen(p2.x, p2.y))
Bram Moolenaar185577e2020-10-29 20:08:21 +01003089 return FALSE;
3090 return TRUE;
3091}
3092
3093/*
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003094 * Return flags used for scrolling.
3095 * The SW_INVALIDATE is required when part of the window is covered or
3096 * off-screen. Refer to MS KB Q75236.
3097 */
3098 static int
3099get_scroll_flags(void)
3100{
3101 HWND hwnd;
3102 RECT rcVim, rcOther, rcDest;
3103
Bram Moolenaar185577e2020-10-29 20:08:21 +01003104 // Check if the window is (partly) off-screen.
3105 if (!is_window_onscreen(s_hwnd))
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003106 return SW_INVALIDATE;
3107
Dominique Pelleaf4a61a2021-12-27 17:21:41 +00003108 // Check if there is a window (partly) on top of us.
Bram Moolenaar185577e2020-10-29 20:08:21 +01003109 GetWindowRect(s_hwnd, &rcVim);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003110 for (hwnd = s_hwnd; (hwnd = GetWindow(hwnd, GW_HWNDPREV)) != (HWND)0; )
3111 if (IsWindowVisible(hwnd))
3112 {
3113 GetWindowRect(hwnd, &rcOther);
3114 if (IntersectRect(&rcDest, &rcVim, &rcOther))
3115 return SW_INVALIDATE;
3116 }
3117 return 0;
3118}
3119
3120/*
3121 * On some Intel GPUs, the regions drawn just prior to ScrollWindowEx()
3122 * may not be scrolled out properly.
3123 * For gVim, when _OnScroll() is repeated, the character at the
3124 * previous cursor position may be left drawn after scroll.
3125 * The problem can be avoided by calling GetPixel() to get a pixel in
3126 * the region before ScrollWindowEx().
3127 */
3128 static void
3129intel_gpu_workaround(void)
3130{
3131 GetPixel(s_hdc, FILL_X(gui.col), FILL_Y(gui.row));
3132}
3133
3134/*
3135 * Delete the given number of lines from the given row, scrolling up any
3136 * text further down within the scroll region.
3137 */
3138 void
3139gui_mch_delete_lines(
3140 int row,
3141 int num_lines)
3142{
3143 RECT rc;
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01003144
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003145 rc.left = FILL_X(gui.scroll_region_left);
3146 rc.right = FILL_X(gui.scroll_region_right + 1);
3147 rc.top = FILL_Y(row);
3148 rc.bottom = FILL_Y(gui.scroll_region_bot + 1);
3149
Bram Moolenaar92467d32017-12-05 13:22:16 +01003150#if defined(FEAT_DIRECTX)
Bram Moolenaar185577e2020-10-29 20:08:21 +01003151 if (IS_ENABLE_DIRECTX() && is_window_onscreen(s_hwnd))
Bram Moolenaar92467d32017-12-05 13:22:16 +01003152 {
Bram Moolenaara338adc2018-01-31 20:51:47 +01003153 DWriteContext_Scroll(s_dwc, 0, -num_lines * gui.char_height, &rc);
Bram Moolenaar92467d32017-12-05 13:22:16 +01003154 }
Bram Moolenaara338adc2018-01-31 20:51:47 +01003155 else
Bram Moolenaar92467d32017-12-05 13:22:16 +01003156#endif
3157 {
Bram Moolenaar185577e2020-10-29 20:08:21 +01003158#if defined(FEAT_DIRECTX)
3159 if (IS_ENABLE_DIRECTX())
3160 DWriteContext_Flush(s_dwc);
3161#endif
Bram Moolenaar92467d32017-12-05 13:22:16 +01003162 intel_gpu_workaround();
3163 ScrollWindowEx(s_textArea, 0, -num_lines * gui.char_height,
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003164 &rc, &rc, NULL, NULL, get_scroll_flags());
Bram Moolenaar7f88b652017-12-14 13:15:19 +01003165 UpdateWindow(s_textArea);
Bram Moolenaar92467d32017-12-05 13:22:16 +01003166 }
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003167
Bram Moolenaar734a8672019-12-02 22:49:38 +01003168 // This seems to be required to avoid the cursor disappearing when
3169 // scrolling such that the cursor ends up in the top-left character on
3170 // the screen... But why? (Webb)
3171 // It's probably fixed by disabling drawing the cursor while scrolling.
3172 // gui.cursor_is_valid = FALSE;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003173
3174 gui_clear_block(gui.scroll_region_bot - num_lines + 1,
3175 gui.scroll_region_left,
3176 gui.scroll_region_bot, gui.scroll_region_right);
3177}
3178
3179/*
3180 * Insert the given number of lines before the given row, scrolling down any
3181 * following text within the scroll region.
3182 */
3183 void
3184gui_mch_insert_lines(
3185 int row,
3186 int num_lines)
3187{
3188 RECT rc;
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01003189
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003190 rc.left = FILL_X(gui.scroll_region_left);
3191 rc.right = FILL_X(gui.scroll_region_right + 1);
3192 rc.top = FILL_Y(row);
3193 rc.bottom = FILL_Y(gui.scroll_region_bot + 1);
Bram Moolenaar92467d32017-12-05 13:22:16 +01003194
3195#if defined(FEAT_DIRECTX)
Bram Moolenaar185577e2020-10-29 20:08:21 +01003196 if (IS_ENABLE_DIRECTX() && is_window_onscreen(s_hwnd))
Bram Moolenaar92467d32017-12-05 13:22:16 +01003197 {
Bram Moolenaara338adc2018-01-31 20:51:47 +01003198 DWriteContext_Scroll(s_dwc, 0, num_lines * gui.char_height, &rc);
Bram Moolenaar92467d32017-12-05 13:22:16 +01003199 }
Bram Moolenaara338adc2018-01-31 20:51:47 +01003200 else
Bram Moolenaar92467d32017-12-05 13:22:16 +01003201#endif
3202 {
Bram Moolenaar185577e2020-10-29 20:08:21 +01003203#if defined(FEAT_DIRECTX)
3204 if (IS_ENABLE_DIRECTX())
3205 DWriteContext_Flush(s_dwc);
3206#endif
Bram Moolenaar92467d32017-12-05 13:22:16 +01003207 intel_gpu_workaround();
Bram Moolenaar734a8672019-12-02 22:49:38 +01003208 // The SW_INVALIDATE is required when part of the window is covered or
3209 // off-screen. How do we avoid it when it's not needed?
Bram Moolenaar92467d32017-12-05 13:22:16 +01003210 ScrollWindowEx(s_textArea, 0, num_lines * gui.char_height,
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003211 &rc, &rc, NULL, NULL, get_scroll_flags());
Bram Moolenaar7f88b652017-12-14 13:15:19 +01003212 UpdateWindow(s_textArea);
Bram Moolenaar92467d32017-12-05 13:22:16 +01003213 }
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003214
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003215 gui_clear_block(row, gui.scroll_region_left,
3216 row + num_lines - 1, gui.scroll_region_right);
3217}
3218
3219
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003220 void
Bram Moolenaar1266d672017-02-01 13:43:36 +01003221gui_mch_exit(int rc UNUSED)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003222{
3223#if defined(FEAT_DIRECTX)
3224 DWriteContext_Close(s_dwc);
3225 DWrite_Final();
3226 s_dwc = NULL;
3227#endif
3228
3229 ReleaseDC(s_textArea, s_hdc);
3230 DeleteObject(s_brush);
3231
3232#ifdef FEAT_TEAROFF
Bram Moolenaar734a8672019-12-02 22:49:38 +01003233 // Unload the tearoff bitmap
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003234 (void)DeleteObject((HGDIOBJ)s_htearbitmap);
3235#endif
3236
Bram Moolenaar734a8672019-12-02 22:49:38 +01003237 // Destroy our window (if we have one).
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003238 if (s_hwnd != NULL)
3239 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01003240 destroying = TRUE; // ignore WM_DESTROY message now
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003241 DestroyWindow(s_hwnd);
3242 }
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003243}
3244
3245 static char_u *
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01003246logfont2name(LOGFONTW lf)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003247{
3248 char *p;
3249 char *res;
3250 char *charset_name;
Bram Moolenaar7c1c6db2016-04-03 22:08:05 +02003251 char *quality_name;
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01003252 char *font_name;
Bram Moolenaarf720d0a2019-04-28 14:02:47 +02003253 int points;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003254
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01003255 font_name = (char *)utf16_to_enc(lf.lfFaceName, NULL);
3256 if (font_name == NULL)
3257 return NULL;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003258 charset_name = charset_id2name((int)lf.lfCharSet);
Bram Moolenaar7c1c6db2016-04-03 22:08:05 +02003259 quality_name = quality_id2name((int)lf.lfQuality);
3260
Bram Moolenaarc799fe22019-05-28 23:08:19 +02003261 res = alloc(strlen(font_name) + 30
Bram Moolenaar2155a6a2019-04-27 19:15:45 +02003262 + (charset_name == NULL ? 0 : strlen(charset_name) + 2)
Bram Moolenaar964b3742019-05-24 18:54:09 +02003263 + (quality_name == NULL ? 0 : strlen(quality_name) + 2));
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003264 if (res != NULL)
3265 {
3266 p = res;
Bram Moolenaarf720d0a2019-04-28 14:02:47 +02003267 // make a normal font string out of the lf thing:
3268 points = pixels_to_points(
3269 lf.lfHeight < 0 ? -lf.lfHeight : lf.lfHeight, TRUE);
3270 if (lf.lfWeight == FW_NORMAL || lf.lfWeight == FW_BOLD)
3271 sprintf((char *)p, "%s:h%d", font_name, points);
3272 else
Bram Moolenaara0e67fc2019-04-29 21:46:26 +02003273 sprintf((char *)p, "%s:h%d:W%ld", font_name, points, lf.lfWeight);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003274 while (*p)
3275 {
3276 if (*p == ' ')
3277 *p = '_';
3278 ++p;
3279 }
3280 if (lf.lfItalic)
3281 STRCAT(p, ":i");
Bram Moolenaarf720d0a2019-04-28 14:02:47 +02003282 if (lf.lfWeight == FW_BOLD)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003283 STRCAT(p, ":b");
3284 if (lf.lfUnderline)
3285 STRCAT(p, ":u");
3286 if (lf.lfStrikeOut)
3287 STRCAT(p, ":s");
3288 if (charset_name != NULL)
3289 {
3290 STRCAT(p, ":c");
3291 STRCAT(p, charset_name);
3292 }
Bram Moolenaar7c1c6db2016-04-03 22:08:05 +02003293 if (quality_name != NULL)
3294 {
3295 STRCAT(p, ":q");
3296 STRCAT(p, quality_name);
3297 }
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003298 }
3299
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01003300 vim_free(font_name);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003301 return (char_u *)res;
3302}
3303
3304
3305#ifdef FEAT_MBYTE_IME
3306/*
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01003307 * Set correct LOGFONTW to IME. Use 'guifontwide' if available, otherwise use
K.Takatac81e9bf2022-01-16 14:15:49 +00003308 * 'guifont'.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003309 */
3310 static void
3311update_im_font(void)
3312{
K.Takatac81e9bf2022-01-16 14:15:49 +00003313 LOGFONTW lf_wide, lf;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003314
3315 if (p_guifontwide != NULL && *p_guifontwide != NUL
3316 && gui.wide_font != NOFONT
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01003317 && GetObjectW((HFONT)gui.wide_font, sizeof(lf_wide), &lf_wide))
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003318 norm_logfont = lf_wide;
3319 else
3320 norm_logfont = sub_logfont;
K.Takatac81e9bf2022-01-16 14:15:49 +00003321
3322 lf = norm_logfont;
3323 if (s_process_dpi_aware == DPI_AWARENESS_UNAWARE)
3324 // Work around when PerMonitorV2 is not enabled in the process level.
3325 lf.lfHeight = lf.lfHeight * DEFAULT_DPI / s_dpi;
3326 im_set_font(&lf);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003327}
3328#endif
3329
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003330/*
3331 * Handler of gui.wide_font (p_guifontwide) changed notification.
3332 */
3333 void
3334gui_mch_wide_font_changed(void)
3335{
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01003336 LOGFONTW lf;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003337
Bram Moolenaarfc3abf42019-01-24 15:54:21 +01003338#ifdef FEAT_MBYTE_IME
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003339 update_im_font();
Bram Moolenaarfc3abf42019-01-24 15:54:21 +01003340#endif
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003341
3342 gui_mch_free_font(gui.wide_ital_font);
3343 gui.wide_ital_font = NOFONT;
3344 gui_mch_free_font(gui.wide_bold_font);
3345 gui.wide_bold_font = NOFONT;
3346 gui_mch_free_font(gui.wide_boldital_font);
3347 gui.wide_boldital_font = NOFONT;
3348
3349 if (gui.wide_font
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01003350 && GetObjectW((HFONT)gui.wide_font, sizeof(lf), &lf))
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003351 {
3352 if (!lf.lfItalic)
3353 {
3354 lf.lfItalic = TRUE;
3355 gui.wide_ital_font = get_font_handle(&lf);
3356 lf.lfItalic = FALSE;
3357 }
3358 if (lf.lfWeight < FW_BOLD)
3359 {
3360 lf.lfWeight = FW_BOLD;
3361 gui.wide_bold_font = get_font_handle(&lf);
3362 if (!lf.lfItalic)
3363 {
3364 lf.lfItalic = TRUE;
3365 gui.wide_boldital_font = get_font_handle(&lf);
3366 }
3367 }
3368 }
3369}
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003370
3371/*
3372 * Initialise vim to use the font with the given name.
3373 * Return FAIL if the font could not be loaded, OK otherwise.
3374 */
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003375 int
Bram Moolenaar1266d672017-02-01 13:43:36 +01003376gui_mch_init_font(char_u *font_name, int fontset UNUSED)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003377{
K.Takatac81e9bf2022-01-16 14:15:49 +00003378 LOGFONTW lf, lfOrig;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003379 GuiFont font = NOFONT;
3380 char_u *p;
3381
Bram Moolenaar734a8672019-12-02 22:49:38 +01003382 // Load the font
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003383 if (get_logfont(&lf, font_name, NULL, TRUE) == OK)
K.Takatac81e9bf2022-01-16 14:15:49 +00003384 {
3385 lfOrig = lf;
3386 lf.lfHeight = adjust_fontsize_by_dpi(lf.lfHeight);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003387 font = get_font_handle(&lf);
K.Takatac81e9bf2022-01-16 14:15:49 +00003388 }
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003389 if (font == NOFONT)
3390 return FAIL;
3391
3392 if (font_name == NULL)
K.Takata45f9cfb2022-01-21 11:11:00 +00003393 font_name = (char_u *)"";
K.Takata4ac893f2022-01-20 12:44:28 +00003394#ifdef FEAT_MBYTE_IME
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003395 norm_logfont = lf;
3396 sub_logfont = lf;
K.Takatac81e9bf2022-01-16 14:15:49 +00003397 if (!s_in_dpichanged)
3398 update_im_font();
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003399#endif
3400 gui_mch_free_font(gui.norm_font);
3401 gui.norm_font = font;
K.Takatac81e9bf2022-01-16 14:15:49 +00003402 current_font_height = lfOrig.lfHeight;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003403 GetFontSize(font);
3404
K.Takatac81e9bf2022-01-16 14:15:49 +00003405 p = logfont2name(lfOrig);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003406 if (p != NULL)
3407 {
3408 hl_set_font_name(p);
3409
Bram Moolenaar734a8672019-12-02 22:49:38 +01003410 // When setting 'guifont' to "*" replace it with the actual font name.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003411 if (STRCMP(font_name, "*") == 0 && STRCMP(p_guifont, "*") == 0)
3412 {
3413 vim_free(p_guifont);
3414 p_guifont = p;
3415 }
3416 else
3417 vim_free(p);
3418 }
3419
3420 gui_mch_free_font(gui.ital_font);
3421 gui.ital_font = NOFONT;
3422 gui_mch_free_font(gui.bold_font);
3423 gui.bold_font = NOFONT;
3424 gui_mch_free_font(gui.boldital_font);
3425 gui.boldital_font = NOFONT;
3426
3427 if (!lf.lfItalic)
3428 {
3429 lf.lfItalic = TRUE;
3430 gui.ital_font = get_font_handle(&lf);
3431 lf.lfItalic = FALSE;
3432 }
3433 if (lf.lfWeight < FW_BOLD)
3434 {
3435 lf.lfWeight = FW_BOLD;
3436 gui.bold_font = get_font_handle(&lf);
3437 if (!lf.lfItalic)
3438 {
3439 lf.lfItalic = TRUE;
3440 gui.boldital_font = get_font_handle(&lf);
3441 }
3442 }
3443
3444 return OK;
3445}
3446
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003447/*
3448 * Return TRUE if the GUI window is maximized, filling the whole screen.
Bram Moolenaarb68ced52020-07-17 22:26:53 +02003449 * Also return TRUE if the window is snapped.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003450 */
3451 int
3452gui_mch_maximized(void)
3453{
3454 WINDOWPLACEMENT wp;
Bram Moolenaarb68ced52020-07-17 22:26:53 +02003455 RECT rc;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003456
3457 wp.length = sizeof(WINDOWPLACEMENT);
3458 if (GetWindowPlacement(s_hwnd, &wp))
Bram Moolenaarb68ced52020-07-17 22:26:53 +02003459 {
3460 if (wp.showCmd == SW_SHOWMAXIMIZED
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003461 || (wp.showCmd == SW_SHOWMINIMIZED
Bram Moolenaarb68ced52020-07-17 22:26:53 +02003462 && wp.flags == WPF_RESTORETOMAXIMIZED))
3463 return TRUE;
3464 if (wp.showCmd == SW_SHOWMINIMIZED)
3465 return FALSE;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003466
Bram Moolenaarb68ced52020-07-17 22:26:53 +02003467 // Assume the window is snapped when the sizes from two APIs differ.
3468 GetWindowRect(s_hwnd, &rc);
3469 if ((rc.right - rc.left !=
3470 wp.rcNormalPosition.right - wp.rcNormalPosition.left)
3471 || (rc.bottom - rc.top !=
3472 wp.rcNormalPosition.bottom - wp.rcNormalPosition.top))
3473 return TRUE;
3474 }
3475 return FALSE;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003476}
3477
3478/*
Bram Moolenaar8ac44152017-11-09 18:33:29 +01003479 * Called when the font changed while the window is maximized or GO_KEEPWINSIZE
3480 * is set. Compute the new Rows and Columns. This is like resizing the
3481 * window.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003482 */
3483 void
3484gui_mch_newfont(void)
3485{
3486 RECT rect;
3487
3488 GetWindowRect(s_hwnd, &rect);
3489 if (win_socket_id == 0)
3490 {
3491 gui_resize_shell(rect.right - rect.left
K.Takatac81e9bf2022-01-16 14:15:49 +00003492 - (pGetSystemMetricsForDpi(SM_CXFRAME, s_dpi) +
3493 pGetSystemMetricsForDpi(SM_CXPADDEDBORDER, s_dpi)) * 2,
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003494 rect.bottom - rect.top
K.Takatac81e9bf2022-01-16 14:15:49 +00003495 - (pGetSystemMetricsForDpi(SM_CYFRAME, s_dpi) +
3496 pGetSystemMetricsForDpi(SM_CXPADDEDBORDER, s_dpi)) * 2
3497 - pGetSystemMetricsForDpi(SM_CYCAPTION, s_dpi)
3498 - gui_mswin_get_menu_height(FALSE));
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003499 }
3500 else
3501 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01003502 // Inside another window, don't use the frame and border.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003503 gui_resize_shell(rect.right - rect.left,
K.Takatac81e9bf2022-01-16 14:15:49 +00003504 rect.bottom - rect.top - gui_mswin_get_menu_height(FALSE));
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003505 }
3506}
3507
3508/*
3509 * Set the window title
3510 */
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003511 void
3512gui_mch_settitle(
3513 char_u *title,
Bram Moolenaar1266d672017-02-01 13:43:36 +01003514 char_u *icon UNUSED)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003515{
3516 set_window_title(s_hwnd, (title == NULL ? "VIM" : (char *)title));
3517}
3518
Bram Moolenaara6b7a082016-08-10 20:53:05 +02003519#if defined(FEAT_MOUSESHAPE) || defined(PROTO)
Bram Moolenaar734a8672019-12-02 22:49:38 +01003520// Table for shape IDCs. Keep in sync with the mshape_names[] table in
3521// misc2.c!
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003522static LPCSTR mshape_idcs[] =
3523{
Bram Moolenaar734a8672019-12-02 22:49:38 +01003524 IDC_ARROW, // arrow
3525 MAKEINTRESOURCE(0), // blank
3526 IDC_IBEAM, // beam
3527 IDC_SIZENS, // updown
3528 IDC_SIZENS, // udsizing
3529 IDC_SIZEWE, // leftright
3530 IDC_SIZEWE, // lrsizing
3531 IDC_WAIT, // busy
3532 IDC_NO, // no
3533 IDC_ARROW, // crosshair
3534 IDC_ARROW, // hand1
3535 IDC_ARROW, // hand2
3536 IDC_ARROW, // pencil
3537 IDC_ARROW, // question
3538 IDC_ARROW, // right-arrow
3539 IDC_UPARROW, // up-arrow
3540 IDC_ARROW // last one
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003541};
3542
3543 void
3544mch_set_mouse_shape(int shape)
3545{
3546 LPCSTR idc;
3547
3548 if (shape == MSHAPE_HIDE)
3549 ShowCursor(FALSE);
3550 else
3551 {
3552 if (shape >= MSHAPE_NUMBERED)
3553 idc = IDC_ARROW;
3554 else
3555 idc = mshape_idcs[shape];
Bram Moolenaara0754902019-11-19 23:01:28 +01003556 SetClassLongPtr(s_textArea, GCLP_HCURSOR, (LONG_PTR)LoadCursor(NULL, idc));
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003557 if (!p_mh)
3558 {
3559 POINT mp;
3560
Bram Moolenaar734a8672019-12-02 22:49:38 +01003561 // Set the position to make it redrawn with the new shape.
K.Takata45f9cfb2022-01-21 11:11:00 +00003562 (void)GetCursorPos(&mp);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003563 (void)SetCursorPos(mp.x, mp.y);
3564 ShowCursor(TRUE);
3565 }
3566 }
3567}
3568#endif
3569
Bram Moolenaara6b7a082016-08-10 20:53:05 +02003570#if defined(FEAT_BROWSE) || defined(PROTO)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003571/*
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003572 * Wide version of convert_filter().
3573 */
3574 static WCHAR *
3575convert_filterW(char_u *s)
3576{
3577 char_u *tmp;
3578 int len;
3579 WCHAR *res;
3580
3581 tmp = convert_filter(s);
3582 if (tmp == NULL)
3583 return NULL;
3584 len = (int)STRLEN(s) + 3;
3585 res = enc_to_utf16(tmp, &len);
3586 vim_free(tmp);
3587 return res;
3588}
3589
3590/*
Bram Moolenaarfc3abf42019-01-24 15:54:21 +01003591 * Pop open a file browser and return the file selected, in allocated memory,
3592 * or NULL if Cancel is hit.
3593 * saving - TRUE if the file will be saved to, FALSE if it will be opened.
3594 * title - Title message for the file browser dialog.
3595 * dflt - Default name of file.
3596 * ext - Default extension to be added to files without extensions.
3597 * initdir - directory in which to open the browser (NULL = current dir)
3598 * filter - Filter for matched files to choose from.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003599 */
Bram Moolenaar091806d2019-01-24 16:27:46 +01003600 char_u *
Bram Moolenaarfc3abf42019-01-24 15:54:21 +01003601gui_mch_browse(
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003602 int saving,
3603 char_u *title,
3604 char_u *dflt,
3605 char_u *ext,
3606 char_u *initdir,
3607 char_u *filter)
3608{
Bram Moolenaar734a8672019-12-02 22:49:38 +01003609 // We always use the wide function. This means enc_to_utf16() must work,
3610 // otherwise it fails miserably!
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003611 OPENFILENAMEW fileStruct;
3612 WCHAR fileBuf[MAXPATHL];
3613 WCHAR *wp;
3614 int i;
3615 WCHAR *titlep = NULL;
3616 WCHAR *extp = NULL;
3617 WCHAR *initdirp = NULL;
3618 WCHAR *filterp;
Bram Moolenaar7ff8a3c2018-09-22 14:39:15 +02003619 char_u *p, *q;
K.Takata14b8d6a2022-01-20 15:05:22 +00003620 BOOL ret;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003621
3622 if (dflt == NULL)
3623 fileBuf[0] = NUL;
3624 else
3625 {
3626 wp = enc_to_utf16(dflt, NULL);
3627 if (wp == NULL)
3628 fileBuf[0] = NUL;
3629 else
3630 {
3631 for (i = 0; wp[i] != NUL && i < MAXPATHL - 1; ++i)
3632 fileBuf[i] = wp[i];
3633 fileBuf[i] = NUL;
3634 vim_free(wp);
3635 }
3636 }
3637
Bram Moolenaar734a8672019-12-02 22:49:38 +01003638 // Convert the filter to Windows format.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003639 filterp = convert_filterW(filter);
3640
Bram Moolenaara80faa82020-04-12 19:37:17 +02003641 CLEAR_FIELD(fileStruct);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01003642# ifdef OPENFILENAME_SIZE_VERSION_400W
Bram Moolenaar734a8672019-12-02 22:49:38 +01003643 // be compatible with Windows NT 4.0
Bram Moolenaar89e375a2016-03-15 18:09:57 +01003644 fileStruct.lStructSize = OPENFILENAME_SIZE_VERSION_400W;
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01003645# else
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003646 fileStruct.lStructSize = sizeof(fileStruct);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01003647# endif
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003648
3649 if (title != NULL)
3650 titlep = enc_to_utf16(title, NULL);
3651 fileStruct.lpstrTitle = titlep;
3652
3653 if (ext != NULL)
3654 extp = enc_to_utf16(ext, NULL);
3655 fileStruct.lpstrDefExt = extp;
3656
3657 fileStruct.lpstrFile = fileBuf;
3658 fileStruct.nMaxFile = MAXPATHL;
3659 fileStruct.lpstrFilter = filterp;
Bram Moolenaar734a8672019-12-02 22:49:38 +01003660 fileStruct.hwndOwner = s_hwnd; // main Vim window is owner
3661 // has an initial dir been specified?
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003662 if (initdir != NULL && *initdir != NUL)
3663 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01003664 // Must have backslashes here, no matter what 'shellslash' says
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003665 initdirp = enc_to_utf16(initdir, NULL);
3666 if (initdirp != NULL)
3667 {
3668 for (wp = initdirp; *wp != NUL; ++wp)
3669 if (*wp == '/')
3670 *wp = '\\';
3671 }
3672 fileStruct.lpstrInitialDir = initdirp;
3673 }
3674
3675 /*
3676 * TODO: Allow selection of multiple files. Needs another arg to this
3677 * function to ask for it, and need to use OFN_ALLOWMULTISELECT below.
3678 * Also, should we use OFN_FILEMUSTEXIST when opening? Vim can edit on
3679 * files that don't exist yet, so I haven't put it in. What about
3680 * OFN_PATHMUSTEXIST?
3681 * Don't use OFN_OVERWRITEPROMPT, Vim has its own ":confirm" dialog.
3682 */
3683 fileStruct.Flags = (OFN_NOCHANGEDIR | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01003684# ifdef FEAT_SHORTCUT
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003685 if (curbuf->b_p_bin)
3686 fileStruct.Flags |= OFN_NODEREFERENCELINKS;
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01003687# endif
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003688 if (saving)
K.Takata14b8d6a2022-01-20 15:05:22 +00003689 ret = GetSaveFileNameW(&fileStruct);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003690 else
K.Takata14b8d6a2022-01-20 15:05:22 +00003691 ret = GetOpenFileNameW(&fileStruct);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003692
3693 vim_free(filterp);
3694 vim_free(initdirp);
3695 vim_free(titlep);
3696 vim_free(extp);
3697
K.Takata14b8d6a2022-01-20 15:05:22 +00003698 if (!ret)
3699 return NULL;
3700
3701 // Convert from UTF-16 to 'encoding'.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003702 p = utf16_to_enc(fileBuf, NULL);
Bram Moolenaar7ff8a3c2018-09-22 14:39:15 +02003703 if (p == NULL)
3704 return NULL;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003705
Bram Moolenaar734a8672019-12-02 22:49:38 +01003706 // Give focus back to main window (when using MDI).
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003707 SetFocus(s_hwnd);
3708
Bram Moolenaar734a8672019-12-02 22:49:38 +01003709 // Shorten the file name if possible
Bram Moolenaar7ff8a3c2018-09-22 14:39:15 +02003710 q = vim_strsave(shorten_fname1(p));
3711 vim_free(p);
3712 return q;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003713}
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003714
3715
3716/*
3717 * Convert the string s to the proper format for a filter string by replacing
3718 * the \t and \n delimiters with \0.
3719 * Returns the converted string in allocated memory.
3720 *
3721 * Keep in sync with convert_filterW() above!
3722 */
3723 static char_u *
3724convert_filter(char_u *s)
3725{
3726 char_u *res;
3727 unsigned s_len = (unsigned)STRLEN(s);
3728 unsigned i;
3729
3730 res = alloc(s_len + 3);
3731 if (res != NULL)
3732 {
3733 for (i = 0; i < s_len; ++i)
3734 if (s[i] == '\t' || s[i] == '\n')
3735 res[i] = '\0';
3736 else
3737 res[i] = s[i];
3738 res[s_len] = NUL;
Bram Moolenaar734a8672019-12-02 22:49:38 +01003739 // Add two extra NULs to make sure it's properly terminated.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003740 res[s_len + 1] = NUL;
3741 res[s_len + 2] = NUL;
3742 }
3743 return res;
3744}
3745
3746/*
3747 * Select a directory.
3748 */
3749 char_u *
3750gui_mch_browsedir(char_u *title, char_u *initdir)
3751{
Bram Moolenaar734a8672019-12-02 22:49:38 +01003752 // We fake this: Use a filter that doesn't select anything and a default
3753 // file name that won't be used.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003754 return gui_mch_browse(0, title, (char_u *)_("Not Used"), NULL,
3755 initdir, (char_u *)_("Directory\t*.nothing\n"));
3756}
Bram Moolenaar734a8672019-12-02 22:49:38 +01003757#endif // FEAT_BROWSE
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003758
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003759 static void
3760_OnDropFiles(
Bram Moolenaar1266d672017-02-01 13:43:36 +01003761 HWND hwnd UNUSED,
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003762 HDROP hDrop)
3763{
Bram Moolenaar4033c552017-09-16 20:54:51 +02003764#define BUFPATHLEN _MAX_PATH
3765#define DRAGQVAL 0xFFFFFFFF
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003766 WCHAR wszFile[BUFPATHLEN];
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003767 char szFile[BUFPATHLEN];
3768 UINT cFiles = DragQueryFile(hDrop, DRAGQVAL, NULL, 0);
3769 UINT i;
3770 char_u **fnames;
3771 POINT pt;
3772 int_u modifiers = 0;
3773
Bram Moolenaar734a8672019-12-02 22:49:38 +01003774 // Obtain dropped position
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003775 DragQueryPoint(hDrop, &pt);
3776 MapWindowPoints(s_hwnd, s_textArea, &pt, 1);
3777
3778 reset_VIsual();
3779
Bram Moolenaarc799fe22019-05-28 23:08:19 +02003780 fnames = ALLOC_MULT(char_u *, cFiles);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003781
3782 if (fnames != NULL)
3783 for (i = 0; i < cFiles; ++i)
3784 {
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003785 if (DragQueryFileW(hDrop, i, wszFile, BUFPATHLEN) > 0)
3786 fnames[i] = utf16_to_enc(wszFile, NULL);
3787 else
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003788 {
3789 DragQueryFile(hDrop, i, szFile, BUFPATHLEN);
3790 fnames[i] = vim_strsave((char_u *)szFile);
3791 }
3792 }
3793
3794 DragFinish(hDrop);
3795
3796 if (fnames != NULL)
3797 {
3798 if ((GetKeyState(VK_SHIFT) & 0x8000) != 0)
3799 modifiers |= MOUSE_SHIFT;
3800 if ((GetKeyState(VK_CONTROL) & 0x8000) != 0)
3801 modifiers |= MOUSE_CTRL;
3802 if ((GetKeyState(VK_MENU) & 0x8000) != 0)
3803 modifiers |= MOUSE_ALT;
3804
3805 gui_handle_drop(pt.x, pt.y, modifiers, fnames, cFiles);
3806
3807 s_need_activate = TRUE;
3808 }
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003809}
3810
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003811 static int
3812_OnScroll(
Bram Moolenaar1266d672017-02-01 13:43:36 +01003813 HWND hwnd UNUSED,
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003814 HWND hwndCtl,
3815 UINT code,
3816 int pos)
3817{
Bram Moolenaar734a8672019-12-02 22:49:38 +01003818 static UINT prev_code = 0; // code of previous call
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003819 scrollbar_T *sb, *sb_info;
3820 long val;
3821 int dragging = FALSE;
3822 int dont_scroll_save = dont_scroll;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003823 SCROLLINFO si;
3824
3825 si.cbSize = sizeof(si);
3826 si.fMask = SIF_POS;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003827
3828 sb = gui_mswin_find_scrollbar(hwndCtl);
3829 if (sb == NULL)
3830 return 0;
3831
Bram Moolenaar734a8672019-12-02 22:49:38 +01003832 if (sb->wp != NULL) // Left or right scrollbar
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003833 {
3834 /*
3835 * Careful: need to get scrollbar info out of first (left) scrollbar
3836 * for window, but keep real scrollbar too because we must pass it to
3837 * gui_drag_scrollbar().
3838 */
3839 sb_info = &sb->wp->w_scrollbars[0];
3840 }
Bram Moolenaar734a8672019-12-02 22:49:38 +01003841 else // Bottom scrollbar
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003842 sb_info = sb;
3843 val = sb_info->value;
3844
3845 switch (code)
3846 {
3847 case SB_THUMBTRACK:
3848 val = pos;
3849 dragging = TRUE;
3850 if (sb->scroll_shift > 0)
3851 val <<= sb->scroll_shift;
3852 break;
3853 case SB_LINEDOWN:
3854 val++;
3855 break;
3856 case SB_LINEUP:
3857 val--;
3858 break;
3859 case SB_PAGEDOWN:
3860 val += (sb_info->size > 2 ? sb_info->size - 2 : 1);
3861 break;
3862 case SB_PAGEUP:
3863 val -= (sb_info->size > 2 ? sb_info->size - 2 : 1);
3864 break;
3865 case SB_TOP:
3866 val = 0;
3867 break;
3868 case SB_BOTTOM:
3869 val = sb_info->max;
3870 break;
3871 case SB_ENDSCROLL:
3872 if (prev_code == SB_THUMBTRACK)
3873 {
3874 /*
3875 * "pos" only gives us 16-bit data. In case of large file,
3876 * use GetScrollPos() which returns 32-bit. Unfortunately it
3877 * is not valid while the scrollbar is being dragged.
3878 */
3879 val = GetScrollPos(hwndCtl, SB_CTL);
3880 if (sb->scroll_shift > 0)
3881 val <<= sb->scroll_shift;
3882 }
3883 break;
3884
3885 default:
Bram Moolenaar734a8672019-12-02 22:49:38 +01003886 // TRACE("Unknown scrollbar event %d\n", code);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003887 return 0;
3888 }
3889 prev_code = code;
3890
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003891 si.nPos = (sb->scroll_shift > 0) ? val >> sb->scroll_shift : val;
3892 SetScrollInfo(hwndCtl, SB_CTL, &si, TRUE);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003893
3894 /*
3895 * When moving a vertical scrollbar, move the other vertical scrollbar too.
3896 */
3897 if (sb->wp != NULL)
3898 {
3899 scrollbar_T *sba = sb->wp->w_scrollbars;
3900 HWND id = sba[ (sb == sba + SBAR_LEFT) ? SBAR_RIGHT : SBAR_LEFT].id;
3901
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003902 SetScrollInfo(id, SB_CTL, &si, TRUE);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003903 }
3904
Bram Moolenaar734a8672019-12-02 22:49:38 +01003905 // Don't let us be interrupted here by another message.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003906 s_busy_processing = TRUE;
3907
Bram Moolenaar734a8672019-12-02 22:49:38 +01003908 // When "allow_scrollbar" is FALSE still need to remember the new
3909 // position, but don't actually scroll by setting "dont_scroll".
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003910 dont_scroll = !allow_scrollbar;
3911
Bram Moolenaara338adc2018-01-31 20:51:47 +01003912 mch_disable_flush();
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003913 gui_drag_scrollbar(sb, val, dragging);
Bram Moolenaara338adc2018-01-31 20:51:47 +01003914 mch_enable_flush();
3915 gui_may_flush();
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003916
3917 s_busy_processing = FALSE;
3918 dont_scroll = dont_scroll_save;
3919
3920 return 0;
3921}
3922
3923
Bram Moolenaar071d4272004-06-13 20:20:40 +00003924#ifdef FEAT_XPM_W32
3925# include "xpm_w32.h"
3926#endif
3927
Bram Moolenaar071d4272004-06-13 20:20:40 +00003928
Bram Moolenaar734a8672019-12-02 22:49:38 +01003929// Some parameters for tearoff menus. All in pixels.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003930#define TEAROFF_PADDING_X 2
3931#define TEAROFF_BUTTON_PAD_X 8
3932#define TEAROFF_MIN_WIDTH 200
3933#define TEAROFF_SUBMENU_LABEL ">>"
3934#define TEAROFF_COLUMN_PADDING 3 // # spaces to pad column with.
3935
3936
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01003937#ifdef FEAT_BEVAL_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00003938# define ID_BEVAL_TOOLTIP 200
3939# define BEVAL_TEXT_LEN MAXPATHL
3940
Bram Moolenaar071d4272004-06-13 20:20:40 +00003941static BalloonEval *cur_beval = NULL;
K.Takataa8ec4912022-02-03 14:32:33 +00003942static UINT_PTR beval_timer_id = 0;
3943static DWORD last_user_activity = 0;
Bram Moolenaar734a8672019-12-02 22:49:38 +01003944#endif // defined(FEAT_BEVAL_GUI)
Bram Moolenaar45360022005-07-21 21:08:21 +00003945
Bram Moolenaarf9393ef2006-04-24 19:47:27 +00003946
Bram Moolenaar734a8672019-12-02 22:49:38 +01003947// Local variables:
Bram Moolenaar071d4272004-06-13 20:20:40 +00003948
3949#ifdef FEAT_MENU
3950static UINT s_menu_id = 100;
Bram Moolenaar786989b2010-10-27 12:15:33 +02003951#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003952
3953/*
3954 * Use the system font for dialogs and tear-off menus. Remove this line to
3955 * use DLG_FONT_NAME.
3956 */
Bram Moolenaar786989b2010-10-27 12:15:33 +02003957#define USE_SYSMENU_FONT
Bram Moolenaar071d4272004-06-13 20:20:40 +00003958
3959#define VIM_NAME "vim"
Bram Moolenaar071d4272004-06-13 20:20:40 +00003960#define VIM_CLASSW L"Vim"
3961
Bram Moolenaar734a8672019-12-02 22:49:38 +01003962// Initial size for the dialog template. For gui_mch_dialog() it's fixed,
3963// thus there should be room for every dialog. For tearoffs it's made bigger
3964// when needed.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003965#define DLG_ALLOC_SIZE 16 * 1024
3966
3967/*
3968 * stuff for dialogs, menus, tearoffs etc.
3969 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003970static PWORD
3971add_dialog_element(
3972 PWORD p,
3973 DWORD lStyle,
3974 WORD x,
3975 WORD y,
3976 WORD w,
3977 WORD h,
3978 WORD Id,
3979 WORD clss,
3980 const char *caption);
3981static LPWORD lpwAlign(LPWORD);
Bram Moolenaar6edeaf32017-09-26 14:46:04 +02003982static int nCopyAnsiToWideChar(LPWORD, LPSTR, BOOL);
Bram Moolenaar065bbac2016-02-20 13:08:46 +01003983#if defined(FEAT_MENU) && defined(FEAT_TEAROFF)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003984static void gui_mch_tearoff(char_u *title, vimmenu_T *menu, int initX, int initY);
Bram Moolenaar065bbac2016-02-20 13:08:46 +01003985#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003986static void get_dialog_font_metrics(void);
3987
3988static int dialog_default_button = -1;
3989
Bram Moolenaar734a8672019-12-02 22:49:38 +01003990// Intellimouse support
Bram Moolenaar071d4272004-06-13 20:20:40 +00003991static int mouse_scroll_lines = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003992
Bram Moolenaar071d4272004-06-13 20:20:40 +00003993#ifdef FEAT_TOOLBAR
3994static void initialise_toolbar(void);
K.Takatac81e9bf2022-01-16 14:15:49 +00003995static void update_toolbar_size(void);
Bram Moolenaar5f919ee2013-07-21 17:46:43 +02003996static LRESULT CALLBACK toolbar_wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003997static int get_toolbar_bitmap(vimmenu_T *menu);
K.Takatac81e9bf2022-01-16 14:15:49 +00003998#else
3999# define update_toolbar_size()
Bram Moolenaar071d4272004-06-13 20:20:40 +00004000#endif
4001
Bram Moolenaar3991dab2006-03-27 17:01:56 +00004002#ifdef FEAT_GUI_TABLINE
4003static void initialise_tabline(void);
Bram Moolenaar5f919ee2013-07-21 17:46:43 +02004004static LRESULT CALLBACK tabline_wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
Bram Moolenaar3991dab2006-03-27 17:01:56 +00004005#endif
4006
Bram Moolenaar071d4272004-06-13 20:20:40 +00004007#ifdef FEAT_MBYTE_IME
4008static LRESULT _OnImeComposition(HWND hwnd, WPARAM dbcs, LPARAM param);
4009static char_u *GetResultStr(HWND hwnd, int GCS, int *lenp);
4010#endif
4011#if defined(FEAT_MBYTE_IME) && defined(DYNAMIC_IME)
4012# ifdef NOIME
4013typedef struct tagCOMPOSITIONFORM {
4014 DWORD dwStyle;
4015 POINT ptCurrentPos;
4016 RECT rcArea;
4017} COMPOSITIONFORM, *PCOMPOSITIONFORM, NEAR *NPCOMPOSITIONFORM, FAR *LPCOMPOSITIONFORM;
4018typedef HANDLE HIMC;
4019# endif
4020
Bram Moolenaard857f0e2005-06-21 22:37:39 +00004021static HINSTANCE hLibImm = NULL;
Bram Moolenaard857f0e2005-06-21 22:37:39 +00004022static LONG (WINAPI *pImmGetCompositionStringW)(HIMC, DWORD, LPVOID, DWORD);
4023static HIMC (WINAPI *pImmGetContext)(HWND);
4024static HIMC (WINAPI *pImmAssociateContext)(HWND, HIMC);
4025static BOOL (WINAPI *pImmReleaseContext)(HWND, HIMC);
4026static BOOL (WINAPI *pImmGetOpenStatus)(HIMC);
4027static BOOL (WINAPI *pImmSetOpenStatus)(HIMC, BOOL);
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01004028static BOOL (WINAPI *pImmGetCompositionFontW)(HIMC, LPLOGFONTW);
4029static BOOL (WINAPI *pImmSetCompositionFontW)(HIMC, LPLOGFONTW);
Bram Moolenaard857f0e2005-06-21 22:37:39 +00004030static BOOL (WINAPI *pImmSetCompositionWindow)(HIMC, LPCOMPOSITIONFORM);
4031static BOOL (WINAPI *pImmGetConversionStatus)(HIMC, LPDWORD, LPDWORD);
Bram Moolenaarca003e12006-03-17 23:19:38 +00004032static BOOL (WINAPI *pImmSetConversionStatus)(HIMC, DWORD, DWORD);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004033static void dyn_imm_load(void);
4034#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004035# define pImmGetCompositionStringW ImmGetCompositionStringW
4036# define pImmGetContext ImmGetContext
4037# define pImmAssociateContext ImmAssociateContext
4038# define pImmReleaseContext ImmReleaseContext
4039# define pImmGetOpenStatus ImmGetOpenStatus
4040# define pImmSetOpenStatus ImmSetOpenStatus
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01004041# define pImmGetCompositionFontW ImmGetCompositionFontW
4042# define pImmSetCompositionFontW ImmSetCompositionFontW
Bram Moolenaar071d4272004-06-13 20:20:40 +00004043# define pImmSetCompositionWindow ImmSetCompositionWindow
4044# define pImmGetConversionStatus ImmGetConversionStatus
Bram Moolenaarca003e12006-03-17 23:19:38 +00004045# define pImmSetConversionStatus ImmSetConversionStatus
Bram Moolenaar071d4272004-06-13 20:20:40 +00004046#endif
4047
Bram Moolenaar071d4272004-06-13 20:20:40 +00004048#ifdef FEAT_MENU
4049/*
4050 * Figure out how high the menu bar is at the moment.
4051 */
4052 static int
4053gui_mswin_get_menu_height(
Bram Moolenaar734a8672019-12-02 22:49:38 +01004054 int fix_window) // If TRUE, resize window if menu height changed
Bram Moolenaar071d4272004-06-13 20:20:40 +00004055{
4056 static int old_menu_height = -1;
4057
4058 RECT rc1, rc2;
4059 int num;
4060 int menu_height;
4061
4062 if (gui.menu_is_active)
4063 num = GetMenuItemCount(s_menuBar);
4064 else
4065 num = 0;
4066
4067 if (num == 0)
4068 menu_height = 0;
Bram Moolenaar71371b12015-03-24 17:57:12 +01004069 else if (IsMinimized(s_hwnd))
4070 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01004071 // The height of the menu cannot be determined while the window is
4072 // minimized. Take the previous height if the menu is changed in that
4073 // state, to avoid that Vim's vertical window size accidentally
4074 // increases due to the unaccounted-for menu height.
Bram Moolenaar71371b12015-03-24 17:57:12 +01004075 menu_height = old_menu_height == -1 ? 0 : old_menu_height;
4076 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004077 else
4078 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +02004079 /*
4080 * In case 'lines' is set in _vimrc/_gvimrc window width doesn't
4081 * seem to have been set yet, so menu wraps in default window
4082 * width which is very narrow. Instead just return height of a
4083 * single menu item. Will still be wrong when the menu really
4084 * should wrap over more than one line.
4085 */
4086 GetMenuItemRect(s_hwnd, s_menuBar, 0, &rc1);
4087 if (gui.starting)
4088 menu_height = rc1.bottom - rc1.top + 1;
4089 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004090 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +02004091 GetMenuItemRect(s_hwnd, s_menuBar, num - 1, &rc2);
4092 menu_height = rc2.bottom - rc1.top + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004093 }
4094 }
4095
4096 if (fix_window && menu_height != old_menu_height)
Bram Moolenaarafa24992006-03-27 20:58:26 +00004097 gui_set_shellsize(FALSE, FALSE, RESIZE_VERT);
Bram Moolenaar71371b12015-03-24 17:57:12 +01004098 old_menu_height = menu_height;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004099
4100 return menu_height;
4101}
Bram Moolenaar734a8672019-12-02 22:49:38 +01004102#endif // FEAT_MENU
Bram Moolenaar071d4272004-06-13 20:20:40 +00004103
4104
4105/*
4106 * Setup for the Intellimouse
4107 */
4108 static void
4109init_mouse_wheel(void)
4110{
Bram Moolenaar734a8672019-12-02 22:49:38 +01004111 mouse_scroll_lines = 3; // reasonable default
Bram Moolenaar071d4272004-06-13 20:20:40 +00004112
Bram Moolenaar734a8672019-12-02 22:49:38 +01004113 // if NT 4.0+ (or Win98) get scroll lines directly from system
Bram Moolenaarcea912a2016-10-12 14:20:24 +02004114 SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0,
4115 &mouse_scroll_lines, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004116}
4117
4118
Bram Moolenaarae20f342019-11-05 21:09:23 +01004119/*
4120 * Intellimouse wheel handler.
4121 * Treat a mouse wheel event as if it were a scroll request.
4122 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004123 static void
4124_OnMouseWheel(
4125 HWND hwnd,
4126 short zDelta)
4127{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004128 int i;
4129 int size;
4130 HWND hwndCtl;
Bram Moolenaarae20f342019-11-05 21:09:23 +01004131 win_T *wp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004132
Bram Moolenaar071d4272004-06-13 20:20:40 +00004133 if (mouse_scroll_lines == 0)
4134 init_mouse_wheel();
4135
Bram Moolenaarae20f342019-11-05 21:09:23 +01004136 wp = gui_mouse_window(FIND_POPUP);
4137
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01004138#ifdef FEAT_PROP_POPUP
Bram Moolenaarae20f342019-11-05 21:09:23 +01004139 if (wp != NULL && popup_is_popup(wp))
Bram Moolenaar0630bb62019-11-04 22:52:12 +01004140 {
Bram Moolenaarae20f342019-11-05 21:09:23 +01004141 cmdarg_T cap;
4142 oparg_T oa;
Bram Moolenaar0630bb62019-11-04 22:52:12 +01004143
Bram Moolenaarae20f342019-11-05 21:09:23 +01004144 // Mouse hovers over popup window, scroll it if possible.
4145 mouse_row = wp->w_winrow;
4146 mouse_col = wp->w_wincol;
Bram Moolenaara80faa82020-04-12 19:37:17 +02004147 CLEAR_FIELD(cap);
Bram Moolenaarae20f342019-11-05 21:09:23 +01004148 cap.arg = zDelta < 0 ? MSCR_UP : MSCR_DOWN;
4149 cap.cmdchar = zDelta < 0 ? K_MOUSEUP : K_MOUSEDOWN;
4150 clear_oparg(&oa);
4151 cap.oap = &oa;
4152 nv_mousescroll(&cap);
4153 update_screen(0);
4154 setcursor();
4155 out_flush();
4156 return;
Bram Moolenaar0630bb62019-11-04 22:52:12 +01004157 }
4158#endif
4159
Bram Moolenaarae20f342019-11-05 21:09:23 +01004160 if (wp == NULL || !p_scf)
4161 wp = curwin;
4162
4163 if (wp->w_scrollbars[SBAR_RIGHT].id != 0)
4164 hwndCtl = wp->w_scrollbars[SBAR_RIGHT].id;
4165 else if (wp->w_scrollbars[SBAR_LEFT].id != 0)
4166 hwndCtl = wp->w_scrollbars[SBAR_LEFT].id;
4167 else
4168 return;
4169 size = wp->w_height;
4170
Bram Moolenaara338adc2018-01-31 20:51:47 +01004171 mch_disable_flush();
Bram Moolenaar071d4272004-06-13 20:20:40 +00004172 if (mouse_scroll_lines > 0
4173 && mouse_scroll_lines < (size > 2 ? size - 2 : 1))
4174 {
4175 for (i = mouse_scroll_lines; i > 0; --i)
4176 _OnScroll(hwnd, hwndCtl, zDelta >= 0 ? SB_LINEUP : SB_LINEDOWN, 0);
4177 }
4178 else
4179 _OnScroll(hwnd, hwndCtl, zDelta >= 0 ? SB_PAGEUP : SB_PAGEDOWN, 0);
Bram Moolenaara338adc2018-01-31 20:51:47 +01004180 mch_enable_flush();
4181 gui_may_flush();
Bram Moolenaar071d4272004-06-13 20:20:40 +00004182}
4183
Bram Moolenaar551dbcc2006-04-25 22:13:59 +00004184#ifdef USE_SYSMENU_FONT
4185/*
4186 * Get Menu Font.
4187 * Return OK or FAIL.
4188 */
4189 static int
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01004190gui_w32_get_menu_font(LOGFONTW *lf)
Bram Moolenaar551dbcc2006-04-25 22:13:59 +00004191{
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01004192 NONCLIENTMETRICSW nm;
Bram Moolenaar551dbcc2006-04-25 22:13:59 +00004193
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01004194 nm.cbSize = sizeof(NONCLIENTMETRICSW);
4195 if (!SystemParametersInfoW(
Bram Moolenaar551dbcc2006-04-25 22:13:59 +00004196 SPI_GETNONCLIENTMETRICS,
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01004197 sizeof(NONCLIENTMETRICSW),
Bram Moolenaar551dbcc2006-04-25 22:13:59 +00004198 &nm,
4199 0))
4200 return FAIL;
4201 *lf = nm.lfMenuFont;
4202 return OK;
4203}
4204#endif
4205
4206
4207#if defined(FEAT_GUI_TABLINE) && defined(USE_SYSMENU_FONT)
4208/*
4209 * Set the GUI tabline font to the system menu font
4210 */
4211 static void
4212set_tabline_font(void)
4213{
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01004214 LOGFONTW lfSysmenu;
Bram Moolenaar551dbcc2006-04-25 22:13:59 +00004215 HFONT font;
4216 HWND hwnd;
4217 HDC hdc;
4218 HFONT hfntOld;
4219 TEXTMETRIC tm;
4220
4221 if (gui_w32_get_menu_font(&lfSysmenu) != OK)
4222 return;
4223
K.Takatac81e9bf2022-01-16 14:15:49 +00004224 lfSysmenu.lfHeight = adjust_fontsize_by_dpi(lfSysmenu.lfHeight);
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01004225 font = CreateFontIndirectW(&lfSysmenu);
Bram Moolenaar551dbcc2006-04-25 22:13:59 +00004226
4227 SendMessage(s_tabhwnd, WM_SETFONT, (WPARAM)font, TRUE);
4228
4229 /*
4230 * Compute the height of the font used for the tab text
4231 */
4232 hwnd = GetDesktopWindow();
4233 hdc = GetWindowDC(hwnd);
4234 hfntOld = SelectFont(hdc, font);
4235
4236 GetTextMetrics(hdc, &tm);
4237
4238 SelectFont(hdc, hfntOld);
4239 ReleaseDC(hwnd, hdc);
4240
4241 /*
4242 * The space used by the tab border and the space between the tab label
4243 * and the tab border is included as 7.
4244 */
4245 gui.tabline_height = tm.tmHeight + tm.tmInternalLeading + 7;
4246}
K.Takatac81e9bf2022-01-16 14:15:49 +00004247#else
4248# define set_tabline_font()
Bram Moolenaar551dbcc2006-04-25 22:13:59 +00004249#endif
4250
Bram Moolenaar520470a2005-06-16 21:59:56 +00004251/*
4252 * Invoked when a setting was changed.
4253 */
4254 static LRESULT CALLBACK
4255_OnSettingChange(UINT n)
4256{
4257 if (n == SPI_SETWHEELSCROLLLINES)
4258 SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0,
4259 &mouse_scroll_lines, 0);
Bram Moolenaar551dbcc2006-04-25 22:13:59 +00004260 if (n == SPI_SETNONCLIENTMETRICS)
4261 set_tabline_font();
Bram Moolenaar520470a2005-06-16 21:59:56 +00004262 return 0;
4263}
4264
Bram Moolenaar071d4272004-06-13 20:20:40 +00004265#ifdef FEAT_NETBEANS_INTG
4266 static void
4267_OnWindowPosChanged(
4268 HWND hwnd,
4269 const LPWINDOWPOS lpwpos)
4270{
4271 static int x = 0, y = 0, cx = 0, cy = 0;
Bram Moolenaarf12d9832016-01-29 21:11:25 +01004272 extern int WSInitialized;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004273
4274 if (WSInitialized && (lpwpos->x != x || lpwpos->y != y
4275 || lpwpos->cx != cx || lpwpos->cy != cy))
4276 {
4277 x = lpwpos->x;
4278 y = lpwpos->y;
4279 cx = lpwpos->cx;
4280 cy = lpwpos->cy;
4281 netbeans_frame_moved(x, y);
4282 }
Bram Moolenaar734a8672019-12-02 22:49:38 +01004283 // Allow to send WM_SIZE and WM_MOVE
K.Takata4ac893f2022-01-20 12:44:28 +00004284 FORWARD_WM_WINDOWPOSCHANGED(hwnd, lpwpos, DefWindowProcW);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004285}
4286#endif
4287
K.Takata4f2417f2021-06-05 16:25:32 +02004288
4289static HWND hwndTip = NULL;
4290
4291 static void
4292show_sizing_tip(int cols, int rows)
4293{
4294 TOOLINFOA ti = {sizeof(ti)};
4295 char buf[32];
4296
4297 ti.hwnd = s_hwnd;
4298 ti.uId = (UINT_PTR)s_hwnd;
4299 ti.uFlags = TTF_SUBCLASS | TTF_IDISHWND;
4300 ti.lpszText = buf;
4301 sprintf(buf, "%dx%d", cols, rows);
4302 if (hwndTip == NULL)
4303 {
4304 hwndTip = CreateWindowExA(0, TOOLTIPS_CLASSA, NULL,
4305 WS_POPUP | TTS_ALWAYSTIP | TTS_NOPREFIX,
4306 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
4307 s_hwnd, NULL, GetModuleHandle(NULL), NULL);
4308 SendMessage(hwndTip, TTM_ADDTOOL, 0, (LPARAM)&ti);
4309 SendMessage(hwndTip, TTM_TRACKACTIVATE, TRUE, (LPARAM)&ti);
4310 }
4311 else
4312 {
4313 SendMessage(hwndTip, TTM_UPDATETIPTEXT, 0, (LPARAM)&ti);
4314 }
4315 SendMessage(hwndTip, TTM_POPUP, 0, 0);
4316}
4317
4318 static void
4319destroy_sizing_tip(void)
4320{
4321 if (hwndTip != NULL)
4322 {
4323 DestroyWindow(hwndTip);
4324 hwndTip = NULL;
4325 }
4326}
4327
Bram Moolenaar071d4272004-06-13 20:20:40 +00004328 static int
4329_DuringSizing(
Bram Moolenaar071d4272004-06-13 20:20:40 +00004330 UINT fwSide,
4331 LPRECT lprc)
4332{
4333 int w, h;
4334 int valid_w, valid_h;
4335 int w_offset, h_offset;
K.Takata4f2417f2021-06-05 16:25:32 +02004336 int cols, rows;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004337
4338 w = lprc->right - lprc->left;
4339 h = lprc->bottom - lprc->top;
K.Takata4f2417f2021-06-05 16:25:32 +02004340 gui_mswin_get_valid_dimensions(w, h, &valid_w, &valid_h, &cols, &rows);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004341 w_offset = w - valid_w;
4342 h_offset = h - valid_h;
4343
4344 if (fwSide == WMSZ_LEFT || fwSide == WMSZ_TOPLEFT
4345 || fwSide == WMSZ_BOTTOMLEFT)
4346 lprc->left += w_offset;
4347 else if (fwSide == WMSZ_RIGHT || fwSide == WMSZ_TOPRIGHT
4348 || fwSide == WMSZ_BOTTOMRIGHT)
4349 lprc->right -= w_offset;
4350
4351 if (fwSide == WMSZ_TOP || fwSide == WMSZ_TOPLEFT
4352 || fwSide == WMSZ_TOPRIGHT)
4353 lprc->top += h_offset;
4354 else if (fwSide == WMSZ_BOTTOM || fwSide == WMSZ_BOTTOMLEFT
4355 || fwSide == WMSZ_BOTTOMRIGHT)
4356 lprc->bottom -= h_offset;
K.Takata4f2417f2021-06-05 16:25:32 +02004357
4358 show_sizing_tip(cols, rows);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004359 return TRUE;
4360}
4361
K.Takata92000e22022-01-20 15:10:57 +00004362#ifdef FEAT_GUI_TABLINE
4363 static void
4364_OnRButtonUp(HWND hwnd, int x, int y, UINT keyFlags)
4365{
4366 if (gui_mch_showing_tabline())
4367 {
4368 POINT pt;
4369 RECT rect;
4370
4371 /*
4372 * If the cursor is on the tabline, display the tab menu
4373 */
4374 GetCursorPos(&pt);
4375 GetWindowRect(s_textArea, &rect);
4376 if (pt.y < rect.top)
4377 {
4378 show_tabline_popup_menu();
4379 return;
4380 }
4381 }
4382 FORWARD_WM_RBUTTONUP(hwnd, x, y, keyFlags, DefWindowProcW);
4383}
4384
4385 static void
4386_OnLButtonDown(HWND hwnd, BOOL fDoubleClick, int x, int y, UINT keyFlags)
4387{
4388 /*
4389 * If the user double clicked the tabline, create a new tab
4390 */
4391 if (gui_mch_showing_tabline())
4392 {
4393 POINT pt;
4394 RECT rect;
4395
4396 GetCursorPos(&pt);
4397 GetWindowRect(s_textArea, &rect);
4398 if (pt.y < rect.top)
4399 send_tabline_menu_event(0, TABLINE_MENU_NEW);
4400 }
4401 FORWARD_WM_LBUTTONDOWN(hwnd, fDoubleClick, x, y, keyFlags, DefWindowProcW);
4402}
4403#endif
4404
4405 static UINT
4406_OnNCHitTest(HWND hwnd, int xPos, int yPos)
4407{
4408 UINT result;
4409 int x, y;
4410
4411 result = FORWARD_WM_NCHITTEST(hwnd, xPos, yPos, DefWindowProcW);
4412 if (result != HTCLIENT)
4413 return result;
4414
4415#ifdef FEAT_GUI_TABLINE
4416 if (gui_mch_showing_tabline())
4417 {
4418 RECT rct;
4419
4420 // If the cursor is on the GUI tabline, don't process this event
4421 GetWindowRect(s_textArea, &rct);
4422 if (yPos < rct.top)
4423 return result;
4424 }
4425#endif
4426 (void)gui_mch_get_winpos(&x, &y);
4427 xPos -= x;
4428
4429 if (xPos < 48) // <VN> TODO should use system metric?
4430 return HTBOTTOMLEFT;
4431 else
4432 return HTBOTTOMRIGHT;
4433}
4434
4435#if defined(FEAT_TOOLBAR) || defined(FEAT_GUI_TABLINE)
4436 static LRESULT
4437_OnNotify(HWND hwnd, UINT id, NMHDR *hdr)
4438{
4439 switch (hdr->code)
4440 {
4441 case TTN_GETDISPINFOW:
4442 case TTN_GETDISPINFO:
4443 {
4444 char_u *str = NULL;
4445 static void *tt_text = NULL;
4446
4447 VIM_CLEAR(tt_text);
4448
4449# ifdef FEAT_GUI_TABLINE
4450 if (gui_mch_showing_tabline()
4451 && hdr->hwndFrom == TabCtrl_GetToolTips(s_tabhwnd))
4452 {
4453 POINT pt;
4454 /*
4455 * Mouse is over the GUI tabline. Display the
4456 * tooltip for the tab under the cursor
4457 *
4458 * Get the cursor position within the tab control
4459 */
4460 GetCursorPos(&pt);
4461 if (ScreenToClient(s_tabhwnd, &pt) != 0)
4462 {
4463 TCHITTESTINFO htinfo;
4464 int idx;
4465
4466 /*
4467 * Get the tab under the cursor
4468 */
4469 htinfo.pt.x = pt.x;
4470 htinfo.pt.y = pt.y;
4471 idx = TabCtrl_HitTest(s_tabhwnd, &htinfo);
4472 if (idx != -1)
4473 {
4474 tabpage_T *tp;
4475
4476 tp = find_tabpage(idx + 1);
4477 if (tp != NULL)
4478 {
4479 get_tabline_label(tp, TRUE);
4480 str = NameBuff;
4481 }
4482 }
4483 }
4484 }
4485# endif
4486# ifdef FEAT_TOOLBAR
4487# ifdef FEAT_GUI_TABLINE
4488 else
4489# endif
4490 {
4491 UINT idButton;
4492 vimmenu_T *pMenu;
4493
4494 idButton = (UINT) hdr->idFrom;
4495 pMenu = gui_mswin_find_menu(root_menu, idButton);
4496 if (pMenu)
4497 str = pMenu->strings[MENU_INDEX_TIP];
4498 }
4499# endif
4500 if (str == NULL)
4501 break;
4502
4503 // Set the maximum width, this also enables using \n for
4504 // line break.
4505 SendMessage(hdr->hwndFrom, TTM_SETMAXTIPWIDTH, 0, 500);
4506
4507 if (hdr->code == TTN_GETDISPINFOW)
4508 {
4509 LPNMTTDISPINFOW lpdi = (LPNMTTDISPINFOW)hdr;
4510
4511 tt_text = enc_to_utf16(str, NULL);
4512 lpdi->lpszText = tt_text;
4513 // can't show tooltip if failed
4514 }
4515 else
4516 {
4517 LPNMTTDISPINFO lpdi = (LPNMTTDISPINFO)hdr;
4518
4519 if (STRLEN(str) < sizeof(lpdi->szText)
4520 || ((tt_text = vim_strsave(str)) == NULL))
4521 vim_strncpy((char_u *)lpdi->szText, str,
4522 sizeof(lpdi->szText) - 1);
4523 else
4524 lpdi->lpszText = tt_text;
4525 }
4526 }
4527 break;
4528
4529# ifdef FEAT_GUI_TABLINE
4530 case TCN_SELCHANGE:
4531 if (gui_mch_showing_tabline() && (hdr->hwndFrom == s_tabhwnd))
4532 {
4533 send_tabline_event(TabCtrl_GetCurSel(s_tabhwnd) + 1);
4534 return 0L;
4535 }
4536 break;
4537
4538 case NM_RCLICK:
4539 if (gui_mch_showing_tabline() && (hdr->hwndFrom == s_tabhwnd))
4540 {
4541 show_tabline_popup_menu();
4542 return 0L;
4543 }
4544 break;
4545# endif
4546
4547 default:
4548 break;
4549 }
4550 return DefWindowProcW(hwnd, WM_NOTIFY, (WPARAM)id, (LPARAM)hdr);
4551}
4552#endif
4553
4554#if defined(MENUHINTS) && defined(FEAT_MENU)
4555 static LRESULT
4556_OnMenuSelect(HWND hwnd, WPARAM wParam, LPARAM lParam)
4557{
4558 if (((UINT) HIWORD(wParam)
4559 & (0xffff ^ (MF_MOUSESELECT + MF_BITMAP + MF_POPUP)))
4560 == MF_HILITE
4561 && (State & CMDLINE) == 0)
4562 {
4563 UINT idButton;
4564 vimmenu_T *pMenu;
4565 static int did_menu_tip = FALSE;
4566
4567 if (did_menu_tip)
4568 {
4569 msg_clr_cmdline();
4570 setcursor();
4571 out_flush();
4572 did_menu_tip = FALSE;
4573 }
4574
4575 idButton = (UINT)LOWORD(wParam);
4576 pMenu = gui_mswin_find_menu(root_menu, idButton);
4577 if (pMenu != NULL && pMenu->strings[MENU_INDEX_TIP] != 0
4578 && GetMenuState(s_menuBar, pMenu->id, MF_BYCOMMAND) != -1)
4579 {
4580 ++msg_hist_off;
4581 msg((char *)pMenu->strings[MENU_INDEX_TIP]);
4582 --msg_hist_off;
4583 setcursor();
4584 out_flush();
4585 did_menu_tip = TRUE;
4586 }
4587 return 0L;
4588 }
4589 return DefWindowProcW(hwnd, WM_MENUSELECT, wParam, lParam);
4590}
4591#endif
4592
K.Takatac81e9bf2022-01-16 14:15:49 +00004593 static LRESULT
4594_OnDpiChanged(HWND hwnd, UINT xdpi, UINT ydpi, RECT *rc)
4595{
4596 s_dpi = ydpi;
4597 s_in_dpichanged = TRUE;
4598 //TRACE("DPI: %d", ydpi);
4599
4600 update_scrollbar_size();
4601 update_toolbar_size();
4602 set_tabline_font();
4603
4604 gui_init_font(*p_guifont == NUL ? hl_get_font_name() : p_guifont, FALSE);
4605 gui_get_wide_font();
4606 gui_mswin_get_menu_height(FALSE);
4607#ifdef FEAT_MBYTE_IME
4608 im_set_position(gui.row, gui.col);
4609#endif
4610 InvalidateRect(hwnd, NULL, TRUE);
4611
4612 s_in_dpichanged = FALSE;
4613 return 0L;
4614}
Bram Moolenaar071d4272004-06-13 20:20:40 +00004615
4616
4617 static LRESULT CALLBACK
4618_WndProc(
4619 HWND hwnd,
4620 UINT uMsg,
4621 WPARAM wParam,
4622 LPARAM lParam)
4623{
LemonBoy77fc0b02022-04-22 22:45:52 +01004624 // TRACE("WndProc: hwnd = %08x, msg = %x, wParam = %x, lParam = %x\n",
4625 // hwnd, uMsg, wParam, lParam);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004626
4627 HandleMouseHide(uMsg, lParam);
4628
4629 s_uMsg = uMsg;
4630 s_wParam = wParam;
4631 s_lParam = lParam;
4632
4633 switch (uMsg)
4634 {
4635 HANDLE_MSG(hwnd, WM_DEADCHAR, _OnDeadChar);
4636 HANDLE_MSG(hwnd, WM_SYSDEADCHAR, _OnDeadChar);
Bram Moolenaar734a8672019-12-02 22:49:38 +01004637 // HANDLE_MSG(hwnd, WM_ACTIVATE, _OnActivate);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004638 HANDLE_MSG(hwnd, WM_CLOSE, _OnClose);
Bram Moolenaar734a8672019-12-02 22:49:38 +01004639 // HANDLE_MSG(hwnd, WM_COMMAND, _OnCommand);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004640 HANDLE_MSG(hwnd, WM_DESTROY, _OnDestroy);
4641 HANDLE_MSG(hwnd, WM_DROPFILES, _OnDropFiles);
4642 HANDLE_MSG(hwnd, WM_HSCROLL, _OnScroll);
4643 HANDLE_MSG(hwnd, WM_KILLFOCUS, _OnKillFocus);
4644#ifdef FEAT_MENU
4645 HANDLE_MSG(hwnd, WM_COMMAND, _OnMenu);
4646#endif
Bram Moolenaar734a8672019-12-02 22:49:38 +01004647 // HANDLE_MSG(hwnd, WM_MOVE, _OnMove);
4648 // HANDLE_MSG(hwnd, WM_NCACTIVATE, _OnNCActivate);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004649 HANDLE_MSG(hwnd, WM_SETFOCUS, _OnSetFocus);
4650 HANDLE_MSG(hwnd, WM_SIZE, _OnSize);
Bram Moolenaar734a8672019-12-02 22:49:38 +01004651 // HANDLE_MSG(hwnd, WM_SYSCOMMAND, _OnSysCommand);
4652 // HANDLE_MSG(hwnd, WM_SYSKEYDOWN, _OnAltKey);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004653 HANDLE_MSG(hwnd, WM_VSCROLL, _OnScroll);
4654 // HANDLE_MSG(hwnd, WM_WINDOWPOSCHANGING, _OnWindowPosChanging);
4655 HANDLE_MSG(hwnd, WM_ACTIVATEAPP, _OnActivateApp);
4656#ifdef FEAT_NETBEANS_INTG
4657 HANDLE_MSG(hwnd, WM_WINDOWPOSCHANGED, _OnWindowPosChanged);
4658#endif
Bram Moolenaarafa24992006-03-27 20:58:26 +00004659#ifdef FEAT_GUI_TABLINE
K.Takata92000e22022-01-20 15:10:57 +00004660 HANDLE_MSG(hwnd, WM_RBUTTONUP, _OnRButtonUp);
4661 HANDLE_MSG(hwnd, WM_LBUTTONDBLCLK, _OnLButtonDown);
Bram Moolenaarafa24992006-03-27 20:58:26 +00004662#endif
K.Takata92000e22022-01-20 15:10:57 +00004663 HANDLE_MSG(hwnd, WM_NCHITTEST, _OnNCHitTest);
Bram Moolenaarafa24992006-03-27 20:58:26 +00004664
Bram Moolenaar734a8672019-12-02 22:49:38 +01004665 case WM_QUERYENDSESSION: // System wants to go down.
4666 gui_shell_closed(); // Will exit when no changed buffers.
4667 return FALSE; // Do NOT allow system to go down.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004668
4669 case WM_ENDSESSION:
Bram Moolenaar734a8672019-12-02 22:49:38 +01004670 if (wParam) // system only really goes down when wParam is TRUE
Bram Moolenaar213ae482011-12-15 21:51:36 +01004671 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00004672 _OnEndSession();
Bram Moolenaar213ae482011-12-15 21:51:36 +01004673 return 0L;
4674 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004675 break;
4676
4677 case WM_CHAR:
Bram Moolenaar734a8672019-12-02 22:49:38 +01004678 // Don't use HANDLE_MSG() for WM_CHAR, it truncates wParam to a single
4679 // byte while we want the UTF-16 character value.
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004680 _OnChar(hwnd, (UINT)wParam, (int)(short)LOWORD(lParam));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004681 return 0L;
4682
4683 case WM_SYSCHAR:
4684 /*
4685 * if 'winaltkeys' is "no", or it's "menu" and it's not a menu
4686 * shortcut key, handle like a typed ALT key, otherwise call Windows
4687 * ALT key handling.
4688 */
4689#ifdef FEAT_MENU
4690 if ( !gui.menu_is_active
4691 || p_wak[0] == 'n'
4692 || (p_wak[0] == 'm' && !gui_is_menu_shortcut((int)wParam))
4693 )
4694#endif
4695 {
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004696 _OnSysChar(hwnd, (UINT)wParam, (int)(short)LOWORD(lParam));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004697 return 0L;
4698 }
4699#ifdef FEAT_MENU
4700 else
K.Takata4ac893f2022-01-20 12:44:28 +00004701 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004702#endif
4703
4704 case WM_SYSKEYUP:
4705#ifdef FEAT_MENU
Bram Moolenaar734a8672019-12-02 22:49:38 +01004706 // This used to be done only when menu is active: ALT key is used for
4707 // that. But that caused problems when menu is disabled and using
4708 // Alt-Tab-Esc: get into a strange state where no mouse-moved events
4709 // are received, mouse pointer remains hidden.
K.Takata4ac893f2022-01-20 12:44:28 +00004710 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004711#else
Bram Moolenaar213ae482011-12-15 21:51:36 +01004712 return 0L;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004713#endif
4714
K.Takata4f2417f2021-06-05 16:25:32 +02004715 case WM_EXITSIZEMOVE:
4716 destroy_sizing_tip();
4717 break;
4718
Bram Moolenaar734a8672019-12-02 22:49:38 +01004719 case WM_SIZING: // HANDLE_MSG doesn't seem to handle this one
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +00004720 return _DuringSizing((UINT)wParam, (LPRECT)lParam);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004721
4722 case WM_MOUSEWHEEL:
4723 _OnMouseWheel(hwnd, HIWORD(wParam));
Bram Moolenaar213ae482011-12-15 21:51:36 +01004724 return 0L;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004725
Bram Moolenaar734a8672019-12-02 22:49:38 +01004726 // Notification for change in SystemParametersInfo()
Bram Moolenaar520470a2005-06-16 21:59:56 +00004727 case WM_SETTINGCHANGE:
4728 return _OnSettingChange((UINT)wParam);
4729
Bram Moolenaar3991dab2006-03-27 17:01:56 +00004730#if defined(FEAT_TOOLBAR) || defined(FEAT_GUI_TABLINE)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004731 case WM_NOTIFY:
K.Takata92000e22022-01-20 15:10:57 +00004732 return _OnNotify(hwnd, (UINT)wParam, (NMHDR*)lParam);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004733#endif
K.Takata92000e22022-01-20 15:10:57 +00004734
Bram Moolenaar071d4272004-06-13 20:20:40 +00004735#if defined(MENUHINTS) && defined(FEAT_MENU)
4736 case WM_MENUSELECT:
K.Takata92000e22022-01-20 15:10:57 +00004737 return _OnMenuSelect(hwnd, wParam, lParam);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004738#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004739
4740#ifdef FEAT_MBYTE_IME
4741 case WM_IME_NOTIFY:
4742 if (!_OnImeNotify(hwnd, (DWORD)wParam, (DWORD)lParam))
K.Takata4ac893f2022-01-20 12:44:28 +00004743 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
Bram Moolenaar213ae482011-12-15 21:51:36 +01004744 return 1L;
4745
Bram Moolenaar071d4272004-06-13 20:20:40 +00004746 case WM_IME_COMPOSITION:
4747 if (!_OnImeComposition(hwnd, wParam, lParam))
K.Takata4ac893f2022-01-20 12:44:28 +00004748 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
Bram Moolenaar213ae482011-12-15 21:51:36 +01004749 return 1L;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004750#endif
K.Takatac81e9bf2022-01-16 14:15:49 +00004751 case WM_DPICHANGED:
4752 return _OnDpiChanged(hwnd, (UINT)LOWORD(wParam), (UINT)HIWORD(wParam),
4753 (RECT*)lParam);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004754
4755 default:
Bram Moolenaar071d4272004-06-13 20:20:40 +00004756#ifdef MSWIN_FIND_REPLACE
Bram Moolenaarcea912a2016-10-12 14:20:24 +02004757 if (uMsg == s_findrep_msg && s_findrep_msg != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004758 _OnFindRepl();
Bram Moolenaar071d4272004-06-13 20:20:40 +00004759#endif
K.Takata92000e22022-01-20 15:10:57 +00004760 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004761 }
4762
K.Takata4ac893f2022-01-20 12:44:28 +00004763 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004764}
4765
4766/*
4767 * End of call-back routines
4768 */
4769
Bram Moolenaar734a8672019-12-02 22:49:38 +01004770// parent window, if specified with -P
Bram Moolenaar071d4272004-06-13 20:20:40 +00004771HWND vim_parent_hwnd = NULL;
4772
4773 static BOOL CALLBACK
4774FindWindowTitle(HWND hwnd, LPARAM lParam)
4775{
4776 char buf[2048];
4777 char *title = (char *)lParam;
4778
4779 if (GetWindowText(hwnd, buf, sizeof(buf)))
4780 {
4781 if (strstr(buf, title) != NULL)
4782 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01004783 // Found it. Store the window ref. and quit searching if MDI
4784 // works.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004785 vim_parent_hwnd = FindWindowEx(hwnd, NULL, "MDIClient", NULL);
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00004786 if (vim_parent_hwnd != NULL)
4787 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004788 }
4789 }
Bram Moolenaar734a8672019-12-02 22:49:38 +01004790 return TRUE; // continue searching
Bram Moolenaar071d4272004-06-13 20:20:40 +00004791}
4792
4793/*
4794 * Invoked for '-P "title"' argument: search for parent application to open
4795 * our window in.
4796 */
4797 void
4798gui_mch_set_parent(char *title)
4799{
4800 EnumWindows(FindWindowTitle, (LPARAM)title);
4801 if (vim_parent_hwnd == NULL)
4802 {
Bram Moolenaara6f79292022-01-04 21:30:47 +00004803 semsg(_(e_cannot_find_window_title_str), title);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004804 mch_exit(2);
4805 }
4806}
4807
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +00004808#ifndef FEAT_OLE
Bram Moolenaar071d4272004-06-13 20:20:40 +00004809 static void
4810ole_error(char *arg)
4811{
Bram Moolenaaraea02fa2007-05-04 20:29:09 +00004812 char buf[IOSIZE];
4813
Bram Moolenaar0b75f7c2019-05-08 22:28:46 +02004814# ifdef VIMDLL
4815 gui.in_use = mch_is_gui_executable();
4816# endif
4817
Bram Moolenaar734a8672019-12-02 22:49:38 +01004818 // Can't use emsg() here, we have not finished initialisation yet.
Bram Moolenaaraea02fa2007-05-04 20:29:09 +00004819 vim_snprintf(buf, IOSIZE,
Bram Moolenaarcbadefe2022-01-01 19:33:50 +00004820 _(e_argument_not_supported_str_use_ole_version), arg);
Bram Moolenaaraea02fa2007-05-04 20:29:09 +00004821 mch_errmsg(buf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004822}
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +00004823#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004824
Bram Moolenaarafde13b2019-04-28 19:46:49 +02004825#if defined(GUI_MAY_SPAWN) || defined(PROTO)
4826 static char *
4827gvim_error(void)
4828{
Bram Moolenaard82a47d2022-01-05 20:24:39 +00004829 char *msg = _(e_gui_cannot_be_used_cannot_execute_gvim_exe);
Bram Moolenaarafde13b2019-04-28 19:46:49 +02004830
4831 if (starting)
4832 {
4833 mch_errmsg(msg);
4834 mch_errmsg("\n");
4835 mch_exit(2);
4836 }
4837 return msg;
4838}
4839
4840 char *
4841gui_mch_do_spawn(char_u *arg)
4842{
4843 int len;
4844# if defined(FEAT_SESSION) && defined(EXPERIMENTAL_GUI_CMD)
4845 char_u *session = NULL;
4846 LPWSTR tofree1 = NULL;
4847# endif
4848 WCHAR name[MAX_PATH];
4849 LPWSTR cmd, newcmd = NULL, p, warg, tofree2 = NULL;
4850 STARTUPINFOW si = {sizeof(si)};
4851 PROCESS_INFORMATION pi;
4852
4853 if (!GetModuleFileNameW(g_hinst, name, MAX_PATH))
4854 goto error;
4855 p = wcsrchr(name, L'\\');
4856 if (p == NULL)
4857 goto error;
4858 // Replace the executable name from vim(d).exe to gvim(d).exe.
4859# ifdef DEBUG
4860 wcscpy(p + 1, L"gvimd.exe");
4861# else
4862 wcscpy(p + 1, L"gvim.exe");
4863# endif
4864
4865# if defined(FEAT_SESSION) && defined(EXPERIMENTAL_GUI_CMD)
4866 if (starting)
4867# endif
4868 {
4869 // Pass the command line to the new process.
4870 p = GetCommandLineW();
4871 // Skip 1st argument.
4872 while (*p && *p != L' ' && *p != L'\t')
4873 {
4874 if (*p == L'"')
4875 {
4876 while (*p && *p != L'"')
4877 ++p;
4878 if (*p)
4879 ++p;
4880 }
4881 else
4882 ++p;
4883 }
4884 cmd = p;
4885 }
4886# if defined(FEAT_SESSION) && defined(EXPERIMENTAL_GUI_CMD)
4887 else
4888 {
4889 // Create a session file and pass it to the new process.
4890 LPWSTR wsession;
4891 char_u *savebg;
4892 int ret;
4893
4894 session = vim_tempname('s', FALSE);
4895 if (session == NULL)
4896 goto error;
4897 savebg = p_bg;
4898 p_bg = vim_strsave((char_u *)"light"); // Set 'bg' to "light".
4899 ret = write_session_file(session);
4900 vim_free(p_bg);
4901 p_bg = savebg;
4902 if (!ret)
4903 goto error;
4904 wsession = enc_to_utf16(session, NULL);
4905 if (wsession == NULL)
4906 goto error;
4907 len = (int)wcslen(wsession) * 2 + 27 + 1;
Bram Moolenaarc799fe22019-05-28 23:08:19 +02004908 cmd = ALLOC_MULT(WCHAR, len);
Bram Moolenaarafde13b2019-04-28 19:46:49 +02004909 if (cmd == NULL)
4910 {
4911 vim_free(wsession);
4912 goto error;
4913 }
4914 tofree1 = cmd;
4915 _snwprintf(cmd, len, L" -S \"%s\" -c \"call delete('%s')\"",
4916 wsession, wsession);
4917 vim_free(wsession);
4918 }
4919# endif
4920
4921 // Check additional arguments to the `:gui` command.
4922 if (arg != NULL)
4923 {
4924 warg = enc_to_utf16(arg, NULL);
4925 if (warg == NULL)
4926 goto error;
4927 tofree2 = warg;
4928 }
4929 else
4930 warg = L"";
4931
4932 // Set up the new command line.
4933 len = (int)wcslen(name) + (int)wcslen(cmd) + (int)wcslen(warg) + 4;
Bram Moolenaarc799fe22019-05-28 23:08:19 +02004934 newcmd = ALLOC_MULT(WCHAR, len);
Bram Moolenaarafde13b2019-04-28 19:46:49 +02004935 if (newcmd == NULL)
4936 goto error;
4937 _snwprintf(newcmd, len, L"\"%s\"%s %s", name, cmd, warg);
4938
4939 // Spawn a new GUI process.
4940 if (!CreateProcessW(NULL, newcmd, NULL, NULL, TRUE, 0,
4941 NULL, NULL, &si, &pi))
4942 goto error;
4943 CloseHandle(pi.hProcess);
4944 CloseHandle(pi.hThread);
4945 mch_exit(0);
4946
4947error:
4948# if defined(FEAT_SESSION) && defined(EXPERIMENTAL_GUI_CMD)
4949 if (session)
4950 mch_remove(session);
4951 vim_free(session);
4952 vim_free(tofree1);
4953# endif
4954 vim_free(newcmd);
4955 vim_free(tofree2);
4956 return gvim_error();
4957}
4958#endif
4959
Bram Moolenaar071d4272004-06-13 20:20:40 +00004960/*
4961 * Parse the GUI related command-line arguments. Any arguments used are
4962 * deleted from argv, and *argc is decremented accordingly. This is called
K.Takataeeec2542021-06-02 13:28:16 +02004963 * when Vim is started, whether or not the GUI has been started.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004964 */
4965 void
4966gui_mch_prepare(int *argc, char **argv)
4967{
4968 int silent = FALSE;
4969 int idx;
4970
Bram Moolenaar734a8672019-12-02 22:49:38 +01004971 // Check for special OLE command line parameters
Bram Moolenaar071d4272004-06-13 20:20:40 +00004972 if ((*argc == 2 || *argc == 3) && (argv[1][0] == '-' || argv[1][0] == '/'))
4973 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01004974 // Check for a "-silent" argument first.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004975 if (*argc == 3 && STRICMP(argv[1] + 1, "silent") == 0
4976 && (argv[2][0] == '-' || argv[2][0] == '/'))
4977 {
4978 silent = TRUE;
4979 idx = 2;
4980 }
4981 else
4982 idx = 1;
4983
Bram Moolenaar734a8672019-12-02 22:49:38 +01004984 // Register Vim as an OLE Automation server
Bram Moolenaar071d4272004-06-13 20:20:40 +00004985 if (STRICMP(argv[idx] + 1, "register") == 0)
4986 {
4987#ifdef FEAT_OLE
4988 RegisterMe(silent);
4989 mch_exit(0);
4990#else
4991 if (!silent)
4992 ole_error("register");
4993 mch_exit(2);
4994#endif
4995 }
4996
Bram Moolenaar734a8672019-12-02 22:49:38 +01004997 // Unregister Vim as an OLE Automation server
Bram Moolenaar071d4272004-06-13 20:20:40 +00004998 if (STRICMP(argv[idx] + 1, "unregister") == 0)
4999 {
5000#ifdef FEAT_OLE
5001 UnregisterMe(!silent);
5002 mch_exit(0);
5003#else
5004 if (!silent)
5005 ole_error("unregister");
5006 mch_exit(2);
5007#endif
5008 }
5009
Bram Moolenaar734a8672019-12-02 22:49:38 +01005010 // Ignore an -embedding argument. It is only relevant if the
5011 // application wants to treat the case when it is started manually
5012 // differently from the case where it is started via automation (and
5013 // we don't).
Bram Moolenaar071d4272004-06-13 20:20:40 +00005014 if (STRICMP(argv[idx] + 1, "embedding") == 0)
5015 {
5016#ifdef FEAT_OLE
5017 *argc = 1;
5018#else
5019 ole_error("embedding");
5020 mch_exit(2);
5021#endif
5022 }
5023 }
5024
5025#ifdef FEAT_OLE
5026 {
5027 int bDoRestart = FALSE;
5028
5029 InitOLE(&bDoRestart);
Bram Moolenaar734a8672019-12-02 22:49:38 +01005030 // automatically exit after registering
Bram Moolenaar071d4272004-06-13 20:20:40 +00005031 if (bDoRestart)
5032 mch_exit(0);
5033 }
5034#endif
5035
5036#ifdef FEAT_NETBEANS_INTG
5037 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01005038 // stolen from gui_x11.c
Bram Moolenaar071d4272004-06-13 20:20:40 +00005039 int arg;
5040
5041 for (arg = 1; arg < *argc; arg++)
5042 if (strncmp("-nb", argv[arg], 3) == 0)
5043 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00005044 netbeansArg = argv[arg];
5045 mch_memmove(&argv[arg], &argv[arg + 1],
5046 (--*argc - arg) * sizeof(char *));
5047 argv[*argc] = NULL;
Bram Moolenaar734a8672019-12-02 22:49:38 +01005048 break; // enough?
Bram Moolenaar071d4272004-06-13 20:20:40 +00005049 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005050 }
5051#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005052}
5053
K.Takatac81e9bf2022-01-16 14:15:49 +00005054 static void
5055load_dpi_func(void)
5056{
5057 HMODULE hUser32;
5058
5059 hUser32 = GetModuleHandle("user32.dll");
5060 if (hUser32 == NULL)
5061 goto fail;
5062
5063 pGetDpiForSystem = (void*)GetProcAddress(hUser32, "GetDpiForSystem");
5064 pGetDpiForWindow = (void*)GetProcAddress(hUser32, "GetDpiForWindow");
5065 pGetSystemMetricsForDpi = (void*)GetProcAddress(hUser32, "GetSystemMetricsForDpi");
5066 //pGetWindowDpiAwarenessContext = (void*)GetProcAddress(hUser32, "GetWindowDpiAwarenessContext");
5067 pSetThreadDpiAwarenessContext = (void*)GetProcAddress(hUser32, "SetThreadDpiAwarenessContext");
5068 pGetAwarenessFromDpiAwarenessContext = (void*)GetProcAddress(hUser32, "GetAwarenessFromDpiAwarenessContext");
5069
5070 if (pSetThreadDpiAwarenessContext != NULL)
5071 {
5072 DPI_AWARENESS_CONTEXT oldctx = pSetThreadDpiAwarenessContext(
5073 DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
5074 if (oldctx != NULL)
5075 {
5076 TRACE("DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 enabled");
5077 s_process_dpi_aware = pGetAwarenessFromDpiAwarenessContext(oldctx);
5078#ifdef DEBUG
5079 if (s_process_dpi_aware == DPI_AWARENESS_UNAWARE)
5080 {
5081 TRACE("WARNING: PerMonitorV2 is not enabled in the process level for some reasons. IME window may not shown correctly.");
5082 }
5083#endif
5084 return;
5085 }
5086 }
5087
5088fail:
5089 // Disable PerMonitorV2 APIs.
5090 pGetDpiForSystem = stubGetDpiForSystem;
5091 pGetDpiForWindow = NULL;
5092 pGetSystemMetricsForDpi = stubGetSystemMetricsForDpi;
5093 pSetThreadDpiAwarenessContext = NULL;
5094 pGetAwarenessFromDpiAwarenessContext = NULL;
5095}
5096
Bram Moolenaar071d4272004-06-13 20:20:40 +00005097/*
5098 * Initialise the GUI. Create all the windows, set up all the call-backs
5099 * etc.
5100 */
5101 int
5102gui_mch_init(void)
5103{
Bram Moolenaar071d4272004-06-13 20:20:40 +00005104 const WCHAR szVimWndClassW[] = VIM_CLASSW;
Bram Moolenaar33d0b692010-02-17 16:31:32 +01005105 const WCHAR szTextAreaClassW[] = L"VimTextArea";
Bram Moolenaar071d4272004-06-13 20:20:40 +00005106 WNDCLASSW wndclassw;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005107
Bram Moolenaar734a8672019-12-02 22:49:38 +01005108 // Return here if the window was already opened (happens when
5109 // gui_mch_dialog() is called early).
Bram Moolenaar071d4272004-06-13 20:20:40 +00005110 if (s_hwnd != NULL)
Bram Moolenaar748bf032005-02-02 23:04:36 +00005111 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005112
5113 /*
5114 * Load the tearoff bitmap
5115 */
5116#ifdef FEAT_TEAROFF
Bram Moolenaarafde13b2019-04-28 19:46:49 +02005117 s_htearbitmap = LoadBitmap(g_hinst, "IDB_TEAROFF");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005118#endif
5119
K.Takatac81e9bf2022-01-16 14:15:49 +00005120 load_dpi_func();
5121
5122 s_dpi = pGetDpiForSystem();
5123 update_scrollbar_size();
5124
Bram Moolenaar071d4272004-06-13 20:20:40 +00005125#ifdef FEAT_MENU
Bram Moolenaar734a8672019-12-02 22:49:38 +01005126 gui.menu_height = 0; // Windows takes care of this
Bram Moolenaar071d4272004-06-13 20:20:40 +00005127#endif
5128 gui.border_width = 0;
K.Takatac81e9bf2022-01-16 14:15:49 +00005129#ifdef FEAT_TOOLBAR
5130 gui.toolbar_height = TOOLBAR_BUTTON_HEIGHT + TOOLBAR_BORDER_HEIGHT;
5131#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005132
5133 s_brush = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
5134
Bram Moolenaar734a8672019-12-02 22:49:38 +01005135 // First try using the wide version, so that we can use any title.
5136 // Otherwise only characters in the active codepage will work.
Bram Moolenaarafde13b2019-04-28 19:46:49 +02005137 if (GetClassInfoW(g_hinst, szVimWndClassW, &wndclassw) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005138 {
Bram Moolenaar910f66f2006-04-05 20:41:53 +00005139 wndclassw.style = CS_DBLCLKS;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005140 wndclassw.lpfnWndProc = _WndProc;
5141 wndclassw.cbClsExtra = 0;
5142 wndclassw.cbWndExtra = 0;
Bram Moolenaarafde13b2019-04-28 19:46:49 +02005143 wndclassw.hInstance = g_hinst;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005144 wndclassw.hIcon = LoadIcon(wndclassw.hInstance, "IDR_VIM");
5145 wndclassw.hCursor = LoadCursor(NULL, IDC_ARROW);
5146 wndclassw.hbrBackground = s_brush;
5147 wndclassw.lpszMenuName = NULL;
5148 wndclassw.lpszClassName = szVimWndClassW;
5149
K.Takata4ac893f2022-01-20 12:44:28 +00005150 if (RegisterClassW(&wndclassw) == 0)
Bram Moolenaarcea912a2016-10-12 14:20:24 +02005151 return FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005152 }
5153
Bram Moolenaar071d4272004-06-13 20:20:40 +00005154 if (vim_parent_hwnd != NULL)
5155 {
5156#ifdef HAVE_TRY_EXCEPT
5157 __try
5158 {
5159#endif
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02005160 // Open inside the specified parent window.
5161 // TODO: last argument should point to a CLIENTCREATESTRUCT
5162 // structure.
5163 s_hwnd = CreateWindowExW(
Bram Moolenaar071d4272004-06-13 20:20:40 +00005164 WS_EX_MDICHILD,
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02005165 szVimWndClassW, L"Vim MSWindows GUI",
Bram Moolenaare78c2062011-08-10 15:56:27 +02005166 WS_OVERLAPPEDWINDOW | WS_CHILD
5167 | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | 0xC000,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005168 gui_win_x == -1 ? CW_USEDEFAULT : gui_win_x,
5169 gui_win_y == -1 ? CW_USEDEFAULT : gui_win_y,
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02005170 100, // Any value will do
5171 100, // Any value will do
Bram Moolenaar071d4272004-06-13 20:20:40 +00005172 vim_parent_hwnd, NULL,
Bram Moolenaarafde13b2019-04-28 19:46:49 +02005173 g_hinst, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005174#ifdef HAVE_TRY_EXCEPT
5175 }
5176 __except(EXCEPTION_EXECUTE_HANDLER)
5177 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01005178 // NOP
Bram Moolenaar071d4272004-06-13 20:20:40 +00005179 }
5180#endif
5181 if (s_hwnd == NULL)
5182 {
Bram Moolenaara6f79292022-01-04 21:30:47 +00005183 emsg(_(e_unable_to_open_window_inside_mdi_application));
Bram Moolenaar071d4272004-06-13 20:20:40 +00005184 mch_exit(2);
5185 }
5186 }
5187 else
Bram Moolenaar78e17622007-08-30 10:26:19 +00005188 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01005189 // If the provided windowid is not valid reset it to zero, so that it
5190 // is ignored and we open our own window.
Bram Moolenaar78e17622007-08-30 10:26:19 +00005191 if (IsWindow((HWND)win_socket_id) <= 0)
5192 win_socket_id = 0;
5193
Bram Moolenaar734a8672019-12-02 22:49:38 +01005194 // Create a window. If win_socket_id is not zero without border and
5195 // titlebar, it will be reparented below.
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02005196 s_hwnd = CreateWindowW(
5197 szVimWndClassW, L"Vim MSWindows GUI",
Bram Moolenaare78c2062011-08-10 15:56:27 +02005198 (win_socket_id == 0 ? WS_OVERLAPPEDWINDOW : WS_POPUP)
5199 | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
Bram Moolenaar78e17622007-08-30 10:26:19 +00005200 gui_win_x == -1 ? CW_USEDEFAULT : gui_win_x,
5201 gui_win_y == -1 ? CW_USEDEFAULT : gui_win_y,
Bram Moolenaar734a8672019-12-02 22:49:38 +01005202 100, // Any value will do
5203 100, // Any value will do
Bram Moolenaar78e17622007-08-30 10:26:19 +00005204 NULL, NULL,
Bram Moolenaarafde13b2019-04-28 19:46:49 +02005205 g_hinst, NULL);
Bram Moolenaar78e17622007-08-30 10:26:19 +00005206 if (s_hwnd != NULL && win_socket_id != 0)
5207 {
5208 SetParent(s_hwnd, (HWND)win_socket_id);
5209 ShowWindow(s_hwnd, SW_SHOWMAXIMIZED);
5210 }
5211 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005212
5213 if (s_hwnd == NULL)
5214 return FAIL;
5215
K.Takatac81e9bf2022-01-16 14:15:49 +00005216 if (pGetDpiForWindow != NULL)
5217 {
5218 s_dpi = pGetDpiForWindow(s_hwnd);
5219 update_scrollbar_size();
5220 //TRACE("System DPI: %d, DPI: %d", pGetDpiForSystem(), s_dpi);
5221 }
5222
Bram Moolenaar071d4272004-06-13 20:20:40 +00005223#if defined(FEAT_MBYTE_IME) && defined(DYNAMIC_IME)
5224 dyn_imm_load();
5225#endif
5226
Bram Moolenaar734a8672019-12-02 22:49:38 +01005227 // Create the text area window
Bram Moolenaarafde13b2019-04-28 19:46:49 +02005228 if (GetClassInfoW(g_hinst, szTextAreaClassW, &wndclassw) == 0)
Bram Moolenaar33d0b692010-02-17 16:31:32 +01005229 {
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02005230 wndclassw.style = CS_OWNDC;
5231 wndclassw.lpfnWndProc = _TextAreaWndProc;
5232 wndclassw.cbClsExtra = 0;
5233 wndclassw.cbWndExtra = 0;
Bram Moolenaarafde13b2019-04-28 19:46:49 +02005234 wndclassw.hInstance = g_hinst;
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02005235 wndclassw.hIcon = NULL;
5236 wndclassw.hCursor = LoadCursor(NULL, IDC_ARROW);
5237 wndclassw.hbrBackground = NULL;
5238 wndclassw.lpszMenuName = NULL;
5239 wndclassw.lpszClassName = szTextAreaClassW;
Bram Moolenaar33d0b692010-02-17 16:31:32 +01005240
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02005241 if (RegisterClassW(&wndclassw) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005242 return FAIL;
5243 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005244
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02005245 s_textArea = CreateWindowExW(
5246 0,
5247 szTextAreaClassW, L"Vim text area",
5248 WS_CHILD | WS_VISIBLE, 0, 0,
5249 100, // Any value will do for now
5250 100, // Any value will do for now
5251 s_hwnd, NULL,
Bram Moolenaarafde13b2019-04-28 19:46:49 +02005252 g_hinst, NULL);
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02005253
Bram Moolenaar071d4272004-06-13 20:20:40 +00005254 if (s_textArea == NULL)
5255 return FAIL;
5256
Bram Moolenaar20321902016-02-17 12:30:17 +01005257#ifdef FEAT_LIBCALL
Bram Moolenaar734a8672019-12-02 22:49:38 +01005258 // Try loading an icon from $RUNTIMEPATH/bitmaps/vim.ico.
Bram Moolenaarcddc91c2014-09-23 21:53:41 +02005259 {
5260 HANDLE hIcon = NULL;
5261
5262 if (mch_icon_load(&hIcon) == OK && hIcon != NULL)
Bram Moolenaar0f519a02014-10-06 18:10:09 +02005263 SendMessage(s_hwnd, WM_SETICON, ICON_SMALL, (LPARAM)hIcon);
Bram Moolenaarcddc91c2014-09-23 21:53:41 +02005264 }
Bram Moolenaar20321902016-02-17 12:30:17 +01005265#endif
Bram Moolenaarcddc91c2014-09-23 21:53:41 +02005266
Bram Moolenaar071d4272004-06-13 20:20:40 +00005267#ifdef FEAT_MENU
5268 s_menuBar = CreateMenu();
5269#endif
5270 s_hdc = GetDC(s_textArea);
5271
Bram Moolenaar071d4272004-06-13 20:20:40 +00005272 DragAcceptFiles(s_hwnd, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005273
Bram Moolenaar734a8672019-12-02 22:49:38 +01005274 // Do we need to bother with this?
K.Takatac81e9bf2022-01-16 14:15:49 +00005275 // m_fMouseAvail = pGetSystemMetricsForDpi(SM_MOUSEPRESENT, s_dpi);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005276
Bram Moolenaar734a8672019-12-02 22:49:38 +01005277 // Get background/foreground colors from the system
Bram Moolenaar071d4272004-06-13 20:20:40 +00005278 gui_mch_def_colors();
5279
Bram Moolenaar734a8672019-12-02 22:49:38 +01005280 // Get the colors from the "Normal" group (set in syntax.c or in a vimrc
5281 // file)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005282 set_normal_colors();
5283
5284 /*
5285 * Check that none of the colors are the same as the background color.
5286 * Then store the current values as the defaults.
5287 */
5288 gui_check_colors();
5289 gui.def_norm_pixel = gui.norm_pixel;
5290 gui.def_back_pixel = gui.back_pixel;
5291
Bram Moolenaar734a8672019-12-02 22:49:38 +01005292 // Get the colors for the highlight groups (gui_check_colors() might have
5293 // changed them)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005294 highlight_gui_started();
5295
5296 /*
Bram Moolenaar97b0b0e2015-11-19 20:23:37 +01005297 * Start out by adding the configured border width into the border offset.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005298 */
Bram Moolenaar97b0b0e2015-11-19 20:23:37 +01005299 gui.border_offset = gui.border_width;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005300
5301 /*
5302 * Set up for Intellimouse processing
5303 */
5304 init_mouse_wheel();
5305
5306 /*
5307 * compute a couple of metrics used for the dialogs
5308 */
5309 get_dialog_font_metrics();
5310#ifdef FEAT_TOOLBAR
5311 /*
5312 * Create the toolbar
5313 */
5314 initialise_toolbar();
5315#endif
Bram Moolenaar3991dab2006-03-27 17:01:56 +00005316#ifdef FEAT_GUI_TABLINE
5317 /*
5318 * Create the tabline
5319 */
5320 initialise_tabline();
5321#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005322#ifdef MSWIN_FIND_REPLACE
5323 /*
5324 * Initialise the dialog box stuff
5325 */
5326 s_findrep_msg = RegisterWindowMessage(FINDMSGSTRING);
5327
Bram Moolenaar734a8672019-12-02 22:49:38 +01005328 // Initialise the struct
Bram Moolenaar071d4272004-06-13 20:20:40 +00005329 s_findrep_struct.lStructSize = sizeof(s_findrep_struct);
Bram Moolenaarc799fe22019-05-28 23:08:19 +02005330 s_findrep_struct.lpstrFindWhat = ALLOC_MULT(WCHAR, MSWIN_FR_BUFSIZE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005331 s_findrep_struct.lpstrFindWhat[0] = NUL;
Bram Moolenaarc799fe22019-05-28 23:08:19 +02005332 s_findrep_struct.lpstrReplaceWith = ALLOC_MULT(WCHAR, MSWIN_FR_BUFSIZE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005333 s_findrep_struct.lpstrReplaceWith[0] = NUL;
5334 s_findrep_struct.wFindWhatLen = MSWIN_FR_BUFSIZE;
5335 s_findrep_struct.wReplaceWithLen = MSWIN_FR_BUFSIZE;
5336#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005337
Bram Moolenaar264e9fd2010-10-27 12:33:17 +02005338#ifdef FEAT_EVAL
Bram Moolenaar734a8672019-12-02 22:49:38 +01005339 // set the v:windowid variable
Bram Moolenaar7154b322011-05-25 21:18:06 +02005340 set_vim_var_nr(VV_WINDOWID, HandleToLong(s_hwnd));
Bram Moolenaar264e9fd2010-10-27 12:33:17 +02005341#endif
5342
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02005343#ifdef FEAT_RENDER_OPTIONS
5344 if (p_rop)
5345 (void)gui_mch_set_rendering_options(p_rop);
5346#endif
5347
Bram Moolenaar748bf032005-02-02 23:04:36 +00005348theend:
Bram Moolenaar734a8672019-12-02 22:49:38 +01005349 // Display any pending error messages
Bram Moolenaar748bf032005-02-02 23:04:36 +00005350 display_errors();
5351
Bram Moolenaar071d4272004-06-13 20:20:40 +00005352 return OK;
5353}
5354
5355/*
5356 * Get the size of the screen, taking position on multiple monitors into
5357 * account (if supported).
5358 */
5359 static void
5360get_work_area(RECT *spi_rect)
5361{
Bram Moolenaarcea912a2016-10-12 14:20:24 +02005362 HMONITOR mon;
5363 MONITORINFO moninfo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005364
Bram Moolenaar734a8672019-12-02 22:49:38 +01005365 // work out which monitor the window is on, and get *its* work area
Bram Moolenaar87f3d202016-12-01 20:18:50 +01005366 mon = MonitorFromWindow(s_hwnd, MONITOR_DEFAULTTOPRIMARY);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02005367 if (mon != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005368 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +02005369 moninfo.cbSize = sizeof(MONITORINFO);
5370 if (GetMonitorInfo(mon, &moninfo))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005371 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +02005372 *spi_rect = moninfo.rcWork;
5373 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005374 }
5375 }
Bram Moolenaar734a8672019-12-02 22:49:38 +01005376 // this is the old method...
Bram Moolenaar071d4272004-06-13 20:20:40 +00005377 SystemParametersInfo(SPI_GETWORKAREA, 0, spi_rect, 0);
5378}
5379
5380/*
5381 * Set the size of the window to the given width and height in pixels.
5382 */
5383 void
Bram Moolenaar1266d672017-02-01 13:43:36 +01005384gui_mch_set_shellsize(
5385 int width,
5386 int height,
5387 int min_width UNUSED,
5388 int min_height UNUSED,
5389 int base_width UNUSED,
5390 int base_height UNUSED,
Bram Moolenaarafa24992006-03-27 20:58:26 +00005391 int direction)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005392{
5393 RECT workarea_rect;
Bram Moolenaar98af99f2020-07-16 22:30:31 +02005394 RECT window_rect;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005395 int win_width, win_height;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005396
Bram Moolenaar734a8672019-12-02 22:49:38 +01005397 // Try to keep window completely on screen.
5398 // Get position of the screen work area. This is the part that is not
5399 // used by the taskbar or appbars.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005400 get_work_area(&workarea_rect);
5401
Bram Moolenaar734a8672019-12-02 22:49:38 +01005402 // Resizing a maximized window looks very strange, unzoom it first.
5403 // But don't do it when still starting up, it may have been requested in
5404 // the shortcut.
Bram Moolenaar98af99f2020-07-16 22:30:31 +02005405 if (IsZoomed(s_hwnd) && starting == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005406 ShowWindow(s_hwnd, SW_SHOWNORMAL);
Bram Moolenaar98af99f2020-07-16 22:30:31 +02005407
5408 GetWindowRect(s_hwnd, &window_rect);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005409
Bram Moolenaar734a8672019-12-02 22:49:38 +01005410 // compute the size of the outside of the window
K.Takatac81e9bf2022-01-16 14:15:49 +00005411 win_width = width + (pGetSystemMetricsForDpi(SM_CXFRAME, s_dpi) +
5412 pGetSystemMetricsForDpi(SM_CXPADDEDBORDER, s_dpi)) * 2;
5413 win_height = height + (pGetSystemMetricsForDpi(SM_CYFRAME, s_dpi) +
5414 pGetSystemMetricsForDpi(SM_CXPADDEDBORDER, s_dpi)) * 2
5415 + pGetSystemMetricsForDpi(SM_CYCAPTION, s_dpi)
5416 + gui_mswin_get_menu_height(FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005417
Bram Moolenaar734a8672019-12-02 22:49:38 +01005418 // The following should take care of keeping Vim on the same monitor, no
5419 // matter if the secondary monitor is left or right of the primary
5420 // monitor.
Bram Moolenaar98af99f2020-07-16 22:30:31 +02005421 window_rect.right = window_rect.left + win_width;
5422 window_rect.bottom = window_rect.top + win_height;
Bram Moolenaar56a907a2006-05-06 21:44:30 +00005423
Bram Moolenaar734a8672019-12-02 22:49:38 +01005424 // If the window is going off the screen, move it on to the screen.
Bram Moolenaar98af99f2020-07-16 22:30:31 +02005425 if ((direction & RESIZE_HOR) && window_rect.right > workarea_rect.right)
5426 OffsetRect(&window_rect, workarea_rect.right - window_rect.right, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005427
Bram Moolenaar98af99f2020-07-16 22:30:31 +02005428 if ((direction & RESIZE_HOR) && window_rect.left < workarea_rect.left)
5429 OffsetRect(&window_rect, workarea_rect.left - window_rect.left, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005430
Bram Moolenaar98af99f2020-07-16 22:30:31 +02005431 if ((direction & RESIZE_VERT) && window_rect.bottom > workarea_rect.bottom)
5432 OffsetRect(&window_rect, 0, workarea_rect.bottom - window_rect.bottom);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005433
Bram Moolenaar98af99f2020-07-16 22:30:31 +02005434 if ((direction & RESIZE_VERT) && window_rect.top < workarea_rect.top)
5435 OffsetRect(&window_rect, 0, workarea_rect.top - window_rect.top);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005436
Bram Moolenaar98af99f2020-07-16 22:30:31 +02005437 MoveWindow(s_hwnd, window_rect.left, window_rect.top,
5438 win_width, win_height, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005439
5440 SetActiveWindow(s_hwnd);
5441 SetFocus(s_hwnd);
5442
Bram Moolenaar734a8672019-12-02 22:49:38 +01005443 // Menu may wrap differently now
Bram Moolenaar071d4272004-06-13 20:20:40 +00005444 gui_mswin_get_menu_height(!gui.starting);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005445}
5446
5447
5448 void
5449gui_mch_set_scrollbar_thumb(
5450 scrollbar_T *sb,
5451 long val,
5452 long size,
5453 long max)
5454{
5455 SCROLLINFO info;
5456
5457 sb->scroll_shift = 0;
5458 while (max > 32767)
5459 {
5460 max = (max + 1) >> 1;
5461 val >>= 1;
5462 size >>= 1;
5463 ++sb->scroll_shift;
5464 }
5465
5466 if (sb->scroll_shift > 0)
5467 ++size;
5468
5469 info.cbSize = sizeof(info);
5470 info.fMask = SIF_POS | SIF_RANGE | SIF_PAGE;
5471 info.nPos = val;
5472 info.nMin = 0;
5473 info.nMax = max;
5474 info.nPage = size;
5475 SetScrollInfo(sb->id, SB_CTL, &info, TRUE);
5476}
5477
5478
5479/*
5480 * Set the current text font.
5481 */
5482 void
5483gui_mch_set_font(GuiFont font)
5484{
5485 gui.currFont = font;
5486}
5487
5488
5489/*
5490 * Set the current text foreground color.
5491 */
5492 void
5493gui_mch_set_fg_color(guicolor_T color)
5494{
5495 gui.currFgColor = color;
5496}
5497
5498/*
5499 * Set the current text background color.
5500 */
5501 void
5502gui_mch_set_bg_color(guicolor_T color)
5503{
5504 gui.currBgColor = color;
5505}
5506
Bram Moolenaare2cc9702005-03-15 22:43:58 +00005507/*
5508 * Set the current text special color.
5509 */
5510 void
5511gui_mch_set_sp_color(guicolor_T color)
5512{
5513 gui.currSpColor = color;
5514}
5515
Bram Moolenaarbdb81392017-11-27 23:24:08 +01005516#ifdef FEAT_MBYTE_IME
Bram Moolenaar071d4272004-06-13 20:20:40 +00005517/*
5518 * Multi-byte handling, originally by Sung-Hoon Baek.
5519 * First static functions (no prototypes generated).
5520 */
Bram Moolenaarbdb81392017-11-27 23:24:08 +01005521# ifdef _MSC_VER
Bram Moolenaar734a8672019-12-02 22:49:38 +01005522# include <ime.h> // Apparently not needed for Cygwin or MinGW.
Bram Moolenaarbdb81392017-11-27 23:24:08 +01005523# endif
5524# include <imm.h>
Bram Moolenaar071d4272004-06-13 20:20:40 +00005525
5526/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00005527 * handle WM_IME_NOTIFY message
5528 */
5529 static LRESULT
Bram Moolenaar1266d672017-02-01 13:43:36 +01005530_OnImeNotify(HWND hWnd, DWORD dwCommand, DWORD dwData UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005531{
5532 LRESULT lResult = 0;
5533 HIMC hImc;
5534
5535 if (!pImmGetContext || (hImc = pImmGetContext(hWnd)) == (HIMC)0)
5536 return lResult;
5537 switch (dwCommand)
5538 {
5539 case IMN_SETOPENSTATUS:
5540 if (pImmGetOpenStatus(hImc))
5541 {
K.Takatac81e9bf2022-01-16 14:15:49 +00005542 LOGFONTW lf = norm_logfont;
5543 if (s_process_dpi_aware == DPI_AWARENESS_UNAWARE)
5544 // Work around when PerMonitorV2 is not enabled in the process level.
5545 lf.lfHeight = lf.lfHeight * DEFAULT_DPI / s_dpi;
5546 pImmSetCompositionFontW(hImc, &lf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005547 im_set_position(gui.row, gui.col);
5548
Bram Moolenaar734a8672019-12-02 22:49:38 +01005549 // Disable langmap
Bram Moolenaar071d4272004-06-13 20:20:40 +00005550 State &= ~LANGMAP;
5551 if (State & INSERT)
5552 {
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01005553# if defined(FEAT_KEYMAP)
Bram Moolenaar734a8672019-12-02 22:49:38 +01005554 // Unshown 'keymap' in status lines
Bram Moolenaar071d4272004-06-13 20:20:40 +00005555 if (curbuf->b_p_iminsert == B_IMODE_LMAP)
5556 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01005557 // Save cursor position
Bram Moolenaar071d4272004-06-13 20:20:40 +00005558 int old_row = gui.row;
5559 int old_col = gui.col;
5560
5561 // This must be called here before
5562 // status_redraw_curbuf(), otherwise the mode
5563 // message may appear in the wrong position.
5564 showmode();
5565 status_redraw_curbuf();
5566 update_screen(0);
Bram Moolenaar734a8672019-12-02 22:49:38 +01005567 // Restore cursor position
Bram Moolenaar071d4272004-06-13 20:20:40 +00005568 gui.row = old_row;
5569 gui.col = old_col;
5570 }
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01005571# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005572 }
5573 }
5574 gui_update_cursor(TRUE, FALSE);
Bram Moolenaar92467d32017-12-05 13:22:16 +01005575 gui_mch_flush();
Bram Moolenaar071d4272004-06-13 20:20:40 +00005576 lResult = 0;
5577 break;
5578 }
5579 pImmReleaseContext(hWnd, hImc);
5580 return lResult;
5581}
5582
5583 static LRESULT
Bram Moolenaar1266d672017-02-01 13:43:36 +01005584_OnImeComposition(HWND hwnd, WPARAM dbcs UNUSED, LPARAM param)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005585{
5586 char_u *ret;
5587 int len;
5588
Bram Moolenaar734a8672019-12-02 22:49:38 +01005589 if ((param & GCS_RESULTSTR) == 0) // Composition unfinished.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005590 return 0;
5591
5592 ret = GetResultStr(hwnd, GCS_RESULTSTR, &len);
5593 if (ret != NULL)
5594 {
5595 add_to_input_buf_csi(ret, len);
5596 vim_free(ret);
5597 return 1;
5598 }
5599 return 0;
5600}
5601
5602/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00005603 * void GetResultStr()
5604 *
5605 * This handles WM_IME_COMPOSITION with GCS_RESULTSTR flag on.
5606 * get complete composition string
5607 */
5608 static char_u *
5609GetResultStr(HWND hwnd, int GCS, int *lenp)
5610{
Bram Moolenaar734a8672019-12-02 22:49:38 +01005611 HIMC hIMC; // Input context handle.
K.Takatab0b2b732022-01-19 12:59:21 +00005612 LONG ret;
5613 WCHAR *buf = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005614 char_u *convbuf = NULL;
5615
5616 if (!pImmGetContext || (hIMC = pImmGetContext(hwnd)) == (HIMC)0)
5617 return NULL;
5618
K.Takatab0b2b732022-01-19 12:59:21 +00005619 // Get the length of the composition string.
5620 ret = pImmGetCompositionStringW(hIMC, GCS, NULL, 0);
5621 if (ret <= 0)
5622 return NULL;
5623
5624 // Allocate the requested buffer plus space for the NUL character.
5625 buf = alloc(ret + sizeof(WCHAR));
Bram Moolenaar071d4272004-06-13 20:20:40 +00005626 if (buf == NULL)
5627 return NULL;
5628
K.Takatab0b2b732022-01-19 12:59:21 +00005629 // Reads in the composition string.
5630 pImmGetCompositionStringW(hIMC, GCS, buf, ret);
5631 *lenp = ret / sizeof(WCHAR);
5632
Bram Moolenaar36f692d2008-11-20 16:10:17 +00005633 convbuf = utf16_to_enc(buf, lenp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005634 pImmReleaseContext(hwnd, hIMC);
5635 vim_free(buf);
5636 return convbuf;
5637}
5638#endif
5639
Bram Moolenaar734a8672019-12-02 22:49:38 +01005640// For global functions we need prototypes.
Bram Moolenaarbdb81392017-11-27 23:24:08 +01005641#if defined(FEAT_MBYTE_IME) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005642
5643/*
5644 * set font to IM.
5645 */
5646 void
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01005647im_set_font(LOGFONTW *lf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005648{
5649 HIMC hImc;
5650
5651 if (pImmGetContext && (hImc = pImmGetContext(s_hwnd)) != (HIMC)0)
5652 {
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01005653 pImmSetCompositionFontW(hImc, lf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005654 pImmReleaseContext(s_hwnd, hImc);
5655 }
5656}
5657
5658/*
5659 * Notify cursor position to IM.
5660 */
5661 void
5662im_set_position(int row, int col)
5663{
5664 HIMC hImc;
5665
5666 if (pImmGetContext && (hImc = pImmGetContext(s_hwnd)) != (HIMC)0)
5667 {
5668 COMPOSITIONFORM cfs;
5669
5670 cfs.dwStyle = CFS_POINT;
5671 cfs.ptCurrentPos.x = FILL_X(col);
5672 cfs.ptCurrentPos.y = FILL_Y(row);
5673 MapWindowPoints(s_textArea, s_hwnd, &cfs.ptCurrentPos, 1);
K.Takatac81e9bf2022-01-16 14:15:49 +00005674 if (s_process_dpi_aware == DPI_AWARENESS_UNAWARE)
5675 {
5676 // Work around when PerMonitorV2 is not enabled in the process level.
5677 cfs.ptCurrentPos.x = cfs.ptCurrentPos.x * DEFAULT_DPI / s_dpi;
5678 cfs.ptCurrentPos.y = cfs.ptCurrentPos.y * DEFAULT_DPI / s_dpi;
5679 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005680 pImmSetCompositionWindow(hImc, &cfs);
5681
5682 pImmReleaseContext(s_hwnd, hImc);
5683 }
5684}
5685
5686/*
5687 * Set IM status on ("active" is TRUE) or off ("active" is FALSE).
5688 */
5689 void
5690im_set_active(int active)
5691{
5692 HIMC hImc;
5693 static HIMC hImcOld = (HIMC)0;
5694
Bram Moolenaar310c32e2019-11-29 23:15:25 +01005695# ifdef VIMDLL
5696 if (!gui.in_use && !gui.starting)
5697 {
5698 mbyte_im_set_active(active);
5699 return;
5700 }
5701# endif
5702
Bram Moolenaar734a8672019-12-02 22:49:38 +01005703 if (pImmGetContext) // if NULL imm32.dll wasn't loaded (yet)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005704 {
5705 if (p_imdisable)
5706 {
5707 if (hImcOld == (HIMC)0)
5708 {
5709 hImcOld = pImmGetContext(s_hwnd);
5710 if (hImcOld)
5711 pImmAssociateContext(s_hwnd, (HIMC)0);
5712 }
5713 active = FALSE;
5714 }
5715 else if (hImcOld != (HIMC)0)
5716 {
5717 pImmAssociateContext(s_hwnd, hImcOld);
5718 hImcOld = (HIMC)0;
5719 }
5720
5721 hImc = pImmGetContext(s_hwnd);
5722 if (hImc)
5723 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00005724 /*
5725 * for Korean ime
5726 */
5727 HKL hKL = GetKeyboardLayout(0);
5728
5729 if (LOWORD(hKL) == MAKELANGID(LANG_KOREAN, SUBLANG_KOREAN))
5730 {
5731 static DWORD dwConversionSaved = 0, dwSentenceSaved = 0;
5732 static BOOL bSaved = FALSE;
5733
5734 if (active)
5735 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01005736 // if we have a saved conversion status, restore it
Bram Moolenaarca003e12006-03-17 23:19:38 +00005737 if (bSaved)
5738 pImmSetConversionStatus(hImc, dwConversionSaved,
5739 dwSentenceSaved);
5740 bSaved = FALSE;
5741 }
5742 else
5743 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01005744 // save conversion status and disable korean
Bram Moolenaarca003e12006-03-17 23:19:38 +00005745 if (pImmGetConversionStatus(hImc, &dwConversionSaved,
5746 &dwSentenceSaved))
5747 {
5748 bSaved = TRUE;
5749 pImmSetConversionStatus(hImc,
5750 dwConversionSaved & ~(IME_CMODE_NATIVE
5751 | IME_CMODE_FULLSHAPE),
5752 dwSentenceSaved);
5753 }
5754 }
5755 }
5756
Bram Moolenaar071d4272004-06-13 20:20:40 +00005757 pImmSetOpenStatus(hImc, active);
5758 pImmReleaseContext(s_hwnd, hImc);
5759 }
5760 }
5761}
5762
5763/*
5764 * Get IM status. When IM is on, return not 0. Else return 0.
5765 */
5766 int
Bram Moolenaar68c2f632016-01-30 17:24:07 +01005767im_get_status(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005768{
5769 int status = 0;
5770 HIMC hImc;
5771
Bram Moolenaar310c32e2019-11-29 23:15:25 +01005772# ifdef VIMDLL
5773 if (!gui.in_use && !gui.starting)
5774 return mbyte_im_get_status();
5775# endif
5776
Bram Moolenaar071d4272004-06-13 20:20:40 +00005777 if (pImmGetContext && (hImc = pImmGetContext(s_hwnd)) != (HIMC)0)
5778 {
5779 status = pImmGetOpenStatus(hImc) ? 1 : 0;
5780 pImmReleaseContext(s_hwnd, hImc);
5781 }
5782 return status;
5783}
5784
Bram Moolenaar734a8672019-12-02 22:49:38 +01005785#endif // FEAT_MBYTE_IME
Bram Moolenaar071d4272004-06-13 20:20:40 +00005786
Bram Moolenaar071d4272004-06-13 20:20:40 +00005787
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005788/*
Bram Moolenaar39f05632006-03-19 22:15:26 +00005789 * Convert latin9 text "text[len]" to ucs-2 in "unicodebuf".
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005790 */
5791 static void
5792latin9_to_ucs(char_u *text, int len, WCHAR *unicodebuf)
5793{
5794 int c;
5795
Bram Moolenaarca003e12006-03-17 23:19:38 +00005796 while (--len >= 0)
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005797 {
5798 c = *text++;
5799 switch (c)
5800 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01005801 case 0xa4: c = 0x20ac; break; // euro
5802 case 0xa6: c = 0x0160; break; // S hat
5803 case 0xa8: c = 0x0161; break; // S -hat
5804 case 0xb4: c = 0x017d; break; // Z hat
5805 case 0xb8: c = 0x017e; break; // Z -hat
5806 case 0xbc: c = 0x0152; break; // OE
5807 case 0xbd: c = 0x0153; break; // oe
5808 case 0xbe: c = 0x0178; break; // Y
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005809 }
5810 *unicodebuf++ = c;
5811 }
5812}
Bram Moolenaar071d4272004-06-13 20:20:40 +00005813
5814#ifdef FEAT_RIGHTLEFT
5815/*
5816 * What is this for? In the case where you are using Win98 or Win2K or later,
5817 * and you are using a Hebrew font (or Arabic!), Windows does you a favor and
5818 * reverses the string sent to the TextOut... family. This sucks, because we
5819 * go to a lot of effort to do the right thing, and there doesn't seem to be a
5820 * way to tell Windblows not to do this!
5821 *
5822 * The short of it is that this 'RevOut' only gets called if you are running
5823 * one of the new, "improved" MS OSes, and only if you are running in
5824 * 'rightleft' mode. It makes display take *slightly* longer, but not
5825 * noticeably so.
5826 */
5827 static void
K.Takata135e1522022-01-29 15:27:58 +00005828RevOut( HDC hdc,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005829 int col,
5830 int row,
5831 UINT foptions,
5832 CONST RECT *pcliprect,
5833 LPCTSTR text,
5834 UINT len,
5835 CONST INT *padding)
5836{
5837 int ix;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005838
Bram Moolenaarcea912a2016-10-12 14:20:24 +02005839 for (ix = 0; ix < (int)len; ++ix)
K.Takata135e1522022-01-29 15:27:58 +00005840 ExtTextOut(hdc, col + TEXT_X(ix), row, foptions,
Bram Moolenaarcea912a2016-10-12 14:20:24 +02005841 pcliprect, text + ix, 1, padding);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005842}
5843#endif
5844
Bram Moolenaar92467d32017-12-05 13:22:16 +01005845 static void
5846draw_line(
5847 int x1,
Bram Moolenaard385b5d2018-12-27 22:43:08 +01005848 int y1,
5849 int x2,
5850 int y2,
Bram Moolenaar92467d32017-12-05 13:22:16 +01005851 COLORREF color)
5852{
5853#if defined(FEAT_DIRECTX)
5854 if (IS_ENABLE_DIRECTX())
5855 DWriteContext_DrawLine(s_dwc, x1, y1, x2, y2, color);
5856 else
5857#endif
5858 {
5859 HPEN hpen = CreatePen(PS_SOLID, 1, color);
5860 HPEN old_pen = SelectObject(s_hdc, hpen);
5861 MoveToEx(s_hdc, x1, y1, NULL);
Bram Moolenaar734a8672019-12-02 22:49:38 +01005862 // Note: LineTo() excludes the last pixel in the line.
Bram Moolenaar92467d32017-12-05 13:22:16 +01005863 LineTo(s_hdc, x2, y2);
5864 DeleteObject(SelectObject(s_hdc, old_pen));
5865 }
5866}
5867
5868 static void
5869set_pixel(
5870 int x,
Bram Moolenaard385b5d2018-12-27 22:43:08 +01005871 int y,
Bram Moolenaar92467d32017-12-05 13:22:16 +01005872 COLORREF color)
5873{
5874#if defined(FEAT_DIRECTX)
5875 if (IS_ENABLE_DIRECTX())
5876 DWriteContext_SetPixel(s_dwc, x, y, color);
5877 else
5878#endif
5879 SetPixel(s_hdc, x, y, color);
5880}
5881
5882 static void
5883fill_rect(
5884 const RECT *rcp,
Bram Moolenaard385b5d2018-12-27 22:43:08 +01005885 HBRUSH hbr,
Bram Moolenaar92467d32017-12-05 13:22:16 +01005886 COLORREF color)
5887{
5888#if defined(FEAT_DIRECTX)
5889 if (IS_ENABLE_DIRECTX())
5890 DWriteContext_FillRect(s_dwc, rcp, color);
5891 else
5892#endif
5893 {
5894 HBRUSH hbr2;
5895
5896 if (hbr == NULL)
5897 hbr2 = CreateSolidBrush(color);
5898 else
5899 hbr2 = hbr;
5900 FillRect(s_hdc, rcp, hbr2);
5901 if (hbr == NULL)
5902 DeleteBrush(hbr2);
5903 }
5904}
5905
Bram Moolenaar071d4272004-06-13 20:20:40 +00005906 void
5907gui_mch_draw_string(
5908 int row,
5909 int col,
5910 char_u *text,
5911 int len,
5912 int flags)
5913{
5914 static int *padding = NULL;
5915 static int pad_size = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005916 const RECT *pcliprect = NULL;
5917 UINT foptions = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005918 static WCHAR *unicodebuf = NULL;
5919 static int *unicodepdy = NULL;
Bram Moolenaar402d2fe2005-04-15 21:00:38 +00005920 static int unibuflen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005921 int n = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005922 int y;
5923
Bram Moolenaar071d4272004-06-13 20:20:40 +00005924 /*
5925 * Italic and bold text seems to have an extra row of pixels at the bottom
5926 * (below where the bottom of the character should be). If we draw the
5927 * characters with a solid background, the top row of pixels in the
5928 * character below will be overwritten. We can fix this by filling in the
5929 * background ourselves, to the correct character proportions, and then
5930 * writing the character in transparent mode. Still have a problem when
5931 * the character is "_", which gets written on to the character below.
5932 * New fix: set gui.char_ascent to -1. This shifts all characters up one
5933 * pixel in their slots, which fixes the problem with the bottom row of
5934 * pixels. We still need this code because otherwise the top row of pixels
5935 * becomes a problem. - webb.
5936 */
5937 static HBRUSH hbr_cache[2] = {NULL, NULL};
5938 static guicolor_T brush_color[2] = {INVALCOLOR, INVALCOLOR};
5939 static int brush_lru = 0;
5940 HBRUSH hbr;
5941 RECT rc;
5942
5943 if (!(flags & DRAW_TRANSP))
5944 {
5945 /*
5946 * Clear background first.
5947 * Note: FillRect() excludes right and bottom of rectangle.
5948 */
5949 rc.left = FILL_X(col);
5950 rc.top = FILL_Y(row);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005951 if (has_mbyte)
5952 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01005953 // Compute the length in display cells.
Bram Moolenaar72597a52010-07-18 15:31:08 +02005954 rc.right = FILL_X(col + mb_string2cells(text, len));
Bram Moolenaar071d4272004-06-13 20:20:40 +00005955 }
5956 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00005957 rc.right = FILL_X(col + len);
5958 rc.bottom = FILL_Y(row + 1);
5959
Bram Moolenaar734a8672019-12-02 22:49:38 +01005960 // Cache the created brush, that saves a lot of time. We need two:
5961 // one for cursor background and one for the normal background.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005962 if (gui.currBgColor == brush_color[0])
5963 {
5964 hbr = hbr_cache[0];
5965 brush_lru = 1;
5966 }
5967 else if (gui.currBgColor == brush_color[1])
5968 {
5969 hbr = hbr_cache[1];
5970 brush_lru = 0;
5971 }
5972 else
5973 {
5974 if (hbr_cache[brush_lru] != NULL)
5975 DeleteBrush(hbr_cache[brush_lru]);
5976 hbr_cache[brush_lru] = CreateSolidBrush(gui.currBgColor);
5977 brush_color[brush_lru] = gui.currBgColor;
5978 hbr = hbr_cache[brush_lru];
5979 brush_lru = !brush_lru;
5980 }
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01005981
Bram Moolenaar92467d32017-12-05 13:22:16 +01005982 fill_rect(&rc, hbr, gui.currBgColor);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005983
5984 SetBkMode(s_hdc, TRANSPARENT);
5985
5986 /*
5987 * When drawing block cursor, prevent inverted character spilling
5988 * over character cell (can happen with bold/italic)
5989 */
5990 if (flags & DRAW_CURSOR)
5991 {
5992 pcliprect = &rc;
5993 foptions = ETO_CLIPPED;
5994 }
5995 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005996 SetTextColor(s_hdc, gui.currFgColor);
5997 SelectFont(s_hdc, gui.currFont);
5998
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02005999#ifdef FEAT_DIRECTX
6000 if (IS_ENABLE_DIRECTX())
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01006001 DWriteContext_SetFont(s_dwc, (HFONT)gui.currFont);
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02006002#endif
6003
Bram Moolenaar071d4272004-06-13 20:20:40 +00006004 if (pad_size != Columns || padding == NULL || padding[0] != gui.char_width)
6005 {
K.Takata135e1522022-01-29 15:27:58 +00006006 int i;
6007
Bram Moolenaar071d4272004-06-13 20:20:40 +00006008 vim_free(padding);
6009 pad_size = Columns;
6010
Bram Moolenaar734a8672019-12-02 22:49:38 +01006011 // Don't give an out-of-memory message here, it would call us
6012 // recursively.
Bram Moolenaar59edb002019-05-28 23:32:47 +02006013 padding = LALLOC_MULT(int, pad_size);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006014 if (padding != NULL)
6015 for (i = 0; i < pad_size; i++)
6016 padding[i] = gui.char_width;
6017 }
6018
Bram Moolenaar071d4272004-06-13 20:20:40 +00006019 /*
6020 * We have to provide the padding argument because italic and bold versions
6021 * of fixed-width fonts are often one pixel or so wider than their normal
6022 * versions.
6023 * No check for DRAW_BOLD, Windows will have done it already.
6024 */
6025
Bram Moolenaar734a8672019-12-02 22:49:38 +01006026 // Check if there are any UTF-8 characters. If not, use normal text
6027 // output to speed up output.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006028 if (enc_utf8)
6029 for (n = 0; n < len; ++n)
6030 if (text[n] >= 0x80)
6031 break;
6032
Bram Moolenaarfc3abf42019-01-24 15:54:21 +01006033#if defined(FEAT_DIRECTX)
Bram Moolenaar734a8672019-12-02 22:49:38 +01006034 // Quick hack to enable DirectWrite. To use DirectWrite (antialias), it is
6035 // required that unicode drawing routine, currently. So this forces it
6036 // enabled.
Bram Moolenaar7f88b652017-12-14 13:15:19 +01006037 if (IS_ENABLE_DIRECTX())
Bram Moolenaar734a8672019-12-02 22:49:38 +01006038 n = 0; // Keep n < len, to enter block for unicode.
Bram Moolenaarfc3abf42019-01-24 15:54:21 +01006039#endif
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02006040
Bram Moolenaar734a8672019-12-02 22:49:38 +01006041 // Check if the Unicode buffer exists and is big enough. Create it
6042 // with the same length as the multi-byte string, the number of wide
6043 // characters is always equal or smaller.
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006044 if ((enc_utf8
6045 || (enc_codepage > 0 && (int)GetACP() != enc_codepage)
6046 || enc_latin9)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006047 && (unicodebuf == NULL || len > unibuflen))
6048 {
6049 vim_free(unicodebuf);
Bram Moolenaarc799fe22019-05-28 23:08:19 +02006050 unicodebuf = LALLOC_MULT(WCHAR, len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006051
6052 vim_free(unicodepdy);
Bram Moolenaarc799fe22019-05-28 23:08:19 +02006053 unicodepdy = LALLOC_MULT(int, len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006054
6055 unibuflen = len;
6056 }
6057
6058 if (enc_utf8 && n < len && unicodebuf != NULL)
6059 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01006060 // Output UTF-8 characters. Composing characters should be
6061 // handled here.
Bram Moolenaarca003e12006-03-17 23:19:38 +00006062 int i;
Bram Moolenaar734a8672019-12-02 22:49:38 +01006063 int wlen; // string length in words
6064 int clen; // string length in characters
6065 int cells; // cell width of string up to composing char
6066 int cw; // width of current cell
Bram Moolenaar362e1a32006-03-06 23:29:24 +00006067 int c;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006068
Bram Moolenaar97b2ad32006-03-18 21:40:56 +00006069 wlen = 0;
Bram Moolenaarca003e12006-03-17 23:19:38 +00006070 clen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006071 cells = 0;
Bram Moolenaarca003e12006-03-17 23:19:38 +00006072 for (i = 0; i < len; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00006073 {
Bram Moolenaar362e1a32006-03-06 23:29:24 +00006074 c = utf_ptr2char(text + i);
6075 if (c >= 0x10000)
6076 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01006077 // Turn into UTF-16 encoding.
Bram Moolenaarca003e12006-03-17 23:19:38 +00006078 unicodebuf[wlen++] = ((c - 0x10000) >> 10) + 0xD800;
6079 unicodebuf[wlen++] = ((c - 0x10000) & 0x3ff) + 0xDC00;
Bram Moolenaar362e1a32006-03-06 23:29:24 +00006080 }
6081 else
6082 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00006083 unicodebuf[wlen++] = c;
Bram Moolenaar362e1a32006-03-06 23:29:24 +00006084 }
Bram Moolenaara6ce1cc2017-10-28 19:23:11 +02006085
6086 if (utf_iscomposing(c))
6087 cw = 0;
6088 else
6089 {
6090 cw = utf_char2cells(c);
Bram Moolenaar734a8672019-12-02 22:49:38 +01006091 if (cw > 2) // don't use 4 for unprintable char
Bram Moolenaara6ce1cc2017-10-28 19:23:11 +02006092 cw = 1;
6093 }
6094
Bram Moolenaar071d4272004-06-13 20:20:40 +00006095 if (unicodepdy != NULL)
6096 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01006097 // Use unicodepdy to make characters fit as we expect, even
6098 // when the font uses different widths (e.g., bold character
6099 // is wider).
Bram Moolenaard804fdf2016-02-27 16:04:58 +01006100 if (c >= 0x10000)
6101 {
6102 unicodepdy[wlen - 2] = cw * gui.char_width;
6103 unicodepdy[wlen - 1] = 0;
6104 }
6105 else
6106 unicodepdy[wlen - 1] = cw * gui.char_width;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006107 }
6108 cells += cw;
Bram Moolenaara6ce1cc2017-10-28 19:23:11 +02006109 i += utf_ptr2len_len(text + i, len - i);
Bram Moolenaarca003e12006-03-17 23:19:38 +00006110 ++clen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006111 }
Bram Moolenaarfc3abf42019-01-24 15:54:21 +01006112#if defined(FEAT_DIRECTX)
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01006113 if (IS_ENABLE_DIRECTX())
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02006114 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01006115 // Add one to "cells" for italics.
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01006116 DWriteContext_DrawText(s_dwc, unicodebuf, wlen,
Bram Moolenaar60ebd522019-03-21 20:50:12 +01006117 TEXT_X(col), TEXT_Y(row),
6118 FILL_X(cells + 1), FILL_Y(1) - p_linespace,
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01006119 gui.char_width, gui.currFgColor,
6120 foptions, pcliprect, unicodepdy);
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02006121 }
6122 else
Bram Moolenaarfc3abf42019-01-24 15:54:21 +01006123#endif
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02006124 ExtTextOutW(s_hdc, TEXT_X(col), TEXT_Y(row),
6125 foptions, pcliprect, unicodebuf, wlen, unicodepdy);
Bram Moolenaar734a8672019-12-02 22:49:38 +01006126 len = cells; // used for underlining
Bram Moolenaar071d4272004-06-13 20:20:40 +00006127 }
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006128 else if ((enc_codepage > 0 && (int)GetACP() != enc_codepage) || enc_latin9)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006129 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01006130 // If we want to display codepage data, and the current CP is not the
6131 // ANSI one, we need to go via Unicode.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006132 if (unicodebuf != NULL)
6133 {
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006134 if (enc_latin9)
6135 latin9_to_ucs(text, len, unicodebuf);
6136 else
6137 len = MultiByteToWideChar(enc_codepage,
Bram Moolenaar071d4272004-06-13 20:20:40 +00006138 MB_PRECOMPOSED,
6139 (char *)text, len,
6140 (LPWSTR)unicodebuf, unibuflen);
6141 if (len != 0)
Bram Moolenaar19a09a12005-03-04 23:39:37 +00006142 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01006143 // Use unicodepdy to make characters fit as we expect, even
6144 // when the font uses different widths (e.g., bold character
6145 // is wider).
Bram Moolenaar19a09a12005-03-04 23:39:37 +00006146 if (unicodepdy != NULL)
6147 {
6148 int i;
6149 int cw;
6150
6151 for (i = 0; i < len; ++i)
6152 {
6153 cw = utf_char2cells(unicodebuf[i]);
6154 if (cw > 2)
6155 cw = 1;
6156 unicodepdy[i] = cw * gui.char_width;
6157 }
6158 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006159 ExtTextOutW(s_hdc, TEXT_X(col), TEXT_Y(row),
Bram Moolenaar19a09a12005-03-04 23:39:37 +00006160 foptions, pcliprect, unicodebuf, len, unicodepdy);
6161 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006162 }
6163 }
6164 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00006165 {
6166#ifdef FEAT_RIGHTLEFT
Bram Moolenaar734a8672019-12-02 22:49:38 +01006167 // Windows will mess up RL text, so we have to draw it character by
6168 // character. Only do this if RL is on, since it's slow.
Bram Moolenaar4ee40b02014-09-19 16:13:53 +02006169 if (curwin->w_p_rl)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006170 RevOut(s_hdc, TEXT_X(col), TEXT_Y(row),
6171 foptions, pcliprect, (char *)text, len, padding);
6172 else
6173#endif
6174 ExtTextOut(s_hdc, TEXT_X(col), TEXT_Y(row),
6175 foptions, pcliprect, (char *)text, len, padding);
6176 }
6177
Bram Moolenaar734a8672019-12-02 22:49:38 +01006178 // Underline
Bram Moolenaar071d4272004-06-13 20:20:40 +00006179 if (flags & DRAW_UNDERL)
6180 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01006181 // When p_linespace is 0, overwrite the bottom row of pixels.
6182 // Otherwise put the line just below the character.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006183 y = FILL_Y(row + 1) - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006184 if (p_linespace > 1)
6185 y -= p_linespace - 1;
Bram Moolenaar92467d32017-12-05 13:22:16 +01006186 draw_line(FILL_X(col), y, FILL_X(col + len), y, gui.currFgColor);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006187 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006188
Bram Moolenaar734a8672019-12-02 22:49:38 +01006189 // Strikethrough
Bram Moolenaarcf4b00c2017-09-02 18:33:56 +02006190 if (flags & DRAW_STRIKE)
6191 {
Bram Moolenaarcf4b00c2017-09-02 18:33:56 +02006192 y = FILL_Y(row + 1) - gui.char_height/2;
Bram Moolenaar92467d32017-12-05 13:22:16 +01006193 draw_line(FILL_X(col), y, FILL_X(col + len), y, gui.currSpColor);
Bram Moolenaarcf4b00c2017-09-02 18:33:56 +02006194 }
6195
Bram Moolenaar734a8672019-12-02 22:49:38 +01006196 // Undercurl
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006197 if (flags & DRAW_UNDERC)
6198 {
6199 int x;
6200 int offset;
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +00006201 static const int val[8] = {1, 0, 0, 0, 1, 2, 2, 2 };
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006202
6203 y = FILL_Y(row + 1) - 1;
6204 for (x = FILL_X(col); x < FILL_X(col + len); ++x)
6205 {
6206 offset = val[x % 8];
Bram Moolenaar92467d32017-12-05 13:22:16 +01006207 set_pixel(x, y - offset, gui.currSpColor);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006208 }
6209 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006210}
6211
6212
6213/*
6214 * Output routines.
6215 */
6216
Bram Moolenaar734a8672019-12-02 22:49:38 +01006217/*
6218 * Flush any output to the screen
6219 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006220 void
6221gui_mch_flush(void)
6222{
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01006223#if defined(FEAT_DIRECTX)
6224 if (IS_ENABLE_DIRECTX())
6225 DWriteContext_Flush(s_dwc);
6226#endif
6227
Bram Moolenaar071d4272004-06-13 20:20:40 +00006228 GdiFlush();
6229}
6230
6231 static void
6232clear_rect(RECT *rcp)
6233{
Bram Moolenaar92467d32017-12-05 13:22:16 +01006234 fill_rect(rcp, NULL, gui.back_pixel);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006235}
6236
6237
Bram Moolenaarc716c302006-01-21 22:12:51 +00006238 void
6239gui_mch_get_screen_dimensions(int *screen_w, int *screen_h)
6240{
6241 RECT workarea_rect;
6242
6243 get_work_area(&workarea_rect);
6244
Bram Moolenaar910f66f2006-04-05 20:41:53 +00006245 *screen_w = workarea_rect.right - workarea_rect.left
K.Takatac81e9bf2022-01-16 14:15:49 +00006246 - (pGetSystemMetricsForDpi(SM_CXFRAME, s_dpi) +
6247 pGetSystemMetricsForDpi(SM_CXPADDEDBORDER, s_dpi)) * 2;
Bram Moolenaarc716c302006-01-21 22:12:51 +00006248
Bram Moolenaar734a8672019-12-02 22:49:38 +01006249 // FIXME: dirty trick: Because the gui_get_base_height() doesn't include
6250 // the menubar for MSwin, we subtract it from the screen height, so that
6251 // the window size can be made to fit on the screen.
Bram Moolenaar910f66f2006-04-05 20:41:53 +00006252 *screen_h = workarea_rect.bottom - workarea_rect.top
K.Takatac81e9bf2022-01-16 14:15:49 +00006253 - (pGetSystemMetricsForDpi(SM_CYFRAME, s_dpi) +
6254 pGetSystemMetricsForDpi(SM_CXPADDEDBORDER, s_dpi)) * 2
6255 - pGetSystemMetricsForDpi(SM_CYCAPTION, s_dpi)
6256 - gui_mswin_get_menu_height(FALSE);
Bram Moolenaarc716c302006-01-21 22:12:51 +00006257}
6258
6259
Bram Moolenaar071d4272004-06-13 20:20:40 +00006260#if defined(FEAT_MENU) || defined(PROTO)
6261/*
6262 * Add a sub menu to the menu bar.
6263 */
6264 void
6265gui_mch_add_menu(
6266 vimmenu_T *menu,
6267 int pos)
6268{
6269 vimmenu_T *parent = menu->parent;
6270
6271 menu->submenu_id = CreatePopupMenu();
6272 menu->id = s_menu_id++;
6273
6274 if (menu_is_menubar(menu->name))
6275 {
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006276 WCHAR *wn;
6277 MENUITEMINFOW infow;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006278
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006279 wn = enc_to_utf16(menu->name, NULL);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006280 if (wn == NULL)
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006281 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006282
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006283 infow.cbSize = sizeof(infow);
6284 infow.fMask = MIIM_DATA | MIIM_TYPE | MIIM_ID
6285 | MIIM_SUBMENU;
6286 infow.dwItemData = (long_u)menu;
6287 infow.wID = menu->id;
6288 infow.fType = MFT_STRING;
6289 infow.dwTypeData = wn;
6290 infow.cch = (UINT)wcslen(wn);
6291 infow.hSubMenu = menu->submenu_id;
6292 InsertMenuItemW((parent == NULL)
6293 ? s_menuBar : parent->submenu_id,
6294 (UINT)pos, TRUE, &infow);
6295 vim_free(wn);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006296 }
6297
Bram Moolenaar734a8672019-12-02 22:49:38 +01006298 // Fix window size if menu may have wrapped
Bram Moolenaar071d4272004-06-13 20:20:40 +00006299 if (parent == NULL)
6300 gui_mswin_get_menu_height(!gui.starting);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006301# ifdef FEAT_TEAROFF
Bram Moolenaar071d4272004-06-13 20:20:40 +00006302 else if (IsWindow(parent->tearoff_handle))
6303 rebuild_tearoff(parent);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006304# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006305}
6306
6307 void
6308gui_mch_show_popupmenu(vimmenu_T *menu)
6309{
6310 POINT mp;
6311
K.Takata45f9cfb2022-01-21 11:11:00 +00006312 (void)GetCursorPos(&mp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006313 gui_mch_show_popupmenu_at(menu, (int)mp.x, (int)mp.y);
6314}
6315
6316 void
Bram Moolenaar045e82d2005-07-08 22:25:33 +00006317gui_make_popup(char_u *path_name, int mouse_pos)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006318{
6319 vimmenu_T *menu = gui_find_menu(path_name);
6320
6321 if (menu != NULL)
6322 {
6323 POINT p;
6324
Bram Moolenaar734a8672019-12-02 22:49:38 +01006325 // Find the position of the current cursor
Bram Moolenaar071d4272004-06-13 20:20:40 +00006326 GetDCOrgEx(s_hdc, &p);
Bram Moolenaar045e82d2005-07-08 22:25:33 +00006327 if (mouse_pos)
6328 {
6329 int mx, my;
6330
6331 gui_mch_getmouse(&mx, &my);
6332 p.x += mx;
6333 p.y += my;
6334 }
6335 else if (curwin != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006336 {
Bram Moolenaar53f81742017-09-22 14:35:51 +02006337 p.x += TEXT_X(curwin->w_wincol + curwin->w_wcol + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006338 p.y += TEXT_Y(W_WINROW(curwin) + curwin->w_wrow + 1);
6339 }
6340 msg_scroll = FALSE;
6341 gui_mch_show_popupmenu_at(menu, (int)p.x, (int)p.y);
6342 }
6343}
6344
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006345# if defined(FEAT_TEAROFF) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006346/*
6347 * Given a menu descriptor, e.g. "File.New", find it in the menu hierarchy and
6348 * create it as a pseudo-"tearoff menu".
6349 */
6350 void
6351gui_make_tearoff(char_u *path_name)
6352{
6353 vimmenu_T *menu = gui_find_menu(path_name);
6354
Bram Moolenaar734a8672019-12-02 22:49:38 +01006355 // Found the menu, so tear it off.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006356 if (menu != NULL)
6357 gui_mch_tearoff(menu->dname, menu, 0xffffL, 0xffffL);
6358}
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006359# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006360
6361/*
6362 * Add a menu item to a menu
6363 */
6364 void
6365gui_mch_add_menu_item(
6366 vimmenu_T *menu,
6367 int idx)
6368{
6369 vimmenu_T *parent = menu->parent;
6370
6371 menu->id = s_menu_id++;
6372 menu->submenu_id = NULL;
6373
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006374# ifdef FEAT_TEAROFF
Bram Moolenaar071d4272004-06-13 20:20:40 +00006375 if (STRNCMP(menu->name, TEAR_STRING, TEAR_LEN) == 0)
6376 {
6377 InsertMenu(parent->submenu_id, (UINT)idx, MF_BITMAP|MF_BYPOSITION,
6378 (UINT)menu->id, (LPCTSTR) s_htearbitmap);
6379 }
6380 else
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006381# endif
6382# ifdef FEAT_TOOLBAR
Bram Moolenaar071d4272004-06-13 20:20:40 +00006383 if (menu_is_toolbar(parent->name))
6384 {
6385 TBBUTTON newtb;
6386
Bram Moolenaara80faa82020-04-12 19:37:17 +02006387 CLEAR_FIELD(newtb);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006388 if (menu_is_separator(menu->name))
6389 {
6390 newtb.iBitmap = 0;
6391 newtb.fsStyle = TBSTYLE_SEP;
6392 }
6393 else
6394 {
6395 newtb.iBitmap = get_toolbar_bitmap(menu);
6396 newtb.fsStyle = TBSTYLE_BUTTON;
6397 }
6398 newtb.idCommand = menu->id;
6399 newtb.fsState = TBSTATE_ENABLED;
6400 newtb.iString = 0;
6401 SendMessage(s_toolbarhwnd, TB_INSERTBUTTON, (WPARAM)idx,
6402 (LPARAM)&newtb);
6403 menu->submenu_id = (HMENU)-1;
6404 }
6405 else
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006406# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006407 {
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006408 WCHAR *wn;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006409
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006410 wn = enc_to_utf16(menu->name, NULL);
6411 if (wn != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006412 {
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006413 InsertMenuW(parent->submenu_id, (UINT)idx,
6414 (menu_is_separator(menu->name)
6415 ? MF_SEPARATOR : MF_STRING) | MF_BYPOSITION,
6416 (UINT)menu->id, wn);
6417 vim_free(wn);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006418 }
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006419# ifdef FEAT_TEAROFF
Bram Moolenaar071d4272004-06-13 20:20:40 +00006420 if (IsWindow(parent->tearoff_handle))
6421 rebuild_tearoff(parent);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006422# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006423 }
6424}
6425
6426/*
6427 * Destroy the machine specific menu widget.
6428 */
6429 void
6430gui_mch_destroy_menu(vimmenu_T *menu)
6431{
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006432# ifdef FEAT_TOOLBAR
Bram Moolenaar071d4272004-06-13 20:20:40 +00006433 /*
6434 * is this a toolbar button?
6435 */
6436 if (menu->submenu_id == (HMENU)-1)
6437 {
6438 int iButton;
6439
6440 iButton = (int)SendMessage(s_toolbarhwnd, TB_COMMANDTOINDEX,
6441 (WPARAM)menu->id, 0);
6442 SendMessage(s_toolbarhwnd, TB_DELETEBUTTON, (WPARAM)iButton, 0);
6443 }
6444 else
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006445# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006446 {
6447 if (menu->parent != NULL
6448 && menu_is_popup(menu->parent->dname)
6449 && menu->parent->submenu_id != NULL)
6450 RemoveMenu(menu->parent->submenu_id, menu->id, MF_BYCOMMAND);
6451 else
6452 RemoveMenu(s_menuBar, menu->id, MF_BYCOMMAND);
6453 if (menu->submenu_id != NULL)
6454 DestroyMenu(menu->submenu_id);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006455# ifdef FEAT_TEAROFF
Bram Moolenaar071d4272004-06-13 20:20:40 +00006456 if (IsWindow(menu->tearoff_handle))
6457 DestroyWindow(menu->tearoff_handle);
6458 if (menu->parent != NULL
6459 && menu->parent->children != NULL
6460 && IsWindow(menu->parent->tearoff_handle))
6461 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01006462 // This menu must not show up when rebuilding the tearoff window.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006463 menu->modes = 0;
6464 rebuild_tearoff(menu->parent);
6465 }
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006466# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006467 }
6468}
6469
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006470# ifdef FEAT_TEAROFF
Bram Moolenaar071d4272004-06-13 20:20:40 +00006471 static void
6472rebuild_tearoff(vimmenu_T *menu)
6473{
Bram Moolenaar734a8672019-12-02 22:49:38 +01006474 //hackish
Bram Moolenaar071d4272004-06-13 20:20:40 +00006475 char_u tbuf[128];
6476 RECT trect;
6477 RECT rct;
6478 RECT roct;
6479 int x, y;
6480
6481 HWND thwnd = menu->tearoff_handle;
6482
Bram Moolenaar418f81b2016-02-16 20:12:02 +01006483 GetWindowText(thwnd, (LPSTR)tbuf, 127);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006484 if (GetWindowRect(thwnd, &trect)
6485 && GetWindowRect(s_hwnd, &rct)
6486 && GetClientRect(s_hwnd, &roct))
6487 {
6488 x = trect.left - rct.left;
6489 y = (trect.top - rct.bottom + roct.bottom);
6490 }
6491 else
6492 {
6493 x = y = 0xffffL;
6494 }
6495 DestroyWindow(thwnd);
6496 if (menu->children != NULL)
6497 {
6498 gui_mch_tearoff(tbuf, menu, x, y);
6499 if (IsWindow(menu->tearoff_handle))
6500 (void) SetWindowPos(menu->tearoff_handle,
6501 NULL,
6502 (int)trect.left,
6503 (int)trect.top,
6504 0, 0,
6505 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
6506 }
6507}
Bram Moolenaar734a8672019-12-02 22:49:38 +01006508# endif // FEAT_TEAROFF
Bram Moolenaar071d4272004-06-13 20:20:40 +00006509
6510/*
6511 * Make a menu either grey or not grey.
6512 */
6513 void
6514gui_mch_menu_grey(
6515 vimmenu_T *menu,
6516 int grey)
6517{
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006518# ifdef FEAT_TOOLBAR
Bram Moolenaar071d4272004-06-13 20:20:40 +00006519 /*
6520 * is this a toolbar button?
6521 */
6522 if (menu->submenu_id == (HMENU)-1)
6523 {
6524 SendMessage(s_toolbarhwnd, TB_ENABLEBUTTON,
K.Takata45f9cfb2022-01-21 11:11:00 +00006525 (WPARAM)menu->id, (LPARAM) MAKELONG((grey ? FALSE : TRUE), 0));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006526 }
6527 else
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006528# endif
Bram Moolenaar762f1752016-06-04 22:36:17 +02006529 (void)EnableMenuItem(menu->parent ? menu->parent->submenu_id : s_menuBar,
6530 menu->id, MF_BYCOMMAND | (grey ? MF_GRAYED : MF_ENABLED));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006531
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006532# ifdef FEAT_TEAROFF
Bram Moolenaar071d4272004-06-13 20:20:40 +00006533 if ((menu->parent != NULL) && (IsWindow(menu->parent->tearoff_handle)))
6534 {
6535 WORD menuID;
6536 HWND menuHandle;
6537
6538 /*
6539 * A tearoff button has changed state.
6540 */
6541 if (menu->children == NULL)
6542 menuID = (WORD)(menu->id);
6543 else
Bram Moolenaareb3593b2006-04-22 22:33:57 +00006544 menuID = (WORD)((long_u)(menu->submenu_id) | (DWORD)0x8000);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006545 menuHandle = GetDlgItem(menu->parent->tearoff_handle, menuID);
6546 if (menuHandle)
6547 EnableWindow(menuHandle, !grey);
6548
6549 }
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006550# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006551}
6552
Bram Moolenaar734a8672019-12-02 22:49:38 +01006553#endif // FEAT_MENU
Bram Moolenaar071d4272004-06-13 20:20:40 +00006554
6555
Bram Moolenaar734a8672019-12-02 22:49:38 +01006556// define some macros used to make the dialogue creation more readable
Bram Moolenaar071d4272004-06-13 20:20:40 +00006557
Bram Moolenaar071d4272004-06-13 20:20:40 +00006558#define add_word(x) *p++ = (x)
Bram Moolenaarf4cd3e82005-12-22 22:47:02 +00006559#define add_long(x) dwp = (DWORD *)p; *dwp++ = (x); p = (WORD *)dwp
Bram Moolenaar071d4272004-06-13 20:20:40 +00006560
6561#if defined(FEAT_GUI_DIALOG) || defined(PROTO)
6562/*
6563 * stuff for dialogs
6564 */
6565
6566/*
6567 * The callback routine used by all the dialogs. Very simple. First,
6568 * acknowledges the INITDIALOG message so that Windows knows to do standard
6569 * dialog stuff (Return = default, Esc = cancel....) Second, if a button is
6570 * pressed, return that button's ID - IDCANCEL (2), which is the button's
6571 * number.
6572 */
6573 static LRESULT CALLBACK
6574dialog_callback(
6575 HWND hwnd,
6576 UINT message,
6577 WPARAM wParam,
Bram Moolenaar1266d672017-02-01 13:43:36 +01006578 LPARAM lParam UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006579{
6580 if (message == WM_INITDIALOG)
6581 {
6582 CenterWindow(hwnd, GetWindow(hwnd, GW_OWNER));
Bram Moolenaar734a8672019-12-02 22:49:38 +01006583 // Set focus to the dialog. Set the default button, if specified.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006584 (void)SetFocus(hwnd);
6585 if (dialog_default_button > IDCANCEL)
6586 (void)SetFocus(GetDlgItem(hwnd, dialog_default_button));
Bram Moolenaar2b80e652007-08-14 14:57:55 +00006587 else
Bram Moolenaar734a8672019-12-02 22:49:38 +01006588 // We don't have a default, set focus on another element of the
6589 // dialog window, probably the icon
Bram Moolenaar2b80e652007-08-14 14:57:55 +00006590 (void)SetFocus(GetDlgItem(hwnd, DLG_NONBUTTON_CONTROL));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006591 return FALSE;
6592 }
6593
6594 if (message == WM_COMMAND)
6595 {
6596 int button = LOWORD(wParam);
6597
Bram Moolenaar734a8672019-12-02 22:49:38 +01006598 // Don't end the dialog if something was selected that was
6599 // not a button.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006600 if (button >= DLG_NONBUTTON_CONTROL)
6601 return TRUE;
6602
Bram Moolenaar734a8672019-12-02 22:49:38 +01006603 // If the edit box exists, copy the string.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006604 if (s_textfield != NULL)
Bram Moolenaar3ca9a8a2009-01-28 20:23:17 +00006605 {
Bram Moolenaarc799fe22019-05-28 23:08:19 +02006606 WCHAR *wp = ALLOC_MULT(WCHAR, IOSIZE);
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006607 char_u *p;
Bram Moolenaar3ca9a8a2009-01-28 20:23:17 +00006608
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006609 GetDlgItemTextW(hwnd, DLG_NONBUTTON_CONTROL + 2, wp, IOSIZE);
6610 p = utf16_to_enc(wp, NULL);
6611 vim_strncpy(s_textfield, p, IOSIZE);
6612 vim_free(p);
6613 vim_free(wp);
Bram Moolenaar3ca9a8a2009-01-28 20:23:17 +00006614 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006615
6616 /*
6617 * Need to check for IDOK because if the user just hits Return to
6618 * accept the default value, some reason this is what we get.
6619 */
6620 if (button == IDOK)
6621 {
6622 if (dialog_default_button > IDCANCEL)
6623 EndDialog(hwnd, dialog_default_button);
6624 }
6625 else
6626 EndDialog(hwnd, button - IDCANCEL);
6627 return TRUE;
6628 }
6629
6630 if ((message == WM_SYSCOMMAND) && (wParam == SC_CLOSE))
6631 {
6632 EndDialog(hwnd, 0);
6633 return TRUE;
6634 }
6635 return FALSE;
6636}
6637
6638/*
6639 * Create a dialog dynamically from the parameter strings.
6640 * type = type of dialog (question, alert, etc.)
6641 * title = dialog title. may be NULL for default title.
6642 * message = text to display. Dialog sizes to accommodate it.
6643 * buttons = '\n' separated list of button captions, default first.
6644 * dfltbutton = number of default button.
6645 *
6646 * This routine returns 1 if the first button is pressed,
6647 * 2 for the second, etc.
6648 *
6649 * 0 indicates Esc was pressed.
6650 * -1 for unexpected error
6651 *
6652 * If stubbing out this fn, return 1.
6653 */
6654
Bram Moolenaar734a8672019-12-02 22:49:38 +01006655static const char *dlg_icons[] = // must match names in resource file
Bram Moolenaar071d4272004-06-13 20:20:40 +00006656{
6657 "IDR_VIM",
6658 "IDR_VIM_ERROR",
6659 "IDR_VIM_ALERT",
6660 "IDR_VIM_INFO",
6661 "IDR_VIM_QUESTION"
6662};
6663
Bram Moolenaar071d4272004-06-13 20:20:40 +00006664 int
6665gui_mch_dialog(
6666 int type,
6667 char_u *title,
6668 char_u *message,
6669 char_u *buttons,
6670 int dfltbutton,
Bram Moolenaard2c340a2011-01-17 20:08:11 +01006671 char_u *textfield,
Bram Moolenaarbd67aac2019-09-21 23:09:04 +02006672 int ex_cmd UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006673{
6674 WORD *p, *pdlgtemplate, *pnumitems;
Bram Moolenaarf4cd3e82005-12-22 22:47:02 +00006675 DWORD *dwp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006676 int numButtons;
6677 int *buttonWidths, *buttonPositions;
6678 int buttonYpos;
6679 int nchar, i;
6680 DWORD lStyle;
6681 int dlgwidth = 0;
6682 int dlgheight;
6683 int editboxheight;
6684 int horizWidth = 0;
6685 int msgheight;
6686 char_u *pstart;
6687 char_u *pend;
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00006688 char_u *last_white;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006689 char_u *tbuffer;
6690 RECT rect;
6691 HWND hwnd;
6692 HDC hdc;
6693 HFONT font, oldFont;
6694 TEXTMETRIC fontInfo;
6695 int fontHeight;
6696 int textWidth, minButtonWidth, messageWidth;
6697 int maxDialogWidth;
Bram Moolenaar748bf032005-02-02 23:04:36 +00006698 int maxDialogHeight;
6699 int scroll_flag = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006700 int vertical;
6701 int dlgPaddingX;
6702 int dlgPaddingY;
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006703# ifdef USE_SYSMENU_FONT
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01006704 LOGFONTW lfSysmenu;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006705 int use_lfSysmenu = FALSE;
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006706# endif
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00006707 garray_T ga;
6708 int l;
K.Takatac81e9bf2022-01-16 14:15:49 +00006709 int dlg_icon_width;
6710 int dlg_icon_height;
6711 int dpi;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006712
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006713# ifndef NO_CONSOLE
Bram Moolenaar734a8672019-12-02 22:49:38 +01006714 // Don't output anything in silent mode ("ex -s")
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006715# ifdef VIMDLL
Bram Moolenaarafde13b2019-04-28 19:46:49 +02006716 if (!(gui.in_use || gui.starting))
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006717# endif
Bram Moolenaarafde13b2019-04-28 19:46:49 +02006718 if (silent_mode)
Bram Moolenaar734a8672019-12-02 22:49:38 +01006719 return dfltbutton; // return default option
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006720# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006721
Bram Moolenaar748bf032005-02-02 23:04:36 +00006722 if (s_hwnd == NULL)
K.Takatac81e9bf2022-01-16 14:15:49 +00006723 {
6724 load_dpi_func();
6725 s_dpi = dpi = pGetDpiForSystem();
Bram Moolenaar748bf032005-02-02 23:04:36 +00006726 get_dialog_font_metrics();
K.Takatac81e9bf2022-01-16 14:15:49 +00006727 }
6728 else
6729 dpi = pGetDpiForSystem();
Bram Moolenaar071d4272004-06-13 20:20:40 +00006730
6731 if ((type < 0) || (type > VIM_LAST_TYPE))
6732 type = 0;
6733
Bram Moolenaar734a8672019-12-02 22:49:38 +01006734 // allocate some memory for dialog template
6735 // TODO should compute this really
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00006736 pdlgtemplate = p = (PWORD)LocalAlloc(LPTR,
Bram Moolenaareb3593b2006-04-22 22:33:57 +00006737 DLG_ALLOC_SIZE + STRLEN(message) * 2);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006738
6739 if (p == NULL)
6740 return -1;
6741
6742 /*
Bram Moolenaar84a05ac2013-05-06 04:24:17 +02006743 * make a copy of 'buttons' to fiddle with it. compiler grizzles because
Bram Moolenaar071d4272004-06-13 20:20:40 +00006744 * vim_strsave() doesn't take a const arg (why not?), so cast away the
6745 * const.
6746 */
6747 tbuffer = vim_strsave(buttons);
6748 if (tbuffer == NULL)
6749 return -1;
6750
Bram Moolenaar734a8672019-12-02 22:49:38 +01006751 --dfltbutton; // Change from one-based to zero-based
Bram Moolenaar071d4272004-06-13 20:20:40 +00006752
Bram Moolenaar734a8672019-12-02 22:49:38 +01006753 // Count buttons
Bram Moolenaar071d4272004-06-13 20:20:40 +00006754 numButtons = 1;
6755 for (i = 0; tbuffer[i] != '\0'; i++)
6756 {
6757 if (tbuffer[i] == DLG_BUTTON_SEP)
6758 numButtons++;
6759 }
6760 if (dfltbutton >= numButtons)
6761 dfltbutton = -1;
6762
Bram Moolenaar734a8672019-12-02 22:49:38 +01006763 // Allocate array to hold the width of each button
Bram Moolenaarc799fe22019-05-28 23:08:19 +02006764 buttonWidths = ALLOC_MULT(int, numButtons);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006765 if (buttonWidths == NULL)
6766 return -1;
6767
Bram Moolenaar734a8672019-12-02 22:49:38 +01006768 // Allocate array to hold the X position of each button
Bram Moolenaarc799fe22019-05-28 23:08:19 +02006769 buttonPositions = ALLOC_MULT(int, numButtons);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006770 if (buttonPositions == NULL)
6771 return -1;
6772
6773 /*
6774 * Calculate how big the dialog must be.
6775 */
6776 hwnd = GetDesktopWindow();
6777 hdc = GetWindowDC(hwnd);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006778# ifdef USE_SYSMENU_FONT
Bram Moolenaar071d4272004-06-13 20:20:40 +00006779 if (gui_w32_get_menu_font(&lfSysmenu) == OK)
6780 {
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01006781 font = CreateFontIndirectW(&lfSysmenu);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006782 use_lfSysmenu = TRUE;
6783 }
6784 else
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006785# endif
K.Takatad1c58992022-01-23 12:31:57 +00006786 font = CreateFont(-DLG_FONT_POINT_SIZE, 0, 0, 0, 0, 0, 0, 0,
6787 0, 0, 0, 0, VARIABLE_PITCH, DLG_FONT_NAME);
6788
6789 oldFont = SelectFont(hdc, font);
6790 dlgPaddingX = DLG_PADDING_X;
6791 dlgPaddingY = DLG_PADDING_Y;
6792
Bram Moolenaar071d4272004-06-13 20:20:40 +00006793 GetTextMetrics(hdc, &fontInfo);
6794 fontHeight = fontInfo.tmHeight;
6795
Bram Moolenaar734a8672019-12-02 22:49:38 +01006796 // Minimum width for horizontal button
Bram Moolenaar418f81b2016-02-16 20:12:02 +01006797 minButtonWidth = GetTextWidth(hdc, (char_u *)"Cancel", 6);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006798
Bram Moolenaar734a8672019-12-02 22:49:38 +01006799 // Maximum width of a dialog, if possible
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00006800 if (s_hwnd == NULL)
6801 {
6802 RECT workarea_rect;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006803
Bram Moolenaar734a8672019-12-02 22:49:38 +01006804 // We don't have a window, use the desktop area.
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00006805 get_work_area(&workarea_rect);
6806 maxDialogWidth = workarea_rect.right - workarea_rect.left - 100;
K.Takatac81e9bf2022-01-16 14:15:49 +00006807 if (maxDialogWidth > adjust_by_system_dpi(600))
6808 maxDialogWidth = adjust_by_system_dpi(600);
Bram Moolenaar734a8672019-12-02 22:49:38 +01006809 // Leave some room for the taskbar.
Bram Moolenaar1b1b0942013-08-01 13:20:42 +02006810 maxDialogHeight = workarea_rect.bottom - workarea_rect.top - 150;
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00006811 }
6812 else
6813 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01006814 // Use our own window for the size, unless it's very small.
Bram Moolenaara95d8232013-08-07 15:27:11 +02006815 GetWindowRect(s_hwnd, &rect);
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00006816 maxDialogWidth = rect.right - rect.left
K.Takatac81e9bf2022-01-16 14:15:49 +00006817 - (pGetSystemMetricsForDpi(SM_CXFRAME, dpi) +
6818 pGetSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi)) * 2;
6819 if (maxDialogWidth < adjust_by_system_dpi(DLG_MIN_MAX_WIDTH))
6820 maxDialogWidth = adjust_by_system_dpi(DLG_MIN_MAX_WIDTH);
Bram Moolenaar748bf032005-02-02 23:04:36 +00006821
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00006822 maxDialogHeight = rect.bottom - rect.top
K.Takatac81e9bf2022-01-16 14:15:49 +00006823 - (pGetSystemMetricsForDpi(SM_CYFRAME, dpi) +
6824 pGetSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi)) * 4
6825 - pGetSystemMetricsForDpi(SM_CYCAPTION, dpi);
6826 if (maxDialogHeight < adjust_by_system_dpi(DLG_MIN_MAX_HEIGHT))
6827 maxDialogHeight = adjust_by_system_dpi(DLG_MIN_MAX_HEIGHT);
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00006828 }
6829
Bram Moolenaar734a8672019-12-02 22:49:38 +01006830 // Set dlgwidth to width of message.
6831 // Copy the message into "ga", changing NL to CR-NL and inserting line
6832 // breaks where needed.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006833 pstart = message;
6834 messageWidth = 0;
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00006835 msgheight = 0;
6836 ga_init2(&ga, sizeof(char), 500);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006837 do
6838 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01006839 msgheight += fontHeight; // at least one line
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00006840
Bram Moolenaar734a8672019-12-02 22:49:38 +01006841 // Need to figure out where to break the string. The system does it
6842 // at a word boundary, which would mean we can't compute the number of
6843 // wrapped lines.
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00006844 textWidth = 0;
6845 last_white = NULL;
6846 for (pend = pstart; *pend != NUL && *pend != '\n'; )
Bram Moolenaar748bf032005-02-02 23:04:36 +00006847 {
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00006848 l = (*mb_ptr2len)(pend);
Bram Moolenaar1c465442017-03-12 20:10:05 +01006849 if (l == 1 && VIM_ISWHITE(*pend)
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00006850 && textWidth > maxDialogWidth * 3 / 4)
6851 last_white = pend;
Bram Moolenaarf05d8112013-06-26 12:58:32 +02006852 textWidth += GetTextWidthEnc(hdc, pend, l);
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00006853 if (textWidth >= maxDialogWidth)
Bram Moolenaar748bf032005-02-02 23:04:36 +00006854 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01006855 // Line will wrap.
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00006856 messageWidth = maxDialogWidth;
Bram Moolenaar748bf032005-02-02 23:04:36 +00006857 msgheight += fontHeight;
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00006858 textWidth = 0;
6859
6860 if (last_white != NULL)
6861 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01006862 // break the line just after a space
K.Takatac81e9bf2022-01-16 14:15:49 +00006863 if (pend > last_white)
6864 ga.ga_len -= (int)(pend - (last_white + 1));
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00006865 pend = last_white + 1;
6866 last_white = NULL;
6867 }
6868 ga_append(&ga, '\r');
6869 ga_append(&ga, '\n');
6870 continue;
Bram Moolenaar748bf032005-02-02 23:04:36 +00006871 }
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00006872
6873 while (--l >= 0)
6874 ga_append(&ga, *pend++);
Bram Moolenaar748bf032005-02-02 23:04:36 +00006875 }
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00006876 if (textWidth > messageWidth)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006877 messageWidth = textWidth;
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00006878
6879 ga_append(&ga, '\r');
6880 ga_append(&ga, '\n');
Bram Moolenaar071d4272004-06-13 20:20:40 +00006881 pstart = pend + 1;
6882 } while (*pend != NUL);
Bram Moolenaar748bf032005-02-02 23:04:36 +00006883
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00006884 if (ga.ga_data != NULL)
6885 message = ga.ga_data;
6886
Bram Moolenaar734a8672019-12-02 22:49:38 +01006887 messageWidth += 10; // roundoff space
Bram Moolenaar748bf032005-02-02 23:04:36 +00006888
K.Takatac81e9bf2022-01-16 14:15:49 +00006889 dlg_icon_width = adjust_by_system_dpi(DLG_ICON_WIDTH);
6890 dlg_icon_height = adjust_by_system_dpi(DLG_ICON_HEIGHT);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006891
K.Takatac81e9bf2022-01-16 14:15:49 +00006892 // Add width of icon to dlgwidth, and some space
6893 dlgwidth = messageWidth + dlg_icon_width + 3 * dlgPaddingX
6894 + pGetSystemMetricsForDpi(SM_CXVSCROLL, dpi);
6895
6896 if (msgheight < dlg_icon_height)
6897 msgheight = dlg_icon_height;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006898
6899 /*
6900 * Check button names. A long one will make the dialog wider.
Bram Moolenaaraea02fa2007-05-04 20:29:09 +00006901 * When called early (-register error message) p_go isn't initialized.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006902 */
Bram Moolenaaraea02fa2007-05-04 20:29:09 +00006903 vertical = (p_go != NULL && vim_strchr(p_go, GO_VERTICAL) != NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006904 if (!vertical)
6905 {
6906 // Place buttons horizontally if they fit.
6907 horizWidth = dlgPaddingX;
6908 pstart = tbuffer;
6909 i = 0;
6910 do
6911 {
6912 pend = vim_strchr(pstart, DLG_BUTTON_SEP);
6913 if (pend == NULL)
6914 pend = pstart + STRLEN(pstart); // Last button name.
Bram Moolenaarb052fe02013-06-26 13:16:20 +02006915 textWidth = GetTextWidthEnc(hdc, pstart, (int)(pend - pstart));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006916 if (textWidth < minButtonWidth)
6917 textWidth = minButtonWidth;
Bram Moolenaar734a8672019-12-02 22:49:38 +01006918 textWidth += dlgPaddingX; // Padding within button
Bram Moolenaar071d4272004-06-13 20:20:40 +00006919 buttonWidths[i] = textWidth;
6920 buttonPositions[i++] = horizWidth;
Bram Moolenaar734a8672019-12-02 22:49:38 +01006921 horizWidth += textWidth + dlgPaddingX; // Pad between buttons
Bram Moolenaar071d4272004-06-13 20:20:40 +00006922 pstart = pend + 1;
6923 } while (*pend != NUL);
6924
6925 if (horizWidth > maxDialogWidth)
6926 vertical = TRUE; // Too wide to fit on the screen.
6927 else if (horizWidth > dlgwidth)
6928 dlgwidth = horizWidth;
6929 }
6930
6931 if (vertical)
6932 {
6933 // Stack buttons vertically.
6934 pstart = tbuffer;
6935 do
6936 {
6937 pend = vim_strchr(pstart, DLG_BUTTON_SEP);
6938 if (pend == NULL)
6939 pend = pstart + STRLEN(pstart); // Last button name.
Bram Moolenaarb052fe02013-06-26 13:16:20 +02006940 textWidth = GetTextWidthEnc(hdc, pstart, (int)(pend - pstart));
Bram Moolenaar734a8672019-12-02 22:49:38 +01006941 textWidth += dlgPaddingX; // Padding within button
6942 textWidth += DLG_VERT_PADDING_X * 2; // Padding around button
Bram Moolenaar071d4272004-06-13 20:20:40 +00006943 if (textWidth > dlgwidth)
6944 dlgwidth = textWidth;
6945 pstart = pend + 1;
6946 } while (*pend != NUL);
6947 }
6948
6949 if (dlgwidth < DLG_MIN_WIDTH)
Bram Moolenaar734a8672019-12-02 22:49:38 +01006950 dlgwidth = DLG_MIN_WIDTH; // Don't allow a really thin dialog!
Bram Moolenaar071d4272004-06-13 20:20:40 +00006951
Bram Moolenaar734a8672019-12-02 22:49:38 +01006952 // start to fill in the dlgtemplate information. addressing by WORDs
K.Takatad1c58992022-01-23 12:31:57 +00006953 lStyle = DS_MODALFRAME | WS_CAPTION | DS_3DLOOK | WS_VISIBLE | DS_SETFONT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006954
6955 add_long(lStyle);
6956 add_long(0); // (lExtendedStyle)
Bram Moolenaar734a8672019-12-02 22:49:38 +01006957 pnumitems = p; //save where the number of items must be stored
Bram Moolenaar071d4272004-06-13 20:20:40 +00006958 add_word(0); // NumberOfItems(will change later)
6959 add_word(10); // x
6960 add_word(10); // y
6961 add_word(PixelToDialogX(dlgwidth)); // cx
6962
6963 // Dialog height.
6964 if (vertical)
Bram Moolenaara95d8232013-08-07 15:27:11 +02006965 dlgheight = msgheight + 2 * dlgPaddingY
6966 + DLG_VERT_PADDING_Y + 2 * fontHeight * numButtons;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006967 else
6968 dlgheight = msgheight + 3 * dlgPaddingY + 2 * fontHeight;
6969
6970 // Dialog needs to be taller if contains an edit box.
6971 editboxheight = fontHeight + dlgPaddingY + 4 * DLG_VERT_PADDING_Y;
6972 if (textfield != NULL)
6973 dlgheight += editboxheight;
6974
Bram Moolenaar734a8672019-12-02 22:49:38 +01006975 // Restrict the size to a maximum. Causes a scrollbar to show up.
Bram Moolenaara95d8232013-08-07 15:27:11 +02006976 if (dlgheight > maxDialogHeight)
6977 {
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02006978 msgheight = msgheight - (dlgheight - maxDialogHeight);
6979 dlgheight = maxDialogHeight;
6980 scroll_flag = WS_VSCROLL;
Bram Moolenaar734a8672019-12-02 22:49:38 +01006981 // Make sure scrollbar doesn't appear in the middle of the dialog
K.Takatac81e9bf2022-01-16 14:15:49 +00006982 messageWidth = dlgwidth - dlg_icon_width - 3 * dlgPaddingX;
Bram Moolenaara95d8232013-08-07 15:27:11 +02006983 }
6984
Bram Moolenaar071d4272004-06-13 20:20:40 +00006985 add_word(PixelToDialogY(dlgheight));
6986
6987 add_word(0); // Menu
6988 add_word(0); // Class
6989
Bram Moolenaar734a8672019-12-02 22:49:38 +01006990 // copy the title of the dialog
Bram Moolenaar6edeaf32017-09-26 14:46:04 +02006991 nchar = nCopyAnsiToWideChar(p, (title ? (LPSTR)title
6992 : (LPSTR)("Vim "VIM_VERSION_MEDIUM)), TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006993 p += nchar;
6994
K.Takatad1c58992022-01-23 12:31:57 +00006995 // do the font, since DS_3DLOOK doesn't work properly
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006996# ifdef USE_SYSMENU_FONT
K.Takatad1c58992022-01-23 12:31:57 +00006997 if (use_lfSysmenu)
6998 {
6999 // point size
7000 *p++ = -MulDiv(lfSysmenu.lfHeight, 72,
7001 GetDeviceCaps(hdc, LOGPIXELSY));
7002 wcscpy(p, lfSysmenu.lfFaceName);
7003 nchar = (int)wcslen(lfSysmenu.lfFaceName) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007004 }
K.Takatad1c58992022-01-23 12:31:57 +00007005 else
7006# endif
7007 {
7008 *p++ = DLG_FONT_POINT_SIZE; // point size
7009 nchar = nCopyAnsiToWideChar(p, DLG_FONT_NAME, FALSE);
7010 }
7011 p += nchar;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007012
7013 buttonYpos = msgheight + 2 * dlgPaddingY;
7014
7015 if (textfield != NULL)
7016 buttonYpos += editboxheight;
7017
7018 pstart = tbuffer;
7019 if (!vertical)
Bram Moolenaar734a8672019-12-02 22:49:38 +01007020 horizWidth = (dlgwidth - horizWidth) / 2; // Now it's X offset
Bram Moolenaar071d4272004-06-13 20:20:40 +00007021 for (i = 0; i < numButtons; i++)
7022 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01007023 // get end of this button.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007024 for ( pend = pstart;
7025 *pend && (*pend != DLG_BUTTON_SEP);
7026 pend++)
7027 ;
7028
7029 if (*pend)
7030 *pend = '\0';
7031
7032 /*
7033 * old NOTE:
7034 * setting the BS_DEFPUSHBUTTON style doesn't work because Windows sets
7035 * the focus to the first tab-able button and in so doing makes that
7036 * the default!! Grrr. Workaround: Make the default button the only
7037 * one with WS_TABSTOP style. Means user can't tab between buttons, but
7038 * he/she can use arrow keys.
7039 *
7040 * new NOTE: BS_DEFPUSHBUTTON is required to be able to select the
Bram Moolenaar2c7a7632007-05-10 18:19:11 +00007041 * right button when hitting <Enter>. E.g., for the ":confirm quit"
Bram Moolenaar071d4272004-06-13 20:20:40 +00007042 * dialog. Also needed for when the textfield is the default control.
7043 * It appears to work now (perhaps not on Win95?).
7044 */
7045 if (vertical)
7046 {
7047 p = add_dialog_element(p,
7048 (i == dfltbutton
7049 ? BS_DEFPUSHBUTTON : BS_PUSHBUTTON) | WS_TABSTOP,
7050 PixelToDialogX(DLG_VERT_PADDING_X),
Bram Moolenaar734a8672019-12-02 22:49:38 +01007051 PixelToDialogY(buttonYpos // TBK
Bram Moolenaar071d4272004-06-13 20:20:40 +00007052 + 2 * fontHeight * i),
7053 PixelToDialogX(dlgwidth - 2 * DLG_VERT_PADDING_X),
7054 (WORD)(PixelToDialogY(2 * fontHeight) - 1),
Bram Moolenaar418f81b2016-02-16 20:12:02 +01007055 (WORD)(IDCANCEL + 1 + i), (WORD)0x0080, (char *)pstart);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007056 }
7057 else
7058 {
7059 p = add_dialog_element(p,
7060 (i == dfltbutton
7061 ? BS_DEFPUSHBUTTON : BS_PUSHBUTTON) | WS_TABSTOP,
7062 PixelToDialogX(horizWidth + buttonPositions[i]),
Bram Moolenaar734a8672019-12-02 22:49:38 +01007063 PixelToDialogY(buttonYpos), // TBK
Bram Moolenaar071d4272004-06-13 20:20:40 +00007064 PixelToDialogX(buttonWidths[i]),
7065 (WORD)(PixelToDialogY(2 * fontHeight) - 1),
Bram Moolenaar418f81b2016-02-16 20:12:02 +01007066 (WORD)(IDCANCEL + 1 + i), (WORD)0x0080, (char *)pstart);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007067 }
Bram Moolenaar734a8672019-12-02 22:49:38 +01007068 pstart = pend + 1; //next button
Bram Moolenaar071d4272004-06-13 20:20:40 +00007069 }
7070 *pnumitems += numButtons;
7071
Bram Moolenaar734a8672019-12-02 22:49:38 +01007072 // Vim icon
Bram Moolenaar071d4272004-06-13 20:20:40 +00007073 p = add_dialog_element(p, SS_ICON,
7074 PixelToDialogX(dlgPaddingX),
7075 PixelToDialogY(dlgPaddingY),
K.Takatac81e9bf2022-01-16 14:15:49 +00007076 PixelToDialogX(dlg_icon_width),
7077 PixelToDialogY(dlg_icon_height),
Bram Moolenaar071d4272004-06-13 20:20:40 +00007078 DLG_NONBUTTON_CONTROL + 0, (WORD)0x0082,
7079 dlg_icons[type]);
7080
Bram Moolenaar734a8672019-12-02 22:49:38 +01007081 // Dialog message
Bram Moolenaar748bf032005-02-02 23:04:36 +00007082 p = add_dialog_element(p, ES_LEFT|scroll_flag|ES_MULTILINE|ES_READONLY,
K.Takatac81e9bf2022-01-16 14:15:49 +00007083 PixelToDialogX(2 * dlgPaddingX + dlg_icon_width),
Bram Moolenaar748bf032005-02-02 23:04:36 +00007084 PixelToDialogY(dlgPaddingY),
7085 (WORD)(PixelToDialogX(messageWidth) + 1),
7086 PixelToDialogY(msgheight),
Bram Moolenaar418f81b2016-02-16 20:12:02 +01007087 DLG_NONBUTTON_CONTROL + 1, (WORD)0x0081, (char *)message);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007088
Bram Moolenaar734a8672019-12-02 22:49:38 +01007089 // Edit box
Bram Moolenaar071d4272004-06-13 20:20:40 +00007090 if (textfield != NULL)
7091 {
7092 p = add_dialog_element(p, ES_LEFT|ES_AUTOHSCROLL|WS_TABSTOP|WS_BORDER,
7093 PixelToDialogX(2 * dlgPaddingX),
7094 PixelToDialogY(2 * dlgPaddingY + msgheight),
7095 PixelToDialogX(dlgwidth - 4 * dlgPaddingX),
7096 PixelToDialogY(fontHeight + dlgPaddingY),
Bram Moolenaar418f81b2016-02-16 20:12:02 +01007097 DLG_NONBUTTON_CONTROL + 2, (WORD)0x0081, (char *)textfield);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007098 *pnumitems += 1;
7099 }
7100
7101 *pnumitems += 2;
7102
7103 SelectFont(hdc, oldFont);
7104 DeleteObject(font);
7105 ReleaseDC(hwnd, hdc);
7106
Bram Moolenaar734a8672019-12-02 22:49:38 +01007107 // Let the dialog_callback() function know which button to make default
7108 // If we have an edit box, make that the default. We also need to tell
7109 // dialog_callback() if this dialog contains an edit box or not. We do
7110 // this by setting s_textfield if it does.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007111 if (textfield != NULL)
7112 {
7113 dialog_default_button = DLG_NONBUTTON_CONTROL + 2;
7114 s_textfield = textfield;
7115 }
7116 else
7117 {
7118 dialog_default_button = IDCANCEL + 1 + dfltbutton;
7119 s_textfield = NULL;
7120 }
7121
Bram Moolenaar734a8672019-12-02 22:49:38 +01007122 // show the dialog box modally and get a return value
Bram Moolenaar071d4272004-06-13 20:20:40 +00007123 nchar = (int)DialogBoxIndirect(
Bram Moolenaarafde13b2019-04-28 19:46:49 +02007124 g_hinst,
Bram Moolenaar071d4272004-06-13 20:20:40 +00007125 (LPDLGTEMPLATE)pdlgtemplate,
7126 s_hwnd,
7127 (DLGPROC)dialog_callback);
7128
7129 LocalFree(LocalHandle(pdlgtemplate));
7130 vim_free(tbuffer);
7131 vim_free(buttonWidths);
7132 vim_free(buttonPositions);
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007133 vim_free(ga.ga_data);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007134
Bram Moolenaar734a8672019-12-02 22:49:38 +01007135 // Focus back to our window (for when MDI is used).
Bram Moolenaar071d4272004-06-13 20:20:40 +00007136 (void)SetFocus(s_hwnd);
7137
7138 return nchar;
7139}
7140
Bram Moolenaar734a8672019-12-02 22:49:38 +01007141#endif // FEAT_GUI_DIALOG
Bram Moolenaareb3593b2006-04-22 22:33:57 +00007142
Bram Moolenaar071d4272004-06-13 20:20:40 +00007143/*
7144 * Put a simple element (basic class) onto a dialog template in memory.
7145 * return a pointer to where the next item should be added.
7146 *
7147 * parameters:
7148 * lStyle = additional style flags
7149 * (be careful, NT3.51 & Win32s will ignore the new ones)
7150 * x,y = x & y positions IN DIALOG UNITS
7151 * w,h = width and height IN DIALOG UNITS
7152 * Id = ID used in messages
7153 * clss = class ID, e.g 0x0080 for a button, 0x0082 for a static
7154 * caption = usually text or resource name
7155 *
7156 * TODO: use the length information noted here to enable the dialog creation
7157 * routines to work out more exactly how much memory they need to alloc.
7158 */
7159 static PWORD
7160add_dialog_element(
7161 PWORD p,
7162 DWORD lStyle,
7163 WORD x,
7164 WORD y,
7165 WORD w,
7166 WORD h,
7167 WORD Id,
7168 WORD clss,
7169 const char *caption)
7170{
7171 int nchar;
7172
Bram Moolenaar734a8672019-12-02 22:49:38 +01007173 p = lpwAlign(p); // Align to dword boundary
Bram Moolenaar071d4272004-06-13 20:20:40 +00007174 lStyle = lStyle | WS_VISIBLE | WS_CHILD;
7175 *p++ = LOWORD(lStyle);
7176 *p++ = HIWORD(lStyle);
7177 *p++ = 0; // LOWORD (lExtendedStyle)
7178 *p++ = 0; // HIWORD (lExtendedStyle)
7179 *p++ = x;
7180 *p++ = y;
7181 *p++ = w;
7182 *p++ = h;
7183 *p++ = Id; //9 or 10 words in all
7184
7185 *p++ = (WORD)0xffff;
7186 *p++ = clss; //2 more here
7187
Bram Moolenaar6edeaf32017-09-26 14:46:04 +02007188 nchar = nCopyAnsiToWideChar(p, (LPSTR)caption, TRUE); //strlen(caption)+1
Bram Moolenaar071d4272004-06-13 20:20:40 +00007189 p += nchar;
7190
7191 *p++ = 0; // advance pointer over nExtraStuff WORD - 2 more
7192
Dominique Pelleaf4a61a2021-12-27 17:21:41 +00007193 return p; // total = 15 + strlen(caption) words
7194 // bytes read = 2 * total
Bram Moolenaar071d4272004-06-13 20:20:40 +00007195}
7196
7197
7198/*
7199 * Helper routine. Take an input pointer, return closest pointer that is
7200 * aligned on a DWORD (4 byte) boundary. Taken from the Win32SDK samples.
7201 */
7202 static LPWORD
7203lpwAlign(
7204 LPWORD lpIn)
7205{
Bram Moolenaareb3593b2006-04-22 22:33:57 +00007206 long_u ul;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007207
Bram Moolenaareb3593b2006-04-22 22:33:57 +00007208 ul = (long_u)lpIn;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007209 ul += 3;
7210 ul >>= 2;
7211 ul <<= 2;
7212 return (LPWORD)ul;
7213}
7214
7215/*
7216 * Helper routine. Takes second parameter as Ansi string, copies it to first
7217 * parameter as wide character (16-bits / char) string, and returns integer
7218 * number of wide characters (words) in string (including the trailing wide
7219 * char NULL). Partly taken from the Win32SDK samples.
Bram Moolenaar6edeaf32017-09-26 14:46:04 +02007220 * If "use_enc" is TRUE, 'encoding' is used for "lpAnsiIn". If FALSE, current
7221 * ACP is used for "lpAnsiIn". */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007222 static int
7223nCopyAnsiToWideChar(
7224 LPWORD lpWCStr,
Bram Moolenaar6edeaf32017-09-26 14:46:04 +02007225 LPSTR lpAnsiIn,
7226 BOOL use_enc)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007227{
7228 int nChar = 0;
Bram Moolenaar734a8672019-12-02 22:49:38 +01007229 int len = lstrlen(lpAnsiIn) + 1; // include NUL character
Bram Moolenaar071d4272004-06-13 20:20:40 +00007230 int i;
7231 WCHAR *wn;
7232
Bram Moolenaar6edeaf32017-09-26 14:46:04 +02007233 if (use_enc && enc_codepage >= 0 && (int)GetACP() != enc_codepage)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007234 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01007235 // Not a codepage, use our own conversion function.
Bram Moolenaar418f81b2016-02-16 20:12:02 +01007236 wn = enc_to_utf16((char_u *)lpAnsiIn, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007237 if (wn != NULL)
7238 {
7239 wcscpy(lpWCStr, wn);
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00007240 nChar = (int)wcslen(wn) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007241 vim_free(wn);
7242 }
7243 }
7244 if (nChar == 0)
Bram Moolenaar734a8672019-12-02 22:49:38 +01007245 // Use Win32 conversion function.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007246 nChar = MultiByteToWideChar(
7247 enc_codepage > 0 ? enc_codepage : CP_ACP,
7248 MB_PRECOMPOSED,
7249 lpAnsiIn, len,
7250 lpWCStr, len);
7251 for (i = 0; i < nChar; ++i)
Bram Moolenaar734a8672019-12-02 22:49:38 +01007252 if (lpWCStr[i] == (WORD)'\t') // replace tabs with spaces
Bram Moolenaar071d4272004-06-13 20:20:40 +00007253 lpWCStr[i] = (WORD)' ';
Bram Moolenaar071d4272004-06-13 20:20:40 +00007254
7255 return nChar;
7256}
7257
7258
7259#ifdef FEAT_TEAROFF
7260/*
Bram Moolenaar66857f42017-10-22 16:43:20 +02007261 * Lookup menu handle from "menu_id".
7262 */
7263 static HMENU
7264tearoff_lookup_menuhandle(
7265 vimmenu_T *menu,
7266 WORD menu_id)
7267{
7268 for ( ; menu != NULL; menu = menu->next)
7269 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01007270 if (menu->modes == 0) // this menu has just been deleted
Bram Moolenaar66857f42017-10-22 16:43:20 +02007271 continue;
7272 if (menu_is_separator(menu->dname))
7273 continue;
7274 if ((WORD)((long_u)(menu->submenu_id) | (DWORD)0x8000) == menu_id)
7275 return menu->submenu_id;
7276 }
7277 return NULL;
7278}
7279
7280/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00007281 * The callback function for all the modeless dialogs that make up the
7282 * "tearoff menus" Very simple - forward button presses (to fool Vim into
7283 * thinking its menus have been clicked), and go away when closed.
7284 */
7285 static LRESULT CALLBACK
7286tearoff_callback(
7287 HWND hwnd,
7288 UINT message,
7289 WPARAM wParam,
7290 LPARAM lParam)
7291{
7292 if (message == WM_INITDIALOG)
Bram Moolenaar66857f42017-10-22 16:43:20 +02007293 {
7294 SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR)lParam);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007295 return (TRUE);
Bram Moolenaar66857f42017-10-22 16:43:20 +02007296 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007297
Bram Moolenaar734a8672019-12-02 22:49:38 +01007298 // May show the mouse pointer again.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007299 HandleMouseHide(message, lParam);
7300
7301 if (message == WM_COMMAND)
7302 {
7303 if ((WORD)(LOWORD(wParam)) & 0x8000)
7304 {
7305 POINT mp;
7306 RECT rect;
7307
7308 if (GetCursorPos(&mp) && GetWindowRect(hwnd, &rect))
7309 {
Bram Moolenaar66857f42017-10-22 16:43:20 +02007310 vimmenu_T *menu;
7311
7312 menu = (vimmenu_T*)GetWindowLongPtr(hwnd, DWLP_USER);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007313 (void)TrackPopupMenu(
Bram Moolenaar66857f42017-10-22 16:43:20 +02007314 tearoff_lookup_menuhandle(menu, LOWORD(wParam)),
Bram Moolenaar071d4272004-06-13 20:20:40 +00007315 TPM_LEFTALIGN | TPM_LEFTBUTTON,
7316 (int)rect.right - 8,
7317 (int)mp.y,
Bram Moolenaar734a8672019-12-02 22:49:38 +01007318 (int)0, // reserved param
Bram Moolenaar071d4272004-06-13 20:20:40 +00007319 s_hwnd,
7320 NULL);
7321 /*
7322 * NOTE: The pop-up menu can eat the mouse up event.
7323 * We deal with this in normal.c.
7324 */
7325 }
7326 }
7327 else
Bram Moolenaar734a8672019-12-02 22:49:38 +01007328 // Pass on messages to the main Vim window
Bram Moolenaar071d4272004-06-13 20:20:40 +00007329 PostMessage(s_hwnd, WM_COMMAND, LOWORD(wParam), 0);
7330 /*
7331 * Give main window the focus back: this is so after
7332 * choosing a tearoff button you can start typing again
7333 * straight away.
7334 */
7335 (void)SetFocus(s_hwnd);
7336 return TRUE;
7337 }
7338 if ((message == WM_SYSCOMMAND) && (wParam == SC_CLOSE))
7339 {
7340 DestroyWindow(hwnd);
7341 return TRUE;
7342 }
7343
Bram Moolenaar734a8672019-12-02 22:49:38 +01007344 // When moved around, give main window the focus back.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007345 if (message == WM_EXITSIZEMOVE)
7346 (void)SetActiveWindow(s_hwnd);
7347
7348 return FALSE;
7349}
7350#endif
7351
7352
7353/*
K.Takatad1c58992022-01-23 12:31:57 +00007354 * Computes the dialog base units based on the current dialog font.
7355 * We don't use the GetDialogBaseUnits() API, because we don't use the
7356 * (old-style) system font.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007357 */
7358 static void
7359get_dialog_font_metrics(void)
7360{
7361 HDC hdc;
7362 HFONT hfontTools = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007363 SIZE size;
7364#ifdef USE_SYSMENU_FONT
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01007365 LOGFONTW lfSysmenu;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007366#endif
7367
Bram Moolenaar071d4272004-06-13 20:20:40 +00007368#ifdef USE_SYSMENU_FONT
Bram Moolenaarcea912a2016-10-12 14:20:24 +02007369 if (gui_w32_get_menu_font(&lfSysmenu) == OK)
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01007370 hfontTools = CreateFontIndirectW(&lfSysmenu);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02007371 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00007372#endif
7373 hfontTools = CreateFont(-DLG_FONT_POINT_SIZE, 0, 0, 0, 0, 0, 0, 0,
K.Takatac81e9bf2022-01-16 14:15:49 +00007374 0, 0, 0, 0, VARIABLE_PITCH, DLG_FONT_NAME);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007375
K.Takatad1c58992022-01-23 12:31:57 +00007376 hdc = GetDC(s_hwnd);
7377 SelectObject(hdc, hfontTools);
K.Takataabe628e2022-01-23 16:25:17 +00007378 GetAverageFontSize(hdc, &size);
K.Takatad1c58992022-01-23 12:31:57 +00007379 ReleaseDC(s_hwnd, hdc);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007380
K.Takataabe628e2022-01-23 16:25:17 +00007381 s_dlgfntwidth = (WORD)size.cx;
K.Takatad1c58992022-01-23 12:31:57 +00007382 s_dlgfntheight = (WORD)size.cy;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007383}
7384
7385#if defined(FEAT_MENU) && defined(FEAT_TEAROFF)
7386/*
7387 * Create a pseudo-"tearoff menu" based on the child
7388 * items of a given menu pointer.
7389 */
7390 static void
7391gui_mch_tearoff(
7392 char_u *title,
7393 vimmenu_T *menu,
7394 int initX,
7395 int initY)
7396{
7397 WORD *p, *pdlgtemplate, *pnumitems, *ptrueheight;
7398 int template_len;
7399 int nchar, textWidth, submenuWidth;
7400 DWORD lStyle;
7401 DWORD lExtendedStyle;
7402 WORD dlgwidth;
7403 WORD menuID;
7404 vimmenu_T *pmenu;
Bram Moolenaar66857f42017-10-22 16:43:20 +02007405 vimmenu_T *top_menu;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007406 vimmenu_T *the_menu = menu;
7407 HWND hwnd;
7408 HDC hdc;
7409 HFONT font, oldFont;
7410 int col, spaceWidth, len;
7411 int columnWidths[2];
7412 char_u *label, *text;
7413 int acLen = 0;
7414 int nameLen;
7415 int padding0, padding1, padding2 = 0;
7416 int sepPadding=0;
Bram Moolenaar9588a0f2005-01-08 21:45:39 +00007417 int x;
7418 int y;
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01007419# ifdef USE_SYSMENU_FONT
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01007420 LOGFONTW lfSysmenu;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007421 int use_lfSysmenu = FALSE;
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01007422# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007423
7424 /*
7425 * If this menu is already torn off, move it to the mouse position.
7426 */
7427 if (IsWindow(menu->tearoff_handle))
7428 {
7429 POINT mp;
K.Takata45f9cfb2022-01-21 11:11:00 +00007430 if (GetCursorPos(&mp))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007431 {
7432 SetWindowPos(menu->tearoff_handle, NULL, mp.x, mp.y, 0, 0,
7433 SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
7434 }
7435 return;
7436 }
7437
7438 /*
7439 * Create a new tearoff.
7440 */
7441 if (*title == MNU_HIDDEN_CHAR)
7442 title++;
7443
Bram Moolenaar734a8672019-12-02 22:49:38 +01007444 // Allocate memory to store the dialog template. It's made bigger when
7445 // needed.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007446 template_len = DLG_ALLOC_SIZE;
7447 pdlgtemplate = p = (WORD *)LocalAlloc(LPTR, template_len);
7448 if (p == NULL)
7449 return;
7450
7451 hwnd = GetDesktopWindow();
7452 hdc = GetWindowDC(hwnd);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01007453# ifdef USE_SYSMENU_FONT
Bram Moolenaar071d4272004-06-13 20:20:40 +00007454 if (gui_w32_get_menu_font(&lfSysmenu) == OK)
7455 {
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01007456 font = CreateFontIndirectW(&lfSysmenu);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007457 use_lfSysmenu = TRUE;
7458 }
7459 else
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01007460# endif
K.Takatad1c58992022-01-23 12:31:57 +00007461 font = CreateFont(-DLG_FONT_POINT_SIZE, 0, 0, 0, 0, 0, 0, 0,
7462 0, 0, 0, 0, VARIABLE_PITCH, DLG_FONT_NAME);
7463
7464 oldFont = SelectFont(hdc, font);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007465
Bram Moolenaar734a8672019-12-02 22:49:38 +01007466 // Calculate width of a single space. Used for padding columns to the
7467 // right width.
Bram Moolenaar418f81b2016-02-16 20:12:02 +01007468 spaceWidth = GetTextWidth(hdc, (char_u *)" ", 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007469
Bram Moolenaar734a8672019-12-02 22:49:38 +01007470 // Figure out max width of the text column, the accelerator column and the
7471 // optional submenu column.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007472 submenuWidth = 0;
7473 for (col = 0; col < 2; col++)
7474 {
7475 columnWidths[col] = 0;
Bram Moolenaar00d253e2020-04-06 22:13:01 +02007476 FOR_ALL_CHILD_MENUS(menu, pmenu)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007477 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01007478 // Use "dname" here to compute the width of the visible text.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007479 text = (col == 0) ? pmenu->dname : pmenu->actext;
7480 if (text != NULL && *text != NUL)
7481 {
7482 textWidth = GetTextWidthEnc(hdc, text, (int)STRLEN(text));
7483 if (textWidth > columnWidths[col])
7484 columnWidths[col] = textWidth;
7485 }
7486 if (pmenu->children != NULL)
7487 submenuWidth = TEAROFF_COLUMN_PADDING * spaceWidth;
7488 }
7489 }
7490 if (columnWidths[1] == 0)
7491 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01007492 // no accelerators
Bram Moolenaar071d4272004-06-13 20:20:40 +00007493 if (submenuWidth != 0)
7494 columnWidths[0] += submenuWidth;
7495 else
7496 columnWidths[0] += spaceWidth;
7497 }
7498 else
7499 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01007500 // there is an accelerator column
Bram Moolenaar071d4272004-06-13 20:20:40 +00007501 columnWidths[0] += TEAROFF_COLUMN_PADDING * spaceWidth;
7502 columnWidths[1] += submenuWidth;
7503 }
7504
7505 /*
7506 * Now find the total width of our 'menu'.
7507 */
7508 textWidth = columnWidths[0] + columnWidths[1];
7509 if (submenuWidth != 0)
7510 {
Bram Moolenaar418f81b2016-02-16 20:12:02 +01007511 submenuWidth = GetTextWidth(hdc, (char_u *)TEAROFF_SUBMENU_LABEL,
Bram Moolenaar071d4272004-06-13 20:20:40 +00007512 (int)STRLEN(TEAROFF_SUBMENU_LABEL));
7513 textWidth += submenuWidth;
7514 }
7515 dlgwidth = GetTextWidthEnc(hdc, title, (int)STRLEN(title));
7516 if (textWidth > dlgwidth)
7517 dlgwidth = textWidth;
7518 dlgwidth += 2 * TEAROFF_PADDING_X + TEAROFF_BUTTON_PAD_X;
7519
Bram Moolenaar734a8672019-12-02 22:49:38 +01007520 // start to fill in the dlgtemplate information. addressing by WORDs
K.Takatad1c58992022-01-23 12:31:57 +00007521 lStyle = DS_MODALFRAME | WS_CAPTION | WS_SYSMENU | DS_SETFONT | WS_VISIBLE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007522
7523 lExtendedStyle = WS_EX_TOOLWINDOW|WS_EX_STATICEDGE;
7524 *p++ = LOWORD(lStyle);
7525 *p++ = HIWORD(lStyle);
7526 *p++ = LOWORD(lExtendedStyle);
7527 *p++ = HIWORD(lExtendedStyle);
Bram Moolenaar734a8672019-12-02 22:49:38 +01007528 pnumitems = p; // save where the number of items must be stored
Bram Moolenaar071d4272004-06-13 20:20:40 +00007529 *p++ = 0; // NumberOfItems(will change later)
Bram Moolenaar9588a0f2005-01-08 21:45:39 +00007530 gui_mch_getmouse(&x, &y);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007531 if (initX == 0xffffL)
Bram Moolenaar9588a0f2005-01-08 21:45:39 +00007532 *p++ = PixelToDialogX(x); // x
Bram Moolenaar071d4272004-06-13 20:20:40 +00007533 else
7534 *p++ = PixelToDialogX(initX); // x
7535 if (initY == 0xffffL)
Bram Moolenaar9588a0f2005-01-08 21:45:39 +00007536 *p++ = PixelToDialogY(y); // y
Bram Moolenaar071d4272004-06-13 20:20:40 +00007537 else
7538 *p++ = PixelToDialogY(initY); // y
7539 *p++ = PixelToDialogX(dlgwidth); // cx
7540 ptrueheight = p;
7541 *p++ = 0; // dialog height: changed later anyway
7542 *p++ = 0; // Menu
7543 *p++ = 0; // Class
7544
Bram Moolenaar734a8672019-12-02 22:49:38 +01007545 // copy the title of the dialog
Bram Moolenaar071d4272004-06-13 20:20:40 +00007546 nchar = nCopyAnsiToWideChar(p, ((*title)
Bram Moolenaar6edeaf32017-09-26 14:46:04 +02007547 ? (LPSTR)title
7548 : (LPSTR)("Vim "VIM_VERSION_MEDIUM)), TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007549 p += nchar;
7550
K.Takatad1c58992022-01-23 12:31:57 +00007551 // do the font, since DS_3DLOOK doesn't work properly
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01007552# ifdef USE_SYSMENU_FONT
K.Takatad1c58992022-01-23 12:31:57 +00007553 if (use_lfSysmenu)
7554 {
7555 // point size
7556 *p++ = -MulDiv(lfSysmenu.lfHeight, 72,
7557 GetDeviceCaps(hdc, LOGPIXELSY));
7558 wcscpy(p, lfSysmenu.lfFaceName);
7559 nchar = (int)wcslen(lfSysmenu.lfFaceName) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007560 }
K.Takatad1c58992022-01-23 12:31:57 +00007561 else
7562# endif
7563 {
7564 *p++ = DLG_FONT_POINT_SIZE; // point size
7565 nchar = nCopyAnsiToWideChar(p, DLG_FONT_NAME, FALSE);
7566 }
7567 p += nchar;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007568
7569 /*
7570 * Loop over all the items in the menu.
7571 * But skip over the tearbar.
7572 */
7573 if (STRCMP(menu->children->name, TEAR_STRING) == 0)
7574 menu = menu->children->next;
7575 else
7576 menu = menu->children;
Bram Moolenaar66857f42017-10-22 16:43:20 +02007577 top_menu = menu;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007578 for ( ; menu != NULL; menu = menu->next)
7579 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01007580 if (menu->modes == 0) // this menu has just been deleted
Bram Moolenaar071d4272004-06-13 20:20:40 +00007581 continue;
7582 if (menu_is_separator(menu->dname))
7583 {
7584 sepPadding += 3;
7585 continue;
7586 }
7587
Bram Moolenaar734a8672019-12-02 22:49:38 +01007588 // Check if there still is plenty of room in the template. Make it
7589 // larger when needed.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007590 if (((char *)p - (char *)pdlgtemplate) + 1000 > template_len)
7591 {
7592 WORD *newp;
7593
7594 newp = (WORD *)LocalAlloc(LPTR, template_len + 4096);
7595 if (newp != NULL)
7596 {
7597 template_len += 4096;
7598 mch_memmove(newp, pdlgtemplate,
7599 (char *)p - (char *)pdlgtemplate);
7600 p = newp + (p - pdlgtemplate);
7601 pnumitems = newp + (pnumitems - pdlgtemplate);
7602 ptrueheight = newp + (ptrueheight - pdlgtemplate);
7603 LocalFree(LocalHandle(pdlgtemplate));
7604 pdlgtemplate = newp;
7605 }
7606 }
7607
Bram Moolenaar734a8672019-12-02 22:49:38 +01007608 // Figure out minimal length of this menu label. Use "name" for the
7609 // actual text, "dname" for estimating the displayed size. "name"
7610 // has "&a" for mnemonic and includes the accelerator.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007611 len = nameLen = (int)STRLEN(menu->name);
7612 padding0 = (columnWidths[0] - GetTextWidthEnc(hdc, menu->dname,
7613 (int)STRLEN(menu->dname))) / spaceWidth;
7614 len += padding0;
7615
7616 if (menu->actext != NULL)
7617 {
7618 acLen = (int)STRLEN(menu->actext);
7619 len += acLen;
7620 textWidth = GetTextWidthEnc(hdc, menu->actext, acLen);
7621 }
7622 else
7623 textWidth = 0;
7624 padding1 = (columnWidths[1] - textWidth) / spaceWidth;
7625 len += padding1;
7626
7627 if (menu->children == NULL)
7628 {
7629 padding2 = submenuWidth / spaceWidth;
7630 len += padding2;
7631 menuID = (WORD)(menu->id);
7632 }
7633 else
7634 {
7635 len += (int)STRLEN(TEAROFF_SUBMENU_LABEL);
Bram Moolenaareb3593b2006-04-22 22:33:57 +00007636 menuID = (WORD)((long_u)(menu->submenu_id) | (DWORD)0x8000);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007637 }
7638
Bram Moolenaar734a8672019-12-02 22:49:38 +01007639 // Allocate menu label and fill it in
Bram Moolenaar964b3742019-05-24 18:54:09 +02007640 text = label = alloc(len + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007641 if (label == NULL)
7642 break;
7643
Bram Moolenaarce0842a2005-07-18 21:58:11 +00007644 vim_strncpy(text, menu->name, nameLen);
Bram Moolenaar734a8672019-12-02 22:49:38 +01007645 text = vim_strchr(text, TAB); // stop at TAB before actext
Bram Moolenaar071d4272004-06-13 20:20:40 +00007646 if (text == NULL)
Bram Moolenaar734a8672019-12-02 22:49:38 +01007647 text = label + nameLen; // no actext, use whole name
Bram Moolenaar071d4272004-06-13 20:20:40 +00007648 while (padding0-- > 0)
7649 *text++ = ' ';
7650 if (menu->actext != NULL)
7651 {
7652 STRNCPY(text, menu->actext, acLen);
7653 text += acLen;
7654 }
7655 while (padding1-- > 0)
7656 *text++ = ' ';
7657 if (menu->children != NULL)
7658 {
7659 STRCPY(text, TEAROFF_SUBMENU_LABEL);
7660 text += STRLEN(TEAROFF_SUBMENU_LABEL);
7661 }
7662 else
7663 {
7664 while (padding2-- > 0)
7665 *text++ = ' ';
7666 }
7667 *text = NUL;
7668
7669 /*
7670 * BS_LEFT will just be ignored on Win32s/NT3.5x - on
7671 * W95/NT4 it makes the tear-off look more like a menu.
7672 */
7673 p = add_dialog_element(p,
7674 BS_PUSHBUTTON|BS_LEFT,
7675 (WORD)PixelToDialogX(TEAROFF_PADDING_X),
7676 (WORD)(sepPadding + 1 + 13 * (*pnumitems)),
7677 (WORD)PixelToDialogX(dlgwidth - 2 * TEAROFF_PADDING_X),
7678 (WORD)12,
Bram Moolenaar418f81b2016-02-16 20:12:02 +01007679 menuID, (WORD)0x0080, (char *)label);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007680 vim_free(label);
7681 (*pnumitems)++;
7682 }
7683
7684 *ptrueheight = (WORD)(sepPadding + 1 + 13 * (*pnumitems));
7685
7686
Bram Moolenaar734a8672019-12-02 22:49:38 +01007687 // show modelessly
Bram Moolenaar66857f42017-10-22 16:43:20 +02007688 the_menu->tearoff_handle = CreateDialogIndirectParam(
Bram Moolenaarafde13b2019-04-28 19:46:49 +02007689 g_hinst,
Bram Moolenaar071d4272004-06-13 20:20:40 +00007690 (LPDLGTEMPLATE)pdlgtemplate,
7691 s_hwnd,
Bram Moolenaar66857f42017-10-22 16:43:20 +02007692 (DLGPROC)tearoff_callback,
7693 (LPARAM)top_menu);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007694
7695 LocalFree(LocalHandle(pdlgtemplate));
7696 SelectFont(hdc, oldFont);
7697 DeleteObject(font);
7698 ReleaseDC(hwnd, hdc);
7699
7700 /*
7701 * Reassert ourselves as the active window. This is so that after creating
7702 * a tearoff, the user doesn't have to click with the mouse just to start
7703 * typing again!
7704 */
7705 (void)SetActiveWindow(s_hwnd);
7706
Bram Moolenaar734a8672019-12-02 22:49:38 +01007707 // make sure the right buttons are enabled
Bram Moolenaar071d4272004-06-13 20:20:40 +00007708 force_menu_update = TRUE;
7709}
7710#endif
7711
7712#if defined(FEAT_TOOLBAR) || defined(PROTO)
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01007713# include "gui_w32_rc.h"
Bram Moolenaar071d4272004-06-13 20:20:40 +00007714
Bram Moolenaar071d4272004-06-13 20:20:40 +00007715/*
7716 * Create the toolbar, initially unpopulated.
7717 * (just like the menu, there are no defaults, it's all
7718 * set up through menu.vim)
7719 */
7720 static void
7721initialise_toolbar(void)
7722{
7723 InitCommonControls();
7724 s_toolbarhwnd = CreateToolbarEx(
7725 s_hwnd,
7726 WS_CHILD | TBSTYLE_TOOLTIPS | TBSTYLE_FLAT,
7727 4000, //any old big number
Bram Moolenaar2c7a7632007-05-10 18:19:11 +00007728 31, //number of images in initial bitmap
Bram Moolenaarafde13b2019-04-28 19:46:49 +02007729 g_hinst,
Bram Moolenaar071d4272004-06-13 20:20:40 +00007730 IDR_TOOLBAR1, // id of initial bitmap
7731 NULL,
7732 0, // initial number of buttons
7733 TOOLBAR_BUTTON_WIDTH, //api guide is wrong!
7734 TOOLBAR_BUTTON_HEIGHT,
7735 TOOLBAR_BUTTON_WIDTH,
7736 TOOLBAR_BUTTON_HEIGHT,
7737 sizeof(TBBUTTON)
7738 );
Bram Moolenaar5f763342019-11-17 22:54:10 +01007739
7740 // Remove transparency from the toolbar to prevent the main window
7741 // background colour showing through
7742 SendMessage(s_toolbarhwnd, TB_SETSTYLE, 0,
7743 SendMessage(s_toolbarhwnd, TB_GETSTYLE, 0, 0) & ~TBSTYLE_TRANSPARENT);
7744
Bram Moolenaar5f919ee2013-07-21 17:46:43 +02007745 s_toolbar_wndproc = SubclassWindow(s_toolbarhwnd, toolbar_wndproc);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007746
7747 gui_mch_show_toolbar(vim_strchr(p_go, GO_TOOLBAR) != NULL);
K.Takatac81e9bf2022-01-16 14:15:49 +00007748
7749 update_toolbar_size();
7750}
7751
7752 static void
7753update_toolbar_size(void)
7754{
7755 int w, h;
7756 TBMETRICS tbm = {sizeof(TBMETRICS)};
7757
7758 tbm.dwMask = TBMF_PAD | TBMF_BUTTONSPACING;
7759 SendMessage(s_toolbarhwnd, TB_GETMETRICS, 0, (LPARAM)&tbm);
7760 //TRACE("Pad: %d, %d", tbm.cxPad, tbm.cyPad);
7761 //TRACE("ButtonSpacing: %d, %d", tbm.cxButtonSpacing, tbm.cyButtonSpacing);
7762
7763 w = (TOOLBAR_BUTTON_WIDTH + tbm.cxPad) * s_dpi / DEFAULT_DPI;
7764 h = (TOOLBAR_BUTTON_HEIGHT + tbm.cyPad) * s_dpi / DEFAULT_DPI;
7765 //TRACE("button size: %d, %d", w, h);
7766 SendMessage(s_toolbarhwnd, TB_SETBUTTONSIZE, 0, MAKELPARAM(w, h));
7767 gui.toolbar_height = h + 6;
7768
7769 //DWORD s = SendMessage(s_toolbarhwnd, TB_GETBUTTONSIZE, 0, 0);
7770 //TRACE("actual button size: %d, %d", LOWORD(s), HIWORD(s));
7771
7772 // TODO:
7773 // Currently, this function only updates the size of toolbar buttons.
7774 // It would be nice if the toolbar images are resized based on DPI.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007775}
7776
Bram Moolenaar5f919ee2013-07-21 17:46:43 +02007777 static LRESULT CALLBACK
7778toolbar_wndproc(
7779 HWND hwnd,
7780 UINT uMsg,
7781 WPARAM wParam,
7782 LPARAM lParam)
7783{
7784 HandleMouseHide(uMsg, lParam);
7785 return CallWindowProc(s_toolbar_wndproc, hwnd, uMsg, wParam, lParam);
7786}
7787
Bram Moolenaar071d4272004-06-13 20:20:40 +00007788 static int
7789get_toolbar_bitmap(vimmenu_T *menu)
7790{
7791 int i = -1;
7792
7793 /*
7794 * Check user bitmaps first, unless builtin is specified.
7795 */
Bram Moolenaarcea912a2016-10-12 14:20:24 +02007796 if (!menu->icon_builtin)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007797 {
7798 char_u fname[MAXPATHL];
7799 HANDLE hbitmap = NULL;
7800
7801 if (menu->iconfile != NULL)
7802 {
7803 gui_find_iconfile(menu->iconfile, fname, "bmp");
7804 hbitmap = LoadImage(
7805 NULL,
Bram Moolenaar418f81b2016-02-16 20:12:02 +01007806 (LPCSTR)fname,
Bram Moolenaar071d4272004-06-13 20:20:40 +00007807 IMAGE_BITMAP,
7808 TOOLBAR_BUTTON_WIDTH,
7809 TOOLBAR_BUTTON_HEIGHT,
7810 LR_LOADFROMFILE |
7811 LR_LOADMAP3DCOLORS
7812 );
7813 }
7814
7815 /*
7816 * If the LoadImage call failed, or the "icon=" file
7817 * didn't exist or wasn't specified, try the menu name
7818 */
7819 if (hbitmap == NULL
Bram Moolenaara5f5c8b2013-06-27 22:29:38 +02007820 && (gui_find_bitmap(
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01007821# ifdef FEAT_MULTI_LANG
Bram Moolenaara5f5c8b2013-06-27 22:29:38 +02007822 menu->en_dname != NULL ? menu->en_dname :
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01007823# endif
Bram Moolenaara5f5c8b2013-06-27 22:29:38 +02007824 menu->dname, fname, "bmp") == OK))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007825 hbitmap = LoadImage(
7826 NULL,
Bram Moolenaar418f81b2016-02-16 20:12:02 +01007827 (LPCSTR)fname,
Bram Moolenaar071d4272004-06-13 20:20:40 +00007828 IMAGE_BITMAP,
7829 TOOLBAR_BUTTON_WIDTH,
7830 TOOLBAR_BUTTON_HEIGHT,
7831 LR_LOADFROMFILE |
7832 LR_LOADMAP3DCOLORS
7833 );
7834
7835 if (hbitmap != NULL)
7836 {
7837 TBADDBITMAP tbAddBitmap;
7838
7839 tbAddBitmap.hInst = NULL;
Bram Moolenaareb3593b2006-04-22 22:33:57 +00007840 tbAddBitmap.nID = (long_u)hbitmap;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007841
7842 i = (int)SendMessage(s_toolbarhwnd, TB_ADDBITMAP,
7843 (WPARAM)1, (LPARAM)&tbAddBitmap);
Bram Moolenaar734a8672019-12-02 22:49:38 +01007844 // i will be set to -1 if it fails
Bram Moolenaar071d4272004-06-13 20:20:40 +00007845 }
7846 }
7847 if (i == -1 && menu->iconidx >= 0 && menu->iconidx < TOOLBAR_BITMAP_COUNT)
7848 i = menu->iconidx;
7849
7850 return i;
7851}
7852#endif
7853
Bram Moolenaar3991dab2006-03-27 17:01:56 +00007854#if defined(FEAT_GUI_TABLINE) || defined(PROTO)
7855 static void
7856initialise_tabline(void)
7857{
7858 InitCommonControls();
7859
Bram Moolenaar910f66f2006-04-05 20:41:53 +00007860 s_tabhwnd = CreateWindow(WC_TABCONTROL, "Vim tabline",
Bram Moolenaareb3593b2006-04-22 22:33:57 +00007861 WS_CHILD|TCS_FOCUSNEVER|TCS_TOOLTIPS,
Bram Moolenaar3991dab2006-03-27 17:01:56 +00007862 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
Bram Moolenaarafde13b2019-04-28 19:46:49 +02007863 CW_USEDEFAULT, s_hwnd, NULL, g_hinst, NULL);
Bram Moolenaar5f919ee2013-07-21 17:46:43 +02007864 s_tabline_wndproc = SubclassWindow(s_tabhwnd, tabline_wndproc);
Bram Moolenaar910f66f2006-04-05 20:41:53 +00007865
Bram Moolenaar551dbcc2006-04-25 22:13:59 +00007866 gui.tabline_height = TABLINE_HEIGHT;
7867
Bram Moolenaar551dbcc2006-04-25 22:13:59 +00007868 set_tabline_font();
Bram Moolenaar3991dab2006-03-27 17:01:56 +00007869}
Bram Moolenaar5f919ee2013-07-21 17:46:43 +02007870
Bram Moolenaarca05aa22017-10-22 15:36:14 +02007871/*
7872 * Get tabpage_T from POINT.
7873 */
7874 static tabpage_T *
7875GetTabFromPoint(
7876 HWND hWnd,
7877 POINT pt)
7878{
7879 tabpage_T *ptp = NULL;
7880
7881 if (gui_mch_showing_tabline())
7882 {
7883 TCHITTESTINFO htinfo;
7884 htinfo.pt = pt;
K.Takatac81e9bf2022-01-16 14:15:49 +00007885 // ignore if a window under cursor is not tabcontrol.
Bram Moolenaarca05aa22017-10-22 15:36:14 +02007886 if (s_tabhwnd == hWnd)
7887 {
7888 int idx = TabCtrl_HitTest(s_tabhwnd, &htinfo);
7889 if (idx != -1)
7890 ptp = find_tabpage(idx + 1);
7891 }
7892 }
7893 return ptp;
7894}
7895
7896static POINT s_pt = {0, 0};
7897static HCURSOR s_hCursor = NULL;
7898
Bram Moolenaar5f919ee2013-07-21 17:46:43 +02007899 static LRESULT CALLBACK
7900tabline_wndproc(
7901 HWND hwnd,
7902 UINT uMsg,
7903 WPARAM wParam,
7904 LPARAM lParam)
7905{
Bram Moolenaarca05aa22017-10-22 15:36:14 +02007906 POINT pt;
7907 tabpage_T *tp;
7908 RECT rect;
7909 int nCenter;
7910 int idx0;
7911 int idx1;
7912
Bram Moolenaar5f919ee2013-07-21 17:46:43 +02007913 HandleMouseHide(uMsg, lParam);
Bram Moolenaarca05aa22017-10-22 15:36:14 +02007914
7915 switch (uMsg)
7916 {
7917 case WM_LBUTTONDOWN:
7918 {
7919 s_pt.x = GET_X_LPARAM(lParam);
7920 s_pt.y = GET_Y_LPARAM(lParam);
7921 SetCapture(hwnd);
Bram Moolenaar734a8672019-12-02 22:49:38 +01007922 s_hCursor = GetCursor(); // backup default cursor
Bram Moolenaarca05aa22017-10-22 15:36:14 +02007923 break;
7924 }
7925 case WM_MOUSEMOVE:
7926 if (GetCapture() == hwnd
7927 && ((wParam & MK_LBUTTON)) != 0)
7928 {
7929 pt.x = GET_X_LPARAM(lParam);
7930 pt.y = s_pt.y;
K.Takatac81e9bf2022-01-16 14:15:49 +00007931 if (abs(pt.x - s_pt.x) >
7932 pGetSystemMetricsForDpi(SM_CXDRAG, s_dpi))
Bram Moolenaarca05aa22017-10-22 15:36:14 +02007933 {
7934 SetCursor(LoadCursor(NULL, IDC_SIZEWE));
7935
7936 tp = GetTabFromPoint(hwnd, pt);
7937 if (tp != NULL)
7938 {
7939 idx0 = tabpage_index(curtab) - 1;
7940 idx1 = tabpage_index(tp) - 1;
7941
7942 TabCtrl_GetItemRect(hwnd, idx1, &rect);
7943 nCenter = rect.left + (rect.right - rect.left) / 2;
7944
Bram Moolenaar734a8672019-12-02 22:49:38 +01007945 // Check if the mouse cursor goes over the center of
7946 // the next tab to prevent "flickering".
Bram Moolenaarca05aa22017-10-22 15:36:14 +02007947 if ((idx0 < idx1) && (nCenter < pt.x))
7948 {
7949 tabpage_move(idx1 + 1);
7950 update_screen(0);
7951 }
7952 else if ((idx1 < idx0) && (pt.x < nCenter))
7953 {
7954 tabpage_move(idx1);
7955 update_screen(0);
7956 }
7957 }
7958 }
7959 }
7960 break;
7961 case WM_LBUTTONUP:
7962 {
7963 if (GetCapture() == hwnd)
7964 {
7965 SetCursor(s_hCursor);
7966 ReleaseCapture();
7967 }
7968 break;
7969 }
7970 default:
7971 break;
7972 }
7973
Bram Moolenaar5f919ee2013-07-21 17:46:43 +02007974 return CallWindowProc(s_tabline_wndproc, hwnd, uMsg, wParam, lParam);
7975}
Bram Moolenaar3991dab2006-03-27 17:01:56 +00007976#endif
7977
Bram Moolenaar071d4272004-06-13 20:20:40 +00007978#if defined(FEAT_OLE) || defined(FEAT_EVAL) || defined(PROTO)
7979/*
7980 * Make the GUI window come to the foreground.
7981 */
7982 void
7983gui_mch_set_foreground(void)
7984{
7985 if (IsIconic(s_hwnd))
7986 SendMessage(s_hwnd, WM_SYSCOMMAND, SC_RESTORE, 0);
7987 SetForegroundWindow(s_hwnd);
7988}
7989#endif
7990
7991#if defined(FEAT_MBYTE_IME) && defined(DYNAMIC_IME)
7992 static void
7993dyn_imm_load(void)
7994{
Bram Moolenaarebbcb822010-10-23 14:02:54 +02007995 hLibImm = vimLoadLib("imm32.dll");
Bram Moolenaar071d4272004-06-13 20:20:40 +00007996 if (hLibImm == NULL)
7997 return;
7998
Bram Moolenaar071d4272004-06-13 20:20:40 +00007999 pImmGetCompositionStringW
8000 = (void *)GetProcAddress(hLibImm, "ImmGetCompositionStringW");
8001 pImmGetContext
8002 = (void *)GetProcAddress(hLibImm, "ImmGetContext");
8003 pImmAssociateContext
8004 = (void *)GetProcAddress(hLibImm, "ImmAssociateContext");
8005 pImmReleaseContext
8006 = (void *)GetProcAddress(hLibImm, "ImmReleaseContext");
8007 pImmGetOpenStatus
8008 = (void *)GetProcAddress(hLibImm, "ImmGetOpenStatus");
8009 pImmSetOpenStatus
8010 = (void *)GetProcAddress(hLibImm, "ImmSetOpenStatus");
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01008011 pImmGetCompositionFontW
8012 = (void *)GetProcAddress(hLibImm, "ImmGetCompositionFontW");
8013 pImmSetCompositionFontW
8014 = (void *)GetProcAddress(hLibImm, "ImmSetCompositionFontW");
Bram Moolenaar071d4272004-06-13 20:20:40 +00008015 pImmSetCompositionWindow
8016 = (void *)GetProcAddress(hLibImm, "ImmSetCompositionWindow");
8017 pImmGetConversionStatus
8018 = (void *)GetProcAddress(hLibImm, "ImmGetConversionStatus");
Bram Moolenaarca003e12006-03-17 23:19:38 +00008019 pImmSetConversionStatus
8020 = (void *)GetProcAddress(hLibImm, "ImmSetConversionStatus");
Bram Moolenaar071d4272004-06-13 20:20:40 +00008021
K.Takatab0b2b732022-01-19 12:59:21 +00008022 if ( pImmGetCompositionStringW == NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00008023 || pImmGetContext == NULL
8024 || pImmAssociateContext == NULL
8025 || pImmReleaseContext == NULL
8026 || pImmGetOpenStatus == NULL
8027 || pImmSetOpenStatus == NULL
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01008028 || pImmGetCompositionFontW == NULL
8029 || pImmSetCompositionFontW == NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00008030 || pImmSetCompositionWindow == NULL
Bram Moolenaarca003e12006-03-17 23:19:38 +00008031 || pImmGetConversionStatus == NULL
8032 || pImmSetConversionStatus == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008033 {
8034 FreeLibrary(hLibImm);
8035 hLibImm = NULL;
8036 pImmGetContext = NULL;
8037 return;
8038 }
8039
8040 return;
8041}
8042
Bram Moolenaar071d4272004-06-13 20:20:40 +00008043#endif
8044
8045#if defined(FEAT_SIGN_ICONS) || defined(PROTO)
8046
8047# ifdef FEAT_XPM_W32
8048# define IMAGE_XPM 100
8049# endif
8050
8051typedef struct _signicon_t
8052{
8053 HANDLE hImage;
8054 UINT uType;
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01008055# ifdef FEAT_XPM_W32
Bram Moolenaar734a8672019-12-02 22:49:38 +01008056 HANDLE hShape; // Mask bitmap handle
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01008057# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008058} signicon_t;
8059
8060 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01008061gui_mch_drawsign(int row, int col, int typenr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008062{
8063 signicon_t *sign;
8064 int x, y, w, h;
8065
8066 if (!gui.in_use || (sign = (signicon_t *)sign_get_image(typenr)) == NULL)
8067 return;
8068
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01008069# if defined(FEAT_DIRECTX)
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01008070 if (IS_ENABLE_DIRECTX())
8071 DWriteContext_Flush(s_dwc);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01008072# endif
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01008073
Bram Moolenaar071d4272004-06-13 20:20:40 +00008074 x = TEXT_X(col);
8075 y = TEXT_Y(row);
8076 w = gui.char_width * 2;
8077 h = gui.char_height;
8078 switch (sign->uType)
8079 {
8080 case IMAGE_BITMAP:
8081 {
8082 HDC hdcMem;
8083 HBITMAP hbmpOld;
8084
8085 hdcMem = CreateCompatibleDC(s_hdc);
8086 hbmpOld = (HBITMAP)SelectObject(hdcMem, sign->hImage);
8087 BitBlt(s_hdc, x, y, w, h, hdcMem, 0, 0, SRCCOPY);
8088 SelectObject(hdcMem, hbmpOld);
8089 DeleteDC(hdcMem);
8090 }
8091 break;
8092 case IMAGE_ICON:
8093 case IMAGE_CURSOR:
8094 DrawIconEx(s_hdc, x, y, (HICON)sign->hImage, w, h, 0, NULL, DI_NORMAL);
8095 break;
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01008096# ifdef FEAT_XPM_W32
Bram Moolenaar071d4272004-06-13 20:20:40 +00008097 case IMAGE_XPM:
8098 {
8099 HDC hdcMem;
8100 HBITMAP hbmpOld;
8101
8102 hdcMem = CreateCompatibleDC(s_hdc);
8103 hbmpOld = (HBITMAP)SelectObject(hdcMem, sign->hShape);
Bram Moolenaar734a8672019-12-02 22:49:38 +01008104 // Make hole
Bram Moolenaar071d4272004-06-13 20:20:40 +00008105 BitBlt(s_hdc, x, y, w, h, hdcMem, 0, 0, SRCAND);
8106
8107 SelectObject(hdcMem, sign->hImage);
Bram Moolenaar734a8672019-12-02 22:49:38 +01008108 // Paint sign
Bram Moolenaar071d4272004-06-13 20:20:40 +00008109 BitBlt(s_hdc, x, y, w, h, hdcMem, 0, 0, SRCPAINT);
8110 SelectObject(hdcMem, hbmpOld);
8111 DeleteDC(hdcMem);
8112 }
8113 break;
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01008114# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008115 }
8116}
8117
8118 static void
8119close_signicon_image(signicon_t *sign)
8120{
8121 if (sign)
8122 switch (sign->uType)
8123 {
8124 case IMAGE_BITMAP:
8125 DeleteObject((HGDIOBJ)sign->hImage);
8126 break;
8127 case IMAGE_CURSOR:
8128 DestroyCursor((HCURSOR)sign->hImage);
8129 break;
8130 case IMAGE_ICON:
8131 DestroyIcon((HICON)sign->hImage);
8132 break;
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01008133# ifdef FEAT_XPM_W32
Bram Moolenaar071d4272004-06-13 20:20:40 +00008134 case IMAGE_XPM:
8135 DeleteObject((HBITMAP)sign->hImage);
8136 DeleteObject((HBITMAP)sign->hShape);
8137 break;
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01008138# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008139 }
8140}
8141
8142 void *
Bram Moolenaar68c2f632016-01-30 17:24:07 +01008143gui_mch_register_sign(char_u *signfile)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008144{
8145 signicon_t sign, *psign;
8146 char_u *ext;
8147
Bram Moolenaar071d4272004-06-13 20:20:40 +00008148 sign.hImage = NULL;
Bram Moolenaar734a8672019-12-02 22:49:38 +01008149 ext = signfile + STRLEN(signfile) - 4; // get extension
Bram Moolenaar071d4272004-06-13 20:20:40 +00008150 if (ext > signfile)
8151 {
8152 int do_load = 1;
8153
8154 if (!STRICMP(ext, ".bmp"))
8155 sign.uType = IMAGE_BITMAP;
8156 else if (!STRICMP(ext, ".ico"))
8157 sign.uType = IMAGE_ICON;
8158 else if (!STRICMP(ext, ".cur") || !STRICMP(ext, ".ani"))
8159 sign.uType = IMAGE_CURSOR;
8160 else
8161 do_load = 0;
8162
8163 if (do_load)
Bram Moolenaar418f81b2016-02-16 20:12:02 +01008164 sign.hImage = (HANDLE)LoadImage(NULL, (LPCSTR)signfile, sign.uType,
Bram Moolenaar071d4272004-06-13 20:20:40 +00008165 gui.char_width * 2, gui.char_height,
8166 LR_LOADFROMFILE | LR_CREATEDIBSECTION);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01008167# ifdef FEAT_XPM_W32
Bram Moolenaar071d4272004-06-13 20:20:40 +00008168 if (!STRICMP(ext, ".xpm"))
8169 {
8170 sign.uType = IMAGE_XPM;
Bram Moolenaar418f81b2016-02-16 20:12:02 +01008171 LoadXpmImage((char *)signfile, (HBITMAP *)&sign.hImage,
8172 (HBITMAP *)&sign.hShape);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008173 }
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01008174# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008175 }
8176
8177 psign = NULL;
Bram Moolenaarc799fe22019-05-28 23:08:19 +02008178 if (sign.hImage && (psign = ALLOC_ONE(signicon_t)) != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008179 *psign = sign;
8180
8181 if (!psign)
8182 {
8183 if (sign.hImage)
8184 close_signicon_image(&sign);
Bram Moolenaar74409f62022-01-01 15:58:22 +00008185 emsg(_(e_couldnt_read_in_sign_data));
Bram Moolenaar071d4272004-06-13 20:20:40 +00008186 }
8187 return (void *)psign;
8188
8189}
8190
8191 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01008192gui_mch_destroy_sign(void *sign)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008193{
8194 if (sign)
8195 {
8196 close_signicon_image((signicon_t *)sign);
8197 vim_free(sign);
8198 }
8199}
Bram Moolenaar5c06f8b2005-05-31 22:14:58 +00008200#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008201
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01008202#if defined(FEAT_BEVAL_GUI) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008203
Bram Moolenaar734a8672019-12-02 22:49:38 +01008204/*
8205 * BALLOON-EVAL IMPLEMENTATION FOR WINDOWS.
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00008206 * Added by Sergey Khorev <sergey.khorev@gmail.com>
Bram Moolenaar071d4272004-06-13 20:20:40 +00008207 *
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01008208 * The only reused thing is beval.h and get_beval_info()
Bram Moolenaar071d4272004-06-13 20:20:40 +00008209 * from gui_beval.c (note it uses x and y of the BalloonEval struct
8210 * to get current mouse position).
8211 *
8212 * Trying to use as more Windows services as possible, and as less
8213 * IE version as possible :)).
8214 *
8215 * 1) Don't create ToolTip in gui_mch_create_beval_area, only initialize
8216 * BalloonEval struct.
8217 * 2) Enable/Disable simply create/kill BalloonEval Timer
8218 * 3) When there was enough inactivity, timer procedure posts
8219 * async request to debugger
8220 * 4) gui_mch_post_balloon (invoked from netbeans.c) creates tooltip control
8221 * and performs some actions to show it ASAP
Bram Moolenaar446cb832008-06-24 21:56:24 +00008222 * 5) WM_NOTIFY:TTN_POP destroys created tooltip
Bram Moolenaar071d4272004-06-13 20:20:40 +00008223 */
8224
Bram Moolenaard385b5d2018-12-27 22:43:08 +01008225 static void
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02008226make_tooltip(BalloonEval *beval, char *text, POINT pt)
Bram Moolenaard385b5d2018-12-27 22:43:08 +01008227{
K.Takata76687d22022-01-25 10:31:37 +00008228 TOOLINFOW *pti;
8229 RECT rect;
Bram Moolenaard385b5d2018-12-27 22:43:08 +01008230
K.Takata76687d22022-01-25 10:31:37 +00008231 pti = alloc(sizeof(TOOLINFOW));
Bram Moolenaard385b5d2018-12-27 22:43:08 +01008232 if (pti == NULL)
8233 return;
8234
8235 beval->balloon = CreateWindowExW(WS_EX_TOPMOST, TOOLTIPS_CLASSW,
8236 NULL, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,
8237 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
Bram Moolenaarafde13b2019-04-28 19:46:49 +02008238 beval->target, NULL, g_hinst, NULL);
Bram Moolenaard385b5d2018-12-27 22:43:08 +01008239
8240 SetWindowPos(beval->balloon, HWND_TOPMOST, 0, 0, 0, 0,
8241 SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
8242
K.Takata76687d22022-01-25 10:31:37 +00008243 pti->cbSize = sizeof(TOOLINFOW);
Bram Moolenaard385b5d2018-12-27 22:43:08 +01008244 pti->uFlags = TTF_SUBCLASS;
8245 pti->hwnd = beval->target;
8246 pti->hinst = 0; // Don't use string resources
8247 pti->uId = ID_BEVAL_TOOLTIP;
8248
Bram Moolenaar0bd663a2022-01-22 10:24:47 +00008249 pti->lpszText = LPSTR_TEXTCALLBACKW;
8250 beval->tofree = enc_to_utf16((char_u*)text, NULL);
8251 pti->lParam = (LPARAM)beval->tofree;
8252 // switch multiline tooltips on
8253 if (GetClientRect(s_textArea, &rect))
8254 SendMessageW(beval->balloon, TTM_SETMAXTIPWIDTH, 0,
8255 (LPARAM)rect.right);
Bram Moolenaard385b5d2018-12-27 22:43:08 +01008256
8257 // Limit ballooneval bounding rect to CursorPos neighbourhood.
8258 pti->rect.left = pt.x - 3;
8259 pti->rect.top = pt.y - 3;
8260 pti->rect.right = pt.x + 3;
8261 pti->rect.bottom = pt.y + 3;
8262
8263 SendMessageW(beval->balloon, TTM_ADDTOOLW, 0, (LPARAM)pti);
8264 // Make tooltip appear sooner.
8265 SendMessageW(beval->balloon, TTM_SETDELAYTIME, TTDT_INITIAL, 10);
8266 // I've performed some tests and it seems the longest possible life time
8267 // of tooltip is 30 seconds.
8268 SendMessageW(beval->balloon, TTM_SETDELAYTIME, TTDT_AUTOPOP, 30000);
8269 /*
8270 * HACK: force tooltip to appear, because it'll not appear until
8271 * first mouse move. D*mn M$
8272 * Amazingly moving (2, 2) and then (-1, -1) the mouse doesn't move.
8273 */
8274 mouse_event(MOUSEEVENTF_MOVE, 2, 2, 0, 0);
8275 mouse_event(MOUSEEVENTF_MOVE, (DWORD)-1, (DWORD)-1, 0, 0);
8276 vim_free(pti);
Bram Moolenaard385b5d2018-12-27 22:43:08 +01008277}
Bram Moolenaard385b5d2018-12-27 22:43:08 +01008278
Bram Moolenaar071d4272004-06-13 20:20:40 +00008279 static void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01008280delete_tooltip(BalloonEval *beval)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008281{
Bram Moolenaar8e5f5b42015-08-26 23:12:38 +02008282 PostMessage(beval->balloon, WM_CLOSE, 0, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008283}
8284
8285 static VOID CALLBACK
K.Takataa8ec4912022-02-03 14:32:33 +00008286beval_timer_proc(
Bram Moolenaar1266d672017-02-01 13:43:36 +01008287 HWND hwnd UNUSED,
8288 UINT uMsg UNUSED,
K.Takataa8ec4912022-02-03 14:32:33 +00008289 UINT_PTR idEvent UNUSED,
Bram Moolenaar1266d672017-02-01 13:43:36 +01008290 DWORD dwTime)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008291{
8292 POINT pt;
8293 RECT rect;
8294
8295 if (cur_beval == NULL || cur_beval->showState == ShS_SHOWING || !p_beval)
8296 return;
8297
8298 GetCursorPos(&pt);
8299 if (WindowFromPoint(pt) != s_textArea)
8300 return;
8301
8302 ScreenToClient(s_textArea, &pt);
8303 GetClientRect(s_textArea, &rect);
8304 if (!PtInRect(&rect, pt))
8305 return;
8306
K.Takataa8ec4912022-02-03 14:32:33 +00008307 if (last_user_activity > 0
8308 && (dwTime - last_user_activity) >= (DWORD)p_bdlay
Bram Moolenaar071d4272004-06-13 20:20:40 +00008309 && (cur_beval->showState != ShS_PENDING
8310 || abs(cur_beval->x - pt.x) > 3
8311 || abs(cur_beval->y - pt.y) > 3))
8312 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01008313 // Pointer resting in one place long enough, it's time to show
8314 // the tooltip.
Bram Moolenaar071d4272004-06-13 20:20:40 +00008315 cur_beval->showState = ShS_PENDING;
8316 cur_beval->x = pt.x;
8317 cur_beval->y = pt.y;
8318
Bram Moolenaar071d4272004-06-13 20:20:40 +00008319 if (cur_beval->msgCB != NULL)
8320 (*cur_beval->msgCB)(cur_beval, 0);
8321 }
8322}
8323
8324 void
Bram Moolenaar1266d672017-02-01 13:43:36 +01008325gui_mch_disable_beval_area(BalloonEval *beval UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008326{
K.Takataa8ec4912022-02-03 14:32:33 +00008327 KillTimer(s_textArea, beval_timer_id);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008328}
8329
8330 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01008331gui_mch_enable_beval_area(BalloonEval *beval)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008332{
Bram Moolenaar071d4272004-06-13 20:20:40 +00008333 if (beval == NULL)
8334 return;
K.Takataa8ec4912022-02-03 14:32:33 +00008335 beval_timer_id = SetTimer(s_textArea, 0, (UINT)(p_bdlay / 2),
8336 beval_timer_proc);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008337}
8338
8339 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01008340gui_mch_post_balloon(BalloonEval *beval, char_u *mesg)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008341{
8342 POINT pt;
Bram Moolenaar1c465442017-03-12 20:10:05 +01008343
Bram Moolenaarbe0a2592019-05-09 13:50:16 +02008344 vim_free(beval->msg);
8345 beval->msg = mesg == NULL ? NULL : vim_strsave(mesg);
8346 if (beval->msg == NULL)
8347 {
8348 delete_tooltip(beval);
8349 beval->showState = ShS_NEUTRAL;
8350 return;
8351 }
8352
Bram Moolenaar071d4272004-06-13 20:20:40 +00008353 if (beval->showState == ShS_SHOWING)
8354 return;
8355 GetCursorPos(&pt);
8356 ScreenToClient(s_textArea, &pt);
8357
8358 if (abs(beval->x - pt.x) < 3 && abs(beval->y - pt.y) < 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008359 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01008360 // cursor is still here
Bram Moolenaar071d4272004-06-13 20:20:40 +00008361 gui_mch_disable_beval_area(cur_beval);
8362 beval->showState = ShS_SHOWING;
Bram Moolenaar418f81b2016-02-16 20:12:02 +01008363 make_tooltip(beval, (char *)mesg, pt);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008364 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008365}
8366
8367 BalloonEval *
Bram Moolenaar68c2f632016-01-30 17:24:07 +01008368gui_mch_create_beval_area(
Bram Moolenaar734a8672019-12-02 22:49:38 +01008369 void *target UNUSED, // ignored, always use s_textArea
Bram Moolenaar68c2f632016-01-30 17:24:07 +01008370 char_u *mesg,
8371 void (*mesgCB)(BalloonEval *, int),
8372 void *clientData)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008373{
Bram Moolenaar734a8672019-12-02 22:49:38 +01008374 // partially stolen from gui_beval.c
Bram Moolenaar071d4272004-06-13 20:20:40 +00008375 BalloonEval *beval;
8376
8377 if (mesg != NULL && mesgCB != NULL)
8378 {
Bram Moolenaarcbadefe2022-01-01 19:33:50 +00008379 iemsg(_(e_cannot_create_ballooneval_with_both_message_and_callback));
Bram Moolenaar071d4272004-06-13 20:20:40 +00008380 return NULL;
8381 }
8382
Bram Moolenaarc799fe22019-05-28 23:08:19 +02008383 beval = ALLOC_CLEAR_ONE(BalloonEval);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008384 if (beval != NULL)
8385 {
8386 beval->target = s_textArea;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008387
8388 beval->showState = ShS_NEUTRAL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008389 beval->msg = mesg;
8390 beval->msgCB = mesgCB;
8391 beval->clientData = clientData;
8392
8393 InitCommonControls();
Bram Moolenaar071d4272004-06-13 20:20:40 +00008394 cur_beval = beval;
8395
8396 if (p_beval)
8397 gui_mch_enable_beval_area(beval);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008398 }
8399 return beval;
8400}
8401
8402 static void
Bram Moolenaar1266d672017-02-01 13:43:36 +01008403Handle_WM_Notify(HWND hwnd UNUSED, LPNMHDR pnmh)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008404{
Bram Moolenaar734a8672019-12-02 22:49:38 +01008405 if (pnmh->idFrom != ID_BEVAL_TOOLTIP) // it is not our tooltip
Bram Moolenaar071d4272004-06-13 20:20:40 +00008406 return;
8407
8408 if (cur_beval != NULL)
8409 {
Bram Moolenaar45360022005-07-21 21:08:21 +00008410 switch (pnmh->code)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008411 {
Bram Moolenaar45360022005-07-21 21:08:21 +00008412 case TTN_SHOW:
Bram Moolenaar45360022005-07-21 21:08:21 +00008413 break;
Bram Moolenaar734a8672019-12-02 22:49:38 +01008414 case TTN_POP: // Before tooltip disappear
Bram Moolenaar071d4272004-06-13 20:20:40 +00008415 delete_tooltip(cur_beval);
8416 gui_mch_enable_beval_area(cur_beval);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008417
8418 cur_beval->showState = ShS_NEUTRAL;
Bram Moolenaar45360022005-07-21 21:08:21 +00008419 break;
8420 case TTN_GETDISPINFO:
Bram Moolenaar6c9176d2008-01-03 19:45:15 +00008421 {
Bram Moolenaar734a8672019-12-02 22:49:38 +01008422 // if you get there then we have new common controls
K.Takata76687d22022-01-25 10:31:37 +00008423 NMTTDISPINFO *info = (NMTTDISPINFO *)pnmh;
Bram Moolenaar6c9176d2008-01-03 19:45:15 +00008424 info->lpszText = (LPSTR)info->lParam;
8425 info->uFlags |= TTF_DI_SETITEM;
8426 }
Bram Moolenaar45360022005-07-21 21:08:21 +00008427 break;
Bram Moolenaard385b5d2018-12-27 22:43:08 +01008428 case TTN_GETDISPINFOW:
8429 {
8430 // if we get here then we have new common controls
K.Takata76687d22022-01-25 10:31:37 +00008431 NMTTDISPINFOW *info = (NMTTDISPINFOW *)pnmh;
Bram Moolenaard385b5d2018-12-27 22:43:08 +01008432 info->lpszText = (LPWSTR)info->lParam;
8433 info->uFlags |= TTF_DI_SETITEM;
8434 }
8435 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008436 }
8437 }
8438}
8439
8440 static void
K.Takataa8ec4912022-02-03 14:32:33 +00008441track_user_activity(UINT uMsg)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008442{
8443 if ((uMsg >= WM_MOUSEFIRST && uMsg <= WM_MOUSELAST)
8444 || (uMsg >= WM_KEYFIRST && uMsg <= WM_KEYLAST))
K.Takataa8ec4912022-02-03 14:32:33 +00008445 last_user_activity = GetTickCount();
Bram Moolenaar071d4272004-06-13 20:20:40 +00008446}
8447
8448 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01008449gui_mch_destroy_beval_area(BalloonEval *beval)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008450{
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01008451# ifdef FEAT_VARTABS
Bram Moolenaar6d9e71a2018-12-28 19:13:34 +01008452 vim_free(beval->vts);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01008453# endif
Bram Moolenaar6d9e71a2018-12-28 19:13:34 +01008454 vim_free(beval->tofree);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008455 vim_free(beval);
8456}
Bram Moolenaar734a8672019-12-02 22:49:38 +01008457#endif // FEAT_BEVAL_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00008458
8459#if defined(FEAT_NETBEANS_INTG) || defined(PROTO)
8460/*
8461 * We have multiple signs to draw at the same location. Draw the
8462 * multi-sign indicator (down-arrow) instead. This is the Win32 version.
8463 */
8464 void
8465netbeans_draw_multisign_indicator(int row)
8466{
8467 int i;
8468 int y;
8469 int x;
8470
Bram Moolenaarb26e6322010-05-22 21:34:09 +02008471 if (!netbeans_active())
Bram Moolenaarcc448b32010-07-14 16:52:17 +02008472 return;
Bram Moolenaarb26e6322010-05-22 21:34:09 +02008473
Bram Moolenaar071d4272004-06-13 20:20:40 +00008474 x = 0;
8475 y = TEXT_Y(row);
8476
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01008477# if defined(FEAT_DIRECTX)
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01008478 if (IS_ENABLE_DIRECTX())
8479 DWriteContext_Flush(s_dwc);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01008480# endif
Bram Moolenaard7ccc4d2017-11-26 14:29:32 +01008481
Bram Moolenaar071d4272004-06-13 20:20:40 +00008482 for (i = 0; i < gui.char_height - 3; i++)
8483 SetPixel(s_hdc, x+2, y++, gui.currFgColor);
8484
8485 SetPixel(s_hdc, x+0, y, gui.currFgColor);
8486 SetPixel(s_hdc, x+2, y, gui.currFgColor);
8487 SetPixel(s_hdc, x+4, y++, gui.currFgColor);
8488 SetPixel(s_hdc, x+1, y, gui.currFgColor);
8489 SetPixel(s_hdc, x+2, y, gui.currFgColor);
8490 SetPixel(s_hdc, x+3, y++, gui.currFgColor);
8491 SetPixel(s_hdc, x+2, y, gui.currFgColor);
8492}
Bram Moolenaare0874f82016-01-24 20:36:41 +01008493#endif