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