blob: ef288e1e64963580c1380d97976c30cf52170cc1 [file] [log] [blame]
/* vi:set ts=8 sts=4 sw=4:
*
* VIM - Vi IMproved by Bram Moolenaar
* GUI support by Robert Webb
*
* Do ":help uganda" in Vim to read copying and usage conditions.
* Do ":help credits" in Vim to see a list of people who contributed.
* See README.txt for an overview of the Vim source code.
*/
/*
* gui_w48.c: This file is included in gui_w16.c and gui_w32.c.
*
* GUI support for Microsoft Windows (Win16 + Win32 = Win48 :-)
*
* The combined efforts of:
* George V. Reilly <george@reilly.org>
* Robert Webb
* Vince Negri
* ...and contributions from many others
*
*/
#include "vim.h"
#include "version.h" /* used by dialog box routine for default title */
#ifdef DEBUG
# include <tchar.h>
#endif
/* cproto fails on missing include files */
#ifndef PROTO
#ifndef __MINGW32__
# include <shellapi.h>
#endif
#if defined(FEAT_TOOLBAR) || defined(FEAT_BEVAL) || defined(FEAT_GUI_TABLINE)
# include <commctrl.h>
#endif
#ifdef WIN16
# include <commdlg.h>
# include <shellapi.h>
# ifdef WIN16_3DLOOK
# include <ctl3d.h>
# endif
#endif
#include <windowsx.h>
#ifdef GLOBAL_IME
# include "glbl_ime.h"
#endif
#endif /* PROTO */
#ifdef FEAT_MENU
# define MENUHINTS /* show menu hints in command line */
#endif
/* Some parameters for dialog boxes. All in pixels. */
#define DLG_PADDING_X 10
#define DLG_PADDING_Y 10
#define DLG_OLD_STYLE_PADDING_X 5
#define DLG_OLD_STYLE_PADDING_Y 5
#define DLG_VERT_PADDING_X 4 /* For vertical buttons */
#define DLG_VERT_PADDING_Y 4
#define DLG_ICON_WIDTH 34
#define DLG_ICON_HEIGHT 34
#define DLG_MIN_WIDTH 150
#define DLG_FONT_NAME "MS Sans Serif"
#define DLG_FONT_POINT_SIZE 8
#define DLG_MIN_MAX_WIDTH 400
#define DLG_MIN_MAX_HEIGHT 400
#define DLG_NONBUTTON_CONTROL 5000 /* First ID of non-button controls */
#ifndef WM_XBUTTONDOWN /* For Win2K / winME ONLY */
# define WM_XBUTTONDOWN 0x020B
# define WM_XBUTTONUP 0x020C
# define WM_XBUTTONDBLCLK 0x020D
# define MK_XBUTTON1 0x0020
# define MK_XBUTTON2 0x0040
#endif
#ifdef PROTO
/*
* Define a few things for generating prototypes. This is just to avoid
* syntax errors, the defines do not need to be correct.
*/
# define APIENTRY
# define CALLBACK
# define CONST
# define FAR
# define NEAR
# define _cdecl
typedef int BOOL;
typedef int BYTE;
typedef int DWORD;
typedef int WCHAR;
typedef int ENUMLOGFONT;
typedef int FINDREPLACE;
typedef int HANDLE;
typedef int HBITMAP;
typedef int HBRUSH;
typedef int HDROP;
typedef int INT;
typedef int LOGFONT[];
typedef int LPARAM;
typedef int LPCREATESTRUCT;
typedef int LPCSTR;
typedef int LPCTSTR;
typedef int LPRECT;
typedef int LPSTR;
typedef int LPWINDOWPOS;
typedef int LPWORD;
typedef int LRESULT;
typedef int HRESULT;
# undef MSG
typedef int MSG;
typedef int NEWTEXTMETRIC;
typedef int OSVERSIONINFO;
typedef int PWORD;
typedef int RECT;
typedef int UINT;
typedef int WORD;
typedef int WPARAM;
typedef int POINT;
typedef void *HINSTANCE;
typedef void *HMENU;
typedef void *HWND;
typedef void *HDC;
typedef void VOID;
typedef int LPNMHDR;
typedef int LONG;
typedef int WNDPROC;
#endif
#ifndef GET_X_LPARAM
# define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp))
#endif
static void _OnPaint( HWND hwnd);
static void clear_rect(RECT *rcp);
static int gui_mswin_get_menu_height(int fix_window);
static WORD s_dlgfntheight; /* height of the dialog font */
static WORD s_dlgfntwidth; /* width of the dialog font */
#ifdef FEAT_MENU
static HMENU s_menuBar = NULL;
#endif
#ifdef FEAT_TEAROFF
static void rebuild_tearoff(vimmenu_T *menu);
static HBITMAP s_htearbitmap; /* bitmap used to indicate tearoff */
#endif
/* Flag that is set while processing a message that must not be interrupted by
* processing another message. */
static int s_busy_processing = FALSE;
static int destroying = FALSE; /* call DestroyWindow() ourselves */
#ifdef MSWIN_FIND_REPLACE
static UINT s_findrep_msg = 0; /* set in gui_w[16/32].c */
static FINDREPLACE s_findrep_struct;
# if defined(FEAT_MBYTE) && defined(WIN3264)
static FINDREPLACEW s_findrep_struct_w;
# endif
static HWND s_findrep_hwnd = NULL;
static int s_findrep_is_find; /* TRUE for find dialog, FALSE
for find/replace dialog */
#endif
static HINSTANCE s_hinst = NULL;
#if !defined(FEAT_SNIFF) && !defined(FEAT_GUI)
static
#endif
HWND s_hwnd = NULL;
static HDC s_hdc = NULL;
static HBRUSH s_brush = NULL;
#ifdef FEAT_TOOLBAR
static HWND s_toolbarhwnd = NULL;
static WNDPROC s_toolbar_wndproc = NULL;
#endif
#ifdef FEAT_GUI_TABLINE
static HWND s_tabhwnd = NULL;
static WNDPROC s_tabline_wndproc = NULL;
static int showing_tabline = 0;
#endif
static WPARAM s_wParam = 0;
static LPARAM s_lParam = 0;
static HWND s_textArea = NULL;
static UINT s_uMsg = 0;
static char_u *s_textfield; /* Used by dialogs to pass back strings */
static int s_need_activate = FALSE;
/* This variable is set when waiting for an event, which is the only moment
* scrollbar dragging can be done directly. It's not allowed while commands
* are executed, because it may move the cursor and that may cause unexpected
* problems (e.g., while ":s" is working).
*/
static int allow_scrollbar = FALSE;
#ifdef GLOBAL_IME
# define MyTranslateMessage(x) global_ime_TranslateMessage(x)
#else
# define MyTranslateMessage(x) TranslateMessage(x)
#endif
#if (defined(WIN3264) && defined(FEAT_MBYTE)) || defined(GLOBAL_IME)
/* use of WindowProc depends on wide_WindowProc */
# define MyWindowProc vim_WindowProc
#else
/* use ordinary WindowProc */
# define MyWindowProc DefWindowProc
#endif
extern int current_font_height; /* this is in os_mswin.c */
static struct
{
UINT key_sym;
char_u vim_code0;
char_u vim_code1;
} special_keys[] =
{
{VK_UP, 'k', 'u'},
{VK_DOWN, 'k', 'd'},
{VK_LEFT, 'k', 'l'},
{VK_RIGHT, 'k', 'r'},
{VK_F1, 'k', '1'},
{VK_F2, 'k', '2'},
{VK_F3, 'k', '3'},
{VK_F4, 'k', '4'},
{VK_F5, 'k', '5'},
{VK_F6, 'k', '6'},
{VK_F7, 'k', '7'},
{VK_F8, 'k', '8'},
{VK_F9, 'k', '9'},
{VK_F10, 'k', ';'},
{VK_F11, 'F', '1'},
{VK_F12, 'F', '2'},
{VK_F13, 'F', '3'},
{VK_F14, 'F', '4'},
{VK_F15, 'F', '5'},
{VK_F16, 'F', '6'},
{VK_F17, 'F', '7'},
{VK_F18, 'F', '8'},
{VK_F19, 'F', '9'},
{VK_F20, 'F', 'A'},
{VK_F21, 'F', 'B'},
#ifdef FEAT_NETBEANS_INTG
{VK_PAUSE, 'F', 'B'}, /* Pause == F21 (see gui_gtk_x11.c) */
#endif
{VK_F22, 'F', 'C'},
{VK_F23, 'F', 'D'},
{VK_F24, 'F', 'E'}, /* winuser.h defines up to F24 */
{VK_HELP, '%', '1'},
{VK_BACK, 'k', 'b'},
{VK_INSERT, 'k', 'I'},
{VK_DELETE, 'k', 'D'},
{VK_HOME, 'k', 'h'},
{VK_END, '@', '7'},
{VK_PRIOR, 'k', 'P'},
{VK_NEXT, 'k', 'N'},
{VK_PRINT, '%', '9'},
{VK_ADD, 'K', '6'},
{VK_SUBTRACT, 'K', '7'},
{VK_DIVIDE, 'K', '8'},
{VK_MULTIPLY, 'K', '9'},
{VK_SEPARATOR, 'K', 'A'}, /* Keypad Enter */
{VK_DECIMAL, 'K', 'B'},
{VK_NUMPAD0, 'K', 'C'},
{VK_NUMPAD1, 'K', 'D'},
{VK_NUMPAD2, 'K', 'E'},
{VK_NUMPAD3, 'K', 'F'},
{VK_NUMPAD4, 'K', 'G'},
{VK_NUMPAD5, 'K', 'H'},
{VK_NUMPAD6, 'K', 'I'},
{VK_NUMPAD7, 'K', 'J'},
{VK_NUMPAD8, 'K', 'K'},
{VK_NUMPAD9, 'K', 'L'},
/* Keys that we want to be able to use any modifier with: */
{VK_SPACE, ' ', NUL},
{VK_TAB, TAB, NUL},
{VK_ESCAPE, ESC, NUL},
{NL, NL, NUL},
{CAR, CAR, NUL},
/* End of list marker: */
{0, 0, 0}
};
/* Local variables */
static int s_button_pending = -1;
/* s_getting_focus is set when we got focus but didn't see mouse-up event yet,
* so don't reset s_button_pending. */
static int s_getting_focus = FALSE;
static int s_x_pending;
static int s_y_pending;
static UINT s_kFlags_pending;
static UINT s_wait_timer = 0; /* Timer for get char from user */
static int s_timed_out = FALSE;
static int dead_key = 0; /* 0 - no dead key, 1 - dead key pressed */
#ifdef WIN3264
static OSVERSIONINFO os_version; /* like it says. Init in gui_mch_init() */
#endif
#ifdef FEAT_BEVAL
/* balloon-eval WM_NOTIFY_HANDLER */
static void Handle_WM_Notify __ARGS((HWND hwnd, LPNMHDR pnmh));
static void TrackUserActivity __ARGS((UINT uMsg));
#endif
/*
* For control IME.
*
* These LOGFONT used for IME.
*/
#ifdef FEAT_MBYTE
# ifdef USE_IM_CONTROL
/* holds LOGFONT for 'guifontwide' if available, otherwise 'guifont' */
static LOGFONT norm_logfont;
/* holds LOGFONT for 'guifont' always. */
static LOGFONT sub_logfont;
# endif
#endif
#ifdef FEAT_MBYTE_IME
static LRESULT _OnImeNotify(HWND hWnd, DWORD dwCommand, DWORD dwData);
#endif
#if defined(FEAT_MBYTE) && defined(WIN3264)
static char_u *convert_filter(char_u *s);
#endif
#ifdef DEBUG_PRINT_ERROR
/*
* Print out the last Windows error message
*/
static void
print_windows_error(void)
{
LPVOID lpMsgBuf;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL, GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf, 0, NULL);
TRACE1("Error: %s\n", lpMsgBuf);
LocalFree(lpMsgBuf);
}
#endif
/*
* Cursor blink functions.
*
* This is a simple state machine:
* BLINK_NONE not blinking at all
* BLINK_OFF blinking, cursor is not shown
* BLINK_ON blinking, cursor is shown
*/
#define BLINK_NONE 0
#define BLINK_OFF 1
#define BLINK_ON 2
static int blink_state = BLINK_NONE;
static long_u blink_waittime = 700;
static long_u blink_ontime = 400;
static long_u blink_offtime = 250;
static UINT blink_timer = 0;
void
gui_mch_set_blinking(long wait, long on, long off)
{
blink_waittime = wait;
blink_ontime = on;
blink_offtime = off;
}
/* ARGSUSED */
static VOID CALLBACK
_OnBlinkTimer(
HWND hwnd,
UINT uMsg,
UINT idEvent,
DWORD dwTime)
{
MSG msg;
/*
TRACE2("Got timer event, id %d, blink_timer %d\n", idEvent, blink_timer);
*/
KillTimer(NULL, idEvent);
/* Eat spurious WM_TIMER messages */
while (pPeekMessage(&msg, hwnd, WM_TIMER, WM_TIMER, PM_REMOVE))
;
if (blink_state == BLINK_ON)
{
gui_undraw_cursor();
blink_state = BLINK_OFF;
blink_timer = (UINT) SetTimer(NULL, 0, (UINT)blink_offtime,
(TIMERPROC)_OnBlinkTimer);
}
else
{
gui_update_cursor(TRUE, FALSE);
blink_state = BLINK_ON;
blink_timer = (UINT) SetTimer(NULL, 0, (UINT)blink_ontime,
(TIMERPROC)_OnBlinkTimer);
}
}
static void
gui_mswin_rm_blink_timer(void)
{
MSG msg;
if (blink_timer != 0)
{
KillTimer(NULL, blink_timer);
/* Eat spurious WM_TIMER messages */
while (pPeekMessage(&msg, s_hwnd, WM_TIMER, WM_TIMER, PM_REMOVE))
;
blink_timer = 0;
}
}
/*
* Stop the cursor blinking. Show the cursor if it wasn't shown.
*/
void
gui_mch_stop_blink(void)
{
gui_mswin_rm_blink_timer();
if (blink_state == BLINK_OFF)
gui_update_cursor(TRUE, FALSE);
blink_state = BLINK_NONE;
}
/*
* Start the cursor blinking. If it was already blinking, this restarts the
* waiting time and shows the cursor.
*/
void
gui_mch_start_blink(void)
{
gui_mswin_rm_blink_timer();
/* Only switch blinking on if none of the times is zero */
if (blink_waittime && blink_ontime && blink_offtime && gui.in_focus)
{
blink_timer = (UINT)SetTimer(NULL, 0, (UINT)blink_waittime,
(TIMERPROC)_OnBlinkTimer);
blink_state = BLINK_ON;
gui_update_cursor(TRUE, FALSE);
}
}
/*
* Call-back routines.
*/
/*ARGSUSED*/
static VOID CALLBACK
_OnTimer(
HWND hwnd,
UINT uMsg,
UINT idEvent,
DWORD dwTime)
{
MSG msg;
/*
TRACE2("Got timer event, id %d, s_wait_timer %d\n", idEvent, s_wait_timer);
*/
KillTimer(NULL, idEvent);
s_timed_out = TRUE;
/* Eat spurious WM_TIMER messages */
while (pPeekMessage(&msg, hwnd, WM_TIMER, WM_TIMER, PM_REMOVE))
;
if (idEvent == s_wait_timer)
s_wait_timer = 0;
}
/*ARGSUSED*/
static void
_OnDeadChar(
HWND hwnd,
UINT ch,
int cRepeat)
{
dead_key = 1;
}
/*
* Convert Unicode character "ch" to bytes in "string[slen]".
* When "had_alt" is TRUE the ALT key was included in "ch".
* Return the length.
*/
static int
char_to_string(int ch, char_u *string, int slen, int had_alt)
{
int len;
int i;
#ifdef FEAT_MBYTE
WCHAR wstring[2];
char_u *ws = NULL;;
if (os_version.dwPlatformId != VER_PLATFORM_WIN32_NT)
{
/* On Windows 95/98 we apparently get the character in the active
* codepage, not in UCS-2. If conversion is needed convert it to
* UCS-2 first. */
if ((int)GetACP() == enc_codepage)
len = 0; /* no conversion required */
else
{
string[0] = ch;
len = MultiByteToWideChar(GetACP(), 0, string, 1, wstring, 2);
}
}
else
{
wstring[0] = ch;
len = 1;
}
if (len > 0)
{
/* "ch" is a UTF-16 character. Convert it to a string of bytes. When
* "enc_codepage" is non-zero use the standard Win32 function,
* otherwise use our own conversion function (e.g., for UTF-8). */
if (enc_codepage > 0)
{
len = WideCharToMultiByte(enc_codepage, 0, wstring, len,
string, slen, 0, NULL);
/* If we had included the ALT key into the character but now the
* upper bit is no longer set, that probably means the conversion
* failed. Convert the original character and set the upper bit
* afterwards. */
if (had_alt && len == 1 && ch >= 0x80 && string[0] < 0x80)
{
wstring[0] = ch & 0x7f;
len = WideCharToMultiByte(enc_codepage, 0, wstring, len,
string, slen, 0, NULL);
if (len == 1) /* safety check */
string[0] |= 0x80;
}
}
else
{
len = 1;
ws = utf16_to_enc(wstring, &len);
if (ws == NULL)
len = 0;
else
{
if (len > slen) /* just in case */
len = slen;
mch_memmove(string, ws, len);
vim_free(ws);
}
}
}
if (len == 0)
#endif
{
string[0] = ch;
len = 1;
}
for (i = 0; i < len; ++i)
if (string[i] == CSI && len <= slen - 2)
{
/* Insert CSI as K_CSI. */
mch_memmove(string + i + 3, string + i + 1, len - i - 1);
string[++i] = KS_EXTRA;
string[++i] = (int)KE_CSI;
len += 2;
}
return len;
}
/*
* Key hit, add it to the input buffer.
*/
/*ARGSUSED*/
static void
_OnChar(
HWND hwnd,
UINT ch,
int cRepeat)
{
char_u string[40];
int len = 0;
dead_key = 0;
len = char_to_string(ch, string, 40, FALSE);
if (len == 1 && string[0] == Ctrl_C && ctrl_c_interrupts)
{
trash_input_buf();
got_int = TRUE;
}
add_to_input_buf(string, len);
}
/*
* Alt-Key hit, add it to the input buffer.
*/
/*ARGSUSED*/
static void
_OnSysChar(
HWND hwnd,
UINT cch,
int cRepeat)
{
char_u string[40]; /* Enough for multibyte character */
int len;
int modifiers;
int ch = cch; /* special keys are negative */
/* TRACE("OnSysChar(%d, %c)\n", ch, ch); */
/* OK, we have a character key (given by ch) which was entered with the
* ALT key pressed. Eg, if the user presses Alt-A, then ch == 'A'. Note
* that the system distinguishes Alt-a and Alt-A (Alt-Shift-a unless
* CAPSLOCK is pressed) at this point.
*/
modifiers = MOD_MASK_ALT;
if (GetKeyState(VK_SHIFT) & 0x8000)
modifiers |= MOD_MASK_SHIFT;
if (GetKeyState(VK_CONTROL) & 0x8000)
modifiers |= MOD_MASK_CTRL;
ch = simplify_key(ch, &modifiers);
/* remove the SHIFT modifier for keys where it's already included, e.g.,
* '(' and '*' */
if (ch < 0x100 && !isalpha(ch) && isprint(ch))
modifiers &= ~MOD_MASK_SHIFT;
/* Interpret the ALT key as making the key META, include SHIFT, etc. */
ch = extract_modifiers(ch, &modifiers);
if (ch == CSI)
ch = K_CSI;
len = 0;
if (modifiers)
{
string[len++] = CSI;
string[len++] = KS_MODIFIER;
string[len++] = modifiers;
}
if (IS_SPECIAL((int)ch))
{
string[len++] = CSI;
string[len++] = K_SECOND((int)ch);
string[len++] = K_THIRD((int)ch);
}
else
{
/* Although the documentation isn't clear about it, we assume "ch" is
* a Unicode character. */
len += char_to_string(ch, string + len, 40 - len, TRUE);
}
add_to_input_buf(string, len);
}
static void
_OnMouseEvent(
int button,
int x,
int y,
int repeated_click,
UINT keyFlags)
{
int vim_modifiers = 0x0;
s_getting_focus = FALSE;
if (keyFlags & MK_SHIFT)
vim_modifiers |= MOUSE_SHIFT;
if (keyFlags & MK_CONTROL)
vim_modifiers |= MOUSE_CTRL;
if (GetKeyState(VK_MENU) & 0x8000)
vim_modifiers |= MOUSE_ALT;
gui_send_mouse_event(button, x, y, repeated_click, vim_modifiers);
}
/*ARGSUSED*/
static void
_OnMouseButtonDown(
HWND hwnd,
BOOL fDoubleClick,
int x,
int y,
UINT keyFlags)
{
static LONG s_prevTime = 0;
LONG currentTime = GetMessageTime();
int button = -1;
int repeated_click;
/* Give main window the focus: this is so the cursor isn't hollow. */
(void)SetFocus(s_hwnd);
if (s_uMsg == WM_LBUTTONDOWN || s_uMsg == WM_LBUTTONDBLCLK)
button = MOUSE_LEFT;
else if (s_uMsg == WM_MBUTTONDOWN || s_uMsg == WM_MBUTTONDBLCLK)
button = MOUSE_MIDDLE;
else if (s_uMsg == WM_RBUTTONDOWN || s_uMsg == WM_RBUTTONDBLCLK)
button = MOUSE_RIGHT;
#ifndef WIN16 /*<VN>*/
else if (s_uMsg == WM_XBUTTONDOWN || s_uMsg == WM_XBUTTONDBLCLK)
{
#ifndef GET_XBUTTON_WPARAM
# define GET_XBUTTON_WPARAM(wParam) (HIWORD(wParam))
#endif
button = ((GET_XBUTTON_WPARAM(s_wParam) == 1) ? MOUSE_X1 : MOUSE_X2);
}
else if (s_uMsg == WM_CAPTURECHANGED)
{
/* on W95/NT4, somehow you get in here with an odd Msg
* if you press one button while holding down the other..*/
if (s_button_pending == MOUSE_LEFT)
button = MOUSE_RIGHT;
else
button = MOUSE_LEFT;
}
#endif
if (button >= 0)
{
repeated_click = ((int)(currentTime - s_prevTime) < p_mouset);
/*
* Holding down the left and right buttons simulates pushing the middle
* button.
*/
if (repeated_click
&& ((button == MOUSE_LEFT && s_button_pending == MOUSE_RIGHT)
|| (button == MOUSE_RIGHT
&& s_button_pending == MOUSE_LEFT)))
{
/*
* Hmm, gui.c will ignore more than one button down at a time, so
* pretend we let go of it first.
*/
gui_send_mouse_event(MOUSE_RELEASE, x, y, FALSE, 0x0);
button = MOUSE_MIDDLE;
repeated_click = FALSE;
s_button_pending = -1;
_OnMouseEvent(button, x, y, repeated_click, keyFlags);
}
else if ((repeated_click)
|| (mouse_model_popup() && (button == MOUSE_RIGHT)))
{
if (s_button_pending > -1)
{
_OnMouseEvent(s_button_pending, x, y, FALSE, keyFlags);
s_button_pending = -1;
}
/* TRACE("Button down at x %d, y %d\n", x, y); */
_OnMouseEvent(button, x, y, repeated_click, keyFlags);
}
else
{
/*
* If this is the first press (i.e. not a multiple click) don't
* action immediately, but store and wait for:
* i) button-up
* ii) mouse move
* iii) another button press
* before using it.
* This enables us to make left+right simulate middle button,
* without left or right being actioned first. The side-effect is
* that if you click and hold the mouse without dragging, the
* cursor doesn't move until you release the button. In practice
* this is hardly a problem.
*/
s_button_pending = button;
s_x_pending = x;
s_y_pending = y;
s_kFlags_pending = keyFlags;
}
s_prevTime = currentTime;
}
}
/*ARGSUSED*/
static void
_OnMouseMoveOrRelease(
HWND hwnd,
int x,
int y,
UINT keyFlags)
{
int button;
s_getting_focus = FALSE;
if (s_button_pending > -1)
{
/* Delayed action for mouse down event */
_OnMouseEvent(s_button_pending, s_x_pending,
s_y_pending, FALSE, s_kFlags_pending);
s_button_pending = -1;
}
if (s_uMsg == WM_MOUSEMOVE)
{
/*
* It's only a MOUSE_DRAG if one or more mouse buttons are being held
* down.
*/
if (!(keyFlags & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON
| MK_XBUTTON1 | MK_XBUTTON2)))
{
gui_mouse_moved(x, y);
return;
}
/*
* While button is down, keep grabbing mouse move events when
* the mouse goes outside the window
*/
SetCapture(s_textArea);
button = MOUSE_DRAG;
/* TRACE(" move at x %d, y %d\n", x, y); */
}
else
{
ReleaseCapture();
button = MOUSE_RELEASE;
/* TRACE(" up at x %d, y %d\n", x, y); */
}
_OnMouseEvent(button, x, y, FALSE, keyFlags);
}
#ifdef FEAT_MENU
/*
* Find the vimmenu_T with the given id
*/
static vimmenu_T *
gui_mswin_find_menu(
vimmenu_T *pMenu,
int id)
{
vimmenu_T *pChildMenu;
while (pMenu)
{
if (pMenu->id == (UINT)id)
break;
if (pMenu->children != NULL)
{
pChildMenu = gui_mswin_find_menu(pMenu->children, id);
if (pChildMenu)
{
pMenu = pChildMenu;
break;
}
}
pMenu = pMenu->next;
}
return pMenu;
}
/*ARGSUSED*/
static void
_OnMenu(
HWND hwnd,
int id,
HWND hwndCtl,
UINT codeNotify)
{
vimmenu_T *pMenu;
pMenu = gui_mswin_find_menu(root_menu, id);
if (pMenu)
gui_menu_cb(pMenu);
}
#endif
#ifdef MSWIN_FIND_REPLACE
# if defined(FEAT_MBYTE) && defined(WIN3264)
/*
* copy useful data from structure LPFINDREPLACE to structure LPFINDREPLACEW
*/
static void
findrep_atow(LPFINDREPLACEW lpfrw, LPFINDREPLACE lpfr)
{
WCHAR *wp;
lpfrw->hwndOwner = lpfr->hwndOwner;
lpfrw->Flags = lpfr->Flags;
wp = enc_to_utf16(lpfr->lpstrFindWhat, NULL);
wcsncpy(lpfrw->lpstrFindWhat, wp, lpfrw->wFindWhatLen - 1);
vim_free(wp);
/* the field "lpstrReplaceWith" doesn't need to be copied */
}
/*
* copy useful data from structure LPFINDREPLACEW to structure LPFINDREPLACE
*/
static void
findrep_wtoa(LPFINDREPLACE lpfr, LPFINDREPLACEW lpfrw)
{
char_u *p;
lpfr->Flags = lpfrw->Flags;
p = utf16_to_enc(lpfrw->lpstrFindWhat, NULL);
vim_strncpy(lpfr->lpstrFindWhat, p, lpfr->wFindWhatLen - 1);
vim_free(p);
p = utf16_to_enc(lpfrw->lpstrReplaceWith, NULL);
vim_strncpy(lpfr->lpstrReplaceWith, p, lpfr->wReplaceWithLen - 1);
vim_free(p);
}
# endif
/*
* Handle a Find/Replace window message.
*/
static void
_OnFindRepl(void)
{
int flags = 0;
int down;
# if defined(FEAT_MBYTE) && defined(WIN3264)
/* If the OS is Windows NT, and 'encoding' differs from active codepage:
* convert text from wide string. */
if (os_version.dwPlatformId == VER_PLATFORM_WIN32_NT
&& enc_codepage >= 0 && (int)GetACP() != enc_codepage)
{
findrep_wtoa(&s_findrep_struct, &s_findrep_struct_w);
}
# endif
if (s_findrep_struct.Flags & FR_DIALOGTERM)
/* Give main window the focus back. */
(void)SetFocus(s_hwnd);
if (s_findrep_struct.Flags & FR_FINDNEXT)
{
flags = FRD_FINDNEXT;
/* Give main window the focus back: this is so the cursor isn't
* hollow. */
(void)SetFocus(s_hwnd);
}
else if (s_findrep_struct.Flags & FR_REPLACE)
{
flags = FRD_REPLACE;
/* Give main window the focus back: this is so the cursor isn't
* hollow. */
(void)SetFocus(s_hwnd);
}
else if (s_findrep_struct.Flags & FR_REPLACEALL)
{
flags = FRD_REPLACEALL;
}
if (flags != 0)
{
/* Call the generic GUI function to do the actual work. */
if (s_findrep_struct.Flags & FR_WHOLEWORD)
flags |= FRD_WHOLE_WORD;
if (s_findrep_struct.Flags & FR_MATCHCASE)
flags |= FRD_MATCH_CASE;
down = (s_findrep_struct.Flags & FR_DOWN) != 0;
gui_do_findrepl(flags, s_findrep_struct.lpstrFindWhat,
s_findrep_struct.lpstrReplaceWith, down);
}
}
#endif
static void
HandleMouseHide(UINT uMsg, LPARAM lParam)
{
static LPARAM last_lParam = 0L;
/* We sometimes get a mousemove when the mouse didn't move... */
if (uMsg == WM_MOUSEMOVE || uMsg == WM_NCMOUSEMOVE)
{
if (lParam == last_lParam)
return;
last_lParam = lParam;
}
/* Handle specially, to centralise coding. We need to be sure we catch all
* possible events which should cause us to restore the cursor (as it is a
* shared resource, we take full responsibility for it).
*/
switch (uMsg)
{
case WM_KEYUP:
case WM_CHAR:
/*
* blank out the pointer if necessary
*/
if (p_mh)
gui_mch_mousehide(TRUE);
break;
case WM_SYSKEYUP: /* show the pointer when a system-key is pressed */
case WM_SYSCHAR:
case WM_MOUSEMOVE: /* show the pointer on any mouse action */
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
case WM_MBUTTONDOWN:
case WM_MBUTTONUP:
case WM_RBUTTONDOWN:
case WM_RBUTTONUP:
case WM_XBUTTONDOWN:
case WM_XBUTTONUP:
case WM_NCMOUSEMOVE:
case WM_NCLBUTTONDOWN:
case WM_NCLBUTTONUP:
case WM_NCMBUTTONDOWN:
case WM_NCMBUTTONUP:
case WM_NCRBUTTONDOWN:
case WM_NCRBUTTONUP:
case WM_KILLFOCUS:
/*
* if the pointer is currently hidden, then we should show it.
*/
gui_mch_mousehide(FALSE);
break;
}
}
static LRESULT CALLBACK
_TextAreaWndProc(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{
/*
TRACE("TextAreaWndProc: hwnd = %08x, msg = %x, wParam = %x, lParam = %x\n",
hwnd, uMsg, wParam, lParam);
*/
HandleMouseHide(uMsg, lParam);
s_uMsg = uMsg;
s_wParam = wParam;
s_lParam = lParam;
#ifdef FEAT_BEVAL
TrackUserActivity(uMsg);
#endif
switch (uMsg)
{
HANDLE_MSG(hwnd, WM_LBUTTONDBLCLK,_OnMouseButtonDown);
HANDLE_MSG(hwnd, WM_LBUTTONDOWN,_OnMouseButtonDown);
HANDLE_MSG(hwnd, WM_LBUTTONUP, _OnMouseMoveOrRelease);
HANDLE_MSG(hwnd, WM_MBUTTONDBLCLK,_OnMouseButtonDown);
HANDLE_MSG(hwnd, WM_MBUTTONDOWN,_OnMouseButtonDown);
HANDLE_MSG(hwnd, WM_MBUTTONUP, _OnMouseMoveOrRelease);
HANDLE_MSG(hwnd, WM_MOUSEMOVE, _OnMouseMoveOrRelease);
HANDLE_MSG(hwnd, WM_PAINT, _OnPaint);
HANDLE_MSG(hwnd, WM_RBUTTONDBLCLK,_OnMouseButtonDown);
HANDLE_MSG(hwnd, WM_RBUTTONDOWN,_OnMouseButtonDown);
HANDLE_MSG(hwnd, WM_RBUTTONUP, _OnMouseMoveOrRelease);
#ifndef WIN16 /*<VN>*/
HANDLE_MSG(hwnd, WM_XBUTTONDBLCLK,_OnMouseButtonDown);
HANDLE_MSG(hwnd, WM_XBUTTONDOWN,_OnMouseButtonDown);
HANDLE_MSG(hwnd, WM_XBUTTONUP, _OnMouseMoveOrRelease);
#endif
#ifdef FEAT_BEVAL
case WM_NOTIFY: Handle_WM_Notify(hwnd, (LPNMHDR)lParam);
return TRUE;
#endif
default:
return MyWindowProc(hwnd, uMsg, wParam, lParam);
}
}
#if (defined(WIN3264) && defined(FEAT_MBYTE)) \
|| defined(GLOBAL_IME) \
|| defined(PROTO)
# ifdef PROTO
typedef int WINAPI;
# endif
LRESULT WINAPI
vim_WindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
# ifdef GLOBAL_IME
return global_ime_DefWindowProc(hwnd, message, wParam, lParam);
# else
if (wide_WindowProc)
return DefWindowProcW(hwnd, message, wParam, lParam);
return DefWindowProc(hwnd, message, wParam, lParam);
#endif
}
#endif
/*
* Called when the foreground or background color has been changed.
*/
void
gui_mch_new_colors(void)
{
/* nothing to do? */
}
/*
* Set the colors to their default values.
*/
void
gui_mch_def_colors()
{
gui.norm_pixel = GetSysColor(COLOR_WINDOWTEXT);
gui.back_pixel = GetSysColor(COLOR_WINDOW);
gui.def_norm_pixel = gui.norm_pixel;
gui.def_back_pixel = gui.back_pixel;
}
/*
* Open the GUI window which was created by a call to gui_mch_init().
*/
int
gui_mch_open(void)
{
#ifndef SW_SHOWDEFAULT
# define SW_SHOWDEFAULT 10 /* Borland 5.0 doesn't have it */
#endif
/* Actually open the window, if not already visible
* (may be done already in gui_mch_set_shellsize) */
if (!IsWindowVisible(s_hwnd))
ShowWindow(s_hwnd, SW_SHOWDEFAULT);
#ifdef MSWIN_FIND_REPLACE
/* Init replace string here, so that we keep it when re-opening the
* dialog. */
s_findrep_struct.lpstrReplaceWith[0] = NUL;
#endif
return OK;
}
/*
* Get the position of the top left corner of the window.
*/
int
gui_mch_get_winpos(int *x, int *y)
{
RECT rect;
GetWindowRect(s_hwnd, &rect);
*x = rect.left;
*y = rect.top;
return OK;
}
/*
* Set the position of the top left corner of the window to the given
* coordinates.
*/
void
gui_mch_set_winpos(int x, int y)
{
SetWindowPos(s_hwnd, NULL, x, y, 0, 0,
SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
}
void
gui_mch_set_text_area_pos(int x, int y, int w, int h)
{
static int oldx = 0;
static int oldy = 0;
SetWindowPos(s_textArea, NULL, x, y, w, h, SWP_NOZORDER | SWP_NOACTIVATE);
#ifdef FEAT_TOOLBAR
if (vim_strchr(p_go, GO_TOOLBAR) != NULL)
SendMessage(s_toolbarhwnd, WM_SIZE,
(WPARAM)0, (LPARAM)(w + ((long)(TOOLBAR_BUTTON_HEIGHT+8)<<16)));
#endif
#if defined(FEAT_GUI_TABLINE)
if (showing_tabline)
{
int top = 0;
RECT rect;
# ifdef FEAT_TOOLBAR
if (vim_strchr(p_go, GO_TOOLBAR) != NULL)
top = TOOLBAR_BUTTON_HEIGHT + TOOLBAR_BORDER_HEIGHT;
# endif
GetClientRect(s_hwnd, &rect);
MoveWindow(s_tabhwnd, 0, top, rect.right, gui.tabline_height, TRUE);
}
#endif
/* When side scroll bar is unshown, the size of window will change.
* then, the text area move left or right. thus client rect should be
* forcedly redrawn. (Yasuhiro Matsumoto) */
if (oldx != x || oldy != y)
{
InvalidateRect(s_hwnd, NULL, FALSE);
oldx = x;
oldy = y;
}
}
/*
* Scrollbar stuff:
*/
void
gui_mch_enable_scrollbar(
scrollbar_T *sb,
int flag)
{
ShowScrollBar(sb->id, SB_CTL, flag);
/* TODO: When the window is maximized, the size of the window stays the
* same, thus the size of the text area changes. On Win98 it's OK, on Win
* NT 4.0 it's not... */
}
void
gui_mch_set_scrollbar_pos(
scrollbar_T *sb,
int x,
int y,
int w,
int h)
{
SetWindowPos(sb->id, NULL, x, y, w, h,
SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW);
}
void
gui_mch_create_scrollbar(
scrollbar_T *sb,
int orient) /* SBAR_VERT or SBAR_HORIZ */
{
sb->id = CreateWindow(
"SCROLLBAR", "Scrollbar",
WS_CHILD | ((orient == SBAR_VERT) ? SBS_VERT : SBS_HORZ), 0, 0,
10, /* Any value will do for now */
10, /* Any value will do for now */
s_hwnd, NULL,
s_hinst, NULL);
}
/*
* Find the scrollbar with the given hwnd.
*/
static scrollbar_T *
gui_mswin_find_scrollbar(HWND hwnd)
{
win_T *wp;
if (gui.bottom_sbar.id == hwnd)
return &gui.bottom_sbar;
FOR_ALL_WINDOWS(wp)
{
if (wp->w_scrollbars[SBAR_LEFT].id == hwnd)
return &wp->w_scrollbars[SBAR_LEFT];
if (wp->w_scrollbars[SBAR_RIGHT].id == hwnd)
return &wp->w_scrollbars[SBAR_RIGHT];
}
return NULL;
}
/*
* Get the character size of a font.
*/
static void
GetFontSize(GuiFont font)
{
HWND hwnd = GetDesktopWindow();
HDC hdc = GetWindowDC(hwnd);
HFONT hfntOld = SelectFont(hdc, (HFONT)font);
TEXTMETRIC tm;
GetTextMetrics(hdc, &tm);
gui.char_width = tm.tmAveCharWidth + tm.tmOverhang;
gui.char_height = tm.tmHeight
#ifndef MSWIN16_FASTTEXT
+ p_linespace
#endif
;
SelectFont(hdc, hfntOld);
ReleaseDC(hwnd, hdc);
}
/*
* Adjust gui.char_height (after 'linespace' was changed).
*/
int
gui_mch_adjust_charheight(void)
{
GetFontSize(gui.norm_font);
return OK;
}
static GuiFont
get_font_handle(LOGFONT *lf)
{
HFONT font = NULL;
/* Load the font */
font = CreateFontIndirect(lf);
if (font == NULL)
return NOFONT;
return (GuiFont)font;
}
static int
pixels_to_points(int pixels, int vertical)
{
int points;
HWND hwnd;
HDC hdc;
hwnd = GetDesktopWindow();
hdc = GetWindowDC(hwnd);
points = MulDiv(pixels, 72,
GetDeviceCaps(hdc, vertical ? LOGPIXELSY : LOGPIXELSX));
ReleaseDC(hwnd, hdc);
return points;
}
GuiFont
gui_mch_get_font(
char_u *name,
int giveErrorIfMissing)
{
LOGFONT lf;
GuiFont font = NOFONT;
if (get_logfont(&lf, name, NULL, giveErrorIfMissing) == OK)
font = get_font_handle(&lf);
if (font == NOFONT && giveErrorIfMissing)
EMSG2(_(e_font), name);
return font;
}
#if defined(FEAT_EVAL) || defined(PROTO)
/*
* Return the name of font "font" in allocated memory.
* Don't know how to get the actual name, thus use the provided name.
*/
/*ARGSUSED*/
char_u *
gui_mch_get_fontname(font, name)
GuiFont font;
char_u *name;
{
if (name == NULL)
return NULL;
return vim_strsave(name);
}
#endif
void
gui_mch_free_font(GuiFont font)
{
if (font)
DeleteObject((HFONT)font);
}
static int
hex_digit(int c)
{
if (VIM_ISDIGIT(c))
return c - '0';
c = TOLOWER_ASC(c);
if (c >= 'a' && c <= 'f')
return c - 'a' + 10;
return -1000;
}
/*
* Return the Pixel value (color) for the given color name.
* Return INVALCOLOR for error.
*/
guicolor_T
gui_mch_get_color(char_u *name)
{
typedef struct guicolor_tTable
{
char *name;
COLORREF color;
} guicolor_tTable;
static guicolor_tTable table[] =
{
{"Black", RGB(0x00, 0x00, 0x00)},
{"DarkGray", RGB(0xA9, 0xA9, 0xA9)},
{"DarkGrey", RGB(0xA9, 0xA9, 0xA9)},
{"Gray", RGB(0xC0, 0xC0, 0xC0)},
{"Grey", RGB(0xC0, 0xC0, 0xC0)},
{"LightGray", RGB(0xD3, 0xD3, 0xD3)},
{"LightGrey", RGB(0xD3, 0xD3, 0xD3)},
{"Gray10", RGB(0x1A, 0x1A, 0x1A)},
{"Grey10", RGB(0x1A, 0x1A, 0x1A)},
{"Gray20", RGB(0x33, 0x33, 0x33)},
{"Grey20", RGB(0x33, 0x33, 0x33)},
{"Gray30", RGB(0x4D, 0x4D, 0x4D)},
{"Grey30", RGB(0x4D, 0x4D, 0x4D)},
{"Gray40", RGB(0x66, 0x66, 0x66)},
{"Grey40", RGB(0x66, 0x66, 0x66)},
{"Gray50", RGB(0x7F, 0x7F, 0x7F)},
{"Grey50", RGB(0x7F, 0x7F, 0x7F)},
{"Gray60", RGB(0x99, 0x99, 0x99)},
{"Grey60", RGB(0x99, 0x99, 0x99)},
{"Gray70", RGB(0xB3, 0xB3, 0xB3)},
{"Grey70", RGB(0xB3, 0xB3, 0xB3)},
{"Gray80", RGB(0xCC, 0xCC, 0xCC)},
{"Grey80", RGB(0xCC, 0xCC, 0xCC)},
{"Gray90", RGB(0xE5, 0xE5, 0xE5)},
{"Grey90", RGB(0xE5, 0xE5, 0xE5)},
{"White", RGB(0xFF, 0xFF, 0xFF)},
{"DarkRed", RGB(0x80, 0x00, 0x00)},
{"Red", RGB(0xFF, 0x00, 0x00)},
{"LightRed", RGB(0xFF, 0xA0, 0xA0)},
{"DarkBlue", RGB(0x00, 0x00, 0x80)},
{"Blue", RGB(0x00, 0x00, 0xFF)},
{"LightBlue", RGB(0xAD, 0xD8, 0xE6)},
{"DarkGreen", RGB(0x00, 0x80, 0x00)},
{"Green", RGB(0x00, 0xFF, 0x00)},
{"LightGreen", RGB(0x90, 0xEE, 0x90)},
{"DarkCyan", RGB(0x00, 0x80, 0x80)},
{"Cyan", RGB(0x00, 0xFF, 0xFF)},
{"LightCyan", RGB(0xE0, 0xFF, 0xFF)},
{"DarkMagenta", RGB(0x80, 0x00, 0x80)},
{"Magenta", RGB(0xFF, 0x00, 0xFF)},
{"LightMagenta", RGB(0xFF, 0xA0, 0xFF)},
{"Brown", RGB(0x80, 0x40, 0x40)},
{"Yellow", RGB(0xFF, 0xFF, 0x00)},
{"LightYellow", RGB(0xFF, 0xFF, 0xE0)},
{"DarkYellow", RGB(0xBB, 0xBB, 0x00)},
{"SeaGreen", RGB(0x2E, 0x8B, 0x57)},
{"Orange", RGB(0xFF, 0xA5, 0x00)},
{"Purple", RGB(0xA0, 0x20, 0xF0)},
{"SlateBlue", RGB(0x6A, 0x5A, 0xCD)},
{"Violet", RGB(0xEE, 0x82, 0xEE)},
};
typedef struct SysColorTable
{
char *name;
int color;
} SysColorTable;
static SysColorTable sys_table[] =
{
#ifdef WIN3264
{"SYS_3DDKSHADOW", COLOR_3DDKSHADOW},
{"SYS_3DHILIGHT", COLOR_3DHILIGHT},
#ifndef __MINGW32__
{"SYS_3DHIGHLIGHT", COLOR_3DHIGHLIGHT},
#endif
{"SYS_BTNHILIGHT", COLOR_BTNHILIGHT},
{"SYS_BTNHIGHLIGHT", COLOR_BTNHIGHLIGHT},
{"SYS_3DLIGHT", COLOR_3DLIGHT},
{"SYS_3DSHADOW", COLOR_3DSHADOW},
{"SYS_DESKTOP", COLOR_DESKTOP},
{"SYS_INFOBK", COLOR_INFOBK},
{"SYS_INFOTEXT", COLOR_INFOTEXT},
{"SYS_3DFACE", COLOR_3DFACE},
#endif
{"SYS_BTNFACE", COLOR_BTNFACE},
{"SYS_BTNSHADOW", COLOR_BTNSHADOW},
{"SYS_ACTIVEBORDER", COLOR_ACTIVEBORDER},
{"SYS_ACTIVECAPTION", COLOR_ACTIVECAPTION},
{"SYS_APPWORKSPACE", COLOR_APPWORKSPACE},
{"SYS_BACKGROUND", COLOR_BACKGROUND},
{"SYS_BTNTEXT", COLOR_BTNTEXT},
{"SYS_CAPTIONTEXT", COLOR_CAPTIONTEXT},
{"SYS_GRAYTEXT", COLOR_GRAYTEXT},
{"SYS_HIGHLIGHT", COLOR_HIGHLIGHT},
{"SYS_HIGHLIGHTTEXT", COLOR_HIGHLIGHTTEXT},
{"SYS_INACTIVEBORDER", COLOR_INACTIVEBORDER},
{"SYS_INACTIVECAPTION", COLOR_INACTIVECAPTION},
{"SYS_INACTIVECAPTIONTEXT", COLOR_INACTIVECAPTIONTEXT},
{"SYS_MENU", COLOR_MENU},
{"SYS_MENUTEXT", COLOR_MENUTEXT},
{"SYS_SCROLLBAR", COLOR_SCROLLBAR},
{"SYS_WINDOW", COLOR_WINDOW},
{"SYS_WINDOWFRAME", COLOR_WINDOWFRAME},
{"SYS_WINDOWTEXT", COLOR_WINDOWTEXT}
};
int r, g, b;
int i;
if (name[0] == '#' && strlen(name) == 7)
{
/* Name is in "#rrggbb" format */
r = hex_digit(name[1]) * 16 + hex_digit(name[2]);
g = hex_digit(name[3]) * 16 + hex_digit(name[4]);
b = hex_digit(name[5]) * 16 + hex_digit(name[6]);
if (r < 0 || g < 0 || b < 0)
return INVALCOLOR;
return RGB(r, g, b);
}
else
{
/* Check if the name is one of the colors we know */
for (i = 0; i < sizeof(table) / sizeof(table[0]); i++)
if (STRICMP(name, table[i].name) == 0)
return table[i].color;
}
/*
* Try to look up a system colour.
*/
for (i = 0; i < sizeof(sys_table) / sizeof(sys_table[0]); i++)
if (STRICMP(name, sys_table[i].name) == 0)
return GetSysColor(sys_table[i].color);
/*
* Last attempt. Look in the file "$VIMRUNTIME/rgb.txt".
*/
{
#define LINE_LEN 100
FILE *fd;
char line[LINE_LEN];
char_u *fname;
fname = expand_env_save((char_u *)"$VIMRUNTIME/rgb.txt");
if (fname == NULL)
return INVALCOLOR;
fd = mch_fopen((char *)fname, "rt");
vim_free(fname);
if (fd == NULL)
return INVALCOLOR;
while (!feof(fd))
{
int len;
int pos;
char *color;
fgets(line, LINE_LEN, fd);
len = (int)STRLEN(line);
if (len <= 1 || line[len-1] != '\n')
continue;
line[len-1] = '\0';
i = sscanf(line, "%d %d %d %n", &r, &g, &b, &pos);
if (i != 3)
continue;
color = line + pos;
if (STRICMP(color, name) == 0)
{
fclose(fd);
return (guicolor_T) RGB(r, g, b);
}
}
fclose(fd);
}
return INVALCOLOR;
}
/*
* Return OK if the key with the termcap name "name" is supported.
*/
int
gui_mch_haskey(char_u *name)
{
int i;
for (i = 0; special_keys[i].vim_code1 != NUL; i++)
if (name[0] == special_keys[i].vim_code0 &&
name[1] == special_keys[i].vim_code1)
return OK;
return FAIL;
}
void
gui_mch_beep(void)
{
MessageBeep(MB_OK);
}
/*
* Invert a rectangle from row r, column c, for nr rows and nc columns.
*/
void
gui_mch_invert_rectangle(
int r,
int c,
int nr,
int nc)
{
RECT rc;
/*
* Note: InvertRect() excludes right and bottom of rectangle.
*/
rc.left = FILL_X(c);
rc.top = FILL_Y(r);
rc.right = rc.left + nc * gui.char_width;
rc.bottom = rc.top + nr * gui.char_height;
InvertRect(s_hdc, &rc);
}
/*
* Iconify the GUI window.
*/
void
gui_mch_iconify(void)
{
ShowWindow(s_hwnd, SW_MINIMIZE);
}
/*
* Draw a cursor without focus.
*/
void
gui_mch_draw_hollow_cursor(guicolor_T color)
{
HBRUSH hbr;
RECT rc;
/*
* Note: FrameRect() excludes right and bottom of rectangle.
*/
rc.left = FILL_X(gui.col);
rc.top = FILL_Y(gui.row);
rc.right = rc.left + gui.char_width;
#ifdef FEAT_MBYTE
if (mb_lefthalve(gui.row, gui.col))
rc.right += gui.char_width;
#endif
rc.bottom = rc.top + gui.char_height;
hbr = CreateSolidBrush(color);
FrameRect(s_hdc, &rc, hbr);
DeleteBrush(hbr);
}
/*
* Draw part of a cursor, "w" pixels wide, and "h" pixels high, using
* color "color".
*/
void
gui_mch_draw_part_cursor(
int w,
int h,
guicolor_T color)
{
HBRUSH hbr;
RECT rc;
/*
* Note: FillRect() excludes right and bottom of rectangle.
*/
rc.left =
#ifdef FEAT_RIGHTLEFT
/* vertical line should be on the right of current point */
CURSOR_BAR_RIGHT ? FILL_X(gui.col + 1) - w :
#endif
FILL_X(gui.col);
rc.top = FILL_Y(gui.row) + gui.char_height - h;
rc.right = rc.left + w;
rc.bottom = rc.top + h;
hbr = CreateSolidBrush(color);
FillRect(s_hdc, &rc, hbr);
DeleteBrush(hbr);
}
/*
* Process a single Windows message.
* If one is not available we hang until one is.
*/
static void
process_message(void)
{
MSG msg;
UINT vk = 0; /* Virtual key */
char_u string[40];
int i;
int modifiers = 0;
int key;
#ifdef FEAT_MENU
static char_u k10[] = {K_SPECIAL, 'k', ';', 0};
#endif
pGetMessage(&msg, NULL, 0, 0);
#ifdef FEAT_OLE
/* Look after OLE Automation commands */
if (msg.message == WM_OLE)
{
char_u *str = (char_u *)msg.lParam;
if (str == NULL || *str == NUL)
{
/* Message can't be ours, forward it. Fixes problem with Ultramon
* 3.0.4 */
pDispatchMessage(&msg);
}
else
{
add_to_input_buf(str, (int)STRLEN(str));
vim_free(str); /* was allocated in CVim::SendKeys() */
}
return;
}
#endif
#ifdef FEAT_NETBEANS_INTG
if (msg.message == WM_NETBEANS)
{
netbeans_read();
return;
}
#endif
#ifdef FEAT_SNIFF
if (sniff_request_waiting && want_sniff_request)
{
static char_u bytes[3] = {CSI, (char_u)KS_EXTRA, (char_u)KE_SNIFF};
add_to_input_buf(bytes, 3); /* K_SNIFF */
sniff_request_waiting = 0;
want_sniff_request = 0;
/* request is handled in normal.c */
}
if (msg.message == WM_USER)
{
MyTranslateMessage(&msg);
pDispatchMessage(&msg);
return;
}
#endif
#ifdef MSWIN_FIND_REPLACE
/* Don't process messages used by the dialog */
if (s_findrep_hwnd != NULL && pIsDialogMessage(s_findrep_hwnd, &msg))
{
HandleMouseHide(msg.message, msg.lParam);
return;
}
#endif
/*
* Check if it's a special key that we recognise. If not, call
* TranslateMessage().
*/
if (msg.message == WM_KEYDOWN || msg.message == WM_SYSKEYDOWN)
{
vk = (int) msg.wParam;
/*
* If a dead key was pressed and the user presses VK_SPACE, VK_BACK, or
* VK_ESCAPE it means that he actually wants to deal with the dead char
* now, so do nothing special and let Windows handle it.
*
* Note that VK_SPACE combines with the dead_key's character and only
* one WM_CHAR will be generated by TranslateMessage(), in the two
* other cases two WM_CHAR will be generated: the dead char and VK_BACK
* or VK_ESCAPE. That is most likely what the user expects.
*/
if (dead_key && (vk == VK_SPACE || vk == VK_BACK || vk == VK_ESCAPE))
{
dead_key = 0;
MyTranslateMessage(&msg);
return;
}
/* Check for CTRL-BREAK */
if (vk == VK_CANCEL)
{
trash_input_buf();
got_int = TRUE;
string[0] = Ctrl_C;
add_to_input_buf(string, 1);
}
for (i = 0; special_keys[i].key_sym != 0; i++)
{
/* ignore VK_SPACE when ALT key pressed: system menu */
if (special_keys[i].key_sym == vk
&& (vk != VK_SPACE || !(GetKeyState(VK_MENU) & 0x8000)))
{
#ifdef FEAT_MENU
/* Check for <F10>: Windows selects the menu. When <F10> is
* mapped we want to use the mapping instead. */
if (vk == VK_F10
&& gui.menu_is_active
&& check_map(k10, State, FALSE, TRUE, FALSE,
NULL, NULL) == NULL)
break;
#endif
if (GetKeyState(VK_SHIFT) & 0x8000)
modifiers |= MOD_MASK_SHIFT;
/*
* Don't use caps-lock as shift, because these are special keys
* being considered here, and we only want letters to get
* shifted -- webb
*/
/*
if (GetKeyState(VK_CAPITAL) & 0x0001)
modifiers ^= MOD_MASK_SHIFT;
*/
if (GetKeyState(VK_CONTROL) & 0x8000)
modifiers |= MOD_MASK_CTRL;
if (GetKeyState(VK_MENU) & 0x8000)
modifiers |= MOD_MASK_ALT;
if (special_keys[i].vim_code1 == NUL)
key = special_keys[i].vim_code0;
else
key = TO_SPECIAL(special_keys[i].vim_code0,
special_keys[i].vim_code1);
key = simplify_key(key, &modifiers);
if (key == CSI)
key = K_CSI;
if (modifiers)
{
string[0] = CSI;
string[1] = KS_MODIFIER;
string[2] = modifiers;
add_to_input_buf(string, 3);
}
if (IS_SPECIAL(key))
{
string[0] = CSI;
string[1] = K_SECOND(key);
string[2] = K_THIRD(key);
add_to_input_buf(string, 3);
}
else
{
int len;
/* Handle "key" as a Unicode character. */
len = char_to_string(key, string, 40, FALSE);
add_to_input_buf(string, len);
}
break;
}
}
if (special_keys[i].key_sym == 0)
{
/* Some keys need C-S- where they should only need C-.
* Ignore 0xff, Windows XP sends it when NUMLOCK has changed since
* system startup (Helmut Stiegler, 2003 Oct 3). */
if (vk != 0xff
&& (GetKeyState(VK_CONTROL) & 0x8000)
&& !(GetKeyState(VK_SHIFT) & 0x8000)
&& !(GetKeyState(VK_MENU) & 0x8000))
{
/* CTRL-6 is '^'; Japanese keyboard maps '^' to vk == 0xDE */
if (vk == '6' || MapVirtualKey(vk, 2) == (UINT)'^')
{
string[0] = Ctrl_HAT;
add_to_input_buf(string, 1);
}
/* vk == 0xBD AZERTY for CTRL-'-', but CTRL-[ for * QWERTY! */
else if (vk == 0xBD) /* QWERTY for CTRL-'-' */
{
string[0] = Ctrl__;
add_to_input_buf(string, 1);
}
/* CTRL-2 is '@'; Japanese keyboard maps '@' to vk == 0xC0 */
else if (vk == '2' || MapVirtualKey(vk, 2) == (UINT)'@')
{
string[0] = Ctrl_AT;
add_to_input_buf(string, 1);
}
else
MyTranslateMessage(&msg);
}
else
MyTranslateMessage(&msg);
}
}
#ifdef FEAT_MBYTE_IME
else if (msg.message == WM_IME_NOTIFY)
_OnImeNotify(msg.hwnd, (DWORD)msg.wParam, (DWORD)msg.lParam);
else if (msg.message == WM_KEYUP && im_get_status())
/* added for non-MS IME (Yasuhiro Matsumoto) */
MyTranslateMessage(&msg);
#endif
#if !defined(FEAT_MBYTE_IME) && defined(GLOBAL_IME)
/* GIME_TEST */
else if (msg.message == WM_IME_STARTCOMPOSITION)
{
POINT point;
global_ime_set_font(&norm_logfont);
point.x = FILL_X(gui.col);
point.y = FILL_Y(gui.row);
MapWindowPoints(s_textArea, s_hwnd, &point, 1);
global_ime_set_position(&point);
}
#endif
#ifdef FEAT_MENU
/* Check for <F10>: Default effect is to select the menu. When <F10> is
* mapped we need to stop it here to avoid strange effects (e.g., for the
* key-up event) */
if (vk != VK_F10 || check_map(k10, State, FALSE, TRUE, FALSE,
NULL, NULL) == NULL)
#endif
pDispatchMessage(&msg);
}
/*
* Catch up with any queued events. This may put keyboard input into the
* input buffer, call resize call-backs, trigger timers etc. If there is
* nothing in the event queue (& no timers pending), then we return
* immediately.
*/
void
gui_mch_update(void)
{
MSG msg;
if (!s_busy_processing)
while (pPeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)
&& !vim_is_input_buf_full())
process_message();
}
/*
* GUI input routine called by gui_wait_for_chars(). Waits for a character
* from the keyboard.
* wtime == -1 Wait forever.
* wtime == 0 This should never happen.
* wtime > 0 Wait wtime milliseconds for a character.
* Returns OK if a character was found to be available within the given time,
* or FAIL otherwise.
*/
int
gui_mch_wait_for_chars(int wtime)
{
MSG msg;
int focus;
s_timed_out = FALSE;
if (wtime > 0)
{
/* Don't do anything while processing a (scroll) message. */
if (s_busy_processing)
return FAIL;
s_wait_timer = (UINT)SetTimer(NULL, 0, (UINT)wtime,
(TIMERPROC)_OnTimer);
}
allow_scrollbar = TRUE;
focus = gui.in_focus;
while (!s_timed_out)
{
/* Stop or start blinking when focus changes */
if (gui.in_focus != focus)
{
if (gui.in_focus)
gui_mch_start_blink();
else
gui_mch_stop_blink();
focus = gui.in_focus;
}
if (s_need_activate)
{
#ifdef WIN32
(void)SetForegroundWindow(s_hwnd);
#else
(void)SetActiveWindow(s_hwnd);
#endif
s_need_activate = FALSE;
}
#ifdef MESSAGE_QUEUE
parse_queued_messages();
#endif
/*
* Don't use gui_mch_update() because then we will spin-lock until a
* char arrives, instead we use GetMessage() to hang until an
* event arrives. No need to check for input_buf_full because we are
* returning as soon as it contains a single char -- webb
*/
process_message();
if (input_available())
{
if (s_wait_timer != 0 && !s_timed_out)
{
KillTimer(NULL, s_wait_timer);
/* Eat spurious WM_TIMER messages */
while (pPeekMessage(&msg, s_hwnd, WM_TIMER, WM_TIMER, PM_REMOVE))
;
s_wait_timer = 0;
}
allow_scrollbar = FALSE;
/* Clear pending mouse button, the release event may have been
* taken by the dialog window. But don't do this when getting
* focus, we need the mouse-up event then. */
if (!s_getting_focus)
s_button_pending = -1;
return OK;
}
}
allow_scrollbar = FALSE;
return FAIL;
}
/*
* Clear a rectangular region of the screen from text pos (row1, col1) to
* (row2, col2) inclusive.
*/
void
gui_mch_clear_block(
int row1,
int col1,
int row2,
int col2)
{
RECT rc;
/*
* Clear one extra pixel at the far right, for when bold characters have
* spilled over to the window border.
* Note: FillRect() excludes right and bottom of rectangle.
*/
rc.left = FILL_X(col1);
rc.top = FILL_Y(row1);
rc.right = FILL_X(col2 + 1) + (col2 == Columns - 1);
rc.bottom = FILL_Y(row2 + 1);
clear_rect(&rc);
}
/*
* Clear the whole text window.
*/
void
gui_mch_clear_all(void)
{
RECT rc;
rc.left = 0;
rc.top = 0;
rc.right = Columns * gui.char_width + 2 * gui.border_width;
rc.bottom = Rows * gui.char_height + 2 * gui.border_width;
clear_rect(&rc);
}
/*
* Menu stuff.
*/
void
gui_mch_enable_menu(int flag)
{
#ifdef FEAT_MENU
SetMenu(s_hwnd, flag ? s_menuBar : NULL);
#endif
}
/*ARGSUSED*/
void
gui_mch_set_menu_pos(
int x,
int y,
int w,
int h)
{
/* It will be in the right place anyway */
}
#if defined(FEAT_MENU) || defined(PROTO)
/*
* Make menu item hidden or not hidden
*/
void
gui_mch_menu_hidden(
vimmenu_T *menu,
int hidden)
{
/*
* This doesn't do what we want. Hmm, just grey the menu items for now.
*/
/*
if (hidden)
EnableMenuItem(s_menuBar, menu->id, MF_BYCOMMAND | MF_DISABLED);
else
EnableMenuItem(s_menuBar, menu->id, MF_BYCOMMAND | MF_ENABLED);
*/
gui_mch_menu_grey(menu, hidden);
}
/*
* This is called after setting all the menus to grey/hidden or not.
*/
void
gui_mch_draw_menubar(void)
{
DrawMenuBar(s_hwnd);
}
#endif /*FEAT_MENU*/
#ifndef PROTO
void
#ifdef VIMDLL
_export
#endif
_cdecl
SaveInst(HINSTANCE hInst)
{
s_hinst = hInst;
}
#endif
/*
* Return the RGB value of a pixel as a long.
*/
long_u
gui_mch_get_rgb(guicolor_T pixel)
{
return (GetRValue(pixel) << 16) + (GetGValue(pixel) << 8)
+ GetBValue(pixel);
}
#if defined(FEAT_GUI_DIALOG) || defined(PROTO)
/* Convert pixels in X to dialog units */
static WORD
PixelToDialogX(int numPixels)
{
return (WORD)((numPixels * 4) / s_dlgfntwidth);
}
/* Convert pixels in Y to dialog units */
static WORD
PixelToDialogY(int numPixels)
{
return (WORD)((numPixels * 8) / s_dlgfntheight);
}
/* Return the width in pixels of the given text in the given DC. */
static int
GetTextWidth(HDC hdc, char_u *str, int len)
{
SIZE size;
GetTextExtentPoint(hdc, str, len, &size);
return size.cx;
}
#ifdef FEAT_MBYTE
/*
* Return the width in pixels of the given text in the given DC, taking care
* of 'encoding' to active codepage conversion.
*/
static int
GetTextWidthEnc(HDC hdc, char_u *str, int len)
{
SIZE size;
WCHAR *wstr;
int n;
int wlen = len;
if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
{
/* 'encoding' differs from active codepage: convert text and use wide
* function */
wstr = enc_to_utf16(str, &wlen);
if (wstr != NULL)
{
n = GetTextExtentPointW(hdc, wstr, wlen, &size);
vim_free(wstr);
if (n)
return size.cx;
}
}
return GetTextWidth(hdc, str, len);
}
#else
# define GetTextWidthEnc(h, s, l) GetTextWidth((h), (s), (l))
#endif
/*
* A quick little routine that will center one window over another, handy for
* dialog boxes. Taken from the Win32SDK samples.
*/
static BOOL
CenterWindow(
HWND hwndChild,
HWND hwndParent)
{
RECT rChild, rParent;
int wChild, hChild, wParent, hParent;
int wScreen, hScreen, xNew, yNew;
HDC hdc;
GetWindowRect(hwndChild, &rChild);
wChild = rChild.right - rChild.left;
hChild = rChild.bottom - rChild.top;
/* If Vim is minimized put the window in the middle of the screen. */
if (hwndParent == NULL || IsMinimized(hwndParent))
{
#ifdef WIN16
rParent.left = 0;
rParent.top = 0;
rParent.right = GetSystemMetrics(SM_CXSCREEN);
rParent.bottom = GetSystemMetrics(SM_CYFULLSCREEN);
#else
SystemParametersInfo(SPI_GETWORKAREA, 0, &rParent, 0);
#endif
}
else
GetWindowRect(hwndParent, &rParent);
wParent = rParent.right - rParent.left;
hParent = rParent.bottom - rParent.top;
hdc = GetDC(hwndChild);
wScreen = GetDeviceCaps (hdc, HORZRES);
hScreen = GetDeviceCaps (hdc, VERTRES);
ReleaseDC(hwndChild, hdc);
xNew = rParent.left + ((wParent - wChild) /2);
if (xNew < 0)
{
xNew = 0;
}
else if ((xNew+wChild) > wScreen)
{
xNew = wScreen - wChild;
}
yNew = rParent.top + ((hParent - hChild) /2);
if (yNew < 0)
yNew = 0;
else if ((yNew+hChild) > hScreen)
yNew = hScreen - hChild;
return SetWindowPos(hwndChild, NULL, xNew, yNew, 0, 0,
SWP_NOSIZE | SWP_NOZORDER);
}
#endif /* FEAT_GUI_DIALOG */
void
gui_mch_activate_window(void)
{
(void)SetActiveWindow(s_hwnd);
}
#if defined(FEAT_TOOLBAR) || defined(PROTO)
void
gui_mch_show_toolbar(int showit)
{
if (s_toolbarhwnd == NULL)
return;
if (showit)
{
# ifdef FEAT_MBYTE
# ifndef TB_SETUNICODEFORMAT
/* For older compilers. We assume this never changes. */
# define TB_SETUNICODEFORMAT 0x2005
# endif
/* Enable/disable unicode support */
int uu = (enc_codepage >= 0 && (int)GetACP() != enc_codepage);
SendMessage(s_toolbarhwnd, TB_SETUNICODEFORMAT, (WPARAM)uu, (LPARAM)0);
# endif
ShowWindow(s_toolbarhwnd, SW_SHOW);
}
else
ShowWindow(s_toolbarhwnd, SW_HIDE);
}
/* Then number of bitmaps is fixed. Exit is missing! */
#define TOOLBAR_BITMAP_COUNT 31
#endif
#if defined(FEAT_GUI_TABLINE) || defined(PROTO)
static void
add_tabline_popup_menu_entry(HMENU pmenu, UINT item_id, char_u *item_text)
{
#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_utf16(item_text, NULL);
if (wn != NULL)
{
MENUITEMINFOW infow;
infow.cbSize = sizeof(infow);
infow.fMask = MIIM_TYPE | MIIM_ID;
infow.wID = item_id;
infow.fType = MFT_STRING;
infow.dwTypeData = wn;
infow.cch = (UINT)wcslen(wn);
n = InsertMenuItemW(pmenu, item_id, FALSE, &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_TYPE | MIIM_ID;
info.wID = item_id;
info.fType = MFT_STRING;
info.dwTypeData = item_text;
info.cch = (UINT)STRLEN(item_text);
InsertMenuItem(pmenu, item_id, FALSE, &info);
}
}
static void
show_tabline_popup_menu(void)
{
HMENU tab_pmenu;
long rval;
POINT pt;
/* When ignoring events don't show the menu. */
if (hold_gui_events
# ifdef FEAT_CMDWIN
|| cmdwin_type != 0
# endif
)
return;
tab_pmenu = CreatePopupMenu();
if (tab_pmenu == NULL)
return;
if (first_tabpage->tp_next != NULL)
add_tabline_popup_menu_entry(tab_pmenu,
TABLINE_MENU_CLOSE, _("Close tab"));
add_tabline_popup_menu_entry(tab_pmenu, TABLINE_MENU_NEW, _("New tab"));
add_tabline_popup_menu_entry(tab_pmenu, TABLINE_MENU_OPEN,
_("Open tab..."));
GetCursorPos(&pt);
rval = TrackPopupMenuEx(tab_pmenu, TPM_RETURNCMD, pt.x, pt.y, s_tabhwnd,
NULL);
DestroyMenu(tab_pmenu);
/* Add the string cmd into input buffer */
if (rval > 0)
{
TCHITTESTINFO htinfo;
int idx;
if (ScreenToClient(s_tabhwnd, &pt) == 0)
return;
htinfo.pt.x = pt.x;
htinfo.pt.y = pt.y;
idx = TabCtrl_HitTest(s_tabhwnd, &htinfo);
if (idx == -1)
idx = 0;
else
idx += 1;
send_tabline_menu_event(idx, (int)rval);
}
}
/*
* Show or hide the tabline.
*/
void
gui_mch_show_tabline(int showit)
{
if (s_tabhwnd == NULL)
return;
if (!showit != !showing_tabline)
{
if (showit)
ShowWindow(s_tabhwnd, SW_SHOW);
else
ShowWindow(s_tabhwnd, SW_HIDE);
showing_tabline = showit;
}
}
/*
* Return TRUE when tabline is displayed.
*/
int
gui_mch_showing_tabline(void)
{
return s_tabhwnd != NULL && showing_tabline;
}
/*
* Update the labels of the tabline.
*/
void
gui_mch_update_tabline(void)
{
tabpage_T *tp;
TCITEM tie;
int nr = 0;
int curtabidx = 0;
int tabadded = 0;
#ifdef FEAT_MBYTE
static int use_unicode = FALSE;
int uu;
WCHAR *wstr = NULL;
#endif
if (s_tabhwnd == NULL)
return;
#if defined(FEAT_MBYTE)
# ifndef CCM_SETUNICODEFORMAT
/* For older compilers. We assume this never changes. */
# define CCM_SETUNICODEFORMAT 0x2005
# endif
uu = (enc_codepage >= 0 && (int)GetACP() != enc_codepage);
if (uu != use_unicode)
{
/* Enable/disable unicode support */
SendMessage(s_tabhwnd, CCM_SETUNICODEFORMAT, (WPARAM)uu, (LPARAM)0);
use_unicode = uu;
}
#endif
tie.mask = TCIF_TEXT;
tie.iImage = -1;
/* Disable redraw for tab updates to eliminate O(N^2) draws. */
SendMessage(s_tabhwnd, WM_SETREDRAW, (WPARAM)FALSE, 0);
/* Add a label for each tab page. They all contain the same text area. */
for (tp = first_tabpage; tp != NULL; tp = tp->tp_next, ++nr)
{
if (tp == curtab)
curtabidx = nr;
if (nr >= TabCtrl_GetItemCount(s_tabhwnd))
{
/* Add the tab */
tie.pszText = "-Empty-";
TabCtrl_InsertItem(s_tabhwnd, nr, &tie);
tabadded = 1;
}
get_tabline_label(tp, FALSE);
tie.pszText = NameBuff;
#ifdef FEAT_MBYTE
wstr = NULL;
if (use_unicode)
{
/* Need to go through Unicode. */
wstr = enc_to_utf16(NameBuff, NULL);
if (wstr != NULL)
{
TCITEMW tiw;
tiw.mask = TCIF_TEXT;
tiw.iImage = -1;
tiw.pszText = wstr;
SendMessage(s_tabhwnd, TCM_SETITEMW, (WPARAM)nr, (LPARAM)&tiw);
vim_free(wstr);
}
}
if (wstr == NULL)
#endif
{
TabCtrl_SetItem(s_tabhwnd, nr, &tie);
}
}
/* Remove any old labels. */
while (nr < TabCtrl_GetItemCount(s_tabhwnd))
TabCtrl_DeleteItem(s_tabhwnd, nr);
if (!tabadded && TabCtrl_GetCurSel(s_tabhwnd) != curtabidx)
TabCtrl_SetCurSel(s_tabhwnd, curtabidx);
/* Re-enable redraw and redraw. */
SendMessage(s_tabhwnd, WM_SETREDRAW, (WPARAM)TRUE, 0);
RedrawWindow(s_tabhwnd, NULL, NULL,
RDW_ERASE | RDW_FRAME | RDW_INVALIDATE | RDW_ALLCHILDREN);
if (tabadded && TabCtrl_GetCurSel(s_tabhwnd) != curtabidx)
TabCtrl_SetCurSel(s_tabhwnd, curtabidx);
}
/*
* Set the current tab to "nr". First tab is 1.
*/
void
gui_mch_set_curtab(nr)
int nr;
{
if (s_tabhwnd == NULL)
return;
if (TabCtrl_GetCurSel(s_tabhwnd) != nr - 1)
TabCtrl_SetCurSel(s_tabhwnd, nr - 1);
}
#endif
/*
* ":simalt" command.
*/
void
ex_simalt(exarg_T *eap)
{
char_u *keys = eap->arg;
PostMessage(s_hwnd, WM_SYSCOMMAND, (WPARAM)SC_KEYMENU, (LPARAM)0);
while (*keys)
{
if (*keys == '~')
*keys = ' '; /* for showing system menu */
PostMessage(s_hwnd, WM_CHAR, (WPARAM)*keys, (LPARAM)0);
keys++;
}
}
/*
* Create the find & replace dialogs.
* You can't have both at once: ":find" when replace is showing, destroys
* the replace dialog first, and the other way around.
*/
#ifdef MSWIN_FIND_REPLACE
static void
initialise_findrep(char_u *initial_string)
{
int wword = FALSE;
int mcase = !p_ic;
char_u *entry_text;
/* Get the search string to use. */
entry_text = get_find_dialog_text(initial_string, &wword, &mcase);
s_findrep_struct.hwndOwner = s_hwnd;
s_findrep_struct.Flags = FR_DOWN;
if (mcase)
s_findrep_struct.Flags |= FR_MATCHCASE;
if (wword)
s_findrep_struct.Flags |= FR_WHOLEWORD;
if (entry_text != NULL && *entry_text != NUL)
vim_strncpy(s_findrep_struct.lpstrFindWhat, entry_text,
s_findrep_struct.wFindWhatLen - 1);
vim_free(entry_text);
}
#endif
static void
set_window_title(HWND hwnd, char *title)
{
#ifdef FEAT_MBYTE
if (title != NULL && enc_codepage >= 0 && enc_codepage != (int)GetACP())
{
WCHAR *wbuf;
int n;
/* Convert the title from 'encoding' to UTF-16. */
wbuf = (WCHAR *)enc_to_utf16((char_u *)title, NULL);
if (wbuf != NULL)
{
n = SetWindowTextW(hwnd, wbuf);
vim_free(wbuf);
if (n != 0 || GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
return;
/* Retry with non-wide function (for Windows 98). */
}
}
#endif
(void)SetWindowText(hwnd, (LPCSTR)title);
}
void
gui_mch_find_dialog(exarg_T *eap)
{
#ifdef MSWIN_FIND_REPLACE
if (s_findrep_msg != 0)
{
if (IsWindow(s_findrep_hwnd) && !s_findrep_is_find)
DestroyWindow(s_findrep_hwnd);
if (!IsWindow(s_findrep_hwnd))
{
initialise_findrep(eap->arg);
# if defined(FEAT_MBYTE) && defined(WIN3264)
/* If the OS is Windows NT, and 'encoding' differs from active
* codepage: convert text and use wide function. */
if (os_version.dwPlatformId == VER_PLATFORM_WIN32_NT
&& enc_codepage >= 0 && (int)GetACP() != enc_codepage)
{
findrep_atow(&s_findrep_struct_w, &s_findrep_struct);
s_findrep_hwnd = FindTextW(
(LPFINDREPLACEW) &s_findrep_struct_w);
}
else
# endif
s_findrep_hwnd = FindText((LPFINDREPLACE) &s_findrep_struct);
}
set_window_title(s_findrep_hwnd,
_("Find string (use '\\\\' to find a '\\')"));
(void)SetFocus(s_findrep_hwnd);
s_findrep_is_find = TRUE;
}
#endif
}
void
gui_mch_replace_dialog(exarg_T *eap)
{
#ifdef MSWIN_FIND_REPLACE
if (s_findrep_msg != 0)
{
if (IsWindow(s_findrep_hwnd) && s_findrep_is_find)
DestroyWindow(s_findrep_hwnd);
if (!IsWindow(s_findrep_hwnd))
{
initialise_findrep(eap->arg);
# if defined(FEAT_MBYTE) && defined(WIN3264)
if (os_version.dwPlatformId == VER_PLATFORM_WIN32_NT
&& enc_codepage >= 0 && (int)GetACP() != enc_codepage)
{
findrep_atow(&s_findrep_struct_w, &s_findrep_struct);
s_findrep_hwnd = ReplaceTextW(
(LPFINDREPLACEW) &s_findrep_struct_w);
}
else
# endif
s_findrep_hwnd = ReplaceText(
(LPFINDREPLACE) &s_findrep_struct);
}
set_window_title(s_findrep_hwnd,
_("Find & Replace (use '\\\\' to find a '\\')"));
(void)SetFocus(s_findrep_hwnd);
s_findrep_is_find = FALSE;
}
#endif
}
/*
* Set visibility of the pointer.
*/
void
gui_mch_mousehide(int hide)
{
if (hide != gui.pointer_hidden)
{
ShowCursor(!hide);
gui.pointer_hidden = hide;
}
}
#ifdef FEAT_MENU
static void
gui_mch_show_popupmenu_at(vimmenu_T *menu, int x, int y)
{
/* Unhide the mouse, we don't get move events here. */
gui_mch_mousehide(FALSE);
(void)TrackPopupMenu(
(HMENU)menu->submenu_id,
TPM_LEFTALIGN | TPM_LEFTBUTTON,
x, 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.
*/
}
#endif
/*
* Got a message when the system will go down.
*/
static void
_OnEndSession(void)
{
getout_preserve_modified(1);
}
/*
* Get this message when the user clicks on the cross in the top right corner
* of a Windows95 window.
*/
/*ARGSUSED*/
static void
_OnClose(
HWND hwnd)
{
gui_shell_closed();
}
/*
* Get a message when the window is being destroyed.
*/
static void
_OnDestroy(
HWND hwnd)
{
#ifdef WIN16_3DLOOK
Ctl3dUnregister(s_hinst);
#endif
if (!destroying)
_OnClose(hwnd);
}
static void
_OnPaint(
HWND hwnd)
{
if (!IsMinimized(hwnd))
{
PAINTSTRUCT ps;
out_flush(); /* make sure all output has been processed */
(void)BeginPaint(hwnd, &ps);
#if defined(FEAT_DIRECTX)
if (IS_ENABLE_DIRECTX())
DWriteContext_BeginDraw(s_dwc);
#endif
#ifdef FEAT_MBYTE
/* prevent multi-byte characters from misprinting on an invalid
* rectangle */
if (has_mbyte)
{
RECT rect;
GetClientRect(hwnd, &rect);
ps.rcPaint.left = rect.left;
ps.rcPaint.right = rect.right;
}
#endif
if (!IsRectEmpty(&ps.rcPaint))
{
#if defined(FEAT_DIRECTX)
if (IS_ENABLE_DIRECTX())
DWriteContext_BindDC(s_dwc, s_hdc, &ps.rcPaint);
#endif
gui_redraw(ps.rcPaint.left, ps.rcPaint.top,
ps.rcPaint.right - ps.rcPaint.left + 1,
ps.rcPaint.bottom - ps.rcPaint.top + 1);
}
#if defined(FEAT_DIRECTX)
if (IS_ENABLE_DIRECTX())
DWriteContext_EndDraw(s_dwc);
#endif
EndPaint(hwnd, &ps);
}
}
/*ARGSUSED*/
static void
_OnSize(
HWND hwnd,
UINT state,
int cx,
int cy)
{
if (!IsMinimized(hwnd))
{
gui_resize_shell(cx, cy);
#ifdef FEAT_MENU
/* Menu bar may wrap differently now */
gui_mswin_get_menu_height(TRUE);
#endif
}
}
static void
_OnSetFocus(
HWND hwnd,
HWND hwndOldFocus)
{
gui_focus_change(TRUE);
s_getting_focus = TRUE;
(void)MyWindowProc(hwnd, WM_SETFOCUS, (WPARAM)hwndOldFocus, 0);
}
static void
_OnKillFocus(
HWND hwnd,
HWND hwndNewFocus)
{
gui_focus_change(FALSE);
s_getting_focus = FALSE;
(void)MyWindowProc(hwnd, WM_KILLFOCUS, (WPARAM)hwndNewFocus, 0);
}
/*
* Get a message when the user switches back to vim
*/
#ifdef WIN16
static BOOL
#else
static LRESULT
#endif
_OnActivateApp(
HWND hwnd,
BOOL fActivate,
#ifdef WIN16
HTASK dwThreadId
#else
DWORD dwThreadId
#endif
)
{
/* we call gui_focus_change() in _OnSetFocus() */
/* gui_focus_change((int)fActivate); */
return MyWindowProc(hwnd, WM_ACTIVATEAPP, fActivate, (DWORD)dwThreadId);
}
#if defined(FEAT_WINDOWS) || defined(PROTO)
void
gui_mch_destroy_scrollbar(scrollbar_T *sb)
{
DestroyWindow(sb->id);
}
#endif
/*
* Get current mouse coordinates in text window.
*/
void
gui_mch_getmouse(int *x, int *y)
{
RECT rct;
POINT mp;
(void)GetWindowRect(s_textArea, &rct);
(void)GetCursorPos((LPPOINT)&mp);
*x = (int)(mp.x - rct.left);
*y = (int)(mp.y - rct.top);
}
/*
* Move mouse pointer to character at (x, y).
*/
void
gui_mch_setmouse(int x, int y)
{
RECT rct;
(void)GetWindowRect(s_textArea, &rct);
(void)SetCursorPos(x + gui.border_offset + rct.left,
y + gui.border_offset + rct.top);
}
static void
gui_mswin_get_valid_dimensions(
int w,
int h,
int *valid_w,
int *valid_h)
{
int base_width, base_height;
base_width = gui_get_base_width()
+ (GetSystemMetrics(SM_CXFRAME) +
GetSystemMetrics(SM_CXPADDEDBORDER)) * 2;
base_height = gui_get_base_height()
+ (GetSystemMetrics(SM_CYFRAME) +
GetSystemMetrics(SM_CXPADDEDBORDER)) * 2
+ GetSystemMetrics(SM_CYCAPTION)
#ifdef FEAT_MENU
+ gui_mswin_get_menu_height(FALSE)
#endif
;
*valid_w = base_width +
((w - base_width) / gui.char_width) * gui.char_width;
*valid_h = base_height +
((h - base_height) / gui.char_height) * gui.char_height;
}
void
gui_mch_flash(int msec)
{
RECT rc;
/*
* Note: InvertRect() excludes right and bottom of rectangle.
*/
rc.left = 0;
rc.top = 0;
rc.right = gui.num_cols * gui.char_width;
rc.bottom = gui.num_rows * gui.char_height;
InvertRect(s_hdc, &rc);
gui_mch_flush(); /* make sure it's displayed */
ui_delay((long)msec, TRUE); /* wait for a few msec */
InvertRect(s_hdc, &rc);
}
/*
* Return flags used for scrolling.
* The SW_INVALIDATE is required when part of the window is covered or
* off-screen. Refer to MS KB Q75236.
*/
static int
get_scroll_flags(void)
{
HWND hwnd;
RECT rcVim, rcOther, rcDest;
GetWindowRect(s_hwnd, &rcVim);
/* Check if the window is partly above or below the screen. We don't care
* about partly left or right of the screen, it is not relevant when
* scrolling up or down. */
if (rcVim.top < 0 || rcVim.bottom > GetSystemMetrics(SM_CYFULLSCREEN))
return SW_INVALIDATE;
/* Check if there is an window (partly) on top of us. */
for (hwnd = s_hwnd; (hwnd = GetWindow(hwnd, GW_HWNDPREV)) != (HWND)0; )
if (IsWindowVisible(hwnd))
{
GetWindowRect(hwnd, &rcOther);
if (IntersectRect(&rcDest, &rcVim, &rcOther))
return SW_INVALIDATE;
}
return 0;
}
/*
* On some Intel GPUs, the regions drawn just prior to ScrollWindowEx()
* may not be scrolled out properly.
* For gVim, when _OnScroll() is repeated, the character at the
* previous cursor position may be left drawn after scroll.
* The problem can be avoided by calling GetPixel() to get a pixel in
* the region before ScrollWindowEx().
*/
static void
intel_gpu_workaround(void)
{
GetPixel(s_hdc, FILL_X(gui.col), FILL_Y(gui.row));
}
/*
* Delete the given number of lines from the given row, scrolling up any
* text further down within the scroll region.
*/
void
gui_mch_delete_lines(
int row,
int num_lines)
{
RECT rc;
intel_gpu_workaround();
rc.left = FILL_X(gui.scroll_region_left);
rc.right = FILL_X(gui.scroll_region_right + 1);
rc.top = FILL_Y(row);
rc.bottom = FILL_Y(gui.scroll_region_bot + 1);
ScrollWindowEx(s_textArea, 0, -num_lines * gui.char_height,
&rc, &rc, NULL, NULL, get_scroll_flags());
UpdateWindow(s_textArea);
/* This seems to be required to avoid the cursor disappearing when
* scrolling such that the cursor ends up in the top-left character on
* the screen... But why? (Webb) */
/* It's probably fixed by disabling drawing the cursor while scrolling. */
/* gui.cursor_is_valid = FALSE; */
gui_clear_block(gui.scroll_region_bot - num_lines + 1,
gui.scroll_region_left,
gui.scroll_region_bot, gui.scroll_region_right);
}
/*
* Insert the given number of lines before the given row, scrolling down any
* following text within the scroll region.
*/
void
gui_mch_insert_lines(
int row,
int num_lines)
{
RECT rc;
intel_gpu_workaround();
rc.left = FILL_X(gui.scroll_region_left);
rc.right = FILL_X(gui.scroll_region_right + 1);
rc.top = FILL_Y(row);
rc.bottom = FILL_Y(gui.scroll_region_bot + 1);
/* The SW_INVALIDATE is required when part of the window is covered or
* off-screen. How do we avoid it when it's not needed? */
ScrollWindowEx(s_textArea, 0, num_lines * gui.char_height,
&rc, &rc, NULL, NULL, get_scroll_flags());
UpdateWindow(s_textArea);
gui_clear_block(row, gui.scroll_region_left,
row + num_lines - 1, gui.scroll_region_right);
}
/*ARGSUSED*/
void
gui_mch_exit(int rc)
{
#if defined(FEAT_DIRECTX)
DWriteContext_Close(s_dwc);
DWrite_Final();
s_dwc = NULL;
#endif
ReleaseDC(s_textArea, s_hdc);
DeleteObject(s_brush);
#ifdef FEAT_TEAROFF
/* Unload the tearoff bitmap */
(void)DeleteObject((HGDIOBJ)s_htearbitmap);
#endif
/* Destroy our window (if we have one). */
if (s_hwnd != NULL)
{
destroying = TRUE; /* ignore WM_DESTROY message now */
DestroyWindow(s_hwnd);
}
#ifdef GLOBAL_IME
global_ime_end();
#endif
}
static char_u *
logfont2name(LOGFONT lf)
{
char *p;
char *res;
char *charset_name;
char *font_name = lf.lfFaceName;
charset_name = charset_id2name((int)lf.lfCharSet);
#ifdef FEAT_MBYTE
/* Convert a font name from the current codepage to 'encoding'.
* TODO: Use Wide APIs (including LOGFONTW) instead of ANSI APIs. */
if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
{
int len;
acp_to_enc(lf.lfFaceName, (int)strlen(lf.lfFaceName),
(char_u **)&font_name, &len);
}
#endif
res = alloc((unsigned)(strlen(font_name) + 20
+ (charset_name == NULL ? 0 : strlen(charset_name) + 2)));
if (res != NULL)
{
p = res;
/* make a normal font string out of the lf thing:*/
sprintf((char *)p, "%s:h%d", font_name, pixels_to_points(
lf.lfHeight < 0 ? -lf.lfHeight : lf.lfHeight, TRUE));
while (*p)
{
if (*p == ' ')
*p = '_';
++p;
}
#ifndef MSWIN16_FASTTEXT
if (lf.lfItalic)
STRCAT(p, ":i");
if (lf.lfWeight >= FW_BOLD)
STRCAT(p, ":b");
#endif
if (lf.lfUnderline)
STRCAT(p, ":u");
if (lf.lfStrikeOut)
STRCAT(p, ":s");
if (charset_name != NULL)
{
STRCAT(p, ":c");
STRCAT(p, charset_name);
}
}
#ifdef FEAT_MBYTE
if (font_name != lf.lfFaceName)
vim_free(font_name);
#endif
return res;
}
#ifdef FEAT_MBYTE_IME
/*
* Set correct LOGFONT to IME. Use 'guifontwide' if available, otherwise use
* 'guifont'
*/
static void
update_im_font(void)
{
LOGFONT lf_wide;
if (p_guifontwide != NULL && *p_guifontwide != NUL
&& gui.wide_font != NOFONT
&& GetObject((HFONT)gui.wide_font, sizeof(lf_wide), &lf_wide))
norm_logfont = lf_wide;
else
norm_logfont = sub_logfont;
im_set_font(&norm_logfont);
}
#endif
#ifdef FEAT_MBYTE
/*
* Handler of gui.wide_font (p_guifontwide) changed notification.
*/
void
gui_mch_wide_font_changed()
{
# ifndef MSWIN16_FASTTEXT
LOGFONT lf;
# endif
# ifdef FEAT_MBYTE_IME
update_im_font();
# endif
# ifndef MSWIN16_FASTTEXT
gui_mch_free_font(gui.wide_ital_font);
gui.wide_ital_font = NOFONT;
gui_mch_free_font(gui.wide_bold_font);
gui.wide_bold_font = NOFONT;
gui_mch_free_font(gui.wide_boldital_font);
gui.wide_boldital_font = NOFONT;
if (gui.wide_font
&& GetObject((HFONT)gui.wide_font, sizeof(lf), &lf))
{
if (!lf.lfItalic)
{
lf.lfItalic = TRUE;
gui.wide_ital_font = get_font_handle(&lf);
lf.lfItalic = FALSE;
}
if (lf.lfWeight < FW_BOLD)
{
lf.lfWeight = FW_BOLD;
gui.wide_bold_font = get_font_handle(&lf);
if (!lf.lfItalic)
{
lf.lfItalic = TRUE;
gui.wide_boldital_font = get_font_handle(&lf);
}
}
}
# endif
}
#endif
/*
* Initialise vim to use the font with the given name.
* Return FAIL if the font could not be loaded, OK otherwise.
*/
/*ARGSUSED*/
int
gui_mch_init_font(char_u *font_name, int fontset)
{
LOGFONT lf;
GuiFont font = NOFONT;
char_u *p;
/* Load the font */
if (get_logfont(&lf, font_name, NULL, TRUE) == OK)
font = get_font_handle(&lf);
if (font == NOFONT)
return FAIL;
if (font_name == NULL)
font_name = lf.lfFaceName;
#if defined(FEAT_MBYTE_IME) || defined(GLOBAL_IME)
norm_logfont = lf;
sub_logfont = lf;
#endif
#ifdef FEAT_MBYTE_IME
update_im_font();
#endif
gui_mch_free_font(gui.norm_font);
gui.norm_font = font;
current_font_height = lf.lfHeight;
GetFontSize(font);
p = logfont2name(lf);
if (p != NULL)
{
hl_set_font_name(p);
/* When setting 'guifont' to "*" replace it with the actual font name.
* */
if (STRCMP(font_name, "*") == 0 && STRCMP(p_guifont, "*") == 0)
{
vim_free(p_guifont);
p_guifont = p;
}
else
vim_free(p);
}
#ifndef MSWIN16_FASTTEXT
gui_mch_free_font(gui.ital_font);
gui.ital_font = NOFONT;
gui_mch_free_font(gui.bold_font);
gui.bold_font = NOFONT;
gui_mch_free_font(gui.boldital_font);
gui.boldital_font = NOFONT;
if (!lf.lfItalic)
{
lf.lfItalic = TRUE;
gui.ital_font = get_font_handle(&lf);
lf.lfItalic = FALSE;
}
if (lf.lfWeight < FW_BOLD)
{
lf.lfWeight = FW_BOLD;
gui.bold_font = get_font_handle(&lf);
if (!lf.lfItalic)
{
lf.lfItalic = TRUE;
gui.boldital_font = get_font_handle(&lf);
}
}
#endif
return OK;
}
#ifndef WPF_RESTORETOMAXIMIZED
# define WPF_RESTORETOMAXIMIZED 2 /* just in case someone doesn't have it */
#endif
/*
* Return TRUE if the GUI window is maximized, filling the whole screen.
*/
int
gui_mch_maximized()
{
WINDOWPLACEMENT wp;
wp.length = sizeof(WINDOWPLACEMENT);
if (GetWindowPlacement(s_hwnd, &wp))
return wp.showCmd == SW_SHOWMAXIMIZED
|| (wp.showCmd == SW_SHOWMINIMIZED
&& wp.flags == WPF_RESTORETOMAXIMIZED);
return 0;
}
/*
* Called when the font changed while the window is maximized. Compute the
* new Rows and Columns. This is like resizing the window.
*/
void
gui_mch_newfont()
{
RECT rect;
GetWindowRect(s_hwnd, &rect);
if (win_socket_id == 0)
{
gui_resize_shell(rect.right - rect.left
- (GetSystemMetrics(SM_CXFRAME) +
GetSystemMetrics(SM_CXPADDEDBORDER)) * 2,
rect.bottom - rect.top
- (GetSystemMetrics(SM_CYFRAME) +
GetSystemMetrics(SM_CXPADDEDBORDER)) * 2
- GetSystemMetrics(SM_CYCAPTION)
#ifdef FEAT_MENU
- gui_mswin_get_menu_height(FALSE)
#endif
);
}
else
{
/* Inside another window, don't use the frame and border. */
gui_resize_shell(rect.right - rect.left,
rect.bottom - rect.top
#ifdef FEAT_MENU
- gui_mswin_get_menu_height(FALSE)
#endif
);
}
}
/*
* Set the window title
*/
/*ARGSUSED*/
void
gui_mch_settitle(
char_u *title,
char_u *icon)
{
set_window_title(s_hwnd, (title == NULL ? "VIM" : (char *)title));
}
#ifdef FEAT_MOUSESHAPE
/* Table for shape IDCs. Keep in sync with the mshape_names[] table in
* misc2.c! */
static LPCSTR mshape_idcs[] =
{
IDC_ARROW, /* arrow */
MAKEINTRESOURCE(0), /* blank */
IDC_IBEAM, /* beam */
IDC_SIZENS, /* updown */
IDC_SIZENS, /* udsizing */
IDC_SIZEWE, /* leftright */
IDC_SIZEWE, /* lrsizing */
IDC_WAIT, /* busy */
#ifdef WIN3264
IDC_NO, /* no */
#else
IDC_ICON, /* no */
#endif
IDC_ARROW, /* crosshair */
IDC_ARROW, /* hand1 */
IDC_ARROW, /* hand2 */
IDC_ARROW, /* pencil */
IDC_ARROW, /* question */
IDC_ARROW, /* right-arrow */
IDC_UPARROW, /* up-arrow */
IDC_ARROW /* last one */
};
void
mch_set_mouse_shape(int shape)
{
LPCSTR idc;
if (shape == MSHAPE_HIDE)
ShowCursor(FALSE);
else
{
if (shape >= MSHAPE_NUMBERED)
idc = IDC_ARROW;
else
idc = mshape_idcs[shape];
#ifdef SetClassLongPtr
SetClassLongPtr(s_textArea, GCLP_HCURSOR, (__int3264)(LONG_PTR)LoadCursor(NULL, idc));
#else
# ifdef WIN32
SetClassLong(s_textArea, GCL_HCURSOR, (long_u)LoadCursor(NULL, idc));
# else /* Win16 */
SetClassWord(s_textArea, GCW_HCURSOR, (WORD)LoadCursor(NULL, idc));
# endif
#endif
if (!p_mh)
{
POINT mp;
/* Set the position to make it redrawn with the new shape. */
(void)GetCursorPos((LPPOINT)&mp);
(void)SetCursorPos(mp.x, mp.y);
ShowCursor(TRUE);
}
}
}
#endif
#ifdef FEAT_BROWSE
/*
* The file browser exists in two versions: with "W" uses wide characters,
* without "W" the current codepage. When FEAT_MBYTE is defined and on
* Windows NT/2000/XP the "W" functions are used.
*/
# if defined(FEAT_MBYTE) && defined(WIN3264)
/*
* Wide version of convert_filter().
*/
static WCHAR *
convert_filterW(char_u *s)
{
char_u *tmp;
int len;
WCHAR *res;
tmp = convert_filter(s);
if (tmp == NULL)
return NULL;
len = (int)STRLEN(s) + 3;
res = enc_to_utf16(tmp, &len);
vim_free(tmp);
return res;
}
/*
* Wide version of gui_mch_browse(). Keep in sync!
*/
static char_u *
gui_mch_browseW(
int saving,
char_u *title,
char_u *dflt,
char_u *ext,
char_u *initdir,
char_u *filter)
{
/* We always use the wide function. This means enc_to_utf16() must work,
* otherwise it fails miserably! */
OPENFILENAMEW fileStruct;
WCHAR fileBuf[MAXPATHL];
WCHAR *wp;
int i;
WCHAR *titlep = NULL;
WCHAR *extp = NULL;
WCHAR *initdirp = NULL;
WCHAR *filterp;
char_u *p;
if (dflt == NULL)
fileBuf[0] = NUL;
else
{
wp = enc_to_utf16(dflt, NULL);
if (wp == NULL)
fileBuf[0] = NUL;
else
{
for (i = 0; wp[i] != NUL && i < MAXPATHL - 1; ++i)
fileBuf[i] = wp[i];
fileBuf[i] = NUL;
vim_free(wp);
}
}
/* Convert the filter to Windows format. */
filterp = convert_filterW(filter);
vim_memset(&fileStruct, 0, sizeof(OPENFILENAMEW));
#ifdef OPENFILENAME_SIZE_VERSION_400
/* be compatible with Windows NT 4.0 */
/* TODO: what to use for OPENFILENAMEW??? */
fileStruct.lStructSize = OPENFILENAME_SIZE_VERSION_400;
#else
fileStruct.lStructSize = sizeof(fileStruct);
#endif
if (title != NULL)
titlep = enc_to_utf16(title, NULL);
fileStruct.lpstrTitle = titlep;
if (ext != NULL)
extp = enc_to_utf16(ext, NULL);
fileStruct.lpstrDefExt = extp;
fileStruct.lpstrFile = fileBuf;
fileStruct.nMaxFile = MAXPATHL;
fileStruct.lpstrFilter = filterp;
fileStruct.hwndOwner = s_hwnd; /* main Vim window is owner*/
/* has an initial dir been specified? */
if (initdir != NULL && *initdir != NUL)
{
/* Must have backslashes here, no matter what 'shellslash' says */
initdirp = enc_to_utf16(initdir, NULL);
if (initdirp != NULL)
{
for (wp = initdirp; *wp != NUL; ++wp)
if (*wp == '/')
*wp = '\\';
}
fileStruct.lpstrInitialDir = initdirp;
}
/*
* TODO: Allow selection of multiple files. Needs another arg to this
* function to ask for it, and need to use OFN_ALLOWMULTISELECT below.
* Also, should we use OFN_FILEMUSTEXIST when opening? Vim can edit on
* files that don't exist yet, so I haven't put it in. What about
* OFN_PATHMUSTEXIST?
* Don't use OFN_OVERWRITEPROMPT, Vim has its own ":confirm" dialog.
*/
fileStruct.Flags = (OFN_NOCHANGEDIR | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY);
#ifdef FEAT_SHORTCUT
if (curbuf->b_p_bin)
fileStruct.Flags |= OFN_NODEREFERENCELINKS;
#endif
if (saving)
{
if (!GetSaveFileNameW(&fileStruct))
return NULL;
}
else
{
if (!GetOpenFileNameW(&fileStruct))
return NULL;
}
vim_free(filterp);
vim_free(initdirp);
vim_free(titlep);
vim_free(extp);
/* Convert from UCS2 to 'encoding'. */
p = utf16_to_enc(fileBuf, NULL);
if (p != NULL)
/* when out of memory we get garbage for non-ASCII chars */
STRCPY(fileBuf, p);
vim_free(p);
/* Give focus back to main window (when using MDI). */
SetFocus(s_hwnd);
/* Shorten the file name if possible */
return vim_strsave(shorten_fname1((char_u *)fileBuf));
}
# endif /* FEAT_MBYTE */
/*
* Convert the string s to the proper format for a filter string by replacing
* the \t and \n delimiters with \0.
* Returns the converted string in allocated memory.
*
* Keep in sync with convert_filterW() above!
*/
static char_u *
convert_filter(char_u *s)
{
char_u *res;
unsigned s_len = (unsigned)STRLEN(s);
unsigned i;
res = alloc(s_len + 3);
if (res != NULL)
{
for (i = 0; i < s_len; ++i)
if (s[i] == '\t' || s[i] == '\n')
res[i] = '\0';
else
res[i] = s[i];
res[s_len] = NUL;
/* Add two extra NULs to make sure it's properly terminated. */
res[s_len + 1] = NUL;
res[s_len + 2] = NUL;
}
return res;
}
/*
* Select a directory.
*/
char_u *
gui_mch_browsedir(char_u *title, char_u *initdir)
{
/* We fake this: Use a filter that doesn't select anything and a default
* file name that won't be used. */
return gui_mch_browse(0, title, (char_u *)_("Not Used"), NULL,
initdir, (char_u *)_("Directory\t*.nothing\n"));
}
/*
* Pop open a file browser and return the file selected, in allocated memory,
* or NULL if Cancel is hit.
* saving - TRUE if the file will be saved to, FALSE if it will be opened.
* title - Title message for the file browser dialog.
* dflt - Default name of file.
* ext - Default extension to be added to files without extensions.
* initdir - directory in which to open the browser (NULL = current dir)
* filter - Filter for matched files to choose from.
*
* Keep in sync with gui_mch_browseW() above!
*/
char_u *
gui_mch_browse(
int saving,
char_u *title,
char_u *dflt,
char_u *ext,
char_u *initdir,
char_u *filter)
{
OPENFILENAME fileStruct;
char_u fileBuf[MAXPATHL];
char_u *initdirp = NULL;
char_u *filterp;
char_u *p;
# if defined(FEAT_MBYTE) && defined(WIN3264)
if (os_version.dwPlatformId == VER_PLATFORM_WIN32_NT)
return gui_mch_browseW(saving, title, dflt, ext, initdir, filter);
# endif
if (dflt == NULL)
fileBuf[0] = NUL;
else
vim_strncpy(fileBuf, dflt, MAXPATHL - 1);
/* Convert the filter to Windows format. */
filterp = convert_filter(filter);
vim_memset(&fileStruct, 0, sizeof(OPENFILENAME));
#ifdef OPENFILENAME_SIZE_VERSION_400
/* be compatible with Windows NT 4.0 */
fileStruct.lStructSize = OPENFILENAME_SIZE_VERSION_400;
#else
fileStruct.lStructSize = sizeof(fileStruct);
#endif
fileStruct.lpstrTitle = title;
fileStruct.lpstrDefExt = ext;
fileStruct.lpstrFile = fileBuf;
fileStruct.nMaxFile = MAXPATHL;
fileStruct.lpstrFilter = filterp;
fileStruct.hwndOwner = s_hwnd; /* main Vim window is owner*/
/* has an initial dir been specified? */
if (initdir != NULL && *initdir != NUL)
{
/* Must have backslashes here, no matter what 'shellslash' says */
initdirp = vim_strsave(initdir);
if (initdirp != NULL)
for (p = initdirp; *p != NUL; ++p)
if (*p == '/')
*p = '\\';
fileStruct.lpstrInitialDir = initdirp;
}
/*
* TODO: Allow selection of multiple files. Needs another arg to this
* function to ask for it, and need to use OFN_ALLOWMULTISELECT below.
* Also, should we use OFN_FILEMUSTEXIST when opening? Vim can edit on
* files that don't exist yet, so I haven't put it in. What about
* OFN_PATHMUSTEXIST?
* Don't use OFN_OVERWRITEPROMPT, Vim has its own ":confirm" dialog.
*/
fileStruct.Flags = (OFN_NOCHANGEDIR | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY);
#ifdef FEAT_SHORTCUT
if (curbuf->b_p_bin)
fileStruct.Flags |= OFN_NODEREFERENCELINKS;
#endif
if (saving)
{
if (!GetSaveFileName(&fileStruct))
return NULL;
}
else
{
if (!GetOpenFileName(&fileStruct))
return NULL;
}
vim_free(filterp);
vim_free(initdirp);
/* Give focus back to main window (when using MDI). */
SetFocus(s_hwnd);
/* Shorten the file name if possible */
return vim_strsave(shorten_fname1((char_u *)fileBuf));
}
#endif /* FEAT_BROWSE */
/*ARGSUSED*/
static void
_OnDropFiles(
HWND hwnd,
HDROP hDrop)
{
#ifdef FEAT_WINDOWS
#ifdef WIN3264
# define BUFPATHLEN _MAX_PATH
# define DRAGQVAL 0xFFFFFFFF
#else
# define BUFPATHLEN MAXPATHL
# define DRAGQVAL 0xFFFF
#endif
#ifdef FEAT_MBYTE
WCHAR wszFile[BUFPATHLEN];
#endif
char szFile[BUFPATHLEN];
UINT cFiles = DragQueryFile(hDrop, DRAGQVAL, NULL, 0);
UINT i;
char_u **fnames;
POINT pt;
int_u modifiers = 0;
/* TRACE("_OnDropFiles: %d files dropped\n", cFiles); */
/* Obtain dropped position */
DragQueryPoint(hDrop, &pt);
MapWindowPoints(s_hwnd, s_textArea, &pt, 1);
reset_VIsual();
fnames = (char_u **)alloc(cFiles * sizeof(char_u *));
if (fnames != NULL)
for (i = 0; i < cFiles; ++i)
{
#ifdef FEAT_MBYTE
if (DragQueryFileW(hDrop, i, wszFile, BUFPATHLEN) > 0)
fnames[i] = utf16_to_enc(wszFile, NULL);
else
#endif
{
DragQueryFile(hDrop, i, szFile, BUFPATHLEN);
fnames[i] = vim_strsave(szFile);
}
}
DragFinish(hDrop);
if (fnames != NULL)
{
if ((GetKeyState(VK_SHIFT) & 0x8000) != 0)
modifiers |= MOUSE_SHIFT;
if ((GetKeyState(VK_CONTROL) & 0x8000) != 0)
modifiers |= MOUSE_CTRL;
if ((GetKeyState(VK_MENU) & 0x8000) != 0)
modifiers |= MOUSE_ALT;
gui_handle_drop(pt.x, pt.y, modifiers, fnames, cFiles);
s_need_activate = TRUE;
}
#endif
}
/*ARGSUSED*/
static int
_OnScroll(
HWND hwnd,
HWND hwndCtl,
UINT code,
int pos)
{
static UINT prev_code = 0; /* code of previous call */
scrollbar_T *sb, *sb_info;
long val;
int dragging = FALSE;
int dont_scroll_save = dont_scroll;
#ifndef WIN3264
int nPos;
#else
SCROLLINFO si;
si.cbSize = sizeof(si);
si.fMask = SIF_POS;
#endif
sb = gui_mswin_find_scrollbar(hwndCtl);
if (sb == NULL)
return 0;
if (sb->wp != NULL) /* Left or right scrollbar */
{
/*
* Careful: need to get scrollbar info out of first (left) scrollbar
* for window, but keep real scrollbar too because we must pass it to
* gui_drag_scrollbar().
*/
sb_info = &sb->wp->w_scrollbars[0];
}
else /* Bottom scrollbar */
sb_info = sb;
val = sb_info->value;
switch (code)
{
case SB_THUMBTRACK:
val = pos;
dragging = TRUE;
if (sb->scroll_shift > 0)
val <<= sb->scroll_shift;
break;
case SB_LINEDOWN:
val++;
break;
case SB_LINEUP:
val--;
break;
case SB_PAGEDOWN:
val += (sb_info->size > 2 ? sb_info->size - 2 : 1);
break;
case SB_PAGEUP:
val -= (sb_info->size > 2 ? sb_info->size - 2 : 1);
break;
case SB_TOP:
val = 0;
break;
case SB_BOTTOM:
val = sb_info->max;
break;
case SB_ENDSCROLL:
if (prev_code == SB_THUMBTRACK)
{
/*
* "pos" only gives us 16-bit data. In case of large file,
* use GetScrollPos() which returns 32-bit. Unfortunately it
* is not valid while the scrollbar is being dragged.
*/
val = GetScrollPos(hwndCtl, SB_CTL);
if (sb->scroll_shift > 0)
val <<= sb->scroll_shift;
}
break;
default:
/* TRACE("Unknown scrollbar event %d\n", code); */
return 0;
}
prev_code = code;
#ifdef WIN3264
si.nPos = (sb->scroll_shift > 0) ? val >> sb->scroll_shift : val;
SetScrollInfo(hwndCtl, SB_CTL, &si, TRUE);
#else
nPos = (sb->scroll_shift > 0) ? val >> sb->scroll_shift : val;
SetScrollPos(hwndCtl, SB_CTL, nPos, TRUE);
#endif
/*
* When moving a vertical scrollbar, move the other vertical scrollbar too.
*/
if (sb->wp != NULL)
{
scrollbar_T *sba = sb->wp->w_scrollbars;
HWND id = sba[ (sb == sba + SBAR_LEFT) ? SBAR_RIGHT : SBAR_LEFT].id;
#ifdef WIN3264
SetScrollInfo(id, SB_CTL, &si, TRUE);
#else
SetScrollPos(id, SB_CTL, nPos, TRUE);
#endif
}
/* Don't let us be interrupted here by another message. */
s_busy_processing = TRUE;
/* When "allow_scrollbar" is FALSE still need to remember the new
* position, but don't actually scroll by setting "dont_scroll". */
dont_scroll = !allow_scrollbar;
gui_drag_scrollbar(sb, val, dragging);
s_busy_processing = FALSE;
dont_scroll = dont_scroll_save;
return 0;
}
/*
* Get command line arguments.
* Use "prog" as the name of the program and "cmdline" as the arguments.
* Copy the arguments to allocated memory.
* Return the number of arguments (including program name).
* Return pointers to the arguments in "argvp". Memory is allocated with
* malloc(), use free() instead of vim_free().
* Return pointer to buffer in "tofree".
* Returns zero when out of memory.
*/
/*ARGSUSED*/
int
get_cmd_args(char *prog, char *cmdline, char ***argvp, char **tofree)
{
int i;
char *p;
char *progp;
char *pnew = NULL;
char *newcmdline;
int inquote;
int argc;
char **argv = NULL;
int round;
*tofree = NULL;
#ifdef FEAT_MBYTE
/* Try using the Unicode version first, it takes care of conversion when
* 'encoding' is changed. */
argc = get_cmd_argsW(&argv);
if (argc != 0)
goto done;
#endif
/* Handle the program name. Remove the ".exe" extension, and find the 1st
* non-space. */
p = strrchr(prog, '.');
if (p != NULL)
*p = NUL;
for (progp = prog; *progp == ' '; ++progp)
;
/* The command line is copied to allocated memory, so that we can change
* it. Add the size of the string, the separating NUL and a terminating
* NUL. */
newcmdline = malloc(STRLEN(cmdline) + STRLEN(progp) + 2);
if (newcmdline == NULL)
return 0;
/*
* First round: count the number of arguments ("pnew" == NULL).
* Second round: produce the arguments.
*/
for (round = 1; round <= 2; ++round)
{
/* First argument is the program name. */
if (pnew != NULL)
{
argv[0] = pnew;
strcpy(pnew, progp);
pnew += strlen(pnew);
*pnew++ = NUL;
}
/*
* Isolate each argument and put it in argv[].
*/
p = cmdline;
argc = 1;
while (*p != NUL)
{
inquote = FALSE;
if (pnew != NULL)
argv[argc] = pnew;
++argc;
while (*p != NUL && (inquote || (*p != ' ' && *p != '\t')))
{
/* Backslashes are only special when followed by a double
* quote. */
i = (int)strspn(p, "\\");
if (p[i] == '"')
{
/* Halve the number of backslashes. */
if (i > 1 && pnew != NULL)
{
vim_memset(pnew, '\\', i / 2);
pnew += i / 2;
}
/* Even nr of backslashes toggles quoting, uneven copies
* the double quote. */
if ((i & 1) == 0)
inquote = !inquote;
else if (pnew != NULL)
*pnew++ = '"';
p += i + 1;
}
else if (i > 0)
{
/* Copy span of backslashes unmodified. */
if (pnew != NULL)
{
vim_memset(pnew, '\\', i);
pnew += i;
}
p += i;
}
else
{
if (pnew != NULL)
*pnew++ = *p;
#ifdef FEAT_MBYTE
/* Can't use mb_* functions, because 'encoding' is not
* initialized yet here. */
if (IsDBCSLeadByte(*p))
{
++p;
if (pnew != NULL)
*pnew++ = *p;
}
#endif
++p;
}
}
if (pnew != NULL)
*pnew++ = NUL;
while (*p == ' ' || *p == '\t')
++p; /* advance until a non-space */
}
if (round == 1)
{
argv = (char **)malloc((argc + 1) * sizeof(char *));
if (argv == NULL )
{
free(newcmdline);
return 0; /* malloc error */
}
pnew = newcmdline;
*tofree = newcmdline;
}
}
#ifdef FEAT_MBYTE
done:
#endif
argv[argc] = NULL; /* NULL-terminated list */
*argvp = argv;
return argc;
}