com.jtattoo.plaf.BaseTabbedPaneUI Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of tinyMediaManager-LaF Show documentation
Show all versions of tinyMediaManager-LaF Show documentation
The look and feel for tinyMediaManager
/*
* Copyright 2003 Sun Microsystems, Inc. All rights reserved.
* SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
/*
* Copyright (c) 2002 and later by MH Software-Entwicklung. All Rights Reserved.
*
* JTattoo is multiple licensed. If your are an open source developer you can use
* it under the terms and conditions of the GNU General Public License version 2.0
* or later as published by the Free Software Foundation.
*
* see: gpl-2.0.txt
*
* If you pay for a license you will become a registered user who could use the
* software under the terms and conditions of the GNU Lesser General Public License
* version 2.0 or later with classpath exception as published by the Free Software
* Foundation.
*
* see: lgpl-2.0.txt
* see: classpath-exception.txt
*
* Registered users could also use JTattoo under the terms and conditions of the
* Apache License, Version 2.0 as published by the Apache Software Foundation.
*
* see: APACHE-LICENSE-2.0.txt
*/
package com.jtattoo.plaf;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Component;
import java.awt.Composite;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Event;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.ContainerEvent;
import java.awt.event.ContainerListener;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionAdapter;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Area;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.HashMap;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JTabbedPane;
import javax.swing.JViewport;
import javax.swing.KeyStroke;
import javax.swing.LookAndFeel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.plaf.ActionMapUIResource;
import javax.swing.plaf.ComponentInputMapUIResource;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.TabbedPaneUI;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicGraphicsUtils;
import javax.swing.plaf.basic.BasicHTML;
import javax.swing.text.View;
/**
* This class is a modified copy of the javax.swing.plaf.basic.BasicTabbedPaneUI
*
* A Basic L&F implementation of TabbedPaneUI.
*
* @version 1.87 06/08/99
* @author Amy Fowler
* @author Philip Milne
* @author Steve Wilson
* @author Tom Santos
* @author Dave Moore
* @author Michael Hagen
*/
public class BaseTabbedPaneUI extends TabbedPaneUI implements SwingConstants {
protected static final Insets NULL_BORDER_INSETS = new Insets(0, 0, 0, 0);
protected static final int GAP = 5;
// Instance variables initialized at installation
protected JTabbedPane tabPane;
protected Color tabAreaBackground;
protected Color selectedColor;
protected int textIconGap;
protected int tabRunOverlay;
protected Insets tabInsets;
protected Insets selectedTabPadInsets;
protected Insets tabAreaInsets;
protected Insets contentBorderInsets;
// Transient variables (recalculated each time TabbedPane is layed out)
protected int tabRuns[] = new int[10];
protected int runCount = 0;
protected int selectedRun = -1;
protected Rectangle rects[] = new Rectangle[0];
protected int maxTabHeight;
protected int maxTabWidth;
// Listeners
protected ChangeListener tabChangeListener;
protected ComponentListener tabComponentListener;
protected PropertyChangeListener propertyChangeListener;
protected MouseListener mouseListener;
protected MouseMotionListener mouseMotionListener;
protected FocusListener focusListener;
// PENDING(api): See comment for ContainerHandler
private ContainerListener containerListener;
// Private instance data
private Insets currentPadInsets = new Insets(0, 0, 0, 0);
private Insets currentTabAreaInsets = new Insets(0, 0, 0, 0);
private Component visibleComponent;
// PENDING(api): See comment for ContainerHandler
private ArrayList htmlViews;
private HashMap mnemonicToIndexMap;
/**
* InputMap used for mnemonics. Only non-null if the JTabbedPane has mnemonics associated with it. Lazily created in initMnemonics.
*/
private InputMap mnemonicInputMap;
// For use when tabLayoutPolicy = SCROLL_TAB_LAYOUT
private ScrollableTabSupport tabScroller;
private TabContainer tabContainer;
/**
* A rectangle used for general layout calculations in order to avoid constructing many new Rectangles on the fly.
*/
protected transient Rectangle calcRect = new Rectangle(0, 0, 0, 0);
/**
* Number of tabs. When the count differs, the mnemonics are updated.
*/
// PENDING: This wouldn't be necessary if JTabbedPane had a better
// way of notifying listeners when the count changed.
private int tabCount;
protected int oldRolloverIndex = -1;
protected int rolloverIndex = -1;
protected boolean roundedTabs = true;
protected boolean simpleButtonBorder = false;
public static ComponentUI createUI(JComponent c) {
return new BaseTabbedPaneUI();
}
// UI Installation/De-installation
public void installUI(JComponent c) {
this.tabPane = (JTabbedPane) c;
c.setLayout(createLayoutManager());
installComponents();
installDefaults();
installListeners();
installKeyboardActions();
}
public void uninstallUI(JComponent c) {
uninstallKeyboardActions();
uninstallListeners();
uninstallDefaults();
uninstallComponents();
c.setLayout(null);
this.tabPane = null;
}
/**
* Invoked by installUI
to create a layout manager object to manage the JTabbedPane
.
*
* @return a layout manager object
*
* @see TabbedPaneLayout
* @see javax.swing.JTabbedPane#getTabLayoutPolicy
*/
protected LayoutManager createLayoutManager() {
if (JTattooUtilities.getJavaVersion() >= 1.4) {
if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) {
return new TabbedPaneScrollLayout();
}
}
/* WRAP_TAB_LAYOUT */
return new TabbedPaneLayout();
}
/*
* In an attempt to preserve backward compatibility for programs which have extended BaseTabbedPaneUI to do their own layout, the UI uses the
* installed layoutManager (and not tabLayoutPolicy) to determine if scrollTabLayout is enabled.
*/
protected boolean scrollableTabLayoutEnabled() {
return (tabPane.getLayout() instanceof TabbedPaneScrollLayout);
}
/**
* Creates and installs any required subcomponents for the JTabbedPane. Invoked by installUI.
*
* @since 1.4
*/
protected void installComponents() {
if (scrollableTabLayoutEnabled()) {
if (tabScroller == null) {
tabScroller = new ScrollableTabSupport(tabPane.getTabPlacement());
tabPane.add(tabScroller.viewport);
tabPane.add(tabScroller.scrollForwardButton);
tabPane.add(tabScroller.scrollBackwardButton);
tabPane.add(tabScroller.popupMenuButton);
tabScroller.tabPanel.setBackground(tabAreaBackground);
}
}
installTabContainer();
}
private Component getTabComponentAt(int index) {
if (JTattooUtilities.getJavaVersion() >= 1.6) {
return tabPane.getTabComponentAt(index);
}
return null;
}
private void installTabContainer() {
if (JTattooUtilities.getJavaVersion() >= 1.6) {
for (int i = 0; i < tabPane.getTabCount(); i++) {
Component tabComponent = getTabComponentAt(i);
if (tabComponent != null) {
if (tabContainer == null) {
tabContainer = new TabContainer();
}
tabContainer.add(tabComponent);
addMyPropertyChangeListeners(tabComponent);
}
}
if (tabContainer == null) {
return;
}
if (scrollableTabLayoutEnabled()) {
tabScroller.tabPanel.add(tabContainer);
}
else {
tabPane.add(tabContainer);
}
}
}
/**
* Removes any installed subcomponents from the JTabbedPane. Invoked by uninstallUI.
*
* @since 1.4
*/
protected void uninstallComponents() {
uninstallTabContainer();
if (scrollableTabLayoutEnabled()) {
tabPane.remove(tabScroller.viewport);
tabPane.remove(tabScroller.scrollForwardButton);
tabPane.remove(tabScroller.scrollBackwardButton);
tabPane.remove(tabScroller.popupMenuButton);
tabScroller = null;
}
}
private void addMyPropertyChangeListeners(Component component) {
component.addPropertyChangeListener(new MyTabComponentListener());
if (component instanceof Container) {
Container container = (Container) component;
for (int i = 0; i < container.getComponentCount(); i++) {
Component c = container.getComponent(i);
addMyPropertyChangeListeners(c);
}
}
}
private void removeMyPropertyChangeListeners(Component component) {
PropertyChangeListener[] listeners = component.getPropertyChangeListeners();
for (int i = 0; i < listeners.length; i++) {
if (listeners[i] instanceof MyTabComponentListener) {
component.removePropertyChangeListener(listeners[i]);
}
}
if (component instanceof Container) {
Container container = (Container) component;
for (int i = 0; i < container.getComponentCount(); i++) {
Component c = container.getComponent(i);
removeMyPropertyChangeListeners(c);
}
}
}
private void uninstallTabContainer() {
if (JTattooUtilities.getJavaVersion() >= 1.6) {
if (tabContainer == null) {
return;
}
// Remove all the tabComponents, making sure not to notify the tabbedpane.
tabContainer.notifyTabbedPane = false;
for (int i = 0; i < tabContainer.getComponentCount(); i++) {
Component c = tabContainer.getComponent(i);
removeMyPropertyChangeListeners(c);
}
tabContainer.removeAll();
if (scrollableTabLayoutEnabled()) {
tabScroller.tabPanel.remove(tabContainer);
}
else {
tabPane.remove(tabContainer);
}
tabContainer = null;
}
}
protected void installDefaults() {
LookAndFeel.installColorsAndFont(tabPane, "TabbedPane.background", "TabbedPane.foreground", "TabbedPane.font");
tabAreaBackground = UIManager.getColor("TabbedPane.tabAreaBackground");
selectedColor = UIManager.getColor("TabbedPane.selected");
textIconGap = UIManager.getInt("TabbedPane.textIconGap");
tabInsets = UIManager.getInsets("TabbedPane.tabInsets");
selectedTabPadInsets = UIManager.getInsets("TabbedPane.selectedTabPadInsets");
tabAreaInsets = UIManager.getInsets("TabbedPane.tabAreaInsets");
contentBorderInsets = UIManager.getInsets("TabbedPane.contentBorderInsets");
tabRunOverlay = UIManager.getInt("TabbedPane.tabRunOverlay");
tabPane.setBorder(UIManager.getBorder("TabbedPane.boder"));
}
protected void uninstallDefaults() {
tabInsets = null;
selectedTabPadInsets = null;
tabAreaInsets = null;
contentBorderInsets = null;
}
protected void installListeners() {
if ((propertyChangeListener = createPropertyChangeListener()) != null) {
tabPane.addPropertyChangeListener(propertyChangeListener);
}
if ((tabChangeListener = createChangeListener()) != null) {
tabPane.addChangeListener(tabChangeListener);
}
if ((tabComponentListener = createComponentListener()) != null) {
tabPane.addComponentListener(tabComponentListener);
}
if ((mouseListener = createMouseListener()) != null) {
if (scrollableTabLayoutEnabled()) {
tabScroller.tabPanel.addMouseListener(mouseListener);
}
else { // WRAP_TAB_LAYOUT
tabPane.addMouseListener(mouseListener);
}
}
if ((mouseMotionListener = createMouseMotionListener()) != null) {
if (scrollableTabLayoutEnabled()) {
tabScroller.tabPanel.addMouseMotionListener(mouseMotionListener);
}
else { // WRAP_TAB_LAYOUT
tabPane.addMouseMotionListener(mouseMotionListener);
}
}
if ((focusListener = createFocusListener()) != null) {
tabPane.addFocusListener(focusListener);
}
// PENDING(api) : See comment for ContainerHandler
containerListener = new ContainerHandler();
tabPane.addContainerListener(containerListener);
if (tabPane.getTabCount() > 0) {
htmlViews = createHTMLViewList();
}
}
protected void uninstallListeners() {
if (mouseListener != null) {
if (scrollableTabLayoutEnabled()) {
// SCROLL_TAB_LAYOUT
tabScroller.tabPanel.removeMouseListener(mouseListener);
}
else {
// WRAP_TAB_LAYOUT
tabPane.removeMouseListener(mouseListener);
}
mouseListener = null;
}
if (mouseMotionListener != null) {
if (scrollableTabLayoutEnabled()) {
// SCROLL_TAB_LAYOUT
tabScroller.tabPanel.removeMouseMotionListener(mouseMotionListener);
}
else {
// WRAP_TAB_LAYOUT
tabPane.removeMouseMotionListener(mouseMotionListener);
}
mouseMotionListener = null;
}
if (focusListener != null) {
tabPane.removeFocusListener(focusListener);
focusListener = null;
}
// PENDING(api): See comment for ContainerHandler
if (containerListener != null) {
tabPane.removeContainerListener(containerListener);
containerListener = null;
if (htmlViews != null) {
htmlViews.clear();
htmlViews = null;
}
}
if (tabChangeListener != null) {
tabPane.removeChangeListener(tabChangeListener);
tabChangeListener = null;
}
if (tabComponentListener != null) {
tabPane.removeComponentListener(tabComponentListener);
tabChangeListener = null;
}
if (propertyChangeListener != null) {
tabPane.removePropertyChangeListener(propertyChangeListener);
propertyChangeListener = null;
}
}
protected MouseListener createMouseListener() {
return new MouseHandler();
}
protected MouseMotionListener createMouseMotionListener() {
return new MouseMotionHandler();
}
protected FocusListener createFocusListener() {
return new FocusHandler();
}
protected ChangeListener createChangeListener() {
return new TabSelectionHandler();
}
protected ComponentListener createComponentListener() {
return new TabComponentHandler();
}
protected PropertyChangeListener createPropertyChangeListener() {
return new PropertyChangeHandler();
}
protected void installKeyboardActions() {
InputMap km = getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
SwingUtilities.replaceUIInputMap(tabPane, JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, km);
km = getInputMap(JComponent.WHEN_FOCUSED);
SwingUtilities.replaceUIInputMap(tabPane, JComponent.WHEN_FOCUSED, km);
ActionMap am = getActionMap();
SwingUtilities.replaceUIActionMap(tabPane, am);
if (scrollableTabLayoutEnabled()) {
tabScroller.scrollForwardButton.setAction(am.get("scrollTabsForwardAction"));
tabScroller.scrollBackwardButton.setAction(am.get("scrollTabsBackwardAction"));
tabScroller.popupMenuButton.setAction(am.get("scrollTabsPopupMenuAction"));
}
}
InputMap getInputMap(int condition) {
if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) {
return (InputMap) UIManager.get("TabbedPane.ancestorInputMap");
}
else if (condition == JComponent.WHEN_FOCUSED) {
return (InputMap) UIManager.get("TabbedPane.focusInputMap");
}
return null;
}
ActionMap getActionMap() {
ActionMap map = (ActionMap) UIManager.get("TabbedPane.actionMap");
if (map == null) {
map = createActionMap();
if (map != null) {
UIManager.getLookAndFeelDefaults().put("TabbedPane.actionMap", map);
}
}
return map;
}
ActionMap createActionMap() {
ActionMap map = new ActionMapUIResource();
map.put("navigateNext", new NextAction());
map.put("navigatePrevious", new PreviousAction());
map.put("navigateRight", new RightAction());
map.put("navigateLeft", new LeftAction());
map.put("navigateUp", new UpAction());
map.put("navigateDown", new DownAction());
map.put("navigatePageUp", new PageUpAction());
map.put("navigatePageDown", new PageDownAction());
map.put("requestFocus", new RequestFocusAction());
map.put("requestFocusForVisibleComponent", new RequestFocusForVisibleAction());
map.put("setSelectedIndex", new SetSelectedIndexAction());
map.put("scrollTabsForwardAction", new ScrollTabsForwardAction());
map.put("scrollTabsBackwardAction", new ScrollTabsBackwardAction());
map.put("scrollTabsPopupMenuAction", new ScrollTabsPopupMenuAction());
return map;
}
protected void uninstallKeyboardActions() {
SwingUtilities.replaceUIActionMap(tabPane, null);
SwingUtilities.replaceUIInputMap(tabPane, JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, null);
SwingUtilities.replaceUIInputMap(tabPane, JComponent.WHEN_FOCUSED, null);
SwingUtilities.replaceUIInputMap(tabPane, JComponent.WHEN_IN_FOCUSED_WINDOW, null);
}
/**
* Reloads the mnemonics. This should be invoked when a memonic changes, when the title of a mnemonic changes, or when tabs are added/removed.
*/
private void updateMnemonics() {
if (JTattooUtilities.getJavaVersion() >= 1.4) {
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.
*/
private void resetMnemonics() {
if (mnemonicToIndexMap != null) {
mnemonicToIndexMap.clear();
mnemonicInputMap.clear();
}
}
/**
* Adds the specified mnemonic at the specified index.
*/
private void addMnemonic(int index, int mnemonic) {
if (mnemonicToIndexMap == null) {
initMnemonics();
}
mnemonicInputMap.put(KeyStroke.getKeyStroke(mnemonic, Event.ALT_MASK), "setSelectedIndex");
mnemonicToIndexMap.put(new Integer(mnemonic), new Integer(index));
}
/**
* Installs the state needed for mnemonics.
*/
private void initMnemonics() {
mnemonicToIndexMap = new HashMap();
// mnemonicInputMap = new InputMapUIResource();
// mnemonicInputMap.setParent(SwingUtilities.getUIInputMap(tabPane, JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT));
// SwingUtilities.replaceUIInputMap(tabPane, JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, mnemonicInputMap);
mnemonicInputMap = new ComponentInputMapUIResource(tabPane);
mnemonicInputMap.setParent(SwingUtilities.getUIInputMap(tabPane, JComponent.WHEN_IN_FOCUSED_WINDOW));
SwingUtilities.replaceUIInputMap(tabPane, JComponent.WHEN_IN_FOCUSED_WINDOW, mnemonicInputMap);
}
protected boolean isContentOpaque() {
if (!tabPane.isOpaque()) {
if (UIManager.get("TabbedPane.contentOpaque") != null) {
return UIManager.getBoolean("TabbedPane.contentOpaque");
}
}
return true;
}
protected boolean isTabOpaque() {
if (!tabPane.isOpaque()) {
if (UIManager.get("TabbedPane.tabsOpaque") != null) {
return UIManager.getBoolean("TabbedPane.tabsOpaque");
}
}
return true;
}
protected boolean hasInnerBorder() {
return false;
}
// colors
protected Color[] getTabColors(int tabIndex, boolean isSelected, boolean isRollover) {
Color colorArr[] = AbstractLookAndFeel.getTheme().getTabColors();
if ((tabIndex >= 0) && (tabIndex < tabPane.getTabCount())) {
boolean isEnabled = tabPane.isEnabledAt(tabIndex);
Color backColor = tabPane.getBackgroundAt(tabIndex);
if (backColor instanceof UIResource) {
if (isSelected) {
colorArr = AbstractLookAndFeel.getTheme().getSelectedColors();
}
else if (isRollover && isEnabled) {
colorArr = AbstractLookAndFeel.getTheme().getRolloverColors();
}
else {
if (JTattooUtilities.isFrameActive(tabPane)) {
colorArr = AbstractLookAndFeel.getTheme().getTabColors();
}
else {
colorArr = AbstractLookAndFeel.getTheme().getInActiveColors();
}
}
}
else if (backColor != null) {
if (isSelected) {
colorArr = ColorHelper.createColorArr(ColorHelper.brighter(backColor, 60), backColor, 20);
}
else if (isRollover && isEnabled) {
colorArr = ColorHelper.createColorArr(ColorHelper.brighter(backColor, 80), ColorHelper.brighter(backColor, 20), 20);
}
else {
colorArr = ColorHelper.createColorArr(ColorHelper.brighter(backColor, 40), ColorHelper.darker(backColor, 10), 20);
}
}
}
return colorArr;
}
protected Color getLoBorderColor(int tabIndex) {
return AbstractLookAndFeel.getControlDarkShadow();
}
protected Color getHiBorderColor(int tabIndex) {
Color backColor = tabPane.getBackgroundAt(tabIndex);
if (tabIndex == tabPane.getSelectedIndex()) {
if (backColor instanceof UIResource) {
return AbstractLookAndFeel.getControlHighlight();
}
else {
return ColorHelper.brighter(backColor, 40);
}
}
if (tabIndex >= 0 && tabIndex <= tabCount) {
if (!isTabOpaque() || backColor instanceof UIResource) {
return AbstractLookAndFeel.getControlHighlight();
}
else {
return ColorHelper.brighter(backColor, 40);
}
}
return AbstractLookAndFeel.getControlHighlight();
}
protected Color[] getContentBorderColors(int tabPlacement) {
int sepHeight = tabAreaInsets.bottom;
Color selColors[] = AbstractLookAndFeel.getTheme().getSelectedColors();
Color loColor = selColors[selColors.length - 1];
Color darkLoColor = ColorHelper.darker(loColor, 20);
return ColorHelper.createColorArr(loColor, darkLoColor, sepHeight);
}
protected Color getContentBorderColor() {
return AbstractLookAndFeel.getFrameColor();
}
protected Color getGapColor(int tabIndex) {
if (isTabOpaque() || tabIndex == tabPane.getSelectedIndex()) {
if ((tabIndex >= 0) && (tabIndex < tabCount)) {
Color tabColors[] = getTabColors(tabIndex, tabIndex == tabPane.getSelectedIndex(), false);
if (tabColors != null && tabColors.length > 0) {
return tabColors[tabColors.length - 1];
}
else {
return tabPane.getBackgroundAt(tabIndex);
}
}
}
if (!tabPane.isOpaque()) {
Container parent = tabPane.getParent();
while (parent != null) {
if (parent.isOpaque()) {
return parent.getBackground();
}
parent = parent.getParent();
}
}
return tabAreaBackground;
}
// Geometry
public Dimension getPreferredSize(JComponent c) {
// Default to LayoutManager's preferredLayoutSize
return null;
}
public Dimension getMinimumSize(JComponent c) {
// Default to LayoutManager's minimumLayoutSize
return null;
}
public Dimension getMaximumSize(JComponent c) {
// Default to LayoutManager's maximumLayoutSize
return null;
}
// UI Rendering
public void paint(Graphics g, JComponent c) {
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, 0, 0, c.getWidth(), c.getHeight());
// 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);
}
}
/**
* 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
*/
protected void paintTabArea(Graphics g, int tabPlacement, int selectedIndex) {
int tc = tabPane.getTabCount();
Rectangle iconRect = new Rectangle(), textRect = new Rectangle();
Shape savedClip = g.getClip();
Rectangle clipRect = g.getClipBounds();
// Dirty trick to fix clipping problem
if (scrollableTabLayoutEnabled() && tabScroller.scrollBackwardButton.isVisible()) {
if ((tabPlacement == TOP) || (tabPlacement == BOTTOM)) {
g.setClip(clipRect.x, clipRect.y, clipRect.width + 1, clipRect.height);
}
else {
g.setClip(clipRect.x, clipRect.y, clipRect.width, clipRect.height + 1);
}
}
// Paint tabRuns of tabs from back to front
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 : tc - 1);
for (int j = start; j <= end; j++) {
if (rects[j].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) && (selectedIndex < rects.length) && getRunForTab(tc, selectedIndex) == 0) {
if (rects[selectedIndex].intersects(clipRect)) {
paintTab(g, tabPlacement, rects, selectedIndex, iconRect, textRect);
}
}
g.setClip(savedClip);
}
protected Font getTabFont(boolean isSelected) {
return tabPane.getFont();
}
protected void paintTab(Graphics g, int tabPlacement, Rectangle[] rects, int tabIndex, Rectangle iconRect, Rectangle textRect) {
Rectangle tabRect = rects[tabIndex];
int selectedIndex = tabPane.getSelectedIndex();
boolean isSelected = selectedIndex == tabIndex;
Graphics2D g2D = null;
Polygon cropShape = null;
Shape savedClip = null;
int cropx = 0;
int cropy = 0;
if (scrollableTabLayoutEnabled()) {
if (g instanceof Graphics2D) {
g2D = (Graphics2D) g;
// Render visual for cropped tab edge...
Rectangle viewRect = tabScroller.viewport.getViewRect();
int cropline;
switch (tabPlacement) {
case LEFT:
case RIGHT:
cropline = viewRect.y + viewRect.height;
if ((tabRect.y < cropline) && (tabRect.y + tabRect.height > cropline)) {
cropShape = createCroppedTabClip(tabPlacement, tabRect, cropline);
cropx = tabRect.x;
cropy = cropline - 1;
}
break;
case TOP:
case BOTTOM:
default:
cropline = viewRect.x + viewRect.width;
if ((tabRect.x < cropline) && (tabRect.x + tabRect.width > cropline)) {
cropShape = createCroppedTabClip(tabPlacement, tabRect, cropline);
cropx = cropline - 1;
cropy = tabRect.y;
}
}
if (cropShape != null) {
savedClip = g2D.getClip();
g2D.clip(cropShape);
}
}
}
paintTabBackground(g, tabPlacement, tabIndex, tabRect.x, tabRect.y, tabRect.width, tabRect.height, isSelected);
paintTabBorder(g, tabPlacement, tabIndex, tabRect.x, tabRect.y, tabRect.width, tabRect.height, isSelected);
try {
boolean doPaintContent = getTabComponentAt(tabIndex) == null;
if (doPaintContent) {
String title = tabPane.getTitleAt(tabIndex);
Font font = getTabFont(isSelected);
FontMetrics fm = JTattooUtilities.getFontMetrics(tabPane, g, font);
Icon icon = getIconForTab(tabIndex);
layoutLabel(tabPlacement, fm, tabIndex, title, icon, tabRect, iconRect, textRect, isSelected);
paintText(g, tabPlacement, font, fm, tabIndex, title, textRect, isSelected);
paintIcon(g, tabPlacement, tabIndex, icon, iconRect, isSelected);
}
paintFocusIndicator(g, tabPlacement, rects, tabIndex, iconRect, textRect, isSelected);
}
catch (Exception ex) {
}
if (cropShape != null) {
paintCroppedTabEdge(g, tabPlacement, tabIndex, cropx, cropy);
if (g2D != null && savedClip != null) {
g2D.setClip(savedClip);
}
}
}
/*
* This method will create and return a polygon shape for the given tab rectangle which has been cropped at the specified cropline with a torn edge
* visual. e.g. A "File" tab which has cropped been cropped just after the "i": ------------- | ..... | | . | | ... . | | . . | | . . | | . . |
* --------------
*
* The x, y arrays below define the pattern used to create a "torn" edge segment which is repeated to fill the edge of the tab. For tabs placed on
* TOP and BOTTOM, this righthand torn edge is created by line segments which are defined by coordinates obtained by subtracting xCropLen[i] from
* (tab.x + tab.width) and adding yCroplen[i] to (tab.y). For tabs placed on LEFT or RIGHT, the bottom torn edge is created by subtracting
* xCropLen[i] from (tab.y + tab.height) and adding yCropLen[i] to (tab.x).
*/
private int xCropLen[] = { 1, 1, 0, 0, 1, 1, 2, 2 };
private int yCropLen[] = { 0, 3, 3, 6, 6, 9, 9, 12 };
private static final int CROP_SEGMENT = 12;
private Polygon createCroppedTabClip(int tabPlacement, Rectangle tabRect, int cropline) {
int rlen;
int start;
int end;
int ostart;
switch (tabPlacement) {
case LEFT:
case RIGHT:
rlen = tabRect.width;
start = tabRect.x;
end = tabRect.x + tabRect.width;
ostart = tabRect.y;
break;
case TOP:
case BOTTOM:
default:
rlen = tabRect.height;
start = tabRect.y;
end = tabRect.y + tabRect.height;
ostart = tabRect.x;
}
int rcnt = rlen / CROP_SEGMENT;
if (rlen % CROP_SEGMENT > 0) {
rcnt++;
}
int npts = 2 + (rcnt * 8);
int xp[] = new int[npts];
int yp[] = new int[npts];
int pcnt = 0;
xp[pcnt] = ostart;
yp[pcnt++] = end;
xp[pcnt] = ostart;
yp[pcnt++] = start;
for (int i = 0; i < rcnt; i++) {
for (int j = 0; j < xCropLen.length; j++) {
xp[pcnt] = cropline - xCropLen[j];
yp[pcnt] = start + (i * CROP_SEGMENT) + yCropLen[j];
if (yp[pcnt] >= end) {
yp[pcnt] = end;
pcnt++;
break;
}
pcnt++;
}
}
if (tabPlacement == JTabbedPane.TOP || tabPlacement == JTabbedPane.BOTTOM) {
return new Polygon(xp, yp, pcnt);
}
else {
// LEFT or RIGHT
return new Polygon(yp, xp, pcnt);
}
}
/*
* If tabLayoutPolicy == SCROLL_TAB_LAYOUT, this method will paint an edge indicating the tab is cropped in the viewport display
*/
private void paintCroppedTabEdge(Graphics g, int tabPlacement, int tabIndex, int x, int y) {
g.setColor(Color.gray);
switch (tabPlacement) {
case LEFT:
case RIGHT:
int xx = x;
while (xx <= x + rects[tabIndex].width) {
for (int i = 0; i < xCropLen.length; i += 2) {
g.drawLine(xx + yCropLen[i], y - xCropLen[i], xx + yCropLen[i + 1] - 1, y - xCropLen[i + 1]);
}
xx += CROP_SEGMENT;
}
break;
case TOP:
case BOTTOM:
default:
int yy = y;
while (yy <= y + rects[tabIndex].height) {
for (int i = 0; i < xCropLen.length; i += 2) {
g.drawLine(x - xCropLen[i], yy + yCropLen[i], x - xCropLen[i + 1], yy + yCropLen[i + 1] - 1);
}
yy += CROP_SEGMENT;
}
}
}
protected void layoutLabel(int tabPlacement, FontMetrics metrics, int tabIndex, String title, Icon icon, Rectangle tabRect, Rectangle iconRect,
Rectangle textRect, boolean isSelected) {
textRect.x = textRect.y = iconRect.x = iconRect.y = 0;
View v = getTextViewForTab(tabIndex);
if (v != null) {
tabPane.putClientProperty("html", v);
}
SwingUtilities.layoutCompoundLabel((JComponent) tabPane, metrics, title, icon, SwingUtilities.CENTER, SwingUtilities.CENTER,
SwingUtilities.CENTER, SwingUtilities.TRAILING, tabRect, iconRect, textRect, textIconGap);
tabPane.putClientProperty("html", null);
int xNudge = getTabLabelShiftX(tabPlacement, tabIndex, isSelected);
int yNudge = getTabLabelShiftY(tabPlacement, tabIndex, isSelected);
iconRect.x += xNudge;
iconRect.y += yNudge;
textRect.x += xNudge;
textRect.y += yNudge;
}
protected int getTabLabelShiftX(int tabPlacement, int tabIndex, boolean isSelected) {
return 0;
}
protected int getTabLabelShiftY(int tabPlacement, int tabIndex, boolean isSelected) {
if (!isSelected) {
if (tabPlacement == TOP) {
return 1;
}
else if (tabPlacement == BOTTOM) {
return -1;
}
}
return 0;
}
protected void paintIcon(Graphics g, int tabPlacement, int tabIndex, Icon icon, Rectangle iconRect, boolean isSelected) {
if (icon != null) {
icon.paintIcon(tabPane, g, iconRect.x, iconRect.y);
}
}
protected void paintText(Graphics g, int tabPlacement, Font font, FontMetrics metrics, int tabIndex, String title, Rectangle textRect,
boolean isSelected) {
g.setFont(font);
View v = getTextViewForTab(tabIndex);
if (v != null) {
// html
Graphics2D g2D = (Graphics2D) g;
Object savedRenderingHint = null;
if (AbstractLookAndFeel.getTheme().isTextAntiAliasingOn()) {
savedRenderingHint = g2D.getRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING);
g2D.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, AbstractLookAndFeel.getTheme().getTextAntiAliasingHint());
}
v.paint(g, textRect);
if (AbstractLookAndFeel.getTheme().isTextAntiAliasingOn()) {
g2D.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, savedRenderingHint);
}
}
else {
// plain text
int mnemIndex = -1;
if (JTattooUtilities.getJavaVersion() >= 1.4) {
mnemIndex = tabPane.getDisplayedMnemonicIndexAt(tabIndex);
}
if (tabPane.isEnabled() && tabPane.isEnabledAt(tabIndex)) {
if (isSelected) {
Color backColor = tabPane.getBackgroundAt(tabIndex);
if (backColor instanceof UIResource) {
g.setColor(AbstractLookAndFeel.getTabSelectionForegroundColor());
}
else {
g.setColor(tabPane.getForegroundAt(tabIndex));
}
}
else {
if (tabIndex == rolloverIndex) {
g.setColor(AbstractLookAndFeel.getTheme().getRolloverForegroundColor());
}
else {
g.setColor(tabPane.getForegroundAt(tabIndex));
}
}
JTattooUtilities.drawStringUnderlineCharAt(tabPane, g, title, mnemIndex, textRect.x, textRect.y + metrics.getAscent());
}
else { // tab disabled
g.setColor(tabPane.getBackgroundAt(tabIndex).brighter());
JTattooUtilities.drawStringUnderlineCharAt(tabPane, g, title, mnemIndex, textRect.x, textRect.y + metrics.getAscent());
g.setColor(tabPane.getBackgroundAt(tabIndex).darker());
JTattooUtilities.drawStringUnderlineCharAt(tabPane, g, title, mnemIndex, textRect.x - 1, textRect.y + metrics.getAscent() - 1);
}
}
}
protected void paintFocusIndicator(Graphics g, int tabPlacement, Rectangle[] rects, int tabIndex, Rectangle iconRect, Rectangle textRect,
boolean isSelected) {
if (tabPane.isRequestFocusEnabled() && tabPane.hasFocus() && isSelected && tabIndex >= 0 && textRect.width > 8) {
g.setColor(AbstractLookAndFeel.getTheme().getFocusColor());
BasicGraphicsUtils.drawDashedRect(g, textRect.x - 4, textRect.y + 1, textRect.width + 8, textRect.height);
}
}
/**
* this function draws the border around each tab note that this function does now draw the background of the tab. that is done elsewhere
*/
protected void paintTabBorder(Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h, boolean isSelected) {
int x2 = x + (w);
int y2 = y + (h);
switch (tabPlacement) {
case LEFT:
paintLeftTabBorder(tabIndex, g, x, y, x2, y2, isSelected);
break;
case RIGHT:
paintRightTabBorder(tabIndex, g, x, y, x2, y2, isSelected);
break;
case BOTTOM:
if (roundedTabs) {
paintRoundedBottomTabBorder(tabIndex, g, x, y, x2, y2 - 1, isSelected);
}
else {
paintBottomTabBorder(tabIndex, g, x, y, x2, y2 - 1, isSelected);
}
break;
case TOP:
default:
if (roundedTabs) {
paintRoundedTopTabBorder(tabIndex, g, x, y, x2, y2, isSelected);
}
else {
paintTopTabBorder(tabIndex, g, x, y, x2, y2, isSelected);
}
}
}
protected void paintRoundedTopTabBorder(int tabIndex, Graphics g, int x1, int y1, int x2, int y2, boolean isSelected) {
Graphics2D g2D = (Graphics2D) g;
Object savedRederingHint = g2D.getRenderingHint(RenderingHints.KEY_ANTIALIASING);
g2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
Color borderColor = getLoBorderColor(tabIndex);
g.setColor(borderColor);
int d = 2 * GAP;
if (isSelected) {
g.drawLine(x1 + GAP, y1, x2 - GAP, y1);
g.drawArc(x1, y1, d, d, 90, 90);
g.drawArc(x2 - d, y1, d, d, 0, 90);
g.drawLine(x1, y1 + GAP + 1, x1, y2);
g.drawLine(x2, y1 + GAP + 1, x2, y2);
}
else {
g.drawLine(x1 + GAP, y1, x2 - GAP, y1);
g.drawArc(x1, y1, d, d, 90, 90);
g.drawArc(x2 - d, y1, d, d, 0, 90);
g.drawLine(x1, y1 + GAP + 1, x1, y2 - 1);
g.drawLine(x2, y1 + GAP + 1, x2, y2 - 1);
}
g2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, savedRederingHint);
}
protected void paintTopTabBorder(int tabIndex, Graphics g, int x1, int y1, int x2, int y2, boolean isSelected) {
int tc = tabPane.getTabCount();
int currentRun = getRunForTab(tc, tabIndex);
int lastIndex = lastTabInRun(tc, currentRun);
int firstIndex = tabRuns[currentRun];
boolean leftToRight = JTattooUtilities.isLeftToRight(tabPane);
Color loColor = getLoBorderColor(tabIndex);
Color hiColor = getHiBorderColor(tabIndex);
g.setColor(loColor);
g.drawLine(x1 + GAP, y1, x2, y1);
g.drawLine(x1 + GAP, y1, x1, y1 + GAP);
g.drawLine(x1, y1 + GAP + 1, x1, y2);
g.drawLine(x2, y1, x2, y2);
g.setColor(hiColor);
g.drawLine(x1 + GAP + 1, y1 + 1, x2 - 1, y1 + 1);
g.drawLine(x1 + GAP + 1, y1 + 1, x1 + 1, y1 + GAP + 1);
g.drawLine(x1 + 1, y1 + GAP + 1, x1 + 1, y2 - 1);
// paint gap
int gapTabIndex = getTabAtLocation(x1 + 2, y1 - 2);
Color gapColor = getGapColor(gapTabIndex);
g.setColor(gapColor);
for (int i = 0; i < GAP; i++) {
g.drawLine(x1, y1 + i, x1 + GAP - i - 1, y1 + i);
}
if (leftToRight) {
if ((tabIndex != firstIndex) || (currentRun != (runCount - 1))) {
g.setColor(loColor);
g.drawLine(x1, y1, x1, y1 + GAP);
}
if (!isSelected && (tabIndex == firstIndex) && (currentRun != (runCount - 1))) {
g.setColor(hiColor);
g.drawLine(x1 + 1, y1, x1 + 1, y1 + GAP - 2);
}
}
else {
if ((tabIndex != lastIndex) || (currentRun != (runCount - 1))) {
g.setColor(loColor);
g.drawLine(x1, y1, x1, y1 + GAP);
}
}
}
protected void paintLeftTabBorder(int tabIndex, Graphics g, int x1, int y1, int x2, int y2, boolean isSelected) {
Graphics2D g2D = (Graphics2D) g;
int tc = tabPane.getTabCount();
int currentRun = getRunForTab(tc, tabIndex);
int lastIndex = lastTabInRun(tc, currentRun);
int firstIndex = tabRuns[currentRun];
Color loColor = getLoBorderColor(tabIndex);
Color hiColor = getHiBorderColor(tabIndex);
g.setColor(hiColor);
Composite savedComposite = g2D.getComposite();
AlphaComposite alpha = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.4f);
g2D.setComposite(alpha);
g.drawLine(x1 + GAP + 1, y1 + 1, x2 - 1, y1 + 1);
g.drawLine(x1 + GAP, y1 + 1, x1 + 1, y1 + GAP);
g.drawLine(x1 + 1, y1 + GAP + 1, x1 + 1, y2 - 1);
g2D.setComposite(savedComposite);
g.setColor(loColor);
g.drawLine(x1 + GAP, y1, x2 - 1, y1);
g.drawLine(x1 + GAP, y1, x1, y1 + GAP);
g.drawLine(x1, y1 + GAP, x1, y2);
g.drawLine(x1 + GAP, y2, x2 - 1, y2);
if (tabIndex == lastIndex) {
g.drawLine(x1, y2, x1 + GAP, y2);
}
// paint gap
int gapTabIndex = getTabAtLocation(x1 + 2, y1 - 2);
Color gapColor = getGapColor(gapTabIndex);
g.setColor(gapColor);
for (int i = 0; i < GAP; i++) {
g.drawLine(x1, y1 + i, x1 + GAP - i - 1, y1 + i);
}
if ((tabIndex != firstIndex) || (currentRun != (runCount - 1))) {
loColor = getLoBorderColor(gapTabIndex);
g.setColor(loColor);
g.drawLine(x1, y1, x1, y1 + GAP - 1);
if (tabIndex != firstIndex) {
g2D.setComposite(alpha);
hiColor = getHiBorderColor(gapTabIndex);
g.setColor(hiColor);
g.drawLine(x1 + 1, y1, x1 + 1, y1 + GAP - 2);
g2D.setComposite(savedComposite);
}
}
}
protected void paintRoundedBottomTabBorder(int tabIndex, Graphics g, int x1, int y1, int x2, int y2, boolean isSelected) {
Graphics2D g2D = (Graphics2D) g;
Object savedRederingHint = g2D.getRenderingHint(RenderingHints.KEY_ANTIALIASING);
g2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
Color loColor = getLoBorderColor(tabIndex);
int d = 2 * GAP;
g.setColor(loColor);
g.drawLine(x1 + GAP, y2, x2 - GAP, y2);
g.drawArc(x1, y2 - d, d, d, 180, 90);
g.drawArc(x2 - d, y2 - d, d, d, -90, 90);
g.drawLine(x1, y1, x1, y2 - GAP - 1);
g.drawLine(x2, y1, x2, y2 - GAP - 1);
g2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, savedRederingHint);
}
protected void paintBottomTabBorder(int tabIndex, Graphics g, int x1, int y1, int x2, int y2, boolean isSelected) {
int tc = tabPane.getTabCount();
int currentRun = getRunForTab(tc, tabIndex);
int lastIndex = lastTabInRun(tc, currentRun);
int firstIndex = tabRuns[currentRun];
boolean leftToRight = JTattooUtilities.isLeftToRight(tabPane);
Color loColor = getLoBorderColor(tabIndex);
Color hiColor = getHiBorderColor(tabIndex);
g.setColor(loColor);
g.drawLine(x1, y1, x1, y2 - GAP);
g.drawLine(x1, y2 - GAP, x1 + GAP, y2);
g.drawLine(x1 + GAP, y2, x2, y2);
g.drawLine(x2, y2, x2, y1);
g.setColor(hiColor);
g.drawLine(x1 + 1, y1, x1 + 1, y2 - GAP - 1);
g.drawLine(x1 + 1, y2 - GAP, x1 + GAP, y2 - 1);
// paint gap
int gapTabIndex = getTabAtLocation(x1 + 2, y2 + 2);
Color gapColor = getGapColor(gapTabIndex);
g.setColor(gapColor);
for (int i = 0; i < GAP; i++) {
g.drawLine(x1, y2 - i, x1 + GAP - i - 1, y2 - i);
}
if (leftToRight) {
if ((tabIndex != firstIndex) || (currentRun != (runCount - 1))) {
g.setColor(loColor);
g.drawLine(x1, y2 - GAP, x1, y2);
}
}
else {
if ((tabIndex != lastIndex) || (currentRun != (runCount - 1))) {
g.setColor(loColor);
g.drawLine(x1, y2 - GAP, x1, y2);
}
}
}
protected void paintRightTabBorder(int tabIndex, Graphics g, int x1, int y1, int x2, int y2, boolean isSelected) {
Graphics2D g2D = (Graphics2D) g;
int tc = tabPane.getTabCount();
int currentRun = getRunForTab(tc, tabIndex);
int lastIndex = lastTabInRun(tc, currentRun);
int firstIndex = tabRuns[currentRun];
Color loColor = getLoBorderColor(tabIndex);
Color hiColor = getHiBorderColor(tabIndex);
Composite savedComposite = g2D.getComposite();
AlphaComposite alpha = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.4f);
g2D.setComposite(alpha);
g.setColor(hiColor);
g.drawLine(x1, y1 + 1, x2 - GAP - 1, y1 + 1);
g.drawLine(x2 - GAP, y1 + 1, x2 - 1, y1 + GAP);
g2D.setComposite(savedComposite);
g.setColor(loColor);
g.drawLine(x1, y1, x2 - GAP, y1);
g.drawLine(x2 - GAP, y1, x2, y1 + GAP);
g.drawLine(x2, y1 + GAP, x2, y2);
if (tabIndex == lastIndex) {
g.drawLine(x2, y2, x1, y2);
}
// paint gap
int gapTabIndex = getTabAtLocation(x1 + 2, y1 - 2);
Color gapColor = getGapColor(gapTabIndex);
g.setColor(gapColor);
for (int i = 0; i < GAP; i++) {
g.drawLine(x2 - GAP + i + 1, y1 + i, x2, y1 + i);
}
if ((tabIndex != firstIndex) || (currentRun != (runCount - 1))) {
loColor = getLoBorderColor(gapTabIndex);
g.setColor(loColor);
g.drawLine(x2, y1, x2, y1 + GAP - 1);
}
}
protected void paintTabBackground(Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h, boolean isSelected) {
if (isTabOpaque() || isSelected) {
Graphics2D g2D = (Graphics2D) g;
Shape savedClip = g.getClip();
Area orgClipArea = new Area(new Rectangle2D.Double(x, y, w, h));
if (savedClip != null) {
orgClipArea = new Area(savedClip);
}
Color colorArr[] = getTabColors(tabIndex, isSelected, tabIndex == rolloverIndex);
int d = 2 * GAP;
switch (tabPlacement) {
case TOP:
default:
if (isSelected) {
Area clipArea = new Area(new RoundRectangle2D.Double(x, y, w, h + 4, d, d));
Area rectArea = new Area(new Rectangle2D.Double(x, y, w, h + 2));
clipArea.intersect(rectArea);
clipArea.intersect(orgClipArea);
g2D.setClip(clipArea);
JTattooUtilities.fillHorGradient(g, colorArr, x, y, w, h + 4);
g2D.setClip(savedClip);
}
else {
Area clipArea = new Area(new RoundRectangle2D.Double(x, y, w, h + 4, d, d));
Area rectArea = new Area(new Rectangle2D.Double(x, y, w, h));
clipArea.intersect(rectArea);
clipArea.intersect(orgClipArea);
g2D.setClip(clipArea);
JTattooUtilities.fillHorGradient(g, colorArr, x, y, w, h + 4);
g2D.setClip(savedClip);
}
break;
case LEFT:
if (isSelected) {
JTattooUtilities.fillHorGradient(g, colorArr, x + 1, y + 1, w + 1, h - 1);
}
else {
JTattooUtilities.fillHorGradient(g, colorArr, x + 1, y + 1, w - 1, h - 1);
}
break;
case BOTTOM:
if (isSelected) {
Area clipArea = new Area(new RoundRectangle2D.Double(x, y - 4, w, h + 4, d, d));
Area rectArea = new Area(new Rectangle2D.Double(x, y - 2, w, h + 1));
clipArea.intersect(rectArea);
clipArea.intersect(orgClipArea);
g2D.setClip(clipArea);
JTattooUtilities.fillHorGradient(g, colorArr, x, y - 4, w, h + 4);
g2D.setClip(savedClip);
}
else {
Area clipArea = new Area(new RoundRectangle2D.Double(x, y - 4, w, h + 4, d, d));
Area rectArea = new Area(new Rectangle2D.Double(x, y, w, h));
clipArea.intersect(rectArea);
clipArea.intersect(orgClipArea);
g2D.setClip(clipArea);
JTattooUtilities.fillHorGradient(g, colorArr, x, y - 4, w, h + 4);
g2D.setClip(savedClip);
}
break;
case RIGHT:
if (isSelected) {
JTattooUtilities.fillHorGradient(g, colorArr, x - 2, y + 1, w + 2, h - 1);
}
else {
JTattooUtilities.fillHorGradient(g, colorArr, x, y + 1, w + 1, h - 1);
}
break;
}
}
}
protected void paintContentBorder(Graphics g, int tabPlacement, int selectedIndex, int x, int y, int w, int h) {
int tabAreaHeight = calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight);
int tabAreaWidth = calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth);
// paint the background
if (tabPane.isOpaque()) {
int xt = tabPlacement == RIGHT ? w - tabAreaWidth : 0;
int yt = tabPlacement == BOTTOM ? h - tabAreaHeight : 0;
int wt = tabPlacement == TOP || tabPlacement == BOTTOM ? w : tabAreaWidth;
int ht = tabPlacement == LEFT || tabPlacement == RIGHT ? h : tabAreaHeight;
g.setColor(tabAreaBackground);
g.fillRect(xt, yt, wt, ht);
}
if (isContentOpaque()) {
int xt = tabPlacement == LEFT ? tabAreaWidth : 0;
int yt = tabPlacement == TOP ? tabAreaHeight : 0;
int wt = tabPlacement == LEFT || tabPlacement == RIGHT ? w - tabAreaWidth : w;
int ht = tabPlacement == TOP || tabPlacement == BOTTOM ? h - tabAreaHeight : h;
g.setColor(tabPane.getBackground());
g.fillRect(xt, yt, wt, ht);
}
Insets bi = new Insets(0, 0, 0, 0);
if (tabPane.getBorder() != null) {
bi = tabPane.getBorder().getBorderInsets(tabPane);
}
if (hasInnerBorder()) {
Color loColor = AbstractLookAndFeel.getControlDarkShadow();
Color hiColor = AbstractLookAndFeel.getControlHighlight();
g.setColor(loColor);
switch (tabPlacement) {
case TOP: {
int x1 = x + bi.left - 1;
int y1 = y + tabAreaHeight + bi.top - 2;
int x2 = x1 + w - bi.left - bi.right + 1;
int y2 = h - bi.bottom;
int ws = w - bi.left - bi.right + 1;
int hs = h - tabAreaHeight - bi.top - bi.bottom + 2;
if (tabPane.getBorder() == null) {
g.drawLine(x1, y1, x2, y1);
g.setColor(hiColor);
g.drawLine(x1, y1 + 1, x2, y1 + 1);
}
else {
g.drawRect(x1, y1, ws, hs);
g.setColor(hiColor);
g.drawLine(x1 + 1, y1 + 1, x2 - 1, y1 + 1);
}
break;
}
case LEFT: {
int x1 = x + tabAreaWidth + bi.left - 2;
int y1 = y + bi.top - 1;
// int x2 = w - bi.right;
int y2 = y1 + h - bi.top - bi.bottom + 1;
int ws = w - tabAreaWidth - bi.left - bi.right + 2;
int hs = h - bi.top - bi.bottom + 1;
if (tabPane.getBorder() == null) {
g.drawLine(x1, y1, x1, y2);
g.setColor(hiColor);
g.drawLine(x1 + 1, y1, x1 + 1, y2);
}
else {
g.drawRect(x1, y1, ws, hs);
g.setColor(hiColor);
g.drawLine(x1 + 1, y1 + 1, x1 + 1, y2 - 1);
}
break;
}
case BOTTOM: {
int x1 = x + bi.left - 1;
int y1 = y + bi.top - 1;
int x2 = x1 + w - bi.left - bi.right + 1;
int y2 = h - tabAreaHeight - bi.bottom;
int ws = w - bi.left - bi.right + 1;
int hs = h - tabAreaHeight - bi.top - bi.bottom + 2;
if (tabPane.getBorder() == null) {
g.drawLine(x1, y2, x2, y2);
}
else {
g.drawRect(x1, y1, ws, hs);
}
break;
}
case RIGHT: {
int x1 = x + bi.left - 1;
int y1 = y + bi.top - 1;
int x2 = w - tabAreaWidth - bi.right + 1;
int y2 = y1 + h - bi.top - bi.bottom + 1;
int ws = w - tabAreaWidth - bi.left - bi.right + 2;
int hs = h - bi.top - bi.bottom + 1;
if (tabPane.getBorder() == null) {
g.drawLine(x2, y1, x2, y2);
}
else {
g.drawRect(x1, y1, ws, hs);
}
break;
}
}
}
else {
int sepHeight = tabAreaInsets.bottom;
if (sepHeight > 0) {
switch (tabPlacement) {
case TOP: {
Color colors[] = getContentBorderColors(tabPlacement);
int ys = y + tabAreaHeight - sepHeight + bi.top;
for (int i = 0; i < colors.length; i++) {
g.setColor(colors[i]);
g.drawLine(x, ys + i, x + w, ys + i);
}
break;
}
case LEFT: {
Color colors[] = getContentBorderColors(tabPlacement);
int xs = x + tabAreaWidth - sepHeight + bi.left;
for (int i = 0; i < colors.length; i++) {
g.setColor(colors[i]);
g.drawLine(xs + i, y, xs + i, y + h);
}
break;
}
case BOTTOM: {
Color colors[] = getContentBorderColors(tabPlacement);
int ys = y + h - tabAreaHeight - bi.bottom;
for (int i = 0; i < colors.length; i++) {
g.setColor(colors[i]);
g.drawLine(x, ys + i, x + w, ys + i);
}
break;
}
case RIGHT: {
Color colors[] = getContentBorderColors(tabPlacement);
int xs = x + w - tabAreaWidth - bi.right;
for (int i = 0; i < colors.length; i++) {
g.setColor(colors[i]);
g.drawLine(xs + i, y, xs + i, y + h);
}
break;
}
}
}
}
}
protected void paintScrollContentBorder(Graphics g, int tabPlacement, int selectedIndex, int x, int y, int w, int h) {
Insets bi = new Insets(0, 0, 0, 0);
if (tabPane.getBorder() != null) {
bi = tabPane.getBorder().getBorderInsets(tabPane);
}
if (tabPane.getTabPlacement() == TOP) {
paintContentBorder(g, tabPane.getTabPlacement(), tabPane.getSelectedIndex(), x, y - bi.top, w, h);
}
else if (tabPane.getTabPlacement() == BOTTOM) {
paintContentBorder(g, tabPane.getTabPlacement(), tabPane.getSelectedIndex(), x, y + bi.bottom, w, h);
}
else if (tabPane.getTabPlacement() == LEFT) {
paintContentBorder(g, tabPane.getTabPlacement(), tabPane.getSelectedIndex(), x - bi.left, y, w, h);
}
else if (tabPane.getTabPlacement() == RIGHT) {
paintContentBorder(g, tabPane.getTabPlacement(), tabPane.getSelectedIndex(), x + bi.right, y, w, h);
}
}
// TabbedPaneUI methods
private void ensureCurrentLayout() {
// TabPane maybe still invalid. See bug 4237677.
((TabbedPaneLayout) tabPane.getLayout()).calculateLayoutInfo();
}
/**
* Returns the bounds of the specified tab index. The bounds are with respect to the JTabbedPane's coordinate space.
*/
public Rectangle getTabBounds(JTabbedPane pane, int i) {
ensureCurrentLayout();
Rectangle tabRect = new Rectangle();
return getTabBounds(i, tabRect);
}
public int getTabRunCount(JTabbedPane pane) {
ensureCurrentLayout();
return runCount;
}
/**
* Returns the tab index which intersects the specified point in the JTabbedPane's coordinate space.
*/
public int tabForCoordinate(JTabbedPane pane, int x, int y) {
ensureCurrentLayout();
Point p = new Point(x, y);
if (scrollableTabLayoutEnabled()) {
translatePointToTabPanel(x, y, p);
}
int tc = tabPane.getTabCount();
for (int i = 0; i < tc; i++) {
if (rects[i].contains(p.x, p.y)) {
return i;
}
}
return -1;
}
/**
* Returns the bounds of the specified tab in the coordinate space of the JTabbedPane component. This is required because the tab rects are by
* default defined in the coordinate space of the component where they are rendered, which could be the JTabbedPane (for WRAP_TAB_LAYOUT) or a
* ScrollableTabPanel (SCROLL_TAB_LAYOUT). This method should be used whenever the tab rectangle must be relative to the JTabbedPane itself and the
* result should be placed in a designated Rectangle object (rather than instantiating and returning a new Rectangle each time). The tab index
* parameter must be a valid tabbed pane tab index (0 to tab count - 1, inclusive). The destination rectangle parameter must be a valid
* Rectangle
instance. The handling of invalid parameters is unspecified.
*
* @param tabIndex
* the index of the tab
* @param dest
* the rectangle where the result should be placed
* @return the resulting rectangle
*
* @since 1.4
*/
protected Rectangle getTabBounds(int tabIndex, Rectangle dest) {
dest.width = rects[tabIndex].width;
dest.height = rects[tabIndex].height;
if (scrollableTabLayoutEnabled()) { // SCROLL_TAB_LAYOUT
// Need to translate coordinates based on viewport location &
// view position
Point vpp = tabScroller.viewport.getLocation();
Point viewp = tabScroller.viewport.getViewPosition();
dest.x = rects[tabIndex].x + vpp.x - viewp.x;
dest.y = rects[tabIndex].y + vpp.y - viewp.y;
}
else { // WRAP_TAB_LAYOUT
dest.x = rects[tabIndex].x;
dest.y = rects[tabIndex].y;
}
return dest;
}
/**
* 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).
*/
protected int getTabAtLocation(int x, int y) {
ensureCurrentLayout();
int tc = tabPane.getTabCount();
for (int i = 0; i < tc; i++) {
if (rects[i].contains(x, y)) {
return i;
}
}
return -1;
}
/**
* Returns the index of the tab closest to the passed in location, note that the returned tab may not contain the location x,y.
*/
protected int getClosestTab(int x, int y) {
int min = 0;
int tc = Math.min(rects.length, tabPane.getTabCount());
int max = tc;
int tabPlacement = tabPane.getTabPlacement();
boolean useX = (tabPlacement == TOP || tabPlacement == BOTTOM);
int want = (useX) ? x : y;
while (min != max) {
int current = (max + min) / 2;
int minLoc;
int maxLoc;
if (useX) {
minLoc = rects[current].x;
maxLoc = minLoc + rects[current].width;
}
else {
minLoc = rects[current].y;
maxLoc = minLoc + rects[current].height;
}
if (want < minLoc) {
max = current;
if (min == max) {
return Math.max(0, current - 1);
}
}
else if (want >= maxLoc) {
min = current;
if (max - min <= 1) {
return Math.max(current + 1, tc - 1);
}
}
else {
return current;
}
}
return min;
}
/**
* Returns a point which is translated from the specified point in the JTabbedPane's coordinate space to the coordinate space of the
* ScrollableTabPanel. This is used for SCROLL_TAB_LAYOUT ONLY.
*/
private Point translatePointToTabPanel(int srcx, int srcy, Point dest) {
Point vpp = tabScroller.viewport.getLocation();
Point viewp = tabScroller.viewport.getViewPosition();
dest.x = srcx - vpp.x + viewp.x;
dest.y = srcy - vpp.y + viewp.y;
return dest;
}
// BaseTabbedPaneUI methods
protected Component getVisibleComponent() {
return visibleComponent;
}
protected void setVisibleComponent(Component component) {
if (visibleComponent != null && visibleComponent != component && visibleComponent.getParent() == tabPane) {
visibleComponent.setVisible(false);
}
if (component != null && !component.isVisible()) {
component.setVisible(true);
}
visibleComponent = component;
}
protected void assureRectsCreated(int tabCount) {
int rectArrayLen = rects.length;
if (tabCount != rectArrayLen) {
Rectangle[] tempRectArray = new Rectangle[tabCount];
System.arraycopy(rects, 0, tempRectArray, 0, Math.min(rectArrayLen, tabCount));
rects = tempRectArray;
for (int rectIndex = rectArrayLen; rectIndex < tabCount; rectIndex++) {
rects[rectIndex] = new Rectangle();
}
}
}
protected void expandTabRunsArray() {
int rectLen = tabRuns.length;
int[] newArray = new int[rectLen + 10];
System.arraycopy(tabRuns, 0, newArray, 0, runCount);
tabRuns = newArray;
}
protected int getRunForTab(int tabCount, int tabIndex) {
for (int i = 0; i < runCount; i++) {
int first = tabRuns[i];
int last = lastTabInRun(tabCount, i);
if (tabIndex >= first && tabIndex <= last) {
return i;
}
}
return 0;
}
protected int lastTabInRun(int tabCount, int run) {
if (runCount == 1) {
return tabCount - 1;
}
int nextRun = (run == runCount - 1 ? 0 : run + 1);
if (tabRuns[nextRun] == 0) {
return tabCount - 1;
}
return tabRuns[nextRun] - 1;
}
protected int getTabRunOverlay(int tabPlacement) {
return tabRunOverlay;
}
protected int getTabRunIndent(int tabPlacement, int run) {
return 0;
}
protected boolean shouldPadTabRun(int tabPlacement, int run) {
return runCount > 1;
}
protected boolean shouldRotateTabRuns(int tabPlacement) {
return true;
}
protected Icon getIconForTab(int tabIndex) {
if (tabIndex >= 0 && tabIndex < tabCount) {
return (!tabPane.isEnabled() || !tabPane.isEnabledAt(tabIndex)) ? tabPane.getDisabledIconAt(tabIndex) : tabPane.getIconAt(tabIndex);
}
return null;
}
/**
* Returns the text View object required to render stylized text (HTML) for the specified tab or null if no specialized text rendering is needed for
* this tab. This is provided to support html rendering inside tabs.
*
* @param tabIndex
* the index of the tab
* @return the text view to render the tab's text or null if no specialized rendering is required
*
* @since 1.4
*/
protected View getTextViewForTab(int tabIndex) {
if (htmlViews != null && htmlViews.size() > tabIndex) {
return (View) htmlViews.get(tabIndex);
}
return null;
}
protected int calculateTabHeight(int tabPlacement, int tabIndex, int fontHeight) {
int height = 0;
Component tabComponent = getTabComponentAt(tabIndex);
if (tabComponent != null) {
height = tabComponent.getPreferredSize().height;
}
else {
View v = getTextViewForTab(tabIndex);
if (v != null) {
// html
height += (int) v.getPreferredSpan(View.Y_AXIS);
}
else {
// plain text
height += fontHeight;
}
Icon icon = getIconForTab(tabIndex);
if (icon != null) {
height = Math.max(height, icon.getIconHeight());
}
}
Insets ti = getTabInsets(tabPlacement, tabIndex);
height += ti.top + ti.bottom + 2;
return height;
}
protected int calculateMaxTabHeight(int tabPlacement) {
int tc = tabPane.getTabCount();
int result = 0;
int fontHeight = getFontMetrics().getHeight();
for (int i = 0; i < tc; i++) {
result = Math.max(calculateTabHeight(tabPlacement, i, fontHeight), result);
}
return result;
}
protected int calculateTabWidth(int tabPlacement, int tabIndex, FontMetrics metrics) {
Insets insets = getTabInsets(tabPlacement, tabIndex);
int width = insets.left + insets.right + 3;
Component tabComponent = getTabComponentAt(tabIndex);
if (tabComponent != null) {
width += tabComponent.getPreferredSize().width;
}
else {
Icon icon = getIconForTab(tabIndex);
if (icon != null) {
width += icon.getIconWidth() + textIconGap;
}
View v = getTextViewForTab(tabIndex);
if (v != null) {
// html
width += (int) v.getPreferredSpan(View.X_AXIS);
}
else {
// plain text
String title = tabPane.getTitleAt(tabIndex);
width += SwingUtilities.computeStringWidth(metrics, title);
}
}
return width;
}
protected int calculateMaxTabWidth(int tabPlacement) {
int tc = tabPane.getTabCount();
int result = 0;
for (int i = 0; i < tc; i++) {
result = Math.max(calculateTabWidth(tabPlacement, i, getFontMetrics()), result);
}
return result;
}
protected int calculateTabAreaHeight(int tabPlacement, int horizRunCount, int maxTabHeight) {
if (tabPlacement == JTabbedPane.TOP || tabPlacement == JTabbedPane.BOTTOM) {
Insets insets = getTabAreaInsets(tabPlacement);
int overlay = getTabRunOverlay(tabPlacement);
return (horizRunCount > 0 ? horizRunCount * (maxTabHeight - overlay) + overlay + insets.top + insets.bottom : 0);
}
else {
return tabPane.getHeight();
}
}
protected int calculateTabAreaWidth(int tabPlacement, int vertRunCount, int maxTabWidth) {
if (tabPlacement == JTabbedPane.LEFT || tabPlacement == JTabbedPane.RIGHT) {
Insets insets = getTabAreaInsets(tabPlacement);
int overlay = getTabRunOverlay(tabPlacement);
return (vertRunCount > 0 ? vertRunCount * (maxTabWidth - overlay) + overlay + insets.left + insets.right : 0);
}
else {
return tabPane.getWidth();
}
}
protected Insets getTabInsets(int tabPlacement, int tabIndex) {
return tabInsets;
}
protected Insets getSelectedTabPadInsets(int tabPlacement) {
rotateInsets(selectedTabPadInsets, currentPadInsets, tabPlacement);
return currentPadInsets;
}
protected Insets getTabAreaInsets(int tabPlacement) {
rotateInsets(tabAreaInsets, currentTabAreaInsets, tabPlacement);
return currentTabAreaInsets;
}
protected Insets getContentBorderInsets(int tabPlacement) {
if (tabPane.getBorder() == null) {
return NULL_BORDER_INSETS;
}
return contentBorderInsets;
}
protected FontMetrics getFontMetrics() {
Font font = tabPane.getFont().deriveFont(Font.BOLD);
return JTattooUtilities.getFontMetrics(tabPane, null, font);
}
// Tab Navigation methods
protected void navigateSelectedTab(int direction) {
int tabPlacement = tabPane.getTabPlacement();
int current = tabPane.getSelectedIndex();
int tc = tabPane.getTabCount();
boolean leftToRight = JTattooUtilities.isLeftToRight(tabPane);
// If we have no tabs then don't navigate.
if (tc <= 0) {
return;
}
int offset;
switch (tabPlacement) {
case NEXT:
selectNextTab(current);
break;
case PREVIOUS:
selectPreviousTab(current);
break;
case LEFT:
case RIGHT:
switch (direction) {
case NORTH:
selectPreviousTabInRun(current);
break;
case SOUTH:
selectNextTabInRun(current);
break;
case WEST:
offset = getTabRunOffset(tabPlacement, tc, current, false);
selectAdjacentRunTab(tabPlacement, current, offset);
break;
case EAST:
offset = getTabRunOffset(tabPlacement, tc, current, true);
selectAdjacentRunTab(tabPlacement, current, offset);
break;
default:
}
break;
case BOTTOM:
case TOP:
default:
switch (direction) {
case NORTH:
offset = getTabRunOffset(tabPlacement, tc, current, false);
selectAdjacentRunTab(tabPlacement, current, offset);
break;
case SOUTH:
offset = getTabRunOffset(tabPlacement, tc, current, true);
selectAdjacentRunTab(tabPlacement, current, offset);
break;
case EAST:
if (leftToRight) {
selectNextTabInRun(current);
}
else {
selectPreviousTabInRun(current);
}
break;
case WEST:
if (leftToRight) {
selectPreviousTabInRun(current);
}
else {
selectNextTabInRun(current);
}
break;
default:
}
}
}
protected void selectNextTabInRun(int current) {
int tc = tabPane.getTabCount();
int tabIndex = getNextTabIndexInRun(tc, current);
while (tabIndex != current && !tabPane.isEnabledAt(tabIndex)) {
tabIndex = getNextTabIndexInRun(tc, tabIndex);
}
tabPane.setSelectedIndex(tabIndex);
}
protected void selectPreviousTabInRun(int current) {
int tc = tabPane.getTabCount();
int tabIndex = getPreviousTabIndexInRun(tc, current);
while (tabIndex != current && !tabPane.isEnabledAt(tabIndex)) {
tabIndex = getPreviousTabIndexInRun(tc, tabIndex);
}
tabPane.setSelectedIndex(tabIndex);
}
protected void selectNextTab(int current) {
int tabIndex = getNextTabIndex(current);
while (tabIndex != current && !tabPane.isEnabledAt(tabIndex)) {
tabIndex = getNextTabIndex(tabIndex);
}
tabPane.setSelectedIndex(tabIndex);
}
protected void selectPreviousTab(int current) {
int tabIndex = getPreviousTabIndex(current);
while (tabIndex != current && !tabPane.isEnabledAt(tabIndex)) {
tabIndex = getPreviousTabIndex(tabIndex);
}
tabPane.setSelectedIndex(tabIndex);
}
protected void selectAdjacentRunTab(int tabPlacement, int tabIndex, int offset) {
if (runCount < 2) {
return;
}
int newIndex;
Rectangle r = rects[tabIndex];
switch (tabPlacement) {
case LEFT:
case RIGHT:
newIndex = getTabAtLocation(r.x + r.width / 2 + offset, r.y + r.height / 2);
break;
case BOTTOM:
case TOP:
default:
newIndex = getTabAtLocation(r.x + r.width / 2, r.y + r.height / 2 + offset);
}
if (newIndex != -1) {
while (!tabPane.isEnabledAt(newIndex) && newIndex != tabIndex) {
newIndex = getNextTabIndex(newIndex);
}
tabPane.setSelectedIndex(newIndex);
}
}
protected int getTabRunOffset(int tabPlacement, int tabCount, int tabIndex, boolean forward) {
int run = getRunForTab(tabCount, tabIndex);
int offset;
switch (tabPlacement) {
case LEFT: {
if (run == 0) {
offset = (forward ? -(calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth) - maxTabWidth) : -maxTabWidth);
}
else if (run == runCount - 1) {
offset = (forward ? maxTabWidth : calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth) - maxTabWidth);
}
else {
offset = (forward ? maxTabWidth : -maxTabWidth);
}
break;
}
case RIGHT: {
if (run == 0) {
offset = (forward ? maxTabWidth : calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth) - maxTabWidth);
}
else if (run == runCount - 1) {
offset = (forward ? -(calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth) - maxTabWidth) : -maxTabWidth);
}
else {
offset = (forward ? maxTabWidth : -maxTabWidth);
}
break;
}
case BOTTOM: {
if (run == 0) {
offset = (forward ? maxTabHeight : calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight) - maxTabHeight);
}
else if (run == runCount - 1) {
offset = (forward ? -(calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight) - maxTabHeight) : -maxTabHeight);
}
else {
offset = (forward ? maxTabHeight : -maxTabHeight);
}
break;
}
case TOP:
default: {
if (run == 0) {
offset = (forward ? -(calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight) - maxTabHeight) : -maxTabHeight);
}
else if (run == runCount - 1) {
offset = (forward ? maxTabHeight : calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight) - maxTabHeight);
}
else {
offset = (forward ? maxTabHeight : -maxTabHeight);
}
}
}
return offset;
}
protected int getPreviousTabIndex(int base) {
int tabIndex = (base - 1 >= 0 ? base - 1 : tabPane.getTabCount() - 1);
return (tabIndex >= 0 ? tabIndex : 0);
}
protected int getNextTabIndex(int base) {
return (base + 1) % tabPane.getTabCount();
}
protected int getNextTabIndexInRun(int tabCount, int base) {
if (runCount < 2) {
return getNextTabIndex(base);
}
int currentRun = getRunForTab(tabCount, base);
int next = getNextTabIndex(base);
if (next == tabRuns[getNextTabRun(currentRun)]) {
return tabRuns[currentRun];
}
return next;
}
protected int getPreviousTabIndexInRun(int tabCount, int base) {
if (runCount < 2) {
return getPreviousTabIndex(base);
}
int currentRun = getRunForTab(tabCount, base);
if (base == tabRuns[currentRun]) {
int previous = tabRuns[getNextTabRun(currentRun)] - 1;
return (previous != -1 ? previous : tabCount - 1);
}
return getPreviousTabIndex(base);
}
protected int getPreviousTabRun(int baseRun) {
int runIndex = (baseRun - 1 >= 0 ? baseRun - 1 : runCount - 1);
return (runIndex >= 0 ? runIndex : 0);
}
protected int getNextTabRun(int baseRun) {
return (baseRun + 1) % runCount;
}
protected static void rotateInsets(Insets topInsets, Insets targetInsets, int targetPlacement) {
switch (targetPlacement) {
case LEFT:
targetInsets.top = topInsets.left;
targetInsets.left = topInsets.top;
targetInsets.bottom = topInsets.right;
targetInsets.right = topInsets.bottom;
break;
case BOTTOM:
targetInsets.top = topInsets.bottom;
targetInsets.left = topInsets.left;
targetInsets.bottom = topInsets.top;
targetInsets.right = topInsets.right;
break;
case RIGHT:
targetInsets.top = topInsets.left;
targetInsets.left = topInsets.bottom;
targetInsets.bottom = topInsets.right;
targetInsets.right = topInsets.top;
break;
case TOP:
default:
targetInsets.top = topInsets.top;
targetInsets.left = topInsets.left;
targetInsets.bottom = topInsets.bottom;
targetInsets.right = topInsets.right;
}
}
protected boolean requestFocusForVisibleComponent() {
Component vc = getVisibleComponent();
if (vc.isFocusTraversable()) {
vc.requestFocus();
return true;
}
else if (vc instanceof JComponent) {
if (((JComponent) vc).requestDefaultFocus()) {
return true;
}
}
return false;
}
private static class RightAction extends AbstractAction {
public void actionPerformed(ActionEvent e) {
JTabbedPane pane = (JTabbedPane) e.getSource();
BaseTabbedPaneUI ui = (BaseTabbedPaneUI) pane.getUI();
ui.navigateSelectedTab(EAST);
}
};
private static class LeftAction extends AbstractAction {
public void actionPerformed(ActionEvent e) {
JTabbedPane pane = (JTabbedPane) e.getSource();
BaseTabbedPaneUI ui = (BaseTabbedPaneUI) pane.getUI();
ui.navigateSelectedTab(WEST);
}
};
private static class UpAction extends AbstractAction {
public void actionPerformed(ActionEvent e) {
JTabbedPane pane = (JTabbedPane) e.getSource();
BaseTabbedPaneUI ui = (BaseTabbedPaneUI) pane.getUI();
ui.navigateSelectedTab(NORTH);
}
};
private static class DownAction extends AbstractAction {
public void actionPerformed(ActionEvent e) {
JTabbedPane pane = (JTabbedPane) e.getSource();
BaseTabbedPaneUI ui = (BaseTabbedPaneUI) pane.getUI();
ui.navigateSelectedTab(SOUTH);
}
};
private static class NextAction extends AbstractAction {
public void actionPerformed(ActionEvent e) {
JTabbedPane pane = (JTabbedPane) e.getSource();
BaseTabbedPaneUI ui = (BaseTabbedPaneUI) pane.getUI();
ui.navigateSelectedTab(NEXT);
}
};
private static class PreviousAction extends AbstractAction {
public void actionPerformed(ActionEvent e) {
JTabbedPane pane = (JTabbedPane) e.getSource();
BaseTabbedPaneUI ui = (BaseTabbedPaneUI) pane.getUI();
ui.navigateSelectedTab(PREVIOUS);
}
};
private static class PageUpAction extends AbstractAction {
public void actionPerformed(ActionEvent e) {
JTabbedPane pane = (JTabbedPane) e.getSource();
BaseTabbedPaneUI ui = (BaseTabbedPaneUI) pane.getUI();
int tabPlacement = pane.getTabPlacement();
if (tabPlacement == TOP || tabPlacement == BOTTOM) {
ui.navigateSelectedTab(WEST);
}
else {
ui.navigateSelectedTab(NORTH);
}
}
};
private static class PageDownAction extends AbstractAction {
public void actionPerformed(ActionEvent e) {
JTabbedPane pane = (JTabbedPane) e.getSource();
BaseTabbedPaneUI ui = (BaseTabbedPaneUI) pane.getUI();
int tabPlacement = pane.getTabPlacement();
if (tabPlacement == TOP || tabPlacement == BOTTOM) {
ui.navigateSelectedTab(EAST);
}
else {
ui.navigateSelectedTab(SOUTH);
}
}
};
private static class RequestFocusAction extends AbstractAction {
public void actionPerformed(ActionEvent e) {
JTabbedPane pane = (JTabbedPane) e.getSource();
pane.requestFocus();
}
};
private static class RequestFocusForVisibleAction extends AbstractAction {
public void actionPerformed(ActionEvent e) {
JTabbedPane pane = (JTabbedPane) e.getSource();
BaseTabbedPaneUI ui = (BaseTabbedPaneUI) pane.getUI();
ui.requestFocusForVisibleComponent();
}
};
/**
* Selects a tab in the JTabbedPane based on the String of the action command. The tab selected is based on the first tab that has a mnemonic
* matching the first character of the action command.
*/
private static class SetSelectedIndexAction extends AbstractAction {
public void actionPerformed(ActionEvent e) {
JTabbedPane pane = (JTabbedPane) e.getSource();
if (pane != null && (pane.getUI() instanceof BaseTabbedPaneUI)) {
BaseTabbedPaneUI ui = (BaseTabbedPaneUI) pane.getUI();
String command = e.getActionCommand();
if (command != null && command.length() > 0) {
int mnemonic = (int) e.getActionCommand().charAt(0);
if (mnemonic >= 'a' && mnemonic <= 'z') {
mnemonic -= ('a' - 'A');
}
Integer index = (Integer) ui.mnemonicToIndexMap.get(new Integer(mnemonic));
if (index != null && pane.isEnabledAt(index.intValue())) {
pane.setSelectedIndex(index.intValue());
}
}
}
}
};
private static class ScrollTabsForwardAction extends AbstractAction {
public void actionPerformed(ActionEvent e) {
JTabbedPane pane;
Object src = e.getSource();
if (src instanceof JTabbedPane) {
pane = (JTabbedPane) src;
}
else if (src instanceof ScrollableTabButton) {
pane = (JTabbedPane) ((ScrollableTabButton) src).getParent();
}
else {
return; // shouldn't happen
}
BaseTabbedPaneUI ui = (BaseTabbedPaneUI) pane.getUI();
if (ui.scrollableTabLayoutEnabled()) {
ui.tabScroller.scrollForward(pane.getTabPlacement());
}
}
}
private static class ScrollTabsBackwardAction extends AbstractAction {
public void actionPerformed(ActionEvent e) {
JTabbedPane pane;
Object src = e.getSource();
if (src instanceof JTabbedPane) {
pane = (JTabbedPane) src;
}
else if (src instanceof ScrollableTabButton) {
pane = (JTabbedPane) ((ScrollableTabButton) src).getParent();
}
else {
return; // shouldn't happen
}
BaseTabbedPaneUI ui = (BaseTabbedPaneUI) pane.getUI();
if (ui.scrollableTabLayoutEnabled()) {
ui.tabScroller.scrollBackward(pane.getTabPlacement());
}
}
}
private static class ScrollTabsPopupMenuItemAction extends AbstractAction {
private JTabbedPane tabbedPane = null;
private int selectIndex = 0;
public ScrollTabsPopupMenuItemAction(JTabbedPane pane, int index) {
tabbedPane = pane;
selectIndex = index;
}
public void actionPerformed(ActionEvent e) {
tabbedPane.setSelectedIndex(selectIndex);
}
}
private static class ScrollTabsPopupMenuAction extends AbstractAction {
private JTabbedPane tabbedPane = null;
public void actionPerformed(ActionEvent e) {
Object src = e.getSource();
if (src instanceof JTabbedPane) {
tabbedPane = (JTabbedPane) src;
}
else if (src instanceof ScrollablePopupMenuTabButton) {
tabbedPane = (JTabbedPane) ((ScrollablePopupMenuTabButton) src).getParent();
}
else {
return; // shouldn't happen
}
BaseTabbedPaneUI ui = (BaseTabbedPaneUI) tabbedPane.getUI();
if (ui.scrollableTabLayoutEnabled()) {
JPopupMenu popup = new JPopupMenu();
for (int i = 0; i < tabbedPane.getTabCount(); i++) {
JMenuItem item = new JMenuItem(tabbedPane.getTitleAt(i));
item.addActionListener(new ScrollTabsPopupMenuItemAction(tabbedPane, i));
item.setEnabled(tabbedPane.isEnabledAt(i));
popup.add(item);
}
popup.show(ui.tabScroller.popupMenuButton, 0, 0);
Point pt = ui.tabScroller.popupMenuButton.getLocationOnScreen();
int x = -popup.getWidth() + ui.tabScroller.popupMenuButton.getWidth();
int y = ui.tabScroller.popupMenuButton.getHeight() - 1;
popup.setLocation(pt.x + x, pt.y + y);
}
}
}
/**
* 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 BaseTabbedPaneUI.
*/
public class TabbedPaneLayout implements LayoutManager {
public void addLayoutComponent(String name, Component comp) {
}
public void removeLayoutComponent(Component comp) {
}
public Dimension preferredLayoutSize(Container parent) {
return calculateSize(false);
}
public Dimension minimumLayoutSize(Container parent) {
return calculateSize(true);
}
protected Dimension calculateSize(boolean minimum) {
int tabPlacement = tabPane.getTabPlacement();
Insets insets = tabPane.getInsets();
Insets contentInsets = getContentBorderInsets(tabPlacement);
Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
Dimension zeroSize = new Dimension(0, 0);
int height = contentInsets.top + contentInsets.bottom;
int width = contentInsets.left + contentInsets.right;
int cWidth = 0;
int cHeight = 0;
// Determine minimum size required to display largest
// child in each dimension
//
for (int i = 0; i < tabPane.getTabCount(); i++) {
Component component = tabPane.getComponentAt(i);
if (component != null) {
Dimension size = minimum ? component.getMinimumSize() : component.getPreferredSize();
if (size != null) {
cHeight = Math.max(size.height, cHeight);
cWidth = Math.max(size.width, cWidth);
}
}
}
// Add content border insets to minimum size
width += cWidth;
height += cHeight;
int tabExtent;
// Calculate how much space the tabs will need, based on the
// minimum size required to display largest child + content border
//
switch (tabPlacement) {
case LEFT:
case RIGHT:
height = Math.max(height, calculateMaxTabHeight(tabPlacement) + tabAreaInsets.top + tabAreaInsets.bottom);
tabExtent = preferredTabAreaWidth(tabPlacement, height);
width += tabExtent;
break;
case TOP:
case BOTTOM:
default:
width = Math.max(width, calculateMaxTabWidth(tabPlacement) + tabAreaInsets.left + tabAreaInsets.right);
tabExtent = preferredTabAreaHeight(tabPlacement, width);
height += tabExtent;
}
return new Dimension(width + insets.left + insets.right, height + insets.bottom + insets.top);
}
protected int preferredTabAreaHeight(int tabPlacement, int width) {
FontMetrics fm = getFontMetrics();
int tc = tabPane.getTabCount();
int total = 0;
if (tc > 0) {
int rows = 1;
int x = 0;
int maxTabHeight = calculateMaxTabHeight(tabPlacement);
for (int i = 0; i < tc; i++) {
int tabWidth = calculateTabWidth(tabPlacement, i, fm);
if (x != 0 && x + tabWidth > width) {
rows++;
x = 0;
}
x += tabWidth;
}
total = calculateTabAreaHeight(tabPlacement, rows, maxTabHeight);
}
return total;
}
protected int preferredTabAreaWidth(int tabPlacement, int height) {
FontMetrics fm = getFontMetrics();
int tc = tabPane.getTabCount();
int total = 0;
if (tc > 0) {
int columns = 1;
int y = 0;
int fontHeight = fm.getHeight();
maxTabWidth = calculateMaxTabWidth(tabPlacement);
for (int i = 0; i < tc; i++) {
int tabHeight = calculateTabHeight(tabPlacement, i, fontHeight);
if (y != 0 && y + tabHeight > height) {
columns++;
y = 0;
}
y += tabHeight;
}
total = calculateTabAreaWidth(tabPlacement, columns, maxTabWidth);
}
return total;
}
public void layoutContainer(Container parent) {
/*
* Some of the code in this method deals with changing the visibility of components to hide and show the contents for the selected tab. This is
* older code that has since been duplicated in JTabbedPane.fireStateChanged(), so as to allow visibility changes to happen sooner (see the note
* there). This code remains for backward compatibility as there are some cases, such as subclasses that don't fireStateChanged() where it may
* be used. Any changes here need to be kept in synch with JTabbedPane.fireStateChanged().
*/
int tabPlacement = tabPane.getTabPlacement();
Insets insets = tabPane.getInsets();
int selectedIndex = tabPane.getSelectedIndex();
Component visibleComponent = getVisibleComponent();
calculateLayoutInfo();
Component selectedComponent = null;
if (selectedIndex < 0) {
if (visibleComponent != null) {
// The last tab was removed, so remove the component
setVisibleComponent(null);
}
}
else {
try {
selectedComponent = tabPane.getComponentAt(selectedIndex);
}
catch (Exception ex) {
}
}
int cx, cy, cw, ch;
int totalTabWidth = 0;
int totalTabHeight = 0;
Insets contentInsets = getContentBorderInsets(tabPlacement);
boolean shouldChangeFocus = false;
// In order to allow programs to use a single component
// as the display for multiple tabs, we will not change
// the visible compnent if the currently selected tab
// has a null component. This is a bit dicey, as we don't
// explicitly state we support this in the spec, but since
// programs are now depending on this, we're making it work.
//
if (selectedComponent != null) {
if (selectedComponent != visibleComponent && visibleComponent != null) {
if (SwingUtilities.findFocusOwner(visibleComponent) != null) {
shouldChangeFocus = true;
}
}
setVisibleComponent(selectedComponent);
}
Rectangle bounds = tabPane.getBounds();
int numChildren = tabPane.getComponentCount();
if (numChildren > 0) {
switch (tabPlacement) {
case LEFT:
totalTabWidth = calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth);
cx = insets.left + totalTabWidth + contentInsets.left;
cy = insets.top + contentInsets.top;
break;
case RIGHT:
totalTabWidth = calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth);
cx = insets.left + contentInsets.left;
cy = insets.top + contentInsets.top;
break;
case BOTTOM:
totalTabHeight = calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight);
cx = insets.left + contentInsets.left;
cy = insets.top + contentInsets.top;
break;
case TOP:
default:
totalTabHeight = calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight);
cx = insets.left + contentInsets.left;
cy = insets.top + totalTabHeight + contentInsets.top;
}
cw = bounds.width - totalTabWidth - insets.left - insets.right - contentInsets.left - contentInsets.right;
ch = bounds.height - totalTabHeight - insets.top - insets.bottom - contentInsets.top - contentInsets.bottom;
for (int i = 0; i < numChildren; i++) {
try {
Component child = tabPane.getComponent(i);
if (tabContainer == child) {
int tabContainerWidth = totalTabWidth == 0 ? cw : totalTabWidth;
int tabContainerHeight = totalTabHeight == 0 ? ch : totalTabHeight;
int tabContainerX = 0;
int tabContainerY = 0;
if (tabPlacement == BOTTOM) {
tabContainerY = bounds.height - tabContainerHeight;
}
else if (tabPlacement == RIGHT) {
tabContainerX = bounds.width - tabContainerWidth;
}
child.setBounds(tabContainerX, tabContainerY, tabContainerWidth, tabContainerHeight);
}
else {
child.setBounds(cx, cy, cw, ch);
}
}
catch (Exception ex) {
}
}
}
layoutTabComponents();
if (shouldChangeFocus) {
if (!requestFocusForVisibleComponent()) {
tabPane.requestFocus();
}
}
}
public void calculateLayoutInfo() {
int tc = tabPane.getTabCount();
assureRectsCreated(tc);
calculateTabRects(tabPane.getTabPlacement(), tc);
}
private void layoutTabComponents() {
if (JTattooUtilities.getJavaVersion() >= 1.6) {
if (tabContainer == null) {
return;
}
Rectangle rect = new Rectangle();
Point delta = new Point(-tabContainer.getX(), -tabContainer.getY());
if (scrollableTabLayoutEnabled()) {
translatePointToTabPanel(0, 0, delta);
}
for (int i = 0; i < tabPane.getTabCount(); i++) {
Component tabComponent = getTabComponentAt(i);
if (tabComponent == null) {
continue;
}
getTabBounds(i, rect);
Dimension preferredSize = tabComponent.getPreferredSize();
Insets insets = getTabInsets(tabPane.getTabPlacement(), i);
int outerX = rect.x + insets.left + delta.x;
int outerY = rect.y + insets.top + delta.y;
int outerWidth = rect.width - insets.left - insets.right;
int outerHeight = rect.height - insets.top - insets.bottom;
// centralize component
int x = outerX + (outerWidth - preferredSize.width) / 2;
int y = outerY + (outerHeight - preferredSize.height) / 2;
int tabPlacement = tabPane.getTabPlacement();
boolean isSeleceted = i == tabPane.getSelectedIndex();
tabComponent.setBounds(x + getTabLabelShiftX(tabPlacement, i, isSeleceted), y + getTabLabelShiftY(tabPlacement, i, isSeleceted),
preferredSize.width, preferredSize.height);
}
}
}
protected void calculateTabRects(int tabPlacement, int tabCount) {
FontMetrics fm = getFontMetrics();
Dimension size = tabPane.getSize();
Insets insets = tabPane.getInsets();
Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
int fontHeight = fm.getHeight();
int selectedIndex = tabPane.getSelectedIndex();
int tabRunOverlay;
int i, j;
int x, y;
int returnAt;
boolean verticalTabRuns = (tabPlacement == LEFT || tabPlacement == RIGHT);
boolean leftToRight = JTattooUtilities.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, fm);
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);
}
}
// 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, returnAt);
}
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, returnAt);
}
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;
}
}
}
/*
* Rotates the run-index array so that the selected run is run[0]
*/
protected void rotateTabRuns(int tabPlacement, int selectedRun) {
for (int i = 0; i < selectedRun; i++) {
int save = tabRuns[0];
for (int j = 1; j < runCount; j++) {
tabRuns[j - 1] = tabRuns[j];
}
tabRuns[runCount - 1] = save;
}
}
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) {
int run = runCount - 1;
boolean keepAdjusting = true;
double weight = 1.25;
// 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;
end = rects[last].x + rects[last].width;
prevLastLen = (int) (maxTabWidth * weight);
// 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;
rects[prevLast].x = start;
for (int i = prevLast + 1; i <= last; i++) {
rects[i].x = rects[i - 1].x + rects[i - 1].width;
}
}
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 += .25;
}
}
}
}
protected void padTabRun(int tabPlacement, int start, int end, int max) {
Rectangle lastRect = rects[end];
if (tabPlacement == TOP || tabPlacement == BOTTOM) {
int runWidth = (lastRect.x + lastRect.width) - rects[start].x;
int deltaWidth = max - (lastRect.x + lastRect.width);
float factor = (float) deltaWidth / (float) runWidth;
for (int j = start; j <= end; j++) {
Rectangle pastRect = rects[j];
if (j > start) {
pastRect.x = rects[j - 1].x + rects[j - 1].width;
}
pastRect.width += Math.round((float) pastRect.width * factor);
}
lastRect.width = max - lastRect.x;
}
else {
int runHeight = (lastRect.y + lastRect.height) - rects[start].y;
int deltaHeight = max - (lastRect.y + lastRect.height);
float factor = (float) deltaHeight / (float) runHeight;
for (int j = start; j <= end; j++) {
Rectangle pastRect = rects[j];
if (j > start) {
pastRect.y = rects[j - 1].y + rects[j - 1].height;
}
pastRect.height += Math.round((float) pastRect.height * factor);
}
lastRect.height = max - lastRect.y;
}
}
protected void padSelectedTab(int tabPlacement, int selectedIndex) {
// if ((selectedIndex >= 0) && (selectedIndex < rects.length)) {
// Rectangle selRect = rects[selectedIndex];
// Insets padInsets = getSelectedTabPadInsets(tabPlacement);
// selRect.x -= padInsets.left;
// selRect.width += (padInsets.left + padInsets.right);
// selRect.y -= padInsets.top;
// selRect.height += (padInsets.top + padInsets.bottom);
// }
}
}
private class TabbedPaneScrollLayout extends TabbedPaneLayout {
protected int preferredTabAreaHeight(int tabPlacement, int width) {
return calculateMaxTabHeight(tabPlacement);
}
protected int preferredTabAreaWidth(int tabPlacement, int height) {
return calculateMaxTabWidth(tabPlacement);
}
public void layoutContainer(Container parent) {
int tabPlacement = tabPane.getTabPlacement();
int tc = tabPane.getTabCount();
Insets insets = tabPane.getInsets();
int selectedIndex = tabPane.getSelectedIndex();
Component visibleComponent = getVisibleComponent();
calculateLayoutInfo();
Component selectedComponent = null;
if (selectedIndex < 0) {
if (visibleComponent != null) {
// The last tab was removed, so remove the component
setVisibleComponent(null);
}
}
else {
try {
selectedComponent = tabPane.getComponentAt(selectedIndex);
}
catch (Exception ex) {
// outStream.print("----------------------------------------------------\n");
// ex.printStackTrace(outStream);
}
}
boolean shouldChangeFocus = false;
// In order to allow programs to use a single component
// as the display for multiple tabs, we will not change
// the visible compnent if the currently selected tab
// has a null component. This is a bit dicey, as we don't
// explicitly state we support this in the spec, but since
// programs are now depending on this, we're making it work.
//
if (selectedComponent != null) {
if (selectedComponent != visibleComponent && visibleComponent != null) {
if (SwingUtilities.findFocusOwner(visibleComponent) != null) {
shouldChangeFocus = true;
}
}
setVisibleComponent(selectedComponent);
}
int tx, ty, tw, th; // tab area bounds
int cx, cy, cw, ch; // content area bounds
Insets contentInsets = getContentBorderInsets(tabPlacement);
Rectangle bounds = tabPane.getBounds();
int numChildren = tabPane.getComponentCount();
int space = 60;
if ((numChildren > 0) && (tc > 0)) {
switch (tabPlacement) {
case LEFT:
// calculate tab area bounds
tw = calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth);
th = bounds.height - insets.top - insets.bottom;
tx = insets.left;
ty = insets.top;
// calculate content area bounds
cx = tx + tw + contentInsets.left;
cy = ty + contentInsets.top;
cw = bounds.width - insets.left - insets.right - tw - contentInsets.left - contentInsets.right;
ch = bounds.height - insets.top - insets.bottom - contentInsets.top - contentInsets.bottom;
break;
case RIGHT:
// calculate tab area bounds
tw = calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth);
th = bounds.height - insets.top - insets.bottom;
tx = bounds.width - insets.right - tw;
ty = insets.top;
// calculate content area bounds
cx = insets.left + contentInsets.left;
cy = insets.top + contentInsets.top;
cw = bounds.width - insets.left - insets.right - tw - contentInsets.left - contentInsets.right;
ch = bounds.height - insets.top - insets.bottom - contentInsets.top - contentInsets.bottom;
break;
case BOTTOM:
// calculate tab area bounds
tw = bounds.width - insets.left - insets.right;
th = calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight);
tx = insets.left;
ty = bounds.height - insets.bottom - th;
// calculate content area bounds
cx = insets.left + contentInsets.left;
cy = insets.top + contentInsets.top;
cw = bounds.width - insets.left - insets.right - contentInsets.left - contentInsets.right;
ch = bounds.height - insets.top - insets.bottom - th - contentInsets.top - contentInsets.bottom;
break;
case TOP:
default:
// calculate tab area bounds
tw = bounds.width - insets.left - insets.right;
th = calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight);
tx = insets.left;
ty = insets.top;
// calculate content area bounds
cx = tx + contentInsets.left;
cy = ty + th + contentInsets.top;
cw = bounds.width - insets.left - insets.right - contentInsets.left - contentInsets.right;
ch = bounds.height - insets.top - insets.bottom - th - contentInsets.top - contentInsets.bottom;
}
for (int i = 0; i < numChildren; i++) {
Component child = tabPane.getComponent(i);
if (child instanceof ScrollableTabViewport) {
JViewport viewport = (JViewport) child;
Rectangle viewRect = viewport.getViewRect();
int vw = tw;
int vh = th;
switch (tabPlacement) {
case LEFT:
case RIGHT:
int totalTabHeight = rects[tc - 1].y + rects[tc - 1].height;
if (totalTabHeight > th) {
// Allow space for scrollbuttons
vh = Math.max(th - space, space);
if (totalTabHeight - viewRect.y <= vh) {
// Scrolled to the end, so ensure the viewport size is
// such that the scroll offset aligns with a tab
vh = totalTabHeight - viewRect.y;
}
}
break;
case BOTTOM:
case TOP:
default:
int totalTabWidth = rects[tc - 1].x + rects[tc - 1].width;
if (totalTabWidth > tw) {
// Allow space for scrollbuttons
vw = Math.max(tw - space, space);
if (totalTabWidth - viewRect.x <= vw) {
// Scrolled to the end, so ensure the viewport size is
// such that the scroll offset aligns with a tab
vw = totalTabWidth - viewRect.x;
}
}
}
child.setBounds(tx, ty, vw, vh);
}
else if (child instanceof ScrollableTabButton) {
ScrollableTabButton scrollbutton = (ScrollableTabButton) child;
Dimension bsize = scrollbutton.getPreferredSize();
int bx = 0;
int by = 0;
int bw = bsize.width;
int bh = bsize.height;
boolean visible = false;
switch (tabPlacement) {
case LEFT:
case RIGHT:
int totalTabHeight = rects[tc - 1].y + rects[tc - 1].height;
if (totalTabHeight > th) {
int dir = scrollbutton.scrollsForward() ? SOUTH : NORTH;
scrollbutton.setDirection(dir);
visible = true;
bx = tabPlacement == LEFT ? tw - insets.left - tabAreaInsets.bottom - bsize.width : bounds.width - insets.left - bsize.width;
by = dir == SOUTH ? bounds.height - insets.bottom - 2 * bsize.height - 2 : bounds.height - insets.bottom - 3 * bsize.height - 2;
}
break;
case BOTTOM:
case TOP:
default:
int totalTabWidth = rects[tc - 1].x + rects[tc - 1].width;
if (totalTabWidth > tw) {
int dir = scrollbutton.scrollsForward() ? EAST : WEST;
scrollbutton.setDirection(dir);
visible = true;
bx = dir == EAST ? bounds.width - insets.left - 2 * bsize.width - 2 : bounds.width - insets.left - 3 * bsize.width - 2;
by = ty + (th - bsize.height - tabAreaInsets.bottom) / 2;
if (tabPlacement == BOTTOM) {
by += tabAreaInsets.bottom;
}
else {
by++;
}
}
}
child.setVisible(visible);
if (visible) {
child.setBounds(bx, by, bw, bh);
}
}
else if (child instanceof ScrollablePopupMenuTabButton) {
ScrollablePopupMenuTabButton button = (ScrollablePopupMenuTabButton) child;
Dimension bsize = button.getPreferredSize();
int bx = 0;
int by = 0;
int bw = bsize.width;
int bh = bsize.height;
boolean visible = false;
switch (tabPlacement) {
case LEFT:
case RIGHT:
int totalTabHeight = rects[tc - 1].y + rects[tc - 1].height;
if (totalTabHeight > th) {
visible = true;
bx = tabPlacement == LEFT ? tw - insets.left - tabAreaInsets.bottom - bsize.width : bounds.width - insets.left - bsize.width;
by = bounds.height - insets.bottom - bsize.height;
}
break;
case BOTTOM:
case TOP:
default:
int totalTabWidth = rects[tc - 1].x + rects[tc - 1].width;
if (totalTabWidth > tw) {
visible = true;
bx = bounds.width - insets.left - bsize.width;
by = ty + (th - bsize.height - tabAreaInsets.bottom) / 2;
if (tabPlacement == BOTTOM) {
by += tabAreaInsets.bottom;
}
else {
by++;
}
}
}
child.setVisible(visible);
if (visible) {
child.setBounds(bx, by, bw, bh);
}
}
else {
// All content children...
child.setBounds(cx, cy, cw, ch);
}
}
super.layoutTabComponents();
if (shouldChangeFocus) {
if (!requestFocusForVisibleComponent()) {
tabPane.requestFocus();
}
}
}
}
protected void calculateTabRects(int tabPlacement, int tabCount) {
FontMetrics fm = getFontMetrics();
Dimension size = tabPane.getSize();
Insets insets = tabPane.getInsets();
Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
int fontHeight = fm.getHeight();
boolean verticalTabRuns = (tabPlacement == LEFT || tabPlacement == RIGHT);
boolean leftToRight = JTattooUtilities.isLeftToRight(tabPane);
int x = tabAreaInsets.left;
int y = tabAreaInsets.top;
int totalWidth = 0;
int totalHeight = 0;
//
// Calculate bounds within which a tab run must fit
//
switch (tabPlacement) {
case LEFT:
case RIGHT:
maxTabWidth = calculateMaxTabWidth(tabPlacement);
break;
case BOTTOM:
case TOP:
default:
maxTabHeight = calculateMaxTabHeight(tabPlacement);
}
runCount = 0;
selectedRun = -1;
if (tabCount == 0) {
return;
}
selectedRun = 0;
runCount = 1;
// Run through tabs and lay them out in a single run
Rectangle rect;
for (int 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;
maxTabWidth = 0;
totalHeight += maxTabHeight;
rect.x = x;
}
rect.width = calculateTabWidth(tabPlacement, i, fm);
totalWidth = rect.x + rect.width;
maxTabWidth = Math.max(maxTabWidth, rect.width);
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;
maxTabHeight = 0;
totalWidth = maxTabWidth;
rect.y = y;
}
rect.height = calculateTabHeight(tabPlacement, i, fontHeight);
totalHeight = rect.y + rect.height;
maxTabHeight = Math.max(maxTabHeight, rect.height);
rect.x = x;
rect.width = maxTabWidth/* - 2 */;
}
}
// 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 (int i = 0; i < tabCount; i++) {
rects[i].x = rightMargin - rects[i].x - rects[i].width;
}
}
// tabPanel.setSize(totalWidth, totalHeight);
tabScroller.tabPanel.setPreferredSize(new Dimension(totalWidth, totalHeight));
}
}
private class ScrollableTabSupport implements ChangeListener {
public ScrollableTabViewport viewport;
public ScrollableTabPanel tabPanel;
public ScrollableTabButton scrollForwardButton;
public ScrollableTabButton scrollBackwardButton;
public ScrollablePopupMenuTabButton popupMenuButton;
public int leadingTabIndex;
private Point tabViewPosition = new Point(0, 0);
ScrollableTabSupport(int tabPlacement) {
viewport = new ScrollableTabViewport();
tabPanel = new ScrollableTabPanel();
viewport.setView(tabPanel);
viewport.addChangeListener(this);
if (tabPlacement == TOP || tabPlacement == BOTTOM) {
scrollForwardButton = new ScrollableTabButton(EAST);
scrollBackwardButton = new ScrollableTabButton(WEST);
}
else { // tabPlacement = LEFT || RIGHT
scrollForwardButton = new ScrollableTabButton(SOUTH);
scrollBackwardButton = new ScrollableTabButton(NORTH);
}
popupMenuButton = new ScrollablePopupMenuTabButton();
}
public void scrollForward(int tabPlacement) {
Dimension viewSize = viewport.getViewSize();
Rectangle viewRect = viewport.getViewRect();
if (tabPlacement == TOP || tabPlacement == BOTTOM) {
if (viewRect.width >= viewSize.width - viewRect.x) {
return; // no room left to scroll
}
}
else {
// tabPlacement == LEFT || tabPlacement == RIGHT
if (viewRect.height >= viewSize.height - viewRect.y) {
return;
}
}
setLeadingTabIndex(tabPlacement, leadingTabIndex + 1);
if (tabPane != null) {
tabPane.doLayout();
}
}
public void scrollBackward(int tabPlacement) {
if (leadingTabIndex == 0) {
return; // no room left to scroll
}
setLeadingTabIndex(tabPlacement, leadingTabIndex - 1);
if (tabPane != null) {
tabPane.doLayout();
}
}
public void scrollTabToVisible(int tabPlacement, int index) {
if (index <= leadingTabIndex) {
setLeadingTabIndex(tabPlacement, index);
}
else {
Rectangle viewRect = viewport.getViewRect();
switch (tabPlacement) {
case TOP:
case BOTTOM: {
int i = index;
int x = viewRect.width - rects[index].width;
while ((i > 0) && (x - rects[i - 1].width >= 0)) {
i--;
x -= rects[i].width;
}
if (leadingTabIndex < i) {
setLeadingTabIndex(tabPlacement, i);
}
break;
}
case LEFT:
case RIGHT: {
int i = index;
int y = viewRect.height - rects[index].height;
while ((i > 0) && (y - rects[i - 1].height > 0)) {
i--;
y -= rects[i].height;
}
if (leadingTabIndex < i) {
setLeadingTabIndex(tabPlacement, i);
}
break;
}
}
}
}
public void setLeadingTabIndex(int tabPlacement, int index) {
leadingTabIndex = index;
Dimension viewSize = viewport.getViewSize();
Rectangle viewRect = viewport.getViewRect();
switch (tabPlacement) {
case TOP:
case BOTTOM:
tabViewPosition.x = leadingTabIndex == 0 ? 0 : rects[leadingTabIndex].x;
if ((viewSize.width - tabViewPosition.x) < viewRect.width) {
// We've scrolled to the end, so adjust the viewport size
// to ensure the view position remains aligned on a tab boundary
Dimension extentSize = new Dimension(viewSize.width - tabViewPosition.x, viewRect.height);
viewport.setExtentSize(extentSize);
}
break;
case LEFT:
case RIGHT:
tabViewPosition.y = leadingTabIndex == 0 ? 0 : rects[leadingTabIndex].y;
if ((viewSize.height - tabViewPosition.y) < viewRect.height) {
// We've scrolled to the end, so adjust the viewport size
// to ensure the view position remains aligned on a tab boundary
Dimension extentSize = new Dimension(viewRect.width, viewSize.height - tabViewPosition.y);
viewport.setExtentSize(extentSize);
}
}
viewport.setViewPosition(tabViewPosition);
}
public void stateChanged(ChangeEvent e) {
JViewport vp = (JViewport) e.getSource();
int tabPlacement = tabPane.getTabPlacement();
int tc = tabPane.getTabCount();
Rectangle vpRect = vp.getBounds();
Dimension viewSize = vp.getViewSize();
Rectangle viewRect = vp.getViewRect();
leadingTabIndex = getClosestTab(viewRect.x, viewRect.y);
if (leadingTabIndex >= rects.length) {
return;
}
// If the tab isn't right aligned, adjust it.
if (leadingTabIndex + 1 < tc) {
switch (tabPlacement) {
case TOP:
case BOTTOM:
if (rects[leadingTabIndex].x < viewRect.x) {
leadingTabIndex++;
}
break;
case LEFT:
case RIGHT:
if (rects[leadingTabIndex].y < viewRect.y) {
leadingTabIndex++;
}
break;
}
}
Insets contentInsets = getContentBorderInsets(tabPlacement);
switch (tabPlacement) {
case LEFT:
tabPane.repaint(vpRect.x + vpRect.width, vpRect.y, contentInsets.left, vpRect.height);
scrollBackwardButton.setEnabled(viewRect.y > 0);
scrollForwardButton.setEnabled(leadingTabIndex < tc - 1 && viewSize.height - viewRect.y > viewRect.height);
break;
case RIGHT:
tabPane.repaint(vpRect.x - contentInsets.right, vpRect.y, contentInsets.right, vpRect.height);
scrollBackwardButton.setEnabled(viewRect.y > 0);
scrollForwardButton.setEnabled(leadingTabIndex < tc - 1 && viewSize.height - viewRect.y > viewRect.height);
break;
case BOTTOM:
tabPane.repaint(vpRect.x, vpRect.y - contentInsets.bottom, vpRect.width, contentInsets.bottom);
scrollBackwardButton.setEnabled(viewRect.x > 0);
scrollForwardButton.setEnabled(leadingTabIndex < tc - 1 && viewSize.width - viewRect.x > viewRect.width);
break;
case TOP:
default:
tabPane.repaint(vpRect.x, vpRect.y + vpRect.height, vpRect.width, contentInsets.top);
scrollBackwardButton.setEnabled(viewRect.x > 0);
scrollForwardButton.setEnabled(leadingTabIndex < tc - 1 && viewSize.width - viewRect.x > viewRect.width);
}
}
}
private class ScrollableTabViewport extends JViewport implements UIResource {
public ScrollableTabViewport() {
setScrollMode(SIMPLE_SCROLL_MODE);
setOpaque(false);
}
}
private class ScrollableTabPanel extends JPanel implements UIResource {
public ScrollableTabPanel() {
setLayout(null);
setOpaque(false);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
paintScrollContentBorder(g, tabPane.getTabPlacement(), tabPane.getSelectedIndex(), 0, 0, getWidth(), getHeight());
paintTabArea(g, tabPane.getTabPlacement(), tabPane.getSelectedIndex());
}
public void doLayout() {
if (getComponentCount() > 0) {
Component child = getComponent(0);
child.setBounds(0, 0, getWidth(), getHeight());
}
}
}
public class ArrowButton extends JButton implements SwingConstants {
protected int direction;
public ArrowButton(int direction) {
super();
this.direction = direction;
setRequestFocusEnabled(false);
if (simpleButtonBorder) {
Color cLo = getLoBorderColor(0);
Color cHi = AbstractLookAndFeel.getTheme().getControlHighlight();
setBorder(BorderFactory.createEtchedBorder(cHi, cLo));
}
}
public int getDirection() {
return direction;
}
public void setDirection(int dir) {
direction = dir;
}
public void paint(Graphics g) {
super.paint(g);
// Draw the arrow
int w = getSize().width;
int h = getSize().height;
int size = Math.min((h - 4) / 3, (w - 4) / 3);
size = Math.max(size, 2);
paintTriangle(g, (w - size) / 2 + 1, (h - size) / 2 + 1, size);
}
public Dimension getPreferredSize() {
return new Dimension(17, 17);
}
public Dimension getMinimumSize() {
return new Dimension(5, 5);
}
public Dimension getMaximumSize() {
return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
}
public boolean isFocusTraversable() {
return false;
}
public void paintTriangle(Graphics g, int x, int y, int size) {
Color oldColor = g.getColor();
int mid, i, j;
size = Math.max(size, 2);
mid = (size / 2) - 1;
Color enabledColor = AbstractLookAndFeel.getTheme().getButtonForegroundColor();
Color disabledColor = AbstractLookAndFeel.getTheme().getDisabledForegroundColor();
g.translate(x, y);
if (isEnabled()) {
g.setColor(enabledColor);
}
else {
g.setColor(disabledColor);
}
switch (direction) {
case NORTH:
for (i = 0; i < size; i++) {
g.drawLine(mid - i, i, mid + i, i);
}
break;
case SOUTH:
j = 0;
for (i = size - 1; i >= 0; i--) {
g.drawLine(mid - i, j, mid + i, j);
j++;
}
break;
case WEST:
for (i = 0; i < size; i++) {
g.drawLine(i, mid - i, i, mid + i);
}
break;
case EAST:
j = 0;
for (i = size - 1; i >= 0; i--) {
g.drawLine(j, mid - i, j, mid + i);
j++;
}
break;
}
g.translate(-x, -y);
g.setColor(oldColor);
}
}
private class ScrollableTabButton extends ArrowButton implements UIResource, SwingConstants {
public ScrollableTabButton(int direction) {
super(direction);
}
public boolean scrollsForward() {
return direction == EAST || direction == SOUTH;
}
}
private class ScrollablePopupMenuTabButton extends ArrowButton implements UIResource, SwingConstants {
public ScrollablePopupMenuTabButton() {
super(SOUTH);
}
}
// Controller: event listeners
/**
* 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 BaseTabbedPaneUI.
*/
public class PropertyChangeHandler implements PropertyChangeListener {
public void propertyChange(PropertyChangeEvent e) {
JTabbedPane pane = (JTabbedPane) e.getSource();
String name = e.getPropertyName();
boolean isScrollLayout = scrollableTabLayoutEnabled();
if ("mnemonicAt".equals(name)) {
updateMnemonics();
pane.repaint();
}
else if ("displayedMnemonicIndexAt".equals(name)) {
pane.repaint();
}
else if ("indexForTitle".equals(name)) {
int index = ((Integer) e.getNewValue()).intValue();
String title = tabPane.getTitleAt(index);
if (BasicHTML.isHTMLString(title)) {
if (htmlViews == null) { // Initialize vector
htmlViews = createHTMLViewList();
}
else { // Vector already exists
View v = BasicHTML.createHTMLView(tabPane, title);
htmlViews.set(index, v);
}
}
else {
if (htmlViews != null && htmlViews.get(index) != null) {
htmlViews.set(index, null);
}
}
updateMnemonics();
}
else if ("tabLayoutPolicy".equals(name)) {
BaseTabbedPaneUI.this.uninstallUI(pane);
BaseTabbedPaneUI.this.installUI(pane);
}
else if ("background".equals(name) && isScrollLayout) {
Color newVal = (Color) e.getNewValue();
tabScroller.tabPanel.setBackground(newVal);
tabScroller.viewport.setBackground(newVal);
Color newColor = selectedColor == null ? newVal : selectedColor;
tabScroller.scrollForwardButton.setBackground(newColor);
tabScroller.scrollBackwardButton.setBackground(newColor);
}
else if ("indexForTabComponent".equals(name)) {
if (tabContainer != null) {
tabContainer.removeUnusedTabComponents();
}
try {
Component tabComponent = getTabComponentAt(((Integer) e.getNewValue()).intValue());
if (tabComponent != null) {
if (tabContainer == null) {
installTabContainer();
}
else {
addMyPropertyChangeListeners(tabComponent);
tabContainer.add(tabComponent);
}
}
}
catch (Exception ex) {
}
tabPane.revalidate();
tabPane.repaint();
}
else if ("componentOrientation".equals(name)) {
pane.revalidate();
pane.repaint();
}
else if ("tabAreaBackground".equals(name)) {
pane.revalidate();
pane.repaint();
}
}
}
public class MyTabComponentListener implements PropertyChangeListener {
public void propertyChange(PropertyChangeEvent evt) {
if ("font".equals(evt.getPropertyName()) || "text".equals(evt.getPropertyName())) {
tabPane.revalidate();
tabPane.repaint();
}
}
}
/**
* 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 BaseTabbedPaneUI.
*/
public class TabSelectionHandler implements ChangeListener {
public void stateChanged(ChangeEvent e) {
JTabbedPane tabPane = (JTabbedPane) e.getSource();
if (JTattooUtilities.getJavaVersion() >= 1.4) {
if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) {
int index = tabPane.getSelectedIndex();
if (index >= 0) {
BaseTabbedPaneUI ui = (BaseTabbedPaneUI) tabPane.getUI();
ui.tabScroller.scrollTabToVisible(tabPane.getTabPlacement(), index);
}
}
}
tabPane.revalidate();
tabPane.repaint();
}
}
public class TabComponentHandler implements ComponentListener {
public void componentResized(ComponentEvent ce) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
if (tabPane != null) {
tabPane.doLayout();
}
}
});
}
public void componentMoved(ComponentEvent ce) {
}
public void componentShown(ComponentEvent ce) {
}
public void componentHidden(ComponentEvent ce) {
}
}
/**
* 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 BaseTabbedPaneUI.
*/
public class MouseHandler extends MouseAdapter {
public void mouseClicked(MouseEvent e) {
if (scrollableTabLayoutEnabled()) {
MouseListener[] ml = tabPane.getMouseListeners();
for (int i = 0; i < ml.length; i++) {
ml[i].mouseClicked(e);
}
}
}
public void mousePressed(MouseEvent e) {
if (scrollableTabLayoutEnabled()) {
MouseListener[] ml = tabPane.getMouseListeners();
for (int i = 0; i < ml.length; i++) {
ml[i].mousePressed(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);
}
}
}
public void mouseReleased(MouseEvent e) {
if (scrollableTabLayoutEnabled()) {
MouseListener[] ml = tabPane.getMouseListeners();
for (int i = 0; i < ml.length; i++) {
ml[i].mouseReleased(e);
}
}
}
public void mouseEntered(MouseEvent e) {
if (scrollableTabLayoutEnabled()) {
MouseListener[] ml = tabPane.getMouseListeners();
for (int i = 0; i < ml.length; i++) {
ml[i].mouseEntered(e);
}
}
}
public void mouseExited(MouseEvent e) {
if (scrollableTabLayoutEnabled()) {
MouseListener[] ml = tabPane.getMouseListeners();
for (int i = 0; i < ml.length; i++) {
ml[i].mouseExited(e);
}
}
rolloverIndex = -1;
if (rolloverIndex != oldRolloverIndex) {
if ((oldRolloverIndex >= 0) && (oldRolloverIndex < tabPane.getTabCount())) {
tabPane.repaint(getTabBounds(tabPane, oldRolloverIndex));
}
if ((rolloverIndex >= 0) && (rolloverIndex < tabPane.getTabCount())) {
tabPane.repaint(getTabBounds(tabPane, rolloverIndex));
}
oldRolloverIndex = rolloverIndex;
}
}
}
/**
* 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 BaseTabbedPaneUI.
*/
public class MouseMotionHandler extends MouseMotionAdapter {
public void mouseDragged(MouseEvent e) {
if (scrollableTabLayoutEnabled()) {
MouseMotionListener[] mml = tabPane.getMouseMotionListeners();
for (int i = 0; i < mml.length; i++) {
mml[i].mouseDragged(e);
}
}
}
public void mouseMoved(MouseEvent e) {
if (scrollableTabLayoutEnabled()) {
MouseMotionListener[] mml = tabPane.getMouseMotionListeners();
for (int i = 0; i < mml.length; i++) {
mml[i].mouseMoved(e);
}
}
rolloverIndex = getTabAtLocation(e.getX(), e.getY());
if (rolloverIndex != oldRolloverIndex) {
if ((oldRolloverIndex >= 0) && (oldRolloverIndex < tabPane.getTabCount())) {
tabPane.repaint(getTabBounds(tabPane, oldRolloverIndex));
}
if ((rolloverIndex >= 0) && (rolloverIndex < tabPane.getTabCount())) {
tabPane.repaint(getTabBounds(tabPane, rolloverIndex));
}
oldRolloverIndex = rolloverIndex;
}
}
}
/**
* 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 BaseTabbedPaneUI.
*/
public class FocusHandler extends FocusAdapter {
public void focusGained(FocusEvent e) {
JTabbedPane tabPane = (JTabbedPane) e.getSource();
int tabCount = tabPane.getTabCount();
int selectedIndex = tabPane.getSelectedIndex();
if (selectedIndex != -1 && tabCount > 0 && tabCount == rects.length) {
tabPane.repaint(getTabBounds(tabPane, selectedIndex));
}
}
public void focusLost(FocusEvent e) {
JTabbedPane tabPane = (JTabbedPane) e.getSource();
int tabCount = tabPane.getTabCount();
int selectedIndex = tabPane.getSelectedIndex();
if (selectedIndex != -1 && tabCount > 0 && tabCount == rects.length) {
tabPane.repaint(getTabBounds(tabPane, selectedIndex));
}
}
}
/*
* GES 2/3/99: The container listener code was added to support HTML rendering of tab titles.
*
* Ideally, we would be able to listen for property changes when a tab is added or its text modified. At the moment there are no such events because
* the Beans spec doesn't allow 'indexed' property changes (i.e. tab 2's text changed from A to B).
*
* In order to get around this, we listen for tabs to be added or removed by listening for the container events. we then queue up a runnable (so the
* component has a chance to complete the add) which checks the tab title of the new component to see if it requires HTML rendering.
*
* The Views (one per tab title requiring HTML rendering) are stored in the htmlViews list, which is only allocated after the first time we run into
* an HTML tab. Note that this list is kept in step with the number of pages, and nulls are added for those pages whose tab title do not require
* HTML rendering.
*
* This makes it easy for the paint and layout code to tell whether to invoke the HTML engine without having to check the string during
* time-sensitive operations.
*
* When we have added a way to listen for tab additions and changes to tab text, this code should be removed and replaced by something which uses
* that.
*/
private class ContainerHandler implements ContainerListener {
public void componentAdded(ContainerEvent e) {
JTabbedPane tp = (JTabbedPane) e.getContainer();
TabbedPaneLayout layout = (TabbedPaneLayout) tp.getLayout();
layout.layoutContainer(tp);
Component child = e.getChild();
if (child instanceof UIResource) {
return;
}
int index = tp.indexOfComponent(child);
String title = tp.getTitleAt(index);
boolean isHTML = BasicHTML.isHTMLString(title);
if (isHTML) {
if (htmlViews == null) {
// Initialize vector
htmlViews = createHTMLViewList();
}
else {
// Vector already exists
View v = BasicHTML.createHTMLView(tp, title);
htmlViews.add(index, v);
}
}
else {
// Not HTML
if (htmlViews != null) {
// Add placeholder
htmlViews.add(index, null);
} // else nada!
}
}
public void componentRemoved(ContainerEvent e) {
JTabbedPane tp = (JTabbedPane) e.getContainer();
Component child = e.getChild();
if (child instanceof UIResource) {
return;
}
// NOTE 4/15/2002 (joutwate):
// This fix is implemented using client properties since there is
// currently no IndexPropertyChangeEvent. Once
// IndexPropertyChangeEvents have been added this code should be
// modified to use it.
Integer indexObj = (Integer) tp.getClientProperty("__index_to_remove__");
if (indexObj != null) {
int index = indexObj.intValue();
if (htmlViews != null && htmlViews.size() >= index) {
htmlViews.remove(index);
}
}
}
}
private ArrayList createHTMLViewList() {
ArrayList viewList = new ArrayList();
int count = tabPane.getTabCount();
for (int i = 0; i < count; i++) {
String title = tabPane.getTitleAt(i);
if (BasicHTML.isHTMLString(title)) {
viewList.add(BasicHTML.createHTMLView(tabPane, title));
}
else {
viewList.add(null);
}
}
return viewList;
}
private class TabContainer extends JPanel implements UIResource {
private boolean notifyTabbedPane = true;
public TabContainer() {
super(null);
setOpaque(false);
}
public void remove(Component comp) {
int index = tabPane.indexOfTabComponent(comp);
PropertyChangeListener[] listeners = comp.getPropertyChangeListeners();
for (int j = 0; j < listeners.length; j++) {
if (listeners[j] instanceof MyTabComponentListener) {
comp.removePropertyChangeListener(listeners[j]);
}
}
super.remove(comp);
if (notifyTabbedPane && index != -1) {
tabPane.setTabComponentAt(index, null);
}
}
private void removeUnusedTabComponents() {
for (int i = 0; i < getComponentCount(); i++) {
Component c = getComponent(i);
if (!(c instanceof UIResource)) {
int index = tabPane.indexOfTabComponent(c);
if (index == -1) {
PropertyChangeListener[] listeners = c.getPropertyChangeListeners();
for (int j = 0; j < listeners.length; j++) {
if (listeners[j] instanceof MyTabComponentListener) {
c.removePropertyChangeListener(listeners[j]);
}
}
super.remove(c);
}
}
}
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy