2 * Copyright 2000-2014 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.intellij.openapi.actionSystem.impl;
18 import com.intellij.icons.AllIcons;
19 import com.intellij.ide.DataManager;
20 import com.intellij.openapi.actionSystem.*;
21 import com.intellij.openapi.actionSystem.ex.ActionButtonLook;
22 import com.intellij.openapi.actionSystem.ex.ActionManagerEx;
23 import com.intellij.openapi.actionSystem.ex.ActionUtil;
24 import com.intellij.openapi.actionSystem.ex.CustomComponentAction;
25 import com.intellij.openapi.keymap.KeymapUtil;
26 import com.intellij.openapi.util.Getter;
27 import com.intellij.openapi.util.IconLoader;
28 import com.intellij.util.ui.EmptyIcon;
29 import com.intellij.util.ui.JBDimension;
30 import com.intellij.util.ui.JBUI;
31 import com.intellij.util.ui.UIUtil;
32 import org.jetbrains.annotations.NonNls;
33 import org.jetbrains.annotations.NotNull;
34 import org.jetbrains.annotations.Nullable;
38 import java.awt.event.MouseEvent;
39 import java.beans.PropertyChangeEvent;
40 import java.beans.PropertyChangeListener;
42 public class ActionButton extends JComponent implements ActionButtonComponent, AnActionHolder {
44 private static final Icon ourEmptyIcon = EmptyIcon.ICON_18;
46 private JBDimension myMinimumButtonSize;
47 private PropertyChangeListener myActionButtonSynchronizer;
48 private Icon myDisabledIcon;
50 protected final Presentation myPresentation;
51 protected final AnAction myAction;
52 protected final String myPlace;
53 private ActionButtonLook myLook = ActionButtonLook.IDEA_LOOK;
54 private boolean myMouseDown;
55 private boolean myRollover;
56 private static boolean ourGlobalMouseDown = false;
58 private boolean myNoIconsInPopup = false;
59 private Insets myInsets;
61 public ActionButton(AnAction action,
62 Presentation presentation,
64 @NotNull Dimension minimumSize) {
65 setMinimumButtonSize(minimumSize);
70 myPresentation = presentation;
73 enableEvents(AWTEvent.MOUSE_EVENT_MASK);
74 myMinimumButtonSize = JBDimension.create(minimumSize);
76 putClientProperty(UIUtil.CENTER_TOOLTIP_DEFAULT, Boolean.TRUE);
79 public void setNoIconsInPopup(boolean noIconsInPopup) {
80 myNoIconsInPopup = noIconsInPopup;
83 public void setMinimumButtonSize(@NotNull Dimension size) {
84 myMinimumButtonSize = JBDimension.create(size);
87 public void paintChildren(Graphics g) {}
89 public int getPopState() {
90 if (myAction instanceof Toggleable) {
91 Boolean selected = (Boolean)myPresentation.getClientProperty(Toggleable.SELECTED_PROPERTY);
92 boolean flag1 = selected != null && selected.booleanValue();
93 return getPopState(flag1);
96 return getPopState(false);
100 protected boolean isButtonEnabled() {
101 return isEnabled() && myPresentation.isEnabled();
104 private void onMousePresenceChanged(boolean setInfo) {
105 ActionMenu.showDescriptionInStatusBar(setInfo, this, myPresentation.getDescription());
108 public void click() {
109 performAction(new MouseEvent(this, MouseEvent.MOUSE_CLICKED, System.currentTimeMillis(), 0, 0, 0, 1, false));
112 private void performAction(MouseEvent e) {
113 AnActionEvent event = AnActionEvent.createFromInputEvent(e, myPlace, myPresentation, getDataContext());
114 if (!ActionUtil.lastUpdateAndCheckDumb(myAction, event, false)) {
118 if (isButtonEnabled()) {
119 final ActionManagerEx manager = ActionManagerEx.getInstanceEx();
120 final DataContext dataContext = event.getDataContext();
121 manager.fireBeforeActionPerformed(myAction, dataContext, event);
122 Component component = PlatformDataKeys.CONTEXT_COMPONENT.getData(dataContext);
123 if (component != null && !component.isShowing()) {
126 actionPerformed(event);
127 manager.queueActionPerformedEvent(myAction, dataContext, event);
131 protected DataContext getDataContext() {
132 ActionToolbar actionToolbar = UIUtil.getParentOfType(ActionToolbar.class, this);
133 return actionToolbar != null ? actionToolbar.getToolbarDataContext() : DataManager.getInstance().getDataContext();
136 private void actionPerformed(final AnActionEvent event) {
137 if (myAction instanceof ActionGroup && !(myAction instanceof CustomComponentAction) && ((ActionGroup)myAction).isPopup()) {
138 final ActionManagerImpl am = (ActionManagerImpl)ActionManager.getInstance();
139 ActionPopupMenuImpl popupMenu = (ActionPopupMenuImpl)am.createActionPopupMenu(event.getPlace(), (ActionGroup)myAction, new MenuItemPresentationFactory() {
141 protected void processPresentation(Presentation presentation) {
142 if (myNoIconsInPopup) {
143 presentation.setIcon(null);
144 presentation.setHoveredIcon(null);
148 popupMenu.setDataContextProvider(new Getter<DataContext>() {
150 public DataContext get() {
151 return ActionButton.this.getDataContext();
154 if (ActionPlaces.isToolbarPlace(event.getPlace())) {
155 popupMenu.getComponent().show(this, 0, getHeight());
158 popupMenu.getComponent().show(this, getWidth(), 0);
162 ActionUtil.performActionDumbAware(myAction, event);
166 public void removeNotify() {
167 if (myActionButtonSynchronizer != null) {
168 myPresentation.removePropertyChangeListener(myActionButtonSynchronizer);
169 myActionButtonSynchronizer = null;
171 super.removeNotify();
174 public void addNotify() {
176 if (myActionButtonSynchronizer == null) {
177 myActionButtonSynchronizer = new ActionButtonSynchronizer();
178 myPresentation.addPropertyChangeListener(myActionButtonSynchronizer);
180 AnActionEvent e = new AnActionEvent(null, getDataContext(), myPlace, myPresentation, ActionManager.getInstance(), 0);
181 ActionUtil.performDumbAwareUpdate(myAction, e, false);
186 public void setToolTipText(String s) {
187 String tooltipText = KeymapUtil.createTooltipText(s, myAction);
188 super.setToolTipText(tooltipText.length() > 0 ? tooltipText : null);
191 public Dimension getPreferredSize() {
192 Icon icon = getIcon();
193 if (icon.getIconWidth() < myMinimumButtonSize.width &&
194 icon.getIconHeight() < myMinimumButtonSize.height) {
195 return myMinimumButtonSize;
198 return new Dimension(
199 icon.getIconWidth() + myInsets.left + myInsets.right,
200 icon.getIconHeight() + myInsets.top + myInsets.bottom
206 public void setIconInsets(@Nullable Insets insets) {
207 myInsets = insets != null ? JBUI.insets(insets) : new Insets(0,0,0,0);
210 public Dimension getMinimumSize() {
211 return getPreferredSize();
215 * @return button's icon. Icon depends on action's state. It means that the method returns
216 * disabled icon if action is disabled. If the action's icon is <code>null</code> then it returns
219 protected Icon getIcon() {
220 Icon icon = isButtonEnabled() ? myIcon : myDisabledIcon;
227 public void updateIcon() {
228 myIcon = myPresentation.getIcon();
229 if (myPresentation.getDisabledIcon() != null) { // set disabled icon if it is specified
230 myDisabledIcon = myPresentation.getDisabledIcon();
233 myDisabledIcon = IconLoader.getDisabledIcon(myIcon);
237 private void setDisabledIcon(Icon icon) {
238 myDisabledIcon = icon;
241 void updateToolTipText() {
242 String text = myPresentation.getText();
243 setToolTipText(text == null ? myPresentation.getDescription() : text);
246 public void paintComponent(Graphics g) {
247 super.paintComponent(g);
251 if (myAction instanceof ActionGroup && ((ActionGroup)myAction).isPopup()) {
256 if (getPopState() == PUSHED) {
261 AllIcons.General.Dropdown.paintIcon(this, g, x, y);
265 protected void paintButtonLook(Graphics g) {
266 ActionButtonLook look = getButtonLook();
267 look.paintBackground(g, this);
268 look.paintIcon(g, this, getIcon());
269 look.paintBorder(g, this);
272 protected ActionButtonLook getButtonLook() {
276 public void setLook(ActionButtonLook look) {
281 myLook = ActionButtonLook.IDEA_LOOK;
286 protected void processMouseEvent(MouseEvent e) {
287 super.processMouseEvent(e);
288 if (e.isConsumed()) return;
289 boolean skipPress = e.isMetaDown() || e.getButton() != MouseEvent.BUTTON1;
291 case MouseEvent.MOUSE_PRESSED:
292 if (skipPress || !isButtonEnabled()) return;
294 ourGlobalMouseDown = true;
298 case MouseEvent.MOUSE_RELEASED:
299 if (skipPress || !isButtonEnabled()) return;
301 ourGlobalMouseDown = false;
308 case MouseEvent.MOUSE_ENTERED:
309 if (!myMouseDown && ourGlobalMouseDown) break;
312 onMousePresenceChanged(true);
315 case MouseEvent.MOUSE_EXITED:
317 if (!myMouseDown && ourGlobalMouseDown) break;
319 onMousePresenceChanged(false);
324 private int getPopState(boolean isPushed) {
325 if (isPushed || myRollover && myMouseDown && isButtonEnabled()) {
329 return !myRollover || !isButtonEnabled() ? NORMAL : POPPED;
333 public AnAction getAction() {
337 private class ActionButtonSynchronizer implements PropertyChangeListener {
338 @NonNls protected static final String SELECTED_PROPERTY_NAME = "selected";
340 public void propertyChange(PropertyChangeEvent e) {
341 String propertyName = e.getPropertyName();
342 if (Presentation.PROP_TEXT.equals(propertyName)) {
344 revalidate(); // recalc preferred size & repaint instantly
346 else if (Presentation.PROP_ENABLED.equals(propertyName)) {
350 else if (Presentation.PROP_ICON.equals(propertyName)) {
354 else if (Presentation.PROP_DISABLED_ICON.equals(propertyName)) {
355 setDisabledIcon(myPresentation.getDisabledIcon());
358 else if (Presentation.PROP_VISIBLE.equals(propertyName)) {
360 else if (SELECTED_PROPERTY_NAME.equals(propertyName)) {