📄 gui.c
字号:
MOUSE_LEFT
# endif
)
{
clip_start_selection(button, x, y, repeated_click, modifiers);
did_clip = TRUE;
}
# ifdef RISCOS
else if (button == MOUSE_LEFT)
{
clip_clear_selection();
did_clip = TRUE;
}
# endif
/* Always allow pasting */
if (button != MOUSE_MIDDLE)
{
if (!mouse_has(checkfor) || button == MOUSE_RELEASE)
return;
button = MOUSE_LEFT;
}
repeated_click = FALSE;
}
if (clipboard.state != SELECT_CLEARED && !did_clip)
clip_clear_selection();
#endif
/*
* Don't put mouse events in the input queue while executing an external
* command.
*/
if (!termcap_active)
return;
row = check_row(Y_2_ROW(y));
col = check_col(X_2_COL(x));
/*
* If we are dragging and the mouse hasn't moved far enough to be on a
* different character, then don't send an event to vim.
*/
if (button == MOUSE_DRAG && row == prev_row && col == prev_col)
return;
/*
* If topline has changed (window scrolled) since the last click, reset
* repeated_click, because we don't want starting Visual mode when
* clicking on a different character in the text.
*/
if (curwin->w_topline != gui_prev_topline)
repeated_click = FALSE;
string[0] = CSI; /* this sequence is recognized by check_termcode() */
string[1] = KS_MOUSE;
string[2] = K_FILLER;
if (button != MOUSE_DRAG && button != MOUSE_RELEASE)
{
if (repeated_click)
{
/*
* Handle multiple clicks. They only count if the mouse is still
* pointing at the same character.
*/
if (button != prev_button || row != prev_row || col != prev_col)
num_clicks = 1;
else if (++num_clicks > 4)
num_clicks = 1;
}
else
num_clicks = 1;
prev_button = button;
gui_prev_topline = curwin->w_topline;
string[3] = (char_u)(button | 0x20);
SET_NUM_MOUSE_CLICKS(string[3], num_clicks);
}
else
string[3] = (char_u)button;
string[3] |= modifiers;
string[4] = (char_u)(col + ' ' + 1);
string[5] = (char_u)(row + ' ' + 1);
add_to_input_buf(string, 6);
prev_row = row;
prev_col = col;
}
/*
* Menu stuff.
*/
void
gui_menu_cb(menu)
GuiMenu *menu;
{
char_u bytes[3 + sizeof(long_u)];
bytes[0] = CSI;
bytes[1] = KS_MENU;
bytes[2] = K_FILLER;
add_long_to_buf((long_u)menu, bytes + 3);
add_to_input_buf(bytes, 3 + sizeof(long_u));
}
/*
* Return the index into the menu->strings or menu->noremap arrays for the
* current state. Returns MENU_INDEX_INVALID if there is no mapping for the
* given menu in the current mode.
*/
int
gui_get_menu_index(menu, state)
GuiMenu *menu;
int state;
{
int idx;
if (VIsual_active)
idx = MENU_INDEX_VISUAL;
else if ((state & INSERT))
idx = MENU_INDEX_INSERT;
else if ((state & CMDLINE))
idx = MENU_INDEX_CMDLINE;
else if (finish_op)
idx = MENU_INDEX_OP_PENDING;
else if ((state & NORMAL))
idx = MENU_INDEX_NORMAL;
else
idx = MENU_INDEX_INVALID;
if (idx != MENU_INDEX_INVALID && menu->strings[idx] == NULL)
idx = MENU_INDEX_INVALID;
return idx;
}
/*
* Return the modes specified by the given menu command (eg :menu! returns
* MENU_CMDLINE_MODE | MENU_INSERT_MODE). If noremap is not NULL, then the
* flag it points to is set according to whether the command is a "nore"
* command. If unmenu is not NULL, then the flag it points to is set
* according to whether the command is an "unmenu" command.
*/
static int
gui_get_menu_cmd_modes(cmd, forceit, noremap, unmenu)
char_u *cmd;
int forceit; /* Was there a "!" after the command? */
int *noremap;
int *unmenu;
{
int modes;
switch (*cmd++)
{
case 'v': /* vmenu, vunmenu, vnoremenu */
modes = MENU_VISUAL_MODE;
break;
case 'o': /* omenu */
modes = MENU_OP_PENDING_MODE;
break;
case 'i': /* imenu */
modes = MENU_INSERT_MODE;
break;
case 't':
modes = MENU_TIP_MODE; /* tmenu */
break;
case 'c': /* cmenu */
modes = MENU_CMDLINE_MODE;
break;
case 'a': /* amenu */
modes = MENU_INSERT_MODE | MENU_CMDLINE_MODE | MENU_NORMAL_MODE
| MENU_VISUAL_MODE | MENU_OP_PENDING_MODE;
break;
case 'n':
if (cmd[1] != 'o') /* nmenu */
{
modes = MENU_NORMAL_MODE;
break;
}
/* FALLTHROUGH */
default:
--cmd;
if (forceit) /* menu!! */
modes = MENU_INSERT_MODE | MENU_CMDLINE_MODE;
else /* menu */
modes = MENU_NORMAL_MODE | MENU_VISUAL_MODE
| MENU_OP_PENDING_MODE;
}
if (noremap != NULL)
*noremap = (*cmd == 'n');
if (unmenu != NULL)
*unmenu = (*cmd == 'u');
return modes;
}
#define MENUDEPTH 10 /* maximum depth of menus */
/*
* Do the :menu commands.
*/
void
do_menu(eap)
EXARG *eap; /* Ex command arguments */
{
char_u *menu_path;
int modes;
char_u *map_to;
int noremap;
int unmenu;
char_u *map_buf;
char_u *arg;
char_u *p;
int i;
int old_menu_height;
int pri_tab[MENUDEPTH + 1];
modes = gui_get_menu_cmd_modes(eap->cmd, eap->forceit, &noremap, &unmenu);
arg = eap->arg;
/* fill in the priority table */
for (p = arg; *p; ++p)
if (!isdigit(*p) && *p != '.')
break;
if (vim_iswhite(*p))
{
for (i = 0; i < MENUDEPTH && !vim_iswhite(*arg); ++i)
{
pri_tab[i] = getdigits(&arg);
if (pri_tab[i] == 0)
pri_tab[i] = 500;
if (*arg == '.')
++arg;
}
arg = skipwhite(arg);
}
else if (eap->addr_count)
{
pri_tab[0] = eap->line2;
i = 1;
}
else
i = 0;
while (i < MENUDEPTH)
pri_tab[i++] = 500;
pri_tab[MENUDEPTH] = -1; /* mark end of the table */
menu_path = arg;
if (*menu_path == NUL)
{
gui_show_menus(menu_path, modes);
return;
}
/* skip the menu name, and translate <Tab> into a real TAB */
while (*arg && !vim_iswhite(*arg))
{
if ((*arg == '\\' || *arg == Ctrl('V')) && arg[1] != NUL)
arg++;
else if (STRNICMP(arg, "<TAB>", 5) == 0)
{
*arg = TAB;
mch_memmove(arg + 1, arg + 5, STRLEN(arg + 4));
}
arg++;
}
if (*arg != NUL)
*arg++ = NUL;
arg = skipwhite(arg);
map_to = arg;
if (*map_to == NUL && !unmenu)
{
gui_show_menus(menu_path, modes);
return;
}
else if (*map_to != NUL && unmenu)
{
EMSG("Trailing characters");
return;
}
old_menu_height = gui.menu_height;
if (unmenu)
{
if (STRCMP(menu_path, "*") == 0) /* meaning: remove all menus */
menu_path = (char_u *)"";
gui_remove_menu(&gui.root_menu, menu_path, modes, FALSE);
/*
* For the PopUp menu, remove a menu for each mode separately.
*/
if (STRNCMP(menu_path, "PopUp", 5) == 0)
{
for (i = 0; i < MENU_INDEX_TIP; ++i)
if (modes & (1 << i))
{
p = popup_mode_name(menu_path, i);
if (p != NULL)
{
gui_remove_menu(&gui.root_menu, p, 1 << i, TRUE);
vim_free(p);
}
}
}
}
else
{
/* Replace special key codes */
map_to = replace_termcodes(map_to, &map_buf, FALSE, TRUE);
gui_add_menu_path(menu_path, modes,
pri_tab, gui_menu_cb, map_to, noremap
#ifdef USE_GUI_WIN32
, TRUE
#endif
);
/*
* For the PopUp menu, add a menu for each mode separately.
*/
if (STRNCMP(menu_path, "PopUp", 5) == 0)
{
for (i = 0; i < MENU_INDEX_TIP; ++i)
if (modes & (1 << i))
{
p = popup_mode_name(menu_path, i);
if (p != NULL)
{
/* Include all modes, to make ":amenu" work */
gui_add_menu_path(p, modes,
pri_tab, gui_menu_cb, map_to, noremap
#ifdef USE_GUI_WIN32
, TRUE
#endif
);
vim_free(p);
}
}
}
vim_free(map_buf);
}
/* If the menubar height changed, resize the window */
if (gui.menu_height != old_menu_height)
gui_set_winsize(FALSE);
}
/*
* Modify a menu name starting with "PopUp" to include the mode character.
* Returns the name in allocated memory (NULL for failure).
*/
static char_u *
popup_mode_name(name, idx)
char_u *name;
int idx;
{
char_u *p;
int len = STRLEN(name);
p = vim_strnsave(name, len + 1);
if (p != NULL)
{
mch_memmove(p + 6, p + 5, len - 4);
p[5] = MENU_MODE_CHARS[idx];
}
return p;
}
/*
* Used recursively by gui_update_menus (see below)
*/
static void
gui_update_menus_recurse(menu, mode)
GuiMenu *menu;
int mode;
{
int i;
while (menu)
{
if (menu->modes & mode)
i = FALSE;
else
i = TRUE;
if (vim_strchr(p_guioptions, GO_GREY) != NULL)
gui_mch_menu_grey(menu, i);
else
gui_mch_menu_hidden(menu, i);
gui_update_menus_recurse(menu->children, mode);
menu = menu->next;
}
}
/*
* Make sure only the valid menu items appear for this mode. If
* force_menu_update is not TRUE, then we only do this if the mode has changed
* since last time. If "modes" is not 0, then we use these modes instead.
*/
void
gui_update_menus(modes)
int modes;
{
static int prev_mode = -1;
int mode = 0;
if (modes != 0x0)
mode = modes;
else
{
mode = get_menu_mode();
if (mode == MENU_INDEX_INVALID)
mode = 0;
else
mode = (1 << mode);
}
if (force_menu_update || mode != prev_mode)
{
gui_update_menus_recurse(gui.root_menu, mode);
gui_mch_draw_menubar();
prev_mode = mode;
force_menu_update = FALSE;
}
}
static int
get_menu_mode()
{
if (VIsual_active)
return MENU_INDEX_VISUAL;
if (State & INSERT)
return MENU_INDEX_INSERT;
if (State & CMDLINE)
return MENU_INDEX_CMDLINE;
if (finish_op)
return MENU_INDEX_OP_PENDING;
if (State & NORMAL)
return MENU_INDEX_NORMAL;
return MENU_INDEX_INVALID;
}
/*
* Check if a key is used as a mnemonic for a toplevel menu.
* Case of the key is ignored.
*/
int
gui_is_menu_shortcut(key)
int key;
{
GuiMenu *menu;
key = TO_LOWER(key);
for (menu = gui.root_menu; menu != NULL; menu = menu->next)
if (TO_LOWER(menu->mnemonic) == key)
return TRUE;
return FALSE;
}
#if defined(USE_GUI_WIN32) || defined(PROTO)
/*
* Deal with tearoff items that are added like a menu item.
* Currently only for Win32 GUI. Others may follow later.
*/
void
gui_mch_toggle_tearoffs(int enable)
{
int pri_tab[MENUDEPTH + 1];
int i;
if (enable)
{
for (i = 0; i < MENUDEPTH; ++i)
pri_tab[i] = 500;
pri_tab[MENUDEPTH] = -1;
gui_create_tearoffs_recurse(gui.root_menu, (char_u *)"", pri_tab, 0);
}
else
gui_destroy_tearoffs_recurse(gui.root_menu);
s_tearoffs = enable;
}
/*
* Recursively add tearoff items
*/
static void
gui_create_tearoffs_recurse(menu, pname, pri_tab, pri_idx)
GuiMenu *menu;
const char_u *pname;
int *pri_tab;
int pri_idx;
{
char_u *newpname = NULL;
int len;
char_u *s;
char_u *d;
if (pri_tab[pri_idx + 1] != -1)
++pri_idx;
while (menu != NULL)
{
if (menu->children != NULL && gui_menubar_menu(menu->name))
{
/* Add the menu name to the menu path. Insert a backslash before
* dots (it's used to separate menu names). */
len = STRLEN(pname) + STRLEN(menu->name);
for (s = menu->name; *s; ++s)
if (*s == '.' || *s == '\\')
++len;
newpname = alloc(len + TEAR_LEN + 2);
if (newpname != NULL)
{
STRCPY(newpname, pname);
d = newpname + STRLEN(newpname);
for (s = menu->name; *s; ++s)
{
if (*s == '.' || *s == '\\')
*d++ = '\\';
*d++ = *s;
}
*d = NUL;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -