/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Tiny Look and Feel *
* *
* (C) Copyright 2003 - 2007 Hans Bickel *
* *
* For licensing information and credits, please refer to the *
* comment in file de.muntjak.tinylookandfeel.TinyLookAndFeel *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
package de.muntjak.tinylookandfeel;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.security.AccessController;
import java.security.PrivilegedAction;
import javax.swing.JComponent;
import javax.swing.JTabbedPane;
import javax.swing.SwingConstants;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicGraphicsUtils;
import javax.swing.plaf.basic.BasicTabbedPaneUI;
import javax.swing.text.View;
import de.muntjak.tinylookandfeel.controlpanel.ColorRoutines;
import de.muntjak.tinylookandfeel.controlpanel.DrawRoutines;
/**
* TinyTabbedPaneUI
*
* @version 1.3.04
* @author Hans Bickel
*/
public class TinyTabbedPaneUI extends BasicTabbedPaneUI {
/**
* Creates the UI delegate for the given component.
*
* @param c The component to create its UI delegate.
* @return The UI delegate for the given component.
*/
public static ComponentUI createUI(JComponent c) {
return new TinyTabbedPaneUI();
}
int rollover = -1;
protected void installListeners() {
super.installListeners();
tabPane.addMouseMotionListener(
(MouseMotionListener)mouseListener);
}
protected MouseListener createMouseListener() {
return new TinyMouseHandler();
}
protected void installDefaults() {
super.installDefaults();
}
private boolean scrollableTabLayoutEnabled() {
return (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT);
}
private void checkRollOver(int tabIndex) {
if(rollover >= tabPane.getTabCount()) {
rollover = -1;
}
if(tabIndex == rollover) return;
if(rollover != -1) { // Update old rollover
tabPane.repaint(getTabBounds(tabPane, rollover));
if(tabIndex == -1) rollover = -1;
}
if(tabIndex >= 0 && tabPane.isEnabledAt(tabIndex)) {
// Paint new rollover
rollover = tabIndex;
tabPane.repaint(getTabBounds(tabPane, tabIndex));
}
}
/**
* Overridden so we can enable/disable tab rotating using
* TinyTabbedPaneLayout.
* Invoked by <code>installUI</code> to create
* a layout manager object to manage
* the <code>JTabbedPane</code>.
*
* @return a layout manager object
*
* @see TabbedPaneLayout
* @see javax.swing.JTabbedPane#getTabLayoutPolicy
*/
protected LayoutManager createLayoutManager() {
if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) {
return super.createLayoutManager();
} else { /* WRAP_TAB_LAYOUT */
return new TinyTabbedPaneLayout();
}
}
private int getTabAtLocation(int x, int y) {
if(TinyLookAndFeel.is1dot4()) {
ensureCurrentLayout();
int tabCount = tabPane.getTabCount();
for(int i = 0; i < tabCount; i++) {
if(rects[i].contains(x, y)) {
return i;
}
}
return -1;
}
else { // JRE 1.5 or higher
return tabForCoordinate(tabPane, x, y);
}
}
private void ensureCurrentLayout() {
if(!tabPane.isValid()) {
tabPane.validate();
}
/* If tabPane doesn't have a peer yet, the validate() call will
* silently fail. We handle that by forcing a layout if tabPane
* is still invalid. See bug 4237677.
*/
if(!tabPane.isValid()) {
TabbedPaneLayout layout = (TabbedPaneLayout)tabPane.getLayout();
layout.calculateLayoutInfo();
}
}
public class TinyMouseHandler implements MouseListener, MouseMotionListener {
public void mousePressed(MouseEvent e) {
if(!tabPane.isEnabled()) return;
// 1.3.04 code - see getTabAtLocation(int, int) for
// JRE 1.5 fix
int tabIndex = getTabAtLocation(e.getX(), e.getY());
if (tabIndex >= 0 && tabPane.isEnabledAt(tabIndex)) {
if(tabIndex != tabPane.getSelectedIndex()) {
// Clicking on unselected tab, change selection, do NOT
// request focus.
// This will trigger the focusIndex to change by way
// of stateChanged.
tabPane.setSelectedIndex(tabIndex);
}
else if(tabPane.isRequestFocusEnabled()) {
// Clicking on selected tab, try and give the tabbedpane
// focus. Repaint will occur in focusGained.
tabPane.requestFocus();
}
}
}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {
if(rollover >= tabPane.getTabCount()) {
rollover = -1;
}
if(rollover != -1) {
tabPane.repaint(getTabBounds(tabPane, rollover));
rollover = -1;
}
}
public void mouseClicked(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {}
public void mouseDragged(MouseEvent e) {}
public void mouseMoved(MouseEvent e) {
if(tabPane == null) return;
if(!tabPane.isEnabled()) return;
// Note: When running JRE v1.4 there's no way to do
// tab rollovers with SCROLL_TAB_LAYOUT
if(TinyLookAndFeel.is1dot4() && scrollableTabLayoutEnabled()) return;
checkRollOver(getTabAtLocation(e.getX(), e.getY()));
}
}
/**
* Paints the backround of a given tab.
*
* @param g The graphics context.
* @param tabPlacement The placement of the tab to paint.
* @param tabIndex The index of the tab to paint.
* @param x The x coordinate of the top left corner.
* @param y The y coordinate of the top left corner.
* @param w The width.
* @param h The height.
* @param isSelected True if the tab to paint is selected otherwise false.
*/
protected void paintTabBackground(Graphics g, int tabPlacement,
int tabIndex, int x, int y, int w, int h, boolean isSelected)
{
boolean isEnabled = (tabPane.isEnabledAt(tabIndex));
if(!tabPane.isEnabled()) isEnabled = false;
if(isSelected && !Theme.ignoreSelectedBg[Theme.style]) {
if(isEnabled) {
g.setColor(Theme.tabSelectedColor[Theme.style].getColor());
}
else {
g.setColor(Theme.tabDisabledSelectedColor[Theme.style].getColor());
}
} else {
if(isEnabled) {
// because (Tiny)JTabbedPane now has a defined
// background color, this should work
g.setColor(tabPane.getBackgroundAt(tabIndex));
}
else {
g.setColor(Theme.tabDisabledColor[Theme.style].getColor());
}
}
switch (tabPlacement) {
case LEFT :
g.fillRect(x + 1, y + 1, w - 1, h - 3);
break;
case RIGHT :
x -= 2;
g.fillRect(x, y + 1, w - 1, h - 3);
break;
case BOTTOM :
y -= 2;
g.fillRect(x + 1, y, w - 3, h - 1);
break;
case TOP :
default :
g.fillRect(x + 1, y + 1, w - 3, h - 1);
}
}
/**
* Paints the border of a given tab.
*
* @param g The graphics context.
* @param tabPlacement The placement of the tab to paint.
* @param selectedIndex The index of the selected tab.
*/
protected void paintContentBorder(Graphics g, int tabPlacement, int selectedIndex) {
}
/**
* @see javax.swing.plaf.basic.BasicTabbedPaneUI#paintFocusIndicator(Graphics, int, Rectangle[], int, Rectangle, Rectangle, boolean)
*/
protected void paintFocusIndicator(Graphics g, int tabPlacement,
Rectangle[] rects, int tabIndex, Rectangle iconRect,
Rectangle textRect, boolean isSelected)
{
if(!Theme.tabFocus[Theme.style]) return;
Rectangle tabRect = rects[tabIndex];
if (tabPane.hasFocus() && isSelected) {
int x, y, w, h;
g.setColor(Theme.tabFontColor[Theme.style].getColor());
switch(tabPlacement) {
case LEFT:
x = tabRect.x + 3;
y = tabRect.y + 3;
w = tabRect.width - 5;
h = tabRect.height - 7;
break;
case RIGHT:
x = tabRect.x;
y = tabRect.y + 3;
w = tabRect.width - 5;
h = tabRect.height - 7;
break;
case BOTTOM:
x = tabRect.x + 3;
y = tabRect.y;
w = tabRect.width - 7;
h = tabRect.height - 5;
break;
case TOP:
default:
x = tabRect.x + 3;
y = tabRect.y + 3;
w = tabRect.width - 7;
h = tabRect.height - 5;
}
BasicGraphicsUtils.drawDashedRect(g, x, y, w, h);
}
}
/**
* Draws the border around each tab.
*
* @param g The graphics context.
* @param tabPlacement The placement of the tabs.
* @param tabIndex The index of the tab to paint.
* @param x The x coordinate of the top left corner.
* @param y The y coordinate of the top left corner.
* @param w The width.
* @param h The height.
* @param isSelected True if the tab to paint is selected otherwise false.
*/
protected void paintTabBorder(Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h, boolean isSelected) {
boolean isEnabled = (tabPane.isEnabledAt(tabIndex));
if(!tabPane.isEnabled()) isEnabled = false;
boolean isRollover = (rollover == tabIndex);
switch (Theme.derivedStyle[Theme.style]) {
case Theme.TINY_STYLE :
drawTinyTabBorder(g, tabPlacement, x, y, w, h, isSelected, isEnabled, isRollover);
break;
case Theme.W99_STYLE :
drawWinTabBorder(g, tabPlacement, x, y, w, h, isSelected, isEnabled, isRollover);
break;
case Theme.YQ_STYLE :
drawXpTabBorder(g, tabPlacement, x, y, w, h, isSelected, isEnabled, isRollover);
break;
}
}
private void drawTinyTabBorder(Graphics g, int tabPlacement, int x, int y, int w, int h, boolean isSelected, boolean isEnabled, boolean isRollover) {
}
private void drawWinTabBorder(Graphics g, int tabPlacement, int x, int y, int w, int h, boolean isSelected, boolean isEnabled, boolean isRollover) {
g.setColor(Theme.tabLightColor[Theme.style].getColor());
switch (tabPlacement) {
case SwingConstants.LEFT :
g.drawLine(x + 2, y, x + w - 1, y);
g.drawLine(x + 1, y + 1, x + 1, y + 1);
g.drawLine(x, y + 2, x, y + h - 3);
g.setColor(Theme.tabDarkColor[Theme.style].getColor());
g.drawLine(x + 2, y + h - 2, x + w - 1, y + h - 2);
g.setColor(Theme.tabBorderColor[Theme.style].getColor());
g.drawLine(x + 2, y + h - 1, x + w - 1, y + h - 1);
g.drawLine(x + 1, y + h - 2, x + 1, y + h - 2);
break;
case SwingConstants.RIGHT :
g.drawLine(x + w - 3, y, x, y);
g.drawLine(x + w - 2, y + 1, x + w - 2, y + 1);
g.drawLine(x + w - 1, y + 2, x + w - 1, y + h - 3);
g.setColor(Theme.tabDarkColor[Theme.style].getColor());
g.drawLine(x + w - 3, y + h - 2, x, y + h - 2);
g.setColor(Theme.tabBorderColor[Theme.style].getColor());
g.drawLine(x + w - 3, y + h - 1, x, y + h - 1);
g.drawLine(x + w - 2, y + h - 2, x + w - 2, y + h - 2);
break;
case SwingConstants.BOTTOM :
g.drawLine(x + 2, y + h - 1, x + w - 3, y + h - 1);
g.drawLine(x, y + h - 3, x, y);
g.drawLine(x + 1, y + h - 2, x + 1, y + h - 2);
g.setColor(Theme.tabDarkColor[Theme.style].getColor());
g.drawLine(x + w - 2, y + h - 3, x + w - 2, y);
g.setColor(Theme.tabBorderColor[Theme.style].getColor());
g.drawLine(x + w - 1, y + h - 3, x + w - 1, y);
g.drawLine(x + w - 2, y + h - 2, x + w - 2, y + h - 2);
break;
case SwingConstants.TOP :
default :
g.drawLine(x + 2, y, x + w - 3, y);
g.drawLine(x, y + 2, x, y + h - 1);
g.drawLine(x + 1, y + 1, x + 1, y + 1);
g.setColor(Theme.tabDarkColor[Theme.style].getColor());
g.drawLine(x + w - 2, y + 2, x + w - 2, y + h - 1);
g.setColor(Theme.tabBorderColor[Theme.style].getColor());
g.drawLine(x + w - 1, y + 2, x + w - 1, y + h - 1);
g.drawLine(x + w - 2, y + 1, x + w - 2, y + 1);
}
}
private void drawXpTabBorder(Graphics g, int tabPlacement, int x, int y, int w, int h,
boolean isSelected, boolean isEnabled, boolean isRollover)
{
if(!isEnabled) {
DrawRoutines.drawXpTabBorder(g,
Theme.tabBorderColor[Theme.style].getColor(), x, y, w, h, tabPlacement);
} else if(isSelected) {
DrawRoutines.drawSelectedXpTabBorder(g,
Theme.tabBorderColor[Theme.style].getColor(), x, y, w, h, tabPlacement);
} else if(isRollover && Theme.tabRollover[Theme.style]) {
DrawRoutines.drawSelectedXpTabBorder(g,
Theme.tabBorderColor[Theme.style].getColor(), x, y, w, h, tabPlacement);
} else {
DrawRoutines.drawXpTabBorder(g,
Theme.tabBorderColor[Theme.style].getColor(), x, y, w, h, tabPlacement);
}
}
/**
* Paint the border and then call paint().
*/
public void update(Graphics g, JComponent c) {
Insets insets = tabPane.getInsets();
int x = insets.left;
int y = insets.top;
int w = tabPane.getWidth() - insets.right - insets.left;
int h = tabPane.getHeight() - insets.top - insets.bottom;
if(c.isOpaque()) {
g.setColor(Theme.backColor[Theme.style].getColor());
g.fillRect(0, 0, c.getWidth(), c.getHeight());
}
int tabPlacement = tabPane.getTabPlacement();
switch (tabPlacement) {
case LEFT :
x += calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth);
w -= (x - insets.left);
break;
case RIGHT :
w -= calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth);
break;
case BOTTOM :
h -= calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight);
break;
case TOP :
default :
y += calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight);
h -= (y - insets.top);
}
switch (Theme.derivedStyle[Theme.style]) {
case Theme.TINY_STYLE :
drawTinyContentBorder(g, x, y, w, h);
break;
case Theme.W99_STYLE :
drawWinContentBorder(g, x, y, w, h);
break;
case Theme.YQ_STYLE :
drawXpContentBorder(g, x, y, w, h);
break;
}
super.paint(g, c);
}
private void drawTinyContentBorder(Graphics g, int x, int y, int w, int h) {
}
private void drawWinContentBorder(Graphics g, int x, int y, int w, int h) {
g.setColor(Theme.tabPaneLightColor[Theme.style].getColor());
g.drawLine(x + 1, y, x + w - 2, y); // top
g.drawLine(x, y, x, y + h - 2); // left
g.setColor(Theme.tabPaneDarkColor[Theme.style].getColor());
g.drawLine(x + 1, y + h - 2, x + w - 2, y + h - 2); // bottom
g.drawLine(x + w - 2, y + 1, x + w - 2, y + h - 2); // right
g.setColor(Theme.tabPaneBorderColor[Theme.style].getColor());
g.drawLine(x, y + h - 1, x + w - 2, y + h - 1); // bottom
g.drawLine(x + w - 1, y, x + w - 1, y + h - 1); // right
}
private void drawXpContentBorder(Graphics g, int x, int y, int w, int h) {
g.setColor(Theme.tabPaneBorderColor[Theme.style].getColor());
g.drawRect(x, y, w - 3, h - 3);
// Shadow
g.setColor(ColorRoutines.darken(Theme.backColor[Theme.style].getColor(), 15));
g.drawLine(x + w - 2, y + 1, x + w - 2, y + h - 2); // right
g.drawLine(x + 1, y + h - 2, x + w - 3, y + h - 2); // bottom
}
protected int getTabLabelShiftX(int tabPlacement, int tabIndex, boolean isSelected) {
Rectangle tabRect = rects[tabIndex];
int nudge = 0;
switch (tabPlacement) {
case LEFT :
nudge = isSelected ? -1 : 1;
break;
case RIGHT :
nudge = isSelected ? 1 : -1;
break;
case BOTTOM :
case TOP :
default :
nudge = 0;
}
return nudge;
}
protected int getTabLabelShiftY(int tabPlacement, int tabIndex, boolean isSelected) {
Rectangle tabRect = rects[tabIndex];
int nudge = 0;
switch (tabPlacement) {
case BOTTOM :
nudge = isSelected ? 1 : -1;
break;
case LEFT :
case RIGHT :
nudge = tabRect.height % 2;
break;
case TOP :
default :
nudge = isSelected ? -1 : 1;
}
return nudge;
}
protected void paintText(Graphics g, int tabPlacement, Font font, FontMetrics metrics,
int tabIndex, String title, Rectangle textRect, boolean isSelected)
{
if(Theme.derivedStyle[Theme.style] == Theme.W99_STYLE) {
super.paintText(g, tabPlacement, font, metrics, tabIndex, title, textRect, isSelected);
return;
}
g.setFont(font);
View v = getTextViewForTab(tabIndex);
if(v != null) {
// html
v.paint(g, textRect);
}
else {
// plain text
int mnemIndex = tabPane.getDisplayedMnemonicIndexAt(tabIndex);
if(tabPane.isEnabled() && tabPane.isEnabledAt(tabIndex)) {
g.setColor(tabPane.getForegroundAt(tabIndex));
}
else { // tab disabled
g.setColor(Theme.tabDisabledTextColor[Theme.style].getColor());
}
BasicGraphicsUtils.drawStringUnderlineCharAt(
g, title, mnemIndex, textRect.x, textRect.y + metrics.getAscent());
}
}
protected class TinyTabbedPaneLayout extends TabbedPaneLayout {
protected void rotateTabRuns(int tabPlacement, int selectedRun) {
if(!Theme.fixedTabs[Theme.style]) {
super.rotateTabRuns(tabPlacement, selectedRun);
}
}
}
}