blob: ce3cb33deb90e0ca5c3b499802051eadd51737a5 [file] [log] [blame]
/*
* Copyright 2000-2013 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.designer;
import com.intellij.icons.AllIcons;
import com.intellij.ide.util.PropertiesComponent;
import com.intellij.openapi.actionSystem.*;
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.Project;
import com.intellij.openapi.ui.ThreeComponentsSplitter;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.wm.*;
import com.intellij.openapi.wm.ex.ToolWindowEx;
import com.intellij.openapi.wm.impl.AnchoredButton;
import com.intellij.openapi.wm.impl.InternalDecorator;
import com.intellij.openapi.wm.impl.StripeButtonUI;
import com.intellij.openapi.wm.impl.content.ToolWindowContentUi;
import com.intellij.ui.*;
import com.intellij.ui.components.panels.Wrapper;
import com.intellij.ui.tabs.TabsUtil;
import com.intellij.util.ui.EmptyIcon;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
/**
* @author Alexander Lobas
*/
public class LightToolWindow extends JPanel {
public static final String LEFT_MIN_KEY = "left";
public static final String RIGHT_MIN_KEY = "right";
public static final int MINIMIZE_WIDTH = 25;
private static final String IGNORE_WIDTH_KEY = "ignore_width";
private final LightToolWindowContent myContent;
private final JComponent myFocusedComponent;
private final ThreeComponentsSplitter myContentSplitter;
private ToolWindowAnchor myAnchor;
private final Project myProject;
private final LightToolWindowManager myManager;
private final PropertiesComponent myPropertiesComponent;
private boolean myShowContent;
private final String myShowStateKey;
private int myCurrentWidth;
private final String myWidthKey;
private final JPanel myMinimizeComponent;
private final AnchoredButton myMinimizeButton;
private final TogglePinnedModeAction myToggleAutoHideModeAction = new TogglePinnedModeAction();
private final ToggleDockModeAction myToggleDockModeAction = new ToggleDockModeAction();
private final ToggleFloatingModeAction myToggleFloatingModeAction = new ToggleFloatingModeAction();
private final ToggleSideModeAction myToggleSideModeAction = new ToggleSideModeAction();
private final ComponentListener myWidthListener = new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {
int width = isLeft() ? myContentSplitter.getFirstSize() : myContentSplitter.getLastSize();
if (width > 0 && width != myCurrentWidth && myContentSplitter.getInnerComponent().getClientProperty(IGNORE_WIDTH_KEY) == null) {
myCurrentWidth = width;
myPropertiesComponent.setValue(myWidthKey, Integer.toString(width));
}
}
};
public LightToolWindow(@NotNull LightToolWindowContent content,
@NotNull String title,
@NotNull Icon icon,
@NotNull JComponent component,
@NotNull JComponent focusedComponent,
@NotNull ThreeComponentsSplitter contentSplitter,
@Nullable ToolWindowAnchor anchor,
@NotNull LightToolWindowManager manager,
@NotNull Project project,
@NotNull PropertiesComponent propertiesComponent,
@NotNull String key,
int defaultWidth,
@Nullable AnAction[] actions) {
super(new BorderLayout());
myContent = content;
myFocusedComponent = focusedComponent;
myContentSplitter = contentSplitter;
myAnchor = anchor;
myProject = project;
myManager = manager;
myPropertiesComponent = propertiesComponent;
myShowStateKey = LightToolWindowManager.EDITOR_MODE + key + ".SHOW";
myWidthKey = LightToolWindowManager.EDITOR_MODE + key + ".WIDTH";
HeaderPanel header = new HeaderPanel();
header.setLayout(new BorderLayout());
add(header, BorderLayout.NORTH);
JLabel titleLabel = new JLabel(title);
titleLabel.setBorder(IdeBorderFactory.createEmptyBorder(2, 5, 2, 10));
titleLabel.setFont(UIUtil.getLabelFont(UIUtil.FontSize.SMALL));
header.add(titleLabel, BorderLayout.CENTER);
JPanel actionPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 5, 0));
actionPanel.setBorder(IdeBorderFactory.createEmptyBorder(3, 0, 2, 0));
actionPanel.setOpaque(false);
header.add(actionPanel, BorderLayout.EAST);
if (actions != null) {
for (AnAction action : actions) {
addAction(actionPanel, action);
}
actionPanel.add(new JLabel(AllIcons.General.Divider));
}
addAction(actionPanel, new GearAction());
addAction(actionPanel, new HideAction());
JPanel contentWrapper = new JPanel(new BorderLayout());
contentWrapper.setBorder(IdeBorderFactory.createBorder(SideBorder.TOP));
contentWrapper.add(component, BorderLayout.CENTER);
add(contentWrapper, BorderLayout.CENTER);
addMouseListener(new MouseAdapter() {
public void mouseReleased(final MouseEvent e) {
IdeFocusManager.getInstance(myProject).requestFocus(myFocusedComponent, true);
}
});
addMouseListener(new PopupHandler() {
public void invokePopup(Component component, int x, int y) {
showGearPopup(component, x, y);
}
});
myMinimizeButton = new AnchoredButton(title, icon) {
@Override
public void updateUI() {
setUI(StripeButtonUI.createUI(this));
setFont(UIUtil.getLabelFont(UIUtil.FontSize.SMALL));
}
@Override
public int getMnemonic2() {
return 0;
}
@Override
public ToolWindowAnchor getAnchor() {
return myAnchor;
}
};
myMinimizeButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
myMinimizeButton.setSelected(false);
updateContent(true, true);
}
});
myMinimizeButton.setBorder(BorderFactory.createEmptyBorder(5, 5, 0, 5));
myMinimizeButton.setFocusable(false);
myMinimizeButton.setRolloverEnabled(true);
myMinimizeButton.setOpaque(false);
myMinimizeComponent = new JPanel() {
@Override
public void doLayout() {
Dimension size = myMinimizeButton.getPreferredSize();
myMinimizeButton.setBounds(0, 0, getWidth(), size.height);
}
};
myMinimizeComponent.add(myMinimizeButton);
configureBorder();
configureWidth(defaultWidth);
updateContent(myPropertiesComponent.getBoolean(myShowStateKey, true), false);
}
private void configureBorder() {
int borderStyle = isLeft() ? SideBorder.RIGHT : SideBorder.LEFT;
setBorder(IdeBorderFactory.createBorder(borderStyle));
myMinimizeComponent.setBorder(IdeBorderFactory.createBorder(borderStyle));
}
private void configureWidth(int defaultWidth) {
myCurrentWidth = myPropertiesComponent.getOrInitInt(myWidthKey, defaultWidth);
updateWidth();
myContentSplitter.getInnerComponent().addComponentListener(myWidthListener);
}
private void updateWidth() {
if (isLeft()) {
myContentSplitter.setFirstSize(myCurrentWidth);
}
else {
myContentSplitter.setLastSize(myCurrentWidth);
}
}
public void updateAnchor(ToolWindowAnchor newAnchor) {
JComponent minimizeParent = myContentSplitter.getInnerComponent();
minimizeParent.putClientProperty(IGNORE_WIDTH_KEY, Boolean.TRUE);
if (myShowContent) {
Object oldWindow = isLeft() ? myContentSplitter.getFirstComponent() : myContentSplitter.getLastComponent();
if (oldWindow == this) {
setContentComponent(null);
}
}
else {
String key = getMinKey();
if (minimizeParent.getClientProperty(key) == myMinimizeComponent) {
minimizeParent.putClientProperty(key, null);
}
minimizeParent.putClientProperty(isLeft() ? RIGHT_MIN_KEY : LEFT_MIN_KEY, myMinimizeComponent);
minimizeParent.revalidate();
}
myAnchor = newAnchor;
configureBorder();
updateWidth();
if (myShowContent) {
setContentComponent(this);
}
minimizeParent.putClientProperty(IGNORE_WIDTH_KEY, null);
}
private void updateContent(boolean show, boolean flag) {
myShowContent = show;
String key = getMinKey();
JComponent minimizeParent = myContentSplitter.getInnerComponent();
if (show) {
minimizeParent.putClientProperty(key, null);
minimizeParent.remove(myMinimizeComponent);
}
setContentComponent(show ? this : null);
if (!show) {
minimizeParent.putClientProperty(key, myMinimizeComponent);
minimizeParent.add(myMinimizeComponent);
}
minimizeParent.revalidate();
if (flag) {
myPropertiesComponent.setValue(myShowStateKey, Boolean.toString(show));
}
}
private void setContentComponent(JComponent component) {
if (isLeft()) {
myContentSplitter.setFirstComponent(component);
}
else {
myContentSplitter.setLastComponent(component);
}
}
public void dispose() {
JComponent minimizeParent = myContentSplitter.getInnerComponent();
minimizeParent.removeComponentListener(myWidthListener);
setContentComponent(null);
myContent.dispose();
if (!myShowContent) {
minimizeParent.putClientProperty(getMinKey(), null);
minimizeParent.remove(myMinimizeComponent);
minimizeParent.revalidate();
}
}
private String getMinKey() {
return isLeft() ? LEFT_MIN_KEY : RIGHT_MIN_KEY;
}
public Object getContent() {
return myContent;
}
private boolean isLeft() {
return myAnchor == ToolWindowAnchor.LEFT;
}
private boolean isActive() {
IdeFocusManager fm = IdeFocusManager.getInstance(myProject);
Component component = fm.getFocusedDescendantFor(this);
if (component != null) {
return true;
}
Component owner = fm.getLastFocusedFor(WindowManager.getInstance().getIdeFrame(myProject));
return owner != null && SwingUtilities.isDescendingFrom(owner, this);
}
private void addAction(JPanel actionPanel, AnAction action) {
actionPanel.add(new ActionButton(action));
}
private DefaultActionGroup createGearPopupGroup() {
DefaultActionGroup group = new DefaultActionGroup();
group.add(myManager.createGearActions());
group.addSeparator();
ToolWindowType type = myManager.getToolWindow().getType();
if (type == ToolWindowType.DOCKED) {
group.add(myToggleAutoHideModeAction);
group.add(myToggleDockModeAction);
group.add(myToggleFloatingModeAction);
group.add(myToggleSideModeAction);
}
else if (type == ToolWindowType.FLOATING) {
group.add(myToggleAutoHideModeAction);
group.add(myToggleFloatingModeAction);
}
else if (type == ToolWindowType.SLIDING) {
group.add(myToggleDockModeAction);
group.add(myToggleFloatingModeAction);
}
return group;
}
private void showGearPopup(Component component, int x, int y) {
ActionPopupMenu popupMenu =
((ActionManagerImpl)ActionManager.getInstance())
.createActionPopupMenu(ToolWindowContentUi.POPUP_PLACE, createGearPopupGroup(), new MenuItemPresentationFactory(true));
popupMenu.getComponent().show(component, x, y);
}
private class GearAction extends AnAction {
public GearAction() {
Presentation presentation = getTemplatePresentation();
presentation.setIcon(AllIcons.General.Gear);
presentation.setHoveredIcon(AllIcons.General.GearHover);
}
@Override
public void actionPerformed(AnActionEvent e) {
int x = 0;
int y = 0;
InputEvent inputEvent = e.getInputEvent();
if (inputEvent instanceof MouseEvent) {
x = ((MouseEvent)inputEvent).getX();
y = ((MouseEvent)inputEvent).getY();
}
showGearPopup(inputEvent.getComponent(), x, y);
}
}
private class HideAction extends AnAction {
public HideAction() {
Presentation presentation = getTemplatePresentation();
presentation.setText(UIBundle.message("tool.window.hide.action.name"));
if (isLeft()) {
presentation.setIcon(AllIcons.General.HideLeftPart);
presentation.setHoveredIcon(AllIcons.General.HideLeftPartHover);
}
else {
presentation.setIcon(AllIcons.General.HideRightPart);
presentation.setHoveredIcon(AllIcons.General.HideRightPartHover);
}
}
@Override
public void actionPerformed(AnActionEvent e) {
updateContent(false, true);
}
}
private class TogglePinnedModeAction extends ToggleAction {
public TogglePinnedModeAction() {
copyFrom(ActionManager.getInstance().getAction(InternalDecorator.TOGGLE_PINNED_MODE_ACTION_ID));
}
@Override
public boolean isSelected(AnActionEvent e) {
return !myManager.getToolWindow().isAutoHide();
}
@Override
public void setSelected(AnActionEvent e, boolean state) {
ToolWindow window = myManager.getToolWindow();
window.setAutoHide(!window.isAutoHide());
myManager.setEditorMode(null);
}
}
private class ToggleDockModeAction extends ToggleAction {
public ToggleDockModeAction() {
copyFrom(ActionManager.getInstance().getAction(InternalDecorator.TOGGLE_DOCK_MODE_ACTION_ID));
}
@Override
public boolean isSelected(AnActionEvent e) {
return myManager.getToolWindow().getType() == ToolWindowType.DOCKED;
}
@Override
public void setSelected(AnActionEvent e, boolean state) {
ToolWindow window = myManager.getToolWindow();
ToolWindowType type = window.getType();
if (type == ToolWindowType.DOCKED) {
window.setType(ToolWindowType.SLIDING, null);
}
else if (type == ToolWindowType.SLIDING) {
window.setType(ToolWindowType.DOCKED, null);
}
myManager.setEditorMode(null);
}
}
private class ToggleFloatingModeAction extends ToggleAction {
public ToggleFloatingModeAction() {
copyFrom(ActionManager.getInstance().getAction(InternalDecorator.TOGGLE_FLOATING_MODE_ACTION_ID));
}
@Override
public boolean isSelected(AnActionEvent e) {
return myManager.getToolWindow().getType() == ToolWindowType.FLOATING;
}
@Override
public void setSelected(AnActionEvent e, boolean state) {
ToolWindow window = myManager.getToolWindow();
ToolWindowType type = window.getType();
if (type == ToolWindowType.FLOATING) {
window.setType(((ToolWindowEx)window).getInternalType(), null);
}
else {
window.setType(ToolWindowType.FLOATING, null);
}
myManager.setEditorMode(null);
}
}
private class ToggleSideModeAction extends ToggleAction {
public ToggleSideModeAction() {
copyFrom(ActionManager.getInstance().getAction(InternalDecorator.TOGGLE_SIDE_MODE_ACTION_ID));
}
@Override
public boolean isSelected(AnActionEvent e) {
return myManager.getToolWindow().isSplitMode();
}
@Override
public void setSelected(AnActionEvent e, boolean state) {
myManager.getToolWindow().setSplitMode(state, null);
myManager.setEditorMode(null);
}
}
private class ActionButton extends Wrapper implements ActionListener {
private final AnAction myAction;
public ActionButton(AnAction action) {
myAction = action;
Presentation presentation = action.getTemplatePresentation();
InplaceButton button = new InplaceButton(KeymapUtil.createTooltipText(presentation.getText(), action), EmptyIcon.ICON_16, this) {
@Override
public boolean isActive() {
return LightToolWindow.this.isActive();
}
};
button.setHoveringEnabled(!SystemInfo.isMac);
setContent(button);
Icon icon = presentation.getIcon();
Icon hoveredIcon = presentation.getHoveredIcon();
button.setIcons(icon, icon, hoveredIcon == null ? icon : hoveredIcon);
}
@Override
public void actionPerformed(ActionEvent e) {
InputEvent inputEvent = e.getSource() instanceof InputEvent ? (InputEvent)e.getSource() : null;
myAction.actionPerformed(AnActionEvent.createFromInputEvent(myAction, inputEvent, ActionPlaces.UNKNOWN));
}
}
private class HeaderPanel extends JPanel {
private BufferedImage myActiveImage;
private BufferedImage myImage;
@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());
}
protected void _paintComponent(Graphics g) { // XXX: visual artifacts on linux
Rectangle r = getBounds();
Image image;
if (isActive()) {
if (myActiveImage == null || myActiveImage.getHeight() != r.height) {
myActiveImage = drawToBuffer(true, r.height);
}
image = myActiveImage;
}
else {
if (myImage == null || myImage.getHeight() != r.height) {
myImage = drawToBuffer(false, r.height);
}
image = myImage;
}
Graphics2D g2d = (Graphics2D)g;
Rectangle clipBounds = g2d.getClip().getBounds();
for (int x = clipBounds.x; x < clipBounds.x + clipBounds.width; x += 150) {
g2d.drawImage(image, x, 0, null);
}
}
protected boolean isActive() {
return LightToolWindow.this.isActive();
}
}
private static BufferedImage drawToBuffer(boolean active, int height) {
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, false, false);
g.dispose();
return image;
}
}