summaryrefslogtreecommitdiffstats
path: root/src/gui_w48.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui_w48.c')
-rw-r--r--src/gui_w48.c3402
1 files changed, 3402 insertions, 0 deletions
diff --git a/src/gui_w48.c b/src/gui_w48.c
new file mode 100644
index 0000000000..761c1af167
--- /dev/null
+++ b/src/gui_w48.c
@@ -0,0 +1,3402 @@
+/* vi:set ts=8 sts=4 sw=4:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ * GUI support by Robert Webb
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ * See README.txt for an overview of the Vim source code.
+ */
+/*
+ * gui_w48.c: This file is included in gui_w16.c and gui_w32.c.
+ *
+ * GUI support for Microsoft Windows (Win16 + Win32 = Win48 :-)
+ *
+ * The combined efforts of:
+ * George V. Reilly <george@reilly.org>
+ * Robert Webb
+ * Vince Negri
+ * ...and contributions from many others
+ *
+ */
+
+#include "vim.h"
+#include "version.h" /* used by dialog box routine for default title */
+#ifdef DEBUG
+# include <tchar.h>
+#endif
+#ifndef __MINGW32__
+# include <shellapi.h>
+#endif
+#if defined(FEAT_TOOLBAR) || defined(FEAT_BEVAL)
+# include <commctrl.h>
+#endif
+#ifdef WIN16
+# include <commdlg.h>
+# include <shellapi.h>
+# ifdef WIN16_3DLOOK
+# include <ctl3d.h>
+# endif
+#endif
+#include <windowsx.h>
+
+#ifdef GLOBAL_IME
+# include "glbl_ime.h"
+#endif
+
+#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_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;
+# 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;
+#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 int gui_mswin_get_menu_height(int fix_window);
+
+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 interupted 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;
+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;
+#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;
+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 __ARGS((HWND hwnd, LPNMHDR pnmh));
+static void TrackUserActivity __ARGS((UINT uMsg));
+#endif
+
+/*
+ * For control IME.
+ */
+#ifdef FEAT_MBYTE
+# ifdef USE_IM_CONTROL
+static LOGFONT norm_logfont;
+# endif
+#endif
+
+#ifdef FEAT_MBYTE_IME
+static LRESULT _OnImeNotify(HWND hWnd, DWORD dwCommand, DWORD dwData);
+#endif
+
+#ifdef DEBUG
+/*
+ * 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 /* DEBUG */
+
+/*
+ * 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 (PeekMessage(&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 (PeekMessage(&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.
+ */
+
+ 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 (PeekMessage(&msg, hwnd, WM_TIMER, WM_TIMER, PM_REMOVE))
+ ;
+ if (idEvent == s_wait_timer)
+ s_wait_timer = 0;
+}
+
+ static void
+_OnDeadChar(
+ HWND hwnd,
+ UINT ch,
+ int cRepeat)
+{
+ dead_key = 1;
+}
+
+/*
+ * Convert Unicode character "ch" to bytes in "string[slen]".
+ * Return the length.
+ */
+ static int
+char_to_string(int ch, char_u *string, int slen)
+{
+ int len;
+ int i;
+#ifdef FEAT_MBYTE
+ WCHAR wstring[2];
+ char_u *ws = NULL;;
+
+ /* "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). */
+ wstring[0] = ch;
+ if (enc_codepage > 0)
+ len = WideCharToMultiByte(enc_codepage, 0, wstring, 1, string, slen,
+ 0, NULL);
+ else
+ {
+ len = 1;
+ ws = ucs2_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.
+ */
+ static void
+_OnChar(
+ HWND hwnd,
+ UINT ch,
+ int cRepeat)
+{
+ char_u string[40];
+ int len = 0;
+
+ len = char_to_string(ch, string, 40);
+ 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.
+ */
+ 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 */
+
+ /* 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);
+ }
+
+ add_to_input_buf(string, len);
+}
+
+ static void
+_OnMouseEvent(
+ int button,
+ int x,
+ int y,
+ int repeated_click,
+ UINT keyFlags)
+{
+ int vim_modifiers = 0x0;
+
+ 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);
+}
+
+ 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;
+#ifndef WIN16 /*<VN>*/
+ 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;
+ }
+#endif
+ 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;
+ }
+}
+
+ static void
+_OnMouseMoveOrRelease(
+ HWND hwnd,
+ int x,
+ int y,
+ UINT keyFlags)
+{
+ int button;
+
+ 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;
+}
+
+ 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
+/*
+ * Handle a Find/Replace window message.
+ */
+ static void
+_OnFindRepl(void)
+{
+ int flags = 0;
+ int down;
+
+ 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, s_findrep_struct.lpstrFindWhat,
+ 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)
+ {
+ 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);
+#ifndef WIN16 /*<VN>*/
+ HANDLE_MSG(hwnd, WM_XBUTTONDBLCLK,_OnMouseButtonDown);
+ HANDLE_MSG(hwnd, WM_XBUTTONDOWN,_OnMouseButtonDown);
+ HANDLE_MSG(hwnd, WM_XBUTTONUP, _OnMouseMoveOrRelease);
+#endif
+
+#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()
+{
+ 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);
+
+ 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
+ /* 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
+ * forcely redraw. (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_set_scrollbar_pos(
+ scrollbar_T *sb,
+ int x,
+ int y,
+ int w,
+ int h)
+{
+ SetWindowPos(sb->id, NULL, x, y, w, h, SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW);
+}
+
+ void
+gui_mch_create_scrollbar(
+ scrollbar_T *sb,
+ int orient) /* SBAR_VERT or SBAR_HORIZ */
+{
+ sb->id = CreateWindow(
+ "SCROLLBAR", "Scrollbar",
+ WS_CHILD | ((orient == SBAR_VERT) ? SBS_VERT : SBS_HORZ), 0, 0,
+ 10, /* Any value will do for now */
+ 10, /* Any value will do for now */
+ s_hwnd, NULL,
+ s_hinst, NULL);
+}
+
+/*
+ * Find the scrollbar with the given hwnd.
+ */
+ static scrollbar_T *
+gui_mswin_find_scrollbar(HWND hwnd)
+{
+ win_T *wp;
+
+ if (gui.bottom_sbar.id == hwnd)
+ return &gui.bottom_sbar;
+ FOR_ALL_WINDOWS(wp)
+ {
+ if (wp->w_scrollbars[SBAR_LEFT].id == hwnd)
+ return &wp->w_scrollbars[SBAR_LEFT];
+ if (wp->w_scrollbars[SBAR_RIGHT].id == hwnd)
+ return &wp->w_scrollbars[SBAR_RIGHT];
+ }
+ return NULL;
+}
+
+/*
+ * Get the character size of a font.
+ */
+ static void
+GetFontSize(GuiFont font)
+{
+ HWND hwnd = GetDesktopWindow();
+ HDC hdc = GetWindowDC(hwnd);
+ HFONT hfntOld = SelectFont(hdc, (HFONT)font);
+ TEXTMETRIC tm;
+
+ GetTextMetrics(hdc, &tm);
+ gui.char_width = tm.tmAveCharWidth + tm.tmOverhang;
+
+ gui.char_height = tm.tmHeight
+#ifndef MSWIN16_FASTTEXT
+ + p_linespace
+#endif
+ ;
+
+ SelectFont(hdc, hfntOld);
+
+ ReleaseDC(hwnd, hdc);
+}
+
+ int
+gui_mch_adjust_charsize(void)
+{
+ GetFontSize(gui.norm_font);
+ return OK;
+}
+
+ static GuiFont
+get_font_handle(LOGFONT *lf)
+{
+ HFONT font = NULL;
+
+ /* Load the font */
+ font = CreateFontIndirect(lf);
+
+ if (font == NULL)
+ return NOFONT;
+
+ return (GuiFont)font;
+}
+
+ static int
+pixels_to_points(int pixels, int vertical)
+{
+ int points;
+ HWND hwnd;
+ HDC hdc;
+
+ hwnd = GetDesktopWindow();
+ hdc = GetWindowDC(hwnd);
+
+ points = MulDiv(pixels, 72,
+ GetDeviceCaps(hdc, vertical ? LOGPIXELSY : LOGPIXELSX));
+
+ ReleaseDC(hwnd, hdc);
+
+ return points;
+}
+
+ GuiFont
+gui_mch_get_font(
+ char_u *name,
+ int giveErrorIfMissing)
+{
+ LOGFONT lf;
+ GuiFont font;
+
+ get_logfont(&lf, name, NULL);
+ font = get_font_handle(&lf);
+ if (font == NOFONT && giveErrorIfMissing)
+ EMSG2(_(e_font), name);
+ return font;
+}
+ void
+gui_mch_free_font(GuiFont font)
+{
+ if (font)
+ DeleteObject((HFONT)font);
+}
+
+ static int
+hex_digit(int c)
+{
+ if (VIM_ISDIGIT(c))
+ return c - '0';
+ c = TOLOWER_ASC(c);
+ if (c >= 'a' && c <= 'f')
+ return c - 'a' + 10;
+ return -1000;
+}
+/*
+ * Return the Pixel value (color) for the given color name.
+ * Return INVALCOLOR for error.
+ */
+ guicolor_T
+gui_mch_get_color(char_u *name)
+{
+ typedef struct guicolor_tTable
+ {
+ char *name;
+ COLORREF color;
+ } guicolor_tTable;
+
+ static guicolor_tTable table[] =
+ {
+ {"Black", RGB(0x00, 0x00, 0x00)},
+ {"DarkGray", RGB(0x80, 0x80, 0x80)},
+ {"DarkGrey", RGB(0x80, 0x80, 0x80)},
+ {"Gray", RGB(0xC0, 0xC0, 0xC0)},
+ {"Grey", RGB(0xC0, 0xC0, 0xC0)},
+ {"LightGray", RGB(0xE0, 0xE0, 0xE0)},
+ {"LightGrey", RGB(0xE0, 0xE0, 0xE0)},
+ {"White", RGB(0xFF, 0xFF, 0xFF)},
+ {"DarkRed", RGB(0x80, 0x00, 0x00)},
+ {"Red", RGB(0xFF, 0x00, 0x00)},
+ {"LightRed", RGB(0xFF, 0xA0, 0xA0)},
+ {"DarkBlue", RGB(0x00, 0x00, 0x80)},
+ {"Blue", RGB(0x00, 0x00, 0xFF)},
+ {"LightBlue", RGB(0xA0, 0xA0, 0xFF)},
+ {"DarkGreen", RGB(0x00, 0x80, 0x00)},
+ {"Green", RGB(0x00, 0xFF, 0x00)},
+ {"LightGreen", RGB(0xA0, 0xFF, 0xA0)},
+ {"DarkCyan", RGB(0x00, 0x80, 0x80)},
+ {"Cyan", RGB(0x00, 0xFF, 0xFF)},
+ {"LightCyan", RGB(0xA0, 0xFF, 0xFF)},
+ {"DarkMagenta", RGB(0x80, 0x00, 0x80)},
+ {"Magenta", RGB(0xFF, 0x00, 0xFF)},
+ {"LightMagenta", RGB(0xFF, 0xA0, 0xFF)},
+ {"Brown", RGB(0x80, 0x40, 0x40)},
+ {"Yellow", RGB(0xFF, 0xFF, 0x00)},
+ {"LightYellow", RGB(0xFF, 0xFF, 0xA0)},
+ {"DarkYellow", RGB(0xBB, 0xBB, 0x00)},
+ {"SeaGreen", RGB(0x2E, 0x8B, 0x57)},
+ {"Orange", RGB(0xFF, 0xA5, 0x00)},
+ {"Purple", RGB(0xA0, 0x20, 0xF0)},
+ {"SlateBlue", RGB(0x6A, 0x5A, 0xCD)},
+ {"Violet", RGB(0xEE, 0x82, 0xEE)},
+ };
+
+ typedef struct SysColorTable
+ {
+ char *name;