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