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 _cdecl 2284 SaveInst(HINSTANCE hInst) 2285 { 2286 s_hinst = hInst; 2287 } 2288 #endif 2289 2290 /* 2291 * Return the RGB value of a pixel as a long. 2292 */ 2293 guicolor_T 2294 gui_mch_get_rgb(guicolor_T pixel) 2295 { 2296 return (guicolor_T)((GetRValue(pixel) << 16) + (GetGValue(pixel) << 8) 2297 + GetBValue(pixel)); 2298 } 2299 2300 #if defined(FEAT_GUI_DIALOG) || defined(PROTO) 2301 /* Convert pixels in X to dialog units */ 2302 static WORD 2303 PixelToDialogX(int numPixels) 2304 { 2305 return (WORD)((numPixels * 4) / s_dlgfntwidth); 2306 } 2307 2308 /* Convert pixels in Y to dialog units */ 2309 static WORD 2310 PixelToDialogY(int numPixels) 2311 { 2312 return (WORD)((numPixels * 8) / s_dlgfntheight); 2313 } 2314 2315 /* Return the width in pixels of the given text in the given DC. */ 2316 static int 2317 GetTextWidth(HDC hdc, char_u *str, int len) 2318 { 2319 SIZE size; 2320 2321 GetTextExtentPoint(hdc, (LPCSTR)str, len, &size); 2322 return size.cx; 2323 } 2324 2325 /* 2326 * Return the width in pixels of the given text in the given DC, taking care 2327 * of 'encoding' to active codepage conversion. 2328 */ 2329 static int 2330 GetTextWidthEnc(HDC hdc, char_u *str, int len) 2331 { 2332 SIZE size; 2333 WCHAR *wstr; 2334 int n; 2335 int wlen = len; 2336 2337 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage) 2338 { 2339 /* 'encoding' differs from active codepage: convert text and use wide 2340 * function */ 2341 wstr = enc_to_utf16(str, &wlen); 2342 if (wstr != NULL) 2343 { 2344 n = GetTextExtentPointW(hdc, wstr, wlen, &size); 2345 vim_free(wstr); 2346 if (n) 2347 return size.cx; 2348 } 2349 } 2350 2351 return GetTextWidth(hdc, str, len); 2352 } 2353 2354 static void get_work_area(RECT *spi_rect); 2355 2356 /* 2357 * A quick little routine that will center one window over another, handy for 2358 * dialog boxes. Taken from the Win32SDK samples and modified for multiple 2359 * monitors. 2360 */ 2361 static BOOL 2362 CenterWindow( 2363 HWND hwndChild, 2364 HWND hwndParent) 2365 { 2366 HMONITOR mon; 2367 MONITORINFO moninfo; 2368 RECT rChild, rParent, rScreen; 2369 int wChild, hChild, wParent, hParent; 2370 int xNew, yNew; 2371 HDC hdc; 2372 2373 GetWindowRect(hwndChild, &rChild); 2374 wChild = rChild.right - rChild.left; 2375 hChild = rChild.bottom - rChild.top; 2376 2377 /* If Vim is minimized put the window in the middle of the screen. */ 2378 if (hwndParent == NULL || IsMinimized(hwndParent)) 2379 get_work_area(&rParent); 2380 else 2381 GetWindowRect(hwndParent, &rParent); 2382 wParent = rParent.right - rParent.left; 2383 hParent = rParent.bottom - rParent.top; 2384 2385 moninfo.cbSize = sizeof(MONITORINFO); 2386 mon = MonitorFromWindow(hwndChild, MONITOR_DEFAULTTOPRIMARY); 2387 if (mon != NULL && GetMonitorInfo(mon, &moninfo)) 2388 { 2389 rScreen = moninfo.rcWork; 2390 } 2391 else 2392 { 2393 hdc = GetDC(hwndChild); 2394 rScreen.left = 0; 2395 rScreen.top = 0; 2396 rScreen.right = GetDeviceCaps(hdc, HORZRES); 2397 rScreen.bottom = GetDeviceCaps(hdc, VERTRES); 2398 ReleaseDC(hwndChild, hdc); 2399 } 2400 2401 xNew = rParent.left + ((wParent - wChild) / 2); 2402 if (xNew < rScreen.left) 2403 xNew = rScreen.left; 2404 else if ((xNew + wChild) > rScreen.right) 2405 xNew = rScreen.right - wChild; 2406 2407 yNew = rParent.top + ((hParent - hChild) / 2); 2408 if (yNew < rScreen.top) 2409 yNew = rScreen.top; 2410 else if ((yNew + hChild) > rScreen.bottom) 2411 yNew = rScreen.bottom - hChild; 2412 2413 return SetWindowPos(hwndChild, NULL, xNew, yNew, 0, 0, 2414 SWP_NOSIZE | SWP_NOZORDER); 2415 } 2416 #endif /* FEAT_GUI_DIALOG */ 2417 2418 #if defined(FEAT_TOOLBAR) || defined(PROTO) 2419 void 2420 gui_mch_show_toolbar(int showit) 2421 { 2422 if (s_toolbarhwnd == NULL) 2423 return; 2424 2425 if (showit) 2426 { 2427 # ifndef TB_SETUNICODEFORMAT 2428 /* For older compilers. We assume this never changes. */ 2429 # define TB_SETUNICODEFORMAT 0x2005 2430 # endif 2431 /* Enable/disable unicode support */ 2432 int uu = (enc_codepage >= 0 && (int)GetACP() != enc_codepage); 2433 SendMessage(s_toolbarhwnd, TB_SETUNICODEFORMAT, (WPARAM)uu, (LPARAM)0); 2434 ShowWindow(s_toolbarhwnd, SW_SHOW); 2435 } 2436 else 2437 ShowWindow(s_toolbarhwnd, SW_HIDE); 2438 } 2439 2440 /* Then number of bitmaps is fixed. Exit is missing! */ 2441 #define TOOLBAR_BITMAP_COUNT 31 2442 2443 #endif 2444 2445 #if defined(FEAT_GUI_TABLINE) || defined(PROTO) 2446 static void 2447 add_tabline_popup_menu_entry(HMENU pmenu, UINT item_id, char_u *item_text) 2448 { 2449 WCHAR *wn = NULL; 2450 2451 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage) 2452 { 2453 /* 'encoding' differs from active codepage: convert menu name 2454 * and use wide function */ 2455 wn = enc_to_utf16(item_text, NULL); 2456 if (wn != NULL) 2457 { 2458 MENUITEMINFOW infow; 2459 2460 infow.cbSize = sizeof(infow); 2461 infow.fMask = MIIM_TYPE | MIIM_ID; 2462 infow.wID = item_id; 2463 infow.fType = MFT_STRING; 2464 infow.dwTypeData = wn; 2465 infow.cch = (UINT)wcslen(wn); 2466 InsertMenuItemW(pmenu, item_id, FALSE, &infow); 2467 vim_free(wn); 2468 } 2469 } 2470 2471 if (wn == NULL) 2472 { 2473 MENUITEMINFO info; 2474 2475 info.cbSize = sizeof(info); 2476 info.fMask = MIIM_TYPE | MIIM_ID; 2477 info.wID = item_id; 2478 info.fType = MFT_STRING; 2479 info.dwTypeData = (LPTSTR)item_text; 2480 info.cch = (UINT)STRLEN(item_text); 2481 InsertMenuItem(pmenu, item_id, FALSE, &info); 2482 } 2483 } 2484 2485 static void 2486 show_tabline_popup_menu(void) 2487 { 2488 HMENU tab_pmenu; 2489 long rval; 2490 POINT pt; 2491 2492 /* When ignoring events don't show the menu. */ 2493 if (hold_gui_events 2494 # ifdef FEAT_CMDWIN 2495 || cmdwin_type != 0 2496 # endif 2497 ) 2498 return; 2499 2500 tab_pmenu = CreatePopupMenu(); 2501 if (tab_pmenu == NULL) 2502 return; 2503 2504 if (first_tabpage->tp_next != NULL) 2505 add_tabline_popup_menu_entry(tab_pmenu, 2506 TABLINE_MENU_CLOSE, (char_u *)_("Close tab")); 2507 add_tabline_popup_menu_entry(tab_pmenu, 2508 TABLINE_MENU_NEW, (char_u *)_("New tab")); 2509 add_tabline_popup_menu_entry(tab_pmenu, 2510 TABLINE_MENU_OPEN, (char_u *)_("Open tab...")); 2511 2512 GetCursorPos(&pt); 2513 rval = TrackPopupMenuEx(tab_pmenu, TPM_RETURNCMD, pt.x, pt.y, s_tabhwnd, 2514 NULL); 2515 2516 DestroyMenu(tab_pmenu); 2517 2518 /* Add the string cmd into input buffer */ 2519 if (rval > 0) 2520 { 2521 TCHITTESTINFO htinfo; 2522 int idx; 2523 2524 if (ScreenToClient(s_tabhwnd, &pt) == 0) 2525 return; 2526 2527 htinfo.pt.x = pt.x; 2528 htinfo.pt.y = pt.y; 2529 idx = TabCtrl_HitTest(s_tabhwnd, &htinfo); 2530 if (idx == -1) 2531 idx = 0; 2532 else 2533 idx += 1; 2534 2535 send_tabline_menu_event(idx, (int)rval); 2536 } 2537 } 2538 2539 /* 2540 * Show or hide the tabline. 2541 */ 2542 void 2543 gui_mch_show_tabline(int showit) 2544 { 2545 if (s_tabhwnd == NULL) 2546 return; 2547 2548 if (!showit != !showing_tabline) 2549 { 2550 if (showit) 2551 ShowWindow(s_tabhwnd, SW_SHOW); 2552 else 2553 ShowWindow(s_tabhwnd, SW_HIDE); 2554 showing_tabline = showit; 2555 } 2556 } 2557 2558 /* 2559 * Return TRUE when tabline is displayed. 2560 */ 2561 int 2562 gui_mch_showing_tabline(void) 2563 { 2564 return s_tabhwnd != NULL && showing_tabline; 2565 } 2566 2567 /* 2568 * Update the labels of the tabline. 2569 */ 2570 void 2571 gui_mch_update_tabline(void) 2572 { 2573 tabpage_T *tp; 2574 TCITEM tie; 2575 int nr = 0; 2576 int curtabidx = 0; 2577 int tabadded = 0; 2578 static int use_unicode = FALSE; 2579 int uu; 2580 WCHAR *wstr = NULL; 2581 2582 if (s_tabhwnd == NULL) 2583 return; 2584 2585 #ifndef CCM_SETUNICODEFORMAT 2586 /* For older compilers. We assume this never changes. */ 2587 # define CCM_SETUNICODEFORMAT 0x2005 2588 #endif 2589 uu = (enc_codepage >= 0 && (int)GetACP() != enc_codepage); 2590 if (uu != use_unicode) 2591 { 2592 /* Enable/disable unicode support */ 2593 SendMessage(s_tabhwnd, CCM_SETUNICODEFORMAT, (WPARAM)uu, (LPARAM)0); 2594 use_unicode = uu; 2595 } 2596 2597 tie.mask = TCIF_TEXT; 2598 tie.iImage = -1; 2599 2600 /* Disable redraw for tab updates to eliminate O(N^2) draws. */ 2601 SendMessage(s_tabhwnd, WM_SETREDRAW, (WPARAM)FALSE, 0); 2602 2603 /* Add a label for each tab page. They all contain the same text area. */ 2604 for (tp = first_tabpage; tp != NULL; tp = tp->tp_next, ++nr) 2605 { 2606 if (tp == curtab) 2607 curtabidx = nr; 2608 2609 if (nr >= TabCtrl_GetItemCount(s_tabhwnd)) 2610 { 2611 /* Add the tab */ 2612 tie.pszText = "-Empty-"; 2613 TabCtrl_InsertItem(s_tabhwnd, nr, &tie); 2614 tabadded = 1; 2615 } 2616 2617 get_tabline_label(tp, FALSE); 2618 tie.pszText = (LPSTR)NameBuff; 2619 wstr = NULL; 2620 if (use_unicode) 2621 { 2622 /* Need to go through Unicode. */ 2623 wstr = enc_to_utf16(NameBuff, NULL); 2624 if (wstr != NULL) 2625 { 2626 TCITEMW tiw; 2627 2628 tiw.mask = TCIF_TEXT; 2629 tiw.iImage = -1; 2630 tiw.pszText = wstr; 2631 SendMessage(s_tabhwnd, TCM_SETITEMW, (WPARAM)nr, (LPARAM)&tiw); 2632 vim_free(wstr); 2633 } 2634 } 2635 if (wstr == NULL) 2636 { 2637 TabCtrl_SetItem(s_tabhwnd, nr, &tie); 2638 } 2639 } 2640 2641 /* Remove any old labels. */ 2642 while (nr < TabCtrl_GetItemCount(s_tabhwnd)) 2643 TabCtrl_DeleteItem(s_tabhwnd, nr); 2644 2645 if (!tabadded && TabCtrl_GetCurSel(s_tabhwnd) != curtabidx) 2646 TabCtrl_SetCurSel(s_tabhwnd, curtabidx); 2647 2648 /* Re-enable redraw and redraw. */ 2649 SendMessage(s_tabhwnd, WM_SETREDRAW, (WPARAM)TRUE, 0); 2650 RedrawWindow(s_tabhwnd, NULL, NULL, 2651 RDW_ERASE | RDW_FRAME | RDW_INVALIDATE | RDW_ALLCHILDREN); 2652 2653 if (tabadded && TabCtrl_GetCurSel(s_tabhwnd) != curtabidx) 2654 TabCtrl_SetCurSel(s_tabhwnd, curtabidx); 2655 } 2656 2657 /* 2658 * Set the current tab to "nr". First tab is 1. 2659 */ 2660 void 2661 gui_mch_set_curtab(int nr) 2662 { 2663 if (s_tabhwnd == NULL) 2664 return; 2665 2666 if (TabCtrl_GetCurSel(s_tabhwnd) != nr - 1) 2667 TabCtrl_SetCurSel(s_tabhwnd, nr - 1); 2668 } 2669 2670 #endif 2671 2672 /* 2673 * ":simalt" command. 2674 */ 2675 void 2676 ex_simalt(exarg_T *eap) 2677 { 2678 char_u *keys = eap->arg; 2679 int fill_typebuf = FALSE; 2680 char_u key_name[4]; 2681 2682 PostMessage(s_hwnd, WM_SYSCOMMAND, (WPARAM)SC_KEYMENU, (LPARAM)0); 2683 while (*keys) 2684 { 2685 if (*keys == '~') 2686 *keys = ' '; /* for showing system menu */ 2687 PostMessage(s_hwnd, WM_CHAR, (WPARAM)*keys, (LPARAM)0); 2688 keys++; 2689 fill_typebuf = TRUE; 2690 } 2691 if (fill_typebuf) 2692 { 2693 /* Put a NOP in the typeahead buffer so that the message will get 2694 * processed. */ 2695 key_name[0] = K_SPECIAL; 2696 key_name[1] = KS_EXTRA; 2697 key_name[2] = KE_NOP; 2698 key_name[3] = NUL; 2699 typebuf_was_filled = TRUE; 2700 (void)ins_typebuf(key_name, REMAP_NONE, 0, TRUE, FALSE); 2701 } 2702 } 2703 2704 /* 2705 * Create the find & replace dialogs. 2706 * You can't have both at once: ":find" when replace is showing, destroys 2707 * the replace dialog first, and the other way around. 2708 */ 2709 #ifdef MSWIN_FIND_REPLACE 2710 static void 2711 initialise_findrep(char_u *initial_string) 2712 { 2713 int wword = FALSE; 2714 int mcase = !p_ic; 2715 char_u *entry_text; 2716 2717 /* Get the search string to use. */ 2718 entry_text = get_find_dialog_text(initial_string, &wword, &mcase); 2719 2720 s_findrep_struct.hwndOwner = s_hwnd; 2721 s_findrep_struct.Flags = FR_DOWN; 2722 if (mcase) 2723 s_findrep_struct.Flags |= FR_MATCHCASE; 2724 if (wword) 2725 s_findrep_struct.Flags |= FR_WHOLEWORD; 2726 if (entry_text != NULL && *entry_text != NUL) 2727 vim_strncpy((char_u *)s_findrep_struct.lpstrFindWhat, entry_text, 2728 s_findrep_struct.wFindWhatLen - 1); 2729 vim_free(entry_text); 2730 } 2731 #endif 2732 2733 static void 2734 set_window_title(HWND hwnd, char *title) 2735 { 2736 if (title != NULL && enc_codepage >= 0 && enc_codepage != (int)GetACP()) 2737 { 2738 WCHAR *wbuf; 2739 2740 /* Convert the title from 'encoding' to UTF-16. */ 2741 wbuf = (WCHAR *)enc_to_utf16((char_u *)title, NULL); 2742 if (wbuf != NULL) 2743 { 2744 SetWindowTextW(hwnd, wbuf); 2745 vim_free(wbuf); 2746 } 2747 return; 2748 } 2749 (void)SetWindowText(hwnd, (LPCSTR)title); 2750 } 2751 2752 void 2753 gui_mch_find_dialog(exarg_T *eap) 2754 { 2755 #ifdef MSWIN_FIND_REPLACE 2756 if (s_findrep_msg != 0) 2757 { 2758 if (IsWindow(s_findrep_hwnd) && !s_findrep_is_find) 2759 DestroyWindow(s_findrep_hwnd); 2760 2761 if (!IsWindow(s_findrep_hwnd)) 2762 { 2763 initialise_findrep(eap->arg); 2764 /* If the OS is Windows NT, and 'encoding' differs from active 2765 * codepage: convert text and use wide function. */ 2766 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage) 2767 { 2768 findrep_atow(&s_findrep_struct_w, &s_findrep_struct); 2769 s_findrep_hwnd = FindTextW( 2770 (LPFINDREPLACEW) &s_findrep_struct_w); 2771 } 2772 else 2773 s_findrep_hwnd = FindText((LPFINDREPLACE) &s_findrep_struct); 2774 } 2775 2776 set_window_title(s_findrep_hwnd, _("Find string")); 2777 (void)SetFocus(s_findrep_hwnd); 2778 2779 s_findrep_is_find = TRUE; 2780 } 2781 #endif 2782 } 2783 2784 2785 void 2786 gui_mch_replace_dialog(exarg_T *eap) 2787 { 2788 #ifdef MSWIN_FIND_REPLACE 2789 if (s_findrep_msg != 0) 2790 { 2791 if (IsWindow(s_findrep_hwnd) && s_findrep_is_find) 2792 DestroyWindow(s_findrep_hwnd); 2793 2794 if (!IsWindow(s_findrep_hwnd)) 2795 { 2796 initialise_findrep(eap->arg); 2797 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage) 2798 { 2799 findrep_atow(&s_findrep_struct_w, &s_findrep_struct); 2800 s_findrep_hwnd = ReplaceTextW( 2801 (LPFINDREPLACEW) &s_findrep_struct_w); 2802 } 2803 else 2804 s_findrep_hwnd = ReplaceText( 2805 (LPFINDREPLACE) &s_findrep_struct); 2806 } 2807 2808 set_window_title(s_findrep_hwnd, _("Find & Replace")); 2809 (void)SetFocus(s_findrep_hwnd); 2810 2811 s_findrep_is_find = FALSE; 2812 } 2813 #endif 2814 } 2815 2816 2817 /* 2818 * Set visibility of the pointer. 2819 */ 2820 void 2821 gui_mch_mousehide(int hide) 2822 { 2823 if (hide != gui.pointer_hidden) 2824 { 2825 ShowCursor(!hide); 2826 gui.pointer_hidden = hide; 2827 } 2828 } 2829 2830 #ifdef FEAT_MENU 2831 static void 2832 gui_mch_show_popupmenu_at(vimmenu_T *menu, int x, int y) 2833 { 2834 /* Unhide the mouse, we don't get move events here. */ 2835 gui_mch_mousehide(FALSE); 2836 2837 (void)TrackPopupMenu( 2838 (HMENU)menu->submenu_id, 2839 TPM_LEFTALIGN | TPM_LEFTBUTTON, 2840 x, y, 2841 (int)0, /*reserved param*/ 2842 s_hwnd, 2843 NULL); 2844 /* 2845 * NOTE: The pop-up menu can eat the mouse up event. 2846 * We deal with this in normal.c. 2847 */ 2848 } 2849 #endif 2850 2851 /* 2852 * Got a message when the system will go down. 2853 */ 2854 static void 2855 _OnEndSession(void) 2856 { 2857 getout_preserve_modified(1); 2858 } 2859 2860 /* 2861 * Get this message when the user clicks on the cross in the top right corner 2862 * of a Windows95 window. 2863 */ 2864 static void 2865 _OnClose(HWND hwnd UNUSED) 2866 { 2867 gui_shell_closed(); 2868 } 2869 2870 /* 2871 * Get a message when the window is being destroyed. 2872 */ 2873 static void 2874 _OnDestroy(HWND hwnd) 2875 { 2876 if (!destroying) 2877 _OnClose(hwnd); 2878 } 2879 2880 static void 2881 _OnPaint( 2882 HWND hwnd) 2883 { 2884 if (!IsMinimized(hwnd)) 2885 { 2886 PAINTSTRUCT ps; 2887 2888 out_flush(); /* make sure all output has been processed */ 2889 (void)BeginPaint(hwnd, &ps); 2890 2891 /* prevent multi-byte characters from misprinting on an invalid 2892 * rectangle */ 2893 if (has_mbyte) 2894 { 2895 RECT rect; 2896 2897 GetClientRect(hwnd, &rect); 2898 ps.rcPaint.left = rect.left; 2899 ps.rcPaint.right = rect.right; 2900 } 2901 2902 if (!IsRectEmpty(&ps.rcPaint)) 2903 { 2904 gui_redraw(ps.rcPaint.left, ps.rcPaint.top, 2905 ps.rcPaint.right - ps.rcPaint.left + 1, 2906 ps.rcPaint.bottom - ps.rcPaint.top + 1); 2907 } 2908 2909 EndPaint(hwnd, &ps); 2910 } 2911 } 2912 2913 static void 2914 _OnSize( 2915 HWND hwnd, 2916 UINT state UNUSED, 2917 int cx, 2918 int cy) 2919 { 2920 if (!IsMinimized(hwnd)) 2921 { 2922 gui_resize_shell(cx, cy); 2923 2924 #ifdef FEAT_MENU 2925 /* Menu bar may wrap differently now */ 2926 gui_mswin_get_menu_height(TRUE); 2927 #endif 2928 } 2929 } 2930 2931 static void 2932 _OnSetFocus( 2933 HWND hwnd, 2934 HWND hwndOldFocus) 2935 { 2936 gui_focus_change(TRUE); 2937 s_getting_focus = TRUE; 2938 (void)MyWindowProc(hwnd, WM_SETFOCUS, (WPARAM)hwndOldFocus, 0); 2939 } 2940 2941 static void 2942 _OnKillFocus( 2943 HWND hwnd, 2944 HWND hwndNewFocus) 2945 { 2946 gui_focus_change(FALSE); 2947 s_getting_focus = FALSE; 2948 (void)MyWindowProc(hwnd, WM_KILLFOCUS, (WPARAM)hwndNewFocus, 0); 2949 } 2950 2951 /* 2952 * Get a message when the user switches back to vim 2953 */ 2954 static LRESULT 2955 _OnActivateApp( 2956 HWND hwnd, 2957 BOOL fActivate, 2958 DWORD dwThreadId) 2959 { 2960 /* we call gui_focus_change() in _OnSetFocus() */ 2961 /* gui_focus_change((int)fActivate); */ 2962 return MyWindowProc(hwnd, WM_ACTIVATEAPP, fActivate, (DWORD)dwThreadId); 2963 } 2964 2965 void 2966 gui_mch_destroy_scrollbar(scrollbar_T *sb) 2967 { 2968 DestroyWindow(sb->id); 2969 } 2970 2971 /* 2972 * Get current mouse coordinates in text window. 2973 */ 2974 void 2975 gui_mch_getmouse(int *x, int *y) 2976 { 2977 RECT rct; 2978 POINT mp; 2979 2980 (void)GetWindowRect(s_textArea, &rct); 2981 (void)GetCursorPos((LPPOINT)&mp); 2982 *x = (int)(mp.x - rct.left); 2983 *y = (int)(mp.y - rct.top); 2984 } 2985 2986 /* 2987 * Move mouse pointer to character at (x, y). 2988 */ 2989 void 2990 gui_mch_setmouse(int x, int y) 2991 { 2992 RECT rct; 2993 2994 (void)GetWindowRect(s_textArea, &rct); 2995 (void)SetCursorPos(x + gui.border_offset + rct.left, 2996 y + gui.border_offset + rct.top); 2997 } 2998 2999 static void 3000 gui_mswin_get_valid_dimensions( 3001 int w, 3002 int h, 3003 int *valid_w, 3004 int *valid_h) 3005 { 3006 int base_width, base_height; 3007 3008 base_width = gui_get_base_width() 3009 + (GetSystemMetrics(SM_CXFRAME) + 3010 GetSystemMetrics(SM_CXPADDEDBORDER)) * 2; 3011 base_height = gui_get_base_height() 3012 + (GetSystemMetrics(SM_CYFRAME) + 3013 GetSystemMetrics(SM_CXPADDEDBORDER)) * 2 3014 + GetSystemMetrics(SM_CYCAPTION) 3015 #ifdef FEAT_MENU 3016 + gui_mswin_get_menu_height(FALSE) 3017 #endif 3018 ; 3019 *valid_w = base_width + 3020 ((w - base_width) / gui.char_width) * gui.char_width; 3021 *valid_h = base_height + 3022 ((h - base_height) / gui.char_height) * gui.char_height; 3023 } 3024 3025 void 3026 gui_mch_flash(int msec) 3027 { 3028 RECT rc; 3029 3030 #if defined(FEAT_DIRECTX) 3031 if (IS_ENABLE_DIRECTX()) 3032 DWriteContext_Flush(s_dwc); 3033 #endif 3034 3035 /* 3036 * Note: InvertRect() excludes right and bottom of rectangle. 3037 */ 3038 rc.left = 0; 3039 rc.top = 0; 3040 rc.right = gui.num_cols * gui.char_width; 3041 rc.bottom = gui.num_rows * gui.char_height; 3042 InvertRect(s_hdc, &rc); 3043 gui_mch_flush(); /* make sure it's displayed */ 3044 3045 ui_delay((long)msec, TRUE); /* wait for a few msec */ 3046 3047 InvertRect(s_hdc, &rc); 3048 } 3049 3050 /* 3051 * Return flags used for scrolling. 3052 * The SW_INVALIDATE is required when part of the window is covered or 3053 * off-screen. Refer to MS KB Q75236. 3054 */ 3055 static int 3056 get_scroll_flags(void) 3057 { 3058 HWND hwnd; 3059 RECT rcVim, rcOther, rcDest; 3060 3061 GetWindowRect(s_hwnd, &rcVim); 3062 3063 /* Check if the window is partly above or below the screen. We don't care 3064 * about partly left or right of the screen, it is not relevant when 3065 * scrolling up or down. */ 3066 if (rcVim.top < 0 || rcVim.bottom > GetSystemMetrics(SM_CYFULLSCREEN)) 3067 return SW_INVALIDATE; 3068 3069 /* Check if there is an window (partly) on top of us. */ 3070 for (hwnd = s_hwnd; (hwnd = GetWindow(hwnd, GW_HWNDPREV)) != (HWND)0; ) 3071 if (IsWindowVisible(hwnd)) 3072 { 3073 GetWindowRect(hwnd, &rcOther); 3074 if (IntersectRect(&rcDest, &rcVim, &rcOther)) 3075 return SW_INVALIDATE; 3076 } 3077 return 0; 3078 } 3079 3080 /* 3081 * On some Intel GPUs, the regions drawn just prior to ScrollWindowEx() 3082 * may not be scrolled out properly. 3083 * For gVim, when _OnScroll() is repeated, the character at the 3084 * previous cursor position may be left drawn after scroll. 3085 * The problem can be avoided by calling GetPixel() to get a pixel in 3086 * the region before ScrollWindowEx(). 3087 */ 3088 static void 3089 intel_gpu_workaround(void) 3090 { 3091 GetPixel(s_hdc, FILL_X(gui.col), FILL_Y(gui.row)); 3092 } 3093 3094 /* 3095 * Delete the given number of lines from the given row, scrolling up any 3096 * text further down within the scroll region. 3097 */ 3098 void 3099 gui_mch_delete_lines( 3100 int row, 3101 int num_lines) 3102 { 3103 RECT rc; 3104 3105 rc.left = FILL_X(gui.scroll_region_left); 3106 rc.right = FILL_X(gui.scroll_region_right + 1); 3107 rc.top = FILL_Y(row); 3108 rc.bottom = FILL_Y(gui.scroll_region_bot + 1); 3109 3110 #if defined(FEAT_DIRECTX) 3111 if (IS_ENABLE_DIRECTX()) 3112 { 3113 DWriteContext_Scroll(s_dwc, 0, -num_lines * gui.char_height, &rc); 3114 DWriteContext_Flush(s_dwc); 3115 } 3116 else 3117 #endif 3118 { 3119 intel_gpu_workaround(); 3120 ScrollWindowEx(s_textArea, 0, -num_lines * gui.char_height, 3121 &rc, &rc, NULL, NULL, get_scroll_flags()); 3122 UpdateWindow(s_textArea); 3123 } 3124 3125 /* This seems to be required to avoid the cursor disappearing when 3126 * scrolling such that the cursor ends up in the top-left character on 3127 * the screen... But why? (Webb) */ 3128 /* It's probably fixed by disabling drawing the cursor while scrolling. */ 3129 /* gui.cursor_is_valid = FALSE; */ 3130 3131 gui_clear_block(gui.scroll_region_bot - num_lines + 1, 3132 gui.scroll_region_left, 3133 gui.scroll_region_bot, gui.scroll_region_right); 3134 } 3135 3136 /* 3137 * Insert the given number of lines before the given row, scrolling down any 3138 * following text within the scroll region. 3139 */ 3140 void 3141 gui_mch_insert_lines( 3142 int row, 3143 int num_lines) 3144 { 3145 RECT rc; 3146 3147 rc.left = FILL_X(gui.scroll_region_left); 3148 rc.right = FILL_X(gui.scroll_region_right + 1); 3149 rc.top = FILL_Y(row); 3150 rc.bottom = FILL_Y(gui.scroll_region_bot + 1); 3151 3152 #if defined(FEAT_DIRECTX) 3153 if (IS_ENABLE_DIRECTX()) 3154 { 3155 DWriteContext_Scroll(s_dwc, 0, num_lines * gui.char_height, &rc); 3156 DWriteContext_Flush(s_dwc); 3157 } 3158 else 3159 #endif 3160 { 3161 intel_gpu_workaround(); 3162 /* The SW_INVALIDATE is required when part of the window is covered or 3163 * off-screen. How do we avoid it when it's not needed? */ 3164 ScrollWindowEx(s_textArea, 0, num_lines * gui.char_height, 3165 &rc, &rc, NULL, NULL, get_scroll_flags()); 3166 UpdateWindow(s_textArea); 3167 } 3168 3169 gui_clear_block(row, gui.scroll_region_left, 3170 row + num_lines - 1, gui.scroll_region_right); 3171 } 3172 3173 3174 void 3175 gui_mch_exit(int rc UNUSED) 3176 { 3177 #if defined(FEAT_DIRECTX) 3178 DWriteContext_Close(s_dwc); 3179 DWrite_Final(); 3180 s_dwc = NULL; 3181 #endif 3182 3183 ReleaseDC(s_textArea, s_hdc); 3184 DeleteObject(s_brush); 3185 3186 #ifdef FEAT_TEAROFF 3187 /* Unload the tearoff bitmap */ 3188 (void)DeleteObject((HGDIOBJ)s_htearbitmap); 3189 #endif 3190 3191 /* Destroy our window (if we have one). */ 3192 if (s_hwnd != NULL) 3193 { 3194 destroying = TRUE; /* ignore WM_DESTROY message now */ 3195 DestroyWindow(s_hwnd); 3196 } 3197 3198 #ifdef GLOBAL_IME 3199 global_ime_end(); 3200 #endif 3201 } 3202 3203 static char_u * 3204 logfont2name(LOGFONT lf) 3205 { 3206 char *p; 3207 char *res; 3208 char *charset_name; 3209 char *quality_name; 3210 char *font_name = lf.lfFaceName; 3211 3212 charset_name = charset_id2name((int)lf.lfCharSet); 3213 /* Convert a font name from the current codepage to 'encoding'. 3214 * TODO: Use Wide APIs (including LOGFONTW) instead of ANSI APIs. */ 3215 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage) 3216 { 3217 int len; 3218 acp_to_enc((char_u *)lf.lfFaceName, (int)strlen(lf.lfFaceName), 3219 (char_u **)&font_name, &len); 3220 } 3221 quality_name = quality_id2name((int)lf.lfQuality); 3222 3223 res = (char *)alloc((unsigned)(strlen(font_name) + 20 3224 + (charset_name == NULL ? 0 : strlen(charset_name) + 2))); 3225 if (res != NULL) 3226 { 3227 p = res; 3228 /* make a normal font string out of the lf thing:*/ 3229 sprintf((char *)p, "%s:h%d", font_name, pixels_to_points( 3230 lf.lfHeight < 0 ? -lf.lfHeight : lf.lfHeight, TRUE)); 3231 while (*p) 3232 { 3233 if (*p == ' ') 3234 *p = '_'; 3235 ++p; 3236 } 3237 if (lf.lfItalic) 3238 STRCAT(p, ":i"); 3239 if (lf.lfWeight >= FW_BOLD) 3240 STRCAT(p, ":b"); 3241 if (lf.lfUnderline) 3242 STRCAT(p, ":u"); 3243 if (lf.lfStrikeOut) 3244 STRCAT(p, ":s"); 3245 if (charset_name != NULL) 3246 { 3247 STRCAT(p, ":c"); 3248 STRCAT(p, charset_name); 3249 } 3250 if (quality_name != NULL) 3251 { 3252 STRCAT(p, ":q"); 3253 STRCAT(p, quality_name); 3254 } 3255 } 3256 3257 if (font_name != lf.lfFaceName) 3258 vim_free(font_name); 3259 return (char_u *)res; 3260 } 3261 3262 3263 #ifdef FEAT_MBYTE_IME 3264 /* 3265 * Set correct LOGFONT to IME. Use 'guifontwide' if available, otherwise use 3266 * 'guifont' 3267 */ 3268 static void 3269 update_im_font(void) 3270 { 3271 LOGFONT lf_wide; 3272 3273 if (p_guifontwide != NULL && *p_guifontwide != NUL 3274 && gui.wide_font != NOFONT 3275 && GetObject((HFONT)gui.wide_font, sizeof(lf_wide), &lf_wide)) 3276 norm_logfont = lf_wide; 3277 else 3278 norm_logfont = sub_logfont; 3279 im_set_font(&norm_logfont); 3280 } 3281 #endif 3282 3283 /* 3284 * Handler of gui.wide_font (p_guifontwide) changed notification. 3285 */ 3286 void 3287 gui_mch_wide_font_changed(void) 3288 { 3289 LOGFONT lf; 3290 3291 #ifdef FEAT_MBYTE_IME 3292 update_im_font(); 3293 #endif 3294 3295 gui_mch_free_font(gui.wide_ital_font); 3296 gui.wide_ital_font = NOFONT; 3297 gui_mch_free_font(gui.wide_bold_font); 3298 gui.wide_bold_font = NOFONT; 3299 gui_mch_free_font(gui.wide_boldital_font); 3300 gui.wide_boldital_font = NOFONT; 3301 3302 if (gui.wide_font 3303 && GetObject((HFONT)gui.wide_font, sizeof(lf), &lf)) 3304 { 3305 if (!lf.lfItalic) 3306 { 3307 lf.lfItalic = TRUE; 3308 gui.wide_ital_font = get_font_handle(&lf); 3309 lf.lfItalic = FALSE; 3310 } 3311 if (lf.lfWeight < FW_BOLD) 3312 { 3313 lf.lfWeight = FW_BOLD; 3314 gui.wide_bold_font = get_font_handle(&lf); 3315 if (!lf.lfItalic) 3316 { 3317 lf.lfItalic = TRUE; 3318 gui.wide_boldital_font = get_font_handle(&lf); 3319 } 3320 } 3321 } 3322 } 3323 3324 /* 3325 * Initialise vim to use the font with the given name. 3326 * Return FAIL if the font could not be loaded, OK otherwise. 3327 */ 3328 int 3329 gui_mch_init_font(char_u *font_name, int fontset UNUSED) 3330 { 3331 LOGFONT lf; 3332 GuiFont font = NOFONT; 3333 char_u *p; 3334 3335 /* Load the font */ 3336 if (get_logfont(&lf, font_name, NULL, TRUE) == OK) 3337 font = get_font_handle(&lf); 3338 if (font == NOFONT) 3339 return FAIL; 3340 3341 if (font_name == NULL) 3342 font_name = (char_u *)lf.lfFaceName; 3343 #if defined(FEAT_MBYTE_IME) || defined(GLOBAL_IME) 3344 norm_logfont = lf; 3345 #endif 3346 #ifdef FEAT_MBYTE_IME 3347 sub_logfont = lf; 3348 #endif 3349 #ifdef FEAT_MBYTE_IME 3350 update_im_font(); 3351 #endif 3352 gui_mch_free_font(gui.norm_font); 3353 gui.norm_font = font; 3354 current_font_height = lf.lfHeight; 3355 GetFontSize(font); 3356 3357 p = logfont2name(lf); 3358 if (p != NULL) 3359 { 3360 hl_set_font_name(p); 3361 3362 /* When setting 'guifont' to "*" replace it with the actual font name. 3363 * */ 3364 if (STRCMP(font_name, "*") == 0 && STRCMP(p_guifont, "*") == 0) 3365 { 3366 vim_free(p_guifont); 3367 p_guifont = p; 3368 } 3369 else 3370 vim_free(p); 3371 } 3372 3373 gui_mch_free_font(gui.ital_font); 3374 gui.ital_font = NOFONT; 3375 gui_mch_free_font(gui.bold_font); 3376 gui.bold_font = NOFONT; 3377 gui_mch_free_font(gui.boldital_font); 3378 gui.boldital_font = NOFONT; 3379 3380 if (!lf.lfItalic) 3381 { 3382 lf.lfItalic = TRUE; 3383 gui.ital_font = get_font_handle(&lf); 3384 lf.lfItalic = FALSE; 3385 } 3386 if (lf.lfWeight < FW_BOLD) 3387 { 3388 lf.lfWeight = FW_BOLD; 3389 gui.bold_font = get_font_handle(&lf); 3390 if (!lf.lfItalic) 3391 { 3392 lf.lfItalic = TRUE; 3393 gui.boldital_font = get_font_handle(&lf); 3394 } 3395 } 3396 3397 return OK; 3398 } 3399 3400 #ifndef WPF_RESTORETOMAXIMIZED 3401 # define WPF_RESTORETOMAXIMIZED 2 /* just in case someone doesn't have it */ 3402 #endif 3403 3404 /* 3405 * Return TRUE if the GUI window is maximized, filling the whole screen. 3406 */ 3407 int 3408 gui_mch_maximized(void) 3409 { 3410 WINDOWPLACEMENT wp; 3411 3412 wp.length = sizeof(WINDOWPLACEMENT); 3413 if (GetWindowPlacement(s_hwnd, &wp)) 3414 return wp.showCmd == SW_SHOWMAXIMIZED 3415 || (wp.showCmd == SW_SHOWMINIMIZED 3416 && wp.flags == WPF_RESTORETOMAXIMIZED); 3417 3418 return 0; 3419 } 3420 3421 /* 3422 * Called when the font changed while the window is maximized or GO_KEEPWINSIZE 3423 * is set. Compute the new Rows and Columns. This is like resizing the 3424 * window. 3425 */ 3426 void 3427 gui_mch_newfont(void) 3428 { 3429 RECT rect; 3430 3431 GetWindowRect(s_hwnd, &rect); 3432 if (win_socket_id == 0) 3433 { 3434 gui_resize_shell(rect.right - rect.left 3435 - (GetSystemMetrics(SM_CXFRAME) + 3436 GetSystemMetrics(SM_CXPADDEDBORDER)) * 2, 3437 rect.bottom - rect.top 3438 - (GetSystemMetrics(SM_CYFRAME) + 3439 GetSystemMetrics(SM_CXPADDEDBORDER)) * 2 3440 - GetSystemMetrics(SM_CYCAPTION) 3441 #ifdef FEAT_MENU 3442 - gui_mswin_get_menu_height(FALSE) 3443 #endif 3444 ); 3445 } 3446 else 3447 { 3448 /* Inside another window, don't use the frame and border. */ 3449 gui_resize_shell(rect.right - rect.left, 3450 rect.bottom - rect.top 3451 #ifdef FEAT_MENU 3452 - gui_mswin_get_menu_height(FALSE) 3453 #endif 3454 ); 3455 } 3456 } 3457 3458 /* 3459 * Set the window title 3460 */ 3461 void 3462 gui_mch_settitle( 3463 char_u *title, 3464 char_u *icon UNUSED) 3465 { 3466 set_window_title(s_hwnd, (title == NULL ? "VIM" : (char *)title)); 3467 } 3468 3469 #if defined(FEAT_MOUSESHAPE) || defined(PROTO) 3470 /* Table for shape IDCs. Keep in sync with the mshape_names[] table in 3471 * misc2.c! */ 3472 static LPCSTR mshape_idcs[] = 3473 { 3474 IDC_ARROW, /* arrow */ 3475 MAKEINTRESOURCE(0), /* blank */ 3476 IDC_IBEAM, /* beam */ 3477 IDC_SIZENS, /* updown */ 3478 IDC_SIZENS, /* udsizing */ 3479 IDC_SIZEWE, /* leftright */ 3480 IDC_SIZEWE, /* lrsizing */ 3481 IDC_WAIT, /* busy */ 3482 IDC_NO, /* no */ 3483 IDC_ARROW, /* crosshair */ 3484 IDC_ARROW, /* hand1 */ 3485 IDC_ARROW, /* hand2 */ 3486 IDC_ARROW, /* pencil */ 3487 IDC_ARROW, /* question */ 3488 IDC_ARROW, /* right-arrow */ 3489 IDC_UPARROW, /* up-arrow */ 3490 IDC_ARROW /* last one */ 3491 }; 3492 3493 void 3494 mch_set_mouse_shape(int shape) 3495 { 3496 LPCSTR idc; 3497 3498 if (shape == MSHAPE_HIDE) 3499 ShowCursor(FALSE); 3500 else 3501 { 3502 if (shape >= MSHAPE_NUMBERED) 3503 idc = IDC_ARROW; 3504 else 3505 idc = mshape_idcs[shape]; 3506 #ifdef SetClassLongPtr 3507 SetClassLongPtr(s_textArea, GCLP_HCURSOR, (__int3264)(LONG_PTR)LoadCursor(NULL, idc)); 3508 #else 3509 SetClassLong(s_textArea, GCL_HCURSOR, (long_u)LoadCursor(NULL, idc)); 3510 #endif 3511 if (!p_mh) 3512 { 3513 POINT mp; 3514 3515 /* Set the position to make it redrawn with the new shape. */ 3516 (void)GetCursorPos((LPPOINT)&mp); 3517 (void)SetCursorPos(mp.x, mp.y); 3518 ShowCursor(TRUE); 3519 } 3520 } 3521 } 3522 #endif 3523 3524 #if defined(FEAT_BROWSE) || defined(PROTO) 3525 /* 3526 * Wide version of convert_filter(). 3527 */ 3528 static WCHAR * 3529 convert_filterW(char_u *s) 3530 { 3531 char_u *tmp; 3532 int len; 3533 WCHAR *res; 3534 3535 tmp = convert_filter(s); 3536 if (tmp == NULL) 3537 return NULL; 3538 len = (int)STRLEN(s) + 3; 3539 res = enc_to_utf16(tmp, &len); 3540 vim_free(tmp); 3541 return res; 3542 } 3543 3544 /* 3545 * Pop open a file browser and return the file selected, in allocated memory, 3546 * or NULL if Cancel is hit. 3547 * saving - TRUE if the file will be saved to, FALSE if it will be opened. 3548 * title - Title message for the file browser dialog. 3549 * dflt - Default name of file. 3550 * ext - Default extension to be added to files without extensions. 3551 * initdir - directory in which to open the browser (NULL = current dir) 3552 * filter - Filter for matched files to choose from. 3553 */ 3554 char_u * 3555 gui_mch_browse( 3556 int saving, 3557 char_u *title, 3558 char_u *dflt, 3559 char_u *ext, 3560 char_u *initdir, 3561 char_u *filter) 3562 { 3563 /* We always use the wide function. This means enc_to_utf16() must work, 3564 * otherwise it fails miserably! */ 3565 OPENFILENAMEW fileStruct; 3566 WCHAR fileBuf[MAXPATHL]; 3567 WCHAR *wp; 3568 int i; 3569 WCHAR *titlep = NULL; 3570 WCHAR *extp = NULL; 3571 WCHAR *initdirp = NULL; 3572 WCHAR *filterp; 3573 char_u *p, *q; 3574 3575 if (dflt == NULL) 3576 fileBuf[0] = NUL; 3577 else 3578 { 3579 wp = enc_to_utf16(dflt, NULL); 3580 if (wp == NULL) 3581 fileBuf[0] = NUL; 3582 else 3583 { 3584 for (i = 0; wp[i] != NUL && i < MAXPATHL - 1; ++i) 3585 fileBuf[i] = wp[i]; 3586 fileBuf[i] = NUL; 3587 vim_free(wp); 3588 } 3589 } 3590 3591 /* Convert the filter to Windows format. */ 3592 filterp = convert_filterW(filter); 3593 3594 vim_memset(&fileStruct, 0, sizeof(OPENFILENAMEW)); 3595 # ifdef OPENFILENAME_SIZE_VERSION_400W 3596 /* be compatible with Windows NT 4.0 */ 3597 fileStruct.lStructSize = OPENFILENAME_SIZE_VERSION_400W; 3598 # else 3599 fileStruct.lStructSize = sizeof(fileStruct); 3600 # endif 3601 3602 if (title != NULL) 3603 titlep = enc_to_utf16(title, NULL); 3604 fileStruct.lpstrTitle = titlep; 3605 3606 if (ext != NULL) 3607 extp = enc_to_utf16(ext, NULL); 3608 fileStruct.lpstrDefExt = extp; 3609 3610 fileStruct.lpstrFile = fileBuf; 3611 fileStruct.nMaxFile = MAXPATHL; 3612 fileStruct.lpstrFilter = filterp; 3613 fileStruct.hwndOwner = s_hwnd; /* main Vim window is owner*/ 3614 /* has an initial dir been specified? */ 3615 if (initdir != NULL && *initdir != NUL) 3616 { 3617 /* Must have backslashes here, no matter what 'shellslash' says */ 3618 initdirp = enc_to_utf16(initdir, NULL); 3619 if (initdirp != NULL) 3620 { 3621 for (wp = initdirp; *wp != NUL; ++wp) 3622 if (*wp == '/') 3623 *wp = '\\'; 3624 } 3625 fileStruct.lpstrInitialDir = initdirp; 3626 } 3627 3628 /* 3629 * TODO: Allow selection of multiple files. Needs another arg to this 3630 * function to ask for it, and need to use OFN_ALLOWMULTISELECT below. 3631 * Also, should we use OFN_FILEMUSTEXIST when opening? Vim can edit on 3632 * files that don't exist yet, so I haven't put it in. What about 3633 * OFN_PATHMUSTEXIST? 3634 * Don't use OFN_OVERWRITEPROMPT, Vim has its own ":confirm" dialog. 3635 */ 3636 fileStruct.Flags = (OFN_NOCHANGEDIR | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY); 3637 # ifdef FEAT_SHORTCUT 3638 if (curbuf->b_p_bin) 3639 fileStruct.Flags |= OFN_NODEREFERENCELINKS; 3640 # endif 3641 if (saving) 3642 { 3643 if (!GetSaveFileNameW(&fileStruct)) 3644 return NULL; 3645 } 3646 else 3647 { 3648 if (!GetOpenFileNameW(&fileStruct)) 3649 return NULL; 3650 } 3651 3652 vim_free(filterp); 3653 vim_free(initdirp); 3654 vim_free(titlep); 3655 vim_free(extp); 3656 3657 /* Convert from UCS2 to 'encoding'. */ 3658 p = utf16_to_enc(fileBuf, NULL); 3659 if (p == NULL) 3660 return NULL; 3661 3662 /* Give focus back to main window (when using MDI). */ 3663 SetFocus(s_hwnd); 3664 3665 /* Shorten the file name if possible */ 3666 q = vim_strsave(shorten_fname1(p)); 3667 vim_free(p); 3668 return q; 3669 } 3670 3671 3672 /* 3673 * Convert the string s to the proper format for a filter string by replacing 3674 * the \t and \n delimiters with \0. 3675 * Returns the converted string in allocated memory. 3676 * 3677 * Keep in sync with convert_filterW() above! 3678 */ 3679 static char_u * 3680 convert_filter(char_u *s) 3681 { 3682 char_u *res; 3683 unsigned s_len = (unsigned)STRLEN(s); 3684 unsigned i; 3685 3686 res = alloc(s_len + 3); 3687 if (res != NULL) 3688 { 3689 for (i = 0; i < s_len; ++i) 3690 if (s[i] == '\t' || s[i] == '\n') 3691 res[i] = '\0'; 3692 else 3693 res[i] = s[i]; 3694 res[s_len] = NUL; 3695 /* Add two extra NULs to make sure it's properly terminated. */ 3696 res[s_len + 1] = NUL; 3697 res[s_len + 2] = NUL; 3698 } 3699 return res; 3700 } 3701 3702 /* 3703 * Select a directory. 3704 */ 3705 char_u * 3706 gui_mch_browsedir(char_u *title, char_u *initdir) 3707 { 3708 /* We fake this: Use a filter that doesn't select anything and a default 3709 * file name that won't be used. */ 3710 return gui_mch_browse(0, title, (char_u *)_("Not Used"), NULL, 3711 initdir, (char_u *)_("Directory\t*.nothing\n")); 3712 } 3713 #endif /* FEAT_BROWSE */ 3714 3715 static void 3716 _OnDropFiles( 3717 HWND hwnd UNUSED, 3718 HDROP hDrop) 3719 { 3720 #define BUFPATHLEN _MAX_PATH 3721 #define DRAGQVAL 0xFFFFFFFF 3722 WCHAR wszFile[BUFPATHLEN]; 3723 char szFile[BUFPATHLEN]; 3724 UINT cFiles = DragQueryFile(hDrop, DRAGQVAL, NULL, 0); 3725 UINT i; 3726 char_u **fnames; 3727 POINT pt; 3728 int_u modifiers = 0; 3729 3730 /* TRACE("_OnDropFiles: %d files dropped\n", cFiles); */ 3731 3732 /* Obtain dropped position */ 3733 DragQueryPoint(hDrop, &pt); 3734 MapWindowPoints(s_hwnd, s_textArea, &pt, 1); 3735 3736 reset_VIsual(); 3737 3738 fnames = (char_u **)alloc(cFiles * sizeof(char_u *)); 3739 3740 if (fnames != NULL) 3741 for (i = 0; i < cFiles; ++i) 3742 { 3743 if (DragQueryFileW(hDrop, i, wszFile, BUFPATHLEN) > 0) 3744 fnames[i] = utf16_to_enc(wszFile, NULL); 3745 else 3746 { 3747 DragQueryFile(hDrop, i, szFile, BUFPATHLEN); 3748 fnames[i] = vim_strsave((char_u *)szFile); 3749 } 3750 } 3751 3752 DragFinish(hDrop); 3753 3754 if (fnames != NULL) 3755 { 3756 if ((GetKeyState(VK_SHIFT) & 0x8000) != 0) 3757 modifiers |= MOUSE_SHIFT; 3758 if ((GetKeyState(VK_CONTROL) & 0x8000) != 0) 3759 modifiers |= MOUSE_CTRL; 3760 if ((GetKeyState(VK_MENU) & 0x8000) != 0) 3761 modifiers |= MOUSE_ALT; 3762 3763 gui_handle_drop(pt.x, pt.y, modifiers, fnames, cFiles); 3764 3765 s_need_activate = TRUE; 3766 } 3767 } 3768 3769 static int 3770 _OnScroll( 3771 HWND hwnd UNUSED, 3772 HWND hwndCtl, 3773 UINT code, 3774 int pos) 3775 { 3776 static UINT prev_code = 0; /* code of previous call */ 3777 scrollbar_T *sb, *sb_info; 3778 long val; 3779 int dragging = FALSE; 3780 int dont_scroll_save = dont_scroll; 3781 SCROLLINFO si; 3782 3783 si.cbSize = sizeof(si); 3784 si.fMask = SIF_POS; 3785 3786 sb = gui_mswin_find_scrollbar(hwndCtl); 3787 if (sb == NULL) 3788 return 0; 3789 3790 if (sb->wp != NULL) /* Left or right scrollbar */ 3791 { 3792 /* 3793 * Careful: need to get scrollbar info out of first (left) scrollbar 3794 * for window, but keep real scrollbar too because we must pass it to 3795 * gui_drag_scrollbar(). 3796 */ 3797 sb_info = &sb->wp->w_scrollbars[0]; 3798 } 3799 else /* Bottom scrollbar */ 3800 sb_info = sb; 3801 val = sb_info->value; 3802 3803 switch (code) 3804 { 3805 case SB_THUMBTRACK: 3806 val = pos; 3807 dragging = TRUE; 3808 if (sb->scroll_shift > 0) 3809 val <<= sb->scroll_shift; 3810 break; 3811 case SB_LINEDOWN: 3812 val++; 3813 break; 3814 case SB_LINEUP: 3815 val--; 3816 break; 3817 case SB_PAGEDOWN: 3818 val += (sb_info->size > 2 ? sb_info->size - 2 : 1); 3819 break; 3820 case SB_PAGEUP: 3821 val -= (sb_info->size > 2 ? sb_info->size - 2 : 1); 3822 break; 3823 case SB_TOP: 3824 val = 0; 3825 break; 3826 case SB_BOTTOM: 3827 val = sb_info->max; 3828 break; 3829 case SB_ENDSCROLL: 3830 if (prev_code == SB_THUMBTRACK) 3831 { 3832 /* 3833 * "pos" only gives us 16-bit data. In case of large file, 3834 * use GetScrollPos() which returns 32-bit. Unfortunately it 3835 * is not valid while the scrollbar is being dragged. 3836 */ 3837 val = GetScrollPos(hwndCtl, SB_CTL); 3838 if (sb->scroll_shift > 0) 3839 val <<= sb->scroll_shift; 3840 } 3841 break; 3842 3843 default: 3844 /* TRACE("Unknown scrollbar event %d\n", code); */ 3845 return 0; 3846 } 3847 prev_code = code; 3848 3849 si.nPos = (sb->scroll_shift > 0) ? val >> sb->scroll_shift : val; 3850 SetScrollInfo(hwndCtl, SB_CTL, &si, TRUE); 3851 3852 /* 3853 * When moving a vertical scrollbar, move the other vertical scrollbar too. 3854 */ 3855 if (sb->wp != NULL) 3856 { 3857 scrollbar_T *sba = sb->wp->w_scrollbars; 3858 HWND id = sba[ (sb == sba + SBAR_LEFT) ? SBAR_RIGHT : SBAR_LEFT].id; 3859 3860 SetScrollInfo(id, SB_CTL, &si, TRUE); 3861 } 3862 3863 /* Don't let us be interrupted here by another message. */ 3864 s_busy_processing = TRUE; 3865 3866 /* When "allow_scrollbar" is FALSE still need to remember the new 3867 * position, but don't actually scroll by setting "dont_scroll". */ 3868 dont_scroll = !allow_scrollbar; 3869 3870 mch_disable_flush(); 3871 gui_drag_scrollbar(sb, val, dragging); 3872 mch_enable_flush(); 3873 gui_may_flush(); 3874 3875 s_busy_processing = FALSE; 3876 dont_scroll = dont_scroll_save; 3877 3878 return 0; 3879 } 3880 3881 3882 #ifdef FEAT_XPM_W32 3883 # include "xpm_w32.h" 3884 #endif 3885 3886 #ifdef PROTO 3887 # define WINAPI 3888 #endif 3889 3890 #ifdef __MINGW32__ 3891 /* 3892 * Add a lot of missing defines. 3893 * They are not always missing, we need the #ifndef's. 3894 */ 3895 # ifndef _cdecl 3896 # define _cdecl 3897 # endif 3898 # ifndef IsMinimized 3899 # define IsMinimized(hwnd) IsIconic(hwnd) 3900 # endif 3901 # ifndef IsMaximized 3902 # define IsMaximized(hwnd) IsZoomed(hwnd) 3903 # endif 3904 # ifndef SelectFont 3905 # define SelectFont(hdc, hfont) ((HFONT)SelectObject((hdc), (HGDIOBJ)(HFONT)(hfont))) 3906 # endif 3907 # ifndef GetStockBrush 3908 # define GetStockBrush(i) ((HBRUSH)GetStockObject(i)) 3909 # endif 3910 # ifndef DeleteBrush 3911 # define DeleteBrush(hbr) DeleteObject((HGDIOBJ)(HBRUSH)(hbr)) 3912 # endif 3913 3914 # ifndef HANDLE_WM_RBUTTONDBLCLK 3915 # define HANDLE_WM_RBUTTONDBLCLK(hwnd, wParam, lParam, fn) \ 3916 ((fn)((hwnd), TRUE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L) 3917 # endif 3918 # ifndef HANDLE_WM_MBUTTONUP 3919 # define HANDLE_WM_MBUTTONUP(hwnd, wParam, lParam, fn) \ 3920 ((fn)((hwnd), (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L) 3921 # endif 3922 # ifndef HANDLE_WM_MBUTTONDBLCLK 3923 # define HANDLE_WM_MBUTTONDBLCLK(hwnd, wParam, lParam, fn) \ 3924 ((fn)((hwnd), TRUE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L) 3925 # endif 3926 # ifndef HANDLE_WM_LBUTTONDBLCLK 3927 # define HANDLE_WM_LBUTTONDBLCLK(hwnd, wParam, lParam, fn) \ 3928 ((fn)((hwnd), TRUE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L) 3929 # endif 3930 # ifndef HANDLE_WM_RBUTTONDOWN 3931 # define HANDLE_WM_RBUTTONDOWN(hwnd, wParam, lParam, fn) \ 3932 ((fn)((hwnd), FALSE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L) 3933 # endif 3934 # ifndef HANDLE_WM_MOUSEMOVE 3935 # define HANDLE_WM_MOUSEMOVE(hwnd, wParam, lParam, fn) \ 3936 ((fn)((hwnd), (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L) 3937 # endif 3938 # ifndef HANDLE_WM_RBUTTONUP 3939 # define HANDLE_WM_RBUTTONUP(hwnd, wParam, lParam, fn) \ 3940 ((fn)((hwnd), (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L) 3941 # endif 3942 # ifndef HANDLE_WM_MBUTTONDOWN 3943 # define HANDLE_WM_MBUTTONDOWN(hwnd, wParam, lParam, fn) \ 3944 ((fn)((hwnd), FALSE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L) 3945 # endif 3946 # ifndef HANDLE_WM_LBUTTONUP 3947 # define HANDLE_WM_LBUTTONUP(hwnd, wParam, lParam, fn) \ 3948 ((fn)((hwnd), (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L) 3949 # endif 3950 # ifndef HANDLE_WM_LBUTTONDOWN 3951 # define HANDLE_WM_LBUTTONDOWN(hwnd, wParam, lParam, fn) \ 3952 ((fn)((hwnd), FALSE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L) 3953 # endif 3954 # ifndef HANDLE_WM_SYSCHAR 3955 # define HANDLE_WM_SYSCHAR(hwnd, wParam, lParam, fn) \ 3956 ((fn)((hwnd), (TCHAR)(wParam), (int)(short)LOWORD(lParam)), 0L) 3957 # endif 3958 # ifndef HANDLE_WM_ACTIVATEAPP 3959 # define HANDLE_WM_ACTIVATEAPP(hwnd, wParam, lParam, fn) \ 3960 ((fn)((hwnd), (BOOL)(wParam), (DWORD)(lParam)), 0L) 3961 # endif 3962 # ifndef HANDLE_WM_WINDOWPOSCHANGING 3963 # define HANDLE_WM_WINDOWPOSCHANGING(hwnd, wParam, lParam, fn) \ 3964 (LRESULT)(DWORD)(BOOL)(fn)((hwnd), (LPWINDOWPOS)(lParam)) 3965 # endif 3966 # ifndef HANDLE_WM_VSCROLL 3967 # define HANDLE_WM_VSCROLL(hwnd, wParam, lParam, fn) \ 3968 ((fn)((hwnd), (HWND)(lParam), (UINT)(LOWORD(wParam)), (int)(short)HIWORD(wParam)), 0L) 3969 # endif 3970 # ifndef HANDLE_WM_SETFOCUS 3971 # define HANDLE_WM_SETFOCUS(hwnd, wParam, lParam, fn) \ 3972 ((fn)((hwnd), (HWND)(wParam)), 0L) 3973 # endif 3974 # ifndef HANDLE_WM_KILLFOCUS 3975 # define HANDLE_WM_KILLFOCUS(hwnd, wParam, lParam, fn) \ 3976 ((fn)((hwnd), (HWND)(wParam)), 0L) 3977 # endif 3978 # ifndef HANDLE_WM_HSCROLL 3979 # define HANDLE_WM_HSCROLL(hwnd, wParam, lParam, fn) \ 3980 ((fn)((hwnd), (HWND)(lParam), (UINT)(LOWORD(wParam)), (int)(short)HIWORD(wParam)), 0L) 3981 # endif 3982 # ifndef HANDLE_WM_DROPFILES 3983 # define HANDLE_WM_DROPFILES(hwnd, wParam, lParam, fn) \ 3984 ((fn)((hwnd), (HDROP)(wParam)), 0L) 3985 # endif 3986 # ifndef HANDLE_WM_CHAR 3987 # define HANDLE_WM_CHAR(hwnd, wParam, lParam, fn) \ 3988 ((fn)((hwnd), (TCHAR)(wParam), (int)(short)LOWORD(lParam)), 0L) 3989 # endif 3990 # ifndef HANDLE_WM_SYSDEADCHAR 3991 # define HANDLE_WM_SYSDEADCHAR(hwnd, wParam, lParam, fn) \ 3992 ((fn)((hwnd), (TCHAR)(wParam), (int)(short)LOWORD(lParam)), 0L) 3993 # endif 3994 # ifndef HANDLE_WM_DEADCHAR 3995 # define HANDLE_WM_DEADCHAR(hwnd, wParam, lParam, fn) \ 3996 ((fn)((hwnd), (TCHAR)(wParam), (int)(short)LOWORD(lParam)), 0L) 3997 # endif 3998 #endif /* __MINGW32__ */ 3999 4000 4001 /* Some parameters for tearoff menus. All in pixels. */ 4002 #define TEAROFF_PADDING_X 2 4003 #define TEAROFF_BUTTON_PAD_X 8 4004 #define TEAROFF_MIN_WIDTH 200 4005 #define TEAROFF_SUBMENU_LABEL ">>" 4006 #define TEAROFF_COLUMN_PADDING 3 // # spaces to pad column with. 4007 4008 4009 /* For the Intellimouse: */ 4010 #ifndef WM_MOUSEWHEEL 4011 #define WM_MOUSEWHEEL 0x20a 4012 #endif 4013 4014 4015 #ifdef FEAT_BEVAL_GUI 4016 # define ID_BEVAL_TOOLTIP 200 4017 # define BEVAL_TEXT_LEN MAXPATHL 4018 4019 #if (defined(_MSC_VER) && _MSC_VER < 1300) || !defined(MAXULONG_PTR) 4020 /* Work around old versions of basetsd.h which wrongly declares 4021 * UINT_PTR as unsigned long. */ 4022 # undef UINT_PTR 4023 # define UINT_PTR UINT 4024 #endif 4025 4026 static BalloonEval *cur_beval = NULL; 4027 static UINT_PTR BevalTimerId = 0; 4028 static DWORD LastActivity = 0; 4029 4030 4031 /* cproto fails on missing include files */ 4032 #ifndef PROTO 4033 4034 /* 4035 * excerpts from headers since this may not be presented 4036 * in the extremely old compilers 4037 */ 4038 # include <pshpack1.h> 4039 4040 #endif 4041 4042 typedef struct _DllVersionInfo 4043 { 4044 DWORD cbSize; 4045 DWORD dwMajorVersion; 4046 DWORD dwMinorVersion; 4047 DWORD dwBuildNumber; 4048 DWORD dwPlatformID; 4049 } DLLVERSIONINFO; 4050 4051 #ifndef PROTO 4052 # include <poppack.h> 4053 #endif 4054 4055 typedef struct tagTOOLINFOA_NEW 4056 { 4057 UINT cbSize; 4058 UINT uFlags; 4059 HWND hwnd; 4060 UINT_PTR uId; 4061 RECT rect; 4062 HINSTANCE hinst; 4063 LPSTR lpszText; 4064 LPARAM lParam; 4065 } TOOLINFO_NEW; 4066 4067 typedef struct tagNMTTDISPINFO_NEW 4068 { 4069 NMHDR hdr; 4070 LPSTR lpszText; 4071 char szText[80]; 4072 HINSTANCE hinst; 4073 UINT uFlags; 4074 LPARAM lParam; 4075 } NMTTDISPINFO_NEW; 4076 4077 typedef struct tagTOOLINFOW_NEW 4078 { 4079 UINT cbSize; 4080 UINT uFlags; 4081 HWND hwnd; 4082 UINT_PTR uId; 4083 RECT rect; 4084 HINSTANCE hinst; 4085 LPWSTR lpszText; 4086 LPARAM lParam; 4087 void *lpReserved; 4088 } TOOLINFOW_NEW; 4089 4090 typedef struct tagNMTTDISPINFOW_NEW 4091 { 4092 NMHDR hdr; 4093 LPWSTR lpszText; 4094 WCHAR szText[80]; 4095 HINSTANCE hinst; 4096 UINT uFlags; 4097 LPARAM lParam; 4098 } NMTTDISPINFOW_NEW; 4099 4100 4101 typedef HRESULT (WINAPI* DLLGETVERSIONPROC)(DLLVERSIONINFO *); 4102 #ifndef TTM_SETMAXTIPWIDTH 4103 # define TTM_SETMAXTIPWIDTH (WM_USER+24) 4104 #endif 4105 4106 #ifndef TTF_DI_SETITEM 4107 # define TTF_DI_SETITEM 0x8000 4108 #endif 4109 4110 #ifndef TTN_GETDISPINFO 4111 # define TTN_GETDISPINFO (TTN_FIRST - 0) 4112 #endif 4113 4114 #endif /* defined(FEAT_BEVAL_GUI) */ 4115 4116 #if defined(FEAT_TOOLBAR) || defined(FEAT_GUI_TABLINE) 4117 /* Older MSVC compilers don't have LPNMTTDISPINFO[AW] thus we need to define 4118 * it here if LPNMTTDISPINFO isn't defined. 4119 * MingW doesn't define LPNMTTDISPINFO but typedefs it. Thus we need to check 4120 * _MSC_VER. */ 4121 # if !defined(LPNMTTDISPINFO) && defined(_MSC_VER) 4122 typedef struct tagNMTTDISPINFOA { 4123 NMHDR hdr; 4124 LPSTR lpszText; 4125 char szText[80]; 4126 HINSTANCE hinst; 4127 UINT uFlags; 4128 LPARAM lParam; 4129 } NMTTDISPINFOA, *LPNMTTDISPINFOA; 4130 # define LPNMTTDISPINFO LPNMTTDISPINFOA 4131 4132 typedef struct tagNMTTDISPINFOW { 4133 NMHDR hdr; 4134 LPWSTR lpszText; 4135 WCHAR szText[80]; 4136 HINSTANCE hinst; 4137 UINT uFlags; 4138 LPARAM lParam; 4139 } NMTTDISPINFOW, *LPNMTTDISPINFOW; 4140 # endif 4141 #endif 4142 4143 #ifndef TTN_GETDISPINFOW 4144 # define TTN_GETDISPINFOW (TTN_FIRST - 10) 4145 #endif 4146 4147 /* Local variables: */ 4148 4149 #ifdef FEAT_MENU 4150 static UINT s_menu_id = 100; 4151 #endif 4152 4153 /* 4154 * Use the system font for dialogs and tear-off menus. Remove this line to 4155 * use DLG_FONT_NAME. 4156 */ 4157 #define USE_SYSMENU_FONT 4158 4159 #define VIM_NAME "vim" 4160 #define VIM_CLASS "Vim" 4161 #define VIM_CLASSW L"Vim" 4162 4163 /* Initial size for the dialog template. For gui_mch_dialog() it's fixed, 4164 * thus there should be room for every dialog. For tearoffs it's made bigger 4165 * when needed. */ 4166 #define DLG_ALLOC_SIZE 16 * 1024 4167 4168 /* 4169 * stuff for dialogs, menus, tearoffs etc. 4170 */ 4171 static PWORD 4172 add_dialog_element( 4173 PWORD p, 4174 DWORD lStyle, 4175 WORD x, 4176 WORD y, 4177 WORD w, 4178 WORD h, 4179 WORD Id, 4180 WORD clss, 4181 const char *caption); 4182 static LPWORD lpwAlign(LPWORD); 4183 static int nCopyAnsiToWideChar(LPWORD, LPSTR, BOOL); 4184 #if defined(FEAT_MENU) && defined(FEAT_TEAROFF) 4185 static void gui_mch_tearoff(char_u *title, vimmenu_T *menu, int initX, int initY); 4186 #endif 4187 static void get_dialog_font_metrics(void); 4188 4189 static int dialog_default_button = -1; 4190 4191 /* Intellimouse support */ 4192 static int mouse_scroll_lines = 0; 4193 4194 static int s_usenewlook; /* emulate W95/NT4 non-bold dialogs */ 4195 #ifdef FEAT_TOOLBAR 4196 static void initialise_toolbar(void); 4197 static LRESULT CALLBACK toolbar_wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); 4198 static int get_toolbar_bitmap(vimmenu_T *menu); 4199 #endif 4200 4201 #ifdef FEAT_GUI_TABLINE 4202 static void initialise_tabline(void); 4203 static LRESULT CALLBACK tabline_wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); 4204 #endif 4205 4206 #ifdef FEAT_MBYTE_IME 4207 static LRESULT _OnImeComposition(HWND hwnd, WPARAM dbcs, LPARAM param); 4208 static char_u *GetResultStr(HWND hwnd, int GCS, int *lenp); 4209 #endif 4210 #if defined(FEAT_MBYTE_IME) && defined(DYNAMIC_IME) 4211 # ifdef NOIME 4212 typedef struct tagCOMPOSITIONFORM { 4213 DWORD dwStyle; 4214 POINT ptCurrentPos; 4215 RECT rcArea; 4216 } COMPOSITIONFORM, *PCOMPOSITIONFORM, NEAR *NPCOMPOSITIONFORM, FAR *LPCOMPOSITIONFORM; 4217 typedef HANDLE HIMC; 4218 # endif 4219 4220 static HINSTANCE hLibImm = NULL; 4221 static LONG (WINAPI *pImmGetCompositionStringA)(HIMC, DWORD, LPVOID, DWORD); 4222 static LONG (WINAPI *pImmGetCompositionStringW)(HIMC, DWORD, LPVOID, DWORD); 4223 static HIMC (WINAPI *pImmGetContext)(HWND); 4224 static HIMC (WINAPI *pImmAssociateContext)(HWND, HIMC); 4225 static BOOL (WINAPI *pImmReleaseContext)(HWND, HIMC); 4226 static BOOL (WINAPI *pImmGetOpenStatus)(HIMC); 4227 static BOOL (WINAPI *pImmSetOpenStatus)(HIMC, BOOL); 4228 static BOOL (WINAPI *pImmGetCompositionFont)(HIMC, LPLOGFONTA); 4229 static BOOL (WINAPI *pImmSetCompositionFont)(HIMC, LPLOGFONTA); 4230 static BOOL (WINAPI *pImmSetCompositionWindow)(HIMC, LPCOMPOSITIONFORM); 4231 static BOOL (WINAPI *pImmGetConversionStatus)(HIMC, LPDWORD, LPDWORD); 4232 static BOOL (WINAPI *pImmSetConversionStatus)(HIMC, DWORD, DWORD); 4233 static void dyn_imm_load(void); 4234 #else 4235 # define pImmGetCompositionStringA ImmGetCompositionStringA 4236 # define pImmGetCompositionStringW ImmGetCompositionStringW 4237 # define pImmGetContext ImmGetContext 4238 # define pImmAssociateContext ImmAssociateContext 4239 # define pImmReleaseContext ImmReleaseContext 4240 # define pImmGetOpenStatus ImmGetOpenStatus 4241 # define pImmSetOpenStatus ImmSetOpenStatus 4242 # define pImmGetCompositionFont ImmGetCompositionFontA 4243 # define pImmSetCompositionFont ImmSetCompositionFontA 4244 # define pImmSetCompositionWindow ImmSetCompositionWindow 4245 # define pImmGetConversionStatus ImmGetConversionStatus 4246 # define pImmSetConversionStatus ImmSetConversionStatus 4247 #endif 4248 4249 #ifdef FEAT_MENU 4250 /* 4251 * Figure out how high the menu bar is at the moment. 4252 */ 4253 static int 4254 gui_mswin_get_menu_height( 4255 int fix_window) /* If TRUE, resize window if menu height changed */ 4256 { 4257 static int old_menu_height = -1; 4258 4259 RECT rc1, rc2; 4260 int num; 4261 int menu_height; 4262 4263 if (gui.menu_is_active) 4264 num = GetMenuItemCount(s_menuBar); 4265 else 4266 num = 0; 4267 4268 if (num == 0) 4269 menu_height = 0; 4270 else if (IsMinimized(s_hwnd)) 4271 { 4272 /* The height of the menu cannot be determined while the window is 4273 * minimized. Take the previous height if the menu is changed in that 4274 * state, to avoid that Vim's vertical window size accidentally 4275 * increases due to the unaccounted-for menu height. */ 4276 menu_height = old_menu_height == -1 ? 0 : old_menu_height; 4277 } 4278 else 4279 { 4280 /* 4281 * In case 'lines' is set in _vimrc/_gvimrc window width doesn't 4282 * seem to have been set yet, so menu wraps in default window 4283 * width which is very narrow. Instead just return height of a 4284 * single menu item. Will still be wrong when the menu really 4285 * should wrap over more than one line. 4286 */ 4287 GetMenuItemRect(s_hwnd, s_menuBar, 0, &rc1); 4288 if (gui.starting) 4289 menu_height = rc1.bottom - rc1.top + 1; 4290 else 4291 { 4292 GetMenuItemRect(s_hwnd, s_menuBar, num - 1, &rc2); 4293 menu_height = rc2.bottom - rc1.top + 1; 4294 } 4295 } 4296 4297 if (fix_window && menu_height != old_menu_height) 4298 { 4299 gui_set_shellsize(FALSE, FALSE, RESIZE_VERT); 4300 } 4301 old_menu_height = menu_height; 4302 4303 return menu_height; 4304 } 4305 #endif /*FEAT_MENU*/ 4306 4307 4308 /* 4309 * Setup for the Intellimouse 4310 */ 4311 static void 4312 init_mouse_wheel(void) 4313 { 4314 4315 #ifndef SPI_GETWHEELSCROLLLINES 4316 # define SPI_GETWHEELSCROLLLINES 104 4317 #endif 4318 #ifndef SPI_SETWHEELSCROLLLINES 4319 # define SPI_SETWHEELSCROLLLINES 105 4320 #endif 4321 4322 #define VMOUSEZ_CLASSNAME "MouseZ" /* hidden wheel window class */ 4323 #define VMOUSEZ_TITLE "Magellan MSWHEEL" /* hidden wheel window title */ 4324 #define VMSH_MOUSEWHEEL "MSWHEEL_ROLLMSG" 4325 #define VMSH_SCROLL_LINES "MSH_SCROLL_LINES_MSG" 4326 4327 mouse_scroll_lines = 3; /* reasonable default */ 4328 4329 /* if NT 4.0+ (or Win98) get scroll lines directly from system */ 4330 SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, 4331 &mouse_scroll_lines, 0); 4332 } 4333 4334 4335 /* Intellimouse wheel handler */ 4336 static void 4337 _OnMouseWheel( 4338 HWND hwnd, 4339 short zDelta) 4340 { 4341 /* Treat a mouse wheel event as if it were a scroll request */ 4342 int i; 4343 int size; 4344 HWND hwndCtl; 4345 4346 if (curwin->w_scrollbars[SBAR_RIGHT].id != 0) 4347 { 4348 hwndCtl = curwin->w_scrollbars[SBAR_RIGHT].id; 4349 size = curwin->w_scrollbars[SBAR_RIGHT].size; 4350 } 4351 else if (curwin->w_scrollbars[SBAR_LEFT].id != 0) 4352 { 4353 hwndCtl = curwin->w_scrollbars[SBAR_LEFT].id; 4354 size = curwin->w_scrollbars[SBAR_LEFT].size; 4355 } 4356 else 4357 return; 4358 4359 size = curwin->w_height; 4360 if (mouse_scroll_lines == 0) 4361 init_mouse_wheel(); 4362 4363 mch_disable_flush(); 4364 if (mouse_scroll_lines > 0 4365 && mouse_scroll_lines < (size > 2 ? size - 2 : 1)) 4366 { 4367 for (i = mouse_scroll_lines; i > 0; --i) 4368 _OnScroll(hwnd, hwndCtl, zDelta >= 0 ? SB_LINEUP : SB_LINEDOWN, 0); 4369 } 4370 else 4371 _OnScroll(hwnd, hwndCtl, zDelta >= 0 ? SB_PAGEUP : SB_PAGEDOWN, 0); 4372 mch_enable_flush(); 4373 gui_may_flush(); 4374 } 4375 4376 #ifdef USE_SYSMENU_FONT 4377 /* 4378 * Get Menu Font. 4379 * Return OK or FAIL. 4380 */ 4381 static int 4382 gui_w32_get_menu_font(LOGFONT *lf) 4383 { 4384 NONCLIENTMETRICS nm; 4385 4386 nm.cbSize = sizeof(NONCLIENTMETRICS); 4387 if (!SystemParametersInfo( 4388 SPI_GETNONCLIENTMETRICS, 4389 sizeof(NONCLIENTMETRICS), 4390 &nm, 4391 0)) 4392 return FAIL; 4393 *lf = nm.lfMenuFont; 4394 return OK; 4395 } 4396 #endif 4397 4398 4399 #if defined(FEAT_GUI_TABLINE) && defined(USE_SYSMENU_FONT) 4400 /* 4401 * Set the GUI tabline font to the system menu font 4402 */ 4403 static void 4404 set_tabline_font(void) 4405 { 4406 LOGFONT lfSysmenu; 4407 HFONT font; 4408 HWND hwnd; 4409 HDC hdc; 4410 HFONT hfntOld; 4411 TEXTMETRIC tm; 4412 4413 if (gui_w32_get_menu_font(&lfSysmenu) != OK) 4414 return; 4415 4416 font = CreateFontIndirect(&lfSysmenu); 4417 4418 SendMessage(s_tabhwnd, WM_SETFONT, (WPARAM)font, TRUE); 4419 4420 /* 4421 * Compute the height of the font used for the tab text 4422 */ 4423 hwnd = GetDesktopWindow(); 4424 hdc = GetWindowDC(hwnd); 4425 hfntOld = SelectFont(hdc, font); 4426 4427 GetTextMetrics(hdc, &tm); 4428 4429 SelectFont(hdc, hfntOld); 4430 ReleaseDC(hwnd, hdc); 4431 4432 /* 4433 * The space used by the tab border and the space between the tab label 4434 * and the tab border is included as 7. 4435 */ 4436 gui.tabline_height = tm.tmHeight + tm.tmInternalLeading + 7; 4437 } 4438 #endif 4439 4440 /* 4441 * Invoked when a setting was changed. 4442 */ 4443 static LRESULT CALLBACK 4444 _OnSettingChange(UINT n) 4445 { 4446 if (n == SPI_SETWHEELSCROLLLINES) 4447 SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, 4448 &mouse_scroll_lines, 0); 4449 #if defined(FEAT_GUI_TABLINE) && defined(USE_SYSMENU_FONT) 4450 if (n == SPI_SETNONCLIENTMETRICS) 4451 set_tabline_font(); 4452 #endif 4453 return 0; 4454 } 4455 4456 #ifdef FEAT_NETBEANS_INTG 4457 static void 4458 _OnWindowPosChanged( 4459 HWND hwnd, 4460 const LPWINDOWPOS lpwpos) 4461 { 4462 static int x = 0, y = 0, cx = 0, cy = 0; 4463 extern int WSInitialized; 4464 4465 if (WSInitialized && (lpwpos->x != x || lpwpos->y != y 4466 || lpwpos->cx != cx || lpwpos->cy != cy)) 4467 { 4468 x = lpwpos->x; 4469 y = lpwpos->y; 4470 cx = lpwpos->cx; 4471 cy = lpwpos->cy; 4472 netbeans_frame_moved(x, y); 4473 } 4474 /* Allow to send WM_SIZE and WM_MOVE */ 4475 FORWARD_WM_WINDOWPOSCHANGED(hwnd, lpwpos, MyWindowProc); 4476 } 4477 #endif 4478 4479 static int 4480 _DuringSizing( 4481 UINT fwSide, 4482 LPRECT lprc) 4483 { 4484 int w, h; 4485 int valid_w, valid_h; 4486 int w_offset, h_offset; 4487 4488 w = lprc->right - lprc->left; 4489 h = lprc->bottom - lprc->top; 4490 gui_mswin_get_valid_dimensions(w, h, &valid_w, &valid_h); 4491 w_offset = w - valid_w; 4492 h_offset = h - valid_h; 4493 4494 if (fwSide == WMSZ_LEFT || fwSide == WMSZ_TOPLEFT 4495 || fwSide == WMSZ_BOTTOMLEFT) 4496 lprc->left += w_offset; 4497 else if (fwSide == WMSZ_RIGHT || fwSide == WMSZ_TOPRIGHT 4498 || fwSide == WMSZ_BOTTOMRIGHT) 4499 lprc->right -= w_offset; 4500 4501 if (fwSide == WMSZ_TOP || fwSide == WMSZ_TOPLEFT 4502 || fwSide == WMSZ_TOPRIGHT) 4503 lprc->top += h_offset; 4504 else if (fwSide == WMSZ_BOTTOM || fwSide == WMSZ_BOTTOMLEFT 4505 || fwSide == WMSZ_BOTTOMRIGHT) 4506 lprc->bottom -= h_offset; 4507 return TRUE; 4508 } 4509 4510 4511 4512 static LRESULT CALLBACK 4513 _WndProc( 4514 HWND hwnd, 4515 UINT uMsg, 4516 WPARAM wParam, 4517 LPARAM lParam) 4518 { 4519 /* 4520 TRACE("WndProc: hwnd = %08x, msg = %x, wParam = %x, lParam = %x\n", 4521 hwnd, uMsg, wParam, lParam); 4522 */ 4523 4524 HandleMouseHide(uMsg, lParam); 4525 4526 s_uMsg = uMsg; 4527 s_wParam = wParam; 4528 s_lParam = lParam; 4529 4530 switch (uMsg) 4531 { 4532 HANDLE_MSG(hwnd, WM_DEADCHAR, _OnDeadChar); 4533 HANDLE_MSG(hwnd, WM_SYSDEADCHAR, _OnDeadChar); 4534 /* HANDLE_MSG(hwnd, WM_ACTIVATE, _OnActivate); */ 4535 HANDLE_MSG(hwnd, WM_CLOSE, _OnClose); 4536 /* HANDLE_MSG(hwnd, WM_COMMAND, _OnCommand); */ 4537 HANDLE_MSG(hwnd, WM_DESTROY, _OnDestroy); 4538 HANDLE_MSG(hwnd, WM_DROPFILES, _OnDropFiles); 4539 HANDLE_MSG(hwnd, WM_HSCROLL, _OnScroll); 4540 HANDLE_MSG(hwnd, WM_KILLFOCUS, _OnKillFocus); 4541 #ifdef FEAT_MENU 4542 HANDLE_MSG(hwnd, WM_COMMAND, _OnMenu); 4543 #endif 4544 /* HANDLE_MSG(hwnd, WM_MOVE, _OnMove); */ 4545 /* HANDLE_MSG(hwnd, WM_NCACTIVATE, _OnNCActivate); */ 4546 HANDLE_MSG(hwnd, WM_SETFOCUS, _OnSetFocus); 4547 HANDLE_MSG(hwnd, WM_SIZE, _OnSize); 4548 /* HANDLE_MSG(hwnd, WM_SYSCOMMAND, _OnSysCommand); */ 4549 /* HANDLE_MSG(hwnd, WM_SYSKEYDOWN, _OnAltKey); */ 4550 HANDLE_MSG(hwnd, WM_VSCROLL, _OnScroll); 4551 // HANDLE_MSG(hwnd, WM_WINDOWPOSCHANGING, _OnWindowPosChanging); 4552 HANDLE_MSG(hwnd, WM_ACTIVATEAPP, _OnActivateApp); 4553 #ifdef FEAT_NETBEANS_INTG 4554 HANDLE_MSG(hwnd, WM_WINDOWPOSCHANGED, _OnWindowPosChanged); 4555 #endif 4556 4557 #ifdef FEAT_GUI_TABLINE 4558 case WM_RBUTTONUP: 4559 { 4560 if (gui_mch_showing_tabline()) 4561 { 4562 POINT pt; 4563 RECT rect; 4564 4565 /* 4566 * If the cursor is on the tabline, display the tab menu 4567 */ 4568 GetCursorPos((LPPOINT)&pt); 4569 GetWindowRect(s_textArea, &rect); 4570 if (pt.y < rect.top) 4571 { 4572 show_tabline_popup_menu(); 4573 return 0L; 4574 } 4575 } 4576 return MyWindowProc(hwnd, uMsg, wParam, lParam); 4577 } 4578 case WM_LBUTTONDBLCLK: 4579 { 4580 /* 4581 * If the user double clicked the tabline, create a new tab 4582 */ 4583 if (gui_mch_showing_tabline()) 4584 { 4585 POINT pt; 4586 RECT rect; 4587 4588 GetCursorPos((LPPOINT)&pt); 4589 GetWindowRect(s_textArea, &rect); 4590 if (pt.y < rect.top) 4591 send_tabline_menu_event(0, TABLINE_MENU_NEW); 4592 } 4593 return MyWindowProc(hwnd, uMsg, wParam, lParam); 4594 } 4595 #endif 4596 4597 case WM_QUERYENDSESSION: /* System wants to go down. */ 4598 gui_shell_closed(); /* Will exit when no changed buffers. */ 4599 return FALSE; /* Do NOT allow system to go down. */ 4600 4601 case WM_ENDSESSION: 4602 if (wParam) /* system only really goes down when wParam is TRUE */ 4603 { 4604 _OnEndSession(); 4605 return 0L; 4606 } 4607 break; 4608 4609 case WM_CHAR: 4610 /* Don't use HANDLE_MSG() for WM_CHAR, it truncates wParam to a single 4611 * byte while we want the UTF-16 character value. */ 4612 _OnChar(hwnd, (UINT)wParam, (int)(short)LOWORD(lParam)); 4613 return 0L; 4614 4615 case WM_SYSCHAR: 4616 /* 4617 * if 'winaltkeys' is "no", or it's "menu" and it's not a menu 4618 * shortcut key, handle like a typed ALT key, otherwise call Windows 4619 * ALT key handling. 4620 */ 4621 #ifdef FEAT_MENU 4622 if ( !gui.menu_is_active 4623 || p_wak[0] == 'n' 4624 || (p_wak[0] == 'm' && !gui_is_menu_shortcut((int)wParam)) 4625 ) 4626 #endif 4627 { 4628 _OnSysChar(hwnd, (UINT)wParam, (int)(short)LOWORD(lParam)); 4629 return 0L; 4630 } 4631 #ifdef FEAT_MENU 4632 else 4633 return MyWindowProc(hwnd, uMsg, wParam, lParam); 4634 #endif 4635 4636 case WM_SYSKEYUP: 4637 #ifdef FEAT_MENU 4638 /* This used to be done only when menu is active: ALT key is used for 4639 * that. But that caused problems when menu is disabled and using 4640 * Alt-Tab-Esc: get into a strange state where no mouse-moved events 4641 * are received, mouse pointer remains hidden. */ 4642 return MyWindowProc(hwnd, uMsg, wParam, lParam); 4643 #else 4644 return 0L; 4645 #endif 4646 4647 case WM_SIZING: /* HANDLE_MSG doesn't seem to handle this one */ 4648 return _DuringSizing((UINT)wParam, (LPRECT)lParam); 4649 4650 case WM_MOUSEWHEEL: 4651 _OnMouseWheel(hwnd, HIWORD(wParam)); 4652 return 0L; 4653 4654 /* Notification for change in SystemParametersInfo() */ 4655 case WM_SETTINGCHANGE: 4656 return _OnSettingChange((UINT)wParam); 4657 4658 #if defined(FEAT_TOOLBAR) || defined(FEAT_GUI_TABLINE) 4659 case WM_NOTIFY: 4660 switch (((LPNMHDR) lParam)->code) 4661 { 4662 case TTN_GETDISPINFOW: 4663 case TTN_GETDISPINFO: 4664 { 4665 LPNMHDR hdr = (LPNMHDR)lParam; 4666 char_u *str = NULL; 4667 static void *tt_text = NULL; 4668 4669 VIM_CLEAR(tt_text); 4670 4671 # ifdef FEAT_GUI_TABLINE 4672 if (gui_mch_showing_tabline() 4673 && hdr->hwndFrom == TabCtrl_GetToolTips(s_tabhwnd)) 4674 { 4675 POINT pt; 4676 /* 4677 * Mouse is over the GUI tabline. Display the 4678 * tooltip for the tab under the cursor 4679 * 4680 * Get the cursor position within the tab control 4681 */ 4682 GetCursorPos(&pt); 4683 if (ScreenToClient(s_tabhwnd, &pt) != 0) 4684 { 4685 TCHITTESTINFO htinfo; 4686 int idx; 4687 4688 /* 4689 * Get the tab under the cursor 4690 */ 4691 htinfo.pt.x = pt.x; 4692 htinfo.pt.y = pt.y; 4693 idx = TabCtrl_HitTest(s_tabhwnd, &htinfo); 4694 if (idx != -1) 4695 { 4696 tabpage_T *tp; 4697 4698 tp = find_tabpage(idx + 1); 4699 if (tp != NULL) 4700 { 4701 get_tabline_label(tp, TRUE); 4702 str = NameBuff; 4703 } 4704 } 4705 } 4706 } 4707 # endif 4708 # ifdef FEAT_TOOLBAR 4709 # ifdef FEAT_GUI_TABLINE 4710 else 4711 # endif 4712 { 4713 UINT idButton; 4714 vimmenu_T *pMenu; 4715 4716 idButton = (UINT) hdr->idFrom; 4717 pMenu = gui_mswin_find_menu(root_menu, idButton); 4718 if (pMenu) 4719 str = pMenu->strings[MENU_INDEX_TIP]; 4720 } 4721 # endif 4722 if (str != NULL) 4723 { 4724 if (hdr->code == TTN_GETDISPINFOW) 4725 { 4726 LPNMTTDISPINFOW lpdi = (LPNMTTDISPINFOW)lParam; 4727 4728 /* Set the maximum width, this also enables using 4729 * \n for line break. */ 4730 SendMessage(lpdi->hdr.hwndFrom, TTM_SETMAXTIPWIDTH, 4731 0, 500); 4732 4733 tt_text = enc_to_utf16(str, NULL); 4734 lpdi->lpszText = tt_text; 4735 /* can't show tooltip if failed */ 4736 } 4737 else 4738 { 4739 LPNMTTDISPINFO lpdi = (LPNMTTDISPINFO)lParam; 4740 4741 /* Set the maximum width, this also enables using 4742 * \n for line break. */ 4743 SendMessage(lpdi->hdr.hwndFrom, TTM_SETMAXTIPWIDTH, 4744 0, 500); 4745 4746 if (STRLEN(str) < sizeof(lpdi->szText) 4747 || ((tt_text = vim_strsave(str)) == NULL)) 4748 vim_strncpy((char_u *)lpdi->szText, str, 4749 sizeof(lpdi->szText) - 1); 4750 else 4751 lpdi->lpszText = tt_text; 4752 } 4753 } 4754 } 4755 break; 4756 # ifdef FEAT_GUI_TABLINE 4757 case TCN_SELCHANGE: 4758 if (gui_mch_showing_tabline() 4759 && ((LPNMHDR)lParam)->hwndFrom == s_tabhwnd) 4760 { 4761 send_tabline_event(TabCtrl_GetCurSel(s_tabhwnd) + 1); 4762 return 0L; 4763 } 4764 break; 4765 4766 case NM_RCLICK: 4767 if (gui_mch_showing_tabline() 4768 && ((LPNMHDR)lParam)->hwndFrom == s_tabhwnd) 4769 { 4770 show_tabline_popup_menu(); 4771 return 0L; 4772 } 4773 break; 4774 # endif 4775 default: 4776 # ifdef FEAT_GUI_TABLINE 4777 if (gui_mch_showing_tabline() 4778 && ((LPNMHDR)lParam)->hwndFrom == s_tabhwnd) 4779 return MyWindowProc(hwnd, uMsg, wParam, lParam); 4780 # endif 4781 break; 4782 } 4783 break; 4784 #endif 4785 #if defined(MENUHINTS) && defined(FEAT_MENU) 4786 case WM_MENUSELECT: 4787 if (((UINT) HIWORD(wParam) 4788 & (0xffff ^ (MF_MOUSESELECT + MF_BITMAP + MF_POPUP))) 4789 == MF_HILITE 4790 && (State & CMDLINE) == 0) 4791 { 4792 UINT idButton; 4793 vimmenu_T *pMenu; 4794 static int did_menu_tip = FALSE; 4795 4796 if (did_menu_tip) 4797 { 4798 msg_clr_cmdline(); 4799 setcursor(); 4800 out_flush(); 4801 did_menu_tip = FALSE; 4802 } 4803 4804 idButton = (UINT)LOWORD(wParam); 4805 pMenu = gui_mswin_find_menu(root_menu, idButton); 4806 if (pMenu != NULL && pMenu->strings[MENU_INDEX_TIP] != 0 4807 && GetMenuState(s_menuBar, pMenu->id, MF_BYCOMMAND) != -1) 4808 { 4809 ++msg_hist_off; 4810 msg((char *)pMenu->strings[MENU_INDEX_TIP]); 4811 --msg_hist_off; 4812 setcursor(); 4813 out_flush(); 4814 did_menu_tip = TRUE; 4815 } 4816 return 0L; 4817 } 4818 break; 4819 #endif 4820 case WM_NCHITTEST: 4821 { 4822 LRESULT result; 4823 int x, y; 4824 int xPos = GET_X_LPARAM(lParam); 4825 4826 result = MyWindowProc(hwnd, uMsg, wParam, lParam); 4827 if (result == HTCLIENT) 4828 { 4829 #ifdef FEAT_GUI_TABLINE 4830 if (gui_mch_showing_tabline()) 4831 { 4832 int yPos = GET_Y_LPARAM(lParam); 4833 RECT rct; 4834 4835 /* If the cursor is on the GUI tabline, don't process this 4836 * event */ 4837 GetWindowRect(s_textArea, &rct); 4838 if (yPos < rct.top) 4839 return result; 4840 } 4841 #endif 4842 (void)gui_mch_get_winpos(&x, &y); 4843 xPos -= x; 4844 4845 if (xPos < 48) /* <VN> TODO should use system metric? */ 4846 return HTBOTTOMLEFT; 4847 else 4848 return HTBOTTOMRIGHT; 4849 } 4850 else 4851 return result; 4852 } 4853 /* break; notreached */ 4854 4855 #ifdef FEAT_MBYTE_IME 4856 case WM_IME_NOTIFY: 4857 if (!_OnImeNotify(hwnd, (DWORD)wParam, (DWORD)lParam)) 4858 return MyWindowProc(hwnd, uMsg, wParam, lParam); 4859 return 1L; 4860 4861 case WM_IME_COMPOSITION: 4862 if (!_OnImeComposition(hwnd, wParam, lParam)) 4863 return MyWindowProc(hwnd, uMsg, wParam, lParam); 4864 return 1L; 4865 #endif 4866 4867 default: 4868 #ifdef MSWIN_FIND_REPLACE 4869 if (uMsg == s_findrep_msg && s_findrep_msg != 0) 4870 { 4871 _OnFindRepl(); 4872 } 4873 #endif 4874 return MyWindowProc(hwnd, uMsg, wParam, lParam); 4875 } 4876 4877 return DefWindowProc(hwnd, uMsg, wParam, lParam); 4878 } 4879 4880 /* 4881 * End of call-back routines 4882 */ 4883 4884 /* parent window, if specified with -P */ 4885 HWND vim_parent_hwnd = NULL; 4886 4887 static BOOL CALLBACK 4888 FindWindowTitle(HWND hwnd, LPARAM lParam) 4889 { 4890 char buf[2048]; 4891 char *title = (char *)lParam; 4892 4893 if (GetWindowText(hwnd, buf, sizeof(buf))) 4894 { 4895 if (strstr(buf, title) != NULL) 4896 { 4897 /* Found it. Store the window ref. and quit searching if MDI 4898 * works. */ 4899 vim_parent_hwnd = FindWindowEx(hwnd, NULL, "MDIClient", NULL); 4900 if (vim_parent_hwnd != NULL) 4901 return FALSE; 4902 } 4903 } 4904 return TRUE; /* continue searching */ 4905 } 4906 4907 /* 4908 * Invoked for '-P "title"' argument: search for parent application to open 4909 * our window in. 4910 */ 4911 void 4912 gui_mch_set_parent(char *title) 4913 { 4914 EnumWindows(FindWindowTitle, (LPARAM)title); 4915 if (vim_parent_hwnd == NULL) 4916 { 4917 semsg(_("E671: Cannot find window title \"%s\""), title); 4918 mch_exit(2); 4919 } 4920 } 4921 4922 #ifndef FEAT_OLE 4923 static void 4924 ole_error(char *arg) 4925 { 4926 char buf[IOSIZE]; 4927 4928 /* Can't use emsg() here, we have not finished initialisation yet. */ 4929 vim_snprintf(buf, IOSIZE, 4930 _("E243: Argument not supported: \"-%s\"; Use the OLE version."), 4931 arg); 4932 mch_errmsg(buf); 4933 } 4934 #endif 4935 4936 /* 4937 * Parse the GUI related command-line arguments. Any arguments used are 4938 * deleted from argv, and *argc is decremented accordingly. This is called 4939 * when vim is started, whether or not the GUI has been started. 4940 */ 4941 void 4942 gui_mch_prepare(int *argc, char **argv) 4943 { 4944 int silent = FALSE; 4945 int idx; 4946 4947 /* Check for special OLE command line parameters */ 4948 if ((*argc == 2 || *argc == 3) && (argv[1][0] == '-' || argv[1][0] == '/')) 4949 { 4950 /* Check for a "-silent" argument first. */ 4951 if (*argc == 3 && STRICMP(argv[1] + 1, "silent") == 0 4952 && (argv[2][0] == '-' || argv[2][0] == '/')) 4953 { 4954 silent = TRUE; 4955 idx = 2; 4956 } 4957 else 4958 idx = 1; 4959 4960 /* Register Vim as an OLE Automation server */ 4961 if (STRICMP(argv[idx] + 1, "register") == 0) 4962 { 4963 #ifdef FEAT_OLE 4964 RegisterMe(silent); 4965 mch_exit(0); 4966 #else 4967 if (!silent) 4968 ole_error("register"); 4969 mch_exit(2); 4970 #endif 4971 } 4972 4973 /* Unregister Vim as an OLE Automation server */ 4974 if (STRICMP(argv[idx] + 1, "unregister") == 0) 4975 { 4976 #ifdef FEAT_OLE 4977 UnregisterMe(!silent); 4978 mch_exit(0); 4979 #else 4980 if (!silent) 4981 ole_error("unregister"); 4982 mch_exit(2); 4983 #endif 4984 } 4985 4986 /* Ignore an -embedding argument. It is only relevant if the 4987 * application wants to treat the case when it is started manually 4988 * differently from the case where it is started via automation (and 4989 * we don't). 4990 */ 4991 if (STRICMP(argv[idx] + 1, "embedding") == 0) 4992 { 4993 #ifdef FEAT_OLE 4994 *argc = 1; 4995 #else 4996 ole_error("embedding"); 4997 mch_exit(2); 4998 #endif 4999 } 5000 } 5001 5002 #ifdef FEAT_OLE 5003 { 5004 int bDoRestart = FALSE; 5005 5006 InitOLE(&bDoRestart); 5007 /* automatically exit after registering */ 5008 if (bDoRestart) 5009 mch_exit(0); 5010 } 5011 #endif 5012 5013 #ifdef FEAT_NETBEANS_INTG 5014 { 5015 /* stolen from gui_x11.c */ 5016 int arg; 5017 5018 for (arg = 1; arg < *argc; arg++) 5019 if (strncmp("-nb", argv[arg], 3) == 0) 5020 { 5021 netbeansArg = argv[arg]; 5022 mch_memmove(&argv[arg], &argv[arg + 1], 5023 (--*argc - arg) * sizeof(char *)); 5024 argv[*argc] = NULL; 5025 break; /* enough? */ 5026 } 5027 } 5028 #endif 5029 } 5030 5031 /* 5032 * Initialise the GUI. Create all the windows, set up all the call-backs 5033 * etc. 5034 */ 5035 int 5036 gui_mch_init(void) 5037 { 5038 const char szVimWndClass[] = VIM_CLASS; 5039 const char szTextAreaClass[] = "VimTextArea"; 5040 WNDCLASS wndclass; 5041 const WCHAR szVimWndClassW[] = VIM_CLASSW; 5042 const WCHAR szTextAreaClassW[] = L"VimTextArea"; 5043 WNDCLASSW wndclassw; 5044 #ifdef GLOBAL_IME 5045 ATOM atom; 5046 #endif 5047 5048 /* Return here if the window was already opened (happens when 5049 * gui_mch_dialog() is called early). */ 5050 if (s_hwnd != NULL) 5051 goto theend; 5052 5053 /* 5054 * Load the tearoff bitmap 5055 */ 5056 #ifdef FEAT_TEAROFF 5057 s_htearbitmap = LoadBitmap(s_hinst, "IDB_TEAROFF"); 5058 #endif 5059 5060 gui.scrollbar_width = GetSystemMetrics(SM_CXVSCROLL); 5061 gui.scrollbar_height = GetSystemMetrics(SM_CYHSCROLL); 5062 #ifdef FEAT_MENU 5063 gui.menu_height = 0; /* Windows takes care of this */ 5064 #endif 5065 gui.border_width = 0; 5066 5067 s_brush = CreateSolidBrush(GetSysColor(COLOR_BTNFACE)); 5068 5069 /* First try using the wide version, so that we can use any title. 5070 * Otherwise only characters in the active codepage will work. */ 5071 if (GetClassInfoW(s_hinst, szVimWndClassW, &wndclassw) == 0) 5072 { 5073 wndclassw.style = CS_DBLCLKS; 5074 wndclassw.lpfnWndProc = _WndProc; 5075 wndclassw.cbClsExtra = 0; 5076 wndclassw.cbWndExtra = 0; 5077 wndclassw.hInstance = s_hinst; 5078 wndclassw.hIcon = LoadIcon(wndclassw.hInstance, "IDR_VIM"); 5079 wndclassw.hCursor = LoadCursor(NULL, IDC_ARROW); 5080 wndclassw.hbrBackground = s_brush; 5081 wndclassw.lpszMenuName = NULL; 5082 wndclassw.lpszClassName = szVimWndClassW; 5083 5084 if (( 5085 #ifdef GLOBAL_IME 5086 atom = 5087 #endif 5088 RegisterClassW(&wndclassw)) == 0) 5089 return FAIL; 5090 else 5091 wide_WindowProc = TRUE; 5092 } 5093 5094 if (!wide_WindowProc) 5095 if (GetClassInfo(s_hinst, szVimWndClass, &wndclass) == 0) 5096 { 5097 wndclass.style = CS_DBLCLKS; 5098 wndclass.lpfnWndProc = _WndProc; 5099 wndclass.cbClsExtra = 0; 5100 wndclass.cbWndExtra = 0; 5101 wndclass.hInstance = s_hinst; 5102 wndclass.hIcon = LoadIcon(wndclass.hInstance, "IDR_VIM"); 5103 wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); 5104 wndclass.hbrBackground = s_brush; 5105 wndclass.lpszMenuName = NULL; 5106 wndclass.lpszClassName = szVimWndClass; 5107 5108 if (( 5109 #ifdef GLOBAL_IME 5110 atom = 5111 #endif 5112 RegisterClass(&wndclass)) == 0) 5113 return FAIL; 5114 } 5115 5116 if (vim_parent_hwnd != NULL) 5117 { 5118 #ifdef HAVE_TRY_EXCEPT 5119 __try 5120 { 5121 #endif 5122 /* Open inside the specified parent window. 5123 * TODO: last argument should point to a CLIENTCREATESTRUCT 5124 * structure. */ 5125 s_hwnd = CreateWindowEx( 5126 WS_EX_MDICHILD, 5127 szVimWndClass, "Vim MSWindows GUI", 5128 WS_OVERLAPPEDWINDOW | WS_CHILD 5129 | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | 0xC000, 5130 gui_win_x == -1 ? CW_USEDEFAULT : gui_win_x, 5131 gui_win_y == -1 ? CW_USEDEFAULT : gui_win_y, 5132 100, /* Any value will do */ 5133 100, /* Any value will do */ 5134 vim_parent_hwnd, NULL, 5135 s_hinst, NULL); 5136 #ifdef HAVE_TRY_EXCEPT 5137 } 5138 __except(EXCEPTION_EXECUTE_HANDLER) 5139 { 5140 /* NOP */ 5141 } 5142 #endif 5143 if (s_hwnd == NULL) 5144 { 5145 emsg(_("E672: Unable to open window inside MDI application")); 5146 mch_exit(2); 5147 } 5148 } 5149 else 5150 { 5151 /* If the provided windowid is not valid reset it to zero, so that it 5152 * is ignored and we open our own window. */ 5153 if (IsWindow((HWND)win_socket_id) <= 0) 5154 win_socket_id = 0; 5155 5156 /* Create a window. If win_socket_id is not zero without border and 5157 * titlebar, it will be reparented below. */ 5158 s_hwnd = CreateWindow( 5159 szVimWndClass, "Vim MSWindows GUI", 5160 (win_socket_id == 0 ? WS_OVERLAPPEDWINDOW : WS_POPUP) 5161 | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, 5162 gui_win_x == -1 ? CW_USEDEFAULT : gui_win_x, 5163 gui_win_y == -1 ? CW_USEDEFAULT : gui_win_y, 5164 100, /* Any value will do */ 5165 100, /* Any value will do */ 5166 NULL, NULL, 5167 s_hinst, NULL); 5168 if (s_hwnd != NULL && win_socket_id != 0) 5169 { 5170 SetParent(s_hwnd, (HWND)win_socket_id); 5171 ShowWindow(s_hwnd, SW_SHOWMAXIMIZED); 5172 } 5173 } 5174 5175 if (s_hwnd == NULL) 5176 return FAIL; 5177 5178 #ifdef GLOBAL_IME 5179 global_ime_init(atom, s_hwnd); 5180 #endif 5181 #if defined(FEAT_MBYTE_IME) && defined(DYNAMIC_IME) 5182 dyn_imm_load(); 5183 #endif 5184 5185 /* Create the text area window */ 5186 if (wide_WindowProc) 5187 { 5188 if (GetClassInfoW(s_hinst, szTextAreaClassW, &wndclassw) == 0) 5189 { 5190 wndclassw.style = CS_OWNDC; 5191 wndclassw.lpfnWndProc = _TextAreaWndProc; 5192 wndclassw.cbClsExtra = 0; 5193 wndclassw.cbWndExtra = 0; 5194 wndclassw.hInstance = s_hinst; 5195 wndclassw.hIcon = NULL; 5196 wndclassw.hCursor = LoadCursor(NULL, IDC_ARROW); 5197 wndclassw.hbrBackground = NULL; 5198 wndclassw.lpszMenuName = NULL; 5199 wndclassw.lpszClassName = szTextAreaClassW; 5200 5201 if (RegisterClassW(&wndclassw) == 0) 5202 return FAIL; 5203 } 5204 5205 s_textArea = CreateWindowExW( 5206 0, 5207 szTextAreaClassW, L"Vim text area", 5208 WS_CHILD | WS_VISIBLE, 0, 0, 5209 100, // Any value will do for now 5210 100, // Any value will do for now 5211 s_hwnd, NULL, 5212 s_hinst, NULL); 5213 } 5214 else if (GetClassInfo(s_hinst, szTextAreaClass, &wndclass) == 0) 5215 { 5216 wndclass.style = CS_OWNDC; 5217 wndclass.lpfnWndProc = _TextAreaWndProc; 5218 wndclass.cbClsExtra = 0; 5219 wndclass.cbWndExtra = 0; 5220 wndclass.hInstance = s_hinst; 5221 wndclass.hIcon = NULL; 5222 wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); 5223 wndclass.hbrBackground = NULL; 5224 wndclass.lpszMenuName = NULL; 5225 wndclass.lpszClassName = szTextAreaClass; 5226 5227 if (RegisterClass(&wndclass) == 0) 5228 return FAIL; 5229 5230 s_textArea = CreateWindowEx( 5231 0, 5232 szTextAreaClass, "Vim text area", 5233 WS_CHILD | WS_VISIBLE, 0, 0, 5234 100, // Any value will do for now 5235 100, // Any value will do for now 5236 s_hwnd, NULL, 5237 s_hinst, NULL); 5238 } 5239 5240 if (s_textArea == NULL) 5241 return FAIL; 5242 5243 #ifdef FEAT_LIBCALL 5244 /* Try loading an icon from $RUNTIMEPATH/bitmaps/vim.ico. */ 5245 { 5246 HANDLE hIcon = NULL; 5247 5248 if (mch_icon_load(&hIcon) == OK && hIcon != NULL) 5249 SendMessage(s_hwnd, WM_SETICON, ICON_SMALL, (LPARAM)hIcon); 5250 } 5251 #endif 5252 5253 #ifdef FEAT_MENU 5254 s_menuBar = CreateMenu(); 5255 #endif 5256 s_hdc = GetDC(s_textArea); 5257 5258 DragAcceptFiles(s_hwnd, TRUE); 5259 5260 /* Do we need to bother with this? */ 5261 /* m_fMouseAvail = GetSystemMetrics(SM_MOUSEPRESENT); */ 5262 5263 /* Get background/foreground colors from the system */ 5264 gui_mch_def_colors(); 5265 5266 /* Get the colors from the "Normal" group (set in syntax.c or in a vimrc 5267 * file) */ 5268 set_normal_colors(); 5269 5270 /* 5271 * Check that none of the colors are the same as the background color. 5272 * Then store the current values as the defaults. 5273 */ 5274 gui_check_colors(); 5275 gui.def_norm_pixel = gui.norm_pixel; 5276 gui.def_back_pixel = gui.back_pixel; 5277 5278 /* Get the colors for the highlight groups (gui_check_colors() might have 5279 * changed them) */ 5280 highlight_gui_started(); 5281 5282 /* 5283 * Start out by adding the configured border width into the border offset. 5284 */ 5285 gui.border_offset = gui.border_width; 5286 5287 /* 5288 * Set up for Intellimouse processing 5289 */ 5290 init_mouse_wheel(); 5291 5292 /* 5293 * compute a couple of metrics used for the dialogs 5294 */ 5295 get_dialog_font_metrics(); 5296 #ifdef FEAT_TOOLBAR 5297 /* 5298 * Create the toolbar 5299 */ 5300 initialise_toolbar(); 5301 #endif 5302 #ifdef FEAT_GUI_TABLINE 5303 /* 5304 * Create the tabline 5305 */ 5306 initialise_tabline(); 5307 #endif 5308 #ifdef MSWIN_FIND_REPLACE 5309 /* 5310 * Initialise the dialog box stuff 5311 */ 5312 s_findrep_msg = RegisterWindowMessage(FINDMSGSTRING); 5313 5314 /* Initialise the struct */ 5315 s_findrep_struct.lStructSize = sizeof(s_findrep_struct); 5316 s_findrep_struct.lpstrFindWhat = (LPSTR)alloc(MSWIN_FR_BUFSIZE); 5317 s_findrep_struct.lpstrFindWhat[0] = NUL; 5318 s_findrep_struct.lpstrReplaceWith = (LPSTR)alloc(MSWIN_FR_BUFSIZE); 5319 s_findrep_struct.lpstrReplaceWith[0] = NUL; 5320 s_findrep_struct.wFindWhatLen = MSWIN_FR_BUFSIZE; 5321 s_findrep_struct.wReplaceWithLen = MSWIN_FR_BUFSIZE; 5322 s_findrep_struct_w.lStructSize = sizeof(s_findrep_struct_w); 5323 s_findrep_struct_w.lpstrFindWhat = 5324 (LPWSTR)alloc(MSWIN_FR_BUFSIZE * sizeof(WCHAR)); 5325 s_findrep_struct_w.lpstrFindWhat[0] = NUL; 5326 s_findrep_struct_w.lpstrReplaceWith = 5327 (LPWSTR)alloc(MSWIN_FR_BUFSIZE * sizeof(WCHAR)); 5328 s_findrep_struct_w.lpstrReplaceWith[0] = NUL; 5329 s_findrep_struct_w.wFindWhatLen = MSWIN_FR_BUFSIZE; 5330 s_findrep_struct_w.wReplaceWithLen = MSWIN_FR_BUFSIZE; 5331 #endif 5332 5333 #ifdef FEAT_EVAL 5334 # if !defined(_MSC_VER) || (_MSC_VER < 1400) 5335 /* Define HandleToLong for old MS and non-MS compilers if not defined. */ 5336 # ifndef HandleToLong 5337 # define HandleToLong(h) ((long)(intptr_t)(h)) 5338 # endif 5339 # endif 5340 /* set the v:windowid variable */ 5341 set_vim_var_nr(VV_WINDOWID, HandleToLong(s_hwnd)); 5342 #endif 5343 5344 #ifdef FEAT_RENDER_OPTIONS 5345 if (p_rop) 5346 (void)gui_mch_set_rendering_options(p_rop); 5347 #endif 5348 5349 theend: 5350 /* Display any pending error messages */ 5351 display_errors(); 5352 5353 return OK; 5354 } 5355 5356 /* 5357 * Get the size of the screen, taking position on multiple monitors into 5358 * account (if supported). 5359 */ 5360 static void 5361 get_work_area(RECT *spi_rect) 5362 { 5363 HMONITOR mon; 5364 MONITORINFO moninfo; 5365 5366 /* work out which monitor the window is on, and get *its* work area */ 5367 mon = MonitorFromWindow(s_hwnd, MONITOR_DEFAULTTOPRIMARY); 5368 if (mon != NULL) 5369 { 5370 moninfo.cbSize = sizeof(MONITORINFO); 5371 if (GetMonitorInfo(mon, &moninfo)) 5372 { 5373 *spi_rect = moninfo.rcWork; 5374 return; 5375 } 5376 } 5377 /* this is the old method... */ 5378 SystemParametersInfo(SPI_GETWORKAREA, 0, spi_rect, 0); 5379 } 5380 5381 /* 5382 * Set the size of the window to the given width and height in pixels. 5383 */ 5384 void 5385 gui_mch_set_shellsize( 5386 int width, 5387 int height, 5388 int min_width UNUSED, 5389 int min_height UNUSED, 5390 int base_width UNUSED, 5391 int base_height UNUSED, 5392 int direction) 5393 { 5394 RECT workarea_rect; 5395 int win_width, win_height; 5396 WINDOWPLACEMENT wndpl; 5397 5398 /* Try to keep window completely on screen. */ 5399 /* Get position of the screen work area. This is the part that is not 5400 * used by the taskbar or appbars. */ 5401 get_work_area(&workarea_rect); 5402 5403 /* Get current position of our window. Note that the .left and .top are 5404 * relative to the work area. */ 5405 wndpl.length = sizeof(WINDOWPLACEMENT); 5406 GetWindowPlacement(s_hwnd, &wndpl); 5407 5408 /* Resizing a maximized window looks very strange, unzoom it first. 5409 * But don't do it when still starting up, it may have been requested in 5410 * the shortcut. */ 5411 if (wndpl.showCmd == SW_SHOWMAXIMIZED && starting == 0) 5412 { 5413 ShowWindow(s_hwnd, SW_SHOWNORMAL); 5414 /* Need to get the settings of the normal window. */ 5415 GetWindowPlacement(s_hwnd, &wndpl); 5416 } 5417 5418 /* compute the size of the outside of the window */ 5419 win_width = width + (GetSystemMetrics(SM_CXFRAME) + 5420 GetSystemMetrics(SM_CXPADDEDBORDER)) * 2; 5421 win_height = height + (GetSystemMetrics(SM_CYFRAME) + 5422 GetSystemMetrics(SM_CXPADDEDBORDER)) * 2 5423 + GetSystemMetrics(SM_CYCAPTION) 5424 #ifdef FEAT_MENU 5425 + gui_mswin_get_menu_height(FALSE) 5426 #endif 5427 ; 5428 5429 /* The following should take care of keeping Vim on the same monitor, no 5430 * matter if the secondary monitor is left or right of the primary 5431 * monitor. */ 5432 wndpl.rcNormalPosition.right = wndpl.rcNormalPosition.left + win_width; 5433 wndpl.rcNormalPosition.bottom = wndpl.rcNormalPosition.top + win_height; 5434 5435 /* If the window is going off the screen, move it on to the screen. */ 5436 if ((direction & RESIZE_HOR) 5437 && wndpl.rcNormalPosition.right > workarea_rect.right) 5438 OffsetRect(&wndpl.rcNormalPosition, 5439 workarea_rect.right - wndpl.rcNormalPosition.right, 0); 5440 5441 if ((direction & RESIZE_HOR) 5442 && wndpl.rcNormalPosition.left < workarea_rect.left) 5443 OffsetRect(&wndpl.rcNormalPosition, 5444 workarea_rect.left - wndpl.rcNormalPosition.left, 0); 5445 5446 if ((direction & RESIZE_VERT) 5447 && wndpl.rcNormalPosition.bottom > workarea_rect.bottom) 5448 OffsetRect(&wndpl.rcNormalPosition, 5449 0, workarea_rect.bottom - wndpl.rcNormalPosition.bottom); 5450 5451 if ((direction & RESIZE_VERT) 5452 && wndpl.rcNormalPosition.top < workarea_rect.top) 5453 OffsetRect(&wndpl.rcNormalPosition, 5454 0, workarea_rect.top - wndpl.rcNormalPosition.top); 5455 5456 /* set window position - we should use SetWindowPlacement rather than 5457 * SetWindowPos as the MSDN docs say the coord systems returned by 5458 * these two are not compatible. */ 5459 SetWindowPlacement(s_hwnd, &wndpl); 5460 5461 SetActiveWindow(s_hwnd); 5462 SetFocus(s_hwnd); 5463 5464 #ifdef FEAT_MENU 5465 /* Menu may wrap differently now */ 5466 gui_mswin_get_menu_height(!gui.starting); 5467 #endif 5468 } 5469 5470 5471 void 5472 gui_mch_set_scrollbar_thumb( 5473 scrollbar_T *sb, 5474 long val, 5475 long size, 5476 long max) 5477 { 5478 SCROLLINFO info; 5479 5480 sb->scroll_shift = 0; 5481 while (max > 32767) 5482 { 5483 max = (max + 1) >> 1; 5484 val >>= 1; 5485 size >>= 1; 5486 ++sb->scroll_shift; 5487 } 5488 5489 if (sb->scroll_shift > 0) 5490 ++size; 5491 5492 info.cbSize = sizeof(info); 5493 info.fMask = SIF_POS | SIF_RANGE | SIF_PAGE; 5494 info.nPos = val; 5495 info.nMin = 0; 5496 info.nMax = max; 5497 info.nPage = size; 5498 SetScrollInfo(sb->id, SB_CTL, &info, TRUE); 5499 } 5500 5501 5502 /* 5503 * Set the current text font. 5504 */ 5505 void 5506 gui_mch_set_font(GuiFont font) 5507 { 5508 gui.currFont = font; 5509 } 5510 5511 5512 /* 5513 * Set the current text foreground color. 5514 */ 5515 void 5516 gui_mch_set_fg_color(guicolor_T color) 5517 { 5518 gui.currFgColor = color; 5519 } 5520 5521 /* 5522 * Set the current text background color. 5523 */ 5524 void 5525 gui_mch_set_bg_color(guicolor_T color) 5526 { 5527 gui.currBgColor = color; 5528 } 5529 5530 /* 5531 * Set the current text special color. 5532 */ 5533 void 5534 gui_mch_set_sp_color(guicolor_T color) 5535 { 5536 gui.currSpColor = color; 5537 } 5538 5539 #ifdef FEAT_MBYTE_IME 5540 /* 5541 * Multi-byte handling, originally by Sung-Hoon Baek. 5542 * First static functions (no prototypes generated). 5543 */ 5544 # ifdef _MSC_VER 5545 # include <ime.h> /* Apparently not needed for Cygwin, MingW or Borland. */ 5546 # endif 5547 # include <imm.h> 5548 5549 /* 5550 * handle WM_IME_NOTIFY message 5551 */ 5552 static LRESULT 5553 _OnImeNotify(HWND hWnd, DWORD dwCommand, DWORD dwData UNUSED) 5554 { 5555 LRESULT lResult = 0; 5556 HIMC hImc; 5557 5558 if (!pImmGetContext || (hImc = pImmGetContext(hWnd)) == (HIMC)0) 5559 return lResult; 5560 switch (dwCommand) 5561 { 5562 case IMN_SETOPENSTATUS: 5563 if (pImmGetOpenStatus(hImc)) 5564 { 5565 pImmSetCompositionFont(hImc, &norm_logfont); 5566 im_set_position(gui.row, gui.col); 5567 5568 /* Disable langmap */ 5569 State &= ~LANGMAP; 5570 if (State & INSERT) 5571 { 5572 #if defined(FEAT_KEYMAP) 5573 /* Unshown 'keymap' in status lines */ 5574 if (curbuf->b_p_iminsert == B_IMODE_LMAP) 5575 { 5576 /* Save cursor position */ 5577 int old_row = gui.row; 5578 int old_col = gui.col; 5579 5580 // This must be called here before 5581 // status_redraw_curbuf(), otherwise the mode 5582 // message may appear in the wrong position. 5583 showmode(); 5584 status_redraw_curbuf(); 5585 update_screen(0); 5586 /* Restore cursor position */ 5587 gui.row = old_row; 5588 gui.col = old_col; 5589 } 5590 #endif 5591 } 5592 } 5593 gui_update_cursor(TRUE, FALSE); 5594 gui_mch_flush(); 5595 lResult = 0; 5596 break; 5597 } 5598 pImmReleaseContext(hWnd, hImc); 5599 return lResult; 5600 } 5601 5602 static LRESULT 5603 _OnImeComposition(HWND hwnd, WPARAM dbcs UNUSED, LPARAM param) 5604 { 5605 char_u *ret; 5606 int len; 5607 5608 if ((param & GCS_RESULTSTR) == 0) /* Composition unfinished. */ 5609 return 0; 5610 5611 ret = GetResultStr(hwnd, GCS_RESULTSTR, &len); 5612 if (ret != NULL) 5613 { 5614 add_to_input_buf_csi(ret, len); 5615 vim_free(ret); 5616 return 1; 5617 } 5618 return 0; 5619 } 5620 5621 /* 5622 * get the current composition string, in UCS-2; *lenp is the number of 5623 * *lenp is the number of Unicode characters. 5624 */ 5625 static short_u * 5626 GetCompositionString_inUCS2(HIMC hIMC, DWORD GCS, int *lenp) 5627 { 5628 LONG ret; 5629 LPWSTR wbuf = NULL; 5630 char_u *buf; 5631 5632 if (!pImmGetContext) 5633 return NULL; /* no imm32.dll */ 5634 5635 /* Try Unicode; this'll always work on NT regardless of codepage. */ 5636 ret = pImmGetCompositionStringW(hIMC, GCS, NULL, 0); 5637 if (ret == 0) 5638 return NULL; /* empty */ 5639 5640 if (ret > 0) 5641 { 5642 /* Allocate the requested buffer plus space for the NUL character. */ 5643 wbuf = (LPWSTR)alloc(ret + sizeof(WCHAR)); 5644 if (wbuf != NULL) 5645 { 5646 pImmGetCompositionStringW(hIMC, GCS, wbuf, ret); 5647 *lenp = ret / sizeof(WCHAR); 5648 } 5649 return (short_u *)wbuf; 5650 } 5651 5652 /* ret < 0; we got an error, so try the ANSI version. This'll work 5653 * on 9x/ME, but only if the codepage happens to be set to whatever 5654 * we're inputting. */ 5655 ret = pImmGetCompositionStringA(hIMC, GCS, NULL, 0); 5656 if (ret <= 0) 5657 return NULL; /* empty or error */ 5658 5659 buf = alloc(ret); 5660 if (buf == NULL) 5661 return NULL; 5662 pImmGetCompositionStringA(hIMC, GCS, buf, ret); 5663 5664 /* convert from codepage to UCS-2 */ 5665 MultiByteToWideChar_alloc(GetACP(), 0, (LPCSTR)buf, ret, &wbuf, lenp); 5666 vim_free(buf); 5667 5668 return (short_u *)wbuf; 5669 } 5670 5671 /* 5672 * void GetResultStr() 5673 * 5674 * This handles WM_IME_COMPOSITION with GCS_RESULTSTR flag on. 5675 * get complete composition string 5676 */ 5677 static char_u * 5678 GetResultStr(HWND hwnd, int GCS, int *lenp) 5679 { 5680 HIMC hIMC; /* Input context handle. */ 5681 short_u *buf = NULL; 5682 char_u *convbuf = NULL; 5683 5684 if (!pImmGetContext || (hIMC = pImmGetContext(hwnd)) == (HIMC)0) 5685 return NULL; 5686 5687 /* Reads in the composition string. */ 5688 buf = GetCompositionString_inUCS2(hIMC, GCS, lenp); 5689 if (buf == NULL) 5690 return NULL; 5691 5692 convbuf = utf16_to_enc(buf, lenp); 5693 pImmReleaseContext(hwnd, hIMC); 5694 vim_free(buf); 5695 return convbuf; 5696 } 5697 #endif 5698 5699 /* For global functions we need prototypes. */ 5700 #if defined(FEAT_MBYTE_IME) || defined(PROTO) 5701 5702 /* 5703 * set font to IM. 5704 */ 5705 void 5706 im_set_font(LOGFONT *lf) 5707 { 5708 HIMC hImc; 5709 5710 if (pImmGetContext && (hImc = pImmGetContext(s_hwnd)) != (HIMC)0) 5711 { 5712 pImmSetCompositionFont(hImc, lf); 5713 pImmReleaseContext(s_hwnd, hImc); 5714 } 5715 } 5716 5717 /* 5718 * Notify cursor position to IM. 5719 */ 5720 void 5721 im_set_position(int row, int col) 5722 { 5723 HIMC hImc; 5724 5725 if (pImmGetContext && (hImc = pImmGetContext(s_hwnd)) != (HIMC)0) 5726 { 5727 COMPOSITIONFORM cfs; 5728 5729 cfs.dwStyle = CFS_POINT; 5730 cfs.ptCurrentPos.x = FILL_X(col); 5731 cfs.ptCurrentPos.y = FILL_Y(row); 5732 MapWindowPoints(s_textArea, s_hwnd, &cfs.ptCurrentPos, 1); 5733 pImmSetCompositionWindow(hImc, &cfs); 5734 5735 pImmReleaseContext(s_hwnd, hImc); 5736 } 5737 } 5738 5739 /* 5740 * Set IM status on ("active" is TRUE) or off ("active" is FALSE). 5741 */ 5742 void 5743 im_set_active(int active) 5744 { 5745 HIMC hImc; 5746 static HIMC hImcOld = (HIMC)0; 5747 5748 if (pImmGetContext) /* if NULL imm32.dll wasn't loaded (yet) */ 5749 { 5750 if (p_imdisable) 5751 { 5752 if (hImcOld == (HIMC)0) 5753 { 5754 hImcOld = pImmGetContext(s_hwnd); 5755 if (hImcOld) 5756 pImmAssociateContext(s_hwnd, (HIMC)0); 5757 } 5758 active = FALSE; 5759 } 5760 else if (hImcOld != (HIMC)0) 5761 { 5762 pImmAssociateContext(s_hwnd, hImcOld); 5763 hImcOld = (HIMC)0; 5764 } 5765 5766 hImc = pImmGetContext(s_hwnd); 5767 if (hImc) 5768 { 5769 /* 5770 * for Korean ime 5771 */ 5772 HKL hKL = GetKeyboardLayout(0); 5773 5774 if (LOWORD(hKL) == MAKELANGID(LANG_KOREAN, SUBLANG_KOREAN)) 5775 { 5776 static DWORD dwConversionSaved = 0, dwSentenceSaved = 0; 5777 static BOOL bSaved = FALSE; 5778 5779 if (active) 5780 { 5781 /* if we have a saved conversion status, restore it */ 5782 if (bSaved) 5783 pImmSetConversionStatus(hImc, dwConversionSaved, 5784 dwSentenceSaved); 5785 bSaved = FALSE; 5786 } 5787 else 5788 { 5789 /* save conversion status and disable korean */ 5790 if (pImmGetConversionStatus(hImc, &dwConversionSaved, 5791 &dwSentenceSaved)) 5792 { 5793 bSaved = TRUE; 5794 pImmSetConversionStatus(hImc, 5795 dwConversionSaved & ~(IME_CMODE_NATIVE 5796 | IME_CMODE_FULLSHAPE), 5797 dwSentenceSaved); 5798 } 5799 } 5800 } 5801 5802 pImmSetOpenStatus(hImc, active); 5803 pImmReleaseContext(s_hwnd, hImc); 5804 } 5805 } 5806 } 5807 5808 /* 5809 * Get IM status. When IM is on, return not 0. Else return 0. 5810 */ 5811 int 5812 im_get_status(void) 5813 { 5814 int status = 0; 5815 HIMC hImc; 5816 5817 if (pImmGetContext && (hImc = pImmGetContext(s_hwnd)) != (HIMC)0) 5818 { 5819 status = pImmGetOpenStatus(hImc) ? 1 : 0; 5820 pImmReleaseContext(s_hwnd, hImc); 5821 } 5822 return status; 5823 } 5824 5825 #endif /* FEAT_MBYTE_IME */ 5826 5827 #if !defined(FEAT_MBYTE_IME) && defined(GLOBAL_IME) 5828 /* Win32 with GLOBAL IME */ 5829 5830 /* 5831 * Notify cursor position to IM. 5832 */ 5833 void 5834 im_set_position(int row, int col) 5835 { 5836 /* Win32 with GLOBAL IME */ 5837 POINT p; 5838 5839 p.x = FILL_X(col); 5840 p.y = FILL_Y(row); 5841 MapWindowPoints(s_textArea, s_hwnd, &p, 1); 5842 global_ime_set_position(&p); 5843 } 5844 5845 /* 5846 * Set IM status on ("active" is TRUE) or off ("active" is FALSE). 5847 */ 5848 void 5849 im_set_active(int active) 5850 { 5851 global_ime_set_status(active); 5852 } 5853 5854 /* 5855 * Get IM status. When IM is on, return not 0. Else return 0. 5856 */ 5857 int 5858 im_get_status(void) 5859 { 5860 return global_ime_get_status(); 5861 } 5862 #endif 5863 5864 /* 5865 * Convert latin9 text "text[len]" to ucs-2 in "unicodebuf". 5866 */ 5867 static void 5868 latin9_to_ucs(char_u *text, int len, WCHAR *unicodebuf) 5869 { 5870 int c; 5871 5872 while (--len >= 0) 5873 { 5874 c = *text++; 5875 switch (c) 5876 { 5877 case 0xa4: c = 0x20ac; break; /* euro */ 5878 case 0xa6: c = 0x0160; break; /* S hat */ 5879 case 0xa8: c = 0x0161; break; /* S -hat */ 5880 case 0xb4: c = 0x017d; break; /* Z hat */ 5881 case 0xb8: c = 0x017e; break; /* Z -hat */ 5882 case 0xbc: c = 0x0152; break; /* OE */ 5883 case 0xbd: c = 0x0153; break; /* oe */ 5884 case 0xbe: c = 0x0178; break; /* Y */ 5885 } 5886 *unicodebuf++ = c; 5887 } 5888 } 5889 5890 #ifdef FEAT_RIGHTLEFT 5891 /* 5892 * What is this for? In the case where you are using Win98 or Win2K or later, 5893 * and you are using a Hebrew font (or Arabic!), Windows does you a favor and 5894 * reverses the string sent to the TextOut... family. This sucks, because we 5895 * go to a lot of effort to do the right thing, and there doesn't seem to be a 5896 * way to tell Windblows not to do this! 5897 * 5898 * The short of it is that this 'RevOut' only gets called if you are running 5899 * one of the new, "improved" MS OSes, and only if you are running in 5900 * 'rightleft' mode. It makes display take *slightly* longer, but not 5901 * noticeably so. 5902 */ 5903 static void 5904 RevOut( HDC s_hdc, 5905 int col, 5906 int row, 5907 UINT foptions, 5908 CONST RECT *pcliprect, 5909 LPCTSTR text, 5910 UINT len, 5911 CONST INT *padding) 5912 { 5913 int ix; 5914 5915 for (ix = 0; ix < (int)len; ++ix) 5916 ExtTextOut(s_hdc, col + TEXT_X(ix), row, foptions, 5917 pcliprect, text + ix, 1, padding); 5918 } 5919 #endif 5920 5921 static void 5922 draw_line( 5923 int x1, 5924 int y1, 5925 int x2, 5926 int y2, 5927 COLORREF color) 5928 { 5929 #if defined(FEAT_DIRECTX) 5930 if (IS_ENABLE_DIRECTX()) 5931 DWriteContext_DrawLine(s_dwc, x1, y1, x2, y2, color); 5932 else 5933 #endif 5934 { 5935 HPEN hpen = CreatePen(PS_SOLID, 1, color); 5936 HPEN old_pen = SelectObject(s_hdc, hpen); 5937 MoveToEx(s_hdc, x1, y1, NULL); 5938 /* Note: LineTo() excludes the last pixel in the line. */ 5939 LineTo(s_hdc, x2, y2); 5940 DeleteObject(SelectObject(s_hdc, old_pen)); 5941 } 5942 } 5943 5944 static void 5945 set_pixel( 5946 int x, 5947 int y, 5948 COLORREF color) 5949 { 5950 #if defined(FEAT_DIRECTX) 5951 if (IS_ENABLE_DIRECTX()) 5952 DWriteContext_SetPixel(s_dwc, x, y, color); 5953 else 5954 #endif 5955 SetPixel(s_hdc, x, y, color); 5956 } 5957 5958 static void 5959 fill_rect( 5960 const RECT *rcp, 5961 HBRUSH hbr, 5962 COLORREF color) 5963 { 5964 #if defined(FEAT_DIRECTX) 5965 if (IS_ENABLE_DIRECTX()) 5966 DWriteContext_FillRect(s_dwc, rcp, color); 5967 else 5968 #endif 5969 { 5970 HBRUSH hbr2; 5971 5972 if (hbr == NULL) 5973 hbr2 = CreateSolidBrush(color); 5974 else 5975 hbr2 = hbr; 5976 FillRect(s_hdc, rcp, hbr2); 5977 if (hbr == NULL) 5978 DeleteBrush(hbr2); 5979 } 5980 } 5981 5982 void 5983 gui_mch_draw_string( 5984 int row, 5985 int col, 5986 char_u *text, 5987 int len, 5988 int flags) 5989 { 5990 static int *padding = NULL; 5991 static int pad_size = 0; 5992 int i; 5993 const RECT *pcliprect = NULL; 5994 UINT foptions = 0; 5995 static WCHAR *unicodebuf = NULL; 5996 static int *unicodepdy = NULL; 5997 static int unibuflen = 0; 5998 int n = 0; 5999 int y; 6000 6001 /* 6002 * Italic and bold text seems to have an extra row of pixels at the bottom 6003 * (below where the bottom of the character should be). If we draw the 6004 * characters with a solid background, the top row of pixels in the 6005 * character below will be overwritten. We can fix this by filling in the 6006 * background ourselves, to the correct character proportions, and then 6007 * writing the character in transparent mode. Still have a problem when 6008 * the character is "_", which gets written on to the character below. 6009 * New fix: set gui.char_ascent to -1. This shifts all characters up one 6010 * pixel in their slots, which fixes the problem with the bottom row of 6011 * pixels. We still need this code because otherwise the top row of pixels 6012 * becomes a problem. - webb. 6013 */ 6014 static HBRUSH hbr_cache[2] = {NULL, NULL}; 6015 static guicolor_T brush_color[2] = {INVALCOLOR, INVALCOLOR}; 6016 static int brush_lru = 0; 6017 HBRUSH hbr; 6018 RECT rc; 6019 6020 if (!(flags & DRAW_TRANSP)) 6021 { 6022 /* 6023 * Clear background first. 6024 * Note: FillRect() excludes right and bottom of rectangle. 6025 */ 6026 rc.left = FILL_X(col); 6027 rc.top = FILL_Y(row); 6028 if (has_mbyte) 6029 { 6030 /* Compute the length in display cells. */ 6031 rc.right = FILL_X(col + mb_string2cells(text, len)); 6032 } 6033 else 6034 rc.right = FILL_X(col + len); 6035 rc.bottom = FILL_Y(row + 1); 6036 6037 /* Cache the created brush, that saves a lot of time. We need two: 6038 * one for cursor background and one for the normal background. */ 6039 if (gui.currBgColor == brush_color[0]) 6040 { 6041 hbr = hbr_cache[0]; 6042 brush_lru = 1; 6043 } 6044 else if (gui.currBgColor == brush_color[1]) 6045 { 6046 hbr = hbr_cache[1]; 6047 brush_lru = 0; 6048 } 6049 else 6050 { 6051 if (hbr_cache[brush_lru] != NULL) 6052 DeleteBrush(hbr_cache[brush_lru]); 6053 hbr_cache[brush_lru] = CreateSolidBrush(gui.currBgColor); 6054 brush_color[brush_lru] = gui.currBgColor; 6055 hbr = hbr_cache[brush_lru]; 6056 brush_lru = !brush_lru; 6057 } 6058 6059 fill_rect(&rc, hbr, gui.currBgColor); 6060 6061 SetBkMode(s_hdc, TRANSPARENT); 6062 6063 /* 6064 * When drawing block cursor, prevent inverted character spilling 6065 * over character cell (can happen with bold/italic) 6066 */ 6067 if (flags & DRAW_CURSOR) 6068 { 6069 pcliprect = &rc; 6070 foptions = ETO_CLIPPED; 6071 } 6072 } 6073 SetTextColor(s_hdc, gui.currFgColor); 6074 SelectFont(s_hdc, gui.currFont); 6075 6076 #ifdef FEAT_DIRECTX 6077 if (IS_ENABLE_DIRECTX()) 6078 DWriteContext_SetFont(s_dwc, (HFONT)gui.currFont); 6079 #endif 6080 6081 if (pad_size != Columns || padding == NULL || padding[0] != gui.char_width) 6082 { 6083 vim_free(padding); 6084 pad_size = Columns; 6085 6086 /* Don't give an out-of-memory message here, it would call us 6087 * recursively. */ 6088 padding = (int *)lalloc(pad_size * sizeof(int), FALSE); 6089 if (padding != NULL) 6090 for (i = 0; i < pad_size; i++) 6091 padding[i] = gui.char_width; 6092 } 6093 6094 /* 6095 * We have to provide the padding argument because italic and bold versions 6096 * of fixed-width fonts are often one pixel or so wider than their normal 6097 * versions. 6098 * No check for DRAW_BOLD, Windows will have done it already. 6099 */ 6100 6101 /* Check if there are any UTF-8 characters. If not, use normal text 6102 * output to speed up output. */ 6103 if (enc_utf8) 6104 for (n = 0; n < len; ++n) 6105 if (text[n] >= 0x80) 6106 break; 6107 6108 #if defined(FEAT_DIRECTX) 6109 /* Quick hack to enable DirectWrite. To use DirectWrite (antialias), it is 6110 * required that unicode drawing routine, currently. So this forces it 6111 * enabled. */ 6112 if (IS_ENABLE_DIRECTX()) 6113 n = 0; /* Keep n < len, to enter block for unicode. */ 6114 #endif 6115 6116 /* Check if the Unicode buffer exists and is big enough. Create it 6117 * with the same length as the multi-byte string, the number of wide 6118 * characters is always equal or smaller. */ 6119 if ((enc_utf8 6120 || (enc_codepage > 0 && (int)GetACP() != enc_codepage) 6121 || enc_latin9) 6122 && (unicodebuf == NULL || len > unibuflen)) 6123 { 6124 vim_free(unicodebuf); 6125 unicodebuf = (WCHAR *)lalloc(len * sizeof(WCHAR), FALSE); 6126 6127 vim_free(unicodepdy); 6128 unicodepdy = (int *)lalloc(len * sizeof(int), FALSE); 6129 6130 unibuflen = len; 6131 } 6132 6133 if (enc_utf8 && n < len && unicodebuf != NULL) 6134 { 6135 /* Output UTF-8 characters. Composing characters should be 6136 * handled here. */ 6137 int i; 6138 int wlen; /* string length in words */ 6139 int clen; /* string length in characters */ 6140 int cells; /* cell width of string up to composing char */ 6141 int cw; /* width of current cell */ 6142 int c; 6143 6144 wlen = 0; 6145 clen = 0; 6146 cells = 0; 6147 for (i = 0; i < len; ) 6148 { 6149 c = utf_ptr2char(text + i); 6150 if (c >= 0x10000) 6151 { 6152 /* Turn into UTF-16 encoding. */ 6153 unicodebuf[wlen++] = ((c - 0x10000) >> 10) + 0xD800; 6154 unicodebuf[wlen++] = ((c - 0x10000) & 0x3ff) + 0xDC00; 6155 } 6156 else 6157 { 6158 unicodebuf[wlen++] = c; 6159 } 6160 6161 if (utf_iscomposing(c)) 6162 cw = 0; 6163 else 6164 { 6165 cw = utf_char2cells(c); 6166 if (cw > 2) /* don't use 4 for unprintable char */ 6167 cw = 1; 6168 } 6169 6170 if (unicodepdy != NULL) 6171 { 6172 /* Use unicodepdy to make characters fit as we expect, even 6173 * when the font uses different widths (e.g., bold character 6174 * is wider). */ 6175 if (c >= 0x10000) 6176 { 6177 unicodepdy[wlen - 2] = cw * gui.char_width; 6178 unicodepdy[wlen - 1] = 0; 6179 } 6180 else 6181 unicodepdy[wlen - 1] = cw * gui.char_width; 6182 } 6183 cells += cw; 6184 i += utf_ptr2len_len(text + i, len - i); 6185 ++clen; 6186 } 6187 #if defined(FEAT_DIRECTX) 6188 if (IS_ENABLE_DIRECTX()) 6189 { 6190 /* Add one to "cells" for italics. */ 6191 DWriteContext_DrawText(s_dwc, unicodebuf, wlen, 6192 TEXT_X(col), TEXT_Y(row), 6193 FILL_X(cells + 1), FILL_Y(1) - p_linespace, 6194 gui.char_width, gui.currFgColor, 6195 foptions, pcliprect, unicodepdy); 6196 } 6197 else 6198 #endif 6199 ExtTextOutW(s_hdc, TEXT_X(col), TEXT_Y(row), 6200 foptions, pcliprect, unicodebuf, wlen, unicodepdy); 6201 len = cells; /* used for underlining */ 6202 } 6203 else if ((enc_codepage > 0 && (int)GetACP() != enc_codepage) || enc_latin9) 6204 { 6205 /* If we want to display codepage data, and the current CP is not the 6206 * ANSI one, we need to go via Unicode. */ 6207 if (unicodebuf != NULL) 6208 { 6209 if (enc_latin9) 6210 latin9_to_ucs(text, len, unicodebuf); 6211 else 6212 len = MultiByteToWideChar(enc_codepage, 6213 MB_PRECOMPOSED, 6214 (char *)text, len, 6215 (LPWSTR)unicodebuf, unibuflen); 6216 if (len != 0) 6217 { 6218 /* Use unicodepdy to make characters fit as we expect, even 6219 * when the font uses different widths (e.g., bold character 6220 * is wider). */ 6221 if (unicodepdy != NULL) 6222 { 6223 int i; 6224 int cw; 6225 6226 for (i = 0; i < len; ++i) 6227 { 6228 cw = utf_char2cells(unicodebuf[i]); 6229 if (cw > 2) 6230 cw = 1; 6231 unicodepdy[i] = cw * gui.char_width; 6232 } 6233 } 6234 ExtTextOutW(s_hdc, TEXT_X(col), TEXT_Y(row), 6235 foptions, pcliprect, unicodebuf, len, unicodepdy); 6236 } 6237 } 6238 } 6239 else 6240 { 6241 #ifdef FEAT_RIGHTLEFT 6242 /* Windows will mess up RL text, so we have to draw it character by 6243 * character. Only do this if RL is on, since it's slow. */ 6244 if (curwin->w_p_rl) 6245 RevOut(s_hdc, TEXT_X(col), TEXT_Y(row), 6246 foptions, pcliprect, (char *)text, len, padding); 6247 else 6248 #endif 6249 ExtTextOut(s_hdc, TEXT_X(col), TEXT_Y(row), 6250 foptions, pcliprect, (char *)text, len, padding); 6251 } 6252 6253 /* Underline */ 6254 if (flags & DRAW_UNDERL) 6255 { 6256 /* When p_linespace is 0, overwrite the bottom row of pixels. 6257 * Otherwise put the line just below the character. */ 6258 y = FILL_Y(row + 1) - 1; 6259 if (p_linespace > 1) 6260 y -= p_linespace - 1; 6261 draw_line(FILL_X(col), y, FILL_X(col + len), y, gui.currFgColor); 6262 } 6263 6264 /* Strikethrough */ 6265 if (flags & DRAW_STRIKE) 6266 { 6267 y = FILL_Y(row + 1) - gui.char_height/2; 6268 draw_line(FILL_X(col), y, FILL_X(col + len), y, gui.currSpColor); 6269 } 6270 6271 /* Undercurl */ 6272 if (flags & DRAW_UNDERC) 6273 { 6274 int x; 6275 int offset; 6276 static const int val[8] = {1, 0, 0, 0, 1, 2, 2, 2 }; 6277 6278 y = FILL_Y(row + 1) - 1; 6279 for (x = FILL_X(col); x < FILL_X(col + len); ++x) 6280 { 6281 offset = val[x % 8]; 6282 set_pixel(x, y - offset, gui.currSpColor); 6283 } 6284 } 6285 } 6286 6287 6288 /* 6289 * Output routines. 6290 */ 6291 6292 /* Flush any output to the screen */ 6293 void 6294 gui_mch_flush(void) 6295 { 6296 # if defined(__BORLANDC__) 6297 /* 6298 * The GdiFlush declaration (in Borland C 5.01 <wingdi.h>) is not a 6299 * prototype declaration. 6300 * The compiler complains if __stdcall is not used in both declarations. 6301 */ 6302 BOOL __stdcall GdiFlush(void); 6303 # endif 6304 6305 #if defined(FEAT_DIRECTX) 6306 if (IS_ENABLE_DIRECTX()) 6307 DWriteContext_Flush(s_dwc); 6308 #endif 6309 6310 GdiFlush(); 6311 } 6312 6313 static void 6314 clear_rect(RECT *rcp) 6315 { 6316 fill_rect(rcp, NULL, gui.back_pixel); 6317 } 6318 6319 6320 void 6321 gui_mch_get_screen_dimensions(int *screen_w, int *screen_h) 6322 { 6323 RECT workarea_rect; 6324 6325 get_work_area(&workarea_rect); 6326 6327 *screen_w = workarea_rect.right - workarea_rect.left 6328 - (GetSystemMetrics(SM_CXFRAME) + 6329 GetSystemMetrics(SM_CXPADDEDBORDER)) * 2; 6330 6331 /* FIXME: dirty trick: Because the gui_get_base_height() doesn't include 6332 * the menubar for MSwin, we subtract it from the screen height, so that 6333 * the window size can be made to fit on the screen. */ 6334 *screen_h = workarea_rect.bottom - workarea_rect.top 6335 - (GetSystemMetrics(SM_CYFRAME) + 6336 GetSystemMetrics(SM_CXPADDEDBORDER)) * 2 6337 - GetSystemMetrics(SM_CYCAPTION) 6338 #ifdef FEAT_MENU 6339 - gui_mswin_get_menu_height(FALSE) 6340 #endif 6341 ; 6342 } 6343 6344 6345 #if defined(FEAT_MENU) || defined(PROTO) 6346 /* 6347 * Add a sub menu to the menu bar. 6348 */ 6349 void 6350 gui_mch_add_menu( 6351 vimmenu_T *menu, 6352 int pos) 6353 { 6354 vimmenu_T *parent = menu->parent; 6355 6356 menu->submenu_id = CreatePopupMenu(); 6357 menu->id = s_menu_id++; 6358 6359 if (menu_is_menubar(menu->name)) 6360 { 6361 WCHAR *wn = NULL; 6362 6363 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage) 6364 { 6365 /* 'encoding' differs from active codepage: convert menu name 6366 * and use wide function */ 6367 wn = enc_to_utf16(menu->name, NULL); 6368 if (wn != NULL) 6369 { 6370 MENUITEMINFOW infow; 6371 6372 infow.cbSize = sizeof(infow); 6373 infow.fMask = MIIM_DATA | MIIM_TYPE | MIIM_ID 6374 | MIIM_SUBMENU; 6375 infow.dwItemData = (long_u)menu; 6376 infow.wID = menu->id; 6377 infow.fType = MFT_STRING; 6378 infow.dwTypeData = wn; 6379 infow.cch = (UINT)wcslen(wn); 6380 infow.hSubMenu = menu->submenu_id; 6381 InsertMenuItemW((parent == NULL) 6382 ? s_menuBar : parent->submenu_id, 6383 (UINT)pos, TRUE, &infow); 6384 vim_free(wn); 6385 } 6386 } 6387 6388 if (wn == NULL) 6389 { 6390 MENUITEMINFO info; 6391 6392 info.cbSize = sizeof(info); 6393 info.fMask = MIIM_DATA | MIIM_TYPE | MIIM_ID | MIIM_SUBMENU; 6394 info.dwItemData = (long_u)menu; 6395 info.wID = menu->id; 6396 info.fType = MFT_STRING; 6397 info.dwTypeData = (LPTSTR)menu->name; 6398 info.cch = (UINT)STRLEN(menu->name); 6399 info.hSubMenu = menu->submenu_id; 6400 InsertMenuItem((parent == NULL) 6401 ? s_menuBar : parent->submenu_id, 6402 (UINT)pos, TRUE, &info); 6403 } 6404 } 6405 6406 /* Fix window size if menu may have wrapped */ 6407 if (parent == NULL) 6408 gui_mswin_get_menu_height(!gui.starting); 6409 #ifdef FEAT_TEAROFF 6410 else if (IsWindow(parent->tearoff_handle)) 6411 rebuild_tearoff(parent); 6412 #endif 6413 } 6414 6415 void 6416 gui_mch_show_popupmenu(vimmenu_T *menu) 6417 { 6418 POINT mp; 6419 6420 (void)GetCursorPos((LPPOINT)&mp); 6421 gui_mch_show_popupmenu_at(menu, (int)mp.x, (int)mp.y); 6422 } 6423 6424 void 6425 gui_make_popup(char_u *path_name, int mouse_pos) 6426 { 6427 vimmenu_T *menu = gui_find_menu(path_name); 6428 6429 if (menu != NULL) 6430 { 6431 POINT p; 6432 6433 /* Find the position of the current cursor */ 6434 GetDCOrgEx(s_hdc, &p); 6435 if (mouse_pos) 6436 { 6437 int mx, my; 6438 6439 gui_mch_getmouse(&mx, &my); 6440 p.x += mx; 6441 p.y += my; 6442 } 6443 else if (curwin != NULL) 6444 { 6445 p.x += TEXT_X(curwin->w_wincol + curwin->w_wcol + 1); 6446 p.y += TEXT_Y(W_WINROW(curwin) + curwin->w_wrow + 1); 6447 } 6448 msg_scroll = FALSE; 6449 gui_mch_show_popupmenu_at(menu, (int)p.x, (int)p.y); 6450 } 6451 } 6452 6453 #if defined(FEAT_TEAROFF) || defined(PROTO) 6454 /* 6455 * Given a menu descriptor, e.g. "File.New", find it in the menu hierarchy and 6456 * create it as a pseudo-"tearoff menu". 6457 */ 6458 void 6459 gui_make_tearoff(char_u *path_name) 6460 { 6461 vimmenu_T *menu = gui_find_menu(path_name); 6462 6463 /* Found the menu, so tear it off. */ 6464 if (menu != NULL) 6465 gui_mch_tearoff(menu->dname, menu, 0xffffL, 0xffffL); 6466 } 6467 #endif 6468 6469 /* 6470 * Add a menu item to a menu 6471 */ 6472 void 6473 gui_mch_add_menu_item( 6474 vimmenu_T *menu, 6475 int idx) 6476 { 6477 vimmenu_T *parent = menu->parent; 6478 6479 menu->id = s_menu_id++; 6480 menu->submenu_id = NULL; 6481 6482 #ifdef FEAT_TEAROFF 6483 if (STRNCMP(menu->name, TEAR_STRING, TEAR_LEN) == 0) 6484 { 6485 InsertMenu(parent->submenu_id, (UINT)idx, MF_BITMAP|MF_BYPOSITION, 6486 (UINT)menu->id, (LPCTSTR) s_htearbitmap); 6487 } 6488 else 6489 #endif 6490 #ifdef FEAT_TOOLBAR 6491 if (menu_is_toolbar(parent->name)) 6492 { 6493 TBBUTTON newtb; 6494 6495 vim_memset(&newtb, 0, sizeof(newtb)); 6496 if (menu_is_separator(menu->name)) 6497 { 6498 newtb.iBitmap = 0; 6499 newtb.fsStyle = TBSTYLE_SEP; 6500 } 6501 else 6502 { 6503 newtb.iBitmap = get_toolbar_bitmap(menu); 6504 newtb.fsStyle = TBSTYLE_BUTTON; 6505 } 6506 newtb.idCommand = menu->id; 6507 newtb.fsState = TBSTATE_ENABLED; 6508 newtb.iString = 0; 6509 SendMessage(s_toolbarhwnd, TB_INSERTBUTTON, (WPARAM)idx, 6510 (LPARAM)&newtb); 6511 menu->submenu_id = (HMENU)-1; 6512 } 6513 else 6514 #endif 6515 { 6516 WCHAR *wn = NULL; 6517 6518 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage) 6519 { 6520 /* 'encoding' differs from active codepage: convert menu item name 6521 * and use wide function */ 6522 wn = enc_to_utf16(menu->name, NULL); 6523 if (wn != NULL) 6524 { 6525 InsertMenuW(parent->submenu_id, (UINT)idx, 6526 (menu_is_separator(menu->name) 6527 ? MF_SEPARATOR : MF_STRING) | MF_BYPOSITION, 6528 (UINT)menu->id, wn); 6529 vim_free(wn); 6530 } 6531 } 6532 if (wn == NULL) 6533 InsertMenu(parent->submenu_id, (UINT)idx, 6534 (menu_is_separator(menu->name) ? MF_SEPARATOR : MF_STRING) 6535 | MF_BYPOSITION, 6536 (UINT)menu->id, (LPCTSTR)menu->name); 6537 #ifdef FEAT_TEAROFF 6538 if (IsWindow(parent->tearoff_handle)) 6539 rebuild_tearoff(parent); 6540 #endif 6541 } 6542 } 6543 6544 /* 6545 * Destroy the machine specific menu widget. 6546 */ 6547 void 6548 gui_mch_destroy_menu(vimmenu_T *menu) 6549 { 6550 #ifdef FEAT_TOOLBAR 6551 /* 6552 * is this a toolbar button? 6553 */ 6554 if (menu->submenu_id == (HMENU)-1) 6555 { 6556 int iButton; 6557 6558 iButton = (int)SendMessage(s_toolbarhwnd, TB_COMMANDTOINDEX, 6559 (WPARAM)menu->id, 0); 6560 SendMessage(s_toolbarhwnd, TB_DELETEBUTTON, (WPARAM)iButton, 0); 6561 } 6562 else 6563 #endif 6564 { 6565 if (menu->parent != NULL 6566 && menu_is_popup(menu->parent->dname) 6567 && menu->parent->submenu_id != NULL) 6568 RemoveMenu(menu->parent->submenu_id, menu->id, MF_BYCOMMAND); 6569 else 6570 RemoveMenu(s_menuBar, menu->id, MF_BYCOMMAND); 6571 if (menu->submenu_id != NULL) 6572 DestroyMenu(menu->submenu_id); 6573 #ifdef FEAT_TEAROFF 6574 if (IsWindow(menu->tearoff_handle)) 6575 DestroyWindow(menu->tearoff_handle); 6576 if (menu->parent != NULL 6577 && menu->parent->children != NULL 6578 && IsWindow(menu->parent->tearoff_handle)) 6579 { 6580 /* This menu must not show up when rebuilding the tearoff window. */ 6581 menu->modes = 0; 6582 rebuild_tearoff(menu->parent); 6583 } 6584 #endif 6585 } 6586 } 6587 6588 #ifdef FEAT_TEAROFF 6589 static void 6590 rebuild_tearoff(vimmenu_T *menu) 6591 { 6592 /*hackish*/ 6593 char_u tbuf[128]; 6594 RECT trect; 6595 RECT rct; 6596 RECT roct; 6597 int x, y; 6598 6599 HWND thwnd = menu->tearoff_handle; 6600 6601 GetWindowText(thwnd, (LPSTR)tbuf, 127); 6602 if (GetWindowRect(thwnd, &trect) 6603 && GetWindowRect(s_hwnd, &rct) 6604 && GetClientRect(s_hwnd, &roct)) 6605 { 6606 x = trect.left - rct.left; 6607 y = (trect.top - rct.bottom + roct.bottom); 6608 } 6609 else 6610 { 6611 x = y = 0xffffL; 6612 } 6613 DestroyWindow(thwnd); 6614 if (menu->children != NULL) 6615 { 6616 gui_mch_tearoff(tbuf, menu, x, y); 6617 if (IsWindow(menu->tearoff_handle)) 6618 (void) SetWindowPos(menu->tearoff_handle, 6619 NULL, 6620 (int)trect.left, 6621 (int)trect.top, 6622 0, 0, 6623 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); 6624 } 6625 } 6626 #endif /* FEAT_TEAROFF */ 6627 6628 /* 6629 * Make a menu either grey or not grey. 6630 */ 6631 void 6632 gui_mch_menu_grey( 6633 vimmenu_T *menu, 6634 int grey) 6635 { 6636 #ifdef FEAT_TOOLBAR 6637 /* 6638 * is this a toolbar button? 6639 */ 6640 if (menu->submenu_id == (HMENU)-1) 6641 { 6642 SendMessage(s_toolbarhwnd, TB_ENABLEBUTTON, 6643 (WPARAM)menu->id, (LPARAM) MAKELONG((grey ? FALSE : TRUE), 0) ); 6644 } 6645 else 6646 #endif 6647 (void)EnableMenuItem(menu->parent ? menu->parent->submenu_id : s_menuBar, 6648 menu->id, MF_BYCOMMAND | (grey ? MF_GRAYED : MF_ENABLED)); 6649 6650 #ifdef FEAT_TEAROFF 6651 if ((menu->parent != NULL) && (IsWindow(menu->parent->tearoff_handle))) 6652 { 6653 WORD menuID; 6654 HWND menuHandle; 6655 6656 /* 6657 * A tearoff button has changed state. 6658 */ 6659 if (menu->children == NULL) 6660 menuID = (WORD)(menu->id); 6661 else 6662 menuID = (WORD)((long_u)(menu->submenu_id) | (DWORD)0x8000); 6663 menuHandle = GetDlgItem(menu->parent->tearoff_handle, menuID); 6664 if (menuHandle) 6665 EnableWindow(menuHandle, !grey); 6666 6667 } 6668 #endif 6669 } 6670 6671 #endif /* FEAT_MENU */ 6672 6673 6674 /* define some macros used to make the dialogue creation more readable */ 6675 6676 #define add_string(s) strcpy((LPSTR)p, s); (LPSTR)p += (strlen((LPSTR)p) + 1) 6677 #define add_word(x) *p++ = (x) 6678 #define add_long(x) dwp = (DWORD *)p; *dwp++ = (x); p = (WORD *)dwp 6679 6680 #if defined(FEAT_GUI_DIALOG) || defined(PROTO) 6681 /* 6682 * stuff for dialogs 6683 */ 6684 6685 /* 6686 * The callback routine used by all the dialogs. Very simple. First, 6687 * acknowledges the INITDIALOG message so that Windows knows to do standard 6688 * dialog stuff (Return = default, Esc = cancel....) Second, if a button is 6689 * pressed, return that button's ID - IDCANCEL (2), which is the button's 6690 * number. 6691 */ 6692 static LRESULT CALLBACK 6693 dialog_callback( 6694 HWND hwnd, 6695 UINT message, 6696 WPARAM wParam, 6697 LPARAM lParam UNUSED) 6698 { 6699 if (message == WM_INITDIALOG) 6700 { 6701 CenterWindow(hwnd, GetWindow(hwnd, GW_OWNER)); 6702 /* Set focus to the dialog. Set the default button, if specified. */ 6703 (void)SetFocus(hwnd); 6704 if (dialog_default_button > IDCANCEL) 6705 (void)SetFocus(GetDlgItem(hwnd, dialog_default_button)); 6706 else 6707 /* We don't have a default, set focus on another element of the 6708 * dialog window, probably the icon */ 6709 (void)SetFocus(GetDlgItem(hwnd, DLG_NONBUTTON_CONTROL)); 6710 return FALSE; 6711 } 6712 6713 if (message == WM_COMMAND) 6714 { 6715 int button = LOWORD(wParam); 6716 6717 /* Don't end the dialog if something was selected that was 6718 * not a button. 6719 */ 6720 if (button >= DLG_NONBUTTON_CONTROL) 6721 return TRUE; 6722 6723 /* If the edit box exists, copy the string. */ 6724 if (s_textfield != NULL) 6725 { 6726 /* If the OS is Windows NT, and 'encoding' differs from active 6727 * codepage: use wide function and convert text. */ 6728 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage) 6729 { 6730 WCHAR *wp = (WCHAR *)alloc(IOSIZE * sizeof(WCHAR)); 6731 char_u *p; 6732 6733 GetDlgItemTextW(hwnd, DLG_NONBUTTON_CONTROL + 2, wp, IOSIZE); 6734 p = utf16_to_enc(wp, NULL); 6735 vim_strncpy(s_textfield, p, IOSIZE); 6736 vim_free(p); 6737 vim_free(wp); 6738 } 6739 else 6740 GetDlgItemText(hwnd, DLG_NONBUTTON_CONTROL + 2, 6741 (LPSTR)s_textfield, IOSIZE); 6742 } 6743 6744 /* 6745 * Need to check for IDOK because if the user just hits Return to 6746 * accept the default value, some reason this is what we get. 6747 */ 6748 if (button == IDOK) 6749 { 6750 if (dialog_default_button > IDCANCEL) 6751 EndDialog(hwnd, dialog_default_button); 6752 } 6753 else 6754 EndDialog(hwnd, button - IDCANCEL); 6755 return TRUE; 6756 } 6757 6758 if ((message == WM_SYSCOMMAND) && (wParam == SC_CLOSE)) 6759 { 6760 EndDialog(hwnd, 0); 6761 return TRUE; 6762 } 6763 return FALSE; 6764 } 6765 6766 /* 6767 * Create a dialog dynamically from the parameter strings. 6768 * type = type of dialog (question, alert, etc.) 6769 * title = dialog title. may be NULL for default title. 6770 * message = text to display. Dialog sizes to accommodate it. 6771 * buttons = '\n' separated list of button captions, default first. 6772 * dfltbutton = number of default button. 6773 * 6774 * This routine returns 1 if the first button is pressed, 6775 * 2 for the second, etc. 6776 * 6777 * 0 indicates Esc was pressed. 6778 * -1 for unexpected error 6779 * 6780 * If stubbing out this fn, return 1. 6781 */ 6782 6783 static const char *dlg_icons[] = /* must match names in resource file */ 6784 { 6785 "IDR_VIM", 6786 "IDR_VIM_ERROR", 6787 "IDR_VIM_ALERT", 6788 "IDR_VIM_INFO", 6789 "IDR_VIM_QUESTION" 6790 }; 6791 6792 int 6793 gui_mch_dialog( 6794 int type, 6795 char_u *title, 6796 char_u *message, 6797 char_u *buttons, 6798 int dfltbutton, 6799 char_u *textfield, 6800 int ex_cmd) 6801 { 6802 WORD *p, *pdlgtemplate, *pnumitems; 6803 DWORD *dwp; 6804 int numButtons; 6805 int *buttonWidths, *buttonPositions; 6806 int buttonYpos; 6807 int nchar, i; 6808 DWORD lStyle; 6809 int dlgwidth = 0; 6810 int dlgheight; 6811 int editboxheight; 6812 int horizWidth = 0; 6813 int msgheight; 6814 char_u *pstart; 6815 char_u *pend; 6816 char_u *last_white; 6817 char_u *tbuffer; 6818 RECT rect; 6819 HWND hwnd; 6820 HDC hdc; 6821 HFONT font, oldFont; 6822 TEXTMETRIC fontInfo; 6823 int fontHeight; 6824 int textWidth, minButtonWidth, messageWidth; 6825 int maxDialogWidth; 6826 int maxDialogHeight; 6827 int scroll_flag = 0; 6828 int vertical; 6829 int dlgPaddingX; 6830 int dlgPaddingY; 6831 #ifdef USE_SYSMENU_FONT 6832 LOGFONT lfSysmenu; 6833 int use_lfSysmenu = FALSE; 6834 #endif 6835 garray_T ga; 6836 int l; 6837 6838 #ifndef NO_CONSOLE 6839 /* Don't output anything in silent mode ("ex -s") */ 6840 if (silent_mode) 6841 return dfltbutton; /* return default option */ 6842 #endif 6843 6844 if (s_hwnd == NULL) 6845 get_dialog_font_metrics(); 6846 6847 if ((type < 0) || (type > VIM_LAST_TYPE)) 6848 type = 0; 6849 6850 /* allocate some memory for dialog template */ 6851 /* TODO should compute this really */ 6852 pdlgtemplate = p = (PWORD)LocalAlloc(LPTR, 6853 DLG_ALLOC_SIZE + STRLEN(message) * 2); 6854 6855 if (p == NULL) 6856 return -1; 6857 6858 /* 6859 * make a copy of 'buttons' to fiddle with it. compiler grizzles because 6860 * vim_strsave() doesn't take a const arg (why not?), so cast away the 6861 * const. 6862 */ 6863 tbuffer = vim_strsave(buttons); 6864 if (tbuffer == NULL) 6865 return -1; 6866 6867 --dfltbutton; /* Change from one-based to zero-based */ 6868 6869 /* Count buttons */ 6870 numButtons = 1; 6871 for (i = 0; tbuffer[i] != '\0'; i++) 6872 { 6873 if (tbuffer[i] == DLG_BUTTON_SEP) 6874 numButtons++; 6875 } 6876 if (dfltbutton >= numButtons) 6877 dfltbutton = -1; 6878 6879 /* Allocate array to hold the width of each button */ 6880 buttonWidths = (int *)lalloc(numButtons * sizeof(int), TRUE); 6881 if (buttonWidths == NULL) 6882 return -1; 6883 6884 /* Allocate array to hold the X position of each button */ 6885 buttonPositions = (int *)lalloc(numButtons * sizeof(int), TRUE); 6886 if (buttonPositions == NULL) 6887 return -1; 6888 6889 /* 6890 * Calculate how big the dialog must be. 6891 */ 6892 hwnd = GetDesktopWindow(); 6893 hdc = GetWindowDC(hwnd); 6894 #ifdef USE_SYSMENU_FONT 6895 if (gui_w32_get_menu_font(&lfSysmenu) == OK) 6896 { 6897 font = CreateFontIndirect(&lfSysmenu); 6898 use_lfSysmenu = TRUE; 6899 } 6900 else 6901 #endif 6902 font = CreateFont(-DLG_FONT_POINT_SIZE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6903 VARIABLE_PITCH , DLG_FONT_NAME); 6904 if (s_usenewlook) 6905 { 6906 oldFont = SelectFont(hdc, font); 6907 dlgPaddingX = DLG_PADDING_X; 6908 dlgPaddingY = DLG_PADDING_Y; 6909 } 6910 else 6911 { 6912 oldFont = SelectFont(hdc, GetStockObject(SYSTEM_FONT)); 6913 dlgPaddingX = DLG_OLD_STYLE_PADDING_X; 6914 dlgPaddingY = DLG_OLD_STYLE_PADDING_Y; 6915 } 6916 GetTextMetrics(hdc, &fontInfo); 6917 fontHeight = fontInfo.tmHeight; 6918 6919 /* Minimum width for horizontal button */ 6920 minButtonWidth = GetTextWidth(hdc, (char_u *)"Cancel", 6); 6921 6922 /* Maximum width of a dialog, if possible */ 6923 if (s_hwnd == NULL) 6924 { 6925 RECT workarea_rect; 6926 6927 /* We don't have a window, use the desktop area. */ 6928 get_work_area(&workarea_rect); 6929 maxDialogWidth = workarea_rect.right - workarea_rect.left - 100; 6930 if (maxDialogWidth > 600) 6931 maxDialogWidth = 600; 6932 /* Leave some room for the taskbar. */ 6933 maxDialogHeight = workarea_rect.bottom - workarea_rect.top - 150; 6934 } 6935 else 6936 { 6937 /* Use our own window for the size, unless it's very small. */ 6938 GetWindowRect(s_hwnd, &rect); 6939 maxDialogWidth = rect.right - rect.left 6940 - (GetSystemMetrics(SM_CXFRAME) + 6941 GetSystemMetrics(SM_CXPADDEDBORDER)) * 2; 6942 if (maxDialogWidth < DLG_MIN_MAX_WIDTH) 6943 maxDialogWidth = DLG_MIN_MAX_WIDTH; 6944 6945 maxDialogHeight = rect.bottom - rect.top 6946 - (GetSystemMetrics(SM_CYFRAME) + 6947 GetSystemMetrics(SM_CXPADDEDBORDER)) * 4 6948 - GetSystemMetrics(SM_CYCAPTION); 6949 if (maxDialogHeight < DLG_MIN_MAX_HEIGHT) 6950 maxDialogHeight = DLG_MIN_MAX_HEIGHT; 6951 } 6952 6953 /* Set dlgwidth to width of message. 6954 * Copy the message into "ga", changing NL to CR-NL and inserting line 6955 * breaks where needed. */ 6956 pstart = message; 6957 messageWidth = 0; 6958 msgheight = 0; 6959 ga_init2(&ga, sizeof(char), 500); 6960 do 6961 { 6962 msgheight += fontHeight; /* at least one line */ 6963 6964 /* Need to figure out where to break the string. The system does it 6965 * at a word boundary, which would mean we can't compute the number of 6966 * wrapped lines. */ 6967 textWidth = 0; 6968 last_white = NULL; 6969 for (pend = pstart; *pend != NUL && *pend != '\n'; ) 6970 { 6971 l = (*mb_ptr2len)(pend); 6972 if (l == 1 && VIM_ISWHITE(*pend) 6973 && textWidth > maxDialogWidth * 3 / 4) 6974 last_white = pend; 6975 textWidth += GetTextWidthEnc(hdc, pend, l); 6976 if (textWidth >= maxDialogWidth) 6977 { 6978 /* Line will wrap. */ 6979 messageWidth = maxDialogWidth; 6980 msgheight += fontHeight; 6981 textWidth = 0; 6982 6983 if (last_white != NULL) 6984 { 6985 /* break the line just after a space */ 6986 ga.ga_len -= (int)(pend - (last_white + 1)); 6987 pend = last_white + 1; 6988 last_white = NULL; 6989 } 6990 ga_append(&ga, '\r'); 6991 ga_append(&ga, '\n'); 6992 continue; 6993 } 6994 6995 while (--l >= 0) 6996 ga_append(&ga, *pend++); 6997 } 6998 if (textWidth > messageWidth) 6999 messageWidth = textWidth; 7000 7001 ga_append(&ga, '\r'); 7002 ga_append(&ga, '\n'); 7003 pstart = pend + 1; 7004 } while (*pend != NUL); 7005 7006 if (ga.ga_data != NULL) 7007 message = ga.ga_data; 7008 7009 messageWidth += 10; /* roundoff space */ 7010 7011 /* Add width of icon to dlgwidth, and some space */ 7012 dlgwidth = messageWidth + DLG_ICON_WIDTH + 3 * dlgPaddingX 7013 + GetSystemMetrics(SM_CXVSCROLL); 7014 7015 if (msgheight < DLG_ICON_HEIGHT) 7016 msgheight = DLG_ICON_HEIGHT; 7017 7018 /* 7019 * Check button names. A long one will make the dialog wider. 7020 * When called early (-register error message) p_go isn't initialized. 7021 */ 7022 vertical = (p_go != NULL && vim_strchr(p_go, GO_VERTICAL) != NULL); 7023 if (!vertical) 7024 { 7025 // Place buttons horizontally if they fit. 7026 horizWidth = dlgPaddingX; 7027 pstart = tbuffer; 7028 i = 0; 7029 do 7030 { 7031 pend = vim_strchr(pstart, DLG_BUTTON_SEP); 7032 if (pend == NULL) 7033 pend = pstart + STRLEN(pstart); // Last button name. 7034 textWidth = GetTextWidthEnc(hdc, pstart, (int)(pend - pstart)); 7035 if (textWidth < minButtonWidth) 7036 textWidth = minButtonWidth; 7037 textWidth += dlgPaddingX; /* Padding within button */ 7038 buttonWidths[i] = textWidth; 7039 buttonPositions[i++] = horizWidth; 7040 horizWidth += textWidth + dlgPaddingX; /* Pad between buttons */ 7041 pstart = pend + 1; 7042 } while (*pend != NUL); 7043 7044 if (horizWidth > maxDialogWidth) 7045 vertical = TRUE; // Too wide to fit on the screen. 7046 else if (horizWidth > dlgwidth) 7047 dlgwidth = horizWidth; 7048 } 7049 7050 if (vertical) 7051 { 7052 // Stack buttons vertically. 7053 pstart = tbuffer; 7054 do 7055 { 7056 pend = vim_strchr(pstart, DLG_BUTTON_SEP); 7057 if (pend == NULL) 7058 pend = pstart + STRLEN(pstart); // Last button name. 7059 textWidth = GetTextWidthEnc(hdc, pstart, (int)(pend - pstart)); 7060 textWidth += dlgPaddingX; /* Padding within button */ 7061 textWidth += DLG_VERT_PADDING_X * 2; /* Padding around button */ 7062 if (textWidth > dlgwidth) 7063 dlgwidth = textWidth; 7064 pstart = pend + 1; 7065 } while (*pend != NUL); 7066 } 7067 7068 if (dlgwidth < DLG_MIN_WIDTH) 7069 dlgwidth = DLG_MIN_WIDTH; /* Don't allow a really thin dialog!*/ 7070 7071 /* start to fill in the dlgtemplate information. addressing by WORDs */ 7072 if (s_usenewlook) 7073 lStyle = DS_MODALFRAME | WS_CAPTION |DS_3DLOOK| WS_VISIBLE |DS_SETFONT; 7074 else 7075 lStyle = DS_MODALFRAME | WS_CAPTION |DS_3DLOOK| WS_VISIBLE; 7076 7077 add_long(lStyle); 7078 add_long(0); // (lExtendedStyle) 7079 pnumitems = p; /*save where the number of items must be stored*/ 7080 add_word(0); // NumberOfItems(will change later) 7081 add_word(10); // x 7082 add_word(10); // y 7083 add_word(PixelToDialogX(dlgwidth)); // cx 7084 7085 // Dialog height. 7086 if (vertical) 7087 dlgheight = msgheight + 2 * dlgPaddingY 7088 + DLG_VERT_PADDING_Y + 2 * fontHeight * numButtons; 7089 else 7090 dlgheight = msgheight + 3 * dlgPaddingY + 2 * fontHeight; 7091 7092 // Dialog needs to be taller if contains an edit box. 7093 editboxheight = fontHeight + dlgPaddingY + 4 * DLG_VERT_PADDING_Y; 7094 if (textfield != NULL) 7095 dlgheight += editboxheight; 7096 7097 /* Restrict the size to a maximum. Causes a scrollbar to show up. */ 7098 if (dlgheight > maxDialogHeight) 7099 { 7100 msgheight = msgheight - (dlgheight - maxDialogHeight); 7101 dlgheight = maxDialogHeight; 7102 scroll_flag = WS_VSCROLL; 7103 /* Make sure scrollbar doesn't appear in the middle of the dialog */ 7104 messageWidth = dlgwidth - DLG_ICON_WIDTH - 3 * dlgPaddingX; 7105 } 7106 7107 add_word(PixelToDialogY(dlgheight)); 7108 7109 add_word(0); // Menu 7110 add_word(0); // Class 7111 7112 /* copy the title of the dialog */ 7113 nchar = nCopyAnsiToWideChar(p, (title ? (LPSTR)title 7114 : (LPSTR)("Vim "VIM_VERSION_MEDIUM)), TRUE); 7115 p += nchar; 7116 7117 if (s_usenewlook) 7118 { 7119 /* do the font, since DS_3DLOOK doesn't work properly */ 7120 #ifdef USE_SYSMENU_FONT 7121 if (use_lfSysmenu) 7122 { 7123 /* point size */ 7124 *p++ = -MulDiv(lfSysmenu.lfHeight, 72, 7125 GetDeviceCaps(hdc, LOGPIXELSY)); 7126 nchar = nCopyAnsiToWideChar(p, lfSysmenu.lfFaceName, FALSE); 7127 } 7128 else 7129 #endif 7130 { 7131 *p++ = DLG_FONT_POINT_SIZE; // point size 7132 nchar = nCopyAnsiToWideChar(p, DLG_FONT_NAME, FALSE); 7133 } 7134 p += nchar; 7135 } 7136 7137 buttonYpos = msgheight + 2 * dlgPaddingY; 7138 7139 if (textfield != NULL) 7140 buttonYpos += editboxheight; 7141 7142 pstart = tbuffer; 7143 if (!vertical) 7144 horizWidth = (dlgwidth - horizWidth) / 2; /* Now it's X offset */ 7145 for (i = 0; i < numButtons; i++) 7146 { 7147 /* get end of this button. */ 7148 for ( pend = pstart; 7149 *pend && (*pend != DLG_BUTTON_SEP); 7150 pend++) 7151 ; 7152 7153 if (*pend) 7154 *pend = '\0'; 7155 7156 /* 7157 * old NOTE: 7158 * setting the BS_DEFPUSHBUTTON style doesn't work because Windows sets 7159 * the focus to the first tab-able button and in so doing makes that 7160 * the default!! Grrr. Workaround: Make the default button the only 7161 * one with WS_TABSTOP style. Means user can't tab between buttons, but 7162 * he/she can use arrow keys. 7163 * 7164 * new NOTE: BS_DEFPUSHBUTTON is required to be able to select the 7165 * right button when hitting <Enter>. E.g., for the ":confirm quit" 7166 * dialog. Also needed for when the textfield is the default control. 7167 * It appears to work now (perhaps not on Win95?). 7168 */ 7169 if (vertical) 7170 { 7171 p = add_dialog_element(p, 7172 (i == dfltbutton 7173 ? BS_DEFPUSHBUTTON : BS_PUSHBUTTON) | WS_TABSTOP, 7174 PixelToDialogX(DLG_VERT_PADDING_X), 7175 PixelToDialogY(buttonYpos /* TBK */ 7176 + 2 * fontHeight * i), 7177 PixelToDialogX(dlgwidth - 2 * DLG_VERT_PADDING_X), 7178 (WORD)(PixelToDialogY(2 * fontHeight) - 1), 7179 (WORD)(IDCANCEL + 1 + i), (WORD)0x0080, (char *)pstart); 7180 } 7181 else 7182 { 7183 p = add_dialog_element(p, 7184 (i == dfltbutton 7185 ? BS_DEFPUSHBUTTON : BS_PUSHBUTTON) | WS_TABSTOP, 7186 PixelToDialogX(horizWidth + buttonPositions[i]), 7187 PixelToDialogY(buttonYpos), /* TBK */ 7188 PixelToDialogX(buttonWidths[i]), 7189 (WORD)(PixelToDialogY(2 * fontHeight) - 1), 7190 (WORD)(IDCANCEL + 1 + i), (WORD)0x0080, (char *)pstart); 7191 } 7192 pstart = pend + 1; /*next button*/ 7193 } 7194 *pnumitems += numButtons; 7195 7196 /* Vim icon */ 7197 p = add_dialog_element(p, SS_ICON, 7198 PixelToDialogX(dlgPaddingX), 7199 PixelToDialogY(dlgPaddingY), 7200 PixelToDialogX(DLG_ICON_WIDTH), 7201 PixelToDialogY(DLG_ICON_HEIGHT), 7202 DLG_NONBUTTON_CONTROL + 0, (WORD)0x0082, 7203 dlg_icons[type]); 7204 7205 /* Dialog message */ 7206 p = add_dialog_element(p, ES_LEFT|scroll_flag|ES_MULTILINE|ES_READONLY, 7207 PixelToDialogX(2 * dlgPaddingX + DLG_ICON_WIDTH), 7208 PixelToDialogY(dlgPaddingY), 7209 (WORD)(PixelToDialogX(messageWidth) + 1), 7210 PixelToDialogY(msgheight), 7211 DLG_NONBUTTON_CONTROL + 1, (WORD)0x0081, (char *)message); 7212 7213 /* Edit box */ 7214 if (textfield != NULL) 7215 { 7216 p = add_dialog_element(p, ES_LEFT|ES_AUTOHSCROLL|WS_TABSTOP|WS_BORDER, 7217 PixelToDialogX(2 * dlgPaddingX), 7218 PixelToDialogY(2 * dlgPaddingY + msgheight), 7219 PixelToDialogX(dlgwidth - 4 * dlgPaddingX), 7220 PixelToDialogY(fontHeight + dlgPaddingY), 7221 DLG_NONBUTTON_CONTROL + 2, (WORD)0x0081, (char *)textfield); 7222 *pnumitems += 1; 7223 } 7224 7225 *pnumitems += 2; 7226 7227 SelectFont(hdc, oldFont); 7228 DeleteObject(font); 7229 ReleaseDC(hwnd, hdc); 7230 7231 /* Let the dialog_callback() function know which button to make default 7232 * If we have an edit box, make that the default. We also need to tell 7233 * dialog_callback() if this dialog contains an edit box or not. We do 7234 * this by setting s_textfield if it does. 7235 */ 7236 if (textfield != NULL) 7237 { 7238 dialog_default_button = DLG_NONBUTTON_CONTROL + 2; 7239 s_textfield = textfield; 7240 } 7241 else 7242 { 7243 dialog_default_button = IDCANCEL + 1 + dfltbutton; 7244 s_textfield = NULL; 7245 } 7246 7247 /* show the dialog box modally and get a return value */ 7248 nchar = (int)DialogBoxIndirect( 7249 s_hinst, 7250 (LPDLGTEMPLATE)pdlgtemplate, 7251 s_hwnd, 7252 (DLGPROC)dialog_callback); 7253 7254 LocalFree(LocalHandle(pdlgtemplate)); 7255 vim_free(tbuffer); 7256 vim_free(buttonWidths); 7257 vim_free(buttonPositions); 7258 vim_free(ga.ga_data); 7259 7260 /* Focus back to our window (for when MDI is used). */ 7261 (void)SetFocus(s_hwnd); 7262 7263 return nchar; 7264 } 7265 7266 #endif /* FEAT_GUI_DIALOG */ 7267 7268 /* 7269 * Put a simple element (basic class) onto a dialog template in memory. 7270 * return a pointer to where the next item should be added. 7271 * 7272 * parameters: 7273 * lStyle = additional style flags 7274 * (be careful, NT3.51 & Win32s will ignore the new ones) 7275 * x,y = x & y positions IN DIALOG UNITS 7276 * w,h = width and height IN DIALOG UNITS 7277 * Id = ID used in messages 7278 * clss = class ID, e.g 0x0080 for a button, 0x0082 for a static 7279 * caption = usually text or resource name 7280 * 7281 * TODO: use the length information noted here to enable the dialog creation 7282 * routines to work out more exactly how much memory they need to alloc. 7283 */ 7284 static PWORD 7285 add_dialog_element( 7286 PWORD p, 7287 DWORD lStyle, 7288 WORD x, 7289 WORD y, 7290 WORD w, 7291 WORD h, 7292 WORD Id, 7293 WORD clss, 7294 const char *caption) 7295 { 7296 int nchar; 7297 7298 p = lpwAlign(p); /* Align to dword boundary*/ 7299 lStyle = lStyle | WS_VISIBLE | WS_CHILD; 7300 *p++ = LOWORD(lStyle); 7301 *p++ = HIWORD(lStyle); 7302 *p++ = 0; // LOWORD (lExtendedStyle) 7303 *p++ = 0; // HIWORD (lExtendedStyle) 7304 *p++ = x; 7305 *p++ = y; 7306 *p++ = w; 7307 *p++ = h; 7308 *p++ = Id; //9 or 10 words in all 7309 7310 *p++ = (WORD)0xffff; 7311 *p++ = clss; //2 more here 7312 7313 nchar = nCopyAnsiToWideChar(p, (LPSTR)caption, TRUE); //strlen(caption)+1 7314 p += nchar; 7315 7316 *p++ = 0; // advance pointer over nExtraStuff WORD - 2 more 7317 7318 return p; //total = 15+ (strlen(caption)) words 7319 // = 30 + 2(strlen(caption) bytes reqd 7320 } 7321 7322 7323 /* 7324 * Helper routine. Take an input pointer, return closest pointer that is 7325 * aligned on a DWORD (4 byte) boundary. Taken from the Win32SDK samples. 7326 */ 7327 static LPWORD 7328 lpwAlign( 7329 LPWORD lpIn) 7330 { 7331 long_u ul; 7332 7333 ul = (long_u)lpIn; 7334 ul += 3; 7335 ul >>= 2; 7336 ul <<= 2; 7337 return (LPWORD)ul; 7338 } 7339 7340 /* 7341 * Helper routine. Takes second parameter as Ansi string, copies it to first 7342 * parameter as wide character (16-bits / char) string, and returns integer 7343 * number of wide characters (words) in string (including the trailing wide 7344 * char NULL). Partly taken from the Win32SDK samples. 7345 * If "use_enc" is TRUE, 'encoding' is used for "lpAnsiIn". If FALSE, current 7346 * ACP is used for "lpAnsiIn". */ 7347 static int 7348 nCopyAnsiToWideChar( 7349 LPWORD lpWCStr, 7350 LPSTR lpAnsiIn, 7351 BOOL use_enc) 7352 { 7353 int nChar = 0; 7354 int len = lstrlen(lpAnsiIn) + 1; /* include NUL character */ 7355 int i; 7356 WCHAR *wn; 7357 7358 if (use_enc && enc_codepage >= 0 && (int)GetACP() != enc_codepage) 7359 { 7360 /* Not a codepage, use our own conversion function. */ 7361 wn = enc_to_utf16((char_u *)lpAnsiIn, NULL); 7362 if (wn != NULL) 7363 { 7364 wcscpy(lpWCStr, wn); 7365 nChar = (int)wcslen(wn) + 1; 7366 vim_free(wn); 7367 } 7368 } 7369 if (nChar == 0) 7370 /* Use Win32 conversion function. */ 7371 nChar = MultiByteToWideChar( 7372 enc_codepage > 0 ? enc_codepage : CP_ACP, 7373 MB_PRECOMPOSED, 7374 lpAnsiIn, len, 7375 lpWCStr, len); 7376 for (i = 0; i < nChar; ++i) 7377 if (lpWCStr[i] == (WORD)'\t') /* replace tabs with spaces */ 7378 lpWCStr[i] = (WORD)' '; 7379 7380 return nChar; 7381 } 7382 7383 7384 #ifdef FEAT_TEAROFF 7385 /* 7386 * Lookup menu handle from "menu_id". 7387 */ 7388 static HMENU 7389 tearoff_lookup_menuhandle( 7390 vimmenu_T *menu, 7391 WORD menu_id) 7392 { 7393 for ( ; menu != NULL; menu = menu->next) 7394 { 7395 if (menu->modes == 0) /* this menu has just been deleted */ 7396 continue; 7397 if (menu_is_separator(menu->dname)) 7398 continue; 7399 if ((WORD)((long_u)(menu->submenu_id) | (DWORD)0x8000) == menu_id) 7400 return menu->submenu_id; 7401 } 7402 return NULL; 7403 } 7404 7405 /* 7406 * The callback function for all the modeless dialogs that make up the 7407 * "tearoff menus" Very simple - forward button presses (to fool Vim into 7408 * thinking its menus have been clicked), and go away when closed. 7409 */ 7410 static LRESULT CALLBACK 7411 tearoff_callback( 7412 HWND hwnd, 7413 UINT message, 7414 WPARAM wParam, 7415 LPARAM lParam) 7416 { 7417 if (message == WM_INITDIALOG) 7418 { 7419 SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR)lParam); 7420 return (TRUE); 7421 } 7422 7423 /* May show the mouse pointer again. */ 7424 HandleMouseHide(message, lParam); 7425 7426 if (message == WM_COMMAND) 7427 { 7428 if ((WORD)(LOWORD(wParam)) & 0x8000) 7429 { 7430 POINT mp; 7431 RECT rect; 7432 7433 if (GetCursorPos(&mp) && GetWindowRect(hwnd, &rect)) 7434 { 7435 vimmenu_T *menu; 7436 7437 menu = (vimmenu_T*)GetWindowLongPtr(hwnd, DWLP_USER); 7438 (void)TrackPopupMenu( 7439 tearoff_lookup_menuhandle(menu, LOWORD(wParam)), 7440 TPM_LEFTALIGN | TPM_LEFTBUTTON, 7441 (int)rect.right - 8, 7442 (int)mp.y, 7443 (int)0, /*reserved param*/ 7444 s_hwnd, 7445 NULL); 7446 /* 7447 * NOTE: The pop-up menu can eat the mouse up event. 7448 * We deal with this in normal.c. 7449 */ 7450 } 7451 } 7452 else 7453 /* Pass on messages to the main Vim window */ 7454 PostMessage(s_hwnd, WM_COMMAND, LOWORD(wParam), 0); 7455 /* 7456 * Give main window the focus back: this is so after 7457 * choosing a tearoff button you can start typing again 7458 * straight away. 7459 */ 7460 (void)SetFocus(s_hwnd); 7461 return TRUE; 7462 } 7463 if ((message == WM_SYSCOMMAND) && (wParam == SC_CLOSE)) 7464 { 7465 DestroyWindow(hwnd); 7466 return TRUE; 7467 } 7468 7469 /* When moved around, give main window the focus back. */ 7470 if (message == WM_EXITSIZEMOVE) 7471 (void)SetActiveWindow(s_hwnd); 7472 7473 return FALSE; 7474 } 7475 #endif 7476 7477 7478 /* 7479 * Decide whether to use the "new look" (small, non-bold font) or the "old 7480 * look" (big, clanky font) for dialogs, and work out a few values for use 7481 * later accordingly. 7482 */ 7483 static void 7484 get_dialog_font_metrics(void) 7485 { 7486 HDC hdc; 7487 HFONT hfontTools = 0; 7488 DWORD dlgFontSize; 7489 SIZE size; 7490 #ifdef USE_SYSMENU_FONT 7491 LOGFONT lfSysmenu; 7492 #endif 7493 7494 s_usenewlook = FALSE; 7495 7496 #ifdef USE_SYSMENU_FONT 7497 if (gui_w32_get_menu_font(&lfSysmenu) == OK) 7498 hfontTools = CreateFontIndirect(&lfSysmenu); 7499 else 7500 #endif 7501 hfontTools = CreateFont(-DLG_FONT_POINT_SIZE, 0, 0, 0, 0, 0, 0, 0, 7502 0, 0, 0, 0, VARIABLE_PITCH , DLG_FONT_NAME); 7503 7504 if (hfontTools) 7505 { 7506 hdc = GetDC(s_hwnd); 7507 SelectObject(hdc, hfontTools); 7508 /* 7509 * GetTextMetrics() doesn't return the right value in 7510 * tmAveCharWidth, so we have to figure out the dialog base units 7511 * ourselves. 7512 */ 7513 GetTextExtentPoint(hdc, 7514 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 7515 52, &size); 7516 ReleaseDC(s_hwnd, hdc); 7517 7518 s_dlgfntwidth = (WORD)((size.cx / 26 + 1) / 2); 7519 s_dlgfntheight = (WORD)size.cy; 7520 s_usenewlook = TRUE; 7521 } 7522 7523 if (!s_usenewlook) 7524 { 7525 dlgFontSize = GetDialogBaseUnits(); /* fall back to big old system*/ 7526 s_dlgfntwidth = LOWORD(dlgFontSize); 7527 s_dlgfntheight = HIWORD(dlgFontSize); 7528 } 7529 } 7530 7531 #if defined(FEAT_MENU) && defined(FEAT_TEAROFF) 7532 /* 7533 * Create a pseudo-"tearoff menu" based on the child 7534 * items of a given menu pointer. 7535 */ 7536 static void 7537 gui_mch_tearoff( 7538 char_u *title, 7539 vimmenu_T *menu, 7540 int initX, 7541 int initY) 7542 { 7543 WORD *p, *pdlgtemplate, *pnumitems, *ptrueheight; 7544 int template_len; 7545 int nchar, textWidth, submenuWidth; 7546 DWORD lStyle; 7547 DWORD lExtendedStyle; 7548 WORD dlgwidth; 7549 WORD menuID; 7550 vimmenu_T *pmenu; 7551 vimmenu_T *top_menu; 7552 vimmenu_T *the_menu = menu; 7553 HWND hwnd; 7554 HDC hdc; 7555 HFONT font, oldFont; 7556 int col, spaceWidth, len; 7557 int columnWidths[2]; 7558 char_u *label, *text; 7559 int acLen = 0; 7560 int nameLen; 7561 int padding0, padding1, padding2 = 0; 7562 int sepPadding=0; 7563 int x; 7564 int y; 7565 #ifdef USE_SYSMENU_FONT 7566 LOGFONT lfSysmenu; 7567 int use_lfSysmenu = FALSE; 7568 #endif 7569 7570 /* 7571 * If this menu is already torn off, move it to the mouse position. 7572 */ 7573 if (IsWindow(menu->tearoff_handle)) 7574 { 7575 POINT mp; 7576 if (GetCursorPos((LPPOINT)&mp)) 7577 { 7578 SetWindowPos(menu->tearoff_handle, NULL, mp.x, mp.y, 0, 0, 7579 SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER); 7580 } 7581 return; 7582 } 7583 7584 /* 7585 * Create a new tearoff. 7586 */ 7587 if (*title == MNU_HIDDEN_CHAR) 7588 title++; 7589 7590 /* Allocate memory to store the dialog template. It's made bigger when 7591 * needed. */ 7592 template_len = DLG_ALLOC_SIZE; 7593 pdlgtemplate = p = (WORD *)LocalAlloc(LPTR, template_len); 7594 if (p == NULL) 7595 return; 7596 7597 hwnd = GetDesktopWindow(); 7598 hdc = GetWindowDC(hwnd); 7599 #ifdef USE_SYSMENU_FONT 7600 if (gui_w32_get_menu_font(&lfSysmenu) == OK) 7601 { 7602 font = CreateFontIndirect(&lfSysmenu); 7603 use_lfSysmenu = TRUE; 7604 } 7605 else 7606 #endif 7607 font = CreateFont(-DLG_FONT_POINT_SIZE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7608 VARIABLE_PITCH , DLG_FONT_NAME); 7609 if (s_usenewlook) 7610 oldFont = SelectFont(hdc, font); 7611 else 7612 oldFont = SelectFont(hdc, GetStockObject(SYSTEM_FONT)); 7613 7614 /* Calculate width of a single space. Used for padding columns to the 7615 * right width. */ 7616 spaceWidth = GetTextWidth(hdc, (char_u *)" ", 1); 7617 7618 /* Figure out max width of the text column, the accelerator column and the 7619 * optional submenu column. */ 7620 submenuWidth = 0; 7621 for (col = 0; col < 2; col++) 7622 { 7623 columnWidths[col] = 0; 7624 for (pmenu = menu->children; pmenu != NULL; pmenu = pmenu->next) 7625 { 7626 /* Use "dname" here to compute the width of the visible text. */ 7627 text = (col == 0) ? pmenu->dname : pmenu->actext; 7628 if (text != NULL && *text != NUL) 7629 { 7630 textWidth = GetTextWidthEnc(hdc, text, (int)STRLEN(text)); 7631 if (textWidth > columnWidths[col]) 7632 columnWidths[col] = textWidth; 7633 } 7634 if (pmenu->children != NULL) 7635 submenuWidth = TEAROFF_COLUMN_PADDING * spaceWidth; 7636 } 7637 } 7638 if (columnWidths[1] == 0) 7639 { 7640 /* no accelerators */ 7641 if (submenuWidth != 0) 7642 columnWidths[0] += submenuWidth; 7643 else 7644 columnWidths[0] += spaceWidth; 7645 } 7646 else 7647 { 7648 /* there is an accelerator column */ 7649 columnWidths[0] += TEAROFF_COLUMN_PADDING * spaceWidth; 7650 columnWidths[1] += submenuWidth; 7651 } 7652 7653 /* 7654 * Now find the total width of our 'menu'. 7655 */ 7656 textWidth = columnWidths[0] + columnWidths[1]; 7657 if (submenuWidth != 0) 7658 { 7659 submenuWidth = GetTextWidth(hdc, (char_u *)TEAROFF_SUBMENU_LABEL, 7660 (int)STRLEN(TEAROFF_SUBMENU_LABEL)); 7661 textWidth += submenuWidth; 7662 } 7663 dlgwidth = GetTextWidthEnc(hdc, title, (int)STRLEN(title)); 7664 if (textWidth > dlgwidth) 7665 dlgwidth = textWidth; 7666 dlgwidth += 2 * TEAROFF_PADDING_X + TEAROFF_BUTTON_PAD_X; 7667 7668 /* start to fill in the dlgtemplate information. addressing by WORDs */ 7669 if (s_usenewlook) 7670 lStyle = DS_MODALFRAME | WS_CAPTION| WS_SYSMENU |DS_SETFONT| WS_VISIBLE; 7671 else 7672 lStyle = DS_MODALFRAME | WS_CAPTION| WS_SYSMENU | WS_VISIBLE; 7673 7674 lExtendedStyle = WS_EX_TOOLWINDOW|WS_EX_STATICEDGE; 7675 *p++ = LOWORD(lStyle); 7676 *p++ = HIWORD(lStyle); 7677 *p++ = LOWORD(lExtendedStyle); 7678 *p++ = HIWORD(lExtendedStyle); 7679 pnumitems = p; /* save where the number of items must be stored */ 7680 *p++ = 0; // NumberOfItems(will change later) 7681 gui_mch_getmouse(&x, &y); 7682 if (initX == 0xffffL) 7683 *p++ = PixelToDialogX(x); // x 7684 else 7685 *p++ = PixelToDialogX(initX); // x 7686 if (initY == 0xffffL) 7687 *p++ = PixelToDialogY(y); // y 7688 else 7689 *p++ = PixelToDialogY(initY); // y 7690 *p++ = PixelToDialogX(dlgwidth); // cx 7691 ptrueheight = p; 7692 *p++ = 0; // dialog height: changed later anyway 7693 *p++ = 0; // Menu 7694 *p++ = 0; // Class 7695 7696 /* copy the title of the dialog */ 7697 nchar = nCopyAnsiToWideChar(p, ((*title) 7698 ? (LPSTR)title 7699 : (LPSTR)("Vim "VIM_VERSION_MEDIUM)), TRUE); 7700 p += nchar; 7701 7702 if (s_usenewlook) 7703 { 7704 /* do the font, since DS_3DLOOK doesn't work properly */ 7705 #ifdef USE_SYSMENU_FONT 7706 if (use_lfSysmenu) 7707 { 7708 /* point size */ 7709 *p++ = -MulDiv(lfSysmenu.lfHeight, 72, 7710 GetDeviceCaps(hdc, LOGPIXELSY)); 7711 nchar = nCopyAnsiToWideChar(p, lfSysmenu.lfFaceName, FALSE); 7712 } 7713 else 7714 #endif 7715 { 7716 *p++ = DLG_FONT_POINT_SIZE; // point size 7717 nchar = nCopyAnsiToWideChar(p, DLG_FONT_NAME, FALSE); 7718 } 7719 p += nchar; 7720 } 7721 7722 /* 7723 * Loop over all the items in the menu. 7724 * But skip over the tearbar. 7725 */ 7726 if (STRCMP(menu->children->name, TEAR_STRING) == 0) 7727 menu = menu->children->next; 7728 else 7729 menu = menu->children; 7730 top_menu = menu; 7731 for ( ; menu != NULL; menu = menu->next) 7732 { 7733 if (menu->modes == 0) /* this menu has just been deleted */ 7734 continue; 7735 if (menu_is_separator(menu->dname)) 7736 { 7737 sepPadding += 3; 7738 continue; 7739 } 7740 7741 /* Check if there still is plenty of room in the template. Make it 7742 * larger when needed. */ 7743 if (((char *)p - (char *)pdlgtemplate) + 1000 > template_len) 7744 { 7745 WORD *newp; 7746 7747 newp = (WORD *)LocalAlloc(LPTR, template_len + 4096); 7748 if (newp != NULL) 7749 { 7750 template_len += 4096; 7751 mch_memmove(newp, pdlgtemplate, 7752 (char *)p - (char *)pdlgtemplate); 7753 p = newp + (p - pdlgtemplate); 7754 pnumitems = newp + (pnumitems - pdlgtemplate); 7755 ptrueheight = newp + (ptrueheight - pdlgtemplate); 7756 LocalFree(LocalHandle(pdlgtemplate)); 7757 pdlgtemplate = newp; 7758 } 7759 } 7760 7761 /* Figure out minimal length of this menu label. Use "name" for the 7762 * actual text, "dname" for estimating the displayed size. "name" 7763 * has "&a" for mnemonic and includes the accelerator. */ 7764 len = nameLen = (int)STRLEN(menu->name); 7765 padding0 = (columnWidths[0] - GetTextWidthEnc(hdc, menu->dname, 7766 (int)STRLEN(menu->dname))) / spaceWidth; 7767 len += padding0; 7768 7769 if (menu->actext != NULL) 7770 { 7771 acLen = (int)STRLEN(menu->actext); 7772 len += acLen; 7773 textWidth = GetTextWidthEnc(hdc, menu->actext, acLen); 7774 } 7775 else 7776 textWidth = 0; 7777 padding1 = (columnWidths[1] - textWidth) / spaceWidth; 7778 len += padding1; 7779 7780 if (menu->children == NULL) 7781 { 7782 padding2 = submenuWidth / spaceWidth; 7783 len += padding2; 7784 menuID = (WORD)(menu->id); 7785 } 7786 else 7787 { 7788 len += (int)STRLEN(TEAROFF_SUBMENU_LABEL); 7789 menuID = (WORD)((long_u)(menu->submenu_id) | (DWORD)0x8000); 7790 } 7791 7792 /* Allocate menu label and fill it in */ 7793 text = label = alloc((unsigned)len + 1); 7794 if (label == NULL) 7795 break; 7796 7797 vim_strncpy(text, menu->name, nameLen); 7798 text = vim_strchr(text, TAB); /* stop at TAB before actext */ 7799 if (text == NULL) 7800 text = label + nameLen; /* no actext, use whole name */ 7801 while (padding0-- > 0) 7802 *text++ = ' '; 7803 if (menu->actext != NULL) 7804 { 7805 STRNCPY(text, menu->actext, acLen); 7806 text += acLen; 7807 } 7808 while (padding1-- > 0) 7809 *text++ = ' '; 7810 if (menu->children != NULL) 7811 { 7812 STRCPY(text, TEAROFF_SUBMENU_LABEL); 7813 text += STRLEN(TEAROFF_SUBMENU_LABEL); 7814 } 7815 else 7816 { 7817 while (padding2-- > 0) 7818 *text++ = ' '; 7819 } 7820 *text = NUL; 7821 7822 /* 7823 * BS_LEFT will just be ignored on Win32s/NT3.5x - on 7824 * W95/NT4 it makes the tear-off look more like a menu. 7825 */ 7826 p = add_dialog_element(p, 7827 BS_PUSHBUTTON|BS_LEFT, 7828 (WORD)PixelToDialogX(TEAROFF_PADDING_X), 7829 (WORD)(sepPadding + 1 + 13 * (*pnumitems)), 7830 (WORD)PixelToDialogX(dlgwidth - 2 * TEAROFF_PADDING_X), 7831 (WORD)12, 7832 menuID, (WORD)0x0080, (char *)label); 7833 vim_free(label); 7834 (*pnumitems)++; 7835 } 7836 7837 *ptrueheight = (WORD)(sepPadding + 1 + 13 * (*pnumitems)); 7838 7839 7840 /* show modelessly */ 7841 the_menu->tearoff_handle = CreateDialogIndirectParam( 7842 s_hinst, 7843 (LPDLGTEMPLATE)pdlgtemplate, 7844 s_hwnd, 7845 (DLGPROC)tearoff_callback, 7846 (LPARAM)top_menu); 7847 7848 LocalFree(LocalHandle(pdlgtemplate)); 7849 SelectFont(hdc, oldFont); 7850 DeleteObject(font); 7851 ReleaseDC(hwnd, hdc); 7852 7853 /* 7854 * Reassert ourselves as the active window. This is so that after creating 7855 * a tearoff, the user doesn't have to click with the mouse just to start 7856 * typing again! 7857 */ 7858 (void)SetActiveWindow(s_hwnd); 7859 7860 /* make sure the right buttons are enabled */ 7861 force_menu_update = TRUE; 7862 } 7863 #endif 7864 7865 #if defined(FEAT_TOOLBAR) || defined(PROTO) 7866 #include "gui_w32_rc.h" 7867 7868 /* This not defined in older SDKs */ 7869 # ifndef TBSTYLE_FLAT 7870 # define TBSTYLE_FLAT 0x0800 7871 # endif 7872 7873 /* 7874 * Create the toolbar, initially unpopulated. 7875 * (just like the menu, there are no defaults, it's all 7876 * set up through menu.vim) 7877 */ 7878 static void 7879 initialise_toolbar(void) 7880 { 7881 InitCommonControls(); 7882 s_toolbarhwnd = CreateToolbarEx( 7883 s_hwnd, 7884 WS_CHILD | TBSTYLE_TOOLTIPS | TBSTYLE_FLAT, 7885 4000, //any old big number 7886 31, //number of images in initial bitmap 7887 s_hinst, 7888 IDR_TOOLBAR1, // id of initial bitmap 7889 NULL, 7890 0, // initial number of buttons 7891 TOOLBAR_BUTTON_WIDTH, //api guide is wrong! 7892 TOOLBAR_BUTTON_HEIGHT, 7893 TOOLBAR_BUTTON_WIDTH, 7894 TOOLBAR_BUTTON_HEIGHT, 7895 sizeof(TBBUTTON) 7896 ); 7897 s_toolbar_wndproc = SubclassWindow(s_toolbarhwnd, toolbar_wndproc); 7898 7899 gui_mch_show_toolbar(vim_strchr(p_go, GO_TOOLBAR) != NULL); 7900 } 7901 7902 static LRESULT CALLBACK 7903 toolbar_wndproc( 7904 HWND hwnd, 7905 UINT uMsg, 7906 WPARAM wParam, 7907 LPARAM lParam) 7908 { 7909 HandleMouseHide(uMsg, lParam); 7910 return CallWindowProc(s_toolbar_wndproc, hwnd, uMsg, wParam, lParam); 7911 } 7912 7913 static int 7914 get_toolbar_bitmap(vimmenu_T *menu) 7915 { 7916 int i = -1; 7917 7918 /* 7919 * Check user bitmaps first, unless builtin is specified. 7920 */ 7921 if (!menu->icon_builtin) 7922 { 7923 char_u fname[MAXPATHL]; 7924 HANDLE hbitmap = NULL; 7925 7926 if (menu->iconfile != NULL) 7927 { 7928 gui_find_iconfile(menu->iconfile, fname, "bmp"); 7929 hbitmap = LoadImage( 7930 NULL, 7931 (LPCSTR)fname, 7932 IMAGE_BITMAP, 7933 TOOLBAR_BUTTON_WIDTH, 7934 TOOLBAR_BUTTON_HEIGHT, 7935 LR_LOADFROMFILE | 7936 LR_LOADMAP3DCOLORS 7937 ); 7938 } 7939 7940 /* 7941 * If the LoadImage call failed, or the "icon=" file 7942 * didn't exist or wasn't specified, try the menu name 7943 */ 7944 if (hbitmap == NULL 7945 && (gui_find_bitmap( 7946 #ifdef FEAT_MULTI_LANG 7947 menu->en_dname != NULL ? menu->en_dname : 7948 #endif 7949 menu->dname, fname, "bmp") == OK)) 7950 hbitmap = LoadImage( 7951 NULL, 7952 (LPCSTR)fname, 7953 IMAGE_BITMAP, 7954 TOOLBAR_BUTTON_WIDTH, 7955 TOOLBAR_BUTTON_HEIGHT, 7956 LR_LOADFROMFILE | 7957 LR_LOADMAP3DCOLORS 7958 ); 7959 7960 if (hbitmap != NULL) 7961 { 7962 TBADDBITMAP tbAddBitmap; 7963 7964 tbAddBitmap.hInst = NULL; 7965 tbAddBitmap.nID = (long_u)hbitmap; 7966 7967 i = (int)SendMessage(s_toolbarhwnd, TB_ADDBITMAP, 7968 (WPARAM)1, (LPARAM)&tbAddBitmap); 7969 /* i will be set to -1 if it fails */ 7970 } 7971 } 7972 if (i == -1 && menu->iconidx >= 0 && menu->iconidx < TOOLBAR_BITMAP_COUNT) 7973 i = menu->iconidx; 7974 7975 return i; 7976 } 7977 #endif 7978 7979 #if defined(FEAT_GUI_TABLINE) || defined(PROTO) 7980 static void 7981 initialise_tabline(void) 7982 { 7983 InitCommonControls(); 7984 7985 s_tabhwnd = CreateWindow(WC_TABCONTROL, "Vim tabline", 7986 WS_CHILD|TCS_FOCUSNEVER|TCS_TOOLTIPS, 7987 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 7988 CW_USEDEFAULT, s_hwnd, NULL, s_hinst, NULL); 7989 s_tabline_wndproc = SubclassWindow(s_tabhwnd, tabline_wndproc); 7990 7991 gui.tabline_height = TABLINE_HEIGHT; 7992 7993 # ifdef USE_SYSMENU_FONT 7994 set_tabline_font(); 7995 # endif 7996 } 7997 7998 /* 7999 * Get tabpage_T from POINT. 8000 */ 8001 static tabpage_T * 8002 GetTabFromPoint( 8003 HWND hWnd, 8004 POINT pt) 8005 { 8006 tabpage_T *ptp = NULL; 8007 8008 if (gui_mch_showing_tabline()) 8009 { 8010 TCHITTESTINFO htinfo; 8011 htinfo.pt = pt; 8012 /* ignore if a window under cusor is not tabcontrol. */ 8013 if (s_tabhwnd == hWnd) 8014 { 8015 int idx = TabCtrl_HitTest(s_tabhwnd, &htinfo); 8016 if (idx != -1) 8017 ptp = find_tabpage(idx + 1); 8018 } 8019 } 8020 return ptp; 8021 } 8022 8023 static POINT s_pt = {0, 0}; 8024 static HCURSOR s_hCursor = NULL; 8025 8026 static LRESULT CALLBACK 8027 tabline_wndproc( 8028 HWND hwnd, 8029 UINT uMsg, 8030 WPARAM wParam, 8031 LPARAM lParam) 8032 { 8033 POINT pt; 8034 tabpage_T *tp; 8035 RECT rect; 8036 int nCenter; 8037 int idx0; 8038 int idx1; 8039 8040 HandleMouseHide(uMsg, lParam); 8041 8042 switch (uMsg) 8043 { 8044 case WM_LBUTTONDOWN: 8045 { 8046 s_pt.x = GET_X_LPARAM(lParam); 8047 s_pt.y = GET_Y_LPARAM(lParam); 8048 SetCapture(hwnd); 8049 s_hCursor = GetCursor(); /* backup default cursor */ 8050 break; 8051 } 8052 case WM_MOUSEMOVE: 8053 if (GetCapture() == hwnd 8054 && ((wParam & MK_LBUTTON)) != 0) 8055 { 8056 pt.x = GET_X_LPARAM(lParam); 8057 pt.y = s_pt.y; 8058 if (abs(pt.x - s_pt.x) > GetSystemMetrics(SM_CXDRAG)) 8059 { 8060 SetCursor(LoadCursor(NULL, IDC_SIZEWE)); 8061 8062 tp = GetTabFromPoint(hwnd, pt); 8063 if (tp != NULL) 8064 { 8065 idx0 = tabpage_index(curtab) - 1; 8066 idx1 = tabpage_index(tp) - 1; 8067 8068 TabCtrl_GetItemRect(hwnd, idx1, &rect); 8069 nCenter = rect.left + (rect.right - rect.left) / 2; 8070 8071 /* Check if the mouse cursor goes over the center of 8072 * the next tab to prevent "flickering". */ 8073 if ((idx0 < idx1) && (nCenter < pt.x)) 8074 { 8075 tabpage_move(idx1 + 1); 8076 update_screen(0); 8077 } 8078 else if ((idx1 < idx0) && (pt.x < nCenter)) 8079 { 8080 tabpage_move(idx1); 8081 update_screen(0); 8082 } 8083 } 8084 } 8085 } 8086 break; 8087 case WM_LBUTTONUP: 8088 { 8089 if (GetCapture() == hwnd) 8090 { 8091 SetCursor(s_hCursor); 8092 ReleaseCapture(); 8093 } 8094 break; 8095 } 8096 default: 8097 break; 8098 } 8099 8100 return CallWindowProc(s_tabline_wndproc, hwnd, uMsg, wParam, lParam); 8101 } 8102 #endif 8103 8104 #if defined(FEAT_OLE) || defined(FEAT_EVAL) || defined(PROTO) 8105 /* 8106 * Make the GUI window come to the foreground. 8107 */ 8108 void 8109 gui_mch_set_foreground(void) 8110 { 8111 if (IsIconic(s_hwnd)) 8112 SendMessage(s_hwnd, WM_SYSCOMMAND, SC_RESTORE, 0); 8113 SetForegroundWindow(s_hwnd); 8114 } 8115 #endif 8116 8117 #if defined(FEAT_MBYTE_IME) && defined(DYNAMIC_IME) 8118 static void 8119 dyn_imm_load(void) 8120 { 8121 hLibImm = vimLoadLib("imm32.dll"); 8122 if (hLibImm == NULL) 8123 return; 8124 8125 pImmGetCompositionStringA 8126 = (void *)GetProcAddress(hLibImm, "ImmGetCompositionStringA"); 8127 pImmGetCompositionStringW 8128 = (void *)GetProcAddress(hLibImm, "ImmGetCompositionStringW"); 8129 pImmGetContext 8130 = (void *)GetProcAddress(hLibImm, "ImmGetContext"); 8131 pImmAssociateContext 8132 = (void *)GetProcAddress(hLibImm, "ImmAssociateContext"); 8133 pImmReleaseContext 8134 = (void *)GetProcAddress(hLibImm, "ImmReleaseContext"); 8135 pImmGetOpenStatus 8136 = (void *)GetProcAddress(hLibImm, "ImmGetOpenStatus"); 8137 pImmSetOpenStatus 8138 = (void *)GetProcAddress(hLibImm, "ImmSetOpenStatus"); 8139 pImmGetCompositionFont 8140 = (void *)GetProcAddress(hLibImm, "ImmGetCompositionFontA"); 8141 pImmSetCompositionFont 8142 = (void *)GetProcAddress(hLibImm, "ImmSetCompositionFontA"); 8143 pImmSetCompositionWindow 8144 = (void *)GetProcAddress(hLibImm, "ImmSetCompositionWindow"); 8145 pImmGetConversionStatus 8146 = (void *)GetProcAddress(hLibImm, "ImmGetConversionStatus"); 8147 pImmSetConversionStatus 8148 = (void *)GetProcAddress(hLibImm, "ImmSetConversionStatus"); 8149 8150 if ( pImmGetCompositionStringA == NULL 8151 || pImmGetCompositionStringW == NULL 8152 || pImmGetContext == NULL 8153 || pImmAssociateContext == NULL 8154 || pImmReleaseContext == NULL 8155 || pImmGetOpenStatus == NULL 8156 || pImmSetOpenStatus == NULL 8157 || pImmGetCompositionFont == NULL 8158 || pImmSetCompositionFont == NULL 8159 || pImmSetCompositionWindow == NULL 8160 || pImmGetConversionStatus == NULL 8161 || pImmSetConversionStatus == NULL) 8162 { 8163 FreeLibrary(hLibImm); 8164 hLibImm = NULL; 8165 pImmGetContext = NULL; 8166 return; 8167 } 8168 8169 return; 8170 } 8171 8172 #endif 8173 8174 #if defined(FEAT_SIGN_ICONS) || defined(PROTO) 8175 8176 # ifdef FEAT_XPM_W32 8177 # define IMAGE_XPM 100 8178 # endif 8179 8180 typedef struct _signicon_t 8181 { 8182 HANDLE hImage; 8183 UINT uType; 8184 #ifdef FEAT_XPM_W32 8185 HANDLE hShape; /* Mask bitmap handle */ 8186 #endif 8187 } signicon_t; 8188 8189 void 8190 gui_mch_drawsign(int row, int col, int typenr) 8191 { 8192 signicon_t *sign; 8193 int x, y, w, h; 8194 8195 if (!gui.in_use || (sign = (signicon_t *)sign_get_image(typenr)) == NULL) 8196 return; 8197 8198 #if defined(FEAT_DIRECTX) 8199 if (IS_ENABLE_DIRECTX()) 8200 DWriteContext_Flush(s_dwc); 8201 #endif 8202 8203 x = TEXT_X(col); 8204 y = TEXT_Y(row); 8205 w = gui.char_width * 2; 8206 h = gui.char_height; 8207 switch (sign->uType) 8208 { 8209 case IMAGE_BITMAP: 8210 { 8211 HDC hdcMem; 8212 HBITMAP hbmpOld; 8213 8214 hdcMem = CreateCompatibleDC(s_hdc); 8215 hbmpOld = (HBITMAP)SelectObject(hdcMem, sign->hImage); 8216 BitBlt(s_hdc, x, y, w, h, hdcMem, 0, 0, SRCCOPY); 8217 SelectObject(hdcMem, hbmpOld); 8218 DeleteDC(hdcMem); 8219 } 8220 break; 8221 case IMAGE_ICON: 8222 case IMAGE_CURSOR: 8223 DrawIconEx(s_hdc, x, y, (HICON)sign->hImage, w, h, 0, NULL, DI_NORMAL); 8224 break; 8225 #ifdef FEAT_XPM_W32 8226 case IMAGE_XPM: 8227 { 8228 HDC hdcMem; 8229 HBITMAP hbmpOld; 8230 8231 hdcMem = CreateCompatibleDC(s_hdc); 8232 hbmpOld = (HBITMAP)SelectObject(hdcMem, sign->hShape); 8233 /* Make hole */ 8234 BitBlt(s_hdc, x, y, w, h, hdcMem, 0, 0, SRCAND); 8235 8236 SelectObject(hdcMem, sign->hImage); 8237 /* Paint sign */ 8238 BitBlt(s_hdc, x, y, w, h, hdcMem, 0, 0, SRCPAINT); 8239 SelectObject(hdcMem, hbmpOld); 8240 DeleteDC(hdcMem); 8241 } 8242 break; 8243 #endif 8244 } 8245 } 8246 8247 static void 8248 close_signicon_image(signicon_t *sign) 8249 { 8250 if (sign) 8251 switch (sign->uType) 8252 { 8253 case IMAGE_BITMAP: 8254 DeleteObject((HGDIOBJ)sign->hImage); 8255 break; 8256 case IMAGE_CURSOR: 8257 DestroyCursor((HCURSOR)sign->hImage); 8258 break; 8259 case IMAGE_ICON: 8260 DestroyIcon((HICON)sign->hImage); 8261 break; 8262 #ifdef FEAT_XPM_W32 8263 case IMAGE_XPM: 8264 DeleteObject((HBITMAP)sign->hImage); 8265 DeleteObject((HBITMAP)sign->hShape); 8266 break; 8267 #endif 8268 } 8269 } 8270 8271 void * 8272 gui_mch_register_sign(char_u *signfile) 8273 { 8274 signicon_t sign, *psign; 8275 char_u *ext; 8276 8277 sign.hImage = NULL; 8278 ext = signfile + STRLEN(signfile) - 4; /* get extension */ 8279 if (ext > signfile) 8280 { 8281 int do_load = 1; 8282 8283 if (!STRICMP(ext, ".bmp")) 8284 sign.uType = IMAGE_BITMAP; 8285 else if (!STRICMP(ext, ".ico")) 8286 sign.uType = IMAGE_ICON; 8287 else if (!STRICMP(ext, ".cur") || !STRICMP(ext, ".ani")) 8288 sign.uType = IMAGE_CURSOR; 8289 else 8290 do_load = 0; 8291 8292 if (do_load) 8293 sign.hImage = (HANDLE)LoadImage(NULL, (LPCSTR)signfile, sign.uType, 8294 gui.char_width * 2, gui.char_height, 8295 LR_LOADFROMFILE | LR_CREATEDIBSECTION); 8296 #ifdef FEAT_XPM_W32 8297 if (!STRICMP(ext, ".xpm")) 8298 { 8299 sign.uType = IMAGE_XPM; 8300 LoadXpmImage((char *)signfile, (HBITMAP *)&sign.hImage, 8301 (HBITMAP *)&sign.hShape); 8302 } 8303 #endif 8304 } 8305 8306 psign = NULL; 8307 if (sign.hImage && (psign = (signicon_t *)alloc(sizeof(signicon_t))) 8308 != NULL) 8309 *psign = sign; 8310 8311 if (!psign) 8312 { 8313 if (sign.hImage) 8314 close_signicon_image(&sign); 8315 emsg(_(e_signdata)); 8316 } 8317 return (void *)psign; 8318 8319 } 8320 8321 void 8322 gui_mch_destroy_sign(void *sign) 8323 { 8324 if (sign) 8325 { 8326 close_signicon_image((signicon_t *)sign); 8327 vim_free(sign); 8328 } 8329 } 8330 #endif 8331 8332 #if defined(FEAT_BEVAL_GUI) || defined(PROTO) 8333 8334 /* BALLOON-EVAL IMPLEMENTATION FOR WINDOWS. 8335 * Added by Sergey Khorev <[email protected]> 8336 * 8337 * The only reused thing is beval.h and get_beval_info() 8338 * from gui_beval.c (note it uses x and y of the BalloonEval struct 8339 * to get current mouse position). 8340 * 8341 * Trying to use as more Windows services as possible, and as less 8342 * IE version as possible :)). 8343 * 8344 * 1) Don't create ToolTip in gui_mch_create_beval_area, only initialize 8345 * BalloonEval struct. 8346 * 2) Enable/Disable simply create/kill BalloonEval Timer 8347 * 3) When there was enough inactivity, timer procedure posts 8348 * async request to debugger 8349 * 4) gui_mch_post_balloon (invoked from netbeans.c) creates tooltip control 8350 * and performs some actions to show it ASAP 8351 * 5) WM_NOTIFY:TTN_POP destroys created tooltip 8352 */ 8353 8354 /* 8355 * determine whether installed Common Controls support multiline tooltips 8356 * (i.e. their version is >= 4.70 8357 */ 8358 int 8359 multiline_balloon_available(void) 8360 { 8361 HINSTANCE hDll; 8362 static char comctl_dll[] = "comctl32.dll"; 8363 static int multiline_tip = MAYBE; 8364 8365 if (multiline_tip != MAYBE) 8366 return multiline_tip; 8367 8368 hDll = GetModuleHandle(comctl_dll); 8369 if (hDll != NULL) 8370 { 8371 DLLGETVERSIONPROC pGetVer; 8372 pGetVer = (DLLGETVERSIONPROC)GetProcAddress(hDll, "DllGetVersion"); 8373 8374 if (pGetVer != NULL) 8375 { 8376 DLLVERSIONINFO dvi; 8377 HRESULT hr; 8378 8379 ZeroMemory(&dvi, sizeof(dvi)); 8380 dvi.cbSize = sizeof(dvi); 8381 8382 hr = (*pGetVer)(&dvi); 8383 8384 if (SUCCEEDED(hr) 8385 && (dvi.dwMajorVersion > 4 8386 || (dvi.dwMajorVersion == 4 8387 && dvi.dwMinorVersion >= 70))) 8388 { 8389 multiline_tip = TRUE; 8390 return multiline_tip; 8391 } 8392 } 8393 else 8394 { 8395 /* there is chance we have ancient CommCtl 4.70 8396 which doesn't export DllGetVersion */ 8397 DWORD dwHandle = 0; 8398 DWORD len = GetFileVersionInfoSize(comctl_dll, &dwHandle); 8399 if (len > 0) 8400 { 8401 VS_FIXEDFILEINFO *ver; 8402 UINT vlen = 0; 8403 void *data = alloc(len); 8404 8405 if ((data != NULL 8406 && GetFileVersionInfo(comctl_dll, 0, len, data) 8407 && VerQueryValue(data, "\\", (void **)&ver, &vlen) 8408 && vlen 8409 && HIWORD(ver->dwFileVersionMS) > 4) 8410 || ((HIWORD(ver->dwFileVersionMS) == 4 8411 && LOWORD(ver->dwFileVersionMS) >= 70))) 8412 { 8413 vim_free(data); 8414 multiline_tip = TRUE; 8415 return multiline_tip; 8416 } 8417 vim_free(data); 8418 } 8419 } 8420 } 8421 multiline_tip = FALSE; 8422 return multiline_tip; 8423 } 8424 8425 static void 8426 make_tooltipw(BalloonEval *beval, char *text, POINT pt) 8427 { 8428 TOOLINFOW *pti; 8429 int ToolInfoSize; 8430 8431 if (multiline_balloon_available() == TRUE) 8432 ToolInfoSize = sizeof(TOOLINFOW_NEW); 8433 else 8434 ToolInfoSize = sizeof(TOOLINFOW); 8435 8436 pti = (TOOLINFOW *)alloc(ToolInfoSize); 8437 if (pti == NULL) 8438 return; 8439 8440 beval->balloon = CreateWindowExW(WS_EX_TOPMOST, TOOLTIPS_CLASSW, 8441 NULL, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP, 8442 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 8443 beval->target, NULL, s_hinst, NULL); 8444 8445 SetWindowPos(beval->balloon, HWND_TOPMOST, 0, 0, 0, 0, 8446 SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); 8447 8448 pti->cbSize = ToolInfoSize; 8449 pti->uFlags = TTF_SUBCLASS; 8450 pti->hwnd = beval->target; 8451 pti->hinst = 0; // Don't use string resources 8452 pti->uId = ID_BEVAL_TOOLTIP; 8453 8454 if (multiline_balloon_available() == TRUE) 8455 { 8456 RECT rect; 8457 TOOLINFOW_NEW *ptin = (TOOLINFOW_NEW *)pti; 8458 pti->lpszText = LPSTR_TEXTCALLBACKW; 8459 beval->tofree = enc_to_utf16((char_u*)text, NULL); 8460 ptin->lParam = (LPARAM)beval->tofree; 8461 // switch multiline tooltips on 8462 if (GetClientRect(s_textArea, &rect)) 8463 SendMessageW(beval->balloon, TTM_SETMAXTIPWIDTH, 0, 8464 (LPARAM)rect.right); 8465 } 8466 else 8467 { 8468 // do this old way 8469 beval->tofree = enc_to_utf16((char_u*)text, NULL); 8470 pti->lpszText = (LPWSTR)beval->tofree; 8471 } 8472 8473 // Limit ballooneval bounding rect to CursorPos neighbourhood. 8474 pti->rect.left = pt.x - 3; 8475 pti->rect.top = pt.y - 3; 8476 pti->rect.right = pt.x + 3; 8477 pti->rect.bottom = pt.y + 3; 8478 8479 SendMessageW(beval->balloon, TTM_ADDTOOLW, 0, (LPARAM)pti); 8480 // Make tooltip appear sooner. 8481 SendMessageW(beval->balloon, TTM_SETDELAYTIME, TTDT_INITIAL, 10); 8482 // I've performed some tests and it seems the longest possible life time 8483 // of tooltip is 30 seconds. 8484 SendMessageW(beval->balloon, TTM_SETDELAYTIME, TTDT_AUTOPOP, 30000); 8485 /* 8486 * HACK: force tooltip to appear, because it'll not appear until 8487 * first mouse move. D*mn M$ 8488 * Amazingly moving (2, 2) and then (-1, -1) the mouse doesn't move. 8489 */ 8490 mouse_event(MOUSEEVENTF_MOVE, 2, 2, 0, 0); 8491 mouse_event(MOUSEEVENTF_MOVE, (DWORD)-1, (DWORD)-1, 0, 0); 8492 vim_free(pti); 8493 } 8494 8495 static void 8496 make_tooltip(BalloonEval *beval, char *text, POINT pt) 8497 { 8498 TOOLINFO *pti; 8499 int ToolInfoSize; 8500 8501 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage) 8502 { 8503 make_tooltipw(beval, text, pt); 8504 return; 8505 } 8506 8507 if (multiline_balloon_available() == TRUE) 8508 ToolInfoSize = sizeof(TOOLINFO_NEW); 8509 else 8510 ToolInfoSize = sizeof(TOOLINFO); 8511 8512 pti = (TOOLINFO *)alloc(ToolInfoSize); 8513 if (pti == NULL) 8514 return; 8515 8516 beval->balloon = CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS, 8517 NULL, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP, 8518 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 8519 beval->target, NULL, s_hinst, NULL); 8520 8521 SetWindowPos(beval->balloon, HWND_TOPMOST, 0, 0, 0, 0, 8522 SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); 8523 8524 pti->cbSize = ToolInfoSize; 8525 pti->uFlags = TTF_SUBCLASS; 8526 pti->hwnd = beval->target; 8527 pti->hinst = 0; /* Don't use string resources */ 8528 pti->uId = ID_BEVAL_TOOLTIP; 8529 8530 if (multiline_balloon_available() == TRUE) 8531 { 8532 RECT rect; 8533 TOOLINFO_NEW *ptin = (TOOLINFO_NEW *)pti; 8534 pti->lpszText = LPSTR_TEXTCALLBACK; 8535 beval->tofree = vim_strsave((char_u*)text); 8536 ptin->lParam = (LPARAM)beval->tofree; 8537 if (GetClientRect(s_textArea, &rect)) /* switch multiline tooltips on */ 8538 SendMessage(beval->balloon, TTM_SETMAXTIPWIDTH, 0, 8539 (LPARAM)rect.right); 8540 } 8541 else 8542 pti->lpszText = text; /* do this old way */ 8543 8544 /* Limit ballooneval bounding rect to CursorPos neighbourhood */ 8545 pti->rect.left = pt.x - 3; 8546 pti->rect.top = pt.y - 3; 8547 pti->rect.right = pt.x + 3; 8548 pti->rect.bottom = pt.y + 3; 8549 8550 SendMessage(beval->balloon, TTM_ADDTOOL, 0, (LPARAM)pti); 8551 /* Make tooltip appear sooner */ 8552 SendMessage(beval->balloon, TTM_SETDELAYTIME, TTDT_INITIAL, 10); 8553 /* I've performed some tests and it seems the longest possible life time 8554 * of tooltip is 30 seconds */ 8555 SendMessage(beval->balloon, TTM_SETDELAYTIME, TTDT_AUTOPOP, 30000); 8556 /* 8557 * HACK: force tooltip to appear, because it'll not appear until 8558 * first mouse move. D*mn M$ 8559 * Amazingly moving (2, 2) and then (-1, -1) the mouse doesn't move. 8560 */ 8561 mouse_event(MOUSEEVENTF_MOVE, 2, 2, 0, 0); 8562 mouse_event(MOUSEEVENTF_MOVE, (DWORD)-1, (DWORD)-1, 0, 0); 8563 vim_free(pti); 8564 } 8565 8566 static void 8567 delete_tooltip(BalloonEval *beval) 8568 { 8569 PostMessage(beval->balloon, WM_CLOSE, 0, 0); 8570 } 8571 8572 static VOID CALLBACK 8573 BevalTimerProc( 8574 HWND hwnd UNUSED, 8575 UINT uMsg UNUSED, 8576 UINT_PTR idEvent UNUSED, 8577 DWORD dwTime) 8578 { 8579 POINT pt; 8580 RECT rect; 8581 8582 if (cur_beval == NULL || cur_beval->showState == ShS_SHOWING || !p_beval) 8583 return; 8584 8585 GetCursorPos(&pt); 8586 if (WindowFromPoint(pt) != s_textArea) 8587 return; 8588 8589 ScreenToClient(s_textArea, &pt); 8590 GetClientRect(s_textArea, &rect); 8591 if (!PtInRect(&rect, pt)) 8592 return; 8593 8594 if (LastActivity > 0 8595 && (dwTime - LastActivity) >= (DWORD)p_bdlay 8596 && (cur_beval->showState != ShS_PENDING 8597 || abs(cur_beval->x - pt.x) > 3 8598 || abs(cur_beval->y - pt.y) > 3)) 8599 { 8600 /* Pointer resting in one place long enough, it's time to show 8601 * the tooltip. */ 8602 cur_beval->showState = ShS_PENDING; 8603 cur_beval->x = pt.x; 8604 cur_beval->y = pt.y; 8605 8606 // TRACE0("BevalTimerProc: sending request"); 8607 8608 if (cur_beval->msgCB != NULL) 8609 (*cur_beval->msgCB)(cur_beval, 0); 8610 } 8611 } 8612 8613 void 8614 gui_mch_disable_beval_area(BalloonEval *beval UNUSED) 8615 { 8616 // TRACE0("gui_mch_disable_beval_area {{{"); 8617 KillTimer(s_textArea, BevalTimerId); 8618 // TRACE0("gui_mch_disable_beval_area }}}"); 8619 } 8620 8621 void 8622 gui_mch_enable_beval_area(BalloonEval *beval) 8623 { 8624 // TRACE0("gui_mch_enable_beval_area |||"); 8625 if (beval == NULL) 8626 return; 8627 // TRACE0("gui_mch_enable_beval_area {{{"); 8628 BevalTimerId = SetTimer(s_textArea, 0, (UINT)(p_bdlay / 2), BevalTimerProc); 8629 // TRACE0("gui_mch_enable_beval_area }}}"); 8630 } 8631 8632 void 8633 gui_mch_post_balloon(BalloonEval *beval, char_u *mesg) 8634 { 8635 POINT pt; 8636 8637 // TRACE0("gui_mch_post_balloon {{{"); 8638 if (beval->showState == ShS_SHOWING) 8639 return; 8640 GetCursorPos(&pt); 8641 ScreenToClient(s_textArea, &pt); 8642 8643 if (abs(beval->x - pt.x) < 3 && abs(beval->y - pt.y) < 3) 8644 { 8645 /* cursor is still here */ 8646 gui_mch_disable_beval_area(cur_beval); 8647 beval->showState = ShS_SHOWING; 8648 make_tooltip(beval, (char *)mesg, pt); 8649 } 8650 // TRACE0("gui_mch_post_balloon }}}"); 8651 } 8652 8653 BalloonEval * 8654 gui_mch_create_beval_area( 8655 void *target, /* ignored, always use s_textArea */ 8656 char_u *mesg, 8657 void (*mesgCB)(BalloonEval *, int), 8658 void *clientData) 8659 { 8660 /* partially stolen from gui_beval.c */ 8661 BalloonEval *beval; 8662 8663 if (mesg != NULL && mesgCB != NULL) 8664 { 8665 iemsg(_("E232: Cannot create BalloonEval with both message and callback")); 8666 return NULL; 8667 } 8668 8669 beval = (BalloonEval *)alloc_clear(sizeof(BalloonEval)); 8670 if (beval != NULL) 8671 { 8672 beval->target = s_textArea; 8673 8674 beval->showState = ShS_NEUTRAL; 8675 beval->msg = mesg; 8676 beval->msgCB = mesgCB; 8677 beval->clientData = clientData; 8678 8679 InitCommonControls(); 8680 cur_beval = beval; 8681 8682 if (p_beval) 8683 gui_mch_enable_beval_area(beval); 8684 } 8685 return beval; 8686 } 8687 8688 static void 8689 Handle_WM_Notify(HWND hwnd UNUSED, LPNMHDR pnmh) 8690 { 8691 if (pnmh->idFrom != ID_BEVAL_TOOLTIP) /* it is not our tooltip */ 8692 return; 8693 8694 if (cur_beval != NULL) 8695 { 8696 switch (pnmh->code) 8697 { 8698 case TTN_SHOW: 8699 // TRACE0("TTN_SHOW {{{"); 8700 // TRACE0("TTN_SHOW }}}"); 8701 break; 8702 case TTN_POP: /* Before tooltip disappear */ 8703 // TRACE0("TTN_POP {{{"); 8704 delete_tooltip(cur_beval); 8705 gui_mch_enable_beval_area(cur_beval); 8706 // TRACE0("TTN_POP }}}"); 8707 8708 cur_beval->showState = ShS_NEUTRAL; 8709 break; 8710 case TTN_GETDISPINFO: 8711 { 8712 /* if you get there then we have new common controls */ 8713 NMTTDISPINFO_NEW *info = (NMTTDISPINFO_NEW *)pnmh; 8714 info->lpszText = (LPSTR)info->lParam; 8715 info->uFlags |= TTF_DI_SETITEM; 8716 } 8717 break; 8718 case TTN_GETDISPINFOW: 8719 { 8720 // if we get here then we have new common controls 8721 NMTTDISPINFOW_NEW *info = (NMTTDISPINFOW_NEW *)pnmh; 8722 info->lpszText = (LPWSTR)info->lParam; 8723 info->uFlags |= TTF_DI_SETITEM; 8724 } 8725 break; 8726 } 8727 } 8728 } 8729 8730 static void 8731 TrackUserActivity(UINT uMsg) 8732 { 8733 if ((uMsg >= WM_MOUSEFIRST && uMsg <= WM_MOUSELAST) 8734 || (uMsg >= WM_KEYFIRST && uMsg <= WM_KEYLAST)) 8735 LastActivity = GetTickCount(); 8736 } 8737 8738 void 8739 gui_mch_destroy_beval_area(BalloonEval *beval) 8740 { 8741 #ifdef FEAT_VARTABS 8742 vim_free(beval->vts); 8743 #endif 8744 vim_free(beval->tofree); 8745 vim_free(beval); 8746 } 8747 #endif /* FEAT_BEVAL_GUI */ 8748 8749 #if defined(FEAT_NETBEANS_INTG) || defined(PROTO) 8750 /* 8751 * We have multiple signs to draw at the same location. Draw the 8752 * multi-sign indicator (down-arrow) instead. This is the Win32 version. 8753 */ 8754 void 8755 netbeans_draw_multisign_indicator(int row) 8756 { 8757 int i; 8758 int y; 8759 int x; 8760 8761 if (!netbeans_active()) 8762 return; 8763 8764 x = 0; 8765 y = TEXT_Y(row); 8766 8767 #if defined(FEAT_DIRECTX) 8768 if (IS_ENABLE_DIRECTX()) 8769 DWriteContext_Flush(s_dwc); 8770 #endif 8771 8772 for (i = 0; i < gui.char_height - 3; i++) 8773 SetPixel(s_hdc, x+2, y++, gui.currFgColor); 8774 8775 SetPixel(s_hdc, x+0, y, gui.currFgColor); 8776 SetPixel(s_hdc, x+2, y, gui.currFgColor); 8777 SetPixel(s_hdc, x+4, y++, gui.currFgColor); 8778 SetPixel(s_hdc, x+1, y, gui.currFgColor); 8779 SetPixel(s_hdc, x+2, y, gui.currFgColor); 8780 SetPixel(s_hdc, x+3, y++, gui.currFgColor); 8781 SetPixel(s_hdc, x+2, y, gui.currFgColor); 8782 } 8783 #endif 8784