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