diff options
Diffstat (limited to 'src/gui_w32.c')
-rw-r--r-- | src/gui_w32.c | 4077 |
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 |