blob: 17a208f08eb6b5c96e0afdaf1328b38e90b6e228 [file] [log] [blame]
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.openapi.keymap.impl.ui;
import com.intellij.icons.AllIcons;
import com.intellij.ide.actionMacro.ActionMacro;
import com.intellij.ide.plugins.IdeaPluginDescriptor;
import com.intellij.ide.plugins.PluginManagerCore;
import com.intellij.ide.ui.search.SearchUtil;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.actionSystem.ex.ActionManagerEx;
import com.intellij.openapi.actionSystem.ex.QuickList;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.extensions.PluginId;
import com.intellij.openapi.keymap.KeyMapBundle;
import com.intellij.openapi.keymap.Keymap;
import com.intellij.openapi.keymap.KeymapExtension;
import com.intellij.openapi.keymap.ex.KeymapManagerEx;
import com.intellij.openapi.keymap.impl.KeymapImpl;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import java.util.*;
public class ActionsTreeUtil {
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.keymap.impl.ui.ActionsTreeUtil");
public static final String MAIN_MENU_TITLE = KeyMapBundle.message("main.menu.action.title");
public static final String MAIN_TOOLBAR = KeyMapBundle.message("main.toolbar.title");
public static final String EDITOR_POPUP = KeyMapBundle.message("editor.popup.menu.title");
public static final String EDITOR_TAB_POPUP = KeyMapBundle.message("editor.tab.popup.menu.title");
public static final String FAVORITES_POPUP = KeyMapBundle.message("favorites.popup.title");
public static final String PROJECT_VIEW_POPUP = KeyMapBundle.message("project.view.popup.menu.title");
public static final String COMMANDER_POPUP = KeyMapBundle.message("commender.view.popup.menu.title");
public static final String J2EE_POPUP = KeyMapBundle.message("j2ee.view.popup.menu.title");
@NonNls
private static final String EDITOR_PREFIX = "Editor";
@NonNls private static final String TOOL_ACTION_PREFIX = "Tool_";
private ActionsTreeUtil() {
}
private static Group createPluginsActionsGroup(Condition<AnAction> filtered) {
Group pluginsGroup = new Group(KeyMapBundle.message("plugins.group.title"), null, null);
final KeymapManagerEx keymapManager = KeymapManagerEx.getInstanceEx();
ActionManagerEx managerEx = ActionManagerEx.getInstanceEx();
final List<IdeaPluginDescriptor> plugins = new ArrayList<IdeaPluginDescriptor>();
Collections.addAll(plugins, PluginManagerCore.getPlugins());
Collections.sort(plugins, new Comparator<IdeaPluginDescriptor>() {
public int compare(IdeaPluginDescriptor o1, IdeaPluginDescriptor o2) {
return o1.getName().compareTo(o2.getName());
}
});
List<PluginId> collected = new ArrayList<PluginId>();
for (IdeaPluginDescriptor plugin : plugins) {
collected.add(plugin.getPluginId());
Group pluginGroup;
if (plugin.getName().equals("IDEA CORE")) {
continue;
}
else {
pluginGroup = new Group(plugin.getName(), null, null);
}
final String[] pluginActions = managerEx.getPluginActions(plugin.getPluginId());
if (pluginActions == null || pluginActions.length == 0) {
continue;
}
Arrays.sort(pluginActions, new Comparator<String>() {
public int compare(String o1, String o2) {
return getTextToCompare(o1).compareTo(getTextToCompare(o2));
}
});
for (String pluginAction : pluginActions) {
if (keymapManager.getBoundActions().contains(pluginAction)) continue;
final AnAction anAction = managerEx.getActionOrStub(pluginAction);
if (filtered == null || filtered.value(anAction)) {
pluginGroup.addActionId(pluginAction);
}
}
if (pluginGroup.getSize() > 0) {
pluginsGroup.addGroup(pluginGroup);
}
}
for (PluginId pluginId : PluginId.getRegisteredIds().values()) {
if (collected.contains(pluginId)) continue;
Group pluginGroup = new Group(pluginId.getIdString(), null, null);
final String[] pluginActions = managerEx.getPluginActions(pluginId);
if (pluginActions == null || pluginActions.length == 0) {
continue;
}
for (String pluginAction : pluginActions) {
if (keymapManager.getBoundActions().contains(pluginAction)) continue;
final AnAction anAction = managerEx.getActionOrStub(pluginAction);
if (filtered == null || filtered.value(anAction)) {
pluginGroup.addActionId(pluginAction);
}
}
if (pluginGroup.getSize() > 0) {
pluginsGroup.addGroup(pluginGroup);
}
}
return pluginsGroup;
}
private static Group createMainMenuGroup(Condition<AnAction> filtered) {
Group group = new Group(MAIN_MENU_TITLE, IdeActions.GROUP_MAIN_MENU, AllIcons.Nodes.KeymapMainMenu);
ActionGroup mainMenuGroup = (ActionGroup)ActionManager.getInstance().getActionOrStub(IdeActions.GROUP_MAIN_MENU);
fillGroupIgnorePopupFlag(mainMenuGroup, group, filtered);
return group;
}
@Nullable
private static Condition<AnAction> wrapFilter(@Nullable final Condition<AnAction> filter, final Keymap keymap, final ActionManager actionManager) {
if (Registry.is("keymap.show.alias.actions")) return filter;
return new Condition<AnAction>() {
@Override
public boolean value(final AnAction action) {
if (action == null) return false;
final String id = action instanceof ActionStub ? ((ActionStub)action).getId() : actionManager.getId(action);
if (id != null) {
String binding = getActionBinding(keymap, id);
boolean bound = binding != null
&& actionManager.getAction(binding) != null // do not hide bound action, that miss the 'bound-with'
&& !hasAssociatedShortcutsInHierarchy(id, keymap); // do not hide bound actions when they are redefined
return filter == null ? !bound : !bound && filter.value(action);
}
return filter == null || filter.value(action);
}
};
}
private static boolean hasAssociatedShortcutsInHierarchy(String id, Keymap keymap) {
while (keymap != null) {
if (((KeymapImpl)keymap).hasOwnActionId(id)) return true;
keymap = keymap.getParent();
}
return false;
}
private static void fillGroupIgnorePopupFlag(ActionGroup actionGroup, Group group, Condition<AnAction> filtered) {
AnAction[] mainMenuTopGroups = actionGroup instanceof DefaultActionGroup
? ((DefaultActionGroup)actionGroup).getChildActionsOrStubs()
: actionGroup.getChildren(null);
for (AnAction action : mainMenuTopGroups) {
if (!(action instanceof ActionGroup)) continue;
Group subGroup = createGroup((ActionGroup)action, false, filtered);
if (subGroup.getSize() > 0) {
group.addGroup(subGroup);
}
}
}
public static Group createGroup(ActionGroup actionGroup, boolean ignore, Condition<AnAction> filtered) {
return createGroup(actionGroup, getName(actionGroup), null, null, ignore, filtered);
}
private static String getName(AnAction action) {
final String name = action.getTemplatePresentation().getText();
if (name != null && !name.isEmpty()) {
return name;
}
else {
final String id = action instanceof ActionStub ? ((ActionStub)action).getId() : ActionManager.getInstance().getId(action);
if (id != null) {
return id;
}
if (action instanceof DefaultActionGroup) {
final DefaultActionGroup group = (DefaultActionGroup)action;
if (group.getChildrenCount() == 0) return "Empty group";
final AnAction[] children = group.getChildActionsOrStubs();
for (AnAction child : children) {
if (!(child instanceof Separator)) {
return "group." + getName(child);
}
}
return "Empty unnamed group";
}
return action.getClass().getName();
}
}
public static Group createGroup(ActionGroup actionGroup,
String groupName,
Icon icon,
Icon openIcon,
boolean ignore,
Condition<AnAction> filtered) {
return createGroup(actionGroup, groupName, icon, openIcon, ignore, filtered, true);
}
public static Group createGroup(ActionGroup actionGroup, String groupName, Icon icon, Icon openIcon, boolean ignore, Condition<AnAction> filtered,
boolean normalizeSeparators) {
ActionManager actionManager = ActionManager.getInstance();
Group group = new Group(groupName, actionManager.getId(actionGroup), icon);
AnAction[] children = actionGroup instanceof DefaultActionGroup
? ((DefaultActionGroup)actionGroup).getChildActionsOrStubs()
: actionGroup.getChildren(null);
for (AnAction action : children) {
if (action == null) {
LOG.error(groupName + " contains null actions");
continue;
}
if (action instanceof ActionGroup) {
Group subGroup = createGroup((ActionGroup)action, getName(action), null, null, ignore, filtered, normalizeSeparators);
if (subGroup.getSize() > 0) {
if (!ignore && !((ActionGroup)action).isPopup()) {
group.addAll(subGroup);
}
else {
group.addGroup(subGroup);
}
}
else if (filtered == null || filtered.value(actionGroup)) {
group.addGroup(subGroup);
}
}
else if (action instanceof Separator) {
group.addSeparator();
}
else {
String id = action instanceof ActionStub ? ((ActionStub)action).getId() : actionManager.getId(action);
if (id != null) {
if (id.startsWith(TOOL_ACTION_PREFIX)) continue;
if (filtered == null || filtered.value(action)) {
group.addActionId(id);
}
}
}
}
if (normalizeSeparators) group.normalizeSeparators();
return group;
}
private static Group createEditorActionsGroup(Condition<AnAction> filtered) {
ActionManager actionManager = ActionManager.getInstance();
DefaultActionGroup editorGroup = (DefaultActionGroup)actionManager.getActionOrStub(IdeActions.GROUP_EDITOR);
ArrayList<String> ids = new ArrayList<String>();
addEditorActions(filtered, editorGroup, ids);
Collections.sort(ids);
Group group = new Group(KeyMapBundle.message("editor.actions.group.title"), IdeActions.GROUP_EDITOR, AllIcons.Nodes.KeymapEditor
);
for (String id : ids) {
group.addActionId(id);
}
return group;
}
@Nullable
private static String getActionBinding(final Keymap keymap, final String id) {
if (keymap == null) return null;
Keymap parent = keymap.getParent();
String result = ((KeymapImpl)keymap).getActionBinding(id);
if (result == null && parent != null) {
result = ((KeymapImpl)parent).getActionBinding(id);
}
return result;
}
private static void addEditorActions(final Condition<AnAction> filtered,
final DefaultActionGroup editorGroup,
final ArrayList<String> ids) {
AnAction[] editorActions = editorGroup.getChildActionsOrStubs();
final ActionManager actionManager = ActionManager.getInstance();
for (AnAction editorAction : editorActions) {
if (editorAction instanceof DefaultActionGroup) {
addEditorActions(filtered, (DefaultActionGroup) editorAction, ids);
}
else {
String actionId = editorAction instanceof ActionStub ? ((ActionStub)editorAction).getId() : actionManager.getId(editorAction);
if (actionId == null) continue;
if (filtered == null || filtered.value(editorAction)) {
ids.add(actionId);
}
}
}
}
private static Group createExtensionGroup(Condition<AnAction> filtered, final Project project, KeymapExtension provider) {
return (Group) provider.createGroup(filtered, project);
}
private static Group createMacrosGroup(Condition<AnAction> filtered) {
final ActionManagerEx actionManager = ActionManagerEx.getInstanceEx();
String[] ids = actionManager.getActionIds(ActionMacro.MACRO_ACTION_PREFIX);
Arrays.sort(ids);
Group group = new Group(KeyMapBundle.message("macros.group.title"), null, null);
for (String id : ids) {
if (filtered == null || filtered.value(actionManager.getActionOrStub(id))) {
group.addActionId(id);
}
}
return group;
}
private static Group createQuickListsGroup(final Condition<AnAction> filtered, final String filter, final boolean forceFiltering, final QuickList[] quickLists) {
Arrays.sort(quickLists, new Comparator<QuickList>() {
public int compare(QuickList l1, QuickList l2) {
return l1.getActionId().compareTo(l2.getActionId());
}
});
Group group = new Group(KeyMapBundle.message("quick.lists.group.title"), null, null);
for (QuickList quickList : quickLists) {
if (filtered != null && filtered.value(ActionManagerEx.getInstanceEx().getAction(quickList.getActionId()))) {
group.addQuickList(quickList);
} else if (SearchUtil.isComponentHighlighted(quickList.getDisplayName(), filter, forceFiltering, null)) {
group.addQuickList(quickList);
} else if (filtered == null && StringUtil.isEmpty(filter)) {
group.addQuickList(quickList);
}
}
return group;
}
private static Group createOtherGroup(Condition<AnAction> filtered, Group addedActions, final Keymap keymap) {
addedActions.initIds();
ArrayList<String> result = new ArrayList<String>();
if (keymap != null) {
String[] actionIds = keymap.getActionIds();
for (String id : actionIds) {
if (id.startsWith(EDITOR_PREFIX)) {
AnAction action = ActionManager.getInstance().getActionOrStub("$" + id.substring(6));
if (action != null) continue;
}
if (!id.startsWith(QuickList.QUICK_LIST_PREFIX) && !addedActions.containsId(id)) {
result.add(id);
}
}
}
// add all registered actions
final ActionManagerEx actionManager = ActionManagerEx.getInstanceEx();
final KeymapManagerEx keymapManager = KeymapManagerEx.getInstanceEx();
String[] registeredActionIds = actionManager.getActionIds("");
for (String id : registeredActionIds) {
if (actionManager.getActionOrStub(id)instanceof ActionGroup) {
continue;
}
if (id.startsWith(QuickList.QUICK_LIST_PREFIX) || addedActions.containsId(id) || result.contains(id)) {
continue;
}
if (keymapManager.getBoundActions().contains(id)) continue;
result.add(id);
}
filterOtherActionsGroup(result);
ContainerUtil.quickSort(result, new Comparator<String>() {
public int compare(String id1, String id2) {
return getTextToCompare(id1).compareToIgnoreCase(getTextToCompare(id2));
}
});
Group group = new Group(KeyMapBundle.message("other.group.title"), AllIcons.Nodes.KeymapOther);
for (String id : result) {
if (filtered == null || filtered.value(actionManager.getActionOrStub(id))) group.addActionId(id);
}
return group;
}
private static String getTextToCompare(String id) {
AnAction action = ActionManager.getInstance().getActionOrStub(id);
if (action == null) {
return id;
}
String text = action.getTemplatePresentation().getText();
return text != null ? text : id;
}
private static void filterOtherActionsGroup(ArrayList<String> actions) {
filterOutGroup(actions, IdeActions.GROUP_GENERATE);
filterOutGroup(actions, IdeActions.GROUP_NEW);
filterOutGroup(actions, IdeActions.GROUP_CHANGE_SCHEME);
}
private static void filterOutGroup(ArrayList<String> actions, String groupId) {
if (groupId == null) {
throw new IllegalArgumentException();
}
ActionManager actionManager = ActionManager.getInstance();
AnAction action = actionManager.getActionOrStub(groupId);
if (action instanceof DefaultActionGroup) {
DefaultActionGroup group = (DefaultActionGroup)action;
AnAction[] children = group.getChildActionsOrStubs();
for (AnAction child : children) {
String childId = child instanceof ActionStub ? ((ActionStub)child).getId() : actionManager.getId(child);
if (childId == null) {
// SCR 35149
continue;
}
if (child instanceof DefaultActionGroup) {
filterOutGroup(actions, childId);
}
else {
actions.remove(childId);
}
}
}
}
public static DefaultMutableTreeNode createNode(Group group) {
DefaultMutableTreeNode node = new DefaultMutableTreeNode(group);
for (Object child : group.getChildren()) {
if (child instanceof Group) {
DefaultMutableTreeNode childNode = createNode((Group)child);
node.add(childNode);
}
else {
node.add(new DefaultMutableTreeNode(child));
}
}
return node;
}
public static Group createMainGroup(final Project project, final Keymap keymap, final QuickList[] quickLists) {
return createMainGroup(project, keymap, quickLists, null, false, null);
}
public static Group createMainGroup(final Project project,
final Keymap keymap,
final QuickList[] quickLists,
final String filter,
final boolean forceFiltering,
final Condition<AnAction> filtered) {
final Condition<AnAction> wrappedFilter = wrapFilter(filtered, keymap, ActionManager.getInstance());
Group mainGroup = new Group(KeyMapBundle.message("all.actions.group.title"), null, null);
mainGroup.addGroup(createEditorActionsGroup(wrappedFilter));
mainGroup.addGroup(createMainMenuGroup(wrappedFilter));
for (KeymapExtension extension : Extensions.getExtensions(KeymapExtension.EXTENSION_POINT_NAME)) {
final Group group = createExtensionGroup(wrappedFilter, project, extension);
if (group != null) {
mainGroup.addGroup(group);
}
}
mainGroup.addGroup(createMacrosGroup(wrappedFilter));
mainGroup.addGroup(createQuickListsGroup(wrappedFilter, filter, forceFiltering, quickLists));
mainGroup.addGroup(createPluginsActionsGroup(wrappedFilter));
mainGroup.addGroup(createOtherGroup(wrappedFilter, mainGroup, keymap));
if (!StringUtil.isEmpty(filter) || filtered != null) {
final ArrayList list = mainGroup.getChildren();
for (Iterator i = list.iterator(); i.hasNext();) {
final Object o = i.next();
if (o instanceof Group) {
final Group group = (Group)o;
if (group.getSize() == 0) {
if (!SearchUtil.isComponentHighlighted(group.getName(), filter, forceFiltering, null)) {
i.remove();
}
}
}
}
}
return mainGroup;
}
public static Condition<AnAction> isActionFiltered(final String filter, final boolean force) {
return new Condition<AnAction>() {
public boolean value(final AnAction action) {
if (filter == null) return true;
if (action == null) return false;
final String insensitiveFilter = filter.toLowerCase();
for (String text : new String[]{action.getTemplatePresentation().getText(),
action.getTemplatePresentation().getDescription(),
action instanceof ActionStub ? ((ActionStub)action).getId() : ActionManager.getInstance().getId(action)}) {
if (text != null) {
final String lowerText = text.toLowerCase();
if (SearchUtil.isComponentHighlighted(lowerText, insensitiveFilter, force, null)) {
return true;
}
else if (lowerText.contains(insensitiveFilter)) {
return true;
}
}
}
return false;
}
};
}
public static Condition<AnAction> isActionFiltered(final ActionManager actionManager,
final Keymap keymap,
final KeyboardShortcut keyboardShortcut) {
return new Condition<AnAction>() {
public boolean value(final AnAction action) {
if (keyboardShortcut == null) return true;
if (action == null) return false;
final Shortcut[] actionShortcuts =
keymap.getShortcuts(action instanceof ActionStub ? ((ActionStub)action).getId() : actionManager.getId(action));
for (Shortcut shortcut : actionShortcuts) {
if (shortcut instanceof KeyboardShortcut) {
final KeyboardShortcut keyboardActionShortcut = (KeyboardShortcut)shortcut;
if (Comparing.equal(keyboardActionShortcut, keyboardShortcut)) {
return true;
}
}
}
return false;
}
};
}
}