/* * 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; import com.intellij.openapi.Disposable; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.PossiblyDumbAware; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Disposer; import com.intellij.util.IncorrectOperationException; import com.intellij.util.SmartList; import org.intellij.lang.annotations.JdkConstants; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.util.List; /** * Represents an entity that has a state, a presentation and can be performed. * * For an action to be useful, you need to implement {@link AnAction#actionPerformed} * and optionally to override {@link com.intellij.openapi.actionSystem.AnAction#update}. By overriding the * {@link com.intellij.openapi.actionSystem.AnAction#update} method you can dynamically change action's presentation * depending on the place (for more information on places see {@link ActionPlaces}. * * The same action can have various presentations. * *
 *  public class MyAction extends AnAction {
 *    public MyAction() {
 *      // ...
 *    }
 *
 *    public void update(AnActionEvent e) {
 *      Presentation presentation = e.getPresentation();
 *      if (e.getPlace().equals(ActionPlaces.MAIN_MENU)) {
 *        presentation.setText("My Menu item name");
 *      } else if (e.getPlace().equals(ActionPlaces.MAIN_TOOLBAR)) {
 *        presentation.setText("My Toolbar item name");
 *      }
 *    }
 *
 *    public void actionPerformed(AnActionEvent e) { ... }
 *  }
 * 
* * @see AnActionEvent * @see Presentation * @see ActionPlaces */ public abstract class AnAction implements PossiblyDumbAware { public static final AnAction[] EMPTY_ARRAY = new AnAction[0]; @NonNls public static final String ourClientProperty = "AnAction.shortcutSet"; private Presentation myTemplatePresentation; private ShortcutSet myShortcutSet; private boolean myEnabledInModalContext; private static final ShortcutSet ourEmptyShortcutSet = new CustomShortcutSet(); private boolean myIsDefaultIcon = true; private boolean myWorksInInjected; /** * Creates a new action with its text, description and icon set to null. */ public AnAction(){ this(null, null, null); } /** * Creates a new action with icon provided. Its text, description set to null. * * @param icon Default icon to appear in toolbars and menus (Note some platform don't have icons in menu). */ public AnAction(Icon icon){ this(null, null, icon); } /** * Creates a new action with the specified text. Description and icon are * set to null. * * @param text Serves as a tooltip when the presentation is a button and the name of the * menu item when the presentation is a menu item. */ public AnAction(@Nullable String text){ this(text, null, null); } /** * Constructs a new action with the specified text, description and icon. * * @param text Serves as a tooltip when the presentation is a button and the name of the * menu item when the presentation is a menu item * * @param description Describes current action, this description will appear on * the status bar when presentation has focus * * @param icon Action's icon */ public AnAction(@Nullable String text, @Nullable String description, @Nullable Icon icon){ myShortcutSet = ourEmptyShortcutSet; myEnabledInModalContext = false; Presentation presentation = getTemplatePresentation(); presentation.setText(text); presentation.setDescription(description); presentation.setIcon(icon); } /** * Returns the shortcut set associated with this action. * * @return shortcut set associated with this action */ public final ShortcutSet getShortcutSet(){ return myShortcutSet; } /** * Registers a set of shortcuts that will be processed when the specified component * is the ancestor of focused component. Note that the action doesn't have * to be registered in action manager in order for that shortcut to work. * * @param shortcutSet the shortcuts for the action. * @param component the component for which the shortcuts will be active. */ public final void registerCustomShortcutSet(@NotNull ShortcutSet shortcutSet, @Nullable JComponent component){ myShortcutSet = shortcutSet; if (component != null){ @SuppressWarnings("unchecked") List actionList = (List)component.getClientProperty(ourClientProperty); if (actionList == null){ actionList = new SmartList(); component.putClientProperty(ourClientProperty, actionList); } if (!actionList.contains(this)){ actionList.add(this); } } } public final void registerCustomShortcutSet(int keyCode, @JdkConstants.InputEventMask int modifiers, @Nullable JComponent component) { registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(keyCode, modifiers)), component); } public final void registerCustomShortcutSet(@NotNull ShortcutSet shortcutSet, @NotNull final JComponent component, @NotNull Disposable parentDisposable) { registerCustomShortcutSet(shortcutSet, component); Disposer.register(parentDisposable, new Disposable() { @Override public void dispose() { unregisterCustomShortcutSet(component); } }); } public final void unregisterCustomShortcutSet(JComponent component){ if (component != null){ @SuppressWarnings("unchecked") List actionList = (List)component.getClientProperty(ourClientProperty); if (actionList != null){ actionList.remove(this); } } } /** * Copies template presentation and shortcuts set from sourceAction. * * @param sourceAction cannot be null */ public final void copyFrom(@NotNull AnAction sourceAction){ Presentation sourcePresentation = sourceAction.getTemplatePresentation(); Presentation presentation = getTemplatePresentation(); presentation.setIcon(sourcePresentation.getIcon()); presentation.setText(sourcePresentation.getTextWithMnemonic()); presentation.setDescription(sourcePresentation.getDescription()); copyShortcutFrom(sourceAction); } public final void copyShortcutFrom(@NotNull AnAction sourceAction) { myShortcutSet = sourceAction.myShortcutSet; } public final boolean isEnabledInModalContext() { return myEnabledInModalContext; } protected final void setEnabledInModalContext(boolean enabledInModalContext) { myEnabledInModalContext = enabledInModalContext; } /** * Override with true returned if your action has to display its text along with the icon when placed in the toolbar */ public boolean displayTextInToolbar() { return false; } /** * Updates the state of the action. Default implementation does nothing. * Override this method to provide the ability to dynamically change action's * state and(or) presentation depending on the context (For example * when your action state depends on the selection you can check for * selection and change the state accordingly). * This method can be called frequently, for instance, if an action is added to a toolbar, * it will be updated twice a second. This means that this method is supposed to work really fast, * no real work should be done at this phase. For example, checking selection in a tree or a list, * is considered valid, but working with a file system is not. If you cannot understand the state of * the action fast you should do it in the {@link #actionPerformed(AnActionEvent)} method and notify * the user that action cannot be executed if it's the case. * * @param e Carries information on the invocation place and data available */ public void update(AnActionEvent e) { } /** * Same as {@link #update(AnActionEvent)} but is calls immediately before actionPerformed() as final check guard. * Default implementation delegates to {@link #update(AnActionEvent)}. * * @param e Carries information on the invocation place and data available */ public void beforeActionPerformedUpdate(@NotNull AnActionEvent e) { boolean worksInInjected = isInInjectedContext(); e.setInjectedContext(worksInInjected); update(e); if (!e.getPresentation().isEnabled() && worksInInjected) { e.setInjectedContext(false); update(e); } } /** * Returns a template presentation that will be used * as a template for created presentations. * * @return template presentation */ @NotNull public final Presentation getTemplatePresentation() { Presentation presentation = myTemplatePresentation; if (presentation == null){ myTemplatePresentation = presentation = new Presentation(); } return presentation; } /** * Implement this method to provide your action handler. * * @param e Carries information on the invocation place */ public abstract void actionPerformed(AnActionEvent e); protected void setShortcutSet(ShortcutSet shortcutSet) { myShortcutSet = shortcutSet; } /** * @deprecated Use KeymapUtil.createTooltipText() */ public static String createTooltipText(String s, @NotNull AnAction action) { throw new IncorrectOperationException("Please use KeymapUtil.createTooltipText()"); } /** * Sets the flag indicating whether the action has an internal or a user-customized icon. * @param isDefaultIconSet true if the icon is internal, false if the icon is customized by the user. */ public void setDefaultIcon(boolean isDefaultIconSet) { myIsDefaultIcon = isDefaultIconSet; } /** * Returns true if the action has an internal, not user-customized icon. * @return true if the icon is internal, false if the icon is customized by the user. */ public boolean isDefaultIcon() { return myIsDefaultIcon; } /** * Enables automatic detection of injected fragments in editor. Values in DataContext, passed to the action, like EDITOR, PSI_FILE * will refer to an injected fragment, if caret is currently positioned on it. */ public void setInjectedContext(boolean worksInInjected) { myWorksInInjected = worksInInjected; } public boolean isInInjectedContext() { return myWorksInInjected; } public boolean isTransparentUpdate() { return this instanceof TransparentUpdate; } @Override public boolean isDumbAware() { return this instanceof DumbAware; } public interface TransparentUpdate { } @Nullable public static Project getEventProject(AnActionEvent e) { return e == null ? null : e.getData(CommonDataKeys.PROJECT); } @Override public String toString() { return getTemplatePresentation().toString(); } }