Java tutorial
/* * Copyright (c) 1998, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package javax.swing.plaf.metal; import javax.swing.*; import javax.swing.event.*; import java.awt.*; import java.awt.event.*; import javax.swing.plaf.*; import java.io.Serializable; import javax.swing.plaf.basic.BasicTabbedPaneUI; /** * The Metal subclass of BasicTabbedPaneUI. * <p> * <strong>Warning:</strong> * Serialized objects of this class will not be compatible with * future Swing releases. The current serialization support is * appropriate for short term storage or RMI between applications running * the same version of Swing. As of 1.4, support for long term storage * of all JavaBeans™ * has been added to the <code>java.beans</code> package. * Please see {@link java.beans.XMLEncoder}. * * @author Tom Santos */ @SuppressWarnings("serial") // Same-version serialization only public class MetalTabbedPaneUI extends BasicTabbedPaneUI { /** * The minimum width of a pane. */ protected int minTabWidth = 40; // Background color for unselected tabs that don't have an explicitly // set color. private Color unselectedBackground; /** * The color of tab's background. */ protected Color tabAreaBackground; /** * The color of the selected pane. */ protected Color selectColor; /** * The color of the highlight. */ protected Color selectHighlight; private boolean tabsOpaque = true; // Whether or not we're using ocean. This is cached as it is used // extensively during painting. private boolean ocean; // Selected border color for ocean. private Color oceanSelectedBorderColor; /** * Constructs {@code MetalTabbedPaneUI}. * * @param x a component * @return an instance of {@code MetalTabbedPaneUI} */ public static ComponentUI createUI(JComponent x) { return new MetalTabbedPaneUI(); } protected LayoutManager createLayoutManager() { if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) { return super.createLayoutManager(); } return new TabbedPaneLayout(); } protected void installDefaults() { super.installDefaults(); tabAreaBackground = UIManager.getColor("TabbedPane.tabAreaBackground"); selectColor = UIManager.getColor("TabbedPane.selected"); selectHighlight = UIManager.getColor("TabbedPane.selectHighlight"); tabsOpaque = UIManager.getBoolean("TabbedPane.tabsOpaque"); unselectedBackground = UIManager.getColor("TabbedPane.unselectedBackground"); ocean = MetalLookAndFeel.usingOcean(); if (ocean) { oceanSelectedBorderColor = UIManager.getColor("TabbedPane.borderHightlightColor"); } } protected void paintTabBorder(Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h, boolean isSelected) { int bottom = y + (h - 1); int right = x + (w - 1); switch (tabPlacement) { case LEFT: paintLeftTabBorder(tabIndex, g, x, y, w, h, bottom, right, isSelected); break; case BOTTOM: paintBottomTabBorder(tabIndex, g, x, y, w, h, bottom, right, isSelected); break; case RIGHT: paintRightTabBorder(tabIndex, g, x, y, w, h, bottom, right, isSelected); break; case TOP: default: paintTopTabBorder(tabIndex, g, x, y, w, h, bottom, right, isSelected); } } /** * Paints the top tab border. * * @param tabIndex a tab index * @param g an instance of {@code Graphics} * @param x an X coordinate * @param y an Y coordinate * @param w a width * @param h a height * @param btm bottom * @param rght right * @param isSelected a selection */ protected void paintTopTabBorder(int tabIndex, Graphics g, int x, int y, int w, int h, int btm, int rght, boolean isSelected) { int currentRun = getRunForTab(tabPane.getTabCount(), tabIndex); int lastIndex = lastTabInRun(tabPane.getTabCount(), currentRun); int firstIndex = tabRuns[currentRun]; boolean leftToRight = MetalUtils.isLeftToRight(tabPane); int selectedIndex = tabPane.getSelectedIndex(); int bottom = h - 1; int right = w - 1; // // Paint Gap // if (shouldFillGap(currentRun, tabIndex, x, y)) { g.translate(x, y); if (leftToRight) { g.setColor(getColorForGap(currentRun, x, y + 1)); g.fillRect(1, 0, 5, 3); g.fillRect(1, 3, 2, 2); } else { g.setColor(getColorForGap(currentRun, x + w - 1, y + 1)); g.fillRect(right - 5, 0, 5, 3); g.fillRect(right - 2, 3, 2, 2); } g.translate(-x, -y); } g.translate(x, y); // // Paint Border // if (ocean && isSelected) { g.setColor(oceanSelectedBorderColor); } else { g.setColor(darkShadow); } if (leftToRight) { // Paint slant g.drawLine(1, 5, 6, 0); // Paint top g.drawLine(6, 0, right, 0); // Paint right if (tabIndex == lastIndex) { // last tab in run g.drawLine(right, 1, right, bottom); } if (ocean && tabIndex - 1 == selectedIndex && currentRun == getRunForTab(tabPane.getTabCount(), selectedIndex)) { g.setColor(oceanSelectedBorderColor); } // Paint left if (tabIndex != tabRuns[runCount - 1]) { // not the first tab in the last run if (ocean && isSelected) { g.drawLine(0, 6, 0, bottom); g.setColor(darkShadow); g.drawLine(0, 0, 0, 5); } else { g.drawLine(0, 0, 0, bottom); } } else { // the first tab in the last run g.drawLine(0, 6, 0, bottom); } } else { // Paint slant g.drawLine(right - 1, 5, right - 6, 0); // Paint top g.drawLine(right - 6, 0, 0, 0); // Paint left if (tabIndex == lastIndex) { // last tab in run g.drawLine(0, 1, 0, bottom); } // Paint right if (ocean && tabIndex - 1 == selectedIndex && currentRun == getRunForTab(tabPane.getTabCount(), selectedIndex)) { g.setColor(oceanSelectedBorderColor); g.drawLine(right, 0, right, bottom); } else if (ocean && isSelected) { g.drawLine(right, 6, right, bottom); if (tabIndex != 0) { g.setColor(darkShadow); g.drawLine(right, 0, right, 5); } } else { if (tabIndex != tabRuns[runCount - 1]) { // not the first tab in the last run g.drawLine(right, 0, right, bottom); } else { // the first tab in the last run g.drawLine(right, 6, right, bottom); } } } // // Paint Highlight // g.setColor(isSelected ? selectHighlight : highlight); if (leftToRight) { // Paint slant g.drawLine(1, 6, 6, 1); // Paint top g.drawLine(6, 1, (tabIndex == lastIndex) ? right - 1 : right, 1); // Paint left g.drawLine(1, 6, 1, bottom); // paint highlight in the gap on tab behind this one // on the left end (where they all line up) if (tabIndex == firstIndex && tabIndex != tabRuns[runCount - 1]) { // first tab in run but not first tab in last run if (tabPane.getSelectedIndex() == tabRuns[currentRun + 1]) { // tab in front of selected tab g.setColor(selectHighlight); } else { // tab in front of normal tab g.setColor(highlight); } g.drawLine(1, 0, 1, 4); } } else { // Paint slant g.drawLine(right - 1, 6, right - 6, 1); // Paint top g.drawLine(right - 6, 1, 1, 1); // Paint left if (tabIndex == lastIndex) { // last tab in run g.drawLine(1, 1, 1, bottom); } else { g.drawLine(0, 1, 0, bottom); } } g.translate(-x, -y); } /** * Returns {@code true} if the gap should be filled. * * @param currentRun the current run * @param tabIndex the tab index * @param x an X coordinate * @param y an Y coordinate * @return {@code true} if the gap should be filled */ protected boolean shouldFillGap(int currentRun, int tabIndex, int x, int y) { boolean result = false; if (!tabsOpaque) { return false; } if (currentRun == runCount - 2) { // If it's the second to last row. Rectangle lastTabBounds = getTabBounds(tabPane, tabPane.getTabCount() - 1); Rectangle tabBounds = getTabBounds(tabPane, tabIndex); if (MetalUtils.isLeftToRight(tabPane)) { int lastTabRight = lastTabBounds.x + lastTabBounds.width - 1; // is the right edge of the last tab to the right // of the left edge of the current tab? if (lastTabRight > tabBounds.x + 2) { return true; } } else { int lastTabLeft = lastTabBounds.x; int currentTabRight = tabBounds.x + tabBounds.width - 1; // is the left edge of the last tab to the left // of the right edge of the current tab? if (lastTabLeft < currentTabRight - 2) { return true; } } } else { // fill in gap for all other rows except last row result = currentRun != runCount - 1; } return result; } /** * Returns the color of the gap. * * @param currentRun the current run * @param x an X coordinate * @param y an Y coordinate * @return the color of the gap */ protected Color getColorForGap(int currentRun, int x, int y) { final int shadowWidth = 4; int selectedIndex = tabPane.getSelectedIndex(); int startIndex = tabRuns[currentRun + 1]; int endIndex = lastTabInRun(tabPane.getTabCount(), currentRun + 1); int tabOverGap = -1; // Check each tab in the row that is 'on top' of this row for (int i = startIndex; i <= endIndex; ++i) { Rectangle tabBounds = getTabBounds(tabPane, i); int tabLeft = tabBounds.x; int tabRight = (tabBounds.x + tabBounds.width) - 1; // Check to see if this tab is over the gap if (MetalUtils.isLeftToRight(tabPane)) { if (tabLeft <= x && tabRight - shadowWidth > x) { return selectedIndex == i ? selectColor : getUnselectedBackgroundAt(i); } } else { if (tabLeft + shadowWidth < x && tabRight >= x) { return selectedIndex == i ? selectColor : getUnselectedBackgroundAt(i); } } } return tabPane.getBackground(); } /** * Paints the left tab border. * * @param tabIndex a tab index * @param g an instance of {@code Graphics} * @param x an X coordinate * @param y an Y coordinate * @param w a width * @param h a height * @param btm bottom * @param rght right * @param isSelected a selection */ protected void paintLeftTabBorder(int tabIndex, Graphics g, int x, int y, int w, int h, int btm, int rght, boolean isSelected) { int tabCount = tabPane.getTabCount(); int currentRun = getRunForTab(tabCount, tabIndex); int lastIndex = lastTabInRun(tabCount, currentRun); int firstIndex = tabRuns[currentRun]; g.translate(x, y); int bottom = h - 1; int right = w - 1; // // Paint part of the tab above // if (tabIndex != firstIndex && tabsOpaque) { g.setColor(tabPane.getSelectedIndex() == tabIndex - 1 ? selectColor : getUnselectedBackgroundAt(tabIndex - 1)); g.fillRect(2, 0, 4, 3); g.drawLine(2, 3, 2, 3); } // // Paint Highlight // if (ocean) { g.setColor(isSelected ? selectHighlight : MetalLookAndFeel.getWhite()); } else { g.setColor(isSelected ? selectHighlight : highlight); } // Paint slant g.drawLine(1, 6, 6, 1); // Paint left g.drawLine(1, 6, 1, bottom); // Paint top g.drawLine(6, 1, right, 1); if (tabIndex != firstIndex) { if (tabPane.getSelectedIndex() == tabIndex - 1) { g.setColor(selectHighlight); } else { g.setColor(ocean ? MetalLookAndFeel.getWhite() : highlight); } g.drawLine(1, 0, 1, 4); } // // Paint Border // if (ocean) { if (isSelected) { g.setColor(oceanSelectedBorderColor); } else { g.setColor(darkShadow); } } else { g.setColor(darkShadow); } // Paint slant g.drawLine(1, 5, 6, 0); // Paint top g.drawLine(6, 0, right, 0); // Paint bottom if (tabIndex == lastIndex) { g.drawLine(0, bottom, right, bottom); } // Paint left if (ocean) { if (tabPane.getSelectedIndex() == tabIndex - 1) { g.drawLine(0, 5, 0, bottom); g.setColor(oceanSelectedBorderColor); g.drawLine(0, 0, 0, 5); } else if (isSelected) { g.drawLine(0, 6, 0, bottom); if (tabIndex != 0) { g.setColor(darkShadow); g.drawLine(0, 0, 0, 5); } } else if (tabIndex != firstIndex) { g.drawLine(0, 0, 0, bottom); } else { g.drawLine(0, 6, 0, bottom); } } else { // metal if (tabIndex != firstIndex) { g.drawLine(0, 0, 0, bottom); } else { g.drawLine(0, 6, 0, bottom); } } g.translate(-x, -y); } /** * Paints the bottom tab border. * * @param tabIndex a tab index * @param g an instance of {@code Graphics} * @param x an X coordinate * @param y an Y coordinate * @param w a width * @param h a height * @param btm bottom * @param rght right * @param isSelected a selection */ protected void paintBottomTabBorder(int tabIndex, Graphics g, int x, int y, int w, int h, int btm, int rght, boolean isSelected) { int tabCount = tabPane.getTabCount(); int currentRun = getRunForTab(tabCount, tabIndex); int lastIndex = lastTabInRun(tabCount, currentRun); int firstIndex = tabRuns[currentRun]; boolean leftToRight = MetalUtils.isLeftToRight(tabPane); int bottom = h - 1; int right = w - 1; // // Paint Gap // if (shouldFillGap(currentRun, tabIndex, x, y)) { g.translate(x, y); if (leftToRight) { g.setColor(getColorForGap(currentRun, x, y)); g.fillRect(1, bottom - 4, 3, 5); g.fillRect(4, bottom - 1, 2, 2); } else { g.setColor(getColorForGap(currentRun, x + w - 1, y)); g.fillRect(right - 3, bottom - 3, 3, 4); g.fillRect(right - 5, bottom - 1, 2, 2); g.drawLine(right - 1, bottom - 4, right - 1, bottom - 4); } g.translate(-x, -y); } g.translate(x, y); // // Paint Border // if (ocean && isSelected) { g.setColor(oceanSelectedBorderColor); } else { g.setColor(darkShadow); } if (leftToRight) { // Paint slant g.drawLine(1, bottom - 5, 6, bottom); // Paint bottom g.drawLine(6, bottom, right, bottom); // Paint right if (tabIndex == lastIndex) { g.drawLine(right, 0, right, bottom); } // Paint left if (ocean && isSelected) { g.drawLine(0, 0, 0, bottom - 6); if ((currentRun == 0 && tabIndex != 0) || (currentRun > 0 && tabIndex != tabRuns[currentRun - 1])) { g.setColor(darkShadow); g.drawLine(0, bottom - 5, 0, bottom); } } else { if (ocean && tabIndex == tabPane.getSelectedIndex() + 1) { g.setColor(oceanSelectedBorderColor); } if (tabIndex != tabRuns[runCount - 1]) { g.drawLine(0, 0, 0, bottom); } else { g.drawLine(0, 0, 0, bottom - 6); } } } else { // Paint slant g.drawLine(right - 1, bottom - 5, right - 6, bottom); // Paint bottom g.drawLine(right - 6, bottom, 0, bottom); // Paint left if (tabIndex == lastIndex) { // last tab in run g.drawLine(0, 0, 0, bottom); } // Paint right if (ocean && tabIndex == tabPane.getSelectedIndex() + 1) { g.setColor(oceanSelectedBorderColor); g.drawLine(right, 0, right, bottom); } else if (ocean && isSelected) { g.drawLine(right, 0, right, bottom - 6); if (tabIndex != firstIndex) { g.setColor(darkShadow); g.drawLine(right, bottom - 5, right, bottom); } } else if (tabIndex != tabRuns[runCount - 1]) { // not the first tab in the last run g.drawLine(right, 0, right, bottom); } else { // the first tab in the last run g.drawLine(right, 0, right, bottom - 6); } } // // Paint Highlight // g.setColor(isSelected ? selectHighlight : highlight); if (leftToRight) { // Paint slant g.drawLine(1, bottom - 6, 6, bottom - 1); // Paint left g.drawLine(1, 0, 1, bottom - 6); // paint highlight in the gap on tab behind this one // on the left end (where they all line up) if (tabIndex == firstIndex && tabIndex != tabRuns[runCount - 1]) { // first tab in run but not first tab in last run if (tabPane.getSelectedIndex() == tabRuns[currentRun + 1]) { // tab in front of selected tab g.setColor(selectHighlight); } else { // tab in front of normal tab g.setColor(highlight); } g.drawLine(1, bottom - 4, 1, bottom); } } else { // Paint left if (tabIndex == lastIndex) { // last tab in run g.drawLine(1, 0, 1, bottom - 1); } else { g.drawLine(0, 0, 0, bottom - 1); } } g.translate(-x, -y); } /** * Paints the right tab border. * * @param tabIndex a tab index * @param g an instance of {@code Graphics} * @param x an X coordinate * @param y an Y coordinate * @param w a width * @param h a height * @param btm bottom * @param rght right * @param isSelected a selection */ protected void paintRightTabBorder(int tabIndex, Graphics g, int x, int y, int w, int h, int btm, int rght, boolean isSelected) { int tabCount = tabPane.getTabCount(); int currentRun = getRunForTab(tabCount, tabIndex); int lastIndex = lastTabInRun(tabCount, currentRun); int firstIndex = tabRuns[currentRun]; g.translate(x, y); int bottom = h - 1; int right = w - 1; // // Paint part of the tab above // if (tabIndex != firstIndex && tabsOpaque) { g.setColor(tabPane.getSelectedIndex() == tabIndex - 1 ? selectColor : getUnselectedBackgroundAt(tabIndex - 1)); g.fillRect(right - 5, 0, 5, 3); g.fillRect(right - 2, 3, 2, 2); } // // Paint Highlight // g.setColor(isSelected ? selectHighlight : highlight); // Paint slant g.drawLine(right - 6, 1, right - 1, 6); // Paint top g.drawLine(0, 1, right - 6, 1); // Paint left if (!isSelected) { g.drawLine(0, 1, 0, bottom); } // // Paint Border // if (ocean && isSelected) { g.setColor(oceanSelectedBorderColor); } else { g.setColor(darkShadow); } // Paint bottom if (tabIndex == lastIndex) { g.drawLine(0, bottom, right, bottom); } // Paint slant if (ocean && tabPane.getSelectedIndex() == tabIndex - 1) { g.setColor(oceanSelectedBorderColor); } g.drawLine(right - 6, 0, right, 6); // Paint top g.drawLine(0, 0, right - 6, 0); // Paint right if (ocean && isSelected) { g.drawLine(right, 6, right, bottom); if (tabIndex != firstIndex) { g.setColor(darkShadow); g.drawLine(right, 0, right, 5); } } else if (ocean && tabPane.getSelectedIndex() == tabIndex - 1) { g.setColor(oceanSelectedBorderColor); g.drawLine(right, 0, right, 6); g.setColor(darkShadow); g.drawLine(right, 6, right, bottom); } else if (tabIndex != firstIndex) { g.drawLine(right, 0, right, bottom); } else { g.drawLine(right, 6, right, bottom); } g.translate(-x, -y); } public void update(Graphics g, JComponent c) { if (c.isOpaque()) { g.setColor(tabAreaBackground); g.fillRect(0, 0, c.getWidth(), c.getHeight()); } paint(g, c); } protected void paintTabBackground(Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h, boolean isSelected) { int slantWidth = h / 2; if (isSelected) { g.setColor(selectColor); } else { g.setColor(getUnselectedBackgroundAt(tabIndex)); } if (MetalUtils.isLeftToRight(tabPane)) { switch (tabPlacement) { case LEFT: g.fillRect(x + 5, y + 1, w - 5, h - 1); g.fillRect(x + 2, y + 4, 3, h - 4); break; case BOTTOM: g.fillRect(x + 2, y, w - 2, h - 4); g.fillRect(x + 5, y + (h - 1) - 3, w - 5, 3); break; case RIGHT: g.fillRect(x, y + 2, w - 4, h - 2); g.fillRect(x + (w - 1) - 3, y + 5, 3, h - 5); break; case TOP: default: g.fillRect(x + 4, y + 2, (w - 1) - 3, (h - 1) - 1); g.fillRect(x + 2, y + 5, 2, h - 5); } } else { switch (tabPlacement) { case LEFT: g.fillRect(x + 5, y + 1, w - 5, h - 1); g.fillRect(x + 2, y + 4, 3, h - 4); break; case BOTTOM: g.fillRect(x, y, w - 5, h - 1); g.fillRect(x + (w - 1) - 4, y, 4, h - 5); g.fillRect(x + (w - 1) - 4, y + (h - 1) - 4, 2, 2); break; case RIGHT: g.fillRect(x + 1, y + 1, w - 5, h - 1); g.fillRect(x + (w - 1) - 3, y + 5, 3, h - 5); break; case TOP: default: g.fillRect(x, y + 2, (w - 1) - 3, (h - 1) - 1); g.fillRect(x + (w - 1) - 3, y + 5, 3, h - 3); } } } /** * Overridden to do nothing for the Java L&F. */ protected int getTabLabelShiftX(int tabPlacement, int tabIndex, boolean isSelected) { return 0; } /** * Overridden to do nothing for the Java L&F. */ protected int getTabLabelShiftY(int tabPlacement, int tabIndex, boolean isSelected) { return 0; } /** * {@inheritDoc} * * @since 1.6 */ protected int getBaselineOffset() { return 0; } public void paint(Graphics g, JComponent c) { int tabPlacement = tabPane.getTabPlacement(); Insets insets = c.getInsets(); Dimension size = c.getSize(); // Paint the background for the tab area if (tabPane.isOpaque()) { Color background = c.getBackground(); if (background instanceof UIResource && tabAreaBackground != null) { g.setColor(tabAreaBackground); } else { g.setColor(background); } switch (tabPlacement) { case LEFT: g.fillRect(insets.left, insets.top, calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth), size.height - insets.bottom - insets.top); break; case BOTTOM: int totalTabHeight = calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight); g.fillRect(insets.left, size.height - insets.bottom - totalTabHeight, size.width - insets.left - insets.right, totalTabHeight); break; case RIGHT: int totalTabWidth = calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth); g.fillRect(size.width - insets.right - totalTabWidth, insets.top, totalTabWidth, size.height - insets.top - insets.bottom); break; case TOP: default: g.fillRect(insets.left, insets.top, size.width - insets.right - insets.left, calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight)); paintHighlightBelowTab(); } } super.paint(g, c); } /** * Paints highlights below tab. */ protected void paintHighlightBelowTab() { } protected void paintFocusIndicator(Graphics g, int tabPlacement, Rectangle[] rects, int tabIndex, Rectangle iconRect, Rectangle textRect, boolean isSelected) { if (tabPane.hasFocus() && isSelected) { Rectangle tabRect = rects[tabIndex]; boolean lastInRun = isLastInRun(tabIndex); g.setColor(focus); g.translate(tabRect.x, tabRect.y); int right = tabRect.width - 1; int bottom = tabRect.height - 1; boolean leftToRight = MetalUtils.isLeftToRight(tabPane); switch (tabPlacement) { case RIGHT: g.drawLine(right - 6, 2, right - 2, 6); // slant g.drawLine(1, 2, right - 6, 2); // top g.drawLine(right - 2, 6, right - 2, bottom); // right g.drawLine(1, 2, 1, bottom); // left g.drawLine(1, bottom, right - 2, bottom); // bottom break; case BOTTOM: if (leftToRight) { g.drawLine(2, bottom - 6, 6, bottom - 2); // slant g.drawLine(6, bottom - 2, right, bottom - 2); // bottom g.drawLine(2, 0, 2, bottom - 6); // left g.drawLine(2, 0, right, 0); // top g.drawLine(right, 0, right, bottom - 2); // right } else { g.drawLine(right - 2, bottom - 6, right - 6, bottom - 2); // slant g.drawLine(right - 2, 0, right - 2, bottom - 6); // right if (lastInRun) { // last tab in run g.drawLine(2, bottom - 2, right - 6, bottom - 2); // bottom g.drawLine(2, 0, right - 2, 0); // top g.drawLine(2, 0, 2, bottom - 2); // left } else { g.drawLine(1, bottom - 2, right - 6, bottom - 2); // bottom g.drawLine(1, 0, right - 2, 0); // top g.drawLine(1, 0, 1, bottom - 2); // left } } break; case LEFT: g.drawLine(2, 6, 6, 2); // slant g.drawLine(2, 6, 2, bottom - 1); // left g.drawLine(6, 2, right, 2); // top g.drawLine(right, 2, right, bottom - 1); // right g.drawLine(2, bottom - 1, right, bottom - 1); // bottom break; case TOP: default: if (leftToRight) { g.drawLine(2, 6, 6, 2); // slant g.drawLine(2, 6, 2, bottom - 1); // left g.drawLine(6, 2, right, 2); // top g.drawLine(right, 2, right, bottom - 1); // right g.drawLine(2, bottom - 1, right, bottom - 1); // bottom } else { g.drawLine(right - 2, 6, right - 6, 2); // slant g.drawLine(right - 2, 6, right - 2, bottom - 1); // right if (lastInRun) { // last tab in run g.drawLine(right - 6, 2, 2, 2); // top g.drawLine(2, 2, 2, bottom - 1); // left g.drawLine(right - 2, bottom - 1, 2, bottom - 1); // bottom } else { g.drawLine(right - 6, 2, 1, 2); // top g.drawLine(1, 2, 1, bottom - 1); // left g.drawLine(right - 2, bottom - 1, 1, bottom - 1); // bottom } } } g.translate(-tabRect.x, -tabRect.y); } } protected void paintContentBorderTopEdge(Graphics g, int tabPlacement, int selectedIndex, int x, int y, int w, int h) { boolean leftToRight = MetalUtils.isLeftToRight(tabPane); int right = x + w - 1; Rectangle selRect = selectedIndex < 0 ? null : getTabBounds(selectedIndex, calcRect); if (ocean) { g.setColor(oceanSelectedBorderColor); } else { g.setColor(selectHighlight); } // Draw unbroken line if tabs are not on TOP, OR // selected tab is not in run adjacent to content, OR // selected tab is not visible (SCROLL_TAB_LAYOUT) // if (tabPlacement != TOP || selectedIndex < 0 || (selRect.y + selRect.height + 1 < y) || (selRect.x < x || selRect.x > x + w)) { g.drawLine(x, y, x + w - 2, y); if (ocean && tabPlacement == TOP) { g.setColor(MetalLookAndFeel.getWhite()); g.drawLine(x, y + 1, x + w - 2, y + 1); } } else { // Break line to show visual connection to selected tab boolean lastInRun = isLastInRun(selectedIndex); if (leftToRight || lastInRun) { g.drawLine(x, y, selRect.x + 1, y); } else { g.drawLine(x, y, selRect.x, y); } if (selRect.x + selRect.width < right - 1) { if (leftToRight && !lastInRun) { g.drawLine(selRect.x + selRect.width, y, right - 1, y); } else { g.drawLine(selRect.x + selRect.width - 1, y, right - 1, y); } } else { g.setColor(shadow); g.drawLine(x + w - 2, y, x + w - 2, y); } if (ocean) { g.setColor(MetalLookAndFeel.getWhite()); if (leftToRight || lastInRun) { g.drawLine(x, y + 1, selRect.x + 1, y + 1); } else { g.drawLine(x, y + 1, selRect.x, y + 1); } if (selRect.x + selRect.width < right - 1) { if (leftToRight && !lastInRun) { g.drawLine(selRect.x + selRect.width, y + 1, right - 1, y + 1); } else { g.drawLine(selRect.x + selRect.width - 1, y + 1, right - 1, y + 1); } } else { g.setColor(shadow); g.drawLine(x + w - 2, y + 1, x + w - 2, y + 1); } } } } protected void paintContentBorderBottomEdge(Graphics g, int tabPlacement, int selectedIndex, int x, int y, int w, int h) { boolean leftToRight = MetalUtils.isLeftToRight(tabPane); int bottom = y + h - 1; int right = x + w - 1; Rectangle selRect = selectedIndex < 0 ? null : getTabBounds(selectedIndex, calcRect); g.setColor(darkShadow); // Draw unbroken line if tabs are not on BOTTOM, OR // selected tab is not in run adjacent to content, OR // selected tab is not visible (SCROLL_TAB_LAYOUT) // if (tabPlacement != BOTTOM || selectedIndex < 0 || (selRect.y - 1 > h) || (selRect.x < x || selRect.x > x + w)) { if (ocean && tabPlacement == BOTTOM) { g.setColor(oceanSelectedBorderColor); } g.drawLine(x, y + h - 1, x + w - 1, y + h - 1); } else { // Break line to show visual connection to selected tab boolean lastInRun = isLastInRun(selectedIndex); if (ocean) { g.setColor(oceanSelectedBorderColor); } if (leftToRight || lastInRun) { g.drawLine(x, bottom, selRect.x, bottom); } else { g.drawLine(x, bottom, selRect.x - 1, bottom); } if (selRect.x + selRect.width < x + w - 2) { if (leftToRight && !lastInRun) { g.drawLine(selRect.x + selRect.width, bottom, right, bottom); } else { g.drawLine(selRect.x + selRect.width - 1, bottom, right, bottom); } } } } protected void paintContentBorderLeftEdge(Graphics g, int tabPlacement, int selectedIndex, int x, int y, int w, int h) { Rectangle selRect = selectedIndex < 0 ? null : getTabBounds(selectedIndex, calcRect); if (ocean) { g.setColor(oceanSelectedBorderColor); } else { g.setColor(selectHighlight); } // Draw unbroken line if tabs are not on LEFT, OR // selected tab is not in run adjacent to content, OR // selected tab is not visible (SCROLL_TAB_LAYOUT) // if (tabPlacement != LEFT || selectedIndex < 0 || (selRect.x + selRect.width + 1 < x) || (selRect.y < y || selRect.y > y + h)) { g.drawLine(x, y + 1, x, y + h - 2); if (ocean && tabPlacement == LEFT) { g.setColor(MetalLookAndFeel.getWhite()); g.drawLine(x + 1, y, x + 1, y + h - 2); } } else { // Break line to show visual connection to selected tab g.drawLine(x, y, x, selRect.y + 1); if (selRect.y + selRect.height < y + h - 2) { g.drawLine(x, selRect.y + selRect.height + 1, x, y + h + 2); } if (ocean) { g.setColor(MetalLookAndFeel.getWhite()); g.drawLine(x + 1, y + 1, x + 1, selRect.y + 1); if (selRect.y + selRect.height < y + h - 2) { g.drawLine(x + 1, selRect.y + selRect.height + 1, x + 1, y + h + 2); } } } } protected void paintContentBorderRightEdge(Graphics g, int tabPlacement, int selectedIndex, int x, int y, int w, int h) { Rectangle selRect = selectedIndex < 0 ? null : getTabBounds(selectedIndex, calcRect); g.setColor(darkShadow); // Draw unbroken line if tabs are not on RIGHT, OR // selected tab is not in run adjacent to content, OR // selected tab is not visible (SCROLL_TAB_LAYOUT) // if (tabPlacement != RIGHT || selectedIndex < 0 || (selRect.x - 1 > w) || (selRect.y < y || selRect.y > y + h)) { if (ocean && tabPlacement == RIGHT) { g.setColor(oceanSelectedBorderColor); } g.drawLine(x + w - 1, y, x + w - 1, y + h - 1); } else { // Break line to show visual connection to selected tab if (ocean) { g.setColor(oceanSelectedBorderColor); } g.drawLine(x + w - 1, y, x + w - 1, selRect.y); if (selRect.y + selRect.height < y + h - 2) { g.drawLine(x + w - 1, selRect.y + selRect.height, x + w - 1, y + h - 2); } } } protected int calculateMaxTabHeight(int tabPlacement) { FontMetrics metrics = getFontMetrics(); int height = metrics.getHeight(); boolean tallerIcons = false; for (int i = 0; i < tabPane.getTabCount(); ++i) { Icon icon = tabPane.getIconAt(i); if (icon != null) { if (icon.getIconHeight() > height) { tallerIcons = true; break; } } } return super.calculateMaxTabHeight(tabPlacement) - (tallerIcons ? (tabInsets.top + tabInsets.bottom) : 0); } protected int getTabRunOverlay(int tabPlacement) { // Tab runs laid out vertically should overlap // at least as much as the largest slant if (tabPlacement == LEFT || tabPlacement == RIGHT) { int maxTabHeight = calculateMaxTabHeight(tabPlacement); return maxTabHeight / 2; } return 0; } /** * Returns {@code true} if tab runs should be rotated. * * @param tabPlacement a tab placement * @param selectedRun a selected run * @return {@code true} if tab runs should be rotated. */ protected boolean shouldRotateTabRuns(int tabPlacement, int selectedRun) { return false; } // Don't pad last run protected boolean shouldPadTabRun(int tabPlacement, int run) { return runCount > 1 && run < runCount - 1; } private boolean isLastInRun(int tabIndex) { int run = getRunForTab(tabPane.getTabCount(), tabIndex); int lastIndex = lastTabInRun(tabPane.getTabCount(), run); return tabIndex == lastIndex; } /** * Returns the color to use for the specified tab. */ private Color getUnselectedBackgroundAt(int index) { Color color = tabPane.getBackgroundAt(index); if (color instanceof UIResource) { if (unselectedBackground != null) { return unselectedBackground; } } return color; } /** * Returns the tab index of JTabbedPane the mouse is currently over */ int getRolloverTabIndex() { return getRolloverTab(); } /** * This class should be treated as a "protected" inner class. * Instantiate it only within subclasses of {@code MetalTabbedPaneUI}. */ public class TabbedPaneLayout extends BasicTabbedPaneUI.TabbedPaneLayout { /** * Constructs {@code TabbedPaneLayout}. */ public TabbedPaneLayout() { MetalTabbedPaneUI.this.super(); } 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) { super.normalizeTabRuns(tabPlacement, tabCount, start, max); } } // Don't rotate runs! protected void rotateTabRuns(int tabPlacement, int selectedRun) { } // Don't pad selected tab protected void padSelectedTab(int tabPlacement, int selectedIndex) { } } }