ch.randelshofer.quaqua.jaguar.QuaquaJaguarTabbedPaneUI Maven / Gradle / Ivy
Show all versions of Quaqua Show documentation
/*
* @(#)QuaquaJaguarTabbedPaneUI.java
*
* Copyright (c) 2001-2010 Werner Randelshofer, Immensee, Switzerland.
* All rights reserved.
*
* You may not use, copy or modify this file, except in compliance with the
* license agreement you entered into with Werner Randelshofer.
* For details see accompanying license terms.
*/
package ch.randelshofer.quaqua.jaguar;
import ch.randelshofer.quaqua.*;
import ch.randelshofer.quaqua.util.*;
import ch.randelshofer.quaqua.util.Debug;
import ch.randelshofer.quaqua.util.NavigatableTabbedPaneUI;
import ch.randelshofer.quaqua.color.PaintableColor;
import ch.randelshofer.quaqua.color.TextureColor;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.text.*;
import java.awt.*;
import java.awt.event.*;
import java.beans.*;
import javax.swing.plaf.*;
import javax.swing.plaf.basic.*;
import java.util.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
/**
* A replacement for the AquaTabbedPaneUI for Mac OS X 10.2 Jaguar.
* Tabs of tabbed panes are stacked instead of moved into a popup menu,
* if not enough space is available to render all tabs in a single line.
*
* Supports the following client properties on the children of the JTabbedPane:
* Quaqua.TabbedPaneChild.contentBackground
specifies the background
* Color to be used to fill the content border.
* Quaqua.TabbedPaneChild.contentInsets
specifies the insets
* to be used to lay out the child component inside the JTabbedPane.
*
* @author Werner Randelshofer, Hausmatt 10, CH-6405 Immensee, Switzerland
* @version $Id: QuaquaJaguarTabbedPaneUI.java 362 2010-11-21 17:35:47Z wrandelshofer $
*/
public class QuaquaJaguarTabbedPaneUI extends BasicTabbedPaneUI
implements VisuallyLayoutable, NavigatableTabbedPaneUI {
// private HierarchyListener hierarchyListener;
protected int minTabWidth = 40;
protected Color tabAreaBackground;
protected Color selectColor;
protected Color selectHighlight;
protected Color disabledForeground;
private Insets currentContentBorderInsets = new Insets(0, 0, 0, 0);
private int tabCount;
/**
* This variable is set in method paint() and used in all subsequent paint...() methods.
*/
private boolean isFrameActive;
/**
* This is the border painted around the content area.
* Don't cache this border because it usually encompasses a huge are on
* a panel.
*/
private Border getContentBorder() {
return UIManager.getBorder("TabbedPane.wrap.contentBorder");
}
private static Border createImageBorder(String name, Insets insets) {
return QuaquaBorderFactory.create(
Images.createImage(QuaquaJaguarTabbedPaneUI.class.getResource(name)),
insets);
}
private static Border createNonCachedImageBorder(String name, Insets insets) {
return QuaquaBorderFactory.create(
Images.createImage(QuaquaJaguarTabbedPaneUI.class.getResource(name)),
insets, insets, true, null, false);
}
/**
* This is the bar used when the tabs are at the top.
*
* Indices 0: active
* 1: inactive
* 2: disabled
*/
private Border getBarTopBorder(int i) {
Border[] borders = (Border[]) UIManager.get("TabbedPane.wrapBarTopBorders");
return borders[i];
}
/**
* This is the bar used when the tabs are at the bottom.
*/
private Border getBarBottomBorder(int i) {
Border[] borders = (Border[]) UIManager.get("TabbedPane.wrapBarBottomBorders");
return borders[i];
}
//--
/**
* This is the bar used when the tabs are at the right.
*/
private Border getBarRightBorder(int i) {
Border[] borders = (Border[]) UIManager.get("TabbedPane.wrapBarRightBorders");
return borders[i];
}
/**
* This is the bar used when the tabs are at the left.
*/
private Border getBarLeftBorder(int i) {
Border[] borders = (Border[]) UIManager.get("TabbedPane.wrapBarLeftBorders");
return borders[i];
}
//--
/**
* This is a tab when the tabs are at the top.
*
* Indices 0: enabled
* 1: selected
* 2: inactive
* 3: disabled
* 4: disabled selected
*/
private static Border[] tabTopBorder;
private Border getTabTopBorder(int i) {
if (tabTopBorder == null) {
Insets insets = new Insets(12, 8, 11, 8);
tabTopBorder = new Border[]{
createImageBorder("images/TabbedPane.tabTop.png", insets),
createImageBorder("images/TabbedPane.tabTop.S.png", insets),
createImageBorder("images/TabbedPane.tabTop.I.png", insets),
createImageBorder("images/TabbedPane.tabTop.D.png", insets),
createImageBorder("images/TabbedPane.tabTop.DS.png", insets),};
}
return tabTopBorder[i];
}
//--
/**
* This is a tab when the tabs are at the bottom.
*/
private static Border[] tabBottomBorder;
private Border getTabBottomBorder(int i) {
if (tabBottomBorder == null) {
Insets insets = new Insets(11, 8, 12, 8);
tabBottomBorder = new Border[]{
createImageBorder("images/TabbedPane.tabBottom.png", insets),
createImageBorder("images/TabbedPane.tabBottom.S.png", insets),
createImageBorder("images/TabbedPane.tabBottom.I.png", insets),
createImageBorder("images/TabbedPane.tabBottom.D.png", insets),
createImageBorder("images/TabbedPane.tabBottom.DS.png", insets),};
}
return tabBottomBorder[i];
}
/**
* This is a tab when the tabs are at the right.
*/
private static Border[] tabRightBorder;
private Border getTabRightBorder(int i) {
if (tabRightBorder == null) {
Insets insets = new Insets(11, 1, 11, 7);
Insets insetsS = new Insets(11, 2, 11, 7);
tabRightBorder = new Border[]{
createImageBorder("images/TabbedPane.tabRight.png", insets),
createImageBorder("images/TabbedPane.tabRight.S.png", insetsS),
createImageBorder("images/TabbedPane.tabRight.I.png", insetsS),
createImageBorder("images/TabbedPane.tabRight.D.png", insets),
createImageBorder("images/TabbedPane.tabRight.DS.png", insetsS),};
}
return tabRightBorder[i];
}
/**
* This is a tab when the tabs are at the left.
*/
private static Border[] tabLeftBorder;
private Border getTabLeftBorder(int i) {
if (tabLeftBorder == null) {
Insets insets = new Insets(11, 7, 11, 1);
Insets insetsS = new Insets(11, 7, 11, 2);
tabLeftBorder = new Border[]{
createImageBorder("images/TabbedPane.tabLeft.png", insets),
createImageBorder("images/TabbedPane.tabLeft.S.png", insetsS),
createImageBorder("images/TabbedPane.tabLeft.I.png", insetsS),
createImageBorder("images/TabbedPane.tabLeft.D.png", insets),
createImageBorder("images/TabbedPane.tabLeft.DS.png", insetsS),};
}
return tabLeftBorder[i];
}
private Hashtable mnemonicToIndexMap;
/**
* InputMap used for mnemonics. Only non-null if the JTabbedPane has
* mnemonics associated with it. Lazily created in initMnemonics.
*/
private InputMap mnemonicInputMap;
/**
* Changeable UIManager property prefix.
* The value is changed by QuaquaPantherTabbedPaneUI.
*/
private String propertyPrefix = "TabbedPane" + ".";
protected String getPropertyPrefix() {
return propertyPrefix;
}
public void setPropertyPrefix(String newValue) {
propertyPrefix = newValue;
}
public static ComponentUI createUI(JComponent x) {
return new QuaquaJaguarTabbedPaneUI();
}
@Override
protected LayoutManager createLayoutManager() {
return new TabbedPaneLayout();
}
@Override
protected PropertyChangeListener createPropertyChangeListener() {
return new QuaquaPropertyChangeHandler();
}
@Override
protected MouseListener createMouseListener() {
return new QuaquaMouseHandler();
}
@Override
protected void installDefaults() {
super.installDefaults();
String prefix = getPropertyPrefix();
LookAndFeel.installColorsAndFont(tabPane, prefix + "background",
prefix + "foreground", prefix + "font");
// Workaround for Java 1.4: For some reason, the background color
// is not set by LookAndFeel.installColorsAndFont.
tabPane.setBackground(UIManager.getColor(prefix + "background"));
highlight = UIManager.getColor(prefix + "light");
lightHighlight = UIManager.getColor(prefix + "highlight");
shadow = UIManager.getColor(prefix + "shadow");
darkShadow = UIManager.getColor(prefix + "darkShadow");
focus = UIManager.getColor(prefix + "focus");
textIconGap = UIManager.getInt(prefix + "textIconGap");
tabInsets = UIManager.getInsets(prefix + "tabInsets");
selectedTabPadInsets = UIManager.getInsets(prefix + "selectedTabPadInsets");
tabAreaInsets = UIManager.getInsets(prefix + "tabAreaInsets");
contentBorderInsets = UIManager.getInsets(prefix + "contentBorderInsets");
tabRunOverlay = UIManager.getInt(prefix + "tabRunOverlay");
tabAreaBackground = UIManager.getColor(prefix + "tabAreaBackground");
selectColor = UIManager.getColor(prefix + "selected");
selectHighlight = UIManager.getColor(prefix + "selectHighlight");
//selectColor = UIManager.getColor("MenuItem.selectionBackground");
disabledForeground = UIManager.getColor(prefix + "disabledForeground");
LookAndFeel.installBorder(tabPane, prefix + "border");
tabPane.setOpaque(UIManager.getBoolean(prefix + "opaque"));
}
@Override
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:
paintTabBorderLeft(tabIndex, g, x, y, w, h, bottom, right, isSelected);
break;
case BOTTOM:
paintTabBorderBottom(tabIndex, g, x, y, w, h, bottom, right, isSelected);
break;
case RIGHT:
paintTabBorderRight(tabIndex, g, x, y, w, h, bottom, right, isSelected);
break;
case TOP:
default:
paintTabBorderTop(tabIndex, g, x, y, w, h, bottom, right, isSelected);
}
}
protected void paintTabBorderTop(int tabIndex, Graphics g,
int x, int y, int w, int h,
int btm, int rght,
boolean isSelected) {
/*
* Indices 0: enabled
* 1: selected
* 2: inactive
* 3: disabled
* 4: disabled selected
*/
int border;
if (isSelected) {
if (tabPane.isEnabled()) {
if (isFrameActive) {
border = 1;
} else {
border = 2;
}
} else {
border = 4;
}
getTabTopBorder(border).paintBorder(tabPane, g, x - 1, y - 1, w + 3, h + 2);
} else {
if (tabPane.isEnabled()) {
if (isFrameActive) {
border = 0;
} else {
// border = 3;
border = 0; /* click-through behavior:use same border for inactive frames, we have got click */
}
} else {
border = 3;
}
getTabTopBorder(border).paintBorder(tabPane, g, x - 1, y - 1, w + 3, h + 1);
}
}
protected boolean shouldFillGap(int currentRun, int tabIndex, int x, int y) {
boolean result = false;
if (currentRun == runCount - 2) { // If it's the second to last row.
Rectangle lastTabBounds = getTabBounds(tabPane, tabPane.getTabCount() - 1);
Rectangle tabBounds = getTabBounds(tabPane, tabIndex);
if (QuaquaUtilities.isLeftToRight(tabPane)) {
int lastTabRight = lastTabBounds.x + lastTabBounds.width - 1;
// is the right edge of the last tab to the right
// of the left edge of the current tab?
if (lastTabRight > tabBounds.x + 2) {
return true;
}
} else {
int lastTabLeft = lastTabBounds.x;
int currentTabRight = tabBounds.x + tabBounds.width - 1;
// is the left edge of the last tab to the left
// of the right edge of the current tab?
if (lastTabLeft < currentTabRight - 2) {
return true;
}
}
} else {
// fill in gap for all other rows except last row
result = currentRun != runCount - 1;
}
return result;
}
protected Color getColorForGap(int currentRun, int x, int y) {
final int shadowWidth = 4;
int selectedIndex = tabPane.getSelectedIndex();
int startIndex = tabRuns[currentRun + 1];
int endIndex = lastTabInRun(tabPane.getTabCount(), currentRun + 1);
int tabOverGap = -1;
// Check each tab in the row that is 'on top' of this row
for (int i = startIndex; i <= endIndex; ++i) {
Rectangle tabBounds = getTabBounds(tabPane, i);
int tabLeft = tabBounds.x;
int tabRight = (tabBounds.x + tabBounds.width) - 1;
// Check to see if this tab is over the gap
if (QuaquaUtilities.isLeftToRight(tabPane)) {
if (tabLeft <= x && tabRight - shadowWidth > x) {
return selectedIndex == i ? selectColor : tabPane.getBackgroundAt(i);
}
} else {
if (tabLeft + shadowWidth < x && tabRight >= x) {
return selectedIndex == i ? selectColor : tabPane.getBackgroundAt(i);
}
}
}
return tabPane.getBackground();
}
protected void paintTabBorderLeft(int tabIndex, Graphics g,
int x, int y, int w, int h,
int btm, int rght,
boolean isSelected) {
/*
* Indices 0: enabled
* 1: selected
* 2: inactive
* 3: disabled
* 4: disabled selected
*/
int border;
if (isSelected) {
if (tabPane.isEnabled()) {
if (isFrameActive) {
border = 1;
} else {
border = 2;
}
} else {
border = 4;
}
getTabLeftBorder(border).paintBorder(tabPane, g, x - 2, y, w + 4, h + 1);
} else {
if (tabPane.isEnabled()) {
if (isFrameActive) {
border = 0;
} else {
// border = 3;
border = 0; /* click-through behavior:use same border for inactive frames, we have got click */
}
} else {
border = 3;
}
getTabLeftBorder(border).paintBorder(tabPane, g, x - 2, y, w + 2, h + 1);
}
}
protected void paintTabBorderBottom(int tabIndex, Graphics g,
int x, int y, int w, int h,
int btm, int rght,
boolean isSelected) {
/*
* Indices 0: enabled
* 1: selected
* 2: inactive
* 3: disabled
* 4: disabled selected
*/
int border;
if (isSelected) {
if (tabPane.isEnabled()) {
if (isFrameActive) {
border = 1;
} else {
border = 2;
}
} else {
border = 4;
}
getTabBottomBorder(border).paintBorder(tabPane, g, x - 1, y - 1, w + 3, h + 2);
} else {
if (tabPane.isEnabled()) {
if (isFrameActive) {
border = 0;
} else {
// border = 3;
border = 0; /* click-through behavior:use same border for inactive frames, we have got click */
}
} else {
border = 3;
}
getTabBottomBorder(border).paintBorder(tabPane, g, x - 1, y, w + 3, h + 1);
}
}
protected void paintTabBorderRight(int tabIndex, Graphics g,
int x, int y, int w, int h,
int btm, int rght,
boolean isSelected) {
/*
* Indices 0: enabled
* 1: selected
* 2: inactive
* 3: disabled
* 4: disabled selected
*/
int border;
if (isSelected) {
if (tabPane.isEnabled()) {
if (isFrameActive) {
border = 1;
} else {
border = 2;
}
} else {
border = 4;
}
getTabRightBorder(border).paintBorder(tabPane, g, x - 2, y, w + 4, h + 1);
} else {
if (tabPane.isEnabled()) {
if (isFrameActive) {
border = 0;
} else {
// border = 3;
border = 0; /* click-through behavior:use same border for inactive frames, we have got click */
}
} else {
border = 3;
}
getTabRightBorder(border).paintBorder(tabPane, g, x, y, w + 2, h + 1);
}
}
@Override
public void update(Graphics g, JComponent c) {
if (c.isOpaque()) {
g.setColor(tabAreaBackground);
g.fillRect(0, 0, c.getWidth(), c.getHeight());
}
paint(g, c);
}
/**
* Overridden to do nothing for the Quaqua L&F.
*/
@Override
protected void paintTabBackground(Graphics g, int tabPlacement,
int tabIndex, int x, int y, int w, int h, boolean isSelected) {
}
/**
* Overridden to do nothing for the Quaqua L&F.
*/
@Override
protected int getTabLabelShiftX(int tabPlacement, int tabIndex, boolean isSelected) {
return 0;
}
protected Insets getVisualMargin() {
Insets margin = (Insets) tabPane.getClientProperty("Quaqua.Component.visualMargin");
if (margin == null) {
margin = UIManager.getInsets("Component.visualMargin");
}
switch (tabPane.getTabPlacement()) {
case LEFT:
return InsetsUtil.add(-1, -2, -4, -3, margin);
case BOTTOM:
return InsetsUtil.add(-1, -3, -5, -3, margin);
case RIGHT:
return InsetsUtil.add(-1, -3, -4, -2, margin);
case TOP:
default:
return InsetsUtil.add(-3, -3, -4, -3, margin);
}
}
/**
* Overridden to return specific shift values for the Quaqua L&F.
* FIXME We should find another way to align the labels properly.
*/
@Override
protected int getTabLabelShiftY(int tabPlacement, int tabIndex, boolean isSelected) {
switch (tabPlacement) {
case LEFT:
return 0;
case BOTTOM:
return -1;
case RIGHT:
return 0;
case TOP:
default:
return 1;
}
}
protected void repaintTabArea() {
int tabPlacement = tabPane.getTabPlacement();
Rectangle clipRect = new Rectangle();
Insets insets = InsetsUtil.add(tabPane.getInsets(), getVisualMargin());
Dimension size = tabPane.getSize();
switch (tabPlacement) {
case LEFT:
clipRect.setBounds(
insets.left,
insets.top,
calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth) + 6,
size.height - insets.bottom - insets.top);
break;
case BOTTOM:
int totalTabHeight = calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight);
clipRect.setBounds(
insets.left,
size.height - insets.bottom - totalTabHeight - 6,
size.width - insets.left - insets.right,
totalTabHeight + 6);
break;
case RIGHT:
int totalTabWidth = calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth);
clipRect.setBounds(
size.width - insets.right - totalTabWidth - 6,
insets.top,
totalTabWidth + 6,
size.height - insets.top - insets.bottom);
break;
case TOP:
default:
clipRect.setBounds(
insets.left,
insets.top,
size.width - insets.right - insets.left,
calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight) + 6);
}
tabPane.repaint(clipRect);
}
@Override
public void paint(Graphics gr, JComponent c) {
Graphics2D g = (Graphics2D) gr;
Object oldHints = QuaquaUtilities.beginGraphics(g);
int tabPlacement = tabPane.getTabPlacement();
isFrameActive = QuaquaUtilities.isOnActiveWindow(c);
Dimension size = c.getSize();
// Paint the background for the tab area
if (tabPane.isOpaque()) {
//g.setColor( c.getBackground() );
g.setPaint(TextureColor.getPaint(c.getBackground(), c));
g.fillRect(0, 0, size.width, size.height);
/*
switch ( tabPlacement ) {
case LEFT:
g.fillRect( insets.left, insets.top,
calculateTabAreaWidth( tabPlacement, runCount, maxTabWidth ),
size.height - insets.bottom - insets.top );
break;
case BOTTOM:
int totalTabHeight = calculateTabAreaHeight( tabPlacement, runCount, maxTabHeight );
g.fillRect( insets.left, size.height - insets.bottom - totalTabHeight,
size.width - insets.left - insets.right,
totalTabHeight );
break;
case RIGHT:
int totalTabWidth = calculateTabAreaWidth( tabPlacement, runCount, maxTabWidth );
g.fillRect( size.width - insets.right - totalTabWidth,
insets.top, totalTabWidth,
size.height - insets.top - insets.bottom );
break;
case TOP:
default:
g.fillRect( insets.left, insets.top,
size.width - insets.right - insets.left,
calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight) );
paintHighlightBelowTab();
}*/
}
int tc = tabPane.getTabCount();
if (tabCount != tc) {
tabCount = tc;
updateMnemonics();
}
int selectedIndex = tabPane.getSelectedIndex();
//int tabPlacement = tabPane.getTabPlacement();
ensureCurrentLayout();
// Paint content border
paintContentBorder(g, tabPlacement, selectedIndex);
// Paint tab area
// If scrollable tabs are enabled, the tab area will be
// painted by the scrollable tab panel instead.
//
//if (!scrollableTabLayoutEnabled()) { // WRAP_TAB_LAYOUT
paintTabArea(g, tabPlacement, selectedIndex);
QuaquaUtilities.endGraphics((Graphics2D) g, oldHints);
Debug.paint(g, c, this);
}
/**
* Paints the tabs in the tab area.
* Invoked by paint().
* The graphics parameter must be a valid Graphics
* object. Tab placement may be either:
* JTabbedPane.TOP
, JTabbedPane.BOTTOM
,
* JTabbedPane.LEFT
, or JTabbedPane.RIGHT
.
* The selected index must be a valid tabbed pane tab index (0 to
* tab count - 1, inclusive) or -1 if no tab is currently selected.
* The handling of invalid parameters is unspecified.
*
* @param g the graphics object to use for rendering
* @param tabPlacement the placement for the tabs within the JTabbedPane
* @param selectedIndex the tab index of the selected component
*
* @since 1.4
*/
@Override
protected void paintTabArea(Graphics g, int tabPlacement, int selectedIndex) {
Rectangle iconRect = new Rectangle(),
textRect = new Rectangle();
Rectangle clipRect = g.getClipBounds();
// Paint tabRuns of tabs from back to front
Rectangle tabClipRect = new Rectangle();
for (int i = runCount - 1; i >= 0; i--) {
int start = tabRuns[i];
int next = tabRuns[(i == runCount - 1) ? 0 : i + 1];
int end = (next != 0 ? next - 1 : tabCount - 1);
for (int j = start; j <= end; j++) {
tabClipRect.setBounds(rects[j]);
tabClipRect.grow(2, 2);
if (tabClipRect.intersects(clipRect)) {
paintTab(g, tabPlacement, rects, j, iconRect, textRect);
}
}
}
// Paint selected tab if its in the front run
// since it may overlap other tabs
if (selectedIndex >= 0 && getRunForTab(tabCount, selectedIndex) == 0) {
tabClipRect.setBounds(rects[selectedIndex]);
tabClipRect.grow(2, 2);
if (tabClipRect.intersects(clipRect)) {
paintTab(g, tabPlacement, rects, selectedIndex, iconRect, textRect);
}
}
}
private void ensureCurrentLayout() {
if (!tabPane.isValid()) {
tabPane.validate();
}
/* If tabPane doesn't have a peer yet, the validate() call will
* silently fail. We handle that by forcing a layout if tabPane
* is still invalid. See bug 4237677.
*/
if (!tabPane.isValid()) {
TabbedPaneLayout layout = (TabbedPaneLayout) tabPane.getLayout();
layout.calculateLayoutInfo();
}
}
/**
* Reloads the mnemonics. This should be invoked when a memonic changes,
* when the title of a mnemonic changes, or when tabs are added/removed.
*/
protected void updateMnemonics() {
// XXX - This needs JDK 1.4 to work.
resetMnemonics();
for (int counter = tabPane.getTabCount() - 1; counter >= 0;
counter--) {
int mnemonic = tabPane.getMnemonicAt(counter);
if (mnemonic > 0) {
addMnemonic(counter, mnemonic);
}
}
}
/**
* Resets the mnemonics bindings to an empty state.
*/
protected void resetMnemonics() {
if (mnemonicToIndexMap != null) {
mnemonicToIndexMap.clear();
mnemonicInputMap.clear();
}
}
/**
* Adds the specified mnemonic at the specified index.
*/
protected void addMnemonic(int index, int mnemonic) {
if (mnemonicToIndexMap == null) {
initMnemonics();
}
mnemonicInputMap.put(KeyStroke.getKeyStroke(mnemonic, Event.ALT_MASK),
"setSelectedIndex");
mnemonicToIndexMap.put(mnemonic, index);
}
/**
* Installs the state needed for mnemonics.
*/
private void initMnemonics() {
mnemonicToIndexMap = new Hashtable();
mnemonicInputMap = new InputMapUIResource();
mnemonicInputMap.setParent(SwingUtilities.getUIInputMap(tabPane,
JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT));
SwingUtilities.replaceUIInputMap(tabPane,
JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
mnemonicInputMap);
}
protected void paintHighlightBelowTab() {
}
/**
* Overridden to do nothing for the Quaqua L&F.
*/
@Override
protected void paintFocusIndicator(Graphics g, int tabPlacement,
Rectangle[] rects, int tabIndex,
Rectangle iconRect, Rectangle textRect,
boolean isSelected) {
}
@Override
protected Insets getTabInsets(int tabPlacement, int tabIndex) {
if (tabPlacement == RIGHT || tabPlacement == LEFT) {
return new Insets(tabInsets.top, 6, tabInsets.bottom, 5);
} else {
return tabInsets;
}
}
@Override
protected Insets getTabAreaInsets(int tabPlacement) {
//return currentTabAreaInsets;
// FIXME We should change the layout code instead of adjusting the
// insets.
Insets i = (Insets) super.getTabAreaInsets(tabPlacement).clone();
switch (tabPlacement) {
case TOP:
i.top -= 1;
break;
case LEFT:
break;
case BOTTOM:
i.bottom += 1;
break;
case RIGHT:
break;
}
return i;
}
@Override
protected Insets getContentBorderInsets(int tabPlacement) {
// We eliminate the insets at the tab location
// because the content border is drawn as a shadow,
// which runs through below the tabs.
Insets insets = contentBorderInsets;
Component selectedComponent = tabPane.getSelectedComponent();
if (selectedComponent instanceof JComponent) {
insets = (Insets) ((JComponent) selectedComponent).getClientProperty("Quaqua.TabbedPaneChild.contentInsets");
}
if (insets == null) {
insets = contentBorderInsets;
}
currentContentBorderInsets.top = insets.top + 3;
currentContentBorderInsets.left = insets.left + 3;
currentContentBorderInsets.bottom = insets.bottom + 3;
currentContentBorderInsets.right = insets.right + 3;
switch (tabPlacement) {
case LEFT:
currentContentBorderInsets.left += 5;
currentContentBorderInsets.bottom += 1;
break;
case RIGHT:
currentContentBorderInsets.right += 4;
currentContentBorderInsets.bottom += 1;
break;
case BOTTOM:
currentContentBorderInsets.bottom += 1;
break;
case TOP:
default:
currentContentBorderInsets.top += 4;
}
return currentContentBorderInsets;
}
@Override
protected void paintContentBorder(Graphics g, int tabPlacement, int selectedIndex) {
int width = tabPane.getWidth();
int height = tabPane.getHeight();
Insets insets = InsetsUtil.add(tabPane.getInsets(), getVisualMargin());
int x = insets.left;
int y = insets.top;
int w = width - insets.right - insets.left;
int h = height - insets.top - insets.bottom;
switch (tabPlacement) {
case LEFT:
x += calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth);
w -= (x - insets.left);
paintContentArea(g, x + 6, y + 1, w - 8, h - 6);
paintContentBorderLeftEdge(g, tabPlacement, selectedIndex, x, y, w, h);
break;
case RIGHT:
w -= calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth);
paintContentArea(g, x + 2, y + 1, w - 7, h - 6);
paintContentBorderRightEdge(g, tabPlacement, selectedIndex, x, y, w, h);
break;
case BOTTOM:
h -= calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight);
paintContentArea(g, x + 2, y + 1, w - 4, h - 6);
paintContentBorderBottomEdge(g, tabPlacement, selectedIndex, x, y, w, h);
break;
case TOP:
default:
y += calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight);
h -= (y - insets.top);
paintContentArea(g, x + 2, y + 7, w - 4, h - 12);
paintContentBorderTopEdge(g, tabPlacement, selectedIndex, x, y, w, h);
}
}
private int getDepth(JTabbedPane tp) {
int depth = 1;
Component parent = tp.getParent();
while (parent != null && !(parent instanceof RootPaneContainer)) {
if (parent instanceof JTabbedPane) {
depth++;
}
parent = parent.getParent();
}
return depth;
}
protected void paintContentArea(Graphics gr, int x, int y, int width, int height) {
Graphics2D g = (Graphics2D) gr;
Color contentBackground = null;
Component selectedComponent = tabPane.getSelectedComponent();
if (selectedComponent instanceof JComponent) {
contentBackground = (Color) ((JComponent) selectedComponent).getClientProperty("Quaqua.TabbedPaneChild.contentBackground");
}
if (contentBackground == null) {
contentBackground = tabPane.getBackground();
}
g.setPaint(PaintableColor.getPaint(contentBackground, tabPane, 0, -getDepth(tabPane) % 4));
g.fillRect(x, y, width, height);
}
@Override
protected void paintContentBorderTopEdge(Graphics g, int tabPlacement,
int selectedIndex,
int x, int y, int w, int h) {
Insets contentBorderInsets = getContentBorder().getBorderInsets(tabPane);
getContentBorder().paintBorder(tabPane, g, x, y - contentBorderInsets.top + 1, w, h + contentBorderInsets.top - 1);
/*
* Indices 0: active
* 1: inactive
* 2: disabled
*/
int bar;
if (tabPane.isEnabled()) {
if (isFrameActive) {
bar = 0;
} else {
bar = 1;
}
} else {
bar = 2;
}
getBarTopBorder(bar).paintBorder(
tabPane, g,
x + contentBorderInsets.left,
y,
w - contentBorderInsets.left - contentBorderInsets.right,
7);
}
@Override
protected void paintContentBorderBottomEdge(Graphics g, int tabPlacement,
int selectedIndex,
int x, int y, int w, int h) {
Insets contentBorderInsets = getContentBorder().getBorderInsets(tabPane);
getContentBorder().paintBorder(tabPane, g, x, y, w, h + contentBorderInsets.bottom);
/*
* Indices 0: active
* 1: inactive
* 2: disabled
*/
int bar;
if (tabPane.isEnabled()) {
if (isFrameActive) {
bar = 0;
} else {
bar = 1;
}
} else {
bar = 2;
}
getBarBottomBorder(bar).paintBorder(
tabPane, g,
x + contentBorderInsets.left,
y + h - 6,
w - contentBorderInsets.left - contentBorderInsets.right,
6);
}
@Override
protected void paintContentBorderLeftEdge(Graphics g, int tabPlacement,
int selectedIndex,
int x, int y, int w, int h) {
getContentBorder().paintBorder(tabPane, g, x, y, w, h);
/*
* Indices 0: active
* 1: inactive
* 2: disabled
*/
int bar;
if (tabPane.isEnabled()) {
if (isFrameActive) {
bar = 0;
} else {
bar = 1;
}
} else {
bar = 2;
}
getBarLeftBorder(bar).paintBorder(tabPane, g, x, y + 1, 7, h - 6);
}
@Override
protected void paintContentBorderRightEdge(Graphics g, int tabPlacement,
int selectedIndex,
int x, int y, int w, int h) {
getContentBorder().paintBorder(tabPane, g, x, y, w + 3, h);
/*
* Indices 0: active
* 1: inactive
* 2: disabled
*/
int bar;
if (tabPane.isEnabled()) {
if (isFrameActive) {
bar = 0;
} else {
bar = 1;
}
} else {
bar = 2;
}
getBarRightBorder(bar).paintBorder(tabPane, g, x + w - 6, y + 1, 6, h - 6);
}
@Override
protected int calculateMaxTabHeight(int tabPlacement) {
FontMetrics metrics = getFontMetrics();
int height = metrics.getHeight();
boolean tallerIcons = false;
for (int i = 0; i < tabPane.getTabCount(); ++i) {
Icon icon = tabPane.getIconAt(i);
if (icon != null) {
if (icon.getIconHeight() > height) {
tallerIcons = true;
break;
}
}
}
return super.calculateMaxTabHeight(tabPlacement)
- (tallerIcons ? (tabInsets.top + tabInsets.bottom) : 0);
}
@Override
protected int getTabRunOverlay(int tabPlacement) {
if (tabPlacement == LEFT || tabPlacement == RIGHT) {
return 2;
} else {
return 1;
}
/*
// Tab runs laid out vertically should overlap
// at least as much as the largest slant
if ( tabPlacement == LEFT || tabPlacement == RIGHT ) {
int maxTabHeight = calculateMaxTabHeight(tabPlacement);
return maxTabHeight / 2;
}
return 0;
*/
}
// Don't rotate runs!
protected boolean shouldRotateTabRuns(int tabPlacement, int selectedRun) {
return false;
}
// Pad all tab runs if there is more than one run.
@Override
protected boolean shouldPadTabRun(int tabPlacement, int run) {
return runCount > 1;
}
private boolean isLastInRun(int tabIndex) {
int run = getRunForTab(tabPane.getTabCount(), tabIndex);
int lastIndex = lastTabInRun(tabPane.getTabCount(), run);
return tabIndex == lastIndex;
}
@Override
protected void paintText(Graphics g, int tabPlacement,
Font font, FontMetrics metrics, int tabIndex,
String title, Rectangle textRect,
boolean isSelected) {
g.setFont(font);
// This needs JDK 1.4 to work.
View v = getTextViewForTab(tabIndex);
if (v != null) {
// html
v.paint(g, textRect);
} else {
// plain text
// This needs JDK 1.4 to work.
int mnemIndex = tabPane.getDisplayedMnemonicIndexAt(tabIndex);
if (tabPane.isEnabled() && tabPane.isEnabledAt(tabIndex)) {
g.setColor(tabPane.getForegroundAt(tabIndex));
QuaquaUtilities.drawStringUnderlineCharAt(g,
title, mnemIndex,
textRect.x, textRect.y + metrics.getAscent());
} else { // tab disabled
g.setColor(disabledForeground);
QuaquaUtilities.drawStringUnderlineCharAt(g,
title, mnemIndex,
textRect.x, textRect.y + metrics.getAscent());
}
}
}
/**
* This inner class is marked "public" due to a compiler bug.
* This class should be treated as a "protected" inner class.
* Instantiate it only within subclasses of MetalTabbedPaneUI.
*/
public class TabbedPaneLayout extends BasicTabbedPaneUI.TabbedPaneLayout {
@Override
protected void calculateTabRects(int tabPlacement, int tabCount) {
Dimension size = tabPane.getSize();
Insets insets = InsetsUtil.add(tabPane.getInsets(), getVisualMargin());
Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
FontMetrics metrics = getFontMetrics();
int fontHeight = metrics.getHeight();
int selectedIndex = tabPane.getSelectedIndex();
int tabRunOverlay;
int i, j;
int x, y;
int returnAt;
boolean verticalTabRuns = (tabPlacement == LEFT || tabPlacement == RIGHT);
boolean leftToRight = QuaquaUtilities.isLeftToRight(tabPane);
//
// Calculate bounds within which a tab run must fit
//
switch (tabPlacement) {
case LEFT:
maxTabWidth = calculateMaxTabWidth(tabPlacement);
x = insets.left + tabAreaInsets.left;
y = insets.top + tabAreaInsets.top;
returnAt = size.height - (insets.bottom + tabAreaInsets.bottom);
break;
case RIGHT:
maxTabWidth = calculateMaxTabWidth(tabPlacement);
x = size.width - insets.right - tabAreaInsets.right - maxTabWidth;
y = insets.top + tabAreaInsets.top;
returnAt = size.height - (insets.bottom + tabAreaInsets.bottom);
break;
case BOTTOM:
maxTabHeight = calculateMaxTabHeight(tabPlacement);
x = insets.left + tabAreaInsets.left;
y = size.height - insets.bottom - tabAreaInsets.bottom - maxTabHeight;
returnAt = size.width - (insets.right + tabAreaInsets.right);
break;
case TOP:
default:
maxTabHeight = calculateMaxTabHeight(tabPlacement);
x = insets.left + tabAreaInsets.left;
y = insets.top + tabAreaInsets.top;
returnAt = size.width - (insets.right + tabAreaInsets.right);
break;
}
tabRunOverlay = getTabRunOverlay(tabPlacement);
runCount = 0;
selectedRun = -1;
if (tabCount == 0) {
return;
}
// Run through tabs and partition them into runs
Rectangle rect;
for (i = 0; i < tabCount; i++) {
rect = rects[i];
if (!verticalTabRuns) {
// Tabs on TOP or BOTTOM....
if (i > 0) {
rect.x = rects[i - 1].x + rects[i - 1].width;
} else {
tabRuns[0] = 0;
runCount = 1;
maxTabWidth = 0;
rect.x = x;
}
rect.width = calculateTabWidth(tabPlacement, i, metrics);
maxTabWidth = Math.max(maxTabWidth, rect.width);
// Never move a TAB down a run if it is in the first column.
// Even if there isn't enough room, moving it to a fresh
// line won't help.
if (rect.x != 2 + insets.left && rect.x + rect.width > returnAt) {
if (runCount > tabRuns.length - 1) {
expandTabRunsArray();
}
tabRuns[runCount] = i;
runCount++;
rect.x = x;
}
// Initialize y position in case there's just one run
rect.y = y;
rect.height = maxTabHeight/* - 2*/;
} else {
// Tabs on LEFT or RIGHT...
if (i > 0) {
rect.y = rects[i - 1].y + rects[i - 1].height;
} else {
tabRuns[0] = 0;
runCount = 1;
maxTabHeight = 0;
rect.y = y;
}
rect.height = calculateTabHeight(tabPlacement, i, fontHeight);
maxTabHeight = Math.max(maxTabHeight, rect.height);
// Never move a TAB over a run if it is in the first run.
// Even if there isn't enough room, moving it to a fresh
// column won't help.
if (rect.y != 2 + insets.top && rect.y + rect.height > returnAt) {
if (runCount > tabRuns.length - 1) {
expandTabRunsArray();
}
tabRuns[runCount] = i;
runCount++;
rect.y = y;
}
// Initialize x position in case there's just one column
rect.x = x;
rect.width = maxTabWidth/* - 2*/;
}
if (i == selectedIndex) {
selectedRun = runCount - 1;
}
}
if (runCount > 1) {
// Re-distribute tabs in case last run has leftover space
normalizeTabRuns(tabPlacement, tabCount, verticalTabRuns ? y : x, returnAt);
selectedRun = getRunForTab(tabCount, selectedIndex);
// Rotate run array so that selected run is first
if (shouldRotateTabRuns(tabPlacement)) {
rotateTabRuns(tabPlacement, selectedRun);
}
}
// Determine how much we want to pad the tabs
int maxPad = 0;
/*
for (i = runCount - 1; i >= 0; i--) {
int start = tabRuns[i];
int next = tabRuns[i == (runCount - 1)? 0 : i + 1];
int end = (next != 0? next - 1 : tabCount - 1);
maxPad = Math.max(maxPad, rects[end].x + rects[end].width - rects[start].x);
}*/
switch (tabPlacement) {
case LEFT:
case RIGHT:
maxPad = size.height - tabAreaInsets.top - tabAreaInsets.bottom;
break;
case BOTTOM:
case TOP:
default:
maxPad = size.width - tabAreaInsets.left - tabAreaInsets.right;
break;
}
// Step through runs from back to front to calculate
// tab y locations and to pad runs appropriately
for (i = runCount - 1; i >= 0; i--) {
int start = tabRuns[i];
int next = tabRuns[i == (runCount - 1) ? 0 : i + 1];
int end = (next != 0 ? next - 1 : tabCount - 1);
if (!verticalTabRuns) {
for (j = start; j <= end; j++) {
rect = rects[j];
rect.y = y;
rect.x += getTabRunIndent(tabPlacement, i);
}
if (shouldPadTabRun(tabPlacement, i)) {
padTabRun(tabPlacement, start, end, maxPad);
}
if (tabPlacement == BOTTOM) {
y -= (maxTabHeight - tabRunOverlay);
} else {
y += (maxTabHeight - tabRunOverlay);
}
} else {
for (j = start; j <= end; j++) {
rect = rects[j];
rect.x = x;
rect.y += getTabRunIndent(tabPlacement, i);
}
if (shouldPadTabRun(tabPlacement, i)) {
padTabRun(tabPlacement, start, end, maxPad);
}
if (tabPlacement == RIGHT) {
x -= (maxTabWidth - tabRunOverlay);
} else {
x += (maxTabWidth - tabRunOverlay);
}
}
}
// Pad the selected tab so that it appears raised in front
padSelectedTab(tabPlacement, selectedIndex);
// if right to left and tab placement on the top or
// the bottom, flip x positions and adjust by widths
if (!leftToRight && !verticalTabRuns) {
int rightMargin = size.width - (insets.right + tabAreaInsets.right);
for (i = 0; i < tabCount; i++) {
rects[i].x = rightMargin - rects[i].x - rects[i].width;
}
}
//
// Center tabs vertically or horizontally
// If centered horizontally ensure that all tab runs have
// the same width.
Integer propertyValue = (Integer) tabPane.getClientProperty("Quaqua.TabbedPane.tabAlignment");
int tabAlignment = (propertyValue != null && propertyValue.intValue() == SwingConstants.LEADING) ? SwingConstants.LEADING : SwingConstants.CENTER;
if (tabAlignment == SwingConstants.CENTER) {
switch (tabPlacement) {
case LEFT:
case RIGHT: {
int availableTabAreaHeight = size.height - insets.top - insets.bottom - tabAreaInsets.top - tabAreaInsets.bottom;
int usedTabAreaHeight = 0;
int pad = 0;
for (int run = 0; run < runCount; run++) {
int firstIndex = tabRuns[run];
int lastIndex = lastTabInRun(tabCount, run);
if (run == 0) {
usedTabAreaHeight = 0;
for (i = firstIndex; i <= lastIndex; i++) {
usedTabAreaHeight += rects[i].height;
}
pad = (availableTabAreaHeight - usedTabAreaHeight) / 2;
}
for (i = firstIndex; i <= lastIndex; i++) {
rects[i].y += pad;
}
}
break;
}
case BOTTOM:
case TOP:
default: {
int availableTabAreaWidth = size.width - insets.left - insets.right - tabAreaInsets.left - tabAreaInsets.right;
for (int run = 0; run < runCount; run++) {
int firstIndex = tabRuns[run];
int lastIndex = lastTabInRun(tabCount, run);
int usedTabAreaWidth = 0;
for (i = firstIndex; i <= lastIndex; i++) {
usedTabAreaWidth += rects[i].width;
}
int pad = (availableTabAreaWidth - usedTabAreaWidth) / 2;
for (i = firstIndex; i <= lastIndex; i++) {
rects[i].x += pad;
}
}
break;
}
}
}
}
@Override
protected void normalizeTabRuns(int tabPlacement, int tabCount,
int start, int max) {
// Only normalize the runs for top & bottom; normalizing
// doesn't look right for Metal's vertical tabs
// because the last run isn't padded and it looks odd to have
// fat tabs in the first vertical runs, but slimmer ones in the
// last (this effect isn't noticeable for horizontal tabs).
if (tabPlacement == TOP || tabPlacement == BOTTOM) {
boolean verticalTabRuns = (tabPlacement == LEFT || tabPlacement == RIGHT);
int run = runCount - 1;
boolean keepAdjusting = true;
double weight = 1.33;
// At this point the tab runs are packed to fit as many
// tabs as possible, which can leave the last run with a lot
// of extra space (resulting in very fat tabs on the last run).
// So we'll attempt to distribute this extra space more evenly
// across the runs in order to make the runs look more consistent.
//
// Starting with the last run, determine whether the last tab in
// the previous run would fit (generously) in this run; if so,
// move tab to current run and shift tabs accordingly. Cycle
// through remaining runs using the same algorithm.
//
while (keepAdjusting) {
int last = lastTabInRun(tabCount, run);
int prevLast = lastTabInRun(tabCount, run - 1);
int end;
int prevLastLen;
if (!verticalTabRuns) {
end = rects[last].x + rects[last].width;
prevLastLen = (int) (maxTabWidth * weight);
} else {
end = rects[last].y + rects[last].height;
prevLastLen = (int) (maxTabHeight * weight * 2);
}
// Check if the run has enough extra space to fit the last tab
// from the previous row...
if (max - end > prevLastLen) {
// Insert tab from previous row and shift rest over
tabRuns[run] = prevLast;
if (!verticalTabRuns) {
rects[prevLast].x = start;
} else {
rects[prevLast].y = start;
}
for (int i = prevLast + 1; i <= last; i++) {
if (!verticalTabRuns) {
rects[i].x = rects[i - 1].x + rects[i - 1].width;
} else {
rects[i].y = rects[i - 1].y + rects[i - 1].height;
}
}
} else if (run == runCount - 1) {
// no more room left in last run, so we're done!
keepAdjusting = false;
}
if (run - 1 > 0) {
// check previous run next...
run -= 1;
} else {
// check last run again...but require a higher ratio
// of extraspace-to-tabsize because we don't want to
// end up with too many tabs on the last run!
run = runCount - 1;
weight += .33;
}
}
}
}
// Don't rotate runs!
@Override
protected void rotateTabRuns(int tabPlacement, int selectedRun) {
}
// Don't pad selected tab
@Override
protected void padSelectedTab(int tabPlacement, int selectedIndex) {
}
@Override
public void calculateLayoutInfo() {
int tabCount = tabPane.getTabCount();
assureRectsCreated(tabCount);
calculateTabRects(tabPane.getTabPlacement(), tabCount);
}
@Override
protected void padTabRun(int tabPlacement, int start, int end, int max) {
// Only pad tab runs if they are on top or bottom
if (tabPlacement == TOP || tabPlacement == BOTTOM) {
super.padTabRun(tabPlacement, start, end, max);
}
}
}
/**
* This inner class is marked "public" due to a compiler bug.
* This class should be treated as a "protected" inner class.
* Instantiate it only within subclasses of BasicTabbedPaneUI.
*/
public class QuaquaPropertyChangeHandler extends BasicTabbedPaneUI.PropertyChangeHandler {
@Override
public void propertyChange(PropertyChangeEvent evt) {
String name = evt.getPropertyName();
if (name.equals("Frame.active")) {
repaintTabArea();
} else if (name.equals("JComponent.sizeVariant")) {
QuaquaUtilities.applySizeVariant(tabPane);
}
// Forward everyhting except tabLayoutPolicy change to super class.
// tabLayoutPolicy must not be forward, because it would break
// the functionality of class
// ch.randelshofer.quaqua.panther.QuaquaPantherJaguarTabbedPaneUI.
if (!name.equals("tabLayoutPolicy")) {
super.propertyChange(evt);
}
}
}
public Rectangle getVisualBounds(
JComponent c, int type, int width, int height) {
Rectangle rect = new Rectangle(0, 0, width, height);
InsetsUtil.subtractInto(c.getInsets(), rect);
if (type != VisuallyLayoutable.CLIP_BOUNDS) {
Insets margin = getVisualMargin();
// XXX - Since we have to use bogus values here, our layout
// must be bogus as well.
switch (((JTabbedPane) c).getTabPlacement()) {
case LEFT:
InsetsUtil.addTo(1, 2, 5, 3, margin);
break;
case BOTTOM:
InsetsUtil.addTo(1, 3, 5, 3, margin);
break;
case RIGHT:
InsetsUtil.addTo(1, 3, 5, 2, margin);
break;
case TOP:
default:
InsetsUtil.addTo(3, 3, 5, 3, margin);
break;
}
rect.x += margin.left;
rect.y += margin.top;
rect.width -= margin.left + margin.right;
rect.height -= margin.top + margin.bottom;
}
return rect;
}
/**
* Returns the tab index which intersects the specified point
* in the coordinate space of the component where the
* tabs are actually rendered, which could be the JTabbedPane
* (for WRAP_TAB_LAYOUT) or a ScrollableTabPanel (SCROLL_TAB_LAYOUT).
*/
private int getTabAtLocation(int x, int y) {
ensureCurrentLayout();
int tCount = tabPane.getTabCount();
for (int i = 0; i < tCount; i++) {
if (rects[i].contains(x, y)) {
return i;
}
}
return -1;
}
@Override
public int getBaseline(JComponent component, int width, int height) {
return -1;
}
@Override
public void navigateSelectedTab(int direction) {
super.navigateSelectedTab(direction);
}
public Integer getIndexForMnemonic(int mnemonic) {
return (Integer) mnemonicToIndexMap.get(mnemonic);
}
public boolean requestFocusForVisibleComponent() {
Component visibleComponent = getVisibleComponent();
if (visibleComponent != null && visibleComponent.isFocusTraversable()) {
QuaquaUtilities.compositeRequestFocus(visibleComponent);
return true;
} else if (visibleComponent instanceof JComponent) {
if (((JComponent) visibleComponent).requestDefaultFocus()) {
return true;
}
}
return false;
}
/**
* This inner class is marked "public" due to a compiler bug.
* This class should be treated as a "protected" inner class.
* Instantiate it only within subclasses of BasicTabbedPaneUI.
*/
public class QuaquaMouseHandler extends MouseAdapter {
@Override
public void mousePressed(MouseEvent e) {
if (!tabPane.isEnabled()) {
return;
}
int tabIndex = getTabAtLocation(e.getX(), e.getY());
if (tabIndex >= 0 && tabPane.isEnabledAt(tabIndex)) {
if (tabIndex == tabPane.getSelectedIndex()) {
if (tabPane.isRequestFocusEnabled()) {
tabPane.requestFocus();
tabPane.repaint(getTabBounds(tabPane, tabIndex));
}
} else {
tabPane.setSelectedIndex(tabIndex);
}
}
}
}
@Override
protected void installComponents() {
// empty
// We must not call super, because with Java 1.4 and higher,
// this would set the 'tabScroller' variable with a non-null value.
}
@Override
protected ChangeListener createChangeListener() {
return new TabSelectionHandler();
}
/**
* This inner class is marked "public" due to a compiler bug.
* This class should be treated as a "protected" inner class.
* Instantiate it only within subclasses of BasicTabbedPaneUI.
*/
private static class TabSelectionHandler implements ChangeListener {
public void stateChanged(ChangeEvent e) {
JTabbedPane tabPane = (JTabbedPane) e.getSource();
tabPane.revalidate();
tabPane.repaint();
/*
if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) {
int index = tabPane.getSelectedIndex();
if (index < rects.length && index != -1) {
tabScroller.tabPanel.scrollRectToVisible(rects[index]);
}
}*/
}
}
}