comparison src/gui_w32.c @ 8140:563c923b1584 v7.4.1364

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