1edf3f97aSBram Moolenaar /* vi:set ts=8 sts=4 sw=4 noet:
2071d4279SBram Moolenaar *
3071d4279SBram Moolenaar * VIM - Vi IMproved by Bram Moolenaar
4071d4279SBram Moolenaar * GUI/Motif support by Robert Webb
5071d4279SBram Moolenaar *
6071d4279SBram Moolenaar * Do ":help uganda" in Vim to read copying and usage conditions.
7071d4279SBram Moolenaar * Do ":help credits" in Vim to see a list of people who contributed.
8071d4279SBram Moolenaar * See README.txt for an overview of the Vim source code.
9071d4279SBram Moolenaar */
10071d4279SBram Moolenaar
11071d4279SBram Moolenaar /*
12071d4279SBram Moolenaar * Code for menus. Used for the GUI and 'wildmenu'.
13071d4279SBram Moolenaar */
14071d4279SBram Moolenaar
15071d4279SBram Moolenaar #include "vim.h"
16071d4279SBram Moolenaar
17071d4279SBram Moolenaar #if defined(FEAT_MENU) || defined(PROTO)
18071d4279SBram Moolenaar
194ba37b58SBram Moolenaar #define MENUDEPTH 10 // maximum depth of menus
20071d4279SBram Moolenaar
214f97475dSBram Moolenaar #ifdef FEAT_GUI_MSWIN
2292b8b2d3SBram Moolenaar static int add_menu_path(char_u *, vimmenu_T *, int *, char_u *, int);
23071d4279SBram Moolenaar #else
2492b8b2d3SBram Moolenaar static int add_menu_path(char_u *, vimmenu_T *, int *, char_u *);
25071d4279SBram Moolenaar #endif
2692b8b2d3SBram Moolenaar static int menu_nable_recurse(vimmenu_T *menu, char_u *name, int modes, int enable);
2792b8b2d3SBram Moolenaar static int remove_menu(vimmenu_T **, char_u *, int, int silent);
2892b8b2d3SBram Moolenaar static void free_menu(vimmenu_T **menup);
2992b8b2d3SBram Moolenaar static void free_menu_string(vimmenu_T *, int);
3092b8b2d3SBram Moolenaar static int show_menus(char_u *, int);
3192b8b2d3SBram Moolenaar static void show_menus_recursive(vimmenu_T *, int, int);
325843f5f3SBram Moolenaar static char_u *menu_name_skip(char_u *name);
3392b8b2d3SBram Moolenaar static int menu_name_equal(char_u *name, vimmenu_T *menu);
3492b8b2d3SBram Moolenaar static int menu_namecmp(char_u *name, char_u *mname);
3592b8b2d3SBram Moolenaar static int get_menu_cmd_modes(char_u *, int, int *, int *);
3692b8b2d3SBram Moolenaar static char_u *popup_mode_name(char_u *name, int idx);
3792b8b2d3SBram Moolenaar static char_u *menu_text(char_u *text, int *mnemonic, char_u **actext);
38071d4279SBram Moolenaar
394f97475dSBram Moolenaar #if defined(FEAT_GUI_MSWIN) && defined(FEAT_TEAROFF)
4092b8b2d3SBram Moolenaar static void gui_create_tearoffs_recurse(vimmenu_T *menu, const char_u *pname, int *pri_tab, int pri_idx);
4192b8b2d3SBram Moolenaar static void gui_add_tearoff(char_u *tearpath, int *pri_tab, int pri_idx);
4292b8b2d3SBram Moolenaar static void gui_destroy_tearoffs_recurse(vimmenu_T *menu);
43071d4279SBram Moolenaar static int s_tearoffs = FALSE;
44071d4279SBram Moolenaar #endif
45071d4279SBram Moolenaar
4692b8b2d3SBram Moolenaar static int menu_is_hidden(char_u *name);
4792b8b2d3SBram Moolenaar static int menu_is_tearoff(char_u *name);
48071d4279SBram Moolenaar
49071d4279SBram Moolenaar #if defined(FEAT_MULTI_LANG) || defined(FEAT_TOOLBAR)
5092b8b2d3SBram Moolenaar static char_u *menu_skip_part(char_u *p);
51071d4279SBram Moolenaar #endif
52071d4279SBram Moolenaar #ifdef FEAT_MULTI_LANG
5392b8b2d3SBram Moolenaar static char_u *menutrans_lookup(char_u *name, int len);
5492b8b2d3SBram Moolenaar static void menu_unescape_name(char_u *p);
55071d4279SBram Moolenaar #endif
56071d4279SBram Moolenaar
5792b8b2d3SBram Moolenaar static char_u *menu_translate_tab_and_shift(char_u *arg_start);
5870b11cddSBram Moolenaar
594ba37b58SBram Moolenaar // The character for each menu mode
604c5d8152SBram Moolenaar static char *menu_mode_chars[] = {"n", "v", "s", "o", "i", "c", "tl", "t"};
61071d4279SBram Moolenaar
62071d4279SBram Moolenaar static char_u e_notsubmenu[] = N_("E327: Part of menu-item path is not sub-menu");
63342337a1SBram Moolenaar static char_u e_nomenu[] = N_("E329: No menu \"%s\"");
64071d4279SBram Moolenaar
65071d4279SBram Moolenaar #ifdef FEAT_TOOLBAR
66071d4279SBram Moolenaar static const char *toolbar_names[] =
67071d4279SBram Moolenaar {
68071d4279SBram Moolenaar /* 0 */ "New", "Open", "Save", "Undo", "Redo",
69071d4279SBram Moolenaar /* 5 */ "Cut", "Copy", "Paste", "Print", "Help",
70071d4279SBram Moolenaar /* 10 */ "Find", "SaveAll", "SaveSesn", "NewSesn", "LoadSesn",
71071d4279SBram Moolenaar /* 15 */ "RunScript", "Replace", "WinClose", "WinMax", "WinMin",
72071d4279SBram Moolenaar /* 20 */ "WinSplit", "Shell", "FindPrev", "FindNext", "FindHelp",
73071d4279SBram Moolenaar /* 25 */ "Make", "TagJump", "RunCtags", "WinVSplit", "WinMaxWidth",
74071d4279SBram Moolenaar /* 30 */ "WinMinWidth", "Exit"
75071d4279SBram Moolenaar };
76eeec2548SK.Takata # define TOOLBAR_NAME_COUNT ARRAY_LENGTH(toolbar_names)
77071d4279SBram Moolenaar #endif
78071d4279SBram Moolenaar
79071d4279SBram Moolenaar /*
801b9645deSBram Moolenaar * Return TRUE if "name" is a window toolbar menu name.
811b9645deSBram Moolenaar */
821b9645deSBram Moolenaar static int
menu_is_winbar(char_u * name)831b9645deSBram Moolenaar menu_is_winbar(char_u *name)
841b9645deSBram Moolenaar {
85378daf87SBram Moolenaar return (STRNCMP(name, "WinBar", 6) == 0);
861b9645deSBram Moolenaar }
871b9645deSBram Moolenaar
881b9645deSBram Moolenaar int
winbar_height(win_T * wp)891b9645deSBram Moolenaar winbar_height(win_T *wp)
901b9645deSBram Moolenaar {
911b9645deSBram Moolenaar if (wp->w_winbar != NULL && wp->w_winbar->children != NULL)
921b9645deSBram Moolenaar return 1;
931b9645deSBram Moolenaar return 0;
941b9645deSBram Moolenaar }
951b9645deSBram Moolenaar
961b9645deSBram Moolenaar static vimmenu_T **
get_root_menu(char_u * name)971b9645deSBram Moolenaar get_root_menu(char_u *name)
981b9645deSBram Moolenaar {
991b9645deSBram Moolenaar if (menu_is_winbar(name))
1001b9645deSBram Moolenaar return &curwin->w_winbar;
1011b9645deSBram Moolenaar return &root_menu;
1021b9645deSBram Moolenaar }
1031b9645deSBram Moolenaar
1041b9645deSBram Moolenaar /*
105071d4279SBram Moolenaar * Do the :menu command and relatives.
106071d4279SBram Moolenaar */
107071d4279SBram Moolenaar void
ex_menu(exarg_T * eap)10852ea13daSBram Moolenaar ex_menu(
1094ba37b58SBram Moolenaar exarg_T *eap) // Ex command arguments
110071d4279SBram Moolenaar {
111071d4279SBram Moolenaar char_u *menu_path;
112071d4279SBram Moolenaar int modes;
113071d4279SBram Moolenaar char_u *map_to;
114071d4279SBram Moolenaar int noremap;
115071d4279SBram Moolenaar int silent = FALSE;
1168b2d9c43SBram Moolenaar int special = FALSE;
117071d4279SBram Moolenaar int unmenu;
118071d4279SBram Moolenaar char_u *map_buf;
119071d4279SBram Moolenaar char_u *arg;
120071d4279SBram Moolenaar char_u *p;
121071d4279SBram Moolenaar int i;
122241a8aaaSBram Moolenaar #if defined(FEAT_GUI) && !defined(FEAT_GUI_GTK)
123071d4279SBram Moolenaar int old_menu_height;
1244f97475dSBram Moolenaar # if defined(FEAT_TOOLBAR) && !defined(FEAT_GUI_MSWIN)
125071d4279SBram Moolenaar int old_toolbar_height;
126071d4279SBram Moolenaar # endif
127071d4279SBram Moolenaar #endif
128071d4279SBram Moolenaar int pri_tab[MENUDEPTH + 1];
1294ba37b58SBram Moolenaar int enable = MAYBE; // TRUE for "menu enable", FALSE for "menu
1304ba37b58SBram Moolenaar // disable
131071d4279SBram Moolenaar #ifdef FEAT_TOOLBAR
132071d4279SBram Moolenaar char_u *icon = NULL;
133071d4279SBram Moolenaar #endif
134071d4279SBram Moolenaar vimmenu_T menuarg;
1351b9645deSBram Moolenaar vimmenu_T **root_menu_ptr;
136071d4279SBram Moolenaar
137071d4279SBram Moolenaar modes = get_menu_cmd_modes(eap->cmd, eap->forceit, &noremap, &unmenu);
138071d4279SBram Moolenaar arg = eap->arg;
139071d4279SBram Moolenaar
140071d4279SBram Moolenaar for (;;)
141071d4279SBram Moolenaar {
142071d4279SBram Moolenaar if (STRNCMP(arg, "<script>", 8) == 0)
143071d4279SBram Moolenaar {
144071d4279SBram Moolenaar noremap = REMAP_SCRIPT;
145071d4279SBram Moolenaar arg = skipwhite(arg + 8);
146071d4279SBram Moolenaar continue;
147071d4279SBram Moolenaar }
148071d4279SBram Moolenaar if (STRNCMP(arg, "<silent>", 8) == 0)
149071d4279SBram Moolenaar {
150071d4279SBram Moolenaar silent = TRUE;
151071d4279SBram Moolenaar arg = skipwhite(arg + 8);
152071d4279SBram Moolenaar continue;
153071d4279SBram Moolenaar }
1548b2d9c43SBram Moolenaar if (STRNCMP(arg, "<special>", 9) == 0)
1558b2d9c43SBram Moolenaar {
1568b2d9c43SBram Moolenaar special = TRUE;
1578b2d9c43SBram Moolenaar arg = skipwhite(arg + 9);
1588b2d9c43SBram Moolenaar continue;
1598b2d9c43SBram Moolenaar }
160071d4279SBram Moolenaar break;
161071d4279SBram Moolenaar }
162071d4279SBram Moolenaar
163071d4279SBram Moolenaar
1644ba37b58SBram Moolenaar // Locate an optional "icon=filename" argument.
165071d4279SBram Moolenaar if (STRNCMP(arg, "icon=", 5) == 0)
166071d4279SBram Moolenaar {
167071d4279SBram Moolenaar arg += 5;
168071d4279SBram Moolenaar #ifdef FEAT_TOOLBAR
169071d4279SBram Moolenaar icon = arg;
170071d4279SBram Moolenaar #endif
171071d4279SBram Moolenaar while (*arg != NUL && *arg != ' ')
172071d4279SBram Moolenaar {
173071d4279SBram Moolenaar if (*arg == '\\')
1748c8de839SBram Moolenaar STRMOVE(arg, arg + 1);
17591acfffcSBram Moolenaar MB_PTR_ADV(arg);
176071d4279SBram Moolenaar }
177071d4279SBram Moolenaar if (*arg != NUL)
178071d4279SBram Moolenaar {
179071d4279SBram Moolenaar *arg++ = NUL;
180071d4279SBram Moolenaar arg = skipwhite(arg);
181071d4279SBram Moolenaar }
182071d4279SBram Moolenaar }
183071d4279SBram Moolenaar
184071d4279SBram Moolenaar /*
185071d4279SBram Moolenaar * Fill in the priority table.
186071d4279SBram Moolenaar */
187071d4279SBram Moolenaar for (p = arg; *p; ++p)
188071d4279SBram Moolenaar if (!VIM_ISDIGIT(*p) && *p != '.')
189071d4279SBram Moolenaar break;
1901c465444SBram Moolenaar if (VIM_ISWHITE(*p))
191071d4279SBram Moolenaar {
1921c465444SBram Moolenaar for (i = 0; i < MENUDEPTH && !VIM_ISWHITE(*arg); ++i)
193071d4279SBram Moolenaar {
194071d4279SBram Moolenaar pri_tab[i] = getdigits(&arg);
195071d4279SBram Moolenaar if (pri_tab[i] == 0)
196071d4279SBram Moolenaar pri_tab[i] = 500;
197071d4279SBram Moolenaar if (*arg == '.')
198071d4279SBram Moolenaar ++arg;
199071d4279SBram Moolenaar }
200071d4279SBram Moolenaar arg = skipwhite(arg);
201071d4279SBram Moolenaar }
202071d4279SBram Moolenaar else if (eap->addr_count && eap->line2 != 0)
203071d4279SBram Moolenaar {
204071d4279SBram Moolenaar pri_tab[0] = eap->line2;
205071d4279SBram Moolenaar i = 1;
206071d4279SBram Moolenaar }
207071d4279SBram Moolenaar else
208071d4279SBram Moolenaar i = 0;
209071d4279SBram Moolenaar while (i < MENUDEPTH)
210071d4279SBram Moolenaar pri_tab[i++] = 500;
2114ba37b58SBram Moolenaar pri_tab[MENUDEPTH] = -1; // mark end of the table
212071d4279SBram Moolenaar
213071d4279SBram Moolenaar /*
214071d4279SBram Moolenaar * Check for "disable" or "enable" argument.
215071d4279SBram Moolenaar */
2161c465444SBram Moolenaar if (STRNCMP(arg, "enable", 6) == 0 && VIM_ISWHITE(arg[6]))
217071d4279SBram Moolenaar {
218071d4279SBram Moolenaar enable = TRUE;
219071d4279SBram Moolenaar arg = skipwhite(arg + 6);
220071d4279SBram Moolenaar }
2211c465444SBram Moolenaar else if (STRNCMP(arg, "disable", 7) == 0 && VIM_ISWHITE(arg[7]))
222071d4279SBram Moolenaar {
223071d4279SBram Moolenaar enable = FALSE;
224071d4279SBram Moolenaar arg = skipwhite(arg + 7);
225071d4279SBram Moolenaar }
226071d4279SBram Moolenaar
227071d4279SBram Moolenaar /*
228071d4279SBram Moolenaar * If there is no argument, display all menus.
229071d4279SBram Moolenaar */
230071d4279SBram Moolenaar if (*arg == NUL)
231071d4279SBram Moolenaar {
232071d4279SBram Moolenaar show_menus(arg, modes);
233071d4279SBram Moolenaar return;
234071d4279SBram Moolenaar }
235071d4279SBram Moolenaar
236071d4279SBram Moolenaar #ifdef FEAT_TOOLBAR
237071d4279SBram Moolenaar /*
238071d4279SBram Moolenaar * Need to get the toolbar icon index before doing the translation.
239071d4279SBram Moolenaar */
240071d4279SBram Moolenaar menuarg.iconidx = -1;
241071d4279SBram Moolenaar menuarg.icon_builtin = FALSE;
242071d4279SBram Moolenaar if (menu_is_toolbar(arg))
243071d4279SBram Moolenaar {
244071d4279SBram Moolenaar menu_path = menu_skip_part(arg);
245071d4279SBram Moolenaar if (*menu_path == '.')
246071d4279SBram Moolenaar {
247071d4279SBram Moolenaar p = menu_skip_part(++menu_path);
248071d4279SBram Moolenaar if (STRNCMP(menu_path, "BuiltIn", 7) == 0)
249071d4279SBram Moolenaar {
250071d4279SBram Moolenaar if (skipdigits(menu_path + 7) == p)
251071d4279SBram Moolenaar {
252071d4279SBram Moolenaar menuarg.iconidx = atoi((char *)menu_path + 7);
253af0167faSBram Moolenaar if (menuarg.iconidx >= (int)TOOLBAR_NAME_COUNT)
254071d4279SBram Moolenaar menuarg.iconidx = -1;
255071d4279SBram Moolenaar else
256071d4279SBram Moolenaar menuarg.icon_builtin = TRUE;
257071d4279SBram Moolenaar }
258071d4279SBram Moolenaar }
259071d4279SBram Moolenaar else
260071d4279SBram Moolenaar {
261af0167faSBram Moolenaar for (i = 0; i < (int)TOOLBAR_NAME_COUNT; ++i)
262071d4279SBram Moolenaar if (STRNCMP(toolbar_names[i], menu_path, p - menu_path)
263071d4279SBram Moolenaar == 0)
264071d4279SBram Moolenaar {
265071d4279SBram Moolenaar menuarg.iconidx = i;
266071d4279SBram Moolenaar break;
267071d4279SBram Moolenaar }
268071d4279SBram Moolenaar }
269071d4279SBram Moolenaar }
270071d4279SBram Moolenaar }
271071d4279SBram Moolenaar #endif
272071d4279SBram Moolenaar
273071d4279SBram Moolenaar menu_path = arg;
274071d4279SBram Moolenaar if (*menu_path == '.')
275071d4279SBram Moolenaar {
276f9e3e09fSBram Moolenaar semsg(_(e_invarg2), menu_path);
277071d4279SBram Moolenaar goto theend;
278071d4279SBram Moolenaar }
279071d4279SBram Moolenaar
28070b11cddSBram Moolenaar map_to = menu_translate_tab_and_shift(arg);
281071d4279SBram Moolenaar
282071d4279SBram Moolenaar /*
283071d4279SBram Moolenaar * If there is only a menu name, display menus with that name.
284071d4279SBram Moolenaar */
285071d4279SBram Moolenaar if (*map_to == NUL && !unmenu && enable == MAYBE)
286071d4279SBram Moolenaar {
287071d4279SBram Moolenaar show_menus(menu_path, modes);
288071d4279SBram Moolenaar goto theend;
289071d4279SBram Moolenaar }
290071d4279SBram Moolenaar else if (*map_to != NUL && (unmenu || enable != MAYBE))
291071d4279SBram Moolenaar {
2922d06bfdeSBram Moolenaar semsg(_(e_trailing_arg), map_to);
293071d4279SBram Moolenaar goto theend;
294071d4279SBram Moolenaar }
295241a8aaaSBram Moolenaar #if defined(FEAT_GUI) && !(defined(FEAT_GUI_GTK) || defined(FEAT_GUI_PHOTON))
296071d4279SBram Moolenaar old_menu_height = gui.menu_height;
2974f97475dSBram Moolenaar # if defined(FEAT_TOOLBAR) && !defined(FEAT_GUI_MSWIN)
298071d4279SBram Moolenaar old_toolbar_height = gui.toolbar_height;
299071d4279SBram Moolenaar # endif
300071d4279SBram Moolenaar #endif
301071d4279SBram Moolenaar
3021b9645deSBram Moolenaar root_menu_ptr = get_root_menu(menu_path);
3031b9645deSBram Moolenaar if (root_menu_ptr == &curwin->w_winbar)
3044ba37b58SBram Moolenaar // Assume the window toolbar menu will change.
3051b9645deSBram Moolenaar redraw_later(NOT_VALID);
3061b9645deSBram Moolenaar
307071d4279SBram Moolenaar if (enable != MAYBE)
308071d4279SBram Moolenaar {
309071d4279SBram Moolenaar /*
310071d4279SBram Moolenaar * Change sensitivity of the menu.
311071d4279SBram Moolenaar * For the PopUp menu, remove a menu for each mode separately.
312071d4279SBram Moolenaar * Careful: menu_nable_recurse() changes menu_path.
313071d4279SBram Moolenaar */
3144ba37b58SBram Moolenaar if (STRCMP(menu_path, "*") == 0) // meaning: do all menus
315071d4279SBram Moolenaar menu_path = (char_u *)"";
316071d4279SBram Moolenaar
317071d4279SBram Moolenaar if (menu_is_popup(menu_path))
318071d4279SBram Moolenaar {
319071d4279SBram Moolenaar for (i = 0; i < MENU_INDEX_TIP; ++i)
320071d4279SBram Moolenaar if (modes & (1 << i))
321071d4279SBram Moolenaar {
322071d4279SBram Moolenaar p = popup_mode_name(menu_path, i);
323071d4279SBram Moolenaar if (p != NULL)
324071d4279SBram Moolenaar {
3251b9645deSBram Moolenaar menu_nable_recurse(*root_menu_ptr, p, MENU_ALL_MODES,
326071d4279SBram Moolenaar enable);
327071d4279SBram Moolenaar vim_free(p);
328071d4279SBram Moolenaar }
329071d4279SBram Moolenaar }
330071d4279SBram Moolenaar }
3311b9645deSBram Moolenaar menu_nable_recurse(*root_menu_ptr, menu_path, modes, enable);
332071d4279SBram Moolenaar }
333071d4279SBram Moolenaar else if (unmenu)
334071d4279SBram Moolenaar {
335071d4279SBram Moolenaar /*
336071d4279SBram Moolenaar * Delete menu(s).
337071d4279SBram Moolenaar */
3384ba37b58SBram Moolenaar if (STRCMP(menu_path, "*") == 0) // meaning: remove all menus
339071d4279SBram Moolenaar menu_path = (char_u *)"";
340071d4279SBram Moolenaar
341071d4279SBram Moolenaar /*
342071d4279SBram Moolenaar * For the PopUp menu, remove a menu for each mode separately.
343071d4279SBram Moolenaar */
344071d4279SBram Moolenaar if (menu_is_popup(menu_path))
345071d4279SBram Moolenaar {
346071d4279SBram Moolenaar for (i = 0; i < MENU_INDEX_TIP; ++i)
347071d4279SBram Moolenaar if (modes & (1 << i))
348071d4279SBram Moolenaar {
349071d4279SBram Moolenaar p = popup_mode_name(menu_path, i);
350071d4279SBram Moolenaar if (p != NULL)
351071d4279SBram Moolenaar {
3521b9645deSBram Moolenaar remove_menu(root_menu_ptr, p, MENU_ALL_MODES, TRUE);
353071d4279SBram Moolenaar vim_free(p);
354071d4279SBram Moolenaar }
355071d4279SBram Moolenaar }
356071d4279SBram Moolenaar }
357071d4279SBram Moolenaar
3584ba37b58SBram Moolenaar // Careful: remove_menu() changes menu_path
3591b9645deSBram Moolenaar remove_menu(root_menu_ptr, menu_path, modes, FALSE);
360071d4279SBram Moolenaar }
361071d4279SBram Moolenaar else
362071d4279SBram Moolenaar {
363071d4279SBram Moolenaar /*
364071d4279SBram Moolenaar * Add menu(s).
365071d4279SBram Moolenaar * Replace special key codes.
366071d4279SBram Moolenaar */
3674ba37b58SBram Moolenaar if (STRICMP(map_to, "<nop>") == 0) // "<Nop>" means nothing
368071d4279SBram Moolenaar {
369071d4279SBram Moolenaar map_to = (char_u *)"";
370071d4279SBram Moolenaar map_buf = NULL;
371071d4279SBram Moolenaar }
3723fdfa4a9SBram Moolenaar else if (modes & MENU_TIP_MODE)
3734ba37b58SBram Moolenaar map_buf = NULL; // Menu tips are plain text.
374071d4279SBram Moolenaar else
375459fd785SBram Moolenaar map_to = replace_termcodes(map_to, &map_buf,
376459fd785SBram Moolenaar REPTERM_DO_LT | (special ? REPTERM_SPECIAL : 0), NULL);
377071d4279SBram Moolenaar menuarg.modes = modes;
378071d4279SBram Moolenaar #ifdef FEAT_TOOLBAR
379071d4279SBram Moolenaar menuarg.iconfile = icon;
380071d4279SBram Moolenaar #endif
381071d4279SBram Moolenaar menuarg.noremap[0] = noremap;
382071d4279SBram Moolenaar menuarg.silent[0] = silent;
383071d4279SBram Moolenaar add_menu_path(menu_path, &menuarg, pri_tab, map_to
3844f97475dSBram Moolenaar #ifdef FEAT_GUI_MSWIN
385071d4279SBram Moolenaar , TRUE
386071d4279SBram Moolenaar #endif
387071d4279SBram Moolenaar );
388071d4279SBram Moolenaar
389071d4279SBram Moolenaar /*
390071d4279SBram Moolenaar * For the PopUp menu, add a menu for each mode separately.
391071d4279SBram Moolenaar */
392071d4279SBram Moolenaar if (menu_is_popup(menu_path))
393071d4279SBram Moolenaar {
394071d4279SBram Moolenaar for (i = 0; i < MENU_INDEX_TIP; ++i)
395071d4279SBram Moolenaar if (modes & (1 << i))
396071d4279SBram Moolenaar {
397071d4279SBram Moolenaar p = popup_mode_name(menu_path, i);
398071d4279SBram Moolenaar if (p != NULL)
399071d4279SBram Moolenaar {
4004ba37b58SBram Moolenaar // Include all modes, to make ":amenu" work
401071d4279SBram Moolenaar menuarg.modes = modes;
402071d4279SBram Moolenaar #ifdef FEAT_TOOLBAR
403071d4279SBram Moolenaar menuarg.iconfile = NULL;
404071d4279SBram Moolenaar menuarg.iconidx = -1;
405071d4279SBram Moolenaar menuarg.icon_builtin = FALSE;
406071d4279SBram Moolenaar #endif
407071d4279SBram Moolenaar add_menu_path(p, &menuarg, pri_tab, map_to
4084f97475dSBram Moolenaar #ifdef FEAT_GUI_MSWIN
409071d4279SBram Moolenaar , TRUE
410071d4279SBram Moolenaar #endif
411071d4279SBram Moolenaar );
412071d4279SBram Moolenaar vim_free(p);
413071d4279SBram Moolenaar }
414071d4279SBram Moolenaar }
415071d4279SBram Moolenaar }
416071d4279SBram Moolenaar
417071d4279SBram Moolenaar vim_free(map_buf);
418071d4279SBram Moolenaar }
419071d4279SBram Moolenaar
420241a8aaaSBram Moolenaar #if defined(FEAT_GUI) && !(defined(FEAT_GUI_GTK))
4214ba37b58SBram Moolenaar // If the menubar height changed, resize the window
422071d4279SBram Moolenaar if (gui.in_use
423071d4279SBram Moolenaar && (gui.menu_height != old_menu_height
4244f97475dSBram Moolenaar # if defined(FEAT_TOOLBAR) && !defined(FEAT_GUI_MSWIN)
425071d4279SBram Moolenaar || gui.toolbar_height != old_toolbar_height
426071d4279SBram Moolenaar # endif
427071d4279SBram Moolenaar ))
42804a9d455SBram Moolenaar gui_set_shellsize(FALSE, FALSE, RESIZE_VERT);
429071d4279SBram Moolenaar #endif
4301b9645deSBram Moolenaar if (root_menu_ptr == &curwin->w_winbar)
4311b9645deSBram Moolenaar {
4321b9645deSBram Moolenaar int h = winbar_height(curwin);
4331b9645deSBram Moolenaar
4341b9645deSBram Moolenaar if (h != curwin->w_winbar_height)
4351b9645deSBram Moolenaar {
4361b9645deSBram Moolenaar if (h == 0)
4371b9645deSBram Moolenaar ++curwin->w_height;
4381b9645deSBram Moolenaar else if (curwin->w_height > 0)
4391b9645deSBram Moolenaar --curwin->w_height;
4401b9645deSBram Moolenaar curwin->w_winbar_height = h;
4411b9645deSBram Moolenaar }
4421b9645deSBram Moolenaar }
443071d4279SBram Moolenaar
444071d4279SBram Moolenaar theend:
445071d4279SBram Moolenaar ;
446071d4279SBram Moolenaar }
447071d4279SBram Moolenaar
448071d4279SBram Moolenaar /*
449071d4279SBram Moolenaar * Add the menu with the given name to the menu hierarchy
450071d4279SBram Moolenaar */
451071d4279SBram Moolenaar static int
add_menu_path(char_u * menu_path,vimmenu_T * menuarg,int * pri_tab,char_u * call_data,int addtearoff)45252ea13daSBram Moolenaar add_menu_path(
45352ea13daSBram Moolenaar char_u *menu_path,
4544ba37b58SBram Moolenaar vimmenu_T *menuarg, // passes modes, iconfile, iconidx,
4554ba37b58SBram Moolenaar // icon_builtin, silent[0], noremap[0]
45652ea13daSBram Moolenaar int *pri_tab,
45752ea13daSBram Moolenaar char_u *call_data
4584f97475dSBram Moolenaar #ifdef FEAT_GUI_MSWIN
4594ba37b58SBram Moolenaar , int addtearoff // may add tearoff item
460071d4279SBram Moolenaar #endif
461071d4279SBram Moolenaar )
462071d4279SBram Moolenaar {
463071d4279SBram Moolenaar char_u *path_name;
464071d4279SBram Moolenaar int modes = menuarg->modes;
465071d4279SBram Moolenaar vimmenu_T **menup;
466071d4279SBram Moolenaar vimmenu_T *menu = NULL;
467071d4279SBram Moolenaar vimmenu_T *parent;
468071d4279SBram Moolenaar vimmenu_T **lower_pri;
469071d4279SBram Moolenaar char_u *p;
470071d4279SBram Moolenaar char_u *name;
471071d4279SBram Moolenaar char_u *dname;
472071d4279SBram Moolenaar char_u *next_name;
473071d4279SBram Moolenaar int i;
474071d4279SBram Moolenaar int c;
4757871a500SBram Moolenaar int d;
476071d4279SBram Moolenaar #ifdef FEAT_GUI
477071d4279SBram Moolenaar int idx;
478071d4279SBram Moolenaar int new_idx;
479071d4279SBram Moolenaar #endif
480071d4279SBram Moolenaar int pri_idx = 0;
481071d4279SBram Moolenaar int old_modes = 0;
482071d4279SBram Moolenaar int amenu;
48370b11cddSBram Moolenaar #ifdef FEAT_MULTI_LANG
48470b11cddSBram Moolenaar char_u *en_name;
48570b11cddSBram Moolenaar char_u *map_to = NULL;
48670b11cddSBram Moolenaar #endif
4871b9645deSBram Moolenaar vimmenu_T **root_menu_ptr;
488071d4279SBram Moolenaar
4894ba37b58SBram Moolenaar // Make a copy so we can stuff around with it, since it could be const
490071d4279SBram Moolenaar path_name = vim_strsave(menu_path);
491071d4279SBram Moolenaar if (path_name == NULL)
492071d4279SBram Moolenaar return FAIL;
4931b9645deSBram Moolenaar root_menu_ptr = get_root_menu(menu_path);
4941b9645deSBram Moolenaar menup = root_menu_ptr;
495071d4279SBram Moolenaar parent = NULL;
496071d4279SBram Moolenaar name = path_name;
497071d4279SBram Moolenaar while (*name)
498071d4279SBram Moolenaar {
4994ba37b58SBram Moolenaar // Get name of this element in the menu hierarchy, and the simplified
5004ba37b58SBram Moolenaar // name (without mnemonic and accelerator text).
501071d4279SBram Moolenaar next_name = menu_name_skip(name);
50270b11cddSBram Moolenaar #ifdef FEAT_MULTI_LANG
503442b4225SBram Moolenaar map_to = menutrans_lookup(name, (int)STRLEN(name));
50470b11cddSBram Moolenaar if (map_to != NULL)
50570b11cddSBram Moolenaar {
50670b11cddSBram Moolenaar en_name = name;
50770b11cddSBram Moolenaar name = map_to;
50870b11cddSBram Moolenaar }
50970b11cddSBram Moolenaar else
51070b11cddSBram Moolenaar en_name = NULL;
51170b11cddSBram Moolenaar #endif
512071d4279SBram Moolenaar dname = menu_text(name, NULL, NULL);
51318a0b128SBram Moolenaar if (dname == NULL)
51418a0b128SBram Moolenaar goto erret;
51518a0b128SBram Moolenaar if (*dname == NUL)
51618a0b128SBram Moolenaar {
5174ba37b58SBram Moolenaar // Only a mnemonic or accelerator is not valid.
518f9e3e09fSBram Moolenaar emsg(_("E792: Empty menu name"));
51918a0b128SBram Moolenaar goto erret;
52018a0b128SBram Moolenaar }
521071d4279SBram Moolenaar
5224ba37b58SBram Moolenaar // See if it's already there
523071d4279SBram Moolenaar lower_pri = menup;
524071d4279SBram Moolenaar #ifdef FEAT_GUI
525071d4279SBram Moolenaar idx = 0;
526071d4279SBram Moolenaar new_idx = 0;
527071d4279SBram Moolenaar #endif
528071d4279SBram Moolenaar menu = *menup;
529071d4279SBram Moolenaar while (menu != NULL)
530071d4279SBram Moolenaar {
531071d4279SBram Moolenaar if (menu_name_equal(name, menu) || menu_name_equal(dname, menu))
532071d4279SBram Moolenaar {
533071d4279SBram Moolenaar if (*next_name == NUL && menu->children != NULL)
534071d4279SBram Moolenaar {
535071d4279SBram Moolenaar if (!sys_menu)
536f9e3e09fSBram Moolenaar emsg(_("E330: Menu path must not lead to a sub-menu"));
537071d4279SBram Moolenaar goto erret;
538071d4279SBram Moolenaar }
539071d4279SBram Moolenaar if (*next_name != NUL && menu->children == NULL
5404f97475dSBram Moolenaar #ifdef FEAT_GUI_MSWIN
541071d4279SBram Moolenaar && addtearoff
542071d4279SBram Moolenaar #endif
543071d4279SBram Moolenaar )
544071d4279SBram Moolenaar {
545071d4279SBram Moolenaar if (!sys_menu)
546f9e3e09fSBram Moolenaar emsg(_(e_notsubmenu));
547071d4279SBram Moolenaar goto erret;
548071d4279SBram Moolenaar }
549071d4279SBram Moolenaar break;
550071d4279SBram Moolenaar }
551071d4279SBram Moolenaar menup = &menu->next;
552071d4279SBram Moolenaar
5534ba37b58SBram Moolenaar // Count menus, to find where this one needs to be inserted.
5544ba37b58SBram Moolenaar // Ignore menus that are not in the menubar (PopUp and Toolbar)
555071d4279SBram Moolenaar if (parent != NULL || menu_is_menubar(menu->name))
556071d4279SBram Moolenaar {
557071d4279SBram Moolenaar #ifdef FEAT_GUI
558071d4279SBram Moolenaar ++idx;
559071d4279SBram Moolenaar #endif
560071d4279SBram Moolenaar if (menu->priority <= pri_tab[pri_idx])
561071d4279SBram Moolenaar {
562071d4279SBram Moolenaar lower_pri = menup;
563071d4279SBram Moolenaar #ifdef FEAT_GUI
564071d4279SBram Moolenaar new_idx = idx;
565071d4279SBram Moolenaar #endif
566071d4279SBram Moolenaar }
567071d4279SBram Moolenaar }
568071d4279SBram Moolenaar menu = menu->next;
569071d4279SBram Moolenaar }
570071d4279SBram Moolenaar
571071d4279SBram Moolenaar if (menu == NULL)
572071d4279SBram Moolenaar {
573071d4279SBram Moolenaar if (*next_name == NUL && parent == NULL)
574071d4279SBram Moolenaar {
575f9e3e09fSBram Moolenaar emsg(_("E331: Must not add menu items directly to menu bar"));
576071d4279SBram Moolenaar goto erret;
577071d4279SBram Moolenaar }
578071d4279SBram Moolenaar
579071d4279SBram Moolenaar if (menu_is_separator(dname) && *next_name != NUL)
580071d4279SBram Moolenaar {
581f9e3e09fSBram Moolenaar emsg(_("E332: Separator cannot be part of a menu path"));
582071d4279SBram Moolenaar goto erret;
583071d4279SBram Moolenaar }
584071d4279SBram Moolenaar
5854ba37b58SBram Moolenaar // Not already there, so lets add it
586e809a4edSBram Moolenaar menu = ALLOC_CLEAR_ONE(vimmenu_T);
587071d4279SBram Moolenaar if (menu == NULL)
588071d4279SBram Moolenaar goto erret;
589071d4279SBram Moolenaar
590071d4279SBram Moolenaar menu->modes = modes;
591071d4279SBram Moolenaar menu->enabled = MENU_ALL_MODES;
592071d4279SBram Moolenaar menu->name = vim_strsave(name);
5934ba37b58SBram Moolenaar // separate mnemonic and accelerator text from actual menu name
594071d4279SBram Moolenaar menu->dname = menu_text(name, &menu->mnemonic, &menu->actext);
59570b11cddSBram Moolenaar #ifdef FEAT_MULTI_LANG
59670b11cddSBram Moolenaar if (en_name != NULL)
59770b11cddSBram Moolenaar {
59870b11cddSBram Moolenaar menu->en_name = vim_strsave(en_name);
59970b11cddSBram Moolenaar menu->en_dname = menu_text(en_name, NULL, NULL);
60070b11cddSBram Moolenaar }
60170b11cddSBram Moolenaar else
60270b11cddSBram Moolenaar {
60370b11cddSBram Moolenaar menu->en_name = NULL;
60470b11cddSBram Moolenaar menu->en_dname = NULL;
60570b11cddSBram Moolenaar }
60670b11cddSBram Moolenaar #endif
607071d4279SBram Moolenaar menu->priority = pri_tab[pri_idx];
608071d4279SBram Moolenaar menu->parent = parent;
609071d4279SBram Moolenaar #ifdef FEAT_GUI_MOTIF
6104ba37b58SBram Moolenaar menu->sensitive = TRUE; // the default
611071d4279SBram Moolenaar #endif
612071d4279SBram Moolenaar #ifdef FEAT_BEVAL_TIP
613071d4279SBram Moolenaar menu->tip = NULL;
614071d4279SBram Moolenaar #endif
615071d4279SBram Moolenaar #ifdef FEAT_GUI_ATHENA
6164ba37b58SBram Moolenaar menu->image = None; // X-Windows definition for NULL
617071d4279SBram Moolenaar #endif
618071d4279SBram Moolenaar
619071d4279SBram Moolenaar /*
620071d4279SBram Moolenaar * Add after menu that has lower priority.
621071d4279SBram Moolenaar */
622071d4279SBram Moolenaar menu->next = *lower_pri;
623071d4279SBram Moolenaar *lower_pri = menu;
624071d4279SBram Moolenaar
625071d4279SBram Moolenaar old_modes = 0;
626071d4279SBram Moolenaar
627071d4279SBram Moolenaar #ifdef FEAT_TOOLBAR
628071d4279SBram Moolenaar menu->iconidx = menuarg->iconidx;
629071d4279SBram Moolenaar menu->icon_builtin = menuarg->icon_builtin;
630071d4279SBram Moolenaar if (*next_name == NUL && menuarg->iconfile != NULL)
631071d4279SBram Moolenaar menu->iconfile = vim_strsave(menuarg->iconfile);
632071d4279SBram Moolenaar #endif
6334f97475dSBram Moolenaar #if defined(FEAT_GUI_MSWIN) && defined(FEAT_TEAROFF)
6344ba37b58SBram Moolenaar // the tearoff item must be present in the modes of each item.
635071d4279SBram Moolenaar if (parent != NULL && menu_is_tearoff(parent->children->dname))
636071d4279SBram Moolenaar parent->children->modes |= modes;
637071d4279SBram Moolenaar #endif
638071d4279SBram Moolenaar }
639071d4279SBram Moolenaar else
640071d4279SBram Moolenaar {
641071d4279SBram Moolenaar old_modes = menu->modes;
642071d4279SBram Moolenaar
643071d4279SBram Moolenaar /*
644071d4279SBram Moolenaar * If this menu option was previously only available in other
645071d4279SBram Moolenaar * modes, then make sure it's available for this one now
646071d4279SBram Moolenaar * Also enable a menu when it's created or changed.
647071d4279SBram Moolenaar */
6484f97475dSBram Moolenaar #ifdef FEAT_GUI_MSWIN
6494ba37b58SBram Moolenaar // If adding a tearbar (addtearoff == FALSE) don't update modes
650071d4279SBram Moolenaar if (addtearoff)
651071d4279SBram Moolenaar #endif
652071d4279SBram Moolenaar {
653071d4279SBram Moolenaar menu->modes |= modes;
654071d4279SBram Moolenaar menu->enabled |= modes;
655071d4279SBram Moolenaar }
656071d4279SBram Moolenaar }
657071d4279SBram Moolenaar
658071d4279SBram Moolenaar #ifdef FEAT_GUI
659071d4279SBram Moolenaar /*
660071d4279SBram Moolenaar * Add the menu item when it's used in one of the modes, but not when
661071d4279SBram Moolenaar * only a tooltip is defined.
662071d4279SBram Moolenaar */
663071d4279SBram Moolenaar if ((old_modes & MENU_ALL_MODES) == 0
664071d4279SBram Moolenaar && (menu->modes & MENU_ALL_MODES) != 0)
665071d4279SBram Moolenaar {
6664ba37b58SBram Moolenaar if (gui.in_use) // Otherwise it will be added when GUI starts
667071d4279SBram Moolenaar {
668071d4279SBram Moolenaar if (*next_name == NUL)
669071d4279SBram Moolenaar {
6704ba37b58SBram Moolenaar // Real menu item, not sub-menu
671071d4279SBram Moolenaar gui_mch_add_menu_item(menu, new_idx);
672071d4279SBram Moolenaar
6734ba37b58SBram Moolenaar // Want to update menus now even if mode not changed
674071d4279SBram Moolenaar force_menu_update = TRUE;
675071d4279SBram Moolenaar }
676071d4279SBram Moolenaar else
677071d4279SBram Moolenaar {
6784ba37b58SBram Moolenaar // Sub-menu (not at end of path yet)
679071d4279SBram Moolenaar gui_mch_add_menu(menu, new_idx);
680071d4279SBram Moolenaar }
681071d4279SBram Moolenaar }
682071d4279SBram Moolenaar
6834f97475dSBram Moolenaar # if defined(FEAT_GUI_MSWIN) & defined(FEAT_TEAROFF)
6844ba37b58SBram Moolenaar // When adding a new submenu, may add a tearoff item
685071d4279SBram Moolenaar if ( addtearoff
686071d4279SBram Moolenaar && *next_name
687071d4279SBram Moolenaar && vim_strchr(p_go, GO_TEAROFF) != NULL
688310c32e8SBram Moolenaar && menu_is_menubar(name)
689310c32e8SBram Moolenaar # ifdef VIMDLL
690310c32e8SBram Moolenaar && (gui.in_use || gui.starting)
691310c32e8SBram Moolenaar # endif
692310c32e8SBram Moolenaar )
693071d4279SBram Moolenaar {
694071d4279SBram Moolenaar char_u *tearpath;
695071d4279SBram Moolenaar
696071d4279SBram Moolenaar /*
697071d4279SBram Moolenaar * The pointers next_name & path_name refer to a string with
698071d4279SBram Moolenaar * \'s and ^V's stripped out. But menu_path is a "raw"
699071d4279SBram Moolenaar * string, so we must correct for special characters.
700071d4279SBram Moolenaar */
701964b3746SBram Moolenaar tearpath = alloc(STRLEN(menu_path) + TEAR_LEN + 2);
702071d4279SBram Moolenaar if (tearpath != NULL)
703071d4279SBram Moolenaar {
704071d4279SBram Moolenaar char_u *s;
705071d4279SBram Moolenaar int idx;
706071d4279SBram Moolenaar
707071d4279SBram Moolenaar STRCPY(tearpath, menu_path);
708071d4279SBram Moolenaar idx = (int)(next_name - path_name - 1);
70991acfffcSBram Moolenaar for (s = tearpath; *s && s < tearpath + idx; MB_PTR_ADV(s))
710071d4279SBram Moolenaar {
711071d4279SBram Moolenaar if ((*s == '\\' || *s == Ctrl_V) && s[1])
712071d4279SBram Moolenaar {
713071d4279SBram Moolenaar ++idx;
714071d4279SBram Moolenaar ++s;
715071d4279SBram Moolenaar }
716071d4279SBram Moolenaar }
717071d4279SBram Moolenaar tearpath[idx] = NUL;
718071d4279SBram Moolenaar gui_add_tearoff(tearpath, pri_tab, pri_idx);
719071d4279SBram Moolenaar vim_free(tearpath);
720071d4279SBram Moolenaar }
721071d4279SBram Moolenaar }
722071d4279SBram Moolenaar # endif
723071d4279SBram Moolenaar }
7244ba37b58SBram Moolenaar #endif // FEAT_GUI
725071d4279SBram Moolenaar
726071d4279SBram Moolenaar menup = &menu->children;
727071d4279SBram Moolenaar parent = menu;
728071d4279SBram Moolenaar name = next_name;
729d23a8236SBram Moolenaar VIM_CLEAR(dname);
730071d4279SBram Moolenaar if (pri_tab[pri_idx + 1] != -1)
731071d4279SBram Moolenaar ++pri_idx;
732071d4279SBram Moolenaar }
733071d4279SBram Moolenaar vim_free(path_name);
734071d4279SBram Moolenaar
735071d4279SBram Moolenaar /*
736071d4279SBram Moolenaar * Only add system menu items which have not been defined yet.
737071d4279SBram Moolenaar * First check if this was an ":amenu".
738071d4279SBram Moolenaar */
739071d4279SBram Moolenaar amenu = ((modes & (MENU_NORMAL_MODE | MENU_INSERT_MODE)) ==
740071d4279SBram Moolenaar (MENU_NORMAL_MODE | MENU_INSERT_MODE));
741071d4279SBram Moolenaar if (sys_menu)
742071d4279SBram Moolenaar modes &= ~old_modes;
743071d4279SBram Moolenaar
744071d4279SBram Moolenaar if (menu != NULL && modes)
745071d4279SBram Moolenaar {
746071d4279SBram Moolenaar #ifdef FEAT_GUI
747071d4279SBram Moolenaar menu->cb = gui_menu_cb;
748071d4279SBram Moolenaar #endif
749071d4279SBram Moolenaar p = (call_data == NULL) ? NULL : vim_strsave(call_data);
750071d4279SBram Moolenaar
7514ba37b58SBram Moolenaar // loop over all modes, may add more than one
752071d4279SBram Moolenaar for (i = 0; i < MENU_MODES; ++i)
753071d4279SBram Moolenaar {
754071d4279SBram Moolenaar if (modes & (1 << i))
755071d4279SBram Moolenaar {
7564ba37b58SBram Moolenaar // free any old menu
757071d4279SBram Moolenaar free_menu_string(menu, i);
758071d4279SBram Moolenaar
7594ba37b58SBram Moolenaar // For "amenu", may insert an extra character.
7604ba37b58SBram Moolenaar // Don't do this if adding a tearbar (addtearoff == FALSE).
7614ba37b58SBram Moolenaar // Don't do this for "<Nop>".
762071d4279SBram Moolenaar c = 0;
7637871a500SBram Moolenaar d = 0;
764071d4279SBram Moolenaar if (amenu && call_data != NULL && *call_data != NUL
7654f97475dSBram Moolenaar #ifdef FEAT_GUI_MSWIN
766071d4279SBram Moolenaar && addtearoff
767071d4279SBram Moolenaar #endif
768071d4279SBram Moolenaar )
769071d4279SBram Moolenaar {
770071d4279SBram Moolenaar switch (1 << i)
771071d4279SBram Moolenaar {
772071d4279SBram Moolenaar case MENU_VISUAL_MODE:
773b3656edcSBram Moolenaar case MENU_SELECT_MODE:
774071d4279SBram Moolenaar case MENU_OP_PENDING_MODE:
775071d4279SBram Moolenaar case MENU_CMDLINE_MODE:
776071d4279SBram Moolenaar c = Ctrl_C;
777071d4279SBram Moolenaar break;
778071d4279SBram Moolenaar case MENU_INSERT_MODE:
7797871a500SBram Moolenaar c = Ctrl_BSL;
7807871a500SBram Moolenaar d = Ctrl_O;
781071d4279SBram Moolenaar break;
782071d4279SBram Moolenaar }
783071d4279SBram Moolenaar }
784071d4279SBram Moolenaar
7857871a500SBram Moolenaar if (c != 0)
786071d4279SBram Moolenaar {
787964b3746SBram Moolenaar menu->strings[i] = alloc(STRLEN(call_data) + 5);
788071d4279SBram Moolenaar if (menu->strings[i] != NULL)
789071d4279SBram Moolenaar {
790071d4279SBram Moolenaar menu->strings[i][0] = c;
7917871a500SBram Moolenaar if (d == 0)
792071d4279SBram Moolenaar STRCPY(menu->strings[i] + 1, call_data);
7937871a500SBram Moolenaar else
7947871a500SBram Moolenaar {
7957871a500SBram Moolenaar menu->strings[i][1] = d;
7967871a500SBram Moolenaar STRCPY(menu->strings[i] + 2, call_data);
7977871a500SBram Moolenaar }
798071d4279SBram Moolenaar if (c == Ctrl_C)
799071d4279SBram Moolenaar {
800a93fa7eeSBram Moolenaar int len = (int)STRLEN(menu->strings[i]);
801071d4279SBram Moolenaar
8024ba37b58SBram Moolenaar // Append CTRL-\ CTRL-G to obey 'insertmode'.
803071d4279SBram Moolenaar menu->strings[i][len] = Ctrl_BSL;
804071d4279SBram Moolenaar menu->strings[i][len + 1] = Ctrl_G;
805071d4279SBram Moolenaar menu->strings[i][len + 2] = NUL;
806071d4279SBram Moolenaar }
807071d4279SBram Moolenaar }
808071d4279SBram Moolenaar }
809071d4279SBram Moolenaar else
810071d4279SBram Moolenaar menu->strings[i] = p;
811071d4279SBram Moolenaar menu->noremap[i] = menuarg->noremap[0];
812071d4279SBram Moolenaar menu->silent[i] = menuarg->silent[0];
813071d4279SBram Moolenaar }
814071d4279SBram Moolenaar }
8154f97475dSBram Moolenaar #if defined(FEAT_TOOLBAR) && !defined(FEAT_GUI_MSWIN) \
816c3719bd8SBram Moolenaar && (defined(FEAT_BEVAL_GUI) || defined(FEAT_GUI_GTK))
8174ba37b58SBram Moolenaar // Need to update the menu tip.
818071d4279SBram Moolenaar if (modes & MENU_TIP_MODE)
819071d4279SBram Moolenaar gui_mch_menu_set_tip(menu);
820071d4279SBram Moolenaar #endif
821071d4279SBram Moolenaar }
822071d4279SBram Moolenaar return OK;
823071d4279SBram Moolenaar
824071d4279SBram Moolenaar erret:
825071d4279SBram Moolenaar vim_free(path_name);
826071d4279SBram Moolenaar vim_free(dname);
82718a0b128SBram Moolenaar
8284ba37b58SBram Moolenaar // Delete any empty submenu we added before discovering the error. Repeat
8294ba37b58SBram Moolenaar // for higher levels.
83018a0b128SBram Moolenaar while (parent != NULL && parent->children == NULL)
83118a0b128SBram Moolenaar {
83218a0b128SBram Moolenaar if (parent->parent == NULL)
8331b9645deSBram Moolenaar menup = root_menu_ptr;
83418a0b128SBram Moolenaar else
83518a0b128SBram Moolenaar menup = &parent->parent->children;
83618a0b128SBram Moolenaar for ( ; *menup != NULL && *menup != parent; menup = &((*menup)->next))
83718a0b128SBram Moolenaar ;
8384ba37b58SBram Moolenaar if (*menup == NULL) // safety check
83918a0b128SBram Moolenaar break;
84018a0b128SBram Moolenaar parent = parent->parent;
84118a0b128SBram Moolenaar free_menu(menup);
84218a0b128SBram Moolenaar }
843071d4279SBram Moolenaar return FAIL;
844071d4279SBram Moolenaar }
845071d4279SBram Moolenaar
846071d4279SBram Moolenaar /*
847071d4279SBram Moolenaar * Set the (sub)menu with the given name to enabled or disabled.
848071d4279SBram Moolenaar * Called recursively.
849071d4279SBram Moolenaar */
850071d4279SBram Moolenaar static int
menu_nable_recurse(vimmenu_T * menu,char_u * name,int modes,int enable)85152ea13daSBram Moolenaar menu_nable_recurse(
85252ea13daSBram Moolenaar vimmenu_T *menu,
85352ea13daSBram Moolenaar char_u *name,
85452ea13daSBram Moolenaar int modes,
85552ea13daSBram Moolenaar int enable)
856071d4279SBram Moolenaar {
857071d4279SBram Moolenaar char_u *p;
858071d4279SBram Moolenaar
859071d4279SBram Moolenaar if (menu == NULL)
8604ba37b58SBram Moolenaar return OK; // Got to bottom of hierarchy
861071d4279SBram Moolenaar
8624ba37b58SBram Moolenaar // Get name of this element in the menu hierarchy
863071d4279SBram Moolenaar p = menu_name_skip(name);
864071d4279SBram Moolenaar
8654ba37b58SBram Moolenaar // Find the menu
866071d4279SBram Moolenaar while (menu != NULL)
867071d4279SBram Moolenaar {
868071d4279SBram Moolenaar if (*name == NUL || *name == '*' || menu_name_equal(name, menu))
869071d4279SBram Moolenaar {
870071d4279SBram Moolenaar if (*p != NUL)
871071d4279SBram Moolenaar {
872071d4279SBram Moolenaar if (menu->children == NULL)
873071d4279SBram Moolenaar {
874f9e3e09fSBram Moolenaar emsg(_(e_notsubmenu));
875071d4279SBram Moolenaar return FAIL;
876071d4279SBram Moolenaar }
877071d4279SBram Moolenaar if (menu_nable_recurse(menu->children, p, modes, enable)
878071d4279SBram Moolenaar == FAIL)
879071d4279SBram Moolenaar return FAIL;
880071d4279SBram Moolenaar }
881071d4279SBram Moolenaar else
882071d4279SBram Moolenaar if (enable)
883071d4279SBram Moolenaar menu->enabled |= modes;
884071d4279SBram Moolenaar else
885071d4279SBram Moolenaar menu->enabled &= ~modes;
886071d4279SBram Moolenaar
887071d4279SBram Moolenaar /*
888071d4279SBram Moolenaar * When name is empty, we are doing all menu items for the given
889071d4279SBram Moolenaar * modes, so keep looping, otherwise we are just doing the named
890071d4279SBram Moolenaar * menu item (which has been found) so break here.
891071d4279SBram Moolenaar */
892071d4279SBram Moolenaar if (*name != NUL && *name != '*')
893071d4279SBram Moolenaar break;
894071d4279SBram Moolenaar }
895071d4279SBram Moolenaar menu = menu->next;
896071d4279SBram Moolenaar }
897071d4279SBram Moolenaar if (*name != NUL && *name != '*' && menu == NULL)
898071d4279SBram Moolenaar {
899f9e3e09fSBram Moolenaar semsg(_(e_nomenu), name);
900071d4279SBram Moolenaar return FAIL;
901071d4279SBram Moolenaar }
902071d4279SBram Moolenaar
903071d4279SBram Moolenaar #ifdef FEAT_GUI
9044ba37b58SBram Moolenaar // Want to update menus now even if mode not changed
905071d4279SBram Moolenaar force_menu_update = TRUE;
906071d4279SBram Moolenaar #endif
907071d4279SBram Moolenaar
908071d4279SBram Moolenaar return OK;
909071d4279SBram Moolenaar }
910071d4279SBram Moolenaar
911071d4279SBram Moolenaar /*
912071d4279SBram Moolenaar * Remove the (sub)menu with the given name from the menu hierarchy
913071d4279SBram Moolenaar * Called recursively.
914071d4279SBram Moolenaar */
915071d4279SBram Moolenaar static int
remove_menu(vimmenu_T ** menup,char_u * name,int modes,int silent)91652ea13daSBram Moolenaar remove_menu(
91752ea13daSBram Moolenaar vimmenu_T **menup,
91852ea13daSBram Moolenaar char_u *name,
91952ea13daSBram Moolenaar int modes,
9204ba37b58SBram Moolenaar int silent) // don't give error messages
921071d4279SBram Moolenaar {
922071d4279SBram Moolenaar vimmenu_T *menu;
923071d4279SBram Moolenaar vimmenu_T *child;
924071d4279SBram Moolenaar char_u *p;
925071d4279SBram Moolenaar
926071d4279SBram Moolenaar if (*menup == NULL)
9274ba37b58SBram Moolenaar return OK; // Got to bottom of hierarchy
928071d4279SBram Moolenaar
9294ba37b58SBram Moolenaar // Get name of this element in the menu hierarchy
930071d4279SBram Moolenaar p = menu_name_skip(name);
931071d4279SBram Moolenaar
9324ba37b58SBram Moolenaar // Find the menu
933071d4279SBram Moolenaar while ((menu = *menup) != NULL)
934071d4279SBram Moolenaar {
935071d4279SBram Moolenaar if (*name == NUL || menu_name_equal(name, menu))
936071d4279SBram Moolenaar {
937071d4279SBram Moolenaar if (*p != NUL && menu->children == NULL)
938071d4279SBram Moolenaar {
939071d4279SBram Moolenaar if (!silent)
940f9e3e09fSBram Moolenaar emsg(_(e_notsubmenu));
941071d4279SBram Moolenaar return FAIL;
942071d4279SBram Moolenaar }
943071d4279SBram Moolenaar if ((menu->modes & modes) != 0x0)
944071d4279SBram Moolenaar {
9454f97475dSBram Moolenaar #if defined(FEAT_GUI_MSWIN) & defined(FEAT_TEAROFF)
946071d4279SBram Moolenaar /*
947071d4279SBram Moolenaar * If we are removing all entries for this menu,MENU_ALL_MODES,
948071d4279SBram Moolenaar * Then kill any tearoff before we start
949071d4279SBram Moolenaar */
950071d4279SBram Moolenaar if (*p == NUL && modes == MENU_ALL_MODES)
951071d4279SBram Moolenaar {
952071d4279SBram Moolenaar if (IsWindow(menu->tearoff_handle))
953071d4279SBram Moolenaar DestroyWindow(menu->tearoff_handle);
954071d4279SBram Moolenaar }
955071d4279SBram Moolenaar #endif
956071d4279SBram Moolenaar if (remove_menu(&menu->children, p, modes, silent) == FAIL)
957071d4279SBram Moolenaar return FAIL;
958071d4279SBram Moolenaar }
959071d4279SBram Moolenaar else if (*name != NUL)
960071d4279SBram Moolenaar {
961071d4279SBram Moolenaar if (!silent)
962f9e3e09fSBram Moolenaar emsg(_(e_menuothermode));
963071d4279SBram Moolenaar return FAIL;
964071d4279SBram Moolenaar }
965071d4279SBram Moolenaar
966071d4279SBram Moolenaar /*
967071d4279SBram Moolenaar * When name is empty, we are removing all menu items for the given
968071d4279SBram Moolenaar * modes, so keep looping, otherwise we are just removing the named
969071d4279SBram Moolenaar * menu item (which has been found) so break here.
970071d4279SBram Moolenaar */
971071d4279SBram Moolenaar if (*name != NUL)
972071d4279SBram Moolenaar break;
973071d4279SBram Moolenaar
9744ba37b58SBram Moolenaar // Remove the menu item for the given mode[s]. If the menu item
9754ba37b58SBram Moolenaar // is no longer valid in ANY mode, delete it
976071d4279SBram Moolenaar menu->modes &= ~modes;
977071d4279SBram Moolenaar if (modes & MENU_TIP_MODE)
978071d4279SBram Moolenaar free_menu_string(menu, MENU_INDEX_TIP);
979071d4279SBram Moolenaar if ((menu->modes & MENU_ALL_MODES) == 0)
980071d4279SBram Moolenaar free_menu(menup);
981071d4279SBram Moolenaar else
982071d4279SBram Moolenaar menup = &menu->next;
983071d4279SBram Moolenaar }
984071d4279SBram Moolenaar else
985071d4279SBram Moolenaar menup = &menu->next;
986071d4279SBram Moolenaar }
987071d4279SBram Moolenaar if (*name != NUL)
988071d4279SBram Moolenaar {
989071d4279SBram Moolenaar if (menu == NULL)
990071d4279SBram Moolenaar {
991071d4279SBram Moolenaar if (!silent)
992f9e3e09fSBram Moolenaar semsg(_(e_nomenu), name);
993071d4279SBram Moolenaar return FAIL;
994071d4279SBram Moolenaar }
995071d4279SBram Moolenaar
996071d4279SBram Moolenaar
9974ba37b58SBram Moolenaar // Recalculate modes for menu based on the new updated children
998071d4279SBram Moolenaar menu->modes &= ~modes;
9994f97475dSBram Moolenaar #if defined(FEAT_GUI_MSWIN) & defined(FEAT_TEAROFF)
10004ba37b58SBram Moolenaar if ((s_tearoffs) && (menu->children != NULL)) // there's a tear bar..
10014ba37b58SBram Moolenaar child = menu->children->next; // don't count tearoff bar
1002071d4279SBram Moolenaar else
1003071d4279SBram Moolenaar #endif
1004071d4279SBram Moolenaar child = menu->children;
1005071d4279SBram Moolenaar for ( ; child != NULL; child = child->next)
1006071d4279SBram Moolenaar menu->modes |= child->modes;
1007071d4279SBram Moolenaar if (modes & MENU_TIP_MODE)
1008071d4279SBram Moolenaar {
1009071d4279SBram Moolenaar free_menu_string(menu, MENU_INDEX_TIP);
10104f97475dSBram Moolenaar #if defined(FEAT_TOOLBAR) && !defined(FEAT_GUI_MSWIN) \
1011c3719bd8SBram Moolenaar && (defined(FEAT_BEVAL_GUI) || defined(FEAT_GUI_GTK))
10124ba37b58SBram Moolenaar // Need to update the menu tip.
1013071d4279SBram Moolenaar if (gui.in_use)
1014071d4279SBram Moolenaar gui_mch_menu_set_tip(menu);
1015071d4279SBram Moolenaar #endif
1016071d4279SBram Moolenaar }
1017071d4279SBram Moolenaar if ((menu->modes & MENU_ALL_MODES) == 0)
1018071d4279SBram Moolenaar {
10194ba37b58SBram Moolenaar // The menu item is no longer valid in ANY mode, so delete it
10204f97475dSBram Moolenaar #if defined(FEAT_GUI_MSWIN) & defined(FEAT_TEAROFF)
10214ba37b58SBram Moolenaar if (s_tearoffs && menu->children != NULL) // there's a tear bar..
1022071d4279SBram Moolenaar free_menu(&menu->children);
1023071d4279SBram Moolenaar #endif
1024071d4279SBram Moolenaar *menup = menu;
1025071d4279SBram Moolenaar free_menu(menup);
1026071d4279SBram Moolenaar }
1027071d4279SBram Moolenaar }
1028071d4279SBram Moolenaar
1029071d4279SBram Moolenaar return OK;
1030071d4279SBram Moolenaar }
1031071d4279SBram Moolenaar
1032071d4279SBram Moolenaar /*
10331b9645deSBram Moolenaar * Remove the WinBar menu from window "wp".
10341b9645deSBram Moolenaar */
10351b9645deSBram Moolenaar void
remove_winbar(win_T * wp)10361b9645deSBram Moolenaar remove_winbar(win_T *wp)
10371b9645deSBram Moolenaar {
10381b9645deSBram Moolenaar remove_menu(&wp->w_winbar, (char_u *)"", MENU_ALL_MODES, TRUE);
10391b9645deSBram Moolenaar vim_free(wp->w_winbar_items);
10401b9645deSBram Moolenaar }
10411b9645deSBram Moolenaar
10421b9645deSBram Moolenaar /*
1043071d4279SBram Moolenaar * Free the given menu structure and remove it from the linked list.
1044071d4279SBram Moolenaar */
1045071d4279SBram Moolenaar static void
free_menu(vimmenu_T ** menup)104652ea13daSBram Moolenaar free_menu(vimmenu_T **menup)
1047071d4279SBram Moolenaar {
1048071d4279SBram Moolenaar int i;
1049071d4279SBram Moolenaar vimmenu_T *menu;
1050071d4279SBram Moolenaar
1051071d4279SBram Moolenaar menu = *menup;
1052071d4279SBram Moolenaar
1053071d4279SBram Moolenaar #ifdef FEAT_GUI
10544ba37b58SBram Moolenaar // Free machine specific menu structures (only when already created)
10554ba37b58SBram Moolenaar // Also may rebuild a tearoff'ed menu
1056071d4279SBram Moolenaar if (gui.in_use)
1057071d4279SBram Moolenaar gui_mch_destroy_menu(menu);
1058071d4279SBram Moolenaar #endif
1059071d4279SBram Moolenaar
10604ba37b58SBram Moolenaar // Don't change *menup until after calling gui_mch_destroy_menu(). The
10614ba37b58SBram Moolenaar // MacOS code needs the original structure to properly delete the menu.
1062071d4279SBram Moolenaar *menup = menu->next;
1063071d4279SBram Moolenaar vim_free(menu->name);
1064071d4279SBram Moolenaar vim_free(menu->dname);
106570b11cddSBram Moolenaar #ifdef FEAT_MULTI_LANG
106670b11cddSBram Moolenaar vim_free(menu->en_name);
106770b11cddSBram Moolenaar vim_free(menu->en_dname);
106870b11cddSBram Moolenaar #endif
1069071d4279SBram Moolenaar vim_free(menu->actext);
1070071d4279SBram Moolenaar #ifdef FEAT_TOOLBAR
1071071d4279SBram Moolenaar vim_free(menu->iconfile);
1072bee0c5b2SBram Moolenaar # ifdef FEAT_GUI_MOTIF
1073bee0c5b2SBram Moolenaar vim_free(menu->xpm_fname);
1074bee0c5b2SBram Moolenaar # endif
1075071d4279SBram Moolenaar #endif
1076071d4279SBram Moolenaar for (i = 0; i < MENU_MODES; i++)
1077071d4279SBram Moolenaar free_menu_string(menu, i);
1078071d4279SBram Moolenaar vim_free(menu);
1079071d4279SBram Moolenaar
1080071d4279SBram Moolenaar #ifdef FEAT_GUI
10814ba37b58SBram Moolenaar // Want to update menus now even if mode not changed
1082071d4279SBram Moolenaar force_menu_update = TRUE;
1083071d4279SBram Moolenaar #endif
1084071d4279SBram Moolenaar }
1085071d4279SBram Moolenaar
1086071d4279SBram Moolenaar /*
1087071d4279SBram Moolenaar * Free the menu->string with the given index.
1088071d4279SBram Moolenaar */
1089071d4279SBram Moolenaar static void
free_menu_string(vimmenu_T * menu,int idx)109052ea13daSBram Moolenaar free_menu_string(vimmenu_T *menu, int idx)
1091071d4279SBram Moolenaar {
1092071d4279SBram Moolenaar int count = 0;
1093071d4279SBram Moolenaar int i;
1094071d4279SBram Moolenaar
1095071d4279SBram Moolenaar for (i = 0; i < MENU_MODES; i++)
1096071d4279SBram Moolenaar if (menu->strings[i] == menu->strings[idx])
1097071d4279SBram Moolenaar count++;
1098071d4279SBram Moolenaar if (count == 1)
1099071d4279SBram Moolenaar vim_free(menu->strings[idx]);
1100071d4279SBram Moolenaar menu->strings[idx] = NULL;
1101071d4279SBram Moolenaar }
1102071d4279SBram Moolenaar
1103071d4279SBram Moolenaar /*
1104071d4279SBram Moolenaar * Show the mapping associated with a menu item or hierarchy in a sub-menu.
1105071d4279SBram Moolenaar */
1106071d4279SBram Moolenaar static int
show_menus(char_u * path_name,int modes)110752ea13daSBram Moolenaar show_menus(char_u *path_name, int modes)
1108071d4279SBram Moolenaar {
1109071d4279SBram Moolenaar char_u *p;
1110071d4279SBram Moolenaar char_u *name;
1111071d4279SBram Moolenaar vimmenu_T *menu;
1112071d4279SBram Moolenaar vimmenu_T *parent = NULL;
1113071d4279SBram Moolenaar
1114071d4279SBram Moolenaar name = path_name = vim_strsave(path_name);
1115071d4279SBram Moolenaar if (path_name == NULL)
1116071d4279SBram Moolenaar return FAIL;
11171b9645deSBram Moolenaar menu = *get_root_menu(path_name);
1118071d4279SBram Moolenaar
11194ba37b58SBram Moolenaar // First, find the (sub)menu with the given name
1120071d4279SBram Moolenaar while (*name)
1121071d4279SBram Moolenaar {
1122071d4279SBram Moolenaar p = menu_name_skip(name);
1123071d4279SBram Moolenaar while (menu != NULL)
1124071d4279SBram Moolenaar {
1125071d4279SBram Moolenaar if (menu_name_equal(name, menu))
1126071d4279SBram Moolenaar {
11274ba37b58SBram Moolenaar // Found menu
1128071d4279SBram Moolenaar if (*p != NUL && menu->children == NULL)
1129071d4279SBram Moolenaar {
1130f9e3e09fSBram Moolenaar emsg(_(e_notsubmenu));
1131071d4279SBram Moolenaar vim_free(path_name);
1132071d4279SBram Moolenaar return FAIL;
1133071d4279SBram Moolenaar }
1134071d4279SBram Moolenaar else if ((menu->modes & modes) == 0x0)
1135071d4279SBram Moolenaar {
1136f9e3e09fSBram Moolenaar emsg(_(e_menuothermode));
1137071d4279SBram Moolenaar vim_free(path_name);
1138071d4279SBram Moolenaar return FAIL;
1139071d4279SBram Moolenaar }
1140071d4279SBram Moolenaar break;
1141071d4279SBram Moolenaar }
1142071d4279SBram Moolenaar menu = menu->next;
1143071d4279SBram Moolenaar }
1144071d4279SBram Moolenaar if (menu == NULL)
1145071d4279SBram Moolenaar {
1146f9e3e09fSBram Moolenaar semsg(_(e_nomenu), name);
1147071d4279SBram Moolenaar vim_free(path_name);
1148071d4279SBram Moolenaar return FAIL;
1149071d4279SBram Moolenaar }
1150071d4279SBram Moolenaar name = p;
1151071d4279SBram Moolenaar parent = menu;
1152071d4279SBram Moolenaar menu = menu->children;
1153071d4279SBram Moolenaar }
1154acbd4427SBram Moolenaar vim_free(path_name);
1155071d4279SBram Moolenaar
11564ba37b58SBram Moolenaar // Now we have found the matching menu, and we list the mappings
11574ba37b58SBram Moolenaar // Highlight title
115832526b3cSBram Moolenaar msg_puts_title(_("\n--- Menus ---"));
1159071d4279SBram Moolenaar
1160071d4279SBram Moolenaar show_menus_recursive(parent, modes, 0);
1161071d4279SBram Moolenaar return OK;
1162071d4279SBram Moolenaar }
1163071d4279SBram Moolenaar
1164071d4279SBram Moolenaar /*
1165071d4279SBram Moolenaar * Recursively show the mappings associated with the menus under the given one
1166071d4279SBram Moolenaar */
1167071d4279SBram Moolenaar static void
show_menus_recursive(vimmenu_T * menu,int modes,int depth)116852ea13daSBram Moolenaar show_menus_recursive(vimmenu_T *menu, int modes, int depth)
1169071d4279SBram Moolenaar {
1170071d4279SBram Moolenaar int i;
1171071d4279SBram Moolenaar int bit;
1172071d4279SBram Moolenaar
1173071d4279SBram Moolenaar if (menu != NULL && (menu->modes & modes) == 0x0)
1174071d4279SBram Moolenaar return;
1175071d4279SBram Moolenaar
1176071d4279SBram Moolenaar if (menu != NULL)
1177071d4279SBram Moolenaar {
1178071d4279SBram Moolenaar msg_putchar('\n');
11794ba37b58SBram Moolenaar if (got_int) // "q" hit for "--more--"
1180071d4279SBram Moolenaar return;
1181071d4279SBram Moolenaar for (i = 0; i < depth; i++)
118232526b3cSBram Moolenaar msg_puts(" ");
1183071d4279SBram Moolenaar if (menu->priority)
1184071d4279SBram Moolenaar {
1185071d4279SBram Moolenaar msg_outnum((long)menu->priority);
118632526b3cSBram Moolenaar msg_puts(" ");
1187071d4279SBram Moolenaar }
11884ba37b58SBram Moolenaar // Same highlighting as for directories!?
11898820b486SBram Moolenaar msg_outtrans_attr(menu->name, HL_ATTR(HLF_D));
1190071d4279SBram Moolenaar }
1191071d4279SBram Moolenaar
1192071d4279SBram Moolenaar if (menu != NULL && menu->children == NULL)
1193071d4279SBram Moolenaar {
1194071d4279SBram Moolenaar for (bit = 0; bit < MENU_MODES; bit++)
1195071d4279SBram Moolenaar if ((menu->modes & modes & (1 << bit)) != 0)
1196071d4279SBram Moolenaar {
1197071d4279SBram Moolenaar msg_putchar('\n');
11984ba37b58SBram Moolenaar if (got_int) // "q" hit for "--more--"
1199071d4279SBram Moolenaar return;
1200071d4279SBram Moolenaar for (i = 0; i < depth + 2; i++)
120132526b3cSBram Moolenaar msg_puts(" ");
120232526b3cSBram Moolenaar msg_puts(menu_mode_chars[bit]);
1203071d4279SBram Moolenaar if (menu->noremap[bit] == REMAP_NONE)
1204071d4279SBram Moolenaar msg_putchar('*');
1205071d4279SBram Moolenaar else if (menu->noremap[bit] == REMAP_SCRIPT)
1206071d4279SBram Moolenaar msg_putchar('&');
1207071d4279SBram Moolenaar else
1208071d4279SBram Moolenaar msg_putchar(' ');
1209071d4279SBram Moolenaar if (menu->silent[bit])
1210071d4279SBram Moolenaar msg_putchar('s');
1211071d4279SBram Moolenaar else
1212071d4279SBram Moolenaar msg_putchar(' ');
1213071d4279SBram Moolenaar if ((menu->modes & menu->enabled & (1 << bit)) == 0)
1214071d4279SBram Moolenaar msg_putchar('-');
1215071d4279SBram Moolenaar else
1216071d4279SBram Moolenaar msg_putchar(' ');
121732526b3cSBram Moolenaar msg_puts(" ");
1218071d4279SBram Moolenaar if (*menu->strings[bit] == NUL)
121932526b3cSBram Moolenaar msg_puts_attr("<Nop>", HL_ATTR(HLF_8));
1220071d4279SBram Moolenaar else
1221725310d8SBram Moolenaar msg_outtrans_special(menu->strings[bit], FALSE, 0);
1222071d4279SBram Moolenaar }
1223071d4279SBram Moolenaar }
1224071d4279SBram Moolenaar else
1225071d4279SBram Moolenaar {
1226071d4279SBram Moolenaar if (menu == NULL)
1227071d4279SBram Moolenaar {
1228071d4279SBram Moolenaar menu = root_menu;
1229071d4279SBram Moolenaar depth--;
1230071d4279SBram Moolenaar }
1231071d4279SBram Moolenaar else
1232071d4279SBram Moolenaar menu = menu->children;
1233071d4279SBram Moolenaar
12344ba37b58SBram Moolenaar // recursively show all children. Skip PopUp[nvoci].
1235071d4279SBram Moolenaar for (; menu != NULL && !got_int; menu = menu->next)
1236071d4279SBram Moolenaar if (!menu_is_hidden(menu->dname))
1237071d4279SBram Moolenaar show_menus_recursive(menu, modes, depth + 1);
1238071d4279SBram Moolenaar }
1239071d4279SBram Moolenaar }
1240071d4279SBram Moolenaar
1241071d4279SBram Moolenaar /*
1242071d4279SBram Moolenaar * Used when expanding menu names.
1243071d4279SBram Moolenaar */
1244071d4279SBram Moolenaar static vimmenu_T *expand_menu = NULL;
12451b9645deSBram Moolenaar static vimmenu_T *expand_menu_alt = NULL;
1246071d4279SBram Moolenaar static int expand_modes = 0x0;
12474ba37b58SBram Moolenaar static int expand_emenu; // TRUE for ":emenu" command
1248071d4279SBram Moolenaar
1249071d4279SBram Moolenaar /*
1250071d4279SBram Moolenaar * Work out what to complete when doing command line completion of menu names.
1251071d4279SBram Moolenaar */
1252071d4279SBram Moolenaar char_u *
set_context_in_menu_cmd(expand_T * xp,char_u * cmd,char_u * arg,int forceit)125352ea13daSBram Moolenaar set_context_in_menu_cmd(
125452ea13daSBram Moolenaar expand_T *xp,
125552ea13daSBram Moolenaar char_u *cmd,
125652ea13daSBram Moolenaar char_u *arg,
125752ea13daSBram Moolenaar int forceit)
1258071d4279SBram Moolenaar {
1259071d4279SBram Moolenaar char_u *after_dot;
1260071d4279SBram Moolenaar char_u *p;
1261071d4279SBram Moolenaar char_u *path_name = NULL;
1262071d4279SBram Moolenaar char_u *name;
1263071d4279SBram Moolenaar int unmenu;
1264071d4279SBram Moolenaar vimmenu_T *menu;
1265071d4279SBram Moolenaar int expand_menus;
1266071d4279SBram Moolenaar
1267071d4279SBram Moolenaar xp->xp_context = EXPAND_UNSUCCESSFUL;
1268071d4279SBram Moolenaar
1269071d4279SBram Moolenaar
12704ba37b58SBram Moolenaar // Check for priority numbers, enable and disable
1271071d4279SBram Moolenaar for (p = arg; *p; ++p)
1272071d4279SBram Moolenaar if (!VIM_ISDIGIT(*p) && *p != '.')
1273071d4279SBram Moolenaar break;
1274071d4279SBram Moolenaar
12751c465444SBram Moolenaar if (!VIM_ISWHITE(*p))
1276071d4279SBram Moolenaar {
1277071d4279SBram Moolenaar if (STRNCMP(arg, "enable", 6) == 0
12781c465444SBram Moolenaar && (arg[6] == NUL || VIM_ISWHITE(arg[6])))
1279071d4279SBram Moolenaar p = arg + 6;
1280071d4279SBram Moolenaar else if (STRNCMP(arg, "disable", 7) == 0
12811c465444SBram Moolenaar && (arg[7] == NUL || VIM_ISWHITE(arg[7])))
1282071d4279SBram Moolenaar p = arg + 7;
1283071d4279SBram Moolenaar else
1284071d4279SBram Moolenaar p = arg;
1285071d4279SBram Moolenaar }
1286071d4279SBram Moolenaar
12871c465444SBram Moolenaar while (*p != NUL && VIM_ISWHITE(*p))
1288071d4279SBram Moolenaar ++p;
1289071d4279SBram Moolenaar
1290071d4279SBram Moolenaar arg = after_dot = p;
1291071d4279SBram Moolenaar
12921c465444SBram Moolenaar for (; *p && !VIM_ISWHITE(*p); ++p)
1293071d4279SBram Moolenaar {
1294071d4279SBram Moolenaar if ((*p == '\\' || *p == Ctrl_V) && p[1] != NUL)
1295071d4279SBram Moolenaar p++;
1296071d4279SBram Moolenaar else if (*p == '.')
1297071d4279SBram Moolenaar after_dot = p + 1;
1298071d4279SBram Moolenaar }
1299071d4279SBram Moolenaar
13004ba37b58SBram Moolenaar // ":tearoff" and ":popup" only use menus, not entries
1301071d4279SBram Moolenaar expand_menus = !((*cmd == 't' && cmd[1] == 'e') || *cmd == 'p');
1302071d4279SBram Moolenaar expand_emenu = (*cmd == 'e');
13031c465444SBram Moolenaar if (expand_menus && VIM_ISWHITE(*p))
13044ba37b58SBram Moolenaar return NULL; // TODO: check for next command?
13054ba37b58SBram Moolenaar if (*p == NUL) // Complete the menu name
1306071d4279SBram Moolenaar {
13071b9645deSBram Moolenaar int try_alt_menu = TRUE;
13081b9645deSBram Moolenaar
1309071d4279SBram Moolenaar /*
1310071d4279SBram Moolenaar * With :unmenu, you only want to match menus for the appropriate mode.
1311071d4279SBram Moolenaar * With :menu though you might want to add a menu with the same name as
1312071d4279SBram Moolenaar * one in another mode, so match menus from other modes too.
1313071d4279SBram Moolenaar */
1314071d4279SBram Moolenaar expand_modes = get_menu_cmd_modes(cmd, forceit, NULL, &unmenu);
1315071d4279SBram Moolenaar if (!unmenu)
1316071d4279SBram Moolenaar expand_modes = MENU_ALL_MODES;
1317071d4279SBram Moolenaar
1318071d4279SBram Moolenaar menu = root_menu;
1319071d4279SBram Moolenaar if (after_dot != arg)
1320071d4279SBram Moolenaar {
1321964b3746SBram Moolenaar path_name = alloc(after_dot - arg);
1322071d4279SBram Moolenaar if (path_name == NULL)
1323071d4279SBram Moolenaar return NULL;
1324ce0842a6SBram Moolenaar vim_strncpy(path_name, arg, after_dot - arg - 1);
1325071d4279SBram Moolenaar }
1326071d4279SBram Moolenaar name = path_name;
1327071d4279SBram Moolenaar while (name != NULL && *name)
1328071d4279SBram Moolenaar {
1329071d4279SBram Moolenaar p = menu_name_skip(name);
1330071d4279SBram Moolenaar while (menu != NULL)
1331071d4279SBram Moolenaar {
1332071d4279SBram Moolenaar if (menu_name_equal(name, menu))
1333071d4279SBram Moolenaar {
13344ba37b58SBram Moolenaar // Found menu
1335071d4279SBram Moolenaar if ((*p != NUL && menu->children == NULL)
1336071d4279SBram Moolenaar || ((menu->modes & expand_modes) == 0x0))
1337071d4279SBram Moolenaar {
1338071d4279SBram Moolenaar /*
1339071d4279SBram Moolenaar * Menu path continues, but we have reached a leaf.
1340071d4279SBram Moolenaar * Or menu exists only in another mode.
1341071d4279SBram Moolenaar */
1342071d4279SBram Moolenaar vim_free(path_name);
1343071d4279SBram Moolenaar return NULL;
1344071d4279SBram Moolenaar }
1345071d4279SBram Moolenaar break;
1346071d4279SBram Moolenaar }
1347071d4279SBram Moolenaar menu = menu->next;
13481b9645deSBram Moolenaar if (menu == NULL && try_alt_menu)
13491b9645deSBram Moolenaar {
13501b9645deSBram Moolenaar menu = curwin->w_winbar;
13511b9645deSBram Moolenaar try_alt_menu = FALSE;
13521b9645deSBram Moolenaar }
1353071d4279SBram Moolenaar }
1354071d4279SBram Moolenaar if (menu == NULL)
1355071d4279SBram Moolenaar {
13564ba37b58SBram Moolenaar // No menu found with the name we were looking for
1357071d4279SBram Moolenaar vim_free(path_name);
1358071d4279SBram Moolenaar return NULL;
1359071d4279SBram Moolenaar }
1360071d4279SBram Moolenaar name = p;
1361071d4279SBram Moolenaar menu = menu->children;
13621b9645deSBram Moolenaar try_alt_menu = FALSE;
1363071d4279SBram Moolenaar }
1364eb3593b3SBram Moolenaar vim_free(path_name);
1365071d4279SBram Moolenaar
1366071d4279SBram Moolenaar xp->xp_context = expand_menus ? EXPAND_MENUNAMES : EXPAND_MENUS;
1367071d4279SBram Moolenaar xp->xp_pattern = after_dot;
1368071d4279SBram Moolenaar expand_menu = menu;
13691b9645deSBram Moolenaar if (expand_menu == root_menu)
13701b9645deSBram Moolenaar expand_menu_alt = curwin->w_winbar;
13711b9645deSBram Moolenaar else
13721b9645deSBram Moolenaar expand_menu_alt = NULL;
1373071d4279SBram Moolenaar }
13744ba37b58SBram Moolenaar else // We're in the mapping part
1375071d4279SBram Moolenaar xp->xp_context = EXPAND_NOTHING;
1376071d4279SBram Moolenaar return NULL;
1377071d4279SBram Moolenaar }
1378071d4279SBram Moolenaar
1379071d4279SBram Moolenaar /*
1380071d4279SBram Moolenaar * Function given to ExpandGeneric() to obtain the list of (sub)menus (not
1381071d4279SBram Moolenaar * entries).
1382071d4279SBram Moolenaar */
1383071d4279SBram Moolenaar char_u *
get_menu_name(expand_T * xp UNUSED,int idx)138452ea13daSBram Moolenaar get_menu_name(expand_T *xp UNUSED, int idx)
1385071d4279SBram Moolenaar {
1386071d4279SBram Moolenaar static vimmenu_T *menu = NULL;
13871b9645deSBram Moolenaar static int did_alt_menu = FALSE;
1388071d4279SBram Moolenaar char_u *str;
138970b11cddSBram Moolenaar #ifdef FEAT_MULTI_LANG
139070b11cddSBram Moolenaar static int should_advance = FALSE;
139170b11cddSBram Moolenaar #endif
1392071d4279SBram Moolenaar
13934ba37b58SBram Moolenaar if (idx == 0) // first call: start at first item
139470b11cddSBram Moolenaar {
1395071d4279SBram Moolenaar menu = expand_menu;
13961b9645deSBram Moolenaar did_alt_menu = FALSE;
139741375647SBram Moolenaar #ifdef FEAT_MULTI_LANG
139870b11cddSBram Moolenaar should_advance = FALSE;
139941375647SBram Moolenaar #endif
140070b11cddSBram Moolenaar }
1401071d4279SBram Moolenaar
14024ba37b58SBram Moolenaar // Skip PopUp[nvoci].
1403071d4279SBram Moolenaar while (menu != NULL && (menu_is_hidden(menu->dname)
1404071d4279SBram Moolenaar || menu_is_separator(menu->dname)
1405071d4279SBram Moolenaar || menu_is_tearoff(menu->dname)
1406071d4279SBram Moolenaar || menu->children == NULL))
14071b9645deSBram Moolenaar {
1408071d4279SBram Moolenaar menu = menu->next;
14091b9645deSBram Moolenaar if (menu == NULL && !did_alt_menu)
14101b9645deSBram Moolenaar {
14111b9645deSBram Moolenaar menu = expand_menu_alt;
14121b9645deSBram Moolenaar did_alt_menu = TRUE;
14131b9645deSBram Moolenaar }
14141b9645deSBram Moolenaar }
1415071d4279SBram Moolenaar
14164ba37b58SBram Moolenaar if (menu == NULL) // at end of linked list
1417071d4279SBram Moolenaar return NULL;
1418071d4279SBram Moolenaar
1419071d4279SBram Moolenaar if (menu->modes & expand_modes)
142070b11cddSBram Moolenaar #ifdef FEAT_MULTI_LANG
142170b11cddSBram Moolenaar if (should_advance)
142270b11cddSBram Moolenaar str = menu->en_dname;
142370b11cddSBram Moolenaar else
142470b11cddSBram Moolenaar {
142570b11cddSBram Moolenaar #endif
1426071d4279SBram Moolenaar str = menu->dname;
142770b11cddSBram Moolenaar #ifdef FEAT_MULTI_LANG
142870b11cddSBram Moolenaar if (menu->en_dname == NULL)
142970b11cddSBram Moolenaar should_advance = TRUE;
143070b11cddSBram Moolenaar }
143170b11cddSBram Moolenaar #endif
1432071d4279SBram Moolenaar else
1433071d4279SBram Moolenaar str = (char_u *)"";
1434071d4279SBram Moolenaar
143570b11cddSBram Moolenaar #ifdef FEAT_MULTI_LANG
143670b11cddSBram Moolenaar if (should_advance)
143770b11cddSBram Moolenaar #endif
14381b9645deSBram Moolenaar {
14394ba37b58SBram Moolenaar // Advance to next menu entry.
1440071d4279SBram Moolenaar menu = menu->next;
14411b9645deSBram Moolenaar if (menu == NULL && !did_alt_menu)
14421b9645deSBram Moolenaar {
14431b9645deSBram Moolenaar menu = expand_menu_alt;
14441b9645deSBram Moolenaar did_alt_menu = TRUE;
14451b9645deSBram Moolenaar }
14461b9645deSBram Moolenaar }
1447071d4279SBram Moolenaar
144870b11cddSBram Moolenaar #ifdef FEAT_MULTI_LANG
144970b11cddSBram Moolenaar should_advance = !should_advance;
145070b11cddSBram Moolenaar #endif
145170b11cddSBram Moolenaar
1452071d4279SBram Moolenaar return str;
1453071d4279SBram Moolenaar }
1454071d4279SBram Moolenaar
1455071d4279SBram Moolenaar /*
1456071d4279SBram Moolenaar * Function given to ExpandGeneric() to obtain the list of menus and menu
1457071d4279SBram Moolenaar * entries.
1458071d4279SBram Moolenaar */
1459071d4279SBram Moolenaar char_u *
get_menu_names(expand_T * xp UNUSED,int idx)146052ea13daSBram Moolenaar get_menu_names(expand_T *xp UNUSED, int idx)
1461071d4279SBram Moolenaar {
1462071d4279SBram Moolenaar static vimmenu_T *menu = NULL;
14631b9645deSBram Moolenaar static int did_alt_menu = FALSE;
1464ef9d6aa7SBram Moolenaar #define TBUFFER_LEN 256
14654ba37b58SBram Moolenaar static char_u tbuffer[TBUFFER_LEN]; //hack
1466071d4279SBram Moolenaar char_u *str;
146770b11cddSBram Moolenaar #ifdef FEAT_MULTI_LANG
146870b11cddSBram Moolenaar static int should_advance = FALSE;
146970b11cddSBram Moolenaar #endif
1470071d4279SBram Moolenaar
14714ba37b58SBram Moolenaar if (idx == 0) // first call: start at first item
147270b11cddSBram Moolenaar {
1473071d4279SBram Moolenaar menu = expand_menu;
14741b9645deSBram Moolenaar did_alt_menu = FALSE;
147541375647SBram Moolenaar #ifdef FEAT_MULTI_LANG
147670b11cddSBram Moolenaar should_advance = FALSE;
147741375647SBram Moolenaar #endif
147870b11cddSBram Moolenaar }
1479071d4279SBram Moolenaar
14804ba37b58SBram Moolenaar // Skip Browse-style entries, popup menus and separators.
1481071d4279SBram Moolenaar while (menu != NULL
1482071d4279SBram Moolenaar && ( menu_is_hidden(menu->dname)
1483071d4279SBram Moolenaar || (expand_emenu && menu_is_separator(menu->dname))
1484071d4279SBram Moolenaar || menu_is_tearoff(menu->dname)
1485071d4279SBram Moolenaar #ifndef FEAT_BROWSE
1486071d4279SBram Moolenaar || menu->dname[STRLEN(menu->dname) - 1] == '.'
1487071d4279SBram Moolenaar #endif
1488071d4279SBram Moolenaar ))
14891b9645deSBram Moolenaar {
1490071d4279SBram Moolenaar menu = menu->next;
14911b9645deSBram Moolenaar if (menu == NULL && !did_alt_menu)
14921b9645deSBram Moolenaar {
14931b9645deSBram Moolenaar menu = expand_menu_alt;
14941b9645deSBram Moolenaar did_alt_menu = TRUE;
14951b9645deSBram Moolenaar }
14961b9645deSBram Moolenaar }
1497071d4279SBram Moolenaar
14984ba37b58SBram Moolenaar if (menu == NULL) // at end of linked list
1499071d4279SBram Moolenaar return NULL;
1500071d4279SBram Moolenaar
1501071d4279SBram Moolenaar if (menu->modes & expand_modes)
1502071d4279SBram Moolenaar {
1503071d4279SBram Moolenaar if (menu->children != NULL)
1504071d4279SBram Moolenaar {
150570b11cddSBram Moolenaar #ifdef FEAT_MULTI_LANG
150670b11cddSBram Moolenaar if (should_advance)
1507ef9d6aa7SBram Moolenaar vim_strncpy(tbuffer, menu->en_dname, TBUFFER_LEN - 2);
150870b11cddSBram Moolenaar else
150970b11cddSBram Moolenaar {
151070b11cddSBram Moolenaar #endif
1511ef9d6aa7SBram Moolenaar vim_strncpy(tbuffer, menu->dname, TBUFFER_LEN - 2);
151270b11cddSBram Moolenaar #ifdef FEAT_MULTI_LANG
151370b11cddSBram Moolenaar if (menu->en_dname == NULL)
151470b11cddSBram Moolenaar should_advance = TRUE;
151570b11cddSBram Moolenaar }
151670b11cddSBram Moolenaar #endif
15174ba37b58SBram Moolenaar // hack on menu separators: use a 'magic' char for the separator
15184ba37b58SBram Moolenaar // so that '.' in names gets escaped properly
1519071d4279SBram Moolenaar STRCAT(tbuffer, "\001");
1520071d4279SBram Moolenaar str = tbuffer;
1521071d4279SBram Moolenaar }
1522071d4279SBram Moolenaar else
152370b11cddSBram Moolenaar #ifdef FEAT_MULTI_LANG
152470b11cddSBram Moolenaar {
152570b11cddSBram Moolenaar if (should_advance)
152670b11cddSBram Moolenaar str = menu->en_dname;
152770b11cddSBram Moolenaar else
152870b11cddSBram Moolenaar {
152970b11cddSBram Moolenaar #endif
1530071d4279SBram Moolenaar str = menu->dname;
153170b11cddSBram Moolenaar #ifdef FEAT_MULTI_LANG
153270b11cddSBram Moolenaar if (menu->en_dname == NULL)
153370b11cddSBram Moolenaar should_advance = TRUE;
153470b11cddSBram Moolenaar }
153570b11cddSBram Moolenaar }
153670b11cddSBram Moolenaar #endif
1537071d4279SBram Moolenaar }
1538071d4279SBram Moolenaar else
1539071d4279SBram Moolenaar str = (char_u *)"";
1540071d4279SBram Moolenaar
154170b11cddSBram Moolenaar #ifdef FEAT_MULTI_LANG
154270b11cddSBram Moolenaar if (should_advance)
154370b11cddSBram Moolenaar #endif
15441b9645deSBram Moolenaar {
15454ba37b58SBram Moolenaar // Advance to next menu entry.
1546071d4279SBram Moolenaar menu = menu->next;
15471b9645deSBram Moolenaar if (menu == NULL && !did_alt_menu)
15481b9645deSBram Moolenaar {
15491b9645deSBram Moolenaar menu = expand_menu_alt;
15501b9645deSBram Moolenaar did_alt_menu = TRUE;
15511b9645deSBram Moolenaar }
15521b9645deSBram Moolenaar }
1553071d4279SBram Moolenaar
155470b11cddSBram Moolenaar #ifdef FEAT_MULTI_LANG
155570b11cddSBram Moolenaar should_advance = !should_advance;
155670b11cddSBram Moolenaar #endif
155770b11cddSBram Moolenaar
1558071d4279SBram Moolenaar return str;
1559071d4279SBram Moolenaar }
1560071d4279SBram Moolenaar
1561071d4279SBram Moolenaar /*
1562071d4279SBram Moolenaar * Skip over this element of the menu path and return the start of the next
1563071d4279SBram Moolenaar * element. Any \ and ^Vs are removed from the current element.
1564342337a1SBram Moolenaar * "name" may be modified.
1565071d4279SBram Moolenaar */
15665843f5f3SBram Moolenaar static char_u *
menu_name_skip(char_u * name)156752ea13daSBram Moolenaar menu_name_skip(char_u *name)
1568071d4279SBram Moolenaar {
1569071d4279SBram Moolenaar char_u *p;
1570071d4279SBram Moolenaar
157191acfffcSBram Moolenaar for (p = name; *p && *p != '.'; MB_PTR_ADV(p))
1572071d4279SBram Moolenaar {
1573071d4279SBram Moolenaar if (*p == '\\' || *p == Ctrl_V)
1574071d4279SBram Moolenaar {
15758c8de839SBram Moolenaar STRMOVE(p, p + 1);
1576071d4279SBram Moolenaar if (*p == NUL)
1577071d4279SBram Moolenaar break;
1578071d4279SBram Moolenaar }
1579071d4279SBram Moolenaar }
1580071d4279SBram Moolenaar if (*p)
1581071d4279SBram Moolenaar *p++ = NUL;
1582071d4279SBram Moolenaar return p;
1583071d4279SBram Moolenaar }
1584071d4279SBram Moolenaar
1585071d4279SBram Moolenaar /*
1586071d4279SBram Moolenaar * Return TRUE when "name" matches with menu "menu". The name is compared in
1587071d4279SBram Moolenaar * two ways: raw menu name and menu name without '&'. ignore part after a TAB.
1588071d4279SBram Moolenaar */
1589071d4279SBram Moolenaar static int
menu_name_equal(char_u * name,vimmenu_T * menu)159052ea13daSBram Moolenaar menu_name_equal(char_u *name, vimmenu_T *menu)
1591071d4279SBram Moolenaar {
159241375647SBram Moolenaar #ifdef FEAT_MULTI_LANG
159370b11cddSBram Moolenaar if (menu->en_name != NULL
159470b11cddSBram Moolenaar && (menu_namecmp(name, menu->en_name)
159570b11cddSBram Moolenaar || menu_namecmp(name, menu->en_dname)))
159670b11cddSBram Moolenaar return TRUE;
159741375647SBram Moolenaar #endif
159870b11cddSBram Moolenaar return menu_namecmp(name, menu->name) || menu_namecmp(name, menu->dname);
1599071d4279SBram Moolenaar }
1600071d4279SBram Moolenaar
1601071d4279SBram Moolenaar static int
menu_namecmp(char_u * name,char_u * mname)160252ea13daSBram Moolenaar menu_namecmp(char_u *name, char_u *mname)
1603071d4279SBram Moolenaar {
1604071d4279SBram Moolenaar int i;
1605071d4279SBram Moolenaar
1606071d4279SBram Moolenaar for (i = 0; name[i] != NUL && name[i] != TAB; ++i)
1607071d4279SBram Moolenaar if (name[i] != mname[i])
1608071d4279SBram Moolenaar break;
1609071d4279SBram Moolenaar return ((name[i] == NUL || name[i] == TAB)
1610071d4279SBram Moolenaar && (mname[i] == NUL || mname[i] == TAB));
1611071d4279SBram Moolenaar }
1612071d4279SBram Moolenaar
1613071d4279SBram Moolenaar /*
1614071d4279SBram Moolenaar * Return the modes specified by the given menu command (eg :menu! returns
1615071d4279SBram Moolenaar * MENU_CMDLINE_MODE | MENU_INSERT_MODE).
1616071d4279SBram Moolenaar * If "noremap" is not NULL, then the flag it points to is set according to
1617071d4279SBram Moolenaar * whether the command is a "nore" command.
1618071d4279SBram Moolenaar * If "unmenu" is not NULL, then the flag it points to is set according to
1619071d4279SBram Moolenaar * whether the command is an "unmenu" command.
1620071d4279SBram Moolenaar */
1621071d4279SBram Moolenaar static int
get_menu_cmd_modes(char_u * cmd,int forceit,int * noremap,int * unmenu)162252ea13daSBram Moolenaar get_menu_cmd_modes(
162352ea13daSBram Moolenaar char_u *cmd,
16244ba37b58SBram Moolenaar int forceit, // Was there a "!" after the command?
162552ea13daSBram Moolenaar int *noremap,
162652ea13daSBram Moolenaar int *unmenu)
1627071d4279SBram Moolenaar {
1628071d4279SBram Moolenaar int modes;
1629071d4279SBram Moolenaar
1630071d4279SBram Moolenaar switch (*cmd++)
1631071d4279SBram Moolenaar {
16324ba37b58SBram Moolenaar case 'v': // vmenu, vunmenu, vnoremenu
1633b3656edcSBram Moolenaar modes = MENU_VISUAL_MODE | MENU_SELECT_MODE;
1634b3656edcSBram Moolenaar break;
16354ba37b58SBram Moolenaar case 'x': // xmenu, xunmenu, xnoremenu
1636071d4279SBram Moolenaar modes = MENU_VISUAL_MODE;
1637071d4279SBram Moolenaar break;
16384ba37b58SBram Moolenaar case 's': // smenu, sunmenu, snoremenu
1639b3656edcSBram Moolenaar modes = MENU_SELECT_MODE;
1640b3656edcSBram Moolenaar break;
16414ba37b58SBram Moolenaar case 'o': // omenu
1642071d4279SBram Moolenaar modes = MENU_OP_PENDING_MODE;
1643071d4279SBram Moolenaar break;
16444ba37b58SBram Moolenaar case 'i': // imenu
1645071d4279SBram Moolenaar modes = MENU_INSERT_MODE;
1646071d4279SBram Moolenaar break;
1647071d4279SBram Moolenaar case 't':
16484ba37b58SBram Moolenaar if (*cmd == 'l') // tlmenu, tlunmenu, tlnoremenu
16494c5d8152SBram Moolenaar {
16504c5d8152SBram Moolenaar modes = MENU_TERMINAL_MODE;
16514c5d8152SBram Moolenaar ++cmd;
16524c5d8152SBram Moolenaar break;
16534c5d8152SBram Moolenaar }
16544ba37b58SBram Moolenaar modes = MENU_TIP_MODE; // tmenu
1655071d4279SBram Moolenaar break;
16564ba37b58SBram Moolenaar case 'c': // cmenu
1657071d4279SBram Moolenaar modes = MENU_CMDLINE_MODE;
1658071d4279SBram Moolenaar break;
16594ba37b58SBram Moolenaar case 'a': // amenu
1660071d4279SBram Moolenaar modes = MENU_INSERT_MODE | MENU_CMDLINE_MODE | MENU_NORMAL_MODE
1661b3656edcSBram Moolenaar | MENU_VISUAL_MODE | MENU_SELECT_MODE
1662b3656edcSBram Moolenaar | MENU_OP_PENDING_MODE;
1663071d4279SBram Moolenaar break;
1664071d4279SBram Moolenaar case 'n':
16654ba37b58SBram Moolenaar if (*cmd != 'o') // nmenu, not noremenu
1666071d4279SBram Moolenaar {
1667071d4279SBram Moolenaar modes = MENU_NORMAL_MODE;
1668071d4279SBram Moolenaar break;
1669071d4279SBram Moolenaar }
16704ba37b58SBram Moolenaar // FALLTHROUGH
1671071d4279SBram Moolenaar default:
1672071d4279SBram Moolenaar --cmd;
16734ba37b58SBram Moolenaar if (forceit) // menu!!
1674071d4279SBram Moolenaar modes = MENU_INSERT_MODE | MENU_CMDLINE_MODE;
16754ba37b58SBram Moolenaar else // menu
1676b3656edcSBram Moolenaar modes = MENU_NORMAL_MODE | MENU_VISUAL_MODE | MENU_SELECT_MODE
1677071d4279SBram Moolenaar | MENU_OP_PENDING_MODE;
1678071d4279SBram Moolenaar }
1679071d4279SBram Moolenaar
1680071d4279SBram Moolenaar if (noremap != NULL)
1681071d4279SBram Moolenaar *noremap = (*cmd == 'n' ? REMAP_NONE : REMAP_YES);
1682071d4279SBram Moolenaar if (unmenu != NULL)
1683071d4279SBram Moolenaar *unmenu = (*cmd == 'u');
1684071d4279SBram Moolenaar return modes;
1685071d4279SBram Moolenaar }
1686071d4279SBram Moolenaar
1687071d4279SBram Moolenaar /*
16880eabd4dcSBram Moolenaar * Return the string representation of the menu modes. Does the opposite
16890eabd4dcSBram Moolenaar * of get_menu_cmd_modes().
16900eabd4dcSBram Moolenaar */
16910eabd4dcSBram Moolenaar static char_u *
get_menu_mode_str(int modes)16920eabd4dcSBram Moolenaar get_menu_mode_str(int modes)
16930eabd4dcSBram Moolenaar {
16940eabd4dcSBram Moolenaar if ((modes & (MENU_INSERT_MODE | MENU_CMDLINE_MODE | MENU_NORMAL_MODE |
16950eabd4dcSBram Moolenaar MENU_VISUAL_MODE | MENU_SELECT_MODE | MENU_OP_PENDING_MODE))
16960eabd4dcSBram Moolenaar == (MENU_INSERT_MODE | MENU_CMDLINE_MODE | MENU_NORMAL_MODE |
16970eabd4dcSBram Moolenaar MENU_VISUAL_MODE | MENU_SELECT_MODE | MENU_OP_PENDING_MODE))
16980eabd4dcSBram Moolenaar return (char_u *)"a";
16990eabd4dcSBram Moolenaar if ((modes & (MENU_NORMAL_MODE | MENU_VISUAL_MODE | MENU_SELECT_MODE |
17000eabd4dcSBram Moolenaar MENU_OP_PENDING_MODE))
17010eabd4dcSBram Moolenaar == (MENU_NORMAL_MODE | MENU_VISUAL_MODE | MENU_SELECT_MODE |
17020eabd4dcSBram Moolenaar MENU_OP_PENDING_MODE))
17030eabd4dcSBram Moolenaar return (char_u *)" ";
17040eabd4dcSBram Moolenaar if ((modes & (MENU_INSERT_MODE | MENU_CMDLINE_MODE))
17050eabd4dcSBram Moolenaar == (MENU_INSERT_MODE | MENU_CMDLINE_MODE))
17060eabd4dcSBram Moolenaar return (char_u *)"!";
17070eabd4dcSBram Moolenaar if ((modes & (MENU_VISUAL_MODE | MENU_SELECT_MODE))
17080eabd4dcSBram Moolenaar == (MENU_VISUAL_MODE | MENU_SELECT_MODE))
17090eabd4dcSBram Moolenaar return (char_u *)"v";
17100eabd4dcSBram Moolenaar if (modes & MENU_VISUAL_MODE)
17110eabd4dcSBram Moolenaar return (char_u *)"x";
17120eabd4dcSBram Moolenaar if (modes & MENU_SELECT_MODE)
17130eabd4dcSBram Moolenaar return (char_u *)"s";
17140eabd4dcSBram Moolenaar if (modes & MENU_OP_PENDING_MODE)
17150eabd4dcSBram Moolenaar return (char_u *)"o";
17160eabd4dcSBram Moolenaar if (modes & MENU_INSERT_MODE)
17170eabd4dcSBram Moolenaar return (char_u *)"i";
17180eabd4dcSBram Moolenaar if (modes & MENU_TERMINAL_MODE)
17190eabd4dcSBram Moolenaar return (char_u *)"tl";
17200eabd4dcSBram Moolenaar if (modes & MENU_CMDLINE_MODE)
17210eabd4dcSBram Moolenaar return (char_u *)"c";
17220eabd4dcSBram Moolenaar if (modes & MENU_NORMAL_MODE)
17230eabd4dcSBram Moolenaar return (char_u *)"n";
17240eabd4dcSBram Moolenaar if (modes & MENU_TIP_MODE)
17250eabd4dcSBram Moolenaar return (char_u *)"t";
17260eabd4dcSBram Moolenaar
17270eabd4dcSBram Moolenaar return (char_u *)"";
17280eabd4dcSBram Moolenaar }
17290eabd4dcSBram Moolenaar
17300eabd4dcSBram Moolenaar /*
1731071d4279SBram Moolenaar * Modify a menu name starting with "PopUp" to include the mode character.
1732071d4279SBram Moolenaar * Returns the name in allocated memory (NULL for failure).
1733071d4279SBram Moolenaar */
1734071d4279SBram Moolenaar static char_u *
popup_mode_name(char_u * name,int idx)173552ea13daSBram Moolenaar popup_mode_name(char_u *name, int idx)
1736071d4279SBram Moolenaar {
1737071d4279SBram Moolenaar char_u *p;
1738071d4279SBram Moolenaar int len = (int)STRLEN(name);
17394c5d8152SBram Moolenaar char *mode_chars = menu_mode_chars[idx];
17404c5d8152SBram Moolenaar int mode_chars_len = (int)strlen(mode_chars);
17414c5d8152SBram Moolenaar int i;
1742071d4279SBram Moolenaar
17434c5d8152SBram Moolenaar p = vim_strnsave(name, len + mode_chars_len);
1744071d4279SBram Moolenaar if (p != NULL)
1745071d4279SBram Moolenaar {
17464c5d8152SBram Moolenaar mch_memmove(p + 5 + mode_chars_len, p + 5, (size_t)(len - 4));
17474c5d8152SBram Moolenaar for (i = 0; i < mode_chars_len; ++i)
17484c5d8152SBram Moolenaar p[5 + i] = menu_mode_chars[idx][i];
17494c5d8152SBram Moolenaar }
1750071d4279SBram Moolenaar return p;
1751071d4279SBram Moolenaar }
1752071d4279SBram Moolenaar
1753071d4279SBram Moolenaar #if defined(FEAT_GUI) || defined(PROTO)
1754071d4279SBram Moolenaar /*
1755071d4279SBram Moolenaar * Return the index into the menu->strings or menu->noremap arrays for the
1756071d4279SBram Moolenaar * current state. Returns MENU_INDEX_INVALID if there is no mapping for the
1757071d4279SBram Moolenaar * given menu in the current mode.
1758071d4279SBram Moolenaar */
1759071d4279SBram Moolenaar int
get_menu_index(vimmenu_T * menu,int state)176052ea13daSBram Moolenaar get_menu_index(vimmenu_T *menu, int state)
1761071d4279SBram Moolenaar {
1762071d4279SBram Moolenaar int idx;
1763071d4279SBram Moolenaar
1764071d4279SBram Moolenaar if ((state & INSERT))
1765071d4279SBram Moolenaar idx = MENU_INDEX_INSERT;
1766071d4279SBram Moolenaar else if (state & CMDLINE)
1767071d4279SBram Moolenaar idx = MENU_INDEX_CMDLINE;
17684c5d8152SBram Moolenaar #ifdef FEAT_TERMINAL
17694c5d8152SBram Moolenaar else if (term_use_loop())
17704c5d8152SBram Moolenaar idx = MENU_INDEX_TERMINAL;
17714c5d8152SBram Moolenaar #endif
1772071d4279SBram Moolenaar else if (VIsual_active)
1773b3656edcSBram Moolenaar {
1774b3656edcSBram Moolenaar if (VIsual_select)
1775b3656edcSBram Moolenaar idx = MENU_INDEX_SELECT;
1776b3656edcSBram Moolenaar else
1777071d4279SBram Moolenaar idx = MENU_INDEX_VISUAL;
1778b3656edcSBram Moolenaar }
1779071d4279SBram Moolenaar else if (state == HITRETURN || state == ASKMORE)
1780071d4279SBram Moolenaar idx = MENU_INDEX_CMDLINE;
1781071d4279SBram Moolenaar else if (finish_op)
1782071d4279SBram Moolenaar idx = MENU_INDEX_OP_PENDING;
1783071d4279SBram Moolenaar else if ((state & NORMAL))
1784071d4279SBram Moolenaar idx = MENU_INDEX_NORMAL;
1785071d4279SBram Moolenaar else
1786071d4279SBram Moolenaar idx = MENU_INDEX_INVALID;
1787071d4279SBram Moolenaar
1788071d4279SBram Moolenaar if (idx != MENU_INDEX_INVALID && menu->strings[idx] == NULL)
1789071d4279SBram Moolenaar idx = MENU_INDEX_INVALID;
1790071d4279SBram Moolenaar return idx;
1791071d4279SBram Moolenaar }
1792071d4279SBram Moolenaar #endif
1793071d4279SBram Moolenaar
1794071d4279SBram Moolenaar /*
1795071d4279SBram Moolenaar * Duplicate the menu item text and then process to see if a mnemonic key
1796071d4279SBram Moolenaar * and/or accelerator text has been identified.
1797071d4279SBram Moolenaar * Returns a pointer to allocated memory, or NULL for failure.
1798071d4279SBram Moolenaar * If mnemonic != NULL, *mnemonic is set to the character after the first '&'.
1799071d4279SBram Moolenaar * If actext != NULL, *actext is set to the text after the first TAB.
1800071d4279SBram Moolenaar */
1801071d4279SBram Moolenaar static char_u *
menu_text(char_u * str,int * mnemonic,char_u ** actext)180252ea13daSBram Moolenaar menu_text(char_u *str, int *mnemonic, char_u **actext)
1803071d4279SBram Moolenaar {
1804071d4279SBram Moolenaar char_u *p;
1805071d4279SBram Moolenaar char_u *text;
1806071d4279SBram Moolenaar
18074ba37b58SBram Moolenaar // Locate accelerator text, after the first TAB
1808071d4279SBram Moolenaar p = vim_strchr(str, TAB);
1809071d4279SBram Moolenaar if (p != NULL)
1810071d4279SBram Moolenaar {
1811071d4279SBram Moolenaar if (actext != NULL)
1812071d4279SBram Moolenaar *actext = vim_strsave(p + 1);
181371ccd03eSBram Moolenaar text = vim_strnsave(str, p - str);
1814071d4279SBram Moolenaar }
1815071d4279SBram Moolenaar else
1816071d4279SBram Moolenaar text = vim_strsave(str);
1817071d4279SBram Moolenaar
18184ba37b58SBram Moolenaar // Find mnemonic characters "&a" and reduce "&&" to "&".
1819071d4279SBram Moolenaar for (p = text; p != NULL; )
1820071d4279SBram Moolenaar {
1821071d4279SBram Moolenaar p = vim_strchr(p, '&');
1822071d4279SBram Moolenaar if (p != NULL)
1823071d4279SBram Moolenaar {
18244ba37b58SBram Moolenaar if (p[1] == NUL) // trailing "&"
1825071d4279SBram Moolenaar break;
1826071d4279SBram Moolenaar if (mnemonic != NULL && p[1] != '&')
1827071d4279SBram Moolenaar #if !defined(__MVS__) || defined(MOTIF390_MNEMONIC_FIXED)
1828071d4279SBram Moolenaar *mnemonic = p[1];
1829071d4279SBram Moolenaar #else
1830071d4279SBram Moolenaar {
1831071d4279SBram Moolenaar /*
1832071d4279SBram Moolenaar * Well there is a bug in the Motif libraries on OS390 Unix.
1833071d4279SBram Moolenaar * The mnemonic keys needs to be converted to ASCII values
1834071d4279SBram Moolenaar * first.
1835071d4279SBram Moolenaar * This behavior has been seen in 2.8 and 2.9.
1836071d4279SBram Moolenaar */
1837071d4279SBram Moolenaar char c = p[1];
1838071d4279SBram Moolenaar __etoa_l(&c, 1);
1839071d4279SBram Moolenaar *mnemonic = c;
1840071d4279SBram Moolenaar }
1841071d4279SBram Moolenaar #endif
18428c8de839SBram Moolenaar STRMOVE(p, p + 1);
1843071d4279SBram Moolenaar p = p + 1;
1844071d4279SBram Moolenaar }
1845071d4279SBram Moolenaar }
1846071d4279SBram Moolenaar return text;
1847071d4279SBram Moolenaar }
1848071d4279SBram Moolenaar
1849071d4279SBram Moolenaar /*
1850071d4279SBram Moolenaar * Return TRUE if "name" can be a menu in the MenuBar.
1851071d4279SBram Moolenaar */
1852071d4279SBram Moolenaar int
menu_is_menubar(char_u * name)185352ea13daSBram Moolenaar menu_is_menubar(char_u *name)
1854071d4279SBram Moolenaar {
1855071d4279SBram Moolenaar return (!menu_is_popup(name)
1856071d4279SBram Moolenaar && !menu_is_toolbar(name)
1857378daf87SBram Moolenaar && !menu_is_winbar(name)
1858071d4279SBram Moolenaar && *name != MNU_HIDDEN_CHAR);
1859071d4279SBram Moolenaar }
1860071d4279SBram Moolenaar
1861071d4279SBram Moolenaar /*
1862071d4279SBram Moolenaar * Return TRUE if "name" is a popup menu name.
1863071d4279SBram Moolenaar */
1864071d4279SBram Moolenaar int
menu_is_popup(char_u * name)186552ea13daSBram Moolenaar menu_is_popup(char_u *name)
1866071d4279SBram Moolenaar {
1867071d4279SBram Moolenaar return (STRNCMP(name, "PopUp", 5) == 0);
1868071d4279SBram Moolenaar }
1869071d4279SBram Moolenaar
1870071d4279SBram Moolenaar #if (defined(FEAT_GUI_MOTIF) && (XmVersion <= 1002)) || defined(PROTO)
1871071d4279SBram Moolenaar /*
1872071d4279SBram Moolenaar * Return TRUE if "name" is part of a popup menu.
1873071d4279SBram Moolenaar */
1874071d4279SBram Moolenaar int
menu_is_child_of_popup(vimmenu_T * menu)187552ea13daSBram Moolenaar menu_is_child_of_popup(vimmenu_T *menu)
1876071d4279SBram Moolenaar {
1877071d4279SBram Moolenaar while (menu->parent != NULL)
1878071d4279SBram Moolenaar menu = menu->parent;
1879071d4279SBram Moolenaar return menu_is_popup(menu->name);
1880071d4279SBram Moolenaar }
1881071d4279SBram Moolenaar #endif
1882071d4279SBram Moolenaar
1883071d4279SBram Moolenaar /*
1884071d4279SBram Moolenaar * Return TRUE if "name" is a toolbar menu name.
1885071d4279SBram Moolenaar */
1886071d4279SBram Moolenaar int
menu_is_toolbar(char_u * name)188752ea13daSBram Moolenaar menu_is_toolbar(char_u *name)
1888071d4279SBram Moolenaar {
1889071d4279SBram Moolenaar return (STRNCMP(name, "ToolBar", 7) == 0);
1890071d4279SBram Moolenaar }
1891071d4279SBram Moolenaar
1892071d4279SBram Moolenaar /*
1893071d4279SBram Moolenaar * Return TRUE if the name is a menu separator identifier: Starts and ends
1894071d4279SBram Moolenaar * with '-'
1895071d4279SBram Moolenaar */
1896071d4279SBram Moolenaar int
menu_is_separator(char_u * name)189752ea13daSBram Moolenaar menu_is_separator(char_u *name)
1898071d4279SBram Moolenaar {
1899071d4279SBram Moolenaar return (name[0] == '-' && name[STRLEN(name) - 1] == '-');
1900071d4279SBram Moolenaar }
1901071d4279SBram Moolenaar
1902071d4279SBram Moolenaar /*
1903071d4279SBram Moolenaar * Return TRUE if the menu is hidden: Starts with ']'
1904071d4279SBram Moolenaar */
1905071d4279SBram Moolenaar static int
menu_is_hidden(char_u * name)190652ea13daSBram Moolenaar menu_is_hidden(char_u *name)
1907071d4279SBram Moolenaar {
1908071d4279SBram Moolenaar return (name[0] == ']') || (menu_is_popup(name) && name[5] != NUL);
1909071d4279SBram Moolenaar }
1910071d4279SBram Moolenaar
1911071d4279SBram Moolenaar /*
1912071d4279SBram Moolenaar * Return TRUE if the menu is the tearoff menu.
1913071d4279SBram Moolenaar */
1914071d4279SBram Moolenaar static int
menu_is_tearoff(char_u * name UNUSED)191552ea13daSBram Moolenaar menu_is_tearoff(char_u *name UNUSED)
1916071d4279SBram Moolenaar {
1917071d4279SBram Moolenaar #ifdef FEAT_GUI
1918071d4279SBram Moolenaar return (STRCMP(name, TEAR_STRING) == 0);
1919071d4279SBram Moolenaar #else
1920071d4279SBram Moolenaar return FALSE;
1921071d4279SBram Moolenaar #endif
1922071d4279SBram Moolenaar }
1923071d4279SBram Moolenaar
1924aef8c3daSBram Moolenaar #if defined(FEAT_GUI) || defined(FEAT_TERM_POPUP_MENU) || defined(PROTO)
1925071d4279SBram Moolenaar
1926071d4279SBram Moolenaar static int
get_menu_mode(void)192752ea13daSBram Moolenaar get_menu_mode(void)
1928071d4279SBram Moolenaar {
19294c5d8152SBram Moolenaar #ifdef FEAT_TERMINAL
19304c5d8152SBram Moolenaar if (term_use_loop())
19314c5d8152SBram Moolenaar return MENU_INDEX_TERMINAL;
19324c5d8152SBram Moolenaar #endif
1933071d4279SBram Moolenaar if (VIsual_active)
1934b3656edcSBram Moolenaar {
1935b3656edcSBram Moolenaar if (VIsual_select)
1936b3656edcSBram Moolenaar return MENU_INDEX_SELECT;
1937071d4279SBram Moolenaar return MENU_INDEX_VISUAL;
1938b3656edcSBram Moolenaar }
1939071d4279SBram Moolenaar if (State & INSERT)
1940071d4279SBram Moolenaar return MENU_INDEX_INSERT;
1941071d4279SBram Moolenaar if ((State & CMDLINE) || State == ASKMORE || State == HITRETURN)
1942071d4279SBram Moolenaar return MENU_INDEX_CMDLINE;
1943071d4279SBram Moolenaar if (finish_op)
1944071d4279SBram Moolenaar return MENU_INDEX_OP_PENDING;
1945071d4279SBram Moolenaar if (State & NORMAL)
1946071d4279SBram Moolenaar return MENU_INDEX_NORMAL;
19474ba37b58SBram Moolenaar if (State & LANGMAP) // must be a "r" command, like Insert mode
1948071d4279SBram Moolenaar return MENU_INDEX_INSERT;
1949071d4279SBram Moolenaar return MENU_INDEX_INVALID;
1950071d4279SBram Moolenaar }
1951071d4279SBram Moolenaar
195229a2c08dSBram Moolenaar int
get_menu_mode_flag(void)195329a2c08dSBram Moolenaar get_menu_mode_flag(void)
195429a2c08dSBram Moolenaar {
195529a2c08dSBram Moolenaar int mode = get_menu_mode();
195629a2c08dSBram Moolenaar
195729a2c08dSBram Moolenaar if (mode == MENU_INDEX_INVALID)
195829a2c08dSBram Moolenaar return 0;
195929a2c08dSBram Moolenaar return 1 << mode;
196029a2c08dSBram Moolenaar }
196129a2c08dSBram Moolenaar
1962071d4279SBram Moolenaar /*
1963aef8c3daSBram Moolenaar * Display the Special "PopUp" menu as a pop-up at the current mouse
1964aef8c3daSBram Moolenaar * position. The "PopUpn" menu is for Normal mode, "PopUpi" for Insert mode,
1965aef8c3daSBram Moolenaar * etc.
1966aef8c3daSBram Moolenaar */
1967aef8c3daSBram Moolenaar void
show_popupmenu(void)1968aef8c3daSBram Moolenaar show_popupmenu(void)
1969aef8c3daSBram Moolenaar {
1970aef8c3daSBram Moolenaar vimmenu_T *menu;
19714c5d8152SBram Moolenaar int menu_mode;
19724c5d8152SBram Moolenaar char* mode;
19734c5d8152SBram Moolenaar int mode_len;
1974aef8c3daSBram Moolenaar
19754c5d8152SBram Moolenaar menu_mode = get_menu_mode();
19764c5d8152SBram Moolenaar if (menu_mode == MENU_INDEX_INVALID)
1977aef8c3daSBram Moolenaar return;
19784c5d8152SBram Moolenaar mode = menu_mode_chars[menu_mode];
19794c5d8152SBram Moolenaar mode_len = (int)strlen(mode);
1980aef8c3daSBram Moolenaar
19814c5d8152SBram Moolenaar apply_autocmds(EVENT_MENUPOPUP, (char_u*)mode, NULL, FALSE, curbuf);
1982aef8c3daSBram Moolenaar
198300d253e2SBram Moolenaar FOR_ALL_MENUS(menu)
19844c5d8152SBram Moolenaar if (STRNCMP("PopUp", menu->name, 5) == 0 && STRNCMP(menu->name + 5, mode, mode_len) == 0)
1985aef8c3daSBram Moolenaar break;
1986aef8c3daSBram Moolenaar
19874ba37b58SBram Moolenaar // Only show a popup when it is defined and has entries
1988aef8c3daSBram Moolenaar if (menu != NULL && menu->children != NULL)
1989aef8c3daSBram Moolenaar {
1990aef8c3daSBram Moolenaar # if defined(FEAT_GUI)
1991aef8c3daSBram Moolenaar if (gui.in_use)
1992aef8c3daSBram Moolenaar {
19934ba37b58SBram Moolenaar // Update the menus now, in case the MenuPopup autocommand did
19944ba37b58SBram Moolenaar // anything.
1995aef8c3daSBram Moolenaar gui_update_menus(0);
1996aef8c3daSBram Moolenaar gui_mch_show_popupmenu(menu);
1997aef8c3daSBram Moolenaar }
1998aef8c3daSBram Moolenaar # endif
1999aef8c3daSBram Moolenaar # if defined(FEAT_GUI) && defined(FEAT_TERM_POPUP_MENU)
2000aef8c3daSBram Moolenaar else
2001aef8c3daSBram Moolenaar # endif
2002aef8c3daSBram Moolenaar # if defined(FEAT_TERM_POPUP_MENU)
2003aef8c3daSBram Moolenaar pum_show_popupmenu(menu);
2004aef8c3daSBram Moolenaar # endif
2005aef8c3daSBram Moolenaar }
2006aef8c3daSBram Moolenaar }
2007aef8c3daSBram Moolenaar #endif
2008aef8c3daSBram Moolenaar
2009aef8c3daSBram Moolenaar #if defined(FEAT_GUI) || defined(PROTO)
2010aef8c3daSBram Moolenaar
2011aef8c3daSBram Moolenaar /*
2012968bbbe4SBram Moolenaar * Check that a pointer appears in the menu tree. Used to protect from using
2013968bbbe4SBram Moolenaar * a menu that was deleted after it was selected but before the event was
2014968bbbe4SBram Moolenaar * handled.
2015968bbbe4SBram Moolenaar * Return OK or FAIL. Used recursively.
2016968bbbe4SBram Moolenaar */
2017968bbbe4SBram Moolenaar int
check_menu_pointer(vimmenu_T * root,vimmenu_T * menu_to_check)201852ea13daSBram Moolenaar check_menu_pointer(vimmenu_T *root, vimmenu_T *menu_to_check)
2019968bbbe4SBram Moolenaar {
2020968bbbe4SBram Moolenaar vimmenu_T *p;
2021968bbbe4SBram Moolenaar
2022968bbbe4SBram Moolenaar for (p = root; p != NULL; p = p->next)
2023968bbbe4SBram Moolenaar if (p == menu_to_check
2024968bbbe4SBram Moolenaar || (p->children != NULL
2025968bbbe4SBram Moolenaar && check_menu_pointer(p->children, menu_to_check) == OK))
2026968bbbe4SBram Moolenaar return OK;
2027968bbbe4SBram Moolenaar return FAIL;
2028968bbbe4SBram Moolenaar }
2029968bbbe4SBram Moolenaar
2030968bbbe4SBram Moolenaar /*
2031071d4279SBram Moolenaar * After we have started the GUI, then we can create any menus that have been
2032071d4279SBram Moolenaar * defined. This is done once here. add_menu_path() may have already been
2033071d4279SBram Moolenaar * called to define these menus, and may be called again. This function calls
2034071d4279SBram Moolenaar * itself recursively. Should be called at the top level with:
2035a06ecab7SBram Moolenaar * gui_create_initial_menus(root_menu);
2036071d4279SBram Moolenaar */
2037071d4279SBram Moolenaar void
gui_create_initial_menus(vimmenu_T * menu)203852ea13daSBram Moolenaar gui_create_initial_menus(vimmenu_T *menu)
2039071d4279SBram Moolenaar {
2040071d4279SBram Moolenaar int idx = 0;
2041071d4279SBram Moolenaar
2042071d4279SBram Moolenaar while (menu != NULL)
2043071d4279SBram Moolenaar {
20444ba37b58SBram Moolenaar // Don't add a menu when only a tip was defined.
2045071d4279SBram Moolenaar if (menu->modes & MENU_ALL_MODES)
2046071d4279SBram Moolenaar {
2047071d4279SBram Moolenaar if (menu->children != NULL)
2048071d4279SBram Moolenaar {
2049071d4279SBram Moolenaar gui_mch_add_menu(menu, idx);
2050071d4279SBram Moolenaar gui_create_initial_menus(menu->children);
2051071d4279SBram Moolenaar }
2052071d4279SBram Moolenaar else
2053071d4279SBram Moolenaar gui_mch_add_menu_item(menu, idx);
2054071d4279SBram Moolenaar }
2055071d4279SBram Moolenaar menu = menu->next;
2056071d4279SBram Moolenaar ++idx;
2057071d4279SBram Moolenaar }
2058071d4279SBram Moolenaar }
2059071d4279SBram Moolenaar
2060071d4279SBram Moolenaar /*
2061071d4279SBram Moolenaar * Used recursively by gui_update_menus (see below)
2062071d4279SBram Moolenaar */
2063071d4279SBram Moolenaar static void
gui_update_menus_recurse(vimmenu_T * menu,int mode)206452ea13daSBram Moolenaar gui_update_menus_recurse(vimmenu_T *menu, int mode)
2065071d4279SBram Moolenaar {
2066071d4279SBram Moolenaar int grey;
2067071d4279SBram Moolenaar
2068071d4279SBram Moolenaar while (menu)
2069071d4279SBram Moolenaar {
2070071d4279SBram Moolenaar if ((menu->modes & menu->enabled & mode)
20714f97475dSBram Moolenaar # if defined(FEAT_GUI_MSWIN) && defined(FEAT_TEAROFF)
2072071d4279SBram Moolenaar || menu_is_tearoff(menu->dname)
2073071d4279SBram Moolenaar # endif
2074071d4279SBram Moolenaar )
2075071d4279SBram Moolenaar grey = FALSE;
2076071d4279SBram Moolenaar else
2077071d4279SBram Moolenaar grey = TRUE;
2078071d4279SBram Moolenaar # ifdef FEAT_GUI_ATHENA
20794ba37b58SBram Moolenaar // Hiding menus doesn't work for Athena, it can cause a crash.
2080071d4279SBram Moolenaar gui_mch_menu_grey(menu, grey);
2081071d4279SBram Moolenaar # else
20824ba37b58SBram Moolenaar // Never hide a toplevel menu, it may make the menubar resize or
20834ba37b58SBram Moolenaar // disappear. Same problem for ToolBar items.
2084071d4279SBram Moolenaar if (vim_strchr(p_go, GO_GREY) != NULL || menu->parent == NULL
2085071d4279SBram Moolenaar # ifdef FEAT_TOOLBAR
2086071d4279SBram Moolenaar || menu_is_toolbar(menu->parent->name)
2087071d4279SBram Moolenaar # endif
2088071d4279SBram Moolenaar )
2089071d4279SBram Moolenaar gui_mch_menu_grey(menu, grey);
2090071d4279SBram Moolenaar else
2091071d4279SBram Moolenaar gui_mch_menu_hidden(menu, grey);
2092071d4279SBram Moolenaar # endif
2093071d4279SBram Moolenaar gui_update_menus_recurse(menu->children, mode);
2094071d4279SBram Moolenaar menu = menu->next;
2095071d4279SBram Moolenaar }
2096071d4279SBram Moolenaar }
2097071d4279SBram Moolenaar
2098071d4279SBram Moolenaar /*
2099071d4279SBram Moolenaar * Make sure only the valid menu items appear for this mode. If
2100071d4279SBram Moolenaar * force_menu_update is not TRUE, then we only do this if the mode has changed
2101071d4279SBram Moolenaar * since last time. If "modes" is not 0, then we use these modes instead.
2102071d4279SBram Moolenaar */
2103071d4279SBram Moolenaar void
gui_update_menus(int modes)210452ea13daSBram Moolenaar gui_update_menus(int modes)
2105071d4279SBram Moolenaar {
2106071d4279SBram Moolenaar static int prev_mode = -1;
2107071d4279SBram Moolenaar int mode = 0;
2108071d4279SBram Moolenaar
2109071d4279SBram Moolenaar if (modes != 0x0)
2110071d4279SBram Moolenaar mode = modes;
2111071d4279SBram Moolenaar else
211229a2c08dSBram Moolenaar mode = get_menu_mode_flag();
2113071d4279SBram Moolenaar
2114071d4279SBram Moolenaar if (force_menu_update || mode != prev_mode)
2115071d4279SBram Moolenaar {
2116071d4279SBram Moolenaar gui_update_menus_recurse(root_menu, mode);
2117071d4279SBram Moolenaar gui_mch_draw_menubar();
2118071d4279SBram Moolenaar prev_mode = mode;
2119071d4279SBram Moolenaar force_menu_update = FALSE;
2120071d4279SBram Moolenaar }
2121071d4279SBram Moolenaar }
2122071d4279SBram Moolenaar
2123241a8aaaSBram Moolenaar # if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_MOTIF) \
2124241a8aaaSBram Moolenaar || defined(FEAT_GUI_GTK) || defined(FEAT_GUI_PHOTON) || defined(PROTO)
2125071d4279SBram Moolenaar /*
2126071d4279SBram Moolenaar * Check if a key is used as a mnemonic for a toplevel menu.
2127071d4279SBram Moolenaar * Case of the key is ignored.
2128071d4279SBram Moolenaar */
2129071d4279SBram Moolenaar int
gui_is_menu_shortcut(int key)213052ea13daSBram Moolenaar gui_is_menu_shortcut(int key)
2131071d4279SBram Moolenaar {
2132071d4279SBram Moolenaar vimmenu_T *menu;
2133071d4279SBram Moolenaar
2134071d4279SBram Moolenaar if (key < 256)
2135071d4279SBram Moolenaar key = TOLOWER_LOC(key);
213600d253e2SBram Moolenaar FOR_ALL_MENUS(menu)
2137071d4279SBram Moolenaar if (menu->mnemonic == key
2138071d4279SBram Moolenaar || (menu->mnemonic < 256 && TOLOWER_LOC(menu->mnemonic) == key))
2139071d4279SBram Moolenaar return TRUE;
2140071d4279SBram Moolenaar return FALSE;
2141071d4279SBram Moolenaar }
2142071d4279SBram Moolenaar # endif
21434ba37b58SBram Moolenaar #endif // FEAT_GUI
2144071d4279SBram Moolenaar
21454f97475dSBram Moolenaar #if (defined(FEAT_GUI_MSWIN) && defined(FEAT_TEAROFF)) || defined(PROTO)
2146071d4279SBram Moolenaar
2147071d4279SBram Moolenaar /*
2148071d4279SBram Moolenaar * Deal with tearoff items that are added like a menu item.
2149071d4279SBram Moolenaar * Currently only for Win32 GUI. Others may follow later.
2150071d4279SBram Moolenaar */
2151071d4279SBram Moolenaar
2152071d4279SBram Moolenaar void
gui_mch_toggle_tearoffs(int enable)2153071d4279SBram Moolenaar gui_mch_toggle_tearoffs(int enable)
2154071d4279SBram Moolenaar {
2155071d4279SBram Moolenaar int pri_tab[MENUDEPTH + 1];
2156071d4279SBram Moolenaar int i;
2157071d4279SBram Moolenaar
2158071d4279SBram Moolenaar if (enable)
2159071d4279SBram Moolenaar {
2160071d4279SBram Moolenaar for (i = 0; i < MENUDEPTH; ++i)
2161071d4279SBram Moolenaar pri_tab[i] = 500;
2162071d4279SBram Moolenaar pri_tab[MENUDEPTH] = -1;
2163071d4279SBram Moolenaar gui_create_tearoffs_recurse(root_menu, (char_u *)"", pri_tab, 0);
2164071d4279SBram Moolenaar }
2165071d4279SBram Moolenaar else
2166071d4279SBram Moolenaar gui_destroy_tearoffs_recurse(root_menu);
2167071d4279SBram Moolenaar s_tearoffs = enable;
2168071d4279SBram Moolenaar }
2169071d4279SBram Moolenaar
2170071d4279SBram Moolenaar /*
2171071d4279SBram Moolenaar * Recursively add tearoff items
2172071d4279SBram Moolenaar */
2173071d4279SBram Moolenaar static void
gui_create_tearoffs_recurse(vimmenu_T * menu,const char_u * pname,int * pri_tab,int pri_idx)217452ea13daSBram Moolenaar gui_create_tearoffs_recurse(
217552ea13daSBram Moolenaar vimmenu_T *menu,
217652ea13daSBram Moolenaar const char_u *pname,
217752ea13daSBram Moolenaar int *pri_tab,
217852ea13daSBram Moolenaar int pri_idx)
2179071d4279SBram Moolenaar {
2180071d4279SBram Moolenaar char_u *newpname = NULL;
2181071d4279SBram Moolenaar int len;
2182071d4279SBram Moolenaar char_u *s;
2183071d4279SBram Moolenaar char_u *d;
2184071d4279SBram Moolenaar
2185071d4279SBram Moolenaar if (pri_tab[pri_idx + 1] != -1)
2186071d4279SBram Moolenaar ++pri_idx;
2187071d4279SBram Moolenaar while (menu != NULL)
2188071d4279SBram Moolenaar {
2189071d4279SBram Moolenaar if (menu->children != NULL && menu_is_menubar(menu->name))
2190071d4279SBram Moolenaar {
21914ba37b58SBram Moolenaar // Add the menu name to the menu path. Insert a backslash before
21924ba37b58SBram Moolenaar // dots (it's used to separate menu names).
2193071d4279SBram Moolenaar len = (int)STRLEN(pname) + (int)STRLEN(menu->name);
2194071d4279SBram Moolenaar for (s = menu->name; *s; ++s)
2195071d4279SBram Moolenaar if (*s == '.' || *s == '\\')
2196071d4279SBram Moolenaar ++len;
2197071d4279SBram Moolenaar newpname = alloc(len + TEAR_LEN + 2);
2198071d4279SBram Moolenaar if (newpname != NULL)
2199071d4279SBram Moolenaar {
2200071d4279SBram Moolenaar STRCPY(newpname, pname);
2201071d4279SBram Moolenaar d = newpname + STRLEN(newpname);
2202071d4279SBram Moolenaar for (s = menu->name; *s; ++s)
2203071d4279SBram Moolenaar {
2204071d4279SBram Moolenaar if (*s == '.' || *s == '\\')
2205071d4279SBram Moolenaar *d++ = '\\';
2206071d4279SBram Moolenaar *d++ = *s;
2207071d4279SBram Moolenaar }
2208071d4279SBram Moolenaar *d = NUL;
2209071d4279SBram Moolenaar
22104ba37b58SBram Moolenaar // check if tearoff already exists
2211071d4279SBram Moolenaar if (STRCMP(menu->children->name, TEAR_STRING) != 0)
2212071d4279SBram Moolenaar {
2213071d4279SBram Moolenaar gui_add_tearoff(newpname, pri_tab, pri_idx - 1);
22144ba37b58SBram Moolenaar *d = NUL; // remove TEAR_STRING
2215071d4279SBram Moolenaar }
2216071d4279SBram Moolenaar
2217071d4279SBram Moolenaar STRCAT(newpname, ".");
2218071d4279SBram Moolenaar gui_create_tearoffs_recurse(menu->children, newpname,
2219071d4279SBram Moolenaar pri_tab, pri_idx);
2220071d4279SBram Moolenaar vim_free(newpname);
2221071d4279SBram Moolenaar }
2222071d4279SBram Moolenaar }
2223071d4279SBram Moolenaar menu = menu->next;
2224071d4279SBram Moolenaar }
2225071d4279SBram Moolenaar }
2226071d4279SBram Moolenaar
2227071d4279SBram Moolenaar /*
2228071d4279SBram Moolenaar * Add tear-off menu item for a submenu.
2229071d4279SBram Moolenaar * "tearpath" is the menu path, and must have room to add TEAR_STRING.
2230071d4279SBram Moolenaar */
2231071d4279SBram Moolenaar static void
gui_add_tearoff(char_u * tearpath,int * pri_tab,int pri_idx)223252ea13daSBram Moolenaar gui_add_tearoff(char_u *tearpath, int *pri_tab, int pri_idx)
2233071d4279SBram Moolenaar {
2234071d4279SBram Moolenaar char_u *tbuf;
2235071d4279SBram Moolenaar int t;
2236071d4279SBram Moolenaar vimmenu_T menuarg;
2237071d4279SBram Moolenaar
2238071d4279SBram Moolenaar tbuf = alloc(5 + (unsigned int)STRLEN(tearpath));
2239071d4279SBram Moolenaar if (tbuf != NULL)
2240071d4279SBram Moolenaar {
2241071d4279SBram Moolenaar tbuf[0] = K_SPECIAL;
2242071d4279SBram Moolenaar tbuf[1] = K_SECOND(K_TEAROFF);
2243071d4279SBram Moolenaar tbuf[2] = K_THIRD(K_TEAROFF);
2244071d4279SBram Moolenaar STRCPY(tbuf + 3, tearpath);
2245071d4279SBram Moolenaar STRCAT(tbuf + 3, "\r");
2246071d4279SBram Moolenaar
2247071d4279SBram Moolenaar STRCAT(tearpath, ".");
2248071d4279SBram Moolenaar STRCAT(tearpath, TEAR_STRING);
2249071d4279SBram Moolenaar
22504ba37b58SBram Moolenaar // Priority of tear-off is always 1
2251071d4279SBram Moolenaar t = pri_tab[pri_idx + 1];
2252071d4279SBram Moolenaar pri_tab[pri_idx + 1] = 1;
2253071d4279SBram Moolenaar
2254071d4279SBram Moolenaar #ifdef FEAT_TOOLBAR
2255071d4279SBram Moolenaar menuarg.iconfile = NULL;
2256071d4279SBram Moolenaar menuarg.iconidx = -1;
2257071d4279SBram Moolenaar menuarg.icon_builtin = FALSE;
2258071d4279SBram Moolenaar #endif
2259071d4279SBram Moolenaar menuarg.noremap[0] = REMAP_NONE;
2260071d4279SBram Moolenaar menuarg.silent[0] = TRUE;
2261071d4279SBram Moolenaar
2262071d4279SBram Moolenaar menuarg.modes = MENU_ALL_MODES;
2263071d4279SBram Moolenaar add_menu_path(tearpath, &menuarg, pri_tab, tbuf, FALSE);
2264071d4279SBram Moolenaar
2265071d4279SBram Moolenaar menuarg.modes = MENU_TIP_MODE;
2266071d4279SBram Moolenaar add_menu_path(tearpath, &menuarg, pri_tab,
2267071d4279SBram Moolenaar (char_u *)_("Tear off this menu"), FALSE);
2268071d4279SBram Moolenaar
2269071d4279SBram Moolenaar pri_tab[pri_idx + 1] = t;
2270071d4279SBram Moolenaar vim_free(tbuf);
2271071d4279SBram Moolenaar }
2272071d4279SBram Moolenaar }
2273071d4279SBram Moolenaar
2274071d4279SBram Moolenaar /*
2275071d4279SBram Moolenaar * Recursively destroy tearoff items
2276071d4279SBram Moolenaar */
2277071d4279SBram Moolenaar static void
gui_destroy_tearoffs_recurse(vimmenu_T * menu)227852ea13daSBram Moolenaar gui_destroy_tearoffs_recurse(vimmenu_T *menu)
2279071d4279SBram Moolenaar {
2280071d4279SBram Moolenaar while (menu)
2281071d4279SBram Moolenaar {
2282071d4279SBram Moolenaar if (menu->children)
2283071d4279SBram Moolenaar {
22844ba37b58SBram Moolenaar // check if tearoff exists
2285071d4279SBram Moolenaar if (STRCMP(menu->children->name, TEAR_STRING) == 0)
2286071d4279SBram Moolenaar {
22874ba37b58SBram Moolenaar // Disconnect the item and free the memory
2288071d4279SBram Moolenaar free_menu(&menu->children);
2289071d4279SBram Moolenaar }
22904ba37b58SBram Moolenaar if (menu->children != NULL) // if not the last one
2291071d4279SBram Moolenaar gui_destroy_tearoffs_recurse(menu->children);
2292071d4279SBram Moolenaar }
2293071d4279SBram Moolenaar menu = menu->next;
2294071d4279SBram Moolenaar }
2295071d4279SBram Moolenaar }
2296071d4279SBram Moolenaar
22974ba37b58SBram Moolenaar #endif // FEAT_GUI_MSWIN && FEAT_TEAROFF
2298071d4279SBram Moolenaar
2299071d4279SBram Moolenaar /*
23001b9645deSBram Moolenaar * Execute "menu". Use by ":emenu" and the window toolbar.
23011b9645deSBram Moolenaar * "eap" is NULL for the window toolbar.
23024c5d8152SBram Moolenaar * "mode_idx" specifies a MENU_INDEX_ value, use -1 to depend on the current
23034c5d8152SBram Moolenaar * state.
2304071d4279SBram Moolenaar */
2305aef8c3daSBram Moolenaar void
execute_menu(exarg_T * eap,vimmenu_T * menu,int mode_idx)23064c5d8152SBram Moolenaar execute_menu(exarg_T *eap, vimmenu_T *menu, int mode_idx)
2307071d4279SBram Moolenaar {
23084c5d8152SBram Moolenaar int idx = mode_idx;
2309071d4279SBram Moolenaar
23104c5d8152SBram Moolenaar if (idx < 0)
23114c5d8152SBram Moolenaar {
23124ba37b58SBram Moolenaar // Use the Insert mode entry when returning to Insert mode.
23139b8d6226SBram Moolenaar if (restart_edit && !current_sctx.sc_sid)
2314071d4279SBram Moolenaar {
2315071d4279SBram Moolenaar idx = MENU_INDEX_INSERT;
2316071d4279SBram Moolenaar }
23174c5d8152SBram Moolenaar #ifdef FEAT_TERMINAL
23184c5d8152SBram Moolenaar else if (term_use_loop())
23194c5d8152SBram Moolenaar {
23204c5d8152SBram Moolenaar idx = MENU_INDEX_TERMINAL;
23214c5d8152SBram Moolenaar }
23224c5d8152SBram Moolenaar #endif
23231b9645deSBram Moolenaar else if (VIsual_active)
23241b9645deSBram Moolenaar {
23251b9645deSBram Moolenaar idx = MENU_INDEX_VISUAL;
23261b9645deSBram Moolenaar }
23271b9645deSBram Moolenaar else if (eap != NULL && eap->addr_count)
2328071d4279SBram Moolenaar {
2329071d4279SBram Moolenaar pos_T tpos;
2330071d4279SBram Moolenaar
2331071d4279SBram Moolenaar idx = MENU_INDEX_VISUAL;
2332071d4279SBram Moolenaar
23334ba37b58SBram Moolenaar // GEDDES: This is not perfect - but it is a
23344ba37b58SBram Moolenaar // quick way of detecting whether we are doing this from a
23354ba37b58SBram Moolenaar // selection - see if the range matches up with the visual
23364ba37b58SBram Moolenaar // select start and end.
2337eddf53b0SBram Moolenaar if ((curbuf->b_visual.vi_start.lnum == eap->line1)
2338eddf53b0SBram Moolenaar && (curbuf->b_visual.vi_end.lnum) == eap->line2)
2339071d4279SBram Moolenaar {
23404ba37b58SBram Moolenaar // Set it up for visual mode - equivalent to gv.
2341eddf53b0SBram Moolenaar VIsual_mode = curbuf->b_visual.vi_mode;
2342eddf53b0SBram Moolenaar tpos = curbuf->b_visual.vi_end;
2343eddf53b0SBram Moolenaar curwin->w_cursor = curbuf->b_visual.vi_start;
2344eddf53b0SBram Moolenaar curwin->w_curswant = curbuf->b_visual.vi_curswant;
2345071d4279SBram Moolenaar }
2346071d4279SBram Moolenaar else
2347071d4279SBram Moolenaar {
23484ba37b58SBram Moolenaar // Set it up for line-wise visual mode
2349071d4279SBram Moolenaar VIsual_mode = 'V';
2350071d4279SBram Moolenaar curwin->w_cursor.lnum = eap->line1;
2351071d4279SBram Moolenaar curwin->w_cursor.col = 1;
2352071d4279SBram Moolenaar tpos.lnum = eap->line2;
2353071d4279SBram Moolenaar tpos.col = MAXCOL;
2354261bfeabSBram Moolenaar tpos.coladd = 0;
2355071d4279SBram Moolenaar }
2356071d4279SBram Moolenaar
23574ba37b58SBram Moolenaar // Activate visual mode
2358071d4279SBram Moolenaar VIsual_active = TRUE;
2359071d4279SBram Moolenaar VIsual_reselect = TRUE;
2360071d4279SBram Moolenaar check_cursor();
2361071d4279SBram Moolenaar VIsual = curwin->w_cursor;
2362071d4279SBram Moolenaar curwin->w_cursor = tpos;
2363071d4279SBram Moolenaar
2364071d4279SBram Moolenaar check_cursor();
2365071d4279SBram Moolenaar
23664ba37b58SBram Moolenaar // Adjust the cursor to make sure it is in the correct pos
23674ba37b58SBram Moolenaar // for exclusive mode
2368071d4279SBram Moolenaar if (*p_sel == 'e' && gchar_cursor() != NUL)
2369071d4279SBram Moolenaar ++curwin->w_cursor.col;
2370071d4279SBram Moolenaar }
23714c5d8152SBram Moolenaar }
2372a21a6a9aSBram Moolenaar
23734ba37b58SBram Moolenaar // For the WinBar menu always use the Normal mode menu.
2374a21a6a9aSBram Moolenaar if (idx == -1 || eap == NULL)
2375071d4279SBram Moolenaar idx = MENU_INDEX_NORMAL;
2376071d4279SBram Moolenaar
2377ce79353aSBram Moolenaar if (idx != MENU_INDEX_INVALID && menu->strings[idx] != NULL
2378ce79353aSBram Moolenaar && (menu->modes & (1 << idx)))
2379071d4279SBram Moolenaar {
23804ba37b58SBram Moolenaar // When executing a script or function execute the commands right now.
23814ba37b58SBram Moolenaar // Also for the window toolbar.
23824ba37b58SBram Moolenaar // Otherwise put them in the typeahead buffer.
23839b8d6226SBram Moolenaar if (eap == NULL || current_sctx.sc_sid != 0)
2384a21a6a9aSBram Moolenaar {
2385a21a6a9aSBram Moolenaar save_state_T save_state;
2386a21a6a9aSBram Moolenaar
2387a21a6a9aSBram Moolenaar ++ex_normal_busy;
2388a21a6a9aSBram Moolenaar if (save_current_state(&save_state))
2389293ee4d4SBram Moolenaar exec_normal_cmd(menu->strings[idx], menu->noremap[idx],
2390293ee4d4SBram Moolenaar menu->silent[idx]);
2391a21a6a9aSBram Moolenaar restore_current_state(&save_state);
2392a21a6a9aSBram Moolenaar --ex_normal_busy;
2393a21a6a9aSBram Moolenaar }
2394293ee4d4SBram Moolenaar else
2395071d4279SBram Moolenaar ins_typebuf(menu->strings[idx], menu->noremap[idx], 0,
2396071d4279SBram Moolenaar TRUE, menu->silent[idx]);
2397071d4279SBram Moolenaar }
23981b9645deSBram Moolenaar else if (eap != NULL)
23994c5d8152SBram Moolenaar {
24004c5d8152SBram Moolenaar char_u *mode;
24014c5d8152SBram Moolenaar
24024c5d8152SBram Moolenaar switch (idx)
24034c5d8152SBram Moolenaar {
24044c5d8152SBram Moolenaar case MENU_INDEX_VISUAL:
24054c5d8152SBram Moolenaar mode = (char_u *)"Visual";
24064c5d8152SBram Moolenaar break;
24074c5d8152SBram Moolenaar case MENU_INDEX_SELECT:
24084c5d8152SBram Moolenaar mode = (char_u *)"Select";
24094c5d8152SBram Moolenaar break;
24104c5d8152SBram Moolenaar case MENU_INDEX_OP_PENDING:
24114c5d8152SBram Moolenaar mode = (char_u *)"Op-pending";
24124c5d8152SBram Moolenaar break;
24134c5d8152SBram Moolenaar case MENU_INDEX_TERMINAL:
24144c5d8152SBram Moolenaar mode = (char_u *)"Terminal";
24154c5d8152SBram Moolenaar break;
24164c5d8152SBram Moolenaar case MENU_INDEX_INSERT:
24174c5d8152SBram Moolenaar mode = (char_u *)"Insert";
24184c5d8152SBram Moolenaar break;
24194c5d8152SBram Moolenaar case MENU_INDEX_CMDLINE:
24204c5d8152SBram Moolenaar mode = (char_u *)"Cmdline";
24214c5d8152SBram Moolenaar break;
24224c5d8152SBram Moolenaar // case MENU_INDEX_TIP: cannot happen
24234c5d8152SBram Moolenaar default:
24244c5d8152SBram Moolenaar mode = (char_u *)"Normal";
24254c5d8152SBram Moolenaar }
2426f9e3e09fSBram Moolenaar semsg(_("E335: Menu not defined for %s mode"), mode);
2427071d4279SBram Moolenaar }
24284c5d8152SBram Moolenaar }
2429071d4279SBram Moolenaar
24301b9645deSBram Moolenaar /*
24310eabd4dcSBram Moolenaar * Lookup a menu by the descriptor name e.g. "File.New"
24320eabd4dcSBram Moolenaar * Returns NULL if the menu is not found
24331b9645deSBram Moolenaar */
24340eabd4dcSBram Moolenaar static vimmenu_T *
menu_getbyname(char_u * name_arg)24350eabd4dcSBram Moolenaar menu_getbyname(char_u *name_arg)
24361b9645deSBram Moolenaar {
24371b9645deSBram Moolenaar char_u *name;
24381b9645deSBram Moolenaar char_u *saved_name;
24390eabd4dcSBram Moolenaar vimmenu_T *menu;
24401b9645deSBram Moolenaar char_u *p;
24414c5d8152SBram Moolenaar int gave_emsg = FALSE;
24421b9645deSBram Moolenaar
24430eabd4dcSBram Moolenaar saved_name = vim_strsave(name_arg);
24441b9645deSBram Moolenaar if (saved_name == NULL)
24450eabd4dcSBram Moolenaar return NULL;
24461b9645deSBram Moolenaar
24471b9645deSBram Moolenaar menu = *get_root_menu(saved_name);
24481b9645deSBram Moolenaar name = saved_name;
24491b9645deSBram Moolenaar while (*name)
24501b9645deSBram Moolenaar {
24514ba37b58SBram Moolenaar // Find in the menu hierarchy
24521b9645deSBram Moolenaar p = menu_name_skip(name);
24531b9645deSBram Moolenaar
24541b9645deSBram Moolenaar while (menu != NULL)
24551b9645deSBram Moolenaar {
24561b9645deSBram Moolenaar if (menu_name_equal(name, menu))
24571b9645deSBram Moolenaar {
24581b9645deSBram Moolenaar if (*p == NUL && menu->children != NULL)
24591b9645deSBram Moolenaar {
2460f9e3e09fSBram Moolenaar emsg(_("E333: Menu path must lead to a menu item"));
24614c5d8152SBram Moolenaar gave_emsg = TRUE;
24621b9645deSBram Moolenaar menu = NULL;
24631b9645deSBram Moolenaar }
24641b9645deSBram Moolenaar else if (*p != NUL && menu->children == NULL)
24651b9645deSBram Moolenaar {
2466f9e3e09fSBram Moolenaar emsg(_(e_notsubmenu));
24671b9645deSBram Moolenaar menu = NULL;
24681b9645deSBram Moolenaar }
24691b9645deSBram Moolenaar break;
24701b9645deSBram Moolenaar }
24711b9645deSBram Moolenaar menu = menu->next;
24721b9645deSBram Moolenaar }
24731b9645deSBram Moolenaar if (menu == NULL || *p == NUL)
24741b9645deSBram Moolenaar break;
24751b9645deSBram Moolenaar menu = menu->children;
24761b9645deSBram Moolenaar name = p;
24771b9645deSBram Moolenaar }
24781b9645deSBram Moolenaar vim_free(saved_name);
24791b9645deSBram Moolenaar if (menu == NULL)
24801b9645deSBram Moolenaar {
24814c5d8152SBram Moolenaar if (!gave_emsg)
24820eabd4dcSBram Moolenaar semsg(_("E334: Menu not found: %s"), name_arg);
24830eabd4dcSBram Moolenaar return NULL;
24840eabd4dcSBram Moolenaar }
24850eabd4dcSBram Moolenaar
24860eabd4dcSBram Moolenaar return menu;
24870eabd4dcSBram Moolenaar }
24880eabd4dcSBram Moolenaar
24890eabd4dcSBram Moolenaar /*
24900eabd4dcSBram Moolenaar * Given a menu descriptor, e.g. "File.New", find it in the menu hierarchy and
24910eabd4dcSBram Moolenaar * execute it.
24920eabd4dcSBram Moolenaar */
24930eabd4dcSBram Moolenaar void
ex_emenu(exarg_T * eap)24940eabd4dcSBram Moolenaar ex_emenu(exarg_T *eap)
24950eabd4dcSBram Moolenaar {
24960eabd4dcSBram Moolenaar vimmenu_T *menu;
24970eabd4dcSBram Moolenaar char_u *arg = eap->arg;
24980eabd4dcSBram Moolenaar int mode_idx = -1;
24990eabd4dcSBram Moolenaar
25000eabd4dcSBram Moolenaar if (arg[0] && VIM_ISWHITE(arg[1]))
25010eabd4dcSBram Moolenaar {
25020eabd4dcSBram Moolenaar switch (arg[0])
25030eabd4dcSBram Moolenaar {
25040eabd4dcSBram Moolenaar case 'n': mode_idx = MENU_INDEX_NORMAL; break;
25050eabd4dcSBram Moolenaar case 'v': mode_idx = MENU_INDEX_VISUAL; break;
25060eabd4dcSBram Moolenaar case 's': mode_idx = MENU_INDEX_SELECT; break;
25070eabd4dcSBram Moolenaar case 'o': mode_idx = MENU_INDEX_OP_PENDING; break;
25080eabd4dcSBram Moolenaar case 't': mode_idx = MENU_INDEX_TERMINAL; break;
25090eabd4dcSBram Moolenaar case 'i': mode_idx = MENU_INDEX_INSERT; break;
25100eabd4dcSBram Moolenaar case 'c': mode_idx = MENU_INDEX_CMDLINE; break;
25110eabd4dcSBram Moolenaar default: semsg(_(e_invarg2), arg);
25121b9645deSBram Moolenaar return;
25131b9645deSBram Moolenaar }
25140eabd4dcSBram Moolenaar arg = skipwhite(arg + 2);
25150eabd4dcSBram Moolenaar }
25160eabd4dcSBram Moolenaar
25170eabd4dcSBram Moolenaar menu = menu_getbyname(arg);
25180eabd4dcSBram Moolenaar if (menu == NULL)
25190eabd4dcSBram Moolenaar return;
25201b9645deSBram Moolenaar
25214c5d8152SBram Moolenaar // Found the menu, so execute.
25224c5d8152SBram Moolenaar execute_menu(eap, menu, mode_idx);
25231b9645deSBram Moolenaar }
25241b9645deSBram Moolenaar
25251b9645deSBram Moolenaar /*
25261b9645deSBram Moolenaar * Handle a click in the window toolbar of "wp" at column "col".
25271b9645deSBram Moolenaar */
25281b9645deSBram Moolenaar void
winbar_click(win_T * wp,int col)25291b9645deSBram Moolenaar winbar_click(win_T *wp, int col)
25301b9645deSBram Moolenaar {
25311b9645deSBram Moolenaar int idx;
25321b9645deSBram Moolenaar
25331b9645deSBram Moolenaar if (wp->w_winbar_items == NULL)
25341b9645deSBram Moolenaar return;
25351b9645deSBram Moolenaar for (idx = 0; wp->w_winbar_items[idx].wb_menu != NULL; ++idx)
25361b9645deSBram Moolenaar {
25371b9645deSBram Moolenaar winbar_item_T *item = &wp->w_winbar_items[idx];
25381b9645deSBram Moolenaar
25391b9645deSBram Moolenaar if (col >= item->wb_startcol && col <= item->wb_endcol)
25401b9645deSBram Moolenaar {
25411b9645deSBram Moolenaar win_T *save_curwin = NULL;
2542a21a6a9aSBram Moolenaar pos_T save_visual = VIsual;
2543a21a6a9aSBram Moolenaar int save_visual_active = VIsual_active;
2544a21a6a9aSBram Moolenaar int save_visual_select = VIsual_select;
2545a21a6a9aSBram Moolenaar int save_visual_reselect = VIsual_reselect;
2546a21a6a9aSBram Moolenaar int save_visual_mode = VIsual_mode;
25471b9645deSBram Moolenaar
25481b9645deSBram Moolenaar if (wp != curwin)
25491b9645deSBram Moolenaar {
25504ba37b58SBram Moolenaar // Clicking in the window toolbar of a not-current window.
25514ba37b58SBram Moolenaar // Make that window the current one and save Visual mode.
25521b9645deSBram Moolenaar save_curwin = curwin;
2553a21a6a9aSBram Moolenaar VIsual_active = FALSE;
25541b9645deSBram Moolenaar curwin = wp;
25551b9645deSBram Moolenaar curbuf = curwin->w_buffer;
25561b9645deSBram Moolenaar check_cursor();
25571b9645deSBram Moolenaar }
25581b9645deSBram Moolenaar
2559d2fad67eSBram Moolenaar // Note: the command might close the current window.
25604c5d8152SBram Moolenaar execute_menu(NULL, item->wb_menu, -1);
25611b9645deSBram Moolenaar
2562d2fad67eSBram Moolenaar if (save_curwin != NULL && win_valid(save_curwin))
25631b9645deSBram Moolenaar {
25641b9645deSBram Moolenaar curwin = save_curwin;
25651b9645deSBram Moolenaar curbuf = curwin->w_buffer;
2566a21a6a9aSBram Moolenaar VIsual = save_visual;
2567a21a6a9aSBram Moolenaar VIsual_active = save_visual_active;
2568a21a6a9aSBram Moolenaar VIsual_select = save_visual_select;
2569a21a6a9aSBram Moolenaar VIsual_reselect = save_visual_reselect;
2570a21a6a9aSBram Moolenaar VIsual_mode = save_visual_mode;
25711b9645deSBram Moolenaar }
2572d2fad67eSBram Moolenaar if (!win_valid(wp))
2573d2fad67eSBram Moolenaar break;
25741b9645deSBram Moolenaar }
25751b9645deSBram Moolenaar }
25761b9645deSBram Moolenaar }
25771b9645deSBram Moolenaar
25781b9645deSBram Moolenaar #if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_GTK) \
2579b3f74069SBram Moolenaar || defined(FEAT_TERM_POPUP_MENU) || defined(FEAT_GUI_HAIKU) \
2580071d4279SBram Moolenaar || defined(FEAT_BEVAL_TIP) || defined(PROTO)
2581071d4279SBram Moolenaar /*
2582071d4279SBram Moolenaar * Given a menu descriptor, e.g. "File.New", find it in the menu hierarchy.
2583071d4279SBram Moolenaar */
2584071d4279SBram Moolenaar vimmenu_T *
gui_find_menu(char_u * path_name)258552ea13daSBram Moolenaar gui_find_menu(char_u *path_name)
2586071d4279SBram Moolenaar {
2587071d4279SBram Moolenaar vimmenu_T *menu = NULL;
2588071d4279SBram Moolenaar char_u *name;
2589071d4279SBram Moolenaar char_u *saved_name;
2590071d4279SBram Moolenaar char_u *p;
2591071d4279SBram Moolenaar
25921b9645deSBram Moolenaar menu = *get_root_menu(path_name);
2593071d4279SBram Moolenaar
2594071d4279SBram Moolenaar saved_name = vim_strsave(path_name);
2595071d4279SBram Moolenaar if (saved_name == NULL)
2596071d4279SBram Moolenaar return NULL;
2597071d4279SBram Moolenaar
2598071d4279SBram Moolenaar name = saved_name;
2599071d4279SBram Moolenaar while (*name)
2600071d4279SBram Moolenaar {
26014ba37b58SBram Moolenaar // find the end of one dot-separated name and put a NUL at the dot
2602071d4279SBram Moolenaar p = menu_name_skip(name);
2603071d4279SBram Moolenaar
2604071d4279SBram Moolenaar while (menu != NULL)
2605071d4279SBram Moolenaar {
2606d91f704bSBram Moolenaar if (menu_name_equal(name, menu))
2607071d4279SBram Moolenaar {
2608071d4279SBram Moolenaar if (menu->children == NULL)
2609071d4279SBram Moolenaar {
26104ba37b58SBram Moolenaar // found a menu item instead of a sub-menu
2611071d4279SBram Moolenaar if (*p == NUL)
2612f9e3e09fSBram Moolenaar emsg(_("E336: Menu path must lead to a sub-menu"));
2613071d4279SBram Moolenaar else
2614f9e3e09fSBram Moolenaar emsg(_(e_notsubmenu));
2615071d4279SBram Moolenaar menu = NULL;
2616071d4279SBram Moolenaar goto theend;
2617071d4279SBram Moolenaar }
26184ba37b58SBram Moolenaar if (*p == NUL) // found a full match
2619071d4279SBram Moolenaar goto theend;
2620071d4279SBram Moolenaar break;
2621071d4279SBram Moolenaar }
2622071d4279SBram Moolenaar menu = menu->next;
2623071d4279SBram Moolenaar }
26244ba37b58SBram Moolenaar if (menu == NULL) // didn't find it
2625071d4279SBram Moolenaar break;
2626071d4279SBram Moolenaar
26274ba37b58SBram Moolenaar // Found a match, search the sub-menu.
2628071d4279SBram Moolenaar menu = menu->children;
2629071d4279SBram Moolenaar name = p;
2630071d4279SBram Moolenaar }
2631071d4279SBram Moolenaar
2632071d4279SBram Moolenaar if (menu == NULL)
2633f9e3e09fSBram Moolenaar emsg(_("E337: Menu not found - check menu names"));
2634071d4279SBram Moolenaar theend:
2635071d4279SBram Moolenaar vim_free(saved_name);
2636071d4279SBram Moolenaar return menu;
2637071d4279SBram Moolenaar }
2638071d4279SBram Moolenaar #endif
2639071d4279SBram Moolenaar
2640071d4279SBram Moolenaar #ifdef FEAT_MULTI_LANG
2641071d4279SBram Moolenaar /*
2642071d4279SBram Moolenaar * Translation of menu names. Just a simple lookup table.
2643071d4279SBram Moolenaar */
2644071d4279SBram Moolenaar
2645071d4279SBram Moolenaar typedef struct
2646071d4279SBram Moolenaar {
26474ba37b58SBram Moolenaar char_u *from; // English name
26484ba37b58SBram Moolenaar char_u *from_noamp; // same, without '&'
26494ba37b58SBram Moolenaar char_u *to; // translated name
2650071d4279SBram Moolenaar } menutrans_T;
2651071d4279SBram Moolenaar
2652071d4279SBram Moolenaar static garray_T menutrans_ga = {0, 0, 0, 0, NULL};
2653071d4279SBram Moolenaar #endif
2654071d4279SBram Moolenaar
2655071d4279SBram Moolenaar /*
2656071d4279SBram Moolenaar * ":menutrans".
2657071d4279SBram Moolenaar * This function is also defined without the +multi_lang feature, in which
2658071d4279SBram Moolenaar * case the commands are ignored.
2659071d4279SBram Moolenaar */
2660071d4279SBram Moolenaar void
ex_menutranslate(exarg_T * eap UNUSED)266152ea13daSBram Moolenaar ex_menutranslate(exarg_T *eap UNUSED)
2662071d4279SBram Moolenaar {
2663071d4279SBram Moolenaar #ifdef FEAT_MULTI_LANG
2664071d4279SBram Moolenaar char_u *arg = eap->arg;
2665071d4279SBram Moolenaar menutrans_T *tp;
2666071d4279SBram Moolenaar int i;
2667071d4279SBram Moolenaar char_u *from, *from_noamp, *to;
2668071d4279SBram Moolenaar
2669071d4279SBram Moolenaar if (menutrans_ga.ga_itemsize == 0)
2670071d4279SBram Moolenaar ga_init2(&menutrans_ga, (int)sizeof(menutrans_T), 5);
2671071d4279SBram Moolenaar
2672071d4279SBram Moolenaar /*
2673071d4279SBram Moolenaar * ":menutrans clear": clear all translations.
2674071d4279SBram Moolenaar */
26751966c248SBram Moolenaar if (STRNCMP(arg, "clear", 5) == 0 && ends_excmd2(arg, skipwhite(arg + 5)))
2676071d4279SBram Moolenaar {
2677071d4279SBram Moolenaar tp = (menutrans_T *)menutrans_ga.ga_data;
2678071d4279SBram Moolenaar for (i = 0; i < menutrans_ga.ga_len; ++i)
2679071d4279SBram Moolenaar {
2680071d4279SBram Moolenaar vim_free(tp[i].from);
2681071d4279SBram Moolenaar vim_free(tp[i].from_noamp);
2682071d4279SBram Moolenaar vim_free(tp[i].to);
2683071d4279SBram Moolenaar }
2684071d4279SBram Moolenaar ga_clear(&menutrans_ga);
2685071d4279SBram Moolenaar # ifdef FEAT_EVAL
26864ba37b58SBram Moolenaar // Delete all "menutrans_" global variables.
2687071d4279SBram Moolenaar del_menutrans_vars();
2688071d4279SBram Moolenaar # endif
2689071d4279SBram Moolenaar }
2690071d4279SBram Moolenaar else
2691071d4279SBram Moolenaar {
26924ba37b58SBram Moolenaar // ":menutrans from to": add translation
2693071d4279SBram Moolenaar from = arg;
2694071d4279SBram Moolenaar arg = menu_skip_part(arg);
2695071d4279SBram Moolenaar to = skipwhite(arg);
2696071d4279SBram Moolenaar *arg = NUL;
2697071d4279SBram Moolenaar arg = menu_skip_part(to);
26981966c248SBram Moolenaar if (arg == to || ends_excmd2(eap->arg, from)
26991966c248SBram Moolenaar || ends_excmd2(eap->arg, to)
27001966c248SBram Moolenaar || !ends_excmd2(eap->arg, skipwhite(arg)))
2701f9e3e09fSBram Moolenaar emsg(_(e_invarg));
2702071d4279SBram Moolenaar else
2703071d4279SBram Moolenaar {
2704071d4279SBram Moolenaar if (ga_grow(&menutrans_ga, 1) == OK)
2705071d4279SBram Moolenaar {
2706071d4279SBram Moolenaar tp = (menutrans_T *)menutrans_ga.ga_data;
2707071d4279SBram Moolenaar from = vim_strsave(from);
2708fc1421ebSBram Moolenaar if (from != NULL)
2709fc1421ebSBram Moolenaar {
2710071d4279SBram Moolenaar from_noamp = menu_text(from, NULL, NULL);
271171ccd03eSBram Moolenaar to = vim_strnsave(to, arg - to);
2712fc1421ebSBram Moolenaar if (from_noamp != NULL && to != NULL)
2713071d4279SBram Moolenaar {
271470b11cddSBram Moolenaar menu_translate_tab_and_shift(from);
271570b11cddSBram Moolenaar menu_translate_tab_and_shift(to);
271670b11cddSBram Moolenaar menu_unescape_name(from);
271770b11cddSBram Moolenaar menu_unescape_name(to);
2718071d4279SBram Moolenaar tp[menutrans_ga.ga_len].from = from;
2719071d4279SBram Moolenaar tp[menutrans_ga.ga_len].from_noamp = from_noamp;
2720071d4279SBram Moolenaar tp[menutrans_ga.ga_len].to = to;
2721071d4279SBram Moolenaar ++menutrans_ga.ga_len;
2722071d4279SBram Moolenaar }
2723071d4279SBram Moolenaar else
2724071d4279SBram Moolenaar {
2725071d4279SBram Moolenaar vim_free(from);
2726071d4279SBram Moolenaar vim_free(from_noamp);
2727071d4279SBram Moolenaar vim_free(to);
2728071d4279SBram Moolenaar }
2729071d4279SBram Moolenaar }
2730071d4279SBram Moolenaar }
2731071d4279SBram Moolenaar }
2732fc1421ebSBram Moolenaar }
2733071d4279SBram Moolenaar #endif
2734071d4279SBram Moolenaar }
2735071d4279SBram Moolenaar
2736071d4279SBram Moolenaar #if defined(FEAT_MULTI_LANG) || defined(FEAT_TOOLBAR)
2737071d4279SBram Moolenaar /*
2738071d4279SBram Moolenaar * Find the character just after one part of a menu name.
2739071d4279SBram Moolenaar */
2740071d4279SBram Moolenaar static char_u *
menu_skip_part(char_u * p)274152ea13daSBram Moolenaar menu_skip_part(char_u *p)
2742071d4279SBram Moolenaar {
27431c465444SBram Moolenaar while (*p != NUL && *p != '.' && !VIM_ISWHITE(*p))
2744071d4279SBram Moolenaar {
2745071d4279SBram Moolenaar if ((*p == '\\' || *p == Ctrl_V) && p[1] != NUL)
2746071d4279SBram Moolenaar ++p;
2747071d4279SBram Moolenaar ++p;
2748071d4279SBram Moolenaar }
2749071d4279SBram Moolenaar return p;
2750071d4279SBram Moolenaar }
2751071d4279SBram Moolenaar #endif
2752071d4279SBram Moolenaar
2753071d4279SBram Moolenaar #ifdef FEAT_MULTI_LANG
2754071d4279SBram Moolenaar /*
2755071d4279SBram Moolenaar * Lookup part of a menu name in the translations.
2756071d4279SBram Moolenaar * Return a pointer to the translation or NULL if not found.
2757071d4279SBram Moolenaar */
2758071d4279SBram Moolenaar static char_u *
menutrans_lookup(char_u * name,int len)275952ea13daSBram Moolenaar menutrans_lookup(char_u *name, int len)
2760071d4279SBram Moolenaar {
2761071d4279SBram Moolenaar menutrans_T *tp = (menutrans_T *)menutrans_ga.ga_data;
2762071d4279SBram Moolenaar int i;
2763071d4279SBram Moolenaar char_u *dname;
2764071d4279SBram Moolenaar
2765071d4279SBram Moolenaar for (i = 0; i < menutrans_ga.ga_len; ++i)
276611dd8c12SBram Moolenaar if (STRNICMP(name, tp[i].from, len) == 0 && tp[i].from[len] == NUL)
2767071d4279SBram Moolenaar return tp[i].to;
2768071d4279SBram Moolenaar
27694ba37b58SBram Moolenaar // Now try again while ignoring '&' characters.
2770071d4279SBram Moolenaar i = name[len];
2771071d4279SBram Moolenaar name[len] = NUL;
2772071d4279SBram Moolenaar dname = menu_text(name, NULL, NULL);
2773071d4279SBram Moolenaar name[len] = i;
2774071d4279SBram Moolenaar if (dname != NULL)
2775071d4279SBram Moolenaar {
2776071d4279SBram Moolenaar for (i = 0; i < menutrans_ga.ga_len; ++i)
277711dd8c12SBram Moolenaar if (STRICMP(dname, tp[i].from_noamp) == 0)
2778071d4279SBram Moolenaar {
2779071d4279SBram Moolenaar vim_free(dname);
2780071d4279SBram Moolenaar return tp[i].to;
2781071d4279SBram Moolenaar }
2782071d4279SBram Moolenaar vim_free(dname);
2783071d4279SBram Moolenaar }
2784071d4279SBram Moolenaar
2785071d4279SBram Moolenaar return NULL;
2786071d4279SBram Moolenaar }
2787071d4279SBram Moolenaar
278870b11cddSBram Moolenaar /*
278970b11cddSBram Moolenaar * Unescape the name in the translate dictionary table.
279070b11cddSBram Moolenaar */
279170b11cddSBram Moolenaar static void
menu_unescape_name(char_u * name)279252ea13daSBram Moolenaar menu_unescape_name(char_u *name)
279370b11cddSBram Moolenaar {
279470b11cddSBram Moolenaar char_u *p;
279570b11cddSBram Moolenaar
279691acfffcSBram Moolenaar for (p = name; *p && *p != '.'; MB_PTR_ADV(p))
279770b11cddSBram Moolenaar if (*p == '\\')
279870b11cddSBram Moolenaar STRMOVE(p, p + 1);
279970b11cddSBram Moolenaar }
28004ba37b58SBram Moolenaar #endif // FEAT_MULTI_LANG
280170b11cddSBram Moolenaar
280270b11cddSBram Moolenaar /*
280370b11cddSBram Moolenaar * Isolate the menu name.
280470b11cddSBram Moolenaar * Skip the menu name, and translate <Tab> into a real TAB.
280570b11cddSBram Moolenaar */
280670b11cddSBram Moolenaar static char_u *
menu_translate_tab_and_shift(char_u * arg_start)280752ea13daSBram Moolenaar menu_translate_tab_and_shift(char_u *arg_start)
280870b11cddSBram Moolenaar {
280970b11cddSBram Moolenaar char_u *arg = arg_start;
281070b11cddSBram Moolenaar
28111c465444SBram Moolenaar while (*arg && !VIM_ISWHITE(*arg))
281270b11cddSBram Moolenaar {
281370b11cddSBram Moolenaar if ((*arg == '\\' || *arg == Ctrl_V) && arg[1] != NUL)
281470b11cddSBram Moolenaar arg++;
281570b11cddSBram Moolenaar else if (STRNICMP(arg, "<TAB>", 5) == 0)
281670b11cddSBram Moolenaar {
281770b11cddSBram Moolenaar *arg = TAB;
281870b11cddSBram Moolenaar STRMOVE(arg + 1, arg + 5);
281970b11cddSBram Moolenaar }
282070b11cddSBram Moolenaar arg++;
282170b11cddSBram Moolenaar }
282270b11cddSBram Moolenaar if (*arg != NUL)
282370b11cddSBram Moolenaar *arg++ = NUL;
282470b11cddSBram Moolenaar arg = skipwhite(arg);
282570b11cddSBram Moolenaar
282670b11cddSBram Moolenaar return arg;
282770b11cddSBram Moolenaar }
282870b11cddSBram Moolenaar
28290eabd4dcSBram Moolenaar /*
28300eabd4dcSBram Moolenaar * Get the information about a menu item in mode 'which'
28310eabd4dcSBram Moolenaar */
28320eabd4dcSBram Moolenaar static int
menuitem_getinfo(char_u * menu_name,vimmenu_T * menu,int modes,dict_T * dict)2833*51491adfSYegappan Lakshmanan menuitem_getinfo(char_u *menu_name, vimmenu_T *menu, int modes, dict_T *dict)
28340eabd4dcSBram Moolenaar {
28350eabd4dcSBram Moolenaar int status;
2836*51491adfSYegappan Lakshmanan list_T *l;
2837*51491adfSYegappan Lakshmanan
2838*51491adfSYegappan Lakshmanan if (*menu_name == NUL)
2839*51491adfSYegappan Lakshmanan {
2840*51491adfSYegappan Lakshmanan // Return all the top-level menus
2841*51491adfSYegappan Lakshmanan vimmenu_T *topmenu;
2842*51491adfSYegappan Lakshmanan
2843*51491adfSYegappan Lakshmanan l = list_alloc();
2844*51491adfSYegappan Lakshmanan if (l == NULL)
2845*51491adfSYegappan Lakshmanan return FAIL;
2846*51491adfSYegappan Lakshmanan
2847*51491adfSYegappan Lakshmanan dict_add_list(dict, "submenus", l);
2848*51491adfSYegappan Lakshmanan // get all the children. Skip PopUp[nvoci].
2849*51491adfSYegappan Lakshmanan for (topmenu = menu; topmenu != NULL; topmenu = topmenu->next)
2850*51491adfSYegappan Lakshmanan if (!menu_is_hidden(topmenu->dname))
2851*51491adfSYegappan Lakshmanan list_append_string(l, topmenu->dname, -1);
2852*51491adfSYegappan Lakshmanan return OK;
2853*51491adfSYegappan Lakshmanan }
28540eabd4dcSBram Moolenaar
28550eabd4dcSBram Moolenaar if (menu_is_tearoff(menu->dname)) // skip tearoff menu item
28560eabd4dcSBram Moolenaar return OK;
28570eabd4dcSBram Moolenaar
28580eabd4dcSBram Moolenaar status = dict_add_string(dict, "name", menu->name);
28590eabd4dcSBram Moolenaar if (status == OK)
28600eabd4dcSBram Moolenaar status = dict_add_string(dict, "display", menu->dname);
28610eabd4dcSBram Moolenaar if (status == OK && menu->actext != NULL)
28620eabd4dcSBram Moolenaar status = dict_add_string(dict, "accel", menu->actext);
28630eabd4dcSBram Moolenaar if (status == OK)
28640eabd4dcSBram Moolenaar status = dict_add_number(dict, "priority", menu->priority);
28650eabd4dcSBram Moolenaar if (status == OK)
28660eabd4dcSBram Moolenaar status = dict_add_string(dict, "modes",
28670eabd4dcSBram Moolenaar get_menu_mode_str(menu->modes));
28680eabd4dcSBram Moolenaar #ifdef FEAT_TOOLBAR
28690eabd4dcSBram Moolenaar if (status == OK && menu->iconfile != NULL)
28700eabd4dcSBram Moolenaar status = dict_add_string(dict, "icon", menu->iconfile);
28710eabd4dcSBram Moolenaar if (status == OK && menu->iconidx >= 0)
28720eabd4dcSBram Moolenaar status = dict_add_number(dict, "iconidx", menu->iconidx);
28730eabd4dcSBram Moolenaar #endif
28740eabd4dcSBram Moolenaar if (status == OK)
28750eabd4dcSBram Moolenaar {
28760eabd4dcSBram Moolenaar char_u buf[NUMBUFLEN];
28770eabd4dcSBram Moolenaar
28780eabd4dcSBram Moolenaar if (has_mbyte)
28790eabd4dcSBram Moolenaar buf[utf_char2bytes(menu->mnemonic, buf)] = NUL;
28800eabd4dcSBram Moolenaar else
28810eabd4dcSBram Moolenaar {
28820eabd4dcSBram Moolenaar buf[0] = (char_u)menu->mnemonic;
28830eabd4dcSBram Moolenaar buf[1] = NUL;
28840eabd4dcSBram Moolenaar }
28850eabd4dcSBram Moolenaar status = dict_add_string(dict, "shortcut", buf);
28860eabd4dcSBram Moolenaar }
28870eabd4dcSBram Moolenaar if (status == OK && menu->children == NULL)
28880eabd4dcSBram Moolenaar {
28890eabd4dcSBram Moolenaar int bit;
28900eabd4dcSBram Moolenaar
28910eabd4dcSBram Moolenaar // Get the first mode in which the menu is available
289256cb3378SBram Moolenaar for (bit = 0; bit < MENU_MODES && !((1 << bit) & modes); bit++)
28930eabd4dcSBram Moolenaar ;
289456cb3378SBram Moolenaar if (bit < MENU_MODES) // just in case, avoid Coverity warning
289556cb3378SBram Moolenaar {
28960eabd4dcSBram Moolenaar if (menu->strings[bit] != NULL)
2897292b90d4SBram Moolenaar {
2898292b90d4SBram Moolenaar char_u *tofree = NULL;
2899292b90d4SBram Moolenaar
29000eabd4dcSBram Moolenaar status = dict_add_string(dict, "rhs",
290156cb3378SBram Moolenaar *menu->strings[bit] == NUL
2902292b90d4SBram Moolenaar ? (char_u *)"<Nop>"
2903292b90d4SBram Moolenaar : (tofree = str2special_save(
2904292b90d4SBram Moolenaar menu->strings[bit], FALSE)));
2905292b90d4SBram Moolenaar vim_free(tofree);
2906292b90d4SBram Moolenaar }
29070eabd4dcSBram Moolenaar if (status == OK)
29080eabd4dcSBram Moolenaar status = dict_add_bool(dict, "noremenu",
29090eabd4dcSBram Moolenaar menu->noremap[bit] == REMAP_NONE);
29100eabd4dcSBram Moolenaar if (status == OK)
29110eabd4dcSBram Moolenaar status = dict_add_bool(dict, "script",
29120eabd4dcSBram Moolenaar menu->noremap[bit] == REMAP_SCRIPT);
29130eabd4dcSBram Moolenaar if (status == OK)
29140eabd4dcSBram Moolenaar status = dict_add_bool(dict, "silent", menu->silent[bit]);
29150eabd4dcSBram Moolenaar if (status == OK)
29160eabd4dcSBram Moolenaar status = dict_add_bool(dict, "enabled",
29170eabd4dcSBram Moolenaar ((menu->enabled & (1 << bit)) != 0));
29180eabd4dcSBram Moolenaar }
291956cb3378SBram Moolenaar }
292056cb3378SBram Moolenaar
29210eabd4dcSBram Moolenaar // If there are submenus, add all the submenu display names
29220eabd4dcSBram Moolenaar if (status == OK && menu->children != NULL)
29230eabd4dcSBram Moolenaar {
29240eabd4dcSBram Moolenaar vimmenu_T *child;
29250eabd4dcSBram Moolenaar
2926*51491adfSYegappan Lakshmanan l = list_alloc();
29270eabd4dcSBram Moolenaar if (l == NULL)
29280eabd4dcSBram Moolenaar return FAIL;
29290eabd4dcSBram Moolenaar
29300eabd4dcSBram Moolenaar dict_add_list(dict, "submenus", l);
29310eabd4dcSBram Moolenaar child = menu->children;
29320eabd4dcSBram Moolenaar while (child)
29330eabd4dcSBram Moolenaar {
29340eabd4dcSBram Moolenaar if (!menu_is_tearoff(child->dname)) // skip tearoff menu
29350eabd4dcSBram Moolenaar list_append_string(l, child->dname, -1);
29360eabd4dcSBram Moolenaar child = child->next;
29370eabd4dcSBram Moolenaar }
29380eabd4dcSBram Moolenaar }
29390eabd4dcSBram Moolenaar
29400eabd4dcSBram Moolenaar return status;
29410eabd4dcSBram Moolenaar }
29420eabd4dcSBram Moolenaar
29430eabd4dcSBram Moolenaar /*
29440eabd4dcSBram Moolenaar * "menu_info()" function
29450eabd4dcSBram Moolenaar * Return information about a menu (including all the child menus)
29460eabd4dcSBram Moolenaar */
29470eabd4dcSBram Moolenaar void
f_menu_info(typval_T * argvars,typval_T * rettv)29480eabd4dcSBram Moolenaar f_menu_info(typval_T *argvars, typval_T *rettv)
29490eabd4dcSBram Moolenaar {
29500eabd4dcSBram Moolenaar char_u *menu_name;
29510eabd4dcSBram Moolenaar char_u *which;
29520eabd4dcSBram Moolenaar int modes;
29530eabd4dcSBram Moolenaar char_u *saved_name;
29540eabd4dcSBram Moolenaar char_u *name;
29550eabd4dcSBram Moolenaar vimmenu_T *menu;
29560eabd4dcSBram Moolenaar dict_T *retdict;
29570eabd4dcSBram Moolenaar
29580eabd4dcSBram Moolenaar if (rettv_dict_alloc(rettv) != OK)
29590eabd4dcSBram Moolenaar return;
29600eabd4dcSBram Moolenaar retdict = rettv->vval.v_dict;
29610eabd4dcSBram Moolenaar
29624490ec4eSYegappan Lakshmanan if (in_vim9script()
29634490ec4eSYegappan Lakshmanan && (check_for_string_arg(argvars, 0) == FAIL
29644490ec4eSYegappan Lakshmanan || check_for_opt_string_arg(argvars, 1) == FAIL))
29654490ec4eSYegappan Lakshmanan return;
29664490ec4eSYegappan Lakshmanan
29670eabd4dcSBram Moolenaar menu_name = tv_get_string_chk(&argvars[0]);
29680eabd4dcSBram Moolenaar if (menu_name == NULL)
29690eabd4dcSBram Moolenaar return;
29700eabd4dcSBram Moolenaar
29710eabd4dcSBram Moolenaar // menu mode
29720eabd4dcSBram Moolenaar if (argvars[1].v_type != VAR_UNKNOWN)
29730eabd4dcSBram Moolenaar which = tv_get_string_chk(&argvars[1]);
29740eabd4dcSBram Moolenaar else
29750eabd4dcSBram Moolenaar which = (char_u *)""; // Default is modes for "menu"
29760eabd4dcSBram Moolenaar if (which == NULL)
29770eabd4dcSBram Moolenaar return;
29780eabd4dcSBram Moolenaar
29790eabd4dcSBram Moolenaar modes = get_menu_cmd_modes(which, *which == '!', NULL, NULL);
29800eabd4dcSBram Moolenaar
29810eabd4dcSBram Moolenaar // Locate the specified menu or menu item
29820eabd4dcSBram Moolenaar menu = *get_root_menu(menu_name);
29830eabd4dcSBram Moolenaar saved_name = vim_strsave(menu_name);
29840eabd4dcSBram Moolenaar if (saved_name == NULL)
29850eabd4dcSBram Moolenaar return;
29860eabd4dcSBram Moolenaar if (*saved_name != NUL)
29870eabd4dcSBram Moolenaar {
29880eabd4dcSBram Moolenaar char_u *p;
29890eabd4dcSBram Moolenaar
29900eabd4dcSBram Moolenaar name = saved_name;
29910eabd4dcSBram Moolenaar while (*name)
29920eabd4dcSBram Moolenaar {
29930eabd4dcSBram Moolenaar // Find in the menu hierarchy
29940eabd4dcSBram Moolenaar p = menu_name_skip(name);
29950eabd4dcSBram Moolenaar while (menu != NULL)
29960eabd4dcSBram Moolenaar {
29970eabd4dcSBram Moolenaar if (menu_name_equal(name, menu))
29980eabd4dcSBram Moolenaar break;
29990eabd4dcSBram Moolenaar menu = menu->next;
30000eabd4dcSBram Moolenaar }
30010eabd4dcSBram Moolenaar if (menu == NULL || *p == NUL)
30020eabd4dcSBram Moolenaar break;
30030eabd4dcSBram Moolenaar menu = menu->children;
30040eabd4dcSBram Moolenaar name = p;
30050eabd4dcSBram Moolenaar }
30060eabd4dcSBram Moolenaar }
30070eabd4dcSBram Moolenaar vim_free(saved_name);
30080eabd4dcSBram Moolenaar
30090eabd4dcSBram Moolenaar if (menu == NULL) // specified menu not found
30100eabd4dcSBram Moolenaar return;
30110eabd4dcSBram Moolenaar
30120eabd4dcSBram Moolenaar if (menu->modes & modes)
3013*51491adfSYegappan Lakshmanan menuitem_getinfo(menu_name, menu, modes, retdict);
30140eabd4dcSBram Moolenaar }
30150eabd4dcSBram Moolenaar
30164ba37b58SBram Moolenaar #endif // FEAT_MENU
3017