📄 gui.c
字号:
/* check if tearoff already exists */
if (STRCMP(menu->children->name, TEAR_STRING) != 0)
{
gui_add_tearoff(newpname, pri_tab, pri_idx - 1);
*d = NUL; /* remove TEAR_STRING */
}
STRCAT(newpname, ".");
gui_create_tearoffs_recurse(menu->children, newpname,
pri_tab, pri_idx);
vim_free(newpname);
}
}
menu = menu->next;
}
}
/*
* Add tear-off menu item for a submenu.
* "tearpath" is the menu path, and must have room to add TEAR_STRING.
*/
static void
gui_add_tearoff(tearpath, pri_tab, pri_idx)
char_u *tearpath;
int *pri_tab;
int pri_idx;
{
char_u *tbuf;
int t;
tbuf = alloc(5 + STRLEN(tearpath));
if (tbuf != NULL)
{
tbuf[0] = K_SPECIAL;
tbuf[1] = K_SECOND(K_TEAROFF);
tbuf[2] = K_THIRD(K_TEAROFF);
STRCPY(tbuf + 3, tearpath);
STRCAT(tbuf + 3, "\r");
STRCAT(tearpath, ".");
STRCAT(tearpath, TEAR_STRING);
/* Priority of tear-off is always 1 */
t = pri_tab[pri_idx + 1];
pri_tab[pri_idx + 1] = 1;
gui_add_menu_path(tearpath, MENU_ALL_MODES, pri_tab,
gui_menu_cb, tbuf, TRUE, FALSE);
gui_add_menu_path(tearpath, MENU_TIP_MODE, pri_tab,
gui_menu_cb, (char_u *)"Tear off this menu", TRUE, FALSE);
pri_tab[pri_idx + 1] = t;
vim_free(tbuf);
}
}
/*
* Recursively destroy tearoff items
*/
static void
gui_destroy_tearoffs_recurse(menu)
GuiMenu *menu;
{
GuiMenu *oldmenu;
while (menu)
{
if (menu->children)
{
/* check if tearoff exists */
if ( STRCMP(menu->children->name, TEAR_STRING) == 0)
{
/* Disconnect the item */
oldmenu = menu->children;
menu->children = oldmenu->next;
/* Free the memory */
gui_free_menu(oldmenu);
}
if (menu->children != NULL) /* might have been last one */
gui_destroy_tearoffs_recurse(menu->children);
}
menu = menu->next;
}
}
#endif /*USE_GUI_WIN32*/
/*
* Add the menu with the given name to the menu hierarchy
*/
static int
gui_add_menu_path(menu_path, modes, pri_tab, call_back, call_data, noremap
#ifdef USE_GUI_WIN32
, addtearoff
#endif
)
char_u *menu_path;
int modes;
int *pri_tab;
void (*call_back)();
char_u *call_data;
int noremap;
#ifdef USE_GUI_WIN32
int addtearoff; /* may add tearoff item */
#endif
{
char_u *path_name;
GuiMenu **menup;
GuiMenu *menu = NULL;
GuiMenu *parent;
GuiMenu **lower_pri;
char_u *p;
char_u *name;
char_u *dname;
char_u *next_name;
int i;
int c;
int idx, new_idx;
int pri_idx = 0;
int old_modes = 0;
int amenu;
/* Make a copy so we can stuff around with it, since it could be const */
path_name = vim_strsave(menu_path);
if (path_name == NULL)
return FAIL;
menup = &gui.root_menu;
parent = NULL;
name = path_name;
while (*name)
{
/* Get name of this element in the menu hierarchy, and the simplified
* name (without mnemonic and accelerator text). */
next_name = gui_menu_name_skip(name);
dname = gui_menu_text(name, NULL, NULL);
/* See if it's already there */
lower_pri = menup;
idx = 0;
new_idx = 0;
menu = *menup;
while (menu != NULL)
{
if (menu_name_equal(name, menu) || menu_name_equal(dname, menu))
{
if (*next_name == NUL && menu->children != NULL)
{
if (!gui_sys_menu)
EMSG("Menu path must not lead to a sub-menu");
vim_free(path_name);
vim_free(dname);
return FAIL;
}
if (*next_name != NUL && menu->children == NULL
#ifdef USE_GUI_WIN32
&& addtearoff
#endif
)
{
if (!gui_sys_menu)
EMSG("Part of menu-item path is not sub-menu");
vim_free(path_name);
vim_free(dname);
return FAIL;
}
break;
}
menup = &menu->next;
/* Count menus, to find where this one needs to be inserted.
* Ignore menus that are not in the menubar (PopUp and Toolbar) */
if (parent != NULL || gui_menubar_menu(menu->name))
{
++idx;
if (menu->priority <= pri_tab[pri_idx])
{
lower_pri = menup;
new_idx = idx;
}
}
menu = menu->next;
}
if (menu == NULL)
{
if (*next_name == NUL && parent == NULL)
{
EMSG("Must not add menu items directly to menu bar");
vim_free(path_name);
vim_free(dname);
return FAIL;
}
/* Not already there, so lets add it */
menu = (GuiMenu *)alloc_clear(sizeof(GuiMenu));
if (menu == NULL)
{
vim_free(path_name);
vim_free(dname);
return FAIL;
}
menu->modes = modes;
menu->name = vim_strsave(name);
/* separate mnemonic and accelerator text from actual menu name */
menu->dname = gui_menu_text(name, &menu->mnemonic, &menu->actext);
menu->priority = pri_tab[pri_idx];
/*
* Add after menu that has lower priority.
*/
menu->next = *lower_pri;
*lower_pri = menu;
if (gui.in_use) /* Otherwise it will be added when GUI starts */
{
if (*next_name == NUL)
{
/* Real menu item, not sub-menu */
gui_mch_add_menu_item(menu, parent, new_idx);
/* Want to update menus now even if mode not changed */
force_menu_update = TRUE;
}
else
{
/* Sub-menu (not at end of path yet) */
gui_mch_add_menu(menu, parent, new_idx);
}
}
#ifdef USE_GUI_WIN32
/* When adding a new submenu, may add a tearoff item */
if ( addtearoff
&& *next_name
&& vim_strchr(p_guioptions, GO_TEAROFF) != NULL
&& gui_menubar_menu(name))
{
char_u *tearpath;
/*
* The pointers next_name & path_name refer to a string with
* \'s and ^V's stripped out. But menu_path is a "raw"
* string, so we must correct for special characters.
*/
tearpath = alloc(STRLEN(menu_path) + TEAR_LEN + 2);
if (tearpath != NULL)
{
char_u *s;
int idx;
STRCPY(tearpath, menu_path);
idx = next_name - path_name - 1;
for (s = tearpath; *s && s < tearpath + idx; ++s)
if ((*s == '\\' || *s == Ctrl('V')) && s[1])
{
++idx;
++s;
}
tearpath[idx] = NUL;
gui_add_tearoff(tearpath, pri_tab, pri_idx);
vim_free(tearpath);
}
}
#endif
old_modes = 0;
}
else
{
old_modes = menu->modes;
/*
* If this menu option was previously only available in other
* modes, then make sure it's available for this one now
*/
#ifdef USE_GUI_WIN32
/* If adding a tearbar (addtearoff == FALSE) don't update modes */
if (addtearoff)
#endif
menu->modes |= modes;
}
menup = &menu->children;
parent = menu;
name = next_name;
vim_free(dname);
if (pri_tab[pri_idx + 1] != -1)
++pri_idx;
}
vim_free(path_name);
/*
* Only add system menu items which have not been defined yet.
* First check if this was an ":amenu".
*/
amenu = ((modes & (MENU_NORMAL_MODE | MENU_INSERT_MODE)) ==
(MENU_NORMAL_MODE | MENU_INSERT_MODE));
if (gui_sys_menu)
modes &= ~old_modes;
if (menu != NULL && modes)
{
menu->cb = call_back;
p = (call_data == NULL) ? NULL : vim_strsave(call_data);
/* loop over all modes, may add more than one */
for (i = 0; i < MENU_MODES; ++i)
{
if (modes & (1 << i))
{
/* free any old menu */
gui_free_menu_string(menu, i);
/* For "amenu", may insert an extra character */
/* Don't do this if adding a tearbar (addtearoff == FALSE) */
c = 0;
if (amenu
#ifdef USE_GUI_WIN32
&& addtearoff
#endif
)
{
switch (1 << i)
{
case MENU_VISUAL_MODE:
case MENU_OP_PENDING_MODE:
case MENU_CMDLINE_MODE:
c = Ctrl('C');
break;
case MENU_INSERT_MODE:
c = Ctrl('O');
break;
}
}
if (c)
{
menu->strings[i] = alloc((unsigned)(STRLEN(call_data) + 2));
if (menu->strings[i] != NULL)
{
menu->strings[i][0] = c;
STRCPY(menu->strings[i] + 1, call_data);
}
}
else
menu->strings[i] = p;
menu->noremap[i] = noremap;
}
}
}
return OK;
}
/*
* Remove the (sub)menu with the given name from the menu hierarchy
* Called recursively.
*/
static int
gui_remove_menu(menup, name, modes, silent)
GuiMenu **menup;
char_u *name;
int modes;
int silent; /* don't give error messages */
{
GuiMenu *menu;
GuiMenu *child;
char_u *p;
if (*menup == NULL)
return OK; /* Got to bottom of hierarchy */
/* Get name of this element in the menu hierarchy */
p = gui_menu_name_skip(name);
/* Find the menu */
menu = *menup;
while (menu != NULL)
{
if (*name == NUL || menu_name_equal(name, menu))
{
if (*p != NUL && menu->children == NULL)
{
if (!silent)
EMSG("Part of menu-item path is not sub-menu");
return FAIL;
}
if ((menu->modes & modes) != 0x0)
{
if (gui_remove_menu(&menu->children, p, modes, silent) == FAIL)
return FAIL;
}
else if (*name != NUL)
{
if (!silent)
EMSG("Menu only exists in another mode");
return FAIL;
}
/*
* When name is empty, we are removing all menu items for the given
* modes, so keep looping, otherwise we are just removing the named
* menu item (which has been found) so break here.
*/
if (*name != NUL)
break;
/* Remove the menu item for the given mode[s] */
menu->modes &= ~modes;
if (modes & MENU_TIP_MODE)
gui_free_menu_string(menu, MENU_INDEX_TIP);
if ((menu->modes & MENU_ALL_MODES) == 0)
{
/* The menu item is no longer valid in ANY mode, so delete it */
*menup = menu->next;
gui_free_menu(menu);
}
else
menup = &menu->next;
}
else
menup = &menu->next;
menu = *menup;
}
if (*name != NUL)
{
if (menu == NULL)
{
if (!silent)
EMSG("No menu of that name");
return FAIL;
}
/* Recalculate modes for menu based on the new updated children */
menu->modes &= ~modes;
#ifdef USE_GUI_WIN32
if ((s_tearoffs) && (menu->children != NULL)) /* there's a tear bar.. */
child = menu->children->next; /* don't count tearoff bar */
else
#endif
child = menu->children;
for ( ; child != NULL; child = child->next)
menu->modes |= child->modes;
if (modes & MENU_TIP_MODE)
gui_free_menu_string(menu, MENU_INDEX_TIP);
if ((menu->modes & MENU_ALL_MODES) == 0)
{
/* The menu item is no longer valid in ANY mode, so delete it */
#ifdef USE_GUI_WIN32
if ((s_tearoffs) && (menu->children != NULL)) /* there's a tear bar.. */
gui_free_menu(menu->children);
#endif
*menup = menu->next;
gui_free_menu(menu);
}
}
return OK;
}
/*
* Free the given menu structure
*/
static void
gui_free_menu(menu)
GuiMenu *menu;
{
int i;
/* Free machine specific menu structures (only when already created) */
if (gui.in_use)
gui_mch_destroy_menu(menu);
vim_free(menu->name);
vim_free(menu->dname);
vim_free(menu->actext);
for (i = 0; i < MENU_MODES; i++)
gui_free_menu_string(menu, i);
vim_free(menu);
/* Want to update menus now even if mode not changed */
force_menu_update = TRUE;
}
/*
* Free the menu->string with the given index.
*/
static void
gui_free_menu_string(menu, idx)
GuiMenu *menu;
int idx;
{
int count = 0;
int i;
for (i = 0; i < MENU_MODES; i++)
if (menu->strings[i] == menu->strings[idx])
count++;
if (count == 1)
vim_free(menu->strings[idx]);
menu->strings[idx] = NULL;
}
/*
* Show the mapping associated with a menu item or hierarchy in a sub-menu.
*/
static int
gui_show_menus(path_name, modes)
char_u *path_name;
int modes;
{
char_u *p;
char_u *name;
GuiMenu *menu;
GuiMenu *parent = NULL;
menu = gui.root_menu;
name = path_name = vim_strsave(path_name);
if (path_name == NULL)
return FAIL;
/* Fir
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -