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