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