/*
* Copyright (c) 2011, Nikolaus Moll
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the jo-widgets.org nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL jo-widgets.org BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
package org.jowidgets.spi.impl.swing.common.widgets.base;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowEvent;
import java.awt.event.WindowFocusListener;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JTextField;
import javax.swing.JToolBar;
import javax.swing.JWindow;
import javax.swing.UIManager;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicTabbedPaneUI;
import org.jowidgets.spi.impl.swing.common.widgets.TabItemImpl.TabComponent;
public class JoWidgetsTabLookAndFeel extends BasicTabbedPaneUI {
private static final Color SELECTED_COLOR = Color.WHITE; // background color of selected tab
private static final int ICON_HEIGHT = 16;
private static final int ARC_SIZE = 2;
private static final Color ARROW_COLOR = new Color(80, 80, 80);
private int firstVisibleTab = 0;
private int lastVisibleTab = 0;
private PopupToolbar popupToolbar;
private JoWidgetsTabbedPaneLayout layout;
public JoWidgetsTabLookAndFeel() {
super();
}
@Override
protected void installDefaults() {
// set defaults before parent is querying them
tabInsets = UIManager.getInsets("TabbedPane.tabInsets");
UIManager.getDefaults().put("TabbedPane.selected", SELECTED_COLOR);
UIManager.getDefaults().put("TabbedPane.selectedTabPadInsets", tabInsets);
UIManager.getDefaults().put("TabbedPane.tabAreaInsets", new Insets(0, 0, 0, 0));
super.installDefaults();
lightHighlight = Color.gray;
// think about resetting defaults here
}
/**
* Add toolbar(s)
*/
@Override
protected void installComponents() {
if (popupToolbar == null) {
popupToolbar = new PopupToolbar();
tabPane.add(popupToolbar);
}
super.installComponents();
}
/**
* Add toolbar(s)
*/
@Override
protected void uninstallComponents() {
if (popupToolbar != null) {
tabPane.remove(popupToolbar);
popupToolbar = null;
}
super.uninstallComponents();
}
/**
* Draw round borders
*/
@Override
protected void paintTabBorder(
final Graphics g,
final int tabPlacement,
final int tabIndex,
final int x,
final int y,
final int w,
final int h,
final boolean isSelected) {
g.setColor(lightHighlight);
final boolean isAfterSelected = isAfterSelected(tabIndex);
final boolean hasLeftArc = isSelected || isFirstVisibleTab(tabIndex);
final boolean hasRightArc = isSelected;
if (tabPlacement == TOP) {
final int y1 = y;
final int y2 = y + h;
final int x1 = x;
final int x2 = x + w;
if (hasLeftArc) {
g.drawLine(x1, y1 + ARC_SIZE, x1 + ARC_SIZE, y1); // arc
g.drawLine(x1, y1 + ARC_SIZE, x1, y2); // left down
if (!isFirstVisibleTab(tabIndex)) {
g.drawLine(x1, y1, x1 + ARC_SIZE, y1); // top connector
}
}
else {
g.drawLine(x1, y1, x1 + ARC_SIZE, y1); // top connector
if (!isAfterSelected) {
g.drawLine(x1, y1, x1, y2);
}
}
if (hasRightArc) {
g.drawLine(x2 - ARC_SIZE, y1, x2, y1 + ARC_SIZE); // arc
g.drawLine(x2, y1 + ARC_SIZE, x2, y2); // right down
}
else {
if (isLastVisibleTab(tabIndex)) {
g.drawLine(x2, y1, x2, y2);
}
}
g.drawLine(x1 + ARC_SIZE, y1, x2 - ARC_SIZE, y1); // top line
g.drawLine(x2 - ARC_SIZE, y1, x2, y1); // top connector
}
else { // BOTTOM
final int y1 = y;
final int y2 = y + h - 1;
final int x1 = x;
final int x2 = x + w;
if (hasLeftArc) {
g.drawLine(x1, y2 - ARC_SIZE, x1 + ARC_SIZE, y2); // arc
g.drawLine(x1, y2 - ARC_SIZE, x1, y1); // left down
if (!isFirstVisibleTab(tabIndex)) {
g.drawLine(x1, y2, x1 + ARC_SIZE, y2); // bottom connector
}
}
else {
g.drawLine(x1, y2, x1 + ARC_SIZE, y2); // bottom connector
if (!isAfterSelected) {
g.drawLine(x1, y1, x1, y2);
}
}
if (hasRightArc) {
g.drawLine(x2 - ARC_SIZE, y2, x2, y2 - ARC_SIZE); // arc
g.drawLine(x2, y2 - ARC_SIZE, x2, y1); // right down
}
else {
if (isLastVisibleTab(tabIndex)) {
g.drawLine(x2, y1, x2, y2);
}
}
g.drawLine(x1 + ARC_SIZE, y2, x2 - ARC_SIZE, y2); // bottom line
g.drawLine(x2 - ARC_SIZE, y2, x2, y2); // bottom connector
}
}
/**
* Overridden because parent method draws a line out of the tabbed panes bounds when
* the selected tab has the x ordinate 0
*/
@Override
protected void paintContentBorderTopEdge(
final Graphics g,
final int tabPlacement,
final int selectedIndex,
final int x,
final int y,
final int w,
final int h) {
g.setColor(lightHighlight);
if (tabPlacement == BOTTOM || selectedIndex < 0) {
g.drawLine(x, y, x + w - 2, y);
}
else {
final Rectangle bounds = getTabBounds(selectedIndex, calcRect);
g.drawLine(x, y, bounds.x, y);
if (bounds.x + bounds.width < x + w - 2) {
g.drawLine(bounds.x + bounds.width, y, x + w - 2, y);
}
}
}
/**
* Overridden because parent method draws a line out of the tabbed panes bounds when
* the selected tab has the x ordinate 0
*/
@Override
protected void paintContentBorderBottomEdge(
final Graphics g,
final int tabPlacement,
final int selectedIndex,
final int x,
final int y,
final int w,
final int h) {
g.setColor(lightHighlight);
final int lineY = y + h - 2;
if (tabPlacement == TOP || selectedIndex < 0) {
g.drawLine(x, lineY, x + w - 2, lineY);
}
else {
final Rectangle bounds = getTabBounds(selectedIndex, calcRect);
g.drawLine(x, lineY, bounds.x, lineY);
if (bounds.x + bounds.width < x + w - 2) {
g.drawLine(bounds.x + bounds.width, lineY, x + w - 2, lineY);
}
}
}
/**
* Overridden to draw rounded borders and additional line if tab count is 0
*/
@Override
public void paint(final Graphics g, final JComponent c) {
super.paint(g, c);
final int tabPlacement = tabPane.getTabPlacement();
final Insets insets = tabPane.getInsets();
if (tabPlacement == TOP) {
if (tabPane.getTabCount() > 0) {
final Rectangle lastTab = rects[lastVisibleTab];
final int edgeX1 = Math.max(insets.left, lastTab.x + lastTab.width);
final int edgeX2 = (tabPane.getWidth() - 1) - insets.right - insets.left;
final int edgeY1 = lastTab.y;
final int edgeY2 = edgeY1 + lastTab.height;
g.setColor(lightHighlight);
g.drawLine(edgeX1, edgeY1, edgeX2 - ARC_SIZE, edgeY1); // top
g.drawLine(edgeX2 - ARC_SIZE, edgeY1, edgeX2, edgeY1 + ARC_SIZE); // arc
g.drawLine(edgeX2, edgeY1 + ARC_SIZE, edgeX2, edgeY2); // right
// TODO NM find a way to determine if necessary
// shadow linux
//g.setColor(darkShadow);
//g.drawLine(edgeX1 + edgeX2 - 1, edgeY1, edgeX1 + edgeX2 + 1, edgeY1 + 2);
//g.drawLine(edgeX1 + edgeX2 + 1, edgeY1 + 3, edgeX1 + edgeX2 + 1, edgeY1 + edgeY2 + 2); // right
}
else {
// empty tabpane
final int lineY = insets.top + calculateTabHeight() + 2;
final int w = tabPane.getWidth() - insets.right - insets.left;
g.setColor(lightHighlight);
g.drawLine(insets.left, lineY, w, lineY);
g.setColor(tabPane.getBackground());
final int arcY = insets.top;
g.fillRect(insets.left, arcY, w, ARC_SIZE + 1);
g.setColor(lightHighlight);
g.drawLine(insets.left + ARC_SIZE, arcY, w - 2 * ARC_SIZE, arcY);
g.drawLine(insets.left, arcY + ARC_SIZE, insets.left + ARC_SIZE, arcY);
g.drawLine(tabPane.getWidth() - insets.right - insets.left - ARC_SIZE - 1, arcY, tabPane.getWidth()
- insets.right
- insets.left
- 1, arcY + ARC_SIZE);
// shadow in top right corner
//g.setColor(darkShadow);
//g.drawLine(tabPane.getWidth() - insets.right - insets.left - ARC_SIZE, arcY, tabPane.getWidth()
// - insets.right
// - insets.left, arcY + ARC_SIZE);
}
}
else { // BOTTOM
if (tabPane.getTabCount() > 0) {
final Rectangle lastTab = rects[lastVisibleTab];
final int edgeX1 = Math.max(insets.left, lastTab.x + lastTab.width);
final int edgeX2 = (tabPane.getWidth() - 1) - insets.right - insets.left;
final int edgeY1 = lastTab.y;
final int edgeY2 = edgeY1 + lastTab.height - 1;
g.setColor(lightHighlight);
g.drawLine(edgeX1, edgeY2, edgeX2 - ARC_SIZE, edgeY2); // bottoom
g.drawLine(edgeX2 - ARC_SIZE, edgeY2, edgeX2, edgeY2 - ARC_SIZE); // arc
g.drawLine(edgeX2, edgeY2 - ARC_SIZE, edgeX2, edgeY1); // right
// TODO NM find a way to determine if necessary
}
else {
// empty tabpane
final int lineY = tabPane.getHeight() - insets.top - calculateTabHeight() - 4; // TODO NM why 4?
final int w = tabPane.getWidth() - insets.right - insets.left;
g.setColor(lightHighlight);
g.drawLine(insets.left, lineY, w, lineY);
g.setColor(tabPane.getBackground());
final int arcY = tabPane.getHeight() - insets.bottom - 2;
g.fillRect(insets.left, arcY - ARC_SIZE, w, ARC_SIZE + 1);
g.setColor(lightHighlight);
g.drawLine(insets.left + ARC_SIZE, arcY, w - 2 * ARC_SIZE, arcY);
g.drawLine(insets.left, arcY - ARC_SIZE, insets.left + ARC_SIZE, arcY);
g.drawLine(tabPane.getWidth() - insets.right - insets.left - ARC_SIZE - 1, arcY, tabPane.getWidth()
- insets.right
- insets.left
- 1, arcY - ARC_SIZE);
// shadow in bottom right corner
// g.setColor(darkShadow);
// g.drawLine(tabPane.getWidth() - insets.right - insets.left - ARC_SIZE, arcY, tabPane.getWidth()
// - insets.right
// - insets.left, arcY - ARC_SIZE);
}
}
}
/**
* Overridden to draw selected tab color
*/
@Override
protected void paintTabBackground(
final Graphics g,
final int tabPlacement,
final int tabIndex,
final int x,
final int y,
final int w,
final int h,
final boolean isSelected) {
g.setColor(getCurrentBackgroundColor(tabIndex, isSelected));
if (tabPlacement == TOP) {
g.fillRect(x + 1, y + 1, w - 1, h - 1);
}
else { // BOTTOM
g.fillRect(x + 1, y, w - 1, h - 1);
}
}
/**
* Create the overridden layout manager
*/
@Override
protected LayoutManager createLayoutManager() {
layout = new JoWidgetsTabbedPaneLayout();
return layout;
}
/**
* Overrides the vertical label shift
*/
@Override
protected int getTabLabelShiftY(final int tabPlacement, final int tabIndex, final boolean isSelected) {
if (tabPlacement == TOP) {
return 1;
}
else {
return -1;
}
}
/**
* Overridden to avoid focus decoration
*/
@Override
protected void paintFocusIndicator(
final Graphics g,
final int tabPlacement,
final Rectangle[] rects,
final int tabIndex,
final Rectangle iconRect,
final Rectangle textRect,
final boolean isSelected) {}
private Color getCurrentBackgroundColor(final int tabIndex, final boolean isSelected) {
if (isSelected) {
return SELECTED_COLOR;
}
return tabPane.getBackgroundAt(tabIndex);
}
private boolean isFirstVisibleTab(final int tabIndex) {
// this might be changed if a list of visible tabs is introduced
return tabIndex == firstVisibleTab;
}
private boolean isLastVisibleTab(final int tabIndex) {
// this might be changed if a list of visible tabs is introduced
return tabIndex == lastVisibleTab;
}
private int calculateTabHeight() {
final Insets tabInsets = getTabInsets(0, 0);
return getFontMetrics().getHeight() + tabInsets.top + tabInsets.bottom + 2;
}
@Override
protected int calculateTabHeight(final int tabPlacement, final int tabIndex, final int fontHeight) {
return Math.max(
ICON_HEIGHT + tabInsets.top + tabInsets.bottom + 2,
super.calculateTabHeight(tabPlacement, tabIndex, fontHeight));
}
private boolean isAfterSelected(final int tabIndex) {
final Rectangle boundsSelected = rects[tabPane.getSelectedIndex()];
final Rectangle boundsTab = rects[tabIndex];
return (boundsTab.x == boundsSelected.x + boundsSelected.width);
}
private class JoWidgetsTabbedPaneLayout extends BasicTabbedPaneUI.TabbedPaneLayout {
private boolean allTabsVisible = true;
private boolean isTabVisible(final int index) {
// this might be changed if visible tab list is introduced
return allTabsVisible || ((index >= firstVisibleTab) && (index <= lastVisibleTab));
}
@Override
protected void padSelectedTab(final int tabPlacement, final int selectedIndex) {
// do not pad selected tab
}
@Override
protected void calculateTabRects(final int tabPlacement, final int tabCount) {
final FontMetrics metrics = getFontMetrics();
final Dimension size = tabPane.getSize();
final Insets insets = tabPane.getInsets();
final Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
tabRunOverlay = getTabRunOverlay(tabPlacement);
selectedRun = -1;
maxTabHeight = calculateMaxTabHeight(tabPlacement);
maxTabWidth = 0;
if (tabCount == 0) {
return;
}
runCount = 1;
tabRuns[0] = 0;
final int y;
if (tabPlacement == TOP) {
y = insets.top + tabAreaInsets.top;
}
else {
y = size.height - insets.bottom - tabAreaInsets.bottom - maxTabHeight - 1;
}
int nextX = insets.left + tabAreaInsets.left;
for (int i = 0; i < tabCount; i++) {
final Rectangle rect = rects[i];
rect.x = nextX;
rect.y = y;
rect.width = calculateTabWidth(tabPlacement, i, metrics);
rect.height = maxTabHeight;
maxTabWidth = Math.max(maxTabWidth, rect.width);
nextX += rect.width;
}
ensureVisibility(tabCount, tabPane.getSelectedIndex());
}
/**
* Ensure selected tab is visible
*/
private void ensureVisibility(final int tabCount, final int selectedIndex) {
final Insets insets = tabPane.getInsets();
final Dimension size = tabPane.getSize();
final int toolbarSpace = 25;
final int availableWidth = size.width
- (insets.left + insets.right + tabAreaInsets.right + ARC_SIZE + PopupToolbar.VERTICAL_GAP)
- toolbarSpace;
allTabsVisible = true;
firstVisibleTab = 0;
lastVisibleTab = -1;
for (int i = 0; i < tabCount; i++) {
int additionalWidth = 0;
if (i == tabCount - 1) {
// when last tab, the available width is higher...
additionalWidth += toolbarSpace;
}
if (rects[i].x != 2 + insets.left && rects[i].x + rects[i].width > availableWidth + additionalWidth) {
// crop tabs
allTabsVisible = false;
break;
}
lastVisibleTab = i;
}
if (allTabsVisible) {
return;
}
if (lastVisibleTab < 0) {
// no tab is visible at all...
lastVisibleTab = selectedIndex;
firstVisibleTab = selectedIndex;
// TODO NM shrink tab or don't let the tabbed pane shrink further
}
else if (selectedIndex > lastVisibleTab) {
// shift tabs
lastVisibleTab = selectedIndex;
int currentWidth = rects[selectedIndex].x + rects[selectedIndex].width;
while ((currentWidth > availableWidth) && (firstVisibleTab <= selectedIndex)) {
currentWidth -= rects[firstVisibleTab].width;
firstVisibleTab++;
}
}
int currentX = insets.left;
for (int i = 0; i < tabCount; i++) {
if ((i < firstVisibleTab) || (i > lastVisibleTab)) {
// make invisible
rects[i].x = Integer.MAX_VALUE;
continue;
}
rects[i].x = currentX;
currentX += rects[i].width;
}
}
/**
* Overriden to fix the toolbar (button) geometry
*/
@Override
public void layoutContainer(final Container parent) {
super.layoutContainer(parent);
final Dimension itemSize = popupToolbar.getPreferredSize();
final Rectangle tabPaneBounds = tabPane.getBounds();
final Insets insets = tabPane.getInsets();
final int tabPlacement = tabPane.getTabPlacement();
itemSize.height = Math.min(itemSize.height, maxTabHeight - 2);
if (!allTabsVisible) {
popupToolbar.popupButton.updateSize();
// TODO NM list of visible rects... improve
int buttonX = insets.left + PopupToolbar.VERTICAL_GAP;
for (int r = 0; r < rects.length; r++) {
if ((rects[r].x >= 0) && (rects[r].x < Integer.MAX_VALUE)) {
buttonX += rects[r].width;
}
}
final int tabHeight = calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight);
final int buttonY;
if (tabPlacement == TOP) {
buttonY = insets.top + Math.max(1, tabHeight - itemSize.height);
}
else {
buttonY = tabPaneBounds.height - insets.bottom - tabHeight;
}
popupToolbar.setBounds(buttonX, buttonY, itemSize.width, itemSize.height);
}
popupToolbar.setVisible(!allTabsVisible);
}
}
/**
* Implement UIResource, so the toolbar is not treated as a tab
*/
private final class PopupToolbar extends JToolBar implements UIResource {
private static final int VERTICAL_GAP = 5;
private static final long serialVersionUID = -3220252938453253069L;
private final PopupButton popupButton;
private PopupToolbar() {
setFloatable(false);
setBorderPainted(false);
setMargin(new Insets(0, 0, 0, 0));
setOpaque(true);
popupButton = new PopupButton();
popupButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(final ActionEvent e) {
createPopupMenu();
}
});
this.add(popupButton);
}
/**
* Only paint the children, do not paint the toolbar itself
*/
@Override
public void paint(final Graphics g) {
paintChildren(g);
}
protected void createPopupMenu() {
final int tabCount = tabPane.getTabCount();
final JPopupMenu popupMenu = new JPopupMenu();
for (int i = 0; i < tabCount; i++) {
final JMenuItem item = new JMenuItem(tabPane.getTitleAt(i));
if (tabPane.getTabComponentAt(i) instanceof TabComponent) {
final TabComponent c = (TabComponent) tabPane.getTabComponentAt(i);
item.setIcon(c.getLabel().getIcon());
}
if (!layout.isTabVisible(i)) {
item.setFont(item.getFont().deriveFont(Font.BOLD));
}
final int index = i;
item.addActionListener(new ActionListener() {
@Override
public void actionPerformed(final ActionEvent e) {
tabPane.setSelectedIndex(index);
}
});
popupMenu.add(item);
}
popupMenu.show(popupButton, 0, popupButton.getHeight());
}
@SuppressWarnings("unused")
protected void createPopupWindow() {
final Point location = popupButton.getLocationOnScreen();
location.y = location.y + popupButton.getHeight();
final int tabCount = tabPane.getTabCount();
final JWindow popupWindow = new JWindow(getFrame(tabPane.getTopLevelAncestor()));
popupWindow.setLocation(location);
final JToolBar tabList = new JToolBar(JToolBar.VERTICAL);
tabList.setFloatable(false);
final JTextField filter = new JTextField("Filter...");
tabList.add(filter);
tabList.addSeparator();
for (int i = 0; i < tabCount; i++) {
final JButton button = new JButton(tabPane.getTitleAt(i));
if (tabPane.getTabComponentAt(i) instanceof TabComponent) {
final TabComponent c = (TabComponent) tabPane.getTabComponentAt(i);
button.setIcon(c.getLabel().getIcon());
}
if (!layout.isTabVisible(i)) {
button.setFont(button.getFont().deriveFont(Font.BOLD));
}
final int index = i;
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(final ActionEvent e) {
tabPane.setSelectedIndex(index);
popupWindow.setVisible(false);
}
});
tabList.add(button);
}
popupWindow.add(tabList);
popupWindow.addWindowFocusListener(new WindowFocusListener() {
@Override
public void windowGainedFocus(final WindowEvent e) {}
@Override
public void windowLostFocus(final WindowEvent e) {
popupWindow.setVisible(false);
}
});
popupWindow.pack();
popupWindow.toFront();
popupWindow.setVisible(true);
popupWindow.requestFocusInWindow();
}
protected Frame getFrame(final Component c) {
if (c == null) {
return null;
}
if (c instanceof Frame) {
return (Frame) c;
}
return getFrame(c.getParent());
}
}
private final class PopupButton extends JButton {
private static final long serialVersionUID = 634813498872739540L;
private final Dimension defaultSize = new Dimension(24, 20);
private final Dimension maxSize = new Dimension(defaultSize.width + 4, 20);
private final int maxTextValue = 99;
private int lastInvisibleCount;
private boolean allowBorder;
private PopupButton() {
allowBorder = false;
addMouseListener(new MouseAdapter() {
@Override
public void mouseEntered(final MouseEvent e) {
allowBorder = true;
}
@Override
public void mouseExited(final MouseEvent e) {
allowBorder = false;
}
});
setPreferredSize(defaultSize);
setMaximumSize(defaultSize);
}
private void updateSize() {
final int currentInvisibleCount = getInvisibleCount();
if (lastInvisibleCount == currentInvisibleCount) {
return;
}
lastInvisibleCount = currentInvisibleCount;
final Dimension dimension;
if (currentInvisibleCount <= maxTextValue) {
dimension = defaultSize;
}
else {
dimension = maxSize;
}
setPreferredSize(dimension);
setMaximumSize(dimension);
}
private int getInvisibleCount() {
return firstVisibleTab + (tabPane.getTabCount() - 1) - lastVisibleTab;
}
@Override
public void paint(final Graphics g) {
super.paint(g);
final Insets insets = getInsets();
final Dimension size = getSize();
final int width = size.width - insets.left - insets.right;
final int height = size.height - insets.top - insets.bottom;
g.setColor(getBackground());
if (allowBorder) {
g.fillRect(insets.left, insets.top, width, height);
}
else {
g.fillRect(0, 0, size.width, size.height);
}
g.setColor(ARROW_COLOR);
drawFilledArrow(insets.left, insets.top, g);
g.setFont(new Font("SansSerif", Font.PLAIN, 9));
g.drawString(getPopupButtonText(getInvisibleCount()), insets.left + 8, insets.top + height);
// right aligned: insets.left + width - g.getFontMetrics().stringWidth(text)
}
private String getPopupButtonText(final int count) {
if (count > maxTextValue) {
return String.valueOf(maxTextValue) + "+";
}
return String.valueOf(count);
}
@SuppressWarnings("unused")
private void drawEmptyArrow(final int x, final int y, final Graphics g) {
g.drawLine(x, y, x, y + 7);
g.drawLine(x + 1, y, x + 4, y + 3);
g.drawLine(x + 1, y + 7, x + 4, y + 4);
}
private void drawFilledArrow(final int x, final int y, final Graphics g) {
for (int i = 0; i < 4; i++) {
g.drawLine(x + i, y + i, x + i, y + 7 - i);
}
}
@SuppressWarnings("unused")
private void drawDoubleArrow(final int x, final int y, final Graphics g) {
drawSimpleArrow(x, y, g);
drawSimpleArrow(x + 3, y, g);
}
private void drawSimpleArrow(final int x, final int y, final Graphics g) {
g.drawLine(x, y, x + 2, y + 2);
g.drawLine(x + 1, y, x + 3, y + 2);
g.drawLine(x, y + 4, x + 2, y + 2);
g.drawLine(x + 1, y + 4, x + 3, y + 2);
}
}
}