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