blob: e84028ead618c3c1eeff43400bd75fb72d985d4c [file] [log] [blame]
/*
* 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.wm.impl;
import com.intellij.icons.AllIcons;
import com.intellij.ide.DataManager;
import com.intellij.ide.ui.UISettings;
import com.intellij.ide.ui.UISettingsListener;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.actionSystem.ex.ActionManagerEx;
import com.intellij.openapi.actionSystem.impl.ActionManagerImpl;
import com.intellij.openapi.actionSystem.impl.MenuItemPresentationFactory;
import com.intellij.openapi.keymap.KeymapUtil;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.wm.ToolWindow;
import com.intellij.openapi.wm.ToolWindowAnchor;
import com.intellij.openapi.wm.ToolWindowType;
import com.intellij.openapi.wm.impl.content.ToolWindowContentUi;
import com.intellij.ui.InplaceButton;
import com.intellij.ui.PopupHandler;
import com.intellij.ui.UIBundle;
import com.intellij.ui.components.panels.Wrapper;
import com.intellij.ui.tabs.TabsUtil;
import com.intellij.util.Producer;
import com.intellij.util.ui.EmptyIcon;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.plaf.PanelUI;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
/**
* @author pegov
*/
public abstract class ToolWindowHeader extends JPanel implements Disposable, UISettingsListener {
@NonNls private static final String HIDE_ACTIVE_WINDOW_ACTION_ID = "HideActiveWindow";
@NonNls private static final String HIDE_ACTIVE_SIDE_WINDOW_ACTION_ID = "HideSideWindows";
private ToolWindow myToolWindow;
private WindowInfoImpl myInfo;
private final ToolWindowHeader.ActionButton myHideButton;
private BufferedImage myImage;
private BufferedImage myActiveImage;
private ToolWindowType myImageType;
private final JPanel myButtonPanel;
private final ToolWindowHeader.ActionButton myGearButton;
public ToolWindowHeader(final ToolWindowImpl toolWindow, @NotNull WindowInfoImpl info, @NotNull final Producer<ActionGroup> gearProducer) {
setLayout(new BorderLayout());
myToolWindow = toolWindow;
myInfo = info;
JPanel westPanel = new JPanel() {
@Override
public void doLayout() {
if (getComponentCount() > 0) {
Rectangle r = getBounds();
Insets insets = getInsets();
Component c = getComponent(0);
Dimension size = c.getPreferredSize();
if (size.width < (r.width - insets.left - insets.right)) {
c.setBounds(insets.left, insets.top, r.width, r.height - insets.top - insets.bottom);
} else {
c.setBounds(insets.left, insets.top, r.width - insets.left - insets.right, r.height - insets.top - insets.bottom);
}
}
}
};
westPanel.setOpaque(false);
add(westPanel, BorderLayout.CENTER);
westPanel.add(toolWindow.getContentUI().getTabComponent());
JPanel eastPanel = new JPanel();
eastPanel.setOpaque(false);
eastPanel.setLayout(new BoxLayout(eastPanel, BoxLayout.X_AXIS));
eastPanel.setBorder(BorderFactory.createEmptyBorder(0, 3, 0, 3));
add(eastPanel, BorderLayout.EAST);
myGearButton = new ActionButton(new AnAction() {
@Override
public void actionPerformed(AnActionEvent e) {
final InputEvent inputEvent = e.getInputEvent();
final ActionPopupMenu popupMenu =
((ActionManagerImpl)ActionManager.getInstance())
.createActionPopupMenu(ToolWindowContentUi.POPUP_PLACE, gearProducer.produce(), new MenuItemPresentationFactory(true));
int x = 0;
int y = 0;
if (inputEvent instanceof MouseEvent) {
x = ((MouseEvent)inputEvent).getX();
y = ((MouseEvent)inputEvent).getY();
}
popupMenu.getComponent().show(inputEvent.getComponent(), x, y);
}
}, AllIcons.General.Gear) {
@Override
protected Icon getActiveHoveredIcon() {
return AllIcons.General.GearHover;
}
};
myHideButton = new ActionButton(new HideAction() {
@Override
public void actionPerformed(AnActionEvent e) {
hideToolWindow();
}
}, new HideSideAction() {
@Override
public void actionPerformed(AnActionEvent e) {
sideHidden();
}
},
AllIcons.General.HideLeft, null, null
) {
@Override
protected Icon getActiveIcon() {
return getHideToolWindowIcon(myToolWindow);
}
@Override
protected Icon getAlternativeIcon() {
return getHideIcon(myToolWindow);
}
@Override
protected Icon getActiveHoveredIcon() {
return getHideToolWindowHoveredIcon(myToolWindow);
}
@Override
protected Icon getAlternativeHoveredIcon() {
return getHideHoveredIcon(myToolWindow);
}
};
addDefaultActions(eastPanel);
myButtonPanel = eastPanel;
addMouseListener(new PopupHandler() {
public void invokePopup(final Component comp, final int x, final int y) {
toolWindow.getContentUI().showContextMenu(comp, x, y, toolWindow.getPopupGroup(), toolWindow.getContentManager().getSelectedContent());
}
});
addMouseListener(new MouseAdapter() {
public void mouseReleased(final MouseEvent e) {
if (!e.isPopupTrigger()) {
if (UIUtil.isCloseClick(e, MouseEvent.MOUSE_RELEASED)) {
if (e.isAltDown()) {
toolWindow.fireHidden();
}
else {
toolWindow.fireHiddenSide();
}
}
else {
toolWindow.fireActivated();
}
}
}
});
setOpaque(true);
setBorder(BorderFactory.createEmptyBorder(TabsUtil.TABS_BORDER, 1, TabsUtil.TABS_BORDER, 1));
UISettings.getInstance().addUISettingsListener(this, toolWindow.getContentUI());
}
@Override
public void uiSettingsChanged(UISettings source) {
clearCaches();
}
private void addDefaultActions(JPanel eastPanel) {
eastPanel.add(myGearButton);
eastPanel.add(Box.createHorizontalStrut(6));
eastPanel.add(myHideButton);
eastPanel.add(Box.createHorizontalStrut(1));
}
@Override
public void dispose() {
removeAll();
myToolWindow = null;
myInfo = null;
}
public void updateTooltips() {
if (myHideButton != null) {
myHideButton.updateTooltip();
}
}
public void setAdditionalTitleActions(AnAction[] actions) {
myButtonPanel.removeAll();
boolean actionAdded = false;
for (final AnAction action : actions) {
if (action == null) continue;
myButtonPanel.add(new ActionButton(action, action.getTemplatePresentation().getIcon()) {
@Override
protected Icon getActiveHoveredIcon() {
final Icon icon = action.getTemplatePresentation().getHoveredIcon();
return icon != null ? icon : super.getActiveHoveredIcon();
}
});
myButtonPanel.add(Box.createHorizontalStrut(9));
actionAdded = true;
}
if (actionAdded) {
myButtonPanel.add(new JLabel(AllIcons.General.Divider));
myButtonPanel.add(Box.createHorizontalStrut(6));
}
addDefaultActions(myButtonPanel);
}
private static Icon getHideToolWindowIcon(ToolWindow toolWindow) {
ToolWindowAnchor anchor = toolWindow.getAnchor();
if (anchor == ToolWindowAnchor.BOTTOM) {
return AllIcons.General.HideDownPart;
}
else if (anchor == ToolWindowAnchor.RIGHT) {
return AllIcons.General.HideRightPart;
}
return AllIcons.General.HideLeftPart;
}
private static Icon getHideIcon(ToolWindow toolWindow) {
ToolWindowAnchor anchor = toolWindow.getAnchor();
if (anchor == ToolWindowAnchor.BOTTOM) {
return AllIcons.General.HideDown;
}
else if (anchor == ToolWindowAnchor.RIGHT) {
return AllIcons.General.HideRight;
}
return AllIcons.General.HideLeft;
}
private static Icon getHideToolWindowHoveredIcon(ToolWindow toolWindow) {
ToolWindowAnchor anchor = toolWindow.getAnchor();
if (anchor == ToolWindowAnchor.BOTTOM) {
return AllIcons.General.HideDownPartHover;
}
else if (anchor == ToolWindowAnchor.RIGHT) {
return AllIcons.General.HideRightPartHover;
}
return AllIcons.General.HideLeftPartHover;
}
private static Icon getHideHoveredIcon(ToolWindow toolWindow) {
ToolWindowAnchor anchor = toolWindow.getAnchor();
if (anchor == ToolWindowAnchor.BOTTOM) {
return AllIcons.General.HideDownHover;
}
else if (anchor == ToolWindowAnchor.RIGHT) {
return AllIcons.General.HideRightHover;
}
return AllIcons.General.HideLeftHover;
}
@Override
protected void paintComponent(Graphics g) {
Rectangle r = getBounds();
Graphics2D g2d = (Graphics2D)g;
Shape clip = g2d.getClip();
ToolWindowType type = myToolWindow.getType();
Image image;
if (isActive()) {
if (myActiveImage == null || /*myActiveImage.getHeight() != r.height ||*/ type != myImageType) {
myActiveImage = drawToBuffer(true, r.height, myToolWindow.getType() == ToolWindowType.FLOATING);
}
image = myActiveImage;
} else {
if (myImage == null || /*myImage.getHeight() != r.height ||*/ type != myImageType) {
myImage = drawToBuffer(false, r.height, myToolWindow.getType() == ToolWindowType.FLOATING);
}
image = myImage;
}
myImageType = myToolWindow.getType();
Rectangle clipBounds = clip.getBounds();
for (int x = clipBounds.x; x < clipBounds.x + clipBounds.width; x+=150) {
UIUtil.drawImage(g, image, x, 0, null);
}
}
private static BufferedImage drawToBuffer(boolean active, int height, boolean floating) {
final int width = 150;
BufferedImage image = UIUtil.createImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = image.createGraphics();
UIUtil.drawHeader(g, 0, width, height, active, true, !floating, true);
g.dispose();
return image;
}
@Override
public void setUI(PanelUI ui) {
clearCaches();
super.setUI(ui);
}
public void clearCaches() {
myImage = null;
myActiveImage = null;
}
@Override
protected void paintChildren(Graphics g) {
Graphics2D graphics = (Graphics2D) g.create();
UIUtil.applyRenderingHints(graphics);
super.paintChildren(graphics);
Rectangle r = getBounds();
if (!isActive() && !UIUtil.isUnderDarcula()) {
graphics.setColor(new Color(255, 255, 255, 30));
graphics.fill(r);
}
graphics.dispose();
}
protected abstract boolean isActive();
protected abstract void hideToolWindow();
protected abstract void sideHidden();
protected abstract void toolWindowTypeChanged(ToolWindowType type);
@Override
public Dimension getPreferredSize() {
Dimension size = super.getPreferredSize();
return new Dimension(size.width, TabsUtil.getTabsHeight());
}
@Override
public Dimension getMinimumSize() {
Dimension size = super.getMinimumSize();
return new Dimension(size.width, TabsUtil.getTabsHeight());
}
private class ActionButton extends Wrapper implements ActionListener, AltStateManager.AltListener {
private final InplaceButton myButton;
private final AnAction myAction;
private final AnAction myAlternativeAction;
private final Icon myActiveIcon;
private final Icon myInactiveIcon;
private final Icon myAlternativeIcon;
private AnAction myCurrentAction;
public ActionButton(AnAction action, AnAction alternativeAction, @NotNull Icon activeIcon, Icon inactiveIcon,
Icon alternativeIcon) {
myAction = action;
myAlternativeAction = alternativeAction;
myActiveIcon = activeIcon;
myInactiveIcon = inactiveIcon;
myAlternativeIcon = alternativeIcon;
myCurrentAction = myAction;
myButton = new InplaceButton(getToolTipTextByAction(action),
EmptyIcon.ICON_16, this) {
@Override
public boolean isActive() {
return ActionButton.this.isActive();
}
};
myButton.setHoveringEnabled(!SystemInfo.isMac);
setContent(myButton);
setOpaque(false);
setIcon(getActiveIcon(), getInactiveIcon() == null ? getActiveIcon() : getInactiveIcon(), getActiveHoveredIcon());
PropertyChangeListener listener = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (myAlternativeAction == null) return;
if ("ancestor".equals(evt.getPropertyName())) {
if (evt.getNewValue() == null) {
AltStateManager.getInstance().removeListener(ActionButton.this);
switchAlternativeAction(false);
}
else {
AltStateManager.getInstance().addListener(ActionButton.this);
}
}
}
};
addPropertyChangeListener(listener);
}
public void updateTooltip() {
myButton.setToolTipText(getToolTipTextByAction(myCurrentAction));
}
protected Icon getActiveIcon() {
return myActiveIcon;
}
protected Icon getActiveHoveredIcon() {
return myActiveIcon;
}
protected Icon getInactiveIcon() {
return myInactiveIcon;
}
protected Icon getAlternativeIcon() {
return myAlternativeIcon;
}
protected Icon getAlternativeHoveredIcon() {
return myAlternativeIcon;
}
private void switchAlternativeAction(boolean b) {
if (b && myCurrentAction == myAlternativeAction) return;
if (!b && myCurrentAction != myAlternativeAction) return;
setIcon(b ? getAlternativeIcon() : getActiveIcon(),
b ? getAlternativeIcon() : getInactiveIcon() == null ? getActiveIcon() : getInactiveIcon(),
b ? getAlternativeHoveredIcon() : getActiveHoveredIcon());
myCurrentAction = b ? myAlternativeAction : myAction;
setToolTipText(getToolTipTextByAction(myCurrentAction));
repaint();
}
public ActionButton(AnAction action, @NotNull Icon activeIcon, Icon inactiveIcon) {
this(action, null, activeIcon, inactiveIcon, null);
}
public ActionButton(AnAction action, Icon activeIcon) {
this(action, activeIcon, activeIcon);
}
public void actionPerformed(final ActionEvent e) {
AnAction action =
myAlternativeAction != null && (e.getModifiers() & InputEvent.ALT_MASK) == InputEvent.ALT_MASK ? myAlternativeAction : myAction;
final DataContext dataContext = DataManager.getInstance().getDataContext(this);
final ActionManagerEx actionManager = ActionManagerEx.getInstanceEx();
InputEvent inputEvent = e.getSource() instanceof InputEvent ? (InputEvent) e.getSource() : null;
final AnActionEvent event =
new AnActionEvent(inputEvent, dataContext, ActionPlaces.UNKNOWN, action.getTemplatePresentation(),
ActionManager.getInstance(),
0);
actionManager.fireBeforeActionPerformed(action, dataContext, event);
final Component component = PlatformDataKeys.CONTEXT_COMPONENT.getData(dataContext);
if (component != null && !component.isShowing()) {
return;
}
action.actionPerformed(event);
}
public boolean isActive() {
return ToolWindowHeader.this.isActive();
}
public void setIcon(final Icon active, Icon inactive, Icon hovered) {
myButton.setIcons(active, inactive, hovered);
}
public void setToolTipText(final String text) {
myButton.setToolTipText(text);
}
@Override
public void altPressed() {
PointerInfo info = MouseInfo.getPointerInfo();
if (info != null) {
Point p = info.getLocation();
SwingUtilities.convertPointFromScreen(p, this);
switchAlternativeAction(myButton.getBounds().contains(p));
}
}
@Override
public void altReleased() {
switchAlternativeAction(false);
}
}
private static String getToolTipTextByAction(AnAction action) {
String text = KeymapUtil.createTooltipText(action.getTemplatePresentation().getText(), action);
if (action instanceof HideAction) {
text += String.format(" (Click with %s to Hide Side)", KeymapUtil.getShortcutText(KeyboardShortcut.fromString("pressed ALT")));
}
return text;
}
private abstract class HideSideAction extends AnAction implements DumbAware {
@NonNls public static final String HIDE_ACTIVE_SIDE_WINDOW_ACTION_ID = ToolWindowHeader.HIDE_ACTIVE_SIDE_WINDOW_ACTION_ID;
public HideSideAction() {
copyFrom(ActionManager.getInstance().getAction(HIDE_ACTIVE_SIDE_WINDOW_ACTION_ID));
getTemplatePresentation().setText(UIBundle.message("tool.window.hideSide.action.name"));
}
public abstract void actionPerformed(final AnActionEvent e);
public final void update(final AnActionEvent event) {
final Presentation presentation = event.getPresentation();
presentation.setEnabled(myInfo.isVisible());
}
}
private abstract class HideAction extends AnAction implements DumbAware {
@NonNls public static final String HIDE_ACTIVE_WINDOW_ACTION_ID = ToolWindowHeader.HIDE_ACTIVE_WINDOW_ACTION_ID;
public HideAction() {
copyFrom(ActionManager.getInstance().getAction(HIDE_ACTIVE_WINDOW_ACTION_ID));
getTemplatePresentation().setText(UIBundle.message("tool.window.hide.action.name"));
}
public final void update(final AnActionEvent event) {
final Presentation presentation = event.getPresentation();
presentation.setEnabled(myInfo.isVisible());
}
}
}