home *** CD-ROM | disk | FTP | other *** search
/ vim.ftp.fu-berlin.de / 2015-02-03.vim.ftp.fu-berlin.de.tar / vim.ftp.fu-berlin.de / unix / vim-6.2.tar.bz2 / vim-6.2.tar / vim62 / src / menu.c < prev    next >
Encoding:
C/C++ Source or Header  |  2003-05-25  |  52.3 KB  |  2,335 lines

  1. /* vi:set ts=8 sts=4 sw=4:
  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_W32
  22. static int add_menu_path __ARGS((char_u *, vimmenu_T *, int *, char_u *, int));
  23. #else
  24. static int add_menu_path __ARGS((char_u *, vimmenu_T *, int *, char_u *));
  25. #endif
  26. static int menu_nable_recurse __ARGS((vimmenu_T *menu, char_u *name, int modes, int enable));
  27. static int remove_menu __ARGS((vimmenu_T **, char_u *, int, int silent));
  28. static void free_menu __ARGS((vimmenu_T **menup));
  29. static void free_menu_string __ARGS((vimmenu_T *, int));
  30. static int show_menus __ARGS((char_u *, int));
  31. static void show_menus_recursive __ARGS((vimmenu_T *, int, int));
  32. static int menu_name_equal __ARGS((char_u *name, vimmenu_T *menu));
  33. static int menu_namecmp __ARGS((char_u *name, char_u *mname));
  34. static int get_menu_cmd_modes __ARGS((char_u *, int, int *, int *));
  35. static char_u *popup_mode_name __ARGS((char_u *name, int idx));
  36. static char_u *menu_text __ARGS((char_u *text, int *mnemonic, char_u **actext));
  37. #ifdef FEAT_GUI
  38. static int get_menu_mode __ARGS((void));
  39. static void gui_update_menus_recurse __ARGS((vimmenu_T *, int));
  40. #endif
  41.  
  42. #if defined(FEAT_GUI_W32) && defined(FEAT_TEAROFF)
  43. static void gui_create_tearoffs_recurse __ARGS((vimmenu_T *menu, const char_u *pname, int *pri_tab, int pri_idx));
  44. static void gui_add_tearoff __ARGS((char_u *tearpath, int *pri_tab, int pri_idx));
  45. static void gui_destroy_tearoffs_recurse __ARGS((vimmenu_T *menu));
  46. static int s_tearoffs = FALSE;
  47. #endif
  48.  
  49. static int menu_is_hidden __ARGS((char_u *name));
  50. #if defined(FEAT_CMDL_COMPL) || (defined(FEAT_GUI_W32) && defined(FEAT_TEAROFF))
  51. static int menu_is_tearoff __ARGS((char_u *name));
  52. #endif
  53.  
  54. #if defined(FEAT_MULTI_LANG) || defined(FEAT_TOOLBAR)
  55. static char_u *menu_skip_part __ARGS((char_u *p));
  56. #endif
  57. #ifdef FEAT_MULTI_LANG
  58. static char_u *menutrans_lookup __ARGS((char_u *name, int len));
  59. #endif
  60.  
  61. /* The character for each menu mode */
  62. static char_u    menu_mode_chars[] = {'n', 'v', 'o', 'i', 'c', 't'};
  63.  
  64. static char_u e_notsubmenu[] = N_("E327: Part of menu-item path is not sub-menu");
  65. static char_u e_othermode[] = N_("E328: Menu only exists in another mode");
  66. static char_u e_nomenu[] = N_("E329: No menu of that name");
  67.  
  68. #ifdef FEAT_TOOLBAR
  69. static const char *toolbar_names[] =
  70. {
  71.     /*  0 */ "New", "Open", "Save", "Undo", "Redo",
  72.     /*  5 */ "Cut", "Copy", "Paste", "Print", "Help",
  73.     /* 10 */ "Find", "SaveAll", "SaveSesn", "NewSesn", "LoadSesn",
  74.     /* 15 */ "RunScript", "Replace", "WinClose", "WinMax", "WinMin",
  75.     /* 20 */ "WinSplit", "Shell", "FindPrev", "FindNext", "FindHelp",
  76.     /* 25 */ "Make", "TagJump", "RunCtags", "WinVSplit", "WinMaxWidth",
  77.     /* 30 */ "WinMinWidth", "Exit"
  78. };
  79. # define TOOLBAR_NAME_COUNT (sizeof(toolbar_names) / sizeof(char *))
  80. #endif
  81.  
  82. /*
  83.  * Do the :menu command and relatives.
  84.  */
  85.     void
  86. ex_menu(eap)
  87.     exarg_T    *eap;            /* Ex command arguments */
  88. {
  89.     char_u    *menu_path;
  90.     int        modes;
  91.     char_u    *map_to;
  92.     int        noremap;
  93.     int        silent = FALSE;
  94.     int        unmenu;
  95.     char_u    *map_buf;
  96.     char_u    *arg;
  97.     char_u    *p;
  98.     int        i;
  99. #if defined(FEAT_GUI) && !defined(FEAT_GUI_GTK)
  100.     int        old_menu_height;
  101. # if defined(FEAT_TOOLBAR) && !defined(FEAT_GUI_W32) && !defined(FEAT_GUI_W16)
  102.     int        old_toolbar_height;
  103. # endif
  104. #endif
  105.     int        pri_tab[MENUDEPTH + 1];
  106.     int        enable = MAYBE;        /* TRUE for "menu enable", FALSE for "menu
  107.                      * disable */
  108. #ifdef FEAT_MULTI_LANG
  109.     char_u    *tofree = NULL;
  110.     char_u    *new_cmd;
  111. #endif
  112. #ifdef FEAT_TOOLBAR
  113.     char_u    *icon = NULL;
  114. #endif
  115.     vimmenu_T    menuarg;
  116.  
  117.     modes = get_menu_cmd_modes(eap->cmd, eap->forceit, &noremap, &unmenu);
  118.     arg = eap->arg;
  119.  
  120.     for (;;)
  121.     {
  122.     if (STRNCMP(arg, "<script>", 8) == 0)
  123.     {
  124.         noremap = REMAP_SCRIPT;
  125.         arg = skipwhite(arg + 8);
  126.         continue;
  127.     }
  128.     if (STRNCMP(arg, "<silent>", 8) == 0)
  129.     {
  130.         silent = TRUE;
  131.         arg = skipwhite(arg + 8);
  132.         continue;
  133.     }
  134.     break;
  135.     }
  136.  
  137.  
  138.     /* Locate an optional "icon=filename" argument. */
  139.     if (STRNCMP(arg, "icon=", 5) == 0)
  140.     {
  141.     arg += 5;
  142. #ifdef FEAT_TOOLBAR
  143.     icon = arg;
  144. #endif
  145.     while (*arg != NUL && *arg != ' ')
  146.     {
  147.         if (*arg == '\\')
  148.         mch_memmove(arg, arg + 1, STRLEN(arg));
  149. #ifdef FEAT_MBYTE
  150.         if (has_mbyte)
  151.         arg += (*mb_ptr2len_check)(arg);
  152.         else
  153. #endif
  154.         ++arg;
  155.     }
  156.     if (*arg != NUL)
  157.     {
  158.         *arg++ = NUL;
  159.         arg = skipwhite(arg);
  160.     }
  161.     }
  162.  
  163.     /*
  164.      * Fill in the priority table.
  165.      */
  166.     for (p = arg; *p; ++p)
  167.     if (!isdigit(*p) && *p != '.')
  168.         break;
  169.     if (vim_iswhite(*p))
  170.     {
  171.     for (i = 0; i < MENUDEPTH && !vim_iswhite(*arg); ++i)
  172.     {
  173.         pri_tab[i] = getdigits(&arg);
  174.         if (pri_tab[i] == 0)
  175.         pri_tab[i] = 500;
  176.         if (*arg == '.')
  177.         ++arg;
  178.     }
  179.     arg = skipwhite(arg);
  180.     }
  181.     else if (eap->addr_count && eap->line2 != 0)
  182.     {
  183.     pri_tab[0] = eap->line2;
  184.     i = 1;
  185.     }
  186.     else
  187.     i = 0;
  188.     while (i < MENUDEPTH)
  189.     pri_tab[i++] = 500;
  190.     pri_tab[MENUDEPTH] = -1;        /* mark end of the table */
  191.  
  192.     /*
  193.      * Check for "disable" or "enable" argument.
  194.      */
  195.     if (STRNCMP(arg, "enable", 6) == 0 && vim_iswhite(arg[6]))
  196.     {
  197.     enable = TRUE;
  198.     arg = skipwhite(arg + 6);
  199.     }
  200.     else if (STRNCMP(arg, "disable", 7) == 0 && vim_iswhite(arg[7]))
  201.     {
  202.     enable = FALSE;
  203.     arg = skipwhite(arg + 7);
  204.     }
  205.  
  206.     /*
  207.      * If there is no argument, display all menus.
  208.      */
  209.     if (*arg == NUL)
  210.     {
  211.     show_menus(arg, modes);
  212.     return;
  213.     }
  214.  
  215. #ifdef FEAT_TOOLBAR
  216.     /*
  217.      * Need to get the toolbar icon index before doing the translation.
  218.      */
  219.     menuarg.iconidx = -1;
  220.     menuarg.icon_builtin = FALSE;
  221.     if (menu_is_toolbar(arg))
  222.     {
  223.     menu_path = menu_skip_part(arg);
  224.     if (*menu_path == '.')
  225.     {
  226.         p = menu_skip_part(++menu_path);
  227.         if (STRNCMP(menu_path, "BuiltIn", 7) == 0)
  228.         {
  229.         if (skipdigits(menu_path + 7) == p)
  230.         {
  231.             menuarg.iconidx = atoi((char *)menu_path + 7);
  232.             if (menuarg.iconidx >= TOOLBAR_NAME_COUNT)
  233.             menuarg.iconidx = -1;
  234.             else
  235.             menuarg.icon_builtin = TRUE;
  236.         }
  237.         }
  238.         else
  239.         {
  240.         for (i = 0; i < TOOLBAR_NAME_COUNT; ++i)
  241.             if (STRNCMP(toolbar_names[i], menu_path, p - menu_path)
  242.                                      == 0)
  243.             {
  244.             menuarg.iconidx = i;
  245.             break;
  246.             }
  247.         }
  248.     }
  249.     }
  250. #endif
  251.  
  252. #ifdef FEAT_MULTI_LANG
  253.     /*
  254.      * Translate menu names as specified with ":menutrans" commands.
  255.      */
  256.     menu_path = arg;
  257.     while (*menu_path)
  258.     {
  259.     /* find the end of one part and check if it should be translated */
  260.     p = menu_skip_part(menu_path);
  261.     map_to = menutrans_lookup(menu_path, (int)(p - menu_path));
  262.     if (map_to != NULL)
  263.     {
  264.         /* found a match: replace with the translated part */
  265.         i = (int)STRLEN(map_to);
  266.         new_cmd = alloc((unsigned)STRLEN(arg) + i + 1);
  267.         if (new_cmd == NULL)
  268.         break;
  269.         mch_memmove(new_cmd, arg, menu_path - arg);
  270.         mch_memmove(new_cmd + (menu_path - arg), map_to, (size_t)i);
  271.         STRCPY(new_cmd + (menu_path - arg) + i, p);
  272.         p = new_cmd + (menu_path - arg) + i;
  273.         vim_free(tofree);
  274.         tofree = new_cmd;
  275.         arg = new_cmd;
  276.     }
  277.     if (*p != '.')
  278.         break;
  279.     menu_path = p + 1;
  280.     }
  281. #endif
  282.  
  283.     /*
  284.      * Isolate the menu name.
  285.      * Skip the menu name, and translate <Tab> into a real TAB.
  286.      */
  287.     menu_path = arg;
  288.     if (*menu_path == '.')
  289.     {
  290.     EMSG2(_(e_invarg2), menu_path);
  291.     goto theend;
  292.     }
  293.  
  294.     while (*arg && !vim_iswhite(*arg))
  295.     {
  296.     if ((*arg == '\\' || *arg == Ctrl_V) && arg[1] != NUL)
  297.         arg++;
  298.     else if (STRNICMP(arg, "<TAB>", 5) == 0)
  299.     {
  300.         *arg = TAB;
  301.         mch_memmove(arg + 1, arg + 5, STRLEN(arg + 4));
  302.     }
  303.     arg++;
  304.     }
  305.     if (*arg != NUL)
  306.     *arg++ = NUL;
  307.     arg = skipwhite(arg);
  308.     map_to = arg;
  309.  
  310.     /*
  311.      * If there is only a menu name, display menus with that name.
  312.      */
  313.     if (*map_to == NUL && !unmenu && enable == MAYBE)
  314.     {
  315.     show_menus(menu_path, modes);
  316.     goto theend;
  317.     }
  318.     else if (*map_to != NUL && (unmenu || enable != MAYBE))
  319.     {
  320.     EMSG(_(e_trailing));
  321.     goto theend;
  322.     }
  323. #if defined(FEAT_GUI) && !(defined(FEAT_GUI_GTK) || defined(FEAT_GUI_PHOTON))
  324.     old_menu_height = gui.menu_height;
  325. # if defined(FEAT_TOOLBAR) && !defined(FEAT_GUI_W32) && !defined(FEAT_GUI_W16)
  326.     old_toolbar_height = gui.toolbar_height;
  327. # endif
  328. #endif
  329.  
  330.     if (enable != MAYBE)
  331.     {
  332.     /*
  333.      * Change sensitivity of the menu.
  334.      * Careful: menu_nable_recurse() changes menu_path.
  335.      */
  336.     if (STRCMP(menu_path, "*") == 0)    /* meaning: do all menus */
  337.         menu_path = (char_u *)"";
  338.     menu_nable_recurse(root_menu, menu_path, modes, enable);
  339.     }
  340.     else if (unmenu)
  341.     {
  342.     /*
  343.      * Delete menu(s).
  344.      */
  345.     if (STRCMP(menu_path, "*") == 0)    /* meaning: remove all menus */
  346.         menu_path = (char_u *)"";
  347.  
  348.     /*
  349.      * For the PopUp menu, remove a menu for each mode separately.
  350.      */
  351.     if (menu_is_popup(menu_path))
  352.     {
  353.         for (i = 0; i < MENU_INDEX_TIP; ++i)
  354.         if (modes & (1 << i))
  355.         {
  356.             p = popup_mode_name(menu_path, i);
  357.             if (p != NULL)
  358.             {
  359.             remove_menu(&root_menu, p, MENU_ALL_MODES, TRUE);
  360.             vim_free(p);
  361.             }
  362.         }
  363.     }
  364.  
  365.     /* Careful: remove_menu() changes menu_path */
  366.     remove_menu(&root_menu, menu_path, modes, FALSE);
  367.     }
  368.     else
  369.     {
  370.     /*
  371.      * Add menu(s).
  372.      * Replace special key codes.
  373.      */
  374.     if (STRICMP(map_to, "<nop>") == 0)    /* "<Nop>" means nothing */
  375.     {
  376.         map_to = (char_u *)"";
  377.         map_buf = NULL;
  378.     }
  379.     else
  380.         map_to = replace_termcodes(map_to, &map_buf, FALSE, TRUE);
  381.     menuarg.modes = modes;
  382. #ifdef FEAT_TOOLBAR
  383.     menuarg.iconfile = icon;
  384. #endif
  385.     menuarg.noremap[0] = noremap;
  386.     menuarg.silent[0] = silent;
  387.     add_menu_path(menu_path, &menuarg, pri_tab, map_to
  388. #ifdef FEAT_GUI_W32
  389.         , TRUE
  390. #endif
  391.         );
  392.  
  393.     /*
  394.      * For the PopUp menu, add a menu for each mode separately.
  395.      */
  396.     if (menu_is_popup(menu_path))
  397.     {
  398.         for (i = 0; i < MENU_INDEX_TIP; ++i)
  399.         if (modes & (1 << i))
  400.         {
  401.             p = popup_mode_name(menu_path, i);
  402.             if (p != NULL)
  403.             {
  404.             /* Include all modes, to make ":amenu" work */
  405.             menuarg.modes = modes;
  406. #ifdef FEAT_TOOLBAR
  407.             menuarg.iconfile = NULL;
  408.             menuarg.iconidx = -1;
  409.             menuarg.icon_builtin = FALSE;
  410. #endif
  411.             add_menu_path(p, &menuarg, pri_tab, map_to
  412. #ifdef FEAT_GUI_W32
  413.                 , TRUE
  414. #endif
  415.                      );
  416.             vim_free(p);
  417.             }
  418.         }
  419.     }
  420.  
  421.     vim_free(map_buf);
  422.     }
  423.  
  424. #if defined(FEAT_GUI) && !defined(FEAT_GUI_GTK)
  425.     /* If the menubar height changed, resize the window */
  426.     if (gui.in_use
  427.         && (gui.menu_height != old_menu_height
  428. # if defined(FEAT_TOOLBAR) && !defined(FEAT_GUI_W32) && !defined(FEAT_GUI_W16)
  429.         || gui.toolbar_height != old_toolbar_height
  430. # endif
  431.         ))
  432.     gui_set_shellsize(FALSE, FALSE);
  433. #endif
  434.  
  435. theend:
  436. #ifdef FEAT_MULTI_LANG
  437.     vim_free(tofree);
  438. #else
  439.     ;
  440. #endif
  441. }
  442.  
  443. /*
  444.  * Add the menu with the given name to the menu hierarchy
  445.  */
  446.     static int
  447. add_menu_path(menu_path, menuarg, pri_tab, call_data
  448. #ifdef FEAT_GUI_W32
  449.     , addtearoff
  450. #endif
  451.     )
  452.     char_u    *menu_path;
  453.     vimmenu_T    *menuarg;    /* passes modes, iconfile, iconidx,
  454.                    icon_builtin, silent[0], noremap[0] */
  455.     int        *pri_tab;
  456.     char_u    *call_data;
  457. #ifdef FEAT_GUI_W32
  458.     int        addtearoff;    /* may add tearoff item */
  459. #endif
  460. {
  461.     char_u    *path_name;
  462.     int        modes = menuarg->modes;
  463.     vimmenu_T    **menup;
  464.     vimmenu_T    *menu = NULL;
  465.     vimmenu_T    *parent;
  466.     vimmenu_T    **lower_pri;
  467.     char_u    *p;
  468.     char_u    *name;
  469.     char_u    *dname;
  470.     char_u    *next_name;
  471.     int        i;
  472.     int        c;
  473. #ifdef FEAT_GUI
  474.     int        idx;
  475.     int        new_idx;
  476. #endif
  477.     int        pri_idx = 0;
  478.     int        old_modes = 0;
  479.     int        amenu;
  480.  
  481.     /* Make a copy so we can stuff around with it, since it could be const */
  482.     path_name = vim_strsave(menu_path);
  483.     if (path_name == NULL)
  484.     return FAIL;
  485.     menup = &root_menu;
  486.     parent = NULL;
  487.     name = path_name;
  488.     while (*name)
  489.     {
  490.     /* Get name of this element in the menu hierarchy, and the simplified
  491.      * name (without mnemonic and accelerator text). */
  492.     next_name = menu_name_skip(name);
  493.     dname = menu_text(name, NULL, NULL);
  494.  
  495.     /* See if it's already there */
  496.     lower_pri = menup;
  497. #ifdef FEAT_GUI
  498.     idx = 0;
  499.     new_idx = 0;
  500. #endif
  501.     menu = *menup;
  502.     while (menu != NULL)
  503.     {
  504.         if (menu_name_equal(name, menu) || menu_name_equal(dname, menu))
  505.         {
  506.         if (*next_name == NUL && menu->children != NULL)
  507.         {
  508.             if (!sys_menu)
  509.             EMSG(_("E330: Menu path must not lead to a sub-menu"));
  510.             goto erret;
  511.         }
  512.         if (*next_name != NUL && menu->children == NULL
  513. #ifdef FEAT_GUI_W32
  514.             && addtearoff
  515. #endif
  516.             )
  517.         {
  518.             if (!sys_menu)
  519.             EMSG(_(e_notsubmenu));
  520.             goto erret;
  521.         }
  522.         break;
  523.         }
  524.         menup = &menu->next;
  525.  
  526.         /* Count menus, to find where this one needs to be inserted.
  527.          * Ignore menus that are not in the menubar (PopUp and Toolbar) */
  528.         if (parent != NULL || menu_is_menubar(menu->name))
  529.         {
  530. #ifdef FEAT_GUI
  531.         ++idx;
  532. #endif
  533.         if (menu->priority <= pri_tab[pri_idx])
  534.         {
  535.             lower_pri = menup;
  536. #ifdef FEAT_GUI
  537.             new_idx = idx;
  538. #endif
  539.         }
  540.         }
  541.         menu = menu->next;
  542.     }
  543.  
  544.     if (menu == NULL)
  545.     {
  546.         if (*next_name == NUL && parent == NULL)
  547.         {
  548.         EMSG(_("E331: Must not add menu items directly to menu bar"));
  549.         goto erret;
  550.         }
  551.  
  552.         if (menu_is_separator(dname) && *next_name != NUL)
  553.         {
  554.         EMSG(_("E332: Separator cannot be part of a menu path"));
  555.         goto erret;
  556.         }
  557.  
  558.         /* Not already there, so lets add it */
  559.         menu = (vimmenu_T *)alloc_clear((unsigned)sizeof(vimmenu_T));
  560.         if (menu == NULL)
  561.         goto erret;
  562.  
  563.         menu->modes = modes;
  564.         menu->enabled = MENU_ALL_MODES;
  565.         menu->name = vim_strsave(name);
  566.         /* separate mnemonic and accelerator text from actual menu name */
  567.         menu->dname = menu_text(name, &menu->mnemonic, &menu->actext);
  568.         menu->priority = pri_tab[pri_idx];
  569.         menu->parent = parent;
  570. #ifdef FEAT_GUI_MOTIF
  571.         menu->sensitive = TRUE;        /* the default */
  572. #endif
  573. #ifdef FEAT_BEVAL_TIP
  574.         menu->tip = NULL;
  575. #endif
  576. #ifdef FEAT_GUI_ATHENA
  577.         menu->image = None;            /* X-Windows definition for NULL*/
  578. #endif
  579.  
  580.         /*
  581.          * Add after menu that has lower priority.
  582.          */
  583.         menu->next = *lower_pri;
  584.         *lower_pri = menu;
  585.  
  586.         old_modes = 0;
  587.  
  588. #ifdef FEAT_TOOLBAR
  589.         menu->iconidx = menuarg->iconidx;
  590.         menu->icon_builtin = menuarg->icon_builtin;
  591.         if (*next_name == NUL && menuarg->iconfile != NULL)
  592.         menu->iconfile = vim_strsave(menuarg->iconfile);
  593. #endif
  594. #if defined(FEAT_GUI_W32) && defined(FEAT_TEAROFF)
  595.         /* the tearoff item must be present in the modes of each item. */
  596.         if (parent != NULL && menu_is_tearoff(parent->children->dname))
  597.         parent->children->modes |= modes;
  598. #endif
  599.     }
  600.     else
  601.     {
  602.         old_modes = menu->modes;
  603.  
  604.         /*
  605.          * If this menu option was previously only available in other
  606.          * modes, then make sure it's available for this one now
  607.          * Also enable a menu when it's created or changed.
  608.          */
  609. #ifdef FEAT_GUI_W32
  610.         /* If adding a tearbar (addtearoff == FALSE) don't update modes */
  611.         if (addtearoff)
  612. #endif
  613.         {
  614.         menu->modes |= modes;
  615.         menu->enabled |= modes;
  616.         }
  617.     }
  618.  
  619. #ifdef FEAT_GUI
  620.     /*
  621.      * Add the menu item when it's used in one of the modes, but not when
  622.      * only a tooltip is defined.
  623.      */
  624.     if ((old_modes & MENU_ALL_MODES) == 0
  625.         && (menu->modes & MENU_ALL_MODES) != 0)
  626.     {
  627.         if (gui.in_use)  /* Otherwise it will be added when GUI starts */
  628.         {
  629.         if (*next_name == NUL)
  630.         {
  631.             /* Real menu item, not sub-menu */
  632.             gui_mch_add_menu_item(menu, new_idx);
  633.  
  634.             /* Want to update menus now even if mode not changed */
  635.             force_menu_update = TRUE;
  636.         }
  637.         else
  638.         {
  639.             /* Sub-menu (not at end of path yet) */
  640.             gui_mch_add_menu(menu, new_idx);
  641.         }
  642.         }
  643.  
  644. # if defined(FEAT_GUI_W32) & defined(FEAT_TEAROFF)
  645.         /* When adding a new submenu, may add a tearoff item */
  646.         if (    addtearoff
  647.             && *next_name
  648.             && vim_strchr(p_go, GO_TEAROFF) != NULL
  649.             && menu_is_menubar(name))
  650.         {
  651.         char_u        *tearpath;
  652.  
  653.         /*
  654.          * The pointers next_name & path_name refer to a string with
  655.          * \'s and ^V's stripped out. But menu_path is a "raw"
  656.          * string, so we must correct for special characters.
  657.          */
  658.         tearpath = alloc((unsigned int)STRLEN(menu_path) + TEAR_LEN + 2);
  659.         if (tearpath != NULL)
  660.         {
  661.             char_u  *s;
  662.             int        idx;
  663.  
  664.             STRCPY(tearpath, menu_path);
  665.             idx = (int)(next_name - path_name - 1);
  666.             for (s = tearpath; *s && s < tearpath + idx; ++s)
  667.             {
  668.             if ((*s == '\\' || *s == Ctrl_V) && s[1])
  669.             {
  670.                 ++idx;
  671.                 ++s;
  672.             }
  673. #  ifdef FEAT_MBYTE
  674.             if (has_mbyte)
  675.                 s += (*mb_ptr2len_check)(s) - 1;
  676. #  endif
  677.             }
  678.             tearpath[idx] = NUL;
  679.             gui_add_tearoff(tearpath, pri_tab, pri_idx);
  680.             vim_free(tearpath);
  681.         }
  682.         }
  683. # endif
  684.     }
  685. #endif /* FEAT_GUI */
  686.  
  687.     menup = &menu->children;
  688.     parent = menu;
  689.     name = next_name;
  690.     vim_free(dname);
  691.     if (pri_tab[pri_idx + 1] != -1)
  692.         ++pri_idx;
  693.     }
  694.     vim_free(path_name);
  695.  
  696.     /*
  697.      * Only add system menu items which have not been defined yet.
  698.      * First check if this was an ":amenu".
  699.      */
  700.     amenu = ((modes & (MENU_NORMAL_MODE | MENU_INSERT_MODE)) ==
  701.                        (MENU_NORMAL_MODE | MENU_INSERT_MODE));
  702.     if (sys_menu)
  703.     modes &= ~old_modes;
  704.  
  705.     if (menu != NULL && modes)
  706.     {
  707. #ifdef FEAT_GUI
  708.     menu->cb = gui_menu_cb;
  709. #endif
  710.     p = (call_data == NULL) ? NULL : vim_strsave(call_data);
  711.  
  712.     /* loop over all modes, may add more than one */
  713.     for (i = 0; i < MENU_MODES; ++i)
  714.     {
  715.         if (modes & (1 << i))
  716.         {
  717.         /* free any old menu */
  718.         free_menu_string(menu, i);
  719.  
  720.         /* For "amenu", may insert an extra character.
  721.          * Don't do this if adding a tearbar (addtearoff == FALSE).
  722.          * Don't do this for "<Nop>". */
  723.         c = 0;
  724.         if (amenu && call_data != NULL && *call_data != NUL
  725. #ifdef FEAT_GUI_W32
  726.                && addtearoff
  727. #endif
  728.                        )
  729.         {
  730.             switch (1 << i)
  731.             {
  732.             case MENU_VISUAL_MODE:
  733.             case MENU_OP_PENDING_MODE:
  734.             case MENU_CMDLINE_MODE:
  735.                 c = Ctrl_C;
  736.                 break;
  737.             case MENU_INSERT_MODE:
  738.                 c = Ctrl_O;
  739.                 break;
  740.             }
  741.         }
  742.  
  743.         if (c)
  744.         {
  745.             menu->strings[i] = alloc((unsigned)(STRLEN(call_data) + 4));
  746.             if (menu->strings[i] != NULL)
  747.             {
  748.             menu->strings[i][0] = c;
  749.             STRCPY(menu->strings[i] + 1, call_data);
  750.             if (c == Ctrl_C)
  751.             {
  752.                 int        len = STRLEN(menu->strings[i]);
  753.  
  754.                 /* Append CTRL-\ CTRL-G to obey 'insertmode'. */
  755.                 menu->strings[i][len] = Ctrl_BSL;
  756.                 menu->strings[i][len + 1] = Ctrl_G;
  757.                 menu->strings[i][len + 2] = NUL;
  758.             }
  759.             }
  760.         }
  761.         else
  762.             menu->strings[i] = p;
  763.         menu->noremap[i] = menuarg->noremap[0];
  764.         menu->silent[i] = menuarg->silent[0];
  765.         }
  766.     }
  767. #if defined(FEAT_TOOLBAR) && (defined(FEAT_BEVAL) || defined(FEAT_GUI_GTK))
  768.     /* Need to update the menu tip. */
  769.     if (modes & MENU_TIP_MODE)
  770.         gui_mch_menu_set_tip(menu);
  771. #endif
  772.     }
  773.     return OK;
  774.  
  775. erret:
  776.     vim_free(path_name);
  777.     vim_free(dname);
  778.     return FAIL;
  779. }
  780.  
  781. /*
  782.  * Set the (sub)menu with the given name to enabled or disabled.
  783.  * Called recursively.
  784.  */
  785.     static int
  786. menu_nable_recurse(menu, name, modes, enable)
  787.     vimmenu_T    *menu;
  788.     char_u    *name;
  789.     int        modes;
  790.     int        enable;
  791. {
  792.     char_u    *p;
  793.  
  794.     if (menu == NULL)
  795.     return OK;        /* Got to bottom of hierarchy */
  796.  
  797.     /* Get name of this element in the menu hierarchy */
  798.     p = menu_name_skip(name);
  799.  
  800.     /* Find the menu */
  801.     while (menu != NULL)
  802.     {
  803.     if (*name == NUL || *name == '*' || menu_name_equal(name, menu))
  804.     {
  805.         if (*p != NUL)
  806.         {
  807.         if (menu->children == NULL)
  808.         {
  809.             EMSG(_(e_notsubmenu));
  810.             return FAIL;
  811.         }
  812.         if (menu_nable_recurse(menu->children, p, modes, enable)
  813.                                       == FAIL)
  814.             return FAIL;
  815.         }
  816.         else
  817.         if (enable)
  818.             menu->enabled |= modes;
  819.         else
  820.             menu->enabled &= ~modes;
  821.  
  822.         /*
  823.          * When name is empty, we are doing all menu items for the given
  824.          * modes, so keep looping, otherwise we are just doing the named
  825.          * menu item (which has been found) so break here.
  826.          */
  827.         if (*name != NUL && *name != '*')
  828.         break;
  829.     }
  830.     menu = menu->next;
  831.     }
  832.     if (*name != NUL && *name != '*' && menu == NULL)
  833.     {
  834.     EMSG(_(e_nomenu));
  835.     return FAIL;
  836.     }
  837.  
  838. #ifdef FEAT_GUI
  839.     /* Want to update menus now even if mode not changed */
  840.     force_menu_update = TRUE;
  841. #endif
  842.  
  843.     return OK;
  844. }
  845.  
  846. /*
  847.  * Remove the (sub)menu with the given name from the menu hierarchy
  848.  * Called recursively.
  849.  */
  850.     static int
  851. remove_menu(menup, name, modes, silent)
  852.     vimmenu_T    **menup;
  853.     char_u    *name;
  854.     int        modes;
  855.     int        silent;        /* don't give error messages */
  856. {
  857.     vimmenu_T    *menu;
  858.     vimmenu_T    *child;
  859.     char_u    *p;
  860.  
  861.     if (*menup == NULL)
  862.     return OK;        /* Got to bottom of hierarchy */
  863.  
  864.     /* Get name of this element in the menu hierarchy */
  865.     p = menu_name_skip(name);
  866.  
  867.     /* Find the menu */
  868.     while ((menu = *menup) != NULL)
  869.     {
  870.     if (*name == NUL || menu_name_equal(name, menu))
  871.     {
  872.         if (*p != NUL && menu->children == NULL)
  873.         {
  874.         if (!silent)
  875.             EMSG(_(e_notsubmenu));
  876.         return FAIL;
  877.         }
  878.         if ((menu->modes & modes) != 0x0)
  879.         {
  880. #if defined(FEAT_GUI_W32) & defined(FEAT_TEAROFF)
  881.         /*
  882.          * If we are removing all entries for this menu,MENU_ALL_MODES,
  883.          * Then kill any tearoff before we start
  884.          */
  885.         if (*p == NUL && modes == MENU_ALL_MODES)
  886.         {
  887.             if (IsWindow(menu->tearoff_handle))
  888.             DestroyWindow(menu->tearoff_handle);
  889.         }
  890. #endif
  891.         if (remove_menu(&menu->children, p, modes, silent) == FAIL)
  892.             return FAIL;
  893.         }
  894.         else if (*name != NUL)
  895.         {
  896.         if (!silent)
  897.             EMSG(_(e_othermode));
  898.         return FAIL;
  899.         }
  900.  
  901.         /*
  902.          * When name is empty, we are removing all menu items for the given
  903.          * modes, so keep looping, otherwise we are just removing the named
  904.          * menu item (which has been found) so break here.
  905.          */
  906.         if (*name != NUL)
  907.         break;
  908.  
  909.         /* Remove the menu item for the given mode[s].  If the menu item
  910.          * is no longer valid in ANY mode, delete it */
  911.         menu->modes &= ~modes;
  912.         if (modes & MENU_TIP_MODE)
  913.         free_menu_string(menu, MENU_INDEX_TIP);
  914.         if ((menu->modes & MENU_ALL_MODES) == 0)
  915.         free_menu(menup);
  916.         else
  917.         menup = &menu->next;
  918.     }
  919.     else
  920.         menup = &menu->next;
  921.     }
  922.     if (*name != NUL)
  923.     {
  924.     if (menu == NULL)
  925.     {
  926.         if (!silent)
  927.         EMSG(_(e_nomenu));
  928.         return FAIL;
  929.     }
  930.  
  931.  
  932.     /* Recalculate modes for menu based on the new updated children */
  933.     menu->modes &= ~modes;
  934. #if defined(FEAT_GUI_W32) & defined(FEAT_TEAROFF)
  935.     if ((s_tearoffs) && (menu->children != NULL)) /* there's a tear bar.. */
  936.         child = menu->children->next; /* don't count tearoff bar */
  937.     else
  938. #endif
  939.         child = menu->children;
  940.     for ( ; child != NULL; child = child->next)
  941.         menu->modes |= child->modes;
  942.     if (modes & MENU_TIP_MODE)
  943.     {
  944.         free_menu_string(menu, MENU_INDEX_TIP);
  945. #if defined(FEAT_TOOLBAR) && (defined(FEAT_BEVAL) || defined(FEAT_GUI_GTK))
  946.         /* Need to update the menu tip. */
  947.         if (gui.in_use)
  948.         gui_mch_menu_set_tip(menu);
  949. #endif
  950.     }
  951.     if ((menu->modes & MENU_ALL_MODES) == 0)
  952.     {
  953.         /* The menu item is no longer valid in ANY mode, so delete it */
  954. #if defined(FEAT_GUI_W32) & defined(FEAT_TEAROFF)
  955.         if (s_tearoffs && menu->children != NULL) /* there's a tear bar.. */
  956.         free_menu(&menu->children);
  957. #endif
  958.         *menup = menu;
  959.         free_menu(menup);
  960.     }
  961.     }
  962.  
  963.     return OK;
  964. }
  965.  
  966. /*
  967.  * Free the given menu structure and remove it from the linked list.
  968.  */
  969.     static void
  970. free_menu(menup)
  971.     vimmenu_T    **menup;
  972. {
  973.     int        i;
  974.     vimmenu_T    *menu;
  975.  
  976.     menu = *menup;
  977.  
  978. #ifdef FEAT_GUI
  979.     /* Free machine specific menu structures (only when already created) */
  980.     /* Also may rebuild a tearoff'ed menu */
  981.     if (gui.in_use)
  982.     gui_mch_destroy_menu(menu);
  983. #endif
  984.  
  985.     /* Don't change *menup until after calling gui_mch_destroy_menu(). The
  986.      * MacOS code needs the original structure to properly delete the menu. */
  987.     *menup = menu->next;
  988.     vim_free(menu->name);
  989.     vim_free(menu->dname);
  990.     vim_free(menu->actext);
  991. #ifdef FEAT_TOOLBAR
  992.     vim_free(menu->iconfile);
  993. #endif
  994.     for (i = 0; i < MENU_MODES; i++)
  995.     free_menu_string(menu, i);
  996.     vim_free(menu);
  997.  
  998. #ifdef FEAT_GUI
  999.     /* Want to update menus now even if mode not changed */
  1000.     force_menu_update = TRUE;
  1001. #endif
  1002. }
  1003.  
  1004. /*
  1005.  * Free the menu->string with the given index.
  1006.  */
  1007.     static void
  1008. free_menu_string(menu, idx)
  1009.     vimmenu_T    *menu;
  1010.     int        idx;
  1011. {
  1012.     int        count = 0;
  1013.     int        i;
  1014.  
  1015.     for (i = 0; i < MENU_MODES; i++)
  1016.     if (menu->strings[i] == menu->strings[idx])
  1017.         count++;
  1018.     if (count == 1)
  1019.     vim_free(menu->strings[idx]);
  1020.     menu->strings[idx] = NULL;
  1021. }
  1022.  
  1023. /*
  1024.  * Show the mapping associated with a menu item or hierarchy in a sub-menu.
  1025.  */
  1026.     static int
  1027. show_menus(path_name, modes)
  1028.     char_u  *path_name;
  1029.     int        modes;
  1030. {
  1031.     char_u    *p;
  1032.     char_u    *name;
  1033.     vimmenu_T    *menu;
  1034.     vimmenu_T    *parent = NULL;
  1035.  
  1036.     menu = root_menu;
  1037.     name = path_name = vim_strsave(path_name);
  1038.     if (path_name == NULL)
  1039.     return FAIL;
  1040.  
  1041.     /* First, find the (sub)menu with the given name */
  1042.     while (*name)
  1043.     {
  1044.     p = menu_name_skip(name);
  1045.     while (menu != NULL)
  1046.     {
  1047.         if (menu_name_equal(name, menu))
  1048.         {
  1049.         /* Found menu */
  1050.         if (*p != NUL && menu->children == NULL)
  1051.         {
  1052.             EMSG(_(e_notsubmenu));
  1053.             vim_free(path_name);
  1054.             return FAIL;
  1055.         }
  1056.         else if ((menu->modes & modes) == 0x0)
  1057.         {
  1058.             EMSG(_(e_othermode));
  1059.             vim_free(path_name);
  1060.             return FAIL;
  1061.         }
  1062.         break;
  1063.         }
  1064.         menu = menu->next;
  1065.     }
  1066.     if (menu == NULL)
  1067.     {
  1068.         EMSG(_(e_nomenu));
  1069.         vim_free(path_name);
  1070.         return FAIL;
  1071.     }
  1072.     name = p;
  1073.     parent = menu;
  1074.     menu = menu->children;
  1075.     }
  1076.  
  1077.     /* Now we have found the matching menu, and we list the mappings */
  1078.                             /* Highlight title */
  1079.     MSG_PUTS_TITLE(_("\n--- Menus ---"));
  1080.  
  1081.     show_menus_recursive(parent, modes, 0);
  1082.     return OK;
  1083. }
  1084.  
  1085. /*
  1086.  * Recursively show the mappings associated with the menus under the given one
  1087.  */
  1088.     static void
  1089. show_menus_recursive(menu, modes, depth)
  1090.     vimmenu_T    *menu;
  1091.     int        modes;
  1092.     int        depth;
  1093. {
  1094.     int        i;
  1095.     int        bit;
  1096.  
  1097.     if (menu != NULL && (menu->modes & modes) == 0x0)
  1098.     return;
  1099.  
  1100.     if (menu != NULL)
  1101.     {
  1102.     msg_putchar('\n');
  1103.     if (got_int)        /* "q" hit for "--more--" */
  1104.         return;
  1105.     for (i = 0; i < depth; i++)
  1106.         MSG_PUTS("  ");
  1107.     if (menu->priority)
  1108.     {
  1109.         msg_outnum((long)menu->priority);
  1110.         MSG_PUTS(" ");
  1111.     }
  1112.                 /* Same highlighting as for directories!? */
  1113.     msg_outtrans_attr(menu->name, hl_attr(HLF_D));
  1114.     }
  1115.  
  1116.     if (menu != NULL && menu->children == NULL)
  1117.     {
  1118.     for (bit = 0; bit < MENU_MODES; bit++)
  1119.         if ((menu->modes & modes & (1 << bit)) != 0)
  1120.         {
  1121.         msg_putchar('\n');
  1122.         if (got_int)        /* "q" hit for "--more--" */
  1123.             return;
  1124.         for (i = 0; i < depth + 2; i++)
  1125.             MSG_PUTS("  ");
  1126.         msg_putchar(menu_mode_chars[bit]);
  1127.         if (menu->noremap[bit] == REMAP_NONE)
  1128.             msg_putchar('*');
  1129.         else if (menu->noremap[bit] == REMAP_SCRIPT)
  1130.             msg_putchar('&');
  1131.         else
  1132.             msg_putchar(' ');
  1133.         if (menu->silent[bit])
  1134.             msg_putchar('s');
  1135.         else
  1136.             msg_putchar(' ');
  1137.         if ((menu->modes & menu->enabled & (1 << bit)) == 0)
  1138.             msg_putchar('-');
  1139.         else
  1140.             msg_putchar(' ');
  1141.         MSG_PUTS(" ");
  1142.         if (*menu->strings[bit] == NUL)
  1143.             msg_puts_attr((char_u *)"<Nop>", hl_attr(HLF_8));
  1144.         else
  1145.             msg_outtrans_special(menu->strings[bit], FALSE);
  1146.         }
  1147.     }
  1148.     else
  1149.     {
  1150.     if (menu == NULL)
  1151.     {
  1152.         menu = root_menu;
  1153.         depth--;
  1154.     }
  1155.     else
  1156.         menu = menu->children;
  1157.  
  1158.     /* recursively show all children.  Skip PopUp[nvoci]. */
  1159.     for (; menu != NULL && !got_int; menu = menu->next)
  1160.         if (!menu_is_hidden(menu->dname))
  1161.         show_menus_recursive(menu, modes, depth + 1);
  1162.     }
  1163. }
  1164.  
  1165. #ifdef FEAT_CMDL_COMPL
  1166.  
  1167. /*
  1168.  * Used when expanding menu names.
  1169.  */
  1170. static vimmenu_T    *expand_menu = NULL;
  1171. static int        expand_modes = 0x0;
  1172. static int        expand_emenu;    /* TRUE for ":emenu" command */
  1173.  
  1174. /*
  1175.  * Work out what to complete when doing command line completion of menu names.
  1176.  */
  1177.     char_u *
  1178. set_context_in_menu_cmd(xp, cmd, arg, forceit)
  1179.     expand_T    *xp;
  1180.     char_u    *cmd;
  1181.     char_u    *arg;
  1182.     int        forceit;
  1183. {
  1184.     char_u    *after_dot;
  1185.     char_u    *p;
  1186.     char_u    *path_name = NULL;
  1187.     char_u    *name;
  1188.     int        unmenu;
  1189.     vimmenu_T    *menu;
  1190.     int        expand_menus;
  1191.  
  1192.     xp->xp_context = EXPAND_UNSUCCESSFUL;
  1193.  
  1194.  
  1195.     /* Check for priority numbers, enable and disable */
  1196.     for (p = arg; *p; ++p)
  1197.     if (!isdigit(*p) && *p != '.')
  1198.         break;
  1199.  
  1200.     if (!vim_iswhite(*p))
  1201.     {
  1202.     if (STRNCMP(arg, "enable", 6) == 0
  1203.         && (arg[6] == NUL ||  vim_iswhite(arg[6])))
  1204.         p = arg + 6;
  1205.     else if (STRNCMP(arg, "disable", 7) == 0
  1206.         && (arg[7] == NUL || vim_iswhite(arg[7])))
  1207.         p = arg + 7;
  1208.     else
  1209.         p = arg;
  1210.     }
  1211.  
  1212.     while (*p != NUL && vim_iswhite(*p))
  1213.     ++p;
  1214.  
  1215.     arg = after_dot = p;
  1216.  
  1217.     for (; *p && !vim_iswhite(*p); ++p)
  1218.     {
  1219.     if ((*p == '\\' || *p == Ctrl_V) && p[1] != NUL)
  1220.         p++;
  1221.     else if (*p == '.')
  1222.         after_dot = p + 1;
  1223.     }
  1224.  
  1225.     /* ":tearoff" and ":popup" only use menus, not entries */
  1226.     expand_menus = !((*cmd == 't' && cmd[1] == 'e') || *cmd == 'p');
  1227.     expand_emenu = (*cmd == 'e');
  1228.     if (expand_menus && vim_iswhite(*p))
  1229.     return NULL;    /* TODO: check for next command? */
  1230.     if (*p == NUL)        /* Complete the menu name */
  1231.     {
  1232.     /*
  1233.      * With :unmenu, you only want to match menus for the appropriate mode.
  1234.      * With :menu though you might want to add a menu with the same name as
  1235.      * one in another mode, so match menus from other modes too.
  1236.      */
  1237.     expand_modes = get_menu_cmd_modes(cmd, forceit, NULL, &unmenu);
  1238.     if (!unmenu)
  1239.         expand_modes = MENU_ALL_MODES;
  1240.  
  1241.     menu = root_menu;
  1242.     if (after_dot != arg)
  1243.     {
  1244.         path_name = alloc((unsigned)(after_dot - arg));
  1245.         if (path_name == NULL)
  1246.         return NULL;
  1247.         STRNCPY(path_name, arg, after_dot - arg - 1);
  1248.         path_name[after_dot - arg - 1] = NUL;
  1249.     }
  1250.     name = path_name;
  1251.     while (name != NULL && *name)
  1252.     {
  1253.         p = menu_name_skip(name);
  1254.         while (menu != NULL)
  1255.         {
  1256.         if (menu_name_equal(name, menu))
  1257.         {
  1258.             /* Found menu */
  1259.             if ((*p != NUL && menu->children == NULL)
  1260.             || ((menu->modes & expand_modes) == 0x0))
  1261.             {
  1262.             /*
  1263.              * Menu path continues, but we have reached a leaf.
  1264.              * Or menu exists only in another mode.
  1265.              */
  1266.             vim_free(path_name);
  1267.             return NULL;
  1268.             }
  1269.             break;
  1270.         }
  1271.         menu = menu->next;
  1272.         }
  1273.         if (menu == NULL)
  1274.         {
  1275.         /* No menu found with the name we were looking for */
  1276.         vim_free(path_name);
  1277.         return NULL;
  1278.         }
  1279.         name = p;
  1280.         menu = menu->children;
  1281.     }
  1282.  
  1283.     xp->xp_context = expand_menus ? EXPAND_MENUNAMES : EXPAND_MENUS;
  1284.     xp->xp_pattern = after_dot;
  1285.     expand_menu = menu;
  1286.     }
  1287.     else            /* We're in the mapping part */
  1288.     xp->xp_context = EXPAND_NOTHING;
  1289.     return NULL;
  1290. }
  1291.  
  1292. /*
  1293.  * Function given to ExpandGeneric() to obtain the list of (sub)menus (not
  1294.  * entries).
  1295.  */
  1296. /*ARGSUSED*/
  1297.     char_u *
  1298. get_menu_name(xp, idx)
  1299.     expand_T    *xp;
  1300.     int        idx;
  1301. {
  1302.     static vimmenu_T    *menu = NULL;
  1303.     char_u        *str;
  1304.  
  1305.     if (idx == 0)        /* first call: start at first item */
  1306.     menu = expand_menu;
  1307.  
  1308.     /* Skip PopUp[nvoci]. */
  1309.     while (menu != NULL && (menu_is_hidden(menu->dname)
  1310.         || menu_is_separator(menu->dname)
  1311.         || menu_is_tearoff(menu->dname)
  1312.         || menu->children == NULL))
  1313.     menu = menu->next;
  1314.  
  1315.     if (menu == NULL)        /* at end of linked list */
  1316.     return NULL;
  1317.  
  1318.     if (menu->modes & expand_modes)
  1319.     str = menu->dname;
  1320.     else
  1321.     str = (char_u *)"";
  1322.  
  1323.     /* Advance to next menu entry. */
  1324.     menu = menu->next;
  1325.  
  1326.     return str;
  1327. }
  1328.  
  1329. /*
  1330.  * Function given to ExpandGeneric() to obtain the list of menus and menu
  1331.  * entries.
  1332.  */
  1333. /*ARGSUSED*/
  1334.     char_u *
  1335. get_menu_names(xp, idx)
  1336.     expand_T    *xp;
  1337.     int        idx;
  1338. {
  1339.     static vimmenu_T    *menu = NULL;
  1340.     static char_u    tbuffer[256]; /*hack*/
  1341.     char_u        *str;
  1342.  
  1343.     if (idx == 0)        /* first call: start at first item */
  1344.     menu = expand_menu;
  1345.  
  1346.     /* Skip Browse-style entries, popup menus and separators. */
  1347.     while (menu != NULL
  1348.         && (   menu_is_hidden(menu->dname)
  1349.         || (expand_emenu && menu_is_separator(menu->dname))
  1350.         || menu_is_tearoff(menu->dname)
  1351. #ifndef FEAT_BROWSE
  1352.         || menu->dname[STRLEN(menu->dname) - 1] == '.'
  1353. #endif
  1354.            ))
  1355.     menu = menu->next;
  1356.  
  1357.     if (menu == NULL)        /* at end of linked list */
  1358.     return NULL;
  1359.  
  1360.     if (menu->modes & expand_modes)
  1361.     {
  1362.     if (menu->children != NULL)
  1363.     {
  1364.         STRCPY(tbuffer, menu->dname);
  1365.         /* hack on menu separators:  use a 'magic' char for the separator
  1366.          * so that '.' in names gets escaped properly */
  1367.         STRCAT(tbuffer, "\001");
  1368.         str = tbuffer;
  1369.     }
  1370.     else
  1371.         str = menu->dname;
  1372.     }
  1373.     else
  1374.     str = (char_u *)"";
  1375.  
  1376.     /* Advance to next menu entry. */
  1377.     menu = menu->next;
  1378.  
  1379.     return str;
  1380. }
  1381. #endif /* FEAT_CMDL_COMPL */
  1382.  
  1383. /*
  1384.  * Skip over this element of the menu path and return the start of the next
  1385.  * element.  Any \ and ^Vs are removed from the current element.
  1386.  */
  1387.     char_u *
  1388. menu_name_skip(name)
  1389.     char_u  *name;
  1390. {
  1391.     char_u  *p;
  1392.  
  1393.     for (p = name; *p && *p != '.'; p++)
  1394.     {
  1395.     if (*p == '\\' || *p == Ctrl_V)
  1396.     {
  1397.         mch_memmove(p, p + 1, STRLEN(p));
  1398.         if (*p == NUL)
  1399.         break;
  1400.     }
  1401. #ifdef FEAT_MBYTE
  1402.     if (has_mbyte)
  1403.         p += (*mb_ptr2len_check)(p) - 1;    /* skip multibyte char */
  1404. #endif
  1405.     }
  1406.     if (*p)
  1407.     *p++ = NUL;
  1408.     return p;
  1409. }
  1410.  
  1411. /*
  1412.  * Return TRUE when "name" matches with menu "menu".  The name is compared in
  1413.  * two ways: raw menu name and menu name without '&'.  ignore part after a TAB.
  1414.  */
  1415.     static int
  1416. menu_name_equal(name, menu)
  1417.     char_u    *name;
  1418.     vimmenu_T    *menu;
  1419. {
  1420.     return (menu_namecmp(name, menu->name) || menu_namecmp(name, menu->dname));
  1421. }
  1422.  
  1423.     static int
  1424. menu_namecmp(name, mname)
  1425.     char_u    *name;
  1426.     char_u    *mname;
  1427. {
  1428.     int        i;
  1429.  
  1430.     for (i = 0; name[i] != NUL && name[i] != TAB; ++i)
  1431.     if (name[i] != mname[i])
  1432.         break;
  1433.     return ((name[i] == NUL || name[i] == TAB)
  1434.         && (mname[i] == NUL || mname[i] == TAB));
  1435. }
  1436.  
  1437. /*
  1438.  * Return the modes specified by the given menu command (eg :menu! returns
  1439.  * MENU_CMDLINE_MODE | MENU_INSERT_MODE).
  1440.  * If "noremap" is not NULL, then the flag it points to is set according to
  1441.  * whether the command is a "nore" command.
  1442.  * If "unmenu" is not NULL, then the flag it points to is set according to
  1443.  * whether the command is an "unmenu" command.
  1444.  */
  1445.     static int
  1446. get_menu_cmd_modes(cmd, forceit, noremap, unmenu)
  1447.     char_u  *cmd;
  1448.     int        forceit;        /* Was there a "!" after the command? */
  1449.     int        *noremap;
  1450.     int        *unmenu;
  1451. {
  1452.     int        modes;
  1453.  
  1454.     switch (*cmd++)
  1455.     {
  1456.     case 'v':            /* vmenu, vunmenu, vnoremenu */
  1457.         modes = MENU_VISUAL_MODE;
  1458.         break;
  1459.     case 'o':            /* omenu */
  1460.         modes = MENU_OP_PENDING_MODE;
  1461.         break;
  1462.     case 'i':            /* imenu */
  1463.         modes = MENU_INSERT_MODE;
  1464.         break;
  1465.     case 't':
  1466.         modes = MENU_TIP_MODE;    /* tmenu */
  1467.         break;
  1468.     case 'c':            /* cmenu */
  1469.         modes = MENU_CMDLINE_MODE;
  1470.         break;
  1471.     case 'a':            /* amenu */
  1472.         modes = MENU_INSERT_MODE | MENU_CMDLINE_MODE | MENU_NORMAL_MODE
  1473.                     | MENU_VISUAL_MODE | MENU_OP_PENDING_MODE;
  1474.         break;
  1475.     case 'n':
  1476.         if (cmd[1] != 'o')        /* nmenu */
  1477.         {
  1478.         modes = MENU_NORMAL_MODE;
  1479.         break;
  1480.         }
  1481.         /* FALLTHROUGH */
  1482.     default:
  1483.         --cmd;
  1484.         if (forceit)        /* menu!! */
  1485.         modes = MENU_INSERT_MODE | MENU_CMDLINE_MODE;
  1486.         else            /* menu */
  1487.         modes = MENU_NORMAL_MODE | MENU_VISUAL_MODE
  1488.                                | MENU_OP_PENDING_MODE;
  1489.     }
  1490.  
  1491.     if (noremap != NULL)
  1492.     *noremap = (*cmd == 'n' ? REMAP_NONE : REMAP_YES);
  1493.     if (unmenu != NULL)
  1494.     *unmenu = (*cmd == 'u');
  1495.     return modes;
  1496. }
  1497.  
  1498. /*
  1499.  * Modify a menu name starting with "PopUp" to include the mode character.
  1500.  * Returns the name in allocated memory (NULL for failure).
  1501.  */
  1502.     static char_u *
  1503. popup_mode_name(name, idx)
  1504.     char_u    *name;
  1505.     int        idx;
  1506. {
  1507.     char_u    *p;
  1508.     int        len = (int)STRLEN(name);
  1509.  
  1510.     p = vim_strnsave(name, len + 1);
  1511.     if (p != NULL)
  1512.     {
  1513.     mch_memmove(p + 6, p + 5, (size_t)(len - 4));
  1514.     p[5] = menu_mode_chars[idx];
  1515.     }
  1516.     return p;
  1517. }
  1518.  
  1519. #if defined(FEAT_GUI) || defined(PROTO)
  1520. /*
  1521.  * Return the index into the menu->strings or menu->noremap arrays for the
  1522.  * current state.  Returns MENU_INDEX_INVALID if there is no mapping for the
  1523.  * given menu in the current mode.
  1524.  */
  1525.     int
  1526. get_menu_index(menu, state)
  1527.     vimmenu_T    *menu;
  1528.     int        state;
  1529. {
  1530.     int        idx;
  1531.  
  1532.     if ((state & INSERT))
  1533.     idx = MENU_INDEX_INSERT;
  1534.     else if (state & CMDLINE)
  1535.     idx = MENU_INDEX_CMDLINE;
  1536. #ifdef FEAT_VISUAL
  1537.     else if (VIsual_active)
  1538.     idx = MENU_INDEX_VISUAL;
  1539. #endif
  1540.     else if (state == HITRETURN || state == ASKMORE)
  1541.     idx = MENU_INDEX_CMDLINE;
  1542.     else if (finish_op)
  1543.     idx = MENU_INDEX_OP_PENDING;
  1544.     else if ((state & NORMAL))
  1545.     idx = MENU_INDEX_NORMAL;
  1546.     else
  1547.     idx = MENU_INDEX_INVALID;
  1548.  
  1549.     if (idx != MENU_INDEX_INVALID && menu->strings[idx] == NULL)
  1550.     idx = MENU_INDEX_INVALID;
  1551.     return idx;
  1552. }
  1553. #endif
  1554.  
  1555. /*
  1556.  * Duplicate the menu item text and then process to see if a mnemonic key
  1557.  * and/or accelerator text has been identified.
  1558.  * Returns a pointer to allocated memory, or NULL for failure.
  1559.  * If mnemonic != NULL, *mnemonic is set to the character after the first '&'.
  1560.  * If actext != NULL, *actext is set to the text after the first TAB.
  1561.  */
  1562.     static char_u *
  1563. menu_text(str, mnemonic, actext)
  1564.     char_u    *str;
  1565.     int        *mnemonic;
  1566.     char_u    **actext;
  1567. {
  1568.     char_u    *p;
  1569.     char_u    *text;
  1570.  
  1571.     /* Locate accelerator text, after the first TAB */
  1572.     p = vim_strchr(str, TAB);
  1573.     if (p != NULL)
  1574.     {
  1575.     if (actext != NULL)
  1576.         *actext = vim_strsave(p + 1);
  1577.     text = vim_strnsave(str, (int)(p - str));
  1578.     }
  1579.     else
  1580.     text = vim_strsave(str);
  1581.     if (text != NULL)
  1582.     {
  1583.     p = vim_strchr(text, '&');
  1584.     if (p != NULL)
  1585.     {
  1586.         if (mnemonic != NULL)
  1587. #if !defined(__MVS__) || defined(MOTIF390_MNEMONIC_FIXED)
  1588.         *mnemonic = p[1];
  1589. #else
  1590.         {
  1591.         /*
  1592.          * Well there is a bug in the Motif libraries on OS390 Unix.
  1593.          * The mnemonic keys needs to be converted to ASCII values
  1594.          * first.
  1595.          * This behavior has been seen in 2.8 and 2.9.
  1596.          */
  1597.         char c = p[1];
  1598.         __etoa_l(&c, 1);
  1599.         *mnemonic = c;
  1600.         }
  1601. #endif
  1602.         mch_memmove(p, p + 1, STRLEN(p));
  1603.     }
  1604.     }
  1605.     return text;
  1606. }
  1607.  
  1608. /*
  1609.  * Return TRUE if "name" can be a menu in the MenuBar.
  1610.  */
  1611.     int
  1612. menu_is_menubar(name)
  1613.     char_u    *name;
  1614. {
  1615.     return (!menu_is_popup(name)
  1616.         && !menu_is_toolbar(name)
  1617.         && *name != MNU_HIDDEN_CHAR);
  1618. }
  1619.  
  1620. /*
  1621.  * Return TRUE if "name" is a popup menu name.
  1622.  */
  1623.     int
  1624. menu_is_popup(name)
  1625.     char_u    *name;
  1626. {
  1627.     return (STRNCMP(name, "PopUp", 5) == 0);
  1628. }
  1629.  
  1630. #if defined(FEAT_GUI_MOTIF) || defined(PROTO)
  1631. /*
  1632.  * Return TRUE if "name" is part of a poup menu.
  1633.  */
  1634.     int
  1635. menu_is_child_of_popup(menu)
  1636.     vimmenu_T *menu;
  1637. {
  1638.     while (menu->parent != NULL)
  1639.     menu = menu->parent;
  1640.     return menu_is_popup(menu->name);
  1641. }
  1642. #endif
  1643.  
  1644. /*
  1645.  * Return TRUE if "name" is a toolbar menu name.
  1646.  */
  1647.     int
  1648. menu_is_toolbar(name)
  1649.     char_u    *name;
  1650. {
  1651.     return (STRNCMP(name, "ToolBar", 7) == 0);
  1652. }
  1653.  
  1654. /*
  1655.  * Return TRUE if the name is a menu separator identifier: Starts and ends
  1656.  * with '-'
  1657.  */
  1658.     int
  1659. menu_is_separator(name)
  1660.     char_u *name;
  1661. {
  1662.     return (name[0] == '-' && name[STRLEN(name) - 1] == '-');
  1663. }
  1664.  
  1665. /*
  1666.  * Return TRUE if the menu is hidden:  Starts with ']'
  1667.  */
  1668.     static int
  1669. menu_is_hidden(name)
  1670.     char_u *name;
  1671. {
  1672.     return (name[0] == ']') || (menu_is_popup(name) && name[5] != NUL);
  1673. }
  1674.  
  1675. #if defined(FEAT_CMDL_COMPL) \
  1676.     || (defined(FEAT_GUI_W32) && defined(FEAT_TEAROFF))
  1677. /*
  1678.  * Return TRUE if the menu is the tearoff menu.
  1679.  */
  1680. /*ARGSUSED*/
  1681.     static int
  1682. menu_is_tearoff(name)
  1683.     char_u *name;
  1684. {
  1685. #ifdef FEAT_GUI
  1686.     return (STRCMP(name, TEAR_STRING) == 0);
  1687. #else
  1688.     return FALSE;
  1689. #endif
  1690. }
  1691. #endif
  1692.  
  1693. #ifdef FEAT_GUI
  1694.  
  1695.     static int
  1696. get_menu_mode()
  1697. {
  1698. #ifdef FEAT_VISUAL
  1699.     if (VIsual_active)
  1700.     return MENU_INDEX_VISUAL;
  1701. #endif
  1702.     if (State & INSERT)
  1703.     return MENU_INDEX_INSERT;
  1704.     if ((State & CMDLINE) || State == ASKMORE || State == HITRETURN)
  1705.     return MENU_INDEX_CMDLINE;
  1706.     if (finish_op)
  1707.     return MENU_INDEX_OP_PENDING;
  1708.     if (State & NORMAL)
  1709.     return MENU_INDEX_NORMAL;
  1710.     if (State & LANGMAP)    /* must be a "r" command, like Insert mode */
  1711.     return MENU_INDEX_INSERT;
  1712.     return MENU_INDEX_INVALID;
  1713. }
  1714.  
  1715. /*
  1716.  * After we have started the GUI, then we can create any menus that have been
  1717.  * defined.  This is done once here.  add_menu_path() may have already been
  1718.  * called to define these menus, and may be called again.  This function calls
  1719.  * itself recursively.    Should be called at the top level with:
  1720.  * gui_create_initial_menus(root_menu, NULL);
  1721.  */
  1722.     void
  1723. gui_create_initial_menus(menu)
  1724.     vimmenu_T    *menu;
  1725. {
  1726.     int        idx = 0;
  1727.  
  1728.     while (menu != NULL)
  1729.     {
  1730.     /* Don't add a menu when only a tip was defined. */
  1731.     if (menu->modes & MENU_ALL_MODES)
  1732.     {
  1733.         if (menu->children != NULL)
  1734.         {
  1735.         gui_mch_add_menu(menu, idx);
  1736.         gui_create_initial_menus(menu->children);
  1737.         }
  1738.         else
  1739.         gui_mch_add_menu_item(menu, idx);
  1740.     }
  1741.     menu = menu->next;
  1742.     ++idx;
  1743.     }
  1744. }
  1745.  
  1746. /*
  1747.  * Used recursively by gui_update_menus (see below)
  1748.  */
  1749.     static void
  1750. gui_update_menus_recurse(menu, mode)
  1751.     vimmenu_T    *menu;
  1752.     int        mode;
  1753. {
  1754.     int        grey;
  1755.  
  1756.     while (menu)
  1757.     {
  1758.     if ((menu->modes & menu->enabled & mode)
  1759. #if defined(FEAT_GUI_W32) && defined(FEAT_TEAROFF)
  1760.         || menu_is_tearoff(menu->dname)
  1761. #endif
  1762.        )
  1763.         grey = FALSE;
  1764.     else
  1765.         grey = TRUE;
  1766. #ifdef FEAT_GUI_ATHENA
  1767.     /* Hiding menus doesn't work for Athena, it can cause a crash. */
  1768.     gui_mch_menu_grey(menu, grey);
  1769. #else
  1770.     /* Never hide a toplevel menu, it may make the menubar resize or
  1771.      * disappear. Same problem for ToolBar items. */
  1772.     if (vim_strchr(p_go, GO_GREY) != NULL || menu->parent == NULL
  1773. # ifdef FEAT_TOOLBAR
  1774.         || menu_is_toolbar(menu->parent->name)
  1775. # endif
  1776.            )
  1777.         gui_mch_menu_grey(menu, grey);
  1778.     else
  1779.         gui_mch_menu_hidden(menu, grey);
  1780. #endif
  1781.     gui_update_menus_recurse(menu->children, mode);
  1782.     menu = menu->next;
  1783.     }
  1784. }
  1785.  
  1786. /*
  1787.  * Make sure only the valid menu items appear for this mode.  If
  1788.  * force_menu_update is not TRUE, then we only do this if the mode has changed
  1789.  * since last time.  If "modes" is not 0, then we use these modes instead.
  1790.  */
  1791.     void
  1792. gui_update_menus(modes)
  1793.     int        modes;
  1794. {
  1795.     static int        prev_mode = -1;
  1796.     int            mode = 0;
  1797.  
  1798.     if (modes != 0x0)
  1799.     mode = modes;
  1800.     else
  1801.     {
  1802.     mode = get_menu_mode();
  1803.     if (mode == MENU_INDEX_INVALID)
  1804.         mode = 0;
  1805.     else
  1806.         mode = (1 << mode);
  1807.     }
  1808.  
  1809.     if (force_menu_update || mode != prev_mode)
  1810.     {
  1811.     gui_update_menus_recurse(root_menu, mode);
  1812.     gui_mch_draw_menubar();
  1813.     prev_mode = mode;
  1814.     force_menu_update = FALSE;
  1815. #ifdef FEAT_GUI_W32
  1816.     /* This can leave a tearoff as active window - make sure we
  1817.      * have the focus <negri>*/
  1818.     gui_mch_activate_window();
  1819. #endif
  1820.     }
  1821. }
  1822.  
  1823. #if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_GTK) \
  1824.     || defined(FEAT_GUI_PHOTON) || defined(PROTO)
  1825. /*
  1826.  * Check if a key is used as a mnemonic for a toplevel menu.
  1827.  * Case of the key is ignored.
  1828.  */
  1829.     int
  1830. gui_is_menu_shortcut(key)
  1831.     int        key;
  1832. {
  1833.     vimmenu_T    *menu;
  1834.  
  1835.     if (key < 256)
  1836.     key = TOLOWER_LOC(key);
  1837.     for (menu = root_menu; menu != NULL; menu = menu->next)
  1838.     if (menu->mnemonic == key
  1839.         || (menu->mnemonic < 256 && TOLOWER_LOC(menu->mnemonic) == key))
  1840.         return TRUE;
  1841.     return FALSE;
  1842. }
  1843. #endif
  1844.  
  1845. /*
  1846.  * Display the Special "PopUp" menu as a pop-up at the current mouse
  1847.  * position.  The "PopUpn" menu is for Normal mode, "PopUpi" for Insert mode,
  1848.  * etc.
  1849.  */
  1850.     void
  1851. gui_show_popupmenu()
  1852. {
  1853.     vimmenu_T    *menu;
  1854.     int        mode;
  1855.  
  1856.     mode = get_menu_mode();
  1857.     if (mode == MENU_INDEX_INVALID)
  1858.     return;
  1859.     mode = menu_mode_chars[mode];
  1860.  
  1861.     for (menu = root_menu; menu != NULL; menu = menu->next)
  1862.     if (STRNCMP("PopUp", menu->name, 5) == 0 && menu->name[5] == mode)
  1863.         break;
  1864.  
  1865.     /* Only show a popup when it is defined and has entries */
  1866.     if (menu != NULL && menu->children != NULL)
  1867.     gui_mch_show_popupmenu(menu);
  1868. }
  1869. #endif /* FEAT_GUI */
  1870.  
  1871. #if (defined(FEAT_GUI_W32) && defined(FEAT_TEAROFF)) || defined(PROTO)
  1872.  
  1873. /*
  1874.  * Deal with tearoff items that are added like a menu item.
  1875.  * Currently only for Win32 GUI.  Others may follow later.
  1876.  */
  1877.  
  1878.     void
  1879. gui_mch_toggle_tearoffs(int enable)
  1880. {
  1881.     int        pri_tab[MENUDEPTH + 1];
  1882.     int        i;
  1883.  
  1884.     if (enable)
  1885.     {
  1886.     for (i = 0; i < MENUDEPTH; ++i)
  1887.         pri_tab[i] = 500;
  1888.     pri_tab[MENUDEPTH] = -1;
  1889.     gui_create_tearoffs_recurse(root_menu, (char_u *)"", pri_tab, 0);
  1890.     }
  1891.     else
  1892.     gui_destroy_tearoffs_recurse(root_menu);
  1893.     s_tearoffs = enable;
  1894. }
  1895.  
  1896. /*
  1897.  * Recursively add tearoff items
  1898.  */
  1899.     static void
  1900. gui_create_tearoffs_recurse(menu, pname, pri_tab, pri_idx)
  1901.     vimmenu_T        *menu;
  1902.     const char_u    *pname;
  1903.     int            *pri_tab;
  1904.     int            pri_idx;
  1905. {
  1906.     char_u    *newpname = NULL;
  1907.     int        len;
  1908.     char_u    *s;
  1909.     char_u    *d;
  1910.  
  1911.     if (pri_tab[pri_idx + 1] != -1)
  1912.     ++pri_idx;
  1913.     while (menu != NULL)
  1914.     {
  1915.     if (menu->children != NULL && menu_is_menubar(menu->name))
  1916.     {
  1917.         /* Add the menu name to the menu path.  Insert a backslash before
  1918.          * dots (it's used to separate menu names). */
  1919.         len = (int)STRLEN(pname) + (int)STRLEN(menu->name);
  1920.         for (s = menu->name; *s; ++s)
  1921.         if (*s == '.' || *s == '\\')
  1922.             ++len;
  1923.         newpname = alloc(len + TEAR_LEN + 2);
  1924.         if (newpname != NULL)
  1925.         {
  1926.         STRCPY(newpname, pname);
  1927.         d = newpname + STRLEN(newpname);
  1928.         for (s = menu->name; *s; ++s)
  1929.         {
  1930.             if (*s == '.' || *s == '\\')
  1931.             *d++ = '\\';
  1932.             *d++ = *s;
  1933.         }
  1934.         *d = NUL;
  1935.  
  1936.         /* check if tearoff already exists */
  1937.         if (STRCMP(menu->children->name, TEAR_STRING) != 0)
  1938.         {
  1939.             gui_add_tearoff(newpname, pri_tab, pri_idx - 1);
  1940.             *d = NUL;            /* remove TEAR_STRING */
  1941.         }
  1942.  
  1943.         STRCAT(newpname, ".");
  1944.         gui_create_tearoffs_recurse(menu->children, newpname,
  1945.                                 pri_tab, pri_idx);
  1946.         vim_free(newpname);
  1947.         }
  1948.     }
  1949.     menu = menu->next;
  1950.     }
  1951. }
  1952.  
  1953. /*
  1954.  * Add tear-off menu item for a submenu.
  1955.  * "tearpath" is the menu path, and must have room to add TEAR_STRING.
  1956.  */
  1957.     static void
  1958. gui_add_tearoff(tearpath, pri_tab, pri_idx)
  1959.     char_u    *tearpath;
  1960.     int        *pri_tab;
  1961.     int        pri_idx;
  1962. {
  1963.     char_u    *tbuf;
  1964.     int        t;
  1965.     vimmenu_T    menuarg;
  1966.  
  1967.     tbuf = alloc(5 + (unsigned int)STRLEN(tearpath));
  1968.     if (tbuf != NULL)
  1969.     {
  1970.     tbuf[0] = K_SPECIAL;
  1971.     tbuf[1] = K_SECOND(K_TEAROFF);
  1972.     tbuf[2] = K_THIRD(K_TEAROFF);
  1973.     STRCPY(tbuf + 3, tearpath);
  1974.     STRCAT(tbuf + 3, "\r");
  1975.  
  1976.     STRCAT(tearpath, ".");
  1977.     STRCAT(tearpath, TEAR_STRING);
  1978.  
  1979.     /* Priority of tear-off is always 1 */
  1980.     t = pri_tab[pri_idx + 1];
  1981.     pri_tab[pri_idx + 1] = 1;
  1982.  
  1983. #ifdef FEAT_TOOLBAR
  1984.     menuarg.iconfile = NULL;
  1985.     menuarg.iconidx = -1;
  1986.     menuarg.icon_builtin = FALSE;
  1987. #endif
  1988.     menuarg.noremap[0] = REMAP_NONE;
  1989.     menuarg.silent[0] = TRUE;
  1990.  
  1991.     menuarg.modes = MENU_ALL_MODES;
  1992.     add_menu_path(tearpath, &menuarg, pri_tab, tbuf, FALSE);
  1993.  
  1994.     menuarg.modes = MENU_TIP_MODE;
  1995.     add_menu_path(tearpath, &menuarg, pri_tab,
  1996.         (char_u *)_("Tear off this menu"), FALSE);
  1997.  
  1998.     pri_tab[pri_idx + 1] = t;
  1999.     vim_free(tbuf);
  2000.     }
  2001. }
  2002.  
  2003. /*
  2004.  * Recursively destroy tearoff items
  2005.  */
  2006.     static void
  2007. gui_destroy_tearoffs_recurse(menu)
  2008.     vimmenu_T    *menu;
  2009. {
  2010.     while (menu)
  2011.     {
  2012.     if (menu->children)
  2013.     {
  2014.         /* check if tearoff exists */
  2015.         if (STRCMP(menu->children->name, TEAR_STRING) == 0)
  2016.         {
  2017.         /* Disconnect the item and free the memory */
  2018.         free_menu(&menu->children);
  2019.         }
  2020.         if (menu->children != NULL) /* if not the last one */
  2021.         gui_destroy_tearoffs_recurse(menu->children);
  2022.     }
  2023.     menu = menu->next;
  2024.     }
  2025. }
  2026.  
  2027. #endif /* FEAT_GUI_W32 && FEAT_TEAROFF */
  2028.  
  2029. /*
  2030.  * Given a menu descriptor, e.g. "File.New", find it in the menu hierarchy and
  2031.  * execute it.
  2032.  */
  2033.     void
  2034. ex_emenu(eap)
  2035.     exarg_T    *eap;
  2036. {
  2037.     vimmenu_T    *menu;
  2038.     char_u    *name;
  2039.     char_u    *saved_name;
  2040.     char_u    *p;
  2041.     int        idx;
  2042.     char_u    *mode;
  2043.  
  2044.     saved_name = vim_strsave(eap->arg);
  2045.     if (saved_name == NULL)
  2046.     return;
  2047.  
  2048.     menu = root_menu;
  2049.     name = saved_name;
  2050.     while (*name)
  2051.     {
  2052.     /* Find in the menu hierarchy */
  2053.     p = menu_name_skip(name);
  2054.  
  2055.     while (menu != NULL)
  2056.     {
  2057.         if (menu_name_equal(name, menu))
  2058.         {
  2059.         if (*p == NUL && menu->children != NULL)
  2060.         {
  2061.             EMSG(_("E333: Menu path must lead to a menu item"));
  2062.             menu = NULL;
  2063.         }
  2064.         else if (*p != NUL && menu->children == NULL)
  2065.         {
  2066.             EMSG(_(e_notsubmenu));
  2067.             menu = NULL;
  2068.         }
  2069.         break;
  2070.         }
  2071.         menu = menu->next;
  2072.     }
  2073.     if (menu == NULL || *p == NUL)
  2074.         break;
  2075.     menu = menu->children;
  2076.     name = p;
  2077.     }
  2078.     vim_free(saved_name);
  2079.     if (menu == NULL)
  2080.     {
  2081.     EMSG2(_("E334: Menu not found: %s"), eap->arg);
  2082.     return;
  2083.     }
  2084.  
  2085.     /* Found the menu, so execute. */
  2086.     if (restart_edit)
  2087.     {
  2088.     mode = (char_u *)"Insert";
  2089.     idx = MENU_INDEX_INSERT;
  2090.     }
  2091.     else if (eap->addr_count)
  2092.     {
  2093.     pos_T    tpos;
  2094.  
  2095.     mode = (char_u *)"Visual";
  2096.     idx = MENU_INDEX_VISUAL;
  2097.  
  2098.     /* GEDDES: This is not perfect - but it is a
  2099.      * quick way of detecting whether we are doing this from a
  2100.      * selection - see if the range matches up with the visual
  2101.      * select start and end.
  2102.      */
  2103.     if ((curbuf->b_visual_start.lnum == eap->line1)
  2104.         && (curbuf->b_visual_end.lnum) == eap->line2)
  2105.     {
  2106.         /* Set it up for visual mode - equivalent to gv.  */
  2107.         VIsual_mode = curbuf->b_visual_mode;
  2108.         tpos = curbuf->b_visual_end;
  2109.         curwin->w_cursor = curbuf->b_visual_start;
  2110.         curwin->w_curswant = curbuf->b_visual_curswant;
  2111.     }
  2112.     else
  2113.     {
  2114.         /* Set it up for line-wise visual mode */
  2115.         VIsual_mode = 'V';
  2116.         curwin->w_cursor.lnum = eap->line1;
  2117.         curwin->w_cursor.col = 1;
  2118.         tpos.lnum = eap->line2;
  2119.         tpos.col = MAXCOL;
  2120.     }
  2121.  
  2122.     /* Activate visual mode
  2123.      */
  2124.     VIsual_active = TRUE;
  2125.     VIsual_reselect = TRUE;
  2126.     check_cursor();
  2127.     VIsual = curwin->w_cursor;
  2128.     curwin->w_cursor = tpos;
  2129.  
  2130.     check_cursor();
  2131.  
  2132.     /* Adjust the cursor to make sure it is in the correct pos
  2133.      * for exclusive mode
  2134.      */
  2135.     if (*p_sel == 'e' && gchar_cursor() != NUL)
  2136.         ++curwin->w_cursor.col;
  2137.     }
  2138.     else
  2139.     {
  2140.     mode = (char_u *)"Normal";
  2141.     idx = MENU_INDEX_NORMAL;
  2142.     }
  2143.  
  2144.     if (idx != MENU_INDEX_INVALID && menu->strings[idx] != NULL)
  2145.     {
  2146.     ins_typebuf(menu->strings[idx], menu->noremap[idx], 0,
  2147.                              TRUE, menu->silent[idx]);
  2148.     }
  2149.     else
  2150.     EMSG2(_("E335: Menu not defined for %s mode"), mode);
  2151. }
  2152.  
  2153. #if defined(FEAT_GUI_MSWIN) \
  2154.     || (defined(FEAT_GUI_GTK) && defined(FEAT_MENU)) \
  2155.     || defined(FEAT_BEVAL_TIP) || defined(PROTO)
  2156. /*
  2157.  * Given a menu descriptor, e.g. "File.New", find it in the menu hierarchy.
  2158.  */
  2159.     vimmenu_T *
  2160. gui_find_menu(path_name)
  2161.     char_u *path_name;
  2162. {
  2163.     vimmenu_T    *menu = NULL;
  2164.     char_u    *name;
  2165.     char_u    *saved_name;
  2166.     char_u    *p;
  2167.  
  2168.     menu = root_menu;
  2169.  
  2170.     saved_name = vim_strsave(path_name);
  2171.     if (saved_name == NULL)
  2172.     return NULL;
  2173.  
  2174.     name = saved_name;
  2175.     while (*name)
  2176.     {
  2177.     /* find the end of one dot-separated name and put a NUL at the dot */
  2178.     p = menu_name_skip(name);
  2179.  
  2180.     while (menu != NULL)
  2181.     {
  2182.         if (STRCMP(name, menu->name) == 0 || STRCMP(name, menu->dname) == 0)
  2183.         {
  2184.         if (menu->children == NULL)
  2185.         {
  2186.             /* found a menu item instead of a sub-menu */
  2187.             if (*p == NUL)
  2188.             EMSG(_("E336: Menu path must lead to a sub-menu"));
  2189.             else
  2190.             EMSG(_(e_notsubmenu));
  2191.             menu = NULL;
  2192.             goto theend;
  2193.         }
  2194.         if (*p == NUL)        /* found a full match */
  2195.             goto theend;
  2196.         break;
  2197.         }
  2198.         menu = menu->next;
  2199.     }
  2200.     if (menu == NULL)    /* didn't find it */
  2201.         break;
  2202.  
  2203.     /* Found a match, search the sub-menu. */
  2204.     menu = menu->children;
  2205.     name = p;
  2206.     }
  2207.  
  2208.     if (menu == NULL)
  2209.     EMSG(_("E337: Menu not found - check menu names"));
  2210. theend:
  2211.     vim_free(saved_name);
  2212.     return menu;
  2213. }
  2214. #endif
  2215.  
  2216. #ifdef FEAT_MULTI_LANG
  2217. /*
  2218.  * Translation of menu names.  Just a simple lookup table.
  2219.  */
  2220.  
  2221. typedef struct
  2222. {
  2223.     char_u    *from;        /* English name */
  2224.     char_u    *to;        /* translated name */
  2225. } menutrans_T;
  2226.  
  2227. static garray_T menutrans_ga = {0, 0, 0, 0, NULL};
  2228. #endif
  2229.  
  2230. /*
  2231.  * ":menutrans".
  2232.  * This function is also defined without the +multi_lang feature, in which
  2233.  * case the commands are ignored.
  2234.  */
  2235. /*ARGSUSED*/
  2236.     void
  2237. ex_menutranslate(eap)
  2238.     exarg_T    *eap;
  2239. {
  2240. #ifdef FEAT_MULTI_LANG
  2241.     char_u        *arg = eap->arg;
  2242.     menutrans_T        *tp;
  2243.     int            i;
  2244.     char_u        *from, *to;
  2245.  
  2246.     if (menutrans_ga.ga_itemsize == 0)
  2247.     ga_init2(&menutrans_ga, (int)sizeof(menutrans_T), 5);
  2248.  
  2249.     /*
  2250.      * ":menutrans clear": clear all translations.
  2251.      */
  2252.     if (STRNCMP(arg, "clear", 5) == 0 && ends_excmd(*skipwhite(arg + 5)))
  2253.     {
  2254.     tp = (menutrans_T *)menutrans_ga.ga_data;
  2255.     for (i = 0; i < menutrans_ga.ga_len; ++i)
  2256.     {
  2257.         vim_free(tp[i].from);
  2258.         vim_free(tp[i].to);
  2259.     }
  2260.     ga_clear(&menutrans_ga);
  2261. # ifdef FEAT_EVAL
  2262.     /* Delete all "menutrans_" global variables. */
  2263.     del_menutrans_vars();
  2264. # endif
  2265.     }
  2266.     else
  2267.     {
  2268.     /* ":menutrans from to": add translation */
  2269.     from = arg;
  2270.     arg = menu_skip_part(arg);
  2271.     to = skipwhite(arg);
  2272.     *arg = NUL;
  2273.     arg = menu_skip_part(to);
  2274.     if (arg == to)
  2275.         EMSG(_(e_invarg));
  2276.     else
  2277.     {
  2278.         if (ga_grow(&menutrans_ga, 1) == OK)
  2279.         {
  2280.         tp = (menutrans_T *)menutrans_ga.ga_data;
  2281.         from = vim_strsave(from);
  2282.         to = vim_strnsave(to, (int)(arg - to));
  2283.         if (from != NULL && to != NULL)
  2284.         {
  2285.             tp[menutrans_ga.ga_len].from = from;
  2286.             tp[menutrans_ga.ga_len].to = to;
  2287.             ++menutrans_ga.ga_len;
  2288.             --menutrans_ga.ga_room;
  2289.         }
  2290.         }
  2291.     }
  2292.     }
  2293. #endif
  2294. }
  2295.  
  2296. #if defined(FEAT_MULTI_LANG) || defined(FEAT_TOOLBAR)
  2297. /*
  2298.  * Find the character just after one part of a menu name.
  2299.  */
  2300.     static char_u *
  2301. menu_skip_part(p)
  2302.     char_u    *p;
  2303. {
  2304.     while (*p != NUL && *p != '.' && !vim_iswhite(*p))
  2305.     {
  2306.     if ((*p == '\\' || *p == Ctrl_V) && p[1] != NUL)
  2307.         ++p;
  2308.     ++p;
  2309.     }
  2310.     return p;
  2311. }
  2312. #endif
  2313.  
  2314. #ifdef FEAT_MULTI_LANG
  2315. /*
  2316.  * Lookup part of a menu name in the translations.
  2317.  * Return a pointer to the translation or NULL if not found.
  2318.  */
  2319.     static char_u *
  2320. menutrans_lookup(name, len)
  2321.     char_u    *name;
  2322.     int        len;
  2323. {
  2324.     menutrans_T        *tp = (menutrans_T *)menutrans_ga.ga_data;
  2325.     int            i;
  2326.  
  2327.     for (i = 0; i < menutrans_ga.ga_len; ++i)
  2328.     if (STRNCMP(name, tp[i].from, len) == 0 && tp[i].from[len] == NUL)
  2329.         return tp[i].to;
  2330.     return NULL;
  2331. }
  2332. #endif /* FEAT_MULTI_LANG */
  2333.  
  2334. #endif /* FEAT_MENU */
  2335.