/* * Free the given menu structure and remove it from the linked list. */ static void free_menu(vimmenu_T **menup) { int i; vimmenu_T *menu; menu = *menup; /* Don't change *menup until after calling gui_mch_destroy_menu(). The * MacOS code needs the original structure to properly delete the menu. */ *menup = menu->next; free(menu->name); free(menu->dname); free(menu->en_name); free(menu->en_dname); free(menu->actext); for (i = 0; i < MENU_MODES; i++) free_menu_string(menu, i); free(menu); }
/* * Add the menu with the given name to the menu hierarchy */ static int add_menu_path ( char_u *menu_path, vimmenu_T *menuarg, /* passes modes, iconfile, iconidx, icon_builtin, silent[0], noremap[0] */ int *pri_tab, char_u *call_data ) { char_u *path_name; int modes = menuarg->modes; vimmenu_T **menup; vimmenu_T *menu = NULL; vimmenu_T *parent; vimmenu_T **lower_pri; char_u *p; char_u *name; char_u *dname; char_u *next_name; int i; int c; int d; int pri_idx = 0; int old_modes = 0; int amenu; char_u *en_name; char_u *map_to = NULL; /* Make a copy so we can stuff around with it, since it could be const */ path_name = vim_strsave(menu_path); menup = &root_menu; parent = NULL; name = path_name; while (*name) { /* Get name of this element in the menu hierarchy, and the simplified * name (without mnemonic and accelerator text). */ next_name = menu_name_skip(name); map_to = menutrans_lookup(name, (int)STRLEN(name)); if (map_to != NULL) { en_name = name; name = map_to; } else en_name = NULL; dname = menu_text(name, NULL, NULL); if (dname == NULL) goto erret; if (*dname == NUL) { /* Only a mnemonic or accelerator is not valid. */ EMSG(_("E792: Empty menu name")); goto erret; } /* See if it's already there */ lower_pri = menup; menu = *menup; while (menu != NULL) { if (menu_name_equal(name, menu) || menu_name_equal(dname, menu)) { if (*next_name == NUL && menu->children != NULL) { if (!sys_menu) EMSG(_("E330: Menu path must not lead to a sub-menu")); goto erret; } if (*next_name != NUL && menu->children == NULL ) { if (!sys_menu) EMSG(_(e_notsubmenu)); goto erret; } break; } menup = &menu->next; /* Count menus, to find where this one needs to be inserted. * Ignore menus that are not in the menubar (PopUp and Toolbar) */ if (parent != NULL || menu_is_menubar(menu->name)) { if (menu->priority <= pri_tab[pri_idx]) { lower_pri = menup; } } menu = menu->next; } if (menu == NULL) { if (*next_name == NUL && parent == NULL) { EMSG(_("E331: Must not add menu items directly to menu bar")); goto erret; } if (menu_is_separator(dname) && *next_name != NUL) { EMSG(_("E332: Separator cannot be part of a menu path")); goto erret; } /* Not already there, so lets add it */ menu = xcalloc(1, sizeof(vimmenu_T)); menu->modes = modes; menu->enabled = MENU_ALL_MODES; menu->name = vim_strsave(name); /* separate mnemonic and accelerator text from actual menu name */ menu->dname = menu_text(name, &menu->mnemonic, &menu->actext); if (en_name != NULL) { menu->en_name = vim_strsave(en_name); menu->en_dname = menu_text(en_name, NULL, NULL); } else { menu->en_name = NULL; menu->en_dname = NULL; } menu->priority = pri_tab[pri_idx]; menu->parent = parent; /* * Add after menu that has lower priority. */ menu->next = *lower_pri; *lower_pri = menu; old_modes = 0; } else { old_modes = menu->modes; /* * If this menu option was previously only available in other * modes, then make sure it's available for this one now * Also enable a menu when it's created or changed. */ { menu->modes |= modes; menu->enabled |= modes; } } menup = &menu->children; parent = menu; name = next_name; free(dname); dname = NULL; if (pri_tab[pri_idx + 1] != -1) ++pri_idx; } free(path_name); /* * Only add system menu items which have not been defined yet. * First check if this was an ":amenu". */ amenu = ((modes & (MENU_NORMAL_MODE | MENU_INSERT_MODE)) == (MENU_NORMAL_MODE | MENU_INSERT_MODE)); if (sys_menu) modes &= ~old_modes; if (menu != NULL && modes) { p = (call_data == NULL) ? NULL : vim_strsave(call_data); /* loop over all modes, may add more than one */ for (i = 0; i < MENU_MODES; ++i) { if (modes & (1 << i)) { /* free any old menu */ free_menu_string(menu, i); /* For "amenu", may insert an extra character. * Don't do this if adding a tearbar (addtearoff == FALSE). * Don't do this for "<Nop>". */ c = 0; d = 0; if (amenu && call_data != NULL && *call_data != NUL ) { switch (1 << i) { case MENU_VISUAL_MODE: case MENU_SELECT_MODE: case MENU_OP_PENDING_MODE: case MENU_CMDLINE_MODE: c = Ctrl_C; break; case MENU_INSERT_MODE: c = Ctrl_BSL; d = Ctrl_O; break; } } if (c != 0) { menu->strings[i] = xmalloc(STRLEN(call_data) + 5 ); menu->strings[i][0] = c; if (d == 0) STRCPY(menu->strings[i] + 1, call_data); else { menu->strings[i][1] = d; STRCPY(menu->strings[i] + 2, call_data); } if (c == Ctrl_C) { int len = (int)STRLEN(menu->strings[i]); /* Append CTRL-\ CTRL-G to obey 'insertmode'. */ menu->strings[i][len] = Ctrl_BSL; menu->strings[i][len + 1] = Ctrl_G; menu->strings[i][len + 2] = NUL; } } else menu->strings[i] = p; menu->noremap[i] = menuarg->noremap[0]; menu->silent[i] = menuarg->silent[0]; } } #if defined(FEAT_TOOLBAR) && !defined(FEAT_GUI_W32) \ && (defined(FEAT_BEVAL) || defined(FEAT_GUI_GTK)) /* Need to update the menu tip. */ if (modes & MENU_TIP_MODE) gui_mch_menu_set_tip(menu); #endif } return OK; erret: free(path_name); free(dname); /* Delete any empty submenu we added before discovering the error. Repeat * for higher levels. */ while (parent != NULL && parent->children == NULL) { if (parent->parent == NULL) menup = &root_menu; else menup = &parent->parent->children; for (; *menup != NULL && *menup != parent; menup = &((*menup)->next)) ; if (*menup == NULL) /* safety check */ break; parent = parent->parent; free_menu(menup); } return FAIL; }
/* * Remove the (sub)menu with the given name from the menu hierarchy * Called recursively. */ static int remove_menu ( vimmenu_T **menup, char_u *name, int modes, int silent /* don't give error messages */ ) { vimmenu_T *menu; vimmenu_T *child; char_u *p; if (*menup == NULL) return OK; /* Got to bottom of hierarchy */ /* Get name of this element in the menu hierarchy */ p = menu_name_skip(name); /* Find the menu */ while ((menu = *menup) != NULL) { if (*name == NUL || menu_name_equal(name, menu)) { if (*p != NUL && menu->children == NULL) { if (!silent) EMSG(_(e_notsubmenu)); return FAIL; } if ((menu->modes & modes) != 0x0) { #if defined(FEAT_GUI_W32) & defined(FEAT_TEAROFF) /* * If we are removing all entries for this menu,MENU_ALL_MODES, * Then kill any tearoff before we start */ if (*p == NUL && modes == MENU_ALL_MODES) { if (IsWindow(menu->tearoff_handle)) DestroyWindow(menu->tearoff_handle); } #endif if (remove_menu(&menu->children, p, modes, silent) == FAIL) return FAIL; } else if (*name != NUL) { if (!silent) EMSG(_(e_othermode)); return FAIL; } /* * When name is empty, we are removing all menu items for the given * modes, so keep looping, otherwise we are just removing the named * menu item (which has been found) so break here. */ if (*name != NUL) break; /* Remove the menu item for the given mode[s]. If the menu item * is no longer valid in ANY mode, delete it */ menu->modes &= ~modes; if (modes & MENU_TIP_MODE) free_menu_string(menu, MENU_INDEX_TIP); if ((menu->modes & MENU_ALL_MODES) == 0) free_menu(menup); else menup = &menu->next; } else menup = &menu->next; } if (*name != NUL) { if (menu == NULL) { if (!silent) EMSG2(_(e_nomenu), name); return FAIL; } /* Recalculate modes for menu based on the new updated children */ menu->modes &= ~modes; #if defined(FEAT_GUI_W32) & defined(FEAT_TEAROFF) if ((s_tearoffs) && (menu->children != NULL)) /* there's a tear bar.. */ child = menu->children->next; /* don't count tearoff bar */ else #endif child = menu->children; for (; child != NULL; child = child->next) menu->modes |= child->modes; if (modes & MENU_TIP_MODE) { free_menu_string(menu, MENU_INDEX_TIP); #if defined(FEAT_TOOLBAR) && !defined(FEAT_GUI_W32) \ && (defined(FEAT_BEVAL) || defined(FEAT_GUI_GTK)) /* Need to update the menu tip. */ if (gui.in_use) gui_mch_menu_set_tip(menu); #endif } if ((menu->modes & MENU_ALL_MODES) == 0) { /* The menu item is no longer valid in ANY mode, so delete it */ #if defined(FEAT_GUI_W32) & defined(FEAT_TEAROFF) if (s_tearoffs && menu->children != NULL) /* there's a tear bar.. */ free_menu(&menu->children); #endif *menup = menu; free_menu(menup); } } return OK; }
/* * Remove the (sub)menu with the given name from the menu hierarchy * Called recursively. */ static int remove_menu ( vimmenu_T **menup, char_u *name, int modes, bool silent /* don't give error messages */ ) { vimmenu_T *menu; vimmenu_T *child; char_u *p; if (*menup == NULL) return OK; /* Got to bottom of hierarchy */ /* Get name of this element in the menu hierarchy */ p = menu_name_skip(name); /* Find the menu */ while ((menu = *menup) != NULL) { if (*name == NUL || menu_name_equal(name, menu)) { if (*p != NUL && menu->children == NULL) { if (!silent) EMSG(_(e_notsubmenu)); return FAIL; } if ((menu->modes & modes) != 0x0) { if (remove_menu(&menu->children, p, modes, silent) == FAIL) return FAIL; } else if (*name != NUL) { if (!silent) EMSG(_(e_othermode)); return FAIL; } /* * When name is empty, we are removing all menu items for the given * modes, so keep looping, otherwise we are just removing the named * menu item (which has been found) so break here. */ if (*name != NUL) break; /* Remove the menu item for the given mode[s]. If the menu item * is no longer valid in ANY mode, delete it */ menu->modes &= ~modes; if (modes & MENU_TIP_MODE) free_menu_string(menu, MENU_INDEX_TIP); if ((menu->modes & MENU_ALL_MODES) == 0) free_menu(menup); else menup = &menu->next; } else menup = &menu->next; } if (*name != NUL) { if (menu == NULL) { if (!silent) EMSG2(_(e_nomenu), name); return FAIL; } /* Recalculate modes for menu based on the new updated children */ menu->modes &= ~modes; child = menu->children; for (; child != NULL; child = child->next) menu->modes |= child->modes; if (modes & MENU_TIP_MODE) { free_menu_string(menu, MENU_INDEX_TIP); } if ((menu->modes & MENU_ALL_MODES) == 0) { /* The menu item is no longer valid in ANY mode, so delete it */ *menup = menu; free_menu(menup); } } return OK; }