All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.jgoodies.looks.windows.WindowsTabbedPaneUI Maven / Gradle / Ivy

Go to download

The JGoodies Looks make your Swing applications and applets look better. They have been optimized for readability, precise micro-design and usability. And they simplify the multi-platform support by using similar widget dimensions. In addition, many people have reviewed them as elegant.

The newest version!
/*
 * Copyright (c) 2001-2014 JGoodies Software GmbH. All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  o Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  o 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.
 *
 *  o Neither the name of JGoodies Software GmbH 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 THE COPYRIGHT OWNER OR
 * CONTRIBUTORS 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 com.jgoodies.looks.windows;

import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Rectangle;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicGraphicsUtils;
import javax.swing.plaf.basic.BasicTabbedPaneUI;
import javax.swing.text.View;

import com.jgoodies.common.base.SystemUtils;
import com.jgoodies.looks.Options;

/**
 * The JGoodies Windows L&F implementation of {@code TabbedPaneUI}.

* * The flat appearance is work in progress; currently it works only * for a single line of tabs and paints distored tabs for multiple lines. * * @author Karsten Lentzsch * @version $Revision: 1.11 $ */ public final class WindowsTabbedPaneUI extends com.sun.java.swing.plaf.windows.WindowsTabbedPaneUI { /** Insets used for the embedded style content border. */ private static final Insets EMPTY_INSETS = new Insets(0, 0, 0, 0); /** Insets used if we paint no content border. */ private static final int INSET = SystemUtils.IS_LAF_WINDOWS_XP_ENABLED ? -1 : 1; private static final Insets NO_CONTENT_BORDER_NORTH_INSETS = new Insets(INSET, 0, 0, 0); private static final Insets NO_CONTENT_BORDER_WEST_INSETS = new Insets(0, INSET, 0, 0); private static final Insets NO_CONTENT_BORDER_SOUTH_INSETS = new Insets(0, 0, INSET, 0); private static final Insets NO_CONTENT_BORDER_EAST_INSETS = new Insets(0, 0, 0, INSET); /** Insets used if we paint content border. */ private static final Insets CONTENT_BORDER_NORTH_INSETS = new Insets(0, 2, 4, 4); private static final Insets CONTENT_BORDER_WEST_INSETS = new Insets(2, 0, 4, 4); private static final Insets CONTENT_BORDER_SOUTH_INSETS = new Insets(4, 2, 0, 4); private static final Insets CONTENT_BORDER_EAST_INSETS = new Insets(2, 4, 4, 0); /** * Describes if tabs are painted with or without icons. */ private static boolean isTabIconsEnabled = Options.isTabIconsEnabled(); /** * Describes if we paint no content border or not; this is false by default. * You can disable the content border by setting the client property * Options.NO_CONTENT_BORDER_KEY to Boolean.TRUE; */ private Boolean noContentBorder; /** * Describes if we paint tabs in an embedded style that is with * less decoration; this is false by default. * You can enable the embedded tabs style by setting the client property * Options.EMBEDDED_TABS_KEY to Boolean.TRUE. */ private Boolean embeddedTabs; /** * Creates and answers the {@code WindowsTabbedPaneUI}. * * @see javax.swing.plaf.ComponentUI#createUI(JComponent) */ public static ComponentUI createUI(JComponent x) { return new WindowsTabbedPaneUI(); } /** * Installs the UI. * * @see javax.swing.plaf.ComponentUI#installUI(JComponent) */ @Override public void installUI(JComponent c) { super.installUI(c); embeddedTabs = (Boolean) c.getClientProperty(Options.EMBEDDED_TABS_KEY); noContentBorder = (Boolean) c.getClientProperty(Options.NO_CONTENT_BORDER_KEY); } /** * Checks and answers if content border will be painted. * This is controlled by the component's client property * Options.NO_CONTENT_BORDER or Options.EMBEDDED. */ private boolean hasNoContentBorder() { return hasEmbeddedTabs() || Boolean.TRUE.equals(noContentBorder); } /** * Checks and answers if tabs are painted with minimal decoration. */ private boolean hasEmbeddedTabs() { return embeddedTabs == null ? false : embeddedTabs.booleanValue(); } /** * Creates and answer a handler that listens to property changes. * Unlike the superclass BasicTabbedPane, the PlasticTabbedPaneUI * uses an extended Handler. */ @Override protected PropertyChangeListener createPropertyChangeListener() { return new MyPropertyChangeHandler(); } private void doLayout() { tabPane.revalidate(); tabPane.repaint(); } /** * Updates the embedded tabs property. This message is sent by * my PropertyChangeHandler whenever the embedded tabs property changes. */ private void embeddedTabsPropertyChanged(Boolean newValue) { embeddedTabs = newValue; doLayout(); } /** * Updates the no content border property. This message is sent * by my PropertyChangeHandler whenever the noContentBorder * property changes. */ private void noContentBorderPropertyChanged(Boolean newValue) { noContentBorder = newValue; doLayout(); } /** * Answers the icon for the tab with the specified index. * In case, we have globally switched of the use tab icons, * we answer {@code null} if and only if we have a title. */ @Override protected Icon getIconForTab(int tabIndex) { String title = tabPane.getTitleAt(tabIndex); boolean hasTitle = title != null && title.length() > 0; return !isTabIconsEnabled && hasTitle ? null : super.getIconForTab(tabIndex); } @Override protected Insets getContentBorderInsets(int tabPlacement) { if (!hasNoContentBorder()) { if (SystemUtils.IS_LAF_WINDOWS_XP_ENABLED) { switch (tabPlacement) { case RIGHT : return CONTENT_BORDER_EAST_INSETS; case LEFT : return CONTENT_BORDER_WEST_INSETS; case TOP : return CONTENT_BORDER_NORTH_INSETS; case BOTTOM : default : return CONTENT_BORDER_SOUTH_INSETS; } } return contentBorderInsets; } else if (hasEmbeddedTabs()) { return EMPTY_INSETS; } else { switch (tabPlacement) { case RIGHT : return NO_CONTENT_BORDER_EAST_INSETS; case LEFT : return NO_CONTENT_BORDER_WEST_INSETS; case TOP : return NO_CONTENT_BORDER_NORTH_INSETS; case BOTTOM : default : return NO_CONTENT_BORDER_SOUTH_INSETS; } } } @Override protected int getTabLabelShiftX(int tabPlacement, int tabIndex, boolean isSelected) { switch (tabPlacement) { case RIGHT : return isSelected ? 2 : 0; case LEFT : return isSelected ? -2 : 0; case TOP : case BOTTOM : default : return 0; } } @Override protected int getTabLabelShiftY(int tabPlacement, int tabIndex, boolean isSelected) { return 0; } @Override protected Insets getSelectedTabPadInsets(int tabPlacement) { if (hasEmbeddedTabs()) { return EMPTY_INSETS; } else if (hasNoContentBorder()) { int inset = SystemUtils.IS_LAF_WINDOWS_XP_ENABLED ? 1 : 0; switch (tabPlacement) { case LEFT: return new Insets(1, 2, 1, inset); case RIGHT: return new Insets(1, inset, 1, 2); case TOP: return new Insets(2, 2, inset, 2); case BOTTOM: return new Insets(inset, 2, 2, 2); default: return EMPTY_INSETS; } } else { Insets superInsets = super.getSelectedTabPadInsets(tabPlacement); int equalized = superInsets.left + superInsets.right / 2; superInsets.left = superInsets.right = equalized; return superInsets; } } @Override protected Insets getTabAreaInsets(int tabPlacement) { return hasEmbeddedTabs() ? /*new Insets(1,1,1,1)*/EMPTY_INSETS : super.getTabAreaInsets(tabPlacement); } /** * Paints the top edge of the pane's content border. */ @SuppressWarnings("null") @Override protected void paintContentBorderTopEdge(Graphics g, int tabPlacement, int selectedIndex, int x, int y, int w, int h) { if (hasNoContentBorder() && tabPlacement != TOP) { return; } Rectangle selRect = selectedIndex < 0 ? null : getTabBounds(selectedIndex, calcRect); if (tabPlacement != TOP || selectedIndex < 0 || selRect.y + selRect.height + 1 < y || selRect.x < x || selRect.x > x + w) { // no special case, do the super thing super.paintContentBorderTopEdge(g, tabPlacement, selectedIndex, x, y, w, h); } else { g.setColor(lightHighlight); g.fillRect(x, y, selRect.x + 1-x, 1); g.fillRect(selRect.x + selRect.width, y, x+w-2 -selRect.x-selRect.width, 1); } } /** * Paints the bottom edge of the pane's content border. */ @SuppressWarnings("null") @Override protected void paintContentBorderBottomEdge(Graphics g, int tabPlacement, int selectedIndex, int x, int y, int w, int h) { if (!hasNoContentBorder()) { Rectangle selRect = selectedIndex < 0 ? null : getTabBounds(selectedIndex, calcRect); if (tabPlacement != BOTTOM || selectedIndex < 0 || selRect.y - 1 > h + y || selRect.x < x || selRect.x > x + w) { // no special case, do the super thing super.paintContentBorderBottomEdge(g, tabPlacement, selectedIndex, x, y, w, h); } else { g.setColor(lightHighlight); g.fillRect(x,y+h-1,1,1); g.setColor(shadow); g.fillRect(x+1, y+h-2, selRect.x - 1-x, 1); g.fillRect(selRect.x + selRect.width, y+h-2, x+w-2-selRect.x-selRect.width, 1); g.setColor(darkShadow); g.fillRect(x, y+h-1, selRect.x - x, 1); g.fillRect(selRect.x + selRect.width -1, y+h-1, x+w-selRect.x-selRect.width, 1); } } else if (!(tabPlacement == BOTTOM)) { // no content border really means only one content border: // the one edge that touches the tabs } else { g.setColor(shadow); g.fillRect(x,y+h,w,1); } } /** * Paints the left Edge of the pane's content border. */ @SuppressWarnings("null") @Override protected void paintContentBorderLeftEdge(Graphics g, int tabPlacement, int selectedIndex, int x, int y, int w, int h) { if (!hasNoContentBorder()) { Rectangle selRect = selectedIndex < 0 ? null : getTabBounds(selectedIndex, calcRect); if (tabPlacement != LEFT || selectedIndex < 0 || selRect.x + selRect.width + 1 < x || selRect.y < y || selRect.y > y + h) { // no special case, do the super thing super.paintContentBorderLeftEdge(g, tabPlacement, selectedIndex, x, y, w, h); } else { g.setColor(lightHighlight); g.fillRect(x, y, 1, selRect.y + 1 - y); g.fillRect(x, selRect.y + selRect.height, 1, y+h-1-selRect.y-selRect.height); } } else if (!(tabPlacement == LEFT)) { // no content border really means only one content border: // the one edge that touches the tabs } else { g.setColor(shadow); g.fillRect(x,y,1,h); } } /** * Paints the right Edge of the pane's content border. */ @SuppressWarnings("null") @Override protected void paintContentBorderRightEdge(Graphics g, int tabPlacement, int selectedIndex, int x, int y, int w, int h) { if (!hasNoContentBorder()) { Rectangle selRect = selectedIndex < 0 ? null : getTabBounds(selectedIndex, calcRect); if (tabPlacement != RIGHT || selectedIndex < 0 || selRect.x - 1 > x+w || selRect.y < y || selRect.y > y + h) { // no special case, do the super thing super.paintContentBorderRightEdge(g, tabPlacement, selectedIndex, x, y, w, h); } else { g.setColor(lightHighlight); g.fillRect(x+w-1, y,1,1); g.setColor(shadow); g.fillRect(x+w-2, y+1, 1, selRect.y - 1-y); g.fillRect(x+w-2, selRect.y + selRect.height, 1, y+h-1-selRect.y- selRect.height); g.setColor(darkShadow); g.fillRect(x+w-1, y, 1, selRect.y - y); g.fillRect(x+w-1, selRect.y + selRect.height-1, 1, y+h-selRect.y-selRect.height); } } else if (!(tabPlacement == RIGHT)) { // no content border really means only one content border: // the one edge that touches the tabs } else { g.setColor(shadow); g.fillRect(x+w,y,1,h); } } /** * Paints the border for a single tab; it does not paint the tab's background. */ @Override protected void paintTabBorder( Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h, boolean isSelected) { if (!hasEmbeddedTabs()) { super.paintTabBorder(g, tabPlacement, tabIndex, x, y, w, h, isSelected); return; } g.translate(x - 1, y - 1); int w1, w2, w3; int h1, h2, h3; switch (tabPlacement) { case TOP : w1 = 1; w2 = w - 2; w3 = 1; h1 = 1; h2 = h - 1; h3 = 0; break; case BOTTOM : w1 = 1; w2 = w - 2; w3 = 1; h1 = 0; h2 = h - 1; h3 = 1; break; case LEFT : w1 = 1; w2 = w - 1; w3 = 0; h1 = 1; h2 = h - 3; h3 = 1; break; case RIGHT : default : w1 = 0; w2 = w - 1; w3 = 1; h1 = 1; h2 = h - 3; h3 = 1; } if (isSelected) { g.setColor(lightHighlight); g.drawRect(w1, h1, w1 + w2 + w3, h1 + h2 + h3); g.setColor(shadow); g.fillRect(1 + w1, 0, w2, h1); g.fillRect(0, 1 + h1, w1, h2); g.fillRect(2 * w1 + w2 + 2 * w3, 1 + h1, w3, h2); g.fillRect(1 + w1, 2 * h1 + h2 + 2 * h3, w2, h3); g.fillRect(1, 1, w1, h1); g.fillRect(2 * w1 + w2 + w3, 1, w3, h1); g.fillRect(1, 2 * h1 + h2 + h3, w1, h3); g.fillRect(2 * w1 + w2 + w3, 2 * h1 + h2 + h3, w3, h3); } else { g.setColor(shadow); g.fillRect(w1 + w2 + 2 * w3, h3 * h2 /2, w3, h2* 2 /3); g.fillRect(w3*w2 /2, h1 + h2 + 2 * h3, w2/2 +2, h3); } g.translate(-x + 1, -y + 1); } @Override protected void paintFocusIndicator( Graphics g, int tabPlacement, Rectangle[] rectangles, int tabIndex, Rectangle iconRect, Rectangle textRect, boolean isSelected) { if (!hasEmbeddedTabs()) { super.paintFocusIndicator(g, tabPlacement, rectangles, tabIndex, iconRect, textRect, isSelected); return; } if (tabPane.hasFocus() && isSelected) { g.setColor(focus); BasicGraphicsUtils.drawDashedRect(g, textRect.x - 2, textRect.y, textRect.width + 3, textRect.height); } } @Override protected boolean shouldRotateTabRuns(int tabPlacement) { return !hasEmbeddedTabs(); } /** * Copied here from super(super)class to avoid labels being centered on * vertical tab runs if they consist of icon and text. */ @Override 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; //fix of issue #4 View v = getTextViewForTab(tabIndex); if (v != null) { tabPane.putClientProperty("html", v); } int xNudge = getTabLabelShiftX(tabPlacement, tabIndex, isSelected); int yNudge = getTabLabelShiftY(tabPlacement, tabIndex, isSelected); if ((tabPlacement == RIGHT || tabPlacement == LEFT) && icon != null && title != null && !title.equals("")) { /* vertical tab runs look ugly if icons and text are centered */ SwingUtilities.layoutCompoundLabel( tabPane, metrics, title, icon, SwingConstants.CENTER, SwingConstants.LEFT, SwingConstants.CENTER, SwingConstants.TRAILING, tabRect, iconRect, textRect, textIconGap); xNudge += 4; } else { /* original superclass behavior */ SwingUtilities.layoutCompoundLabel( tabPane, metrics, title, icon, SwingConstants.CENTER, SwingConstants.CENTER, SwingConstants.CENTER, SwingConstants.TRAILING, tabRect, iconRect, textRect, textIconGap); } //fix of issue #4 tabPane.putClientProperty("html", null); iconRect.x += xNudge; iconRect.y += yNudge; textRect.x += xNudge; textRect.y += yNudge; } /** * Catches and handles property change events. In addition to the super * class behavior we listen to changes of the ancestor, tab placement, * and JGoodies options for content border, and embedded tabs. */ private final class MyPropertyChangeHandler extends BasicTabbedPaneUI.PropertyChangeHandler { @Override public void propertyChange(PropertyChangeEvent e) { super.propertyChange(e); String pName = e.getPropertyName(); if (null == pName) { return; } if (pName.equals(Options.EMBEDDED_TABS_KEY)) { embeddedTabsPropertyChanged((Boolean)e.getNewValue()); return; } if (pName.equals(Options.NO_CONTENT_BORDER_KEY)) { noContentBorderPropertyChanged((Boolean)e.getNewValue()); return; } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy