blob: a91e0710beb38fc1e780b906f6159e3a16942264 [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
200#if defined(FEAT_TOOLBAR) || defined(FEAT_BEVAL) || defined(FEAT_GUI_TABLINE)
201# 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 Moolenaarcf7164a2016-02-20 13:55:06 +0100476#ifdef FEAT_BEVAL
477/* 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
488# ifdef USE_IM_CONTROL
489/* holds LOGFONT for 'guifontwide' if available, otherwise 'guifont' */
490static LOGFONT norm_logfont;
491/* holds LOGFONT for 'guifont' always. */
492static LOGFONT sub_logfont;
493# endif
494#endif
495
496#ifdef FEAT_MBYTE_IME
497static LRESULT _OnImeNotify(HWND hWnd, DWORD dwCommand, DWORD dwData);
498#endif
499
500#if defined(FEAT_BROWSE)
501static char_u *convert_filter(char_u *s);
502#endif
503
504#ifdef DEBUG_PRINT_ERROR
505/*
506 * Print out the last Windows error message
507 */
508 static void
509print_windows_error(void)
510{
511 LPVOID lpMsgBuf;
512
513 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
514 NULL, GetLastError(),
515 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
516 (LPTSTR) &lpMsgBuf, 0, NULL);
517 TRACE1("Error: %s\n", lpMsgBuf);
518 LocalFree(lpMsgBuf);
519}
520#endif
521
522/*
523 * Cursor blink functions.
524 *
525 * This is a simple state machine:
526 * BLINK_NONE not blinking at all
527 * BLINK_OFF blinking, cursor is not shown
528 * BLINK_ON blinking, cursor is shown
529 */
530
531#define BLINK_NONE 0
532#define BLINK_OFF 1
533#define BLINK_ON 2
534
535static int blink_state = BLINK_NONE;
536static long_u blink_waittime = 700;
537static long_u blink_ontime = 400;
538static long_u blink_offtime = 250;
539static UINT blink_timer = 0;
540
Bram Moolenaar703a8042016-06-04 16:24:32 +0200541 int
542gui_mch_is_blinking(void)
543{
544 return blink_state != BLINK_NONE;
545}
546
Bram Moolenaar9d5d3c92016-07-07 16:43:02 +0200547 int
548gui_mch_is_blink_off(void)
549{
550 return blink_state == BLINK_OFF;
551}
552
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100553 void
554gui_mch_set_blinking(long wait, long on, long off)
555{
556 blink_waittime = wait;
557 blink_ontime = on;
558 blink_offtime = off;
559}
560
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100561 static VOID CALLBACK
562_OnBlinkTimer(
563 HWND hwnd,
Bram Moolenaar1266d672017-02-01 13:43:36 +0100564 UINT uMsg UNUSED,
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100565 UINT idEvent,
Bram Moolenaar1266d672017-02-01 13:43:36 +0100566 DWORD dwTime UNUSED)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100567{
568 MSG msg;
569
570 /*
571 TRACE2("Got timer event, id %d, blink_timer %d\n", idEvent, blink_timer);
572 */
573
574 KillTimer(NULL, idEvent);
575
576 /* Eat spurious WM_TIMER messages */
577 while (pPeekMessage(&msg, hwnd, WM_TIMER, WM_TIMER, PM_REMOVE))
578 ;
579
580 if (blink_state == BLINK_ON)
581 {
582 gui_undraw_cursor();
583 blink_state = BLINK_OFF;
584 blink_timer = (UINT) SetTimer(NULL, 0, (UINT)blink_offtime,
585 (TIMERPROC)_OnBlinkTimer);
586 }
587 else
588 {
589 gui_update_cursor(TRUE, FALSE);
590 blink_state = BLINK_ON;
591 blink_timer = (UINT) SetTimer(NULL, 0, (UINT)blink_ontime,
Bram Moolenaar1266d672017-02-01 13:43:36 +0100592 (TIMERPROC)_OnBlinkTimer);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100593 }
594}
595
596 static void
597gui_mswin_rm_blink_timer(void)
598{
599 MSG msg;
600
601 if (blink_timer != 0)
602 {
603 KillTimer(NULL, blink_timer);
604 /* Eat spurious WM_TIMER messages */
605 while (pPeekMessage(&msg, s_hwnd, WM_TIMER, WM_TIMER, PM_REMOVE))
606 ;
607 blink_timer = 0;
608 }
609}
610
611/*
612 * Stop the cursor blinking. Show the cursor if it wasn't shown.
613 */
614 void
615gui_mch_stop_blink(void)
616{
617 gui_mswin_rm_blink_timer();
618 if (blink_state == BLINK_OFF)
619 gui_update_cursor(TRUE, FALSE);
620 blink_state = BLINK_NONE;
621}
622
623/*
624 * Start the cursor blinking. If it was already blinking, this restarts the
625 * waiting time and shows the cursor.
626 */
627 void
628gui_mch_start_blink(void)
629{
630 gui_mswin_rm_blink_timer();
631
632 /* Only switch blinking on if none of the times is zero */
633 if (blink_waittime && blink_ontime && blink_offtime && gui.in_focus)
634 {
635 blink_timer = (UINT)SetTimer(NULL, 0, (UINT)blink_waittime,
636 (TIMERPROC)_OnBlinkTimer);
637 blink_state = BLINK_ON;
638 gui_update_cursor(TRUE, FALSE);
639 }
640}
641
642/*
643 * Call-back routines.
644 */
645
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100646 static VOID CALLBACK
647_OnTimer(
648 HWND hwnd,
Bram Moolenaar1266d672017-02-01 13:43:36 +0100649 UINT uMsg UNUSED,
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100650 UINT idEvent,
Bram Moolenaar1266d672017-02-01 13:43:36 +0100651 DWORD dwTime UNUSED)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100652{
653 MSG msg;
654
655 /*
656 TRACE2("Got timer event, id %d, s_wait_timer %d\n", idEvent, s_wait_timer);
657 */
658 KillTimer(NULL, idEvent);
659 s_timed_out = TRUE;
660
661 /* Eat spurious WM_TIMER messages */
662 while (pPeekMessage(&msg, hwnd, WM_TIMER, WM_TIMER, PM_REMOVE))
663 ;
664 if (idEvent == s_wait_timer)
665 s_wait_timer = 0;
666}
667
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100668 static void
669_OnDeadChar(
Bram Moolenaar1266d672017-02-01 13:43:36 +0100670 HWND hwnd UNUSED,
671 UINT ch UNUSED,
672 int cRepeat UNUSED)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100673{
674 dead_key = 1;
675}
676
677/*
678 * Convert Unicode character "ch" to bytes in "string[slen]".
679 * When "had_alt" is TRUE the ALT key was included in "ch".
680 * Return the length.
681 */
682 static int
683char_to_string(int ch, char_u *string, int slen, int had_alt)
684{
685 int len;
686 int i;
687#ifdef FEAT_MBYTE
688 WCHAR wstring[2];
Bram Moolenaar945ec092016-06-08 21:17:43 +0200689 char_u *ws = NULL;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100690
Bram Moolenaarcea912a2016-10-12 14:20:24 +0200691 wstring[0] = ch;
692 len = 1;
693
694 /* "ch" is a UTF-16 character. Convert it to a string of bytes. When
695 * "enc_codepage" is non-zero use the standard Win32 function,
696 * otherwise use our own conversion function (e.g., for UTF-8). */
697 if (enc_codepage > 0)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100698 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +0200699 len = WideCharToMultiByte(enc_codepage, 0, wstring, len,
700 (LPSTR)string, slen, 0, NULL);
701 /* If we had included the ALT key into the character but now the
702 * upper bit is no longer set, that probably means the conversion
703 * failed. Convert the original character and set the upper bit
704 * afterwards. */
705 if (had_alt && len == 1 && ch >= 0x80 && string[0] < 0x80)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100706 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +0200707 wstring[0] = ch & 0x7f;
708 len = WideCharToMultiByte(enc_codepage, 0, wstring, len,
709 (LPSTR)string, slen, 0, NULL);
710 if (len == 1) /* safety check */
711 string[0] |= 0x80;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100712 }
713 }
714 else
715 {
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100716 len = 1;
Bram Moolenaarcea912a2016-10-12 14:20:24 +0200717 ws = utf16_to_enc(wstring, &len);
718 if (ws == NULL)
719 len = 0;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100720 else
721 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +0200722 if (len > slen) /* just in case */
723 len = slen;
724 mch_memmove(string, ws, len);
725 vim_free(ws);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100726 }
727 }
728
729 if (len == 0)
730#endif
731 {
732 string[0] = ch;
733 len = 1;
734 }
735
736 for (i = 0; i < len; ++i)
737 if (string[i] == CSI && len <= slen - 2)
738 {
739 /* Insert CSI as K_CSI. */
740 mch_memmove(string + i + 3, string + i + 1, len - i - 1);
741 string[++i] = KS_EXTRA;
742 string[++i] = (int)KE_CSI;
743 len += 2;
744 }
745
746 return len;
747}
748
749/*
750 * Key hit, add it to the input buffer.
751 */
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100752 static void
753_OnChar(
Bram Moolenaar1266d672017-02-01 13:43:36 +0100754 HWND hwnd UNUSED,
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100755 UINT ch,
Bram Moolenaar1266d672017-02-01 13:43:36 +0100756 int cRepeat UNUSED)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100757{
758 char_u string[40];
759 int len = 0;
760
761 dead_key = 0;
762
763 len = char_to_string(ch, string, 40, FALSE);
764 if (len == 1 && string[0] == Ctrl_C && ctrl_c_interrupts)
765 {
766 trash_input_buf();
767 got_int = TRUE;
768 }
769
770 add_to_input_buf(string, len);
771}
772
773/*
774 * Alt-Key hit, add it to the input buffer.
775 */
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100776 static void
777_OnSysChar(
Bram Moolenaar1266d672017-02-01 13:43:36 +0100778 HWND hwnd UNUSED,
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100779 UINT cch,
Bram Moolenaar1266d672017-02-01 13:43:36 +0100780 int cRepeat UNUSED)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100781{
782 char_u string[40]; /* Enough for multibyte character */
783 int len;
784 int modifiers;
785 int ch = cch; /* special keys are negative */
786
787 dead_key = 0;
788
789 /* TRACE("OnSysChar(%d, %c)\n", ch, ch); */
790
791 /* OK, we have a character key (given by ch) which was entered with the
792 * ALT key pressed. Eg, if the user presses Alt-A, then ch == 'A'. Note
793 * that the system distinguishes Alt-a and Alt-A (Alt-Shift-a unless
794 * CAPSLOCK is pressed) at this point.
795 */
796 modifiers = MOD_MASK_ALT;
797 if (GetKeyState(VK_SHIFT) & 0x8000)
798 modifiers |= MOD_MASK_SHIFT;
799 if (GetKeyState(VK_CONTROL) & 0x8000)
800 modifiers |= MOD_MASK_CTRL;
801
802 ch = simplify_key(ch, &modifiers);
803 /* remove the SHIFT modifier for keys where it's already included, e.g.,
804 * '(' and '*' */
805 if (ch < 0x100 && !isalpha(ch) && isprint(ch))
806 modifiers &= ~MOD_MASK_SHIFT;
807
808 /* Interpret the ALT key as making the key META, include SHIFT, etc. */
809 ch = extract_modifiers(ch, &modifiers);
810 if (ch == CSI)
811 ch = K_CSI;
812
813 len = 0;
814 if (modifiers)
815 {
816 string[len++] = CSI;
817 string[len++] = KS_MODIFIER;
818 string[len++] = modifiers;
819 }
820
821 if (IS_SPECIAL((int)ch))
822 {
823 string[len++] = CSI;
824 string[len++] = K_SECOND((int)ch);
825 string[len++] = K_THIRD((int)ch);
826 }
827 else
828 {
829 /* Although the documentation isn't clear about it, we assume "ch" is
830 * a Unicode character. */
831 len += char_to_string(ch, string + len, 40 - len, TRUE);
832 }
833
834 add_to_input_buf(string, len);
835}
836
837 static void
838_OnMouseEvent(
839 int button,
840 int x,
841 int y,
842 int repeated_click,
843 UINT keyFlags)
844{
845 int vim_modifiers = 0x0;
846
847 s_getting_focus = FALSE;
848
849 if (keyFlags & MK_SHIFT)
850 vim_modifiers |= MOUSE_SHIFT;
851 if (keyFlags & MK_CONTROL)
852 vim_modifiers |= MOUSE_CTRL;
853 if (GetKeyState(VK_MENU) & 0x8000)
854 vim_modifiers |= MOUSE_ALT;
855
856 gui_send_mouse_event(button, x, y, repeated_click, vim_modifiers);
857}
858
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100859 static void
860_OnMouseButtonDown(
Bram Moolenaar1266d672017-02-01 13:43:36 +0100861 HWND hwnd UNUSED,
862 BOOL fDoubleClick UNUSED,
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100863 int x,
864 int y,
865 UINT keyFlags)
866{
867 static LONG s_prevTime = 0;
868
869 LONG currentTime = GetMessageTime();
870 int button = -1;
871 int repeated_click;
872
873 /* Give main window the focus: this is so the cursor isn't hollow. */
874 (void)SetFocus(s_hwnd);
875
876 if (s_uMsg == WM_LBUTTONDOWN || s_uMsg == WM_LBUTTONDBLCLK)
877 button = MOUSE_LEFT;
878 else if (s_uMsg == WM_MBUTTONDOWN || s_uMsg == WM_MBUTTONDBLCLK)
879 button = MOUSE_MIDDLE;
880 else if (s_uMsg == WM_RBUTTONDOWN || s_uMsg == WM_RBUTTONDBLCLK)
881 button = MOUSE_RIGHT;
882 else if (s_uMsg == WM_XBUTTONDOWN || s_uMsg == WM_XBUTTONDBLCLK)
883 {
884#ifndef GET_XBUTTON_WPARAM
885# define GET_XBUTTON_WPARAM(wParam) (HIWORD(wParam))
886#endif
887 button = ((GET_XBUTTON_WPARAM(s_wParam) == 1) ? MOUSE_X1 : MOUSE_X2);
888 }
889 else if (s_uMsg == WM_CAPTURECHANGED)
890 {
891 /* on W95/NT4, somehow you get in here with an odd Msg
892 * if you press one button while holding down the other..*/
893 if (s_button_pending == MOUSE_LEFT)
894 button = MOUSE_RIGHT;
895 else
896 button = MOUSE_LEFT;
897 }
898 if (button >= 0)
899 {
900 repeated_click = ((int)(currentTime - s_prevTime) < p_mouset);
901
902 /*
903 * Holding down the left and right buttons simulates pushing the middle
904 * button.
905 */
906 if (repeated_click
907 && ((button == MOUSE_LEFT && s_button_pending == MOUSE_RIGHT)
908 || (button == MOUSE_RIGHT
909 && s_button_pending == MOUSE_LEFT)))
910 {
911 /*
912 * Hmm, gui.c will ignore more than one button down at a time, so
913 * pretend we let go of it first.
914 */
915 gui_send_mouse_event(MOUSE_RELEASE, x, y, FALSE, 0x0);
916 button = MOUSE_MIDDLE;
917 repeated_click = FALSE;
918 s_button_pending = -1;
919 _OnMouseEvent(button, x, y, repeated_click, keyFlags);
920 }
921 else if ((repeated_click)
922 || (mouse_model_popup() && (button == MOUSE_RIGHT)))
923 {
924 if (s_button_pending > -1)
925 {
926 _OnMouseEvent(s_button_pending, x, y, FALSE, keyFlags);
927 s_button_pending = -1;
928 }
929 /* TRACE("Button down at x %d, y %d\n", x, y); */
930 _OnMouseEvent(button, x, y, repeated_click, keyFlags);
931 }
932 else
933 {
934 /*
935 * If this is the first press (i.e. not a multiple click) don't
936 * action immediately, but store and wait for:
937 * i) button-up
938 * ii) mouse move
939 * iii) another button press
940 * before using it.
941 * This enables us to make left+right simulate middle button,
942 * without left or right being actioned first. The side-effect is
943 * that if you click and hold the mouse without dragging, the
944 * cursor doesn't move until you release the button. In practice
945 * this is hardly a problem.
946 */
947 s_button_pending = button;
948 s_x_pending = x;
949 s_y_pending = y;
950 s_kFlags_pending = keyFlags;
951 }
952
953 s_prevTime = currentTime;
954 }
955}
956
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100957 static void
958_OnMouseMoveOrRelease(
Bram Moolenaar1266d672017-02-01 13:43:36 +0100959 HWND hwnd UNUSED,
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100960 int x,
961 int y,
962 UINT keyFlags)
963{
964 int button;
965
966 s_getting_focus = FALSE;
967 if (s_button_pending > -1)
968 {
969 /* Delayed action for mouse down event */
970 _OnMouseEvent(s_button_pending, s_x_pending,
971 s_y_pending, FALSE, s_kFlags_pending);
972 s_button_pending = -1;
973 }
974 if (s_uMsg == WM_MOUSEMOVE)
975 {
976 /*
977 * It's only a MOUSE_DRAG if one or more mouse buttons are being held
978 * down.
979 */
980 if (!(keyFlags & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON
981 | MK_XBUTTON1 | MK_XBUTTON2)))
982 {
983 gui_mouse_moved(x, y);
984 return;
985 }
986
987 /*
988 * While button is down, keep grabbing mouse move events when
989 * the mouse goes outside the window
990 */
991 SetCapture(s_textArea);
992 button = MOUSE_DRAG;
993 /* TRACE(" move at x %d, y %d\n", x, y); */
994 }
995 else
996 {
997 ReleaseCapture();
998 button = MOUSE_RELEASE;
999 /* TRACE(" up at x %d, y %d\n", x, y); */
1000 }
1001
1002 _OnMouseEvent(button, x, y, FALSE, keyFlags);
1003}
1004
1005#ifdef FEAT_MENU
1006/*
1007 * Find the vimmenu_T with the given id
1008 */
1009 static vimmenu_T *
1010gui_mswin_find_menu(
1011 vimmenu_T *pMenu,
1012 int id)
1013{
1014 vimmenu_T *pChildMenu;
1015
1016 while (pMenu)
1017 {
1018 if (pMenu->id == (UINT)id)
1019 break;
1020 if (pMenu->children != NULL)
1021 {
1022 pChildMenu = gui_mswin_find_menu(pMenu->children, id);
1023 if (pChildMenu)
1024 {
1025 pMenu = pChildMenu;
1026 break;
1027 }
1028 }
1029 pMenu = pMenu->next;
1030 }
1031 return pMenu;
1032}
1033
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001034 static void
1035_OnMenu(
Bram Moolenaar1266d672017-02-01 13:43:36 +01001036 HWND hwnd UNUSED,
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001037 int id,
Bram Moolenaar1266d672017-02-01 13:43:36 +01001038 HWND hwndCtl UNUSED,
1039 UINT codeNotify UNUSED)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001040{
1041 vimmenu_T *pMenu;
1042
1043 pMenu = gui_mswin_find_menu(root_menu, id);
1044 if (pMenu)
1045 gui_menu_cb(pMenu);
1046}
1047#endif
1048
1049#ifdef MSWIN_FIND_REPLACE
Bram Moolenaarcea912a2016-10-12 14:20:24 +02001050# ifdef FEAT_MBYTE
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001051/*
1052 * copy useful data from structure LPFINDREPLACE to structure LPFINDREPLACEW
1053 */
1054 static void
1055findrep_atow(LPFINDREPLACEW lpfrw, LPFINDREPLACE lpfr)
1056{
1057 WCHAR *wp;
1058
1059 lpfrw->hwndOwner = lpfr->hwndOwner;
1060 lpfrw->Flags = lpfr->Flags;
1061
1062 wp = enc_to_utf16((char_u *)lpfr->lpstrFindWhat, NULL);
1063 wcsncpy(lpfrw->lpstrFindWhat, wp, lpfrw->wFindWhatLen - 1);
1064 vim_free(wp);
1065
1066 /* the field "lpstrReplaceWith" doesn't need to be copied */
1067}
1068
1069/*
1070 * copy useful data from structure LPFINDREPLACEW to structure LPFINDREPLACE
1071 */
1072 static void
1073findrep_wtoa(LPFINDREPLACE lpfr, LPFINDREPLACEW lpfrw)
1074{
1075 char_u *p;
1076
1077 lpfr->Flags = lpfrw->Flags;
1078
1079 p = utf16_to_enc((short_u*)lpfrw->lpstrFindWhat, NULL);
1080 vim_strncpy((char_u *)lpfr->lpstrFindWhat, p, lpfr->wFindWhatLen - 1);
1081 vim_free(p);
1082
1083 p = utf16_to_enc((short_u*)lpfrw->lpstrReplaceWith, NULL);
1084 vim_strncpy((char_u *)lpfr->lpstrReplaceWith, p, lpfr->wReplaceWithLen - 1);
1085 vim_free(p);
1086}
1087# endif
1088
1089/*
1090 * Handle a Find/Replace window message.
1091 */
1092 static void
1093_OnFindRepl(void)
1094{
1095 int flags = 0;
1096 int down;
1097
Bram Moolenaarcea912a2016-10-12 14:20:24 +02001098# ifdef FEAT_MBYTE
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001099 /* If the OS is Windows NT, and 'encoding' differs from active codepage:
1100 * convert text from wide string. */
Bram Moolenaarcea912a2016-10-12 14:20:24 +02001101 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001102 {
1103 findrep_wtoa(&s_findrep_struct, &s_findrep_struct_w);
1104 }
1105# endif
1106
1107 if (s_findrep_struct.Flags & FR_DIALOGTERM)
1108 /* Give main window the focus back. */
1109 (void)SetFocus(s_hwnd);
1110
1111 if (s_findrep_struct.Flags & FR_FINDNEXT)
1112 {
1113 flags = FRD_FINDNEXT;
1114
1115 /* Give main window the focus back: this is so the cursor isn't
1116 * hollow. */
1117 (void)SetFocus(s_hwnd);
1118 }
1119 else if (s_findrep_struct.Flags & FR_REPLACE)
1120 {
1121 flags = FRD_REPLACE;
1122
1123 /* Give main window the focus back: this is so the cursor isn't
1124 * hollow. */
1125 (void)SetFocus(s_hwnd);
1126 }
1127 else if (s_findrep_struct.Flags & FR_REPLACEALL)
1128 {
1129 flags = FRD_REPLACEALL;
1130 }
1131
1132 if (flags != 0)
1133 {
1134 /* Call the generic GUI function to do the actual work. */
1135 if (s_findrep_struct.Flags & FR_WHOLEWORD)
1136 flags |= FRD_WHOLE_WORD;
1137 if (s_findrep_struct.Flags & FR_MATCHCASE)
1138 flags |= FRD_MATCH_CASE;
1139 down = (s_findrep_struct.Flags & FR_DOWN) != 0;
1140 gui_do_findrepl(flags, (char_u *)s_findrep_struct.lpstrFindWhat,
1141 (char_u *)s_findrep_struct.lpstrReplaceWith, down);
1142 }
1143}
1144#endif
1145
1146 static void
1147HandleMouseHide(UINT uMsg, LPARAM lParam)
1148{
1149 static LPARAM last_lParam = 0L;
1150
1151 /* We sometimes get a mousemove when the mouse didn't move... */
1152 if (uMsg == WM_MOUSEMOVE || uMsg == WM_NCMOUSEMOVE)
1153 {
1154 if (lParam == last_lParam)
1155 return;
1156 last_lParam = lParam;
1157 }
1158
1159 /* Handle specially, to centralise coding. We need to be sure we catch all
1160 * possible events which should cause us to restore the cursor (as it is a
1161 * shared resource, we take full responsibility for it).
1162 */
1163 switch (uMsg)
1164 {
1165 case WM_KEYUP:
1166 case WM_CHAR:
1167 /*
1168 * blank out the pointer if necessary
1169 */
1170 if (p_mh)
1171 gui_mch_mousehide(TRUE);
1172 break;
1173
1174 case WM_SYSKEYUP: /* show the pointer when a system-key is pressed */
1175 case WM_SYSCHAR:
1176 case WM_MOUSEMOVE: /* show the pointer on any mouse action */
1177 case WM_LBUTTONDOWN:
1178 case WM_LBUTTONUP:
1179 case WM_MBUTTONDOWN:
1180 case WM_MBUTTONUP:
1181 case WM_RBUTTONDOWN:
1182 case WM_RBUTTONUP:
1183 case WM_XBUTTONDOWN:
1184 case WM_XBUTTONUP:
1185 case WM_NCMOUSEMOVE:
1186 case WM_NCLBUTTONDOWN:
1187 case WM_NCLBUTTONUP:
1188 case WM_NCMBUTTONDOWN:
1189 case WM_NCMBUTTONUP:
1190 case WM_NCRBUTTONDOWN:
1191 case WM_NCRBUTTONUP:
1192 case WM_KILLFOCUS:
1193 /*
1194 * if the pointer is currently hidden, then we should show it.
1195 */
1196 gui_mch_mousehide(FALSE);
1197 break;
1198 }
1199}
1200
1201 static LRESULT CALLBACK
1202_TextAreaWndProc(
1203 HWND hwnd,
1204 UINT uMsg,
1205 WPARAM wParam,
1206 LPARAM lParam)
1207{
1208 /*
1209 TRACE("TextAreaWndProc: hwnd = %08x, msg = %x, wParam = %x, lParam = %x\n",
1210 hwnd, uMsg, wParam, lParam);
1211 */
1212
1213 HandleMouseHide(uMsg, lParam);
1214
1215 s_uMsg = uMsg;
1216 s_wParam = wParam;
1217 s_lParam = lParam;
1218
1219#ifdef FEAT_BEVAL
1220 TrackUserActivity(uMsg);
1221#endif
1222
1223 switch (uMsg)
1224 {
1225 HANDLE_MSG(hwnd, WM_LBUTTONDBLCLK,_OnMouseButtonDown);
1226 HANDLE_MSG(hwnd, WM_LBUTTONDOWN,_OnMouseButtonDown);
1227 HANDLE_MSG(hwnd, WM_LBUTTONUP, _OnMouseMoveOrRelease);
1228 HANDLE_MSG(hwnd, WM_MBUTTONDBLCLK,_OnMouseButtonDown);
1229 HANDLE_MSG(hwnd, WM_MBUTTONDOWN,_OnMouseButtonDown);
1230 HANDLE_MSG(hwnd, WM_MBUTTONUP, _OnMouseMoveOrRelease);
1231 HANDLE_MSG(hwnd, WM_MOUSEMOVE, _OnMouseMoveOrRelease);
1232 HANDLE_MSG(hwnd, WM_PAINT, _OnPaint);
1233 HANDLE_MSG(hwnd, WM_RBUTTONDBLCLK,_OnMouseButtonDown);
1234 HANDLE_MSG(hwnd, WM_RBUTTONDOWN,_OnMouseButtonDown);
1235 HANDLE_MSG(hwnd, WM_RBUTTONUP, _OnMouseMoveOrRelease);
1236 HANDLE_MSG(hwnd, WM_XBUTTONDBLCLK,_OnMouseButtonDown);
1237 HANDLE_MSG(hwnd, WM_XBUTTONDOWN,_OnMouseButtonDown);
1238 HANDLE_MSG(hwnd, WM_XBUTTONUP, _OnMouseMoveOrRelease);
1239
1240#ifdef FEAT_BEVAL
1241 case WM_NOTIFY: Handle_WM_Notify(hwnd, (LPNMHDR)lParam);
1242 return TRUE;
1243#endif
1244 default:
1245 return MyWindowProc(hwnd, uMsg, wParam, lParam);
1246 }
1247}
1248
Bram Moolenaarcea912a2016-10-12 14:20:24 +02001249#if defined(FEAT_MBYTE) \
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001250 || defined(GLOBAL_IME) \
1251 || defined(PROTO)
1252# ifdef PROTO
1253typedef int WINAPI;
1254# endif
1255
1256 LRESULT WINAPI
1257vim_WindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
1258{
1259# ifdef GLOBAL_IME
1260 return global_ime_DefWindowProc(hwnd, message, wParam, lParam);
1261# else
1262 if (wide_WindowProc)
1263 return DefWindowProcW(hwnd, message, wParam, lParam);
1264 return DefWindowProc(hwnd, message, wParam, lParam);
1265#endif
1266}
1267#endif
1268
1269/*
1270 * Called when the foreground or background color has been changed.
1271 */
1272 void
1273gui_mch_new_colors(void)
1274{
1275 /* nothing to do? */
1276}
1277
1278/*
1279 * Set the colors to their default values.
1280 */
1281 void
1282gui_mch_def_colors(void)
1283{
1284 gui.norm_pixel = GetSysColor(COLOR_WINDOWTEXT);
1285 gui.back_pixel = GetSysColor(COLOR_WINDOW);
1286 gui.def_norm_pixel = gui.norm_pixel;
1287 gui.def_back_pixel = gui.back_pixel;
1288}
1289
1290/*
1291 * Open the GUI window which was created by a call to gui_mch_init().
1292 */
1293 int
1294gui_mch_open(void)
1295{
1296#ifndef SW_SHOWDEFAULT
1297# define SW_SHOWDEFAULT 10 /* Borland 5.0 doesn't have it */
1298#endif
1299 /* Actually open the window, if not already visible
1300 * (may be done already in gui_mch_set_shellsize) */
1301 if (!IsWindowVisible(s_hwnd))
1302 ShowWindow(s_hwnd, SW_SHOWDEFAULT);
1303
1304#ifdef MSWIN_FIND_REPLACE
1305 /* Init replace string here, so that we keep it when re-opening the
1306 * dialog. */
1307 s_findrep_struct.lpstrReplaceWith[0] = NUL;
1308#endif
1309
1310 return OK;
1311}
1312
1313/*
1314 * Get the position of the top left corner of the window.
1315 */
1316 int
1317gui_mch_get_winpos(int *x, int *y)
1318{
1319 RECT rect;
1320
1321 GetWindowRect(s_hwnd, &rect);
1322 *x = rect.left;
1323 *y = rect.top;
1324 return OK;
1325}
1326
1327/*
1328 * Set the position of the top left corner of the window to the given
1329 * coordinates.
1330 */
1331 void
1332gui_mch_set_winpos(int x, int y)
1333{
1334 SetWindowPos(s_hwnd, NULL, x, y, 0, 0,
1335 SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
1336}
1337 void
1338gui_mch_set_text_area_pos(int x, int y, int w, int h)
1339{
1340 static int oldx = 0;
1341 static int oldy = 0;
1342
1343 SetWindowPos(s_textArea, NULL, x, y, w, h, SWP_NOZORDER | SWP_NOACTIVATE);
1344
1345#ifdef FEAT_TOOLBAR
1346 if (vim_strchr(p_go, GO_TOOLBAR) != NULL)
1347 SendMessage(s_toolbarhwnd, WM_SIZE,
1348 (WPARAM)0, (LPARAM)(w + ((long)(TOOLBAR_BUTTON_HEIGHT+8)<<16)));
1349#endif
1350#if defined(FEAT_GUI_TABLINE)
1351 if (showing_tabline)
1352 {
1353 int top = 0;
1354 RECT rect;
1355
1356# ifdef FEAT_TOOLBAR
1357 if (vim_strchr(p_go, GO_TOOLBAR) != NULL)
1358 top = TOOLBAR_BUTTON_HEIGHT + TOOLBAR_BORDER_HEIGHT;
1359# endif
1360 GetClientRect(s_hwnd, &rect);
1361 MoveWindow(s_tabhwnd, 0, top, rect.right, gui.tabline_height, TRUE);
1362 }
1363#endif
1364
1365 /* When side scroll bar is unshown, the size of window will change.
1366 * then, the text area move left or right. thus client rect should be
1367 * forcedly redrawn. (Yasuhiro Matsumoto) */
1368 if (oldx != x || oldy != y)
1369 {
1370 InvalidateRect(s_hwnd, NULL, FALSE);
1371 oldx = x;
1372 oldy = y;
1373 }
1374}
1375
1376
1377/*
1378 * Scrollbar stuff:
1379 */
1380
1381 void
1382gui_mch_enable_scrollbar(
1383 scrollbar_T *sb,
1384 int flag)
1385{
1386 ShowScrollBar(sb->id, SB_CTL, flag);
1387
1388 /* TODO: When the window is maximized, the size of the window stays the
1389 * same, thus the size of the text area changes. On Win98 it's OK, on Win
1390 * NT 4.0 it's not... */
1391}
1392
1393 void
1394gui_mch_set_scrollbar_pos(
1395 scrollbar_T *sb,
1396 int x,
1397 int y,
1398 int w,
1399 int h)
1400{
1401 SetWindowPos(sb->id, NULL, x, y, w, h,
1402 SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW);
1403}
1404
1405 void
1406gui_mch_create_scrollbar(
1407 scrollbar_T *sb,
1408 int orient) /* SBAR_VERT or SBAR_HORIZ */
1409{
1410 sb->id = CreateWindow(
1411 "SCROLLBAR", "Scrollbar",
1412 WS_CHILD | ((orient == SBAR_VERT) ? SBS_VERT : SBS_HORZ), 0, 0,
1413 10, /* Any value will do for now */
1414 10, /* Any value will do for now */
1415 s_hwnd, NULL,
1416 s_hinst, NULL);
1417}
1418
1419/*
1420 * Find the scrollbar with the given hwnd.
1421 */
1422 static scrollbar_T *
1423gui_mswin_find_scrollbar(HWND hwnd)
1424{
1425 win_T *wp;
1426
1427 if (gui.bottom_sbar.id == hwnd)
1428 return &gui.bottom_sbar;
1429 FOR_ALL_WINDOWS(wp)
1430 {
1431 if (wp->w_scrollbars[SBAR_LEFT].id == hwnd)
1432 return &wp->w_scrollbars[SBAR_LEFT];
1433 if (wp->w_scrollbars[SBAR_RIGHT].id == hwnd)
1434 return &wp->w_scrollbars[SBAR_RIGHT];
1435 }
1436 return NULL;
1437}
1438
1439/*
1440 * Get the character size of a font.
1441 */
1442 static void
1443GetFontSize(GuiFont font)
1444{
1445 HWND hwnd = GetDesktopWindow();
1446 HDC hdc = GetWindowDC(hwnd);
1447 HFONT hfntOld = SelectFont(hdc, (HFONT)font);
1448 TEXTMETRIC tm;
1449
1450 GetTextMetrics(hdc, &tm);
1451 gui.char_width = tm.tmAveCharWidth + tm.tmOverhang;
1452
1453 gui.char_height = tm.tmHeight + p_linespace;
1454
1455 SelectFont(hdc, hfntOld);
1456
1457 ReleaseDC(hwnd, hdc);
1458}
1459
1460/*
1461 * Adjust gui.char_height (after 'linespace' was changed).
1462 */
1463 int
1464gui_mch_adjust_charheight(void)
1465{
1466 GetFontSize(gui.norm_font);
1467 return OK;
1468}
1469
1470 static GuiFont
1471get_font_handle(LOGFONT *lf)
1472{
1473 HFONT font = NULL;
1474
1475 /* Load the font */
1476 font = CreateFontIndirect(lf);
1477
1478 if (font == NULL)
1479 return NOFONT;
1480
1481 return (GuiFont)font;
1482}
1483
1484 static int
1485pixels_to_points(int pixels, int vertical)
1486{
1487 int points;
1488 HWND hwnd;
1489 HDC hdc;
1490
1491 hwnd = GetDesktopWindow();
1492 hdc = GetWindowDC(hwnd);
1493
1494 points = MulDiv(pixels, 72,
1495 GetDeviceCaps(hdc, vertical ? LOGPIXELSY : LOGPIXELSX));
1496
1497 ReleaseDC(hwnd, hdc);
1498
1499 return points;
1500}
1501
1502 GuiFont
1503gui_mch_get_font(
1504 char_u *name,
1505 int giveErrorIfMissing)
1506{
1507 LOGFONT lf;
1508 GuiFont font = NOFONT;
1509
1510 if (get_logfont(&lf, name, NULL, giveErrorIfMissing) == OK)
1511 font = get_font_handle(&lf);
1512 if (font == NOFONT && giveErrorIfMissing)
1513 EMSG2(_(e_font), name);
1514 return font;
1515}
1516
1517#if defined(FEAT_EVAL) || defined(PROTO)
1518/*
1519 * Return the name of font "font" in allocated memory.
1520 * Don't know how to get the actual name, thus use the provided name.
1521 */
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001522 char_u *
Bram Moolenaar1266d672017-02-01 13:43:36 +01001523gui_mch_get_fontname(GuiFont font UNUSED, char_u *name)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001524{
1525 if (name == NULL)
1526 return NULL;
1527 return vim_strsave(name);
1528}
1529#endif
1530
1531 void
1532gui_mch_free_font(GuiFont font)
1533{
1534 if (font)
1535 DeleteObject((HFONT)font);
1536}
1537
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001538/*
1539 * Return the Pixel value (color) for the given color name.
1540 * Return INVALCOLOR for error.
1541 */
1542 guicolor_T
1543gui_mch_get_color(char_u *name)
1544{
Bram Moolenaarc285fe72016-04-26 21:51:48 +02001545 int i;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001546
1547 typedef struct SysColorTable
1548 {
1549 char *name;
1550 int color;
1551 } SysColorTable;
1552
1553 static SysColorTable sys_table[] =
1554 {
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001555 {"SYS_3DDKSHADOW", COLOR_3DDKSHADOW},
1556 {"SYS_3DHILIGHT", COLOR_3DHILIGHT},
Bram Moolenaarcea912a2016-10-12 14:20:24 +02001557#ifdef COLOR_3DHIGHLIGHT
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001558 {"SYS_3DHIGHLIGHT", COLOR_3DHIGHLIGHT},
1559#endif
1560 {"SYS_BTNHILIGHT", COLOR_BTNHILIGHT},
1561 {"SYS_BTNHIGHLIGHT", COLOR_BTNHIGHLIGHT},
1562 {"SYS_3DLIGHT", COLOR_3DLIGHT},
1563 {"SYS_3DSHADOW", COLOR_3DSHADOW},
1564 {"SYS_DESKTOP", COLOR_DESKTOP},
1565 {"SYS_INFOBK", COLOR_INFOBK},
1566 {"SYS_INFOTEXT", COLOR_INFOTEXT},
1567 {"SYS_3DFACE", COLOR_3DFACE},
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001568 {"SYS_BTNFACE", COLOR_BTNFACE},
1569 {"SYS_BTNSHADOW", COLOR_BTNSHADOW},
1570 {"SYS_ACTIVEBORDER", COLOR_ACTIVEBORDER},
1571 {"SYS_ACTIVECAPTION", COLOR_ACTIVECAPTION},
1572 {"SYS_APPWORKSPACE", COLOR_APPWORKSPACE},
1573 {"SYS_BACKGROUND", COLOR_BACKGROUND},
1574 {"SYS_BTNTEXT", COLOR_BTNTEXT},
1575 {"SYS_CAPTIONTEXT", COLOR_CAPTIONTEXT},
1576 {"SYS_GRAYTEXT", COLOR_GRAYTEXT},
1577 {"SYS_HIGHLIGHT", COLOR_HIGHLIGHT},
1578 {"SYS_HIGHLIGHTTEXT", COLOR_HIGHLIGHTTEXT},
1579 {"SYS_INACTIVEBORDER", COLOR_INACTIVEBORDER},
1580 {"SYS_INACTIVECAPTION", COLOR_INACTIVECAPTION},
1581 {"SYS_INACTIVECAPTIONTEXT", COLOR_INACTIVECAPTIONTEXT},
1582 {"SYS_MENU", COLOR_MENU},
1583 {"SYS_MENUTEXT", COLOR_MENUTEXT},
1584 {"SYS_SCROLLBAR", COLOR_SCROLLBAR},
1585 {"SYS_WINDOW", COLOR_WINDOW},
1586 {"SYS_WINDOWFRAME", COLOR_WINDOWFRAME},
1587 {"SYS_WINDOWTEXT", COLOR_WINDOWTEXT}
1588 };
1589
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001590 /*
1591 * Try to look up a system colour.
1592 */
1593 for (i = 0; i < sizeof(sys_table) / sizeof(sys_table[0]); i++)
1594 if (STRICMP(name, sys_table[i].name) == 0)
1595 return GetSysColor(sys_table[i].color);
1596
Bram Moolenaarab302212016-04-26 20:59:29 +02001597 return gui_get_color_cmn(name);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001598}
Bram Moolenaarc285fe72016-04-26 21:51:48 +02001599
Bram Moolenaar26af85d2017-07-23 16:45:10 +02001600 guicolor_T
1601gui_mch_get_rgb_color(int r, int g, int b)
1602{
1603 return gui_get_rgb_color_cmn(r, g, b);
1604}
1605
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001606/*
1607 * Return OK if the key with the termcap name "name" is supported.
1608 */
1609 int
1610gui_mch_haskey(char_u *name)
1611{
1612 int i;
1613
1614 for (i = 0; special_keys[i].vim_code1 != NUL; i++)
1615 if (name[0] == special_keys[i].vim_code0 &&
1616 name[1] == special_keys[i].vim_code1)
1617 return OK;
1618 return FAIL;
1619}
1620
1621 void
1622gui_mch_beep(void)
1623{
1624 MessageBeep(MB_OK);
1625}
1626/*
1627 * Invert a rectangle from row r, column c, for nr rows and nc columns.
1628 */
1629 void
1630gui_mch_invert_rectangle(
1631 int r,
1632 int c,
1633 int nr,
1634 int nc)
1635{
1636 RECT rc;
1637
1638 /*
1639 * Note: InvertRect() excludes right and bottom of rectangle.
1640 */
1641 rc.left = FILL_X(c);
1642 rc.top = FILL_Y(r);
1643 rc.right = rc.left + nc * gui.char_width;
1644 rc.bottom = rc.top + nr * gui.char_height;
1645 InvertRect(s_hdc, &rc);
1646}
1647
1648/*
1649 * Iconify the GUI window.
1650 */
1651 void
1652gui_mch_iconify(void)
1653{
1654 ShowWindow(s_hwnd, SW_MINIMIZE);
1655}
1656
1657/*
1658 * Draw a cursor without focus.
1659 */
1660 void
1661gui_mch_draw_hollow_cursor(guicolor_T color)
1662{
1663 HBRUSH hbr;
1664 RECT rc;
1665
1666 /*
1667 * Note: FrameRect() excludes right and bottom of rectangle.
1668 */
1669 rc.left = FILL_X(gui.col);
1670 rc.top = FILL_Y(gui.row);
1671 rc.right = rc.left + gui.char_width;
1672#ifdef FEAT_MBYTE
1673 if (mb_lefthalve(gui.row, gui.col))
1674 rc.right += gui.char_width;
1675#endif
1676 rc.bottom = rc.top + gui.char_height;
1677 hbr = CreateSolidBrush(color);
1678 FrameRect(s_hdc, &rc, hbr);
1679 DeleteBrush(hbr);
1680}
1681/*
1682 * Draw part of a cursor, "w" pixels wide, and "h" pixels high, using
1683 * color "color".
1684 */
1685 void
1686gui_mch_draw_part_cursor(
1687 int w,
1688 int h,
1689 guicolor_T color)
1690{
1691 HBRUSH hbr;
1692 RECT rc;
1693
1694 /*
1695 * Note: FillRect() excludes right and bottom of rectangle.
1696 */
1697 rc.left =
1698#ifdef FEAT_RIGHTLEFT
1699 /* vertical line should be on the right of current point */
1700 CURSOR_BAR_RIGHT ? FILL_X(gui.col + 1) - w :
1701#endif
1702 FILL_X(gui.col);
1703 rc.top = FILL_Y(gui.row) + gui.char_height - h;
1704 rc.right = rc.left + w;
1705 rc.bottom = rc.top + h;
1706 hbr = CreateSolidBrush(color);
1707 FillRect(s_hdc, &rc, hbr);
1708 DeleteBrush(hbr);
1709}
1710
1711
1712/*
1713 * Generates a VK_SPACE when the internal dead_key flag is set to output the
1714 * dead key's nominal character and re-post the original message.
1715 */
1716 static void
1717outputDeadKey_rePost(MSG originalMsg)
1718{
1719 static MSG deadCharExpel;
1720
1721 if (!dead_key)
1722 return;
1723
1724 dead_key = 0;
1725
1726 /* Make Windows generate the dead key's character */
1727 deadCharExpel.message = originalMsg.message;
1728 deadCharExpel.hwnd = originalMsg.hwnd;
1729 deadCharExpel.wParam = VK_SPACE;
1730
1731 MyTranslateMessage(&deadCharExpel);
1732
1733 /* re-generate the current character free of the dead char influence */
1734 PostMessage(originalMsg.hwnd, originalMsg.message, originalMsg.wParam,
1735 originalMsg.lParam);
1736}
1737
1738
1739/*
1740 * Process a single Windows message.
1741 * If one is not available we hang until one is.
1742 */
1743 static void
1744process_message(void)
1745{
1746 MSG msg;
1747 UINT vk = 0; /* Virtual key */
1748 char_u string[40];
1749 int i;
1750 int modifiers = 0;
1751 int key;
1752#ifdef FEAT_MENU
1753 static char_u k10[] = {K_SPECIAL, 'k', ';', 0};
1754#endif
1755
1756 pGetMessage(&msg, NULL, 0, 0);
1757
1758#ifdef FEAT_OLE
1759 /* Look after OLE Automation commands */
1760 if (msg.message == WM_OLE)
1761 {
1762 char_u *str = (char_u *)msg.lParam;
1763 if (str == NULL || *str == NUL)
1764 {
1765 /* Message can't be ours, forward it. Fixes problem with Ultramon
1766 * 3.0.4 */
1767 pDispatchMessage(&msg);
1768 }
1769 else
1770 {
1771 add_to_input_buf(str, (int)STRLEN(str));
1772 vim_free(str); /* was allocated in CVim::SendKeys() */
1773 }
1774 return;
1775 }
1776#endif
1777
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001778#ifdef MSWIN_FIND_REPLACE
1779 /* Don't process messages used by the dialog */
1780 if (s_findrep_hwnd != NULL && pIsDialogMessage(s_findrep_hwnd, &msg))
1781 {
1782 HandleMouseHide(msg.message, msg.lParam);
1783 return;
1784 }
1785#endif
1786
1787 /*
1788 * Check if it's a special key that we recognise. If not, call
1789 * TranslateMessage().
1790 */
1791 if (msg.message == WM_KEYDOWN || msg.message == WM_SYSKEYDOWN)
1792 {
1793 vk = (int) msg.wParam;
1794
1795 /*
1796 * Handle dead keys in special conditions in other cases we let Windows
1797 * handle them and do not interfere.
1798 *
1799 * The dead_key flag must be reset on several occasions:
1800 * - in _OnChar() (or _OnSysChar()) as any dead key was necessarily
1801 * consumed at that point (This is when we let Windows combine the
1802 * dead character on its own)
1803 *
1804 * - Before doing something special such as regenerating keypresses to
1805 * expel the dead character as this could trigger an infinite loop if
1806 * for some reason MyTranslateMessage() do not trigger a call
1807 * immediately to _OnChar() (or _OnSysChar()).
1808 */
1809 if (dead_key)
1810 {
1811 /*
1812 * If a dead key was pressed and the user presses VK_SPACE,
1813 * VK_BACK, or VK_ESCAPE it means that he actually wants to deal
1814 * with the dead char now, so do nothing special and let Windows
1815 * handle it.
1816 *
1817 * Note that VK_SPACE combines with the dead_key's character and
1818 * only one WM_CHAR will be generated by TranslateMessage(), in
1819 * the two other cases two WM_CHAR will be generated: the dead
1820 * char and VK_BACK or VK_ESCAPE. That is most likely what the
1821 * user expects.
1822 */
1823 if ((vk == VK_SPACE || vk == VK_BACK || vk == VK_ESCAPE))
1824 {
1825 dead_key = 0;
1826 MyTranslateMessage(&msg);
1827 return;
1828 }
1829 /* In modes where we are not typing, dead keys should behave
1830 * normally */
1831 else if (!(get_real_state() & (INSERT | CMDLINE | SELECTMODE)))
1832 {
1833 outputDeadKey_rePost(msg);
1834 return;
1835 }
1836 }
1837
1838 /* Check for CTRL-BREAK */
1839 if (vk == VK_CANCEL)
1840 {
1841 trash_input_buf();
1842 got_int = TRUE;
1843 string[0] = Ctrl_C;
1844 add_to_input_buf(string, 1);
1845 }
1846
1847 for (i = 0; special_keys[i].key_sym != 0; i++)
1848 {
1849 /* ignore VK_SPACE when ALT key pressed: system menu */
1850 if (special_keys[i].key_sym == vk
1851 && (vk != VK_SPACE || !(GetKeyState(VK_MENU) & 0x8000)))
1852 {
1853 /*
Bram Moolenaar945ec092016-06-08 21:17:43 +02001854 * Behave as expected if we have a dead key and the special key
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001855 * is a key that would normally trigger the dead key nominal
1856 * character output (such as a NUMPAD printable character or
1857 * the TAB key, etc...).
1858 */
1859 if (dead_key && (special_keys[i].vim_code0 == 'K'
1860 || vk == VK_TAB || vk == CAR))
1861 {
1862 outputDeadKey_rePost(msg);
1863 return;
1864 }
1865
1866#ifdef FEAT_MENU
1867 /* Check for <F10>: Windows selects the menu. When <F10> is
1868 * mapped we want to use the mapping instead. */
1869 if (vk == VK_F10
1870 && gui.menu_is_active
1871 && check_map(k10, State, FALSE, TRUE, FALSE,
1872 NULL, NULL) == NULL)
1873 break;
1874#endif
1875 if (GetKeyState(VK_SHIFT) & 0x8000)
1876 modifiers |= MOD_MASK_SHIFT;
1877 /*
1878 * Don't use caps-lock as shift, because these are special keys
1879 * being considered here, and we only want letters to get
1880 * shifted -- webb
1881 */
1882 /*
1883 if (GetKeyState(VK_CAPITAL) & 0x0001)
1884 modifiers ^= MOD_MASK_SHIFT;
1885 */
1886 if (GetKeyState(VK_CONTROL) & 0x8000)
1887 modifiers |= MOD_MASK_CTRL;
1888 if (GetKeyState(VK_MENU) & 0x8000)
1889 modifiers |= MOD_MASK_ALT;
1890
1891 if (special_keys[i].vim_code1 == NUL)
1892 key = special_keys[i].vim_code0;
1893 else
1894 key = TO_SPECIAL(special_keys[i].vim_code0,
1895 special_keys[i].vim_code1);
1896 key = simplify_key(key, &modifiers);
1897 if (key == CSI)
1898 key = K_CSI;
1899
1900 if (modifiers)
1901 {
1902 string[0] = CSI;
1903 string[1] = KS_MODIFIER;
1904 string[2] = modifiers;
1905 add_to_input_buf(string, 3);
1906 }
1907
1908 if (IS_SPECIAL(key))
1909 {
1910 string[0] = CSI;
1911 string[1] = K_SECOND(key);
1912 string[2] = K_THIRD(key);
1913 add_to_input_buf(string, 3);
1914 }
1915 else
1916 {
1917 int len;
1918
1919 /* Handle "key" as a Unicode character. */
1920 len = char_to_string(key, string, 40, FALSE);
1921 add_to_input_buf(string, len);
1922 }
1923 break;
1924 }
1925 }
1926 if (special_keys[i].key_sym == 0)
1927 {
1928 /* Some keys need C-S- where they should only need C-.
1929 * Ignore 0xff, Windows XP sends it when NUMLOCK has changed since
1930 * system startup (Helmut Stiegler, 2003 Oct 3). */
1931 if (vk != 0xff
1932 && (GetKeyState(VK_CONTROL) & 0x8000)
1933 && !(GetKeyState(VK_SHIFT) & 0x8000)
1934 && !(GetKeyState(VK_MENU) & 0x8000))
1935 {
1936 /* CTRL-6 is '^'; Japanese keyboard maps '^' to vk == 0xDE */
1937 if (vk == '6' || MapVirtualKey(vk, 2) == (UINT)'^')
1938 {
1939 string[0] = Ctrl_HAT;
1940 add_to_input_buf(string, 1);
1941 }
1942 /* vk == 0xBD AZERTY for CTRL-'-', but CTRL-[ for * QWERTY! */
1943 else if (vk == 0xBD) /* QWERTY for CTRL-'-' */
1944 {
1945 string[0] = Ctrl__;
1946 add_to_input_buf(string, 1);
1947 }
1948 /* CTRL-2 is '@'; Japanese keyboard maps '@' to vk == 0xC0 */
1949 else if (vk == '2' || MapVirtualKey(vk, 2) == (UINT)'@')
1950 {
1951 string[0] = Ctrl_AT;
1952 add_to_input_buf(string, 1);
1953 }
1954 else
1955 MyTranslateMessage(&msg);
1956 }
1957 else
1958 MyTranslateMessage(&msg);
1959 }
1960 }
1961#ifdef FEAT_MBYTE_IME
1962 else if (msg.message == WM_IME_NOTIFY)
1963 _OnImeNotify(msg.hwnd, (DWORD)msg.wParam, (DWORD)msg.lParam);
1964 else if (msg.message == WM_KEYUP && im_get_status())
1965 /* added for non-MS IME (Yasuhiro Matsumoto) */
1966 MyTranslateMessage(&msg);
1967#endif
1968#if !defined(FEAT_MBYTE_IME) && defined(GLOBAL_IME)
1969/* GIME_TEST */
1970 else if (msg.message == WM_IME_STARTCOMPOSITION)
1971 {
1972 POINT point;
1973
1974 global_ime_set_font(&norm_logfont);
1975 point.x = FILL_X(gui.col);
1976 point.y = FILL_Y(gui.row);
1977 MapWindowPoints(s_textArea, s_hwnd, &point, 1);
1978 global_ime_set_position(&point);
1979 }
1980#endif
1981
1982#ifdef FEAT_MENU
1983 /* Check for <F10>: Default effect is to select the menu. When <F10> is
1984 * mapped we need to stop it here to avoid strange effects (e.g., for the
1985 * key-up event) */
1986 if (vk != VK_F10 || check_map(k10, State, FALSE, TRUE, FALSE,
1987 NULL, NULL) == NULL)
1988#endif
1989 pDispatchMessage(&msg);
1990}
1991
1992/*
1993 * Catch up with any queued events. This may put keyboard input into the
1994 * input buffer, call resize call-backs, trigger timers etc. If there is
1995 * nothing in the event queue (& no timers pending), then we return
1996 * immediately.
1997 */
1998 void
1999gui_mch_update(void)
2000{
2001 MSG msg;
2002
2003 if (!s_busy_processing)
2004 while (pPeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)
2005 && !vim_is_input_buf_full())
2006 process_message();
2007}
2008
Bram Moolenaar4231da42016-06-02 14:30:04 +02002009 static void
2010remove_any_timer(void)
2011{
2012 MSG msg;
2013
2014 if (s_wait_timer != 0 && !s_timed_out)
2015 {
2016 KillTimer(NULL, s_wait_timer);
2017
2018 /* Eat spurious WM_TIMER messages */
2019 while (pPeekMessage(&msg, s_hwnd, WM_TIMER, WM_TIMER, PM_REMOVE))
2020 ;
2021 s_wait_timer = 0;
2022 }
2023}
2024
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002025/*
2026 * GUI input routine called by gui_wait_for_chars(). Waits for a character
2027 * from the keyboard.
2028 * wtime == -1 Wait forever.
2029 * wtime == 0 This should never happen.
2030 * wtime > 0 Wait wtime milliseconds for a character.
2031 * Returns OK if a character was found to be available within the given time,
2032 * or FAIL otherwise.
2033 */
2034 int
2035gui_mch_wait_for_chars(int wtime)
2036{
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002037 int focus;
2038
2039 s_timed_out = FALSE;
2040
2041 if (wtime > 0)
2042 {
2043 /* Don't do anything while processing a (scroll) message. */
2044 if (s_busy_processing)
2045 return FAIL;
2046 s_wait_timer = (UINT)SetTimer(NULL, 0, (UINT)wtime,
2047 (TIMERPROC)_OnTimer);
2048 }
2049
2050 allow_scrollbar = TRUE;
2051
2052 focus = gui.in_focus;
2053 while (!s_timed_out)
2054 {
2055 /* Stop or start blinking when focus changes */
2056 if (gui.in_focus != focus)
2057 {
2058 if (gui.in_focus)
2059 gui_mch_start_blink();
2060 else
2061 gui_mch_stop_blink();
2062 focus = gui.in_focus;
2063 }
2064
2065 if (s_need_activate)
2066 {
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002067 (void)SetForegroundWindow(s_hwnd);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002068 s_need_activate = FALSE;
2069 }
2070
Bram Moolenaar4231da42016-06-02 14:30:04 +02002071#ifdef FEAT_TIMERS
2072 did_add_timer = FALSE;
2073#endif
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002074#ifdef MESSAGE_QUEUE
Bram Moolenaar9186a272016-02-23 19:34:01 +01002075 /* Check channel while waiting message. */
2076 for (;;)
2077 {
2078 MSG msg;
2079
2080 parse_queued_messages();
2081
2082 if (pPeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)
Bram Moolenaarf28d8712016-04-02 15:59:40 +02002083 || MsgWaitForMultipleObjects(0, NULL, FALSE, 100, QS_ALLINPUT)
Bram Moolenaar9186a272016-02-23 19:34:01 +01002084 != WAIT_TIMEOUT)
2085 break;
2086 }
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002087#endif
2088
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002089 /*
2090 * Don't use gui_mch_update() because then we will spin-lock until a
2091 * char arrives, instead we use GetMessage() to hang until an
2092 * event arrives. No need to check for input_buf_full because we are
2093 * returning as soon as it contains a single char -- webb
2094 */
2095 process_message();
2096
2097 if (input_available())
2098 {
Bram Moolenaar4231da42016-06-02 14:30:04 +02002099 remove_any_timer();
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002100 allow_scrollbar = FALSE;
2101
2102 /* Clear pending mouse button, the release event may have been
2103 * taken by the dialog window. But don't do this when getting
2104 * focus, we need the mouse-up event then. */
2105 if (!s_getting_focus)
2106 s_button_pending = -1;
2107
2108 return OK;
2109 }
Bram Moolenaar4231da42016-06-02 14:30:04 +02002110
2111#ifdef FEAT_TIMERS
2112 if (did_add_timer)
2113 {
2114 /* Need to recompute the waiting time. */
2115 remove_any_timer();
2116 break;
2117 }
2118#endif
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002119 }
2120 allow_scrollbar = FALSE;
2121 return FAIL;
2122}
2123
2124/*
2125 * Clear a rectangular region of the screen from text pos (row1, col1) to
2126 * (row2, col2) inclusive.
2127 */
2128 void
2129gui_mch_clear_block(
2130 int row1,
2131 int col1,
2132 int row2,
2133 int col2)
2134{
2135 RECT rc;
2136
2137 /*
2138 * Clear one extra pixel at the far right, for when bold characters have
2139 * spilled over to the window border.
2140 * Note: FillRect() excludes right and bottom of rectangle.
2141 */
2142 rc.left = FILL_X(col1);
2143 rc.top = FILL_Y(row1);
2144 rc.right = FILL_X(col2 + 1) + (col2 == Columns - 1);
2145 rc.bottom = FILL_Y(row2 + 1);
2146 clear_rect(&rc);
2147}
2148
2149/*
2150 * Clear the whole text window.
2151 */
2152 void
2153gui_mch_clear_all(void)
2154{
2155 RECT rc;
2156
2157 rc.left = 0;
2158 rc.top = 0;
2159 rc.right = Columns * gui.char_width + 2 * gui.border_width;
2160 rc.bottom = Rows * gui.char_height + 2 * gui.border_width;
2161 clear_rect(&rc);
2162}
2163/*
2164 * Menu stuff.
2165 */
2166
2167 void
2168gui_mch_enable_menu(int flag)
2169{
2170#ifdef FEAT_MENU
2171 SetMenu(s_hwnd, flag ? s_menuBar : NULL);
2172#endif
2173}
2174
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002175 void
2176gui_mch_set_menu_pos(
Bram Moolenaar1266d672017-02-01 13:43:36 +01002177 int x UNUSED,
2178 int y UNUSED,
2179 int w UNUSED,
2180 int h UNUSED)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002181{
2182 /* It will be in the right place anyway */
2183}
2184
2185#if defined(FEAT_MENU) || defined(PROTO)
2186/*
2187 * Make menu item hidden or not hidden
2188 */
2189 void
2190gui_mch_menu_hidden(
2191 vimmenu_T *menu,
2192 int hidden)
2193{
2194 /*
2195 * This doesn't do what we want. Hmm, just grey the menu items for now.
2196 */
2197 /*
2198 if (hidden)
2199 EnableMenuItem(s_menuBar, menu->id, MF_BYCOMMAND | MF_DISABLED);
2200 else
2201 EnableMenuItem(s_menuBar, menu->id, MF_BYCOMMAND | MF_ENABLED);
2202 */
2203 gui_mch_menu_grey(menu, hidden);
2204}
2205
2206/*
2207 * This is called after setting all the menus to grey/hidden or not.
2208 */
2209 void
2210gui_mch_draw_menubar(void)
2211{
2212 DrawMenuBar(s_hwnd);
2213}
2214#endif /*FEAT_MENU*/
2215
2216#ifndef PROTO
2217void
2218#ifdef VIMDLL
2219_export
2220#endif
2221_cdecl
2222SaveInst(HINSTANCE hInst)
2223{
2224 s_hinst = hInst;
2225}
2226#endif
2227
2228/*
2229 * Return the RGB value of a pixel as a long.
2230 */
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02002231 guicolor_T
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002232gui_mch_get_rgb(guicolor_T pixel)
2233{
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02002234 return (guicolor_T)((GetRValue(pixel) << 16) + (GetGValue(pixel) << 8)
2235 + GetBValue(pixel));
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002236}
2237
2238#if defined(FEAT_GUI_DIALOG) || defined(PROTO)
2239/* Convert pixels in X to dialog units */
2240 static WORD
2241PixelToDialogX(int numPixels)
2242{
2243 return (WORD)((numPixels * 4) / s_dlgfntwidth);
2244}
2245
2246/* Convert pixels in Y to dialog units */
2247 static WORD
2248PixelToDialogY(int numPixels)
2249{
2250 return (WORD)((numPixels * 8) / s_dlgfntheight);
2251}
2252
2253/* Return the width in pixels of the given text in the given DC. */
2254 static int
2255GetTextWidth(HDC hdc, char_u *str, int len)
2256{
2257 SIZE size;
2258
2259 GetTextExtentPoint(hdc, (LPCSTR)str, len, &size);
2260 return size.cx;
2261}
2262
2263#ifdef FEAT_MBYTE
2264/*
2265 * Return the width in pixels of the given text in the given DC, taking care
2266 * of 'encoding' to active codepage conversion.
2267 */
2268 static int
2269GetTextWidthEnc(HDC hdc, char_u *str, int len)
2270{
2271 SIZE size;
2272 WCHAR *wstr;
2273 int n;
2274 int wlen = len;
2275
2276 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
2277 {
2278 /* 'encoding' differs from active codepage: convert text and use wide
2279 * function */
2280 wstr = enc_to_utf16(str, &wlen);
2281 if (wstr != NULL)
2282 {
2283 n = GetTextExtentPointW(hdc, wstr, wlen, &size);
2284 vim_free(wstr);
2285 if (n)
2286 return size.cx;
2287 }
2288 }
2289
2290 return GetTextWidth(hdc, str, len);
2291}
2292#else
2293# define GetTextWidthEnc(h, s, l) GetTextWidth((h), (s), (l))
2294#endif
2295
Bram Moolenaar87f3d202016-12-01 20:18:50 +01002296static void get_work_area(RECT *spi_rect);
2297
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002298/*
2299 * A quick little routine that will center one window over another, handy for
Bram Moolenaar87f3d202016-12-01 20:18:50 +01002300 * dialog boxes. Taken from the Win32SDK samples and modified for multiple
2301 * monitors.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002302 */
2303 static BOOL
2304CenterWindow(
2305 HWND hwndChild,
2306 HWND hwndParent)
2307{
Bram Moolenaar87f3d202016-12-01 20:18:50 +01002308 HMONITOR mon;
2309 MONITORINFO moninfo;
2310 RECT rChild, rParent, rScreen;
2311 int wChild, hChild, wParent, hParent;
2312 int xNew, yNew;
2313 HDC hdc;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002314
2315 GetWindowRect(hwndChild, &rChild);
2316 wChild = rChild.right - rChild.left;
2317 hChild = rChild.bottom - rChild.top;
2318
2319 /* If Vim is minimized put the window in the middle of the screen. */
2320 if (hwndParent == NULL || IsMinimized(hwndParent))
Bram Moolenaar87f3d202016-12-01 20:18:50 +01002321 get_work_area(&rParent);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002322 else
2323 GetWindowRect(hwndParent, &rParent);
2324 wParent = rParent.right - rParent.left;
2325 hParent = rParent.bottom - rParent.top;
2326
Bram Moolenaar87f3d202016-12-01 20:18:50 +01002327 moninfo.cbSize = sizeof(MONITORINFO);
2328 mon = MonitorFromWindow(hwndChild, MONITOR_DEFAULTTOPRIMARY);
2329 if (mon != NULL && GetMonitorInfo(mon, &moninfo))
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002330 {
Bram Moolenaar87f3d202016-12-01 20:18:50 +01002331 rScreen = moninfo.rcWork;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002332 }
Bram Moolenaar87f3d202016-12-01 20:18:50 +01002333 else
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002334 {
Bram Moolenaar87f3d202016-12-01 20:18:50 +01002335 hdc = GetDC(hwndChild);
2336 rScreen.left = 0;
2337 rScreen.top = 0;
2338 rScreen.right = GetDeviceCaps(hdc, HORZRES);
2339 rScreen.bottom = GetDeviceCaps(hdc, VERTRES);
2340 ReleaseDC(hwndChild, hdc);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002341 }
2342
Bram Moolenaar87f3d202016-12-01 20:18:50 +01002343 xNew = rParent.left + ((wParent - wChild) / 2);
2344 if (xNew < rScreen.left)
2345 xNew = rScreen.left;
2346 else if ((xNew + wChild) > rScreen.right)
2347 xNew = rScreen.right - wChild;
2348
2349 yNew = rParent.top + ((hParent - hChild) / 2);
2350 if (yNew < rScreen.top)
2351 yNew = rScreen.top;
2352 else if ((yNew + hChild) > rScreen.bottom)
2353 yNew = rScreen.bottom - hChild;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002354
2355 return SetWindowPos(hwndChild, NULL, xNew, yNew, 0, 0,
2356 SWP_NOSIZE | SWP_NOZORDER);
2357}
2358#endif /* FEAT_GUI_DIALOG */
2359
2360void
2361gui_mch_activate_window(void)
2362{
2363 (void)SetActiveWindow(s_hwnd);
2364}
2365
2366#if defined(FEAT_TOOLBAR) || defined(PROTO)
2367 void
2368gui_mch_show_toolbar(int showit)
2369{
2370 if (s_toolbarhwnd == NULL)
2371 return;
2372
2373 if (showit)
2374 {
2375# ifdef FEAT_MBYTE
2376# ifndef TB_SETUNICODEFORMAT
2377 /* For older compilers. We assume this never changes. */
2378# define TB_SETUNICODEFORMAT 0x2005
2379# endif
2380 /* Enable/disable unicode support */
2381 int uu = (enc_codepage >= 0 && (int)GetACP() != enc_codepage);
2382 SendMessage(s_toolbarhwnd, TB_SETUNICODEFORMAT, (WPARAM)uu, (LPARAM)0);
2383# endif
2384 ShowWindow(s_toolbarhwnd, SW_SHOW);
2385 }
2386 else
2387 ShowWindow(s_toolbarhwnd, SW_HIDE);
2388}
2389
2390/* Then number of bitmaps is fixed. Exit is missing! */
2391#define TOOLBAR_BITMAP_COUNT 31
2392
2393#endif
2394
2395#if defined(FEAT_GUI_TABLINE) || defined(PROTO)
2396 static void
2397add_tabline_popup_menu_entry(HMENU pmenu, UINT item_id, char_u *item_text)
2398{
2399#ifdef FEAT_MBYTE
2400 WCHAR *wn = NULL;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002401
2402 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
2403 {
2404 /* 'encoding' differs from active codepage: convert menu name
2405 * and use wide function */
2406 wn = enc_to_utf16(item_text, NULL);
2407 if (wn != NULL)
2408 {
2409 MENUITEMINFOW infow;
2410
2411 infow.cbSize = sizeof(infow);
2412 infow.fMask = MIIM_TYPE | MIIM_ID;
2413 infow.wID = item_id;
2414 infow.fType = MFT_STRING;
2415 infow.dwTypeData = wn;
2416 infow.cch = (UINT)wcslen(wn);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02002417 InsertMenuItemW(pmenu, item_id, FALSE, &infow);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002418 vim_free(wn);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002419 }
2420 }
2421
2422 if (wn == NULL)
2423#endif
2424 {
2425 MENUITEMINFO info;
2426
2427 info.cbSize = sizeof(info);
2428 info.fMask = MIIM_TYPE | MIIM_ID;
2429 info.wID = item_id;
2430 info.fType = MFT_STRING;
2431 info.dwTypeData = (LPTSTR)item_text;
2432 info.cch = (UINT)STRLEN(item_text);
2433 InsertMenuItem(pmenu, item_id, FALSE, &info);
2434 }
2435}
2436
2437 static void
2438show_tabline_popup_menu(void)
2439{
2440 HMENU tab_pmenu;
2441 long rval;
2442 POINT pt;
2443
2444 /* When ignoring events don't show the menu. */
2445 if (hold_gui_events
2446# ifdef FEAT_CMDWIN
2447 || cmdwin_type != 0
2448# endif
2449 )
2450 return;
2451
2452 tab_pmenu = CreatePopupMenu();
2453 if (tab_pmenu == NULL)
2454 return;
2455
2456 if (first_tabpage->tp_next != NULL)
2457 add_tabline_popup_menu_entry(tab_pmenu,
2458 TABLINE_MENU_CLOSE, (char_u *)_("Close tab"));
2459 add_tabline_popup_menu_entry(tab_pmenu,
2460 TABLINE_MENU_NEW, (char_u *)_("New tab"));
2461 add_tabline_popup_menu_entry(tab_pmenu,
2462 TABLINE_MENU_OPEN, (char_u *)_("Open tab..."));
2463
2464 GetCursorPos(&pt);
2465 rval = TrackPopupMenuEx(tab_pmenu, TPM_RETURNCMD, pt.x, pt.y, s_tabhwnd,
2466 NULL);
2467
2468 DestroyMenu(tab_pmenu);
2469
2470 /* Add the string cmd into input buffer */
2471 if (rval > 0)
2472 {
2473 TCHITTESTINFO htinfo;
2474 int idx;
2475
2476 if (ScreenToClient(s_tabhwnd, &pt) == 0)
2477 return;
2478
2479 htinfo.pt.x = pt.x;
2480 htinfo.pt.y = pt.y;
2481 idx = TabCtrl_HitTest(s_tabhwnd, &htinfo);
2482 if (idx == -1)
2483 idx = 0;
2484 else
2485 idx += 1;
2486
2487 send_tabline_menu_event(idx, (int)rval);
2488 }
2489}
2490
2491/*
2492 * Show or hide the tabline.
2493 */
2494 void
2495gui_mch_show_tabline(int showit)
2496{
2497 if (s_tabhwnd == NULL)
2498 return;
2499
2500 if (!showit != !showing_tabline)
2501 {
2502 if (showit)
2503 ShowWindow(s_tabhwnd, SW_SHOW);
2504 else
2505 ShowWindow(s_tabhwnd, SW_HIDE);
2506 showing_tabline = showit;
2507 }
2508}
2509
2510/*
2511 * Return TRUE when tabline is displayed.
2512 */
2513 int
2514gui_mch_showing_tabline(void)
2515{
2516 return s_tabhwnd != NULL && showing_tabline;
2517}
2518
2519/*
2520 * Update the labels of the tabline.
2521 */
2522 void
2523gui_mch_update_tabline(void)
2524{
2525 tabpage_T *tp;
2526 TCITEM tie;
2527 int nr = 0;
2528 int curtabidx = 0;
2529 int tabadded = 0;
2530#ifdef FEAT_MBYTE
2531 static int use_unicode = FALSE;
2532 int uu;
2533 WCHAR *wstr = NULL;
2534#endif
2535
2536 if (s_tabhwnd == NULL)
2537 return;
2538
Bram Moolenaarcea912a2016-10-12 14:20:24 +02002539#ifdef FEAT_MBYTE
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002540# ifndef CCM_SETUNICODEFORMAT
2541 /* For older compilers. We assume this never changes. */
2542# define CCM_SETUNICODEFORMAT 0x2005
2543# endif
2544 uu = (enc_codepage >= 0 && (int)GetACP() != enc_codepage);
2545 if (uu != use_unicode)
2546 {
2547 /* Enable/disable unicode support */
2548 SendMessage(s_tabhwnd, CCM_SETUNICODEFORMAT, (WPARAM)uu, (LPARAM)0);
2549 use_unicode = uu;
2550 }
2551#endif
2552
2553 tie.mask = TCIF_TEXT;
2554 tie.iImage = -1;
2555
2556 /* Disable redraw for tab updates to eliminate O(N^2) draws. */
2557 SendMessage(s_tabhwnd, WM_SETREDRAW, (WPARAM)FALSE, 0);
2558
2559 /* Add a label for each tab page. They all contain the same text area. */
2560 for (tp = first_tabpage; tp != NULL; tp = tp->tp_next, ++nr)
2561 {
2562 if (tp == curtab)
2563 curtabidx = nr;
2564
2565 if (nr >= TabCtrl_GetItemCount(s_tabhwnd))
2566 {
2567 /* Add the tab */
2568 tie.pszText = "-Empty-";
2569 TabCtrl_InsertItem(s_tabhwnd, nr, &tie);
2570 tabadded = 1;
2571 }
2572
2573 get_tabline_label(tp, FALSE);
2574 tie.pszText = (LPSTR)NameBuff;
2575#ifdef FEAT_MBYTE
2576 wstr = NULL;
2577 if (use_unicode)
2578 {
2579 /* Need to go through Unicode. */
2580 wstr = enc_to_utf16(NameBuff, NULL);
2581 if (wstr != NULL)
2582 {
2583 TCITEMW tiw;
2584
2585 tiw.mask = TCIF_TEXT;
2586 tiw.iImage = -1;
2587 tiw.pszText = wstr;
2588 SendMessage(s_tabhwnd, TCM_SETITEMW, (WPARAM)nr, (LPARAM)&tiw);
2589 vim_free(wstr);
2590 }
2591 }
2592 if (wstr == NULL)
2593#endif
2594 {
2595 TabCtrl_SetItem(s_tabhwnd, nr, &tie);
2596 }
2597 }
2598
2599 /* Remove any old labels. */
2600 while (nr < TabCtrl_GetItemCount(s_tabhwnd))
2601 TabCtrl_DeleteItem(s_tabhwnd, nr);
2602
2603 if (!tabadded && TabCtrl_GetCurSel(s_tabhwnd) != curtabidx)
2604 TabCtrl_SetCurSel(s_tabhwnd, curtabidx);
2605
2606 /* Re-enable redraw and redraw. */
2607 SendMessage(s_tabhwnd, WM_SETREDRAW, (WPARAM)TRUE, 0);
2608 RedrawWindow(s_tabhwnd, NULL, NULL,
2609 RDW_ERASE | RDW_FRAME | RDW_INVALIDATE | RDW_ALLCHILDREN);
2610
2611 if (tabadded && TabCtrl_GetCurSel(s_tabhwnd) != curtabidx)
2612 TabCtrl_SetCurSel(s_tabhwnd, curtabidx);
2613}
2614
2615/*
2616 * Set the current tab to "nr". First tab is 1.
2617 */
2618 void
2619gui_mch_set_curtab(int nr)
2620{
2621 if (s_tabhwnd == NULL)
2622 return;
2623
2624 if (TabCtrl_GetCurSel(s_tabhwnd) != nr - 1)
2625 TabCtrl_SetCurSel(s_tabhwnd, nr - 1);
2626}
2627
2628#endif
2629
2630/*
2631 * ":simalt" command.
2632 */
2633 void
2634ex_simalt(exarg_T *eap)
2635{
Bram Moolenaar7a85b0f2017-04-22 15:17:40 +02002636 char_u *keys = eap->arg;
2637 int fill_typebuf = FALSE;
2638 char_u key_name[4];
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002639
2640 PostMessage(s_hwnd, WM_SYSCOMMAND, (WPARAM)SC_KEYMENU, (LPARAM)0);
2641 while (*keys)
2642 {
2643 if (*keys == '~')
2644 *keys = ' '; /* for showing system menu */
2645 PostMessage(s_hwnd, WM_CHAR, (WPARAM)*keys, (LPARAM)0);
2646 keys++;
Bram Moolenaar7a85b0f2017-04-22 15:17:40 +02002647 fill_typebuf = TRUE;
2648 }
2649 if (fill_typebuf)
2650 {
Bram Moolenaara21ccb72017-04-29 17:40:22 +02002651 /* Put a NOP in the typeahead buffer so that the message will get
Bram Moolenaar7a85b0f2017-04-22 15:17:40 +02002652 * processed. */
2653 key_name[0] = K_SPECIAL;
2654 key_name[1] = KS_EXTRA;
Bram Moolenaara21ccb72017-04-29 17:40:22 +02002655 key_name[2] = KE_NOP;
Bram Moolenaar7a85b0f2017-04-22 15:17:40 +02002656 key_name[3] = NUL;
2657 typebuf_was_filled = TRUE;
2658 (void)ins_typebuf(key_name, REMAP_NONE, 0, TRUE, FALSE);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002659 }
2660}
2661
2662/*
2663 * Create the find & replace dialogs.
2664 * You can't have both at once: ":find" when replace is showing, destroys
2665 * the replace dialog first, and the other way around.
2666 */
2667#ifdef MSWIN_FIND_REPLACE
2668 static void
2669initialise_findrep(char_u *initial_string)
2670{
2671 int wword = FALSE;
2672 int mcase = !p_ic;
2673 char_u *entry_text;
2674
2675 /* Get the search string to use. */
2676 entry_text = get_find_dialog_text(initial_string, &wword, &mcase);
2677
2678 s_findrep_struct.hwndOwner = s_hwnd;
2679 s_findrep_struct.Flags = FR_DOWN;
2680 if (mcase)
2681 s_findrep_struct.Flags |= FR_MATCHCASE;
2682 if (wword)
2683 s_findrep_struct.Flags |= FR_WHOLEWORD;
2684 if (entry_text != NULL && *entry_text != NUL)
2685 vim_strncpy((char_u *)s_findrep_struct.lpstrFindWhat, entry_text,
2686 s_findrep_struct.wFindWhatLen - 1);
2687 vim_free(entry_text);
2688}
2689#endif
2690
2691 static void
2692set_window_title(HWND hwnd, char *title)
2693{
2694#ifdef FEAT_MBYTE
2695 if (title != NULL && enc_codepage >= 0 && enc_codepage != (int)GetACP())
2696 {
2697 WCHAR *wbuf;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002698
2699 /* Convert the title from 'encoding' to UTF-16. */
2700 wbuf = (WCHAR *)enc_to_utf16((char_u *)title, NULL);
2701 if (wbuf != NULL)
2702 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +02002703 SetWindowTextW(hwnd, wbuf);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002704 vim_free(wbuf);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002705 }
Bram Moolenaarcea912a2016-10-12 14:20:24 +02002706 return;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002707 }
2708#endif
2709 (void)SetWindowText(hwnd, (LPCSTR)title);
2710}
2711
2712 void
2713gui_mch_find_dialog(exarg_T *eap)
2714{
2715#ifdef MSWIN_FIND_REPLACE
2716 if (s_findrep_msg != 0)
2717 {
2718 if (IsWindow(s_findrep_hwnd) && !s_findrep_is_find)
2719 DestroyWindow(s_findrep_hwnd);
2720
2721 if (!IsWindow(s_findrep_hwnd))
2722 {
2723 initialise_findrep(eap->arg);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02002724# ifdef FEAT_MBYTE
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002725 /* If the OS is Windows NT, and 'encoding' differs from active
2726 * codepage: convert text and use wide function. */
Bram Moolenaarcea912a2016-10-12 14:20:24 +02002727 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002728 {
2729 findrep_atow(&s_findrep_struct_w, &s_findrep_struct);
2730 s_findrep_hwnd = FindTextW(
2731 (LPFINDREPLACEW) &s_findrep_struct_w);
2732 }
2733 else
2734# endif
2735 s_findrep_hwnd = FindText((LPFINDREPLACE) &s_findrep_struct);
2736 }
2737
2738 set_window_title(s_findrep_hwnd,
2739 _("Find string (use '\\\\' to find a '\\')"));
2740 (void)SetFocus(s_findrep_hwnd);
2741
2742 s_findrep_is_find = TRUE;
2743 }
2744#endif
2745}
2746
2747
2748 void
2749gui_mch_replace_dialog(exarg_T *eap)
2750{
2751#ifdef MSWIN_FIND_REPLACE
2752 if (s_findrep_msg != 0)
2753 {
2754 if (IsWindow(s_findrep_hwnd) && s_findrep_is_find)
2755 DestroyWindow(s_findrep_hwnd);
2756
2757 if (!IsWindow(s_findrep_hwnd))
2758 {
2759 initialise_findrep(eap->arg);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02002760# ifdef FEAT_MBYTE
2761 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002762 {
2763 findrep_atow(&s_findrep_struct_w, &s_findrep_struct);
2764 s_findrep_hwnd = ReplaceTextW(
2765 (LPFINDREPLACEW) &s_findrep_struct_w);
2766 }
2767 else
2768# endif
2769 s_findrep_hwnd = ReplaceText(
2770 (LPFINDREPLACE) &s_findrep_struct);
2771 }
2772
2773 set_window_title(s_findrep_hwnd,
2774 _("Find & Replace (use '\\\\' to find a '\\')"));
2775 (void)SetFocus(s_findrep_hwnd);
2776
2777 s_findrep_is_find = FALSE;
2778 }
2779#endif
2780}
2781
2782
2783/*
2784 * Set visibility of the pointer.
2785 */
2786 void
2787gui_mch_mousehide(int hide)
2788{
2789 if (hide != gui.pointer_hidden)
2790 {
2791 ShowCursor(!hide);
2792 gui.pointer_hidden = hide;
2793 }
2794}
2795
2796#ifdef FEAT_MENU
2797 static void
2798gui_mch_show_popupmenu_at(vimmenu_T *menu, int x, int y)
2799{
2800 /* Unhide the mouse, we don't get move events here. */
2801 gui_mch_mousehide(FALSE);
2802
2803 (void)TrackPopupMenu(
2804 (HMENU)menu->submenu_id,
2805 TPM_LEFTALIGN | TPM_LEFTBUTTON,
2806 x, y,
2807 (int)0, /*reserved param*/
2808 s_hwnd,
2809 NULL);
2810 /*
2811 * NOTE: The pop-up menu can eat the mouse up event.
2812 * We deal with this in normal.c.
2813 */
2814}
2815#endif
2816
2817/*
2818 * Got a message when the system will go down.
2819 */
2820 static void
2821_OnEndSession(void)
2822{
2823 getout_preserve_modified(1);
2824}
2825
2826/*
2827 * Get this message when the user clicks on the cross in the top right corner
2828 * of a Windows95 window.
2829 */
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002830 static void
Bram Moolenaar1266d672017-02-01 13:43:36 +01002831_OnClose(HWND hwnd UNUSED)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002832{
2833 gui_shell_closed();
2834}
2835
2836/*
2837 * Get a message when the window is being destroyed.
2838 */
2839 static void
Bram Moolenaar1266d672017-02-01 13:43:36 +01002840_OnDestroy(HWND hwnd)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002841{
2842 if (!destroying)
2843 _OnClose(hwnd);
2844}
2845
2846 static void
2847_OnPaint(
2848 HWND hwnd)
2849{
2850 if (!IsMinimized(hwnd))
2851 {
2852 PAINTSTRUCT ps;
2853
2854 out_flush(); /* make sure all output has been processed */
2855 (void)BeginPaint(hwnd, &ps);
2856#if defined(FEAT_DIRECTX)
2857 if (IS_ENABLE_DIRECTX())
2858 DWriteContext_BeginDraw(s_dwc);
2859#endif
2860
2861#ifdef FEAT_MBYTE
2862 /* prevent multi-byte characters from misprinting on an invalid
2863 * rectangle */
2864 if (has_mbyte)
2865 {
2866 RECT rect;
2867
2868 GetClientRect(hwnd, &rect);
2869 ps.rcPaint.left = rect.left;
2870 ps.rcPaint.right = rect.right;
2871 }
2872#endif
2873
2874 if (!IsRectEmpty(&ps.rcPaint))
2875 {
2876#if defined(FEAT_DIRECTX)
2877 if (IS_ENABLE_DIRECTX())
2878 DWriteContext_BindDC(s_dwc, s_hdc, &ps.rcPaint);
2879#endif
2880 gui_redraw(ps.rcPaint.left, ps.rcPaint.top,
2881 ps.rcPaint.right - ps.rcPaint.left + 1,
2882 ps.rcPaint.bottom - ps.rcPaint.top + 1);
2883 }
2884
2885#if defined(FEAT_DIRECTX)
2886 if (IS_ENABLE_DIRECTX())
2887 DWriteContext_EndDraw(s_dwc);
2888#endif
2889 EndPaint(hwnd, &ps);
2890 }
2891}
2892
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002893 static void
2894_OnSize(
2895 HWND hwnd,
Bram Moolenaar1266d672017-02-01 13:43:36 +01002896 UINT state UNUSED,
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002897 int cx,
2898 int cy)
2899{
2900 if (!IsMinimized(hwnd))
2901 {
2902 gui_resize_shell(cx, cy);
2903
2904#ifdef FEAT_MENU
2905 /* Menu bar may wrap differently now */
2906 gui_mswin_get_menu_height(TRUE);
2907#endif
2908 }
2909}
2910
2911 static void
2912_OnSetFocus(
2913 HWND hwnd,
2914 HWND hwndOldFocus)
2915{
2916 gui_focus_change(TRUE);
2917 s_getting_focus = TRUE;
2918 (void)MyWindowProc(hwnd, WM_SETFOCUS, (WPARAM)hwndOldFocus, 0);
2919}
2920
2921 static void
2922_OnKillFocus(
2923 HWND hwnd,
2924 HWND hwndNewFocus)
2925{
2926 gui_focus_change(FALSE);
2927 s_getting_focus = FALSE;
2928 (void)MyWindowProc(hwnd, WM_KILLFOCUS, (WPARAM)hwndNewFocus, 0);
2929}
2930
2931/*
2932 * Get a message when the user switches back to vim
2933 */
2934 static LRESULT
2935_OnActivateApp(
2936 HWND hwnd,
2937 BOOL fActivate,
2938 DWORD dwThreadId)
2939{
2940 /* we call gui_focus_change() in _OnSetFocus() */
2941 /* gui_focus_change((int)fActivate); */
2942 return MyWindowProc(hwnd, WM_ACTIVATEAPP, fActivate, (DWORD)dwThreadId);
2943}
2944
2945#if defined(FEAT_WINDOWS) || defined(PROTO)
2946 void
2947gui_mch_destroy_scrollbar(scrollbar_T *sb)
2948{
2949 DestroyWindow(sb->id);
2950}
2951#endif
2952
2953/*
2954 * Get current mouse coordinates in text window.
2955 */
2956 void
2957gui_mch_getmouse(int *x, int *y)
2958{
2959 RECT rct;
2960 POINT mp;
2961
2962 (void)GetWindowRect(s_textArea, &rct);
2963 (void)GetCursorPos((LPPOINT)&mp);
2964 *x = (int)(mp.x - rct.left);
2965 *y = (int)(mp.y - rct.top);
2966}
2967
2968/*
2969 * Move mouse pointer to character at (x, y).
2970 */
2971 void
2972gui_mch_setmouse(int x, int y)
2973{
2974 RECT rct;
2975
2976 (void)GetWindowRect(s_textArea, &rct);
2977 (void)SetCursorPos(x + gui.border_offset + rct.left,
2978 y + gui.border_offset + rct.top);
2979}
2980
2981 static void
2982gui_mswin_get_valid_dimensions(
2983 int w,
2984 int h,
2985 int *valid_w,
2986 int *valid_h)
2987{
2988 int base_width, base_height;
2989
2990 base_width = gui_get_base_width()
2991 + (GetSystemMetrics(SM_CXFRAME) +
2992 GetSystemMetrics(SM_CXPADDEDBORDER)) * 2;
2993 base_height = gui_get_base_height()
2994 + (GetSystemMetrics(SM_CYFRAME) +
2995 GetSystemMetrics(SM_CXPADDEDBORDER)) * 2
2996 + GetSystemMetrics(SM_CYCAPTION)
2997#ifdef FEAT_MENU
2998 + gui_mswin_get_menu_height(FALSE)
2999#endif
3000 ;
3001 *valid_w = base_width +
3002 ((w - base_width) / gui.char_width) * gui.char_width;
3003 *valid_h = base_height +
3004 ((h - base_height) / gui.char_height) * gui.char_height;
3005}
3006
3007 void
3008gui_mch_flash(int msec)
3009{
3010 RECT rc;
3011
3012 /*
3013 * Note: InvertRect() excludes right and bottom of rectangle.
3014 */
3015 rc.left = 0;
3016 rc.top = 0;
3017 rc.right = gui.num_cols * gui.char_width;
3018 rc.bottom = gui.num_rows * gui.char_height;
3019 InvertRect(s_hdc, &rc);
3020 gui_mch_flush(); /* make sure it's displayed */
3021
3022 ui_delay((long)msec, TRUE); /* wait for a few msec */
3023
3024 InvertRect(s_hdc, &rc);
3025}
3026
3027/*
3028 * Return flags used for scrolling.
3029 * The SW_INVALIDATE is required when part of the window is covered or
3030 * off-screen. Refer to MS KB Q75236.
3031 */
3032 static int
3033get_scroll_flags(void)
3034{
3035 HWND hwnd;
3036 RECT rcVim, rcOther, rcDest;
3037
3038 GetWindowRect(s_hwnd, &rcVim);
3039
3040 /* Check if the window is partly above or below the screen. We don't care
3041 * about partly left or right of the screen, it is not relevant when
3042 * scrolling up or down. */
3043 if (rcVim.top < 0 || rcVim.bottom > GetSystemMetrics(SM_CYFULLSCREEN))
3044 return SW_INVALIDATE;
3045
3046 /* Check if there is an window (partly) on top of us. */
3047 for (hwnd = s_hwnd; (hwnd = GetWindow(hwnd, GW_HWNDPREV)) != (HWND)0; )
3048 if (IsWindowVisible(hwnd))
3049 {
3050 GetWindowRect(hwnd, &rcOther);
3051 if (IntersectRect(&rcDest, &rcVim, &rcOther))
3052 return SW_INVALIDATE;
3053 }
3054 return 0;
3055}
3056
3057/*
3058 * On some Intel GPUs, the regions drawn just prior to ScrollWindowEx()
3059 * may not be scrolled out properly.
3060 * For gVim, when _OnScroll() is repeated, the character at the
3061 * previous cursor position may be left drawn after scroll.
3062 * The problem can be avoided by calling GetPixel() to get a pixel in
3063 * the region before ScrollWindowEx().
3064 */
3065 static void
3066intel_gpu_workaround(void)
3067{
3068 GetPixel(s_hdc, FILL_X(gui.col), FILL_Y(gui.row));
3069}
3070
3071/*
3072 * Delete the given number of lines from the given row, scrolling up any
3073 * text further down within the scroll region.
3074 */
3075 void
3076gui_mch_delete_lines(
3077 int row,
3078 int num_lines)
3079{
3080 RECT rc;
3081
3082 intel_gpu_workaround();
3083
3084 rc.left = FILL_X(gui.scroll_region_left);
3085 rc.right = FILL_X(gui.scroll_region_right + 1);
3086 rc.top = FILL_Y(row);
3087 rc.bottom = FILL_Y(gui.scroll_region_bot + 1);
3088
3089 ScrollWindowEx(s_textArea, 0, -num_lines * gui.char_height,
3090 &rc, &rc, NULL, NULL, get_scroll_flags());
3091
3092 UpdateWindow(s_textArea);
3093 /* This seems to be required to avoid the cursor disappearing when
3094 * scrolling such that the cursor ends up in the top-left character on
3095 * the screen... But why? (Webb) */
3096 /* It's probably fixed by disabling drawing the cursor while scrolling. */
3097 /* gui.cursor_is_valid = FALSE; */
3098
3099 gui_clear_block(gui.scroll_region_bot - num_lines + 1,
3100 gui.scroll_region_left,
3101 gui.scroll_region_bot, gui.scroll_region_right);
3102}
3103
3104/*
3105 * Insert the given number of lines before the given row, scrolling down any
3106 * following text within the scroll region.
3107 */
3108 void
3109gui_mch_insert_lines(
3110 int row,
3111 int num_lines)
3112{
3113 RECT rc;
3114
3115 intel_gpu_workaround();
3116
3117 rc.left = FILL_X(gui.scroll_region_left);
3118 rc.right = FILL_X(gui.scroll_region_right + 1);
3119 rc.top = FILL_Y(row);
3120 rc.bottom = FILL_Y(gui.scroll_region_bot + 1);
3121 /* The SW_INVALIDATE is required when part of the window is covered or
3122 * off-screen. How do we avoid it when it's not needed? */
3123 ScrollWindowEx(s_textArea, 0, num_lines * gui.char_height,
3124 &rc, &rc, NULL, NULL, get_scroll_flags());
3125
3126 UpdateWindow(s_textArea);
3127
3128 gui_clear_block(row, gui.scroll_region_left,
3129 row + num_lines - 1, gui.scroll_region_right);
3130}
3131
3132
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003133 void
Bram Moolenaar1266d672017-02-01 13:43:36 +01003134gui_mch_exit(int rc UNUSED)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003135{
3136#if defined(FEAT_DIRECTX)
3137 DWriteContext_Close(s_dwc);
3138 DWrite_Final();
3139 s_dwc = NULL;
3140#endif
3141
3142 ReleaseDC(s_textArea, s_hdc);
3143 DeleteObject(s_brush);
3144
3145#ifdef FEAT_TEAROFF
3146 /* Unload the tearoff bitmap */
3147 (void)DeleteObject((HGDIOBJ)s_htearbitmap);
3148#endif
3149
3150 /* Destroy our window (if we have one). */
3151 if (s_hwnd != NULL)
3152 {
3153 destroying = TRUE; /* ignore WM_DESTROY message now */
3154 DestroyWindow(s_hwnd);
3155 }
3156
3157#ifdef GLOBAL_IME
3158 global_ime_end();
3159#endif
3160}
3161
3162 static char_u *
3163logfont2name(LOGFONT lf)
3164{
3165 char *p;
3166 char *res;
3167 char *charset_name;
Bram Moolenaar7c1c6db2016-04-03 22:08:05 +02003168 char *quality_name;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003169 char *font_name = lf.lfFaceName;
3170
3171 charset_name = charset_id2name((int)lf.lfCharSet);
3172#ifdef FEAT_MBYTE
3173 /* Convert a font name from the current codepage to 'encoding'.
3174 * TODO: Use Wide APIs (including LOGFONTW) instead of ANSI APIs. */
3175 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
3176 {
3177 int len;
3178 acp_to_enc((char_u *)lf.lfFaceName, (int)strlen(lf.lfFaceName),
3179 (char_u **)&font_name, &len);
3180 }
3181#endif
Bram Moolenaar7c1c6db2016-04-03 22:08:05 +02003182 quality_name = quality_id2name((int)lf.lfQuality);
3183
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003184 res = (char *)alloc((unsigned)(strlen(font_name) + 20
3185 + (charset_name == NULL ? 0 : strlen(charset_name) + 2)));
3186 if (res != NULL)
3187 {
3188 p = res;
3189 /* make a normal font string out of the lf thing:*/
3190 sprintf((char *)p, "%s:h%d", font_name, pixels_to_points(
3191 lf.lfHeight < 0 ? -lf.lfHeight : lf.lfHeight, TRUE));
3192 while (*p)
3193 {
3194 if (*p == ' ')
3195 *p = '_';
3196 ++p;
3197 }
3198 if (lf.lfItalic)
3199 STRCAT(p, ":i");
3200 if (lf.lfWeight >= FW_BOLD)
3201 STRCAT(p, ":b");
3202 if (lf.lfUnderline)
3203 STRCAT(p, ":u");
3204 if (lf.lfStrikeOut)
3205 STRCAT(p, ":s");
3206 if (charset_name != NULL)
3207 {
3208 STRCAT(p, ":c");
3209 STRCAT(p, charset_name);
3210 }
Bram Moolenaar7c1c6db2016-04-03 22:08:05 +02003211 if (quality_name != NULL)
3212 {
3213 STRCAT(p, ":q");
3214 STRCAT(p, quality_name);
3215 }
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003216 }
3217
3218#ifdef FEAT_MBYTE
3219 if (font_name != lf.lfFaceName)
3220 vim_free(font_name);
3221#endif
3222 return (char_u *)res;
3223}
3224
3225
3226#ifdef FEAT_MBYTE_IME
3227/*
3228 * Set correct LOGFONT to IME. Use 'guifontwide' if available, otherwise use
3229 * 'guifont'
3230 */
3231 static void
3232update_im_font(void)
3233{
3234 LOGFONT lf_wide;
3235
3236 if (p_guifontwide != NULL && *p_guifontwide != NUL
3237 && gui.wide_font != NOFONT
3238 && GetObject((HFONT)gui.wide_font, sizeof(lf_wide), &lf_wide))
3239 norm_logfont = lf_wide;
3240 else
3241 norm_logfont = sub_logfont;
3242 im_set_font(&norm_logfont);
3243}
3244#endif
3245
3246#ifdef FEAT_MBYTE
3247/*
3248 * Handler of gui.wide_font (p_guifontwide) changed notification.
3249 */
3250 void
3251gui_mch_wide_font_changed(void)
3252{
3253 LOGFONT lf;
3254
3255# ifdef FEAT_MBYTE_IME
3256 update_im_font();
3257# endif
3258
3259 gui_mch_free_font(gui.wide_ital_font);
3260 gui.wide_ital_font = NOFONT;
3261 gui_mch_free_font(gui.wide_bold_font);
3262 gui.wide_bold_font = NOFONT;
3263 gui_mch_free_font(gui.wide_boldital_font);
3264 gui.wide_boldital_font = NOFONT;
3265
3266 if (gui.wide_font
3267 && GetObject((HFONT)gui.wide_font, sizeof(lf), &lf))
3268 {
3269 if (!lf.lfItalic)
3270 {
3271 lf.lfItalic = TRUE;
3272 gui.wide_ital_font = get_font_handle(&lf);
3273 lf.lfItalic = FALSE;
3274 }
3275 if (lf.lfWeight < FW_BOLD)
3276 {
3277 lf.lfWeight = FW_BOLD;
3278 gui.wide_bold_font = get_font_handle(&lf);
3279 if (!lf.lfItalic)
3280 {
3281 lf.lfItalic = TRUE;
3282 gui.wide_boldital_font = get_font_handle(&lf);
3283 }
3284 }
3285 }
3286}
3287#endif
3288
3289/*
3290 * Initialise vim to use the font with the given name.
3291 * Return FAIL if the font could not be loaded, OK otherwise.
3292 */
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003293 int
Bram Moolenaar1266d672017-02-01 13:43:36 +01003294gui_mch_init_font(char_u *font_name, int fontset UNUSED)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003295{
3296 LOGFONT lf;
3297 GuiFont font = NOFONT;
3298 char_u *p;
3299
3300 /* Load the font */
3301 if (get_logfont(&lf, font_name, NULL, TRUE) == OK)
3302 font = get_font_handle(&lf);
3303 if (font == NOFONT)
3304 return FAIL;
3305
3306 if (font_name == NULL)
3307 font_name = (char_u *)lf.lfFaceName;
3308#if defined(FEAT_MBYTE_IME) || defined(GLOBAL_IME)
3309 norm_logfont = lf;
3310 sub_logfont = lf;
3311#endif
3312#ifdef FEAT_MBYTE_IME
3313 update_im_font();
3314#endif
3315 gui_mch_free_font(gui.norm_font);
3316 gui.norm_font = font;
3317 current_font_height = lf.lfHeight;
3318 GetFontSize(font);
3319
3320 p = logfont2name(lf);
3321 if (p != NULL)
3322 {
3323 hl_set_font_name(p);
3324
3325 /* When setting 'guifont' to "*" replace it with the actual font name.
3326 * */
3327 if (STRCMP(font_name, "*") == 0 && STRCMP(p_guifont, "*") == 0)
3328 {
3329 vim_free(p_guifont);
3330 p_guifont = p;
3331 }
3332 else
3333 vim_free(p);
3334 }
3335
3336 gui_mch_free_font(gui.ital_font);
3337 gui.ital_font = NOFONT;
3338 gui_mch_free_font(gui.bold_font);
3339 gui.bold_font = NOFONT;
3340 gui_mch_free_font(gui.boldital_font);
3341 gui.boldital_font = NOFONT;
3342
3343 if (!lf.lfItalic)
3344 {
3345 lf.lfItalic = TRUE;
3346 gui.ital_font = get_font_handle(&lf);
3347 lf.lfItalic = FALSE;
3348 }
3349 if (lf.lfWeight < FW_BOLD)
3350 {
3351 lf.lfWeight = FW_BOLD;
3352 gui.bold_font = get_font_handle(&lf);
3353 if (!lf.lfItalic)
3354 {
3355 lf.lfItalic = TRUE;
3356 gui.boldital_font = get_font_handle(&lf);
3357 }
3358 }
3359
3360 return OK;
3361}
3362
3363#ifndef WPF_RESTORETOMAXIMIZED
3364# define WPF_RESTORETOMAXIMIZED 2 /* just in case someone doesn't have it */
3365#endif
3366
3367/*
3368 * Return TRUE if the GUI window is maximized, filling the whole screen.
3369 */
3370 int
3371gui_mch_maximized(void)
3372{
3373 WINDOWPLACEMENT wp;
3374
3375 wp.length = sizeof(WINDOWPLACEMENT);
3376 if (GetWindowPlacement(s_hwnd, &wp))
3377 return wp.showCmd == SW_SHOWMAXIMIZED
3378 || (wp.showCmd == SW_SHOWMINIMIZED
3379 && wp.flags == WPF_RESTORETOMAXIMIZED);
3380
3381 return 0;
3382}
3383
3384/*
3385 * Called when the font changed while the window is maximized. Compute the
3386 * new Rows and Columns. This is like resizing the window.
3387 */
3388 void
3389gui_mch_newfont(void)
3390{
3391 RECT rect;
3392
3393 GetWindowRect(s_hwnd, &rect);
3394 if (win_socket_id == 0)
3395 {
3396 gui_resize_shell(rect.right - rect.left
3397 - (GetSystemMetrics(SM_CXFRAME) +
3398 GetSystemMetrics(SM_CXPADDEDBORDER)) * 2,
3399 rect.bottom - rect.top
3400 - (GetSystemMetrics(SM_CYFRAME) +
3401 GetSystemMetrics(SM_CXPADDEDBORDER)) * 2
3402 - GetSystemMetrics(SM_CYCAPTION)
3403#ifdef FEAT_MENU
3404 - gui_mswin_get_menu_height(FALSE)
3405#endif
3406 );
3407 }
3408 else
3409 {
3410 /* Inside another window, don't use the frame and border. */
3411 gui_resize_shell(rect.right - rect.left,
3412 rect.bottom - rect.top
3413#ifdef FEAT_MENU
3414 - gui_mswin_get_menu_height(FALSE)
3415#endif
3416 );
3417 }
3418}
3419
3420/*
3421 * Set the window title
3422 */
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003423 void
3424gui_mch_settitle(
3425 char_u *title,
Bram Moolenaar1266d672017-02-01 13:43:36 +01003426 char_u *icon UNUSED)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003427{
3428 set_window_title(s_hwnd, (title == NULL ? "VIM" : (char *)title));
3429}
3430
Bram Moolenaara6b7a082016-08-10 20:53:05 +02003431#if defined(FEAT_MOUSESHAPE) || defined(PROTO)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003432/* Table for shape IDCs. Keep in sync with the mshape_names[] table in
3433 * misc2.c! */
3434static LPCSTR mshape_idcs[] =
3435{
3436 IDC_ARROW, /* arrow */
3437 MAKEINTRESOURCE(0), /* blank */
3438 IDC_IBEAM, /* beam */
3439 IDC_SIZENS, /* updown */
3440 IDC_SIZENS, /* udsizing */
3441 IDC_SIZEWE, /* leftright */
3442 IDC_SIZEWE, /* lrsizing */
3443 IDC_WAIT, /* busy */
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003444 IDC_NO, /* no */
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003445 IDC_ARROW, /* crosshair */
3446 IDC_ARROW, /* hand1 */
3447 IDC_ARROW, /* hand2 */
3448 IDC_ARROW, /* pencil */
3449 IDC_ARROW, /* question */
3450 IDC_ARROW, /* right-arrow */
3451 IDC_UPARROW, /* up-arrow */
3452 IDC_ARROW /* last one */
3453};
3454
3455 void
3456mch_set_mouse_shape(int shape)
3457{
3458 LPCSTR idc;
3459
3460 if (shape == MSHAPE_HIDE)
3461 ShowCursor(FALSE);
3462 else
3463 {
3464 if (shape >= MSHAPE_NUMBERED)
3465 idc = IDC_ARROW;
3466 else
3467 idc = mshape_idcs[shape];
3468#ifdef SetClassLongPtr
3469 SetClassLongPtr(s_textArea, GCLP_HCURSOR, (__int3264)(LONG_PTR)LoadCursor(NULL, idc));
3470#else
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003471 SetClassLong(s_textArea, GCL_HCURSOR, (long_u)LoadCursor(NULL, idc));
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003472#endif
3473 if (!p_mh)
3474 {
3475 POINT mp;
3476
3477 /* Set the position to make it redrawn with the new shape. */
3478 (void)GetCursorPos((LPPOINT)&mp);
3479 (void)SetCursorPos(mp.x, mp.y);
3480 ShowCursor(TRUE);
3481 }
3482 }
3483}
3484#endif
3485
Bram Moolenaara6b7a082016-08-10 20:53:05 +02003486#if defined(FEAT_BROWSE) || defined(PROTO)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003487/*
3488 * The file browser exists in two versions: with "W" uses wide characters,
3489 * without "W" the current codepage. When FEAT_MBYTE is defined and on
3490 * Windows NT/2000/XP the "W" functions are used.
3491 */
3492
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003493# ifdef FEAT_MBYTE
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003494/*
3495 * Wide version of convert_filter().
3496 */
3497 static WCHAR *
3498convert_filterW(char_u *s)
3499{
3500 char_u *tmp;
3501 int len;
3502 WCHAR *res;
3503
3504 tmp = convert_filter(s);
3505 if (tmp == NULL)
3506 return NULL;
3507 len = (int)STRLEN(s) + 3;
3508 res = enc_to_utf16(tmp, &len);
3509 vim_free(tmp);
3510 return res;
3511}
3512
3513/*
3514 * Wide version of gui_mch_browse(). Keep in sync!
3515 */
3516 static char_u *
3517gui_mch_browseW(
3518 int saving,
3519 char_u *title,
3520 char_u *dflt,
3521 char_u *ext,
3522 char_u *initdir,
3523 char_u *filter)
3524{
3525 /* We always use the wide function. This means enc_to_utf16() must work,
3526 * otherwise it fails miserably! */
3527 OPENFILENAMEW fileStruct;
3528 WCHAR fileBuf[MAXPATHL];
3529 WCHAR *wp;
3530 int i;
3531 WCHAR *titlep = NULL;
3532 WCHAR *extp = NULL;
3533 WCHAR *initdirp = NULL;
3534 WCHAR *filterp;
3535 char_u *p;
3536
3537 if (dflt == NULL)
3538 fileBuf[0] = NUL;
3539 else
3540 {
3541 wp = enc_to_utf16(dflt, NULL);
3542 if (wp == NULL)
3543 fileBuf[0] = NUL;
3544 else
3545 {
3546 for (i = 0; wp[i] != NUL && i < MAXPATHL - 1; ++i)
3547 fileBuf[i] = wp[i];
3548 fileBuf[i] = NUL;
3549 vim_free(wp);
3550 }
3551 }
3552
3553 /* Convert the filter to Windows format. */
3554 filterp = convert_filterW(filter);
3555
3556 vim_memset(&fileStruct, 0, sizeof(OPENFILENAMEW));
Bram Moolenaarb04a98f2016-12-01 20:32:29 +01003557# ifdef OPENFILENAME_SIZE_VERSION_400W
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003558 /* be compatible with Windows NT 4.0 */
Bram Moolenaar89e375a2016-03-15 18:09:57 +01003559 fileStruct.lStructSize = OPENFILENAME_SIZE_VERSION_400W;
Bram Moolenaarb04a98f2016-12-01 20:32:29 +01003560# else
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003561 fileStruct.lStructSize = sizeof(fileStruct);
Bram Moolenaarb04a98f2016-12-01 20:32:29 +01003562# endif
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003563
3564 if (title != NULL)
3565 titlep = enc_to_utf16(title, NULL);
3566 fileStruct.lpstrTitle = titlep;
3567
3568 if (ext != NULL)
3569 extp = enc_to_utf16(ext, NULL);
3570 fileStruct.lpstrDefExt = extp;
3571
3572 fileStruct.lpstrFile = fileBuf;
3573 fileStruct.nMaxFile = MAXPATHL;
3574 fileStruct.lpstrFilter = filterp;
3575 fileStruct.hwndOwner = s_hwnd; /* main Vim window is owner*/
3576 /* has an initial dir been specified? */
3577 if (initdir != NULL && *initdir != NUL)
3578 {
3579 /* Must have backslashes here, no matter what 'shellslash' says */
3580 initdirp = enc_to_utf16(initdir, NULL);
3581 if (initdirp != NULL)
3582 {
3583 for (wp = initdirp; *wp != NUL; ++wp)
3584 if (*wp == '/')
3585 *wp = '\\';
3586 }
3587 fileStruct.lpstrInitialDir = initdirp;
3588 }
3589
3590 /*
3591 * TODO: Allow selection of multiple files. Needs another arg to this
3592 * function to ask for it, and need to use OFN_ALLOWMULTISELECT below.
3593 * Also, should we use OFN_FILEMUSTEXIST when opening? Vim can edit on
3594 * files that don't exist yet, so I haven't put it in. What about
3595 * OFN_PATHMUSTEXIST?
3596 * Don't use OFN_OVERWRITEPROMPT, Vim has its own ":confirm" dialog.
3597 */
3598 fileStruct.Flags = (OFN_NOCHANGEDIR | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY);
Bram Moolenaarb04a98f2016-12-01 20:32:29 +01003599# ifdef FEAT_SHORTCUT
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003600 if (curbuf->b_p_bin)
3601 fileStruct.Flags |= OFN_NODEREFERENCELINKS;
Bram Moolenaarb04a98f2016-12-01 20:32:29 +01003602# endif
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003603 if (saving)
3604 {
3605 if (!GetSaveFileNameW(&fileStruct))
3606 return NULL;
3607 }
3608 else
3609 {
3610 if (!GetOpenFileNameW(&fileStruct))
3611 return NULL;
3612 }
3613
3614 vim_free(filterp);
3615 vim_free(initdirp);
3616 vim_free(titlep);
3617 vim_free(extp);
3618
3619 /* Convert from UCS2 to 'encoding'. */
3620 p = utf16_to_enc(fileBuf, NULL);
3621 if (p != NULL)
3622 /* when out of memory we get garbage for non-ASCII chars */
3623 STRCPY(fileBuf, p);
3624 vim_free(p);
3625
3626 /* Give focus back to main window (when using MDI). */
3627 SetFocus(s_hwnd);
3628
3629 /* Shorten the file name if possible */
3630 return vim_strsave(shorten_fname1((char_u *)fileBuf));
3631}
3632# endif /* FEAT_MBYTE */
3633
3634
3635/*
3636 * Convert the string s to the proper format for a filter string by replacing
3637 * the \t and \n delimiters with \0.
3638 * Returns the converted string in allocated memory.
3639 *
3640 * Keep in sync with convert_filterW() above!
3641 */
3642 static char_u *
3643convert_filter(char_u *s)
3644{
3645 char_u *res;
3646 unsigned s_len = (unsigned)STRLEN(s);
3647 unsigned i;
3648
3649 res = alloc(s_len + 3);
3650 if (res != NULL)
3651 {
3652 for (i = 0; i < s_len; ++i)
3653 if (s[i] == '\t' || s[i] == '\n')
3654 res[i] = '\0';
3655 else
3656 res[i] = s[i];
3657 res[s_len] = NUL;
3658 /* Add two extra NULs to make sure it's properly terminated. */
3659 res[s_len + 1] = NUL;
3660 res[s_len + 2] = NUL;
3661 }
3662 return res;
3663}
3664
3665/*
3666 * Select a directory.
3667 */
3668 char_u *
3669gui_mch_browsedir(char_u *title, char_u *initdir)
3670{
3671 /* We fake this: Use a filter that doesn't select anything and a default
3672 * file name that won't be used. */
3673 return gui_mch_browse(0, title, (char_u *)_("Not Used"), NULL,
3674 initdir, (char_u *)_("Directory\t*.nothing\n"));
3675}
3676
3677/*
3678 * Pop open a file browser and return the file selected, in allocated memory,
3679 * or NULL if Cancel is hit.
3680 * saving - TRUE if the file will be saved to, FALSE if it will be opened.
3681 * title - Title message for the file browser dialog.
3682 * dflt - Default name of file.
3683 * ext - Default extension to be added to files without extensions.
3684 * initdir - directory in which to open the browser (NULL = current dir)
3685 * filter - Filter for matched files to choose from.
3686 *
3687 * Keep in sync with gui_mch_browseW() above!
3688 */
3689 char_u *
3690gui_mch_browse(
3691 int saving,
3692 char_u *title,
3693 char_u *dflt,
3694 char_u *ext,
3695 char_u *initdir,
3696 char_u *filter)
3697{
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003698# ifdef FEAT_MBYTE
3699 return gui_mch_browseW(saving, title, dflt, ext, initdir, filter);
3700# else
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003701 OPENFILENAME fileStruct;
3702 char_u fileBuf[MAXPATHL];
3703 char_u *initdirp = NULL;
3704 char_u *filterp;
3705 char_u *p;
3706
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003707 if (dflt == NULL)
3708 fileBuf[0] = NUL;
3709 else
3710 vim_strncpy(fileBuf, dflt, MAXPATHL - 1);
3711
3712 /* Convert the filter to Windows format. */
3713 filterp = convert_filter(filter);
3714
3715 vim_memset(&fileStruct, 0, sizeof(OPENFILENAME));
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003716# ifdef OPENFILENAME_SIZE_VERSION_400
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003717 /* be compatible with Windows NT 4.0 */
3718 fileStruct.lStructSize = OPENFILENAME_SIZE_VERSION_400;
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003719# else
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003720 fileStruct.lStructSize = sizeof(fileStruct);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003721# endif
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003722
3723 fileStruct.lpstrTitle = (LPSTR)title;
3724 fileStruct.lpstrDefExt = (LPSTR)ext;
3725
3726 fileStruct.lpstrFile = (LPSTR)fileBuf;
3727 fileStruct.nMaxFile = MAXPATHL;
3728 fileStruct.lpstrFilter = (LPSTR)filterp;
3729 fileStruct.hwndOwner = s_hwnd; /* main Vim window is owner*/
3730 /* has an initial dir been specified? */
3731 if (initdir != NULL && *initdir != NUL)
3732 {
3733 /* Must have backslashes here, no matter what 'shellslash' says */
3734 initdirp = vim_strsave(initdir);
3735 if (initdirp != NULL)
3736 for (p = initdirp; *p != NUL; ++p)
3737 if (*p == '/')
3738 *p = '\\';
3739 fileStruct.lpstrInitialDir = (LPSTR)initdirp;
3740 }
3741
3742 /*
3743 * TODO: Allow selection of multiple files. Needs another arg to this
3744 * function to ask for it, and need to use OFN_ALLOWMULTISELECT below.
3745 * Also, should we use OFN_FILEMUSTEXIST when opening? Vim can edit on
3746 * files that don't exist yet, so I haven't put it in. What about
3747 * OFN_PATHMUSTEXIST?
3748 * Don't use OFN_OVERWRITEPROMPT, Vim has its own ":confirm" dialog.
3749 */
3750 fileStruct.Flags = (OFN_NOCHANGEDIR | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003751# ifdef FEAT_SHORTCUT
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003752 if (curbuf->b_p_bin)
3753 fileStruct.Flags |= OFN_NODEREFERENCELINKS;
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003754# endif
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003755 if (saving)
3756 {
3757 if (!GetSaveFileName(&fileStruct))
3758 return NULL;
3759 }
3760 else
3761 {
3762 if (!GetOpenFileName(&fileStruct))
3763 return NULL;
3764 }
3765
3766 vim_free(filterp);
3767 vim_free(initdirp);
3768
3769 /* Give focus back to main window (when using MDI). */
3770 SetFocus(s_hwnd);
3771
3772 /* Shorten the file name if possible */
3773 return vim_strsave(shorten_fname1((char_u *)fileBuf));
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003774# endif
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003775}
3776#endif /* FEAT_BROWSE */
3777
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003778 static void
3779_OnDropFiles(
Bram Moolenaar1266d672017-02-01 13:43:36 +01003780 HWND hwnd UNUSED,
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003781 HDROP hDrop)
3782{
3783#ifdef FEAT_WINDOWS
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003784# define BUFPATHLEN _MAX_PATH
3785# define DRAGQVAL 0xFFFFFFFF
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003786# ifdef FEAT_MBYTE
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003787 WCHAR wszFile[BUFPATHLEN];
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003788# endif
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003789 char szFile[BUFPATHLEN];
3790 UINT cFiles = DragQueryFile(hDrop, DRAGQVAL, NULL, 0);
3791 UINT i;
3792 char_u **fnames;
3793 POINT pt;
3794 int_u modifiers = 0;
3795
3796 /* TRACE("_OnDropFiles: %d files dropped\n", cFiles); */
3797
3798 /* Obtain dropped position */
3799 DragQueryPoint(hDrop, &pt);
3800 MapWindowPoints(s_hwnd, s_textArea, &pt, 1);
3801
3802 reset_VIsual();
3803
3804 fnames = (char_u **)alloc(cFiles * sizeof(char_u *));
3805
3806 if (fnames != NULL)
3807 for (i = 0; i < cFiles; ++i)
3808 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003809# ifdef FEAT_MBYTE
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003810 if (DragQueryFileW(hDrop, i, wszFile, BUFPATHLEN) > 0)
3811 fnames[i] = utf16_to_enc(wszFile, NULL);
3812 else
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003813# endif
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003814 {
3815 DragQueryFile(hDrop, i, szFile, BUFPATHLEN);
3816 fnames[i] = vim_strsave((char_u *)szFile);
3817 }
3818 }
3819
3820 DragFinish(hDrop);
3821
3822 if (fnames != NULL)
3823 {
3824 if ((GetKeyState(VK_SHIFT) & 0x8000) != 0)
3825 modifiers |= MOUSE_SHIFT;
3826 if ((GetKeyState(VK_CONTROL) & 0x8000) != 0)
3827 modifiers |= MOUSE_CTRL;
3828 if ((GetKeyState(VK_MENU) & 0x8000) != 0)
3829 modifiers |= MOUSE_ALT;
3830
3831 gui_handle_drop(pt.x, pt.y, modifiers, fnames, cFiles);
3832
3833 s_need_activate = TRUE;
3834 }
3835#endif
3836}
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
4232#ifdef FEAT_BEVAL
4233# 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
4311#endif /* defined(FEAT_BEVAL) */
4312
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);
4386static int nCopyAnsiToWideChar(LPWORD, LPSTR);
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#ifdef FEAT_WINDOWS
5462 DragAcceptFiles(s_hwnd, TRUE);
5463#endif
5464
5465 /* Do we need to bother with this? */
5466 /* m_fMouseAvail = GetSystemMetrics(SM_MOUSEPRESENT); */
5467
5468 /* Get background/foreground colors from the system */
5469 gui_mch_def_colors();
5470
5471 /* Get the colors from the "Normal" group (set in syntax.c or in a vimrc
5472 * file) */
5473 set_normal_colors();
5474
5475 /*
5476 * Check that none of the colors are the same as the background color.
5477 * Then store the current values as the defaults.
5478 */
5479 gui_check_colors();
5480 gui.def_norm_pixel = gui.norm_pixel;
5481 gui.def_back_pixel = gui.back_pixel;
5482
5483 /* Get the colors for the highlight groups (gui_check_colors() might have
5484 * changed them) */
5485 highlight_gui_started();
5486
5487 /*
Bram Moolenaar97b0b0e2015-11-19 20:23:37 +01005488 * Start out by adding the configured border width into the border offset.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005489 */
Bram Moolenaar97b0b0e2015-11-19 20:23:37 +01005490 gui.border_offset = gui.border_width;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005491
5492 /*
5493 * Set up for Intellimouse processing
5494 */
5495 init_mouse_wheel();
5496
5497 /*
5498 * compute a couple of metrics used for the dialogs
5499 */
5500 get_dialog_font_metrics();
5501#ifdef FEAT_TOOLBAR
5502 /*
5503 * Create the toolbar
5504 */
5505 initialise_toolbar();
5506#endif
Bram Moolenaar3991dab2006-03-27 17:01:56 +00005507#ifdef FEAT_GUI_TABLINE
5508 /*
5509 * Create the tabline
5510 */
5511 initialise_tabline();
5512#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005513#ifdef MSWIN_FIND_REPLACE
5514 /*
5515 * Initialise the dialog box stuff
5516 */
5517 s_findrep_msg = RegisterWindowMessage(FINDMSGSTRING);
5518
5519 /* Initialise the struct */
5520 s_findrep_struct.lStructSize = sizeof(s_findrep_struct);
Bram Moolenaar418f81b2016-02-16 20:12:02 +01005521 s_findrep_struct.lpstrFindWhat = (LPSTR)alloc(MSWIN_FR_BUFSIZE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005522 s_findrep_struct.lpstrFindWhat[0] = NUL;
Bram Moolenaar418f81b2016-02-16 20:12:02 +01005523 s_findrep_struct.lpstrReplaceWith = (LPSTR)alloc(MSWIN_FR_BUFSIZE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005524 s_findrep_struct.lpstrReplaceWith[0] = NUL;
5525 s_findrep_struct.wFindWhatLen = MSWIN_FR_BUFSIZE;
5526 s_findrep_struct.wReplaceWithLen = MSWIN_FR_BUFSIZE;
Bram Moolenaarcea912a2016-10-12 14:20:24 +02005527# ifdef FEAT_MBYTE
Bram Moolenaar3ca9a8a2009-01-28 20:23:17 +00005528 s_findrep_struct_w.lStructSize = sizeof(s_findrep_struct_w);
5529 s_findrep_struct_w.lpstrFindWhat =
5530 (LPWSTR)alloc(MSWIN_FR_BUFSIZE * sizeof(WCHAR));
5531 s_findrep_struct_w.lpstrFindWhat[0] = NUL;
5532 s_findrep_struct_w.lpstrReplaceWith =
5533 (LPWSTR)alloc(MSWIN_FR_BUFSIZE * sizeof(WCHAR));
5534 s_findrep_struct_w.lpstrReplaceWith[0] = NUL;
5535 s_findrep_struct_w.wFindWhatLen = MSWIN_FR_BUFSIZE;
5536 s_findrep_struct_w.wReplaceWithLen = MSWIN_FR_BUFSIZE;
5537# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005538#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005539
Bram Moolenaar264e9fd2010-10-27 12:33:17 +02005540#ifdef FEAT_EVAL
Bram Moolenaarf32c5cd2016-01-10 16:07:44 +01005541# if !defined(_MSC_VER) || (_MSC_VER < 1400)
5542/* Define HandleToLong for old MS and non-MS compilers if not defined. */
5543# ifndef HandleToLong
Bram Moolenaara87e2c22016-02-17 20:48:19 +01005544# define HandleToLong(h) ((long)(intptr_t)(h))
Bram Moolenaarf32c5cd2016-01-10 16:07:44 +01005545# endif
Bram Moolenaar4da95d32011-07-07 17:43:41 +02005546# endif
Bram Moolenaar264e9fd2010-10-27 12:33:17 +02005547 /* set the v:windowid variable */
Bram Moolenaar7154b322011-05-25 21:18:06 +02005548 set_vim_var_nr(VV_WINDOWID, HandleToLong(s_hwnd));
Bram Moolenaar264e9fd2010-10-27 12:33:17 +02005549#endif
5550
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02005551#ifdef FEAT_RENDER_OPTIONS
5552 if (p_rop)
5553 (void)gui_mch_set_rendering_options(p_rop);
5554#endif
5555
Bram Moolenaar748bf032005-02-02 23:04:36 +00005556theend:
5557 /* Display any pending error messages */
5558 display_errors();
5559
Bram Moolenaar071d4272004-06-13 20:20:40 +00005560 return OK;
5561}
5562
5563/*
5564 * Get the size of the screen, taking position on multiple monitors into
5565 * account (if supported).
5566 */
5567 static void
5568get_work_area(RECT *spi_rect)
5569{
Bram Moolenaarcea912a2016-10-12 14:20:24 +02005570 HMONITOR mon;
5571 MONITORINFO moninfo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005572
Bram Moolenaarcea912a2016-10-12 14:20:24 +02005573 /* work out which monitor the window is on, and get *it's* work area */
Bram Moolenaar87f3d202016-12-01 20:18:50 +01005574 mon = MonitorFromWindow(s_hwnd, MONITOR_DEFAULTTOPRIMARY);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02005575 if (mon != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005576 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +02005577 moninfo.cbSize = sizeof(MONITORINFO);
5578 if (GetMonitorInfo(mon, &moninfo))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005579 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +02005580 *spi_rect = moninfo.rcWork;
5581 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005582 }
5583 }
5584 /* this is the old method... */
5585 SystemParametersInfo(SPI_GETWORKAREA, 0, spi_rect, 0);
5586}
5587
5588/*
5589 * Set the size of the window to the given width and height in pixels.
5590 */
5591 void
Bram Moolenaar1266d672017-02-01 13:43:36 +01005592gui_mch_set_shellsize(
5593 int width,
5594 int height,
5595 int min_width UNUSED,
5596 int min_height UNUSED,
5597 int base_width UNUSED,
5598 int base_height UNUSED,
Bram Moolenaarafa24992006-03-27 20:58:26 +00005599 int direction)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005600{
5601 RECT workarea_rect;
5602 int win_width, win_height;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005603 WINDOWPLACEMENT wndpl;
5604
Bram Moolenaar910f66f2006-04-05 20:41:53 +00005605 /* Try to keep window completely on screen. */
5606 /* Get position of the screen work area. This is the part that is not
5607 * used by the taskbar or appbars. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005608 get_work_area(&workarea_rect);
5609
Bram Moolenaar84a05ac2013-05-06 04:24:17 +02005610 /* Get current position of our window. Note that the .left and .top are
Bram Moolenaar910f66f2006-04-05 20:41:53 +00005611 * relative to the work area. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005612 wndpl.length = sizeof(WINDOWPLACEMENT);
5613 GetWindowPlacement(s_hwnd, &wndpl);
5614
5615 /* Resizing a maximized window looks very strange, unzoom it first.
5616 * But don't do it when still starting up, it may have been requested in
5617 * the shortcut. */
5618 if (wndpl.showCmd == SW_SHOWMAXIMIZED && starting == 0)
5619 {
5620 ShowWindow(s_hwnd, SW_SHOWNORMAL);
5621 /* Need to get the settings of the normal window. */
5622 GetWindowPlacement(s_hwnd, &wndpl);
5623 }
5624
Bram Moolenaar071d4272004-06-13 20:20:40 +00005625 /* compute the size of the outside of the window */
Bram Moolenaar9d488952013-07-21 17:53:58 +02005626 win_width = width + (GetSystemMetrics(SM_CXFRAME) +
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02005627 GetSystemMetrics(SM_CXPADDEDBORDER)) * 2;
Bram Moolenaar9d488952013-07-21 17:53:58 +02005628 win_height = height + (GetSystemMetrics(SM_CYFRAME) +
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02005629 GetSystemMetrics(SM_CXPADDEDBORDER)) * 2
Bram Moolenaar071d4272004-06-13 20:20:40 +00005630 + GetSystemMetrics(SM_CYCAPTION)
5631#ifdef FEAT_MENU
5632 + gui_mswin_get_menu_height(FALSE)
5633#endif
5634 ;
5635
Bram Moolenaar6ef47c22012-01-04 20:29:22 +01005636 /* The following should take care of keeping Vim on the same monitor, no
5637 * matter if the secondary monitor is left or right of the primary
5638 * monitor. */
5639 wndpl.rcNormalPosition.right = wndpl.rcNormalPosition.left + win_width;
5640 wndpl.rcNormalPosition.bottom = wndpl.rcNormalPosition.top + win_height;
Bram Moolenaar56a907a2006-05-06 21:44:30 +00005641
Bram Moolenaar6ef47c22012-01-04 20:29:22 +01005642 /* If the window is going off the screen, move it on to the screen. */
Bram Moolenaar910f66f2006-04-05 20:41:53 +00005643 if ((direction & RESIZE_HOR)
Bram Moolenaar6ef47c22012-01-04 20:29:22 +01005644 && wndpl.rcNormalPosition.right > workarea_rect.right)
5645 OffsetRect(&wndpl.rcNormalPosition,
5646 workarea_rect.right - wndpl.rcNormalPosition.right, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005647
Bram Moolenaar6ef47c22012-01-04 20:29:22 +01005648 if ((direction & RESIZE_HOR)
5649 && wndpl.rcNormalPosition.left < workarea_rect.left)
5650 OffsetRect(&wndpl.rcNormalPosition,
5651 workarea_rect.left - wndpl.rcNormalPosition.left, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005652
Bram Moolenaarafa24992006-03-27 20:58:26 +00005653 if ((direction & RESIZE_VERT)
Bram Moolenaar6ef47c22012-01-04 20:29:22 +01005654 && wndpl.rcNormalPosition.bottom > workarea_rect.bottom)
5655 OffsetRect(&wndpl.rcNormalPosition,
5656 0, workarea_rect.bottom - wndpl.rcNormalPosition.bottom);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005657
Bram Moolenaar6ef47c22012-01-04 20:29:22 +01005658 if ((direction & RESIZE_VERT)
5659 && wndpl.rcNormalPosition.top < workarea_rect.top)
5660 OffsetRect(&wndpl.rcNormalPosition,
5661 0, workarea_rect.top - wndpl.rcNormalPosition.top);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005662
5663 /* set window position - we should use SetWindowPlacement rather than
5664 * SetWindowPos as the MSDN docs say the coord systems returned by
5665 * these two are not compatible. */
5666 SetWindowPlacement(s_hwnd, &wndpl);
5667
5668 SetActiveWindow(s_hwnd);
5669 SetFocus(s_hwnd);
5670
5671#ifdef FEAT_MENU
5672 /* Menu may wrap differently now */
5673 gui_mswin_get_menu_height(!gui.starting);
5674#endif
5675}
5676
5677
5678 void
5679gui_mch_set_scrollbar_thumb(
5680 scrollbar_T *sb,
5681 long val,
5682 long size,
5683 long max)
5684{
5685 SCROLLINFO info;
5686
5687 sb->scroll_shift = 0;
5688 while (max > 32767)
5689 {
5690 max = (max + 1) >> 1;
5691 val >>= 1;
5692 size >>= 1;
5693 ++sb->scroll_shift;
5694 }
5695
5696 if (sb->scroll_shift > 0)
5697 ++size;
5698
5699 info.cbSize = sizeof(info);
5700 info.fMask = SIF_POS | SIF_RANGE | SIF_PAGE;
5701 info.nPos = val;
5702 info.nMin = 0;
5703 info.nMax = max;
5704 info.nPage = size;
5705 SetScrollInfo(sb->id, SB_CTL, &info, TRUE);
5706}
5707
5708
5709/*
5710 * Set the current text font.
5711 */
5712 void
5713gui_mch_set_font(GuiFont font)
5714{
5715 gui.currFont = font;
5716}
5717
5718
5719/*
5720 * Set the current text foreground color.
5721 */
5722 void
5723gui_mch_set_fg_color(guicolor_T color)
5724{
5725 gui.currFgColor = color;
5726}
5727
5728/*
5729 * Set the current text background color.
5730 */
5731 void
5732gui_mch_set_bg_color(guicolor_T color)
5733{
5734 gui.currBgColor = color;
5735}
5736
Bram Moolenaare2cc9702005-03-15 22:43:58 +00005737/*
5738 * Set the current text special color.
5739 */
5740 void
5741gui_mch_set_sp_color(guicolor_T color)
5742{
5743 gui.currSpColor = color;
5744}
5745
Bram Moolenaar071d4272004-06-13 20:20:40 +00005746#if defined(FEAT_MBYTE) && defined(FEAT_MBYTE_IME)
5747/*
5748 * Multi-byte handling, originally by Sung-Hoon Baek.
5749 * First static functions (no prototypes generated).
5750 */
5751#ifdef _MSC_VER
5752# include <ime.h> /* Apparently not needed for Cygwin, MingW or Borland. */
5753#endif
5754#include <imm.h>
5755
5756/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00005757 * handle WM_IME_NOTIFY message
5758 */
5759 static LRESULT
Bram Moolenaar1266d672017-02-01 13:43:36 +01005760_OnImeNotify(HWND hWnd, DWORD dwCommand, DWORD dwData UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005761{
5762 LRESULT lResult = 0;
5763 HIMC hImc;
5764
5765 if (!pImmGetContext || (hImc = pImmGetContext(hWnd)) == (HIMC)0)
5766 return lResult;
5767 switch (dwCommand)
5768 {
5769 case IMN_SETOPENSTATUS:
5770 if (pImmGetOpenStatus(hImc))
5771 {
5772 pImmSetCompositionFont(hImc, &norm_logfont);
5773 im_set_position(gui.row, gui.col);
5774
5775 /* Disable langmap */
5776 State &= ~LANGMAP;
5777 if (State & INSERT)
5778 {
5779#if defined(FEAT_WINDOWS) && defined(FEAT_KEYMAP)
5780 /* Unshown 'keymap' in status lines */
5781 if (curbuf->b_p_iminsert == B_IMODE_LMAP)
5782 {
5783 /* Save cursor position */
5784 int old_row = gui.row;
5785 int old_col = gui.col;
5786
5787 // This must be called here before
5788 // status_redraw_curbuf(), otherwise the mode
5789 // message may appear in the wrong position.
5790 showmode();
5791 status_redraw_curbuf();
5792 update_screen(0);
5793 /* Restore cursor position */
5794 gui.row = old_row;
5795 gui.col = old_col;
5796 }
5797#endif
5798 }
5799 }
5800 gui_update_cursor(TRUE, FALSE);
5801 lResult = 0;
5802 break;
5803 }
5804 pImmReleaseContext(hWnd, hImc);
5805 return lResult;
5806}
5807
5808 static LRESULT
Bram Moolenaar1266d672017-02-01 13:43:36 +01005809_OnImeComposition(HWND hwnd, WPARAM dbcs UNUSED, LPARAM param)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005810{
5811 char_u *ret;
5812 int len;
5813
5814 if ((param & GCS_RESULTSTR) == 0) /* Composition unfinished. */
5815 return 0;
5816
5817 ret = GetResultStr(hwnd, GCS_RESULTSTR, &len);
5818 if (ret != NULL)
5819 {
5820 add_to_input_buf_csi(ret, len);
5821 vim_free(ret);
5822 return 1;
5823 }
5824 return 0;
5825}
5826
5827/*
5828 * get the current composition string, in UCS-2; *lenp is the number of
5829 * *lenp is the number of Unicode characters.
5830 */
5831 static short_u *
5832GetCompositionString_inUCS2(HIMC hIMC, DWORD GCS, int *lenp)
5833{
5834 LONG ret;
5835 LPWSTR wbuf = NULL;
5836 char_u *buf;
5837
5838 if (!pImmGetContext)
5839 return NULL; /* no imm32.dll */
5840
5841 /* Try Unicode; this'll always work on NT regardless of codepage. */
5842 ret = pImmGetCompositionStringW(hIMC, GCS, NULL, 0);
5843 if (ret == 0)
5844 return NULL; /* empty */
5845
5846 if (ret > 0)
5847 {
5848 /* Allocate the requested buffer plus space for the NUL character. */
5849 wbuf = (LPWSTR)alloc(ret + sizeof(WCHAR));
5850 if (wbuf != NULL)
5851 {
5852 pImmGetCompositionStringW(hIMC, GCS, wbuf, ret);
5853 *lenp = ret / sizeof(WCHAR);
5854 }
5855 return (short_u *)wbuf;
5856 }
5857
5858 /* ret < 0; we got an error, so try the ANSI version. This'll work
5859 * on 9x/ME, but only if the codepage happens to be set to whatever
5860 * we're inputting. */
5861 ret = pImmGetCompositionStringA(hIMC, GCS, NULL, 0);
5862 if (ret <= 0)
5863 return NULL; /* empty or error */
5864
5865 buf = alloc(ret);
5866 if (buf == NULL)
5867 return NULL;
5868 pImmGetCompositionStringA(hIMC, GCS, buf, ret);
5869
5870 /* convert from codepage to UCS-2 */
Bram Moolenaar418f81b2016-02-16 20:12:02 +01005871 MultiByteToWideChar_alloc(GetACP(), 0, (LPCSTR)buf, ret, &wbuf, lenp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005872 vim_free(buf);
5873
5874 return (short_u *)wbuf;
5875}
5876
5877/*
5878 * void GetResultStr()
5879 *
5880 * This handles WM_IME_COMPOSITION with GCS_RESULTSTR flag on.
5881 * get complete composition string
5882 */
5883 static char_u *
5884GetResultStr(HWND hwnd, int GCS, int *lenp)
5885{
5886 HIMC hIMC; /* Input context handle. */
5887 short_u *buf = NULL;
5888 char_u *convbuf = NULL;
5889
5890 if (!pImmGetContext || (hIMC = pImmGetContext(hwnd)) == (HIMC)0)
5891 return NULL;
5892
5893 /* Reads in the composition string. */
5894 buf = GetCompositionString_inUCS2(hIMC, GCS, lenp);
5895 if (buf == NULL)
5896 return NULL;
5897
Bram Moolenaar36f692d2008-11-20 16:10:17 +00005898 convbuf = utf16_to_enc(buf, lenp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005899 pImmReleaseContext(hwnd, hIMC);
5900 vim_free(buf);
5901 return convbuf;
5902}
5903#endif
5904
5905/* For global functions we need prototypes. */
5906#if (defined(FEAT_MBYTE) && defined(FEAT_MBYTE_IME)) || defined(PROTO)
5907
5908/*
5909 * set font to IM.
5910 */
5911 void
5912im_set_font(LOGFONT *lf)
5913{
5914 HIMC hImc;
5915
5916 if (pImmGetContext && (hImc = pImmGetContext(s_hwnd)) != (HIMC)0)
5917 {
5918 pImmSetCompositionFont(hImc, lf);
5919 pImmReleaseContext(s_hwnd, hImc);
5920 }
5921}
5922
5923/*
5924 * Notify cursor position to IM.
5925 */
5926 void
5927im_set_position(int row, int col)
5928{
5929 HIMC hImc;
5930
5931 if (pImmGetContext && (hImc = pImmGetContext(s_hwnd)) != (HIMC)0)
5932 {
5933 COMPOSITIONFORM cfs;
5934
5935 cfs.dwStyle = CFS_POINT;
5936 cfs.ptCurrentPos.x = FILL_X(col);
5937 cfs.ptCurrentPos.y = FILL_Y(row);
5938 MapWindowPoints(s_textArea, s_hwnd, &cfs.ptCurrentPos, 1);
5939 pImmSetCompositionWindow(hImc, &cfs);
5940
5941 pImmReleaseContext(s_hwnd, hImc);
5942 }
5943}
5944
5945/*
5946 * Set IM status on ("active" is TRUE) or off ("active" is FALSE).
5947 */
5948 void
5949im_set_active(int active)
5950{
5951 HIMC hImc;
5952 static HIMC hImcOld = (HIMC)0;
5953
5954 if (pImmGetContext) /* if NULL imm32.dll wasn't loaded (yet) */
5955 {
5956 if (p_imdisable)
5957 {
5958 if (hImcOld == (HIMC)0)
5959 {
5960 hImcOld = pImmGetContext(s_hwnd);
5961 if (hImcOld)
5962 pImmAssociateContext(s_hwnd, (HIMC)0);
5963 }
5964 active = FALSE;
5965 }
5966 else if (hImcOld != (HIMC)0)
5967 {
5968 pImmAssociateContext(s_hwnd, hImcOld);
5969 hImcOld = (HIMC)0;
5970 }
5971
5972 hImc = pImmGetContext(s_hwnd);
5973 if (hImc)
5974 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00005975 /*
5976 * for Korean ime
5977 */
5978 HKL hKL = GetKeyboardLayout(0);
5979
5980 if (LOWORD(hKL) == MAKELANGID(LANG_KOREAN, SUBLANG_KOREAN))
5981 {
5982 static DWORD dwConversionSaved = 0, dwSentenceSaved = 0;
5983 static BOOL bSaved = FALSE;
5984
5985 if (active)
5986 {
5987 /* if we have a saved conversion status, restore it */
5988 if (bSaved)
5989 pImmSetConversionStatus(hImc, dwConversionSaved,
5990 dwSentenceSaved);
5991 bSaved = FALSE;
5992 }
5993 else
5994 {
5995 /* save conversion status and disable korean */
5996 if (pImmGetConversionStatus(hImc, &dwConversionSaved,
5997 &dwSentenceSaved))
5998 {
5999 bSaved = TRUE;
6000 pImmSetConversionStatus(hImc,
6001 dwConversionSaved & ~(IME_CMODE_NATIVE
6002 | IME_CMODE_FULLSHAPE),
6003 dwSentenceSaved);
6004 }
6005 }
6006 }
6007
Bram Moolenaar071d4272004-06-13 20:20:40 +00006008 pImmSetOpenStatus(hImc, active);
6009 pImmReleaseContext(s_hwnd, hImc);
6010 }
6011 }
6012}
6013
6014/*
6015 * Get IM status. When IM is on, return not 0. Else return 0.
6016 */
6017 int
Bram Moolenaar68c2f632016-01-30 17:24:07 +01006018im_get_status(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006019{
6020 int status = 0;
6021 HIMC hImc;
6022
6023 if (pImmGetContext && (hImc = pImmGetContext(s_hwnd)) != (HIMC)0)
6024 {
6025 status = pImmGetOpenStatus(hImc) ? 1 : 0;
6026 pImmReleaseContext(s_hwnd, hImc);
6027 }
6028 return status;
6029}
6030
6031#endif /* FEAT_MBYTE && FEAT_MBYTE_IME */
6032
6033#if defined(FEAT_MBYTE) && !defined(FEAT_MBYTE_IME) && defined(GLOBAL_IME)
6034/* Win32 with GLOBAL IME */
6035
6036/*
6037 * Notify cursor position to IM.
6038 */
6039 void
6040im_set_position(int row, int col)
6041{
6042 /* Win32 with GLOBAL IME */
6043 POINT p;
6044
6045 p.x = FILL_X(col);
6046 p.y = FILL_Y(row);
6047 MapWindowPoints(s_textArea, s_hwnd, &p, 1);
6048 global_ime_set_position(&p);
6049}
6050
6051/*
6052 * Set IM status on ("active" is TRUE) or off ("active" is FALSE).
6053 */
6054 void
6055im_set_active(int active)
6056{
6057 global_ime_set_status(active);
6058}
6059
6060/*
6061 * Get IM status. When IM is on, return not 0. Else return 0.
6062 */
6063 int
Bram Moolenaard14e00e2016-01-31 17:30:51 +01006064im_get_status(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006065{
6066 return global_ime_get_status();
6067}
6068#endif
6069
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006070#ifdef FEAT_MBYTE
6071/*
Bram Moolenaar39f05632006-03-19 22:15:26 +00006072 * Convert latin9 text "text[len]" to ucs-2 in "unicodebuf".
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006073 */
6074 static void
6075latin9_to_ucs(char_u *text, int len, WCHAR *unicodebuf)
6076{
6077 int c;
6078
Bram Moolenaarca003e12006-03-17 23:19:38 +00006079 while (--len >= 0)
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006080 {
6081 c = *text++;
6082 switch (c)
6083 {
6084 case 0xa4: c = 0x20ac; break; /* euro */
6085 case 0xa6: c = 0x0160; break; /* S hat */
6086 case 0xa8: c = 0x0161; break; /* S -hat */
6087 case 0xb4: c = 0x017d; break; /* Z hat */
6088 case 0xb8: c = 0x017e; break; /* Z -hat */
6089 case 0xbc: c = 0x0152; break; /* OE */
6090 case 0xbd: c = 0x0153; break; /* oe */
6091 case 0xbe: c = 0x0178; break; /* Y */
6092 }
6093 *unicodebuf++ = c;
6094 }
6095}
6096#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006097
6098#ifdef FEAT_RIGHTLEFT
6099/*
6100 * What is this for? In the case where you are using Win98 or Win2K or later,
6101 * and you are using a Hebrew font (or Arabic!), Windows does you a favor and
6102 * reverses the string sent to the TextOut... family. This sucks, because we
6103 * go to a lot of effort to do the right thing, and there doesn't seem to be a
6104 * way to tell Windblows not to do this!
6105 *
6106 * The short of it is that this 'RevOut' only gets called if you are running
6107 * one of the new, "improved" MS OSes, and only if you are running in
6108 * 'rightleft' mode. It makes display take *slightly* longer, but not
6109 * noticeably so.
6110 */
6111 static void
6112RevOut( HDC s_hdc,
6113 int col,
6114 int row,
6115 UINT foptions,
6116 CONST RECT *pcliprect,
6117 LPCTSTR text,
6118 UINT len,
6119 CONST INT *padding)
6120{
6121 int ix;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006122
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006123 for (ix = 0; ix < (int)len; ++ix)
6124 ExtTextOut(s_hdc, col + TEXT_X(ix), row, foptions,
6125 pcliprect, text + ix, 1, padding);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006126}
6127#endif
6128
6129 void
6130gui_mch_draw_string(
6131 int row,
6132 int col,
6133 char_u *text,
6134 int len,
6135 int flags)
6136{
6137 static int *padding = NULL;
6138 static int pad_size = 0;
6139 int i;
6140 const RECT *pcliprect = NULL;
6141 UINT foptions = 0;
6142#ifdef FEAT_MBYTE
6143 static WCHAR *unicodebuf = NULL;
6144 static int *unicodepdy = NULL;
Bram Moolenaar402d2fe2005-04-15 21:00:38 +00006145 static int unibuflen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006146 int n = 0;
6147#endif
6148 HPEN hpen, old_pen;
6149 int y;
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02006150#ifdef FEAT_DIRECTX
6151 int font_is_ttf_or_vector = 0;
6152#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006153
Bram Moolenaar071d4272004-06-13 20:20:40 +00006154 /*
6155 * Italic and bold text seems to have an extra row of pixels at the bottom
6156 * (below where the bottom of the character should be). If we draw the
6157 * characters with a solid background, the top row of pixels in the
6158 * character below will be overwritten. We can fix this by filling in the
6159 * background ourselves, to the correct character proportions, and then
6160 * writing the character in transparent mode. Still have a problem when
6161 * the character is "_", which gets written on to the character below.
6162 * New fix: set gui.char_ascent to -1. This shifts all characters up one
6163 * pixel in their slots, which fixes the problem with the bottom row of
6164 * pixels. We still need this code because otherwise the top row of pixels
6165 * becomes a problem. - webb.
6166 */
6167 static HBRUSH hbr_cache[2] = {NULL, NULL};
6168 static guicolor_T brush_color[2] = {INVALCOLOR, INVALCOLOR};
6169 static int brush_lru = 0;
6170 HBRUSH hbr;
6171 RECT rc;
6172
6173 if (!(flags & DRAW_TRANSP))
6174 {
6175 /*
6176 * Clear background first.
6177 * Note: FillRect() excludes right and bottom of rectangle.
6178 */
6179 rc.left = FILL_X(col);
6180 rc.top = FILL_Y(row);
6181#ifdef FEAT_MBYTE
6182 if (has_mbyte)
6183 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006184 /* Compute the length in display cells. */
Bram Moolenaar72597a52010-07-18 15:31:08 +02006185 rc.right = FILL_X(col + mb_string2cells(text, len));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006186 }
6187 else
6188#endif
6189 rc.right = FILL_X(col + len);
6190 rc.bottom = FILL_Y(row + 1);
6191
6192 /* Cache the created brush, that saves a lot of time. We need two:
6193 * one for cursor background and one for the normal background. */
6194 if (gui.currBgColor == brush_color[0])
6195 {
6196 hbr = hbr_cache[0];
6197 brush_lru = 1;
6198 }
6199 else if (gui.currBgColor == brush_color[1])
6200 {
6201 hbr = hbr_cache[1];
6202 brush_lru = 0;
6203 }
6204 else
6205 {
6206 if (hbr_cache[brush_lru] != NULL)
6207 DeleteBrush(hbr_cache[brush_lru]);
6208 hbr_cache[brush_lru] = CreateSolidBrush(gui.currBgColor);
6209 brush_color[brush_lru] = gui.currBgColor;
6210 hbr = hbr_cache[brush_lru];
6211 brush_lru = !brush_lru;
6212 }
6213 FillRect(s_hdc, &rc, hbr);
6214
6215 SetBkMode(s_hdc, TRANSPARENT);
6216
6217 /*
6218 * When drawing block cursor, prevent inverted character spilling
6219 * over character cell (can happen with bold/italic)
6220 */
6221 if (flags & DRAW_CURSOR)
6222 {
6223 pcliprect = &rc;
6224 foptions = ETO_CLIPPED;
6225 }
6226 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006227 SetTextColor(s_hdc, gui.currFgColor);
6228 SelectFont(s_hdc, gui.currFont);
6229
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02006230#ifdef FEAT_DIRECTX
6231 if (IS_ENABLE_DIRECTX())
6232 {
6233 TEXTMETRIC tm;
6234
6235 GetTextMetrics(s_hdc, &tm);
6236 if (tm.tmPitchAndFamily & (TMPF_TRUETYPE | TMPF_VECTOR))
6237 {
6238 font_is_ttf_or_vector = 1;
6239 DWriteContext_SetFont(s_dwc, (HFONT)gui.currFont);
6240 }
6241 }
6242#endif
6243
Bram Moolenaar071d4272004-06-13 20:20:40 +00006244 if (pad_size != Columns || padding == NULL || padding[0] != gui.char_width)
6245 {
6246 vim_free(padding);
6247 pad_size = Columns;
6248
Bram Moolenaarc54b8a72005-09-30 21:20:29 +00006249 /* Don't give an out-of-memory message here, it would call us
6250 * recursively. */
6251 padding = (int *)lalloc(pad_size * sizeof(int), FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006252 if (padding != NULL)
6253 for (i = 0; i < pad_size; i++)
6254 padding[i] = gui.char_width;
6255 }
6256
Bram Moolenaar071d4272004-06-13 20:20:40 +00006257 /*
6258 * We have to provide the padding argument because italic and bold versions
6259 * of fixed-width fonts are often one pixel or so wider than their normal
6260 * versions.
6261 * No check for DRAW_BOLD, Windows will have done it already.
6262 */
6263
6264#ifdef FEAT_MBYTE
6265 /* Check if there are any UTF-8 characters. If not, use normal text
6266 * output to speed up output. */
6267 if (enc_utf8)
6268 for (n = 0; n < len; ++n)
6269 if (text[n] >= 0x80)
6270 break;
6271
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02006272#if defined(FEAT_DIRECTX)
6273 /* Quick hack to enable DirectWrite. To use DirectWrite (antialias), it is
6274 * required that unicode drawing routine, currently. So this forces it
6275 * enabled. */
6276 if (enc_utf8 && IS_ENABLE_DIRECTX())
6277 n = 0; /* Keep n < len, to enter block for unicode. */
6278#endif
6279
Bram Moolenaar071d4272004-06-13 20:20:40 +00006280 /* Check if the Unicode buffer exists and is big enough. Create it
Bram Moolenaar402d2fe2005-04-15 21:00:38 +00006281 * with the same length as the multi-byte string, the number of wide
Bram Moolenaar071d4272004-06-13 20:20:40 +00006282 * characters is always equal or smaller. */
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006283 if ((enc_utf8
6284 || (enc_codepage > 0 && (int)GetACP() != enc_codepage)
6285 || enc_latin9)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006286 && (unicodebuf == NULL || len > unibuflen))
6287 {
6288 vim_free(unicodebuf);
Bram Moolenaarc54b8a72005-09-30 21:20:29 +00006289 unicodebuf = (WCHAR *)lalloc(len * sizeof(WCHAR), FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006290
6291 vim_free(unicodepdy);
Bram Moolenaarc54b8a72005-09-30 21:20:29 +00006292 unicodepdy = (int *)lalloc(len * sizeof(int), FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006293
6294 unibuflen = len;
6295 }
6296
6297 if (enc_utf8 && n < len && unicodebuf != NULL)
6298 {
6299 /* Output UTF-8 characters. Caller has already separated
6300 * composing characters. */
Bram Moolenaarca003e12006-03-17 23:19:38 +00006301 int i;
6302 int wlen; /* string length in words */
6303 int clen; /* string length in characters */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006304 int cells; /* cell width of string up to composing char */
6305 int cw; /* width of current cell */
Bram Moolenaar362e1a32006-03-06 23:29:24 +00006306 int c;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006307
Bram Moolenaar97b2ad32006-03-18 21:40:56 +00006308 wlen = 0;
Bram Moolenaarca003e12006-03-17 23:19:38 +00006309 clen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006310 cells = 0;
Bram Moolenaarca003e12006-03-17 23:19:38 +00006311 for (i = 0; i < len; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00006312 {
Bram Moolenaar362e1a32006-03-06 23:29:24 +00006313 c = utf_ptr2char(text + i);
6314 if (c >= 0x10000)
6315 {
6316 /* Turn into UTF-16 encoding. */
Bram Moolenaarca003e12006-03-17 23:19:38 +00006317 unicodebuf[wlen++] = ((c - 0x10000) >> 10) + 0xD800;
6318 unicodebuf[wlen++] = ((c - 0x10000) & 0x3ff) + 0xDC00;
Bram Moolenaar362e1a32006-03-06 23:29:24 +00006319 }
6320 else
6321 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00006322 unicodebuf[wlen++] = c;
Bram Moolenaar362e1a32006-03-06 23:29:24 +00006323 }
6324 cw = utf_char2cells(c);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006325 if (cw > 2) /* don't use 4 for unprintable char */
6326 cw = 1;
6327 if (unicodepdy != NULL)
6328 {
6329 /* Use unicodepdy to make characters fit as we expect, even
6330 * when the font uses different widths (e.g., bold character
6331 * is wider). */
Bram Moolenaard804fdf2016-02-27 16:04:58 +01006332 if (c >= 0x10000)
6333 {
6334 unicodepdy[wlen - 2] = cw * gui.char_width;
6335 unicodepdy[wlen - 1] = 0;
6336 }
6337 else
6338 unicodepdy[wlen - 1] = cw * gui.char_width;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006339 }
6340 cells += cw;
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00006341 i += utfc_ptr2len_len(text + i, len - i);
Bram Moolenaarca003e12006-03-17 23:19:38 +00006342 ++clen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006343 }
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02006344#if defined(FEAT_DIRECTX)
6345 if (IS_ENABLE_DIRECTX() && font_is_ttf_or_vector)
6346 {
Bram Moolenaar9b352c42014-08-06 16:49:55 +02006347 /* Add one to "cells" for italics. */
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02006348 DWriteContext_DrawText(s_dwc, s_hdc, unicodebuf, wlen,
Bram Moolenaar9b352c42014-08-06 16:49:55 +02006349 TEXT_X(col), TEXT_Y(row), FILL_X(cells + 1), FILL_Y(1),
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02006350 gui.char_width, gui.currFgColor);
6351 }
6352 else
6353#endif
6354 ExtTextOutW(s_hdc, TEXT_X(col), TEXT_Y(row),
6355 foptions, pcliprect, unicodebuf, wlen, unicodepdy);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006356 len = cells; /* used for underlining */
6357 }
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006358 else if ((enc_codepage > 0 && (int)GetACP() != enc_codepage) || enc_latin9)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006359 {
6360 /* If we want to display codepage data, and the current CP is not the
6361 * ANSI one, we need to go via Unicode. */
6362 if (unicodebuf != NULL)
6363 {
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006364 if (enc_latin9)
6365 latin9_to_ucs(text, len, unicodebuf);
6366 else
6367 len = MultiByteToWideChar(enc_codepage,
Bram Moolenaar071d4272004-06-13 20:20:40 +00006368 MB_PRECOMPOSED,
6369 (char *)text, len,
6370 (LPWSTR)unicodebuf, unibuflen);
6371 if (len != 0)
Bram Moolenaar19a09a12005-03-04 23:39:37 +00006372 {
6373 /* Use unicodepdy to make characters fit as we expect, even
6374 * when the font uses different widths (e.g., bold character
6375 * is wider). */
6376 if (unicodepdy != NULL)
6377 {
6378 int i;
6379 int cw;
6380
6381 for (i = 0; i < len; ++i)
6382 {
6383 cw = utf_char2cells(unicodebuf[i]);
6384 if (cw > 2)
6385 cw = 1;
6386 unicodepdy[i] = cw * gui.char_width;
6387 }
6388 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006389 ExtTextOutW(s_hdc, TEXT_X(col), TEXT_Y(row),
Bram Moolenaar19a09a12005-03-04 23:39:37 +00006390 foptions, pcliprect, unicodebuf, len, unicodepdy);
6391 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006392 }
6393 }
6394 else
6395#endif
6396 {
6397#ifdef FEAT_RIGHTLEFT
Bram Moolenaar4ee40b02014-09-19 16:13:53 +02006398 /* Windows will mess up RL text, so we have to draw it character by
6399 * character. Only do this if RL is on, since it's slow. */
6400 if (curwin->w_p_rl)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006401 RevOut(s_hdc, TEXT_X(col), TEXT_Y(row),
6402 foptions, pcliprect, (char *)text, len, padding);
6403 else
6404#endif
6405 ExtTextOut(s_hdc, TEXT_X(col), TEXT_Y(row),
6406 foptions, pcliprect, (char *)text, len, padding);
6407 }
6408
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006409 /* Underline */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006410 if (flags & DRAW_UNDERL)
6411 {
6412 hpen = CreatePen(PS_SOLID, 1, gui.currFgColor);
6413 old_pen = SelectObject(s_hdc, hpen);
6414 /* When p_linespace is 0, overwrite the bottom row of pixels.
6415 * Otherwise put the line just below the character. */
6416 y = FILL_Y(row + 1) - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006417 if (p_linespace > 1)
6418 y -= p_linespace - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006419 MoveToEx(s_hdc, FILL_X(col), y, NULL);
6420 /* Note: LineTo() excludes the last pixel in the line. */
6421 LineTo(s_hdc, FILL_X(col + len), y);
6422 DeleteObject(SelectObject(s_hdc, old_pen));
6423 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006424
6425 /* Undercurl */
6426 if (flags & DRAW_UNDERC)
6427 {
6428 int x;
6429 int offset;
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +00006430 static const int val[8] = {1, 0, 0, 0, 1, 2, 2, 2 };
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006431
6432 y = FILL_Y(row + 1) - 1;
6433 for (x = FILL_X(col); x < FILL_X(col + len); ++x)
6434 {
6435 offset = val[x % 8];
6436 SetPixel(s_hdc, x, y - offset, gui.currSpColor);
6437 }
6438 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006439}
6440
6441
6442/*
6443 * Output routines.
6444 */
6445
6446/* Flush any output to the screen */
6447 void
6448gui_mch_flush(void)
6449{
6450# if defined(__BORLANDC__)
6451 /*
6452 * The GdiFlush declaration (in Borland C 5.01 <wingdi.h>) is not a
6453 * prototype declaration.
6454 * The compiler complains if __stdcall is not used in both declarations.
6455 */
6456 BOOL __stdcall GdiFlush(void);
6457# endif
6458
6459 GdiFlush();
6460}
6461
6462 static void
6463clear_rect(RECT *rcp)
6464{
6465 HBRUSH hbr;
6466
6467 hbr = CreateSolidBrush(gui.back_pixel);
6468 FillRect(s_hdc, rcp, hbr);
6469 DeleteBrush(hbr);
6470}
6471
6472
Bram Moolenaarc716c302006-01-21 22:12:51 +00006473 void
6474gui_mch_get_screen_dimensions(int *screen_w, int *screen_h)
6475{
6476 RECT workarea_rect;
6477
6478 get_work_area(&workarea_rect);
6479
Bram Moolenaar910f66f2006-04-05 20:41:53 +00006480 *screen_w = workarea_rect.right - workarea_rect.left
Bram Moolenaar9d488952013-07-21 17:53:58 +02006481 - (GetSystemMetrics(SM_CXFRAME) +
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02006482 GetSystemMetrics(SM_CXPADDEDBORDER)) * 2;
Bram Moolenaarc716c302006-01-21 22:12:51 +00006483
6484 /* FIXME: dirty trick: Because the gui_get_base_height() doesn't include
6485 * the menubar for MSwin, we subtract it from the screen height, so that
6486 * the window size can be made to fit on the screen. */
Bram Moolenaar910f66f2006-04-05 20:41:53 +00006487 *screen_h = workarea_rect.bottom - workarea_rect.top
Bram Moolenaar9d488952013-07-21 17:53:58 +02006488 - (GetSystemMetrics(SM_CYFRAME) +
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02006489 GetSystemMetrics(SM_CXPADDEDBORDER)) * 2
Bram Moolenaarc716c302006-01-21 22:12:51 +00006490 - GetSystemMetrics(SM_CYCAPTION)
6491#ifdef FEAT_MENU
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00006492 - gui_mswin_get_menu_height(FALSE)
Bram Moolenaarc716c302006-01-21 22:12:51 +00006493#endif
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00006494 ;
Bram Moolenaarc716c302006-01-21 22:12:51 +00006495}
6496
6497
Bram Moolenaar071d4272004-06-13 20:20:40 +00006498#if defined(FEAT_MENU) || defined(PROTO)
6499/*
6500 * Add a sub menu to the menu bar.
6501 */
6502 void
6503gui_mch_add_menu(
6504 vimmenu_T *menu,
6505 int pos)
6506{
6507 vimmenu_T *parent = menu->parent;
6508
6509 menu->submenu_id = CreatePopupMenu();
6510 menu->id = s_menu_id++;
6511
6512 if (menu_is_menubar(menu->name))
6513 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006514#ifdef FEAT_MBYTE
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006515 WCHAR *wn = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006516
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006517 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
6518 {
6519 /* 'encoding' differs from active codepage: convert menu name
6520 * and use wide function */
6521 wn = enc_to_utf16(menu->name, NULL);
6522 if (wn != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006523 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006524 MENUITEMINFOW infow;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006525
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006526 infow.cbSize = sizeof(infow);
6527 infow.fMask = MIIM_DATA | MIIM_TYPE | MIIM_ID
6528 | MIIM_SUBMENU;
6529 infow.dwItemData = (long_u)menu;
6530 infow.wID = menu->id;
6531 infow.fType = MFT_STRING;
6532 infow.dwTypeData = wn;
6533 infow.cch = (UINT)wcslen(wn);
6534 infow.hSubMenu = menu->submenu_id;
6535 InsertMenuItemW((parent == NULL)
6536 ? s_menuBar : parent->submenu_id,
6537 (UINT)pos, TRUE, &infow);
6538 vim_free(wn);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006539 }
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006540 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006541
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006542 if (wn == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006543#endif
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006544 {
6545 MENUITEMINFO info;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006546
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006547 info.cbSize = sizeof(info);
6548 info.fMask = MIIM_DATA | MIIM_TYPE | MIIM_ID | MIIM_SUBMENU;
6549 info.dwItemData = (long_u)menu;
6550 info.wID = menu->id;
6551 info.fType = MFT_STRING;
6552 info.dwTypeData = (LPTSTR)menu->name;
6553 info.cch = (UINT)STRLEN(menu->name);
6554 info.hSubMenu = menu->submenu_id;
6555 InsertMenuItem((parent == NULL)
6556 ? s_menuBar : parent->submenu_id,
6557 (UINT)pos, TRUE, &info);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006558 }
6559 }
6560
6561 /* Fix window size if menu may have wrapped */
6562 if (parent == NULL)
6563 gui_mswin_get_menu_height(!gui.starting);
6564#ifdef FEAT_TEAROFF
6565 else if (IsWindow(parent->tearoff_handle))
6566 rebuild_tearoff(parent);
6567#endif
6568}
6569
6570 void
6571gui_mch_show_popupmenu(vimmenu_T *menu)
6572{
6573 POINT mp;
6574
6575 (void)GetCursorPos((LPPOINT)&mp);
6576 gui_mch_show_popupmenu_at(menu, (int)mp.x, (int)mp.y);
6577}
6578
6579 void
Bram Moolenaar045e82d2005-07-08 22:25:33 +00006580gui_make_popup(char_u *path_name, int mouse_pos)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006581{
6582 vimmenu_T *menu = gui_find_menu(path_name);
6583
6584 if (menu != NULL)
6585 {
6586 POINT p;
6587
6588 /* Find the position of the current cursor */
6589 GetDCOrgEx(s_hdc, &p);
Bram Moolenaar045e82d2005-07-08 22:25:33 +00006590 if (mouse_pos)
6591 {
6592 int mx, my;
6593
6594 gui_mch_getmouse(&mx, &my);
6595 p.x += mx;
6596 p.y += my;
6597 }
6598 else if (curwin != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006599 {
6600 p.x += TEXT_X(W_WINCOL(curwin) + curwin->w_wcol + 1);
6601 p.y += TEXT_Y(W_WINROW(curwin) + curwin->w_wrow + 1);
6602 }
6603 msg_scroll = FALSE;
6604 gui_mch_show_popupmenu_at(menu, (int)p.x, (int)p.y);
6605 }
6606}
6607
6608#if defined(FEAT_TEAROFF) || defined(PROTO)
6609/*
6610 * Given a menu descriptor, e.g. "File.New", find it in the menu hierarchy and
6611 * create it as a pseudo-"tearoff menu".
6612 */
6613 void
6614gui_make_tearoff(char_u *path_name)
6615{
6616 vimmenu_T *menu = gui_find_menu(path_name);
6617
6618 /* Found the menu, so tear it off. */
6619 if (menu != NULL)
6620 gui_mch_tearoff(menu->dname, menu, 0xffffL, 0xffffL);
6621}
6622#endif
6623
6624/*
6625 * Add a menu item to a menu
6626 */
6627 void
6628gui_mch_add_menu_item(
6629 vimmenu_T *menu,
6630 int idx)
6631{
6632 vimmenu_T *parent = menu->parent;
6633
6634 menu->id = s_menu_id++;
6635 menu->submenu_id = NULL;
6636
6637#ifdef FEAT_TEAROFF
6638 if (STRNCMP(menu->name, TEAR_STRING, TEAR_LEN) == 0)
6639 {
6640 InsertMenu(parent->submenu_id, (UINT)idx, MF_BITMAP|MF_BYPOSITION,
6641 (UINT)menu->id, (LPCTSTR) s_htearbitmap);
6642 }
6643 else
6644#endif
6645#ifdef FEAT_TOOLBAR
6646 if (menu_is_toolbar(parent->name))
6647 {
6648 TBBUTTON newtb;
6649
6650 vim_memset(&newtb, 0, sizeof(newtb));
6651 if (menu_is_separator(menu->name))
6652 {
6653 newtb.iBitmap = 0;
6654 newtb.fsStyle = TBSTYLE_SEP;
6655 }
6656 else
6657 {
6658 newtb.iBitmap = get_toolbar_bitmap(menu);
6659 newtb.fsStyle = TBSTYLE_BUTTON;
6660 }
6661 newtb.idCommand = menu->id;
6662 newtb.fsState = TBSTATE_ENABLED;
6663 newtb.iString = 0;
6664 SendMessage(s_toolbarhwnd, TB_INSERTBUTTON, (WPARAM)idx,
6665 (LPARAM)&newtb);
6666 menu->submenu_id = (HMENU)-1;
6667 }
6668 else
6669#endif
6670 {
6671#ifdef FEAT_MBYTE
6672 WCHAR *wn = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006673
6674 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
6675 {
6676 /* 'encoding' differs from active codepage: convert menu item name
6677 * and use wide function */
Bram Moolenaar36f692d2008-11-20 16:10:17 +00006678 wn = enc_to_utf16(menu->name, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006679 if (wn != NULL)
6680 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006681 InsertMenuW(parent->submenu_id, (UINT)idx,
Bram Moolenaar071d4272004-06-13 20:20:40 +00006682 (menu_is_separator(menu->name)
6683 ? MF_SEPARATOR : MF_STRING) | MF_BYPOSITION,
6684 (UINT)menu->id, wn);
6685 vim_free(wn);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006686 }
6687 }
6688 if (wn == NULL)
6689#endif
6690 InsertMenu(parent->submenu_id, (UINT)idx,
6691 (menu_is_separator(menu->name) ? MF_SEPARATOR : MF_STRING)
6692 | MF_BYPOSITION,
6693 (UINT)menu->id, (LPCTSTR)menu->name);
6694#ifdef FEAT_TEAROFF
6695 if (IsWindow(parent->tearoff_handle))
6696 rebuild_tearoff(parent);
6697#endif
6698 }
6699}
6700
6701/*
6702 * Destroy the machine specific menu widget.
6703 */
6704 void
6705gui_mch_destroy_menu(vimmenu_T *menu)
6706{
6707#ifdef FEAT_TOOLBAR
6708 /*
6709 * is this a toolbar button?
6710 */
6711 if (menu->submenu_id == (HMENU)-1)
6712 {
6713 int iButton;
6714
6715 iButton = (int)SendMessage(s_toolbarhwnd, TB_COMMANDTOINDEX,
6716 (WPARAM)menu->id, 0);
6717 SendMessage(s_toolbarhwnd, TB_DELETEBUTTON, (WPARAM)iButton, 0);
6718 }
6719 else
6720#endif
6721 {
6722 if (menu->parent != NULL
6723 && menu_is_popup(menu->parent->dname)
6724 && menu->parent->submenu_id != NULL)
6725 RemoveMenu(menu->parent->submenu_id, menu->id, MF_BYCOMMAND);
6726 else
6727 RemoveMenu(s_menuBar, menu->id, MF_BYCOMMAND);
6728 if (menu->submenu_id != NULL)
6729 DestroyMenu(menu->submenu_id);
6730#ifdef FEAT_TEAROFF
6731 if (IsWindow(menu->tearoff_handle))
6732 DestroyWindow(menu->tearoff_handle);
6733 if (menu->parent != NULL
6734 && menu->parent->children != NULL
6735 && IsWindow(menu->parent->tearoff_handle))
6736 {
6737 /* This menu must not show up when rebuilding the tearoff window. */
6738 menu->modes = 0;
6739 rebuild_tearoff(menu->parent);
6740 }
6741#endif
6742 }
6743}
6744
6745#ifdef FEAT_TEAROFF
6746 static void
6747rebuild_tearoff(vimmenu_T *menu)
6748{
6749 /*hackish*/
6750 char_u tbuf[128];
6751 RECT trect;
6752 RECT rct;
6753 RECT roct;
6754 int x, y;
6755
6756 HWND thwnd = menu->tearoff_handle;
6757
Bram Moolenaar418f81b2016-02-16 20:12:02 +01006758 GetWindowText(thwnd, (LPSTR)tbuf, 127);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006759 if (GetWindowRect(thwnd, &trect)
6760 && GetWindowRect(s_hwnd, &rct)
6761 && GetClientRect(s_hwnd, &roct))
6762 {
6763 x = trect.left - rct.left;
6764 y = (trect.top - rct.bottom + roct.bottom);
6765 }
6766 else
6767 {
6768 x = y = 0xffffL;
6769 }
6770 DestroyWindow(thwnd);
6771 if (menu->children != NULL)
6772 {
6773 gui_mch_tearoff(tbuf, menu, x, y);
6774 if (IsWindow(menu->tearoff_handle))
6775 (void) SetWindowPos(menu->tearoff_handle,
6776 NULL,
6777 (int)trect.left,
6778 (int)trect.top,
6779 0, 0,
6780 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
6781 }
6782}
6783#endif /* FEAT_TEAROFF */
6784
6785/*
6786 * Make a menu either grey or not grey.
6787 */
6788 void
6789gui_mch_menu_grey(
6790 vimmenu_T *menu,
6791 int grey)
6792{
6793#ifdef FEAT_TOOLBAR
6794 /*
6795 * is this a toolbar button?
6796 */
6797 if (menu->submenu_id == (HMENU)-1)
6798 {
6799 SendMessage(s_toolbarhwnd, TB_ENABLEBUTTON,
6800 (WPARAM)menu->id, (LPARAM) MAKELONG((grey ? FALSE : TRUE), 0) );
6801 }
6802 else
6803#endif
Bram Moolenaar762f1752016-06-04 22:36:17 +02006804 (void)EnableMenuItem(menu->parent ? menu->parent->submenu_id : s_menuBar,
6805 menu->id, MF_BYCOMMAND | (grey ? MF_GRAYED : MF_ENABLED));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006806
6807#ifdef FEAT_TEAROFF
6808 if ((menu->parent != NULL) && (IsWindow(menu->parent->tearoff_handle)))
6809 {
6810 WORD menuID;
6811 HWND menuHandle;
6812
6813 /*
6814 * A tearoff button has changed state.
6815 */
6816 if (menu->children == NULL)
6817 menuID = (WORD)(menu->id);
6818 else
Bram Moolenaareb3593b2006-04-22 22:33:57 +00006819 menuID = (WORD)((long_u)(menu->submenu_id) | (DWORD)0x8000);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006820 menuHandle = GetDlgItem(menu->parent->tearoff_handle, menuID);
6821 if (menuHandle)
6822 EnableWindow(menuHandle, !grey);
6823
6824 }
6825#endif
6826}
6827
6828#endif /* FEAT_MENU */
6829
6830
6831/* define some macros used to make the dialogue creation more readable */
6832
6833#define add_string(s) strcpy((LPSTR)p, s); (LPSTR)p += (strlen((LPSTR)p) + 1)
6834#define add_word(x) *p++ = (x)
Bram Moolenaarf4cd3e82005-12-22 22:47:02 +00006835#define add_long(x) dwp = (DWORD *)p; *dwp++ = (x); p = (WORD *)dwp
Bram Moolenaar071d4272004-06-13 20:20:40 +00006836
6837#if defined(FEAT_GUI_DIALOG) || defined(PROTO)
6838/*
6839 * stuff for dialogs
6840 */
6841
6842/*
6843 * The callback routine used by all the dialogs. Very simple. First,
6844 * acknowledges the INITDIALOG message so that Windows knows to do standard
6845 * dialog stuff (Return = default, Esc = cancel....) Second, if a button is
6846 * pressed, return that button's ID - IDCANCEL (2), which is the button's
6847 * number.
6848 */
6849 static LRESULT CALLBACK
6850dialog_callback(
6851 HWND hwnd,
6852 UINT message,
6853 WPARAM wParam,
Bram Moolenaar1266d672017-02-01 13:43:36 +01006854 LPARAM lParam UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006855{
6856 if (message == WM_INITDIALOG)
6857 {
6858 CenterWindow(hwnd, GetWindow(hwnd, GW_OWNER));
6859 /* Set focus to the dialog. Set the default button, if specified. */
6860 (void)SetFocus(hwnd);
6861 if (dialog_default_button > IDCANCEL)
6862 (void)SetFocus(GetDlgItem(hwnd, dialog_default_button));
Bram Moolenaar2b80e652007-08-14 14:57:55 +00006863 else
6864 /* We don't have a default, set focus on another element of the
6865 * dialog window, probably the icon */
6866 (void)SetFocus(GetDlgItem(hwnd, DLG_NONBUTTON_CONTROL));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006867 return FALSE;
6868 }
6869
6870 if (message == WM_COMMAND)
6871 {
6872 int button = LOWORD(wParam);
6873
6874 /* Don't end the dialog if something was selected that was
6875 * not a button.
6876 */
6877 if (button >= DLG_NONBUTTON_CONTROL)
6878 return TRUE;
6879
6880 /* If the edit box exists, copy the string. */
6881 if (s_textfield != NULL)
Bram Moolenaar3ca9a8a2009-01-28 20:23:17 +00006882 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006883# ifdef FEAT_MBYTE
Bram Moolenaar3ca9a8a2009-01-28 20:23:17 +00006884 /* If the OS is Windows NT, and 'encoding' differs from active
6885 * codepage: use wide function and convert text. */
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006886 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
Bram Moolenaarcc448b32010-07-14 16:52:17 +02006887 {
Bram Moolenaar3ca9a8a2009-01-28 20:23:17 +00006888 WCHAR *wp = (WCHAR *)alloc(IOSIZE * sizeof(WCHAR));
6889 char_u *p;
6890
6891 GetDlgItemTextW(hwnd, DLG_NONBUTTON_CONTROL + 2, wp, IOSIZE);
6892 p = utf16_to_enc(wp, NULL);
6893 vim_strncpy(s_textfield, p, IOSIZE);
6894 vim_free(p);
6895 vim_free(wp);
6896 }
6897 else
6898# endif
6899 GetDlgItemText(hwnd, DLG_NONBUTTON_CONTROL + 2,
Bram Moolenaar418f81b2016-02-16 20:12:02 +01006900 (LPSTR)s_textfield, IOSIZE);
Bram Moolenaar3ca9a8a2009-01-28 20:23:17 +00006901 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006902
6903 /*
6904 * Need to check for IDOK because if the user just hits Return to
6905 * accept the default value, some reason this is what we get.
6906 */
6907 if (button == IDOK)
6908 {
6909 if (dialog_default_button > IDCANCEL)
6910 EndDialog(hwnd, dialog_default_button);
6911 }
6912 else
6913 EndDialog(hwnd, button - IDCANCEL);
6914 return TRUE;
6915 }
6916
6917 if ((message == WM_SYSCOMMAND) && (wParam == SC_CLOSE))
6918 {
6919 EndDialog(hwnd, 0);
6920 return TRUE;
6921 }
6922 return FALSE;
6923}
6924
6925/*
6926 * Create a dialog dynamically from the parameter strings.
6927 * type = type of dialog (question, alert, etc.)
6928 * title = dialog title. may be NULL for default title.
6929 * message = text to display. Dialog sizes to accommodate it.
6930 * buttons = '\n' separated list of button captions, default first.
6931 * dfltbutton = number of default button.
6932 *
6933 * This routine returns 1 if the first button is pressed,
6934 * 2 for the second, etc.
6935 *
6936 * 0 indicates Esc was pressed.
6937 * -1 for unexpected error
6938 *
6939 * If stubbing out this fn, return 1.
6940 */
6941
Bram Moolenaar418f81b2016-02-16 20:12:02 +01006942static const char *dlg_icons[] = /* must match names in resource file */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006943{
6944 "IDR_VIM",
6945 "IDR_VIM_ERROR",
6946 "IDR_VIM_ALERT",
6947 "IDR_VIM_INFO",
6948 "IDR_VIM_QUESTION"
6949};
6950
Bram Moolenaar071d4272004-06-13 20:20:40 +00006951 int
6952gui_mch_dialog(
6953 int type,
6954 char_u *title,
6955 char_u *message,
6956 char_u *buttons,
6957 int dfltbutton,
Bram Moolenaard2c340a2011-01-17 20:08:11 +01006958 char_u *textfield,
6959 int ex_cmd)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006960{
6961 WORD *p, *pdlgtemplate, *pnumitems;
Bram Moolenaarf4cd3e82005-12-22 22:47:02 +00006962 DWORD *dwp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006963 int numButtons;
6964 int *buttonWidths, *buttonPositions;
6965 int buttonYpos;
6966 int nchar, i;
6967 DWORD lStyle;
6968 int dlgwidth = 0;
6969 int dlgheight;
6970 int editboxheight;
6971 int horizWidth = 0;
6972 int msgheight;
6973 char_u *pstart;
6974 char_u *pend;
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00006975 char_u *last_white;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006976 char_u *tbuffer;
6977 RECT rect;
6978 HWND hwnd;
6979 HDC hdc;
6980 HFONT font, oldFont;
6981 TEXTMETRIC fontInfo;
6982 int fontHeight;
6983 int textWidth, minButtonWidth, messageWidth;
6984 int maxDialogWidth;
Bram Moolenaar748bf032005-02-02 23:04:36 +00006985 int maxDialogHeight;
6986 int scroll_flag = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006987 int vertical;
6988 int dlgPaddingX;
6989 int dlgPaddingY;
6990#ifdef USE_SYSMENU_FONT
6991 LOGFONT lfSysmenu;
6992 int use_lfSysmenu = FALSE;
6993#endif
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00006994 garray_T ga;
6995 int l;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006996
6997#ifndef NO_CONSOLE
6998 /* Don't output anything in silent mode ("ex -s") */
6999 if (silent_mode)
7000 return dfltbutton; /* return default option */
7001#endif
7002
Bram Moolenaar748bf032005-02-02 23:04:36 +00007003 if (s_hwnd == NULL)
7004 get_dialog_font_metrics();
Bram Moolenaar071d4272004-06-13 20:20:40 +00007005
7006 if ((type < 0) || (type > VIM_LAST_TYPE))
7007 type = 0;
7008
7009 /* allocate some memory for dialog template */
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00007010 /* TODO should compute this really */
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007011 pdlgtemplate = p = (PWORD)LocalAlloc(LPTR,
Bram Moolenaareb3593b2006-04-22 22:33:57 +00007012 DLG_ALLOC_SIZE + STRLEN(message) * 2);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007013
7014 if (p == NULL)
7015 return -1;
7016
7017 /*
Bram Moolenaar84a05ac2013-05-06 04:24:17 +02007018 * make a copy of 'buttons' to fiddle with it. compiler grizzles because
Bram Moolenaar071d4272004-06-13 20:20:40 +00007019 * vim_strsave() doesn't take a const arg (why not?), so cast away the
7020 * const.
7021 */
7022 tbuffer = vim_strsave(buttons);
7023 if (tbuffer == NULL)
7024 return -1;
7025
7026 --dfltbutton; /* Change from one-based to zero-based */
7027
7028 /* Count buttons */
7029 numButtons = 1;
7030 for (i = 0; tbuffer[i] != '\0'; i++)
7031 {
7032 if (tbuffer[i] == DLG_BUTTON_SEP)
7033 numButtons++;
7034 }
7035 if (dfltbutton >= numButtons)
7036 dfltbutton = -1;
7037
7038 /* Allocate array to hold the width of each button */
Bram Moolenaarc54b8a72005-09-30 21:20:29 +00007039 buttonWidths = (int *)lalloc(numButtons * sizeof(int), TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007040 if (buttonWidths == NULL)
7041 return -1;
7042
7043 /* Allocate array to hold the X position of each button */
Bram Moolenaarc54b8a72005-09-30 21:20:29 +00007044 buttonPositions = (int *)lalloc(numButtons * sizeof(int), TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007045 if (buttonPositions == NULL)
7046 return -1;
7047
7048 /*
7049 * Calculate how big the dialog must be.
7050 */
7051 hwnd = GetDesktopWindow();
7052 hdc = GetWindowDC(hwnd);
7053#ifdef USE_SYSMENU_FONT
7054 if (gui_w32_get_menu_font(&lfSysmenu) == OK)
7055 {
7056 font = CreateFontIndirect(&lfSysmenu);
7057 use_lfSysmenu = TRUE;
7058 }
7059 else
7060#endif
7061 font = CreateFont(-DLG_FONT_POINT_SIZE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
7062 VARIABLE_PITCH , DLG_FONT_NAME);
7063 if (s_usenewlook)
7064 {
7065 oldFont = SelectFont(hdc, font);
7066 dlgPaddingX = DLG_PADDING_X;
7067 dlgPaddingY = DLG_PADDING_Y;
7068 }
7069 else
7070 {
7071 oldFont = SelectFont(hdc, GetStockObject(SYSTEM_FONT));
7072 dlgPaddingX = DLG_OLD_STYLE_PADDING_X;
7073 dlgPaddingY = DLG_OLD_STYLE_PADDING_Y;
7074 }
7075 GetTextMetrics(hdc, &fontInfo);
7076 fontHeight = fontInfo.tmHeight;
7077
7078 /* Minimum width for horizontal button */
Bram Moolenaar418f81b2016-02-16 20:12:02 +01007079 minButtonWidth = GetTextWidth(hdc, (char_u *)"Cancel", 6);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007080
7081 /* Maximum width of a dialog, if possible */
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007082 if (s_hwnd == NULL)
7083 {
7084 RECT workarea_rect;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007085
Bram Moolenaarc716c302006-01-21 22:12:51 +00007086 /* We don't have a window, use the desktop area. */
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007087 get_work_area(&workarea_rect);
7088 maxDialogWidth = workarea_rect.right - workarea_rect.left - 100;
7089 if (maxDialogWidth > 600)
7090 maxDialogWidth = 600;
Bram Moolenaar1b1b0942013-08-01 13:20:42 +02007091 /* Leave some room for the taskbar. */
7092 maxDialogHeight = workarea_rect.bottom - workarea_rect.top - 150;
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007093 }
7094 else
7095 {
Bram Moolenaara95d8232013-08-07 15:27:11 +02007096 /* Use our own window for the size, unless it's very small. */
7097 GetWindowRect(s_hwnd, &rect);
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007098 maxDialogWidth = rect.right - rect.left
Bram Moolenaar9d488952013-07-21 17:53:58 +02007099 - (GetSystemMetrics(SM_CXFRAME) +
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02007100 GetSystemMetrics(SM_CXPADDEDBORDER)) * 2;
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007101 if (maxDialogWidth < DLG_MIN_MAX_WIDTH)
7102 maxDialogWidth = DLG_MIN_MAX_WIDTH;
Bram Moolenaar748bf032005-02-02 23:04:36 +00007103
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007104 maxDialogHeight = rect.bottom - rect.top
Bram Moolenaar1b1b0942013-08-01 13:20:42 +02007105 - (GetSystemMetrics(SM_CYFRAME) +
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02007106 GetSystemMetrics(SM_CXPADDEDBORDER)) * 4
Bram Moolenaara95d8232013-08-07 15:27:11 +02007107 - GetSystemMetrics(SM_CYCAPTION);
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007108 if (maxDialogHeight < DLG_MIN_MAX_HEIGHT)
7109 maxDialogHeight = DLG_MIN_MAX_HEIGHT;
7110 }
7111
7112 /* Set dlgwidth to width of message.
7113 * Copy the message into "ga", changing NL to CR-NL and inserting line
7114 * breaks where needed. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007115 pstart = message;
7116 messageWidth = 0;
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007117 msgheight = 0;
7118 ga_init2(&ga, sizeof(char), 500);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007119 do
7120 {
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007121 msgheight += fontHeight; /* at least one line */
7122
7123 /* Need to figure out where to break the string. The system does it
7124 * at a word boundary, which would mean we can't compute the number of
7125 * wrapped lines. */
7126 textWidth = 0;
7127 last_white = NULL;
7128 for (pend = pstart; *pend != NUL && *pend != '\n'; )
Bram Moolenaar748bf032005-02-02 23:04:36 +00007129 {
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007130#ifdef FEAT_MBYTE
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00007131 l = (*mb_ptr2len)(pend);
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007132#else
7133 l = 1;
7134#endif
Bram Moolenaar1c465442017-03-12 20:10:05 +01007135 if (l == 1 && VIM_ISWHITE(*pend)
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007136 && textWidth > maxDialogWidth * 3 / 4)
7137 last_white = pend;
Bram Moolenaarf05d8112013-06-26 12:58:32 +02007138 textWidth += GetTextWidthEnc(hdc, pend, l);
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007139 if (textWidth >= maxDialogWidth)
Bram Moolenaar748bf032005-02-02 23:04:36 +00007140 {
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007141 /* Line will wrap. */
7142 messageWidth = maxDialogWidth;
Bram Moolenaar748bf032005-02-02 23:04:36 +00007143 msgheight += fontHeight;
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007144 textWidth = 0;
7145
7146 if (last_white != NULL)
7147 {
7148 /* break the line just after a space */
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00007149 ga.ga_len -= (int)(pend - (last_white + 1));
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007150 pend = last_white + 1;
7151 last_white = NULL;
7152 }
7153 ga_append(&ga, '\r');
7154 ga_append(&ga, '\n');
7155 continue;
Bram Moolenaar748bf032005-02-02 23:04:36 +00007156 }
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007157
7158 while (--l >= 0)
7159 ga_append(&ga, *pend++);
Bram Moolenaar748bf032005-02-02 23:04:36 +00007160 }
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007161 if (textWidth > messageWidth)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007162 messageWidth = textWidth;
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007163
7164 ga_append(&ga, '\r');
7165 ga_append(&ga, '\n');
Bram Moolenaar071d4272004-06-13 20:20:40 +00007166 pstart = pend + 1;
7167 } while (*pend != NUL);
Bram Moolenaar748bf032005-02-02 23:04:36 +00007168
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007169 if (ga.ga_data != NULL)
7170 message = ga.ga_data;
7171
Bram Moolenaar748bf032005-02-02 23:04:36 +00007172 messageWidth += 10; /* roundoff space */
7173
Bram Moolenaar071d4272004-06-13 20:20:40 +00007174 /* Add width of icon to dlgwidth, and some space */
Bram Moolenaara95d8232013-08-07 15:27:11 +02007175 dlgwidth = messageWidth + DLG_ICON_WIDTH + 3 * dlgPaddingX
7176 + GetSystemMetrics(SM_CXVSCROLL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007177
7178 if (msgheight < DLG_ICON_HEIGHT)
7179 msgheight = DLG_ICON_HEIGHT;
7180
7181 /*
7182 * Check button names. A long one will make the dialog wider.
Bram Moolenaaraea02fa2007-05-04 20:29:09 +00007183 * When called early (-register error message) p_go isn't initialized.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007184 */
Bram Moolenaaraea02fa2007-05-04 20:29:09 +00007185 vertical = (p_go != NULL && vim_strchr(p_go, GO_VERTICAL) != NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007186 if (!vertical)
7187 {
7188 // Place buttons horizontally if they fit.
7189 horizWidth = dlgPaddingX;
7190 pstart = tbuffer;
7191 i = 0;
7192 do
7193 {
7194 pend = vim_strchr(pstart, DLG_BUTTON_SEP);
7195 if (pend == NULL)
7196 pend = pstart + STRLEN(pstart); // Last button name.
Bram Moolenaarb052fe02013-06-26 13:16:20 +02007197 textWidth = GetTextWidthEnc(hdc, pstart, (int)(pend - pstart));
Bram Moolenaar071d4272004-06-13 20:20:40 +00007198 if (textWidth < minButtonWidth)
7199 textWidth = minButtonWidth;
7200 textWidth += dlgPaddingX; /* Padding within button */
7201 buttonWidths[i] = textWidth;
7202 buttonPositions[i++] = horizWidth;
7203 horizWidth += textWidth + dlgPaddingX; /* Pad between buttons */
7204 pstart = pend + 1;
7205 } while (*pend != NUL);
7206
7207 if (horizWidth > maxDialogWidth)
7208 vertical = TRUE; // Too wide to fit on the screen.
7209 else if (horizWidth > dlgwidth)
7210 dlgwidth = horizWidth;
7211 }
7212
7213 if (vertical)
7214 {
7215 // Stack buttons vertically.
7216 pstart = tbuffer;
7217 do
7218 {
7219 pend = vim_strchr(pstart, DLG_BUTTON_SEP);
7220 if (pend == NULL)
7221 pend = pstart + STRLEN(pstart); // Last button name.
Bram Moolenaarb052fe02013-06-26 13:16:20 +02007222 textWidth = GetTextWidthEnc(hdc, pstart, (int)(pend - pstart));
Bram Moolenaar071d4272004-06-13 20:20:40 +00007223 textWidth += dlgPaddingX; /* Padding within button */
7224 textWidth += DLG_VERT_PADDING_X * 2; /* Padding around button */
7225 if (textWidth > dlgwidth)
7226 dlgwidth = textWidth;
7227 pstart = pend + 1;
7228 } while (*pend != NUL);
7229 }
7230
7231 if (dlgwidth < DLG_MIN_WIDTH)
7232 dlgwidth = DLG_MIN_WIDTH; /* Don't allow a really thin dialog!*/
7233
7234 /* start to fill in the dlgtemplate information. addressing by WORDs */
7235 if (s_usenewlook)
7236 lStyle = DS_MODALFRAME | WS_CAPTION |DS_3DLOOK| WS_VISIBLE |DS_SETFONT;
7237 else
7238 lStyle = DS_MODALFRAME | WS_CAPTION |DS_3DLOOK| WS_VISIBLE;
7239
7240 add_long(lStyle);
7241 add_long(0); // (lExtendedStyle)
7242 pnumitems = p; /*save where the number of items must be stored*/
7243 add_word(0); // NumberOfItems(will change later)
7244 add_word(10); // x
7245 add_word(10); // y
7246 add_word(PixelToDialogX(dlgwidth)); // cx
7247
7248 // Dialog height.
7249 if (vertical)
Bram Moolenaara95d8232013-08-07 15:27:11 +02007250 dlgheight = msgheight + 2 * dlgPaddingY
7251 + DLG_VERT_PADDING_Y + 2 * fontHeight * numButtons;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007252 else
7253 dlgheight = msgheight + 3 * dlgPaddingY + 2 * fontHeight;
7254
7255 // Dialog needs to be taller if contains an edit box.
7256 editboxheight = fontHeight + dlgPaddingY + 4 * DLG_VERT_PADDING_Y;
7257 if (textfield != NULL)
7258 dlgheight += editboxheight;
7259
Bram Moolenaara95d8232013-08-07 15:27:11 +02007260 /* Restrict the size to a maximum. Causes a scrollbar to show up. */
7261 if (dlgheight > maxDialogHeight)
7262 {
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02007263 msgheight = msgheight - (dlgheight - maxDialogHeight);
7264 dlgheight = maxDialogHeight;
7265 scroll_flag = WS_VSCROLL;
7266 /* Make sure scrollbar doesn't appear in the middle of the dialog */
7267 messageWidth = dlgwidth - DLG_ICON_WIDTH - 3 * dlgPaddingX;
Bram Moolenaara95d8232013-08-07 15:27:11 +02007268 }
7269
Bram Moolenaar071d4272004-06-13 20:20:40 +00007270 add_word(PixelToDialogY(dlgheight));
7271
7272 add_word(0); // Menu
7273 add_word(0); // Class
7274
7275 /* copy the title of the dialog */
7276 nchar = nCopyAnsiToWideChar(p, (title ?
7277 (LPSTR)title :
7278 (LPSTR)("Vim "VIM_VERSION_MEDIUM)));
7279 p += nchar;
7280
7281 if (s_usenewlook)
7282 {
7283 /* do the font, since DS_3DLOOK doesn't work properly */
7284#ifdef USE_SYSMENU_FONT
7285 if (use_lfSysmenu)
7286 {
7287 /* point size */
7288 *p++ = -MulDiv(lfSysmenu.lfHeight, 72,
7289 GetDeviceCaps(hdc, LOGPIXELSY));
7290 nchar = nCopyAnsiToWideChar(p, TEXT(lfSysmenu.lfFaceName));
7291 }
7292 else
7293#endif
7294 {
7295 *p++ = DLG_FONT_POINT_SIZE; // point size
7296 nchar = nCopyAnsiToWideChar(p, TEXT(DLG_FONT_NAME));
7297 }
7298 p += nchar;
7299 }
7300
7301 buttonYpos = msgheight + 2 * dlgPaddingY;
7302
7303 if (textfield != NULL)
7304 buttonYpos += editboxheight;
7305
7306 pstart = tbuffer;
7307 if (!vertical)
7308 horizWidth = (dlgwidth - horizWidth) / 2; /* Now it's X offset */
7309 for (i = 0; i < numButtons; i++)
7310 {
7311 /* get end of this button. */
7312 for ( pend = pstart;
7313 *pend && (*pend != DLG_BUTTON_SEP);
7314 pend++)
7315 ;
7316
7317 if (*pend)
7318 *pend = '\0';
7319
7320 /*
7321 * old NOTE:
7322 * setting the BS_DEFPUSHBUTTON style doesn't work because Windows sets
7323 * the focus to the first tab-able button and in so doing makes that
7324 * the default!! Grrr. Workaround: Make the default button the only
7325 * one with WS_TABSTOP style. Means user can't tab between buttons, but
7326 * he/she can use arrow keys.
7327 *
7328 * new NOTE: BS_DEFPUSHBUTTON is required to be able to select the
Bram Moolenaar2c7a7632007-05-10 18:19:11 +00007329 * right button when hitting <Enter>. E.g., for the ":confirm quit"
Bram Moolenaar071d4272004-06-13 20:20:40 +00007330 * dialog. Also needed for when the textfield is the default control.
7331 * It appears to work now (perhaps not on Win95?).
7332 */
7333 if (vertical)
7334 {
7335 p = add_dialog_element(p,
7336 (i == dfltbutton
7337 ? BS_DEFPUSHBUTTON : BS_PUSHBUTTON) | WS_TABSTOP,
7338 PixelToDialogX(DLG_VERT_PADDING_X),
7339 PixelToDialogY(buttonYpos /* TBK */
7340 + 2 * fontHeight * i),
7341 PixelToDialogX(dlgwidth - 2 * DLG_VERT_PADDING_X),
7342 (WORD)(PixelToDialogY(2 * fontHeight) - 1),
Bram Moolenaar418f81b2016-02-16 20:12:02 +01007343 (WORD)(IDCANCEL + 1 + i), (WORD)0x0080, (char *)pstart);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007344 }
7345 else
7346 {
7347 p = add_dialog_element(p,
7348 (i == dfltbutton
7349 ? BS_DEFPUSHBUTTON : BS_PUSHBUTTON) | WS_TABSTOP,
7350 PixelToDialogX(horizWidth + buttonPositions[i]),
7351 PixelToDialogY(buttonYpos), /* TBK */
7352 PixelToDialogX(buttonWidths[i]),
7353 (WORD)(PixelToDialogY(2 * fontHeight) - 1),
Bram Moolenaar418f81b2016-02-16 20:12:02 +01007354 (WORD)(IDCANCEL + 1 + i), (WORD)0x0080, (char *)pstart);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007355 }
7356 pstart = pend + 1; /*next button*/
7357 }
7358 *pnumitems += numButtons;
7359
7360 /* Vim icon */
7361 p = add_dialog_element(p, SS_ICON,
7362 PixelToDialogX(dlgPaddingX),
7363 PixelToDialogY(dlgPaddingY),
7364 PixelToDialogX(DLG_ICON_WIDTH),
7365 PixelToDialogY(DLG_ICON_HEIGHT),
7366 DLG_NONBUTTON_CONTROL + 0, (WORD)0x0082,
7367 dlg_icons[type]);
7368
Bram Moolenaar748bf032005-02-02 23:04:36 +00007369 /* Dialog message */
7370 p = add_dialog_element(p, ES_LEFT|scroll_flag|ES_MULTILINE|ES_READONLY,
7371 PixelToDialogX(2 * dlgPaddingX + DLG_ICON_WIDTH),
7372 PixelToDialogY(dlgPaddingY),
7373 (WORD)(PixelToDialogX(messageWidth) + 1),
7374 PixelToDialogY(msgheight),
Bram Moolenaar418f81b2016-02-16 20:12:02 +01007375 DLG_NONBUTTON_CONTROL + 1, (WORD)0x0081, (char *)message);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007376
7377 /* Edit box */
7378 if (textfield != NULL)
7379 {
7380 p = add_dialog_element(p, ES_LEFT|ES_AUTOHSCROLL|WS_TABSTOP|WS_BORDER,
7381 PixelToDialogX(2 * dlgPaddingX),
7382 PixelToDialogY(2 * dlgPaddingY + msgheight),
7383 PixelToDialogX(dlgwidth - 4 * dlgPaddingX),
7384 PixelToDialogY(fontHeight + dlgPaddingY),
Bram Moolenaar418f81b2016-02-16 20:12:02 +01007385 DLG_NONBUTTON_CONTROL + 2, (WORD)0x0081, (char *)textfield);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007386 *pnumitems += 1;
7387 }
7388
7389 *pnumitems += 2;
7390
7391 SelectFont(hdc, oldFont);
7392 DeleteObject(font);
7393 ReleaseDC(hwnd, hdc);
7394
7395 /* Let the dialog_callback() function know which button to make default
7396 * If we have an edit box, make that the default. We also need to tell
7397 * dialog_callback() if this dialog contains an edit box or not. We do
7398 * this by setting s_textfield if it does.
7399 */
7400 if (textfield != NULL)
7401 {
7402 dialog_default_button = DLG_NONBUTTON_CONTROL + 2;
7403 s_textfield = textfield;
7404 }
7405 else
7406 {
7407 dialog_default_button = IDCANCEL + 1 + dfltbutton;
7408 s_textfield = NULL;
7409 }
7410
7411 /* show the dialog box modally and get a return value */
7412 nchar = (int)DialogBoxIndirect(
7413 s_hinst,
7414 (LPDLGTEMPLATE)pdlgtemplate,
7415 s_hwnd,
7416 (DLGPROC)dialog_callback);
7417
7418 LocalFree(LocalHandle(pdlgtemplate));
7419 vim_free(tbuffer);
7420 vim_free(buttonWidths);
7421 vim_free(buttonPositions);
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007422 vim_free(ga.ga_data);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007423
7424 /* Focus back to our window (for when MDI is used). */
7425 (void)SetFocus(s_hwnd);
7426
7427 return nchar;
7428}
7429
7430#endif /* FEAT_GUI_DIALOG */
Bram Moolenaareb3593b2006-04-22 22:33:57 +00007431
Bram Moolenaar071d4272004-06-13 20:20:40 +00007432/*
7433 * Put a simple element (basic class) onto a dialog template in memory.
7434 * return a pointer to where the next item should be added.
7435 *
7436 * parameters:
7437 * lStyle = additional style flags
7438 * (be careful, NT3.51 & Win32s will ignore the new ones)
7439 * x,y = x & y positions IN DIALOG UNITS
7440 * w,h = width and height IN DIALOG UNITS
7441 * Id = ID used in messages
7442 * clss = class ID, e.g 0x0080 for a button, 0x0082 for a static
7443 * caption = usually text or resource name
7444 *
7445 * TODO: use the length information noted here to enable the dialog creation
7446 * routines to work out more exactly how much memory they need to alloc.
7447 */
7448 static PWORD
7449add_dialog_element(
7450 PWORD p,
7451 DWORD lStyle,
7452 WORD x,
7453 WORD y,
7454 WORD w,
7455 WORD h,
7456 WORD Id,
7457 WORD clss,
7458 const char *caption)
7459{
7460 int nchar;
7461
7462 p = lpwAlign(p); /* Align to dword boundary*/
7463 lStyle = lStyle | WS_VISIBLE | WS_CHILD;
7464 *p++ = LOWORD(lStyle);
7465 *p++ = HIWORD(lStyle);
7466 *p++ = 0; // LOWORD (lExtendedStyle)
7467 *p++ = 0; // HIWORD (lExtendedStyle)
7468 *p++ = x;
7469 *p++ = y;
7470 *p++ = w;
7471 *p++ = h;
7472 *p++ = Id; //9 or 10 words in all
7473
7474 *p++ = (WORD)0xffff;
7475 *p++ = clss; //2 more here
7476
7477 nchar = nCopyAnsiToWideChar(p, (LPSTR)caption); //strlen(caption)+1
7478 p += nchar;
7479
7480 *p++ = 0; // advance pointer over nExtraStuff WORD - 2 more
7481
7482 return p; //total = 15+ (strlen(caption)) words
7483 // = 30 + 2(strlen(caption) bytes reqd
7484}
7485
7486
7487/*
7488 * Helper routine. Take an input pointer, return closest pointer that is
7489 * aligned on a DWORD (4 byte) boundary. Taken from the Win32SDK samples.
7490 */
7491 static LPWORD
7492lpwAlign(
7493 LPWORD lpIn)
7494{
Bram Moolenaareb3593b2006-04-22 22:33:57 +00007495 long_u ul;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007496
Bram Moolenaareb3593b2006-04-22 22:33:57 +00007497 ul = (long_u)lpIn;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007498 ul += 3;
7499 ul >>= 2;
7500 ul <<= 2;
7501 return (LPWORD)ul;
7502}
7503
7504/*
7505 * Helper routine. Takes second parameter as Ansi string, copies it to first
7506 * parameter as wide character (16-bits / char) string, and returns integer
7507 * number of wide characters (words) in string (including the trailing wide
7508 * char NULL). Partly taken from the Win32SDK samples.
7509 */
7510 static int
7511nCopyAnsiToWideChar(
7512 LPWORD lpWCStr,
7513 LPSTR lpAnsiIn)
7514{
7515 int nChar = 0;
7516#ifdef FEAT_MBYTE
7517 int len = lstrlen(lpAnsiIn) + 1; /* include NUL character */
7518 int i;
7519 WCHAR *wn;
7520
7521 if (enc_codepage == 0 && (int)GetACP() != enc_codepage)
7522 {
7523 /* Not a codepage, use our own conversion function. */
Bram Moolenaar418f81b2016-02-16 20:12:02 +01007524 wn = enc_to_utf16((char_u *)lpAnsiIn, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007525 if (wn != NULL)
7526 {
7527 wcscpy(lpWCStr, wn);
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00007528 nChar = (int)wcslen(wn) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007529 vim_free(wn);
7530 }
7531 }
7532 if (nChar == 0)
7533 /* Use Win32 conversion function. */
7534 nChar = MultiByteToWideChar(
7535 enc_codepage > 0 ? enc_codepage : CP_ACP,
7536 MB_PRECOMPOSED,
7537 lpAnsiIn, len,
7538 lpWCStr, len);
7539 for (i = 0; i < nChar; ++i)
7540 if (lpWCStr[i] == (WORD)'\t') /* replace tabs with spaces */
7541 lpWCStr[i] = (WORD)' ';
7542#else
7543 do
7544 {
7545 if (*lpAnsiIn == '\t')
7546 *lpWCStr++ = (WORD)' ';
7547 else
7548 *lpWCStr++ = (WORD)*lpAnsiIn;
7549 nChar++;
7550 } while (*lpAnsiIn++);
7551#endif
7552
7553 return nChar;
7554}
7555
7556
7557#ifdef FEAT_TEAROFF
7558/*
7559 * The callback function for all the modeless dialogs that make up the
7560 * "tearoff menus" Very simple - forward button presses (to fool Vim into
7561 * thinking its menus have been clicked), and go away when closed.
7562 */
7563 static LRESULT CALLBACK
7564tearoff_callback(
7565 HWND hwnd,
7566 UINT message,
7567 WPARAM wParam,
7568 LPARAM lParam)
7569{
7570 if (message == WM_INITDIALOG)
7571 return (TRUE);
7572
7573 /* May show the mouse pointer again. */
7574 HandleMouseHide(message, lParam);
7575
7576 if (message == WM_COMMAND)
7577 {
7578 if ((WORD)(LOWORD(wParam)) & 0x8000)
7579 {
7580 POINT mp;
7581 RECT rect;
7582
7583 if (GetCursorPos(&mp) && GetWindowRect(hwnd, &rect))
7584 {
7585 (void)TrackPopupMenu(
Bram Moolenaareb3593b2006-04-22 22:33:57 +00007586 (HMENU)(long_u)(LOWORD(wParam) ^ 0x8000),
Bram Moolenaar071d4272004-06-13 20:20:40 +00007587 TPM_LEFTALIGN | TPM_LEFTBUTTON,
7588 (int)rect.right - 8,
7589 (int)mp.y,
7590 (int)0, /*reserved param*/
7591 s_hwnd,
7592 NULL);
7593 /*
7594 * NOTE: The pop-up menu can eat the mouse up event.
7595 * We deal with this in normal.c.
7596 */
7597 }
7598 }
7599 else
7600 /* Pass on messages to the main Vim window */
7601 PostMessage(s_hwnd, WM_COMMAND, LOWORD(wParam), 0);
7602 /*
7603 * Give main window the focus back: this is so after
7604 * choosing a tearoff button you can start typing again
7605 * straight away.
7606 */
7607 (void)SetFocus(s_hwnd);
7608 return TRUE;
7609 }
7610 if ((message == WM_SYSCOMMAND) && (wParam == SC_CLOSE))
7611 {
7612 DestroyWindow(hwnd);
7613 return TRUE;
7614 }
7615
7616 /* When moved around, give main window the focus back. */
7617 if (message == WM_EXITSIZEMOVE)
7618 (void)SetActiveWindow(s_hwnd);
7619
7620 return FALSE;
7621}
7622#endif
7623
7624
7625/*
7626 * Decide whether to use the "new look" (small, non-bold font) or the "old
7627 * look" (big, clanky font) for dialogs, and work out a few values for use
7628 * later accordingly.
7629 */
7630 static void
7631get_dialog_font_metrics(void)
7632{
7633 HDC hdc;
7634 HFONT hfontTools = 0;
7635 DWORD dlgFontSize;
7636 SIZE size;
7637#ifdef USE_SYSMENU_FONT
7638 LOGFONT lfSysmenu;
7639#endif
7640
7641 s_usenewlook = FALSE;
7642
Bram Moolenaar071d4272004-06-13 20:20:40 +00007643#ifdef USE_SYSMENU_FONT
Bram Moolenaarcea912a2016-10-12 14:20:24 +02007644 if (gui_w32_get_menu_font(&lfSysmenu) == OK)
7645 hfontTools = CreateFontIndirect(&lfSysmenu);
7646 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00007647#endif
7648 hfontTools = CreateFont(-DLG_FONT_POINT_SIZE, 0, 0, 0, 0, 0, 0, 0,
7649 0, 0, 0, 0, VARIABLE_PITCH , DLG_FONT_NAME);
7650
Bram Moolenaarcea912a2016-10-12 14:20:24 +02007651 if (hfontTools)
7652 {
7653 hdc = GetDC(s_hwnd);
7654 SelectObject(hdc, hfontTools);
7655 /*
7656 * GetTextMetrics() doesn't return the right value in
7657 * tmAveCharWidth, so we have to figure out the dialog base units
7658 * ourselves.
7659 */
7660 GetTextExtentPoint(hdc,
7661 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
7662 52, &size);
7663 ReleaseDC(s_hwnd, hdc);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007664
Bram Moolenaarcea912a2016-10-12 14:20:24 +02007665 s_dlgfntwidth = (WORD)((size.cx / 26 + 1) / 2);
7666 s_dlgfntheight = (WORD)size.cy;
7667 s_usenewlook = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007668 }
7669
7670 if (!s_usenewlook)
7671 {
7672 dlgFontSize = GetDialogBaseUnits(); /* fall back to big old system*/
7673 s_dlgfntwidth = LOWORD(dlgFontSize);
7674 s_dlgfntheight = HIWORD(dlgFontSize);
7675 }
7676}
7677
7678#if defined(FEAT_MENU) && defined(FEAT_TEAROFF)
7679/*
7680 * Create a pseudo-"tearoff menu" based on the child
7681 * items of a given menu pointer.
7682 */
7683 static void
7684gui_mch_tearoff(
7685 char_u *title,
7686 vimmenu_T *menu,
7687 int initX,
7688 int initY)
7689{
7690 WORD *p, *pdlgtemplate, *pnumitems, *ptrueheight;
7691 int template_len;
7692 int nchar, textWidth, submenuWidth;
7693 DWORD lStyle;
7694 DWORD lExtendedStyle;
7695 WORD dlgwidth;
7696 WORD menuID;
7697 vimmenu_T *pmenu;
7698 vimmenu_T *the_menu = menu;
7699 HWND hwnd;
7700 HDC hdc;
7701 HFONT font, oldFont;
7702 int col, spaceWidth, len;
7703 int columnWidths[2];
7704 char_u *label, *text;
7705 int acLen = 0;
7706 int nameLen;
7707 int padding0, padding1, padding2 = 0;
7708 int sepPadding=0;
Bram Moolenaar9588a0f2005-01-08 21:45:39 +00007709 int x;
7710 int y;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007711#ifdef USE_SYSMENU_FONT
7712 LOGFONT lfSysmenu;
7713 int use_lfSysmenu = FALSE;
7714#endif
7715
7716 /*
7717 * If this menu is already torn off, move it to the mouse position.
7718 */
7719 if (IsWindow(menu->tearoff_handle))
7720 {
7721 POINT mp;
7722 if (GetCursorPos((LPPOINT)&mp))
7723 {
7724 SetWindowPos(menu->tearoff_handle, NULL, mp.x, mp.y, 0, 0,
7725 SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
7726 }
7727 return;
7728 }
7729
7730 /*
7731 * Create a new tearoff.
7732 */
7733 if (*title == MNU_HIDDEN_CHAR)
7734 title++;
7735
7736 /* Allocate memory to store the dialog template. It's made bigger when
7737 * needed. */
7738 template_len = DLG_ALLOC_SIZE;
7739 pdlgtemplate = p = (WORD *)LocalAlloc(LPTR, template_len);
7740 if (p == NULL)
7741 return;
7742
7743 hwnd = GetDesktopWindow();
7744 hdc = GetWindowDC(hwnd);
7745#ifdef USE_SYSMENU_FONT
7746 if (gui_w32_get_menu_font(&lfSysmenu) == OK)
7747 {
7748 font = CreateFontIndirect(&lfSysmenu);
7749 use_lfSysmenu = TRUE;
7750 }
7751 else
7752#endif
7753 font = CreateFont(-DLG_FONT_POINT_SIZE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
7754 VARIABLE_PITCH , DLG_FONT_NAME);
7755 if (s_usenewlook)
7756 oldFont = SelectFont(hdc, font);
7757 else
7758 oldFont = SelectFont(hdc, GetStockObject(SYSTEM_FONT));
7759
7760 /* Calculate width of a single space. Used for padding columns to the
7761 * right width. */
Bram Moolenaar418f81b2016-02-16 20:12:02 +01007762 spaceWidth = GetTextWidth(hdc, (char_u *)" ", 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007763
7764 /* Figure out max width of the text column, the accelerator column and the
7765 * optional submenu column. */
7766 submenuWidth = 0;
7767 for (col = 0; col < 2; col++)
7768 {
7769 columnWidths[col] = 0;
7770 for (pmenu = menu->children; pmenu != NULL; pmenu = pmenu->next)
7771 {
7772 /* Use "dname" here to compute the width of the visible text. */
7773 text = (col == 0) ? pmenu->dname : pmenu->actext;
7774 if (text != NULL && *text != NUL)
7775 {
7776 textWidth = GetTextWidthEnc(hdc, text, (int)STRLEN(text));
7777 if (textWidth > columnWidths[col])
7778 columnWidths[col] = textWidth;
7779 }
7780 if (pmenu->children != NULL)
7781 submenuWidth = TEAROFF_COLUMN_PADDING * spaceWidth;
7782 }
7783 }
7784 if (columnWidths[1] == 0)
7785 {
7786 /* no accelerators */
7787 if (submenuWidth != 0)
7788 columnWidths[0] += submenuWidth;
7789 else
7790 columnWidths[0] += spaceWidth;
7791 }
7792 else
7793 {
7794 /* there is an accelerator column */
7795 columnWidths[0] += TEAROFF_COLUMN_PADDING * spaceWidth;
7796 columnWidths[1] += submenuWidth;
7797 }
7798
7799 /*
7800 * Now find the total width of our 'menu'.
7801 */
7802 textWidth = columnWidths[0] + columnWidths[1];
7803 if (submenuWidth != 0)
7804 {
Bram Moolenaar418f81b2016-02-16 20:12:02 +01007805 submenuWidth = GetTextWidth(hdc, (char_u *)TEAROFF_SUBMENU_LABEL,
Bram Moolenaar071d4272004-06-13 20:20:40 +00007806 (int)STRLEN(TEAROFF_SUBMENU_LABEL));
7807 textWidth += submenuWidth;
7808 }
7809 dlgwidth = GetTextWidthEnc(hdc, title, (int)STRLEN(title));
7810 if (textWidth > dlgwidth)
7811 dlgwidth = textWidth;
7812 dlgwidth += 2 * TEAROFF_PADDING_X + TEAROFF_BUTTON_PAD_X;
7813
Bram Moolenaar071d4272004-06-13 20:20:40 +00007814 /* start to fill in the dlgtemplate information. addressing by WORDs */
7815 if (s_usenewlook)
7816 lStyle = DS_MODALFRAME | WS_CAPTION| WS_SYSMENU |DS_SETFONT| WS_VISIBLE;
7817 else
7818 lStyle = DS_MODALFRAME | WS_CAPTION| WS_SYSMENU | WS_VISIBLE;
7819
7820 lExtendedStyle = WS_EX_TOOLWINDOW|WS_EX_STATICEDGE;
7821 *p++ = LOWORD(lStyle);
7822 *p++ = HIWORD(lStyle);
7823 *p++ = LOWORD(lExtendedStyle);
7824 *p++ = HIWORD(lExtendedStyle);
7825 pnumitems = p; /* save where the number of items must be stored */
7826 *p++ = 0; // NumberOfItems(will change later)
Bram Moolenaar9588a0f2005-01-08 21:45:39 +00007827 gui_mch_getmouse(&x, &y);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007828 if (initX == 0xffffL)
Bram Moolenaar9588a0f2005-01-08 21:45:39 +00007829 *p++ = PixelToDialogX(x); // x
Bram Moolenaar071d4272004-06-13 20:20:40 +00007830 else
7831 *p++ = PixelToDialogX(initX); // x
7832 if (initY == 0xffffL)
Bram Moolenaar9588a0f2005-01-08 21:45:39 +00007833 *p++ = PixelToDialogY(y); // y
Bram Moolenaar071d4272004-06-13 20:20:40 +00007834 else
7835 *p++ = PixelToDialogY(initY); // y
7836 *p++ = PixelToDialogX(dlgwidth); // cx
7837 ptrueheight = p;
7838 *p++ = 0; // dialog height: changed later anyway
7839 *p++ = 0; // Menu
7840 *p++ = 0; // Class
7841
7842 /* copy the title of the dialog */
7843 nchar = nCopyAnsiToWideChar(p, ((*title)
7844 ? (LPSTR)title
7845 : (LPSTR)("Vim "VIM_VERSION_MEDIUM)));
7846 p += nchar;
7847
7848 if (s_usenewlook)
7849 {
7850 /* do the font, since DS_3DLOOK doesn't work properly */
7851#ifdef USE_SYSMENU_FONT
7852 if (use_lfSysmenu)
7853 {
7854 /* point size */
7855 *p++ = -MulDiv(lfSysmenu.lfHeight, 72,
7856 GetDeviceCaps(hdc, LOGPIXELSY));
7857 nchar = nCopyAnsiToWideChar(p, TEXT(lfSysmenu.lfFaceName));
7858 }
7859 else
7860#endif
7861 {
7862 *p++ = DLG_FONT_POINT_SIZE; // point size
7863 nchar = nCopyAnsiToWideChar (p, TEXT(DLG_FONT_NAME));
7864 }
7865 p += nchar;
7866 }
7867
7868 /*
7869 * Loop over all the items in the menu.
7870 * But skip over the tearbar.
7871 */
7872 if (STRCMP(menu->children->name, TEAR_STRING) == 0)
7873 menu = menu->children->next;
7874 else
7875 menu = menu->children;
7876 for ( ; menu != NULL; menu = menu->next)
7877 {
7878 if (menu->modes == 0) /* this menu has just been deleted */
7879 continue;
7880 if (menu_is_separator(menu->dname))
7881 {
7882 sepPadding += 3;
7883 continue;
7884 }
7885
7886 /* Check if there still is plenty of room in the template. Make it
7887 * larger when needed. */
7888 if (((char *)p - (char *)pdlgtemplate) + 1000 > template_len)
7889 {
7890 WORD *newp;
7891
7892 newp = (WORD *)LocalAlloc(LPTR, template_len + 4096);
7893 if (newp != NULL)
7894 {
7895 template_len += 4096;
7896 mch_memmove(newp, pdlgtemplate,
7897 (char *)p - (char *)pdlgtemplate);
7898 p = newp + (p - pdlgtemplate);
7899 pnumitems = newp + (pnumitems - pdlgtemplate);
7900 ptrueheight = newp + (ptrueheight - pdlgtemplate);
7901 LocalFree(LocalHandle(pdlgtemplate));
7902 pdlgtemplate = newp;
7903 }
7904 }
7905
7906 /* Figure out minimal length of this menu label. Use "name" for the
7907 * actual text, "dname" for estimating the displayed size. "name"
7908 * has "&a" for mnemonic and includes the accelerator. */
7909 len = nameLen = (int)STRLEN(menu->name);
7910 padding0 = (columnWidths[0] - GetTextWidthEnc(hdc, menu->dname,
7911 (int)STRLEN(menu->dname))) / spaceWidth;
7912 len += padding0;
7913
7914 if (menu->actext != NULL)
7915 {
7916 acLen = (int)STRLEN(menu->actext);
7917 len += acLen;
7918 textWidth = GetTextWidthEnc(hdc, menu->actext, acLen);
7919 }
7920 else
7921 textWidth = 0;
7922 padding1 = (columnWidths[1] - textWidth) / spaceWidth;
7923 len += padding1;
7924
7925 if (menu->children == NULL)
7926 {
7927 padding2 = submenuWidth / spaceWidth;
7928 len += padding2;
7929 menuID = (WORD)(menu->id);
7930 }
7931 else
7932 {
7933 len += (int)STRLEN(TEAROFF_SUBMENU_LABEL);
Bram Moolenaareb3593b2006-04-22 22:33:57 +00007934 menuID = (WORD)((long_u)(menu->submenu_id) | (DWORD)0x8000);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007935 }
7936
7937 /* Allocate menu label and fill it in */
7938 text = label = alloc((unsigned)len + 1);
7939 if (label == NULL)
7940 break;
7941
Bram Moolenaarce0842a2005-07-18 21:58:11 +00007942 vim_strncpy(text, menu->name, nameLen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007943 text = vim_strchr(text, TAB); /* stop at TAB before actext */
7944 if (text == NULL)
7945 text = label + nameLen; /* no actext, use whole name */
7946 while (padding0-- > 0)
7947 *text++ = ' ';
7948 if (menu->actext != NULL)
7949 {
7950 STRNCPY(text, menu->actext, acLen);
7951 text += acLen;
7952 }
7953 while (padding1-- > 0)
7954 *text++ = ' ';
7955 if (menu->children != NULL)
7956 {
7957 STRCPY(text, TEAROFF_SUBMENU_LABEL);
7958 text += STRLEN(TEAROFF_SUBMENU_LABEL);
7959 }
7960 else
7961 {
7962 while (padding2-- > 0)
7963 *text++ = ' ';
7964 }
7965 *text = NUL;
7966
7967 /*
7968 * BS_LEFT will just be ignored on Win32s/NT3.5x - on
7969 * W95/NT4 it makes the tear-off look more like a menu.
7970 */
7971 p = add_dialog_element(p,
7972 BS_PUSHBUTTON|BS_LEFT,
7973 (WORD)PixelToDialogX(TEAROFF_PADDING_X),
7974 (WORD)(sepPadding + 1 + 13 * (*pnumitems)),
7975 (WORD)PixelToDialogX(dlgwidth - 2 * TEAROFF_PADDING_X),
7976 (WORD)12,
Bram Moolenaar418f81b2016-02-16 20:12:02 +01007977 menuID, (WORD)0x0080, (char *)label);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007978 vim_free(label);
7979 (*pnumitems)++;
7980 }
7981
7982 *ptrueheight = (WORD)(sepPadding + 1 + 13 * (*pnumitems));
7983
7984
7985 /* show modelessly */
7986 the_menu->tearoff_handle = CreateDialogIndirect(
7987 s_hinst,
7988 (LPDLGTEMPLATE)pdlgtemplate,
7989 s_hwnd,
7990 (DLGPROC)tearoff_callback);
7991
7992 LocalFree(LocalHandle(pdlgtemplate));
7993 SelectFont(hdc, oldFont);
7994 DeleteObject(font);
7995 ReleaseDC(hwnd, hdc);
7996
7997 /*
7998 * Reassert ourselves as the active window. This is so that after creating
7999 * a tearoff, the user doesn't have to click with the mouse just to start
8000 * typing again!
8001 */
8002 (void)SetActiveWindow(s_hwnd);
8003
8004 /* make sure the right buttons are enabled */
8005 force_menu_update = TRUE;
8006}
8007#endif
8008
8009#if defined(FEAT_TOOLBAR) || defined(PROTO)
8010#include "gui_w32_rc.h"
8011
8012/* This not defined in older SDKs */
8013# ifndef TBSTYLE_FLAT
8014# define TBSTYLE_FLAT 0x0800
8015# endif
8016
8017/*
8018 * Create the toolbar, initially unpopulated.
8019 * (just like the menu, there are no defaults, it's all
8020 * set up through menu.vim)
8021 */
8022 static void
8023initialise_toolbar(void)
8024{
8025 InitCommonControls();
8026 s_toolbarhwnd = CreateToolbarEx(
8027 s_hwnd,
8028 WS_CHILD | TBSTYLE_TOOLTIPS | TBSTYLE_FLAT,
8029 4000, //any old big number
Bram Moolenaar2c7a7632007-05-10 18:19:11 +00008030 31, //number of images in initial bitmap
Bram Moolenaar071d4272004-06-13 20:20:40 +00008031 s_hinst,
8032 IDR_TOOLBAR1, // id of initial bitmap
8033 NULL,
8034 0, // initial number of buttons
8035 TOOLBAR_BUTTON_WIDTH, //api guide is wrong!
8036 TOOLBAR_BUTTON_HEIGHT,
8037 TOOLBAR_BUTTON_WIDTH,
8038 TOOLBAR_BUTTON_HEIGHT,
8039 sizeof(TBBUTTON)
8040 );
Bram Moolenaar5f919ee2013-07-21 17:46:43 +02008041 s_toolbar_wndproc = SubclassWindow(s_toolbarhwnd, toolbar_wndproc);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008042
8043 gui_mch_show_toolbar(vim_strchr(p_go, GO_TOOLBAR) != NULL);
8044}
8045
Bram Moolenaar5f919ee2013-07-21 17:46:43 +02008046 static LRESULT CALLBACK
8047toolbar_wndproc(
8048 HWND hwnd,
8049 UINT uMsg,
8050 WPARAM wParam,
8051 LPARAM lParam)
8052{
8053 HandleMouseHide(uMsg, lParam);
8054 return CallWindowProc(s_toolbar_wndproc, hwnd, uMsg, wParam, lParam);
8055}
8056
Bram Moolenaar071d4272004-06-13 20:20:40 +00008057 static int
8058get_toolbar_bitmap(vimmenu_T *menu)
8059{
8060 int i = -1;
8061
8062 /*
8063 * Check user bitmaps first, unless builtin is specified.
8064 */
Bram Moolenaarcea912a2016-10-12 14:20:24 +02008065 if (!menu->icon_builtin)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008066 {
8067 char_u fname[MAXPATHL];
8068 HANDLE hbitmap = NULL;
8069
8070 if (menu->iconfile != NULL)
8071 {
8072 gui_find_iconfile(menu->iconfile, fname, "bmp");
8073 hbitmap = LoadImage(
8074 NULL,
Bram Moolenaar418f81b2016-02-16 20:12:02 +01008075 (LPCSTR)fname,
Bram Moolenaar071d4272004-06-13 20:20:40 +00008076 IMAGE_BITMAP,
8077 TOOLBAR_BUTTON_WIDTH,
8078 TOOLBAR_BUTTON_HEIGHT,
8079 LR_LOADFROMFILE |
8080 LR_LOADMAP3DCOLORS
8081 );
8082 }
8083
8084 /*
8085 * If the LoadImage call failed, or the "icon=" file
8086 * didn't exist or wasn't specified, try the menu name
8087 */
8088 if (hbitmap == NULL
Bram Moolenaara5f5c8b2013-06-27 22:29:38 +02008089 && (gui_find_bitmap(
8090#ifdef FEAT_MULTI_LANG
8091 menu->en_dname != NULL ? menu->en_dname :
8092#endif
8093 menu->dname, fname, "bmp") == OK))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008094 hbitmap = LoadImage(
8095 NULL,
Bram Moolenaar418f81b2016-02-16 20:12:02 +01008096 (LPCSTR)fname,
Bram Moolenaar071d4272004-06-13 20:20:40 +00008097 IMAGE_BITMAP,
8098 TOOLBAR_BUTTON_WIDTH,
8099 TOOLBAR_BUTTON_HEIGHT,
8100 LR_LOADFROMFILE |
8101 LR_LOADMAP3DCOLORS
8102 );
8103
8104 if (hbitmap != NULL)
8105 {
8106 TBADDBITMAP tbAddBitmap;
8107
8108 tbAddBitmap.hInst = NULL;
Bram Moolenaareb3593b2006-04-22 22:33:57 +00008109 tbAddBitmap.nID = (long_u)hbitmap;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008110
8111 i = (int)SendMessage(s_toolbarhwnd, TB_ADDBITMAP,
8112 (WPARAM)1, (LPARAM)&tbAddBitmap);
8113 /* i will be set to -1 if it fails */
8114 }
8115 }
8116 if (i == -1 && menu->iconidx >= 0 && menu->iconidx < TOOLBAR_BITMAP_COUNT)
8117 i = menu->iconidx;
8118
8119 return i;
8120}
8121#endif
8122
Bram Moolenaar3991dab2006-03-27 17:01:56 +00008123#if defined(FEAT_GUI_TABLINE) || defined(PROTO)
8124 static void
8125initialise_tabline(void)
8126{
8127 InitCommonControls();
8128
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008129 s_tabhwnd = CreateWindow(WC_TABCONTROL, "Vim tabline",
Bram Moolenaareb3593b2006-04-22 22:33:57 +00008130 WS_CHILD|TCS_FOCUSNEVER|TCS_TOOLTIPS,
Bram Moolenaar3991dab2006-03-27 17:01:56 +00008131 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
8132 CW_USEDEFAULT, s_hwnd, NULL, s_hinst, NULL);
Bram Moolenaar5f919ee2013-07-21 17:46:43 +02008133 s_tabline_wndproc = SubclassWindow(s_tabhwnd, tabline_wndproc);
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008134
Bram Moolenaar551dbcc2006-04-25 22:13:59 +00008135 gui.tabline_height = TABLINE_HEIGHT;
8136
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008137# ifdef USE_SYSMENU_FONT
Bram Moolenaar551dbcc2006-04-25 22:13:59 +00008138 set_tabline_font();
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008139# endif
Bram Moolenaar3991dab2006-03-27 17:01:56 +00008140}
Bram Moolenaar5f919ee2013-07-21 17:46:43 +02008141
8142 static LRESULT CALLBACK
8143tabline_wndproc(
8144 HWND hwnd,
8145 UINT uMsg,
8146 WPARAM wParam,
8147 LPARAM lParam)
8148{
8149 HandleMouseHide(uMsg, lParam);
8150 return CallWindowProc(s_tabline_wndproc, hwnd, uMsg, wParam, lParam);
8151}
Bram Moolenaar3991dab2006-03-27 17:01:56 +00008152#endif
8153
Bram Moolenaar071d4272004-06-13 20:20:40 +00008154#if defined(FEAT_OLE) || defined(FEAT_EVAL) || defined(PROTO)
8155/*
8156 * Make the GUI window come to the foreground.
8157 */
8158 void
8159gui_mch_set_foreground(void)
8160{
8161 if (IsIconic(s_hwnd))
8162 SendMessage(s_hwnd, WM_SYSCOMMAND, SC_RESTORE, 0);
8163 SetForegroundWindow(s_hwnd);
8164}
8165#endif
8166
8167#if defined(FEAT_MBYTE_IME) && defined(DYNAMIC_IME)
8168 static void
8169dyn_imm_load(void)
8170{
Bram Moolenaarebbcb822010-10-23 14:02:54 +02008171 hLibImm = vimLoadLib("imm32.dll");
Bram Moolenaar071d4272004-06-13 20:20:40 +00008172 if (hLibImm == NULL)
8173 return;
8174
8175 pImmGetCompositionStringA
8176 = (void *)GetProcAddress(hLibImm, "ImmGetCompositionStringA");
8177 pImmGetCompositionStringW
8178 = (void *)GetProcAddress(hLibImm, "ImmGetCompositionStringW");
8179 pImmGetContext
8180 = (void *)GetProcAddress(hLibImm, "ImmGetContext");
8181 pImmAssociateContext
8182 = (void *)GetProcAddress(hLibImm, "ImmAssociateContext");
8183 pImmReleaseContext
8184 = (void *)GetProcAddress(hLibImm, "ImmReleaseContext");
8185 pImmGetOpenStatus
8186 = (void *)GetProcAddress(hLibImm, "ImmGetOpenStatus");
8187 pImmSetOpenStatus
8188 = (void *)GetProcAddress(hLibImm, "ImmSetOpenStatus");
8189 pImmGetCompositionFont
8190 = (void *)GetProcAddress(hLibImm, "ImmGetCompositionFontA");
8191 pImmSetCompositionFont
8192 = (void *)GetProcAddress(hLibImm, "ImmSetCompositionFontA");
8193 pImmSetCompositionWindow
8194 = (void *)GetProcAddress(hLibImm, "ImmSetCompositionWindow");
8195 pImmGetConversionStatus
8196 = (void *)GetProcAddress(hLibImm, "ImmGetConversionStatus");
Bram Moolenaarca003e12006-03-17 23:19:38 +00008197 pImmSetConversionStatus
8198 = (void *)GetProcAddress(hLibImm, "ImmSetConversionStatus");
Bram Moolenaar071d4272004-06-13 20:20:40 +00008199
8200 if ( pImmGetCompositionStringA == NULL
8201 || pImmGetCompositionStringW == NULL
8202 || pImmGetContext == NULL
8203 || pImmAssociateContext == NULL
8204 || pImmReleaseContext == NULL
8205 || pImmGetOpenStatus == NULL
8206 || pImmSetOpenStatus == NULL
8207 || pImmGetCompositionFont == NULL
8208 || pImmSetCompositionFont == NULL
8209 || pImmSetCompositionWindow == NULL
Bram Moolenaarca003e12006-03-17 23:19:38 +00008210 || pImmGetConversionStatus == NULL
8211 || pImmSetConversionStatus == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008212 {
8213 FreeLibrary(hLibImm);
8214 hLibImm = NULL;
8215 pImmGetContext = NULL;
8216 return;
8217 }
8218
8219 return;
8220}
8221
Bram Moolenaar071d4272004-06-13 20:20:40 +00008222#endif
8223
8224#if defined(FEAT_SIGN_ICONS) || defined(PROTO)
8225
8226# ifdef FEAT_XPM_W32
8227# define IMAGE_XPM 100
8228# endif
8229
8230typedef struct _signicon_t
8231{
8232 HANDLE hImage;
8233 UINT uType;
8234#ifdef FEAT_XPM_W32
8235 HANDLE hShape; /* Mask bitmap handle */
8236#endif
8237} signicon_t;
8238
8239 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01008240gui_mch_drawsign(int row, int col, int typenr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008241{
8242 signicon_t *sign;
8243 int x, y, w, h;
8244
8245 if (!gui.in_use || (sign = (signicon_t *)sign_get_image(typenr)) == NULL)
8246 return;
8247
8248 x = TEXT_X(col);
8249 y = TEXT_Y(row);
8250 w = gui.char_width * 2;
8251 h = gui.char_height;
8252 switch (sign->uType)
8253 {
8254 case IMAGE_BITMAP:
8255 {
8256 HDC hdcMem;
8257 HBITMAP hbmpOld;
8258
8259 hdcMem = CreateCompatibleDC(s_hdc);
8260 hbmpOld = (HBITMAP)SelectObject(hdcMem, sign->hImage);
8261 BitBlt(s_hdc, x, y, w, h, hdcMem, 0, 0, SRCCOPY);
8262 SelectObject(hdcMem, hbmpOld);
8263 DeleteDC(hdcMem);
8264 }
8265 break;
8266 case IMAGE_ICON:
8267 case IMAGE_CURSOR:
8268 DrawIconEx(s_hdc, x, y, (HICON)sign->hImage, w, h, 0, NULL, DI_NORMAL);
8269 break;
8270#ifdef FEAT_XPM_W32
8271 case IMAGE_XPM:
8272 {
8273 HDC hdcMem;
8274 HBITMAP hbmpOld;
8275
8276 hdcMem = CreateCompatibleDC(s_hdc);
8277 hbmpOld = (HBITMAP)SelectObject(hdcMem, sign->hShape);
8278 /* Make hole */
8279 BitBlt(s_hdc, x, y, w, h, hdcMem, 0, 0, SRCAND);
8280
8281 SelectObject(hdcMem, sign->hImage);
8282 /* Paint sign */
8283 BitBlt(s_hdc, x, y, w, h, hdcMem, 0, 0, SRCPAINT);
8284 SelectObject(hdcMem, hbmpOld);
8285 DeleteDC(hdcMem);
8286 }
8287 break;
8288#endif
8289 }
8290}
8291
8292 static void
8293close_signicon_image(signicon_t *sign)
8294{
8295 if (sign)
8296 switch (sign->uType)
8297 {
8298 case IMAGE_BITMAP:
8299 DeleteObject((HGDIOBJ)sign->hImage);
8300 break;
8301 case IMAGE_CURSOR:
8302 DestroyCursor((HCURSOR)sign->hImage);
8303 break;
8304 case IMAGE_ICON:
8305 DestroyIcon((HICON)sign->hImage);
8306 break;
8307#ifdef FEAT_XPM_W32
8308 case IMAGE_XPM:
8309 DeleteObject((HBITMAP)sign->hImage);
8310 DeleteObject((HBITMAP)sign->hShape);
8311 break;
8312#endif
8313 }
8314}
8315
8316 void *
Bram Moolenaar68c2f632016-01-30 17:24:07 +01008317gui_mch_register_sign(char_u *signfile)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008318{
8319 signicon_t sign, *psign;
8320 char_u *ext;
8321
Bram Moolenaar071d4272004-06-13 20:20:40 +00008322 sign.hImage = NULL;
Bram Moolenaar84a05ac2013-05-06 04:24:17 +02008323 ext = signfile + STRLEN(signfile) - 4; /* get extension */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008324 if (ext > signfile)
8325 {
8326 int do_load = 1;
8327
8328 if (!STRICMP(ext, ".bmp"))
8329 sign.uType = IMAGE_BITMAP;
8330 else if (!STRICMP(ext, ".ico"))
8331 sign.uType = IMAGE_ICON;
8332 else if (!STRICMP(ext, ".cur") || !STRICMP(ext, ".ani"))
8333 sign.uType = IMAGE_CURSOR;
8334 else
8335 do_load = 0;
8336
8337 if (do_load)
Bram Moolenaar418f81b2016-02-16 20:12:02 +01008338 sign.hImage = (HANDLE)LoadImage(NULL, (LPCSTR)signfile, sign.uType,
Bram Moolenaar071d4272004-06-13 20:20:40 +00008339 gui.char_width * 2, gui.char_height,
8340 LR_LOADFROMFILE | LR_CREATEDIBSECTION);
8341#ifdef FEAT_XPM_W32
8342 if (!STRICMP(ext, ".xpm"))
8343 {
8344 sign.uType = IMAGE_XPM;
Bram Moolenaar418f81b2016-02-16 20:12:02 +01008345 LoadXpmImage((char *)signfile, (HBITMAP *)&sign.hImage,
8346 (HBITMAP *)&sign.hShape);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008347 }
8348#endif
8349 }
8350
8351 psign = NULL;
8352 if (sign.hImage && (psign = (signicon_t *)alloc(sizeof(signicon_t)))
8353 != NULL)
8354 *psign = sign;
8355
8356 if (!psign)
8357 {
8358 if (sign.hImage)
8359 close_signicon_image(&sign);
8360 EMSG(_(e_signdata));
8361 }
8362 return (void *)psign;
8363
8364}
8365
8366 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01008367gui_mch_destroy_sign(void *sign)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008368{
8369 if (sign)
8370 {
8371 close_signicon_image((signicon_t *)sign);
8372 vim_free(sign);
8373 }
8374}
Bram Moolenaar5c06f8b2005-05-31 22:14:58 +00008375#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008376
8377#if defined(FEAT_BEVAL) || defined(PROTO)
8378
8379/* BALLOON-EVAL IMPLEMENTATION FOR WINDOWS.
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00008380 * Added by Sergey Khorev <sergey.khorev@gmail.com>
Bram Moolenaar071d4272004-06-13 20:20:40 +00008381 *
Bram Moolenaare4efc3b2005-03-07 23:16:51 +00008382 * The only reused thing is gui_beval.h and get_beval_info()
Bram Moolenaar071d4272004-06-13 20:20:40 +00008383 * from gui_beval.c (note it uses x and y of the BalloonEval struct
8384 * to get current mouse position).
8385 *
8386 * Trying to use as more Windows services as possible, and as less
8387 * IE version as possible :)).
8388 *
8389 * 1) Don't create ToolTip in gui_mch_create_beval_area, only initialize
8390 * BalloonEval struct.
8391 * 2) Enable/Disable simply create/kill BalloonEval Timer
8392 * 3) When there was enough inactivity, timer procedure posts
8393 * async request to debugger
8394 * 4) gui_mch_post_balloon (invoked from netbeans.c) creates tooltip control
8395 * and performs some actions to show it ASAP
Bram Moolenaar446cb832008-06-24 21:56:24 +00008396 * 5) WM_NOTIFY:TTN_POP destroys created tooltip
Bram Moolenaar071d4272004-06-13 20:20:40 +00008397 */
8398
Bram Moolenaar45360022005-07-21 21:08:21 +00008399/*
8400 * determine whether installed Common Controls support multiline tooltips
8401 * (i.e. their version is >= 4.70
8402 */
8403 int
8404multiline_balloon_available(void)
8405{
8406 HINSTANCE hDll;
8407 static char comctl_dll[] = "comctl32.dll";
8408 static int multiline_tip = MAYBE;
8409
8410 if (multiline_tip != MAYBE)
8411 return multiline_tip;
8412
8413 hDll = GetModuleHandle(comctl_dll);
8414 if (hDll != NULL)
8415 {
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00008416 DLLGETVERSIONPROC pGetVer;
8417 pGetVer = (DLLGETVERSIONPROC)GetProcAddress(hDll, "DllGetVersion");
Bram Moolenaar45360022005-07-21 21:08:21 +00008418
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00008419 if (pGetVer != NULL)
8420 {
8421 DLLVERSIONINFO dvi;
8422 HRESULT hr;
Bram Moolenaar45360022005-07-21 21:08:21 +00008423
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00008424 ZeroMemory(&dvi, sizeof(dvi));
8425 dvi.cbSize = sizeof(dvi);
Bram Moolenaar45360022005-07-21 21:08:21 +00008426
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00008427 hr = (*pGetVer)(&dvi);
Bram Moolenaar45360022005-07-21 21:08:21 +00008428
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00008429 if (SUCCEEDED(hr)
Bram Moolenaar45360022005-07-21 21:08:21 +00008430 && (dvi.dwMajorVersion > 4
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00008431 || (dvi.dwMajorVersion == 4
8432 && dvi.dwMinorVersion >= 70)))
Bram Moolenaar45360022005-07-21 21:08:21 +00008433 {
8434 multiline_tip = TRUE;
8435 return multiline_tip;
8436 }
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00008437 }
Bram Moolenaar45360022005-07-21 21:08:21 +00008438 else
8439 {
8440 /* there is chance we have ancient CommCtl 4.70
8441 which doesn't export DllGetVersion */
8442 DWORD dwHandle = 0;
8443 DWORD len = GetFileVersionInfoSize(comctl_dll, &dwHandle);
8444 if (len > 0)
8445 {
8446 VS_FIXEDFILEINFO *ver;
8447 UINT vlen = 0;
8448 void *data = alloc(len);
8449
Bram Moolenaar418f81b2016-02-16 20:12:02 +01008450 if ((data != NULL
Bram Moolenaar45360022005-07-21 21:08:21 +00008451 && GetFileVersionInfo(comctl_dll, 0, len, data)
8452 && VerQueryValue(data, "\\", (void **)&ver, &vlen)
8453 && vlen
Bram Moolenaar418f81b2016-02-16 20:12:02 +01008454 && HIWORD(ver->dwFileVersionMS) > 4)
8455 || ((HIWORD(ver->dwFileVersionMS) == 4
8456 && LOWORD(ver->dwFileVersionMS) >= 70)))
Bram Moolenaar45360022005-07-21 21:08:21 +00008457 {
8458 vim_free(data);
8459 multiline_tip = TRUE;
8460 return multiline_tip;
8461 }
8462 vim_free(data);
8463 }
8464 }
8465 }
8466 multiline_tip = FALSE;
8467 return multiline_tip;
8468}
8469
Bram Moolenaar071d4272004-06-13 20:20:40 +00008470 static void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01008471make_tooltip(BalloonEval *beval, char *text, POINT pt)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008472{
Bram Moolenaar45360022005-07-21 21:08:21 +00008473 TOOLINFO *pti;
8474 int ToolInfoSize;
8475
8476 if (multiline_balloon_available() == TRUE)
8477 ToolInfoSize = sizeof(TOOLINFO_NEW);
8478 else
8479 ToolInfoSize = sizeof(TOOLINFO);
8480
8481 pti = (TOOLINFO *)alloc(ToolInfoSize);
8482 if (pti == NULL)
8483 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008484
8485 beval->balloon = CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS,
8486 NULL, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,
8487 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
8488 beval->target, NULL, s_hinst, NULL);
8489
8490 SetWindowPos(beval->balloon, HWND_TOPMOST, 0, 0, 0, 0,
8491 SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
8492
Bram Moolenaar45360022005-07-21 21:08:21 +00008493 pti->cbSize = ToolInfoSize;
8494 pti->uFlags = TTF_SUBCLASS;
8495 pti->hwnd = beval->target;
8496 pti->hinst = 0; /* Don't use string resources */
8497 pti->uId = ID_BEVAL_TOOLTIP;
8498
8499 if (multiline_balloon_available() == TRUE)
8500 {
8501 RECT rect;
8502 TOOLINFO_NEW *ptin = (TOOLINFO_NEW *)pti;
8503 pti->lpszText = LPSTR_TEXTCALLBACK;
8504 ptin->lParam = (LPARAM)text;
8505 if (GetClientRect(s_textArea, &rect)) /* switch multiline tooltips on */
8506 SendMessage(beval->balloon, TTM_SETMAXTIPWIDTH, 0,
8507 (LPARAM)rect.right);
8508 }
8509 else
8510 pti->lpszText = text; /* do this old way */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008511
8512 /* Limit ballooneval bounding rect to CursorPos neighbourhood */
Bram Moolenaar45360022005-07-21 21:08:21 +00008513 pti->rect.left = pt.x - 3;
8514 pti->rect.top = pt.y - 3;
8515 pti->rect.right = pt.x + 3;
8516 pti->rect.bottom = pt.y + 3;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008517
Bram Moolenaar45360022005-07-21 21:08:21 +00008518 SendMessage(beval->balloon, TTM_ADDTOOL, 0, (LPARAM)pti);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008519 /* Make tooltip appear sooner */
8520 SendMessage(beval->balloon, TTM_SETDELAYTIME, TTDT_INITIAL, 10);
Bram Moolenaarb52e5322008-01-05 12:15:52 +00008521 /* I've performed some tests and it seems the longest possible life time
8522 * of tooltip is 30 seconds */
8523 SendMessage(beval->balloon, TTM_SETDELAYTIME, TTDT_AUTOPOP, 30000);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008524 /*
8525 * HACK: force tooltip to appear, because it'll not appear until
8526 * first mouse move. D*mn M$
Bram Moolenaarb52e5322008-01-05 12:15:52 +00008527 * Amazingly moving (2, 2) and then (-1, -1) the mouse doesn't move.
Bram Moolenaar071d4272004-06-13 20:20:40 +00008528 */
Bram Moolenaarb52e5322008-01-05 12:15:52 +00008529 mouse_event(MOUSEEVENTF_MOVE, 2, 2, 0, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008530 mouse_event(MOUSEEVENTF_MOVE, (DWORD)-1, (DWORD)-1, 0, 0);
Bram Moolenaar45360022005-07-21 21:08:21 +00008531 vim_free(pti);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008532}
8533
8534 static void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01008535delete_tooltip(BalloonEval *beval)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008536{
Bram Moolenaar8e5f5b42015-08-26 23:12:38 +02008537 PostMessage(beval->balloon, WM_CLOSE, 0, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008538}
8539
8540 static VOID CALLBACK
Bram Moolenaar68c2f632016-01-30 17:24:07 +01008541BevalTimerProc(
Bram Moolenaar1266d672017-02-01 13:43:36 +01008542 HWND hwnd UNUSED,
8543 UINT uMsg UNUSED,
8544 UINT_PTR idEvent UNUSED,
8545 DWORD dwTime)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008546{
8547 POINT pt;
8548 RECT rect;
8549
8550 if (cur_beval == NULL || cur_beval->showState == ShS_SHOWING || !p_beval)
8551 return;
8552
8553 GetCursorPos(&pt);
8554 if (WindowFromPoint(pt) != s_textArea)
8555 return;
8556
8557 ScreenToClient(s_textArea, &pt);
8558 GetClientRect(s_textArea, &rect);
8559 if (!PtInRect(&rect, pt))
8560 return;
8561
8562 if (LastActivity > 0
8563 && (dwTime - LastActivity) >= (DWORD)p_bdlay
8564 && (cur_beval->showState != ShS_PENDING
8565 || abs(cur_beval->x - pt.x) > 3
8566 || abs(cur_beval->y - pt.y) > 3))
8567 {
8568 /* Pointer resting in one place long enough, it's time to show
8569 * the tooltip. */
8570 cur_beval->showState = ShS_PENDING;
8571 cur_beval->x = pt.x;
8572 cur_beval->y = pt.y;
8573
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008574 // TRACE0("BevalTimerProc: sending request");
Bram Moolenaar071d4272004-06-13 20:20:40 +00008575
8576 if (cur_beval->msgCB != NULL)
8577 (*cur_beval->msgCB)(cur_beval, 0);
8578 }
8579}
8580
8581 void
Bram Moolenaar1266d672017-02-01 13:43:36 +01008582gui_mch_disable_beval_area(BalloonEval *beval UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008583{
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008584 // TRACE0("gui_mch_disable_beval_area {{{");
Bram Moolenaar071d4272004-06-13 20:20:40 +00008585 KillTimer(s_textArea, BevalTimerId);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008586 // TRACE0("gui_mch_disable_beval_area }}}");
Bram Moolenaar071d4272004-06-13 20:20:40 +00008587}
8588
8589 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01008590gui_mch_enable_beval_area(BalloonEval *beval)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008591{
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008592 // TRACE0("gui_mch_enable_beval_area |||");
Bram Moolenaar071d4272004-06-13 20:20:40 +00008593 if (beval == NULL)
8594 return;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008595 // TRACE0("gui_mch_enable_beval_area {{{");
Bram Moolenaar167632f2010-05-26 21:42:54 +02008596 BevalTimerId = SetTimer(s_textArea, 0, (UINT)(p_bdlay / 2), BevalTimerProc);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008597 // TRACE0("gui_mch_enable_beval_area }}}");
Bram Moolenaar071d4272004-06-13 20:20:40 +00008598}
8599
8600 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01008601gui_mch_post_balloon(BalloonEval *beval, char_u *mesg)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008602{
8603 POINT pt;
Bram Moolenaar1c465442017-03-12 20:10:05 +01008604
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008605 // TRACE0("gui_mch_post_balloon {{{");
Bram Moolenaar071d4272004-06-13 20:20:40 +00008606 if (beval->showState == ShS_SHOWING)
8607 return;
8608 GetCursorPos(&pt);
8609 ScreenToClient(s_textArea, &pt);
8610
8611 if (abs(beval->x - pt.x) < 3 && abs(beval->y - pt.y) < 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008612 {
Bram Moolenaar1c465442017-03-12 20:10:05 +01008613 /* cursor is still here */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008614 gui_mch_disable_beval_area(cur_beval);
8615 beval->showState = ShS_SHOWING;
Bram Moolenaar418f81b2016-02-16 20:12:02 +01008616 make_tooltip(beval, (char *)mesg, pt);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008617 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008618 // TRACE0("gui_mch_post_balloon }}}");
Bram Moolenaar071d4272004-06-13 20:20:40 +00008619}
8620
8621 BalloonEval *
Bram Moolenaar68c2f632016-01-30 17:24:07 +01008622gui_mch_create_beval_area(
8623 void *target, /* ignored, always use s_textArea */
8624 char_u *mesg,
8625 void (*mesgCB)(BalloonEval *, int),
8626 void *clientData)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008627{
8628 /* partially stolen from gui_beval.c */
8629 BalloonEval *beval;
8630
8631 if (mesg != NULL && mesgCB != NULL)
8632 {
Bram Moolenaar95f09602016-11-10 20:01:45 +01008633 IEMSG(_("E232: Cannot create BalloonEval with both message and callback"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00008634 return NULL;
8635 }
8636
8637 beval = (BalloonEval *)alloc(sizeof(BalloonEval));
8638 if (beval != NULL)
8639 {
8640 beval->target = s_textArea;
8641 beval->balloon = NULL;
8642
8643 beval->showState = ShS_NEUTRAL;
8644 beval->x = 0;
8645 beval->y = 0;
8646 beval->msg = mesg;
8647 beval->msgCB = mesgCB;
8648 beval->clientData = clientData;
8649
8650 InitCommonControls();
Bram Moolenaar071d4272004-06-13 20:20:40 +00008651 cur_beval = beval;
8652
8653 if (p_beval)
8654 gui_mch_enable_beval_area(beval);
8655
8656 }
8657 return beval;
8658}
8659
8660 static void
Bram Moolenaar1266d672017-02-01 13:43:36 +01008661Handle_WM_Notify(HWND hwnd UNUSED, LPNMHDR pnmh)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008662{
8663 if (pnmh->idFrom != ID_BEVAL_TOOLTIP) /* it is not our tooltip */
8664 return;
8665
8666 if (cur_beval != NULL)
8667 {
Bram Moolenaar45360022005-07-21 21:08:21 +00008668 switch (pnmh->code)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008669 {
Bram Moolenaar45360022005-07-21 21:08:21 +00008670 case TTN_SHOW:
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008671 // TRACE0("TTN_SHOW {{{");
8672 // TRACE0("TTN_SHOW }}}");
Bram Moolenaar45360022005-07-21 21:08:21 +00008673 break;
8674 case TTN_POP: /* Before tooltip disappear */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008675 // TRACE0("TTN_POP {{{");
Bram Moolenaar071d4272004-06-13 20:20:40 +00008676 delete_tooltip(cur_beval);
8677 gui_mch_enable_beval_area(cur_beval);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008678 // TRACE0("TTN_POP }}}");
Bram Moolenaar071d4272004-06-13 20:20:40 +00008679
8680 cur_beval->showState = ShS_NEUTRAL;
Bram Moolenaar45360022005-07-21 21:08:21 +00008681 break;
8682 case TTN_GETDISPINFO:
Bram Moolenaar6c9176d2008-01-03 19:45:15 +00008683 {
8684 /* if you get there then we have new common controls */
8685 NMTTDISPINFO_NEW *info = (NMTTDISPINFO_NEW *)pnmh;
8686 info->lpszText = (LPSTR)info->lParam;
8687 info->uFlags |= TTF_DI_SETITEM;
8688 }
Bram Moolenaar45360022005-07-21 21:08:21 +00008689 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008690 }
8691 }
8692}
8693
8694 static void
8695TrackUserActivity(UINT uMsg)
8696{
8697 if ((uMsg >= WM_MOUSEFIRST && uMsg <= WM_MOUSELAST)
8698 || (uMsg >= WM_KEYFIRST && uMsg <= WM_KEYLAST))
8699 LastActivity = GetTickCount();
8700}
8701
8702 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01008703gui_mch_destroy_beval_area(BalloonEval *beval)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008704{
8705 vim_free(beval);
8706}
8707#endif /* FEAT_BEVAL */
8708
8709#if defined(FEAT_NETBEANS_INTG) || defined(PROTO)
8710/*
8711 * We have multiple signs to draw at the same location. Draw the
8712 * multi-sign indicator (down-arrow) instead. This is the Win32 version.
8713 */
8714 void
8715netbeans_draw_multisign_indicator(int row)
8716{
8717 int i;
8718 int y;
8719 int x;
8720
Bram Moolenaarb26e6322010-05-22 21:34:09 +02008721 if (!netbeans_active())
Bram Moolenaarcc448b32010-07-14 16:52:17 +02008722 return;
Bram Moolenaarb26e6322010-05-22 21:34:09 +02008723
Bram Moolenaar071d4272004-06-13 20:20:40 +00008724 x = 0;
8725 y = TEXT_Y(row);
8726
8727 for (i = 0; i < gui.char_height - 3; i++)
8728 SetPixel(s_hdc, x+2, y++, gui.currFgColor);
8729
8730 SetPixel(s_hdc, x+0, y, gui.currFgColor);
8731 SetPixel(s_hdc, x+2, y, gui.currFgColor);
8732 SetPixel(s_hdc, x+4, y++, gui.currFgColor);
8733 SetPixel(s_hdc, x+1, y, gui.currFgColor);
8734 SetPixel(s_hdc, x+2, y, gui.currFgColor);
8735 SetPixel(s_hdc, x+3, y++, gui.currFgColor);
8736 SetPixel(s_hdc, x+2, y, gui.currFgColor);
8737}
Bram Moolenaare0874f82016-01-24 20:36:41 +01008738#endif