blob: eae37bf4d9dd1cb0fbce7498fbad790206f856c1 [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
561/* ARGSUSED */
562 static VOID CALLBACK
563_OnBlinkTimer(
564 HWND hwnd,
565 UINT uMsg,
566 UINT idEvent,
567 DWORD dwTime)
568{
569 MSG msg;
570
571 /*
572 TRACE2("Got timer event, id %d, blink_timer %d\n", idEvent, blink_timer);
573 */
574
575 KillTimer(NULL, idEvent);
576
577 /* Eat spurious WM_TIMER messages */
578 while (pPeekMessage(&msg, hwnd, WM_TIMER, WM_TIMER, PM_REMOVE))
579 ;
580
581 if (blink_state == BLINK_ON)
582 {
583 gui_undraw_cursor();
584 blink_state = BLINK_OFF;
585 blink_timer = (UINT) SetTimer(NULL, 0, (UINT)blink_offtime,
586 (TIMERPROC)_OnBlinkTimer);
587 }
588 else
589 {
590 gui_update_cursor(TRUE, FALSE);
591 blink_state = BLINK_ON;
592 blink_timer = (UINT) SetTimer(NULL, 0, (UINT)blink_ontime,
593 (TIMERPROC)_OnBlinkTimer);
594 }
595}
596
597 static void
598gui_mswin_rm_blink_timer(void)
599{
600 MSG msg;
601
602 if (blink_timer != 0)
603 {
604 KillTimer(NULL, blink_timer);
605 /* Eat spurious WM_TIMER messages */
606 while (pPeekMessage(&msg, s_hwnd, WM_TIMER, WM_TIMER, PM_REMOVE))
607 ;
608 blink_timer = 0;
609 }
610}
611
612/*
613 * Stop the cursor blinking. Show the cursor if it wasn't shown.
614 */
615 void
616gui_mch_stop_blink(void)
617{
618 gui_mswin_rm_blink_timer();
619 if (blink_state == BLINK_OFF)
620 gui_update_cursor(TRUE, FALSE);
621 blink_state = BLINK_NONE;
622}
623
624/*
625 * Start the cursor blinking. If it was already blinking, this restarts the
626 * waiting time and shows the cursor.
627 */
628 void
629gui_mch_start_blink(void)
630{
631 gui_mswin_rm_blink_timer();
632
633 /* Only switch blinking on if none of the times is zero */
634 if (blink_waittime && blink_ontime && blink_offtime && gui.in_focus)
635 {
636 blink_timer = (UINT)SetTimer(NULL, 0, (UINT)blink_waittime,
637 (TIMERPROC)_OnBlinkTimer);
638 blink_state = BLINK_ON;
639 gui_update_cursor(TRUE, FALSE);
640 }
641}
642
643/*
644 * Call-back routines.
645 */
646
647/*ARGSUSED*/
648 static VOID CALLBACK
649_OnTimer(
650 HWND hwnd,
651 UINT uMsg,
652 UINT idEvent,
653 DWORD dwTime)
654{
655 MSG msg;
656
657 /*
658 TRACE2("Got timer event, id %d, s_wait_timer %d\n", idEvent, s_wait_timer);
659 */
660 KillTimer(NULL, idEvent);
661 s_timed_out = TRUE;
662
663 /* Eat spurious WM_TIMER messages */
664 while (pPeekMessage(&msg, hwnd, WM_TIMER, WM_TIMER, PM_REMOVE))
665 ;
666 if (idEvent == s_wait_timer)
667 s_wait_timer = 0;
668}
669
670/*ARGSUSED*/
671 static void
672_OnDeadChar(
673 HWND hwnd,
674 UINT ch,
675 int cRepeat)
676{
677 dead_key = 1;
678}
679
680/*
681 * Convert Unicode character "ch" to bytes in "string[slen]".
682 * When "had_alt" is TRUE the ALT key was included in "ch".
683 * Return the length.
684 */
685 static int
686char_to_string(int ch, char_u *string, int slen, int had_alt)
687{
688 int len;
689 int i;
690#ifdef FEAT_MBYTE
691 WCHAR wstring[2];
Bram Moolenaar945ec092016-06-08 21:17:43 +0200692 char_u *ws = NULL;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100693
Bram Moolenaarcea912a2016-10-12 14:20:24 +0200694 wstring[0] = ch;
695 len = 1;
696
697 /* "ch" is a UTF-16 character. Convert it to a string of bytes. When
698 * "enc_codepage" is non-zero use the standard Win32 function,
699 * otherwise use our own conversion function (e.g., for UTF-8). */
700 if (enc_codepage > 0)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100701 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +0200702 len = WideCharToMultiByte(enc_codepage, 0, wstring, len,
703 (LPSTR)string, slen, 0, NULL);
704 /* If we had included the ALT key into the character but now the
705 * upper bit is no longer set, that probably means the conversion
706 * failed. Convert the original character and set the upper bit
707 * afterwards. */
708 if (had_alt && len == 1 && ch >= 0x80 && string[0] < 0x80)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100709 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +0200710 wstring[0] = ch & 0x7f;
711 len = WideCharToMultiByte(enc_codepage, 0, wstring, len,
712 (LPSTR)string, slen, 0, NULL);
713 if (len == 1) /* safety check */
714 string[0] |= 0x80;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100715 }
716 }
717 else
718 {
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100719 len = 1;
Bram Moolenaarcea912a2016-10-12 14:20:24 +0200720 ws = utf16_to_enc(wstring, &len);
721 if (ws == NULL)
722 len = 0;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100723 else
724 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +0200725 if (len > slen) /* just in case */
726 len = slen;
727 mch_memmove(string, ws, len);
728 vim_free(ws);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100729 }
730 }
731
732 if (len == 0)
733#endif
734 {
735 string[0] = ch;
736 len = 1;
737 }
738
739 for (i = 0; i < len; ++i)
740 if (string[i] == CSI && len <= slen - 2)
741 {
742 /* Insert CSI as K_CSI. */
743 mch_memmove(string + i + 3, string + i + 1, len - i - 1);
744 string[++i] = KS_EXTRA;
745 string[++i] = (int)KE_CSI;
746 len += 2;
747 }
748
749 return len;
750}
751
752/*
753 * Key hit, add it to the input buffer.
754 */
755/*ARGSUSED*/
756 static void
757_OnChar(
758 HWND hwnd,
759 UINT ch,
760 int cRepeat)
761{
762 char_u string[40];
763 int len = 0;
764
765 dead_key = 0;
766
767 len = char_to_string(ch, string, 40, FALSE);
768 if (len == 1 && string[0] == Ctrl_C && ctrl_c_interrupts)
769 {
770 trash_input_buf();
771 got_int = TRUE;
772 }
773
774 add_to_input_buf(string, len);
775}
776
777/*
778 * Alt-Key hit, add it to the input buffer.
779 */
780/*ARGSUSED*/
781 static void
782_OnSysChar(
783 HWND hwnd,
784 UINT cch,
785 int cRepeat)
786{
787 char_u string[40]; /* Enough for multibyte character */
788 int len;
789 int modifiers;
790 int ch = cch; /* special keys are negative */
791
792 dead_key = 0;
793
794 /* TRACE("OnSysChar(%d, %c)\n", ch, ch); */
795
796 /* OK, we have a character key (given by ch) which was entered with the
797 * ALT key pressed. Eg, if the user presses Alt-A, then ch == 'A'. Note
798 * that the system distinguishes Alt-a and Alt-A (Alt-Shift-a unless
799 * CAPSLOCK is pressed) at this point.
800 */
801 modifiers = MOD_MASK_ALT;
802 if (GetKeyState(VK_SHIFT) & 0x8000)
803 modifiers |= MOD_MASK_SHIFT;
804 if (GetKeyState(VK_CONTROL) & 0x8000)
805 modifiers |= MOD_MASK_CTRL;
806
807 ch = simplify_key(ch, &modifiers);
808 /* remove the SHIFT modifier for keys where it's already included, e.g.,
809 * '(' and '*' */
810 if (ch < 0x100 && !isalpha(ch) && isprint(ch))
811 modifiers &= ~MOD_MASK_SHIFT;
812
813 /* Interpret the ALT key as making the key META, include SHIFT, etc. */
814 ch = extract_modifiers(ch, &modifiers);
815 if (ch == CSI)
816 ch = K_CSI;
817
818 len = 0;
819 if (modifiers)
820 {
821 string[len++] = CSI;
822 string[len++] = KS_MODIFIER;
823 string[len++] = modifiers;
824 }
825
826 if (IS_SPECIAL((int)ch))
827 {
828 string[len++] = CSI;
829 string[len++] = K_SECOND((int)ch);
830 string[len++] = K_THIRD((int)ch);
831 }
832 else
833 {
834 /* Although the documentation isn't clear about it, we assume "ch" is
835 * a Unicode character. */
836 len += char_to_string(ch, string + len, 40 - len, TRUE);
837 }
838
839 add_to_input_buf(string, len);
840}
841
842 static void
843_OnMouseEvent(
844 int button,
845 int x,
846 int y,
847 int repeated_click,
848 UINT keyFlags)
849{
850 int vim_modifiers = 0x0;
851
852 s_getting_focus = FALSE;
853
854 if (keyFlags & MK_SHIFT)
855 vim_modifiers |= MOUSE_SHIFT;
856 if (keyFlags & MK_CONTROL)
857 vim_modifiers |= MOUSE_CTRL;
858 if (GetKeyState(VK_MENU) & 0x8000)
859 vim_modifiers |= MOUSE_ALT;
860
861 gui_send_mouse_event(button, x, y, repeated_click, vim_modifiers);
862}
863
864/*ARGSUSED*/
865 static void
866_OnMouseButtonDown(
867 HWND hwnd,
868 BOOL fDoubleClick,
869 int x,
870 int y,
871 UINT keyFlags)
872{
873 static LONG s_prevTime = 0;
874
875 LONG currentTime = GetMessageTime();
876 int button = -1;
877 int repeated_click;
878
879 /* Give main window the focus: this is so the cursor isn't hollow. */
880 (void)SetFocus(s_hwnd);
881
882 if (s_uMsg == WM_LBUTTONDOWN || s_uMsg == WM_LBUTTONDBLCLK)
883 button = MOUSE_LEFT;
884 else if (s_uMsg == WM_MBUTTONDOWN || s_uMsg == WM_MBUTTONDBLCLK)
885 button = MOUSE_MIDDLE;
886 else if (s_uMsg == WM_RBUTTONDOWN || s_uMsg == WM_RBUTTONDBLCLK)
887 button = MOUSE_RIGHT;
888 else if (s_uMsg == WM_XBUTTONDOWN || s_uMsg == WM_XBUTTONDBLCLK)
889 {
890#ifndef GET_XBUTTON_WPARAM
891# define GET_XBUTTON_WPARAM(wParam) (HIWORD(wParam))
892#endif
893 button = ((GET_XBUTTON_WPARAM(s_wParam) == 1) ? MOUSE_X1 : MOUSE_X2);
894 }
895 else if (s_uMsg == WM_CAPTURECHANGED)
896 {
897 /* on W95/NT4, somehow you get in here with an odd Msg
898 * if you press one button while holding down the other..*/
899 if (s_button_pending == MOUSE_LEFT)
900 button = MOUSE_RIGHT;
901 else
902 button = MOUSE_LEFT;
903 }
904 if (button >= 0)
905 {
906 repeated_click = ((int)(currentTime - s_prevTime) < p_mouset);
907
908 /*
909 * Holding down the left and right buttons simulates pushing the middle
910 * button.
911 */
912 if (repeated_click
913 && ((button == MOUSE_LEFT && s_button_pending == MOUSE_RIGHT)
914 || (button == MOUSE_RIGHT
915 && s_button_pending == MOUSE_LEFT)))
916 {
917 /*
918 * Hmm, gui.c will ignore more than one button down at a time, so
919 * pretend we let go of it first.
920 */
921 gui_send_mouse_event(MOUSE_RELEASE, x, y, FALSE, 0x0);
922 button = MOUSE_MIDDLE;
923 repeated_click = FALSE;
924 s_button_pending = -1;
925 _OnMouseEvent(button, x, y, repeated_click, keyFlags);
926 }
927 else if ((repeated_click)
928 || (mouse_model_popup() && (button == MOUSE_RIGHT)))
929 {
930 if (s_button_pending > -1)
931 {
932 _OnMouseEvent(s_button_pending, x, y, FALSE, keyFlags);
933 s_button_pending = -1;
934 }
935 /* TRACE("Button down at x %d, y %d\n", x, y); */
936 _OnMouseEvent(button, x, y, repeated_click, keyFlags);
937 }
938 else
939 {
940 /*
941 * If this is the first press (i.e. not a multiple click) don't
942 * action immediately, but store and wait for:
943 * i) button-up
944 * ii) mouse move
945 * iii) another button press
946 * before using it.
947 * This enables us to make left+right simulate middle button,
948 * without left or right being actioned first. The side-effect is
949 * that if you click and hold the mouse without dragging, the
950 * cursor doesn't move until you release the button. In practice
951 * this is hardly a problem.
952 */
953 s_button_pending = button;
954 s_x_pending = x;
955 s_y_pending = y;
956 s_kFlags_pending = keyFlags;
957 }
958
959 s_prevTime = currentTime;
960 }
961}
962
963/*ARGSUSED*/
964 static void
965_OnMouseMoveOrRelease(
966 HWND hwnd,
967 int x,
968 int y,
969 UINT keyFlags)
970{
971 int button;
972
973 s_getting_focus = FALSE;
974 if (s_button_pending > -1)
975 {
976 /* Delayed action for mouse down event */
977 _OnMouseEvent(s_button_pending, s_x_pending,
978 s_y_pending, FALSE, s_kFlags_pending);
979 s_button_pending = -1;
980 }
981 if (s_uMsg == WM_MOUSEMOVE)
982 {
983 /*
984 * It's only a MOUSE_DRAG if one or more mouse buttons are being held
985 * down.
986 */
987 if (!(keyFlags & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON
988 | MK_XBUTTON1 | MK_XBUTTON2)))
989 {
990 gui_mouse_moved(x, y);
991 return;
992 }
993
994 /*
995 * While button is down, keep grabbing mouse move events when
996 * the mouse goes outside the window
997 */
998 SetCapture(s_textArea);
999 button = MOUSE_DRAG;
1000 /* TRACE(" move at x %d, y %d\n", x, y); */
1001 }
1002 else
1003 {
1004 ReleaseCapture();
1005 button = MOUSE_RELEASE;
1006 /* TRACE(" up at x %d, y %d\n", x, y); */
1007 }
1008
1009 _OnMouseEvent(button, x, y, FALSE, keyFlags);
1010}
1011
1012#ifdef FEAT_MENU
1013/*
1014 * Find the vimmenu_T with the given id
1015 */
1016 static vimmenu_T *
1017gui_mswin_find_menu(
1018 vimmenu_T *pMenu,
1019 int id)
1020{
1021 vimmenu_T *pChildMenu;
1022
1023 while (pMenu)
1024 {
1025 if (pMenu->id == (UINT)id)
1026 break;
1027 if (pMenu->children != NULL)
1028 {
1029 pChildMenu = gui_mswin_find_menu(pMenu->children, id);
1030 if (pChildMenu)
1031 {
1032 pMenu = pChildMenu;
1033 break;
1034 }
1035 }
1036 pMenu = pMenu->next;
1037 }
1038 return pMenu;
1039}
1040
1041/*ARGSUSED*/
1042 static void
1043_OnMenu(
1044 HWND hwnd,
1045 int id,
1046 HWND hwndCtl,
1047 UINT codeNotify)
1048{
1049 vimmenu_T *pMenu;
1050
1051 pMenu = gui_mswin_find_menu(root_menu, id);
1052 if (pMenu)
1053 gui_menu_cb(pMenu);
1054}
1055#endif
1056
1057#ifdef MSWIN_FIND_REPLACE
Bram Moolenaarcea912a2016-10-12 14:20:24 +02001058# ifdef FEAT_MBYTE
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001059/*
1060 * copy useful data from structure LPFINDREPLACE to structure LPFINDREPLACEW
1061 */
1062 static void
1063findrep_atow(LPFINDREPLACEW lpfrw, LPFINDREPLACE lpfr)
1064{
1065 WCHAR *wp;
1066
1067 lpfrw->hwndOwner = lpfr->hwndOwner;
1068 lpfrw->Flags = lpfr->Flags;
1069
1070 wp = enc_to_utf16((char_u *)lpfr->lpstrFindWhat, NULL);
1071 wcsncpy(lpfrw->lpstrFindWhat, wp, lpfrw->wFindWhatLen - 1);
1072 vim_free(wp);
1073
1074 /* the field "lpstrReplaceWith" doesn't need to be copied */
1075}
1076
1077/*
1078 * copy useful data from structure LPFINDREPLACEW to structure LPFINDREPLACE
1079 */
1080 static void
1081findrep_wtoa(LPFINDREPLACE lpfr, LPFINDREPLACEW lpfrw)
1082{
1083 char_u *p;
1084
1085 lpfr->Flags = lpfrw->Flags;
1086
1087 p = utf16_to_enc((short_u*)lpfrw->lpstrFindWhat, NULL);
1088 vim_strncpy((char_u *)lpfr->lpstrFindWhat, p, lpfr->wFindWhatLen - 1);
1089 vim_free(p);
1090
1091 p = utf16_to_enc((short_u*)lpfrw->lpstrReplaceWith, NULL);
1092 vim_strncpy((char_u *)lpfr->lpstrReplaceWith, p, lpfr->wReplaceWithLen - 1);
1093 vim_free(p);
1094}
1095# endif
1096
1097/*
1098 * Handle a Find/Replace window message.
1099 */
1100 static void
1101_OnFindRepl(void)
1102{
1103 int flags = 0;
1104 int down;
1105
Bram Moolenaarcea912a2016-10-12 14:20:24 +02001106# ifdef FEAT_MBYTE
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001107 /* If the OS is Windows NT, and 'encoding' differs from active codepage:
1108 * convert text from wide string. */
Bram Moolenaarcea912a2016-10-12 14:20:24 +02001109 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001110 {
1111 findrep_wtoa(&s_findrep_struct, &s_findrep_struct_w);
1112 }
1113# endif
1114
1115 if (s_findrep_struct.Flags & FR_DIALOGTERM)
1116 /* Give main window the focus back. */
1117 (void)SetFocus(s_hwnd);
1118
1119 if (s_findrep_struct.Flags & FR_FINDNEXT)
1120 {
1121 flags = FRD_FINDNEXT;
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_REPLACE)
1128 {
1129 flags = FRD_REPLACE;
1130
1131 /* Give main window the focus back: this is so the cursor isn't
1132 * hollow. */
1133 (void)SetFocus(s_hwnd);
1134 }
1135 else if (s_findrep_struct.Flags & FR_REPLACEALL)
1136 {
1137 flags = FRD_REPLACEALL;
1138 }
1139
1140 if (flags != 0)
1141 {
1142 /* Call the generic GUI function to do the actual work. */
1143 if (s_findrep_struct.Flags & FR_WHOLEWORD)
1144 flags |= FRD_WHOLE_WORD;
1145 if (s_findrep_struct.Flags & FR_MATCHCASE)
1146 flags |= FRD_MATCH_CASE;
1147 down = (s_findrep_struct.Flags & FR_DOWN) != 0;
1148 gui_do_findrepl(flags, (char_u *)s_findrep_struct.lpstrFindWhat,
1149 (char_u *)s_findrep_struct.lpstrReplaceWith, down);
1150 }
1151}
1152#endif
1153
1154 static void
1155HandleMouseHide(UINT uMsg, LPARAM lParam)
1156{
1157 static LPARAM last_lParam = 0L;
1158
1159 /* We sometimes get a mousemove when the mouse didn't move... */
1160 if (uMsg == WM_MOUSEMOVE || uMsg == WM_NCMOUSEMOVE)
1161 {
1162 if (lParam == last_lParam)
1163 return;
1164 last_lParam = lParam;
1165 }
1166
1167 /* Handle specially, to centralise coding. We need to be sure we catch all
1168 * possible events which should cause us to restore the cursor (as it is a
1169 * shared resource, we take full responsibility for it).
1170 */
1171 switch (uMsg)
1172 {
1173 case WM_KEYUP:
1174 case WM_CHAR:
1175 /*
1176 * blank out the pointer if necessary
1177 */
1178 if (p_mh)
1179 gui_mch_mousehide(TRUE);
1180 break;
1181
1182 case WM_SYSKEYUP: /* show the pointer when a system-key is pressed */
1183 case WM_SYSCHAR:
1184 case WM_MOUSEMOVE: /* show the pointer on any mouse action */
1185 case WM_LBUTTONDOWN:
1186 case WM_LBUTTONUP:
1187 case WM_MBUTTONDOWN:
1188 case WM_MBUTTONUP:
1189 case WM_RBUTTONDOWN:
1190 case WM_RBUTTONUP:
1191 case WM_XBUTTONDOWN:
1192 case WM_XBUTTONUP:
1193 case WM_NCMOUSEMOVE:
1194 case WM_NCLBUTTONDOWN:
1195 case WM_NCLBUTTONUP:
1196 case WM_NCMBUTTONDOWN:
1197 case WM_NCMBUTTONUP:
1198 case WM_NCRBUTTONDOWN:
1199 case WM_NCRBUTTONUP:
1200 case WM_KILLFOCUS:
1201 /*
1202 * if the pointer is currently hidden, then we should show it.
1203 */
1204 gui_mch_mousehide(FALSE);
1205 break;
1206 }
1207}
1208
1209 static LRESULT CALLBACK
1210_TextAreaWndProc(
1211 HWND hwnd,
1212 UINT uMsg,
1213 WPARAM wParam,
1214 LPARAM lParam)
1215{
1216 /*
1217 TRACE("TextAreaWndProc: hwnd = %08x, msg = %x, wParam = %x, lParam = %x\n",
1218 hwnd, uMsg, wParam, lParam);
1219 */
1220
1221 HandleMouseHide(uMsg, lParam);
1222
1223 s_uMsg = uMsg;
1224 s_wParam = wParam;
1225 s_lParam = lParam;
1226
1227#ifdef FEAT_BEVAL
1228 TrackUserActivity(uMsg);
1229#endif
1230
1231 switch (uMsg)
1232 {
1233 HANDLE_MSG(hwnd, WM_LBUTTONDBLCLK,_OnMouseButtonDown);
1234 HANDLE_MSG(hwnd, WM_LBUTTONDOWN,_OnMouseButtonDown);
1235 HANDLE_MSG(hwnd, WM_LBUTTONUP, _OnMouseMoveOrRelease);
1236 HANDLE_MSG(hwnd, WM_MBUTTONDBLCLK,_OnMouseButtonDown);
1237 HANDLE_MSG(hwnd, WM_MBUTTONDOWN,_OnMouseButtonDown);
1238 HANDLE_MSG(hwnd, WM_MBUTTONUP, _OnMouseMoveOrRelease);
1239 HANDLE_MSG(hwnd, WM_MOUSEMOVE, _OnMouseMoveOrRelease);
1240 HANDLE_MSG(hwnd, WM_PAINT, _OnPaint);
1241 HANDLE_MSG(hwnd, WM_RBUTTONDBLCLK,_OnMouseButtonDown);
1242 HANDLE_MSG(hwnd, WM_RBUTTONDOWN,_OnMouseButtonDown);
1243 HANDLE_MSG(hwnd, WM_RBUTTONUP, _OnMouseMoveOrRelease);
1244 HANDLE_MSG(hwnd, WM_XBUTTONDBLCLK,_OnMouseButtonDown);
1245 HANDLE_MSG(hwnd, WM_XBUTTONDOWN,_OnMouseButtonDown);
1246 HANDLE_MSG(hwnd, WM_XBUTTONUP, _OnMouseMoveOrRelease);
1247
1248#ifdef FEAT_BEVAL
1249 case WM_NOTIFY: Handle_WM_Notify(hwnd, (LPNMHDR)lParam);
1250 return TRUE;
1251#endif
1252 default:
1253 return MyWindowProc(hwnd, uMsg, wParam, lParam);
1254 }
1255}
1256
Bram Moolenaarcea912a2016-10-12 14:20:24 +02001257#if defined(FEAT_MBYTE) \
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001258 || defined(GLOBAL_IME) \
1259 || defined(PROTO)
1260# ifdef PROTO
1261typedef int WINAPI;
1262# endif
1263
1264 LRESULT WINAPI
1265vim_WindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
1266{
1267# ifdef GLOBAL_IME
1268 return global_ime_DefWindowProc(hwnd, message, wParam, lParam);
1269# else
1270 if (wide_WindowProc)
1271 return DefWindowProcW(hwnd, message, wParam, lParam);
1272 return DefWindowProc(hwnd, message, wParam, lParam);
1273#endif
1274}
1275#endif
1276
1277/*
1278 * Called when the foreground or background color has been changed.
1279 */
1280 void
1281gui_mch_new_colors(void)
1282{
1283 /* nothing to do? */
1284}
1285
1286/*
1287 * Set the colors to their default values.
1288 */
1289 void
1290gui_mch_def_colors(void)
1291{
1292 gui.norm_pixel = GetSysColor(COLOR_WINDOWTEXT);
1293 gui.back_pixel = GetSysColor(COLOR_WINDOW);
1294 gui.def_norm_pixel = gui.norm_pixel;
1295 gui.def_back_pixel = gui.back_pixel;
1296}
1297
1298/*
1299 * Open the GUI window which was created by a call to gui_mch_init().
1300 */
1301 int
1302gui_mch_open(void)
1303{
1304#ifndef SW_SHOWDEFAULT
1305# define SW_SHOWDEFAULT 10 /* Borland 5.0 doesn't have it */
1306#endif
1307 /* Actually open the window, if not already visible
1308 * (may be done already in gui_mch_set_shellsize) */
1309 if (!IsWindowVisible(s_hwnd))
1310 ShowWindow(s_hwnd, SW_SHOWDEFAULT);
1311
1312#ifdef MSWIN_FIND_REPLACE
1313 /* Init replace string here, so that we keep it when re-opening the
1314 * dialog. */
1315 s_findrep_struct.lpstrReplaceWith[0] = NUL;
1316#endif
1317
1318 return OK;
1319}
1320
1321/*
1322 * Get the position of the top left corner of the window.
1323 */
1324 int
1325gui_mch_get_winpos(int *x, int *y)
1326{
1327 RECT rect;
1328
1329 GetWindowRect(s_hwnd, &rect);
1330 *x = rect.left;
1331 *y = rect.top;
1332 return OK;
1333}
1334
1335/*
1336 * Set the position of the top left corner of the window to the given
1337 * coordinates.
1338 */
1339 void
1340gui_mch_set_winpos(int x, int y)
1341{
1342 SetWindowPos(s_hwnd, NULL, x, y, 0, 0,
1343 SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
1344}
1345 void
1346gui_mch_set_text_area_pos(int x, int y, int w, int h)
1347{
1348 static int oldx = 0;
1349 static int oldy = 0;
1350
1351 SetWindowPos(s_textArea, NULL, x, y, w, h, SWP_NOZORDER | SWP_NOACTIVATE);
1352
1353#ifdef FEAT_TOOLBAR
1354 if (vim_strchr(p_go, GO_TOOLBAR) != NULL)
1355 SendMessage(s_toolbarhwnd, WM_SIZE,
1356 (WPARAM)0, (LPARAM)(w + ((long)(TOOLBAR_BUTTON_HEIGHT+8)<<16)));
1357#endif
1358#if defined(FEAT_GUI_TABLINE)
1359 if (showing_tabline)
1360 {
1361 int top = 0;
1362 RECT rect;
1363
1364# ifdef FEAT_TOOLBAR
1365 if (vim_strchr(p_go, GO_TOOLBAR) != NULL)
1366 top = TOOLBAR_BUTTON_HEIGHT + TOOLBAR_BORDER_HEIGHT;
1367# endif
1368 GetClientRect(s_hwnd, &rect);
1369 MoveWindow(s_tabhwnd, 0, top, rect.right, gui.tabline_height, TRUE);
1370 }
1371#endif
1372
1373 /* When side scroll bar is unshown, the size of window will change.
1374 * then, the text area move left or right. thus client rect should be
1375 * forcedly redrawn. (Yasuhiro Matsumoto) */
1376 if (oldx != x || oldy != y)
1377 {
1378 InvalidateRect(s_hwnd, NULL, FALSE);
1379 oldx = x;
1380 oldy = y;
1381 }
1382}
1383
1384
1385/*
1386 * Scrollbar stuff:
1387 */
1388
1389 void
1390gui_mch_enable_scrollbar(
1391 scrollbar_T *sb,
1392 int flag)
1393{
1394 ShowScrollBar(sb->id, SB_CTL, flag);
1395
1396 /* TODO: When the window is maximized, the size of the window stays the
1397 * same, thus the size of the text area changes. On Win98 it's OK, on Win
1398 * NT 4.0 it's not... */
1399}
1400
1401 void
1402gui_mch_set_scrollbar_pos(
1403 scrollbar_T *sb,
1404 int x,
1405 int y,
1406 int w,
1407 int h)
1408{
1409 SetWindowPos(sb->id, NULL, x, y, w, h,
1410 SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW);
1411}
1412
1413 void
1414gui_mch_create_scrollbar(
1415 scrollbar_T *sb,
1416 int orient) /* SBAR_VERT or SBAR_HORIZ */
1417{
1418 sb->id = CreateWindow(
1419 "SCROLLBAR", "Scrollbar",
1420 WS_CHILD | ((orient == SBAR_VERT) ? SBS_VERT : SBS_HORZ), 0, 0,
1421 10, /* Any value will do for now */
1422 10, /* Any value will do for now */
1423 s_hwnd, NULL,
1424 s_hinst, NULL);
1425}
1426
1427/*
1428 * Find the scrollbar with the given hwnd.
1429 */
1430 static scrollbar_T *
1431gui_mswin_find_scrollbar(HWND hwnd)
1432{
1433 win_T *wp;
1434
1435 if (gui.bottom_sbar.id == hwnd)
1436 return &gui.bottom_sbar;
1437 FOR_ALL_WINDOWS(wp)
1438 {
1439 if (wp->w_scrollbars[SBAR_LEFT].id == hwnd)
1440 return &wp->w_scrollbars[SBAR_LEFT];
1441 if (wp->w_scrollbars[SBAR_RIGHT].id == hwnd)
1442 return &wp->w_scrollbars[SBAR_RIGHT];
1443 }
1444 return NULL;
1445}
1446
1447/*
1448 * Get the character size of a font.
1449 */
1450 static void
1451GetFontSize(GuiFont font)
1452{
1453 HWND hwnd = GetDesktopWindow();
1454 HDC hdc = GetWindowDC(hwnd);
1455 HFONT hfntOld = SelectFont(hdc, (HFONT)font);
1456 TEXTMETRIC tm;
1457
1458 GetTextMetrics(hdc, &tm);
1459 gui.char_width = tm.tmAveCharWidth + tm.tmOverhang;
1460
1461 gui.char_height = tm.tmHeight + p_linespace;
1462
1463 SelectFont(hdc, hfntOld);
1464
1465 ReleaseDC(hwnd, hdc);
1466}
1467
1468/*
1469 * Adjust gui.char_height (after 'linespace' was changed).
1470 */
1471 int
1472gui_mch_adjust_charheight(void)
1473{
1474 GetFontSize(gui.norm_font);
1475 return OK;
1476}
1477
1478 static GuiFont
1479get_font_handle(LOGFONT *lf)
1480{
1481 HFONT font = NULL;
1482
1483 /* Load the font */
1484 font = CreateFontIndirect(lf);
1485
1486 if (font == NULL)
1487 return NOFONT;
1488
1489 return (GuiFont)font;
1490}
1491
1492 static int
1493pixels_to_points(int pixels, int vertical)
1494{
1495 int points;
1496 HWND hwnd;
1497 HDC hdc;
1498
1499 hwnd = GetDesktopWindow();
1500 hdc = GetWindowDC(hwnd);
1501
1502 points = MulDiv(pixels, 72,
1503 GetDeviceCaps(hdc, vertical ? LOGPIXELSY : LOGPIXELSX));
1504
1505 ReleaseDC(hwnd, hdc);
1506
1507 return points;
1508}
1509
1510 GuiFont
1511gui_mch_get_font(
1512 char_u *name,
1513 int giveErrorIfMissing)
1514{
1515 LOGFONT lf;
1516 GuiFont font = NOFONT;
1517
1518 if (get_logfont(&lf, name, NULL, giveErrorIfMissing) == OK)
1519 font = get_font_handle(&lf);
1520 if (font == NOFONT && giveErrorIfMissing)
1521 EMSG2(_(e_font), name);
1522 return font;
1523}
1524
1525#if defined(FEAT_EVAL) || defined(PROTO)
1526/*
1527 * Return the name of font "font" in allocated memory.
1528 * Don't know how to get the actual name, thus use the provided name.
1529 */
1530/*ARGSUSED*/
1531 char_u *
1532gui_mch_get_fontname(GuiFont font, char_u *name)
1533{
1534 if (name == NULL)
1535 return NULL;
1536 return vim_strsave(name);
1537}
1538#endif
1539
1540 void
1541gui_mch_free_font(GuiFont font)
1542{
1543 if (font)
1544 DeleteObject((HFONT)font);
1545}
1546
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001547/*
1548 * Return the Pixel value (color) for the given color name.
1549 * Return INVALCOLOR for error.
1550 */
1551 guicolor_T
1552gui_mch_get_color(char_u *name)
1553{
Bram Moolenaarc285fe72016-04-26 21:51:48 +02001554 int i;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001555
1556 typedef struct SysColorTable
1557 {
1558 char *name;
1559 int color;
1560 } SysColorTable;
1561
1562 static SysColorTable sys_table[] =
1563 {
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001564 {"SYS_3DDKSHADOW", COLOR_3DDKSHADOW},
1565 {"SYS_3DHILIGHT", COLOR_3DHILIGHT},
Bram Moolenaarcea912a2016-10-12 14:20:24 +02001566#ifdef COLOR_3DHIGHLIGHT
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001567 {"SYS_3DHIGHLIGHT", COLOR_3DHIGHLIGHT},
1568#endif
1569 {"SYS_BTNHILIGHT", COLOR_BTNHILIGHT},
1570 {"SYS_BTNHIGHLIGHT", COLOR_BTNHIGHLIGHT},
1571 {"SYS_3DLIGHT", COLOR_3DLIGHT},
1572 {"SYS_3DSHADOW", COLOR_3DSHADOW},
1573 {"SYS_DESKTOP", COLOR_DESKTOP},
1574 {"SYS_INFOBK", COLOR_INFOBK},
1575 {"SYS_INFOTEXT", COLOR_INFOTEXT},
1576 {"SYS_3DFACE", COLOR_3DFACE},
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001577 {"SYS_BTNFACE", COLOR_BTNFACE},
1578 {"SYS_BTNSHADOW", COLOR_BTNSHADOW},
1579 {"SYS_ACTIVEBORDER", COLOR_ACTIVEBORDER},
1580 {"SYS_ACTIVECAPTION", COLOR_ACTIVECAPTION},
1581 {"SYS_APPWORKSPACE", COLOR_APPWORKSPACE},
1582 {"SYS_BACKGROUND", COLOR_BACKGROUND},
1583 {"SYS_BTNTEXT", COLOR_BTNTEXT},
1584 {"SYS_CAPTIONTEXT", COLOR_CAPTIONTEXT},
1585 {"SYS_GRAYTEXT", COLOR_GRAYTEXT},
1586 {"SYS_HIGHLIGHT", COLOR_HIGHLIGHT},
1587 {"SYS_HIGHLIGHTTEXT", COLOR_HIGHLIGHTTEXT},
1588 {"SYS_INACTIVEBORDER", COLOR_INACTIVEBORDER},
1589 {"SYS_INACTIVECAPTION", COLOR_INACTIVECAPTION},
1590 {"SYS_INACTIVECAPTIONTEXT", COLOR_INACTIVECAPTIONTEXT},
1591 {"SYS_MENU", COLOR_MENU},
1592 {"SYS_MENUTEXT", COLOR_MENUTEXT},
1593 {"SYS_SCROLLBAR", COLOR_SCROLLBAR},
1594 {"SYS_WINDOW", COLOR_WINDOW},
1595 {"SYS_WINDOWFRAME", COLOR_WINDOWFRAME},
1596 {"SYS_WINDOWTEXT", COLOR_WINDOWTEXT}
1597 };
1598
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001599 /*
1600 * Try to look up a system colour.
1601 */
1602 for (i = 0; i < sizeof(sys_table) / sizeof(sys_table[0]); i++)
1603 if (STRICMP(name, sys_table[i].name) == 0)
1604 return GetSysColor(sys_table[i].color);
1605
Bram Moolenaarab302212016-04-26 20:59:29 +02001606 return gui_get_color_cmn(name);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001607}
Bram Moolenaarc285fe72016-04-26 21:51:48 +02001608
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001609/*
1610 * Return OK if the key with the termcap name "name" is supported.
1611 */
1612 int
1613gui_mch_haskey(char_u *name)
1614{
1615 int i;
1616
1617 for (i = 0; special_keys[i].vim_code1 != NUL; i++)
1618 if (name[0] == special_keys[i].vim_code0 &&
1619 name[1] == special_keys[i].vim_code1)
1620 return OK;
1621 return FAIL;
1622}
1623
1624 void
1625gui_mch_beep(void)
1626{
1627 MessageBeep(MB_OK);
1628}
1629/*
1630 * Invert a rectangle from row r, column c, for nr rows and nc columns.
1631 */
1632 void
1633gui_mch_invert_rectangle(
1634 int r,
1635 int c,
1636 int nr,
1637 int nc)
1638{
1639 RECT rc;
1640
1641 /*
1642 * Note: InvertRect() excludes right and bottom of rectangle.
1643 */
1644 rc.left = FILL_X(c);
1645 rc.top = FILL_Y(r);
1646 rc.right = rc.left + nc * gui.char_width;
1647 rc.bottom = rc.top + nr * gui.char_height;
1648 InvertRect(s_hdc, &rc);
1649}
1650
1651/*
1652 * Iconify the GUI window.
1653 */
1654 void
1655gui_mch_iconify(void)
1656{
1657 ShowWindow(s_hwnd, SW_MINIMIZE);
1658}
1659
1660/*
1661 * Draw a cursor without focus.
1662 */
1663 void
1664gui_mch_draw_hollow_cursor(guicolor_T color)
1665{
1666 HBRUSH hbr;
1667 RECT rc;
1668
1669 /*
1670 * Note: FrameRect() excludes right and bottom of rectangle.
1671 */
1672 rc.left = FILL_X(gui.col);
1673 rc.top = FILL_Y(gui.row);
1674 rc.right = rc.left + gui.char_width;
1675#ifdef FEAT_MBYTE
1676 if (mb_lefthalve(gui.row, gui.col))
1677 rc.right += gui.char_width;
1678#endif
1679 rc.bottom = rc.top + gui.char_height;
1680 hbr = CreateSolidBrush(color);
1681 FrameRect(s_hdc, &rc, hbr);
1682 DeleteBrush(hbr);
1683}
1684/*
1685 * Draw part of a cursor, "w" pixels wide, and "h" pixels high, using
1686 * color "color".
1687 */
1688 void
1689gui_mch_draw_part_cursor(
1690 int w,
1691 int h,
1692 guicolor_T color)
1693{
1694 HBRUSH hbr;
1695 RECT rc;
1696
1697 /*
1698 * Note: FillRect() excludes right and bottom of rectangle.
1699 */
1700 rc.left =
1701#ifdef FEAT_RIGHTLEFT
1702 /* vertical line should be on the right of current point */
1703 CURSOR_BAR_RIGHT ? FILL_X(gui.col + 1) - w :
1704#endif
1705 FILL_X(gui.col);
1706 rc.top = FILL_Y(gui.row) + gui.char_height - h;
1707 rc.right = rc.left + w;
1708 rc.bottom = rc.top + h;
1709 hbr = CreateSolidBrush(color);
1710 FillRect(s_hdc, &rc, hbr);
1711 DeleteBrush(hbr);
1712}
1713
1714
1715/*
1716 * Generates a VK_SPACE when the internal dead_key flag is set to output the
1717 * dead key's nominal character and re-post the original message.
1718 */
1719 static void
1720outputDeadKey_rePost(MSG originalMsg)
1721{
1722 static MSG deadCharExpel;
1723
1724 if (!dead_key)
1725 return;
1726
1727 dead_key = 0;
1728
1729 /* Make Windows generate the dead key's character */
1730 deadCharExpel.message = originalMsg.message;
1731 deadCharExpel.hwnd = originalMsg.hwnd;
1732 deadCharExpel.wParam = VK_SPACE;
1733
1734 MyTranslateMessage(&deadCharExpel);
1735
1736 /* re-generate the current character free of the dead char influence */
1737 PostMessage(originalMsg.hwnd, originalMsg.message, originalMsg.wParam,
1738 originalMsg.lParam);
1739}
1740
1741
1742/*
1743 * Process a single Windows message.
1744 * If one is not available we hang until one is.
1745 */
1746 static void
1747process_message(void)
1748{
1749 MSG msg;
1750 UINT vk = 0; /* Virtual key */
1751 char_u string[40];
1752 int i;
1753 int modifiers = 0;
1754 int key;
1755#ifdef FEAT_MENU
1756 static char_u k10[] = {K_SPECIAL, 'k', ';', 0};
1757#endif
1758
1759 pGetMessage(&msg, NULL, 0, 0);
1760
1761#ifdef FEAT_OLE
1762 /* Look after OLE Automation commands */
1763 if (msg.message == WM_OLE)
1764 {
1765 char_u *str = (char_u *)msg.lParam;
1766 if (str == NULL || *str == NUL)
1767 {
1768 /* Message can't be ours, forward it. Fixes problem with Ultramon
1769 * 3.0.4 */
1770 pDispatchMessage(&msg);
1771 }
1772 else
1773 {
1774 add_to_input_buf(str, (int)STRLEN(str));
1775 vim_free(str); /* was allocated in CVim::SendKeys() */
1776 }
1777 return;
1778 }
1779#endif
1780
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001781#ifdef MSWIN_FIND_REPLACE
1782 /* Don't process messages used by the dialog */
1783 if (s_findrep_hwnd != NULL && pIsDialogMessage(s_findrep_hwnd, &msg))
1784 {
1785 HandleMouseHide(msg.message, msg.lParam);
1786 return;
1787 }
1788#endif
1789
1790 /*
1791 * Check if it's a special key that we recognise. If not, call
1792 * TranslateMessage().
1793 */
1794 if (msg.message == WM_KEYDOWN || msg.message == WM_SYSKEYDOWN)
1795 {
1796 vk = (int) msg.wParam;
1797
1798 /*
1799 * Handle dead keys in special conditions in other cases we let Windows
1800 * handle them and do not interfere.
1801 *
1802 * The dead_key flag must be reset on several occasions:
1803 * - in _OnChar() (or _OnSysChar()) as any dead key was necessarily
1804 * consumed at that point (This is when we let Windows combine the
1805 * dead character on its own)
1806 *
1807 * - Before doing something special such as regenerating keypresses to
1808 * expel the dead character as this could trigger an infinite loop if
1809 * for some reason MyTranslateMessage() do not trigger a call
1810 * immediately to _OnChar() (or _OnSysChar()).
1811 */
1812 if (dead_key)
1813 {
1814 /*
1815 * If a dead key was pressed and the user presses VK_SPACE,
1816 * VK_BACK, or VK_ESCAPE it means that he actually wants to deal
1817 * with the dead char now, so do nothing special and let Windows
1818 * handle it.
1819 *
1820 * Note that VK_SPACE combines with the dead_key's character and
1821 * only one WM_CHAR will be generated by TranslateMessage(), in
1822 * the two other cases two WM_CHAR will be generated: the dead
1823 * char and VK_BACK or VK_ESCAPE. That is most likely what the
1824 * user expects.
1825 */
1826 if ((vk == VK_SPACE || vk == VK_BACK || vk == VK_ESCAPE))
1827 {
1828 dead_key = 0;
1829 MyTranslateMessage(&msg);
1830 return;
1831 }
1832 /* In modes where we are not typing, dead keys should behave
1833 * normally */
1834 else if (!(get_real_state() & (INSERT | CMDLINE | SELECTMODE)))
1835 {
1836 outputDeadKey_rePost(msg);
1837 return;
1838 }
1839 }
1840
1841 /* Check for CTRL-BREAK */
1842 if (vk == VK_CANCEL)
1843 {
1844 trash_input_buf();
1845 got_int = TRUE;
1846 string[0] = Ctrl_C;
1847 add_to_input_buf(string, 1);
1848 }
1849
1850 for (i = 0; special_keys[i].key_sym != 0; i++)
1851 {
1852 /* ignore VK_SPACE when ALT key pressed: system menu */
1853 if (special_keys[i].key_sym == vk
1854 && (vk != VK_SPACE || !(GetKeyState(VK_MENU) & 0x8000)))
1855 {
1856 /*
Bram Moolenaar945ec092016-06-08 21:17:43 +02001857 * Behave as expected if we have a dead key and the special key
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01001858 * is a key that would normally trigger the dead key nominal
1859 * character output (such as a NUMPAD printable character or
1860 * the TAB key, etc...).
1861 */
1862 if (dead_key && (special_keys[i].vim_code0 == 'K'
1863 || vk == VK_TAB || vk == CAR))
1864 {
1865 outputDeadKey_rePost(msg);
1866 return;
1867 }
1868
1869#ifdef FEAT_MENU
1870 /* Check for <F10>: Windows selects the menu. When <F10> is
1871 * mapped we want to use the mapping instead. */
1872 if (vk == VK_F10
1873 && gui.menu_is_active
1874 && check_map(k10, State, FALSE, TRUE, FALSE,
1875 NULL, NULL) == NULL)
1876 break;
1877#endif
1878 if (GetKeyState(VK_SHIFT) & 0x8000)
1879 modifiers |= MOD_MASK_SHIFT;
1880 /*
1881 * Don't use caps-lock as shift, because these are special keys
1882 * being considered here, and we only want letters to get
1883 * shifted -- webb
1884 */
1885 /*
1886 if (GetKeyState(VK_CAPITAL) & 0x0001)
1887 modifiers ^= MOD_MASK_SHIFT;
1888 */
1889 if (GetKeyState(VK_CONTROL) & 0x8000)
1890 modifiers |= MOD_MASK_CTRL;
1891 if (GetKeyState(VK_MENU) & 0x8000)
1892 modifiers |= MOD_MASK_ALT;
1893
1894 if (special_keys[i].vim_code1 == NUL)
1895 key = special_keys[i].vim_code0;
1896 else
1897 key = TO_SPECIAL(special_keys[i].vim_code0,
1898 special_keys[i].vim_code1);
1899 key = simplify_key(key, &modifiers);
1900 if (key == CSI)
1901 key = K_CSI;
1902
1903 if (modifiers)
1904 {
1905 string[0] = CSI;
1906 string[1] = KS_MODIFIER;
1907 string[2] = modifiers;
1908 add_to_input_buf(string, 3);
1909 }
1910
1911 if (IS_SPECIAL(key))
1912 {
1913 string[0] = CSI;
1914 string[1] = K_SECOND(key);
1915 string[2] = K_THIRD(key);
1916 add_to_input_buf(string, 3);
1917 }
1918 else
1919 {
1920 int len;
1921
1922 /* Handle "key" as a Unicode character. */
1923 len = char_to_string(key, string, 40, FALSE);
1924 add_to_input_buf(string, len);
1925 }
1926 break;
1927 }
1928 }
1929 if (special_keys[i].key_sym == 0)
1930 {
1931 /* Some keys need C-S- where they should only need C-.
1932 * Ignore 0xff, Windows XP sends it when NUMLOCK has changed since
1933 * system startup (Helmut Stiegler, 2003 Oct 3). */
1934 if (vk != 0xff
1935 && (GetKeyState(VK_CONTROL) & 0x8000)
1936 && !(GetKeyState(VK_SHIFT) & 0x8000)
1937 && !(GetKeyState(VK_MENU) & 0x8000))
1938 {
1939 /* CTRL-6 is '^'; Japanese keyboard maps '^' to vk == 0xDE */
1940 if (vk == '6' || MapVirtualKey(vk, 2) == (UINT)'^')
1941 {
1942 string[0] = Ctrl_HAT;
1943 add_to_input_buf(string, 1);
1944 }
1945 /* vk == 0xBD AZERTY for CTRL-'-', but CTRL-[ for * QWERTY! */
1946 else if (vk == 0xBD) /* QWERTY for CTRL-'-' */
1947 {
1948 string[0] = Ctrl__;
1949 add_to_input_buf(string, 1);
1950 }
1951 /* CTRL-2 is '@'; Japanese keyboard maps '@' to vk == 0xC0 */
1952 else if (vk == '2' || MapVirtualKey(vk, 2) == (UINT)'@')
1953 {
1954 string[0] = Ctrl_AT;
1955 add_to_input_buf(string, 1);
1956 }
1957 else
1958 MyTranslateMessage(&msg);
1959 }
1960 else
1961 MyTranslateMessage(&msg);
1962 }
1963 }
1964#ifdef FEAT_MBYTE_IME
1965 else if (msg.message == WM_IME_NOTIFY)
1966 _OnImeNotify(msg.hwnd, (DWORD)msg.wParam, (DWORD)msg.lParam);
1967 else if (msg.message == WM_KEYUP && im_get_status())
1968 /* added for non-MS IME (Yasuhiro Matsumoto) */
1969 MyTranslateMessage(&msg);
1970#endif
1971#if !defined(FEAT_MBYTE_IME) && defined(GLOBAL_IME)
1972/* GIME_TEST */
1973 else if (msg.message == WM_IME_STARTCOMPOSITION)
1974 {
1975 POINT point;
1976
1977 global_ime_set_font(&norm_logfont);
1978 point.x = FILL_X(gui.col);
1979 point.y = FILL_Y(gui.row);
1980 MapWindowPoints(s_textArea, s_hwnd, &point, 1);
1981 global_ime_set_position(&point);
1982 }
1983#endif
1984
1985#ifdef FEAT_MENU
1986 /* Check for <F10>: Default effect is to select the menu. When <F10> is
1987 * mapped we need to stop it here to avoid strange effects (e.g., for the
1988 * key-up event) */
1989 if (vk != VK_F10 || check_map(k10, State, FALSE, TRUE, FALSE,
1990 NULL, NULL) == NULL)
1991#endif
1992 pDispatchMessage(&msg);
1993}
1994
1995/*
1996 * Catch up with any queued events. This may put keyboard input into the
1997 * input buffer, call resize call-backs, trigger timers etc. If there is
1998 * nothing in the event queue (& no timers pending), then we return
1999 * immediately.
2000 */
2001 void
2002gui_mch_update(void)
2003{
2004 MSG msg;
2005
2006 if (!s_busy_processing)
2007 while (pPeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)
2008 && !vim_is_input_buf_full())
2009 process_message();
2010}
2011
Bram Moolenaar4231da42016-06-02 14:30:04 +02002012 static void
2013remove_any_timer(void)
2014{
2015 MSG msg;
2016
2017 if (s_wait_timer != 0 && !s_timed_out)
2018 {
2019 KillTimer(NULL, s_wait_timer);
2020
2021 /* Eat spurious WM_TIMER messages */
2022 while (pPeekMessage(&msg, s_hwnd, WM_TIMER, WM_TIMER, PM_REMOVE))
2023 ;
2024 s_wait_timer = 0;
2025 }
2026}
2027
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002028/*
2029 * GUI input routine called by gui_wait_for_chars(). Waits for a character
2030 * from the keyboard.
2031 * wtime == -1 Wait forever.
2032 * wtime == 0 This should never happen.
2033 * wtime > 0 Wait wtime milliseconds for a character.
2034 * Returns OK if a character was found to be available within the given time,
2035 * or FAIL otherwise.
2036 */
2037 int
2038gui_mch_wait_for_chars(int wtime)
2039{
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002040 int focus;
2041
2042 s_timed_out = FALSE;
2043
2044 if (wtime > 0)
2045 {
2046 /* Don't do anything while processing a (scroll) message. */
2047 if (s_busy_processing)
2048 return FAIL;
2049 s_wait_timer = (UINT)SetTimer(NULL, 0, (UINT)wtime,
2050 (TIMERPROC)_OnTimer);
2051 }
2052
2053 allow_scrollbar = TRUE;
2054
2055 focus = gui.in_focus;
2056 while (!s_timed_out)
2057 {
2058 /* Stop or start blinking when focus changes */
2059 if (gui.in_focus != focus)
2060 {
2061 if (gui.in_focus)
2062 gui_mch_start_blink();
2063 else
2064 gui_mch_stop_blink();
2065 focus = gui.in_focus;
2066 }
2067
2068 if (s_need_activate)
2069 {
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002070 (void)SetForegroundWindow(s_hwnd);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002071 s_need_activate = FALSE;
2072 }
2073
Bram Moolenaar4231da42016-06-02 14:30:04 +02002074#ifdef FEAT_TIMERS
2075 did_add_timer = FALSE;
2076#endif
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002077#ifdef MESSAGE_QUEUE
Bram Moolenaar9186a272016-02-23 19:34:01 +01002078 /* Check channel while waiting message. */
2079 for (;;)
2080 {
2081 MSG msg;
2082
2083 parse_queued_messages();
2084
2085 if (pPeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)
Bram Moolenaarf28d8712016-04-02 15:59:40 +02002086 || MsgWaitForMultipleObjects(0, NULL, FALSE, 100, QS_ALLINPUT)
Bram Moolenaar9186a272016-02-23 19:34:01 +01002087 != WAIT_TIMEOUT)
2088 break;
2089 }
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002090#endif
2091
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002092 /*
2093 * Don't use gui_mch_update() because then we will spin-lock until a
2094 * char arrives, instead we use GetMessage() to hang until an
2095 * event arrives. No need to check for input_buf_full because we are
2096 * returning as soon as it contains a single char -- webb
2097 */
2098 process_message();
2099
2100 if (input_available())
2101 {
Bram Moolenaar4231da42016-06-02 14:30:04 +02002102 remove_any_timer();
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002103 allow_scrollbar = FALSE;
2104
2105 /* Clear pending mouse button, the release event may have been
2106 * taken by the dialog window. But don't do this when getting
2107 * focus, we need the mouse-up event then. */
2108 if (!s_getting_focus)
2109 s_button_pending = -1;
2110
2111 return OK;
2112 }
Bram Moolenaar4231da42016-06-02 14:30:04 +02002113
2114#ifdef FEAT_TIMERS
2115 if (did_add_timer)
2116 {
2117 /* Need to recompute the waiting time. */
2118 remove_any_timer();
2119 break;
2120 }
2121#endif
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002122 }
2123 allow_scrollbar = FALSE;
2124 return FAIL;
2125}
2126
2127/*
2128 * Clear a rectangular region of the screen from text pos (row1, col1) to
2129 * (row2, col2) inclusive.
2130 */
2131 void
2132gui_mch_clear_block(
2133 int row1,
2134 int col1,
2135 int row2,
2136 int col2)
2137{
2138 RECT rc;
2139
2140 /*
2141 * Clear one extra pixel at the far right, for when bold characters have
2142 * spilled over to the window border.
2143 * Note: FillRect() excludes right and bottom of rectangle.
2144 */
2145 rc.left = FILL_X(col1);
2146 rc.top = FILL_Y(row1);
2147 rc.right = FILL_X(col2 + 1) + (col2 == Columns - 1);
2148 rc.bottom = FILL_Y(row2 + 1);
2149 clear_rect(&rc);
2150}
2151
2152/*
2153 * Clear the whole text window.
2154 */
2155 void
2156gui_mch_clear_all(void)
2157{
2158 RECT rc;
2159
2160 rc.left = 0;
2161 rc.top = 0;
2162 rc.right = Columns * gui.char_width + 2 * gui.border_width;
2163 rc.bottom = Rows * gui.char_height + 2 * gui.border_width;
2164 clear_rect(&rc);
2165}
2166/*
2167 * Menu stuff.
2168 */
2169
2170 void
2171gui_mch_enable_menu(int flag)
2172{
2173#ifdef FEAT_MENU
2174 SetMenu(s_hwnd, flag ? s_menuBar : NULL);
2175#endif
2176}
2177
2178/*ARGSUSED*/
2179 void
2180gui_mch_set_menu_pos(
2181 int x,
2182 int y,
2183 int w,
2184 int h)
2185{
2186 /* It will be in the right place anyway */
2187}
2188
2189#if defined(FEAT_MENU) || defined(PROTO)
2190/*
2191 * Make menu item hidden or not hidden
2192 */
2193 void
2194gui_mch_menu_hidden(
2195 vimmenu_T *menu,
2196 int hidden)
2197{
2198 /*
2199 * This doesn't do what we want. Hmm, just grey the menu items for now.
2200 */
2201 /*
2202 if (hidden)
2203 EnableMenuItem(s_menuBar, menu->id, MF_BYCOMMAND | MF_DISABLED);
2204 else
2205 EnableMenuItem(s_menuBar, menu->id, MF_BYCOMMAND | MF_ENABLED);
2206 */
2207 gui_mch_menu_grey(menu, hidden);
2208}
2209
2210/*
2211 * This is called after setting all the menus to grey/hidden or not.
2212 */
2213 void
2214gui_mch_draw_menubar(void)
2215{
2216 DrawMenuBar(s_hwnd);
2217}
2218#endif /*FEAT_MENU*/
2219
2220#ifndef PROTO
2221void
2222#ifdef VIMDLL
2223_export
2224#endif
2225_cdecl
2226SaveInst(HINSTANCE hInst)
2227{
2228 s_hinst = hInst;
2229}
2230#endif
2231
2232/*
2233 * Return the RGB value of a pixel as a long.
2234 */
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02002235 guicolor_T
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002236gui_mch_get_rgb(guicolor_T pixel)
2237{
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02002238 return (guicolor_T)((GetRValue(pixel) << 16) + (GetGValue(pixel) << 8)
2239 + GetBValue(pixel));
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002240}
2241
2242#if defined(FEAT_GUI_DIALOG) || defined(PROTO)
2243/* Convert pixels in X to dialog units */
2244 static WORD
2245PixelToDialogX(int numPixels)
2246{
2247 return (WORD)((numPixels * 4) / s_dlgfntwidth);
2248}
2249
2250/* Convert pixels in Y to dialog units */
2251 static WORD
2252PixelToDialogY(int numPixels)
2253{
2254 return (WORD)((numPixels * 8) / s_dlgfntheight);
2255}
2256
2257/* Return the width in pixels of the given text in the given DC. */
2258 static int
2259GetTextWidth(HDC hdc, char_u *str, int len)
2260{
2261 SIZE size;
2262
2263 GetTextExtentPoint(hdc, (LPCSTR)str, len, &size);
2264 return size.cx;
2265}
2266
2267#ifdef FEAT_MBYTE
2268/*
2269 * Return the width in pixels of the given text in the given DC, taking care
2270 * of 'encoding' to active codepage conversion.
2271 */
2272 static int
2273GetTextWidthEnc(HDC hdc, char_u *str, int len)
2274{
2275 SIZE size;
2276 WCHAR *wstr;
2277 int n;
2278 int wlen = len;
2279
2280 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
2281 {
2282 /* 'encoding' differs from active codepage: convert text and use wide
2283 * function */
2284 wstr = enc_to_utf16(str, &wlen);
2285 if (wstr != NULL)
2286 {
2287 n = GetTextExtentPointW(hdc, wstr, wlen, &size);
2288 vim_free(wstr);
2289 if (n)
2290 return size.cx;
2291 }
2292 }
2293
2294 return GetTextWidth(hdc, str, len);
2295}
2296#else
2297# define GetTextWidthEnc(h, s, l) GetTextWidth((h), (s), (l))
2298#endif
2299
Bram Moolenaar87f3d202016-12-01 20:18:50 +01002300static void get_work_area(RECT *spi_rect);
2301
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002302/*
2303 * A quick little routine that will center one window over another, handy for
Bram Moolenaar87f3d202016-12-01 20:18:50 +01002304 * dialog boxes. Taken from the Win32SDK samples and modified for multiple
2305 * monitors.
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002306 */
2307 static BOOL
2308CenterWindow(
2309 HWND hwndChild,
2310 HWND hwndParent)
2311{
Bram Moolenaar87f3d202016-12-01 20:18:50 +01002312 HMONITOR mon;
2313 MONITORINFO moninfo;
2314 RECT rChild, rParent, rScreen;
2315 int wChild, hChild, wParent, hParent;
2316 int xNew, yNew;
2317 HDC hdc;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002318
2319 GetWindowRect(hwndChild, &rChild);
2320 wChild = rChild.right - rChild.left;
2321 hChild = rChild.bottom - rChild.top;
2322
2323 /* If Vim is minimized put the window in the middle of the screen. */
2324 if (hwndParent == NULL || IsMinimized(hwndParent))
Bram Moolenaar87f3d202016-12-01 20:18:50 +01002325 get_work_area(&rParent);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002326 else
2327 GetWindowRect(hwndParent, &rParent);
2328 wParent = rParent.right - rParent.left;
2329 hParent = rParent.bottom - rParent.top;
2330
Bram Moolenaar87f3d202016-12-01 20:18:50 +01002331 moninfo.cbSize = sizeof(MONITORINFO);
2332 mon = MonitorFromWindow(hwndChild, MONITOR_DEFAULTTOPRIMARY);
2333 if (mon != NULL && GetMonitorInfo(mon, &moninfo))
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002334 {
Bram Moolenaar87f3d202016-12-01 20:18:50 +01002335 rScreen = moninfo.rcWork;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002336 }
Bram Moolenaar87f3d202016-12-01 20:18:50 +01002337 else
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002338 {
Bram Moolenaar87f3d202016-12-01 20:18:50 +01002339 hdc = GetDC(hwndChild);
2340 rScreen.left = 0;
2341 rScreen.top = 0;
2342 rScreen.right = GetDeviceCaps(hdc, HORZRES);
2343 rScreen.bottom = GetDeviceCaps(hdc, VERTRES);
2344 ReleaseDC(hwndChild, hdc);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002345 }
2346
Bram Moolenaar87f3d202016-12-01 20:18:50 +01002347 xNew = rParent.left + ((wParent - wChild) / 2);
2348 if (xNew < rScreen.left)
2349 xNew = rScreen.left;
2350 else if ((xNew + wChild) > rScreen.right)
2351 xNew = rScreen.right - wChild;
2352
2353 yNew = rParent.top + ((hParent - hChild) / 2);
2354 if (yNew < rScreen.top)
2355 yNew = rScreen.top;
2356 else if ((yNew + hChild) > rScreen.bottom)
2357 yNew = rScreen.bottom - hChild;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002358
2359 return SetWindowPos(hwndChild, NULL, xNew, yNew, 0, 0,
2360 SWP_NOSIZE | SWP_NOZORDER);
2361}
2362#endif /* FEAT_GUI_DIALOG */
2363
2364void
2365gui_mch_activate_window(void)
2366{
2367 (void)SetActiveWindow(s_hwnd);
2368}
2369
2370#if defined(FEAT_TOOLBAR) || defined(PROTO)
2371 void
2372gui_mch_show_toolbar(int showit)
2373{
2374 if (s_toolbarhwnd == NULL)
2375 return;
2376
2377 if (showit)
2378 {
2379# ifdef FEAT_MBYTE
2380# ifndef TB_SETUNICODEFORMAT
2381 /* For older compilers. We assume this never changes. */
2382# define TB_SETUNICODEFORMAT 0x2005
2383# endif
2384 /* Enable/disable unicode support */
2385 int uu = (enc_codepage >= 0 && (int)GetACP() != enc_codepage);
2386 SendMessage(s_toolbarhwnd, TB_SETUNICODEFORMAT, (WPARAM)uu, (LPARAM)0);
2387# endif
2388 ShowWindow(s_toolbarhwnd, SW_SHOW);
2389 }
2390 else
2391 ShowWindow(s_toolbarhwnd, SW_HIDE);
2392}
2393
2394/* Then number of bitmaps is fixed. Exit is missing! */
2395#define TOOLBAR_BITMAP_COUNT 31
2396
2397#endif
2398
2399#if defined(FEAT_GUI_TABLINE) || defined(PROTO)
2400 static void
2401add_tabline_popup_menu_entry(HMENU pmenu, UINT item_id, char_u *item_text)
2402{
2403#ifdef FEAT_MBYTE
2404 WCHAR *wn = NULL;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002405
2406 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
2407 {
2408 /* 'encoding' differs from active codepage: convert menu name
2409 * and use wide function */
2410 wn = enc_to_utf16(item_text, NULL);
2411 if (wn != NULL)
2412 {
2413 MENUITEMINFOW infow;
2414
2415 infow.cbSize = sizeof(infow);
2416 infow.fMask = MIIM_TYPE | MIIM_ID;
2417 infow.wID = item_id;
2418 infow.fType = MFT_STRING;
2419 infow.dwTypeData = wn;
2420 infow.cch = (UINT)wcslen(wn);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02002421 InsertMenuItemW(pmenu, item_id, FALSE, &infow);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002422 vim_free(wn);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002423 }
2424 }
2425
2426 if (wn == NULL)
2427#endif
2428 {
2429 MENUITEMINFO info;
2430
2431 info.cbSize = sizeof(info);
2432 info.fMask = MIIM_TYPE | MIIM_ID;
2433 info.wID = item_id;
2434 info.fType = MFT_STRING;
2435 info.dwTypeData = (LPTSTR)item_text;
2436 info.cch = (UINT)STRLEN(item_text);
2437 InsertMenuItem(pmenu, item_id, FALSE, &info);
2438 }
2439}
2440
2441 static void
2442show_tabline_popup_menu(void)
2443{
2444 HMENU tab_pmenu;
2445 long rval;
2446 POINT pt;
2447
2448 /* When ignoring events don't show the menu. */
2449 if (hold_gui_events
2450# ifdef FEAT_CMDWIN
2451 || cmdwin_type != 0
2452# endif
2453 )
2454 return;
2455
2456 tab_pmenu = CreatePopupMenu();
2457 if (tab_pmenu == NULL)
2458 return;
2459
2460 if (first_tabpage->tp_next != NULL)
2461 add_tabline_popup_menu_entry(tab_pmenu,
2462 TABLINE_MENU_CLOSE, (char_u *)_("Close tab"));
2463 add_tabline_popup_menu_entry(tab_pmenu,
2464 TABLINE_MENU_NEW, (char_u *)_("New tab"));
2465 add_tabline_popup_menu_entry(tab_pmenu,
2466 TABLINE_MENU_OPEN, (char_u *)_("Open tab..."));
2467
2468 GetCursorPos(&pt);
2469 rval = TrackPopupMenuEx(tab_pmenu, TPM_RETURNCMD, pt.x, pt.y, s_tabhwnd,
2470 NULL);
2471
2472 DestroyMenu(tab_pmenu);
2473
2474 /* Add the string cmd into input buffer */
2475 if (rval > 0)
2476 {
2477 TCHITTESTINFO htinfo;
2478 int idx;
2479
2480 if (ScreenToClient(s_tabhwnd, &pt) == 0)
2481 return;
2482
2483 htinfo.pt.x = pt.x;
2484 htinfo.pt.y = pt.y;
2485 idx = TabCtrl_HitTest(s_tabhwnd, &htinfo);
2486 if (idx == -1)
2487 idx = 0;
2488 else
2489 idx += 1;
2490
2491 send_tabline_menu_event(idx, (int)rval);
2492 }
2493}
2494
2495/*
2496 * Show or hide the tabline.
2497 */
2498 void
2499gui_mch_show_tabline(int showit)
2500{
2501 if (s_tabhwnd == NULL)
2502 return;
2503
2504 if (!showit != !showing_tabline)
2505 {
2506 if (showit)
2507 ShowWindow(s_tabhwnd, SW_SHOW);
2508 else
2509 ShowWindow(s_tabhwnd, SW_HIDE);
2510 showing_tabline = showit;
2511 }
2512}
2513
2514/*
2515 * Return TRUE when tabline is displayed.
2516 */
2517 int
2518gui_mch_showing_tabline(void)
2519{
2520 return s_tabhwnd != NULL && showing_tabline;
2521}
2522
2523/*
2524 * Update the labels of the tabline.
2525 */
2526 void
2527gui_mch_update_tabline(void)
2528{
2529 tabpage_T *tp;
2530 TCITEM tie;
2531 int nr = 0;
2532 int curtabidx = 0;
2533 int tabadded = 0;
2534#ifdef FEAT_MBYTE
2535 static int use_unicode = FALSE;
2536 int uu;
2537 WCHAR *wstr = NULL;
2538#endif
2539
2540 if (s_tabhwnd == NULL)
2541 return;
2542
Bram Moolenaarcea912a2016-10-12 14:20:24 +02002543#ifdef FEAT_MBYTE
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002544# ifndef CCM_SETUNICODEFORMAT
2545 /* For older compilers. We assume this never changes. */
2546# define CCM_SETUNICODEFORMAT 0x2005
2547# endif
2548 uu = (enc_codepage >= 0 && (int)GetACP() != enc_codepage);
2549 if (uu != use_unicode)
2550 {
2551 /* Enable/disable unicode support */
2552 SendMessage(s_tabhwnd, CCM_SETUNICODEFORMAT, (WPARAM)uu, (LPARAM)0);
2553 use_unicode = uu;
2554 }
2555#endif
2556
2557 tie.mask = TCIF_TEXT;
2558 tie.iImage = -1;
2559
2560 /* Disable redraw for tab updates to eliminate O(N^2) draws. */
2561 SendMessage(s_tabhwnd, WM_SETREDRAW, (WPARAM)FALSE, 0);
2562
2563 /* Add a label for each tab page. They all contain the same text area. */
2564 for (tp = first_tabpage; tp != NULL; tp = tp->tp_next, ++nr)
2565 {
2566 if (tp == curtab)
2567 curtabidx = nr;
2568
2569 if (nr >= TabCtrl_GetItemCount(s_tabhwnd))
2570 {
2571 /* Add the tab */
2572 tie.pszText = "-Empty-";
2573 TabCtrl_InsertItem(s_tabhwnd, nr, &tie);
2574 tabadded = 1;
2575 }
2576
2577 get_tabline_label(tp, FALSE);
2578 tie.pszText = (LPSTR)NameBuff;
2579#ifdef FEAT_MBYTE
2580 wstr = NULL;
2581 if (use_unicode)
2582 {
2583 /* Need to go through Unicode. */
2584 wstr = enc_to_utf16(NameBuff, NULL);
2585 if (wstr != NULL)
2586 {
2587 TCITEMW tiw;
2588
2589 tiw.mask = TCIF_TEXT;
2590 tiw.iImage = -1;
2591 tiw.pszText = wstr;
2592 SendMessage(s_tabhwnd, TCM_SETITEMW, (WPARAM)nr, (LPARAM)&tiw);
2593 vim_free(wstr);
2594 }
2595 }
2596 if (wstr == NULL)
2597#endif
2598 {
2599 TabCtrl_SetItem(s_tabhwnd, nr, &tie);
2600 }
2601 }
2602
2603 /* Remove any old labels. */
2604 while (nr < TabCtrl_GetItemCount(s_tabhwnd))
2605 TabCtrl_DeleteItem(s_tabhwnd, nr);
2606
2607 if (!tabadded && TabCtrl_GetCurSel(s_tabhwnd) != curtabidx)
2608 TabCtrl_SetCurSel(s_tabhwnd, curtabidx);
2609
2610 /* Re-enable redraw and redraw. */
2611 SendMessage(s_tabhwnd, WM_SETREDRAW, (WPARAM)TRUE, 0);
2612 RedrawWindow(s_tabhwnd, NULL, NULL,
2613 RDW_ERASE | RDW_FRAME | RDW_INVALIDATE | RDW_ALLCHILDREN);
2614
2615 if (tabadded && TabCtrl_GetCurSel(s_tabhwnd) != curtabidx)
2616 TabCtrl_SetCurSel(s_tabhwnd, curtabidx);
2617}
2618
2619/*
2620 * Set the current tab to "nr". First tab is 1.
2621 */
2622 void
2623gui_mch_set_curtab(int nr)
2624{
2625 if (s_tabhwnd == NULL)
2626 return;
2627
2628 if (TabCtrl_GetCurSel(s_tabhwnd) != nr - 1)
2629 TabCtrl_SetCurSel(s_tabhwnd, nr - 1);
2630}
2631
2632#endif
2633
2634/*
2635 * ":simalt" command.
2636 */
2637 void
2638ex_simalt(exarg_T *eap)
2639{
2640 char_u *keys = eap->arg;
2641
2642 PostMessage(s_hwnd, WM_SYSCOMMAND, (WPARAM)SC_KEYMENU, (LPARAM)0);
2643 while (*keys)
2644 {
2645 if (*keys == '~')
2646 *keys = ' '; /* for showing system menu */
2647 PostMessage(s_hwnd, WM_CHAR, (WPARAM)*keys, (LPARAM)0);
2648 keys++;
2649 }
2650}
2651
2652/*
2653 * Create the find & replace dialogs.
2654 * You can't have both at once: ":find" when replace is showing, destroys
2655 * the replace dialog first, and the other way around.
2656 */
2657#ifdef MSWIN_FIND_REPLACE
2658 static void
2659initialise_findrep(char_u *initial_string)
2660{
2661 int wword = FALSE;
2662 int mcase = !p_ic;
2663 char_u *entry_text;
2664
2665 /* Get the search string to use. */
2666 entry_text = get_find_dialog_text(initial_string, &wword, &mcase);
2667
2668 s_findrep_struct.hwndOwner = s_hwnd;
2669 s_findrep_struct.Flags = FR_DOWN;
2670 if (mcase)
2671 s_findrep_struct.Flags |= FR_MATCHCASE;
2672 if (wword)
2673 s_findrep_struct.Flags |= FR_WHOLEWORD;
2674 if (entry_text != NULL && *entry_text != NUL)
2675 vim_strncpy((char_u *)s_findrep_struct.lpstrFindWhat, entry_text,
2676 s_findrep_struct.wFindWhatLen - 1);
2677 vim_free(entry_text);
2678}
2679#endif
2680
2681 static void
2682set_window_title(HWND hwnd, char *title)
2683{
2684#ifdef FEAT_MBYTE
2685 if (title != NULL && enc_codepage >= 0 && enc_codepage != (int)GetACP())
2686 {
2687 WCHAR *wbuf;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002688
2689 /* Convert the title from 'encoding' to UTF-16. */
2690 wbuf = (WCHAR *)enc_to_utf16((char_u *)title, NULL);
2691 if (wbuf != NULL)
2692 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +02002693 SetWindowTextW(hwnd, wbuf);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002694 vim_free(wbuf);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002695 }
Bram Moolenaarcea912a2016-10-12 14:20:24 +02002696 return;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002697 }
2698#endif
2699 (void)SetWindowText(hwnd, (LPCSTR)title);
2700}
2701
2702 void
2703gui_mch_find_dialog(exarg_T *eap)
2704{
2705#ifdef MSWIN_FIND_REPLACE
2706 if (s_findrep_msg != 0)
2707 {
2708 if (IsWindow(s_findrep_hwnd) && !s_findrep_is_find)
2709 DestroyWindow(s_findrep_hwnd);
2710
2711 if (!IsWindow(s_findrep_hwnd))
2712 {
2713 initialise_findrep(eap->arg);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02002714# ifdef FEAT_MBYTE
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002715 /* If the OS is Windows NT, and 'encoding' differs from active
2716 * codepage: convert text and use wide function. */
Bram Moolenaarcea912a2016-10-12 14:20:24 +02002717 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002718 {
2719 findrep_atow(&s_findrep_struct_w, &s_findrep_struct);
2720 s_findrep_hwnd = FindTextW(
2721 (LPFINDREPLACEW) &s_findrep_struct_w);
2722 }
2723 else
2724# endif
2725 s_findrep_hwnd = FindText((LPFINDREPLACE) &s_findrep_struct);
2726 }
2727
2728 set_window_title(s_findrep_hwnd,
2729 _("Find string (use '\\\\' to find a '\\')"));
2730 (void)SetFocus(s_findrep_hwnd);
2731
2732 s_findrep_is_find = TRUE;
2733 }
2734#endif
2735}
2736
2737
2738 void
2739gui_mch_replace_dialog(exarg_T *eap)
2740{
2741#ifdef MSWIN_FIND_REPLACE
2742 if (s_findrep_msg != 0)
2743 {
2744 if (IsWindow(s_findrep_hwnd) && s_findrep_is_find)
2745 DestroyWindow(s_findrep_hwnd);
2746
2747 if (!IsWindow(s_findrep_hwnd))
2748 {
2749 initialise_findrep(eap->arg);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02002750# ifdef FEAT_MBYTE
2751 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01002752 {
2753 findrep_atow(&s_findrep_struct_w, &s_findrep_struct);
2754 s_findrep_hwnd = ReplaceTextW(
2755 (LPFINDREPLACEW) &s_findrep_struct_w);
2756 }
2757 else
2758# endif
2759 s_findrep_hwnd = ReplaceText(
2760 (LPFINDREPLACE) &s_findrep_struct);
2761 }
2762
2763 set_window_title(s_findrep_hwnd,
2764 _("Find & Replace (use '\\\\' to find a '\\')"));
2765 (void)SetFocus(s_findrep_hwnd);
2766
2767 s_findrep_is_find = FALSE;
2768 }
2769#endif
2770}
2771
2772
2773/*
2774 * Set visibility of the pointer.
2775 */
2776 void
2777gui_mch_mousehide(int hide)
2778{
2779 if (hide != gui.pointer_hidden)
2780 {
2781 ShowCursor(!hide);
2782 gui.pointer_hidden = hide;
2783 }
2784}
2785
2786#ifdef FEAT_MENU
2787 static void
2788gui_mch_show_popupmenu_at(vimmenu_T *menu, int x, int y)
2789{
2790 /* Unhide the mouse, we don't get move events here. */
2791 gui_mch_mousehide(FALSE);
2792
2793 (void)TrackPopupMenu(
2794 (HMENU)menu->submenu_id,
2795 TPM_LEFTALIGN | TPM_LEFTBUTTON,
2796 x, y,
2797 (int)0, /*reserved param*/
2798 s_hwnd,
2799 NULL);
2800 /*
2801 * NOTE: The pop-up menu can eat the mouse up event.
2802 * We deal with this in normal.c.
2803 */
2804}
2805#endif
2806
2807/*
2808 * Got a message when the system will go down.
2809 */
2810 static void
2811_OnEndSession(void)
2812{
2813 getout_preserve_modified(1);
2814}
2815
2816/*
2817 * Get this message when the user clicks on the cross in the top right corner
2818 * of a Windows95 window.
2819 */
2820/*ARGSUSED*/
2821 static void
2822_OnClose(
2823 HWND hwnd)
2824{
2825 gui_shell_closed();
2826}
2827
2828/*
2829 * Get a message when the window is being destroyed.
2830 */
2831 static void
2832_OnDestroy(
2833 HWND hwnd)
2834{
2835 if (!destroying)
2836 _OnClose(hwnd);
2837}
2838
2839 static void
2840_OnPaint(
2841 HWND hwnd)
2842{
2843 if (!IsMinimized(hwnd))
2844 {
2845 PAINTSTRUCT ps;
2846
2847 out_flush(); /* make sure all output has been processed */
2848 (void)BeginPaint(hwnd, &ps);
2849#if defined(FEAT_DIRECTX)
2850 if (IS_ENABLE_DIRECTX())
2851 DWriteContext_BeginDraw(s_dwc);
2852#endif
2853
2854#ifdef FEAT_MBYTE
2855 /* prevent multi-byte characters from misprinting on an invalid
2856 * rectangle */
2857 if (has_mbyte)
2858 {
2859 RECT rect;
2860
2861 GetClientRect(hwnd, &rect);
2862 ps.rcPaint.left = rect.left;
2863 ps.rcPaint.right = rect.right;
2864 }
2865#endif
2866
2867 if (!IsRectEmpty(&ps.rcPaint))
2868 {
2869#if defined(FEAT_DIRECTX)
2870 if (IS_ENABLE_DIRECTX())
2871 DWriteContext_BindDC(s_dwc, s_hdc, &ps.rcPaint);
2872#endif
2873 gui_redraw(ps.rcPaint.left, ps.rcPaint.top,
2874 ps.rcPaint.right - ps.rcPaint.left + 1,
2875 ps.rcPaint.bottom - ps.rcPaint.top + 1);
2876 }
2877
2878#if defined(FEAT_DIRECTX)
2879 if (IS_ENABLE_DIRECTX())
2880 DWriteContext_EndDraw(s_dwc);
2881#endif
2882 EndPaint(hwnd, &ps);
2883 }
2884}
2885
2886/*ARGSUSED*/
2887 static void
2888_OnSize(
2889 HWND hwnd,
2890 UINT state,
2891 int cx,
2892 int cy)
2893{
2894 if (!IsMinimized(hwnd))
2895 {
2896 gui_resize_shell(cx, cy);
2897
2898#ifdef FEAT_MENU
2899 /* Menu bar may wrap differently now */
2900 gui_mswin_get_menu_height(TRUE);
2901#endif
2902 }
2903}
2904
2905 static void
2906_OnSetFocus(
2907 HWND hwnd,
2908 HWND hwndOldFocus)
2909{
2910 gui_focus_change(TRUE);
2911 s_getting_focus = TRUE;
2912 (void)MyWindowProc(hwnd, WM_SETFOCUS, (WPARAM)hwndOldFocus, 0);
2913}
2914
2915 static void
2916_OnKillFocus(
2917 HWND hwnd,
2918 HWND hwndNewFocus)
2919{
2920 gui_focus_change(FALSE);
2921 s_getting_focus = FALSE;
2922 (void)MyWindowProc(hwnd, WM_KILLFOCUS, (WPARAM)hwndNewFocus, 0);
2923}
2924
2925/*
2926 * Get a message when the user switches back to vim
2927 */
2928 static LRESULT
2929_OnActivateApp(
2930 HWND hwnd,
2931 BOOL fActivate,
2932 DWORD dwThreadId)
2933{
2934 /* we call gui_focus_change() in _OnSetFocus() */
2935 /* gui_focus_change((int)fActivate); */
2936 return MyWindowProc(hwnd, WM_ACTIVATEAPP, fActivate, (DWORD)dwThreadId);
2937}
2938
2939#if defined(FEAT_WINDOWS) || defined(PROTO)
2940 void
2941gui_mch_destroy_scrollbar(scrollbar_T *sb)
2942{
2943 DestroyWindow(sb->id);
2944}
2945#endif
2946
2947/*
2948 * Get current mouse coordinates in text window.
2949 */
2950 void
2951gui_mch_getmouse(int *x, int *y)
2952{
2953 RECT rct;
2954 POINT mp;
2955
2956 (void)GetWindowRect(s_textArea, &rct);
2957 (void)GetCursorPos((LPPOINT)&mp);
2958 *x = (int)(mp.x - rct.left);
2959 *y = (int)(mp.y - rct.top);
2960}
2961
2962/*
2963 * Move mouse pointer to character at (x, y).
2964 */
2965 void
2966gui_mch_setmouse(int x, int y)
2967{
2968 RECT rct;
2969
2970 (void)GetWindowRect(s_textArea, &rct);
2971 (void)SetCursorPos(x + gui.border_offset + rct.left,
2972 y + gui.border_offset + rct.top);
2973}
2974
2975 static void
2976gui_mswin_get_valid_dimensions(
2977 int w,
2978 int h,
2979 int *valid_w,
2980 int *valid_h)
2981{
2982 int base_width, base_height;
2983
2984 base_width = gui_get_base_width()
2985 + (GetSystemMetrics(SM_CXFRAME) +
2986 GetSystemMetrics(SM_CXPADDEDBORDER)) * 2;
2987 base_height = gui_get_base_height()
2988 + (GetSystemMetrics(SM_CYFRAME) +
2989 GetSystemMetrics(SM_CXPADDEDBORDER)) * 2
2990 + GetSystemMetrics(SM_CYCAPTION)
2991#ifdef FEAT_MENU
2992 + gui_mswin_get_menu_height(FALSE)
2993#endif
2994 ;
2995 *valid_w = base_width +
2996 ((w - base_width) / gui.char_width) * gui.char_width;
2997 *valid_h = base_height +
2998 ((h - base_height) / gui.char_height) * gui.char_height;
2999}
3000
3001 void
3002gui_mch_flash(int msec)
3003{
3004 RECT rc;
3005
3006 /*
3007 * Note: InvertRect() excludes right and bottom of rectangle.
3008 */
3009 rc.left = 0;
3010 rc.top = 0;
3011 rc.right = gui.num_cols * gui.char_width;
3012 rc.bottom = gui.num_rows * gui.char_height;
3013 InvertRect(s_hdc, &rc);
3014 gui_mch_flush(); /* make sure it's displayed */
3015
3016 ui_delay((long)msec, TRUE); /* wait for a few msec */
3017
3018 InvertRect(s_hdc, &rc);
3019}
3020
3021/*
3022 * Return flags used for scrolling.
3023 * The SW_INVALIDATE is required when part of the window is covered or
3024 * off-screen. Refer to MS KB Q75236.
3025 */
3026 static int
3027get_scroll_flags(void)
3028{
3029 HWND hwnd;
3030 RECT rcVim, rcOther, rcDest;
3031
3032 GetWindowRect(s_hwnd, &rcVim);
3033
3034 /* Check if the window is partly above or below the screen. We don't care
3035 * about partly left or right of the screen, it is not relevant when
3036 * scrolling up or down. */
3037 if (rcVim.top < 0 || rcVim.bottom > GetSystemMetrics(SM_CYFULLSCREEN))
3038 return SW_INVALIDATE;
3039
3040 /* Check if there is an window (partly) on top of us. */
3041 for (hwnd = s_hwnd; (hwnd = GetWindow(hwnd, GW_HWNDPREV)) != (HWND)0; )
3042 if (IsWindowVisible(hwnd))
3043 {
3044 GetWindowRect(hwnd, &rcOther);
3045 if (IntersectRect(&rcDest, &rcVim, &rcOther))
3046 return SW_INVALIDATE;
3047 }
3048 return 0;
3049}
3050
3051/*
3052 * On some Intel GPUs, the regions drawn just prior to ScrollWindowEx()
3053 * may not be scrolled out properly.
3054 * For gVim, when _OnScroll() is repeated, the character at the
3055 * previous cursor position may be left drawn after scroll.
3056 * The problem can be avoided by calling GetPixel() to get a pixel in
3057 * the region before ScrollWindowEx().
3058 */
3059 static void
3060intel_gpu_workaround(void)
3061{
3062 GetPixel(s_hdc, FILL_X(gui.col), FILL_Y(gui.row));
3063}
3064
3065/*
3066 * Delete the given number of lines from the given row, scrolling up any
3067 * text further down within the scroll region.
3068 */
3069 void
3070gui_mch_delete_lines(
3071 int row,
3072 int num_lines)
3073{
3074 RECT rc;
3075
3076 intel_gpu_workaround();
3077
3078 rc.left = FILL_X(gui.scroll_region_left);
3079 rc.right = FILL_X(gui.scroll_region_right + 1);
3080 rc.top = FILL_Y(row);
3081 rc.bottom = FILL_Y(gui.scroll_region_bot + 1);
3082
3083 ScrollWindowEx(s_textArea, 0, -num_lines * gui.char_height,
3084 &rc, &rc, NULL, NULL, get_scroll_flags());
3085
3086 UpdateWindow(s_textArea);
3087 /* This seems to be required to avoid the cursor disappearing when
3088 * scrolling such that the cursor ends up in the top-left character on
3089 * the screen... But why? (Webb) */
3090 /* It's probably fixed by disabling drawing the cursor while scrolling. */
3091 /* gui.cursor_is_valid = FALSE; */
3092
3093 gui_clear_block(gui.scroll_region_bot - num_lines + 1,
3094 gui.scroll_region_left,
3095 gui.scroll_region_bot, gui.scroll_region_right);
3096}
3097
3098/*
3099 * Insert the given number of lines before the given row, scrolling down any
3100 * following text within the scroll region.
3101 */
3102 void
3103gui_mch_insert_lines(
3104 int row,
3105 int num_lines)
3106{
3107 RECT rc;
3108
3109 intel_gpu_workaround();
3110
3111 rc.left = FILL_X(gui.scroll_region_left);
3112 rc.right = FILL_X(gui.scroll_region_right + 1);
3113 rc.top = FILL_Y(row);
3114 rc.bottom = FILL_Y(gui.scroll_region_bot + 1);
3115 /* The SW_INVALIDATE is required when part of the window is covered or
3116 * off-screen. How do we avoid it when it's not needed? */
3117 ScrollWindowEx(s_textArea, 0, num_lines * gui.char_height,
3118 &rc, &rc, NULL, NULL, get_scroll_flags());
3119
3120 UpdateWindow(s_textArea);
3121
3122 gui_clear_block(row, gui.scroll_region_left,
3123 row + num_lines - 1, gui.scroll_region_right);
3124}
3125
3126
3127/*ARGSUSED*/
3128 void
3129gui_mch_exit(int rc)
3130{
3131#if defined(FEAT_DIRECTX)
3132 DWriteContext_Close(s_dwc);
3133 DWrite_Final();
3134 s_dwc = NULL;
3135#endif
3136
3137 ReleaseDC(s_textArea, s_hdc);
3138 DeleteObject(s_brush);
3139
3140#ifdef FEAT_TEAROFF
3141 /* Unload the tearoff bitmap */
3142 (void)DeleteObject((HGDIOBJ)s_htearbitmap);
3143#endif
3144
3145 /* Destroy our window (if we have one). */
3146 if (s_hwnd != NULL)
3147 {
3148 destroying = TRUE; /* ignore WM_DESTROY message now */
3149 DestroyWindow(s_hwnd);
3150 }
3151
3152#ifdef GLOBAL_IME
3153 global_ime_end();
3154#endif
3155}
3156
3157 static char_u *
3158logfont2name(LOGFONT lf)
3159{
3160 char *p;
3161 char *res;
3162 char *charset_name;
Bram Moolenaar7c1c6db2016-04-03 22:08:05 +02003163 char *quality_name;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003164 char *font_name = lf.lfFaceName;
3165
3166 charset_name = charset_id2name((int)lf.lfCharSet);
3167#ifdef FEAT_MBYTE
3168 /* Convert a font name from the current codepage to 'encoding'.
3169 * TODO: Use Wide APIs (including LOGFONTW) instead of ANSI APIs. */
3170 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
3171 {
3172 int len;
3173 acp_to_enc((char_u *)lf.lfFaceName, (int)strlen(lf.lfFaceName),
3174 (char_u **)&font_name, &len);
3175 }
3176#endif
Bram Moolenaar7c1c6db2016-04-03 22:08:05 +02003177 quality_name = quality_id2name((int)lf.lfQuality);
3178
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003179 res = (char *)alloc((unsigned)(strlen(font_name) + 20
3180 + (charset_name == NULL ? 0 : strlen(charset_name) + 2)));
3181 if (res != NULL)
3182 {
3183 p = res;
3184 /* make a normal font string out of the lf thing:*/
3185 sprintf((char *)p, "%s:h%d", font_name, pixels_to_points(
3186 lf.lfHeight < 0 ? -lf.lfHeight : lf.lfHeight, TRUE));
3187 while (*p)
3188 {
3189 if (*p == ' ')
3190 *p = '_';
3191 ++p;
3192 }
3193 if (lf.lfItalic)
3194 STRCAT(p, ":i");
3195 if (lf.lfWeight >= FW_BOLD)
3196 STRCAT(p, ":b");
3197 if (lf.lfUnderline)
3198 STRCAT(p, ":u");
3199 if (lf.lfStrikeOut)
3200 STRCAT(p, ":s");
3201 if (charset_name != NULL)
3202 {
3203 STRCAT(p, ":c");
3204 STRCAT(p, charset_name);
3205 }
Bram Moolenaar7c1c6db2016-04-03 22:08:05 +02003206 if (quality_name != NULL)
3207 {
3208 STRCAT(p, ":q");
3209 STRCAT(p, quality_name);
3210 }
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003211 }
3212
3213#ifdef FEAT_MBYTE
3214 if (font_name != lf.lfFaceName)
3215 vim_free(font_name);
3216#endif
3217 return (char_u *)res;
3218}
3219
3220
3221#ifdef FEAT_MBYTE_IME
3222/*
3223 * Set correct LOGFONT to IME. Use 'guifontwide' if available, otherwise use
3224 * 'guifont'
3225 */
3226 static void
3227update_im_font(void)
3228{
3229 LOGFONT lf_wide;
3230
3231 if (p_guifontwide != NULL && *p_guifontwide != NUL
3232 && gui.wide_font != NOFONT
3233 && GetObject((HFONT)gui.wide_font, sizeof(lf_wide), &lf_wide))
3234 norm_logfont = lf_wide;
3235 else
3236 norm_logfont = sub_logfont;
3237 im_set_font(&norm_logfont);
3238}
3239#endif
3240
3241#ifdef FEAT_MBYTE
3242/*
3243 * Handler of gui.wide_font (p_guifontwide) changed notification.
3244 */
3245 void
3246gui_mch_wide_font_changed(void)
3247{
3248 LOGFONT lf;
3249
3250# ifdef FEAT_MBYTE_IME
3251 update_im_font();
3252# endif
3253
3254 gui_mch_free_font(gui.wide_ital_font);
3255 gui.wide_ital_font = NOFONT;
3256 gui_mch_free_font(gui.wide_bold_font);
3257 gui.wide_bold_font = NOFONT;
3258 gui_mch_free_font(gui.wide_boldital_font);
3259 gui.wide_boldital_font = NOFONT;
3260
3261 if (gui.wide_font
3262 && GetObject((HFONT)gui.wide_font, sizeof(lf), &lf))
3263 {
3264 if (!lf.lfItalic)
3265 {
3266 lf.lfItalic = TRUE;
3267 gui.wide_ital_font = get_font_handle(&lf);
3268 lf.lfItalic = FALSE;
3269 }
3270 if (lf.lfWeight < FW_BOLD)
3271 {
3272 lf.lfWeight = FW_BOLD;
3273 gui.wide_bold_font = get_font_handle(&lf);
3274 if (!lf.lfItalic)
3275 {
3276 lf.lfItalic = TRUE;
3277 gui.wide_boldital_font = get_font_handle(&lf);
3278 }
3279 }
3280 }
3281}
3282#endif
3283
3284/*
3285 * Initialise vim to use the font with the given name.
3286 * Return FAIL if the font could not be loaded, OK otherwise.
3287 */
3288/*ARGSUSED*/
3289 int
3290gui_mch_init_font(char_u *font_name, int fontset)
3291{
3292 LOGFONT lf;
3293 GuiFont font = NOFONT;
3294 char_u *p;
3295
3296 /* Load the font */
3297 if (get_logfont(&lf, font_name, NULL, TRUE) == OK)
3298 font = get_font_handle(&lf);
3299 if (font == NOFONT)
3300 return FAIL;
3301
3302 if (font_name == NULL)
3303 font_name = (char_u *)lf.lfFaceName;
3304#if defined(FEAT_MBYTE_IME) || defined(GLOBAL_IME)
3305 norm_logfont = lf;
3306 sub_logfont = lf;
3307#endif
3308#ifdef FEAT_MBYTE_IME
3309 update_im_font();
3310#endif
3311 gui_mch_free_font(gui.norm_font);
3312 gui.norm_font = font;
3313 current_font_height = lf.lfHeight;
3314 GetFontSize(font);
3315
3316 p = logfont2name(lf);
3317 if (p != NULL)
3318 {
3319 hl_set_font_name(p);
3320
3321 /* When setting 'guifont' to "*" replace it with the actual font name.
3322 * */
3323 if (STRCMP(font_name, "*") == 0 && STRCMP(p_guifont, "*") == 0)
3324 {
3325 vim_free(p_guifont);
3326 p_guifont = p;
3327 }
3328 else
3329 vim_free(p);
3330 }
3331
3332 gui_mch_free_font(gui.ital_font);
3333 gui.ital_font = NOFONT;
3334 gui_mch_free_font(gui.bold_font);
3335 gui.bold_font = NOFONT;
3336 gui_mch_free_font(gui.boldital_font);
3337 gui.boldital_font = NOFONT;
3338
3339 if (!lf.lfItalic)
3340 {
3341 lf.lfItalic = TRUE;
3342 gui.ital_font = get_font_handle(&lf);
3343 lf.lfItalic = FALSE;
3344 }
3345 if (lf.lfWeight < FW_BOLD)
3346 {
3347 lf.lfWeight = FW_BOLD;
3348 gui.bold_font = get_font_handle(&lf);
3349 if (!lf.lfItalic)
3350 {
3351 lf.lfItalic = TRUE;
3352 gui.boldital_font = get_font_handle(&lf);
3353 }
3354 }
3355
3356 return OK;
3357}
3358
3359#ifndef WPF_RESTORETOMAXIMIZED
3360# define WPF_RESTORETOMAXIMIZED 2 /* just in case someone doesn't have it */
3361#endif
3362
3363/*
3364 * Return TRUE if the GUI window is maximized, filling the whole screen.
3365 */
3366 int
3367gui_mch_maximized(void)
3368{
3369 WINDOWPLACEMENT wp;
3370
3371 wp.length = sizeof(WINDOWPLACEMENT);
3372 if (GetWindowPlacement(s_hwnd, &wp))
3373 return wp.showCmd == SW_SHOWMAXIMIZED
3374 || (wp.showCmd == SW_SHOWMINIMIZED
3375 && wp.flags == WPF_RESTORETOMAXIMIZED);
3376
3377 return 0;
3378}
3379
3380/*
3381 * Called when the font changed while the window is maximized. Compute the
3382 * new Rows and Columns. This is like resizing the window.
3383 */
3384 void
3385gui_mch_newfont(void)
3386{
3387 RECT rect;
3388
3389 GetWindowRect(s_hwnd, &rect);
3390 if (win_socket_id == 0)
3391 {
3392 gui_resize_shell(rect.right - rect.left
3393 - (GetSystemMetrics(SM_CXFRAME) +
3394 GetSystemMetrics(SM_CXPADDEDBORDER)) * 2,
3395 rect.bottom - rect.top
3396 - (GetSystemMetrics(SM_CYFRAME) +
3397 GetSystemMetrics(SM_CXPADDEDBORDER)) * 2
3398 - GetSystemMetrics(SM_CYCAPTION)
3399#ifdef FEAT_MENU
3400 - gui_mswin_get_menu_height(FALSE)
3401#endif
3402 );
3403 }
3404 else
3405 {
3406 /* Inside another window, don't use the frame and border. */
3407 gui_resize_shell(rect.right - rect.left,
3408 rect.bottom - rect.top
3409#ifdef FEAT_MENU
3410 - gui_mswin_get_menu_height(FALSE)
3411#endif
3412 );
3413 }
3414}
3415
3416/*
3417 * Set the window title
3418 */
3419/*ARGSUSED*/
3420 void
3421gui_mch_settitle(
3422 char_u *title,
3423 char_u *icon)
3424{
3425 set_window_title(s_hwnd, (title == NULL ? "VIM" : (char *)title));
3426}
3427
Bram Moolenaara6b7a082016-08-10 20:53:05 +02003428#if defined(FEAT_MOUSESHAPE) || defined(PROTO)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003429/* Table for shape IDCs. Keep in sync with the mshape_names[] table in
3430 * misc2.c! */
3431static LPCSTR mshape_idcs[] =
3432{
3433 IDC_ARROW, /* arrow */
3434 MAKEINTRESOURCE(0), /* blank */
3435 IDC_IBEAM, /* beam */
3436 IDC_SIZENS, /* updown */
3437 IDC_SIZENS, /* udsizing */
3438 IDC_SIZEWE, /* leftright */
3439 IDC_SIZEWE, /* lrsizing */
3440 IDC_WAIT, /* busy */
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003441 IDC_NO, /* no */
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003442 IDC_ARROW, /* crosshair */
3443 IDC_ARROW, /* hand1 */
3444 IDC_ARROW, /* hand2 */
3445 IDC_ARROW, /* pencil */
3446 IDC_ARROW, /* question */
3447 IDC_ARROW, /* right-arrow */
3448 IDC_UPARROW, /* up-arrow */
3449 IDC_ARROW /* last one */
3450};
3451
3452 void
3453mch_set_mouse_shape(int shape)
3454{
3455 LPCSTR idc;
3456
3457 if (shape == MSHAPE_HIDE)
3458 ShowCursor(FALSE);
3459 else
3460 {
3461 if (shape >= MSHAPE_NUMBERED)
3462 idc = IDC_ARROW;
3463 else
3464 idc = mshape_idcs[shape];
3465#ifdef SetClassLongPtr
3466 SetClassLongPtr(s_textArea, GCLP_HCURSOR, (__int3264)(LONG_PTR)LoadCursor(NULL, idc));
3467#else
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003468 SetClassLong(s_textArea, GCL_HCURSOR, (long_u)LoadCursor(NULL, idc));
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003469#endif
3470 if (!p_mh)
3471 {
3472 POINT mp;
3473
3474 /* Set the position to make it redrawn with the new shape. */
3475 (void)GetCursorPos((LPPOINT)&mp);
3476 (void)SetCursorPos(mp.x, mp.y);
3477 ShowCursor(TRUE);
3478 }
3479 }
3480}
3481#endif
3482
Bram Moolenaara6b7a082016-08-10 20:53:05 +02003483#if defined(FEAT_BROWSE) || defined(PROTO)
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003484/*
3485 * The file browser exists in two versions: with "W" uses wide characters,
3486 * without "W" the current codepage. When FEAT_MBYTE is defined and on
3487 * Windows NT/2000/XP the "W" functions are used.
3488 */
3489
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003490# ifdef FEAT_MBYTE
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003491/*
3492 * Wide version of convert_filter().
3493 */
3494 static WCHAR *
3495convert_filterW(char_u *s)
3496{
3497 char_u *tmp;
3498 int len;
3499 WCHAR *res;
3500
3501 tmp = convert_filter(s);
3502 if (tmp == NULL)
3503 return NULL;
3504 len = (int)STRLEN(s) + 3;
3505 res = enc_to_utf16(tmp, &len);
3506 vim_free(tmp);
3507 return res;
3508}
3509
3510/*
3511 * Wide version of gui_mch_browse(). Keep in sync!
3512 */
3513 static char_u *
3514gui_mch_browseW(
3515 int saving,
3516 char_u *title,
3517 char_u *dflt,
3518 char_u *ext,
3519 char_u *initdir,
3520 char_u *filter)
3521{
3522 /* We always use the wide function. This means enc_to_utf16() must work,
3523 * otherwise it fails miserably! */
3524 OPENFILENAMEW fileStruct;
3525 WCHAR fileBuf[MAXPATHL];
3526 WCHAR *wp;
3527 int i;
3528 WCHAR *titlep = NULL;
3529 WCHAR *extp = NULL;
3530 WCHAR *initdirp = NULL;
3531 WCHAR *filterp;
3532 char_u *p;
3533
3534 if (dflt == NULL)
3535 fileBuf[0] = NUL;
3536 else
3537 {
3538 wp = enc_to_utf16(dflt, NULL);
3539 if (wp == NULL)
3540 fileBuf[0] = NUL;
3541 else
3542 {
3543 for (i = 0; wp[i] != NUL && i < MAXPATHL - 1; ++i)
3544 fileBuf[i] = wp[i];
3545 fileBuf[i] = NUL;
3546 vim_free(wp);
3547 }
3548 }
3549
3550 /* Convert the filter to Windows format. */
3551 filterp = convert_filterW(filter);
3552
3553 vim_memset(&fileStruct, 0, sizeof(OPENFILENAMEW));
Bram Moolenaarb04a98f2016-12-01 20:32:29 +01003554# ifdef OPENFILENAME_SIZE_VERSION_400W
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003555 /* be compatible with Windows NT 4.0 */
Bram Moolenaar89e375a2016-03-15 18:09:57 +01003556 fileStruct.lStructSize = OPENFILENAME_SIZE_VERSION_400W;
Bram Moolenaarb04a98f2016-12-01 20:32:29 +01003557# else
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003558 fileStruct.lStructSize = sizeof(fileStruct);
Bram Moolenaarb04a98f2016-12-01 20:32:29 +01003559# endif
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003560
3561 if (title != NULL)
3562 titlep = enc_to_utf16(title, NULL);
3563 fileStruct.lpstrTitle = titlep;
3564
3565 if (ext != NULL)
3566 extp = enc_to_utf16(ext, NULL);
3567 fileStruct.lpstrDefExt = extp;
3568
3569 fileStruct.lpstrFile = fileBuf;
3570 fileStruct.nMaxFile = MAXPATHL;
3571 fileStruct.lpstrFilter = filterp;
3572 fileStruct.hwndOwner = s_hwnd; /* main Vim window is owner*/
3573 /* has an initial dir been specified? */
3574 if (initdir != NULL && *initdir != NUL)
3575 {
3576 /* Must have backslashes here, no matter what 'shellslash' says */
3577 initdirp = enc_to_utf16(initdir, NULL);
3578 if (initdirp != NULL)
3579 {
3580 for (wp = initdirp; *wp != NUL; ++wp)
3581 if (*wp == '/')
3582 *wp = '\\';
3583 }
3584 fileStruct.lpstrInitialDir = initdirp;
3585 }
3586
3587 /*
3588 * TODO: Allow selection of multiple files. Needs another arg to this
3589 * function to ask for it, and need to use OFN_ALLOWMULTISELECT below.
3590 * Also, should we use OFN_FILEMUSTEXIST when opening? Vim can edit on
3591 * files that don't exist yet, so I haven't put it in. What about
3592 * OFN_PATHMUSTEXIST?
3593 * Don't use OFN_OVERWRITEPROMPT, Vim has its own ":confirm" dialog.
3594 */
3595 fileStruct.Flags = (OFN_NOCHANGEDIR | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY);
Bram Moolenaarb04a98f2016-12-01 20:32:29 +01003596# ifdef FEAT_SHORTCUT
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003597 if (curbuf->b_p_bin)
3598 fileStruct.Flags |= OFN_NODEREFERENCELINKS;
Bram Moolenaarb04a98f2016-12-01 20:32:29 +01003599# endif
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003600 if (saving)
3601 {
3602 if (!GetSaveFileNameW(&fileStruct))
3603 return NULL;
3604 }
3605 else
3606 {
3607 if (!GetOpenFileNameW(&fileStruct))
3608 return NULL;
3609 }
3610
3611 vim_free(filterp);
3612 vim_free(initdirp);
3613 vim_free(titlep);
3614 vim_free(extp);
3615
3616 /* Convert from UCS2 to 'encoding'. */
3617 p = utf16_to_enc(fileBuf, NULL);
3618 if (p != NULL)
3619 /* when out of memory we get garbage for non-ASCII chars */
3620 STRCPY(fileBuf, p);
3621 vim_free(p);
3622
3623 /* Give focus back to main window (when using MDI). */
3624 SetFocus(s_hwnd);
3625
3626 /* Shorten the file name if possible */
3627 return vim_strsave(shorten_fname1((char_u *)fileBuf));
3628}
3629# endif /* FEAT_MBYTE */
3630
3631
3632/*
3633 * Convert the string s to the proper format for a filter string by replacing
3634 * the \t and \n delimiters with \0.
3635 * Returns the converted string in allocated memory.
3636 *
3637 * Keep in sync with convert_filterW() above!
3638 */
3639 static char_u *
3640convert_filter(char_u *s)
3641{
3642 char_u *res;
3643 unsigned s_len = (unsigned)STRLEN(s);
3644 unsigned i;
3645
3646 res = alloc(s_len + 3);
3647 if (res != NULL)
3648 {
3649 for (i = 0; i < s_len; ++i)
3650 if (s[i] == '\t' || s[i] == '\n')
3651 res[i] = '\0';
3652 else
3653 res[i] = s[i];
3654 res[s_len] = NUL;
3655 /* Add two extra NULs to make sure it's properly terminated. */
3656 res[s_len + 1] = NUL;
3657 res[s_len + 2] = NUL;
3658 }
3659 return res;
3660}
3661
3662/*
3663 * Select a directory.
3664 */
3665 char_u *
3666gui_mch_browsedir(char_u *title, char_u *initdir)
3667{
3668 /* We fake this: Use a filter that doesn't select anything and a default
3669 * file name that won't be used. */
3670 return gui_mch_browse(0, title, (char_u *)_("Not Used"), NULL,
3671 initdir, (char_u *)_("Directory\t*.nothing\n"));
3672}
3673
3674/*
3675 * Pop open a file browser and return the file selected, in allocated memory,
3676 * or NULL if Cancel is hit.
3677 * saving - TRUE if the file will be saved to, FALSE if it will be opened.
3678 * title - Title message for the file browser dialog.
3679 * dflt - Default name of file.
3680 * ext - Default extension to be added to files without extensions.
3681 * initdir - directory in which to open the browser (NULL = current dir)
3682 * filter - Filter for matched files to choose from.
3683 *
3684 * Keep in sync with gui_mch_browseW() above!
3685 */
3686 char_u *
3687gui_mch_browse(
3688 int saving,
3689 char_u *title,
3690 char_u *dflt,
3691 char_u *ext,
3692 char_u *initdir,
3693 char_u *filter)
3694{
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003695# ifdef FEAT_MBYTE
3696 return gui_mch_browseW(saving, title, dflt, ext, initdir, filter);
3697# else
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003698 OPENFILENAME fileStruct;
3699 char_u fileBuf[MAXPATHL];
3700 char_u *initdirp = NULL;
3701 char_u *filterp;
3702 char_u *p;
3703
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003704 if (dflt == NULL)
3705 fileBuf[0] = NUL;
3706 else
3707 vim_strncpy(fileBuf, dflt, MAXPATHL - 1);
3708
3709 /* Convert the filter to Windows format. */
3710 filterp = convert_filter(filter);
3711
3712 vim_memset(&fileStruct, 0, sizeof(OPENFILENAME));
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003713# ifdef OPENFILENAME_SIZE_VERSION_400
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003714 /* be compatible with Windows NT 4.0 */
3715 fileStruct.lStructSize = OPENFILENAME_SIZE_VERSION_400;
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003716# else
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003717 fileStruct.lStructSize = sizeof(fileStruct);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003718# endif
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003719
3720 fileStruct.lpstrTitle = (LPSTR)title;
3721 fileStruct.lpstrDefExt = (LPSTR)ext;
3722
3723 fileStruct.lpstrFile = (LPSTR)fileBuf;
3724 fileStruct.nMaxFile = MAXPATHL;
3725 fileStruct.lpstrFilter = (LPSTR)filterp;
3726 fileStruct.hwndOwner = s_hwnd; /* main Vim window is owner*/
3727 /* has an initial dir been specified? */
3728 if (initdir != NULL && *initdir != NUL)
3729 {
3730 /* Must have backslashes here, no matter what 'shellslash' says */
3731 initdirp = vim_strsave(initdir);
3732 if (initdirp != NULL)
3733 for (p = initdirp; *p != NUL; ++p)
3734 if (*p == '/')
3735 *p = '\\';
3736 fileStruct.lpstrInitialDir = (LPSTR)initdirp;
3737 }
3738
3739 /*
3740 * TODO: Allow selection of multiple files. Needs another arg to this
3741 * function to ask for it, and need to use OFN_ALLOWMULTISELECT below.
3742 * Also, should we use OFN_FILEMUSTEXIST when opening? Vim can edit on
3743 * files that don't exist yet, so I haven't put it in. What about
3744 * OFN_PATHMUSTEXIST?
3745 * Don't use OFN_OVERWRITEPROMPT, Vim has its own ":confirm" dialog.
3746 */
3747 fileStruct.Flags = (OFN_NOCHANGEDIR | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003748# ifdef FEAT_SHORTCUT
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003749 if (curbuf->b_p_bin)
3750 fileStruct.Flags |= OFN_NODEREFERENCELINKS;
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003751# endif
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003752 if (saving)
3753 {
3754 if (!GetSaveFileName(&fileStruct))
3755 return NULL;
3756 }
3757 else
3758 {
3759 if (!GetOpenFileName(&fileStruct))
3760 return NULL;
3761 }
3762
3763 vim_free(filterp);
3764 vim_free(initdirp);
3765
3766 /* Give focus back to main window (when using MDI). */
3767 SetFocus(s_hwnd);
3768
3769 /* Shorten the file name if possible */
3770 return vim_strsave(shorten_fname1((char_u *)fileBuf));
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003771# endif
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003772}
3773#endif /* FEAT_BROWSE */
3774
3775/*ARGSUSED*/
3776 static void
3777_OnDropFiles(
3778 HWND hwnd,
3779 HDROP hDrop)
3780{
3781#ifdef FEAT_WINDOWS
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003782# define BUFPATHLEN _MAX_PATH
3783# define DRAGQVAL 0xFFFFFFFF
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003784# ifdef FEAT_MBYTE
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003785 WCHAR wszFile[BUFPATHLEN];
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003786# endif
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003787 char szFile[BUFPATHLEN];
3788 UINT cFiles = DragQueryFile(hDrop, DRAGQVAL, NULL, 0);
3789 UINT i;
3790 char_u **fnames;
3791 POINT pt;
3792 int_u modifiers = 0;
3793
3794 /* TRACE("_OnDropFiles: %d files dropped\n", cFiles); */
3795
3796 /* Obtain dropped position */
3797 DragQueryPoint(hDrop, &pt);
3798 MapWindowPoints(s_hwnd, s_textArea, &pt, 1);
3799
3800 reset_VIsual();
3801
3802 fnames = (char_u **)alloc(cFiles * sizeof(char_u *));
3803
3804 if (fnames != NULL)
3805 for (i = 0; i < cFiles; ++i)
3806 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003807# ifdef FEAT_MBYTE
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003808 if (DragQueryFileW(hDrop, i, wszFile, BUFPATHLEN) > 0)
3809 fnames[i] = utf16_to_enc(wszFile, NULL);
3810 else
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003811# endif
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003812 {
3813 DragQueryFile(hDrop, i, szFile, BUFPATHLEN);
3814 fnames[i] = vim_strsave((char_u *)szFile);
3815 }
3816 }
3817
3818 DragFinish(hDrop);
3819
3820 if (fnames != NULL)
3821 {
3822 if ((GetKeyState(VK_SHIFT) & 0x8000) != 0)
3823 modifiers |= MOUSE_SHIFT;
3824 if ((GetKeyState(VK_CONTROL) & 0x8000) != 0)
3825 modifiers |= MOUSE_CTRL;
3826 if ((GetKeyState(VK_MENU) & 0x8000) != 0)
3827 modifiers |= MOUSE_ALT;
3828
3829 gui_handle_drop(pt.x, pt.y, modifiers, fnames, cFiles);
3830
3831 s_need_activate = TRUE;
3832 }
3833#endif
3834}
3835
3836/*ARGSUSED*/
3837 static int
3838_OnScroll(
3839 HWND hwnd,
3840 HWND hwndCtl,
3841 UINT code,
3842 int pos)
3843{
3844 static UINT prev_code = 0; /* code of previous call */
3845 scrollbar_T *sb, *sb_info;
3846 long val;
3847 int dragging = FALSE;
3848 int dont_scroll_save = dont_scroll;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003849 SCROLLINFO si;
3850
3851 si.cbSize = sizeof(si);
3852 si.fMask = SIF_POS;
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003853
3854 sb = gui_mswin_find_scrollbar(hwndCtl);
3855 if (sb == NULL)
3856 return 0;
3857
3858 if (sb->wp != NULL) /* Left or right scrollbar */
3859 {
3860 /*
3861 * Careful: need to get scrollbar info out of first (left) scrollbar
3862 * for window, but keep real scrollbar too because we must pass it to
3863 * gui_drag_scrollbar().
3864 */
3865 sb_info = &sb->wp->w_scrollbars[0];
3866 }
3867 else /* Bottom scrollbar */
3868 sb_info = sb;
3869 val = sb_info->value;
3870
3871 switch (code)
3872 {
3873 case SB_THUMBTRACK:
3874 val = pos;
3875 dragging = TRUE;
3876 if (sb->scroll_shift > 0)
3877 val <<= sb->scroll_shift;
3878 break;
3879 case SB_LINEDOWN:
3880 val++;
3881 break;
3882 case SB_LINEUP:
3883 val--;
3884 break;
3885 case SB_PAGEDOWN:
3886 val += (sb_info->size > 2 ? sb_info->size - 2 : 1);
3887 break;
3888 case SB_PAGEUP:
3889 val -= (sb_info->size > 2 ? sb_info->size - 2 : 1);
3890 break;
3891 case SB_TOP:
3892 val = 0;
3893 break;
3894 case SB_BOTTOM:
3895 val = sb_info->max;
3896 break;
3897 case SB_ENDSCROLL:
3898 if (prev_code == SB_THUMBTRACK)
3899 {
3900 /*
3901 * "pos" only gives us 16-bit data. In case of large file,
3902 * use GetScrollPos() which returns 32-bit. Unfortunately it
3903 * is not valid while the scrollbar is being dragged.
3904 */
3905 val = GetScrollPos(hwndCtl, SB_CTL);
3906 if (sb->scroll_shift > 0)
3907 val <<= sb->scroll_shift;
3908 }
3909 break;
3910
3911 default:
3912 /* TRACE("Unknown scrollbar event %d\n", code); */
3913 return 0;
3914 }
3915 prev_code = code;
3916
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003917 si.nPos = (sb->scroll_shift > 0) ? val >> sb->scroll_shift : val;
3918 SetScrollInfo(hwndCtl, SB_CTL, &si, TRUE);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003919
3920 /*
3921 * When moving a vertical scrollbar, move the other vertical scrollbar too.
3922 */
3923 if (sb->wp != NULL)
3924 {
3925 scrollbar_T *sba = sb->wp->w_scrollbars;
3926 HWND id = sba[ (sb == sba + SBAR_LEFT) ? SBAR_RIGHT : SBAR_LEFT].id;
3927
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003928 SetScrollInfo(id, SB_CTL, &si, TRUE);
Bram Moolenaarcf7164a2016-02-20 13:55:06 +01003929 }
3930
3931 /* Don't let us be interrupted here by another message. */
3932 s_busy_processing = TRUE;
3933
3934 /* When "allow_scrollbar" is FALSE still need to remember the new
3935 * position, but don't actually scroll by setting "dont_scroll". */
3936 dont_scroll = !allow_scrollbar;
3937
3938 gui_drag_scrollbar(sb, val, dragging);
3939
3940 s_busy_processing = FALSE;
3941 dont_scroll = dont_scroll_save;
3942
3943 return 0;
3944}
3945
3946
3947/*
3948 * Get command line arguments.
3949 * Use "prog" as the name of the program and "cmdline" as the arguments.
3950 * Copy the arguments to allocated memory.
3951 * Return the number of arguments (including program name).
3952 * Return pointers to the arguments in "argvp". Memory is allocated with
3953 * malloc(), use free() instead of vim_free().
3954 * Return pointer to buffer in "tofree".
3955 * Returns zero when out of memory.
3956 */
3957/*ARGSUSED*/
3958 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 */
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +00005591/*ARGSUSED*/
Bram Moolenaar071d4272004-06-13 20:20:40 +00005592 void
5593gui_mch_set_shellsize(int width, int height,
Bram Moolenaarafa24992006-03-27 20:58:26 +00005594 int min_width, int min_height, int base_width, int base_height,
5595 int direction)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005596{
5597 RECT workarea_rect;
5598 int win_width, win_height;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005599 WINDOWPLACEMENT wndpl;
5600
Bram Moolenaar910f66f2006-04-05 20:41:53 +00005601 /* Try to keep window completely on screen. */
5602 /* Get position of the screen work area. This is the part that is not
5603 * used by the taskbar or appbars. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005604 get_work_area(&workarea_rect);
5605
Bram Moolenaar84a05ac2013-05-06 04:24:17 +02005606 /* Get current position of our window. Note that the .left and .top are
Bram Moolenaar910f66f2006-04-05 20:41:53 +00005607 * relative to the work area. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005608 wndpl.length = sizeof(WINDOWPLACEMENT);
5609 GetWindowPlacement(s_hwnd, &wndpl);
5610
5611 /* Resizing a maximized window looks very strange, unzoom it first.
5612 * But don't do it when still starting up, it may have been requested in
5613 * the shortcut. */
5614 if (wndpl.showCmd == SW_SHOWMAXIMIZED && starting == 0)
5615 {
5616 ShowWindow(s_hwnd, SW_SHOWNORMAL);
5617 /* Need to get the settings of the normal window. */
5618 GetWindowPlacement(s_hwnd, &wndpl);
5619 }
5620
Bram Moolenaar071d4272004-06-13 20:20:40 +00005621 /* compute the size of the outside of the window */
Bram Moolenaar9d488952013-07-21 17:53:58 +02005622 win_width = width + (GetSystemMetrics(SM_CXFRAME) +
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02005623 GetSystemMetrics(SM_CXPADDEDBORDER)) * 2;
Bram Moolenaar9d488952013-07-21 17:53:58 +02005624 win_height = height + (GetSystemMetrics(SM_CYFRAME) +
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02005625 GetSystemMetrics(SM_CXPADDEDBORDER)) * 2
Bram Moolenaar071d4272004-06-13 20:20:40 +00005626 + GetSystemMetrics(SM_CYCAPTION)
5627#ifdef FEAT_MENU
5628 + gui_mswin_get_menu_height(FALSE)
5629#endif
5630 ;
5631
Bram Moolenaar6ef47c22012-01-04 20:29:22 +01005632 /* The following should take care of keeping Vim on the same monitor, no
5633 * matter if the secondary monitor is left or right of the primary
5634 * monitor. */
5635 wndpl.rcNormalPosition.right = wndpl.rcNormalPosition.left + win_width;
5636 wndpl.rcNormalPosition.bottom = wndpl.rcNormalPosition.top + win_height;
Bram Moolenaar56a907a2006-05-06 21:44:30 +00005637
Bram Moolenaar6ef47c22012-01-04 20:29:22 +01005638 /* If the window is going off the screen, move it on to the screen. */
Bram Moolenaar910f66f2006-04-05 20:41:53 +00005639 if ((direction & RESIZE_HOR)
Bram Moolenaar6ef47c22012-01-04 20:29:22 +01005640 && wndpl.rcNormalPosition.right > workarea_rect.right)
5641 OffsetRect(&wndpl.rcNormalPosition,
5642 workarea_rect.right - wndpl.rcNormalPosition.right, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005643
Bram Moolenaar6ef47c22012-01-04 20:29:22 +01005644 if ((direction & RESIZE_HOR)
5645 && wndpl.rcNormalPosition.left < workarea_rect.left)
5646 OffsetRect(&wndpl.rcNormalPosition,
5647 workarea_rect.left - wndpl.rcNormalPosition.left, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005648
Bram Moolenaarafa24992006-03-27 20:58:26 +00005649 if ((direction & RESIZE_VERT)
Bram Moolenaar6ef47c22012-01-04 20:29:22 +01005650 && wndpl.rcNormalPosition.bottom > workarea_rect.bottom)
5651 OffsetRect(&wndpl.rcNormalPosition,
5652 0, workarea_rect.bottom - wndpl.rcNormalPosition.bottom);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005653
Bram Moolenaar6ef47c22012-01-04 20:29:22 +01005654 if ((direction & RESIZE_VERT)
5655 && wndpl.rcNormalPosition.top < workarea_rect.top)
5656 OffsetRect(&wndpl.rcNormalPosition,
5657 0, workarea_rect.top - wndpl.rcNormalPosition.top);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005658
5659 /* set window position - we should use SetWindowPlacement rather than
5660 * SetWindowPos as the MSDN docs say the coord systems returned by
5661 * these two are not compatible. */
5662 SetWindowPlacement(s_hwnd, &wndpl);
5663
5664 SetActiveWindow(s_hwnd);
5665 SetFocus(s_hwnd);
5666
5667#ifdef FEAT_MENU
5668 /* Menu may wrap differently now */
5669 gui_mswin_get_menu_height(!gui.starting);
5670#endif
5671}
5672
5673
5674 void
5675gui_mch_set_scrollbar_thumb(
5676 scrollbar_T *sb,
5677 long val,
5678 long size,
5679 long max)
5680{
5681 SCROLLINFO info;
5682
5683 sb->scroll_shift = 0;
5684 while (max > 32767)
5685 {
5686 max = (max + 1) >> 1;
5687 val >>= 1;
5688 size >>= 1;
5689 ++sb->scroll_shift;
5690 }
5691
5692 if (sb->scroll_shift > 0)
5693 ++size;
5694
5695 info.cbSize = sizeof(info);
5696 info.fMask = SIF_POS | SIF_RANGE | SIF_PAGE;
5697 info.nPos = val;
5698 info.nMin = 0;
5699 info.nMax = max;
5700 info.nPage = size;
5701 SetScrollInfo(sb->id, SB_CTL, &info, TRUE);
5702}
5703
5704
5705/*
5706 * Set the current text font.
5707 */
5708 void
5709gui_mch_set_font(GuiFont font)
5710{
5711 gui.currFont = font;
5712}
5713
5714
5715/*
5716 * Set the current text foreground color.
5717 */
5718 void
5719gui_mch_set_fg_color(guicolor_T color)
5720{
5721 gui.currFgColor = color;
5722}
5723
5724/*
5725 * Set the current text background color.
5726 */
5727 void
5728gui_mch_set_bg_color(guicolor_T color)
5729{
5730 gui.currBgColor = color;
5731}
5732
Bram Moolenaare2cc9702005-03-15 22:43:58 +00005733/*
5734 * Set the current text special color.
5735 */
5736 void
5737gui_mch_set_sp_color(guicolor_T color)
5738{
5739 gui.currSpColor = color;
5740}
5741
Bram Moolenaar071d4272004-06-13 20:20:40 +00005742#if defined(FEAT_MBYTE) && defined(FEAT_MBYTE_IME)
5743/*
5744 * Multi-byte handling, originally by Sung-Hoon Baek.
5745 * First static functions (no prototypes generated).
5746 */
5747#ifdef _MSC_VER
5748# include <ime.h> /* Apparently not needed for Cygwin, MingW or Borland. */
5749#endif
5750#include <imm.h>
5751
5752/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00005753 * handle WM_IME_NOTIFY message
5754 */
Bram Moolenaard857f0e2005-06-21 22:37:39 +00005755/*ARGSUSED*/
Bram Moolenaar071d4272004-06-13 20:20:40 +00005756 static LRESULT
5757_OnImeNotify(HWND hWnd, DWORD dwCommand, DWORD dwData)
5758{
5759 LRESULT lResult = 0;
5760 HIMC hImc;
5761
5762 if (!pImmGetContext || (hImc = pImmGetContext(hWnd)) == (HIMC)0)
5763 return lResult;
5764 switch (dwCommand)
5765 {
5766 case IMN_SETOPENSTATUS:
5767 if (pImmGetOpenStatus(hImc))
5768 {
5769 pImmSetCompositionFont(hImc, &norm_logfont);
5770 im_set_position(gui.row, gui.col);
5771
5772 /* Disable langmap */
5773 State &= ~LANGMAP;
5774 if (State & INSERT)
5775 {
5776#if defined(FEAT_WINDOWS) && defined(FEAT_KEYMAP)
5777 /* Unshown 'keymap' in status lines */
5778 if (curbuf->b_p_iminsert == B_IMODE_LMAP)
5779 {
5780 /* Save cursor position */
5781 int old_row = gui.row;
5782 int old_col = gui.col;
5783
5784 // This must be called here before
5785 // status_redraw_curbuf(), otherwise the mode
5786 // message may appear in the wrong position.
5787 showmode();
5788 status_redraw_curbuf();
5789 update_screen(0);
5790 /* Restore cursor position */
5791 gui.row = old_row;
5792 gui.col = old_col;
5793 }
5794#endif
5795 }
5796 }
5797 gui_update_cursor(TRUE, FALSE);
5798 lResult = 0;
5799 break;
5800 }
5801 pImmReleaseContext(hWnd, hImc);
5802 return lResult;
5803}
5804
Bram Moolenaard857f0e2005-06-21 22:37:39 +00005805/*ARGSUSED*/
Bram Moolenaar071d4272004-06-13 20:20:40 +00005806 static LRESULT
5807_OnImeComposition(HWND hwnd, WPARAM dbcs, LPARAM param)
5808{
5809 char_u *ret;
5810 int len;
5811
5812 if ((param & GCS_RESULTSTR) == 0) /* Composition unfinished. */
5813 return 0;
5814
5815 ret = GetResultStr(hwnd, GCS_RESULTSTR, &len);
5816 if (ret != NULL)
5817 {
5818 add_to_input_buf_csi(ret, len);
5819 vim_free(ret);
5820 return 1;
5821 }
5822 return 0;
5823}
5824
5825/*
5826 * get the current composition string, in UCS-2; *lenp is the number of
5827 * *lenp is the number of Unicode characters.
5828 */
5829 static short_u *
5830GetCompositionString_inUCS2(HIMC hIMC, DWORD GCS, int *lenp)
5831{
5832 LONG ret;
5833 LPWSTR wbuf = NULL;
5834 char_u *buf;
5835
5836 if (!pImmGetContext)
5837 return NULL; /* no imm32.dll */
5838
5839 /* Try Unicode; this'll always work on NT regardless of codepage. */
5840 ret = pImmGetCompositionStringW(hIMC, GCS, NULL, 0);
5841 if (ret == 0)
5842 return NULL; /* empty */
5843
5844 if (ret > 0)
5845 {
5846 /* Allocate the requested buffer plus space for the NUL character. */
5847 wbuf = (LPWSTR)alloc(ret + sizeof(WCHAR));
5848 if (wbuf != NULL)
5849 {
5850 pImmGetCompositionStringW(hIMC, GCS, wbuf, ret);
5851 *lenp = ret / sizeof(WCHAR);
5852 }
5853 return (short_u *)wbuf;
5854 }
5855
5856 /* ret < 0; we got an error, so try the ANSI version. This'll work
5857 * on 9x/ME, but only if the codepage happens to be set to whatever
5858 * we're inputting. */
5859 ret = pImmGetCompositionStringA(hIMC, GCS, NULL, 0);
5860 if (ret <= 0)
5861 return NULL; /* empty or error */
5862
5863 buf = alloc(ret);
5864 if (buf == NULL)
5865 return NULL;
5866 pImmGetCompositionStringA(hIMC, GCS, buf, ret);
5867
5868 /* convert from codepage to UCS-2 */
Bram Moolenaar418f81b2016-02-16 20:12:02 +01005869 MultiByteToWideChar_alloc(GetACP(), 0, (LPCSTR)buf, ret, &wbuf, lenp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005870 vim_free(buf);
5871
5872 return (short_u *)wbuf;
5873}
5874
5875/*
5876 * void GetResultStr()
5877 *
5878 * This handles WM_IME_COMPOSITION with GCS_RESULTSTR flag on.
5879 * get complete composition string
5880 */
5881 static char_u *
5882GetResultStr(HWND hwnd, int GCS, int *lenp)
5883{
5884 HIMC hIMC; /* Input context handle. */
5885 short_u *buf = NULL;
5886 char_u *convbuf = NULL;
5887
5888 if (!pImmGetContext || (hIMC = pImmGetContext(hwnd)) == (HIMC)0)
5889 return NULL;
5890
5891 /* Reads in the composition string. */
5892 buf = GetCompositionString_inUCS2(hIMC, GCS, lenp);
5893 if (buf == NULL)
5894 return NULL;
5895
Bram Moolenaar36f692d2008-11-20 16:10:17 +00005896 convbuf = utf16_to_enc(buf, lenp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005897 pImmReleaseContext(hwnd, hIMC);
5898 vim_free(buf);
5899 return convbuf;
5900}
5901#endif
5902
5903/* For global functions we need prototypes. */
5904#if (defined(FEAT_MBYTE) && defined(FEAT_MBYTE_IME)) || defined(PROTO)
5905
5906/*
5907 * set font to IM.
5908 */
5909 void
5910im_set_font(LOGFONT *lf)
5911{
5912 HIMC hImc;
5913
5914 if (pImmGetContext && (hImc = pImmGetContext(s_hwnd)) != (HIMC)0)
5915 {
5916 pImmSetCompositionFont(hImc, lf);
5917 pImmReleaseContext(s_hwnd, hImc);
5918 }
5919}
5920
5921/*
5922 * Notify cursor position to IM.
5923 */
5924 void
5925im_set_position(int row, int col)
5926{
5927 HIMC hImc;
5928
5929 if (pImmGetContext && (hImc = pImmGetContext(s_hwnd)) != (HIMC)0)
5930 {
5931 COMPOSITIONFORM cfs;
5932
5933 cfs.dwStyle = CFS_POINT;
5934 cfs.ptCurrentPos.x = FILL_X(col);
5935 cfs.ptCurrentPos.y = FILL_Y(row);
5936 MapWindowPoints(s_textArea, s_hwnd, &cfs.ptCurrentPos, 1);
5937 pImmSetCompositionWindow(hImc, &cfs);
5938
5939 pImmReleaseContext(s_hwnd, hImc);
5940 }
5941}
5942
5943/*
5944 * Set IM status on ("active" is TRUE) or off ("active" is FALSE).
5945 */
5946 void
5947im_set_active(int active)
5948{
5949 HIMC hImc;
5950 static HIMC hImcOld = (HIMC)0;
5951
5952 if (pImmGetContext) /* if NULL imm32.dll wasn't loaded (yet) */
5953 {
5954 if (p_imdisable)
5955 {
5956 if (hImcOld == (HIMC)0)
5957 {
5958 hImcOld = pImmGetContext(s_hwnd);
5959 if (hImcOld)
5960 pImmAssociateContext(s_hwnd, (HIMC)0);
5961 }
5962 active = FALSE;
5963 }
5964 else if (hImcOld != (HIMC)0)
5965 {
5966 pImmAssociateContext(s_hwnd, hImcOld);
5967 hImcOld = (HIMC)0;
5968 }
5969
5970 hImc = pImmGetContext(s_hwnd);
5971 if (hImc)
5972 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00005973 /*
5974 * for Korean ime
5975 */
5976 HKL hKL = GetKeyboardLayout(0);
5977
5978 if (LOWORD(hKL) == MAKELANGID(LANG_KOREAN, SUBLANG_KOREAN))
5979 {
5980 static DWORD dwConversionSaved = 0, dwSentenceSaved = 0;
5981 static BOOL bSaved = FALSE;
5982
5983 if (active)
5984 {
5985 /* if we have a saved conversion status, restore it */
5986 if (bSaved)
5987 pImmSetConversionStatus(hImc, dwConversionSaved,
5988 dwSentenceSaved);
5989 bSaved = FALSE;
5990 }
5991 else
5992 {
5993 /* save conversion status and disable korean */
5994 if (pImmGetConversionStatus(hImc, &dwConversionSaved,
5995 &dwSentenceSaved))
5996 {
5997 bSaved = TRUE;
5998 pImmSetConversionStatus(hImc,
5999 dwConversionSaved & ~(IME_CMODE_NATIVE
6000 | IME_CMODE_FULLSHAPE),
6001 dwSentenceSaved);
6002 }
6003 }
6004 }
6005
Bram Moolenaar071d4272004-06-13 20:20:40 +00006006 pImmSetOpenStatus(hImc, active);
6007 pImmReleaseContext(s_hwnd, hImc);
6008 }
6009 }
6010}
6011
6012/*
6013 * Get IM status. When IM is on, return not 0. Else return 0.
6014 */
6015 int
Bram Moolenaar68c2f632016-01-30 17:24:07 +01006016im_get_status(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006017{
6018 int status = 0;
6019 HIMC hImc;
6020
6021 if (pImmGetContext && (hImc = pImmGetContext(s_hwnd)) != (HIMC)0)
6022 {
6023 status = pImmGetOpenStatus(hImc) ? 1 : 0;
6024 pImmReleaseContext(s_hwnd, hImc);
6025 }
6026 return status;
6027}
6028
6029#endif /* FEAT_MBYTE && FEAT_MBYTE_IME */
6030
6031#if defined(FEAT_MBYTE) && !defined(FEAT_MBYTE_IME) && defined(GLOBAL_IME)
6032/* Win32 with GLOBAL IME */
6033
6034/*
6035 * Notify cursor position to IM.
6036 */
6037 void
6038im_set_position(int row, int col)
6039{
6040 /* Win32 with GLOBAL IME */
6041 POINT p;
6042
6043 p.x = FILL_X(col);
6044 p.y = FILL_Y(row);
6045 MapWindowPoints(s_textArea, s_hwnd, &p, 1);
6046 global_ime_set_position(&p);
6047}
6048
6049/*
6050 * Set IM status on ("active" is TRUE) or off ("active" is FALSE).
6051 */
6052 void
6053im_set_active(int active)
6054{
6055 global_ime_set_status(active);
6056}
6057
6058/*
6059 * Get IM status. When IM is on, return not 0. Else return 0.
6060 */
6061 int
Bram Moolenaard14e00e2016-01-31 17:30:51 +01006062im_get_status(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006063{
6064 return global_ime_get_status();
6065}
6066#endif
6067
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006068#ifdef FEAT_MBYTE
6069/*
Bram Moolenaar39f05632006-03-19 22:15:26 +00006070 * Convert latin9 text "text[len]" to ucs-2 in "unicodebuf".
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006071 */
6072 static void
6073latin9_to_ucs(char_u *text, int len, WCHAR *unicodebuf)
6074{
6075 int c;
6076
Bram Moolenaarca003e12006-03-17 23:19:38 +00006077 while (--len >= 0)
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006078 {
6079 c = *text++;
6080 switch (c)
6081 {
6082 case 0xa4: c = 0x20ac; break; /* euro */
6083 case 0xa6: c = 0x0160; break; /* S hat */
6084 case 0xa8: c = 0x0161; break; /* S -hat */
6085 case 0xb4: c = 0x017d; break; /* Z hat */
6086 case 0xb8: c = 0x017e; break; /* Z -hat */
6087 case 0xbc: c = 0x0152; break; /* OE */
6088 case 0xbd: c = 0x0153; break; /* oe */
6089 case 0xbe: c = 0x0178; break; /* Y */
6090 }
6091 *unicodebuf++ = c;
6092 }
6093}
6094#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006095
6096#ifdef FEAT_RIGHTLEFT
6097/*
6098 * What is this for? In the case where you are using Win98 or Win2K or later,
6099 * and you are using a Hebrew font (or Arabic!), Windows does you a favor and
6100 * reverses the string sent to the TextOut... family. This sucks, because we
6101 * go to a lot of effort to do the right thing, and there doesn't seem to be a
6102 * way to tell Windblows not to do this!
6103 *
6104 * The short of it is that this 'RevOut' only gets called if you are running
6105 * one of the new, "improved" MS OSes, and only if you are running in
6106 * 'rightleft' mode. It makes display take *slightly* longer, but not
6107 * noticeably so.
6108 */
6109 static void
6110RevOut( HDC s_hdc,
6111 int col,
6112 int row,
6113 UINT foptions,
6114 CONST RECT *pcliprect,
6115 LPCTSTR text,
6116 UINT len,
6117 CONST INT *padding)
6118{
6119 int ix;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006120
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006121 for (ix = 0; ix < (int)len; ++ix)
6122 ExtTextOut(s_hdc, col + TEXT_X(ix), row, foptions,
6123 pcliprect, text + ix, 1, padding);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006124}
6125#endif
6126
6127 void
6128gui_mch_draw_string(
6129 int row,
6130 int col,
6131 char_u *text,
6132 int len,
6133 int flags)
6134{
6135 static int *padding = NULL;
6136 static int pad_size = 0;
6137 int i;
6138 const RECT *pcliprect = NULL;
6139 UINT foptions = 0;
6140#ifdef FEAT_MBYTE
6141 static WCHAR *unicodebuf = NULL;
6142 static int *unicodepdy = NULL;
Bram Moolenaar402d2fe2005-04-15 21:00:38 +00006143 static int unibuflen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006144 int n = 0;
6145#endif
6146 HPEN hpen, old_pen;
6147 int y;
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02006148#ifdef FEAT_DIRECTX
6149 int font_is_ttf_or_vector = 0;
6150#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006151
Bram Moolenaar071d4272004-06-13 20:20:40 +00006152 /*
6153 * Italic and bold text seems to have an extra row of pixels at the bottom
6154 * (below where the bottom of the character should be). If we draw the
6155 * characters with a solid background, the top row of pixels in the
6156 * character below will be overwritten. We can fix this by filling in the
6157 * background ourselves, to the correct character proportions, and then
6158 * writing the character in transparent mode. Still have a problem when
6159 * the character is "_", which gets written on to the character below.
6160 * New fix: set gui.char_ascent to -1. This shifts all characters up one
6161 * pixel in their slots, which fixes the problem with the bottom row of
6162 * pixels. We still need this code because otherwise the top row of pixels
6163 * becomes a problem. - webb.
6164 */
6165 static HBRUSH hbr_cache[2] = {NULL, NULL};
6166 static guicolor_T brush_color[2] = {INVALCOLOR, INVALCOLOR};
6167 static int brush_lru = 0;
6168 HBRUSH hbr;
6169 RECT rc;
6170
6171 if (!(flags & DRAW_TRANSP))
6172 {
6173 /*
6174 * Clear background first.
6175 * Note: FillRect() excludes right and bottom of rectangle.
6176 */
6177 rc.left = FILL_X(col);
6178 rc.top = FILL_Y(row);
6179#ifdef FEAT_MBYTE
6180 if (has_mbyte)
6181 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006182 /* Compute the length in display cells. */
Bram Moolenaar72597a52010-07-18 15:31:08 +02006183 rc.right = FILL_X(col + mb_string2cells(text, len));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006184 }
6185 else
6186#endif
6187 rc.right = FILL_X(col + len);
6188 rc.bottom = FILL_Y(row + 1);
6189
6190 /* Cache the created brush, that saves a lot of time. We need two:
6191 * one for cursor background and one for the normal background. */
6192 if (gui.currBgColor == brush_color[0])
6193 {
6194 hbr = hbr_cache[0];
6195 brush_lru = 1;
6196 }
6197 else if (gui.currBgColor == brush_color[1])
6198 {
6199 hbr = hbr_cache[1];
6200 brush_lru = 0;
6201 }
6202 else
6203 {
6204 if (hbr_cache[brush_lru] != NULL)
6205 DeleteBrush(hbr_cache[brush_lru]);
6206 hbr_cache[brush_lru] = CreateSolidBrush(gui.currBgColor);
6207 brush_color[brush_lru] = gui.currBgColor;
6208 hbr = hbr_cache[brush_lru];
6209 brush_lru = !brush_lru;
6210 }
6211 FillRect(s_hdc, &rc, hbr);
6212
6213 SetBkMode(s_hdc, TRANSPARENT);
6214
6215 /*
6216 * When drawing block cursor, prevent inverted character spilling
6217 * over character cell (can happen with bold/italic)
6218 */
6219 if (flags & DRAW_CURSOR)
6220 {
6221 pcliprect = &rc;
6222 foptions = ETO_CLIPPED;
6223 }
6224 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006225 SetTextColor(s_hdc, gui.currFgColor);
6226 SelectFont(s_hdc, gui.currFont);
6227
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02006228#ifdef FEAT_DIRECTX
6229 if (IS_ENABLE_DIRECTX())
6230 {
6231 TEXTMETRIC tm;
6232
6233 GetTextMetrics(s_hdc, &tm);
6234 if (tm.tmPitchAndFamily & (TMPF_TRUETYPE | TMPF_VECTOR))
6235 {
6236 font_is_ttf_or_vector = 1;
6237 DWriteContext_SetFont(s_dwc, (HFONT)gui.currFont);
6238 }
6239 }
6240#endif
6241
Bram Moolenaar071d4272004-06-13 20:20:40 +00006242 if (pad_size != Columns || padding == NULL || padding[0] != gui.char_width)
6243 {
6244 vim_free(padding);
6245 pad_size = Columns;
6246
Bram Moolenaarc54b8a72005-09-30 21:20:29 +00006247 /* Don't give an out-of-memory message here, it would call us
6248 * recursively. */
6249 padding = (int *)lalloc(pad_size * sizeof(int), FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006250 if (padding != NULL)
6251 for (i = 0; i < pad_size; i++)
6252 padding[i] = gui.char_width;
6253 }
6254
Bram Moolenaar071d4272004-06-13 20:20:40 +00006255 /*
6256 * We have to provide the padding argument because italic and bold versions
6257 * of fixed-width fonts are often one pixel or so wider than their normal
6258 * versions.
6259 * No check for DRAW_BOLD, Windows will have done it already.
6260 */
6261
6262#ifdef FEAT_MBYTE
6263 /* Check if there are any UTF-8 characters. If not, use normal text
6264 * output to speed up output. */
6265 if (enc_utf8)
6266 for (n = 0; n < len; ++n)
6267 if (text[n] >= 0x80)
6268 break;
6269
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02006270#if defined(FEAT_DIRECTX)
6271 /* Quick hack to enable DirectWrite. To use DirectWrite (antialias), it is
6272 * required that unicode drawing routine, currently. So this forces it
6273 * enabled. */
6274 if (enc_utf8 && IS_ENABLE_DIRECTX())
6275 n = 0; /* Keep n < len, to enter block for unicode. */
6276#endif
6277
Bram Moolenaar071d4272004-06-13 20:20:40 +00006278 /* Check if the Unicode buffer exists and is big enough. Create it
Bram Moolenaar402d2fe2005-04-15 21:00:38 +00006279 * with the same length as the multi-byte string, the number of wide
Bram Moolenaar071d4272004-06-13 20:20:40 +00006280 * characters is always equal or smaller. */
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006281 if ((enc_utf8
6282 || (enc_codepage > 0 && (int)GetACP() != enc_codepage)
6283 || enc_latin9)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006284 && (unicodebuf == NULL || len > unibuflen))
6285 {
6286 vim_free(unicodebuf);
Bram Moolenaarc54b8a72005-09-30 21:20:29 +00006287 unicodebuf = (WCHAR *)lalloc(len * sizeof(WCHAR), FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006288
6289 vim_free(unicodepdy);
Bram Moolenaarc54b8a72005-09-30 21:20:29 +00006290 unicodepdy = (int *)lalloc(len * sizeof(int), FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006291
6292 unibuflen = len;
6293 }
6294
6295 if (enc_utf8 && n < len && unicodebuf != NULL)
6296 {
6297 /* Output UTF-8 characters. Caller has already separated
6298 * composing characters. */
Bram Moolenaarca003e12006-03-17 23:19:38 +00006299 int i;
6300 int wlen; /* string length in words */
6301 int clen; /* string length in characters */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006302 int cells; /* cell width of string up to composing char */
6303 int cw; /* width of current cell */
Bram Moolenaar362e1a32006-03-06 23:29:24 +00006304 int c;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006305
Bram Moolenaar97b2ad32006-03-18 21:40:56 +00006306 wlen = 0;
Bram Moolenaarca003e12006-03-17 23:19:38 +00006307 clen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006308 cells = 0;
Bram Moolenaarca003e12006-03-17 23:19:38 +00006309 for (i = 0; i < len; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00006310 {
Bram Moolenaar362e1a32006-03-06 23:29:24 +00006311 c = utf_ptr2char(text + i);
6312 if (c >= 0x10000)
6313 {
6314 /* Turn into UTF-16 encoding. */
Bram Moolenaarca003e12006-03-17 23:19:38 +00006315 unicodebuf[wlen++] = ((c - 0x10000) >> 10) + 0xD800;
6316 unicodebuf[wlen++] = ((c - 0x10000) & 0x3ff) + 0xDC00;
Bram Moolenaar362e1a32006-03-06 23:29:24 +00006317 }
6318 else
6319 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00006320 unicodebuf[wlen++] = c;
Bram Moolenaar362e1a32006-03-06 23:29:24 +00006321 }
6322 cw = utf_char2cells(c);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006323 if (cw > 2) /* don't use 4 for unprintable char */
6324 cw = 1;
6325 if (unicodepdy != NULL)
6326 {
6327 /* Use unicodepdy to make characters fit as we expect, even
6328 * when the font uses different widths (e.g., bold character
6329 * is wider). */
Bram Moolenaard804fdf2016-02-27 16:04:58 +01006330 if (c >= 0x10000)
6331 {
6332 unicodepdy[wlen - 2] = cw * gui.char_width;
6333 unicodepdy[wlen - 1] = 0;
6334 }
6335 else
6336 unicodepdy[wlen - 1] = cw * gui.char_width;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006337 }
6338 cells += cw;
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00006339 i += utfc_ptr2len_len(text + i, len - i);
Bram Moolenaarca003e12006-03-17 23:19:38 +00006340 ++clen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006341 }
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02006342#if defined(FEAT_DIRECTX)
6343 if (IS_ENABLE_DIRECTX() && font_is_ttf_or_vector)
6344 {
Bram Moolenaar9b352c42014-08-06 16:49:55 +02006345 /* Add one to "cells" for italics. */
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02006346 DWriteContext_DrawText(s_dwc, s_hdc, unicodebuf, wlen,
Bram Moolenaar9b352c42014-08-06 16:49:55 +02006347 TEXT_X(col), TEXT_Y(row), FILL_X(cells + 1), FILL_Y(1),
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02006348 gui.char_width, gui.currFgColor);
6349 }
6350 else
6351#endif
6352 ExtTextOutW(s_hdc, TEXT_X(col), TEXT_Y(row),
6353 foptions, pcliprect, unicodebuf, wlen, unicodepdy);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006354 len = cells; /* used for underlining */
6355 }
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006356 else if ((enc_codepage > 0 && (int)GetACP() != enc_codepage) || enc_latin9)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006357 {
6358 /* If we want to display codepage data, and the current CP is not the
6359 * ANSI one, we need to go via Unicode. */
6360 if (unicodebuf != NULL)
6361 {
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006362 if (enc_latin9)
6363 latin9_to_ucs(text, len, unicodebuf);
6364 else
6365 len = MultiByteToWideChar(enc_codepage,
Bram Moolenaar071d4272004-06-13 20:20:40 +00006366 MB_PRECOMPOSED,
6367 (char *)text, len,
6368 (LPWSTR)unicodebuf, unibuflen);
6369 if (len != 0)
Bram Moolenaar19a09a12005-03-04 23:39:37 +00006370 {
6371 /* Use unicodepdy to make characters fit as we expect, even
6372 * when the font uses different widths (e.g., bold character
6373 * is wider). */
6374 if (unicodepdy != NULL)
6375 {
6376 int i;
6377 int cw;
6378
6379 for (i = 0; i < len; ++i)
6380 {
6381 cw = utf_char2cells(unicodebuf[i]);
6382 if (cw > 2)
6383 cw = 1;
6384 unicodepdy[i] = cw * gui.char_width;
6385 }
6386 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006387 ExtTextOutW(s_hdc, TEXT_X(col), TEXT_Y(row),
Bram Moolenaar19a09a12005-03-04 23:39:37 +00006388 foptions, pcliprect, unicodebuf, len, unicodepdy);
6389 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006390 }
6391 }
6392 else
6393#endif
6394 {
6395#ifdef FEAT_RIGHTLEFT
Bram Moolenaar4ee40b02014-09-19 16:13:53 +02006396 /* Windows will mess up RL text, so we have to draw it character by
6397 * character. Only do this if RL is on, since it's slow. */
6398 if (curwin->w_p_rl)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006399 RevOut(s_hdc, TEXT_X(col), TEXT_Y(row),
6400 foptions, pcliprect, (char *)text, len, padding);
6401 else
6402#endif
6403 ExtTextOut(s_hdc, TEXT_X(col), TEXT_Y(row),
6404 foptions, pcliprect, (char *)text, len, padding);
6405 }
6406
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006407 /* Underline */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006408 if (flags & DRAW_UNDERL)
6409 {
6410 hpen = CreatePen(PS_SOLID, 1, gui.currFgColor);
6411 old_pen = SelectObject(s_hdc, hpen);
6412 /* When p_linespace is 0, overwrite the bottom row of pixels.
6413 * Otherwise put the line just below the character. */
6414 y = FILL_Y(row + 1) - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006415 if (p_linespace > 1)
6416 y -= p_linespace - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006417 MoveToEx(s_hdc, FILL_X(col), y, NULL);
6418 /* Note: LineTo() excludes the last pixel in the line. */
6419 LineTo(s_hdc, FILL_X(col + len), y);
6420 DeleteObject(SelectObject(s_hdc, old_pen));
6421 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006422
6423 /* Undercurl */
6424 if (flags & DRAW_UNDERC)
6425 {
6426 int x;
6427 int offset;
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +00006428 static const int val[8] = {1, 0, 0, 0, 1, 2, 2, 2 };
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006429
6430 y = FILL_Y(row + 1) - 1;
6431 for (x = FILL_X(col); x < FILL_X(col + len); ++x)
6432 {
6433 offset = val[x % 8];
6434 SetPixel(s_hdc, x, y - offset, gui.currSpColor);
6435 }
6436 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006437}
6438
6439
6440/*
6441 * Output routines.
6442 */
6443
6444/* Flush any output to the screen */
6445 void
6446gui_mch_flush(void)
6447{
6448# if defined(__BORLANDC__)
6449 /*
6450 * The GdiFlush declaration (in Borland C 5.01 <wingdi.h>) is not a
6451 * prototype declaration.
6452 * The compiler complains if __stdcall is not used in both declarations.
6453 */
6454 BOOL __stdcall GdiFlush(void);
6455# endif
6456
6457 GdiFlush();
6458}
6459
6460 static void
6461clear_rect(RECT *rcp)
6462{
6463 HBRUSH hbr;
6464
6465 hbr = CreateSolidBrush(gui.back_pixel);
6466 FillRect(s_hdc, rcp, hbr);
6467 DeleteBrush(hbr);
6468}
6469
6470
Bram Moolenaarc716c302006-01-21 22:12:51 +00006471 void
6472gui_mch_get_screen_dimensions(int *screen_w, int *screen_h)
6473{
6474 RECT workarea_rect;
6475
6476 get_work_area(&workarea_rect);
6477
Bram Moolenaar910f66f2006-04-05 20:41:53 +00006478 *screen_w = workarea_rect.right - workarea_rect.left
Bram Moolenaar9d488952013-07-21 17:53:58 +02006479 - (GetSystemMetrics(SM_CXFRAME) +
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02006480 GetSystemMetrics(SM_CXPADDEDBORDER)) * 2;
Bram Moolenaarc716c302006-01-21 22:12:51 +00006481
6482 /* FIXME: dirty trick: Because the gui_get_base_height() doesn't include
6483 * the menubar for MSwin, we subtract it from the screen height, so that
6484 * the window size can be made to fit on the screen. */
Bram Moolenaar910f66f2006-04-05 20:41:53 +00006485 *screen_h = workarea_rect.bottom - workarea_rect.top
Bram Moolenaar9d488952013-07-21 17:53:58 +02006486 - (GetSystemMetrics(SM_CYFRAME) +
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02006487 GetSystemMetrics(SM_CXPADDEDBORDER)) * 2
Bram Moolenaarc716c302006-01-21 22:12:51 +00006488 - GetSystemMetrics(SM_CYCAPTION)
6489#ifdef FEAT_MENU
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00006490 - gui_mswin_get_menu_height(FALSE)
Bram Moolenaarc716c302006-01-21 22:12:51 +00006491#endif
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00006492 ;
Bram Moolenaarc716c302006-01-21 22:12:51 +00006493}
6494
6495
Bram Moolenaar071d4272004-06-13 20:20:40 +00006496#if defined(FEAT_MENU) || defined(PROTO)
6497/*
6498 * Add a sub menu to the menu bar.
6499 */
6500 void
6501gui_mch_add_menu(
6502 vimmenu_T *menu,
6503 int pos)
6504{
6505 vimmenu_T *parent = menu->parent;
6506
6507 menu->submenu_id = CreatePopupMenu();
6508 menu->id = s_menu_id++;
6509
6510 if (menu_is_menubar(menu->name))
6511 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006512#ifdef FEAT_MBYTE
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006513 WCHAR *wn = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006514
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006515 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
6516 {
6517 /* 'encoding' differs from active codepage: convert menu name
6518 * and use wide function */
6519 wn = enc_to_utf16(menu->name, NULL);
6520 if (wn != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006521 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006522 MENUITEMINFOW infow;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006523
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006524 infow.cbSize = sizeof(infow);
6525 infow.fMask = MIIM_DATA | MIIM_TYPE | MIIM_ID
6526 | MIIM_SUBMENU;
6527 infow.dwItemData = (long_u)menu;
6528 infow.wID = menu->id;
6529 infow.fType = MFT_STRING;
6530 infow.dwTypeData = wn;
6531 infow.cch = (UINT)wcslen(wn);
6532 infow.hSubMenu = menu->submenu_id;
6533 InsertMenuItemW((parent == NULL)
6534 ? s_menuBar : parent->submenu_id,
6535 (UINT)pos, TRUE, &infow);
6536 vim_free(wn);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006537 }
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006538 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006539
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006540 if (wn == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006541#endif
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006542 {
6543 MENUITEMINFO info;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006544
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006545 info.cbSize = sizeof(info);
6546 info.fMask = MIIM_DATA | MIIM_TYPE | MIIM_ID | MIIM_SUBMENU;
6547 info.dwItemData = (long_u)menu;
6548 info.wID = menu->id;
6549 info.fType = MFT_STRING;
6550 info.dwTypeData = (LPTSTR)menu->name;
6551 info.cch = (UINT)STRLEN(menu->name);
6552 info.hSubMenu = menu->submenu_id;
6553 InsertMenuItem((parent == NULL)
6554 ? s_menuBar : parent->submenu_id,
6555 (UINT)pos, TRUE, &info);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006556 }
6557 }
6558
6559 /* Fix window size if menu may have wrapped */
6560 if (parent == NULL)
6561 gui_mswin_get_menu_height(!gui.starting);
6562#ifdef FEAT_TEAROFF
6563 else if (IsWindow(parent->tearoff_handle))
6564 rebuild_tearoff(parent);
6565#endif
6566}
6567
6568 void
6569gui_mch_show_popupmenu(vimmenu_T *menu)
6570{
6571 POINT mp;
6572
6573 (void)GetCursorPos((LPPOINT)&mp);
6574 gui_mch_show_popupmenu_at(menu, (int)mp.x, (int)mp.y);
6575}
6576
6577 void
Bram Moolenaar045e82d2005-07-08 22:25:33 +00006578gui_make_popup(char_u *path_name, int mouse_pos)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006579{
6580 vimmenu_T *menu = gui_find_menu(path_name);
6581
6582 if (menu != NULL)
6583 {
6584 POINT p;
6585
6586 /* Find the position of the current cursor */
6587 GetDCOrgEx(s_hdc, &p);
Bram Moolenaar045e82d2005-07-08 22:25:33 +00006588 if (mouse_pos)
6589 {
6590 int mx, my;
6591
6592 gui_mch_getmouse(&mx, &my);
6593 p.x += mx;
6594 p.y += my;
6595 }
6596 else if (curwin != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006597 {
6598 p.x += TEXT_X(W_WINCOL(curwin) + curwin->w_wcol + 1);
6599 p.y += TEXT_Y(W_WINROW(curwin) + curwin->w_wrow + 1);
6600 }
6601 msg_scroll = FALSE;
6602 gui_mch_show_popupmenu_at(menu, (int)p.x, (int)p.y);
6603 }
6604}
6605
6606#if defined(FEAT_TEAROFF) || defined(PROTO)
6607/*
6608 * Given a menu descriptor, e.g. "File.New", find it in the menu hierarchy and
6609 * create it as a pseudo-"tearoff menu".
6610 */
6611 void
6612gui_make_tearoff(char_u *path_name)
6613{
6614 vimmenu_T *menu = gui_find_menu(path_name);
6615
6616 /* Found the menu, so tear it off. */
6617 if (menu != NULL)
6618 gui_mch_tearoff(menu->dname, menu, 0xffffL, 0xffffL);
6619}
6620#endif
6621
6622/*
6623 * Add a menu item to a menu
6624 */
6625 void
6626gui_mch_add_menu_item(
6627 vimmenu_T *menu,
6628 int idx)
6629{
6630 vimmenu_T *parent = menu->parent;
6631
6632 menu->id = s_menu_id++;
6633 menu->submenu_id = NULL;
6634
6635#ifdef FEAT_TEAROFF
6636 if (STRNCMP(menu->name, TEAR_STRING, TEAR_LEN) == 0)
6637 {
6638 InsertMenu(parent->submenu_id, (UINT)idx, MF_BITMAP|MF_BYPOSITION,
6639 (UINT)menu->id, (LPCTSTR) s_htearbitmap);
6640 }
6641 else
6642#endif
6643#ifdef FEAT_TOOLBAR
6644 if (menu_is_toolbar(parent->name))
6645 {
6646 TBBUTTON newtb;
6647
6648 vim_memset(&newtb, 0, sizeof(newtb));
6649 if (menu_is_separator(menu->name))
6650 {
6651 newtb.iBitmap = 0;
6652 newtb.fsStyle = TBSTYLE_SEP;
6653 }
6654 else
6655 {
6656 newtb.iBitmap = get_toolbar_bitmap(menu);
6657 newtb.fsStyle = TBSTYLE_BUTTON;
6658 }
6659 newtb.idCommand = menu->id;
6660 newtb.fsState = TBSTATE_ENABLED;
6661 newtb.iString = 0;
6662 SendMessage(s_toolbarhwnd, TB_INSERTBUTTON, (WPARAM)idx,
6663 (LPARAM)&newtb);
6664 menu->submenu_id = (HMENU)-1;
6665 }
6666 else
6667#endif
6668 {
6669#ifdef FEAT_MBYTE
6670 WCHAR *wn = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006671
6672 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
6673 {
6674 /* 'encoding' differs from active codepage: convert menu item name
6675 * and use wide function */
Bram Moolenaar36f692d2008-11-20 16:10:17 +00006676 wn = enc_to_utf16(menu->name, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006677 if (wn != NULL)
6678 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006679 InsertMenuW(parent->submenu_id, (UINT)idx,
Bram Moolenaar071d4272004-06-13 20:20:40 +00006680 (menu_is_separator(menu->name)
6681 ? MF_SEPARATOR : MF_STRING) | MF_BYPOSITION,
6682 (UINT)menu->id, wn);
6683 vim_free(wn);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006684 }
6685 }
6686 if (wn == NULL)
6687#endif
6688 InsertMenu(parent->submenu_id, (UINT)idx,
6689 (menu_is_separator(menu->name) ? MF_SEPARATOR : MF_STRING)
6690 | MF_BYPOSITION,
6691 (UINT)menu->id, (LPCTSTR)menu->name);
6692#ifdef FEAT_TEAROFF
6693 if (IsWindow(parent->tearoff_handle))
6694 rebuild_tearoff(parent);
6695#endif
6696 }
6697}
6698
6699/*
6700 * Destroy the machine specific menu widget.
6701 */
6702 void
6703gui_mch_destroy_menu(vimmenu_T *menu)
6704{
6705#ifdef FEAT_TOOLBAR
6706 /*
6707 * is this a toolbar button?
6708 */
6709 if (menu->submenu_id == (HMENU)-1)
6710 {
6711 int iButton;
6712
6713 iButton = (int)SendMessage(s_toolbarhwnd, TB_COMMANDTOINDEX,
6714 (WPARAM)menu->id, 0);
6715 SendMessage(s_toolbarhwnd, TB_DELETEBUTTON, (WPARAM)iButton, 0);
6716 }
6717 else
6718#endif
6719 {
6720 if (menu->parent != NULL
6721 && menu_is_popup(menu->parent->dname)
6722 && menu->parent->submenu_id != NULL)
6723 RemoveMenu(menu->parent->submenu_id, menu->id, MF_BYCOMMAND);
6724 else
6725 RemoveMenu(s_menuBar, menu->id, MF_BYCOMMAND);
6726 if (menu->submenu_id != NULL)
6727 DestroyMenu(menu->submenu_id);
6728#ifdef FEAT_TEAROFF
6729 if (IsWindow(menu->tearoff_handle))
6730 DestroyWindow(menu->tearoff_handle);
6731 if (menu->parent != NULL
6732 && menu->parent->children != NULL
6733 && IsWindow(menu->parent->tearoff_handle))
6734 {
6735 /* This menu must not show up when rebuilding the tearoff window. */
6736 menu->modes = 0;
6737 rebuild_tearoff(menu->parent);
6738 }
6739#endif
6740 }
6741}
6742
6743#ifdef FEAT_TEAROFF
6744 static void
6745rebuild_tearoff(vimmenu_T *menu)
6746{
6747 /*hackish*/
6748 char_u tbuf[128];
6749 RECT trect;
6750 RECT rct;
6751 RECT roct;
6752 int x, y;
6753
6754 HWND thwnd = menu->tearoff_handle;
6755
Bram Moolenaar418f81b2016-02-16 20:12:02 +01006756 GetWindowText(thwnd, (LPSTR)tbuf, 127);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006757 if (GetWindowRect(thwnd, &trect)
6758 && GetWindowRect(s_hwnd, &rct)
6759 && GetClientRect(s_hwnd, &roct))
6760 {
6761 x = trect.left - rct.left;
6762 y = (trect.top - rct.bottom + roct.bottom);
6763 }
6764 else
6765 {
6766 x = y = 0xffffL;
6767 }
6768 DestroyWindow(thwnd);
6769 if (menu->children != NULL)
6770 {
6771 gui_mch_tearoff(tbuf, menu, x, y);
6772 if (IsWindow(menu->tearoff_handle))
6773 (void) SetWindowPos(menu->tearoff_handle,
6774 NULL,
6775 (int)trect.left,
6776 (int)trect.top,
6777 0, 0,
6778 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
6779 }
6780}
6781#endif /* FEAT_TEAROFF */
6782
6783/*
6784 * Make a menu either grey or not grey.
6785 */
6786 void
6787gui_mch_menu_grey(
6788 vimmenu_T *menu,
6789 int grey)
6790{
6791#ifdef FEAT_TOOLBAR
6792 /*
6793 * is this a toolbar button?
6794 */
6795 if (menu->submenu_id == (HMENU)-1)
6796 {
6797 SendMessage(s_toolbarhwnd, TB_ENABLEBUTTON,
6798 (WPARAM)menu->id, (LPARAM) MAKELONG((grey ? FALSE : TRUE), 0) );
6799 }
6800 else
6801#endif
Bram Moolenaar762f1752016-06-04 22:36:17 +02006802 (void)EnableMenuItem(menu->parent ? menu->parent->submenu_id : s_menuBar,
6803 menu->id, MF_BYCOMMAND | (grey ? MF_GRAYED : MF_ENABLED));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006804
6805#ifdef FEAT_TEAROFF
6806 if ((menu->parent != NULL) && (IsWindow(menu->parent->tearoff_handle)))
6807 {
6808 WORD menuID;
6809 HWND menuHandle;
6810
6811 /*
6812 * A tearoff button has changed state.
6813 */
6814 if (menu->children == NULL)
6815 menuID = (WORD)(menu->id);
6816 else
Bram Moolenaareb3593b2006-04-22 22:33:57 +00006817 menuID = (WORD)((long_u)(menu->submenu_id) | (DWORD)0x8000);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006818 menuHandle = GetDlgItem(menu->parent->tearoff_handle, menuID);
6819 if (menuHandle)
6820 EnableWindow(menuHandle, !grey);
6821
6822 }
6823#endif
6824}
6825
6826#endif /* FEAT_MENU */
6827
6828
6829/* define some macros used to make the dialogue creation more readable */
6830
6831#define add_string(s) strcpy((LPSTR)p, s); (LPSTR)p += (strlen((LPSTR)p) + 1)
6832#define add_word(x) *p++ = (x)
Bram Moolenaarf4cd3e82005-12-22 22:47:02 +00006833#define add_long(x) dwp = (DWORD *)p; *dwp++ = (x); p = (WORD *)dwp
Bram Moolenaar071d4272004-06-13 20:20:40 +00006834
6835#if defined(FEAT_GUI_DIALOG) || defined(PROTO)
6836/*
6837 * stuff for dialogs
6838 */
6839
6840/*
6841 * The callback routine used by all the dialogs. Very simple. First,
6842 * acknowledges the INITDIALOG message so that Windows knows to do standard
6843 * dialog stuff (Return = default, Esc = cancel....) Second, if a button is
6844 * pressed, return that button's ID - IDCANCEL (2), which is the button's
6845 * number.
6846 */
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +00006847/*ARGSUSED*/
Bram Moolenaar071d4272004-06-13 20:20:40 +00006848 static LRESULT CALLBACK
6849dialog_callback(
6850 HWND hwnd,
6851 UINT message,
6852 WPARAM wParam,
6853 LPARAM lParam)
6854{
6855 if (message == WM_INITDIALOG)
6856 {
6857 CenterWindow(hwnd, GetWindow(hwnd, GW_OWNER));
6858 /* Set focus to the dialog. Set the default button, if specified. */
6859 (void)SetFocus(hwnd);
6860 if (dialog_default_button > IDCANCEL)
6861 (void)SetFocus(GetDlgItem(hwnd, dialog_default_button));
Bram Moolenaar2b80e652007-08-14 14:57:55 +00006862 else
6863 /* We don't have a default, set focus on another element of the
6864 * dialog window, probably the icon */
6865 (void)SetFocus(GetDlgItem(hwnd, DLG_NONBUTTON_CONTROL));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006866 return FALSE;
6867 }
6868
6869 if (message == WM_COMMAND)
6870 {
6871 int button = LOWORD(wParam);
6872
6873 /* Don't end the dialog if something was selected that was
6874 * not a button.
6875 */
6876 if (button >= DLG_NONBUTTON_CONTROL)
6877 return TRUE;
6878
6879 /* If the edit box exists, copy the string. */
6880 if (s_textfield != NULL)
Bram Moolenaar3ca9a8a2009-01-28 20:23:17 +00006881 {
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006882# ifdef FEAT_MBYTE
Bram Moolenaar3ca9a8a2009-01-28 20:23:17 +00006883 /* If the OS is Windows NT, and 'encoding' differs from active
6884 * codepage: use wide function and convert text. */
Bram Moolenaarcea912a2016-10-12 14:20:24 +02006885 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
Bram Moolenaarcc448b32010-07-14 16:52:17 +02006886 {
Bram Moolenaar3ca9a8a2009-01-28 20:23:17 +00006887 WCHAR *wp = (WCHAR *)alloc(IOSIZE * sizeof(WCHAR));
6888 char_u *p;
6889
6890 GetDlgItemTextW(hwnd, DLG_NONBUTTON_CONTROL + 2, wp, IOSIZE);
6891 p = utf16_to_enc(wp, NULL);
6892 vim_strncpy(s_textfield, p, IOSIZE);
6893 vim_free(p);
6894 vim_free(wp);
6895 }
6896 else
6897# endif
6898 GetDlgItemText(hwnd, DLG_NONBUTTON_CONTROL + 2,
Bram Moolenaar418f81b2016-02-16 20:12:02 +01006899 (LPSTR)s_textfield, IOSIZE);
Bram Moolenaar3ca9a8a2009-01-28 20:23:17 +00006900 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006901
6902 /*
6903 * Need to check for IDOK because if the user just hits Return to
6904 * accept the default value, some reason this is what we get.
6905 */
6906 if (button == IDOK)
6907 {
6908 if (dialog_default_button > IDCANCEL)
6909 EndDialog(hwnd, dialog_default_button);
6910 }
6911 else
6912 EndDialog(hwnd, button - IDCANCEL);
6913 return TRUE;
6914 }
6915
6916 if ((message == WM_SYSCOMMAND) && (wParam == SC_CLOSE))
6917 {
6918 EndDialog(hwnd, 0);
6919 return TRUE;
6920 }
6921 return FALSE;
6922}
6923
6924/*
6925 * Create a dialog dynamically from the parameter strings.
6926 * type = type of dialog (question, alert, etc.)
6927 * title = dialog title. may be NULL for default title.
6928 * message = text to display. Dialog sizes to accommodate it.
6929 * buttons = '\n' separated list of button captions, default first.
6930 * dfltbutton = number of default button.
6931 *
6932 * This routine returns 1 if the first button is pressed,
6933 * 2 for the second, etc.
6934 *
6935 * 0 indicates Esc was pressed.
6936 * -1 for unexpected error
6937 *
6938 * If stubbing out this fn, return 1.
6939 */
6940
Bram Moolenaar418f81b2016-02-16 20:12:02 +01006941static const char *dlg_icons[] = /* must match names in resource file */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006942{
6943 "IDR_VIM",
6944 "IDR_VIM_ERROR",
6945 "IDR_VIM_ALERT",
6946 "IDR_VIM_INFO",
6947 "IDR_VIM_QUESTION"
6948};
6949
Bram Moolenaar071d4272004-06-13 20:20:40 +00006950 int
6951gui_mch_dialog(
6952 int type,
6953 char_u *title,
6954 char_u *message,
6955 char_u *buttons,
6956 int dfltbutton,
Bram Moolenaard2c340a2011-01-17 20:08:11 +01006957 char_u *textfield,
6958 int ex_cmd)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006959{
6960 WORD *p, *pdlgtemplate, *pnumitems;
Bram Moolenaarf4cd3e82005-12-22 22:47:02 +00006961 DWORD *dwp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006962 int numButtons;
6963 int *buttonWidths, *buttonPositions;
6964 int buttonYpos;
6965 int nchar, i;
6966 DWORD lStyle;
6967 int dlgwidth = 0;
6968 int dlgheight;
6969 int editboxheight;
6970 int horizWidth = 0;
6971 int msgheight;
6972 char_u *pstart;
6973 char_u *pend;
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00006974 char_u *last_white;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006975 char_u *tbuffer;
6976 RECT rect;
6977 HWND hwnd;
6978 HDC hdc;
6979 HFONT font, oldFont;
6980 TEXTMETRIC fontInfo;
6981 int fontHeight;
6982 int textWidth, minButtonWidth, messageWidth;
6983 int maxDialogWidth;
Bram Moolenaar748bf032005-02-02 23:04:36 +00006984 int maxDialogHeight;
6985 int scroll_flag = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006986 int vertical;
6987 int dlgPaddingX;
6988 int dlgPaddingY;
6989#ifdef USE_SYSMENU_FONT
6990 LOGFONT lfSysmenu;
6991 int use_lfSysmenu = FALSE;
6992#endif
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00006993 garray_T ga;
6994 int l;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006995
6996#ifndef NO_CONSOLE
6997 /* Don't output anything in silent mode ("ex -s") */
6998 if (silent_mode)
6999 return dfltbutton; /* return default option */
7000#endif
7001
Bram Moolenaar748bf032005-02-02 23:04:36 +00007002 if (s_hwnd == NULL)
7003 get_dialog_font_metrics();
Bram Moolenaar071d4272004-06-13 20:20:40 +00007004
7005 if ((type < 0) || (type > VIM_LAST_TYPE))
7006 type = 0;
7007
7008 /* allocate some memory for dialog template */
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00007009 /* TODO should compute this really */
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007010 pdlgtemplate = p = (PWORD)LocalAlloc(LPTR,
Bram Moolenaareb3593b2006-04-22 22:33:57 +00007011 DLG_ALLOC_SIZE + STRLEN(message) * 2);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007012
7013 if (p == NULL)
7014 return -1;
7015
7016 /*
Bram Moolenaar84a05ac2013-05-06 04:24:17 +02007017 * make a copy of 'buttons' to fiddle with it. compiler grizzles because
Bram Moolenaar071d4272004-06-13 20:20:40 +00007018 * vim_strsave() doesn't take a const arg (why not?), so cast away the
7019 * const.
7020 */
7021 tbuffer = vim_strsave(buttons);
7022 if (tbuffer == NULL)
7023 return -1;
7024
7025 --dfltbutton; /* Change from one-based to zero-based */
7026
7027 /* Count buttons */
7028 numButtons = 1;
7029 for (i = 0; tbuffer[i] != '\0'; i++)
7030 {
7031 if (tbuffer[i] == DLG_BUTTON_SEP)
7032 numButtons++;
7033 }
7034 if (dfltbutton >= numButtons)
7035 dfltbutton = -1;
7036
7037 /* Allocate array to hold the width of each button */
Bram Moolenaarc54b8a72005-09-30 21:20:29 +00007038 buttonWidths = (int *)lalloc(numButtons * sizeof(int), TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007039 if (buttonWidths == NULL)
7040 return -1;
7041
7042 /* Allocate array to hold the X position of each button */
Bram Moolenaarc54b8a72005-09-30 21:20:29 +00007043 buttonPositions = (int *)lalloc(numButtons * sizeof(int), TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007044 if (buttonPositions == NULL)
7045 return -1;
7046
7047 /*
7048 * Calculate how big the dialog must be.
7049 */
7050 hwnd = GetDesktopWindow();
7051 hdc = GetWindowDC(hwnd);
7052#ifdef USE_SYSMENU_FONT
7053 if (gui_w32_get_menu_font(&lfSysmenu) == OK)
7054 {
7055 font = CreateFontIndirect(&lfSysmenu);
7056 use_lfSysmenu = TRUE;
7057 }
7058 else
7059#endif
7060 font = CreateFont(-DLG_FONT_POINT_SIZE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
7061 VARIABLE_PITCH , DLG_FONT_NAME);
7062 if (s_usenewlook)
7063 {
7064 oldFont = SelectFont(hdc, font);
7065 dlgPaddingX = DLG_PADDING_X;
7066 dlgPaddingY = DLG_PADDING_Y;
7067 }
7068 else
7069 {
7070 oldFont = SelectFont(hdc, GetStockObject(SYSTEM_FONT));
7071 dlgPaddingX = DLG_OLD_STYLE_PADDING_X;
7072 dlgPaddingY = DLG_OLD_STYLE_PADDING_Y;
7073 }
7074 GetTextMetrics(hdc, &fontInfo);
7075 fontHeight = fontInfo.tmHeight;
7076
7077 /* Minimum width for horizontal button */
Bram Moolenaar418f81b2016-02-16 20:12:02 +01007078 minButtonWidth = GetTextWidth(hdc, (char_u *)"Cancel", 6);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007079
7080 /* Maximum width of a dialog, if possible */
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007081 if (s_hwnd == NULL)
7082 {
7083 RECT workarea_rect;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007084
Bram Moolenaarc716c302006-01-21 22:12:51 +00007085 /* We don't have a window, use the desktop area. */
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007086 get_work_area(&workarea_rect);
7087 maxDialogWidth = workarea_rect.right - workarea_rect.left - 100;
7088 if (maxDialogWidth > 600)
7089 maxDialogWidth = 600;
Bram Moolenaar1b1b0942013-08-01 13:20:42 +02007090 /* Leave some room for the taskbar. */
7091 maxDialogHeight = workarea_rect.bottom - workarea_rect.top - 150;
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007092 }
7093 else
7094 {
Bram Moolenaara95d8232013-08-07 15:27:11 +02007095 /* Use our own window for the size, unless it's very small. */
7096 GetWindowRect(s_hwnd, &rect);
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007097 maxDialogWidth = rect.right - rect.left
Bram Moolenaar9d488952013-07-21 17:53:58 +02007098 - (GetSystemMetrics(SM_CXFRAME) +
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02007099 GetSystemMetrics(SM_CXPADDEDBORDER)) * 2;
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007100 if (maxDialogWidth < DLG_MIN_MAX_WIDTH)
7101 maxDialogWidth = DLG_MIN_MAX_WIDTH;
Bram Moolenaar748bf032005-02-02 23:04:36 +00007102
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007103 maxDialogHeight = rect.bottom - rect.top
Bram Moolenaar1b1b0942013-08-01 13:20:42 +02007104 - (GetSystemMetrics(SM_CYFRAME) +
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02007105 GetSystemMetrics(SM_CXPADDEDBORDER)) * 4
Bram Moolenaara95d8232013-08-07 15:27:11 +02007106 - GetSystemMetrics(SM_CYCAPTION);
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007107 if (maxDialogHeight < DLG_MIN_MAX_HEIGHT)
7108 maxDialogHeight = DLG_MIN_MAX_HEIGHT;
7109 }
7110
7111 /* Set dlgwidth to width of message.
7112 * Copy the message into "ga", changing NL to CR-NL and inserting line
7113 * breaks where needed. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007114 pstart = message;
7115 messageWidth = 0;
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007116 msgheight = 0;
7117 ga_init2(&ga, sizeof(char), 500);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007118 do
7119 {
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007120 msgheight += fontHeight; /* at least one line */
7121
7122 /* Need to figure out where to break the string. The system does it
7123 * at a word boundary, which would mean we can't compute the number of
7124 * wrapped lines. */
7125 textWidth = 0;
7126 last_white = NULL;
7127 for (pend = pstart; *pend != NUL && *pend != '\n'; )
Bram Moolenaar748bf032005-02-02 23:04:36 +00007128 {
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007129#ifdef FEAT_MBYTE
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00007130 l = (*mb_ptr2len)(pend);
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007131#else
7132 l = 1;
7133#endif
7134 if (l == 1 && vim_iswhite(*pend)
7135 && textWidth > maxDialogWidth * 3 / 4)
7136 last_white = pend;
Bram Moolenaarf05d8112013-06-26 12:58:32 +02007137 textWidth += GetTextWidthEnc(hdc, pend, l);
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007138 if (textWidth >= maxDialogWidth)
Bram Moolenaar748bf032005-02-02 23:04:36 +00007139 {
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007140 /* Line will wrap. */
7141 messageWidth = maxDialogWidth;
Bram Moolenaar748bf032005-02-02 23:04:36 +00007142 msgheight += fontHeight;
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007143 textWidth = 0;
7144
7145 if (last_white != NULL)
7146 {
7147 /* break the line just after a space */
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00007148 ga.ga_len -= (int)(pend - (last_white + 1));
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007149 pend = last_white + 1;
7150 last_white = NULL;
7151 }
7152 ga_append(&ga, '\r');
7153 ga_append(&ga, '\n');
7154 continue;
Bram Moolenaar748bf032005-02-02 23:04:36 +00007155 }
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007156
7157 while (--l >= 0)
7158 ga_append(&ga, *pend++);
Bram Moolenaar748bf032005-02-02 23:04:36 +00007159 }
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007160 if (textWidth > messageWidth)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007161 messageWidth = textWidth;
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007162
7163 ga_append(&ga, '\r');
7164 ga_append(&ga, '\n');
Bram Moolenaar071d4272004-06-13 20:20:40 +00007165 pstart = pend + 1;
7166 } while (*pend != NUL);
Bram Moolenaar748bf032005-02-02 23:04:36 +00007167
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007168 if (ga.ga_data != NULL)
7169 message = ga.ga_data;
7170
Bram Moolenaar748bf032005-02-02 23:04:36 +00007171 messageWidth += 10; /* roundoff space */
7172
Bram Moolenaar071d4272004-06-13 20:20:40 +00007173 /* Add width of icon to dlgwidth, and some space */
Bram Moolenaara95d8232013-08-07 15:27:11 +02007174 dlgwidth = messageWidth + DLG_ICON_WIDTH + 3 * dlgPaddingX
7175 + GetSystemMetrics(SM_CXVSCROLL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007176
7177 if (msgheight < DLG_ICON_HEIGHT)
7178 msgheight = DLG_ICON_HEIGHT;
7179
7180 /*
7181 * Check button names. A long one will make the dialog wider.
Bram Moolenaaraea02fa2007-05-04 20:29:09 +00007182 * When called early (-register error message) p_go isn't initialized.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007183 */
Bram Moolenaaraea02fa2007-05-04 20:29:09 +00007184 vertical = (p_go != NULL && vim_strchr(p_go, GO_VERTICAL) != NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007185 if (!vertical)
7186 {
7187 // Place buttons horizontally if they fit.
7188 horizWidth = dlgPaddingX;
7189 pstart = tbuffer;
7190 i = 0;
7191 do
7192 {
7193 pend = vim_strchr(pstart, DLG_BUTTON_SEP);
7194 if (pend == NULL)
7195 pend = pstart + STRLEN(pstart); // Last button name.
Bram Moolenaarb052fe02013-06-26 13:16:20 +02007196 textWidth = GetTextWidthEnc(hdc, pstart, (int)(pend - pstart));
Bram Moolenaar071d4272004-06-13 20:20:40 +00007197 if (textWidth < minButtonWidth)
7198 textWidth = minButtonWidth;
7199 textWidth += dlgPaddingX; /* Padding within button */
7200 buttonWidths[i] = textWidth;
7201 buttonPositions[i++] = horizWidth;
7202 horizWidth += textWidth + dlgPaddingX; /* Pad between buttons */
7203 pstart = pend + 1;
7204 } while (*pend != NUL);
7205
7206 if (horizWidth > maxDialogWidth)
7207 vertical = TRUE; // Too wide to fit on the screen.
7208 else if (horizWidth > dlgwidth)
7209 dlgwidth = horizWidth;
7210 }
7211
7212 if (vertical)
7213 {
7214 // Stack buttons vertically.
7215 pstart = tbuffer;
7216 do
7217 {
7218 pend = vim_strchr(pstart, DLG_BUTTON_SEP);
7219 if (pend == NULL)
7220 pend = pstart + STRLEN(pstart); // Last button name.
Bram Moolenaarb052fe02013-06-26 13:16:20 +02007221 textWidth = GetTextWidthEnc(hdc, pstart, (int)(pend - pstart));
Bram Moolenaar071d4272004-06-13 20:20:40 +00007222 textWidth += dlgPaddingX; /* Padding within button */
7223 textWidth += DLG_VERT_PADDING_X * 2; /* Padding around button */
7224 if (textWidth > dlgwidth)
7225 dlgwidth = textWidth;
7226 pstart = pend + 1;
7227 } while (*pend != NUL);
7228 }
7229
7230 if (dlgwidth < DLG_MIN_WIDTH)
7231 dlgwidth = DLG_MIN_WIDTH; /* Don't allow a really thin dialog!*/
7232
7233 /* start to fill in the dlgtemplate information. addressing by WORDs */
7234 if (s_usenewlook)
7235 lStyle = DS_MODALFRAME | WS_CAPTION |DS_3DLOOK| WS_VISIBLE |DS_SETFONT;
7236 else
7237 lStyle = DS_MODALFRAME | WS_CAPTION |DS_3DLOOK| WS_VISIBLE;
7238
7239 add_long(lStyle);
7240 add_long(0); // (lExtendedStyle)
7241 pnumitems = p; /*save where the number of items must be stored*/
7242 add_word(0); // NumberOfItems(will change later)
7243 add_word(10); // x
7244 add_word(10); // y
7245 add_word(PixelToDialogX(dlgwidth)); // cx
7246
7247 // Dialog height.
7248 if (vertical)
Bram Moolenaara95d8232013-08-07 15:27:11 +02007249 dlgheight = msgheight + 2 * dlgPaddingY
7250 + DLG_VERT_PADDING_Y + 2 * fontHeight * numButtons;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007251 else
7252 dlgheight = msgheight + 3 * dlgPaddingY + 2 * fontHeight;
7253
7254 // Dialog needs to be taller if contains an edit box.
7255 editboxheight = fontHeight + dlgPaddingY + 4 * DLG_VERT_PADDING_Y;
7256 if (textfield != NULL)
7257 dlgheight += editboxheight;
7258
Bram Moolenaara95d8232013-08-07 15:27:11 +02007259 /* Restrict the size to a maximum. Causes a scrollbar to show up. */
7260 if (dlgheight > maxDialogHeight)
7261 {
Bram Moolenaarb5a7a8b2014-08-06 14:52:30 +02007262 msgheight = msgheight - (dlgheight - maxDialogHeight);
7263 dlgheight = maxDialogHeight;
7264 scroll_flag = WS_VSCROLL;
7265 /* Make sure scrollbar doesn't appear in the middle of the dialog */
7266 messageWidth = dlgwidth - DLG_ICON_WIDTH - 3 * dlgPaddingX;
Bram Moolenaara95d8232013-08-07 15:27:11 +02007267 }
7268
Bram Moolenaar071d4272004-06-13 20:20:40 +00007269 add_word(PixelToDialogY(dlgheight));
7270
7271 add_word(0); // Menu
7272 add_word(0); // Class
7273
7274 /* copy the title of the dialog */
7275 nchar = nCopyAnsiToWideChar(p, (title ?
7276 (LPSTR)title :
7277 (LPSTR)("Vim "VIM_VERSION_MEDIUM)));
7278 p += nchar;
7279
7280 if (s_usenewlook)
7281 {
7282 /* do the font, since DS_3DLOOK doesn't work properly */
7283#ifdef USE_SYSMENU_FONT
7284 if (use_lfSysmenu)
7285 {
7286 /* point size */
7287 *p++ = -MulDiv(lfSysmenu.lfHeight, 72,
7288 GetDeviceCaps(hdc, LOGPIXELSY));
7289 nchar = nCopyAnsiToWideChar(p, TEXT(lfSysmenu.lfFaceName));
7290 }
7291 else
7292#endif
7293 {
7294 *p++ = DLG_FONT_POINT_SIZE; // point size
7295 nchar = nCopyAnsiToWideChar(p, TEXT(DLG_FONT_NAME));
7296 }
7297 p += nchar;
7298 }
7299
7300 buttonYpos = msgheight + 2 * dlgPaddingY;
7301
7302 if (textfield != NULL)
7303 buttonYpos += editboxheight;
7304
7305 pstart = tbuffer;
7306 if (!vertical)
7307 horizWidth = (dlgwidth - horizWidth) / 2; /* Now it's X offset */
7308 for (i = 0; i < numButtons; i++)
7309 {
7310 /* get end of this button. */
7311 for ( pend = pstart;
7312 *pend && (*pend != DLG_BUTTON_SEP);
7313 pend++)
7314 ;
7315
7316 if (*pend)
7317 *pend = '\0';
7318
7319 /*
7320 * old NOTE:
7321 * setting the BS_DEFPUSHBUTTON style doesn't work because Windows sets
7322 * the focus to the first tab-able button and in so doing makes that
7323 * the default!! Grrr. Workaround: Make the default button the only
7324 * one with WS_TABSTOP style. Means user can't tab between buttons, but
7325 * he/she can use arrow keys.
7326 *
7327 * new NOTE: BS_DEFPUSHBUTTON is required to be able to select the
Bram Moolenaar2c7a7632007-05-10 18:19:11 +00007328 * right button when hitting <Enter>. E.g., for the ":confirm quit"
Bram Moolenaar071d4272004-06-13 20:20:40 +00007329 * dialog. Also needed for when the textfield is the default control.
7330 * It appears to work now (perhaps not on Win95?).
7331 */
7332 if (vertical)
7333 {
7334 p = add_dialog_element(p,
7335 (i == dfltbutton
7336 ? BS_DEFPUSHBUTTON : BS_PUSHBUTTON) | WS_TABSTOP,
7337 PixelToDialogX(DLG_VERT_PADDING_X),
7338 PixelToDialogY(buttonYpos /* TBK */
7339 + 2 * fontHeight * i),
7340 PixelToDialogX(dlgwidth - 2 * DLG_VERT_PADDING_X),
7341 (WORD)(PixelToDialogY(2 * fontHeight) - 1),
Bram Moolenaar418f81b2016-02-16 20:12:02 +01007342 (WORD)(IDCANCEL + 1 + i), (WORD)0x0080, (char *)pstart);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007343 }
7344 else
7345 {
7346 p = add_dialog_element(p,
7347 (i == dfltbutton
7348 ? BS_DEFPUSHBUTTON : BS_PUSHBUTTON) | WS_TABSTOP,
7349 PixelToDialogX(horizWidth + buttonPositions[i]),
7350 PixelToDialogY(buttonYpos), /* TBK */
7351 PixelToDialogX(buttonWidths[i]),
7352 (WORD)(PixelToDialogY(2 * fontHeight) - 1),
Bram Moolenaar418f81b2016-02-16 20:12:02 +01007353 (WORD)(IDCANCEL + 1 + i), (WORD)0x0080, (char *)pstart);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007354 }
7355 pstart = pend + 1; /*next button*/
7356 }
7357 *pnumitems += numButtons;
7358
7359 /* Vim icon */
7360 p = add_dialog_element(p, SS_ICON,
7361 PixelToDialogX(dlgPaddingX),
7362 PixelToDialogY(dlgPaddingY),
7363 PixelToDialogX(DLG_ICON_WIDTH),
7364 PixelToDialogY(DLG_ICON_HEIGHT),
7365 DLG_NONBUTTON_CONTROL + 0, (WORD)0x0082,
7366 dlg_icons[type]);
7367
Bram Moolenaar748bf032005-02-02 23:04:36 +00007368 /* Dialog message */
7369 p = add_dialog_element(p, ES_LEFT|scroll_flag|ES_MULTILINE|ES_READONLY,
7370 PixelToDialogX(2 * dlgPaddingX + DLG_ICON_WIDTH),
7371 PixelToDialogY(dlgPaddingY),
7372 (WORD)(PixelToDialogX(messageWidth) + 1),
7373 PixelToDialogY(msgheight),
Bram Moolenaar418f81b2016-02-16 20:12:02 +01007374 DLG_NONBUTTON_CONTROL + 1, (WORD)0x0081, (char *)message);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007375
7376 /* Edit box */
7377 if (textfield != NULL)
7378 {
7379 p = add_dialog_element(p, ES_LEFT|ES_AUTOHSCROLL|WS_TABSTOP|WS_BORDER,
7380 PixelToDialogX(2 * dlgPaddingX),
7381 PixelToDialogY(2 * dlgPaddingY + msgheight),
7382 PixelToDialogX(dlgwidth - 4 * dlgPaddingX),
7383 PixelToDialogY(fontHeight + dlgPaddingY),
Bram Moolenaar418f81b2016-02-16 20:12:02 +01007384 DLG_NONBUTTON_CONTROL + 2, (WORD)0x0081, (char *)textfield);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007385 *pnumitems += 1;
7386 }
7387
7388 *pnumitems += 2;
7389
7390 SelectFont(hdc, oldFont);
7391 DeleteObject(font);
7392 ReleaseDC(hwnd, hdc);
7393
7394 /* Let the dialog_callback() function know which button to make default
7395 * If we have an edit box, make that the default. We also need to tell
7396 * dialog_callback() if this dialog contains an edit box or not. We do
7397 * this by setting s_textfield if it does.
7398 */
7399 if (textfield != NULL)
7400 {
7401 dialog_default_button = DLG_NONBUTTON_CONTROL + 2;
7402 s_textfield = textfield;
7403 }
7404 else
7405 {
7406 dialog_default_button = IDCANCEL + 1 + dfltbutton;
7407 s_textfield = NULL;
7408 }
7409
7410 /* show the dialog box modally and get a return value */
7411 nchar = (int)DialogBoxIndirect(
7412 s_hinst,
7413 (LPDLGTEMPLATE)pdlgtemplate,
7414 s_hwnd,
7415 (DLGPROC)dialog_callback);
7416
7417 LocalFree(LocalHandle(pdlgtemplate));
7418 vim_free(tbuffer);
7419 vim_free(buttonWidths);
7420 vim_free(buttonPositions);
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00007421 vim_free(ga.ga_data);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007422
7423 /* Focus back to our window (for when MDI is used). */
7424 (void)SetFocus(s_hwnd);
7425
7426 return nchar;
7427}
7428
7429#endif /* FEAT_GUI_DIALOG */
Bram Moolenaareb3593b2006-04-22 22:33:57 +00007430
Bram Moolenaar071d4272004-06-13 20:20:40 +00007431/*
7432 * Put a simple element (basic class) onto a dialog template in memory.
7433 * return a pointer to where the next item should be added.
7434 *
7435 * parameters:
7436 * lStyle = additional style flags
7437 * (be careful, NT3.51 & Win32s will ignore the new ones)
7438 * x,y = x & y positions IN DIALOG UNITS
7439 * w,h = width and height IN DIALOG UNITS
7440 * Id = ID used in messages
7441 * clss = class ID, e.g 0x0080 for a button, 0x0082 for a static
7442 * caption = usually text or resource name
7443 *
7444 * TODO: use the length information noted here to enable the dialog creation
7445 * routines to work out more exactly how much memory they need to alloc.
7446 */
7447 static PWORD
7448add_dialog_element(
7449 PWORD p,
7450 DWORD lStyle,
7451 WORD x,
7452 WORD y,
7453 WORD w,
7454 WORD h,
7455 WORD Id,
7456 WORD clss,
7457 const char *caption)
7458{
7459 int nchar;
7460
7461 p = lpwAlign(p); /* Align to dword boundary*/
7462 lStyle = lStyle | WS_VISIBLE | WS_CHILD;
7463 *p++ = LOWORD(lStyle);
7464 *p++ = HIWORD(lStyle);
7465 *p++ = 0; // LOWORD (lExtendedStyle)
7466 *p++ = 0; // HIWORD (lExtendedStyle)
7467 *p++ = x;
7468 *p++ = y;
7469 *p++ = w;
7470 *p++ = h;
7471 *p++ = Id; //9 or 10 words in all
7472
7473 *p++ = (WORD)0xffff;
7474 *p++ = clss; //2 more here
7475
7476 nchar = nCopyAnsiToWideChar(p, (LPSTR)caption); //strlen(caption)+1
7477 p += nchar;
7478
7479 *p++ = 0; // advance pointer over nExtraStuff WORD - 2 more
7480
7481 return p; //total = 15+ (strlen(caption)) words
7482 // = 30 + 2(strlen(caption) bytes reqd
7483}
7484
7485
7486/*
7487 * Helper routine. Take an input pointer, return closest pointer that is
7488 * aligned on a DWORD (4 byte) boundary. Taken from the Win32SDK samples.
7489 */
7490 static LPWORD
7491lpwAlign(
7492 LPWORD lpIn)
7493{
Bram Moolenaareb3593b2006-04-22 22:33:57 +00007494 long_u ul;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007495
Bram Moolenaareb3593b2006-04-22 22:33:57 +00007496 ul = (long_u)lpIn;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007497 ul += 3;
7498 ul >>= 2;
7499 ul <<= 2;
7500 return (LPWORD)ul;
7501}
7502
7503/*
7504 * Helper routine. Takes second parameter as Ansi string, copies it to first
7505 * parameter as wide character (16-bits / char) string, and returns integer
7506 * number of wide characters (words) in string (including the trailing wide
7507 * char NULL). Partly taken from the Win32SDK samples.
7508 */
7509 static int
7510nCopyAnsiToWideChar(
7511 LPWORD lpWCStr,
7512 LPSTR lpAnsiIn)
7513{
7514 int nChar = 0;
7515#ifdef FEAT_MBYTE
7516 int len = lstrlen(lpAnsiIn) + 1; /* include NUL character */
7517 int i;
7518 WCHAR *wn;
7519
7520 if (enc_codepage == 0 && (int)GetACP() != enc_codepage)
7521 {
7522 /* Not a codepage, use our own conversion function. */
Bram Moolenaar418f81b2016-02-16 20:12:02 +01007523 wn = enc_to_utf16((char_u *)lpAnsiIn, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007524 if (wn != NULL)
7525 {
7526 wcscpy(lpWCStr, wn);
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00007527 nChar = (int)wcslen(wn) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007528 vim_free(wn);
7529 }
7530 }
7531 if (nChar == 0)
7532 /* Use Win32 conversion function. */
7533 nChar = MultiByteToWideChar(
7534 enc_codepage > 0 ? enc_codepage : CP_ACP,
7535 MB_PRECOMPOSED,
7536 lpAnsiIn, len,
7537 lpWCStr, len);
7538 for (i = 0; i < nChar; ++i)
7539 if (lpWCStr[i] == (WORD)'\t') /* replace tabs with spaces */
7540 lpWCStr[i] = (WORD)' ';
7541#else
7542 do
7543 {
7544 if (*lpAnsiIn == '\t')
7545 *lpWCStr++ = (WORD)' ';
7546 else
7547 *lpWCStr++ = (WORD)*lpAnsiIn;
7548 nChar++;
7549 } while (*lpAnsiIn++);
7550#endif
7551
7552 return nChar;
7553}
7554
7555
7556#ifdef FEAT_TEAROFF
7557/*
7558 * The callback function for all the modeless dialogs that make up the
7559 * "tearoff menus" Very simple - forward button presses (to fool Vim into
7560 * thinking its menus have been clicked), and go away when closed.
7561 */
7562 static LRESULT CALLBACK
7563tearoff_callback(
7564 HWND hwnd,
7565 UINT message,
7566 WPARAM wParam,
7567 LPARAM lParam)
7568{
7569 if (message == WM_INITDIALOG)
7570 return (TRUE);
7571
7572 /* May show the mouse pointer again. */
7573 HandleMouseHide(message, lParam);
7574
7575 if (message == WM_COMMAND)
7576 {
7577 if ((WORD)(LOWORD(wParam)) & 0x8000)
7578 {
7579 POINT mp;
7580 RECT rect;
7581
7582 if (GetCursorPos(&mp) && GetWindowRect(hwnd, &rect))
7583 {
7584 (void)TrackPopupMenu(
Bram Moolenaareb3593b2006-04-22 22:33:57 +00007585 (HMENU)(long_u)(LOWORD(wParam) ^ 0x8000),
Bram Moolenaar071d4272004-06-13 20:20:40 +00007586 TPM_LEFTALIGN | TPM_LEFTBUTTON,
7587 (int)rect.right - 8,
7588 (int)mp.y,
7589 (int)0, /*reserved param*/
7590 s_hwnd,
7591 NULL);
7592 /*
7593 * NOTE: The pop-up menu can eat the mouse up event.
7594 * We deal with this in normal.c.
7595 */
7596 }
7597 }
7598 else
7599 /* Pass on messages to the main Vim window */
7600 PostMessage(s_hwnd, WM_COMMAND, LOWORD(wParam), 0);
7601 /*
7602 * Give main window the focus back: this is so after
7603 * choosing a tearoff button you can start typing again
7604 * straight away.
7605 */
7606 (void)SetFocus(s_hwnd);
7607 return TRUE;
7608 }
7609 if ((message == WM_SYSCOMMAND) && (wParam == SC_CLOSE))
7610 {
7611 DestroyWindow(hwnd);
7612 return TRUE;
7613 }
7614
7615 /* When moved around, give main window the focus back. */
7616 if (message == WM_EXITSIZEMOVE)
7617 (void)SetActiveWindow(s_hwnd);
7618
7619 return FALSE;
7620}
7621#endif
7622
7623
7624/*
7625 * Decide whether to use the "new look" (small, non-bold font) or the "old
7626 * look" (big, clanky font) for dialogs, and work out a few values for use
7627 * later accordingly.
7628 */
7629 static void
7630get_dialog_font_metrics(void)
7631{
7632 HDC hdc;
7633 HFONT hfontTools = 0;
7634 DWORD dlgFontSize;
7635 SIZE size;
7636#ifdef USE_SYSMENU_FONT
7637 LOGFONT lfSysmenu;
7638#endif
7639
7640 s_usenewlook = FALSE;
7641
Bram Moolenaar071d4272004-06-13 20:20:40 +00007642#ifdef USE_SYSMENU_FONT
Bram Moolenaarcea912a2016-10-12 14:20:24 +02007643 if (gui_w32_get_menu_font(&lfSysmenu) == OK)
7644 hfontTools = CreateFontIndirect(&lfSysmenu);
7645 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00007646#endif
7647 hfontTools = CreateFont(-DLG_FONT_POINT_SIZE, 0, 0, 0, 0, 0, 0, 0,
7648 0, 0, 0, 0, VARIABLE_PITCH , DLG_FONT_NAME);
7649
Bram Moolenaarcea912a2016-10-12 14:20:24 +02007650 if (hfontTools)
7651 {
7652 hdc = GetDC(s_hwnd);
7653 SelectObject(hdc, hfontTools);
7654 /*
7655 * GetTextMetrics() doesn't return the right value in
7656 * tmAveCharWidth, so we have to figure out the dialog base units
7657 * ourselves.
7658 */
7659 GetTextExtentPoint(hdc,
7660 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
7661 52, &size);
7662 ReleaseDC(s_hwnd, hdc);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007663
Bram Moolenaarcea912a2016-10-12 14:20:24 +02007664 s_dlgfntwidth = (WORD)((size.cx / 26 + 1) / 2);
7665 s_dlgfntheight = (WORD)size.cy;
7666 s_usenewlook = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007667 }
7668
7669 if (!s_usenewlook)
7670 {
7671 dlgFontSize = GetDialogBaseUnits(); /* fall back to big old system*/
7672 s_dlgfntwidth = LOWORD(dlgFontSize);
7673 s_dlgfntheight = HIWORD(dlgFontSize);
7674 }
7675}
7676
7677#if defined(FEAT_MENU) && defined(FEAT_TEAROFF)
7678/*
7679 * Create a pseudo-"tearoff menu" based on the child
7680 * items of a given menu pointer.
7681 */
7682 static void
7683gui_mch_tearoff(
7684 char_u *title,
7685 vimmenu_T *menu,
7686 int initX,
7687 int initY)
7688{
7689 WORD *p, *pdlgtemplate, *pnumitems, *ptrueheight;
7690 int template_len;
7691 int nchar, textWidth, submenuWidth;
7692 DWORD lStyle;
7693 DWORD lExtendedStyle;
7694 WORD dlgwidth;
7695 WORD menuID;
7696 vimmenu_T *pmenu;
7697 vimmenu_T *the_menu = menu;
7698 HWND hwnd;
7699 HDC hdc;
7700 HFONT font, oldFont;
7701 int col, spaceWidth, len;
7702 int columnWidths[2];
7703 char_u *label, *text;
7704 int acLen = 0;
7705 int nameLen;
7706 int padding0, padding1, padding2 = 0;
7707 int sepPadding=0;
Bram Moolenaar9588a0f2005-01-08 21:45:39 +00007708 int x;
7709 int y;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007710#ifdef USE_SYSMENU_FONT
7711 LOGFONT lfSysmenu;
7712 int use_lfSysmenu = FALSE;
7713#endif
7714
7715 /*
7716 * If this menu is already torn off, move it to the mouse position.
7717 */
7718 if (IsWindow(menu->tearoff_handle))
7719 {
7720 POINT mp;
7721 if (GetCursorPos((LPPOINT)&mp))
7722 {
7723 SetWindowPos(menu->tearoff_handle, NULL, mp.x, mp.y, 0, 0,
7724 SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
7725 }
7726 return;
7727 }
7728
7729 /*
7730 * Create a new tearoff.
7731 */
7732 if (*title == MNU_HIDDEN_CHAR)
7733 title++;
7734
7735 /* Allocate memory to store the dialog template. It's made bigger when
7736 * needed. */
7737 template_len = DLG_ALLOC_SIZE;
7738 pdlgtemplate = p = (WORD *)LocalAlloc(LPTR, template_len);
7739 if (p == NULL)
7740 return;
7741
7742 hwnd = GetDesktopWindow();
7743 hdc = GetWindowDC(hwnd);
7744#ifdef USE_SYSMENU_FONT
7745 if (gui_w32_get_menu_font(&lfSysmenu) == OK)
7746 {
7747 font = CreateFontIndirect(&lfSysmenu);
7748 use_lfSysmenu = TRUE;
7749 }
7750 else
7751#endif
7752 font = CreateFont(-DLG_FONT_POINT_SIZE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
7753 VARIABLE_PITCH , DLG_FONT_NAME);
7754 if (s_usenewlook)
7755 oldFont = SelectFont(hdc, font);
7756 else
7757 oldFont = SelectFont(hdc, GetStockObject(SYSTEM_FONT));
7758
7759 /* Calculate width of a single space. Used for padding columns to the
7760 * right width. */
Bram Moolenaar418f81b2016-02-16 20:12:02 +01007761 spaceWidth = GetTextWidth(hdc, (char_u *)" ", 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007762
7763 /* Figure out max width of the text column, the accelerator column and the
7764 * optional submenu column. */
7765 submenuWidth = 0;
7766 for (col = 0; col < 2; col++)
7767 {
7768 columnWidths[col] = 0;
7769 for (pmenu = menu->children; pmenu != NULL; pmenu = pmenu->next)
7770 {
7771 /* Use "dname" here to compute the width of the visible text. */
7772 text = (col == 0) ? pmenu->dname : pmenu->actext;
7773 if (text != NULL && *text != NUL)
7774 {
7775 textWidth = GetTextWidthEnc(hdc, text, (int)STRLEN(text));
7776 if (textWidth > columnWidths[col])
7777 columnWidths[col] = textWidth;
7778 }
7779 if (pmenu->children != NULL)
7780 submenuWidth = TEAROFF_COLUMN_PADDING * spaceWidth;
7781 }
7782 }
7783 if (columnWidths[1] == 0)
7784 {
7785 /* no accelerators */
7786 if (submenuWidth != 0)
7787 columnWidths[0] += submenuWidth;
7788 else
7789 columnWidths[0] += spaceWidth;
7790 }
7791 else
7792 {
7793 /* there is an accelerator column */
7794 columnWidths[0] += TEAROFF_COLUMN_PADDING * spaceWidth;
7795 columnWidths[1] += submenuWidth;
7796 }
7797
7798 /*
7799 * Now find the total width of our 'menu'.
7800 */
7801 textWidth = columnWidths[0] + columnWidths[1];
7802 if (submenuWidth != 0)
7803 {
Bram Moolenaar418f81b2016-02-16 20:12:02 +01007804 submenuWidth = GetTextWidth(hdc, (char_u *)TEAROFF_SUBMENU_LABEL,
Bram Moolenaar071d4272004-06-13 20:20:40 +00007805 (int)STRLEN(TEAROFF_SUBMENU_LABEL));
7806 textWidth += submenuWidth;
7807 }
7808 dlgwidth = GetTextWidthEnc(hdc, title, (int)STRLEN(title));
7809 if (textWidth > dlgwidth)
7810 dlgwidth = textWidth;
7811 dlgwidth += 2 * TEAROFF_PADDING_X + TEAROFF_BUTTON_PAD_X;
7812
Bram Moolenaar071d4272004-06-13 20:20:40 +00007813 /* start to fill in the dlgtemplate information. addressing by WORDs */
7814 if (s_usenewlook)
7815 lStyle = DS_MODALFRAME | WS_CAPTION| WS_SYSMENU |DS_SETFONT| WS_VISIBLE;
7816 else
7817 lStyle = DS_MODALFRAME | WS_CAPTION| WS_SYSMENU | WS_VISIBLE;
7818
7819 lExtendedStyle = WS_EX_TOOLWINDOW|WS_EX_STATICEDGE;
7820 *p++ = LOWORD(lStyle);
7821 *p++ = HIWORD(lStyle);
7822 *p++ = LOWORD(lExtendedStyle);
7823 *p++ = HIWORD(lExtendedStyle);
7824 pnumitems = p; /* save where the number of items must be stored */
7825 *p++ = 0; // NumberOfItems(will change later)
Bram Moolenaar9588a0f2005-01-08 21:45:39 +00007826 gui_mch_getmouse(&x, &y);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007827 if (initX == 0xffffL)
Bram Moolenaar9588a0f2005-01-08 21:45:39 +00007828 *p++ = PixelToDialogX(x); // x
Bram Moolenaar071d4272004-06-13 20:20:40 +00007829 else
7830 *p++ = PixelToDialogX(initX); // x
7831 if (initY == 0xffffL)
Bram Moolenaar9588a0f2005-01-08 21:45:39 +00007832 *p++ = PixelToDialogY(y); // y
Bram Moolenaar071d4272004-06-13 20:20:40 +00007833 else
7834 *p++ = PixelToDialogY(initY); // y
7835 *p++ = PixelToDialogX(dlgwidth); // cx
7836 ptrueheight = p;
7837 *p++ = 0; // dialog height: changed later anyway
7838 *p++ = 0; // Menu
7839 *p++ = 0; // Class
7840
7841 /* copy the title of the dialog */
7842 nchar = nCopyAnsiToWideChar(p, ((*title)
7843 ? (LPSTR)title
7844 : (LPSTR)("Vim "VIM_VERSION_MEDIUM)));
7845 p += nchar;
7846
7847 if (s_usenewlook)
7848 {
7849 /* do the font, since DS_3DLOOK doesn't work properly */
7850#ifdef USE_SYSMENU_FONT
7851 if (use_lfSysmenu)
7852 {
7853 /* point size */
7854 *p++ = -MulDiv(lfSysmenu.lfHeight, 72,
7855 GetDeviceCaps(hdc, LOGPIXELSY));
7856 nchar = nCopyAnsiToWideChar(p, TEXT(lfSysmenu.lfFaceName));
7857 }
7858 else
7859#endif
7860 {
7861 *p++ = DLG_FONT_POINT_SIZE; // point size
7862 nchar = nCopyAnsiToWideChar (p, TEXT(DLG_FONT_NAME));
7863 }
7864 p += nchar;
7865 }
7866
7867 /*
7868 * Loop over all the items in the menu.
7869 * But skip over the tearbar.
7870 */
7871 if (STRCMP(menu->children->name, TEAR_STRING) == 0)
7872 menu = menu->children->next;
7873 else
7874 menu = menu->children;
7875 for ( ; menu != NULL; menu = menu->next)
7876 {
7877 if (menu->modes == 0) /* this menu has just been deleted */
7878 continue;
7879 if (menu_is_separator(menu->dname))
7880 {
7881 sepPadding += 3;
7882 continue;
7883 }
7884
7885 /* Check if there still is plenty of room in the template. Make it
7886 * larger when needed. */
7887 if (((char *)p - (char *)pdlgtemplate) + 1000 > template_len)
7888 {
7889 WORD *newp;
7890
7891 newp = (WORD *)LocalAlloc(LPTR, template_len + 4096);
7892 if (newp != NULL)
7893 {
7894 template_len += 4096;
7895 mch_memmove(newp, pdlgtemplate,
7896 (char *)p - (char *)pdlgtemplate);
7897 p = newp + (p - pdlgtemplate);
7898 pnumitems = newp + (pnumitems - pdlgtemplate);
7899 ptrueheight = newp + (ptrueheight - pdlgtemplate);
7900 LocalFree(LocalHandle(pdlgtemplate));
7901 pdlgtemplate = newp;
7902 }
7903 }
7904
7905 /* Figure out minimal length of this menu label. Use "name" for the
7906 * actual text, "dname" for estimating the displayed size. "name"
7907 * has "&a" for mnemonic and includes the accelerator. */
7908 len = nameLen = (int)STRLEN(menu->name);
7909 padding0 = (columnWidths[0] - GetTextWidthEnc(hdc, menu->dname,
7910 (int)STRLEN(menu->dname))) / spaceWidth;
7911 len += padding0;
7912
7913 if (menu->actext != NULL)
7914 {
7915 acLen = (int)STRLEN(menu->actext);
7916 len += acLen;
7917 textWidth = GetTextWidthEnc(hdc, menu->actext, acLen);
7918 }
7919 else
7920 textWidth = 0;
7921 padding1 = (columnWidths[1] - textWidth) / spaceWidth;
7922 len += padding1;
7923
7924 if (menu->children == NULL)
7925 {
7926 padding2 = submenuWidth / spaceWidth;
7927 len += padding2;
7928 menuID = (WORD)(menu->id);
7929 }
7930 else
7931 {
7932 len += (int)STRLEN(TEAROFF_SUBMENU_LABEL);
Bram Moolenaareb3593b2006-04-22 22:33:57 +00007933 menuID = (WORD)((long_u)(menu->submenu_id) | (DWORD)0x8000);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007934 }
7935
7936 /* Allocate menu label and fill it in */
7937 text = label = alloc((unsigned)len + 1);
7938 if (label == NULL)
7939 break;
7940
Bram Moolenaarce0842a2005-07-18 21:58:11 +00007941 vim_strncpy(text, menu->name, nameLen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007942 text = vim_strchr(text, TAB); /* stop at TAB before actext */
7943 if (text == NULL)
7944 text = label + nameLen; /* no actext, use whole name */
7945 while (padding0-- > 0)
7946 *text++ = ' ';
7947 if (menu->actext != NULL)
7948 {
7949 STRNCPY(text, menu->actext, acLen);
7950 text += acLen;
7951 }
7952 while (padding1-- > 0)
7953 *text++ = ' ';
7954 if (menu->children != NULL)
7955 {
7956 STRCPY(text, TEAROFF_SUBMENU_LABEL);
7957 text += STRLEN(TEAROFF_SUBMENU_LABEL);
7958 }
7959 else
7960 {
7961 while (padding2-- > 0)
7962 *text++ = ' ';
7963 }
7964 *text = NUL;
7965
7966 /*
7967 * BS_LEFT will just be ignored on Win32s/NT3.5x - on
7968 * W95/NT4 it makes the tear-off look more like a menu.
7969 */
7970 p = add_dialog_element(p,
7971 BS_PUSHBUTTON|BS_LEFT,
7972 (WORD)PixelToDialogX(TEAROFF_PADDING_X),
7973 (WORD)(sepPadding + 1 + 13 * (*pnumitems)),
7974 (WORD)PixelToDialogX(dlgwidth - 2 * TEAROFF_PADDING_X),
7975 (WORD)12,
Bram Moolenaar418f81b2016-02-16 20:12:02 +01007976 menuID, (WORD)0x0080, (char *)label);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007977 vim_free(label);
7978 (*pnumitems)++;
7979 }
7980
7981 *ptrueheight = (WORD)(sepPadding + 1 + 13 * (*pnumitems));
7982
7983
7984 /* show modelessly */
7985 the_menu->tearoff_handle = CreateDialogIndirect(
7986 s_hinst,
7987 (LPDLGTEMPLATE)pdlgtemplate,
7988 s_hwnd,
7989 (DLGPROC)tearoff_callback);
7990
7991 LocalFree(LocalHandle(pdlgtemplate));
7992 SelectFont(hdc, oldFont);
7993 DeleteObject(font);
7994 ReleaseDC(hwnd, hdc);
7995
7996 /*
7997 * Reassert ourselves as the active window. This is so that after creating
7998 * a tearoff, the user doesn't have to click with the mouse just to start
7999 * typing again!
8000 */
8001 (void)SetActiveWindow(s_hwnd);
8002
8003 /* make sure the right buttons are enabled */
8004 force_menu_update = TRUE;
8005}
8006#endif
8007
8008#if defined(FEAT_TOOLBAR) || defined(PROTO)
8009#include "gui_w32_rc.h"
8010
8011/* This not defined in older SDKs */
8012# ifndef TBSTYLE_FLAT
8013# define TBSTYLE_FLAT 0x0800
8014# endif
8015
8016/*
8017 * Create the toolbar, initially unpopulated.
8018 * (just like the menu, there are no defaults, it's all
8019 * set up through menu.vim)
8020 */
8021 static void
8022initialise_toolbar(void)
8023{
8024 InitCommonControls();
8025 s_toolbarhwnd = CreateToolbarEx(
8026 s_hwnd,
8027 WS_CHILD | TBSTYLE_TOOLTIPS | TBSTYLE_FLAT,
8028 4000, //any old big number
Bram Moolenaar2c7a7632007-05-10 18:19:11 +00008029 31, //number of images in initial bitmap
Bram Moolenaar071d4272004-06-13 20:20:40 +00008030 s_hinst,
8031 IDR_TOOLBAR1, // id of initial bitmap
8032 NULL,
8033 0, // initial number of buttons
8034 TOOLBAR_BUTTON_WIDTH, //api guide is wrong!
8035 TOOLBAR_BUTTON_HEIGHT,
8036 TOOLBAR_BUTTON_WIDTH,
8037 TOOLBAR_BUTTON_HEIGHT,
8038 sizeof(TBBUTTON)
8039 );
Bram Moolenaar5f919ee2013-07-21 17:46:43 +02008040 s_toolbar_wndproc = SubclassWindow(s_toolbarhwnd, toolbar_wndproc);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008041
8042 gui_mch_show_toolbar(vim_strchr(p_go, GO_TOOLBAR) != NULL);
8043}
8044
Bram Moolenaar5f919ee2013-07-21 17:46:43 +02008045 static LRESULT CALLBACK
8046toolbar_wndproc(
8047 HWND hwnd,
8048 UINT uMsg,
8049 WPARAM wParam,
8050 LPARAM lParam)
8051{
8052 HandleMouseHide(uMsg, lParam);
8053 return CallWindowProc(s_toolbar_wndproc, hwnd, uMsg, wParam, lParam);
8054}
8055
Bram Moolenaar071d4272004-06-13 20:20:40 +00008056 static int
8057get_toolbar_bitmap(vimmenu_T *menu)
8058{
8059 int i = -1;
8060
8061 /*
8062 * Check user bitmaps first, unless builtin is specified.
8063 */
Bram Moolenaarcea912a2016-10-12 14:20:24 +02008064 if (!menu->icon_builtin)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008065 {
8066 char_u fname[MAXPATHL];
8067 HANDLE hbitmap = NULL;
8068
8069 if (menu->iconfile != NULL)
8070 {
8071 gui_find_iconfile(menu->iconfile, fname, "bmp");
8072 hbitmap = LoadImage(
8073 NULL,
Bram Moolenaar418f81b2016-02-16 20:12:02 +01008074 (LPCSTR)fname,
Bram Moolenaar071d4272004-06-13 20:20:40 +00008075 IMAGE_BITMAP,
8076 TOOLBAR_BUTTON_WIDTH,
8077 TOOLBAR_BUTTON_HEIGHT,
8078 LR_LOADFROMFILE |
8079 LR_LOADMAP3DCOLORS
8080 );
8081 }
8082
8083 /*
8084 * If the LoadImage call failed, or the "icon=" file
8085 * didn't exist or wasn't specified, try the menu name
8086 */
8087 if (hbitmap == NULL
Bram Moolenaara5f5c8b2013-06-27 22:29:38 +02008088 && (gui_find_bitmap(
8089#ifdef FEAT_MULTI_LANG
8090 menu->en_dname != NULL ? menu->en_dname :
8091#endif
8092 menu->dname, fname, "bmp") == OK))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008093 hbitmap = LoadImage(
8094 NULL,
Bram Moolenaar418f81b2016-02-16 20:12:02 +01008095 (LPCSTR)fname,
Bram Moolenaar071d4272004-06-13 20:20:40 +00008096 IMAGE_BITMAP,
8097 TOOLBAR_BUTTON_WIDTH,
8098 TOOLBAR_BUTTON_HEIGHT,
8099 LR_LOADFROMFILE |
8100 LR_LOADMAP3DCOLORS
8101 );
8102
8103 if (hbitmap != NULL)
8104 {
8105 TBADDBITMAP tbAddBitmap;
8106
8107 tbAddBitmap.hInst = NULL;
Bram Moolenaareb3593b2006-04-22 22:33:57 +00008108 tbAddBitmap.nID = (long_u)hbitmap;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008109
8110 i = (int)SendMessage(s_toolbarhwnd, TB_ADDBITMAP,
8111 (WPARAM)1, (LPARAM)&tbAddBitmap);
8112 /* i will be set to -1 if it fails */
8113 }
8114 }
8115 if (i == -1 && menu->iconidx >= 0 && menu->iconidx < TOOLBAR_BITMAP_COUNT)
8116 i = menu->iconidx;
8117
8118 return i;
8119}
8120#endif
8121
Bram Moolenaar3991dab2006-03-27 17:01:56 +00008122#if defined(FEAT_GUI_TABLINE) || defined(PROTO)
8123 static void
8124initialise_tabline(void)
8125{
8126 InitCommonControls();
8127
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008128 s_tabhwnd = CreateWindow(WC_TABCONTROL, "Vim tabline",
Bram Moolenaareb3593b2006-04-22 22:33:57 +00008129 WS_CHILD|TCS_FOCUSNEVER|TCS_TOOLTIPS,
Bram Moolenaar3991dab2006-03-27 17:01:56 +00008130 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
8131 CW_USEDEFAULT, s_hwnd, NULL, s_hinst, NULL);
Bram Moolenaar5f919ee2013-07-21 17:46:43 +02008132 s_tabline_wndproc = SubclassWindow(s_tabhwnd, tabline_wndproc);
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008133
Bram Moolenaar551dbcc2006-04-25 22:13:59 +00008134 gui.tabline_height = TABLINE_HEIGHT;
8135
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008136# ifdef USE_SYSMENU_FONT
Bram Moolenaar551dbcc2006-04-25 22:13:59 +00008137 set_tabline_font();
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008138# endif
Bram Moolenaar3991dab2006-03-27 17:01:56 +00008139}
Bram Moolenaar5f919ee2013-07-21 17:46:43 +02008140
8141 static LRESULT CALLBACK
8142tabline_wndproc(
8143 HWND hwnd,
8144 UINT uMsg,
8145 WPARAM wParam,
8146 LPARAM lParam)
8147{
8148 HandleMouseHide(uMsg, lParam);
8149 return CallWindowProc(s_tabline_wndproc, hwnd, uMsg, wParam, lParam);
8150}
Bram Moolenaar3991dab2006-03-27 17:01:56 +00008151#endif
8152
Bram Moolenaar071d4272004-06-13 20:20:40 +00008153#if defined(FEAT_OLE) || defined(FEAT_EVAL) || defined(PROTO)
8154/*
8155 * Make the GUI window come to the foreground.
8156 */
8157 void
8158gui_mch_set_foreground(void)
8159{
8160 if (IsIconic(s_hwnd))
8161 SendMessage(s_hwnd, WM_SYSCOMMAND, SC_RESTORE, 0);
8162 SetForegroundWindow(s_hwnd);
8163}
8164#endif
8165
8166#if defined(FEAT_MBYTE_IME) && defined(DYNAMIC_IME)
8167 static void
8168dyn_imm_load(void)
8169{
Bram Moolenaarebbcb822010-10-23 14:02:54 +02008170 hLibImm = vimLoadLib("imm32.dll");
Bram Moolenaar071d4272004-06-13 20:20:40 +00008171 if (hLibImm == NULL)
8172 return;
8173
8174 pImmGetCompositionStringA
8175 = (void *)GetProcAddress(hLibImm, "ImmGetCompositionStringA");
8176 pImmGetCompositionStringW
8177 = (void *)GetProcAddress(hLibImm, "ImmGetCompositionStringW");
8178 pImmGetContext
8179 = (void *)GetProcAddress(hLibImm, "ImmGetContext");
8180 pImmAssociateContext
8181 = (void *)GetProcAddress(hLibImm, "ImmAssociateContext");
8182 pImmReleaseContext
8183 = (void *)GetProcAddress(hLibImm, "ImmReleaseContext");
8184 pImmGetOpenStatus
8185 = (void *)GetProcAddress(hLibImm, "ImmGetOpenStatus");
8186 pImmSetOpenStatus
8187 = (void *)GetProcAddress(hLibImm, "ImmSetOpenStatus");
8188 pImmGetCompositionFont
8189 = (void *)GetProcAddress(hLibImm, "ImmGetCompositionFontA");
8190 pImmSetCompositionFont
8191 = (void *)GetProcAddress(hLibImm, "ImmSetCompositionFontA");
8192 pImmSetCompositionWindow
8193 = (void *)GetProcAddress(hLibImm, "ImmSetCompositionWindow");
8194 pImmGetConversionStatus
8195 = (void *)GetProcAddress(hLibImm, "ImmGetConversionStatus");
Bram Moolenaarca003e12006-03-17 23:19:38 +00008196 pImmSetConversionStatus
8197 = (void *)GetProcAddress(hLibImm, "ImmSetConversionStatus");
Bram Moolenaar071d4272004-06-13 20:20:40 +00008198
8199 if ( pImmGetCompositionStringA == NULL
8200 || pImmGetCompositionStringW == NULL
8201 || pImmGetContext == NULL
8202 || pImmAssociateContext == NULL
8203 || pImmReleaseContext == NULL
8204 || pImmGetOpenStatus == NULL
8205 || pImmSetOpenStatus == NULL
8206 || pImmGetCompositionFont == NULL
8207 || pImmSetCompositionFont == NULL
8208 || pImmSetCompositionWindow == NULL
Bram Moolenaarca003e12006-03-17 23:19:38 +00008209 || pImmGetConversionStatus == NULL
8210 || pImmSetConversionStatus == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008211 {
8212 FreeLibrary(hLibImm);
8213 hLibImm = NULL;
8214 pImmGetContext = NULL;
8215 return;
8216 }
8217
8218 return;
8219}
8220
Bram Moolenaar071d4272004-06-13 20:20:40 +00008221#endif
8222
8223#if defined(FEAT_SIGN_ICONS) || defined(PROTO)
8224
8225# ifdef FEAT_XPM_W32
8226# define IMAGE_XPM 100
8227# endif
8228
8229typedef struct _signicon_t
8230{
8231 HANDLE hImage;
8232 UINT uType;
8233#ifdef FEAT_XPM_W32
8234 HANDLE hShape; /* Mask bitmap handle */
8235#endif
8236} signicon_t;
8237
8238 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01008239gui_mch_drawsign(int row, int col, int typenr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008240{
8241 signicon_t *sign;
8242 int x, y, w, h;
8243
8244 if (!gui.in_use || (sign = (signicon_t *)sign_get_image(typenr)) == NULL)
8245 return;
8246
8247 x = TEXT_X(col);
8248 y = TEXT_Y(row);
8249 w = gui.char_width * 2;
8250 h = gui.char_height;
8251 switch (sign->uType)
8252 {
8253 case IMAGE_BITMAP:
8254 {
8255 HDC hdcMem;
8256 HBITMAP hbmpOld;
8257
8258 hdcMem = CreateCompatibleDC(s_hdc);
8259 hbmpOld = (HBITMAP)SelectObject(hdcMem, sign->hImage);
8260 BitBlt(s_hdc, x, y, w, h, hdcMem, 0, 0, SRCCOPY);
8261 SelectObject(hdcMem, hbmpOld);
8262 DeleteDC(hdcMem);
8263 }
8264 break;
8265 case IMAGE_ICON:
8266 case IMAGE_CURSOR:
8267 DrawIconEx(s_hdc, x, y, (HICON)sign->hImage, w, h, 0, NULL, DI_NORMAL);
8268 break;
8269#ifdef FEAT_XPM_W32
8270 case IMAGE_XPM:
8271 {
8272 HDC hdcMem;
8273 HBITMAP hbmpOld;
8274
8275 hdcMem = CreateCompatibleDC(s_hdc);
8276 hbmpOld = (HBITMAP)SelectObject(hdcMem, sign->hShape);
8277 /* Make hole */
8278 BitBlt(s_hdc, x, y, w, h, hdcMem, 0, 0, SRCAND);
8279
8280 SelectObject(hdcMem, sign->hImage);
8281 /* Paint sign */
8282 BitBlt(s_hdc, x, y, w, h, hdcMem, 0, 0, SRCPAINT);
8283 SelectObject(hdcMem, hbmpOld);
8284 DeleteDC(hdcMem);
8285 }
8286 break;
8287#endif
8288 }
8289}
8290
8291 static void
8292close_signicon_image(signicon_t *sign)
8293{
8294 if (sign)
8295 switch (sign->uType)
8296 {
8297 case IMAGE_BITMAP:
8298 DeleteObject((HGDIOBJ)sign->hImage);
8299 break;
8300 case IMAGE_CURSOR:
8301 DestroyCursor((HCURSOR)sign->hImage);
8302 break;
8303 case IMAGE_ICON:
8304 DestroyIcon((HICON)sign->hImage);
8305 break;
8306#ifdef FEAT_XPM_W32
8307 case IMAGE_XPM:
8308 DeleteObject((HBITMAP)sign->hImage);
8309 DeleteObject((HBITMAP)sign->hShape);
8310 break;
8311#endif
8312 }
8313}
8314
8315 void *
Bram Moolenaar68c2f632016-01-30 17:24:07 +01008316gui_mch_register_sign(char_u *signfile)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008317{
8318 signicon_t sign, *psign;
8319 char_u *ext;
8320
Bram Moolenaar071d4272004-06-13 20:20:40 +00008321 sign.hImage = NULL;
Bram Moolenaar84a05ac2013-05-06 04:24:17 +02008322 ext = signfile + STRLEN(signfile) - 4; /* get extension */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008323 if (ext > signfile)
8324 {
8325 int do_load = 1;
8326
8327 if (!STRICMP(ext, ".bmp"))
8328 sign.uType = IMAGE_BITMAP;
8329 else if (!STRICMP(ext, ".ico"))
8330 sign.uType = IMAGE_ICON;
8331 else if (!STRICMP(ext, ".cur") || !STRICMP(ext, ".ani"))
8332 sign.uType = IMAGE_CURSOR;
8333 else
8334 do_load = 0;
8335
8336 if (do_load)
Bram Moolenaar418f81b2016-02-16 20:12:02 +01008337 sign.hImage = (HANDLE)LoadImage(NULL, (LPCSTR)signfile, sign.uType,
Bram Moolenaar071d4272004-06-13 20:20:40 +00008338 gui.char_width * 2, gui.char_height,
8339 LR_LOADFROMFILE | LR_CREATEDIBSECTION);
8340#ifdef FEAT_XPM_W32
8341 if (!STRICMP(ext, ".xpm"))
8342 {
8343 sign.uType = IMAGE_XPM;
Bram Moolenaar418f81b2016-02-16 20:12:02 +01008344 LoadXpmImage((char *)signfile, (HBITMAP *)&sign.hImage,
8345 (HBITMAP *)&sign.hShape);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008346 }
8347#endif
8348 }
8349
8350 psign = NULL;
8351 if (sign.hImage && (psign = (signicon_t *)alloc(sizeof(signicon_t)))
8352 != NULL)
8353 *psign = sign;
8354
8355 if (!psign)
8356 {
8357 if (sign.hImage)
8358 close_signicon_image(&sign);
8359 EMSG(_(e_signdata));
8360 }
8361 return (void *)psign;
8362
8363}
8364
8365 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01008366gui_mch_destroy_sign(void *sign)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008367{
8368 if (sign)
8369 {
8370 close_signicon_image((signicon_t *)sign);
8371 vim_free(sign);
8372 }
8373}
Bram Moolenaar5c06f8b2005-05-31 22:14:58 +00008374#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008375
8376#if defined(FEAT_BEVAL) || defined(PROTO)
8377
8378/* BALLOON-EVAL IMPLEMENTATION FOR WINDOWS.
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00008379 * Added by Sergey Khorev <sergey.khorev@gmail.com>
Bram Moolenaar071d4272004-06-13 20:20:40 +00008380 *
Bram Moolenaare4efc3b2005-03-07 23:16:51 +00008381 * The only reused thing is gui_beval.h and get_beval_info()
Bram Moolenaar071d4272004-06-13 20:20:40 +00008382 * from gui_beval.c (note it uses x and y of the BalloonEval struct
8383 * to get current mouse position).
8384 *
8385 * Trying to use as more Windows services as possible, and as less
8386 * IE version as possible :)).
8387 *
8388 * 1) Don't create ToolTip in gui_mch_create_beval_area, only initialize
8389 * BalloonEval struct.
8390 * 2) Enable/Disable simply create/kill BalloonEval Timer
8391 * 3) When there was enough inactivity, timer procedure posts
8392 * async request to debugger
8393 * 4) gui_mch_post_balloon (invoked from netbeans.c) creates tooltip control
8394 * and performs some actions to show it ASAP
Bram Moolenaar446cb832008-06-24 21:56:24 +00008395 * 5) WM_NOTIFY:TTN_POP destroys created tooltip
Bram Moolenaar071d4272004-06-13 20:20:40 +00008396 */
8397
Bram Moolenaar45360022005-07-21 21:08:21 +00008398/*
8399 * determine whether installed Common Controls support multiline tooltips
8400 * (i.e. their version is >= 4.70
8401 */
8402 int
8403multiline_balloon_available(void)
8404{
8405 HINSTANCE hDll;
8406 static char comctl_dll[] = "comctl32.dll";
8407 static int multiline_tip = MAYBE;
8408
8409 if (multiline_tip != MAYBE)
8410 return multiline_tip;
8411
8412 hDll = GetModuleHandle(comctl_dll);
8413 if (hDll != NULL)
8414 {
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00008415 DLLGETVERSIONPROC pGetVer;
8416 pGetVer = (DLLGETVERSIONPROC)GetProcAddress(hDll, "DllGetVersion");
Bram Moolenaar45360022005-07-21 21:08:21 +00008417
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00008418 if (pGetVer != NULL)
8419 {
8420 DLLVERSIONINFO dvi;
8421 HRESULT hr;
Bram Moolenaar45360022005-07-21 21:08:21 +00008422
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00008423 ZeroMemory(&dvi, sizeof(dvi));
8424 dvi.cbSize = sizeof(dvi);
Bram Moolenaar45360022005-07-21 21:08:21 +00008425
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00008426 hr = (*pGetVer)(&dvi);
Bram Moolenaar45360022005-07-21 21:08:21 +00008427
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00008428 if (SUCCEEDED(hr)
Bram Moolenaar45360022005-07-21 21:08:21 +00008429 && (dvi.dwMajorVersion > 4
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00008430 || (dvi.dwMajorVersion == 4
8431 && dvi.dwMinorVersion >= 70)))
Bram Moolenaar45360022005-07-21 21:08:21 +00008432 {
8433 multiline_tip = TRUE;
8434 return multiline_tip;
8435 }
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00008436 }
Bram Moolenaar45360022005-07-21 21:08:21 +00008437 else
8438 {
8439 /* there is chance we have ancient CommCtl 4.70
8440 which doesn't export DllGetVersion */
8441 DWORD dwHandle = 0;
8442 DWORD len = GetFileVersionInfoSize(comctl_dll, &dwHandle);
8443 if (len > 0)
8444 {
8445 VS_FIXEDFILEINFO *ver;
8446 UINT vlen = 0;
8447 void *data = alloc(len);
8448
Bram Moolenaar418f81b2016-02-16 20:12:02 +01008449 if ((data != NULL
Bram Moolenaar45360022005-07-21 21:08:21 +00008450 && GetFileVersionInfo(comctl_dll, 0, len, data)
8451 && VerQueryValue(data, "\\", (void **)&ver, &vlen)
8452 && vlen
Bram Moolenaar418f81b2016-02-16 20:12:02 +01008453 && HIWORD(ver->dwFileVersionMS) > 4)
8454 || ((HIWORD(ver->dwFileVersionMS) == 4
8455 && LOWORD(ver->dwFileVersionMS) >= 70)))
Bram Moolenaar45360022005-07-21 21:08:21 +00008456 {
8457 vim_free(data);
8458 multiline_tip = TRUE;
8459 return multiline_tip;
8460 }
8461 vim_free(data);
8462 }
8463 }
8464 }
8465 multiline_tip = FALSE;
8466 return multiline_tip;
8467}
8468
Bram Moolenaar071d4272004-06-13 20:20:40 +00008469 static void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01008470make_tooltip(BalloonEval *beval, char *text, POINT pt)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008471{
Bram Moolenaar45360022005-07-21 21:08:21 +00008472 TOOLINFO *pti;
8473 int ToolInfoSize;
8474
8475 if (multiline_balloon_available() == TRUE)
8476 ToolInfoSize = sizeof(TOOLINFO_NEW);
8477 else
8478 ToolInfoSize = sizeof(TOOLINFO);
8479
8480 pti = (TOOLINFO *)alloc(ToolInfoSize);
8481 if (pti == NULL)
8482 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008483
8484 beval->balloon = CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS,
8485 NULL, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,
8486 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
8487 beval->target, NULL, s_hinst, NULL);
8488
8489 SetWindowPos(beval->balloon, HWND_TOPMOST, 0, 0, 0, 0,
8490 SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
8491
Bram Moolenaar45360022005-07-21 21:08:21 +00008492 pti->cbSize = ToolInfoSize;
8493 pti->uFlags = TTF_SUBCLASS;
8494 pti->hwnd = beval->target;
8495 pti->hinst = 0; /* Don't use string resources */
8496 pti->uId = ID_BEVAL_TOOLTIP;
8497
8498 if (multiline_balloon_available() == TRUE)
8499 {
8500 RECT rect;
8501 TOOLINFO_NEW *ptin = (TOOLINFO_NEW *)pti;
8502 pti->lpszText = LPSTR_TEXTCALLBACK;
8503 ptin->lParam = (LPARAM)text;
8504 if (GetClientRect(s_textArea, &rect)) /* switch multiline tooltips on */
8505 SendMessage(beval->balloon, TTM_SETMAXTIPWIDTH, 0,
8506 (LPARAM)rect.right);
8507 }
8508 else
8509 pti->lpszText = text; /* do this old way */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008510
8511 /* Limit ballooneval bounding rect to CursorPos neighbourhood */
Bram Moolenaar45360022005-07-21 21:08:21 +00008512 pti->rect.left = pt.x - 3;
8513 pti->rect.top = pt.y - 3;
8514 pti->rect.right = pt.x + 3;
8515 pti->rect.bottom = pt.y + 3;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008516
Bram Moolenaar45360022005-07-21 21:08:21 +00008517 SendMessage(beval->balloon, TTM_ADDTOOL, 0, (LPARAM)pti);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008518 /* Make tooltip appear sooner */
8519 SendMessage(beval->balloon, TTM_SETDELAYTIME, TTDT_INITIAL, 10);
Bram Moolenaarb52e5322008-01-05 12:15:52 +00008520 /* I've performed some tests and it seems the longest possible life time
8521 * of tooltip is 30 seconds */
8522 SendMessage(beval->balloon, TTM_SETDELAYTIME, TTDT_AUTOPOP, 30000);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008523 /*
8524 * HACK: force tooltip to appear, because it'll not appear until
8525 * first mouse move. D*mn M$
Bram Moolenaarb52e5322008-01-05 12:15:52 +00008526 * Amazingly moving (2, 2) and then (-1, -1) the mouse doesn't move.
Bram Moolenaar071d4272004-06-13 20:20:40 +00008527 */
Bram Moolenaarb52e5322008-01-05 12:15:52 +00008528 mouse_event(MOUSEEVENTF_MOVE, 2, 2, 0, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008529 mouse_event(MOUSEEVENTF_MOVE, (DWORD)-1, (DWORD)-1, 0, 0);
Bram Moolenaar45360022005-07-21 21:08:21 +00008530 vim_free(pti);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008531}
8532
8533 static void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01008534delete_tooltip(BalloonEval *beval)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008535{
Bram Moolenaar8e5f5b42015-08-26 23:12:38 +02008536 PostMessage(beval->balloon, WM_CLOSE, 0, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008537}
8538
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +00008539/*ARGSUSED*/
Bram Moolenaar071d4272004-06-13 20:20:40 +00008540 static VOID CALLBACK
Bram Moolenaar68c2f632016-01-30 17:24:07 +01008541BevalTimerProc(
8542 HWND hwnd,
8543 UINT uMsg,
8544 UINT_PTR idEvent,
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
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +00008581/*ARGSUSED*/
Bram Moolenaar071d4272004-06-13 20:20:40 +00008582 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01008583gui_mch_disable_beval_area(BalloonEval *beval)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008584{
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008585 // TRACE0("gui_mch_disable_beval_area {{{");
Bram Moolenaar071d4272004-06-13 20:20:40 +00008586 KillTimer(s_textArea, BevalTimerId);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008587 // TRACE0("gui_mch_disable_beval_area }}}");
Bram Moolenaar071d4272004-06-13 20:20:40 +00008588}
8589
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +00008590/*ARGSUSED*/
Bram Moolenaar071d4272004-06-13 20:20:40 +00008591 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01008592gui_mch_enable_beval_area(BalloonEval *beval)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008593{
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008594 // TRACE0("gui_mch_enable_beval_area |||");
Bram Moolenaar071d4272004-06-13 20:20:40 +00008595 if (beval == NULL)
8596 return;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008597 // TRACE0("gui_mch_enable_beval_area {{{");
Bram Moolenaar167632f2010-05-26 21:42:54 +02008598 BevalTimerId = SetTimer(s_textArea, 0, (UINT)(p_bdlay / 2), BevalTimerProc);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008599 // TRACE0("gui_mch_enable_beval_area }}}");
Bram Moolenaar071d4272004-06-13 20:20:40 +00008600}
8601
8602 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01008603gui_mch_post_balloon(BalloonEval *beval, char_u *mesg)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008604{
8605 POINT pt;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008606 // TRACE0("gui_mch_post_balloon {{{");
Bram Moolenaar071d4272004-06-13 20:20:40 +00008607 if (beval->showState == ShS_SHOWING)
8608 return;
8609 GetCursorPos(&pt);
8610 ScreenToClient(s_textArea, &pt);
8611
8612 if (abs(beval->x - pt.x) < 3 && abs(beval->y - pt.y) < 3)
8613 /* cursor is still here */
8614 {
8615 gui_mch_disable_beval_area(cur_beval);
8616 beval->showState = ShS_SHOWING;
Bram Moolenaar418f81b2016-02-16 20:12:02 +01008617 make_tooltip(beval, (char *)mesg, pt);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008618 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008619 // TRACE0("gui_mch_post_balloon }}}");
Bram Moolenaar071d4272004-06-13 20:20:40 +00008620}
8621
Bram Moolenaard857f0e2005-06-21 22:37:39 +00008622/*ARGSUSED*/
Bram Moolenaar071d4272004-06-13 20:20:40 +00008623 BalloonEval *
Bram Moolenaar68c2f632016-01-30 17:24:07 +01008624gui_mch_create_beval_area(
8625 void *target, /* ignored, always use s_textArea */
8626 char_u *mesg,
8627 void (*mesgCB)(BalloonEval *, int),
8628 void *clientData)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008629{
8630 /* partially stolen from gui_beval.c */
8631 BalloonEval *beval;
8632
8633 if (mesg != NULL && mesgCB != NULL)
8634 {
Bram Moolenaar95f09602016-11-10 20:01:45 +01008635 IEMSG(_("E232: Cannot create BalloonEval with both message and callback"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00008636 return NULL;
8637 }
8638
8639 beval = (BalloonEval *)alloc(sizeof(BalloonEval));
8640 if (beval != NULL)
8641 {
8642 beval->target = s_textArea;
8643 beval->balloon = NULL;
8644
8645 beval->showState = ShS_NEUTRAL;
8646 beval->x = 0;
8647 beval->y = 0;
8648 beval->msg = mesg;
8649 beval->msgCB = mesgCB;
8650 beval->clientData = clientData;
8651
8652 InitCommonControls();
Bram Moolenaar071d4272004-06-13 20:20:40 +00008653 cur_beval = beval;
8654
8655 if (p_beval)
8656 gui_mch_enable_beval_area(beval);
8657
8658 }
8659 return beval;
8660}
8661
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +00008662/*ARGSUSED*/
Bram Moolenaar071d4272004-06-13 20:20:40 +00008663 static void
Bram Moolenaar442b4222010-05-24 21:34:22 +02008664Handle_WM_Notify(HWND hwnd, LPNMHDR pnmh)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008665{
8666 if (pnmh->idFrom != ID_BEVAL_TOOLTIP) /* it is not our tooltip */
8667 return;
8668
8669 if (cur_beval != NULL)
8670 {
Bram Moolenaar45360022005-07-21 21:08:21 +00008671 switch (pnmh->code)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008672 {
Bram Moolenaar45360022005-07-21 21:08:21 +00008673 case TTN_SHOW:
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008674 // TRACE0("TTN_SHOW {{{");
8675 // TRACE0("TTN_SHOW }}}");
Bram Moolenaar45360022005-07-21 21:08:21 +00008676 break;
8677 case TTN_POP: /* Before tooltip disappear */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008678 // TRACE0("TTN_POP {{{");
Bram Moolenaar071d4272004-06-13 20:20:40 +00008679 delete_tooltip(cur_beval);
8680 gui_mch_enable_beval_area(cur_beval);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008681 // TRACE0("TTN_POP }}}");
Bram Moolenaar071d4272004-06-13 20:20:40 +00008682
8683 cur_beval->showState = ShS_NEUTRAL;
Bram Moolenaar45360022005-07-21 21:08:21 +00008684 break;
8685 case TTN_GETDISPINFO:
Bram Moolenaar6c9176d2008-01-03 19:45:15 +00008686 {
8687 /* if you get there then we have new common controls */
8688 NMTTDISPINFO_NEW *info = (NMTTDISPINFO_NEW *)pnmh;
8689 info->lpszText = (LPSTR)info->lParam;
8690 info->uFlags |= TTF_DI_SETITEM;
8691 }
Bram Moolenaar45360022005-07-21 21:08:21 +00008692 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008693 }
8694 }
8695}
8696
8697 static void
8698TrackUserActivity(UINT uMsg)
8699{
8700 if ((uMsg >= WM_MOUSEFIRST && uMsg <= WM_MOUSELAST)
8701 || (uMsg >= WM_KEYFIRST && uMsg <= WM_KEYLAST))
8702 LastActivity = GetTickCount();
8703}
8704
8705 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01008706gui_mch_destroy_beval_area(BalloonEval *beval)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008707{
8708 vim_free(beval);
8709}
8710#endif /* FEAT_BEVAL */
8711
8712#if defined(FEAT_NETBEANS_INTG) || defined(PROTO)
8713/*
8714 * We have multiple signs to draw at the same location. Draw the
8715 * multi-sign indicator (down-arrow) instead. This is the Win32 version.
8716 */
8717 void
8718netbeans_draw_multisign_indicator(int row)
8719{
8720 int i;
8721 int y;
8722 int x;
8723
Bram Moolenaarb26e6322010-05-22 21:34:09 +02008724 if (!netbeans_active())
Bram Moolenaarcc448b32010-07-14 16:52:17 +02008725 return;
Bram Moolenaarb26e6322010-05-22 21:34:09 +02008726
Bram Moolenaar071d4272004-06-13 20:20:40 +00008727 x = 0;
8728 y = TEXT_Y(row);
8729
8730 for (i = 0; i < gui.char_height - 3; i++)
8731 SetPixel(s_hdc, x+2, y++, gui.currFgColor);
8732
8733 SetPixel(s_hdc, x+0, y, gui.currFgColor);
8734 SetPixel(s_hdc, x+2, y, gui.currFgColor);
8735 SetPixel(s_hdc, x+4, y++, gui.currFgColor);
8736 SetPixel(s_hdc, x+1, y, gui.currFgColor);
8737 SetPixel(s_hdc, x+2, y, gui.currFgColor);
8738 SetPixel(s_hdc, x+3, y++, gui.currFgColor);
8739 SetPixel(s_hdc, x+2, y, gui.currFgColor);
8740}
Bram Moolenaare0874f82016-01-24 20:36:41 +01008741#endif