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