📄 gui_w32.c
字号:
/* 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.
*
* Windows GUI.
*
* GUI support for Microsoft Windows. Win32 initially; maybe Win16 later
*
* George V. Reilly <gvr@halcyon.com> 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).
*/
#define WIN32_FIND_REPLACE /* include code for find/replace dialog */
#define MENUHINTS /* show menu hints in command line */
#include "vim.h"
#include "version.h" /* used by dialog box routine for default title */
#include <windows.h>
#include <shellapi.h>
#ifdef USE_GUI_WIN32_TOOLBAR
#include <commctrl.h>
#endif
#include <windowsx.h>
/* 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
/* 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.
#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 HINSTANCE void *
# define HWND void *
# define HDC void *
# define HMENU void *
# define UINT int
# define WPARAM int
# define LPARAM int
typedef int LOGFONT[];
# define ENUMLOGFONT int
# define NEWTEXTMETRIC int
# define VOID void
# define CALLBACK
# define WORD int
# define DWORD int
# define HBITMAP int
# define HDROP int
# define BOOL int
# define PWORD int
# define LPWORD int
# define LPRECT int
# define LRESULT int
# define WINAPI
# define APIENTRY
# define LPSTR int
# define LPWINDOWPOS int
# define RECT int
# define LPCREATESTRUCT int
# define _cdecl
# define FINDREPLACE int
# define LPCTSTR int
# define OSVERSIONINFO int
#endif
/* For the Intellimouse: */
#ifndef WM_MOUSEWHEEL
#define WM_MOUSEWHEEL 0x20a
#endif
#ifdef MULTI_BYTE
static int sysfixed_width = 0;
static int sysfixed_height = 0;
#endif
/* Local variables: */
static int s_button_pending = -1;
static int s_x_pending;
static int s_y_pending;
static UINT s_kFlags_pending;
static HINSTANCE s_hinst = NULL;
static HWND s_hwnd = NULL;
static HDC s_hdc = NULL;
#ifdef USE_GUI_WIN32_TOOLBAR
static HWND s_toolbarhwnd = NULL;
#endif
#ifdef WIN32_FIND_REPLACE
static HWND s_findrep_hwnd = NULL;
static UINT s_findrep_msg = 0;
static FINDREPLACE s_findrep_struct;
static int s_findrep_is_find;
#endif
static HWND s_textArea = NULL;
static HMENU s_menuBar = NULL;
static UINT s_menu_id = 0;
static UINT s_wait_timer = 0; /* Timer for get char from user */
static int destroying = FALSE; /* calling DestroyWindow() ourselves */
static UINT s_uMsg = 0;
static WPARAM s_wParam = 0;
static LPARAM s_lParam = 0;
static int s_timed_out = FALSE;
static const LOGFONT s_lfDefault =
{
-12, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET,
OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
PROOF_QUALITY, FIXED_PITCH | FF_DONTCARE,
"Fixedsys" /* see _ReadVimIni */
};
/* Initialise the "current height" to -12 (same as s_lfDefault) just
* in case the user specifies a font in "guifont" with no size before a font
* with an explicit size has been set. This defaults the size to this value
* (-12 equates to roughly 9pt).
*/
static int current_font_height = -12;
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'},
{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 */
/* 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},
{CR, CR, NUL},
/* End of list marker: */
{0, 0, 0}
};
#define VIM_NAME "vim"
#define VIM_CLASS "Vim"
static int dead_key = 0; /* 0 - no dead key, 1 - dead key pressed */
static OSVERSIONINFO os_version; /* like it says. Init in gui_mch_init() */
/* ron: No need to be stingy on Win32. Make it 16K - s/b big enough for
* everyone!
* I have had problems with the original 1000 byte, and with 2 or 5 K. But
* 16K should be good for all but the biggest. Anyway, we free the memory
* right away.
*/
#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 BOOL CenterWindow(HWND hwndChild, HWND hwndParent);
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, GuiMenu *menu, int initX, int initY);
static void rebuild_tearoff(GuiMenu *menu);
static void get_dialog_font_metrics(void);
/*
* The scrollbar stuff can handle only up to 32767 lines. When the file is
* longer, scroll_shift is set to the number of shifts to reduce the count.
*/
static int scroll_shift = 0;
/* Intellimouse support */
static int mouse_scroll_lines = 0;
static UINT msh_msgmousewheel = 0;
static int s_usenewlook; /* emulate W95/NT4 non-bold dialogs */
static WORD s_dlgfntheight; /* height of the dialog font */
static WORD s_dlgfntwidth; /* width of the dialog font */
static HBITMAP s_htearbitmap; /* bitmap used to indicate tearoff */
#ifdef USE_GUI_WIN32_TOOLBAR
static void initialise_toolbar(void);
static int get_toolbar_bitmap(char_u *name);
#endif
#ifdef WIN32_FIND_REPLACE
static void initialise_findrep(char_u *initial_string);
static void find_rep_mode_adjust(char_u * buf);
#endif
#ifdef DEBUG
/*
* Print out the last Windows error message
*/
static void
print_windows_error(void)
{
LPVOID lpMsgBuf;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL, GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf, 0, NULL);
TRACE1("Error: %s\n", lpMsgBuf);
LocalFree(lpMsgBuf);
}
#endif /* DEBUG */
/*
* 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);
}
/*
* Figure out how high the menu bar is at the moment.
*/
static int
gui_w32_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 */
{
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) */
{
GetMenuItemRect(s_hwnd, s_menuBar, 0, &rc1);
GetMenuItemRect(s_hwnd, s_menuBar, num - 1, &rc2);
menu_height = rc2.bottom - rc1.top + 1;
}
}
if (fix_window && menu_height != old_menu_height)
gui_set_winsize(FALSE);
old_menu_height = menu_height;
return menu_height;
}
/*
* Cursor blink functions.
*
* This is a simple state machine:
* BLINK_NONE not blinking at all
* BLINK_OFF blinking, cursor is not shown
* BLINK_ON blinking, cursor is shown
*/
#define BLINK_NONE 0
#define BLINK_OFF 1
#define BLINK_ON 2
static int blink_state = BLINK_NONE;
static long_u blink_waittime = 700;
static long_u blink_ontime = 400;
static long_u blink_offtime = 250;
static UINT blink_timer = 0;
void
gui_mch_set_blinking(long wait, long on, long off)
{
blink_waittime = wait;
blink_ontime = on;
blink_offtime = off;
}
/* ARGSUSED */
static VOID CALLBACK
_OnBlinkTimer(
HWND hwnd,
UINT uMsg,
UINT idEvent,
DWORD dwTime)
{
MSG msg;
/*
TRACE2("Got timer event, id %d, blink_timer %d\n", idEvent, blink_timer);
*/
KillTimer(NULL, idEvent);
/* Eat spurious WM_TIMER messages */
while (PeekMessage(&msg, hwnd, WM_TIMER, WM_TIMER, PM_REMOVE))
;
if (blink_state == BLINK_ON)
{
gui_undraw_cursor();
blink_state = BLINK_OFF;
blink_timer = SetTimer(NULL, 0, (UINT)blink_offtime,
(TIMERPROC)_OnBlinkTimer);
}
else
{
gui_update_cursor(TRUE, FALSE);
blink_state = BLINK_ON;
blink_timer = SetTimer(NULL, 0, (UINT)blink_ontime,
(TIMERPROC)_OnBlinkTimer);
}
}
static void
gui_w32_rm_blink_timer(void)
{
MSG msg;
if (blink_timer != 0)
{
KillTimer(NULL, blink_timer);
/* Eat spurious WM_TIMER messages */
while (PeekMessage(&msg, s_hwnd, WM_TIMER, WM_TIMER, PM_REMOVE))
;
blink_timer = 0;
}
}
/*
* Stop the cursor blinking. Show the cursor if it wasn't shown.
*/
void
gui_mch_stop_blink(void)
{
gui_w32_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_w32_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 = SetTimer(NULL, 0, (UINT)blink_waittime,
(TIMERPROC)_OnBlinkTimer);
blink_state = BLINK_ON;
gui_update_cursor(TRUE, FALSE);
}
}
/*
* Call-back routines.
*/
static VOID CALLBACK
_OnTimer(
HWND hwnd,
UINT uMsg,
UINT idEvent,
DWORD dwTime)
{
MSG msg;
/*
TRACE2("Got timer event, id %d, s_wait_timer %d\n", idEvent, s_wait_timer);
*/
KillTimer(NULL, idEvent);
s_timed_out = TRUE;
/* Eat spurious WM_TIMER messages */
while (PeekMessage(&msg, hwnd, WM_TIMER, WM_TIMER, PM_REMOVE))
;
if (idEvent == s_wait_timer)
s_wait_timer = 0;
}
/*
* Get this message when the user clicks on the cross in the top right corner
* of a Windows95 window.
*/
static void
_OnClose(
HWND hwnd)
{
#ifdef USE_BROWSE
int save_browse = browse;
#endif
#if defined(GUI_DIALOG) || defined(CON_DIALOG)
int save_confirm = confirm;
#endif
/* Only exit when there are no changed files */
exiting = TRUE;
#ifdef USE_BROWSE
browse = TRUE;
#endif
#if defined(GUI_DIALOG) || defined(CON_DIALOG)
confirm = TRUE;
#endif
if (!check_changed_any(FALSE)) /* will give warning for changed buffer */
getout(0);
exiting = FALSE;
#ifdef USE_BROWSE
browse = save_browse;
#endif
#if defined(GUI_DIALOG) || defined(CON_DIALOG)
confirm = save_confirm;
#endif
setcursor(); /* position cursor */
out_flush();
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -