diff --git a/platform/platform-api/src/com/intellij/openapi/actionSystem/DefaultActionGroup.java b/platform/platform-api/src/com/intellij/openapi/actionSystem/DefaultActionGroup.java index 4077dffb9c83..481c07c555d3 100644 --- a/platform/platform-api/src/com/intellij/openapi/actionSystem/DefaultActionGroup.java +++ b/platform/platform-api/src/com/intellij/openapi/actionSystem/DefaultActionGroup.java @@ -85,15 +85,15 @@ public class DefaultActionGroup extends ActionGroup { * @param actionManager ActionManager instance */ public final void add(@NotNull AnAction action, @NotNull ActionManager actionManager) { - add(action, new Constraints(Anchor.LAST, null), actionManager); + add(action, Constraints.LAST, actionManager); } public final void add(@NotNull AnAction action) { - addAction(action, new Constraints(Anchor.LAST, null)); + addAction(action, Constraints.LAST); } public final ActionInGroup addAction(@NotNull AnAction action) { - return addAction(action, new Constraints(Anchor.LAST, null)); + return addAction(action, Constraints.LAST); } /** @@ -236,6 +236,29 @@ public class DefaultActionGroup extends ActionGroup { myPairs.clear(); } + + /** + * Replaces specified action with the new one. + */ + public boolean replaceAction(AnAction oldAction, AnAction newAction) { + int index = mySortedChildren.indexOf(oldAction); + if (index >= 0) { + mySortedChildren.set(index, newAction); + return true; + } + else { + for (int i = 0; i < myPairs.size(); i++) { + Pair pair = myPairs.get(i); + if (pair.first.equals(newAction)) { + myPairs.set(i, Pair.create(newAction, pair.second)); + return true; + } + } + } + return false; + } + + /** * Returns group's children in the order determined by constraints. * diff --git a/platform/platform-api/src/com/intellij/openapi/actionSystem/EmptyAction.java b/platform/platform-api/src/com/intellij/openapi/actionSystem/EmptyAction.java index afc60805744e..684ea221b789 100644 --- a/platform/platform-api/src/com/intellij/openapi/actionSystem/EmptyAction.java +++ b/platform/platform-api/src/com/intellij/openapi/actionSystem/EmptyAction.java @@ -111,4 +111,11 @@ public class EmptyAction extends AnAction { } }; } + + public static class Group extends DefaultActionGroup { + public Group() { + super(); + getTemplatePresentation().setEnabledAndVisible(false); + } + } } diff --git a/platform/platform-api/src/com/intellij/openapi/actionSystem/ex/ActionManagerEx.java b/platform/platform-api/src/com/intellij/openapi/actionSystem/ex/ActionManagerEx.java index e1f198dd8f0f..757b0677a52c 100644 --- a/platform/platform-api/src/com/intellij/openapi/actionSystem/ex/ActionManagerEx.java +++ b/platform/platform-api/src/com/intellij/openapi/actionSystem/ex/ActionManagerEx.java @@ -67,45 +67,24 @@ public abstract class ActionManagerEx extends ActionManager { * * @return null if string cannot be parsed. */ - @Nullable - public static KeyStroke getKeyStroke(String s) { - KeyStroke result = null; - try { - result = KeyStroke.getKeyStroke(s); - } catch (Exception ex) { - //ok - } - - if (result == null && s != null && s.length() >= 2 && s.charAt(s.length() - 2) == ' ') { - try { - String s1 = s.substring(0, s.length() - 1) + Character.toUpperCase(s.charAt(s.length() - 1)); - result = KeyStroke.getKeyStroke(s1); - } - catch (Exception ex) { - - // ok - + catch (Exception ignored) { } - } - - return result; - } diff --git a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionManagerImpl.java index 77c5de0f55b5..923f788c1025 100644 --- a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionManagerImpl.java @@ -22,6 +22,7 @@ import com.intellij.ide.ActivityTracker; import com.intellij.ide.DataManager; import com.intellij.ide.plugins.IdeaPluginDescriptor; import com.intellij.ide.plugins.PluginManager; +import com.intellij.ide.plugins.PluginManagerCore; import com.intellij.idea.IdeaLogger; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.*; @@ -47,7 +48,9 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.openapi.wm.IdeFrame; import com.intellij.util.ArrayUtil; +import com.intellij.util.ObjectUtils; import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.MultiMap; import com.intellij.util.messages.MessageBusConnection; import com.intellij.util.ui.UIUtil; import gnu.trove.THashMap; @@ -78,6 +81,7 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat private final Map> myPlugin2Id = new THashMap>(); private final TObjectIntHashMap myId2Index = new TObjectIntHashMap(); private final Map myAction2Id = new THashMap(); + private final MultiMap myId2GroupId = new MultiMap(); private final List myNotRegisteredInternalActionIds = new ArrayList(); private MyTimer myTimer; @@ -88,6 +92,7 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat private final DataManager myDataManager; private String myPrevPerformedActionId; private long myLastTimeEditorWasTypedIn = 0; + @NonNls public static final String ACTION_ELEMENT_NAME = "action"; @NonNls public static final String GROUP_ELEMENT_NAME = "group"; @NonNls public static final String ACTIONS_ELEMENT_NAME = "actions"; @@ -123,6 +128,7 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat @NonNls public static final String VALUE_ATTR_NAME = "value"; @NonNls public static final String ACTIONS_BUNDLE = "messages.ActionsBundle"; @NonNls public static final String USE_SHORTCUT_OF_ATTR_NAME = "use-shortcut-of"; + @NonNls public static final String OVERRIDES_ATTR_NAME = "overrides"; private final List myPopups = new ArrayList(); @@ -208,9 +214,9 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat private void registerPluginActions() { - final IdeaPluginDescriptor[] plugins = PluginManager.getPlugins(); + final IdeaPluginDescriptor[] plugins = PluginManagerCore.getPlugins(); for (IdeaPluginDescriptor plugin : plugins) { - if (PluginManager.shouldSkipPlugin(plugin)) continue; + if (PluginManagerCore.shouldSkipPlugin(plugin)) continue; final List elementList = plugin.getActionsDescriptionElements(); if (elementList != null) { for (Element e : elementList) { @@ -412,11 +418,24 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat ((KeymapManagerEx)myKeymapManager).bindShortcuts(element.getAttributeValue(USE_SHORTCUT_OF_ATTR_NAME), id); } - // register action - registerAction(id, stub, pluginId); + registerOrReplaceActionInner(element, id, stub, pluginId); return stub; } + private void registerOrReplaceActionInner(@NotNull Element element, @NotNull String id, @NotNull AnAction action, @Nullable PluginId pluginId) { + synchronized (myLock) { + if (Boolean.valueOf(element.getAttributeValue(OVERRIDES_ATTR_NAME)).booleanValue()) { + if (getActionOrStub(id) == null) { + throw new RuntimeException(element.getName() + " '" + id + "' doesn't override anything"); + } + replaceAction(id, action, pluginId); + } + else { + registerAction(id, action, pluginId); + } + } + } + private static void processAbbreviationNode(Element e, String id) { final String abbr = e.getAttributeValue(VALUE_ATTR_NAME); if (!StringUtil.isEmpty(abbr)) { @@ -427,7 +446,8 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat @Nullable private static ResourceBundle getActionsResourceBundle(ClassLoader loader, IdeaPluginDescriptor plugin) { - @NonNls final String resBundleName = plugin != null && !plugin.getPluginId().getIdString().equals("com.intellij") ? plugin.getResourceBundleBaseName() : ACTIONS_BUNDLE; + @NonNls final String resBundleName = plugin != null && !"com.intellij".equals(plugin.getPluginId().getIdString()) + ? plugin.getResourceBundleBaseName() : ACTIONS_BUNDLE; ResourceBundle bundle = null; if (resBundleName != null) { bundle = AbstractBundle.getResourceBundle(resBundleName, loader); @@ -560,7 +580,7 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat } if (id != null) { - registerAction(id, group); + registerOrReplaceActionInner(element, id, group, pluginId); } Presentation presentation = group.getTemplatePresentation(); @@ -593,7 +613,7 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat AnAction action = processActionElement(child, loader, pluginId); if (action != null) { assertActionIsGroupOrStub(action); - ((DefaultActionGroup)group).addAction(action, Constraints.LAST, this).setAsSecondary(isSecondary(child)); + addToGroupInner(group, action, Constraints.LAST, isSecondary(child)); } } else if (SEPARATOR_ELEMENT_NAME.equals(name)) { @@ -602,7 +622,7 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat else if (GROUP_ELEMENT_NAME.equals(name)) { AnAction action = processGroupElement(child, loader, pluginId); if (action != null) { - ((DefaultActionGroup)group).add(action, this); + addToGroupInner(group, action, Constraints.LAST, false); } } else if (ADD_TO_GROUP_ELEMENT_NAME.equals(name)) { @@ -611,7 +631,7 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat else if (REFERENCE_ELEMENT_NAME.equals(name)) { AnAction action = processReferenceElement(child, pluginId); if (action != null) { - ((DefaultActionGroup)group).addAction(action, Constraints.LAST, this).setAsSecondary(isSecondary(child)); + addToGroupInner(group, action, Constraints.LAST, isSecondary(child)); } } else { @@ -679,8 +699,7 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat } // anchor attribute - final Anchor anchor = parseAnchor(element.getAttributeValue(ANCHOR_ELEMENT_NAME), - actionName, pluginId); + final Anchor anchor = parseAnchor(element.getAttributeValue(ANCHOR_ELEMENT_NAME), actionName, pluginId); if (anchor == null) { return; } @@ -689,8 +708,12 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat if (!checkRelativeToAction(relativeToActionId, anchor, actionName, pluginId)) { return; } - final DefaultActionGroup group = (DefaultActionGroup)parentGroup; - group.addAction(action, new Constraints(anchor, relativeToActionId), this).setAsSecondary(secondary); + addToGroupInner(parentGroup, action, new Constraints(anchor, relativeToActionId), secondary); + } + + private void addToGroupInner(AnAction group, AnAction action, Constraints constraints, boolean secondary) { + ((DefaultActionGroup)group).addAction(action, constraints, this).setAsSecondary(secondary); + myId2GroupId.putValue(myAction2Id.get(action), myAction2Id.get(group)); } public static boolean checkRelativeToAction(final String relativeToActionId, @@ -916,7 +939,7 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat public void registerAction(@NotNull String actionId, @NotNull AnAction action, @Nullable PluginId pluginId) { synchronized (myLock) { if (myId2Action.containsKey(actionId)) { - reportActionError(pluginId, "action with the ID \"" + actionId + "\" was already registered. Action being registered is " + action.toString() + + reportActionError(pluginId, "action with the ID \"" + actionId + "\" was already registered. Action being registered is " + action + "; Registered action is " + myId2Action.get(actionId) + getPluginInfo(pluginId)); return; @@ -1047,6 +1070,33 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat return myTransparentOnlyUpdate; } + //@Override + //public AnAction replaceAction(String actionId, @NotNull AnAction newAction) { + // synchronized (myLock) { + // return replaceAction(actionId, newAction, null); + // } + //} + + private AnAction replaceAction(@NotNull String actionId, @NotNull AnAction newAction, @Nullable PluginId pluginId) { + AnAction oldAction = getActionOrStub(actionId); + if (oldAction != null) { + boolean isGroup = oldAction instanceof ActionGroup; + if (isGroup != newAction instanceof ActionGroup) { + throw new IllegalStateException("cannot replace a group with an action and vice versa: " + actionId); + } + unregisterAction(actionId); + if (isGroup) { + myId2GroupId.values().remove(actionId); + } + } + registerAction(actionId, newAction, pluginId); + for (String groupId : myId2GroupId.get(actionId)) { + DefaultActionGroup group = ObjectUtils.assertNotNull((DefaultActionGroup)getActionOrStub(groupId)); + group.replaceAction(oldAction, newAction); + } + return oldAction; + } + private void flushActionPerformed() { final Set actions = myQueuedNotifications.keySet(); for (final AnAction eachAction : actions) { diff --git a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/config/ActionBean.java b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/config/ActionBean.java index 82afa01e0d70..01aa0ff4c49e 100644 --- a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/config/ActionBean.java +++ b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/config/ActionBean.java @@ -50,9 +50,12 @@ public class ActionBean { @Attribute(ActionManagerImpl.INTERNAL_ATTR_NAME) public boolean internal; - @Attribute("use-shortcut-of") + @Attribute(ActionManagerImpl.USE_SHORTCUT_OF_ATTR_NAME) public boolean useShortcutOf; @Attribute(ActionManagerImpl.KEYMAP_ATTR_NAME) public String keymap; + + @Attribute(ActionManagerImpl.OVERRIDES_ATTR_NAME) + public boolean overrides; } diff --git a/plugins/devkit/src/dom/Action.java b/plugins/devkit/src/dom/Action.java index be3d595990ac..14c0e946c932 100644 --- a/plugins/devkit/src/dom/Action.java +++ b/plugins/devkit/src/dom/Action.java @@ -90,6 +90,13 @@ public interface Action extends DomElement { @Required GenericAttributeValue getId(); + /** + * Returns the value of the overrides child. + * Attribute overrides + * @return the value of the overrides child. + */ + @NotNull + GenericAttributeValue getOverrides(); /** * Returns the list of keyboard-shortcut children. diff --git a/plugins/devkit/src/dom/Group.java b/plugins/devkit/src/dom/Group.java index c35c31fa4edc..0eace6698b90 100644 --- a/plugins/devkit/src/dom/Group.java +++ b/plugins/devkit/src/dom/Group.java @@ -87,10 +87,22 @@ public interface Group extends Actions { @NotNull GenericAttributeValue getText(); - + /** + * Returns the value of the id child. + * Attribute id + * @return the value of the id child. + */ @NotNull GenericAttributeValue getId(); + /** + * Returns the value of the overrides child. + * Attribute overrides + * @return the value of the overrides child. + */ + @NotNull + GenericAttributeValue getOverrides(); + /** * Returns the list of reference children. * @return the list of reference children.