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