blob: 782235a11c25342f2da21211c4b923055828b5aa [file] [log] [blame]
Bram Moolenaaredf3f972016-08-29 22:49:24 +02001/* vi:set ts=8 sts=4 sw=4 noet:
Bram Moolenaar071d4272004-06-13 20:20:40 +00002 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 * GUI/Motif support by Robert Webb
5 *
6 * Do ":help uganda" in Vim to read copying and usage conditions.
7 * Do ":help credits" in Vim to see a list of people who contributed.
8 * See README.txt for an overview of the Vim source code.
9 */
10
11/*
12 * Code for menus. Used for the GUI and 'wildmenu'.
13 */
14
15#include "vim.h"
16
17#if defined(FEAT_MENU) || defined(PROTO)
18
19#define MENUDEPTH 10 /* maximum depth of menus */
20
21#ifdef FEAT_GUI_W32
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +010022static int add_menu_path(char_u *, vimmenu_T *, int *, char_u *, int);
Bram Moolenaar071d4272004-06-13 20:20:40 +000023#else
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +010024static int add_menu_path(char_u *, vimmenu_T *, int *, char_u *);
Bram Moolenaar071d4272004-06-13 20:20:40 +000025#endif
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +010026static int menu_nable_recurse(vimmenu_T *menu, char_u *name, int modes, int enable);
27static int remove_menu(vimmenu_T **, char_u *, int, int silent);
28static void free_menu(vimmenu_T **menup);
29static void free_menu_string(vimmenu_T *, int);
30static int show_menus(char_u *, int);
31static void show_menus_recursive(vimmenu_T *, int, int);
32static int menu_name_equal(char_u *name, vimmenu_T *menu);
33static int menu_namecmp(char_u *name, char_u *mname);
34static int get_menu_cmd_modes(char_u *, int, int *, int *);
35static char_u *popup_mode_name(char_u *name, int idx);
36static char_u *menu_text(char_u *text, int *mnemonic, char_u **actext);
Bram Moolenaar071d4272004-06-13 20:20:40 +000037
38#if defined(FEAT_GUI_W32) && defined(FEAT_TEAROFF)
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +010039static void gui_create_tearoffs_recurse(vimmenu_T *menu, const char_u *pname, int *pri_tab, int pri_idx);
40static void gui_add_tearoff(char_u *tearpath, int *pri_tab, int pri_idx);
41static void gui_destroy_tearoffs_recurse(vimmenu_T *menu);
Bram Moolenaar071d4272004-06-13 20:20:40 +000042static int s_tearoffs = FALSE;
43#endif
44
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +010045static int menu_is_hidden(char_u *name);
Bram Moolenaar071d4272004-06-13 20:20:40 +000046#if defined(FEAT_CMDL_COMPL) || (defined(FEAT_GUI_W32) && defined(FEAT_TEAROFF))
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +010047static int menu_is_tearoff(char_u *name);
Bram Moolenaar071d4272004-06-13 20:20:40 +000048#endif
49
50#if defined(FEAT_MULTI_LANG) || defined(FEAT_TOOLBAR)
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +010051static char_u *menu_skip_part(char_u *p);
Bram Moolenaar071d4272004-06-13 20:20:40 +000052#endif
53#ifdef FEAT_MULTI_LANG
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +010054static char_u *menutrans_lookup(char_u *name, int len);
55static void menu_unescape_name(char_u *p);
Bram Moolenaar071d4272004-06-13 20:20:40 +000056#endif
57
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +010058static char_u *menu_translate_tab_and_shift(char_u *arg_start);
Bram Moolenaar70b11cd2010-05-14 22:24:40 +020059
Bram Moolenaar071d4272004-06-13 20:20:40 +000060/* The character for each menu mode */
Bram Moolenaar4c5d8152018-10-19 22:36:53 +020061static char *menu_mode_chars[] = {"n", "v", "s", "o", "i", "c", "tl", "t"};
Bram Moolenaar071d4272004-06-13 20:20:40 +000062
63static char_u e_notsubmenu[] = N_("E327: Part of menu-item path is not sub-menu");
64static char_u e_othermode[] = N_("E328: Menu only exists in another mode");
Bram Moolenaar342337a2005-07-21 21:11:17 +000065static char_u e_nomenu[] = N_("E329: No menu \"%s\"");
Bram Moolenaar071d4272004-06-13 20:20:40 +000066
67#ifdef FEAT_TOOLBAR
68static const char *toolbar_names[] =
69{
70 /* 0 */ "New", "Open", "Save", "Undo", "Redo",
71 /* 5 */ "Cut", "Copy", "Paste", "Print", "Help",
72 /* 10 */ "Find", "SaveAll", "SaveSesn", "NewSesn", "LoadSesn",
73 /* 15 */ "RunScript", "Replace", "WinClose", "WinMax", "WinMin",
74 /* 20 */ "WinSplit", "Shell", "FindPrev", "FindNext", "FindHelp",
75 /* 25 */ "Make", "TagJump", "RunCtags", "WinVSplit", "WinMaxWidth",
76 /* 30 */ "WinMinWidth", "Exit"
77};
78# define TOOLBAR_NAME_COUNT (sizeof(toolbar_names) / sizeof(char *))
79#endif
80
81/*
Bram Moolenaar1b9645d2017-09-17 23:03:31 +020082 * Return TRUE if "name" is a window toolbar menu name.
83 */
84 static int
85menu_is_winbar(char_u *name)
86{
Bram Moolenaar378daf82017-09-23 23:58:28 +020087 return (STRNCMP(name, "WinBar", 6) == 0);
Bram Moolenaar1b9645d2017-09-17 23:03:31 +020088}
89
90 int
91winbar_height(win_T *wp)
92{
93 if (wp->w_winbar != NULL && wp->w_winbar->children != NULL)
94 return 1;
95 return 0;
96}
97
98 static vimmenu_T **
99get_root_menu(char_u *name)
100{
101 if (menu_is_winbar(name))
102 return &curwin->w_winbar;
103 return &root_menu;
104}
105
106/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000107 * Do the :menu command and relatives.
108 */
109 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100110ex_menu(
111 exarg_T *eap) /* Ex command arguments */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000112{
113 char_u *menu_path;
114 int modes;
115 char_u *map_to;
116 int noremap;
117 int silent = FALSE;
Bram Moolenaar8b2d9c42006-05-03 21:28:47 +0000118 int special = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000119 int unmenu;
120 char_u *map_buf;
121 char_u *arg;
122 char_u *p;
123 int i;
Bram Moolenaar241a8aa2005-12-06 20:04:44 +0000124#if defined(FEAT_GUI) && !defined(FEAT_GUI_GTK)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000125 int old_menu_height;
Bram Moolenaare89ff042016-02-20 22:17:05 +0100126# if defined(FEAT_TOOLBAR) && !defined(FEAT_GUI_W32)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000127 int old_toolbar_height;
128# endif
129#endif
130 int pri_tab[MENUDEPTH + 1];
131 int enable = MAYBE; /* TRUE for "menu enable", FALSE for "menu
132 * disable */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000133#ifdef FEAT_TOOLBAR
134 char_u *icon = NULL;
135#endif
136 vimmenu_T menuarg;
Bram Moolenaar1b9645d2017-09-17 23:03:31 +0200137 vimmenu_T **root_menu_ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000138
139 modes = get_menu_cmd_modes(eap->cmd, eap->forceit, &noremap, &unmenu);
140 arg = eap->arg;
141
142 for (;;)
143 {
144 if (STRNCMP(arg, "<script>", 8) == 0)
145 {
146 noremap = REMAP_SCRIPT;
147 arg = skipwhite(arg + 8);
148 continue;
149 }
150 if (STRNCMP(arg, "<silent>", 8) == 0)
151 {
152 silent = TRUE;
153 arg = skipwhite(arg + 8);
154 continue;
155 }
Bram Moolenaar8b2d9c42006-05-03 21:28:47 +0000156 if (STRNCMP(arg, "<special>", 9) == 0)
157 {
158 special = TRUE;
159 arg = skipwhite(arg + 9);
160 continue;
161 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000162 break;
163 }
164
165
166 /* Locate an optional "icon=filename" argument. */
167 if (STRNCMP(arg, "icon=", 5) == 0)
168 {
169 arg += 5;
170#ifdef FEAT_TOOLBAR
171 icon = arg;
172#endif
173 while (*arg != NUL && *arg != ' ')
174 {
175 if (*arg == '\\')
Bram Moolenaar8c8de832008-06-24 22:58:06 +0000176 STRMOVE(arg, arg + 1);
Bram Moolenaar91acfff2017-03-12 19:22:36 +0100177 MB_PTR_ADV(arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000178 }
179 if (*arg != NUL)
180 {
181 *arg++ = NUL;
182 arg = skipwhite(arg);
183 }
184 }
185
186 /*
187 * Fill in the priority table.
188 */
189 for (p = arg; *p; ++p)
190 if (!VIM_ISDIGIT(*p) && *p != '.')
191 break;
Bram Moolenaar1c465442017-03-12 20:10:05 +0100192 if (VIM_ISWHITE(*p))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000193 {
Bram Moolenaar1c465442017-03-12 20:10:05 +0100194 for (i = 0; i < MENUDEPTH && !VIM_ISWHITE(*arg); ++i)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000195 {
196 pri_tab[i] = getdigits(&arg);
197 if (pri_tab[i] == 0)
198 pri_tab[i] = 500;
199 if (*arg == '.')
200 ++arg;
201 }
202 arg = skipwhite(arg);
203 }
204 else if (eap->addr_count && eap->line2 != 0)
205 {
206 pri_tab[0] = eap->line2;
207 i = 1;
208 }
209 else
210 i = 0;
211 while (i < MENUDEPTH)
212 pri_tab[i++] = 500;
213 pri_tab[MENUDEPTH] = -1; /* mark end of the table */
214
215 /*
216 * Check for "disable" or "enable" argument.
217 */
Bram Moolenaar1c465442017-03-12 20:10:05 +0100218 if (STRNCMP(arg, "enable", 6) == 0 && VIM_ISWHITE(arg[6]))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000219 {
220 enable = TRUE;
221 arg = skipwhite(arg + 6);
222 }
Bram Moolenaar1c465442017-03-12 20:10:05 +0100223 else if (STRNCMP(arg, "disable", 7) == 0 && VIM_ISWHITE(arg[7]))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000224 {
225 enable = FALSE;
226 arg = skipwhite(arg + 7);
227 }
228
229 /*
230 * If there is no argument, display all menus.
231 */
232 if (*arg == NUL)
233 {
234 show_menus(arg, modes);
235 return;
236 }
237
238#ifdef FEAT_TOOLBAR
239 /*
240 * Need to get the toolbar icon index before doing the translation.
241 */
242 menuarg.iconidx = -1;
243 menuarg.icon_builtin = FALSE;
244 if (menu_is_toolbar(arg))
245 {
246 menu_path = menu_skip_part(arg);
247 if (*menu_path == '.')
248 {
249 p = menu_skip_part(++menu_path);
250 if (STRNCMP(menu_path, "BuiltIn", 7) == 0)
251 {
252 if (skipdigits(menu_path + 7) == p)
253 {
254 menuarg.iconidx = atoi((char *)menu_path + 7);
Bram Moolenaaraf0167f2009-05-16 15:31:32 +0000255 if (menuarg.iconidx >= (int)TOOLBAR_NAME_COUNT)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000256 menuarg.iconidx = -1;
257 else
258 menuarg.icon_builtin = TRUE;
259 }
260 }
261 else
262 {
Bram Moolenaaraf0167f2009-05-16 15:31:32 +0000263 for (i = 0; i < (int)TOOLBAR_NAME_COUNT; ++i)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000264 if (STRNCMP(toolbar_names[i], menu_path, p - menu_path)
265 == 0)
266 {
267 menuarg.iconidx = i;
268 break;
269 }
270 }
271 }
272 }
273#endif
274
Bram Moolenaar071d4272004-06-13 20:20:40 +0000275 menu_path = arg;
276 if (*menu_path == '.')
277 {
278 EMSG2(_(e_invarg2), menu_path);
279 goto theend;
280 }
281
Bram Moolenaar70b11cd2010-05-14 22:24:40 +0200282 map_to = menu_translate_tab_and_shift(arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000283
284 /*
285 * If there is only a menu name, display menus with that name.
286 */
287 if (*map_to == NUL && !unmenu && enable == MAYBE)
288 {
289 show_menus(menu_path, modes);
290 goto theend;
291 }
292 else if (*map_to != NUL && (unmenu || enable != MAYBE))
293 {
294 EMSG(_(e_trailing));
295 goto theend;
296 }
Bram Moolenaar241a8aa2005-12-06 20:04:44 +0000297#if defined(FEAT_GUI) && !(defined(FEAT_GUI_GTK) || defined(FEAT_GUI_PHOTON))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000298 old_menu_height = gui.menu_height;
Bram Moolenaare89ff042016-02-20 22:17:05 +0100299# if defined(FEAT_TOOLBAR) && !defined(FEAT_GUI_W32)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000300 old_toolbar_height = gui.toolbar_height;
301# endif
302#endif
303
Bram Moolenaar1b9645d2017-09-17 23:03:31 +0200304 root_menu_ptr = get_root_menu(menu_path);
305 if (root_menu_ptr == &curwin->w_winbar)
306 /* Assume the window toolbar menu will change. */
307 redraw_later(NOT_VALID);
308
Bram Moolenaar071d4272004-06-13 20:20:40 +0000309 if (enable != MAYBE)
310 {
311 /*
312 * Change sensitivity of the menu.
313 * For the PopUp menu, remove a menu for each mode separately.
314 * Careful: menu_nable_recurse() changes menu_path.
315 */
316 if (STRCMP(menu_path, "*") == 0) /* meaning: do all menus */
317 menu_path = (char_u *)"";
318
319 if (menu_is_popup(menu_path))
320 {
321 for (i = 0; i < MENU_INDEX_TIP; ++i)
322 if (modes & (1 << i))
323 {
324 p = popup_mode_name(menu_path, i);
325 if (p != NULL)
326 {
Bram Moolenaar1b9645d2017-09-17 23:03:31 +0200327 menu_nable_recurse(*root_menu_ptr, p, MENU_ALL_MODES,
Bram Moolenaar071d4272004-06-13 20:20:40 +0000328 enable);
329 vim_free(p);
330 }
331 }
332 }
Bram Moolenaar1b9645d2017-09-17 23:03:31 +0200333 menu_nable_recurse(*root_menu_ptr, menu_path, modes, enable);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000334 }
335 else if (unmenu)
336 {
337 /*
338 * Delete menu(s).
339 */
340 if (STRCMP(menu_path, "*") == 0) /* meaning: remove all menus */
341 menu_path = (char_u *)"";
342
343 /*
344 * For the PopUp menu, remove a menu for each mode separately.
345 */
346 if (menu_is_popup(menu_path))
347 {
348 for (i = 0; i < MENU_INDEX_TIP; ++i)
349 if (modes & (1 << i))
350 {
351 p = popup_mode_name(menu_path, i);
352 if (p != NULL)
353 {
Bram Moolenaar1b9645d2017-09-17 23:03:31 +0200354 remove_menu(root_menu_ptr, p, MENU_ALL_MODES, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000355 vim_free(p);
356 }
357 }
358 }
359
360 /* Careful: remove_menu() changes menu_path */
Bram Moolenaar1b9645d2017-09-17 23:03:31 +0200361 remove_menu(root_menu_ptr, menu_path, modes, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000362 }
363 else
364 {
365 /*
366 * Add menu(s).
367 * Replace special key codes.
368 */
369 if (STRICMP(map_to, "<nop>") == 0) /* "<Nop>" means nothing */
370 {
371 map_to = (char_u *)"";
372 map_buf = NULL;
373 }
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000374 else if (modes & MENU_TIP_MODE)
375 map_buf = NULL; /* Menu tips are plain text. */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000376 else
Bram Moolenaar8b2d9c42006-05-03 21:28:47 +0000377 map_to = replace_termcodes(map_to, &map_buf, FALSE, TRUE, special);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000378 menuarg.modes = modes;
379#ifdef FEAT_TOOLBAR
380 menuarg.iconfile = icon;
381#endif
382 menuarg.noremap[0] = noremap;
383 menuarg.silent[0] = silent;
384 add_menu_path(menu_path, &menuarg, pri_tab, map_to
385#ifdef FEAT_GUI_W32
386 , TRUE
387#endif
388 );
389
390 /*
391 * For the PopUp menu, add a menu for each mode separately.
392 */
393 if (menu_is_popup(menu_path))
394 {
395 for (i = 0; i < MENU_INDEX_TIP; ++i)
396 if (modes & (1 << i))
397 {
398 p = popup_mode_name(menu_path, i);
399 if (p != NULL)
400 {
401 /* Include all modes, to make ":amenu" work */
402 menuarg.modes = modes;
403#ifdef FEAT_TOOLBAR
404 menuarg.iconfile = NULL;
405 menuarg.iconidx = -1;
406 menuarg.icon_builtin = FALSE;
407#endif
408 add_menu_path(p, &menuarg, pri_tab, map_to
409#ifdef FEAT_GUI_W32
410 , TRUE
411#endif
412 );
413 vim_free(p);
414 }
415 }
416 }
417
418 vim_free(map_buf);
419 }
420
Bram Moolenaar241a8aa2005-12-06 20:04:44 +0000421#if defined(FEAT_GUI) && !(defined(FEAT_GUI_GTK))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000422 /* If the menubar height changed, resize the window */
423 if (gui.in_use
424 && (gui.menu_height != old_menu_height
Bram Moolenaare89ff042016-02-20 22:17:05 +0100425# if defined(FEAT_TOOLBAR) && !defined(FEAT_GUI_W32)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000426 || gui.toolbar_height != old_toolbar_height
427# endif
428 ))
Bram Moolenaar04a9d452006-03-27 21:03:26 +0000429 gui_set_shellsize(FALSE, FALSE, RESIZE_VERT);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000430#endif
Bram Moolenaar1b9645d2017-09-17 23:03:31 +0200431 if (root_menu_ptr == &curwin->w_winbar)
432 {
433 int h = winbar_height(curwin);
434
435 if (h != curwin->w_winbar_height)
436 {
437 if (h == 0)
438 ++curwin->w_height;
439 else if (curwin->w_height > 0)
440 --curwin->w_height;
441 curwin->w_winbar_height = h;
442 }
443 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000444
445theend:
Bram Moolenaar071d4272004-06-13 20:20:40 +0000446 ;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000447}
448
449/*
450 * Add the menu with the given name to the menu hierarchy
451 */
452 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100453add_menu_path(
454 char_u *menu_path,
455 vimmenu_T *menuarg, /* passes modes, iconfile, iconidx,
Bram Moolenaar071d4272004-06-13 20:20:40 +0000456 icon_builtin, silent[0], noremap[0] */
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100457 int *pri_tab,
458 char_u *call_data
Bram Moolenaar071d4272004-06-13 20:20:40 +0000459#ifdef FEAT_GUI_W32
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100460 , int addtearoff /* may add tearoff item */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000461#endif
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100462 )
Bram Moolenaar071d4272004-06-13 20:20:40 +0000463{
464 char_u *path_name;
465 int modes = menuarg->modes;
466 vimmenu_T **menup;
467 vimmenu_T *menu = NULL;
468 vimmenu_T *parent;
469 vimmenu_T **lower_pri;
470 char_u *p;
471 char_u *name;
472 char_u *dname;
473 char_u *next_name;
474 int i;
475 int c;
Bram Moolenaar7871a502010-05-14 21:19:23 +0200476 int d;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000477#ifdef FEAT_GUI
478 int idx;
479 int new_idx;
480#endif
481 int pri_idx = 0;
482 int old_modes = 0;
483 int amenu;
Bram Moolenaar70b11cd2010-05-14 22:24:40 +0200484#ifdef FEAT_MULTI_LANG
485 char_u *en_name;
486 char_u *map_to = NULL;
487#endif
Bram Moolenaar1b9645d2017-09-17 23:03:31 +0200488 vimmenu_T **root_menu_ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000489
490 /* Make a copy so we can stuff around with it, since it could be const */
491 path_name = vim_strsave(menu_path);
492 if (path_name == NULL)
493 return FAIL;
Bram Moolenaar1b9645d2017-09-17 23:03:31 +0200494 root_menu_ptr = get_root_menu(menu_path);
495 menup = root_menu_ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000496 parent = NULL;
497 name = path_name;
498 while (*name)
499 {
500 /* Get name of this element in the menu hierarchy, and the simplified
501 * name (without mnemonic and accelerator text). */
502 next_name = menu_name_skip(name);
Bram Moolenaar70b11cd2010-05-14 22:24:40 +0200503#ifdef FEAT_MULTI_LANG
Bram Moolenaar442b4222010-05-24 21:34:22 +0200504 map_to = menutrans_lookup(name, (int)STRLEN(name));
Bram Moolenaar70b11cd2010-05-14 22:24:40 +0200505 if (map_to != NULL)
506 {
507 en_name = name;
508 name = map_to;
509 }
510 else
511 en_name = NULL;
512#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000513 dname = menu_text(name, NULL, NULL);
Bram Moolenaar18a0b122006-08-16 13:55:16 +0000514 if (dname == NULL)
515 goto erret;
516 if (*dname == NUL)
517 {
518 /* Only a mnemonic or accelerator is not valid. */
519 EMSG(_("E792: Empty menu name"));
520 goto erret;
521 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000522
523 /* See if it's already there */
524 lower_pri = menup;
525#ifdef FEAT_GUI
526 idx = 0;
527 new_idx = 0;
528#endif
529 menu = *menup;
530 while (menu != NULL)
531 {
532 if (menu_name_equal(name, menu) || menu_name_equal(dname, menu))
533 {
534 if (*next_name == NUL && menu->children != NULL)
535 {
536 if (!sys_menu)
537 EMSG(_("E330: Menu path must not lead to a sub-menu"));
538 goto erret;
539 }
540 if (*next_name != NUL && menu->children == NULL
541#ifdef FEAT_GUI_W32
542 && addtearoff
543#endif
544 )
545 {
546 if (!sys_menu)
547 EMSG(_(e_notsubmenu));
548 goto erret;
549 }
550 break;
551 }
552 menup = &menu->next;
553
554 /* Count menus, to find where this one needs to be inserted.
555 * Ignore menus that are not in the menubar (PopUp and Toolbar) */
556 if (parent != NULL || menu_is_menubar(menu->name))
557 {
558#ifdef FEAT_GUI
559 ++idx;
560#endif
561 if (menu->priority <= pri_tab[pri_idx])
562 {
563 lower_pri = menup;
564#ifdef FEAT_GUI
565 new_idx = idx;
566#endif
567 }
568 }
569 menu = menu->next;
570 }
571
572 if (menu == NULL)
573 {
574 if (*next_name == NUL && parent == NULL)
575 {
576 EMSG(_("E331: Must not add menu items directly to menu bar"));
577 goto erret;
578 }
579
580 if (menu_is_separator(dname) && *next_name != NUL)
581 {
582 EMSG(_("E332: Separator cannot be part of a menu path"));
583 goto erret;
584 }
585
586 /* Not already there, so lets add it */
587 menu = (vimmenu_T *)alloc_clear((unsigned)sizeof(vimmenu_T));
588 if (menu == NULL)
589 goto erret;
590
591 menu->modes = modes;
592 menu->enabled = MENU_ALL_MODES;
593 menu->name = vim_strsave(name);
594 /* separate mnemonic and accelerator text from actual menu name */
595 menu->dname = menu_text(name, &menu->mnemonic, &menu->actext);
Bram Moolenaar70b11cd2010-05-14 22:24:40 +0200596#ifdef FEAT_MULTI_LANG
597 if (en_name != NULL)
598 {
599 menu->en_name = vim_strsave(en_name);
600 menu->en_dname = menu_text(en_name, NULL, NULL);
601 }
602 else
603 {
604 menu->en_name = NULL;
605 menu->en_dname = NULL;
606 }
607#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000608 menu->priority = pri_tab[pri_idx];
609 menu->parent = parent;
610#ifdef FEAT_GUI_MOTIF
611 menu->sensitive = TRUE; /* the default */
612#endif
613#ifdef FEAT_BEVAL_TIP
614 menu->tip = NULL;
615#endif
616#ifdef FEAT_GUI_ATHENA
617 menu->image = None; /* X-Windows definition for NULL*/
618#endif
619
620 /*
621 * Add after menu that has lower priority.
622 */
623 menu->next = *lower_pri;
624 *lower_pri = menu;
625
626 old_modes = 0;
627
628#ifdef FEAT_TOOLBAR
629 menu->iconidx = menuarg->iconidx;
630 menu->icon_builtin = menuarg->icon_builtin;
631 if (*next_name == NUL && menuarg->iconfile != NULL)
632 menu->iconfile = vim_strsave(menuarg->iconfile);
633#endif
634#if defined(FEAT_GUI_W32) && defined(FEAT_TEAROFF)
635 /* the tearoff item must be present in the modes of each item. */
636 if (parent != NULL && menu_is_tearoff(parent->children->dname))
637 parent->children->modes |= modes;
638#endif
639 }
640 else
641 {
642 old_modes = menu->modes;
643
644 /*
645 * If this menu option was previously only available in other
646 * modes, then make sure it's available for this one now
647 * Also enable a menu when it's created or changed.
648 */
649#ifdef FEAT_GUI_W32
650 /* If adding a tearbar (addtearoff == FALSE) don't update modes */
651 if (addtearoff)
652#endif
653 {
654 menu->modes |= modes;
655 menu->enabled |= modes;
656 }
657 }
658
659#ifdef FEAT_GUI
660 /*
661 * Add the menu item when it's used in one of the modes, but not when
662 * only a tooltip is defined.
663 */
664 if ((old_modes & MENU_ALL_MODES) == 0
665 && (menu->modes & MENU_ALL_MODES) != 0)
666 {
667 if (gui.in_use) /* Otherwise it will be added when GUI starts */
668 {
669 if (*next_name == NUL)
670 {
671 /* Real menu item, not sub-menu */
672 gui_mch_add_menu_item(menu, new_idx);
673
674 /* Want to update menus now even if mode not changed */
675 force_menu_update = TRUE;
676 }
677 else
678 {
679 /* Sub-menu (not at end of path yet) */
680 gui_mch_add_menu(menu, new_idx);
681 }
682 }
683
684# if defined(FEAT_GUI_W32) & defined(FEAT_TEAROFF)
685 /* When adding a new submenu, may add a tearoff item */
686 if ( addtearoff
687 && *next_name
688 && vim_strchr(p_go, GO_TEAROFF) != NULL
689 && menu_is_menubar(name))
690 {
691 char_u *tearpath;
692
693 /*
694 * The pointers next_name & path_name refer to a string with
695 * \'s and ^V's stripped out. But menu_path is a "raw"
696 * string, so we must correct for special characters.
697 */
698 tearpath = alloc((unsigned int)STRLEN(menu_path) + TEAR_LEN + 2);
699 if (tearpath != NULL)
700 {
701 char_u *s;
702 int idx;
703
704 STRCPY(tearpath, menu_path);
705 idx = (int)(next_name - path_name - 1);
Bram Moolenaar91acfff2017-03-12 19:22:36 +0100706 for (s = tearpath; *s && s < tearpath + idx; MB_PTR_ADV(s))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000707 {
708 if ((*s == '\\' || *s == Ctrl_V) && s[1])
709 {
710 ++idx;
711 ++s;
712 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000713 }
714 tearpath[idx] = NUL;
715 gui_add_tearoff(tearpath, pri_tab, pri_idx);
716 vim_free(tearpath);
717 }
718 }
719# endif
720 }
721#endif /* FEAT_GUI */
722
723 menup = &menu->children;
724 parent = menu;
725 name = next_name;
Bram Moolenaard23a8232018-02-10 18:45:26 +0100726 VIM_CLEAR(dname);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000727 if (pri_tab[pri_idx + 1] != -1)
728 ++pri_idx;
729 }
730 vim_free(path_name);
731
732 /*
733 * Only add system menu items which have not been defined yet.
734 * First check if this was an ":amenu".
735 */
736 amenu = ((modes & (MENU_NORMAL_MODE | MENU_INSERT_MODE)) ==
737 (MENU_NORMAL_MODE | MENU_INSERT_MODE));
738 if (sys_menu)
739 modes &= ~old_modes;
740
741 if (menu != NULL && modes)
742 {
743#ifdef FEAT_GUI
744 menu->cb = gui_menu_cb;
745#endif
746 p = (call_data == NULL) ? NULL : vim_strsave(call_data);
747
748 /* loop over all modes, may add more than one */
749 for (i = 0; i < MENU_MODES; ++i)
750 {
751 if (modes & (1 << i))
752 {
753 /* free any old menu */
754 free_menu_string(menu, i);
755
756 /* For "amenu", may insert an extra character.
757 * Don't do this if adding a tearbar (addtearoff == FALSE).
758 * Don't do this for "<Nop>". */
759 c = 0;
Bram Moolenaar7871a502010-05-14 21:19:23 +0200760 d = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000761 if (amenu && call_data != NULL && *call_data != NUL
762#ifdef FEAT_GUI_W32
763 && addtearoff
764#endif
765 )
766 {
767 switch (1 << i)
768 {
769 case MENU_VISUAL_MODE:
Bram Moolenaarb3656ed2006-03-20 21:59:49 +0000770 case MENU_SELECT_MODE:
Bram Moolenaar071d4272004-06-13 20:20:40 +0000771 case MENU_OP_PENDING_MODE:
772 case MENU_CMDLINE_MODE:
773 c = Ctrl_C;
774 break;
775 case MENU_INSERT_MODE:
Bram Moolenaar7871a502010-05-14 21:19:23 +0200776 c = Ctrl_BSL;
777 d = Ctrl_O;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000778 break;
779 }
780 }
781
Bram Moolenaar7871a502010-05-14 21:19:23 +0200782 if (c != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000783 {
Bram Moolenaar7871a502010-05-14 21:19:23 +0200784 menu->strings[i] = alloc((unsigned)(STRLEN(call_data) + 5 ));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000785 if (menu->strings[i] != NULL)
786 {
787 menu->strings[i][0] = c;
Bram Moolenaar7871a502010-05-14 21:19:23 +0200788 if (d == 0)
789 STRCPY(menu->strings[i] + 1, call_data);
790 else
791 {
792 menu->strings[i][1] = d;
793 STRCPY(menu->strings[i] + 2, call_data);
794 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000795 if (c == Ctrl_C)
796 {
Bram Moolenaara93fa7e2006-04-17 22:14:47 +0000797 int len = (int)STRLEN(menu->strings[i]);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000798
799 /* Append CTRL-\ CTRL-G to obey 'insertmode'. */
800 menu->strings[i][len] = Ctrl_BSL;
801 menu->strings[i][len + 1] = Ctrl_G;
802 menu->strings[i][len + 2] = NUL;
803 }
804 }
805 }
806 else
807 menu->strings[i] = p;
808 menu->noremap[i] = menuarg->noremap[0];
809 menu->silent[i] = menuarg->silent[0];
810 }
811 }
812#if defined(FEAT_TOOLBAR) && !defined(FEAT_GUI_W32) \
Bram Moolenaarc3719bd2017-11-18 22:13:31 +0100813 && (defined(FEAT_BEVAL_GUI) || defined(FEAT_GUI_GTK))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000814 /* Need to update the menu tip. */
815 if (modes & MENU_TIP_MODE)
816 gui_mch_menu_set_tip(menu);
817#endif
818 }
819 return OK;
820
821erret:
822 vim_free(path_name);
823 vim_free(dname);
Bram Moolenaar18a0b122006-08-16 13:55:16 +0000824
825 /* Delete any empty submenu we added before discovering the error. Repeat
826 * for higher levels. */
827 while (parent != NULL && parent->children == NULL)
828 {
829 if (parent->parent == NULL)
Bram Moolenaar1b9645d2017-09-17 23:03:31 +0200830 menup = root_menu_ptr;
Bram Moolenaar18a0b122006-08-16 13:55:16 +0000831 else
832 menup = &parent->parent->children;
833 for ( ; *menup != NULL && *menup != parent; menup = &((*menup)->next))
834 ;
835 if (*menup == NULL) /* safety check */
836 break;
837 parent = parent->parent;
838 free_menu(menup);
839 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000840 return FAIL;
841}
842
843/*
844 * Set the (sub)menu with the given name to enabled or disabled.
845 * Called recursively.
846 */
847 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100848menu_nable_recurse(
849 vimmenu_T *menu,
850 char_u *name,
851 int modes,
852 int enable)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000853{
854 char_u *p;
855
856 if (menu == NULL)
857 return OK; /* Got to bottom of hierarchy */
858
859 /* Get name of this element in the menu hierarchy */
860 p = menu_name_skip(name);
861
862 /* Find the menu */
863 while (menu != NULL)
864 {
865 if (*name == NUL || *name == '*' || menu_name_equal(name, menu))
866 {
867 if (*p != NUL)
868 {
869 if (menu->children == NULL)
870 {
871 EMSG(_(e_notsubmenu));
872 return FAIL;
873 }
874 if (menu_nable_recurse(menu->children, p, modes, enable)
875 == FAIL)
876 return FAIL;
877 }
878 else
879 if (enable)
880 menu->enabled |= modes;
881 else
882 menu->enabled &= ~modes;
883
884 /*
885 * When name is empty, we are doing all menu items for the given
886 * modes, so keep looping, otherwise we are just doing the named
887 * menu item (which has been found) so break here.
888 */
889 if (*name != NUL && *name != '*')
890 break;
891 }
892 menu = menu->next;
893 }
894 if (*name != NUL && *name != '*' && menu == NULL)
895 {
Bram Moolenaar342337a2005-07-21 21:11:17 +0000896 EMSG2(_(e_nomenu), name);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000897 return FAIL;
898 }
899
900#ifdef FEAT_GUI
901 /* Want to update menus now even if mode not changed */
902 force_menu_update = TRUE;
903#endif
904
905 return OK;
906}
907
908/*
909 * Remove the (sub)menu with the given name from the menu hierarchy
910 * Called recursively.
911 */
912 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100913remove_menu(
914 vimmenu_T **menup,
915 char_u *name,
916 int modes,
917 int silent) /* don't give error messages */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000918{
919 vimmenu_T *menu;
920 vimmenu_T *child;
921 char_u *p;
922
923 if (*menup == NULL)
924 return OK; /* Got to bottom of hierarchy */
925
926 /* Get name of this element in the menu hierarchy */
927 p = menu_name_skip(name);
928
929 /* Find the menu */
930 while ((menu = *menup) != NULL)
931 {
932 if (*name == NUL || menu_name_equal(name, menu))
933 {
934 if (*p != NUL && menu->children == NULL)
935 {
936 if (!silent)
937 EMSG(_(e_notsubmenu));
938 return FAIL;
939 }
940 if ((menu->modes & modes) != 0x0)
941 {
942#if defined(FEAT_GUI_W32) & defined(FEAT_TEAROFF)
943 /*
944 * If we are removing all entries for this menu,MENU_ALL_MODES,
945 * Then kill any tearoff before we start
946 */
947 if (*p == NUL && modes == MENU_ALL_MODES)
948 {
949 if (IsWindow(menu->tearoff_handle))
950 DestroyWindow(menu->tearoff_handle);
951 }
952#endif
953 if (remove_menu(&menu->children, p, modes, silent) == FAIL)
954 return FAIL;
955 }
956 else if (*name != NUL)
957 {
958 if (!silent)
959 EMSG(_(e_othermode));
960 return FAIL;
961 }
962
963 /*
964 * When name is empty, we are removing all menu items for the given
965 * modes, so keep looping, otherwise we are just removing the named
966 * menu item (which has been found) so break here.
967 */
968 if (*name != NUL)
969 break;
970
971 /* Remove the menu item for the given mode[s]. If the menu item
972 * is no longer valid in ANY mode, delete it */
973 menu->modes &= ~modes;
974 if (modes & MENU_TIP_MODE)
975 free_menu_string(menu, MENU_INDEX_TIP);
976 if ((menu->modes & MENU_ALL_MODES) == 0)
977 free_menu(menup);
978 else
979 menup = &menu->next;
980 }
981 else
982 menup = &menu->next;
983 }
984 if (*name != NUL)
985 {
986 if (menu == NULL)
987 {
988 if (!silent)
Bram Moolenaar342337a2005-07-21 21:11:17 +0000989 EMSG2(_(e_nomenu), name);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000990 return FAIL;
991 }
992
993
994 /* Recalculate modes for menu based on the new updated children */
995 menu->modes &= ~modes;
996#if defined(FEAT_GUI_W32) & defined(FEAT_TEAROFF)
997 if ((s_tearoffs) && (menu->children != NULL)) /* there's a tear bar.. */
998 child = menu->children->next; /* don't count tearoff bar */
999 else
1000#endif
1001 child = menu->children;
1002 for ( ; child != NULL; child = child->next)
1003 menu->modes |= child->modes;
1004 if (modes & MENU_TIP_MODE)
1005 {
1006 free_menu_string(menu, MENU_INDEX_TIP);
1007#if defined(FEAT_TOOLBAR) && !defined(FEAT_GUI_W32) \
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01001008 && (defined(FEAT_BEVAL_GUI) || defined(FEAT_GUI_GTK))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001009 /* Need to update the menu tip. */
1010 if (gui.in_use)
1011 gui_mch_menu_set_tip(menu);
1012#endif
1013 }
1014 if ((menu->modes & MENU_ALL_MODES) == 0)
1015 {
1016 /* The menu item is no longer valid in ANY mode, so delete it */
1017#if defined(FEAT_GUI_W32) & defined(FEAT_TEAROFF)
1018 if (s_tearoffs && menu->children != NULL) /* there's a tear bar.. */
1019 free_menu(&menu->children);
1020#endif
1021 *menup = menu;
1022 free_menu(menup);
1023 }
1024 }
1025
1026 return OK;
1027}
1028
1029/*
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02001030 * Remove the WinBar menu from window "wp".
1031 */
1032 void
1033remove_winbar(win_T *wp)
1034{
1035 remove_menu(&wp->w_winbar, (char_u *)"", MENU_ALL_MODES, TRUE);
1036 vim_free(wp->w_winbar_items);
1037}
1038
1039/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00001040 * Free the given menu structure and remove it from the linked list.
1041 */
1042 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001043free_menu(vimmenu_T **menup)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001044{
1045 int i;
1046 vimmenu_T *menu;
1047
1048 menu = *menup;
1049
1050#ifdef FEAT_GUI
1051 /* Free machine specific menu structures (only when already created) */
1052 /* Also may rebuild a tearoff'ed menu */
1053 if (gui.in_use)
1054 gui_mch_destroy_menu(menu);
1055#endif
1056
1057 /* Don't change *menup until after calling gui_mch_destroy_menu(). The
1058 * MacOS code needs the original structure to properly delete the menu. */
1059 *menup = menu->next;
1060 vim_free(menu->name);
1061 vim_free(menu->dname);
Bram Moolenaar70b11cd2010-05-14 22:24:40 +02001062#ifdef FEAT_MULTI_LANG
1063 vim_free(menu->en_name);
1064 vim_free(menu->en_dname);
1065#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001066 vim_free(menu->actext);
1067#ifdef FEAT_TOOLBAR
1068 vim_free(menu->iconfile);
Bram Moolenaarbee0c5b2005-02-07 22:03:36 +00001069# ifdef FEAT_GUI_MOTIF
1070 vim_free(menu->xpm_fname);
1071# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001072#endif
1073 for (i = 0; i < MENU_MODES; i++)
1074 free_menu_string(menu, i);
1075 vim_free(menu);
1076
1077#ifdef FEAT_GUI
1078 /* Want to update menus now even if mode not changed */
1079 force_menu_update = TRUE;
1080#endif
1081}
1082
1083/*
1084 * Free the menu->string with the given index.
1085 */
1086 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001087free_menu_string(vimmenu_T *menu, int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001088{
1089 int count = 0;
1090 int i;
1091
1092 for (i = 0; i < MENU_MODES; i++)
1093 if (menu->strings[i] == menu->strings[idx])
1094 count++;
1095 if (count == 1)
1096 vim_free(menu->strings[idx]);
1097 menu->strings[idx] = NULL;
1098}
1099
1100/*
1101 * Show the mapping associated with a menu item or hierarchy in a sub-menu.
1102 */
1103 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001104show_menus(char_u *path_name, int modes)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001105{
1106 char_u *p;
1107 char_u *name;
1108 vimmenu_T *menu;
1109 vimmenu_T *parent = NULL;
1110
Bram Moolenaar071d4272004-06-13 20:20:40 +00001111 name = path_name = vim_strsave(path_name);
1112 if (path_name == NULL)
1113 return FAIL;
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02001114 menu = *get_root_menu(path_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001115
1116 /* First, find the (sub)menu with the given name */
1117 while (*name)
1118 {
1119 p = menu_name_skip(name);
1120 while (menu != NULL)
1121 {
1122 if (menu_name_equal(name, menu))
1123 {
1124 /* Found menu */
1125 if (*p != NUL && menu->children == NULL)
1126 {
1127 EMSG(_(e_notsubmenu));
1128 vim_free(path_name);
1129 return FAIL;
1130 }
1131 else if ((menu->modes & modes) == 0x0)
1132 {
1133 EMSG(_(e_othermode));
1134 vim_free(path_name);
1135 return FAIL;
1136 }
1137 break;
1138 }
1139 menu = menu->next;
1140 }
1141 if (menu == NULL)
1142 {
Bram Moolenaar342337a2005-07-21 21:11:17 +00001143 EMSG2(_(e_nomenu), name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001144 vim_free(path_name);
1145 return FAIL;
1146 }
1147 name = p;
1148 parent = menu;
1149 menu = menu->children;
1150 }
Bram Moolenaaracbd4422008-08-17 21:44:45 +00001151 vim_free(path_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001152
1153 /* Now we have found the matching menu, and we list the mappings */
1154 /* Highlight title */
1155 MSG_PUTS_TITLE(_("\n--- Menus ---"));
1156
1157 show_menus_recursive(parent, modes, 0);
1158 return OK;
1159}
1160
1161/*
1162 * Recursively show the mappings associated with the menus under the given one
1163 */
1164 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001165show_menus_recursive(vimmenu_T *menu, int modes, int depth)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001166{
1167 int i;
1168 int bit;
1169
1170 if (menu != NULL && (menu->modes & modes) == 0x0)
1171 return;
1172
1173 if (menu != NULL)
1174 {
1175 msg_putchar('\n');
1176 if (got_int) /* "q" hit for "--more--" */
1177 return;
1178 for (i = 0; i < depth; i++)
1179 MSG_PUTS(" ");
1180 if (menu->priority)
1181 {
1182 msg_outnum((long)menu->priority);
1183 MSG_PUTS(" ");
1184 }
1185 /* Same highlighting as for directories!? */
Bram Moolenaar8820b482017-03-16 17:23:31 +01001186 msg_outtrans_attr(menu->name, HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001187 }
1188
1189 if (menu != NULL && menu->children == NULL)
1190 {
1191 for (bit = 0; bit < MENU_MODES; bit++)
1192 if ((menu->modes & modes & (1 << bit)) != 0)
1193 {
1194 msg_putchar('\n');
1195 if (got_int) /* "q" hit for "--more--" */
1196 return;
1197 for (i = 0; i < depth + 2; i++)
1198 MSG_PUTS(" ");
Bram Moolenaar4c5d8152018-10-19 22:36:53 +02001199 msg_puts((char_u*)menu_mode_chars[bit]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001200 if (menu->noremap[bit] == REMAP_NONE)
1201 msg_putchar('*');
1202 else if (menu->noremap[bit] == REMAP_SCRIPT)
1203 msg_putchar('&');
1204 else
1205 msg_putchar(' ');
1206 if (menu->silent[bit])
1207 msg_putchar('s');
1208 else
1209 msg_putchar(' ');
1210 if ((menu->modes & menu->enabled & (1 << bit)) == 0)
1211 msg_putchar('-');
1212 else
1213 msg_putchar(' ');
1214 MSG_PUTS(" ");
1215 if (*menu->strings[bit] == NUL)
Bram Moolenaar8820b482017-03-16 17:23:31 +01001216 msg_puts_attr((char_u *)"<Nop>", HL_ATTR(HLF_8));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001217 else
1218 msg_outtrans_special(menu->strings[bit], FALSE);
1219 }
1220 }
1221 else
1222 {
1223 if (menu == NULL)
1224 {
1225 menu = root_menu;
1226 depth--;
1227 }
1228 else
1229 menu = menu->children;
1230
1231 /* recursively show all children. Skip PopUp[nvoci]. */
1232 for (; menu != NULL && !got_int; menu = menu->next)
1233 if (!menu_is_hidden(menu->dname))
1234 show_menus_recursive(menu, modes, depth + 1);
1235 }
1236}
1237
1238#ifdef FEAT_CMDL_COMPL
1239
1240/*
1241 * Used when expanding menu names.
1242 */
1243static vimmenu_T *expand_menu = NULL;
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02001244static vimmenu_T *expand_menu_alt = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001245static int expand_modes = 0x0;
1246static int expand_emenu; /* TRUE for ":emenu" command */
1247
1248/*
1249 * Work out what to complete when doing command line completion of menu names.
1250 */
1251 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001252set_context_in_menu_cmd(
1253 expand_T *xp,
1254 char_u *cmd,
1255 char_u *arg,
1256 int forceit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001257{
1258 char_u *after_dot;
1259 char_u *p;
1260 char_u *path_name = NULL;
1261 char_u *name;
1262 int unmenu;
1263 vimmenu_T *menu;
1264 int expand_menus;
1265
1266 xp->xp_context = EXPAND_UNSUCCESSFUL;
1267
1268
1269 /* Check for priority numbers, enable and disable */
1270 for (p = arg; *p; ++p)
1271 if (!VIM_ISDIGIT(*p) && *p != '.')
1272 break;
1273
Bram Moolenaar1c465442017-03-12 20:10:05 +01001274 if (!VIM_ISWHITE(*p))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001275 {
1276 if (STRNCMP(arg, "enable", 6) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01001277 && (arg[6] == NUL || VIM_ISWHITE(arg[6])))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001278 p = arg + 6;
1279 else if (STRNCMP(arg, "disable", 7) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01001280 && (arg[7] == NUL || VIM_ISWHITE(arg[7])))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001281 p = arg + 7;
1282 else
1283 p = arg;
1284 }
1285
Bram Moolenaar1c465442017-03-12 20:10:05 +01001286 while (*p != NUL && VIM_ISWHITE(*p))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001287 ++p;
1288
1289 arg = after_dot = p;
1290
Bram Moolenaar1c465442017-03-12 20:10:05 +01001291 for (; *p && !VIM_ISWHITE(*p); ++p)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001292 {
1293 if ((*p == '\\' || *p == Ctrl_V) && p[1] != NUL)
1294 p++;
1295 else if (*p == '.')
1296 after_dot = p + 1;
1297 }
1298
1299 /* ":tearoff" and ":popup" only use menus, not entries */
1300 expand_menus = !((*cmd == 't' && cmd[1] == 'e') || *cmd == 'p');
1301 expand_emenu = (*cmd == 'e');
Bram Moolenaar1c465442017-03-12 20:10:05 +01001302 if (expand_menus && VIM_ISWHITE(*p))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001303 return NULL; /* TODO: check for next command? */
1304 if (*p == NUL) /* Complete the menu name */
1305 {
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02001306 int try_alt_menu = TRUE;
1307
Bram Moolenaar071d4272004-06-13 20:20:40 +00001308 /*
1309 * With :unmenu, you only want to match menus for the appropriate mode.
1310 * With :menu though you might want to add a menu with the same name as
1311 * one in another mode, so match menus from other modes too.
1312 */
1313 expand_modes = get_menu_cmd_modes(cmd, forceit, NULL, &unmenu);
1314 if (!unmenu)
1315 expand_modes = MENU_ALL_MODES;
1316
1317 menu = root_menu;
1318 if (after_dot != arg)
1319 {
1320 path_name = alloc((unsigned)(after_dot - arg));
1321 if (path_name == NULL)
1322 return NULL;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001323 vim_strncpy(path_name, arg, after_dot - arg - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001324 }
1325 name = path_name;
1326 while (name != NULL && *name)
1327 {
1328 p = menu_name_skip(name);
1329 while (menu != NULL)
1330 {
1331 if (menu_name_equal(name, menu))
1332 {
1333 /* Found menu */
1334 if ((*p != NUL && menu->children == NULL)
1335 || ((menu->modes & expand_modes) == 0x0))
1336 {
1337 /*
1338 * Menu path continues, but we have reached a leaf.
1339 * Or menu exists only in another mode.
1340 */
1341 vim_free(path_name);
1342 return NULL;
1343 }
1344 break;
1345 }
1346 menu = menu->next;
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02001347 if (menu == NULL && try_alt_menu)
1348 {
1349 menu = curwin->w_winbar;
1350 try_alt_menu = FALSE;
1351 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001352 }
1353 if (menu == NULL)
1354 {
1355 /* No menu found with the name we were looking for */
1356 vim_free(path_name);
1357 return NULL;
1358 }
1359 name = p;
1360 menu = menu->children;
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02001361 try_alt_menu = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001362 }
Bram Moolenaareb3593b2006-04-22 22:33:57 +00001363 vim_free(path_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001364
1365 xp->xp_context = expand_menus ? EXPAND_MENUNAMES : EXPAND_MENUS;
1366 xp->xp_pattern = after_dot;
1367 expand_menu = menu;
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02001368 if (expand_menu == root_menu)
1369 expand_menu_alt = curwin->w_winbar;
1370 else
1371 expand_menu_alt = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001372 }
1373 else /* We're in the mapping part */
1374 xp->xp_context = EXPAND_NOTHING;
1375 return NULL;
1376}
1377
1378/*
1379 * Function given to ExpandGeneric() to obtain the list of (sub)menus (not
1380 * entries).
1381 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001382 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001383get_menu_name(expand_T *xp UNUSED, int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001384{
1385 static vimmenu_T *menu = NULL;
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02001386 static int did_alt_menu = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001387 char_u *str;
Bram Moolenaar70b11cd2010-05-14 22:24:40 +02001388#ifdef FEAT_MULTI_LANG
1389 static int should_advance = FALSE;
1390#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001391
1392 if (idx == 0) /* first call: start at first item */
Bram Moolenaar70b11cd2010-05-14 22:24:40 +02001393 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001394 menu = expand_menu;
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02001395 did_alt_menu = FALSE;
Bram Moolenaar41375642010-05-16 12:49:27 +02001396#ifdef FEAT_MULTI_LANG
Bram Moolenaar70b11cd2010-05-14 22:24:40 +02001397 should_advance = FALSE;
Bram Moolenaar41375642010-05-16 12:49:27 +02001398#endif
Bram Moolenaar70b11cd2010-05-14 22:24:40 +02001399 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001400
1401 /* Skip PopUp[nvoci]. */
1402 while (menu != NULL && (menu_is_hidden(menu->dname)
1403 || menu_is_separator(menu->dname)
1404 || menu_is_tearoff(menu->dname)
1405 || menu->children == NULL))
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02001406 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001407 menu = menu->next;
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02001408 if (menu == NULL && !did_alt_menu)
1409 {
1410 menu = expand_menu_alt;
1411 did_alt_menu = TRUE;
1412 }
1413 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001414
1415 if (menu == NULL) /* at end of linked list */
1416 return NULL;
1417
1418 if (menu->modes & expand_modes)
Bram Moolenaar70b11cd2010-05-14 22:24:40 +02001419#ifdef FEAT_MULTI_LANG
1420 if (should_advance)
1421 str = menu->en_dname;
1422 else
1423 {
1424#endif
1425 str = menu->dname;
1426#ifdef FEAT_MULTI_LANG
1427 if (menu->en_dname == NULL)
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001428 should_advance = TRUE;
Bram Moolenaar70b11cd2010-05-14 22:24:40 +02001429 }
1430#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001431 else
1432 str = (char_u *)"";
1433
Bram Moolenaar70b11cd2010-05-14 22:24:40 +02001434#ifdef FEAT_MULTI_LANG
1435 if (should_advance)
1436#endif
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02001437 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001438 /* Advance to next menu entry. */
1439 menu = menu->next;
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02001440 if (menu == NULL && !did_alt_menu)
1441 {
1442 menu = expand_menu_alt;
1443 did_alt_menu = TRUE;
1444 }
1445 }
Bram Moolenaar70b11cd2010-05-14 22:24:40 +02001446
1447#ifdef FEAT_MULTI_LANG
1448 should_advance = !should_advance;
1449#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001450
1451 return str;
1452}
1453
1454/*
1455 * Function given to ExpandGeneric() to obtain the list of menus and menu
1456 * entries.
1457 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001458 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001459get_menu_names(expand_T *xp UNUSED, int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001460{
1461 static vimmenu_T *menu = NULL;
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02001462 static int did_alt_menu = FALSE;
Bram Moolenaaref9d6aa2011-04-11 16:56:35 +02001463#define TBUFFER_LEN 256
1464 static char_u tbuffer[TBUFFER_LEN]; /*hack*/
Bram Moolenaar071d4272004-06-13 20:20:40 +00001465 char_u *str;
Bram Moolenaar70b11cd2010-05-14 22:24:40 +02001466#ifdef FEAT_MULTI_LANG
1467 static int should_advance = FALSE;
1468#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001469
1470 if (idx == 0) /* first call: start at first item */
Bram Moolenaar70b11cd2010-05-14 22:24:40 +02001471 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001472 menu = expand_menu;
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02001473 did_alt_menu = FALSE;
Bram Moolenaar41375642010-05-16 12:49:27 +02001474#ifdef FEAT_MULTI_LANG
Bram Moolenaar70b11cd2010-05-14 22:24:40 +02001475 should_advance = FALSE;
Bram Moolenaar41375642010-05-16 12:49:27 +02001476#endif
Bram Moolenaar70b11cd2010-05-14 22:24:40 +02001477 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001478
1479 /* Skip Browse-style entries, popup menus and separators. */
1480 while (menu != NULL
1481 && ( menu_is_hidden(menu->dname)
1482 || (expand_emenu && menu_is_separator(menu->dname))
1483 || menu_is_tearoff(menu->dname)
1484#ifndef FEAT_BROWSE
1485 || menu->dname[STRLEN(menu->dname) - 1] == '.'
1486#endif
1487 ))
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02001488 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001489 menu = menu->next;
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02001490 if (menu == NULL && !did_alt_menu)
1491 {
1492 menu = expand_menu_alt;
1493 did_alt_menu = TRUE;
1494 }
1495 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001496
1497 if (menu == NULL) /* at end of linked list */
1498 return NULL;
1499
1500 if (menu->modes & expand_modes)
1501 {
1502 if (menu->children != NULL)
1503 {
Bram Moolenaar70b11cd2010-05-14 22:24:40 +02001504#ifdef FEAT_MULTI_LANG
1505 if (should_advance)
Bram Moolenaaref9d6aa2011-04-11 16:56:35 +02001506 vim_strncpy(tbuffer, menu->en_dname, TBUFFER_LEN - 2);
Bram Moolenaar70b11cd2010-05-14 22:24:40 +02001507 else
1508 {
1509#endif
Bram Moolenaaref9d6aa2011-04-11 16:56:35 +02001510 vim_strncpy(tbuffer, menu->dname, TBUFFER_LEN - 2);
Bram Moolenaar70b11cd2010-05-14 22:24:40 +02001511#ifdef FEAT_MULTI_LANG
1512 if (menu->en_dname == NULL)
1513 should_advance = TRUE;
1514 }
1515#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001516 /* hack on menu separators: use a 'magic' char for the separator
1517 * so that '.' in names gets escaped properly */
1518 STRCAT(tbuffer, "\001");
1519 str = tbuffer;
1520 }
1521 else
Bram Moolenaar70b11cd2010-05-14 22:24:40 +02001522#ifdef FEAT_MULTI_LANG
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001523 {
1524 if (should_advance)
1525 str = menu->en_dname;
1526 else
1527 {
Bram Moolenaar70b11cd2010-05-14 22:24:40 +02001528#endif
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001529 str = menu->dname;
Bram Moolenaar70b11cd2010-05-14 22:24:40 +02001530#ifdef FEAT_MULTI_LANG
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001531 if (menu->en_dname == NULL)
1532 should_advance = TRUE;
1533 }
1534 }
Bram Moolenaar70b11cd2010-05-14 22:24:40 +02001535#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001536 }
1537 else
1538 str = (char_u *)"";
1539
Bram Moolenaar70b11cd2010-05-14 22:24:40 +02001540#ifdef FEAT_MULTI_LANG
1541 if (should_advance)
1542#endif
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02001543 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001544 /* Advance to next menu entry. */
1545 menu = menu->next;
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02001546 if (menu == NULL && !did_alt_menu)
1547 {
1548 menu = expand_menu_alt;
1549 did_alt_menu = TRUE;
1550 }
1551 }
Bram Moolenaar70b11cd2010-05-14 22:24:40 +02001552
1553#ifdef FEAT_MULTI_LANG
1554 should_advance = !should_advance;
1555#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001556
1557 return str;
1558}
1559#endif /* FEAT_CMDL_COMPL */
1560
1561/*
1562 * Skip over this element of the menu path and return the start of the next
1563 * element. Any \ and ^Vs are removed from the current element.
Bram Moolenaar342337a2005-07-21 21:11:17 +00001564 * "name" may be modified.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001565 */
1566 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001567menu_name_skip(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001568{
1569 char_u *p;
1570
Bram Moolenaar91acfff2017-03-12 19:22:36 +01001571 for (p = name; *p && *p != '.'; MB_PTR_ADV(p))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001572 {
1573 if (*p == '\\' || *p == Ctrl_V)
1574 {
Bram Moolenaar8c8de832008-06-24 22:58:06 +00001575 STRMOVE(p, p + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001576 if (*p == NUL)
1577 break;
1578 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001579 }
1580 if (*p)
1581 *p++ = NUL;
1582 return p;
1583}
1584
1585/*
1586 * Return TRUE when "name" matches with menu "menu". The name is compared in
1587 * two ways: raw menu name and menu name without '&'. ignore part after a TAB.
1588 */
1589 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001590menu_name_equal(char_u *name, vimmenu_T *menu)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001591{
Bram Moolenaar41375642010-05-16 12:49:27 +02001592#ifdef FEAT_MULTI_LANG
Bram Moolenaar70b11cd2010-05-14 22:24:40 +02001593 if (menu->en_name != NULL
Bram Moolenaard91f7042011-01-04 17:49:32 +01001594 && (menu_namecmp(name, menu->en_name)
1595 || menu_namecmp(name, menu->en_dname)))
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001596 return TRUE;
Bram Moolenaar41375642010-05-16 12:49:27 +02001597#endif
Bram Moolenaar70b11cd2010-05-14 22:24:40 +02001598 return menu_namecmp(name, menu->name) || menu_namecmp(name, menu->dname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001599}
1600
1601 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001602menu_namecmp(char_u *name, char_u *mname)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001603{
1604 int i;
1605
1606 for (i = 0; name[i] != NUL && name[i] != TAB; ++i)
1607 if (name[i] != mname[i])
1608 break;
1609 return ((name[i] == NUL || name[i] == TAB)
1610 && (mname[i] == NUL || mname[i] == TAB));
1611}
1612
1613/*
1614 * Return the modes specified by the given menu command (eg :menu! returns
1615 * MENU_CMDLINE_MODE | MENU_INSERT_MODE).
1616 * If "noremap" is not NULL, then the flag it points to is set according to
1617 * whether the command is a "nore" command.
1618 * If "unmenu" is not NULL, then the flag it points to is set according to
1619 * whether the command is an "unmenu" command.
1620 */
1621 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001622get_menu_cmd_modes(
1623 char_u *cmd,
1624 int forceit, /* Was there a "!" after the command? */
1625 int *noremap,
1626 int *unmenu)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001627{
1628 int modes;
1629
1630 switch (*cmd++)
1631 {
1632 case 'v': /* vmenu, vunmenu, vnoremenu */
Bram Moolenaarb3656ed2006-03-20 21:59:49 +00001633 modes = MENU_VISUAL_MODE | MENU_SELECT_MODE;
1634 break;
1635 case 'x': /* xmenu, xunmenu, xnoremenu */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001636 modes = MENU_VISUAL_MODE;
1637 break;
Bram Moolenaarb3656ed2006-03-20 21:59:49 +00001638 case 's': /* smenu, sunmenu, snoremenu */
1639 modes = MENU_SELECT_MODE;
1640 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001641 case 'o': /* omenu */
1642 modes = MENU_OP_PENDING_MODE;
1643 break;
1644 case 'i': /* imenu */
1645 modes = MENU_INSERT_MODE;
1646 break;
1647 case 't':
Bram Moolenaar4c5d8152018-10-19 22:36:53 +02001648 if (*cmd == 'l') /* tlmenu, tlunmenu, tlnoremenu */
1649 {
1650 modes = MENU_TERMINAL_MODE;
1651 ++cmd;
1652 break;
1653 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001654 modes = MENU_TIP_MODE; /* tmenu */
1655 break;
1656 case 'c': /* cmenu */
1657 modes = MENU_CMDLINE_MODE;
1658 break;
1659 case 'a': /* amenu */
1660 modes = MENU_INSERT_MODE | MENU_CMDLINE_MODE | MENU_NORMAL_MODE
Bram Moolenaarb3656ed2006-03-20 21:59:49 +00001661 | MENU_VISUAL_MODE | MENU_SELECT_MODE
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00001662 | MENU_OP_PENDING_MODE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001663 break;
1664 case 'n':
1665 if (*cmd != 'o') /* nmenu, not noremenu */
1666 {
1667 modes = MENU_NORMAL_MODE;
1668 break;
1669 }
1670 /* FALLTHROUGH */
1671 default:
1672 --cmd;
1673 if (forceit) /* menu!! */
1674 modes = MENU_INSERT_MODE | MENU_CMDLINE_MODE;
1675 else /* menu */
Bram Moolenaarb3656ed2006-03-20 21:59:49 +00001676 modes = MENU_NORMAL_MODE | MENU_VISUAL_MODE | MENU_SELECT_MODE
Bram Moolenaar071d4272004-06-13 20:20:40 +00001677 | MENU_OP_PENDING_MODE;
1678 }
1679
1680 if (noremap != NULL)
1681 *noremap = (*cmd == 'n' ? REMAP_NONE : REMAP_YES);
1682 if (unmenu != NULL)
1683 *unmenu = (*cmd == 'u');
1684 return modes;
1685}
1686
1687/*
1688 * Modify a menu name starting with "PopUp" to include the mode character.
1689 * Returns the name in allocated memory (NULL for failure).
1690 */
1691 static char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001692popup_mode_name(char_u *name, int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001693{
1694 char_u *p;
1695 int len = (int)STRLEN(name);
Bram Moolenaar4c5d8152018-10-19 22:36:53 +02001696 char *mode_chars = menu_mode_chars[idx];
1697 int mode_chars_len = (int)strlen(mode_chars);
1698 int i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001699
Bram Moolenaar4c5d8152018-10-19 22:36:53 +02001700 p = vim_strnsave(name, len + mode_chars_len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001701 if (p != NULL)
1702 {
Bram Moolenaar4c5d8152018-10-19 22:36:53 +02001703 mch_memmove(p + 5 + mode_chars_len, p + 5, (size_t)(len - 4));
1704 for (i = 0; i < mode_chars_len; ++i)
1705 {
1706 p[5 + i] = menu_mode_chars[idx][i];
1707 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001708 }
1709 return p;
1710}
1711
1712#if defined(FEAT_GUI) || defined(PROTO)
1713/*
1714 * Return the index into the menu->strings or menu->noremap arrays for the
1715 * current state. Returns MENU_INDEX_INVALID if there is no mapping for the
1716 * given menu in the current mode.
1717 */
1718 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001719get_menu_index(vimmenu_T *menu, int state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001720{
1721 int idx;
1722
1723 if ((state & INSERT))
1724 idx = MENU_INDEX_INSERT;
1725 else if (state & CMDLINE)
1726 idx = MENU_INDEX_CMDLINE;
Bram Moolenaar4c5d8152018-10-19 22:36:53 +02001727#ifdef FEAT_TERMINAL
1728 else if (term_use_loop())
1729 idx = MENU_INDEX_TERMINAL;
1730#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001731 else if (VIsual_active)
Bram Moolenaarb3656ed2006-03-20 21:59:49 +00001732 {
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00001733 if (VIsual_select)
1734 idx = MENU_INDEX_SELECT;
1735 else
1736 idx = MENU_INDEX_VISUAL;
Bram Moolenaarb3656ed2006-03-20 21:59:49 +00001737 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001738 else if (state == HITRETURN || state == ASKMORE)
1739 idx = MENU_INDEX_CMDLINE;
1740 else if (finish_op)
1741 idx = MENU_INDEX_OP_PENDING;
1742 else if ((state & NORMAL))
1743 idx = MENU_INDEX_NORMAL;
1744 else
1745 idx = MENU_INDEX_INVALID;
1746
1747 if (idx != MENU_INDEX_INVALID && menu->strings[idx] == NULL)
1748 idx = MENU_INDEX_INVALID;
1749 return idx;
1750}
1751#endif
1752
1753/*
1754 * Duplicate the menu item text and then process to see if a mnemonic key
1755 * and/or accelerator text has been identified.
1756 * Returns a pointer to allocated memory, or NULL for failure.
1757 * If mnemonic != NULL, *mnemonic is set to the character after the first '&'.
1758 * If actext != NULL, *actext is set to the text after the first TAB.
1759 */
1760 static char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001761menu_text(char_u *str, int *mnemonic, char_u **actext)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001762{
1763 char_u *p;
1764 char_u *text;
1765
1766 /* Locate accelerator text, after the first TAB */
1767 p = vim_strchr(str, TAB);
1768 if (p != NULL)
1769 {
1770 if (actext != NULL)
1771 *actext = vim_strsave(p + 1);
1772 text = vim_strnsave(str, (int)(p - str));
1773 }
1774 else
1775 text = vim_strsave(str);
1776
1777 /* Find mnemonic characters "&a" and reduce "&&" to "&". */
1778 for (p = text; p != NULL; )
1779 {
1780 p = vim_strchr(p, '&');
1781 if (p != NULL)
1782 {
1783 if (p[1] == NUL) /* trailing "&" */
1784 break;
1785 if (mnemonic != NULL && p[1] != '&')
1786#if !defined(__MVS__) || defined(MOTIF390_MNEMONIC_FIXED)
1787 *mnemonic = p[1];
1788#else
1789 {
1790 /*
1791 * Well there is a bug in the Motif libraries on OS390 Unix.
1792 * The mnemonic keys needs to be converted to ASCII values
1793 * first.
1794 * This behavior has been seen in 2.8 and 2.9.
1795 */
1796 char c = p[1];
1797 __etoa_l(&c, 1);
1798 *mnemonic = c;
1799 }
1800#endif
Bram Moolenaar8c8de832008-06-24 22:58:06 +00001801 STRMOVE(p, p + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001802 p = p + 1;
1803 }
1804 }
1805 return text;
1806}
1807
1808/*
1809 * Return TRUE if "name" can be a menu in the MenuBar.
1810 */
1811 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001812menu_is_menubar(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001813{
1814 return (!menu_is_popup(name)
1815 && !menu_is_toolbar(name)
Bram Moolenaar378daf82017-09-23 23:58:28 +02001816 && !menu_is_winbar(name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001817 && *name != MNU_HIDDEN_CHAR);
1818}
1819
1820/*
1821 * Return TRUE if "name" is a popup menu name.
1822 */
1823 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001824menu_is_popup(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001825{
1826 return (STRNCMP(name, "PopUp", 5) == 0);
1827}
1828
1829#if (defined(FEAT_GUI_MOTIF) && (XmVersion <= 1002)) || defined(PROTO)
1830/*
1831 * Return TRUE if "name" is part of a popup menu.
1832 */
1833 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001834menu_is_child_of_popup(vimmenu_T *menu)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001835{
1836 while (menu->parent != NULL)
1837 menu = menu->parent;
1838 return menu_is_popup(menu->name);
1839}
1840#endif
1841
1842/*
1843 * Return TRUE if "name" is a toolbar menu name.
1844 */
1845 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001846menu_is_toolbar(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001847{
1848 return (STRNCMP(name, "ToolBar", 7) == 0);
1849}
1850
1851/*
1852 * Return TRUE if the name is a menu separator identifier: Starts and ends
1853 * with '-'
1854 */
1855 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001856menu_is_separator(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001857{
1858 return (name[0] == '-' && name[STRLEN(name) - 1] == '-');
1859}
1860
1861/*
1862 * Return TRUE if the menu is hidden: Starts with ']'
1863 */
1864 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001865menu_is_hidden(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001866{
1867 return (name[0] == ']') || (menu_is_popup(name) && name[5] != NUL);
1868}
1869
1870#if defined(FEAT_CMDL_COMPL) \
1871 || (defined(FEAT_GUI_W32) && defined(FEAT_TEAROFF))
1872/*
1873 * Return TRUE if the menu is the tearoff menu.
1874 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001875 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001876menu_is_tearoff(char_u *name UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001877{
1878#ifdef FEAT_GUI
1879 return (STRCMP(name, TEAR_STRING) == 0);
1880#else
1881 return FALSE;
1882#endif
1883}
1884#endif
1885
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001886#if defined(FEAT_GUI) || defined(FEAT_TERM_POPUP_MENU) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001887
1888 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001889get_menu_mode(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001890{
Bram Moolenaar4c5d8152018-10-19 22:36:53 +02001891#ifdef FEAT_TERMINAL
1892 if (term_use_loop())
1893 {
1894 return MENU_INDEX_TERMINAL;
1895 }
1896#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001897 if (VIsual_active)
Bram Moolenaarb3656ed2006-03-20 21:59:49 +00001898 {
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00001899 if (VIsual_select)
1900 return MENU_INDEX_SELECT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001901 return MENU_INDEX_VISUAL;
Bram Moolenaarb3656ed2006-03-20 21:59:49 +00001902 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001903 if (State & INSERT)
1904 return MENU_INDEX_INSERT;
1905 if ((State & CMDLINE) || State == ASKMORE || State == HITRETURN)
1906 return MENU_INDEX_CMDLINE;
1907 if (finish_op)
1908 return MENU_INDEX_OP_PENDING;
1909 if (State & NORMAL)
1910 return MENU_INDEX_NORMAL;
1911 if (State & LANGMAP) /* must be a "r" command, like Insert mode */
1912 return MENU_INDEX_INSERT;
1913 return MENU_INDEX_INVALID;
1914}
1915
Bram Moolenaar29a2c082018-03-05 21:06:23 +01001916 int
1917get_menu_mode_flag(void)
1918{
1919 int mode = get_menu_mode();
1920
1921 if (mode == MENU_INDEX_INVALID)
1922 return 0;
1923 return 1 << mode;
1924}
1925
Bram Moolenaar071d4272004-06-13 20:20:40 +00001926/*
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001927 * Display the Special "PopUp" menu as a pop-up at the current mouse
1928 * position. The "PopUpn" menu is for Normal mode, "PopUpi" for Insert mode,
1929 * etc.
1930 */
1931 void
1932show_popupmenu(void)
1933{
1934 vimmenu_T *menu;
Bram Moolenaar4c5d8152018-10-19 22:36:53 +02001935 int menu_mode;
1936 char* mode;
1937 int mode_len;
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001938
Bram Moolenaar4c5d8152018-10-19 22:36:53 +02001939 menu_mode = get_menu_mode();
1940 if (menu_mode == MENU_INDEX_INVALID)
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001941 return;
Bram Moolenaar4c5d8152018-10-19 22:36:53 +02001942 mode = menu_mode_chars[menu_mode];
1943 mode_len = (int)strlen(mode);
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001944
Bram Moolenaar4c5d8152018-10-19 22:36:53 +02001945 apply_autocmds(EVENT_MENUPOPUP, (char_u*)mode, NULL, FALSE, curbuf);
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001946
1947 for (menu = root_menu; menu != NULL; menu = menu->next)
Bram Moolenaar4c5d8152018-10-19 22:36:53 +02001948 if (STRNCMP("PopUp", menu->name, 5) == 0 && STRNCMP(menu->name + 5, mode, mode_len) == 0)
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001949 break;
1950
1951 /* Only show a popup when it is defined and has entries */
1952 if (menu != NULL && menu->children != NULL)
1953 {
1954# if defined(FEAT_GUI)
1955 if (gui.in_use)
1956 {
1957 /* Update the menus now, in case the MenuPopup autocommand did
1958 * anything. */
1959 gui_update_menus(0);
1960 gui_mch_show_popupmenu(menu);
1961 }
1962# endif
1963# if defined(FEAT_GUI) && defined(FEAT_TERM_POPUP_MENU)
1964 else
1965# endif
1966# if defined(FEAT_TERM_POPUP_MENU)
1967 pum_show_popupmenu(menu);
1968# endif
1969 }
1970}
1971#endif
1972
1973#if defined(FEAT_GUI) || defined(PROTO)
1974
1975/*
Bram Moolenaar968bbbe2006-08-16 19:41:08 +00001976 * Check that a pointer appears in the menu tree. Used to protect from using
1977 * a menu that was deleted after it was selected but before the event was
1978 * handled.
1979 * Return OK or FAIL. Used recursively.
1980 */
1981 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001982check_menu_pointer(vimmenu_T *root, vimmenu_T *menu_to_check)
Bram Moolenaar968bbbe2006-08-16 19:41:08 +00001983{
1984 vimmenu_T *p;
1985
1986 for (p = root; p != NULL; p = p->next)
1987 if (p == menu_to_check
1988 || (p->children != NULL
1989 && check_menu_pointer(p->children, menu_to_check) == OK))
1990 return OK;
1991 return FAIL;
1992}
1993
1994/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00001995 * After we have started the GUI, then we can create any menus that have been
1996 * defined. This is done once here. add_menu_path() may have already been
1997 * called to define these menus, and may be called again. This function calls
1998 * itself recursively. Should be called at the top level with:
Bram Moolenaara06ecab2016-07-16 14:47:36 +02001999 * gui_create_initial_menus(root_menu);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002000 */
2001 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002002gui_create_initial_menus(vimmenu_T *menu)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002003{
2004 int idx = 0;
2005
2006 while (menu != NULL)
2007 {
2008 /* Don't add a menu when only a tip was defined. */
2009 if (menu->modes & MENU_ALL_MODES)
2010 {
2011 if (menu->children != NULL)
2012 {
2013 gui_mch_add_menu(menu, idx);
2014 gui_create_initial_menus(menu->children);
2015 }
2016 else
2017 gui_mch_add_menu_item(menu, idx);
2018 }
2019 menu = menu->next;
2020 ++idx;
2021 }
2022}
2023
2024/*
2025 * Used recursively by gui_update_menus (see below)
2026 */
2027 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002028gui_update_menus_recurse(vimmenu_T *menu, int mode)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002029{
2030 int grey;
2031
2032 while (menu)
2033 {
2034 if ((menu->modes & menu->enabled & mode)
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01002035# if defined(FEAT_GUI_W32) && defined(FEAT_TEAROFF)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002036 || menu_is_tearoff(menu->dname)
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01002037# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002038 )
2039 grey = FALSE;
2040 else
2041 grey = TRUE;
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01002042# ifdef FEAT_GUI_ATHENA
Bram Moolenaar071d4272004-06-13 20:20:40 +00002043 /* Hiding menus doesn't work for Athena, it can cause a crash. */
2044 gui_mch_menu_grey(menu, grey);
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01002045# else
Bram Moolenaar071d4272004-06-13 20:20:40 +00002046 /* Never hide a toplevel menu, it may make the menubar resize or
2047 * disappear. Same problem for ToolBar items. */
2048 if (vim_strchr(p_go, GO_GREY) != NULL || menu->parent == NULL
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01002049# ifdef FEAT_TOOLBAR
Bram Moolenaar071d4272004-06-13 20:20:40 +00002050 || menu_is_toolbar(menu->parent->name)
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01002051# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002052 )
2053 gui_mch_menu_grey(menu, grey);
2054 else
2055 gui_mch_menu_hidden(menu, grey);
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01002056# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002057 gui_update_menus_recurse(menu->children, mode);
2058 menu = menu->next;
2059 }
2060}
2061
2062/*
2063 * Make sure only the valid menu items appear for this mode. If
2064 * force_menu_update is not TRUE, then we only do this if the mode has changed
2065 * since last time. If "modes" is not 0, then we use these modes instead.
2066 */
2067 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002068gui_update_menus(int modes)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002069{
2070 static int prev_mode = -1;
2071 int mode = 0;
2072
2073 if (modes != 0x0)
2074 mode = modes;
2075 else
Bram Moolenaar29a2c082018-03-05 21:06:23 +01002076 mode = get_menu_mode_flag();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002077
2078 if (force_menu_update || mode != prev_mode)
2079 {
2080 gui_update_menus_recurse(root_menu, mode);
2081 gui_mch_draw_menubar();
2082 prev_mode = mode;
2083 force_menu_update = FALSE;
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01002084# ifdef FEAT_GUI_W32
Bram Moolenaar071d4272004-06-13 20:20:40 +00002085 /* This can leave a tearoff as active window - make sure we
2086 * have the focus <negri>*/
2087 gui_mch_activate_window();
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01002088# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002089 }
2090}
2091
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01002092# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_MOTIF) \
Bram Moolenaar241a8aa2005-12-06 20:04:44 +00002093 || defined(FEAT_GUI_GTK) || defined(FEAT_GUI_PHOTON) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002094/*
2095 * Check if a key is used as a mnemonic for a toplevel menu.
2096 * Case of the key is ignored.
2097 */
2098 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002099gui_is_menu_shortcut(int key)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002100{
2101 vimmenu_T *menu;
2102
2103 if (key < 256)
2104 key = TOLOWER_LOC(key);
2105 for (menu = root_menu; menu != NULL; menu = menu->next)
2106 if (menu->mnemonic == key
2107 || (menu->mnemonic < 256 && TOLOWER_LOC(menu->mnemonic) == key))
2108 return TRUE;
2109 return FALSE;
2110}
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01002111# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002112#endif /* FEAT_GUI */
2113
2114#if (defined(FEAT_GUI_W32) && defined(FEAT_TEAROFF)) || defined(PROTO)
2115
2116/*
2117 * Deal with tearoff items that are added like a menu item.
2118 * Currently only for Win32 GUI. Others may follow later.
2119 */
2120
2121 void
2122gui_mch_toggle_tearoffs(int enable)
2123{
2124 int pri_tab[MENUDEPTH + 1];
2125 int i;
2126
2127 if (enable)
2128 {
2129 for (i = 0; i < MENUDEPTH; ++i)
2130 pri_tab[i] = 500;
2131 pri_tab[MENUDEPTH] = -1;
2132 gui_create_tearoffs_recurse(root_menu, (char_u *)"", pri_tab, 0);
2133 }
2134 else
2135 gui_destroy_tearoffs_recurse(root_menu);
2136 s_tearoffs = enable;
2137}
2138
2139/*
2140 * Recursively add tearoff items
2141 */
2142 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002143gui_create_tearoffs_recurse(
2144 vimmenu_T *menu,
2145 const char_u *pname,
2146 int *pri_tab,
2147 int pri_idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002148{
2149 char_u *newpname = NULL;
2150 int len;
2151 char_u *s;
2152 char_u *d;
2153
2154 if (pri_tab[pri_idx + 1] != -1)
2155 ++pri_idx;
2156 while (menu != NULL)
2157 {
2158 if (menu->children != NULL && menu_is_menubar(menu->name))
2159 {
2160 /* Add the menu name to the menu path. Insert a backslash before
2161 * dots (it's used to separate menu names). */
2162 len = (int)STRLEN(pname) + (int)STRLEN(menu->name);
2163 for (s = menu->name; *s; ++s)
2164 if (*s == '.' || *s == '\\')
2165 ++len;
2166 newpname = alloc(len + TEAR_LEN + 2);
2167 if (newpname != NULL)
2168 {
2169 STRCPY(newpname, pname);
2170 d = newpname + STRLEN(newpname);
2171 for (s = menu->name; *s; ++s)
2172 {
2173 if (*s == '.' || *s == '\\')
2174 *d++ = '\\';
2175 *d++ = *s;
2176 }
2177 *d = NUL;
2178
2179 /* check if tearoff already exists */
2180 if (STRCMP(menu->children->name, TEAR_STRING) != 0)
2181 {
2182 gui_add_tearoff(newpname, pri_tab, pri_idx - 1);
2183 *d = NUL; /* remove TEAR_STRING */
2184 }
2185
2186 STRCAT(newpname, ".");
2187 gui_create_tearoffs_recurse(menu->children, newpname,
2188 pri_tab, pri_idx);
2189 vim_free(newpname);
2190 }
2191 }
2192 menu = menu->next;
2193 }
2194}
2195
2196/*
2197 * Add tear-off menu item for a submenu.
2198 * "tearpath" is the menu path, and must have room to add TEAR_STRING.
2199 */
2200 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002201gui_add_tearoff(char_u *tearpath, int *pri_tab, int pri_idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002202{
2203 char_u *tbuf;
2204 int t;
2205 vimmenu_T menuarg;
2206
2207 tbuf = alloc(5 + (unsigned int)STRLEN(tearpath));
2208 if (tbuf != NULL)
2209 {
2210 tbuf[0] = K_SPECIAL;
2211 tbuf[1] = K_SECOND(K_TEAROFF);
2212 tbuf[2] = K_THIRD(K_TEAROFF);
2213 STRCPY(tbuf + 3, tearpath);
2214 STRCAT(tbuf + 3, "\r");
2215
2216 STRCAT(tearpath, ".");
2217 STRCAT(tearpath, TEAR_STRING);
2218
2219 /* Priority of tear-off is always 1 */
2220 t = pri_tab[pri_idx + 1];
2221 pri_tab[pri_idx + 1] = 1;
2222
2223#ifdef FEAT_TOOLBAR
2224 menuarg.iconfile = NULL;
2225 menuarg.iconidx = -1;
2226 menuarg.icon_builtin = FALSE;
2227#endif
2228 menuarg.noremap[0] = REMAP_NONE;
2229 menuarg.silent[0] = TRUE;
2230
2231 menuarg.modes = MENU_ALL_MODES;
2232 add_menu_path(tearpath, &menuarg, pri_tab, tbuf, FALSE);
2233
2234 menuarg.modes = MENU_TIP_MODE;
2235 add_menu_path(tearpath, &menuarg, pri_tab,
2236 (char_u *)_("Tear off this menu"), FALSE);
2237
2238 pri_tab[pri_idx + 1] = t;
2239 vim_free(tbuf);
2240 }
2241}
2242
2243/*
2244 * Recursively destroy tearoff items
2245 */
2246 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002247gui_destroy_tearoffs_recurse(vimmenu_T *menu)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002248{
2249 while (menu)
2250 {
2251 if (menu->children)
2252 {
2253 /* check if tearoff exists */
2254 if (STRCMP(menu->children->name, TEAR_STRING) == 0)
2255 {
2256 /* Disconnect the item and free the memory */
2257 free_menu(&menu->children);
2258 }
2259 if (menu->children != NULL) /* if not the last one */
2260 gui_destroy_tearoffs_recurse(menu->children);
2261 }
2262 menu = menu->next;
2263 }
2264}
2265
2266#endif /* FEAT_GUI_W32 && FEAT_TEAROFF */
2267
2268/*
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02002269 * Execute "menu". Use by ":emenu" and the window toolbar.
2270 * "eap" is NULL for the window toolbar.
Bram Moolenaar4c5d8152018-10-19 22:36:53 +02002271 * "mode_idx" specifies a MENU_INDEX_ value, use -1 to depend on the current
2272 * state.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002273 */
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01002274 void
Bram Moolenaar4c5d8152018-10-19 22:36:53 +02002275execute_menu(exarg_T *eap, vimmenu_T *menu, int mode_idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002276{
Bram Moolenaar4c5d8152018-10-19 22:36:53 +02002277 int idx = mode_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002278
Bram Moolenaar4c5d8152018-10-19 22:36:53 +02002279 if (idx < 0)
2280 {
2281 /* Use the Insert mode entry when returning to Insert mode. */
2282 if (restart_edit
Bram Moolenaar4463f292005-09-25 22:20:24 +00002283#ifdef FEAT_EVAL
Bram Moolenaar4c5d8152018-10-19 22:36:53 +02002284 && !current_sctx.sc_sid
Bram Moolenaar4463f292005-09-25 22:20:24 +00002285#endif
Bram Moolenaar4c5d8152018-10-19 22:36:53 +02002286 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00002287 {
Bram Moolenaar4c5d8152018-10-19 22:36:53 +02002288 idx = MENU_INDEX_INSERT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002289 }
Bram Moolenaar4c5d8152018-10-19 22:36:53 +02002290#ifdef FEAT_TERMINAL
2291 else if (term_use_loop())
Bram Moolenaar071d4272004-06-13 20:20:40 +00002292 {
Bram Moolenaar4c5d8152018-10-19 22:36:53 +02002293 idx = MENU_INDEX_TERMINAL;
2294 }
2295#endif
2296 else if (VIsual_active)
2297 {
2298 idx = MENU_INDEX_VISUAL;
2299 }
2300 else if (eap != NULL && eap->addr_count)
2301 {
2302 pos_T tpos;
2303
2304 idx = MENU_INDEX_VISUAL;
2305
2306 /* GEDDES: This is not perfect - but it is a
2307 * quick way of detecting whether we are doing this from a
2308 * selection - see if the range matches up with the visual
2309 * select start and end. */
2310 if ((curbuf->b_visual.vi_start.lnum == eap->line1)
2311 && (curbuf->b_visual.vi_end.lnum) == eap->line2)
2312 {
2313 /* Set it up for visual mode - equivalent to gv. */
2314 VIsual_mode = curbuf->b_visual.vi_mode;
2315 tpos = curbuf->b_visual.vi_end;
2316 curwin->w_cursor = curbuf->b_visual.vi_start;
2317 curwin->w_curswant = curbuf->b_visual.vi_curswant;
2318 }
2319 else
2320 {
2321 /* Set it up for line-wise visual mode */
2322 VIsual_mode = 'V';
2323 curwin->w_cursor.lnum = eap->line1;
2324 curwin->w_cursor.col = 1;
2325 tpos.lnum = eap->line2;
2326 tpos.col = MAXCOL;
Bram Moolenaar261bfea2006-03-01 22:12:31 +00002327#ifdef FEAT_VIRTUALEDIT
Bram Moolenaar4c5d8152018-10-19 22:36:53 +02002328 tpos.coladd = 0;
Bram Moolenaar261bfea2006-03-01 22:12:31 +00002329#endif
Bram Moolenaar4c5d8152018-10-19 22:36:53 +02002330 }
2331
2332 /* Activate visual mode */
2333 VIsual_active = TRUE;
2334 VIsual_reselect = TRUE;
2335 check_cursor();
2336 VIsual = curwin->w_cursor;
2337 curwin->w_cursor = tpos;
2338
2339 check_cursor();
2340
2341 /* Adjust the cursor to make sure it is in the correct pos
2342 * for exclusive mode */
2343 if (*p_sel == 'e' && gchar_cursor() != NUL)
2344 ++curwin->w_cursor.col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002345 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002346 }
Bram Moolenaara21a6a92017-09-23 16:33:50 +02002347
2348 /* For the WinBar menu always use the Normal mode menu. */
2349 if (idx == -1 || eap == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002350 idx = MENU_INDEX_NORMAL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002351
2352 if (idx != MENU_INDEX_INVALID && menu->strings[idx] != NULL)
2353 {
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002354 /* When executing a script or function execute the commands right now.
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02002355 * Also for the window toolbar.
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002356 * Otherwise put them in the typeahead buffer. */
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02002357 if (eap == NULL
Bram Moolenaar9c4b4ab2006-12-05 20:29:56 +00002358#ifdef FEAT_EVAL
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02002359 || current_sctx.sc_sid != 0
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02002360#endif
2361 )
Bram Moolenaara21a6a92017-09-23 16:33:50 +02002362 {
2363 save_state_T save_state;
2364
2365 ++ex_normal_busy;
2366 if (save_current_state(&save_state))
2367 exec_normal_cmd(menu->strings[idx], menu->noremap[idx],
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002368 menu->silent[idx]);
Bram Moolenaara21a6a92017-09-23 16:33:50 +02002369 restore_current_state(&save_state);
2370 --ex_normal_busy;
2371 }
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002372 else
2373 ins_typebuf(menu->strings[idx], menu->noremap[idx], 0,
Bram Moolenaar071d4272004-06-13 20:20:40 +00002374 TRUE, menu->silent[idx]);
2375 }
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02002376 else if (eap != NULL)
Bram Moolenaar4c5d8152018-10-19 22:36:53 +02002377 {
2378 char_u *mode;
2379
2380 switch (idx)
2381 {
2382 case MENU_INDEX_VISUAL:
2383 mode = (char_u *)"Visual";
2384 break;
2385 case MENU_INDEX_SELECT:
2386 mode = (char_u *)"Select";
2387 break;
2388 case MENU_INDEX_OP_PENDING:
2389 mode = (char_u *)"Op-pending";
2390 break;
2391 case MENU_INDEX_TERMINAL:
2392 mode = (char_u *)"Terminal";
2393 break;
2394 case MENU_INDEX_INSERT:
2395 mode = (char_u *)"Insert";
2396 break;
2397 case MENU_INDEX_CMDLINE:
2398 mode = (char_u *)"Cmdline";
2399 break;
2400 // case MENU_INDEX_TIP: cannot happen
2401 default:
2402 mode = (char_u *)"Normal";
2403 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002404 EMSG2(_("E335: Menu not defined for %s mode"), mode);
Bram Moolenaar4c5d8152018-10-19 22:36:53 +02002405 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002406}
2407
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02002408/*
2409 * Given a menu descriptor, e.g. "File.New", find it in the menu hierarchy and
2410 * execute it.
2411 */
2412 void
2413ex_emenu(exarg_T *eap)
2414{
2415 vimmenu_T *menu;
2416 char_u *name;
2417 char_u *saved_name;
Bram Moolenaar4c5d8152018-10-19 22:36:53 +02002418 char_u *arg = eap->arg;
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02002419 char_u *p;
Bram Moolenaar4c5d8152018-10-19 22:36:53 +02002420 int gave_emsg = FALSE;
2421 int mode_idx = -1;
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02002422
Bram Moolenaar4c5d8152018-10-19 22:36:53 +02002423 if (arg[0] && VIM_ISWHITE(arg[1]))
2424 {
2425 switch (arg[0])
2426 {
2427 case 'n': mode_idx = MENU_INDEX_NORMAL; break;
2428 case 'v': mode_idx = MENU_INDEX_VISUAL; break;
2429 case 's': mode_idx = MENU_INDEX_SELECT; break;
2430 case 'o': mode_idx = MENU_INDEX_OP_PENDING; break;
2431 case 't': mode_idx = MENU_INDEX_TERMINAL; break;
2432 case 'i': mode_idx = MENU_INDEX_INSERT; break;
2433 case 'c': mode_idx = MENU_INDEX_CMDLINE; break;
2434 default: EMSG2(_(e_invarg2), arg);
2435 return;
2436 }
2437 arg = skipwhite(arg + 2);
2438 }
2439
2440 saved_name = vim_strsave(arg);
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02002441 if (saved_name == NULL)
2442 return;
2443
2444 menu = *get_root_menu(saved_name);
2445 name = saved_name;
2446 while (*name)
2447 {
2448 /* Find in the menu hierarchy */
2449 p = menu_name_skip(name);
2450
2451 while (menu != NULL)
2452 {
2453 if (menu_name_equal(name, menu))
2454 {
2455 if (*p == NUL && menu->children != NULL)
2456 {
2457 EMSG(_("E333: Menu path must lead to a menu item"));
Bram Moolenaar4c5d8152018-10-19 22:36:53 +02002458 gave_emsg = TRUE;
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02002459 menu = NULL;
2460 }
2461 else if (*p != NUL && menu->children == NULL)
2462 {
2463 EMSG(_(e_notsubmenu));
2464 menu = NULL;
2465 }
2466 break;
2467 }
2468 menu = menu->next;
2469 }
2470 if (menu == NULL || *p == NUL)
2471 break;
2472 menu = menu->children;
2473 name = p;
2474 }
2475 vim_free(saved_name);
2476 if (menu == NULL)
2477 {
Bram Moolenaar4c5d8152018-10-19 22:36:53 +02002478 if (!gave_emsg)
2479 EMSG2(_("E334: Menu not found: %s"), arg);
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02002480 return;
2481 }
2482
Bram Moolenaar4c5d8152018-10-19 22:36:53 +02002483 // Found the menu, so execute.
2484 execute_menu(eap, menu, mode_idx);
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02002485}
2486
2487/*
2488 * Handle a click in the window toolbar of "wp" at column "col".
2489 */
2490 void
2491winbar_click(win_T *wp, int col)
2492{
2493 int idx;
2494
2495 if (wp->w_winbar_items == NULL)
2496 return;
2497 for (idx = 0; wp->w_winbar_items[idx].wb_menu != NULL; ++idx)
2498 {
2499 winbar_item_T *item = &wp->w_winbar_items[idx];
2500
2501 if (col >= item->wb_startcol && col <= item->wb_endcol)
2502 {
2503 win_T *save_curwin = NULL;
Bram Moolenaara21a6a92017-09-23 16:33:50 +02002504 pos_T save_visual = VIsual;
2505 int save_visual_active = VIsual_active;
2506 int save_visual_select = VIsual_select;
2507 int save_visual_reselect = VIsual_reselect;
2508 int save_visual_mode = VIsual_mode;
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02002509
2510 if (wp != curwin)
2511 {
2512 /* Clicking in the window toolbar of a not-current window.
Bram Moolenaara21a6a92017-09-23 16:33:50 +02002513 * Make that window the current one and save Visual mode. */
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02002514 save_curwin = curwin;
Bram Moolenaara21a6a92017-09-23 16:33:50 +02002515 VIsual_active = FALSE;
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02002516 curwin = wp;
2517 curbuf = curwin->w_buffer;
2518 check_cursor();
2519 }
2520
Bram Moolenaar4c5d8152018-10-19 22:36:53 +02002521 execute_menu(NULL, item->wb_menu, -1);
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02002522
2523 if (save_curwin != NULL)
2524 {
2525 curwin = save_curwin;
2526 curbuf = curwin->w_buffer;
Bram Moolenaara21a6a92017-09-23 16:33:50 +02002527 VIsual = save_visual;
2528 VIsual_active = save_visual_active;
2529 VIsual_select = save_visual_select;
2530 VIsual_reselect = save_visual_reselect;
2531 VIsual_mode = save_visual_mode;
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02002532 }
2533 }
2534 }
2535}
2536
2537#if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_GTK) \
Bram Moolenaar40d77b02018-03-05 21:32:27 +01002538 || defined(FEAT_TERM_POPUP_MENU) \
Bram Moolenaar071d4272004-06-13 20:20:40 +00002539 || defined(FEAT_BEVAL_TIP) || defined(PROTO)
2540/*
2541 * Given a menu descriptor, e.g. "File.New", find it in the menu hierarchy.
2542 */
2543 vimmenu_T *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002544gui_find_menu(char_u *path_name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002545{
2546 vimmenu_T *menu = NULL;
2547 char_u *name;
2548 char_u *saved_name;
2549 char_u *p;
2550
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02002551 menu = *get_root_menu(path_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002552
2553 saved_name = vim_strsave(path_name);
2554 if (saved_name == NULL)
2555 return NULL;
2556
2557 name = saved_name;
2558 while (*name)
2559 {
2560 /* find the end of one dot-separated name and put a NUL at the dot */
2561 p = menu_name_skip(name);
2562
2563 while (menu != NULL)
2564 {
Bram Moolenaard91f7042011-01-04 17:49:32 +01002565 if (menu_name_equal(name, menu))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002566 {
2567 if (menu->children == NULL)
2568 {
2569 /* found a menu item instead of a sub-menu */
2570 if (*p == NUL)
2571 EMSG(_("E336: Menu path must lead to a sub-menu"));
2572 else
2573 EMSG(_(e_notsubmenu));
2574 menu = NULL;
2575 goto theend;
2576 }
2577 if (*p == NUL) /* found a full match */
2578 goto theend;
2579 break;
2580 }
2581 menu = menu->next;
2582 }
2583 if (menu == NULL) /* didn't find it */
2584 break;
2585
2586 /* Found a match, search the sub-menu. */
2587 menu = menu->children;
2588 name = p;
2589 }
2590
2591 if (menu == NULL)
2592 EMSG(_("E337: Menu not found - check menu names"));
2593theend:
2594 vim_free(saved_name);
2595 return menu;
2596}
2597#endif
2598
2599#ifdef FEAT_MULTI_LANG
2600/*
2601 * Translation of menu names. Just a simple lookup table.
2602 */
2603
2604typedef struct
2605{
2606 char_u *from; /* English name */
2607 char_u *from_noamp; /* same, without '&' */
2608 char_u *to; /* translated name */
2609} menutrans_T;
2610
2611static garray_T menutrans_ga = {0, 0, 0, 0, NULL};
2612#endif
2613
2614/*
2615 * ":menutrans".
2616 * This function is also defined without the +multi_lang feature, in which
2617 * case the commands are ignored.
2618 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002619 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002620ex_menutranslate(exarg_T *eap UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002621{
2622#ifdef FEAT_MULTI_LANG
2623 char_u *arg = eap->arg;
2624 menutrans_T *tp;
2625 int i;
2626 char_u *from, *from_noamp, *to;
2627
2628 if (menutrans_ga.ga_itemsize == 0)
2629 ga_init2(&menutrans_ga, (int)sizeof(menutrans_T), 5);
2630
2631 /*
2632 * ":menutrans clear": clear all translations.
2633 */
2634 if (STRNCMP(arg, "clear", 5) == 0 && ends_excmd(*skipwhite(arg + 5)))
2635 {
2636 tp = (menutrans_T *)menutrans_ga.ga_data;
2637 for (i = 0; i < menutrans_ga.ga_len; ++i)
2638 {
2639 vim_free(tp[i].from);
2640 vim_free(tp[i].from_noamp);
2641 vim_free(tp[i].to);
2642 }
2643 ga_clear(&menutrans_ga);
2644# ifdef FEAT_EVAL
2645 /* Delete all "menutrans_" global variables. */
2646 del_menutrans_vars();
2647# endif
2648 }
2649 else
2650 {
2651 /* ":menutrans from to": add translation */
2652 from = arg;
2653 arg = menu_skip_part(arg);
2654 to = skipwhite(arg);
2655 *arg = NUL;
2656 arg = menu_skip_part(to);
2657 if (arg == to)
2658 EMSG(_(e_invarg));
2659 else
2660 {
2661 if (ga_grow(&menutrans_ga, 1) == OK)
2662 {
2663 tp = (menutrans_T *)menutrans_ga.ga_data;
2664 from = vim_strsave(from);
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00002665 if (from != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002666 {
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00002667 from_noamp = menu_text(from, NULL, NULL);
2668 to = vim_strnsave(to, (int)(arg - to));
2669 if (from_noamp != NULL && to != NULL)
2670 {
Bram Moolenaar70b11cd2010-05-14 22:24:40 +02002671 menu_translate_tab_and_shift(from);
2672 menu_translate_tab_and_shift(to);
2673 menu_unescape_name(from);
2674 menu_unescape_name(to);
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00002675 tp[menutrans_ga.ga_len].from = from;
2676 tp[menutrans_ga.ga_len].from_noamp = from_noamp;
2677 tp[menutrans_ga.ga_len].to = to;
2678 ++menutrans_ga.ga_len;
2679 }
2680 else
2681 {
2682 vim_free(from);
2683 vim_free(from_noamp);
2684 vim_free(to);
2685 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002686 }
2687 }
2688 }
2689 }
2690#endif
2691}
2692
2693#if defined(FEAT_MULTI_LANG) || defined(FEAT_TOOLBAR)
2694/*
2695 * Find the character just after one part of a menu name.
2696 */
2697 static char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002698menu_skip_part(char_u *p)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002699{
Bram Moolenaar1c465442017-03-12 20:10:05 +01002700 while (*p != NUL && *p != '.' && !VIM_ISWHITE(*p))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002701 {
2702 if ((*p == '\\' || *p == Ctrl_V) && p[1] != NUL)
2703 ++p;
2704 ++p;
2705 }
2706 return p;
2707}
2708#endif
2709
2710#ifdef FEAT_MULTI_LANG
2711/*
2712 * Lookup part of a menu name in the translations.
2713 * Return a pointer to the translation or NULL if not found.
2714 */
2715 static char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002716menutrans_lookup(char_u *name, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002717{
2718 menutrans_T *tp = (menutrans_T *)menutrans_ga.ga_data;
2719 int i;
2720 char_u *dname;
2721
2722 for (i = 0; i < menutrans_ga.ga_len; ++i)
Bram Moolenaar11dd8c12017-03-04 20:41:34 +01002723 if (STRNICMP(name, tp[i].from, len) == 0 && tp[i].from[len] == NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002724 return tp[i].to;
2725
2726 /* Now try again while ignoring '&' characters. */
2727 i = name[len];
2728 name[len] = NUL;
2729 dname = menu_text(name, NULL, NULL);
2730 name[len] = i;
2731 if (dname != NULL)
2732 {
2733 for (i = 0; i < menutrans_ga.ga_len; ++i)
Bram Moolenaar11dd8c12017-03-04 20:41:34 +01002734 if (STRICMP(dname, tp[i].from_noamp) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002735 {
2736 vim_free(dname);
2737 return tp[i].to;
2738 }
2739 vim_free(dname);
2740 }
2741
2742 return NULL;
2743}
Bram Moolenaar071d4272004-06-13 20:20:40 +00002744
Bram Moolenaar70b11cd2010-05-14 22:24:40 +02002745/*
2746 * Unescape the name in the translate dictionary table.
2747 */
2748 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002749menu_unescape_name(char_u *name)
Bram Moolenaar70b11cd2010-05-14 22:24:40 +02002750{
2751 char_u *p;
2752
Bram Moolenaar91acfff2017-03-12 19:22:36 +01002753 for (p = name; *p && *p != '.'; MB_PTR_ADV(p))
Bram Moolenaar70b11cd2010-05-14 22:24:40 +02002754 if (*p == '\\')
2755 STRMOVE(p, p + 1);
2756}
Bram Moolenaar56be9502010-06-06 14:20:26 +02002757#endif /* FEAT_MULTI_LANG */
Bram Moolenaar70b11cd2010-05-14 22:24:40 +02002758
2759/*
2760 * Isolate the menu name.
2761 * Skip the menu name, and translate <Tab> into a real TAB.
2762 */
2763 static char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002764menu_translate_tab_and_shift(char_u *arg_start)
Bram Moolenaar70b11cd2010-05-14 22:24:40 +02002765{
2766 char_u *arg = arg_start;
2767
Bram Moolenaar1c465442017-03-12 20:10:05 +01002768 while (*arg && !VIM_ISWHITE(*arg))
Bram Moolenaar70b11cd2010-05-14 22:24:40 +02002769 {
2770 if ((*arg == '\\' || *arg == Ctrl_V) && arg[1] != NUL)
2771 arg++;
2772 else if (STRNICMP(arg, "<TAB>", 5) == 0)
2773 {
2774 *arg = TAB;
2775 STRMOVE(arg + 1, arg + 5);
2776 }
2777 arg++;
2778 }
2779 if (*arg != NUL)
2780 *arg++ = NUL;
2781 arg = skipwhite(arg);
2782
2783 return arg;
2784}
2785
Bram Moolenaar071d4272004-06-13 20:20:40 +00002786#endif /* FEAT_MENU */