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);
75 putClientProperty(UIUtil.CENTER_TOOLTIP_DEFAULT, Boolean.TRUE);
78 public void setNoIconsInPopup(boolean noIconsInPopup) {
79 myNoIconsInPopup = noIconsInPopup;
82 public void setMinimumButtonSize(@NotNull Dimension size) {
83 myMinimumButtonSize = JBDimension.create(size);
86 public void paintChildren(Graphics g) {}
88 public int getPopState() {
89 if (myAction instanceof Toggleable) {
90 Boolean selected = (Boolean)myPresentation.getClientProperty(Toggleable.SELECTED_PROPERTY);
91 boolean flag1 = selected != null && selected.booleanValue();
92 return getPopState(flag1);
95 return getPopState(false);
99 protected boolean isButtonEnabled() {
100 return isEnabled() && myPresentation.isEnabled();
103 private void onMousePresenceChanged(boolean setInfo) {
104 ActionMenu.showDescriptionInStatusBar(setInfo, this, myPresentation.getDescription());
107 public void click() {
108 performAction(new MouseEvent(this, MouseEvent.MOUSE_CLICKED, System.currentTimeMillis(), 0, 0, 0, 1, false));
111 private void performAction(MouseEvent e) {
112 AnActionEvent event = AnActionEvent.createFromInputEvent(e, myPlace, myPresentation, getDataContext());
113 if (!ActionUtil.lastUpdateAndCheckDumb(myAction, event, false)) {
117 if (isButtonEnabled()) {
118 final ActionManagerEx manager = ActionManagerEx.getInstanceEx();
119 final DataContext dataContext = event.getDataContext();
120 manager.fireBeforeActionPerformed(myAction, dataContext, event);
121 Component component = PlatformDataKeys.CONTEXT_COMPONENT.getData(dataContext);
122 if (component != null && !component.isShowing()) {
125 actionPerformed(event);
126 manager.queueActionPerformedEvent(myAction, dataContext, event);
130 protected DataContext getDataContext() {
131 ActionToolbar actionToolbar = UIUtil.getParentOfType(ActionToolbar.class, this);
132 return actionToolbar != null ? actionToolbar.getToolbarDataContext() : DataManager.getInstance().getDataContext();
135 private void actionPerformed(final AnActionEvent event) {
136 if (myAction instanceof ActionGroup && !(myAction instanceof CustomComponentAction) && ((ActionGroup)myAction).isPopup()) {
137 final ActionManagerImpl am = (ActionManagerImpl)ActionManager.getInstance();
138 ActionPopupMenuImpl popupMenu = (ActionPopupMenuImpl)am.createActionPopupMenu(event.getPlace(), (ActionGroup)myAction, new MenuItemPresentationFactory() {
140 protected void processPresentation(Presentation presentation) {
141 if (myNoIconsInPopup) {
142 presentation.setIcon(null);
143 presentation.setHoveredIcon(null);
147 popupMenu.setDataContextProvider(new Getter<DataContext>() {
149 public DataContext get() {
150 return ActionButton.this.getDataContext();
153 if (ActionPlaces.isToolbarPlace(event.getPlace())) {
154 popupMenu.getComponent().show(this, 0, getHeight());
157 popupMenu.getComponent().show(this, getWidth(), 0);
161 ActionUtil.performActionDumbAware(myAction, event);
165 public void removeNotify() {
166 if (myActionButtonSynchronizer != null) {
167 myPresentation.removePropertyChangeListener(myActionButtonSynchronizer);
168 myActionButtonSynchronizer = null;
170 super.removeNotify();
173 public void addNotify() {
175 if (myActionButtonSynchronizer == null) {
176 myActionButtonSynchronizer = new ActionButtonSynchronizer();
177 myPresentation.addPropertyChangeListener(myActionButtonSynchronizer);
179 AnActionEvent e = new AnActionEvent(null, getDataContext(), myPlace, myPresentation, ActionManager.getInstance(), 0);
180 ActionUtil.performDumbAwareUpdate(myAction, e, false);
185 public void setToolTipText(String s) {
186 String tooltipText = KeymapUtil.createTooltipText(s, myAction);
187 super.setToolTipText(tooltipText.length() > 0 ? tooltipText : null);
190 public Dimension getPreferredSize() {
191 Icon icon = getIcon();
192 if (icon.getIconWidth() < myMinimumButtonSize.width &&
193 icon.getIconHeight() < myMinimumButtonSize.height) {
194 return myMinimumButtonSize;
197 return new Dimension(
198 Math.max(myMinimumButtonSize.width, icon.getIconWidth() + myInsets.left + myInsets.right),
199 Math.max(myMinimumButtonSize.height, icon.getIconHeight() + myInsets.top + myInsets.bottom)
205 public void setIconInsets(@Nullable Insets insets) {
206 myInsets = insets != null ? JBUI.insets(insets) : new Insets(0,0,0,0);
209 public Dimension getMinimumSize() {
210 return getPreferredSize();
214 * @return button's icon. Icon depends on action's state. It means that the method returns
215 * disabled icon if action is disabled. If the action's icon is <code>null</code> then it returns
218 protected Icon getIcon() {
219 Icon icon = isButtonEnabled() ? myIcon : myDisabledIcon;
226 public void updateIcon() {
227 myIcon = myPresentation.getIcon();
228 if (myPresentation.getDisabledIcon() != null) { // set disabled icon if it is specified
229 myDisabledIcon = myPresentation.getDisabledIcon();
232 myDisabledIcon = IconLoader.getDisabledIcon(myIcon);
236 private void setDisabledIcon(Icon icon) {
237 myDisabledIcon = icon;
240 void updateToolTipText() {
241 String text = myPresentation.getText();
242 setToolTipText(text == null ? myPresentation.getDescription() : text);
245 public void paintComponent(Graphics g) {
246 super.paintComponent(g);
250 if (myAction instanceof ActionGroup && ((ActionGroup)myAction).isPopup()) {
255 if (getPopState() == PUSHED) {
260 AllIcons.General.Dropdown.paintIcon(this, g, x, y);
264 protected void paintButtonLook(Graphics g) {
265 ActionButtonLook look = getButtonLook();
266 look.paintBackground(g, this);
267 look.paintIcon(g, this, getIcon());
268 look.paintBorder(g, this);
271 protected ActionButtonLook getButtonLook() {
275 public void setLook(ActionButtonLook look) {
280 myLook = ActionButtonLook.IDEA_LOOK;
285 protected void processMouseEvent(MouseEvent e) {
286 super.processMouseEvent(e);
287 if (e.isConsumed()) return;
288 boolean skipPress = e.isMetaDown() || e.getButton() != MouseEvent.BUTTON1;
290 case MouseEvent.MOUSE_PRESSED:
291 if (skipPress || !isButtonEnabled()) return;
293 ourGlobalMouseDown = true;
297 case MouseEvent.MOUSE_RELEASED:
298 if (skipPress || !isButtonEnabled()) return;
300 ourGlobalMouseDown = false;
307 case MouseEvent.MOUSE_ENTERED:
308 if (!myMouseDown && ourGlobalMouseDown) break;
311 onMousePresenceChanged(true);
314 case MouseEvent.MOUSE_EXITED:
316 if (!myMouseDown && ourGlobalMouseDown) break;
318 onMousePresenceChanged(false);
323 private int getPopState(boolean isPushed) {
324 if (isPushed || myRollover && myMouseDown && isButtonEnabled()) {
328 return !myRollover || !isButtonEnabled() ? NORMAL : POPPED;
332 public AnAction getAction() {
336 private class ActionButtonSynchronizer implements PropertyChangeListener {
337 @NonNls protected static final String SELECTED_PROPERTY_NAME = "selected";
339 public void propertyChange(PropertyChangeEvent e) {
340 String propertyName = e.getPropertyName();
341 if (Presentation.PROP_TEXT.equals(propertyName)) {
343 revalidate(); // recalc preferred size & repaint instantly
345 else if (Presentation.PROP_ENABLED.equals(propertyName)) {
349 else if (Presentation.PROP_ICON.equals(propertyName)) {
353 else if (Presentation.PROP_DISABLED_ICON.equals(propertyName)) {
354 setDisabledIcon(myPresentation.getDisabledIcon());
357 else if (Presentation.PROP_VISIBLE.equals(propertyName)) {
359 else if (SELECTED_PROPERTY_NAME.equals(propertyName)) {