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