mirror of git://gcc.gnu.org/git/gcc.git
1270 lines
38 KiB
Java
1270 lines
38 KiB
Java
/* MetalTabbedPaneUI.java
|
|
Copyright (C) 2005, 2006 Free Software Foundation, Inc.
|
|
|
|
This file is part of GNU Classpath.
|
|
|
|
GNU Classpath is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2, or (at your option)
|
|
any later version.
|
|
|
|
GNU Classpath is distributed in the hope that it will be useful, but
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with GNU Classpath; see the file COPYING. If not, write to the
|
|
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
02110-1301 USA.
|
|
|
|
Linking this library statically or dynamically with other modules is
|
|
making a combined work based on this library. Thus, the terms and
|
|
conditions of the GNU General Public License cover the whole
|
|
combination.
|
|
|
|
As a special exception, the copyright holders of this library give you
|
|
permission to link this library with independent modules to produce an
|
|
executable, regardless of the license terms of these independent
|
|
modules, and to copy and distribute the resulting executable under
|
|
terms of your choice, provided that you also meet, for each linked
|
|
independent module, the terms and conditions of the license of that
|
|
module. An independent module is a module which is not derived from
|
|
or based on this library. If you modify this library, you may extend
|
|
this exception to your version of the library, but you are not
|
|
obligated to do so. If you do not wish to do so, delete this
|
|
exception statement from your version. */
|
|
|
|
|
|
package javax.swing.plaf.metal;
|
|
|
|
import java.awt.Color;
|
|
import java.awt.Graphics;
|
|
import java.awt.LayoutManager;
|
|
import java.awt.Rectangle;
|
|
|
|
import javax.swing.JComponent;
|
|
import javax.swing.JTabbedPane;
|
|
import javax.swing.UIManager;
|
|
import javax.swing.plaf.ComponentUI;
|
|
import javax.swing.plaf.UIResource;
|
|
import javax.swing.plaf.basic.BasicTabbedPaneUI;
|
|
|
|
/**
|
|
* A UI delegate for the {@link JTabbedPane} component.
|
|
*/
|
|
public class MetalTabbedPaneUI extends BasicTabbedPaneUI
|
|
{
|
|
|
|
/**
|
|
* A {@link LayoutManager} responsible for placing all the tabs and the
|
|
* visible component inside the {@link JTabbedPane}. This class is only used
|
|
* for {@link JTabbedPane#WRAP_TAB_LAYOUT}.
|
|
*
|
|
* @specnote Apparently this class was intended to be protected,
|
|
* but was made public by a compiler bug and is now
|
|
* public for compatibility.
|
|
*/
|
|
public class TabbedPaneLayout
|
|
extends BasicTabbedPaneUI.TabbedPaneLayout
|
|
{
|
|
/**
|
|
* Creates a new instance of the layout manager.
|
|
*/
|
|
public TabbedPaneLayout()
|
|
{
|
|
// Nothing to do here.
|
|
}
|
|
|
|
/**
|
|
* Overridden to do nothing, because tab runs are not rotated in the
|
|
* {@link MetalLookAndFeel}.
|
|
*
|
|
* @param tabPlacement the tab placement (one of {@link #TOP},
|
|
* {@link #BOTTOM}, {@link #LEFT} or {@link #RIGHT}).
|
|
* @param selectedRun the index of the selected run.
|
|
*/
|
|
protected void rotateTabRuns(int tabPlacement, int selectedRun)
|
|
{
|
|
// do nothing, because tab runs are not rotated in the MetalLookAndFeel
|
|
}
|
|
|
|
/**
|
|
* Overridden to do nothing, because the selected tab does not have extra
|
|
* padding in the {@link MetalLookAndFeel}.
|
|
*
|
|
* @param tabPlacement the tab placement (one of {@link #TOP},
|
|
* {@link #BOTTOM}, {@link #LEFT} or {@link #RIGHT}).
|
|
* @param selectedIndex the index of the selected tab.
|
|
*/
|
|
protected void padSelectedTab(int tabPlacement, int selectedIndex)
|
|
{
|
|
// do nothing, because the selected tab does not have extra padding in
|
|
// the MetalLookAndFeel
|
|
}
|
|
|
|
/**
|
|
* Overridden because tab runs are only normalized for TOP and BOTTOM
|
|
* tab placement in the Metal L&F.
|
|
*/
|
|
protected void normalizeTabRuns(int tabPlacement, int tabCount, int start,
|
|
int max)
|
|
{
|
|
if (tabPlacement == TOP || tabPlacement == BOTTOM)
|
|
super.normalizeTabRuns(tabPlacement, tabCount, start, max);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* The minimum tab width.
|
|
*/
|
|
protected int minTabWidth;
|
|
|
|
/**
|
|
* The color for the selected tab.
|
|
*/
|
|
protected Color selectColor;
|
|
|
|
/**
|
|
* The color for a highlighted selected tab.
|
|
*/
|
|
protected Color selectHighlight;
|
|
|
|
/**
|
|
* The background color used for the tab area.
|
|
*/
|
|
protected Color tabAreaBackground;
|
|
|
|
/** The graphics to draw the highlight below the tab. */
|
|
private Graphics hg;
|
|
|
|
/**
|
|
* Indicates if the tabs are having their background filled.
|
|
*/
|
|
private boolean tabsOpaque;
|
|
|
|
/**
|
|
* Constructs a new instance of MetalTabbedPaneUI.
|
|
*/
|
|
public MetalTabbedPaneUI()
|
|
{
|
|
super();
|
|
}
|
|
|
|
/**
|
|
* Returns an instance of MetalTabbedPaneUI.
|
|
*
|
|
* @param component the component for which we return an UI instance
|
|
*
|
|
* @return an instance of MetalTabbedPaneUI
|
|
*/
|
|
public static ComponentUI createUI(JComponent component)
|
|
{
|
|
return new MetalTabbedPaneUI();
|
|
}
|
|
|
|
/**
|
|
* Creates and returns an instance of {@link TabbedPaneLayout}.
|
|
*
|
|
* @return A layout manager used by this UI delegate.
|
|
*/
|
|
protected LayoutManager createLayoutManager()
|
|
{
|
|
return (tabPane.getTabLayoutPolicy() == JTabbedPane.WRAP_TAB_LAYOUT)
|
|
? new MetalTabbedPaneUI.TabbedPaneLayout()
|
|
: super.createLayoutManager();
|
|
}
|
|
|
|
/**
|
|
* Paints the border for a single tab.
|
|
*
|
|
* @param g the graphics device.
|
|
* @param tabPlacement the tab placement ({@link #TOP}, {@link #LEFT},
|
|
* {@link #BOTTOM} or {@link #RIGHT}).
|
|
* @param tabIndex the index of the tab to draw the border for.
|
|
* @param x the x-coordinate for the tab's bounding rectangle.
|
|
* @param y the y-coordinate for the tab's bounding rectangle.
|
|
* @param w the width for the tab's bounding rectangle.
|
|
* @param h the height for the tab's bounding rectangle.
|
|
* @param isSelected indicates whether or not the tab is selected.
|
|
*/
|
|
protected void paintTabBorder(Graphics g, int tabPlacement, int tabIndex,
|
|
int x, int y, int w, int h, boolean isSelected)
|
|
{
|
|
int bottom = y + h - 1;
|
|
int right = x + w - 1;
|
|
|
|
switch (tabPlacement)
|
|
{
|
|
case LEFT:
|
|
paintLeftTabBorder(tabIndex, g, x, y, w, h, bottom, right, isSelected);
|
|
break;
|
|
case BOTTOM:
|
|
paintBottomTabBorder(tabIndex, g, x, y, w, h, bottom, right, isSelected);
|
|
break;
|
|
case RIGHT:
|
|
paintRightTabBorder(tabIndex, g, x, y, w, h, bottom, right, isSelected);
|
|
break;
|
|
case TOP:
|
|
default:
|
|
paintTopTabBorder(tabIndex, g, x, y, w, h, bottom, right, isSelected);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Paints the border for a tab assuming that the tab position is at the top
|
|
* ({@link #TOP}).
|
|
*
|
|
* @param tabIndex the tab index.
|
|
* @param g the graphics device.
|
|
* @param x the x-coordinate for the tab's bounding rectangle.
|
|
* @param y the y-coordinate for the tab's bounding rectangle.
|
|
* @param w the width for the tab's bounding rectangle.
|
|
* @param h the height for the tab's bounding rectangle.
|
|
* @param btm the y coordinate of the bottom border
|
|
* @param rght the x coordinate of the right border
|
|
* @param isSelected indicates whether the tab is selected.
|
|
*/
|
|
protected void paintTopTabBorder(int tabIndex, Graphics g, int x, int y,
|
|
int w, int h, int btm, int rght, boolean isSelected)
|
|
{
|
|
int tabCount = tabPane.getTabCount();
|
|
int currentRun = getRunForTab(tabCount, tabIndex);
|
|
int right = w - 1;
|
|
int bottom = h - 1;
|
|
|
|
// Paint gap.
|
|
if (shouldFillGap(currentRun, tabIndex, x, y))
|
|
{
|
|
g.translate(x, y);
|
|
g.setColor(getColorForGap(currentRun, x, y + 1));
|
|
g.fillRect(1, 0, 5, 3);
|
|
g.fillRect(1, 3, 2, 2);
|
|
g.translate(-x, -y);
|
|
}
|
|
|
|
g.translate(x, y);
|
|
|
|
boolean isOcean = MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme;
|
|
Color oceanSelectedBorder =
|
|
UIManager.getColor("TabbedPane.borderHightlightColor");
|
|
if (isOcean && isSelected)
|
|
g.setColor(oceanSelectedBorder);
|
|
else
|
|
g.setColor(darkShadow);
|
|
|
|
// Slant
|
|
g.drawLine(1, 5, 6, 0);
|
|
// Top.
|
|
g.drawLine(6, 0, right, 0);
|
|
// Right.
|
|
int lastIndex = lastTabInRun(tabCount, currentRun);
|
|
if (tabIndex == lastIndex)
|
|
g.drawLine(right, 1, right, bottom);
|
|
// Left.
|
|
int selectedIndex = tabPane.getSelectedIndex();
|
|
if (isOcean && tabIndex - 1 == selectedIndex
|
|
&& currentRun == getRunForTab(tabCount, selectedIndex))
|
|
{
|
|
g.setColor(oceanSelectedBorder);
|
|
}
|
|
if (tabIndex != tabRuns[runCount - 1])
|
|
{
|
|
if (isOcean && isSelected)
|
|
{
|
|
g.drawLine(0, 6, 0, bottom);
|
|
g.setColor(darkShadow);
|
|
g.drawLine(0, 0, 0, 5);
|
|
}
|
|
else
|
|
{
|
|
g.drawLine(0, 0, 0, bottom);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
g.drawLine(0, 6, 0, bottom);
|
|
}
|
|
|
|
// Paint the highlight.
|
|
g.setColor(isSelected ? selectHighlight : highlight);
|
|
// Slant.
|
|
g.drawLine(1, 6, 6, 1);
|
|
// Top.
|
|
g.drawLine(6, 1, right, 1);
|
|
// Left.
|
|
g.drawLine(1, 6, 1, bottom);
|
|
int firstIndex = tabRuns[currentRun];
|
|
if (tabIndex == firstIndex && tabIndex != tabRuns[runCount - 1])
|
|
{
|
|
if (tabPane.getSelectedIndex() == tabRuns[currentRun + 1])
|
|
g.setColor(selectHighlight);
|
|
else
|
|
g.setColor(highlight);
|
|
g.drawLine(1, 0, 1, 4);
|
|
}
|
|
|
|
g.translate(-x, -y);
|
|
}
|
|
|
|
/**
|
|
* Paints the border for a tab assuming that the tab position is at the left
|
|
* ({@link #LEFT}).
|
|
*
|
|
* @param tabIndex the tab index.
|
|
* @param g the graphics device.
|
|
* @param x the x-coordinate for the tab's bounding rectangle.
|
|
* @param y the y-coordinate for the tab's bounding rectangle.
|
|
* @param w the width for the tab's bounding rectangle.
|
|
* @param h the height for the tab's bounding rectangle.
|
|
* @param btm ???
|
|
* @param rght ???
|
|
* @param isSelected indicates whether the tab is selected.
|
|
*/
|
|
protected void paintLeftTabBorder(int tabIndex, Graphics g, int x, int y,
|
|
int w, int h, int btm, int rght, boolean isSelected)
|
|
{
|
|
g.translate(x, y);
|
|
int bottom = h - 1;
|
|
int right = w - 1;
|
|
|
|
int tabCount = tabPane.getTabCount();
|
|
int currentRun = getRunForTab(tabCount, tabIndex);
|
|
int firstIndex = tabRuns[currentRun];
|
|
|
|
// Paint the part of the above tab.
|
|
if (tabIndex != firstIndex && tabIndex > 0 && tabsOpaque)
|
|
{
|
|
Color c;
|
|
if (tabPane.getSelectedIndex() == tabIndex - 1)
|
|
c = selectColor;
|
|
else
|
|
c = getUnselectedBackground(tabIndex - 1);
|
|
g.setColor(c);
|
|
g.fillRect(2, 0, 4, 3);
|
|
g.drawLine(2, 3, 2, 3);
|
|
}
|
|
|
|
// Paint the highlight.
|
|
boolean isOcean = MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme;
|
|
if (isOcean)
|
|
{
|
|
g.setColor(isSelected ? selectHighlight : MetalLookAndFeel.getWhite());
|
|
}
|
|
else
|
|
{
|
|
g.setColor(isSelected ? selectHighlight : highlight);
|
|
}
|
|
// Slant.
|
|
g.drawLine(1, 6, 6, 1);
|
|
// Left.
|
|
g.drawLine(1, 6, 1, bottom);
|
|
// Top.
|
|
g.drawLine(6, 1, right, 1);
|
|
if (tabIndex != firstIndex)
|
|
{
|
|
if (isOcean)
|
|
{
|
|
g.setColor(MetalLookAndFeel.getWhite());
|
|
}
|
|
g.drawLine(1, 0, 1, 4);
|
|
}
|
|
|
|
// Paint border.
|
|
Color oceanSelectedBorder =
|
|
UIManager.getColor("TabbedPane.borderHightlightColor");
|
|
if (isOcean && isSelected)
|
|
{
|
|
g.setColor(oceanSelectedBorder);
|
|
}
|
|
else
|
|
{
|
|
g.setColor(darkShadow);
|
|
}
|
|
|
|
// Slant.
|
|
g.drawLine(1, 5, 6, 0);
|
|
// Top.
|
|
g.drawLine(6, 0, right, 0);
|
|
// Bottom.
|
|
int lastIndex = lastTabInRun(tabCount, currentRun);
|
|
if (tabIndex == lastIndex)
|
|
{
|
|
g.drawLine(0, bottom, right, bottom);
|
|
}
|
|
// Left.
|
|
if (isOcean)
|
|
{
|
|
if (tabPane.getSelectedIndex() == tabIndex - 1)
|
|
{
|
|
g.drawLine(0, 6, 0, bottom);
|
|
if (tabIndex != firstIndex)
|
|
{
|
|
g.setColor(oceanSelectedBorder);
|
|
g.drawLine(0, 0, 0, 5);
|
|
}
|
|
}
|
|
else if (isSelected)
|
|
{
|
|
g.drawLine(0, 5, 0, bottom);
|
|
if (tabIndex != firstIndex)
|
|
{
|
|
g.setColor(darkShadow);
|
|
g.drawLine(0, 0, 0, 5);
|
|
}
|
|
}
|
|
else if (tabIndex != firstIndex)
|
|
{
|
|
g.drawLine(0, 0, 0, bottom);
|
|
}
|
|
else
|
|
{
|
|
g.drawLine(0, 6, 0, bottom);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (tabIndex != firstIndex)
|
|
{
|
|
g.drawLine(0, 0, 0, bottom);
|
|
}
|
|
else
|
|
{
|
|
g.drawLine(0, 6, 0, bottom);
|
|
}
|
|
}
|
|
|
|
g.translate(-x, -y);
|
|
}
|
|
|
|
/**
|
|
* Paints the border for a tab assuming that the tab position is at the right
|
|
* ({@link #RIGHT}).
|
|
*
|
|
* @param tabIndex the tab index.
|
|
* @param g the graphics device.
|
|
* @param x the x-coordinate for the tab's bounding rectangle.
|
|
* @param y the y-coordinate for the tab's bounding rectangle.
|
|
* @param w the width for the tab's bounding rectangle.
|
|
* @param h the height for the tab's bounding rectangle.
|
|
* @param btm ???
|
|
* @param rght ???
|
|
* @param isSelected indicates whether the tab is selected.
|
|
*/
|
|
protected void paintRightTabBorder(int tabIndex, Graphics g, int x, int y,
|
|
int w, int h, int btm, int rght, boolean isSelected)
|
|
{
|
|
g.translate(x, y);
|
|
int bottom = h - 1;
|
|
int right = w - 1;
|
|
|
|
int tabCount = tabPane.getTabCount();
|
|
int currentRun = getRunForTab(tabCount, tabIndex);
|
|
int firstIndex = tabRuns[currentRun];
|
|
|
|
// Paint part of the above tab.
|
|
if (tabIndex != firstIndex && tabIndex > 0 && tabsOpaque)
|
|
{
|
|
Color c;
|
|
if (tabPane.getSelectedIndex() == tabIndex - 1)
|
|
c = selectColor;
|
|
else
|
|
c = getUnselectedBackground(tabIndex - 1);
|
|
g.setColor(c);
|
|
g.fillRect(right - 5, 0, 5, 3);
|
|
g.fillRect(right - 2, 3, 2, 2);
|
|
}
|
|
|
|
// Paint highlight.
|
|
g.setColor(isSelected ? selectHighlight : highlight);
|
|
|
|
// Slant.
|
|
g.drawLine(right - 6, 1, right - 1, 6);
|
|
// Top.
|
|
g.drawLine(0, 1, right - 6, 1);
|
|
// Left.
|
|
if (! isSelected)
|
|
{
|
|
g.drawLine(0, 1, 0, bottom);
|
|
}
|
|
|
|
// Paint border.
|
|
boolean isOcean = MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme;
|
|
Color oceanSelectedBorder =
|
|
UIManager.getColor("TabbedPane.borderHightlightColor");
|
|
if (isOcean && isSelected)
|
|
{
|
|
g.setColor(oceanSelectedBorder);
|
|
}
|
|
else
|
|
{
|
|
g.setColor(darkShadow);
|
|
}
|
|
|
|
// Bottom.
|
|
int lastIndex = lastTabInRun(tabCount, currentRun);
|
|
if (tabIndex == lastIndex)
|
|
{
|
|
g.drawLine(0, bottom, right, bottom);
|
|
}
|
|
// Slant.
|
|
if (isOcean && tabPane.getSelectedIndex() == tabIndex - 1)
|
|
{
|
|
g.setColor(oceanSelectedBorder);
|
|
}
|
|
g.drawLine(right - 6, 0, right, 6);
|
|
// Top.
|
|
g.drawLine(0, 0, right - 6, 0);
|
|
// Right.
|
|
if (isOcean && isSelected)
|
|
{
|
|
g.drawLine(right, 6, right, bottom);
|
|
if (tabIndex != firstIndex)
|
|
{
|
|
g.setColor(darkShadow);
|
|
g.drawLine(right, 0, right, 5);
|
|
}
|
|
}
|
|
else if (isOcean && tabPane.getSelectedIndex() == tabIndex - 1)
|
|
{
|
|
if (tabIndex != firstIndex)
|
|
{
|
|
g.setColor(oceanSelectedBorder);
|
|
g.drawLine(right, 0, right, 6);
|
|
}
|
|
g.setColor(darkShadow);
|
|
g.drawLine(right, 7, right, bottom);
|
|
}
|
|
else if (tabIndex != firstIndex)
|
|
{
|
|
g.drawLine(right, 0, right, bottom);
|
|
}
|
|
else
|
|
{
|
|
g.drawLine(right, 6, right, bottom);
|
|
}
|
|
g.translate(-x, -y);
|
|
}
|
|
|
|
/**
|
|
* Paints the border for a tab assuming that the tab position is at the bottom
|
|
* ({@link #BOTTOM}).
|
|
*
|
|
* @param tabIndex the tab index.
|
|
* @param g the graphics device.
|
|
* @param x the x-coordinate for the tab's bounding rectangle.
|
|
* @param y the y-coordinate for the tab's bounding rectangle.
|
|
* @param w the width for the tab's bounding rectangle.
|
|
* @param h the height for the tab's bounding rectangle.
|
|
* @param btm ???
|
|
* @param rght ???
|
|
* @param isSelected indicates whether the tab is selected.
|
|
*/
|
|
protected void paintBottomTabBorder(int tabIndex, Graphics g, int x, int y,
|
|
int w, int h, int btm, int rght, boolean isSelected)
|
|
{
|
|
int bottom = h - 1;
|
|
int right = w - 1;
|
|
|
|
int tabCount = tabPane.getTabCount();
|
|
int currentRun = getRunForTab(tabCount, tabIndex);
|
|
// Paint gap if necessary.
|
|
if (shouldFillGap(currentRun, tabIndex, x, y))
|
|
{
|
|
g.translate(x, y);
|
|
g.setColor(getColorForGap(currentRun, x, y));
|
|
g.fillRect(1, bottom - 4, 3, 5);
|
|
g.fillRect(4, bottom - 1, 2, 2);
|
|
g.translate(-x, -y);
|
|
}
|
|
|
|
g.translate(x, y);
|
|
|
|
// Paint border.
|
|
boolean isOcean = MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme;
|
|
Color oceanSelectedBorder =
|
|
UIManager.getColor("TabbedPane.borderHightlightColor");
|
|
if (isOcean && isSelected)
|
|
{
|
|
g.setColor(oceanSelectedBorder);
|
|
}
|
|
else
|
|
{
|
|
g.setColor(darkShadow);
|
|
}
|
|
// Slant.
|
|
g.drawLine(1, bottom - 5, 6, bottom);
|
|
// Bottom.
|
|
g.drawLine(6, bottom, right, bottom);
|
|
// Right.
|
|
int lastIndex = lastTabInRun(tabCount, currentRun);
|
|
if (tabIndex == lastIndex)
|
|
{
|
|
g.drawLine(right, 0, right, bottom);
|
|
}
|
|
// Left.
|
|
if (isOcean && isSelected)
|
|
{
|
|
g.drawLine(0, 0, 0, bottom - 5);
|
|
|
|
// Paint a connecting line to the tab below for all
|
|
// but the first tab in the last run.
|
|
if (tabIndex != tabRuns[runCount-1])
|
|
{
|
|
g.setColor(darkShadow);
|
|
g.drawLine(0, bottom - 5, 0, bottom);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (isOcean && tabIndex == tabPane.getSelectedIndex() + 1)
|
|
{
|
|
g.setColor(oceanSelectedBorder);
|
|
}
|
|
if (tabIndex != tabRuns[runCount - 1])
|
|
{
|
|
g.drawLine(0, 0, 0, bottom);
|
|
}
|
|
else
|
|
{
|
|
g.drawLine(0, 0, 0, bottom - 6);
|
|
}
|
|
}
|
|
|
|
// Paint highlight.
|
|
g.setColor(isSelected ? selectHighlight : highlight);
|
|
// Slant.
|
|
g.drawLine(1, bottom - 6, 6, bottom - 1);
|
|
// Left.
|
|
g.drawLine(1, 0, 1, bottom - 6);
|
|
|
|
int firstIndex = tabRuns[currentRun];
|
|
if (tabIndex == firstIndex && tabIndex != tabRuns[runCount - 1])
|
|
{
|
|
if (tabPane.getSelectedIndex() == tabRuns[currentRun + 1])
|
|
{
|
|
g.setColor(selectHighlight);
|
|
}
|
|
else
|
|
{
|
|
g.setColor(highlight);
|
|
}
|
|
g.drawLine(1, bottom - 4, 1, bottom);
|
|
}
|
|
|
|
g.translate(-x, -y);
|
|
}
|
|
|
|
/**
|
|
* Paints the background for a tab.
|
|
*
|
|
* @param g the graphics device.
|
|
* @param tabPlacement the tab placement ({@link #TOP}, {@link #LEFT},
|
|
* {@link #BOTTOM} or {@link #RIGHT}).
|
|
* @param tabIndex the index of the tab to draw the border for.
|
|
* @param x the x-coordinate for the tab's bounding rectangle.
|
|
* @param y the y-coordinate for the tab's bounding rectangle.
|
|
* @param w the width for the tab's bounding rectangle.
|
|
* @param h the height for the tab's bounding rectangle.
|
|
* @param isSelected indicates whether or not the tab is selected.
|
|
*/
|
|
protected void paintTabBackground(Graphics g, int tabPlacement,
|
|
int tabIndex, int x, int y, int w, int h, boolean isSelected)
|
|
{
|
|
if (isSelected)
|
|
g.setColor(selectColor);
|
|
else
|
|
g.setColor(getUnselectedBackground(tabIndex));
|
|
|
|
switch (tabPlacement)
|
|
{
|
|
case LEFT:
|
|
g.fillRect(x + 5, y + 1, w - 5, h - 1);
|
|
g.fillRect(x + 2, y + 4, 3, h - 4);
|
|
break;
|
|
case BOTTOM:
|
|
g.fillRect(x + 2, y, w - 2, h - 3);
|
|
g.fillRect(x + 5, y + h - 4, w - 5, 3);
|
|
break;
|
|
case RIGHT:
|
|
g.fillRect(x, y + 1, w - 4, h - 1);
|
|
g.fillRect(x + w - 4, y + 5, 3, h - 5);
|
|
break;
|
|
case TOP:
|
|
default:
|
|
g.fillRect(x + 4, y + 2, w - 4, h - 2);
|
|
g.fillRect(x + 2, y + 5, 2, h - 5);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This method paints the focus rectangle around the selected tab.
|
|
*
|
|
* @param g The Graphics object to paint with.
|
|
* @param tabPlacement The JTabbedPane's tab placement.
|
|
* @param rects The array of rectangles keeping track of size and position.
|
|
* @param tabIndex The tab index.
|
|
* @param iconRect The icon bounds.
|
|
* @param textRect The text bounds.
|
|
* @param isSelected Whether this tab is selected.
|
|
*/
|
|
protected void paintFocusIndicator(Graphics g, int tabPlacement,
|
|
Rectangle[] rects, int tabIndex,
|
|
Rectangle iconRect, Rectangle textRect,
|
|
boolean isSelected)
|
|
{
|
|
if (tabPane.hasFocus() && isSelected)
|
|
{
|
|
Rectangle rect = rects[tabIndex];
|
|
|
|
g.setColor(focus);
|
|
g.translate(rect.x, rect.y);
|
|
|
|
switch (tabPlacement)
|
|
{
|
|
case LEFT:
|
|
// Top line
|
|
g.drawLine(7, 2, rect.width-2, 2);
|
|
|
|
// Right line
|
|
g.drawLine(rect.width-1, 2, rect.width-1, rect.height-3);
|
|
|
|
// Bottom line
|
|
g.drawLine(rect.width-2, rect.height-2, 3, rect.height-2);
|
|
|
|
// Left line
|
|
g.drawLine(2, rect.height-3, 2, 7);
|
|
|
|
// Slant
|
|
g.drawLine(2, 6, 6, 2);
|
|
break;
|
|
case RIGHT:
|
|
// Top line
|
|
g.drawLine(1, 2, rect.width-8, 2);
|
|
|
|
// Slant
|
|
g.drawLine(rect.width-7, 2, rect.width-3, 6);
|
|
|
|
// Right line
|
|
g.drawLine(rect.width-3, 7, rect.width-3, rect.height-3);
|
|
|
|
// Bottom line
|
|
g.drawLine(rect.width-3, rect.height-2, 2, rect.height-2);
|
|
|
|
// Left line
|
|
g.drawLine(1, rect.height-2, 1, 2);
|
|
break;
|
|
case BOTTOM:
|
|
// Top line
|
|
g.drawLine(2, 1, rect.width-2, 1);
|
|
|
|
// Right line
|
|
g.drawLine(rect.width-1, 2, rect.width-1, rect.height-3);
|
|
|
|
// Bottom line
|
|
g.drawLine(7, rect.height-3, rect.width-2, rect.height-3);
|
|
|
|
// Slant
|
|
g.drawLine(6, rect.height-3, 2, rect.height-7);
|
|
|
|
// Left line
|
|
g.drawLine(2, rect.height-8, 2, 2);
|
|
|
|
break;
|
|
case TOP:
|
|
default:
|
|
// Top line
|
|
g.drawLine(6, 2, rect.width-2, 2);
|
|
|
|
// Right line
|
|
g.drawLine(rect.width-1, 2, rect.width-1, rect.height-3);
|
|
|
|
// Bottom line
|
|
g.drawLine(3, rect.height-3, rect.width-2, rect.height-3);
|
|
|
|
// Left line
|
|
g.drawLine(2, rect.height-3, 2, 7);
|
|
|
|
// Slant
|
|
g.drawLine(2, 6, 6, 2);
|
|
|
|
}
|
|
|
|
g.translate(-rect.x, -rect.y);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns <code>true</code> if the tabs in the specified run should be
|
|
* padded to make the run fill the width/height of the {@link JTabbedPane}.
|
|
*
|
|
* @param tabPlacement the tab placement for the {@link JTabbedPane} (one of
|
|
* {@link #TOP}, {@link #BOTTOM}, {@link #LEFT} and {@link #RIGHT}).
|
|
* @param run the run index.
|
|
*
|
|
* @return A boolean.
|
|
*/
|
|
protected boolean shouldPadTabRun(int tabPlacement, int run)
|
|
{
|
|
// as far as I can tell, all runs should be padded except the last run
|
|
// (which is drawn at the very top for tabPlacement == TOP)
|
|
return run < this.runCount - 1;
|
|
}
|
|
|
|
/**
|
|
* Installs the defaults for this UI. This method calls super.installDefaults
|
|
* and then loads the Metal specific defaults for TabbedPane.
|
|
*/
|
|
protected void installDefaults()
|
|
{
|
|
super.installDefaults();
|
|
selectColor = UIManager.getColor("TabbedPane.selected");
|
|
selectHighlight = UIManager.getColor("TabbedPane.selectHighlight");
|
|
tabAreaBackground = UIManager.getColor("TabbedPane.tabAreaBackground");
|
|
tabsOpaque = UIManager.getBoolean("TabbedPane.tabsOpaque");
|
|
minTabWidth = 0;
|
|
}
|
|
|
|
/**
|
|
* Returns the color for the gap.
|
|
*
|
|
* @param currentRun - The current run to return the color for
|
|
* @param x - The x position of the current run
|
|
* @param y - The y position of the current run
|
|
*
|
|
* @return the color for the gap in the current run.
|
|
*/
|
|
protected Color getColorForGap(int currentRun, int x, int y)
|
|
{
|
|
int index = tabForCoordinate(tabPane, x, y);
|
|
int selected = tabPane.getSelectedIndex();
|
|
if (selected == index)
|
|
return selectColor;
|
|
return tabAreaBackground;
|
|
}
|
|
|
|
/**
|
|
* Returns true if the gap should be filled in.
|
|
*
|
|
* @param currentRun - The current run
|
|
* @param tabIndex - The current tab
|
|
* @param x - The x position of the tab
|
|
* @param y - The y position of the tab
|
|
*
|
|
* @return true if the gap at the current run should be filled
|
|
*/
|
|
protected boolean shouldFillGap(int currentRun, int tabIndex, int x, int y)
|
|
{
|
|
// As far as I can tell, the gap is never filled in.
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Paints the highlight below the tab, if there is one.
|
|
*/
|
|
protected void paintHighlightBelowTab()
|
|
{
|
|
int selected = tabPane.getSelectedIndex();
|
|
int tabPlacement = tabPane.getTabPlacement();
|
|
Rectangle bounds = getTabBounds(tabPane, selected);
|
|
|
|
hg.setColor(selectColor);
|
|
int x = bounds.x;
|
|
int y = bounds.y;
|
|
int w = bounds.width;
|
|
int h = bounds.height;
|
|
|
|
if (tabPlacement == TOP)
|
|
hg.fillRect(x, y + h - 2, w, 30);
|
|
else if (tabPlacement == LEFT)
|
|
hg.fillRect(x + w - 1, y, 20, h);
|
|
else if (tabPlacement == BOTTOM)
|
|
hg.fillRect(x, y - h + 2, w, 30);
|
|
else if (tabPlacement == RIGHT)
|
|
hg.fillRect(x - 18, y, 20, h);
|
|
else
|
|
throw new AssertionError("Unrecognised 'tabPlacement' argument.");
|
|
hg = null;
|
|
}
|
|
|
|
/**
|
|
* Returns true if we should rotate the tab runs.
|
|
*
|
|
* @param tabPlacement - The current tab placement.
|
|
* @param selectedRun - The selected run.
|
|
*
|
|
* @return true if the tab runs should be rotated.
|
|
*/
|
|
protected boolean shouldRotateTabRuns(int tabPlacement,
|
|
int selectedRun)
|
|
{
|
|
// false because tab runs are not rotated in the MetalLookAndFeel
|
|
return false;
|
|
}
|
|
|
|
protected int calculateMaxTabHeight(int tabPlacement)
|
|
{
|
|
// FIXME: Why is this overridden?
|
|
return super.calculateMaxTabHeight(tabPlacement);
|
|
}
|
|
|
|
/**
|
|
* Returns the amount of overlay among the tabs. In
|
|
* the Metal L&F the overlay for LEFT and RIGHT placement
|
|
* is half of the maxTabHeight. For TOP and BOTTOM placement
|
|
* the tabs do not overlay.
|
|
*
|
|
* @param tabPlacement the placement
|
|
*
|
|
* @return the amount of overlay among the tabs
|
|
*/
|
|
protected int getTabRunOverlay(int tabPlacement)
|
|
{
|
|
int overlay = 0;
|
|
if (tabPlacement == LEFT || tabPlacement == RIGHT)
|
|
{
|
|
int maxHeight = calculateMaxTabHeight(tabPlacement);
|
|
overlay = maxTabHeight / 2;
|
|
}
|
|
return overlay;
|
|
}
|
|
|
|
/**
|
|
* Paints the upper edge of the content border.
|
|
*
|
|
* @param g the graphics to use for painting
|
|
* @param tabPlacement the tab placement
|
|
* @param selectedIndex the index of the selected tab
|
|
* @param x the upper left coordinate of the content area
|
|
* @param y the upper left coordinate of the content area
|
|
* @param w the width of the content area
|
|
* @param h the height of the content area
|
|
*/
|
|
protected void paintContentBorderTopEdge(Graphics g, int tabPlacement,
|
|
int selectedIndex, int x, int y,
|
|
int w, int h)
|
|
{
|
|
Color oceanSelectedBorder =
|
|
UIManager.getColor("TabbedPane.borderHightlightColor");
|
|
boolean isOcean = MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme;
|
|
if (isOcean)
|
|
{
|
|
g.setColor(oceanSelectedBorder);
|
|
}
|
|
else
|
|
{
|
|
g.setColor(selectHighlight);
|
|
}
|
|
|
|
Rectangle rect = selectedIndex < 0 ? null :
|
|
getTabBounds(selectedIndex, calcRect);
|
|
|
|
// If tabs are not placed on TOP, or if the selected tab is not in the
|
|
// run directly above the content or the selected tab is not visible,
|
|
// then we draw an unbroken line.
|
|
if (tabPlacement != TOP || selectedIndex < 0
|
|
|| rect.y + rect.height + 1 < y || rect.x < x || rect.x > x + w)
|
|
{
|
|
g.drawLine(x, y, x + w - 2, y);
|
|
if (isOcean && tabPlacement == TOP)
|
|
{
|
|
g.setColor(MetalLookAndFeel.getWhite());
|
|
g.drawLine(x, y + 1, x + w - 2, y + 1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
boolean isLast = isLastTabInRun(selectedIndex);
|
|
if (isLast)
|
|
{
|
|
g.drawLine(x, y, rect.x + 1, y);
|
|
}
|
|
else
|
|
{
|
|
g.drawLine(x, y, rect.x, y);
|
|
}
|
|
|
|
int right = x + w - 1;
|
|
if (rect.x + rect.width < right - 1)
|
|
{
|
|
if (isLast)
|
|
{
|
|
g.drawLine(rect.x + rect.width - 1, y, right - 1, y);
|
|
}
|
|
else
|
|
{
|
|
g.drawLine(rect.x + rect.width, y, right - 1, y);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
g.setColor(shadow);
|
|
g.drawLine(x + w - 2, y, x + w - 2, y);
|
|
}
|
|
|
|
// When in OceanTheme, draw another white line.
|
|
if (isOcean)
|
|
{
|
|
g.setColor(MetalLookAndFeel.getWhite());
|
|
if (isLast)
|
|
{
|
|
g.drawLine(x, y + 1, rect.x + 1, y + 1);
|
|
}
|
|
else
|
|
{
|
|
g.drawLine(x, y + 1, rect.x, y + 1);
|
|
}
|
|
|
|
if (rect.x + rect.width < right - 1)
|
|
{
|
|
if (isLast)
|
|
{
|
|
g.drawLine(rect.x + rect.width - 1, y + 1, right - 1,
|
|
y + 1);
|
|
}
|
|
else
|
|
{
|
|
g.drawLine(rect.x + rect.width, y + 1, right - 1, y + 1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
g.setColor(shadow);
|
|
g.drawLine(x + w - 2, y + 1, x + w - 2, y + 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Paints the lower edge of the content border.
|
|
*
|
|
* @param g the graphics to use for painting
|
|
* @param tabPlacement the tab placement
|
|
* @param selectedIndex the index of the selected tab
|
|
* @param x the upper left coordinate of the content area
|
|
* @param y the upper left coordinate of the content area
|
|
* @param w the width of the content area
|
|
* @param h the height of the content area
|
|
*/
|
|
protected void paintContentBorderBottomEdge(Graphics g, int tabPlacement,
|
|
int selectedIndex, int x, int y,
|
|
int w, int h)
|
|
{
|
|
g.setColor(darkShadow);
|
|
|
|
// If tabs are not placed on BOTTOM, or if the selected tab is not in the
|
|
// run directly below the content or the selected tab is not visible,
|
|
// then we draw an unbroken line.
|
|
Rectangle rect = selectedIndex < 0 ? null :
|
|
getTabBounds(selectedIndex, calcRect);
|
|
boolean isOcean = MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme;
|
|
Color oceanSelectedBorder =
|
|
UIManager.getColor("TabbedPane.borderHightlightColor");
|
|
if (tabPlacement != BOTTOM || selectedIndex < 0 || rect.y - 1 > h
|
|
|| rect.x < x || rect.x > x + w)
|
|
{
|
|
if (isOcean && tabPlacement == BOTTOM)
|
|
{
|
|
g.setColor(oceanSelectedBorder);
|
|
}
|
|
g.drawLine(x, y + h - 1, x + w - 1, y + h - 1);
|
|
}
|
|
else
|
|
{
|
|
boolean isLast = isLastTabInRun(selectedIndex);
|
|
if (isOcean)
|
|
{
|
|
g.setColor(oceanSelectedBorder);
|
|
}
|
|
|
|
int bottom = y + h - 1;
|
|
int right = x + w - 1;
|
|
if (isLast)
|
|
{
|
|
g.drawLine(x, bottom, rect.x, bottom);
|
|
}
|
|
else
|
|
{
|
|
g.drawLine(x, bottom, rect.x - 1, bottom);
|
|
}
|
|
|
|
if (rect.x + rect.width < x + w - 2)
|
|
{
|
|
if (isLast)
|
|
{
|
|
g.drawLine(rect.x + rect.width - 1, bottom, right, bottom);
|
|
}
|
|
else
|
|
{
|
|
g.drawLine(rect.x + rect.width, bottom, right, bottom);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Paints the left edge of the content border.
|
|
*
|
|
* @param g the graphics to use for painting
|
|
* @param tabPlacement the tab placement
|
|
* @param selectedIndex the index of the selected tab
|
|
* @param x the upper left coordinate of the content area
|
|
* @param y the upper left coordinate of the content area
|
|
* @param w the width of the content area
|
|
* @param h the height of the content area
|
|
*/
|
|
protected void paintContentBorderLeftEdge(Graphics g, int tabPlacement,
|
|
int selectedIndex, int x, int y,
|
|
int w, int h)
|
|
{
|
|
boolean isOcean = MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme;
|
|
Color oceanSelectedBorder =
|
|
UIManager.getColor("TabbedPane.borderHightlightColor");
|
|
Rectangle rect = selectedIndex < 0 ? null :
|
|
getTabBounds(selectedIndex, calcRect);
|
|
|
|
if (isOcean)
|
|
{
|
|
g.setColor(oceanSelectedBorder);
|
|
}
|
|
else
|
|
{
|
|
g.setColor(selectHighlight);
|
|
}
|
|
|
|
// If tabs are not placed on LEFT, or if the selected tab is not in the
|
|
// run directly left to the content or the selected tab is not visible,
|
|
// then we draw an unbroken line.
|
|
if (tabPlacement != LEFT || selectedIndex < 0
|
|
|| rect.x + rect.width + 1 < x || rect.y < y || rect.y > y + h)
|
|
{
|
|
g.drawLine(x, y + 1, x, y + h - 2);
|
|
if (isOcean && tabPlacement == LEFT)
|
|
{
|
|
g.setColor(MetalLookAndFeel.getWhite());
|
|
g.drawLine(x, y + 1, x, y + h - 2);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
g.drawLine(x, y, x, rect.y + 1);
|
|
if (rect.y + rect.height < y + h - 2)
|
|
{
|
|
g.drawLine(x, rect.y + rect.height + 1, x, y + h + 2);
|
|
}
|
|
if (isOcean)
|
|
{
|
|
g.setColor(MetalLookAndFeel.getWhite());
|
|
g.drawLine(x + 1, y + 1, x + 1, rect.y + 1);
|
|
if (rect.y + rect.height < y + h - 2)
|
|
{
|
|
g.drawLine(x + 1, rect.y + rect.height + 1, x + 1, y + h + 2);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Paints the right edge of the content border.
|
|
*
|
|
* @param g the graphics to use for painting
|
|
* @param tabPlacement the tab placement
|
|
* @param selectedIndex the index of the selected tab
|
|
* @param x the upper left coordinate of the content area
|
|
* @param y the upper left coordinate of the content area
|
|
* @param w the width of the content area
|
|
* @param h the height of the content area
|
|
*/
|
|
protected void paintContentBorderRightEdge(Graphics g, int tabPlacement,
|
|
int selectedIndex, int x, int y,
|
|
int w, int h)
|
|
{
|
|
g.setColor(darkShadow);
|
|
Rectangle rect = selectedIndex < 0 ? null :
|
|
getTabBounds(selectedIndex, calcRect);
|
|
boolean isOcean = MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme;
|
|
Color oceanSelectedBorder =
|
|
UIManager.getColor("TabbedPane.borderHightlightColor");
|
|
|
|
// If tabs are not placed on RIGHT, or if the selected tab is not in the
|
|
// run directly right to the content or the selected tab is not visible,
|
|
// then we draw an unbroken line.
|
|
if (tabPlacement != RIGHT || selectedIndex < 0 || rect.x - 1 > w
|
|
|| rect.y < y || rect.y > y + h)
|
|
{
|
|
if (isOcean && tabPlacement == RIGHT)
|
|
{
|
|
g.setColor(oceanSelectedBorder);
|
|
}
|
|
g.drawLine(x + w - 1, y, x + w - 1, y + h - 1);
|
|
}
|
|
else
|
|
{
|
|
if (isOcean)
|
|
{
|
|
g.setColor(oceanSelectedBorder);
|
|
}
|
|
g.drawLine(x + w - 1, y, x + w - 1, rect.y);
|
|
|
|
if (rect.y + rect.height < y + h - 2)
|
|
{
|
|
g.drawLine(x + w - 1, rect.y + rect.height, x + w - 1, y + h - 2);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Determines if the specified tab is the last tab in its tab run.
|
|
*
|
|
* @param tabIndex the index of the tab
|
|
*
|
|
* @return if the specified tab is the last tab in its tab run
|
|
*/
|
|
private boolean isLastTabInRun(int tabIndex)
|
|
{
|
|
int count = tabPane.getTabCount();
|
|
int run = getRunForTab(count, tabIndex);
|
|
int lastIndex = lastTabInRun(count, run);
|
|
return tabIndex == lastIndex;
|
|
}
|
|
|
|
/**
|
|
* Returns the background for an unselected tab. This first asks the
|
|
* JTabbedPane for the background at the specified tab index, if this
|
|
* is an UIResource (that means, it is inherited from the JTabbedPane)
|
|
* and the TabbedPane.unselectedBackground UI property is not null,
|
|
* this returns the value of the TabbedPane.unselectedBackground property,
|
|
* otherwise the value returned by the JTabbedPane.
|
|
*
|
|
* @param tabIndex the index of the tab for which we query the background
|
|
*
|
|
* @return the background for an unselected tab
|
|
*/
|
|
private Color getUnselectedBackground(int tabIndex)
|
|
{
|
|
Color bg = tabPane.getBackgroundAt(tabIndex);
|
|
Color unselectedBackground =
|
|
UIManager.getColor("TabbedPane.unselectedBackground");
|
|
if (bg instanceof UIResource && unselectedBackground != null)
|
|
bg = unselectedBackground;
|
|
return bg;
|
|
}
|
|
|
|
protected int getTabLabelShiftX(int tabPlacement,
|
|
int index,
|
|
boolean isSelected)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
protected int getTabLabelShiftY(int tabPlacement,
|
|
int index,
|
|
boolean isSelected)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
}
|