1 /* vi:set ts=8 sts=4 sw=4 noet: 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, aka Win32. Also for Win64. 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 && enc_utf8) 37 static int directx_enabled(void); 38 static void directx_binddc(void); 39 #endif 40 41 #ifdef FEAT_MENU 42 static int gui_mswin_get_menu_height(int fix_window); 43 #endif 44 45 #if defined(FEAT_RENDER_OPTIONS) || defined(PROTO) 46 int 47 gui_mch_set_rendering_options(char_u *s) 48 { 49 # ifdef FEAT_DIRECTX 50 char_u *p, *q; 51 52 int dx_enable = 0; 53 int dx_flags = 0; 54 float dx_gamma = 0.0f; 55 float dx_contrast = 0.0f; 56 float dx_level = 0.0f; 57 int dx_geom = 0; 58 int dx_renmode = 0; 59 int dx_taamode = 0; 60 61 /* parse string as rendering options. */ 62 for (p = s; p != NULL && *p != NUL; ) 63 { 64 char_u item[256]; 65 char_u name[128]; 66 char_u value[128]; 67 68 copy_option_part(&p, item, sizeof(item), ","); 69 if (p == NULL) 70 break; 71 q = &item[0]; 72 copy_option_part(&q, name, sizeof(name), ":"); 73 if (q == NULL) 74 return FAIL; 75 copy_option_part(&q, value, sizeof(value), ":"); 76 77 if (STRCMP(name, "type") == 0) 78 { 79 if (STRCMP(value, "directx") == 0) 80 dx_enable = 1; 81 else 82 return FAIL; 83 } 84 else if (STRCMP(name, "gamma") == 0) 85 { 86 dx_flags |= 1 << 0; 87 dx_gamma = (float)atof((char *)value); 88 } 89 else if (STRCMP(name, "contrast") == 0) 90 { 91 dx_flags |= 1 << 1; 92 dx_contrast = (float)atof((char *)value); 93 } 94 else if (STRCMP(name, "level") == 0) 95 { 96 dx_flags |= 1 << 2; 97 dx_level = (float)atof((char *)value); 98 } 99 else if (STRCMP(name, "geom") == 0) 100 { 101 dx_flags |= 1 << 3; 102 dx_geom = atoi((char *)value); 103 if (dx_geom < 0 || dx_geom > 2) 104 return FAIL; 105 } 106 else if (STRCMP(name, "renmode") == 0) 107 { 108 dx_flags |= 1 << 4; 109 dx_renmode = atoi((char *)value); 110 if (dx_renmode < 0 || dx_renmode > 6) 111 return FAIL; 112 } 113 else if (STRCMP(name, "taamode") == 0) 114 { 115 dx_flags |= 1 << 5; 116 dx_taamode = atoi((char *)value); 117 if (dx_taamode < 0 || dx_taamode > 3) 118 return FAIL; 119 } 120 else if (STRCMP(name, "scrlines") == 0) 121 { 122 /* Deprecated. Simply ignore it. */ 123 } 124 else 125 return FAIL; 126 } 127 128 if (!gui.in_use) 129 return OK; /* only checking the syntax of the value */ 130 131 /* Enable DirectX/DirectWrite */ 132 if (dx_enable) 133 { 134 if (!directx_enabled()) 135 return FAIL; 136 DWriteContext_SetRenderingParams(s_dwc, NULL); 137 if (dx_flags) 138 { 139 DWriteRenderingParams param; 140 DWriteContext_GetRenderingParams(s_dwc, ¶m); 141 if (dx_flags & (1 << 0)) 142 param.gamma = dx_gamma; 143 if (dx_flags & (1 << 1)) 144 param.enhancedContrast = dx_contrast; 145 if (dx_flags & (1 << 2)) 146 param.clearTypeLevel = dx_level; 147 if (dx_flags & (1 << 3)) 148 param.pixelGeometry = dx_geom; 149 if (dx_flags & (1 << 4)) 150 param.renderingMode = dx_renmode; 151 if (dx_flags & (1 << 5)) 152 param.textAntialiasMode = dx_taamode; 153 DWriteContext_SetRenderingParams(s_dwc, ¶m); 154 } 155 } 156 s_directx_enabled = dx_enable; 157 158 return OK; 159 # else 160 return FAIL; 161 # endif 162 } 163 #endif 164 165 /* 166 * These are new in Windows ME/XP, only defined in recent compilers. 167 */ 168 #ifndef HANDLE_WM_XBUTTONUP 169 # define HANDLE_WM_XBUTTONUP(hwnd, wParam, lParam, fn) \ 170 ((fn)((hwnd), (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L) 171 #endif 172 #ifndef HANDLE_WM_XBUTTONDOWN 173 # define HANDLE_WM_XBUTTONDOWN(hwnd, wParam, lParam, fn) \ 174 ((fn)((hwnd), FALSE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L) 175 #endif 176 #ifndef HANDLE_WM_XBUTTONDBLCLK 177 # define HANDLE_WM_XBUTTONDBLCLK(hwnd, wParam, lParam, fn) \ 178 ((fn)((hwnd), TRUE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L) 179 #endif 180 181 182 #include "version.h" /* used by dialog box routine for default title */ 183 #ifdef DEBUG 184 # include <tchar.h> 185 #endif 186 187 /* cproto fails on missing include files */ 188 #ifndef PROTO 189 190 #ifndef __MINGW32__ 191 # include <shellapi.h> 192 #endif 193 #if defined(FEAT_TOOLBAR) || defined(FEAT_BEVAL_GUI) || defined(FEAT_GUI_TABLINE) 194 # include <commctrl.h> 195 #endif 196 #include <windowsx.h> 197 198 #ifdef GLOBAL_IME 199 # include "glbl_ime.h" 200 #endif 201 202 #endif /* PROTO */ 203 204 #ifdef FEAT_MENU 205 # define MENUHINTS /* show menu hints in command line */ 206 #endif 207 208 /* Some parameters for dialog boxes. All in pixels. */ 209 #define DLG_PADDING_X 10 210 #define DLG_PADDING_Y 10 211 #define DLG_OLD_STYLE_PADDING_X 5 212 #define DLG_OLD_STYLE_PADDING_Y 5 213 #define DLG_VERT_PADDING_X 4 /* For vertical buttons */ 214 #define DLG_VERT_PADDING_Y 4 215 #define DLG_ICON_WIDTH 34 216 #define DLG_ICON_HEIGHT 34 217 #define DLG_MIN_WIDTH 150 218 #define DLG_FONT_NAME "MS Sans Serif" 219 #define DLG_FONT_POINT_SIZE 8 220 #define DLG_MIN_MAX_WIDTH 400 221 #define DLG_MIN_MAX_HEIGHT 400 222 223 #define DLG_NONBUTTON_CONTROL 5000 /* First ID of non-button controls */ 224 225 #ifndef WM_XBUTTONDOWN /* For Win2K / winME ONLY */ 226 # define WM_XBUTTONDOWN 0x020B 227 # define WM_XBUTTONUP 0x020C 228 # define WM_XBUTTONDBLCLK 0x020D 229 # define MK_XBUTTON1 0x0020 230 # define MK_XBUTTON2 0x0040 231 #endif 232 233 #ifdef PROTO 234 /* 235 * Define a few things for generating prototypes. This is just to avoid 236 * syntax errors, the defines do not need to be correct. 237 */ 238 # define APIENTRY 239 # define CALLBACK 240 # define CONST 241 # define FAR 242 # define NEAR 243 # undef _cdecl 244 # define _cdecl 245 typedef int BOOL; 246 typedef int BYTE; 247 typedef int DWORD; 248 typedef int WCHAR; 249 typedef int ENUMLOGFONT; 250 typedef int FINDREPLACE; 251 typedef int HANDLE; 252 typedef int HBITMAP; 253 typedef int HBRUSH; 254 typedef int HDROP; 255 typedef int INT; 256 typedef int LOGFONT[]; 257 typedef int LPARAM; 258 typedef int LPCREATESTRUCT; 259 typedef int LPCSTR; 260 typedef int LPCTSTR; 261 typedef int LPRECT; 262 typedef int LPSTR; 263 typedef int LPWINDOWPOS; 264 typedef int LPWORD; 265 typedef int LRESULT; 266 typedef int HRESULT; 267 # undef MSG 268 typedef int MSG; 269 typedef int NEWTEXTMETRIC; 270 typedef int OSVERSIONINFO; 271 typedef int PWORD; 272 typedef int RECT; 273 typedef int UINT; 274 typedef int WORD; 275 typedef int WPARAM; 276 typedef int POINT; 277 typedef void *HINSTANCE; 278 typedef void *HMENU; 279 typedef void *HWND; 280 typedef void *HDC; 281 typedef void VOID; 282 typedef int LPNMHDR; 283 typedef int LONG; 284 typedef int WNDPROC; 285 typedef int UINT_PTR; 286 typedef int COLORREF; 287 typedef int HCURSOR; 288 #endif 289 290 #ifndef GET_X_LPARAM 291 # define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp)) 292 #endif 293 294 static void _OnPaint( HWND hwnd); 295 static void fill_rect(const RECT *rcp, HBRUSH hbr, COLORREF color); 296 static void clear_rect(RECT *rcp); 297 298 static WORD s_dlgfntheight; /* height of the dialog font */ 299 static WORD s_dlgfntwidth; /* width of the dialog font */ 300 301 #ifdef FEAT_MENU 302 static HMENU s_menuBar = NULL; 303 #endif 304 #ifdef FEAT_TEAROFF 305 static void rebuild_tearoff(vimmenu_T *menu); 306 static HBITMAP s_htearbitmap; /* bitmap used to indicate tearoff */ 307 #endif 308 309 /* Flag that is set while processing a message that must not be interrupted by 310 * processing another message. */ 311 static int s_busy_processing = FALSE; 312 313 static int destroying = FALSE; /* call DestroyWindow() ourselves */ 314 315 #ifdef MSWIN_FIND_REPLACE 316 static UINT s_findrep_msg = 0; /* set in gui_w[16/32].c */ 317 static FINDREPLACE s_findrep_struct; 318 static FINDREPLACEW s_findrep_struct_w; 319 static HWND s_findrep_hwnd = NULL; 320 static int s_findrep_is_find; /* TRUE for find dialog, FALSE 321 for find/replace dialog */ 322 #endif 323 324 static HINSTANCE s_hinst = NULL; 325 #if !defined(FEAT_GUI) 326 static 327 #endif 328 HWND s_hwnd = NULL; 329 static HDC s_hdc = NULL; 330 static HBRUSH s_brush = NULL; 331 332 #ifdef FEAT_TOOLBAR 333 static HWND s_toolbarhwnd = NULL; 334 static WNDPROC s_toolbar_wndproc = NULL; 335 #endif 336 337 #ifdef FEAT_GUI_TABLINE 338 static HWND s_tabhwnd = NULL; 339 static WNDPROC s_tabline_wndproc = NULL; 340 static int showing_tabline = 0; 341 #endif 342 343 static WPARAM s_wParam = 0; 344 static LPARAM s_lParam = 0; 345 346 static HWND s_textArea = NULL; 347 static UINT s_uMsg = 0; 348 349 static char_u *s_textfield; /* Used by dialogs to pass back strings */ 350 351 static int s_need_activate = FALSE; 352 353 /* This variable is set when waiting for an event, which is the only moment 354 * scrollbar dragging can be done directly. It's not allowed while commands 355 * are executed, because it may move the cursor and that may cause unexpected 356 * problems (e.g., while ":s" is working). 357 */ 358 static int allow_scrollbar = FALSE; 359 360 #ifdef GLOBAL_IME 361 # define MyTranslateMessage(x) global_ime_TranslateMessage(x) 362 #else 363 # define MyTranslateMessage(x) TranslateMessage(x) 364 #endif 365 366 #if defined(FEAT_DIRECTX) 367 static int 368 directx_enabled(void) 369 { 370 if (s_dwc != NULL) 371 return 1; 372 else if (s_directx_load_attempted) 373 return 0; 374 /* load DirectX */ 375 DWrite_Init(); 376 s_directx_load_attempted = 1; 377 s_dwc = DWriteContext_Open(); 378 directx_binddc(); 379 return s_dwc != NULL ? 1 : 0; 380 } 381 382 static void 383 directx_binddc(void) 384 { 385 if (s_textArea != NULL) 386 { 387 RECT rect; 388 GetClientRect(s_textArea, &rect); 389 DWriteContext_BindDC(s_dwc, s_hdc, &rect); 390 } 391 } 392 #endif 393 394 /* use of WindowProc depends on wide_WindowProc */ 395 #define MyWindowProc vim_WindowProc 396 397 extern int current_font_height; /* this is in os_mswin.c */ 398 399 static struct 400 { 401 UINT key_sym; 402 char_u vim_code0; 403 char_u vim_code1; 404 } special_keys[] = 405 { 406 {VK_UP, 'k', 'u'}, 407 {VK_DOWN, 'k', 'd'}, 408 {VK_LEFT, 'k', 'l'}, 409 {VK_RIGHT, 'k', 'r'}, 410 411 {VK_F1, 'k', '1'}, 412 {VK_F2, 'k', '2'}, 413 {VK_F3, 'k', '3'}, 414 {VK_F4, 'k', '4'}, 415 {VK_F5, 'k', '5'}, 416 {VK_F6, 'k', '6'}, 417 {VK_F7, 'k', '7'}, 418 {VK_F8, 'k', '8'}, 419 {VK_F9, 'k', '9'}, 420 {VK_F10, 'k', ';'}, 421 422 {VK_F11, 'F', '1'}, 423 {VK_F12, 'F', '2'}, 424 {VK_F13, 'F', '3'}, 425 {VK_F14, 'F', '4'}, 426 {VK_F15, 'F', '5'}, 427 {VK_F16, 'F', '6'}, 428 {VK_F17, 'F', '7'}, 429 {VK_F18, 'F', '8'}, 430 {VK_F19, 'F', '9'}, 431 {VK_F20, 'F', 'A'}, 432 433 {VK_F21, 'F', 'B'}, 434 #ifdef FEAT_NETBEANS_INTG 435 {VK_PAUSE, 'F', 'B'}, /* Pause == F21 (see gui_gtk_x11.c) */ 436 #endif 437 {VK_F22, 'F', 'C'}, 438 {VK_F23, 'F', 'D'}, 439 {VK_F24, 'F', 'E'}, /* winuser.h defines up to F24 */ 440 441 {VK_HELP, '%', '1'}, 442 {VK_BACK, 'k', 'b'}, 443 {VK_INSERT, 'k', 'I'}, 444 {VK_DELETE, 'k', 'D'}, 445 {VK_HOME, 'k', 'h'}, 446 {VK_END, '@', '7'}, 447 {VK_PRIOR, 'k', 'P'}, 448 {VK_NEXT, 'k', 'N'}, 449 {VK_PRINT, '%', '9'}, 450 {VK_ADD, 'K', '6'}, 451 {VK_SUBTRACT, 'K', '7'}, 452 {VK_DIVIDE, 'K', '8'}, 453 {VK_MULTIPLY, 'K', '9'}, 454 {VK_SEPARATOR, 'K', 'A'}, /* Keypad Enter */ 455 {VK_DECIMAL, 'K', 'B'}, 456 457 {VK_NUMPAD0, 'K', 'C'}, 458 {VK_NUMPAD1, 'K', 'D'}, 459 {VK_NUMPAD2, 'K', 'E'}, 460 {VK_NUMPAD3, 'K', 'F'}, 461 {VK_NUMPAD4, 'K', 'G'}, 462 {VK_NUMPAD5, 'K', 'H'}, 463 {VK_NUMPAD6, 'K', 'I'}, 464 {VK_NUMPAD7, 'K', 'J'}, 465 {VK_NUMPAD8, 'K', 'K'}, 466 {VK_NUMPAD9, 'K', 'L'}, 467 468 /* Keys that we want to be able to use any modifier with: */ 469 {VK_SPACE, ' ', NUL}, 470 {VK_TAB, TAB, NUL}, 471 {VK_ESCAPE, ESC, NUL}, 472 {NL, NL, NUL}, 473 {CAR, CAR, NUL}, 474 475 /* End of list marker: */ 476 {0, 0, 0} 477 }; 478 479 /* Local variables */ 480 static int s_button_pending = -1; 481 482 /* s_getting_focus is set when we got focus but didn't see mouse-up event yet, 483 * so don't reset s_button_pending. */ 484 static int s_getting_focus = FALSE; 485 486 static int s_x_pending; 487 static int s_y_pending; 488 static UINT s_kFlags_pending; 489 static UINT s_wait_timer = 0; // Timer for get char from user 490 static int s_timed_out = FALSE; 491 static int dead_key = 0; // 0: no dead key, 1: dead key pressed 492 static UINT surrogate_pending_ch = 0; // 0: no surrogate pending, 493 // else a high surrogate 494 495 #ifdef FEAT_BEVAL_GUI 496 /* balloon-eval WM_NOTIFY_HANDLER */ 497 static void Handle_WM_Notify(HWND hwnd, LPNMHDR pnmh); 498 static void TrackUserActivity(UINT uMsg); 499 #endif 500 501 /* 502 * For control IME. 503 * 504 * These LOGFONT used for IME. 505 */ 506 #if defined(FEAT_MBYTE_IME) || defined(GLOBAL_IME) 507 /* holds LOGFONT for 'guifontwide' if available, otherwise 'guifont' */ 508 static LOGFONT norm_logfont; 509 #endif 510 #ifdef FEAT_MBYTE_IME 511 /* holds LOGFONT for 'guifont' always. */ 512 static LOGFONT sub_logfont; 513 #endif 514 515 #ifdef FEAT_MBYTE_IME 516 static LRESULT _OnImeNotify(HWND hWnd, DWORD dwCommand, DWORD dwData); 517 #endif 518 519 #if defined(FEAT_BROWSE) 520 static char_u *convert_filter(char_u *s); 521 #endif 522 523 #ifdef DEBUG_PRINT_ERROR 524 /* 525 * Print out the last Windows error message 526 */ 527 static void 528 print_windows_error(void) 529 { 530 LPVOID lpMsgBuf; 531 532 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, 533 NULL, GetLastError(), 534 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 535 (LPTSTR) &lpMsgBuf, 0, NULL); 536 TRACE1("Error: %s\n", lpMsgBuf); 537 LocalFree(lpMsgBuf); 538 } 539 #endif 540 541 /* 542 * Cursor blink functions. 543 * 544 * This is a simple state machine: 545 * BLINK_NONE not blinking at all 546 * BLINK_OFF blinking, cursor is not shown 547 * BLINK_ON blinking, cursor is shown 548 */ 549 550 #define BLINK_NONE 0 551 #define BLINK_OFF 1 552 #define BLINK_ON 2 553 554 static int blink_state = BLINK_NONE; 555 static long_u blink_waittime = 700; 556 static long_u blink_ontime = 400; 557 static long_u blink_offtime = 250; 558 static UINT blink_timer = 0; 559 560 int 561 gui_mch_is_blinking(void) 562 { 563 return blink_state != BLINK_NONE; 564 } 565 566 int 567 gui_mch_is_blink_off(void) 568 { 569 return blink_state == BLINK_OFF; 570 } 571 572 void 573 gui_mch_set_blinking(long wait, long on, long off) 574 { 575 blink_waittime = wait; 576 blink_ontime = on; 577 blink_offtime = off; 578 } 579 580 static VOID CALLBACK 581 _OnBlinkTimer( 582 HWND hwnd, 583 UINT uMsg UNUSED, 584 UINT idEvent, 585 DWORD dwTime UNUSED) 586 { 587 MSG msg; 588 589 /* 590 TRACE2("Got timer event, id %d, blink_timer %d\n", idEvent, blink_timer); 591 */ 592 593 KillTimer(NULL, idEvent); 594 595 /* Eat spurious WM_TIMER messages */ 596 while (pPeekMessage(&msg, hwnd, WM_TIMER, WM_TIMER, PM_REMOVE)) 597 ; 598 599 if (blink_state == BLINK_ON) 600 { 601 gui_undraw_cursor(); 602 blink_state = BLINK_OFF; 603 blink_timer = (UINT) SetTimer(NULL, 0, (UINT)blink_offtime, 604 (TIMERPROC)_OnBlinkTimer); 605 } 606 else 607 { 608 gui_update_cursor(TRUE, FALSE); 609 blink_state = BLINK_ON; 610 blink_timer = (UINT) SetTimer(NULL, 0, (UINT)blink_ontime, 611 (TIMERPROC)_OnBlinkTimer); 612 } 613 gui_mch_flush(); 614 } 615 616 static void 617 gui_mswin_rm_blink_timer(void) 618 { 619 MSG msg; 620 621 if (blink_timer != 0) 622 { 623 KillTimer(NULL, blink_timer); 624 /* Eat spurious WM_TIMER messages */ 625 while (pPeekMessage(&msg, s_hwnd, WM_TIMER, WM_TIMER, PM_REMOVE)) 626 ; 627 blink_timer = 0; 628 } 629 } 630 631 /* 632 * Stop the cursor blinking. Show the cursor if it wasn't shown. 633 */ 634 void 635 gui_mch_stop_blink(int may_call_gui_update_cursor) 636 { 637 gui_mswin_rm_blink_timer(); 638 if (blink_state == BLINK_OFF && may_call_gui_update_cursor) 639 { 640 gui_update_cursor(TRUE, FALSE); 641 gui_mch_flush(); 642 } 643 blink_state = BLINK_NONE; 644 } 645 646 /* 647 * Start the cursor blinking. If it was already blinking, this restarts the 648 * waiting time and shows the cursor. 649 */ 650 void 651 gui_mch_start_blink(void) 652 { 653 gui_mswin_rm_blink_timer(); 654 655 /* Only switch blinking on if none of the times is zero */ 656 if (blink_waittime && blink_ontime && blink_offtime && gui.in_focus) 657 { 658 blink_timer = (UINT)SetTimer(NULL, 0, (UINT)blink_waittime, 659 (TIMERPROC)_OnBlinkTimer); 660 blink_state = BLINK_ON; 661 gui_update_cursor(TRUE, FALSE); 662 gui_mch_flush(); 663 } 664 } 665 666 /* 667 * Call-back routines. 668 */ 669 670 static VOID CALLBACK 671 _OnTimer( 672 HWND hwnd, 673 UINT uMsg UNUSED, 674 UINT idEvent, 675 DWORD dwTime UNUSED) 676 { 677 MSG msg; 678 679 /* 680 TRACE2("Got timer event, id %d, s_wait_timer %d\n", idEvent, s_wait_timer); 681 */ 682 KillTimer(NULL, idEvent); 683 s_timed_out = TRUE; 684 685 /* Eat spurious WM_TIMER messages */ 686 while (pPeekMessage(&msg, hwnd, WM_TIMER, WM_TIMER, PM_REMOVE)) 687 ; 688 if (idEvent == s_wait_timer) 689 s_wait_timer = 0; 690 } 691 692 static void 693 _OnDeadChar( 694 HWND hwnd UNUSED, 695 UINT ch UNUSED, 696 int cRepeat UNUSED) 697 { 698 dead_key = 1; 699 } 700 701 /* 702 * Convert Unicode character "ch" to bytes in "string[slen]". 703 * When "had_alt" is TRUE the ALT key was included in "ch". 704 * Return the length. 705 * Because the Windows API uses UTF-16, we have to deal with surrogate 706 * pairs; this is where we choose to deal with them: if "ch" is a high 707 * surrogate, it will be stored, and the length returned will be zero; the next 708 * char_to_string call will then include the high surrogate, decoding the pair 709 * of UTF-16 code units to a single Unicode code point, presuming it is the 710 * matching low surrogate. 711 */ 712 static int 713 char_to_string(int ch, char_u *string, int slen, int had_alt) 714 { 715 int len; 716 int i; 717 WCHAR wstring[2]; 718 char_u *ws = NULL; 719 720 if (surrogate_pending_ch != 0) 721 { 722 /* We don't guarantee ch is a low surrogate to match the high surrogate 723 * we already have; it should be, but if it isn't, tough luck. */ 724 wstring[0] = surrogate_pending_ch; 725 wstring[1] = ch; 726 surrogate_pending_ch = 0; 727 len = 2; 728 } 729 else if (ch >= 0xD800 && ch <= 0xDBFF) /* high surrogate */ 730 { 731 /* We don't have the entire code point yet, only the first UTF-16 code 732 * unit; so just remember it and use it in the next call. */ 733 surrogate_pending_ch = ch; 734 return 0; 735 } 736 else 737 { 738 wstring[0] = ch; 739 len = 1; 740 } 741 742 /* "ch" is a UTF-16 character. Convert it to a string of bytes. When 743 * "enc_codepage" is non-zero use the standard Win32 function, 744 * otherwise use our own conversion function (e.g., for UTF-8). */ 745 if (enc_codepage > 0) 746 { 747 len = WideCharToMultiByte(enc_codepage, 0, wstring, len, 748 (LPSTR)string, slen, 0, NULL); 749 /* If we had included the ALT key into the character but now the 750 * upper bit is no longer set, that probably means the conversion 751 * failed. Convert the original character and set the upper bit 752 * afterwards. */ 753 if (had_alt && len == 1 && ch >= 0x80 && string[0] < 0x80) 754 { 755 wstring[0] = ch & 0x7f; 756 len = WideCharToMultiByte(enc_codepage, 0, wstring, len, 757 (LPSTR)string, slen, 0, NULL); 758 if (len == 1) /* safety check */ 759 string[0] |= 0x80; 760 } 761 } 762 else 763 { 764 ws = utf16_to_enc(wstring, &len); 765 if (ws == NULL) 766 len = 0; 767 else 768 { 769 if (len > slen) /* just in case */ 770 len = slen; 771 mch_memmove(string, ws, len); 772 vim_free(ws); 773 } 774 } 775 776 if (len == 0) 777 { 778 string[0] = ch; 779 len = 1; 780 } 781 782 for (i = 0; i < len; ++i) 783 if (string[i] == CSI && len <= slen - 2) 784 { 785 /* Insert CSI as K_CSI. */ 786 mch_memmove(string + i + 3, string + i + 1, len - i - 1); 787 string[++i] = KS_EXTRA; 788 string[++i] = (int)KE_CSI; 789 len += 2; 790 } 791 792 return len; 793 } 794 795 /* 796 * Key hit, add it to the input buffer. 797 */ 798 static void 799 _OnChar( 800 HWND hwnd UNUSED, 801 UINT ch, 802 int cRepeat UNUSED) 803 { 804 char_u string[40]; 805 int len = 0; 806 807 dead_key = 0; 808 809 len = char_to_string(ch, string, 40, FALSE); 810 if (len == 1 && string[0] == Ctrl_C && ctrl_c_interrupts) 811 { 812 trash_input_buf(); 813 got_int = TRUE; 814 } 815 816 add_to_input_buf(string, len); 817 } 818 819 /* 820 * Alt-Key hit, add it to the input buffer. 821 */ 822 static void 823 _OnSysChar( 824 HWND hwnd UNUSED, 825 UINT cch, 826 int cRepeat UNUSED) 827 { 828 char_u string[40]; /* Enough for multibyte character */ 829 int len; 830 int modifiers; 831 int ch = cch; /* special keys are negative */ 832 833 dead_key = 0; 834 835 /* TRACE("OnSysChar(%d, %c)\n", ch, ch); */ 836 837 /* OK, we have a character key (given by ch) which was entered with the 838 * ALT key pressed. Eg, if the user presses Alt-A, then ch == 'A'. Note 839 * that the system distinguishes Alt-a and Alt-A (Alt-Shift-a unless 840 * CAPSLOCK is pressed) at this point. 841 */ 842 modifiers = MOD_MASK_ALT; 843 if (GetKeyState(VK_SHIFT) & 0x8000) 844 modifiers |= MOD_MASK_SHIFT; 845 if (GetKeyState(VK_CONTROL) & 0x8000) 846 modifiers |= MOD_MASK_CTRL; 847 848 ch = simplify_key(ch, &modifiers); 849 /* remove the SHIFT modifier for keys where it's already included, e.g., 850 * '(' and '*' */ 851 if (ch < 0x100 && !isalpha(ch) && isprint(ch)) 852 modifiers &= ~MOD_MASK_SHIFT; 853 854 /* Interpret the ALT key as making the key META, include SHIFT, etc. */ 855 ch = extract_modifiers(ch, &modifiers); 856 if (ch == CSI) 857 ch = K_CSI; 858 859 len = 0; 860 if (modifiers) 861 { 862 string[len++] = CSI; 863 string[len++] = KS_MODIFIER; 864 string[len++] = modifiers; 865 } 866 867 if (IS_SPECIAL((int)ch)) 868 { 869 string[len++] = CSI; 870 string[len++] = K_SECOND((int)ch); 871 string[len++] = K_THIRD((int)ch); 872 } 873 else 874 { 875 /* Although the documentation isn't clear about it, we assume "ch" is 876 * a Unicode character. */ 877 len += char_to_string(ch, string + len, 40 - len, TRUE); 878 } 879 880 add_to_input_buf(string, len); 881 } 882 883 static void 884 _OnMouseEvent( 885 int button, 886 int x, 887 int y, 888 int repeated_click, 889 UINT keyFlags) 890 { 891 int vim_modifiers = 0x0; 892 893 s_getting_focus = FALSE; 894 895 if (keyFlags & MK_SHIFT) 896 vim_modifiers |= MOUSE_SHIFT; 897 if (keyFlags & MK_CONTROL) 898 vim_modifiers |= MOUSE_CTRL; 899 if (GetKeyState(VK_MENU) & 0x8000) 900 vim_modifiers |= MOUSE_ALT; 901 902 gui_send_mouse_event(button, x, y, repeated_click, vim_modifiers); 903 } 904 905 static void 906 _OnMouseButtonDown( 907 HWND hwnd UNUSED, 908 BOOL fDoubleClick UNUSED, 909 int x, 910 int y, 911 UINT keyFlags) 912 { 913 static LONG s_prevTime = 0; 914 915 LONG currentTime = GetMessageTime(); 916 int button = -1; 917 int repeated_click; 918 919 /* Give main window the focus: this is so the cursor isn't hollow. */ 920 (void)SetFocus(s_hwnd); 921 922 if (s_uMsg == WM_LBUTTONDOWN || s_uMsg == WM_LBUTTONDBLCLK) 923 button = MOUSE_LEFT; 924 else if (s_uMsg == WM_MBUTTONDOWN || s_uMsg == WM_MBUTTONDBLCLK) 925 button = MOUSE_MIDDLE; 926 else if (s_uMsg == WM_RBUTTONDOWN || s_uMsg == WM_RBUTTONDBLCLK) 927 button = MOUSE_RIGHT; 928 else if (s_uMsg == WM_XBUTTONDOWN || s_uMsg == WM_XBUTTONDBLCLK) 929 { 930 #ifndef GET_XBUTTON_WPARAM 931 # define GET_XBUTTON_WPARAM(wParam) (HIWORD(wParam)) 932 #endif 933 button = ((GET_XBUTTON_WPARAM(s_wParam) == 1) ? MOUSE_X1 : MOUSE_X2); 934 } 935 else if (s_uMsg == WM_CAPTURECHANGED) 936 { 937 /* on W95/NT4, somehow you get in here with an odd Msg 938 * if you press one button while holding down the other..*/ 939 if (s_button_pending == MOUSE_LEFT) 940 button = MOUSE_RIGHT; 941 else 942 button = MOUSE_LEFT; 943 } 944 if (button >= 0) 945 { 946 repeated_click = ((int)(currentTime - s_prevTime) < p_mouset); 947 948 /* 949 * Holding down the left and right buttons simulates pushing the middle 950 * button. 951 */ 952 if (repeated_click 953 && ((button == MOUSE_LEFT && s_button_pending == MOUSE_RIGHT) 954 || (button == MOUSE_RIGHT 955 && s_button_pending == MOUSE_LEFT))) 956 { 957 /* 958 * Hmm, gui.c will ignore more than one button down at a time, so 959 * pretend we let go of it first. 960 */ 961 gui_send_mouse_event(MOUSE_RELEASE, x, y, FALSE, 0x0); 962 button = MOUSE_MIDDLE; 963 repeated_click = FALSE; 964 s_button_pending = -1; 965 _OnMouseEvent(button, x, y, repeated_click, keyFlags); 966 } 967 else if ((repeated_click) 968 || (mouse_model_popup() && (button == MOUSE_RIGHT))) 969 { 970 if (s_button_pending > -1) 971 { 972 _OnMouseEvent(s_button_pending, x, y, FALSE, keyFlags); 973 s_button_pending = -1; 974 } 975 /* TRACE("Button down at x %d, y %d\n", x, y); */ 976 _OnMouseEvent(button, x, y, repeated_click, keyFlags); 977 } 978 else 979 { 980 /* 981 * If this is the first press (i.e. not a multiple click) don't 982 * action immediately, but store and wait for: 983 * i) button-up 984 * ii) mouse move 985 * iii) another button press 986 * before using it. 987 * This enables us to make left+right simulate middle button, 988 * without left or right being actioned first. The side-effect is 989 * that if you click and hold the mouse without dragging, the 990 * cursor doesn't move until you release the button. In practice 991 * this is hardly a problem. 992 */ 993 s_button_pending = button; 994 s_x_pending = x; 995 s_y_pending = y; 996 s_kFlags_pending = keyFlags; 997 } 998 999 s_prevTime = currentTime; 1000 } 1001 } 1002 1003 static void 1004 _OnMouseMoveOrRelease( 1005 HWND hwnd UNUSED, 1006 int x, 1007 int y, 1008 UINT keyFlags) 1009 { 1010 int button; 1011 1012 s_getting_focus = FALSE; 1013 if (s_button_pending > -1) 1014 { 1015 /* Delayed action for mouse down event */ 1016 _OnMouseEvent(s_button_pending, s_x_pending, 1017 s_y_pending, FALSE, s_kFlags_pending); 1018 s_button_pending = -1; 1019 } 1020 if (s_uMsg == WM_MOUSEMOVE) 1021 { 1022 /* 1023 * It's only a MOUSE_DRAG if one or more mouse buttons are being held 1024 * down. 1025 */ 1026 if (!(keyFlags & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON 1027 | MK_XBUTTON1 | MK_XBUTTON2))) 1028 { 1029 gui_mouse_moved(x, y); 1030 return; 1031 } 1032 1033 /* 1034 * While button is down, keep grabbing mouse move events when 1035 * the mouse goes outside the window 1036 */ 1037 SetCapture(s_textArea); 1038 button = MOUSE_DRAG; 1039 /* TRACE(" move at x %d, y %d\n", x, y); */ 1040 } 1041 else 1042 { 1043 ReleaseCapture(); 1044 button = MOUSE_RELEASE; 1045 /* TRACE(" up at x %d, y %d\n", x, y); */ 1046 } 1047 1048 _OnMouseEvent(button, x, y, FALSE, keyFlags); 1049 } 1050 1051 static void 1052 _OnSizeTextArea( 1053 HWND hwnd UNUSED, 1054 UINT state UNUSED, 1055 int cx UNUSED, 1056 int cy UNUSED) 1057 { 1058 #if defined(FEAT_DIRECTX) 1059 if (IS_ENABLE_DIRECTX()) 1060 directx_binddc(); 1061 #endif 1062 } 1063 1064 #ifdef FEAT_MENU 1065 /* 1066 * Find the vimmenu_T with the given id 1067 */ 1068 static vimmenu_T * 1069 gui_mswin_find_menu( 1070 vimmenu_T *pMenu, 1071 int id) 1072 { 1073 vimmenu_T *pChildMenu; 1074 1075 while (pMenu) 1076 { 1077 if (pMenu->id == (UINT)id) 1078 break; 1079 if (pMenu->children != NULL) 1080 { 1081 pChildMenu = gui_mswin_find_menu(pMenu->children, id); 1082 if (pChildMenu) 1083 { 1084 pMenu = pChildMenu; 1085 break; 1086 } 1087 } 1088 pMenu = pMenu->next; 1089 } 1090 return pMenu; 1091 } 1092 1093 static void 1094 _OnMenu( 1095 HWND hwnd UNUSED, 1096 int id, 1097 HWND hwndCtl UNUSED, 1098 UINT codeNotify UNUSED) 1099 { 1100 vimmenu_T *pMenu; 1101 1102 pMenu = gui_mswin_find_menu(root_menu, id); 1103 if (pMenu) 1104 gui_menu_cb(pMenu); 1105 } 1106 #endif 1107 1108 #ifdef MSWIN_FIND_REPLACE 1109 /* 1110 * copy useful data from structure LPFINDREPLACE to structure LPFINDREPLACEW 1111 */ 1112 static void 1113 findrep_atow(LPFINDREPLACEW lpfrw, LPFINDREPLACE lpfr) 1114 { 1115 WCHAR *wp; 1116 1117 lpfrw->hwndOwner = lpfr->hwndOwner; 1118 lpfrw->Flags = lpfr->Flags; 1119 1120 wp = enc_to_utf16((char_u *)lpfr->lpstrFindWhat, NULL); 1121 wcsncpy(lpfrw->lpstrFindWhat, wp, lpfrw->wFindWhatLen - 1); 1122 vim_free(wp); 1123 1124 /* the field "lpstrReplaceWith" doesn't need to be copied */ 1125 } 1126 1127 /* 1128 * copy useful data from structure LPFINDREPLACEW to structure LPFINDREPLACE 1129 */ 1130 static void 1131 findrep_wtoa(LPFINDREPLACE lpfr, LPFINDREPLACEW lpfrw) 1132 { 1133 char_u *p; 1134 1135 lpfr->Flags = lpfrw->Flags; 1136 1137 p = utf16_to_enc((short_u*)lpfrw->lpstrFindWhat, NULL); 1138 vim_strncpy((char_u *)lpfr->lpstrFindWhat, p, lpfr->wFindWhatLen - 1); 1139 vim_free(p); 1140 1141 p = utf16_to_enc((short_u*)lpfrw->lpstrReplaceWith, NULL); 1142 vim_strncpy((char_u *)lpfr->lpstrReplaceWith, p, lpfr->wReplaceWithLen - 1); 1143 vim_free(p); 1144 } 1145 1146 /* 1147 * Handle a Find/Replace window message. 1148 */ 1149 static void 1150 _OnFindRepl(void) 1151 { 1152 int flags = 0; 1153 int down; 1154 1155 /* If the OS is Windows NT, and 'encoding' differs from active codepage: 1156 * convert text from wide string. */ 1157 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage) 1158 { 1159 findrep_wtoa(&s_findrep_struct, &s_findrep_struct_w); 1160 } 1161 1162 if (s_findrep_struct.Flags & FR_DIALOGTERM) 1163 /* Give main window the focus back. */ 1164 (void)SetFocus(s_hwnd); 1165 1166 if (s_findrep_struct.Flags & FR_FINDNEXT) 1167 { 1168 flags = FRD_FINDNEXT; 1169 1170 /* Give main window the focus back: this is so the cursor isn't 1171 * hollow. */ 1172 (void)SetFocus(s_hwnd); 1173 } 1174 else if (s_findrep_struct.Flags & FR_REPLACE) 1175 { 1176 flags = FRD_REPLACE; 1177 1178 /* Give main window the focus back: this is so the cursor isn't 1179 * hollow. */ 1180 (void)SetFocus(s_hwnd); 1181 } 1182 else if (s_findrep_struct.Flags & FR_REPLACEALL) 1183 { 1184 flags = FRD_REPLACEALL; 1185 } 1186 1187 if (flags != 0) 1188 { 1189 /* Call the generic GUI function to do the actual work. */ 1190 if (s_findrep_struct.Flags & FR_WHOLEWORD) 1191 flags |= FRD_WHOLE_WORD; 1192 if (s_findrep_struct.Flags & FR_MATCHCASE) 1193 flags |= FRD_MATCH_CASE; 1194 down = (s_findrep_struct.Flags & FR_DOWN) != 0; 1195 gui_do_findrepl(flags, (char_u *)s_findrep_struct.lpstrFindWhat, 1196 (char_u *)s_findrep_struct.lpstrReplaceWith, down); 1197 } 1198 } 1199 #endif 1200 1201 static void 1202 HandleMouseHide(UINT uMsg, LPARAM lParam) 1203 { 1204 static LPARAM last_lParam = 0L; 1205 1206 /* We sometimes get a mousemove when the mouse didn't move... */ 1207 if (uMsg == WM_MOUSEMOVE || uMsg == WM_NCMOUSEMOVE) 1208 { 1209 if (lParam == last_lParam) 1210 return; 1211 last_lParam = lParam; 1212 } 1213 1214 /* Handle specially, to centralise coding. We need to be sure we catch all 1215 * possible events which should cause us to restore the cursor (as it is a 1216 * shared resource, we take full responsibility for it). 1217 */ 1218 switch (uMsg) 1219 { 1220 case WM_KEYUP: 1221 case WM_CHAR: 1222 /* 1223 * blank out the pointer if necessary 1224 */ 1225 if (p_mh) 1226 gui_mch_mousehide(TRUE); 1227 break; 1228 1229 case WM_SYSKEYUP: /* show the pointer when a system-key is pressed */ 1230 case WM_SYSCHAR: 1231 case WM_MOUSEMOVE: /* show the pointer on any mouse action */ 1232 case WM_LBUTTONDOWN: 1233 case WM_LBUTTONUP: 1234 case WM_MBUTTONDOWN: 1235 case WM_MBUTTONUP: 1236 case WM_RBUTTONDOWN: 1237 case WM_RBUTTONUP: 1238 case WM_XBUTTONDOWN: 1239 case WM_XBUTTONUP: 1240 case WM_NCMOUSEMOVE: 1241 case WM_NCLBUTTONDOWN: 1242 case WM_NCLBUTTONUP: 1243 case WM_NCMBUTTONDOWN: 1244 case WM_NCMBUTTONUP: 1245 case WM_NCRBUTTONDOWN: 1246 case WM_NCRBUTTONUP: 1247 case WM_KILLFOCUS: 1248 /* 1249 * if the pointer is currently hidden, then we should show it. 1250 */ 1251 gui_mch_mousehide(FALSE); 1252 break; 1253 } 1254 } 1255 1256 static LRESULT CALLBACK 1257 _TextAreaWndProc( 1258 HWND hwnd, 1259 UINT uMsg, 1260 WPARAM wParam, 1261 LPARAM lParam) 1262 { 1263 /* 1264 TRACE("TextAreaWndProc: hwnd = %08x, msg = %x, wParam = %x, lParam = %x\n", 1265 hwnd, uMsg, wParam, lParam); 1266 */ 1267 1268 HandleMouseHide(uMsg, lParam); 1269 1270 s_uMsg = uMsg; 1271 s_wParam = wParam; 1272 s_lParam = lParam; 1273 1274 #ifdef FEAT_BEVAL_GUI 1275 TrackUserActivity(uMsg); 1276 #endif 1277 1278 switch (uMsg) 1279 { 1280 HANDLE_MSG(hwnd, WM_LBUTTONDBLCLK,_OnMouseButtonDown); 1281 HANDLE_MSG(hwnd, WM_LBUTTONDOWN,_OnMouseButtonDown); 1282 HANDLE_MSG(hwnd, WM_LBUTTONUP, _OnMouseMoveOrRelease); 1283 HANDLE_MSG(hwnd, WM_MBUTTONDBLCLK,_OnMouseButtonDown); 1284 HANDLE_MSG(hwnd, WM_MBUTTONDOWN,_OnMouseButtonDown); 1285 HANDLE_MSG(hwnd, WM_MBUTTONUP, _OnMouseMoveOrRelease); 1286 HANDLE_MSG(hwnd, WM_MOUSEMOVE, _OnMouseMoveOrRelease); 1287 HANDLE_MSG(hwnd, WM_PAINT, _OnPaint); 1288 HANDLE_MSG(hwnd, WM_RBUTTONDBLCLK,_OnMouseButtonDown); 1289 HANDLE_MSG(hwnd, WM_RBUTTONDOWN,_OnMouseButtonDown); 1290 HANDLE_MSG(hwnd, WM_RBUTTONUP, _OnMouseMoveOrRelease); 1291 HANDLE_MSG(hwnd, WM_XBUTTONDBLCLK,_OnMouseButtonDown); 1292 HANDLE_MSG(hwnd, WM_XBUTTONDOWN,_OnMouseButtonDown); 1293 HANDLE_MSG(hwnd, WM_XBUTTONUP, _OnMouseMoveOrRelease); 1294 HANDLE_MSG(hwnd, WM_SIZE, _OnSizeTextArea); 1295 1296 #ifdef FEAT_BEVAL_GUI 1297 case WM_NOTIFY: Handle_WM_Notify(hwnd, (LPNMHDR)lParam); 1298 return TRUE; 1299 #endif 1300 default: 1301 return MyWindowProc(hwnd, uMsg, wParam, lParam); 1302 } 1303 } 1304 1305 #ifdef PROTO 1306 typedef int WINAPI; 1307 #endif 1308 1309 LRESULT WINAPI 1310 vim_WindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) 1311 { 1312 #ifdef GLOBAL_IME 1313 return global_ime_DefWindowProc(hwnd, message, wParam, lParam); 1314 #else 1315 if (wide_WindowProc) 1316 return DefWindowProcW(hwnd, message, wParam, lParam); 1317 return DefWindowProc(hwnd, message, wParam, lParam); 1318 #endif 1319 } 1320 1321 /* 1322 * Called when the foreground or background color has been changed. 1323 */ 1324 void 1325 gui_mch_new_colors(void) 1326 { 1327 /* nothing to do? */ 1328 } 1329 1330 /* 1331 * Set the colors to their default values. 1332 */ 1333 void 1334 gui_mch_def_colors(void) 1335 { 1336 gui.norm_pixel = GetSysColor(COLOR_WINDOWTEXT); 1337 gui.back_pixel = GetSysColor(COLOR_WINDOW); 1338 gui.def_norm_pixel = gui.norm_pixel; 1339 gui.def_back_pixel = gui.back_pixel; 1340 } 1341 1342 /* 1343 * Open the GUI window which was created by a call to gui_mch_init(). 1344 */ 1345 int 1346 gui_mch_open(void) 1347 { 1348 #ifndef SW_SHOWDEFAULT 1349 # define SW_SHOWDEFAULT 10 /* Borland 5.0 doesn't have it */ 1350 #endif 1351 /* Actually open the window, if not already visible 1352 * (may be done already in gui_mch_set_shellsize) */ 1353 if (!IsWindowVisible(s_hwnd)) 1354 ShowWindow(s_hwnd, SW_SHOWDEFAULT); 1355 1356 #ifdef MSWIN_FIND_REPLACE 1357 /* Init replace string here, so that we keep it when re-opening the 1358 * dialog. */ 1359 s_findrep_struct.lpstrReplaceWith[0] = NUL; 1360 #endif 1361 1362 return OK; 1363 } 1364 1365 /* 1366 * Get the position of the top left corner of the window. 1367 */ 1368 int 1369 gui_mch_get_winpos(int *x, int *y) 1370 { 1371 RECT rect; 1372 1373 GetWindowRect(s_hwnd, &rect); 1374 *x = rect.left; 1375 *y = rect.top; 1376 return OK; 1377 } 1378 1379 /* 1380 * Set the position of the top left corner of the window to the given 1381 * coordinates. 1382 */ 1383 void 1384 gui_mch_set_winpos(int x, int y) 1385 { 1386 SetWindowPos(s_hwnd, NULL, x, y, 0, 0, 1387 SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE); 1388 } 1389 void 1390 gui_mch_set_text_area_pos(int x, int y, int w, int h) 1391 { 1392 static int oldx = 0; 1393 static int oldy = 0; 1394 1395 SetWindowPos(s_textArea, NULL, x, y, w, h, SWP_NOZORDER | SWP_NOACTIVATE); 1396 1397 #ifdef FEAT_TOOLBAR 1398 if (vim_strchr(p_go, GO_TOOLBAR) != NULL) 1399 SendMessage(s_toolbarhwnd, WM_SIZE, 1400 (WPARAM)0, (LPARAM)(w + ((long)(TOOLBAR_BUTTON_HEIGHT+8)<<16))); 1401 #endif 1402 #if defined(FEAT_GUI_TABLINE) 1403 if (showing_tabline) 1404 { 1405 int top = 0; 1406 RECT rect; 1407 1408 # ifdef FEAT_TOOLBAR 1409 if (vim_strchr(p_go, GO_TOOLBAR) != NULL) 1410 top = TOOLBAR_BUTTON_HEIGHT + TOOLBAR_BORDER_HEIGHT; 1411 # endif 1412 GetClientRect(s_hwnd, &rect); 1413 MoveWindow(s_tabhwnd, 0, top, rect.right, gui.tabline_height, TRUE); 1414 } 1415 #endif 1416 1417 /* When side scroll bar is unshown, the size of window will change. 1418 * then, the text area move left or right. thus client rect should be 1419 * forcedly redrawn. (Yasuhiro Matsumoto) */ 1420 if (oldx != x || oldy != y) 1421 { 1422 InvalidateRect(s_hwnd, NULL, FALSE); 1423 oldx = x; 1424 oldy = y; 1425 } 1426 } 1427 1428 1429 /* 1430 * Scrollbar stuff: 1431 */ 1432 1433 void 1434 gui_mch_enable_scrollbar( 1435 scrollbar_T *sb, 1436 int flag) 1437 { 1438 ShowScrollBar(sb->id, SB_CTL, flag); 1439 1440 /* TODO: When the window is maximized, the size of the window stays the 1441 * same, thus the size of the text area changes. On Win98 it's OK, on Win 1442 * NT 4.0 it's not... */ 1443 } 1444 1445 void 1446 gui_mch_set_scrollbar_pos( 1447 scrollbar_T *sb, 1448 int x, 1449 int y, 1450 int w, 1451 int h) 1452 { 1453 SetWindowPos(sb->id, NULL, x, y, w, h, 1454 SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW); 1455 } 1456 1457 void 1458 gui_mch_create_scrollbar( 1459 scrollbar_T *sb, 1460 int orient) /* SBAR_VERT or SBAR_HORIZ */ 1461 { 1462 sb->id = CreateWindow( 1463 "SCROLLBAR", "Scrollbar", 1464 WS_CHILD | ((orient == SBAR_VERT) ? SBS_VERT : SBS_HORZ), 0, 0, 1465 10, /* Any value will do for now */ 1466 10, /* Any value will do for now */ 1467 s_hwnd, NULL, 1468 s_hinst, NULL); 1469 } 1470 1471 /* 1472 * Find the scrollbar with the given hwnd. 1473 */ 1474 static scrollbar_T * 1475 gui_mswin_find_scrollbar(HWND hwnd) 1476 { 1477 win_T *wp; 1478 1479 if (gui.bottom_sbar.id == hwnd) 1480 return &gui.bottom_sbar; 1481 FOR_ALL_WINDOWS(wp) 1482 { 1483 if (wp->w_scrollbars[SBAR_LEFT].id == hwnd) 1484 return &wp->w_scrollbars[SBAR_LEFT]; 1485 if (wp->w_scrollbars[SBAR_RIGHT].id == hwnd) 1486 return &wp->w_scrollbars[SBAR_RIGHT]; 1487 } 1488 return NULL; 1489 } 1490 1491 /* 1492 * Get the character size of a font. 1493 */ 1494 static void 1495 GetFontSize(GuiFont font) 1496 { 1497 HWND hwnd = GetDesktopWindow(); 1498 HDC hdc = GetWindowDC(hwnd); 1499 HFONT hfntOld = SelectFont(hdc, (HFONT)font); 1500 TEXTMETRIC tm; 1501 1502 GetTextMetrics(hdc, &tm); 1503 gui.char_width = tm.tmAveCharWidth + tm.tmOverhang; 1504 1505 gui.char_height = tm.tmHeight + p_linespace; 1506 1507 SelectFont(hdc, hfntOld); 1508 1509 ReleaseDC(hwnd, hdc); 1510 } 1511 1512 /* 1513 * Adjust gui.char_height (after 'linespace' was changed). 1514 */ 1515 int 1516 gui_mch_adjust_charheight(void) 1517 { 1518 GetFontSize(gui.norm_font); 1519 return OK; 1520 } 1521 1522 static GuiFont 1523 get_font_handle(LOGFONT *lf) 1524 { 1525 HFONT font = NULL; 1526 1527 /* Load the font */ 1528 font = CreateFontIndirect(lf); 1529 1530 if (font == NULL) 1531 return NOFONT; 1532 1533 return (GuiFont)font; 1534 } 1535 1536 static int 1537 pixels_to_points(int pixels, int vertical) 1538 { 1539 int points; 1540 HWND hwnd; 1541 HDC hdc; 1542 1543 hwnd = GetDesktopWindow(); 1544 hdc = GetWindowDC(hwnd); 1545 1546 points = MulDiv(pixels, 72, 1547 GetDeviceCaps(hdc, vertical ? LOGPIXELSY : LOGPIXELSX)); 1548 1549 ReleaseDC(hwnd, hdc); 1550 1551 return points; 1552 } 1553 1554 GuiFont 1555 gui_mch_get_font( 1556 char_u *name, 1557 int giveErrorIfMissing) 1558 { 1559 LOGFONT lf; 1560 GuiFont font = NOFONT; 1561 1562 if (get_logfont(&lf, name, NULL, giveErrorIfMissing) == OK) 1563 font = get_font_handle(&lf); 1564 if (font == NOFONT && giveErrorIfMissing) 1565 semsg(_(e_font), name); 1566 return font; 1567 } 1568 1569 #if defined(FEAT_EVAL) || defined(PROTO) 1570 /* 1571 * Return the name of font "font" in allocated memory. 1572 * Don't know how to get the actual name, thus use the provided name. 1573 */ 1574 char_u * 1575 gui_mch_get_fontname(GuiFont font UNUSED, char_u *name) 1576 { 1577 if (name == NULL) 1578 return NULL; 1579 return vim_strsave(name); 1580 } 1581 #endif 1582 1583 void 1584 gui_mch_free_font(GuiFont font) 1585 { 1586 if (font) 1587 DeleteObject((HFONT)font); 1588 } 1589 1590 /* 1591 * Return the Pixel value (color) for the given color name. 1592 * Return INVALCOLOR for error. 1593 */ 1594 guicolor_T 1595 gui_mch_get_color(char_u *name) 1596 { 1597 int i; 1598 1599 typedef struct SysColorTable 1600 { 1601 char *name; 1602 int color; 1603 } SysColorTable; 1604 1605 static SysColorTable sys_table[] = 1606 { 1607 {"SYS_3DDKSHADOW", COLOR_3DDKSHADOW}, 1608 {"SYS_3DHILIGHT", COLOR_3DHILIGHT}, 1609 #ifdef COLOR_3DHIGHLIGHT 1610 {"SYS_3DHIGHLIGHT", COLOR_3DHIGHLIGHT}, 1611 #endif 1612 {"SYS_BTNHILIGHT", COLOR_BTNHILIGHT}, 1613 {"SYS_BTNHIGHLIGHT", COLOR_BTNHIGHLIGHT}, 1614 {"SYS_3DLIGHT", COLOR_3DLIGHT}, 1615 {"SYS_3DSHADOW", COLOR_3DSHADOW}, 1616 {"SYS_DESKTOP", COLOR_DESKTOP}, 1617 {"SYS_INFOBK", COLOR_INFOBK}, 1618 {"SYS_INFOTEXT", COLOR_INFOTEXT}, 1619 {"SYS_3DFACE", COLOR_3DFACE}, 1620 {"SYS_BTNFACE", COLOR_BTNFACE}, 1621 {"SYS_BTNSHADOW", COLOR_BTNSHADOW}, 1622 {"SYS_ACTIVEBORDER", COLOR_ACTIVEBORDER}, 1623 {"SYS_ACTIVECAPTION", COLOR_ACTIVECAPTION}, 1624 {"SYS_APPWORKSPACE", COLOR_APPWORKSPACE}, 1625 {"SYS_BACKGROUND", COLOR_BACKGROUND}, 1626 {"SYS_BTNTEXT", COLOR_BTNTEXT}, 1627 {"SYS_CAPTIONTEXT", COLOR_CAPTIONTEXT}, 1628 {"SYS_GRAYTEXT", COLOR_GRAYTEXT}, 1629 {"SYS_HIGHLIGHT", COLOR_HIGHLIGHT}, 1630 {"SYS_HIGHLIGHTTEXT", COLOR_HIGHLIGHTTEXT}, 1631 {"SYS_INACTIVEBORDER", COLOR_INACTIVEBORDER}, 1632 {"SYS_INACTIVECAPTION", COLOR_INACTIVECAPTION}, 1633 {"SYS_INACTIVECAPTIONTEXT", COLOR_INACTIVECAPTIONTEXT}, 1634 {"SYS_MENU", COLOR_MENU}, 1635 {"SYS_MENUTEXT", COLOR_MENUTEXT}, 1636 {"SYS_SCROLLBAR", COLOR_SCROLLBAR}, 1637 {"SYS_WINDOW", COLOR_WINDOW}, 1638 {"SYS_WINDOWFRAME", COLOR_WINDOWFRAME}, 1639 {"SYS_WINDOWTEXT", COLOR_WINDOWTEXT} 1640 }; 1641 1642 /* 1643 * Try to look up a system colour. 1644 */ 1645 for (i = 0; i < sizeof(sys_table) / sizeof(sys_table[0]); i++) 1646 if (STRICMP(name, sys_table[i].name) == 0) 1647 return GetSysColor(sys_table[i].color); 1648 1649 return gui_get_color_cmn(name); 1650 } 1651 1652 guicolor_T 1653 gui_mch_get_rgb_color(int r, int g, int b) 1654 { 1655 return gui_get_rgb_color_cmn(r, g, b); 1656 } 1657 1658 /* 1659 * Return OK if the key with the termcap name "name" is supported. 1660 */ 1661 int 1662 gui_mch_haskey(char_u *name) 1663 { 1664 int i; 1665 1666 for (i = 0; special_keys[i].vim_code1 != NUL; i++) 1667 if (name[0] == special_keys[i].vim_code0 && 1668 name[1] == special_keys[i].vim_code1) 1669 return OK; 1670 return FAIL; 1671 } 1672 1673 void 1674 gui_mch_beep(void) 1675 { 1676 MessageBeep(MB_OK); 1677 } 1678 /* 1679 * Invert a rectangle from row r, column c, for nr rows and nc columns. 1680 */ 1681 void 1682 gui_mch_invert_rectangle( 1683 int r, 1684 int c, 1685 int nr, 1686 int nc) 1687 { 1688 RECT rc; 1689 1690 #if defined(FEAT_DIRECTX) 1691 if (IS_ENABLE_DIRECTX()) 1692 DWriteContext_Flush(s_dwc); 1693 #endif 1694 1695 /* 1696 * Note: InvertRect() excludes right and bottom of rectangle. 1697 */ 1698 rc.left = FILL_X(c); 1699 rc.top = FILL_Y(r); 1700 rc.right = rc.left + nc * gui.char_width; 1701 rc.bottom = rc.top + nr * gui.char_height; 1702 InvertRect(s_hdc, &rc); 1703 } 1704 1705 /* 1706 * Iconify the GUI window. 1707 */ 1708 void 1709 gui_mch_iconify(void) 1710 { 1711 ShowWindow(s_hwnd, SW_MINIMIZE); 1712 } 1713 1714 /* 1715 * Draw a cursor without focus. 1716 */ 1717 void 1718 gui_mch_draw_hollow_cursor(guicolor_T color) 1719 { 1720 HBRUSH hbr; 1721 RECT rc; 1722 1723 #if defined(FEAT_DIRECTX) 1724 if (IS_ENABLE_DIRECTX()) 1725 DWriteContext_Flush(s_dwc); 1726 #endif 1727 1728 /* 1729 * Note: FrameRect() excludes right and bottom of rectangle. 1730 */ 1731 rc.left = FILL_X(gui.col); 1732 rc.top = FILL_Y(gui.row); 1733 rc.right = rc.left + gui.char_width; 1734 if (mb_lefthalve(gui.row, gui.col)) 1735 rc.right += gui.char_width; 1736 rc.bottom = rc.top + gui.char_height; 1737 hbr = CreateSolidBrush(color); 1738 FrameRect(s_hdc, &rc, hbr); 1739 DeleteBrush(hbr); 1740 } 1741 /* 1742 * Draw part of a cursor, "w" pixels wide, and "h" pixels high, using 1743 * color "color". 1744 */ 1745 void 1746 gui_mch_draw_part_cursor( 1747 int w, 1748 int h, 1749 guicolor_T color) 1750 { 1751 RECT rc; 1752 1753 /* 1754 * Note: FillRect() excludes right and bottom of rectangle. 1755 */ 1756 rc.left = 1757 #ifdef FEAT_RIGHTLEFT 1758 /* vertical line should be on the right of current point */ 1759 CURSOR_BAR_RIGHT ? FILL_X(gui.col + 1) - w : 1760 #endif 1761 FILL_X(gui.col); 1762 rc.top = FILL_Y(gui.row) + gui.char_height - h; 1763 rc.right = rc.left + w; 1764 rc.bottom = rc.top + h; 1765 1766 fill_rect(&rc, NULL, color); 1767 } 1768 1769 1770 /* 1771 * Generates a VK_SPACE when the internal dead_key flag is set to output the 1772 * dead key's nominal character and re-post the original message. 1773 */ 1774 static void 1775 outputDeadKey_rePost(MSG originalMsg) 1776 { 1777 static MSG deadCharExpel; 1778 1779 if (!dead_key) 1780 return; 1781 1782 dead_key = 0; 1783 1784 /* Make Windows generate the dead key's character */ 1785 deadCharExpel.message = originalMsg.message; 1786 deadCharExpel.hwnd = originalMsg.hwnd; 1787 deadCharExpel.wParam = VK_SPACE; 1788 1789 MyTranslateMessage(&deadCharExpel); 1790 1791 /* re-generate the current character free of the dead char influence */ 1792 PostMessage(originalMsg.hwnd, originalMsg.message, originalMsg.wParam, 1793 originalMsg.lParam); 1794 } 1795 1796 1797 /* 1798 * Process a single Windows message. 1799 * If one is not available we hang until one is. 1800 */ 1801 static void 1802 process_message(void) 1803 { 1804 MSG msg; 1805 UINT vk = 0; /* Virtual key */ 1806 char_u string[40]; 1807 int i; 1808 int modifiers = 0; 1809 int key; 1810 #ifdef FEAT_MENU 1811 static char_u k10[] = {K_SPECIAL, 'k', ';', 0}; 1812 #endif 1813 1814 pGetMessage(&msg, NULL, 0, 0); 1815 1816 #ifdef FEAT_OLE 1817 /* Look after OLE Automation commands */ 1818 if (msg.message == WM_OLE) 1819 { 1820 char_u *str = (char_u *)msg.lParam; 1821 if (str == NULL || *str == NUL) 1822 { 1823 /* Message can't be ours, forward it. Fixes problem with Ultramon 1824 * 3.0.4 */ 1825 pDispatchMessage(&msg); 1826 } 1827 else 1828 { 1829 add_to_input_buf(str, (int)STRLEN(str)); 1830 vim_free(str); /* was allocated in CVim::SendKeys() */ 1831 } 1832 return; 1833 } 1834 #endif 1835 1836 #ifdef MSWIN_FIND_REPLACE 1837 /* Don't process messages used by the dialog */ 1838 if (s_findrep_hwnd != NULL && pIsDialogMessage(s_findrep_hwnd, &msg)) 1839 { 1840 HandleMouseHide(msg.message, msg.lParam); 1841 return; 1842 } 1843 #endif 1844 1845 /* 1846 * Check if it's a special key that we recognise. If not, call 1847 * TranslateMessage(). 1848 */ 1849 if (msg.message == WM_KEYDOWN || msg.message == WM_SYSKEYDOWN) 1850 { 1851 vk = (int) msg.wParam; 1852 1853 /* 1854 * Handle dead keys in special conditions in other cases we let Windows 1855 * handle them and do not interfere. 1856 * 1857 * The dead_key flag must be reset on several occasions: 1858 * - in _OnChar() (or _OnSysChar()) as any dead key was necessarily 1859 * consumed at that point (This is when we let Windows combine the 1860 * dead character on its own) 1861 * 1862 * - Before doing something special such as regenerating keypresses to 1863 * expel the dead character as this could trigger an infinite loop if 1864 * for some reason MyTranslateMessage() do not trigger a call 1865 * immediately to _OnChar() (or _OnSysChar()). 1866 */ 1867 if (dead_key) 1868 { 1869 /* 1870 * If a dead key was pressed and the user presses VK_SPACE, 1871 * VK_BACK, or VK_ESCAPE it means that he actually wants to deal 1872 * with the dead char now, so do nothing special and let Windows 1873 * handle it. 1874 * 1875 * Note that VK_SPACE combines with the dead_key's character and 1876 * only one WM_CHAR will be generated by TranslateMessage(), in 1877 * the two other cases two WM_CHAR will be generated: the dead 1878 * char and VK_BACK or VK_ESCAPE. That is most likely what the 1879 * user expects. 1880 */ 1881 if ((vk == VK_SPACE || vk == VK_BACK || vk == VK_ESCAPE)) 1882 { 1883 dead_key = 0; 1884 MyTranslateMessage(&msg); 1885 return; 1886 } 1887 /* In modes where we are not typing, dead keys should behave 1888 * normally */ 1889 else if (!(get_real_state() & (INSERT | CMDLINE | SELECTMODE))) 1890 { 1891 outputDeadKey_rePost(msg); 1892 return; 1893 } 1894 } 1895 1896 /* Check for CTRL-BREAK */ 1897 if (vk == VK_CANCEL) 1898 { 1899 trash_input_buf(); 1900 got_int = TRUE; 1901 ctrl_break_was_pressed = TRUE; 1902 string[0] = Ctrl_C; 1903 add_to_input_buf(string, 1); 1904 } 1905 1906 for (i = 0; special_keys[i].key_sym != 0; i++) 1907 { 1908 /* ignore VK_SPACE when ALT key pressed: system menu */ 1909 if (special_keys[i].key_sym == vk 1910 && (vk != VK_SPACE || !(GetKeyState(VK_MENU) & 0x8000))) 1911 { 1912 /* 1913 * Behave as expected if we have a dead key and the special key 1914 * is a key that would normally trigger the dead key nominal 1915 * character output (such as a NUMPAD printable character or 1916 * the TAB key, etc...). 1917 */ 1918 if (dead_key && (special_keys[i].vim_code0 == 'K' 1919 || vk == VK_TAB || vk == CAR)) 1920 { 1921 outputDeadKey_rePost(msg); 1922 return; 1923 } 1924 1925 #ifdef FEAT_MENU 1926 /* Check for <F10>: Windows selects the menu. When <F10> is 1927 * mapped we want to use the mapping instead. */ 1928 if (vk == VK_F10 1929 && gui.menu_is_active 1930 && check_map(k10, State, FALSE, TRUE, FALSE, 1931 NULL, NULL) == NULL) 1932 break; 1933 #endif 1934 if (GetKeyState(VK_SHIFT) & 0x8000) 1935 modifiers |= MOD_MASK_SHIFT; 1936 /* 1937 * Don't use caps-lock as shift, because these are special keys 1938 * being considered here, and we only want letters to get 1939 * shifted -- webb 1940 */ 1941 /* 1942 if (GetKeyState(VK_CAPITAL) & 0x0001) 1943 modifiers ^= MOD_MASK_SHIFT; 1944 */ 1945 if (GetKeyState(VK_CONTROL) & 0x8000) 1946 modifiers |= MOD_MASK_CTRL; 1947 if (GetKeyState(VK_MENU) & 0x8000) 1948 modifiers |= MOD_MASK_ALT; 1949 1950 if (special_keys[i].vim_code1 == NUL) 1951 key = special_keys[i].vim_code0; 1952 else 1953 key = TO_SPECIAL(special_keys[i].vim_code0, 1954 special_keys[i].vim_code1); 1955 key = simplify_key(key, &modifiers); 1956 if (key == CSI) 1957 key = K_CSI; 1958 1959 if (modifiers) 1960 { 1961 string[0] = CSI; 1962 string[1] = KS_MODIFIER; 1963 string[2] = modifiers; 1964 add_to_input_buf(string, 3); 1965 } 1966 1967 if (IS_SPECIAL(key)) 1968 { 1969 string[0] = CSI; 1970 string[1] = K_SECOND(key); 1971 string[2] = K_THIRD(key); 1972 add_to_input_buf(string, 3); 1973 } 1974 else 1975 { 1976 int len; 1977 1978 /* Handle "key" as a Unicode character. */ 1979 len = char_to_string(key, string, 40, FALSE); 1980 add_to_input_buf(string, len); 1981 } 1982 break; 1983 } 1984 } 1985 if (special_keys[i].key_sym == 0) 1986 { 1987 /* Some keys need C-S- where they should only need C-. 1988 * Ignore 0xff, Windows XP sends it when NUMLOCK has changed since 1989 * system startup (Helmut Stiegler, 2003 Oct 3). */ 1990 if (vk != 0xff 1991 && (GetKeyState(VK_CONTROL) & 0x8000) 1992 && !(GetKeyState(VK_SHIFT) & 0x8000) 1993 && !(GetKeyState(VK_MENU) & 0x8000)) 1994 { 1995 /* CTRL-6 is '^'; Japanese keyboard maps '^' to vk == 0xDE */ 1996 if (vk == '6' || MapVirtualKey(vk, 2) == (UINT)'^') 1997 { 1998 string[0] = Ctrl_HAT; 1999 add_to_input_buf(string, 1); 2000 } 2001 /* vk == 0xBD AZERTY for CTRL-'-', but CTRL-[ for * QWERTY! */ 2002 else if (vk == 0xBD) /* QWERTY for CTRL-'-' */ 2003 { 2004 string[0] = Ctrl__; 2005 add_to_input_buf(string, 1); 2006 } 2007 /* CTRL-2 is '@'; Japanese keyboard maps '@' to vk == 0xC0 */ 2008 else if (vk == '2' || MapVirtualKey(vk, 2) == (UINT)'@') 2009 { 2010 string[0] = Ctrl_AT; 2011 add_to_input_buf(string, 1); 2012 } 2013 else 2014 MyTranslateMessage(&msg); 2015 } 2016 else 2017 MyTranslateMessage(&msg); 2018 } 2019 } 2020 #ifdef FEAT_MBYTE_IME 2021 else if (msg.message == WM_IME_NOTIFY) 2022 _OnImeNotify(msg.hwnd, (DWORD)msg.wParam, (DWORD)msg.lParam); 2023 else if (msg.message == WM_KEYUP && im_get_status()) 2024 /* added for non-MS IME (Yasuhiro Matsumoto) */ 2025 MyTranslateMessage(&msg); 2026 #endif 2027 #if !defined(FEAT_MBYTE_IME) && defined(GLOBAL_IME) 2028 /* GIME_TEST */ 2029 else if (msg.message == WM_IME_STARTCOMPOSITION) 2030 { 2031 POINT point; 2032 2033 global_ime_set_font(&norm_logfont); 2034 point.x = FILL_X(gui.col); 2035 point.y = FILL_Y(gui.row); 2036 MapWindowPoints(s_textArea, s_hwnd, &point, 1); 2037 global_ime_set_position(&point); 2038 } 2039 #endif 2040 2041 #ifdef FEAT_MENU 2042 /* Check for <F10>: Default effect is to select the menu. When <F10> is 2043 * mapped we need to stop it here to avoid strange effects (e.g., for the 2044 * key-up event) */ 2045 if (vk != VK_F10 || check_map(k10, State, FALSE, TRUE, FALSE, 2046 NULL, NULL) == NULL) 2047 #endif 2048 pDispatchMessage(&msg); 2049 } 2050 2051 /* 2052 * Catch up with any queued events. This may put keyboard input into the 2053 * input buffer, call resize call-backs, trigger timers etc. If there is 2054 * nothing in the event queue (& no timers pending), then we return 2055 * immediately. 2056 */ 2057 void 2058 gui_mch_update(void) 2059 { 2060 MSG msg; 2061 2062 if (!s_busy_processing) 2063 while (pPeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE) 2064 && !vim_is_input_buf_full()) 2065 process_message(); 2066 } 2067 2068 static void 2069 remove_any_timer(void) 2070 { 2071 MSG msg; 2072 2073 if (s_wait_timer != 0 && !s_timed_out) 2074 { 2075 KillTimer(NULL, s_wait_timer); 2076 2077 /* Eat spurious WM_TIMER messages */ 2078 while (pPeekMessage(&msg, s_hwnd, WM_TIMER, WM_TIMER, PM_REMOVE)) 2079 ; 2080 s_wait_timer = 0; 2081 } 2082 } 2083 2084 /* 2085 * GUI input routine called by gui_wait_for_chars(). Waits for a character 2086 * from the keyboard. 2087 * wtime == -1 Wait forever. 2088 * wtime == 0 This should never happen. 2089 * wtime > 0 Wait wtime milliseconds for a character. 2090 * Returns OK if a character was found to be available within the given time, 2091 * or FAIL otherwise. 2092 */ 2093 int 2094 gui_mch_wait_for_chars(int wtime) 2095 { 2096 int focus; 2097 2098 s_timed_out = FALSE; 2099 2100 if (wtime >= 0) 2101 { 2102 // Don't do anything while processing a (scroll) message. 2103 if (s_busy_processing) 2104 return FAIL; 2105 2106 // When called with "wtime" zero, just want one msec. 2107 s_wait_timer = (UINT)SetTimer(NULL, 0, (UINT)(wtime == 0 ? 1 : wtime), 2108 (TIMERPROC)_OnTimer); 2109 } 2110 2111 allow_scrollbar = TRUE; 2112 2113 focus = gui.in_focus; 2114 while (!s_timed_out) 2115 { 2116 /* Stop or start blinking when focus changes */ 2117 if (gui.in_focus != focus) 2118 { 2119 if (gui.in_focus) 2120 gui_mch_start_blink(); 2121 else 2122 gui_mch_stop_blink(TRUE); 2123 focus = gui.in_focus; 2124 } 2125 2126 if (s_need_activate) 2127 { 2128 (void)SetForegroundWindow(s_hwnd); 2129 s_need_activate = FALSE; 2130 } 2131 2132 #ifdef FEAT_TIMERS 2133 did_add_timer = FALSE; 2134 #endif 2135 #ifdef MESSAGE_QUEUE 2136 /* Check channel I/O while waiting for a message. */ 2137 for (;;) 2138 { 2139 MSG msg; 2140 2141 parse_queued_messages(); 2142 2143 if (pPeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) 2144 { 2145 process_message(); 2146 break; 2147 } 2148 else if (MsgWaitForMultipleObjects(0, NULL, FALSE, 100, QS_ALLINPUT) 2149 != WAIT_TIMEOUT) 2150 break; 2151 } 2152 #else 2153 /* 2154 * Don't use gui_mch_update() because then we will spin-lock until a 2155 * char arrives, instead we use GetMessage() to hang until an 2156 * event arrives. No need to check for input_buf_full because we are 2157 * returning as soon as it contains a single char -- webb 2158 */ 2159 process_message(); 2160 #endif 2161 2162 if (input_available()) 2163 { 2164 remove_any_timer(); 2165 allow_scrollbar = FALSE; 2166 2167 /* Clear pending mouse button, the release event may have been 2168 * taken by the dialog window. But don't do this when getting 2169 * focus, we need the mouse-up event then. */ 2170 if (!s_getting_focus) 2171 s_button_pending = -1; 2172 2173 return OK; 2174 } 2175 2176 #ifdef FEAT_TIMERS 2177 if (did_add_timer) 2178 { 2179 /* Need to recompute the waiting time. */ 2180 remove_any_timer(); 2181 break; 2182 } 2183 #endif 2184 } 2185 allow_scrollbar = FALSE; 2186 return FAIL; 2187 } 2188 2189 /* 2190 * Clear a rectangular region of the screen from text pos (row1, col1) to 2191 * (row2, col2) inclusive. 2192 */ 2193 void 2194 gui_mch_clear_block( 2195 int row1, 2196 int col1, 2197 int row2, 2198 int col2) 2199 { 2200 RECT rc; 2201 2202 /* 2203 * Clear one extra pixel at the far right, for when bold characters have 2204 * spilled over to the window border. 2205 * Note: FillRect() excludes right and bottom of rectangle. 2206 */ 2207 rc.left = FILL_X(col1); 2208 rc.top = FILL_Y(row1); 2209 rc.right = FILL_X(col2 + 1) + (col2 == Columns - 1); 2210 rc.bottom = FILL_Y(row2 + 1); 2211 clear_rect(&rc); 2212 } 2213 2214 /* 2215 * Clear the whole text window. 2216 */ 2217 void 2218 gui_mch_clear_all(void) 2219 { 2220 RECT rc; 2221 2222 rc.left = 0; 2223 rc.top = 0; 2224 rc.right = Columns * gui.char_width + 2 * gui.border_width; 2225 rc.bottom = Rows * gui.char_height + 2 * gui.border_width; 2226 clear_rect(&rc); 2227 } 2228 /* 2229 * Menu stuff. 2230 */ 2231 2232 void 2233 gui_mch_enable_menu(int flag) 2234 { 2235 #ifdef FEAT_MENU 2236 SetMenu(s_hwnd, flag ? s_menuBar : NULL); 2237 #endif 2238 } 2239 2240 void 2241 gui_mch_set_menu_pos( 2242 int x UNUSED, 2243 int y UNUSED, 2244 int w UNUSED, 2245 int h UNUSED) 2246 { 2247 /* It will be in the right place anyway */ 2248 } 2249 2250 #if defined(FEAT_MENU) || defined(PROTO) 2251 /* 2252 * Make menu item hidden or not hidden 2253 */ 2254 void 2255 gui_mch_menu_hidden( 2256 vimmenu_T *menu, 2257 int hidden) 2258 { 2259 /* 2260 * This doesn't do what we want. Hmm, just grey the menu items for now. 2261 */ 2262 /* 2263 if (hidden) 2264 EnableMenuItem(s_menuBar, menu->id, MF_BYCOMMAND | MF_DISABLED); 2265 else 2266 EnableMenuItem(s_menuBar, menu->id, MF_BYCOMMAND | MF_ENABLED); 2267 */ 2268 gui_mch_menu_grey(menu, hidden); 2269 } 2270 2271 /* 2272 * This is called after setting all the menus to grey/hidden or not. 2273 */ 2274 void 2275 gui_mch_draw_menubar(void) 2276 { 2277 DrawMenuBar(s_hwnd); 2278 } 2279 #endif /*FEAT_MENU*/ 2280 2281 #ifndef PROTO 2282 void 2283 #ifdef VIMDLL 2284 _export 2285 #endif 2286 _cdecl 2287 SaveInst(HINSTANCE hInst) 2288 { 2289 s_hinst = hInst; 2290 } 2291 #endif 2292 2293 /* 2294 * Return the RGB value of a pixel as a long. 2295 */ 2296 guicolor_T 2297 gui_mch_get_rgb(guicolor_T pixel) 2298 { 2299 return (guicolor_T)((GetRValue(pixel) << 16) + (GetGValue(pixel) << 8) 2300 + GetBValue(pixel)); 2301 } 2302 2303 #if defined(FEAT_GUI_DIALOG) || defined(PROTO) 2304 /* Convert pixels in X to dialog units */ 2305 static WORD 2306 PixelToDialogX(int numPixels) 2307 { 2308 return (WORD)((numPixels * 4) / s_dlgfntwidth); 2309 } 2310 2311 /* Convert pixels in Y to dialog units */ 2312 static WORD 2313 PixelToDialogY(int numPixels) 2314 { 2315 return (WORD)((numPixels * 8) / s_dlgfntheight); 2316 } 2317 2318 /* Return the width in pixels of the given text in the given DC. */ 2319 static int 2320 GetTextWidth(HDC hdc, char_u *str, int len) 2321 { 2322 SIZE size; 2323 2324 GetTextExtentPoint(hdc, (LPCSTR)str, len, &size); 2325 return size.cx; 2326 } 2327 2328 /* 2329 * Return the width in pixels of the given text in the given DC, taking care 2330 * of 'encoding' to active codepage conversion. 2331 */ 2332 static int 2333 GetTextWidthEnc(HDC hdc, char_u *str, int len) 2334 { 2335 SIZE size; 2336 WCHAR *wstr; 2337 int n; 2338 int wlen = len; 2339 2340 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage) 2341 { 2342 /* 'encoding' differs from active codepage: convert text and use wide 2343 * function */ 2344 wstr = enc_to_utf16(str, &wlen); 2345 if (wstr != NULL) 2346 { 2347 n = GetTextExtentPointW(hdc, wstr, wlen, &size); 2348 vim_free(wstr); 2349 if (n) 2350 return size.cx; 2351 } 2352 } 2353 2354 return GetTextWidth(hdc, str, len); 2355 } 2356 2357 static void get_work_area(RECT *spi_rect); 2358 2359 /* 2360 * A quick little routine that will center one window over another, handy for 2361 * dialog boxes. Taken from the Win32SDK samples and modified for multiple 2362 * monitors. 2363 */ 2364 static BOOL 2365 CenterWindow( 2366 HWND hwndChild, 2367 HWND hwndParent) 2368 { 2369 HMONITOR mon; 2370 MONITORINFO moninfo; 2371 RECT rChild, rParent, rScreen; 2372 int wChild, hChild, wParent, hParent; 2373 int xNew, yNew; 2374 HDC hdc; 2375 2376 GetWindowRect(hwndChild, &rChild); 2377 wChild = rChild.right - rChild.left; 2378 hChild = rChild.bottom - rChild.top; 2379 2380 /* If Vim is minimized put the window in the middle of the screen. */ 2381 if (hwndParent == NULL || IsMinimized(hwndParent)) 2382 get_work_area(&rParent); 2383 else 2384 GetWindowRect(hwndParent, &rParent); 2385 wParent = rParent.right - rParent.left; 2386 hParent = rParent.bottom - rParent.top; 2387 2388 moninfo.cbSize = sizeof(MONITORINFO); 2389 mon = MonitorFromWindow(hwndChild, MONITOR_DEFAULTTOPRIMARY); 2390 if (mon != NULL && GetMonitorInfo(mon, &moninfo)) 2391 { 2392 rScreen = moninfo.rcWork; 2393 } 2394 else 2395 { 2396 hdc = GetDC(hwndChild); 2397 rScreen.left = 0; 2398 rScreen.top = 0; 2399 rScreen.right = GetDeviceCaps(hdc, HORZRES); 2400 rScreen.bottom = GetDeviceCaps(hdc, VERTRES); 2401 ReleaseDC(hwndChild, hdc); 2402 } 2403 2404 xNew = rParent.left + ((wParent - wChild) / 2); 2405 if (xNew < rScreen.left) 2406 xNew = rScreen.left; 2407 else if ((xNew + wChild) > rScreen.right) 2408 xNew = rScreen.right - wChild; 2409 2410 yNew = rParent.top + ((hParent - hChild) / 2); 2411 if (yNew < rScreen.top) 2412 yNew = rScreen.top; 2413 else if ((yNew + hChild) > rScreen.bottom) 2414 yNew = rScreen.bottom - hChild; 2415 2416 return SetWindowPos(hwndChild, NULL, xNew, yNew, 0, 0, 2417 SWP_NOSIZE | SWP_NOZORDER); 2418 } 2419 #endif /* FEAT_GUI_DIALOG */ 2420 2421 #if defined(FEAT_TOOLBAR) || defined(PROTO) 2422 void 2423 gui_mch_show_toolbar(int showit) 2424 { 2425 if (s_toolbarhwnd == NULL) 2426 return; 2427 2428 if (showit) 2429 { 2430 # ifndef TB_SETUNICODEFORMAT 2431 /* For older compilers. We assume this never changes. */ 2432 # define TB_SETUNICODEFORMAT 0x2005 2433 # endif 2434 /* Enable/disable unicode support */ 2435 int uu = (enc_codepage >= 0 && (int)GetACP() != enc_codepage); 2436 SendMessage(s_toolbarhwnd, TB_SETUNICODEFORMAT, (WPARAM)uu, (LPARAM)0); 2437 ShowWindow(s_toolbarhwnd, SW_SHOW); 2438 } 2439 else 2440 ShowWindow(s_toolbarhwnd, SW_HIDE); 2441 } 2442 2443 /* Then number of bitmaps is fixed. Exit is missing! */ 2444 #define TOOLBAR_BITMAP_COUNT 31 2445 2446 #endif 2447 2448 #if defined(FEAT_GUI_TABLINE) || defined(PROTO) 2449 static void 2450 add_tabline_popup_menu_entry(HMENU pmenu, UINT item_id, char_u *item_text) 2451 { 2452 WCHAR *wn = NULL; 2453 2454 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage) 2455 { 2456 /* 'encoding' differs from active codepage: convert menu name 2457 * and use wide function */ 2458 wn = enc_to_utf16(item_text, NULL); 2459 if (wn != NULL) 2460 { 2461 MENUITEMINFOW infow; 2462 2463 infow.cbSize = sizeof(infow); 2464 infow.fMask = MIIM_TYPE | MIIM_ID; 2465 infow.wID = item_id; 2466 infow.fType = MFT_STRING; 2467 infow.dwTypeData = wn; 2468 infow.cch = (UINT)wcslen(wn); 2469 InsertMenuItemW(pmenu, item_id, FALSE, &infow); 2470 vim_free(wn); 2471 } 2472 } 2473 2474 if (wn == NULL) 2475 { 2476 MENUITEMINFO info; 2477 2478 info.cbSize = sizeof(info); 2479 info.fMask = MIIM_TYPE | MIIM_ID; 2480 info.wID = item_id; 2481 info.fType = MFT_STRING; 2482 info.dwTypeData = (LPTSTR)item_text; 2483 info.cch = (UINT)STRLEN(item_text); 2484 InsertMenuItem(pmenu, item_id, FALSE, &info); 2485 } 2486 } 2487 2488 static void 2489 show_tabline_popup_menu(void) 2490 { 2491 HMENU tab_pmenu; 2492 long rval; 2493 POINT pt; 2494 2495 /* When ignoring events don't show the menu. */ 2496 if (hold_gui_events 2497 # ifdef FEAT_CMDWIN 2498 || cmdwin_type != 0 2499 # endif 2500 ) 2501 return; 2502 2503 tab_pmenu = CreatePopupMenu(); 2504 if (tab_pmenu == NULL) 2505 return; 2506 2507 if (first_tabpage->tp_next != NULL) 2508 add_tabline_popup_menu_entry(tab_pmenu, 2509 TABLINE_MENU_CLOSE, (char_u *)_("Close tab")); 2510 add_tabline_popup_menu_entry(tab_pmenu, 2511 TABLINE_MENU_NEW, (char_u *)_("New tab")); 2512 add_tabline_popup_menu_entry(tab_pmenu, 2513 TABLINE_MENU_OPEN, (char_u *)_("Open tab...")); 2514 2515 GetCursorPos(&pt); 2516 rval = TrackPopupMenuEx(tab_pmenu, TPM_RETURNCMD, pt.x, pt.y, s_tabhwnd, 2517 NULL); 2518 2519 DestroyMenu(tab_pmenu); 2520 2521 /* Add the string cmd into input buffer */ 2522 if (rval > 0) 2523 { 2524 TCHITTESTINFO htinfo; 2525 int idx; 2526 2527 if (ScreenToClient(s_tabhwnd, &pt) == 0) 2528 return; 2529 2530 htinfo.pt.x = pt.x; 2531 htinfo.pt.y = pt.y; 2532 idx = TabCtrl_HitTest(s_tabhwnd, &htinfo); 2533 if (idx == -1) 2534 idx = 0; 2535 else 2536 idx += 1; 2537 2538 send_tabline_menu_event(idx, (int)rval); 2539 } 2540 } 2541 2542 /* 2543 * Show or hide the tabline. 2544 */ 2545 void 2546 gui_mch_show_tabline(int showit) 2547 { 2548 if (s_tabhwnd == NULL) 2549 return; 2550 2551 if (!showit != !showing_tabline) 2552 { 2553 if (showit) 2554 ShowWindow(s_tabhwnd, SW_SHOW); 2555 else 2556 ShowWindow(s_tabhwnd, SW_HIDE); 2557 showing_tabline = showit; 2558 } 2559 } 2560 2561 /* 2562 * Return TRUE when tabline is displayed. 2563 */ 2564 int 2565 gui_mch_showing_tabline(void) 2566 { 2567 return s_tabhwnd != NULL && showing_tabline; 2568 } 2569 2570 /* 2571 * Update the labels of the tabline. 2572 */ 2573 void 2574 gui_mch_update_tabline(void) 2575 { 2576 tabpage_T *tp; 2577 TCITEM tie; 2578 int nr = 0; 2579 int curtabidx = 0; 2580 int tabadded = 0; 2581 static int use_unicode = FALSE; 2582 int uu; 2583 WCHAR *wstr = NULL; 2584 2585 if (s_tabhwnd == NULL) 2586 return; 2587 2588 #ifndef CCM_SETUNICODEFORMAT 2589 /* For older compilers. We assume this never changes. */ 2590 # define CCM_SETUNICODEFORMAT 0x2005 2591 #endif 2592 uu = (enc_codepage >= 0 && (int)GetACP() != enc_codepage); 2593 if (uu != use_unicode) 2594 { 2595 /* Enable/disable unicode support */ 2596 SendMessage(s_tabhwnd, CCM_SETUNICODEFORMAT, (WPARAM)uu, (LPARAM)0); 2597 use_unicode = uu; 2598 } 2599 2600 tie.mask = TCIF_TEXT; 2601 tie.iImage = -1; 2602 2603 /* Disable redraw for tab updates to eliminate O(N^2) draws. */ 2604 SendMessage(s_tabhwnd, WM_SETREDRAW, (WPARAM)FALSE, 0); 2605 2606 /* Add a label for each tab page. They all contain the same text area. */ 2607 for (tp = first_tabpage; tp != NULL; tp = tp->tp_next, ++nr) 2608 { 2609 if (tp == curtab) 2610 curtabidx = nr; 2611 2612 if (nr >= TabCtrl_GetItemCount(s_tabhwnd)) 2613 { 2614 /* Add the tab */ 2615 tie.pszText = "-Empty-"; 2616 TabCtrl_InsertItem(s_tabhwnd, nr, &tie); 2617 tabadded = 1; 2618 } 2619 2620 get_tabline_label(tp, FALSE); 2621 tie.pszText = (LPSTR)NameBuff; 2622 wstr = NULL; 2623 if (use_unicode) 2624 { 2625 /* Need to go through Unicode. */ 2626 wstr = enc_to_utf16(NameBuff, NULL); 2627 if (wstr != NULL) 2628 { 2629 TCITEMW tiw; 2630 2631 tiw.mask = TCIF_TEXT; 2632 tiw.iImage = -1; 2633 tiw.pszText = wstr; 2634 SendMessage(s_tabhwnd, TCM_SETITEMW, (WPARAM)nr, (LPARAM)&tiw); 2635 vim_free(wstr); 2636 } 2637 } 2638 if (wstr == NULL) 2639 { 2640 TabCtrl_SetItem(s_tabhwnd, nr, &tie); 2641 } 2642 } 2643 2644 /* Remove any old labels. */ 2645 while (nr < TabCtrl_GetItemCount(s_tabhwnd)) 2646 TabCtrl_DeleteItem(s_tabhwnd, nr); 2647 2648 if (!tabadded && TabCtrl_GetCurSel(s_tabhwnd) != curtabidx) 2649 TabCtrl_SetCurSel(s_tabhwnd, curtabidx); 2650 2651 /* Re-enable redraw and redraw. */ 2652 SendMessage(s_tabhwnd, WM_SETREDRAW, (WPARAM)TRUE, 0); 2653 RedrawWindow(s_tabhwnd, NULL, NULL, 2654 RDW_ERASE | RDW_FRAME | RDW_INVALIDATE | RDW_ALLCHILDREN); 2655 2656 if (tabadded && TabCtrl_GetCurSel(s_tabhwnd) != curtabidx) 2657 TabCtrl_SetCurSel(s_tabhwnd, curtabidx); 2658 } 2659 2660 /* 2661 * Set the current tab to "nr". First tab is 1. 2662 */ 2663 void 2664 gui_mch_set_curtab(int nr) 2665 { 2666 if (s_tabhwnd == NULL) 2667 return; 2668 2669 if (TabCtrl_GetCurSel(s_tabhwnd) != nr - 1) 2670 TabCtrl_SetCurSel(s_tabhwnd, nr - 1); 2671 } 2672 2673 #endif 2674 2675 /* 2676 * ":simalt" command. 2677 */ 2678 void 2679 ex_simalt(exarg_T *eap) 2680 { 2681 char_u *keys = eap->arg; 2682 int fill_typebuf = FALSE; 2683 char_u key_name[4]; 2684 2685 PostMessage(s_hwnd, WM_SYSCOMMAND, (WPARAM)SC_KEYMENU, (LPARAM)0); 2686 while (*keys) 2687 { 2688 if (*keys == '~') 2689 *keys = ' '; /* for showing system menu */ 2690 PostMessage(s_hwnd, WM_CHAR, (WPARAM)*keys, (LPARAM)0); 2691 keys++; 2692 fill_typebuf = TRUE; 2693 } 2694 if (fill_typebuf) 2695 { 2696 /* Put a NOP in the typeahead buffer so that the message will get 2697 * processed. */ 2698 key_name[0] = K_SPECIAL; 2699 key_name[1] = KS_EXTRA; 2700 key_name[2] = KE_NOP; 2701 key_name[3] = NUL; 2702 typebuf_was_filled = TRUE; 2703 (void)ins_typebuf(key_name, REMAP_NONE, 0, TRUE, FALSE); 2704 } 2705 } 2706 2707 /* 2708 * Create the find & replace dialogs. 2709 * You can't have both at once: ":find" when replace is showing, destroys 2710 * the replace dialog first, and the other way around. 2711 */ 2712 #ifdef MSWIN_FIND_REPLACE 2713 static void 2714 initialise_findrep(char_u *initial_string) 2715 { 2716 int wword = FALSE; 2717 int mcase = !p_ic; 2718 char_u *entry_text; 2719 2720 /* Get the search string to use. */ 2721 entry_text = get_find_dialog_text(initial_string, &wword, &mcase); 2722 2723 s_findrep_struct.hwndOwner = s_hwnd; 2724 s_findrep_struct.Flags = FR_DOWN; 2725 if (mcase) 2726 s_findrep_struct.Flags |= FR_MATCHCASE; 2727 if (wword) 2728 s_findrep_struct.Flags |= FR_WHOLEWORD; 2729 if (entry_text != NULL && *entry_text != NUL) 2730 vim_strncpy((char_u *)s_findrep_struct.lpstrFindWhat, entry_text, 2731 s_findrep_struct.wFindWhatLen - 1); 2732 vim_free(entry_text); 2733 } 2734 #endif 2735 2736 static void 2737 set_window_title(HWND hwnd, char *title) 2738 { 2739 if (title != NULL && enc_codepage >= 0 && enc_codepage != (int)GetACP()) 2740 { 2741 WCHAR *wbuf; 2742 2743 /* Convert the title from 'encoding' to UTF-16. */ 2744 wbuf = (WCHAR *)enc_to_utf16((char_u *)title, NULL); 2745 if (wbuf != NULL) 2746 { 2747 SetWindowTextW(hwnd, wbuf); 2748 vim_free(wbuf); 2749 } 2750 return; 2751 } 2752 (void)SetWindowText(hwnd, (LPCSTR)title); 2753 } 2754 2755 void 2756 gui_mch_find_dialog(exarg_T *eap) 2757 { 2758 #ifdef MSWIN_FIND_REPLACE 2759 if (s_findrep_msg != 0) 2760 { 2761 if (IsWindow(s_findrep_hwnd) && !s_findrep_is_find) 2762 DestroyWindow(s_findrep_hwnd); 2763 2764 if (!IsWindow(s_findrep_hwnd)) 2765 { 2766 initialise_findrep(eap->arg); 2767 /* If the OS is Windows NT, and 'encoding' differs from active 2768 * codepage: convert text and use wide function. */ 2769 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage) 2770 { 2771 findrep_atow(&s_findrep_struct_w, &s_findrep_struct); 2772 s_findrep_hwnd = FindTextW( 2773 (LPFINDREPLACEW) &s_findrep_struct_w); 2774 } 2775 else 2776 s_findrep_hwnd = FindText((LPFINDREPLACE) &s_findrep_struct); 2777 } 2778 2779 set_window_title(s_findrep_hwnd, _("Find string")); 2780 (void)SetFocus(s_findrep_hwnd); 2781 2782 s_findrep_is_find = TRUE; 2783 } 2784 #endif 2785 } 2786 2787 2788 void 2789 gui_mch_replace_dialog(exarg_T *eap) 2790 { 2791 #ifdef MSWIN_FIND_REPLACE 2792 if (s_findrep_msg != 0) 2793 { 2794 if (IsWindow(s_findrep_hwnd) && s_findrep_is_find) 2795 DestroyWindow(s_findrep_hwnd); 2796 2797 if (!IsWindow(s_findrep_hwnd)) 2798 { 2799 initialise_findrep(eap->arg); 2800 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage) 2801 { 2802 findrep_atow(&s_findrep_struct_w, &s_findrep_struct); 2803 s_findrep_hwnd = ReplaceTextW( 2804 (LPFINDREPLACEW) &s_findrep_struct_w); 2805 } 2806 else 2807 s_findrep_hwnd = ReplaceText( 2808 (LPFINDREPLACE) &s_findrep_struct); 2809 } 2810 2811 set_window_title(s_findrep_hwnd, _("Find & Replace")); 2812 (void)SetFocus(s_findrep_hwnd); 2813 2814 s_findrep_is_find = FALSE; 2815 } 2816 #endif 2817 } 2818 2819 2820 /* 2821 * Set visibility of the pointer. 2822 */ 2823 void 2824 gui_mch_mousehide(int hide) 2825 { 2826 if (hide != gui.pointer_hidden) 2827 { 2828 ShowCursor(!hide); 2829 gui.pointer_hidden = hide; 2830 } 2831 } 2832 2833 #ifdef FEAT_MENU 2834 static void 2835 gui_mch_show_popupmenu_at(vimmenu_T *menu, int x, int y) 2836 { 2837 /* Unhide the mouse, we don't get move events here. */ 2838 gui_mch_mousehide(FALSE); 2839 2840 (void)TrackPopupMenu( 2841 (HMENU)menu->submenu_id, 2842 TPM_LEFTALIGN | TPM_LEFTBUTTON, 2843 x, y, 2844 (int)0, /*reserved param*/ 2845 s_hwnd, 2846 NULL); 2847 /* 2848 * NOTE: The pop-up menu can eat the mouse up event. 2849 * We deal with this in normal.c. 2850 */ 2851 } 2852 #endif 2853 2854 /* 2855 * Got a message when the system will go down. 2856 */ 2857 static void 2858 _OnEndSession(void) 2859 { 2860 getout_preserve_modified(1); 2861 } 2862 2863 /* 2864 * Get this message when the user clicks on the cross in the top right corner 2865 * of a Windows95 window. 2866 */ 2867 static void 2868 _OnClose(HWND hwnd UNUSED) 2869 { 2870 gui_shell_closed(); 2871 } 2872 2873 /* 2874 * Get a message when the window is being destroyed. 2875 */ 2876 static void 2877 _OnDestroy(HWND hwnd) 2878 { 2879 if (!destroying) 2880 _OnClose(hwnd); 2881 } 2882 2883 static void 2884 _OnPaint( 2885 HWND hwnd) 2886 { 2887 if (!IsMinimized(hwnd)) 2888 { 2889 PAINTSTRUCT ps; 2890 2891 out_flush(); /* make sure all output has been processed */ 2892 (void)BeginPaint(hwnd, &ps); 2893 2894 /* prevent multi-byte characters from misprinting on an invalid 2895 * rectangle */ 2896 if (has_mbyte) 2897 { 2898 RECT rect; 2899 2900 GetClientRect(hwnd, &rect); 2901 ps.rcPaint.left = rect.left; 2902 ps.rcPaint.right = rect.right; 2903 } 2904 2905 if (!IsRectEmpty(&ps.rcPaint)) 2906 { 2907 gui_redraw(ps.rcPaint.left, ps.rcPaint.top, 2908 ps.rcPaint.right - ps.rcPaint.left + 1, 2909 ps.rcPaint.bottom - ps.rcPaint.top + 1); 2910 } 2911 2912 EndPaint(hwnd, &ps); 2913 } 2914 } 2915 2916 static void 2917 _OnSize( 2918 HWND hwnd, 2919 UINT state UNUSED, 2920 int cx, 2921 int cy) 2922 { 2923 if (!IsMinimized(hwnd)) 2924 { 2925 gui_resize_shell(cx, cy); 2926 2927 #ifdef FEAT_MENU 2928 /* Menu bar may wrap differently now */ 2929 gui_mswin_get_menu_height(TRUE); 2930 #endif 2931 } 2932 } 2933 2934 static void 2935 _OnSetFocus( 2936 HWND hwnd, 2937 HWND hwndOldFocus) 2938 { 2939 gui_focus_change(TRUE); 2940 s_getting_focus = TRUE; 2941 (void)MyWindowProc(hwnd, WM_SETFOCUS, (WPARAM)hwndOldFocus, 0); 2942 } 2943 2944 static void 2945 _OnKillFocus( 2946 HWND hwnd, 2947 HWND hwndNewFocus) 2948 { 2949 gui_focus_change(FALSE); 2950 s_getting_focus = FALSE; 2951 (void)MyWindowProc(hwnd, WM_KILLFOCUS, (WPARAM)hwndNewFocus, 0); 2952 } 2953 2954 /* 2955 * Get a message when the user switches back to vim 2956 */ 2957 static LRESULT 2958 _OnActivateApp( 2959 HWND hwnd, 2960 BOOL fActivate, 2961 DWORD dwThreadId) 2962 { 2963 /* we call gui_focus_change() in _OnSetFocus() */ 2964 /* gui_focus_change((int)fActivate); */ 2965 return MyWindowProc(hwnd, WM_ACTIVATEAPP, fActivate, (DWORD)dwThreadId); 2966 } 2967 2968 void 2969 gui_mch_destroy_scrollbar(scrollbar_T *sb) 2970 { 2971 DestroyWindow(sb->id); 2972 } 2973 2974 /* 2975 * Get current mouse coordinates in text window. 2976 */ 2977 void 2978 gui_mch_getmouse(int *x, int *y) 2979 { 2980 RECT rct; 2981 POINT mp; 2982 2983 (void)GetWindowRect(s_textArea, &rct); 2984 (void)GetCursorPos((LPPOINT)&mp); 2985 *x = (int)(mp.x - rct.left); 2986 *y = (int)(mp.y - rct.top); 2987 } 2988 2989 /* 2990 * Move mouse pointer to character at (x, y). 2991 */ 2992 void 2993 gui_mch_setmouse(int x, int y) 2994 { 2995 RECT rct; 2996 2997 (void)GetWindowRect(s_textArea, &rct); 2998 (void)SetCursorPos(x + gui.border_offset + rct.left, 2999 y + gui.border_offset + rct.top); 3000 } 3001 3002 static void 3003 gui_mswin_get_valid_dimensions( 3004 int w, 3005 int h, 3006 int *valid_w, 3007 int *valid_h) 3008 { 3009 int base_width, base_height; 3010 3011 base_width = gui_get_base_width() 3012 + (GetSystemMetrics(SM_CXFRAME) + 3013 GetSystemMetrics(SM_CXPADDEDBORDER)) * 2; 3014 base_height = gui_get_base_height() 3015 + (GetSystemMetrics(SM_CYFRAME) + 3016 GetSystemMetrics(SM_CXPADDEDBORDER)) * 2 3017 + GetSystemMetrics(SM_CYCAPTION) 3018 #ifdef FEAT_MENU 3019 + gui_mswin_get_menu_height(FALSE) 3020 #endif 3021 ; 3022 *valid_w = base_width + 3023 ((w - base_width) / gui.char_width) * gui.char_width; 3024 *valid_h = base_height + 3025 ((h - base_height) / gui.char_height) * gui.char_height; 3026 } 3027 3028 void 3029 gui_mch_flash(int msec) 3030 { 3031 RECT rc; 3032 3033 #if defined(FEAT_DIRECTX) 3034 if (IS_ENABLE_DIRECTX()) 3035 DWriteContext_Flush(s_dwc); 3036 #endif 3037 3038 /* 3039 * Note: InvertRect() excludes right and bottom of rectangle. 3040 */ 3041 rc.left = 0; 3042 rc.top = 0; 3043 rc.right = gui.num_cols * gui.char_width; 3044 rc.bottom = gui.num_rows * gui.char_height; 3045 InvertRect(s_hdc, &rc); 3046 gui_mch_flush(); /* make sure it's displayed */ 3047 3048 ui_delay((long)msec, TRUE); /* wait for a few msec */ 3049 3050 InvertRect(s_hdc, &rc); 3051 } 3052 3053 /* 3054 * Return flags used for scrolling. 3055 * The SW_INVALIDATE is required when part of the window is covered or 3056 * off-screen. Refer to MS KB Q75236. 3057 */ 3058 static int 3059 get_scroll_flags(void) 3060 { 3061 HWND hwnd; 3062 RECT rcVim, rcOther, rcDest; 3063 3064 GetWindowRect(s_hwnd, &rcVim); 3065 3066 /* Check if the window is partly above or below the screen. We don't care 3067 * about partly left or right of the screen, it is not relevant when 3068 * scrolling up or down. */ 3069 if (rcVim.top < 0 || rcVim.bottom > GetSystemMetrics(SM_CYFULLSCREEN)) 3070 return SW_INVALIDATE; 3071 3072 /* Check if there is an window (partly) on top of us. */ 3073 for (hwnd = s_hwnd; (hwnd = GetWindow(hwnd, GW_HWNDPREV)) != (HWND)0; ) 3074 if (IsWindowVisible(hwnd)) 3075 { 3076 GetWindowRect(hwnd, &rcOther); 3077 if (IntersectRect(&rcDest, &rcVim, &rcOther)) 3078 return SW_INVALIDATE; 3079 } 3080 return 0; 3081 } 3082 3083 /* 3084 * On some Intel GPUs, the regions drawn just prior to ScrollWindowEx() 3085 * may not be scrolled out properly. 3086 * For gVim, when _OnScroll() is repeated, the character at the 3087 * previous cursor position may be left drawn after scroll. 3088 * The problem can be avoided by calling GetPixel() to get a pixel in 3089 * the region before ScrollWindowEx(). 3090 */ 3091 static void 3092 intel_gpu_workaround(void) 3093 { 3094 GetPixel(s_hdc, FILL_X(gui.col), FILL_Y(gui.row)); 3095 } 3096 3097 /* 3098 * Delete the given number of lines from the given row, scrolling up any 3099 * text further down within the scroll region. 3100 */ 3101 void 3102 gui_mch_delete_lines( 3103 int row, 3104 int num_lines) 3105 { 3106 RECT rc; 3107 3108 rc.left = FILL_X(gui.scroll_region_left); 3109 rc.right = FILL_X(gui.scroll_region_right + 1); 3110 rc.top = FILL_Y(row); 3111 rc.bottom = FILL_Y(gui.scroll_region_bot + 1); 3112 3113 #if defined(FEAT_DIRECTX) 3114 if (IS_ENABLE_DIRECTX()) 3115 { 3116 DWriteContext_Scroll(s_dwc, 0, -num_lines * gui.char_height, &rc); 3117 DWriteContext_Flush(s_dwc); 3118 } 3119 else 3120 #endif 3121 { 3122 intel_gpu_workaround(); 3123 ScrollWindowEx(s_textArea, 0, -num_lines * gui.char_height, 3124 &rc, &rc, NULL, NULL, get_scroll_flags()); 3125 UpdateWindow(s_textArea); 3126 } 3127 3128 /* This seems to be required to avoid the cursor disappearing when 3129 * scrolling such that the cursor ends up in the top-left character on 3130 * the screen... But why? (Webb) */ 3131 /* It's probably fixed by disabling drawing the cursor while scrolling. */ 3132 /* gui.cursor_is_valid = FALSE; */ 3133 3134 gui_clear_block(gui.scroll_region_bot - num_lines + 1, 3135 gui.scroll_region_left, 3136 gui.scroll_region_bot, gui.scroll_region_right); 3137 } 3138 3139 /* 3140 * Insert the given number of lines before the given row, scrolling down any 3141 * following text within the scroll region. 3142 */ 3143 void 3144 gui_mch_insert_lines( 3145 int row, 3146 int num_lines) 3147 { 3148 RECT rc; 3149 3150 rc.left = FILL_X(gui.scroll_region_left); 3151 rc.right = FILL_X(gui.scroll_region_right + 1); 3152 rc.top = FILL_Y(row); 3153 rc.bottom = FILL_Y(gui.scroll_region_bot + 1); 3154 3155 #if defined(FEAT_DIRECTX) 3156 if (IS_ENABLE_DIRECTX()) 3157 { 3158 DWriteContext_Scroll(s_dwc, 0, num_lines * gui.char_height, &rc); 3159 DWriteContext_Flush(s_dwc); 3160 } 3161 else 3162 #endif 3163 { 3164 intel_gpu_workaround(); 3165 /* The SW_INVALIDATE is required when part of the window is covered or 3166 * off-screen. How do we avoid it when it's not needed? */ 3167 ScrollWindowEx(s_textArea, 0, num_lines * gui.char_height, 3168 &rc, &rc, NULL, NULL, get_scroll_flags()); 3169 UpdateWindow(s_textArea); 3170 } 3171 3172 gui_clear_block(row, gui.scroll_region_left, 3173 row + num_lines - 1, gui.scroll_region_right); 3174 } 3175 3176 3177 void 3178 gui_mch_exit(int rc UNUSED) 3179 { 3180 #if defined(FEAT_DIRECTX) 3181 DWriteContext_Close(s_dwc); 3182 DWrite_Final(); 3183 s_dwc = NULL; 3184 #endif 3185 3186 ReleaseDC(s_textArea, s_hdc); 3187 DeleteObject(s_brush); 3188 3189 #ifdef FEAT_TEAROFF 3190 /* Unload the tearoff bitmap */ 3191 (void)DeleteObject((HGDIOBJ)s_htearbitmap); 3192 #endif 3193 3194 /* Destroy our window (if we have one). */ 3195 if (s_hwnd != NULL) 3196 { 3197 destroying = TRUE; /* ignore WM_DESTROY message now */ 3198 DestroyWindow(s_hwnd); 3199 } 3200 3201 #ifdef GLOBAL_IME 3202 global_ime_end(); 3203 #endif 3204 } 3205 3206 static char_u * 3207 logfont2name(LOGFONT lf) 3208 { 3209 char *p; 3210 char *res; 3211 char *charset_name; 3212 char *quality_name; 3213 char *font_name = lf.lfFaceName; 3214 3215 charset_name = charset_id2name((int)lf.lfCharSet); 3216 /* Convert a font name from the current codepage to 'encoding'. 3217 * TODO: Use Wide APIs (including LOGFONTW) instead of ANSI APIs. */ 3218 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage) 3219 { 3220 int len; 3221 acp_to_enc((char_u *)lf.lfFaceName, (int)strlen(lf.lfFaceName), 3222 (char_u **)&font_name, &len); 3223 } 3224 quality_name = quality_id2name((int)lf.lfQuality); 3225 3226 res = (char *)alloc((unsigned)(strlen(font_name) + 20 3227 + (charset_name == NULL ? 0 : strlen(charset_name) + 2))); 3228 if (res != NULL) 3229 { 3230 p = res; 3231 /* make a normal font string out of the lf thing:*/ 3232 sprintf((char *)p, "%s:h%d", font_name, pixels_to_points( 3233 lf.lfHeight < 0 ? -lf.lfHeight : lf.lfHeight, TRUE)); 3234 while (*p) 3235 { 3236 if (*p == ' ') 3237 *p = '_'; 3238 ++p; 3239 } 3240 if (lf.lfItalic) 3241 STRCAT(p, ":i"); 3242 if (lf.lfWeight >= FW_BOLD) 3243 STRCAT(p, ":b"); 3244 if (lf.lfUnderline) 3245 STRCAT(p, ":u"); 3246 if (lf.lfStrikeOut) 3247 STRCAT(p, ":s"); 3248 if (charset_name != NULL) 3249 { 3250 STRCAT(p, ":c"); 3251 STRCAT(p, charset_name); 3252 } 3253 if (quality_name != NULL) 3254 { 3255 STRCAT(p, ":q"); 3256 STRCAT(p, quality_name); 3257 } 3258 } 3259 3260 if (font_name != lf.lfFaceName) 3261 vim_free(font_name); 3262 return (char_u *)res; 3263 } 3264 3265 3266 #ifdef FEAT_MBYTE_IME 3267 /* 3268 * Set correct LOGFONT to IME. Use 'guifontwide' if available, otherwise use 3269 * 'guifont' 3270 */ 3271 static void 3272 update_im_font(void) 3273 { 3274 LOGFONT lf_wide; 3275 3276 if (p_guifontwide != NULL && *p_guifontwide != NUL 3277 && gui.wide_font != NOFONT 3278 && GetObject((HFONT)gui.wide_font, sizeof(lf_wide), &lf_wide)) 3279 norm_logfont = lf_wide; 3280 else 3281 norm_logfont = sub_logfont; 3282 im_set_font(&norm_logfont); 3283 } 3284 #endif 3285 3286 /* 3287 * Handler of gui.wide_font (p_guifontwide) changed notification. 3288 */ 3289 void 3290 gui_mch_wide_font_changed(void) 3291 { 3292 LOGFONT lf; 3293 3294 #ifdef FEAT_MBYTE_IME 3295 update_im_font(); 3296 #endif 3297 3298 gui_mch_free_font(gui.wide_ital_font); 3299 gui.wide_ital_font = NOFONT; 3300 gui_mch_free_font(gui.wide_bold_font); 3301 gui.wide_bold_font = NOFONT; 3302 gui_mch_free_font(gui.wide_boldital_font); 3303 gui.wide_boldital_font = NOFONT; 3304 3305 if (gui.wide_font 3306 && GetObject((HFONT)gui.wide_font, sizeof(lf), &lf)) 3307 { 3308 if (!lf.lfItalic) 3309 { 3310 lf.lfItalic = TRUE; 3311 gui.wide_ital_font = get_font_handle(&lf); 3312 lf.lfItalic = FALSE; 3313 } 3314 if (lf.lfWeight < FW_BOLD) 3315 { 3316 lf.lfWeight = FW_BOLD; 3317 gui.wide_bold_font = get_font_handle(&lf); 3318 if (!lf.lfItalic) 3319 { 3320 lf.lfItalic = TRUE; 3321 gui.wide_boldital_font = get_font_handle(&lf); 3322 } 3323 } 3324 } 3325 } 3326 3327 /* 3328 * Initialise vim to use the font with the given name. 3329 * Return FAIL if the font could not be loaded, OK otherwise. 3330 */ 3331 int 3332 gui_mch_init_font(char_u *font_name, int fontset UNUSED) 3333 { 3334 LOGFONT lf; 3335 GuiFont font = NOFONT; 3336 char_u *p; 3337 3338 /* Load the font */ 3339 if (get_logfont(&lf, font_name, NULL, TRUE) == OK) 3340 font = get_font_handle(&lf); 3341 if (font == NOFONT) 3342 return FAIL; 3343 3344 if (font_name == NULL) 3345 font_name = (char_u *)lf.lfFaceName; 3346 #if defined(FEAT_MBYTE_IME) || defined(GLOBAL_IME) 3347 norm_logfont = lf; 3348 #endif 3349 #ifdef FEAT_MBYTE_IME 3350 sub_logfont = lf; 3351 #endif 3352 #ifdef FEAT_MBYTE_IME 3353 update_im_font(); 3354 #endif 3355 gui_mch_free_font(gui.norm_font); 3356 gui.norm_font = font; 3357 current_font_height = lf.lfHeight; 3358 GetFontSize(font); 3359 3360 p = logfont2name(lf); 3361 if (p != NULL) 3362 { 3363 hl_set_font_name(p); 3364 3365 /* When setting 'guifont' to "*" replace it with the actual font name. 3366 * */ 3367 if (STRCMP(font_name, "*") == 0 && STRCMP(p_guifont, "*") == 0) 3368 { 3369 vim_free(p_guifont); 3370 p_guifont = p; 3371 } 3372 else 3373 vim_free(p); 3374 } 3375 3376 gui_mch_free_font(gui.ital_font); 3377 gui.ital_font = NOFONT; 3378 gui_mch_free_font(gui.bold_font); 3379 gui.bold_font = NOFONT; 3380 gui_mch_free_font(gui.boldital_font); 3381 gui.boldital_font = NOFONT; 3382 3383 if (!lf.lfItalic) 3384 { 3385 lf.lfItalic = TRUE; 3386 gui.ital_font = get_font_handle(&lf); 3387 lf.lfItalic = FALSE; 3388 } 3389 if (lf.lfWeight < FW_BOLD) 3390 { 3391 lf.lfWeight = FW_BOLD; 3392 gui.bold_font = get_font_handle(&lf); 3393 if (!lf.lfItalic) 3394 { 3395 lf.lfItalic = TRUE; 3396 gui.boldital_font = get_font_handle(&lf); 3397 } 3398 } 3399 3400 return OK; 3401 } 3402 3403 #ifndef WPF_RESTORETOMAXIMIZED 3404 # define WPF_RESTORETOMAXIMIZED 2 /* just in case someone doesn't have it */ 3405 #endif 3406 3407 /* 3408 * Return TRUE if the GUI window is maximized, filling the whole screen. 3409 */ 3410 int 3411 gui_mch_maximized(void) 3412 { 3413 WINDOWPLACEMENT wp; 3414 3415 wp.length = sizeof(WINDOWPLACEMENT); 3416 if (GetWindowPlacement(s_hwnd, &wp)) 3417 return wp.showCmd == SW_SHOWMAXIMIZED 3418 || (wp.showCmd == SW_SHOWMINIMIZED 3419 && wp.flags == WPF_RESTORETOMAXIMIZED); 3420 3421 return 0; 3422 } 3423 3424 /* 3425 * Called when the font changed while the window is maximized or GO_KEEPWINSIZE 3426 * is set. Compute the new Rows and Columns. This is like resizing the 3427 * window. 3428 */ 3429 void 3430 gui_mch_newfont(void) 3431 { 3432 RECT rect; 3433 3434 GetWindowRect(s_hwnd, &rect); 3435 if (win_socket_id == 0) 3436 { 3437 gui_resize_shell(rect.right - rect.left 3438 - (GetSystemMetrics(SM_CXFRAME) + 3439 GetSystemMetrics(SM_CXPADDEDBORDER)) * 2, 3440 rect.bottom - rect.top 3441 - (GetSystemMetrics(SM_CYFRAME) + 3442 GetSystemMetrics(SM_CXPADDEDBORDER)) * 2 3443 - GetSystemMetrics(SM_CYCAPTION) 3444 #ifdef FEAT_MENU 3445 - gui_mswin_get_menu_height(FALSE) 3446 #endif 3447 ); 3448 } 3449 else 3450 { 3451 /* Inside another window, don't use the frame and border. */ 3452 gui_resize_shell(rect.right - rect.left, 3453 rect.bottom - rect.top 3454 #ifdef FEAT_MENU 3455 - gui_mswin_get_menu_height(FALSE) 3456 #endif 3457 ); 3458 } 3459 } 3460 3461 /* 3462 * Set the window title 3463 */ 3464 void 3465 gui_mch_settitle( 3466 char_u *title, 3467 char_u *icon UNUSED) 3468 { 3469 set_window_title(s_hwnd, (title == NULL ? "VIM" : (char *)title)); 3470 } 3471 3472 #if defined(FEAT_MOUSESHAPE) || defined(PROTO) 3473 /* Table for shape IDCs. Keep in sync with the mshape_names[] table in 3474 * misc2.c! */ 3475 static LPCSTR mshape_idcs[] = 3476 { 3477 IDC_ARROW, /* arrow */ 3478 MAKEINTRESOURCE(0), /* blank */ 3479 IDC_IBEAM, /* beam */ 3480 IDC_SIZENS, /* updown */ 3481 IDC_SIZENS, /* udsizing */ 3482 IDC_SIZEWE, /* leftright */ 3483 IDC_SIZEWE, /* lrsizing */ 3484 IDC_WAIT, /* busy */ 3485 IDC_NO, /* no */ 3486 IDC_ARROW, /* crosshair */ 3487 IDC_ARROW, /* hand1 */ 3488 IDC_ARROW, /* hand2 */ 3489 IDC_ARROW, /* pencil */ 3490 IDC_ARROW, /* question */ 3491 IDC_ARROW, /* right-arrow */ 3492 IDC_UPARROW, /* up-arrow */ 3493 IDC_ARROW /* last one */ 3494 }; 3495 3496 void 3497 mch_set_mouse_shape(int shape) 3498 { 3499 LPCSTR idc; 3500 3501 if (shape == MSHAPE_HIDE) 3502 ShowCursor(FALSE); 3503 else 3504 { 3505 if (shape >= MSHAPE_NUMBERED) 3506 idc = IDC_ARROW; 3507 else 3508 idc = mshape_idcs[shape]; 3509 #ifdef SetClassLongPtr 3510 SetClassLongPtr(s_textArea, GCLP_HCURSOR, (__int3264)(LONG_PTR)LoadCursor(NULL, idc)); 3511 #else 3512 SetClassLong(s_textArea, GCL_HCURSOR, (long_u)LoadCursor(NULL, idc)); 3513 #endif 3514 if (!p_mh) 3515 { 3516 POINT mp; 3517 3518 /* Set the position to make it redrawn with the new shape. */ 3519 (void)GetCursorPos((LPPOINT)&mp); 3520 (void)SetCursorPos(mp.x, mp.y); 3521 ShowCursor(TRUE); 3522 } 3523 } 3524 } 3525 #endif 3526 3527 #if defined(FEAT_BROWSE) || defined(PROTO) 3528 /* 3529 * Wide version of convert_filter(). 3530 */ 3531 static WCHAR * 3532 convert_filterW(char_u *s) 3533 { 3534 char_u *tmp; 3535 int len; 3536 WCHAR *res; 3537 3538 tmp = convert_filter(s); 3539 if (tmp == NULL) 3540 return NULL; 3541 len = (int)STRLEN(s) + 3; 3542 res = enc_to_utf16(tmp, &len); 3543 vim_free(tmp); 3544 return res; 3545 } 3546 3547 /* 3548 * Pop open a file browser and return the file selected, in allocated memory, 3549 * or NULL if Cancel is hit. 3550 * saving - TRUE if the file will be saved to, FALSE if it will be opened. 3551 * title - Title message for the file browser dialog. 3552 * dflt - Default name of file. 3553 * ext - Default extension to be added to files without extensions. 3554 * initdir - directory in which to open the browser (NULL = current dir) 3555 * filter - Filter for matched files to choose from. 3556 */ 3557 char_u * 3558 gui_mch_browse( 3559 int saving, 3560 char_u *title, 3561 char_u *dflt, 3562 char_u *ext, 3563 char_u *initdir, 3564 char_u *filter) 3565 { 3566 /* We always use the wide function. This means enc_to_utf16() must work, 3567 * otherwise it fails miserably! */ 3568 OPENFILENAMEW fileStruct; 3569 WCHAR fileBuf[MAXPATHL]; 3570 WCHAR *wp; 3571 int i; 3572 WCHAR *titlep = NULL; 3573 WCHAR *extp = NULL; 3574 WCHAR *initdirp = NULL; 3575 WCHAR *filterp; 3576 char_u *p, *q; 3577 3578 if (dflt == NULL) 3579 fileBuf[0] = NUL; 3580 else 3581 { 3582 wp = enc_to_utf16(dflt, NULL); 3583 if (wp == NULL) 3584 fileBuf[0] = NUL; 3585 else 3586 { 3587 for (i = 0; wp[i] != NUL && i < MAXPATHL - 1; ++i) 3588 fileBuf[i] = wp[i]; 3589 fileBuf[i] = NUL; 3590 vim_free(wp); 3591 } 3592 } 3593 3594 /* Convert the filter to Windows format. */ 3595 filterp = convert_filterW(filter); 3596 3597 vim_memset(&fileStruct, 0, sizeof(OPENFILENAMEW)); 3598 # ifdef OPENFILENAME_SIZE_VERSION_400W 3599 /* be compatible with Windows NT 4.0 */ 3600 fileStruct.lStructSize = OPENFILENAME_SIZE_VERSION_400W; 3601 # else 3602 fileStruct.lStructSize = sizeof(fileStruct); 3603 # endif 3604 3605 if (title != NULL) 3606 titlep = enc_to_utf16(title, NULL); 3607 fileStruct.lpstrTitle = titlep; 3608 3609 if (ext != NULL) 3610 extp = enc_to_utf16(ext, NULL); 3611 fileStruct.lpstrDefExt = extp; 3612 3613 fileStruct.lpstrFile = fileBuf; 3614 fileStruct.nMaxFile = MAXPATHL; 3615 fileStruct.lpstrFilter = filterp; 3616 fileStruct.hwndOwner = s_hwnd; /* main Vim window is owner*/ 3617 /* has an initial dir been specified? */ 3618 if (initdir != NULL && *initdir != NUL) 3619 { 3620 /* Must have backslashes here, no matter what 'shellslash' says */ 3621 initdirp = enc_to_utf16(initdir, NULL); 3622 if (initdirp != NULL) 3623 { 3624 for (wp = initdirp; *wp != NUL; ++wp) 3625 if (*wp == '/') 3626 *wp = '\\'; 3627 } 3628 fileStruct.lpstrInitialDir = initdirp; 3629 } 3630 3631 /* 3632 * TODO: Allow selection of multiple files. Needs another arg to this 3633 * function to ask for it, and need to use OFN_ALLOWMULTISELECT below. 3634 * Also, should we use OFN_FILEMUSTEXIST when opening? Vim can edit on 3635 * files that don't exist yet, so I haven't put it in. What about 3636 * OFN_PATHMUSTEXIST? 3637 * Don't use OFN_OVERWRITEPROMPT, Vim has its own ":confirm" dialog. 3638 */ 3639 fileStruct.Flags = (OFN_NOCHANGEDIR | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY); 3640 # ifdef FEAT_SHORTCUT 3641 if (curbuf->b_p_bin) 3642 fileStruct.Flags |= OFN_NODEREFERENCELINKS; 3643 # endif 3644 if (saving) 3645 { 3646 if (!GetSaveFileNameW(&fileStruct)) 3647 return NULL; 3648 } 3649 else 3650 { 3651 if (!GetOpenFileNameW(&fileStruct)) 3652 return NULL; 3653 } 3654 3655 vim_free(filterp); 3656 vim_free(initdirp); 3657 vim_free(titlep); 3658 vim_free(extp); 3659 3660 /* Convert from UCS2 to 'encoding'. */ 3661 p = utf16_to_enc(fileBuf, NULL); 3662 if (p == NULL) 3663 return NULL; 3664 3665 /* Give focus back to main window (when using MDI). */ 3666 SetFocus(s_hwnd); 3667 3668 /* Shorten the file name if possible */ 3669 q = vim_strsave(shorten_fname1(p)); 3670 vim_free(p); 3671 return q; 3672 } 3673 3674 3675 /* 3676 * Convert the string s to the proper format for a filter string by replacing 3677 * the \t and \n delimiters with \0. 3678 * Returns the converted string in allocated memory. 3679 * 3680 * Keep in sync with convert_filterW() above! 3681 */ 3682 static char_u * 3683 convert_filter(char_u *s) 3684 { 3685 char_u *res; 3686 unsigned s_len = (unsigned)STRLEN(s); 3687 unsigned i; 3688 3689 res = alloc(s_len + 3); 3690 if (res != NULL) 3691 { 3692 for (i = 0; i < s_len; ++i) 3693 if (s[i] == '\t' || s[i] == '\n') 3694 res[i] = '\0'; 3695 else 3696 res[i] = s[i]; 3697 res[s_len] = NUL; 3698 /* Add two extra NULs to make sure it's properly terminated. */ 3699 res[s_len + 1] = NUL; 3700 res[s_len + 2] = NUL; 3701 } 3702 return res; 3703 } 3704 3705 /* 3706 * Select a directory. 3707 */ 3708 char_u * 3709 gui_mch_browsedir(char_u *title, char_u *initdir) 3710 { 3711 /* We fake this: Use a filter that doesn't select anything and a default 3712 * file name that won't be used. */ 3713 return gui_mch_browse(0, title, (char_u *)_("Not Used"), NULL, 3714 initdir, (char_u *)_("Directory\t*.nothing\n")); 3715 } 3716 #endif /* FEAT_BROWSE */ 3717 3718 static void 3719 _OnDropFiles( 3720 HWND hwnd UNUSED, 3721 HDROP hDrop) 3722 { 3723 #define BUFPATHLEN _MAX_PATH 3724 #define DRAGQVAL 0xFFFFFFFF 3725 WCHAR wszFile[BUFPATHLEN]; 3726 char szFile[BUFPATHLEN]; 3727 UINT cFiles = DragQueryFile(hDrop, DRAGQVAL, NULL, 0); 3728 UINT i; 3729 char_u **fnames; 3730 POINT pt; 3731 int_u modifiers = 0; 3732 3733 /* TRACE("_OnDropFiles: %d files dropped\n", cFiles); */ 3734 3735 /* Obtain dropped position */ 3736 DragQueryPoint(hDrop, &pt); 3737 MapWindowPoints(s_hwnd, s_textArea, &pt, 1); 3738 3739 reset_VIsual(); 3740 3741 fnames = (char_u **)alloc(cFiles * sizeof(char_u *)); 3742 3743 if (fnames != NULL) 3744 for (i = 0; i < cFiles; ++i) 3745 { 3746 if (DragQueryFileW(hDrop, i, wszFile, BUFPATHLEN) > 0) 3747 fnames[i] = utf16_to_enc(wszFile, NULL); 3748 else 3749 { 3750 DragQueryFile(hDrop, i, szFile, BUFPATHLEN); 3751 fnames[i] = vim_strsave((char_u *)szFile); 3752 } 3753 } 3754 3755 DragFinish(hDrop); 3756 3757 if (fnames != NULL) 3758 { 3759 if ((GetKeyState(VK_SHIFT) & 0x8000) != 0) 3760 modifiers |= MOUSE_SHIFT; 3761 if ((GetKeyState(VK_CONTROL) & 0x8000) != 0) 3762 modifiers |= MOUSE_CTRL; 3763 if ((GetKeyState(VK_MENU) & 0x8000) != 0) 3764 modifiers |= MOUSE_ALT; 3765 3766 gui_handle_drop(pt.x, pt.y, modifiers, fnames, cFiles); 3767 3768 s_need_activate = TRUE; 3769 } 3770 } 3771 3772 static int 3773 _OnScroll( 3774 HWND hwnd UNUSED, 3775 HWND hwndCtl, 3776 UINT code, 3777 int pos) 3778 { 3779 static UINT prev_code = 0; /* code of previous call */ 3780 scrollbar_T *sb, *sb_info; 3781 long val; 3782 int dragging = FALSE; 3783 int dont_scroll_save = dont_scroll; 3784 SCROLLINFO si; 3785 3786 si.cbSize = sizeof(si); 3787 si.fMask = SIF_POS; 3788 3789 sb = gui_mswin_find_scrollbar(hwndCtl); 3790 if (sb == NULL) 3791 return 0; 3792 3793 if (sb->wp != NULL) /* Left or right scrollbar */ 3794 { 3795 /* 3796 * Careful: need to get scrollbar info out of first (left) scrollbar 3797 * for window, but keep real scrollbar too because we must pass it to 3798 * gui_drag_scrollbar(). 3799 */ 3800 sb_info = &sb->wp->w_scrollbars[0]; 3801 } 3802 else /* Bottom scrollbar */ 3803 sb_info = sb; 3804 val = sb_info->value; 3805 3806 switch (code) 3807 { 3808 case SB_THUMBTRACK: 3809 val = pos; 3810 dragging = TRUE; 3811 if (sb->scroll_shift > 0) 3812 val <<= sb->scroll_shift; 3813 break; 3814 case SB_LINEDOWN: 3815 val++; 3816 break; 3817 case SB_LINEUP: 3818 val--; 3819 break; 3820 case SB_PAGEDOWN: 3821 val += (sb_info->size > 2 ? sb_info->size - 2 : 1); 3822 break; 3823 case SB_PAGEUP: 3824 val -= (sb_info->size > 2 ? sb_info->size - 2 : 1); 3825 break; 3826 case SB_TOP: 3827 val = 0; 3828 break; 3829 case SB_BOTTOM: 3830 val = sb_info->max; 3831 break; 3832 case SB_ENDSCROLL: 3833 if (prev_code == SB_THUMBTRACK) 3834 { 3835 /* 3836 * "pos" only gives us 16-bit data. In case of large file, 3837 * use GetScrollPos() which returns 32-bit. Unfortunately it 3838 * is not valid while the scrollbar is being dragged. 3839 */ 3840 val = GetScrollPos(hwndCtl, SB_CTL); 3841 if (sb->scroll_shift > 0) 3842 val <<= sb->scroll_shift; 3843 } 3844 break; 3845 3846 default: 3847 /* TRACE("Unknown scrollbar event %d\n", code); */ 3848 return 0; 3849 } 3850 prev_code = code; 3851 3852 si.nPos = (sb->scroll_shift > 0) ? val >> sb->scroll_shift : val; 3853 SetScrollInfo(hwndCtl, SB_CTL, &si, TRUE); 3854 3855 /* 3856 * When moving a vertical scrollbar, move the other vertical scrollbar too. 3857 */ 3858 if (sb->wp != NULL) 3859 { 3860 scrollbar_T *sba = sb->wp->w_scrollbars; 3861 HWND id = sba[ (sb == sba + SBAR_LEFT) ? SBAR_RIGHT : SBAR_LEFT].id; 3862 3863 SetScrollInfo(id, SB_CTL, &si, TRUE); 3864 } 3865 3866 /* Don't let us be interrupted here by another message. */ 3867 s_busy_processing = TRUE; 3868 3869 /* When "allow_scrollbar" is FALSE still need to remember the new 3870 * position, but don't actually scroll by setting "dont_scroll". */ 3871 dont_scroll = !allow_scrollbar; 3872 3873 mch_disable_flush(); 3874 gui_drag_scrollbar(sb, val, dragging); 3875 mch_enable_flush(); 3876 gui_may_flush(); 3877 3878 s_busy_processing = FALSE; 3879 dont_scroll = dont_scroll_save; 3880 3881 return 0; 3882 } 3883 3884 3885 /* 3886 * Get command line arguments. 3887 * Use "prog" as the name of the program and "cmdline" as the arguments. 3888 * Copy the arguments to allocated memory. 3889 * Return the number of arguments (including program name). 3890 * Return pointers to the arguments in "argvp". Memory is allocated with 3891 * malloc(), use free() instead of vim_free(). 3892 * Return pointer to buffer in "tofree". 3893 * Returns zero when out of memory. 3894 */ 3895 int 3896 get_cmd_args(char *prog, char *cmdline, char ***argvp, char **tofree) 3897 { 3898 int i; 3899 char *p; 3900 char *progp; 3901 char *pnew = NULL; 3902 char *newcmdline; 3903 int inquote; 3904 int argc; 3905 char **argv = NULL; 3906 int round; 3907 3908 *tofree = NULL; 3909 3910 /* Try using the Unicode version first, it takes care of conversion when 3911 * 'encoding' is changed. */ 3912 argc = get_cmd_argsW(&argv); 3913 if (argc != 0) 3914 goto done; 3915 3916 /* Handle the program name. Remove the ".exe" extension, and find the 1st 3917 * non-space. */ 3918 p = strrchr(prog, '.'); 3919 if (p != NULL) 3920 *p = NUL; 3921 for (progp = prog; *progp == ' '; ++progp) 3922 ; 3923 3924 /* The command line is copied to allocated memory, so that we can change 3925 * it. Add the size of the string, the separating NUL and a terminating 3926 * NUL. */ 3927 newcmdline = malloc(STRLEN(cmdline) + STRLEN(progp) + 2); 3928 if (newcmdline == NULL) 3929 return 0; 3930 3931 /* 3932 * First round: count the number of arguments ("pnew" == NULL). 3933 * Second round: produce the arguments. 3934 */ 3935 for (round = 1; round <= 2; ++round) 3936 { 3937 /* First argument is the program name. */ 3938 if (pnew != NULL) 3939 { 3940 argv[0] = pnew; 3941 strcpy(pnew, progp); 3942 pnew += strlen(pnew); 3943 *pnew++ = NUL; 3944 } 3945 3946 /* 3947 * Isolate each argument and put it in argv[]. 3948 */ 3949 p = cmdline; 3950 argc = 1; 3951 while (*p != NUL) 3952 { 3953 inquote = FALSE; 3954 if (pnew != NULL) 3955 argv[argc] = pnew; 3956 ++argc; 3957 while (*p != NUL && (inquote || (*p != ' ' && *p != '\t'))) 3958 { 3959 /* Backslashes are only special when followed by a double 3960 * quote. */ 3961 i = (int)strspn(p, "\\"); 3962 if (p[i] == '"') 3963 { 3964 /* Halve the number of backslashes. */ 3965 if (i > 1 && pnew != NULL) 3966 { 3967 vim_memset(pnew, '\\', i / 2); 3968 pnew += i / 2; 3969 } 3970 3971 /* Even nr of backslashes toggles quoting, uneven copies 3972 * the double quote. */ 3973 if ((i & 1) == 0) 3974 inquote = !inquote; 3975 else if (pnew != NULL) 3976 *pnew++ = '"'; 3977 p += i + 1; 3978 } 3979 else if (i > 0) 3980 { 3981 /* Copy span of backslashes unmodified. */ 3982 if (pnew != NULL) 3983 { 3984 vim_memset(pnew, '\\', i); 3985 pnew += i; 3986 } 3987 p += i; 3988 } 3989 else 3990 { 3991 if (pnew != NULL) 3992 *pnew++ = *p; 3993 /* Can't use mb_* functions, because 'encoding' is not 3994 * initialized yet here. */ 3995 if (IsDBCSLeadByte(*p)) 3996 { 3997 ++p; 3998 if (pnew != NULL) 3999 *pnew++ = *p; 4000 } 4001 ++p; 4002 } 4003 } 4004 4005 if (pnew != NULL) 4006 *pnew++ = NUL; 4007 while (*p == ' ' || *p == '\t') 4008 ++p; /* advance until a non-space */ 4009 } 4010 4011 if (round == 1) 4012 { 4013 argv = (char **)malloc((argc + 1) * sizeof(char *)); 4014 if (argv == NULL ) 4015 { 4016 free(newcmdline); 4017 return 0; /* malloc error */ 4018 } 4019 pnew = newcmdline; 4020 *tofree = newcmdline; 4021 } 4022 } 4023 4024 done: 4025 argv[argc] = NULL; /* NULL-terminated list */ 4026 *argvp = argv; 4027 return argc; 4028 } 4029 4030 #ifdef FEAT_XPM_W32 4031 # include "xpm_w32.h" 4032 #endif 4033 4034 #ifdef PROTO 4035 # define WINAPI 4036 #endif 4037 4038 #ifdef __MINGW32__ 4039 /* 4040 * Add a lot of missing defines. 4041 * They are not always missing, we need the #ifndef's. 4042 */ 4043 # ifndef _cdecl 4044 # define _cdecl 4045 # endif 4046 # ifndef IsMinimized 4047 # define IsMinimized(hwnd) IsIconic(hwnd) 4048 # endif 4049 # ifndef IsMaximized 4050 # define IsMaximized(hwnd) IsZoomed(hwnd) 4051 # endif 4052 # ifndef SelectFont 4053 # define SelectFont(hdc, hfont) ((HFONT)SelectObject((hdc), (HGDIOBJ)(HFONT)(hfont))) 4054 # endif 4055 # ifndef GetStockBrush 4056 # define GetStockBrush(i) ((HBRUSH)GetStockObject(i)) 4057 # endif 4058 # ifndef DeleteBrush 4059 # define DeleteBrush(hbr) DeleteObject((HGDIOBJ)(HBRUSH)(hbr)) 4060 # endif 4061 4062 # ifndef HANDLE_WM_RBUTTONDBLCLK 4063 # define HANDLE_WM_RBUTTONDBLCLK(hwnd, wParam, lParam, fn) \ 4064 ((fn)((hwnd), TRUE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L) 4065 # endif 4066 # ifndef HANDLE_WM_MBUTTONUP 4067 # define HANDLE_WM_MBUTTONUP(hwnd, wParam, lParam, fn) \ 4068 ((fn)((hwnd), (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L) 4069 # endif 4070 # ifndef HANDLE_WM_MBUTTONDBLCLK 4071 # define HANDLE_WM_MBUTTONDBLCLK(hwnd, wParam, lParam, fn) \ 4072 ((fn)((hwnd), TRUE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L) 4073 # endif 4074 # ifndef HANDLE_WM_LBUTTONDBLCLK 4075 # define HANDLE_WM_LBUTTONDBLCLK(hwnd, wParam, lParam, fn) \ 4076 ((fn)((hwnd), TRUE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L) 4077 # endif 4078 # ifndef HANDLE_WM_RBUTTONDOWN 4079 # define HANDLE_WM_RBUTTONDOWN(hwnd, wParam, lParam, fn) \ 4080 ((fn)((hwnd), FALSE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L) 4081 # endif 4082 # ifndef HANDLE_WM_MOUSEMOVE 4083 # define HANDLE_WM_MOUSEMOVE(hwnd, wParam, lParam, fn) \ 4084 ((fn)((hwnd), (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L) 4085 # endif 4086 # ifndef HANDLE_WM_RBUTTONUP 4087 # define HANDLE_WM_RBUTTONUP(hwnd, wParam, lParam, fn) \ 4088 ((fn)((hwnd), (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L) 4089 # endif 4090 # ifndef HANDLE_WM_MBUTTONDOWN 4091 # define HANDLE_WM_MBUTTONDOWN(hwnd, wParam, lParam, fn) \ 4092 ((fn)((hwnd), FALSE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L) 4093 # endif 4094 # ifndef HANDLE_WM_LBUTTONUP 4095 # define HANDLE_WM_LBUTTONUP(hwnd, wParam, lParam, fn) \ 4096 ((fn)((hwnd), (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L) 4097 # endif 4098 # ifndef HANDLE_WM_LBUTTONDOWN 4099 # define HANDLE_WM_LBUTTONDOWN(hwnd, wParam, lParam, fn) \ 4100 ((fn)((hwnd), FALSE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L) 4101 # endif 4102 # ifndef HANDLE_WM_SYSCHAR 4103 # define HANDLE_WM_SYSCHAR(hwnd, wParam, lParam, fn) \ 4104 ((fn)((hwnd), (TCHAR)(wParam), (int)(short)LOWORD(lParam)), 0L) 4105 # endif 4106 # ifndef HANDLE_WM_ACTIVATEAPP 4107 # define HANDLE_WM_ACTIVATEAPP(hwnd, wParam, lParam, fn) \ 4108 ((fn)((hwnd), (BOOL)(wParam), (DWORD)(lParam)), 0L) 4109 # endif 4110 # ifndef HANDLE_WM_WINDOWPOSCHANGING 4111 # define HANDLE_WM_WINDOWPOSCHANGING(hwnd, wParam, lParam, fn) \ 4112 (LRESULT)(DWORD)(BOOL)(fn)((hwnd), (LPWINDOWPOS)(lParam)) 4113 # endif 4114 # ifndef HANDLE_WM_VSCROLL 4115 # define HANDLE_WM_VSCROLL(hwnd, wParam, lParam, fn) \ 4116 ((fn)((hwnd), (HWND)(lParam), (UINT)(LOWORD(wParam)), (int)(short)HIWORD(wParam)), 0L) 4117 # endif 4118 # ifndef HANDLE_WM_SETFOCUS 4119 # define HANDLE_WM_SETFOCUS(hwnd, wParam, lParam, fn) \ 4120 ((fn)((hwnd), (HWND)(wParam)), 0L) 4121 # endif 4122 # ifndef HANDLE_WM_KILLFOCUS 4123 # define HANDLE_WM_KILLFOCUS(hwnd, wParam, lParam, fn) \ 4124 ((fn)((hwnd), (HWND)(wParam)), 0L) 4125 # endif 4126 # ifndef HANDLE_WM_HSCROLL 4127 # define HANDLE_WM_HSCROLL(hwnd, wParam, lParam, fn) \ 4128 ((fn)((hwnd), (HWND)(lParam), (UINT)(LOWORD(wParam)), (int)(short)HIWORD(wParam)), 0L) 4129 # endif 4130 # ifndef HANDLE_WM_DROPFILES 4131 # define HANDLE_WM_DROPFILES(hwnd, wParam, lParam, fn) \ 4132 ((fn)((hwnd), (HDROP)(wParam)), 0L) 4133 # endif 4134 # ifndef HANDLE_WM_CHAR 4135 # define HANDLE_WM_CHAR(hwnd, wParam, lParam, fn) \ 4136 ((fn)((hwnd), (TCHAR)(wParam), (int)(short)LOWORD(lParam)), 0L) 4137 # endif 4138 # ifndef HANDLE_WM_SYSDEADCHAR 4139 # define HANDLE_WM_SYSDEADCHAR(hwnd, wParam, lParam, fn) \ 4140 ((fn)((hwnd), (TCHAR)(wParam), (int)(short)LOWORD(lParam)), 0L) 4141 # endif 4142 # ifndef HANDLE_WM_DEADCHAR 4143 # define HANDLE_WM_DEADCHAR(hwnd, wParam, lParam, fn) \ 4144 ((fn)((hwnd), (TCHAR)(wParam), (int)(short)LOWORD(lParam)), 0L) 4145 # endif 4146 #endif /* __MINGW32__ */ 4147 4148 4149 /* Some parameters for tearoff menus. All in pixels. */ 4150 #define TEAROFF_PADDING_X 2 4151 #define TEAROFF_BUTTON_PAD_X 8 4152 #define TEAROFF_MIN_WIDTH 200 4153 #define TEAROFF_SUBMENU_LABEL ">>" 4154 #define TEAROFF_COLUMN_PADDING 3 // # spaces to pad column with. 4155 4156 4157 /* For the Intellimouse: */ 4158 #ifndef WM_MOUSEWHEEL 4159 #define WM_MOUSEWHEEL 0x20a 4160 #endif 4161 4162 4163 #ifdef FEAT_BEVAL_GUI 4164 # define ID_BEVAL_TOOLTIP 200 4165 # define BEVAL_TEXT_LEN MAXPATHL 4166 4167 #if (defined(_MSC_VER) && _MSC_VER < 1300) || !defined(MAXULONG_PTR) 4168 /* Work around old versions of basetsd.h which wrongly declares 4169 * UINT_PTR as unsigned long. */ 4170 # undef UINT_PTR 4171 # define UINT_PTR UINT 4172 #endif 4173 4174 static BalloonEval *cur_beval = NULL; 4175 static UINT_PTR BevalTimerId = 0; 4176 static DWORD LastActivity = 0; 4177 4178 4179 /* cproto fails on missing include files */ 4180 #ifndef PROTO 4181 4182 /* 4183 * excerpts from headers since this may not be presented 4184 * in the extremely old compilers 4185 */ 4186 # include <pshpack1.h> 4187 4188 #endif 4189 4190 typedef struct _DllVersionInfo 4191 { 4192 DWORD cbSize; 4193 DWORD dwMajorVersion; 4194 DWORD dwMinorVersion; 4195 DWORD dwBuildNumber; 4196 DWORD dwPlatformID; 4197 } DLLVERSIONINFO; 4198 4199 #ifndef PROTO 4200 # include <poppack.h> 4201 #endif 4202 4203 typedef struct tagTOOLINFOA_NEW 4204 { 4205 UINT cbSize; 4206 UINT uFlags; 4207 HWND hwnd; 4208 UINT_PTR uId; 4209 RECT rect; 4210 HINSTANCE hinst; 4211 LPSTR lpszText; 4212 LPARAM lParam; 4213 } TOOLINFO_NEW; 4214 4215 typedef struct tagNMTTDISPINFO_NEW 4216 { 4217 NMHDR hdr; 4218 LPSTR lpszText; 4219 char szText[80]; 4220 HINSTANCE hinst; 4221 UINT uFlags; 4222 LPARAM lParam; 4223 } NMTTDISPINFO_NEW; 4224 4225 typedef struct tagTOOLINFOW_NEW 4226 { 4227 UINT cbSize; 4228 UINT uFlags; 4229 HWND hwnd; 4230 UINT_PTR uId; 4231 RECT rect; 4232 HINSTANCE hinst; 4233 LPWSTR lpszText; 4234 LPARAM lParam; 4235 void *lpReserved; 4236 } TOOLINFOW_NEW; 4237 4238 typedef struct tagNMTTDISPINFOW_NEW 4239 { 4240 NMHDR hdr; 4241 LPWSTR lpszText; 4242 WCHAR szText[80]; 4243 HINSTANCE hinst; 4244 UINT uFlags; 4245 LPARAM lParam; 4246 } NMTTDISPINFOW_NEW; 4247 4248 4249 typedef HRESULT (WINAPI* DLLGETVERSIONPROC)(DLLVERSIONINFO *); 4250 #ifndef TTM_SETMAXTIPWIDTH 4251 # define TTM_SETMAXTIPWIDTH (WM_USER+24) 4252 #endif 4253 4254 #ifndef TTF_DI_SETITEM 4255 # define TTF_DI_SETITEM 0x8000 4256 #endif 4257 4258 #ifndef TTN_GETDISPINFO 4259 # define TTN_GETDISPINFO (TTN_FIRST - 0) 4260 #endif 4261 4262 #endif /* defined(FEAT_BEVAL_GUI) */ 4263 4264 #if defined(FEAT_TOOLBAR) || defined(FEAT_GUI_TABLINE) 4265 /* Older MSVC compilers don't have LPNMTTDISPINFO[AW] thus we need to define 4266 * it here if LPNMTTDISPINFO isn't defined. 4267 * MingW doesn't define LPNMTTDISPINFO but typedefs it. Thus we need to check 4268 * _MSC_VER. */ 4269 # if !defined(LPNMTTDISPINFO) && defined(_MSC_VER) 4270 typedef struct tagNMTTDISPINFOA { 4271 NMHDR hdr; 4272 LPSTR lpszText; 4273 char szText[80]; 4274 HINSTANCE hinst; 4275 UINT uFlags; 4276 LPARAM lParam; 4277 } NMTTDISPINFOA, *LPNMTTDISPINFOA; 4278 # define LPNMTTDISPINFO LPNMTTDISPINFOA 4279 4280 typedef struct tagNMTTDISPINFOW { 4281 NMHDR hdr; 4282 LPWSTR lpszText; 4283 WCHAR szText[80]; 4284 HINSTANCE hinst; 4285 UINT uFlags; 4286 LPARAM lParam; 4287 } NMTTDISPINFOW, *LPNMTTDISPINFOW; 4288 # endif 4289 #endif 4290 4291 #ifndef TTN_GETDISPINFOW 4292 # define TTN_GETDISPINFOW (TTN_FIRST - 10) 4293 #endif 4294 4295 /* Local variables: */ 4296 4297 #ifdef FEAT_MENU 4298 static UINT s_menu_id = 100; 4299 #endif 4300 4301 /* 4302 * Use the system font for dialogs and tear-off menus. Remove this line to 4303 * use DLG_FONT_NAME. 4304 */ 4305 #define USE_SYSMENU_FONT 4306 4307 #define VIM_NAME "vim" 4308 #define VIM_CLASS "Vim" 4309 #define VIM_CLASSW L"Vim" 4310 4311 /* Initial size for the dialog template. For gui_mch_dialog() it's fixed, 4312 * thus there should be room for every dialog. For tearoffs it's made bigger 4313 * when needed. */ 4314 #define DLG_ALLOC_SIZE 16 * 1024 4315 4316 /* 4317 * stuff for dialogs, menus, tearoffs etc. 4318 */ 4319 static PWORD 4320 add_dialog_element( 4321 PWORD p, 4322 DWORD lStyle, 4323 WORD x, 4324 WORD y, 4325 WORD w, 4326 WORD h, 4327 WORD Id, 4328 WORD clss, 4329 const char *caption); 4330 static LPWORD lpwAlign(LPWORD); 4331 static int nCopyAnsiToWideChar(LPWORD, LPSTR, BOOL); 4332 #if defined(FEAT_MENU) && defined(FEAT_TEAROFF) 4333 static void gui_mch_tearoff(char_u *title, vimmenu_T *menu, int initX, int initY); 4334 #endif 4335 static void get_dialog_font_metrics(void); 4336 4337 static int dialog_default_button = -1; 4338 4339 /* Intellimouse support */ 4340 static int mouse_scroll_lines = 0; 4341 4342 static int s_usenewlook; /* emulate W95/NT4 non-bold dialogs */ 4343 #ifdef FEAT_TOOLBAR 4344 static void initialise_toolbar(void); 4345 static LRESULT CALLBACK toolbar_wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); 4346 static int get_toolbar_bitmap(vimmenu_T *menu); 4347 #endif 4348 4349 #ifdef FEAT_GUI_TABLINE 4350 static void initialise_tabline(void); 4351 static LRESULT CALLBACK tabline_wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); 4352 #endif 4353 4354 #ifdef FEAT_MBYTE_IME 4355 static LRESULT _OnImeComposition(HWND hwnd, WPARAM dbcs, LPARAM param); 4356 static char_u *GetResultStr(HWND hwnd, int GCS, int *lenp); 4357 #endif 4358 #if defined(FEAT_MBYTE_IME) && defined(DYNAMIC_IME) 4359 # ifdef NOIME 4360 typedef struct tagCOMPOSITIONFORM { 4361 DWORD dwStyle; 4362 POINT ptCurrentPos; 4363 RECT rcArea; 4364 } COMPOSITIONFORM, *PCOMPOSITIONFORM, NEAR *NPCOMPOSITIONFORM, FAR *LPCOMPOSITIONFORM; 4365 typedef HANDLE HIMC; 4366 # endif 4367 4368 static HINSTANCE hLibImm = NULL; 4369 static LONG (WINAPI *pImmGetCompositionStringA)(HIMC, DWORD, LPVOID, DWORD); 4370 static LONG (WINAPI *pImmGetCompositionStringW)(HIMC, DWORD, LPVOID, DWORD); 4371 static HIMC (WINAPI *pImmGetContext)(HWND); 4372 static HIMC (WINAPI *pImmAssociateContext)(HWND, HIMC); 4373 static BOOL (WINAPI *pImmReleaseContext)(HWND, HIMC); 4374 static BOOL (WINAPI *pImmGetOpenStatus)(HIMC); 4375 static BOOL (WINAPI *pImmSetOpenStatus)(HIMC, BOOL); 4376 static BOOL (WINAPI *pImmGetCompositionFont)(HIMC, LPLOGFONTA); 4377 static BOOL (WINAPI *pImmSetCompositionFont)(HIMC, LPLOGFONTA); 4378 static BOOL (WINAPI *pImmSetCompositionWindow)(HIMC, LPCOMPOSITIONFORM); 4379 static BOOL (WINAPI *pImmGetConversionStatus)(HIMC, LPDWORD, LPDWORD); 4380 static BOOL (WINAPI *pImmSetConversionStatus)(HIMC, DWORD, DWORD); 4381 static void dyn_imm_load(void); 4382 #else 4383 # define pImmGetCompositionStringA ImmGetCompositionStringA 4384 # define pImmGetCompositionStringW ImmGetCompositionStringW 4385 # define pImmGetContext ImmGetContext 4386 # define pImmAssociateContext ImmAssociateContext 4387 # define pImmReleaseContext ImmReleaseContext 4388 # define pImmGetOpenStatus ImmGetOpenStatus 4389 # define pImmSetOpenStatus ImmSetOpenStatus 4390 # define pImmGetCompositionFont ImmGetCompositionFontA 4391 # define pImmSetCompositionFont ImmSetCompositionFontA 4392 # define pImmSetCompositionWindow ImmSetCompositionWindow 4393 # define pImmGetConversionStatus ImmGetConversionStatus 4394 # define pImmSetConversionStatus ImmSetConversionStatus 4395 #endif 4396 4397 #ifdef FEAT_MENU 4398 /* 4399 * Figure out how high the menu bar is at the moment. 4400 */ 4401 static int 4402 gui_mswin_get_menu_height( 4403 int fix_window) /* If TRUE, resize window if menu height changed */ 4404 { 4405 static int old_menu_height = -1; 4406 4407 RECT rc1, rc2; 4408 int num; 4409 int menu_height; 4410 4411 if (gui.menu_is_active) 4412 num = GetMenuItemCount(s_menuBar); 4413 else 4414 num = 0; 4415 4416 if (num == 0) 4417 menu_height = 0; 4418 else if (IsMinimized(s_hwnd)) 4419 { 4420 /* The height of the menu cannot be determined while the window is 4421 * minimized. Take the previous height if the menu is changed in that 4422 * state, to avoid that Vim's vertical window size accidentally 4423 * increases due to the unaccounted-for menu height. */ 4424 menu_height = old_menu_height == -1 ? 0 : old_menu_height; 4425 } 4426 else 4427 { 4428 /* 4429 * In case 'lines' is set in _vimrc/_gvimrc window width doesn't 4430 * seem to have been set yet, so menu wraps in default window 4431 * width which is very narrow. Instead just return height of a 4432 * single menu item. Will still be wrong when the menu really 4433 * should wrap over more than one line. 4434 */ 4435 GetMenuItemRect(s_hwnd, s_menuBar, 0, &rc1); 4436 if (gui.starting) 4437 menu_height = rc1.bottom - rc1.top + 1; 4438 else 4439 { 4440 GetMenuItemRect(s_hwnd, s_menuBar, num - 1, &rc2); 4441 menu_height = rc2.bottom - rc1.top + 1; 4442 } 4443 } 4444 4445 if (fix_window && menu_height != old_menu_height) 4446 { 4447 gui_set_shellsize(FALSE, FALSE, RESIZE_VERT); 4448 } 4449 old_menu_height = menu_height; 4450 4451 return menu_height; 4452 } 4453 #endif /*FEAT_MENU*/ 4454 4455 4456 /* 4457 * Setup for the Intellimouse 4458 */ 4459 static void 4460 init_mouse_wheel(void) 4461 { 4462 4463 #ifndef SPI_GETWHEELSCROLLLINES 4464 # define SPI_GETWHEELSCROLLLINES 104 4465 #endif 4466 #ifndef SPI_SETWHEELSCROLLLINES 4467 # define SPI_SETWHEELSCROLLLINES 105 4468 #endif 4469 4470 #define VMOUSEZ_CLASSNAME "MouseZ" /* hidden wheel window class */ 4471 #define VMOUSEZ_TITLE "Magellan MSWHEEL" /* hidden wheel window title */ 4472 #define VMSH_MOUSEWHEEL "MSWHEEL_ROLLMSG" 4473 #define VMSH_SCROLL_LINES "MSH_SCROLL_LINES_MSG" 4474 4475 mouse_scroll_lines = 3; /* reasonable default */ 4476 4477 /* if NT 4.0+ (or Win98) get scroll lines directly from system */ 4478 SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, 4479 &mouse_scroll_lines, 0); 4480 } 4481 4482 4483 /* Intellimouse wheel handler */ 4484 static void 4485 _OnMouseWheel( 4486 HWND hwnd, 4487 short zDelta) 4488 { 4489 /* Treat a mouse wheel event as if it were a scroll request */ 4490 int i; 4491 int size; 4492 HWND hwndCtl; 4493 4494 if (curwin->w_scrollbars[SBAR_RIGHT].id != 0) 4495 { 4496 hwndCtl = curwin->w_scrollbars[SBAR_RIGHT].id; 4497 size = curwin->w_scrollbars[SBAR_RIGHT].size; 4498 } 4499 else if (curwin->w_scrollbars[SBAR_LEFT].id != 0) 4500 { 4501 hwndCtl = curwin->w_scrollbars[SBAR_LEFT].id; 4502 size = curwin->w_scrollbars[SBAR_LEFT].size; 4503 } 4504 else 4505 return; 4506 4507 size = curwin->w_height; 4508 if (mouse_scroll_lines == 0) 4509 init_mouse_wheel(); 4510 4511 mch_disable_flush(); 4512 if (mouse_scroll_lines > 0 4513 && mouse_scroll_lines < (size > 2 ? size - 2 : 1)) 4514 { 4515 for (i = mouse_scroll_lines; i > 0; --i) 4516 _OnScroll(hwnd, hwndCtl, zDelta >= 0 ? SB_LINEUP : SB_LINEDOWN, 0); 4517 } 4518 else 4519 _OnScroll(hwnd, hwndCtl, zDelta >= 0 ? SB_PAGEUP : SB_PAGEDOWN, 0); 4520 mch_enable_flush(); 4521 gui_may_flush(); 4522 } 4523 4524 #ifdef USE_SYSMENU_FONT 4525 /* 4526 * Get Menu Font. 4527 * Return OK or FAIL. 4528 */ 4529 static int 4530 gui_w32_get_menu_font(LOGFONT *lf) 4531 { 4532 NONCLIENTMETRICS nm; 4533 4534 nm.cbSize = sizeof(NONCLIENTMETRICS); 4535 if (!SystemParametersInfo( 4536 SPI_GETNONCLIENTMETRICS, 4537 sizeof(NONCLIENTMETRICS), 4538 &nm, 4539 0)) 4540 return FAIL; 4541 *lf = nm.lfMenuFont; 4542 return OK; 4543 } 4544 #endif 4545 4546 4547 #if defined(FEAT_GUI_TABLINE) && defined(USE_SYSMENU_FONT) 4548 /* 4549 * Set the GUI tabline font to the system menu font 4550 */ 4551 static void 4552 set_tabline_font(void) 4553 { 4554 LOGFONT lfSysmenu; 4555 HFONT font; 4556 HWND hwnd; 4557 HDC hdc; 4558 HFONT hfntOld; 4559 TEXTMETRIC tm; 4560 4561 if (gui_w32_get_menu_font(&lfSysmenu) != OK) 4562 return; 4563 4564 font = CreateFontIndirect(&lfSysmenu); 4565 4566 SendMessage(s_tabhwnd, WM_SETFONT, (WPARAM)font, TRUE); 4567 4568 /* 4569 * Compute the height of the font used for the tab text 4570 */ 4571 hwnd = GetDesktopWindow(); 4572 hdc = GetWindowDC(hwnd); 4573 hfntOld = SelectFont(hdc, font); 4574 4575 GetTextMetrics(hdc, &tm); 4576 4577 SelectFont(hdc, hfntOld); 4578 ReleaseDC(hwnd, hdc); 4579 4580 /* 4581 * The space used by the tab border and the space between the tab label 4582 * and the tab border is included as 7. 4583 */ 4584 gui.tabline_height = tm.tmHeight + tm.tmInternalLeading + 7; 4585 } 4586 #endif 4587 4588 /* 4589 * Invoked when a setting was changed. 4590 */ 4591 static LRESULT CALLBACK 4592 _OnSettingChange(UINT n) 4593 { 4594 if (n == SPI_SETWHEELSCROLLLINES) 4595 SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, 4596 &mouse_scroll_lines, 0); 4597 #if defined(FEAT_GUI_TABLINE) && defined(USE_SYSMENU_FONT) 4598 if (n == SPI_SETNONCLIENTMETRICS) 4599 set_tabline_font(); 4600 #endif 4601 return 0; 4602 } 4603 4604 #ifdef FEAT_NETBEANS_INTG 4605 static void 4606 _OnWindowPosChanged( 4607 HWND hwnd, 4608 const LPWINDOWPOS lpwpos) 4609 { 4610 static int x = 0, y = 0, cx = 0, cy = 0; 4611 extern int WSInitialized; 4612 4613 if (WSInitialized && (lpwpos->x != x || lpwpos->y != y 4614 || lpwpos->cx != cx || lpwpos->cy != cy)) 4615 { 4616 x = lpwpos->x; 4617 y = lpwpos->y; 4618 cx = lpwpos->cx; 4619 cy = lpwpos->cy; 4620 netbeans_frame_moved(x, y); 4621 } 4622 /* Allow to send WM_SIZE and WM_MOVE */ 4623 FORWARD_WM_WINDOWPOSCHANGED(hwnd, lpwpos, MyWindowProc); 4624 } 4625 #endif 4626 4627 static int 4628 _DuringSizing( 4629 UINT fwSide, 4630 LPRECT lprc) 4631 { 4632 int w, h; 4633 int valid_w, valid_h; 4634 int w_offset, h_offset; 4635 4636 w = lprc->right - lprc->left; 4637 h = lprc->bottom - lprc->top; 4638 gui_mswin_get_valid_dimensions(w, h, &valid_w, &valid_h); 4639 w_offset = w - valid_w; 4640 h_offset = h - valid_h; 4641 4642 if (fwSide == WMSZ_LEFT || fwSide == WMSZ_TOPLEFT 4643 || fwSide == WMSZ_BOTTOMLEFT) 4644 lprc->left += w_offset; 4645 else if (fwSide == WMSZ_RIGHT || fwSide == WMSZ_TOPRIGHT 4646 || fwSide == WMSZ_BOTTOMRIGHT) 4647 lprc->right -= w_offset; 4648 4649 if (fwSide == WMSZ_TOP || fwSide == WMSZ_TOPLEFT 4650 || fwSide == WMSZ_TOPRIGHT) 4651 lprc->top += h_offset; 4652 else if (fwSide == WMSZ_BOTTOM || fwSide == WMSZ_BOTTOMLEFT 4653 || fwSide == WMSZ_BOTTOMRIGHT) 4654 lprc->bottom -= h_offset; 4655 return TRUE; 4656 } 4657 4658 4659 4660 static LRESULT CALLBACK 4661 _WndProc( 4662 HWND hwnd, 4663 UINT uMsg, 4664 WPARAM wParam, 4665 LPARAM lParam) 4666 { 4667 /* 4668 TRACE("WndProc: hwnd = %08x, msg = %x, wParam = %x, lParam = %x\n", 4669 hwnd, uMsg, wParam, lParam); 4670 */ 4671 4672 HandleMouseHide(uMsg, lParam); 4673 4674 s_uMsg = uMsg; 4675 s_wParam = wParam; 4676 s_lParam = lParam; 4677 4678 switch (uMsg) 4679 { 4680 HANDLE_MSG(hwnd, WM_DEADCHAR, _OnDeadChar); 4681 HANDLE_MSG(hwnd, WM_SYSDEADCHAR, _OnDeadChar); 4682 /* HANDLE_MSG(hwnd, WM_ACTIVATE, _OnActivate); */ 4683 HANDLE_MSG(hwnd, WM_CLOSE, _OnClose); 4684 /* HANDLE_MSG(hwnd, WM_COMMAND, _OnCommand); */ 4685 HANDLE_MSG(hwnd, WM_DESTROY, _OnDestroy); 4686 HANDLE_MSG(hwnd, WM_DROPFILES, _OnDropFiles); 4687 HANDLE_MSG(hwnd, WM_HSCROLL, _OnScroll); 4688 HANDLE_MSG(hwnd, WM_KILLFOCUS, _OnKillFocus); 4689 #ifdef FEAT_MENU 4690 HANDLE_MSG(hwnd, WM_COMMAND, _OnMenu); 4691 #endif 4692 /* HANDLE_MSG(hwnd, WM_MOVE, _OnMove); */ 4693 /* HANDLE_MSG(hwnd, WM_NCACTIVATE, _OnNCActivate); */ 4694 HANDLE_MSG(hwnd, WM_SETFOCUS, _OnSetFocus); 4695 HANDLE_MSG(hwnd, WM_SIZE, _OnSize); 4696 /* HANDLE_MSG(hwnd, WM_SYSCOMMAND, _OnSysCommand); */ 4697 /* HANDLE_MSG(hwnd, WM_SYSKEYDOWN, _OnAltKey); */ 4698 HANDLE_MSG(hwnd, WM_VSCROLL, _OnScroll); 4699 // HANDLE_MSG(hwnd, WM_WINDOWPOSCHANGING, _OnWindowPosChanging); 4700 HANDLE_MSG(hwnd, WM_ACTIVATEAPP, _OnActivateApp); 4701 #ifdef FEAT_NETBEANS_INTG 4702 HANDLE_MSG(hwnd, WM_WINDOWPOSCHANGED, _OnWindowPosChanged); 4703 #endif 4704 4705 #ifdef FEAT_GUI_TABLINE 4706 case WM_RBUTTONUP: 4707 { 4708 if (gui_mch_showing_tabline()) 4709 { 4710 POINT pt; 4711 RECT rect; 4712 4713 /* 4714 * If the cursor is on the tabline, display the tab menu 4715 */ 4716 GetCursorPos((LPPOINT)&pt); 4717 GetWindowRect(s_textArea, &rect); 4718 if (pt.y < rect.top) 4719 { 4720 show_tabline_popup_menu(); 4721 return 0L; 4722 } 4723 } 4724 return MyWindowProc(hwnd, uMsg, wParam, lParam); 4725 } 4726 case WM_LBUTTONDBLCLK: 4727 { 4728 /* 4729 * If the user double clicked the tabline, create a new tab 4730 */ 4731 if (gui_mch_showing_tabline()) 4732 { 4733 POINT pt; 4734 RECT rect; 4735 4736 GetCursorPos((LPPOINT)&pt); 4737 GetWindowRect(s_textArea, &rect); 4738 if (pt.y < rect.top) 4739 send_tabline_menu_event(0, TABLINE_MENU_NEW); 4740 } 4741 return MyWindowProc(hwnd, uMsg, wParam, lParam); 4742 } 4743 #endif 4744 4745 case WM_QUERYENDSESSION: /* System wants to go down. */ 4746 gui_shell_closed(); /* Will exit when no changed buffers. */ 4747 return FALSE; /* Do NOT allow system to go down. */ 4748 4749 case WM_ENDSESSION: 4750 if (wParam) /* system only really goes down when wParam is TRUE */ 4751 { 4752 _OnEndSession(); 4753 return 0L; 4754 } 4755 break; 4756 4757 case WM_CHAR: 4758 /* Don't use HANDLE_MSG() for WM_CHAR, it truncates wParam to a single 4759 * byte while we want the UTF-16 character value. */ 4760 _OnChar(hwnd, (UINT)wParam, (int)(short)LOWORD(lParam)); 4761 return 0L; 4762 4763 case WM_SYSCHAR: 4764 /* 4765 * if 'winaltkeys' is "no", or it's "menu" and it's not a menu 4766 * shortcut key, handle like a typed ALT key, otherwise call Windows 4767 * ALT key handling. 4768 */ 4769 #ifdef FEAT_MENU 4770 if ( !gui.menu_is_active 4771 || p_wak[0] == 'n' 4772 || (p_wak[0] == 'm' && !gui_is_menu_shortcut((int)wParam)) 4773 ) 4774 #endif 4775 { 4776 _OnSysChar(hwnd, (UINT)wParam, (int)(short)LOWORD(lParam)); 4777 return 0L; 4778 } 4779 #ifdef FEAT_MENU 4780 else 4781 return MyWindowProc(hwnd, uMsg, wParam, lParam); 4782 #endif 4783 4784 case WM_SYSKEYUP: 4785 #ifdef FEAT_MENU 4786 /* This used to be done only when menu is active: ALT key is used for 4787 * that. But that caused problems when menu is disabled and using 4788 * Alt-Tab-Esc: get into a strange state where no mouse-moved events 4789 * are received, mouse pointer remains hidden. */ 4790 return MyWindowProc(hwnd, uMsg, wParam, lParam); 4791 #else 4792 return 0L; 4793 #endif 4794 4795 case WM_SIZING: /* HANDLE_MSG doesn't seem to handle this one */ 4796 return _DuringSizing((UINT)wParam, (LPRECT)lParam); 4797 4798 case WM_MOUSEWHEEL: 4799 _OnMouseWheel(hwnd, HIWORD(wParam)); 4800 return 0L; 4801 4802 /* Notification for change in SystemParametersInfo() */ 4803 case WM_SETTINGCHANGE: 4804 return _OnSettingChange((UINT)wParam); 4805 4806 #if defined(FEAT_TOOLBAR) || defined(FEAT_GUI_TABLINE) 4807 case WM_NOTIFY: 4808 switch (((LPNMHDR) lParam)->code) 4809 { 4810 case TTN_GETDISPINFOW: 4811 case TTN_GETDISPINFO: 4812 { 4813 LPNMHDR hdr = (LPNMHDR)lParam; 4814 char_u *str = NULL; 4815 static void *tt_text = NULL; 4816 4817 VIM_CLEAR(tt_text); 4818 4819 # ifdef FEAT_GUI_TABLINE 4820 if (gui_mch_showing_tabline() 4821 && hdr->hwndFrom == TabCtrl_GetToolTips(s_tabhwnd)) 4822 { 4823 POINT pt; 4824 /* 4825 * Mouse is over the GUI tabline. Display the 4826 * tooltip for the tab under the cursor 4827 * 4828 * Get the cursor position within the tab control 4829 */ 4830 GetCursorPos(&pt); 4831 if (ScreenToClient(s_tabhwnd, &pt) != 0) 4832 { 4833 TCHITTESTINFO htinfo; 4834 int idx; 4835 4836 /* 4837 * Get the tab under the cursor 4838 */ 4839 htinfo.pt.x = pt.x; 4840 htinfo.pt.y = pt.y; 4841 idx = TabCtrl_HitTest(s_tabhwnd, &htinfo); 4842 if (idx != -1) 4843 { 4844 tabpage_T *tp; 4845 4846 tp = find_tabpage(idx + 1); 4847 if (tp != NULL) 4848 { 4849 get_tabline_label(tp, TRUE); 4850 str = NameBuff; 4851 } 4852 } 4853 } 4854 } 4855 # endif 4856 # ifdef FEAT_TOOLBAR 4857 # ifdef FEAT_GUI_TABLINE 4858 else 4859 # endif 4860 { 4861 UINT idButton; 4862 vimmenu_T *pMenu; 4863 4864 idButton = (UINT) hdr->idFrom; 4865 pMenu = gui_mswin_find_menu(root_menu, idButton); 4866 if (pMenu) 4867 str = pMenu->strings[MENU_INDEX_TIP]; 4868 } 4869 # endif 4870 if (str != NULL) 4871 { 4872 if (hdr->code == TTN_GETDISPINFOW) 4873 { 4874 LPNMTTDISPINFOW lpdi = (LPNMTTDISPINFOW)lParam; 4875 4876 /* Set the maximum width, this also enables using 4877 * \n for line break. */ 4878 SendMessage(lpdi->hdr.hwndFrom, TTM_SETMAXTIPWIDTH, 4879 0, 500); 4880 4881 tt_text = enc_to_utf16(str, NULL); 4882 lpdi->lpszText = tt_text; 4883 /* can't show tooltip if failed */ 4884 } 4885 else 4886 { 4887 LPNMTTDISPINFO lpdi = (LPNMTTDISPINFO)lParam; 4888 4889 /* Set the maximum width, this also enables using 4890 * \n for line break. */ 4891 SendMessage(lpdi->hdr.hwndFrom, TTM_SETMAXTIPWIDTH, 4892 0, 500); 4893 4894 if (STRLEN(str) < sizeof(lpdi->szText) 4895 || ((tt_text = vim_strsave(str)) == NULL)) 4896 vim_strncpy((char_u *)lpdi->szText, str, 4897 sizeof(lpdi->szText) - 1); 4898 else 4899 lpdi->lpszText = tt_text; 4900 } 4901 } 4902 } 4903 break; 4904 # ifdef FEAT_GUI_TABLINE 4905 case TCN_SELCHANGE: 4906 if (gui_mch_showing_tabline() 4907 && ((LPNMHDR)lParam)->hwndFrom == s_tabhwnd) 4908 { 4909 send_tabline_event(TabCtrl_GetCurSel(s_tabhwnd) + 1); 4910 return 0L; 4911 } 4912 break; 4913 4914 case NM_RCLICK: 4915 if (gui_mch_showing_tabline() 4916 && ((LPNMHDR)lParam)->hwndFrom == s_tabhwnd) 4917 { 4918 show_tabline_popup_menu(); 4919 return 0L; 4920 } 4921 break; 4922 # endif 4923 default: 4924 # ifdef FEAT_GUI_TABLINE 4925 if (gui_mch_showing_tabline() 4926 && ((LPNMHDR)lParam)->hwndFrom == s_tabhwnd) 4927 return MyWindowProc(hwnd, uMsg, wParam, lParam); 4928 # endif 4929 break; 4930 } 4931 break; 4932 #endif 4933 #if defined(MENUHINTS) && defined(FEAT_MENU) 4934 case WM_MENUSELECT: 4935 if (((UINT) HIWORD(wParam) 4936 & (0xffff ^ (MF_MOUSESELECT + MF_BITMAP + MF_POPUP))) 4937 == MF_HILITE 4938 && (State & CMDLINE) == 0) 4939 { 4940 UINT idButton; 4941 vimmenu_T *pMenu; 4942 static int did_menu_tip = FALSE; 4943 4944 if (did_menu_tip) 4945 { 4946 msg_clr_cmdline(); 4947 setcursor(); 4948 out_flush(); 4949 did_menu_tip = FALSE; 4950 } 4951 4952 idButton = (UINT)LOWORD(wParam); 4953 pMenu = gui_mswin_find_menu(root_menu, idButton); 4954 if (pMenu != NULL && pMenu->strings[MENU_INDEX_TIP] != 0 4955 && GetMenuState(s_menuBar, pMenu->id, MF_BYCOMMAND) != -1) 4956 { 4957 ++msg_hist_off; 4958 msg((char *)pMenu->strings[MENU_INDEX_TIP]); 4959 --msg_hist_off; 4960 setcursor(); 4961 out_flush(); 4962 did_menu_tip = TRUE; 4963 } 4964 return 0L; 4965 } 4966 break; 4967 #endif 4968 case WM_NCHITTEST: 4969 { 4970 LRESULT result; 4971 int x, y; 4972 int xPos = GET_X_LPARAM(lParam); 4973 4974 result = MyWindowProc(hwnd, uMsg, wParam, lParam); 4975 if (result == HTCLIENT) 4976 { 4977 #ifdef FEAT_GUI_TABLINE 4978 if (gui_mch_showing_tabline()) 4979 { 4980 int yPos = GET_Y_LPARAM(lParam); 4981 RECT rct; 4982 4983 /* If the cursor is on the GUI tabline, don't process this 4984 * event */ 4985 GetWindowRect(s_textArea, &rct); 4986 if (yPos < rct.top) 4987 return result; 4988 } 4989 #endif 4990 (void)gui_mch_get_winpos(&x, &y); 4991 xPos -= x; 4992 4993 if (xPos < 48) /* <VN> TODO should use system metric? */ 4994 return HTBOTTOMLEFT; 4995 else 4996 return HTBOTTOMRIGHT; 4997 } 4998 else 4999 return result; 5000 } 5001 /* break; notreached */ 5002 5003 #ifdef FEAT_MBYTE_IME 5004 case WM_IME_NOTIFY: 5005 if (!_OnImeNotify(hwnd, (DWORD)wParam, (DWORD)lParam)) 5006 return MyWindowProc(hwnd, uMsg, wParam, lParam); 5007 return 1L; 5008 5009 case WM_IME_COMPOSITION: 5010 if (!_OnImeComposition(hwnd, wParam, lParam)) 5011 return MyWindowProc(hwnd, uMsg, wParam, lParam); 5012 return 1L; 5013 #endif 5014 5015 default: 5016 #ifdef MSWIN_FIND_REPLACE 5017 if (uMsg == s_findrep_msg && s_findrep_msg != 0) 5018 { 5019 _OnFindRepl(); 5020 } 5021 #endif 5022 return MyWindowProc(hwnd, uMsg, wParam, lParam); 5023 } 5024 5025 return DefWindowProc(hwnd, uMsg, wParam, lParam); 5026 } 5027 5028 /* 5029 * End of call-back routines 5030 */ 5031 5032 /* parent window, if specified with -P */ 5033 HWND vim_parent_hwnd = NULL; 5034 5035 static BOOL CALLBACK 5036 FindWindowTitle(HWND hwnd, LPARAM lParam) 5037 { 5038 char buf[2048]; 5039 char *title = (char *)lParam; 5040 5041 if (GetWindowText(hwnd, buf, sizeof(buf))) 5042 { 5043 if (strstr(buf, title) != NULL) 5044 { 5045 /* Found it. Store the window ref. and quit searching if MDI 5046 * works. */ 5047 vim_parent_hwnd = FindWindowEx(hwnd, NULL, "MDIClient", NULL); 5048 if (vim_parent_hwnd != NULL) 5049 return FALSE; 5050 } 5051 } 5052 return TRUE; /* continue searching */ 5053 } 5054 5055 /* 5056 * Invoked for '-P "title"' argument: search for parent application to open 5057 * our window in. 5058 */ 5059 void 5060 gui_mch_set_parent(char *title) 5061 { 5062 EnumWindows(FindWindowTitle, (LPARAM)title); 5063 if (vim_parent_hwnd == NULL) 5064 { 5065 semsg(_("E671: Cannot find window title \"%s\""), title); 5066 mch_exit(2); 5067 } 5068 } 5069 5070 #ifndef FEAT_OLE 5071 static void 5072 ole_error(char *arg) 5073 { 5074 char buf[IOSIZE]; 5075 5076 /* Can't use emsg() here, we have not finished initialisation yet. */ 5077 vim_snprintf(buf, IOSIZE, 5078 _("E243: Argument not supported: \"-%s\"; Use the OLE version."), 5079 arg); 5080 mch_errmsg(buf); 5081 } 5082 #endif 5083 5084 /* 5085 * Parse the GUI related command-line arguments. Any arguments used are 5086 * deleted from argv, and *argc is decremented accordingly. This is called 5087 * when vim is started, whether or not the GUI has been started. 5088 */ 5089 void 5090 gui_mch_prepare(int *argc, char **argv) 5091 { 5092 int silent = FALSE; 5093 int idx; 5094 5095 /* Check for special OLE command line parameters */ 5096 if ((*argc == 2 || *argc == 3) && (argv[1][0] == '-' || argv[1][0] == '/')) 5097 { 5098 /* Check for a "-silent" argument first. */ 5099 if (*argc == 3 && STRICMP(argv[1] + 1, "silent") == 0 5100 && (argv[2][0] == '-' || argv[2][0] == '/')) 5101 { 5102 silent = TRUE; 5103 idx = 2; 5104 } 5105 else 5106 idx = 1; 5107 5108 /* Register Vim as an OLE Automation server */ 5109 if (STRICMP(argv[idx] + 1, "register") == 0) 5110 { 5111 #ifdef FEAT_OLE 5112 RegisterMe(silent); 5113 mch_exit(0); 5114 #else 5115 if (!silent) 5116 ole_error("register"); 5117 mch_exit(2); 5118 #endif 5119 } 5120 5121 /* Unregister Vim as an OLE Automation server */ 5122 if (STRICMP(argv[idx] + 1, "unregister") == 0) 5123 { 5124 #ifdef FEAT_OLE 5125 UnregisterMe(!silent); 5126 mch_exit(0); 5127 #else 5128 if (!silent) 5129 ole_error("unregister"); 5130 mch_exit(2); 5131 #endif 5132 } 5133 5134 /* Ignore an -embedding argument. It is only relevant if the 5135 * application wants to treat the case when it is started manually 5136 * differently from the case where it is started via automation (and 5137 * we don't). 5138 */ 5139 if (STRICMP(argv[idx] + 1, "embedding") == 0) 5140 { 5141 #ifdef FEAT_OLE 5142 *argc = 1; 5143 #else 5144 ole_error("embedding"); 5145 mch_exit(2); 5146 #endif 5147 } 5148 } 5149 5150 #ifdef FEAT_OLE 5151 { 5152 int bDoRestart = FALSE; 5153 5154 InitOLE(&bDoRestart); 5155 /* automatically exit after registering */ 5156 if (bDoRestart) 5157 mch_exit(0); 5158 } 5159 #endif 5160 5161 #ifdef FEAT_NETBEANS_INTG 5162 { 5163 /* stolen from gui_x11.c */ 5164 int arg; 5165 5166 for (arg = 1; arg < *argc; arg++) 5167 if (strncmp("-nb", argv[arg], 3) == 0) 5168 { 5169 netbeansArg = argv[arg]; 5170 mch_memmove(&argv[arg], &argv[arg + 1], 5171 (--*argc - arg) * sizeof(char *)); 5172 argv[*argc] = NULL; 5173 break; /* enough? */ 5174 } 5175 } 5176 #endif 5177 } 5178 5179 /* 5180 * Initialise the GUI. Create all the windows, set up all the call-backs 5181 * etc. 5182 */ 5183 int 5184 gui_mch_init(void) 5185 { 5186 const char szVimWndClass[] = VIM_CLASS; 5187 const char szTextAreaClass[] = "VimTextArea"; 5188 WNDCLASS wndclass; 5189 const WCHAR szVimWndClassW[] = VIM_CLASSW; 5190 const WCHAR szTextAreaClassW[] = L"VimTextArea"; 5191 WNDCLASSW wndclassw; 5192 #ifdef GLOBAL_IME 5193 ATOM atom; 5194 #endif 5195 5196 /* Return here if the window was already opened (happens when 5197 * gui_mch_dialog() is called early). */ 5198 if (s_hwnd != NULL) 5199 goto theend; 5200 5201 /* 5202 * Load the tearoff bitmap 5203 */ 5204 #ifdef FEAT_TEAROFF 5205 s_htearbitmap = LoadBitmap(s_hinst, "IDB_TEAROFF"); 5206 #endif 5207 5208 gui.scrollbar_width = GetSystemMetrics(SM_CXVSCROLL); 5209 gui.scrollbar_height = GetSystemMetrics(SM_CYHSCROLL); 5210 #ifdef FEAT_MENU 5211 gui.menu_height = 0; /* Windows takes care of this */ 5212 #endif 5213 gui.border_width = 0; 5214 5215 s_brush = CreateSolidBrush(GetSysColor(COLOR_BTNFACE)); 5216 5217 /* First try using the wide version, so that we can use any title. 5218 * Otherwise only characters in the active codepage will work. */ 5219 if (GetClassInfoW(s_hinst, szVimWndClassW, &wndclassw) == 0) 5220 { 5221 wndclassw.style = CS_DBLCLKS; 5222 wndclassw.lpfnWndProc = _WndProc; 5223 wndclassw.cbClsExtra = 0; 5224 wndclassw.cbWndExtra = 0; 5225 wndclassw.hInstance = s_hinst; 5226 wndclassw.hIcon = LoadIcon(wndclassw.hInstance, "IDR_VIM"); 5227 wndclassw.hCursor = LoadCursor(NULL, IDC_ARROW); 5228 wndclassw.hbrBackground = s_brush; 5229 wndclassw.lpszMenuName = NULL; 5230 wndclassw.lpszClassName = szVimWndClassW; 5231 5232 if (( 5233 #ifdef GLOBAL_IME 5234 atom = 5235 #endif 5236 RegisterClassW(&wndclassw)) == 0) 5237 return FAIL; 5238 else 5239 wide_WindowProc = TRUE; 5240 } 5241 5242 if (!wide_WindowProc) 5243 if (GetClassInfo(s_hinst, szVimWndClass, &wndclass) == 0) 5244 { 5245 wndclass.style = CS_DBLCLKS; 5246 wndclass.lpfnWndProc = _WndProc; 5247 wndclass.cbClsExtra = 0; 5248 wndclass.cbWndExtra = 0; 5249 wndclass.hInstance = s_hinst; 5250 wndclass.hIcon = LoadIcon(wndclass.hInstance, "IDR_VIM"); 5251 wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); 5252 wndclass.hbrBackground = s_brush; 5253 wndclass.lpszMenuName = NULL; 5254 wndclass.lpszClassName = szVimWndClass; 5255 5256 if (( 5257 #ifdef GLOBAL_IME 5258 atom = 5259 #endif 5260 RegisterClass(&wndclass)) == 0) 5261 return FAIL; 5262 } 5263 5264 if (vim_parent_hwnd != NULL) 5265 { 5266 #ifdef HAVE_TRY_EXCEPT 5267 __try 5268 { 5269 #endif 5270 /* Open inside the specified parent window. 5271 * TODO: last argument should point to a CLIENTCREATESTRUCT 5272 * structure. */ 5273 s_hwnd = CreateWindowEx( 5274 WS_EX_MDICHILD, 5275 szVimWndClass, "Vim MSWindows GUI", 5276 WS_OVERLAPPEDWINDOW | WS_CHILD 5277 | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | 0xC000, 5278 gui_win_x == -1 ? CW_USEDEFAULT : gui_win_x, 5279 gui_win_y == -1 ? CW_USEDEFAULT : gui_win_y, 5280 100, /* Any value will do */ 5281 100, /* Any value will do */ 5282 vim_parent_hwnd, NULL, 5283 s_hinst, NULL); 5284 #ifdef HAVE_TRY_EXCEPT 5285 } 5286 __except(EXCEPTION_EXECUTE_HANDLER) 5287 { 5288 /* NOP */ 5289 } 5290 #endif 5291 if (s_hwnd == NULL) 5292 { 5293 emsg(_("E672: Unable to open window inside MDI application")); 5294 mch_exit(2); 5295 } 5296 } 5297 else 5298 { 5299 /* If the provided windowid is not valid reset it to zero, so that it 5300 * is ignored and we open our own window. */ 5301 if (IsWindow((HWND)win_socket_id) <= 0) 5302 win_socket_id = 0; 5303 5304 /* Create a window. If win_socket_id is not zero without border and 5305 * titlebar, it will be reparented below. */ 5306 s_hwnd = CreateWindow( 5307 szVimWndClass, "Vim MSWindows GUI", 5308 (win_socket_id == 0 ? WS_OVERLAPPEDWINDOW : WS_POPUP) 5309 | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, 5310 gui_win_x == -1 ? CW_USEDEFAULT : gui_win_x, 5311 gui_win_y == -1 ? CW_USEDEFAULT : gui_win_y, 5312 100, /* Any value will do */ 5313 100, /* Any value will do */ 5314 NULL, NULL, 5315 s_hinst, NULL); 5316 if (s_hwnd != NULL && win_socket_id != 0) 5317 { 5318 SetParent(s_hwnd, (HWND)win_socket_id); 5319 ShowWindow(s_hwnd, SW_SHOWMAXIMIZED); 5320 } 5321 } 5322 5323 if (s_hwnd == NULL) 5324 return FAIL; 5325 5326 #ifdef GLOBAL_IME 5327 global_ime_init(atom, s_hwnd); 5328 #endif 5329 #if defined(FEAT_MBYTE_IME) && defined(DYNAMIC_IME) 5330 dyn_imm_load(); 5331 #endif 5332 5333 /* Create the text area window */ 5334 if (wide_WindowProc) 5335 { 5336 if (GetClassInfoW(s_hinst, szTextAreaClassW, &wndclassw) == 0) 5337 { 5338 wndclassw.style = CS_OWNDC; 5339 wndclassw.lpfnWndProc = _TextAreaWndProc; 5340 wndclassw.cbClsExtra = 0; 5341 wndclassw.cbWndExtra = 0; 5342 wndclassw.hInstance = s_hinst; 5343 wndclassw.hIcon = NULL; 5344 wndclassw.hCursor = LoadCursor(NULL, IDC_ARROW); 5345 wndclassw.hbrBackground = NULL; 5346 wndclassw.lpszMenuName = NULL; 5347 wndclassw.lpszClassName = szTextAreaClassW; 5348 5349 if (RegisterClassW(&wndclassw) == 0) 5350 return FAIL; 5351 } 5352 5353 s_textArea = CreateWindowExW( 5354 0, 5355 szTextAreaClassW, L"Vim text area", 5356 WS_CHILD | WS_VISIBLE, 0, 0, 5357 100, // Any value will do for now 5358 100, // Any value will do for now 5359 s_hwnd, NULL, 5360 s_hinst, NULL); 5361 } 5362 else if (GetClassInfo(s_hinst, szTextAreaClass, &wndclass) == 0) 5363 { 5364 wndclass.style = CS_OWNDC; 5365 wndclass.lpfnWndProc = _TextAreaWndProc; 5366 wndclass.cbClsExtra = 0; 5367 wndclass.cbWndExtra = 0; 5368 wndclass.hInstance = s_hinst; 5369 wndclass.hIcon = NULL; 5370 wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); 5371 wndclass.hbrBackground = NULL; 5372 wndclass.lpszMenuName = NULL; 5373 wndclass.lpszClassName = szTextAreaClass; 5374 5375 if (RegisterClass(&wndclass) == 0) 5376 return FAIL; 5377 5378 s_textArea = CreateWindowEx( 5379 0, 5380 szTextAreaClass, "Vim text area", 5381 WS_CHILD | WS_VISIBLE, 0, 0, 5382 100, // Any value will do for now 5383 100, // Any value will do for now 5384 s_hwnd, NULL, 5385 s_hinst, NULL); 5386 } 5387 5388 if (s_textArea == NULL) 5389 return FAIL; 5390 5391 #ifdef FEAT_LIBCALL 5392 /* Try loading an icon from $RUNTIMEPATH/bitmaps/vim.ico. */ 5393 { 5394 HANDLE hIcon = NULL; 5395 5396 if (mch_icon_load(&hIcon) == OK && hIcon != NULL) 5397 SendMessage(s_hwnd, WM_SETICON, ICON_SMALL, (LPARAM)hIcon); 5398 } 5399 #endif 5400 5401 #ifdef FEAT_MENU 5402 s_menuBar = CreateMenu(); 5403 #endif 5404 s_hdc = GetDC(s_textArea); 5405 5406 DragAcceptFiles(s_hwnd, TRUE); 5407 5408 /* Do we need to bother with this? */ 5409 /* m_fMouseAvail = GetSystemMetrics(SM_MOUSEPRESENT); */ 5410 5411 /* Get background/foreground colors from the system */ 5412 gui_mch_def_colors(); 5413 5414 /* Get the colors from the "Normal" group (set in syntax.c or in a vimrc 5415 * file) */ 5416 set_normal_colors(); 5417 5418 /* 5419 * Check that none of the colors are the same as the background color. 5420 * Then store the current values as the defaults. 5421 */ 5422 gui_check_colors(); 5423 gui.def_norm_pixel = gui.norm_pixel; 5424 gui.def_back_pixel = gui.back_pixel; 5425 5426 /* Get the colors for the highlight groups (gui_check_colors() might have 5427 * changed them) */ 5428 highlight_gui_started(); 5429 5430 /* 5431 * Start out by adding the configured border width into the border offset. 5432 */ 5433 gui.border_offset = gui.border_width; 5434 5435 /* 5436 * Set up for Intellimouse processing 5437 */ 5438 init_mouse_wheel(); 5439 5440 /* 5441 * compute a couple of metrics used for the dialogs 5442 */ 5443 get_dialog_font_metrics(); 5444 #ifdef FEAT_TOOLBAR 5445 /* 5446 * Create the toolbar 5447 */ 5448 initialise_toolbar(); 5449 #endif 5450 #ifdef FEAT_GUI_TABLINE 5451 /* 5452 * Create the tabline 5453 */ 5454 initialise_tabline(); 5455 #endif 5456 #ifdef MSWIN_FIND_REPLACE 5457 /* 5458 * Initialise the dialog box stuff 5459 */ 5460 s_findrep_msg = RegisterWindowMessage(FINDMSGSTRING); 5461 5462 /* Initialise the struct */ 5463 s_findrep_struct.lStructSize = sizeof(s_findrep_struct); 5464 s_findrep_struct.lpstrFindWhat = (LPSTR)alloc(MSWIN_FR_BUFSIZE); 5465 s_findrep_struct.lpstrFindWhat[0] = NUL; 5466 s_findrep_struct.lpstrReplaceWith = (LPSTR)alloc(MSWIN_FR_BUFSIZE); 5467 s_findrep_struct.lpstrReplaceWith[0] = NUL; 5468 s_findrep_struct.wFindWhatLen = MSWIN_FR_BUFSIZE; 5469 s_findrep_struct.wReplaceWithLen = MSWIN_FR_BUFSIZE; 5470 s_findrep_struct_w.lStructSize = sizeof(s_findrep_struct_w); 5471 s_findrep_struct_w.lpstrFindWhat = 5472 (LPWSTR)alloc(MSWIN_FR_BUFSIZE * sizeof(WCHAR)); 5473 s_findrep_struct_w.lpstrFindWhat[0] = NUL; 5474 s_findrep_struct_w.lpstrReplaceWith = 5475 (LPWSTR)alloc(MSWIN_FR_BUFSIZE * sizeof(WCHAR)); 5476 s_findrep_struct_w.lpstrReplaceWith[0] = NUL; 5477 s_findrep_struct_w.wFindWhatLen = MSWIN_FR_BUFSIZE; 5478 s_findrep_struct_w.wReplaceWithLen = MSWIN_FR_BUFSIZE; 5479 #endif 5480 5481 #ifdef FEAT_EVAL 5482 # if !defined(_MSC_VER) || (_MSC_VER < 1400) 5483 /* Define HandleToLong for old MS and non-MS compilers if not defined. */ 5484 # ifndef HandleToLong 5485 # define HandleToLong(h) ((long)(intptr_t)(h)) 5486 # endif 5487 # endif 5488 /* set the v:windowid variable */ 5489 set_vim_var_nr(VV_WINDOWID, HandleToLong(s_hwnd)); 5490 #endif 5491 5492 #ifdef FEAT_RENDER_OPTIONS 5493 if (p_rop) 5494 (void)gui_mch_set_rendering_options(p_rop); 5495 #endif 5496 5497 theend: 5498 /* Display any pending error messages */ 5499 display_errors(); 5500 5501 return OK; 5502 } 5503 5504 /* 5505 * Get the size of the screen, taking position on multiple monitors into 5506 * account (if supported). 5507 */ 5508 static void 5509 get_work_area(RECT *spi_rect) 5510 { 5511 HMONITOR mon; 5512 MONITORINFO moninfo; 5513 5514 /* work out which monitor the window is on, and get *its* work area */ 5515 mon = MonitorFromWindow(s_hwnd, MONITOR_DEFAULTTOPRIMARY); 5516 if (mon != NULL) 5517 { 5518 moninfo.cbSize = sizeof(MONITORINFO); 5519 if (GetMonitorInfo(mon, &moninfo)) 5520 { 5521 *spi_rect = moninfo.rcWork; 5522 return; 5523 } 5524 } 5525 /* this is the old method... */ 5526 SystemParametersInfo(SPI_GETWORKAREA, 0, spi_rect, 0); 5527 } 5528 5529 /* 5530 * Set the size of the window to the given width and height in pixels. 5531 */ 5532 void 5533 gui_mch_set_shellsize( 5534 int width, 5535 int height, 5536 int min_width UNUSED, 5537 int min_height UNUSED, 5538 int base_width UNUSED, 5539 int base_height UNUSED, 5540 int direction) 5541 { 5542 RECT workarea_rect; 5543 int win_width, win_height; 5544 WINDOWPLACEMENT wndpl; 5545 5546 /* Try to keep window completely on screen. */ 5547 /* Get position of the screen work area. This is the part that is not 5548 * used by the taskbar or appbars. */ 5549 get_work_area(&workarea_rect); 5550 5551 /* Get current position of our window. Note that the .left and .top are 5552 * relative to the work area. */ 5553 wndpl.length = sizeof(WINDOWPLACEMENT); 5554 GetWindowPlacement(s_hwnd, &wndpl); 5555 5556 /* Resizing a maximized window looks very strange, unzoom it first. 5557 * But don't do it when still starting up, it may have been requested in 5558 * the shortcut. */ 5559 if (wndpl.showCmd == SW_SHOWMAXIMIZED && starting == 0) 5560 { 5561 ShowWindow(s_hwnd, SW_SHOWNORMAL); 5562 /* Need to get the settings of the normal window. */ 5563 GetWindowPlacement(s_hwnd, &wndpl); 5564 } 5565 5566 /* compute the size of the outside of the window */ 5567 win_width = width + (GetSystemMetrics(SM_CXFRAME) + 5568 GetSystemMetrics(SM_CXPADDEDBORDER)) * 2; 5569 win_height = height + (GetSystemMetrics(SM_CYFRAME) + 5570 GetSystemMetrics(SM_CXPADDEDBORDER)) * 2 5571 + GetSystemMetrics(SM_CYCAPTION) 5572 #ifdef FEAT_MENU 5573 + gui_mswin_get_menu_height(FALSE) 5574 #endif 5575 ; 5576 5577 /* The following should take care of keeping Vim on the same monitor, no 5578 * matter if the secondary monitor is left or right of the primary 5579 * monitor. */ 5580 wndpl.rcNormalPosition.right = wndpl.rcNormalPosition.left + win_width; 5581 wndpl.rcNormalPosition.bottom = wndpl.rcNormalPosition.top + win_height; 5582 5583 /* If the window is going off the screen, move it on to the screen. */ 5584 if ((direction & RESIZE_HOR) 5585 && wndpl.rcNormalPosition.right > workarea_rect.right) 5586 OffsetRect(&wndpl.rcNormalPosition, 5587 workarea_rect.right - wndpl.rcNormalPosition.right, 0); 5588 5589 if ((direction & RESIZE_HOR) 5590 && wndpl.rcNormalPosition.left < workarea_rect.left) 5591 OffsetRect(&wndpl.rcNormalPosition, 5592 workarea_rect.left - wndpl.rcNormalPosition.left, 0); 5593 5594 if ((direction & RESIZE_VERT) 5595 && wndpl.rcNormalPosition.bottom > workarea_rect.bottom) 5596 OffsetRect(&wndpl.rcNormalPosition, 5597 0, workarea_rect.bottom - wndpl.rcNormalPosition.bottom); 5598 5599 if ((direction & RESIZE_VERT) 5600 && wndpl.rcNormalPosition.top < workarea_rect.top) 5601 OffsetRect(&wndpl.rcNormalPosition, 5602 0, workarea_rect.top - wndpl.rcNormalPosition.top); 5603 5604 /* set window position - we should use SetWindowPlacement rather than 5605 * SetWindowPos as the MSDN docs say the coord systems returned by 5606 * these two are not compatible. */ 5607 SetWindowPlacement(s_hwnd, &wndpl); 5608 5609 SetActiveWindow(s_hwnd); 5610 SetFocus(s_hwnd); 5611 5612 #ifdef FEAT_MENU 5613 /* Menu may wrap differently now */ 5614 gui_mswin_get_menu_height(!gui.starting); 5615 #endif 5616 } 5617 5618 5619 void 5620 gui_mch_set_scrollbar_thumb( 5621 scrollbar_T *sb, 5622 long val, 5623 long size, 5624 long max) 5625 { 5626 SCROLLINFO info; 5627 5628 sb->scroll_shift = 0; 5629 while (max > 32767) 5630 { 5631 max = (max + 1) >> 1; 5632 val >>= 1; 5633 size >>= 1; 5634 ++sb->scroll_shift; 5635 } 5636 5637 if (sb->scroll_shift > 0) 5638 ++size; 5639 5640 info.cbSize = sizeof(info); 5641 info.fMask = SIF_POS | SIF_RANGE | SIF_PAGE; 5642 info.nPos = val; 5643 info.nMin = 0; 5644 info.nMax = max; 5645 info.nPage = size; 5646 SetScrollInfo(sb->id, SB_CTL, &info, TRUE); 5647 } 5648 5649 5650 /* 5651 * Set the current text font. 5652 */ 5653 void 5654 gui_mch_set_font(GuiFont font) 5655 { 5656 gui.currFont = font; 5657 } 5658 5659 5660 /* 5661 * Set the current text foreground color. 5662 */ 5663 void 5664 gui_mch_set_fg_color(guicolor_T color) 5665 { 5666 gui.currFgColor = color; 5667 } 5668 5669 /* 5670 * Set the current text background color. 5671 */ 5672 void 5673 gui_mch_set_bg_color(guicolor_T color) 5674 { 5675 gui.currBgColor = color; 5676 } 5677 5678 /* 5679 * Set the current text special color. 5680 */ 5681 void 5682 gui_mch_set_sp_color(guicolor_T color) 5683 { 5684 gui.currSpColor = color; 5685 } 5686 5687 #ifdef FEAT_MBYTE_IME 5688 /* 5689 * Multi-byte handling, originally by Sung-Hoon Baek. 5690 * First static functions (no prototypes generated). 5691 */ 5692 # ifdef _MSC_VER 5693 # include <ime.h> /* Apparently not needed for Cygwin, MingW or Borland. */ 5694 # endif 5695 # include <imm.h> 5696 5697 /* 5698 * handle WM_IME_NOTIFY message 5699 */ 5700 static LRESULT 5701 _OnImeNotify(HWND hWnd, DWORD dwCommand, DWORD dwData UNUSED) 5702 { 5703 LRESULT lResult = 0; 5704 HIMC hImc; 5705 5706 if (!pImmGetContext || (hImc = pImmGetContext(hWnd)) == (HIMC)0) 5707 return lResult; 5708 switch (dwCommand) 5709 { 5710 case IMN_SETOPENSTATUS: 5711 if (pImmGetOpenStatus(hImc)) 5712 { 5713 pImmSetCompositionFont(hImc, &norm_logfont); 5714 im_set_position(gui.row, gui.col); 5715 5716 /* Disable langmap */ 5717 State &= ~LANGMAP; 5718 if (State & INSERT) 5719 { 5720 #if defined(FEAT_KEYMAP) 5721 /* Unshown 'keymap' in status lines */ 5722 if (curbuf->b_p_iminsert == B_IMODE_LMAP) 5723 { 5724 /* Save cursor position */ 5725 int old_row = gui.row; 5726 int old_col = gui.col; 5727 5728 // This must be called here before 5729 // status_redraw_curbuf(), otherwise the mode 5730 // message may appear in the wrong position. 5731 showmode(); 5732 status_redraw_curbuf(); 5733 update_screen(0); 5734 /* Restore cursor position */ 5735 gui.row = old_row; 5736 gui.col = old_col; 5737 } 5738 #endif 5739 } 5740 } 5741 gui_update_cursor(TRUE, FALSE); 5742 gui_mch_flush(); 5743 lResult = 0; 5744 break; 5745 } 5746 pImmReleaseContext(hWnd, hImc); 5747 return lResult; 5748 } 5749 5750 static LRESULT 5751 _OnImeComposition(HWND hwnd, WPARAM dbcs UNUSED, LPARAM param) 5752 { 5753 char_u *ret; 5754 int len; 5755 5756 if ((param & GCS_RESULTSTR) == 0) /* Composition unfinished. */ 5757 return 0; 5758 5759 ret = GetResultStr(hwnd, GCS_RESULTSTR, &len); 5760 if (ret != NULL) 5761 { 5762 add_to_input_buf_csi(ret, len); 5763 vim_free(ret); 5764 return 1; 5765 } 5766 return 0; 5767 } 5768 5769 /* 5770 * get the current composition string, in UCS-2; *lenp is the number of 5771 * *lenp is the number of Unicode characters. 5772 */ 5773 static short_u * 5774 GetCompositionString_inUCS2(HIMC hIMC, DWORD GCS, int *lenp) 5775 { 5776 LONG ret; 5777 LPWSTR wbuf = NULL; 5778 char_u *buf; 5779 5780 if (!pImmGetContext) 5781 return NULL; /* no imm32.dll */ 5782 5783 /* Try Unicode; this'll always work on NT regardless of codepage. */ 5784 ret = pImmGetCompositionStringW(hIMC, GCS, NULL, 0); 5785 if (ret == 0) 5786 return NULL; /* empty */ 5787 5788 if (ret > 0) 5789 { 5790 /* Allocate the requested buffer plus space for the NUL character. */ 5791 wbuf = (LPWSTR)alloc(ret + sizeof(WCHAR)); 5792 if (wbuf != NULL) 5793 { 5794 pImmGetCompositionStringW(hIMC, GCS, wbuf, ret); 5795 *lenp = ret / sizeof(WCHAR); 5796 } 5797 return (short_u *)wbuf; 5798 } 5799 5800 /* ret < 0; we got an error, so try the ANSI version. This'll work 5801 * on 9x/ME, but only if the codepage happens to be set to whatever 5802 * we're inputting. */ 5803 ret = pImmGetCompositionStringA(hIMC, GCS, NULL, 0); 5804 if (ret <= 0) 5805 return NULL; /* empty or error */ 5806 5807 buf = alloc(ret); 5808 if (buf == NULL) 5809 return NULL; 5810 pImmGetCompositionStringA(hIMC, GCS, buf, ret); 5811 5812 /* convert from codepage to UCS-2 */ 5813 MultiByteToWideChar_alloc(GetACP(), 0, (LPCSTR)buf, ret, &wbuf, lenp); 5814 vim_free(buf); 5815 5816 return (short_u *)wbuf; 5817 } 5818 5819 /* 5820 * void GetResultStr() 5821 * 5822 * This handles WM_IME_COMPOSITION with GCS_RESULTSTR flag on. 5823 * get complete composition string 5824 */ 5825 static char_u * 5826 GetResultStr(HWND hwnd, int GCS, int *lenp) 5827 { 5828 HIMC hIMC; /* Input context handle. */ 5829 short_u *buf = NULL; 5830 char_u *convbuf = NULL; 5831 5832 if (!pImmGetContext || (hIMC = pImmGetContext(hwnd)) == (HIMC)0) 5833 return NULL; 5834 5835 /* Reads in the composition string. */ 5836 buf = GetCompositionString_inUCS2(hIMC, GCS, lenp); 5837 if (buf == NULL) 5838 return NULL; 5839 5840 convbuf = utf16_to_enc(buf, lenp); 5841 pImmReleaseContext(hwnd, hIMC); 5842 vim_free(buf); 5843 return convbuf; 5844 } 5845 #endif 5846 5847 /* For global functions we need prototypes. */ 5848 #if defined(FEAT_MBYTE_IME) || defined(PROTO) 5849 5850 /* 5851 * set font to IM. 5852 */ 5853 void 5854 im_set_font(LOGFONT *lf) 5855 { 5856 HIMC hImc; 5857 5858 if (pImmGetContext && (hImc = pImmGetContext(s_hwnd)) != (HIMC)0) 5859 { 5860 pImmSetCompositionFont(hImc, lf); 5861 pImmReleaseContext(s_hwnd, hImc); 5862 } 5863 } 5864 5865 /* 5866 * Notify cursor position to IM. 5867 */ 5868 void 5869 im_set_position(int row, int col) 5870 { 5871 HIMC hImc; 5872 5873 if (pImmGetContext && (hImc = pImmGetContext(s_hwnd)) != (HIMC)0) 5874 { 5875 COMPOSITIONFORM cfs; 5876 5877 cfs.dwStyle = CFS_POINT; 5878 cfs.ptCurrentPos.x = FILL_X(col); 5879 cfs.ptCurrentPos.y = FILL_Y(row); 5880 MapWindowPoints(s_textArea, s_hwnd, &cfs.ptCurrentPos, 1); 5881 pImmSetCompositionWindow(hImc, &cfs); 5882 5883 pImmReleaseContext(s_hwnd, hImc); 5884 } 5885 } 5886 5887 /* 5888 * Set IM status on ("active" is TRUE) or off ("active" is FALSE). 5889 */ 5890 void 5891 im_set_active(int active) 5892 { 5893 HIMC hImc; 5894 static HIMC hImcOld = (HIMC)0; 5895 5896 if (pImmGetContext) /* if NULL imm32.dll wasn't loaded (yet) */ 5897 { 5898 if (p_imdisable) 5899 { 5900 if (hImcOld == (HIMC)0) 5901 { 5902 hImcOld = pImmGetContext(s_hwnd); 5903 if (hImcOld) 5904 pImmAssociateContext(s_hwnd, (HIMC)0); 5905 } 5906 active = FALSE; 5907 } 5908 else if (hImcOld != (HIMC)0) 5909 { 5910 pImmAssociateContext(s_hwnd, hImcOld); 5911 hImcOld = (HIMC)0; 5912 } 5913 5914 hImc = pImmGetContext(s_hwnd); 5915 if (hImc) 5916 { 5917 /* 5918 * for Korean ime 5919 */ 5920 HKL hKL = GetKeyboardLayout(0); 5921 5922 if (LOWORD(hKL) == MAKELANGID(LANG_KOREAN, SUBLANG_KOREAN)) 5923 { 5924 static DWORD dwConversionSaved = 0, dwSentenceSaved = 0; 5925 static BOOL bSaved = FALSE; 5926 5927 if (active) 5928 { 5929 /* if we have a saved conversion status, restore it */ 5930 if (bSaved) 5931 pImmSetConversionStatus(hImc, dwConversionSaved, 5932 dwSentenceSaved); 5933 bSaved = FALSE; 5934 } 5935 else 5936 { 5937 /* save conversion status and disable korean */ 5938 if (pImmGetConversionStatus(hImc, &dwConversionSaved, 5939 &dwSentenceSaved)) 5940 { 5941 bSaved = TRUE; 5942 pImmSetConversionStatus(hImc, 5943 dwConversionSaved & ~(IME_CMODE_NATIVE 5944 | IME_CMODE_FULLSHAPE), 5945 dwSentenceSaved); 5946 } 5947 } 5948 } 5949 5950 pImmSetOpenStatus(hImc, active); 5951 pImmReleaseContext(s_hwnd, hImc); 5952 } 5953 } 5954 } 5955 5956 /* 5957 * Get IM status. When IM is on, return not 0. Else return 0. 5958 */ 5959 int 5960 im_get_status(void) 5961 { 5962 int status = 0; 5963 HIMC hImc; 5964 5965 if (pImmGetContext && (hImc = pImmGetContext(s_hwnd)) != (HIMC)0) 5966 { 5967 status = pImmGetOpenStatus(hImc) ? 1 : 0; 5968 pImmReleaseContext(s_hwnd, hImc); 5969 } 5970 return status; 5971 } 5972 5973 #endif /* FEAT_MBYTE_IME */ 5974 5975 #if !defined(FEAT_MBYTE_IME) && defined(GLOBAL_IME) 5976 /* Win32 with GLOBAL IME */ 5977 5978 /* 5979 * Notify cursor position to IM. 5980 */ 5981 void 5982 im_set_position(int row, int col) 5983 { 5984 /* Win32 with GLOBAL IME */ 5985 POINT p; 5986 5987 p.x = FILL_X(col); 5988 p.y = FILL_Y(row); 5989 MapWindowPoints(s_textArea, s_hwnd, &p, 1); 5990 global_ime_set_position(&p); 5991 } 5992 5993 /* 5994 * Set IM status on ("active" is TRUE) or off ("active" is FALSE). 5995 */ 5996 void 5997 im_set_active(int active) 5998 { 5999 global_ime_set_status(active); 6000 } 6001 6002 /* 6003 * Get IM status. When IM is on, return not 0. Else return 0. 6004 */ 6005 int 6006 im_get_status(void) 6007 { 6008 return global_ime_get_status(); 6009 } 6010 #endif 6011 6012 /* 6013 * Convert latin9 text "text[len]" to ucs-2 in "unicodebuf". 6014 */ 6015 static void 6016 latin9_to_ucs(char_u *text, int len, WCHAR *unicodebuf) 6017 { 6018 int c; 6019 6020 while (--len >= 0) 6021 { 6022 c = *text++; 6023 switch (c) 6024 { 6025 case 0xa4: c = 0x20ac; break; /* euro */ 6026 case 0xa6: c = 0x0160; break; /* S hat */ 6027 case 0xa8: c = 0x0161; break; /* S -hat */ 6028 case 0xb4: c = 0x017d; break; /* Z hat */ 6029 case 0xb8: c = 0x017e; break; /* Z -hat */ 6030 case 0xbc: c = 0x0152; break; /* OE */ 6031 case 0xbd: c = 0x0153; break; /* oe */ 6032 case 0xbe: c = 0x0178; break; /* Y */ 6033 } 6034 *unicodebuf++ = c; 6035 } 6036 } 6037 6038 #ifdef FEAT_RIGHTLEFT 6039 /* 6040 * What is this for? In the case where you are using Win98 or Win2K or later, 6041 * and you are using a Hebrew font (or Arabic!), Windows does you a favor and 6042 * reverses the string sent to the TextOut... family. This sucks, because we 6043 * go to a lot of effort to do the right thing, and there doesn't seem to be a 6044 * way to tell Windblows not to do this! 6045 * 6046 * The short of it is that this 'RevOut' only gets called if you are running 6047 * one of the new, "improved" MS OSes, and only if you are running in 6048 * 'rightleft' mode. It makes display take *slightly* longer, but not 6049 * noticeably so. 6050 */ 6051 static void 6052 RevOut( HDC s_hdc, 6053 int col, 6054 int row, 6055 UINT foptions, 6056 CONST RECT *pcliprect, 6057 LPCTSTR text, 6058 UINT len, 6059 CONST INT *padding) 6060 { 6061 int ix; 6062 6063 for (ix = 0; ix < (int)len; ++ix) 6064 ExtTextOut(s_hdc, col + TEXT_X(ix), row, foptions, 6065 pcliprect, text + ix, 1, padding); 6066 } 6067 #endif 6068 6069 static void 6070 draw_line( 6071 int x1, 6072 int y1, 6073 int x2, 6074 int y2, 6075 COLORREF color) 6076 { 6077 #if defined(FEAT_DIRECTX) 6078 if (IS_ENABLE_DIRECTX()) 6079 DWriteContext_DrawLine(s_dwc, x1, y1, x2, y2, color); 6080 else 6081 #endif 6082 { 6083 HPEN hpen = CreatePen(PS_SOLID, 1, color); 6084 HPEN old_pen = SelectObject(s_hdc, hpen); 6085 MoveToEx(s_hdc, x1, y1, NULL); 6086 /* Note: LineTo() excludes the last pixel in the line. */ 6087 LineTo(s_hdc, x2, y2); 6088 DeleteObject(SelectObject(s_hdc, old_pen)); 6089 } 6090 } 6091 6092 static void 6093 set_pixel( 6094 int x, 6095 int y, 6096 COLORREF color) 6097 { 6098 #if defined(FEAT_DIRECTX) 6099 if (IS_ENABLE_DIRECTX()) 6100 DWriteContext_SetPixel(s_dwc, x, y, color); 6101 else 6102 #endif 6103 SetPixel(s_hdc, x, y, color); 6104 } 6105 6106 static void 6107 fill_rect( 6108 const RECT *rcp, 6109 HBRUSH hbr, 6110 COLORREF color) 6111 { 6112 #if defined(FEAT_DIRECTX) 6113 if (IS_ENABLE_DIRECTX()) 6114 DWriteContext_FillRect(s_dwc, rcp, color); 6115 else 6116 #endif 6117 { 6118 HBRUSH hbr2; 6119 6120 if (hbr == NULL) 6121 hbr2 = CreateSolidBrush(color); 6122 else 6123 hbr2 = hbr; 6124 FillRect(s_hdc, rcp, hbr2); 6125 if (hbr == NULL) 6126 DeleteBrush(hbr2); 6127 } 6128 } 6129 6130 void 6131 gui_mch_draw_string( 6132 int row, 6133 int col, 6134 char_u *text, 6135 int len, 6136 int flags) 6137 { 6138 static int *padding = NULL; 6139 static int pad_size = 0; 6140 int i; 6141 const RECT *pcliprect = NULL; 6142 UINT foptions = 0; 6143 static WCHAR *unicodebuf = NULL; 6144 static int *unicodepdy = NULL; 6145 static int unibuflen = 0; 6146 int n = 0; 6147 int y; 6148 6149 /* 6150 * Italic and bold text seems to have an extra row of pixels at the bottom 6151 * (below where the bottom of the character should be). If we draw the 6152 * characters with a solid background, the top row of pixels in the 6153 * character below will be overwritten. We can fix this by filling in the 6154 * background ourselves, to the correct character proportions, and then 6155 * writing the character in transparent mode. Still have a problem when 6156 * the character is "_", which gets written on to the character below. 6157 * New fix: set gui.char_ascent to -1. This shifts all characters up one 6158 * pixel in their slots, which fixes the problem with the bottom row of 6159 * pixels. We still need this code because otherwise the top row of pixels 6160 * becomes a problem. - webb. 6161 */ 6162 static HBRUSH hbr_cache[2] = {NULL, NULL}; 6163 static guicolor_T brush_color[2] = {INVALCOLOR, INVALCOLOR}; 6164 static int brush_lru = 0; 6165 HBRUSH hbr; 6166 RECT rc; 6167 6168 if (!(flags & DRAW_TRANSP)) 6169 { 6170 /* 6171 * Clear background first. 6172 * Note: FillRect() excludes right and bottom of rectangle. 6173 */ 6174 rc.left = FILL_X(col); 6175 rc.top = FILL_Y(row); 6176 if (has_mbyte) 6177 { 6178 /* Compute the length in display cells. */ 6179 rc.right = FILL_X(col + mb_string2cells(text, len)); 6180 } 6181 else 6182 rc.right = FILL_X(col + len); 6183 rc.bottom = FILL_Y(row + 1); 6184 6185 /* Cache the created brush, that saves a lot of time. We need two: 6186 * one for cursor background and one for the normal background. */ 6187 if (gui.currBgColor == brush_color[0]) 6188 { 6189 hbr = hbr_cache[0]; 6190 brush_lru = 1; 6191 } 6192 else if (gui.currBgColor == brush_color[1]) 6193 { 6194 hbr = hbr_cache[1]; 6195 brush_lru = 0; 6196 } 6197 else 6198 { 6199 if (hbr_cache[brush_lru] != NULL) 6200 DeleteBrush(hbr_cache[brush_lru]); 6201 hbr_cache[brush_lru] = CreateSolidBrush(gui.currBgColor); 6202 brush_color[brush_lru] = gui.currBgColor; 6203 hbr = hbr_cache[brush_lru]; 6204 brush_lru = !brush_lru; 6205 } 6206 6207 fill_rect(&rc, hbr, gui.currBgColor); 6208 6209 SetBkMode(s_hdc, TRANSPARENT); 6210 6211 /* 6212 * When drawing block cursor, prevent inverted character spilling 6213 * over character cell (can happen with bold/italic) 6214 */ 6215 if (flags & DRAW_CURSOR) 6216 { 6217 pcliprect = &rc; 6218 foptions = ETO_CLIPPED; 6219 } 6220 } 6221 SetTextColor(s_hdc, gui.currFgColor); 6222 SelectFont(s_hdc, gui.currFont); 6223 6224 #ifdef FEAT_DIRECTX 6225 if (IS_ENABLE_DIRECTX()) 6226 DWriteContext_SetFont(s_dwc, (HFONT)gui.currFont); 6227 #endif 6228 6229 if (pad_size != Columns || padding == NULL || padding[0] != gui.char_width) 6230 { 6231 vim_free(padding); 6232 pad_size = Columns; 6233 6234 /* Don't give an out-of-memory message here, it would call us 6235 * recursively. */ 6236 padding = (int *)lalloc(pad_size * sizeof(int), FALSE); 6237 if (padding != NULL) 6238 for (i = 0; i < pad_size; i++) 6239 padding[i] = gui.char_width; 6240 } 6241 6242 /* 6243 * We have to provide the padding argument because italic and bold versions 6244 * of fixed-width fonts are often one pixel or so wider than their normal 6245 * versions. 6246 * No check for DRAW_BOLD, Windows will have done it already. 6247 */ 6248 6249 /* Check if there are any UTF-8 characters. If not, use normal text 6250 * output to speed up output. */ 6251 if (enc_utf8) 6252 for (n = 0; n < len; ++n) 6253 if (text[n] >= 0x80) 6254 break; 6255 6256 #if defined(FEAT_DIRECTX) 6257 /* Quick hack to enable DirectWrite. To use DirectWrite (antialias), it is 6258 * required that unicode drawing routine, currently. So this forces it 6259 * enabled. */ 6260 if (IS_ENABLE_DIRECTX()) 6261 n = 0; /* Keep n < len, to enter block for unicode. */ 6262 #endif 6263 6264 /* Check if the Unicode buffer exists and is big enough. Create it 6265 * with the same length as the multi-byte string, the number of wide 6266 * characters is always equal or smaller. */ 6267 if ((enc_utf8 6268 || (enc_codepage > 0 && (int)GetACP() != enc_codepage) 6269 || enc_latin9) 6270 && (unicodebuf == NULL || len > unibuflen)) 6271 { 6272 vim_free(unicodebuf); 6273 unicodebuf = (WCHAR *)lalloc(len * sizeof(WCHAR), FALSE); 6274 6275 vim_free(unicodepdy); 6276 unicodepdy = (int *)lalloc(len * sizeof(int), FALSE); 6277 6278 unibuflen = len; 6279 } 6280 6281 if (enc_utf8 && n < len && unicodebuf != NULL) 6282 { 6283 /* Output UTF-8 characters. Composing characters should be 6284 * handled here. */ 6285 int i; 6286 int wlen; /* string length in words */ 6287 int clen; /* string length in characters */ 6288 int cells; /* cell width of string up to composing char */ 6289 int cw; /* width of current cell */ 6290 int c; 6291 6292 wlen = 0; 6293 clen = 0; 6294 cells = 0; 6295 for (i = 0; i < len; ) 6296 { 6297 c = utf_ptr2char(text + i); 6298 if (c >= 0x10000) 6299 { 6300 /* Turn into UTF-16 encoding. */ 6301 unicodebuf[wlen++] = ((c - 0x10000) >> 10) + 0xD800; 6302 unicodebuf[wlen++] = ((c - 0x10000) & 0x3ff) + 0xDC00; 6303 } 6304 else 6305 { 6306 unicodebuf[wlen++] = c; 6307 } 6308 6309 if (utf_iscomposing(c)) 6310 cw = 0; 6311 else 6312 { 6313 cw = utf_char2cells(c); 6314 if (cw > 2) /* don't use 4 for unprintable char */ 6315 cw = 1; 6316 } 6317 6318 if (unicodepdy != NULL) 6319 { 6320 /* Use unicodepdy to make characters fit as we expect, even 6321 * when the font uses different widths (e.g., bold character 6322 * is wider). */ 6323 if (c >= 0x10000) 6324 { 6325 unicodepdy[wlen - 2] = cw * gui.char_width; 6326 unicodepdy[wlen - 1] = 0; 6327 } 6328 else 6329 unicodepdy[wlen - 1] = cw * gui.char_width; 6330 } 6331 cells += cw; 6332 i += utf_ptr2len_len(text + i, len - i); 6333 ++clen; 6334 } 6335 #if defined(FEAT_DIRECTX) 6336 if (IS_ENABLE_DIRECTX()) 6337 { 6338 /* Add one to "cells" for italics. */ 6339 DWriteContext_DrawText(s_dwc, unicodebuf, wlen, 6340 TEXT_X(col), TEXT_Y(row), FILL_X(cells + 1), FILL_Y(1), 6341 gui.char_width, gui.currFgColor, 6342 foptions, pcliprect, unicodepdy); 6343 } 6344 else 6345 #endif 6346 ExtTextOutW(s_hdc, TEXT_X(col), TEXT_Y(row), 6347 foptions, pcliprect, unicodebuf, wlen, unicodepdy); 6348 len = cells; /* used for underlining */ 6349 } 6350 else if ((enc_codepage > 0 && (int)GetACP() != enc_codepage) || enc_latin9) 6351 { 6352 /* If we want to display codepage data, and the current CP is not the 6353 * ANSI one, we need to go via Unicode. */ 6354 if (unicodebuf != NULL) 6355 { 6356 if (enc_latin9) 6357 latin9_to_ucs(text, len, unicodebuf); 6358 else 6359 len = MultiByteToWideChar(enc_codepage, 6360 MB_PRECOMPOSED, 6361 (char *)text, len, 6362 (LPWSTR)unicodebuf, unibuflen); 6363 if (len != 0) 6364 { 6365 /* Use unicodepdy to make characters fit as we expect, even 6366 * when the font uses different widths (e.g., bold character 6367 * is wider). */ 6368 if (unicodepdy != NULL) 6369 { 6370 int i; 6371 int cw; 6372 6373 for (i = 0; i < len; ++i) 6374 { 6375 cw = utf_char2cells(unicodebuf[i]); 6376 if (cw > 2) 6377 cw = 1; 6378 unicodepdy[i] = cw * gui.char_width; 6379 } 6380 } 6381 ExtTextOutW(s_hdc, TEXT_X(col), TEXT_Y(row), 6382 foptions, pcliprect, unicodebuf, len, unicodepdy); 6383 } 6384 } 6385 } 6386 else 6387 { 6388 #ifdef FEAT_RIGHTLEFT 6389 /* Windows will mess up RL text, so we have to draw it character by 6390 * character. Only do this if RL is on, since it's slow. */ 6391 if (curwin->w_p_rl) 6392 RevOut(s_hdc, TEXT_X(col), TEXT_Y(row), 6393 foptions, pcliprect, (char *)text, len, padding); 6394 else 6395 #endif 6396 ExtTextOut(s_hdc, TEXT_X(col), TEXT_Y(row), 6397 foptions, pcliprect, (char *)text, len, padding); 6398 } 6399 6400 /* Underline */ 6401 if (flags & DRAW_UNDERL) 6402 { 6403 /* When p_linespace is 0, overwrite the bottom row of pixels. 6404 * Otherwise put the line just below the character. */ 6405 y = FILL_Y(row + 1) - 1; 6406 if (p_linespace > 1) 6407 y -= p_linespace - 1; 6408 draw_line(FILL_X(col), y, FILL_X(col + len), y, gui.currFgColor); 6409 } 6410 6411 /* Strikethrough */ 6412 if (flags & DRAW_STRIKE) 6413 { 6414 y = FILL_Y(row + 1) - gui.char_height/2; 6415 draw_line(FILL_X(col), y, FILL_X(col + len), y, gui.currSpColor); 6416 } 6417 6418 /* Undercurl */ 6419 if (flags & DRAW_UNDERC) 6420 { 6421 int x; 6422 int offset; 6423 static const int val[8] = {1, 0, 0, 0, 1, 2, 2, 2 }; 6424 6425 y = FILL_Y(row + 1) - 1; 6426 for (x = FILL_X(col); x < FILL_X(col + len); ++x) 6427 { 6428 offset = val[x % 8]; 6429 set_pixel(x, y - offset, gui.currSpColor); 6430 } 6431 } 6432 } 6433 6434 6435 /* 6436 * Output routines. 6437 */ 6438 6439 /* Flush any output to the screen */ 6440 void 6441 gui_mch_flush(void) 6442 { 6443 # if defined(__BORLANDC__) 6444 /* 6445 * The GdiFlush declaration (in Borland C 5.01 <wingdi.h>) is not a 6446 * prototype declaration. 6447 * The compiler complains if __stdcall is not used in both declarations. 6448 */ 6449 BOOL __stdcall GdiFlush(void); 6450 # endif 6451 6452 #if defined(FEAT_DIRECTX) 6453 if (IS_ENABLE_DIRECTX()) 6454 DWriteContext_Flush(s_dwc); 6455 #endif 6456 6457 GdiFlush(); 6458 } 6459 6460 static void 6461 clear_rect(RECT *rcp) 6462 { 6463 fill_rect(rcp, NULL, gui.back_pixel); 6464 } 6465 6466 6467 void 6468 gui_mch_get_screen_dimensions(int *screen_w, int *screen_h) 6469 { 6470 RECT workarea_rect; 6471 6472 get_work_area(&workarea_rect); 6473 6474 *screen_w = workarea_rect.right - workarea_rect.left 6475 - (GetSystemMetrics(SM_CXFRAME) + 6476 GetSystemMetrics(SM_CXPADDEDBORDER)) * 2; 6477 6478 /* FIXME: dirty trick: Because the gui_get_base_height() doesn't include 6479 * the menubar for MSwin, we subtract it from the screen height, so that 6480 * the window size can be made to fit on the screen. */ 6481 *screen_h = workarea_rect.bottom - workarea_rect.top 6482 - (GetSystemMetrics(SM_CYFRAME) + 6483 GetSystemMetrics(SM_CXPADDEDBORDER)) * 2 6484 - GetSystemMetrics(SM_CYCAPTION) 6485 #ifdef FEAT_MENU 6486 - gui_mswin_get_menu_height(FALSE) 6487 #endif 6488 ; 6489 } 6490 6491 6492 #if defined(FEAT_MENU) || defined(PROTO) 6493 /* 6494 * Add a sub menu to the menu bar. 6495 */ 6496 void 6497 gui_mch_add_menu( 6498 vimmenu_T *menu, 6499 int pos) 6500 { 6501 vimmenu_T *parent = menu->parent; 6502 6503 menu->submenu_id = CreatePopupMenu(); 6504 menu->id = s_menu_id++; 6505 6506 if (menu_is_menubar(menu->name)) 6507 { 6508 WCHAR *wn = NULL; 6509 6510 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage) 6511 { 6512 /* 'encoding' differs from active codepage: convert menu name 6513 * and use wide function */ 6514 wn = enc_to_utf16(menu->name, NULL); 6515 if (wn != NULL) 6516 { 6517 MENUITEMINFOW infow; 6518 6519 infow.cbSize = sizeof(infow); 6520 infow.fMask = MIIM_DATA | MIIM_TYPE | MIIM_ID 6521 | MIIM_SUBMENU; 6522 infow.dwItemData = (long_u)menu; 6523 infow.wID = menu->id; 6524 infow.fType = MFT_STRING; 6525 infow.dwTypeData = wn; 6526 infow.cch = (UINT)wcslen(wn); 6527 infow.hSubMenu = menu->submenu_id; 6528 InsertMenuItemW((parent == NULL) 6529 ? s_menuBar : parent->submenu_id, 6530 (UINT)pos, TRUE, &infow); 6531 vim_free(wn); 6532 } 6533 } 6534 6535 if (wn == NULL) 6536 { 6537 MENUITEMINFO info; 6538 6539 info.cbSize = sizeof(info); 6540 info.fMask = MIIM_DATA | MIIM_TYPE | MIIM_ID | MIIM_SUBMENU; 6541 info.dwItemData = (long_u)menu; 6542 info.wID = menu->id; 6543 info.fType = MFT_STRING; 6544 info.dwTypeData = (LPTSTR)menu->name; 6545 info.cch = (UINT)STRLEN(menu->name); 6546 info.hSubMenu = menu->submenu_id; 6547 InsertMenuItem((parent == NULL) 6548 ? s_menuBar : parent->submenu_id, 6549 (UINT)pos, TRUE, &info); 6550 } 6551 } 6552 6553 /* Fix window size if menu may have wrapped */ 6554 if (parent == NULL) 6555 gui_mswin_get_menu_height(!gui.starting); 6556 #ifdef FEAT_TEAROFF 6557 else if (IsWindow(parent->tearoff_handle)) 6558 rebuild_tearoff(parent); 6559 #endif 6560 } 6561 6562 void 6563 gui_mch_show_popupmenu(vimmenu_T *menu) 6564 { 6565 POINT mp; 6566 6567 (void)GetCursorPos((LPPOINT)&mp); 6568 gui_mch_show_popupmenu_at(menu, (int)mp.x, (int)mp.y); 6569 } 6570 6571 void 6572 gui_make_popup(char_u *path_name, int mouse_pos) 6573 { 6574 vimmenu_T *menu = gui_find_menu(path_name); 6575 6576 if (menu != NULL) 6577 { 6578 POINT p; 6579 6580 /* Find the position of the current cursor */ 6581 GetDCOrgEx(s_hdc, &p); 6582 if (mouse_pos) 6583 { 6584 int mx, my; 6585 6586 gui_mch_getmouse(&mx, &my); 6587 p.x += mx; 6588 p.y += my; 6589 } 6590 else if (curwin != NULL) 6591 { 6592 p.x += TEXT_X(curwin->w_wincol + curwin->w_wcol + 1); 6593 p.y += TEXT_Y(W_WINROW(curwin) + curwin->w_wrow + 1); 6594 } 6595 msg_scroll = FALSE; 6596 gui_mch_show_popupmenu_at(menu, (int)p.x, (int)p.y); 6597 } 6598 } 6599 6600 #if defined(FEAT_TEAROFF) || defined(PROTO) 6601 /* 6602 * Given a menu descriptor, e.g. "File.New", find it in the menu hierarchy and 6603 * create it as a pseudo-"tearoff menu". 6604 */ 6605 void 6606 gui_make_tearoff(char_u *path_name) 6607 { 6608 vimmenu_T *menu = gui_find_menu(path_name); 6609 6610 /* Found the menu, so tear it off. */ 6611 if (menu != NULL) 6612 gui_mch_tearoff(menu->dname, menu, 0xffffL, 0xffffL); 6613 } 6614 #endif 6615 6616 /* 6617 * Add a menu item to a menu 6618 */ 6619 void 6620 gui_mch_add_menu_item( 6621 vimmenu_T *menu, 6622 int idx) 6623 { 6624 vimmenu_T *parent = menu->parent; 6625 6626 menu->id = s_menu_id++; 6627 menu->submenu_id = NULL; 6628 6629 #ifdef FEAT_TEAROFF 6630 if (STRNCMP(menu->name, TEAR_STRING, TEAR_LEN) == 0) 6631 { 6632 InsertMenu(parent->submenu_id, (UINT)idx, MF_BITMAP|MF_BYPOSITION, 6633 (UINT)menu->id, (LPCTSTR) s_htearbitmap); 6634 } 6635 else 6636 #endif 6637 #ifdef FEAT_TOOLBAR 6638 if (menu_is_toolbar(parent->name)) 6639 { 6640 TBBUTTON newtb; 6641 6642 vim_memset(&newtb, 0, sizeof(newtb)); 6643 if (menu_is_separator(menu->name)) 6644 { 6645 newtb.iBitmap = 0; 6646 newtb.fsStyle = TBSTYLE_SEP; 6647 } 6648 else 6649 { 6650 newtb.iBitmap = get_toolbar_bitmap(menu); 6651 newtb.fsStyle = TBSTYLE_BUTTON; 6652 } 6653 newtb.idCommand = menu->id; 6654 newtb.fsState = TBSTATE_ENABLED; 6655 newtb.iString = 0; 6656 SendMessage(s_toolbarhwnd, TB_INSERTBUTTON, (WPARAM)idx, 6657 (LPARAM)&newtb); 6658 menu->submenu_id = (HMENU)-1; 6659 } 6660 else 6661 #endif 6662 { 6663 WCHAR *wn = NULL; 6664 6665 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage) 6666 { 6667 /* 'encoding' differs from active codepage: convert menu item name 6668 * and use wide function */ 6669 wn = enc_to_utf16(menu->name, NULL); 6670 if (wn != NULL) 6671 { 6672 InsertMenuW(parent->submenu_id, (UINT)idx, 6673 (menu_is_separator(menu->name) 6674 ? MF_SEPARATOR : MF_STRING) | MF_BYPOSITION, 6675 (UINT)menu->id, wn); 6676 vim_free(wn); 6677 } 6678 } 6679 if (wn == NULL) 6680 InsertMenu(parent->submenu_id, (UINT)idx, 6681 (menu_is_separator(menu->name) ? MF_SEPARATOR : MF_STRING) 6682 | MF_BYPOSITION, 6683 (UINT)menu->id, (LPCTSTR)menu->name); 6684 #ifdef FEAT_TEAROFF 6685 if (IsWindow(parent->tearoff_handle)) 6686 rebuild_tearoff(parent); 6687 #endif 6688 } 6689 } 6690 6691 /* 6692 * Destroy the machine specific menu widget. 6693 */ 6694 void 6695 gui_mch_destroy_menu(vimmenu_T *menu) 6696 { 6697 #ifdef FEAT_TOOLBAR 6698 /* 6699 * is this a toolbar button? 6700 */ 6701 if (menu->submenu_id == (HMENU)-1) 6702 { 6703 int iButton; 6704 6705 iButton = (int)SendMessage(s_toolbarhwnd, TB_COMMANDTOINDEX, 6706 (WPARAM)menu->id, 0); 6707 SendMessage(s_toolbarhwnd, TB_DELETEBUTTON, (WPARAM)iButton, 0); 6708 } 6709 else 6710 #endif 6711 { 6712 if (menu->parent != NULL 6713 && menu_is_popup(menu->parent->dname) 6714 && menu->parent->submenu_id != NULL) 6715 RemoveMenu(menu->parent->submenu_id, menu->id, MF_BYCOMMAND); 6716 else 6717 RemoveMenu(s_menuBar, menu->id, MF_BYCOMMAND); 6718 if (menu->submenu_id != NULL) 6719 DestroyMenu(menu->submenu_id); 6720 #ifdef FEAT_TEAROFF 6721 if (IsWindow(menu->tearoff_handle)) 6722 DestroyWindow(menu->tearoff_handle); 6723 if (menu->parent != NULL 6724 && menu->parent->children != NULL 6725 && IsWindow(menu->parent->tearoff_handle)) 6726 { 6727 /* This menu must not show up when rebuilding the tearoff window. */ 6728 menu->modes = 0; 6729 rebuild_tearoff(menu->parent); 6730 } 6731 #endif 6732 } 6733 } 6734 6735 #ifdef FEAT_TEAROFF 6736 static void 6737 rebuild_tearoff(vimmenu_T *menu) 6738 { 6739 /*hackish*/ 6740 char_u tbuf[128]; 6741 RECT trect; 6742 RECT rct; 6743 RECT roct; 6744 int x, y; 6745 6746 HWND thwnd = menu->tearoff_handle; 6747 6748 GetWindowText(thwnd, (LPSTR)tbuf, 127); 6749 if (GetWindowRect(thwnd, &trect) 6750 && GetWindowRect(s_hwnd, &rct) 6751 && GetClientRect(s_hwnd, &roct)) 6752 { 6753 x = trect.left - rct.left; 6754 y = (trect.top - rct.bottom + roct.bottom); 6755 } 6756 else 6757 { 6758 x = y = 0xffffL; 6759 } 6760 DestroyWindow(thwnd); 6761 if (menu->children != NULL) 6762 { 6763 gui_mch_tearoff(tbuf, menu, x, y); 6764 if (IsWindow(menu->tearoff_handle)) 6765 (void) SetWindowPos(menu->tearoff_handle, 6766 NULL, 6767 (int)trect.left, 6768 (int)trect.top, 6769 0, 0, 6770 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); 6771 } 6772 } 6773 #endif /* FEAT_TEAROFF */ 6774 6775 /* 6776 * Make a menu either grey or not grey. 6777 */ 6778 void 6779 gui_mch_menu_grey( 6780 vimmenu_T *menu, 6781 int grey) 6782 { 6783 #ifdef FEAT_TOOLBAR 6784 /* 6785 * is this a toolbar button? 6786 */ 6787 if (menu->submenu_id == (HMENU)-1) 6788 { 6789 SendMessage(s_toolbarhwnd, TB_ENABLEBUTTON, 6790 (WPARAM)menu->id, (LPARAM) MAKELONG((grey ? FALSE : TRUE), 0) ); 6791 } 6792 else 6793 #endif 6794 (void)EnableMenuItem(menu->parent ? menu->parent->submenu_id : s_menuBar, 6795 menu->id, MF_BYCOMMAND | (grey ? MF_GRAYED : MF_ENABLED)); 6796 6797 #ifdef FEAT_TEAROFF 6798 if ((menu->parent != NULL) && (IsWindow(menu->parent->tearoff_handle))) 6799 { 6800 WORD menuID; 6801 HWND menuHandle; 6802 6803 /* 6804 * A tearoff button has changed state. 6805 */ 6806 if (menu->children == NULL) 6807 menuID = (WORD)(menu->id); 6808 else 6809 menuID = (WORD)((long_u)(menu->submenu_id) | (DWORD)0x8000); 6810 menuHandle = GetDlgItem(menu->parent->tearoff_handle, menuID); 6811 if (menuHandle) 6812 EnableWindow(menuHandle, !grey); 6813 6814 } 6815 #endif 6816 } 6817 6818 #endif /* FEAT_MENU */ 6819 6820 6821 /* define some macros used to make the dialogue creation more readable */ 6822 6823 #define add_string(s) strcpy((LPSTR)p, s); (LPSTR)p += (strlen((LPSTR)p) + 1) 6824 #define add_word(x) *p++ = (x) 6825 #define add_long(x) dwp = (DWORD *)p; *dwp++ = (x); p = (WORD *)dwp 6826 6827 #if defined(FEAT_GUI_DIALOG) || defined(PROTO) 6828 /* 6829 * stuff for dialogs 6830 */ 6831 6832 /* 6833 * The callback routine used by all the dialogs. Very simple. First, 6834 * acknowledges the INITDIALOG message so that Windows knows to do standard 6835 * dialog stuff (Return = default, Esc = cancel....) Second, if a button is 6836 * pressed, return that button's ID - IDCANCEL (2), which is the button's 6837 * number. 6838 */ 6839 static LRESULT CALLBACK 6840 dialog_callback( 6841 HWND hwnd, 6842 UINT message, 6843 WPARAM wParam, 6844 LPARAM lParam UNUSED) 6845 { 6846 if (message == WM_INITDIALOG) 6847 { 6848 CenterWindow(hwnd, GetWindow(hwnd, GW_OWNER)); 6849 /* Set focus to the dialog. Set the default button, if specified. */ 6850 (void)SetFocus(hwnd); 6851 if (dialog_default_button > IDCANCEL) 6852 (void)SetFocus(GetDlgItem(hwnd, dialog_default_button)); 6853 else 6854 /* We don't have a default, set focus on another element of the 6855 * dialog window, probably the icon */ 6856 (void)SetFocus(GetDlgItem(hwnd, DLG_NONBUTTON_CONTROL)); 6857 return FALSE; 6858 } 6859 6860 if (message == WM_COMMAND) 6861 { 6862 int button = LOWORD(wParam); 6863 6864 /* Don't end the dialog if something was selected that was 6865 * not a button. 6866 */ 6867 if (button >= DLG_NONBUTTON_CONTROL) 6868 return TRUE; 6869 6870 /* If the edit box exists, copy the string. */ 6871 if (s_textfield != NULL) 6872 { 6873 /* If the OS is Windows NT, and 'encoding' differs from active 6874 * codepage: use wide function and convert text. */ 6875 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage) 6876 { 6877 WCHAR *wp = (WCHAR *)alloc(IOSIZE * sizeof(WCHAR)); 6878 char_u *p; 6879 6880 GetDlgItemTextW(hwnd, DLG_NONBUTTON_CONTROL + 2, wp, IOSIZE); 6881 p = utf16_to_enc(wp, NULL); 6882 vim_strncpy(s_textfield, p, IOSIZE); 6883 vim_free(p); 6884 vim_free(wp); 6885 } 6886 else 6887 GetDlgItemText(hwnd, DLG_NONBUTTON_CONTROL + 2, 6888 (LPSTR)s_textfield, IOSIZE); 6889 } 6890 6891 /* 6892 * Need to check for IDOK because if the user just hits Return to 6893 * accept the default value, some reason this is what we get. 6894 */ 6895 if (button == IDOK) 6896 { 6897 if (dialog_default_button > IDCANCEL) 6898 EndDialog(hwnd, dialog_default_button); 6899 } 6900 else 6901 EndDialog(hwnd, button - IDCANCEL); 6902 return TRUE; 6903 } 6904 6905 if ((message == WM_SYSCOMMAND) && (wParam == SC_CLOSE)) 6906 { 6907 EndDialog(hwnd, 0); 6908 return TRUE; 6909 } 6910 return FALSE; 6911 } 6912 6913 /* 6914 * Create a dialog dynamically from the parameter strings. 6915 * type = type of dialog (question, alert, etc.) 6916 * title = dialog title. may be NULL for default title. 6917 * message = text to display. Dialog sizes to accommodate it. 6918 * buttons = '\n' separated list of button captions, default first. 6919 * dfltbutton = number of default button. 6920 * 6921 * This routine returns 1 if the first button is pressed, 6922 * 2 for the second, etc. 6923 * 6924 * 0 indicates Esc was pressed. 6925 * -1 for unexpected error 6926 * 6927 * If stubbing out this fn, return 1. 6928 */ 6929 6930 static const char *dlg_icons[] = /* must match names in resource file */ 6931 { 6932 "IDR_VIM", 6933 "IDR_VIM_ERROR", 6934 "IDR_VIM_ALERT", 6935 "IDR_VIM_INFO", 6936 "IDR_VIM_QUESTION" 6937 }; 6938 6939 int 6940 gui_mch_dialog( 6941 int type, 6942 char_u *title, 6943 char_u *message, 6944 char_u *buttons, 6945 int dfltbutton, 6946 char_u *textfield, 6947 int ex_cmd) 6948 { 6949 WORD *p, *pdlgtemplate, *pnumitems; 6950 DWORD *dwp; 6951 int numButtons; 6952 int *buttonWidths, *buttonPositions; 6953 int buttonYpos; 6954 int nchar, i; 6955 DWORD lStyle; 6956 int dlgwidth = 0; 6957 int dlgheight; 6958 int editboxheight; 6959 int horizWidth = 0; 6960 int msgheight; 6961 char_u *pstart; 6962 char_u *pend; 6963 char_u *last_white; 6964 char_u *tbuffer; 6965 RECT rect; 6966 HWND hwnd; 6967 HDC hdc; 6968 HFONT font, oldFont; 6969 TEXTMETRIC fontInfo; 6970 int fontHeight; 6971 int textWidth, minButtonWidth, messageWidth; 6972 int maxDialogWidth; 6973 int maxDialogHeight; 6974 int scroll_flag = 0; 6975 int vertical; 6976 int dlgPaddingX; 6977 int dlgPaddingY; 6978 #ifdef USE_SYSMENU_FONT 6979 LOGFONT lfSysmenu; 6980 int use_lfSysmenu = FALSE; 6981 #endif 6982 garray_T ga; 6983 int l; 6984 6985 #ifndef NO_CONSOLE 6986 /* Don't output anything in silent mode ("ex -s") */ 6987 if (silent_mode) 6988 return dfltbutton; /* return default option */ 6989 #endif 6990 6991 if (s_hwnd == NULL) 6992 get_dialog_font_metrics(); 6993 6994 if ((type < 0) || (type > VIM_LAST_TYPE)) 6995 type = 0; 6996 6997 /* allocate some memory for dialog template */ 6998 /* TODO should compute this really */ 6999 pdlgtemplate = p = (PWORD)LocalAlloc(LPTR, 7000 DLG_ALLOC_SIZE + STRLEN(message) * 2); 7001 7002 if (p == NULL) 7003 return -1; 7004 7005 /* 7006 * make a copy of 'buttons' to fiddle with it. compiler grizzles because 7007 * vim_strsave() doesn't take a const arg (why not?), so cast away the 7008 * const. 7009 */ 7010 tbuffer = vim_strsave(buttons); 7011 if (tbuffer == NULL) 7012 return -1; 7013 7014 --dfltbutton; /* Change from one-based to zero-based */ 7015 7016 /* Count buttons */ 7017 numButtons = 1; 7018 for (i = 0; tbuffer[i] != '\0'; i++) 7019 { 7020 if (tbuffer[i] == DLG_BUTTON_SEP) 7021 numButtons++; 7022 } 7023 if (dfltbutton >= numButtons) 7024 dfltbutton = -1; 7025 7026 /* Allocate array to hold the width of each button */ 7027 buttonWidths = (int *)lalloc(numButtons * sizeof(int), TRUE); 7028 if (buttonWidths == NULL) 7029 return -1; 7030 7031 /* Allocate array to hold the X position of each button */ 7032 buttonPositions = (int *)lalloc(numButtons * sizeof(int), TRUE); 7033 if (buttonPositions == NULL) 7034 return -1; 7035 7036 /* 7037 * Calculate how big the dialog must be. 7038 */ 7039 hwnd = GetDesktopWindow(); 7040 hdc = GetWindowDC(hwnd); 7041 #ifdef USE_SYSMENU_FONT 7042 if (gui_w32_get_menu_font(&lfSysmenu) == OK) 7043 { 7044 font = CreateFontIndirect(&lfSysmenu); 7045 use_lfSysmenu = TRUE; 7046 } 7047 else 7048 #endif 7049 font = CreateFont(-DLG_FONT_POINT_SIZE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7050 VARIABLE_PITCH , DLG_FONT_NAME); 7051 if (s_usenewlook) 7052 { 7053 oldFont = SelectFont(hdc, font); 7054 dlgPaddingX = DLG_PADDING_X; 7055 dlgPaddingY = DLG_PADDING_Y; 7056 } 7057 else 7058 { 7059 oldFont = SelectFont(hdc, GetStockObject(SYSTEM_FONT)); 7060 dlgPaddingX = DLG_OLD_STYLE_PADDING_X; 7061 dlgPaddingY = DLG_OLD_STYLE_PADDING_Y; 7062 } 7063 GetTextMetrics(hdc, &fontInfo); 7064 fontHeight = fontInfo.tmHeight; 7065 7066 /* Minimum width for horizontal button */ 7067 minButtonWidth = GetTextWidth(hdc, (char_u *)"Cancel", 6); 7068 7069 /* Maximum width of a dialog, if possible */ 7070 if (s_hwnd == NULL) 7071 { 7072 RECT workarea_rect; 7073 7074 /* We don't have a window, use the desktop area. */ 7075 get_work_area(&workarea_rect); 7076 maxDialogWidth = workarea_rect.right - workarea_rect.left - 100; 7077 if (maxDialogWidth > 600) 7078 maxDialogWidth = 600; 7079 /* Leave some room for the taskbar. */ 7080 maxDialogHeight = workarea_rect.bottom - workarea_rect.top - 150; 7081 } 7082 else 7083 { 7084 /* Use our own window for the size, unless it's very small. */ 7085 GetWindowRect(s_hwnd, &rect); 7086 maxDialogWidth = rect.right - rect.left 7087 - (GetSystemMetrics(SM_CXFRAME) + 7088 GetSystemMetrics(SM_CXPADDEDBORDER)) * 2; 7089 if (maxDialogWidth < DLG_MIN_MAX_WIDTH) 7090 maxDialogWidth = DLG_MIN_MAX_WIDTH; 7091 7092 maxDialogHeight = rect.bottom - rect.top 7093 - (GetSystemMetrics(SM_CYFRAME) + 7094 GetSystemMetrics(SM_CXPADDEDBORDER)) * 4 7095 - GetSystemMetrics(SM_CYCAPTION); 7096 if (maxDialogHeight < DLG_MIN_MAX_HEIGHT) 7097 maxDialogHeight = DLG_MIN_MAX_HEIGHT; 7098 } 7099 7100 /* Set dlgwidth to width of message. 7101 * Copy the message into "ga", changing NL to CR-NL and inserting line 7102 * breaks where needed. */ 7103 pstart = message; 7104 messageWidth = 0; 7105 msgheight = 0; 7106 ga_init2(&ga, sizeof(char), 500); 7107 do 7108 { 7109 msgheight += fontHeight; /* at least one line */ 7110 7111 /* Need to figure out where to break the string. The system does it 7112 * at a word boundary, which would mean we can't compute the number of 7113 * wrapped lines. */ 7114 textWidth = 0; 7115 last_white = NULL; 7116 for (pend = pstart; *pend != NUL && *pend != '\n'; ) 7117 { 7118 l = (*mb_ptr2len)(pend); 7119 if (l == 1 && VIM_ISWHITE(*pend) 7120 && textWidth > maxDialogWidth * 3 / 4) 7121 last_white = pend; 7122 textWidth += GetTextWidthEnc(hdc, pend, l); 7123 if (textWidth >= maxDialogWidth) 7124 { 7125 /* Line will wrap. */ 7126 messageWidth = maxDialogWidth; 7127 msgheight += fontHeight; 7128 textWidth = 0; 7129 7130 if (last_white != NULL) 7131 { 7132 /* break the line just after a space */ 7133 ga.ga_len -= (int)(pend - (last_white + 1)); 7134 pend = last_white + 1; 7135 last_white = NULL; 7136 } 7137 ga_append(&ga, '\r'); 7138 ga_append(&ga, '\n'); 7139 continue; 7140 } 7141 7142 while (--l >= 0) 7143 ga_append(&ga, *pend++); 7144 } 7145 if (textWidth > messageWidth) 7146 messageWidth = textWidth; 7147 7148 ga_append(&ga, '\r'); 7149 ga_append(&ga, '\n'); 7150 pstart = pend + 1; 7151 } while (*pend != NUL); 7152 7153 if (ga.ga_data != NULL) 7154 message = ga.ga_data; 7155 7156 messageWidth += 10; /* roundoff space */ 7157 7158 /* Add width of icon to dlgwidth, and some space */ 7159 dlgwidth = messageWidth + DLG_ICON_WIDTH + 3 * dlgPaddingX 7160 + GetSystemMetrics(SM_CXVSCROLL); 7161 7162 if (msgheight < DLG_ICON_HEIGHT) 7163 msgheight = DLG_ICON_HEIGHT; 7164 7165 /* 7166 * Check button names. A long one will make the dialog wider. 7167 * When called early (-register error message) p_go isn't initialized. 7168 */ 7169 vertical = (p_go != NULL && vim_strchr(p_go, GO_VERTICAL) != NULL); 7170 if (!vertical) 7171 { 7172 // Place buttons horizontally if they fit. 7173 horizWidth = dlgPaddingX; 7174 pstart = tbuffer; 7175 i = 0; 7176 do 7177 { 7178 pend = vim_strchr(pstart, DLG_BUTTON_SEP); 7179 if (pend == NULL) 7180 pend = pstart + STRLEN(pstart); // Last button name. 7181 textWidth = GetTextWidthEnc(hdc, pstart, (int)(pend - pstart)); 7182 if (textWidth < minButtonWidth) 7183 textWidth = minButtonWidth; 7184 textWidth += dlgPaddingX; /* Padding within button */ 7185 buttonWidths[i] = textWidth; 7186 buttonPositions[i++] = horizWidth; 7187 horizWidth += textWidth + dlgPaddingX; /* Pad between buttons */ 7188 pstart = pend + 1; 7189 } while (*pend != NUL); 7190 7191 if (horizWidth > maxDialogWidth) 7192 vertical = TRUE; // Too wide to fit on the screen. 7193 else if (horizWidth > dlgwidth) 7194 dlgwidth = horizWidth; 7195 } 7196 7197 if (vertical) 7198 { 7199 // Stack buttons vertically. 7200 pstart = tbuffer; 7201 do 7202 { 7203 pend = vim_strchr(pstart, DLG_BUTTON_SEP); 7204 if (pend == NULL) 7205 pend = pstart + STRLEN(pstart); // Last button name. 7206 textWidth = GetTextWidthEnc(hdc, pstart, (int)(pend - pstart)); 7207 textWidth += dlgPaddingX; /* Padding within button */ 7208 textWidth += DLG_VERT_PADDING_X * 2; /* Padding around button */ 7209 if (textWidth > dlgwidth) 7210 dlgwidth = textWidth; 7211 pstart = pend + 1; 7212 } while (*pend != NUL); 7213 } 7214 7215 if (dlgwidth < DLG_MIN_WIDTH) 7216 dlgwidth = DLG_MIN_WIDTH; /* Don't allow a really thin dialog!*/ 7217 7218 /* start to fill in the dlgtemplate information. addressing by WORDs */ 7219 if (s_usenewlook) 7220 lStyle = DS_MODALFRAME | WS_CAPTION |DS_3DLOOK| WS_VISIBLE |DS_SETFONT; 7221 else 7222 lStyle = DS_MODALFRAME | WS_CAPTION |DS_3DLOOK| WS_VISIBLE; 7223 7224 add_long(lStyle); 7225 add_long(0); // (lExtendedStyle) 7226 pnumitems = p; /*save where the number of items must be stored*/ 7227 add_word(0); // NumberOfItems(will change later) 7228 add_word(10); // x 7229 add_word(10); // y 7230 add_word(PixelToDialogX(dlgwidth)); // cx 7231 7232 // Dialog height. 7233 if (vertical) 7234 dlgheight = msgheight + 2 * dlgPaddingY 7235 + DLG_VERT_PADDING_Y + 2 * fontHeight * numButtons; 7236 else 7237 dlgheight = msgheight + 3 * dlgPaddingY + 2 * fontHeight; 7238 7239 // Dialog needs to be taller if contains an edit box. 7240 editboxheight = fontHeight + dlgPaddingY + 4 * DLG_VERT_PADDING_Y; 7241 if (textfield != NULL) 7242 dlgheight += editboxheight; 7243 7244 /* Restrict the size to a maximum. Causes a scrollbar to show up. */ 7245 if (dlgheight > maxDialogHeight) 7246 { 7247 msgheight = msgheight - (dlgheight - maxDialogHeight); 7248 dlgheight = maxDialogHeight; 7249 scroll_flag = WS_VSCROLL; 7250 /* Make sure scrollbar doesn't appear in the middle of the dialog */ 7251 messageWidth = dlgwidth - DLG_ICON_WIDTH - 3 * dlgPaddingX; 7252 } 7253 7254 add_word(PixelToDialogY(dlgheight)); 7255 7256 add_word(0); // Menu 7257 add_word(0); // Class 7258 7259 /* copy the title of the dialog */ 7260 nchar = nCopyAnsiToWideChar(p, (title ? (LPSTR)title 7261 : (LPSTR)("Vim "VIM_VERSION_MEDIUM)), TRUE); 7262 p += nchar; 7263 7264 if (s_usenewlook) 7265 { 7266 /* do the font, since DS_3DLOOK doesn't work properly */ 7267 #ifdef USE_SYSMENU_FONT 7268 if (use_lfSysmenu) 7269 { 7270 /* point size */ 7271 *p++ = -MulDiv(lfSysmenu.lfHeight, 72, 7272 GetDeviceCaps(hdc, LOGPIXELSY)); 7273 nchar = nCopyAnsiToWideChar(p, lfSysmenu.lfFaceName, FALSE); 7274 } 7275 else 7276 #endif 7277 { 7278 *p++ = DLG_FONT_POINT_SIZE; // point size 7279 nchar = nCopyAnsiToWideChar(p, DLG_FONT_NAME, FALSE); 7280 } 7281 p += nchar; 7282 } 7283 7284 buttonYpos = msgheight + 2 * dlgPaddingY; 7285 7286 if (textfield != NULL) 7287 buttonYpos += editboxheight; 7288 7289 pstart = tbuffer; 7290 if (!vertical) 7291 horizWidth = (dlgwidth - horizWidth) / 2; /* Now it's X offset */ 7292 for (i = 0; i < numButtons; i++) 7293 { 7294 /* get end of this button. */ 7295 for ( pend = pstart; 7296 *pend && (*pend != DLG_BUTTON_SEP); 7297 pend++) 7298 ; 7299 7300 if (*pend) 7301 *pend = '\0'; 7302 7303 /* 7304 * old NOTE: 7305 * setting the BS_DEFPUSHBUTTON style doesn't work because Windows sets 7306 * the focus to the first tab-able button and in so doing makes that 7307 * the default!! Grrr. Workaround: Make the default button the only 7308 * one with WS_TABSTOP style. Means user can't tab between buttons, but 7309 * he/she can use arrow keys. 7310 * 7311 * new NOTE: BS_DEFPUSHBUTTON is required to be able to select the 7312 * right button when hitting <Enter>. E.g., for the ":confirm quit" 7313 * dialog. Also needed for when the textfield is the default control. 7314 * It appears to work now (perhaps not on Win95?). 7315 */ 7316 if (vertical) 7317 { 7318 p = add_dialog_element(p, 7319 (i == dfltbutton 7320 ? BS_DEFPUSHBUTTON : BS_PUSHBUTTON) | WS_TABSTOP, 7321 PixelToDialogX(DLG_VERT_PADDING_X), 7322 PixelToDialogY(buttonYpos /* TBK */ 7323 + 2 * fontHeight * i), 7324 PixelToDialogX(dlgwidth - 2 * DLG_VERT_PADDING_X), 7325 (WORD)(PixelToDialogY(2 * fontHeight) - 1), 7326 (WORD)(IDCANCEL + 1 + i), (WORD)0x0080, (char *)pstart); 7327 } 7328 else 7329 { 7330 p = add_dialog_element(p, 7331 (i == dfltbutton 7332 ? BS_DEFPUSHBUTTON : BS_PUSHBUTTON) | WS_TABSTOP, 7333 PixelToDialogX(horizWidth + buttonPositions[i]), 7334 PixelToDialogY(buttonYpos), /* TBK */ 7335 PixelToDialogX(buttonWidths[i]), 7336 (WORD)(PixelToDialogY(2 * fontHeight) - 1), 7337 (WORD)(IDCANCEL + 1 + i), (WORD)0x0080, (char *)pstart); 7338 } 7339 pstart = pend + 1; /*next button*/ 7340 } 7341 *pnumitems += numButtons; 7342 7343 /* Vim icon */ 7344 p = add_dialog_element(p, SS_ICON, 7345 PixelToDialogX(dlgPaddingX), 7346 PixelToDialogY(dlgPaddingY), 7347 PixelToDialogX(DLG_ICON_WIDTH), 7348 PixelToDialogY(DLG_ICON_HEIGHT), 7349 DLG_NONBUTTON_CONTROL + 0, (WORD)0x0082, 7350 dlg_icons[type]); 7351 7352 /* Dialog message */ 7353 p = add_dialog_element(p, ES_LEFT|scroll_flag|ES_MULTILINE|ES_READONLY, 7354 PixelToDialogX(2 * dlgPaddingX + DLG_ICON_WIDTH), 7355 PixelToDialogY(dlgPaddingY), 7356 (WORD)(PixelToDialogX(messageWidth) + 1), 7357 PixelToDialogY(msgheight), 7358 DLG_NONBUTTON_CONTROL + 1, (WORD)0x0081, (char *)message); 7359 7360 /* Edit box */ 7361 if (textfield != NULL) 7362 { 7363 p = add_dialog_element(p, ES_LEFT|ES_AUTOHSCROLL|WS_TABSTOP|WS_BORDER, 7364 PixelToDialogX(2 * dlgPaddingX), 7365 PixelToDialogY(2 * dlgPaddingY + msgheight), 7366 PixelToDialogX(dlgwidth - 4 * dlgPaddingX), 7367 PixelToDialogY(fontHeight + dlgPaddingY), 7368 DLG_NONBUTTON_CONTROL + 2, (WORD)0x0081, (char *)textfield); 7369 *pnumitems += 1; 7370 } 7371 7372 *pnumitems += 2; 7373 7374 SelectFont(hdc, oldFont); 7375 DeleteObject(font); 7376 ReleaseDC(hwnd, hdc); 7377 7378 /* Let the dialog_callback() function know which button to make default 7379 * If we have an edit box, make that the default. We also need to tell 7380 * dialog_callback() if this dialog contains an edit box or not. We do 7381 * this by setting s_textfield if it does. 7382 */ 7383 if (textfield != NULL) 7384 { 7385 dialog_default_button = DLG_NONBUTTON_CONTROL + 2; 7386 s_textfield = textfield; 7387 } 7388 else 7389 { 7390 dialog_default_button = IDCANCEL + 1 + dfltbutton; 7391 s_textfield = NULL; 7392 } 7393 7394 /* show the dialog box modally and get a return value */ 7395 nchar = (int)DialogBoxIndirect( 7396 s_hinst, 7397 (LPDLGTEMPLATE)pdlgtemplate, 7398 s_hwnd, 7399 (DLGPROC)dialog_callback); 7400 7401 LocalFree(LocalHandle(pdlgtemplate)); 7402 vim_free(tbuffer); 7403 vim_free(buttonWidths); 7404 vim_free(buttonPositions); 7405 vim_free(ga.ga_data); 7406 7407 /* Focus back to our window (for when MDI is used). */ 7408 (void)SetFocus(s_hwnd); 7409 7410 return nchar; 7411 } 7412 7413 #endif /* FEAT_GUI_DIALOG */ 7414 7415 /* 7416 * Put a simple element (basic class) onto a dialog template in memory. 7417 * return a pointer to where the next item should be added. 7418 * 7419 * parameters: 7420 * lStyle = additional style flags 7421 * (be careful, NT3.51 & Win32s will ignore the new ones) 7422 * x,y = x & y positions IN DIALOG UNITS 7423 * w,h = width and height IN DIALOG UNITS 7424 * Id = ID used in messages 7425 * clss = class ID, e.g 0x0080 for a button, 0x0082 for a static 7426 * caption = usually text or resource name 7427 * 7428 * TODO: use the length information noted here to enable the dialog creation 7429 * routines to work out more exactly how much memory they need to alloc. 7430 */ 7431 static PWORD 7432 add_dialog_element( 7433 PWORD p, 7434 DWORD lStyle, 7435 WORD x, 7436 WORD y, 7437 WORD w, 7438 WORD h, 7439 WORD Id, 7440 WORD clss, 7441 const char *caption) 7442 { 7443 int nchar; 7444 7445 p = lpwAlign(p); /* Align to dword boundary*/ 7446 lStyle = lStyle | WS_VISIBLE | WS_CHILD; 7447 *p++ = LOWORD(lStyle); 7448 *p++ = HIWORD(lStyle); 7449 *p++ = 0; // LOWORD (lExtendedStyle) 7450 *p++ = 0; // HIWORD (lExtendedStyle) 7451 *p++ = x; 7452 *p++ = y; 7453 *p++ = w; 7454 *p++ = h; 7455 *p++ = Id; //9 or 10 words in all 7456 7457 *p++ = (WORD)0xffff; 7458 *p++ = clss; //2 more here 7459 7460 nchar = nCopyAnsiToWideChar(p, (LPSTR)caption, TRUE); //strlen(caption)+1 7461 p += nchar; 7462 7463 *p++ = 0; // advance pointer over nExtraStuff WORD - 2 more 7464 7465 return p; //total = 15+ (strlen(caption)) words 7466 // = 30 + 2(strlen(caption) bytes reqd 7467 } 7468 7469 7470 /* 7471 * Helper routine. Take an input pointer, return closest pointer that is 7472 * aligned on a DWORD (4 byte) boundary. Taken from the Win32SDK samples. 7473 */ 7474 static LPWORD 7475 lpwAlign( 7476 LPWORD lpIn) 7477 { 7478 long_u ul; 7479 7480 ul = (long_u)lpIn; 7481 ul += 3; 7482 ul >>= 2; 7483 ul <<= 2; 7484 return (LPWORD)ul; 7485 } 7486 7487 /* 7488 * Helper routine. Takes second parameter as Ansi string, copies it to first 7489 * parameter as wide character (16-bits / char) string, and returns integer 7490 * number of wide characters (words) in string (including the trailing wide 7491 * char NULL). Partly taken from the Win32SDK samples. 7492 * If "use_enc" is TRUE, 'encoding' is used for "lpAnsiIn". If FALSE, current 7493 * ACP is used for "lpAnsiIn". */ 7494 static int 7495 nCopyAnsiToWideChar( 7496 LPWORD lpWCStr, 7497 LPSTR lpAnsiIn, 7498 BOOL use_enc) 7499 { 7500 int nChar = 0; 7501 int len = lstrlen(lpAnsiIn) + 1; /* include NUL character */ 7502 int i; 7503 WCHAR *wn; 7504 7505 if (use_enc && enc_codepage >= 0 && (int)GetACP() != enc_codepage) 7506 { 7507 /* Not a codepage, use our own conversion function. */ 7508 wn = enc_to_utf16((char_u *)lpAnsiIn, NULL); 7509 if (wn != NULL) 7510 { 7511 wcscpy(lpWCStr, wn); 7512 nChar = (int)wcslen(wn) + 1; 7513 vim_free(wn); 7514 } 7515 } 7516 if (nChar == 0) 7517 /* Use Win32 conversion function. */ 7518 nChar = MultiByteToWideChar( 7519 enc_codepage > 0 ? enc_codepage : CP_ACP, 7520 MB_PRECOMPOSED, 7521 lpAnsiIn, len, 7522 lpWCStr, len); 7523 for (i = 0; i < nChar; ++i) 7524 if (lpWCStr[i] == (WORD)'\t') /* replace tabs with spaces */ 7525 lpWCStr[i] = (WORD)' '; 7526 7527 return nChar; 7528 } 7529 7530 7531 #ifdef FEAT_TEAROFF 7532 /* 7533 * Lookup menu handle from "menu_id". 7534 */ 7535 static HMENU 7536 tearoff_lookup_menuhandle( 7537 vimmenu_T *menu, 7538 WORD menu_id) 7539 { 7540 for ( ; menu != NULL; menu = menu->next) 7541 { 7542 if (menu->modes == 0) /* this menu has just been deleted */ 7543 continue; 7544 if (menu_is_separator(menu->dname)) 7545 continue; 7546 if ((WORD)((long_u)(menu->submenu_id) | (DWORD)0x8000) == menu_id) 7547 return menu->submenu_id; 7548 } 7549 return NULL; 7550 } 7551 7552 /* 7553 * The callback function for all the modeless dialogs that make up the 7554 * "tearoff menus" Very simple - forward button presses (to fool Vim into 7555 * thinking its menus have been clicked), and go away when closed. 7556 */ 7557 static LRESULT CALLBACK 7558 tearoff_callback( 7559 HWND hwnd, 7560 UINT message, 7561 WPARAM wParam, 7562 LPARAM lParam) 7563 { 7564 if (message == WM_INITDIALOG) 7565 { 7566 SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR)lParam); 7567 return (TRUE); 7568 } 7569 7570 /* May show the mouse pointer again. */ 7571 HandleMouseHide(message, lParam); 7572 7573 if (message == WM_COMMAND) 7574 { 7575 if ((WORD)(LOWORD(wParam)) & 0x8000) 7576 { 7577 POINT mp; 7578 RECT rect; 7579 7580 if (GetCursorPos(&mp) && GetWindowRect(hwnd, &rect)) 7581 { 7582 vimmenu_T *menu; 7583 7584 menu = (vimmenu_T*)GetWindowLongPtr(hwnd, DWLP_USER); 7585 (void)TrackPopupMenu( 7586 tearoff_lookup_menuhandle(menu, LOWORD(wParam)), 7587 TPM_LEFTALIGN | TPM_LEFTBUTTON, 7588 (int)rect.right - 8, 7589 (int)mp.y, 7590 (int)0, /*reserved param*/ 7591 s_hwnd, 7592 NULL); 7593 /* 7594 * NOTE: The pop-up menu can eat the mouse up event. 7595 * We deal with this in normal.c. 7596 */ 7597 } 7598 } 7599 else 7600 /* Pass on messages to the main Vim window */ 7601 PostMessage(s_hwnd, WM_COMMAND, LOWORD(wParam), 0); 7602 /* 7603 * Give main window the focus back: this is so after 7604 * choosing a tearoff button you can start typing again 7605 * straight away. 7606 */ 7607 (void)SetFocus(s_hwnd); 7608 return TRUE; 7609 } 7610 if ((message == WM_SYSCOMMAND) && (wParam == SC_CLOSE)) 7611 { 7612 DestroyWindow(hwnd); 7613 return TRUE; 7614 } 7615 7616 /* When moved around, give main window the focus back. */ 7617 if (message == WM_EXITSIZEMOVE) 7618 (void)SetActiveWindow(s_hwnd); 7619 7620 return FALSE; 7621 } 7622 #endif 7623 7624 7625 /* 7626 * Decide whether to use the "new look" (small, non-bold font) or the "old 7627 * look" (big, clanky font) for dialogs, and work out a few values for use 7628 * later accordingly. 7629 */ 7630 static void 7631 get_dialog_font_metrics(void) 7632 { 7633 HDC hdc; 7634 HFONT hfontTools = 0; 7635 DWORD dlgFontSize; 7636 SIZE size; 7637 #ifdef USE_SYSMENU_FONT 7638 LOGFONT lfSysmenu; 7639 #endif 7640 7641 s_usenewlook = FALSE; 7642 7643 #ifdef USE_SYSMENU_FONT 7644 if (gui_w32_get_menu_font(&lfSysmenu) == OK) 7645 hfontTools = CreateFontIndirect(&lfSysmenu); 7646 else 7647 #endif 7648 hfontTools = CreateFont(-DLG_FONT_POINT_SIZE, 0, 0, 0, 0, 0, 0, 0, 7649 0, 0, 0, 0, VARIABLE_PITCH , DLG_FONT_NAME); 7650 7651 if (hfontTools) 7652 { 7653 hdc = GetDC(s_hwnd); 7654 SelectObject(hdc, hfontTools); 7655 /* 7656 * GetTextMetrics() doesn't return the right value in 7657 * tmAveCharWidth, so we have to figure out the dialog base units 7658 * ourselves. 7659 */ 7660 GetTextExtentPoint(hdc, 7661 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 7662 52, &size); 7663 ReleaseDC(s_hwnd, hdc); 7664 7665 s_dlgfntwidth = (WORD)((size.cx / 26 + 1) / 2); 7666 s_dlgfntheight = (WORD)size.cy; 7667 s_usenewlook = TRUE; 7668 } 7669 7670 if (!s_usenewlook) 7671 { 7672 dlgFontSize = GetDialogBaseUnits(); /* fall back to big old system*/ 7673 s_dlgfntwidth = LOWORD(dlgFontSize); 7674 s_dlgfntheight = HIWORD(dlgFontSize); 7675 } 7676 } 7677 7678 #if defined(FEAT_MENU) && defined(FEAT_TEAROFF) 7679 /* 7680 * Create a pseudo-"tearoff menu" based on the child 7681 * items of a given menu pointer. 7682 */ 7683 static void 7684 gui_mch_tearoff( 7685 char_u *title, 7686 vimmenu_T *menu, 7687 int initX, 7688 int initY) 7689 { 7690 WORD *p, *pdlgtemplate, *pnumitems, *ptrueheight; 7691 int template_len; 7692 int nchar, textWidth, submenuWidth; 7693 DWORD lStyle; 7694 DWORD lExtendedStyle; 7695 WORD dlgwidth; 7696 WORD menuID; 7697 vimmenu_T *pmenu; 7698 vimmenu_T *top_menu; 7699 vimmenu_T *the_menu = menu; 7700 HWND hwnd; 7701 HDC hdc; 7702 HFONT font, oldFont; 7703 int col, spaceWidth, len; 7704 int columnWidths[2]; 7705 char_u *label, *text; 7706 int acLen = 0; 7707 int nameLen; 7708 int padding0, padding1, padding2 = 0; 7709 int sepPadding=0; 7710 int x; 7711 int y; 7712 #ifdef USE_SYSMENU_FONT 7713 LOGFONT lfSysmenu; 7714 int use_lfSysmenu = FALSE; 7715 #endif 7716 7717 /* 7718 * If this menu is already torn off, move it to the mouse position. 7719 */ 7720 if (IsWindow(menu->tearoff_handle)) 7721 { 7722 POINT mp; 7723 if (GetCursorPos((LPPOINT)&mp)) 7724 { 7725 SetWindowPos(menu->tearoff_handle, NULL, mp.x, mp.y, 0, 0, 7726 SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER); 7727 } 7728 return; 7729 } 7730 7731 /* 7732 * Create a new tearoff. 7733 */ 7734 if (*title == MNU_HIDDEN_CHAR) 7735 title++; 7736 7737 /* Allocate memory to store the dialog template. It's made bigger when 7738 * needed. */ 7739 template_len = DLG_ALLOC_SIZE; 7740 pdlgtemplate = p = (WORD *)LocalAlloc(LPTR, template_len); 7741 if (p == NULL) 7742 return; 7743 7744 hwnd = GetDesktopWindow(); 7745 hdc = GetWindowDC(hwnd); 7746 #ifdef USE_SYSMENU_FONT 7747 if (gui_w32_get_menu_font(&lfSysmenu) == OK) 7748 { 7749 font = CreateFontIndirect(&lfSysmenu); 7750 use_lfSysmenu = TRUE; 7751 } 7752 else 7753 #endif 7754 font = CreateFont(-DLG_FONT_POINT_SIZE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7755 VARIABLE_PITCH , DLG_FONT_NAME); 7756 if (s_usenewlook) 7757 oldFont = SelectFont(hdc, font); 7758 else 7759 oldFont = SelectFont(hdc, GetStockObject(SYSTEM_FONT)); 7760 7761 /* Calculate width of a single space. Used for padding columns to the 7762 * right width. */ 7763 spaceWidth = GetTextWidth(hdc, (char_u *)" ", 1); 7764 7765 /* Figure out max width of the text column, the accelerator column and the 7766 * optional submenu column. */ 7767 submenuWidth = 0; 7768 for (col = 0; col < 2; col++) 7769 { 7770 columnWidths[col] = 0; 7771 for (pmenu = menu->children; pmenu != NULL; pmenu = pmenu->next) 7772 { 7773 /* Use "dname" here to compute the width of the visible text. */ 7774 text = (col == 0) ? pmenu->dname : pmenu->actext; 7775 if (text != NULL && *text != NUL) 7776 { 7777 textWidth = GetTextWidthEnc(hdc, text, (int)STRLEN(text)); 7778 if (textWidth > columnWidths[col]) 7779 columnWidths[col] = textWidth; 7780 } 7781 if (pmenu->children != NULL) 7782 submenuWidth = TEAROFF_COLUMN_PADDING * spaceWidth; 7783 } 7784 } 7785 if (columnWidths[1] == 0) 7786 { 7787 /* no accelerators */ 7788 if (submenuWidth != 0) 7789 columnWidths[0] += submenuWidth; 7790 else 7791 columnWidths[0] += spaceWidth; 7792 } 7793 else 7794 { 7795 /* there is an accelerator column */ 7796 columnWidths[0] += TEAROFF_COLUMN_PADDING * spaceWidth; 7797 columnWidths[1] += submenuWidth; 7798 } 7799 7800 /* 7801 * Now find the total width of our 'menu'. 7802 */ 7803 textWidth = columnWidths[0] + columnWidths[1]; 7804 if (submenuWidth != 0) 7805 { 7806 submenuWidth = GetTextWidth(hdc, (char_u *)TEAROFF_SUBMENU_LABEL, 7807 (int)STRLEN(TEAROFF_SUBMENU_LABEL)); 7808 textWidth += submenuWidth; 7809 } 7810 dlgwidth = GetTextWidthEnc(hdc, title, (int)STRLEN(title)); 7811 if (textWidth > dlgwidth) 7812 dlgwidth = textWidth; 7813 dlgwidth += 2 * TEAROFF_PADDING_X + TEAROFF_BUTTON_PAD_X; 7814 7815 /* start to fill in the dlgtemplate information. addressing by WORDs */ 7816 if (s_usenewlook) 7817 lStyle = DS_MODALFRAME | WS_CAPTION| WS_SYSMENU |DS_SETFONT| WS_VISIBLE; 7818 else 7819 lStyle = DS_MODALFRAME | WS_CAPTION| WS_SYSMENU | WS_VISIBLE; 7820 7821 lExtendedStyle = WS_EX_TOOLWINDOW|WS_EX_STATICEDGE; 7822 *p++ = LOWORD(lStyle); 7823 *p++ = HIWORD(lStyle); 7824 *p++ = LOWORD(lExtendedStyle); 7825 *p++ = HIWORD(lExtendedStyle); 7826 pnumitems = p; /* save where the number of items must be stored */ 7827 *p++ = 0; // NumberOfItems(will change later) 7828 gui_mch_getmouse(&x, &y); 7829 if (initX == 0xffffL) 7830 *p++ = PixelToDialogX(x); // x 7831 else 7832 *p++ = PixelToDialogX(initX); // x 7833 if (initY == 0xffffL) 7834 *p++ = PixelToDialogY(y); // y 7835 else 7836 *p++ = PixelToDialogY(initY); // y 7837 *p++ = PixelToDialogX(dlgwidth); // cx 7838 ptrueheight = p; 7839 *p++ = 0; // dialog height: changed later anyway 7840 *p++ = 0; // Menu 7841 *p++ = 0; // Class 7842 7843 /* copy the title of the dialog */ 7844 nchar = nCopyAnsiToWideChar(p, ((*title) 7845 ? (LPSTR)title 7846 : (LPSTR)("Vim "VIM_VERSION_MEDIUM)), TRUE); 7847 p += nchar; 7848 7849 if (s_usenewlook) 7850 { 7851 /* do the font, since DS_3DLOOK doesn't work properly */ 7852 #ifdef USE_SYSMENU_FONT 7853 if (use_lfSysmenu) 7854 { 7855 /* point size */ 7856 *p++ = -MulDiv(lfSysmenu.lfHeight, 72, 7857 GetDeviceCaps(hdc, LOGPIXELSY)); 7858 nchar = nCopyAnsiToWideChar(p, lfSysmenu.lfFaceName, FALSE); 7859 } 7860 else 7861 #endif 7862 { 7863 *p++ = DLG_FONT_POINT_SIZE; // point size 7864 nchar = nCopyAnsiToWideChar(p, DLG_FONT_NAME, FALSE); 7865 } 7866 p += nchar; 7867 } 7868 7869 /* 7870 * Loop over all the items in the menu. 7871 * But skip over the tearbar. 7872 */ 7873 if (STRCMP(menu->children->name, TEAR_STRING) == 0) 7874 menu = menu->children->next; 7875 else 7876 menu = menu->children; 7877 top_menu = menu; 7878 for ( ; menu != NULL; menu = menu->next) 7879 { 7880 if (menu->modes == 0) /* this menu has just been deleted */ 7881 continue; 7882 if (menu_is_separator(menu->dname)) 7883 { 7884 sepPadding += 3; 7885 continue; 7886 } 7887 7888 /* Check if there still is plenty of room in the template. Make it 7889 * larger when needed. */ 7890 if (((char *)p - (char *)pdlgtemplate) + 1000 > template_len) 7891 { 7892 WORD *newp; 7893 7894 newp = (WORD *)LocalAlloc(LPTR, template_len + 4096); 7895 if (newp != NULL) 7896 { 7897 template_len += 4096; 7898 mch_memmove(newp, pdlgtemplate, 7899 (char *)p - (char *)pdlgtemplate); 7900 p = newp + (p - pdlgtemplate); 7901 pnumitems = newp + (pnumitems - pdlgtemplate); 7902 ptrueheight = newp + (ptrueheight - pdlgtemplate); 7903 LocalFree(LocalHandle(pdlgtemplate)); 7904 pdlgtemplate = newp; 7905 } 7906 } 7907 7908 /* Figure out minimal length of this menu label. Use "name" for the 7909 * actual text, "dname" for estimating the displayed size. "name" 7910 * has "&a" for mnemonic and includes the accelerator. */ 7911 len = nameLen = (int)STRLEN(menu->name); 7912 padding0 = (columnWidths[0] - GetTextWidthEnc(hdc, menu->dname, 7913 (int)STRLEN(menu->dname))) / spaceWidth; 7914 len += padding0; 7915 7916 if (menu->actext != NULL) 7917 { 7918 acLen = (int)STRLEN(menu->actext); 7919 len += acLen; 7920 textWidth = GetTextWidthEnc(hdc, menu->actext, acLen); 7921 } 7922 else 7923 textWidth = 0; 7924 padding1 = (columnWidths[1] - textWidth) / spaceWidth; 7925 len += padding1; 7926 7927 if (menu->children == NULL) 7928 { 7929 padding2 = submenuWidth / spaceWidth; 7930 len += padding2; 7931 menuID = (WORD)(menu->id); 7932 } 7933 else 7934 { 7935 len += (int)STRLEN(TEAROFF_SUBMENU_LABEL); 7936 menuID = (WORD)((long_u)(menu->submenu_id) | (DWORD)0x8000); 7937 } 7938 7939 /* Allocate menu label and fill it in */ 7940 text = label = alloc((unsigned)len + 1); 7941 if (label == NULL) 7942 break; 7943 7944 vim_strncpy(text, menu->name, nameLen); 7945 text = vim_strchr(text, TAB); /* stop at TAB before actext */ 7946 if (text == NULL) 7947 text = label + nameLen; /* no actext, use whole name */ 7948 while (padding0-- > 0) 7949 *text++ = ' '; 7950 if (menu->actext != NULL) 7951 { 7952 STRNCPY(text, menu->actext, acLen); 7953 text += acLen; 7954 } 7955 while (padding1-- > 0) 7956 *text++ = ' '; 7957 if (menu->children != NULL) 7958 { 7959 STRCPY(text, TEAROFF_SUBMENU_LABEL); 7960 text += STRLEN(TEAROFF_SUBMENU_LABEL); 7961 } 7962 else 7963 { 7964 while (padding2-- > 0) 7965 *text++ = ' '; 7966 } 7967 *text = NUL; 7968 7969 /* 7970 * BS_LEFT will just be ignored on Win32s/NT3.5x - on 7971 * W95/NT4 it makes the tear-off look more like a menu. 7972 */ 7973 p = add_dialog_element(p, 7974 BS_PUSHBUTTON|BS_LEFT, 7975 (WORD)PixelToDialogX(TEAROFF_PADDING_X), 7976 (WORD)(sepPadding + 1 + 13 * (*pnumitems)), 7977 (WORD)PixelToDialogX(dlgwidth - 2 * TEAROFF_PADDING_X), 7978 (WORD)12, 7979 menuID, (WORD)0x0080, (char *)label); 7980 vim_free(label); 7981 (*pnumitems)++; 7982 } 7983 7984 *ptrueheight = (WORD)(sepPadding + 1 + 13 * (*pnumitems)); 7985 7986 7987 /* show modelessly */ 7988 the_menu->tearoff_handle = CreateDialogIndirectParam( 7989 s_hinst, 7990 (LPDLGTEMPLATE)pdlgtemplate, 7991 s_hwnd, 7992 (DLGPROC)tearoff_callback, 7993 (LPARAM)top_menu); 7994 7995 LocalFree(LocalHandle(pdlgtemplate)); 7996 SelectFont(hdc, oldFont); 7997 DeleteObject(font); 7998 ReleaseDC(hwnd, hdc); 7999 8000 /* 8001 * Reassert ourselves as the active window. This is so that after creating 8002 * a tearoff, the user doesn't have to click with the mouse just to start 8003 * typing again! 8004 */ 8005 (void)SetActiveWindow(s_hwnd); 8006 8007 /* make sure the right buttons are enabled */ 8008 force_menu_update = TRUE; 8009 } 8010 #endif 8011 8012 #if defined(FEAT_TOOLBAR) || defined(PROTO) 8013 #include "gui_w32_rc.h" 8014 8015 /* This not defined in older SDKs */ 8016 # ifndef TBSTYLE_FLAT 8017 # define TBSTYLE_FLAT 0x0800 8018 # endif 8019 8020 /* 8021 * Create the toolbar, initially unpopulated. 8022 * (just like the menu, there are no defaults, it's all 8023 * set up through menu.vim) 8024 */ 8025 static void 8026 initialise_toolbar(void) 8027 { 8028 InitCommonControls(); 8029 s_toolbarhwnd = CreateToolbarEx( 8030 s_hwnd, 8031 WS_CHILD | TBSTYLE_TOOLTIPS | TBSTYLE_FLAT, 8032 4000, //any old big number 8033 31, //number of images in initial bitmap 8034 s_hinst, 8035 IDR_TOOLBAR1, // id of initial bitmap 8036 NULL, 8037 0, // initial number of buttons 8038 TOOLBAR_BUTTON_WIDTH, //api guide is wrong! 8039 TOOLBAR_BUTTON_HEIGHT, 8040 TOOLBAR_BUTTON_WIDTH, 8041 TOOLBAR_BUTTON_HEIGHT, 8042 sizeof(TBBUTTON) 8043 ); 8044 s_toolbar_wndproc = SubclassWindow(s_toolbarhwnd, toolbar_wndproc); 8045 8046 gui_mch_show_toolbar(vim_strchr(p_go, GO_TOOLBAR) != NULL); 8047 } 8048 8049 static LRESULT CALLBACK 8050 toolbar_wndproc( 8051 HWND hwnd, 8052 UINT uMsg, 8053 WPARAM wParam, 8054 LPARAM lParam) 8055 { 8056 HandleMouseHide(uMsg, lParam); 8057 return CallWindowProc(s_toolbar_wndproc, hwnd, uMsg, wParam, lParam); 8058 } 8059 8060 static int 8061 get_toolbar_bitmap(vimmenu_T *menu) 8062 { 8063 int i = -1; 8064 8065 /* 8066 * Check user bitmaps first, unless builtin is specified. 8067 */ 8068 if (!menu->icon_builtin) 8069 { 8070 char_u fname[MAXPATHL]; 8071 HANDLE hbitmap = NULL; 8072 8073 if (menu->iconfile != NULL) 8074 { 8075 gui_find_iconfile(menu->iconfile, fname, "bmp"); 8076 hbitmap = LoadImage( 8077 NULL, 8078 (LPCSTR)fname, 8079 IMAGE_BITMAP, 8080 TOOLBAR_BUTTON_WIDTH, 8081 TOOLBAR_BUTTON_HEIGHT, 8082 LR_LOADFROMFILE | 8083 LR_LOADMAP3DCOLORS 8084 ); 8085 } 8086 8087 /* 8088 * If the LoadImage call failed, or the "icon=" file 8089 * didn't exist or wasn't specified, try the menu name 8090 */ 8091 if (hbitmap == NULL 8092 && (gui_find_bitmap( 8093 #ifdef FEAT_MULTI_LANG 8094 menu->en_dname != NULL ? menu->en_dname : 8095 #endif 8096 menu->dname, fname, "bmp") == OK)) 8097 hbitmap = LoadImage( 8098 NULL, 8099 (LPCSTR)fname, 8100 IMAGE_BITMAP, 8101 TOOLBAR_BUTTON_WIDTH, 8102 TOOLBAR_BUTTON_HEIGHT, 8103 LR_LOADFROMFILE | 8104 LR_LOADMAP3DCOLORS 8105 ); 8106 8107 if (hbitmap != NULL) 8108 { 8109 TBADDBITMAP tbAddBitmap; 8110 8111 tbAddBitmap.hInst = NULL; 8112 tbAddBitmap.nID = (long_u)hbitmap; 8113 8114 i = (int)SendMessage(s_toolbarhwnd, TB_ADDBITMAP, 8115 (WPARAM)1, (LPARAM)&tbAddBitmap); 8116 /* i will be set to -1 if it fails */ 8117 } 8118 } 8119 if (i == -1 && menu->iconidx >= 0 && menu->iconidx < TOOLBAR_BITMAP_COUNT) 8120 i = menu->iconidx; 8121 8122 return i; 8123 } 8124 #endif 8125 8126 #if defined(FEAT_GUI_TABLINE) || defined(PROTO) 8127 static void 8128 initialise_tabline(void) 8129 { 8130 InitCommonControls(); 8131 8132 s_tabhwnd = CreateWindow(WC_TABCONTROL, "Vim tabline", 8133 WS_CHILD|TCS_FOCUSNEVER|TCS_TOOLTIPS, 8134 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 8135 CW_USEDEFAULT, s_hwnd, NULL, s_hinst, NULL); 8136 s_tabline_wndproc = SubclassWindow(s_tabhwnd, tabline_wndproc); 8137 8138 gui.tabline_height = TABLINE_HEIGHT; 8139 8140 # ifdef USE_SYSMENU_FONT 8141 set_tabline_font(); 8142 # endif 8143 } 8144 8145 /* 8146 * Get tabpage_T from POINT. 8147 */ 8148 static tabpage_T * 8149 GetTabFromPoint( 8150 HWND hWnd, 8151 POINT pt) 8152 { 8153 tabpage_T *ptp = NULL; 8154 8155 if (gui_mch_showing_tabline()) 8156 { 8157 TCHITTESTINFO htinfo; 8158 htinfo.pt = pt; 8159 /* ignore if a window under cusor is not tabcontrol. */ 8160 if (s_tabhwnd == hWnd) 8161 { 8162 int idx = TabCtrl_HitTest(s_tabhwnd, &htinfo); 8163 if (idx != -1) 8164 ptp = find_tabpage(idx + 1); 8165 } 8166 } 8167 return ptp; 8168 } 8169 8170 static POINT s_pt = {0, 0}; 8171 static HCURSOR s_hCursor = NULL; 8172 8173 static LRESULT CALLBACK 8174 tabline_wndproc( 8175 HWND hwnd, 8176 UINT uMsg, 8177 WPARAM wParam, 8178 LPARAM lParam) 8179 { 8180 POINT pt; 8181 tabpage_T *tp; 8182 RECT rect; 8183 int nCenter; 8184 int idx0; 8185 int idx1; 8186 8187 HandleMouseHide(uMsg, lParam); 8188 8189 switch (uMsg) 8190 { 8191 case WM_LBUTTONDOWN: 8192 { 8193 s_pt.x = GET_X_LPARAM(lParam); 8194 s_pt.y = GET_Y_LPARAM(lParam); 8195 SetCapture(hwnd); 8196 s_hCursor = GetCursor(); /* backup default cursor */ 8197 break; 8198 } 8199 case WM_MOUSEMOVE: 8200 if (GetCapture() == hwnd 8201 && ((wParam & MK_LBUTTON)) != 0) 8202 { 8203 pt.x = GET_X_LPARAM(lParam); 8204 pt.y = s_pt.y; 8205 if (abs(pt.x - s_pt.x) > GetSystemMetrics(SM_CXDRAG)) 8206 { 8207 SetCursor(LoadCursor(NULL, IDC_SIZEWE)); 8208 8209 tp = GetTabFromPoint(hwnd, pt); 8210 if (tp != NULL) 8211 { 8212 idx0 = tabpage_index(curtab) - 1; 8213 idx1 = tabpage_index(tp) - 1; 8214 8215 TabCtrl_GetItemRect(hwnd, idx1, &rect); 8216 nCenter = rect.left + (rect.right - rect.left) / 2; 8217 8218 /* Check if the mouse cursor goes over the center of 8219 * the next tab to prevent "flickering". */ 8220 if ((idx0 < idx1) && (nCenter < pt.x)) 8221 { 8222 tabpage_move(idx1 + 1); 8223 update_screen(0); 8224 } 8225 else if ((idx1 < idx0) && (pt.x < nCenter)) 8226 { 8227 tabpage_move(idx1); 8228 update_screen(0); 8229 } 8230 } 8231 } 8232 } 8233 break; 8234 case WM_LBUTTONUP: 8235 { 8236 if (GetCapture() == hwnd) 8237 { 8238 SetCursor(s_hCursor); 8239 ReleaseCapture(); 8240 } 8241 break; 8242 } 8243 default: 8244 break; 8245 } 8246 8247 return CallWindowProc(s_tabline_wndproc, hwnd, uMsg, wParam, lParam); 8248 } 8249 #endif 8250 8251 #if defined(FEAT_OLE) || defined(FEAT_EVAL) || defined(PROTO) 8252 /* 8253 * Make the GUI window come to the foreground. 8254 */ 8255 void 8256 gui_mch_set_foreground(void) 8257 { 8258 if (IsIconic(s_hwnd)) 8259 SendMessage(s_hwnd, WM_SYSCOMMAND, SC_RESTORE, 0); 8260 SetForegroundWindow(s_hwnd); 8261 } 8262 #endif 8263 8264 #if defined(FEAT_MBYTE_IME) && defined(DYNAMIC_IME) 8265 static void 8266 dyn_imm_load(void) 8267 { 8268 hLibImm = vimLoadLib("imm32.dll"); 8269 if (hLibImm == NULL) 8270 return; 8271 8272 pImmGetCompositionStringA 8273 = (void *)GetProcAddress(hLibImm, "ImmGetCompositionStringA"); 8274 pImmGetCompositionStringW 8275 = (void *)GetProcAddress(hLibImm, "ImmGetCompositionStringW"); 8276 pImmGetContext 8277 = (void *)GetProcAddress(hLibImm, "ImmGetContext"); 8278 pImmAssociateContext 8279 = (void *)GetProcAddress(hLibImm, "ImmAssociateContext"); 8280 pImmReleaseContext 8281 = (void *)GetProcAddress(hLibImm, "ImmReleaseContext"); 8282 pImmGetOpenStatus 8283 = (void *)GetProcAddress(hLibImm, "ImmGetOpenStatus"); 8284 pImmSetOpenStatus 8285 = (void *)GetProcAddress(hLibImm, "ImmSetOpenStatus"); 8286 pImmGetCompositionFont 8287 = (void *)GetProcAddress(hLibImm, "ImmGetCompositionFontA"); 8288 pImmSetCompositionFont 8289 = (void *)GetProcAddress(hLibImm, "ImmSetCompositionFontA"); 8290 pImmSetCompositionWindow 8291 = (void *)GetProcAddress(hLibImm, "ImmSetCompositionWindow"); 8292 pImmGetConversionStatus 8293 = (void *)GetProcAddress(hLibImm, "ImmGetConversionStatus"); 8294 pImmSetConversionStatus 8295 = (void *)GetProcAddress(hLibImm, "ImmSetConversionStatus"); 8296 8297 if ( pImmGetCompositionStringA == NULL 8298 || pImmGetCompositionStringW == NULL 8299 || pImmGetContext == NULL 8300 || pImmAssociateContext == NULL 8301 || pImmReleaseContext == NULL 8302 || pImmGetOpenStatus == NULL 8303 || pImmSetOpenStatus == NULL 8304 || pImmGetCompositionFont == NULL 8305 || pImmSetCompositionFont == NULL 8306 || pImmSetCompositionWindow == NULL 8307 || pImmGetConversionStatus == NULL 8308 || pImmSetConversionStatus == NULL) 8309 { 8310 FreeLibrary(hLibImm); 8311 hLibImm = NULL; 8312 pImmGetContext = NULL; 8313 return; 8314 } 8315 8316 return; 8317 } 8318 8319 #endif 8320 8321 #if defined(FEAT_SIGN_ICONS) || defined(PROTO) 8322 8323 # ifdef FEAT_XPM_W32 8324 # define IMAGE_XPM 100 8325 # endif 8326 8327 typedef struct _signicon_t 8328 { 8329 HANDLE hImage; 8330 UINT uType; 8331 #ifdef FEAT_XPM_W32 8332 HANDLE hShape; /* Mask bitmap handle */ 8333 #endif 8334 } signicon_t; 8335 8336 void 8337 gui_mch_drawsign(int row, int col, int typenr) 8338 { 8339 signicon_t *sign; 8340 int x, y, w, h; 8341 8342 if (!gui.in_use || (sign = (signicon_t *)sign_get_image(typenr)) == NULL) 8343 return; 8344 8345 #if defined(FEAT_DIRECTX) 8346 if (IS_ENABLE_DIRECTX()) 8347 DWriteContext_Flush(s_dwc); 8348 #endif 8349 8350 x = TEXT_X(col); 8351 y = TEXT_Y(row); 8352 w = gui.char_width * 2; 8353 h = gui.char_height; 8354 switch (sign->uType) 8355 { 8356 case IMAGE_BITMAP: 8357 { 8358 HDC hdcMem; 8359 HBITMAP hbmpOld; 8360 8361 hdcMem = CreateCompatibleDC(s_hdc); 8362 hbmpOld = (HBITMAP)SelectObject(hdcMem, sign->hImage); 8363 BitBlt(s_hdc, x, y, w, h, hdcMem, 0, 0, SRCCOPY); 8364 SelectObject(hdcMem, hbmpOld); 8365 DeleteDC(hdcMem); 8366 } 8367 break; 8368 case IMAGE_ICON: 8369 case IMAGE_CURSOR: 8370 DrawIconEx(s_hdc, x, y, (HICON)sign->hImage, w, h, 0, NULL, DI_NORMAL); 8371 break; 8372 #ifdef FEAT_XPM_W32 8373 case IMAGE_XPM: 8374 { 8375 HDC hdcMem; 8376 HBITMAP hbmpOld; 8377 8378 hdcMem = CreateCompatibleDC(s_hdc); 8379 hbmpOld = (HBITMAP)SelectObject(hdcMem, sign->hShape); 8380 /* Make hole */ 8381 BitBlt(s_hdc, x, y, w, h, hdcMem, 0, 0, SRCAND); 8382 8383 SelectObject(hdcMem, sign->hImage); 8384 /* Paint sign */ 8385 BitBlt(s_hdc, x, y, w, h, hdcMem, 0, 0, SRCPAINT); 8386 SelectObject(hdcMem, hbmpOld); 8387 DeleteDC(hdcMem); 8388 } 8389 break; 8390 #endif 8391 } 8392 } 8393 8394 static void 8395 close_signicon_image(signicon_t *sign) 8396 { 8397 if (sign) 8398 switch (sign->uType) 8399 { 8400 case IMAGE_BITMAP: 8401 DeleteObject((HGDIOBJ)sign->hImage); 8402 break; 8403 case IMAGE_CURSOR: 8404 DestroyCursor((HCURSOR)sign->hImage); 8405 break; 8406 case IMAGE_ICON: 8407 DestroyIcon((HICON)sign->hImage); 8408 break; 8409 #ifdef FEAT_XPM_W32 8410 case IMAGE_XPM: 8411 DeleteObject((HBITMAP)sign->hImage); 8412 DeleteObject((HBITMAP)sign->hShape); 8413 break; 8414 #endif 8415 } 8416 } 8417 8418 void * 8419 gui_mch_register_sign(char_u *signfile) 8420 { 8421 signicon_t sign, *psign; 8422 char_u *ext; 8423 8424 sign.hImage = NULL; 8425 ext = signfile + STRLEN(signfile) - 4; /* get extension */ 8426 if (ext > signfile) 8427 { 8428 int do_load = 1; 8429 8430 if (!STRICMP(ext, ".bmp")) 8431 sign.uType = IMAGE_BITMAP; 8432 else if (!STRICMP(ext, ".ico")) 8433 sign.uType = IMAGE_ICON; 8434 else if (!STRICMP(ext, ".cur") || !STRICMP(ext, ".ani")) 8435 sign.uType = IMAGE_CURSOR; 8436 else 8437 do_load = 0; 8438 8439 if (do_load) 8440 sign.hImage = (HANDLE)LoadImage(NULL, (LPCSTR)signfile, sign.uType, 8441 gui.char_width * 2, gui.char_height, 8442 LR_LOADFROMFILE | LR_CREATEDIBSECTION); 8443 #ifdef FEAT_XPM_W32 8444 if (!STRICMP(ext, ".xpm")) 8445 { 8446 sign.uType = IMAGE_XPM; 8447 LoadXpmImage((char *)signfile, (HBITMAP *)&sign.hImage, 8448 (HBITMAP *)&sign.hShape); 8449 } 8450 #endif 8451 } 8452 8453 psign = NULL; 8454 if (sign.hImage && (psign = (signicon_t *)alloc(sizeof(signicon_t))) 8455 != NULL) 8456 *psign = sign; 8457 8458 if (!psign) 8459 { 8460 if (sign.hImage) 8461 close_signicon_image(&sign); 8462 emsg(_(e_signdata)); 8463 } 8464 return (void *)psign; 8465 8466 } 8467 8468 void 8469 gui_mch_destroy_sign(void *sign) 8470 { 8471 if (sign) 8472 { 8473 close_signicon_image((signicon_t *)sign); 8474 vim_free(sign); 8475 } 8476 } 8477 #endif 8478 8479 #if defined(FEAT_BEVAL_GUI) || defined(PROTO) 8480 8481 /* BALLOON-EVAL IMPLEMENTATION FOR WINDOWS. 8482 * Added by Sergey Khorev <[email protected]> 8483 * 8484 * The only reused thing is beval.h and get_beval_info() 8485 * from gui_beval.c (note it uses x and y of the BalloonEval struct 8486 * to get current mouse position). 8487 * 8488 * Trying to use as more Windows services as possible, and as less 8489 * IE version as possible :)). 8490 * 8491 * 1) Don't create ToolTip in gui_mch_create_beval_area, only initialize 8492 * BalloonEval struct. 8493 * 2) Enable/Disable simply create/kill BalloonEval Timer 8494 * 3) When there was enough inactivity, timer procedure posts 8495 * async request to debugger 8496 * 4) gui_mch_post_balloon (invoked from netbeans.c) creates tooltip control 8497 * and performs some actions to show it ASAP 8498 * 5) WM_NOTIFY:TTN_POP destroys created tooltip 8499 */ 8500 8501 /* 8502 * determine whether installed Common Controls support multiline tooltips 8503 * (i.e. their version is >= 4.70 8504 */ 8505 int 8506 multiline_balloon_available(void) 8507 { 8508 HINSTANCE hDll; 8509 static char comctl_dll[] = "comctl32.dll"; 8510 static int multiline_tip = MAYBE; 8511 8512 if (multiline_tip != MAYBE) 8513 return multiline_tip; 8514 8515 hDll = GetModuleHandle(comctl_dll); 8516 if (hDll != NULL) 8517 { 8518 DLLGETVERSIONPROC pGetVer; 8519 pGetVer = (DLLGETVERSIONPROC)GetProcAddress(hDll, "DllGetVersion"); 8520 8521 if (pGetVer != NULL) 8522 { 8523 DLLVERSIONINFO dvi; 8524 HRESULT hr; 8525 8526 ZeroMemory(&dvi, sizeof(dvi)); 8527 dvi.cbSize = sizeof(dvi); 8528 8529 hr = (*pGetVer)(&dvi); 8530 8531 if (SUCCEEDED(hr) 8532 && (dvi.dwMajorVersion > 4 8533 || (dvi.dwMajorVersion == 4 8534 && dvi.dwMinorVersion >= 70))) 8535 { 8536 multiline_tip = TRUE; 8537 return multiline_tip; 8538 } 8539 } 8540 else 8541 { 8542 /* there is chance we have ancient CommCtl 4.70 8543 which doesn't export DllGetVersion */ 8544 DWORD dwHandle = 0; 8545 DWORD len = GetFileVersionInfoSize(comctl_dll, &dwHandle); 8546 if (len > 0) 8547 { 8548 VS_FIXEDFILEINFO *ver; 8549 UINT vlen = 0; 8550 void *data = alloc(len); 8551 8552 if ((data != NULL 8553 && GetFileVersionInfo(comctl_dll, 0, len, data) 8554 && VerQueryValue(data, "\\", (void **)&ver, &vlen) 8555 && vlen 8556 && HIWORD(ver->dwFileVersionMS) > 4) 8557 || ((HIWORD(ver->dwFileVersionMS) == 4 8558 && LOWORD(ver->dwFileVersionMS) >= 70))) 8559 { 8560 vim_free(data); 8561 multiline_tip = TRUE; 8562 return multiline_tip; 8563 } 8564 vim_free(data); 8565 } 8566 } 8567 } 8568 multiline_tip = FALSE; 8569 return multiline_tip; 8570 } 8571 8572 static void 8573 make_tooltipw(BalloonEval *beval, char *text, POINT pt) 8574 { 8575 TOOLINFOW *pti; 8576 int ToolInfoSize; 8577 8578 if (multiline_balloon_available() == TRUE) 8579 ToolInfoSize = sizeof(TOOLINFOW_NEW); 8580 else 8581 ToolInfoSize = sizeof(TOOLINFOW); 8582 8583 pti = (TOOLINFOW *)alloc(ToolInfoSize); 8584 if (pti == NULL) 8585 return; 8586 8587 beval->balloon = CreateWindowExW(WS_EX_TOPMOST, TOOLTIPS_CLASSW, 8588 NULL, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP, 8589 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 8590 beval->target, NULL, s_hinst, NULL); 8591 8592 SetWindowPos(beval->balloon, HWND_TOPMOST, 0, 0, 0, 0, 8593 SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); 8594 8595 pti->cbSize = ToolInfoSize; 8596 pti->uFlags = TTF_SUBCLASS; 8597 pti->hwnd = beval->target; 8598 pti->hinst = 0; // Don't use string resources 8599 pti->uId = ID_BEVAL_TOOLTIP; 8600 8601 if (multiline_balloon_available() == TRUE) 8602 { 8603 RECT rect; 8604 TOOLINFOW_NEW *ptin = (TOOLINFOW_NEW *)pti; 8605 pti->lpszText = LPSTR_TEXTCALLBACKW; 8606 beval->tofree = enc_to_utf16((char_u*)text, NULL); 8607 ptin->lParam = (LPARAM)beval->tofree; 8608 // switch multiline tooltips on 8609 if (GetClientRect(s_textArea, &rect)) 8610 SendMessageW(beval->balloon, TTM_SETMAXTIPWIDTH, 0, 8611 (LPARAM)rect.right); 8612 } 8613 else 8614 { 8615 // do this old way 8616 beval->tofree = enc_to_utf16((char_u*)text, NULL); 8617 pti->lpszText = (LPWSTR)beval->tofree; 8618 } 8619 8620 // Limit ballooneval bounding rect to CursorPos neighbourhood. 8621 pti->rect.left = pt.x - 3; 8622 pti->rect.top = pt.y - 3; 8623 pti->rect.right = pt.x + 3; 8624 pti->rect.bottom = pt.y + 3; 8625 8626 SendMessageW(beval->balloon, TTM_ADDTOOLW, 0, (LPARAM)pti); 8627 // Make tooltip appear sooner. 8628 SendMessageW(beval->balloon, TTM_SETDELAYTIME, TTDT_INITIAL, 10); 8629 // I've performed some tests and it seems the longest possible life time 8630 // of tooltip is 30 seconds. 8631 SendMessageW(beval->balloon, TTM_SETDELAYTIME, TTDT_AUTOPOP, 30000); 8632 /* 8633 * HACK: force tooltip to appear, because it'll not appear until 8634 * first mouse move. D*mn M$ 8635 * Amazingly moving (2, 2) and then (-1, -1) the mouse doesn't move. 8636 */ 8637 mouse_event(MOUSEEVENTF_MOVE, 2, 2, 0, 0); 8638 mouse_event(MOUSEEVENTF_MOVE, (DWORD)-1, (DWORD)-1, 0, 0); 8639 vim_free(pti); 8640 } 8641 8642 static void 8643 make_tooltip(BalloonEval *beval, char *text, POINT pt) 8644 { 8645 TOOLINFO *pti; 8646 int ToolInfoSize; 8647 8648 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage) 8649 { 8650 make_tooltipw(beval, text, pt); 8651 return; 8652 } 8653 8654 if (multiline_balloon_available() == TRUE) 8655 ToolInfoSize = sizeof(TOOLINFO_NEW); 8656 else 8657 ToolInfoSize = sizeof(TOOLINFO); 8658 8659 pti = (TOOLINFO *)alloc(ToolInfoSize); 8660 if (pti == NULL) 8661 return; 8662 8663 beval->balloon = CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS, 8664 NULL, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP, 8665 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 8666 beval->target, NULL, s_hinst, NULL); 8667 8668 SetWindowPos(beval->balloon, HWND_TOPMOST, 0, 0, 0, 0, 8669 SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); 8670 8671 pti->cbSize = ToolInfoSize; 8672 pti->uFlags = TTF_SUBCLASS; 8673 pti->hwnd = beval->target; 8674 pti->hinst = 0; /* Don't use string resources */ 8675 pti->uId = ID_BEVAL_TOOLTIP; 8676 8677 if (multiline_balloon_available() == TRUE) 8678 { 8679 RECT rect; 8680 TOOLINFO_NEW *ptin = (TOOLINFO_NEW *)pti; 8681 pti->lpszText = LPSTR_TEXTCALLBACK; 8682 beval->tofree = vim_strsave((char_u*)text); 8683 ptin->lParam = (LPARAM)beval->tofree; 8684 if (GetClientRect(s_textArea, &rect)) /* switch multiline tooltips on */ 8685 SendMessage(beval->balloon, TTM_SETMAXTIPWIDTH, 0, 8686 (LPARAM)rect.right); 8687 } 8688 else 8689 pti->lpszText = text; /* do this old way */ 8690 8691 /* Limit ballooneval bounding rect to CursorPos neighbourhood */ 8692 pti->rect.left = pt.x - 3; 8693 pti->rect.top = pt.y - 3; 8694 pti->rect.right = pt.x + 3; 8695 pti->rect.bottom = pt.y + 3; 8696 8697 SendMessage(beval->balloon, TTM_ADDTOOL, 0, (LPARAM)pti); 8698 /* Make tooltip appear sooner */ 8699 SendMessage(beval->balloon, TTM_SETDELAYTIME, TTDT_INITIAL, 10); 8700 /* I've performed some tests and it seems the longest possible life time 8701 * of tooltip is 30 seconds */ 8702 SendMessage(beval->balloon, TTM_SETDELAYTIME, TTDT_AUTOPOP, 30000); 8703 /* 8704 * HACK: force tooltip to appear, because it'll not appear until 8705 * first mouse move. D*mn M$ 8706 * Amazingly moving (2, 2) and then (-1, -1) the mouse doesn't move. 8707 */ 8708 mouse_event(MOUSEEVENTF_MOVE, 2, 2, 0, 0); 8709 mouse_event(MOUSEEVENTF_MOVE, (DWORD)-1, (DWORD)-1, 0, 0); 8710 vim_free(pti); 8711 } 8712 8713 static void 8714 delete_tooltip(BalloonEval *beval) 8715 { 8716 PostMessage(beval->balloon, WM_CLOSE, 0, 0); 8717 } 8718 8719 static VOID CALLBACK 8720 BevalTimerProc( 8721 HWND hwnd UNUSED, 8722 UINT uMsg UNUSED, 8723 UINT_PTR idEvent UNUSED, 8724 DWORD dwTime) 8725 { 8726 POINT pt; 8727 RECT rect; 8728 8729 if (cur_beval == NULL || cur_beval->showState == ShS_SHOWING || !p_beval) 8730 return; 8731 8732 GetCursorPos(&pt); 8733 if (WindowFromPoint(pt) != s_textArea) 8734 return; 8735 8736 ScreenToClient(s_textArea, &pt); 8737 GetClientRect(s_textArea, &rect); 8738 if (!PtInRect(&rect, pt)) 8739 return; 8740 8741 if (LastActivity > 0 8742 && (dwTime - LastActivity) >= (DWORD)p_bdlay 8743 && (cur_beval->showState != ShS_PENDING 8744 || abs(cur_beval->x - pt.x) > 3 8745 || abs(cur_beval->y - pt.y) > 3)) 8746 { 8747 /* Pointer resting in one place long enough, it's time to show 8748 * the tooltip. */ 8749 cur_beval->showState = ShS_PENDING; 8750 cur_beval->x = pt.x; 8751 cur_beval->y = pt.y; 8752 8753 // TRACE0("BevalTimerProc: sending request"); 8754 8755 if (cur_beval->msgCB != NULL) 8756 (*cur_beval->msgCB)(cur_beval, 0); 8757 } 8758 } 8759 8760 void 8761 gui_mch_disable_beval_area(BalloonEval *beval UNUSED) 8762 { 8763 // TRACE0("gui_mch_disable_beval_area {{{"); 8764 KillTimer(s_textArea, BevalTimerId); 8765 // TRACE0("gui_mch_disable_beval_area }}}"); 8766 } 8767 8768 void 8769 gui_mch_enable_beval_area(BalloonEval *beval) 8770 { 8771 // TRACE0("gui_mch_enable_beval_area |||"); 8772 if (beval == NULL) 8773 return; 8774 // TRACE0("gui_mch_enable_beval_area {{{"); 8775 BevalTimerId = SetTimer(s_textArea, 0, (UINT)(p_bdlay / 2), BevalTimerProc); 8776 // TRACE0("gui_mch_enable_beval_area }}}"); 8777 } 8778 8779 void 8780 gui_mch_post_balloon(BalloonEval *beval, char_u *mesg) 8781 { 8782 POINT pt; 8783 8784 // TRACE0("gui_mch_post_balloon {{{"); 8785 if (beval->showState == ShS_SHOWING) 8786 return; 8787 GetCursorPos(&pt); 8788 ScreenToClient(s_textArea, &pt); 8789 8790 if (abs(beval->x - pt.x) < 3 && abs(beval->y - pt.y) < 3) 8791 { 8792 /* cursor is still here */ 8793 gui_mch_disable_beval_area(cur_beval); 8794 beval->showState = ShS_SHOWING; 8795 make_tooltip(beval, (char *)mesg, pt); 8796 } 8797 // TRACE0("gui_mch_post_balloon }}}"); 8798 } 8799 8800 BalloonEval * 8801 gui_mch_create_beval_area( 8802 void *target, /* ignored, always use s_textArea */ 8803 char_u *mesg, 8804 void (*mesgCB)(BalloonEval *, int), 8805 void *clientData) 8806 { 8807 /* partially stolen from gui_beval.c */ 8808 BalloonEval *beval; 8809 8810 if (mesg != NULL && mesgCB != NULL) 8811 { 8812 iemsg(_("E232: Cannot create BalloonEval with both message and callback")); 8813 return NULL; 8814 } 8815 8816 beval = (BalloonEval *)alloc_clear(sizeof(BalloonEval)); 8817 if (beval != NULL) 8818 { 8819 beval->target = s_textArea; 8820 8821 beval->showState = ShS_NEUTRAL; 8822 beval->msg = mesg; 8823 beval->msgCB = mesgCB; 8824 beval->clientData = clientData; 8825 8826 InitCommonControls(); 8827 cur_beval = beval; 8828 8829 if (p_beval) 8830 gui_mch_enable_beval_area(beval); 8831 } 8832 return beval; 8833 } 8834 8835 static void 8836 Handle_WM_Notify(HWND hwnd UNUSED, LPNMHDR pnmh) 8837 { 8838 if (pnmh->idFrom != ID_BEVAL_TOOLTIP) /* it is not our tooltip */ 8839 return; 8840 8841 if (cur_beval != NULL) 8842 { 8843 switch (pnmh->code) 8844 { 8845 case TTN_SHOW: 8846 // TRACE0("TTN_SHOW {{{"); 8847 // TRACE0("TTN_SHOW }}}"); 8848 break; 8849 case TTN_POP: /* Before tooltip disappear */ 8850 // TRACE0("TTN_POP {{{"); 8851 delete_tooltip(cur_beval); 8852 gui_mch_enable_beval_area(cur_beval); 8853 // TRACE0("TTN_POP }}}"); 8854 8855 cur_beval->showState = ShS_NEUTRAL; 8856 break; 8857 case TTN_GETDISPINFO: 8858 { 8859 /* if you get there then we have new common controls */ 8860 NMTTDISPINFO_NEW *info = (NMTTDISPINFO_NEW *)pnmh; 8861 info->lpszText = (LPSTR)info->lParam; 8862 info->uFlags |= TTF_DI_SETITEM; 8863 } 8864 break; 8865 case TTN_GETDISPINFOW: 8866 { 8867 // if we get here then we have new common controls 8868 NMTTDISPINFOW_NEW *info = (NMTTDISPINFOW_NEW *)pnmh; 8869 info->lpszText = (LPWSTR)info->lParam; 8870 info->uFlags |= TTF_DI_SETITEM; 8871 } 8872 break; 8873 } 8874 } 8875 } 8876 8877 static void 8878 TrackUserActivity(UINT uMsg) 8879 { 8880 if ((uMsg >= WM_MOUSEFIRST && uMsg <= WM_MOUSELAST) 8881 || (uMsg >= WM_KEYFIRST && uMsg <= WM_KEYLAST)) 8882 LastActivity = GetTickCount(); 8883 } 8884 8885 void 8886 gui_mch_destroy_beval_area(BalloonEval *beval) 8887 { 8888 #ifdef FEAT_VARTABS 8889 vim_free(beval->vts); 8890 #endif 8891 vim_free(beval->tofree); 8892 vim_free(beval); 8893 } 8894 #endif /* FEAT_BEVAL_GUI */ 8895 8896 #if defined(FEAT_NETBEANS_INTG) || defined(PROTO) 8897 /* 8898 * We have multiple signs to draw at the same location. Draw the 8899 * multi-sign indicator (down-arrow) instead. This is the Win32 version. 8900 */ 8901 void 8902 netbeans_draw_multisign_indicator(int row) 8903 { 8904 int i; 8905 int y; 8906 int x; 8907 8908 if (!netbeans_active()) 8909 return; 8910 8911 x = 0; 8912 y = TEXT_Y(row); 8913 8914 #if defined(FEAT_DIRECTX) 8915 if (IS_ENABLE_DIRECTX()) 8916 DWriteContext_Flush(s_dwc); 8917 #endif 8918 8919 for (i = 0; i < gui.char_height - 3; i++) 8920 SetPixel(s_hdc, x+2, y++, gui.currFgColor); 8921 8922 SetPixel(s_hdc, x+0, y, gui.currFgColor); 8923 SetPixel(s_hdc, x+2, y, gui.currFgColor); 8924 SetPixel(s_hdc, x+4, y++, gui.currFgColor); 8925 SetPixel(s_hdc, x+1, y, gui.currFgColor); 8926 SetPixel(s_hdc, x+2, y, gui.currFgColor); 8927 SetPixel(s_hdc, x+3, y++, gui.currFgColor); 8928 SetPixel(s_hdc, x+2, y, gui.currFgColor); 8929 } 8930 #endif 8931