xref: /vim-8.2.3635/src/menu.c (revision 71ccd03e)
1 /* vi:set ts=8 sts=4 sw=4 noet:
2  *
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_MSWIN
22 static int add_menu_path(char_u *, vimmenu_T *, int *, char_u *, int);
23 #else
24 static int add_menu_path(char_u *, vimmenu_T *, int *, char_u *);
25 #endif
26 static int menu_nable_recurse(vimmenu_T *menu, char_u *name, int modes, int enable);
27 static int remove_menu(vimmenu_T **, char_u *, int, int silent);
28 static void free_menu(vimmenu_T **menup);
29 static void free_menu_string(vimmenu_T *, int);
30 static int show_menus(char_u *, int);
31 static void show_menus_recursive(vimmenu_T *, int, int);
32 static char_u *menu_name_skip(char_u *name);
33 static int menu_name_equal(char_u *name, vimmenu_T *menu);
34 static int menu_namecmp(char_u *name, char_u *mname);
35 static int get_menu_cmd_modes(char_u *, int, int *, int *);
36 static char_u *popup_mode_name(char_u *name, int idx);
37 static char_u *menu_text(char_u *text, int *mnemonic, char_u **actext);
38 
39 #if defined(FEAT_GUI_MSWIN) && defined(FEAT_TEAROFF)
40 static void gui_create_tearoffs_recurse(vimmenu_T *menu, const char_u *pname, int *pri_tab, int pri_idx);
41 static void gui_add_tearoff(char_u *tearpath, int *pri_tab, int pri_idx);
42 static void gui_destroy_tearoffs_recurse(vimmenu_T *menu);
43 static int s_tearoffs = FALSE;
44 #endif
45 
46 static int menu_is_hidden(char_u *name);
47 static int menu_is_tearoff(char_u *name);
48 
49 #if defined(FEAT_MULTI_LANG) || defined(FEAT_TOOLBAR)
50 static char_u *menu_skip_part(char_u *p);
51 #endif
52 #ifdef FEAT_MULTI_LANG
53 static char_u *menutrans_lookup(char_u *name, int len);
54 static void menu_unescape_name(char_u	*p);
55 #endif
56 
57 static char_u *menu_translate_tab_and_shift(char_u *arg_start);
58 
59 // The character for each menu mode
60 static char *menu_mode_chars[] = {"n", "v", "s", "o", "i", "c", "tl", "t"};
61 
62 static char_u e_notsubmenu[] = N_("E327: Part of menu-item path is not sub-menu");
63 static char_u e_nomenu[] = N_("E329: No menu \"%s\"");
64 
65 #ifdef FEAT_TOOLBAR
66 static const char *toolbar_names[] =
67 {
68     /*  0 */ "New", "Open", "Save", "Undo", "Redo",
69     /*  5 */ "Cut", "Copy", "Paste", "Print", "Help",
70     /* 10 */ "Find", "SaveAll", "SaveSesn", "NewSesn", "LoadSesn",
71     /* 15 */ "RunScript", "Replace", "WinClose", "WinMax", "WinMin",
72     /* 20 */ "WinSplit", "Shell", "FindPrev", "FindNext", "FindHelp",
73     /* 25 */ "Make", "TagJump", "RunCtags", "WinVSplit", "WinMaxWidth",
74     /* 30 */ "WinMinWidth", "Exit"
75 };
76 # define TOOLBAR_NAME_COUNT (sizeof(toolbar_names) / sizeof(char *))
77 #endif
78 
79 /*
80  * Return TRUE if "name" is a window toolbar menu name.
81  */
82     static int
83 menu_is_winbar(char_u *name)
84 {
85     return (STRNCMP(name, "WinBar", 6) == 0);
86 }
87 
88     int
89 winbar_height(win_T *wp)
90 {
91     if (wp->w_winbar != NULL && wp->w_winbar->children != NULL)
92 	return 1;
93     return 0;
94 }
95 
96     static vimmenu_T **
97 get_root_menu(char_u *name)
98 {
99     if (menu_is_winbar(name))
100 	return &curwin->w_winbar;
101     return &root_menu;
102 }
103 
104 /*
105  * Do the :menu command and relatives.
106  */
107     void
108 ex_menu(
109     exarg_T	*eap)		    // Ex command arguments
110 {
111     char_u	*menu_path;
112     int		modes;
113     char_u	*map_to;
114     int		noremap;
115     int		silent = FALSE;
116     int		special = FALSE;
117     int		unmenu;
118     char_u	*map_buf;
119     char_u	*arg;
120     char_u	*p;
121     int		i;
122 #if defined(FEAT_GUI) && !defined(FEAT_GUI_GTK)
123     int		old_menu_height;
124 # if defined(FEAT_TOOLBAR) && !defined(FEAT_GUI_MSWIN)
125     int		old_toolbar_height;
126 # endif
127 #endif
128     int		pri_tab[MENUDEPTH + 1];
129     int		enable = MAYBE;	    // TRUE for "menu enable", FALSE for "menu
130 				    // disable
131 #ifdef FEAT_TOOLBAR
132     char_u	*icon = NULL;
133 #endif
134     vimmenu_T	menuarg;
135     vimmenu_T	**root_menu_ptr;
136 
137     modes = get_menu_cmd_modes(eap->cmd, eap->forceit, &noremap, &unmenu);
138     arg = eap->arg;
139 
140     for (;;)
141     {
142 	if (STRNCMP(arg, "<script>", 8) == 0)
143 	{
144 	    noremap = REMAP_SCRIPT;
145 	    arg = skipwhite(arg + 8);
146 	    continue;
147 	}
148 	if (STRNCMP(arg, "<silent>", 8) == 0)
149 	{
150 	    silent = TRUE;
151 	    arg = skipwhite(arg + 8);
152 	    continue;
153 	}
154 	if (STRNCMP(arg, "<special>", 9) == 0)
155 	{
156 	    special = TRUE;
157 	    arg = skipwhite(arg + 9);
158 	    continue;
159 	}
160 	break;
161     }
162 
163 
164     // Locate an optional "icon=filename" argument.
165     if (STRNCMP(arg, "icon=", 5) == 0)
166     {
167 	arg += 5;
168 #ifdef FEAT_TOOLBAR
169 	icon = arg;
170 #endif
171 	while (*arg != NUL && *arg != ' ')
172 	{
173 	    if (*arg == '\\')
174 		STRMOVE(arg, arg + 1);
175 	    MB_PTR_ADV(arg);
176 	}
177 	if (*arg != NUL)
178 	{
179 	    *arg++ = NUL;
180 	    arg = skipwhite(arg);
181 	}
182     }
183 
184     /*
185      * Fill in the priority table.
186      */
187     for (p = arg; *p; ++p)
188 	if (!VIM_ISDIGIT(*p) && *p != '.')
189 	    break;
190     if (VIM_ISWHITE(*p))
191     {
192 	for (i = 0; i < MENUDEPTH && !VIM_ISWHITE(*arg); ++i)
193 	{
194 	    pri_tab[i] = getdigits(&arg);
195 	    if (pri_tab[i] == 0)
196 		pri_tab[i] = 500;
197 	    if (*arg == '.')
198 		++arg;
199 	}
200 	arg = skipwhite(arg);
201     }
202     else if (eap->addr_count && eap->line2 != 0)
203     {
204 	pri_tab[0] = eap->line2;
205 	i = 1;
206     }
207     else
208 	i = 0;
209     while (i < MENUDEPTH)
210 	pri_tab[i++] = 500;
211     pri_tab[MENUDEPTH] = -1;		// mark end of the table
212 
213     /*
214      * Check for "disable" or "enable" argument.
215      */
216     if (STRNCMP(arg, "enable", 6) == 0 && VIM_ISWHITE(arg[6]))
217     {
218 	enable = TRUE;
219 	arg = skipwhite(arg + 6);
220     }
221     else if (STRNCMP(arg, "disable", 7) == 0 && VIM_ISWHITE(arg[7]))
222     {
223 	enable = FALSE;
224 	arg = skipwhite(arg + 7);
225     }
226 
227     /*
228      * If there is no argument, display all menus.
229      */
230     if (*arg == NUL)
231     {
232 	show_menus(arg, modes);
233 	return;
234     }
235 
236 #ifdef FEAT_TOOLBAR
237     /*
238      * Need to get the toolbar icon index before doing the translation.
239      */
240     menuarg.iconidx = -1;
241     menuarg.icon_builtin = FALSE;
242     if (menu_is_toolbar(arg))
243     {
244 	menu_path = menu_skip_part(arg);
245 	if (*menu_path == '.')
246 	{
247 	    p = menu_skip_part(++menu_path);
248 	    if (STRNCMP(menu_path, "BuiltIn", 7) == 0)
249 	    {
250 		if (skipdigits(menu_path + 7) == p)
251 		{
252 		    menuarg.iconidx = atoi((char *)menu_path + 7);
253 		    if (menuarg.iconidx >= (int)TOOLBAR_NAME_COUNT)
254 			menuarg.iconidx = -1;
255 		    else
256 			menuarg.icon_builtin = TRUE;
257 		}
258 	    }
259 	    else
260 	    {
261 		for (i = 0; i < (int)TOOLBAR_NAME_COUNT; ++i)
262 		    if (STRNCMP(toolbar_names[i], menu_path, p - menu_path)
263 									 == 0)
264 		    {
265 			menuarg.iconidx = i;
266 			break;
267 		    }
268 	    }
269 	}
270     }
271 #endif
272 
273     menu_path = arg;
274     if (*menu_path == '.')
275     {
276 	semsg(_(e_invarg2), menu_path);
277 	goto theend;
278     }
279 
280     map_to = menu_translate_tab_and_shift(arg);
281 
282     /*
283      * If there is only a menu name, display menus with that name.
284      */
285     if (*map_to == NUL && !unmenu && enable == MAYBE)
286     {
287 	show_menus(menu_path, modes);
288 	goto theend;
289     }
290     else if (*map_to != NUL && (unmenu || enable != MAYBE))
291     {
292 	emsg(_(e_trailing));
293 	goto theend;
294     }
295 #if defined(FEAT_GUI) && !(defined(FEAT_GUI_GTK) || defined(FEAT_GUI_PHOTON))
296     old_menu_height = gui.menu_height;
297 # if defined(FEAT_TOOLBAR) && !defined(FEAT_GUI_MSWIN)
298     old_toolbar_height = gui.toolbar_height;
299 # endif
300 #endif
301 
302     root_menu_ptr = get_root_menu(menu_path);
303     if (root_menu_ptr == &curwin->w_winbar)
304 	// Assume the window toolbar menu will change.
305 	redraw_later(NOT_VALID);
306 
307     if (enable != MAYBE)
308     {
309 	/*
310 	 * Change sensitivity of the menu.
311 	 * For the PopUp menu, remove a menu for each mode separately.
312 	 * Careful: menu_nable_recurse() changes menu_path.
313 	 */
314 	if (STRCMP(menu_path, "*") == 0)	// meaning: do all menus
315 	    menu_path = (char_u *)"";
316 
317 	if (menu_is_popup(menu_path))
318 	{
319 	    for (i = 0; i < MENU_INDEX_TIP; ++i)
320 		if (modes & (1 << i))
321 		{
322 		    p = popup_mode_name(menu_path, i);
323 		    if (p != NULL)
324 		    {
325 			menu_nable_recurse(*root_menu_ptr, p, MENU_ALL_MODES,
326 								      enable);
327 			vim_free(p);
328 		    }
329 		}
330 	}
331 	menu_nable_recurse(*root_menu_ptr, menu_path, modes, enable);
332     }
333     else if (unmenu)
334     {
335 	/*
336 	 * Delete menu(s).
337 	 */
338 	if (STRCMP(menu_path, "*") == 0)	// meaning: remove all menus
339 	    menu_path = (char_u *)"";
340 
341 	/*
342 	 * For the PopUp menu, remove a menu for each mode separately.
343 	 */
344 	if (menu_is_popup(menu_path))
345 	{
346 	    for (i = 0; i < MENU_INDEX_TIP; ++i)
347 		if (modes & (1 << i))
348 		{
349 		    p = popup_mode_name(menu_path, i);
350 		    if (p != NULL)
351 		    {
352 			remove_menu(root_menu_ptr, p, MENU_ALL_MODES, TRUE);
353 			vim_free(p);
354 		    }
355 		}
356 	}
357 
358 	// Careful: remove_menu() changes menu_path
359 	remove_menu(root_menu_ptr, menu_path, modes, FALSE);
360     }
361     else
362     {
363 	/*
364 	 * Add menu(s).
365 	 * Replace special key codes.
366 	 */
367 	if (STRICMP(map_to, "<nop>") == 0)	// "<Nop>" means nothing
368 	{
369 	    map_to = (char_u *)"";
370 	    map_buf = NULL;
371 	}
372 	else if (modes & MENU_TIP_MODE)
373 	    map_buf = NULL;	// Menu tips are plain text.
374 	else
375 	    map_to = replace_termcodes(map_to, &map_buf,
376 			REPTERM_DO_LT | (special ? REPTERM_SPECIAL : 0), NULL);
377 	menuarg.modes = modes;
378 #ifdef FEAT_TOOLBAR
379 	menuarg.iconfile = icon;
380 #endif
381 	menuarg.noremap[0] = noremap;
382 	menuarg.silent[0] = silent;
383 	add_menu_path(menu_path, &menuarg, pri_tab, map_to
384 #ifdef FEAT_GUI_MSWIN
385 		, TRUE
386 #endif
387 		);
388 
389 	/*
390 	 * For the PopUp menu, add a menu for each mode separately.
391 	 */
392 	if (menu_is_popup(menu_path))
393 	{
394 	    for (i = 0; i < MENU_INDEX_TIP; ++i)
395 		if (modes & (1 << i))
396 		{
397 		    p = popup_mode_name(menu_path, i);
398 		    if (p != NULL)
399 		    {
400 			// Include all modes, to make ":amenu" work
401 			menuarg.modes = modes;
402 #ifdef FEAT_TOOLBAR
403 			menuarg.iconfile = NULL;
404 			menuarg.iconidx = -1;
405 			menuarg.icon_builtin = FALSE;
406 #endif
407 			add_menu_path(p, &menuarg, pri_tab, map_to
408 #ifdef FEAT_GUI_MSWIN
409 				, TRUE
410 #endif
411 					 );
412 			vim_free(p);
413 		    }
414 		}
415 	}
416 
417 	vim_free(map_buf);
418     }
419 
420 #if defined(FEAT_GUI) && !(defined(FEAT_GUI_GTK))
421     // If the menubar height changed, resize the window
422     if (gui.in_use
423 	    && (gui.menu_height != old_menu_height
424 # if defined(FEAT_TOOLBAR) && !defined(FEAT_GUI_MSWIN)
425 		|| gui.toolbar_height != old_toolbar_height
426 # endif
427 	    ))
428 	gui_set_shellsize(FALSE, FALSE, RESIZE_VERT);
429 #endif
430     if (root_menu_ptr == &curwin->w_winbar)
431     {
432 	int h = winbar_height(curwin);
433 
434 	if (h != curwin->w_winbar_height)
435 	{
436 	    if (h == 0)
437 		++curwin->w_height;
438 	    else if (curwin->w_height > 0)
439 		--curwin->w_height;
440 	    curwin->w_winbar_height = h;
441 	}
442     }
443 
444 theend:
445     ;
446 }
447 
448 /*
449  * Add the menu with the given name to the menu hierarchy
450  */
451     static int
452 add_menu_path(
453     char_u	*menu_path,
454     vimmenu_T	*menuarg,	// passes modes, iconfile, iconidx,
455 				// icon_builtin, silent[0], noremap[0]
456     int		*pri_tab,
457     char_u	*call_data
458 #ifdef FEAT_GUI_MSWIN
459     , int	addtearoff	// may add tearoff item
460 #endif
461     )
462 {
463     char_u	*path_name;
464     int		modes = menuarg->modes;
465     vimmenu_T	**menup;
466     vimmenu_T	*menu = NULL;
467     vimmenu_T	*parent;
468     vimmenu_T	**lower_pri;
469     char_u	*p;
470     char_u	*name;
471     char_u	*dname;
472     char_u	*next_name;
473     int		i;
474     int		c;
475     int		d;
476 #ifdef FEAT_GUI
477     int		idx;
478     int		new_idx;
479 #endif
480     int		pri_idx = 0;
481     int		old_modes = 0;
482     int		amenu;
483 #ifdef FEAT_MULTI_LANG
484     char_u	*en_name;
485     char_u	*map_to = NULL;
486 #endif
487     vimmenu_T	**root_menu_ptr;
488 
489     // Make a copy so we can stuff around with it, since it could be const
490     path_name = vim_strsave(menu_path);
491     if (path_name == NULL)
492 	return FAIL;
493     root_menu_ptr = get_root_menu(menu_path);
494     menup = root_menu_ptr;
495     parent = NULL;
496     name = path_name;
497     while (*name)
498     {
499 	// Get name of this element in the menu hierarchy, and the simplified
500 	// name (without mnemonic and accelerator text).
501 	next_name = menu_name_skip(name);
502 #ifdef	FEAT_MULTI_LANG
503 	map_to = menutrans_lookup(name, (int)STRLEN(name));
504 	if (map_to != NULL)
505 	{
506 	    en_name = name;
507 	    name = map_to;
508 	}
509 	else
510 	    en_name = NULL;
511 #endif
512 	dname = menu_text(name, NULL, NULL);
513 	if (dname == NULL)
514 	    goto erret;
515 	if (*dname == NUL)
516 	{
517 	    // Only a mnemonic or accelerator is not valid.
518 	    emsg(_("E792: Empty menu name"));
519 	    goto erret;
520 	}
521 
522 	// See if it's already there
523 	lower_pri = menup;
524 #ifdef FEAT_GUI
525 	idx = 0;
526 	new_idx = 0;
527 #endif
528 	menu = *menup;
529 	while (menu != NULL)
530 	{
531 	    if (menu_name_equal(name, menu) || menu_name_equal(dname, menu))
532 	    {
533 		if (*next_name == NUL && menu->children != NULL)
534 		{
535 		    if (!sys_menu)
536 			emsg(_("E330: Menu path must not lead to a sub-menu"));
537 		    goto erret;
538 		}
539 		if (*next_name != NUL && menu->children == NULL
540 #ifdef FEAT_GUI_MSWIN
541 			&& addtearoff
542 #endif
543 			)
544 		{
545 		    if (!sys_menu)
546 			emsg(_(e_notsubmenu));
547 		    goto erret;
548 		}
549 		break;
550 	    }
551 	    menup = &menu->next;
552 
553 	    // Count menus, to find where this one needs to be inserted.
554 	    // Ignore menus that are not in the menubar (PopUp and Toolbar)
555 	    if (parent != NULL || menu_is_menubar(menu->name))
556 	    {
557 #ifdef FEAT_GUI
558 		++idx;
559 #endif
560 		if (menu->priority <= pri_tab[pri_idx])
561 		{
562 		    lower_pri = menup;
563 #ifdef FEAT_GUI
564 		    new_idx = idx;
565 #endif
566 		}
567 	    }
568 	    menu = menu->next;
569 	}
570 
571 	if (menu == NULL)
572 	{
573 	    if (*next_name == NUL && parent == NULL)
574 	    {
575 		emsg(_("E331: Must not add menu items directly to menu bar"));
576 		goto erret;
577 	    }
578 
579 	    if (menu_is_separator(dname) && *next_name != NUL)
580 	    {
581 		emsg(_("E332: Separator cannot be part of a menu path"));
582 		goto erret;
583 	    }
584 
585 	    // Not already there, so lets add it
586 	    menu = ALLOC_CLEAR_ONE(vimmenu_T);
587 	    if (menu == NULL)
588 		goto erret;
589 
590 	    menu->modes = modes;
591 	    menu->enabled = MENU_ALL_MODES;
592 	    menu->name = vim_strsave(name);
593 	    // separate mnemonic and accelerator text from actual menu name
594 	    menu->dname = menu_text(name, &menu->mnemonic, &menu->actext);
595 #ifdef	FEAT_MULTI_LANG
596 	    if (en_name != NULL)
597 	    {
598 		menu->en_name = vim_strsave(en_name);
599 		menu->en_dname = menu_text(en_name, NULL, NULL);
600 	    }
601 	    else
602 	    {
603 		menu->en_name = NULL;
604 		menu->en_dname = NULL;
605 	    }
606 #endif
607 	    menu->priority = pri_tab[pri_idx];
608 	    menu->parent = parent;
609 #ifdef FEAT_GUI_MOTIF
610 	    menu->sensitive = TRUE;	    // the default
611 #endif
612 #ifdef FEAT_BEVAL_TIP
613 	    menu->tip = NULL;
614 #endif
615 #ifdef FEAT_GUI_ATHENA
616 	    menu->image = None;		    // X-Windows definition for NULL
617 #endif
618 
619 	    /*
620 	     * Add after menu that has lower priority.
621 	     */
622 	    menu->next = *lower_pri;
623 	    *lower_pri = menu;
624 
625 	    old_modes = 0;
626 
627 #ifdef FEAT_TOOLBAR
628 	    menu->iconidx = menuarg->iconidx;
629 	    menu->icon_builtin = menuarg->icon_builtin;
630 	    if (*next_name == NUL && menuarg->iconfile != NULL)
631 		menu->iconfile = vim_strsave(menuarg->iconfile);
632 #endif
633 #if defined(FEAT_GUI_MSWIN) && defined(FEAT_TEAROFF)
634 	    // the tearoff item must be present in the modes of each item.
635 	    if (parent != NULL && menu_is_tearoff(parent->children->dname))
636 		parent->children->modes |= modes;
637 #endif
638 	}
639 	else
640 	{
641 	    old_modes = menu->modes;
642 
643 	    /*
644 	     * If this menu option was previously only available in other
645 	     * modes, then make sure it's available for this one now
646 	     * Also enable a menu when it's created or changed.
647 	     */
648 #ifdef FEAT_GUI_MSWIN
649 	    // If adding a tearbar (addtearoff == FALSE) don't update modes
650 	    if (addtearoff)
651 #endif
652 	    {
653 		menu->modes |= modes;
654 		menu->enabled |= modes;
655 	    }
656 	}
657 
658 #ifdef FEAT_GUI
659 	/*
660 	 * Add the menu item when it's used in one of the modes, but not when
661 	 * only a tooltip is defined.
662 	 */
663 	if ((old_modes & MENU_ALL_MODES) == 0
664 		&& (menu->modes & MENU_ALL_MODES) != 0)
665 	{
666 	    if (gui.in_use)  // Otherwise it will be added when GUI starts
667 	    {
668 		if (*next_name == NUL)
669 		{
670 		    // Real menu item, not sub-menu
671 		    gui_mch_add_menu_item(menu, new_idx);
672 
673 		    // Want to update menus now even if mode not changed
674 		    force_menu_update = TRUE;
675 		}
676 		else
677 		{
678 		    // Sub-menu (not at end of path yet)
679 		    gui_mch_add_menu(menu, new_idx);
680 		}
681 	    }
682 
683 # if defined(FEAT_GUI_MSWIN) & defined(FEAT_TEAROFF)
684 	    // When adding a new submenu, may add a tearoff item
685 	    if (	addtearoff
686 		    && *next_name
687 		    && vim_strchr(p_go, GO_TEAROFF) != NULL
688 		    && menu_is_menubar(name)
689 #  ifdef VIMDLL
690 		    && (gui.in_use || gui.starting)
691 #  endif
692 		    )
693 	    {
694 		char_u		*tearpath;
695 
696 		/*
697 		 * The pointers next_name & path_name refer to a string with
698 		 * \'s and ^V's stripped out. But menu_path is a "raw"
699 		 * string, so we must correct for special characters.
700 		 */
701 		tearpath = alloc(STRLEN(menu_path) + TEAR_LEN + 2);
702 		if (tearpath != NULL)
703 		{
704 		    char_u  *s;
705 		    int	    idx;
706 
707 		    STRCPY(tearpath, menu_path);
708 		    idx = (int)(next_name - path_name - 1);
709 		    for (s = tearpath; *s && s < tearpath + idx; MB_PTR_ADV(s))
710 		    {
711 			if ((*s == '\\' || *s == Ctrl_V) && s[1])
712 			{
713 			    ++idx;
714 			    ++s;
715 			}
716 		    }
717 		    tearpath[idx] = NUL;
718 		    gui_add_tearoff(tearpath, pri_tab, pri_idx);
719 		    vim_free(tearpath);
720 		}
721 	    }
722 # endif
723 	}
724 #endif // FEAT_GUI
725 
726 	menup = &menu->children;
727 	parent = menu;
728 	name = next_name;
729 	VIM_CLEAR(dname);
730 	if (pri_tab[pri_idx + 1] != -1)
731 	    ++pri_idx;
732     }
733     vim_free(path_name);
734 
735     /*
736      * Only add system menu items which have not been defined yet.
737      * First check if this was an ":amenu".
738      */
739     amenu = ((modes & (MENU_NORMAL_MODE | MENU_INSERT_MODE)) ==
740 				       (MENU_NORMAL_MODE | MENU_INSERT_MODE));
741     if (sys_menu)
742 	modes &= ~old_modes;
743 
744     if (menu != NULL && modes)
745     {
746 #ifdef FEAT_GUI
747 	menu->cb = gui_menu_cb;
748 #endif
749 	p = (call_data == NULL) ? NULL : vim_strsave(call_data);
750 
751 	// loop over all modes, may add more than one
752 	for (i = 0; i < MENU_MODES; ++i)
753 	{
754 	    if (modes & (1 << i))
755 	    {
756 		// free any old menu
757 		free_menu_string(menu, i);
758 
759 		// For "amenu", may insert an extra character.
760 		// Don't do this if adding a tearbar (addtearoff == FALSE).
761 		// Don't do this for "<Nop>".
762 		c = 0;
763 		d = 0;
764 		if (amenu && call_data != NULL && *call_data != NUL
765 #ifdef FEAT_GUI_MSWIN
766 		       && addtearoff
767 #endif
768 				       )
769 		{
770 		    switch (1 << i)
771 		    {
772 			case MENU_VISUAL_MODE:
773 			case MENU_SELECT_MODE:
774 			case MENU_OP_PENDING_MODE:
775 			case MENU_CMDLINE_MODE:
776 			    c = Ctrl_C;
777 			    break;
778 			case MENU_INSERT_MODE:
779 			    c = Ctrl_BSL;
780 			    d = Ctrl_O;
781 			    break;
782 		    }
783 		}
784 
785 		if (c != 0)
786 		{
787 		    menu->strings[i] = alloc(STRLEN(call_data) + 5);
788 		    if (menu->strings[i] != NULL)
789 		    {
790 			menu->strings[i][0] = c;
791 			if (d == 0)
792 			    STRCPY(menu->strings[i] + 1, call_data);
793 			else
794 			{
795 			    menu->strings[i][1] = d;
796 			    STRCPY(menu->strings[i] + 2, call_data);
797 			}
798 			if (c == Ctrl_C)
799 			{
800 			    int	    len = (int)STRLEN(menu->strings[i]);
801 
802 			    // Append CTRL-\ CTRL-G to obey 'insertmode'.
803 			    menu->strings[i][len] = Ctrl_BSL;
804 			    menu->strings[i][len + 1] = Ctrl_G;
805 			    menu->strings[i][len + 2] = NUL;
806 			}
807 		    }
808 		}
809 		else
810 		    menu->strings[i] = p;
811 		menu->noremap[i] = menuarg->noremap[0];
812 		menu->silent[i] = menuarg->silent[0];
813 	    }
814 	}
815 #if defined(FEAT_TOOLBAR) && !defined(FEAT_GUI_MSWIN) \
816 	&& (defined(FEAT_BEVAL_GUI) || defined(FEAT_GUI_GTK))
817 	// Need to update the menu tip.
818 	if (modes & MENU_TIP_MODE)
819 	    gui_mch_menu_set_tip(menu);
820 #endif
821     }
822     return OK;
823 
824 erret:
825     vim_free(path_name);
826     vim_free(dname);
827 
828     // Delete any empty submenu we added before discovering the error.  Repeat
829     // for higher levels.
830     while (parent != NULL && parent->children == NULL)
831     {
832 	if (parent->parent == NULL)
833 	    menup = root_menu_ptr;
834 	else
835 	    menup = &parent->parent->children;
836 	for ( ; *menup != NULL && *menup != parent; menup = &((*menup)->next))
837 	    ;
838 	if (*menup == NULL) // safety check
839 	    break;
840 	parent = parent->parent;
841 	free_menu(menup);
842     }
843     return FAIL;
844 }
845 
846 /*
847  * Set the (sub)menu with the given name to enabled or disabled.
848  * Called recursively.
849  */
850     static int
851 menu_nable_recurse(
852     vimmenu_T	*menu,
853     char_u	*name,
854     int		modes,
855     int		enable)
856 {
857     char_u	*p;
858 
859     if (menu == NULL)
860 	return OK;		// Got to bottom of hierarchy
861 
862     // Get name of this element in the menu hierarchy
863     p = menu_name_skip(name);
864 
865     // Find the menu
866     while (menu != NULL)
867     {
868 	if (*name == NUL || *name == '*' || menu_name_equal(name, menu))
869 	{
870 	    if (*p != NUL)
871 	    {
872 		if (menu->children == NULL)
873 		{
874 		    emsg(_(e_notsubmenu));
875 		    return FAIL;
876 		}
877 		if (menu_nable_recurse(menu->children, p, modes, enable)
878 								      == FAIL)
879 		    return FAIL;
880 	    }
881 	    else
882 		if (enable)
883 		    menu->enabled |= modes;
884 		else
885 		    menu->enabled &= ~modes;
886 
887 	    /*
888 	     * When name is empty, we are doing all menu items for the given
889 	     * modes, so keep looping, otherwise we are just doing the named
890 	     * menu item (which has been found) so break here.
891 	     */
892 	    if (*name != NUL && *name != '*')
893 		break;
894 	}
895 	menu = menu->next;
896     }
897     if (*name != NUL && *name != '*' && menu == NULL)
898     {
899 	semsg(_(e_nomenu), name);
900 	return FAIL;
901     }
902 
903 #ifdef FEAT_GUI
904     // Want to update menus now even if mode not changed
905     force_menu_update = TRUE;
906 #endif
907 
908     return OK;
909 }
910 
911 /*
912  * Remove the (sub)menu with the given name from the menu hierarchy
913  * Called recursively.
914  */
915     static int
916 remove_menu(
917     vimmenu_T	**menup,
918     char_u	*name,
919     int		modes,
920     int		silent)		// don't give error messages
921 {
922     vimmenu_T	*menu;
923     vimmenu_T	*child;
924     char_u	*p;
925 
926     if (*menup == NULL)
927 	return OK;		// Got to bottom of hierarchy
928 
929     // Get name of this element in the menu hierarchy
930     p = menu_name_skip(name);
931 
932     // Find the menu
933     while ((menu = *menup) != NULL)
934     {
935 	if (*name == NUL || menu_name_equal(name, menu))
936 	{
937 	    if (*p != NUL && menu->children == NULL)
938 	    {
939 		if (!silent)
940 		    emsg(_(e_notsubmenu));
941 		return FAIL;
942 	    }
943 	    if ((menu->modes & modes) != 0x0)
944 	    {
945 #if defined(FEAT_GUI_MSWIN) & defined(FEAT_TEAROFF)
946 		/*
947 		 * If we are removing all entries for this menu,MENU_ALL_MODES,
948 		 * Then kill any tearoff before we start
949 		 */
950 		if (*p == NUL && modes == MENU_ALL_MODES)
951 		{
952 		    if (IsWindow(menu->tearoff_handle))
953 			DestroyWindow(menu->tearoff_handle);
954 		}
955 #endif
956 		if (remove_menu(&menu->children, p, modes, silent) == FAIL)
957 		    return FAIL;
958 	    }
959 	    else if (*name != NUL)
960 	    {
961 		if (!silent)
962 		    emsg(_(e_menuothermode));
963 		return FAIL;
964 	    }
965 
966 	    /*
967 	     * When name is empty, we are removing all menu items for the given
968 	     * modes, so keep looping, otherwise we are just removing the named
969 	     * menu item (which has been found) so break here.
970 	     */
971 	    if (*name != NUL)
972 		break;
973 
974 	    // Remove the menu item for the given mode[s].  If the menu item
975 	    // is no longer valid in ANY mode, delete it
976 	    menu->modes &= ~modes;
977 	    if (modes & MENU_TIP_MODE)
978 		free_menu_string(menu, MENU_INDEX_TIP);
979 	    if ((menu->modes & MENU_ALL_MODES) == 0)
980 		free_menu(menup);
981 	    else
982 		menup = &menu->next;
983 	}
984 	else
985 	    menup = &menu->next;
986     }
987     if (*name != NUL)
988     {
989 	if (menu == NULL)
990 	{
991 	    if (!silent)
992 		semsg(_(e_nomenu), name);
993 	    return FAIL;
994 	}
995 
996 
997 	// Recalculate modes for menu based on the new updated children
998 	menu->modes &= ~modes;
999 #if defined(FEAT_GUI_MSWIN) & defined(FEAT_TEAROFF)
1000 	if ((s_tearoffs) && (menu->children != NULL)) // there's a tear bar..
1001 	    child = menu->children->next; // don't count tearoff bar
1002 	else
1003 #endif
1004 	    child = menu->children;
1005 	for ( ; child != NULL; child = child->next)
1006 	    menu->modes |= child->modes;
1007 	if (modes & MENU_TIP_MODE)
1008 	{
1009 	    free_menu_string(menu, MENU_INDEX_TIP);
1010 #if defined(FEAT_TOOLBAR) && !defined(FEAT_GUI_MSWIN) \
1011 	    && (defined(FEAT_BEVAL_GUI) || defined(FEAT_GUI_GTK))
1012 	    // Need to update the menu tip.
1013 	    if (gui.in_use)
1014 		gui_mch_menu_set_tip(menu);
1015 #endif
1016 	}
1017 	if ((menu->modes & MENU_ALL_MODES) == 0)
1018 	{
1019 	    // The menu item is no longer valid in ANY mode, so delete it
1020 #if defined(FEAT_GUI_MSWIN) & defined(FEAT_TEAROFF)
1021 	    if (s_tearoffs && menu->children != NULL) // there's a tear bar..
1022 		free_menu(&menu->children);
1023 #endif
1024 	    *menup = menu;
1025 	    free_menu(menup);
1026 	}
1027     }
1028 
1029     return OK;
1030 }
1031 
1032 /*
1033  * Remove the WinBar menu from window "wp".
1034  */
1035     void
1036 remove_winbar(win_T *wp)
1037 {
1038     remove_menu(&wp->w_winbar, (char_u *)"", MENU_ALL_MODES, TRUE);
1039     vim_free(wp->w_winbar_items);
1040 }
1041 
1042 /*
1043  * Free the given menu structure and remove it from the linked list.
1044  */
1045     static void
1046 free_menu(vimmenu_T **menup)
1047 {
1048     int		i;
1049     vimmenu_T	*menu;
1050 
1051     menu = *menup;
1052 
1053 #ifdef FEAT_GUI
1054     // Free machine specific menu structures (only when already created)
1055     // Also may rebuild a tearoff'ed menu
1056     if (gui.in_use)
1057 	gui_mch_destroy_menu(menu);
1058 #endif
1059 
1060     // Don't change *menup until after calling gui_mch_destroy_menu(). The
1061     // MacOS code needs the original structure to properly delete the menu.
1062     *menup = menu->next;
1063     vim_free(menu->name);
1064     vim_free(menu->dname);
1065 #ifdef FEAT_MULTI_LANG
1066     vim_free(menu->en_name);
1067     vim_free(menu->en_dname);
1068 #endif
1069     vim_free(menu->actext);
1070 #ifdef FEAT_TOOLBAR
1071     vim_free(menu->iconfile);
1072 # ifdef FEAT_GUI_MOTIF
1073     vim_free(menu->xpm_fname);
1074 # endif
1075 #endif
1076     for (i = 0; i < MENU_MODES; i++)
1077 	free_menu_string(menu, i);
1078     vim_free(menu);
1079 
1080 #ifdef FEAT_GUI
1081     // Want to update menus now even if mode not changed
1082     force_menu_update = TRUE;
1083 #endif
1084 }
1085 
1086 /*
1087  * Free the menu->string with the given index.
1088  */
1089     static void
1090 free_menu_string(vimmenu_T *menu, int idx)
1091 {
1092     int		count = 0;
1093     int		i;
1094 
1095     for (i = 0; i < MENU_MODES; i++)
1096 	if (menu->strings[i] == menu->strings[idx])
1097 	    count++;
1098     if (count == 1)
1099 	vim_free(menu->strings[idx]);
1100     menu->strings[idx] = NULL;
1101 }
1102 
1103 /*
1104  * Show the mapping associated with a menu item or hierarchy in a sub-menu.
1105  */
1106     static int
1107 show_menus(char_u *path_name, int modes)
1108 {
1109     char_u	*p;
1110     char_u	*name;
1111     vimmenu_T	*menu;
1112     vimmenu_T	*parent = NULL;
1113 
1114     name = path_name = vim_strsave(path_name);
1115     if (path_name == NULL)
1116 	return FAIL;
1117     menu = *get_root_menu(path_name);
1118 
1119     // First, find the (sub)menu with the given name
1120     while (*name)
1121     {
1122 	p = menu_name_skip(name);
1123 	while (menu != NULL)
1124 	{
1125 	    if (menu_name_equal(name, menu))
1126 	    {
1127 		// Found menu
1128 		if (*p != NUL && menu->children == NULL)
1129 		{
1130 		    emsg(_(e_notsubmenu));
1131 		    vim_free(path_name);
1132 		    return FAIL;
1133 		}
1134 		else if ((menu->modes & modes) == 0x0)
1135 		{
1136 		    emsg(_(e_menuothermode));
1137 		    vim_free(path_name);
1138 		    return FAIL;
1139 		}
1140 		break;
1141 	    }
1142 	    menu = menu->next;
1143 	}
1144 	if (menu == NULL)
1145 	{
1146 	    semsg(_(e_nomenu), name);
1147 	    vim_free(path_name);
1148 	    return FAIL;
1149 	}
1150 	name = p;
1151 	parent = menu;
1152 	menu = menu->children;
1153     }
1154     vim_free(path_name);
1155 
1156     // Now we have found the matching menu, and we list the mappings
1157 						    // Highlight title
1158     msg_puts_title(_("\n--- Menus ---"));
1159 
1160     show_menus_recursive(parent, modes, 0);
1161     return OK;
1162 }
1163 
1164 /*
1165  * Recursively show the mappings associated with the menus under the given one
1166  */
1167     static void
1168 show_menus_recursive(vimmenu_T *menu, int modes, int depth)
1169 {
1170     int		i;
1171     int		bit;
1172 
1173     if (menu != NULL && (menu->modes & modes) == 0x0)
1174 	return;
1175 
1176     if (menu != NULL)
1177     {
1178 	msg_putchar('\n');
1179 	if (got_int)		// "q" hit for "--more--"
1180 	    return;
1181 	for (i = 0; i < depth; i++)
1182 	    msg_puts("  ");
1183 	if (menu->priority)
1184 	{
1185 	    msg_outnum((long)menu->priority);
1186 	    msg_puts(" ");
1187 	}
1188 				// Same highlighting as for directories!?
1189 	msg_outtrans_attr(menu->name, HL_ATTR(HLF_D));
1190     }
1191 
1192     if (menu != NULL && menu->children == NULL)
1193     {
1194 	for (bit = 0; bit < MENU_MODES; bit++)
1195 	    if ((menu->modes & modes & (1 << bit)) != 0)
1196 	    {
1197 		msg_putchar('\n');
1198 		if (got_int)		// "q" hit for "--more--"
1199 		    return;
1200 		for (i = 0; i < depth + 2; i++)
1201 		    msg_puts("  ");
1202 		msg_puts(menu_mode_chars[bit]);
1203 		if (menu->noremap[bit] == REMAP_NONE)
1204 		    msg_putchar('*');
1205 		else if (menu->noremap[bit] == REMAP_SCRIPT)
1206 		    msg_putchar('&');
1207 		else
1208 		    msg_putchar(' ');
1209 		if (menu->silent[bit])
1210 		    msg_putchar('s');
1211 		else
1212 		    msg_putchar(' ');
1213 		if ((menu->modes & menu->enabled & (1 << bit)) == 0)
1214 		    msg_putchar('-');
1215 		else
1216 		    msg_putchar(' ');
1217 		msg_puts(" ");
1218 		if (*menu->strings[bit] == NUL)
1219 		    msg_puts_attr("<Nop>", HL_ATTR(HLF_8));
1220 		else
1221 		    msg_outtrans_special(menu->strings[bit], FALSE, 0);
1222 	    }
1223     }
1224     else
1225     {
1226 	if (menu == NULL)
1227 	{
1228 	    menu = root_menu;
1229 	    depth--;
1230 	}
1231 	else
1232 	    menu = menu->children;
1233 
1234 	// recursively show all children.  Skip PopUp[nvoci].
1235 	for (; menu != NULL && !got_int; menu = menu->next)
1236 	    if (!menu_is_hidden(menu->dname))
1237 		show_menus_recursive(menu, modes, depth + 1);
1238     }
1239 }
1240 
1241 /*
1242  * Used when expanding menu names.
1243  */
1244 static vimmenu_T	*expand_menu = NULL;
1245 static vimmenu_T	*expand_menu_alt = NULL;
1246 static int		expand_modes = 0x0;
1247 static int		expand_emenu;	// TRUE for ":emenu" command
1248 
1249 /*
1250  * Work out what to complete when doing command line completion of menu names.
1251  */
1252     char_u *
1253 set_context_in_menu_cmd(
1254     expand_T	*xp,
1255     char_u	*cmd,
1256     char_u	*arg,
1257     int		forceit)
1258 {
1259     char_u	*after_dot;
1260     char_u	*p;
1261     char_u	*path_name = NULL;
1262     char_u	*name;
1263     int		unmenu;
1264     vimmenu_T	*menu;
1265     int		expand_menus;
1266 
1267     xp->xp_context = EXPAND_UNSUCCESSFUL;
1268 
1269 
1270     // Check for priority numbers, enable and disable
1271     for (p = arg; *p; ++p)
1272 	if (!VIM_ISDIGIT(*p) && *p != '.')
1273 	    break;
1274 
1275     if (!VIM_ISWHITE(*p))
1276     {
1277 	if (STRNCMP(arg, "enable", 6) == 0
1278 		&& (arg[6] == NUL ||  VIM_ISWHITE(arg[6])))
1279 	    p = arg + 6;
1280 	else if (STRNCMP(arg, "disable", 7) == 0
1281 		&& (arg[7] == NUL || VIM_ISWHITE(arg[7])))
1282 	    p = arg + 7;
1283 	else
1284 	    p = arg;
1285     }
1286 
1287     while (*p != NUL && VIM_ISWHITE(*p))
1288 	++p;
1289 
1290     arg = after_dot = p;
1291 
1292     for (; *p && !VIM_ISWHITE(*p); ++p)
1293     {
1294 	if ((*p == '\\' || *p == Ctrl_V) && p[1] != NUL)
1295 	    p++;
1296 	else if (*p == '.')
1297 	    after_dot = p + 1;
1298     }
1299 
1300     // ":tearoff" and ":popup" only use menus, not entries
1301     expand_menus = !((*cmd == 't' && cmd[1] == 'e') || *cmd == 'p');
1302     expand_emenu = (*cmd == 'e');
1303     if (expand_menus && VIM_ISWHITE(*p))
1304 	return NULL;	// TODO: check for next command?
1305     if (*p == NUL)		// Complete the menu name
1306     {
1307 	int try_alt_menu = TRUE;
1308 
1309 	/*
1310 	 * With :unmenu, you only want to match menus for the appropriate mode.
1311 	 * With :menu though you might want to add a menu with the same name as
1312 	 * one in another mode, so match menus from other modes too.
1313 	 */
1314 	expand_modes = get_menu_cmd_modes(cmd, forceit, NULL, &unmenu);
1315 	if (!unmenu)
1316 	    expand_modes = MENU_ALL_MODES;
1317 
1318 	menu = root_menu;
1319 	if (after_dot != arg)
1320 	{
1321 	    path_name = alloc(after_dot - arg);
1322 	    if (path_name == NULL)
1323 		return NULL;
1324 	    vim_strncpy(path_name, arg, after_dot - arg - 1);
1325 	}
1326 	name = path_name;
1327 	while (name != NULL && *name)
1328 	{
1329 	    p = menu_name_skip(name);
1330 	    while (menu != NULL)
1331 	    {
1332 		if (menu_name_equal(name, menu))
1333 		{
1334 		    // Found menu
1335 		    if ((*p != NUL && menu->children == NULL)
1336 			|| ((menu->modes & expand_modes) == 0x0))
1337 		    {
1338 			/*
1339 			 * Menu path continues, but we have reached a leaf.
1340 			 * Or menu exists only in another mode.
1341 			 */
1342 			vim_free(path_name);
1343 			return NULL;
1344 		    }
1345 		    break;
1346 		}
1347 		menu = menu->next;
1348 		if (menu == NULL && try_alt_menu)
1349 		{
1350 		    menu = curwin->w_winbar;
1351 		    try_alt_menu = FALSE;
1352 		}
1353 	    }
1354 	    if (menu == NULL)
1355 	    {
1356 		// No menu found with the name we were looking for
1357 		vim_free(path_name);
1358 		return NULL;
1359 	    }
1360 	    name = p;
1361 	    menu = menu->children;
1362 	    try_alt_menu = FALSE;
1363 	}
1364 	vim_free(path_name);
1365 
1366 	xp->xp_context = expand_menus ? EXPAND_MENUNAMES : EXPAND_MENUS;
1367 	xp->xp_pattern = after_dot;
1368 	expand_menu = menu;
1369 	if (expand_menu == root_menu)
1370 	    expand_menu_alt = curwin->w_winbar;
1371 	else
1372 	    expand_menu_alt = NULL;
1373     }
1374     else			// We're in the mapping part
1375 	xp->xp_context = EXPAND_NOTHING;
1376     return NULL;
1377 }
1378 
1379 /*
1380  * Function given to ExpandGeneric() to obtain the list of (sub)menus (not
1381  * entries).
1382  */
1383     char_u *
1384 get_menu_name(expand_T *xp UNUSED, int idx)
1385 {
1386     static vimmenu_T	*menu = NULL;
1387     static int		did_alt_menu = FALSE;
1388     char_u		*str;
1389 #ifdef FEAT_MULTI_LANG
1390     static  int		should_advance = FALSE;
1391 #endif
1392 
1393     if (idx == 0)	    // first call: start at first item
1394     {
1395 	menu = expand_menu;
1396 	did_alt_menu = FALSE;
1397 #ifdef FEAT_MULTI_LANG
1398 	should_advance = FALSE;
1399 #endif
1400     }
1401 
1402     // Skip PopUp[nvoci].
1403     while (menu != NULL && (menu_is_hidden(menu->dname)
1404 	    || menu_is_separator(menu->dname)
1405 	    || menu_is_tearoff(menu->dname)
1406 	    || menu->children == NULL))
1407     {
1408 	menu = menu->next;
1409 	if (menu == NULL && !did_alt_menu)
1410 	{
1411 	    menu = expand_menu_alt;
1412 	    did_alt_menu = TRUE;
1413 	}
1414     }
1415 
1416     if (menu == NULL)	    // at end of linked list
1417 	return NULL;
1418 
1419     if (menu->modes & expand_modes)
1420 #ifdef FEAT_MULTI_LANG
1421 	if (should_advance)
1422 	    str = menu->en_dname;
1423 	else
1424 	{
1425 #endif
1426 	    str = menu->dname;
1427 #ifdef FEAT_MULTI_LANG
1428 	    if (menu->en_dname == NULL)
1429 		should_advance = TRUE;
1430 	}
1431 #endif
1432     else
1433 	str = (char_u *)"";
1434 
1435 #ifdef FEAT_MULTI_LANG
1436     if (should_advance)
1437 #endif
1438     {
1439 	// Advance to next menu entry.
1440 	menu = menu->next;
1441 	if (menu == NULL && !did_alt_menu)
1442 	{
1443 	    menu = expand_menu_alt;
1444 	    did_alt_menu = TRUE;
1445 	}
1446     }
1447 
1448 #ifdef FEAT_MULTI_LANG
1449     should_advance = !should_advance;
1450 #endif
1451 
1452     return str;
1453 }
1454 
1455 /*
1456  * Function given to ExpandGeneric() to obtain the list of menus and menu
1457  * entries.
1458  */
1459     char_u *
1460 get_menu_names(expand_T *xp UNUSED, int idx)
1461 {
1462     static vimmenu_T	*menu = NULL;
1463     static int		did_alt_menu = FALSE;
1464 #define TBUFFER_LEN 256
1465     static char_u	tbuffer[TBUFFER_LEN]; //hack
1466     char_u		*str;
1467 #ifdef FEAT_MULTI_LANG
1468     static  int		should_advance = FALSE;
1469 #endif
1470 
1471     if (idx == 0)	    // first call: start at first item
1472     {
1473 	menu = expand_menu;
1474 	did_alt_menu = FALSE;
1475 #ifdef FEAT_MULTI_LANG
1476 	should_advance = FALSE;
1477 #endif
1478     }
1479 
1480     // Skip Browse-style entries, popup menus and separators.
1481     while (menu != NULL
1482 	    && (   menu_is_hidden(menu->dname)
1483 		|| (expand_emenu && menu_is_separator(menu->dname))
1484 		|| menu_is_tearoff(menu->dname)
1485 #ifndef FEAT_BROWSE
1486 		|| menu->dname[STRLEN(menu->dname) - 1] == '.'
1487 #endif
1488 	       ))
1489     {
1490 	menu = menu->next;
1491 	if (menu == NULL && !did_alt_menu)
1492 	{
1493 	    menu = expand_menu_alt;
1494 	    did_alt_menu = TRUE;
1495 	}
1496     }
1497 
1498     if (menu == NULL)	    // at end of linked list
1499 	return NULL;
1500 
1501     if (menu->modes & expand_modes)
1502     {
1503 	if (menu->children != NULL)
1504 	{
1505 #ifdef FEAT_MULTI_LANG
1506 	    if (should_advance)
1507 		vim_strncpy(tbuffer, menu->en_dname, TBUFFER_LEN - 2);
1508 	    else
1509 	    {
1510 #endif
1511 		vim_strncpy(tbuffer, menu->dname,  TBUFFER_LEN - 2);
1512 #ifdef FEAT_MULTI_LANG
1513 		if (menu->en_dname == NULL)
1514 		    should_advance = TRUE;
1515 	    }
1516 #endif
1517 	    // hack on menu separators:  use a 'magic' char for the separator
1518 	    // so that '.' in names gets escaped properly
1519 	    STRCAT(tbuffer, "\001");
1520 	    str = tbuffer;
1521 	}
1522 	else
1523 #ifdef FEAT_MULTI_LANG
1524 	{
1525 	    if (should_advance)
1526 		str = menu->en_dname;
1527 	    else
1528 	    {
1529 #endif
1530 		str = menu->dname;
1531 #ifdef FEAT_MULTI_LANG
1532 		if (menu->en_dname == NULL)
1533 		    should_advance = TRUE;
1534 	    }
1535 	}
1536 #endif
1537     }
1538     else
1539 	str = (char_u *)"";
1540 
1541 #ifdef FEAT_MULTI_LANG
1542     if (should_advance)
1543 #endif
1544     {
1545 	// Advance to next menu entry.
1546 	menu = menu->next;
1547 	if (menu == NULL && !did_alt_menu)
1548 	{
1549 	    menu = expand_menu_alt;
1550 	    did_alt_menu = TRUE;
1551 	}
1552     }
1553 
1554 #ifdef FEAT_MULTI_LANG
1555     should_advance = !should_advance;
1556 #endif
1557 
1558     return str;
1559 }
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.
1564  * "name" may be modified.
1565  */
1566     static char_u *
1567 menu_name_skip(char_u *name)
1568 {
1569     char_u  *p;
1570 
1571     for (p = name; *p && *p != '.'; MB_PTR_ADV(p))
1572     {
1573 	if (*p == '\\' || *p == Ctrl_V)
1574 	{
1575 	    STRMOVE(p, p + 1);
1576 	    if (*p == NUL)
1577 		break;
1578 	}
1579     }
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
1590 menu_name_equal(char_u *name, vimmenu_T *menu)
1591 {
1592 #ifdef FEAT_MULTI_LANG
1593     if (menu->en_name != NULL
1594 	    && (menu_namecmp(name, menu->en_name)
1595 		|| menu_namecmp(name, menu->en_dname)))
1596 	return TRUE;
1597 #endif
1598     return menu_namecmp(name, menu->name) || menu_namecmp(name, menu->dname);
1599 }
1600 
1601     static int
1602 menu_namecmp(char_u *name, char_u *mname)
1603 {
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
1622 get_menu_cmd_modes(
1623     char_u  *cmd,
1624     int	    forceit,	    // Was there a "!" after the command?
1625     int	    *noremap,
1626     int	    *unmenu)
1627 {
1628     int	    modes;
1629 
1630     switch (*cmd++)
1631     {
1632 	case 'v':			// vmenu, vunmenu, vnoremenu
1633 	    modes = MENU_VISUAL_MODE | MENU_SELECT_MODE;
1634 	    break;
1635 	case 'x':			// xmenu, xunmenu, xnoremenu
1636 	    modes = MENU_VISUAL_MODE;
1637 	    break;
1638 	case 's':			// smenu, sunmenu, snoremenu
1639 	    modes = MENU_SELECT_MODE;
1640 	    break;
1641 	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':
1648 	    if (*cmd == 'l')            // tlmenu, tlunmenu, tlnoremenu
1649 	    {
1650 		modes = MENU_TERMINAL_MODE;
1651 		++cmd;
1652 		break;
1653 	    }
1654 	    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
1661 				    | MENU_VISUAL_MODE | MENU_SELECT_MODE
1662 				    | MENU_OP_PENDING_MODE;
1663 	    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
1676 		modes = MENU_NORMAL_MODE | MENU_VISUAL_MODE | MENU_SELECT_MODE
1677 						       | 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  * Return the string representation of the menu modes. Does the opposite
1689  * of get_menu_cmd_modes().
1690  */
1691     static char_u *
1692 get_menu_mode_str(int modes)
1693 {
1694     if ((modes & (MENU_INSERT_MODE | MENU_CMDLINE_MODE | MENU_NORMAL_MODE |
1695 		  MENU_VISUAL_MODE | MENU_SELECT_MODE | MENU_OP_PENDING_MODE))
1696 	    == (MENU_INSERT_MODE | MENU_CMDLINE_MODE | MENU_NORMAL_MODE |
1697 		MENU_VISUAL_MODE | MENU_SELECT_MODE | MENU_OP_PENDING_MODE))
1698 	return (char_u *)"a";
1699     if ((modes & (MENU_NORMAL_MODE | MENU_VISUAL_MODE | MENU_SELECT_MODE |
1700 		  MENU_OP_PENDING_MODE))
1701 	    == (MENU_NORMAL_MODE | MENU_VISUAL_MODE | MENU_SELECT_MODE |
1702 		MENU_OP_PENDING_MODE))
1703 	return (char_u *)" ";
1704     if ((modes & (MENU_INSERT_MODE | MENU_CMDLINE_MODE))
1705 	    == (MENU_INSERT_MODE | MENU_CMDLINE_MODE))
1706 	return (char_u *)"!";
1707     if ((modes & (MENU_VISUAL_MODE | MENU_SELECT_MODE))
1708 	    == (MENU_VISUAL_MODE | MENU_SELECT_MODE))
1709 	return (char_u *)"v";
1710     if (modes & MENU_VISUAL_MODE)
1711 	return (char_u *)"x";
1712     if (modes & MENU_SELECT_MODE)
1713 	return (char_u *)"s";
1714     if (modes & MENU_OP_PENDING_MODE)
1715 	return (char_u *)"o";
1716     if (modes & MENU_INSERT_MODE)
1717 	return (char_u *)"i";
1718     if (modes & MENU_TERMINAL_MODE)
1719 	return (char_u *)"tl";
1720     if (modes & MENU_CMDLINE_MODE)
1721 	return (char_u *)"c";
1722     if (modes & MENU_NORMAL_MODE)
1723 	return (char_u *)"n";
1724     if (modes & MENU_TIP_MODE)
1725 	return (char_u *)"t";
1726 
1727     return (char_u *)"";
1728 }
1729 
1730 /*
1731  * Modify a menu name starting with "PopUp" to include the mode character.
1732  * Returns the name in allocated memory (NULL for failure).
1733  */
1734     static char_u *
1735 popup_mode_name(char_u *name, int idx)
1736 {
1737     char_u	*p;
1738     int		len = (int)STRLEN(name);
1739     char	*mode_chars = menu_mode_chars[idx];
1740     int		mode_chars_len = (int)strlen(mode_chars);
1741     int		i;
1742 
1743     p = vim_strnsave(name, len + mode_chars_len);
1744     if (p != NULL)
1745     {
1746 	mch_memmove(p + 5 + mode_chars_len, p + 5, (size_t)(len - 4));
1747 	for (i = 0; i < mode_chars_len; ++i)
1748 	    p[5 + i] = menu_mode_chars[idx][i];
1749     }
1750     return p;
1751 }
1752 
1753 #if defined(FEAT_GUI) || defined(PROTO)
1754 /*
1755  * Return the index into the menu->strings or menu->noremap arrays for the
1756  * current state.  Returns MENU_INDEX_INVALID if there is no mapping for the
1757  * given menu in the current mode.
1758  */
1759     int
1760 get_menu_index(vimmenu_T *menu, int state)
1761 {
1762     int		idx;
1763 
1764     if ((state & INSERT))
1765 	idx = MENU_INDEX_INSERT;
1766     else if (state & CMDLINE)
1767 	idx = MENU_INDEX_CMDLINE;
1768 #ifdef FEAT_TERMINAL
1769     else if (term_use_loop())
1770 	idx = MENU_INDEX_TERMINAL;
1771 #endif
1772     else if (VIsual_active)
1773     {
1774 	if (VIsual_select)
1775 	    idx = MENU_INDEX_SELECT;
1776 	else
1777 	    idx = MENU_INDEX_VISUAL;
1778     }
1779     else if (state == HITRETURN || state == ASKMORE)
1780 	idx = MENU_INDEX_CMDLINE;
1781     else if (finish_op)
1782 	idx = MENU_INDEX_OP_PENDING;
1783     else if ((state & NORMAL))
1784 	idx = MENU_INDEX_NORMAL;
1785     else
1786 	idx = MENU_INDEX_INVALID;
1787 
1788     if (idx != MENU_INDEX_INVALID && menu->strings[idx] == NULL)
1789 	idx = MENU_INDEX_INVALID;
1790     return idx;
1791 }
1792 #endif
1793 
1794 /*
1795  * Duplicate the menu item text and then process to see if a mnemonic key
1796  * and/or accelerator text has been identified.
1797  * Returns a pointer to allocated memory, or NULL for failure.
1798  * If mnemonic != NULL, *mnemonic is set to the character after the first '&'.
1799  * If actext != NULL, *actext is set to the text after the first TAB.
1800  */
1801     static char_u *
1802 menu_text(char_u *str, int *mnemonic, char_u **actext)
1803 {
1804     char_u	*p;
1805     char_u	*text;
1806 
1807     // Locate accelerator text, after the first TAB
1808     p = vim_strchr(str, TAB);
1809     if (p != NULL)
1810     {
1811 	if (actext != NULL)
1812 	    *actext = vim_strsave(p + 1);
1813 	text = vim_strnsave(str, p - str);
1814     }
1815     else
1816 	text = vim_strsave(str);
1817 
1818     // Find mnemonic characters "&a" and reduce "&&" to "&".
1819     for (p = text; p != NULL; )
1820     {
1821 	p = vim_strchr(p, '&');
1822 	if (p != NULL)
1823 	{
1824 	    if (p[1] == NUL)	    // trailing "&"
1825 		break;
1826 	    if (mnemonic != NULL && p[1] != '&')
1827 #if !defined(__MVS__) || defined(MOTIF390_MNEMONIC_FIXED)
1828 		*mnemonic = p[1];
1829 #else
1830 	    {
1831 		/*
1832 		 * Well there is a bug in the Motif libraries on OS390 Unix.
1833 		 * The mnemonic keys needs to be converted to ASCII values
1834 		 * first.
1835 		 * This behavior has been seen in 2.8 and 2.9.
1836 		 */
1837 		char c = p[1];
1838 		__etoa_l(&c, 1);
1839 		*mnemonic = c;
1840 	    }
1841 #endif
1842 	    STRMOVE(p, p + 1);
1843 	    p = p + 1;
1844 	}
1845     }
1846     return text;
1847 }
1848 
1849 /*
1850  * Return TRUE if "name" can be a menu in the MenuBar.
1851  */
1852     int
1853 menu_is_menubar(char_u *name)
1854 {
1855     return (!menu_is_popup(name)
1856 	    && !menu_is_toolbar(name)
1857 	    && !menu_is_winbar(name)
1858 	    && *name != MNU_HIDDEN_CHAR);
1859 }
1860 
1861 /*
1862  * Return TRUE if "name" is a popup menu name.
1863  */
1864     int
1865 menu_is_popup(char_u *name)
1866 {
1867     return (STRNCMP(name, "PopUp", 5) == 0);
1868 }
1869 
1870 #if (defined(FEAT_GUI_MOTIF) && (XmVersion <= 1002)) || defined(PROTO)
1871 /*
1872  * Return TRUE if "name" is part of a popup menu.
1873  */
1874     int
1875 menu_is_child_of_popup(vimmenu_T *menu)
1876 {
1877     while (menu->parent != NULL)
1878 	menu = menu->parent;
1879     return menu_is_popup(menu->name);
1880 }
1881 #endif
1882 
1883 /*
1884  * Return TRUE if "name" is a toolbar menu name.
1885  */
1886     int
1887 menu_is_toolbar(char_u *name)
1888 {
1889     return (STRNCMP(name, "ToolBar", 7) == 0);
1890 }
1891 
1892 /*
1893  * Return TRUE if the name is a menu separator identifier: Starts and ends
1894  * with '-'
1895  */
1896     int
1897 menu_is_separator(char_u *name)
1898 {
1899     return (name[0] == '-' && name[STRLEN(name) - 1] == '-');
1900 }
1901 
1902 /*
1903  * Return TRUE if the menu is hidden:  Starts with ']'
1904  */
1905     static int
1906 menu_is_hidden(char_u *name)
1907 {
1908     return (name[0] == ']') || (menu_is_popup(name) && name[5] != NUL);
1909 }
1910 
1911 /*
1912  * Return TRUE if the menu is the tearoff menu.
1913  */
1914     static int
1915 menu_is_tearoff(char_u *name UNUSED)
1916 {
1917 #ifdef FEAT_GUI
1918     return (STRCMP(name, TEAR_STRING) == 0);
1919 #else
1920     return FALSE;
1921 #endif
1922 }
1923 
1924 #if defined(FEAT_GUI) || defined(FEAT_TERM_POPUP_MENU) || defined(PROTO)
1925 
1926     static int
1927 get_menu_mode(void)
1928 {
1929 #ifdef FEAT_TERMINAL
1930     if (term_use_loop())
1931 	return MENU_INDEX_TERMINAL;
1932 #endif
1933     if (VIsual_active)
1934     {
1935 	if (VIsual_select)
1936 	    return MENU_INDEX_SELECT;
1937 	return MENU_INDEX_VISUAL;
1938     }
1939     if (State & INSERT)
1940 	return MENU_INDEX_INSERT;
1941     if ((State & CMDLINE) || State == ASKMORE || State == HITRETURN)
1942 	return MENU_INDEX_CMDLINE;
1943     if (finish_op)
1944 	return MENU_INDEX_OP_PENDING;
1945     if (State & NORMAL)
1946 	return MENU_INDEX_NORMAL;
1947     if (State & LANGMAP)	// must be a "r" command, like Insert mode
1948 	return MENU_INDEX_INSERT;
1949     return MENU_INDEX_INVALID;
1950 }
1951 
1952     int
1953 get_menu_mode_flag(void)
1954 {
1955     int mode = get_menu_mode();
1956 
1957     if (mode == MENU_INDEX_INVALID)
1958 	return 0;
1959     return 1 << mode;
1960 }
1961 
1962 /*
1963  * Display the Special "PopUp" menu as a pop-up at the current mouse
1964  * position.  The "PopUpn" menu is for Normal mode, "PopUpi" for Insert mode,
1965  * etc.
1966  */
1967     void
1968 show_popupmenu(void)
1969 {
1970     vimmenu_T	*menu;
1971     int		menu_mode;
1972     char*	mode;
1973     int		mode_len;
1974 
1975     menu_mode = get_menu_mode();
1976     if (menu_mode == MENU_INDEX_INVALID)
1977 	return;
1978     mode = menu_mode_chars[menu_mode];
1979     mode_len = (int)strlen(mode);
1980 
1981     apply_autocmds(EVENT_MENUPOPUP, (char_u*)mode, NULL, FALSE, curbuf);
1982 
1983     FOR_ALL_MENUS(menu)
1984 	if (STRNCMP("PopUp", menu->name, 5) == 0 && STRNCMP(menu->name + 5, mode, mode_len) == 0)
1985 	    break;
1986 
1987     // Only show a popup when it is defined and has entries
1988     if (menu != NULL && menu->children != NULL)
1989     {
1990 # if defined(FEAT_GUI)
1991 	if (gui.in_use)
1992 	{
1993 	    // Update the menus now, in case the MenuPopup autocommand did
1994 	    // anything.
1995 	    gui_update_menus(0);
1996 	    gui_mch_show_popupmenu(menu);
1997 	}
1998 # endif
1999 #  if defined(FEAT_GUI) && defined(FEAT_TERM_POPUP_MENU)
2000 	else
2001 #  endif
2002 #  if defined(FEAT_TERM_POPUP_MENU)
2003 	    pum_show_popupmenu(menu);
2004 #  endif
2005     }
2006 }
2007 #endif
2008 
2009 #if defined(FEAT_GUI) || defined(PROTO)
2010 
2011 /*
2012  * Check that a pointer appears in the menu tree.  Used to protect from using
2013  * a menu that was deleted after it was selected but before the event was
2014  * handled.
2015  * Return OK or FAIL.  Used recursively.
2016  */
2017     int
2018 check_menu_pointer(vimmenu_T *root, vimmenu_T *menu_to_check)
2019 {
2020     vimmenu_T	*p;
2021 
2022     for (p = root; p != NULL; p = p->next)
2023 	if (p == menu_to_check
2024 		|| (p->children != NULL
2025 		    && check_menu_pointer(p->children, menu_to_check) == OK))
2026 	    return OK;
2027     return FAIL;
2028 }
2029 
2030 /*
2031  * After we have started the GUI, then we can create any menus that have been
2032  * defined.  This is done once here.  add_menu_path() may have already been
2033  * called to define these menus, and may be called again.  This function calls
2034  * itself recursively.	Should be called at the top level with:
2035  * gui_create_initial_menus(root_menu);
2036  */
2037     void
2038 gui_create_initial_menus(vimmenu_T *menu)
2039 {
2040     int		idx = 0;
2041 
2042     while (menu != NULL)
2043     {
2044 	// Don't add a menu when only a tip was defined.
2045 	if (menu->modes & MENU_ALL_MODES)
2046 	{
2047 	    if (menu->children != NULL)
2048 	    {
2049 		gui_mch_add_menu(menu, idx);
2050 		gui_create_initial_menus(menu->children);
2051 	    }
2052 	    else
2053 		gui_mch_add_menu_item(menu, idx);
2054 	}
2055 	menu = menu->next;
2056 	++idx;
2057     }
2058 }
2059 
2060 /*
2061  * Used recursively by gui_update_menus (see below)
2062  */
2063     static void
2064 gui_update_menus_recurse(vimmenu_T *menu, int mode)
2065 {
2066     int		grey;
2067 
2068     while (menu)
2069     {
2070 	if ((menu->modes & menu->enabled & mode)
2071 # if defined(FEAT_GUI_MSWIN) && defined(FEAT_TEAROFF)
2072 		|| menu_is_tearoff(menu->dname)
2073 # endif
2074 	   )
2075 	    grey = FALSE;
2076 	else
2077 	    grey = TRUE;
2078 # ifdef FEAT_GUI_ATHENA
2079 	// Hiding menus doesn't work for Athena, it can cause a crash.
2080 	gui_mch_menu_grey(menu, grey);
2081 # else
2082 	// Never hide a toplevel menu, it may make the menubar resize or
2083 	// disappear. Same problem for ToolBar items.
2084 	if (vim_strchr(p_go, GO_GREY) != NULL || menu->parent == NULL
2085 #  ifdef FEAT_TOOLBAR
2086 		|| menu_is_toolbar(menu->parent->name)
2087 #  endif
2088 		   )
2089 	    gui_mch_menu_grey(menu, grey);
2090 	else
2091 	    gui_mch_menu_hidden(menu, grey);
2092 # endif
2093 	gui_update_menus_recurse(menu->children, mode);
2094 	menu = menu->next;
2095     }
2096 }
2097 
2098 /*
2099  * Make sure only the valid menu items appear for this mode.  If
2100  * force_menu_update is not TRUE, then we only do this if the mode has changed
2101  * since last time.  If "modes" is not 0, then we use these modes instead.
2102  */
2103     void
2104 gui_update_menus(int modes)
2105 {
2106     static int	    prev_mode = -1;
2107     int		    mode = 0;
2108 
2109     if (modes != 0x0)
2110 	mode = modes;
2111     else
2112 	mode = get_menu_mode_flag();
2113 
2114     if (force_menu_update || mode != prev_mode)
2115     {
2116 	gui_update_menus_recurse(root_menu, mode);
2117 	gui_mch_draw_menubar();
2118 	prev_mode = mode;
2119 	force_menu_update = FALSE;
2120     }
2121 }
2122 
2123 # if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_MOTIF) \
2124     || defined(FEAT_GUI_GTK) || defined(FEAT_GUI_PHOTON) || defined(PROTO)
2125 /*
2126  * Check if a key is used as a mnemonic for a toplevel menu.
2127  * Case of the key is ignored.
2128  */
2129     int
2130 gui_is_menu_shortcut(int key)
2131 {
2132     vimmenu_T	*menu;
2133 
2134     if (key < 256)
2135 	key = TOLOWER_LOC(key);
2136     FOR_ALL_MENUS(menu)
2137 	if (menu->mnemonic == key
2138 		|| (menu->mnemonic < 256 && TOLOWER_LOC(menu->mnemonic) == key))
2139 	    return TRUE;
2140     return FALSE;
2141 }
2142 # endif
2143 #endif // FEAT_GUI
2144 
2145 #if (defined(FEAT_GUI_MSWIN) && defined(FEAT_TEAROFF)) || defined(PROTO)
2146 
2147 /*
2148  * Deal with tearoff items that are added like a menu item.
2149  * Currently only for Win32 GUI.  Others may follow later.
2150  */
2151 
2152     void
2153 gui_mch_toggle_tearoffs(int enable)
2154 {
2155     int		pri_tab[MENUDEPTH + 1];
2156     int		i;
2157 
2158     if (enable)
2159     {
2160 	for (i = 0; i < MENUDEPTH; ++i)
2161 	    pri_tab[i] = 500;
2162 	pri_tab[MENUDEPTH] = -1;
2163 	gui_create_tearoffs_recurse(root_menu, (char_u *)"", pri_tab, 0);
2164     }
2165     else
2166 	gui_destroy_tearoffs_recurse(root_menu);
2167     s_tearoffs = enable;
2168 }
2169 
2170 /*
2171  * Recursively add tearoff items
2172  */
2173     static void
2174 gui_create_tearoffs_recurse(
2175     vimmenu_T		*menu,
2176     const char_u	*pname,
2177     int			*pri_tab,
2178     int			pri_idx)
2179 {
2180     char_u	*newpname = NULL;
2181     int		len;
2182     char_u	*s;
2183     char_u	*d;
2184 
2185     if (pri_tab[pri_idx + 1] != -1)
2186 	++pri_idx;
2187     while (menu != NULL)
2188     {
2189 	if (menu->children != NULL && menu_is_menubar(menu->name))
2190 	{
2191 	    // Add the menu name to the menu path.  Insert a backslash before
2192 	    // dots (it's used to separate menu names).
2193 	    len = (int)STRLEN(pname) + (int)STRLEN(menu->name);
2194 	    for (s = menu->name; *s; ++s)
2195 		if (*s == '.' || *s == '\\')
2196 		    ++len;
2197 	    newpname = alloc(len + TEAR_LEN + 2);
2198 	    if (newpname != NULL)
2199 	    {
2200 		STRCPY(newpname, pname);
2201 		d = newpname + STRLEN(newpname);
2202 		for (s = menu->name; *s; ++s)
2203 		{
2204 		    if (*s == '.' || *s == '\\')
2205 			*d++ = '\\';
2206 		    *d++ = *s;
2207 		}
2208 		*d = NUL;
2209 
2210 		// check if tearoff already exists
2211 		if (STRCMP(menu->children->name, TEAR_STRING) != 0)
2212 		{
2213 		    gui_add_tearoff(newpname, pri_tab, pri_idx - 1);
2214 		    *d = NUL;			// remove TEAR_STRING
2215 		}
2216 
2217 		STRCAT(newpname, ".");
2218 		gui_create_tearoffs_recurse(menu->children, newpname,
2219 							    pri_tab, pri_idx);
2220 		vim_free(newpname);
2221 	    }
2222 	}
2223 	menu = menu->next;
2224     }
2225 }
2226 
2227 /*
2228  * Add tear-off menu item for a submenu.
2229  * "tearpath" is the menu path, and must have room to add TEAR_STRING.
2230  */
2231     static void
2232 gui_add_tearoff(char_u *tearpath, int *pri_tab, int pri_idx)
2233 {
2234     char_u	*tbuf;
2235     int		t;
2236     vimmenu_T	menuarg;
2237 
2238     tbuf = alloc(5 + (unsigned int)STRLEN(tearpath));
2239     if (tbuf != NULL)
2240     {
2241 	tbuf[0] = K_SPECIAL;
2242 	tbuf[1] = K_SECOND(K_TEAROFF);
2243 	tbuf[2] = K_THIRD(K_TEAROFF);
2244 	STRCPY(tbuf + 3, tearpath);
2245 	STRCAT(tbuf + 3, "\r");
2246 
2247 	STRCAT(tearpath, ".");
2248 	STRCAT(tearpath, TEAR_STRING);
2249 
2250 	// Priority of tear-off is always 1
2251 	t = pri_tab[pri_idx + 1];
2252 	pri_tab[pri_idx + 1] = 1;
2253 
2254 #ifdef FEAT_TOOLBAR
2255 	menuarg.iconfile = NULL;
2256 	menuarg.iconidx = -1;
2257 	menuarg.icon_builtin = FALSE;
2258 #endif
2259 	menuarg.noremap[0] = REMAP_NONE;
2260 	menuarg.silent[0] = TRUE;
2261 
2262 	menuarg.modes = MENU_ALL_MODES;
2263 	add_menu_path(tearpath, &menuarg, pri_tab, tbuf, FALSE);
2264 
2265 	menuarg.modes = MENU_TIP_MODE;
2266 	add_menu_path(tearpath, &menuarg, pri_tab,
2267 		(char_u *)_("Tear off this menu"), FALSE);
2268 
2269 	pri_tab[pri_idx + 1] = t;
2270 	vim_free(tbuf);
2271     }
2272 }
2273 
2274 /*
2275  * Recursively destroy tearoff items
2276  */
2277     static void
2278 gui_destroy_tearoffs_recurse(vimmenu_T *menu)
2279 {
2280     while (menu)
2281     {
2282 	if (menu->children)
2283 	{
2284 	    // check if tearoff exists
2285 	    if (STRCMP(menu->children->name, TEAR_STRING) == 0)
2286 	    {
2287 		// Disconnect the item and free the memory
2288 		free_menu(&menu->children);
2289 	    }
2290 	    if (menu->children != NULL) // if not the last one
2291 		gui_destroy_tearoffs_recurse(menu->children);
2292 	}
2293 	menu = menu->next;
2294     }
2295 }
2296 
2297 #endif // FEAT_GUI_MSWIN && FEAT_TEAROFF
2298 
2299 /*
2300  * Execute "menu".  Use by ":emenu" and the window toolbar.
2301  * "eap" is NULL for the window toolbar.
2302  * "mode_idx" specifies a MENU_INDEX_ value, use -1 to depend on the current
2303  * state.
2304  */
2305     void
2306 execute_menu(exarg_T *eap, vimmenu_T *menu, int mode_idx)
2307 {
2308     int		idx = mode_idx;
2309 
2310     if (idx < 0)
2311     {
2312 	// Use the Insert mode entry when returning to Insert mode.
2313 	if (restart_edit
2314 #ifdef FEAT_EVAL
2315 		&& !current_sctx.sc_sid
2316 #endif
2317 		)
2318 	{
2319 	    idx = MENU_INDEX_INSERT;
2320 	}
2321 #ifdef FEAT_TERMINAL
2322 	else if (term_use_loop())
2323 	{
2324 	    idx = MENU_INDEX_TERMINAL;
2325 	}
2326 #endif
2327 	else if (VIsual_active)
2328 	{
2329 	    idx = MENU_INDEX_VISUAL;
2330 	}
2331 	else if (eap != NULL && eap->addr_count)
2332 	{
2333 	    pos_T	tpos;
2334 
2335 	    idx = MENU_INDEX_VISUAL;
2336 
2337 	    // GEDDES: This is not perfect - but it is a
2338 	    // quick way of detecting whether we are doing this from a
2339 	    // selection - see if the range matches up with the visual
2340 	    // select start and end.
2341 	    if ((curbuf->b_visual.vi_start.lnum == eap->line1)
2342 		    && (curbuf->b_visual.vi_end.lnum) == eap->line2)
2343 	    {
2344 		// Set it up for visual mode - equivalent to gv.
2345 		VIsual_mode = curbuf->b_visual.vi_mode;
2346 		tpos = curbuf->b_visual.vi_end;
2347 		curwin->w_cursor = curbuf->b_visual.vi_start;
2348 		curwin->w_curswant = curbuf->b_visual.vi_curswant;
2349 	    }
2350 	    else
2351 	    {
2352 		// Set it up for line-wise visual mode
2353 		VIsual_mode = 'V';
2354 		curwin->w_cursor.lnum = eap->line1;
2355 		curwin->w_cursor.col = 1;
2356 		tpos.lnum = eap->line2;
2357 		tpos.col = MAXCOL;
2358 		tpos.coladd = 0;
2359 	    }
2360 
2361 	    // Activate visual mode
2362 	    VIsual_active = TRUE;
2363 	    VIsual_reselect = TRUE;
2364 	    check_cursor();
2365 	    VIsual = curwin->w_cursor;
2366 	    curwin->w_cursor = tpos;
2367 
2368 	    check_cursor();
2369 
2370 	    // Adjust the cursor to make sure it is in the correct pos
2371 	    // for exclusive mode
2372 	    if (*p_sel == 'e' && gchar_cursor() != NUL)
2373 		++curwin->w_cursor.col;
2374 	}
2375     }
2376 
2377     // For the WinBar menu always use the Normal mode menu.
2378     if (idx == -1 || eap == NULL)
2379 	idx = MENU_INDEX_NORMAL;
2380 
2381     if (idx != MENU_INDEX_INVALID && menu->strings[idx] != NULL
2382 						 && (menu->modes & (1 << idx)))
2383     {
2384 	// When executing a script or function execute the commands right now.
2385 	// Also for the window toolbar.
2386 	// Otherwise put them in the typeahead buffer.
2387 	if (eap == NULL
2388 #ifdef FEAT_EVAL
2389 		|| current_sctx.sc_sid != 0
2390 #endif
2391 	   )
2392 	{
2393 	    save_state_T save_state;
2394 
2395 	    ++ex_normal_busy;
2396 	    if (save_current_state(&save_state))
2397 		exec_normal_cmd(menu->strings[idx], menu->noremap[idx],
2398 							   menu->silent[idx]);
2399 	    restore_current_state(&save_state);
2400 	    --ex_normal_busy;
2401 	}
2402 	else
2403 	    ins_typebuf(menu->strings[idx], menu->noremap[idx], 0,
2404 						     TRUE, menu->silent[idx]);
2405     }
2406     else if (eap != NULL)
2407     {
2408 	char_u	*mode;
2409 
2410 	switch (idx)
2411 	{
2412 	    case MENU_INDEX_VISUAL:
2413 		mode = (char_u *)"Visual";
2414 		break;
2415 	    case MENU_INDEX_SELECT:
2416 		mode = (char_u *)"Select";
2417 		break;
2418 	    case MENU_INDEX_OP_PENDING:
2419 		mode = (char_u *)"Op-pending";
2420 		break;
2421 	    case MENU_INDEX_TERMINAL:
2422 		mode = (char_u *)"Terminal";
2423 		break;
2424 	    case MENU_INDEX_INSERT:
2425 		mode = (char_u *)"Insert";
2426 		break;
2427 	    case MENU_INDEX_CMDLINE:
2428 		mode = (char_u *)"Cmdline";
2429 		break;
2430 	    // case MENU_INDEX_TIP: cannot happen
2431 	    default:
2432 		mode = (char_u *)"Normal";
2433 	}
2434 	semsg(_("E335: Menu not defined for %s mode"), mode);
2435     }
2436 }
2437 
2438 /*
2439  * Lookup a menu by the descriptor name e.g. "File.New"
2440  * Returns NULL if the menu is not found
2441  */
2442     static vimmenu_T *
2443 menu_getbyname(char_u *name_arg)
2444 {
2445     char_u	*name;
2446     char_u	*saved_name;
2447     vimmenu_T	*menu;
2448     char_u	*p;
2449     int		gave_emsg = FALSE;
2450 
2451     saved_name = vim_strsave(name_arg);
2452     if (saved_name == NULL)
2453 	return NULL;
2454 
2455     menu = *get_root_menu(saved_name);
2456     name = saved_name;
2457     while (*name)
2458     {
2459 	// Find in the menu hierarchy
2460 	p = menu_name_skip(name);
2461 
2462 	while (menu != NULL)
2463 	{
2464 	    if (menu_name_equal(name, menu))
2465 	    {
2466 		if (*p == NUL && menu->children != NULL)
2467 		{
2468 		    emsg(_("E333: Menu path must lead to a menu item"));
2469 		    gave_emsg = TRUE;
2470 		    menu = NULL;
2471 		}
2472 		else if (*p != NUL && menu->children == NULL)
2473 		{
2474 		    emsg(_(e_notsubmenu));
2475 		    menu = NULL;
2476 		}
2477 		break;
2478 	    }
2479 	    menu = menu->next;
2480 	}
2481 	if (menu == NULL || *p == NUL)
2482 	    break;
2483 	menu = menu->children;
2484 	name = p;
2485     }
2486     vim_free(saved_name);
2487     if (menu == NULL)
2488     {
2489 	if (!gave_emsg)
2490 	    semsg(_("E334: Menu not found: %s"), name_arg);
2491 	return NULL;
2492     }
2493 
2494     return menu;
2495 }
2496 
2497 /*
2498  * Given a menu descriptor, e.g. "File.New", find it in the menu hierarchy and
2499  * execute it.
2500  */
2501     void
2502 ex_emenu(exarg_T *eap)
2503 {
2504     vimmenu_T	*menu;
2505     char_u	*arg = eap->arg;
2506     int		mode_idx = -1;
2507 
2508     if (arg[0] && VIM_ISWHITE(arg[1]))
2509     {
2510 	switch (arg[0])
2511 	{
2512 	    case 'n': mode_idx = MENU_INDEX_NORMAL; break;
2513 	    case 'v': mode_idx = MENU_INDEX_VISUAL; break;
2514 	    case 's': mode_idx = MENU_INDEX_SELECT; break;
2515 	    case 'o': mode_idx = MENU_INDEX_OP_PENDING; break;
2516 	    case 't': mode_idx = MENU_INDEX_TERMINAL; break;
2517 	    case 'i': mode_idx = MENU_INDEX_INSERT; break;
2518 	    case 'c': mode_idx = MENU_INDEX_CMDLINE; break;
2519 	    default: semsg(_(e_invarg2), arg);
2520 		     return;
2521 	}
2522 	arg = skipwhite(arg + 2);
2523     }
2524 
2525     menu = menu_getbyname(arg);
2526     if (menu == NULL)
2527 	return;
2528 
2529     // Found the menu, so execute.
2530     execute_menu(eap, menu, mode_idx);
2531 }
2532 
2533 /*
2534  * Handle a click in the window toolbar of "wp" at column "col".
2535  */
2536     void
2537 winbar_click(win_T *wp, int col)
2538 {
2539     int		idx;
2540 
2541     if (wp->w_winbar_items == NULL)
2542 	return;
2543     for (idx = 0; wp->w_winbar_items[idx].wb_menu != NULL; ++idx)
2544     {
2545 	winbar_item_T *item = &wp->w_winbar_items[idx];
2546 
2547 	if (col >= item->wb_startcol && col <= item->wb_endcol)
2548 	{
2549 	    win_T   *save_curwin = NULL;
2550 	    pos_T   save_visual = VIsual;
2551 	    int	    save_visual_active = VIsual_active;
2552 	    int	    save_visual_select = VIsual_select;
2553 	    int	    save_visual_reselect = VIsual_reselect;
2554 	    int	    save_visual_mode = VIsual_mode;
2555 
2556 	    if (wp != curwin)
2557 	    {
2558 		// Clicking in the window toolbar of a not-current window.
2559 		// Make that window the current one and save Visual mode.
2560 		save_curwin = curwin;
2561 		VIsual_active = FALSE;
2562 		curwin = wp;
2563 		curbuf = curwin->w_buffer;
2564 		check_cursor();
2565 	    }
2566 
2567 	    // Note: the command might close the current window.
2568 	    execute_menu(NULL, item->wb_menu, -1);
2569 
2570 	    if (save_curwin != NULL && win_valid(save_curwin))
2571 	    {
2572 		curwin = save_curwin;
2573 		curbuf = curwin->w_buffer;
2574 		VIsual = save_visual;
2575 		VIsual_active = save_visual_active;
2576 		VIsual_select = save_visual_select;
2577 		VIsual_reselect = save_visual_reselect;
2578 		VIsual_mode = save_visual_mode;
2579 	    }
2580 	    if (!win_valid(wp))
2581 		break;
2582 	}
2583     }
2584 }
2585 
2586 #if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_GTK) \
2587 	|| defined(FEAT_TERM_POPUP_MENU) || defined(FEAT_GUI_HAIKU) \
2588 	|| defined(FEAT_BEVAL_TIP) || defined(PROTO)
2589 /*
2590  * Given a menu descriptor, e.g. "File.New", find it in the menu hierarchy.
2591  */
2592     vimmenu_T *
2593 gui_find_menu(char_u *path_name)
2594 {
2595     vimmenu_T	*menu = NULL;
2596     char_u	*name;
2597     char_u	*saved_name;
2598     char_u	*p;
2599 
2600     menu = *get_root_menu(path_name);
2601 
2602     saved_name = vim_strsave(path_name);
2603     if (saved_name == NULL)
2604 	return NULL;
2605 
2606     name = saved_name;
2607     while (*name)
2608     {
2609 	// find the end of one dot-separated name and put a NUL at the dot
2610 	p = menu_name_skip(name);
2611 
2612 	while (menu != NULL)
2613 	{
2614 	    if (menu_name_equal(name, menu))
2615 	    {
2616 		if (menu->children == NULL)
2617 		{
2618 		    // found a menu item instead of a sub-menu
2619 		    if (*p == NUL)
2620 			emsg(_("E336: Menu path must lead to a sub-menu"));
2621 		    else
2622 			emsg(_(e_notsubmenu));
2623 		    menu = NULL;
2624 		    goto theend;
2625 		}
2626 		if (*p == NUL)	    // found a full match
2627 		    goto theend;
2628 		break;
2629 	    }
2630 	    menu = menu->next;
2631 	}
2632 	if (menu == NULL)	// didn't find it
2633 	    break;
2634 
2635 	// Found a match, search the sub-menu.
2636 	menu = menu->children;
2637 	name = p;
2638     }
2639 
2640     if (menu == NULL)
2641 	emsg(_("E337: Menu not found - check menu names"));
2642 theend:
2643     vim_free(saved_name);
2644     return menu;
2645 }
2646 #endif
2647 
2648 #ifdef FEAT_MULTI_LANG
2649 /*
2650  * Translation of menu names.  Just a simple lookup table.
2651  */
2652 
2653 typedef struct
2654 {
2655     char_u	*from;		// English name
2656     char_u	*from_noamp;	// same, without '&'
2657     char_u	*to;		// translated name
2658 } menutrans_T;
2659 
2660 static garray_T menutrans_ga = {0, 0, 0, 0, NULL};
2661 #endif
2662 
2663 /*
2664  * ":menutrans".
2665  * This function is also defined without the +multi_lang feature, in which
2666  * case the commands are ignored.
2667  */
2668     void
2669 ex_menutranslate(exarg_T *eap UNUSED)
2670 {
2671 #ifdef FEAT_MULTI_LANG
2672     char_u		*arg = eap->arg;
2673     menutrans_T		*tp;
2674     int			i;
2675     char_u		*from, *from_noamp, *to;
2676 
2677     if (menutrans_ga.ga_itemsize == 0)
2678 	ga_init2(&menutrans_ga, (int)sizeof(menutrans_T), 5);
2679 
2680     /*
2681      * ":menutrans clear": clear all translations.
2682      */
2683     if (STRNCMP(arg, "clear", 5) == 0 && ends_excmd2(arg, skipwhite(arg + 5)))
2684     {
2685 	tp = (menutrans_T *)menutrans_ga.ga_data;
2686 	for (i = 0; i < menutrans_ga.ga_len; ++i)
2687 	{
2688 	    vim_free(tp[i].from);
2689 	    vim_free(tp[i].from_noamp);
2690 	    vim_free(tp[i].to);
2691 	}
2692 	ga_clear(&menutrans_ga);
2693 # ifdef FEAT_EVAL
2694 	// Delete all "menutrans_" global variables.
2695 	del_menutrans_vars();
2696 # endif
2697     }
2698     else
2699     {
2700 	// ":menutrans from to": add translation
2701 	from = arg;
2702 	arg = menu_skip_part(arg);
2703 	to = skipwhite(arg);
2704 	*arg = NUL;
2705 	arg = menu_skip_part(to);
2706 	if (arg == to || ends_excmd2(eap->arg, from)
2707 		      || ends_excmd2(eap->arg, to)
2708 		      || !ends_excmd2(eap->arg, skipwhite(arg)))
2709 	    emsg(_(e_invarg));
2710 	else
2711 	{
2712 	    if (ga_grow(&menutrans_ga, 1) == OK)
2713 	    {
2714 		tp = (menutrans_T *)menutrans_ga.ga_data;
2715 		from = vim_strsave(from);
2716 		if (from != NULL)
2717 		{
2718 		    from_noamp = menu_text(from, NULL, NULL);
2719 		    to = vim_strnsave(to, arg - to);
2720 		    if (from_noamp != NULL && to != NULL)
2721 		    {
2722 			menu_translate_tab_and_shift(from);
2723 			menu_translate_tab_and_shift(to);
2724 			menu_unescape_name(from);
2725 			menu_unescape_name(to);
2726 			tp[menutrans_ga.ga_len].from = from;
2727 			tp[menutrans_ga.ga_len].from_noamp = from_noamp;
2728 			tp[menutrans_ga.ga_len].to = to;
2729 			++menutrans_ga.ga_len;
2730 		    }
2731 		    else
2732 		    {
2733 			vim_free(from);
2734 			vim_free(from_noamp);
2735 			vim_free(to);
2736 		    }
2737 		}
2738 	    }
2739 	}
2740     }
2741 #endif
2742 }
2743 
2744 #if defined(FEAT_MULTI_LANG) || defined(FEAT_TOOLBAR)
2745 /*
2746  * Find the character just after one part of a menu name.
2747  */
2748     static char_u *
2749 menu_skip_part(char_u *p)
2750 {
2751     while (*p != NUL && *p != '.' && !VIM_ISWHITE(*p))
2752     {
2753 	if ((*p == '\\' || *p == Ctrl_V) && p[1] != NUL)
2754 	    ++p;
2755 	++p;
2756     }
2757     return p;
2758 }
2759 #endif
2760 
2761 #ifdef FEAT_MULTI_LANG
2762 /*
2763  * Lookup part of a menu name in the translations.
2764  * Return a pointer to the translation or NULL if not found.
2765  */
2766     static char_u *
2767 menutrans_lookup(char_u *name, int len)
2768 {
2769     menutrans_T		*tp = (menutrans_T *)menutrans_ga.ga_data;
2770     int			i;
2771     char_u		*dname;
2772 
2773     for (i = 0; i < menutrans_ga.ga_len; ++i)
2774 	if (STRNICMP(name, tp[i].from, len) == 0 && tp[i].from[len] == NUL)
2775 	    return tp[i].to;
2776 
2777     // Now try again while ignoring '&' characters.
2778     i = name[len];
2779     name[len] = NUL;
2780     dname = menu_text(name, NULL, NULL);
2781     name[len] = i;
2782     if (dname != NULL)
2783     {
2784 	for (i = 0; i < menutrans_ga.ga_len; ++i)
2785 	    if (STRICMP(dname, tp[i].from_noamp) == 0)
2786 	    {
2787 		vim_free(dname);
2788 		return tp[i].to;
2789 	    }
2790 	vim_free(dname);
2791     }
2792 
2793     return NULL;
2794 }
2795 
2796 /*
2797  * Unescape the name in the translate dictionary table.
2798  */
2799     static void
2800 menu_unescape_name(char_u *name)
2801 {
2802     char_u  *p;
2803 
2804     for (p = name; *p && *p != '.'; MB_PTR_ADV(p))
2805 	if (*p == '\\')
2806 	    STRMOVE(p, p + 1);
2807 }
2808 #endif // FEAT_MULTI_LANG
2809 
2810 /*
2811  * Isolate the menu name.
2812  * Skip the menu name, and translate <Tab> into a real TAB.
2813  */
2814     static char_u *
2815 menu_translate_tab_and_shift(char_u *arg_start)
2816 {
2817     char_u	*arg = arg_start;
2818 
2819     while (*arg && !VIM_ISWHITE(*arg))
2820     {
2821 	if ((*arg == '\\' || *arg == Ctrl_V) && arg[1] != NUL)
2822 	    arg++;
2823 	else if (STRNICMP(arg, "<TAB>", 5) == 0)
2824 	{
2825 	    *arg = TAB;
2826 	    STRMOVE(arg + 1, arg + 5);
2827 	}
2828 	arg++;
2829     }
2830     if (*arg != NUL)
2831 	*arg++ = NUL;
2832     arg = skipwhite(arg);
2833 
2834     return arg;
2835 }
2836 
2837 /*
2838  * Get the information about a menu item in mode 'which'
2839  */
2840     static int
2841 menuitem_getinfo(vimmenu_T *menu, int modes, dict_T *dict)
2842 {
2843     int		status;
2844 
2845     if (menu_is_tearoff(menu->dname))		// skip tearoff menu item
2846 	return OK;
2847 
2848     status = dict_add_string(dict, "name", menu->name);
2849     if (status == OK)
2850 	status = dict_add_string(dict, "display", menu->dname);
2851     if (status == OK && menu->actext != NULL)
2852 	status = dict_add_string(dict, "accel", menu->actext);
2853     if (status == OK)
2854 	status = dict_add_number(dict, "priority", menu->priority);
2855     if (status == OK)
2856 	status = dict_add_string(dict, "modes",
2857 					get_menu_mode_str(menu->modes));
2858 #ifdef FEAT_TOOLBAR
2859     if (status == OK && menu->iconfile != NULL)
2860 	status = dict_add_string(dict, "icon", menu->iconfile);
2861     if (status == OK && menu->iconidx >= 0)
2862 	status = dict_add_number(dict, "iconidx", menu->iconidx);
2863 #endif
2864     if (status == OK)
2865     {
2866 	char_u	buf[NUMBUFLEN];
2867 
2868 	if (has_mbyte)
2869 	    buf[utf_char2bytes(menu->mnemonic, buf)] = NUL;
2870 	else
2871 	{
2872 	    buf[0] = (char_u)menu->mnemonic;
2873 	    buf[1] = NUL;
2874 	}
2875 	status = dict_add_string(dict, "shortcut", buf);
2876     }
2877     if (status == OK && menu->children == NULL)
2878     {
2879 	int		bit;
2880 
2881 	// Get the first mode in which the menu is available
2882 	for (bit = 0; bit < MENU_MODES && !((1 << bit) & modes); bit++)
2883 	    ;
2884 	if (bit < MENU_MODES) // just in case, avoid Coverity warning
2885 	{
2886 	    if (menu->strings[bit] != NULL)
2887 	    {
2888 		char_u *tofree = NULL;
2889 
2890 		status = dict_add_string(dict, "rhs",
2891 			*menu->strings[bit] == NUL
2892 				? (char_u *)"<Nop>"
2893 				: (tofree = str2special_save(
2894 						  menu->strings[bit], FALSE)));
2895 		vim_free(tofree);
2896 	    }
2897 	    if (status == OK)
2898 		status = dict_add_bool(dict, "noremenu",
2899 					     menu->noremap[bit] == REMAP_NONE);
2900 	    if (status == OK)
2901 		status = dict_add_bool(dict, "script",
2902 					   menu->noremap[bit] == REMAP_SCRIPT);
2903 	    if (status == OK)
2904 		status = dict_add_bool(dict, "silent", menu->silent[bit]);
2905 	    if (status == OK)
2906 		status = dict_add_bool(dict, "enabled",
2907 					  ((menu->enabled & (1 << bit)) != 0));
2908 	}
2909     }
2910 
2911     // If there are submenus, add all the submenu display names
2912     if (status == OK && menu->children != NULL)
2913     {
2914 	list_T		*l = list_alloc();
2915 	vimmenu_T	*child;
2916 
2917 	if (l == NULL)
2918 	    return FAIL;
2919 
2920 	dict_add_list(dict, "submenus", l);
2921 	child = menu->children;
2922 	while (child)
2923 	{
2924 	    if (!menu_is_tearoff(child->dname))		// skip tearoff menu
2925 		list_append_string(l, child->dname, -1);
2926 	    child = child->next;
2927 	}
2928     }
2929 
2930     return status;
2931 }
2932 
2933 /*
2934  * "menu_info()" function
2935  * Return information about a menu (including all the child menus)
2936  */
2937     void
2938 f_menu_info(typval_T *argvars, typval_T *rettv)
2939 {
2940     char_u	*menu_name;
2941     char_u	*which;
2942     int		modes;
2943     char_u	*saved_name;
2944     char_u	*name;
2945     vimmenu_T	*menu;
2946     dict_T	*retdict;
2947 
2948     if (rettv_dict_alloc(rettv) != OK)
2949 	return;
2950     retdict = rettv->vval.v_dict;
2951 
2952     menu_name = tv_get_string_chk(&argvars[0]);
2953     if (menu_name == NULL)
2954 	return;
2955 
2956     // menu mode
2957     if (argvars[1].v_type != VAR_UNKNOWN)
2958 	which = tv_get_string_chk(&argvars[1]);
2959     else
2960 	which = (char_u *)"";	    // Default is modes for "menu"
2961     if (which == NULL)
2962 	return;
2963 
2964     modes = get_menu_cmd_modes(which, *which == '!', NULL, NULL);
2965 
2966     // Locate the specified menu or menu item
2967     menu = *get_root_menu(menu_name);
2968     saved_name = vim_strsave(menu_name);
2969     if (saved_name == NULL)
2970 	return;
2971     if (*saved_name != NUL)
2972     {
2973 	char_u	*p;
2974 
2975 	name = saved_name;
2976 	while (*name)
2977 	{
2978 	    // Find in the menu hierarchy
2979 	    p = menu_name_skip(name);
2980 	    while (menu != NULL)
2981 	    {
2982 		if (menu_name_equal(name, menu))
2983 		    break;
2984 		menu = menu->next;
2985 	    }
2986 	    if (menu == NULL || *p == NUL)
2987 		break;
2988 	    menu = menu->children;
2989 	    name = p;
2990 	}
2991     }
2992     vim_free(saved_name);
2993 
2994     if (menu == NULL)		// specified menu not found
2995 	return;
2996 
2997     if (menu->modes & modes)
2998 	menuitem_getinfo(menu, modes, retdict);
2999 }
3000 
3001 #endif // FEAT_MENU
3002