1/* vi:set ts=8 sts=4 sw=4: 2 * 3 * VIM - Vi IMproved by Bram Moolenaar 4 * GUI support by Robert Webb 5 * 6 * Do ":help uganda" in Vim to read copying and usage conditions. 7 * Do ":help credits" in Vim to see a list of people who contributed. 8 * See README.txt for an overview of the Vim source code. 9 */ 10/* 11 * gui_w48.c: This file is included in gui_w16.c and gui_w32.c. 12 * 13 * GUI support for Microsoft Windows (Win16 + Win32 = Win48 :-) 14 * 15 * The combined efforts of: 16 * George V. Reilly <george@reilly.org> 17 * Robert Webb 18 * Vince Negri 19 * ...and contributions from many others 20 * 21 */ 22 23#include "vim.h" 24#include "version.h" /* used by dialog box routine for default title */ 25#ifdef DEBUG 26# include <tchar.h> 27#endif 28#ifndef __MINGW32__ 29# include <shellapi.h> 30#endif 31#if defined(FEAT_TOOLBAR) || defined(FEAT_BEVAL) || defined(FEAT_GUI_TABLINE) 32# include <commctrl.h> 33#endif 34#ifdef WIN16 35# include <commdlg.h> 36# include <shellapi.h> 37# ifdef WIN16_3DLOOK 38# include <ctl3d.h> 39# endif 40#endif 41#include <windowsx.h> 42 43#ifdef GLOBAL_IME 44# include "glbl_ime.h" 45#endif 46 47#ifdef FEAT_MENU 48# define MENUHINTS /* show menu hints in command line */ 49#endif 50 51/* Some parameters for dialog boxes. All in pixels. */ 52#define DLG_PADDING_X 10 53#define DLG_PADDING_Y 10 54#define DLG_OLD_STYLE_PADDING_X 5 55#define DLG_OLD_STYLE_PADDING_Y 5 56#define DLG_VERT_PADDING_X 4 /* For vertical buttons */ 57#define DLG_VERT_PADDING_Y 4 58#define DLG_ICON_WIDTH 34 59#define DLG_ICON_HEIGHT 34 60#define DLG_MIN_WIDTH 150 61#define DLG_FONT_NAME "MS Sans Serif" 62#define DLG_FONT_POINT_SIZE 8 63#define DLG_MIN_MAX_WIDTH 400 64#define DLG_MIN_MAX_HEIGHT 400 65 66#define DLG_NONBUTTON_CONTROL 5000 /* First ID of non-button controls */ 67 68#ifndef WM_XBUTTONDOWN /* For Win2K / winME ONLY */ 69# define WM_XBUTTONDOWN 0x020B 70# define WM_XBUTTONUP 0x020C 71# define WM_XBUTTONDBLCLK 0x020D 72# define MK_XBUTTON1 0x0020 73# define MK_XBUTTON2 0x0040 74#endif 75 76#ifdef PROTO 77/* 78 * Define a few things for generating prototypes. This is just to avoid 79 * syntax errors, the defines do not need to be correct. 80 */ 81# define APIENTRY 82# define CALLBACK 83# define CONST 84# define FAR 85# define NEAR 86# define _cdecl 87typedef int BOOL; 88typedef int BYTE; 89typedef int DWORD; 90typedef int WCHAR; 91typedef int ENUMLOGFONT; 92typedef int FINDREPLACE; 93typedef int HANDLE; 94typedef int HBITMAP; 95typedef int HBRUSH; 96typedef int HDROP; 97typedef int INT; 98typedef int LOGFONT[]; 99typedef int LPARAM; 100typedef int LPCREATESTRUCT; 101typedef int LPCSTR; 102typedef int LPCTSTR; 103typedef int LPRECT; 104typedef int LPSTR; 105typedef int LPWINDOWPOS; 106typedef int LPWORD; 107typedef int LRESULT; 108typedef int HRESULT; 109# undef MSG 110typedef int MSG; 111typedef int NEWTEXTMETRIC; 112typedef int OSVERSIONINFO; 113typedef int PWORD; 114typedef int RECT; 115typedef int UINT; 116typedef int WORD; 117typedef int WPARAM; 118typedef int POINT; 119typedef void *HINSTANCE; 120typedef void *HMENU; 121typedef void *HWND; 122typedef void *HDC; 123typedef void VOID; 124typedef int LPNMHDR; 125typedef int LONG; 126#endif 127 128#ifndef GET_X_LPARAM 129# define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp)) 130#endif 131 132static void _OnPaint( HWND hwnd); 133static void clear_rect(RECT *rcp); 134static int gui_mswin_get_menu_height(int fix_window); 135 136static WORD s_dlgfntheight; /* height of the dialog font */ 137static WORD s_dlgfntwidth; /* width of the dialog font */ 138 139#ifdef FEAT_MENU 140static HMENU s_menuBar = NULL; 141#endif 142#ifdef FEAT_TEAROFF 143static void rebuild_tearoff(vimmenu_T *menu); 144static HBITMAP s_htearbitmap; /* bitmap used to indicate tearoff */ 145#endif 146 147/* Flag that is set while processing a message that must not be interrupted by 148 * processing another message. */ 149static int s_busy_processing = FALSE; 150 151static int destroying = FALSE; /* call DestroyWindow() ourselves */ 152 153#ifdef MSWIN_FIND_REPLACE 154static UINT s_findrep_msg = 0; /* set in gui_w[16/32].c */ 155static FINDREPLACE s_findrep_struct; 156# if defined(FEAT_MBYTE) && defined(WIN3264) 157static FINDREPLACEW s_findrep_struct_w; 158# endif 159static HWND s_findrep_hwnd = NULL; 160static int s_findrep_is_find; /* TRUE for find dialog, FALSE 161 for find/replace dialog */ 162#endif 163 164static HINSTANCE s_hinst = NULL; 165#if !defined(FEAT_SNIFF) && !defined(FEAT_GUI) 166static 167#endif 168HWND s_hwnd = NULL; 169static HDC s_hdc = NULL; 170static HBRUSH s_brush = NULL; 171 172#ifdef FEAT_TOOLBAR 173static HWND s_toolbarhwnd = NULL; 174#endif 175 176#ifdef FEAT_GUI_TABLINE 177static HWND s_tabhwnd = NULL; 178static int showing_tabline = 0; 179#endif 180 181static WPARAM s_wParam = 0; 182static LPARAM s_lParam = 0; 183 184static HWND s_textArea = NULL; 185static UINT s_uMsg = 0; 186 187static char_u *s_textfield; /* Used by dialogs to pass back strings */ 188 189static int s_need_activate = FALSE; 190 191/* This variable is set when waiting for an event, which is the only moment 192 * scrollbar dragging can be done directly. It's not allowed while commands 193 * are executed, because it may move the cursor and that may cause unexpected 194 * problems (e.g., while ":s" is working). 195 */ 196static int allow_scrollbar = FALSE; 197 198#ifdef GLOBAL_IME 199# define MyTranslateMessage(x) global_ime_TranslateMessage(x) 200#else 201# define MyTranslateMessage(x) TranslateMessage(x) 202#endif 203 204#if (defined(WIN3264) && defined(FEAT_MBYTE)) || defined(GLOBAL_IME) 205 /* use of WindowProc depends on wide_WindowProc */ 206# define MyWindowProc vim_WindowProc 207#else 208 /* use ordinary WindowProc */ 209# define MyWindowProc DefWindowProc 210#endif 211 212extern int current_font_height; /* this is in os_mswin.c */ 213 214static struct 215{ 216 UINT key_sym; 217 char_u vim_code0; 218 char_u vim_code1; 219} special_keys[] = 220{ 221 {VK_UP, 'k', 'u'}, 222 {VK_DOWN, 'k', 'd'}, 223 {VK_LEFT, 'k', 'l'}, 224 {VK_RIGHT, 'k', 'r'}, 225 226 {VK_F1, 'k', '1'}, 227 {VK_F2, 'k', '2'}, 228 {VK_F3, 'k', '3'}, 229 {VK_F4, 'k', '4'}, 230 {VK_F5, 'k', '5'}, 231 {VK_F6, 'k', '6'}, 232 {VK_F7, 'k', '7'}, 233 {VK_F8, 'k', '8'}, 234 {VK_F9, 'k', '9'}, 235 {VK_F10, 'k', ';'}, 236 237 {VK_F11, 'F', '1'}, 238 {VK_F12, 'F', '2'}, 239 {VK_F13, 'F', '3'}, 240 {VK_F14, 'F', '4'}, 241 {VK_F15, 'F', '5'}, 242 {VK_F16, 'F', '6'}, 243 {VK_F17, 'F', '7'}, 244 {VK_F18, 'F', '8'}, 245 {VK_F19, 'F', '9'}, 246 {VK_F20, 'F', 'A'}, 247 248 {VK_F21, 'F', 'B'}, 249#ifdef FEAT_NETBEANS_INTG 250 {VK_PAUSE, 'F', 'B'}, /* Pause == F21 (see gui_gtk_x11.c) */ 251#endif 252 {VK_F22, 'F', 'C'}, 253 {VK_F23, 'F', 'D'}, 254 {VK_F24, 'F', 'E'}, /* winuser.h defines up to F24 */ 255 256 {VK_HELP, '%', '1'}, 257 {VK_BACK, 'k', 'b'}, 258 {VK_INSERT, 'k', 'I'}, 259 {VK_DELETE, 'k', 'D'}, 260 {VK_HOME, 'k', 'h'}, 261 {VK_END, '@', '7'}, 262 {VK_PRIOR, 'k', 'P'}, 263 {VK_NEXT, 'k', 'N'}, 264 {VK_PRINT, '%', '9'}, 265 {VK_ADD, 'K', '6'}, 266 {VK_SUBTRACT, 'K', '7'}, 267 {VK_DIVIDE, 'K', '8'}, 268 {VK_MULTIPLY, 'K', '9'}, 269 {VK_SEPARATOR, 'K', 'A'}, /* Keypad Enter */ 270 {VK_DECIMAL, 'K', 'B'}, 271 272 {VK_NUMPAD0, 'K', 'C'}, 273 {VK_NUMPAD1, 'K', 'D'}, 274 {VK_NUMPAD2, 'K', 'E'}, 275 {VK_NUMPAD3, 'K', 'F'}, 276 {VK_NUMPAD4, 'K', 'G'}, 277 {VK_NUMPAD5, 'K', 'H'}, 278 {VK_NUMPAD6, 'K', 'I'}, 279 {VK_NUMPAD7, 'K', 'J'}, 280 {VK_NUMPAD8, 'K', 'K'}, 281 {VK_NUMPAD9, 'K', 'L'}, 282 283 /* Keys that we want to be able to use any modifier with: */ 284 {VK_SPACE, ' ', NUL}, 285 {VK_TAB, TAB, NUL}, 286 {VK_ESCAPE, ESC, NUL}, 287 {NL, NL, NUL}, 288 {CAR, CAR, NUL}, 289 290 /* End of list marker: */ 291 {0, 0, 0} 292}; 293 294/* Local variables */ 295static int s_button_pending = -1; 296 297/* s_getting_focus is set when we got focus but didn't see mouse-up event yet, 298 * so don't reset s_button_pending. */ 299static int s_getting_focus = FALSE; 300 301static int s_x_pending; 302static int s_y_pending; 303static UINT s_kFlags_pending; 304static UINT s_wait_timer = 0; /* Timer for get char from user */ 305static int s_timed_out = FALSE; 306static int dead_key = 0; /* 0 - no dead key, 1 - dead key pressed */ 307 308#ifdef WIN3264 309static OSVERSIONINFO os_version; /* like it says. Init in gui_mch_init() */ 310#endif 311 312#ifdef FEAT_BEVAL 313/* balloon-eval WM_NOTIFY_HANDLER */ 314static void Handle_WM_Notify __ARGS((HWND hwnd, LPNMHDR pnmh)); 315static void TrackUserActivity __ARGS((UINT uMsg)); 316#endif 317 318/* 319 * For control IME. 320 */ 321#ifdef FEAT_MBYTE 322# ifdef USE_IM_CONTROL 323static LOGFONT norm_logfont; 324# endif 325#endif 326 327#ifdef FEAT_MBYTE_IME 328static LRESULT _OnImeNotify(HWND hWnd, DWORD dwCommand, DWORD dwData); 329#endif 330 331#ifdef DEBUG_PRINT_ERROR 332/* 333 * Print out the last Windows error message 334 */ 335 static void 336print_windows_error(void) 337{ 338 LPVOID lpMsgBuf; 339 340 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, 341 NULL, GetLastError(), 342 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 343 (LPTSTR) &lpMsgBuf, 0, NULL); 344 TRACE1("Error: %s\n", lpMsgBuf); 345 LocalFree(lpMsgBuf); 346} 347#endif 348 349/* 350 * Cursor blink functions. 351 * 352 * This is a simple state machine: 353 * BLINK_NONE not blinking at all 354 * BLINK_OFF blinking, cursor is not shown 355 * BLINK_ON blinking, cursor is shown 356 */ 357 358#define BLINK_NONE 0 359#define BLINK_OFF 1 360#define BLINK_ON 2 361 362static int blink_state = BLINK_NONE; 363static long_u blink_waittime = 700; 364static long_u blink_ontime = 400; 365static long_u blink_offtime = 250; 366static UINT blink_timer = 0; 367 368 void 369gui_mch_set_blinking(long wait, long on, long off) 370{ 371 blink_waittime = wait; 372 blink_ontime = on; 373 blink_offtime = off; 374} 375 376/* ARGSUSED */ 377 static VOID CALLBACK 378_OnBlinkTimer( 379 HWND hwnd, 380 UINT uMsg, 381 UINT idEvent, 382 DWORD dwTime) 383{ 384 MSG msg; 385 386 /* 387 TRACE2("Got timer event, id %d, blink_timer %d\n", idEvent, blink_timer); 388 */ 389 390 KillTimer(NULL, idEvent); 391 392 /* Eat spurious WM_TIMER messages */ 393 while (PeekMessage(&msg, hwnd, WM_TIMER, WM_TIMER, PM_REMOVE)) 394 ; 395 396 if (blink_state == BLINK_ON) 397 { 398 gui_undraw_cursor(); 399 blink_state = BLINK_OFF; 400 blink_timer = (UINT) SetTimer(NULL, 0, (UINT)blink_offtime, 401 (TIMERPROC)_OnBlinkTimer); 402 } 403 else 404 { 405 gui_update_cursor(TRUE, FALSE); 406 blink_state = BLINK_ON; 407 blink_timer = (UINT) SetTimer(NULL, 0, (UINT)blink_ontime, 408 (TIMERPROC)_OnBlinkTimer); 409 } 410} 411 412 static void 413gui_mswin_rm_blink_timer(void) 414{ 415 MSG msg; 416 417 if (blink_timer != 0) 418 { 419 KillTimer(NULL, blink_timer); 420 /* Eat spurious WM_TIMER messages */ 421 while (PeekMessage(&msg, s_hwnd, WM_TIMER, WM_TIMER, PM_REMOVE)) 422 ; 423 blink_timer = 0; 424 } 425} 426 427/* 428 * Stop the cursor blinking. Show the cursor if it wasn't shown. 429 */ 430 void 431gui_mch_stop_blink(void) 432{ 433 gui_mswin_rm_blink_timer(); 434 if (blink_state == BLINK_OFF) 435 gui_update_cursor(TRUE, FALSE); 436 blink_state = BLINK_NONE; 437} 438 439/* 440 * Start the cursor blinking. If it was already blinking, this restarts the 441 * waiting time and shows the cursor. 442 */ 443 void 444gui_mch_start_blink(void) 445{ 446 gui_mswin_rm_blink_timer(); 447 448 /* Only switch blinking on if none of the times is zero */ 449 if (blink_waittime && blink_ontime && blink_offtime && gui.in_focus) 450 { 451 blink_timer = (UINT)SetTimer(NULL, 0, (UINT)blink_waittime, 452 (TIMERPROC)_OnBlinkTimer); 453 blink_state = BLINK_ON; 454 gui_update_cursor(TRUE, FALSE); 455 } 456} 457 458/* 459 * Call-back routines. 460 */ 461 462/*ARGSUSED*/ 463 static VOID CALLBACK 464_OnTimer( 465 HWND hwnd, 466 UINT uMsg, 467 UINT idEvent, 468 DWORD dwTime) 469{ 470 MSG msg; 471 472 /* 473 TRACE2("Got timer event, id %d, s_wait_timer %d\n", idEvent, s_wait_timer); 474 */ 475 KillTimer(NULL, idEvent); 476 s_timed_out = TRUE; 477 478 /* Eat spurious WM_TIMER messages */ 479 while (PeekMessage(&msg, hwnd, WM_TIMER, WM_TIMER, PM_REMOVE)) 480 ; 481 if (idEvent == s_wait_timer) 482 s_wait_timer = 0; 483} 484 485/*ARGSUSED*/ 486 static void 487_OnDeadChar( 488 HWND hwnd, 489 UINT ch, 490 int cRepeat) 491{ 492 dead_key = 1; 493} 494 495/* 496 * Convert Unicode character "ch" to bytes in "string[slen]". 497 * When "had_alt" is TRUE the ALT key was included in "ch". 498 * Return the length. 499 */ 500 static int 501char_to_string(int ch, char_u *string, int slen, int had_alt) 502{ 503 int len; 504 int i; 505#ifdef FEAT_MBYTE 506 WCHAR wstring[2]; 507 char_u *ws = NULL;; 508 509 if (os_version.dwPlatformId != VER_PLATFORM_WIN32_NT) 510 { 511 /* On Windows 95/98 we apparently get the character in the active 512 * codepage, not in UCS-2. If conversion is needed convert it to 513 * UCS-2 first. */ 514 if ((int)GetACP() == enc_codepage) 515 len = 0; /* no conversion required */ 516 else 517 { 518 string[0] = ch; 519 len = MultiByteToWideChar(GetACP(), 0, string, 1, wstring, 2); 520 } 521 } 522 else 523 { 524 wstring[0] = ch; 525 len = 1; 526 } 527 528 if (len > 0) 529 { 530 /* "ch" is a UTF-16 character. Convert it to a string of bytes. When 531 * "enc_codepage" is non-zero use the standard Win32 function, 532 * otherwise use our own conversion function (e.g., for UTF-8). */ 533 if (enc_codepage > 0) 534 { 535 len = WideCharToMultiByte(enc_codepage, 0, wstring, len, 536 string, slen, 0, NULL); 537 /* If we had included the ALT key into the character but now the 538 * upper bit is no longer set, that probably means the conversion 539 * failed. Convert the original character and set the upper bit 540 * afterwards. */ 541 if (had_alt && len == 1 && ch >= 0x80 && string[0] < 0x80) 542 { 543 wstring[0] = ch & 0x7f; 544 len = WideCharToMultiByte(enc_codepage, 0, wstring, len, 545 string, slen, 0, NULL); 546 if (len == 1) /* safety check */ 547 string[0] |= 0x80; 548 } 549 } 550 else 551 { 552 len = 1; 553 ws = utf16_to_enc(wstring, &len); 554 if (ws == NULL) 555 len = 0; 556 else 557 { 558 if (len > slen) /* just in case */ 559 len = slen; 560 mch_memmove(string, ws, len); 561 vim_free(ws); 562 } 563 } 564 } 565 566 if (len == 0) 567#endif 568 { 569 string[0] = ch; 570 len = 1; 571 } 572 573 for (i = 0; i < len; ++i) 574 if (string[i] == CSI && len <= slen - 2) 575 { 576 /* Insert CSI as K_CSI. */ 577 mch_memmove(string + i + 3, string + i + 1, len - i - 1); 578 string[++i] = KS_EXTRA; 579 string[++i] = (int)KE_CSI; 580 len += 2; 581 } 582 583 return len; 584} 585 586/* 587 * Key hit, add it to the input buffer. 588 */ 589/*ARGSUSED*/ 590 static void 591_OnChar( 592 HWND hwnd, 593 UINT ch, 594 int cRepeat) 595{ 596 char_u string[40]; 597 int len = 0; 598 599 len = char_to_string(ch, string, 40, FALSE); 600 if (len == 1 && string[0] == Ctrl_C && ctrl_c_interrupts) 601 { 602 trash_input_buf(); 603 got_int = TRUE; 604 } 605 606 add_to_input_buf(string, len); 607} 608 609/* 610 * Alt-Key hit, add it to the input buffer. 611 */ 612/*ARGSUSED*/ 613 static void 614_OnSysChar( 615 HWND hwnd, 616 UINT cch, 617 int cRepeat) 618{ 619 char_u string[40]; /* Enough for multibyte character */ 620 int len; 621 int modifiers; 622 int ch = cch; /* special keys are negative */ 623 624 /* TRACE("OnSysChar(%d, %c)\n", ch, ch); */ 625 626 /* OK, we have a character key (given by ch) which was entered with the 627 * ALT key pressed. Eg, if the user presses Alt-A, then ch == 'A'. Note 628 * that the system distinguishes Alt-a and Alt-A (Alt-Shift-a unless 629 * CAPSLOCK is pressed) at this point. 630 */ 631 modifiers = MOD_MASK_ALT; 632 if (GetKeyState(VK_SHIFT) & 0x8000) 633 modifiers |= MOD_MASK_SHIFT; 634 if (GetKeyState(VK_CONTROL) & 0x8000) 635 modifiers |= MOD_MASK_CTRL; 636 637 ch = simplify_key(ch, &modifiers); 638 /* remove the SHIFT modifier for keys where it's already included, e.g., 639 * '(' and '*' */ 640 if (ch < 0x100 && !isalpha(ch) && isprint(ch)) 641 modifiers &= ~MOD_MASK_SHIFT; 642 643 /* Interpret the ALT key as making the key META, include SHIFT, etc. */ 644 ch = extract_modifiers(ch, &modifiers); 645 if (ch == CSI) 646 ch = K_CSI; 647 648 len = 0; 649 if (modifiers) 650 { 651 string[len++] = CSI; 652 string[len++] = KS_MODIFIER; 653 string[len++] = modifiers; 654 } 655 656 if (IS_SPECIAL((int)ch)) 657 { 658 string[len++] = CSI; 659 string[len++] = K_SECOND((int)ch); 660 string[len++] = K_THIRD((int)ch); 661 } 662 else 663 { 664 /* Although the documentation isn't clear about it, we assume "ch" is 665 * a Unicode character. */ 666 len += char_to_string(ch, string + len, 40 - len, TRUE); 667 } 668 669 add_to_input_buf(string, len); 670} 671 672 static void 673_OnMouseEvent( 674 int button, 675 int x, 676 int y, 677 int repeated_click, 678 UINT keyFlags) 679{ 680 int vim_modifiers = 0x0; 681 682 s_getting_focus = FALSE; 683 684 if (keyFlags & MK_SHIFT) 685 vim_modifiers |= MOUSE_SHIFT; 686 if (keyFlags & MK_CONTROL) 687 vim_modifiers |= MOUSE_CTRL; 688 if (GetKeyState(VK_MENU) & 0x8000) 689 vim_modifiers |= MOUSE_ALT; 690 691 gui_send_mouse_event(button, x, y, repeated_click, vim_modifiers); 692} 693 694/*ARGSUSED*/ 695 static void 696_OnMouseButtonDown( 697 HWND hwnd, 698 BOOL fDoubleClick, 699 int x, 700 int y, 701 UINT keyFlags) 702{ 703 static LONG s_prevTime = 0; 704 705 LONG currentTime = GetMessageTime(); 706 int button = -1; 707 int repeated_click; 708 709 /* Give main window the focus: this is so the cursor isn't hollow. */ 710 (void)SetFocus(s_hwnd); 711 712 if (s_uMsg == WM_LBUTTONDOWN || s_uMsg == WM_LBUTTONDBLCLK) 713 button = MOUSE_LEFT; 714 else if (s_uMsg == WM_MBUTTONDOWN || s_uMsg == WM_MBUTTONDBLCLK) 715 button = MOUSE_MIDDLE; 716 else if (s_uMsg == WM_RBUTTONDOWN || s_uMsg == WM_RBUTTONDBLCLK) 717 button = MOUSE_RIGHT; 718#ifndef WIN16 /*<VN>*/ 719 else if (s_uMsg == WM_XBUTTONDOWN || s_uMsg == WM_XBUTTONDBLCLK) 720 { 721#ifndef GET_XBUTTON_WPARAM 722# define GET_XBUTTON_WPARAM(wParam) (HIWORD(wParam)) 723#endif 724 button = ((GET_XBUTTON_WPARAM(s_wParam) == 1) ? MOUSE_X1 : MOUSE_X2); 725 } 726 else if (s_uMsg == WM_CAPTURECHANGED) 727 { 728 /* on W95/NT4, somehow you get in here with an odd Msg 729 * if you press one button while holding down the other..*/ 730 if (s_button_pending == MOUSE_LEFT) 731 button = MOUSE_RIGHT; 732 else 733 button = MOUSE_LEFT; 734 } 735#endif 736 if (button >= 0) 737 { 738 repeated_click = ((int)(currentTime - s_prevTime) < p_mouset); 739 740 /* 741 * Holding down the left and right buttons simulates pushing the middle 742 * button. 743 */ 744 if (repeated_click 745 && ((button == MOUSE_LEFT && s_button_pending == MOUSE_RIGHT) 746 || (button == MOUSE_RIGHT 747 && s_button_pending == MOUSE_LEFT))) 748 { 749 /* 750 * Hmm, gui.c will ignore more than one button down at a time, so 751 * pretend we let go of it first. 752 */ 753 gui_send_mouse_event(MOUSE_RELEASE, x, y, FALSE, 0x0); 754 button = MOUSE_MIDDLE; 755 repeated_click = FALSE; 756 s_button_pending = -1; 757 _OnMouseEvent(button, x, y, repeated_click, keyFlags); 758 } 759 else if ((repeated_click) 760 || (mouse_model_popup() && (button == MOUSE_RIGHT))) 761 { 762 if (s_button_pending > -1) 763 { 764 _OnMouseEvent(s_button_pending, x, y, FALSE, keyFlags); 765 s_button_pending = -1; 766 } 767 /* TRACE("Button down at x %d, y %d\n", x, y); */ 768 _OnMouseEvent(button, x, y, repeated_click, keyFlags); 769 } 770 else 771 { 772 /* 773 * If this is the first press (i.e. not a multiple click) don't 774 * action immediately, but store and wait for: 775 * i) button-up 776 * ii) mouse move 777 * iii) another button press 778 * before using it. 779 * This enables us to make left+right simulate middle button, 780 * without left or right being actioned first. The side-effect is 781 * that if you click and hold the mouse without dragging, the 782 * cursor doesn't move until you release the button. In practice 783 * this is hardly a problem. 784 */ 785 s_button_pending = button; 786 s_x_pending = x; 787 s_y_pending = y; 788 s_kFlags_pending = keyFlags; 789 } 790 791 s_prevTime = currentTime; 792 } 793} 794 795/*ARGSUSED*/ 796 static void 797_OnMouseMoveOrRelease( 798 HWND hwnd, 799 int x, 800 int y, 801 UINT keyFlags) 802{ 803 int button; 804 805 s_getting_focus = FALSE; 806 if (s_button_pending > -1) 807 { 808 /* Delayed action for mouse down event */ 809 _OnMouseEvent(s_button_pending, s_x_pending, 810 s_y_pending, FALSE, s_kFlags_pending); 811 s_button_pending = -1; 812 } 813 if (s_uMsg == WM_MOUSEMOVE) 814 { 815 /* 816 * It's only a MOUSE_DRAG if one or more mouse buttons are being held 817 * down. 818 */ 819 if (!(keyFlags & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON 820 | MK_XBUTTON1 | MK_XBUTTON2))) 821 { 822 gui_mouse_moved(x, y); 823 return; 824 } 825 826 /* 827 * While button is down, keep grabbing mouse move events when 828 * the mouse goes outside the window 829 */ 830 SetCapture(s_textArea); 831 button = MOUSE_DRAG; 832 /* TRACE(" move at x %d, y %d\n", x, y); */ 833 } 834 else 835 { 836 ReleaseCapture(); 837 button = MOUSE_RELEASE; 838 /* TRACE(" up at x %d, y %d\n", x, y); */ 839 } 840 841 _OnMouseEvent(button, x, y, FALSE, keyFlags); 842} 843 844#ifdef FEAT_MENU 845/* 846 * Find the vimmenu_T with the given id 847 */ 848 static vimmenu_T * 849gui_mswin_find_menu( 850 vimmenu_T *pMenu, 851 int id) 852{ 853 vimmenu_T *pChildMenu; 854 855 while (pMenu) 856 { 857 if (pMenu->id == (UINT)id) 858 break; 859 if (pMenu->children != NULL) 860 { 861 pChildMenu = gui_mswin_find_menu(pMenu->children, id); 862 if (pChildMenu) 863 { 864 pMenu = pChildMenu; 865 break; 866 } 867 } 868 pMenu = pMenu->next; 869 } 870 return pMenu; 871} 872 873/*ARGSUSED*/ 874 static void 875_OnMenu( 876 HWND hwnd, 877 int id, 878 HWND hwndCtl, 879 UINT codeNotify) 880{ 881 vimmenu_T *pMenu; 882 883 pMenu = gui_mswin_find_menu(root_menu, id); 884 if (pMenu) 885 gui_menu_cb(pMenu); 886} 887#endif 888 889#ifdef MSWIN_FIND_REPLACE 890# if defined(FEAT_MBYTE) && defined(WIN3264) 891/* 892 * copy useful data from structure LPFINDREPLACE to structure LPFINDREPLACEW 893 */ 894 static void 895findrep_atow(LPFINDREPLACEW lpfrw, LPFINDREPLACE lpfr) 896{ 897 WCHAR *wp; 898 899 lpfrw->hwndOwner = lpfr->hwndOwner; 900 lpfrw->Flags = lpfr->Flags; 901 902 wp = enc_to_utf16(lpfr->lpstrFindWhat, NULL); 903 wcsncpy(lpfrw->lpstrFindWhat, wp, lpfrw->wFindWhatLen - 1); 904 vim_free(wp); 905 906 /* the field "lpstrReplaceWith" doesn't need to be copied */ 907} 908 909/* 910 * copy useful data from structure LPFINDREPLACEW to structure LPFINDREPLACE 911 */ 912 static void 913findrep_wtoa(LPFINDREPLACE lpfr, LPFINDREPLACEW lpfrw) 914{ 915 char_u *p; 916 917 lpfr->Flags = lpfrw->Flags; 918 919 p = utf16_to_enc(lpfrw->lpstrFindWhat, NULL); 920 vim_strncpy(lpfr->lpstrFindWhat, p, lpfr->wFindWhatLen - 1); 921 vim_free(p); 922 923 p = utf16_to_enc(lpfrw->lpstrReplaceWith, NULL); 924 vim_strncpy(lpfr->lpstrReplaceWith, p, lpfr->wReplaceWithLen - 1); 925 vim_free(p); 926} 927# endif 928 929/* 930 * Handle a Find/Replace window message. 931 */ 932 static void 933_OnFindRepl(void) 934{ 935 int flags = 0; 936 int down; 937 938# if defined(FEAT_MBYTE) && defined(WIN3264) 939 /* If the OS is Windows NT, and 'encoding' differs from active codepage: 940 * convert text from wide string. */ 941 if (os_version.dwPlatformId == VER_PLATFORM_WIN32_NT 942 && enc_codepage >= 0 && (int)GetACP() != enc_codepage) 943 { 944 findrep_wtoa(&s_findrep_struct, &s_findrep_struct_w); 945 } 946# endif 947 948 if (s_findrep_struct.Flags & FR_DIALOGTERM) 949 /* Give main window the focus back. */ 950 (void)SetFocus(s_hwnd); 951 952 if (s_findrep_struct.Flags & FR_FINDNEXT) 953 { 954 flags = FRD_FINDNEXT; 955 956 /* Give main window the focus back: this is so the cursor isn't 957 * hollow. */ 958 (void)SetFocus(s_hwnd); 959 } 960 else if (s_findrep_struct.Flags & FR_REPLACE) 961 { 962 flags = FRD_REPLACE; 963 964 /* Give main window the focus back: this is so the cursor isn't 965 * hollow. */ 966 (void)SetFocus(s_hwnd); 967 } 968 else if (s_findrep_struct.Flags & FR_REPLACEALL) 969 { 970 flags = FRD_REPLACEALL; 971 } 972 973 if (flags != 0) 974 { 975 /* Call the generic GUI function to do the actual work. */ 976 if (s_findrep_struct.Flags & FR_WHOLEWORD) 977 flags |= FRD_WHOLE_WORD; 978 if (s_findrep_struct.Flags & FR_MATCHCASE) 979 flags |= FRD_MATCH_CASE; 980 down = (s_findrep_struct.Flags & FR_DOWN) != 0; 981 gui_do_findrepl(flags, s_findrep_struct.lpstrFindWhat, 982 s_findrep_struct.lpstrReplaceWith, down); 983 } 984} 985#endif 986 987 static void 988HandleMouseHide(UINT uMsg, LPARAM lParam) 989{ 990 static LPARAM last_lParam = 0L; 991 992 /* We sometimes get a mousemove when the mouse didn't move... */ 993 if (uMsg == WM_MOUSEMOVE) 994 { 995 if (lParam == last_lParam) 996 return; 997 last_lParam = lParam; 998 } 999 1000 /* Handle specially, to centralise coding. We need to be sure we catch all 1001 * possible events which should cause us to restore the cursor (as it is a 1002 * shared resource, we take full responsibility for it). 1003 */ 1004 switch (uMsg) 1005 { 1006 case WM_KEYUP: 1007 case WM_CHAR: 1008 /* 1009 * blank out the pointer if necessary 1010 */ 1011 if (p_mh) 1012 gui_mch_mousehide(TRUE); 1013 break; 1014 1015 case WM_SYSKEYUP: /* show the pointer when a system-key is pressed */ 1016 case WM_SYSCHAR: 1017 case WM_MOUSEMOVE: /* show the pointer on any mouse action */ 1018 case WM_LBUTTONDOWN: 1019 case WM_LBUTTONUP: 1020 case WM_MBUTTONDOWN: 1021 case WM_MBUTTONUP: 1022 case WM_RBUTTONDOWN: 1023 case WM_RBUTTONUP: 1024 case WM_XBUTTONDOWN: 1025 case WM_XBUTTONUP: 1026 case WM_NCMOUSEMOVE: 1027 case WM_NCLBUTTONDOWN: 1028 case WM_NCLBUTTONUP: 1029 case WM_NCMBUTTONDOWN: 1030 case WM_NCMBUTTONUP: 1031 case WM_NCRBUTTONDOWN: 1032 case WM_NCRBUTTONUP: 1033 case WM_KILLFOCUS: 1034 /* 1035 * if the pointer is currently hidden, then we should show it. 1036 */ 1037 gui_mch_mousehide(FALSE); 1038 break; 1039 } 1040} 1041 1042 static LRESULT CALLBACK 1043_TextAreaWndProc( 1044 HWND hwnd, 1045 UINT uMsg, 1046 WPARAM wParam, 1047 LPARAM lParam) 1048{ 1049 /* 1050 TRACE("TextAreaWndProc: hwnd = %08x, msg = %x, wParam = %x, lParam = %x\n", 1051 hwnd, uMsg, wParam, lParam); 1052 */ 1053 1054 HandleMouseHide(uMsg, lParam); 1055 1056 s_uMsg = uMsg; 1057 s_wParam = wParam; 1058 s_lParam = lParam; 1059 1060#ifdef FEAT_BEVAL 1061 TrackUserActivity(uMsg); 1062#endif 1063 1064 switch (uMsg) 1065 { 1066 HANDLE_MSG(hwnd, WM_LBUTTONDBLCLK,_OnMouseButtonDown); 1067 HANDLE_MSG(hwnd, WM_LBUTTONDOWN,_OnMouseButtonDown); 1068 HANDLE_MSG(hwnd, WM_LBUTTONUP, _OnMouseMoveOrRelease); 1069 HANDLE_MSG(hwnd, WM_MBUTTONDBLCLK,_OnMouseButtonDown); 1070 HANDLE_MSG(hwnd, WM_MBUTTONDOWN,_OnMouseButtonDown); 1071 HANDLE_MSG(hwnd, WM_MBUTTONUP, _OnMouseMoveOrRelease); 1072 HANDLE_MSG(hwnd, WM_MOUSEMOVE, _OnMouseMoveOrRelease); 1073 HANDLE_MSG(hwnd, WM_PAINT, _OnPaint); 1074 HANDLE_MSG(hwnd, WM_RBUTTONDBLCLK,_OnMouseButtonDown); 1075 HANDLE_MSG(hwnd, WM_RBUTTONDOWN,_OnMouseButtonDown); 1076 HANDLE_MSG(hwnd, WM_RBUTTONUP, _OnMouseMoveOrRelease); 1077#ifndef WIN16 /*<VN>*/ 1078 HANDLE_MSG(hwnd, WM_XBUTTONDBLCLK,_OnMouseButtonDown); 1079 HANDLE_MSG(hwnd, WM_XBUTTONDOWN,_OnMouseButtonDown); 1080 HANDLE_MSG(hwnd, WM_XBUTTONUP, _OnMouseMoveOrRelease); 1081#endif 1082 1083#ifdef FEAT_BEVAL 1084 case WM_NOTIFY: Handle_WM_Notify(hwnd, (LPNMHDR)lParam); 1085 return TRUE; 1086#endif 1087 default: 1088 return MyWindowProc(hwnd, uMsg, wParam, lParam); 1089 } 1090} 1091 1092#if (defined(WIN3264) && defined(FEAT_MBYTE)) \ 1093 || defined(GLOBAL_IME) \ 1094 || defined(PROTO) 1095# ifdef PROTO 1096typedef int WINAPI; 1097# endif 1098 1099 LRESULT WINAPI 1100vim_WindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) 1101{ 1102# ifdef GLOBAL_IME 1103 return global_ime_DefWindowProc(hwnd, message, wParam, lParam); 1104# else 1105 if (wide_WindowProc) 1106 return DefWindowProcW(hwnd, message, wParam, lParam); 1107 return DefWindowProc(hwnd, message, wParam, lParam); 1108#endif 1109} 1110#endif 1111 1112/* 1113 * Called when the foreground or background color has been changed. 1114 */ 1115 void 1116gui_mch_new_colors(void) 1117{ 1118 /* nothing to do? */ 1119} 1120 1121/* 1122 * Set the colors to their default values. 1123 */ 1124 void 1125gui_mch_def_colors() 1126{ 1127 gui.norm_pixel = GetSysColor(COLOR_WINDOWTEXT); 1128 gui.back_pixel = GetSysColor(COLOR_WINDOW); 1129 gui.def_norm_pixel = gui.norm_pixel; 1130 gui.def_back_pixel = gui.back_pixel; 1131} 1132 1133/* 1134 * Open the GUI window which was created by a call to gui_mch_init(). 1135 */ 1136 int 1137gui_mch_open(void) 1138{ 1139#ifndef SW_SHOWDEFAULT 1140# define SW_SHOWDEFAULT 10 /* Borland 5.0 doesn't have it */ 1141#endif 1142 /* Actually open the window, if not already visible 1143 * (may be done already in gui_mch_set_shellsize) */ 1144 if (!IsWindowVisible(s_hwnd)) 1145 ShowWindow(s_hwnd, SW_SHOWDEFAULT); 1146 1147#ifdef MSWIN_FIND_REPLACE 1148 /* Init replace string here, so that we keep it when re-opening the 1149 * dialog. */ 1150 s_findrep_struct.lpstrReplaceWith[0] = NUL; 1151#endif 1152 1153 return OK; 1154} 1155 1156/* 1157 * Get the position of the top left corner of the window. 1158 */ 1159 int 1160gui_mch_get_winpos(int *x, int *y) 1161{ 1162 RECT rect; 1163 1164 GetWindowRect(s_hwnd, &rect); 1165 *x = rect.left; 1166 *y = rect.top; 1167 return OK; 1168} 1169 1170/* 1171 * Set the position of the top left corner of the window to the given 1172 * coordinates. 1173 */ 1174 void 1175gui_mch_set_winpos(int x, int y) 1176{ 1177 SetWindowPos(s_hwnd, NULL, x, y, 0, 0, 1178 SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE); 1179} 1180 void 1181gui_mch_set_text_area_pos(int x, int y, int w, int h) 1182{ 1183 static int oldx = 0; 1184 static int oldy = 0; 1185 1186 SetWindowPos(s_textArea, NULL, x, y, w, h, SWP_NOZORDER | SWP_NOACTIVATE); 1187 1188#ifdef FEAT_TOOLBAR 1189 if (vim_strchr(p_go, GO_TOOLBAR) != NULL) 1190 SendMessage(s_toolbarhwnd, WM_SIZE, 1191 (WPARAM)0, (LPARAM)(w + ((long)(TOOLBAR_BUTTON_HEIGHT+8)<<16))); 1192#endif 1193#if defined(FEAT_GUI_TABLINE) 1194 if (showing_tabline) 1195 { 1196 int top = 0; 1197 RECT rect; 1198 1199# ifdef FEAT_TOOLBAR 1200 if (vim_strchr(p_go, GO_TOOLBAR) != NULL) 1201 top = TOOLBAR_BUTTON_HEIGHT + TOOLBAR_BORDER_HEIGHT; 1202# endif 1203 GetClientRect(s_hwnd, &rect); 1204 MoveWindow(s_tabhwnd, 0, top, rect.right, gui.tabline_height, TRUE); 1205 } 1206#endif 1207 1208 /* When side scroll bar is unshown, the size of window will change. 1209 * then, the text area move left or right. thus client rect should be 1210 * forcely redraw. (Yasuhiro Matsumoto) */ 1211 if (oldx != x || oldy != y) 1212 { 1213 InvalidateRect(s_hwnd, NULL, FALSE); 1214 oldx = x; 1215 oldy = y; 1216 } 1217} 1218 1219 1220/* 1221 * Scrollbar stuff: 1222 */ 1223 1224 void 1225gui_mch_enable_scrollbar( 1226 scrollbar_T *sb, 1227 int flag) 1228{ 1229 ShowScrollBar(sb->id, SB_CTL, flag); 1230 1231 /* TODO: When the window is maximized, the size of the window stays the 1232 * same, thus the size of the text area changes. On Win98 it's OK, on Win 1233 * NT 4.0 it's not... */ 1234} 1235 1236 void 1237gui_mch_set_scrollbar_pos( 1238 scrollbar_T *sb, 1239 int x, 1240 int y, 1241 int w, 1242 int h) 1243{ 1244 SetWindowPos(sb->id, NULL, x, y, w, h, 1245 SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW); 1246} 1247 1248 void 1249gui_mch_create_scrollbar( 1250 scrollbar_T *sb, 1251 int orient) /* SBAR_VERT or SBAR_HORIZ */ 1252{ 1253 sb->id = CreateWindow( 1254 "SCROLLBAR", "Scrollbar", 1255 WS_CHILD | ((orient == SBAR_VERT) ? SBS_VERT : SBS_HORZ), 0, 0, 1256 10, /* Any value will do for now */ 1257 10, /* Any value will do for now */ 1258 s_hwnd, NULL, 1259 s_hinst, NULL); 1260} 1261 1262/* 1263 * Find the scrollbar with the given hwnd. 1264 */ 1265 static scrollbar_T * 1266gui_mswin_find_scrollbar(HWND hwnd) 1267{ 1268 win_T *wp; 1269 1270 if (gui.bottom_sbar.id == hwnd) 1271 return &gui.bottom_sbar; 1272 FOR_ALL_WINDOWS(wp) 1273 { 1274 if (wp->w_scrollbars[SBAR_LEFT].id == hwnd) 1275 return &wp->w_scrollbars[SBAR_LEFT]; 1276 if (wp->w_scrollbars[SBAR_RIGHT].id == hwnd) 1277 return &wp->w_scrollbars[SBAR_RIGHT]; 1278 } 1279 return NULL; 1280} 1281 1282/* 1283 * Get the character size of a font. 1284 */ 1285 static void 1286GetFontSize(GuiFont font) 1287{ 1288 HWND hwnd = GetDesktopWindow(); 1289 HDC hdc = GetWindowDC(hwnd); 1290 HFONT hfntOld = SelectFont(hdc, (HFONT)font); 1291 TEXTMETRIC tm; 1292 1293 GetTextMetrics(hdc, &tm); 1294 gui.char_width = tm.tmAveCharWidth + tm.tmOverhang; 1295 1296 gui.char_height = tm.tmHeight 1297#ifndef MSWIN16_FASTTEXT 1298 + p_linespace 1299#endif 1300 ; 1301 1302 SelectFont(hdc, hfntOld); 1303 1304 ReleaseDC(hwnd, hdc); 1305} 1306 1307/* 1308 * Adjust gui.char_height (after 'linespace' was changed). 1309 */ 1310 int 1311gui_mch_adjust_charheight(void) 1312{ 1313 GetFontSize(gui.norm_font); 1314 return OK; 1315} 1316 1317 static GuiFont 1318get_font_handle(LOGFONT *lf) 1319{ 1320 HFONT font = NULL; 1321 1322 /* Load the font */ 1323 font = CreateFontIndirect(lf); 1324 1325 if (font == NULL) 1326 return NOFONT; 1327 1328 return (GuiFont)font; 1329} 1330 1331 static int 1332pixels_to_points(int pixels, int vertical) 1333{ 1334 int points; 1335 HWND hwnd; 1336 HDC hdc; 1337 1338 hwnd = GetDesktopWindow(); 1339 hdc = GetWindowDC(hwnd); 1340 1341 points = MulDiv(pixels, 72, 1342 GetDeviceCaps(hdc, vertical ? LOGPIXELSY : LOGPIXELSX)); 1343 1344 ReleaseDC(hwnd, hdc); 1345 1346 return points; 1347} 1348 1349 GuiFont 1350gui_mch_get_font( 1351 char_u *name, 1352 int giveErrorIfMissing) 1353{ 1354 LOGFONT lf; 1355 GuiFont font = NOFONT; 1356 1357 if (get_logfont(&lf, name, NULL, giveErrorIfMissing) == OK) 1358 font = get_font_handle(&lf); 1359 if (font == NOFONT && giveErrorIfMissing) 1360 EMSG2(_(e_font), name); 1361 return font; 1362} 1363 1364#if defined(FEAT_EVAL) || defined(PROTO) 1365/* 1366 * Return the name of font "font" in allocated memory. 1367 * Don't know how to get the actual name, thus use the provided name. 1368 */ 1369/*ARGSUSED*/ 1370 char_u * 1371gui_mch_get_fontname(font, name) 1372 GuiFont font; 1373 char_u *name; 1374{ 1375 if (name == NULL) 1376 return NULL; 1377 return vim_strsave(name); 1378} 1379#endif 1380 1381 void 1382gui_mch_free_font(GuiFont font) 1383{ 1384 if (font) 1385 DeleteObject((HFONT)font); 1386} 1387 1388 static int 1389hex_digit(int c) 1390{ 1391 if (VIM_ISDIGIT(c)) 1392 return c - '0'; 1393 c = TOLOWER_ASC(c); 1394 if (c >= 'a' && c <= 'f') 1395 return c - 'a' + 10; 1396 return -1000; 1397} 1398/* 1399 * Return the Pixel value (color) for the given color name. 1400 * Return INVALCOLOR for error. 1401 */ 1402 guicolor_T 1403gui_mch_get_color(char_u *name) 1404{ 1405 typedef struct guicolor_tTable 1406 { 1407 char *name; 1408 COLORREF color; 1409 } guicolor_tTable; 1410 1411 static guicolor_tTable table[] = 1412 { 1413 {"Black", RGB(0x00, 0x00, 0x00)}, 1414 {"DarkGray", RGB(0x80, 0x80, 0x80)}, 1415 {"DarkGrey", RGB(0x80, 0x80, 0x80)}, 1416 {"Gray", RGB(0xC0, 0xC0, 0xC0)}, 1417 {"Grey", RGB(0xC0, 0xC0, 0xC0)}, 1418 {"LightGray", RGB(0xE0, 0xE0, 0xE0)}, 1419 {"LightGrey", RGB(0xE0, 0xE0, 0xE0)}, 1420 {"Gray10", RGB(0x1A, 0x1A, 0x1A)}, 1421 {"Grey10", RGB(0x1A, 0x1A, 0x1A)}, 1422 {"Gray20", RGB(0x33, 0x33, 0x33)}, 1423 {"Grey20", RGB(0x33, 0x33, 0x33)}, 1424 {"Gray30", RGB(0x4D, 0x4D, 0x4D)}, 1425 {"Grey30", RGB(0x4D, 0x4D, 0x4D)}, 1426 {"Gray40", RGB(0x66, 0x66, 0x66)}, 1427 {"Grey40", RGB(0x66, 0x66, 0x66)}, 1428 {"Gray50", RGB(0x7F, 0x7F, 0x7F)}, 1429 {"Grey50", RGB(0x7F, 0x7F, 0x7F)}, 1430 {"Gray60", RGB(0x99, 0x99, 0x99)}, 1431 {"Grey60", RGB(0x99, 0x99, 0x99)}, 1432 {"Gray70", RGB(0xB3, 0xB3, 0xB3)}, 1433 {"Grey70", RGB(0xB3, 0xB3, 0xB3)}, 1434 {"Gray80", RGB(0xCC, 0xCC, 0xCC)}, 1435 {"Grey80", RGB(0xCC, 0xCC, 0xCC)}, 1436 {"Gray90", RGB(0xE5, 0xE5, 0xE5)}, 1437 {"Grey90", RGB(0xE5, 0xE5, 0xE5)}, 1438 {"White", RGB(0xFF, 0xFF, 0xFF)}, 1439 {"DarkRed", RGB(0x80, 0x00, 0x00)}, 1440 {"Red", RGB(0xFF, 0x00, 0x00)}, 1441 {"LightRed", RGB(0xFF, 0xA0, 0xA0)}, 1442 {"DarkBlue", RGB(0x00, 0x00, 0x80)}, 1443 {"Blue", RGB(0x00, 0x00, 0xFF)}, 1444 {"LightBlue", RGB(0xA0, 0xA0, 0xFF)}, 1445 {"DarkGreen", RGB(0x00, 0x80, 0x00)}, 1446 {"Green", RGB(0x00, 0xFF, 0x00)}, 1447 {"LightGreen", RGB(0xA0, 0xFF, 0xA0)}, 1448 {"DarkCyan", RGB(0x00, 0x80, 0x80)}, 1449 {"Cyan", RGB(0x00, 0xFF, 0xFF)}, 1450 {"LightCyan", RGB(0xA0, 0xFF, 0xFF)}, 1451 {"DarkMagenta", RGB(0x80, 0x00, 0x80)}, 1452 {"Magenta", RGB(0xFF, 0x00, 0xFF)}, 1453 {"LightMagenta", RGB(0xFF, 0xA0, 0xFF)}, 1454 {"Brown", RGB(0x80, 0x40, 0x40)}, 1455 {"Yellow", RGB(0xFF, 0xFF, 0x00)}, 1456 {"LightYellow", RGB(0xFF, 0xFF, 0xA0)}, 1457 {"DarkYellow", RGB(0xBB, 0xBB, 0x00)}, 1458 {"SeaGreen", RGB(0x2E, 0x8B, 0x57)}, 1459 {"Orange", RGB(0xFF, 0xA5, 0x00)}, 1460 {"Purple", RGB(0xA0, 0x20, 0xF0)}, 1461 {"SlateBlue", RGB(0x6A, 0x5A, 0xCD)}, 1462 {"Violet", RGB(0xEE, 0x82, 0xEE)}, 1463 }; 1464 1465 typedef struct SysColorTable 1466 { 1467 char *name; 1468 int color; 1469 } SysColorTable; 1470 1471 static SysColorTable sys_table[] = 1472 { 1473#ifdef WIN3264 1474 {"SYS_3DDKSHADOW", COLOR_3DDKSHADOW}, 1475 {"SYS_3DHILIGHT", COLOR_3DHILIGHT}, 1476#ifndef __MINGW32__ 1477 {"SYS_3DHIGHLIGHT", COLOR_3DHIGHLIGHT}, 1478#endif 1479 {"SYS_BTNHILIGHT", COLOR_BTNHILIGHT}, 1480 {"SYS_BTNHIGHLIGHT", COLOR_BTNHIGHLIGHT}, 1481 {"SYS_3DLIGHT", COLOR_3DLIGHT}, 1482 {"SYS_3DSHADOW", COLOR_3DSHADOW}, 1483 {"SYS_DESKTOP", COLOR_DESKTOP}, 1484 {"SYS_INFOBK", COLOR_INFOBK}, 1485 {"SYS_INFOTEXT", COLOR_INFOTEXT}, 1486 {"SYS_3DFACE", COLOR_3DFACE}, 1487#endif 1488 {"SYS_BTNFACE", COLOR_BTNFACE}, 1489 {"SYS_BTNSHADOW", COLOR_BTNSHADOW}, 1490 {"SYS_ACTIVEBORDER", COLOR_ACTIVEBORDER}, 1491 {"SYS_ACTIVECAPTION", COLOR_ACTIVECAPTION}, 1492 {"SYS_APPWORKSPACE", COLOR_APPWORKSPACE}, 1493 {"SYS_BACKGROUND", COLOR_BACKGROUND}, 1494 {"SYS_BTNTEXT", COLOR_BTNTEXT}, 1495 {"SYS_CAPTIONTEXT", COLOR_CAPTIONTEXT}, 1496 {"SYS_GRAYTEXT", COLOR_GRAYTEXT}, 1497 {"SYS_HIGHLIGHT", COLOR_HIGHLIGHT}, 1498 {"SYS_HIGHLIGHTTEXT", COLOR_HIGHLIGHTTEXT}, 1499 {"SYS_INACTIVEBORDER", COLOR_INACTIVEBORDER}, 1500 {"SYS_INACTIVECAPTION", COLOR_INACTIVECAPTION}, 1501 {"SYS_INACTIVECAPTIONTEXT", COLOR_INACTIVECAPTIONTEXT}, 1502 {"SYS_MENU", COLOR_MENU}, 1503 {"SYS_MENUTEXT", COLOR_MENUTEXT}, 1504 {"SYS_SCROLLBAR", COLOR_SCROLLBAR}, 1505 {"SYS_WINDOW", COLOR_WINDOW}, 1506 {"SYS_WINDOWFRAME", COLOR_WINDOWFRAME}, 1507 {"SYS_WINDOWTEXT", COLOR_WINDOWTEXT} 1508 }; 1509 1510 int r, g, b; 1511 int i; 1512 1513 if (name[0] == '#' && strlen(name) == 7) 1514 { 1515 /* Name is in "#rrggbb" format */ 1516 r = hex_digit(name[1]) * 16 + hex_digit(name[2]); 1517 g = hex_digit(name[3]) * 16 + hex_digit(name[4]); 1518 b = hex_digit(name[5]) * 16 + hex_digit(name[6]); 1519 if (r < 0 || g < 0 || b < 0) 1520 return INVALCOLOR; 1521 return RGB(r, g, b); 1522 } 1523 else 1524 { 1525 /* Check if the name is one of the colors we know */ 1526 for (i = 0; i < sizeof(table) / sizeof(table[0]); i++) 1527 if (STRICMP(name, table[i].name) == 0) 1528 return table[i].color; 1529 } 1530 1531 /* 1532 * Try to look up a system colour. 1533 */ 1534 for (i = 0; i < sizeof(sys_table) / sizeof(sys_table[0]); i++) 1535 if (STRICMP(name, sys_table[i].name) == 0) 1536 return GetSysColor(sys_table[i].color); 1537 1538 /* 1539 * Last attempt. Look in the file "$VIMRUNTIME/rgb.txt". 1540 */ 1541 { 1542#define LINE_LEN 100 1543 FILE *fd; 1544 char line[LINE_LEN]; 1545 char_u *fname; 1546 1547 fname = expand_env_save((char_u *)"$VIMRUNTIME/rgb.txt"); 1548 if (fname == NULL) 1549 return INVALCOLOR; 1550 1551 fd = mch_fopen((char *)fname, "rt"); 1552 vim_free(fname); 1553 if (fd == NULL) 1554 return INVALCOLOR; 1555 1556 while (!feof(fd)) 1557 { 1558 int len; 1559 int pos; 1560 char *color; 1561 1562 fgets(line, LINE_LEN, fd); 1563 len = (int)STRLEN(line); 1564 1565 if (len <= 1 || line[len-1] != '\n') 1566 continue; 1567 1568 line[len-1] = '\0'; 1569 1570 i = sscanf(line, "%d %d %d %n", &r, &g, &b, &pos); 1571 if (i != 3) 1572 continue; 1573 1574 color = line + pos; 1575 1576 if (STRICMP(color, name) == 0) 1577 { 1578 fclose(fd); 1579 return (guicolor_T) RGB(r, g, b); 1580 } 1581 } 1582 1583 fclose(fd); 1584 } 1585 1586 return INVALCOLOR; 1587} 1588/* 1589 * Return OK if the key with the termcap name "name" is supported. 1590 */ 1591 int 1592gui_mch_haskey(char_u *name) 1593{ 1594 int i; 1595 1596 for (i = 0; special_keys[i].vim_code1 != NUL; i++) 1597 if (name[0] == special_keys[i].vim_code0 && 1598 name[1] == special_keys[i].vim_code1) 1599 return OK; 1600 return FAIL; 1601} 1602 1603 void 1604gui_mch_beep(void) 1605{ 1606 MessageBeep(MB_OK); 1607} 1608/* 1609 * Invert a rectangle from row r, column c, for nr rows and nc columns. 1610 */ 1611 void 1612gui_mch_invert_rectangle( 1613 int r, 1614 int c, 1615 int nr, 1616 int nc) 1617{ 1618 RECT rc; 1619 1620 /* 1621 * Note: InvertRect() excludes right and bottom of rectangle. 1622 */ 1623 rc.left = FILL_X(c); 1624 rc.top = FILL_Y(r); 1625 rc.right = rc.left + nc * gui.char_width; 1626 rc.bottom = rc.top + nr * gui.char_height; 1627 InvertRect(s_hdc, &rc); 1628} 1629 1630/* 1631 * Iconify the GUI window. 1632 */ 1633 void 1634gui_mch_iconify(void) 1635{ 1636 ShowWindow(s_hwnd, SW_MINIMIZE); 1637} 1638 1639/* 1640 * Draw a cursor without focus. 1641 */ 1642 void 1643gui_mch_draw_hollow_cursor(guicolor_T color) 1644{ 1645 HBRUSH hbr; 1646 RECT rc; 1647 1648 /* 1649 * Note: FrameRect() excludes right and bottom of rectangle. 1650 */ 1651 rc.left = FILL_X(gui.col); 1652 rc.top = FILL_Y(gui.row); 1653 rc.right = rc.left + gui.char_width; 1654#ifdef FEAT_MBYTE 1655 if (mb_lefthalve(gui.row, gui.col)) 1656 rc.right += gui.char_width; 1657#endif 1658 rc.bottom = rc.top + gui.char_height; 1659 hbr = CreateSolidBrush(color); 1660 FrameRect(s_hdc, &rc, hbr); 1661 DeleteBrush(hbr); 1662} 1663/* 1664 * Draw part of a cursor, "w" pixels wide, and "h" pixels high, using 1665 * color "color". 1666 */ 1667 void 1668gui_mch_draw_part_cursor( 1669 int w, 1670 int h, 1671 guicolor_T color) 1672{ 1673 HBRUSH hbr; 1674 RECT rc; 1675 1676 /* 1677 * Note: FillRect() excludes right and bottom of rectangle. 1678 */ 1679 rc.left = 1680#ifdef FEAT_RIGHTLEFT 1681 /* vertical line should be on the right of current point */ 1682 CURSOR_BAR_RIGHT ? FILL_X(gui.col + 1) - w : 1683#endif 1684 FILL_X(gui.col); 1685 rc.top = FILL_Y(gui.row) + gui.char_height - h; 1686 rc.right = rc.left + w; 1687 rc.bottom = rc.top + h; 1688 hbr = CreateSolidBrush(color); 1689 FillRect(s_hdc, &rc, hbr); 1690 DeleteBrush(hbr); 1691} 1692 1693/* 1694 * Process a single Windows message. 1695 * If one is not available we hang until one is. 1696 */ 1697 static void 1698process_message(void) 1699{ 1700 MSG msg; 1701 UINT vk = 0; /* Virtual key */ 1702 char_u string[40]; 1703 int i; 1704 int modifiers = 0; 1705 int key; 1706#ifdef FEAT_MENU 1707 static char_u k10[] = {K_SPECIAL, 'k', ';', 0}; 1708#endif 1709 1710 GetMessage(&msg, NULL, 0, 0); 1711 1712#ifdef FEAT_OLE 1713 /* Look after OLE Automation commands */ 1714 if (msg.message == WM_OLE) 1715 { 1716 char_u *str = (char_u *)msg.lParam; 1717 if (str == NULL || *str == NUL) 1718 { 1719 /* Message can't be ours, forward it. Fixes problem with Ultramon 1720 * 3.0.4 */ 1721 DispatchMessage(&msg); 1722 } 1723 else 1724 { 1725 add_to_input_buf(str, (int)STRLEN(str)); 1726 vim_free(str); /* was allocated in CVim::SendKeys() */ 1727 } 1728 return; 1729 } 1730#endif 1731 1732#ifdef FEAT_NETBEANS_INTG 1733 if (msg.message == WM_NETBEANS) 1734 { 1735 netbeans_read(); 1736 return; 1737 } 1738#endif 1739 1740#ifdef FEAT_SNIFF 1741 if (sniff_request_waiting && want_sniff_request) 1742 { 1743 static char_u bytes[3] = {CSI, (char_u)KS_EXTRA, (char_u)KE_SNIFF}; 1744 add_to_input_buf(bytes, 3); /* K_SNIFF */ 1745 sniff_request_waiting = 0; 1746 want_sniff_request = 0; 1747 /* request is handled in normal.c */ 1748 } 1749 if (msg.message == WM_USER) 1750 { 1751 MyTranslateMessage(&msg); 1752 DispatchMessage(&msg); 1753 return; 1754 } 1755#endif 1756 1757#ifdef MSWIN_FIND_REPLACE 1758 /* Don't process messages used by the dialog */ 1759 if (s_findrep_hwnd != NULL && IsDialogMessage(s_findrep_hwnd, &msg)) 1760 { 1761 HandleMouseHide(msg.message, msg.lParam); 1762 return; 1763 } 1764#endif 1765 1766 /* 1767 * Check if it's a special key that we recognise. If not, call 1768 * TranslateMessage(). 1769 */ 1770 if (msg.message == WM_KEYDOWN || msg.message == WM_SYSKEYDOWN) 1771 { 1772 vk = (int) msg.wParam; 1773 /* handle key after dead key, but ignore shift, alt and control */ 1774 if (dead_key && vk != VK_SHIFT && vk != VK_MENU && vk != VK_CONTROL) 1775 { 1776 dead_key = 0; 1777 /* handle non-alphabetic keys (ones that hopefully cannot generate 1778 * umlaut-characters), unless when control is down */ 1779 if (vk < 'A' || vk > 'Z' || (GetKeyState(VK_CONTROL) & 0x8000)) 1780 { 1781 MSG dm; 1782 1783 dm.message = msg.message; 1784 dm.hwnd = msg.hwnd; 1785 dm.wParam = VK_SPACE; 1786 MyTranslateMessage(&dm); /* generate dead character */ 1787 if (vk != VK_SPACE) /* and send current character once more */ 1788 PostMessage(msg.hwnd, msg.message, msg.wParam, msg.lParam); 1789 return; 1790 } 1791 } 1792 1793 /* Check for CTRL-BREAK */ 1794 if (vk == VK_CANCEL) 1795 { 1796 trash_input_buf(); 1797 got_int = TRUE; 1798 string[0] = Ctrl_C; 1799 add_to_input_buf(string, 1); 1800 } 1801 1802 for (i = 0; special_keys[i].key_sym != 0; i++) 1803 { 1804 /* ignore VK_SPACE when ALT key pressed: system menu */ 1805 if (special_keys[i].key_sym == vk 1806 && (vk != VK_SPACE || !(GetKeyState(VK_MENU) & 0x8000))) 1807 { 1808#ifdef FEAT_MENU 1809 /* Check for <F10>: Windows selects the menu. When <F10> is 1810 * mapped we want to use the mapping instead. */ 1811 if (vk == VK_F10 1812 && gui.menu_is_active 1813 && check_map(k10, State, FALSE, TRUE, FALSE) == NULL) 1814 break; 1815#endif 1816 if (GetKeyState(VK_SHIFT) & 0x8000) 1817 modifiers |= MOD_MASK_SHIFT; 1818 /* 1819 * Don't use caps-lock as shift, because these are special keys 1820 * being considered here, and we only want letters to get 1821 * shifted -- webb 1822 */ 1823 /* 1824 if (GetKeyState(VK_CAPITAL) & 0x0001) 1825 modifiers ^= MOD_MASK_SHIFT; 1826 */ 1827 if (GetKeyState(VK_CONTROL) & 0x8000) 1828 modifiers |= MOD_MASK_CTRL; 1829 if (GetKeyState(VK_MENU) & 0x8000) 1830 modifiers |= MOD_MASK_ALT; 1831 1832 if (special_keys[i].vim_code1 == NUL) 1833 key = special_keys[i].vim_code0; 1834 else 1835 key = TO_SPECIAL(special_keys[i].vim_code0, 1836 special_keys[i].vim_code1); 1837 key = simplify_key(key, &modifiers); 1838 if (key == CSI) 1839 key = K_CSI; 1840 1841 if (modifiers) 1842 { 1843 string[0] = CSI; 1844 string[1] = KS_MODIFIER; 1845 string[2] = modifiers; 1846 add_to_input_buf(string, 3); 1847 } 1848 1849 if (IS_SPECIAL(key)) 1850 { 1851 string[0] = CSI; 1852 string[1] = K_SECOND(key); 1853 string[2] = K_THIRD(key); 1854 add_to_input_buf(string, 3); 1855 } 1856 else 1857 { 1858 int len; 1859 1860 /* Handle "key" as a Unicode character. */ 1861 len = char_to_string(key, string, 40, FALSE); 1862 add_to_input_buf(string, len); 1863 } 1864 break; 1865 } 1866 } 1867 if (special_keys[i].key_sym == 0) 1868 { 1869 /* Some keys need C-S- where they should only need C-. 1870 * Ignore 0xff, Windows XP sends it when NUMLOCK has changed since 1871 * system startup (Helmut Stiegler, 2003 Oct 3). */ 1872 if (vk != 0xff 1873 && (GetKeyState(VK_CONTROL) & 0x8000) 1874 && !(GetKeyState(VK_SHIFT) & 0x8000) 1875 && !(GetKeyState(VK_MENU) & 0x8000)) 1876 { 1877 /* CTRL-6 is '^'; Japanese keyboard maps '^' to vk == 0xDE */ 1878 if (vk == '6' || MapVirtualKey(vk, 2) == (UINT)'^') 1879 { 1880 string[0] = Ctrl_HAT; 1881 add_to_input_buf(string, 1); 1882 } 1883 /* vk == 0xBD AZERTY for CTRL-'-', but CTRL-[ for * QWERTY! */ 1884 else if (vk == 0xBD) /* QWERTY for CTRL-'-' */ 1885 { 1886 string[0] = Ctrl__; 1887 add_to_input_buf(string, 1); 1888 } 1889 /* CTRL-2 is '@'; Japanese keyboard maps '@' to vk == 0xC0 */ 1890 else if (vk == '2' || MapVirtualKey(vk, 2) == (UINT)'@') 1891 { 1892 string[0] = Ctrl_AT; 1893 add_to_input_buf(string, 1); 1894 } 1895 else 1896 MyTranslateMessage(&msg); 1897 } 1898 else 1899 MyTranslateMessage(&msg); 1900 } 1901 } 1902#ifdef FEAT_MBYTE_IME 1903 else if (msg.message == WM_IME_NOTIFY) 1904 _OnImeNotify(msg.hwnd, (DWORD)msg.wParam, (DWORD)msg.lParam); 1905 else if (msg.message == WM_KEYUP && im_get_status()) 1906 /* added for non-MS IME (Yasuhiro Matsumoto) */ 1907 MyTranslateMessage(&msg); 1908#endif 1909#if !defined(FEAT_MBYTE_IME) && defined(GLOBAL_IME) 1910/* GIME_TEST */ 1911 else if (msg.message == WM_IME_STARTCOMPOSITION) 1912 { 1913 POINT point; 1914 1915 global_ime_set_font(&norm_logfont); 1916 point.x = FILL_X(gui.col); 1917 point.y = FILL_Y(gui.row); 1918 MapWindowPoints(s_textArea, s_hwnd, &point, 1); 1919 global_ime_set_position(&point); 1920 } 1921#endif 1922 1923#ifdef FEAT_MENU 1924 /* Check for <F10>: Default effect is to select the menu. When <F10> is 1925 * mapped we need to stop it here to avoid strange effects (e.g., for the 1926 * key-up event) */ 1927 if (vk != VK_F10 || check_map(k10, State, FALSE, TRUE, FALSE) == NULL) 1928#endif 1929 DispatchMessage(&msg); 1930} 1931 1932/* 1933 * Catch up with any queued events. This may put keyboard input into the 1934 * input buffer, call resize call-backs, trigger timers etc. If there is 1935 * nothing in the event queue (& no timers pending), then we return 1936 * immediately. 1937 */ 1938 void 1939gui_mch_update(void) 1940{ 1941 MSG msg; 1942 1943 if (!s_busy_processing) 1944 while (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE) 1945 && !vim_is_input_buf_full()) 1946 process_message(); 1947} 1948 1949/* 1950 * GUI input routine called by gui_wait_for_chars(). Waits for a character 1951 * from the keyboard. 1952 * wtime == -1 Wait forever. 1953 * wtime == 0 This should never happen. 1954 * wtime > 0 Wait wtime milliseconds for a character. 1955 * Returns OK if a character was found to be available within the given time, 1956 * or FAIL otherwise. 1957 */ 1958 int 1959gui_mch_wait_for_chars(int wtime) 1960{ 1961 MSG msg; 1962 int focus; 1963 1964 s_timed_out = FALSE; 1965 1966 if (wtime > 0) 1967 { 1968 /* Don't do anything while processing a (scroll) message. */ 1969 if (s_busy_processing) 1970 return FAIL; 1971 s_wait_timer = (UINT)SetTimer(NULL, 0, (UINT)wtime, 1972 (TIMERPROC)_OnTimer); 1973 } 1974 1975 allow_scrollbar = TRUE; 1976 1977 focus = gui.in_focus; 1978 while (!s_timed_out) 1979 { 1980 /* Stop or start blinking when focus changes */ 1981 if (gui.in_focus != focus) 1982 { 1983 if (gui.in_focus) 1984 gui_mch_start_blink(); 1985 else 1986 gui_mch_stop_blink(); 1987 focus = gui.in_focus; 1988 } 1989 1990 if (s_need_activate) 1991 { 1992#ifdef WIN32 1993 (void)SetForegroundWindow(s_hwnd); 1994#else 1995 (void)SetActiveWindow(s_hwnd); 1996#endif 1997 s_need_activate = FALSE; 1998 } 1999 2000#ifdef FEAT_NETBEANS_INTG 2001 /* Process the queued netbeans messages. */ 2002 netbeans_parse_messages(); 2003#endif 2004 2005 /* 2006 * Don't use gui_mch_update() because then we will spin-lock until a 2007 * char arrives, instead we use GetMessage() to hang until an 2008 * event arrives. No need to check for input_buf_full because we are 2009 * returning as soon as it contains a single char -- webb 2010 */ 2011 process_message(); 2012 2013 if (input_available()) 2014 { 2015 if (s_wait_timer != 0 && !s_timed_out) 2016 { 2017 KillTimer(NULL, s_wait_timer); 2018 2019 /* Eat spurious WM_TIMER messages */ 2020 while (PeekMessage(&msg, s_hwnd, WM_TIMER, WM_TIMER, PM_REMOVE)) 2021 ; 2022 s_wait_timer = 0; 2023 } 2024 allow_scrollbar = FALSE; 2025 2026 /* Clear pending mouse button, the release event may have been 2027 * taken by the dialog window. But don't do this when getting 2028 * focus, we need the mouse-up event then. */ 2029 if (!s_getting_focus) 2030 s_button_pending = -1; 2031 2032 return OK; 2033 } 2034 } 2035 allow_scrollbar = FALSE; 2036 return FAIL; 2037} 2038 2039/* 2040 * Clear a rectangular region of the screen from text pos (row1, col1) to 2041 * (row2, col2) inclusive. 2042 */ 2043 void 2044gui_mch_clear_block( 2045 int row1, 2046 int col1, 2047 int row2, 2048 int col2) 2049{ 2050 RECT rc; 2051 2052 /* 2053 * Clear one extra pixel at the far right, for when bold characters have 2054 * spilled over to the window border. 2055 * Note: FillRect() excludes right and bottom of rectangle. 2056 */ 2057 rc.left = FILL_X(col1); 2058 rc.top = FILL_Y(row1); 2059 rc.right = FILL_X(col2 + 1) + (col2 == Columns - 1); 2060 rc.bottom = FILL_Y(row2 + 1); 2061 clear_rect(&rc); 2062} 2063 2064/* 2065 * Clear the whole text window. 2066 */ 2067 void 2068gui_mch_clear_all(void) 2069{ 2070 RECT rc; 2071 2072 rc.left = 0; 2073 rc.top = 0; 2074 rc.right = Columns * gui.char_width + 2 * gui.border_width; 2075 rc.bottom = Rows * gui.char_height + 2 * gui.border_width; 2076 clear_rect(&rc); 2077} 2078/* 2079 * Menu stuff. 2080 */ 2081 2082 void 2083gui_mch_enable_menu(int flag) 2084{ 2085#ifdef FEAT_MENU 2086 SetMenu(s_hwnd, flag ? s_menuBar : NULL); 2087#endif 2088} 2089 2090/*ARGSUSED*/ 2091 void 2092gui_mch_set_menu_pos( 2093 int x, 2094 int y, 2095 int w, 2096 int h) 2097{ 2098 /* It will be in the right place anyway */ 2099} 2100 2101#if defined(FEAT_MENU) || defined(PROTO) 2102/* 2103 * Make menu item hidden or not hidden 2104 */ 2105 void 2106gui_mch_menu_hidden( 2107 vimmenu_T *menu, 2108 int hidden) 2109{ 2110 /* 2111 * This doesn't do what we want. Hmm, just grey the menu items for now. 2112 */ 2113 /* 2114 if (hidden) 2115 EnableMenuItem(s_menuBar, menu->id, MF_BYCOMMAND | MF_DISABLED); 2116 else 2117 EnableMenuItem(s_menuBar, menu->id, MF_BYCOMMAND | MF_ENABLED); 2118 */ 2119 gui_mch_menu_grey(menu, hidden); 2120} 2121 2122/* 2123 * This is called after setting all the menus to grey/hidden or not. 2124 */ 2125 void 2126gui_mch_draw_menubar(void) 2127{ 2128 DrawMenuBar(s_hwnd); 2129} 2130#endif /*FEAT_MENU*/ 2131 2132#ifndef PROTO 2133void 2134#ifdef VIMDLL 2135_export 2136#endif 2137_cdecl 2138SaveInst(HINSTANCE hInst) 2139{ 2140 s_hinst = hInst; 2141} 2142#endif 2143 2144/* 2145 * Return the RGB value of a pixel as a long. 2146 */ 2147 long_u 2148gui_mch_get_rgb(guicolor_T pixel) 2149{ 2150 return (GetRValue(pixel) << 16) + (GetGValue(pixel) << 8) 2151 + GetBValue(pixel); 2152} 2153 2154#if defined(FEAT_GUI_DIALOG) || defined(PROTO) 2155/* Convert pixels in X to dialog units */ 2156 static WORD 2157PixelToDialogX(int numPixels) 2158{ 2159 return (WORD)((numPixels * 4) / s_dlgfntwidth); 2160} 2161 2162/* Convert pixels in Y to dialog units */ 2163 static WORD 2164PixelToDialogY(int numPixels) 2165{ 2166 return (WORD)((numPixels * 8) / s_dlgfntheight); 2167} 2168 2169/* Return the width in pixels of the given text in the given DC. */ 2170 static int 2171GetTextWidth(HDC hdc, char_u *str, int len) 2172{ 2173 SIZE size; 2174 2175 GetTextExtentPoint(hdc, str, len, &size); 2176 return size.cx; 2177} 2178 2179#ifdef FEAT_MBYTE 2180/* 2181 * Return the width in pixels of the given text in the given DC, taking care 2182 * of 'encoding' to active codepage conversion. 2183 */ 2184 static int 2185GetTextWidthEnc(HDC hdc, char_u *str, int len) 2186{ 2187 SIZE size; 2188 WCHAR *wstr; 2189 int n; 2190 int wlen = len; 2191 2192 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage) 2193 { 2194 /* 'encoding' differs from active codepage: convert text and use wide 2195 * function */ 2196 wstr = enc_to_utf16(str, &wlen); 2197 if (wstr != NULL) 2198 { 2199 n = GetTextExtentPointW(hdc, wstr, wlen, &size); 2200 vim_free(wstr); 2201 if (n) 2202 return size.cx; 2203 } 2204 } 2205 2206 return GetTextWidth(hdc, str, len); 2207} 2208#else 2209# define GetTextWidthEnc(h, s, l) GetTextWidth((h), (s), (l)) 2210#endif 2211 2212/* 2213 * A quick little routine that will center one window over another, handy for 2214 * dialog boxes. Taken from the Win32SDK samples. 2215 */ 2216 static BOOL 2217CenterWindow( 2218 HWND hwndChild, 2219 HWND hwndParent) 2220{ 2221 RECT rChild, rParent; 2222 int wChild, hChild, wParent, hParent; 2223 int wScreen, hScreen, xNew, yNew; 2224 HDC hdc; 2225 2226 GetWindowRect(hwndChild, &rChild); 2227 wChild = rChild.right - rChild.left; 2228 hChild = rChild.bottom - rChild.top; 2229 2230 /* If Vim is minimized put the window in the middle of the screen. */ 2231 if (hwndParent == NULL || IsMinimized(hwndParent)) 2232 { 2233#ifdef WIN16 2234 rParent.left = 0; 2235 rParent.top = 0; 2236 rParent.right = GetSystemMetrics(SM_CXSCREEN); 2237 rParent.bottom = GetSystemMetrics(SM_CYFULLSCREEN); 2238#else 2239 SystemParametersInfo(SPI_GETWORKAREA, 0, &rParent, 0); 2240#endif 2241 } 2242 else 2243 GetWindowRect(hwndParent, &rParent); 2244 wParent = rParent.right - rParent.left; 2245 hParent = rParent.bottom - rParent.top; 2246 2247 hdc = GetDC(hwndChild); 2248 wScreen = GetDeviceCaps (hdc, HORZRES); 2249 hScreen = GetDeviceCaps (hdc, VERTRES); 2250 ReleaseDC(hwndChild, hdc); 2251 2252 xNew = rParent.left + ((wParent - wChild) /2); 2253 if (xNew < 0) 2254 { 2255 xNew = 0; 2256 } 2257 else if ((xNew+wChild) > wScreen) 2258 { 2259 xNew = wScreen - wChild; 2260 } 2261 2262 yNew = rParent.top + ((hParent - hChild) /2); 2263 if (yNew < 0) 2264 yNew = 0; 2265 else if ((yNew+hChild) > hScreen) 2266 yNew = hScreen - hChild; 2267 2268 return SetWindowPos(hwndChild, NULL, xNew, yNew, 0, 0, 2269 SWP_NOSIZE | SWP_NOZORDER); 2270} 2271#endif /* FEAT_GUI_DIALOG */ 2272 2273void 2274gui_mch_activate_window(void) 2275{ 2276 (void)SetActiveWindow(s_hwnd); 2277} 2278 2279#if defined(FEAT_TOOLBAR) || defined(PROTO) 2280 void 2281gui_mch_show_toolbar(int showit) 2282{ 2283 if (s_toolbarhwnd == NULL) 2284 return; 2285 2286 if (showit) 2287 { 2288# ifdef FEAT_MBYTE 2289# ifndef TB_SETUNICODEFORMAT 2290 /* For older compilers. We assume this never changes. */ 2291# define TB_SETUNICODEFORMAT 0x2005 2292# endif 2293 /* Enable/disable unicode support */ 2294 int uu = (enc_codepage >= 0 && (int)GetACP() != enc_codepage); 2295 SendMessage(s_toolbarhwnd, TB_SETUNICODEFORMAT, (WPARAM)uu, (LPARAM)0); 2296# endif 2297 ShowWindow(s_toolbarhwnd, SW_SHOW); 2298 } 2299 else 2300 ShowWindow(s_toolbarhwnd, SW_HIDE); 2301} 2302 2303/* Then number of bitmaps is fixed. Exit is missing! */ 2304#define TOOLBAR_BITMAP_COUNT 31 2305 2306#endif 2307 2308#if defined(FEAT_GUI_TABLINE) || defined(PROTO) 2309 static void 2310add_tabline_popup_menu_entry(HMENU pmenu, UINT item_id, char_u *item_text) 2311{ 2312#ifdef FEAT_MBYTE 2313 WCHAR *wn = NULL; 2314 int n; 2315 2316 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage) 2317 { 2318 /* 'encoding' differs from active codepage: convert menu name 2319 * and use wide function */ 2320 wn = enc_to_utf16(item_text, NULL); 2321 if (wn != NULL) 2322 { 2323 MENUITEMINFOW infow; 2324 2325 infow.cbSize = sizeof(infow); 2326 infow.fMask = MIIM_TYPE | MIIM_ID; 2327 infow.wID = item_id; 2328 infow.fType = MFT_STRING; 2329 infow.dwTypeData = wn; 2330 infow.cch = (UINT)wcslen(wn); 2331 n = InsertMenuItemW(pmenu, item_id, FALSE, &infow); 2332 vim_free(wn); 2333 if (n == 0 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) 2334 /* Failed, try using non-wide function. */ 2335 wn = NULL; 2336 } 2337 } 2338 2339 if (wn == NULL) 2340#endif 2341 { 2342 MENUITEMINFO info; 2343 2344 info.cbSize = sizeof(info); 2345 info.fMask = MIIM_TYPE | MIIM_ID; 2346 info.wID = item_id; 2347 info.fType = MFT_STRING; 2348 info.dwTypeData = item_text; 2349 info.cch = (UINT)STRLEN(item_text); 2350 InsertMenuItem(pmenu, item_id, FALSE, &info); 2351 } 2352} 2353 2354 static void 2355show_tabline_popup_menu(void) 2356{ 2357 HMENU tab_pmenu; 2358 long rval; 2359 POINT pt; 2360 2361 /* When ignoring events don't show the menu. */ 2362 if (hold_gui_events 2363# ifdef FEAT_CMDWIN 2364 || cmdwin_type != 0 2365# endif 2366 ) 2367 return; 2368 2369 tab_pmenu = CreatePopupMenu(); 2370 if (tab_pmenu == NULL) 2371 return; 2372 2373 add_tabline_popup_menu_entry(tab_pmenu, TABLINE_MENU_CLOSE, _("Close tab")); 2374 add_tabline_popup_menu_entry(tab_pmenu, TABLINE_MENU_NEW, _("New tab")); 2375 add_tabline_popup_menu_entry(tab_pmenu, TABLINE_MENU_OPEN, 2376 _("Open tab...")); 2377 2378 GetCursorPos(&pt); 2379 rval = TrackPopupMenuEx(tab_pmenu, TPM_RETURNCMD, pt.x, pt.y, s_tabhwnd, 2380 NULL); 2381 2382 DestroyMenu(tab_pmenu); 2383 2384 /* Add the string cmd into input buffer */ 2385 if (rval > 0) 2386 { 2387 TCHITTESTINFO htinfo; 2388 int idx; 2389 2390 if (ScreenToClient(s_tabhwnd, &pt) == 0) 2391 return; 2392 2393 htinfo.pt.x = pt.x; 2394 htinfo.pt.y = pt.y; 2395 idx = TabCtrl_HitTest(s_tabhwnd, &htinfo); 2396 if (idx == -1) 2397 idx = 0; 2398 else 2399 idx += 1; 2400 2401 send_tabline_menu_event(idx, (int)rval); 2402 } 2403} 2404 2405/* 2406 * Show or hide the tabline. 2407 */ 2408 void 2409gui_mch_show_tabline(int showit) 2410{ 2411 if (s_tabhwnd == NULL) 2412 return; 2413 2414 if (!showit != !showing_tabline) 2415 { 2416 if (showit) 2417 ShowWindow(s_tabhwnd, SW_SHOW); 2418 else 2419 ShowWindow(s_tabhwnd, SW_HIDE); 2420 showing_tabline = showit; 2421 } 2422} 2423 2424/* 2425 * Return TRUE when tabline is displayed. 2426 */ 2427 int 2428gui_mch_showing_tabline(void) 2429{ 2430 return s_tabhwnd != NULL && showing_tabline; 2431} 2432 2433/* 2434 * Update the labels of the tabline. 2435 */ 2436 void 2437gui_mch_update_tabline(void) 2438{ 2439 tabpage_T *tp; 2440 TCITEM tie; 2441 int nr = 0; 2442 int curtabidx = 0; 2443 RECT rc; 2444#ifdef FEAT_MBYTE 2445 static int use_unicode = FALSE; 2446 int uu; 2447 WCHAR *wstr = NULL; 2448#endif 2449 2450 if (s_tabhwnd == NULL) 2451 return; 2452 2453#if defined(FEAT_MBYTE) 2454# ifndef CCM_SETUNICODEFORMAT 2455 /* For older compilers. We assume this never changes. */ 2456# define CCM_SETUNICODEFORMAT 0x2005 2457# endif 2458 uu = (enc_codepage >= 0 && (int)GetACP() != enc_codepage); 2459 if (uu != use_unicode) 2460 { 2461 /* Enable/disable unicode support */ 2462 SendMessage(s_tabhwnd, CCM_SETUNICODEFORMAT, (WPARAM)uu, (LPARAM)0); 2463 use_unicode = uu; 2464 } 2465#endif 2466 2467 tie.mask = TCIF_TEXT; 2468 tie.iImage = -1; 2469 2470 /* Add a label for each tab page. They all contain the same text area. */ 2471 for (tp = first_tabpage; tp != NULL; tp = tp->tp_next, ++nr) 2472 { 2473 if (tp == curtab) 2474 curtabidx = nr; 2475 2476 if (!TabCtrl_GetItemRect(s_tabhwnd, nr, &rc)) 2477 { 2478 /* Add the tab */ 2479 tie.pszText = "-Empty-"; 2480 TabCtrl_InsertItem(s_tabhwnd, nr, &tie); 2481 } 2482 2483 get_tabline_label(tp, FALSE); 2484 tie.pszText = NameBuff; 2485#ifdef FEAT_MBYTE 2486 wstr = NULL; 2487 if (use_unicode) 2488 { 2489 /* Need to go through Unicode. */ 2490 wstr = enc_to_utf16(NameBuff, NULL); 2491 if (wstr != NULL) 2492 { 2493 TCITEMW tiw; 2494 2495 tiw.mask = TCIF_TEXT; 2496 tiw.iImage = -1; 2497 tiw.pszText = wstr; 2498 SendMessage(s_tabhwnd, TCM_SETITEMW, (WPARAM)nr, (LPARAM)&tiw); 2499 vim_free(wstr); 2500 } 2501 } 2502 if (wstr == NULL) 2503#endif 2504 { 2505 TabCtrl_SetItem(s_tabhwnd, nr, &tie); 2506 } 2507 } 2508 2509 /* Remove any old labels. */ 2510 while (TabCtrl_GetItemRect(s_tabhwnd, nr, &rc)) 2511 TabCtrl_DeleteItem(s_tabhwnd, nr); 2512 2513 if (TabCtrl_GetCurSel(s_tabhwnd) != curtabidx) 2514 TabCtrl_SetCurSel(s_tabhwnd, curtabidx); 2515} 2516 2517/* 2518 * Set the current tab to "nr". First tab is 1. 2519 */ 2520 void 2521gui_mch_set_curtab(nr) 2522 int nr; 2523{ 2524 if (s_tabhwnd == NULL) 2525 return; 2526 2527 if (TabCtrl_GetCurSel(s_tabhwnd) != nr -1) 2528 TabCtrl_SetCurSel(s_tabhwnd, nr -1); 2529} 2530 2531#endif 2532 2533/* 2534 * ":simalt" command. 2535 */ 2536 void 2537ex_simalt(exarg_T *eap) 2538{ 2539 char_u *keys = eap->arg; 2540 2541 PostMessage(s_hwnd, WM_SYSCOMMAND, (WPARAM)SC_KEYMENU, (LPARAM)0); 2542 while (*keys) 2543 { 2544 if (*keys == '~') 2545 *keys = ' '; /* for showing system menu */ 2546 PostMessage(s_hwnd, WM_CHAR, (WPARAM)*keys, (LPARAM)0); 2547 keys++; 2548 } 2549} 2550 2551/* 2552 * Create the find & replace dialogs. 2553 * You can't have both at once: ":find" when replace is showing, destroys 2554 * the replace dialog first, and the other way around. 2555 */ 2556#ifdef MSWIN_FIND_REPLACE 2557 static void 2558initialise_findrep(char_u *initial_string) 2559{ 2560 int wword = FALSE; 2561 int mcase = !p_ic; 2562 char_u *entry_text; 2563 2564 /* Get the search string to use. */ 2565 entry_text = get_find_dialog_text(initial_string, &wword, &mcase); 2566 2567 s_findrep_struct.hwndOwner = s_hwnd; 2568 s_findrep_struct.Flags = FR_DOWN; 2569 if (mcase) 2570 s_findrep_struct.Flags |= FR_MATCHCASE; 2571 if (wword) 2572 s_findrep_struct.Flags |= FR_WHOLEWORD; 2573 if (entry_text != NULL && *entry_text != NUL) 2574 vim_strncpy(s_findrep_struct.lpstrFindWhat, entry_text, 2575 s_findrep_struct.wFindWhatLen - 1); 2576 vim_free(entry_text); 2577} 2578#endif 2579 2580 static void 2581set_window_title(HWND hwnd, char *title) 2582{ 2583#ifdef FEAT_MBYTE 2584 if (title != NULL && enc_codepage >= 0 && enc_codepage != (int)GetACP()) 2585 { 2586 WCHAR *wbuf; 2587 int n; 2588 2589 /* Convert the title from 'encoding' to UTF-16. */ 2590 wbuf = (WCHAR *)enc_to_utf16((char_u *)title, NULL); 2591 if (wbuf != NULL) 2592 { 2593 n = SetWindowTextW(hwnd, wbuf); 2594 vim_free(wbuf); 2595 if (n != 0 || GetLastError() != ERROR_CALL_NOT_IMPLEMENTED) 2596 return; 2597 /* Retry with non-wide function (for Windows 98). */ 2598 } 2599 } 2600#endif 2601 (void)SetWindowText(hwnd, (LPCSTR)title); 2602} 2603 2604 void 2605gui_mch_find_dialog(exarg_T *eap) 2606{ 2607#ifdef MSWIN_FIND_REPLACE 2608 if (s_findrep_msg != 0) 2609 { 2610 if (IsWindow(s_findrep_hwnd) && !s_findrep_is_find) 2611 DestroyWindow(s_findrep_hwnd); 2612 2613 if (!IsWindow(s_findrep_hwnd)) 2614 { 2615 initialise_findrep(eap->arg); 2616# if defined(FEAT_MBYTE) && defined(WIN3264) 2617 /* If the OS is Windows NT, and 'encoding' differs from active 2618 * codepage: convert text and use wide function. */ 2619 if (os_version.dwPlatformId == VER_PLATFORM_WIN32_NT 2620 && enc_codepage >= 0 && (int)GetACP() != enc_codepage) 2621 { 2622 findrep_atow(&s_findrep_struct_w, &s_findrep_struct); 2623 s_findrep_hwnd = FindTextW( 2624 (LPFINDREPLACEW) &s_findrep_struct_w); 2625 } 2626 else 2627# endif 2628 s_findrep_hwnd = FindText((LPFINDREPLACE) &s_findrep_struct); 2629 } 2630 2631 set_window_title(s_findrep_hwnd, 2632 _("Find string (use '\\\\' to find a '\\')")); 2633 (void)SetFocus(s_findrep_hwnd); 2634 2635 s_findrep_is_find = TRUE; 2636 } 2637#endif 2638} 2639 2640 2641 void 2642gui_mch_replace_dialog(exarg_T *eap) 2643{ 2644#ifdef MSWIN_FIND_REPLACE 2645 if (s_findrep_msg != 0) 2646 { 2647 if (IsWindow(s_findrep_hwnd) && s_findrep_is_find) 2648 DestroyWindow(s_findrep_hwnd); 2649 2650 if (!IsWindow(s_findrep_hwnd)) 2651 { 2652 initialise_findrep(eap->arg); 2653# if defined(FEAT_MBYTE) && defined(WIN3264) 2654 if (os_version.dwPlatformId == VER_PLATFORM_WIN32_NT 2655 && enc_codepage >= 0 && (int)GetACP() != enc_codepage) 2656 { 2657 findrep_atow(&s_findrep_struct_w, &s_findrep_struct); 2658 s_findrep_hwnd = ReplaceTextW( 2659 (LPFINDREPLACEW) &s_findrep_struct_w); 2660 } 2661 else 2662# endif 2663 s_findrep_hwnd = ReplaceText( 2664 (LPFINDREPLACE) &s_findrep_struct); 2665 } 2666 2667 set_window_title(s_findrep_hwnd, 2668 _("Find & Replace (use '\\\\' to find a '\\')")); 2669 (void)SetFocus(s_findrep_hwnd); 2670 2671 s_findrep_is_find = FALSE; 2672 } 2673#endif 2674} 2675 2676 2677/* 2678 * Set visibility of the pointer. 2679 */ 2680 void 2681gui_mch_mousehide(int hide) 2682{ 2683 if (hide != gui.pointer_hidden) 2684 { 2685 ShowCursor(!hide); 2686 gui.pointer_hidden = hide; 2687 } 2688} 2689 2690#ifdef FEAT_MENU 2691 static void 2692gui_mch_show_popupmenu_at(vimmenu_T *menu, int x, int y) 2693{ 2694 /* Unhide the mouse, we don't get move events here. */ 2695 gui_mch_mousehide(FALSE); 2696 2697 (void)TrackPopupMenu( 2698 (HMENU)menu->submenu_id, 2699 TPM_LEFTALIGN | TPM_LEFTBUTTON, 2700 x, y, 2701 (int)0, /*reserved param*/ 2702 s_hwnd, 2703 NULL); 2704 /* 2705 * NOTE: The pop-up menu can eat the mouse up event. 2706 * We deal with this in normal.c. 2707 */ 2708} 2709#endif 2710 2711/* 2712 * Got a message when the system will go down. 2713 */ 2714 static void 2715_OnEndSession(void) 2716{ 2717 getout_preserve_modified(1); 2718} 2719 2720/* 2721 * Get this message when the user clicks on the cross in the top right corner 2722 * of a Windows95 window. 2723 */ 2724/*ARGSUSED*/ 2725 static void 2726_OnClose( 2727 HWND hwnd) 2728{ 2729 gui_shell_closed(); 2730} 2731 2732/* 2733 * Get a message when the window is being destroyed. 2734 */ 2735 static void 2736_OnDestroy( 2737 HWND hwnd) 2738{ 2739#ifdef WIN16_3DLOOK 2740 Ctl3dUnregister(s_hinst); 2741#endif 2742 if (!destroying) 2743 _OnClose(hwnd); 2744} 2745 2746 static void 2747_OnPaint( 2748 HWND hwnd) 2749{ 2750 if (!IsMinimized(hwnd)) 2751 { 2752 PAINTSTRUCT ps; 2753 2754 out_flush(); /* make sure all output has been processed */ 2755 (void)BeginPaint(hwnd, &ps); 2756 2757#ifdef FEAT_MBYTE 2758 /* prevent multi-byte characters from misprinting on an invalid 2759 * rectangle */ 2760 if (has_mbyte) 2761 { 2762 RECT rect; 2763 2764 GetClientRect(hwnd, &rect); 2765 ps.rcPaint.left = rect.left; 2766 ps.rcPaint.right = rect.right; 2767 } 2768#endif 2769 2770 if (!IsRectEmpty(&ps.rcPaint)) 2771 gui_redraw(ps.rcPaint.left, ps.rcPaint.top, 2772 ps.rcPaint.right - ps.rcPaint.left + 1, 2773 ps.rcPaint.bottom - ps.rcPaint.top + 1); 2774 EndPaint(hwnd, &ps); 2775 } 2776} 2777 2778/*ARGSUSED*/ 2779 static void 2780_OnSize( 2781 HWND hwnd, 2782 UINT state, 2783 int cx, 2784 int cy) 2785{ 2786 if (!IsMinimized(hwnd)) 2787 { 2788 gui_resize_shell(cx, cy); 2789 2790#ifdef FEAT_MENU 2791 /* Menu bar may wrap differently now */ 2792 gui_mswin_get_menu_height(TRUE); 2793#endif 2794 } 2795} 2796 2797 static void 2798_OnSetFocus( 2799 HWND hwnd, 2800 HWND hwndOldFocus) 2801{ 2802 gui_focus_change(TRUE); 2803 s_getting_focus = TRUE; 2804 (void)MyWindowProc(hwnd, WM_SETFOCUS, (WPARAM)hwndOldFocus, 0); 2805} 2806 2807 static void 2808_OnKillFocus( 2809 HWND hwnd, 2810 HWND hwndNewFocus) 2811{ 2812 gui_focus_change(FALSE); 2813 s_getting_focus = FALSE; 2814 (void)MyWindowProc(hwnd, WM_KILLFOCUS, (WPARAM)hwndNewFocus, 0); 2815} 2816 2817/* 2818 * Get a message when the user switches back to vim 2819 */ 2820#ifdef WIN16 2821 static BOOL 2822#else 2823 static LRESULT 2824#endif 2825_OnActivateApp( 2826 HWND hwnd, 2827 BOOL fActivate, 2828#ifdef WIN16 2829 HTASK dwThreadId 2830#else 2831 DWORD dwThreadId 2832#endif 2833 ) 2834{ 2835 /* we call gui_focus_change() in _OnSetFocus() */ 2836 /* gui_focus_change((int)fActivate); */ 2837 return MyWindowProc(hwnd, WM_ACTIVATEAPP, fActivate, (DWORD)dwThreadId); 2838} 2839 2840#if defined(FEAT_WINDOWS) || defined(PROTO) 2841 void 2842gui_mch_destroy_scrollbar(scrollbar_T *sb) 2843{ 2844 DestroyWindow(sb->id); 2845} 2846#endif 2847 2848/* 2849 * Get current mouse coordinates in text window. 2850 */ 2851 void 2852gui_mch_getmouse(int *x, int *y) 2853{ 2854 RECT rct; 2855 POINT mp; 2856 2857 (void)GetWindowRect(s_textArea, &rct); 2858 (void)GetCursorPos((LPPOINT)&mp); 2859 *x = (int)(mp.x - rct.left); 2860 *y = (int)(mp.y - rct.top); 2861} 2862 2863/* 2864 * Move mouse pointer to character at (x, y). 2865 */ 2866 void 2867gui_mch_setmouse(int x, int y) 2868{ 2869 RECT rct; 2870 2871 (void)GetWindowRect(s_textArea, &rct); 2872 (void)SetCursorPos(x + gui.border_offset + rct.left, 2873 y + gui.border_offset + rct.top); 2874} 2875 2876 static void 2877gui_mswin_get_valid_dimensions( 2878 int w, 2879 int h, 2880 int *valid_w, 2881 int *valid_h) 2882{ 2883 int base_width, base_height; 2884 2885 base_width = gui_get_base_width() 2886 + GetSystemMetrics(SM_CXFRAME) * 2; 2887 base_height = gui_get_base_height() 2888 + GetSystemMetrics(SM_CYFRAME) * 2 2889 + GetSystemMetrics(SM_CYCAPTION) 2890#ifdef FEAT_MENU 2891 + gui_mswin_get_menu_height(FALSE) 2892#endif 2893 ; 2894 *valid_w = base_width + 2895 ((w - base_width) / gui.char_width) * gui.char_width; 2896 *valid_h = base_height + 2897 ((h - base_height) / gui.char_height) * gui.char_height; 2898} 2899 2900 void 2901gui_mch_flash(int msec) 2902{ 2903 RECT rc; 2904 2905 /* 2906 * Note: InvertRect() excludes right and bottom of rectangle. 2907 */ 2908 rc.left = 0; 2909 rc.top = 0; 2910 rc.right = gui.num_cols * gui.char_width; 2911 rc.bottom = gui.num_rows * gui.char_height; 2912 InvertRect(s_hdc, &rc); 2913 gui_mch_flush(); /* make sure it's displayed */ 2914 2915 ui_delay((long)msec, TRUE); /* wait for a few msec */ 2916 2917 InvertRect(s_hdc, &rc); 2918} 2919 2920/* 2921 * Return flags used for scrolling. 2922 * The SW_INVALIDATE is required when part of the window is covered or 2923 * off-screen. Refer to MS KB Q75236. 2924 */ 2925 static int 2926get_scroll_flags(void) 2927{ 2928 HWND hwnd; 2929 RECT rcVim, rcOther, rcDest; 2930 2931 GetWindowRect(s_hwnd, &rcVim); 2932 2933 /* Check if the window is partly above or below the screen. We don't care 2934 * about partly left or right of the screen, it is not relevant when 2935 * scrolling up or down. */ 2936 if (rcVim.top < 0 || rcVim.bottom > GetSystemMetrics(SM_CYFULLSCREEN)) 2937 return SW_INVALIDATE; 2938 2939 /* Check if there is an window (partly) on top of us. */ 2940 for (hwnd = s_hwnd; (hwnd = GetWindow(hwnd, GW_HWNDPREV)) != (HWND)0; ) 2941 if (IsWindowVisible(hwnd)) 2942 { 2943 GetWindowRect(hwnd, &rcOther); 2944 if (IntersectRect(&rcDest, &rcVim, &rcOther)) 2945 return SW_INVALIDATE; 2946 } 2947 return 0; 2948} 2949 2950/* 2951 * Delete the given number of lines from the given row, scrolling up any 2952 * text further down within the scroll region. 2953 */ 2954 void 2955gui_mch_delete_lines( 2956 int row, 2957 int num_lines) 2958{ 2959 RECT rc; 2960 2961 rc.left = FILL_X(gui.scroll_region_left); 2962 rc.right = FILL_X(gui.scroll_region_right + 1); 2963 rc.top = FILL_Y(row); 2964 rc.bottom = FILL_Y(gui.scroll_region_bot + 1); 2965 2966 ScrollWindowEx(s_textArea, 0, -num_lines * gui.char_height, 2967 &rc, &rc, NULL, NULL, get_scroll_flags()); 2968 2969 UpdateWindow(s_textArea); 2970 /* This seems to be required to avoid the cursor disappearing when 2971 * scrolling such that the cursor ends up in the top-left character on 2972 * the screen... But why? (Webb) */ 2973 /* It's probably fixed by disabling drawing the cursor while scrolling. */ 2974 /* gui.cursor_is_valid = FALSE; */ 2975 2976 gui_clear_block(gui.scroll_region_bot - num_lines + 1, 2977 gui.scroll_region_left, 2978 gui.scroll_region_bot, gui.scroll_region_right); 2979} 2980 2981/* 2982 * Insert the given number of lines before the given row, scrolling down any 2983 * following text within the scroll region. 2984 */ 2985 void 2986gui_mch_insert_lines( 2987 int row, 2988 int num_lines) 2989{ 2990 RECT rc; 2991 2992 rc.left = FILL_X(gui.scroll_region_left); 2993 rc.right = FILL_X(gui.scroll_region_right + 1); 2994 rc.top = FILL_Y(row); 2995 rc.bottom = FILL_Y(gui.scroll_region_bot + 1); 2996 /* The SW_INVALIDATE is required when part of the window is covered or 2997 * off-screen. How do we avoid it when it's not needed? */ 2998 ScrollWindowEx(s_textArea, 0, num_lines * gui.char_height, 2999 &rc, &rc, NULL, NULL, get_scroll_flags()); 3000 3001 UpdateWindow(s_textArea); 3002 3003 gui_clear_block(row, gui.scroll_region_left, 3004 row + num_lines - 1, gui.scroll_region_right); 3005} 3006 3007 3008/*ARGSUSED*/ 3009 void 3010gui_mch_exit(int rc) 3011{ 3012 ReleaseDC(s_textArea, s_hdc); 3013 DeleteObject(s_brush); 3014 3015#ifdef FEAT_TEAROFF 3016 /* Unload the tearoff bitmap */ 3017 (void)DeleteObject((HGDIOBJ)s_htearbitmap); 3018#endif 3019 3020 /* Destroy our window (if we have one). */ 3021 if (s_hwnd != NULL) 3022 { 3023 destroying = TRUE; /* ignore WM_DESTROY message now */ 3024 DestroyWindow(s_hwnd); 3025 } 3026 3027#ifdef GLOBAL_IME 3028 global_ime_end(); 3029#endif 3030} 3031 3032 static char_u * 3033logfont2name(LOGFONT lf) 3034{ 3035 char *p; 3036 char *res; 3037 char *charset_name; 3038 3039 charset_name = charset_id2name((int)lf.lfCharSet); 3040 res = alloc((unsigned)(strlen(lf.lfFaceName) + 20 3041 + (charset_name == NULL ? 0 : strlen(charset_name) + 2))); 3042 if (res != NULL) 3043 { 3044 p = res; 3045 /* make a normal font string out of the lf thing:*/ 3046 sprintf((char *)p, "%s:h%d", lf.lfFaceName, pixels_to_points( 3047 lf.lfHeight < 0 ? -lf.lfHeight : lf.lfHeight, TRUE)); 3048 while (*p) 3049 { 3050 if (*p == ' ') 3051 *p = '_'; 3052 ++p; 3053 } 3054#ifndef MSWIN16_FASTTEXT 3055 if (lf.lfItalic) 3056 STRCAT(p, ":i"); 3057 if (lf.lfWeight >= FW_BOLD) 3058 STRCAT(p, ":b"); 3059#endif 3060 if (lf.lfUnderline) 3061 STRCAT(p, ":u"); 3062 if (lf.lfStrikeOut) 3063 STRCAT(p, ":s"); 3064 if (charset_name != NULL) 3065 { 3066 STRCAT(p, ":c"); 3067 STRCAT(p, charset_name); 3068 } 3069 } 3070 3071 return res; 3072} 3073 3074/* 3075 * Initialise vim to use the font with the given name. 3076 * Return FAIL if the font could not be loaded, OK otherwise. 3077 */ 3078/*ARGSUSED*/ 3079 int 3080gui_mch_init_font(char_u *font_name, int fontset) 3081{ 3082 LOGFONT lf; 3083 GuiFont font = NOFONT; 3084 char_u *p; 3085 3086 /* Load the font */ 3087 if (get_logfont(&lf, font_name, NULL, TRUE) == OK) 3088 font = get_font_handle(&lf); 3089 if (font == NOFONT) 3090 return FAIL; 3091 3092 if (font_name == NULL) 3093 font_name = lf.lfFaceName; 3094#if defined(FEAT_MBYTE_IME) || defined(GLOBAL_IME) 3095 norm_logfont = lf; 3096#endif 3097#ifdef FEAT_MBYTE_IME 3098 im_set_font(&lf); 3099#endif 3100 gui_mch_free_font(gui.norm_font); 3101 gui.norm_font = font; 3102 current_font_height = lf.lfHeight; 3103 GetFontSize(font); 3104 3105 p = logfont2name(lf); 3106 if (p != NULL) 3107 { 3108 hl_set_font_name(p); 3109 3110 /* When setting 'guifont' to "*" replace it with the actual font name. 3111 * */ 3112 if (STRCMP(font_name, "*") == 0 && STRCMP(p_guifont, "*") == 0) 3113 { 3114 vim_free(p_guifont); 3115 p_guifont = p; 3116 } 3117 else 3118 vim_free(p); 3119 } 3120 3121#ifndef MSWIN16_FASTTEXT 3122 gui_mch_free_font(gui.ital_font); 3123 gui.ital_font = NOFONT; 3124 gui_mch_free_font(gui.bold_font); 3125 gui.bold_font = NOFONT; 3126 gui_mch_free_font(gui.boldital_font); 3127 gui.boldital_font = NOFONT; 3128 3129 if (!lf.lfItalic) 3130 { 3131 lf.lfItalic = TRUE; 3132 gui.ital_font = get_font_handle(&lf); 3133 lf.lfItalic = FALSE; 3134 } 3135 if (lf.lfWeight < FW_BOLD) 3136 { 3137 lf.lfWeight = FW_BOLD; 3138 gui.bold_font = get_font_handle(&lf); 3139 if (!lf.lfItalic) 3140 { 3141 lf.lfItalic = TRUE; 3142 gui.boldital_font = get_font_handle(&lf); 3143 } 3144 } 3145#endif 3146 3147 return OK; 3148} 3149 3150#ifndef WPF_RESTORETOMAXIMIZED 3151# define WPF_RESTORETOMAXIMIZED 2 /* just in case someone doesn't have it */ 3152#endif 3153 3154/* 3155 * Return TRUE if the GUI window is maximized, filling the whole screen. 3156 */ 3157 int 3158gui_mch_maximized() 3159{ 3160 WINDOWPLACEMENT wp; 3161 3162 wp.length = sizeof(WINDOWPLACEMENT); 3163 if (GetWindowPlacement(s_hwnd, &wp)) 3164 return wp.showCmd == SW_SHOWMAXIMIZED 3165 || (wp.showCmd == SW_SHOWMINIMIZED 3166 && wp.flags == WPF_RESTORETOMAXIMIZED); 3167 3168 return 0; 3169} 3170 3171/* 3172 * Called when the font changed while the window is maximized. Compute the 3173 * new Rows and Columns. This is like resizing the window. 3174 */ 3175 void 3176gui_mch_newfont() 3177{ 3178 RECT rect; 3179 3180 GetWindowRect(s_hwnd, &rect); 3181 gui_resize_shell(rect.right - rect.left 3182 - GetSystemMetrics(SM_CXFRAME) * 2, 3183 rect.bottom - rect.top 3184 - GetSystemMetrics(SM_CYFRAME) * 2 3185 - GetSystemMetrics(SM_CYCAPTION) 3186#ifdef FEAT_MENU 3187 - gui_mswin_get_menu_height(FALSE) 3188#endif 3189 ); 3190} 3191 3192/* 3193 * Set the window title 3194 */ 3195/*ARGSUSED*/ 3196 void 3197gui_mch_settitle( 3198 char_u *title, 3199 char_u *icon) 3200{ 3201 set_window_title(s_hwnd, (title == NULL ? "VIM" : (char *)title)); 3202} 3203 3204#ifdef FEAT_MOUSESHAPE 3205/* Table for shape IDCs. Keep in sync with the mshape_names[] table in 3206 * misc2.c! */ 3207static LPCSTR mshape_idcs[] = 3208{ 3209 MAKEINTRESOURCE(IDC_ARROW), /* arrow */ 3210 MAKEINTRESOURCE(0), /* blank */ 3211 MAKEINTRESOURCE(IDC_IBEAM), /* beam */ 3212 MAKEINTRESOURCE(IDC_SIZENS), /* updown */ 3213 MAKEINTRESOURCE(IDC_SIZENS), /* udsizing */ 3214 MAKEINTRESOURCE(IDC_SIZEWE), /* leftright */ 3215 MAKEINTRESOURCE(IDC_SIZEWE), /* lrsizing */ 3216 MAKEINTRESOURCE(IDC_WAIT), /* busy */ 3217#ifdef WIN3264 3218 MAKEINTRESOURCE(IDC_NO), /* no */ 3219#else 3220 MAKEINTRESOURCE(IDC_ICON), /* no */ 3221#endif 3222 MAKEINTRESOURCE(IDC_ARROW), /* crosshair */ 3223 MAKEINTRESOURCE(IDC_ARROW), /* hand1 */ 3224 MAKEINTRESOURCE(IDC_ARROW), /* hand2 */ 3225 MAKEINTRESOURCE(IDC_ARROW), /* pencil */ 3226 MAKEINTRESOURCE(IDC_ARROW), /* question */ 3227 MAKEINTRESOURCE(IDC_ARROW), /* right-arrow */ 3228 MAKEINTRESOURCE(IDC_UPARROW), /* up-arrow */ 3229 MAKEINTRESOURCE(IDC_ARROW) /* last one */ 3230}; 3231 3232 void 3233mch_set_mouse_shape(int shape) 3234{ 3235 LPCSTR idc; 3236 3237 if (shape == MSHAPE_HIDE) 3238 ShowCursor(FALSE); 3239 else 3240 { 3241 if (shape >= MSHAPE_NUMBERED) 3242 idc = MAKEINTRESOURCE(IDC_ARROW); 3243 else 3244 idc = mshape_idcs[shape]; 3245#ifdef SetClassLongPtr 3246 SetClassLongPtr(s_textArea, GCLP_HCURSOR, (__int3264)(LONG_PTR)LoadCursor(NULL, idc)); 3247#else 3248# ifdef WIN32 3249 SetClassLong(s_textArea, GCL_HCURSOR, (long_u)LoadCursor(NULL, idc)); 3250# else /* Win16 */ 3251 SetClassWord(s_textArea, GCW_HCURSOR, (WORD)LoadCursor(NULL, idc)); 3252# endif 3253#endif 3254 if (!p_mh) 3255 { 3256 POINT mp; 3257 3258 /* Set the position to make it redrawn with the new shape. */ 3259 (void)GetCursorPos((LPPOINT)&mp); 3260 (void)SetCursorPos(mp.x, mp.y); 3261 ShowCursor(TRUE); 3262 } 3263 } 3264} 3265#endif 3266 3267#ifdef FEAT_BROWSE 3268/* 3269 * The file browser exists in two versions: with "W" uses wide characters, 3270 * without "W" the current codepage. When FEAT_MBYTE is defined and on 3271 * Windows NT/2000/XP the "W" functions are used. 3272 */ 3273 3274# if defined(FEAT_MBYTE) && defined(WIN3264) 3275/* 3276 * Wide version of convert_filter(). Keep in sync! 3277 */ 3278 static WCHAR * 3279convert_filterW(char_u *s) 3280{ 3281 WCHAR *res; 3282 unsigned s_len = (unsigned)STRLEN(s); 3283 unsigned i; 3284 3285 res = (WCHAR *)alloc((s_len + 3) * sizeof(WCHAR)); 3286 if (res != NULL) 3287 { 3288 for (i = 0; i < s_len; ++i) 3289 if (s[i] == '\t' || s[i] == '\n') 3290 res[i] = '\0'; 3291 else 3292 res[i] = s[i]; 3293 res[s_len] = NUL; 3294 /* Add two extra NULs to make sure it's properly terminated. */ 3295 res[s_len + 1] = NUL; 3296 res[s_len + 2] = NUL; 3297 } 3298 return res; 3299} 3300 3301/* 3302 * Wide version of gui_mch_browse(). Keep in sync! 3303 */ 3304 static char_u * 3305gui_mch_browseW( 3306 int saving, 3307 char_u *title, 3308 char_u *dflt, 3309 char_u *ext, 3310 char_u *initdir, 3311 char_u *filter) 3312{ 3313 /* We always use the wide function. This means enc_to_utf16() must work, 3314 * otherwise it fails miserably! */ 3315 OPENFILENAMEW fileStruct; 3316 WCHAR fileBuf[MAXPATHL]; 3317 WCHAR *wp; 3318 int i; 3319 WCHAR *titlep = NULL; 3320 WCHAR *extp = NULL; 3321 WCHAR *initdirp = NULL; 3322 WCHAR *filterp; 3323 char_u *p; 3324 3325 if (dflt == NULL) 3326 fileBuf[0] = NUL; 3327 else 3328 { 3329 wp = enc_to_utf16(dflt, NULL); 3330 if (wp == NULL) 3331 fileBuf[0] = NUL; 3332 else 3333 { 3334 for (i = 0; wp[i] != NUL && i < MAXPATHL - 1; ++i) 3335 fileBuf[i] = wp[i]; 3336 fileBuf[i] = NUL; 3337 vim_free(wp); 3338 } 3339 } 3340 3341 /* Convert the filter to Windows format. */ 3342 filterp = convert_filterW(filter); 3343 3344 vim_memset(&fileStruct, 0, sizeof(OPENFILENAMEW)); 3345#ifdef OPENFILENAME_SIZE_VERSION_400 3346 /* be compatible with Windows NT 4.0 */ 3347 /* TODO: what to use for OPENFILENAMEW??? */ 3348 fileStruct.lStructSize = OPENFILENAME_SIZE_VERSION_400; 3349#else 3350 fileStruct.lStructSize = sizeof(fileStruct); 3351#endif 3352 3353 if (title != NULL) 3354 titlep = enc_to_utf16(title, NULL); 3355 fileStruct.lpstrTitle = titlep; 3356 3357 if (ext != NULL) 3358 extp = enc_to_utf16(ext, NULL); 3359 fileStruct.lpstrDefExt = extp; 3360 3361 fileStruct.lpstrFile = fileBuf; 3362 fileStruct.nMaxFile = MAXPATHL; 3363 fileStruct.lpstrFilter = filterp; 3364 fileStruct.hwndOwner = s_hwnd; /* main Vim window is owner*/ 3365 /* has an initial dir been specified? */ 3366 if (initdir != NULL && *initdir != NUL) 3367 { 3368 /* Must have backslashes here, no matter what 'shellslash' says */ 3369 initdirp = enc_to_utf16(initdir, NULL); 3370 if (initdirp != NULL) 3371 { 3372 for (wp = initdirp; *wp != NUL; ++wp) 3373 if (*wp == '/') 3374 *wp = '\\'; 3375 } 3376 fileStruct.lpstrInitialDir = initdirp; 3377 } 3378 3379 /* 3380 * TODO: Allow selection of multiple files. Needs another arg to this 3381 * function to ask for it, and need to use OFN_ALLOWMULTISELECT below. 3382 * Also, should we use OFN_FILEMUSTEXIST when opening? Vim can edit on 3383 * files that don't exist yet, so I haven't put it in. What about 3384 * OFN_PATHMUSTEXIST? 3385 * Don't use OFN_OVERWRITEPROMPT, Vim has its own ":confirm" dialog. 3386 */ 3387 fileStruct.Flags = (OFN_NOCHANGEDIR | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY); 3388#ifdef FEAT_SHORTCUT 3389 if (curbuf->b_p_bin) 3390 fileStruct.Flags |= OFN_NODEREFERENCELINKS; 3391#endif 3392 if (saving) 3393 { 3394 if (!GetSaveFileNameW(&fileStruct)) 3395 return NULL; 3396 } 3397 else 3398 { 3399 if (!GetOpenFileNameW(&fileStruct)) 3400 return NULL; 3401 } 3402 3403 vim_free(filterp); 3404 vim_free(initdirp); 3405 vim_free(titlep); 3406 vim_free(extp); 3407 3408 /* Convert from UCS2 to 'encoding'. */ 3409 p = utf16_to_enc(fileBuf, NULL); 3410 if (p != NULL) 3411 /* when out of memory we get garbage for non-ASCII chars */ 3412 STRCPY(fileBuf, p); 3413 vim_free(p); 3414 3415 /* Give focus back to main window (when using MDI). */ 3416 SetFocus(s_hwnd); 3417 3418 /* Shorten the file name if possible */ 3419 return vim_strsave(shorten_fname1((char_u *)fileBuf)); 3420} 3421# endif /* FEAT_MBYTE */ 3422 3423 3424/* 3425 * Convert the string s to the proper format for a filter string by replacing 3426 * the \t and \n delimiters with \0. 3427 * Returns the converted string in allocated memory. 3428 * 3429 * Keep in sync with convert_filterW() above! 3430 */ 3431 static char_u * 3432convert_filter(char_u *s) 3433{ 3434 char_u *res; 3435 unsigned s_len = (unsigned)STRLEN(s); 3436 unsigned i; 3437 3438 res = alloc(s_len + 3); 3439 if (res != NULL) 3440 { 3441 for (i = 0; i < s_len; ++i) 3442 if (s[i] == '\t' || s[i] == '\n') 3443 res[i] = '\0'; 3444 else 3445 res[i] = s[i]; 3446 res[s_len] = NUL; 3447 /* Add two extra NULs to make sure it's properly terminated. */ 3448 res[s_len + 1] = NUL; 3449 res[s_len + 2] = NUL; 3450 } 3451 return res; 3452} 3453 3454/* 3455 * Select a directory. 3456 */ 3457 char_u * 3458gui_mch_browsedir(char_u *title, char_u *initdir) 3459{ 3460 /* We fake this: Use a filter that doesn't select anything and a default 3461 * file name that won't be used. */ 3462 return gui_mch_browse(0, title, (char_u *)_("Not Used"), NULL, 3463 initdir, (char_u *)_("Directory\t*.nothing\n")); 3464} 3465 3466/* 3467 * Pop open a file browser and return the file selected, in allocated memory, 3468 * or NULL if Cancel is hit. 3469 * saving - TRUE if the file will be saved to, FALSE if it will be opened. 3470 * title - Title message for the file browser dialog. 3471 * dflt - Default name of file. 3472 * ext - Default extension to be added to files without extensions. 3473 * initdir - directory in which to open the browser (NULL = current dir) 3474 * filter - Filter for matched files to choose from. 3475 * 3476 * Keep in sync with gui_mch_browseW() above! 3477 */ 3478 char_u * 3479gui_mch_browse( 3480 int saving, 3481 char_u *title, 3482 char_u *dflt, 3483 char_u *ext, 3484 char_u *initdir, 3485 char_u *filter) 3486{ 3487 OPENFILENAME fileStruct; 3488 char_u fileBuf[MAXPATHL]; 3489 char_u *initdirp = NULL; 3490 char_u *filterp; 3491 char_u *p; 3492 3493# if defined(FEAT_MBYTE) && defined(WIN3264) 3494 if (os_version.dwPlatformId == VER_PLATFORM_WIN32_NT) 3495 return gui_mch_browseW(saving, title, dflt, ext, initdir, filter); 3496# endif 3497 3498 if (dflt == NULL) 3499 fileBuf[0] = NUL; 3500 else 3501 vim_strncpy(fileBuf, dflt, MAXPATHL - 1); 3502 3503 /* Convert the filter to Windows format. */ 3504 filterp = convert_filter(filter); 3505 3506 vim_memset(&fileStruct, 0, sizeof(OPENFILENAME)); 3507#ifdef OPENFILENAME_SIZE_VERSION_400 3508 /* be compatible with Windows NT 4.0 */ 3509 fileStruct.lStructSize = OPENFILENAME_SIZE_VERSION_400; 3510#else 3511 fileStruct.lStructSize = sizeof(fileStruct); 3512#endif 3513 3514 fileStruct.lpstrTitle = title; 3515 fileStruct.lpstrDefExt = ext; 3516 3517 fileStruct.lpstrFile = fileBuf; 3518 fileStruct.nMaxFile = MAXPATHL; 3519 fileStruct.lpstrFilter = filterp; 3520 fileStruct.hwndOwner = s_hwnd; /* main Vim window is owner*/ 3521 /* has an initial dir been specified? */ 3522 if (initdir != NULL && *initdir != NUL) 3523 { 3524 /* Must have backslashes here, no matter what 'shellslash' says */ 3525 initdirp = vim_strsave(initdir); 3526 if (initdirp != NULL) 3527 for (p = initdirp; *p != NUL; ++p) 3528 if (*p == '/') 3529 *p = '\\'; 3530 fileStruct.lpstrInitialDir = initdirp; 3531 } 3532 3533 /* 3534 * TODO: Allow selection of multiple files. Needs another arg to this 3535 * function to ask for it, and need to use OFN_ALLOWMULTISELECT below. 3536 * Also, should we use OFN_FILEMUSTEXIST when opening? Vim can edit on 3537 * files that don't exist yet, so I haven't put it in. What about 3538 * OFN_PATHMUSTEXIST? 3539 * Don't use OFN_OVERWRITEPROMPT, Vim has its own ":confirm" dialog. 3540 */ 3541 fileStruct.Flags = (OFN_NOCHANGEDIR | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY); 3542#ifdef FEAT_SHORTCUT 3543 if (curbuf->b_p_bin) 3544 fileStruct.Flags |= OFN_NODEREFERENCELINKS; 3545#endif 3546 if (saving) 3547 { 3548 if (!GetSaveFileName(&fileStruct)) 3549 return NULL; 3550 } 3551 else 3552 { 3553 if (!GetOpenFileName(&fileStruct)) 3554 return NULL; 3555 } 3556 3557 vim_free(filterp); 3558 vim_free(initdirp); 3559 3560 /* Give focus back to main window (when using MDI). */ 3561 SetFocus(s_hwnd); 3562 3563 /* Shorten the file name if possible */ 3564 return vim_strsave(shorten_fname1((char_u *)fileBuf)); 3565} 3566#endif /* FEAT_BROWSE */ 3567 3568/*ARGSUSED*/ 3569 static void 3570_OnDropFiles( 3571 HWND hwnd, 3572 HDROP hDrop) 3573{ 3574#ifdef FEAT_WINDOWS 3575#ifdef WIN3264 3576# define BUFPATHLEN _MAX_PATH 3577# define DRAGQVAL 0xFFFFFFFF 3578#else 3579# define BUFPATHLEN MAXPATHL 3580# define DRAGQVAL 0xFFFF 3581#endif 3582#ifdef FEAT_MBYTE 3583 WCHAR wszFile[BUFPATHLEN]; 3584#endif 3585 char szFile[BUFPATHLEN]; 3586 UINT cFiles = DragQueryFile(hDrop, DRAGQVAL, NULL, 0); 3587 UINT i; 3588 char_u **fnames; 3589 POINT pt; 3590 int_u modifiers = 0; 3591 3592 /* TRACE("_OnDropFiles: %d files dropped\n", cFiles); */ 3593 3594 /* Obtain dropped position */ 3595 DragQueryPoint(hDrop, &pt); 3596 MapWindowPoints(s_hwnd, s_textArea, &pt, 1); 3597 3598# ifdef FEAT_VISUAL 3599 reset_VIsual(); 3600# endif 3601 3602 fnames = (char_u **)alloc(cFiles * sizeof(char_u *)); 3603 3604 if (fnames != NULL) 3605 for (i = 0; i < cFiles; ++i) 3606 { 3607#ifdef FEAT_MBYTE 3608 if (DragQueryFileW(hDrop, i, wszFile, BUFPATHLEN) > 0) 3609 fnames[i] = utf16_to_enc(wszFile, NULL); 3610 else 3611#endif 3612 { 3613 DragQueryFile(hDrop, i, szFile, BUFPATHLEN); 3614 fnames[i] = vim_strsave(szFile); 3615 } 3616 } 3617 3618 DragFinish(hDrop); 3619 3620 if (fnames != NULL) 3621 { 3622 if ((GetKeyState(VK_SHIFT) & 0x8000) != 0) 3623 modifiers |= MOUSE_SHIFT; 3624 if ((GetKeyState(VK_CONTROL) & 0x8000) != 0) 3625 modifiers |= MOUSE_CTRL; 3626 if ((GetKeyState(VK_MENU) & 0x8000) != 0) 3627 modifiers |= MOUSE_ALT; 3628 3629 gui_handle_drop(pt.x, pt.y, modifiers, fnames, cFiles); 3630 3631 s_need_activate = TRUE; 3632 } 3633#endif 3634} 3635 3636/*ARGSUSED*/ 3637 static int 3638_OnScroll( 3639 HWND hwnd, 3640 HWND hwndCtl, 3641 UINT code, 3642 int pos) 3643{ 3644 static UINT prev_code = 0; /* code of previous call */ 3645 scrollbar_T *sb, *sb_info; 3646 long val; 3647 int dragging = FALSE; 3648 int dont_scroll_save = dont_scroll; 3649#ifndef WIN3264 3650 int nPos; 3651#else 3652 SCROLLINFO si; 3653 3654 si.cbSize = sizeof(si); 3655 si.fMask = SIF_POS; 3656#endif 3657 3658 sb = gui_mswin_find_scrollbar(hwndCtl); 3659 if (sb == NULL) 3660 return 0; 3661 3662 if (sb->wp != NULL) /* Left or right scrollbar */ 3663 { 3664 /* 3665 * Careful: need to get scrollbar info out of first (left) scrollbar 3666 * for window, but keep real scrollbar too because we must pass it to 3667 * gui_drag_scrollbar(). 3668 */ 3669 sb_info = &sb->wp->w_scrollbars[0]; 3670 } 3671 else /* Bottom scrollbar */ 3672 sb_info = sb; 3673 val = sb_info->value; 3674 3675 switch (code) 3676 { 3677 case SB_THUMBTRACK: 3678 val = pos; 3679 dragging = TRUE; 3680 if (sb->scroll_shift > 0) 3681 val <<= sb->scroll_shift; 3682 break; 3683 case SB_LINEDOWN: 3684 val++; 3685 break; 3686 case SB_LINEUP: 3687 val--; 3688 break; 3689 case SB_PAGEDOWN: 3690 val += (sb_info->size > 2 ? sb_info->size - 2 : 1); 3691 break; 3692 case SB_PAGEUP: 3693 val -= (sb_info->size > 2 ? sb_info->size - 2 : 1); 3694 break; 3695 case SB_TOP: 3696 val = 0; 3697 break; 3698 case SB_BOTTOM: 3699 val = sb_info->max; 3700 break; 3701 case SB_ENDSCROLL: 3702 if (prev_code == SB_THUMBTRACK) 3703 { 3704 /* 3705 * "pos" only gives us 16-bit data. In case of large file, 3706 * use GetScrollPos() which returns 32-bit. Unfortunately it 3707 * is not valid while the scrollbar is being dragged. 3708 */ 3709 val = GetScrollPos(hwndCtl, SB_CTL); 3710 if (sb->scroll_shift > 0) 3711 val <<= sb->scroll_shift; 3712 } 3713 break; 3714 3715 default: 3716 /* TRACE("Unknown scrollbar event %d\n", code); */ 3717 return 0; 3718 } 3719 prev_code = code; 3720 3721#ifdef WIN3264 3722 si.nPos = (sb->scroll_shift > 0) ? val >> sb->scroll_shift : val; 3723 SetScrollInfo(hwndCtl, SB_CTL, &si, TRUE); 3724#else 3725 nPos = (sb->scroll_shift > 0) ? val >> sb->scroll_shift : val; 3726 SetScrollPos(hwndCtl, SB_CTL, nPos, TRUE); 3727#endif 3728 3729 /* 3730 * When moving a vertical scrollbar, move the other vertical scrollbar too. 3731 */ 3732 if (sb->wp != NULL) 3733 { 3734 scrollbar_T *sba = sb->wp->w_scrollbars; 3735 HWND id = sba[ (sb == sba + SBAR_LEFT) ? SBAR_RIGHT : SBAR_LEFT].id; 3736 3737#ifdef WIN3264 3738 SetScrollInfo(id, SB_CTL, &si, TRUE); 3739#else 3740 SetScrollPos(id, SB_CTL, nPos, TRUE); 3741#endif 3742 } 3743 3744 /* Don't let us be interrupted here by another message. */ 3745 s_busy_processing = TRUE; 3746 3747 /* When "allow_scrollbar" is FALSE still need to remember the new 3748 * position, but don't actually scroll by setting "dont_scroll". */ 3749 dont_scroll = !allow_scrollbar; 3750 3751 gui_drag_scrollbar(sb, val, dragging); 3752 3753 s_busy_processing = FALSE; 3754 dont_scroll = dont_scroll_save; 3755 3756 return 0; 3757} 3758 3759 3760/* 3761 * Get command line arguments. 3762 * Use "prog" as the name of the program and "cmdline" as the arguments. 3763 * Copy the arguments to allocated memory. 3764 * Return the number of arguments (including program name). 3765 * Return pointers to the arguments in "argvp". Memory is allocated with 3766 * malloc(), use free() instead of vim_free(). 3767 * Return pointer to buffer in "tofree". 3768 * Returns zero when out of memory. 3769 */ 3770/*ARGSUSED*/ 3771 int 3772get_cmd_args(char *prog, char *cmdline, char ***argvp, char **tofree) 3773{ 3774 int i; 3775 char *p; 3776 char *progp; 3777 char *pnew = NULL; 3778 char *newcmdline; 3779 int inquote; 3780 int argc; 3781 char **argv = NULL; 3782 int round; 3783 3784 *tofree = NULL; 3785 3786#ifdef FEAT_MBYTE 3787 /* Try using the Unicode version first, it takes care of conversion when 3788 * 'encoding' is changed. */ 3789 argc = get_cmd_argsW(&argv); 3790 if (argc != 0) 3791 goto done; 3792#endif 3793 3794 /* Handle the program name. Remove the ".exe" extension, and find the 1st 3795 * non-space. */ 3796 p = strrchr(prog, '.'); 3797 if (p != NULL) 3798 *p = NUL; 3799 for (progp = prog; *progp == ' '; ++progp) 3800 ; 3801 3802 /* The command line is copied to allocated memory, so that we can change 3803 * it. Add the size of the string, the separating NUL and a terminating 3804 * NUL. */ 3805 newcmdline = malloc(STRLEN(cmdline) + STRLEN(progp) + 2); 3806 if (newcmdline == NULL) 3807 return 0; 3808 3809 /* 3810 * First round: count the number of arguments ("pnew" == NULL). 3811 * Second round: produce the arguments. 3812 */ 3813 for (round = 1; round <= 2; ++round) 3814 { 3815 /* First argument is the program name. */ 3816 if (pnew != NULL) 3817 { 3818 argv[0] = pnew; 3819 strcpy(pnew, progp); 3820 pnew += strlen(pnew); 3821 *pnew++ = NUL; 3822 } 3823 3824 /* 3825 * Isolate each argument and put it in argv[]. 3826 */ 3827 p = cmdline; 3828 argc = 1; 3829 while (*p != NUL) 3830 { 3831 inquote = FALSE; 3832 if (pnew != NULL) 3833 argv[argc] = pnew; 3834 ++argc; 3835 while (*p != NUL && (inquote || (*p != ' ' && *p != '\t'))) 3836 { 3837 /* Backslashes are only special when followed by a double 3838 * quote. */ 3839 i = (int)strspn(p, "\\"); 3840 if (p[i] == '"') 3841 { 3842 /* Halve the number of backslashes. */ 3843 if (i > 1 && pnew != NULL) 3844 { 3845 vim_memset(pnew, '\\', i / 2); 3846 pnew += i / 2; 3847 } 3848 3849 /* Even nr of backslashes toggles quoting, uneven copies 3850 * the double quote. */ 3851 if ((i & 1) == 0) 3852 inquote = !inquote; 3853 else if (pnew != NULL) 3854 *pnew++ = '"'; 3855 p += i + 1; 3856 } 3857 else if (i > 0) 3858 { 3859 /* Copy span of backslashes unmodified. */ 3860 if (pnew != NULL) 3861 { 3862 vim_memset(pnew, '\\', i); 3863 pnew += i; 3864 } 3865 p += i; 3866 } 3867 else 3868 { 3869 if (pnew != NULL) 3870 *pnew++ = *p; 3871#ifdef FEAT_MBYTE 3872 /* Can't use mb_* functions, because 'encoding' is not 3873 * initialized yet here. */ 3874 if (IsDBCSLeadByte(*p)) 3875 { 3876 ++p; 3877 if (pnew != NULL) 3878 *pnew++ = *p; 3879 } 3880#endif 3881 ++p; 3882 } 3883 } 3884 3885 if (pnew != NULL) 3886 *pnew++ = NUL; 3887 while (*p == ' ' || *p == '\t') 3888 ++p; /* advance until a non-space */ 3889 } 3890 3891 if (round == 1) 3892 { 3893 argv = (char **)malloc((argc + 1) * sizeof(char *)); 3894 if (argv == NULL ) 3895 { 3896 free(newcmdline); 3897 return 0; /* malloc error */ 3898 } 3899 pnew = newcmdline; 3900 *tofree = newcmdline; 3901 } 3902 } 3903 3904#ifdef FEAT_MBYTE 3905done: 3906#endif 3907 argv[argc] = NULL; /* NULL-terminated list */ 3908 *argvp = argv; 3909 return argc; 3910} 3911