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