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