1 /* vi:set ts=8 sts=4 sw=4 noet: 2 * 3 * VIM - Vi IMproved by Bram Moolenaar 4 * GUI/Motif support by Robert Webb 5 * 6 * Do ":help uganda" in Vim to read copying and usage conditions. 7 * Do ":help credits" in Vim to see a list of people who contributed. 8 * See README.txt for an overview of the Vim source code. 9 */ 10 11 /* 12 * Code for menus. Used for the GUI and 'wildmenu'. 13 */ 14 15 #include "vim.h" 16 17 #if defined(FEAT_MENU) || defined(PROTO) 18 19 #define MENUDEPTH 10 // maximum depth of menus 20 21 #ifdef FEAT_GUI_MSWIN 22 static int add_menu_path(char_u *, vimmenu_T *, int *, char_u *, int); 23 #else 24 static int add_menu_path(char_u *, vimmenu_T *, int *, char_u *); 25 #endif 26 static int menu_nable_recurse(vimmenu_T *menu, char_u *name, int modes, int enable); 27 static int remove_menu(vimmenu_T **, char_u *, int, int silent); 28 static void free_menu(vimmenu_T **menup); 29 static void free_menu_string(vimmenu_T *, int); 30 static int show_menus(char_u *, int); 31 static void show_menus_recursive(vimmenu_T *, int, int); 32 static char_u *menu_name_skip(char_u *name); 33 static int menu_name_equal(char_u *name, vimmenu_T *menu); 34 static int menu_namecmp(char_u *name, char_u *mname); 35 static int get_menu_cmd_modes(char_u *, int, int *, int *); 36 static char_u *popup_mode_name(char_u *name, int idx); 37 static char_u *menu_text(char_u *text, int *mnemonic, char_u **actext); 38 39 #if defined(FEAT_GUI_MSWIN) && defined(FEAT_TEAROFF) 40 static void gui_create_tearoffs_recurse(vimmenu_T *menu, const char_u *pname, int *pri_tab, int pri_idx); 41 static void gui_add_tearoff(char_u *tearpath, int *pri_tab, int pri_idx); 42 static void gui_destroy_tearoffs_recurse(vimmenu_T *menu); 43 static int s_tearoffs = FALSE; 44 #endif 45 46 static int menu_is_hidden(char_u *name); 47 static int menu_is_tearoff(char_u *name); 48 49 #if defined(FEAT_MULTI_LANG) || defined(FEAT_TOOLBAR) 50 static char_u *menu_skip_part(char_u *p); 51 #endif 52 #ifdef FEAT_MULTI_LANG 53 static char_u *menutrans_lookup(char_u *name, int len); 54 static void menu_unescape_name(char_u *p); 55 #endif 56 57 static char_u *menu_translate_tab_and_shift(char_u *arg_start); 58 59 // The character for each menu mode 60 static char *menu_mode_chars[] = {"n", "v", "s", "o", "i", "c", "tl", "t"}; 61 62 static char_u e_notsubmenu[] = N_("E327: Part of menu-item path is not sub-menu"); 63 static char_u e_nomenu[] = N_("E329: No menu \"%s\""); 64 65 #ifdef FEAT_TOOLBAR 66 static const char *toolbar_names[] = 67 { 68 /* 0 */ "New", "Open", "Save", "Undo", "Redo", 69 /* 5 */ "Cut", "Copy", "Paste", "Print", "Help", 70 /* 10 */ "Find", "SaveAll", "SaveSesn", "NewSesn", "LoadSesn", 71 /* 15 */ "RunScript", "Replace", "WinClose", "WinMax", "WinMin", 72 /* 20 */ "WinSplit", "Shell", "FindPrev", "FindNext", "FindHelp", 73 /* 25 */ "Make", "TagJump", "RunCtags", "WinVSplit", "WinMaxWidth", 74 /* 30 */ "WinMinWidth", "Exit" 75 }; 76 # define TOOLBAR_NAME_COUNT (sizeof(toolbar_names) / sizeof(char *)) 77 #endif 78 79 /* 80 * Return TRUE if "name" is a window toolbar menu name. 81 */ 82 static int 83 menu_is_winbar(char_u *name) 84 { 85 return (STRNCMP(name, "WinBar", 6) == 0); 86 } 87 88 int 89 winbar_height(win_T *wp) 90 { 91 if (wp->w_winbar != NULL && wp->w_winbar->children != NULL) 92 return 1; 93 return 0; 94 } 95 96 static vimmenu_T ** 97 get_root_menu(char_u *name) 98 { 99 if (menu_is_winbar(name)) 100 return &curwin->w_winbar; 101 return &root_menu; 102 } 103 104 /* 105 * Do the :menu command and relatives. 106 */ 107 void 108 ex_menu( 109 exarg_T *eap) // Ex command arguments 110 { 111 char_u *menu_path; 112 int modes; 113 char_u *map_to; 114 int noremap; 115 int silent = FALSE; 116 int special = FALSE; 117 int unmenu; 118 char_u *map_buf; 119 char_u *arg; 120 char_u *p; 121 int i; 122 #if defined(FEAT_GUI) && !defined(FEAT_GUI_GTK) 123 int old_menu_height; 124 # if defined(FEAT_TOOLBAR) && !defined(FEAT_GUI_MSWIN) 125 int old_toolbar_height; 126 # endif 127 #endif 128 int pri_tab[MENUDEPTH + 1]; 129 int enable = MAYBE; // TRUE for "menu enable", FALSE for "menu 130 // disable 131 #ifdef FEAT_TOOLBAR 132 char_u *icon = NULL; 133 #endif 134 vimmenu_T menuarg; 135 vimmenu_T **root_menu_ptr; 136 137 modes = get_menu_cmd_modes(eap->cmd, eap->forceit, &noremap, &unmenu); 138 arg = eap->arg; 139 140 for (;;) 141 { 142 if (STRNCMP(arg, "<script>", 8) == 0) 143 { 144 noremap = REMAP_SCRIPT; 145 arg = skipwhite(arg + 8); 146 continue; 147 } 148 if (STRNCMP(arg, "<silent>", 8) == 0) 149 { 150 silent = TRUE; 151 arg = skipwhite(arg + 8); 152 continue; 153 } 154 if (STRNCMP(arg, "<special>", 9) == 0) 155 { 156 special = TRUE; 157 arg = skipwhite(arg + 9); 158 continue; 159 } 160 break; 161 } 162 163 164 // Locate an optional "icon=filename" argument. 165 if (STRNCMP(arg, "icon=", 5) == 0) 166 { 167 arg += 5; 168 #ifdef FEAT_TOOLBAR 169 icon = arg; 170 #endif 171 while (*arg != NUL && *arg != ' ') 172 { 173 if (*arg == '\\') 174 STRMOVE(arg, arg + 1); 175 MB_PTR_ADV(arg); 176 } 177 if (*arg != NUL) 178 { 179 *arg++ = NUL; 180 arg = skipwhite(arg); 181 } 182 } 183 184 /* 185 * Fill in the priority table. 186 */ 187 for (p = arg; *p; ++p) 188 if (!VIM_ISDIGIT(*p) && *p != '.') 189 break; 190 if (VIM_ISWHITE(*p)) 191 { 192 for (i = 0; i < MENUDEPTH && !VIM_ISWHITE(*arg); ++i) 193 { 194 pri_tab[i] = getdigits(&arg); 195 if (pri_tab[i] == 0) 196 pri_tab[i] = 500; 197 if (*arg == '.') 198 ++arg; 199 } 200 arg = skipwhite(arg); 201 } 202 else if (eap->addr_count && eap->line2 != 0) 203 { 204 pri_tab[0] = eap->line2; 205 i = 1; 206 } 207 else 208 i = 0; 209 while (i < MENUDEPTH) 210 pri_tab[i++] = 500; 211 pri_tab[MENUDEPTH] = -1; // mark end of the table 212 213 /* 214 * Check for "disable" or "enable" argument. 215 */ 216 if (STRNCMP(arg, "enable", 6) == 0 && VIM_ISWHITE(arg[6])) 217 { 218 enable = TRUE; 219 arg = skipwhite(arg + 6); 220 } 221 else if (STRNCMP(arg, "disable", 7) == 0 && VIM_ISWHITE(arg[7])) 222 { 223 enable = FALSE; 224 arg = skipwhite(arg + 7); 225 } 226 227 /* 228 * If there is no argument, display all menus. 229 */ 230 if (*arg == NUL) 231 { 232 show_menus(arg, modes); 233 return; 234 } 235 236 #ifdef FEAT_TOOLBAR 237 /* 238 * Need to get the toolbar icon index before doing the translation. 239 */ 240 menuarg.iconidx = -1; 241 menuarg.icon_builtin = FALSE; 242 if (menu_is_toolbar(arg)) 243 { 244 menu_path = menu_skip_part(arg); 245 if (*menu_path == '.') 246 { 247 p = menu_skip_part(++menu_path); 248 if (STRNCMP(menu_path, "BuiltIn", 7) == 0) 249 { 250 if (skipdigits(menu_path + 7) == p) 251 { 252 menuarg.iconidx = atoi((char *)menu_path + 7); 253 if (menuarg.iconidx >= (int)TOOLBAR_NAME_COUNT) 254 menuarg.iconidx = -1; 255 else 256 menuarg.icon_builtin = TRUE; 257 } 258 } 259 else 260 { 261 for (i = 0; i < (int)TOOLBAR_NAME_COUNT; ++i) 262 if (STRNCMP(toolbar_names[i], menu_path, p - menu_path) 263 == 0) 264 { 265 menuarg.iconidx = i; 266 break; 267 } 268 } 269 } 270 } 271 #endif 272 273 menu_path = arg; 274 if (*menu_path == '.') 275 { 276 semsg(_(e_invarg2), menu_path); 277 goto theend; 278 } 279 280 map_to = menu_translate_tab_and_shift(arg); 281 282 /* 283 * If there is only a menu name, display menus with that name. 284 */ 285 if (*map_to == NUL && !unmenu && enable == MAYBE) 286 { 287 show_menus(menu_path, modes); 288 goto theend; 289 } 290 else if (*map_to != NUL && (unmenu || enable != MAYBE)) 291 { 292 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 2314 #ifdef FEAT_EVAL 2315 && !current_sctx.sc_sid 2316 #endif 2317 ) 2318 { 2319 idx = MENU_INDEX_INSERT; 2320 } 2321 #ifdef FEAT_TERMINAL 2322 else if (term_use_loop()) 2323 { 2324 idx = MENU_INDEX_TERMINAL; 2325 } 2326 #endif 2327 else if (VIsual_active) 2328 { 2329 idx = MENU_INDEX_VISUAL; 2330 } 2331 else if (eap != NULL && eap->addr_count) 2332 { 2333 pos_T tpos; 2334 2335 idx = MENU_INDEX_VISUAL; 2336 2337 // GEDDES: This is not perfect - but it is a 2338 // quick way of detecting whether we are doing this from a 2339 // selection - see if the range matches up with the visual 2340 // select start and end. 2341 if ((curbuf->b_visual.vi_start.lnum == eap->line1) 2342 && (curbuf->b_visual.vi_end.lnum) == eap->line2) 2343 { 2344 // Set it up for visual mode - equivalent to gv. 2345 VIsual_mode = curbuf->b_visual.vi_mode; 2346 tpos = curbuf->b_visual.vi_end; 2347 curwin->w_cursor = curbuf->b_visual.vi_start; 2348 curwin->w_curswant = curbuf->b_visual.vi_curswant; 2349 } 2350 else 2351 { 2352 // Set it up for line-wise visual mode 2353 VIsual_mode = 'V'; 2354 curwin->w_cursor.lnum = eap->line1; 2355 curwin->w_cursor.col = 1; 2356 tpos.lnum = eap->line2; 2357 tpos.col = MAXCOL; 2358 tpos.coladd = 0; 2359 } 2360 2361 // Activate visual mode 2362 VIsual_active = TRUE; 2363 VIsual_reselect = TRUE; 2364 check_cursor(); 2365 VIsual = curwin->w_cursor; 2366 curwin->w_cursor = tpos; 2367 2368 check_cursor(); 2369 2370 // Adjust the cursor to make sure it is in the correct pos 2371 // for exclusive mode 2372 if (*p_sel == 'e' && gchar_cursor() != NUL) 2373 ++curwin->w_cursor.col; 2374 } 2375 } 2376 2377 // For the WinBar menu always use the Normal mode menu. 2378 if (idx == -1 || eap == NULL) 2379 idx = MENU_INDEX_NORMAL; 2380 2381 if (idx != MENU_INDEX_INVALID && menu->strings[idx] != NULL 2382 && (menu->modes & (1 << idx))) 2383 { 2384 // When executing a script or function execute the commands right now. 2385 // Also for the window toolbar. 2386 // Otherwise put them in the typeahead buffer. 2387 if (eap == NULL 2388 #ifdef FEAT_EVAL 2389 || current_sctx.sc_sid != 0 2390 #endif 2391 ) 2392 { 2393 save_state_T save_state; 2394 2395 ++ex_normal_busy; 2396 if (save_current_state(&save_state)) 2397 exec_normal_cmd(menu->strings[idx], menu->noremap[idx], 2398 menu->silent[idx]); 2399 restore_current_state(&save_state); 2400 --ex_normal_busy; 2401 } 2402 else 2403 ins_typebuf(menu->strings[idx], menu->noremap[idx], 0, 2404 TRUE, menu->silent[idx]); 2405 } 2406 else if (eap != NULL) 2407 { 2408 char_u *mode; 2409 2410 switch (idx) 2411 { 2412 case MENU_INDEX_VISUAL: 2413 mode = (char_u *)"Visual"; 2414 break; 2415 case MENU_INDEX_SELECT: 2416 mode = (char_u *)"Select"; 2417 break; 2418 case MENU_INDEX_OP_PENDING: 2419 mode = (char_u *)"Op-pending"; 2420 break; 2421 case MENU_INDEX_TERMINAL: 2422 mode = (char_u *)"Terminal"; 2423 break; 2424 case MENU_INDEX_INSERT: 2425 mode = (char_u *)"Insert"; 2426 break; 2427 case MENU_INDEX_CMDLINE: 2428 mode = (char_u *)"Cmdline"; 2429 break; 2430 // case MENU_INDEX_TIP: cannot happen 2431 default: 2432 mode = (char_u *)"Normal"; 2433 } 2434 semsg(_("E335: Menu not defined for %s mode"), mode); 2435 } 2436 } 2437 2438 /* 2439 * Lookup a menu by the descriptor name e.g. "File.New" 2440 * Returns NULL if the menu is not found 2441 */ 2442 static vimmenu_T * 2443 menu_getbyname(char_u *name_arg) 2444 { 2445 char_u *name; 2446 char_u *saved_name; 2447 vimmenu_T *menu; 2448 char_u *p; 2449 int gave_emsg = FALSE; 2450 2451 saved_name = vim_strsave(name_arg); 2452 if (saved_name == NULL) 2453 return NULL; 2454 2455 menu = *get_root_menu(saved_name); 2456 name = saved_name; 2457 while (*name) 2458 { 2459 // Find in the menu hierarchy 2460 p = menu_name_skip(name); 2461 2462 while (menu != NULL) 2463 { 2464 if (menu_name_equal(name, menu)) 2465 { 2466 if (*p == NUL && menu->children != NULL) 2467 { 2468 emsg(_("E333: Menu path must lead to a menu item")); 2469 gave_emsg = TRUE; 2470 menu = NULL; 2471 } 2472 else if (*p != NUL && menu->children == NULL) 2473 { 2474 emsg(_(e_notsubmenu)); 2475 menu = NULL; 2476 } 2477 break; 2478 } 2479 menu = menu->next; 2480 } 2481 if (menu == NULL || *p == NUL) 2482 break; 2483 menu = menu->children; 2484 name = p; 2485 } 2486 vim_free(saved_name); 2487 if (menu == NULL) 2488 { 2489 if (!gave_emsg) 2490 semsg(_("E334: Menu not found: %s"), name_arg); 2491 return NULL; 2492 } 2493 2494 return menu; 2495 } 2496 2497 /* 2498 * Given a menu descriptor, e.g. "File.New", find it in the menu hierarchy and 2499 * execute it. 2500 */ 2501 void 2502 ex_emenu(exarg_T *eap) 2503 { 2504 vimmenu_T *menu; 2505 char_u *arg = eap->arg; 2506 int mode_idx = -1; 2507 2508 if (arg[0] && VIM_ISWHITE(arg[1])) 2509 { 2510 switch (arg[0]) 2511 { 2512 case 'n': mode_idx = MENU_INDEX_NORMAL; break; 2513 case 'v': mode_idx = MENU_INDEX_VISUAL; break; 2514 case 's': mode_idx = MENU_INDEX_SELECT; break; 2515 case 'o': mode_idx = MENU_INDEX_OP_PENDING; break; 2516 case 't': mode_idx = MENU_INDEX_TERMINAL; break; 2517 case 'i': mode_idx = MENU_INDEX_INSERT; break; 2518 case 'c': mode_idx = MENU_INDEX_CMDLINE; break; 2519 default: semsg(_(e_invarg2), arg); 2520 return; 2521 } 2522 arg = skipwhite(arg + 2); 2523 } 2524 2525 menu = menu_getbyname(arg); 2526 if (menu == NULL) 2527 return; 2528 2529 // Found the menu, so execute. 2530 execute_menu(eap, menu, mode_idx); 2531 } 2532 2533 /* 2534 * Handle a click in the window toolbar of "wp" at column "col". 2535 */ 2536 void 2537 winbar_click(win_T *wp, int col) 2538 { 2539 int idx; 2540 2541 if (wp->w_winbar_items == NULL) 2542 return; 2543 for (idx = 0; wp->w_winbar_items[idx].wb_menu != NULL; ++idx) 2544 { 2545 winbar_item_T *item = &wp->w_winbar_items[idx]; 2546 2547 if (col >= item->wb_startcol && col <= item->wb_endcol) 2548 { 2549 win_T *save_curwin = NULL; 2550 pos_T save_visual = VIsual; 2551 int save_visual_active = VIsual_active; 2552 int save_visual_select = VIsual_select; 2553 int save_visual_reselect = VIsual_reselect; 2554 int save_visual_mode = VIsual_mode; 2555 2556 if (wp != curwin) 2557 { 2558 // Clicking in the window toolbar of a not-current window. 2559 // Make that window the current one and save Visual mode. 2560 save_curwin = curwin; 2561 VIsual_active = FALSE; 2562 curwin = wp; 2563 curbuf = curwin->w_buffer; 2564 check_cursor(); 2565 } 2566 2567 // Note: the command might close the current window. 2568 execute_menu(NULL, item->wb_menu, -1); 2569 2570 if (save_curwin != NULL && win_valid(save_curwin)) 2571 { 2572 curwin = save_curwin; 2573 curbuf = curwin->w_buffer; 2574 VIsual = save_visual; 2575 VIsual_active = save_visual_active; 2576 VIsual_select = save_visual_select; 2577 VIsual_reselect = save_visual_reselect; 2578 VIsual_mode = save_visual_mode; 2579 } 2580 if (!win_valid(wp)) 2581 break; 2582 } 2583 } 2584 } 2585 2586 #if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_GTK) \ 2587 || defined(FEAT_TERM_POPUP_MENU) || defined(FEAT_GUI_HAIKU) \ 2588 || defined(FEAT_BEVAL_TIP) || defined(PROTO) 2589 /* 2590 * Given a menu descriptor, e.g. "File.New", find it in the menu hierarchy. 2591 */ 2592 vimmenu_T * 2593 gui_find_menu(char_u *path_name) 2594 { 2595 vimmenu_T *menu = NULL; 2596 char_u *name; 2597 char_u *saved_name; 2598 char_u *p; 2599 2600 menu = *get_root_menu(path_name); 2601 2602 saved_name = vim_strsave(path_name); 2603 if (saved_name == NULL) 2604 return NULL; 2605 2606 name = saved_name; 2607 while (*name) 2608 { 2609 // find the end of one dot-separated name and put a NUL at the dot 2610 p = menu_name_skip(name); 2611 2612 while (menu != NULL) 2613 { 2614 if (menu_name_equal(name, menu)) 2615 { 2616 if (menu->children == NULL) 2617 { 2618 // found a menu item instead of a sub-menu 2619 if (*p == NUL) 2620 emsg(_("E336: Menu path must lead to a sub-menu")); 2621 else 2622 emsg(_(e_notsubmenu)); 2623 menu = NULL; 2624 goto theend; 2625 } 2626 if (*p == NUL) // found a full match 2627 goto theend; 2628 break; 2629 } 2630 menu = menu->next; 2631 } 2632 if (menu == NULL) // didn't find it 2633 break; 2634 2635 // Found a match, search the sub-menu. 2636 menu = menu->children; 2637 name = p; 2638 } 2639 2640 if (menu == NULL) 2641 emsg(_("E337: Menu not found - check menu names")); 2642 theend: 2643 vim_free(saved_name); 2644 return menu; 2645 } 2646 #endif 2647 2648 #ifdef FEAT_MULTI_LANG 2649 /* 2650 * Translation of menu names. Just a simple lookup table. 2651 */ 2652 2653 typedef struct 2654 { 2655 char_u *from; // English name 2656 char_u *from_noamp; // same, without '&' 2657 char_u *to; // translated name 2658 } menutrans_T; 2659 2660 static garray_T menutrans_ga = {0, 0, 0, 0, NULL}; 2661 #endif 2662 2663 /* 2664 * ":menutrans". 2665 * This function is also defined without the +multi_lang feature, in which 2666 * case the commands are ignored. 2667 */ 2668 void 2669 ex_menutranslate(exarg_T *eap UNUSED) 2670 { 2671 #ifdef FEAT_MULTI_LANG 2672 char_u *arg = eap->arg; 2673 menutrans_T *tp; 2674 int i; 2675 char_u *from, *from_noamp, *to; 2676 2677 if (menutrans_ga.ga_itemsize == 0) 2678 ga_init2(&menutrans_ga, (int)sizeof(menutrans_T), 5); 2679 2680 /* 2681 * ":menutrans clear": clear all translations. 2682 */ 2683 if (STRNCMP(arg, "clear", 5) == 0 && ends_excmd2(arg, skipwhite(arg + 5))) 2684 { 2685 tp = (menutrans_T *)menutrans_ga.ga_data; 2686 for (i = 0; i < menutrans_ga.ga_len; ++i) 2687 { 2688 vim_free(tp[i].from); 2689 vim_free(tp[i].from_noamp); 2690 vim_free(tp[i].to); 2691 } 2692 ga_clear(&menutrans_ga); 2693 # ifdef FEAT_EVAL 2694 // Delete all "menutrans_" global variables. 2695 del_menutrans_vars(); 2696 # endif 2697 } 2698 else 2699 { 2700 // ":menutrans from to": add translation 2701 from = arg; 2702 arg = menu_skip_part(arg); 2703 to = skipwhite(arg); 2704 *arg = NUL; 2705 arg = menu_skip_part(to); 2706 if (arg == to || ends_excmd2(eap->arg, from) 2707 || ends_excmd2(eap->arg, to) 2708 || !ends_excmd2(eap->arg, skipwhite(arg))) 2709 emsg(_(e_invarg)); 2710 else 2711 { 2712 if (ga_grow(&menutrans_ga, 1) == OK) 2713 { 2714 tp = (menutrans_T *)menutrans_ga.ga_data; 2715 from = vim_strsave(from); 2716 if (from != NULL) 2717 { 2718 from_noamp = menu_text(from, NULL, NULL); 2719 to = vim_strnsave(to, arg - to); 2720 if (from_noamp != NULL && to != NULL) 2721 { 2722 menu_translate_tab_and_shift(from); 2723 menu_translate_tab_and_shift(to); 2724 menu_unescape_name(from); 2725 menu_unescape_name(to); 2726 tp[menutrans_ga.ga_len].from = from; 2727 tp[menutrans_ga.ga_len].from_noamp = from_noamp; 2728 tp[menutrans_ga.ga_len].to = to; 2729 ++menutrans_ga.ga_len; 2730 } 2731 else 2732 { 2733 vim_free(from); 2734 vim_free(from_noamp); 2735 vim_free(to); 2736 } 2737 } 2738 } 2739 } 2740 } 2741 #endif 2742 } 2743 2744 #if defined(FEAT_MULTI_LANG) || defined(FEAT_TOOLBAR) 2745 /* 2746 * Find the character just after one part of a menu name. 2747 */ 2748 static char_u * 2749 menu_skip_part(char_u *p) 2750 { 2751 while (*p != NUL && *p != '.' && !VIM_ISWHITE(*p)) 2752 { 2753 if ((*p == '\\' || *p == Ctrl_V) && p[1] != NUL) 2754 ++p; 2755 ++p; 2756 } 2757 return p; 2758 } 2759 #endif 2760 2761 #ifdef FEAT_MULTI_LANG 2762 /* 2763 * Lookup part of a menu name in the translations. 2764 * Return a pointer to the translation or NULL if not found. 2765 */ 2766 static char_u * 2767 menutrans_lookup(char_u *name, int len) 2768 { 2769 menutrans_T *tp = (menutrans_T *)menutrans_ga.ga_data; 2770 int i; 2771 char_u *dname; 2772 2773 for (i = 0; i < menutrans_ga.ga_len; ++i) 2774 if (STRNICMP(name, tp[i].from, len) == 0 && tp[i].from[len] == NUL) 2775 return tp[i].to; 2776 2777 // Now try again while ignoring '&' characters. 2778 i = name[len]; 2779 name[len] = NUL; 2780 dname = menu_text(name, NULL, NULL); 2781 name[len] = i; 2782 if (dname != NULL) 2783 { 2784 for (i = 0; i < menutrans_ga.ga_len; ++i) 2785 if (STRICMP(dname, tp[i].from_noamp) == 0) 2786 { 2787 vim_free(dname); 2788 return tp[i].to; 2789 } 2790 vim_free(dname); 2791 } 2792 2793 return NULL; 2794 } 2795 2796 /* 2797 * Unescape the name in the translate dictionary table. 2798 */ 2799 static void 2800 menu_unescape_name(char_u *name) 2801 { 2802 char_u *p; 2803 2804 for (p = name; *p && *p != '.'; MB_PTR_ADV(p)) 2805 if (*p == '\\') 2806 STRMOVE(p, p + 1); 2807 } 2808 #endif // FEAT_MULTI_LANG 2809 2810 /* 2811 * Isolate the menu name. 2812 * Skip the menu name, and translate <Tab> into a real TAB. 2813 */ 2814 static char_u * 2815 menu_translate_tab_and_shift(char_u *arg_start) 2816 { 2817 char_u *arg = arg_start; 2818 2819 while (*arg && !VIM_ISWHITE(*arg)) 2820 { 2821 if ((*arg == '\\' || *arg == Ctrl_V) && arg[1] != NUL) 2822 arg++; 2823 else if (STRNICMP(arg, "<TAB>", 5) == 0) 2824 { 2825 *arg = TAB; 2826 STRMOVE(arg + 1, arg + 5); 2827 } 2828 arg++; 2829 } 2830 if (*arg != NUL) 2831 *arg++ = NUL; 2832 arg = skipwhite(arg); 2833 2834 return arg; 2835 } 2836 2837 /* 2838 * Get the information about a menu item in mode 'which' 2839 */ 2840 static int 2841 menuitem_getinfo(vimmenu_T *menu, int modes, dict_T *dict) 2842 { 2843 int status; 2844 2845 if (menu_is_tearoff(menu->dname)) // skip tearoff menu item 2846 return OK; 2847 2848 status = dict_add_string(dict, "name", menu->name); 2849 if (status == OK) 2850 status = dict_add_string(dict, "display", menu->dname); 2851 if (status == OK && menu->actext != NULL) 2852 status = dict_add_string(dict, "accel", menu->actext); 2853 if (status == OK) 2854 status = dict_add_number(dict, "priority", menu->priority); 2855 if (status == OK) 2856 status = dict_add_string(dict, "modes", 2857 get_menu_mode_str(menu->modes)); 2858 #ifdef FEAT_TOOLBAR 2859 if (status == OK && menu->iconfile != NULL) 2860 status = dict_add_string(dict, "icon", menu->iconfile); 2861 if (status == OK && menu->iconidx >= 0) 2862 status = dict_add_number(dict, "iconidx", menu->iconidx); 2863 #endif 2864 if (status == OK) 2865 { 2866 char_u buf[NUMBUFLEN]; 2867 2868 if (has_mbyte) 2869 buf[utf_char2bytes(menu->mnemonic, buf)] = NUL; 2870 else 2871 { 2872 buf[0] = (char_u)menu->mnemonic; 2873 buf[1] = NUL; 2874 } 2875 status = dict_add_string(dict, "shortcut", buf); 2876 } 2877 if (status == OK && menu->children == NULL) 2878 { 2879 int bit; 2880 2881 // Get the first mode in which the menu is available 2882 for (bit = 0; bit < MENU_MODES && !((1 << bit) & modes); bit++) 2883 ; 2884 if (bit < MENU_MODES) // just in case, avoid Coverity warning 2885 { 2886 if (menu->strings[bit] != NULL) 2887 { 2888 char_u *tofree = NULL; 2889 2890 status = dict_add_string(dict, "rhs", 2891 *menu->strings[bit] == NUL 2892 ? (char_u *)"<Nop>" 2893 : (tofree = str2special_save( 2894 menu->strings[bit], FALSE))); 2895 vim_free(tofree); 2896 } 2897 if (status == OK) 2898 status = dict_add_bool(dict, "noremenu", 2899 menu->noremap[bit] == REMAP_NONE); 2900 if (status == OK) 2901 status = dict_add_bool(dict, "script", 2902 menu->noremap[bit] == REMAP_SCRIPT); 2903 if (status == OK) 2904 status = dict_add_bool(dict, "silent", menu->silent[bit]); 2905 if (status == OK) 2906 status = dict_add_bool(dict, "enabled", 2907 ((menu->enabled & (1 << bit)) != 0)); 2908 } 2909 } 2910 2911 // If there are submenus, add all the submenu display names 2912 if (status == OK && menu->children != NULL) 2913 { 2914 list_T *l = list_alloc(); 2915 vimmenu_T *child; 2916 2917 if (l == NULL) 2918 return FAIL; 2919 2920 dict_add_list(dict, "submenus", l); 2921 child = menu->children; 2922 while (child) 2923 { 2924 if (!menu_is_tearoff(child->dname)) // skip tearoff menu 2925 list_append_string(l, child->dname, -1); 2926 child = child->next; 2927 } 2928 } 2929 2930 return status; 2931 } 2932 2933 /* 2934 * "menu_info()" function 2935 * Return information about a menu (including all the child menus) 2936 */ 2937 void 2938 f_menu_info(typval_T *argvars, typval_T *rettv) 2939 { 2940 char_u *menu_name; 2941 char_u *which; 2942 int modes; 2943 char_u *saved_name; 2944 char_u *name; 2945 vimmenu_T *menu; 2946 dict_T *retdict; 2947 2948 if (rettv_dict_alloc(rettv) != OK) 2949 return; 2950 retdict = rettv->vval.v_dict; 2951 2952 menu_name = tv_get_string_chk(&argvars[0]); 2953 if (menu_name == NULL) 2954 return; 2955 2956 // menu mode 2957 if (argvars[1].v_type != VAR_UNKNOWN) 2958 which = tv_get_string_chk(&argvars[1]); 2959 else 2960 which = (char_u *)""; // Default is modes for "menu" 2961 if (which == NULL) 2962 return; 2963 2964 modes = get_menu_cmd_modes(which, *which == '!', NULL, NULL); 2965 2966 // Locate the specified menu or menu item 2967 menu = *get_root_menu(menu_name); 2968 saved_name = vim_strsave(menu_name); 2969 if (saved_name == NULL) 2970 return; 2971 if (*saved_name != NUL) 2972 { 2973 char_u *p; 2974 2975 name = saved_name; 2976 while (*name) 2977 { 2978 // Find in the menu hierarchy 2979 p = menu_name_skip(name); 2980 while (menu != NULL) 2981 { 2982 if (menu_name_equal(name, menu)) 2983 break; 2984 menu = menu->next; 2985 } 2986 if (menu == NULL || *p == NUL) 2987 break; 2988 menu = menu->children; 2989 name = p; 2990 } 2991 } 2992 vim_free(saved_name); 2993 2994 if (menu == NULL) // specified menu not found 2995 return; 2996 2997 if (menu->modes & modes) 2998 menuitem_getinfo(menu, modes, retdict); 2999 } 3000 3001 #endif // FEAT_MENU 3002