diff --git a/platform/editor-ui-api/src/com/intellij/openapi/actionSystem/ActionManager.java b/platform/editor-ui-api/src/com/intellij/openapi/actionSystem/ActionManager.java index 5577664bf9f7..ee850c3fe060 100644 --- a/platform/editor-ui-api/src/com/intellij/openapi/actionSystem/ActionManager.java +++ b/platform/editor-ui-api/src/com/intellij/openapi/actionSystem/ActionManager.java @@ -20,6 +20,7 @@ import com.intellij.openapi.actionSystem.ex.AnActionListener; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.ApplicationComponent; import com.intellij.openapi.extensions.PluginId; +import com.intellij.openapi.project.ProjectType; import com.intellij.openapi.util.ActionCallback; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -80,7 +81,7 @@ public abstract class ActionManager implements ApplicationComponent { * @param actionId Id of the registered action * * @return Action associated with the specified actionId, null if - * there is no actions associated with the speicified actionId + * there is no actions associated with the specified actionId * * @exception java.lang.IllegalArgumentException if actionId is null * @@ -88,6 +89,20 @@ public abstract class ActionManager implements ApplicationComponent { */ public abstract AnAction getAction(@NonNls @NotNull String actionId); + /** + * Returns action associated with the specified actionId. + * + * @param actionId Id of the registered action + * + * @return Action associated with the specified actionId, null if + * there is no actions associated with the specified actionId + * + * @exception java.lang.IllegalArgumentException if actionId is null + * + * @see com.intellij.openapi.actionSystem.IdeActions + */ + public abstract AnAction getAction(@NonNls @NotNull String actionId, @Nullable ProjectType projectType); + /** * Returns actionId associated with the specified action. * diff --git a/platform/extensions/src/com/intellij/openapi/extensions/Extensions.java b/platform/extensions/src/com/intellij/openapi/extensions/Extensions.java index 1c0152514e33..60d73084e988 100644 --- a/platform/extensions/src/com/intellij/openapi/extensions/Extensions.java +++ b/platform/extensions/src/com/intellij/openapi/extensions/Extensions.java @@ -28,15 +28,16 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; public class Extensions { - private static LogProvider ourLogger = new SimpleLogProvider(); - public static final ExtensionPointName AREA_LISTENER_EXTENSION_POINT = new ExtensionPointName("com.intellij.arealistener"); - + private static LogProvider ourLogger = new SimpleLogProvider(); private static Map ourAreaInstance2area = new THashMap(); private static Map ourAreaClass2Configuration = new THashMap(); @NotNull private static ExtensionsAreaImpl ourRootArea = createRootArea(); + private Extensions() { + } + @NotNull private static ExtensionsAreaImpl createRootArea() { ExtensionsAreaImpl rootArea = new ExtensionsAreaImpl(null, null, null, ourLogger); @@ -44,9 +45,6 @@ public class Extensions { return rootArea; } - private Extensions() { - } - public static void setSynchronized() { assert ourAreaInstance2area.isEmpty(); assert ourAreaClass2Configuration.isEmpty(); @@ -101,7 +99,7 @@ public class Extensions { @NotNull @SuppressWarnings({"unchecked"}) public static T[] getExtensions(@NotNull ExtensionPointName extensionPointName, AreaInstance areaInstance) { - return Extensions.getExtensions(extensionPointName.getName(), areaInstance); + return getExtensions(extensionPointName.getName(), areaInstance); } @NotNull diff --git a/platform/platform-api/src/com/intellij/openapi/actionSystem/ActionStub.java b/platform/platform-api/src/com/intellij/openapi/actionSystem/ActionStub.java index 09896aaf0da8..c96ce2aa3371 100644 --- a/platform/platform-api/src/com/intellij/openapi/actionSystem/ActionStub.java +++ b/platform/platform-api/src/com/intellij/openapi/actionSystem/ActionStub.java @@ -29,6 +29,7 @@ public class ActionStub extends AnAction{ private static final Logger LOG=Logger.getInstance("#com.intellij.openapi.actionSystem.ActionStub"); private final String myClassName; + private final String myProjectType; private final String myId; private final String myText; private final ClassLoader myLoader; @@ -40,9 +41,10 @@ public class ActionStub extends AnAction{ @NotNull String text, ClassLoader loader, PluginId pluginId, - String iconPath) { + String iconPath, String projectType) { myLoader = loader; myClassName=actionClass; + myProjectType = projectType; LOG.assertTrue(!id.isEmpty()); myId=id; myText=text; @@ -102,4 +104,7 @@ public class ActionStub extends AnAction{ targetAction.setShortcutSet(getShortcutSet()); } + public String getProjectType() { + return myProjectType; + } } 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 723956ef9daa..c5a9b01c4d21 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 @@ -39,6 +39,7 @@ import com.intellij.openapi.keymap.KeymapManager; import com.intellij.openapi.keymap.KeymapUtil; import com.intellij.openapi.keymap.ex.KeymapManagerEx; import com.intellij.openapi.progress.ProcessCanceledException; +import com.intellij.openapi.project.ProjectType; import com.intellij.openapi.util.ActionCallback; import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.Disposer; @@ -71,28 +72,6 @@ import java.util.*; import java.util.List; public final class ActionManagerImpl extends ActionManagerEx implements ApplicationComponent { - private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.actionSystem.impl.ActionManagerImpl"); - private static final int DEACTIVATED_TIMER_DELAY = 5000; - private static final int TIMER_DELAY = 500; - private static final int UPDATE_DELAY_AFTER_TYPING = 500; - - private final Object myLock = new Object(); - private final Map myId2Action = new THashMap(); - 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; - - private int myRegisteredActionsCount; - private final List myActionListeners = ContainerUtil.createLockFreeCopyOnWriteList(); - private String myLastPreformedActionId; - private final KeymapManager myKeymapManager; - 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"; @@ -130,14 +109,32 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat @NonNls public static final String USE_SHORTCUT_OF_ATTR_NAME = "use-shortcut-of"; @NonNls public static final String OVERRIDES_ATTR_NAME = "overrides"; @NonNls public static final String KEEP_CONTENT_ATTR_NAME = "keep-content"; - + @NonNls public static final String PROJECT_TYPE = "project-type"; + private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.actionSystem.impl.ActionManagerImpl"); + private static final int DEACTIVATED_TIMER_DELAY = 5000; + private static final int TIMER_DELAY = 500; + private static final int UPDATE_DELAY_AFTER_TYPING = 500; + private final Object myLock = new Object(); + private final Map myId2Action = new THashMap(); + 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 final List myActionListeners = ContainerUtil.createLockFreeCopyOnWriteList(); + private final KeymapManager myKeymapManager; + private final DataManager myDataManager; private final List myPopups = new ArrayList(); - private final Map myQueuedNotifications = new LinkedHashMap(); private final Map myQueuedNotificationsEvents = new LinkedHashMap(); - + private MyTimer myTimer; + private int myRegisteredActionsCount; + private String myLastPreformedActionId; + private String myPrevPerformedActionId; + private long myLastTimeEditorWasTypedIn = 0; private Runnable myPreloadActionsRunnable; private boolean myTransparentOnlyUpdate; + private int myActionsPreloaded = 0; ActionManagerImpl(KeymapManager keymapManager, DataManager dataManager) { myKeymapManager = keymapManager; @@ -146,116 +143,7 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat registerPluginActions(); } - @Override - public void initComponent() {} - - @Override - public void disposeComponent() { - if (myTimer != null) { - myTimer.stop(); - myTimer = null; - } - } - - @Override - public void addTimerListener(int delay, final TimerListener listener) { - _addTimerListener(listener, false); - } - - @Override - public void removeTimerListener(TimerListener listener) { - _removeTimerListener(listener, false); - } - - @Override - public void addTransparentTimerListener(int delay, TimerListener listener) { - _addTimerListener(listener, true); - } - - @Override - public void removeTransparentTimerListener(TimerListener listener) { - _removeTimerListener(listener, true); - } - - private void _addTimerListener(final TimerListener listener, boolean transparent) { - if (ApplicationManager.getApplication().isUnitTestMode()) return; - if (myTimer == null) { - myTimer = new MyTimer(); - myTimer.start(); - } - - myTimer.addTimerListener(listener, transparent); - } - - private void _removeTimerListener(TimerListener listener, boolean transparent) { - if (ApplicationManager.getApplication().isUnitTestMode()) return; - LOG.assertTrue(myTimer != null); - - myTimer.removeTimerListener(listener, transparent); - } - - public ActionPopupMenu createActionPopupMenu(String place, @NotNull ActionGroup group, @Nullable PresentationFactory presentationFactory) { - return new ActionPopupMenuImpl(place, group, this, presentationFactory); - } - - @Override - public ActionPopupMenu createActionPopupMenu(String place, @NotNull ActionGroup group) { - return new ActionPopupMenuImpl(place, group, this, null); - } - - @Override - public ActionToolbar createActionToolbar(final String place, @NotNull final ActionGroup group, final boolean horizontal) { - return createActionToolbar(place, group, horizontal, false); - } - - @Override - public ActionToolbar createActionToolbar(final String place, @NotNull final ActionGroup group, final boolean horizontal, final boolean decorateButtons) { - return new ActionToolbarImpl(place, group, horizontal, decorateButtons, myDataManager, this, (KeymapManagerEx)myKeymapManager); - } - - - private void registerPluginActions() { - final IdeaPluginDescriptor[] plugins = PluginManagerCore.getPlugins(); - for (IdeaPluginDescriptor plugin : plugins) { - if (PluginManagerCore.shouldSkipPlugin(plugin)) continue; - final List elementList = plugin.getActionsDescriptionElements(); - if (elementList != null) { - for (Element e : elementList) { - processActionsChildElement(plugin.getPluginClassLoader(), plugin.getPluginId(), e); - } - } - } - } - - @Override - public AnAction getAction(@NotNull String id) { - return getActionImpl(id, false); - } - - private AnAction getActionImpl(String id, boolean canReturnStub) { - synchronized (myLock) { - AnAction action = (AnAction)myId2Action.get(id); - if (!canReturnStub && action instanceof ActionStub) { - action = convert((ActionStub)action); - } - return action; - } - } - - /** - * Converts action's stub to normal action. - */ - @NotNull - private AnAction convert(@NotNull ActionStub stub) { - LOG.assertTrue(myAction2Id.containsKey(stub)); - myAction2Id.remove(stub); - - LOG.assertTrue(myId2Action.containsKey(stub.getId())); - - AnAction action = (AnAction)myId2Action.remove(stub.getId()); - LOG.assertTrue(action != null); - LOG.assertTrue(action.equals(stub)); - + static AnAction convertStub(ActionStub stub) { Object obj; String className = stub.getClassName(); try { @@ -304,143 +192,9 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat Class actionClass = anAction.getClass(); setIconFromClass(actionClass, actionClass.getClassLoader(), iconPath, anAction.getTemplatePresentation(), stub.getPluginId()); } - - myId2Action.put(stub.getId(), obj); - myAction2Id.put(obj, stub.getId()); - return anAction; } - @Override - public String getId(@NotNull AnAction action) { - LOG.assertTrue(!(action instanceof ActionStub)); - synchronized (myLock) { - return myAction2Id.get(action); - } - } - - @Override - public String[] getActionIds(@NotNull String idPrefix) { - synchronized (myLock) { - ArrayList idList = new ArrayList(); - for (String id : myId2Action.keySet()) { - if (id.startsWith(idPrefix)) { - idList.add(id); - } - } - return ArrayUtil.toStringArray(idList); - } - } - - @Override - public boolean isGroup(@NotNull String actionId) { - return getActionImpl(actionId, true) instanceof ActionGroup; - } - - @Override - public JComponent createButtonToolbar(final String actionPlace, @NotNull final ActionGroup messageActionGroup) { - return new ButtonToolbarImpl(actionPlace, messageActionGroup, myDataManager, this); - } - - @Override - public AnAction getActionOrStub(String id) { - return getActionImpl(id, true); - } - - /** - * @return instance of ActionGroup or ActionStub. The method never returns real subclasses - * of AnAction. - */ - @Nullable - private AnAction processActionElement(Element element, final ClassLoader loader, PluginId pluginId) { - final IdeaPluginDescriptor plugin = PluginManager.getPlugin(pluginId); - ResourceBundle bundle = getActionsResourceBundle(loader, plugin); - - if (!ACTION_ELEMENT_NAME.equals(element.getName())) { - reportActionError(pluginId, "unexpected name of element \"" + element.getName() + "\""); - return null; - } - String className = element.getAttributeValue(CLASS_ATTR_NAME); - if (className == null || className.isEmpty()) { - reportActionError(pluginId, "action element should have specified \"class\" attribute"); - return null; - } - // read ID and register loaded action - String id = element.getAttributeValue(ID_ATTR_NAME); - if (id == null || id.isEmpty()) { - id = StringUtil.getShortName(className); - } - if (Boolean.valueOf(element.getAttributeValue(INTERNAL_ATTR_NAME)).booleanValue() && !ApplicationManagerEx.getApplicationEx().isInternal()) { - myNotRegisteredInternalActionIds.add(id); - return null; - } - - String text = loadTextForElement(element, bundle, id, ACTION_ELEMENT_NAME); - - String iconPath = element.getAttributeValue(ICON_ATTR_NAME); - - if (text == null) { - @NonNls String message = "'text' attribute is mandatory (action ID=" + id + ";" + - (plugin == null ? "" : " plugin path: "+plugin.getPath()) + ")"; - reportActionError(pluginId, message); - return null; - } - - ActionStub stub = new ActionStub(className, id, text, loader, pluginId, iconPath); - Presentation presentation = stub.getTemplatePresentation(); - presentation.setText(text); - - // description - - presentation.setDescription(loadDescriptionForElement(element, bundle, id, ACTION_ELEMENT_NAME)); - - // process all links and key bindings if any - for (final Object o : element.getChildren()) { - Element e = (Element)o; - if (ADD_TO_GROUP_ELEMENT_NAME.equals(e.getName())) { - processAddToGroupNode(stub, e, pluginId, isSecondary(e)); - } - else if (SHORTCUT_ELEMENT_NAME.equals(e.getName())) { - processKeyboardShortcutNode(e, id, pluginId); - } - else if (MOUSE_SHORTCUT_ELEMENT_NAME.equals(e.getName())) { - processMouseShortcutNode(e, id, pluginId); - } - else if (ABBREVIATION_ELEMENT_NAME.equals(e.getName())) { - processAbbreviationNode(e, id); - } - else { - reportActionError(pluginId, "unexpected name of element \"" + e.getName() + "\""); - return null; - } - } - if (element.getAttributeValue(USE_SHORTCUT_OF_ATTR_NAME) != null) { - ((KeymapManagerEx)myKeymapManager).bindShortcuts(element.getAttributeValue(USE_SHORTCUT_OF_ATTR_NAME), id); - } - - 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))) { - if (getActionOrStub(id) == null) { - throw new RuntimeException(element.getName() + " '" + id + "' doesn't override anything"); - } - AnAction prev = replaceAction(id, action, pluginId); - if (action instanceof DefaultActionGroup && prev instanceof DefaultActionGroup) { - if (Boolean.valueOf(element.getAttributeValue(KEEP_CONTENT_ATTR_NAME))) { - ((DefaultActionGroup)action).copyFromGroup((DefaultActionGroup)prev); - } - } - } - 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)) { @@ -534,6 +288,376 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat return CommonBundle.messageOrDefault(bundle, elementType + "." + id + "." + TEXT_ATTR_NAME, value == null ? "" : value); } + public static boolean checkRelativeToAction(final String relativeToActionId, + @NotNull final Anchor anchor, + @NotNull final String actionName, + @Nullable final PluginId pluginId) { + if ((Anchor.BEFORE == anchor || Anchor.AFTER == anchor) && relativeToActionId == null) { + reportActionError(pluginId, actionName + ": \"relative-to-action\" cannot be null if anchor is \"after\" or \"before\""); + return false; + } + return true; + } + + @Nullable + public static Anchor parseAnchor(final String anchorStr, + @Nullable final String actionName, + @Nullable final PluginId pluginId) { + if (anchorStr == null) { + return Anchor.LAST; + } + + if (FIRST.equalsIgnoreCase(anchorStr)) { + return Anchor.FIRST; + } + else if (LAST.equalsIgnoreCase(anchorStr)) { + return Anchor.LAST; + } + else if (BEFORE.equalsIgnoreCase(anchorStr)) { + return Anchor.BEFORE; + } + else if (AFTER.equalsIgnoreCase(anchorStr)) { + return Anchor.AFTER; + } + else { + reportActionError(pluginId, actionName + ": anchor should be one of the following constants: \"first\", \"last\", \"before\" or \"after\""); + return null; + } + } + + private static void processMouseShortcutNode(Element element, String actionId, PluginId pluginId) { + String keystrokeString = element.getAttributeValue(KEYSTROKE_ATTR_NAME); + if (keystrokeString == null || keystrokeString.trim().isEmpty()) { + reportActionError(pluginId, "\"keystroke\" attribute must be specified for action with id=" + actionId); + return; + } + MouseShortcut shortcut; + try { + shortcut = KeymapUtil.parseMouseShortcut(keystrokeString); + } + catch (Exception ex) { + reportActionError(pluginId, "\"keystroke\" attribute has invalid value for action with id=" + actionId); + return; + } + + String keymapName = element.getAttributeValue(KEYMAP_ATTR_NAME); + if (keymapName == null || keymapName.isEmpty()) { + reportActionError(pluginId, "attribute \"keymap\" should be defined"); + return; + } + Keymap keymap = KeymapManager.getInstance().getKeymap(keymapName); + if (keymap == null) { + reportActionError(pluginId, "keymap \"" + keymapName + "\" not found"); + return; + } + + final String removeOption = element.getAttributeValue(REMOVE_SHORTCUT_ATTR_NAME); + if (Boolean.valueOf(removeOption)) { + keymap.removeShortcut(actionId, shortcut); + } else { + keymap.addShortcut(actionId, shortcut); + } + } + + private static void assertActionIsGroupOrStub(final AnAction action) { + if (!(action instanceof ActionGroup || action instanceof ActionStub)) { + LOG.error("Action : " + action + "; class: " + action.getClass()); + } + } + + private static void reportActionError(final PluginId pluginId, @NonNls @NotNull String message) { + if (pluginId == null) { + LOG.error(message); + } + else { + LOG.error(new PluginException(message, null, pluginId)); + } + } + + @NonNls + private static String getPluginInfo(@Nullable PluginId id) { + if (id != null) { + final IdeaPluginDescriptor plugin = PluginManager.getPlugin(id); + if (plugin != null) { + String name = plugin.getName(); + if (name == null) { + name = id.getIdString(); + } + return " Plugin: " + name; + } + } + return ""; + } + + private static DataContext getContextBy(Component contextComponent) { + final DataManager dataManager = DataManager.getInstance(); + return contextComponent != null ? dataManager.getDataContext(contextComponent) : dataManager.getDataContext(); + } + + @Override + public void initComponent() {} + + @Override + public void disposeComponent() { + if (myTimer != null) { + myTimer.stop(); + myTimer = null; + } + } + + @Override + public void addTimerListener(int delay, final TimerListener listener) { + _addTimerListener(listener, false); + } + + @Override + public void removeTimerListener(TimerListener listener) { + _removeTimerListener(listener, false); + } + + @Override + public void addTransparentTimerListener(int delay, TimerListener listener) { + _addTimerListener(listener, true); + } + + @Override + public void removeTransparentTimerListener(TimerListener listener) { + _removeTimerListener(listener, true); + } + + private void _addTimerListener(final TimerListener listener, boolean transparent) { + if (ApplicationManager.getApplication().isUnitTestMode()) return; + if (myTimer == null) { + myTimer = new MyTimer(); + myTimer.start(); + } + + myTimer.addTimerListener(listener, transparent); + } + + private void _removeTimerListener(TimerListener listener, boolean transparent) { + if (ApplicationManager.getApplication().isUnitTestMode()) return; + LOG.assertTrue(myTimer != null); + + myTimer.removeTimerListener(listener, transparent); + } + + public ActionPopupMenu createActionPopupMenu(String place, @NotNull ActionGroup group, @Nullable PresentationFactory presentationFactory) { + return new ActionPopupMenuImpl(place, group, this, presentationFactory); + } + + @Override + public ActionPopupMenu createActionPopupMenu(String place, @NotNull ActionGroup group) { + return new ActionPopupMenuImpl(place, group, this, null); + } + + @Override + public ActionToolbar createActionToolbar(final String place, @NotNull final ActionGroup group, final boolean horizontal) { + return createActionToolbar(place, group, horizontal, false); + } + + @Override + public ActionToolbar createActionToolbar(final String place, @NotNull final ActionGroup group, final boolean horizontal, final boolean decorateButtons) { + return new ActionToolbarImpl(place, group, horizontal, decorateButtons, myDataManager, this, (KeymapManagerEx)myKeymapManager); + } + + private void registerPluginActions() { + final IdeaPluginDescriptor[] plugins = PluginManagerCore.getPlugins(); + for (IdeaPluginDescriptor plugin : plugins) { + if (PluginManagerCore.shouldSkipPlugin(plugin)) continue; + final List elementList = plugin.getActionsDescriptionElements(); + if (elementList != null) { + for (Element e : elementList) { + processActionsChildElement(plugin.getPluginClassLoader(), plugin.getPluginId(), e); + } + } + } + } + + @Override + public AnAction getAction(@NotNull String id) { + return getActionImpl(id, false, null); + } + + @Override + public AnAction getAction(@NonNls @NotNull String actionId, @Nullable ProjectType projectType) { + return getActionImpl(actionId, false, projectType); + } + + private AnAction getActionImpl(String id, boolean canReturnStub, ProjectType projectType) { + synchronized (myLock) { + AnAction action; + Object o = myId2Action.get(id); + if (o == null) { + return null; + } + if (o instanceof AnAction) { + action = (AnAction)o; + } + else { + //noinspection unchecked + action = ((Map)o).get(projectType); + } + if (!canReturnStub && action instanceof ActionStub) { + action = convert((ActionStub)action); + } + return action; + } + } + + /** + * Converts action's stub to normal action. + */ + @NotNull + private AnAction convert(@NotNull ActionStub stub) { + LOG.assertTrue(myAction2Id.containsKey(stub)); + myAction2Id.remove(stub); + + LOG.assertTrue(myId2Action.containsKey(stub.getId())); + + AnAction action = myId2Action.remove(stub.getId()); + LOG.assertTrue(action != null); + LOG.assertTrue(action.equals(stub)); + + AnAction anAction = convertStub(stub); + + addToMap(stub.getId(), anAction, stub.getPluginId(), stub.getProjectType() == null ? null : new ProjectType(stub.getProjectType())); + myAction2Id.put(anAction, stub.getId()); + + return anAction; + } + + @Override + public String getId(@NotNull AnAction action) { + LOG.assertTrue(!(action instanceof ActionStub)); + synchronized (myLock) { + return myAction2Id.get(action); + } + } + + @Override + public String[] getActionIds(@NotNull String idPrefix) { + synchronized (myLock) { + ArrayList idList = new ArrayList(); + for (String id : myId2Action.keySet()) { + if (id.startsWith(idPrefix)) { + idList.add(id); + } + } + return ArrayUtil.toStringArray(idList); + } + } + + @Override + public boolean isGroup(@NotNull String actionId) { + return getActionImpl(actionId, true, null) instanceof ActionGroup; + } + + @Override + public JComponent createButtonToolbar(final String actionPlace, @NotNull final ActionGroup messageActionGroup) { + return new ButtonToolbarImpl(actionPlace, messageActionGroup, myDataManager, this); + } + + @Override + public AnAction getActionOrStub(String id) { + return getActionImpl(id, true, null); + } + + /** + * @return instance of ActionGroup or ActionStub. The method never returns real subclasses + * of AnAction. + */ + @Nullable + private AnAction processActionElement(Element element, final ClassLoader loader, PluginId pluginId) { + final IdeaPluginDescriptor plugin = PluginManager.getPlugin(pluginId); + ResourceBundle bundle = getActionsResourceBundle(loader, plugin); + + if (!ACTION_ELEMENT_NAME.equals(element.getName())) { + reportActionError(pluginId, "unexpected name of element \"" + element.getName() + "\""); + return null; + } + String className = element.getAttributeValue(CLASS_ATTR_NAME); + if (className == null || className.isEmpty()) { + reportActionError(pluginId, "action element should have specified \"class\" attribute"); + return null; + } + // read ID and register loaded action + String id = element.getAttributeValue(ID_ATTR_NAME); + if (id == null || id.isEmpty()) { + id = StringUtil.getShortName(className); + } + if (Boolean.valueOf(element.getAttributeValue(INTERNAL_ATTR_NAME)).booleanValue() && !ApplicationManagerEx.getApplicationEx().isInternal()) { + myNotRegisteredInternalActionIds.add(id); + return null; + } + + String text = loadTextForElement(element, bundle, id, ACTION_ELEMENT_NAME); + + String iconPath = element.getAttributeValue(ICON_ATTR_NAME); + + if (text == null) { + @NonNls String message = "'text' attribute is mandatory (action ID=" + id + ";" + + (plugin == null ? "" : " plugin path: "+plugin.getPath()) + ")"; + reportActionError(pluginId, message); + return null; + } + + String projectType = element.getAttributeValue(PROJECT_TYPE); + ActionStub stub = new ActionStub(className, id, text, loader, pluginId, iconPath, projectType); + Presentation presentation = stub.getTemplatePresentation(); + presentation.setText(text); + + // description + + presentation.setDescription(loadDescriptionForElement(element, bundle, id, ACTION_ELEMENT_NAME)); + + // process all links and key bindings if any + for (final Object o : element.getChildren()) { + Element e = (Element)o; + if (ADD_TO_GROUP_ELEMENT_NAME.equals(e.getName())) { + processAddToGroupNode(stub, e, pluginId, isSecondary(e)); + } + else if (SHORTCUT_ELEMENT_NAME.equals(e.getName())) { + processKeyboardShortcutNode(e, id, pluginId); + } + else if (MOUSE_SHORTCUT_ELEMENT_NAME.equals(e.getName())) { + processMouseShortcutNode(e, id, pluginId); + } + else if (ABBREVIATION_ELEMENT_NAME.equals(e.getName())) { + processAbbreviationNode(e, id); + } + else { + reportActionError(pluginId, "unexpected name of element \"" + e.getName() + "\""); + return null; + } + } + if (element.getAttributeValue(USE_SHORTCUT_OF_ATTR_NAME) != null) { + ((KeymapManagerEx)myKeymapManager).bindShortcuts(element.getAttributeValue(USE_SHORTCUT_OF_ATTR_NAME), id); + } + + 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))) { + if (getActionOrStub(id) == null) { + throw new RuntimeException(element.getName() + " '" + id + "' doesn't override anything"); + } + AnAction prev = replaceAction(id, action, pluginId); + if (action instanceof DefaultActionGroup && prev instanceof DefaultActionGroup) { + if (Boolean.valueOf(element.getAttributeValue(KEEP_CONTENT_ATTR_NAME))) { + ((DefaultActionGroup)action).copyFromGroup((DefaultActionGroup)prev); + } + } + } + else { + registerAction(id, action, pluginId); + } + } + } + private AnAction processGroupElement(Element element, final ClassLoader loader, PluginId pluginId) { final IdeaPluginDescriptor plugin = PluginManager.getPlugin(pluginId); ResourceBundle bundle = getActionsResourceBundle(loader, plugin); @@ -723,43 +847,6 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat myId2GroupId.putValue(myAction2Id.get(action), myAction2Id.get(group)); } - public static boolean checkRelativeToAction(final String relativeToActionId, - @NotNull final Anchor anchor, - @NotNull final String actionName, - @Nullable final PluginId pluginId) { - if ((Anchor.BEFORE == anchor || Anchor.AFTER == anchor) && relativeToActionId == null) { - reportActionError(pluginId, actionName + ": \"relative-to-action\" cannot be null if anchor is \"after\" or \"before\""); - return false; - } - return true; - } - - @Nullable - public static Anchor parseAnchor(final String anchorStr, - @Nullable final String actionName, - @Nullable final PluginId pluginId) { - if (anchorStr == null) { - return Anchor.LAST; - } - - if (FIRST.equalsIgnoreCase(anchorStr)) { - return Anchor.FIRST; - } - else if (LAST.equalsIgnoreCase(anchorStr)) { - return Anchor.LAST; - } - else if (BEFORE.equalsIgnoreCase(anchorStr)) { - return Anchor.BEFORE; - } - else if (AFTER.equalsIgnoreCase(anchorStr)) { - return Anchor.AFTER; - } - else { - reportActionError(pluginId, actionName + ": anchor should be one of the following constants: \"first\", \"last\", \"before\" or \"after\""); - return null; - } - } - @Nullable public AnAction getParentGroup(final String groupId, @Nullable final String actionName, @@ -768,10 +855,10 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat reportActionError(pluginId, actionName + ": attribute \"group-id\" should be defined"); return null; } - AnAction parentGroup = getActionImpl(groupId, true); + AnAction parentGroup = getActionImpl(groupId, true, null); if (parentGroup == null) { reportActionError(pluginId, actionName + ": group with id \"" + groupId + "\" isn't registered; action will be added to the \"Other\" group"); - parentGroup = getActionImpl(IdeActions.GROUP_OTHER_MENU, true); + parentGroup = getActionImpl(IdeActions.GROUP_OTHER_MENU, true, null); } if (!(parentGroup instanceof DefaultActionGroup)) { reportActionError(pluginId, actionName + ": group with id \"" + groupId + "\" should be instance of " + DefaultActionGroup.class.getName() + @@ -850,40 +937,6 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat } } - private static void processMouseShortcutNode(Element element, String actionId, PluginId pluginId) { - String keystrokeString = element.getAttributeValue(KEYSTROKE_ATTR_NAME); - if (keystrokeString == null || keystrokeString.trim().isEmpty()) { - reportActionError(pluginId, "\"keystroke\" attribute must be specified for action with id=" + actionId); - return; - } - MouseShortcut shortcut; - try { - shortcut = KeymapUtil.parseMouseShortcut(keystrokeString); - } - catch (Exception ex) { - reportActionError(pluginId, "\"keystroke\" attribute has invalid value for action with id=" + actionId); - return; - } - - String keymapName = element.getAttributeValue(KEYMAP_ATTR_NAME); - if (keymapName == null || keymapName.isEmpty()) { - reportActionError(pluginId, "attribute \"keymap\" should be defined"); - return; - } - Keymap keymap = KeymapManager.getInstance().getKeymap(keymapName); - if (keymap == null) { - reportActionError(pluginId, "keymap \"" + keymapName + "\" not found"); - return; - } - - final String removeOption = element.getAttributeValue(REMOVE_SHORTCUT_ATTR_NAME); - if (Boolean.valueOf(removeOption)) { - keymap.removeShortcut(actionId, shortcut); - } else { - keymap.addShortcut(actionId, shortcut); - } - } - @Nullable private AnAction processReferenceElement(Element element, PluginId pluginId) { if (!REFERENCE_ELEMENT_NAME.equals(element.getName())) { @@ -902,7 +955,7 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat return null; } - AnAction action = getActionImpl(ref, true); + AnAction action = getActionImpl(ref, true, null); if (action == null) { if (!myNotRegisteredInternalActionIds.contains(ref)) { @@ -936,27 +989,15 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat } } - private static void assertActionIsGroupOrStub(final AnAction action) { - if (!(action instanceof ActionGroup || action instanceof ActionStub)) { - LOG.error("Action : " + action + "; class: " + action.getClass()); - } - } - @Override 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 + - "; Registered action is " + - myId2Action.get(actionId) + getPluginInfo(pluginId)); - return; - } + if (!addToMap(actionId, action, pluginId, null)) return; if (myAction2Id.containsKey(action)) { reportActionError(pluginId, "action was already registered for another ID. ID is " + myAction2Id.get(action) + getPluginInfo(pluginId)); return; } - myId2Action.put(actionId, action); myId2Index.put(actionId, myRegisteredActionsCount++); myAction2Id.put(action, actionId); if (pluginId != null && !(action instanceof ActionGroup)){ @@ -971,28 +1012,31 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat } } - private static void reportActionError(final PluginId pluginId, @NonNls @NotNull String message) { - if (pluginId == null) { - LOG.error(message); - } - else { - LOG.error(new PluginException(message, null, pluginId)); - } - } - - @NonNls - private static String getPluginInfo(@Nullable PluginId id) { - if (id != null) { - final IdeaPluginDescriptor plugin = PluginManager.getPlugin(id); - if (plugin != null) { - String name = plugin.getName(); - if (name == null) { - name = id.getIdString(); - } - return " Plugin: " + name; + public boolean addToMap(String actionId, AnAction action, PluginId pluginId, ProjectType projectType) { + if (myId2Action.containsKey(actionId)) { + // make sure id+projectType is unique + AnAction o = myId2Action.get(actionId); + ChameleonAction chameleonAction; + if (o instanceof ChameleonAction) { + chameleonAction = (ChameleonAction)o; + } + else { + chameleonAction = new ChameleonAction(o, projectType); + myId2Action.put(actionId, chameleonAction); + } + AnAction old = chameleonAction.addAction(action, projectType); + if (old != null) { + reportActionError(pluginId, + "action with the ID \"" + actionId + "\" was already registered. Action being registered is " + action + + "; Registered action is " + + myId2Action.get(actionId) + getPluginInfo(pluginId)); + return false; } } - return ""; + else { + myId2Action.put(actionId, action); + } + return true; } @Override @@ -1067,6 +1111,12 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat } } + //@Override + //public AnAction replaceAction(String actionId, @NotNull AnAction newAction) { + // synchronized (myLock) { + // return replaceAction(actionId, newAction, null); + // } + //} @Override public boolean isActionPopupStackEmpty() { @@ -1078,13 +1128,6 @@ 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) { @@ -1208,8 +1251,6 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat } } - private int myActionsPreloaded = 0; - public void preloadActions() { if (myPreloadActionsRunnable == null) { myPreloadActionsRunnable = new Runnable() { @@ -1281,6 +1322,85 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat } } + @Override + public ActionCallback tryToExecute(@NotNull final AnAction action, @NotNull final InputEvent inputEvent, @Nullable final Component contextComponent, @Nullable final String place, + boolean now) { + + final Application app = ApplicationManager.getApplication(); + assert app.isDispatchThread(); + + final ActionCallback result = new ActionCallback(); + final Runnable doRunnable = new Runnable() { + @Override + public void run() { + tryToExecuteNow(action, inputEvent, contextComponent, place, result); + } + }; + + if (now) { + doRunnable.run(); + } else { + //noinspection SSBasedInspection + SwingUtilities.invokeLater(doRunnable); + } + + return result; + } + + private void tryToExecuteNow(final AnAction action, final InputEvent inputEvent, final Component contextComponent, final String place, final ActionCallback result) { + final Presentation presentation = action.getTemplatePresentation().clone(); + + IdeFocusManager.findInstanceByContext(getContextBy(contextComponent)).doWhenFocusSettlesDown(new Runnable() { + @Override + public void run() { + final DataContext context = getContextBy(contextComponent); + + AnActionEvent event = new AnActionEvent( + inputEvent, context, + place != null ? place : ActionPlaces.UNKNOWN, + presentation, ActionManagerImpl.this, + inputEvent.getModifiersEx() + ); + + ActionUtil.performDumbAwareUpdate(action, event, false); + if (!event.getPresentation().isEnabled()) { + result.setRejected(); + return; + } + + ActionUtil.lastUpdateAndCheckDumb(action, event, false); + if (!event.getPresentation().isEnabled()) { + result.setRejected(); + return; + } + + Component component = PlatformDataKeys.CONTEXT_COMPONENT.getData(context); + if (component != null && !component.isShowing()) { + result.setRejected(); + return; + } + + fireBeforeActionPerformed(action, context, event); + + UIUtil.addAwtListener(new AWTEventListener() { + @Override + public void eventDispatched(AWTEvent event) { + if (event.getID() == WindowEvent.WINDOW_OPENED ||event.getID() == WindowEvent.WINDOW_ACTIVATED) { + if (!result.isProcessed()) { + final WindowEvent we = (WindowEvent)event; + IdeFocusManager.findInstanceByComponent(we.getWindow()).doWhenFocusSettlesDown(result.createSetDoneRunnable()); + } + } + } + }, AWTEvent.WINDOW_EVENT_MASK, result); + + ActionUtil.performActionDumbAware(action, event); + result.setDone(); + queueActionPerformedEvent(action, context, event); + } + }); + } + private class MyTimer extends Timer implements ActionListener { private final List myTimerListeners = ContainerUtil.createLockFreeCopyOnWriteList(); private final List myTransparentTimerListeners = ContainerUtil.createLockFreeCopyOnWriteList(); @@ -1371,88 +1491,4 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat } } } - - @Override - public ActionCallback tryToExecute(@NotNull final AnAction action, @NotNull final InputEvent inputEvent, @Nullable final Component contextComponent, @Nullable final String place, - boolean now) { - - final Application app = ApplicationManager.getApplication(); - assert app.isDispatchThread(); - - final ActionCallback result = new ActionCallback(); - final Runnable doRunnable = new Runnable() { - @Override - public void run() { - tryToExecuteNow(action, inputEvent, contextComponent, place, result); - } - }; - - if (now) { - doRunnable.run(); - } else { - //noinspection SSBasedInspection - SwingUtilities.invokeLater(doRunnable); - } - - return result; - } - - private void tryToExecuteNow(final AnAction action, final InputEvent inputEvent, final Component contextComponent, final String place, final ActionCallback result) { - final Presentation presentation = action.getTemplatePresentation().clone(); - - IdeFocusManager.findInstanceByContext(getContextBy(contextComponent)).doWhenFocusSettlesDown(new Runnable() { - @Override - public void run() { - final DataContext context = getContextBy(contextComponent); - - AnActionEvent event = new AnActionEvent( - inputEvent, context, - place != null ? place : ActionPlaces.UNKNOWN, - presentation, ActionManagerImpl.this, - inputEvent.getModifiersEx() - ); - - ActionUtil.performDumbAwareUpdate(action, event, false); - if (!event.getPresentation().isEnabled()) { - result.setRejected(); - return; - } - - ActionUtil.lastUpdateAndCheckDumb(action, event, false); - if (!event.getPresentation().isEnabled()) { - result.setRejected(); - return; - } - - Component component = PlatformDataKeys.CONTEXT_COMPONENT.getData(context); - if (component != null && !component.isShowing()) { - result.setRejected(); - return; - } - - fireBeforeActionPerformed(action, context, event); - - UIUtil.addAwtListener(new AWTEventListener() { - @Override - public void eventDispatched(AWTEvent event) { - if (event.getID() == WindowEvent.WINDOW_OPENED ||event.getID() == WindowEvent.WINDOW_ACTIVATED) { - if (!result.isProcessed()) { - final WindowEvent we = (WindowEvent)event; - IdeFocusManager.findInstanceByComponent(we.getWindow()).doWhenFocusSettlesDown(result.createSetDoneRunnable()); - } - } - } - }, AWTEvent.WINDOW_EVENT_MASK, result); - - ActionUtil.performActionDumbAware(action, event); - result.setDone(); - queueActionPerformedEvent(action, context, event); - } - }); - } - - private static DataContext getContextBy(Component contextComponent) { - final DataManager dataManager = DataManager.getInstance(); - return contextComponent != null ? dataManager.getDataContext(contextComponent) : dataManager.getDataContext(); - } } diff --git a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ChameleonAction.java b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ChameleonAction.java new file mode 100644 index 000000000000..4b46dc191c0c --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ChameleonAction.java @@ -0,0 +1,68 @@ +/* + * Copyright 2000-2014 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.actionSystem.impl; + +import com.intellij.openapi.actionSystem.ActionStub; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.CommonDataKeys; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.project.ProjectType; +import com.intellij.openapi.project.ProjectTypeService; +import org.jetbrains.annotations.NotNull; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author Dmitry Avdeev + */ +public class ChameleonAction extends AnAction { + + private final Map myActions = new HashMap(); + + public ChameleonAction(@NotNull AnAction first, ProjectType projectType) { + addAction(first, projectType); + } + + public AnAction addAction(AnAction action, ProjectType projectType) { + if (action instanceof ActionStub) { + String type = ((ActionStub)action).getProjectType(); + action = ActionManagerImpl.convertStub((ActionStub)action); + projectType = type == null ? null : new ProjectType(type); + } + return myActions.put(projectType, action); + } + + @Override + public void actionPerformed(AnActionEvent e) { + getAction(e).actionPerformed(e); + } + + @Override + public void update(AnActionEvent e) { + super.update(e); + } + + private AnAction getAction(AnActionEvent e) { + Project project = CommonDataKeys.PROJECT.getData(e.getDataContext()); + ProjectType projectType = ProjectTypeService.getProjectType(project); + AnAction action = myActions.get(projectType); + if (action == null) action = myActions.get(null); + if (action == null) action = myActions.values().iterator().next(); + return action; + } +} 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 01aa0ff4c49e..4de847213103 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 @@ -40,7 +40,6 @@ public class ActionBean { @Attribute(ActionManagerImpl.ICON_ATTR_NAME) public String icon; - @Attribute(ActionManagerImpl.POPUP_ATTR_NAME) public String isPopup; @@ -58,4 +57,7 @@ public class ActionBean { @Attribute(ActionManagerImpl.OVERRIDES_ATTR_NAME) public boolean overrides; + + @Attribute(ActionManagerImpl.PROJECT_TYPE) + public String projectType; } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/ContextMenuImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/ContextMenuImpl.java index 28a7c2204265..cbe6da5379cf 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/ContextMenuImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/ContextMenuImpl.java @@ -46,16 +46,15 @@ import java.awt.event.ActionListener; public class ContextMenuImpl extends JPanel implements Disposable { @NonNls public static final String ACTION_GROUP = "EditorContextBarMenu"; - - private ActionGroup myActionGroup; private final JComponent myComponent; + private final JLayeredPane myLayeredPane; + private ActionGroup myActionGroup; private boolean myVisible = false; private boolean myShow = false; private int myCurrentOpacity; private Timer myTimer; private EditorImpl myEditor; private boolean myDisposed; - private final JLayeredPane myLayeredPane; private ActionToolbar myActionToolbar; public ContextMenuImpl(JLayeredPane layeredPane, @NotNull final JScrollPane container, @NotNull final EditorImpl editor) { @@ -81,7 +80,7 @@ public class ContextMenuImpl extends JPanel implements Disposable { } }); - AnAction action = actionManager.getAction("EditorContextBarMenu"); + AnAction action = actionManager.getAction(ACTION_GROUP); if (action == null) { action = new DefaultActionGroup(); actionManager.registerAction(ACTION_GROUP, action); @@ -107,6 +106,15 @@ public class ContextMenuImpl extends JPanel implements Disposable { return activationArea.contains(p.x, p.y - viewPosition.y); } + public static boolean mayShowToolbar(@Nullable final Document document) { + if (document == null) { + return false; + } + + final VirtualFile file = FileDocumentManager.getInstance().getFile(document); + return file != null && file.isValid() && (file.isInLocalFileSystem() || file instanceof HttpVirtualFile); + } + private void toggleContextToolbar(final boolean show) { final Component toolbar = myComponent.getComponent(0); final int count = ((Container)toolbar).getComponentCount(); @@ -201,15 +209,6 @@ public class ContextMenuImpl extends JPanel implements Disposable { } } - public static boolean mayShowToolbar(@Nullable final Document document) { - if (document == null) { - return false; - } - - final VirtualFile file = FileDocumentManager.getInstance().getFile(document); - return file != null && file.isValid() && (file.isInLocalFileSystem() || file instanceof HttpVirtualFile); - } - private void scheduleHide() { if (myTimer != null && myTimer.isRunning()) { myTimer.stop(); diff --git a/platform/platform-resources/src/META-INF/PlatformExtensionPoints.xml b/platform/platform-resources/src/META-INF/PlatformExtensionPoints.xml index a4dfd49a71a8..baea70a67c7a 100644 --- a/platform/platform-resources/src/META-INF/PlatformExtensionPoints.xml +++ b/platform/platform-resources/src/META-INF/PlatformExtensionPoints.xml @@ -9,6 +9,9 @@ + + diff --git a/platform/platform-resources/src/META-INF/PlatformExtensions.xml b/platform/platform-resources/src/META-INF/PlatformExtensions.xml index aa3ee139c1d3..51028d93edac 100644 --- a/platform/platform-resources/src/META-INF/PlatformExtensions.xml +++ b/platform/platform-resources/src/META-INF/PlatformExtensions.xml @@ -155,6 +155,7 @@ serviceImplementation="com.intellij.openapi.vcs.readOnlyHandler.ReadonlyStatusHandlerImpl"/> + diff --git a/platform/projectModel-api/src/com/intellij/openapi/project/DefaultProjectTypeProvider.java b/platform/projectModel-api/src/com/intellij/openapi/project/DefaultProjectTypeProvider.java new file mode 100644 index 000000000000..81887eba0f98 --- /dev/null +++ b/platform/projectModel-api/src/com/intellij/openapi/project/DefaultProjectTypeProvider.java @@ -0,0 +1,35 @@ +/* + * Copyright 2000-2014 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.project; + +import com.intellij.openapi.extensions.ExtensionPointName; +import org.jetbrains.annotations.Nullable; + +/** + * @author Dmitry Avdeev + */ +public abstract class DefaultProjectTypeProvider { + + private final static ExtensionPointName EXTENSION_POINT_NAME = ExtensionPointName.create("com.intellij.defaultProjectTypeProvider"); + + @Nullable + public static ProjectType getDefaultProjectType() { + DefaultProjectTypeProvider[] extensions = EXTENSION_POINT_NAME.getExtensions(); + return extensions.length > 0 ? extensions[0].getProjectType() : null; + } + + protected abstract ProjectType getProjectType(); +} diff --git a/platform/projectModel-api/src/com/intellij/openapi/project/ProjectType.java b/platform/projectModel-api/src/com/intellij/openapi/project/ProjectType.java new file mode 100644 index 000000000000..f096e67c0ff5 --- /dev/null +++ b/platform/projectModel-api/src/com/intellij/openapi/project/ProjectType.java @@ -0,0 +1,59 @@ +/* + * Copyright 2000-2014 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.project; + +/** + * @author Dmitry Avdeev + */ +public class ProjectType { + + private String id; + + /** + * For serialization. + */ + public ProjectType() { + } + + public ProjectType(String id) { + this.id = id; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ProjectType type = (ProjectType)o; + + if (id != null ? !id.equals(type.id) : type.id != null) return false; + + return true; + } + + @Override + public int hashCode() { + return id != null ? id.hashCode() : 0; + } +} diff --git a/platform/projectModel-api/src/com/intellij/openapi/project/ProjectTypeService.java b/platform/projectModel-api/src/com/intellij/openapi/project/ProjectTypeService.java new file mode 100644 index 000000000000..e43059390b19 --- /dev/null +++ b/platform/projectModel-api/src/com/intellij/openapi/project/ProjectTypeService.java @@ -0,0 +1,61 @@ +/* + * Copyright 2000-2014 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.project; + +import com.intellij.openapi.components.*; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author Dmitry Avdeev + */ + +@State( + name = "ProjectType", + storages = { + @Storage(file = StoragePathMacros.PROJECT_FILE), + @Storage(file = StoragePathMacros.PROJECT_CONFIG_DIR + "/misc.xml", scheme = StorageScheme.DIRECTORY_BASED) + } +) +public class ProjectTypeService implements PersistentStateComponent { + + private ProjectType myProjectType; + + @Nullable + public static ProjectType getProjectType(@Nullable Project project) { + ProjectType projectType; + if (project != null) { + projectType = getInstance(project).myProjectType; + if (projectType != null) return projectType; + } + return DefaultProjectTypeProvider.getDefaultProjectType(); + } + + public static ProjectTypeService getInstance(@NotNull Project project) { + return ServiceManager.getService(project, ProjectTypeService.class); + } + + @Nullable + @Override + public ProjectType getState() { + return myProjectType; + } + + @Override + public void loadState(ProjectType state) { + myProjectType = state; + } +}