blob: 598034d36f636d53a2c6df4b8b1f1861ae09842f [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;
36# define IS_ENABLE_DIRECTX() (s_directx_enabled && s_dwc != NULL)
Bram Moolenaarb8e0bdb2014-11-12 16:10:48 +010037#endif
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +020038
Bram Moolenaar065bbac2016-02-20 13:08:46 +010039#ifdef FEAT_MENU
40static int gui_mswin_get_menu_height(int fix_window);
41#endif
42
Bram Moolenaarb8e0bdb2014-11-12 16:10:48 +010043#if defined(FEAT_DIRECTX) || defined(PROTO)
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +020044 int
45directx_enabled(void)
46{
47 if (s_dwc != NULL)
48 return 1;
49 else if (s_directx_load_attempted)
50 return 0;
51 /* load DirectX */
52 DWrite_Init();
53 s_directx_load_attempted = 1;
54 s_dwc = DWriteContext_Open();
55 return s_dwc != NULL ? 1 : 0;
56}
57#endif
58
59#if defined(FEAT_RENDER_OPTIONS) || defined(PROTO)
60 int
61gui_mch_set_rendering_options(char_u *s)
62{
63#ifdef FEAT_DIRECTX
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +020064 char_u *p, *q;
65
66 int dx_enable = 0;
67 int dx_flags = 0;
68 float dx_gamma = 0.0f;
69 float dx_contrast = 0.0f;
70 float dx_level = 0.0f;
71 int dx_geom = 0;
72 int dx_renmode = 0;
73 int dx_taamode = 0;
74
75 /* parse string as rendering options. */
76 for (p = s; p != NULL && *p != NUL; )
77 {
78 char_u item[256];
79 char_u name[128];
80 char_u value[128];
81
Bram Moolenaarcde88542015-08-11 19:14:00 +020082 copy_option_part(&p, item, sizeof(item), ",");
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +020083 if (p == NULL)
84 break;
85 q = &item[0];
86 copy_option_part(&q, name, sizeof(name), ":");
87 if (q == NULL)
88 return FAIL;
89 copy_option_part(&q, value, sizeof(value), ":");
90
91 if (STRCMP(name, "type") == 0)
92 {
93 if (STRCMP(value, "directx") == 0)
94 dx_enable = 1;
95 else
96 return FAIL;
97 }
98 else if (STRCMP(name, "gamma") == 0)
99 {
100 dx_flags |= 1 << 0;
Bram Moolenaar7f0608f2016-02-18 20:46:39 +0100101 dx_gamma = (float)atof((char *)value);
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200102 }
103 else if (STRCMP(name, "contrast") == 0)
104 {
105 dx_flags |= 1 << 1;
Bram Moolenaar7f0608f2016-02-18 20:46:39 +0100106 dx_contrast = (float)atof((char *)value);
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200107 }
108 else if (STRCMP(name, "level") == 0)
109 {
110 dx_flags |= 1 << 2;
Bram Moolenaar7f0608f2016-02-18 20:46:39 +0100111 dx_level = (float)atof((char *)value);
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200112 }
113 else if (STRCMP(name, "geom") == 0)
114 {
115 dx_flags |= 1 << 3;
Bram Moolenaar7f0608f2016-02-18 20:46:39 +0100116 dx_geom = atoi((char *)value);
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200117 if (dx_geom < 0 || dx_geom > 2)
118 return FAIL;
119 }
120 else if (STRCMP(name, "renmode") == 0)
121 {
122 dx_flags |= 1 << 4;
Bram Moolenaar7f0608f2016-02-18 20:46:39 +0100123 dx_renmode = atoi((char *)value);
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200124 if (dx_renmode < 0 || dx_renmode > 6)
125 return FAIL;
126 }
127 else if (STRCMP(name, "taamode") == 0)
128 {
129 dx_flags |= 1 << 5;
Bram Moolenaar7f0608f2016-02-18 20:46:39 +0100130 dx_taamode = atoi((char *)value);
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +0200131 if (dx_taamode < 0 || dx_taamode > 3)
132 return FAIL;
133 }
134 else
135 return FAIL;
136 }
137
138 /* Enable DirectX/DirectWrite */
139 if (dx_enable)
140 {
141 if (!directx_enabled())
142 return FAIL;
143 DWriteContext_SetRenderingParams(s_dwc, NULL);
144 if (dx_flags)
145 {
146 DWriteRenderingParams param;
147 DWriteContext_GetRenderingParams(s_dwc, &param);
148 if (dx_flags & (1 << 0))
149 param.gamma = dx_gamma;
150 if (dx_flags & (1 << 1))
151 param.enhancedContrast = dx_contrast;
152 if (dx_flags & (1 << 2))
153 param.clearTypeLevel = dx_level;
154 if (dx_flags & (1 << 3))
155 param.pixelGeometry = dx_geom;
156 if (dx_flags & (1 << 4))
157 param.renderingMode = dx_renmode;
158 if (dx_flags & (1 << 5))
159 param.textAntialiasMode = dx_taamode;
160 DWriteContext_SetRenderingParams(s_dwc, &param);
161 }
162 }
163 s_directx_enabled = dx_enable;
164
165 return OK;
166#else
167 return FAIL;
168#endif
169}
170#endif
171
Bram Moolenaar071d4272004-06-13 20:20:40 +0000172/*
173 * These are new in Windows ME/XP, only defined in recent compilers.
174 */
175#ifndef HANDLE_WM_XBUTTONUP
176# define HANDLE_WM_XBUTTONUP(hwnd, wParam, lParam, fn) \
177 ((fn)((hwnd), (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
178#endif
179#ifndef HANDLE_WM_XBUTTONDOWN
180# define HANDLE_WM_XBUTTONDOWN(hwnd, wParam, lParam, fn) \
181 ((fn)((hwnd), FALSE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
182#endif
183#ifndef HANDLE_WM_XBUTTONDBLCLK
184# define HANDLE_WM_XBUTTONDBLCLK(hwnd, wParam, lParam, fn) \
185 ((fn)((hwnd), TRUE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
186#endif
187
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100188
189#include "version.h" /* used by dialog box routine for default title */
190#ifdef DEBUG
191# include <tchar.h>
192#endif
193
194/* cproto fails on missing include files */
195#ifndef PROTO
196
197#ifndef __MINGW32__
198# include <shellapi.h>
199#endif
Bram Moolenaarc3719bd2017-11-18 22:13:31 +0100200#if defined(FEAT_TOOLBAR) || defined(FEAT_BEVAL_GUI) || defined(FEAT_GUI_TABLINE)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100201# include <commctrl.h>
202#endif
203#include <windowsx.h>
204
205#ifdef GLOBAL_IME
206# include "glbl_ime.h"
207#endif
208
209#endif /* PROTO */
210
211#ifdef FEAT_MENU
212# define MENUHINTS /* show menu hints in command line */
213#endif
214
215/* Some parameters for dialog boxes. All in pixels. */
216#define DLG_PADDING_X 10
217#define DLG_PADDING_Y 10
218#define DLG_OLD_STYLE_PADDING_X 5
219#define DLG_OLD_STYLE_PADDING_Y 5
220#define DLG_VERT_PADDING_X 4 /* For vertical buttons */
221#define DLG_VERT_PADDING_Y 4
222#define DLG_ICON_WIDTH 34
223#define DLG_ICON_HEIGHT 34
224#define DLG_MIN_WIDTH 150
225#define DLG_FONT_NAME "MS Sans Serif"
226#define DLG_FONT_POINT_SIZE 8
227#define DLG_MIN_MAX_WIDTH 400
228#define DLG_MIN_MAX_HEIGHT 400
229
230#define DLG_NONBUTTON_CONTROL 5000 /* First ID of non-button controls */
231
232#ifndef WM_XBUTTONDOWN /* For Win2K / winME ONLY */
233# define WM_XBUTTONDOWN 0x020B
234# define WM_XBUTTONUP 0x020C
235# define WM_XBUTTONDBLCLK 0x020D
236# define MK_XBUTTON1 0x0020
237# define MK_XBUTTON2 0x0040
238#endif
239
240#ifdef PROTO
Bram Moolenaar071d4272004-06-13 20:20:40 +0000241/*
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100242 * Define a few things for generating prototypes. This is just to avoid
243 * syntax errors, the defines do not need to be correct.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000244 */
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100245# define APIENTRY
246# define CALLBACK
247# define CONST
248# define FAR
249# define NEAR
Bram Moolenaara6b7a082016-08-10 20:53:05 +0200250# undef _cdecl
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100251# define _cdecl
252typedef int BOOL;
253typedef int BYTE;
254typedef int DWORD;
255typedef int WCHAR;
256typedef int ENUMLOGFONT;
257typedef int FINDREPLACE;
258typedef int HANDLE;
259typedef int HBITMAP;
260typedef int HBRUSH;
261typedef int HDROP;
262typedef int INT;
263typedef int LOGFONT[];
264typedef int LPARAM;
265typedef int LPCREATESTRUCT;
266typedef int LPCSTR;
267typedef int LPCTSTR;
268typedef int LPRECT;
269typedef int LPSTR;
270typedef int LPWINDOWPOS;
271typedef int LPWORD;
272typedef int LRESULT;
273typedef int HRESULT;
274# undef MSG
275typedef int MSG;
276typedef int NEWTEXTMETRIC;
277typedef int OSVERSIONINFO;
278typedef int PWORD;
279typedef int RECT;
280typedef int UINT;
281typedef int WORD;
282typedef int WPARAM;
283typedef int POINT;
284typedef void *HINSTANCE;
285typedef void *HMENU;
286typedef void *HWND;
287typedef void *HDC;
288typedef void VOID;
289typedef int LPNMHDR;
290typedef int LONG;
291typedef int WNDPROC;
Bram Moolenaara6b7a082016-08-10 20:53:05 +0200292typedef int UINT_PTR;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100293#endif
294
295#ifndef GET_X_LPARAM
296# define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp))
297#endif
298
299static void _OnPaint( HWND hwnd);
300static void clear_rect(RECT *rcp);
301
302static WORD s_dlgfntheight; /* height of the dialog font */
303static WORD s_dlgfntwidth; /* width of the dialog font */
304
305#ifdef FEAT_MENU
306static HMENU s_menuBar = NULL;
307#endif
308#ifdef FEAT_TEAROFF
309static void rebuild_tearoff(vimmenu_T *menu);
310static HBITMAP s_htearbitmap; /* bitmap used to indicate tearoff */
311#endif
312
313/* Flag that is set while processing a message that must not be interrupted by
314 * processing another message. */
315static int s_busy_processing = FALSE;
316
317static int destroying = FALSE; /* call DestroyWindow() ourselves */
318
319#ifdef MSWIN_FIND_REPLACE
320static UINT s_findrep_msg = 0; /* set in gui_w[16/32].c */
321static FINDREPLACE s_findrep_struct;
Bram Moolenaarcea912a2016-10-12 14:20:24 +0200322# ifdef FEAT_MBYTE
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100323static FINDREPLACEW s_findrep_struct_w;
324# endif
325static HWND s_findrep_hwnd = NULL;
326static int s_findrep_is_find; /* TRUE for find dialog, FALSE
327 for find/replace dialog */
328#endif
329
330static HINSTANCE s_hinst = NULL;
Bram Moolenaar85b11762016-02-27 18:13:23 +0100331#if !defined(FEAT_GUI)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100332static
333#endif
334HWND s_hwnd = NULL;
335static HDC s_hdc = NULL;
336static HBRUSH s_brush = NULL;
337
338#ifdef FEAT_TOOLBAR
339static HWND s_toolbarhwnd = NULL;
340static WNDPROC s_toolbar_wndproc = NULL;
341#endif
342
343#ifdef FEAT_GUI_TABLINE
344static HWND s_tabhwnd = NULL;
345static WNDPROC s_tabline_wndproc = NULL;
346static int showing_tabline = 0;
347#endif
348
349static WPARAM s_wParam = 0;
350static LPARAM s_lParam = 0;
351
352static HWND s_textArea = NULL;
353static UINT s_uMsg = 0;
354
355static char_u *s_textfield; /* Used by dialogs to pass back strings */
356
357static int s_need_activate = FALSE;
358
359/* This variable is set when waiting for an event, which is the only moment
360 * scrollbar dragging can be done directly. It's not allowed while commands
361 * are executed, because it may move the cursor and that may cause unexpected
362 * problems (e.g., while ":s" is working).
363 */
364static int allow_scrollbar = FALSE;
365
366#ifdef GLOBAL_IME
367# define MyTranslateMessage(x) global_ime_TranslateMessage(x)
368#else
369# define MyTranslateMessage(x) TranslateMessage(x)
370#endif
371
Bram Moolenaarcea912a2016-10-12 14:20:24 +0200372#if defined(FEAT_MBYTE) || defined(GLOBAL_IME)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100373 /* use of WindowProc depends on wide_WindowProc */
374# define MyWindowProc vim_WindowProc
375#else
376 /* use ordinary WindowProc */
377# define MyWindowProc DefWindowProc
378#endif
379
380extern int current_font_height; /* this is in os_mswin.c */
381
382static struct
383{
384 UINT key_sym;
385 char_u vim_code0;
386 char_u vim_code1;
387} special_keys[] =
388{
389 {VK_UP, 'k', 'u'},
390 {VK_DOWN, 'k', 'd'},
391 {VK_LEFT, 'k', 'l'},
392 {VK_RIGHT, 'k', 'r'},
393
394 {VK_F1, 'k', '1'},
395 {VK_F2, 'k', '2'},
396 {VK_F3, 'k', '3'},
397 {VK_F4, 'k', '4'},
398 {VK_F5, 'k', '5'},
399 {VK_F6, 'k', '6'},
400 {VK_F7, 'k', '7'},
401 {VK_F8, 'k', '8'},
402 {VK_F9, 'k', '9'},
403 {VK_F10, 'k', ';'},
404
405 {VK_F11, 'F', '1'},
406 {VK_F12, 'F', '2'},
407 {VK_F13, 'F', '3'},
408 {VK_F14, 'F', '4'},
409 {VK_F15, 'F', '5'},
410 {VK_F16, 'F', '6'},
411 {VK_F17, 'F', '7'},
412 {VK_F18, 'F', '8'},
413 {VK_F19, 'F', '9'},
414 {VK_F20, 'F', 'A'},
415
416 {VK_F21, 'F', 'B'},
417#ifdef FEAT_NETBEANS_INTG
418 {VK_PAUSE, 'F', 'B'}, /* Pause == F21 (see gui_gtk_x11.c) */
419#endif
420 {VK_F22, 'F', 'C'},
421 {VK_F23, 'F', 'D'},
422 {VK_F24, 'F', 'E'}, /* winuser.h defines up to F24 */
423
424 {VK_HELP, '%', '1'},
425 {VK_BACK, 'k', 'b'},
426 {VK_INSERT, 'k', 'I'},
427 {VK_DELETE, 'k', 'D'},
428 {VK_HOME, 'k', 'h'},
429 {VK_END, '@', '7'},
430 {VK_PRIOR, 'k', 'P'},
431 {VK_NEXT, 'k', 'N'},
432 {VK_PRINT, '%', '9'},
433 {VK_ADD, 'K', '6'},
434 {VK_SUBTRACT, 'K', '7'},
435 {VK_DIVIDE, 'K', '8'},
436 {VK_MULTIPLY, 'K', '9'},
437 {VK_SEPARATOR, 'K', 'A'}, /* Keypad Enter */
438 {VK_DECIMAL, 'K', 'B'},
439
440 {VK_NUMPAD0, 'K', 'C'},
441 {VK_NUMPAD1, 'K', 'D'},
442 {VK_NUMPAD2, 'K', 'E'},
443 {VK_NUMPAD3, 'K', 'F'},
444 {VK_NUMPAD4, 'K', 'G'},
445 {VK_NUMPAD5, 'K', 'H'},
446 {VK_NUMPAD6, 'K', 'I'},
447 {VK_NUMPAD7, 'K', 'J'},
448 {VK_NUMPAD8, 'K', 'K'},
449 {VK_NUMPAD9, 'K', 'L'},
450
451 /* Keys that we want to be able to use any modifier with: */
452 {VK_SPACE, ' ', NUL},
453 {VK_TAB, TAB, NUL},
454 {VK_ESCAPE, ESC, NUL},
455 {NL, NL, NUL},
456 {CAR, CAR, NUL},
457
458 /* End of list marker: */
459 {0, 0, 0}
460};
461
462/* Local variables */
463static int s_button_pending = -1;
464
465/* s_getting_focus is set when we got focus but didn't see mouse-up event yet,
466 * so don't reset s_button_pending. */
467static int s_getting_focus = FALSE;
468
469static int s_x_pending;
470static int s_y_pending;
471static UINT s_kFlags_pending;
472static UINT s_wait_timer = 0; /* Timer for get char from user */
473static int s_timed_out = FALSE;
474static int dead_key = 0; /* 0: no dead key, 1: dead key pressed */
475
Bram Moolenaarc3719bd2017-11-18 22:13:31 +0100476#ifdef FEAT_BEVAL_GUI
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100477/* balloon-eval WM_NOTIFY_HANDLER */
478static void Handle_WM_Notify(HWND hwnd, LPNMHDR pnmh);
479static void TrackUserActivity(UINT uMsg);
480#endif
481
482/*
483 * For control IME.
484 *
485 * These LOGFONT used for IME.
486 */
487#ifdef FEAT_MBYTE
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100488/* holds LOGFONT for 'guifontwide' if available, otherwise 'guifont' */
489static LOGFONT norm_logfont;
490/* holds LOGFONT for 'guifont' always. */
491static LOGFONT sub_logfont;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100492#endif
493
494#ifdef FEAT_MBYTE_IME
495static LRESULT _OnImeNotify(HWND hWnd, DWORD dwCommand, DWORD dwData);
496#endif
497
498#if defined(FEAT_BROWSE)
499static char_u *convert_filter(char_u *s);
500#endif
501
502#ifdef DEBUG_PRINT_ERROR
503/*
504 * Print out the last Windows error message
505 */
506 static void
507print_windows_error(void)
508{
509 LPVOID lpMsgBuf;
510
511 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
512 NULL, GetLastError(),
513 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
514 (LPTSTR) &lpMsgBuf, 0, NULL);
515 TRACE1("Error: %s\n", lpMsgBuf);
516 LocalFree(lpMsgBuf);
517}
518#endif
519
520/*
521 * Cursor blink functions.
522 *
523 * This is a simple state machine:
524 * BLINK_NONE not blinking at all
525 * BLINK_OFF blinking, cursor is not shown
526 * BLINK_ON blinking, cursor is shown
527 */
528
529#define BLINK_NONE 0
530#define BLINK_OFF 1
531#define BLINK_ON 2
532
533static int blink_state = BLINK_NONE;
534static long_u blink_waittime = 700;
535static long_u blink_ontime = 400;
536static long_u blink_offtime = 250;
537static UINT blink_timer = 0;
538
Bram Moolenaar703a8042016-06-04 16:24:32 +0200539 int
540gui_mch_is_blinking(void)
541{
542 return blink_state != BLINK_NONE;
543}
544
Bram Moolenaar9d5d3c92016-07-07 16:43:02 +0200545 int
546gui_mch_is_blink_off(void)
547{
548 return blink_state == BLINK_OFF;
549}
550
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100551 void
552gui_mch_set_blinking(long wait, long on, long off)
553{
554 blink_waittime = wait;
555 blink_ontime = on;
556 blink_offtime = off;
557}
558
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100559 static VOID CALLBACK
560_OnBlinkTimer(
561 HWND hwnd,
Bram Moolenaar1266d672017-02-01 13:43:36 +0100562 UINT uMsg UNUSED,
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100563 UINT idEvent,
Bram Moolenaar1266d672017-02-01 13:43:36 +0100564 DWORD dwTime UNUSED)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100565{
566 MSG msg;
567
568 /*
569 TRACE2("Got timer event, id %d, blink_timer %d\n", idEvent, blink_timer);
570 */
571
572 KillTimer(NULL, idEvent);
573
574 /* Eat spurious WM_TIMER messages */
575 while (pPeekMessage(&msg, hwnd, WM_TIMER, WM_TIMER, PM_REMOVE))
576 ;
577
578 if (blink_state == BLINK_ON)
579 {
580 gui_undraw_cursor();
581 blink_state = BLINK_OFF;
582 blink_timer = (UINT) SetTimer(NULL, 0, (UINT)blink_offtime,
583 (TIMERPROC)_OnBlinkTimer);
584 }
585 else
586 {
587 gui_update_cursor(TRUE, FALSE);
588 blink_state = BLINK_ON;
589 blink_timer = (UINT) SetTimer(NULL, 0, (UINT)blink_ontime,
Bram Moolenaar1266d672017-02-01 13:43:36 +0100590 (TIMERPROC)_OnBlinkTimer);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100591 }
592}
593
594 static void
595gui_mswin_rm_blink_timer(void)
596{
597 MSG msg;
598
599 if (blink_timer != 0)
600 {
601 KillTimer(NULL, blink_timer);
602 /* Eat spurious WM_TIMER messages */
603 while (pPeekMessage(&msg, s_hwnd, WM_TIMER, WM_TIMER, PM_REMOVE))
604 ;
605 blink_timer = 0;
606 }
607}
608
609/*
610 * Stop the cursor blinking. Show the cursor if it wasn't shown.
611 */
612 void
613gui_mch_stop_blink(void)
614{
615 gui_mswin_rm_blink_timer();
616 if (blink_state == BLINK_OFF)
617 gui_update_cursor(TRUE, FALSE);
618 blink_state = BLINK_NONE;
619}
620
621/*
622 * Start the cursor blinking. If it was already blinking, this restarts the
623 * waiting time and shows the cursor.
624 */
625 void
626gui_mch_start_blink(void)
627{
628 gui_mswin_rm_blink_timer();
629
630 /* Only switch blinking on if none of the times is zero */
631 if (blink_waittime && blink_ontime && blink_offtime && gui.in_focus)
632 {
633 blink_timer = (UINT)SetTimer(NULL, 0, (UINT)blink_waittime,
634 (TIMERPROC)_OnBlinkTimer);
635 blink_state = BLINK_ON;
636 gui_update_cursor(TRUE, FALSE);
637 }
638}
639
640/*
641 * Call-back routines.
642 */
643
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100644 static VOID CALLBACK
645_OnTimer(
646 HWND hwnd,
Bram Moolenaar1266d672017-02-01 13:43:36 +0100647 UINT uMsg UNUSED,
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100648 UINT idEvent,
Bram Moolenaar1266d672017-02-01 13:43:36 +0100649 DWORD dwTime UNUSED)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100650{
651 MSG msg;
652
653 /*
654 TRACE2("Got timer event, id %d, s_wait_timer %d\n", idEvent, s_wait_timer);
655 */
656 KillTimer(NULL, idEvent);
657 s_timed_out = TRUE;
658
659 /* Eat spurious WM_TIMER messages */
660 while (pPeekMessage(&msg, hwnd, WM_TIMER, WM_TIMER, PM_REMOVE))
661 ;
662 if (idEvent == s_wait_timer)
663 s_wait_timer = 0;
664}
665
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100666 static void
667_OnDeadChar(
Bram Moolenaar1266d672017-02-01 13:43:36 +0100668 HWND hwnd UNUSED,
669 UINT ch UNUSED,
670 int cRepeat UNUSED)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100671{
672 dead_key = 1;
673}
674
675/*
676 * Convert Unicode character "ch" to bytes in "string[slen]".
677 * When "had_alt" is TRUE the ALT key was included in "ch".
678 * Return the length.
679 */
680 static int
681char_to_string(int ch, char_u *string, int slen, int had_alt)
682{
683 int len;
684 int i;
685#ifdef FEAT_MBYTE
686 WCHAR wstring[2];
Bram Moolenaar945ec092016-06-08 21:17:43 +0200687 char_u *ws = NULL;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100688
Bram Moolenaarcea912a2016-10-12 14:20:24 +0200689 wstring[0] = ch;
690 len = 1;
691
692 /* "ch" is a UTF-16 character. Convert it to a string of bytes. When
693 * "enc_codepage" is non-zero use the standard Win32 function,
694 * otherwise use our own conversion function (e.g., for UTF-8). */
695 if (enc_codepage > 0)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100696 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +0200697 len = WideCharToMultiByte(enc_codepage, 0, wstring, len,
698 (LPSTR)string, slen, 0, NULL);
699 /* If we had included the ALT key into the character but now the
700 * upper bit is no longer set, that probably means the conversion
701 * failed. Convert the original character and set the upper bit
702 * afterwards. */
703 if (had_alt && len == 1 && ch >= 0x80 && string[0] < 0x80)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100704 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +0200705 wstring[0] = ch & 0x7f;
706 len = WideCharToMultiByte(enc_codepage, 0, wstring, len,
707 (LPSTR)string, slen, 0, NULL);
708 if (len == 1) /* safety check */
709 string[0] |= 0x80;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100710 }
711 }
712 else
713 {
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100714 len = 1;
Bram Moolenaarcea912a2016-10-12 14:20:24 +0200715 ws = utf16_to_enc(wstring, &len);
716 if (ws == NULL)
717 len = 0;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100718 else
719 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +0200720 if (len > slen) /* just in case */
721 len = slen;
722 mch_memmove(string, ws, len);
723 vim_free(ws);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100724 }
725 }
726
727 if (len == 0)
728#endif
729 {
730 string[0] = ch;
731 len = 1;
732 }
733
734 for (i = 0; i < len; ++i)
735 if (string[i] == CSI && len <= slen - 2)
736 {
737 /* Insert CSI as K_CSI. */
738 mch_memmove(string + i + 3, string + i + 1, len - i - 1);
739 string[++i] = KS_EXTRA;
740 string[++i] = (int)KE_CSI;
741 len += 2;
742 }
743
744 return len;
745}
746
747/*
748 * Key hit, add it to the input buffer.
749 */
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100750 static void
751_OnChar(
Bram Moolenaar1266d672017-02-01 13:43:36 +0100752 HWND hwnd UNUSED,
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100753 UINT ch,
Bram Moolenaar1266d672017-02-01 13:43:36 +0100754 int cRepeat UNUSED)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100755{
756 char_u string[40];
757 int len = 0;
758
759 dead_key = 0;
760
761 len = char_to_string(ch, string, 40, FALSE);
762 if (len == 1 && string[0] == Ctrl_C && ctrl_c_interrupts)
763 {
764 trash_input_buf();
765 got_int = TRUE;
766 }
767
768 add_to_input_buf(string, len);
769}
770
771/*
772 * Alt-Key hit, add it to the input buffer.
773 */
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100774 static void
775_OnSysChar(
Bram Moolenaar1266d672017-02-01 13:43:36 +0100776 HWND hwnd UNUSED,
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100777 UINT cch,
Bram Moolenaar1266d672017-02-01 13:43:36 +0100778 int cRepeat UNUSED)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100779{
780 char_u string[40]; /* Enough for multibyte character */
781 int len;
782 int modifiers;
783 int ch = cch; /* special keys are negative */
784
785 dead_key = 0;
786
787 /* TRACE("OnSysChar(%d, %c)\n", ch, ch); */
788
789 /* OK, we have a character key (given by ch) which was entered with the
790 * ALT key pressed. Eg, if the user presses Alt-A, then ch == 'A'. Note
791 * that the system distinguishes Alt-a and Alt-A (Alt-Shift-a unless
792 * CAPSLOCK is pressed) at this point.
793 */
794 modifiers = MOD_MASK_ALT;
795 if (GetKeyState(VK_SHIFT) & 0x8000)
796 modifiers |= MOD_MASK_SHIFT;
797 if (GetKeyState(VK_CONTROL) & 0x8000)
798 modifiers |= MOD_MASK_CTRL;
799
800 ch = simplify_key(ch, &modifiers);
801 /* remove the SHIFT modifier for keys where it's already included, e.g.,
802 * '(' and '*' */
803 if (ch < 0x100 && !isalpha(ch) && isprint(ch))
804 modifiers &= ~MOD_MASK_SHIFT;
805
806 /* Interpret the ALT key as making the key META, include SHIFT, etc. */
807 ch = extract_modifiers(ch, &modifiers);
808 if (ch == CSI)
809 ch = K_CSI;
810
811 len = 0;
812 if (modifiers)
813 {
814 string[len++] = CSI;
815 string[len++] = KS_MODIFIER;
816 string[len++] = modifiers;
817 }
818
819 if (IS_SPECIAL((int)ch))
820 {
821 string[len++] = CSI;
822 string[len++] = K_SECOND((int)ch);
823 string[len++] = K_THIRD((int)ch);
824 }
825 else
826 {
827 /* Although the documentation isn't clear about it, we assume "ch" is
828 * a Unicode character. */
829 len += char_to_string(ch, string + len, 40 - len, TRUE);
830 }
831
832 add_to_input_buf(string, len);
833}
834
835 static void
836_OnMouseEvent(
837 int button,
838 int x,
839 int y,
840 int repeated_click,
841 UINT keyFlags)
842{
843 int vim_modifiers = 0x0;
844
845 s_getting_focus = FALSE;
846
847 if (keyFlags & MK_SHIFT)
848 vim_modifiers |= MOUSE_SHIFT;
849 if (keyFlags & MK_CONTROL)
850 vim_modifiers |= MOUSE_CTRL;
851 if (GetKeyState(VK_MENU) & 0x8000)
852 vim_modifiers |= MOUSE_ALT;
853
854 gui_send_mouse_event(button, x, y, repeated_click, vim_modifiers);
855}
856
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100857 static void
858_OnMouseButtonDown(
Bram Moolenaar1266d672017-02-01 13:43:36 +0100859 HWND hwnd UNUSED,
860 BOOL fDoubleClick UNUSED,
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100861 int x,
862 int y,
863 UINT keyFlags)
864{
865 static LONG s_prevTime = 0;
866
867 LONG currentTime = GetMessageTime();
868 int button = -1;
869 int repeated_click;
870
871 /* Give main window the focus: this is so the cursor isn't hollow. */
872 (void)SetFocus(s_hwnd);
873
874 if (s_uMsg == WM_LBUTTONDOWN || s_uMsg == WM_LBUTTONDBLCLK)
875 button = MOUSE_LEFT;
876 else if (s_uMsg == WM_MBUTTONDOWN || s_uMsg == WM_MBUTTONDBLCLK)
877 button = MOUSE_MIDDLE;
878 else if (s_uMsg == WM_RBUTTONDOWN || s_uMsg == WM_RBUTTONDBLCLK)
879 button = MOUSE_RIGHT;
880 else if (s_uMsg == WM_XBUTTONDOWN || s_uMsg == WM_XBUTTONDBLCLK)
881 {
882#ifndef GET_XBUTTON_WPARAM
883# define GET_XBUTTON_WPARAM(wParam) (HIWORD(wParam))
884#endif
885 button = ((GET_XBUTTON_WPARAM(s_wParam) == 1) ? MOUSE_X1 : MOUSE_X2);
886 }
887 else if (s_uMsg == WM_CAPTURECHANGED)
888 {
889 /* on W95/NT4, somehow you get in here with an odd Msg
890 * if you press one button while holding down the other..*/
891 if (s_button_pending == MOUSE_LEFT)
892 button = MOUSE_RIGHT;
893 else
894 button = MOUSE_LEFT;
895 }
896 if (button >= 0)
897 {
898 repeated_click = ((int)(currentTime - s_prevTime) < p_mouset);
899
900 /*
901 * Holding down the left and right buttons simulates pushing the middle
902 * button.
903 */
904 if (repeated_click
905 && ((button == MOUSE_LEFT && s_button_pending == MOUSE_RIGHT)
906 || (button == MOUSE_RIGHT
907 && s_button_pending == MOUSE_LEFT)))
908 {
909 /*
910 * Hmm, gui.c will ignore more than one button down at a time, so
911 * pretend we let go of it first.
912 */
913 gui_send_mouse_event(MOUSE_RELEASE, x, y, FALSE, 0x0);
914 button = MOUSE_MIDDLE;
915 repeated_click = FALSE;
916 s_button_pending = -1;
917 _OnMouseEvent(button, x, y, repeated_click, keyFlags);
918 }
919 else if ((repeated_click)
920 || (mouse_model_popup() && (button == MOUSE_RIGHT)))
921 {
922 if (s_button_pending > -1)
923 {
924 _OnMouseEvent(s_button_pending, x, y, FALSE, keyFlags);
925 s_button_pending = -1;
926 }
927 /* TRACE("Button down at x %d, y %d\n", x, y); */
928 _OnMouseEvent(button, x, y, repeated_click, keyFlags);
929 }
930 else
931 {
932 /*
933 * If this is the first press (i.e. not a multiple click) don't
934 * action immediately, but store and wait for:
935 * i) button-up
936 * ii) mouse move
937 * iii) another button press
938 * before using it.
939 * This enables us to make left+right simulate middle button,
940 * without left or right being actioned first. The side-effect is
941 * that if you click and hold the mouse without dragging, the
942 * cursor doesn't move until you release the button. In practice
943 * this is hardly a problem.
944 */
945 s_button_pending = button;
946 s_x_pending = x;
947 s_y_pending = y;
948 s_kFlags_pending = keyFlags;
949 }
950
951 s_prevTime = currentTime;
952 }
953}
954
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100955 static void
956_OnMouseMoveOrRelease(
Bram Moolenaar1266d672017-02-01 13:43:36 +0100957 HWND hwnd UNUSED,
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100958 int x,
959 int y,
960 UINT keyFlags)
961{
962 int button;
963
964 s_getting_focus = FALSE;
965 if (s_button_pending > -1)
966 {
967 /* Delayed action for mouse down event */
968 _OnMouseEvent(s_button_pending, s_x_pending,
969 s_y_pending, FALSE, s_kFlags_pending);
970 s_button_pending = -1;
971 }
972 if (s_uMsg == WM_MOUSEMOVE)
973 {
974 /*
975 * It's only a MOUSE_DRAG if one or more mouse buttons are being held
976 * down.
977 */
978 if (!(keyFlags & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON
979 | MK_XBUTTON1 | MK_XBUTTON2)))
980 {
981 gui_mouse_moved(x, y);
982 return;
983 }
984
985 /*
986 * While button is down, keep grabbing mouse move events when
987 * the mouse goes outside the window
988 */
989 SetCapture(s_textArea);
990 button = MOUSE_DRAG;
991 /* TRACE(" move at x %d, y %d\n", x, y); */
992 }
993 else
994 {
995 ReleaseCapture();
996 button = MOUSE_RELEASE;
997 /* TRACE(" up at x %d, y %d\n", x, y); */
998 }
999
1000 _OnMouseEvent(button, x, y, FALSE, keyFlags);
1001}
1002
1003#ifdef FEAT_MENU
1004/*
1005 * Find the vimmenu_T with the given id
1006 */
1007 static vimmenu_T *
1008gui_mswin_find_menu(
1009 vimmenu_T *pMenu,
1010 int id)
1011{
1012 vimmenu_T *pChildMenu;
1013
1014 while (pMenu)
1015 {
1016 if (pMenu->id == (UINT)id)
1017 break;
1018 if (pMenu->children != NULL)
1019 {
1020 pChildMenu = gui_mswin_find_menu(pMenu->children, id);
1021 if (pChildMenu)
1022 {
1023 pMenu = pChildMenu;
1024 break;
1025 }
1026 }
1027 pMenu = pMenu->next;
1028 }
1029 return pMenu;
1030}
1031
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001032 static void
1033_OnMenu(
Bram Moolenaar1266d672017-02-01 13:43:36 +01001034 HWND hwnd UNUSED,
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001035 int id,
Bram Moolenaar1266d672017-02-01 13:43:36 +01001036 HWND hwndCtl UNUSED,
1037 UINT codeNotify UNUSED)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001038{
1039 vimmenu_T *pMenu;
1040
1041 pMenu = gui_mswin_find_menu(root_menu, id);
1042 if (pMenu)
1043 gui_menu_cb(pMenu);
1044}
1045#endif
1046
1047#ifdef MSWIN_FIND_REPLACE
Bram Moolenaarcea912a2016-10-12 14:20:24 +02001048# ifdef FEAT_MBYTE
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001049/*
1050 * copy useful data from structure LPFINDREPLACE to structure LPFINDREPLACEW
1051 */
1052 static void
1053findrep_atow(LPFINDREPLACEW lpfrw, LPFINDREPLACE lpfr)
1054{
1055 WCHAR *wp;
1056
1057 lpfrw->hwndOwner = lpfr->hwndOwner;
1058 lpfrw->Flags = lpfr->Flags;
1059
1060 wp = enc_to_utf16((char_u *)lpfr->lpstrFindWhat, NULL);
1061 wcsncpy(lpfrw->lpstrFindWhat, wp, lpfrw->wFindWhatLen - 1);
1062 vim_free(wp);
1063
1064 /* the field "lpstrReplaceWith" doesn't need to be copied */
1065}
1066
1067/*
1068 * copy useful data from structure LPFINDREPLACEW to structure LPFINDREPLACE
1069 */
1070 static void
1071findrep_wtoa(LPFINDREPLACE lpfr, LPFINDREPLACEW lpfrw)
1072{
1073 char_u *p;
1074
1075 lpfr->Flags = lpfrw->Flags;
1076
1077 p = utf16_to_enc((short_u*)lpfrw->lpstrFindWhat, NULL);
1078 vim_strncpy((char_u *)lpfr->lpstrFindWhat, p, lpfr->wFindWhatLen - 1);
1079 vim_free(p);
1080
1081 p = utf16_to_enc((short_u*)lpfrw->lpstrReplaceWith, NULL);
1082 vim_strncpy((char_u *)lpfr->lpstrReplaceWith, p, lpfr->wReplaceWithLen - 1);
1083 vim_free(p);
1084}
1085# endif
1086
1087/*
1088 * Handle a Find/Replace window message.
1089 */
1090 static void
1091_OnFindRepl(void)
1092{
1093 int flags = 0;
1094 int down;
1095
Bram Moolenaarcea912a2016-10-12 14:20:24 +02001096# ifdef FEAT_MBYTE
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001097 /* If the OS is Windows NT, and 'encoding' differs from active codepage:
1098 * convert text from wide string. */
Bram Moolenaarcea912a2016-10-12 14:20:24 +02001099 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001100 {
1101 findrep_wtoa(&s_findrep_struct, &s_findrep_struct_w);
1102 }
1103# endif
1104
1105 if (s_findrep_struct.Flags & FR_DIALOGTERM)
1106 /* Give main window the focus back. */
1107 (void)SetFocus(s_hwnd);
1108
1109 if (s_findrep_struct.Flags & FR_FINDNEXT)
1110 {
1111 flags = FRD_FINDNEXT;
1112
1113 /* Give main window the focus back: this is so the cursor isn't
1114 * hollow. */
1115 (void)SetFocus(s_hwnd);
1116 }
1117 else if (s_findrep_struct.Flags & FR_REPLACE)
1118 {
1119 flags = FRD_REPLACE;
1120
1121 /* Give main window the focus back: this is so the cursor isn't
1122 * hollow. */
1123 (void)SetFocus(s_hwnd);
1124 }
1125 else if (s_findrep_struct.Flags & FR_REPLACEALL)
1126 {
1127 flags = FRD_REPLACEALL;
1128 }
1129
1130 if (flags != 0)
1131 {
1132 /* Call the generic GUI function to do the actual work. */
1133 if (s_findrep_struct.Flags & FR_WHOLEWORD)
1134 flags |= FRD_WHOLE_WORD;
1135 if (s_findrep_struct.Flags & FR_MATCHCASE)
1136 flags |= FRD_MATCH_CASE;
1137 down = (s_findrep_struct.Flags & FR_DOWN) != 0;
1138 gui_do_findrepl(flags, (char_u *)s_findrep_struct.lpstrFindWhat,
1139 (char_u *)s_findrep_struct.lpstrReplaceWith, down);
1140 }
1141}
1142#endif
1143
1144 static void
1145HandleMouseHide(UINT uMsg, LPARAM lParam)
1146{
1147 static LPARAM last_lParam = 0L;
1148
1149 /* We sometimes get a mousemove when the mouse didn't move... */
1150 if (uMsg == WM_MOUSEMOVE || uMsg == WM_NCMOUSEMOVE)
1151 {
1152 if (lParam == last_lParam)
1153 return;
1154 last_lParam = lParam;
1155 }
1156
1157 /* Handle specially, to centralise coding. We need to be sure we catch all
1158 * possible events which should cause us to restore the cursor (as it is a
1159 * shared resource, we take full responsibility for it).
1160 */
1161 switch (uMsg)
1162 {
1163 case WM_KEYUP:
1164 case WM_CHAR:
1165 /*
1166 * blank out the pointer if necessary
1167 */
1168 if (p_mh)
1169 gui_mch_mousehide(TRUE);
1170 break;
1171
1172 case WM_SYSKEYUP: /* show the pointer when a system-key is pressed */
1173 case WM_SYSCHAR:
1174 case WM_MOUSEMOVE: /* show the pointer on any mouse action */
1175 case WM_LBUTTONDOWN:
1176 case WM_LBUTTONUP:
1177 case WM_MBUTTONDOWN:
1178 case WM_MBUTTONUP:
1179 case WM_RBUTTONDOWN:
1180 case WM_RBUTTONUP:
1181 case WM_XBUTTONDOWN:
1182 case WM_XBUTTONUP:
1183 case WM_NCMOUSEMOVE:
1184 case WM_NCLBUTTONDOWN:
1185 case WM_NCLBUTTONUP:
1186 case WM_NCMBUTTONDOWN:
1187 case WM_NCMBUTTONUP:
1188 case WM_NCRBUTTONDOWN:
1189 case WM_NCRBUTTONUP:
1190 case WM_KILLFOCUS:
1191 /*
1192 * if the pointer is currently hidden, then we should show it.
1193 */
1194 gui_mch_mousehide(FALSE);
1195 break;
1196 }
1197}
1198
1199 static LRESULT CALLBACK
1200_TextAreaWndProc(
1201 HWND hwnd,
1202 UINT uMsg,
1203 WPARAM wParam,
1204 LPARAM lParam)
1205{
1206 /*
1207 TRACE("TextAreaWndProc: hwnd = %08x, msg = %x, wParam = %x, lParam = %x\n",
1208 hwnd, uMsg, wParam, lParam);
1209 */
1210
1211 HandleMouseHide(uMsg, lParam);
1212
1213 s_uMsg = uMsg;
1214 s_wParam = wParam;
1215 s_lParam = lParam;
1216
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01001217#ifdef FEAT_BEVAL_GUI
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001218 TrackUserActivity(uMsg);
1219#endif
1220
1221 switch (uMsg)
1222 {
1223 HANDLE_MSG(hwnd, WM_LBUTTONDBLCLK,_OnMouseButtonDown);
1224 HANDLE_MSG(hwnd, WM_LBUTTONDOWN,_OnMouseButtonDown);
1225 HANDLE_MSG(hwnd, WM_LBUTTONUP, _OnMouseMoveOrRelease);
1226 HANDLE_MSG(hwnd, WM_MBUTTONDBLCLK,_OnMouseButtonDown);
1227 HANDLE_MSG(hwnd, WM_MBUTTONDOWN,_OnMouseButtonDown);
1228 HANDLE_MSG(hwnd, WM_MBUTTONUP, _OnMouseMoveOrRelease);
1229 HANDLE_MSG(hwnd, WM_MOUSEMOVE, _OnMouseMoveOrRelease);
1230 HANDLE_MSG(hwnd, WM_PAINT, _OnPaint);
1231 HANDLE_MSG(hwnd, WM_RBUTTONDBLCLK,_OnMouseButtonDown);
1232 HANDLE_MSG(hwnd, WM_RBUTTONDOWN,_OnMouseButtonDown);
1233 HANDLE_MSG(hwnd, WM_RBUTTONUP, _OnMouseMoveOrRelease);
1234 HANDLE_MSG(hwnd, WM_XBUTTONDBLCLK,_OnMouseButtonDown);
1235 HANDLE_MSG(hwnd, WM_XBUTTONDOWN,_OnMouseButtonDown);
1236 HANDLE_MSG(hwnd, WM_XBUTTONUP, _OnMouseMoveOrRelease);
1237
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01001238#ifdef FEAT_BEVAL_GUI
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001239 case WM_NOTIFY: Handle_WM_Notify(hwnd, (LPNMHDR)lParam);
1240 return TRUE;
1241#endif
1242 default:
1243 return MyWindowProc(hwnd, uMsg, wParam, lParam);
1244 }
1245}
1246
Bram Moolenaarcea912a2016-10-12 14:20:24 +02001247#if defined(FEAT_MBYTE) \
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001248 || defined(GLOBAL_IME) \
1249 || defined(PROTO)
1250# ifdef PROTO
1251typedef int WINAPI;
1252# endif
1253
1254 LRESULT WINAPI
1255vim_WindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
1256{
1257# ifdef GLOBAL_IME
1258 return global_ime_DefWindowProc(hwnd, message, wParam, lParam);
1259# else
1260 if (wide_WindowProc)
1261 return DefWindowProcW(hwnd, message, wParam, lParam);
1262 return DefWindowProc(hwnd, message, wParam, lParam);
1263#endif
1264}
1265#endif
1266
1267/*
1268 * Called when the foreground or background color has been changed.
1269 */
1270 void
1271gui_mch_new_colors(void)
1272{
1273 /* nothing to do? */
1274}
1275
1276/*
1277 * Set the colors to their default values.
1278 */
1279 void
1280gui_mch_def_colors(void)
1281{
1282 gui.norm_pixel = GetSysColor(COLOR_WINDOWTEXT);
1283 gui.back_pixel = GetSysColor(COLOR_WINDOW);
1284 gui.def_norm_pixel = gui.norm_pixel;
1285 gui.def_back_pixel = gui.back_pixel;
1286}
1287
1288/*
1289 * Open the GUI window which was created by a call to gui_mch_init().
1290 */
1291 int
1292gui_mch_open(void)
1293{
1294#ifndef SW_SHOWDEFAULT
1295# define SW_SHOWDEFAULT 10 /* Borland 5.0 doesn't have it */
1296#endif
1297 /* Actually open the window, if not already visible
1298 * (may be done already in gui_mch_set_shellsize) */
1299 if (!IsWindowVisible(s_hwnd))
1300 ShowWindow(s_hwnd, SW_SHOWDEFAULT);
1301
1302#ifdef MSWIN_FIND_REPLACE
1303 /* Init replace string here, so that we keep it when re-opening the
1304 * dialog. */
1305 s_findrep_struct.lpstrReplaceWith[0] = NUL;
1306#endif
1307
1308 return OK;
1309}
1310
1311/*
1312 * Get the position of the top left corner of the window.
1313 */
1314 int
1315gui_mch_get_winpos(int *x, int *y)
1316{
1317 RECT rect;
1318
1319 GetWindowRect(s_hwnd, &rect);
1320 *x = rect.left;
1321 *y = rect.top;
1322 return OK;
1323}
1324
1325/*
1326 * Set the position of the top left corner of the window to the given
1327 * coordinates.
1328 */
1329 void
1330gui_mch_set_winpos(int x, int y)
1331{
1332 SetWindowPos(s_hwnd, NULL, x, y, 0, 0,
1333 SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
1334}
1335 void
1336gui_mch_set_text_area_pos(int x, int y, int w, int h)
1337{
1338 static int oldx = 0;
1339 static int oldy = 0;
1340
1341 SetWindowPos(s_textArea, NULL, x, y, w, h, SWP_NOZORDER | SWP_NOACTIVATE);
1342
1343#ifdef FEAT_TOOLBAR
1344 if (vim_strchr(p_go, GO_TOOLBAR) != NULL)
1345 SendMessage(s_toolbarhwnd, WM_SIZE,
1346 (WPARAM)0, (LPARAM)(w + ((long)(TOOLBAR_BUTTON_HEIGHT+8)<<16)));
1347#endif
1348#if defined(FEAT_GUI_TABLINE)
1349 if (showing_tabline)
1350 {
1351 int top = 0;
1352 RECT rect;
1353
1354# ifdef FEAT_TOOLBAR
1355 if (vim_strchr(p_go, GO_TOOLBAR) != NULL)
1356 top = TOOLBAR_BUTTON_HEIGHT + TOOLBAR_BORDER_HEIGHT;
1357# endif
1358 GetClientRect(s_hwnd, &rect);
1359 MoveWindow(s_tabhwnd, 0, top, rect.right, gui.tabline_height, TRUE);
1360 }
1361#endif
1362
1363 /* When side scroll bar is unshown, the size of window will change.
1364 * then, the text area move left or right. thus client rect should be
1365 * forcedly redrawn. (Yasuhiro Matsumoto) */
1366 if (oldx != x || oldy != y)
1367 {
1368 InvalidateRect(s_hwnd, NULL, FALSE);
1369 oldx = x;
1370 oldy = y;
1371 }
1372}
1373
1374
1375/*
1376 * Scrollbar stuff:
1377 */
1378
1379 void
1380gui_mch_enable_scrollbar(
1381 scrollbar_T *sb,
1382 int flag)
1383{
1384 ShowScrollBar(sb->id, SB_CTL, flag);
1385
1386 /* TODO: When the window is maximized, the size of the window stays the
1387 * same, thus the size of the text area changes. On Win98 it's OK, on Win
1388 * NT 4.0 it's not... */
1389}
1390
1391 void
1392gui_mch_set_scrollbar_pos(
1393 scrollbar_T *sb,
1394 int x,
1395 int y,
1396 int w,
1397 int h)
1398{
1399 SetWindowPos(sb->id, NULL, x, y, w, h,
1400 SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW);
1401}
1402
1403 void
1404gui_mch_create_scrollbar(
1405 scrollbar_T *sb,
1406 int orient) /* SBAR_VERT or SBAR_HORIZ */
1407{
1408 sb->id = CreateWindow(
1409 "SCROLLBAR", "Scrollbar",
1410 WS_CHILD | ((orient == SBAR_VERT) ? SBS_VERT : SBS_HORZ), 0, 0,
1411 10, /* Any value will do for now */
1412 10, /* Any value will do for now */
1413 s_hwnd, NULL,
1414 s_hinst, NULL);
1415}
1416
1417/*
1418 * Find the scrollbar with the given hwnd.
1419 */
1420 static scrollbar_T *
1421gui_mswin_find_scrollbar(HWND hwnd)
1422{
1423 win_T *wp;
1424
1425 if (gui.bottom_sbar.id == hwnd)
1426 return &gui.bottom_sbar;
1427 FOR_ALL_WINDOWS(wp)
1428 {
1429 if (wp->w_scrollbars[SBAR_LEFT].id == hwnd)
1430 return &wp->w_scrollbars[SBAR_LEFT];
1431 if (wp->w_scrollbars[SBAR_RIGHT].id == hwnd)
1432 return &wp->w_scrollbars[SBAR_RIGHT];
1433 }
1434 return NULL;
1435}
1436
1437/*
1438 * Get the character size of a font.
1439 */
1440 static void
1441GetFontSize(GuiFont font)
1442{
1443 HWND hwnd = GetDesktopWindow();
1444 HDC hdc = GetWindowDC(hwnd);
1445 HFONT hfntOld = SelectFont(hdc, (HFONT)font);
1446 TEXTMETRIC tm;
1447
1448 GetTextMetrics(hdc, &tm);
1449 gui.char_width = tm.tmAveCharWidth + tm.tmOverhang;
1450
1451 gui.char_height = tm.tmHeight + p_linespace;
1452
1453 SelectFont(hdc, hfntOld);
1454
1455 ReleaseDC(hwnd, hdc);
1456}
1457
1458/*
1459 * Adjust gui.char_height (after 'linespace' was changed).
1460 */
1461 int
1462gui_mch_adjust_charheight(void)
1463{
1464 GetFontSize(gui.norm_font);
1465 return OK;
1466}
1467
1468 static GuiFont
1469get_font_handle(LOGFONT *lf)
1470{
1471 HFONT font = NULL;
1472
1473 /* Load the font */
1474 font = CreateFontIndirect(lf);
1475
1476 if (font == NULL)
1477 return NOFONT;
1478
1479 return (GuiFont)font;
1480}
1481
1482 static int
1483pixels_to_points(int pixels, int vertical)
1484{
1485 int points;
1486 HWND hwnd;
1487 HDC hdc;
1488
1489 hwnd = GetDesktopWindow();
1490 hdc = GetWindowDC(hwnd);
1491
1492 points = MulDiv(pixels, 72,
1493 GetDeviceCaps(hdc, vertical ? LOGPIXELSY : LOGPIXELSX));
1494
1495 ReleaseDC(hwnd, hdc);
1496
1497 return points;
1498}
1499
1500 GuiFont
1501gui_mch_get_font(
1502 char_u *name,
1503 int giveErrorIfMissing)
1504{
1505 LOGFONT lf;
1506 GuiFont font = NOFONT;
1507
1508 if (get_logfont(&lf, name, NULL, giveErrorIfMissing) == OK)
1509 font = get_font_handle(&lf);
1510 if (font == NOFONT && giveErrorIfMissing)
1511 EMSG2(_(e_font), name);
1512 return font;
1513}
1514
1515#if defined(FEAT_EVAL) || defined(PROTO)
1516/*
1517 * Return the name of font "font" in allocated memory.
1518 * Don't know how to get the actual name, thus use the provided name.
1519 */
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001520 char_u *
Bram Moolenaar1266d672017-02-01 13:43:36 +01001521gui_mch_get_fontname(GuiFont font UNUSED, char_u *name)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001522{
1523 if (name == NULL)
1524 return NULL;
1525 return vim_strsave(name);
1526}
1527#endif
1528
1529 void
1530gui_mch_free_font(GuiFont font)
1531{
1532 if (font)
1533 DeleteObject((HFONT)font);
1534}
1535
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001536/*
1537 * Return the Pixel value (color) for the given color name.
1538 * Return INVALCOLOR for error.
1539 */
1540 guicolor_T
1541gui_mch_get_color(char_u *name)
1542{
Bram Moolenaarc285fe72016-04-26 21:51:48 +02001543 int i;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001544
1545 typedef struct SysColorTable
1546 {
1547 char *name;
1548 int color;
1549 } SysColorTable;
1550
1551 static SysColorTable sys_table[] =
1552 {
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001553 {"SYS_3DDKSHADOW", COLOR_3DDKSHADOW},
1554 {"SYS_3DHILIGHT", COLOR_3DHILIGHT},
Bram Moolenaarcea912a2016-10-12 14:20:24 +02001555#ifdef COLOR_3DHIGHLIGHT
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001556 {"SYS_3DHIGHLIGHT", COLOR_3DHIGHLIGHT},
1557#endif
1558 {"SYS_BTNHILIGHT", COLOR_BTNHILIGHT},
1559 {"SYS_BTNHIGHLIGHT", COLOR_BTNHIGHLIGHT},
1560 {"SYS_3DLIGHT", COLOR_3DLIGHT},
1561 {"SYS_3DSHADOW", COLOR_3DSHADOW},
1562 {"SYS_DESKTOP", COLOR_DESKTOP},
1563 {"SYS_INFOBK", COLOR_INFOBK},
1564 {"SYS_INFOTEXT", COLOR_INFOTEXT},
1565 {"SYS_3DFACE", COLOR_3DFACE},
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001566 {"SYS_BTNFACE", COLOR_BTNFACE},
1567 {"SYS_BTNSHADOW", COLOR_BTNSHADOW},
1568 {"SYS_ACTIVEBORDER", COLOR_ACTIVEBORDER},
1569 {"SYS_ACTIVECAPTION", COLOR_ACTIVECAPTION},
1570 {"SYS_APPWORKSPACE", COLOR_APPWORKSPACE},
1571 {"SYS_BACKGROUND", COLOR_BACKGROUND},
1572 {"SYS_BTNTEXT", COLOR_BTNTEXT},
1573 {"SYS_CAPTIONTEXT", COLOR_CAPTIONTEXT},
1574 {"SYS_GRAYTEXT", COLOR_GRAYTEXT},
1575 {"SYS_HIGHLIGHT", COLOR_HIGHLIGHT},
1576 {"SYS_HIGHLIGHTTEXT", COLOR_HIGHLIGHTTEXT},
1577 {"SYS_INACTIVEBORDER", COLOR_INACTIVEBORDER},
1578 {"SYS_INACTIVECAPTION", COLOR_INACTIVECAPTION},
1579 {"SYS_INACTIVECAPTIONTEXT", COLOR_INACTIVECAPTIONTEXT},
1580 {"SYS_MENU", COLOR_MENU},
1581 {"SYS_MENUTEXT", COLOR_MENUTEXT},
1582 {"SYS_SCROLLBAR", COLOR_SCROLLBAR},
1583 {"SYS_WINDOW", COLOR_WINDOW},
1584 {"SYS_WINDOWFRAME", COLOR_WINDOWFRAME},
1585 {"SYS_WINDOWTEXT", COLOR_WINDOWTEXT}
1586 };
1587
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001588 /*
1589 * Try to look up a system colour.
1590 */
1591 for (i = 0; i < sizeof(sys_table) / sizeof(sys_table[0]); i++)
1592 if (STRICMP(name, sys_table[i].name) == 0)
1593 return GetSysColor(sys_table[i].color);
1594
Bram Moolenaarab302212016-04-26 20:59:29 +02001595 return gui_get_color_cmn(name);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001596}
Bram Moolenaarc285fe72016-04-26 21:51:48 +02001597
Bram Moolenaar26af85d2017-07-23 16:45:10 +02001598 guicolor_T
1599gui_mch_get_rgb_color(int r, int g, int b)
1600{
1601 return gui_get_rgb_color_cmn(r, g, b);
1602}
1603
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001604/*
1605 * Return OK if the key with the termcap name "name" is supported.
1606 */
1607 int
1608gui_mch_haskey(char_u *name)
1609{
1610 int i;
1611
1612 for (i = 0; special_keys[i].vim_code1 != NUL; i++)
1613 if (name[0] == special_keys[i].vim_code0 &&
1614 name[1] == special_keys[i].vim_code1)
1615 return OK;
1616 return FAIL;
1617}
1618
1619 void
1620gui_mch_beep(void)
1621{
1622 MessageBeep(MB_OK);
1623}
1624/*
1625 * Invert a rectangle from row r, column c, for nr rows and nc columns.
1626 */
1627 void
1628gui_mch_invert_rectangle(
1629 int r,
1630 int c,
1631 int nr,
1632 int nc)
1633{
1634 RECT rc;
1635
1636 /*
1637 * Note: InvertRect() excludes right and bottom of rectangle.
1638 */
1639 rc.left = FILL_X(c);
1640 rc.top = FILL_Y(r);
1641 rc.right = rc.left + nc * gui.char_width;
1642 rc.bottom = rc.top + nr * gui.char_height;
1643 InvertRect(s_hdc, &rc);
1644}
1645
1646/*
1647 * Iconify the GUI window.
1648 */
1649 void
1650gui_mch_iconify(void)
1651{
1652 ShowWindow(s_hwnd, SW_MINIMIZE);
1653}
1654
1655/*
1656 * Draw a cursor without focus.
1657 */
1658 void
1659gui_mch_draw_hollow_cursor(guicolor_T color)
1660{
1661 HBRUSH hbr;
1662 RECT rc;
1663
1664 /*
1665 * Note: FrameRect() excludes right and bottom of rectangle.
1666 */
1667 rc.left = FILL_X(gui.col);
1668 rc.top = FILL_Y(gui.row);
1669 rc.right = rc.left + gui.char_width;
1670#ifdef FEAT_MBYTE
1671 if (mb_lefthalve(gui.row, gui.col))
1672 rc.right += gui.char_width;
1673#endif
1674 rc.bottom = rc.top + gui.char_height;
1675 hbr = CreateSolidBrush(color);
1676 FrameRect(s_hdc, &rc, hbr);
1677 DeleteBrush(hbr);
1678}
1679/*
1680 * Draw part of a cursor, "w" pixels wide, and "h" pixels high, using
1681 * color "color".
1682 */
1683 void
1684gui_mch_draw_part_cursor(
1685 int w,
1686 int h,
1687 guicolor_T color)
1688{
1689 HBRUSH hbr;
1690 RECT rc;
1691
1692 /*
1693 * Note: FillRect() excludes right and bottom of rectangle.
1694 */
1695 rc.left =
1696#ifdef FEAT_RIGHTLEFT
1697 /* vertical line should be on the right of current point */
1698 CURSOR_BAR_RIGHT ? FILL_X(gui.col + 1) - w :
1699#endif
1700 FILL_X(gui.col);
1701 rc.top = FILL_Y(gui.row) + gui.char_height - h;
1702 rc.right = rc.left + w;
1703 rc.bottom = rc.top + h;
1704 hbr = CreateSolidBrush(color);
1705 FillRect(s_hdc, &rc, hbr);
1706 DeleteBrush(hbr);
1707}
1708
1709
1710/*
1711 * Generates a VK_SPACE when the internal dead_key flag is set to output the
1712 * dead key's nominal character and re-post the original message.
1713 */
1714 static void
1715outputDeadKey_rePost(MSG originalMsg)
1716{
1717 static MSG deadCharExpel;
1718
1719 if (!dead_key)
1720 return;
1721
1722 dead_key = 0;
1723
1724 /* Make Windows generate the dead key's character */
1725 deadCharExpel.message = originalMsg.message;
1726 deadCharExpel.hwnd = originalMsg.hwnd;
1727 deadCharExpel.wParam = VK_SPACE;
1728
1729 MyTranslateMessage(&deadCharExpel);
1730
1731 /* re-generate the current character free of the dead char influence */
1732 PostMessage(originalMsg.hwnd, originalMsg.message, originalMsg.wParam,
1733 originalMsg.lParam);
1734}
1735
1736
1737/*
1738 * Process a single Windows message.
1739 * If one is not available we hang until one is.
1740 */
1741 static void
1742process_message(void)
1743{
1744 MSG msg;
1745 UINT vk = 0; /* Virtual key */
1746 char_u string[40];
1747 int i;
1748 int modifiers = 0;
1749 int key;
1750#ifdef FEAT_MENU
1751 static char_u k10[] = {K_SPECIAL, 'k', ';', 0};
1752#endif
1753
1754 pGetMessage(&msg, NULL, 0, 0);
1755
1756#ifdef FEAT_OLE
1757 /* Look after OLE Automation commands */
1758 if (msg.message == WM_OLE)
1759 {
1760 char_u *str = (char_u *)msg.lParam;
1761 if (str == NULL || *str == NUL)
1762 {
1763 /* Message can't be ours, forward it. Fixes problem with Ultramon
1764 * 3.0.4 */
1765 pDispatchMessage(&msg);
1766 }
1767 else
1768 {
1769 add_to_input_buf(str, (int)STRLEN(str));
1770 vim_free(str); /* was allocated in CVim::SendKeys() */
1771 }
1772 return;
1773 }
1774#endif
1775
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001776#ifdef MSWIN_FIND_REPLACE
1777 /* Don't process messages used by the dialog */
1778 if (s_findrep_hwnd != NULL && pIsDialogMessage(s_findrep_hwnd, &msg))
1779 {
1780 HandleMouseHide(msg.message, msg.lParam);
1781 return;
1782 }
1783#endif
1784
1785 /*
1786 * Check if it's a special key that we recognise. If not, call
1787 * TranslateMessage().
1788 */
1789 if (msg.message == WM_KEYDOWN || msg.message == WM_SYSKEYDOWN)
1790 {
1791 vk = (int) msg.wParam;
1792
1793 /*
1794 * Handle dead keys in special conditions in other cases we let Windows
1795 * handle them and do not interfere.
1796 *
1797 * The dead_key flag must be reset on several occasions:
1798 * - in _OnChar() (or _OnSysChar()) as any dead key was necessarily
1799 * consumed at that point (This is when we let Windows combine the
1800 * dead character on its own)
1801 *
1802 * - Before doing something special such as regenerating keypresses to
1803 * expel the dead character as this could trigger an infinite loop if
1804 * for some reason MyTranslateMessage() do not trigger a call
1805 * immediately to _OnChar() (or _OnSysChar()).
1806 */
1807 if (dead_key)
1808 {
1809 /*
1810 * If a dead key was pressed and the user presses VK_SPACE,
1811 * VK_BACK, or VK_ESCAPE it means that he actually wants to deal
1812 * with the dead char now, so do nothing special and let Windows
1813 * handle it.
1814 *
1815 * Note that VK_SPACE combines with the dead_key's character and
1816 * only one WM_CHAR will be generated by TranslateMessage(), in
1817 * the two other cases two WM_CHAR will be generated: the dead
1818 * char and VK_BACK or VK_ESCAPE. That is most likely what the
1819 * user expects.
1820 */
1821 if ((vk == VK_SPACE || vk == VK_BACK || vk == VK_ESCAPE))
1822 {
1823 dead_key = 0;
1824 MyTranslateMessage(&msg);
1825 return;
1826 }
1827 /* In modes where we are not typing, dead keys should behave
1828 * normally */
1829 else if (!(get_real_state() & (INSERT | CMDLINE | SELECTMODE)))
1830 {
1831 outputDeadKey_rePost(msg);
1832 return;
1833 }
1834 }
1835
1836 /* Check for CTRL-BREAK */
1837 if (vk == VK_CANCEL)
1838 {
1839 trash_input_buf();
1840 got_int = TRUE;
Bram Moolenaar9698ad72017-08-12 14:52:15 +02001841 ctrl_break_was_pressed = TRUE;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001842 string[0] = Ctrl_C;
1843 add_to_input_buf(string, 1);
1844 }
1845
1846 for (i = 0; special_keys[i].key_sym != 0; i++)
1847 {
1848 /* ignore VK_SPACE when ALT key pressed: system menu */
1849 if (special_keys[i].key_sym == vk
1850 && (vk != VK_SPACE || !(GetKeyState(VK_MENU) & 0x8000)))
1851 {
1852 /*
Bram Moolenaar945ec092016-06-08 21:17:43 +02001853 * Behave as expected if we have a dead key and the special key
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001854 * is a key that would normally trigger the dead key nominal
1855 * character output (such as a NUMPAD printable character or
1856 * the TAB key, etc...).
1857 */
1858 if (dead_key && (special_keys[i].vim_code0 == 'K'
1859 || vk == VK_TAB || vk == CAR))
1860 {
1861 outputDeadKey_rePost(msg);
1862 return;
1863 }
1864
1865#ifdef FEAT_MENU
1866 /* Check for <F10>: Windows selects the menu. When <F10> is
1867 * mapped we want to use the mapping instead. */
1868 if (vk == VK_F10
1869 && gui.menu_is_active
1870 && check_map(k10, State, FALSE, TRUE, FALSE,
1871 NULL, NULL) == NULL)
1872 break;
1873#endif
1874 if (GetKeyState(VK_SHIFT) & 0x8000)
1875 modifiers |= MOD_MASK_SHIFT;
1876 /*
1877 * Don't use caps-lock as shift, because these are special keys
1878 * being considered here, and we only want letters to get
1879 * shifted -- webb
1880 */
1881 /*
1882 if (GetKeyState(VK_CAPITAL) & 0x0001)
1883 modifiers ^= MOD_MASK_SHIFT;
1884 */
1885 if (GetKeyState(VK_CONTROL) & 0x8000)
1886 modifiers |= MOD_MASK_CTRL;
1887 if (GetKeyState(VK_MENU) & 0x8000)
1888 modifiers |= MOD_MASK_ALT;
1889
1890 if (special_keys[i].vim_code1 == NUL)
1891 key = special_keys[i].vim_code0;
1892 else
1893 key = TO_SPECIAL(special_keys[i].vim_code0,
1894 special_keys[i].vim_code1);
1895 key = simplify_key(key, &modifiers);
1896 if (key == CSI)
1897 key = K_CSI;
1898
1899 if (modifiers)
1900 {
1901 string[0] = CSI;
1902 string[1] = KS_MODIFIER;
1903 string[2] = modifiers;
1904 add_to_input_buf(string, 3);
1905 }
1906
1907 if (IS_SPECIAL(key))
1908 {
1909 string[0] = CSI;
1910 string[1] = K_SECOND(key);
1911 string[2] = K_THIRD(key);
1912 add_to_input_buf(string, 3);
1913 }
1914 else
1915 {
1916 int len;
1917
1918 /* Handle "key" as a Unicode character. */
1919 len = char_to_string(key, string, 40, FALSE);
1920 add_to_input_buf(string, len);
1921 }
1922 break;
1923 }
1924 }
1925 if (special_keys[i].key_sym == 0)
1926 {
1927 /* Some keys need C-S- where they should only need C-.
1928 * Ignore 0xff, Windows XP sends it when NUMLOCK has changed since
1929 * system startup (Helmut Stiegler, 2003 Oct 3). */
1930 if (vk != 0xff
1931 && (GetKeyState(VK_CONTROL) & 0x8000)
1932 && !(GetKeyState(VK_SHIFT) & 0x8000)
1933 && !(GetKeyState(VK_MENU) & 0x8000))
1934 {
1935 /* CTRL-6 is '^'; Japanese keyboard maps '^' to vk == 0xDE */
1936 if (vk == '6' || MapVirtualKey(vk, 2) == (UINT)'^')
1937 {
1938 string[0] = Ctrl_HAT;
1939 add_to_input_buf(string, 1);
1940 }
1941 /* vk == 0xBD AZERTY for CTRL-'-', but CTRL-[ for * QWERTY! */
1942 else if (vk == 0xBD) /* QWERTY for CTRL-'-' */
1943 {
1944 string[0] = Ctrl__;
1945 add_to_input_buf(string, 1);
1946 }
1947 /* CTRL-2 is '@'; Japanese keyboard maps '@' to vk == 0xC0 */
1948 else if (vk == '2' || MapVirtualKey(vk, 2) == (UINT)'@')
1949 {
1950 string[0] = Ctrl_AT;
1951 add_to_input_buf(string, 1);
1952 }
1953 else
1954 MyTranslateMessage(&msg);
1955 }
1956 else
1957 MyTranslateMessage(&msg);
1958 }
1959 }
1960#ifdef FEAT_MBYTE_IME
1961 else if (msg.message == WM_IME_NOTIFY)
1962 _OnImeNotify(msg.hwnd, (DWORD)msg.wParam, (DWORD)msg.lParam);
1963 else if (msg.message == WM_KEYUP && im_get_status())
1964 /* added for non-MS IME (Yasuhiro Matsumoto) */
1965 MyTranslateMessage(&msg);
1966#endif
1967#if !defined(FEAT_MBYTE_IME) && defined(GLOBAL_IME)
1968/* GIME_TEST */
1969 else if (msg.message == WM_IME_STARTCOMPOSITION)
1970 {
1971 POINT point;
1972
1973 global_ime_set_font(&norm_logfont);
1974 point.x = FILL_X(gui.col);
1975 point.y = FILL_Y(gui.row);
1976 MapWindowPoints(s_textArea, s_hwnd, &point, 1);
1977 global_ime_set_position(&point);
1978 }
1979#endif
1980
1981#ifdef FEAT_MENU
1982 /* Check for <F10>: Default effect is to select the menu. When <F10> is
1983 * mapped we need to stop it here to avoid strange effects (e.g., for the
1984 * key-up event) */
1985 if (vk != VK_F10 || check_map(k10, State, FALSE, TRUE, FALSE,
1986 NULL, NULL) == NULL)
1987#endif
1988 pDispatchMessage(&msg);
1989}
1990
1991/*
1992 * Catch up with any queued events. This may put keyboard input into the
1993 * input buffer, call resize call-backs, trigger timers etc. If there is
1994 * nothing in the event queue (& no timers pending), then we return
1995 * immediately.
1996 */
1997 void
1998gui_mch_update(void)
1999{
2000 MSG msg;
2001
2002 if (!s_busy_processing)
2003 while (pPeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)
2004 && !vim_is_input_buf_full())
2005 process_message();
2006}
2007
Bram Moolenaar4231da42016-06-02 14:30:04 +02002008 static void
2009remove_any_timer(void)
2010{
2011 MSG msg;
2012
2013 if (s_wait_timer != 0 && !s_timed_out)
2014 {
2015 KillTimer(NULL, s_wait_timer);
2016
2017 /* Eat spurious WM_TIMER messages */
2018 while (pPeekMessage(&msg, s_hwnd, WM_TIMER, WM_TIMER, PM_REMOVE))
2019 ;
2020 s_wait_timer = 0;
2021 }
2022}
2023
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002024/*
2025 * GUI input routine called by gui_wait_for_chars(). Waits for a character
2026 * from the keyboard.
2027 * wtime == -1 Wait forever.
2028 * wtime == 0 This should never happen.
2029 * wtime > 0 Wait wtime milliseconds for a character.
2030 * Returns OK if a character was found to be available within the given time,
2031 * or FAIL otherwise.
2032 */
2033 int
2034gui_mch_wait_for_chars(int wtime)
2035{
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002036 int focus;
2037
2038 s_timed_out = FALSE;
2039
2040 if (wtime > 0)
2041 {
2042 /* Don't do anything while processing a (scroll) message. */
2043 if (s_busy_processing)
2044 return FAIL;
2045 s_wait_timer = (UINT)SetTimer(NULL, 0, (UINT)wtime,
2046 (TIMERPROC)_OnTimer);
2047 }
2048
2049 allow_scrollbar = TRUE;
2050
2051 focus = gui.in_focus;
2052 while (!s_timed_out)
2053 {
2054 /* Stop or start blinking when focus changes */
2055 if (gui.in_focus != focus)
2056 {
2057 if (gui.in_focus)
2058 gui_mch_start_blink();
2059 else
2060 gui_mch_stop_blink();
2061 focus = gui.in_focus;
2062 }
2063
2064 if (s_need_activate)
2065 {
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002066 (void)SetForegroundWindow(s_hwnd);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002067 s_need_activate = FALSE;
2068 }
2069
Bram Moolenaar4231da42016-06-02 14:30:04 +02002070#ifdef FEAT_TIMERS
2071 did_add_timer = FALSE;
2072#endif
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002073#ifdef MESSAGE_QUEUE
Bram Moolenaar62426e12017-08-13 15:37:58 +02002074 /* Check channel I/O while waiting for a message. */
Bram Moolenaar9186a272016-02-23 19:34:01 +01002075 for (;;)
2076 {
2077 MSG msg;
2078
2079 parse_queued_messages();
2080
Bram Moolenaar62426e12017-08-13 15:37:58 +02002081 if (pPeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
2082 {
2083 process_message();
2084 break;
2085 }
2086 else if (MsgWaitForMultipleObjects(0, NULL, FALSE, 100, QS_ALLINPUT)
2087 != WAIT_TIMEOUT)
Bram Moolenaar9186a272016-02-23 19:34:01 +01002088 break;
2089 }
Bram Moolenaar62426e12017-08-13 15:37:58 +02002090#else
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002091 /*
2092 * Don't use gui_mch_update() because then we will spin-lock until a
2093 * char arrives, instead we use GetMessage() to hang until an
2094 * event arrives. No need to check for input_buf_full because we are
2095 * returning as soon as it contains a single char -- webb
2096 */
2097 process_message();
Bram Moolenaar62426e12017-08-13 15:37:58 +02002098#endif
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002099
2100 if (input_available())
2101 {
Bram Moolenaar4231da42016-06-02 14:30:04 +02002102 remove_any_timer();
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002103 allow_scrollbar = FALSE;
2104
2105 /* Clear pending mouse button, the release event may have been
2106 * taken by the dialog window. But don't do this when getting
2107 * focus, we need the mouse-up event then. */
2108 if (!s_getting_focus)
2109 s_button_pending = -1;
2110
2111 return OK;
2112 }
Bram Moolenaar4231da42016-06-02 14:30:04 +02002113
2114#ifdef FEAT_TIMERS
2115 if (did_add_timer)
2116 {
2117 /* Need to recompute the waiting time. */
2118 remove_any_timer();
2119 break;
2120 }
2121#endif
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002122 }
2123 allow_scrollbar = FALSE;
2124 return FAIL;
2125}
2126
2127/*
2128 * Clear a rectangular region of the screen from text pos (row1, col1) to
2129 * (row2, col2) inclusive.
2130 */
2131 void
2132gui_mch_clear_block(
2133 int row1,
2134 int col1,
2135 int row2,
2136 int col2)
2137{
2138 RECT rc;
2139
2140 /*
2141 * Clear one extra pixel at the far right, for when bold characters have
2142 * spilled over to the window border.
2143 * Note: FillRect() excludes right and bottom of rectangle.
2144 */
2145 rc.left = FILL_X(col1);
2146 rc.top = FILL_Y(row1);
2147 rc.right = FILL_X(col2 + 1) + (col2 == Columns - 1);
2148 rc.bottom = FILL_Y(row2 + 1);
2149 clear_rect(&rc);
2150}
2151
2152/*
2153 * Clear the whole text window.
2154 */
2155 void
2156gui_mch_clear_all(void)
2157{
2158 RECT rc;
2159
2160 rc.left = 0;
2161 rc.top = 0;
2162 rc.right = Columns * gui.char_width + 2 * gui.border_width;
2163 rc.bottom = Rows * gui.char_height + 2 * gui.border_width;
2164 clear_rect(&rc);
2165}
2166/*
2167 * Menu stuff.
2168 */
2169
2170 void
2171gui_mch_enable_menu(int flag)
2172{
2173#ifdef FEAT_MENU
2174 SetMenu(s_hwnd, flag ? s_menuBar : NULL);
2175#endif
2176}
2177
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002178 void
2179gui_mch_set_menu_pos(
Bram Moolenaar1266d672017-02-01 13:43:36 +01002180 int x UNUSED,
2181 int y UNUSED,
2182 int w UNUSED,
2183 int h UNUSED)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002184{
2185 /* It will be in the right place anyway */
2186}
2187
2188#if defined(FEAT_MENU) || defined(PROTO)
2189/*
2190 * Make menu item hidden or not hidden
2191 */
2192 void
2193gui_mch_menu_hidden(
2194 vimmenu_T *menu,
2195 int hidden)
2196{
2197 /*
2198 * This doesn't do what we want. Hmm, just grey the menu items for now.
2199 */
2200 /*
2201 if (hidden)
2202 EnableMenuItem(s_menuBar, menu->id, MF_BYCOMMAND | MF_DISABLED);
2203 else
2204 EnableMenuItem(s_menuBar, menu->id, MF_BYCOMMAND | MF_ENABLED);
2205 */
2206 gui_mch_menu_grey(menu, hidden);
2207}
2208
2209/*
2210 * This is called after setting all the menus to grey/hidden or not.
2211 */
2212 void
2213gui_mch_draw_menubar(void)
2214{
2215 DrawMenuBar(s_hwnd);
2216}
2217#endif /*FEAT_MENU*/
2218
2219#ifndef PROTO
2220void
2221#ifdef VIMDLL
2222_export
2223#endif
2224_cdecl
2225SaveInst(HINSTANCE hInst)
2226{
2227 s_hinst = hInst;
2228}
2229#endif
2230
2231/*
2232 * Return the RGB value of a pixel as a long.
2233 */
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02002234 guicolor_T
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002235gui_mch_get_rgb(guicolor_T pixel)
2236{
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02002237 return (guicolor_T)((GetRValue(pixel) << 16) + (GetGValue(pixel) << 8)
2238 + GetBValue(pixel));
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002239}
2240
2241#if defined(FEAT_GUI_DIALOG) || defined(PROTO)
2242/* Convert pixels in X to dialog units */
2243 static WORD
2244PixelToDialogX(int numPixels)
2245{
2246 return (WORD)((numPixels * 4) / s_dlgfntwidth);
2247}
2248
2249/* Convert pixels in Y to dialog units */
2250 static WORD
2251PixelToDialogY(int numPixels)
2252{
2253 return (WORD)((numPixels * 8) / s_dlgfntheight);
2254}
2255
2256/* Return the width in pixels of the given text in the given DC. */
2257 static int
2258GetTextWidth(HDC hdc, char_u *str, int len)
2259{
2260 SIZE size;
2261
2262 GetTextExtentPoint(hdc, (LPCSTR)str, len, &size);
2263 return size.cx;
2264}
2265
2266#ifdef FEAT_MBYTE
2267/*
2268 * Return the width in pixels of the given text in the given DC, taking care
2269 * of 'encoding' to active codepage conversion.
2270 */
2271 static int
2272GetTextWidthEnc(HDC hdc, char_u *str, int len)
2273{
2274 SIZE size;
2275 WCHAR *wstr;
2276 int n;
2277 int wlen = len;
2278
2279 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
2280 {
2281 /* 'encoding' differs from active codepage: convert text and use wide
2282 * function */
2283 wstr = enc_to_utf16(str, &wlen);
2284 if (wstr != NULL)
2285 {
2286 n = GetTextExtentPointW(hdc, wstr, wlen, &size);
2287 vim_free(wstr);
2288 if (n)
2289 return size.cx;
2290 }
2291 }
2292
2293 return GetTextWidth(hdc, str, len);
2294}
2295#else
2296# define GetTextWidthEnc(h, s, l) GetTextWidth((h), (s), (l))
2297#endif
2298
Bram Moolenaar87f3d202016-12-01 20:18:50 +01002299static void get_work_area(RECT *spi_rect);
2300
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002301/*
2302 * A quick little routine that will center one window over another, handy for
Bram Moolenaar87f3d202016-12-01 20:18:50 +01002303 * dialog boxes. Taken from the Win32SDK samples and modified for multiple
2304 * monitors.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002305 */
2306 static BOOL
2307CenterWindow(
2308 HWND hwndChild,
2309 HWND hwndParent)
2310{
Bram Moolenaar87f3d202016-12-01 20:18:50 +01002311 HMONITOR mon;
2312 MONITORINFO moninfo;
2313 RECT rChild, rParent, rScreen;
2314 int wChild, hChild, wParent, hParent;
2315 int xNew, yNew;
2316 HDC hdc;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002317
2318 GetWindowRect(hwndChild, &rChild);
2319 wChild = rChild.right - rChild.left;
2320 hChild = rChild.bottom - rChild.top;
2321
2322 /* If Vim is minimized put the window in the middle of the screen. */
2323 if (hwndParent == NULL || IsMinimized(hwndParent))
Bram Moolenaar87f3d202016-12-01 20:18:50 +01002324 get_work_area(&rParent);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002325 else
2326 GetWindowRect(hwndParent, &rParent);
2327 wParent = rParent.right - rParent.left;
2328 hParent = rParent.bottom - rParent.top;
2329
Bram Moolenaar87f3d202016-12-01 20:18:50 +01002330 moninfo.cbSize = sizeof(MONITORINFO);
2331 mon = MonitorFromWindow(hwndChild, MONITOR_DEFAULTTOPRIMARY);
2332 if (mon != NULL && GetMonitorInfo(mon, &moninfo))
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002333 {
Bram Moolenaar87f3d202016-12-01 20:18:50 +01002334 rScreen = moninfo.rcWork;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002335 }
Bram Moolenaar87f3d202016-12-01 20:18:50 +01002336 else
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002337 {
Bram Moolenaar87f3d202016-12-01 20:18:50 +01002338 hdc = GetDC(hwndChild);
2339 rScreen.left = 0;
2340 rScreen.top = 0;
2341 rScreen.right = GetDeviceCaps(hdc, HORZRES);
2342 rScreen.bottom = GetDeviceCaps(hdc, VERTRES);
2343 ReleaseDC(hwndChild, hdc);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002344 }
2345
Bram Moolenaar87f3d202016-12-01 20:18:50 +01002346 xNew = rParent.left + ((wParent - wChild) / 2);
2347 if (xNew < rScreen.left)
2348 xNew = rScreen.left;
2349 else if ((xNew + wChild) > rScreen.right)
2350 xNew = rScreen.right - wChild;
2351
2352 yNew = rParent.top + ((hParent - hChild) / 2);
2353 if (yNew < rScreen.top)
2354 yNew = rScreen.top;
2355 else if ((yNew + hChild) > rScreen.bottom)
2356 yNew = rScreen.bottom - hChild;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002357
2358 return SetWindowPos(hwndChild, NULL, xNew, yNew, 0, 0,
2359 SWP_NOSIZE | SWP_NOZORDER);
2360}
2361#endif /* FEAT_GUI_DIALOG */
2362
2363void
2364gui_mch_activate_window(void)
2365{
2366 (void)SetActiveWindow(s_hwnd);
2367}
2368
2369#if defined(FEAT_TOOLBAR) || defined(PROTO)
2370 void
2371gui_mch_show_toolbar(int showit)
2372{
2373 if (s_toolbarhwnd == NULL)
2374 return;
2375
2376 if (showit)
2377 {
2378# ifdef FEAT_MBYTE
2379# ifndef TB_SETUNICODEFORMAT
2380 /* For older compilers. We assume this never changes. */
2381# define TB_SETUNICODEFORMAT 0x2005
2382# endif
2383 /* Enable/disable unicode support */
2384 int uu = (enc_codepage >= 0 && (int)GetACP() != enc_codepage);
2385 SendMessage(s_toolbarhwnd, TB_SETUNICODEFORMAT, (WPARAM)uu, (LPARAM)0);
2386# endif
2387 ShowWindow(s_toolbarhwnd, SW_SHOW);
2388 }
2389 else
2390 ShowWindow(s_toolbarhwnd, SW_HIDE);
2391}
2392
2393/* Then number of bitmaps is fixed. Exit is missing! */
2394#define TOOLBAR_BITMAP_COUNT 31
2395
2396#endif
2397
2398#if defined(FEAT_GUI_TABLINE) || defined(PROTO)
2399 static void
2400add_tabline_popup_menu_entry(HMENU pmenu, UINT item_id, char_u *item_text)
2401{
2402#ifdef FEAT_MBYTE
2403 WCHAR *wn = NULL;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002404
2405 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
2406 {
2407 /* 'encoding' differs from active codepage: convert menu name
2408 * and use wide function */
2409 wn = enc_to_utf16(item_text, NULL);
2410 if (wn != NULL)
2411 {
2412 MENUITEMINFOW infow;
2413
2414 infow.cbSize = sizeof(infow);
2415 infow.fMask = MIIM_TYPE | MIIM_ID;
2416 infow.wID = item_id;
2417 infow.fType = MFT_STRING;
2418 infow.dwTypeData = wn;
2419 infow.cch = (UINT)wcslen(wn);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02002420 InsertMenuItemW(pmenu, item_id, FALSE, &infow);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002421 vim_free(wn);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002422 }
2423 }
2424
2425 if (wn == NULL)
2426#endif
2427 {
2428 MENUITEMINFO info;
2429
2430 info.cbSize = sizeof(info);
2431 info.fMask = MIIM_TYPE | MIIM_ID;
2432 info.wID = item_id;
2433 info.fType = MFT_STRING;
2434 info.dwTypeData = (LPTSTR)item_text;
2435 info.cch = (UINT)STRLEN(item_text);
2436 InsertMenuItem(pmenu, item_id, FALSE, &info);
2437 }
2438}
2439
2440 static void
2441show_tabline_popup_menu(void)
2442{
2443 HMENU tab_pmenu;
2444 long rval;
2445 POINT pt;
2446
2447 /* When ignoring events don't show the menu. */
2448 if (hold_gui_events
2449# ifdef FEAT_CMDWIN
2450 || cmdwin_type != 0
2451# endif
2452 )
2453 return;
2454
2455 tab_pmenu = CreatePopupMenu();
2456 if (tab_pmenu == NULL)
2457 return;
2458
2459 if (first_tabpage->tp_next != NULL)
2460 add_tabline_popup_menu_entry(tab_pmenu,
2461 TABLINE_MENU_CLOSE, (char_u *)_("Close tab"));
2462 add_tabline_popup_menu_entry(tab_pmenu,
2463 TABLINE_MENU_NEW, (char_u *)_("New tab"));
2464 add_tabline_popup_menu_entry(tab_pmenu,
2465 TABLINE_MENU_OPEN, (char_u *)_("Open tab..."));
2466
2467 GetCursorPos(&pt);
2468 rval = TrackPopupMenuEx(tab_pmenu, TPM_RETURNCMD, pt.x, pt.y, s_tabhwnd,
2469 NULL);
2470
2471 DestroyMenu(tab_pmenu);
2472
2473 /* Add the string cmd into input buffer */
2474 if (rval > 0)
2475 {
2476 TCHITTESTINFO htinfo;
2477 int idx;
2478
2479 if (ScreenToClient(s_tabhwnd, &pt) == 0)
2480 return;
2481
2482 htinfo.pt.x = pt.x;
2483 htinfo.pt.y = pt.y;
2484 idx = TabCtrl_HitTest(s_tabhwnd, &htinfo);
2485 if (idx == -1)
2486 idx = 0;
2487 else
2488 idx += 1;
2489
2490 send_tabline_menu_event(idx, (int)rval);
2491 }
2492}
2493
2494/*
2495 * Show or hide the tabline.
2496 */
2497 void
2498gui_mch_show_tabline(int showit)
2499{
2500 if (s_tabhwnd == NULL)
2501 return;
2502
2503 if (!showit != !showing_tabline)
2504 {
2505 if (showit)
2506 ShowWindow(s_tabhwnd, SW_SHOW);
2507 else
2508 ShowWindow(s_tabhwnd, SW_HIDE);
2509 showing_tabline = showit;
2510 }
2511}
2512
2513/*
2514 * Return TRUE when tabline is displayed.
2515 */
2516 int
2517gui_mch_showing_tabline(void)
2518{
2519 return s_tabhwnd != NULL && showing_tabline;
2520}
2521
2522/*
2523 * Update the labels of the tabline.
2524 */
2525 void
2526gui_mch_update_tabline(void)
2527{
2528 tabpage_T *tp;
2529 TCITEM tie;
2530 int nr = 0;
2531 int curtabidx = 0;
2532 int tabadded = 0;
2533#ifdef FEAT_MBYTE
2534 static int use_unicode = FALSE;
2535 int uu;
2536 WCHAR *wstr = NULL;
2537#endif
2538
2539 if (s_tabhwnd == NULL)
2540 return;
2541
Bram Moolenaarcea912a2016-10-12 14:20:24 +02002542#ifdef FEAT_MBYTE
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002543# ifndef CCM_SETUNICODEFORMAT
2544 /* For older compilers. We assume this never changes. */
2545# define CCM_SETUNICODEFORMAT 0x2005
2546# endif
2547 uu = (enc_codepage >= 0 && (int)GetACP() != enc_codepage);
2548 if (uu != use_unicode)
2549 {
2550 /* Enable/disable unicode support */
2551 SendMessage(s_tabhwnd, CCM_SETUNICODEFORMAT, (WPARAM)uu, (LPARAM)0);
2552 use_unicode = uu;
2553 }
2554#endif
2555
2556 tie.mask = TCIF_TEXT;
2557 tie.iImage = -1;
2558
2559 /* Disable redraw for tab updates to eliminate O(N^2) draws. */
2560 SendMessage(s_tabhwnd, WM_SETREDRAW, (WPARAM)FALSE, 0);
2561
2562 /* Add a label for each tab page. They all contain the same text area. */
2563 for (tp = first_tabpage; tp != NULL; tp = tp->tp_next, ++nr)
2564 {
2565 if (tp == curtab)
2566 curtabidx = nr;
2567
2568 if (nr >= TabCtrl_GetItemCount(s_tabhwnd))
2569 {
2570 /* Add the tab */
2571 tie.pszText = "-Empty-";
2572 TabCtrl_InsertItem(s_tabhwnd, nr, &tie);
2573 tabadded = 1;
2574 }
2575
2576 get_tabline_label(tp, FALSE);
2577 tie.pszText = (LPSTR)NameBuff;
2578#ifdef FEAT_MBYTE
2579 wstr = NULL;
2580 if (use_unicode)
2581 {
2582 /* Need to go through Unicode. */
2583 wstr = enc_to_utf16(NameBuff, NULL);
2584 if (wstr != NULL)
2585 {
2586 TCITEMW tiw;
2587
2588 tiw.mask = TCIF_TEXT;
2589 tiw.iImage = -1;
2590 tiw.pszText = wstr;
2591 SendMessage(s_tabhwnd, TCM_SETITEMW, (WPARAM)nr, (LPARAM)&tiw);
2592 vim_free(wstr);
2593 }
2594 }
2595 if (wstr == NULL)
2596#endif
2597 {
2598 TabCtrl_SetItem(s_tabhwnd, nr, &tie);
2599 }
2600 }
2601
2602 /* Remove any old labels. */
2603 while (nr < TabCtrl_GetItemCount(s_tabhwnd))
2604 TabCtrl_DeleteItem(s_tabhwnd, nr);
2605
2606 if (!tabadded && TabCtrl_GetCurSel(s_tabhwnd) != curtabidx)
2607 TabCtrl_SetCurSel(s_tabhwnd, curtabidx);
2608
2609 /* Re-enable redraw and redraw. */
2610 SendMessage(s_tabhwnd, WM_SETREDRAW, (WPARAM)TRUE, 0);
2611 RedrawWindow(s_tabhwnd, NULL, NULL,
2612 RDW_ERASE | RDW_FRAME | RDW_INVALIDATE | RDW_ALLCHILDREN);
2613
2614 if (tabadded && TabCtrl_GetCurSel(s_tabhwnd) != curtabidx)
2615 TabCtrl_SetCurSel(s_tabhwnd, curtabidx);
2616}
2617
2618/*
2619 * Set the current tab to "nr". First tab is 1.
2620 */
2621 void
2622gui_mch_set_curtab(int nr)
2623{
2624 if (s_tabhwnd == NULL)
2625 return;
2626
2627 if (TabCtrl_GetCurSel(s_tabhwnd) != nr - 1)
2628 TabCtrl_SetCurSel(s_tabhwnd, nr - 1);
2629}
2630
2631#endif
2632
2633/*
2634 * ":simalt" command.
2635 */
2636 void
2637ex_simalt(exarg_T *eap)
2638{
Bram Moolenaar7a85b0f2017-04-22 15:17:40 +02002639 char_u *keys = eap->arg;
2640 int fill_typebuf = FALSE;
2641 char_u key_name[4];
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002642
2643 PostMessage(s_hwnd, WM_SYSCOMMAND, (WPARAM)SC_KEYMENU, (LPARAM)0);
2644 while (*keys)
2645 {
2646 if (*keys == '~')
2647 *keys = ' '; /* for showing system menu */
2648 PostMessage(s_hwnd, WM_CHAR, (WPARAM)*keys, (LPARAM)0);
2649 keys++;
Bram Moolenaar7a85b0f2017-04-22 15:17:40 +02002650 fill_typebuf = TRUE;
2651 }
2652 if (fill_typebuf)
2653 {
Bram Moolenaara21ccb72017-04-29 17:40:22 +02002654 /* Put a NOP in the typeahead buffer so that the message will get
Bram Moolenaar7a85b0f2017-04-22 15:17:40 +02002655 * processed. */
2656 key_name[0] = K_SPECIAL;
2657 key_name[1] = KS_EXTRA;
Bram Moolenaara21ccb72017-04-29 17:40:22 +02002658 key_name[2] = KE_NOP;
Bram Moolenaar7a85b0f2017-04-22 15:17:40 +02002659 key_name[3] = NUL;
2660 typebuf_was_filled = TRUE;
2661 (void)ins_typebuf(key_name, REMAP_NONE, 0, TRUE, FALSE);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002662 }
2663}
2664
2665/*
2666 * Create the find & replace dialogs.
2667 * You can't have both at once: ":find" when replace is showing, destroys
2668 * the replace dialog first, and the other way around.
2669 */
2670#ifdef MSWIN_FIND_REPLACE
2671 static void
2672initialise_findrep(char_u *initial_string)
2673{
2674 int wword = FALSE;
2675 int mcase = !p_ic;
2676 char_u *entry_text;
2677
2678 /* Get the search string to use. */
2679 entry_text = get_find_dialog_text(initial_string, &wword, &mcase);
2680
2681 s_findrep_struct.hwndOwner = s_hwnd;
2682 s_findrep_struct.Flags = FR_DOWN;
2683 if (mcase)
2684 s_findrep_struct.Flags |= FR_MATCHCASE;
2685 if (wword)
2686 s_findrep_struct.Flags |= FR_WHOLEWORD;
2687 if (entry_text != NULL && *entry_text != NUL)
2688 vim_strncpy((char_u *)s_findrep_struct.lpstrFindWhat, entry_text,
2689 s_findrep_struct.wFindWhatLen - 1);
2690 vim_free(entry_text);
2691}
2692#endif
2693
2694 static void
2695set_window_title(HWND hwnd, char *title)
2696{
2697#ifdef FEAT_MBYTE
2698 if (title != NULL && enc_codepage >= 0 && enc_codepage != (int)GetACP())
2699 {
2700 WCHAR *wbuf;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002701
2702 /* Convert the title from 'encoding' to UTF-16. */
2703 wbuf = (WCHAR *)enc_to_utf16((char_u *)title, NULL);
2704 if (wbuf != NULL)
2705 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +02002706 SetWindowTextW(hwnd, wbuf);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002707 vim_free(wbuf);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002708 }
Bram Moolenaarcea912a2016-10-12 14:20:24 +02002709 return;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002710 }
2711#endif
2712 (void)SetWindowText(hwnd, (LPCSTR)title);
2713}
2714
2715 void
2716gui_mch_find_dialog(exarg_T *eap)
2717{
2718#ifdef MSWIN_FIND_REPLACE
2719 if (s_findrep_msg != 0)
2720 {
2721 if (IsWindow(s_findrep_hwnd) && !s_findrep_is_find)
2722 DestroyWindow(s_findrep_hwnd);
2723
2724 if (!IsWindow(s_findrep_hwnd))
2725 {
2726 initialise_findrep(eap->arg);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02002727# ifdef FEAT_MBYTE
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002728 /* If the OS is Windows NT, and 'encoding' differs from active
2729 * codepage: convert text and use wide function. */
Bram Moolenaarcea912a2016-10-12 14:20:24 +02002730 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002731 {
2732 findrep_atow(&s_findrep_struct_w, &s_findrep_struct);
2733 s_findrep_hwnd = FindTextW(
2734 (LPFINDREPLACEW) &s_findrep_struct_w);
2735 }
2736 else
2737# endif
2738 s_findrep_hwnd = FindText((LPFINDREPLACE) &s_findrep_struct);
2739 }
2740
2741 set_window_title(s_findrep_hwnd,
2742 _("Find string (use '\\\\' to find a '\\')"));
2743 (void)SetFocus(s_findrep_hwnd);
2744
2745 s_findrep_is_find = TRUE;
2746 }
2747#endif
2748}
2749
2750
2751 void
2752gui_mch_replace_dialog(exarg_T *eap)
2753{
2754#ifdef MSWIN_FIND_REPLACE
2755 if (s_findrep_msg != 0)
2756 {
2757 if (IsWindow(s_findrep_hwnd) && s_findrep_is_find)
2758 DestroyWindow(s_findrep_hwnd);
2759
2760 if (!IsWindow(s_findrep_hwnd))
2761 {
2762 initialise_findrep(eap->arg);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02002763# ifdef FEAT_MBYTE
2764 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002765 {
2766 findrep_atow(&s_findrep_struct_w, &s_findrep_struct);
2767 s_findrep_hwnd = ReplaceTextW(
2768 (LPFINDREPLACEW) &s_findrep_struct_w);
2769 }
2770 else
2771# endif
2772 s_findrep_hwnd = ReplaceText(
2773 (LPFINDREPLACE) &s_findrep_struct);
2774 }
2775
2776 set_window_title(s_findrep_hwnd,
2777 _("Find & Replace (use '\\\\' to find a '\\')"));
2778 (void)SetFocus(s_findrep_hwnd);
2779
2780 s_findrep_is_find = FALSE;
2781 }
2782#endif
2783}
2784
2785
2786/*
2787 * Set visibility of the pointer.
2788 */
2789 void
2790gui_mch_mousehide(int hide)
2791{
2792 if (hide != gui.pointer_hidden)
2793 {
2794 ShowCursor(!hide);
2795 gui.pointer_hidden = hide;
2796 }
2797}
2798
2799#ifdef FEAT_MENU
2800 static void
2801gui_mch_show_popupmenu_at(vimmenu_T *menu, int x, int y)
2802{
2803 /* Unhide the mouse, we don't get move events here. */
2804 gui_mch_mousehide(FALSE);
2805
2806 (void)TrackPopupMenu(
2807 (HMENU)menu->submenu_id,
2808 TPM_LEFTALIGN | TPM_LEFTBUTTON,
2809 x, y,
2810 (int)0, /*reserved param*/
2811 s_hwnd,
2812 NULL);
2813 /*
2814 * NOTE: The pop-up menu can eat the mouse up event.
2815 * We deal with this in normal.c.
2816 */
2817}
2818#endif
2819
2820/*
2821 * Got a message when the system will go down.
2822 */
2823 static void
2824_OnEndSession(void)
2825{
2826 getout_preserve_modified(1);
2827}
2828
2829/*
2830 * Get this message when the user clicks on the cross in the top right corner
2831 * of a Windows95 window.
2832 */
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002833 static void
Bram Moolenaar1266d672017-02-01 13:43:36 +01002834_OnClose(HWND hwnd UNUSED)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002835{
2836 gui_shell_closed();
2837}
2838
2839/*
2840 * Get a message when the window is being destroyed.
2841 */
2842 static void
Bram Moolenaar1266d672017-02-01 13:43:36 +01002843_OnDestroy(HWND hwnd)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002844{
2845 if (!destroying)
2846 _OnClose(hwnd);
2847}
2848
2849 static void
2850_OnPaint(
2851 HWND hwnd)
2852{
2853 if (!IsMinimized(hwnd))
2854 {
2855 PAINTSTRUCT ps;
2856
2857 out_flush(); /* make sure all output has been processed */
2858 (void)BeginPaint(hwnd, &ps);
2859#if defined(FEAT_DIRECTX)
2860 if (IS_ENABLE_DIRECTX())
2861 DWriteContext_BeginDraw(s_dwc);
2862#endif
2863
2864#ifdef FEAT_MBYTE
2865 /* prevent multi-byte characters from misprinting on an invalid
2866 * rectangle */
2867 if (has_mbyte)
2868 {
2869 RECT rect;
2870
2871 GetClientRect(hwnd, &rect);
2872 ps.rcPaint.left = rect.left;
2873 ps.rcPaint.right = rect.right;
2874 }
2875#endif
2876
2877 if (!IsRectEmpty(&ps.rcPaint))
2878 {
2879#if defined(FEAT_DIRECTX)
2880 if (IS_ENABLE_DIRECTX())
2881 DWriteContext_BindDC(s_dwc, s_hdc, &ps.rcPaint);
2882#endif
2883 gui_redraw(ps.rcPaint.left, ps.rcPaint.top,
2884 ps.rcPaint.right - ps.rcPaint.left + 1,
2885 ps.rcPaint.bottom - ps.rcPaint.top + 1);
2886 }
2887
2888#if defined(FEAT_DIRECTX)
2889 if (IS_ENABLE_DIRECTX())
2890 DWriteContext_EndDraw(s_dwc);
2891#endif
2892 EndPaint(hwnd, &ps);
2893 }
2894}
2895
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002896 static void
2897_OnSize(
2898 HWND hwnd,
Bram Moolenaar1266d672017-02-01 13:43:36 +01002899 UINT state UNUSED,
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002900 int cx,
2901 int cy)
2902{
2903 if (!IsMinimized(hwnd))
2904 {
2905 gui_resize_shell(cx, cy);
2906
2907#ifdef FEAT_MENU
2908 /* Menu bar may wrap differently now */
2909 gui_mswin_get_menu_height(TRUE);
2910#endif
2911 }
2912}
2913
2914 static void
2915_OnSetFocus(
2916 HWND hwnd,
2917 HWND hwndOldFocus)
2918{
2919 gui_focus_change(TRUE);
2920 s_getting_focus = TRUE;
2921 (void)MyWindowProc(hwnd, WM_SETFOCUS, (WPARAM)hwndOldFocus, 0);
2922}
2923
2924 static void
2925_OnKillFocus(
2926 HWND hwnd,
2927 HWND hwndNewFocus)
2928{
2929 gui_focus_change(FALSE);
2930 s_getting_focus = FALSE;
2931 (void)MyWindowProc(hwnd, WM_KILLFOCUS, (WPARAM)hwndNewFocus, 0);
2932}
2933
2934/*
2935 * Get a message when the user switches back to vim
2936 */
2937 static LRESULT
2938_OnActivateApp(
2939 HWND hwnd,
2940 BOOL fActivate,
2941 DWORD dwThreadId)
2942{
2943 /* we call gui_focus_change() in _OnSetFocus() */
2944 /* gui_focus_change((int)fActivate); */
2945 return MyWindowProc(hwnd, WM_ACTIVATEAPP, fActivate, (DWORD)dwThreadId);
2946}
2947
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002948 void
2949gui_mch_destroy_scrollbar(scrollbar_T *sb)
2950{
2951 DestroyWindow(sb->id);
2952}
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002953
2954/*
2955 * Get current mouse coordinates in text window.
2956 */
2957 void
2958gui_mch_getmouse(int *x, int *y)
2959{
2960 RECT rct;
2961 POINT mp;
2962
2963 (void)GetWindowRect(s_textArea, &rct);
2964 (void)GetCursorPos((LPPOINT)&mp);
2965 *x = (int)(mp.x - rct.left);
2966 *y = (int)(mp.y - rct.top);
2967}
2968
2969/*
2970 * Move mouse pointer to character at (x, y).
2971 */
2972 void
2973gui_mch_setmouse(int x, int y)
2974{
2975 RECT rct;
2976
2977 (void)GetWindowRect(s_textArea, &rct);
2978 (void)SetCursorPos(x + gui.border_offset + rct.left,
2979 y + gui.border_offset + rct.top);
2980}
2981
2982 static void
2983gui_mswin_get_valid_dimensions(
2984 int w,
2985 int h,
2986 int *valid_w,
2987 int *valid_h)
2988{
2989 int base_width, base_height;
2990
2991 base_width = gui_get_base_width()
2992 + (GetSystemMetrics(SM_CXFRAME) +
2993 GetSystemMetrics(SM_CXPADDEDBORDER)) * 2;
2994 base_height = gui_get_base_height()
2995 + (GetSystemMetrics(SM_CYFRAME) +
2996 GetSystemMetrics(SM_CXPADDEDBORDER)) * 2
2997 + GetSystemMetrics(SM_CYCAPTION)
2998#ifdef FEAT_MENU
2999 + gui_mswin_get_menu_height(FALSE)
3000#endif
3001 ;
3002 *valid_w = base_width +
3003 ((w - base_width) / gui.char_width) * gui.char_width;
3004 *valid_h = base_height +
3005 ((h - base_height) / gui.char_height) * gui.char_height;
3006}
3007
3008 void
3009gui_mch_flash(int msec)
3010{
3011 RECT rc;
3012
3013 /*
3014 * Note: InvertRect() excludes right and bottom of rectangle.
3015 */
3016 rc.left = 0;
3017 rc.top = 0;
3018 rc.right = gui.num_cols * gui.char_width;
3019 rc.bottom = gui.num_rows * gui.char_height;
3020 InvertRect(s_hdc, &rc);
3021 gui_mch_flush(); /* make sure it's displayed */
3022
3023 ui_delay((long)msec, TRUE); /* wait for a few msec */
3024
3025 InvertRect(s_hdc, &rc);
3026}
3027
3028/*
3029 * Return flags used for scrolling.
3030 * The SW_INVALIDATE is required when part of the window is covered or
3031 * off-screen. Refer to MS KB Q75236.
3032 */
3033 static int
3034get_scroll_flags(void)
3035{
3036 HWND hwnd;
3037 RECT rcVim, rcOther, rcDest;
3038
3039 GetWindowRect(s_hwnd, &rcVim);
3040
3041 /* Check if the window is partly above or below the screen. We don't care
3042 * about partly left or right of the screen, it is not relevant when
3043 * scrolling up or down. */
3044 if (rcVim.top < 0 || rcVim.bottom > GetSystemMetrics(SM_CYFULLSCREEN))
3045 return SW_INVALIDATE;
3046
3047 /* Check if there is an window (partly) on top of us. */
3048 for (hwnd = s_hwnd; (hwnd = GetWindow(hwnd, GW_HWNDPREV)) != (HWND)0; )
3049 if (IsWindowVisible(hwnd))
3050 {
3051 GetWindowRect(hwnd, &rcOther);
3052 if (IntersectRect(&rcDest, &rcVim, &rcOther))
3053 return SW_INVALIDATE;
3054 }
3055 return 0;
3056}
3057
3058/*
3059 * On some Intel GPUs, the regions drawn just prior to ScrollWindowEx()
3060 * may not be scrolled out properly.
3061 * For gVim, when _OnScroll() is repeated, the character at the
3062 * previous cursor position may be left drawn after scroll.
3063 * The problem can be avoided by calling GetPixel() to get a pixel in
3064 * the region before ScrollWindowEx().
3065 */
3066 static void
3067intel_gpu_workaround(void)
3068{
3069 GetPixel(s_hdc, FILL_X(gui.col), FILL_Y(gui.row));
3070}
3071
3072/*
3073 * Delete the given number of lines from the given row, scrolling up any
3074 * text further down within the scroll region.
3075 */
3076 void
3077gui_mch_delete_lines(
3078 int row,
3079 int num_lines)
3080{
3081 RECT rc;
3082
3083 intel_gpu_workaround();
3084
3085 rc.left = FILL_X(gui.scroll_region_left);
3086 rc.right = FILL_X(gui.scroll_region_right + 1);
3087 rc.top = FILL_Y(row);
3088 rc.bottom = FILL_Y(gui.scroll_region_bot + 1);
3089
3090 ScrollWindowEx(s_textArea, 0, -num_lines * gui.char_height,
3091 &rc, &rc, NULL, NULL, get_scroll_flags());
3092
3093 UpdateWindow(s_textArea);
3094 /* This seems to be required to avoid the cursor disappearing when
3095 * scrolling such that the cursor ends up in the top-left character on
3096 * the screen... But why? (Webb) */
3097 /* It's probably fixed by disabling drawing the cursor while scrolling. */
3098 /* gui.cursor_is_valid = FALSE; */
3099
3100 gui_clear_block(gui.scroll_region_bot - num_lines + 1,
3101 gui.scroll_region_left,
3102 gui.scroll_region_bot, gui.scroll_region_right);
3103}
3104
3105/*
3106 * Insert the given number of lines before the given row, scrolling down any
3107 * following text within the scroll region.
3108 */
3109 void
3110gui_mch_insert_lines(
3111 int row,
3112 int num_lines)
3113{
3114 RECT rc;
3115
3116 intel_gpu_workaround();
3117
3118 rc.left = FILL_X(gui.scroll_region_left);
3119 rc.right = FILL_X(gui.scroll_region_right + 1);
3120 rc.top = FILL_Y(row);
3121 rc.bottom = FILL_Y(gui.scroll_region_bot + 1);
3122 /* The SW_INVALIDATE is required when part of the window is covered or
3123 * off-screen. How do we avoid it when it's not needed? */
3124 ScrollWindowEx(s_textArea, 0, num_lines * gui.char_height,
3125 &rc, &rc, NULL, NULL, get_scroll_flags());
3126
3127 UpdateWindow(s_textArea);
3128
3129 gui_clear_block(row, gui.scroll_region_left,
3130 row + num_lines - 1, gui.scroll_region_right);
3131}
3132
3133
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003134 void
Bram Moolenaar1266d672017-02-01 13:43:36 +01003135gui_mch_exit(int rc UNUSED)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003136{
3137#if defined(FEAT_DIRECTX)
3138 DWriteContext_Close(s_dwc);
3139 DWrite_Final();
3140 s_dwc = NULL;
3141#endif
3142
3143 ReleaseDC(s_textArea, s_hdc);
3144 DeleteObject(s_brush);
3145
3146#ifdef FEAT_TEAROFF
3147 /* Unload the tearoff bitmap */
3148 (void)DeleteObject((HGDIOBJ)s_htearbitmap);
3149#endif
3150
3151 /* Destroy our window (if we have one). */
3152 if (s_hwnd != NULL)
3153 {
3154 destroying = TRUE; /* ignore WM_DESTROY message now */
3155 DestroyWindow(s_hwnd);
3156 }
3157
3158#ifdef GLOBAL_IME
3159 global_ime_end();
3160#endif
3161}
3162
3163 static char_u *
3164logfont2name(LOGFONT lf)
3165{
3166 char *p;
3167 char *res;
3168 char *charset_name;
Bram Moolenaar7c1c6db2016-04-03 22:08:05 +02003169 char *quality_name;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003170 char *font_name = lf.lfFaceName;
3171
3172 charset_name = charset_id2name((int)lf.lfCharSet);
3173#ifdef FEAT_MBYTE
3174 /* Convert a font name from the current codepage to 'encoding'.
3175 * TODO: Use Wide APIs (including LOGFONTW) instead of ANSI APIs. */
3176 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
3177 {
3178 int len;
3179 acp_to_enc((char_u *)lf.lfFaceName, (int)strlen(lf.lfFaceName),
3180 (char_u **)&font_name, &len);
3181 }
3182#endif
Bram Moolenaar7c1c6db2016-04-03 22:08:05 +02003183 quality_name = quality_id2name((int)lf.lfQuality);
3184
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003185 res = (char *)alloc((unsigned)(strlen(font_name) + 20
3186 + (charset_name == NULL ? 0 : strlen(charset_name) + 2)));
3187 if (res != NULL)
3188 {
3189 p = res;
3190 /* make a normal font string out of the lf thing:*/
3191 sprintf((char *)p, "%s:h%d", font_name, pixels_to_points(
3192 lf.lfHeight < 0 ? -lf.lfHeight : lf.lfHeight, TRUE));
3193 while (*p)
3194 {
3195 if (*p == ' ')
3196 *p = '_';
3197 ++p;
3198 }
3199 if (lf.lfItalic)
3200 STRCAT(p, ":i");
3201 if (lf.lfWeight >= FW_BOLD)
3202 STRCAT(p, ":b");
3203 if (lf.lfUnderline)
3204 STRCAT(p, ":u");
3205 if (lf.lfStrikeOut)
3206 STRCAT(p, ":s");
3207 if (charset_name != NULL)
3208 {
3209 STRCAT(p, ":c");
3210 STRCAT(p, charset_name);
3211 }
Bram Moolenaar7c1c6db2016-04-03 22:08:05 +02003212 if (quality_name != NULL)
3213 {
3214 STRCAT(p, ":q");
3215 STRCAT(p, quality_name);
3216 }
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003217 }
3218
3219#ifdef FEAT_MBYTE
3220 if (font_name != lf.lfFaceName)
3221 vim_free(font_name);
3222#endif
3223 return (char_u *)res;
3224}
3225
3226
3227#ifdef FEAT_MBYTE_IME
3228/*
3229 * Set correct LOGFONT to IME. Use 'guifontwide' if available, otherwise use
3230 * 'guifont'
3231 */
3232 static void
3233update_im_font(void)
3234{
3235 LOGFONT lf_wide;
3236
3237 if (p_guifontwide != NULL && *p_guifontwide != NUL
3238 && gui.wide_font != NOFONT
3239 && GetObject((HFONT)gui.wide_font, sizeof(lf_wide), &lf_wide))
3240 norm_logfont = lf_wide;
3241 else
3242 norm_logfont = sub_logfont;
3243 im_set_font(&norm_logfont);
3244}
3245#endif
3246
3247#ifdef FEAT_MBYTE
3248/*
3249 * Handler of gui.wide_font (p_guifontwide) changed notification.
3250 */
3251 void
3252gui_mch_wide_font_changed(void)
3253{
3254 LOGFONT lf;
3255
3256# ifdef FEAT_MBYTE_IME
3257 update_im_font();
3258# endif
3259
3260 gui_mch_free_font(gui.wide_ital_font);
3261 gui.wide_ital_font = NOFONT;
3262 gui_mch_free_font(gui.wide_bold_font);
3263 gui.wide_bold_font = NOFONT;
3264 gui_mch_free_font(gui.wide_boldital_font);
3265 gui.wide_boldital_font = NOFONT;
3266
3267 if (gui.wide_font
3268 && GetObject((HFONT)gui.wide_font, sizeof(lf), &lf))
3269 {
3270 if (!lf.lfItalic)
3271 {
3272 lf.lfItalic = TRUE;
3273 gui.wide_ital_font = get_font_handle(&lf);
3274 lf.lfItalic = FALSE;
3275 }
3276 if (lf.lfWeight < FW_BOLD)
3277 {
3278 lf.lfWeight = FW_BOLD;
3279 gui.wide_bold_font = get_font_handle(&lf);
3280 if (!lf.lfItalic)
3281 {
3282 lf.lfItalic = TRUE;
3283 gui.wide_boldital_font = get_font_handle(&lf);
3284 }
3285 }
3286 }
3287}
3288#endif
3289
3290/*
3291 * Initialise vim to use the font with the given name.
3292 * Return FAIL if the font could not be loaded, OK otherwise.
3293 */
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003294 int
Bram Moolenaar1266d672017-02-01 13:43:36 +01003295gui_mch_init_font(char_u *font_name, int fontset UNUSED)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003296{
3297 LOGFONT lf;
3298 GuiFont font = NOFONT;
3299 char_u *p;
3300
3301 /* Load the font */
3302 if (get_logfont(&lf, font_name, NULL, TRUE) == OK)
3303 font = get_font_handle(&lf);
3304 if (font == NOFONT)
3305 return FAIL;
3306
3307 if (font_name == NULL)
3308 font_name = (char_u *)lf.lfFaceName;
3309#if defined(FEAT_MBYTE_IME) || defined(GLOBAL_IME)
3310 norm_logfont = lf;
3311 sub_logfont = lf;
3312#endif
3313#ifdef FEAT_MBYTE_IME
3314 update_im_font();
3315#endif
3316 gui_mch_free_font(gui.norm_font);
3317 gui.norm_font = font;
3318 current_font_height = lf.lfHeight;
3319 GetFontSize(font);
3320
3321 p = logfont2name(lf);
3322 if (p != NULL)
3323 {
3324 hl_set_font_name(p);
3325
3326 /* When setting 'guifont' to "*" replace it with the actual font name.
3327 * */
3328 if (STRCMP(font_name, "*") == 0 && STRCMP(p_guifont, "*") == 0)
3329 {
3330 vim_free(p_guifont);
3331 p_guifont = p;
3332 }
3333 else
3334 vim_free(p);
3335 }
3336
3337 gui_mch_free_font(gui.ital_font);
3338 gui.ital_font = NOFONT;
3339 gui_mch_free_font(gui.bold_font);
3340 gui.bold_font = NOFONT;
3341 gui_mch_free_font(gui.boldital_font);
3342 gui.boldital_font = NOFONT;
3343
3344 if (!lf.lfItalic)
3345 {
3346 lf.lfItalic = TRUE;
3347 gui.ital_font = get_font_handle(&lf);
3348 lf.lfItalic = FALSE;
3349 }
3350 if (lf.lfWeight < FW_BOLD)
3351 {
3352 lf.lfWeight = FW_BOLD;
3353 gui.bold_font = get_font_handle(&lf);
3354 if (!lf.lfItalic)
3355 {
3356 lf.lfItalic = TRUE;
3357 gui.boldital_font = get_font_handle(&lf);
3358 }
3359 }
3360
3361 return OK;
3362}
3363
3364#ifndef WPF_RESTORETOMAXIMIZED
3365# define WPF_RESTORETOMAXIMIZED 2 /* just in case someone doesn't have it */
3366#endif
3367
3368/*
3369 * Return TRUE if the GUI window is maximized, filling the whole screen.
3370 */
3371 int
3372gui_mch_maximized(void)
3373{
3374 WINDOWPLACEMENT wp;
3375
3376 wp.length = sizeof(WINDOWPLACEMENT);
3377 if (GetWindowPlacement(s_hwnd, &wp))
3378 return wp.showCmd == SW_SHOWMAXIMIZED
3379 || (wp.showCmd == SW_SHOWMINIMIZED
3380 && wp.flags == WPF_RESTORETOMAXIMIZED);
3381
3382 return 0;
3383}
3384
3385/*
Bram Moolenaar8ac44152017-11-09 18:33:29 +01003386 * Called when the font changed while the window is maximized or GO_KEEPWINSIZE
3387 * is set. Compute the new Rows and Columns. This is like resizing the
3388 * window.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003389 */
3390 void
3391gui_mch_newfont(void)
3392{
3393 RECT rect;
3394
3395 GetWindowRect(s_hwnd, &rect);
3396 if (win_socket_id == 0)
3397 {
3398 gui_resize_shell(rect.right - rect.left
3399 - (GetSystemMetrics(SM_CXFRAME) +
3400 GetSystemMetrics(SM_CXPADDEDBORDER)) * 2,
3401 rect.bottom - rect.top
3402 - (GetSystemMetrics(SM_CYFRAME) +
3403 GetSystemMetrics(SM_CXPADDEDBORDER)) * 2
3404 - GetSystemMetrics(SM_CYCAPTION)
3405#ifdef FEAT_MENU
3406 - gui_mswin_get_menu_height(FALSE)
3407#endif
3408 );
3409 }
3410 else
3411 {
3412 /* Inside another window, don't use the frame and border. */
3413 gui_resize_shell(rect.right - rect.left,
3414 rect.bottom - rect.top
3415#ifdef FEAT_MENU
3416 - gui_mswin_get_menu_height(FALSE)
3417#endif
3418 );
3419 }
3420}
3421
3422/*
3423 * Set the window title
3424 */
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003425 void
3426gui_mch_settitle(
3427 char_u *title,
Bram Moolenaar1266d672017-02-01 13:43:36 +01003428 char_u *icon UNUSED)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003429{
3430 set_window_title(s_hwnd, (title == NULL ? "VIM" : (char *)title));
3431}
3432
Bram Moolenaara6b7a082016-08-10 20:53:05 +02003433#if defined(FEAT_MOUSESHAPE) || defined(PROTO)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003434/* Table for shape IDCs. Keep in sync with the mshape_names[] table in
3435 * misc2.c! */
3436static LPCSTR mshape_idcs[] =
3437{
3438 IDC_ARROW, /* arrow */
3439 MAKEINTRESOURCE(0), /* blank */
3440 IDC_IBEAM, /* beam */
3441 IDC_SIZENS, /* updown */
3442 IDC_SIZENS, /* udsizing */
3443 IDC_SIZEWE, /* leftright */
3444 IDC_SIZEWE, /* lrsizing */
3445 IDC_WAIT, /* busy */
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003446 IDC_NO, /* no */
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003447 IDC_ARROW, /* crosshair */
3448 IDC_ARROW, /* hand1 */
3449 IDC_ARROW, /* hand2 */
3450 IDC_ARROW, /* pencil */
3451 IDC_ARROW, /* question */
3452 IDC_ARROW, /* right-arrow */
3453 IDC_UPARROW, /* up-arrow */
3454 IDC_ARROW /* last one */
3455};
3456
3457 void
3458mch_set_mouse_shape(int shape)
3459{
3460 LPCSTR idc;
3461
3462 if (shape == MSHAPE_HIDE)
3463 ShowCursor(FALSE);
3464 else
3465 {
3466 if (shape >= MSHAPE_NUMBERED)
3467 idc = IDC_ARROW;
3468 else
3469 idc = mshape_idcs[shape];
3470#ifdef SetClassLongPtr
3471 SetClassLongPtr(s_textArea, GCLP_HCURSOR, (__int3264)(LONG_PTR)LoadCursor(NULL, idc));
3472#else
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003473 SetClassLong(s_textArea, GCL_HCURSOR, (long_u)LoadCursor(NULL, idc));
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003474#endif
3475 if (!p_mh)
3476 {
3477 POINT mp;
3478
3479 /* Set the position to make it redrawn with the new shape. */
3480 (void)GetCursorPos((LPPOINT)&mp);
3481 (void)SetCursorPos(mp.x, mp.y);
3482 ShowCursor(TRUE);
3483 }
3484 }
3485}
3486#endif
3487
Bram Moolenaara6b7a082016-08-10 20:53:05 +02003488#if defined(FEAT_BROWSE) || defined(PROTO)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003489/*
3490 * The file browser exists in two versions: with "W" uses wide characters,
3491 * without "W" the current codepage. When FEAT_MBYTE is defined and on
3492 * Windows NT/2000/XP the "W" functions are used.
3493 */
3494
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003495# ifdef FEAT_MBYTE
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003496/*
3497 * Wide version of convert_filter().
3498 */
3499 static WCHAR *
3500convert_filterW(char_u *s)
3501{
3502 char_u *tmp;
3503 int len;
3504 WCHAR *res;
3505
3506 tmp = convert_filter(s);
3507 if (tmp == NULL)
3508 return NULL;
3509 len = (int)STRLEN(s) + 3;
3510 res = enc_to_utf16(tmp, &len);
3511 vim_free(tmp);
3512 return res;
3513}
3514
3515/*
3516 * Wide version of gui_mch_browse(). Keep in sync!
3517 */
3518 static char_u *
3519gui_mch_browseW(
3520 int saving,
3521 char_u *title,
3522 char_u *dflt,
3523 char_u *ext,
3524 char_u *initdir,
3525 char_u *filter)
3526{
3527 /* We always use the wide function. This means enc_to_utf16() must work,
3528 * otherwise it fails miserably! */
3529 OPENFILENAMEW fileStruct;
3530 WCHAR fileBuf[MAXPATHL];
3531 WCHAR *wp;
3532 int i;
3533 WCHAR *titlep = NULL;
3534 WCHAR *extp = NULL;
3535 WCHAR *initdirp = NULL;
3536 WCHAR *filterp;
3537 char_u *p;
3538
3539 if (dflt == NULL)
3540 fileBuf[0] = NUL;
3541 else
3542 {
3543 wp = enc_to_utf16(dflt, NULL);
3544 if (wp == NULL)
3545 fileBuf[0] = NUL;
3546 else
3547 {
3548 for (i = 0; wp[i] != NUL && i < MAXPATHL - 1; ++i)
3549 fileBuf[i] = wp[i];
3550 fileBuf[i] = NUL;
3551 vim_free(wp);
3552 }
3553 }
3554
3555 /* Convert the filter to Windows format. */
3556 filterp = convert_filterW(filter);
3557
3558 vim_memset(&fileStruct, 0, sizeof(OPENFILENAMEW));
Bram Moolenaarb04a98f2016-12-01 20:32:29 +01003559# ifdef OPENFILENAME_SIZE_VERSION_400W
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003560 /* be compatible with Windows NT 4.0 */
Bram Moolenaar89e375a2016-03-15 18:09:57 +01003561 fileStruct.lStructSize = OPENFILENAME_SIZE_VERSION_400W;
Bram Moolenaarb04a98f2016-12-01 20:32:29 +01003562# else
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003563 fileStruct.lStructSize = sizeof(fileStruct);
Bram Moolenaarb04a98f2016-12-01 20:32:29 +01003564# endif
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003565
3566 if (title != NULL)
3567 titlep = enc_to_utf16(title, NULL);
3568 fileStruct.lpstrTitle = titlep;
3569
3570 if (ext != NULL)
3571 extp = enc_to_utf16(ext, NULL);
3572 fileStruct.lpstrDefExt = extp;
3573
3574 fileStruct.lpstrFile = fileBuf;
3575 fileStruct.nMaxFile = MAXPATHL;
3576 fileStruct.lpstrFilter = filterp;
3577 fileStruct.hwndOwner = s_hwnd; /* main Vim window is owner*/
3578 /* has an initial dir been specified? */
3579 if (initdir != NULL && *initdir != NUL)
3580 {
3581 /* Must have backslashes here, no matter what 'shellslash' says */
3582 initdirp = enc_to_utf16(initdir, NULL);
3583 if (initdirp != NULL)
3584 {
3585 for (wp = initdirp; *wp != NUL; ++wp)
3586 if (*wp == '/')
3587 *wp = '\\';
3588 }
3589 fileStruct.lpstrInitialDir = initdirp;
3590 }
3591
3592 /*
3593 * TODO: Allow selection of multiple files. Needs another arg to this
3594 * function to ask for it, and need to use OFN_ALLOWMULTISELECT below.
3595 * Also, should we use OFN_FILEMUSTEXIST when opening? Vim can edit on
3596 * files that don't exist yet, so I haven't put it in. What about
3597 * OFN_PATHMUSTEXIST?
3598 * Don't use OFN_OVERWRITEPROMPT, Vim has its own ":confirm" dialog.
3599 */
3600 fileStruct.Flags = (OFN_NOCHANGEDIR | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY);
Bram Moolenaarb04a98f2016-12-01 20:32:29 +01003601# ifdef FEAT_SHORTCUT
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003602 if (curbuf->b_p_bin)
3603 fileStruct.Flags |= OFN_NODEREFERENCELINKS;
Bram Moolenaarb04a98f2016-12-01 20:32:29 +01003604# endif
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003605 if (saving)
3606 {
3607 if (!GetSaveFileNameW(&fileStruct))
3608 return NULL;
3609 }
3610 else
3611 {
3612 if (!GetOpenFileNameW(&fileStruct))
3613 return NULL;
3614 }
3615
3616 vim_free(filterp);
3617 vim_free(initdirp);
3618 vim_free(titlep);
3619 vim_free(extp);
3620
3621 /* Convert from UCS2 to 'encoding'. */
3622 p = utf16_to_enc(fileBuf, NULL);
3623 if (p != NULL)
3624 /* when out of memory we get garbage for non-ASCII chars */
3625 STRCPY(fileBuf, p);
3626 vim_free(p);
3627
3628 /* Give focus back to main window (when using MDI). */
3629 SetFocus(s_hwnd);
3630
3631 /* Shorten the file name if possible */
3632 return vim_strsave(shorten_fname1((char_u *)fileBuf));
3633}
3634# endif /* FEAT_MBYTE */
3635
3636
3637/*
3638 * Convert the string s to the proper format for a filter string by replacing
3639 * the \t and \n delimiters with \0.
3640 * Returns the converted string in allocated memory.
3641 *
3642 * Keep in sync with convert_filterW() above!
3643 */
3644 static char_u *
3645convert_filter(char_u *s)
3646{
3647 char_u *res;
3648 unsigned s_len = (unsigned)STRLEN(s);
3649 unsigned i;
3650
3651 res = alloc(s_len + 3);
3652 if (res != NULL)
3653 {
3654 for (i = 0; i < s_len; ++i)
3655 if (s[i] == '\t' || s[i] == '\n')
3656 res[i] = '\0';
3657 else
3658 res[i] = s[i];
3659 res[s_len] = NUL;
3660 /* Add two extra NULs to make sure it's properly terminated. */
3661 res[s_len + 1] = NUL;
3662 res[s_len + 2] = NUL;
3663 }
3664 return res;
3665}
3666
3667/*
3668 * Select a directory.
3669 */
3670 char_u *
3671gui_mch_browsedir(char_u *title, char_u *initdir)
3672{
3673 /* We fake this: Use a filter that doesn't select anything and a default
3674 * file name that won't be used. */
3675 return gui_mch_browse(0, title, (char_u *)_("Not Used"), NULL,
3676 initdir, (char_u *)_("Directory\t*.nothing\n"));
3677}
3678
3679/*
3680 * Pop open a file browser and return the file selected, in allocated memory,
3681 * or NULL if Cancel is hit.
3682 * saving - TRUE if the file will be saved to, FALSE if it will be opened.
3683 * title - Title message for the file browser dialog.
3684 * dflt - Default name of file.
3685 * ext - Default extension to be added to files without extensions.
3686 * initdir - directory in which to open the browser (NULL = current dir)
3687 * filter - Filter for matched files to choose from.
3688 *
3689 * Keep in sync with gui_mch_browseW() above!
3690 */
3691 char_u *
3692gui_mch_browse(
3693 int saving,
3694 char_u *title,
3695 char_u *dflt,
3696 char_u *ext,
3697 char_u *initdir,
3698 char_u *filter)
3699{
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003700# ifdef FEAT_MBYTE
3701 return gui_mch_browseW(saving, title, dflt, ext, initdir, filter);
3702# else
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003703 OPENFILENAME fileStruct;
3704 char_u fileBuf[MAXPATHL];
3705 char_u *initdirp = NULL;
3706 char_u *filterp;
3707 char_u *p;
3708
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003709 if (dflt == NULL)
3710 fileBuf[0] = NUL;
3711 else
3712 vim_strncpy(fileBuf, dflt, MAXPATHL - 1);
3713
3714 /* Convert the filter to Windows format. */
3715 filterp = convert_filter(filter);
3716
3717 vim_memset(&fileStruct, 0, sizeof(OPENFILENAME));
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003718# ifdef OPENFILENAME_SIZE_VERSION_400
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003719 /* be compatible with Windows NT 4.0 */
3720 fileStruct.lStructSize = OPENFILENAME_SIZE_VERSION_400;
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003721# else
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003722 fileStruct.lStructSize = sizeof(fileStruct);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003723# endif
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003724
3725 fileStruct.lpstrTitle = (LPSTR)title;
3726 fileStruct.lpstrDefExt = (LPSTR)ext;
3727
3728 fileStruct.lpstrFile = (LPSTR)fileBuf;
3729 fileStruct.nMaxFile = MAXPATHL;
3730 fileStruct.lpstrFilter = (LPSTR)filterp;
3731 fileStruct.hwndOwner = s_hwnd; /* main Vim window is owner*/
3732 /* has an initial dir been specified? */
3733 if (initdir != NULL && *initdir != NUL)
3734 {
3735 /* Must have backslashes here, no matter what 'shellslash' says */
3736 initdirp = vim_strsave(initdir);
3737 if (initdirp != NULL)
3738 for (p = initdirp; *p != NUL; ++p)
3739 if (*p == '/')
3740 *p = '\\';
3741 fileStruct.lpstrInitialDir = (LPSTR)initdirp;
3742 }
3743
3744 /*
3745 * TODO: Allow selection of multiple files. Needs another arg to this
3746 * function to ask for it, and need to use OFN_ALLOWMULTISELECT below.
3747 * Also, should we use OFN_FILEMUSTEXIST when opening? Vim can edit on
3748 * files that don't exist yet, so I haven't put it in. What about
3749 * OFN_PATHMUSTEXIST?
3750 * Don't use OFN_OVERWRITEPROMPT, Vim has its own ":confirm" dialog.
3751 */
3752 fileStruct.Flags = (OFN_NOCHANGEDIR | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003753# ifdef FEAT_SHORTCUT
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003754 if (curbuf->b_p_bin)
3755 fileStruct.Flags |= OFN_NODEREFERENCELINKS;
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003756# endif
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003757 if (saving)
3758 {
3759 if (!GetSaveFileName(&fileStruct))
3760 return NULL;
3761 }
3762 else
3763 {
3764 if (!GetOpenFileName(&fileStruct))
3765 return NULL;
3766 }
3767
3768 vim_free(filterp);
3769 vim_free(initdirp);
3770
3771 /* Give focus back to main window (when using MDI). */
3772 SetFocus(s_hwnd);
3773
3774 /* Shorten the file name if possible */
3775 return vim_strsave(shorten_fname1((char_u *)fileBuf));
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003776# endif
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003777}
3778#endif /* FEAT_BROWSE */
3779
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003780 static void
3781_OnDropFiles(
Bram Moolenaar1266d672017-02-01 13:43:36 +01003782 HWND hwnd UNUSED,
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003783 HDROP hDrop)
3784{
Bram Moolenaar4033c552017-09-16 20:54:51 +02003785#define BUFPATHLEN _MAX_PATH
3786#define DRAGQVAL 0xFFFFFFFF
3787#ifdef FEAT_MBYTE
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003788 WCHAR wszFile[BUFPATHLEN];
Bram Moolenaar4033c552017-09-16 20:54:51 +02003789#endif
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003790 char szFile[BUFPATHLEN];
3791 UINT cFiles = DragQueryFile(hDrop, DRAGQVAL, NULL, 0);
3792 UINT i;
3793 char_u **fnames;
3794 POINT pt;
3795 int_u modifiers = 0;
3796
3797 /* TRACE("_OnDropFiles: %d files dropped\n", cFiles); */
3798
3799 /* Obtain dropped position */
3800 DragQueryPoint(hDrop, &pt);
3801 MapWindowPoints(s_hwnd, s_textArea, &pt, 1);
3802
3803 reset_VIsual();
3804
3805 fnames = (char_u **)alloc(cFiles * sizeof(char_u *));
3806
3807 if (fnames != NULL)
3808 for (i = 0; i < cFiles; ++i)
3809 {
Bram Moolenaar4033c552017-09-16 20:54:51 +02003810#ifdef FEAT_MBYTE
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003811 if (DragQueryFileW(hDrop, i, wszFile, BUFPATHLEN) > 0)
3812 fnames[i] = utf16_to_enc(wszFile, NULL);
3813 else
Bram Moolenaar4033c552017-09-16 20:54:51 +02003814#endif
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003815 {
3816 DragQueryFile(hDrop, i, szFile, BUFPATHLEN);
3817 fnames[i] = vim_strsave((char_u *)szFile);
3818 }
3819 }
3820
3821 DragFinish(hDrop);
3822
3823 if (fnames != NULL)
3824 {
3825 if ((GetKeyState(VK_SHIFT) & 0x8000) != 0)
3826 modifiers |= MOUSE_SHIFT;
3827 if ((GetKeyState(VK_CONTROL) & 0x8000) != 0)
3828 modifiers |= MOUSE_CTRL;
3829 if ((GetKeyState(VK_MENU) & 0x8000) != 0)
3830 modifiers |= MOUSE_ALT;
3831
3832 gui_handle_drop(pt.x, pt.y, modifiers, fnames, cFiles);
3833
3834 s_need_activate = TRUE;
3835 }
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003836}
3837
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003838 static int
3839_OnScroll(
Bram Moolenaar1266d672017-02-01 13:43:36 +01003840 HWND hwnd UNUSED,
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003841 HWND hwndCtl,
3842 UINT code,
3843 int pos)
3844{
3845 static UINT prev_code = 0; /* code of previous call */
3846 scrollbar_T *sb, *sb_info;
3847 long val;
3848 int dragging = FALSE;
3849 int dont_scroll_save = dont_scroll;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003850 SCROLLINFO si;
3851
3852 si.cbSize = sizeof(si);
3853 si.fMask = SIF_POS;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003854
3855 sb = gui_mswin_find_scrollbar(hwndCtl);
3856 if (sb == NULL)
3857 return 0;
3858
3859 if (sb->wp != NULL) /* Left or right scrollbar */
3860 {
3861 /*
3862 * Careful: need to get scrollbar info out of first (left) scrollbar
3863 * for window, but keep real scrollbar too because we must pass it to
3864 * gui_drag_scrollbar().
3865 */
3866 sb_info = &sb->wp->w_scrollbars[0];
3867 }
3868 else /* Bottom scrollbar */
3869 sb_info = sb;
3870 val = sb_info->value;
3871
3872 switch (code)
3873 {
3874 case SB_THUMBTRACK:
3875 val = pos;
3876 dragging = TRUE;
3877 if (sb->scroll_shift > 0)
3878 val <<= sb->scroll_shift;
3879 break;
3880 case SB_LINEDOWN:
3881 val++;
3882 break;
3883 case SB_LINEUP:
3884 val--;
3885 break;
3886 case SB_PAGEDOWN:
3887 val += (sb_info->size > 2 ? sb_info->size - 2 : 1);
3888 break;
3889 case SB_PAGEUP:
3890 val -= (sb_info->size > 2 ? sb_info->size - 2 : 1);
3891 break;
3892 case SB_TOP:
3893 val = 0;
3894 break;
3895 case SB_BOTTOM:
3896 val = sb_info->max;
3897 break;
3898 case SB_ENDSCROLL:
3899 if (prev_code == SB_THUMBTRACK)
3900 {
3901 /*
3902 * "pos" only gives us 16-bit data. In case of large file,
3903 * use GetScrollPos() which returns 32-bit. Unfortunately it
3904 * is not valid while the scrollbar is being dragged.
3905 */
3906 val = GetScrollPos(hwndCtl, SB_CTL);
3907 if (sb->scroll_shift > 0)
3908 val <<= sb->scroll_shift;
3909 }
3910 break;
3911
3912 default:
3913 /* TRACE("Unknown scrollbar event %d\n", code); */
3914 return 0;
3915 }
3916 prev_code = code;
3917
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003918 si.nPos = (sb->scroll_shift > 0) ? val >> sb->scroll_shift : val;
3919 SetScrollInfo(hwndCtl, SB_CTL, &si, TRUE);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003920
3921 /*
3922 * When moving a vertical scrollbar, move the other vertical scrollbar too.
3923 */
3924 if (sb->wp != NULL)
3925 {
3926 scrollbar_T *sba = sb->wp->w_scrollbars;
3927 HWND id = sba[ (sb == sba + SBAR_LEFT) ? SBAR_RIGHT : SBAR_LEFT].id;
3928
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003929 SetScrollInfo(id, SB_CTL, &si, TRUE);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003930 }
3931
3932 /* Don't let us be interrupted here by another message. */
3933 s_busy_processing = TRUE;
3934
3935 /* When "allow_scrollbar" is FALSE still need to remember the new
3936 * position, but don't actually scroll by setting "dont_scroll". */
3937 dont_scroll = !allow_scrollbar;
3938
3939 gui_drag_scrollbar(sb, val, dragging);
3940
3941 s_busy_processing = FALSE;
3942 dont_scroll = dont_scroll_save;
3943
3944 return 0;
3945}
3946
3947
3948/*
3949 * Get command line arguments.
3950 * Use "prog" as the name of the program and "cmdline" as the arguments.
3951 * Copy the arguments to allocated memory.
3952 * Return the number of arguments (including program name).
3953 * Return pointers to the arguments in "argvp". Memory is allocated with
3954 * malloc(), use free() instead of vim_free().
3955 * Return pointer to buffer in "tofree".
3956 * Returns zero when out of memory.
3957 */
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003958 int
3959get_cmd_args(char *prog, char *cmdline, char ***argvp, char **tofree)
3960{
3961 int i;
3962 char *p;
3963 char *progp;
3964 char *pnew = NULL;
3965 char *newcmdline;
3966 int inquote;
3967 int argc;
3968 char **argv = NULL;
3969 int round;
3970
3971 *tofree = NULL;
3972
3973#ifdef FEAT_MBYTE
3974 /* Try using the Unicode version first, it takes care of conversion when
3975 * 'encoding' is changed. */
3976 argc = get_cmd_argsW(&argv);
3977 if (argc != 0)
3978 goto done;
3979#endif
3980
3981 /* Handle the program name. Remove the ".exe" extension, and find the 1st
3982 * non-space. */
3983 p = strrchr(prog, '.');
3984 if (p != NULL)
3985 *p = NUL;
3986 for (progp = prog; *progp == ' '; ++progp)
3987 ;
3988
3989 /* The command line is copied to allocated memory, so that we can change
3990 * it. Add the size of the string, the separating NUL and a terminating
3991 * NUL. */
3992 newcmdline = malloc(STRLEN(cmdline) + STRLEN(progp) + 2);
3993 if (newcmdline == NULL)
3994 return 0;
3995
3996 /*
3997 * First round: count the number of arguments ("pnew" == NULL).
3998 * Second round: produce the arguments.
3999 */
4000 for (round = 1; round <= 2; ++round)
4001 {
4002 /* First argument is the program name. */
4003 if (pnew != NULL)
4004 {
4005 argv[0] = pnew;
4006 strcpy(pnew, progp);
4007 pnew += strlen(pnew);
4008 *pnew++ = NUL;
4009 }
4010
4011 /*
4012 * Isolate each argument and put it in argv[].
4013 */
4014 p = cmdline;
4015 argc = 1;
4016 while (*p != NUL)
4017 {
4018 inquote = FALSE;
4019 if (pnew != NULL)
4020 argv[argc] = pnew;
4021 ++argc;
4022 while (*p != NUL && (inquote || (*p != ' ' && *p != '\t')))
4023 {
4024 /* Backslashes are only special when followed by a double
4025 * quote. */
4026 i = (int)strspn(p, "\\");
4027 if (p[i] == '"')
4028 {
4029 /* Halve the number of backslashes. */
4030 if (i > 1 && pnew != NULL)
4031 {
4032 vim_memset(pnew, '\\', i / 2);
4033 pnew += i / 2;
4034 }
4035
4036 /* Even nr of backslashes toggles quoting, uneven copies
4037 * the double quote. */
4038 if ((i & 1) == 0)
4039 inquote = !inquote;
4040 else if (pnew != NULL)
4041 *pnew++ = '"';
4042 p += i + 1;
4043 }
4044 else if (i > 0)
4045 {
4046 /* Copy span of backslashes unmodified. */
4047 if (pnew != NULL)
4048 {
4049 vim_memset(pnew, '\\', i);
4050 pnew += i;
4051 }
4052 p += i;
4053 }
4054 else
4055 {
4056 if (pnew != NULL)
4057 *pnew++ = *p;
4058#ifdef FEAT_MBYTE
4059 /* Can't use mb_* functions, because 'encoding' is not
4060 * initialized yet here. */
4061 if (IsDBCSLeadByte(*p))
4062 {
4063 ++p;
4064 if (pnew != NULL)
4065 *pnew++ = *p;
4066 }
4067#endif
4068 ++p;
4069 }
4070 }
4071
4072 if (pnew != NULL)
4073 *pnew++ = NUL;
4074 while (*p == ' ' || *p == '\t')
4075 ++p; /* advance until a non-space */
4076 }
4077
4078 if (round == 1)
4079 {
4080 argv = (char **)malloc((argc + 1) * sizeof(char *));
4081 if (argv == NULL )
4082 {
4083 free(newcmdline);
4084 return 0; /* malloc error */
4085 }
4086 pnew = newcmdline;
4087 *tofree = newcmdline;
4088 }
4089 }
4090
4091#ifdef FEAT_MBYTE
4092done:
4093#endif
4094 argv[argc] = NULL; /* NULL-terminated list */
4095 *argvp = argv;
4096 return argc;
4097}
Bram Moolenaar071d4272004-06-13 20:20:40 +00004098
4099#ifdef FEAT_XPM_W32
4100# include "xpm_w32.h"
4101#endif
4102
4103#ifdef PROTO
4104# define WINAPI
4105#endif
4106
4107#ifdef __MINGW32__
4108/*
4109 * Add a lot of missing defines.
4110 * They are not always missing, we need the #ifndef's.
4111 */
4112# ifndef _cdecl
4113# define _cdecl
4114# endif
4115# ifndef IsMinimized
4116# define IsMinimized(hwnd) IsIconic(hwnd)
4117# endif
4118# ifndef IsMaximized
4119# define IsMaximized(hwnd) IsZoomed(hwnd)
4120# endif
4121# ifndef SelectFont
4122# define SelectFont(hdc, hfont) ((HFONT)SelectObject((hdc), (HGDIOBJ)(HFONT)(hfont)))
4123# endif
4124# ifndef GetStockBrush
4125# define GetStockBrush(i) ((HBRUSH)GetStockObject(i))
4126# endif
4127# ifndef DeleteBrush
4128# define DeleteBrush(hbr) DeleteObject((HGDIOBJ)(HBRUSH)(hbr))
4129# endif
4130
4131# ifndef HANDLE_WM_RBUTTONDBLCLK
4132# define HANDLE_WM_RBUTTONDBLCLK(hwnd, wParam, lParam, fn) \
4133 ((fn)((hwnd), TRUE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
4134# endif
4135# ifndef HANDLE_WM_MBUTTONUP
4136# define HANDLE_WM_MBUTTONUP(hwnd, wParam, lParam, fn) \
4137 ((fn)((hwnd), (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
4138# endif
4139# ifndef HANDLE_WM_MBUTTONDBLCLK
4140# define HANDLE_WM_MBUTTONDBLCLK(hwnd, wParam, lParam, fn) \
4141 ((fn)((hwnd), TRUE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
4142# endif
4143# ifndef HANDLE_WM_LBUTTONDBLCLK
4144# define HANDLE_WM_LBUTTONDBLCLK(hwnd, wParam, lParam, fn) \
4145 ((fn)((hwnd), TRUE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
4146# endif
4147# ifndef HANDLE_WM_RBUTTONDOWN
4148# define HANDLE_WM_RBUTTONDOWN(hwnd, wParam, lParam, fn) \
4149 ((fn)((hwnd), FALSE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
4150# endif
4151# ifndef HANDLE_WM_MOUSEMOVE
4152# define HANDLE_WM_MOUSEMOVE(hwnd, wParam, lParam, fn) \
4153 ((fn)((hwnd), (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
4154# endif
4155# ifndef HANDLE_WM_RBUTTONUP
4156# define HANDLE_WM_RBUTTONUP(hwnd, wParam, lParam, fn) \
4157 ((fn)((hwnd), (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
4158# endif
4159# ifndef HANDLE_WM_MBUTTONDOWN
4160# define HANDLE_WM_MBUTTONDOWN(hwnd, wParam, lParam, fn) \
4161 ((fn)((hwnd), FALSE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
4162# endif
4163# ifndef HANDLE_WM_LBUTTONUP
4164# define HANDLE_WM_LBUTTONUP(hwnd, wParam, lParam, fn) \
4165 ((fn)((hwnd), (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
4166# endif
4167# ifndef HANDLE_WM_LBUTTONDOWN
4168# define HANDLE_WM_LBUTTONDOWN(hwnd, wParam, lParam, fn) \
4169 ((fn)((hwnd), FALSE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
4170# endif
4171# ifndef HANDLE_WM_SYSCHAR
4172# define HANDLE_WM_SYSCHAR(hwnd, wParam, lParam, fn) \
4173 ((fn)((hwnd), (TCHAR)(wParam), (int)(short)LOWORD(lParam)), 0L)
4174# endif
4175# ifndef HANDLE_WM_ACTIVATEAPP
4176# define HANDLE_WM_ACTIVATEAPP(hwnd, wParam, lParam, fn) \
4177 ((fn)((hwnd), (BOOL)(wParam), (DWORD)(lParam)), 0L)
4178# endif
4179# ifndef HANDLE_WM_WINDOWPOSCHANGING
4180# define HANDLE_WM_WINDOWPOSCHANGING(hwnd, wParam, lParam, fn) \
4181 (LRESULT)(DWORD)(BOOL)(fn)((hwnd), (LPWINDOWPOS)(lParam))
4182# endif
4183# ifndef HANDLE_WM_VSCROLL
4184# define HANDLE_WM_VSCROLL(hwnd, wParam, lParam, fn) \
4185 ((fn)((hwnd), (HWND)(lParam), (UINT)(LOWORD(wParam)), (int)(short)HIWORD(wParam)), 0L)
4186# endif
4187# ifndef HANDLE_WM_SETFOCUS
4188# define HANDLE_WM_SETFOCUS(hwnd, wParam, lParam, fn) \
4189 ((fn)((hwnd), (HWND)(wParam)), 0L)
4190# endif
4191# ifndef HANDLE_WM_KILLFOCUS
4192# define HANDLE_WM_KILLFOCUS(hwnd, wParam, lParam, fn) \
4193 ((fn)((hwnd), (HWND)(wParam)), 0L)
4194# endif
4195# ifndef HANDLE_WM_HSCROLL
4196# define HANDLE_WM_HSCROLL(hwnd, wParam, lParam, fn) \
4197 ((fn)((hwnd), (HWND)(lParam), (UINT)(LOWORD(wParam)), (int)(short)HIWORD(wParam)), 0L)
4198# endif
4199# ifndef HANDLE_WM_DROPFILES
4200# define HANDLE_WM_DROPFILES(hwnd, wParam, lParam, fn) \
4201 ((fn)((hwnd), (HDROP)(wParam)), 0L)
4202# endif
4203# ifndef HANDLE_WM_CHAR
4204# define HANDLE_WM_CHAR(hwnd, wParam, lParam, fn) \
4205 ((fn)((hwnd), (TCHAR)(wParam), (int)(short)LOWORD(lParam)), 0L)
4206# endif
4207# ifndef HANDLE_WM_SYSDEADCHAR
4208# define HANDLE_WM_SYSDEADCHAR(hwnd, wParam, lParam, fn) \
4209 ((fn)((hwnd), (TCHAR)(wParam), (int)(short)LOWORD(lParam)), 0L)
4210# endif
4211# ifndef HANDLE_WM_DEADCHAR
4212# define HANDLE_WM_DEADCHAR(hwnd, wParam, lParam, fn) \
4213 ((fn)((hwnd), (TCHAR)(wParam), (int)(short)LOWORD(lParam)), 0L)
4214# endif
4215#endif /* __MINGW32__ */
4216
4217
4218/* Some parameters for tearoff menus. All in pixels. */
4219#define TEAROFF_PADDING_X 2
4220#define TEAROFF_BUTTON_PAD_X 8
4221#define TEAROFF_MIN_WIDTH 200
4222#define TEAROFF_SUBMENU_LABEL ">>"
4223#define TEAROFF_COLUMN_PADDING 3 // # spaces to pad column with.
4224
4225
4226/* For the Intellimouse: */
4227#ifndef WM_MOUSEWHEEL
4228#define WM_MOUSEWHEEL 0x20a
4229#endif
4230
4231
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01004232#ifdef FEAT_BEVAL_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00004233# define ID_BEVAL_TOOLTIP 200
4234# define BEVAL_TEXT_LEN MAXPATHL
4235
Bram Moolenaar167632f2010-05-26 21:42:54 +02004236#if (defined(_MSC_VER) && _MSC_VER < 1300) || !defined(MAXULONG_PTR)
Bram Moolenaar446cb832008-06-24 21:56:24 +00004237/* Work around old versions of basetsd.h which wrongly declares
4238 * UINT_PTR as unsigned long. */
Bram Moolenaar167632f2010-05-26 21:42:54 +02004239# undef UINT_PTR
Bram Moolenaar8424a622006-04-19 21:23:36 +00004240# define UINT_PTR UINT
4241#endif
Bram Moolenaareb3593b2006-04-22 22:33:57 +00004242
Bram Moolenaard25c16e2016-01-29 22:13:30 +01004243static void make_tooltip(BalloonEval *beval, char *text, POINT pt);
4244static void delete_tooltip(BalloonEval *beval);
4245static VOID CALLBACK BevalTimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime);
Bram Moolenaareb3593b2006-04-22 22:33:57 +00004246
Bram Moolenaar071d4272004-06-13 20:20:40 +00004247static BalloonEval *cur_beval = NULL;
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004248static UINT_PTR BevalTimerId = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004249static DWORD LastActivity = 0;
Bram Moolenaar45360022005-07-21 21:08:21 +00004250
Bram Moolenaar82881492012-11-20 16:53:39 +01004251
4252/* cproto fails on missing include files */
4253#ifndef PROTO
4254
Bram Moolenaar45360022005-07-21 21:08:21 +00004255/*
4256 * excerpts from headers since this may not be presented
Bram Moolenaar551dbcc2006-04-25 22:13:59 +00004257 * in the extremely old compilers
Bram Moolenaar45360022005-07-21 21:08:21 +00004258 */
Bram Moolenaar82881492012-11-20 16:53:39 +01004259# include <pshpack1.h>
4260
4261#endif
Bram Moolenaar45360022005-07-21 21:08:21 +00004262
4263typedef struct _DllVersionInfo
4264{
4265 DWORD cbSize;
4266 DWORD dwMajorVersion;
4267 DWORD dwMinorVersion;
4268 DWORD dwBuildNumber;
4269 DWORD dwPlatformID;
4270} DLLVERSIONINFO;
4271
Bram Moolenaar82881492012-11-20 16:53:39 +01004272#ifndef PROTO
4273# include <poppack.h>
4274#endif
Bram Moolenaar281daf62009-12-24 15:11:40 +00004275
Bram Moolenaar45360022005-07-21 21:08:21 +00004276typedef struct tagTOOLINFOA_NEW
4277{
4278 UINT cbSize;
4279 UINT uFlags;
4280 HWND hwnd;
Bram Moolenaar281daf62009-12-24 15:11:40 +00004281 UINT_PTR uId;
Bram Moolenaar45360022005-07-21 21:08:21 +00004282 RECT rect;
4283 HINSTANCE hinst;
4284 LPSTR lpszText;
4285 LPARAM lParam;
4286} TOOLINFO_NEW;
4287
4288typedef struct tagNMTTDISPINFO_NEW
4289{
4290 NMHDR hdr;
Bram Moolenaar281daf62009-12-24 15:11:40 +00004291 LPSTR lpszText;
Bram Moolenaar45360022005-07-21 21:08:21 +00004292 char szText[80];
4293 HINSTANCE hinst;
4294 UINT uFlags;
4295 LPARAM lParam;
4296} NMTTDISPINFO_NEW;
4297
Bram Moolenaar45360022005-07-21 21:08:21 +00004298typedef HRESULT (WINAPI* DLLGETVERSIONPROC)(DLLVERSIONINFO *);
4299#ifndef TTM_SETMAXTIPWIDTH
Bram Moolenaarf9393ef2006-04-24 19:47:27 +00004300# define TTM_SETMAXTIPWIDTH (WM_USER+24)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004301#endif
4302
Bram Moolenaar45360022005-07-21 21:08:21 +00004303#ifndef TTF_DI_SETITEM
Bram Moolenaarf9393ef2006-04-24 19:47:27 +00004304# define TTF_DI_SETITEM 0x8000
Bram Moolenaar45360022005-07-21 21:08:21 +00004305#endif
4306
4307#ifndef TTN_GETDISPINFO
Bram Moolenaarf9393ef2006-04-24 19:47:27 +00004308# define TTN_GETDISPINFO (TTN_FIRST - 0)
Bram Moolenaar45360022005-07-21 21:08:21 +00004309#endif
4310
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01004311#endif /* defined(FEAT_BEVAL_GUI) */
Bram Moolenaar45360022005-07-21 21:08:21 +00004312
Bram Moolenaar2c7a7632007-05-10 18:19:11 +00004313#if defined(FEAT_TOOLBAR) || defined(FEAT_GUI_TABLINE)
4314/* Older MSVC compilers don't have LPNMTTDISPINFO[AW] thus we need to define
4315 * it here if LPNMTTDISPINFO isn't defined.
4316 * MingW doesn't define LPNMTTDISPINFO but typedefs it. Thus we need to check
4317 * _MSC_VER. */
4318# if !defined(LPNMTTDISPINFO) && defined(_MSC_VER)
4319typedef struct tagNMTTDISPINFOA {
4320 NMHDR hdr;
4321 LPSTR lpszText;
4322 char szText[80];
4323 HINSTANCE hinst;
4324 UINT uFlags;
4325 LPARAM lParam;
4326} NMTTDISPINFOA, *LPNMTTDISPINFOA;
4327# define LPNMTTDISPINFO LPNMTTDISPINFOA
4328
4329# ifdef FEAT_MBYTE
4330typedef struct tagNMTTDISPINFOW {
4331 NMHDR hdr;
4332 LPWSTR lpszText;
4333 WCHAR szText[80];
4334 HINSTANCE hinst;
4335 UINT uFlags;
4336 LPARAM lParam;
4337} NMTTDISPINFOW, *LPNMTTDISPINFOW;
4338# endif
4339# endif
4340#endif
4341
Bram Moolenaarf9393ef2006-04-24 19:47:27 +00004342#ifndef TTN_GETDISPINFOW
4343# define TTN_GETDISPINFOW (TTN_FIRST - 10)
4344#endif
4345
Bram Moolenaar071d4272004-06-13 20:20:40 +00004346/* Local variables: */
4347
4348#ifdef FEAT_MENU
4349static UINT s_menu_id = 100;
Bram Moolenaar786989b2010-10-27 12:15:33 +02004350#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004351
4352/*
4353 * Use the system font for dialogs and tear-off menus. Remove this line to
4354 * use DLG_FONT_NAME.
4355 */
Bram Moolenaar786989b2010-10-27 12:15:33 +02004356#define USE_SYSMENU_FONT
Bram Moolenaar071d4272004-06-13 20:20:40 +00004357
4358#define VIM_NAME "vim"
4359#define VIM_CLASS "Vim"
4360#define VIM_CLASSW L"Vim"
4361
4362/* Initial size for the dialog template. For gui_mch_dialog() it's fixed,
4363 * thus there should be room for every dialog. For tearoffs it's made bigger
4364 * when needed. */
4365#define DLG_ALLOC_SIZE 16 * 1024
4366
4367/*
4368 * stuff for dialogs, menus, tearoffs etc.
4369 */
4370static LRESULT APIENTRY dialog_callback(HWND, UINT, WPARAM, LPARAM);
Bram Moolenaar065bbac2016-02-20 13:08:46 +01004371#ifdef FEAT_TEAROFF
Bram Moolenaar071d4272004-06-13 20:20:40 +00004372static LRESULT APIENTRY tearoff_callback(HWND, UINT, WPARAM, LPARAM);
Bram Moolenaar065bbac2016-02-20 13:08:46 +01004373#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004374static PWORD
4375add_dialog_element(
4376 PWORD p,
4377 DWORD lStyle,
4378 WORD x,
4379 WORD y,
4380 WORD w,
4381 WORD h,
4382 WORD Id,
4383 WORD clss,
4384 const char *caption);
4385static LPWORD lpwAlign(LPWORD);
Bram Moolenaar6edeaf32017-09-26 14:46:04 +02004386static int nCopyAnsiToWideChar(LPWORD, LPSTR, BOOL);
Bram Moolenaar065bbac2016-02-20 13:08:46 +01004387#if defined(FEAT_MENU) && defined(FEAT_TEAROFF)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004388static void gui_mch_tearoff(char_u *title, vimmenu_T *menu, int initX, int initY);
Bram Moolenaar065bbac2016-02-20 13:08:46 +01004389#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004390static void get_dialog_font_metrics(void);
4391
4392static int dialog_default_button = -1;
4393
4394/* Intellimouse support */
4395static int mouse_scroll_lines = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004396
4397static int s_usenewlook; /* emulate W95/NT4 non-bold dialogs */
4398#ifdef FEAT_TOOLBAR
4399static void initialise_toolbar(void);
Bram Moolenaar5f919ee2013-07-21 17:46:43 +02004400static LRESULT CALLBACK toolbar_wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004401static int get_toolbar_bitmap(vimmenu_T *menu);
4402#endif
4403
Bram Moolenaar3991dab2006-03-27 17:01:56 +00004404#ifdef FEAT_GUI_TABLINE
4405static void initialise_tabline(void);
Bram Moolenaar5f919ee2013-07-21 17:46:43 +02004406static LRESULT CALLBACK tabline_wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
Bram Moolenaar3991dab2006-03-27 17:01:56 +00004407#endif
4408
Bram Moolenaar071d4272004-06-13 20:20:40 +00004409#ifdef FEAT_MBYTE_IME
4410static LRESULT _OnImeComposition(HWND hwnd, WPARAM dbcs, LPARAM param);
4411static char_u *GetResultStr(HWND hwnd, int GCS, int *lenp);
4412#endif
4413#if defined(FEAT_MBYTE_IME) && defined(DYNAMIC_IME)
4414# ifdef NOIME
4415typedef struct tagCOMPOSITIONFORM {
4416 DWORD dwStyle;
4417 POINT ptCurrentPos;
4418 RECT rcArea;
4419} COMPOSITIONFORM, *PCOMPOSITIONFORM, NEAR *NPCOMPOSITIONFORM, FAR *LPCOMPOSITIONFORM;
4420typedef HANDLE HIMC;
4421# endif
4422
Bram Moolenaard857f0e2005-06-21 22:37:39 +00004423static HINSTANCE hLibImm = NULL;
4424static LONG (WINAPI *pImmGetCompositionStringA)(HIMC, DWORD, LPVOID, DWORD);
4425static LONG (WINAPI *pImmGetCompositionStringW)(HIMC, DWORD, LPVOID, DWORD);
4426static HIMC (WINAPI *pImmGetContext)(HWND);
4427static HIMC (WINAPI *pImmAssociateContext)(HWND, HIMC);
4428static BOOL (WINAPI *pImmReleaseContext)(HWND, HIMC);
4429static BOOL (WINAPI *pImmGetOpenStatus)(HIMC);
4430static BOOL (WINAPI *pImmSetOpenStatus)(HIMC, BOOL);
4431static BOOL (WINAPI *pImmGetCompositionFont)(HIMC, LPLOGFONTA);
4432static BOOL (WINAPI *pImmSetCompositionFont)(HIMC, LPLOGFONTA);
4433static BOOL (WINAPI *pImmSetCompositionWindow)(HIMC, LPCOMPOSITIONFORM);
4434static BOOL (WINAPI *pImmGetConversionStatus)(HIMC, LPDWORD, LPDWORD);
Bram Moolenaarca003e12006-03-17 23:19:38 +00004435static BOOL (WINAPI *pImmSetConversionStatus)(HIMC, DWORD, DWORD);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004436static void dyn_imm_load(void);
4437#else
4438# define pImmGetCompositionStringA ImmGetCompositionStringA
4439# define pImmGetCompositionStringW ImmGetCompositionStringW
4440# define pImmGetContext ImmGetContext
4441# define pImmAssociateContext ImmAssociateContext
4442# define pImmReleaseContext ImmReleaseContext
4443# define pImmGetOpenStatus ImmGetOpenStatus
4444# define pImmSetOpenStatus ImmSetOpenStatus
4445# define pImmGetCompositionFont ImmGetCompositionFontA
4446# define pImmSetCompositionFont ImmSetCompositionFontA
4447# define pImmSetCompositionWindow ImmSetCompositionWindow
4448# define pImmGetConversionStatus ImmGetConversionStatus
Bram Moolenaarca003e12006-03-17 23:19:38 +00004449# define pImmSetConversionStatus ImmSetConversionStatus
Bram Moolenaar071d4272004-06-13 20:20:40 +00004450#endif
4451
Bram Moolenaar071d4272004-06-13 20:20:40 +00004452#ifdef FEAT_MENU
4453/*
4454 * Figure out how high the menu bar is at the moment.
4455 */
4456 static int
4457gui_mswin_get_menu_height(
4458 int fix_window) /* If TRUE, resize window if menu height changed */
4459{
4460 static int old_menu_height = -1;
4461
4462 RECT rc1, rc2;
4463 int num;
4464 int menu_height;
4465
4466 if (gui.menu_is_active)
4467 num = GetMenuItemCount(s_menuBar);
4468 else
4469 num = 0;
4470
4471 if (num == 0)
4472 menu_height = 0;
Bram Moolenaar71371b12015-03-24 17:57:12 +01004473 else if (IsMinimized(s_hwnd))
4474 {
4475 /* The height of the menu cannot be determined while the window is
4476 * minimized. Take the previous height if the menu is changed in that
4477 * state, to avoid that Vim's vertical window size accidentally
4478 * increases due to the unaccounted-for menu height. */
4479 menu_height = old_menu_height == -1 ? 0 : old_menu_height;
4480 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004481 else
4482 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +02004483 /*
4484 * In case 'lines' is set in _vimrc/_gvimrc window width doesn't
4485 * seem to have been set yet, so menu wraps in default window
4486 * width which is very narrow. Instead just return height of a
4487 * single menu item. Will still be wrong when the menu really
4488 * should wrap over more than one line.
4489 */
4490 GetMenuItemRect(s_hwnd, s_menuBar, 0, &rc1);
4491 if (gui.starting)
4492 menu_height = rc1.bottom - rc1.top + 1;
4493 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004494 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +02004495 GetMenuItemRect(s_hwnd, s_menuBar, num - 1, &rc2);
4496 menu_height = rc2.bottom - rc1.top + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004497 }
4498 }
4499
4500 if (fix_window && menu_height != old_menu_height)
4501 {
Bram Moolenaarafa24992006-03-27 20:58:26 +00004502 gui_set_shellsize(FALSE, FALSE, RESIZE_VERT);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004503 }
Bram Moolenaar71371b12015-03-24 17:57:12 +01004504 old_menu_height = menu_height;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004505
4506 return menu_height;
4507}
4508#endif /*FEAT_MENU*/
4509
4510
4511/*
4512 * Setup for the Intellimouse
4513 */
4514 static void
4515init_mouse_wheel(void)
4516{
4517
4518#ifndef SPI_GETWHEELSCROLLLINES
4519# define SPI_GETWHEELSCROLLLINES 104
4520#endif
Bram Moolenaare7566042005-06-17 22:00:15 +00004521#ifndef SPI_SETWHEELSCROLLLINES
4522# define SPI_SETWHEELSCROLLLINES 105
4523#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004524
4525#define VMOUSEZ_CLASSNAME "MouseZ" /* hidden wheel window class */
4526#define VMOUSEZ_TITLE "Magellan MSWHEEL" /* hidden wheel window title */
4527#define VMSH_MOUSEWHEEL "MSWHEEL_ROLLMSG"
4528#define VMSH_SCROLL_LINES "MSH_SCROLL_LINES_MSG"
4529
Bram Moolenaar071d4272004-06-13 20:20:40 +00004530 mouse_scroll_lines = 3; /* reasonable default */
4531
Bram Moolenaarcea912a2016-10-12 14:20:24 +02004532 /* if NT 4.0+ (or Win98) get scroll lines directly from system */
4533 SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0,
4534 &mouse_scroll_lines, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004535}
4536
4537
4538/* Intellimouse wheel handler */
4539 static void
4540_OnMouseWheel(
4541 HWND hwnd,
4542 short zDelta)
4543{
4544/* Treat a mouse wheel event as if it were a scroll request */
4545 int i;
4546 int size;
4547 HWND hwndCtl;
4548
4549 if (curwin->w_scrollbars[SBAR_RIGHT].id != 0)
4550 {
4551 hwndCtl = curwin->w_scrollbars[SBAR_RIGHT].id;
4552 size = curwin->w_scrollbars[SBAR_RIGHT].size;
4553 }
4554 else if (curwin->w_scrollbars[SBAR_LEFT].id != 0)
4555 {
4556 hwndCtl = curwin->w_scrollbars[SBAR_LEFT].id;
4557 size = curwin->w_scrollbars[SBAR_LEFT].size;
4558 }
4559 else
4560 return;
4561
4562 size = curwin->w_height;
4563 if (mouse_scroll_lines == 0)
4564 init_mouse_wheel();
4565
4566 if (mouse_scroll_lines > 0
4567 && mouse_scroll_lines < (size > 2 ? size - 2 : 1))
4568 {
4569 for (i = mouse_scroll_lines; i > 0; --i)
4570 _OnScroll(hwnd, hwndCtl, zDelta >= 0 ? SB_LINEUP : SB_LINEDOWN, 0);
4571 }
4572 else
4573 _OnScroll(hwnd, hwndCtl, zDelta >= 0 ? SB_PAGEUP : SB_PAGEDOWN, 0);
4574}
4575
Bram Moolenaar551dbcc2006-04-25 22:13:59 +00004576#ifdef USE_SYSMENU_FONT
4577/*
4578 * Get Menu Font.
4579 * Return OK or FAIL.
4580 */
4581 static int
4582gui_w32_get_menu_font(LOGFONT *lf)
4583{
4584 NONCLIENTMETRICS nm;
4585
4586 nm.cbSize = sizeof(NONCLIENTMETRICS);
4587 if (!SystemParametersInfo(
4588 SPI_GETNONCLIENTMETRICS,
4589 sizeof(NONCLIENTMETRICS),
4590 &nm,
4591 0))
4592 return FAIL;
4593 *lf = nm.lfMenuFont;
4594 return OK;
4595}
4596#endif
4597
4598
4599#if defined(FEAT_GUI_TABLINE) && defined(USE_SYSMENU_FONT)
4600/*
4601 * Set the GUI tabline font to the system menu font
4602 */
4603 static void
4604set_tabline_font(void)
4605{
4606 LOGFONT lfSysmenu;
4607 HFONT font;
4608 HWND hwnd;
4609 HDC hdc;
4610 HFONT hfntOld;
4611 TEXTMETRIC tm;
4612
4613 if (gui_w32_get_menu_font(&lfSysmenu) != OK)
4614 return;
4615
4616 font = CreateFontIndirect(&lfSysmenu);
4617
4618 SendMessage(s_tabhwnd, WM_SETFONT, (WPARAM)font, TRUE);
4619
4620 /*
4621 * Compute the height of the font used for the tab text
4622 */
4623 hwnd = GetDesktopWindow();
4624 hdc = GetWindowDC(hwnd);
4625 hfntOld = SelectFont(hdc, font);
4626
4627 GetTextMetrics(hdc, &tm);
4628
4629 SelectFont(hdc, hfntOld);
4630 ReleaseDC(hwnd, hdc);
4631
4632 /*
4633 * The space used by the tab border and the space between the tab label
4634 * and the tab border is included as 7.
4635 */
4636 gui.tabline_height = tm.tmHeight + tm.tmInternalLeading + 7;
4637}
4638#endif
4639
Bram Moolenaar520470a2005-06-16 21:59:56 +00004640/*
4641 * Invoked when a setting was changed.
4642 */
4643 static LRESULT CALLBACK
4644_OnSettingChange(UINT n)
4645{
4646 if (n == SPI_SETWHEELSCROLLLINES)
4647 SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0,
4648 &mouse_scroll_lines, 0);
Bram Moolenaar551dbcc2006-04-25 22:13:59 +00004649#if defined(FEAT_GUI_TABLINE) && defined(USE_SYSMENU_FONT)
4650 if (n == SPI_SETNONCLIENTMETRICS)
4651 set_tabline_font();
4652#endif
Bram Moolenaar520470a2005-06-16 21:59:56 +00004653 return 0;
4654}
4655
Bram Moolenaar071d4272004-06-13 20:20:40 +00004656#ifdef FEAT_NETBEANS_INTG
4657 static void
4658_OnWindowPosChanged(
4659 HWND hwnd,
4660 const LPWINDOWPOS lpwpos)
4661{
4662 static int x = 0, y = 0, cx = 0, cy = 0;
Bram Moolenaarf12d9832016-01-29 21:11:25 +01004663 extern int WSInitialized;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004664
4665 if (WSInitialized && (lpwpos->x != x || lpwpos->y != y
4666 || lpwpos->cx != cx || lpwpos->cy != cy))
4667 {
4668 x = lpwpos->x;
4669 y = lpwpos->y;
4670 cx = lpwpos->cx;
4671 cy = lpwpos->cy;
4672 netbeans_frame_moved(x, y);
4673 }
4674 /* Allow to send WM_SIZE and WM_MOVE */
4675 FORWARD_WM_WINDOWPOSCHANGED(hwnd, lpwpos, MyWindowProc);
4676}
4677#endif
4678
4679 static int
4680_DuringSizing(
Bram Moolenaar071d4272004-06-13 20:20:40 +00004681 UINT fwSide,
4682 LPRECT lprc)
4683{
4684 int w, h;
4685 int valid_w, valid_h;
4686 int w_offset, h_offset;
4687
4688 w = lprc->right - lprc->left;
4689 h = lprc->bottom - lprc->top;
4690 gui_mswin_get_valid_dimensions(w, h, &valid_w, &valid_h);
4691 w_offset = w - valid_w;
4692 h_offset = h - valid_h;
4693
4694 if (fwSide == WMSZ_LEFT || fwSide == WMSZ_TOPLEFT
4695 || fwSide == WMSZ_BOTTOMLEFT)
4696 lprc->left += w_offset;
4697 else if (fwSide == WMSZ_RIGHT || fwSide == WMSZ_TOPRIGHT
4698 || fwSide == WMSZ_BOTTOMRIGHT)
4699 lprc->right -= w_offset;
4700
4701 if (fwSide == WMSZ_TOP || fwSide == WMSZ_TOPLEFT
4702 || fwSide == WMSZ_TOPRIGHT)
4703 lprc->top += h_offset;
4704 else if (fwSide == WMSZ_BOTTOM || fwSide == WMSZ_BOTTOMLEFT
4705 || fwSide == WMSZ_BOTTOMRIGHT)
4706 lprc->bottom -= h_offset;
4707 return TRUE;
4708}
4709
4710
4711
4712 static LRESULT CALLBACK
4713_WndProc(
4714 HWND hwnd,
4715 UINT uMsg,
4716 WPARAM wParam,
4717 LPARAM lParam)
4718{
4719 /*
4720 TRACE("WndProc: hwnd = %08x, msg = %x, wParam = %x, lParam = %x\n",
4721 hwnd, uMsg, wParam, lParam);
4722 */
4723
4724 HandleMouseHide(uMsg, lParam);
4725
4726 s_uMsg = uMsg;
4727 s_wParam = wParam;
4728 s_lParam = lParam;
4729
4730 switch (uMsg)
4731 {
4732 HANDLE_MSG(hwnd, WM_DEADCHAR, _OnDeadChar);
4733 HANDLE_MSG(hwnd, WM_SYSDEADCHAR, _OnDeadChar);
4734 /* HANDLE_MSG(hwnd, WM_ACTIVATE, _OnActivate); */
4735 HANDLE_MSG(hwnd, WM_CLOSE, _OnClose);
4736 /* HANDLE_MSG(hwnd, WM_COMMAND, _OnCommand); */
4737 HANDLE_MSG(hwnd, WM_DESTROY, _OnDestroy);
4738 HANDLE_MSG(hwnd, WM_DROPFILES, _OnDropFiles);
4739 HANDLE_MSG(hwnd, WM_HSCROLL, _OnScroll);
4740 HANDLE_MSG(hwnd, WM_KILLFOCUS, _OnKillFocus);
4741#ifdef FEAT_MENU
4742 HANDLE_MSG(hwnd, WM_COMMAND, _OnMenu);
4743#endif
4744 /* HANDLE_MSG(hwnd, WM_MOVE, _OnMove); */
4745 /* HANDLE_MSG(hwnd, WM_NCACTIVATE, _OnNCActivate); */
4746 HANDLE_MSG(hwnd, WM_SETFOCUS, _OnSetFocus);
4747 HANDLE_MSG(hwnd, WM_SIZE, _OnSize);
4748 /* HANDLE_MSG(hwnd, WM_SYSCOMMAND, _OnSysCommand); */
4749 /* HANDLE_MSG(hwnd, WM_SYSKEYDOWN, _OnAltKey); */
4750 HANDLE_MSG(hwnd, WM_VSCROLL, _OnScroll);
4751 // HANDLE_MSG(hwnd, WM_WINDOWPOSCHANGING, _OnWindowPosChanging);
4752 HANDLE_MSG(hwnd, WM_ACTIVATEAPP, _OnActivateApp);
4753#ifdef FEAT_NETBEANS_INTG
4754 HANDLE_MSG(hwnd, WM_WINDOWPOSCHANGED, _OnWindowPosChanged);
4755#endif
4756
Bram Moolenaarafa24992006-03-27 20:58:26 +00004757#ifdef FEAT_GUI_TABLINE
4758 case WM_RBUTTONUP:
4759 {
4760 if (gui_mch_showing_tabline())
4761 {
4762 POINT pt;
4763 RECT rect;
4764
4765 /*
4766 * If the cursor is on the tabline, display the tab menu
4767 */
4768 GetCursorPos((LPPOINT)&pt);
4769 GetWindowRect(s_textArea, &rect);
4770 if (pt.y < rect.top)
4771 {
4772 show_tabline_popup_menu();
Bram Moolenaar213ae482011-12-15 21:51:36 +01004773 return 0L;
Bram Moolenaarafa24992006-03-27 20:58:26 +00004774 }
4775 }
4776 return MyWindowProc(hwnd, uMsg, wParam, lParam);
4777 }
Bram Moolenaar910f66f2006-04-05 20:41:53 +00004778 case WM_LBUTTONDBLCLK:
4779 {
4780 /*
4781 * If the user double clicked the tabline, create a new tab
4782 */
4783 if (gui_mch_showing_tabline())
4784 {
4785 POINT pt;
4786 RECT rect;
4787
4788 GetCursorPos((LPPOINT)&pt);
4789 GetWindowRect(s_textArea, &rect);
4790 if (pt.y < rect.top)
Bram Moolenaarc6fe9192006-04-09 21:54:49 +00004791 send_tabline_menu_event(0, TABLINE_MENU_NEW);
Bram Moolenaar910f66f2006-04-05 20:41:53 +00004792 }
4793 return MyWindowProc(hwnd, uMsg, wParam, lParam);
4794 }
Bram Moolenaarafa24992006-03-27 20:58:26 +00004795#endif
4796
Bram Moolenaar071d4272004-06-13 20:20:40 +00004797 case WM_QUERYENDSESSION: /* System wants to go down. */
4798 gui_shell_closed(); /* Will exit when no changed buffers. */
4799 return FALSE; /* Do NOT allow system to go down. */
4800
4801 case WM_ENDSESSION:
4802 if (wParam) /* system only really goes down when wParam is TRUE */
Bram Moolenaar213ae482011-12-15 21:51:36 +01004803 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00004804 _OnEndSession();
Bram Moolenaar213ae482011-12-15 21:51:36 +01004805 return 0L;
4806 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004807 break;
4808
4809 case WM_CHAR:
4810 /* Don't use HANDLE_MSG() for WM_CHAR, it truncates wParam to a single
4811 * byte while we want the UTF-16 character value. */
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004812 _OnChar(hwnd, (UINT)wParam, (int)(short)LOWORD(lParam));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004813 return 0L;
4814
4815 case WM_SYSCHAR:
4816 /*
4817 * if 'winaltkeys' is "no", or it's "menu" and it's not a menu
4818 * shortcut key, handle like a typed ALT key, otherwise call Windows
4819 * ALT key handling.
4820 */
4821#ifdef FEAT_MENU
4822 if ( !gui.menu_is_active
4823 || p_wak[0] == 'n'
4824 || (p_wak[0] == 'm' && !gui_is_menu_shortcut((int)wParam))
4825 )
4826#endif
4827 {
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004828 _OnSysChar(hwnd, (UINT)wParam, (int)(short)LOWORD(lParam));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004829 return 0L;
4830 }
4831#ifdef FEAT_MENU
4832 else
4833 return MyWindowProc(hwnd, uMsg, wParam, lParam);
4834#endif
4835
4836 case WM_SYSKEYUP:
4837#ifdef FEAT_MENU
4838 /* This used to be done only when menu is active: ALT key is used for
4839 * that. But that caused problems when menu is disabled and using
4840 * Alt-Tab-Esc: get into a strange state where no mouse-moved events
4841 * are received, mouse pointer remains hidden. */
4842 return MyWindowProc(hwnd, uMsg, wParam, lParam);
4843#else
Bram Moolenaar213ae482011-12-15 21:51:36 +01004844 return 0L;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004845#endif
4846
4847 case WM_SIZING: /* HANDLE_MSG doesn't seem to handle this one */
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +00004848 return _DuringSizing((UINT)wParam, (LPRECT)lParam);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004849
4850 case WM_MOUSEWHEEL:
4851 _OnMouseWheel(hwnd, HIWORD(wParam));
Bram Moolenaar213ae482011-12-15 21:51:36 +01004852 return 0L;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004853
Bram Moolenaar520470a2005-06-16 21:59:56 +00004854 /* Notification for change in SystemParametersInfo() */
4855 case WM_SETTINGCHANGE:
4856 return _OnSettingChange((UINT)wParam);
4857
Bram Moolenaar3991dab2006-03-27 17:01:56 +00004858#if defined(FEAT_TOOLBAR) || defined(FEAT_GUI_TABLINE)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004859 case WM_NOTIFY:
4860 switch (((LPNMHDR) lParam)->code)
4861 {
Bram Moolenaareb3593b2006-04-22 22:33:57 +00004862# ifdef FEAT_MBYTE
4863 case TTN_GETDISPINFOW:
4864# endif
Bram Moolenaar8f2ff9f2006-08-29 19:26:50 +00004865 case TTN_GETDISPINFO:
Bram Moolenaar071d4272004-06-13 20:20:40 +00004866 {
Bram Moolenaar8f2ff9f2006-08-29 19:26:50 +00004867 LPNMHDR hdr = (LPNMHDR)lParam;
4868 char_u *str = NULL;
4869 static void *tt_text = NULL;
Bram Moolenaareb3593b2006-04-22 22:33:57 +00004870
Bram Moolenaar8f2ff9f2006-08-29 19:26:50 +00004871 vim_free(tt_text);
4872 tt_text = NULL;
Bram Moolenaareb3593b2006-04-22 22:33:57 +00004873
Bram Moolenaar8f2ff9f2006-08-29 19:26:50 +00004874# ifdef FEAT_GUI_TABLINE
4875 if (gui_mch_showing_tabline()
4876 && hdr->hwndFrom == TabCtrl_GetToolTips(s_tabhwnd))
Bram Moolenaareb3593b2006-04-22 22:33:57 +00004877 {
Bram Moolenaar8f2ff9f2006-08-29 19:26:50 +00004878 POINT pt;
Bram Moolenaareb3593b2006-04-22 22:33:57 +00004879 /*
Bram Moolenaar8f2ff9f2006-08-29 19:26:50 +00004880 * Mouse is over the GUI tabline. Display the
4881 * tooltip for the tab under the cursor
4882 *
4883 * Get the cursor position within the tab control
Bram Moolenaareb3593b2006-04-22 22:33:57 +00004884 */
Bram Moolenaar8f2ff9f2006-08-29 19:26:50 +00004885 GetCursorPos(&pt);
4886 if (ScreenToClient(s_tabhwnd, &pt) != 0)
Bram Moolenaareb3593b2006-04-22 22:33:57 +00004887 {
Bram Moolenaar8f2ff9f2006-08-29 19:26:50 +00004888 TCHITTESTINFO htinfo;
4889 int idx;
Bram Moolenaareb3593b2006-04-22 22:33:57 +00004890
Bram Moolenaar8f2ff9f2006-08-29 19:26:50 +00004891 /*
4892 * Get the tab under the cursor
4893 */
4894 htinfo.pt.x = pt.x;
4895 htinfo.pt.y = pt.y;
4896 idx = TabCtrl_HitTest(s_tabhwnd, &htinfo);
4897 if (idx != -1)
Bram Moolenaareb3593b2006-04-22 22:33:57 +00004898 {
Bram Moolenaar8f2ff9f2006-08-29 19:26:50 +00004899 tabpage_T *tp;
Bram Moolenaareb3593b2006-04-22 22:33:57 +00004900
Bram Moolenaar8f2ff9f2006-08-29 19:26:50 +00004901 tp = find_tabpage(idx + 1);
4902 if (tp != NULL)
Bram Moolenaareb3593b2006-04-22 22:33:57 +00004903 {
Bram Moolenaar8f2ff9f2006-08-29 19:26:50 +00004904 get_tabline_label(tp, TRUE);
4905 str = NameBuff;
Bram Moolenaareb3593b2006-04-22 22:33:57 +00004906 }
4907 }
4908 }
4909 }
Bram Moolenaareb3593b2006-04-22 22:33:57 +00004910# endif
Bram Moolenaareb3593b2006-04-22 22:33:57 +00004911# ifdef FEAT_TOOLBAR
Bram Moolenaar8f2ff9f2006-08-29 19:26:50 +00004912# ifdef FEAT_GUI_TABLINE
4913 else
4914# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004915 {
Bram Moolenaar8f2ff9f2006-08-29 19:26:50 +00004916 UINT idButton;
4917 vimmenu_T *pMenu;
4918
4919 idButton = (UINT) hdr->idFrom;
4920 pMenu = gui_mswin_find_menu(root_menu, idButton);
4921 if (pMenu)
4922 str = pMenu->strings[MENU_INDEX_TIP];
Bram Moolenaar071d4272004-06-13 20:20:40 +00004923 }
Bram Moolenaareb3593b2006-04-22 22:33:57 +00004924# endif
Bram Moolenaar8f2ff9f2006-08-29 19:26:50 +00004925 if (str != NULL)
4926 {
4927# ifdef FEAT_MBYTE
4928 if (hdr->code == TTN_GETDISPINFOW)
4929 {
4930 LPNMTTDISPINFOW lpdi = (LPNMTTDISPINFOW)lParam;
4931
Bram Moolenaar6c9176d2008-01-03 19:45:15 +00004932 /* Set the maximum width, this also enables using
4933 * \n for line break. */
4934 SendMessage(lpdi->hdr.hwndFrom, TTM_SETMAXTIPWIDTH,
4935 0, 500);
4936
Bram Moolenaar36f692d2008-11-20 16:10:17 +00004937 tt_text = enc_to_utf16(str, NULL);
Bram Moolenaar8f2ff9f2006-08-29 19:26:50 +00004938 lpdi->lpszText = tt_text;
4939 /* can't show tooltip if failed */
4940 }
4941 else
4942# endif
4943 {
4944 LPNMTTDISPINFO lpdi = (LPNMTTDISPINFO)lParam;
4945
Bram Moolenaar6c9176d2008-01-03 19:45:15 +00004946 /* Set the maximum width, this also enables using
4947 * \n for line break. */
4948 SendMessage(lpdi->hdr.hwndFrom, TTM_SETMAXTIPWIDTH,
4949 0, 500);
4950
Bram Moolenaar8f2ff9f2006-08-29 19:26:50 +00004951 if (STRLEN(str) < sizeof(lpdi->szText)
4952 || ((tt_text = vim_strsave(str)) == NULL))
Bram Moolenaar418f81b2016-02-16 20:12:02 +01004953 vim_strncpy((char_u *)lpdi->szText, str,
Bram Moolenaar8f2ff9f2006-08-29 19:26:50 +00004954 sizeof(lpdi->szText) - 1);
4955 else
4956 lpdi->lpszText = tt_text;
4957 }
4958 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004959 }
4960 break;
Bram Moolenaar3991dab2006-03-27 17:01:56 +00004961# ifdef FEAT_GUI_TABLINE
4962 case TCN_SELCHANGE:
4963 if (gui_mch_showing_tabline()
4964 && ((LPNMHDR)lParam)->hwndFrom == s_tabhwnd)
Bram Moolenaar213ae482011-12-15 21:51:36 +01004965 {
Bram Moolenaar3991dab2006-03-27 17:01:56 +00004966 send_tabline_event(TabCtrl_GetCurSel(s_tabhwnd) + 1);
Bram Moolenaar213ae482011-12-15 21:51:36 +01004967 return 0L;
4968 }
Bram Moolenaar3991dab2006-03-27 17:01:56 +00004969 break;
Bram Moolenaarafa24992006-03-27 20:58:26 +00004970
4971 case NM_RCLICK:
4972 if (gui_mch_showing_tabline()
4973 && ((LPNMHDR)lParam)->hwndFrom == s_tabhwnd)
Bram Moolenaar213ae482011-12-15 21:51:36 +01004974 {
Bram Moolenaarafa24992006-03-27 20:58:26 +00004975 show_tabline_popup_menu();
Bram Moolenaar213ae482011-12-15 21:51:36 +01004976 return 0L;
4977 }
Bram Moolenaarafa24992006-03-27 20:58:26 +00004978 break;
Bram Moolenaar3991dab2006-03-27 17:01:56 +00004979# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004980 default:
Bram Moolenaar3991dab2006-03-27 17:01:56 +00004981# ifdef FEAT_GUI_TABLINE
4982 if (gui_mch_showing_tabline()
4983 && ((LPNMHDR)lParam)->hwndFrom == s_tabhwnd)
4984 return MyWindowProc(hwnd, uMsg, wParam, lParam);
4985# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004986 break;
4987 }
4988 break;
4989#endif
4990#if defined(MENUHINTS) && defined(FEAT_MENU)
4991 case WM_MENUSELECT:
4992 if (((UINT) HIWORD(wParam)
4993 & (0xffff ^ (MF_MOUSESELECT + MF_BITMAP + MF_POPUP)))
4994 == MF_HILITE
4995 && (State & CMDLINE) == 0)
4996 {
4997 UINT idButton;
4998 vimmenu_T *pMenu;
4999 static int did_menu_tip = FALSE;
5000
5001 if (did_menu_tip)
5002 {
5003 msg_clr_cmdline();
5004 setcursor();
5005 out_flush();
5006 did_menu_tip = FALSE;
5007 }
5008
5009 idButton = (UINT)LOWORD(wParam);
5010 pMenu = gui_mswin_find_menu(root_menu, idButton);
5011 if (pMenu != NULL && pMenu->strings[MENU_INDEX_TIP] != 0
5012 && GetMenuState(s_menuBar, pMenu->id, MF_BYCOMMAND) != -1)
5013 {
Bram Moolenaar2d8ab992007-06-19 08:06:18 +00005014 ++msg_hist_off;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005015 msg(pMenu->strings[MENU_INDEX_TIP]);
Bram Moolenaar2d8ab992007-06-19 08:06:18 +00005016 --msg_hist_off;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005017 setcursor();
5018 out_flush();
5019 did_menu_tip = TRUE;
5020 }
Bram Moolenaar213ae482011-12-15 21:51:36 +01005021 return 0L;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005022 }
5023 break;
5024#endif
5025 case WM_NCHITTEST:
5026 {
5027 LRESULT result;
5028 int x, y;
5029 int xPos = GET_X_LPARAM(lParam);
5030
5031 result = MyWindowProc(hwnd, uMsg, wParam, lParam);
5032 if (result == HTCLIENT)
5033 {
Bram Moolenaar3991dab2006-03-27 17:01:56 +00005034#ifdef FEAT_GUI_TABLINE
5035 if (gui_mch_showing_tabline())
5036 {
5037 int yPos = GET_Y_LPARAM(lParam);
5038 RECT rct;
5039
5040 /* If the cursor is on the GUI tabline, don't process this
5041 * event */
5042 GetWindowRect(s_textArea, &rct);
5043 if (yPos < rct.top)
5044 return result;
5045 }
5046#endif
Bram Moolenaarcde88542015-08-11 19:14:00 +02005047 (void)gui_mch_get_winpos(&x, &y);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005048 xPos -= x;
5049
5050 if (xPos < 48) /* <VN> TODO should use system metric? */
5051 return HTBOTTOMLEFT;
5052 else
5053 return HTBOTTOMRIGHT;
5054 }
5055 else
5056 return result;
5057 }
5058 /* break; notreached */
5059
5060#ifdef FEAT_MBYTE_IME
5061 case WM_IME_NOTIFY:
5062 if (!_OnImeNotify(hwnd, (DWORD)wParam, (DWORD)lParam))
5063 return MyWindowProc(hwnd, uMsg, wParam, lParam);
Bram Moolenaar213ae482011-12-15 21:51:36 +01005064 return 1L;
5065
Bram Moolenaar071d4272004-06-13 20:20:40 +00005066 case WM_IME_COMPOSITION:
5067 if (!_OnImeComposition(hwnd, wParam, lParam))
5068 return MyWindowProc(hwnd, uMsg, wParam, lParam);
Bram Moolenaar213ae482011-12-15 21:51:36 +01005069 return 1L;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005070#endif
5071
5072 default:
Bram Moolenaar071d4272004-06-13 20:20:40 +00005073#ifdef MSWIN_FIND_REPLACE
Bram Moolenaarcea912a2016-10-12 14:20:24 +02005074 if (uMsg == s_findrep_msg && s_findrep_msg != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005075 {
5076 _OnFindRepl();
5077 }
5078#endif
5079 return MyWindowProc(hwnd, uMsg, wParam, lParam);
5080 }
5081
Bram Moolenaar2787ab92011-12-14 15:23:59 +01005082 return DefWindowProc(hwnd, uMsg, wParam, lParam);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005083}
5084
5085/*
5086 * End of call-back routines
5087 */
5088
5089/* parent window, if specified with -P */
5090HWND vim_parent_hwnd = NULL;
5091
5092 static BOOL CALLBACK
5093FindWindowTitle(HWND hwnd, LPARAM lParam)
5094{
5095 char buf[2048];
5096 char *title = (char *)lParam;
5097
5098 if (GetWindowText(hwnd, buf, sizeof(buf)))
5099 {
5100 if (strstr(buf, title) != NULL)
5101 {
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00005102 /* Found it. Store the window ref. and quit searching if MDI
5103 * works. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005104 vim_parent_hwnd = FindWindowEx(hwnd, NULL, "MDIClient", NULL);
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00005105 if (vim_parent_hwnd != NULL)
5106 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005107 }
5108 }
5109 return TRUE; /* continue searching */
5110}
5111
5112/*
5113 * Invoked for '-P "title"' argument: search for parent application to open
5114 * our window in.
5115 */
5116 void
5117gui_mch_set_parent(char *title)
5118{
5119 EnumWindows(FindWindowTitle, (LPARAM)title);
5120 if (vim_parent_hwnd == NULL)
5121 {
5122 EMSG2(_("E671: Cannot find window title \"%s\""), title);
5123 mch_exit(2);
5124 }
5125}
5126
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +00005127#ifndef FEAT_OLE
Bram Moolenaar071d4272004-06-13 20:20:40 +00005128 static void
5129ole_error(char *arg)
5130{
Bram Moolenaaraea02fa2007-05-04 20:29:09 +00005131 char buf[IOSIZE];
5132
5133 /* Can't use EMSG() here, we have not finished initialisation yet. */
5134 vim_snprintf(buf, IOSIZE,
5135 _("E243: Argument not supported: \"-%s\"; Use the OLE version."),
5136 arg);
5137 mch_errmsg(buf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005138}
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +00005139#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005140
5141/*
5142 * Parse the GUI related command-line arguments. Any arguments used are
5143 * deleted from argv, and *argc is decremented accordingly. This is called
5144 * when vim is started, whether or not the GUI has been started.
5145 */
5146 void
5147gui_mch_prepare(int *argc, char **argv)
5148{
5149 int silent = FALSE;
5150 int idx;
5151
5152 /* Check for special OLE command line parameters */
5153 if ((*argc == 2 || *argc == 3) && (argv[1][0] == '-' || argv[1][0] == '/'))
5154 {
5155 /* Check for a "-silent" argument first. */
5156 if (*argc == 3 && STRICMP(argv[1] + 1, "silent") == 0
5157 && (argv[2][0] == '-' || argv[2][0] == '/'))
5158 {
5159 silent = TRUE;
5160 idx = 2;
5161 }
5162 else
5163 idx = 1;
5164
5165 /* Register Vim as an OLE Automation server */
5166 if (STRICMP(argv[idx] + 1, "register") == 0)
5167 {
5168#ifdef FEAT_OLE
5169 RegisterMe(silent);
5170 mch_exit(0);
5171#else
5172 if (!silent)
5173 ole_error("register");
5174 mch_exit(2);
5175#endif
5176 }
5177
5178 /* Unregister Vim as an OLE Automation server */
5179 if (STRICMP(argv[idx] + 1, "unregister") == 0)
5180 {
5181#ifdef FEAT_OLE
5182 UnregisterMe(!silent);
5183 mch_exit(0);
5184#else
5185 if (!silent)
5186 ole_error("unregister");
5187 mch_exit(2);
5188#endif
5189 }
5190
5191 /* Ignore an -embedding argument. It is only relevant if the
5192 * application wants to treat the case when it is started manually
5193 * differently from the case where it is started via automation (and
5194 * we don't).
5195 */
5196 if (STRICMP(argv[idx] + 1, "embedding") == 0)
5197 {
5198#ifdef FEAT_OLE
5199 *argc = 1;
5200#else
5201 ole_error("embedding");
5202 mch_exit(2);
5203#endif
5204 }
5205 }
5206
5207#ifdef FEAT_OLE
5208 {
5209 int bDoRestart = FALSE;
5210
5211 InitOLE(&bDoRestart);
5212 /* automatically exit after registering */
5213 if (bDoRestart)
5214 mch_exit(0);
5215 }
5216#endif
5217
5218#ifdef FEAT_NETBEANS_INTG
5219 {
Bram Moolenaar84a05ac2013-05-06 04:24:17 +02005220 /* stolen from gui_x11.c */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005221 int arg;
5222
5223 for (arg = 1; arg < *argc; arg++)
5224 if (strncmp("-nb", argv[arg], 3) == 0)
5225 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00005226 netbeansArg = argv[arg];
5227 mch_memmove(&argv[arg], &argv[arg + 1],
5228 (--*argc - arg) * sizeof(char *));
5229 argv[*argc] = NULL;
5230 break; /* enough? */
5231 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005232 }
5233#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005234}
5235
5236/*
5237 * Initialise the GUI. Create all the windows, set up all the call-backs
5238 * etc.
5239 */
5240 int
5241gui_mch_init(void)
5242{
5243 const char szVimWndClass[] = VIM_CLASS;
5244 const char szTextAreaClass[] = "VimTextArea";
5245 WNDCLASS wndclass;
5246#ifdef FEAT_MBYTE
5247 const WCHAR szVimWndClassW[] = VIM_CLASSW;
Bram Moolenaar33d0b692010-02-17 16:31:32 +01005248 const WCHAR szTextAreaClassW[] = L"VimTextArea";
Bram Moolenaar071d4272004-06-13 20:20:40 +00005249 WNDCLASSW wndclassw;
5250#endif
5251#ifdef GLOBAL_IME
5252 ATOM atom;
5253#endif
5254
Bram Moolenaar071d4272004-06-13 20:20:40 +00005255 /* Return here if the window was already opened (happens when
5256 * gui_mch_dialog() is called early). */
5257 if (s_hwnd != NULL)
Bram Moolenaar748bf032005-02-02 23:04:36 +00005258 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005259
5260 /*
5261 * Load the tearoff bitmap
5262 */
5263#ifdef FEAT_TEAROFF
5264 s_htearbitmap = LoadBitmap(s_hinst, "IDB_TEAROFF");
5265#endif
5266
5267 gui.scrollbar_width = GetSystemMetrics(SM_CXVSCROLL);
5268 gui.scrollbar_height = GetSystemMetrics(SM_CYHSCROLL);
5269#ifdef FEAT_MENU
5270 gui.menu_height = 0; /* Windows takes care of this */
5271#endif
5272 gui.border_width = 0;
5273
5274 s_brush = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
5275
5276#ifdef FEAT_MBYTE
5277 /* First try using the wide version, so that we can use any title.
5278 * Otherwise only characters in the active codepage will work. */
5279 if (GetClassInfoW(s_hinst, szVimWndClassW, &wndclassw) == 0)
5280 {
Bram Moolenaar910f66f2006-04-05 20:41:53 +00005281 wndclassw.style = CS_DBLCLKS;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005282 wndclassw.lpfnWndProc = _WndProc;
5283 wndclassw.cbClsExtra = 0;
5284 wndclassw.cbWndExtra = 0;
5285 wndclassw.hInstance = s_hinst;
5286 wndclassw.hIcon = LoadIcon(wndclassw.hInstance, "IDR_VIM");
5287 wndclassw.hCursor = LoadCursor(NULL, IDC_ARROW);
5288 wndclassw.hbrBackground = s_brush;
5289 wndclassw.lpszMenuName = NULL;
5290 wndclassw.lpszClassName = szVimWndClassW;
5291
5292 if ((
5293#ifdef GLOBAL_IME
5294 atom =
5295#endif
5296 RegisterClassW(&wndclassw)) == 0)
Bram Moolenaarcea912a2016-10-12 14:20:24 +02005297 return FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005298 else
5299 wide_WindowProc = TRUE;
5300 }
5301
5302 if (!wide_WindowProc)
5303#endif
5304
5305 if (GetClassInfo(s_hinst, szVimWndClass, &wndclass) == 0)
5306 {
Bram Moolenaar910f66f2006-04-05 20:41:53 +00005307 wndclass.style = CS_DBLCLKS;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005308 wndclass.lpfnWndProc = _WndProc;
5309 wndclass.cbClsExtra = 0;
5310 wndclass.cbWndExtra = 0;
5311 wndclass.hInstance = s_hinst;
5312 wndclass.hIcon = LoadIcon(wndclass.hInstance, "IDR_VIM");
5313 wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
5314 wndclass.hbrBackground = s_brush;
5315 wndclass.lpszMenuName = NULL;
5316 wndclass.lpszClassName = szVimWndClass;
5317
5318 if ((
5319#ifdef GLOBAL_IME
5320 atom =
5321#endif
5322 RegisterClass(&wndclass)) == 0)
5323 return FAIL;
5324 }
5325
5326 if (vim_parent_hwnd != NULL)
5327 {
5328#ifdef HAVE_TRY_EXCEPT
5329 __try
5330 {
5331#endif
5332 /* Open inside the specified parent window.
5333 * TODO: last argument should point to a CLIENTCREATESTRUCT
5334 * structure. */
5335 s_hwnd = CreateWindowEx(
5336 WS_EX_MDICHILD,
5337 szVimWndClass, "Vim MSWindows GUI",
Bram Moolenaare78c2062011-08-10 15:56:27 +02005338 WS_OVERLAPPEDWINDOW | WS_CHILD
5339 | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | 0xC000,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005340 gui_win_x == -1 ? CW_USEDEFAULT : gui_win_x,
5341 gui_win_y == -1 ? CW_USEDEFAULT : gui_win_y,
5342 100, /* Any value will do */
5343 100, /* Any value will do */
5344 vim_parent_hwnd, NULL,
5345 s_hinst, NULL);
5346#ifdef HAVE_TRY_EXCEPT
5347 }
5348 __except(EXCEPTION_EXECUTE_HANDLER)
5349 {
5350 /* NOP */
5351 }
5352#endif
5353 if (s_hwnd == NULL)
5354 {
5355 EMSG(_("E672: Unable to open window inside MDI application"));
5356 mch_exit(2);
5357 }
5358 }
5359 else
Bram Moolenaar78e17622007-08-30 10:26:19 +00005360 {
5361 /* If the provided windowid is not valid reset it to zero, so that it
5362 * is ignored and we open our own window. */
5363 if (IsWindow((HWND)win_socket_id) <= 0)
5364 win_socket_id = 0;
5365
5366 /* Create a window. If win_socket_id is not zero without border and
5367 * titlebar, it will be reparented below. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005368 s_hwnd = CreateWindow(
Bram Moolenaar78e17622007-08-30 10:26:19 +00005369 szVimWndClass, "Vim MSWindows GUI",
Bram Moolenaare78c2062011-08-10 15:56:27 +02005370 (win_socket_id == 0 ? WS_OVERLAPPEDWINDOW : WS_POPUP)
5371 | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
Bram Moolenaar78e17622007-08-30 10:26:19 +00005372 gui_win_x == -1 ? CW_USEDEFAULT : gui_win_x,
5373 gui_win_y == -1 ? CW_USEDEFAULT : gui_win_y,
5374 100, /* Any value will do */
5375 100, /* Any value will do */
5376 NULL, NULL,
5377 s_hinst, NULL);
5378 if (s_hwnd != NULL && win_socket_id != 0)
5379 {
5380 SetParent(s_hwnd, (HWND)win_socket_id);
5381 ShowWindow(s_hwnd, SW_SHOWMAXIMIZED);
5382 }
5383 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005384
5385 if (s_hwnd == NULL)
5386 return FAIL;
5387
5388#ifdef GLOBAL_IME
5389 global_ime_init(atom, s_hwnd);
5390#endif
5391#if defined(FEAT_MBYTE_IME) && defined(DYNAMIC_IME)
5392 dyn_imm_load();
5393#endif
5394
5395 /* Create the text area window */
Bram Moolenaar33d0b692010-02-17 16:31:32 +01005396#ifdef FEAT_MBYTE
5397 if (wide_WindowProc)
5398 {
5399 if (GetClassInfoW(s_hinst, szTextAreaClassW, &wndclassw) == 0)
5400 {
5401 wndclassw.style = CS_OWNDC;
5402 wndclassw.lpfnWndProc = _TextAreaWndProc;
5403 wndclassw.cbClsExtra = 0;
5404 wndclassw.cbWndExtra = 0;
5405 wndclassw.hInstance = s_hinst;
5406 wndclassw.hIcon = NULL;
5407 wndclassw.hCursor = LoadCursor(NULL, IDC_ARROW);
5408 wndclassw.hbrBackground = NULL;
5409 wndclassw.lpszMenuName = NULL;
5410 wndclassw.lpszClassName = szTextAreaClassW;
5411
5412 if (RegisterClassW(&wndclassw) == 0)
5413 return FAIL;
5414 }
5415 }
5416 else
5417#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005418 if (GetClassInfo(s_hinst, szTextAreaClass, &wndclass) == 0)
5419 {
5420 wndclass.style = CS_OWNDC;
5421 wndclass.lpfnWndProc = _TextAreaWndProc;
5422 wndclass.cbClsExtra = 0;
5423 wndclass.cbWndExtra = 0;
5424 wndclass.hInstance = s_hinst;
5425 wndclass.hIcon = NULL;
5426 wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
5427 wndclass.hbrBackground = NULL;
5428 wndclass.lpszMenuName = NULL;
5429 wndclass.lpszClassName = szTextAreaClass;
5430
5431 if (RegisterClass(&wndclass) == 0)
5432 return FAIL;
5433 }
5434 s_textArea = CreateWindowEx(
Bram Moolenaar97b0b0e2015-11-19 20:23:37 +01005435 0,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005436 szTextAreaClass, "Vim text area",
5437 WS_CHILD | WS_VISIBLE, 0, 0,
5438 100, /* Any value will do for now */
5439 100, /* Any value will do for now */
5440 s_hwnd, NULL,
5441 s_hinst, NULL);
5442
5443 if (s_textArea == NULL)
5444 return FAIL;
5445
Bram Moolenaar20321902016-02-17 12:30:17 +01005446#ifdef FEAT_LIBCALL
Bram Moolenaarcddc91c2014-09-23 21:53:41 +02005447 /* Try loading an icon from $RUNTIMEPATH/bitmaps/vim.ico. */
5448 {
5449 HANDLE hIcon = NULL;
5450
5451 if (mch_icon_load(&hIcon) == OK && hIcon != NULL)
Bram Moolenaar0f519a02014-10-06 18:10:09 +02005452 SendMessage(s_hwnd, WM_SETICON, ICON_SMALL, (LPARAM)hIcon);
Bram Moolenaarcddc91c2014-09-23 21:53:41 +02005453 }
Bram Moolenaar20321902016-02-17 12:30:17 +01005454#endif
Bram Moolenaarcddc91c2014-09-23 21:53:41 +02005455
Bram Moolenaar071d4272004-06-13 20:20:40 +00005456#ifdef FEAT_MENU
5457 s_menuBar = CreateMenu();
5458#endif
5459 s_hdc = GetDC(s_textArea);
5460
Bram Moolenaar071d4272004-06-13 20:20:40 +00005461 DragAcceptFiles(s_hwnd, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005462
5463 /* Do we need to bother with this? */
5464 /* m_fMouseAvail = GetSystemMetrics(SM_MOUSEPRESENT); */
5465
5466 /* Get background/foreground colors from the system */
5467 gui_mch_def_colors();
5468
5469 /* Get the colors from the "Normal" group (set in syntax.c or in a vimrc
5470 * file) */
5471 set_normal_colors();
5472
5473 /*
5474 * Check that none of the colors are the same as the background color.
5475 * Then store the current values as the defaults.
5476 */
5477 gui_check_colors();
5478 gui.def_norm_pixel = gui.norm_pixel;
5479 gui.def_back_pixel = gui.back_pixel;
5480
5481 /* Get the colors for the highlight groups (gui_check_colors() might have
5482 * changed them) */
5483 highlight_gui_started();
5484
5485 /*
Bram Moolenaar97b0b0e2015-11-19 20:23:37 +01005486 * Start out by adding the configured border width into the border offset.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005487 */
Bram Moolenaar97b0b0e2015-11-19 20:23:37 +01005488 gui.border_offset = gui.border_width;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005489
5490 /*
5491 * Set up for Intellimouse processing
5492 */
5493 init_mouse_wheel();
5494
5495 /*
5496 * compute a couple of metrics used for the dialogs
5497 */
5498 get_dialog_font_metrics();
5499#ifdef FEAT_TOOLBAR
5500 /*
5501 * Create the toolbar
5502 */
5503 initialise_toolbar();
5504#endif
Bram Moolenaar3991dab2006-03-27 17:01:56 +00005505#ifdef FEAT_GUI_TABLINE
5506 /*
5507 * Create the tabline
5508 */
5509 initialise_tabline();
5510#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005511#ifdef MSWIN_FIND_REPLACE
5512 /*
5513 * Initialise the dialog box stuff
5514 */
5515 s_findrep_msg = RegisterWindowMessage(FINDMSGSTRING);
5516
5517 /* Initialise the struct */
5518 s_findrep_struct.lStructSize = sizeof(s_findrep_struct);
Bram Moolenaar418f81b2016-02-16 20:12:02 +01005519 s_findrep_struct.lpstrFindWhat = (LPSTR)alloc(MSWIN_FR_BUFSIZE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005520 s_findrep_struct.lpstrFindWhat[0] = NUL;
Bram Moolenaar418f81b2016-02-16 20:12:02 +01005521 s_findrep_struct.lpstrReplaceWith = (LPSTR)alloc(MSWIN_FR_BUFSIZE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005522 s_findrep_struct.lpstrReplaceWith[0] = NUL;
5523 s_findrep_struct.wFindWhatLen = MSWIN_FR_BUFSIZE;
5524 s_findrep_struct.wReplaceWithLen = MSWIN_FR_BUFSIZE;
Bram Moolenaarcea912a2016-10-12 14:20:24 +02005525# ifdef FEAT_MBYTE
Bram Moolenaar3ca9a8a2009-01-28 20:23:17 +00005526 s_findrep_struct_w.lStructSize = sizeof(s_findrep_struct_w);
5527 s_findrep_struct_w.lpstrFindWhat =
5528 (LPWSTR)alloc(MSWIN_FR_BUFSIZE * sizeof(WCHAR));
5529 s_findrep_struct_w.lpstrFindWhat[0] = NUL;
5530 s_findrep_struct_w.lpstrReplaceWith =
5531 (LPWSTR)alloc(MSWIN_FR_BUFSIZE * sizeof(WCHAR));
5532 s_findrep_struct_w.lpstrReplaceWith[0] = NUL;
5533 s_findrep_struct_w.wFindWhatLen = MSWIN_FR_BUFSIZE;
5534 s_findrep_struct_w.wReplaceWithLen = MSWIN_FR_BUFSIZE;
5535# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005536#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005537
Bram Moolenaar264e9fd2010-10-27 12:33:17 +02005538#ifdef FEAT_EVAL
Bram Moolenaarf32c5cd2016-01-10 16:07:44 +01005539# if !defined(_MSC_VER) || (_MSC_VER < 1400)
5540/* Define HandleToLong for old MS and non-MS compilers if not defined. */
5541# ifndef HandleToLong
Bram Moolenaara87e2c22016-02-17 20:48:19 +01005542# define HandleToLong(h) ((long)(intptr_t)(h))
Bram Moolenaarf32c5cd2016-01-10 16:07:44 +01005543# endif
Bram Moolenaar4da95d32011-07-07 17:43:41 +02005544# endif
Bram Moolenaar264e9fd2010-10-27 12:33:17 +02005545 /* set the v:windowid variable */
Bram Moolenaar7154b322011-05-25 21:18:06 +02005546 set_vim_var_nr(VV_WINDOWID, HandleToLong(s_hwnd));
Bram Moolenaar264e9fd2010-10-27 12:33:17 +02005547#endif
5548
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02005549#ifdef FEAT_RENDER_OPTIONS
5550 if (p_rop)
5551 (void)gui_mch_set_rendering_options(p_rop);
5552#endif
5553
Bram Moolenaar748bf032005-02-02 23:04:36 +00005554theend:
5555 /* Display any pending error messages */
5556 display_errors();
5557
Bram Moolenaar071d4272004-06-13 20:20:40 +00005558 return OK;
5559}
5560
5561/*
5562 * Get the size of the screen, taking position on multiple monitors into
5563 * account (if supported).
5564 */
5565 static void
5566get_work_area(RECT *spi_rect)
5567{
Bram Moolenaarcea912a2016-10-12 14:20:24 +02005568 HMONITOR mon;
5569 MONITORINFO moninfo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005570
Bram Moolenaarcea912a2016-10-12 14:20:24 +02005571 /* work out which monitor the window is on, and get *it's* work area */
Bram Moolenaar87f3d202016-12-01 20:18:50 +01005572 mon = MonitorFromWindow(s_hwnd, MONITOR_DEFAULTTOPRIMARY);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02005573 if (mon != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005574 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +02005575 moninfo.cbSize = sizeof(MONITORINFO);
5576 if (GetMonitorInfo(mon, &moninfo))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005577 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +02005578 *spi_rect = moninfo.rcWork;
5579 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005580 }
5581 }
5582 /* this is the old method... */
5583 SystemParametersInfo(SPI_GETWORKAREA, 0, spi_rect, 0);
5584}
5585
5586/*
5587 * Set the size of the window to the given width and height in pixels.
5588 */
5589 void
Bram Moolenaar1266d672017-02-01 13:43:36 +01005590gui_mch_set_shellsize(
5591 int width,
5592 int height,
5593 int min_width UNUSED,
5594 int min_height UNUSED,
5595 int base_width UNUSED,
5596 int base_height UNUSED,
Bram Moolenaarafa24992006-03-27 20:58:26 +00005597 int direction)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005598{
5599 RECT workarea_rect;
5600 int win_width, win_height;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005601 WINDOWPLACEMENT wndpl;
5602
Bram Moolenaar910f66f2006-04-05 20:41:53 +00005603 /* Try to keep window completely on screen. */
5604 /* Get position of the screen work area. This is the part that is not
5605 * used by the taskbar or appbars. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005606 get_work_area(&workarea_rect);
5607
Bram Moolenaar84a05ac2013-05-06 04:24:17 +02005608 /* Get current position of our window. Note that the .left and .top are
Bram Moolenaar910f66f2006-04-05 20:41:53 +00005609 * relative to the work area. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005610 wndpl.length = sizeof(WINDOWPLACEMENT);
5611 GetWindowPlacement(s_hwnd, &wndpl);
5612
5613 /* Resizing a maximized window looks very strange, unzoom it first.
5614 * But don't do it when still starting up, it may have been requested in
5615 * the shortcut. */
5616 if (wndpl.showCmd == SW_SHOWMAXIMIZED && starting == 0)
5617 {
5618 ShowWindow(s_hwnd, SW_SHOWNORMAL);
5619 /* Need to get the settings of the normal window. */
5620 GetWindowPlacement(s_hwnd, &wndpl);
5621 }
5622
Bram Moolenaar071d4272004-06-13 20:20:40 +00005623 /* compute the size of the outside of the window */
Bram Moolenaar9d488952013-07-21 17:53:58 +02005624 win_width = width + (GetSystemMetrics(SM_CXFRAME) +
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02005625 GetSystemMetrics(SM_CXPADDEDBORDER)) * 2;
Bram Moolenaar9d488952013-07-21 17:53:58 +02005626 win_height = height + (GetSystemMetrics(SM_CYFRAME) +
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02005627 GetSystemMetrics(SM_CXPADDEDBORDER)) * 2
Bram Moolenaar071d4272004-06-13 20:20:40 +00005628 + GetSystemMetrics(SM_CYCAPTION)
5629#ifdef FEAT_MENU
5630 + gui_mswin_get_menu_height(FALSE)
5631#endif
5632 ;
5633
Bram Moolenaar6ef47c22012-01-04 20:29:22 +01005634 /* The following should take care of keeping Vim on the same monitor, no
5635 * matter if the secondary monitor is left or right of the primary
5636 * monitor. */
5637 wndpl.rcNormalPosition.right = wndpl.rcNormalPosition.left + win_width;
5638 wndpl.rcNormalPosition.bottom = wndpl.rcNormalPosition.top + win_height;
Bram Moolenaar56a907a2006-05-06 21:44:30 +00005639
Bram Moolenaar6ef47c22012-01-04 20:29:22 +01005640 /* If the window is going off the screen, move it on to the screen. */
Bram Moolenaar910f66f2006-04-05 20:41:53 +00005641 if ((direction & RESIZE_HOR)
Bram Moolenaar6ef47c22012-01-04 20:29:22 +01005642 && wndpl.rcNormalPosition.right > workarea_rect.right)
5643 OffsetRect(&wndpl.rcNormalPosition,
5644 workarea_rect.right - wndpl.rcNormalPosition.right, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005645
Bram Moolenaar6ef47c22012-01-04 20:29:22 +01005646 if ((direction & RESIZE_HOR)
5647 && wndpl.rcNormalPosition.left < workarea_rect.left)
5648 OffsetRect(&wndpl.rcNormalPosition,
5649 workarea_rect.left - wndpl.rcNormalPosition.left, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005650
Bram Moolenaarafa24992006-03-27 20:58:26 +00005651 if ((direction & RESIZE_VERT)
Bram Moolenaar6ef47c22012-01-04 20:29:22 +01005652 && wndpl.rcNormalPosition.bottom > workarea_rect.bottom)
5653 OffsetRect(&wndpl.rcNormalPosition,
5654 0, workarea_rect.bottom - wndpl.rcNormalPosition.bottom);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005655
Bram Moolenaar6ef47c22012-01-04 20:29:22 +01005656 if ((direction & RESIZE_VERT)
5657 && wndpl.rcNormalPosition.top < workarea_rect.top)
5658 OffsetRect(&wndpl.rcNormalPosition,
5659 0, workarea_rect.top - wndpl.rcNormalPosition.top);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005660
5661 /* set window position - we should use SetWindowPlacement rather than
5662 * SetWindowPos as the MSDN docs say the coord systems returned by
5663 * these two are not compatible. */
5664 SetWindowPlacement(s_hwnd, &wndpl);
5665
5666 SetActiveWindow(s_hwnd);
5667 SetFocus(s_hwnd);
5668
5669#ifdef FEAT_MENU
5670 /* Menu may wrap differently now */
5671 gui_mswin_get_menu_height(!gui.starting);
5672#endif
5673}
5674
5675
5676 void
5677gui_mch_set_scrollbar_thumb(
5678 scrollbar_T *sb,
5679 long val,
5680 long size,
5681 long max)
5682{
5683 SCROLLINFO info;
5684
5685 sb->scroll_shift = 0;
5686 while (max > 32767)
5687 {
5688 max = (max + 1) >> 1;
5689 val >>= 1;
5690 size >>= 1;
5691 ++sb->scroll_shift;
5692 }
5693
5694 if (sb->scroll_shift > 0)
5695 ++size;
5696
5697 info.cbSize = sizeof(info);
5698 info.fMask = SIF_POS | SIF_RANGE | SIF_PAGE;
5699 info.nPos = val;
5700 info.nMin = 0;
5701 info.nMax = max;
5702 info.nPage = size;
5703 SetScrollInfo(sb->id, SB_CTL, &info, TRUE);
5704}
5705
5706
5707/*
5708 * Set the current text font.
5709 */
5710 void
5711gui_mch_set_font(GuiFont font)
5712{
5713 gui.currFont = font;
5714}
5715
5716
5717/*
5718 * Set the current text foreground color.
5719 */
5720 void
5721gui_mch_set_fg_color(guicolor_T color)
5722{
5723 gui.currFgColor = color;
5724}
5725
5726/*
5727 * Set the current text background color.
5728 */
5729 void
5730gui_mch_set_bg_color(guicolor_T color)
5731{
5732 gui.currBgColor = color;
5733}
5734
Bram Moolenaare2cc9702005-03-15 22:43:58 +00005735/*
5736 * Set the current text special color.
5737 */
5738 void
5739gui_mch_set_sp_color(guicolor_T color)
5740{
5741 gui.currSpColor = color;
5742}
5743
Bram Moolenaar071d4272004-06-13 20:20:40 +00005744#if defined(FEAT_MBYTE) && defined(FEAT_MBYTE_IME)
5745/*
5746 * Multi-byte handling, originally by Sung-Hoon Baek.
5747 * First static functions (no prototypes generated).
5748 */
5749#ifdef _MSC_VER
5750# include <ime.h> /* Apparently not needed for Cygwin, MingW or Borland. */
5751#endif
5752#include <imm.h>
5753
5754/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00005755 * handle WM_IME_NOTIFY message
5756 */
5757 static LRESULT
Bram Moolenaar1266d672017-02-01 13:43:36 +01005758_OnImeNotify(HWND hWnd, DWORD dwCommand, DWORD dwData UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005759{
5760 LRESULT lResult = 0;
5761 HIMC hImc;
5762
5763 if (!pImmGetContext || (hImc = pImmGetContext(hWnd)) == (HIMC)0)
5764 return lResult;
5765 switch (dwCommand)
5766 {
5767 case IMN_SETOPENSTATUS:
5768 if (pImmGetOpenStatus(hImc))
5769 {
5770 pImmSetCompositionFont(hImc, &norm_logfont);
5771 im_set_position(gui.row, gui.col);
5772
5773 /* Disable langmap */
5774 State &= ~LANGMAP;
5775 if (State & INSERT)
5776 {
Bram Moolenaar4033c552017-09-16 20:54:51 +02005777#if defined(FEAT_KEYMAP)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005778 /* Unshown 'keymap' in status lines */
5779 if (curbuf->b_p_iminsert == B_IMODE_LMAP)
5780 {
5781 /* Save cursor position */
5782 int old_row = gui.row;
5783 int old_col = gui.col;
5784
5785 // This must be called here before
5786 // status_redraw_curbuf(), otherwise the mode
5787 // message may appear in the wrong position.
5788 showmode();
5789 status_redraw_curbuf();
5790 update_screen(0);
5791 /* Restore cursor position */
5792 gui.row = old_row;
5793 gui.col = old_col;
5794 }
5795#endif
5796 }
5797 }
5798 gui_update_cursor(TRUE, FALSE);
5799 lResult = 0;
5800 break;
5801 }
5802 pImmReleaseContext(hWnd, hImc);
5803 return lResult;
5804}
5805
5806 static LRESULT
Bram Moolenaar1266d672017-02-01 13:43:36 +01005807_OnImeComposition(HWND hwnd, WPARAM dbcs UNUSED, LPARAM param)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005808{
5809 char_u *ret;
5810 int len;
5811
5812 if ((param & GCS_RESULTSTR) == 0) /* Composition unfinished. */
5813 return 0;
5814
5815 ret = GetResultStr(hwnd, GCS_RESULTSTR, &len);
5816 if (ret != NULL)
5817 {
5818 add_to_input_buf_csi(ret, len);
5819 vim_free(ret);
5820 return 1;
5821 }
5822 return 0;
5823}
5824
5825/*
5826 * get the current composition string, in UCS-2; *lenp is the number of
5827 * *lenp is the number of Unicode characters.
5828 */
5829 static short_u *
5830GetCompositionString_inUCS2(HIMC hIMC, DWORD GCS, int *lenp)
5831{
5832 LONG ret;
5833 LPWSTR wbuf = NULL;
5834 char_u *buf;
5835
5836 if (!pImmGetContext)
5837 return NULL; /* no imm32.dll */
5838
5839 /* Try Unicode; this'll always work on NT regardless of codepage. */
5840 ret = pImmGetCompositionStringW(hIMC, GCS, NULL, 0);
5841 if (ret == 0)
5842 return NULL; /* empty */
5843
5844 if (ret > 0)
5845 {
5846 /* Allocate the requested buffer plus space for the NUL character. */
5847 wbuf = (LPWSTR)alloc(ret + sizeof(WCHAR));
5848 if (wbuf != NULL)
5849 {
5850 pImmGetCompositionStringW(hIMC, GCS, wbuf, ret);
5851 *lenp = ret / sizeof(WCHAR);
5852 }
5853 return (short_u *)wbuf;
5854 }
5855
5856 /* ret < 0; we got an error, so try the ANSI version. This'll work
5857 * on 9x/ME, but only if the codepage happens to be set to whatever
5858 * we're inputting. */
5859 ret = pImmGetCompositionStringA(hIMC, GCS, NULL, 0);
5860 if (ret <= 0)
5861 return NULL; /* empty or error */
5862
5863 buf = alloc(ret);
5864 if (buf == NULL)
5865 return NULL;
5866 pImmGetCompositionStringA(hIMC, GCS, buf, ret);
5867
5868 /* convert from codepage to UCS-2 */
Bram Moolenaar418f81b2016-02-16 20:12:02 +01005869 MultiByteToWideChar_alloc(GetACP(), 0, (LPCSTR)buf, ret, &wbuf, lenp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005870 vim_free(buf);
5871
5872 return (short_u *)wbuf;
5873}
5874
5875/*
5876 * void GetResultStr()
5877 *
5878 * This handles WM_IME_COMPOSITION with GCS_RESULTSTR flag on.
5879 * get complete composition string
5880 */
5881 static char_u *
5882GetResultStr(HWND hwnd, int GCS, int *lenp)
5883{
5884 HIMC hIMC; /* Input context handle. */
5885 short_u *buf = NULL;
5886 char_u *convbuf = NULL;
5887
5888 if (!pImmGetContext || (hIMC = pImmGetContext(hwnd)) == (HIMC)0)
5889 return NULL;
5890
5891 /* Reads in the composition string. */
5892 buf = GetCompositionString_inUCS2(hIMC, GCS, lenp);
5893 if (buf == NULL)
5894 return NULL;
5895
Bram Moolenaar36f692d2008-11-20 16:10:17 +00005896 convbuf = utf16_to_enc(buf, lenp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005897 pImmReleaseContext(hwnd, hIMC);
5898 vim_free(buf);
5899 return convbuf;
5900}
5901#endif
5902
5903/* For global functions we need prototypes. */
5904#if (defined(FEAT_MBYTE) && defined(FEAT_MBYTE_IME)) || defined(PROTO)
5905
5906/*
5907 * set font to IM.
5908 */
5909 void
5910im_set_font(LOGFONT *lf)
5911{
5912 HIMC hImc;
5913
5914 if (pImmGetContext && (hImc = pImmGetContext(s_hwnd)) != (HIMC)0)
5915 {
5916 pImmSetCompositionFont(hImc, lf);
5917 pImmReleaseContext(s_hwnd, hImc);
5918 }
5919}
5920
5921/*
5922 * Notify cursor position to IM.
5923 */
5924 void
5925im_set_position(int row, int col)
5926{
5927 HIMC hImc;
5928
5929 if (pImmGetContext && (hImc = pImmGetContext(s_hwnd)) != (HIMC)0)
5930 {
5931 COMPOSITIONFORM cfs;
5932
5933 cfs.dwStyle = CFS_POINT;
5934 cfs.ptCurrentPos.x = FILL_X(col);
5935 cfs.ptCurrentPos.y = FILL_Y(row);
5936 MapWindowPoints(s_textArea, s_hwnd, &cfs.ptCurrentPos, 1);
5937 pImmSetCompositionWindow(hImc, &cfs);
5938
5939 pImmReleaseContext(s_hwnd, hImc);
5940 }
5941}
5942
5943/*
5944 * Set IM status on ("active" is TRUE) or off ("active" is FALSE).
5945 */
5946 void
5947im_set_active(int active)
5948{
5949 HIMC hImc;
5950 static HIMC hImcOld = (HIMC)0;
5951
5952 if (pImmGetContext) /* if NULL imm32.dll wasn't loaded (yet) */
5953 {
5954 if (p_imdisable)
5955 {
5956 if (hImcOld == (HIMC)0)
5957 {
5958 hImcOld = pImmGetContext(s_hwnd);
5959 if (hImcOld)
5960 pImmAssociateContext(s_hwnd, (HIMC)0);
5961 }
5962 active = FALSE;
5963 }
5964 else if (hImcOld != (HIMC)0)
5965 {
5966 pImmAssociateContext(s_hwnd, hImcOld);
5967 hImcOld = (HIMC)0;
5968 }
5969
5970 hImc = pImmGetContext(s_hwnd);
5971 if (hImc)
5972 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00005973 /*
5974 * for Korean ime
5975 */
5976 HKL hKL = GetKeyboardLayout(0);
5977
5978 if (LOWORD(hKL) == MAKELANGID(LANG_KOREAN, SUBLANG_KOREAN))
5979 {
5980 static DWORD dwConversionSaved = 0, dwSentenceSaved = 0;
5981 static BOOL bSaved = FALSE;
5982
5983 if (active)
5984 {
5985 /* if we have a saved conversion status, restore it */
5986 if (bSaved)
5987 pImmSetConversionStatus(hImc, dwConversionSaved,
5988 dwSentenceSaved);
5989 bSaved = FALSE;
5990 }
5991 else
5992 {
5993 /* save conversion status and disable korean */
5994 if (pImmGetConversionStatus(hImc, &dwConversionSaved,
5995 &dwSentenceSaved))
5996 {
5997 bSaved = TRUE;
5998 pImmSetConversionStatus(hImc,
5999 dwConversionSaved & ~(IME_CMODE_NATIVE
6000 | IME_CMODE_FULLSHAPE),
6001 dwSentenceSaved);
6002 }
6003 }
6004 }
6005
Bram Moolenaar071d4272004-06-13 20:20:40 +00006006 pImmSetOpenStatus(hImc, active);
6007 pImmReleaseContext(s_hwnd, hImc);
6008 }
6009 }
6010}
6011
6012/*
6013 * Get IM status. When IM is on, return not 0. Else return 0.
6014 */
6015 int
Bram Moolenaar68c2f632016-01-30 17:24:07 +01006016im_get_status(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006017{
6018 int status = 0;
6019 HIMC hImc;
6020
6021 if (pImmGetContext && (hImc = pImmGetContext(s_hwnd)) != (HIMC)0)
6022 {
6023 status = pImmGetOpenStatus(hImc) ? 1 : 0;
6024 pImmReleaseContext(s_hwnd, hImc);
6025 }
6026 return status;
6027}
6028
6029#endif /* FEAT_MBYTE && FEAT_MBYTE_IME */
6030
6031#if defined(FEAT_MBYTE) && !defined(FEAT_MBYTE_IME) && defined(GLOBAL_IME)
6032/* Win32 with GLOBAL IME */
6033
6034/*
6035 * Notify cursor position to IM.
6036 */
6037 void
6038im_set_position(int row, int col)
6039{
6040 /* Win32 with GLOBAL IME */
6041 POINT p;
6042
6043 p.x = FILL_X(col);
6044 p.y = FILL_Y(row);
6045 MapWindowPoints(s_textArea, s_hwnd, &p, 1);
6046 global_ime_set_position(&p);
6047}
6048
6049/*
6050 * Set IM status on ("active" is TRUE) or off ("active" is FALSE).
6051 */
6052 void
6053im_set_active(int active)
6054{
6055 global_ime_set_status(active);
6056}
6057
6058/*
6059 * Get IM status. When IM is on, return not 0. Else return 0.
6060 */
6061 int
Bram Moolenaard14e00e2016-01-31 17:30:51 +01006062im_get_status(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006063{
6064 return global_ime_get_status();
6065}
6066#endif
6067
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006068#ifdef FEAT_MBYTE
6069/*
Bram Moolenaar39f05632006-03-19 22:15:26 +00006070 * Convert latin9 text "text[len]" to ucs-2 in "unicodebuf".
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006071 */
6072 static void
6073latin9_to_ucs(char_u *text, int len, WCHAR *unicodebuf)
6074{
6075 int c;
6076
Bram Moolenaarca003e12006-03-17 23:19:38 +00006077 while (--len >= 0)
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006078 {
6079 c = *text++;
6080 switch (c)
6081 {
6082 case 0xa4: c = 0x20ac; break; /* euro */
6083 case 0xa6: c = 0x0160; break; /* S hat */
6084 case 0xa8: c = 0x0161; break; /* S -hat */
6085 case 0xb4: c = 0x017d; break; /* Z hat */
6086 case 0xb8: c = 0x017e; break; /* Z -hat */
6087 case 0xbc: c = 0x0152; break; /* OE */
6088 case 0xbd: c = 0x0153; break; /* oe */
6089 case 0xbe: c = 0x0178; break; /* Y */
6090 }
6091 *unicodebuf++ = c;
6092 }
6093}
6094#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006095
6096#ifdef FEAT_RIGHTLEFT
6097/*
6098 * What is this for? In the case where you are using Win98 or Win2K or later,
6099 * and you are using a Hebrew font (or Arabic!), Windows does you a favor and
6100 * reverses the string sent to the TextOut... family. This sucks, because we
6101 * go to a lot of effort to do the right thing, and there doesn't seem to be a
6102 * way to tell Windblows not to do this!
6103 *
6104 * The short of it is that this 'RevOut' only gets called if you are running
6105 * one of the new, "improved" MS OSes, and only if you are running in
6106 * 'rightleft' mode. It makes display take *slightly* longer, but not
6107 * noticeably so.
6108 */
6109 static void
6110RevOut( HDC s_hdc,
6111 int col,
6112 int row,
6113 UINT foptions,
6114 CONST RECT *pcliprect,
6115 LPCTSTR text,
6116 UINT len,
6117 CONST INT *padding)
6118{
6119 int ix;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006120
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006121 for (ix = 0; ix < (int)len; ++ix)
6122 ExtTextOut(s_hdc, col + TEXT_X(ix), row, foptions,
6123 pcliprect, text + ix, 1, padding);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006124}
6125#endif
6126
6127 void
6128gui_mch_draw_string(
6129 int row,
6130 int col,
6131 char_u *text,
6132 int len,
6133 int flags)
6134{
6135 static int *padding = NULL;
6136 static int pad_size = 0;
6137 int i;
6138 const RECT *pcliprect = NULL;
6139 UINT foptions = 0;
6140#ifdef FEAT_MBYTE
6141 static WCHAR *unicodebuf = NULL;
6142 static int *unicodepdy = NULL;
Bram Moolenaar402d2fe2005-04-15 21:00:38 +00006143 static int unibuflen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006144 int n = 0;
6145#endif
6146 HPEN hpen, old_pen;
6147 int y;
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02006148#ifdef FEAT_DIRECTX
6149 int font_is_ttf_or_vector = 0;
6150#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006151
Bram Moolenaar071d4272004-06-13 20:20:40 +00006152 /*
6153 * Italic and bold text seems to have an extra row of pixels at the bottom
6154 * (below where the bottom of the character should be). If we draw the
6155 * characters with a solid background, the top row of pixels in the
6156 * character below will be overwritten. We can fix this by filling in the
6157 * background ourselves, to the correct character proportions, and then
6158 * writing the character in transparent mode. Still have a problem when
6159 * the character is "_", which gets written on to the character below.
6160 * New fix: set gui.char_ascent to -1. This shifts all characters up one
6161 * pixel in their slots, which fixes the problem with the bottom row of
6162 * pixels. We still need this code because otherwise the top row of pixels
6163 * becomes a problem. - webb.
6164 */
6165 static HBRUSH hbr_cache[2] = {NULL, NULL};
6166 static guicolor_T brush_color[2] = {INVALCOLOR, INVALCOLOR};
6167 static int brush_lru = 0;
6168 HBRUSH hbr;
6169 RECT rc;
6170
6171 if (!(flags & DRAW_TRANSP))
6172 {
6173 /*
6174 * Clear background first.
6175 * Note: FillRect() excludes right and bottom of rectangle.
6176 */
6177 rc.left = FILL_X(col);
6178 rc.top = FILL_Y(row);
6179#ifdef FEAT_MBYTE
6180 if (has_mbyte)
6181 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006182 /* Compute the length in display cells. */
Bram Moolenaar72597a52010-07-18 15:31:08 +02006183 rc.right = FILL_X(col + mb_string2cells(text, len));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006184 }
6185 else
6186#endif
6187 rc.right = FILL_X(col + len);
6188 rc.bottom = FILL_Y(row + 1);
6189
6190 /* Cache the created brush, that saves a lot of time. We need two:
6191 * one for cursor background and one for the normal background. */
6192 if (gui.currBgColor == brush_color[0])
6193 {
6194 hbr = hbr_cache[0];
6195 brush_lru = 1;
6196 }
6197 else if (gui.currBgColor == brush_color[1])
6198 {
6199 hbr = hbr_cache[1];
6200 brush_lru = 0;
6201 }
6202 else
6203 {
6204 if (hbr_cache[brush_lru] != NULL)
6205 DeleteBrush(hbr_cache[brush_lru]);
6206 hbr_cache[brush_lru] = CreateSolidBrush(gui.currBgColor);
6207 brush_color[brush_lru] = gui.currBgColor;
6208 hbr = hbr_cache[brush_lru];
6209 brush_lru = !brush_lru;
6210 }
6211 FillRect(s_hdc, &rc, hbr);
6212
6213 SetBkMode(s_hdc, TRANSPARENT);
6214
6215 /*
6216 * When drawing block cursor, prevent inverted character spilling
6217 * over character cell (can happen with bold/italic)
6218 */
6219 if (flags & DRAW_CURSOR)
6220 {
6221 pcliprect = &rc;
6222 foptions = ETO_CLIPPED;
6223 }
6224 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006225 SetTextColor(s_hdc, gui.currFgColor);
6226 SelectFont(s_hdc, gui.currFont);
6227
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02006228#ifdef FEAT_DIRECTX
6229 if (IS_ENABLE_DIRECTX())
6230 {
6231 TEXTMETRIC tm;
6232
6233 GetTextMetrics(s_hdc, &tm);
6234 if (tm.tmPitchAndFamily & (TMPF_TRUETYPE | TMPF_VECTOR))
6235 {
6236 font_is_ttf_or_vector = 1;
6237 DWriteContext_SetFont(s_dwc, (HFONT)gui.currFont);
6238 }
6239 }
6240#endif
6241
Bram Moolenaar071d4272004-06-13 20:20:40 +00006242 if (pad_size != Columns || padding == NULL || padding[0] != gui.char_width)
6243 {
6244 vim_free(padding);
6245 pad_size = Columns;
6246
Bram Moolenaarc54b8a72005-09-30 21:20:29 +00006247 /* Don't give an out-of-memory message here, it would call us
6248 * recursively. */
6249 padding = (int *)lalloc(pad_size * sizeof(int), FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006250 if (padding != NULL)
6251 for (i = 0; i < pad_size; i++)
6252 padding[i] = gui.char_width;
6253 }
6254
Bram Moolenaar071d4272004-06-13 20:20:40 +00006255 /*
6256 * We have to provide the padding argument because italic and bold versions
6257 * of fixed-width fonts are often one pixel or so wider than their normal
6258 * versions.
6259 * No check for DRAW_BOLD, Windows will have done it already.
6260 */
6261
6262#ifdef FEAT_MBYTE
6263 /* Check if there are any UTF-8 characters. If not, use normal text
6264 * output to speed up output. */
6265 if (enc_utf8)
6266 for (n = 0; n < len; ++n)
6267 if (text[n] >= 0x80)
6268 break;
6269
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02006270#if defined(FEAT_DIRECTX)
6271 /* Quick hack to enable DirectWrite. To use DirectWrite (antialias), it is
6272 * required that unicode drawing routine, currently. So this forces it
6273 * enabled. */
6274 if (enc_utf8 && IS_ENABLE_DIRECTX())
6275 n = 0; /* Keep n < len, to enter block for unicode. */
6276#endif
6277
Bram Moolenaar071d4272004-06-13 20:20:40 +00006278 /* Check if the Unicode buffer exists and is big enough. Create it
Bram Moolenaar402d2fe2005-04-15 21:00:38 +00006279 * with the same length as the multi-byte string, the number of wide
Bram Moolenaar071d4272004-06-13 20:20:40 +00006280 * characters is always equal or smaller. */
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006281 if ((enc_utf8
6282 || (enc_codepage > 0 && (int)GetACP() != enc_codepage)
6283 || enc_latin9)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006284 && (unicodebuf == NULL || len > unibuflen))
6285 {
6286 vim_free(unicodebuf);
Bram Moolenaarc54b8a72005-09-30 21:20:29 +00006287 unicodebuf = (WCHAR *)lalloc(len * sizeof(WCHAR), FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006288
6289 vim_free(unicodepdy);
Bram Moolenaarc54b8a72005-09-30 21:20:29 +00006290 unicodepdy = (int *)lalloc(len * sizeof(int), FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006291
6292 unibuflen = len;
6293 }
6294
6295 if (enc_utf8 && n < len && unicodebuf != NULL)
6296 {
Bram Moolenaara6ce1cc2017-10-28 19:23:11 +02006297 /* Output UTF-8 characters. Composing characters should be
6298 * handled here. */
Bram Moolenaarca003e12006-03-17 23:19:38 +00006299 int i;
6300 int wlen; /* string length in words */
6301 int clen; /* string length in characters */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006302 int cells; /* cell width of string up to composing char */
6303 int cw; /* width of current cell */
Bram Moolenaar362e1a32006-03-06 23:29:24 +00006304 int c;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006305
Bram Moolenaar97b2ad32006-03-18 21:40:56 +00006306 wlen = 0;
Bram Moolenaarca003e12006-03-17 23:19:38 +00006307 clen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006308 cells = 0;
Bram Moolenaarca003e12006-03-17 23:19:38 +00006309 for (i = 0; i < len; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00006310 {
Bram Moolenaar362e1a32006-03-06 23:29:24 +00006311 c = utf_ptr2char(text + i);
6312 if (c >= 0x10000)
6313 {
6314 /* Turn into UTF-16 encoding. */
Bram Moolenaarca003e12006-03-17 23:19:38 +00006315 unicodebuf[wlen++] = ((c - 0x10000) >> 10) + 0xD800;
6316 unicodebuf[wlen++] = ((c - 0x10000) & 0x3ff) + 0xDC00;
Bram Moolenaar362e1a32006-03-06 23:29:24 +00006317 }
6318 else
6319 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00006320 unicodebuf[wlen++] = c;
Bram Moolenaar362e1a32006-03-06 23:29:24 +00006321 }
Bram Moolenaara6ce1cc2017-10-28 19:23:11 +02006322
6323 if (utf_iscomposing(c))
6324 cw = 0;
6325 else
6326 {
6327 cw = utf_char2cells(c);
6328 if (cw > 2) /* don't use 4 for unprintable char */
6329 cw = 1;
6330 }
6331
Bram Moolenaar071d4272004-06-13 20:20:40 +00006332 if (unicodepdy != NULL)
6333 {
6334 /* Use unicodepdy to make characters fit as we expect, even
6335 * when the font uses different widths (e.g., bold character
6336 * is wider). */
Bram Moolenaard804fdf2016-02-27 16:04:58 +01006337 if (c >= 0x10000)
6338 {
6339 unicodepdy[wlen - 2] = cw * gui.char_width;
6340 unicodepdy[wlen - 1] = 0;
6341 }
6342 else
6343 unicodepdy[wlen - 1] = cw * gui.char_width;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006344 }
6345 cells += cw;
Bram Moolenaara6ce1cc2017-10-28 19:23:11 +02006346 i += utf_ptr2len_len(text + i, len - i);
Bram Moolenaarca003e12006-03-17 23:19:38 +00006347 ++clen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006348 }
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02006349#if defined(FEAT_DIRECTX)
6350 if (IS_ENABLE_DIRECTX() && font_is_ttf_or_vector)
6351 {
Bram Moolenaar9b352c42014-08-06 16:49:55 +02006352 /* Add one to "cells" for italics. */
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02006353 DWriteContext_DrawText(s_dwc, s_hdc, unicodebuf, wlen,
Bram Moolenaar9b352c42014-08-06 16:49:55 +02006354 TEXT_X(col), TEXT_Y(row), FILL_X(cells + 1), FILL_Y(1),
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02006355 gui.char_width, gui.currFgColor);
6356 }
6357 else
6358#endif
6359 ExtTextOutW(s_hdc, TEXT_X(col), TEXT_Y(row),
6360 foptions, pcliprect, unicodebuf, wlen, unicodepdy);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006361 len = cells; /* used for underlining */
6362 }
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006363 else if ((enc_codepage > 0 && (int)GetACP() != enc_codepage) || enc_latin9)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006364 {
6365 /* If we want to display codepage data, and the current CP is not the
6366 * ANSI one, we need to go via Unicode. */
6367 if (unicodebuf != NULL)
6368 {
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006369 if (enc_latin9)
6370 latin9_to_ucs(text, len, unicodebuf);
6371 else
6372 len = MultiByteToWideChar(enc_codepage,
Bram Moolenaar071d4272004-06-13 20:20:40 +00006373 MB_PRECOMPOSED,
6374 (char *)text, len,
6375 (LPWSTR)unicodebuf, unibuflen);
6376 if (len != 0)
Bram Moolenaar19a09a12005-03-04 23:39:37 +00006377 {
6378 /* Use unicodepdy to make characters fit as we expect, even
6379 * when the font uses different widths (e.g., bold character
6380 * is wider). */
6381 if (unicodepdy != NULL)
6382 {
6383 int i;
6384 int cw;
6385
6386 for (i = 0; i < len; ++i)
6387 {
6388 cw = utf_char2cells(unicodebuf[i]);
6389 if (cw > 2)
6390 cw = 1;
6391 unicodepdy[i] = cw * gui.char_width;
6392 }
6393 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006394 ExtTextOutW(s_hdc, TEXT_X(col), TEXT_Y(row),
Bram Moolenaar19a09a12005-03-04 23:39:37 +00006395 foptions, pcliprect, unicodebuf, len, unicodepdy);
6396 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006397 }
6398 }
6399 else
6400#endif
6401 {
6402#ifdef FEAT_RIGHTLEFT
Bram Moolenaar4ee40b02014-09-19 16:13:53 +02006403 /* Windows will mess up RL text, so we have to draw it character by
6404 * character. Only do this if RL is on, since it's slow. */
6405 if (curwin->w_p_rl)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006406 RevOut(s_hdc, TEXT_X(col), TEXT_Y(row),
6407 foptions, pcliprect, (char *)text, len, padding);
6408 else
6409#endif
6410 ExtTextOut(s_hdc, TEXT_X(col), TEXT_Y(row),
6411 foptions, pcliprect, (char *)text, len, padding);
6412 }
6413
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006414 /* Underline */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006415 if (flags & DRAW_UNDERL)
6416 {
6417 hpen = CreatePen(PS_SOLID, 1, gui.currFgColor);
6418 old_pen = SelectObject(s_hdc, hpen);
6419 /* When p_linespace is 0, overwrite the bottom row of pixels.
6420 * Otherwise put the line just below the character. */
6421 y = FILL_Y(row + 1) - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006422 if (p_linespace > 1)
6423 y -= p_linespace - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006424 MoveToEx(s_hdc, FILL_X(col), y, NULL);
6425 /* Note: LineTo() excludes the last pixel in the line. */
6426 LineTo(s_hdc, FILL_X(col + len), y);
6427 DeleteObject(SelectObject(s_hdc, old_pen));
6428 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006429
Bram Moolenaarcf4b00c2017-09-02 18:33:56 +02006430 /* Strikethrough */
6431 if (flags & DRAW_STRIKE)
6432 {
6433 hpen = CreatePen(PS_SOLID, 1, gui.currSpColor);
6434 old_pen = SelectObject(s_hdc, hpen);
6435 y = FILL_Y(row + 1) - gui.char_height/2;
6436 MoveToEx(s_hdc, FILL_X(col), y, NULL);
6437 /* Note: LineTo() excludes the last pixel in the line. */
6438 LineTo(s_hdc, FILL_X(col + len), y);
6439 DeleteObject(SelectObject(s_hdc, old_pen));
6440 }
6441
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006442 /* Undercurl */
6443 if (flags & DRAW_UNDERC)
6444 {
6445 int x;
6446 int offset;
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +00006447 static const int val[8] = {1, 0, 0, 0, 1, 2, 2, 2 };
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006448
6449 y = FILL_Y(row + 1) - 1;
6450 for (x = FILL_X(col); x < FILL_X(col + len); ++x)
6451 {
6452 offset = val[x % 8];
6453 SetPixel(s_hdc, x, y - offset, gui.currSpColor);
6454 }
6455 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006456}
6457
6458
6459/*
6460 * Output routines.
6461 */
6462
6463/* Flush any output to the screen */
6464 void
6465gui_mch_flush(void)
6466{
6467# if defined(__BORLANDC__)
6468 /*
6469 * The GdiFlush declaration (in Borland C 5.01 <wingdi.h>) is not a
6470 * prototype declaration.
6471 * The compiler complains if __stdcall is not used in both declarations.
6472 */
6473 BOOL __stdcall GdiFlush(void);
6474# endif
6475
6476 GdiFlush();
6477}
6478
6479 static void
6480clear_rect(RECT *rcp)
6481{
6482 HBRUSH hbr;
6483
6484 hbr = CreateSolidBrush(gui.back_pixel);
6485 FillRect(s_hdc, rcp, hbr);
6486 DeleteBrush(hbr);
6487}
6488
6489
Bram Moolenaarc716c302006-01-21 22:12:51 +00006490 void
6491gui_mch_get_screen_dimensions(int *screen_w, int *screen_h)
6492{
6493 RECT workarea_rect;
6494
6495 get_work_area(&workarea_rect);
6496
Bram Moolenaar910f66f2006-04-05 20:41:53 +00006497 *screen_w = workarea_rect.right - workarea_rect.left
Bram Moolenaar9d488952013-07-21 17:53:58 +02006498 - (GetSystemMetrics(SM_CXFRAME) +
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02006499 GetSystemMetrics(SM_CXPADDEDBORDER)) * 2;
Bram Moolenaarc716c302006-01-21 22:12:51 +00006500
6501 /* FIXME: dirty trick: Because the gui_get_base_height() doesn't include
6502 * the menubar for MSwin, we subtract it from the screen height, so that
6503 * the window size can be made to fit on the screen. */
Bram Moolenaar910f66f2006-04-05 20:41:53 +00006504 *screen_h = workarea_rect.bottom - workarea_rect.top
Bram Moolenaar9d488952013-07-21 17:53:58 +02006505 - (GetSystemMetrics(SM_CYFRAME) +
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02006506 GetSystemMetrics(SM_CXPADDEDBORDER)) * 2
Bram Moolenaarc716c302006-01-21 22:12:51 +00006507 - GetSystemMetrics(SM_CYCAPTION)
6508#ifdef FEAT_MENU
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00006509 - gui_mswin_get_menu_height(FALSE)
Bram Moolenaarc716c302006-01-21 22:12:51 +00006510#endif
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00006511 ;
Bram Moolenaarc716c302006-01-21 22:12:51 +00006512}
6513
6514
Bram Moolenaar071d4272004-06-13 20:20:40 +00006515#if defined(FEAT_MENU) || defined(PROTO)
6516/*
6517 * Add a sub menu to the menu bar.
6518 */
6519 void
6520gui_mch_add_menu(
6521 vimmenu_T *menu,
6522 int pos)
6523{
6524 vimmenu_T *parent = menu->parent;
6525
6526 menu->submenu_id = CreatePopupMenu();
6527 menu->id = s_menu_id++;
6528
6529 if (menu_is_menubar(menu->name))
6530 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006531#ifdef FEAT_MBYTE
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006532 WCHAR *wn = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006533
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006534 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
6535 {
6536 /* 'encoding' differs from active codepage: convert menu name
6537 * and use wide function */
6538 wn = enc_to_utf16(menu->name, NULL);
6539 if (wn != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006540 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006541 MENUITEMINFOW infow;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006542
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006543 infow.cbSize = sizeof(infow);
6544 infow.fMask = MIIM_DATA | MIIM_TYPE | MIIM_ID
6545 | MIIM_SUBMENU;
6546 infow.dwItemData = (long_u)menu;
6547 infow.wID = menu->id;
6548 infow.fType = MFT_STRING;
6549 infow.dwTypeData = wn;
6550 infow.cch = (UINT)wcslen(wn);
6551 infow.hSubMenu = menu->submenu_id;
6552 InsertMenuItemW((parent == NULL)
6553 ? s_menuBar : parent->submenu_id,
6554 (UINT)pos, TRUE, &infow);
6555 vim_free(wn);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006556 }
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006557 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006558
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006559 if (wn == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006560#endif
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006561 {
6562 MENUITEMINFO info;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006563
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006564 info.cbSize = sizeof(info);
6565 info.fMask = MIIM_DATA | MIIM_TYPE | MIIM_ID | MIIM_SUBMENU;
6566 info.dwItemData = (long_u)menu;
6567 info.wID = menu->id;
6568 info.fType = MFT_STRING;
6569 info.dwTypeData = (LPTSTR)menu->name;
6570 info.cch = (UINT)STRLEN(menu->name);
6571 info.hSubMenu = menu->submenu_id;
6572 InsertMenuItem((parent == NULL)
6573 ? s_menuBar : parent->submenu_id,
6574 (UINT)pos, TRUE, &info);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006575 }
6576 }
6577
6578 /* Fix window size if menu may have wrapped */
6579 if (parent == NULL)
6580 gui_mswin_get_menu_height(!gui.starting);
6581#ifdef FEAT_TEAROFF
6582 else if (IsWindow(parent->tearoff_handle))
6583 rebuild_tearoff(parent);
6584#endif
6585}
6586
6587 void
6588gui_mch_show_popupmenu(vimmenu_T *menu)
6589{
6590 POINT mp;
6591
6592 (void)GetCursorPos((LPPOINT)&mp);
6593 gui_mch_show_popupmenu_at(menu, (int)mp.x, (int)mp.y);
6594}
6595
6596 void
Bram Moolenaar045e82d2005-07-08 22:25:33 +00006597gui_make_popup(char_u *path_name, int mouse_pos)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006598{
6599 vimmenu_T *menu = gui_find_menu(path_name);
6600
6601 if (menu != NULL)
6602 {
6603 POINT p;
6604
6605 /* Find the position of the current cursor */
6606 GetDCOrgEx(s_hdc, &p);
Bram Moolenaar045e82d2005-07-08 22:25:33 +00006607 if (mouse_pos)
6608 {
6609 int mx, my;
6610
6611 gui_mch_getmouse(&mx, &my);
6612 p.x += mx;
6613 p.y += my;
6614 }
6615 else if (curwin != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006616 {
Bram Moolenaar53f81742017-09-22 14:35:51 +02006617 p.x += TEXT_X(curwin->w_wincol + curwin->w_wcol + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006618 p.y += TEXT_Y(W_WINROW(curwin) + curwin->w_wrow + 1);
6619 }
6620 msg_scroll = FALSE;
6621 gui_mch_show_popupmenu_at(menu, (int)p.x, (int)p.y);
6622 }
6623}
6624
6625#if defined(FEAT_TEAROFF) || defined(PROTO)
6626/*
6627 * Given a menu descriptor, e.g. "File.New", find it in the menu hierarchy and
6628 * create it as a pseudo-"tearoff menu".
6629 */
6630 void
6631gui_make_tearoff(char_u *path_name)
6632{
6633 vimmenu_T *menu = gui_find_menu(path_name);
6634
6635 /* Found the menu, so tear it off. */
6636 if (menu != NULL)
6637 gui_mch_tearoff(menu->dname, menu, 0xffffL, 0xffffL);
6638}
6639#endif
6640
6641/*
6642 * Add a menu item to a menu
6643 */
6644 void
6645gui_mch_add_menu_item(
6646 vimmenu_T *menu,
6647 int idx)
6648{
6649 vimmenu_T *parent = menu->parent;
6650
6651 menu->id = s_menu_id++;
6652 menu->submenu_id = NULL;
6653
6654#ifdef FEAT_TEAROFF
6655 if (STRNCMP(menu->name, TEAR_STRING, TEAR_LEN) == 0)
6656 {
6657 InsertMenu(parent->submenu_id, (UINT)idx, MF_BITMAP|MF_BYPOSITION,
6658 (UINT)menu->id, (LPCTSTR) s_htearbitmap);
6659 }
6660 else
6661#endif
6662#ifdef FEAT_TOOLBAR
6663 if (menu_is_toolbar(parent->name))
6664 {
6665 TBBUTTON newtb;
6666
6667 vim_memset(&newtb, 0, sizeof(newtb));
6668 if (menu_is_separator(menu->name))
6669 {
6670 newtb.iBitmap = 0;
6671 newtb.fsStyle = TBSTYLE_SEP;
6672 }
6673 else
6674 {
6675 newtb.iBitmap = get_toolbar_bitmap(menu);
6676 newtb.fsStyle = TBSTYLE_BUTTON;
6677 }
6678 newtb.idCommand = menu->id;
6679 newtb.fsState = TBSTATE_ENABLED;
6680 newtb.iString = 0;
6681 SendMessage(s_toolbarhwnd, TB_INSERTBUTTON, (WPARAM)idx,
6682 (LPARAM)&newtb);
6683 menu->submenu_id = (HMENU)-1;
6684 }
6685 else
6686#endif
6687 {
6688#ifdef FEAT_MBYTE
6689 WCHAR *wn = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006690
6691 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
6692 {
6693 /* 'encoding' differs from active codepage: convert menu item name
6694 * and use wide function */
Bram Moolenaar36f692d2008-11-20 16:10:17 +00006695 wn = enc_to_utf16(menu->name, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006696 if (wn != NULL)
6697 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006698 InsertMenuW(parent->submenu_id, (UINT)idx,
Bram Moolenaar071d4272004-06-13 20:20:40 +00006699 (menu_is_separator(menu->name)
6700 ? MF_SEPARATOR : MF_STRING) | MF_BYPOSITION,
6701 (UINT)menu->id, wn);
6702 vim_free(wn);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006703 }
6704 }
6705 if (wn == NULL)
6706#endif
6707 InsertMenu(parent->submenu_id, (UINT)idx,
6708 (menu_is_separator(menu->name) ? MF_SEPARATOR : MF_STRING)
6709 | MF_BYPOSITION,
6710 (UINT)menu->id, (LPCTSTR)menu->name);
6711#ifdef FEAT_TEAROFF
6712 if (IsWindow(parent->tearoff_handle))
6713 rebuild_tearoff(parent);
6714#endif
6715 }
6716}
6717
6718/*
6719 * Destroy the machine specific menu widget.
6720 */
6721 void
6722gui_mch_destroy_menu(vimmenu_T *menu)
6723{
6724#ifdef FEAT_TOOLBAR
6725 /*
6726 * is this a toolbar button?
6727 */
6728 if (menu->submenu_id == (HMENU)-1)
6729 {
6730 int iButton;
6731
6732 iButton = (int)SendMessage(s_toolbarhwnd, TB_COMMANDTOINDEX,
6733 (WPARAM)menu->id, 0);
6734 SendMessage(s_toolbarhwnd, TB_DELETEBUTTON, (WPARAM)iButton, 0);
6735 }
6736 else
6737#endif
6738 {
6739 if (menu->parent != NULL
6740 && menu_is_popup(menu->parent->dname)
6741 && menu->parent->submenu_id != NULL)
6742 RemoveMenu(menu->parent->submenu_id, menu->id, MF_BYCOMMAND);
6743 else
6744 RemoveMenu(s_menuBar, menu->id, MF_BYCOMMAND);
6745 if (menu->submenu_id != NULL)
6746 DestroyMenu(menu->submenu_id);
6747#ifdef FEAT_TEAROFF
6748 if (IsWindow(menu->tearoff_handle))
6749 DestroyWindow(menu->tearoff_handle);
6750 if (menu->parent != NULL
6751 && menu->parent->children != NULL
6752 && IsWindow(menu->parent->tearoff_handle))
6753 {
6754 /* This menu must not show up when rebuilding the tearoff window. */
6755 menu->modes = 0;
6756 rebuild_tearoff(menu->parent);
6757 }
6758#endif
6759 }
6760}
6761
6762#ifdef FEAT_TEAROFF
6763 static void
6764rebuild_tearoff(vimmenu_T *menu)
6765{
6766 /*hackish*/
6767 char_u tbuf[128];
6768 RECT trect;
6769 RECT rct;
6770 RECT roct;
6771 int x, y;
6772
6773 HWND thwnd = menu->tearoff_handle;
6774
Bram Moolenaar418f81b2016-02-16 20:12:02 +01006775 GetWindowText(thwnd, (LPSTR)tbuf, 127);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006776 if (GetWindowRect(thwnd, &trect)
6777 && GetWindowRect(s_hwnd, &rct)
6778 && GetClientRect(s_hwnd, &roct))
6779 {
6780 x = trect.left - rct.left;
6781 y = (trect.top - rct.bottom + roct.bottom);
6782 }
6783 else
6784 {
6785 x = y = 0xffffL;
6786 }
6787 DestroyWindow(thwnd);
6788 if (menu->children != NULL)
6789 {
6790 gui_mch_tearoff(tbuf, menu, x, y);
6791 if (IsWindow(menu->tearoff_handle))
6792 (void) SetWindowPos(menu->tearoff_handle,
6793 NULL,
6794 (int)trect.left,
6795 (int)trect.top,
6796 0, 0,
6797 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
6798 }
6799}
6800#endif /* FEAT_TEAROFF */
6801
6802/*
6803 * Make a menu either grey or not grey.
6804 */
6805 void
6806gui_mch_menu_grey(
6807 vimmenu_T *menu,
6808 int grey)
6809{
6810#ifdef FEAT_TOOLBAR
6811 /*
6812 * is this a toolbar button?
6813 */
6814 if (menu->submenu_id == (HMENU)-1)
6815 {
6816 SendMessage(s_toolbarhwnd, TB_ENABLEBUTTON,
6817 (WPARAM)menu->id, (LPARAM) MAKELONG((grey ? FALSE : TRUE), 0) );
6818 }
6819 else
6820#endif
Bram Moolenaar762f1752016-06-04 22:36:17 +02006821 (void)EnableMenuItem(menu->parent ? menu->parent->submenu_id : s_menuBar,
6822 menu->id, MF_BYCOMMAND | (grey ? MF_GRAYED : MF_ENABLED));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006823
6824#ifdef FEAT_TEAROFF
6825 if ((menu->parent != NULL) && (IsWindow(menu->parent->tearoff_handle)))
6826 {
6827 WORD menuID;
6828 HWND menuHandle;
6829
6830 /*
6831 * A tearoff button has changed state.
6832 */
6833 if (menu->children == NULL)
6834 menuID = (WORD)(menu->id);
6835 else
Bram Moolenaareb3593b2006-04-22 22:33:57 +00006836 menuID = (WORD)((long_u)(menu->submenu_id) | (DWORD)0x8000);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006837 menuHandle = GetDlgItem(menu->parent->tearoff_handle, menuID);
6838 if (menuHandle)
6839 EnableWindow(menuHandle, !grey);
6840
6841 }
6842#endif
6843}
6844
6845#endif /* FEAT_MENU */
6846
6847
6848/* define some macros used to make the dialogue creation more readable */
6849
6850#define add_string(s) strcpy((LPSTR)p, s); (LPSTR)p += (strlen((LPSTR)p) + 1)
6851#define add_word(x) *p++ = (x)
Bram Moolenaarf4cd3e82005-12-22 22:47:02 +00006852#define add_long(x) dwp = (DWORD *)p; *dwp++ = (x); p = (WORD *)dwp
Bram Moolenaar071d4272004-06-13 20:20:40 +00006853
6854#if defined(FEAT_GUI_DIALOG) || defined(PROTO)
6855/*
6856 * stuff for dialogs
6857 */
6858
6859/*
6860 * The callback routine used by all the dialogs. Very simple. First,
6861 * acknowledges the INITDIALOG message so that Windows knows to do standard
6862 * dialog stuff (Return = default, Esc = cancel....) Second, if a button is
6863 * pressed, return that button's ID - IDCANCEL (2), which is the button's
6864 * number.
6865 */
6866 static LRESULT CALLBACK
6867dialog_callback(
6868 HWND hwnd,
6869 UINT message,
6870 WPARAM wParam,
Bram Moolenaar1266d672017-02-01 13:43:36 +01006871 LPARAM lParam UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006872{
6873 if (message == WM_INITDIALOG)
6874 {
6875 CenterWindow(hwnd, GetWindow(hwnd, GW_OWNER));
6876 /* Set focus to the dialog. Set the default button, if specified. */
6877 (void)SetFocus(hwnd);
6878 if (dialog_default_button > IDCANCEL)
6879 (void)SetFocus(GetDlgItem(hwnd, dialog_default_button));
Bram Moolenaar2b80e652007-08-14 14:57:55 +00006880 else
6881 /* We don't have a default, set focus on another element of the
6882 * dialog window, probably the icon */
6883 (void)SetFocus(GetDlgItem(hwnd, DLG_NONBUTTON_CONTROL));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006884 return FALSE;
6885 }
6886
6887 if (message == WM_COMMAND)
6888 {
6889 int button = LOWORD(wParam);
6890
6891 /* Don't end the dialog if something was selected that was
6892 * not a button.
6893 */
6894 if (button >= DLG_NONBUTTON_CONTROL)
6895 return TRUE;
6896
6897 /* If the edit box exists, copy the string. */
6898 if (s_textfield != NULL)
Bram Moolenaar3ca9a8a2009-01-28 20:23:17 +00006899 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006900# ifdef FEAT_MBYTE
Bram Moolenaar3ca9a8a2009-01-28 20:23:17 +00006901 /* If the OS is Windows NT, and 'encoding' differs from active
6902 * codepage: use wide function and convert text. */
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006903 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
Bram Moolenaarcc448b32010-07-14 16:52:17 +02006904 {
Bram Moolenaar3ca9a8a2009-01-28 20:23:17 +00006905 WCHAR *wp = (WCHAR *)alloc(IOSIZE * sizeof(WCHAR));
6906 char_u *p;
6907
6908 GetDlgItemTextW(hwnd, DLG_NONBUTTON_CONTROL + 2, wp, IOSIZE);
6909 p = utf16_to_enc(wp, NULL);
6910 vim_strncpy(s_textfield, p, IOSIZE);
6911 vim_free(p);
6912 vim_free(wp);
6913 }
6914 else
6915# endif
6916 GetDlgItemText(hwnd, DLG_NONBUTTON_CONTROL + 2,
Bram Moolenaar418f81b2016-02-16 20:12:02 +01006917 (LPSTR)s_textfield, IOSIZE);
Bram Moolenaar3ca9a8a2009-01-28 20:23:17 +00006918 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006919
6920 /*
6921 * Need to check for IDOK because if the user just hits Return to
6922 * accept the default value, some reason this is what we get.
6923 */
6924 if (button == IDOK)
6925 {
6926 if (dialog_default_button > IDCANCEL)
6927 EndDialog(hwnd, dialog_default_button);
6928 }
6929 else
6930 EndDialog(hwnd, button - IDCANCEL);
6931 return TRUE;
6932 }
6933
6934 if ((message == WM_SYSCOMMAND) && (wParam == SC_CLOSE))
6935 {
6936 EndDialog(hwnd, 0);
6937 return TRUE;
6938 }
6939 return FALSE;
6940}
6941
6942/*
6943 * Create a dialog dynamically from the parameter strings.
6944 * type = type of dialog (question, alert, etc.)
6945 * title = dialog title. may be NULL for default title.
6946 * message = text to display. Dialog sizes to accommodate it.
6947 * buttons = '\n' separated list of button captions, default first.
6948 * dfltbutton = number of default button.
6949 *
6950 * This routine returns 1 if the first button is pressed,
6951 * 2 for the second, etc.
6952 *
6953 * 0 indicates Esc was pressed.
6954 * -1 for unexpected error
6955 *
6956 * If stubbing out this fn, return 1.
6957 */
6958
Bram Moolenaar418f81b2016-02-16 20:12:02 +01006959static const char *dlg_icons[] = /* must match names in resource file */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006960{
6961 "IDR_VIM",
6962 "IDR_VIM_ERROR",
6963 "IDR_VIM_ALERT",
6964 "IDR_VIM_INFO",
6965 "IDR_VIM_QUESTION"
6966};
6967
Bram Moolenaar071d4272004-06-13 20:20:40 +00006968 int
6969gui_mch_dialog(
6970 int type,
6971 char_u *title,
6972 char_u *message,
6973 char_u *buttons,
6974 int dfltbutton,
Bram Moolenaard2c340a2011-01-17 20:08:11 +01006975 char_u *textfield,
6976 int ex_cmd)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006977{
6978 WORD *p, *pdlgtemplate, *pnumitems;
Bram Moolenaarf4cd3e82005-12-22 22:47:02 +00006979 DWORD *dwp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006980 int numButtons;
6981 int *buttonWidths, *buttonPositions;
6982 int buttonYpos;
6983 int nchar, i;
6984 DWORD lStyle;
6985 int dlgwidth = 0;
6986 int dlgheight;
6987 int editboxheight;
6988 int horizWidth = 0;
6989 int msgheight;
6990 char_u *pstart;
6991 char_u *pend;
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00006992 char_u *last_white;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006993 char_u *tbuffer;
6994 RECT rect;
6995 HWND hwnd;
6996 HDC hdc;
6997 HFONT font, oldFont;
6998 TEXTMETRIC fontInfo;
6999 int fontHeight;
7000 int textWidth, minButtonWidth, messageWidth;
7001 int maxDialogWidth;
Bram Moolenaar748bf032005-02-02 23:04:36 +00007002 int maxDialogHeight;
7003 int scroll_flag = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007004 int vertical;
7005 int dlgPaddingX;
7006 int dlgPaddingY;
7007#ifdef USE_SYSMENU_FONT
7008 LOGFONT lfSysmenu;
7009 int use_lfSysmenu = FALSE;
7010#endif
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007011 garray_T ga;
7012 int l;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007013
7014#ifndef NO_CONSOLE
7015 /* Don't output anything in silent mode ("ex -s") */
7016 if (silent_mode)
7017 return dfltbutton; /* return default option */
7018#endif
7019
Bram Moolenaar748bf032005-02-02 23:04:36 +00007020 if (s_hwnd == NULL)
7021 get_dialog_font_metrics();
Bram Moolenaar071d4272004-06-13 20:20:40 +00007022
7023 if ((type < 0) || (type > VIM_LAST_TYPE))
7024 type = 0;
7025
7026 /* allocate some memory for dialog template */
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00007027 /* TODO should compute this really */
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007028 pdlgtemplate = p = (PWORD)LocalAlloc(LPTR,
Bram Moolenaareb3593b2006-04-22 22:33:57 +00007029 DLG_ALLOC_SIZE + STRLEN(message) * 2);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007030
7031 if (p == NULL)
7032 return -1;
7033
7034 /*
Bram Moolenaar84a05ac2013-05-06 04:24:17 +02007035 * make a copy of 'buttons' to fiddle with it. compiler grizzles because
Bram Moolenaar071d4272004-06-13 20:20:40 +00007036 * vim_strsave() doesn't take a const arg (why not?), so cast away the
7037 * const.
7038 */
7039 tbuffer = vim_strsave(buttons);
7040 if (tbuffer == NULL)
7041 return -1;
7042
7043 --dfltbutton; /* Change from one-based to zero-based */
7044
7045 /* Count buttons */
7046 numButtons = 1;
7047 for (i = 0; tbuffer[i] != '\0'; i++)
7048 {
7049 if (tbuffer[i] == DLG_BUTTON_SEP)
7050 numButtons++;
7051 }
7052 if (dfltbutton >= numButtons)
7053 dfltbutton = -1;
7054
7055 /* Allocate array to hold the width of each button */
Bram Moolenaarc54b8a72005-09-30 21:20:29 +00007056 buttonWidths = (int *)lalloc(numButtons * sizeof(int), TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007057 if (buttonWidths == NULL)
7058 return -1;
7059
7060 /* Allocate array to hold the X position of each button */
Bram Moolenaarc54b8a72005-09-30 21:20:29 +00007061 buttonPositions = (int *)lalloc(numButtons * sizeof(int), TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007062 if (buttonPositions == NULL)
7063 return -1;
7064
7065 /*
7066 * Calculate how big the dialog must be.
7067 */
7068 hwnd = GetDesktopWindow();
7069 hdc = GetWindowDC(hwnd);
7070#ifdef USE_SYSMENU_FONT
7071 if (gui_w32_get_menu_font(&lfSysmenu) == OK)
7072 {
7073 font = CreateFontIndirect(&lfSysmenu);
7074 use_lfSysmenu = TRUE;
7075 }
7076 else
7077#endif
7078 font = CreateFont(-DLG_FONT_POINT_SIZE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
7079 VARIABLE_PITCH , DLG_FONT_NAME);
7080 if (s_usenewlook)
7081 {
7082 oldFont = SelectFont(hdc, font);
7083 dlgPaddingX = DLG_PADDING_X;
7084 dlgPaddingY = DLG_PADDING_Y;
7085 }
7086 else
7087 {
7088 oldFont = SelectFont(hdc, GetStockObject(SYSTEM_FONT));
7089 dlgPaddingX = DLG_OLD_STYLE_PADDING_X;
7090 dlgPaddingY = DLG_OLD_STYLE_PADDING_Y;
7091 }
7092 GetTextMetrics(hdc, &fontInfo);
7093 fontHeight = fontInfo.tmHeight;
7094
7095 /* Minimum width for horizontal button */
Bram Moolenaar418f81b2016-02-16 20:12:02 +01007096 minButtonWidth = GetTextWidth(hdc, (char_u *)"Cancel", 6);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007097
7098 /* Maximum width of a dialog, if possible */
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007099 if (s_hwnd == NULL)
7100 {
7101 RECT workarea_rect;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007102
Bram Moolenaarc716c302006-01-21 22:12:51 +00007103 /* We don't have a window, use the desktop area. */
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007104 get_work_area(&workarea_rect);
7105 maxDialogWidth = workarea_rect.right - workarea_rect.left - 100;
7106 if (maxDialogWidth > 600)
7107 maxDialogWidth = 600;
Bram Moolenaar1b1b0942013-08-01 13:20:42 +02007108 /* Leave some room for the taskbar. */
7109 maxDialogHeight = workarea_rect.bottom - workarea_rect.top - 150;
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007110 }
7111 else
7112 {
Bram Moolenaara95d8232013-08-07 15:27:11 +02007113 /* Use our own window for the size, unless it's very small. */
7114 GetWindowRect(s_hwnd, &rect);
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007115 maxDialogWidth = rect.right - rect.left
Bram Moolenaar9d488952013-07-21 17:53:58 +02007116 - (GetSystemMetrics(SM_CXFRAME) +
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02007117 GetSystemMetrics(SM_CXPADDEDBORDER)) * 2;
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007118 if (maxDialogWidth < DLG_MIN_MAX_WIDTH)
7119 maxDialogWidth = DLG_MIN_MAX_WIDTH;
Bram Moolenaar748bf032005-02-02 23:04:36 +00007120
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007121 maxDialogHeight = rect.bottom - rect.top
Bram Moolenaar1b1b0942013-08-01 13:20:42 +02007122 - (GetSystemMetrics(SM_CYFRAME) +
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02007123 GetSystemMetrics(SM_CXPADDEDBORDER)) * 4
Bram Moolenaara95d8232013-08-07 15:27:11 +02007124 - GetSystemMetrics(SM_CYCAPTION);
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007125 if (maxDialogHeight < DLG_MIN_MAX_HEIGHT)
7126 maxDialogHeight = DLG_MIN_MAX_HEIGHT;
7127 }
7128
7129 /* Set dlgwidth to width of message.
7130 * Copy the message into "ga", changing NL to CR-NL and inserting line
7131 * breaks where needed. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007132 pstart = message;
7133 messageWidth = 0;
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007134 msgheight = 0;
7135 ga_init2(&ga, sizeof(char), 500);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007136 do
7137 {
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007138 msgheight += fontHeight; /* at least one line */
7139
7140 /* Need to figure out where to break the string. The system does it
7141 * at a word boundary, which would mean we can't compute the number of
7142 * wrapped lines. */
7143 textWidth = 0;
7144 last_white = NULL;
7145 for (pend = pstart; *pend != NUL && *pend != '\n'; )
Bram Moolenaar748bf032005-02-02 23:04:36 +00007146 {
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007147#ifdef FEAT_MBYTE
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00007148 l = (*mb_ptr2len)(pend);
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007149#else
7150 l = 1;
7151#endif
Bram Moolenaar1c465442017-03-12 20:10:05 +01007152 if (l == 1 && VIM_ISWHITE(*pend)
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007153 && textWidth > maxDialogWidth * 3 / 4)
7154 last_white = pend;
Bram Moolenaarf05d8112013-06-26 12:58:32 +02007155 textWidth += GetTextWidthEnc(hdc, pend, l);
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007156 if (textWidth >= maxDialogWidth)
Bram Moolenaar748bf032005-02-02 23:04:36 +00007157 {
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007158 /* Line will wrap. */
7159 messageWidth = maxDialogWidth;
Bram Moolenaar748bf032005-02-02 23:04:36 +00007160 msgheight += fontHeight;
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007161 textWidth = 0;
7162
7163 if (last_white != NULL)
7164 {
7165 /* break the line just after a space */
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00007166 ga.ga_len -= (int)(pend - (last_white + 1));
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007167 pend = last_white + 1;
7168 last_white = NULL;
7169 }
7170 ga_append(&ga, '\r');
7171 ga_append(&ga, '\n');
7172 continue;
Bram Moolenaar748bf032005-02-02 23:04:36 +00007173 }
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007174
7175 while (--l >= 0)
7176 ga_append(&ga, *pend++);
Bram Moolenaar748bf032005-02-02 23:04:36 +00007177 }
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007178 if (textWidth > messageWidth)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007179 messageWidth = textWidth;
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007180
7181 ga_append(&ga, '\r');
7182 ga_append(&ga, '\n');
Bram Moolenaar071d4272004-06-13 20:20:40 +00007183 pstart = pend + 1;
7184 } while (*pend != NUL);
Bram Moolenaar748bf032005-02-02 23:04:36 +00007185
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007186 if (ga.ga_data != NULL)
7187 message = ga.ga_data;
7188
Bram Moolenaar748bf032005-02-02 23:04:36 +00007189 messageWidth += 10; /* roundoff space */
7190
Bram Moolenaar071d4272004-06-13 20:20:40 +00007191 /* Add width of icon to dlgwidth, and some space */
Bram Moolenaara95d8232013-08-07 15:27:11 +02007192 dlgwidth = messageWidth + DLG_ICON_WIDTH + 3 * dlgPaddingX
7193 + GetSystemMetrics(SM_CXVSCROLL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007194
7195 if (msgheight < DLG_ICON_HEIGHT)
7196 msgheight = DLG_ICON_HEIGHT;
7197
7198 /*
7199 * Check button names. A long one will make the dialog wider.
Bram Moolenaaraea02fa2007-05-04 20:29:09 +00007200 * When called early (-register error message) p_go isn't initialized.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007201 */
Bram Moolenaaraea02fa2007-05-04 20:29:09 +00007202 vertical = (p_go != NULL && vim_strchr(p_go, GO_VERTICAL) != NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007203 if (!vertical)
7204 {
7205 // Place buttons horizontally if they fit.
7206 horizWidth = dlgPaddingX;
7207 pstart = tbuffer;
7208 i = 0;
7209 do
7210 {
7211 pend = vim_strchr(pstart, DLG_BUTTON_SEP);
7212 if (pend == NULL)
7213 pend = pstart + STRLEN(pstart); // Last button name.
Bram Moolenaarb052fe02013-06-26 13:16:20 +02007214 textWidth = GetTextWidthEnc(hdc, pstart, (int)(pend - pstart));
Bram Moolenaar071d4272004-06-13 20:20:40 +00007215 if (textWidth < minButtonWidth)
7216 textWidth = minButtonWidth;
7217 textWidth += dlgPaddingX; /* Padding within button */
7218 buttonWidths[i] = textWidth;
7219 buttonPositions[i++] = horizWidth;
7220 horizWidth += textWidth + dlgPaddingX; /* Pad between buttons */
7221 pstart = pend + 1;
7222 } while (*pend != NUL);
7223
7224 if (horizWidth > maxDialogWidth)
7225 vertical = TRUE; // Too wide to fit on the screen.
7226 else if (horizWidth > dlgwidth)
7227 dlgwidth = horizWidth;
7228 }
7229
7230 if (vertical)
7231 {
7232 // Stack buttons vertically.
7233 pstart = tbuffer;
7234 do
7235 {
7236 pend = vim_strchr(pstart, DLG_BUTTON_SEP);
7237 if (pend == NULL)
7238 pend = pstart + STRLEN(pstart); // Last button name.
Bram Moolenaarb052fe02013-06-26 13:16:20 +02007239 textWidth = GetTextWidthEnc(hdc, pstart, (int)(pend - pstart));
Bram Moolenaar071d4272004-06-13 20:20:40 +00007240 textWidth += dlgPaddingX; /* Padding within button */
7241 textWidth += DLG_VERT_PADDING_X * 2; /* Padding around button */
7242 if (textWidth > dlgwidth)
7243 dlgwidth = textWidth;
7244 pstart = pend + 1;
7245 } while (*pend != NUL);
7246 }
7247
7248 if (dlgwidth < DLG_MIN_WIDTH)
7249 dlgwidth = DLG_MIN_WIDTH; /* Don't allow a really thin dialog!*/
7250
7251 /* start to fill in the dlgtemplate information. addressing by WORDs */
7252 if (s_usenewlook)
7253 lStyle = DS_MODALFRAME | WS_CAPTION |DS_3DLOOK| WS_VISIBLE |DS_SETFONT;
7254 else
7255 lStyle = DS_MODALFRAME | WS_CAPTION |DS_3DLOOK| WS_VISIBLE;
7256
7257 add_long(lStyle);
7258 add_long(0); // (lExtendedStyle)
7259 pnumitems = p; /*save where the number of items must be stored*/
7260 add_word(0); // NumberOfItems(will change later)
7261 add_word(10); // x
7262 add_word(10); // y
7263 add_word(PixelToDialogX(dlgwidth)); // cx
7264
7265 // Dialog height.
7266 if (vertical)
Bram Moolenaara95d8232013-08-07 15:27:11 +02007267 dlgheight = msgheight + 2 * dlgPaddingY
7268 + DLG_VERT_PADDING_Y + 2 * fontHeight * numButtons;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007269 else
7270 dlgheight = msgheight + 3 * dlgPaddingY + 2 * fontHeight;
7271
7272 // Dialog needs to be taller if contains an edit box.
7273 editboxheight = fontHeight + dlgPaddingY + 4 * DLG_VERT_PADDING_Y;
7274 if (textfield != NULL)
7275 dlgheight += editboxheight;
7276
Bram Moolenaara95d8232013-08-07 15:27:11 +02007277 /* Restrict the size to a maximum. Causes a scrollbar to show up. */
7278 if (dlgheight > maxDialogHeight)
7279 {
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02007280 msgheight = msgheight - (dlgheight - maxDialogHeight);
7281 dlgheight = maxDialogHeight;
7282 scroll_flag = WS_VSCROLL;
7283 /* Make sure scrollbar doesn't appear in the middle of the dialog */
7284 messageWidth = dlgwidth - DLG_ICON_WIDTH - 3 * dlgPaddingX;
Bram Moolenaara95d8232013-08-07 15:27:11 +02007285 }
7286
Bram Moolenaar071d4272004-06-13 20:20:40 +00007287 add_word(PixelToDialogY(dlgheight));
7288
7289 add_word(0); // Menu
7290 add_word(0); // Class
7291
7292 /* copy the title of the dialog */
Bram Moolenaar6edeaf32017-09-26 14:46:04 +02007293 nchar = nCopyAnsiToWideChar(p, (title ? (LPSTR)title
7294 : (LPSTR)("Vim "VIM_VERSION_MEDIUM)), TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007295 p += nchar;
7296
7297 if (s_usenewlook)
7298 {
7299 /* do the font, since DS_3DLOOK doesn't work properly */
7300#ifdef USE_SYSMENU_FONT
7301 if (use_lfSysmenu)
7302 {
7303 /* point size */
7304 *p++ = -MulDiv(lfSysmenu.lfHeight, 72,
7305 GetDeviceCaps(hdc, LOGPIXELSY));
Bram Moolenaar6edeaf32017-09-26 14:46:04 +02007306 nchar = nCopyAnsiToWideChar(p, lfSysmenu.lfFaceName, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007307 }
7308 else
7309#endif
7310 {
7311 *p++ = DLG_FONT_POINT_SIZE; // point size
Bram Moolenaar6edeaf32017-09-26 14:46:04 +02007312 nchar = nCopyAnsiToWideChar(p, DLG_FONT_NAME, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007313 }
7314 p += nchar;
7315 }
7316
7317 buttonYpos = msgheight + 2 * dlgPaddingY;
7318
7319 if (textfield != NULL)
7320 buttonYpos += editboxheight;
7321
7322 pstart = tbuffer;
7323 if (!vertical)
7324 horizWidth = (dlgwidth - horizWidth) / 2; /* Now it's X offset */
7325 for (i = 0; i < numButtons; i++)
7326 {
7327 /* get end of this button. */
7328 for ( pend = pstart;
7329 *pend && (*pend != DLG_BUTTON_SEP);
7330 pend++)
7331 ;
7332
7333 if (*pend)
7334 *pend = '\0';
7335
7336 /*
7337 * old NOTE:
7338 * setting the BS_DEFPUSHBUTTON style doesn't work because Windows sets
7339 * the focus to the first tab-able button and in so doing makes that
7340 * the default!! Grrr. Workaround: Make the default button the only
7341 * one with WS_TABSTOP style. Means user can't tab between buttons, but
7342 * he/she can use arrow keys.
7343 *
7344 * new NOTE: BS_DEFPUSHBUTTON is required to be able to select the
Bram Moolenaar2c7a7632007-05-10 18:19:11 +00007345 * right button when hitting <Enter>. E.g., for the ":confirm quit"
Bram Moolenaar071d4272004-06-13 20:20:40 +00007346 * dialog. Also needed for when the textfield is the default control.
7347 * It appears to work now (perhaps not on Win95?).
7348 */
7349 if (vertical)
7350 {
7351 p = add_dialog_element(p,
7352 (i == dfltbutton
7353 ? BS_DEFPUSHBUTTON : BS_PUSHBUTTON) | WS_TABSTOP,
7354 PixelToDialogX(DLG_VERT_PADDING_X),
7355 PixelToDialogY(buttonYpos /* TBK */
7356 + 2 * fontHeight * i),
7357 PixelToDialogX(dlgwidth - 2 * DLG_VERT_PADDING_X),
7358 (WORD)(PixelToDialogY(2 * fontHeight) - 1),
Bram Moolenaar418f81b2016-02-16 20:12:02 +01007359 (WORD)(IDCANCEL + 1 + i), (WORD)0x0080, (char *)pstart);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007360 }
7361 else
7362 {
7363 p = add_dialog_element(p,
7364 (i == dfltbutton
7365 ? BS_DEFPUSHBUTTON : BS_PUSHBUTTON) | WS_TABSTOP,
7366 PixelToDialogX(horizWidth + buttonPositions[i]),
7367 PixelToDialogY(buttonYpos), /* TBK */
7368 PixelToDialogX(buttonWidths[i]),
7369 (WORD)(PixelToDialogY(2 * fontHeight) - 1),
Bram Moolenaar418f81b2016-02-16 20:12:02 +01007370 (WORD)(IDCANCEL + 1 + i), (WORD)0x0080, (char *)pstart);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007371 }
7372 pstart = pend + 1; /*next button*/
7373 }
7374 *pnumitems += numButtons;
7375
7376 /* Vim icon */
7377 p = add_dialog_element(p, SS_ICON,
7378 PixelToDialogX(dlgPaddingX),
7379 PixelToDialogY(dlgPaddingY),
7380 PixelToDialogX(DLG_ICON_WIDTH),
7381 PixelToDialogY(DLG_ICON_HEIGHT),
7382 DLG_NONBUTTON_CONTROL + 0, (WORD)0x0082,
7383 dlg_icons[type]);
7384
Bram Moolenaar748bf032005-02-02 23:04:36 +00007385 /* Dialog message */
7386 p = add_dialog_element(p, ES_LEFT|scroll_flag|ES_MULTILINE|ES_READONLY,
7387 PixelToDialogX(2 * dlgPaddingX + DLG_ICON_WIDTH),
7388 PixelToDialogY(dlgPaddingY),
7389 (WORD)(PixelToDialogX(messageWidth) + 1),
7390 PixelToDialogY(msgheight),
Bram Moolenaar418f81b2016-02-16 20:12:02 +01007391 DLG_NONBUTTON_CONTROL + 1, (WORD)0x0081, (char *)message);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007392
7393 /* Edit box */
7394 if (textfield != NULL)
7395 {
7396 p = add_dialog_element(p, ES_LEFT|ES_AUTOHSCROLL|WS_TABSTOP|WS_BORDER,
7397 PixelToDialogX(2 * dlgPaddingX),
7398 PixelToDialogY(2 * dlgPaddingY + msgheight),
7399 PixelToDialogX(dlgwidth - 4 * dlgPaddingX),
7400 PixelToDialogY(fontHeight + dlgPaddingY),
Bram Moolenaar418f81b2016-02-16 20:12:02 +01007401 DLG_NONBUTTON_CONTROL + 2, (WORD)0x0081, (char *)textfield);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007402 *pnumitems += 1;
7403 }
7404
7405 *pnumitems += 2;
7406
7407 SelectFont(hdc, oldFont);
7408 DeleteObject(font);
7409 ReleaseDC(hwnd, hdc);
7410
7411 /* Let the dialog_callback() function know which button to make default
7412 * If we have an edit box, make that the default. We also need to tell
7413 * dialog_callback() if this dialog contains an edit box or not. We do
7414 * this by setting s_textfield if it does.
7415 */
7416 if (textfield != NULL)
7417 {
7418 dialog_default_button = DLG_NONBUTTON_CONTROL + 2;
7419 s_textfield = textfield;
7420 }
7421 else
7422 {
7423 dialog_default_button = IDCANCEL + 1 + dfltbutton;
7424 s_textfield = NULL;
7425 }
7426
7427 /* show the dialog box modally and get a return value */
7428 nchar = (int)DialogBoxIndirect(
7429 s_hinst,
7430 (LPDLGTEMPLATE)pdlgtemplate,
7431 s_hwnd,
7432 (DLGPROC)dialog_callback);
7433
7434 LocalFree(LocalHandle(pdlgtemplate));
7435 vim_free(tbuffer);
7436 vim_free(buttonWidths);
7437 vim_free(buttonPositions);
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007438 vim_free(ga.ga_data);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007439
7440 /* Focus back to our window (for when MDI is used). */
7441 (void)SetFocus(s_hwnd);
7442
7443 return nchar;
7444}
7445
7446#endif /* FEAT_GUI_DIALOG */
Bram Moolenaareb3593b2006-04-22 22:33:57 +00007447
Bram Moolenaar071d4272004-06-13 20:20:40 +00007448/*
7449 * Put a simple element (basic class) onto a dialog template in memory.
7450 * return a pointer to where the next item should be added.
7451 *
7452 * parameters:
7453 * lStyle = additional style flags
7454 * (be careful, NT3.51 & Win32s will ignore the new ones)
7455 * x,y = x & y positions IN DIALOG UNITS
7456 * w,h = width and height IN DIALOG UNITS
7457 * Id = ID used in messages
7458 * clss = class ID, e.g 0x0080 for a button, 0x0082 for a static
7459 * caption = usually text or resource name
7460 *
7461 * TODO: use the length information noted here to enable the dialog creation
7462 * routines to work out more exactly how much memory they need to alloc.
7463 */
7464 static PWORD
7465add_dialog_element(
7466 PWORD p,
7467 DWORD lStyle,
7468 WORD x,
7469 WORD y,
7470 WORD w,
7471 WORD h,
7472 WORD Id,
7473 WORD clss,
7474 const char *caption)
7475{
7476 int nchar;
7477
7478 p = lpwAlign(p); /* Align to dword boundary*/
7479 lStyle = lStyle | WS_VISIBLE | WS_CHILD;
7480 *p++ = LOWORD(lStyle);
7481 *p++ = HIWORD(lStyle);
7482 *p++ = 0; // LOWORD (lExtendedStyle)
7483 *p++ = 0; // HIWORD (lExtendedStyle)
7484 *p++ = x;
7485 *p++ = y;
7486 *p++ = w;
7487 *p++ = h;
7488 *p++ = Id; //9 or 10 words in all
7489
7490 *p++ = (WORD)0xffff;
7491 *p++ = clss; //2 more here
7492
Bram Moolenaar6edeaf32017-09-26 14:46:04 +02007493 nchar = nCopyAnsiToWideChar(p, (LPSTR)caption, TRUE); //strlen(caption)+1
Bram Moolenaar071d4272004-06-13 20:20:40 +00007494 p += nchar;
7495
7496 *p++ = 0; // advance pointer over nExtraStuff WORD - 2 more
7497
7498 return p; //total = 15+ (strlen(caption)) words
7499 // = 30 + 2(strlen(caption) bytes reqd
7500}
7501
7502
7503/*
7504 * Helper routine. Take an input pointer, return closest pointer that is
7505 * aligned on a DWORD (4 byte) boundary. Taken from the Win32SDK samples.
7506 */
7507 static LPWORD
7508lpwAlign(
7509 LPWORD lpIn)
7510{
Bram Moolenaareb3593b2006-04-22 22:33:57 +00007511 long_u ul;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007512
Bram Moolenaareb3593b2006-04-22 22:33:57 +00007513 ul = (long_u)lpIn;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007514 ul += 3;
7515 ul >>= 2;
7516 ul <<= 2;
7517 return (LPWORD)ul;
7518}
7519
7520/*
7521 * Helper routine. Takes second parameter as Ansi string, copies it to first
7522 * parameter as wide character (16-bits / char) string, and returns integer
7523 * number of wide characters (words) in string (including the trailing wide
7524 * char NULL). Partly taken from the Win32SDK samples.
Bram Moolenaar6edeaf32017-09-26 14:46:04 +02007525 * If "use_enc" is TRUE, 'encoding' is used for "lpAnsiIn". If FALSE, current
7526 * ACP is used for "lpAnsiIn". */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007527 static int
7528nCopyAnsiToWideChar(
7529 LPWORD lpWCStr,
Bram Moolenaar6edeaf32017-09-26 14:46:04 +02007530 LPSTR lpAnsiIn,
7531 BOOL use_enc)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007532{
7533 int nChar = 0;
7534#ifdef FEAT_MBYTE
7535 int len = lstrlen(lpAnsiIn) + 1; /* include NUL character */
7536 int i;
7537 WCHAR *wn;
7538
Bram Moolenaar6edeaf32017-09-26 14:46:04 +02007539 if (use_enc && enc_codepage >= 0 && (int)GetACP() != enc_codepage)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007540 {
7541 /* Not a codepage, use our own conversion function. */
Bram Moolenaar418f81b2016-02-16 20:12:02 +01007542 wn = enc_to_utf16((char_u *)lpAnsiIn, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007543 if (wn != NULL)
7544 {
7545 wcscpy(lpWCStr, wn);
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00007546 nChar = (int)wcslen(wn) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007547 vim_free(wn);
7548 }
7549 }
7550 if (nChar == 0)
7551 /* Use Win32 conversion function. */
7552 nChar = MultiByteToWideChar(
7553 enc_codepage > 0 ? enc_codepage : CP_ACP,
7554 MB_PRECOMPOSED,
7555 lpAnsiIn, len,
7556 lpWCStr, len);
7557 for (i = 0; i < nChar; ++i)
7558 if (lpWCStr[i] == (WORD)'\t') /* replace tabs with spaces */
7559 lpWCStr[i] = (WORD)' ';
7560#else
7561 do
7562 {
7563 if (*lpAnsiIn == '\t')
7564 *lpWCStr++ = (WORD)' ';
7565 else
7566 *lpWCStr++ = (WORD)*lpAnsiIn;
7567 nChar++;
7568 } while (*lpAnsiIn++);
7569#endif
7570
7571 return nChar;
7572}
7573
7574
7575#ifdef FEAT_TEAROFF
7576/*
Bram Moolenaar66857f42017-10-22 16:43:20 +02007577 * Lookup menu handle from "menu_id".
7578 */
7579 static HMENU
7580tearoff_lookup_menuhandle(
7581 vimmenu_T *menu,
7582 WORD menu_id)
7583{
7584 for ( ; menu != NULL; menu = menu->next)
7585 {
7586 if (menu->modes == 0) /* this menu has just been deleted */
7587 continue;
7588 if (menu_is_separator(menu->dname))
7589 continue;
7590 if ((WORD)((long_u)(menu->submenu_id) | (DWORD)0x8000) == menu_id)
7591 return menu->submenu_id;
7592 }
7593 return NULL;
7594}
7595
7596/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00007597 * The callback function for all the modeless dialogs that make up the
7598 * "tearoff menus" Very simple - forward button presses (to fool Vim into
7599 * thinking its menus have been clicked), and go away when closed.
7600 */
7601 static LRESULT CALLBACK
7602tearoff_callback(
7603 HWND hwnd,
7604 UINT message,
7605 WPARAM wParam,
7606 LPARAM lParam)
7607{
7608 if (message == WM_INITDIALOG)
Bram Moolenaar66857f42017-10-22 16:43:20 +02007609 {
7610 SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR)lParam);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007611 return (TRUE);
Bram Moolenaar66857f42017-10-22 16:43:20 +02007612 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007613
7614 /* May show the mouse pointer again. */
7615 HandleMouseHide(message, lParam);
7616
7617 if (message == WM_COMMAND)
7618 {
7619 if ((WORD)(LOWORD(wParam)) & 0x8000)
7620 {
7621 POINT mp;
7622 RECT rect;
7623
7624 if (GetCursorPos(&mp) && GetWindowRect(hwnd, &rect))
7625 {
Bram Moolenaar66857f42017-10-22 16:43:20 +02007626 vimmenu_T *menu;
7627
7628 menu = (vimmenu_T*)GetWindowLongPtr(hwnd, DWLP_USER);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007629 (void)TrackPopupMenu(
Bram Moolenaar66857f42017-10-22 16:43:20 +02007630 tearoff_lookup_menuhandle(menu, LOWORD(wParam)),
Bram Moolenaar071d4272004-06-13 20:20:40 +00007631 TPM_LEFTALIGN | TPM_LEFTBUTTON,
7632 (int)rect.right - 8,
7633 (int)mp.y,
7634 (int)0, /*reserved param*/
7635 s_hwnd,
7636 NULL);
7637 /*
7638 * NOTE: The pop-up menu can eat the mouse up event.
7639 * We deal with this in normal.c.
7640 */
7641 }
7642 }
7643 else
7644 /* Pass on messages to the main Vim window */
7645 PostMessage(s_hwnd, WM_COMMAND, LOWORD(wParam), 0);
7646 /*
7647 * Give main window the focus back: this is so after
7648 * choosing a tearoff button you can start typing again
7649 * straight away.
7650 */
7651 (void)SetFocus(s_hwnd);
7652 return TRUE;
7653 }
7654 if ((message == WM_SYSCOMMAND) && (wParam == SC_CLOSE))
7655 {
7656 DestroyWindow(hwnd);
7657 return TRUE;
7658 }
7659
7660 /* When moved around, give main window the focus back. */
7661 if (message == WM_EXITSIZEMOVE)
7662 (void)SetActiveWindow(s_hwnd);
7663
7664 return FALSE;
7665}
7666#endif
7667
7668
7669/*
7670 * Decide whether to use the "new look" (small, non-bold font) or the "old
7671 * look" (big, clanky font) for dialogs, and work out a few values for use
7672 * later accordingly.
7673 */
7674 static void
7675get_dialog_font_metrics(void)
7676{
7677 HDC hdc;
7678 HFONT hfontTools = 0;
7679 DWORD dlgFontSize;
7680 SIZE size;
7681#ifdef USE_SYSMENU_FONT
7682 LOGFONT lfSysmenu;
7683#endif
7684
7685 s_usenewlook = FALSE;
7686
Bram Moolenaar071d4272004-06-13 20:20:40 +00007687#ifdef USE_SYSMENU_FONT
Bram Moolenaarcea912a2016-10-12 14:20:24 +02007688 if (gui_w32_get_menu_font(&lfSysmenu) == OK)
7689 hfontTools = CreateFontIndirect(&lfSysmenu);
7690 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00007691#endif
7692 hfontTools = CreateFont(-DLG_FONT_POINT_SIZE, 0, 0, 0, 0, 0, 0, 0,
7693 0, 0, 0, 0, VARIABLE_PITCH , DLG_FONT_NAME);
7694
Bram Moolenaarcea912a2016-10-12 14:20:24 +02007695 if (hfontTools)
7696 {
7697 hdc = GetDC(s_hwnd);
7698 SelectObject(hdc, hfontTools);
7699 /*
7700 * GetTextMetrics() doesn't return the right value in
7701 * tmAveCharWidth, so we have to figure out the dialog base units
7702 * ourselves.
7703 */
7704 GetTextExtentPoint(hdc,
7705 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
7706 52, &size);
7707 ReleaseDC(s_hwnd, hdc);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007708
Bram Moolenaarcea912a2016-10-12 14:20:24 +02007709 s_dlgfntwidth = (WORD)((size.cx / 26 + 1) / 2);
7710 s_dlgfntheight = (WORD)size.cy;
7711 s_usenewlook = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007712 }
7713
7714 if (!s_usenewlook)
7715 {
7716 dlgFontSize = GetDialogBaseUnits(); /* fall back to big old system*/
7717 s_dlgfntwidth = LOWORD(dlgFontSize);
7718 s_dlgfntheight = HIWORD(dlgFontSize);
7719 }
7720}
7721
7722#if defined(FEAT_MENU) && defined(FEAT_TEAROFF)
7723/*
7724 * Create a pseudo-"tearoff menu" based on the child
7725 * items of a given menu pointer.
7726 */
7727 static void
7728gui_mch_tearoff(
7729 char_u *title,
7730 vimmenu_T *menu,
7731 int initX,
7732 int initY)
7733{
7734 WORD *p, *pdlgtemplate, *pnumitems, *ptrueheight;
7735 int template_len;
7736 int nchar, textWidth, submenuWidth;
7737 DWORD lStyle;
7738 DWORD lExtendedStyle;
7739 WORD dlgwidth;
7740 WORD menuID;
7741 vimmenu_T *pmenu;
Bram Moolenaar66857f42017-10-22 16:43:20 +02007742 vimmenu_T *top_menu;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007743 vimmenu_T *the_menu = menu;
7744 HWND hwnd;
7745 HDC hdc;
7746 HFONT font, oldFont;
7747 int col, spaceWidth, len;
7748 int columnWidths[2];
7749 char_u *label, *text;
7750 int acLen = 0;
7751 int nameLen;
7752 int padding0, padding1, padding2 = 0;
7753 int sepPadding=0;
Bram Moolenaar9588a0f2005-01-08 21:45:39 +00007754 int x;
7755 int y;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007756#ifdef USE_SYSMENU_FONT
7757 LOGFONT lfSysmenu;
7758 int use_lfSysmenu = FALSE;
7759#endif
7760
7761 /*
7762 * If this menu is already torn off, move it to the mouse position.
7763 */
7764 if (IsWindow(menu->tearoff_handle))
7765 {
7766 POINT mp;
7767 if (GetCursorPos((LPPOINT)&mp))
7768 {
7769 SetWindowPos(menu->tearoff_handle, NULL, mp.x, mp.y, 0, 0,
7770 SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
7771 }
7772 return;
7773 }
7774
7775 /*
7776 * Create a new tearoff.
7777 */
7778 if (*title == MNU_HIDDEN_CHAR)
7779 title++;
7780
7781 /* Allocate memory to store the dialog template. It's made bigger when
7782 * needed. */
7783 template_len = DLG_ALLOC_SIZE;
7784 pdlgtemplate = p = (WORD *)LocalAlloc(LPTR, template_len);
7785 if (p == NULL)
7786 return;
7787
7788 hwnd = GetDesktopWindow();
7789 hdc = GetWindowDC(hwnd);
7790#ifdef USE_SYSMENU_FONT
7791 if (gui_w32_get_menu_font(&lfSysmenu) == OK)
7792 {
7793 font = CreateFontIndirect(&lfSysmenu);
7794 use_lfSysmenu = TRUE;
7795 }
7796 else
7797#endif
7798 font = CreateFont(-DLG_FONT_POINT_SIZE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
7799 VARIABLE_PITCH , DLG_FONT_NAME);
7800 if (s_usenewlook)
7801 oldFont = SelectFont(hdc, font);
7802 else
7803 oldFont = SelectFont(hdc, GetStockObject(SYSTEM_FONT));
7804
7805 /* Calculate width of a single space. Used for padding columns to the
7806 * right width. */
Bram Moolenaar418f81b2016-02-16 20:12:02 +01007807 spaceWidth = GetTextWidth(hdc, (char_u *)" ", 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007808
7809 /* Figure out max width of the text column, the accelerator column and the
7810 * optional submenu column. */
7811 submenuWidth = 0;
7812 for (col = 0; col < 2; col++)
7813 {
7814 columnWidths[col] = 0;
7815 for (pmenu = menu->children; pmenu != NULL; pmenu = pmenu->next)
7816 {
7817 /* Use "dname" here to compute the width of the visible text. */
7818 text = (col == 0) ? pmenu->dname : pmenu->actext;
7819 if (text != NULL && *text != NUL)
7820 {
7821 textWidth = GetTextWidthEnc(hdc, text, (int)STRLEN(text));
7822 if (textWidth > columnWidths[col])
7823 columnWidths[col] = textWidth;
7824 }
7825 if (pmenu->children != NULL)
7826 submenuWidth = TEAROFF_COLUMN_PADDING * spaceWidth;
7827 }
7828 }
7829 if (columnWidths[1] == 0)
7830 {
7831 /* no accelerators */
7832 if (submenuWidth != 0)
7833 columnWidths[0] += submenuWidth;
7834 else
7835 columnWidths[0] += spaceWidth;
7836 }
7837 else
7838 {
7839 /* there is an accelerator column */
7840 columnWidths[0] += TEAROFF_COLUMN_PADDING * spaceWidth;
7841 columnWidths[1] += submenuWidth;
7842 }
7843
7844 /*
7845 * Now find the total width of our 'menu'.
7846 */
7847 textWidth = columnWidths[0] + columnWidths[1];
7848 if (submenuWidth != 0)
7849 {
Bram Moolenaar418f81b2016-02-16 20:12:02 +01007850 submenuWidth = GetTextWidth(hdc, (char_u *)TEAROFF_SUBMENU_LABEL,
Bram Moolenaar071d4272004-06-13 20:20:40 +00007851 (int)STRLEN(TEAROFF_SUBMENU_LABEL));
7852 textWidth += submenuWidth;
7853 }
7854 dlgwidth = GetTextWidthEnc(hdc, title, (int)STRLEN(title));
7855 if (textWidth > dlgwidth)
7856 dlgwidth = textWidth;
7857 dlgwidth += 2 * TEAROFF_PADDING_X + TEAROFF_BUTTON_PAD_X;
7858
Bram Moolenaar071d4272004-06-13 20:20:40 +00007859 /* start to fill in the dlgtemplate information. addressing by WORDs */
7860 if (s_usenewlook)
7861 lStyle = DS_MODALFRAME | WS_CAPTION| WS_SYSMENU |DS_SETFONT| WS_VISIBLE;
7862 else
7863 lStyle = DS_MODALFRAME | WS_CAPTION| WS_SYSMENU | WS_VISIBLE;
7864
7865 lExtendedStyle = WS_EX_TOOLWINDOW|WS_EX_STATICEDGE;
7866 *p++ = LOWORD(lStyle);
7867 *p++ = HIWORD(lStyle);
7868 *p++ = LOWORD(lExtendedStyle);
7869 *p++ = HIWORD(lExtendedStyle);
7870 pnumitems = p; /* save where the number of items must be stored */
7871 *p++ = 0; // NumberOfItems(will change later)
Bram Moolenaar9588a0f2005-01-08 21:45:39 +00007872 gui_mch_getmouse(&x, &y);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007873 if (initX == 0xffffL)
Bram Moolenaar9588a0f2005-01-08 21:45:39 +00007874 *p++ = PixelToDialogX(x); // x
Bram Moolenaar071d4272004-06-13 20:20:40 +00007875 else
7876 *p++ = PixelToDialogX(initX); // x
7877 if (initY == 0xffffL)
Bram Moolenaar9588a0f2005-01-08 21:45:39 +00007878 *p++ = PixelToDialogY(y); // y
Bram Moolenaar071d4272004-06-13 20:20:40 +00007879 else
7880 *p++ = PixelToDialogY(initY); // y
7881 *p++ = PixelToDialogX(dlgwidth); // cx
7882 ptrueheight = p;
7883 *p++ = 0; // dialog height: changed later anyway
7884 *p++ = 0; // Menu
7885 *p++ = 0; // Class
7886
7887 /* copy the title of the dialog */
7888 nchar = nCopyAnsiToWideChar(p, ((*title)
Bram Moolenaar6edeaf32017-09-26 14:46:04 +02007889 ? (LPSTR)title
7890 : (LPSTR)("Vim "VIM_VERSION_MEDIUM)), TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007891 p += nchar;
7892
7893 if (s_usenewlook)
7894 {
7895 /* do the font, since DS_3DLOOK doesn't work properly */
7896#ifdef USE_SYSMENU_FONT
7897 if (use_lfSysmenu)
7898 {
7899 /* point size */
7900 *p++ = -MulDiv(lfSysmenu.lfHeight, 72,
7901 GetDeviceCaps(hdc, LOGPIXELSY));
Bram Moolenaar6edeaf32017-09-26 14:46:04 +02007902 nchar = nCopyAnsiToWideChar(p, lfSysmenu.lfFaceName, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007903 }
7904 else
7905#endif
7906 {
7907 *p++ = DLG_FONT_POINT_SIZE; // point size
Bram Moolenaar6edeaf32017-09-26 14:46:04 +02007908 nchar = nCopyAnsiToWideChar(p, DLG_FONT_NAME, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007909 }
7910 p += nchar;
7911 }
7912
7913 /*
7914 * Loop over all the items in the menu.
7915 * But skip over the tearbar.
7916 */
7917 if (STRCMP(menu->children->name, TEAR_STRING) == 0)
7918 menu = menu->children->next;
7919 else
7920 menu = menu->children;
Bram Moolenaar66857f42017-10-22 16:43:20 +02007921 top_menu = menu;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007922 for ( ; menu != NULL; menu = menu->next)
7923 {
7924 if (menu->modes == 0) /* this menu has just been deleted */
7925 continue;
7926 if (menu_is_separator(menu->dname))
7927 {
7928 sepPadding += 3;
7929 continue;
7930 }
7931
7932 /* Check if there still is plenty of room in the template. Make it
7933 * larger when needed. */
7934 if (((char *)p - (char *)pdlgtemplate) + 1000 > template_len)
7935 {
7936 WORD *newp;
7937
7938 newp = (WORD *)LocalAlloc(LPTR, template_len + 4096);
7939 if (newp != NULL)
7940 {
7941 template_len += 4096;
7942 mch_memmove(newp, pdlgtemplate,
7943 (char *)p - (char *)pdlgtemplate);
7944 p = newp + (p - pdlgtemplate);
7945 pnumitems = newp + (pnumitems - pdlgtemplate);
7946 ptrueheight = newp + (ptrueheight - pdlgtemplate);
7947 LocalFree(LocalHandle(pdlgtemplate));
7948 pdlgtemplate = newp;
7949 }
7950 }
7951
7952 /* Figure out minimal length of this menu label. Use "name" for the
7953 * actual text, "dname" for estimating the displayed size. "name"
7954 * has "&a" for mnemonic and includes the accelerator. */
7955 len = nameLen = (int)STRLEN(menu->name);
7956 padding0 = (columnWidths[0] - GetTextWidthEnc(hdc, menu->dname,
7957 (int)STRLEN(menu->dname))) / spaceWidth;
7958 len += padding0;
7959
7960 if (menu->actext != NULL)
7961 {
7962 acLen = (int)STRLEN(menu->actext);
7963 len += acLen;
7964 textWidth = GetTextWidthEnc(hdc, menu->actext, acLen);
7965 }
7966 else
7967 textWidth = 0;
7968 padding1 = (columnWidths[1] - textWidth) / spaceWidth;
7969 len += padding1;
7970
7971 if (menu->children == NULL)
7972 {
7973 padding2 = submenuWidth / spaceWidth;
7974 len += padding2;
7975 menuID = (WORD)(menu->id);
7976 }
7977 else
7978 {
7979 len += (int)STRLEN(TEAROFF_SUBMENU_LABEL);
Bram Moolenaareb3593b2006-04-22 22:33:57 +00007980 menuID = (WORD)((long_u)(menu->submenu_id) | (DWORD)0x8000);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007981 }
7982
7983 /* Allocate menu label and fill it in */
7984 text = label = alloc((unsigned)len + 1);
7985 if (label == NULL)
7986 break;
7987
Bram Moolenaarce0842a2005-07-18 21:58:11 +00007988 vim_strncpy(text, menu->name, nameLen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007989 text = vim_strchr(text, TAB); /* stop at TAB before actext */
7990 if (text == NULL)
7991 text = label + nameLen; /* no actext, use whole name */
7992 while (padding0-- > 0)
7993 *text++ = ' ';
7994 if (menu->actext != NULL)
7995 {
7996 STRNCPY(text, menu->actext, acLen);
7997 text += acLen;
7998 }
7999 while (padding1-- > 0)
8000 *text++ = ' ';
8001 if (menu->children != NULL)
8002 {
8003 STRCPY(text, TEAROFF_SUBMENU_LABEL);
8004 text += STRLEN(TEAROFF_SUBMENU_LABEL);
8005 }
8006 else
8007 {
8008 while (padding2-- > 0)
8009 *text++ = ' ';
8010 }
8011 *text = NUL;
8012
8013 /*
8014 * BS_LEFT will just be ignored on Win32s/NT3.5x - on
8015 * W95/NT4 it makes the tear-off look more like a menu.
8016 */
8017 p = add_dialog_element(p,
8018 BS_PUSHBUTTON|BS_LEFT,
8019 (WORD)PixelToDialogX(TEAROFF_PADDING_X),
8020 (WORD)(sepPadding + 1 + 13 * (*pnumitems)),
8021 (WORD)PixelToDialogX(dlgwidth - 2 * TEAROFF_PADDING_X),
8022 (WORD)12,
Bram Moolenaar418f81b2016-02-16 20:12:02 +01008023 menuID, (WORD)0x0080, (char *)label);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008024 vim_free(label);
8025 (*pnumitems)++;
8026 }
8027
8028 *ptrueheight = (WORD)(sepPadding + 1 + 13 * (*pnumitems));
8029
8030
8031 /* show modelessly */
Bram Moolenaar66857f42017-10-22 16:43:20 +02008032 the_menu->tearoff_handle = CreateDialogIndirectParam(
Bram Moolenaar071d4272004-06-13 20:20:40 +00008033 s_hinst,
8034 (LPDLGTEMPLATE)pdlgtemplate,
8035 s_hwnd,
Bram Moolenaar66857f42017-10-22 16:43:20 +02008036 (DLGPROC)tearoff_callback,
8037 (LPARAM)top_menu);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008038
8039 LocalFree(LocalHandle(pdlgtemplate));
8040 SelectFont(hdc, oldFont);
8041 DeleteObject(font);
8042 ReleaseDC(hwnd, hdc);
8043
8044 /*
8045 * Reassert ourselves as the active window. This is so that after creating
8046 * a tearoff, the user doesn't have to click with the mouse just to start
8047 * typing again!
8048 */
8049 (void)SetActiveWindow(s_hwnd);
8050
8051 /* make sure the right buttons are enabled */
8052 force_menu_update = TRUE;
8053}
8054#endif
8055
8056#if defined(FEAT_TOOLBAR) || defined(PROTO)
8057#include "gui_w32_rc.h"
8058
8059/* This not defined in older SDKs */
8060# ifndef TBSTYLE_FLAT
8061# define TBSTYLE_FLAT 0x0800
8062# endif
8063
8064/*
8065 * Create the toolbar, initially unpopulated.
8066 * (just like the menu, there are no defaults, it's all
8067 * set up through menu.vim)
8068 */
8069 static void
8070initialise_toolbar(void)
8071{
8072 InitCommonControls();
8073 s_toolbarhwnd = CreateToolbarEx(
8074 s_hwnd,
8075 WS_CHILD | TBSTYLE_TOOLTIPS | TBSTYLE_FLAT,
8076 4000, //any old big number
Bram Moolenaar2c7a7632007-05-10 18:19:11 +00008077 31, //number of images in initial bitmap
Bram Moolenaar071d4272004-06-13 20:20:40 +00008078 s_hinst,
8079 IDR_TOOLBAR1, // id of initial bitmap
8080 NULL,
8081 0, // initial number of buttons
8082 TOOLBAR_BUTTON_WIDTH, //api guide is wrong!
8083 TOOLBAR_BUTTON_HEIGHT,
8084 TOOLBAR_BUTTON_WIDTH,
8085 TOOLBAR_BUTTON_HEIGHT,
8086 sizeof(TBBUTTON)
8087 );
Bram Moolenaar5f919ee2013-07-21 17:46:43 +02008088 s_toolbar_wndproc = SubclassWindow(s_toolbarhwnd, toolbar_wndproc);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008089
8090 gui_mch_show_toolbar(vim_strchr(p_go, GO_TOOLBAR) != NULL);
8091}
8092
Bram Moolenaar5f919ee2013-07-21 17:46:43 +02008093 static LRESULT CALLBACK
8094toolbar_wndproc(
8095 HWND hwnd,
8096 UINT uMsg,
8097 WPARAM wParam,
8098 LPARAM lParam)
8099{
8100 HandleMouseHide(uMsg, lParam);
8101 return CallWindowProc(s_toolbar_wndproc, hwnd, uMsg, wParam, lParam);
8102}
8103
Bram Moolenaar071d4272004-06-13 20:20:40 +00008104 static int
8105get_toolbar_bitmap(vimmenu_T *menu)
8106{
8107 int i = -1;
8108
8109 /*
8110 * Check user bitmaps first, unless builtin is specified.
8111 */
Bram Moolenaarcea912a2016-10-12 14:20:24 +02008112 if (!menu->icon_builtin)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008113 {
8114 char_u fname[MAXPATHL];
8115 HANDLE hbitmap = NULL;
8116
8117 if (menu->iconfile != NULL)
8118 {
8119 gui_find_iconfile(menu->iconfile, fname, "bmp");
8120 hbitmap = LoadImage(
8121 NULL,
Bram Moolenaar418f81b2016-02-16 20:12:02 +01008122 (LPCSTR)fname,
Bram Moolenaar071d4272004-06-13 20:20:40 +00008123 IMAGE_BITMAP,
8124 TOOLBAR_BUTTON_WIDTH,
8125 TOOLBAR_BUTTON_HEIGHT,
8126 LR_LOADFROMFILE |
8127 LR_LOADMAP3DCOLORS
8128 );
8129 }
8130
8131 /*
8132 * If the LoadImage call failed, or the "icon=" file
8133 * didn't exist or wasn't specified, try the menu name
8134 */
8135 if (hbitmap == NULL
Bram Moolenaara5f5c8b2013-06-27 22:29:38 +02008136 && (gui_find_bitmap(
8137#ifdef FEAT_MULTI_LANG
8138 menu->en_dname != NULL ? menu->en_dname :
8139#endif
8140 menu->dname, fname, "bmp") == OK))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008141 hbitmap = LoadImage(
8142 NULL,
Bram Moolenaar418f81b2016-02-16 20:12:02 +01008143 (LPCSTR)fname,
Bram Moolenaar071d4272004-06-13 20:20:40 +00008144 IMAGE_BITMAP,
8145 TOOLBAR_BUTTON_WIDTH,
8146 TOOLBAR_BUTTON_HEIGHT,
8147 LR_LOADFROMFILE |
8148 LR_LOADMAP3DCOLORS
8149 );
8150
8151 if (hbitmap != NULL)
8152 {
8153 TBADDBITMAP tbAddBitmap;
8154
8155 tbAddBitmap.hInst = NULL;
Bram Moolenaareb3593b2006-04-22 22:33:57 +00008156 tbAddBitmap.nID = (long_u)hbitmap;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008157
8158 i = (int)SendMessage(s_toolbarhwnd, TB_ADDBITMAP,
8159 (WPARAM)1, (LPARAM)&tbAddBitmap);
8160 /* i will be set to -1 if it fails */
8161 }
8162 }
8163 if (i == -1 && menu->iconidx >= 0 && menu->iconidx < TOOLBAR_BITMAP_COUNT)
8164 i = menu->iconidx;
8165
8166 return i;
8167}
8168#endif
8169
Bram Moolenaar3991dab2006-03-27 17:01:56 +00008170#if defined(FEAT_GUI_TABLINE) || defined(PROTO)
8171 static void
8172initialise_tabline(void)
8173{
8174 InitCommonControls();
8175
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008176 s_tabhwnd = CreateWindow(WC_TABCONTROL, "Vim tabline",
Bram Moolenaareb3593b2006-04-22 22:33:57 +00008177 WS_CHILD|TCS_FOCUSNEVER|TCS_TOOLTIPS,
Bram Moolenaar3991dab2006-03-27 17:01:56 +00008178 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
8179 CW_USEDEFAULT, s_hwnd, NULL, s_hinst, NULL);
Bram Moolenaar5f919ee2013-07-21 17:46:43 +02008180 s_tabline_wndproc = SubclassWindow(s_tabhwnd, tabline_wndproc);
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008181
Bram Moolenaar551dbcc2006-04-25 22:13:59 +00008182 gui.tabline_height = TABLINE_HEIGHT;
8183
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008184# ifdef USE_SYSMENU_FONT
Bram Moolenaar551dbcc2006-04-25 22:13:59 +00008185 set_tabline_font();
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008186# endif
Bram Moolenaar3991dab2006-03-27 17:01:56 +00008187}
Bram Moolenaar5f919ee2013-07-21 17:46:43 +02008188
Bram Moolenaarca05aa22017-10-22 15:36:14 +02008189/*
8190 * Get tabpage_T from POINT.
8191 */
8192 static tabpage_T *
8193GetTabFromPoint(
8194 HWND hWnd,
8195 POINT pt)
8196{
8197 tabpage_T *ptp = NULL;
8198
8199 if (gui_mch_showing_tabline())
8200 {
8201 TCHITTESTINFO htinfo;
8202 htinfo.pt = pt;
8203 /* ignore if a window under cusor is not tabcontrol. */
8204 if (s_tabhwnd == hWnd)
8205 {
8206 int idx = TabCtrl_HitTest(s_tabhwnd, &htinfo);
8207 if (idx != -1)
8208 ptp = find_tabpage(idx + 1);
8209 }
8210 }
8211 return ptp;
8212}
8213
8214static POINT s_pt = {0, 0};
8215static HCURSOR s_hCursor = NULL;
8216
Bram Moolenaar5f919ee2013-07-21 17:46:43 +02008217 static LRESULT CALLBACK
8218tabline_wndproc(
8219 HWND hwnd,
8220 UINT uMsg,
8221 WPARAM wParam,
8222 LPARAM lParam)
8223{
Bram Moolenaarca05aa22017-10-22 15:36:14 +02008224 POINT pt;
8225 tabpage_T *tp;
8226 RECT rect;
8227 int nCenter;
8228 int idx0;
8229 int idx1;
8230
Bram Moolenaar5f919ee2013-07-21 17:46:43 +02008231 HandleMouseHide(uMsg, lParam);
Bram Moolenaarca05aa22017-10-22 15:36:14 +02008232
8233 switch (uMsg)
8234 {
8235 case WM_LBUTTONDOWN:
8236 {
8237 s_pt.x = GET_X_LPARAM(lParam);
8238 s_pt.y = GET_Y_LPARAM(lParam);
8239 SetCapture(hwnd);
8240 s_hCursor = GetCursor(); /* backup default cursor */
8241 break;
8242 }
8243 case WM_MOUSEMOVE:
8244 if (GetCapture() == hwnd
8245 && ((wParam & MK_LBUTTON)) != 0)
8246 {
8247 pt.x = GET_X_LPARAM(lParam);
8248 pt.y = s_pt.y;
8249 if (abs(pt.x - s_pt.x) > GetSystemMetrics(SM_CXDRAG))
8250 {
8251 SetCursor(LoadCursor(NULL, IDC_SIZEWE));
8252
8253 tp = GetTabFromPoint(hwnd, pt);
8254 if (tp != NULL)
8255 {
8256 idx0 = tabpage_index(curtab) - 1;
8257 idx1 = tabpage_index(tp) - 1;
8258
8259 TabCtrl_GetItemRect(hwnd, idx1, &rect);
8260 nCenter = rect.left + (rect.right - rect.left) / 2;
8261
8262 /* Check if the mouse cursor goes over the center of
8263 * the next tab to prevent "flickering". */
8264 if ((idx0 < idx1) && (nCenter < pt.x))
8265 {
8266 tabpage_move(idx1 + 1);
8267 update_screen(0);
8268 }
8269 else if ((idx1 < idx0) && (pt.x < nCenter))
8270 {
8271 tabpage_move(idx1);
8272 update_screen(0);
8273 }
8274 }
8275 }
8276 }
8277 break;
8278 case WM_LBUTTONUP:
8279 {
8280 if (GetCapture() == hwnd)
8281 {
8282 SetCursor(s_hCursor);
8283 ReleaseCapture();
8284 }
8285 break;
8286 }
8287 default:
8288 break;
8289 }
8290
Bram Moolenaar5f919ee2013-07-21 17:46:43 +02008291 return CallWindowProc(s_tabline_wndproc, hwnd, uMsg, wParam, lParam);
8292}
Bram Moolenaar3991dab2006-03-27 17:01:56 +00008293#endif
8294
Bram Moolenaar071d4272004-06-13 20:20:40 +00008295#if defined(FEAT_OLE) || defined(FEAT_EVAL) || defined(PROTO)
8296/*
8297 * Make the GUI window come to the foreground.
8298 */
8299 void
8300gui_mch_set_foreground(void)
8301{
8302 if (IsIconic(s_hwnd))
8303 SendMessage(s_hwnd, WM_SYSCOMMAND, SC_RESTORE, 0);
8304 SetForegroundWindow(s_hwnd);
8305}
8306#endif
8307
8308#if defined(FEAT_MBYTE_IME) && defined(DYNAMIC_IME)
8309 static void
8310dyn_imm_load(void)
8311{
Bram Moolenaarebbcb822010-10-23 14:02:54 +02008312 hLibImm = vimLoadLib("imm32.dll");
Bram Moolenaar071d4272004-06-13 20:20:40 +00008313 if (hLibImm == NULL)
8314 return;
8315
8316 pImmGetCompositionStringA
8317 = (void *)GetProcAddress(hLibImm, "ImmGetCompositionStringA");
8318 pImmGetCompositionStringW
8319 = (void *)GetProcAddress(hLibImm, "ImmGetCompositionStringW");
8320 pImmGetContext
8321 = (void *)GetProcAddress(hLibImm, "ImmGetContext");
8322 pImmAssociateContext
8323 = (void *)GetProcAddress(hLibImm, "ImmAssociateContext");
8324 pImmReleaseContext
8325 = (void *)GetProcAddress(hLibImm, "ImmReleaseContext");
8326 pImmGetOpenStatus
8327 = (void *)GetProcAddress(hLibImm, "ImmGetOpenStatus");
8328 pImmSetOpenStatus
8329 = (void *)GetProcAddress(hLibImm, "ImmSetOpenStatus");
8330 pImmGetCompositionFont
8331 = (void *)GetProcAddress(hLibImm, "ImmGetCompositionFontA");
8332 pImmSetCompositionFont
8333 = (void *)GetProcAddress(hLibImm, "ImmSetCompositionFontA");
8334 pImmSetCompositionWindow
8335 = (void *)GetProcAddress(hLibImm, "ImmSetCompositionWindow");
8336 pImmGetConversionStatus
8337 = (void *)GetProcAddress(hLibImm, "ImmGetConversionStatus");
Bram Moolenaarca003e12006-03-17 23:19:38 +00008338 pImmSetConversionStatus
8339 = (void *)GetProcAddress(hLibImm, "ImmSetConversionStatus");
Bram Moolenaar071d4272004-06-13 20:20:40 +00008340
8341 if ( pImmGetCompositionStringA == NULL
8342 || pImmGetCompositionStringW == NULL
8343 || pImmGetContext == NULL
8344 || pImmAssociateContext == NULL
8345 || pImmReleaseContext == NULL
8346 || pImmGetOpenStatus == NULL
8347 || pImmSetOpenStatus == NULL
8348 || pImmGetCompositionFont == NULL
8349 || pImmSetCompositionFont == NULL
8350 || pImmSetCompositionWindow == NULL
Bram Moolenaarca003e12006-03-17 23:19:38 +00008351 || pImmGetConversionStatus == NULL
8352 || pImmSetConversionStatus == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008353 {
8354 FreeLibrary(hLibImm);
8355 hLibImm = NULL;
8356 pImmGetContext = NULL;
8357 return;
8358 }
8359
8360 return;
8361}
8362
Bram Moolenaar071d4272004-06-13 20:20:40 +00008363#endif
8364
8365#if defined(FEAT_SIGN_ICONS) || defined(PROTO)
8366
8367# ifdef FEAT_XPM_W32
8368# define IMAGE_XPM 100
8369# endif
8370
8371typedef struct _signicon_t
8372{
8373 HANDLE hImage;
8374 UINT uType;
8375#ifdef FEAT_XPM_W32
8376 HANDLE hShape; /* Mask bitmap handle */
8377#endif
8378} signicon_t;
8379
8380 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01008381gui_mch_drawsign(int row, int col, int typenr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008382{
8383 signicon_t *sign;
8384 int x, y, w, h;
8385
8386 if (!gui.in_use || (sign = (signicon_t *)sign_get_image(typenr)) == NULL)
8387 return;
8388
8389 x = TEXT_X(col);
8390 y = TEXT_Y(row);
8391 w = gui.char_width * 2;
8392 h = gui.char_height;
8393 switch (sign->uType)
8394 {
8395 case IMAGE_BITMAP:
8396 {
8397 HDC hdcMem;
8398 HBITMAP hbmpOld;
8399
8400 hdcMem = CreateCompatibleDC(s_hdc);
8401 hbmpOld = (HBITMAP)SelectObject(hdcMem, sign->hImage);
8402 BitBlt(s_hdc, x, y, w, h, hdcMem, 0, 0, SRCCOPY);
8403 SelectObject(hdcMem, hbmpOld);
8404 DeleteDC(hdcMem);
8405 }
8406 break;
8407 case IMAGE_ICON:
8408 case IMAGE_CURSOR:
8409 DrawIconEx(s_hdc, x, y, (HICON)sign->hImage, w, h, 0, NULL, DI_NORMAL);
8410 break;
8411#ifdef FEAT_XPM_W32
8412 case IMAGE_XPM:
8413 {
8414 HDC hdcMem;
8415 HBITMAP hbmpOld;
8416
8417 hdcMem = CreateCompatibleDC(s_hdc);
8418 hbmpOld = (HBITMAP)SelectObject(hdcMem, sign->hShape);
8419 /* Make hole */
8420 BitBlt(s_hdc, x, y, w, h, hdcMem, 0, 0, SRCAND);
8421
8422 SelectObject(hdcMem, sign->hImage);
8423 /* Paint sign */
8424 BitBlt(s_hdc, x, y, w, h, hdcMem, 0, 0, SRCPAINT);
8425 SelectObject(hdcMem, hbmpOld);
8426 DeleteDC(hdcMem);
8427 }
8428 break;
8429#endif
8430 }
8431}
8432
8433 static void
8434close_signicon_image(signicon_t *sign)
8435{
8436 if (sign)
8437 switch (sign->uType)
8438 {
8439 case IMAGE_BITMAP:
8440 DeleteObject((HGDIOBJ)sign->hImage);
8441 break;
8442 case IMAGE_CURSOR:
8443 DestroyCursor((HCURSOR)sign->hImage);
8444 break;
8445 case IMAGE_ICON:
8446 DestroyIcon((HICON)sign->hImage);
8447 break;
8448#ifdef FEAT_XPM_W32
8449 case IMAGE_XPM:
8450 DeleteObject((HBITMAP)sign->hImage);
8451 DeleteObject((HBITMAP)sign->hShape);
8452 break;
8453#endif
8454 }
8455}
8456
8457 void *
Bram Moolenaar68c2f632016-01-30 17:24:07 +01008458gui_mch_register_sign(char_u *signfile)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008459{
8460 signicon_t sign, *psign;
8461 char_u *ext;
8462
Bram Moolenaar071d4272004-06-13 20:20:40 +00008463 sign.hImage = NULL;
Bram Moolenaar84a05ac2013-05-06 04:24:17 +02008464 ext = signfile + STRLEN(signfile) - 4; /* get extension */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008465 if (ext > signfile)
8466 {
8467 int do_load = 1;
8468
8469 if (!STRICMP(ext, ".bmp"))
8470 sign.uType = IMAGE_BITMAP;
8471 else if (!STRICMP(ext, ".ico"))
8472 sign.uType = IMAGE_ICON;
8473 else if (!STRICMP(ext, ".cur") || !STRICMP(ext, ".ani"))
8474 sign.uType = IMAGE_CURSOR;
8475 else
8476 do_load = 0;
8477
8478 if (do_load)
Bram Moolenaar418f81b2016-02-16 20:12:02 +01008479 sign.hImage = (HANDLE)LoadImage(NULL, (LPCSTR)signfile, sign.uType,
Bram Moolenaar071d4272004-06-13 20:20:40 +00008480 gui.char_width * 2, gui.char_height,
8481 LR_LOADFROMFILE | LR_CREATEDIBSECTION);
8482#ifdef FEAT_XPM_W32
8483 if (!STRICMP(ext, ".xpm"))
8484 {
8485 sign.uType = IMAGE_XPM;
Bram Moolenaar418f81b2016-02-16 20:12:02 +01008486 LoadXpmImage((char *)signfile, (HBITMAP *)&sign.hImage,
8487 (HBITMAP *)&sign.hShape);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008488 }
8489#endif
8490 }
8491
8492 psign = NULL;
8493 if (sign.hImage && (psign = (signicon_t *)alloc(sizeof(signicon_t)))
8494 != NULL)
8495 *psign = sign;
8496
8497 if (!psign)
8498 {
8499 if (sign.hImage)
8500 close_signicon_image(&sign);
8501 EMSG(_(e_signdata));
8502 }
8503 return (void *)psign;
8504
8505}
8506
8507 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01008508gui_mch_destroy_sign(void *sign)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008509{
8510 if (sign)
8511 {
8512 close_signicon_image((signicon_t *)sign);
8513 vim_free(sign);
8514 }
8515}
Bram Moolenaar5c06f8b2005-05-31 22:14:58 +00008516#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008517
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01008518#if defined(FEAT_BEVAL_GUI) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008519
8520/* BALLOON-EVAL IMPLEMENTATION FOR WINDOWS.
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00008521 * Added by Sergey Khorev <sergey.khorev@gmail.com>
Bram Moolenaar071d4272004-06-13 20:20:40 +00008522 *
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01008523 * The only reused thing is beval.h and get_beval_info()
Bram Moolenaar071d4272004-06-13 20:20:40 +00008524 * from gui_beval.c (note it uses x and y of the BalloonEval struct
8525 * to get current mouse position).
8526 *
8527 * Trying to use as more Windows services as possible, and as less
8528 * IE version as possible :)).
8529 *
8530 * 1) Don't create ToolTip in gui_mch_create_beval_area, only initialize
8531 * BalloonEval struct.
8532 * 2) Enable/Disable simply create/kill BalloonEval Timer
8533 * 3) When there was enough inactivity, timer procedure posts
8534 * async request to debugger
8535 * 4) gui_mch_post_balloon (invoked from netbeans.c) creates tooltip control
8536 * and performs some actions to show it ASAP
Bram Moolenaar446cb832008-06-24 21:56:24 +00008537 * 5) WM_NOTIFY:TTN_POP destroys created tooltip
Bram Moolenaar071d4272004-06-13 20:20:40 +00008538 */
8539
Bram Moolenaar45360022005-07-21 21:08:21 +00008540/*
8541 * determine whether installed Common Controls support multiline tooltips
8542 * (i.e. their version is >= 4.70
8543 */
8544 int
8545multiline_balloon_available(void)
8546{
8547 HINSTANCE hDll;
8548 static char comctl_dll[] = "comctl32.dll";
8549 static int multiline_tip = MAYBE;
8550
8551 if (multiline_tip != MAYBE)
8552 return multiline_tip;
8553
8554 hDll = GetModuleHandle(comctl_dll);
8555 if (hDll != NULL)
8556 {
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00008557 DLLGETVERSIONPROC pGetVer;
8558 pGetVer = (DLLGETVERSIONPROC)GetProcAddress(hDll, "DllGetVersion");
Bram Moolenaar45360022005-07-21 21:08:21 +00008559
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00008560 if (pGetVer != NULL)
8561 {
8562 DLLVERSIONINFO dvi;
8563 HRESULT hr;
Bram Moolenaar45360022005-07-21 21:08:21 +00008564
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00008565 ZeroMemory(&dvi, sizeof(dvi));
8566 dvi.cbSize = sizeof(dvi);
Bram Moolenaar45360022005-07-21 21:08:21 +00008567
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00008568 hr = (*pGetVer)(&dvi);
Bram Moolenaar45360022005-07-21 21:08:21 +00008569
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00008570 if (SUCCEEDED(hr)
Bram Moolenaar45360022005-07-21 21:08:21 +00008571 && (dvi.dwMajorVersion > 4
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00008572 || (dvi.dwMajorVersion == 4
8573 && dvi.dwMinorVersion >= 70)))
Bram Moolenaar45360022005-07-21 21:08:21 +00008574 {
8575 multiline_tip = TRUE;
8576 return multiline_tip;
8577 }
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00008578 }
Bram Moolenaar45360022005-07-21 21:08:21 +00008579 else
8580 {
8581 /* there is chance we have ancient CommCtl 4.70
8582 which doesn't export DllGetVersion */
8583 DWORD dwHandle = 0;
8584 DWORD len = GetFileVersionInfoSize(comctl_dll, &dwHandle);
8585 if (len > 0)
8586 {
8587 VS_FIXEDFILEINFO *ver;
8588 UINT vlen = 0;
8589 void *data = alloc(len);
8590
Bram Moolenaar418f81b2016-02-16 20:12:02 +01008591 if ((data != NULL
Bram Moolenaar45360022005-07-21 21:08:21 +00008592 && GetFileVersionInfo(comctl_dll, 0, len, data)
8593 && VerQueryValue(data, "\\", (void **)&ver, &vlen)
8594 && vlen
Bram Moolenaar418f81b2016-02-16 20:12:02 +01008595 && HIWORD(ver->dwFileVersionMS) > 4)
8596 || ((HIWORD(ver->dwFileVersionMS) == 4
8597 && LOWORD(ver->dwFileVersionMS) >= 70)))
Bram Moolenaar45360022005-07-21 21:08:21 +00008598 {
8599 vim_free(data);
8600 multiline_tip = TRUE;
8601 return multiline_tip;
8602 }
8603 vim_free(data);
8604 }
8605 }
8606 }
8607 multiline_tip = FALSE;
8608 return multiline_tip;
8609}
8610
Bram Moolenaar071d4272004-06-13 20:20:40 +00008611 static void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01008612make_tooltip(BalloonEval *beval, char *text, POINT pt)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008613{
Bram Moolenaar45360022005-07-21 21:08:21 +00008614 TOOLINFO *pti;
8615 int ToolInfoSize;
8616
8617 if (multiline_balloon_available() == TRUE)
8618 ToolInfoSize = sizeof(TOOLINFO_NEW);
8619 else
8620 ToolInfoSize = sizeof(TOOLINFO);
8621
8622 pti = (TOOLINFO *)alloc(ToolInfoSize);
8623 if (pti == NULL)
8624 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008625
8626 beval->balloon = CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS,
8627 NULL, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,
8628 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
8629 beval->target, NULL, s_hinst, NULL);
8630
8631 SetWindowPos(beval->balloon, HWND_TOPMOST, 0, 0, 0, 0,
8632 SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
8633
Bram Moolenaar45360022005-07-21 21:08:21 +00008634 pti->cbSize = ToolInfoSize;
8635 pti->uFlags = TTF_SUBCLASS;
8636 pti->hwnd = beval->target;
8637 pti->hinst = 0; /* Don't use string resources */
8638 pti->uId = ID_BEVAL_TOOLTIP;
8639
8640 if (multiline_balloon_available() == TRUE)
8641 {
8642 RECT rect;
8643 TOOLINFO_NEW *ptin = (TOOLINFO_NEW *)pti;
8644 pti->lpszText = LPSTR_TEXTCALLBACK;
8645 ptin->lParam = (LPARAM)text;
8646 if (GetClientRect(s_textArea, &rect)) /* switch multiline tooltips on */
8647 SendMessage(beval->balloon, TTM_SETMAXTIPWIDTH, 0,
8648 (LPARAM)rect.right);
8649 }
8650 else
8651 pti->lpszText = text; /* do this old way */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008652
8653 /* Limit ballooneval bounding rect to CursorPos neighbourhood */
Bram Moolenaar45360022005-07-21 21:08:21 +00008654 pti->rect.left = pt.x - 3;
8655 pti->rect.top = pt.y - 3;
8656 pti->rect.right = pt.x + 3;
8657 pti->rect.bottom = pt.y + 3;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008658
Bram Moolenaar45360022005-07-21 21:08:21 +00008659 SendMessage(beval->balloon, TTM_ADDTOOL, 0, (LPARAM)pti);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008660 /* Make tooltip appear sooner */
8661 SendMessage(beval->balloon, TTM_SETDELAYTIME, TTDT_INITIAL, 10);
Bram Moolenaarb52e5322008-01-05 12:15:52 +00008662 /* I've performed some tests and it seems the longest possible life time
8663 * of tooltip is 30 seconds */
8664 SendMessage(beval->balloon, TTM_SETDELAYTIME, TTDT_AUTOPOP, 30000);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008665 /*
8666 * HACK: force tooltip to appear, because it'll not appear until
8667 * first mouse move. D*mn M$
Bram Moolenaarb52e5322008-01-05 12:15:52 +00008668 * Amazingly moving (2, 2) and then (-1, -1) the mouse doesn't move.
Bram Moolenaar071d4272004-06-13 20:20:40 +00008669 */
Bram Moolenaarb52e5322008-01-05 12:15:52 +00008670 mouse_event(MOUSEEVENTF_MOVE, 2, 2, 0, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008671 mouse_event(MOUSEEVENTF_MOVE, (DWORD)-1, (DWORD)-1, 0, 0);
Bram Moolenaar45360022005-07-21 21:08:21 +00008672 vim_free(pti);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008673}
8674
8675 static void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01008676delete_tooltip(BalloonEval *beval)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008677{
Bram Moolenaar8e5f5b42015-08-26 23:12:38 +02008678 PostMessage(beval->balloon, WM_CLOSE, 0, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008679}
8680
8681 static VOID CALLBACK
Bram Moolenaar68c2f632016-01-30 17:24:07 +01008682BevalTimerProc(
Bram Moolenaar1266d672017-02-01 13:43:36 +01008683 HWND hwnd UNUSED,
8684 UINT uMsg UNUSED,
8685 UINT_PTR idEvent UNUSED,
8686 DWORD dwTime)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008687{
8688 POINT pt;
8689 RECT rect;
8690
8691 if (cur_beval == NULL || cur_beval->showState == ShS_SHOWING || !p_beval)
8692 return;
8693
8694 GetCursorPos(&pt);
8695 if (WindowFromPoint(pt) != s_textArea)
8696 return;
8697
8698 ScreenToClient(s_textArea, &pt);
8699 GetClientRect(s_textArea, &rect);
8700 if (!PtInRect(&rect, pt))
8701 return;
8702
8703 if (LastActivity > 0
8704 && (dwTime - LastActivity) >= (DWORD)p_bdlay
8705 && (cur_beval->showState != ShS_PENDING
8706 || abs(cur_beval->x - pt.x) > 3
8707 || abs(cur_beval->y - pt.y) > 3))
8708 {
8709 /* Pointer resting in one place long enough, it's time to show
8710 * the tooltip. */
8711 cur_beval->showState = ShS_PENDING;
8712 cur_beval->x = pt.x;
8713 cur_beval->y = pt.y;
8714
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008715 // TRACE0("BevalTimerProc: sending request");
Bram Moolenaar071d4272004-06-13 20:20:40 +00008716
8717 if (cur_beval->msgCB != NULL)
8718 (*cur_beval->msgCB)(cur_beval, 0);
8719 }
8720}
8721
8722 void
Bram Moolenaar1266d672017-02-01 13:43:36 +01008723gui_mch_disable_beval_area(BalloonEval *beval UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008724{
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008725 // TRACE0("gui_mch_disable_beval_area {{{");
Bram Moolenaar071d4272004-06-13 20:20:40 +00008726 KillTimer(s_textArea, BevalTimerId);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008727 // TRACE0("gui_mch_disable_beval_area }}}");
Bram Moolenaar071d4272004-06-13 20:20:40 +00008728}
8729
8730 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01008731gui_mch_enable_beval_area(BalloonEval *beval)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008732{
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008733 // TRACE0("gui_mch_enable_beval_area |||");
Bram Moolenaar071d4272004-06-13 20:20:40 +00008734 if (beval == NULL)
8735 return;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008736 // TRACE0("gui_mch_enable_beval_area {{{");
Bram Moolenaar167632f2010-05-26 21:42:54 +02008737 BevalTimerId = SetTimer(s_textArea, 0, (UINT)(p_bdlay / 2), BevalTimerProc);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008738 // TRACE0("gui_mch_enable_beval_area }}}");
Bram Moolenaar071d4272004-06-13 20:20:40 +00008739}
8740
8741 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01008742gui_mch_post_balloon(BalloonEval *beval, char_u *mesg)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008743{
8744 POINT pt;
Bram Moolenaar1c465442017-03-12 20:10:05 +01008745
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008746 // TRACE0("gui_mch_post_balloon {{{");
Bram Moolenaar071d4272004-06-13 20:20:40 +00008747 if (beval->showState == ShS_SHOWING)
8748 return;
8749 GetCursorPos(&pt);
8750 ScreenToClient(s_textArea, &pt);
8751
8752 if (abs(beval->x - pt.x) < 3 && abs(beval->y - pt.y) < 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008753 {
Bram Moolenaar1c465442017-03-12 20:10:05 +01008754 /* cursor is still here */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008755 gui_mch_disable_beval_area(cur_beval);
8756 beval->showState = ShS_SHOWING;
Bram Moolenaar418f81b2016-02-16 20:12:02 +01008757 make_tooltip(beval, (char *)mesg, pt);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008758 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008759 // TRACE0("gui_mch_post_balloon }}}");
Bram Moolenaar071d4272004-06-13 20:20:40 +00008760}
8761
8762 BalloonEval *
Bram Moolenaar68c2f632016-01-30 17:24:07 +01008763gui_mch_create_beval_area(
8764 void *target, /* ignored, always use s_textArea */
8765 char_u *mesg,
8766 void (*mesgCB)(BalloonEval *, int),
8767 void *clientData)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008768{
8769 /* partially stolen from gui_beval.c */
8770 BalloonEval *beval;
8771
8772 if (mesg != NULL && mesgCB != NULL)
8773 {
Bram Moolenaar95f09602016-11-10 20:01:45 +01008774 IEMSG(_("E232: Cannot create BalloonEval with both message and callback"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00008775 return NULL;
8776 }
8777
8778 beval = (BalloonEval *)alloc(sizeof(BalloonEval));
8779 if (beval != NULL)
8780 {
8781 beval->target = s_textArea;
8782 beval->balloon = NULL;
8783
8784 beval->showState = ShS_NEUTRAL;
8785 beval->x = 0;
8786 beval->y = 0;
8787 beval->msg = mesg;
8788 beval->msgCB = mesgCB;
8789 beval->clientData = clientData;
8790
8791 InitCommonControls();
Bram Moolenaar071d4272004-06-13 20:20:40 +00008792 cur_beval = beval;
8793
8794 if (p_beval)
8795 gui_mch_enable_beval_area(beval);
8796
8797 }
8798 return beval;
8799}
8800
8801 static void
Bram Moolenaar1266d672017-02-01 13:43:36 +01008802Handle_WM_Notify(HWND hwnd UNUSED, LPNMHDR pnmh)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008803{
8804 if (pnmh->idFrom != ID_BEVAL_TOOLTIP) /* it is not our tooltip */
8805 return;
8806
8807 if (cur_beval != NULL)
8808 {
Bram Moolenaar45360022005-07-21 21:08:21 +00008809 switch (pnmh->code)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008810 {
Bram Moolenaar45360022005-07-21 21:08:21 +00008811 case TTN_SHOW:
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008812 // TRACE0("TTN_SHOW {{{");
8813 // TRACE0("TTN_SHOW }}}");
Bram Moolenaar45360022005-07-21 21:08:21 +00008814 break;
8815 case TTN_POP: /* Before tooltip disappear */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008816 // TRACE0("TTN_POP {{{");
Bram Moolenaar071d4272004-06-13 20:20:40 +00008817 delete_tooltip(cur_beval);
8818 gui_mch_enable_beval_area(cur_beval);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008819 // TRACE0("TTN_POP }}}");
Bram Moolenaar071d4272004-06-13 20:20:40 +00008820
8821 cur_beval->showState = ShS_NEUTRAL;
Bram Moolenaar45360022005-07-21 21:08:21 +00008822 break;
8823 case TTN_GETDISPINFO:
Bram Moolenaar6c9176d2008-01-03 19:45:15 +00008824 {
8825 /* if you get there then we have new common controls */
8826 NMTTDISPINFO_NEW *info = (NMTTDISPINFO_NEW *)pnmh;
8827 info->lpszText = (LPSTR)info->lParam;
8828 info->uFlags |= TTF_DI_SETITEM;
8829 }
Bram Moolenaar45360022005-07-21 21:08:21 +00008830 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008831 }
8832 }
8833}
8834
8835 static void
8836TrackUserActivity(UINT uMsg)
8837{
8838 if ((uMsg >= WM_MOUSEFIRST && uMsg <= WM_MOUSELAST)
8839 || (uMsg >= WM_KEYFIRST && uMsg <= WM_KEYLAST))
8840 LastActivity = GetTickCount();
8841}
8842
8843 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01008844gui_mch_destroy_beval_area(BalloonEval *beval)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008845{
8846 vim_free(beval);
8847}
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01008848#endif /* FEAT_BEVAL_GUI */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008849
8850#if defined(FEAT_NETBEANS_INTG) || defined(PROTO)
8851/*
8852 * We have multiple signs to draw at the same location. Draw the
8853 * multi-sign indicator (down-arrow) instead. This is the Win32 version.
8854 */
8855 void
8856netbeans_draw_multisign_indicator(int row)
8857{
8858 int i;
8859 int y;
8860 int x;
8861
Bram Moolenaarb26e6322010-05-22 21:34:09 +02008862 if (!netbeans_active())
Bram Moolenaarcc448b32010-07-14 16:52:17 +02008863 return;
Bram Moolenaarb26e6322010-05-22 21:34:09 +02008864
Bram Moolenaar071d4272004-06-13 20:20:40 +00008865 x = 0;
8866 y = TEXT_Y(row);
8867
8868 for (i = 0; i < gui.char_height - 3; i++)
8869 SetPixel(s_hdc, x+2, y++, gui.currFgColor);
8870
8871 SetPixel(s_hdc, x+0, y, gui.currFgColor);
8872 SetPixel(s_hdc, x+2, y, gui.currFgColor);
8873 SetPixel(s_hdc, x+4, y++, gui.currFgColor);
8874 SetPixel(s_hdc, x+1, y, gui.currFgColor);
8875 SetPixel(s_hdc, x+2, y, gui.currFgColor);
8876 SetPixel(s_hdc, x+3, y++, gui.currFgColor);
8877 SetPixel(s_hdc, x+2, y, gui.currFgColor);
8878}
Bram Moolenaare0874f82016-01-24 20:36:41 +01008879#endif