blob: dae10849c07e645d5dffc31bffd79fe62f4a0339 [file] [log] [blame]
/*
* 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.icons.AllIcons;
import com.intellij.ide.DataManager;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.actionSystem.ex.ActionButtonLook;
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.Getter;
import com.intellij.openapi.util.IconLoader;
import com.intellij.util.ui.EmptyIcon;
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 Insets ICON_INSETS = new Insets(2, 2, 2, 2);
private static final Icon ourEmptyIcon = EmptyIcon.ICON_18;
private Dimension myMinimumButtonSize;
private PropertyChangeListener myActionButtonSynchronizer;
private Icon myDisabledIcon;
private Icon myIcon;
protected final Presentation myPresentation;
protected final AnAction myAction;
private final String myPlace;
private ActionButtonLook myLook = ActionButtonLook.IDEA_LOOK;
private boolean myMouseDown;
private boolean myRollover;
private static boolean ourGlobalMouseDown = false;
private boolean myNoIconsInPopup = false;
private Insets myInsets;
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);
}
public void setNoIconsInPopup(boolean noIconsInPopup) {
myNoIconsInPopup = noIconsInPopup;
}
public void setMinimumButtonSize(@NotNull Dimension size) {
myMinimumButtonSize = size;
}
public void paintChildren(Graphics g) {}
public int getPopState() {
if (myAction instanceof Toggleable) {
Boolean selected = (Boolean)myPresentation.getClientProperty(Toggleable.SELECTED_PROPERTY);
boolean flag1 = selected != null && selected.booleanValue();
return getPopState(flag1);
}
else {
return getPopState(false);
}
}
protected 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(new Getter<DataContext>() {
@Override
public DataContext get() {
return 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);
}
}
public void removeNotify() {
if (myActionButtonSynchronizer != null) {
myPresentation.removePropertyChangeListener(myActionButtonSynchronizer);
myActionButtonSynchronizer = null;
}
super.removeNotify();
}
public void addNotify() {
super.addNotify();
if (myActionButtonSynchronizer == null) {
myActionButtonSynchronizer = new ActionButtonSynchronizer();
myPresentation.addPropertyChangeListener(myActionButtonSynchronizer);
}
AnActionEvent e = new AnActionEvent(null, getDataContext(), myPlace, myPresentation, ActionManager.getInstance(), 0);
ActionUtil.performDumbAwareUpdate(myAction, e, false);
updateToolTipText();
updateIcon();
}
public void setToolTipText(String s) {
String tooltipText = KeymapUtil.createTooltipText(s, myAction);
super.setToolTipText(tooltipText.length() > 0 ? tooltipText : null);
}
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 : new Insets(0,0,0,0);
}
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.
*/
protected Icon getIcon() {
Icon icon = isButtonEnabled() ? myIcon : myDisabledIcon;
if (icon == null) {
icon = ourEmptyIcon;
}
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);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
paintButtonLook(g);
if (myAction instanceof ActionGroup && ((ActionGroup)myAction).isPopup()) {
int x = 5;
int y = 4;
if (getPopState() == PUSHED) {
x++;
y++;
}
AllIcons.General.Dropdown.paintIcon(this, g, x, y);
}
}
protected void paintButtonLook(Graphics g) {
ActionButtonLook look = getButtonLook();
look.paintBackground(g, this);
look.paintIcon(g, this, getIcon());
look.paintBorder(g, this);
}
protected ActionButtonLook getButtonLook() {
return myLook;
}
public void setLook(ActionButtonLook look) {
if (look != null) {
myLook = look;
}
else {
myLook = ActionButtonLook.IDEA_LOOK;
}
repaint();
}
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 AnAction getAction() {
return myAction;
}
private class ActionButtonSynchronizer implements PropertyChangeListener {
@NonNls protected static final String SELECTED_PROPERTY_NAME = "selected";
public void propertyChange(PropertyChangeEvent e) {
String propertyName = e.getPropertyName();
if (Presentation.PROP_TEXT.equals(propertyName)) {
updateToolTipText();
}
else if (Presentation.PROP_ENABLED.equals(propertyName)) {
updateIcon();
repaint();
}
else if (Presentation.PROP_ICON.equals(propertyName)) {
updateIcon();
repaint();
}
else if (Presentation.PROP_DISABLED_ICON.equals(propertyName)) {
setDisabledIcon(myPresentation.getDisabledIcon());
repaint();
}
else if (Presentation.PROP_VISIBLE.equals(propertyName)) {
}
else if (SELECTED_PROPERTY_NAME.equals(propertyName)) {
repaint();
}
}
}
}