/*
* Copyright 2000-2011 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.ide.DataManager;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.actionSystem.ex.ActionManagerEx;
import com.intellij.openapi.actionSystem.ex.ActionUtil;
import com.intellij.openapi.actionSystem.ex.CustomComponentAction;
import com.intellij.openapi.keymap.KeymapUtil;
import com.intellij.openapi.util.IconLoader;
import com.intellij.util.ui.EmptyIcon;
import com.intellij.util.ui.JBUI;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
public class ActionButton extends JComponent implements ActionButtonComponent, AnActionHolder {
private static final String uiClassID = "ActionButtonUI";
@Deprecated
private static final Icon ourEmptyIcon = EmptyIcon.ICON_18;
private static final int ourEmptyIconSize = 18;
private Dimension myMinimumButtonSize;
private PropertyChangeListener myActionButtonSynchronizer;
private Icon myDisabledIcon;
private Icon myIcon;
protected final Presentation myPresentation;
protected final AnAction myAction;
protected final String myPlace;
private boolean myMouseDown;
private boolean myRollover;
private static boolean ourGlobalMouseDown;
private boolean myNoIconsInPopup;
private Insets myInsets;
private boolean myMinimalMode;
private boolean myDecorateButtons;
public ActionButton(final AnAction action, final Presentation presentation, final String place, @NotNull final Dimension minimumSize) {
setMinimumButtonSize(minimumSize);
setIconInsets(null);
myRollover = false;
myMouseDown = false;
myAction = action;
myPresentation = presentation;
myPlace = place;
setFocusable(false);
enableEvents(AWTEvent.MOUSE_EVENT_MASK);
myMinimumButtonSize = minimumSize;
putClientProperty(UIUtil.CENTER_TOOLTIP_DEFAULT, Boolean.TRUE);
updateUI();
}
public void setMinimalMode(boolean minimalMode) {
myMinimalMode = minimalMode;
}
public void setDecorateButtons(boolean decorateButtons) {
myDecorateButtons = decorateButtons;
}
public boolean isMinimalMode() {
return myMinimalMode;
}
public boolean isDecorateButtons() {
return myDecorateButtons;
}
public void setNoIconsInPopup(boolean noIconsInPopup) {
myNoIconsInPopup = noIconsInPopup;
}
public void setMinimumButtonSize(@NotNull Dimension size) {
myMinimumButtonSize = size;
}
@Override
public int getPopState() {
if (myAction instanceof Toggleable) {
Boolean selected = (Boolean)myPresentation.getClientProperty(Toggleable.SELECTED_PROPERTY);
boolean flag1 = selected != null && selected;
return getPopState(flag1);
}
else {
return getPopState(false);
}
}
public boolean isButtonEnabled() {
return isEnabled() && myPresentation.isEnabled();
}
private void onMousePresenceChanged(boolean setInfo) {
ActionMenu.showDescriptionInStatusBar(setInfo, this, myPresentation.getDescription());
}
public void click() {
performAction(new MouseEvent(this, MouseEvent.MOUSE_CLICKED, System.currentTimeMillis(), 0, 0, 0, 1, false));
}
private void performAction(MouseEvent e) {
AnActionEvent event = new AnActionEvent(e, getDataContext(), myPlace, myPresentation, ActionManager.getInstance(), e.getModifiers());
if (!ActionUtil.lastUpdateAndCheckDumb(myAction, event, false)) {
return;
}
if (isButtonEnabled()) {
final ActionManagerEx manager = ActionManagerEx.getInstanceEx();
final DataContext dataContext = event.getDataContext();
manager.fireBeforeActionPerformed(myAction, dataContext, event);
Component component = PlatformDataKeys.CONTEXT_COMPONENT.getData(dataContext);
if (component != null && !component.isShowing()) {
return;
}
actionPerformed(event);
manager.queueActionPerformedEvent(myAction, dataContext, event);
}
}
protected DataContext getDataContext() {
ActionToolbar actionToolbar = UIUtil.getParentOfType(ActionToolbar.class, this);
return actionToolbar != null ? actionToolbar.getToolbarDataContext() : DataManager.getInstance().getDataContext();
}
private void actionPerformed(final AnActionEvent event) {
if (myAction instanceof ActionGroup && !(myAction instanceof CustomComponentAction) && ((ActionGroup)myAction).isPopup()) {
final ActionManagerImpl am = (ActionManagerImpl)ActionManager.getInstance();
ActionPopupMenuImpl popupMenu = (ActionPopupMenuImpl)am.createActionPopupMenu(event.getPlace(), (ActionGroup)myAction, new MenuItemPresentationFactory() {
@Override
protected void processPresentation(Presentation presentation) {
if (myNoIconsInPopup) {
presentation.setIcon(null);
presentation.setHoveredIcon(null);
}
}
});
popupMenu.setDataContextProvider(ActionButton.this::getDataContext);
if (ActionPlaces.isToolbarPlace(event.getPlace())) {
popupMenu.getComponent().show(this, 0, getHeight());
}
else {
popupMenu.getComponent().show(this, getWidth(), 0);
}
}
else {
ActionUtil.performActionDumbAware(myAction, event);
}
}
@Override
public void removeNotify() {
if (myActionButtonSynchronizer != null) {
myPresentation.removePropertyChangeListener(myActionButtonSynchronizer);
myActionButtonSynchronizer = null;
}
super.removeNotify();
}
@Override
public void addNotify() {
super.addNotify();
if (myActionButtonSynchronizer == null) {
myActionButtonSynchronizer = new ActionButtonSynchronizer();
myPresentation.addPropertyChangeListener(myActionButtonSynchronizer);
}
updateToolTipText();
updateIcon();
}
@Override
public void setToolTipText(String s) {
String tooltipText = KeymapUtil.createTooltipText(s, myAction);
super.setToolTipText(tooltipText.length() > 0 ? tooltipText : null);
}
@Override
public Dimension getPreferredSize() {
Icon icon = getIcon();
if (icon.getIconWidth() < myMinimumButtonSize.width && icon.getIconHeight() < myMinimumButtonSize.height) {
return myMinimumButtonSize;
}
else {
return new Dimension(icon.getIconWidth() + myInsets.left + myInsets.right, icon.getIconHeight() + myInsets.top + myInsets.bottom);
}
}
public void setIconInsets(@Nullable Insets insets) {
myInsets = insets != null ? insets : JBUI.emptyInsets();
}
@Override
public Dimension getMinimumSize() {
return getPreferredSize();
}
/**
* @return button's icon. Icon depends on action's state. It means that the method returns
* disabled icon if action is disabled. If the action's icon is <code>null</code> then it returns
* an empty icon.
*/
public Icon getIcon() {
Icon icon = isButtonEnabled() ? myIcon : myDisabledIcon;
if (icon == null) {
icon = JBUI.emptyIcon(ourEmptyIconSize);
}
return icon;
}
public void updateIcon() {
myIcon = myPresentation.getIcon();
if (myPresentation.getDisabledIcon() != null) { // set disabled icon if it is specified
myDisabledIcon = myPresentation.getDisabledIcon();
}
else {
myDisabledIcon = IconLoader.getDisabledIcon(myIcon);
}
}
private void setDisabledIcon(Icon icon) {
myDisabledIcon = icon;
}
void updateToolTipText() {
String text = myPresentation.getText();
setToolTipText(text == null ? myPresentation.getDescription() : text);
}
@Override
public String getUIClassID() {
return uiClassID;
}
@Override
public void updateUI() {
setUI(UIManager.getUI(this));
}
@Override
protected void processMouseEvent(MouseEvent e) {
super.processMouseEvent(e);
if (e.isConsumed()) return;
boolean skipPress = e.isMetaDown() || e.getButton() != MouseEvent.BUTTON1;
switch (e.getID()) {
case MouseEvent.MOUSE_PRESSED:
if (skipPress || !isButtonEnabled()) return;
myMouseDown = true;
ourGlobalMouseDown = true;
repaint();
break;
case MouseEvent.MOUSE_RELEASED:
if (skipPress || !isButtonEnabled()) return;
myMouseDown = false;
ourGlobalMouseDown = false;
if (myRollover) {
performAction(e);
}
repaint();
break;
case MouseEvent.MOUSE_ENTERED:
if (!myMouseDown && ourGlobalMouseDown) break;
myRollover = true;
repaint();
onMousePresenceChanged(true);
break;
case MouseEvent.MOUSE_EXITED:
myRollover = false;
if (!myMouseDown && ourGlobalMouseDown) break;
repaint();
onMousePresenceChanged(false);
break;
}
}
private int getPopState(boolean isPushed) {
if (isPushed || myRollover && myMouseDown && isButtonEnabled()) {
return PUSHED;
}
else {
return !myRollover || !isButtonEnabled() ? NORMAL : POPPED;
}
}
public Presentation getPresentation() {
return myPresentation;
}
@Override
public AnAction getAction() {
return myAction;
}
private class ActionButtonSynchronizer implements PropertyChangeListener {
@NonNls protected static final String SELECTED_PROPERTY_NAME = "selected";
@Override
public void propertyChange(PropertyChangeEvent e) {
String propertyName = e.getPropertyName();
switch (propertyName) {
case Presentation.PROP_TEXT:
updateToolTipText();
break;
case Presentation.PROP_ENABLED:
repaint();
break;
case Presentation.PROP_ICON:
updateIcon();
repaint();
break;
case Presentation.PROP_DISABLED_ICON:
setDisabledIcon(myPresentation.getDisabledIcon());
repaint();
break;
case Presentation.PROP_VISIBLE:
break;
case SELECTED_PROPERTY_NAME:
repaint();
break;
}
}
}
}