summaryrefslogtreecommitdiffstats
path: root/src/gui_w32.c
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2016-02-20 13:55:06 +0100
committerBram Moolenaar <Bram@vim.org>2016-02-20 13:55:06 +0100
commitcf7164a088664961e7d70dd100c5874dc5ceb293 (patch)
tree93cf8f7643786a9cb9488852b73c1ef480031ee1 /src/gui_w32.c
parent065bbac8adfe29a09958570237d223457f235c6c (diff)
patch 7.4.1364v7.4.1364
Problem: The Win 16 code is not maintained and unused. Solution: Remove the Win 16 support.
Diffstat (limited to 'src/gui_w32.c')
-rw-r--r--src/gui_w32.c4107
1 files changed, 4088 insertions, 19 deletions
diff --git a/src/gui_w32.c b/src/gui_w32.c
index feeb6d402c..5129c5fbcb 100644
--- a/src/gui_w32.c
+++ b/src/gui_w32.c
@@ -10,7 +10,7 @@
/*
* Windows GUI.
*
- * GUI support for Microsoft Windows. Win32 initially; maybe Win16 later
+ * GUI support for Microsoft Windows, aka Win32. Also for Win64.
*
* George V. Reilly <george@reilly.org> wrote the original Win32 GUI.
* Robert Webb reworked it to use the existing GUI stuff and added menu,
@@ -185,10 +185,4095 @@ gui_mch_set_rendering_options(char_u *s)
((fn)((hwnd), TRUE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
#endif
+
+#include "version.h" /* used by dialog box routine for default title */
+#ifdef DEBUG
+# include <tchar.h>
+#endif
+
+/* cproto fails on missing include files */
+#ifndef PROTO
+
+#ifndef __MINGW32__
+# include <shellapi.h>
+#endif
+#if defined(FEAT_TOOLBAR) || defined(FEAT_BEVAL) || defined(FEAT_GUI_TABLINE)
+# include <commctrl.h>
+#endif
+#include <windowsx.h>
+
+#ifdef GLOBAL_IME
+# include "glbl_ime.h"
+#endif
+
+#endif /* PROTO */
+
+#ifdef FEAT_MENU
+# define MENUHINTS /* show menu hints in command line */
+#endif
+
+/* Some parameters for dialog boxes. All in pixels. */
+#define DLG_PADDING_X 10
+#define DLG_PADDING_Y 10
+#define DLG_OLD_STYLE_PADDING_X 5
+#define DLG_OLD_STYLE_PADDING_Y 5
+#define DLG_VERT_PADDING_X 4 /* For vertical buttons */
+#define DLG_VERT_PADDING_Y 4
+#define DLG_ICON_WIDTH 34
+#define DLG_ICON_HEIGHT 34
+#define DLG_MIN_WIDTH 150
+#define DLG_FONT_NAME "MS Sans Serif"
+#define DLG_FONT_POINT_SIZE 8
+#define DLG_MIN_MAX_WIDTH 400
+#define DLG_MIN_MAX_HEIGHT 400
+
+#define DLG_NONBUTTON_CONTROL 5000 /* First ID of non-button controls */
+
+#ifndef WM_XBUTTONDOWN /* For Win2K / winME ONLY */
+# define WM_XBUTTONDOWN 0x020B
+# define WM_XBUTTONUP 0x020C
+# define WM_XBUTTONDBLCLK 0x020D
+# define MK_XBUTTON1 0x0020
+# define MK_XBUTTON2 0x0040
+#endif
+
+#ifdef PROTO
+/*
+ * Define a few things for generating prototypes. This is just to avoid
+ * syntax errors, the defines do not need to be correct.
+ */
+# define APIENTRY
+# define CALLBACK
+# define CONST
+# define FAR
+# define NEAR
+# define _cdecl
+typedef int BOOL;
+typedef int BYTE;
+typedef int DWORD;
+typedef int WCHAR;
+typedef int ENUMLOGFONT;
+typedef int FINDREPLACE;
+typedef int HANDLE;
+typedef int HBITMAP;
+typedef int HBRUSH;
+typedef int HDROP;
+typedef int INT;
+typedef int LOGFONT[];
+typedef int LPARAM;
+typedef int LPCREATESTRUCT;
+typedef int LPCSTR;
+typedef int LPCTSTR;
+typedef int LPRECT;
+typedef int LPSTR;
+typedef int LPWINDOWPOS;
+typedef int LPWORD;
+typedef int LRESULT;
+typedef int HRESULT;
+# undef MSG
+typedef int MSG;
+typedef int NEWTEXTMETRIC;
+typedef int OSVERSIONINFO;
+typedef int PWORD;
+typedef int RECT;
+typedef int UINT;
+typedef int WORD;
+typedef int WPARAM;
+typedef int POINT;
+typedef void *HINSTANCE;
+typedef void *HMENU;
+typedef void *HWND;
+typedef void *HDC;
+typedef void VOID;
+typedef int LPNMHDR;
+typedef int LONG;
+typedef int WNDPROC;
+#endif
+
+#ifndef GET_X_LPARAM
+# define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp))
+#endif
+
+static void _OnPaint( HWND hwnd);
+static void clear_rect(RECT *rcp);
+
+static WORD s_dlgfntheight; /* height of the dialog font */
+static WORD s_dlgfntwidth; /* width of the dialog font */
+
+#ifdef FEAT_MENU
+static HMENU s_menuBar = NULL;
+#endif
+#ifdef FEAT_TEAROFF
+static void rebuild_tearoff(vimmenu_T *menu);
+static HBITMAP s_htearbitmap; /* bitmap used to indicate tearoff */
+#endif
+
+/* Flag that is set while processing a message that must not be interrupted by
+ * processing another message. */
+static int s_busy_processing = FALSE;
+
+static int destroying = FALSE; /* call DestroyWindow() ourselves */
+
+#ifdef MSWIN_FIND_REPLACE
+static UINT s_findrep_msg = 0; /* set in gui_w[16/32].c */
+static FINDREPLACE s_findrep_struct;
+# if defined(FEAT_MBYTE) && defined(WIN3264)
+static FINDREPLACEW s_findrep_struct_w;
+# endif
+static HWND s_findrep_hwnd = NULL;
+static int s_findrep_is_find; /* TRUE for find dialog, FALSE
+ for find/replace dialog */
+#endif
+
+static HINSTANCE s_hinst = NULL;
+#if !defined(FEAT_SNIFF) && !defined(FEAT_GUI)
+static
+#endif
+HWND s_hwnd = NULL;
+static HDC s_hdc = NULL;
+static HBRUSH s_brush = NULL;
+
+#ifdef FEAT_TOOLBAR
+static HWND s_toolbarhwnd = NULL;
+static WNDPROC s_toolbar_wndproc = NULL;
+#endif
+
+#ifdef FEAT_GUI_TABLINE
+static HWND s_tabhwnd = NULL;
+static WNDPROC s_tabline_wndproc = NULL;
+static int showing_tabline = 0;
+#endif
+
+static WPARAM s_wParam = 0;
+static LPARAM s_lParam = 0;
+
+static HWND s_textArea = NULL;
+static UINT s_uMsg = 0;
+
+static char_u *s_textfield; /* Used by dialogs to pass back strings */
+
+static int s_need_activate = FALSE;
+
+/* This variable is set when waiting for an event, which is the only moment
+ * scrollbar dragging can be done directly. It's not allowed while commands
+ * are executed, because it may move the cursor and that may cause unexpected
+ * problems (e.g., while ":s" is working).
+ */
+static int allow_scrollbar = FALSE;
+
+#ifdef GLOBAL_IME
+# define MyTranslateMessage(x) global_ime_TranslateMessage(x)
+#else
+# define MyTranslateMessage(x) TranslateMessage(x)
+#endif
+
+#if (defined(WIN3264) && defined(FEAT_MBYTE)) || defined(GLOBAL_IME)
+ /* use of WindowProc depends on wide_WindowProc */
+# define MyWindowProc vim_WindowProc
+#else
+ /* use ordinary WindowProc */
+# define MyWindowProc DefWindowProc
+#endif
+
+extern int current_font_height; /* this is in os_mswin.c */
+
+static struct
+{
+ UINT key_sym;
+ char_u vim_code0;
+ char_u vim_code1;
+} special_keys[] =
+{
+ {VK_UP, 'k', 'u'},
+ {VK_DOWN, 'k', 'd'},
+ {VK_LEFT, 'k', 'l'},
+ {VK_RIGHT, 'k', 'r'},
+
+ {VK_F1, 'k', '1'},
+ {VK_F2, 'k', '2'},
+ {VK_F3, 'k', '3'},
+ {VK_F4, 'k', '4'},
+ {VK_F5, 'k', '5'},
+ {VK_F6, 'k', '6'},
+ {VK_F7, 'k', '7'},
+ {VK_F8, 'k', '8'},
+ {VK_F9, 'k', '9'},
+ {VK_F10, 'k', ';'},
+
+ {VK_F11, 'F', '1'},
+ {VK_F12, 'F', '2'},
+ {VK_F13, 'F', '3'},
+ {VK_F14, 'F', '4'},
+ {VK_F15, 'F', '5'},
+ {VK_F16, 'F', '6'},
+ {VK_F17, 'F', '7'},
+ {VK_F18, 'F', '8'},
+ {VK_F19, 'F', '9'},
+ {VK_F20, 'F', 'A'},
+
+ {VK_F21, 'F', 'B'},
+#ifdef FEAT_NETBEANS_INTG
+ {VK_PAUSE, 'F', 'B'}, /* Pause == F21 (see gui_gtk_x11.c) */
+#endif
+ {VK_F22, 'F', 'C'},
+ {VK_F23, 'F', 'D'},
+ {VK_F24, 'F', 'E'}, /* winuser.h defines up to F24 */
+
+ {VK_HELP, '%', '1'},
+ {VK_BACK, 'k', 'b'},
+ {VK_INSERT, 'k', 'I'},
+ {VK_DELETE, 'k', 'D'},
+ {VK_HOME, 'k', 'h'},
+ {VK_END, '@', '7'},
+ {VK_PRIOR, 'k', 'P'},
+ {VK_NEXT, 'k', 'N'},
+ {VK_PRINT, '%', '9'},
+ {VK_ADD, 'K', '6'},
+ {VK_SUBTRACT, 'K', '7'},
+ {VK_DIVIDE, 'K', '8'},
+ {VK_MULTIPLY, 'K', '9'},
+ {VK_SEPARATOR, 'K', 'A'}, /* Keypad Enter */
+ {VK_DECIMAL, 'K', 'B'},
+
+ {VK_NUMPAD0, 'K', 'C'},
+ {VK_NUMPAD1, 'K', 'D'},
+ {VK_NUMPAD2, 'K', 'E'},
+ {VK_NUMPAD3, 'K', 'F'},
+ {VK_NUMPAD4, 'K', 'G'},
+ {VK_NUMPAD5, 'K', 'H'},
+ {VK_NUMPAD6, 'K', 'I'},
+ {VK_NUMPAD7, 'K', 'J'},
+ {VK_NUMPAD8, 'K', 'K'},
+ {VK_NUMPAD9, 'K', 'L'},
+
+ /* Keys that we want to be able to use any modifier with: */
+ {VK_SPACE, ' ', NUL},
+ {VK_TAB, TAB, NUL},
+ {VK_ESCAPE, ESC, NUL},
+ {NL, NL, NUL},
+ {CAR, CAR, NUL},
+
+ /* End of list marker: */
+ {0, 0, 0}
+};
+
+/* Local variables */
+static int s_button_pending = -1;
+
+/* s_getting_focus is set when we got focus but didn't see mouse-up event yet,
+ * so don't reset s_button_pending. */
+static int s_getting_focus = FALSE;
+
+static int s_x_pending;
+static int s_y_pending;
+static UINT s_kFlags_pending;
+static UINT s_wait_timer = 0; /* Timer for get char from user */
+static int s_timed_out = FALSE;
+static int dead_key = 0; /* 0: no dead key, 1: dead key pressed */
+
+#ifdef WIN3264
+static OSVERSIONINFO os_version; /* like it says. Init in gui_mch_init() */
+#endif
+
+#ifdef FEAT_BEVAL
+/* balloon-eval WM_NOTIFY_HANDLER */
+static void Handle_WM_Notify(HWND hwnd, LPNMHDR pnmh);
+static void TrackUserActivity(UINT uMsg);
+#endif
+
+/*
+ * For control IME.
+ *
+ * These LOGFONT used for IME.
+ */
+#ifdef FEAT_MBYTE
+# ifdef USE_IM_CONTROL
+/* holds LOGFONT for 'guifontwide' if available, otherwise 'guifont' */
+static LOGFONT norm_logfont;
+/* holds LOGFONT for 'guifont' always. */
+static LOGFONT sub_logfont;
+# endif
+#endif
+
+#ifdef FEAT_MBYTE_IME
+static LRESULT _OnImeNotify(HWND hWnd, DWORD dwCommand, DWORD dwData);
+#endif
+
+#if defined(FEAT_BROWSE)
+static char_u *convert_filter(char_u *s);
+#endif
+
+#ifdef DEBUG_PRINT_ERROR
+/*
+ * Print out the last Windows error message
+ */
+ static void
+print_windows_error(void)
+{
+ LPVOID lpMsgBuf;
+
+ FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL, GetLastError(),
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR) &lpMsgBuf, 0, NULL);
+ TRACE1("Error: %s\n", lpMsgBuf);
+ LocalFree(lpMsgBuf);
+}
+#endif
+
+/*
+ * Cursor blink functions.
+ *
+ * This is a simple state machine:
+ * BLINK_NONE not blinking at all
+ * BLINK_OFF blinking, cursor is not shown
+ * BLINK_ON blinking, cursor is shown
+ */
+
+#define BLINK_NONE 0
+#define BLINK_OFF 1
+#define BLINK_ON 2
+
+static int blink_state = BLINK_NONE;
+static long_u blink_waittime = 700;
+static long_u blink_ontime = 400;
+static long_u blink_offtime = 250;
+static UINT blink_timer = 0;
+
+ void
+gui_mch_set_blinking(long wait, long on, long off)
+{
+ blink_waittime = wait;
+ blink_ontime = on;
+ blink_offtime = off;
+}
+
+/* ARGSUSED */
+ static VOID CALLBACK
+_OnBlinkTimer(
+ HWND hwnd,
+ UINT uMsg,
+ UINT idEvent,
+ DWORD dwTime)
+{
+ MSG msg;
+
+ /*
+ TRACE2("Got timer event, id %d, blink_timer %d\n", idEvent, blink_timer);
+ */
+
+ KillTimer(NULL, idEvent);
+
+ /* Eat spurious WM_TIMER messages */
+ while (pPeekMessage(&msg, hwnd, WM_TIMER, WM_TIMER, PM_REMOVE))
+ ;
+
+ if (blink_state == BLINK_ON)
+ {
+ gui_undraw_cursor();
+ blink_state = BLINK_OFF;
+ blink_timer = (UINT) SetTimer(NULL, 0, (UINT)blink_offtime,
+ (TIMERPROC)_OnBlinkTimer);
+ }
+ else
+ {
+ gui_update_cursor(TRUE, FALSE);
+ blink_state = BLINK_ON;
+ blink_timer = (UINT) SetTimer(NULL, 0, (UINT)blink_ontime,
+ (TIMERPROC)_OnBlinkTimer);
+ }
+}
+
+ static void
+gui_mswin_rm_blink_timer(void)
+{
+ MSG msg;
+
+ if (blink_timer != 0)
+ {
+ KillTimer(NULL, blink_timer);
+ /* Eat spurious WM_TIMER messages */
+ while (pPeekMessage(&msg, s_hwnd, WM_TIMER, WM_TIMER, PM_REMOVE))
+ ;
+ blink_timer = 0;
+ }
+}
+
+/*
+ * Stop the cursor blinking. Show the cursor if it wasn't shown.
+ */
+ void
+gui_mch_stop_blink(void)
+{
+ gui_mswin_rm_blink_timer();
+ if (blink_state == BLINK_OFF)
+ gui_update_cursor(TRUE, FALSE);
+ blink_state = BLINK_NONE;
+}
+
+/*
+ * Start the cursor blinking. If it was already blinking, this restarts the
+ * waiting time and shows the cursor.
+ */
+ void
+gui_mch_start_blink(void)
+{
+ gui_mswin_rm_blink_timer();
+
+ /* Only switch blinking on if none of the times is zero */
+ if (blink_waittime && blink_ontime && blink_offtime && gui.in_focus)
+ {
+ blink_timer = (UINT)SetTimer(NULL, 0, (UINT)blink_waittime,
+ (TIMERPROC)_OnBlinkTimer);
+ blink_state = BLINK_ON;
+ gui_update_cursor(TRUE, FALSE);
+ }
+}
+
+/*
+ * Call-back routines.
+ */
+
+/*ARGSUSED*/
+ static VOID CALLBACK
+_OnTimer(
+ HWND hwnd,
+ UINT uMsg,
+ UINT idEvent,
+ DWORD dwTime)
+{
+ MSG msg;
+
+ /*
+ TRACE2("Got timer event, id %d, s_wait_timer %d\n", idEvent, s_wait_timer);
+ */
+ KillTimer(NULL, idEvent);
+ s_timed_out = TRUE;
+
+ /* Eat spurious WM_TIMER messages */
+ while (pPeekMessage(&msg, hwnd, WM_TIMER, WM_TIMER, PM_REMOVE))
+ ;
+ if (idEvent == s_wait_timer)
+ s_wait_timer = 0;
+}
+
+/*ARGSUSED*/
+ static void
+_OnDeadChar(
+ HWND hwnd,
+ UINT ch,
+ int cRepeat)
+{
+ dead_key = 1;
+}
+
/*
- * Include the common stuff for MS-Windows GUI.
+ * Convert Unicode character "ch" to bytes in "string[slen]".
+ * When "had_alt" is TRUE the ALT key was included in "ch".
+ * Return the length.
*/
-#include "gui_w48.c"
+ static int
+char_to_string(int ch, char_u *string, int slen, int had_alt)
+{
+ int len;
+ int i;
+#ifdef FEAT_MBYTE
+ WCHAR wstring[2];
+ char_u *ws = NULL;;
+
+ if (os_version.dwPlatformId != VER_PLATFORM_WIN32_NT)
+ {
+ /* On Windows 95/98 we apparently get the character in the active
+ * codepage, not in UCS-2. If conversion is needed convert it to
+ * UCS-2 first. */
+ if ((int)GetACP() == enc_codepage)
+ len = 0; /* no conversion required */
+ else
+ {
+ string[0] = ch;
+ len = MultiByteToWideChar(GetACP(), 0, (LPCSTR)string,
+ 1, wstring, 2);
+ }
+ }
+ else
+ {
+ wstring[0] = ch;
+ len = 1;
+ }
+
+ if (len > 0)
+ {
+ /* "ch" is a UTF-16 character. Convert it to a string of bytes. When
+ * "enc_codepage" is non-zero use the standard Win32 function,
+ * otherwise use our own conversion function (e.g., for UTF-8). */
+ if (enc_codepage > 0)
+ {
+ len = WideCharToMultiByte(enc_codepage, 0, wstring, len,
+ (LPSTR)string, slen, 0, NULL);
+ /* If we had included the ALT key into the character but now the
+ * upper bit is no longer set, that probably means the conversion
+ * failed. Convert the original character and set the upper bit
+ * afterwards. */
+ if (had_alt && len == 1 && ch >= 0x80 && string[0] < 0x80)
+ {
+ wstring[0] = ch & 0x7f;
+ len = WideCharToMultiByte(enc_codepage, 0, wstring, len,
+ (LPSTR)string, slen, 0, NULL);
+ if (len == 1) /* safety check */
+ string[0] |= 0x80;
+ }
+ }
+ else
+ {
+ len = 1;
+ ws = utf16_to_enc(wstring, &len);
+ if (ws == NULL)
+ len = 0;
+ else
+ {
+ if (len > slen) /* just in case */
+ len = slen;
+ mch_memmove(string, ws, len);
+ vim_free(ws);
+ }
+ }
+ }
+
+ if (len == 0)
+#endif
+ {
+ string[0] = ch;
+ len = 1;
+ }
+
+ for (i = 0; i < len; ++i)
+ if (string[i] == CSI && len <= slen - 2)
+ {
+ /* Insert CSI as K_CSI. */
+ mch_memmove(string + i + 3, string + i + 1, len - i - 1);
+ string[++i] = KS_EXTRA;
+ string[++i] = (int)KE_CSI;
+ len += 2;
+ }
+
+ return len;
+}
+
+/*
+ * Key hit, add it to the input buffer.
+ */
+/*ARGSUSED*/
+ static void
+_OnChar(
+ HWND hwnd,
+ UINT ch,
+ int cRepeat)
+{
+ char_u string[40];
+ int len = 0;
+
+ dead_key = 0;
+
+ len = char_to_string(ch, string, 40, FALSE);
+ if (len == 1 && string[0] == Ctrl_C && ctrl_c_interrupts)
+ {
+ trash_input_buf();
+ got_int = TRUE;
+ }
+
+ add_to_input_buf(string, len);
+}
+
+/*
+ * Alt-Key hit, add it to the input buffer.
+ */
+/*ARGSUSED*/
+ static void
+_OnSysChar(
+ HWND hwnd,
+ UINT cch,
+ int cRepeat)
+{
+ char_u string[40]; /* Enough for multibyte character */
+ int len;
+ int modifiers;
+ int ch = cch; /* special keys are negative */
+
+ dead_key = 0;
+
+ /* TRACE("OnSysChar(%d, %c)\n", ch, ch); */
+
+ /* OK, we have a character key (given by ch) which was entered with the
+ * ALT key pressed. Eg, if the user presses Alt-A, then ch == 'A'. Note
+ * that the system distinguishes Alt-a and Alt-A (Alt-Shift-a unless
+ * CAPSLOCK is pressed) at this point.
+ */
+ modifiers = MOD_MASK_ALT;
+ if (GetKeyState(VK_SHIFT) & 0x8000)
+ modifiers |= MOD_MASK_SHIFT;
+ if (GetKeyState(VK_CONTROL) & 0x8000)
+ modifiers |= MOD_MASK_CTRL;
+
+ ch = simplify_key(ch, &modifiers);
+ /* remove the SHIFT modifier for keys where it's already included, e.g.,
+ * '(' and '*' */
+ if (ch < 0x100 && !isalpha(ch) && isprint(ch))
+ modifiers &= ~MOD_MASK_SHIFT;
+
+ /* Interpret the ALT key as making the key META, include SHIFT, etc. */
+ ch = extract_modifiers(ch, &modifiers);
+ if (ch == CSI)
+ ch = K_CSI;
+
+ len = 0;
+ if (modifiers)
+ {
+ string[len++] = CSI;
+ string[len++] = KS_MODIFIER;
+ string[len++] = modifiers;
+ }
+
+ if (IS_SPECIAL((int)ch))
+ {
+ string[len++] = CSI;
+ string[len++] = K_SECOND((int)ch);
+ string[len++] = K_THIRD((int)ch);
+ }
+ else
+ {
+ /* Although the documentation isn't clear about it, we assume "ch" is
+ * a Unicode character. */
+ len += char_to_string(ch, string + len, 40 - len, TRUE);
+ }
+
+ add_to_input_buf(string, len);
+}
+
+ static void
+_OnMouseEvent(
+ int button,
+ int x,
+ int y,
+ int repeated_click,
+ UINT keyFlags)
+{
+ int vim_modifiers = 0x0;
+
+ s_getting_focus = FALSE;
+
+ if (keyFlags & MK_SHIFT)
+ vim_modifiers |= MOUSE_SHIFT;
+ if (keyFlags & MK_CONTROL)
+ vim_modifiers |= MOUSE_CTRL;
+ if (GetKeyState(VK_MENU) & 0x8000)
+ vim_modifiers |= MOUSE_ALT;
+
+ gui_send_mouse_event(button, x, y, repeated_click, vim_modifiers);
+}
+
+/*ARGSUSED*/
+ static void
+_OnMouseButtonDown(
+ HWND hwnd,
+ BOOL fDoubleClick,
+ int x,
+ int y,
+ UINT keyFlags)
+{
+ static LONG s_prevTime = 0;
+
+ LONG currentTime = GetMessageTime();
+ int button = -1;
+ int repeated_click;
+
+ /* Give main window the focus: this is so the cursor isn't hollow. */
+ (void)SetFocus(s_hwnd);
+
+ if (s_uMsg == WM_LBUTTONDOWN || s_uMsg == WM_LBUTTONDBLCLK)
+ button = MOUSE_LEFT;
+ else if (s_uMsg == WM_MBUTTONDOWN || s_uMsg == WM_MBUTTONDBLCLK)
+ button = MOUSE_MIDDLE;
+ else if (s_uMsg == WM_RBUTTONDOWN || s_uMsg == WM_RBUTTONDBLCLK)
+ button = MOUSE_RIGHT;
+ else if (s_uMsg == WM_XBUTTONDOWN || s_uMsg == WM_XBUTTONDBLCLK)
+ {
+#ifndef GET_XBUTTON_WPARAM
+# define GET_XBUTTON_WPARAM(wParam) (HIWORD(wParam))
+#endif
+ button = ((GET_XBUTTON_WPARAM(s_wParam) == 1) ? MOUSE_X1 : MOUSE_X2);
+ }
+ else if (s_uMsg == WM_CAPTURECHANGED)
+ {
+ /* on W95/NT4, somehow you get in here with an odd Msg
+ * if you press one button while holding down the other..*/
+ if (s_button_pending == MOUSE_LEFT)
+ button = MOUSE_RIGHT;
+ else
+ button = MOUSE_LEFT;
+ }
+ if (button >= 0)
+ {
+ repeated_click = ((int)(currentTime - s_prevTime) < p_mouset);
+
+ /*
+ * Holding down the left and right buttons simulates pushing the middle
+ * button.
+ */
+ if (repeated_click
+ && ((button == MOUSE_LEFT && s_button_pending == MOUSE_RIGHT)
+ || (button == MOUSE_RIGHT
+ && s_button_pending == MOUSE_LEFT)))
+ {
+ /*
+ * Hmm, gui.c will ignore more than one button down at a time, so
+ * pretend we let go of it first.
+ */
+ gui_send_mouse_event(MOUSE_RELEASE, x, y, FALSE, 0x0);
+ button = MOUSE_MIDDLE;
+ repeated_click = FALSE;
+ s_button_pending = -1;
+ _OnMouseEvent(button, x, y, repeated_click, keyFlags);
+ }
+ else if ((repeated_click)
+ || (mouse_model_popup() && (button == MOUSE_RIGHT)))
+ {
+ if (s_button_pending > -1)
+ {
+ _OnMouseEvent(s_button_pending, x, y, FALSE, keyFlags);
+ s_button_pending = -1;
+ }
+ /* TRACE("Button down at x %d, y %d\n", x, y); */
+ _OnMouseEvent(button, x, y, repeated_click, keyFlags);
+ }
+ else
+ {
+ /*
+ * If this is the first press (i.e. not a multiple click) don't
+ * action immediately, but store and wait for:
+ * i) button-up
+ * ii) mouse move
+ * iii) another button press
+ * before using it.
+ * This enables us to make left+right simulate middle button,
+ * without left or right being actioned first. The side-effect is
+ * that if you click and hold the mouse without dragging, the
+ * cursor doesn't move until you release the button. In practice
+ * this is hardly a problem.
+ */
+ s_button_pending = button;
+ s_x_pending = x;
+ s_y_pending = y;
+ s_kFlags_pending = keyFlags;
+ }
+
+ s_prevTime = currentTime;
+ }
+}
+
+/*ARGSUSED*/
+ static void
+_OnMouseMoveOrRelease(
+ HWND hwnd,
+ int x,
+ int y,
+ UINT keyFlags)
+{
+ int button;
+
+ s_getting_focus = FALSE;
+ if (s_button_pending > -1)
+ {
+ /* Delayed action for mouse down event */
+ _OnMouseEvent(s_button_pending, s_x_pending,
+ s_y_pending, FALSE, s_kFlags_pending);
+ s_button_pending = -1;
+ }
+ if (s_uMsg == WM_MOUSEMOVE)
+ {
+ /*
+ * It's only a MOUSE_DRAG if one or more mouse buttons are being held
+ * down.
+ */
+ if (!(keyFlags & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON
+ | MK_XBUTTON1 | MK_XBUTTON2)))
+ {
+ gui_mouse_moved(x, y);
+ return;
+ }
+
+ /*
+ * While button is down, keep grabbing mouse move events when
+ * the mouse goes outside the window
+ */
+ SetCapture(s_textArea);
+ button = MOUSE_DRAG;
+ /* TRACE(" move at x %d, y %d\n", x, y); */
+ }
+ else
+ {
+ ReleaseCapture();
+ button = MOUSE_RELEASE;
+ /* TRACE(" up at x %d, y %d\n", x, y); */
+ }
+
+ _OnMouseEvent(button, x, y, FALSE, keyFlags);
+}
+
+#ifdef FEAT_MENU
+/*
+ * Find the vimmenu_T with the given id
+ */
+ static vimmenu_T *
+gui_mswin_find_menu(
+ vimmenu_T *pMenu,
+ int id)
+{
+ vimmenu_T *pChildMenu;
+
+ while (pMenu)
+ {
+ if (pMenu->id == (UINT)id)
+ break;
+ if (pMenu->children != NULL)
+ {
+ pChildMenu = gui_mswin_find_menu(pMenu->children, id);
+ if (pChildMenu)
+ {
+ pMenu = pChildMenu;
+ break;
+ }
+ }
+ pMenu = pMenu->next;
+ }
+ return pMenu;
+}
+
+/*ARGSUSED*/
+ static void
+_OnMenu(
+ HWND hwnd,
+ int id,
+ HWND hwndCtl,
+ UINT codeNotify)
+{
+ vimmenu_T *pMenu;
+
+ pMenu = gui_mswin_find_menu(root_menu, id);
+ if (pMenu)
+ gui_menu_cb(pMenu);
+}
+#endif
+
+#ifdef MSWIN_FIND_REPLACE
+# if defined(FEAT_MBYTE) && defined(WIN3264)
+/*
+ * copy useful data from structure LPFINDREPLACE to structure LPFINDREPLACEW
+ */
+ static void
+findrep_atow(LPFINDREPLACEW lpfrw, LPFINDREPLACE lpfr)
+{
+ WCHAR *wp;
+
+ lpfrw->hwndOwner = lpfr->hwndOwner;
+ lpfrw->Flags = lpfr->Flags;
+
+ wp = enc_to_utf16((char_u *)lpfr->lpstrFindWhat, NULL);
+ wcsncpy(lpfrw->lpstrFindWhat, wp, lpfrw->wFindWhatLen - 1);
+ vim_free(wp);
+
+ /* the field "lpstrReplaceWith" doesn't need to be copied */
+}
+
+/*
+ * copy useful data from structure LPFINDREPLACEW to structure LPFINDREPLACE
+ */
+ static void
+findrep_wtoa(LPFINDREPLACE lpfr, LPFINDREPLACEW lpfrw)
+{
+ char_u *p;
+
+ lpfr->Flags = lpfrw->Flags;
+
+ p = utf16_to_enc((short_u*)lpfrw->lpstrFindWhat, NULL);
+ vim_strncpy((char_u *)lpfr->lpstrFindWhat, p, lpfr->wFindWhatLen - 1);
+ vim_free(p);
+
+ p = utf16_to_enc((short_u*)lpfrw->lpstrReplaceWith, NULL);
+ vim_strncpy((char_u *)lpfr->lpstrReplaceWith, p, lpfr->wReplaceWithLen - 1);
+ vim_free(p);
+}
+# endif
+
+/*
+ * Handle a Find/Replace window message.
+ */
+ static void
+_OnFindRepl(void)
+{
+ int flags = 0;
+ int down;
+
+# if defined(FEAT_MBYTE) && defined(WIN3264)
+ /* If the OS is Windows NT, and 'encoding' differs from active codepage:
+ * convert text from wide string. */
+ if (os_version.dwPlatformId == VER_PLATFORM_WIN32_NT
+ && enc_codepage >= 0 && (int)GetACP() != enc_codepage)
+ {
+ findrep_wtoa(&s_findrep_struct, &s_findrep_struct_w);
+ }
+# endif
+
+ if (s_findrep_struct.Flags & FR_DIALOGTERM)
+ /* Give main window the focus back. */
+ (void)SetFocus(s_hwnd);
+
+ if (s_findrep_struct.Flags & FR_FINDNEXT)
+ {
+ flags = FRD_FINDNEXT;
+
+ /* Give main window the focus back: this is so the cursor isn't
+ * hollow. */
+ (void)SetFocus(s_hwnd);
+ }
+ else if (s_findrep_struct.Flags & FR_REPLACE)
+ {
+ flags = FRD_REPLACE;
+
+ /* Give main window the focus back: this is so the cursor isn't
+ * hollow. */
+ (void)SetFocus(s_hwnd);
+ }
+ else if (s_findrep_struct.Flags & FR_REPLACEALL)
+ {
+ flags = FRD_REPLACEALL;
+ }
+
+ if (flags != 0)
+ {
+ /* Call the generic GUI function to do the actual work. */
+ if (s_findrep_struct.Flags & FR_WHOLEWORD)
+ flags |= FRD_WHOLE_WORD;
+ if (s_findrep_struct.Flags & FR_MATCHCASE)
+ flags |= FRD_MATCH_CASE;
+ down = (s_findrep_struct.Flags & FR_DOWN) != 0;
+ gui_do_findrepl(flags, (char_u *)s_findrep_struct.lpstrFindWhat,
+ (char_u *)s_findrep_struct.lpstrReplaceWith, down);
+ }
+}
+#endif
+
+ static void
+HandleMouseHide(UINT uMsg, LPARAM lParam)
+{
+ static LPARAM last_lParam = 0L;
+
+ /* We sometimes get a mousemove when the mouse didn't move... */
+ if (uMsg == WM_MOUSEMOVE || uMsg == WM_NCMOUSEMOVE)
+ {
+ if (lParam == last_lParam)
+ return;
+ last_lParam = lParam;
+ }
+
+ /* Handle specially, to centralise coding. We need to be sure we catch all
+ * possible events which should cause us to restore the cursor (as it is a
+ * shared resource, we take full responsibility for it).
+ */
+ switch (uMsg)
+ {
+ case WM_KEYUP:
+ case WM_CHAR:
+ /*
+ * blank out the pointer if necessary
+ */
+ if (p_mh)
+ gui_mch_mousehide(TRUE);
+ break;
+
+ case WM_SYSKEYUP: /* show the pointer when a system-key is pressed */
+ case WM_SYSCHAR:
+ case WM_MOUSEMOVE: /* show the pointer on any mouse action */
+ case WM_LBUTTONDOWN:
+ case WM_LBUTTONUP:
+ case WM_MBUTTONDOWN:
+ case WM_MBUTTONUP:
+ case WM_RBUTTONDOWN:
+ case WM_RBUTTONUP:
+ case WM_XBUTTONDOWN:
+ case WM_XBUTTONUP:
+ case WM_NCMOUSEMOVE:
+ case WM_NCLBUTTONDOWN:
+ case WM_NCLBUTTONUP:
+ case WM_NCMBUTTONDOWN:
+ case WM_NCMBUTTONUP:
+ case WM_NCRBUTTONDOWN:
+ case WM_NCRBUTTONUP:
+ case WM_KILLFOCUS:
+ /*
+ * if the pointer is currently hidden, then we should show it.
+ */
+ gui_mch_mousehide(FALSE);
+ break;
+ }
+}
+
+ static LRESULT CALLBACK
+_TextAreaWndProc(
+ HWND hwnd,
+ UINT uMsg,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ /*
+ TRACE("TextAreaWndProc: hwnd = %08x, msg = %x, wParam = %x, lParam = %x\n",
+ hwnd, uMsg, wParam, lParam);
+ */
+
+ HandleMouseHide(uMsg, lParam);
+
+ s_uMsg = uMsg;
+ s_wParam = wParam;
+ s_lParam = lParam;
+
+#ifdef FEAT_BEVAL
+ TrackUserActivity(uMsg);
+#endif
+
+ switch (uMsg)
+ {
+ HANDLE_MSG(hwnd, WM_LBUTTONDBLCLK,_OnMouseButtonDown);
+ HANDLE_MSG(hwnd, WM_LBUTTONDOWN,_OnMouseButtonDown);
+ HANDLE_MSG(hwnd, WM_LBUTTONUP, _OnMouseMoveOrRelease);
+ HANDLE_MSG(hwnd, WM_MBUTTONDBLCLK,_OnMouseButtonDown);
+ HANDLE_MSG(hwnd, WM_MBUTTONDOWN,_OnMouseButtonDown);
+ HANDLE_MSG(hwnd, WM_MBUTTONUP, _OnMouseMoveOrRelease);
+ HANDLE_MSG(hwnd, WM_MOUSEMOVE, _OnMouseMoveOrRelease);
+ HANDLE_MSG(hwnd, WM_PAINT, _OnPaint);
+ HANDLE_MSG(hwnd, WM_RBUTTONDBLCLK,_OnMouseButtonDown);
+ HANDLE_MSG(hwnd, WM_RBUTTONDOWN,_OnMouseButtonDown);
+ HANDLE_MSG(hwnd, WM_RBUTTONUP, _OnMouseMoveOrRelease);
+ HANDLE_MSG(hwnd, WM_XBUTTONDBLCLK,_OnMouseButtonDown);
+ HANDLE_MSG(hwnd, WM_XBUTTONDOWN,_OnMouseButtonDown);
+ HANDLE_MSG(hwnd, WM_XBUTTONUP, _OnMouseMoveOrRelease);
+
+#ifdef FEAT_BEVAL
+ case WM_NOTIFY: Handle_WM_Notify(hwnd, (LPNMHDR)lParam);
+ return TRUE;
+#endif
+ default:
+ return MyWindowProc(hwnd, uMsg, wParam, lParam);
+ }
+}
+
+#if (defined(WIN3264) && defined(FEAT_MBYTE)) \
+ || defined(GLOBAL_IME) \
+ || defined(PROTO)
+# ifdef PROTO
+typedef int WINAPI;
+# endif
+
+ LRESULT WINAPI
+vim_WindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+# ifdef GLOBAL_IME
+ return global_ime_DefWindowProc(hwnd, message, wParam, lParam);
+# else
+ if (wide_WindowProc)
+ return DefWindowProcW(hwnd, message, wParam, lParam);
+ return DefWindowProc(hwnd, message, wParam, lParam);
+#endif
+}
+#endif
+
+/*
+ * Called when the foreground or background color has been changed.
+ */
+ void
+gui_mch_new_colors(void)
+{
+ /* nothing to do? */
+}
+
+/*
+ * Set the colors to their default values.
+ */
+ void
+gui_mch_def_colors(void)
+{
+ gui.norm_pixel = GetSysColor(COLOR_WINDOWTEXT);
+ gui.back_pixel = GetSysColor(COLOR_WINDOW);
+ gui.def_norm_pixel = gui.norm_pixel;
+ gui.def_back_pixel = gui.back_pixel;
+}
+
+/*
+ * Open the GUI window which was created by a call to gui_mch_init().
+ */
+ int
+gui_mch_open(void)
+{
+#ifndef SW_SHOWDEFAULT
+# define SW_SHOWDEFAULT 10 /* Borland 5.0 doesn't have it */
+#endif
+ /* Actually open the window, if not already visible
+ * (may be done already in gui_mch_set_shellsize) */
+ if (!IsWindowVisible(s_hwnd))
+ ShowWindow(s_hwnd, SW_SHOWDEFAULT);
+
+#ifdef MSWIN_FIND_REPLACE
+ /* Init replace string here, so that we keep it when re-opening the
+ * dialog. */
+ s_findrep_struct.lpstrReplaceWith[0] = NUL;
+#endif
+
+ return OK;
+}
+
+/*
+ * Get the position of the top left corner of the window.
+ */
+ int
+gui_mch_get_winpos(int *x, int *y)
+{
+ RECT rect;
+
+ GetWindowRect(s_hwnd, &rect);
+ *x = rect.left;
+ *y = rect.top;
+ return OK;
+}
+
+/*
+ * Set the position of the top left corner of the window to the given
+ * coordinates.
+ */
+ void
+gui_mch_set_winpos(int x, int y)
+{
+ SetWindowPos(s_hwnd, NULL, x, y, 0, 0,
+ SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
+}
+ void
+gui_mch_set_text_area_pos(int x, int y, int w, int h)
+{
+ static int oldx = 0;
+ static int oldy = 0;
+
+ SetWindowPos(s_textArea, NULL, x, y, w, h, SWP_NOZORDER | SWP_NOACTIVATE);
+
+#ifdef FEAT_TOOLBAR
+ if (vim_strchr(p_go, GO_TOOLBAR) != NULL)
+ SendMessage(s_toolbarhwnd, WM_SIZE,
+ (WPARAM)0, (LPARAM)(w + ((long)(TOOLBAR_BUTTON_HEIGHT+8)<<16)));
+#endif
+#if defined(FEAT_GUI_TABLINE)
+ if (showing_tabline)
+ {
+ int top = 0;
+ RECT rect;
+
+# ifdef FEAT_TOOLBAR
+ if (vim_strchr(p_go, GO_TOOLBAR) != NULL)
+ top = TOOLBAR_BUTTON_HEIGHT + TOOLBAR_BORDER_HEIGHT;
+# endif
+ GetClientRect(s_hwnd, &rect);
+ MoveWindow(s_tabhwnd, 0, top, rect.right, gui.tabline_height, TRUE);
+ }
+#endif
+
+ /* When side scroll bar is unshown, the size of window will change.
+ * then, the text area move left or right. thus client rect should be
+ * forcedly redrawn. (Yasuhiro Matsumoto) */
+ if (oldx != x || oldy != y)
+ {
+ InvalidateRect(s_hwnd, NULL, FALSE);
+ oldx = x;
+ oldy = y;
+ }
+}
+
+
+/*
+ * Scrollbar stuff:
+ */
+
+ void
+gui_mch_enable_scrollbar(
+ scrollbar_T *sb,
+ int flag)
+{
+ ShowScrollBar(sb->id, SB_CTL, flag);
+
+ /* TODO: When the window is maximized, the size of the window stays the
+ * same, thus the size of the text area changes. On Win98 it's OK, on Win
+ * NT 4.0 it's not... */
+}
+
+ void
+gui_mch