blob: 3c3d9949bf76f80f8fa18976ffaea24b41b20d11 [file] [log] [blame]
Bram Moolenaar071d4272004-06-13 20:20:40 +00001/* vi:set ts=8 sts=4 sw=4:
2 *
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 *
13 * GUI support for Microsoft Windows. Win32 initially; maybe Win16 later
14 *
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
20 * os_win32.c. (It can also be done from the terminal version).
21 *
22 * TODO: Some of the function signatures ought to be updated for Win64;
23 * e.g., replace LONG with LONG_PTR, etc.
24 */
25
26/*
27 * These are new in Windows ME/XP, only defined in recent compilers.
28 */
29#ifndef HANDLE_WM_XBUTTONUP
30# define HANDLE_WM_XBUTTONUP(hwnd, wParam, lParam, fn) \
31 ((fn)((hwnd), (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
32#endif
33#ifndef HANDLE_WM_XBUTTONDOWN
34# define HANDLE_WM_XBUTTONDOWN(hwnd, wParam, lParam, fn) \
35 ((fn)((hwnd), FALSE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
36#endif
37#ifndef HANDLE_WM_XBUTTONDBLCLK
38# define HANDLE_WM_XBUTTONDBLCLK(hwnd, wParam, lParam, fn) \
39 ((fn)((hwnd), TRUE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
40#endif
41
42/*
43 * Include the common stuff for MS-Windows GUI.
44 */
45#include "gui_w48.c"
46
47#ifdef FEAT_XPM_W32
48# include "xpm_w32.h"
49#endif
50
51#ifdef PROTO
52# define WINAPI
53#endif
54
55#ifdef __MINGW32__
56/*
57 * Add a lot of missing defines.
58 * They are not always missing, we need the #ifndef's.
59 */
60# ifndef _cdecl
61# define _cdecl
62# endif
63# ifndef IsMinimized
64# define IsMinimized(hwnd) IsIconic(hwnd)
65# endif
66# ifndef IsMaximized
67# define IsMaximized(hwnd) IsZoomed(hwnd)
68# endif
69# ifndef SelectFont
70# define SelectFont(hdc, hfont) ((HFONT)SelectObject((hdc), (HGDIOBJ)(HFONT)(hfont)))
71# endif
72# ifndef GetStockBrush
73# define GetStockBrush(i) ((HBRUSH)GetStockObject(i))
74# endif
75# ifndef DeleteBrush
76# define DeleteBrush(hbr) DeleteObject((HGDIOBJ)(HBRUSH)(hbr))
77# endif
78
79# ifndef HANDLE_WM_RBUTTONDBLCLK
80# define HANDLE_WM_RBUTTONDBLCLK(hwnd, wParam, lParam, fn) \
81 ((fn)((hwnd), TRUE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
82# endif
83# ifndef HANDLE_WM_MBUTTONUP
84# define HANDLE_WM_MBUTTONUP(hwnd, wParam, lParam, fn) \
85 ((fn)((hwnd), (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
86# endif
87# ifndef HANDLE_WM_MBUTTONDBLCLK
88# define HANDLE_WM_MBUTTONDBLCLK(hwnd, wParam, lParam, fn) \
89 ((fn)((hwnd), TRUE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
90# endif
91# ifndef HANDLE_WM_LBUTTONDBLCLK
92# define HANDLE_WM_LBUTTONDBLCLK(hwnd, wParam, lParam, fn) \
93 ((fn)((hwnd), TRUE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
94# endif
95# ifndef HANDLE_WM_RBUTTONDOWN
96# define HANDLE_WM_RBUTTONDOWN(hwnd, wParam, lParam, fn) \
97 ((fn)((hwnd), FALSE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
98# endif
99# ifndef HANDLE_WM_MOUSEMOVE
100# define HANDLE_WM_MOUSEMOVE(hwnd, wParam, lParam, fn) \
101 ((fn)((hwnd), (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
102# endif
103# ifndef HANDLE_WM_RBUTTONUP
104# define HANDLE_WM_RBUTTONUP(hwnd, wParam, lParam, fn) \
105 ((fn)((hwnd), (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
106# endif
107# ifndef HANDLE_WM_MBUTTONDOWN
108# define HANDLE_WM_MBUTTONDOWN(hwnd, wParam, lParam, fn) \
109 ((fn)((hwnd), FALSE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
110# endif
111# ifndef HANDLE_WM_LBUTTONUP
112# define HANDLE_WM_LBUTTONUP(hwnd, wParam, lParam, fn) \
113 ((fn)((hwnd), (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
114# endif
115# ifndef HANDLE_WM_LBUTTONDOWN
116# define HANDLE_WM_LBUTTONDOWN(hwnd, wParam, lParam, fn) \
117 ((fn)((hwnd), FALSE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
118# endif
119# ifndef HANDLE_WM_SYSCHAR
120# define HANDLE_WM_SYSCHAR(hwnd, wParam, lParam, fn) \
121 ((fn)((hwnd), (TCHAR)(wParam), (int)(short)LOWORD(lParam)), 0L)
122# endif
123# ifndef HANDLE_WM_ACTIVATEAPP
124# define HANDLE_WM_ACTIVATEAPP(hwnd, wParam, lParam, fn) \
125 ((fn)((hwnd), (BOOL)(wParam), (DWORD)(lParam)), 0L)
126# endif
127# ifndef HANDLE_WM_WINDOWPOSCHANGING
128# define HANDLE_WM_WINDOWPOSCHANGING(hwnd, wParam, lParam, fn) \
129 (LRESULT)(DWORD)(BOOL)(fn)((hwnd), (LPWINDOWPOS)(lParam))
130# endif
131# ifndef HANDLE_WM_VSCROLL
132# define HANDLE_WM_VSCROLL(hwnd, wParam, lParam, fn) \
133 ((fn)((hwnd), (HWND)(lParam), (UINT)(LOWORD(wParam)), (int)(short)HIWORD(wParam)), 0L)
134# endif
135# ifndef HANDLE_WM_SETFOCUS
136# define HANDLE_WM_SETFOCUS(hwnd, wParam, lParam, fn) \
137 ((fn)((hwnd), (HWND)(wParam)), 0L)
138# endif
139# ifndef HANDLE_WM_KILLFOCUS
140# define HANDLE_WM_KILLFOCUS(hwnd, wParam, lParam, fn) \
141 ((fn)((hwnd), (HWND)(wParam)), 0L)
142# endif
143# ifndef HANDLE_WM_HSCROLL
144# define HANDLE_WM_HSCROLL(hwnd, wParam, lParam, fn) \
145 ((fn)((hwnd), (HWND)(lParam), (UINT)(LOWORD(wParam)), (int)(short)HIWORD(wParam)), 0L)
146# endif
147# ifndef HANDLE_WM_DROPFILES
148# define HANDLE_WM_DROPFILES(hwnd, wParam, lParam, fn) \
149 ((fn)((hwnd), (HDROP)(wParam)), 0L)
150# endif
151# ifndef HANDLE_WM_CHAR
152# define HANDLE_WM_CHAR(hwnd, wParam, lParam, fn) \
153 ((fn)((hwnd), (TCHAR)(wParam), (int)(short)LOWORD(lParam)), 0L)
154# endif
155# ifndef HANDLE_WM_SYSDEADCHAR
156# define HANDLE_WM_SYSDEADCHAR(hwnd, wParam, lParam, fn) \
157 ((fn)((hwnd), (TCHAR)(wParam), (int)(short)LOWORD(lParam)), 0L)
158# endif
159# ifndef HANDLE_WM_DEADCHAR
160# define HANDLE_WM_DEADCHAR(hwnd, wParam, lParam, fn) \
161 ((fn)((hwnd), (TCHAR)(wParam), (int)(short)LOWORD(lParam)), 0L)
162# endif
163#endif /* __MINGW32__ */
164
165
166/* Some parameters for tearoff menus. All in pixels. */
167#define TEAROFF_PADDING_X 2
168#define TEAROFF_BUTTON_PAD_X 8
169#define TEAROFF_MIN_WIDTH 200
170#define TEAROFF_SUBMENU_LABEL ">>"
171#define TEAROFF_COLUMN_PADDING 3 // # spaces to pad column with.
172
173
174/* For the Intellimouse: */
175#ifndef WM_MOUSEWHEEL
176#define WM_MOUSEWHEEL 0x20a
177#endif
178
179
180#ifdef FEAT_BEVAL
181# define ID_BEVAL_TOOLTIP 200
182# define BEVAL_TEXT_LEN MAXPATHL
183
184static void make_tooltip __ARGS((BalloonEval *beval, char *text, POINT pt));
185static void delete_tooltip __ARGS((BalloonEval *beval));
186static VOID CALLBACK BevalTimerProc __ARGS((HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime));
187
188static BalloonEval *cur_beval = NULL;
189static UINT BevalTimerId = 0;
190static DWORD LastActivity = 0;
Bram Moolenaar45360022005-07-21 21:08:21 +0000191
192/*
193 * excerpts from headers since this may not be presented
194 * in the extremelly old compilers
195 */
196#include <pshpack1.h>
197
198typedef struct _DllVersionInfo
199{
200 DWORD cbSize;
201 DWORD dwMajorVersion;
202 DWORD dwMinorVersion;
203 DWORD dwBuildNumber;
204 DWORD dwPlatformID;
205} DLLVERSIONINFO;
206
207typedef struct tagTOOLINFOA_NEW
208{
209 UINT cbSize;
210 UINT uFlags;
211 HWND hwnd;
212 UINT uId;
213 RECT rect;
214 HINSTANCE hinst;
215 LPSTR lpszText;
216 LPARAM lParam;
217} TOOLINFO_NEW;
218
219typedef struct tagNMTTDISPINFO_NEW
220{
221 NMHDR hdr;
222 LPTSTR lpszText;
223 char szText[80];
224 HINSTANCE hinst;
225 UINT uFlags;
226 LPARAM lParam;
227} NMTTDISPINFO_NEW;
228
229#include <poppack.h>
230
231typedef HRESULT (WINAPI* DLLGETVERSIONPROC)(DLLVERSIONINFO *);
232#ifndef TTM_SETMAXTIPWIDTH
233# define TTM_SETMAXTIPWIDTH (WM_USER+24)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000234#endif
235
Bram Moolenaar45360022005-07-21 21:08:21 +0000236#ifndef TTF_DI_SETITEM
237# define TTF_DI_SETITEM 0x8000
238#endif
239
240#ifndef TTN_GETDISPINFO
241# define TTN_GETDISPINFO (TTN_FIRST - 0)
242#endif
243
244#endif /* defined(FEAT_BEVAL) */
245
Bram Moolenaar071d4272004-06-13 20:20:40 +0000246/* Local variables: */
247
248#ifdef FEAT_MENU
249static UINT s_menu_id = 100;
250
251/*
252 * Use the system font for dialogs and tear-off menus. Remove this line to
253 * use DLG_FONT_NAME.
254 */
255# define USE_SYSMENU_FONT
256#endif
257
258#define VIM_NAME "vim"
259#define VIM_CLASS "Vim"
260#define VIM_CLASSW L"Vim"
261
262/* Initial size for the dialog template. For gui_mch_dialog() it's fixed,
263 * thus there should be room for every dialog. For tearoffs it's made bigger
264 * when needed. */
265#define DLG_ALLOC_SIZE 16 * 1024
266
267/*
268 * stuff for dialogs, menus, tearoffs etc.
269 */
270static LRESULT APIENTRY dialog_callback(HWND, UINT, WPARAM, LPARAM);
271static LRESULT APIENTRY tearoff_callback(HWND, UINT, WPARAM, LPARAM);
272static PWORD
273add_dialog_element(
274 PWORD p,
275 DWORD lStyle,
276 WORD x,
277 WORD y,
278 WORD w,
279 WORD h,
280 WORD Id,
281 WORD clss,
282 const char *caption);
283static LPWORD lpwAlign(LPWORD);
284static int nCopyAnsiToWideChar(LPWORD, LPSTR);
285static void gui_mch_tearoff(char_u *title, vimmenu_T *menu, int initX, int initY);
286static void get_dialog_font_metrics(void);
287
288static int dialog_default_button = -1;
289
290/* Intellimouse support */
291static int mouse_scroll_lines = 0;
292static UINT msh_msgmousewheel = 0;
293
294static int s_usenewlook; /* emulate W95/NT4 non-bold dialogs */
295#ifdef FEAT_TOOLBAR
296static void initialise_toolbar(void);
297static int get_toolbar_bitmap(vimmenu_T *menu);
298#endif
299
300#ifdef FEAT_MBYTE_IME
301static LRESULT _OnImeComposition(HWND hwnd, WPARAM dbcs, LPARAM param);
302static char_u *GetResultStr(HWND hwnd, int GCS, int *lenp);
303#endif
304#if defined(FEAT_MBYTE_IME) && defined(DYNAMIC_IME)
305# ifdef NOIME
306typedef struct tagCOMPOSITIONFORM {
307 DWORD dwStyle;
308 POINT ptCurrentPos;
309 RECT rcArea;
310} COMPOSITIONFORM, *PCOMPOSITIONFORM, NEAR *NPCOMPOSITIONFORM, FAR *LPCOMPOSITIONFORM;
311typedef HANDLE HIMC;
312# endif
313
Bram Moolenaard857f0e2005-06-21 22:37:39 +0000314static HINSTANCE hLibImm = NULL;
315static LONG (WINAPI *pImmGetCompositionStringA)(HIMC, DWORD, LPVOID, DWORD);
316static LONG (WINAPI *pImmGetCompositionStringW)(HIMC, DWORD, LPVOID, DWORD);
317static HIMC (WINAPI *pImmGetContext)(HWND);
318static HIMC (WINAPI *pImmAssociateContext)(HWND, HIMC);
319static BOOL (WINAPI *pImmReleaseContext)(HWND, HIMC);
320static BOOL (WINAPI *pImmGetOpenStatus)(HIMC);
321static BOOL (WINAPI *pImmSetOpenStatus)(HIMC, BOOL);
322static BOOL (WINAPI *pImmGetCompositionFont)(HIMC, LPLOGFONTA);
323static BOOL (WINAPI *pImmSetCompositionFont)(HIMC, LPLOGFONTA);
324static BOOL (WINAPI *pImmSetCompositionWindow)(HIMC, LPCOMPOSITIONFORM);
325static BOOL (WINAPI *pImmGetConversionStatus)(HIMC, LPDWORD, LPDWORD);
Bram Moolenaarca003e12006-03-17 23:19:38 +0000326static BOOL (WINAPI *pImmSetConversionStatus)(HIMC, DWORD, DWORD);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000327static void dyn_imm_load(void);
328#else
329# define pImmGetCompositionStringA ImmGetCompositionStringA
330# define pImmGetCompositionStringW ImmGetCompositionStringW
331# define pImmGetContext ImmGetContext
332# define pImmAssociateContext ImmAssociateContext
333# define pImmReleaseContext ImmReleaseContext
334# define pImmGetOpenStatus ImmGetOpenStatus
335# define pImmSetOpenStatus ImmSetOpenStatus
336# define pImmGetCompositionFont ImmGetCompositionFontA
337# define pImmSetCompositionFont ImmSetCompositionFontA
338# define pImmSetCompositionWindow ImmSetCompositionWindow
339# define pImmGetConversionStatus ImmGetConversionStatus
Bram Moolenaarca003e12006-03-17 23:19:38 +0000340# define pImmSetConversionStatus ImmSetConversionStatus
Bram Moolenaar071d4272004-06-13 20:20:40 +0000341#endif
342
343#ifndef ETO_IGNORELANGUAGE
344# define ETO_IGNORELANGUAGE 0x1000
345#endif
346
347/* multi monitor support */
348typedef struct _MONITORINFOstruct
349{
350 DWORD cbSize;
351 RECT rcMonitor;
352 RECT rcWork;
353 DWORD dwFlags;
354} _MONITORINFO;
355
356typedef HANDLE _HMONITOR;
357typedef _HMONITOR (WINAPI *TMonitorFromWindow)(HWND, DWORD);
358typedef BOOL (WINAPI *TGetMonitorInfo)(_HMONITOR, _MONITORINFO *);
359
360static TMonitorFromWindow pMonitorFromWindow = NULL;
361static TGetMonitorInfo pGetMonitorInfo = NULL;
362static HANDLE user32_lib = NULL;
363#ifdef FEAT_NETBEANS_INTG
364int WSInitialized = FALSE; /* WinSock is initialized */
365#endif
366/*
367 * Return TRUE when running under Windows NT 3.x or Win32s, both of which have
368 * less fancy GUI APIs.
369 */
370 static int
371is_winnt_3(void)
372{
373 return ((os_version.dwPlatformId == VER_PLATFORM_WIN32_NT
374 && os_version.dwMajorVersion == 3)
375 || (os_version.dwPlatformId == VER_PLATFORM_WIN32s));
376}
377
378/*
379 * Return TRUE when running under Win32s.
380 */
381 int
382gui_is_win32s(void)
383{
384 return (os_version.dwPlatformId == VER_PLATFORM_WIN32s);
385}
386
387#ifdef FEAT_MENU
388/*
389 * Figure out how high the menu bar is at the moment.
390 */
391 static int
392gui_mswin_get_menu_height(
393 int fix_window) /* If TRUE, resize window if menu height changed */
394{
395 static int old_menu_height = -1;
396
397 RECT rc1, rc2;
398 int num;
399 int menu_height;
400
401 if (gui.menu_is_active)
402 num = GetMenuItemCount(s_menuBar);
403 else
404 num = 0;
405
406 if (num == 0)
407 menu_height = 0;
408 else
409 {
410 if (is_winnt_3()) /* for NT 3.xx */
411 {
412 if (gui.starting)
413 menu_height = GetSystemMetrics(SM_CYMENU);
414 else
415 {
416 RECT r1, r2;
417 int frameht = GetSystemMetrics(SM_CYFRAME);
418 int capht = GetSystemMetrics(SM_CYCAPTION);
419
420 /* get window rect of s_hwnd
421 * get client rect of s_hwnd
422 * get cap height
423 * subtract from window rect, the sum of client height,
424 * (if not maximized)frame thickness, and caption height.
425 */
426 GetWindowRect(s_hwnd, &r1);
427 GetClientRect(s_hwnd, &r2);
428 menu_height = r1.bottom - r1.top - (r2.bottom - r2.top
429 + 2 * frameht * (!IsZoomed(s_hwnd)) + capht);
430 }
431 }
432 else /* win95 and variants (NT 4.0, I guess) */
433 {
434 /*
435 * In case 'lines' is set in _vimrc/_gvimrc window width doesn't
436 * seem to have been set yet, so menu wraps in default window
437 * width which is very narrow. Instead just return height of a
438 * single menu item. Will still be wrong when the menu really
439 * should wrap over more than one line.
440 */
441 GetMenuItemRect(s_hwnd, s_menuBar, 0, &rc1);
442 if (gui.starting)
443 menu_height = rc1.bottom - rc1.top + 1;
444 else
445 {
446 GetMenuItemRect(s_hwnd, s_menuBar, num - 1, &rc2);
447 menu_height = rc2.bottom - rc1.top + 1;
448 }
449 }
450 }
451
452 if (fix_window && menu_height != old_menu_height)
453 {
454 old_menu_height = menu_height;
455 gui_set_shellsize(FALSE, FALSE);
456 }
457
458 return menu_height;
459}
460#endif /*FEAT_MENU*/
461
462
463/*
464 * Setup for the Intellimouse
465 */
466 static void
467init_mouse_wheel(void)
468{
469
470#ifndef SPI_GETWHEELSCROLLLINES
471# define SPI_GETWHEELSCROLLLINES 104
472#endif
Bram Moolenaare7566042005-06-17 22:00:15 +0000473#ifndef SPI_SETWHEELSCROLLLINES
474# define SPI_SETWHEELSCROLLLINES 105
475#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000476
477#define VMOUSEZ_CLASSNAME "MouseZ" /* hidden wheel window class */
478#define VMOUSEZ_TITLE "Magellan MSWHEEL" /* hidden wheel window title */
479#define VMSH_MOUSEWHEEL "MSWHEEL_ROLLMSG"
480#define VMSH_SCROLL_LINES "MSH_SCROLL_LINES_MSG"
481
482 HWND hdl_mswheel;
483 UINT msh_msgscrolllines;
484
485 msh_msgmousewheel = 0;
486 mouse_scroll_lines = 3; /* reasonable default */
487
488 if ((os_version.dwPlatformId == VER_PLATFORM_WIN32_NT
489 && os_version.dwMajorVersion >= 4)
490 || (os_version.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS
491 && ((os_version.dwMajorVersion == 4
492 && os_version.dwMinorVersion >= 10)
493 || os_version.dwMajorVersion >= 5)))
494 {
495 /* if NT 4.0+ (or Win98) get scroll lines directly from system */
496 SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0,
497 &mouse_scroll_lines, 0);
498 }
499 else if (os_version.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS
500 || (os_version.dwPlatformId == VER_PLATFORM_WIN32_NT
501 && os_version.dwMajorVersion < 4))
502 { /*
503 * If Win95 or NT 3.51,
504 * try to find the hidden point32 window.
505 */
506 hdl_mswheel = FindWindow(VMOUSEZ_CLASSNAME, VMOUSEZ_TITLE);
507 if (hdl_mswheel)
508 {
509 msh_msgscrolllines = RegisterWindowMessage(VMSH_SCROLL_LINES);
510 if (msh_msgscrolllines)
511 {
512 mouse_scroll_lines = (int)SendMessage(hdl_mswheel,
513 msh_msgscrolllines, 0, 0);
514 msh_msgmousewheel = RegisterWindowMessage(VMSH_MOUSEWHEEL);
515 }
516 }
517 }
518}
519
520
521/* Intellimouse wheel handler */
522 static void
523_OnMouseWheel(
524 HWND hwnd,
525 short zDelta)
526{
527/* Treat a mouse wheel event as if it were a scroll request */
528 int i;
529 int size;
530 HWND hwndCtl;
531
532 if (curwin->w_scrollbars[SBAR_RIGHT].id != 0)
533 {
534 hwndCtl = curwin->w_scrollbars[SBAR_RIGHT].id;
535 size = curwin->w_scrollbars[SBAR_RIGHT].size;
536 }
537 else if (curwin->w_scrollbars[SBAR_LEFT].id != 0)
538 {
539 hwndCtl = curwin->w_scrollbars[SBAR_LEFT].id;
540 size = curwin->w_scrollbars[SBAR_LEFT].size;
541 }
542 else
543 return;
544
545 size = curwin->w_height;
546 if (mouse_scroll_lines == 0)
547 init_mouse_wheel();
548
549 if (mouse_scroll_lines > 0
550 && mouse_scroll_lines < (size > 2 ? size - 2 : 1))
551 {
552 for (i = mouse_scroll_lines; i > 0; --i)
553 _OnScroll(hwnd, hwndCtl, zDelta >= 0 ? SB_LINEUP : SB_LINEDOWN, 0);
554 }
555 else
556 _OnScroll(hwnd, hwndCtl, zDelta >= 0 ? SB_PAGEUP : SB_PAGEDOWN, 0);
557}
558
Bram Moolenaar520470a2005-06-16 21:59:56 +0000559/*
560 * Invoked when a setting was changed.
561 */
562 static LRESULT CALLBACK
563_OnSettingChange(UINT n)
564{
565 if (n == SPI_SETWHEELSCROLLLINES)
566 SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0,
567 &mouse_scroll_lines, 0);
568 return 0;
569}
570
Bram Moolenaar071d4272004-06-13 20:20:40 +0000571#if 0 /* disabled, a gap appears below and beside the window, and the window
572 can be moved (in a strange way) */
573/*
574 * Even though we have _DuringSizing() which makes the rubber band a valid
575 * size, we need this for when the user maximises the window.
576 * TODO: Doesn't seem to adjust the width though for some reason.
577 */
578 static BOOL
579_OnWindowPosChanging(
580 HWND hwnd,
581 LPWINDOWPOS lpwpos)
582{
583 RECT workarea_rect;
584
585 if (!(lpwpos->flags & SWP_NOSIZE))
586 {
587 if (IsMaximized(hwnd)
588 && (os_version.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS
589 || (os_version.dwPlatformId == VER_PLATFORM_WIN32_NT
590 && os_version.dwMajorVersion >= 4)))
591 {
592 SystemParametersInfo(SPI_GETWORKAREA, 0, &workarea_rect, 0);
593 lpwpos->x = workarea_rect.left;
594 lpwpos->y = workarea_rect.top;
595 lpwpos->cx = workarea_rect.right - workarea_rect.left;
596 lpwpos->cy = workarea_rect.bottom - workarea_rect.top;
597 }
598 gui_mswin_get_valid_dimensions(lpwpos->cx, lpwpos->cy,
599 &lpwpos->cx, &lpwpos->cy);
600 }
601 return 0;
602}
603#endif
604
605#ifdef FEAT_NETBEANS_INTG
606 static void
607_OnWindowPosChanged(
608 HWND hwnd,
609 const LPWINDOWPOS lpwpos)
610{
611 static int x = 0, y = 0, cx = 0, cy = 0;
612
613 if (WSInitialized && (lpwpos->x != x || lpwpos->y != y
614 || lpwpos->cx != cx || lpwpos->cy != cy))
615 {
616 x = lpwpos->x;
617 y = lpwpos->y;
618 cx = lpwpos->cx;
619 cy = lpwpos->cy;
620 netbeans_frame_moved(x, y);
621 }
622 /* Allow to send WM_SIZE and WM_MOVE */
623 FORWARD_WM_WINDOWPOSCHANGED(hwnd, lpwpos, MyWindowProc);
624}
625#endif
626
627 static int
628_DuringSizing(
Bram Moolenaar071d4272004-06-13 20:20:40 +0000629 UINT fwSide,
630 LPRECT lprc)
631{
632 int w, h;
633 int valid_w, valid_h;
634 int w_offset, h_offset;
635
636 w = lprc->right - lprc->left;
637 h = lprc->bottom - lprc->top;
638 gui_mswin_get_valid_dimensions(w, h, &valid_w, &valid_h);
639 w_offset = w - valid_w;
640 h_offset = h - valid_h;
641
642 if (fwSide == WMSZ_LEFT || fwSide == WMSZ_TOPLEFT
643 || fwSide == WMSZ_BOTTOMLEFT)
644 lprc->left += w_offset;
645 else if (fwSide == WMSZ_RIGHT || fwSide == WMSZ_TOPRIGHT
646 || fwSide == WMSZ_BOTTOMRIGHT)
647 lprc->right -= w_offset;
648
649 if (fwSide == WMSZ_TOP || fwSide == WMSZ_TOPLEFT
650 || fwSide == WMSZ_TOPRIGHT)
651 lprc->top += h_offset;
652 else if (fwSide == WMSZ_BOTTOM || fwSide == WMSZ_BOTTOMLEFT
653 || fwSide == WMSZ_BOTTOMRIGHT)
654 lprc->bottom -= h_offset;
655 return TRUE;
656}
657
658
659
660 static LRESULT CALLBACK
661_WndProc(
662 HWND hwnd,
663 UINT uMsg,
664 WPARAM wParam,
665 LPARAM lParam)
666{
667 /*
668 TRACE("WndProc: hwnd = %08x, msg = %x, wParam = %x, lParam = %x\n",
669 hwnd, uMsg, wParam, lParam);
670 */
671
672 HandleMouseHide(uMsg, lParam);
673
674 s_uMsg = uMsg;
675 s_wParam = wParam;
676 s_lParam = lParam;
677
678 switch (uMsg)
679 {
680 HANDLE_MSG(hwnd, WM_DEADCHAR, _OnDeadChar);
681 HANDLE_MSG(hwnd, WM_SYSDEADCHAR, _OnDeadChar);
682 /* HANDLE_MSG(hwnd, WM_ACTIVATE, _OnActivate); */
683 HANDLE_MSG(hwnd, WM_CLOSE, _OnClose);
684 /* HANDLE_MSG(hwnd, WM_COMMAND, _OnCommand); */
685 HANDLE_MSG(hwnd, WM_DESTROY, _OnDestroy);
686 HANDLE_MSG(hwnd, WM_DROPFILES, _OnDropFiles);
687 HANDLE_MSG(hwnd, WM_HSCROLL, _OnScroll);
688 HANDLE_MSG(hwnd, WM_KILLFOCUS, _OnKillFocus);
689#ifdef FEAT_MENU
690 HANDLE_MSG(hwnd, WM_COMMAND, _OnMenu);
691#endif
692 /* HANDLE_MSG(hwnd, WM_MOVE, _OnMove); */
693 /* HANDLE_MSG(hwnd, WM_NCACTIVATE, _OnNCActivate); */
694 HANDLE_MSG(hwnd, WM_SETFOCUS, _OnSetFocus);
695 HANDLE_MSG(hwnd, WM_SIZE, _OnSize);
696 /* HANDLE_MSG(hwnd, WM_SYSCOMMAND, _OnSysCommand); */
697 /* HANDLE_MSG(hwnd, WM_SYSKEYDOWN, _OnAltKey); */
698 HANDLE_MSG(hwnd, WM_VSCROLL, _OnScroll);
699 // HANDLE_MSG(hwnd, WM_WINDOWPOSCHANGING, _OnWindowPosChanging);
700 HANDLE_MSG(hwnd, WM_ACTIVATEAPP, _OnActivateApp);
701#ifdef FEAT_NETBEANS_INTG
702 HANDLE_MSG(hwnd, WM_WINDOWPOSCHANGED, _OnWindowPosChanged);
703#endif
704
705 case WM_QUERYENDSESSION: /* System wants to go down. */
706 gui_shell_closed(); /* Will exit when no changed buffers. */
707 return FALSE; /* Do NOT allow system to go down. */
708
709 case WM_ENDSESSION:
710 if (wParam) /* system only really goes down when wParam is TRUE */
711 _OnEndSession();
712 break;
713
714 case WM_CHAR:
715 /* Don't use HANDLE_MSG() for WM_CHAR, it truncates wParam to a single
716 * byte while we want the UTF-16 character value. */
717 _OnChar(hwnd, wParam, (int)(short)LOWORD(lParam));
718 return 0L;
719
720 case WM_SYSCHAR:
721 /*
722 * if 'winaltkeys' is "no", or it's "menu" and it's not a menu
723 * shortcut key, handle like a typed ALT key, otherwise call Windows
724 * ALT key handling.
725 */
726#ifdef FEAT_MENU
727 if ( !gui.menu_is_active
728 || p_wak[0] == 'n'
729 || (p_wak[0] == 'm' && !gui_is_menu_shortcut((int)wParam))
730 )
731#endif
732 {
733 _OnSysChar(hwnd, wParam, (int)(short)LOWORD(lParam));
734 return 0L;
735 }
736#ifdef FEAT_MENU
737 else
738 return MyWindowProc(hwnd, uMsg, wParam, lParam);
739#endif
740
741 case WM_SYSKEYUP:
742#ifdef FEAT_MENU
743 /* This used to be done only when menu is active: ALT key is used for
744 * that. But that caused problems when menu is disabled and using
745 * Alt-Tab-Esc: get into a strange state where no mouse-moved events
746 * are received, mouse pointer remains hidden. */
747 return MyWindowProc(hwnd, uMsg, wParam, lParam);
748#else
749 return 0;
750#endif
751
752 case WM_SIZING: /* HANDLE_MSG doesn't seem to handle this one */
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +0000753 return _DuringSizing((UINT)wParam, (LPRECT)lParam);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000754
755 case WM_MOUSEWHEEL:
756 _OnMouseWheel(hwnd, HIWORD(wParam));
757 break;
758
Bram Moolenaar520470a2005-06-16 21:59:56 +0000759 /* Notification for change in SystemParametersInfo() */
760 case WM_SETTINGCHANGE:
761 return _OnSettingChange((UINT)wParam);
762
Bram Moolenaar071d4272004-06-13 20:20:40 +0000763#ifdef FEAT_TOOLBAR
764 case WM_NOTIFY:
765 switch (((LPNMHDR) lParam)->code)
766 {
767 case TTN_NEEDTEXT:
768 {
769 LPTOOLTIPTEXT lpttt;
770 UINT idButton;
771 int idx;
772 vimmenu_T *pMenu;
773
774 lpttt = (LPTOOLTIPTEXT)lParam;
775 idButton = (UINT) lpttt->hdr.idFrom;
776 pMenu = gui_mswin_find_menu(root_menu, idButton);
777 if (pMenu)
778 {
779 idx = MENU_INDEX_TIP;
780 if (pMenu->strings[idx])
781 {
782 lpttt->hinst = NULL; /* string, not resource */
783 lpttt->lpszText = pMenu->strings[idx];
784 }
785 }
786 }
787 break;
788 default:
789 break;
790 }
791 break;
792#endif
793#if defined(MENUHINTS) && defined(FEAT_MENU)
794 case WM_MENUSELECT:
795 if (((UINT) HIWORD(wParam)
796 & (0xffff ^ (MF_MOUSESELECT + MF_BITMAP + MF_POPUP)))
797 == MF_HILITE
798 && (State & CMDLINE) == 0)
799 {
800 UINT idButton;
801 vimmenu_T *pMenu;
802 static int did_menu_tip = FALSE;
803
804 if (did_menu_tip)
805 {
806 msg_clr_cmdline();
807 setcursor();
808 out_flush();
809 did_menu_tip = FALSE;
810 }
811
812 idButton = (UINT)LOWORD(wParam);
813 pMenu = gui_mswin_find_menu(root_menu, idButton);
814 if (pMenu != NULL && pMenu->strings[MENU_INDEX_TIP] != 0
815 && GetMenuState(s_menuBar, pMenu->id, MF_BYCOMMAND) != -1)
816 {
817 msg(pMenu->strings[MENU_INDEX_TIP]);
818 setcursor();
819 out_flush();
820 did_menu_tip = TRUE;
821 }
822 }
823 break;
824#endif
825 case WM_NCHITTEST:
826 {
827 LRESULT result;
828 int x, y;
829 int xPos = GET_X_LPARAM(lParam);
830
831 result = MyWindowProc(hwnd, uMsg, wParam, lParam);
832 if (result == HTCLIENT)
833 {
834 gui_mch_get_winpos(&x, &y);
835 xPos -= x;
836
837 if (xPos < 48) /* <VN> TODO should use system metric? */
838 return HTBOTTOMLEFT;
839 else
840 return HTBOTTOMRIGHT;
841 }
842 else
843 return result;
844 }
845 /* break; notreached */
846
847#ifdef FEAT_MBYTE_IME
848 case WM_IME_NOTIFY:
849 if (!_OnImeNotify(hwnd, (DWORD)wParam, (DWORD)lParam))
850 return MyWindowProc(hwnd, uMsg, wParam, lParam);
851 break;
852 case WM_IME_COMPOSITION:
853 if (!_OnImeComposition(hwnd, wParam, lParam))
854 return MyWindowProc(hwnd, uMsg, wParam, lParam);
855 break;
856#endif
857
858 default:
859 if (uMsg == msh_msgmousewheel && msh_msgmousewheel != 0)
860 { /* handle MSH_MOUSEWHEEL messages for Intellimouse */
861 _OnMouseWheel(hwnd, HIWORD(wParam));
862 break;
863 }
864#ifdef MSWIN_FIND_REPLACE
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000865 else if (uMsg == s_findrep_msg && s_findrep_msg != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000866 {
867 _OnFindRepl();
868 }
869#endif
870 return MyWindowProc(hwnd, uMsg, wParam, lParam);
871 }
872
873 return 1;
874}
875
876/*
877 * End of call-back routines
878 */
879
880/* parent window, if specified with -P */
881HWND vim_parent_hwnd = NULL;
882
883 static BOOL CALLBACK
884FindWindowTitle(HWND hwnd, LPARAM lParam)
885{
886 char buf[2048];
887 char *title = (char *)lParam;
888
889 if (GetWindowText(hwnd, buf, sizeof(buf)))
890 {
891 if (strstr(buf, title) != NULL)
892 {
Bram Moolenaar69a7cb42004-06-20 12:51:53 +0000893 /* Found it. Store the window ref. and quit searching if MDI
894 * works. */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000895 vim_parent_hwnd = FindWindowEx(hwnd, NULL, "MDIClient", NULL);
Bram Moolenaar69a7cb42004-06-20 12:51:53 +0000896 if (vim_parent_hwnd != NULL)
897 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000898 }
899 }
900 return TRUE; /* continue searching */
901}
902
903/*
904 * Invoked for '-P "title"' argument: search for parent application to open
905 * our window in.
906 */
907 void
908gui_mch_set_parent(char *title)
909{
910 EnumWindows(FindWindowTitle, (LPARAM)title);
911 if (vim_parent_hwnd == NULL)
912 {
913 EMSG2(_("E671: Cannot find window title \"%s\""), title);
914 mch_exit(2);
915 }
916}
917
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +0000918#ifndef FEAT_OLE
Bram Moolenaar071d4272004-06-13 20:20:40 +0000919 static void
920ole_error(char *arg)
921{
922 EMSG2(_("E243: Argument not supported: \"-%s\"; Use the OLE version."),
923 arg);
924}
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +0000925#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000926
927/*
928 * Parse the GUI related command-line arguments. Any arguments used are
929 * deleted from argv, and *argc is decremented accordingly. This is called
930 * when vim is started, whether or not the GUI has been started.
931 */
932 void
933gui_mch_prepare(int *argc, char **argv)
934{
935 int silent = FALSE;
936 int idx;
937
938 /* Check for special OLE command line parameters */
939 if ((*argc == 2 || *argc == 3) && (argv[1][0] == '-' || argv[1][0] == '/'))
940 {
941 /* Check for a "-silent" argument first. */
942 if (*argc == 3 && STRICMP(argv[1] + 1, "silent") == 0
943 && (argv[2][0] == '-' || argv[2][0] == '/'))
944 {
945 silent = TRUE;
946 idx = 2;
947 }
948 else
949 idx = 1;
950
951 /* Register Vim as an OLE Automation server */
952 if (STRICMP(argv[idx] + 1, "register") == 0)
953 {
954#ifdef FEAT_OLE
955 RegisterMe(silent);
956 mch_exit(0);
957#else
958 if (!silent)
959 ole_error("register");
960 mch_exit(2);
961#endif
962 }
963
964 /* Unregister Vim as an OLE Automation server */
965 if (STRICMP(argv[idx] + 1, "unregister") == 0)
966 {
967#ifdef FEAT_OLE
968 UnregisterMe(!silent);
969 mch_exit(0);
970#else
971 if (!silent)
972 ole_error("unregister");
973 mch_exit(2);
974#endif
975 }
976
977 /* Ignore an -embedding argument. It is only relevant if the
978 * application wants to treat the case when it is started manually
979 * differently from the case where it is started via automation (and
980 * we don't).
981 */
982 if (STRICMP(argv[idx] + 1, "embedding") == 0)
983 {
984#ifdef FEAT_OLE
985 *argc = 1;
986#else
987 ole_error("embedding");
988 mch_exit(2);
989#endif
990 }
991 }
992
993#ifdef FEAT_OLE
994 {
995 int bDoRestart = FALSE;
996
997 InitOLE(&bDoRestart);
998 /* automatically exit after registering */
999 if (bDoRestart)
1000 mch_exit(0);
1001 }
1002#endif
1003
1004#ifdef FEAT_NETBEANS_INTG
1005 {
1006 /* stolen from gui_x11.x */
1007 int arg;
1008
1009 for (arg = 1; arg < *argc; arg++)
1010 if (strncmp("-nb", argv[arg], 3) == 0)
1011 {
1012 usingNetbeans++;
1013 netbeansArg = argv[arg];
1014 mch_memmove(&argv[arg], &argv[arg + 1],
1015 (--*argc - arg) * sizeof(char *));
1016 argv[*argc] = NULL;
1017 break; /* enough? */
1018 }
1019
1020 if (usingNetbeans)
1021 {
1022 WSADATA wsaData;
1023 int wsaerr;
1024
1025 /* Init WinSock */
1026 wsaerr = WSAStartup(MAKEWORD(2, 2), &wsaData);
1027 if (wsaerr == 0)
1028 WSInitialized = TRUE;
1029 }
1030 }
1031#endif
1032
1033 /* get the OS version info */
1034 os_version.dwOSVersionInfoSize = sizeof(os_version);
1035 GetVersionEx(&os_version); /* this call works on Win32s, Win95 and WinNT */
1036
1037 /* try and load the user32.dll library and get the entry points for
1038 * multi-monitor-support. */
1039 if ((user32_lib = LoadLibrary("User32.dll")) != NULL)
1040 {
1041 pMonitorFromWindow = (TMonitorFromWindow)GetProcAddress(user32_lib,
1042 "MonitorFromWindow");
1043
1044 /* there are ...A and ...W version of GetMonitorInfo - looking at
1045 * winuser.h, they have exactly the same declaration. */
1046 pGetMonitorInfo = (TGetMonitorInfo)GetProcAddress(user32_lib,
1047 "GetMonitorInfoA");
1048 }
1049}
1050
1051/*
1052 * Initialise the GUI. Create all the windows, set up all the call-backs
1053 * etc.
1054 */
1055 int
1056gui_mch_init(void)
1057{
1058 const char szVimWndClass[] = VIM_CLASS;
1059 const char szTextAreaClass[] = "VimTextArea";
1060 WNDCLASS wndclass;
1061#ifdef FEAT_MBYTE
1062 const WCHAR szVimWndClassW[] = VIM_CLASSW;
1063 WNDCLASSW wndclassw;
1064#endif
1065#ifdef GLOBAL_IME
1066 ATOM atom;
1067#endif
1068
Bram Moolenaar071d4272004-06-13 20:20:40 +00001069 /* Return here if the window was already opened (happens when
1070 * gui_mch_dialog() is called early). */
1071 if (s_hwnd != NULL)
Bram Moolenaar748bf032005-02-02 23:04:36 +00001072 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001073
1074 /*
1075 * Load the tearoff bitmap
1076 */
1077#ifdef FEAT_TEAROFF
1078 s_htearbitmap = LoadBitmap(s_hinst, "IDB_TEAROFF");
1079#endif
1080
1081 gui.scrollbar_width = GetSystemMetrics(SM_CXVSCROLL);
1082 gui.scrollbar_height = GetSystemMetrics(SM_CYHSCROLL);
1083#ifdef FEAT_MENU
1084 gui.menu_height = 0; /* Windows takes care of this */
1085#endif
1086 gui.border_width = 0;
1087
1088 s_brush = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
1089
1090#ifdef FEAT_MBYTE
1091 /* First try using the wide version, so that we can use any title.
1092 * Otherwise only characters in the active codepage will work. */
1093 if (GetClassInfoW(s_hinst, szVimWndClassW, &wndclassw) == 0)
1094 {
1095 wndclassw.style = 0;
1096 wndclassw.lpfnWndProc = _WndProc;
1097 wndclassw.cbClsExtra = 0;
1098 wndclassw.cbWndExtra = 0;
1099 wndclassw.hInstance = s_hinst;
1100 wndclassw.hIcon = LoadIcon(wndclassw.hInstance, "IDR_VIM");
1101 wndclassw.hCursor = LoadCursor(NULL, IDC_ARROW);
1102 wndclassw.hbrBackground = s_brush;
1103 wndclassw.lpszMenuName = NULL;
1104 wndclassw.lpszClassName = szVimWndClassW;
1105
1106 if ((
1107#ifdef GLOBAL_IME
1108 atom =
1109#endif
1110 RegisterClassW(&wndclassw)) == 0)
1111 {
1112 if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
1113 return FAIL;
1114
1115 /* Must be Windows 98, fall back to non-wide function. */
1116 }
1117 else
1118 wide_WindowProc = TRUE;
1119 }
1120
1121 if (!wide_WindowProc)
1122#endif
1123
1124 if (GetClassInfo(s_hinst, szVimWndClass, &wndclass) == 0)
1125 {
1126 wndclass.style = 0;
1127 wndclass.lpfnWndProc = _WndProc;
1128 wndclass.cbClsExtra = 0;
1129 wndclass.cbWndExtra = 0;
1130 wndclass.hInstance = s_hinst;
1131 wndclass.hIcon = LoadIcon(wndclass.hInstance, "IDR_VIM");
1132 wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
1133 wndclass.hbrBackground = s_brush;
1134 wndclass.lpszMenuName = NULL;
1135 wndclass.lpszClassName = szVimWndClass;
1136
1137 if ((
1138#ifdef GLOBAL_IME
1139 atom =
1140#endif
1141 RegisterClass(&wndclass)) == 0)
1142 return FAIL;
1143 }
1144
1145 if (vim_parent_hwnd != NULL)
1146 {
1147#ifdef HAVE_TRY_EXCEPT
1148 __try
1149 {
1150#endif
1151 /* Open inside the specified parent window.
1152 * TODO: last argument should point to a CLIENTCREATESTRUCT
1153 * structure. */
1154 s_hwnd = CreateWindowEx(
1155 WS_EX_MDICHILD,
1156 szVimWndClass, "Vim MSWindows GUI",
1157 WS_OVERLAPPEDWINDOW | WS_CHILD | WS_CLIPSIBLINGS | 0xC000,
1158 gui_win_x == -1 ? CW_USEDEFAULT : gui_win_x,
1159 gui_win_y == -1 ? CW_USEDEFAULT : gui_win_y,
1160 100, /* Any value will do */
1161 100, /* Any value will do */
1162 vim_parent_hwnd, NULL,
1163 s_hinst, NULL);
1164#ifdef HAVE_TRY_EXCEPT
1165 }
1166 __except(EXCEPTION_EXECUTE_HANDLER)
1167 {
1168 /* NOP */
1169 }
1170#endif
1171 if (s_hwnd == NULL)
1172 {
1173 EMSG(_("E672: Unable to open window inside MDI application"));
1174 mch_exit(2);
1175 }
1176 }
1177 else
1178 /* Open toplevel window. */
1179 s_hwnd = CreateWindow(
1180 szVimWndClass, "Vim MSWindows GUI",
1181 WS_OVERLAPPEDWINDOW,
1182 gui_win_x == -1 ? CW_USEDEFAULT : gui_win_x,
1183 gui_win_y == -1 ? CW_USEDEFAULT : gui_win_y,
1184 100, /* Any value will do */
1185 100, /* Any value will do */
1186 NULL, NULL,
1187 s_hinst, NULL);
1188
1189 if (s_hwnd == NULL)
1190 return FAIL;
1191
1192#ifdef GLOBAL_IME
1193 global_ime_init(atom, s_hwnd);
1194#endif
1195#if defined(FEAT_MBYTE_IME) && defined(DYNAMIC_IME)
1196 dyn_imm_load();
1197#endif
1198
1199 /* Create the text area window */
1200 if (GetClassInfo(s_hinst, szTextAreaClass, &wndclass) == 0)
1201 {
1202 wndclass.style = CS_OWNDC;
1203 wndclass.lpfnWndProc = _TextAreaWndProc;
1204 wndclass.cbClsExtra = 0;
1205 wndclass.cbWndExtra = 0;
1206 wndclass.hInstance = s_hinst;
1207 wndclass.hIcon = NULL;
1208 wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
1209 wndclass.hbrBackground = NULL;
1210 wndclass.lpszMenuName = NULL;
1211 wndclass.lpszClassName = szTextAreaClass;
1212
1213 if (RegisterClass(&wndclass) == 0)
1214 return FAIL;
1215 }
1216 s_textArea = CreateWindowEx(
1217 WS_EX_CLIENTEDGE,
1218 szTextAreaClass, "Vim text area",
1219 WS_CHILD | WS_VISIBLE, 0, 0,
1220 100, /* Any value will do for now */
1221 100, /* Any value will do for now */
1222 s_hwnd, NULL,
1223 s_hinst, NULL);
1224
1225 if (s_textArea == NULL)
1226 return FAIL;
1227
1228#ifdef FEAT_MENU
1229 s_menuBar = CreateMenu();
1230#endif
1231 s_hdc = GetDC(s_textArea);
1232
1233#ifdef MSWIN16_FASTTEXT
1234 SetBkMode(s_hdc, OPAQUE);
1235#endif
1236
1237#ifdef FEAT_WINDOWS
1238 DragAcceptFiles(s_hwnd, TRUE);
1239#endif
1240
1241 /* Do we need to bother with this? */
1242 /* m_fMouseAvail = GetSystemMetrics(SM_MOUSEPRESENT); */
1243
1244 /* Get background/foreground colors from the system */
1245 gui_mch_def_colors();
1246
1247 /* Get the colors from the "Normal" group (set in syntax.c or in a vimrc
1248 * file) */
1249 set_normal_colors();
1250
1251 /*
1252 * Check that none of the colors are the same as the background color.
1253 * Then store the current values as the defaults.
1254 */
1255 gui_check_colors();
1256 gui.def_norm_pixel = gui.norm_pixel;
1257 gui.def_back_pixel = gui.back_pixel;
1258
1259 /* Get the colors for the highlight groups (gui_check_colors() might have
1260 * changed them) */
1261 highlight_gui_started();
1262
1263 /*
1264 * Start out by adding the configured border width into the border offset
1265 */
1266 gui.border_offset = gui.border_width + 2; /*CLIENT EDGE*/
1267
1268 /*
1269 * Set up for Intellimouse processing
1270 */
1271 init_mouse_wheel();
1272
1273 /*
1274 * compute a couple of metrics used for the dialogs
1275 */
1276 get_dialog_font_metrics();
1277#ifdef FEAT_TOOLBAR
1278 /*
1279 * Create the toolbar
1280 */
1281 initialise_toolbar();
1282#endif
1283#ifdef MSWIN_FIND_REPLACE
1284 /*
1285 * Initialise the dialog box stuff
1286 */
1287 s_findrep_msg = RegisterWindowMessage(FINDMSGSTRING);
1288
1289 /* Initialise the struct */
1290 s_findrep_struct.lStructSize = sizeof(s_findrep_struct);
1291 s_findrep_struct.lpstrFindWhat = alloc(MSWIN_FR_BUFSIZE);
1292 s_findrep_struct.lpstrFindWhat[0] = NUL;
1293 s_findrep_struct.lpstrReplaceWith = alloc(MSWIN_FR_BUFSIZE);
1294 s_findrep_struct.lpstrReplaceWith[0] = NUL;
1295 s_findrep_struct.wFindWhatLen = MSWIN_FR_BUFSIZE;
1296 s_findrep_struct.wReplaceWithLen = MSWIN_FR_BUFSIZE;
1297#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001298
Bram Moolenaar748bf032005-02-02 23:04:36 +00001299theend:
1300 /* Display any pending error messages */
1301 display_errors();
1302
Bram Moolenaar071d4272004-06-13 20:20:40 +00001303 return OK;
1304}
1305
1306/*
1307 * Get the size of the screen, taking position on multiple monitors into
1308 * account (if supported).
1309 */
1310 static void
1311get_work_area(RECT *spi_rect)
1312{
1313 _HMONITOR mon;
1314 _MONITORINFO moninfo;
1315
1316 /* use these functions only if available */
1317 if (pMonitorFromWindow != NULL && pGetMonitorInfo != NULL)
1318 {
1319 /* work out which monitor the window is on, and get *it's* work area */
1320 mon = pMonitorFromWindow(s_hwnd, 1 /*MONITOR_DEFAULTTOPRIMARY*/);
1321 if (mon != NULL)
1322 {
1323 moninfo.cbSize = sizeof(_MONITORINFO);
1324 if (pGetMonitorInfo(mon, &moninfo))
1325 {
1326 *spi_rect = moninfo.rcWork;
1327 return;
1328 }
1329 }
1330 }
1331 /* this is the old method... */
1332 SystemParametersInfo(SPI_GETWORKAREA, 0, spi_rect, 0);
1333}
1334
1335/*
1336 * Set the size of the window to the given width and height in pixels.
1337 */
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +00001338/*ARGSUSED*/
Bram Moolenaar071d4272004-06-13 20:20:40 +00001339 void
1340gui_mch_set_shellsize(int width, int height,
1341 int min_width, int min_height, int base_width, int base_height)
1342{
1343 RECT workarea_rect;
1344 int win_width, win_height;
1345 int win_xpos, win_ypos;
1346 WINDOWPLACEMENT wndpl;
1347
1348 /* try to keep window completely on screen */
1349 /* get size of the screen work area (excludes taskbar, appbars) */
1350 get_work_area(&workarea_rect);
1351
1352 /* get current posision of our window */
1353 wndpl.length = sizeof(WINDOWPLACEMENT);
1354 GetWindowPlacement(s_hwnd, &wndpl);
1355
1356 /* Resizing a maximized window looks very strange, unzoom it first.
1357 * But don't do it when still starting up, it may have been requested in
1358 * the shortcut. */
1359 if (wndpl.showCmd == SW_SHOWMAXIMIZED && starting == 0)
1360 {
1361 ShowWindow(s_hwnd, SW_SHOWNORMAL);
1362 /* Need to get the settings of the normal window. */
1363 GetWindowPlacement(s_hwnd, &wndpl);
1364 }
1365
1366 win_xpos = wndpl.rcNormalPosition.left;
1367 win_ypos = wndpl.rcNormalPosition.top;
1368
1369 /* compute the size of the outside of the window */
1370 win_width = width + GetSystemMetrics(SM_CXFRAME) * 2;
1371 win_height = height + GetSystemMetrics(SM_CYFRAME) * 2
1372 + GetSystemMetrics(SM_CYCAPTION)
1373#ifdef FEAT_MENU
1374 + gui_mswin_get_menu_height(FALSE)
1375#endif
1376 ;
1377
1378 /* if the window is going off the screen, move it on to the screen */
1379 if (win_xpos + win_width > workarea_rect.right)
1380 win_xpos = workarea_rect.right - win_width;
1381
1382 if (win_xpos < workarea_rect.left)
1383 win_xpos = workarea_rect.left;
1384
1385 if (win_ypos + win_height > workarea_rect.bottom)
1386 win_ypos = workarea_rect.bottom - win_height;
1387
1388 if (win_ypos < workarea_rect.top)
1389 win_ypos = workarea_rect.top;
1390
1391 /* When the taskbar is placed on the left or top of the screen,
1392 * SetWindowPlacement() adds its width or height automatically, compensate
1393 * for that. When the offset is over 400 it's probably something else,
1394 * skip it then (just in case). */
1395 if (workarea_rect.left > 0 && workarea_rect.left < 400)
1396 win_xpos -= workarea_rect.left;
1397 if (workarea_rect.top > 0 && workarea_rect.top < 400)
1398 win_ypos -= workarea_rect.top;
1399
1400 wndpl.rcNormalPosition.left = win_xpos;
1401 wndpl.rcNormalPosition.right = win_xpos + win_width;
1402 wndpl.rcNormalPosition.top = win_ypos;
1403 wndpl.rcNormalPosition.bottom = win_ypos + win_height;
1404
1405 /* set window position - we should use SetWindowPlacement rather than
1406 * SetWindowPos as the MSDN docs say the coord systems returned by
1407 * these two are not compatible. */
1408 SetWindowPlacement(s_hwnd, &wndpl);
1409
1410 SetActiveWindow(s_hwnd);
1411 SetFocus(s_hwnd);
1412
1413#ifdef FEAT_MENU
1414 /* Menu may wrap differently now */
1415 gui_mswin_get_menu_height(!gui.starting);
1416#endif
1417}
1418
1419
1420 void
1421gui_mch_set_scrollbar_thumb(
1422 scrollbar_T *sb,
1423 long val,
1424 long size,
1425 long max)
1426{
1427 SCROLLINFO info;
1428
1429 sb->scroll_shift = 0;
1430 while (max > 32767)
1431 {
1432 max = (max + 1) >> 1;
1433 val >>= 1;
1434 size >>= 1;
1435 ++sb->scroll_shift;
1436 }
1437
1438 if (sb->scroll_shift > 0)
1439 ++size;
1440
1441 info.cbSize = sizeof(info);
1442 info.fMask = SIF_POS | SIF_RANGE | SIF_PAGE;
1443 info.nPos = val;
1444 info.nMin = 0;
1445 info.nMax = max;
1446 info.nPage = size;
1447 SetScrollInfo(sb->id, SB_CTL, &info, TRUE);
1448}
1449
1450
1451/*
1452 * Set the current text font.
1453 */
1454 void
1455gui_mch_set_font(GuiFont font)
1456{
1457 gui.currFont = font;
1458}
1459
1460
1461/*
1462 * Set the current text foreground color.
1463 */
1464 void
1465gui_mch_set_fg_color(guicolor_T color)
1466{
1467 gui.currFgColor = color;
1468}
1469
1470/*
1471 * Set the current text background color.
1472 */
1473 void
1474gui_mch_set_bg_color(guicolor_T color)
1475{
1476 gui.currBgColor = color;
1477}
1478
Bram Moolenaare2cc9702005-03-15 22:43:58 +00001479/*
1480 * Set the current text special color.
1481 */
1482 void
1483gui_mch_set_sp_color(guicolor_T color)
1484{
1485 gui.currSpColor = color;
1486}
1487
Bram Moolenaar071d4272004-06-13 20:20:40 +00001488#if defined(FEAT_MBYTE) && defined(FEAT_MBYTE_IME)
1489/*
1490 * Multi-byte handling, originally by Sung-Hoon Baek.
1491 * First static functions (no prototypes generated).
1492 */
1493#ifdef _MSC_VER
1494# include <ime.h> /* Apparently not needed for Cygwin, MingW or Borland. */
1495#endif
1496#include <imm.h>
1497
1498/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00001499 * handle WM_IME_NOTIFY message
1500 */
Bram Moolenaard857f0e2005-06-21 22:37:39 +00001501/*ARGSUSED*/
Bram Moolenaar071d4272004-06-13 20:20:40 +00001502 static LRESULT
1503_OnImeNotify(HWND hWnd, DWORD dwCommand, DWORD dwData)
1504{
1505 LRESULT lResult = 0;
1506 HIMC hImc;
1507
1508 if (!pImmGetContext || (hImc = pImmGetContext(hWnd)) == (HIMC)0)
1509 return lResult;
1510 switch (dwCommand)
1511 {
1512 case IMN_SETOPENSTATUS:
1513 if (pImmGetOpenStatus(hImc))
1514 {
1515 pImmSetCompositionFont(hImc, &norm_logfont);
1516 im_set_position(gui.row, gui.col);
1517
1518 /* Disable langmap */
1519 State &= ~LANGMAP;
1520 if (State & INSERT)
1521 {
1522#if defined(FEAT_WINDOWS) && defined(FEAT_KEYMAP)
1523 /* Unshown 'keymap' in status lines */
1524 if (curbuf->b_p_iminsert == B_IMODE_LMAP)
1525 {
1526 /* Save cursor position */
1527 int old_row = gui.row;
1528 int old_col = gui.col;
1529
1530 // This must be called here before
1531 // status_redraw_curbuf(), otherwise the mode
1532 // message may appear in the wrong position.
1533 showmode();
1534 status_redraw_curbuf();
1535 update_screen(0);
1536 /* Restore cursor position */
1537 gui.row = old_row;
1538 gui.col = old_col;
1539 }
1540#endif
1541 }
1542 }
1543 gui_update_cursor(TRUE, FALSE);
1544 lResult = 0;
1545 break;
1546 }
1547 pImmReleaseContext(hWnd, hImc);
1548 return lResult;
1549}
1550
Bram Moolenaard857f0e2005-06-21 22:37:39 +00001551/*ARGSUSED*/
Bram Moolenaar071d4272004-06-13 20:20:40 +00001552 static LRESULT
1553_OnImeComposition(HWND hwnd, WPARAM dbcs, LPARAM param)
1554{
1555 char_u *ret;
1556 int len;
1557
1558 if ((param & GCS_RESULTSTR) == 0) /* Composition unfinished. */
1559 return 0;
1560
1561 ret = GetResultStr(hwnd, GCS_RESULTSTR, &len);
1562 if (ret != NULL)
1563 {
1564 add_to_input_buf_csi(ret, len);
1565 vim_free(ret);
1566 return 1;
1567 }
1568 return 0;
1569}
1570
1571/*
1572 * get the current composition string, in UCS-2; *lenp is the number of
1573 * *lenp is the number of Unicode characters.
1574 */
1575 static short_u *
1576GetCompositionString_inUCS2(HIMC hIMC, DWORD GCS, int *lenp)
1577{
1578 LONG ret;
1579 LPWSTR wbuf = NULL;
1580 char_u *buf;
1581
1582 if (!pImmGetContext)
1583 return NULL; /* no imm32.dll */
1584
1585 /* Try Unicode; this'll always work on NT regardless of codepage. */
1586 ret = pImmGetCompositionStringW(hIMC, GCS, NULL, 0);
1587 if (ret == 0)
1588 return NULL; /* empty */
1589
1590 if (ret > 0)
1591 {
1592 /* Allocate the requested buffer plus space for the NUL character. */
1593 wbuf = (LPWSTR)alloc(ret + sizeof(WCHAR));
1594 if (wbuf != NULL)
1595 {
1596 pImmGetCompositionStringW(hIMC, GCS, wbuf, ret);
1597 *lenp = ret / sizeof(WCHAR);
1598 }
1599 return (short_u *)wbuf;
1600 }
1601
1602 /* ret < 0; we got an error, so try the ANSI version. This'll work
1603 * on 9x/ME, but only if the codepage happens to be set to whatever
1604 * we're inputting. */
1605 ret = pImmGetCompositionStringA(hIMC, GCS, NULL, 0);
1606 if (ret <= 0)
1607 return NULL; /* empty or error */
1608
1609 buf = alloc(ret);
1610 if (buf == NULL)
1611 return NULL;
1612 pImmGetCompositionStringA(hIMC, GCS, buf, ret);
1613
1614 /* convert from codepage to UCS-2 */
1615 MultiByteToWideChar_alloc(GetACP(), 0, buf, ret, &wbuf, lenp);
1616 vim_free(buf);
1617
1618 return (short_u *)wbuf;
1619}
1620
1621/*
1622 * void GetResultStr()
1623 *
1624 * This handles WM_IME_COMPOSITION with GCS_RESULTSTR flag on.
1625 * get complete composition string
1626 */
1627 static char_u *
1628GetResultStr(HWND hwnd, int GCS, int *lenp)
1629{
1630 HIMC hIMC; /* Input context handle. */
1631 short_u *buf = NULL;
1632 char_u *convbuf = NULL;
1633
1634 if (!pImmGetContext || (hIMC = pImmGetContext(hwnd)) == (HIMC)0)
1635 return NULL;
1636
1637 /* Reads in the composition string. */
1638 buf = GetCompositionString_inUCS2(hIMC, GCS, lenp);
1639 if (buf == NULL)
1640 return NULL;
1641
1642 convbuf = ucs2_to_enc(buf, lenp);
1643 pImmReleaseContext(hwnd, hIMC);
1644 vim_free(buf);
1645 return convbuf;
1646}
1647#endif
1648
1649/* For global functions we need prototypes. */
1650#if (defined(FEAT_MBYTE) && defined(FEAT_MBYTE_IME)) || defined(PROTO)
1651
1652/*
1653 * set font to IM.
1654 */
1655 void
1656im_set_font(LOGFONT *lf)
1657{
1658 HIMC hImc;
1659
1660 if (pImmGetContext && (hImc = pImmGetContext(s_hwnd)) != (HIMC)0)
1661 {
1662 pImmSetCompositionFont(hImc, lf);
1663 pImmReleaseContext(s_hwnd, hImc);
1664 }
1665}
1666
1667/*
1668 * Notify cursor position to IM.
1669 */
1670 void
1671im_set_position(int row, int col)
1672{
1673 HIMC hImc;
1674
1675 if (pImmGetContext && (hImc = pImmGetContext(s_hwnd)) != (HIMC)0)
1676 {
1677 COMPOSITIONFORM cfs;
1678
1679 cfs.dwStyle = CFS_POINT;
1680 cfs.ptCurrentPos.x = FILL_X(col);
1681 cfs.ptCurrentPos.y = FILL_Y(row);
1682 MapWindowPoints(s_textArea, s_hwnd, &cfs.ptCurrentPos, 1);
1683 pImmSetCompositionWindow(hImc, &cfs);
1684
1685 pImmReleaseContext(s_hwnd, hImc);
1686 }
1687}
1688
1689/*
1690 * Set IM status on ("active" is TRUE) or off ("active" is FALSE).
1691 */
1692 void
1693im_set_active(int active)
1694{
1695 HIMC hImc;
1696 static HIMC hImcOld = (HIMC)0;
1697
1698 if (pImmGetContext) /* if NULL imm32.dll wasn't loaded (yet) */
1699 {
1700 if (p_imdisable)
1701 {
1702 if (hImcOld == (HIMC)0)
1703 {
1704 hImcOld = pImmGetContext(s_hwnd);
1705 if (hImcOld)
1706 pImmAssociateContext(s_hwnd, (HIMC)0);
1707 }
1708 active = FALSE;
1709 }
1710 else if (hImcOld != (HIMC)0)
1711 {
1712 pImmAssociateContext(s_hwnd, hImcOld);
1713 hImcOld = (HIMC)0;
1714 }
1715
1716 hImc = pImmGetContext(s_hwnd);
1717 if (hImc)
1718 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00001719 /*
1720 * for Korean ime
1721 */
1722 HKL hKL = GetKeyboardLayout(0);
1723
1724 if (LOWORD(hKL) == MAKELANGID(LANG_KOREAN, SUBLANG_KOREAN))
1725 {
1726 static DWORD dwConversionSaved = 0, dwSentenceSaved = 0;
1727 static BOOL bSaved = FALSE;
1728
1729 if (active)
1730 {
1731 /* if we have a saved conversion status, restore it */
1732 if (bSaved)
1733 pImmSetConversionStatus(hImc, dwConversionSaved,
1734 dwSentenceSaved);
1735 bSaved = FALSE;
1736 }
1737 else
1738 {
1739 /* save conversion status and disable korean */
1740 if (pImmGetConversionStatus(hImc, &dwConversionSaved,
1741 &dwSentenceSaved))
1742 {
1743 bSaved = TRUE;
1744 pImmSetConversionStatus(hImc,
1745 dwConversionSaved & ~(IME_CMODE_NATIVE
1746 | IME_CMODE_FULLSHAPE),
1747 dwSentenceSaved);
1748 }
1749 }
1750 }
1751
Bram Moolenaar071d4272004-06-13 20:20:40 +00001752 pImmSetOpenStatus(hImc, active);
1753 pImmReleaseContext(s_hwnd, hImc);
1754 }
1755 }
1756}
1757
1758/*
1759 * Get IM status. When IM is on, return not 0. Else return 0.
1760 */
1761 int
1762im_get_status()
1763{
1764 int status = 0;
1765 HIMC hImc;
1766
1767 if (pImmGetContext && (hImc = pImmGetContext(s_hwnd)) != (HIMC)0)
1768 {
1769 status = pImmGetOpenStatus(hImc) ? 1 : 0;
1770 pImmReleaseContext(s_hwnd, hImc);
1771 }
1772 return status;
1773}
1774
1775#endif /* FEAT_MBYTE && FEAT_MBYTE_IME */
1776
1777#if defined(FEAT_MBYTE) && !defined(FEAT_MBYTE_IME) && defined(GLOBAL_IME)
1778/* Win32 with GLOBAL IME */
1779
1780/*
1781 * Notify cursor position to IM.
1782 */
1783 void
1784im_set_position(int row, int col)
1785{
1786 /* Win32 with GLOBAL IME */
1787 POINT p;
1788
1789 p.x = FILL_X(col);
1790 p.y = FILL_Y(row);
1791 MapWindowPoints(s_textArea, s_hwnd, &p, 1);
1792 global_ime_set_position(&p);
1793}
1794
1795/*
1796 * Set IM status on ("active" is TRUE) or off ("active" is FALSE).
1797 */
1798 void
1799im_set_active(int active)
1800{
1801 global_ime_set_status(active);
1802}
1803
1804/*
1805 * Get IM status. When IM is on, return not 0. Else return 0.
1806 */
1807 int
1808im_get_status()
1809{
1810 return global_ime_get_status();
1811}
1812#endif
1813
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00001814#ifdef FEAT_MBYTE
1815/*
1816 * Convert latin9 text to ucs-2.
1817 */
1818 static void
1819latin9_to_ucs(char_u *text, int len, WCHAR *unicodebuf)
1820{
1821 int c;
1822
Bram Moolenaarca003e12006-03-17 23:19:38 +00001823 while (--len >= 0)
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00001824 {
1825 c = *text++;
1826 switch (c)
1827 {
1828 case 0xa4: c = 0x20ac; break; /* euro */
1829 case 0xa6: c = 0x0160; break; /* S hat */
1830 case 0xa8: c = 0x0161; break; /* S -hat */
1831 case 0xb4: c = 0x017d; break; /* Z hat */
1832 case 0xb8: c = 0x017e; break; /* Z -hat */
1833 case 0xbc: c = 0x0152; break; /* OE */
1834 case 0xbd: c = 0x0153; break; /* oe */
1835 case 0xbe: c = 0x0178; break; /* Y */
1836 }
1837 *unicodebuf++ = c;
1838 }
1839}
1840#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001841
1842#ifdef FEAT_RIGHTLEFT
1843/*
1844 * What is this for? In the case where you are using Win98 or Win2K or later,
1845 * and you are using a Hebrew font (or Arabic!), Windows does you a favor and
1846 * reverses the string sent to the TextOut... family. This sucks, because we
1847 * go to a lot of effort to do the right thing, and there doesn't seem to be a
1848 * way to tell Windblows not to do this!
1849 *
1850 * The short of it is that this 'RevOut' only gets called if you are running
1851 * one of the new, "improved" MS OSes, and only if you are running in
1852 * 'rightleft' mode. It makes display take *slightly* longer, but not
1853 * noticeably so.
1854 */
1855 static void
1856RevOut( HDC s_hdc,
1857 int col,
1858 int row,
1859 UINT foptions,
1860 CONST RECT *pcliprect,
1861 LPCTSTR text,
1862 UINT len,
1863 CONST INT *padding)
1864{
1865 int ix;
1866 static int special = -1;
1867
1868 if (special == -1)
1869 {
1870 /* Check windows version: special treatment is needed if it is NT 5 or
1871 * Win98 or higher. */
1872 if ((os_version.dwPlatformId == VER_PLATFORM_WIN32_NT
1873 && os_version.dwMajorVersion >= 5)
1874 || (os_version.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS
1875 && (os_version.dwMajorVersion > 4
1876 || (os_version.dwMajorVersion == 4
1877 && os_version.dwMinorVersion > 0))))
1878 special = 1;
1879 else
1880 special = 0;
1881 }
1882
1883 if (special)
1884 for (ix = 0; ix < (int)len; ++ix)
1885 ExtTextOut(s_hdc, col + TEXT_X(ix), row, foptions,
1886 pcliprect, text + ix, 1, padding);
1887 else
1888 ExtTextOut(s_hdc, col, row, foptions, pcliprect, text, len, padding);
1889}
1890#endif
1891
1892 void
1893gui_mch_draw_string(
1894 int row,
1895 int col,
1896 char_u *text,
1897 int len,
1898 int flags)
1899{
1900 static int *padding = NULL;
1901 static int pad_size = 0;
1902 int i;
1903 const RECT *pcliprect = NULL;
1904 UINT foptions = 0;
1905#ifdef FEAT_MBYTE
1906 static WCHAR *unicodebuf = NULL;
1907 static int *unicodepdy = NULL;
Bram Moolenaar402d2fe2005-04-15 21:00:38 +00001908 static int unibuflen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001909 int n = 0;
1910#endif
1911 HPEN hpen, old_pen;
1912 int y;
1913
1914#ifndef MSWIN16_FASTTEXT
1915 /*
1916 * Italic and bold text seems to have an extra row of pixels at the bottom
1917 * (below where the bottom of the character should be). If we draw the
1918 * characters with a solid background, the top row of pixels in the
1919 * character below will be overwritten. We can fix this by filling in the
1920 * background ourselves, to the correct character proportions, and then
1921 * writing the character in transparent mode. Still have a problem when
1922 * the character is "_", which gets written on to the character below.
1923 * New fix: set gui.char_ascent to -1. This shifts all characters up one
1924 * pixel in their slots, which fixes the problem with the bottom row of
1925 * pixels. We still need this code because otherwise the top row of pixels
1926 * becomes a problem. - webb.
1927 */
1928 static HBRUSH hbr_cache[2] = {NULL, NULL};
1929 static guicolor_T brush_color[2] = {INVALCOLOR, INVALCOLOR};
1930 static int brush_lru = 0;
1931 HBRUSH hbr;
1932 RECT rc;
1933
1934 if (!(flags & DRAW_TRANSP))
1935 {
1936 /*
1937 * Clear background first.
1938 * Note: FillRect() excludes right and bottom of rectangle.
1939 */
1940 rc.left = FILL_X(col);
1941 rc.top = FILL_Y(row);
1942#ifdef FEAT_MBYTE
1943 if (has_mbyte)
1944 {
1945 int cell_len = 0;
1946
1947 /* Compute the length in display cells. */
1948 for (n = 0; n < len; n += MB_BYTE2LEN(text[n]))
1949 cell_len += (*mb_ptr2cells)(text + n);
1950 rc.right = FILL_X(col + cell_len);
1951 }
1952 else
1953#endif
1954 rc.right = FILL_X(col + len);
1955 rc.bottom = FILL_Y(row + 1);
1956
1957 /* Cache the created brush, that saves a lot of time. We need two:
1958 * one for cursor background and one for the normal background. */
1959 if (gui.currBgColor == brush_color[0])
1960 {
1961 hbr = hbr_cache[0];
1962 brush_lru = 1;
1963 }
1964 else if (gui.currBgColor == brush_color[1])
1965 {
1966 hbr = hbr_cache[1];
1967 brush_lru = 0;
1968 }
1969 else
1970 {
1971 if (hbr_cache[brush_lru] != NULL)
1972 DeleteBrush(hbr_cache[brush_lru]);
1973 hbr_cache[brush_lru] = CreateSolidBrush(gui.currBgColor);
1974 brush_color[brush_lru] = gui.currBgColor;
1975 hbr = hbr_cache[brush_lru];
1976 brush_lru = !brush_lru;
1977 }
1978 FillRect(s_hdc, &rc, hbr);
1979
1980 SetBkMode(s_hdc, TRANSPARENT);
1981
1982 /*
1983 * When drawing block cursor, prevent inverted character spilling
1984 * over character cell (can happen with bold/italic)
1985 */
1986 if (flags & DRAW_CURSOR)
1987 {
1988 pcliprect = &rc;
1989 foptions = ETO_CLIPPED;
1990 }
1991 }
1992#else
1993 /*
1994 * The alternative would be to write the characters in opaque mode, but
1995 * when the text is not exactly the same proportions as normal text, too
1996 * big or too little a rectangle gets drawn for the background.
1997 */
1998 SetBkMode(s_hdc, OPAQUE);
1999 SetBkColor(s_hdc, gui.currBgColor);
2000#endif
2001 SetTextColor(s_hdc, gui.currFgColor);
2002 SelectFont(s_hdc, gui.currFont);
2003
2004 if (pad_size != Columns || padding == NULL || padding[0] != gui.char_width)
2005 {
2006 vim_free(padding);
2007 pad_size = Columns;
2008
Bram Moolenaarc54b8a72005-09-30 21:20:29 +00002009 /* Don't give an out-of-memory message here, it would call us
2010 * recursively. */
2011 padding = (int *)lalloc(pad_size * sizeof(int), FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002012 if (padding != NULL)
2013 for (i = 0; i < pad_size; i++)
2014 padding[i] = gui.char_width;
2015 }
2016
2017 /* On NT, tell the font renderer not to "help" us with Hebrew and Arabic
2018 * text. This doesn't work in 9x, so we have to deal with it manually on
2019 * those systems. */
2020 if (os_version.dwPlatformId == VER_PLATFORM_WIN32_NT)
2021 foptions |= ETO_IGNORELANGUAGE;
2022
2023 /*
2024 * We have to provide the padding argument because italic and bold versions
2025 * of fixed-width fonts are often one pixel or so wider than their normal
2026 * versions.
2027 * No check for DRAW_BOLD, Windows will have done it already.
2028 */
2029
2030#ifdef FEAT_MBYTE
2031 /* Check if there are any UTF-8 characters. If not, use normal text
2032 * output to speed up output. */
2033 if (enc_utf8)
2034 for (n = 0; n < len; ++n)
2035 if (text[n] >= 0x80)
2036 break;
2037
2038 /* Check if the Unicode buffer exists and is big enough. Create it
Bram Moolenaar402d2fe2005-04-15 21:00:38 +00002039 * with the same length as the multi-byte string, the number of wide
Bram Moolenaar071d4272004-06-13 20:20:40 +00002040 * characters is always equal or smaller. */
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002041 if ((enc_utf8
2042 || (enc_codepage > 0 && (int)GetACP() != enc_codepage)
2043 || enc_latin9)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002044 && (unicodebuf == NULL || len > unibuflen))
2045 {
2046 vim_free(unicodebuf);
Bram Moolenaarc54b8a72005-09-30 21:20:29 +00002047 unicodebuf = (WCHAR *)lalloc(len * sizeof(WCHAR), FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002048
2049 vim_free(unicodepdy);
Bram Moolenaarc54b8a72005-09-30 21:20:29 +00002050 unicodepdy = (int *)lalloc(len * sizeof(int), FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002051
2052 unibuflen = len;
2053 }
2054
2055 if (enc_utf8 && n < len && unicodebuf != NULL)
2056 {
2057 /* Output UTF-8 characters. Caller has already separated
2058 * composing characters. */
Bram Moolenaarca003e12006-03-17 23:19:38 +00002059 int i;
2060 int wlen; /* string length in words */
2061 int clen; /* string length in characters */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002062 int cells; /* cell width of string up to composing char */
2063 int cw; /* width of current cell */
Bram Moolenaar362e1a32006-03-06 23:29:24 +00002064 int c;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002065
Bram Moolenaar97b2ad32006-03-18 21:40:56 +00002066 wlen = 0;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002067 clen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002068 cells = 0;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002069 for (i = 0; i < len; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00002070 {
Bram Moolenaar362e1a32006-03-06 23:29:24 +00002071 c = utf_ptr2char(text + i);
2072 if (c >= 0x10000)
2073 {
2074 /* Turn into UTF-16 encoding. */
Bram Moolenaarca003e12006-03-17 23:19:38 +00002075 unicodebuf[wlen++] = ((c - 0x10000) >> 10) + 0xD800;
2076 unicodebuf[wlen++] = ((c - 0x10000) & 0x3ff) + 0xDC00;
Bram Moolenaar362e1a32006-03-06 23:29:24 +00002077 }
2078 else
2079 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00002080 unicodebuf[wlen++] = c;
Bram Moolenaar362e1a32006-03-06 23:29:24 +00002081 }
2082 cw = utf_char2cells(c);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002083 if (cw > 2) /* don't use 4 for unprintable char */
2084 cw = 1;
2085 if (unicodepdy != NULL)
2086 {
2087 /* Use unicodepdy to make characters fit as we expect, even
2088 * when the font uses different widths (e.g., bold character
2089 * is wider). */
2090 unicodepdy[clen] = cw * gui.char_width;
2091 }
2092 cells += cw;
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00002093 i += utfc_ptr2len_len(text + i, len - i);
Bram Moolenaarca003e12006-03-17 23:19:38 +00002094 ++clen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002095 }
2096 ExtTextOutW(s_hdc, TEXT_X(col), TEXT_Y(row),
Bram Moolenaarca003e12006-03-17 23:19:38 +00002097 foptions, pcliprect, unicodebuf, wlen, unicodepdy);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002098 len = cells; /* used for underlining */
2099 }
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002100 else if ((enc_codepage > 0 && (int)GetACP() != enc_codepage) || enc_latin9)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002101 {
2102 /* If we want to display codepage data, and the current CP is not the
2103 * ANSI one, we need to go via Unicode. */
2104 if (unicodebuf != NULL)
2105 {
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002106 if (enc_latin9)
2107 latin9_to_ucs(text, len, unicodebuf);
2108 else
2109 len = MultiByteToWideChar(enc_codepage,
Bram Moolenaar071d4272004-06-13 20:20:40 +00002110 MB_PRECOMPOSED,
2111 (char *)text, len,
2112 (LPWSTR)unicodebuf, unibuflen);
2113 if (len != 0)
Bram Moolenaar19a09a12005-03-04 23:39:37 +00002114 {
2115 /* Use unicodepdy to make characters fit as we expect, even
2116 * when the font uses different widths (e.g., bold character
2117 * is wider). */
2118 if (unicodepdy != NULL)
2119 {
2120 int i;
2121 int cw;
2122
2123 for (i = 0; i < len; ++i)
2124 {
2125 cw = utf_char2cells(unicodebuf[i]);
2126 if (cw > 2)
2127 cw = 1;
2128 unicodepdy[i] = cw * gui.char_width;
2129 }
2130 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002131 ExtTextOutW(s_hdc, TEXT_X(col), TEXT_Y(row),
Bram Moolenaar19a09a12005-03-04 23:39:37 +00002132 foptions, pcliprect, unicodebuf, len, unicodepdy);
2133 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002134 }
2135 }
2136 else
2137#endif
2138 {
2139#ifdef FEAT_RIGHTLEFT
2140 /* If we can't use ETO_IGNORELANGUAGE, we can't tell Windows not to
2141 * mess up RL text, so we have to draw it character-by-character.
2142 * Only do this if RL is on, since it's slow. */
2143 if (curwin->w_p_rl && !(foptions & ETO_IGNORELANGUAGE))
2144 RevOut(s_hdc, TEXT_X(col), TEXT_Y(row),
2145 foptions, pcliprect, (char *)text, len, padding);
2146 else
2147#endif
2148 ExtTextOut(s_hdc, TEXT_X(col), TEXT_Y(row),
2149 foptions, pcliprect, (char *)text, len, padding);
2150 }
2151
Bram Moolenaare2cc9702005-03-15 22:43:58 +00002152 /* Underline */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002153 if (flags & DRAW_UNDERL)
2154 {
2155 hpen = CreatePen(PS_SOLID, 1, gui.currFgColor);
2156 old_pen = SelectObject(s_hdc, hpen);
2157 /* When p_linespace is 0, overwrite the bottom row of pixels.
2158 * Otherwise put the line just below the character. */
2159 y = FILL_Y(row + 1) - 1;
2160#ifndef MSWIN16_FASTTEXT
2161 if (p_linespace > 1)
2162 y -= p_linespace - 1;
2163#endif
2164 MoveToEx(s_hdc, FILL_X(col), y, NULL);
2165 /* Note: LineTo() excludes the last pixel in the line. */
2166 LineTo(s_hdc, FILL_X(col + len), y);
2167 DeleteObject(SelectObject(s_hdc, old_pen));
2168 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00002169
2170 /* Undercurl */
2171 if (flags & DRAW_UNDERC)
2172 {
2173 int x;
2174 int offset;
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +00002175 static const int val[8] = {1, 0, 0, 0, 1, 2, 2, 2 };
Bram Moolenaare2cc9702005-03-15 22:43:58 +00002176
2177 y = FILL_Y(row + 1) - 1;
2178 for (x = FILL_X(col); x < FILL_X(col + len); ++x)
2179 {
2180 offset = val[x % 8];
2181 SetPixel(s_hdc, x, y - offset, gui.currSpColor);
2182 }
2183 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002184}
2185
2186
2187/*
2188 * Output routines.
2189 */
2190
2191/* Flush any output to the screen */
2192 void
2193gui_mch_flush(void)
2194{
2195# if defined(__BORLANDC__)
2196 /*
2197 * The GdiFlush declaration (in Borland C 5.01 <wingdi.h>) is not a
2198 * prototype declaration.
2199 * The compiler complains if __stdcall is not used in both declarations.
2200 */
2201 BOOL __stdcall GdiFlush(void);
2202# endif
2203
2204 GdiFlush();
2205}
2206
2207 static void
2208clear_rect(RECT *rcp)
2209{
2210 HBRUSH hbr;
2211
2212 hbr = CreateSolidBrush(gui.back_pixel);
2213 FillRect(s_hdc, rcp, hbr);
2214 DeleteBrush(hbr);
2215}
2216
2217
Bram Moolenaarc716c302006-01-21 22:12:51 +00002218 void
2219gui_mch_get_screen_dimensions(int *screen_w, int *screen_h)
2220{
2221 RECT workarea_rect;
2222
2223 get_work_area(&workarea_rect);
2224
2225 *screen_w = workarea_rect.right
2226 - GetSystemMetrics(SM_CXFRAME) * 2;
2227
2228 /* FIXME: dirty trick: Because the gui_get_base_height() doesn't include
2229 * the menubar for MSwin, we subtract it from the screen height, so that
2230 * the window size can be made to fit on the screen. */
2231 *screen_h = workarea_rect.bottom
2232 - GetSystemMetrics(SM_CYFRAME) * 2
2233 - GetSystemMetrics(SM_CYCAPTION)
2234#ifdef FEAT_MENU
2235 - gui_mswin_get_menu_height(FALSE)
2236#endif
2237 ;
2238}
2239
2240
Bram Moolenaar071d4272004-06-13 20:20:40 +00002241#if defined(FEAT_MENU) || defined(PROTO)
2242/*
2243 * Add a sub menu to the menu bar.
2244 */
2245 void
2246gui_mch_add_menu(
2247 vimmenu_T *menu,
2248 int pos)
2249{
2250 vimmenu_T *parent = menu->parent;
2251
2252 menu->submenu_id = CreatePopupMenu();
2253 menu->id = s_menu_id++;
2254
2255 if (menu_is_menubar(menu->name))
2256 {
2257 if (is_winnt_3())
2258 {
2259 InsertMenu((parent == NULL) ? s_menuBar : parent->submenu_id,
2260 (UINT)pos, MF_POPUP | MF_STRING | MF_BYPOSITION,
2261 (UINT)menu->submenu_id, (LPCTSTR) menu->name);
2262 }
2263 else
2264 {
2265#ifdef FEAT_MBYTE
2266 WCHAR *wn = NULL;
2267 int n;
2268
2269 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
2270 {
2271 /* 'encoding' differs from active codepage: convert menu name
2272 * and use wide function */
2273 wn = enc_to_ucs2(menu->name, NULL);
2274 if (wn != NULL)
2275 {
2276 MENUITEMINFOW infow;
2277
2278 infow.cbSize = sizeof(infow);
2279 infow.fMask = MIIM_DATA | MIIM_TYPE | MIIM_ID
2280 | MIIM_SUBMENU;
2281 infow.dwItemData = (DWORD)menu;
2282 infow.wID = menu->id;
2283 infow.fType = MFT_STRING;
2284 infow.dwTypeData = wn;
2285 infow.cch = wcslen(wn);
2286 infow.hSubMenu = menu->submenu_id;
2287 n = InsertMenuItemW((parent == NULL)
2288 ? s_menuBar : parent->submenu_id,
2289 (UINT)pos, TRUE, &infow);
2290 vim_free(wn);
2291 if (n == 0 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
2292 /* Failed, try using non-wide function. */
2293 wn = NULL;
2294 }
2295 }
2296
2297 if (wn == NULL)
2298#endif
2299 {
2300 MENUITEMINFO info;
2301
2302 info.cbSize = sizeof(info);
2303 info.fMask = MIIM_DATA | MIIM_TYPE | MIIM_ID | MIIM_SUBMENU;
2304 info.dwItemData = (DWORD)menu;
2305 info.wID = menu->id;
2306 info.fType = MFT_STRING;
2307 info.dwTypeData = (LPTSTR)menu->name;
2308 info.cch = (UINT)STRLEN(menu->name);
2309 info.hSubMenu = menu->submenu_id;
2310 InsertMenuItem((parent == NULL)
2311 ? s_menuBar : parent->submenu_id,
2312 (UINT)pos, TRUE, &info);
2313 }
2314 }
2315 }
2316
2317 /* Fix window size if menu may have wrapped */
2318 if (parent == NULL)
2319 gui_mswin_get_menu_height(!gui.starting);
2320#ifdef FEAT_TEAROFF
2321 else if (IsWindow(parent->tearoff_handle))
2322 rebuild_tearoff(parent);
2323#endif
2324}
2325
2326 void
2327gui_mch_show_popupmenu(vimmenu_T *menu)
2328{
2329 POINT mp;
2330
2331 (void)GetCursorPos((LPPOINT)&mp);
2332 gui_mch_show_popupmenu_at(menu, (int)mp.x, (int)mp.y);
2333}
2334
2335 void
Bram Moolenaar045e82d2005-07-08 22:25:33 +00002336gui_make_popup(char_u *path_name, int mouse_pos)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002337{
2338 vimmenu_T *menu = gui_find_menu(path_name);
2339
2340 if (menu != NULL)
2341 {
2342 POINT p;
2343
2344 /* Find the position of the current cursor */
2345 GetDCOrgEx(s_hdc, &p);
Bram Moolenaar045e82d2005-07-08 22:25:33 +00002346 if (mouse_pos)
2347 {
2348 int mx, my;
2349
2350 gui_mch_getmouse(&mx, &my);
2351 p.x += mx;
2352 p.y += my;
2353 }
2354 else if (curwin != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002355 {
2356 p.x += TEXT_X(W_WINCOL(curwin) + curwin->w_wcol + 1);
2357 p.y += TEXT_Y(W_WINROW(curwin) + curwin->w_wrow + 1);
2358 }
2359 msg_scroll = FALSE;
2360 gui_mch_show_popupmenu_at(menu, (int)p.x, (int)p.y);
2361 }
2362}
2363
2364#if defined(FEAT_TEAROFF) || defined(PROTO)
2365/*
2366 * Given a menu descriptor, e.g. "File.New", find it in the menu hierarchy and
2367 * create it as a pseudo-"tearoff menu".
2368 */
2369 void
2370gui_make_tearoff(char_u *path_name)
2371{
2372 vimmenu_T *menu = gui_find_menu(path_name);
2373
2374 /* Found the menu, so tear it off. */
2375 if (menu != NULL)
2376 gui_mch_tearoff(menu->dname, menu, 0xffffL, 0xffffL);
2377}
2378#endif
2379
2380/*
2381 * Add a menu item to a menu
2382 */
2383 void
2384gui_mch_add_menu_item(
2385 vimmenu_T *menu,
2386 int idx)
2387{
2388 vimmenu_T *parent = menu->parent;
2389
2390 menu->id = s_menu_id++;
2391 menu->submenu_id = NULL;
2392
2393#ifdef FEAT_TEAROFF
2394 if (STRNCMP(menu->name, TEAR_STRING, TEAR_LEN) == 0)
2395 {
2396 InsertMenu(parent->submenu_id, (UINT)idx, MF_BITMAP|MF_BYPOSITION,
2397 (UINT)menu->id, (LPCTSTR) s_htearbitmap);
2398 }
2399 else
2400#endif
2401#ifdef FEAT_TOOLBAR
2402 if (menu_is_toolbar(parent->name))
2403 {
2404 TBBUTTON newtb;
2405
2406 vim_memset(&newtb, 0, sizeof(newtb));
2407 if (menu_is_separator(menu->name))
2408 {
2409 newtb.iBitmap = 0;
2410 newtb.fsStyle = TBSTYLE_SEP;
2411 }
2412 else
2413 {
2414 newtb.iBitmap = get_toolbar_bitmap(menu);
2415 newtb.fsStyle = TBSTYLE_BUTTON;
2416 }
2417 newtb.idCommand = menu->id;
2418 newtb.fsState = TBSTATE_ENABLED;
2419 newtb.iString = 0;
2420 SendMessage(s_toolbarhwnd, TB_INSERTBUTTON, (WPARAM)idx,
2421 (LPARAM)&newtb);
2422 menu->submenu_id = (HMENU)-1;
2423 }
2424 else
2425#endif
2426 {
2427#ifdef FEAT_MBYTE
2428 WCHAR *wn = NULL;
2429 int n;
2430
2431 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
2432 {
2433 /* 'encoding' differs from active codepage: convert menu item name
2434 * and use wide function */
2435 wn = enc_to_ucs2(menu->name, NULL);
2436 if (wn != NULL)
2437 {
2438 n = InsertMenuW(parent->submenu_id, (UINT)idx,
2439 (menu_is_separator(menu->name)
2440 ? MF_SEPARATOR : MF_STRING) | MF_BYPOSITION,
2441 (UINT)menu->id, wn);
2442 vim_free(wn);
2443 if (n == 0 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
2444 /* Failed, try using non-wide function. */
2445 wn = NULL;
2446 }
2447 }
2448 if (wn == NULL)
2449#endif
2450 InsertMenu(parent->submenu_id, (UINT)idx,
2451 (menu_is_separator(menu->name) ? MF_SEPARATOR : MF_STRING)
2452 | MF_BYPOSITION,
2453 (UINT)menu->id, (LPCTSTR)menu->name);
2454#ifdef FEAT_TEAROFF
2455 if (IsWindow(parent->tearoff_handle))
2456 rebuild_tearoff(parent);
2457#endif
2458 }
2459}
2460
2461/*
2462 * Destroy the machine specific menu widget.
2463 */
2464 void
2465gui_mch_destroy_menu(vimmenu_T *menu)
2466{
2467#ifdef FEAT_TOOLBAR
2468 /*
2469 * is this a toolbar button?
2470 */
2471 if (menu->submenu_id == (HMENU)-1)
2472 {
2473 int iButton;
2474
2475 iButton = (int)SendMessage(s_toolbarhwnd, TB_COMMANDTOINDEX,
2476 (WPARAM)menu->id, 0);
2477 SendMessage(s_toolbarhwnd, TB_DELETEBUTTON, (WPARAM)iButton, 0);
2478 }
2479 else
2480#endif
2481 {
2482 if (menu->parent != NULL
2483 && menu_is_popup(menu->parent->dname)
2484 && menu->parent->submenu_id != NULL)
2485 RemoveMenu(menu->parent->submenu_id, menu->id, MF_BYCOMMAND);
2486 else
2487 RemoveMenu(s_menuBar, menu->id, MF_BYCOMMAND);
2488 if (menu->submenu_id != NULL)
2489 DestroyMenu(menu->submenu_id);
2490#ifdef FEAT_TEAROFF
2491 if (IsWindow(menu->tearoff_handle))
2492 DestroyWindow(menu->tearoff_handle);
2493 if (menu->parent != NULL
2494 && menu->parent->children != NULL
2495 && IsWindow(menu->parent->tearoff_handle))
2496 {
2497 /* This menu must not show up when rebuilding the tearoff window. */
2498 menu->modes = 0;
2499 rebuild_tearoff(menu->parent);
2500 }
2501#endif
2502 }
2503}
2504
2505#ifdef FEAT_TEAROFF
2506 static void
2507rebuild_tearoff(vimmenu_T *menu)
2508{
2509 /*hackish*/
2510 char_u tbuf[128];
2511 RECT trect;
2512 RECT rct;
2513 RECT roct;
2514 int x, y;
2515
2516 HWND thwnd = menu->tearoff_handle;
2517
2518 GetWindowText(thwnd, tbuf, 127);
2519 if (GetWindowRect(thwnd, &trect)
2520 && GetWindowRect(s_hwnd, &rct)
2521 && GetClientRect(s_hwnd, &roct))
2522 {
2523 x = trect.left - rct.left;
2524 y = (trect.top - rct.bottom + roct.bottom);
2525 }
2526 else
2527 {
2528 x = y = 0xffffL;
2529 }
2530 DestroyWindow(thwnd);
2531 if (menu->children != NULL)
2532 {
2533 gui_mch_tearoff(tbuf, menu, x, y);
2534 if (IsWindow(menu->tearoff_handle))
2535 (void) SetWindowPos(menu->tearoff_handle,
2536 NULL,
2537 (int)trect.left,
2538 (int)trect.top,
2539 0, 0,
2540 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
2541 }
2542}
2543#endif /* FEAT_TEAROFF */
2544
2545/*
2546 * Make a menu either grey or not grey.
2547 */
2548 void
2549gui_mch_menu_grey(
2550 vimmenu_T *menu,
2551 int grey)
2552{
2553#ifdef FEAT_TOOLBAR
2554 /*
2555 * is this a toolbar button?
2556 */
2557 if (menu->submenu_id == (HMENU)-1)
2558 {
2559 SendMessage(s_toolbarhwnd, TB_ENABLEBUTTON,
2560 (WPARAM)menu->id, (LPARAM) MAKELONG((grey ? FALSE : TRUE), 0) );
2561 }
2562 else
2563#endif
2564 if (grey)
2565 EnableMenuItem(s_menuBar, menu->id, MF_BYCOMMAND | MF_GRAYED);
2566 else
2567 EnableMenuItem(s_menuBar, menu->id, MF_BYCOMMAND | MF_ENABLED);
2568
2569#ifdef FEAT_TEAROFF
2570 if ((menu->parent != NULL) && (IsWindow(menu->parent->tearoff_handle)))
2571 {
2572 WORD menuID;
2573 HWND menuHandle;
2574
2575 /*
2576 * A tearoff button has changed state.
2577 */
2578 if (menu->children == NULL)
2579 menuID = (WORD)(menu->id);
2580 else
2581 menuID = (WORD)((DWORD)(menu->submenu_id) | (DWORD)0x8000);
2582 menuHandle = GetDlgItem(menu->parent->tearoff_handle, menuID);
2583 if (menuHandle)
2584 EnableWindow(menuHandle, !grey);
2585
2586 }
2587#endif
2588}
2589
2590#endif /* FEAT_MENU */
2591
2592
2593/* define some macros used to make the dialogue creation more readable */
2594
2595#define add_string(s) strcpy((LPSTR)p, s); (LPSTR)p += (strlen((LPSTR)p) + 1)
2596#define add_word(x) *p++ = (x)
Bram Moolenaarf4cd3e82005-12-22 22:47:02 +00002597#define add_long(x) dwp = (DWORD *)p; *dwp++ = (x); p = (WORD *)dwp
Bram Moolenaar071d4272004-06-13 20:20:40 +00002598
2599#if defined(FEAT_GUI_DIALOG) || defined(PROTO)
2600/*
2601 * stuff for dialogs
2602 */
2603
2604/*
2605 * The callback routine used by all the dialogs. Very simple. First,
2606 * acknowledges the INITDIALOG message so that Windows knows to do standard
2607 * dialog stuff (Return = default, Esc = cancel....) Second, if a button is
2608 * pressed, return that button's ID - IDCANCEL (2), which is the button's
2609 * number.
2610 */
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +00002611/*ARGSUSED*/
Bram Moolenaar071d4272004-06-13 20:20:40 +00002612 static LRESULT CALLBACK
2613dialog_callback(
2614 HWND hwnd,
2615 UINT message,
2616 WPARAM wParam,
2617 LPARAM lParam)
2618{
2619 if (message == WM_INITDIALOG)
2620 {
2621 CenterWindow(hwnd, GetWindow(hwnd, GW_OWNER));
2622 /* Set focus to the dialog. Set the default button, if specified. */
2623 (void)SetFocus(hwnd);
2624 if (dialog_default_button > IDCANCEL)
2625 (void)SetFocus(GetDlgItem(hwnd, dialog_default_button));
2626 return FALSE;
2627 }
2628
2629 if (message == WM_COMMAND)
2630 {
2631 int button = LOWORD(wParam);
2632
2633 /* Don't end the dialog if something was selected that was
2634 * not a button.
2635 */
2636 if (button >= DLG_NONBUTTON_CONTROL)
2637 return TRUE;
2638
2639 /* If the edit box exists, copy the string. */
2640 if (s_textfield != NULL)
2641 GetDlgItemText(hwnd, DLG_NONBUTTON_CONTROL + 2,
2642 s_textfield, IOSIZE);
2643
2644 /*
2645 * Need to check for IDOK because if the user just hits Return to
2646 * accept the default value, some reason this is what we get.
2647 */
2648 if (button == IDOK)
2649 {
2650 if (dialog_default_button > IDCANCEL)
2651 EndDialog(hwnd, dialog_default_button);
2652 }
2653 else
2654 EndDialog(hwnd, button - IDCANCEL);
2655 return TRUE;
2656 }
2657
2658 if ((message == WM_SYSCOMMAND) && (wParam == SC_CLOSE))
2659 {
2660 EndDialog(hwnd, 0);
2661 return TRUE;
2662 }
2663 return FALSE;
2664}
2665
2666/*
2667 * Create a dialog dynamically from the parameter strings.
2668 * type = type of dialog (question, alert, etc.)
2669 * title = dialog title. may be NULL for default title.
2670 * message = text to display. Dialog sizes to accommodate it.
2671 * buttons = '\n' separated list of button captions, default first.
2672 * dfltbutton = number of default button.
2673 *
2674 * This routine returns 1 if the first button is pressed,
2675 * 2 for the second, etc.
2676 *
2677 * 0 indicates Esc was pressed.
2678 * -1 for unexpected error
2679 *
2680 * If stubbing out this fn, return 1.
2681 */
2682
2683static const char_u *dlg_icons[] = /* must match names in resource file */
2684{
2685 "IDR_VIM",
2686 "IDR_VIM_ERROR",
2687 "IDR_VIM_ALERT",
2688 "IDR_VIM_INFO",
2689 "IDR_VIM_QUESTION"
2690};
2691
2692#ifdef USE_SYSMENU_FONT
2693/*
2694 * Get Menu Font.
2695 * Return OK or FAIL.
2696 */
2697 static int
2698gui_w32_get_menu_font(LOGFONT *lf)
2699{
2700 NONCLIENTMETRICS nm;
2701
2702 nm.cbSize = sizeof(NONCLIENTMETRICS);
2703 if (!SystemParametersInfo(
2704 SPI_GETNONCLIENTMETRICS,
2705 sizeof(NONCLIENTMETRICS),
2706 &nm,
2707 0))
2708 return FAIL;
2709 *lf = nm.lfMenuFont;
2710 return OK;
2711}
2712#endif
2713
2714 int
2715gui_mch_dialog(
2716 int type,
2717 char_u *title,
2718 char_u *message,
2719 char_u *buttons,
2720 int dfltbutton,
2721 char_u *textfield)
2722{
2723 WORD *p, *pdlgtemplate, *pnumitems;
Bram Moolenaarf4cd3e82005-12-22 22:47:02 +00002724 DWORD *dwp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002725 int numButtons;
2726 int *buttonWidths, *buttonPositions;
2727 int buttonYpos;
2728 int nchar, i;
2729 DWORD lStyle;
2730 int dlgwidth = 0;
2731 int dlgheight;
2732 int editboxheight;
2733 int horizWidth = 0;
2734 int msgheight;
2735 char_u *pstart;
2736 char_u *pend;
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00002737 char_u *last_white;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002738 char_u *tbuffer;
2739 RECT rect;
2740 HWND hwnd;
2741 HDC hdc;
2742 HFONT font, oldFont;
2743 TEXTMETRIC fontInfo;
2744 int fontHeight;
2745 int textWidth, minButtonWidth, messageWidth;
2746 int maxDialogWidth;
Bram Moolenaar748bf032005-02-02 23:04:36 +00002747 int maxDialogHeight;
2748 int scroll_flag = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002749 int vertical;
2750 int dlgPaddingX;
2751 int dlgPaddingY;
2752#ifdef USE_SYSMENU_FONT
2753 LOGFONT lfSysmenu;
2754 int use_lfSysmenu = FALSE;
2755#endif
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00002756 garray_T ga;
2757 int l;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002758
2759#ifndef NO_CONSOLE
2760 /* Don't output anything in silent mode ("ex -s") */
2761 if (silent_mode)
2762 return dfltbutton; /* return default option */
2763#endif
2764
Bram Moolenaar748bf032005-02-02 23:04:36 +00002765#if 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00002766 /* If there is no window yet, open it. */
2767 if (s_hwnd == NULL && gui_mch_init() == FAIL)
2768 return dfltbutton;
Bram Moolenaar748bf032005-02-02 23:04:36 +00002769#else
2770 if (s_hwnd == NULL)
2771 get_dialog_font_metrics();
2772#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002773
2774 if ((type < 0) || (type > VIM_LAST_TYPE))
2775 type = 0;
2776
2777 /* allocate some memory for dialog template */
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002778 /* TODO should compute this really */
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00002779 pdlgtemplate = p = (PWORD)LocalAlloc(LPTR,
2780 DLG_ALLOC_SIZE + STRLEN(message));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002781
2782 if (p == NULL)
2783 return -1;
2784
2785 /*
2786 * make a copy of 'buttons' to fiddle with it. complier grizzles because
2787 * vim_strsave() doesn't take a const arg (why not?), so cast away the
2788 * const.
2789 */
2790 tbuffer = vim_strsave(buttons);
2791 if (tbuffer == NULL)
2792 return -1;
2793
2794 --dfltbutton; /* Change from one-based to zero-based */
2795
2796 /* Count buttons */
2797 numButtons = 1;
2798 for (i = 0; tbuffer[i] != '\0'; i++)
2799 {
2800 if (tbuffer[i] == DLG_BUTTON_SEP)
2801 numButtons++;
2802 }
2803 if (dfltbutton >= numButtons)
2804 dfltbutton = -1;
2805
2806 /* Allocate array to hold the width of each button */
Bram Moolenaarc54b8a72005-09-30 21:20:29 +00002807 buttonWidths = (int *)lalloc(numButtons * sizeof(int), TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002808 if (buttonWidths == NULL)
2809 return -1;
2810
2811 /* Allocate array to hold the X position of each button */
Bram Moolenaarc54b8a72005-09-30 21:20:29 +00002812 buttonPositions = (int *)lalloc(numButtons * sizeof(int), TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002813 if (buttonPositions == NULL)
2814 return -1;
2815
2816 /*
2817 * Calculate how big the dialog must be.
2818 */
2819 hwnd = GetDesktopWindow();
2820 hdc = GetWindowDC(hwnd);
2821#ifdef USE_SYSMENU_FONT
2822 if (gui_w32_get_menu_font(&lfSysmenu) == OK)
2823 {
2824 font = CreateFontIndirect(&lfSysmenu);
2825 use_lfSysmenu = TRUE;
2826 }
2827 else
2828#endif
2829 font = CreateFont(-DLG_FONT_POINT_SIZE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2830 VARIABLE_PITCH , DLG_FONT_NAME);
2831 if (s_usenewlook)
2832 {
2833 oldFont = SelectFont(hdc, font);
2834 dlgPaddingX = DLG_PADDING_X;
2835 dlgPaddingY = DLG_PADDING_Y;
2836 }
2837 else
2838 {
2839 oldFont = SelectFont(hdc, GetStockObject(SYSTEM_FONT));
2840 dlgPaddingX = DLG_OLD_STYLE_PADDING_X;
2841 dlgPaddingY = DLG_OLD_STYLE_PADDING_Y;
2842 }
2843 GetTextMetrics(hdc, &fontInfo);
2844 fontHeight = fontInfo.tmHeight;
2845
2846 /* Minimum width for horizontal button */
2847 minButtonWidth = GetTextWidth(hdc, "Cancel", 6);
2848
2849 /* Maximum width of a dialog, if possible */
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00002850 if (s_hwnd == NULL)
2851 {
2852 RECT workarea_rect;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002853
Bram Moolenaarc716c302006-01-21 22:12:51 +00002854 /* We don't have a window, use the desktop area. */
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00002855 get_work_area(&workarea_rect);
2856 maxDialogWidth = workarea_rect.right - workarea_rect.left - 100;
2857 if (maxDialogWidth > 600)
2858 maxDialogWidth = 600;
2859 maxDialogHeight = workarea_rect.bottom - workarea_rect.top - 100;
2860 }
2861 else
2862 {
2863 /* Use our own window for the size, unless it's very small. */
2864 GetWindowRect(s_hwnd, &rect);
2865 maxDialogWidth = rect.right - rect.left
2866 - GetSystemMetrics(SM_CXFRAME) * 2;
2867 if (maxDialogWidth < DLG_MIN_MAX_WIDTH)
2868 maxDialogWidth = DLG_MIN_MAX_WIDTH;
Bram Moolenaar748bf032005-02-02 23:04:36 +00002869
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00002870 maxDialogHeight = rect.bottom - rect.top
2871 - GetSystemMetrics(SM_CXFRAME) * 2;
2872 if (maxDialogHeight < DLG_MIN_MAX_HEIGHT)
2873 maxDialogHeight = DLG_MIN_MAX_HEIGHT;
2874 }
2875
2876 /* Set dlgwidth to width of message.
2877 * Copy the message into "ga", changing NL to CR-NL and inserting line
2878 * breaks where needed. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002879 pstart = message;
2880 messageWidth = 0;
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00002881 msgheight = 0;
2882 ga_init2(&ga, sizeof(char), 500);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002883 do
2884 {
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00002885 msgheight += fontHeight; /* at least one line */
2886
2887 /* Need to figure out where to break the string. The system does it
2888 * at a word boundary, which would mean we can't compute the number of
2889 * wrapped lines. */
2890 textWidth = 0;
2891 last_white = NULL;
2892 for (pend = pstart; *pend != NUL && *pend != '\n'; )
Bram Moolenaar748bf032005-02-02 23:04:36 +00002893 {
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00002894#ifdef FEAT_MBYTE
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00002895 l = (*mb_ptr2len)(pend);
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00002896#else
2897 l = 1;
2898#endif
2899 if (l == 1 && vim_iswhite(*pend)
2900 && textWidth > maxDialogWidth * 3 / 4)
2901 last_white = pend;
2902 textWidth += GetTextWidth(hdc, pend, l);
2903 if (textWidth >= maxDialogWidth)
Bram Moolenaar748bf032005-02-02 23:04:36 +00002904 {
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00002905 /* Line will wrap. */
2906 messageWidth = maxDialogWidth;
Bram Moolenaar748bf032005-02-02 23:04:36 +00002907 msgheight += fontHeight;
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00002908 textWidth = 0;
2909
2910 if (last_white != NULL)
2911 {
2912 /* break the line just after a space */
2913 ga.ga_len -= pend - (last_white + 1);
2914 pend = last_white + 1;
2915 last_white = NULL;
2916 }
2917 ga_append(&ga, '\r');
2918 ga_append(&ga, '\n');
2919 continue;
Bram Moolenaar748bf032005-02-02 23:04:36 +00002920 }
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00002921
2922 while (--l >= 0)
2923 ga_append(&ga, *pend++);
Bram Moolenaar748bf032005-02-02 23:04:36 +00002924 }
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00002925 if (textWidth > messageWidth)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002926 messageWidth = textWidth;
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00002927
2928 ga_append(&ga, '\r');
2929 ga_append(&ga, '\n');
Bram Moolenaar071d4272004-06-13 20:20:40 +00002930 pstart = pend + 1;
2931 } while (*pend != NUL);
Bram Moolenaar748bf032005-02-02 23:04:36 +00002932
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00002933 if (ga.ga_data != NULL)
2934 message = ga.ga_data;
2935
Bram Moolenaar748bf032005-02-02 23:04:36 +00002936 messageWidth += 10; /* roundoff space */
2937
2938 /* Restrict the size to a maximum. Causes a scrollbar to show up. */
2939 if (msgheight > maxDialogHeight)
2940 {
2941 msgheight = maxDialogHeight;
2942 scroll_flag = WS_VSCROLL;
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00002943 messageWidth += GetSystemMetrics(SM_CXVSCROLL);
Bram Moolenaar748bf032005-02-02 23:04:36 +00002944 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002945
2946 /* Add width of icon to dlgwidth, and some space */
Bram Moolenaar748bf032005-02-02 23:04:36 +00002947 dlgwidth = messageWidth + DLG_ICON_WIDTH + 3 * dlgPaddingX;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002948
2949 if (msgheight < DLG_ICON_HEIGHT)
2950 msgheight = DLG_ICON_HEIGHT;
2951
2952 /*
2953 * Check button names. A long one will make the dialog wider.
2954 */
2955 vertical = (vim_strchr(p_go, GO_VERTICAL) != NULL);
2956 if (!vertical)
2957 {
2958 // Place buttons horizontally if they fit.
2959 horizWidth = dlgPaddingX;
2960 pstart = tbuffer;
2961 i = 0;
2962 do
2963 {
2964 pend = vim_strchr(pstart, DLG_BUTTON_SEP);
2965 if (pend == NULL)
2966 pend = pstart + STRLEN(pstart); // Last button name.
2967 textWidth = GetTextWidth(hdc, pstart, (int)(pend - pstart));
2968 if (textWidth < minButtonWidth)
2969 textWidth = minButtonWidth;
2970 textWidth += dlgPaddingX; /* Padding within button */
2971 buttonWidths[i] = textWidth;
2972 buttonPositions[i++] = horizWidth;
2973 horizWidth += textWidth + dlgPaddingX; /* Pad between buttons */
2974 pstart = pend + 1;
2975 } while (*pend != NUL);
2976
2977 if (horizWidth > maxDialogWidth)
2978 vertical = TRUE; // Too wide to fit on the screen.
2979 else if (horizWidth > dlgwidth)
2980 dlgwidth = horizWidth;
2981 }
2982
2983 if (vertical)
2984 {
2985 // Stack buttons vertically.
2986 pstart = tbuffer;
2987 do
2988 {
2989 pend = vim_strchr(pstart, DLG_BUTTON_SEP);
2990 if (pend == NULL)
2991 pend = pstart + STRLEN(pstart); // Last button name.
2992 textWidth = GetTextWidth(hdc, pstart, (int)(pend - pstart));
2993 textWidth += dlgPaddingX; /* Padding within button */
2994 textWidth += DLG_VERT_PADDING_X * 2; /* Padding around button */
2995 if (textWidth > dlgwidth)
2996 dlgwidth = textWidth;
2997 pstart = pend + 1;
2998 } while (*pend != NUL);
2999 }
3000
3001 if (dlgwidth < DLG_MIN_WIDTH)
3002 dlgwidth = DLG_MIN_WIDTH; /* Don't allow a really thin dialog!*/
3003
3004 /* start to fill in the dlgtemplate information. addressing by WORDs */
3005 if (s_usenewlook)
3006 lStyle = DS_MODALFRAME | WS_CAPTION |DS_3DLOOK| WS_VISIBLE |DS_SETFONT;
3007 else
3008 lStyle = DS_MODALFRAME | WS_CAPTION |DS_3DLOOK| WS_VISIBLE;
3009
3010 add_long(lStyle);
3011 add_long(0); // (lExtendedStyle)
3012 pnumitems = p; /*save where the number of items must be stored*/
3013 add_word(0); // NumberOfItems(will change later)
3014 add_word(10); // x
3015 add_word(10); // y
3016 add_word(PixelToDialogX(dlgwidth)); // cx
3017
3018 // Dialog height.
3019 if (vertical)
3020 dlgheight = msgheight + 2 * dlgPaddingY +
3021 DLG_VERT_PADDING_Y + 2 * fontHeight * numButtons;
3022 else
3023 dlgheight = msgheight + 3 * dlgPaddingY + 2 * fontHeight;
3024
3025 // Dialog needs to be taller if contains an edit box.
3026 editboxheight = fontHeight + dlgPaddingY + 4 * DLG_VERT_PADDING_Y;
3027 if (textfield != NULL)
3028 dlgheight += editboxheight;
3029
3030 add_word(PixelToDialogY(dlgheight));
3031
3032 add_word(0); // Menu
3033 add_word(0); // Class
3034
3035 /* copy the title of the dialog */
3036 nchar = nCopyAnsiToWideChar(p, (title ?
3037 (LPSTR)title :
3038 (LPSTR)("Vim "VIM_VERSION_MEDIUM)));
3039 p += nchar;
3040
3041 if (s_usenewlook)
3042 {
3043 /* do the font, since DS_3DLOOK doesn't work properly */
3044#ifdef USE_SYSMENU_FONT
3045 if (use_lfSysmenu)
3046 {
3047 /* point size */
3048 *p++ = -MulDiv(lfSysmenu.lfHeight, 72,
3049 GetDeviceCaps(hdc, LOGPIXELSY));
3050 nchar = nCopyAnsiToWideChar(p, TEXT(lfSysmenu.lfFaceName));
3051 }
3052 else
3053#endif
3054 {
3055 *p++ = DLG_FONT_POINT_SIZE; // point size
3056 nchar = nCopyAnsiToWideChar(p, TEXT(DLG_FONT_NAME));
3057 }
3058 p += nchar;
3059 }
3060
3061 buttonYpos = msgheight + 2 * dlgPaddingY;
3062
3063 if (textfield != NULL)
3064 buttonYpos += editboxheight;
3065
3066 pstart = tbuffer;
3067 if (!vertical)
3068 horizWidth = (dlgwidth - horizWidth) / 2; /* Now it's X offset */
3069 for (i = 0; i < numButtons; i++)
3070 {
3071 /* get end of this button. */
3072 for ( pend = pstart;
3073 *pend && (*pend != DLG_BUTTON_SEP);
3074 pend++)
3075 ;
3076
3077 if (*pend)
3078 *pend = '\0';
3079
3080 /*
3081 * old NOTE:
3082 * setting the BS_DEFPUSHBUTTON style doesn't work because Windows sets
3083 * the focus to the first tab-able button and in so doing makes that
3084 * the default!! Grrr. Workaround: Make the default button the only
3085 * one with WS_TABSTOP style. Means user can't tab between buttons, but
3086 * he/she can use arrow keys.
3087 *
3088 * new NOTE: BS_DEFPUSHBUTTON is required to be able to select the
3089 * right buttun when hitting <Enter>. E.g., for the ":confirm quit"
3090 * dialog. Also needed for when the textfield is the default control.
3091 * It appears to work now (perhaps not on Win95?).
3092 */
3093 if (vertical)
3094 {
3095 p = add_dialog_element(p,
3096 (i == dfltbutton
3097 ? BS_DEFPUSHBUTTON : BS_PUSHBUTTON) | WS_TABSTOP,
3098 PixelToDialogX(DLG_VERT_PADDING_X),
3099 PixelToDialogY(buttonYpos /* TBK */
3100 + 2 * fontHeight * i),
3101 PixelToDialogX(dlgwidth - 2 * DLG_VERT_PADDING_X),
3102 (WORD)(PixelToDialogY(2 * fontHeight) - 1),
3103 (WORD)(IDCANCEL + 1 + i), (WORD)0x0080, pstart);
3104 }
3105 else
3106 {
3107 p = add_dialog_element(p,
3108 (i == dfltbutton
3109 ? BS_DEFPUSHBUTTON : BS_PUSHBUTTON) | WS_TABSTOP,
3110 PixelToDialogX(horizWidth + buttonPositions[i]),
3111 PixelToDialogY(buttonYpos), /* TBK */
3112 PixelToDialogX(buttonWidths[i]),
3113 (WORD)(PixelToDialogY(2 * fontHeight) - 1),
3114 (WORD)(IDCANCEL + 1 + i), (WORD)0x0080, pstart);
3115 }
3116 pstart = pend + 1; /*next button*/
3117 }
3118 *pnumitems += numButtons;
3119
3120 /* Vim icon */
3121 p = add_dialog_element(p, SS_ICON,
3122 PixelToDialogX(dlgPaddingX),
3123 PixelToDialogY(dlgPaddingY),
3124 PixelToDialogX(DLG_ICON_WIDTH),
3125 PixelToDialogY(DLG_ICON_HEIGHT),
3126 DLG_NONBUTTON_CONTROL + 0, (WORD)0x0082,
3127 dlg_icons[type]);
3128
Bram Moolenaar748bf032005-02-02 23:04:36 +00003129#if 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00003130 /* Dialog message */
3131 p = add_dialog_element(p, SS_LEFT,
3132 PixelToDialogX(2 * dlgPaddingX + DLG_ICON_WIDTH),
3133 PixelToDialogY(dlgPaddingY),
3134 (WORD)(PixelToDialogX(messageWidth) + 1),
3135 PixelToDialogY(msgheight),
3136 DLG_NONBUTTON_CONTROL + 1, (WORD)0x0082, message);
Bram Moolenaar748bf032005-02-02 23:04:36 +00003137#else
3138 /* Dialog message */
3139 p = add_dialog_element(p, ES_LEFT|scroll_flag|ES_MULTILINE|ES_READONLY,
3140 PixelToDialogX(2 * dlgPaddingX + DLG_ICON_WIDTH),
3141 PixelToDialogY(dlgPaddingY),
3142 (WORD)(PixelToDialogX(messageWidth) + 1),
3143 PixelToDialogY(msgheight),
3144 DLG_NONBUTTON_CONTROL + 1, (WORD)0x0081, message);
3145#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003146
3147 /* Edit box */
3148 if (textfield != NULL)
3149 {
3150 p = add_dialog_element(p, ES_LEFT|ES_AUTOHSCROLL|WS_TABSTOP|WS_BORDER,
3151 PixelToDialogX(2 * dlgPaddingX),
3152 PixelToDialogY(2 * dlgPaddingY + msgheight),
3153 PixelToDialogX(dlgwidth - 4 * dlgPaddingX),
3154 PixelToDialogY(fontHeight + dlgPaddingY),
3155 DLG_NONBUTTON_CONTROL + 2, (WORD)0x0081, textfield);
3156 *pnumitems += 1;
3157 }
3158
3159 *pnumitems += 2;
3160
3161 SelectFont(hdc, oldFont);
3162 DeleteObject(font);
3163 ReleaseDC(hwnd, hdc);
3164
3165 /* Let the dialog_callback() function know which button to make default
3166 * If we have an edit box, make that the default. We also need to tell
3167 * dialog_callback() if this dialog contains an edit box or not. We do
3168 * this by setting s_textfield if it does.
3169 */
3170 if (textfield != NULL)
3171 {
3172 dialog_default_button = DLG_NONBUTTON_CONTROL + 2;
3173 s_textfield = textfield;
3174 }
3175 else
3176 {
3177 dialog_default_button = IDCANCEL + 1 + dfltbutton;
3178 s_textfield = NULL;
3179 }
3180
3181 /* show the dialog box modally and get a return value */
3182 nchar = (int)DialogBoxIndirect(
3183 s_hinst,
3184 (LPDLGTEMPLATE)pdlgtemplate,
3185 s_hwnd,
3186 (DLGPROC)dialog_callback);
3187
3188 LocalFree(LocalHandle(pdlgtemplate));
3189 vim_free(tbuffer);
3190 vim_free(buttonWidths);
3191 vim_free(buttonPositions);
Bram Moolenaar3a7c85b2005-02-05 21:39:53 +00003192 vim_free(ga.ga_data);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003193
3194 /* Focus back to our window (for when MDI is used). */
3195 (void)SetFocus(s_hwnd);
3196
3197 return nchar;
3198}
3199
3200#endif /* FEAT_GUI_DIALOG */
3201/*
3202 * Put a simple element (basic class) onto a dialog template in memory.
3203 * return a pointer to where the next item should be added.
3204 *
3205 * parameters:
3206 * lStyle = additional style flags
3207 * (be careful, NT3.51 & Win32s will ignore the new ones)
3208 * x,y = x & y positions IN DIALOG UNITS
3209 * w,h = width and height IN DIALOG UNITS
3210 * Id = ID used in messages
3211 * clss = class ID, e.g 0x0080 for a button, 0x0082 for a static
3212 * caption = usually text or resource name
3213 *
3214 * TODO: use the length information noted here to enable the dialog creation
3215 * routines to work out more exactly how much memory they need to alloc.
3216 */
3217 static PWORD
3218add_dialog_element(
3219 PWORD p,
3220 DWORD lStyle,
3221 WORD x,
3222 WORD y,
3223 WORD w,
3224 WORD h,
3225 WORD Id,
3226 WORD clss,
3227 const char *caption)
3228{
3229 int nchar;
3230
3231 p = lpwAlign(p); /* Align to dword boundary*/
3232 lStyle = lStyle | WS_VISIBLE | WS_CHILD;
3233 *p++ = LOWORD(lStyle);
3234 *p++ = HIWORD(lStyle);
3235 *p++ = 0; // LOWORD (lExtendedStyle)
3236 *p++ = 0; // HIWORD (lExtendedStyle)
3237 *p++ = x;
3238 *p++ = y;
3239 *p++ = w;
3240 *p++ = h;
3241 *p++ = Id; //9 or 10 words in all
3242
3243 *p++ = (WORD)0xffff;
3244 *p++ = clss; //2 more here
3245
3246 nchar = nCopyAnsiToWideChar(p, (LPSTR)caption); //strlen(caption)+1
3247 p += nchar;
3248
3249 *p++ = 0; // advance pointer over nExtraStuff WORD - 2 more
3250
3251 return p; //total = 15+ (strlen(caption)) words
3252 // = 30 + 2(strlen(caption) bytes reqd
3253}
3254
3255
3256/*
3257 * Helper routine. Take an input pointer, return closest pointer that is
3258 * aligned on a DWORD (4 byte) boundary. Taken from the Win32SDK samples.
3259 */
3260 static LPWORD
3261lpwAlign(
3262 LPWORD lpIn)
3263{
3264 ULONG ul;
3265
3266 ul = (ULONG)lpIn;
3267 ul += 3;
3268 ul >>= 2;
3269 ul <<= 2;
3270 return (LPWORD)ul;
3271}
3272
3273/*
3274 * Helper routine. Takes second parameter as Ansi string, copies it to first
3275 * parameter as wide character (16-bits / char) string, and returns integer
3276 * number of wide characters (words) in string (including the trailing wide
3277 * char NULL). Partly taken from the Win32SDK samples.
3278 */
3279 static int
3280nCopyAnsiToWideChar(
3281 LPWORD lpWCStr,
3282 LPSTR lpAnsiIn)
3283{
3284 int nChar = 0;
3285#ifdef FEAT_MBYTE
3286 int len = lstrlen(lpAnsiIn) + 1; /* include NUL character */
3287 int i;
3288 WCHAR *wn;
3289
3290 if (enc_codepage == 0 && (int)GetACP() != enc_codepage)
3291 {
3292 /* Not a codepage, use our own conversion function. */
3293 wn = enc_to_ucs2(lpAnsiIn, NULL);
3294 if (wn != NULL)
3295 {
3296 wcscpy(lpWCStr, wn);
3297 nChar = wcslen(wn) + 1;
3298 vim_free(wn);
3299 }
3300 }
3301 if (nChar == 0)
3302 /* Use Win32 conversion function. */
3303 nChar = MultiByteToWideChar(
3304 enc_codepage > 0 ? enc_codepage : CP_ACP,
3305 MB_PRECOMPOSED,
3306 lpAnsiIn, len,
3307 lpWCStr, len);
3308 for (i = 0; i < nChar; ++i)
3309 if (lpWCStr[i] == (WORD)'\t') /* replace tabs with spaces */
3310 lpWCStr[i] = (WORD)' ';
3311#else
3312 do
3313 {
3314 if (*lpAnsiIn == '\t')
3315 *lpWCStr++ = (WORD)' ';
3316 else
3317 *lpWCStr++ = (WORD)*lpAnsiIn;
3318 nChar++;
3319 } while (*lpAnsiIn++);
3320#endif
3321
3322 return nChar;
3323}
3324
3325
3326#ifdef FEAT_TEAROFF
3327/*
3328 * The callback function for all the modeless dialogs that make up the
3329 * "tearoff menus" Very simple - forward button presses (to fool Vim into
3330 * thinking its menus have been clicked), and go away when closed.
3331 */
3332 static LRESULT CALLBACK
3333tearoff_callback(
3334 HWND hwnd,
3335 UINT message,
3336 WPARAM wParam,
3337 LPARAM lParam)
3338{
3339 if (message == WM_INITDIALOG)
3340 return (TRUE);
3341
3342 /* May show the mouse pointer again. */
3343 HandleMouseHide(message, lParam);
3344
3345 if (message == WM_COMMAND)
3346 {
3347 if ((WORD)(LOWORD(wParam)) & 0x8000)
3348 {
3349 POINT mp;
3350 RECT rect;
3351
3352 if (GetCursorPos(&mp) && GetWindowRect(hwnd, &rect))
3353 {
3354 (void)TrackPopupMenu(
3355 (HMENU)(LOWORD(wParam) ^ 0x8000),
3356 TPM_LEFTALIGN | TPM_LEFTBUTTON,
3357 (int)rect.right - 8,
3358 (int)mp.y,
3359 (int)0, /*reserved param*/
3360 s_hwnd,
3361 NULL);
3362 /*
3363 * NOTE: The pop-up menu can eat the mouse up event.
3364 * We deal with this in normal.c.
3365 */
3366 }
3367 }
3368 else
3369 /* Pass on messages to the main Vim window */
3370 PostMessage(s_hwnd, WM_COMMAND, LOWORD(wParam), 0);
3371 /*
3372 * Give main window the focus back: this is so after
3373 * choosing a tearoff button you can start typing again
3374 * straight away.
3375 */
3376 (void)SetFocus(s_hwnd);
3377 return TRUE;
3378 }
3379 if ((message == WM_SYSCOMMAND) && (wParam == SC_CLOSE))
3380 {
3381 DestroyWindow(hwnd);
3382 return TRUE;
3383 }
3384
3385 /* When moved around, give main window the focus back. */
3386 if (message == WM_EXITSIZEMOVE)
3387 (void)SetActiveWindow(s_hwnd);
3388
3389 return FALSE;
3390}
3391#endif
3392
3393
3394/*
3395 * Decide whether to use the "new look" (small, non-bold font) or the "old
3396 * look" (big, clanky font) for dialogs, and work out a few values for use
3397 * later accordingly.
3398 */
3399 static void
3400get_dialog_font_metrics(void)
3401{
3402 HDC hdc;
3403 HFONT hfontTools = 0;
3404 DWORD dlgFontSize;
3405 SIZE size;
3406#ifdef USE_SYSMENU_FONT
3407 LOGFONT lfSysmenu;
3408#endif
3409
3410 s_usenewlook = FALSE;
3411
3412 /*
3413 * For NT3.51 and Win32s, we stick with the old look
3414 * because it matches everything else.
3415 */
3416 if (!is_winnt_3())
3417 {
3418#ifdef USE_SYSMENU_FONT
3419 if (gui_w32_get_menu_font(&lfSysmenu) == OK)
3420 hfontTools = CreateFontIndirect(&lfSysmenu);
3421 else
3422#endif
3423 hfontTools = CreateFont(-DLG_FONT_POINT_SIZE, 0, 0, 0, 0, 0, 0, 0,
3424 0, 0, 0, 0, VARIABLE_PITCH , DLG_FONT_NAME);
3425
3426 if (hfontTools)
3427 {
3428 hdc = GetDC(s_hwnd);
3429 SelectObject(hdc, hfontTools);
3430 /*
3431 * GetTextMetrics() doesn't return the right value in
3432 * tmAveCharWidth, so we have to figure out the dialog base units
3433 * ourselves.
3434 */
3435 GetTextExtentPoint(hdc,
3436 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
3437 52, &size);
3438 ReleaseDC(s_hwnd, hdc);
3439
3440 s_dlgfntwidth = (WORD)((size.cx / 26 + 1) / 2);
3441 s_dlgfntheight = (WORD)size.cy;
3442 s_usenewlook = TRUE;
3443 }
3444 }
3445
3446 if (!s_usenewlook)
3447 {
3448 dlgFontSize = GetDialogBaseUnits(); /* fall back to big old system*/
3449 s_dlgfntwidth = LOWORD(dlgFontSize);
3450 s_dlgfntheight = HIWORD(dlgFontSize);
3451 }
3452}
3453
3454#if defined(FEAT_MENU) && defined(FEAT_TEAROFF)
3455/*
3456 * Create a pseudo-"tearoff menu" based on the child
3457 * items of a given menu pointer.
3458 */
3459 static void
3460gui_mch_tearoff(
3461 char_u *title,
3462 vimmenu_T *menu,
3463 int initX,
3464 int initY)
3465{
3466 WORD *p, *pdlgtemplate, *pnumitems, *ptrueheight;
3467 int template_len;
3468 int nchar, textWidth, submenuWidth;
3469 DWORD lStyle;
3470 DWORD lExtendedStyle;
3471 WORD dlgwidth;
3472 WORD menuID;
3473 vimmenu_T *pmenu;
3474 vimmenu_T *the_menu = menu;
3475 HWND hwnd;
3476 HDC hdc;
3477 HFONT font, oldFont;
3478 int col, spaceWidth, len;
3479 int columnWidths[2];
3480 char_u *label, *text;
3481 int acLen = 0;
3482 int nameLen;
3483 int padding0, padding1, padding2 = 0;
3484 int sepPadding=0;
Bram Moolenaar9588a0f2005-01-08 21:45:39 +00003485 int x;
3486 int y;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003487#ifdef USE_SYSMENU_FONT
3488 LOGFONT lfSysmenu;
3489 int use_lfSysmenu = FALSE;
3490#endif
3491
3492 /*
3493 * If this menu is already torn off, move it to the mouse position.
3494 */
3495 if (IsWindow(menu->tearoff_handle))
3496 {
3497 POINT mp;
3498 if (GetCursorPos((LPPOINT)&mp))
3499 {
3500 SetWindowPos(menu->tearoff_handle, NULL, mp.x, mp.y, 0, 0,
3501 SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
3502 }
3503 return;
3504 }
3505
3506 /*
3507 * Create a new tearoff.
3508 */
3509 if (*title == MNU_HIDDEN_CHAR)
3510 title++;
3511
3512 /* Allocate memory to store the dialog template. It's made bigger when
3513 * needed. */
3514 template_len = DLG_ALLOC_SIZE;
3515 pdlgtemplate = p = (WORD *)LocalAlloc(LPTR, template_len);
3516 if (p == NULL)
3517 return;
3518
3519 hwnd = GetDesktopWindow();
3520 hdc = GetWindowDC(hwnd);
3521#ifdef USE_SYSMENU_FONT
3522 if (gui_w32_get_menu_font(&lfSysmenu) == OK)
3523 {
3524 font = CreateFontIndirect(&lfSysmenu);
3525 use_lfSysmenu = TRUE;
3526 }
3527 else
3528#endif
3529 font = CreateFont(-DLG_FONT_POINT_SIZE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3530 VARIABLE_PITCH , DLG_FONT_NAME);
3531 if (s_usenewlook)
3532 oldFont = SelectFont(hdc, font);
3533 else
3534 oldFont = SelectFont(hdc, GetStockObject(SYSTEM_FONT));
3535
3536 /* Calculate width of a single space. Used for padding columns to the
3537 * right width. */
3538 spaceWidth = GetTextWidth(hdc, " ", 1);
3539
3540 /* Figure out max width of the text column, the accelerator column and the
3541 * optional submenu column. */
3542 submenuWidth = 0;
3543 for (col = 0; col < 2; col++)
3544 {
3545 columnWidths[col] = 0;
3546 for (pmenu = menu->children; pmenu != NULL; pmenu = pmenu->next)
3547 {
3548 /* Use "dname" here to compute the width of the visible text. */
3549 text = (col == 0) ? pmenu->dname : pmenu->actext;
3550 if (text != NULL && *text != NUL)
3551 {
3552 textWidth = GetTextWidthEnc(hdc, text, (int)STRLEN(text));
3553 if (textWidth > columnWidths[col])
3554 columnWidths[col] = textWidth;
3555 }
3556 if (pmenu->children != NULL)
3557 submenuWidth = TEAROFF_COLUMN_PADDING * spaceWidth;
3558 }
3559 }
3560 if (columnWidths[1] == 0)
3561 {
3562 /* no accelerators */
3563 if (submenuWidth != 0)
3564 columnWidths[0] += submenuWidth;
3565 else
3566 columnWidths[0] += spaceWidth;
3567 }
3568 else
3569 {
3570 /* there is an accelerator column */
3571 columnWidths[0] += TEAROFF_COLUMN_PADDING * spaceWidth;
3572 columnWidths[1] += submenuWidth;
3573 }
3574
3575 /*
3576 * Now find the total width of our 'menu'.
3577 */
3578 textWidth = columnWidths[0] + columnWidths[1];
3579 if (submenuWidth != 0)
3580 {
3581 submenuWidth = GetTextWidth(hdc, TEAROFF_SUBMENU_LABEL,
3582 (int)STRLEN(TEAROFF_SUBMENU_LABEL));
3583 textWidth += submenuWidth;
3584 }
3585 dlgwidth = GetTextWidthEnc(hdc, title, (int)STRLEN(title));
3586 if (textWidth > dlgwidth)
3587 dlgwidth = textWidth;
3588 dlgwidth += 2 * TEAROFF_PADDING_X + TEAROFF_BUTTON_PAD_X;
3589
3590 /* W95 can't do thin dialogs, they look v. weird! */
3591 if (mch_windows95() && dlgwidth < TEAROFF_MIN_WIDTH)
3592 dlgwidth = TEAROFF_MIN_WIDTH;
3593
3594 /* start to fill in the dlgtemplate information. addressing by WORDs */
3595 if (s_usenewlook)
3596 lStyle = DS_MODALFRAME | WS_CAPTION| WS_SYSMENU |DS_SETFONT| WS_VISIBLE;
3597 else
3598 lStyle = DS_MODALFRAME | WS_CAPTION| WS_SYSMENU | WS_VISIBLE;
3599
3600 lExtendedStyle = WS_EX_TOOLWINDOW|WS_EX_STATICEDGE;
3601 *p++ = LOWORD(lStyle);
3602 *p++ = HIWORD(lStyle);
3603 *p++ = LOWORD(lExtendedStyle);
3604 *p++ = HIWORD(lExtendedStyle);
3605 pnumitems = p; /* save where the number of items must be stored */
3606 *p++ = 0; // NumberOfItems(will change later)
Bram Moolenaar9588a0f2005-01-08 21:45:39 +00003607 gui_mch_getmouse(&x, &y);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003608 if (initX == 0xffffL)
Bram Moolenaar9588a0f2005-01-08 21:45:39 +00003609 *p++ = PixelToDialogX(x); // x
Bram Moolenaar071d4272004-06-13 20:20:40 +00003610 else
3611 *p++ = PixelToDialogX(initX); // x
3612 if (initY == 0xffffL)
Bram Moolenaar9588a0f2005-01-08 21:45:39 +00003613 *p++ = PixelToDialogY(y); // y
Bram Moolenaar071d4272004-06-13 20:20:40 +00003614 else
3615 *p++ = PixelToDialogY(initY); // y
3616 *p++ = PixelToDialogX(dlgwidth); // cx
3617 ptrueheight = p;
3618 *p++ = 0; // dialog height: changed later anyway
3619 *p++ = 0; // Menu
3620 *p++ = 0; // Class
3621
3622 /* copy the title of the dialog */
3623 nchar = nCopyAnsiToWideChar(p, ((*title)
3624 ? (LPSTR)title
3625 : (LPSTR)("Vim "VIM_VERSION_MEDIUM)));
3626 p += nchar;
3627
3628 if (s_usenewlook)
3629 {
3630 /* do the font, since DS_3DLOOK doesn't work properly */
3631#ifdef USE_SYSMENU_FONT
3632 if (use_lfSysmenu)
3633 {
3634 /* point size */
3635 *p++ = -MulDiv(lfSysmenu.lfHeight, 72,
3636 GetDeviceCaps(hdc, LOGPIXELSY));
3637 nchar = nCopyAnsiToWideChar(p, TEXT(lfSysmenu.lfFaceName));
3638 }
3639 else
3640#endif
3641 {
3642 *p++ = DLG_FONT_POINT_SIZE; // point size
3643 nchar = nCopyAnsiToWideChar (p, TEXT(DLG_FONT_NAME));
3644 }
3645 p += nchar;
3646 }
3647
3648 /*
3649 * Loop over all the items in the menu.
3650 * But skip over the tearbar.
3651 */
3652 if (STRCMP(menu->children->name, TEAR_STRING) == 0)
3653 menu = menu->children->next;
3654 else
3655 menu = menu->children;
3656 for ( ; menu != NULL; menu = menu->next)
3657 {
3658 if (menu->modes == 0) /* this menu has just been deleted */
3659 continue;
3660 if (menu_is_separator(menu->dname))
3661 {
3662 sepPadding += 3;
3663 continue;
3664 }
3665
3666 /* Check if there still is plenty of room in the template. Make it
3667 * larger when needed. */
3668 if (((char *)p - (char *)pdlgtemplate) + 1000 > template_len)
3669 {
3670 WORD *newp;
3671
3672 newp = (WORD *)LocalAlloc(LPTR, template_len + 4096);
3673 if (newp != NULL)
3674 {
3675 template_len += 4096;
3676 mch_memmove(newp, pdlgtemplate,
3677 (char *)p - (char *)pdlgtemplate);
3678 p = newp + (p - pdlgtemplate);
3679 pnumitems = newp + (pnumitems - pdlgtemplate);
3680 ptrueheight = newp + (ptrueheight - pdlgtemplate);
3681 LocalFree(LocalHandle(pdlgtemplate));
3682 pdlgtemplate = newp;
3683 }
3684 }
3685
3686 /* Figure out minimal length of this menu label. Use "name" for the
3687 * actual text, "dname" for estimating the displayed size. "name"
3688 * has "&a" for mnemonic and includes the accelerator. */
3689 len = nameLen = (int)STRLEN(menu->name);
3690 padding0 = (columnWidths[0] - GetTextWidthEnc(hdc, menu->dname,
3691 (int)STRLEN(menu->dname))) / spaceWidth;
3692 len += padding0;
3693
3694 if (menu->actext != NULL)
3695 {
3696 acLen = (int)STRLEN(menu->actext);
3697 len += acLen;
3698 textWidth = GetTextWidthEnc(hdc, menu->actext, acLen);
3699 }
3700 else
3701 textWidth = 0;
3702 padding1 = (columnWidths[1] - textWidth) / spaceWidth;
3703 len += padding1;
3704
3705 if (menu->children == NULL)
3706 {
3707 padding2 = submenuWidth / spaceWidth;
3708 len += padding2;
3709 menuID = (WORD)(menu->id);
3710 }
3711 else
3712 {
3713 len += (int)STRLEN(TEAROFF_SUBMENU_LABEL);
3714 menuID = (WORD)((DWORD)(menu->submenu_id) | (DWORD)0x8000);
3715 }
3716
3717 /* Allocate menu label and fill it in */
3718 text = label = alloc((unsigned)len + 1);
3719 if (label == NULL)
3720 break;
3721
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003722 vim_strncpy(text, menu->name, nameLen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003723 text = vim_strchr(text, TAB); /* stop at TAB before actext */
3724 if (text == NULL)
3725 text = label + nameLen; /* no actext, use whole name */
3726 while (padding0-- > 0)
3727 *text++ = ' ';
3728 if (menu->actext != NULL)
3729 {
3730 STRNCPY(text, menu->actext, acLen);
3731 text += acLen;
3732 }
3733 while (padding1-- > 0)
3734 *text++ = ' ';
3735 if (menu->children != NULL)
3736 {
3737 STRCPY(text, TEAROFF_SUBMENU_LABEL);
3738 text += STRLEN(TEAROFF_SUBMENU_LABEL);
3739 }
3740 else
3741 {
3742 while (padding2-- > 0)
3743 *text++ = ' ';
3744 }
3745 *text = NUL;
3746
3747 /*
3748 * BS_LEFT will just be ignored on Win32s/NT3.5x - on
3749 * W95/NT4 it makes the tear-off look more like a menu.
3750 */
3751 p = add_dialog_element(p,
3752 BS_PUSHBUTTON|BS_LEFT,
3753 (WORD)PixelToDialogX(TEAROFF_PADDING_X),
3754 (WORD)(sepPadding + 1 + 13 * (*pnumitems)),
3755 (WORD)PixelToDialogX(dlgwidth - 2 * TEAROFF_PADDING_X),
3756 (WORD)12,
3757 menuID, (WORD)0x0080, label);
3758 vim_free(label);
3759 (*pnumitems)++;
3760 }
3761
3762 *ptrueheight = (WORD)(sepPadding + 1 + 13 * (*pnumitems));
3763
3764
3765 /* show modelessly */
3766 the_menu->tearoff_handle = CreateDialogIndirect(
3767 s_hinst,
3768 (LPDLGTEMPLATE)pdlgtemplate,
3769 s_hwnd,
3770 (DLGPROC)tearoff_callback);
3771
3772 LocalFree(LocalHandle(pdlgtemplate));
3773 SelectFont(hdc, oldFont);
3774 DeleteObject(font);
3775 ReleaseDC(hwnd, hdc);
3776
3777 /*
3778 * Reassert ourselves as the active window. This is so that after creating
3779 * a tearoff, the user doesn't have to click with the mouse just to start
3780 * typing again!
3781 */
3782 (void)SetActiveWindow(s_hwnd);
3783
3784 /* make sure the right buttons are enabled */
3785 force_menu_update = TRUE;
3786}
3787#endif
3788
3789#if defined(FEAT_TOOLBAR) || defined(PROTO)
3790#include "gui_w32_rc.h"
3791
3792/* This not defined in older SDKs */
3793# ifndef TBSTYLE_FLAT
3794# define TBSTYLE_FLAT 0x0800
3795# endif
3796
3797/*
3798 * Create the toolbar, initially unpopulated.
3799 * (just like the menu, there are no defaults, it's all
3800 * set up through menu.vim)
3801 */
3802 static void
3803initialise_toolbar(void)
3804{
3805 InitCommonControls();
3806 s_toolbarhwnd = CreateToolbarEx(
3807 s_hwnd,
3808 WS_CHILD | TBSTYLE_TOOLTIPS | TBSTYLE_FLAT,
3809 4000, //any old big number
3810 31, //number of images in inital bitmap
3811 s_hinst,
3812 IDR_TOOLBAR1, // id of initial bitmap
3813 NULL,
3814 0, // initial number of buttons
3815 TOOLBAR_BUTTON_WIDTH, //api guide is wrong!
3816 TOOLBAR_BUTTON_HEIGHT,
3817 TOOLBAR_BUTTON_WIDTH,
3818 TOOLBAR_BUTTON_HEIGHT,
3819 sizeof(TBBUTTON)
3820 );
3821
3822 gui_mch_show_toolbar(vim_strchr(p_go, GO_TOOLBAR) != NULL);
3823}
3824
3825 static int
3826get_toolbar_bitmap(vimmenu_T *menu)
3827{
3828 int i = -1;
3829
3830 /*
3831 * Check user bitmaps first, unless builtin is specified.
3832 */
3833 if (!is_winnt_3() && !menu->icon_builtin)
3834 {
3835 char_u fname[MAXPATHL];
3836 HANDLE hbitmap = NULL;
3837
3838 if (menu->iconfile != NULL)
3839 {
3840 gui_find_iconfile(menu->iconfile, fname, "bmp");
3841 hbitmap = LoadImage(
3842 NULL,
3843 fname,
3844 IMAGE_BITMAP,
3845 TOOLBAR_BUTTON_WIDTH,
3846 TOOLBAR_BUTTON_HEIGHT,
3847 LR_LOADFROMFILE |
3848 LR_LOADMAP3DCOLORS
3849 );
3850 }
3851
3852 /*
3853 * If the LoadImage call failed, or the "icon=" file
3854 * didn't exist or wasn't specified, try the menu name
3855 */
3856 if (hbitmap == NULL
3857 && (gui_find_bitmap(menu->name, fname, "bmp") == OK))
3858 hbitmap = LoadImage(
3859 NULL,
3860 fname,
3861 IMAGE_BITMAP,
3862 TOOLBAR_BUTTON_WIDTH,
3863 TOOLBAR_BUTTON_HEIGHT,
3864 LR_LOADFROMFILE |
3865 LR_LOADMAP3DCOLORS
3866 );
3867
3868 if (hbitmap != NULL)
3869 {
3870 TBADDBITMAP tbAddBitmap;
3871
3872 tbAddBitmap.hInst = NULL;
3873 tbAddBitmap.nID = (UINT)hbitmap;
3874
3875 i = (int)SendMessage(s_toolbarhwnd, TB_ADDBITMAP,
3876 (WPARAM)1, (LPARAM)&tbAddBitmap);
3877 /* i will be set to -1 if it fails */
3878 }
3879 }
3880 if (i == -1 && menu->iconidx >= 0 && menu->iconidx < TOOLBAR_BITMAP_COUNT)
3881 i = menu->iconidx;
3882
3883 return i;
3884}
3885#endif
3886
3887#if defined(FEAT_OLE) || defined(FEAT_EVAL) || defined(PROTO)
3888/*
3889 * Make the GUI window come to the foreground.
3890 */
3891 void
3892gui_mch_set_foreground(void)
3893{
3894 if (IsIconic(s_hwnd))
3895 SendMessage(s_hwnd, WM_SYSCOMMAND, SC_RESTORE, 0);
3896 SetForegroundWindow(s_hwnd);
3897}
3898#endif
3899
3900#if defined(FEAT_MBYTE_IME) && defined(DYNAMIC_IME)
3901 static void
3902dyn_imm_load(void)
3903{
3904 hLibImm = LoadLibrary("imm32.dll");
3905 if (hLibImm == NULL)
3906 return;
3907
3908 pImmGetCompositionStringA
3909 = (void *)GetProcAddress(hLibImm, "ImmGetCompositionStringA");
3910 pImmGetCompositionStringW
3911 = (void *)GetProcAddress(hLibImm, "ImmGetCompositionStringW");
3912 pImmGetContext
3913 = (void *)GetProcAddress(hLibImm, "ImmGetContext");
3914 pImmAssociateContext
3915 = (void *)GetProcAddress(hLibImm, "ImmAssociateContext");
3916 pImmReleaseContext
3917 = (void *)GetProcAddress(hLibImm, "ImmReleaseContext");
3918 pImmGetOpenStatus
3919 = (void *)GetProcAddress(hLibImm, "ImmGetOpenStatus");
3920 pImmSetOpenStatus
3921 = (void *)GetProcAddress(hLibImm, "ImmSetOpenStatus");
3922 pImmGetCompositionFont
3923 = (void *)GetProcAddress(hLibImm, "ImmGetCompositionFontA");
3924 pImmSetCompositionFont
3925 = (void *)GetProcAddress(hLibImm, "ImmSetCompositionFontA");
3926 pImmSetCompositionWindow
3927 = (void *)GetProcAddress(hLibImm, "ImmSetCompositionWindow");
3928 pImmGetConversionStatus
3929 = (void *)GetProcAddress(hLibImm, "ImmGetConversionStatus");
Bram Moolenaarca003e12006-03-17 23:19:38 +00003930 pImmSetConversionStatus
3931 = (void *)GetProcAddress(hLibImm, "ImmSetConversionStatus");
Bram Moolenaar071d4272004-06-13 20:20:40 +00003932
3933 if ( pImmGetCompositionStringA == NULL
3934 || pImmGetCompositionStringW == NULL
3935 || pImmGetContext == NULL
3936 || pImmAssociateContext == NULL
3937 || pImmReleaseContext == NULL
3938 || pImmGetOpenStatus == NULL
3939 || pImmSetOpenStatus == NULL
3940 || pImmGetCompositionFont == NULL
3941 || pImmSetCompositionFont == NULL
3942 || pImmSetCompositionWindow == NULL
Bram Moolenaarca003e12006-03-17 23:19:38 +00003943 || pImmGetConversionStatus == NULL
3944 || pImmSetConversionStatus == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003945 {
3946 FreeLibrary(hLibImm);
3947 hLibImm = NULL;
3948 pImmGetContext = NULL;
3949 return;
3950 }
3951
3952 return;
3953}
3954
3955# if 0 /* not used */
3956 int
3957dyn_imm_unload(void)
3958{
3959 if (!hLibImm)
3960 return FALSE;
3961 FreeLibrary(hLibImm);
3962 hLibImm = NULL;
3963 return TRUE;
3964}
3965# endif
3966
3967#endif
3968
3969#if defined(FEAT_SIGN_ICONS) || defined(PROTO)
3970
3971# ifdef FEAT_XPM_W32
3972# define IMAGE_XPM 100
3973# endif
3974
3975typedef struct _signicon_t
3976{
3977 HANDLE hImage;
3978 UINT uType;
3979#ifdef FEAT_XPM_W32
3980 HANDLE hShape; /* Mask bitmap handle */
3981#endif
3982} signicon_t;
3983
3984 void
3985gui_mch_drawsign(row, col, typenr)
3986 int row;
3987 int col;
3988 int typenr;
3989{
3990 signicon_t *sign;
3991 int x, y, w, h;
3992
3993 if (!gui.in_use || (sign = (signicon_t *)sign_get_image(typenr)) == NULL)
3994 return;
3995
3996 x = TEXT_X(col);
3997 y = TEXT_Y(row);
3998 w = gui.char_width * 2;
3999 h = gui.char_height;
4000 switch (sign->uType)
4001 {
4002 case IMAGE_BITMAP:
4003 {
4004 HDC hdcMem;
4005 HBITMAP hbmpOld;
4006
4007 hdcMem = CreateCompatibleDC(s_hdc);
4008 hbmpOld = (HBITMAP)SelectObject(hdcMem, sign->hImage);
4009 BitBlt(s_hdc, x, y, w, h, hdcMem, 0, 0, SRCCOPY);
4010 SelectObject(hdcMem, hbmpOld);
4011 DeleteDC(hdcMem);
4012 }
4013 break;
4014 case IMAGE_ICON:
4015 case IMAGE_CURSOR:
4016 DrawIconEx(s_hdc, x, y, (HICON)sign->hImage, w, h, 0, NULL, DI_NORMAL);
4017 break;
4018#ifdef FEAT_XPM_W32
4019 case IMAGE_XPM:
4020 {
4021 HDC hdcMem;
4022 HBITMAP hbmpOld;
4023
4024 hdcMem = CreateCompatibleDC(s_hdc);
4025 hbmpOld = (HBITMAP)SelectObject(hdcMem, sign->hShape);
4026 /* Make hole */
4027 BitBlt(s_hdc, x, y, w, h, hdcMem, 0, 0, SRCAND);
4028
4029 SelectObject(hdcMem, sign->hImage);
4030 /* Paint sign */
4031 BitBlt(s_hdc, x, y, w, h, hdcMem, 0, 0, SRCPAINT);
4032 SelectObject(hdcMem, hbmpOld);
4033 DeleteDC(hdcMem);
4034 }
4035 break;
4036#endif
4037 }
4038}
4039
4040 static void
4041close_signicon_image(signicon_t *sign)
4042{
4043 if (sign)
4044 switch (sign->uType)
4045 {
4046 case IMAGE_BITMAP:
4047 DeleteObject((HGDIOBJ)sign->hImage);
4048 break;
4049 case IMAGE_CURSOR:
4050 DestroyCursor((HCURSOR)sign->hImage);
4051 break;
4052 case IMAGE_ICON:
4053 DestroyIcon((HICON)sign->hImage);
4054 break;
4055#ifdef FEAT_XPM_W32
4056 case IMAGE_XPM:
4057 DeleteObject((HBITMAP)sign->hImage);
4058 DeleteObject((HBITMAP)sign->hShape);
4059 break;
4060#endif
4061 }
4062}
4063
4064 void *
4065gui_mch_register_sign(signfile)
4066 char_u *signfile;
4067{
4068 signicon_t sign, *psign;
4069 char_u *ext;
4070
4071 if (is_winnt_3())
4072 {
4073 EMSG(_(e_signdata));
4074 return NULL;
4075 }
4076
4077 sign.hImage = NULL;
4078 ext = signfile + STRLEN(signfile) - 4; /* get extention */
4079 if (ext > signfile)
4080 {
4081 int do_load = 1;
4082
4083 if (!STRICMP(ext, ".bmp"))
4084 sign.uType = IMAGE_BITMAP;
4085 else if (!STRICMP(ext, ".ico"))
4086 sign.uType = IMAGE_ICON;
4087 else if (!STRICMP(ext, ".cur") || !STRICMP(ext, ".ani"))
4088 sign.uType = IMAGE_CURSOR;
4089 else
4090 do_load = 0;
4091
4092 if (do_load)
4093 sign.hImage = (HANDLE)LoadImage(NULL, signfile, sign.uType,
4094 gui.char_width * 2, gui.char_height,
4095 LR_LOADFROMFILE | LR_CREATEDIBSECTION);
4096#ifdef FEAT_XPM_W32
4097 if (!STRICMP(ext, ".xpm"))
4098 {
4099 sign.uType = IMAGE_XPM;
4100 LoadXpmImage(signfile, (HBITMAP *)&sign.hImage, (HBITMAP *)&sign.hShape);
4101 }
4102#endif
4103 }
4104
4105 psign = NULL;
4106 if (sign.hImage && (psign = (signicon_t *)alloc(sizeof(signicon_t)))
4107 != NULL)
4108 *psign = sign;
4109
4110 if (!psign)
4111 {
4112 if (sign.hImage)
4113 close_signicon_image(&sign);
4114 EMSG(_(e_signdata));
4115 }
4116 return (void *)psign;
4117
4118}
4119
4120 void
4121gui_mch_destroy_sign(sign)
4122 void *sign;
4123{
4124 if (sign)
4125 {
4126 close_signicon_image((signicon_t *)sign);
4127 vim_free(sign);
4128 }
4129}
Bram Moolenaar5c06f8b2005-05-31 22:14:58 +00004130#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004131
4132#if defined(FEAT_BEVAL) || defined(PROTO)
4133
4134/* BALLOON-EVAL IMPLEMENTATION FOR WINDOWS.
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00004135 * Added by Sergey Khorev <sergey.khorev@gmail.com>
Bram Moolenaar071d4272004-06-13 20:20:40 +00004136 *
Bram Moolenaare4efc3b2005-03-07 23:16:51 +00004137 * The only reused thing is gui_beval.h and get_beval_info()
Bram Moolenaar071d4272004-06-13 20:20:40 +00004138 * from gui_beval.c (note it uses x and y of the BalloonEval struct
4139 * to get current mouse position).
4140 *
4141 * Trying to use as more Windows services as possible, and as less
4142 * IE version as possible :)).
4143 *
4144 * 1) Don't create ToolTip in gui_mch_create_beval_area, only initialize
4145 * BalloonEval struct.
4146 * 2) Enable/Disable simply create/kill BalloonEval Timer
4147 * 3) When there was enough inactivity, timer procedure posts
4148 * async request to debugger
4149 * 4) gui_mch_post_balloon (invoked from netbeans.c) creates tooltip control
4150 * and performs some actions to show it ASAP
4151 * 5) WM_NOTOFY:TTN_POP destroys created tooltip
4152 */
4153
Bram Moolenaar45360022005-07-21 21:08:21 +00004154/*
4155 * determine whether installed Common Controls support multiline tooltips
4156 * (i.e. their version is >= 4.70
4157 */
4158 int
4159multiline_balloon_available(void)
4160{
4161 HINSTANCE hDll;
4162 static char comctl_dll[] = "comctl32.dll";
4163 static int multiline_tip = MAYBE;
4164
4165 if (multiline_tip != MAYBE)
4166 return multiline_tip;
4167
4168 hDll = GetModuleHandle(comctl_dll);
4169 if (hDll != NULL)
4170 {
4171 DLLGETVERSIONPROC pGetVer;
4172 pGetVer = (DLLGETVERSIONPROC)GetProcAddress(hDll, "DllGetVersion");
4173
4174 if (pGetVer != NULL)
4175 {
4176 DLLVERSIONINFO dvi;
4177 HRESULT hr;
4178
4179 ZeroMemory(&dvi, sizeof(dvi));
4180 dvi.cbSize = sizeof(dvi);
4181
4182 hr = (*pGetVer)(&dvi);
4183
4184 if (SUCCEEDED(hr)
4185 && (dvi.dwMajorVersion > 4
4186 || (dvi.dwMajorVersion == 4 && dvi.dwMinorVersion >= 70)))
4187 {
4188 multiline_tip = TRUE;
4189 return multiline_tip;
4190 }
4191 }
4192 else
4193 {
4194 /* there is chance we have ancient CommCtl 4.70
4195 which doesn't export DllGetVersion */
4196 DWORD dwHandle = 0;
4197 DWORD len = GetFileVersionInfoSize(comctl_dll, &dwHandle);
4198 if (len > 0)
4199 {
4200 VS_FIXEDFILEINFO *ver;
4201 UINT vlen = 0;
4202 void *data = alloc(len);
4203
4204 if (data != NULL
4205 && GetFileVersionInfo(comctl_dll, 0, len, data)
4206 && VerQueryValue(data, "\\", (void **)&ver, &vlen)
4207 && vlen
4208 && HIWORD(ver->dwFileVersionMS) > 4
4209 || (HIWORD(ver->dwFileVersionMS) == 4
4210 && LOWORD(ver->dwFileVersionMS) >= 70))
4211 {
4212 vim_free(data);
4213 multiline_tip = TRUE;
4214 return multiline_tip;
4215 }
4216 vim_free(data);
4217 }
4218 }
4219 }
4220 multiline_tip = FALSE;
4221 return multiline_tip;
4222}
4223
Bram Moolenaar071d4272004-06-13 20:20:40 +00004224 static void
4225make_tooltip(beval, text, pt)
4226 BalloonEval *beval;
4227 char *text;
4228 POINT pt;
4229{
Bram Moolenaar45360022005-07-21 21:08:21 +00004230 TOOLINFO *pti;
4231 int ToolInfoSize;
4232
4233 if (multiline_balloon_available() == TRUE)
4234 ToolInfoSize = sizeof(TOOLINFO_NEW);
4235 else
4236 ToolInfoSize = sizeof(TOOLINFO);
4237
4238 pti = (TOOLINFO *)alloc(ToolInfoSize);
4239 if (pti == NULL)
4240 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004241
4242 beval->balloon = CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS,
4243 NULL, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,
4244 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
4245 beval->target, NULL, s_hinst, NULL);
4246
4247 SetWindowPos(beval->balloon, HWND_TOPMOST, 0, 0, 0, 0,
4248 SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
4249
Bram Moolenaar45360022005-07-21 21:08:21 +00004250 pti->cbSize = ToolInfoSize;
4251 pti->uFlags = TTF_SUBCLASS;
4252 pti->hwnd = beval->target;
4253 pti->hinst = 0; /* Don't use string resources */
4254 pti->uId = ID_BEVAL_TOOLTIP;
4255
4256 if (multiline_balloon_available() == TRUE)
4257 {
4258 RECT rect;
4259 TOOLINFO_NEW *ptin = (TOOLINFO_NEW *)pti;
4260 pti->lpszText = LPSTR_TEXTCALLBACK;
4261 ptin->lParam = (LPARAM)text;
4262 if (GetClientRect(s_textArea, &rect)) /* switch multiline tooltips on */
4263 SendMessage(beval->balloon, TTM_SETMAXTIPWIDTH, 0,
4264 (LPARAM)rect.right);
4265 }
4266 else
4267 pti->lpszText = text; /* do this old way */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004268
4269 /* Limit ballooneval bounding rect to CursorPos neighbourhood */
Bram Moolenaar45360022005-07-21 21:08:21 +00004270 pti->rect.left = pt.x - 3;
4271 pti->rect.top = pt.y - 3;
4272 pti->rect.right = pt.x + 3;
4273 pti->rect.bottom = pt.y + 3;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004274
Bram Moolenaar45360022005-07-21 21:08:21 +00004275 SendMessage(beval->balloon, TTM_ADDTOOL, 0, (LPARAM)pti);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004276 /* Make tooltip appear sooner */
4277 SendMessage(beval->balloon, TTM_SETDELAYTIME, TTDT_INITIAL, 10);
4278 /*
4279 * HACK: force tooltip to appear, because it'll not appear until
4280 * first mouse move. D*mn M$
4281 */
4282 mouse_event(MOUSEEVENTF_MOVE, 1, 1, 0, 0);
4283 mouse_event(MOUSEEVENTF_MOVE, (DWORD)-1, (DWORD)-1, 0, 0);
Bram Moolenaar45360022005-07-21 21:08:21 +00004284 vim_free(pti);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004285}
4286
4287 static void
4288delete_tooltip(beval)
4289 BalloonEval *beval;
4290{
4291 DestroyWindow(beval->balloon);
4292}
4293
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +00004294/*ARGSUSED*/
Bram Moolenaar071d4272004-06-13 20:20:40 +00004295 static VOID CALLBACK
4296BevalTimerProc(hwnd, uMsg, idEvent, dwTime)
4297 HWND hwnd;
4298 UINT uMsg;
4299 UINT idEvent;
4300 DWORD dwTime;
4301{
4302 POINT pt;
4303 RECT rect;
4304
4305 if (cur_beval == NULL || cur_beval->showState == ShS_SHOWING || !p_beval)
4306 return;
4307
4308 GetCursorPos(&pt);
4309 if (WindowFromPoint(pt) != s_textArea)
4310 return;
4311
4312 ScreenToClient(s_textArea, &pt);
4313 GetClientRect(s_textArea, &rect);
4314 if (!PtInRect(&rect, pt))
4315 return;
4316
4317 if (LastActivity > 0
4318 && (dwTime - LastActivity) >= (DWORD)p_bdlay
4319 && (cur_beval->showState != ShS_PENDING
4320 || abs(cur_beval->x - pt.x) > 3
4321 || abs(cur_beval->y - pt.y) > 3))
4322 {
4323 /* Pointer resting in one place long enough, it's time to show
4324 * the tooltip. */
4325 cur_beval->showState = ShS_PENDING;
4326 cur_beval->x = pt.x;
4327 cur_beval->y = pt.y;
4328
Bram Moolenaare2cc9702005-03-15 22:43:58 +00004329 // TRACE0("BevalTimerProc: sending request");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004330
4331 if (cur_beval->msgCB != NULL)
4332 (*cur_beval->msgCB)(cur_beval, 0);
4333 }
4334}
4335
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +00004336/*ARGSUSED*/
Bram Moolenaar071d4272004-06-13 20:20:40 +00004337 void
4338gui_mch_disable_beval_area(beval)
4339 BalloonEval *beval;
4340{
Bram Moolenaare2cc9702005-03-15 22:43:58 +00004341 // TRACE0("gui_mch_disable_beval_area {{{");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004342 KillTimer(s_textArea, BevalTimerId);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00004343 // TRACE0("gui_mch_disable_beval_area }}}");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004344}
4345
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +00004346/*ARGSUSED*/
Bram Moolenaar071d4272004-06-13 20:20:40 +00004347 void
4348gui_mch_enable_beval_area(beval)
4349 BalloonEval *beval;
4350{
Bram Moolenaare2cc9702005-03-15 22:43:58 +00004351 // TRACE0("gui_mch_enable_beval_area |||");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004352 if (beval == NULL)
4353 return;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00004354 // TRACE0("gui_mch_enable_beval_area {{{");
Bram Moolenaar05159a02005-02-26 23:04:13 +00004355 BevalTimerId = SetTimer(s_textArea, 0, p_bdlay / 2,
4356 (TIMERPROC)BevalTimerProc);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00004357 // TRACE0("gui_mch_enable_beval_area }}}");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004358}
4359
4360 void
4361gui_mch_post_balloon(beval, mesg)
4362 BalloonEval *beval;
4363 char_u *mesg;
4364{
4365 POINT pt;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00004366 // TRACE0("gui_mch_post_balloon {{{");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004367 if (beval->showState == ShS_SHOWING)
4368 return;
4369 GetCursorPos(&pt);
4370 ScreenToClient(s_textArea, &pt);
4371
4372 if (abs(beval->x - pt.x) < 3 && abs(beval->y - pt.y) < 3)
4373 /* cursor is still here */
4374 {
4375 gui_mch_disable_beval_area(cur_beval);
4376 beval->showState = ShS_SHOWING;
4377 make_tooltip(beval, mesg, pt);
4378 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00004379 // TRACE0("gui_mch_post_balloon }}}");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004380}
4381
Bram Moolenaard857f0e2005-06-21 22:37:39 +00004382/*ARGSUSED*/
Bram Moolenaar071d4272004-06-13 20:20:40 +00004383 BalloonEval *
4384gui_mch_create_beval_area(target, mesg, mesgCB, clientData)
4385 void *target; /* ignored, always use s_textArea */
4386 char_u *mesg;
4387 void (*mesgCB)__ARGS((BalloonEval *, int));
4388 void *clientData;
4389{
4390 /* partially stolen from gui_beval.c */
4391 BalloonEval *beval;
4392
4393 if (mesg != NULL && mesgCB != NULL)
4394 {
4395 EMSG(_("E232: Cannot create BalloonEval with both message and callback"));
4396 return NULL;
4397 }
4398
4399 beval = (BalloonEval *)alloc(sizeof(BalloonEval));
4400 if (beval != NULL)
4401 {
4402 beval->target = s_textArea;
4403 beval->balloon = NULL;
4404
4405 beval->showState = ShS_NEUTRAL;
4406 beval->x = 0;
4407 beval->y = 0;
4408 beval->msg = mesg;
4409 beval->msgCB = mesgCB;
4410 beval->clientData = clientData;
4411
4412 InitCommonControls();
Bram Moolenaar071d4272004-06-13 20:20:40 +00004413 cur_beval = beval;
4414
4415 if (p_beval)
4416 gui_mch_enable_beval_area(beval);
4417
4418 }
4419 return beval;
4420}
4421
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +00004422/*ARGSUSED*/
Bram Moolenaar071d4272004-06-13 20:20:40 +00004423 static void
4424Handle_WM_Notify(hwnd, pnmh)
4425 HWND hwnd;
4426 LPNMHDR pnmh;
4427{
4428 if (pnmh->idFrom != ID_BEVAL_TOOLTIP) /* it is not our tooltip */
4429 return;
4430
4431 if (cur_beval != NULL)
4432 {
Bram Moolenaar45360022005-07-21 21:08:21 +00004433 switch (pnmh->code)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004434 {
Bram Moolenaar45360022005-07-21 21:08:21 +00004435 case TTN_SHOW:
Bram Moolenaare2cc9702005-03-15 22:43:58 +00004436 // TRACE0("TTN_SHOW {{{");
4437 // TRACE0("TTN_SHOW }}}");
Bram Moolenaar45360022005-07-21 21:08:21 +00004438 break;
4439 case TTN_POP: /* Before tooltip disappear */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00004440 // TRACE0("TTN_POP {{{");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004441 delete_tooltip(cur_beval);
4442 gui_mch_enable_beval_area(cur_beval);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00004443 // TRACE0("TTN_POP }}}");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004444
4445 cur_beval->showState = ShS_NEUTRAL;
Bram Moolenaar45360022005-07-21 21:08:21 +00004446 break;
4447 case TTN_GETDISPINFO:
4448 {
4449 /* if you get there then we have new common controls */
4450 NMTTDISPINFO_NEW *info = (NMTTDISPINFO_NEW *)pnmh;
4451 info->lpszText = (LPSTR)info->lParam;
4452 info->uFlags |= TTF_DI_SETITEM;
4453 }
4454 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004455 }
4456 }
4457}
4458
4459 static void
4460TrackUserActivity(UINT uMsg)
4461{
4462 if ((uMsg >= WM_MOUSEFIRST && uMsg <= WM_MOUSELAST)
4463 || (uMsg >= WM_KEYFIRST && uMsg <= WM_KEYLAST))
4464 LastActivity = GetTickCount();
4465}
4466
4467 void
4468gui_mch_destroy_beval_area(beval)
4469 BalloonEval *beval;
4470{
4471 vim_free(beval);
4472}
4473#endif /* FEAT_BEVAL */
4474
4475#if defined(FEAT_NETBEANS_INTG) || defined(PROTO)
4476/*
4477 * We have multiple signs to draw at the same location. Draw the
4478 * multi-sign indicator (down-arrow) instead. This is the Win32 version.
4479 */
4480 void
4481netbeans_draw_multisign_indicator(int row)
4482{
4483 int i;
4484 int y;
4485 int x;
4486
4487 x = 0;
4488 y = TEXT_Y(row);
4489
4490 for (i = 0; i < gui.char_height - 3; i++)
4491 SetPixel(s_hdc, x+2, y++, gui.currFgColor);
4492
4493 SetPixel(s_hdc, x+0, y, gui.currFgColor);
4494 SetPixel(s_hdc, x+2, y, gui.currFgColor);
4495 SetPixel(s_hdc, x+4, y++, gui.currFgColor);
4496 SetPixel(s_hdc, x+1, y, gui.currFgColor);
4497 SetPixel(s_hdc, x+2, y, gui.currFgColor);
4498 SetPixel(s_hdc, x+3, y++, gui.currFgColor);
4499 SetPixel(s_hdc, x+2, y, gui.currFgColor);
4500}
4501#endif