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