summaryrefslogtreecommitdiff
path: root/src/gui_w32.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui_w32.c')
-rw-r--r--src/gui_w32.c4077
1 files changed, 4077 insertions, 0 deletions
diff --git a/src/gui_w32.c b/src/gui_w32.c
new file mode 100644
index 000000000..df079538a
--- /dev/null
+++ b/src/gui_w32.c
@@ -0,0 +1,4077 @@
+/* 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.
+ */
+/*
+ * Windows GUI.
+ *
+ * GUI support for Microsoft Windows. Win32 initially; maybe Win16 later
+ *
+ * George V. Reilly <george@reilly.org> wrote the original Win32 GUI.
+ * Robert Webb reworked it to use the existing GUI stuff and added menu,
+ * scrollbars, etc.
+ *
+ * Note: Clipboard stuff, for cutting and pasting text to other windows, is in
+ * os_win32.c. (It can also be done from the terminal version).
+ *
+ * TODO: Some of the function signatures ought to be updated for Win64;
+ * e.g., replace LONG with LONG_PTR, etc.
+ */
+
+/*
+ * These are new in Windows ME/XP, only defined in recent compilers.
+ */
+#ifndef HANDLE_WM_XBUTTONUP
+# define HANDLE_WM_XBUTTONUP(hwnd, wParam, lParam, fn) \
+ ((fn)((hwnd), (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
+#endif
+#ifndef HANDLE_WM_XBUTTONDOWN
+# define HANDLE_WM_XBUTTONDOWN(hwnd, wParam, lParam, fn) \
+ ((fn)((hwnd), FALSE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
+#endif
+#ifndef HANDLE_WM_XBUTTONDBLCLK
+# define HANDLE_WM_XBUTTONDBLCLK(hwnd, wParam, lParam, fn) \
+ ((fn)((hwnd), TRUE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
+#endif
+
+/*
+ * Include the common stuff for MS-Windows GUI.
+ */
+#include "gui_w48.c"
+
+#ifdef FEAT_XPM_W32
+# include "xpm_w32.h"
+#endif
+
+#ifdef PROTO
+# define WINAPI
+#endif
+
+#ifdef __MINGW32__
+/*
+ * Add a lot of missing defines.
+ * They are not always missing, we need the #ifndef's.
+ */
+# ifndef _cdecl
+# define _cdecl
+# endif
+# ifndef IsMinimized
+# define IsMinimized(hwnd) IsIconic(hwnd)
+# endif
+# ifndef IsMaximized
+# define IsMaximized(hwnd) IsZoomed(hwnd)
+# endif
+# ifndef SelectFont
+# define SelectFont(hdc, hfont) ((HFONT)SelectObject((hdc), (HGDIOBJ)(HFONT)(hfont)))
+# endif
+# ifndef GetStockBrush
+# define GetStockBrush(i) ((HBRUSH)GetStockObject(i))
+# endif
+# ifndef DeleteBrush
+# define DeleteBrush(hbr) DeleteObject((HGDIOBJ)(HBRUSH)(hbr))
+# endif
+
+# ifndef HANDLE_WM_RBUTTONDBLCLK
+# define HANDLE_WM_RBUTTONDBLCLK(hwnd, wParam, lParam, fn) \
+ ((fn)((hwnd), TRUE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
+# endif
+# ifndef HANDLE_WM_MBUTTONUP
+# define HANDLE_WM_MBUTTONUP(hwnd, wParam, lParam, fn) \
+ ((fn)((hwnd), (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
+# endif
+# ifndef HANDLE_WM_MBUTTONDBLCLK
+# define HANDLE_WM_MBUTTONDBLCLK(hwnd, wParam, lParam, fn) \
+ ((fn)((hwnd), TRUE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
+# endif
+# ifndef HANDLE_WM_LBUTTONDBLCLK
+# define HANDLE_WM_LBUTTONDBLCLK(hwnd, wParam, lParam, fn) \
+ ((fn)((hwnd), TRUE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
+# endif
+# ifndef HANDLE_WM_RBUTTONDOWN
+# define HANDLE_WM_RBUTTONDOWN(hwnd, wParam, lParam, fn) \
+ ((fn)((hwnd), FALSE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
+# endif
+# ifndef HANDLE_WM_MOUSEMOVE
+# define HANDLE_WM_MOUSEMOVE(hwnd, wParam, lParam, fn) \
+ ((fn)((hwnd), (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
+# endif
+# ifndef HANDLE_WM_RBUTTONUP
+# define HANDLE_WM_RBUTTONUP(hwnd, wParam, lParam, fn) \
+ ((fn)((hwnd), (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
+# endif
+# ifndef HANDLE_WM_MBUTTONDOWN
+# define HANDLE_WM_MBUTTONDOWN(hwnd, wParam, lParam, fn) \
+ ((fn)((hwnd), FALSE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
+# endif
+# ifndef HANDLE_WM_LBUTTONUP
+# define HANDLE_WM_LBUTTONUP(hwnd, wParam, lParam, fn) \
+ ((fn)((hwnd), (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
+# endif
+# ifndef HANDLE_WM_LBUTTONDOWN
+# define HANDLE_WM_LBUTTONDOWN(hwnd, wParam, lParam, fn) \
+ ((fn)((hwnd), FALSE, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), (UINT)(wParam)), 0L)
+# endif
+# ifndef HANDLE_WM_SYSCHAR
+# define HANDLE_WM_SYSCHAR(hwnd, wParam, lParam, fn) \
+ ((fn)((hwnd), (TCHAR)(wParam), (int)(short)LOWORD(lParam)), 0L)
+# endif
+# ifndef HANDLE_WM_ACTIVATEAPP
+# define HANDLE_WM_ACTIVATEAPP(hwnd, wParam, lParam, fn) \
+ ((fn)((hwnd), (BOOL)(wParam), (DWORD)(lParam)), 0L)
+# endif
+# ifndef HANDLE_WM_WINDOWPOSCHANGING
+# define HANDLE_WM_WINDOWPOSCHANGING(hwnd, wParam, lParam, fn) \
+ (LRESULT)(DWORD)(BOOL)(fn)((hwnd), (LPWINDOWPOS)(lParam))
+# endif
+# ifndef HANDLE_WM_VSCROLL
+# define HANDLE_WM_VSCROLL(hwnd, wParam, lParam, fn) \
+ ((fn)((hwnd), (HWND)(lParam), (UINT)(LOWORD(wParam)), (int)(short)HIWORD(wParam)), 0L)
+# endif
+# ifndef HANDLE_WM_SETFOCUS
+# define HANDLE_WM_SETFOCUS(hwnd, wParam, lParam, fn) \
+ ((fn)((hwnd), (HWND)(wParam)), 0L)
+# endif
+# ifndef HANDLE_WM_KILLFOCUS
+# define HANDLE_WM_KILLFOCUS(hwnd, wParam, lParam, fn) \
+ ((fn)((hwnd), (HWND)(wParam)), 0L)
+# endif
+# ifndef HANDLE_WM_HSCROLL
+# define HANDLE_WM_HSCROLL(hwnd, wParam, lParam, fn) \
+ ((fn)((hwnd), (HWND)(lParam), (UINT)(LOWORD(wParam)), (int)(short)HIWORD(wParam)), 0L)
+# endif
+# ifndef HANDLE_WM_DROPFILES
+# define HANDLE_WM_DROPFILES(hwnd, wParam, lParam, fn) \
+ ((fn)((hwnd), (HDROP)(wParam)), 0L)
+# endif
+# ifndef HANDLE_WM_CHAR
+# define HANDLE_WM_CHAR(hwnd, wParam, lParam, fn) \
+ ((fn)((hwnd), (TCHAR)(wParam), (int)(short)LOWORD(lParam)), 0L)
+# endif
+# ifndef HANDLE_WM_SYSDEADCHAR
+# define HANDLE_WM_SYSDEADCHAR(hwnd, wParam, lParam, fn) \
+ ((fn)((hwnd), (TCHAR)(wParam), (int)(short)LOWORD(lParam)), 0L)
+# endif
+# ifndef HANDLE_WM_DEADCHAR
+# define HANDLE_WM_DEADCHAR(hwnd, wParam, lParam, fn) \
+ ((fn)((hwnd), (TCHAR)(wParam), (int)(short)LOWORD(lParam)), 0L)
+# endif
+#endif /* __MINGW32__ */
+
+
+/* Some parameters for tearoff menus. All in pixels. */
+#define TEAROFF_PADDING_X 2
+#define TEAROFF_BUTTON_PAD_X 8
+#define TEAROFF_MIN_WIDTH 200
+#define TEAROFF_SUBMENU_LABEL ">>"
+#define TEAROFF_COLUMN_PADDING 3 // # spaces to pad column with.
+
+
+/* For the Intellimouse: */
+#ifndef WM_MOUSEWHEEL
+#define WM_MOUSEWHEEL 0x20a
+#endif
+
+
+#ifdef FEAT_BEVAL
+# define ID_BEVAL_TOOLTIP 200
+# define BEVAL_TEXT_LEN MAXPATHL
+
+static void make_tooltip __ARGS((BalloonEval *beval, char *text, POINT pt));
+static void delete_tooltip __ARGS((BalloonEval *beval));
+static VOID CALLBACK BevalTimerProc __ARGS((HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime));
+
+static BalloonEval *cur_beval = NULL;
+static UINT BevalTimerId = 0;
+static DWORD LastActivity = 0;
+#endif
+
+/* Local variables: */
+
+#ifdef FEAT_MENU
+static UINT s_menu_id = 100;
+
+/*
+ * Use the system font for dialogs and tear-off menus. Remove this line to
+ * use DLG_FONT_NAME.
+ */
+# define USE_SYSMENU_FONT
+#endif
+
+#define VIM_NAME "vim"
+#define VIM_CLASS "Vim"
+#define VIM_CLASSW L"Vim"
+
+/* Initial size for the dialog template. For gui_mch_dialog() it's fixed,
+ * thus there should be room for every dialog. For tearoffs it's made bigger
+ * when needed. */
+#define DLG_ALLOC_SIZE 16 * 1024
+
+/*
+ * stuff for dialogs, menus, tearoffs etc.
+ */
+static LRESULT APIENTRY dialog_callback(HWND, UINT, WPARAM, LPARAM);
+static LRESULT APIENTRY tearoff_callback(HWND, UINT, WPARAM, LPARAM);
+static PWORD
+add_dialog_element(
+ PWORD p,
+ DWORD lStyle,
+ WORD x,
+ WORD y,
+ WORD w,
+ WORD h,
+ WORD Id,
+ WORD clss,
+ const char *caption);
+static LPWORD lpwAlign(LPWORD);
+static int nCopyAnsiToWideChar(LPWORD, LPSTR);
+static void gui_mch_tearoff(char_u *title, vimmenu_T *menu, int initX, int initY);
+static void get_dialog_font_metrics(void);
+
+static int dialog_default_button = -1;
+
+/* Intellimouse support */
+static int mouse_scroll_lines = 0;
+static UINT msh_msgmousewheel = 0;
+
+static int s_usenewlook; /* emulate W95/NT4 non-bold dialogs */
+#ifdef FEAT_TOOLBAR
+static void initialise_toolbar(void);
+static int get_toolbar_bitmap(vimmenu_T *menu);
+#endif
+
+#ifdef FEAT_MBYTE_IME
+static LRESULT _OnImeComposition(HWND hwnd, WPARAM dbcs, LPARAM param);
+static char_u *GetResultStr(HWND hwnd, int GCS, int *lenp);
+#endif
+#if defined(FEAT_MBYTE_IME) && defined(DYNAMIC_IME)
+# ifdef NOIME
+typedef struct tagCOMPOSITIONFORM {
+ DWORD dwStyle;
+ POINT ptCurrentPos;
+ RECT rcArea;
+} COMPOSITIONFORM, *PCOMPOSITIONFORM, NEAR *NPCOMPOSITIONFORM, FAR *LPCOMPOSITIONFORM;
+typedef HANDLE HIMC;
+# endif
+
+HINSTANCE hLibImm = NULL;
+LONG (WINAPI *pImmGetCompositionStringA)(HIMC, DWORD, LPVOID, DWORD);
+LONG (WINAPI *pImmGetCompositionStringW)(HIMC, DWORD, LPVOID, DWORD);
+HIMC (WINAPI *pImmGetContext)(HWND);
+HIMC (WINAPI *pImmAssociateContext)(HWND, HIMC);
+BOOL (WINAPI *pImmReleaseContext)(HWND, HIMC);
+BOOL (WINAPI *pImmGetOpenStatus)(HIMC);
+BOOL (WINAPI *pImmSetOpenStatus)(HIMC, BOOL);
+BOOL (WINAPI *pImmGetCompositionFont)(HIMC, LPLOGFONTA);
+BOOL (WINAPI *pImmSetCompositionFont)(HIMC, LPLOGFONTA);
+BOOL (WINAPI *pImmSetCompositionWindow)(HIMC, LPCOMPOSITIONFORM);
+BOOL (WINAPI *pImmGetConversionStatus)(HIMC, LPDWORD, LPDWORD);
+static void dyn_imm_load(void);
+#else
+# define pImmGetCompositionStringA ImmGetCompositionStringA
+# define pImmGetCompositionStringW ImmGetCompositionStringW
+# define pImmGetContext ImmGetContext
+# define pImmAssociateContext ImmAssociateContext
+# define pImmReleaseContext ImmReleaseContext
+# define pImmGetOpenStatus ImmGetOpenStatus
+# define pImmSetOpenStatus ImmSetOpenStatus
+# define pImmGetCompositionFont ImmGetCompositionFontA
+# define pImmSetCompositionFont ImmSetCompositionFontA
+# define pImmSetCompositionWindow ImmSetCompositionWindow
+# define pImmGetConversionStatus ImmGetConversionStatus
+#endif
+
+#ifndef ETO_IGNORELANGUAGE
+# define ETO_IGNORELANGUAGE 0x1000
+#endif
+
+/* multi monitor support */
+typedef struct _MONITORINFOstruct
+{
+ DWORD cbSize;
+ RECT rcMonitor;
+ RECT rcWork;
+ DWORD dwFlags;
+} _MONITORINFO;
+
+typedef HANDLE _HMONITOR;
+typedef _HMONITOR (WINAPI *TMonitorFromWindow)(HWND, DWORD);
+typedef BOOL (WINAPI *TGetMonitorInfo)(_HMONITOR, _MONITORINFO *);
+
+static TMonitorFromWindow pMonitorFromWindow = NULL;
+static TGetMonitorInfo pGetMonitorInfo = NULL;
+static HANDLE user32_lib = NULL;
+#ifdef FEAT_NETBEANS_INTG
+int WSInitialized = FALSE; /* WinSock is initialized */
+#endif
+/*
+ * Return TRUE when running under Windows NT 3.x or Win32s, both of which have
+ * less fancy GUI APIs.
+ */
+ static int
+is_winnt_3(void)
+{
+ return ((os_version.dwPlatformId == VER_PLATFORM_WIN32_NT
+ && os_version.dwMajorVersion == 3)
+ || (os_version.dwPlatformId == VER_PLATFORM_WIN32s));
+}
+
+/*
+ * Return TRUE when running under Win32s.
+ */
+ int
+gui_is_win32s(void)
+{
+ return (os_version.dwPlatformId == VER_PLATFORM_WIN32s);
+}
+
+#ifdef FEAT_MENU
+/*
+ * Figure out how high the menu bar is at the moment.
+ */
+ static int
+gui_mswin_get_menu_height(
+ int fix_window) /* If TRUE, resize window if menu height changed */
+{
+ static int old_menu_height = -1;
+
+ RECT rc1, rc2;
+ int num;
+ int menu_height;
+
+ if (gui.menu_is_active)
+ num = GetMenuItemCount(s_menuBar);
+ else
+ num = 0;
+
+ if (num == 0)
+ menu_height = 0;
+ else
+ {
+ if (is_winnt_3()) /* for NT 3.xx */
+ {
+ if (gui.starting)
+ menu_height = GetSystemMetrics(SM_CYMENU);
+ else
+ {
+ RECT r1, r2;
+ int frameht = GetSystemMetrics(SM_CYFRAME);
+ int capht = GetSystemMetrics(SM_CYCAPTION);
+
+ /* get window rect of s_hwnd
+ * get client rect of s_hwnd
+ * get cap height
+ * subtract from window rect, the sum of client height,
+ * (if not maximized)frame thickness, and caption height.
+ */
+ GetWindowRect(s_hwnd, &r1);
+ GetClientRect(s_hwnd, &r2);
+ menu_height = r1.bottom - r1.top - (r2.bottom - r2.top
+ + 2 * frameht * (!IsZoomed(s_hwnd)) + capht);
+ }
+ }
+ else /* win95 and variants (NT 4.0, I guess) */
+ {
+ /*
+ * In case 'lines' is set in _vimrc/_gvimrc window width doesn't
+ * seem to have been set yet, so menu wraps in default window
+ * width which is very narrow. Instead just return height of a
+ * single menu item. Will still be wrong when the menu really
+ * should wrap over more than one line.
+ */
+ GetMenuItemRect(s_hwnd, s_menuBar, 0, &rc1);
+ if (gui.starting)
+ menu_height = rc1.bottom - rc1.top + 1;
+ else
+ {
+ GetMenuItemRect(s_hwnd, s_menuBar, num - 1, &rc2);
+ menu_height = rc2.bottom - rc1.top + 1;
+ }
+ }
+ }
+
+ if (fix_window && menu_height != old_menu_height)
+ {
+ old_menu_height = menu_height;
+ gui_set_shellsize(FALSE, FALSE);
+ }
+
+ return menu_height;
+}
+#endif /*FEAT_MENU*/
+
+
+/*
+ * Setup for the Intellimouse
+ */
+ static void
+init_mouse_wheel(void)
+{
+
+#ifndef SPI_GETWHEELSCROLLLINES
+# define SPI_GETWHEELSCROLLLINES 104
+#endif
+
+#define VMOUSEZ_CLASSNAME "MouseZ" /* hidden wheel window class */
+#define VMOUSEZ_TITLE "Magellan MSWHEEL" /* hidden wheel window title */
+#define VMSH_MOUSEWHEEL "MSWHEEL_ROLLMSG"
+#define VMSH_SCROLL_LINES "MSH_SCROLL_LINES_MSG"
+
+ HWND hdl_mswheel;
+ UINT msh_msgscrolllines;
+
+ msh_msgmousewheel = 0;
+ mouse_scroll_lines = 3; /* reasonable default */
+
+ if ((os_version.dwPlatformId == VER_PLATFORM_WIN32_NT
+ && os_version.dwMajorVersion >= 4)
+ || (os_version.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS
+ && ((os_version.dwMajorVersion == 4
+ && os_version.dwMinorVersion >= 10)
+ || os_version.dwMajorVersion >= 5)))
+ {
+ /* if NT 4.0+ (or Win98) get scroll lines directly from system */
+ SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0,
+ &mouse_scroll_lines, 0);
+ }
+ else if (os_version.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS
+ || (os_version.dwPlatformId == VER_PLATFORM_WIN32_NT
+ && os_version.dwMajorVersion < 4))
+ { /*
+ * If Win95 or NT 3.51,
+ * try to find the hidden point32 window.
+ */
+ hdl_mswheel = FindWindow(VMOUSEZ_CLASSNAME, VMOUSEZ_TITLE);
+ if (hdl_mswheel)
+ {
+ msh_msgscrolllines = RegisterWindowMessage(VMSH_SCROLL_LINES);
+ if (msh_msgscrolllines)
+ {
+ mouse_scroll_lines = (int)SendMessage(hdl_mswheel,
+ msh_msgscrolllines, 0, 0);
+ msh_msgmousewheel = RegisterWindowMessage(VMSH_MOUSEWHEEL);
+ }
+ }
+ }
+}
+
+
+/* Intellimouse wheel handler */
+ static void
+_OnMouseWheel(
+ HWND hwnd,
+ short zDelta)
+{
+/* Treat a mouse wheel event as if it were a scroll request */
+ int i;
+ int size;
+ HWND hwndCtl;
+
+ if (curwin->w_scrollbars[SBAR_RIGHT].id != 0)
+ {
+ hwndCtl = curwin->w_scrollbars[SBAR_RIGHT].id;
+ size = curwin->w_scrollbars[SBAR_RIGHT].size;
+ }
+ else if (curwin->w_scrollbars[SBAR_LEFT].id != 0)
+ {
+ hwndCtl = curwin->w_scrollbars[SBAR_LEFT].id;
+ size = curwin->w_scrollbars[SBAR_LEFT].size;
+ }
+ else
+ return;
+
+ size = curwin->w_height;
+ if (mouse_scroll_lines == 0)
+ init_mouse_wheel();
+
+ if (mouse_scroll_lines > 0
+ && mouse_scroll_lines < (size > 2 ? size - 2 : 1))
+ {
+ for (i = mouse_scroll_lines; i > 0; --i)
+ _OnScroll(hwnd, hwndCtl, zDelta >= 0 ? SB_LINEUP : SB_LINEDOWN, 0);
+ }
+ else
+ _OnScroll(hwnd, hwndCtl, zDelta >= 0 ? SB_PAGEUP : SB_PAGEDOWN, 0);
+}
+
+#if 0 /* disabled, a gap appears below and beside the window, and the window
+ can be moved (in a strange way) */
+/*
+ * Even though we have _DuringSizing() which makes the rubber band a valid
+ * size, we need this for when the user maximises the window.
+ * TODO: Doesn't seem to adjust the width though for some reason.
+ */
+ static BOOL
+_OnWindowPosChanging(
+ HWND hwnd,
+ LPWINDOWPOS lpwpos)
+{
+ RECT workarea_rect;
+
+ if (!(lpwpos->flags & SWP_NOSIZE))
+ {
+ if (IsMaximized(hwnd)
+ && (os_version.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS
+ || (os_version.dwPlatformId == VER_PLATFORM_WIN32_NT
+ && os_version.dwMajorVersion >= 4)))
+ {
+ SystemParametersInfo(SPI_GETWORKAREA, 0, &workarea_rect, 0);
+ lpwpos->x = workarea_rect.left;
+ lpwpos->y = workarea_rect.top;
+ lpwpos->cx = workarea_rect.right - workarea_rect.left;
+ lpwpos->cy = workarea_rect.bottom - workarea_rect.top;
+ }
+ gui_mswin_get_valid_dimensions(lpwpos->cx, lpwpos->cy,
+ &lpwpos->cx, &lpwpos->cy);
+ }
+ return 0;
+}
+#endif
+
+#ifdef FEAT_NETBEANS_INTG
+ static void
+_OnWindowPosChanged(
+ HWND hwnd,
+ const LPWINDOWPOS lpwpos)
+{
+ static int x = 0, y = 0, cx = 0, cy = 0;
+
+ if (WSInitialized && (lpwpos->x != x || lpwpos->y != y
+ || lpwpos->cx != cx || lpwpos->cy != cy))
+ {
+ x = lpwpos->x;
+ y = lpwpos->y;
+ cx = lpwpos->cx;
+ cy = lpwpos->cy;
+ netbeans_frame_moved(x, y);
+ }
+ /* Allow to send WM_SIZE and WM_MOVE */
+ FORWARD_WM_WINDOWPOSCHANGED(hwnd, lpwpos, MyWindowProc);
+}
+#endif
+
+ static int
+_DuringSizing(
+ HWND hwnd,
+ UINT fwSide,
+ LPRECT lprc)
+{
+ int w, h;
+ int valid_w, valid_h;
+ int w_offset, h_offset;
+
+ w = lprc->right - lprc->left;
+ h = lprc->bottom - lprc->top;
+ gui_mswin_get_valid_dimensions(w, h, &valid_w, &valid_h);
+ w_offset = w - valid_w;
+ h_offset = h - valid_h;
+
+ if (fwSide == WMSZ_LEFT || fwSide == WMSZ_TOPLEFT
+ || fwSide == WMSZ_BOTTOMLEFT)
+ lprc->left += w_offset;
+ else if (fwSide == WMSZ_RIGHT || fwSide == WMSZ_TOPRIGHT
+ || fwSide == WMSZ_BOTTOMRIGHT)
+ lprc->right -= w_offset;
+
+ if (fwSide == WMSZ_TOP || fwSide == WMSZ_TOPLEFT
+ || fwSide == WMSZ_TOPRIGHT)
+ lprc->top += h_offset;
+ else if (fwSide == WMSZ_BOTTOM || fwSide == WMSZ_BOTTOMLEFT
+ || fwSide == WMSZ_BOTTOMRIGHT)
+ lprc->bottom -= h_offset;
+ return TRUE;
+}
+
+
+
+ static LRESULT CALLBACK
+_WndProc(
+ HWND hwnd,
+ UINT uMsg,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ /*
+ TRACE("WndProc: 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;
+
+ switch (uMsg)
+ {
+ HANDLE_MSG(hwnd, WM_DEADCHAR, _OnDeadChar);
+ HANDLE_MSG(hwnd, WM_SYSDEADCHAR, _OnDeadChar);
+ /* HANDLE_MSG(hwnd, WM_ACTIVATE, _OnActivate); */
+ HANDLE_MSG(hwnd, WM_CLOSE, _OnClose);
+ /* HANDLE_MSG(hwnd, WM_COMMAND, _OnCommand); */
+ HANDLE_MSG(hwnd, WM_DESTROY, _OnDestroy);
+ HANDLE_MSG(hwnd, WM_DROPFILES, _OnDropFiles);
+ HANDLE_MSG(hwnd, WM_HSCROLL, _OnScroll);
+ HANDLE_MSG(hwnd, WM_KILLFOCUS, _OnKillFocus);
+#ifdef FEAT_MENU
+ HANDLE_MSG(hwnd, WM_COMMAND, _OnMenu);
+#endif
+ /* HANDLE_MSG(hwnd, WM_MOVE, _OnMove); */
+ /* HANDLE_MSG(hwnd, WM_NCACTIVATE, _OnNCActivate); */
+ HANDLE_MSG(hwnd, WM_SETFOCUS, _OnSetFocus);
+ HANDLE_MSG(hwnd, WM_SIZE, _OnSize);
+ /* HANDLE_MSG(hwnd, WM_SYSCOMMAND, _OnSysCommand); */
+ /* HANDLE_MSG(hwnd, WM_SYSKEYDOWN, _OnAltKey); */
+ HANDLE_MSG(hwnd, WM_VSCROLL, _OnScroll);
+ // HANDLE_MSG(hwnd, WM_WINDOWPOSCHANGING, _OnWindowPosChanging);
+ HANDLE_MSG(hwnd, WM_ACTIVATEAPP, _OnActivateApp);
+#ifdef FEAT_NETBEANS_INTG
+ HANDLE_MSG(hwnd, WM_WINDOWPOSCHANGED, _OnWindowPosChanged);
+#endif
+
+ case WM_QUERYENDSESSION: /* System wants to go down. */
+ gui_shell_closed(); /* Will exit when no changed buffers. */
+ return FALSE; /* Do NOT allow system to go down. */
+
+ case WM_ENDSESSION:
+ if (wParam) /* system only really goes down when wParam is TRUE */
+ _OnEndSession();
+ break;
+
+ case WM_CHAR:
+ /* Don't use HANDLE_MSG() for WM_CHAR, it truncates wParam to a single
+ * byte while we want the UTF-16 character value. */
+ _OnChar(hwnd, wParam, (int)(short)LOWORD(lParam));
+ return 0L;
+
+ case WM_SYSCHAR:
+ /*
+ * if 'winaltkeys' is "no", or it's "menu" and it's not a menu
+ * shortcut key, handle like a typed ALT key, otherwise call Windows
+ * ALT key handling.
+ */
+#ifdef FEAT_MENU
+ if ( !gui.menu_is_active
+ || p_wak[0] == 'n'
+ || (p_wak[0] == 'm' && !gui_is_menu_shortcut((int)wParam))
+ )
+#endif
+ {
+ _OnSysChar(hwnd, wParam, (int)(short)LOWORD(lParam));
+ return 0L;
+ }
+#ifdef FEAT_MENU
+ else
+ return MyWindowProc(hwnd, uMsg, wParam, lParam);
+#endif
+
+ case WM_SYSKEYUP:
+#ifdef FEAT_MENU
+ /* This used to be done only when menu is active: ALT key is used for
+ * that. But that caused problems when menu is disabled and using
+ * Alt-Tab-Esc: get into a strange state where no mouse-moved events
+ * are received, mouse pointer remains hidden. */
+ return MyWindowProc(hwnd, uMsg, wParam, lParam);
+#else
+ return 0;
+#endif
+
+ case WM_SIZING: /* HANDLE_MSG doesn't seem to handle this one */
+ return _DuringSizing(hwnd, (UINT)wParam, (LPRECT)lParam);
+
+ case WM_MOUSEWHEEL:
+ _OnMouseWheel(hwnd, HIWORD(wParam));
+ break;
+
+#ifdef FEAT_TOOLBAR
+ case WM_NOTIFY:
+ switch (((LPNMHDR) lParam)->code)
+ {
+ case TTN_NEEDTEXT:
+ {
+ LPTOOLTIPTEXT lpttt;
+ UINT idButton;
+ int idx;
+ vimmenu_T *pMenu;
+
+ lpttt = (LPTOOLTIPTEXT)lParam;
+ idButton = (UINT) lpttt->hdr.idFrom;
+ pMenu = gui_mswin_find_menu(root_menu, idButton);
+ if (pMenu)
+ {
+ idx = MENU_INDEX_TIP;
+ if (pMenu->strings[idx])
+ {
+ lpttt->hinst = NULL; /* string, not resource */
+ lpttt->lpszText = pMenu->strings[idx];
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+#endif
+#if defined(MENUHINTS) && defined(FEAT_MENU)
+ case WM_MENUSELECT:
+ if (((UINT) HIWORD(wParam)
+ & (0xffff ^ (MF_MOUSESELECT + MF_BITMAP + MF_POPUP)))
+ == MF_HILITE
+ && (State & CMDLINE) == 0)
+ {
+ UINT idButton;
+ vimmenu_T *pMenu;
+ static int did_menu_tip = FALSE;
+
+ if (did_menu_tip)
+ {
+ msg_clr_cmdline();
+ setcursor();
+ out_flush();
+ did_menu_tip = FALSE;
+ }
+
+ idButton = (UINT)LOWORD(wParam);
+ pMenu = gui_mswin_find_menu(root_menu, idButton);
+ if (pMenu != NULL && pMenu->strings[MENU_INDEX_TIP] != 0
+ && GetMenuState(s_menuBar, pMenu->id, MF_BYCOMMAND) != -1)
+ {
+ msg(pMenu->strings[MENU_INDEX_TIP]);
+ setcursor();
+ out_flush();
+ did_menu_tip = TRUE;
+ }
+ }
+ break;
+#endif
+ case WM_NCHITTEST:
+ {
+ LRESULT result;
+ int x, y;
+ int xPos = GET_X_LPARAM(lParam);
+
+ result = MyWindowProc(hwnd, uMsg, wParam, lParam);
+ if (result == HTCLIENT)
+ {
+ gui_mch_get_winpos(&x, &y);
+ xPos -= x;
+
+ if (xPos < 48) /* <VN> TODO should use system metric? */
+ return HTBOTTOMLEFT;
+ else
+ return HTBOTTOMRIGHT;
+ }
+ else
+ return result;
+ }
+ /* break; notreached */
+
+#ifdef FEAT_MBYTE_IME
+ case WM_IME_NOTIFY:
+ if (!_OnImeNotify(hwnd, (DWORD)wParam, (DWORD)lParam))
+ return MyWindowProc(hwnd, uMsg, wParam, lParam);
+ break;
+ case WM_IME_COMPOSITION:
+ if (!_OnImeComposition(hwnd, wParam, lParam))
+ return MyWindowProc(hwnd, uMsg, wParam, lParam);
+ break;
+#endif
+
+ default:
+ if (uMsg == msh_msgmousewheel && msh_msgmousewheel != 0)
+ { /* handle MSH_MOUSEWHEEL messages for Intellimouse */
+ _OnMouseWheel(hwnd, HIWORD(wParam));
+ break;
+ }
+#ifdef MSWIN_FIND_REPLACE
+ else
+ if (uMsg == s_findrep_msg && s_findrep_msg != 0)
+ {
+ _OnFindRepl();
+ }
+#endif
+ return MyWindowProc(hwnd, uMsg, wParam, lParam);
+ }
+
+ return 1;
+}
+
+/*
+ * End of call-back routines
+ */
+
+/* parent window, if specified with -P */
+HWND vim_parent_hwnd = NULL;
+
+ static BOOL CALLBACK
+FindWindowTitle(HWND hwnd, LPARAM lParam)
+{
+ char buf[2048];
+ char *title = (char *)lParam;
+
+ if (GetWindowText(hwnd, buf, sizeof(buf)))
+ {
+ if (strstr(buf, title) != NULL)
+ {
+ /* Found it. Store the window ref. and quit searching. */
+ vim_parent_hwnd = FindWindowEx(hwnd, NULL, "MDIClient", NULL);
+ return FALSE;
+ }
+ }
+ return TRUE; /* continue searching */
+}
+
+/*
+ * Invoked for '-P "title"' argument: search for parent application to open
+ * our window in.
+ */
+ void
+gui_mch_set_parent(char *title)
+{
+ EnumWindows(FindWindowTitle, (LPARAM)title);
+ if (vim_parent_hwnd == NULL)
+ {
+ EMSG2(_("E671: Cannot find window title \"%s\""), title);
+ mch_exit(2);
+ }
+}
+
+ static void
+ole_error(char *arg)
+{
+ EMSG2(_("E243: Argument not supported: \"-%s\"; Use the OLE version."),
+ arg);
+}
+
+/*
+ * Parse the GUI related command-line arguments. Any arguments used are
+ * deleted from argv, and *argc is decremented accordingly. This is called
+ * when vim is started, whether or not the GUI has been started.
+ */
+ void
+gui_mch_prepare(int *argc, char **argv)
+{
+ int silent = FALSE;
+ int idx;
+
+ /* Check for special OLE command line parameters */
+ if ((*argc == 2 || *argc == 3) && (argv[1][0] == '-' || argv[1][0] == '/'))
+ {
+ /* Check for a "-silent" argument first. */
+ if (*argc == 3 && STRICMP(argv[1] + 1, "silent") == 0
+ && (argv[2][0] == '-' || argv[2][0] == '/'))
+ {
+ silent = TRUE;
+ idx = 2;
+ }
+ else
+ idx = 1;
+
+ /* Register Vim as an OLE Automation server */
+ if (STRICMP(argv[idx] + 1, "register") == 0)
+ {
+#ifdef FEAT_OLE
+ RegisterMe(silent);
+ mch_exit(0);
+#else
+ if (!silent)
+ ole_error("register");
+ mch_exit(2);
+#endif
+ }
+
+ /* Unregister Vim as an OLE Automation server */
+ if (STRICMP(argv[idx] + 1, "unregister") == 0)
+ {
+#ifdef FEAT_OLE
+ UnregisterMe(!silent);
+ mch_exit(0);
+#else
+ if (!silent)
+ ole_error("unregister");
+ mch_exit(2);
+#endif
+ }
+
+ /* Ignore an -embedding argument. It is only relevant if the
+ * application wants to treat the case when it is started manually
+ * differently from the case where it is started via automation (and
+ * we don't).
+ */
+ if (STRICMP(argv[idx] + 1, "embedding") == 0)
+ {
+#ifdef FEAT_OLE
+ *argc = 1;
+#else
+ ole_error("embedding");
+ mch_exit(2);
+#endif
+ }
+ }
+
+#ifdef FEAT_OLE
+ {
+ int bDoRestart = FALSE;
+
+ InitOLE(&bDoRestart);
+ /* automatically exit after registering */
+ if (bDoRestart)
+ mch_exit(0);
+ }
+#endif
+
+#ifdef FEAT_NETBEANS_INTG
+ {
+ /* stolen from gui_x11.x */
+ int arg;
+
+ for (arg = 1; arg < *argc; arg++)
+ if (strncmp("-nb", argv[arg], 3) == 0)
+ {
+ usingNetbeans++;
+ netbeansArg = argv[arg];
+ mch_memmove(&argv[arg], &argv[arg + 1],
+ (--*argc - arg) * sizeof(char *));
+ argv[*argc] = NULL;
+ break; /* enough? */
+ }
+
+ if (usingNetbeans)
+ {
+ WSADATA wsaData;
+ int wsaerr;
+
+ /* Init WinSock */
+ wsaerr = WSAStartup(MAKEWORD(2, 2), &wsaData);
+ if (wsaerr == 0)
+ WSInitialized = TRUE;
+ }
+ }
+#endif
+
+ /* get the OS version info */
+ os_version.dwOSVersionInfoSize = sizeof(os_version);
+ GetVersionEx(&os_version); /* this call works on Win32s, Win95 and WinNT */
+
+ /* try and load the user32.dll library and get the entry points for
+ * multi-monitor-support. */
+ if ((user32_lib = LoadLibrary("User32.dll")) != NULL)
+ {
+ pMonitorFromWindow = (TMonitorFromWindow)GetProcAddress(user32_lib,
+ "MonitorFromWindow");
+
+ /* there are ...A and ...W version of GetMonitorInfo - looking at
+ * winuser.h, they have exactly the same declaration. */
+ pGetMonitorInfo = (TGetMonitorInfo)GetProcAddress(user32_lib,
+ "GetMonitorInfoA");
+ }
+}
+
+/*
+ * Initialise the GUI. Create all the windows, set up all the call-backs
+ * etc.
+ */
+ int
+gui_mch_init(void)
+{
+ const char szVimWndClass[] = VIM_CLASS;
+ const char szTextAreaClass[] = "VimTextArea";
+ WNDCLASS wndclass;
+#ifdef FEAT_MBYTE
+ const WCHAR szVimWndClassW[] = VIM_CLASSW;
+ WNDCLASSW wndclassw;
+#endif
+#ifdef GLOBAL_IME
+ ATOM atom;
+#endif
+
+ /* Display any pending error messages */
+ display_errors();
+
+ /* Return here if the window was already opened (happens when
+ * gui_mch_dialog() is called early). */
+ if (s_hwnd != NULL)
+ return OK;
+
+ /*
+ * Load the tearoff bitmap
+ */
+#ifdef FEAT_TEAROFF
+ s_htearbitmap = LoadBitmap(s_hinst, "IDB_TEAROFF");
+#endif
+
+ gui.scrollbar_width = GetSystemMetrics(SM_CXVSCROLL);
+ gui.scrollbar_height = GetSystemMetrics(SM_CYHSCROLL);
+#ifdef FEAT_MENU
+ gui.menu_height = 0; /* Windows takes care of this */
+#endif
+ gui.border_width = 0;
+
+ s_brush = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
+
+#ifdef FEAT_MBYTE
+ /* First try using the wide version, so that we can use any title.
+ * Otherwise only characters in the active codepage will work. */
+ if (GetClassInfoW(s_hinst, szVimWndClassW, &wndclassw) == 0)
+ {
+ wndclassw.style = 0;
+ wndclassw.lpfnWndProc = _WndProc;
+ wndclassw.cbClsExtra = 0;
+ wndclassw.cbWndExtra = 0;
+ wndclassw.hInstance = s_hinst;
+ wndclassw.hIcon = LoadIcon(wndclassw.hInstance, "IDR_VIM");
+ wndclassw.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wndclassw.hbrBackground = s_brush;
+ wndclassw.lpszMenuName = NULL;
+ wndclassw.lpszClassName = szVimWndClassW;
+
+ if ((
+#ifdef GLOBAL_IME
+ atom =
+#endif
+ RegisterClassW(&wndclassw)) == 0)
+ {
+ if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
+ return FAIL;
+
+ /* Must be Windows 98, fall back to non-wide function. */
+ }
+ else
+ wide_WindowProc = TRUE;
+ }
+
+ if (!wide_WindowProc)
+#endif
+
+ if (GetClassInfo(s_hinst, szVimWndClass, &wndclass) == 0)
+ {
+ wndclass.style = 0;
+ wndclass.lpfnWndProc = _WndProc;
+ wndclass.cbClsExtra = 0;
+ wndclass.cbWndExtra = 0;
+ wndclass.hInstance = s_hinst;
+ wndclass.hIcon = LoadIcon(wndclass.hInstance, "IDR_VIM");
+ wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wndclass.hbrBackground = s_brush;
+ wndclass.lpszMenuName = NULL;
+ wndclass.lpszClassName = szVimWndClass;
+
+ if ((
+#ifdef GLOBAL_IME
+ atom =
+#endif
+ RegisterClass(&wndclass)) == 0)
+ return FAIL;
+ }
+
+ if (vim_parent_hwnd != NULL)
+ {
+#ifdef HAVE_TRY_EXCEPT
+ __try
+ {
+#endif
+ /* Open inside the specified parent window.
+ * TODO: last argument should point to a CLIENTCREATESTRUCT
+ * structure. */
+ s_hwnd = CreateWindowEx(
+ WS_EX_MDICHILD,
+ szVimWndClass, "Vim MSWindows GUI",
+ WS_OVERLAPPEDWINDOW | WS_CHILD | WS_CLIPSIBLINGS | 0xC000,
+ gui_win_x == -1 ? CW_USEDEFAULT : gui_win_x,
+ gui_win_y == -1 ? CW_USEDEFAULT : gui_win_y,
+ 100, /* Any value will do */
+ 100, /* Any value will do */
+ vim_parent_hwnd, NULL,
+ s_hinst, NULL);
+#ifdef HAVE_TRY_EXCEPT
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ /* NOP */
+ }
+#endif
+ if (s_hwnd == NULL)
+ {
+ EMSG(_("E672: Unable to open window inside MDI application"));
+ mch_exit(2);
+ }
+ }
+ else
+ /* Open toplevel window. */
+ s_hwnd = CreateWindow(
+ szVimWndClass, "Vim MSWindows GUI",
+ WS_OVERLAPPEDWINDOW,
+ gui_win_x == -1 ? CW_USEDEFAULT : gui_win_x,
+ gui_win_y == -1 ? CW_USEDEFAULT : gui_win_y,
+ 100, /* Any value will do */
+ 100, /* Any value will do */
+ NULL, NULL,
+ s_hinst, NULL);
+
+ if (s_hwnd == NULL)
+ return FAIL;
+
+#ifdef GLOBAL_IME
+ global_ime_init(atom, s_hwnd);
+#endif
+#if defined(FEAT_MBYTE_IME) && defined(DYNAMIC_IME)
+ dyn_imm_load();
+#endif
+
+ /* Create the text area window */
+ if (GetClassInfo(s_hinst, szTextAreaClass, &wndclass) == 0)
+ {
+ wndclass.style = CS_OWNDC;
+ wndclass.lpfnWndProc = _TextAreaWndProc;
+ wndclass.cbClsExtra = 0;
+ wndclass.cbWndExtra = 0;
+ wndclass.hInstance = s_hinst;
+ wndclass.hIcon = NULL;
+ wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wndclass.hbrBackground = NULL;
+ wndclass.lpszMenuName = NULL;
+ wndclass.lpszClassName = szTextAreaClass;
+
+ if (RegisterClass(&wndclass) == 0)
+ return FAIL;
+ }
+ s_textArea = CreateWindowEx(
+ WS_EX_CLIENTEDGE,
+ szTextAreaClass, "Vim text area",
+ WS_CHILD | WS_VISIBLE, 0, 0,
+ 100, /* Any value will do for now */
+ 100, /* Any value will do for now */
+ s_hwnd, NULL,
+ s_hinst, NULL);
+
+ if (s_textArea == NULL)
+ return FAIL;
+
+#ifdef FEAT_MENU
+ s_menuBar = CreateMenu();
+#endif
+ s_hdc = GetDC(s_textArea);
+
+#ifdef MSWIN16_FASTTEXT
+ SetBkMode(s_hdc, OPAQUE);
+#endif
+
+#ifdef FEAT_WINDOWS
+ DragAcceptFiles(s_hwnd, TRUE);
+#endif
+
+ /* Do we need to bother with this? */
+ /* m_fMouseAvail = GetSystemMetrics(SM_MOUSEPRESENT); */
+
+ /* Get background/foreground colors from the system */
+ gui_mch_def_colors();
+
+ /* Get the colors from the "Normal" group (set in syntax.c or in a vimrc
+ * file) */
+ set_normal_colors();
+
+ /*
+ * Check that none of the colors are the same as the background color.
+ * Then store the current values as the defaults.
+ */
+ gui_check_colors();
+ gui.def_norm_pixel = gui.norm_pixel;
+ gui.def_back_pixel = gui.back_pixel;
+
+ /* Get the colors for the highlight groups (gui_check_colors() might have
+ * changed them) */
+ highlight_gui_started();
+
+ /*
+ * Start out by adding the configured border width into the border offset
+ */
+ gui.border_offset = gui.border_width + 2; /*CLIENT EDGE*/
+
+ /*
+ * Set up for Intellimouse processing
+ */
+ init_mouse_wheel();
+
+ /*
+ * compute a couple of metrics used for the dialogs
+ */
+ get_dialog_font_metrics();
+#ifdef FEAT_TOOLBAR
+ /*
+ * Create the toolbar
+ */
+ initialise_toolbar();
+#endif
+#ifdef MSWIN_FIND_REPLACE
+ /*
+ * Initialise the dialog box stuff
+ */
+ s_findrep_msg = RegisterWindowMessage(FINDMSGSTRING);
+
+ /* Initialise the struct */
+ s_findrep_struct.lStructSize = sizeof(s_findrep_struct);
+ s_findrep_struct.lpstrFindWhat = alloc(MSWIN_FR_BUFSIZE);
+ s_findrep_struct.lpstrFindWhat[0] = NUL;
+ s_findrep_struct.lpstrReplaceWith = alloc(MSWIN_FR_BUFSIZE);
+ s_findrep_struct.lpstrReplaceWith[0] = NUL;
+ s_findrep_struct.wFindWhatLen = MSWIN_FR_BUFSIZE;
+ s_findrep_struct.wReplaceWithLen = MSWIN_FR_BUFSIZE;
+#endif
+#ifdef FEAT_NETBEANS_INTG
+ if (usingNetbeans)
+ netbeans_w32_connect();
+#endif
+
+ return OK;
+}
+
+/*
+ * Get the size of the screen, taking position on multiple monitors into
+ * account (if supported).
+ */
+ static void
+get_work_area(RECT *spi_rect)
+{
+ _HMONITOR mon;
+ _MONITORINFO moninfo;
+
+ /* use these functions only if available */
+ if (pMonitorFromWindow != NULL && pGetMonitorInfo != NULL)
+ {
+ /* work out which monitor the window is on, and get *it's* work area */
+ mon = pMonitorFromWindow(s_hwnd, 1 /*MONITOR_DEFAULTTOPRIMARY*/);
+ if (mon != NULL)
+ {
+ moninfo.cbSize = sizeof(_MONITORINFO);
+ if (pGetMonitorInfo(mon, &moninfo))
+ {
+ *spi_rect = moninfo.rcWork;
+ return;
+ }
+ }
+ }
+ /* this is the old method... */
+ SystemParametersInfo(SPI_GETWORKAREA, 0, spi_rect, 0);
+}
+
+/*
+ * Set the size of the window to the given width and height in pixels.
+ */
+ void
+gui_mch_set_shellsize(int width, int height,
+ int min_width, int min_height, int base_width, int base_height)
+{
+ RECT workarea_rect;
+ int win_width, win_height;
+ int win_xpos, win_ypos;
+ WINDOWPLACEMENT wndpl;
+
+ /* try to keep window completely on screen */
+ /* get size of the screen work area (excludes taskbar, appbars) */
+ get_work_area(&workarea_rect);
+
+ /* get current posision of our window */
+ wndpl.length = sizeof(WINDOWPLACEMENT);
+ GetWindowPlacement(s_hwnd, &wndpl);
+
+ /* Resizing a maximized window looks very strange, unzoom it first.
+ * But don't do it when still starting up, it may have been requested in
+ * the shortcut. */
+ if (wndpl.showCmd == SW_SHOWMAXIMIZED && starting == 0)
+ {
+ ShowWindow(s_hwnd, SW_SHOWNORMAL);
+ /* Need to get the settings of the normal window. */
+ GetWindowPlacement(s_hwnd, &wndpl);
+ }
+
+ win_xpos = wndpl.rcNormalPosition.left;
+ win_ypos = wndpl.rcNormalPosition.top;
+
+ /* compute the size of the outside of the window */
+ win_width = width + GetSystemMetrics(SM_CXFRAME) * 2;
+ win_height = height + GetSystemMetrics(SM_CYFRAME) * 2
+ + GetSystemMetrics(SM_CYCAPTION)
+#ifdef FEAT_MENU
+ + gui_mswin_get_menu_height(FALSE)
+#endif
+ ;
+
+ /* if the window is going off the screen, move it on to the screen */
+ if (win_xpos + win_width > workarea_rect.right)
+ win_xpos = workarea_rect.right - win_width;
+
+ if (win_xpos < workarea_rect.left)
+ win_xpos = workarea_rect.left;
+
+ if (win_ypos + win_height > workarea_rect.bottom)
+ win_ypos = workarea_rect.bottom - win_height;
+
+ if (win_ypos < workarea_rect.top)
+ win_ypos = workarea_rect.top;
+
+ /* When the taskbar is placed on the left or top of the screen,
+ * SetWindowPlacement() adds its width or height automatically, compensate
+ * for that. When the offset is over 400 it's probably something else,
+ * skip it then (just in case). */
+ if (workarea_rect.left > 0 && workarea_rect.left < 400)
+ win_xpos -= workarea_rect.left;
+ if (workarea_rect.top > 0 && workarea_rect.top < 400)
+ win_ypos -= workarea_rect.top;
+
+ wndpl.rcNormalPosition.left = win_xpos;
+ wndpl.rcNormalPosition.right = win_xpos + win_width;
+ wndpl.rcNormalPosition.top = win_ypos;
+ wndpl.rcNormalPosition.bottom = win_ypos + win_height;
+
+ /* set window position - we should use SetWindowPlacement rather than
+ * SetWindowPos as the MSDN docs say the coord systems returned by
+ * these two are not compatible. */
+ SetWindowPlacement(s_hwnd, &wndpl);
+
+ SetActiveWindow(s_hwnd);
+ SetFocus(s_hwnd);
+
+#ifdef FEAT_MENU
+ /* Menu may wrap differently now */
+ gui_mswin_get_menu_height(!gui.starting);
+#endif
+}
+
+
+ void
+gui_mch_set_scrollbar_thumb(
+ scrollbar_T *sb,
+ long val,
+ long size,
+ long max)
+{
+ SCROLLINFO info;
+
+ sb->scroll_shift = 0;
+ while (max > 32767)
+ {
+ max = (max + 1) >> 1;
+ val >>= 1;
+ size >>= 1;
+ ++sb->scroll_shift;
+ }
+
+ if (sb->scroll_shift > 0)
+ ++size;
+
+ info.cbSize = sizeof(info);
+ info.fMask = SIF_POS | SIF_RANGE | SIF_PAGE;
+ info.nPos = val;
+ info.nMin = 0;
+ info.nMax = max;
+ info.nPage = size;
+ SetScrollInfo(sb->id, SB_CTL, &info, TRUE);
+}
+
+
+/*
+ * Set the current text font.
+ */
+ void
+gui_mch_set_font(GuiFont font)
+{
+ gui.currFont = font;
+}
+
+
+/*
+ * Set the current text foreground color.
+ */
+ void
+gui_mch_set_fg_color(guicolor_T color)
+{
+ gui.currFgColor = color;
+}
+
+/*
+ * Set the current text background color.
+ */
+ void
+gui_mch_set_bg_color(guicolor_T color)
+{
+ gui.currBgColor = color;
+}
+
+#if defined(FEAT_MBYTE) && defined(FEAT_MBYTE_IME)
+/*
+ * Multi-byte handling, originally by Sung-Hoon Baek.
+ * First static functions (no prototypes generated).
+ */
+#ifdef _MSC_VER
+# include <ime.h> /* Apparently not needed for Cygwin, MingW or Borland. */
+#endif
+#include <imm.h>
+
+/*
+ * display composition string(korean)
+ */
+ static void
+DisplayCompStringOpaque(char_u *s, int len)
+{
+ int OldBkMode = GetBkMode(s_hdc);
+
+ SetBkMode(s_hdc, OPAQUE);
+ gui_outstr_nowrap(s, len, GUI_MON_TRS_CURSOR,
+ (guicolor_T)0, (guicolor_T)0, 0);
+ SetBkMode(s_hdc, OldBkMode);
+}
+
+/*
+ * handle WM_IME_NOTIFY message
+ */
+ static LRESULT
+_OnImeNotify(HWND hWnd, DWORD dwCommand, DWORD dwData)
+{
+ LRESULT lResult = 0;
+ HIMC hImc;
+
+ if (!pImmGetContext || (hImc = pImmGetContext(hWnd)) == (HIMC)0)
+ return lResult;
+ switch (dwCommand)
+ {
+ case IMN_SETOPENSTATUS:
+ if (pImmGetOpenStatus(hImc))
+ {
+ pImmSetCompositionFont(hImc, &norm_logfont);
+ im_set_position(gui.row, gui.col);
+
+ /* Disable langmap */
+ State &= ~LANGMAP;
+ if (State & INSERT)
+ {
+#if defined(FEAT_WINDOWS) && defined(FEAT_KEYMAP)
+ /* Unshown 'keymap' in status lines */
+ if (curbuf->b_p_iminsert == B_IMODE_LMAP)
+ {
+ /* Save cursor position */
+ int old_row = gui.row;
+ int old_col = gui.col;
+
+ // This must be called here before
+ // status_redraw_curbuf(), otherwise the mode
+ // message may appear in the wrong position.
+ showmode();
+ status_redraw_curbuf();
+ update_screen(0);
+ /* Restore cursor position */
+ gui.row = old_row;
+ gui.col = old_col;
+ }
+#endif
+ }
+ }
+ gui_update_cursor(TRUE, FALSE);
+ lResult = 0;
+ break;
+ }
+ pImmReleaseContext(hWnd, hImc);
+ return lResult;
+}
+
+ static LRESULT
+_OnImeComposition(HWND hwnd, WPARAM dbcs, LPARAM param)
+{
+ char_u *ret;
+ int len;
+
+ if ((param & GCS_RESULTSTR) == 0) /* Composition unfinished. */
+ return 0;
+
+ ret = GetResultStr(hwnd, GCS_RESULTSTR, &len);
+ if (ret != NULL)
+ {
+ add_to_input_buf_csi(ret, len);
+ vim_free(ret);
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * get the current composition string, in UCS-2; *lenp is the number of
+ * *lenp is the number of Unicode characters.
+ */
+ static short_u *
+GetCompositionString_inUCS2(HIMC hIMC, DWORD GCS, int *lenp)
+{
+ LONG ret;
+ LPWSTR wbuf = NULL;
+ char_u *buf;
+
+ if (!pImmGetContext)
+ return NULL; /* no imm32.dll */
+
+ /* Try Unicode; this'll always work on NT regardless of codepage. */
+ ret = pImmGetCompositionStringW(hIMC, GCS, NULL, 0);
+ if (ret == 0)
+ return NULL; /* empty */
+
+ if (ret > 0)
+ {
+ /* Allocate the requested buffer plus space for the NUL character. */
+ wbuf = (LPWSTR)alloc(ret + sizeof(WCHAR));
+ if (wbuf != NULL)
+ {
+ pImmGetCompositionStringW(hIMC, GCS, wbuf, ret);
+ *lenp = ret / sizeof(WCHAR);
+ }
+ return (short_u *)wbuf;
+ }
+
+ /* ret < 0; we got an error, so try the ANSI version. This'll work
+ * on 9x/ME, but only if the codepage happens to be set to whatever
+ * we're inputting. */
+ ret = pImmGetCompositionStringA(hIMC, GCS, NULL, 0);
+ if (ret <= 0)
+ return NULL; /* empty or error */
+
+ buf = alloc(ret);
+ if (buf == NULL)
+ return NULL;
+ pImmGetCompositionStringA(hIMC, GCS, buf, ret);
+
+ /* convert from codepage to UCS-2 */
+ MultiByteToWideChar_alloc(GetACP(), 0, buf, ret, &wbuf, lenp);
+ vim_free(buf);
+
+ return (short_u *)wbuf;
+}
+
+/*
+ * void GetResultStr()
+ *
+ * This handles WM_IME_COMPOSITION with GCS_RESULTSTR flag on.
+ * get complete composition string
+ */
+ static char_u *
+GetResultStr(HWND hwnd, int GCS, int *lenp)
+{
+ HIMC hIMC; /* Input context handle. */
+ short_u *buf = NULL;
+ char_u *convbuf = NULL;
+
+ if (!pImmGetContext || (hIMC = pImmGetContext(hwnd)) == (HIMC)0)
+ return NULL;
+
+ /* Reads in the composition string. */
+ buf = GetCompositionString_inUCS2(hIMC, GCS, lenp);
+ if (buf == NULL)
+ return NULL;
+
+ convbuf = ucs2_to_enc(buf, lenp);
+ pImmReleaseContext(hwnd, hIMC);
+ vim_free(buf);
+ return convbuf;
+}
+#endif
+
+/* For global functions we need prototypes. */
+#if (defined(FEAT_MBYTE) && defined(FEAT_MBYTE_IME)) || defined(PROTO)
+
+/*
+ * set font to IM.
+ */
+ void
+im_set_font(LOGFONT *lf)
+{
+ HIMC hImc;
+
+ if (pImmGetContext && (hImc = pImmGetContext(s_hwnd)) != (HIMC)0)
+ {
+ pImmSetCompositionFont(hImc, lf);
+ pImmReleaseContext(s_hwnd, hImc);
+ }
+}
+
+/*
+ * Notify cursor position to IM.
+ */
+ void
+im_set_position(int row, int col)
+{
+ HIMC hImc;
+
+ if (pImmGetContext && (hImc = pImmGetContext(s_hwnd)) != (HIMC)0)
+ {
+ COMPOSITIONFORM cfs;
+
+ cfs.dwStyle = CFS_POINT;
+ cfs.ptCurrentPos.x = FILL_X(col);
+ cfs.ptCurrentPos.y = FILL_Y(row);
+ MapWindowPoints(s_textArea, s_hwnd, &cfs.ptCurrentPos, 1);
+ pImmSetCompositionWindow(hImc, &cfs);
+
+ pImmReleaseContext(s_hwnd, hImc);
+ }
+}
+
+/*
+ * Set IM status on ("active" is TRUE) or off ("active" is FALSE).
+ */
+ void
+im_set_active(int active)
+{
+ HIMC hImc;
+ static HIMC hImcOld = (HIMC)0;
+
+ if (pImmGetContext) /* if NULL imm32.dll wasn't loaded (yet) */
+ {
+ if (p_imdisable)
+ {
+ if (hImcOld == (HIMC)0)
+ {
+ hImcOld = pImmGetContext(s_hwnd);
+ if (hImcOld)
+ pImmAssociateContext(s_hwnd, (HIMC)0);
+ }
+ active = FALSE;
+ }
+ else if (hImcOld != (HIMC)0)
+ {
+ pImmAssociateContext(s_hwnd, hImcOld);
+ hImcOld = (HIMC)0;
+ }
+
+ hImc = pImmGetContext(s_hwnd);
+ if (hImc)
+ {
+ pImmSetOpenStatus(hImc, active);
+ pImmReleaseContext(s_hwnd, hImc);
+ }
+ }
+}
+
+/*
+ * Get IM status. When IM is on, return not 0. Else return 0.
+ */
+ int
+im_get_status()
+{
+ int status = 0;
+ HIMC hImc;
+
+ if (pImmGetContext && (hImc = pImmGetContext(s_hwnd)) != (HIMC)0)
+ {
+ status = pImmGetOpenStatus(hImc) ? 1 : 0;
+ pImmReleaseContext(s_hwnd, hImc);
+ }
+ return status;
+}
+
+#endif /* FEAT_MBYTE && FEAT_MBYTE_IME */
+
+#if defined(FEAT_MBYTE) && !defined(FEAT_MBYTE_IME) && defined(GLOBAL_IME)
+/* Win32 with GLOBAL IME */
+
+/*
+ * Notify cursor position to IM.
+ */
+ void
+im_set_position(int row, int col)
+{
+ /* Win32 with GLOBAL IME */
+ POINT p;
+
+ p.x = FILL_X(col);
+ p.y = FILL_Y(row);
+ MapWindowPoints(s_textArea, s_hwnd, &p, 1);
+ global_ime_set_position(&p);
+}
+
+/*
+ * Set IM status on ("active" is TRUE) or off ("active" is FALSE).
+ */
+ void
+im_set_active(int active)
+{
+ global_ime_set_status(active);
+}
+
+/*
+ * Get IM status. When IM is on, return not 0. Else return 0.
+ */
+ int
+im_get_status()
+{
+ return global_ime_get_status();
+}
+#endif
+
+
+#ifdef FEAT_RIGHTLEFT
+/*
+ * What is this for? In the case where you are using Win98 or Win2K or later,
+ * and you are using a Hebrew font (or Arabic!), Windows does you a favor and
+ * reverses the string sent to the TextOut... family. This sucks, because we
+ * go to a lot of effort to do the right thing, and there doesn't seem to be a
+ * way to tell Windblows not to do this!
+ *
+ * The short of it is that this 'RevOut' only gets called if you are running
+ * one of the new, "improved" MS OSes, and only if you are running in
+ * 'rightleft' mode. It makes display take *slightly* longer, but not
+ * noticeably so.
+ */
+ static void
+RevOut( HDC s_hdc,
+ int col,
+ int row,
+ UINT foptions,
+ CONST RECT *pcliprect,
+ LPCTSTR text,
+ UINT len,
+ CONST INT *padding)
+{
+ int ix;
+ static int special = -1;
+
+ if (special == -1)
+ {
+ /* Check windows version: special treatment is needed if it is NT 5 or
+ * Win98 or higher. */
+ if ((os_version.dwPlatformId == VER_PLATFORM_WIN32_NT
+ && os_version.dwMajorVersion >= 5)
+ || (os_version.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS
+ && (os_version.dwMajorVersion > 4
+ || (os_version.dwMajorVersion == 4
+ && os_version.dwMinorVersion > 0))))
+ special = 1;
+ else
+ special = 0;
+ }
+
+ if (special)
+ for (ix = 0; ix < (int)len; ++ix)
+ ExtTextOut(s_hdc, col + TEXT_X(ix), row, foptions,
+ pcliprect, text + ix, 1, padding);
+ else
+ ExtTextOut(s_hdc, col, row, foptions, pcliprect, text, len, padding);
+}
+#endif
+
+ void
+gui_mch_draw_string(
+ int row,
+ int col,
+ char_u *text,
+ int len,
+ int flags)
+{
+ static int *padding = NULL;
+ static int pad_size = 0;
+ int i;
+ const RECT *pcliprect = NULL;
+ UINT foptions = 0;
+#ifdef FEAT_MBYTE
+ static WCHAR *unicodebuf = NULL;
+ static int *unicodepdy = NULL;
+ int unibuflen = 0;
+ int n = 0;
+#endif
+ HPEN hpen, old_pen;
+ int y;
+
+#ifndef MSWIN16_FASTTEXT
+ /*
+ * Italic and bold text seems to have an extra row of pixels at the bottom
+ * (below where the bottom of the character should be). If we draw the
+ * characters with a solid background, the top row of pixels in the
+ * character below will be overwritten. We can fix this by filling in the
+ * background ourselves, to the correct character proportions, and then
+ * writing the character in transparent mode. Still have a problem when
+ * the character is "_", which gets written on to the character below.
+ * New fix: set gui.char_ascent to -1. This shifts all characters up one
+ * pixel in their slots, which fixes the problem with the bottom row of
+ * pixels. We still need this code because otherwise the top row of pixels
+ * becomes a problem. - webb.
+ */
+ static HBRUSH hbr_cache[2] = {NULL, NULL};
+ static guicolor_T brush_color[2] = {INVALCOLOR, INVALCOLOR};
+ static int brush_lru = 0;
+ HBRUSH hbr;
+ RECT rc;
+
+ if (!(flags & DRAW_TRANSP))
+ {
+ /*
+ * Clear background first.
+ * Note: FillRect() excludes right and bottom of rectangle.
+ */
+ rc.left = FILL_X(col);
+ rc.top = FILL_Y(row);
+#ifdef FEAT_MBYTE
+ if (has_mbyte)
+ {
+ int cell_len = 0;
+
+ /* Compute the length in display cells. */
+ for (n = 0; n < len; n += MB_BYTE2LEN(text[n]))
+ cell_len += (*mb_ptr2cells)(text + n);
+ rc.right = FILL_X(col + cell_len);
+ }
+ else
+#endif
+ rc.right = FILL_X(col + len);
+ rc.bottom = FILL_Y(row + 1);
+
+ /* Cache the created brush, that saves a lot of time. We need two:
+ * one for cursor background and one for the normal background. */
+ if (gui.currBgColor == brush_color[0])
+ {
+ hbr = hbr_cache[0];
+ brush_lru = 1;
+ }
+ else if (gui.currBgColor == brush_color[1])
+ {
+ hbr = hbr_cache[1];
+ brush_lru = 0;
+ }
+ else
+ {
+ if (hbr_cache[brush_lru] != NULL)
+ DeleteBrush(hbr_cache[brush_lru]);
+ hbr_cache[brush_lru] = CreateSolidBrush(gui.currBgColor);
+ brush_color[brush_lru] = gui.currBgColor;
+ hbr = hbr_cache[brush_lru];
+ brush_lru = !brush_lru;
+ }
+ FillRect(s_hdc, &rc, hbr);
+
+ SetBkMode(s_hdc, TRANSPARENT);
+
+ /*
+ * When drawing block cursor, prevent inverted character spilling
+ * over character cell (can happen with bold/italic)
+ */
+ if (flags & DRAW_CURSOR)
+ {
+ pcliprect = &rc;
+ foptions = ETO_CLIPPED;
+ }
+ }
+#else
+ /*
+ * The alternative would be to write the characters in opaque mode, but
+ * when the text is not exactly the same proportions as normal text, too
+ * big or too little a rectangle gets drawn for the background.
+ */
+ SetBkMode(s_hdc, OPAQUE);
+ SetBkColor(s_hdc, gui.currBgColor);
+#endif
+ SetTextColor(s_hdc, gui.currFgColor);
+ SelectFont(s_hdc, gui.currFont);
+
+ if (pad_size != Columns || padding == NULL || padding[0] != gui.char_width)
+ {
+ vim_free(padding);
+ pad_size = Columns;
+
+ padding = (int *)alloc(pad_size * sizeof(int));
+ if (padding != NULL)
+ for (i = 0; i < pad_size; i++)
+ padding[i] = gui.char_width;
+ }
+
+ /* On NT, tell the font renderer not to "help" us with Hebrew and Arabic
+ * text. This doesn't work in 9x, so we have to deal with it manually on
+ * those systems. */
+ if (os_version.dwPlatformId == VER_PLATFORM_WIN32_NT)
+ foptions |= ETO_IGNORELANGUAGE;
+
+ /*
+ * We have to provide the padding argument because italic and bold versions
+ * of fixed-width fonts are often one pixel or so wider than their normal
+ * versions.
+ * No check for DRAW_BOLD, Windows will have done it already.
+ */
+
+#ifdef FEAT_MBYTE
+ /* Check if there are any UTF-8 characters. If not, use normal text
+ * output to speed up output. */
+ if (enc_utf8)
+ for (n = 0; n < len; ++n)
+ if (text[n] >= 0x80)
+ break;
+
+ /* Check if the Unicode buffer exists and is big enough. Create it
+ * with the same lengt as the multi-byte string, the number of wide
+ * characters is always equal or smaller. */
+ if ((enc_utf8 || (enc_codepage > 0 && (int)GetACP() != enc_codepage))
+ && (unicodebuf == NULL || len > unibuflen))
+ {
+ vim_free(unicodebuf);
+ unicodebuf = (WCHAR *)alloc(len * sizeof(WCHAR));
+
+ vim_free(unicodepdy);
+ unicodepdy = (int *)alloc(len * sizeof(int));
+
+ unibuflen = len;
+ }
+
+ if (enc_utf8 && n < len && unicodebuf != NULL)
+ {
+ /* Output UTF-8 characters. Caller has already separated
+ * composing characters. */
+ int i = 0;
+ int clen; /* string length up to composing char */
+ int cells; /* cell width of string up to composing char */
+ int cw; /* width of current cell */
+
+ cells = 0;
+ for (clen = 0; i < len; )
+ {
+ unicodebuf[clen] = utf_ptr2char(text + i);
+ cw = utf_char2cells(unicodebuf[clen]);
+ if (cw > 2) /* don't use 4 for unprintable char */
+ cw = 1;
+ if (unicodepdy != NULL)
+ {
+ /* Use unicodepdy to make characters fit as we expect, even
+ * when the font uses different widths (e.g., bold character
+ * is wider). */
+ unicodepdy[clen] = cw * gui.char_width;
+ }
+ cells += cw;
+ i += utfc_ptr2len_check_len(text + i, len - i);
+ ++clen;
+ }
+ ExtTextOutW(s_hdc, TEXT_X(col), TEXT_Y(row),
+ foptions, pcliprect, unicodebuf, clen, unicodepdy);
+ len = cells; /* used for underlining */
+ }
+ else if (enc_codepage > 0 && (int)GetACP() != enc_codepage)
+ {
+ /* If we want to display codepage data, and the current CP is not the
+ * ANSI one, we need to go via Unicode. */
+ if (unicodebuf != NULL)
+ {
+ len = MultiByteToWideChar(enc_codepage,
+ MB_PRECOMPOSED,
+ (char *)text, len,
+ (LPWSTR)unicodebuf, unibuflen);
+ if (len != 0)
+ ExtTextOutW(s_hdc, TEXT_X(col), TEXT_Y(row),
+ foptions, pcliprect, unicodebuf, len, NULL);
+ }
+ }
+ else
+#endif
+ {
+#ifdef FEAT_RIGHTLEFT
+ /* If we can't use ETO_IGNORELANGUAGE, we can't tell Windows not to
+ * mess up RL text, so we have to draw it character-by-character.
+ * Only do this if RL is on, since it's slow. */
+ if (curwin->w_p_rl && !(foptions & ETO_IGNORELANGUAGE))
+ RevOut(s_hdc, TEXT_X(col), TEXT_Y(row),
+ foptions, pcliprect, (char *)text, len, padding);
+ else
+#endif
+ ExtTextOut(s_hdc, TEXT_X(col), TEXT_Y(row),
+ foptions, pcliprect, (char *)text, len, padding);
+ }
+
+ if (flags & DRAW_UNDERL)
+ {
+ hpen = CreatePen(PS_SOLID, 1, gui.currFgColor);
+ old_pen = SelectObject(s_hdc, hpen);
+ /* When p_linespace is 0, overwrite the bottom row of pixels.
+ * Otherwise put the line just below the character. */
+ y = FILL_Y(row + 1) - 1;
+#ifndef MSWIN16_FASTTEXT
+ if (p_linespace > 1)
+ y -= p_linespace - 1;
+#endif
+ MoveToEx(s_hdc, FILL_X(col), y, NULL);
+ /* Note: LineTo() excludes the last pixel in the line. */
+ LineTo(s_hdc, FILL_X(col + len), y);
+ DeleteObject(SelectObject(s_hdc, old_pen));
+ }
+}
+
+
+/*
+ * Output routines.
+ */
+
+/* Flush any output to the screen */
+ void
+gui_mch_flush(void)
+{
+# if defined(__BORLANDC__)
+ /*
+ * The GdiFlush declaration (in Borland C 5.01 <wingdi.h>) is not a
+ * prototype declaration.
+ * The compiler complains if __stdcall is not used in both declarations.
+ */
+ BOOL __stdcall GdiFlush(void);
+# endif
+
+ GdiFlush();
+}
+
+ static void
+clear_rect(RECT *rcp)
+{
+ HBRUSH hbr;
+
+ hbr = CreateSolidBrush(gui.back_pixel);
+ FillRect(s_hdc, rcp, hbr);
+ DeleteBrush(hbr);
+}
+
+
+#if defined(FEAT_MENU) || defined(PROTO)
+/*
+ * Add a sub menu to the menu bar.
+ */
+ void
+gui_mch_add_menu(
+ vimmenu_T *menu,
+ int pos)
+{
+ vimmenu_T *parent = menu->parent;
+
+ menu->submenu_id = CreatePopupMenu();
+ menu->id = s_menu_id++;
+
+ if (menu_is_menubar(menu->name))
+ {
+ if (is_winnt_3())
+ {
+ InsertMenu((parent == NULL) ? s_menuBar : parent->submenu_id,
+ (UINT)pos, MF_POPUP | MF_STRING | MF_BYPOSITION,
+ (UINT)menu->submenu_id, (LPCTSTR) menu->name);
+ }
+ else
+ {
+#ifdef FEAT_MBYTE
+ WCHAR *wn = NULL;
+ int n;
+
+ if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
+ {
+ /* 'encoding' differs from active codepage: convert menu name
+ * and use wide function */
+ wn = enc_to_ucs2(menu->name, NULL);
+ if (wn != NULL)
+ {
+ MENUITEMINFOW infow;
+
+ infow.cbSize = sizeof(infow);
+ infow.fMask = MIIM_DATA | MIIM_TYPE | MIIM_ID
+ | MIIM_SUBMENU;
+ infow.dwItemData = (DWORD)menu;
+ infow.wID = menu->id;
+ infow.fType = MFT_STRING;
+ infow.dwTypeData = wn;
+ infow.cch = wcslen(wn);
+ infow.hSubMenu = menu->submenu_id;
+ n = InsertMenuItemW((parent == NULL)
+ ? s_menuBar : parent->submenu_id,
+ (UINT)pos, TRUE, &infow);
+ vim_free(wn);
+ if (n == 0 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
+ /* Failed, try using non-wide function. */
+ wn = NULL;
+ }
+ }
+
+ if (wn == NULL)
+#endif
+ {
+ MENUITEMINFO info;
+
+ info.cbSize = sizeof(info);
+ info.fMask = MIIM_DATA | MIIM_TYPE | MIIM_ID | MIIM_SUBMENU;
+ info.dwItemData = (DWORD)menu;
+ info.wID = menu->id;
+ info.fType = MFT_STRING;
+ info.dwTypeData = (LPTSTR)menu->name;
+ info.cch = (UINT)STRLEN(menu->name);
+ info.hSubMenu = menu->submenu_id;
+ InsertMenuItem((parent == NULL)
+ ? s_menuBar : parent->submenu_id,
+ (UINT)pos, TRUE, &info);
+ }
+ }
+ }
+
+ /* Fix window size if menu may have wrapped */
+ if (parent == NULL)
+ gui_mswin_get_menu_height(!gui.starting);
+#ifdef FEAT_TEAROFF
+ else if (IsWindow(parent->tearoff_handle))
+ rebuild_tearoff(parent);
+#endif
+}
+
+ void
+gui_mch_show_popupmenu(vimmenu_T *menu)
+{
+ POINT mp;
+
+ (void)GetCursorPos((LPPOINT)&mp);
+ gui_mch_show_popupmenu_at(menu, (int)mp.x, (int)mp.y);
+}
+
+ void
+gui_make_popup(char_u *path_name)
+{
+ vimmenu_T *menu = gui_find_menu(path_name);
+
+ if (menu != NULL)
+ {
+ POINT p;
+
+ /* Find the position of the current cursor */
+ GetDCOrgEx(s_hdc, &p);
+ if (curwin != NULL)
+ {
+ p.x += TEXT_X(W_WINCOL(curwin) + curwin->w_wcol + 1);
+ p.y += TEXT_Y(W_WINROW(curwin) + curwin->w_wrow + 1);
+ }
+ msg_scroll = FALSE;
+ gui_mch_show_popupmenu_at(menu, (int)p.x, (int)p.y);
+ }
+}
+
+#if defined(FEAT_TEAROFF) || defined(PROTO)
+/*
+ * Given a menu descriptor, e.g. "File.New", find it in the menu hierarchy and
+ * create it as a pseudo-"tearoff menu".
+ */
+ void
+gui_make_tearoff(char_u *path_name)
+{
+ vimmenu_T *menu = gui_find_menu(path_name);
+
+ /* Found the menu, so tear it off. */
+ if (menu != NULL)
+ gui_mch_tearoff(menu->dname, menu, 0xffffL, 0xffffL);
+}
+#endif
+
+/*
+ * Add a menu item to a menu
+ */
+ void
+gui_mch_add_menu_item(
+ vimmenu_T *menu,
+ int idx)
+{
+ vimmenu_T *parent = menu->parent;
+
+ menu->id = s_menu_id++;
+ menu->submenu_id = NULL;
+
+#ifdef FEAT_TEAROFF
+ if (STRNCMP(menu->name, TEAR_STRING, TEAR_LEN) == 0)
+ {
+ InsertMenu(parent->submenu_id, (UINT)idx, MF_BITMAP|MF_BYPOSITION,
+ (UINT)menu->id, (LPCTSTR) s_htearbitmap);
+ }
+ else
+#endif
+#ifdef FEAT_TOOLBAR
+ if (menu_is_toolbar(parent->name))
+ {
+ TBBUTTON newtb;
+
+ vim_memset(&newtb, 0, sizeof(newtb));
+ if (menu_is_separator(menu->name))
+ {
+ newtb.iBitmap = 0;
+ newtb.fsStyle = TBSTYLE_SEP;
+ }
+ else
+ {
+ newtb.iBitmap = get_toolbar_bitmap(menu);
+ newtb.fsStyle = TBSTYLE_BUTTON;
+ }
+ newtb.idCommand = menu->id;
+ newtb.fsState = TBSTATE_ENABLED;
+ newtb.iString = 0;
+ SendMessage(s_toolbarhwnd, TB_INSERTBUTTON, (WPARAM)idx,
+ (LPARAM)&newtb);
+ menu->submenu_id = (HMENU)-1;
+ }
+ else
+#endif
+ {
+#ifdef FEAT_MBYTE
+ WCHAR *wn = NULL;
+ int n;
+
+ if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
+ {
+ /* 'encoding' differs from active codepage: convert menu item name
+ * and use wide function */
+ wn = enc_to_ucs2(menu->name, NULL);
+ if (wn != NULL)
+ {
+ n = InsertMenuW(parent->submenu_id, (UINT)idx,
+ (menu_is_separator(menu->name)
+ ? MF_SEPARATOR : MF_STRING) | MF_BYPOSITION,
+ (UINT)menu->id, wn);
+ vim_free(wn);
+ if (n == 0 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
+ /* Failed, try using non-wide function. */
+ wn = NULL;
+ }
+ }
+ if (wn == NULL)
+#endif
+ InsertMenu(parent->submenu_id, (UINT)idx,
+ (menu_is_separator(menu->name) ? MF_SEPARATOR : MF_STRING)
+ | MF_BYPOSITION,
+ (UINT)menu->id, (LPCTSTR)menu->name);
+#ifdef FEAT_TEAROFF
+ if (IsWindow(parent->tearoff_handle))
+ rebuild_tearoff(parent);
+#endif
+ }
+}
+
+/*
+ * Destroy the machine specific menu widget.
+ */
+ void
+gui_mch_destroy_menu(vimmenu_T *menu)
+{
+#ifdef FEAT_TOOLBAR
+ /*
+ * is this a toolbar button?
+ */
+ if (menu->submenu_id == (HMENU)-1)
+ {
+ int iButton;
+
+ iButton = (int)SendMessage(s_toolbarhwnd, TB_COMMANDTOINDEX,
+ (WPARAM)menu->id, 0);
+ SendMessage(s_toolbarhwnd, TB_DELETEBUTTON, (WPARAM)iButton, 0);
+ }
+ else
+#endif
+ {
+ if (menu->parent != NULL
+ && menu_is_popup(menu->parent->dname)
+ && menu->parent->submenu_id != NULL)
+ RemoveMenu(menu->parent->submenu_id, menu->id, MF_BYCOMMAND);
+ else
+ RemoveMenu(s_menuBar, menu->id, MF_BYCOMMAND);
+ if (menu->submenu_id != NULL)
+ DestroyMenu(menu->submenu_id);
+#ifdef FEAT_TEAROFF
+ if (IsWindow(menu->tearoff_handle))
+ DestroyWindow(menu->tearoff_handle);
+ if (menu->parent != NULL
+ && menu->parent->children != NULL
+ && IsWindow(menu->parent->tearoff_handle))
+ {
+ /* This menu must not show up when rebuilding the tearoff window. */
+ menu->modes = 0;
+ rebuild_tearoff(menu->parent);
+ }
+#endif
+ }
+}
+
+#ifdef FEAT_TEAROFF
+ static void
+rebuild_tearoff(vimmenu_T *menu)
+{
+ /*hackish*/
+ char_u tbuf[128];
+ RECT trect;
+ RECT rct;
+ RECT roct;
+ int x, y;
+
+ HWND thwnd = menu->tearoff_handle;
+
+ GetWindowText(thwnd, tbuf, 127);
+ if (GetWindowRect(thwnd, &trect)
+ && GetWindowRect(s_hwnd, &rct)
+ && GetClientRect(s_hwnd, &roct))
+ {
+ x = trect.left - rct.left;
+ y = (trect.top - rct.bottom + roct.bottom);
+ }
+ else
+ {
+ x = y = 0xffffL;
+ }
+ DestroyWindow(thwnd);
+ if (menu->children != NULL)
+ {
+ gui_mch_tearoff(tbuf, menu, x, y);
+ if (IsWindow(menu->tearoff_handle))
+ (void) SetWindowPos(menu->tearoff_handle,
+ NULL,
+ (int)trect.left,
+ (int)trect.top,
+ 0, 0,
+ SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
+ }
+}
+#endif /* FEAT_TEAROFF */
+
+/*
+ * Make a menu either grey or not grey.
+ */
+ void
+gui_mch_menu_grey(
+ vimmenu_T *menu,
+ int grey)
+{
+#ifdef FEAT_TOOLBAR
+ /*
+ * is this a toolbar button?
+ */
+ if (menu->submenu_id == (HMENU)-1)
+ {
+ SendMessage(s_toolbarhwnd, TB_ENABLEBUTTON,
+ (WPARAM)menu->id, (LPARAM) MAKELONG((grey ? FALSE : TRUE), 0) );
+ }
+ else
+#endif
+ if (grey)
+ EnableMenuItem(s_menuBar, menu->id, MF_BYCOMMAND | MF_GRAYED);
+ else
+ EnableMenuItem(s_menuBar, menu->id, MF_BYCOMMAND | MF_ENABLED);
+
+#ifdef FEAT_TEAROFF
+ if ((menu->parent != NULL) && (IsWindow(menu->parent->tearoff_handle)))
+ {
+ WORD menuID;
+ HWND menuHandle;
+
+ /*
+ * A tearoff button has changed state.
+ */
+ if (menu->children == NULL)
+ menuID = (WORD)(menu->id);
+ else
+ menuID = (WORD)((DWORD)(menu->submenu_id) | (DWORD)0x8000);
+ menuHandle = GetDlgItem(menu->parent->tearoff_handle, menuID);
+ if (menuHandle)
+ EnableWindow(menuHandle, !grey);
+
+ }
+#endif
+}
+
+#endif /* FEAT_MENU */
+
+
+/* define some macros used to make the dialogue creation more readable */
+
+#define add_string(s) strcpy((LPSTR)p, s); (LPSTR)p += (strlen((LPSTR)p) + 1)
+#define add_word(x) *p++ = (x)
+#define add_byte(x) *((LPSTR)p)++ = (x)
+#define add_long(x) *((LPDWORD)p)++ = (x)
+
+#if defined(FEAT_GUI_DIALOG) || defined(PROTO)
+/*
+ * stuff for dialogs
+ */
+
+/*
+ * The callback routine used by all the dialogs. Very simple. First,
+ * acknowledges the INITDIALOG message so that Windows knows to do standard
+ * dialog stuff (Return = default, Esc = cancel....) Second, if a button is
+ * pressed, return that button's ID - IDCANCEL (2), which is the button's
+ * number.
+ */
+ static LRESULT CALLBACK
+dialog_callback(
+ HWND hwnd,
+ UINT message,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ if (message == WM_INITDIALOG)
+ {
+ CenterWindow(hwnd, GetWindow(hwnd, GW_OWNER));
+ /* Set focus to the dialog. Set the default button, if specified. */
+ (void)SetFocus(hwnd);
+ if (dialog_default_button > IDCANCEL)
+ (void)SetFocus(GetDlgItem(hwnd, dialog_default_button));
+ return FALSE;
+ }
+
+ if (message == WM_COMMAND)
+ {
+ int button = LOWORD(wParam);
+
+ /* Don't end the dialog if something was selected that was
+ * not a button.
+ */
+ if (button >= DLG_NONBUTTON_CONTROL)
+ return TRUE;
+
+ /* If the edit box exists, copy the string. */
+ if (s_textfield != NULL)
+ GetDlgItemText(hwnd, DLG_NONBUTTON_CONTROL + 2,
+ s_textfield, IOSIZE);
+
+ /*
+ * Need to check for IDOK because if the user just hits Return to
+ * accept the default value, some reason this is what we get.
+ */
+ if (button == IDOK)
+ {
+ if (dialog_default_button > IDCANCEL)
+ EndDialog(hwnd, dialog_default_button);
+ }
+ else
+ EndDialog(hwnd, button - IDCANCEL);
+ return TRUE;
+ }
+
+ if ((message == WM_SYSCOMMAND) && (wParam == SC_CLOSE))
+ {
+ EndDialog(hwnd, 0);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*
+ * Create a dialog dynamically from the parameter strings.
+ * type = type of dialog (question, alert, etc.)
+ * title = dialog title. may be NULL for default title.
+ * message = text to display. Dialog sizes to accommodate it.
+ * buttons = '\n' separated list of button captions, default first.
+ * dfltbutton = number of default button.
+ *
+ * This routine returns 1 if the first button is pressed,
+ * 2 for the second, etc.
+ *
+ * 0 indicates Esc was pressed.
+ * -1 for unexpected error
+ *
+ * If stubbing out this fn, return 1.
+ */
+
+static const char_u *dlg_icons[] = /* must match names in resource file */
+{
+ "IDR_VIM",
+ "IDR_VIM_ERROR",
+ "IDR_VIM_ALERT",
+ "IDR_VIM_INFO",
+ "IDR_VIM_QUESTION"
+};
+
+#ifdef USE_SYSMENU_FONT
+/*
+ * Get Menu Font.
+ * Return OK or FAIL.
+ */
+ static int
+gui_w32_get_menu_font(LOGFONT *lf)
+{
+ NONCLIENTMETRICS nm;
+
+ nm.cbSize = sizeof(NONCLIENTMETRICS);
+ if (!SystemParametersInfo(
+ SPI_GETNONCLIENTMETRICS,
+ sizeof(NONCLIENTMETRICS),
+ &nm,
+ 0))
+ return FAIL;
+ *lf = nm.lfMenuFont;
+ return OK;
+}
+#endif
+
+ int
+gui_mch_dialog(
+ int type,
+ char_u *title,
+ char_u *message,
+ char_u *buttons,
+ int dfltbutton,
+ char_u *textfield)
+{
+ WORD *p, *pdlgtemplate, *pnumitems;
+ int numButtons;
+ int *buttonWidths, *buttonPositions;
+ int buttonYpos;
+ int nchar, i;
+ DWORD lStyle;
+ int dlgwidth = 0;
+ int dlgheight;
+ int editboxheight;
+ int horizWidth = 0;
+ int msgheight;
+ char_u *pstart;
+ char_u *pend;
+ char_u *tbuffer;
+ RECT rect;
+ HWND hwnd;
+ HDC hdc;
+ HFONT font, oldFont;
+ TEXTMETRIC fontInfo;
+ int fontHeight;
+ int textWidth, minButtonWidth, messageWidth;
+ int maxDialogWidth;
+ int vertical;
+ int dlgPaddingX;
+ int dlgPaddingY;
+#ifdef USE_SYSMENU_FONT
+ LOGFONT lfSysmenu;
+ int use_lfSysmenu = FALSE;
+#endif
+
+#ifndef NO_CONSOLE
+ /* Don't output anything in silent mode ("ex -s") */
+ if (silent_mode)
+ return dfltbutton; /* return default option */
+#endif
+
+ /* If there is no window yet, open it. */
+ if (s_hwnd == NULL && gui_mch_init() == FAIL)
+ return dfltbutton;
+
+ if ((type < 0) || (type > VIM_LAST_TYPE))
+ type = 0;
+
+ /* allocate some memory for dialog template */
+ /* TODO should compute this really*/
+ pdlgtemplate = p = (PWORD)LocalAlloc(LPTR, DLG_ALLOC_SIZE);
+
+ if (p == NULL)
+ return -1;
+
+ /*
+ * make a copy of 'buttons' to fiddle with it. complier grizzles because
+ * vim_strsave() doesn't take a const arg (why not?), so cast away the
+ * const.
+ */
+ tbuffer = vim_strsave(buttons);
+ if (tbuffer == NULL)
+ return -1;
+
+ --dfltbutton; /* Change from one-based to zero-based */
+
+ /* Count buttons */
+ numButtons = 1;
+ for (i = 0; tbuffer[i] != '\0'; i++)
+ {
+ if (tbuffer[i] == DLG_BUTTON_SEP)
+ numButtons++;
+ }
+ if (dfltbutton >= numButtons)
+ dfltbutton = -1;
+
+ /* Allocate array to hold the width of each button */
+ buttonWidths = (int *) lalloc(numButtons * sizeof(int), TRUE);
+ if (buttonWidths == NULL)
+ return -1;
+
+ /* Allocate array to hold the X position of each button */
+ buttonPositions = (int *) lalloc(numButtons * sizeof(int), TRUE);
+ if (buttonPositions == NULL)
+ return -1;
+
+ /*
+ * Calculate how big the dialog must be.
+ */
+ hwnd = GetDesktopWindow();
+ hdc = GetWindowDC(hwnd);
+#ifdef USE_SYSMENU_FONT
+ if (gui_w32_get_menu_font(&lfSysmenu) == OK)
+ {
+ font = CreateFontIndirect(&lfSysmenu);
+ use_lfSysmenu = TRUE;
+ }
+ else
+#endif
+ font = CreateFont(-DLG_FONT_POINT_SIZE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ VARIABLE_PITCH , DLG_FONT_NAME);
+ if (s_usenewlook)
+ {
+ oldFont = SelectFont(hdc, font);
+ dlgPaddingX = DLG_PADDING_X;
+ dlgPaddingY = DLG_PADDING_Y;
+ }
+ else
+ {
+ oldFont = SelectFont(hdc, GetStockObject(SYSTEM_FONT));
+ dlgPaddingX = DLG_OLD_STYLE_PADDING_X;
+ dlgPaddingY = DLG_OLD_STYLE_PADDING_Y;
+ }
+ GetTextMetrics(hdc, &fontInfo);
+ fontHeight = fontInfo.tmHeight;
+
+ /* Minimum width for horizontal button */
+ minButtonWidth = GetTextWidth(hdc, "Cancel", 6);
+
+ /* Maximum width of a dialog, if possible */
+ GetWindowRect(s_hwnd, &rect);
+ maxDialogWidth = rect.right - rect.left
+ - GetSystemMetrics(SM_CXFRAME) * 2;
+ if (maxDialogWidth < DLG_MIN_MAX_WIDTH)
+ maxDialogWidth = DLG_MIN_MAX_WIDTH;
+
+ /* Set dlgwidth to width of message */
+ pstart = message;
+ messageWidth = 0;
+ msgheight = 0;
+ do
+ {
+ pend = vim_strchr(pstart, DLG_BUTTON_SEP);
+ if (pend == NULL)
+ pend = pstart + STRLEN(pstart); /* Last line of message. */
+ msgheight += fontHeight;
+ textWidth = GetTextWidth(hdc, pstart, (int)(pend - pstart));
+ if (textWidth > messageWidth)
+ messageWidth = textWidth;
+ pstart = pend + 1;
+ } while (*pend != NUL);
+ dlgwidth = messageWidth;
+
+ /* Add width of icon to dlgwidth, and some space */
+ dlgwidth += DLG_ICON_WIDTH + 3 * dlgPaddingX;
+
+ if (msgheight < DLG_ICON_HEIGHT)
+ msgheight = DLG_ICON_HEIGHT;
+
+ /*
+ * Check button names. A long one will make the dialog wider.
+ */
+ vertical = (vim_strchr(p_go, GO_VERTICAL) != NULL);
+ if (!vertical)
+ {
+ // Place buttons horizontally if they fit.
+ horizWidth = dlgPaddingX;
+ pstart = tbuffer;
+ i = 0;
+ do
+ {
+ pend = vim_strchr(pstart, DLG_BUTTON_SEP);
+ if (pend == NULL)
+ pend = pstart + STRLEN(pstart); // Last button name.
+ textWidth = GetTextWidth(hdc, pstart, (int)(pend - pstart));
+ if (textWidth < minButtonWidth)
+ textWidth = minButtonWidth;
+ textWidth += dlgPaddingX; /* Padding within button */
+ buttonWidths[i] = textWidth;
+ buttonPositions[i++] = horizWidth;
+ horizWidth += textWidth + dlgPaddingX; /* Pad between buttons */
+ pstart = pend + 1;
+ } while (*pend != NUL);
+
+ if (horizWidth > maxDialogWidth)
+ vertical = TRUE; // Too wide to fit on the screen.
+ else if (horizWidth > dlgwidth)
+ dlgwidth = horizWidth;
+ }
+
+ if (vertical)
+ {
+ // Stack buttons vertically.
+ pstart = tbuffer;
+ do
+ {
+ pend = vim_strchr(pstart, DLG_BUTTON_SEP);
+ if (pend == NULL)
+ pend = pstart + STRLEN(pstart); // Last button name.
+ textWidth = GetTextWidth(hdc, pstart, (int)(pend - pstart));
+ textWidth += dlgPaddingX; /* Padding within button */
+ textWidth += DLG_VERT_PADDING_X * 2; /* Padding around button */
+ if (textWidth > dlgwidth)
+ dlgwidth = textWidth;
+ pstart = pend + 1;
+ } while (*pend != NUL);
+ }
+
+ if (dlgwidth < DLG_MIN_WIDTH)
+ dlgwidth = DLG_MIN_WIDTH; /* Don't allow a really thin dialog!*/
+
+ /* start to fill in the dlgtemplate information. addressing by WORDs */
+ if (s_usenewlook)
+ lStyle = DS_MODALFRAME | WS_CAPTION |DS_3DLOOK| WS_VISIBLE |DS_SETFONT;
+ else
+ lStyle = DS_MODALFRAME | WS_CAPTION |DS_3DLOOK| WS_VISIBLE;
+
+ add_long(lStyle);
+ add_long(0); // (lExtendedStyle)
+ pnumitems = p; /*save where the number of items must be stored*/
+ add_word(0); // NumberOfItems(will change later)
+ add_word(10); // x
+ add_word(10); // y
+ add_word(PixelToDialogX(dlgwidth)); // cx
+
+ // Dialog height.
+ if (vertical)
+ dlgheight = msgheight + 2 * dlgPaddingY +
+ DLG_VERT_PADDING_Y + 2 * fontHeight * numButtons;
+ else
+ dlgheight = msgheight + 3 * dlgPaddingY + 2 * fontHeight;
+
+ // Dialog needs to be taller if contains an edit box.
+ editboxheight = fontHeight + dlgPaddingY + 4 * DLG_VERT_PADDING_Y;
+ if (textfield != NULL)
+ dlgheight += editboxheight;
+
+ add_word(PixelToDialogY(dlgheight));
+
+ add_word(0); // Menu
+ add_word(0); // Class
+
+ /* copy the title of the dialog */
+ nchar = nCopyAnsiToWideChar(p, (title ?
+ (LPSTR)title :
+ (LPSTR)("Vim "VIM_VERSION_MEDIUM)));
+ p += nchar;
+
+ if (s_usenewlook)
+ {
+ /* do the font, since DS_3DLOOK doesn't work properly */
+#ifdef USE_SYSMENU_FONT
+ if (use_lfSysmenu)
+ {
+ /* point size */
+ *p++ = -MulDiv(lfSysmenu.lfHeight, 72,
+ GetDeviceCaps(hdc, LOGPIXELSY));
+ nchar = nCopyAnsiToWideChar(p, TEXT(lfSysmenu.lfFaceName));
+ }
+ else
+#endif
+ {
+ *p++ = DLG_FONT_POINT_SIZE; // point size
+ nchar = nCopyAnsiToWideChar(p, TEXT(DLG_FONT_NAME));
+ }
+ p += nchar;
+ }
+
+ buttonYpos = msgheight + 2 * dlgPaddingY;
+
+ if (textfield != NULL)
+ buttonYpos += editboxheight;
+
+ pstart = tbuffer;
+ if (!vertical)
+ horizWidth = (dlgwidth - horizWidth) / 2; /* Now it's X offset */
+ for (i = 0; i < numButtons; i++)
+ {
+ /* get end of this button. */
+ for ( pend = pstart;
+ *pend && (*pend != DLG_BUTTON_SEP);
+ pend++)
+ ;
+
+ if (*pend)
+ *pend = '\0';
+
+ /*
+ * old NOTE:
+ * setting the BS_DEFPUSHBUTTON style doesn't work because Windows sets
+ * the focus to the first tab-able button and in so doing makes that
+ * the default!! Grrr. Workaround: Make the default button the only
+ * one with WS_TABSTOP style. Means user can't tab between buttons, but
+ * he/she can use arrow keys.
+ *
+ * new NOTE: BS_DEFPUSHBUTTON is required to be able to select the
+ * right buttun when hitting <Enter>. E.g., for the ":confirm quit"
+ * dialog. Also needed for when the textfield is the default control.
+ * It appears to work now (perhaps not on Win95?).
+ */
+ if (vertical)
+ {
+ p = add_dialog_element(p,
+ (i == dfltbutton
+ ? BS_DEFPUSHBUTTON : BS_PUSHBUTTON) | WS_TABSTOP,
+ PixelToDialogX(DLG_VERT_PADDING_X),
+ PixelToDialogY(buttonYpos /* TBK */
+ + 2 * fontHeight * i),
+ PixelToDialogX(dlgwidth - 2 * DLG_VERT_PADDING_X),
+ (WORD)(PixelToDialogY(2 * fontHeight) - 1),
+ (WORD)(IDCANCEL + 1 + i), (WORD)0x0080, pstart);
+ }
+ else
+ {
+ p = add_dialog_element(p,
+ (i == dfltbutton
+ ? BS_DEFPUSHBUTTON : BS_PUSHBUTTON) | WS_TABSTOP,
+ PixelToDialogX(horizWidth + buttonPositions[i]),
+ PixelToDialogY(buttonYpos), /* TBK */
+ PixelToDialogX(buttonWidths[i]),
+ (WORD)(PixelToDialogY(2 * fontHeight) - 1),
+ (WORD)(IDCANCEL + 1 + i), (WORD)0x0080, pstart);
+ }
+ pstart = pend + 1; /*next button*/
+ }
+ *pnumitems += numButtons;
+
+ /* Vim icon */
+ p = add_dialog_element(p, SS_ICON,
+ PixelToDialogX(dlgPaddingX),
+ PixelToDialogY(dlgPaddingY),
+ PixelToDialogX(DLG_ICON_WIDTH),
+ PixelToDialogY(DLG_ICON_HEIGHT),
+ DLG_NONBUTTON_CONTROL + 0, (WORD)0x0082,
+ dlg_icons[type]);
+
+ /* Dialog message */
+ p = add_dialog_element(p, SS_LEFT,
+ PixelToDialogX(2 * dlgPaddingX + DLG_ICON_WIDTH),
+ PixelToDialogY(dlgPaddingY),
+ (WORD)(PixelToDialogX(messageWidth) + 1),
+ PixelToDialogY(msgheight),
+ DLG_NONBUTTON_CONTROL + 1, (WORD)0x0082, message);
+
+ /* Edit box */
+ if (textfield != NULL)
+ {
+ p = add_dialog_element(p, ES_LEFT|ES_AUTOHSCROLL|WS_TABSTOP|WS_BORDER,
+ PixelToDialogX(2 * dlgPaddingX),
+ PixelToDialogY(2 * dlgPaddingY + msgheight),
+ PixelToDialogX(dlgwidth - 4 * dlgPaddingX),
+ PixelToDialogY(fontHeight + dlgPaddingY),
+ DLG_NONBUTTON_CONTROL + 2, (WORD)0x0081, textfield);
+ *pnumitems += 1;
+ }
+
+ *pnumitems += 2;
+
+ SelectFont(hdc, oldFont);
+ DeleteObject(font);
+ ReleaseDC(hwnd, hdc);
+
+ /* Let the dialog_callback() function know which button to make default
+ * If we have an edit box, make that the default. We also need to tell
+ * dialog_callback() if this dialog contains an edit box or not. We do
+ * this by setting s_textfield if it does.
+ */
+ if (textfield != NULL)
+ {
+ dialog_default_button = DLG_NONBUTTON_CONTROL + 2;
+ s_textfield = textfield;
+ }
+ else
+ {
+ dialog_default_button = IDCANCEL + 1 + dfltbutton;
+ s_textfield = NULL;
+ }
+
+ /* show the dialog box modally and get a return value */
+ nchar = (int)DialogBoxIndirect(
+ s_hinst,
+ (LPDLGTEMPLATE)pdlgtemplate,
+ s_hwnd,
+ (DLGPROC)dialog_callback);
+
+ LocalFree(LocalHandle(pdlgtemplate));
+ vim_free(tbuffer);
+ vim_free(buttonWidths);
+ vim_free(buttonPositions);
+
+ /* Focus back to our window (for when MDI is used). */
+ (void)SetFocus(s_hwnd);
+
+ return nchar;
+}
+
+#endif /* FEAT_GUI_DIALOG */
+/*
+ * Put a simple element (basic class) onto a dialog template in memory.
+ * return a pointer to where the next item should be added.
+ *
+ * parameters:
+ * lStyle = additional style flags
+ * (be careful, NT3.51 & Win32s will ignore the new ones)
+ * x,y = x & y positions IN DIALOG UNITS
+ * w,h = width and height IN DIALOG UNITS
+ * Id = ID used in messages
+ * clss = class ID, e.g 0x0080 for a button, 0x0082 for a static
+ * caption = usually text or resource name
+ *
+ * TODO: use the length information noted here to enable the dialog creation
+ * routines to work out more exactly how much memory they need to alloc.
+ */
+ static PWORD
+add_dialog_element(
+ PWORD p,
+ DWORD lStyle,
+ WORD x,
+ WORD y,
+ WORD w,
+ WORD h,
+ WORD Id,
+ WORD clss,
+ const char *caption)
+{
+ int nchar;
+
+ p = lpwAlign(p); /* Align to dword boundary*/
+ lStyle = lStyle | WS_VISIBLE | WS_CHILD;
+ *p++ = LOWORD(lStyle);
+ *p++ = HIWORD(lStyle);
+ *p++ = 0; // LOWORD (lExtendedStyle)
+ *p++ = 0; // HIWORD (lExtendedStyle)
+ *p++ = x;
+ *p++ = y;
+ *p++ = w;
+ *p++ = h;
+ *p++ = Id; //9 or 10 words in all
+
+ *p++ = (WORD)0xffff;
+ *p++ = clss; //2 more here
+
+ nchar = nCopyAnsiToWideChar(p, (LPSTR)caption); //strlen(caption)+1
+ p += nchar;
+
+ *p++ = 0; // advance pointer over nExtraStuff WORD - 2 more
+
+ return p; //total = 15+ (strlen(caption)) words
+ // = 30 + 2(strlen(caption) bytes reqd
+}
+
+
+/*
+ * Helper routine. Take an input pointer, return closest pointer that is
+ * aligned on a DWORD (4 byte) boundary. Taken from the Win32SDK samples.
+ */
+ static LPWORD
+lpwAlign(
+ LPWORD lpIn)
+{
+ ULONG ul;
+
+ ul = (ULONG)lpIn;
+ ul += 3;
+ ul >>= 2;
+ ul <<= 2;
+ return (LPWORD)ul;
+}
+
+/*
+ * Helper routine. Takes second parameter as Ansi string, copies it to first
+ * parameter as wide character (16-bits / char) string, and returns integer
+ * number of wide characters (words) in string (including the trailing wide
+ * char NULL). Partly taken from the Win32SDK samples.
+ */
+ static int
+nCopyAnsiToWideChar(
+ LPWORD lpWCStr,
+ LPSTR lpAnsiIn)
+{
+ int nChar = 0;
+#ifdef FEAT_MBYTE
+ int len = lstrlen(lpAnsiIn) + 1; /* include NUL character */
+ int i;
+ WCHAR *wn;
+
+ if (enc_codepage == 0 && (int)GetACP() != enc_codepage)
+ {
+ /* Not a codepage, use our own conversion function. */
+ wn = enc_to_ucs2(lpAnsiIn, NULL);
+ if (wn != NULL)
+ {
+ wcscpy(lpWCStr, wn);
+ nChar = wcslen(wn) + 1;
+ vim_free(wn);
+ }
+ }
+ if (nChar == 0)
+ /* Use Win32 conversion function. */
+ nChar = MultiByteToWideChar(
+ enc_codepage > 0 ? enc_codepage : CP_ACP,
+ MB_PRECOMPOSED,
+ lpAnsiIn, len,
+ lpWCStr, len);
+ for (i = 0; i < nChar; ++i)
+ if (lpWCStr[i] == (WORD)'\t') /* replace tabs with spaces */
+ lpWCStr[i] = (WORD)' ';
+#else
+ do
+ {
+ if (*lpAnsiIn == '\t')
+ *lpWCStr++ = (WORD)' ';
+ else
+ *lpWCStr++ = (WORD)*lpAnsiIn;
+ nChar++;
+ } while (*lpAnsiIn++);
+#endif
+
+ return nChar;
+}
+
+
+#ifdef FEAT_TEAROFF
+/*
+ * The callback function for all the modeless dialogs that make up the
+ * "tearoff menus" Very simple - forward button presses (to fool Vim into
+ * thinking its menus have been clicked), and go away when closed.
+ */
+ static LRESULT CALLBACK
+tearoff_callback(
+ HWND hwnd,
+ UINT message,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ if (message == WM_INITDIALOG)
+ return (TRUE);
+
+ /* May show the mouse pointer again. */
+ HandleMouseHide(message, lParam);
+
+ if (message == WM_COMMAND)
+ {
+ if ((WORD)(LOWORD(wParam)) & 0x8000)
+ {
+ POINT mp;
+ RECT rect;
+
+ if (GetCursorPos(&mp) && GetWindowRect(hwnd, &rect))
+ {
+ (void)TrackPopupMenu(
+ (HMENU)(LOWORD(wParam) ^ 0x8000),
+ TPM_LEFTALIGN | TPM_LEFTBUTTON,
+ (int)rect.right - 8,
+ (int)mp.y,
+ (int)0, /*reserved param*/
+ s_hwnd,
+ NULL);
+ /*
+ * NOTE: The pop-up menu can eat the mouse up event.
+ * We deal with this in normal.c.
+ */
+ }
+ }
+ else
+ /* Pass on messages to the main Vim window */
+ PostMessage(s_hwnd, WM_COMMAND, LOWORD(wParam), 0);
+ /*
+ * Give main window the focus back: this is so after
+ * choosing a tearoff button you can start typing again
+ * straight away.
+ */
+ (void)SetFocus(s_hwnd);
+ return TRUE;
+ }
+ if ((message == WM_SYSCOMMAND) && (wParam == SC_CLOSE))
+ {
+ DestroyWindow(hwnd);
+ return TRUE;
+ }
+
+ /* When moved around, give main window the focus back. */
+ if (message == WM_EXITSIZEMOVE)
+ (void)SetActiveWindow(s_hwnd);
+
+ return FALSE;
+}
+#endif
+
+
+/*
+ * Decide whether to use the "new look" (small, non-bold font) or the "old
+ * look" (big, clanky font) for dialogs, and work out a few values for use
+ * later accordingly.
+ */
+ static void
+get_dialog_font_metrics(void)
+{
+ HDC hdc;
+ HFONT hfontTools = 0;
+ DWORD dlgFontSize;
+ SIZE size;
+#ifdef USE_SYSMENU_FONT
+ LOGFONT lfSysmenu;
+#endif
+
+ s_usenewlook = FALSE;
+
+ /*
+ * For NT3.51 and Win32s, we stick with the old look
+ * because it matches everything else.
+ */
+ if (!is_winnt_3())
+ {
+#ifdef USE_SYSMENU_FONT
+ if (gui_w32_get_menu_font(&lfSysmenu) == OK)
+ hfontTools = CreateFontIndirect(&lfSysmenu);
+ else
+#endif
+ hfontTools = CreateFont(-DLG_FONT_POINT_SIZE, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, VARIABLE_PITCH , DLG_FONT_NAME);
+
+ if (hfontTools)
+ {
+ hdc = GetDC(s_hwnd);
+ SelectObject(hdc, hfontTools);
+ /*
+ * GetTextMetrics() doesn't return the right value in
+ * tmAveCharWidth, so we have to figure out the dialog base units
+ * ourselves.
+ */
+ GetTextExtentPoint(hdc,
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
+ 52, &size);
+ ReleaseDC(s_hwnd, hdc);
+
+ s_dlgfntwidth = (WORD)((size.cx / 26 + 1) / 2);
+ s_dlgfntheight = (WORD)size.cy;
+ s_usenewlook = TRUE;
+ }
+ }
+
+ if (!s_usenewlook)
+ {
+ dlgFontSize = GetDialogBaseUnits(); /* fall back to big old system*/
+ s_dlgfntwidth = LOWORD(dlgFontSize);
+ s_dlgfntheight = HIWORD(dlgFontSize);
+ }
+}
+
+#if defined(FEAT_MENU) && defined(FEAT_TEAROFF)
+/*
+ * Create a pseudo-"tearoff menu" based on the child
+ * items of a given menu pointer.
+ */
+ static void
+gui_mch_tearoff(
+ char_u *title,
+ vimmenu_T *menu,
+ int initX,
+ int initY)
+{
+ WORD *p, *pdlgtemplate, *pnumitems, *ptrueheight;
+ int template_len;
+ int nchar, textWidth, submenuWidth;
+ DWORD lStyle;
+ DWORD lExtendedStyle;
+ WORD dlgwidth;
+ WORD menuID;
+ vimmenu_T *pmenu;
+ vimmenu_T *the_menu = menu;
+ HWND hwnd;
+ HDC hdc;
+ HFONT font, oldFont;
+ int col, spaceWidth, len;
+ int columnWidths[2];
+ char_u *label, *text;
+ int acLen = 0;
+ int nameLen;
+ int padding0, padding1, padding2 = 0;
+ int sepPadding=0;
+#ifdef USE_SYSMENU_FONT
+ LOGFONT lfSysmenu;
+ int use_lfSysmenu = FALSE;
+#endif
+
+ /*
+ * If this menu is already torn off, move it to the mouse position.
+ */
+ if (IsWindow(menu->tearoff_handle))
+ {
+ POINT mp;
+ if (GetCursorPos((LPPOINT)&mp))
+ {
+ SetWindowPos(menu->tearoff_handle, NULL, mp.x, mp.y, 0, 0,
+ SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
+ }
+ return;
+ }
+
+ /*
+ * Create a new tearoff.
+ */
+ if (*title == MNU_HIDDEN_CHAR)
+ title++;
+
+ /* Allocate memory to store the dialog template. It's made bigger when
+ * needed. */
+ template_len = DLG_ALLOC_SIZE;
+ pdlgtemplate = p = (WORD *)LocalAlloc(LPTR, template_len);
+ if (p == NULL)
+ return;
+
+ hwnd = GetDesktopWindow();
+ hdc = GetWindowDC(hwnd);
+#ifdef USE_SYSMENU_FONT
+ if (gui_w32_get_menu_font(&lfSysmenu) == OK)
+ {
+ font = CreateFontIndirect(&lfSysmenu);
+ use_lfSysmenu = TRUE;
+ }
+ else
+#endif
+ font = CreateFont(-DLG_FONT_POINT_SIZE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ VARIABLE_PITCH , DLG_FONT_NAME);
+ if (s_usenewlook)
+ oldFont = SelectFont(hdc, font);
+ else
+ oldFont = SelectFont(hdc, GetStockObject(SYSTEM_FONT));
+
+ /* Calculate width of a single space. Used for padding columns to the
+ * right width. */
+ spaceWidth = GetTextWidth(hdc, " ", 1);
+
+ /* Figure out max width of the text column, the accelerator column and the
+ * optional submenu column. */
+ submenuWidth = 0;
+ for (col = 0; col < 2; col++)
+ {
+ columnWidths[col] = 0;
+ for (pmenu = menu->children; pmenu != NULL; pmenu = pmenu->next)
+ {
+ /* Use "dname" here to compute the width of the visible text. */
+ text = (col == 0) ? pmenu->dname : pmenu->actext;
+ if (text != NULL && *text != NUL)
+ {
+ textWidth = GetTextWidthEnc(hdc, text, (int)STRLEN(text));
+ if (textWidth > columnWidths[col])
+ columnWidths[col] = textWidth;
+ }
+ if (pmenu->children != NULL)
+ submenuWidth = TEAROFF_COLUMN_PADDING * spaceWidth;
+ }
+ }
+ if (columnWidths[1] == 0)
+ {
+ /* no accelerators */
+ if (submenuWidth != 0)
+ columnWidths[0] += submenuWidth;
+ else
+ columnWidths[0] += spaceWidth;
+ }
+ else
+ {
+ /* there is an accelerator column */
+ columnWidths[0] += TEAROFF_COLUMN_PADDING * spaceWidth;
+ columnWidths[1] += submenuWidth;
+ }
+
+ /*
+ * Now find the total width of our 'menu'.
+ */
+ textWidth = columnWidths[0] + columnWidths[1];
+ if (submenuWidth != 0)
+ {
+ submenuWidth = GetTextWidth(hdc, TEAROFF_SUBMENU_LABEL,
+ (int)STRLEN(TEAROFF_SUBMENU_LABEL));
+ textWidth += submenuWidth;
+ }
+ dlgwidth = GetTextWidthEnc(hdc, title, (int)STRLEN(title));
+ if (textWidth > dlgwidth)
+ dlgwidth = textWidth;
+ dlgwidth += 2 * TEAROFF_PADDING_X + TEAROFF_BUTTON_PAD_X;
+
+ /* W95 can't do thin dialogs, they look v. weird! */
+ if (mch_windows95() && dlgwidth < TEAROFF_MIN_WIDTH)
+ dlgwidth = TEAROFF_MIN_WIDTH;
+
+ /* start to fill in the dlgtemplate information. addressing by WORDs */
+ if (s_usenewlook)
+ lStyle = DS_MODALFRAME | WS_CAPTION| WS_SYSMENU |DS_SETFONT| WS_VISIBLE;
+ else
+ lStyle = DS_MODALFRAME | WS_CAPTION| WS_SYSMENU | WS_VISIBLE;
+
+ lExtendedStyle = WS_EX_TOOLWINDOW|WS_EX_STATICEDGE;
+ *p++ = LOWORD(lStyle);
+ *p++ = HIWORD(lStyle);
+ *p++ = LOWORD(lExtendedStyle);
+ *p++ = HIWORD(lExtendedStyle);
+ pnumitems = p; /* save where the number of items must be stored */
+ *p++ = 0; // NumberOfItems(will change later)
+ if (initX == 0xffffL)
+ *p++ = PixelToDialogX(gui_mch_get_mouse_x()); // x
+ else
+ *p++ = PixelToDialogX(initX); // x
+ if (initY == 0xffffL)
+ *p++ = PixelToDialogY(gui_mch_get_mouse_y()); // y
+ else
+ *p++ = PixelToDialogY(initY); // y
+ *p++ = PixelToDialogX(dlgwidth); // cx
+ ptrueheight = p;
+ *p++ = 0; // dialog height: changed later anyway
+ *p++ = 0; // Menu
+ *p++ = 0; // Class
+
+ /* copy the title of the dialog */
+ nchar = nCopyAnsiToWideChar(p, ((*title)
+ ? (LPSTR)title
+ : (LPSTR)("Vim "VIM_VERSION_MEDIUM)));
+ p += nchar;
+
+ if (s_usenewlook)
+ {
+ /* do the font, since DS_3DLOOK doesn't work properly */
+#ifdef USE_SYSMENU_FONT
+ if (use_lfSysmenu)
+ {
+ /* point size */
+ *p++ = -MulDiv(lfSysmenu.lfHeight, 72,
+ GetDeviceCaps(hdc, LOGPIXELSY));
+ nchar = nCopyAnsiToWideChar(p, TEXT(lfSysmenu.lfFaceName));
+ }
+ else
+#endif
+ {
+ *p++ = DLG_FONT_POINT_SIZE; // point size
+ nchar = nCopyAnsiToWideChar (p, TEXT(DLG_FONT_NAME));
+ }
+ p += nchar;
+ }
+
+ /*
+ * Loop over all the items in the menu.
+ * But skip over the tearbar.
+ */
+ if (STRCMP(menu->children->name, TEAR_STRING) == 0)
+ menu = menu->children->next;
+ else
+ menu = menu->children;
+ for ( ; menu != NULL; menu = menu->next)
+ {
+ if (menu->modes == 0) /* this menu has just been deleted */
+ continue;
+ if (menu_is_separator(menu->dname))
+ {
+ sepPadding += 3;
+ continue;
+ }
+
+ /* Check if there still is plenty of room in the template. Make it
+ * larger when needed. */
+ if (((char *)p - (char *)pdlgtemplate) + 1000 > template_len)
+ {
+ WORD *newp;
+
+ newp = (WORD *)LocalAlloc(LPTR, template_len + 4096);
+ if (newp != NULL)
+ {
+ template_len += 4096;
+ mch_memmove(newp, pdlgtemplate,
+ (char *)p - (char *)pdlgtemplate);
+ p = newp + (p - pdlgtemplate);
+ pnumitems = newp + (pnumitems - pdlgtemplate);
+ ptrueheight = newp + (ptrueheight - pdlgtemplate);
+ LocalFree(LocalHandle(pdlgtemplate));
+ pdlgtemplate = newp;
+ }
+ }
+
+ /* Figure out minimal length of this menu label. Use "name" for the
+ * actual text, "dname" for estimating the displayed size. "name"
+ * has "&a" for mnemonic and includes the accelerator. */
+ len = nameLen = (int)STRLEN(menu->name);
+ padding0 = (columnWidths[0] - GetTextWidthEnc(hdc, menu->dname,
+ (int)STRLEN(menu->dname))) / spaceWidth;
+ len += padding0;
+
+ if (menu->actext != NULL)
+ {
+ acLen = (int)STRLEN(menu->actext);
+ len += acLen;
+ textWidth = GetTextWidthEnc(hdc, menu->actext, acLen);
+ }
+ else
+ textWidth = 0;
+ padding1 = (columnWidths[1] - textWidth) / spaceWidth;
+ len += padding1;
+
+ if (menu->children == NULL)
+ {
+ padding2 = submenuWidth / spaceWidth;
+ len += padding2;
+ menuID = (WORD)(menu->id);
+ }
+ else
+ {
+ len += (int)STRLEN(TEAROFF_SUBMENU_LABEL);
+ menuID = (WORD)((DWORD)(menu->submenu_id) | (DWORD)0x8000);
+ }
+
+ /* Allocate menu label and fill it in */
+ text = label = alloc((unsigned)len + 1);
+ if (label == NULL)
+ break;
+
+ STRNCPY(text, menu->name, nameLen);
+ text[nameLen] = NUL;
+ text = vim_strchr(text, TAB); /* stop at TAB before actext */
+ if (text == NULL)
+ text = label + nameLen; /* no actext, use whole name */
+ while (padding0-- > 0)
+ *text++ = ' ';
+ if (menu->actext != NULL)
+ {
+ STRNCPY(text, menu->actext, acLen);
+ text += acLen;
+ }
+ while (padding1-- > 0)
+ *text++ = ' ';
+ if (menu->children != NULL)
+ {
+ STRCPY(text, TEAROFF_SUBMENU_LABEL);
+ text += STRLEN(TEAROFF_SUBMENU_LABEL);
+ }
+ else
+ {
+ while (padding2-- > 0)
+ *text++ = ' ';
+ }
+ *text = NUL;
+
+ /*
+ * BS_LEFT will just be ignored on Win32s/NT3.5x - on
+ * W95/NT4 it makes the tear-off look more like a menu.
+ */
+ p = add_dialog_element(p,
+ BS_PUSHBUTTON|BS_LEFT,
+ (WORD)PixelToDialogX(TEAROFF_PADDING_X),
+ (WORD)(sepPadding + 1 + 13 * (*pnumitems)),
+ (WORD)PixelToDialogX(dlgwidth - 2 * TEAROFF_PADDING_X),
+ (WORD)12,
+ menuID, (WORD)0x0080, label);
+ vim_free(label);
+ (*pnumitems)++;
+ }
+
+ *ptrueheight = (WORD)(sepPadding + 1 + 13 * (*pnumitems));
+
+
+ /* show modelessly */
+ the_menu->tearoff_handle = CreateDialogIndirect(
+ s_hinst,
+ (LPDLGTEMPLATE)pdlgtemplate,
+ s_hwnd,
+ (DLGPROC)tearoff_callback);
+
+ LocalFree(LocalHandle(pdlgtemplate));
+ SelectFont(hdc, oldFont);
+ DeleteObject(font);
+ ReleaseDC(hwnd, hdc);
+
+ /*
+ * Reassert ourselves as the active window. This is so that after creating
+ * a tearoff, the user doesn't have to click with the mouse just to start
+ * typing again!
+ */
+ (void)SetActiveWindow(s_hwnd);
+
+ /* make sure the right buttons are enabled */
+ force_menu_update = TRUE;
+}
+#endif
+
+#if defined(FEAT_TOOLBAR) || defined(PROTO)
+#include "gui_w32_rc.h"
+
+/* This not defined in older SDKs */
+# ifndef TBSTYLE_FLAT
+# define TBSTYLE_FLAT 0x0800
+# endif
+
+/*
+ * Create the toolbar, initially unpopulated.
+ * (just like the menu, there are no defaults, it's all
+ * set up through menu.vim)
+ */
+ static void
+initialise_toolbar(void)
+{
+ InitCommonControls();
+ s_toolbarhwnd = CreateToolbarEx(
+ s_hwnd,
+ WS_CHILD | TBSTYLE_TOOLTIPS | TBSTYLE_FLAT,
+ 4000, //any old big number
+ 31, //number of images in inital bitmap
+ s_hinst,
+ IDR_TOOLBAR1, // id of initial bitmap
+ NULL,
+ 0, // initial number of buttons
+ TOOLBAR_BUTTON_WIDTH, //api guide is wrong!
+ TOOLBAR_BUTTON_HEIGHT,
+ TOOLBAR_BUTTON_WIDTH,
+ TOOLBAR_BUTTON_HEIGHT,
+ sizeof(TBBUTTON)
+ );
+
+ gui_mch_show_toolbar(vim_strchr(p_go, GO_TOOLBAR) != NULL);
+}
+
+ static int
+get_toolbar_bitmap(vimmenu_T *menu)
+{
+ int i = -1;
+
+ /*
+ * Check user bitmaps first, unless builtin is specified.
+ */
+ if (!is_winnt_3() && !menu->icon_builtin)
+ {
+ char_u fname[MAXPATHL];
+ HANDLE hbitmap = NULL;
+
+ if (menu->iconfile != NULL)
+ {
+ gui_find_iconfile(menu->iconfile, fname, "bmp");
+ hbitmap = LoadImage(
+ NULL,
+ fname,
+ IMAGE_BITMAP,
+ TOOLBAR_BUTTON_WIDTH,
+ TOOLBAR_BUTTON_HEIGHT,
+ LR_LOADFROMFILE |
+ LR_LOADMAP3DCOLORS
+ );
+ }
+
+ /*
+ * If the LoadImage call failed, or the "icon=" file
+ * didn't exist or wasn't specified, try the menu name
+ */
+ if (hbitmap == NULL
+ && (gui_find_bitmap(menu->name, fname, "bmp") == OK))
+ hbitmap = LoadImage(
+ NULL,
+ fname,
+ IMAGE_BITMAP,
+ TOOLBAR_BUTTON_WIDTH,
+ TOOLBAR_BUTTON_HEIGHT,
+ LR_LOADFROMFILE |
+ LR_LOADMAP3DCOLORS
+ );
+
+ if (hbitmap != NULL)
+ {
+ TBADDBITMAP tbAddBitmap;
+
+ tbAddBitmap.hInst = NULL;
+ tbAddBitmap.nID = (UINT)hbitmap;
+
+ i = (int)SendMessage(s_toolbarhwnd, TB_ADDBITMAP,
+ (WPARAM)1, (LPARAM)&tbAddBitmap);
+ /* i will be set to -1 if it fails */
+ }
+ }
+ if (i == -1 && menu->iconidx >= 0 && menu->iconidx < TOOLBAR_BITMAP_COUNT)
+ i = menu->iconidx;
+
+ return i;
+}
+#endif
+
+#if defined(FEAT_OLE) || defined(FEAT_EVAL) || defined(PROTO)
+/*
+ * Make the GUI window come to the foreground.
+ */
+ void
+gui_mch_set_foreground(void)
+{
+ if (IsIconic(s_hwnd))
+ SendMessage(s_hwnd, WM_SYSCOMMAND, SC_RESTORE, 0);
+ SetForegroundWindow(s_hwnd);
+}
+#endif
+
+#if defined(FEAT_MBYTE_IME) && defined(DYNAMIC_IME)
+ static void
+dyn_imm_load(void)
+{
+ hLibImm = LoadLibrary("imm32.dll");
+ if (hLibImm == NULL)
+ return;
+
+ pImmGetCompositionStringA
+ = (void *)GetProcAddress(hLibImm, "ImmGetCompositionStringA");
+ pImmGetCompositionStringW
+ = (void *)GetProcAddress(hLibImm, "ImmGetCompositionStringW");
+ pImmGetContext
+ = (void *)GetProcAddress(hLibImm, "ImmGetContext");
+ pImmAssociateContext
+ = (void *)GetProcAddress(hLibImm, "ImmAssociateContext");
+ pImmReleaseContext
+ = (void *)GetProcAddress(hLibImm, "ImmReleaseContext");
+ pImmGetOpenStatus
+ = (void *)GetProcAddress(hLibImm, "ImmGetOpenStatus");
+ pImmSetOpenStatus
+ = (void *)GetProcAddress(hLibImm, "ImmSetOpenStatus");
+ pImmGetCompositionFont
+ = (void *)GetProcAddress(hLibImm, "ImmGetCompositionFontA");
+ pImmSetCompositionFont
+ = (void *)GetProcAddress(hLibImm, "ImmSetCompositionFontA");
+ pImmSetCompositionWindow
+ = (void *)GetProcAddress(hLibImm, "ImmSetCompositionWindow");
+ pImmGetConversionStatus
+ = (void *)GetProcAddress(hLibImm, "ImmGetConversionStatus");
+
+ if ( pImmGetCompositionStringA == NULL
+ || pImmGetCompositionStringW == NULL
+ || pImmGetContext == NULL
+ || pImmAssociateContext == NULL
+ || pImmReleaseContext == NULL
+ || pImmGetOpenStatus == NULL
+ || pImmSetOpenStatus == NULL
+ || pImmGetCompositionFont == NULL
+ || pImmSetCompositionFont == NULL
+ || pImmSetCompositionWindow == NULL
+ || pImmGetConversionStatus == NULL)
+ {
+ FreeLibrary(hLibImm);
+ hLibImm = NULL;
+ pImmGetContext = NULL;
+ return;
+ }
+
+ return;
+}
+
+# if 0 /* not used */
+ int
+dyn_imm_unload(void)
+{
+ if (!hLibImm)
+ return FALSE;
+ FreeLibrary(hLibImm);
+ hLibImm = NULL;
+ return TRUE;
+}
+# endif
+
+#endif
+
+#if defined(FEAT_SIGN_ICONS) || defined(PROTO)
+
+# ifdef FEAT_XPM_W32
+# define IMAGE_XPM 100
+# endif
+
+typedef struct _signicon_t
+{
+ HANDLE hImage;
+ UINT uType;
+#ifdef FEAT_XPM_W32
+ HANDLE hShape; /* Mask bitmap handle */
+#endif
+} signicon_t;
+
+ void
+gui_mch_drawsign(row, col, typenr)
+ int row;
+ int col;
+ int typenr;
+{
+ signicon_t *sign;
+ int x, y, w, h;
+
+ if (!gui.in_use || (sign = (signicon_t *)sign_get_image(typenr)) == NULL)
+ return;
+
+ x = TEXT_X(col);
+ y = TEXT_Y(row);
+ w = gui.char_width * 2;
+ h = gui.char_height;
+ switch (sign->uType)
+ {
+ case IMAGE_BITMAP:
+ {
+ HDC hdcMem;
+ HBITMAP hbmpOld;
+
+ hdcMem = CreateCompatibleDC(s_hdc);
+ hbmpOld = (HBITMAP)SelectObject(hdcMem, sign->hImage);
+ BitBlt(s_hdc, x, y, w, h, hdcMem, 0, 0, SRCCOPY);
+ SelectObject(hdcMem, hbmpOld);
+ DeleteDC(hdcMem);
+ }
+ break;
+ case IMAGE_ICON:
+ case IMAGE_CURSOR:
+ DrawIconEx(s_hdc, x, y, (HICON)sign->hImage, w, h, 0, NULL, DI_NORMAL);
+ break;
+#ifdef FEAT_XPM_W32
+ case IMAGE_XPM:
+ {
+ HDC hdcMem;
+ HBITMAP hbmpOld;
+
+ hdcMem = CreateCompatibleDC(s_hdc);
+ hbmpOld = (HBITMAP)SelectObject(hdcMem, sign->hShape);
+ /* Make hole */
+ BitBlt(s_hdc, x, y, w, h, hdcMem, 0, 0, SRCAND);
+
+ SelectObject(hdcMem, sign->hImage);
+ /* Paint sign */
+ BitBlt(s_hdc, x, y, w, h, hdcMem, 0, 0, SRCPAINT);
+ SelectObject(hdcMem, hbmpOld);
+ DeleteDC(hdcMem);
+ }
+ break;
+#endif
+ }
+}
+
+ static void
+close_signicon_image(signicon_t *sign)
+{
+ if (sign)
+ switch (sign->uType)
+ {
+ case IMAGE_BITMAP:
+ DeleteObject((HGDIOBJ)sign->hImage);
+ break;
+ case IMAGE_CURSOR:
+ DestroyCursor((HCURSOR)sign->hImage);
+ break;
+ case IMAGE_ICON:
+ DestroyIcon((HICON)sign->hImage);
+ break;
+#ifdef FEAT_XPM_W32
+ case IMAGE_XPM:
+ DeleteObject((HBITMAP)sign->hImage);
+ DeleteObject((HBITMAP)sign->hShape);
+ break;
+#endif
+ }
+}
+
+ void *
+gui_mch_register_sign(signfile)
+ char_u *signfile;
+{
+ signicon_t sign, *psign;
+ char_u *ext;
+
+ if (is_winnt_3())
+ {
+ EMSG(_(e_signdata));
+ return NULL;
+ }
+
+ sign.hImage = NULL;
+ ext = signfile + STRLEN(signfile) - 4; /* get extention */
+ if (ext > signfile)
+ {
+ int do_load = 1;
+
+ if (!STRICMP(ext, ".bmp"))
+ sign.uType = IMAGE_BITMAP;
+ else if (!STRICMP(ext, ".ico"))
+ sign.uType = IMAGE_ICON;
+ else if (!STRICMP(ext, ".cur") || !STRICMP(ext, ".ani"))
+ sign.uType = IMAGE_CURSOR;
+ else
+ do_load = 0;
+
+ if (do_load)
+ sign.hImage = (HANDLE)LoadImage(NULL, signfile, sign.uType,
+ gui.char_width * 2, gui.char_height,
+ LR_LOADFROMFILE | LR_CREATEDIBSECTION);
+#ifdef FEAT_XPM_W32
+ if (!STRICMP(ext, ".xpm"))
+ {
+ sign.uType = IMAGE_XPM;
+ LoadXpmImage(signfile, (HBITMAP *)&sign.hImage, (HBITMAP *)&sign.hShape);
+ }
+#endif
+ }
+
+ psign = NULL;
+ if (sign.hImage && (psign = (signicon_t *)alloc(sizeof(signicon_t)))
+ != NULL)
+ *psign = sign;
+
+ if (!psign)
+ {
+ if (sign.hImage)
+ close_signicon_image(&sign);
+ EMSG(_(e_signdata));
+ }
+ return (void *)psign;
+
+}
+
+ void
+gui_mch_destroy_sign(sign)
+ void *sign;
+{
+ if (sign)
+ {
+ close_signicon_image((signicon_t *)sign);
+ vim_free(sign);
+ }
+}
+
+#if defined(FEAT_BEVAL) || defined(PROTO)
+
+/* BALLOON-EVAL IMPLEMENTATION FOR WINDOWS.
+ * Added by Sergey Khorev
+ *
+ * The only reused thing is gui_beval.h and gui_mch_get_beval_info()
+ * from gui_beval.c (note it uses x and y of the BalloonEval struct
+ * to get current mouse position).
+ *
+ * Trying to use as more Windows services as possible, and as less
+ * IE version as possible :)).
+ *
+ * 1) Don't create ToolTip in gui_mch_create_beval_area, only initialize
+ * BalloonEval struct.
+ * 2) Enable/Disable simply create/kill BalloonEval Timer
+ * 3) When there was enough inactivity, timer procedure posts
+ * async request to debugger
+ * 4) gui_mch_post_balloon (invoked from netbeans.c) creates tooltip control
+ * and performs some actions to show it ASAP
+ * 5) WM_NOTOFY:TTN_POP destroys created tooltip
+ */
+
+ static void
+make_tooltip(beval, text, pt)
+ BalloonEval *beval;
+ char *text;
+ POINT pt;
+{
+ TOOLINFO ti;
+
+ beval->balloon = CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS,
+ NULL, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,
+ CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
+ beval->target, NULL, s_hinst, NULL);
+
+ SetWindowPos(beval->balloon, HWND_TOPMOST, 0, 0, 0, 0,
+ SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
+
+ ti.cbSize = sizeof(TOOLINFO);
+ ti.uFlags = TTF_SUBCLASS;
+ ti.hwnd = beval->target;
+ ti.hinst = 0; /* Don't use string resources */
+ ti.uId = ID_BEVAL_TOOLTIP;
+ ti.lpszText = text;
+
+ /* Limit ballooneval bounding rect to CursorPos neighbourhood */
+ ti.rect.left = pt.x - 3;
+ ti.rect.top = pt.y - 3;
+ ti.rect.right = pt.x + 3;
+ ti.rect.bottom = pt.y + 3;
+
+ SendMessage(beval->balloon, TTM_ADDTOOL, 0, (LPARAM)&ti);
+ /* Make tooltip appear sooner */
+ SendMessage(beval->balloon, TTM_SETDELAYTIME, TTDT_INITIAL, 10);
+ /*
+ * HACK: force tooltip to appear, because it'll not appear until
+ * first mouse move. D*mn M$
+ */
+ mouse_event(MOUSEEVENTF_MOVE, 1, 1, 0, 0);
+ mouse_event(MOUSEEVENTF_MOVE, (DWORD)-1, (DWORD)-1, 0, 0);
+}
+
+ static void
+delete_tooltip(beval)
+ BalloonEval *beval;
+{
+ DestroyWindow(beval->balloon);
+}
+
+ static VOID CALLBACK
+BevalTimerProc(hwnd, uMsg, idEvent, dwTime)
+ HWND hwnd;
+ UINT uMsg;
+ UINT idEvent;
+ DWORD dwTime;
+{
+ POINT pt;
+ RECT rect;
+
+ if (cur_beval == NULL || cur_beval->showState == ShS_SHOWING || !p_beval)
+ return;
+
+ GetCursorPos(&pt);
+ if (WindowFromPoint(pt) != s_textArea)
+ return;
+
+ ScreenToClient(s_textArea, &pt);
+ GetClientRect(s_textArea, &rect);
+ if (!PtInRect(&rect, pt))
+ return;
+
+ if (LastActivity > 0
+ && (dwTime - LastActivity) >= (DWORD)p_bdlay
+ && (cur_beval->showState != ShS_PENDING
+ || abs(cur_beval->x - pt.x) > 3
+ || abs(cur_beval->y - pt.y) > 3))
+ {
+ /* Pointer resting in one place long enough, it's time to show
+ * the tooltip. */
+ cur_beval->showState = ShS_PENDING;
+ cur_beval->x = pt.x;
+ cur_beval->y = pt.y;
+
+ TRACE0("BevalTimerProc: sending request");
+
+ if (cur_beval->msgCB != NULL)
+ (*cur_beval->msgCB)(cur_beval, 0);
+ }
+}
+
+ void
+gui_mch_disable_beval_area(beval)
+ BalloonEval *beval;
+{
+ TRACE0("gui_mch_disable_beval_area {{{");
+ KillTimer(s_textArea, BevalTimerId);
+ TRACE0("gui_mch_disable_beval_area }}}");
+}
+
+ void
+gui_mch_enable_beval_area(beval)
+ BalloonEval *beval;
+{
+ TRACE0("gui_mch_enable_beval_area |||");
+ if (beval == NULL)
+ return;
+ TRACE0("gui_mch_enable_beval_area {{{");
+ BevalTimerId = SetTimer(s_textArea, 0, p_bdlay / 2, (TIMERPROC)BevalTimerProc);
+ TRACE0("gui_mch_enable_beval_area }}}");
+}
+
+ void
+gui_mch_post_balloon(beval, mesg)
+ BalloonEval *beval;
+ char_u *mesg;
+{
+ POINT pt;
+ TRACE0("gui_mch_post_balloon {{{");
+ if (beval->showState == ShS_SHOWING)
+ return;
+ GetCursorPos(&pt);
+ ScreenToClient(s_textArea, &pt);
+
+ if (abs(beval->x - pt.x) < 3 && abs(beval->y - pt.y) < 3)
+ /* cursor is still here */
+ {
+ gui_mch_disable_beval_area(cur_beval);
+ beval->showState = ShS_SHOWING;
+ make_tooltip(beval, mesg, pt);
+ }
+ TRACE0("gui_mch_post_balloon }}}");
+}
+
+ BalloonEval *
+gui_mch_create_beval_area(target, mesg, mesgCB, clientData)
+ void *target; /* ignored, always use s_textArea */
+ char_u *mesg;
+ void (*mesgCB)__ARGS((BalloonEval *, int));
+ void *clientData;
+{
+ /* partially stolen from gui_beval.c */
+ BalloonEval *beval;
+
+ if (mesg != NULL && mesgCB != NULL)
+ {
+ EMSG(_("E232: Cannot create BalloonEval with both message and callback"));
+ return NULL;
+ }
+
+ beval = (BalloonEval *)alloc(sizeof(BalloonEval));
+ if (beval != NULL)
+ {
+ beval->target = s_textArea;
+ beval->balloon = NULL;
+
+ beval->showState = ShS_NEUTRAL;
+ beval->x = 0;
+ beval->y = 0;
+ beval->msg = mesg;
+ beval->msgCB = mesgCB;
+ beval->clientData = clientData;
+
+ InitCommonControls();
+
+ cur_beval = beval;
+
+ if (p_beval)
+ gui_mch_enable_beval_area(beval);
+
+ }
+ return beval;
+}
+
+ static void
+Handle_WM_Notify(hwnd, pnmh)
+ HWND hwnd;
+ LPNMHDR pnmh;
+{
+ if (pnmh->idFrom != ID_BEVAL_TOOLTIP) /* it is not our tooltip */
+ return;
+
+ if (cur_beval != NULL)
+ {
+ if (pnmh->code == TTN_SHOW)
+ {
+ TRACE0("TTN_SHOW {{{");
+ TRACE0("TTN_SHOW }}}");
+ }
+ else if (pnmh->code == TTN_POP) /* Before tooltip disappear */
+ {
+ TRACE0("TTN_POP {{{");
+ delete_tooltip(cur_beval);
+ gui_mch_enable_beval_area(cur_beval);
+ TRACE0("TTN_POP }}}");
+
+ cur_beval->showState = ShS_NEUTRAL;
+ }
+ }
+}
+
+ static void
+TrackUserActivity(UINT uMsg)
+{
+ if ((uMsg >= WM_MOUSEFIRST && uMsg <= WM_MOUSELAST)
+ || (uMsg >= WM_KEYFIRST && uMsg <= WM_KEYLAST))
+ LastActivity = GetTickCount();
+}
+
+ void
+gui_mch_destroy_beval_area(beval)
+ BalloonEval *beval;
+{
+ vim_free(beval);
+}
+#endif /* FEAT_BEVAL */
+
+#if defined(FEAT_NETBEANS_INTG) || defined(PROTO)
+/*
+ * We have multiple signs to draw at the same location. Draw the
+ * multi-sign indicator (down-arrow) instead. This is the Win32 version.
+ */
+ void
+netbeans_draw_multisign_indicator(int row)
+{
+ int i;
+ int y;
+ int x;
+
+ x = 0;
+ y = TEXT_Y(row);
+
+ for (i = 0; i < gui.char_height - 3; i++)
+ SetPixel(s_hdc, x+2, y++, gui.currFgColor);
+
+ SetPixel(s_hdc, x+0, y, gui.currFgColor);
+ SetPixel(s_hdc, x+2, y, gui.currFgColor);
+ SetPixel(s_hdc, x+4, y++, gui.currFgColor);
+ SetPixel(s_hdc, x+1, y, gui.currFgColor);
+ SetPixel(s_hdc, x+2, y, gui.currFgColor);
+ SetPixel(s_hdc, x+3, y++, gui.currFgColor);
+ SetPixel(s_hdc, x+2, y, gui.currFgColor);
+}
+#endif
+
+#endif