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