blob: 62246de04248745d6ea63f66c02853d5a76f232e [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.openapi.wm.impl.content;
import com.intellij.openapi.ui.JBPopupMenu;
import com.intellij.openapi.ui.popup.ListPopup;
import com.intellij.ui.UIBundle;
import com.intellij.ui.awt.RelativeRectangle;
import com.intellij.ui.content.Content;
import com.intellij.ui.content.ContentManager;
import com.intellij.ui.content.ContentManagerEvent;
import com.intellij.util.ui.BaseButtonBehavior;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
class TabContentLayout extends ContentLayout {
static final int MORE_ICON_BORDER = 6;
LayoutData myLastLayout;
JPopupMenu myPopup;
final PopupMenuListener myPopupListener;
ArrayList<ContentTabLabel> myTabs = new ArrayList<ContentTabLabel>();
final Map<Content, ContentTabLabel> myContent2Tabs = new HashMap<Content, ContentTabLabel>();
private Map<String, BufferedImage> myCached = new com.intellij.util.containers.HashMap<String, BufferedImage>();
private final MoreIcon myMoreIcon = new MoreIcon() {
protected Rectangle getIconRec() {
return myLastLayout.moreRect;
}
protected boolean isActive() {
return myUi.myWindow.isActive();
}
protected int getIconY(final Rectangle iconRec) {
return iconRec.height / TAB_ARC - getIconHeight() / TAB_ARC;
}
};
TabContentLayout(ToolWindowContentUi ui) {
super(ui);
myPopupListener = new MyPopupListener();
new BaseButtonBehavior(myUi) {
protected void execute(final MouseEvent e) {
if (!myUi.isCurrent(TabContentLayout.this)) return;
if (myLastLayout != null) {
final Rectangle moreRect = myLastLayout.moreRect;
if (moreRect != null && moreRect.contains(e.getPoint())) {
showPopup();
}
}
}
};
}
@Override
public void init() {
reset();
myIdLabel = new BaseLabel(myUi, false) {
@Override
protected boolean allowEngravement() {
return myUi.myWindow.isActive();
}
};
for (int i = 0; i < myUi.myManager.getContentCount(); i++) {
contentAdded(new ContentManagerEvent(this, myUi.myManager.getContent(i), i));
}
}
@Override
public void reset() {
myTabs.clear();
myContent2Tabs.clear();
myIdLabel = null;
}
private void showPopup() {
myPopup = new JBPopupMenu();
myPopup.addPopupMenuListener(myPopupListener);
ArrayList<ContentTabLabel> tabs = myTabs;
for (final ContentTabLabel each : tabs) {
final JCheckBoxMenuItem item = new JCheckBoxMenuItem(each.getText());
if (myUi.myManager.isSelected(each.myContent)) {
item.setSelected(true);
}
item.addActionListener(new ActionListener() {
public void actionPerformed(final ActionEvent e) {
myUi.myManager.setSelectedContent(each.myContent, true);
}
});
myPopup.add(item);
}
myPopup.show(myUi, myLastLayout.moreRect.x, myLastLayout.moreRect.y);
}
private class MyPopupListener implements PopupMenuListener {
public void popupMenuWillBecomeVisible(final PopupMenuEvent e) {
}
public void popupMenuWillBecomeInvisible(final PopupMenuEvent e) {
if (myPopup != null) {
myPopup.removePopupMenuListener(this);
}
myPopup = null;
}
public void popupMenuCanceled(final PopupMenuEvent e) {
}
}
@Override
public void layout() {
Rectangle bounds = myUi.getBounds();
ContentManager manager = myUi.myManager;
LayoutData data = new LayoutData(myUi);
data.eachX = 2;
data.eachY = 0;
if (isIdVisible()) {
myIdLabel.setBounds(data.eachX, data.eachY, myIdLabel.getPreferredSize().width, bounds.height);
data.eachX += myIdLabel.getPreferredSize().width;
}
int tabsStart = data.eachX;
if (manager.getContentCount() == 0) return;
Content selected = manager.getSelectedContent();
if (selected == null) {
selected = manager.getContents()[0];
}
if (myLastLayout != null &&
myLastLayout.layoutSize.equals(bounds.getSize()) &&
myLastLayout.contentCount == manager.getContentCount()) {
for (ContentTabLabel each : myTabs) {
if (!each.isValid()) break;
if (each.myContent == selected && each.getBounds().width != 0) {
data = myLastLayout;
data.fullLayout = false;
}
}
}
if (data.fullLayout) {
for (ContentTabLabel eachTab : myTabs) {
final Dimension eachSize = eachTab.getPreferredSize();
data.requiredWidth += eachSize.width;
data.requiredWidth++;
data.toLayout.add(eachTab);
}
data.moreRectWidth = myMoreIcon.getIconWidth() + MORE_ICON_BORDER * TAB_ARC;
data.toFitWidth = bounds.getSize().width - data.eachX;
final ContentTabLabel selectedTab = myContent2Tabs.get(selected);
while (true) {
if (data.requiredWidth <= data.toFitWidth) break;
if (data.toLayout.size() <= 1) break;
if (data.toLayout.get(0) != selectedTab) {
dropTab(data, data.toLayout.remove(0));
}
else if (data.toLayout.get(data.toLayout.size() - 1) != selectedTab) {
dropTab(data, data.toLayout.remove(data.toLayout.size() - 1));
}
else {
break;
}
}
boolean reachedBounds = false;
data.moreRect = null;
for (ContentTabLabel each : data.toLayout) {
data.eachY = 0;
final Dimension eachSize = each.getPreferredSize();
if (data.eachX + eachSize.width < data.toFitWidth + tabsStart) {
each.setBounds(data.eachX, data.eachY, eachSize.width, bounds.height - data.eachY);
data.eachX += eachSize.width;
}
else {
if (!reachedBounds) {
final int width = bounds.width - data.eachX - data.moreRectWidth;
each.setBounds(data.eachX, data.eachY, width, bounds.height - data.eachY);
data.eachX += width;
}
else {
each.setBounds(0, 0, 0, 0);
}
reachedBounds = true;
}
}
for (ContentTabLabel each : data.toDrop) {
each.setBounds(0, 0, 0, 0);
}
}
if (data.toDrop.size() > 0) {
data.moreRect = new Rectangle(data.eachX + MORE_ICON_BORDER, 0, myMoreIcon.getIconWidth(), bounds.height);
final int selectedIndex = manager.getIndexOfContent(manager.getSelectedContent());
if (selectedIndex == 0) {
myMoreIcon.setPaintedIcons(false, true);
}
else if (selectedIndex == manager.getContentCount() - 1) {
myMoreIcon.setPaintedIcons(true, false);
}
else {
myMoreIcon.setPaintedIcons(true, true);
}
}
else {
data.moreRect = null;
}
myLastLayout = data;
}
@Override
public int getMinimumWidth() {
int result = 0;
if (myIdLabel != null) {
result += myIdLabel.getPreferredSize().width;
Insets insets = myIdLabel.getInsets();
if (insets != null) {
result += insets.left + insets.right;
}
}
if (myLastLayout != null) {
result += myLastLayout.moreRectWidth + myLastLayout.requiredWidth;
result -= myLastLayout.toLayout.size() > 1 ? myLastLayout.moreRectWidth + 1 : -14;
}
return result;
}
static void dropTab(final LayoutData data, final ContentTabLabel toDropLabel) {
data.requiredWidth -= (toDropLabel.getPreferredSize().width + 1);
data.toDrop.add(toDropLabel);
if (data.toDrop.size() == 1) {
data.toFitWidth -= data.moreRectWidth;
}
}
boolean isToDrawTabs() {
return myTabs.size() > 1;
}
static class LayoutData {
int toFitWidth;
int requiredWidth;
Dimension layoutSize;
boolean fullLayout = true;
int moreRectWidth;
ArrayList<ContentTabLabel> toLayout = new ArrayList<ContentTabLabel>();
ArrayList<ContentTabLabel> toDrop = new ArrayList<ContentTabLabel>();
Rectangle moreRect;
public int eachX;
public int eachY;
public int contentCount;
LayoutData(ToolWindowContentUi ui) {
layoutSize = ui.getSize();
contentCount = ui.myManager.getContentCount();
}
}
@Override
public void paintComponent(Graphics g) {
if (!isToDrawTabs()) return;
boolean prevSelected = false;
for (int i = 0; i < myTabs.size(); i++) {
boolean last = (i == myTabs.size() - 1) || ((i + 1 < myTabs.size() && myTabs.get(i + 1).getBounds().width == 0));
ContentTabLabel each = myTabs.get(i);
Rectangle r = each.getBounds();
StringBuilder key = new StringBuilder().append(i);
if (each.isSelected()) key.append('s');
if (prevSelected) key.append('p');
if (last) key.append('l');
if (myUi.myWindow.isActive()) key.append('a');
BufferedImage image = myCached.get(key.toString());
if (image == null || image.getWidth() != r.width || image.getHeight() != r.height) {
image = drawToBuffer(r, each.isSelected(), last, prevSelected, myUi.myWindow.isActive());
myCached.put(key.toString(), image);
}
if (image != null) {
UIUtil.drawImage(g, image, isIdVisible() ? r.x : r.x - 2, r.y, null);
}
prevSelected = each.isSelected();
}
}
@Nullable
private static BufferedImage drawToBuffer(Rectangle r, boolean selected, boolean last, boolean prevSelected, boolean active) {
if (r.width <= 0 || r.height <= 0) return null;
BufferedImage image = UIUtil.createImage(r.width, r.height, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = image.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
if (selected) {
if (!UIUtil.isUnderDarcula()) {
g2d.setColor(active ? new Color(0, 0, 0, 70) : new Color(0, 0, 0, 90));
g2d.fillRect(0, 0, r.width, r.height);
g2d.setColor(new Color(0, 0, 0, 140));
g2d.drawLine(0, 0, r.width - 1, 0);
g2d.drawLine(0, 1, 0, r.height - 1);
g2d.setColor(new Color(0, 0, 0, 20));
g2d.drawLine(1, 1, r.width - 1, 1);
g2d.drawLine(1, 2, 1, r.height - 2);
g2d.drawLine(1, r.height - 1, r.width - 1, r.height - 1);
g2d.setColor(new Color(0, 0, 0, 60));
g2d.drawLine(r.width - 1, 1, r.width - 1, r.height - 2);
}
if (active) {
g2d.setColor(new Color(100, 150, 230, 50));
g2d.fill(new Rectangle(0, 0, r.width, r.height));
}
}
else {
g2d.setPaint(UIUtil.getGradientPaint(0, 0, new Color(0, 0, 0, 10), 0, r.height, new Color(0, 0, 0, 30)));
g2d.fillRect(0, 0, r.width, r.height);
final Color c = new Color(255, 255, 255, UIUtil.isUnderDarcula() ? 10 : 80);
if (last) {
if (prevSelected) {
g2d.setColor(c);
g2d.drawRect(0, 0, r.width - 2, r.height - 1);
} else {
g2d.setColor(c);
g2d.drawRect(1, 0, r.width - 3, r.height - 1);
g2d.setColor(new Color(0, 0, 0, 60));
g2d.drawLine(0, 0, 0, r.height);
}
g2d.setColor(new Color(0, 0, 0, 60));
g2d.drawLine(r.width - 1, 0, r.width - 1, r.height);
} else {
if (prevSelected) {
g2d.setColor(c);
g2d.drawRect(0, 0, r.width - 1, r.height - 1);
}
else {
g2d.setColor(c);
g2d.drawRect(1, 0, r.width - 2, r.height - 1);
g2d.setColor(new Color(0, 0, 0, 60));
g2d.drawLine(0, 0, 0, r.height);
}
}
}
g2d.dispose();
return image;
}
@Override
public void paintChildren(Graphics g) {
if (!isToDrawTabs()) return;
if (myLastLayout != null && myLastLayout.moreRect != null) {
myMoreIcon.paintIcon(myUi, g);
}
}
@Override
public void update() {
for (ContentTabLabel each : myTabs) {
each.update();
}
updateIdLabel(myIdLabel);
}
@Override
public void rebuild() {
myUi.removeAll();
myUi.add(myIdLabel);
myUi.initMouseListeners(myIdLabel, myUi);
for (ContentTabLabel each : myTabs) {
myUi.add(each);
myUi.initMouseListeners(each, myUi);
}
myCached.clear();
}
@Override
public void contentAdded(ContentManagerEvent event) {
final ContentTabLabel tab = new ContentTabLabel(event.getContent(), this);
myTabs.add(event.getIndex(), tab);
myContent2Tabs.put(event.getContent(), tab);
myCached.clear();
}
@Override
public void contentRemoved(ContentManagerEvent event) {
final ContentTabLabel tab = myContent2Tabs.get(event.getContent());
if (tab != null) {
myTabs.remove(tab);
myContent2Tabs.remove(event.getContent());
}
myCached.clear();
}
@Override
public boolean shouldDrawDecorations() {
return isToDrawTabs();
}
@Override
public void showContentPopup(ListPopup listPopup) {
Content selected = myUi.myManager.getSelectedContent();
if (selected != null) {
ContentTabLabel tab = myContent2Tabs.get(selected);
listPopup.showUnderneathOf(tab);
} else {
listPopup.showUnderneathOf(myIdLabel);
}
}
@Override
public RelativeRectangle getRectangleFor(Content content) {
ContentTabLabel label = myContent2Tabs.get(content);
return new RelativeRectangle(label.getParent(), label.getBounds());
}
public Component getComponentFor(Content content) {
return myContent2Tabs.get(content);
}
@Override
public String getCloseActionName() {
return UIBundle.message("tabbed.pane.close.tab.action.name");
}
@Override
public String getCloseAllButThisActionName() {
return UIBundle.message("tabbed.pane.close.all.tabs.but.this.action.name");
}
@Override
public String getPreviousContentActionName() {
return "Select Previous Tab";
}
@Override
public String getNextContentActionName() {
return "Select Next Tab";
}
}