0001:/*
0002: * Copyright (c) 2005-2008 Substance Kirill Grouchnikov. All Rights Reserved.
0003: *
0004: * Redistribution and use in source and binary forms, with or without
0005: * modification, are permitted provided that the following conditions are met:
0006: *
0007: * o Redistributions of source code must retain the above copyright notice,
0008: * this list of conditions and the following disclaimer.
0009: *
0010: * o Redistributions in binary form must reproduce the above copyright notice,
0011: * this list of conditions and the following disclaimer in the documentation
0012: * and/or other materials provided with the distribution.
0013: *
0014: * o Neither the name of Substance Kirill Grouchnikov nor the names of
0015: * its contributors may be used to endorse or promote products derived
0016: * from this software without specific prior written permission.
0017: *
0018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
0020: * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
0021: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
0022: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
0023: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
0024: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
0025: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
0026: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
0027: * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
0028: * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0029: */
0030:package org.jvnet.substance;
0031:
0032:import java.awt.*;
0033:import java.awt.event.*;
0034:import java.awt.geom.*;
0035:import java.awt.image.BufferedImage;
0036:import java.beans.PropertyChangeEvent;
0037:import java.beans.PropertyChangeListener;
0038:import java.util.*;
0039:import java.util.List;
0040:
0041:import javax.swing.*;
0042:import javax.swing.event.ChangeEvent;
0043:import javax.swing.event.ChangeListener;
0044:import javax.swing.plaf.ComponentUI;
0045:import javax.swing.plaf.UIResource;
0046:import javax.swing.plaf.basic.BasicTabbedPaneUI;
0047:import javax.swing.text.View;
0048:
0049:import org.jvnet.lafwidget.animation.*;
0050:import org.jvnet.lafwidget.layout.TransitionLayout;
0051:import org.jvnet.lafwidget.utils.LafConstants;
0052:import org.jvnet.substance.border.SubstanceBorderPainter;
0053:import org.jvnet.substance.button.*;
0054:import org.jvnet.substance.color.ColorScheme;
0055:import org.jvnet.substance.painter.ControlBackgroundComposite;
0056:import org.jvnet.substance.painter.SubstanceGradientPainter;
0057:import org.jvnet.substance.painter.text.SubstanceTextPainter;
0058:import org.jvnet.substance.scroll.SubstanceScrollButton;
0059:import org.jvnet.substance.tabbed.*;
0060:import org.jvnet.substance.theme.SubstanceTheme;
0061:import org.jvnet.substance.utils.*;
0062:import org.jvnet.substance.utils.SubstanceConstants.*;
0063:import org.jvnet.substance.utils.icon.RotatableIcon;
0064:import org.jvnet.substance.utils.icon.TransitionAwareIcon;
0065:
0066:/**
0067: * UI for tabbed panes in <b>Substance</b> look and feel.
0068: *
0069: * @author Kirill Grouchnikov
0070: */
0071:public class SubstanceTabbedPaneUI extends BasicTabbedPaneUI {
0072: /**
0073: * Current mouse location.
0074: */
0075: protected Point substanceMouseLocation;
0076:
0077: /**
0078: * Hash map for storing already computed backgrounds.
0079: */
0080: private static Map<String, BufferedImage> backgroundMap = new SoftHashMap<String, BufferedImage>();
0081:
0082: /**
0083: * Hash map for storing already computed backgrounds.
0084: */
0085: private static Map<String, BufferedImage> closeButtonMap = new SoftHashMap<String, BufferedImage>();
0086:
0087: /**
0088: * Key - tab component. Value - ID of the (looping) fade transition that
0089: * animates the tab component when it's marked as modified (with
0090: * {@link SubstanceLookAndFeel#WINDOW_MODIFIED} property).
0091: */
0092: private Map<Component, Long> fadeModifiedIds;
0093:
0094: // /**
0095: // * Key - tab component. Value - ID of the (looping) fade transition that
0096: // * animates the tab component when it has icon animation (with
0097: // * {@link SubstanceLookAndFeel#TABBED_PANE_TAB_ANIMATION_KIND} property).
0098: // */
0099: // private Map<Component, Long> fadeIconIds;
0100:
0101: /**
0102: * Map of previous fade states (for state-aware theme transitions).
0103: */
0104: private Map<Integer, ComponentState> prevStateMap;
0105:
0106: /**
0107: * Map of next fade states (for state-aware theme transitions).
0108: */
0109: private Map<Integer, ComponentState> nextStateMap;
0110:
0111: /**
0112: * Resets image maps (used when setting new theme).
0113: *
0114: * @see SubstanceLookAndFeel#setCurrentTheme(String)
0115: * @see SubstanceLookAndFeel#setCurrentTheme(SubstanceTheme)
0116: */
0117: public static synchronized void reset() {
0118: SubstanceTabbedPaneUI.backgroundMap.clear();
0119: }
0120:
0121: /*
0122: * (non-Javadoc)
0123: *
0124: * @see javax.swing.plaf.ComponentUI#createUI(javax.swing.JComponent)
0125: */
0126: public static ComponentUI createUI(JComponent tabPane) {
0127: SubstanceTabbedPaneUI result = new SubstanceTabbedPaneUI();
0128: // Object proxy = UIDelegateProxy.newInstance(result);
0129: // SubstanceTabbedPaneUI sProxy = (SubstanceTabbedPaneUI)proxy;
0130: return result;
0131: }
0132:
0133: /**
0134: * Mouse handler for rollover effects.
0135: */
0136: protected MouseRolloverHandler substanceRolloverHandler;
0137:
0138: /**
0139: * Tracks changes to the tabbed pane contents. Each tab component is tracked
0140: * for changes on the {@link SubstanceLookAndFeel#WINDOW_MODIFIED} property.
0141: */
0142: protected TabbedContainerListener substanceContainerListener;
0143:
0144: /**
0145: * Listener for animation effects on tab selection.
0146: */
0147: protected ChangeListener substanceSelectionListener;
0148:
0149: /**
0150: * Background delegate.
0151: */
0152: private SubstanceFillBackgroundDelegate bgDelegate;
0153:
0154: /**
0155: * Tracks changes to the tabbed pane contents. Each tab component is tracked
0156: * for changes on the {@link SubstanceLookAndFeel#WINDOW_MODIFIED} property.
0157: *
0158: * @author Kirill Grouchnikov
0159: */
0160: protected final class TabbedContainerListener extends ContainerAdapter {
0161: /**
0162: * Property change listeners on the tab components. <p/> Fixes defect
0163: * 135 - memory leaks on tabbed panes.
0164: */
0165: private Map<Component, List<PropertyChangeListener>> listeners = new HashMap<Component, List<PropertyChangeListener>>();
0166:
0167: /**
0168: * Creates a new container listener.
0169: */
0170: public TabbedContainerListener() {
0171: }
0172:
0173: /**
0174: * Tracks all existing tab component.
0175: */
0176: protected void trackExistingTabs() {
0177: // register listeners on all existing tabs
0178: for (int i = 0; i < SubstanceTabbedPaneUI.this .tabPane
0179: .getTabCount(); i++) {
0180: this .trackTab(SubstanceTabbedPaneUI.this .tabPane
0181: .getComponentAt(i));
0182: }
0183: }
0184:
0185: /**
0186: * Tracks changes in a single tab component.
0187: *
0188: * @param tabComponent
0189: * Tab component.
0190: */
0191: protected void trackTab(final Component tabComponent) {
0192: if (tabComponent == null)
0193: return;
0194:
0195: PropertyChangeListener tabModifiedListener = new PropertyChangeListener() {
0196: public void propertyChange(PropertyChangeEvent evt) {
0197: if (SubstanceLookAndFeel.WINDOW_MODIFIED.equals(evt
0198: .getPropertyName())) {
0199: Object oldValue = evt.getOldValue();
0200: Object newValue = evt.getNewValue();
0201: boolean wasModified = Boolean.TRUE.equals(oldValue);
0202: boolean isModified = Boolean.TRUE.equals(newValue);
0203:
0204: if (wasModified) {
0205: if (!isModified) {
0206: long fadeInstanceId = SubstanceTabbedPaneUI.this .fadeModifiedIds
0207: .get(tabComponent);
0208: FadeTracker.getInstance().cancelFadeInstance(
0209: fadeInstanceId);
0210: }
0211: } else {
0212: if (isModified) {
0213: int tabIndex = SubstanceTabbedPaneUI.this .tabPane
0214: .indexOfComponent(tabComponent);
0215: if (tabIndex >= 0) {
0216: long fadeInstanceId = FadeTracker
0217: .getInstance()
0218: .trackFadeLooping(
0219: ModifiedFadeStep.MARKED_MODIFIED_FADE_KIND,
0220: new LafConstants.AnimationKind(
0221: new ModifiedFadeStep(),
0222: "modified"),
0223: SubstanceTabbedPaneUI.this .tabPane,
0224: tabIndex,
0225: false,
0226: SubstanceTabbedPaneUI.this
0227: .getCallback(tabIndex),
0228: -1, true);
0229: SubstanceTabbedPaneUI.this .fadeModifiedIds
0230: .put(tabComponent, fadeInstanceId);
0231: }
0232: }
0233: }
0234: }
0235: }
0236: };
0237: tabComponent.addPropertyChangeListener(tabModifiedListener);
0238: // fix for defect 135 - memory leaks on tabbed panes
0239: List<PropertyChangeListener> currList = this .listeners
0240: .get(tabComponent);
0241: if (currList == null)
0242: currList = new LinkedList<PropertyChangeListener>();
0243: currList.add(tabModifiedListener);
0244: // System.err.println(this.hashCode() + " adding for " +
0245: // tabComponent.hashCode());
0246: this .listeners.put(tabComponent, currList);
0247: // Fix for defect 104 - a 'modified' component is added to
0248: // the tabbed pane. In this case it should be animated from the
0249: // beginning.
0250: if (tabComponent instanceof JComponent) {
0251: if (Boolean.TRUE
0252: .equals(((JComponent) tabComponent)
0253: .getClientProperty(SubstanceLookAndFeel.WINDOW_MODIFIED))) {
0254: // TabPulseTracker.update(SubstanceTabbedPaneUI.this.tabPane,
0255: // tabComponent);
0256:
0257: int tabIndex = SubstanceTabbedPaneUI.this .tabPane
0258: .indexOfComponent(tabComponent);
0259: if (tabIndex >= 0) {
0260: long fadeInstanceId = FadeTracker
0261: .getInstance()
0262: .trackFadeLooping(
0263: ModifiedFadeStep.MARKED_MODIFIED_FADE_KIND,
0264: new LafConstants.AnimationKind(
0265: new ModifiedFadeStep(),
0266: "modified"),
0267: SubstanceTabbedPaneUI.this .tabPane,
0268: tabIndex,
0269: false,
0270: SubstanceTabbedPaneUI.this
0271: .getCallback(tabIndex), -1,
0272: true);
0273: SubstanceTabbedPaneUI.this .fadeModifiedIds.put(
0274: tabComponent, fadeInstanceId);
0275: }
0276: }
0277: }
0278: }
0279:
0280: /**
0281: * Stops tracking changes to a single tab component.
0282: *
0283: * @param tabComponent
0284: * Tab component.
0285: */
0286: protected void stopTrackTab(final Component tabComponent) {
0287: if (tabComponent == null)
0288: return;
0289:
0290: List<PropertyChangeListener> pclList = this .listeners
0291: .get(tabComponent);
0292: if (pclList != null) {
0293: for (PropertyChangeListener pcl : pclList)
0294: tabComponent.removePropertyChangeListener(pcl);
0295: }
0296:
0297: this .listeners.put(tabComponent, null);
0298: }
0299:
0300: /**
0301: * Stops tracking all tab components.
0302: */
0303: protected void stopTrackExistingTabs() {
0304: // register listeners on all existing tabs
0305: for (int i = 0; i < SubstanceTabbedPaneUI.this .tabPane
0306: .getTabCount(); i++) {
0307: this .stopTrackTab(SubstanceTabbedPaneUI.this .tabPane
0308: .getComponentAt(i));
0309: }
0310: }
0311:
0312: /*
0313: * (non-Javadoc)
0314: *
0315: * @see java.awt.event.ContainerAdapter#componentAdded(java.awt.event.ContainerEvent)
0316: */
0317: @Override
0318: public void componentAdded(final ContainerEvent e) {
0319: final Component tabComponent = e.getChild();
0320: if (tabComponent instanceof UIResource)
0321: return;
0322: this .trackTab(tabComponent);
0323: }
0324:
0325: /*
0326: * (non-Javadoc)
0327: *
0328: * @see java.awt.event.ContainerAdapter#componentRemoved(java.awt.event.ContainerEvent)
0329: */
0330: @Override
0331: public void componentRemoved(ContainerEvent e) {
0332: // fix for defect 135 - memory leaks on tabbed panes
0333: final Component tabComponent = e.getChild();
0334: if (tabComponent == null)
0335: return;
0336: // System.err.println(this.hashCode() + " removing for " +
0337: // tabComponent.hashCode());
0338: if (tabComponent instanceof UIResource)
0339: return;
0340: for (PropertyChangeListener pcl : this .listeners.get(tabComponent))
0341: tabComponent.removePropertyChangeListener(pcl);
0342: this .listeners.get(tabComponent).clear();
0343: this .listeners.remove(tabComponent);
0344: // this.cleanListeners(tabComponent);
0345: }
0346:
0347: // /**
0348: // * Cleans all listeners on the component.
0349: // *
0350: // * @param comp
0351: // * Component.
0352: // */
0353: // private void cleanListeners(Component comp) {
0354: // // if (comp instanceof JTabbedPane) {
0355: // // JTabbedPane jtp = (JTabbedPane)comp;
0356: // // SubstanceTabbedPaneUI ui = (SubstanceTabbedPaneUI)jtp
0357: // // .getUI();
0358: // // // TabbedContainerListener tcl = ui.containerListener;
0359: // // // tcl.stopTrackExistingTabs();
0360: // // // tcl.listeners.clear();
0361: // // // //jtp.removeContainerListener(tcl);
0362: // // //
0363: // //
0364: // // ui.uninstallUI(jtp);
0365: // // }
0366: //
0367: // if (comp instanceof Container) {
0368: // Container cont = (Container) comp;
0369: // for (int i = 0; i < cont.getComponentCount(); i++) {
0370: // this.cleanListeners(cont.getComponent(i));
0371: // }
0372: // }
0373: // if (comp instanceof JTabbedPane) {
0374: // JTabbedPane jtp = (JTabbedPane) comp;
0375: // SubstanceTabbedPaneUI ui = (SubstanceTabbedPaneUI) jtp.getUI();
0376: // // TabbedContainerListener tcl = ui.containerListener;
0377: // // tcl.stopTrackExistingTabs();
0378: // // tcl.listeners.clear();
0379: // // //jtp.removeContainerListener(tcl);
0380: // //
0381: //
0382: // ui.uninstallUI(jtp);
0383: // }
0384: // }
0385: }
0386:
0387: /**
0388: * Listener for rollover animation effects.
0389: *
0390: * @author Kirill Grouchnikov
0391: */
0392: protected class MouseRolloverHandler implements MouseListener,
0393: MouseMotionListener {
0394: /**
0395: * Index of the tab that was rolloed over on the previous mouse event.
0396: */
0397: int prevRolledOver = -1;
0398:
0399: /**
0400: * Indicates whether the previous mouse event was located in a close
0401: * button.
0402: */
0403: boolean prevInCloseButton = false;
0404:
0405: int tabOfPressedCloseButton = -1;
0406:
0407: /*
0408: * (non-Javadoc)
0409: *
0410: * @see java.awt.event.MouseListener#mouseClicked(java.awt.event.MouseEvent)
0411: */
0412: public void mouseClicked(final MouseEvent e) {
0413: final int tabIndex = SubstanceTabbedPaneUI.this .tabForCoordinate(
0414: SubstanceTabbedPaneUI.this .tabPane, e.getX(), e.getY());
0415: TabCloseCallback closeCallback = SubstanceCoreUtilities
0416: .getTabCloseCallback(e, SubstanceTabbedPaneUI.this .tabPane,
0417: tabIndex);
0418: if (closeCallback == null)
0419: return;
0420:
0421: final TabCloseKind tabCloseKind = closeCallback.onAreaClick(
0422: SubstanceTabbedPaneUI.this .tabPane, tabIndex, e);
0423: if (tabCloseKind == TabCloseKind.NONE)
0424: return;
0425:
0426: SwingUtilities.invokeLater(new Runnable() {
0427: public void run() {
0428: SubstanceTabbedPaneUI.this .tryCloseTabs(tabIndex,
0429: tabCloseKind);
0430: }
0431: });
0432: }
0433:
0434: /*
0435: * (non-Javadoc)
0436: *
0437: * @see java.awt.event.MouseMotionListener#mouseDragged(java.awt.event.MouseEvent)
0438: */
0439: public void mouseDragged(MouseEvent e) {
0440: this .handleMouseMoveDrag(e);
0441: }
0442:
0443: /*
0444: * (non-Javadoc)
0445: *
0446: * @see java.awt.event.MouseListener#mouseEntered(java.awt.event.MouseEvent)
0447: */
0448: public void mouseEntered(MouseEvent e) {
0449: setRolloverTab(tabForCoordinate(tabPane, e.getX(), e.getY()));
0450: }
0451:
0452: /*
0453: * (non-Javadoc)
0454: *
0455: * @see java.awt.event.MouseListener#mousePressed(java.awt.event.MouseEvent)
0456: */
0457: public void mousePressed(MouseEvent e) {
0458: if (!tabPane.isEnabled()) {
0459: return;
0460: }
0461: int tabIndex = tabForCoordinate(tabPane, e.getX(), e.getY());
0462: if (tabIndex >= 0 && tabPane.isEnabledAt(tabIndex)) {
0463: Rectangle rect = new Rectangle();
0464: rect = getTabBounds(tabIndex, rect);
0465: Rectangle close = getCloseButtonRectangleForEvents(tabIndex,
0466: rect.x, rect.y, rect.width, rect.height);
0467: boolean inCloseButton = close.contains(e.getPoint());
0468: this .tabOfPressedCloseButton = inCloseButton ? tabIndex : -1;
0469: if (tabIndex != tabPane.getSelectedIndex()) {
0470: // enhancement 307 - don't select tab on pressing its
0471: // close button
0472: if (inCloseButton) {
0473: return;
0474: }
0475: // Clicking on unselected tab, change selection, do NOT
0476: // request focus.
0477: // This will trigger the focusIndex to change by way
0478: // of stateChanged.
0479: tabPane.setSelectedIndex(tabIndex);
0480: }
0481: else if (tabPane.isRequestFocusEnabled()) {
0482: // Clicking on selected tab, try and give the tabbedpane
0483: // focus. Repaint will occur in focusGained.
0484: tabPane.requestFocus();
0485: }
0486: }
0487: }
0488:
0489: /*
0490: * (non-Javadoc)
0491: *
0492: * @see java.awt.event.MouseMotionListener#mouseMoved(java.awt.event.MouseEvent)
0493: */
0494: public void mouseMoved(MouseEvent e) {
0495: this .handleMouseMoveDrag(e);
0496: }
0497:
0498: private void handleMouseMoveDrag(MouseEvent e) {
0499: if (e.getSource() != tabPane)
0500: return;
0501:
0502: setRolloverTab(tabForCoordinate(tabPane, e.getX(), e.getY()));
0503: if (SubstanceLookAndFeel.toIgnoreAnimation(tabPane.getClass()))
0504: return;
0505:
0506: SubstanceTabbedPaneUI.this .substanceMouseLocation = e.getPoint();
0507: int currRolledOver = SubstanceTabbedPaneUI.this .getRolloverTab();
0508: TabCloseCallback tabCloseCallback = SubstanceCoreUtilities
0509: .getTabCloseCallback(e, tabPane, currRolledOver);
0510:// System.err.println("Mouse moved " + currRolledOver + ":" +
0511:// prevRolledOver);
0512: if (currRolledOver == this .prevRolledOver) {
0513: if (currRolledOver >= 0) {
0514: Rectangle rect = new Rectangle();
0515: rect = getTabBounds(currRolledOver, rect);
0516: Rectangle close = getCloseButtonRectangleForEvents(currRolledOver,
0517: rect.x, rect.y, rect.width, rect.height);
0518:// System.out.println("move " + rect + " " + close + " "
0519:// + e.getPoint());
0520: boolean inCloseButton = close.contains(e.getPoint());
0521: if (this .prevInCloseButton == inCloseButton)
0522: return;
0523: this .prevInCloseButton = inCloseButton;
0524: if (tabCloseCallback != null) {
0525: if (inCloseButton) {
0526: String closeButtonTooltip = tabCloseCallback
0527: .getCloseButtonTooltip(tabPane,
0528: currRolledOver);
0529: tabPane.setToolTipTextAt(currRolledOver,
0530: closeButtonTooltip);
0531: } else {
0532: String areaTooltip = tabCloseCallback
0533: .getAreaTooltip(tabPane,
0534: currRolledOver);
0535: tabPane.setToolTipTextAt(currRolledOver,
0536: areaTooltip);
0537: }
0538: }
0539: if ((currRolledOver >= 0)
0540: && (currRolledOver < tabPane.getTabCount())) {
0541: FadeTrackerCallback currCallback = SubstanceTabbedPaneUI.this
0542: .getCallback(currRolledOver);
0543: currCallback.fadePerformed(FadeKind.ROLLOVER, 0.0f);
0544: }
0545: }
0546: } else {
0547: FadeTracker fadeTracker = FadeTracker.getInstance();
0548: if ((this .prevRolledOver >= 0)
0549: && (this .prevRolledOver < tabPane.getTabCount())
0550: && tabPane.isEnabledAt(this .prevRolledOver)) {
0551: // System.out.println("Fading out " + prevRolledOver);
0552: fadeTracker.trackFadeOut(FadeKind.ROLLOVER, tabPane,
0553: this .prevRolledOver, true,
0554: new TabRepaintCallback(tabPane, this .prevRolledOver));
0555: }
0556: if ((currRolledOver >= 0)
0557: && (currRolledOver < tabPane.getTabCount())
0558: && tabPane.isEnabledAt(currRolledOver)) {
0559: fadeTracker.trackFadeIn(FadeKind.ROLLOVER,
0560: tabPane, currRolledOver, true,
0561: new TabRepaintCallback(tabPane, currRolledOver));
0562: }
0563: }
0564: this .prevRolledOver = currRolledOver;
0565: }
0566:
0567: /*
0568: * (non-Javadoc)
0569: *
0570: * @see java.awt.event.MouseListener#mouseExited(java.awt.event.MouseEvent)
0571: */
0572: public void mouseExited(MouseEvent e) {
0573: setRolloverTab(-1);
0574: // fix for bug 69 - non-selected non-rollover tab
0575: // may remain with close button after moving mouse quickly
0576: // to inner JTabbedPane
0577: if ((this .prevRolledOver >= 0)
0578: && (this .prevRolledOver < SubstanceTabbedPaneUI.this .tabPane
0579: .getTabCount())
0580: && SubstanceTabbedPaneUI.this .tabPane
0581: .isEnabledAt(this .prevRolledOver)) {
0582: // only the previously rolled-over tab needs to be
0583: // repainted (fade-out) instead of repainting the
0584: // whole tab as before.
0585: FadeTracker fadeTracker = FadeTracker.getInstance();
0586: fadeTracker.trackFadeOut(FadeKind.ROLLOVER,
0587: SubstanceTabbedPaneUI.this .tabPane,
0588: this .prevRolledOver, true, new TabRepaintCallback(
0589: SubstanceTabbedPaneUI.this .tabPane,
0590: this .prevRolledOver));
0591:
0592: if (SubstanceCoreUtilities.getTabCloseCallback(e, tabPane,
0593: this .prevRolledOver) != null) {
0594: tabPane.setToolTipTextAt(this .prevRolledOver, null);
0595: }
0596: }
0597: this .prevRolledOver = -1;
0598: }
0599:
0600: /*
0601: * (non-Javadoc)
0602: *
0603: * @see java.awt.event.MouseListener#mouseReleased(java.awt.event.MouseEvent)
0604: */
0605: public void mouseReleased(final MouseEvent e) {
0606: // enhancement 307 - moving the tab close to be on mouse release
0607: // and not on mouse press.
0608: final int tabIndex = SubstanceTabbedPaneUI.this .tabForCoordinate(
0609: SubstanceTabbedPaneUI.this .tabPane, e.getX(), e.getY());
0610: // check that the mouse release is on the same tab as
0611: // mouse press, and that the tab has close button
0612: if (SubstanceCoreUtilities.hasCloseButton(
0613: SubstanceTabbedPaneUI.this .tabPane, tabIndex) &&
0614: (tabIndex == this .tabOfPressedCloseButton)) {
0615: SwingUtilities.invokeLater(new Runnable() {
0616: public void run() {
0617: if ((tabIndex >= 0)
0618: && SubstanceTabbedPaneUI.this .tabPane
0619: .isEnabledAt(tabIndex)) {
0620: Rectangle rect = new Rectangle();
0621: rect = SubstanceTabbedPaneUI.this .getTabBounds(
0622: tabIndex, rect);
0623:
0624: Rectangle close = SubstanceTabbedPaneUI.this
0625: .getCloseButtonRectangleForEvents(tabIndex,
0626: rect.x, rect.y, rect.width,
0627: rect.height);
0628: // System.out.println("press " + close + " "
0629: // + e.getPoint());
0630: if (close.contains(e.getPoint())) {
0631: TabCloseCallback closeCallback = SubstanceCoreUtilities
0632: .getTabCloseCallback(
0633: e,
0634: SubstanceTabbedPaneUI.this .tabPane,
0635: tabIndex);
0636:
0637: TabCloseKind tabCloseKind = (closeCallback == null) ? TabCloseKind.THIS
0638: : closeCallback
0639: .onCloseButtonClick(
0640: SubstanceTabbedPaneUI.this .tabPane,
0641: tabIndex, e);
0642:
0643: SubstanceTabbedPaneUI.this .tryCloseTabs(
0644: tabIndex, tabCloseKind);
0645: }
0646: }
0647: }
0648: });
0649: this .tabOfPressedCloseButton = -1;
0650: }
0651: }
0652: }
0653:
0654: /**
0655: * Creates the new UI delegate.
0656: */
0657: public SubstanceTabbedPaneUI() {
0658: super ();
0659: this .bgDelegate = new SubstanceFillBackgroundDelegate();
0660: this .prevStateMap = new HashMap<Integer, ComponentState>();
0661: this .nextStateMap = new HashMap<Integer, ComponentState>();
0662: }
0663:
0664: /*
0665: * (non-Javadoc)
0666: *
0667: * @see javax.swing.plaf.basic.BasicTabbedPaneUI#installListeners()
0668: */
0669: @Override
0670: protected void installListeners() {
0671: super .installListeners();
0672: // Install listener to repaint the tabbed pane
0673: // on mouse move (for rollover effects).
0674: this .substanceRolloverHandler = new MouseRolloverHandler();
0675: this .tabPane.addMouseMotionListener(this .substanceRolloverHandler);
0676: this .tabPane.addMouseListener(this .substanceRolloverHandler);
0677:
0678: // Add container listener to wire property change listener
0679: // on each tab in the tabbed pane.
0680: this .substanceContainerListener = new TabbedContainerListener();
0681: this .substanceContainerListener.trackExistingTabs();
0682:
0683: for (int i = 0; i < this .tabPane.getTabCount(); i++) {
0684: Component tabComp = this .tabPane.getComponentAt(i);
0685: if (SubstanceCoreUtilities.isTabModified(tabComp)) {
0686: // TabPulseTracker.update(this.tabPane, tabComp);
0687: long fadeInstanceId = FadeTracker.getInstance()
0688: .trackFadeLooping(
0689: ModifiedFadeStep.MARKED_MODIFIED_FADE_KIND,
0690: new LafConstants.AnimationKind(
0691: new ModifiedFadeStep(), "modified"),
0692: this .tabPane, i, false, this .getCallback(i),
0693: -1, true);
0694: this .fadeModifiedIds.put(tabComp, fadeInstanceId);
0695: }
0696: }
0697:
0698: this .tabPane.addContainerListener(this .substanceContainerListener);
0699:
0700: this .substanceSelectionListener = new ChangeListener() {
0701: public void stateChanged(ChangeEvent e) {
0702: SwingUtilities.invokeLater(new Runnable() {
0703: public void run() {
0704: if (SubstanceTabbedPaneUI.this .tabPane == null)
0705: return;
0706: int selected = SubstanceTabbedPaneUI.this .tabPane
0707: .getSelectedIndex();
0708: FadeTracker fadeTracker = FadeTracker.getInstance();
0709: if ((selected >= 0)
0710: && (selected < SubstanceTabbedPaneUI.this .tabPane
0711: .getTabCount())
0712: && SubstanceTabbedPaneUI.this .tabPane
0713: .isEnabledAt(selected)) {
0714: fadeTracker.trackFadeIn(FadeKind.ROLLOVER,
0715: SubstanceTabbedPaneUI.this .tabPane,
0716: selected, true, new TabRepaintCallback(
0717: SubstanceTabbedPaneUI.this .tabPane,
0718: selected));
0719: }
0720: }
0721: });
0722: }
0723: };
0724: this .tabPane.getModel().addChangeListener(
0725: this .substanceSelectionListener);
0726: }
0727:
0728: /*
0729: * (non-Javadoc)
0730: *
0731: * @see javax.swing.plaf.basic.BasicTabbedPaneUI#uninstallListeners()
0732: */
0733: @Override
0734: protected void uninstallListeners() {
0735: super .uninstallListeners();
0736: if (this .substanceRolloverHandler != null) {
0737: this .tabPane
0738: .removeMouseMotionListener(this .substanceRolloverHandler);
0739: this .tabPane.removeMouseListener(this .substanceRolloverHandler);
0740: this .substanceRolloverHandler = null;
0741: }
0742: if (this .substanceContainerListener != null) {
0743: for (Map.Entry<Component, List<PropertyChangeListener>> entry : this .substanceContainerListener.listeners
0744: .entrySet()) {
0745: Component comp = entry.getKey();
0746: // System.out.println(this.containerListener.hashCode() + "
0747: // removing all for " + comp.hashCode());
0748: for (PropertyChangeListener pcl : entry.getValue()) {
0749: comp.removePropertyChangeListener(pcl);
0750: }
0751: }
0752: this .substanceContainerListener.listeners.clear();
0753:
0754: this .tabPane
0755: .removeContainerListener(this .substanceContainerListener);
0756: this .substanceContainerListener = null;
0757: }
0758: this .tabPane.getModel().removeChangeListener(
0759: this .substanceSelectionListener);
0760: this .substanceSelectionListener = null;
0761: }
0762:
0763: /*
0764: * (non-Javadoc)
0765: *
0766: * @see javax.swing.plaf.basic.BasicTabbedPaneUI#installDefaults()
0767: */
0768: @Override
0769: protected void installDefaults() {
0770: super .installDefaults();
0771: this .fadeModifiedIds = new HashMap<Component, Long>();
0772: }
0773:
0774: /*
0775: * (non-Javadoc)
0776: *
0777: * @see javax.swing.plaf.basic.BasicTabbedPaneUI#uninstallDefaults()
0778: */
0779: @Override
0780: protected void uninstallDefaults() {
0781: this .fadeModifiedIds.clear();
0782: super .uninstallDefaults();
0783: }
0784:
0785: /**
0786: * Retrieves tab background.
0787: *
0788: * @param tabPane
0789: * Tabbed pane.
0790: * @param width
0791: * Tab width.
0792: * @param height
0793: * Tab height.
0794: * @param isSelected
0795: * Indication whether the tab is selected.
0796: * @param cyclePos
0797: * Tab cycle position (for rollover effects).
0798: * @param tabPlacement
0799: * Tab placement.
0800: * @param side
0801: * Tab open side.
0802: * @param colorScheme
0803: * Color scheme for coloring the background.
0804: * @param colorScheme2
0805: * Second color scheme for coloring the background.
0806: * @param borderScheme
0807: * Color scheme for coloring the border.
0808: * @param borderScheme2
0809: * Second color scheme for coloring the border.
0810: * @param paintOnlyBorder
0811: * If <code>true</code>, only the border will be painted.
0812: * @return Tab background of specified parameters.
0813: */
0814: private static synchronized BufferedImage getTabBackground(
0815: JTabbedPane tabPane, int width, int height, boolean isSelected,
0816: float cyclePos, int tabPlacement, SubstanceConstants.Side side,
0817: ColorScheme colorScheme, ColorScheme colorScheme2,
0818: ColorScheme borderScheme, ColorScheme borderScheme2, boolean paintOnlyBorder) {
0819: SubstanceGradientPainter gradientPainter =
0820: SubstanceCoreUtilities.getGradientPainter(tabPane);
0821: if (gradientPainter == null)
0822: return null;
0823: SubstanceBorderPainter borderPainter = SubstanceCoreUtilities
0824: .getBorderPainter(tabPane);
0825:
0826: int borderDelta = (int) Math.ceil(2.0 * SubstanceSizeUtils
0827: .getBorderStrokeWidth(SubstanceSizeUtils
0828: .getComponentFontSize(tabPane)));
0829: int borderInsets = (int) Math.floor(SubstanceSizeUtils
0830: .getBorderStrokeWidth(SubstanceSizeUtils
0831: .getComponentFontSize(tabPane)) / 2.0);
0832: int dx = 0;
0833: int ox = 0;
0834: int oy = 0;
0835: int dy = 0;
0836: switch (side) {
0837: case BOTTOM:
0838: dy = 2 + borderDelta;
0839: // oy = -2 - borderDelta;
0840: break;
0841: case TOP:
0842: dy = 2 + borderDelta;
0843: // oy = 2 + borderDelta;
0844: break;
0845: case RIGHT:
0846: dx = 2 + borderDelta;
0847: break;
0848: case LEFT:
0849: dx = 2 + borderDelta;
0850: ox = -2 - borderDelta;
0851: }
0852:
0853: SubstanceButtonShaper shaper = SubstanceCoreUtilities.getButtonShaper(tabPane);
0854: String key = (width + dx) + ":" + (height + dy) + ":" + isSelected
0855: + ":" + cyclePos + ":" + side.toString() + ":"
0856: + gradientPainter.getDisplayName() + ":"
0857: + borderPainter.getDisplayName() + ":"
0858: + shaper.getDisplayName() + ":"
0859: + tabPlacement + ":"
0860: + side.name() + ":"
0861: + SubstanceCoreUtilities.getSchemeId(colorScheme) + ":"
0862: + SubstanceCoreUtilities.getSchemeId(colorScheme2) + ":"
0863: + SubstanceCoreUtilities.getSchemeId(borderScheme) + ":"
0864: + SubstanceCoreUtilities.getSchemeId(borderScheme2) + ":" +
0865: paintOnlyBorder;
0866:
0867: BufferedImage result = SubstanceTabbedPaneUI.backgroundMap.get(key);
0868: if (result == null) {
0869: Set<Side> straightSides = new HashSet<Side>();
0870: straightSides.add(side);
0871:
0872: int cornerRadius = height / 3;
0873: if (shaper instanceof ClassicButtonShaper) {
0874: cornerRadius = (int) SubstanceSizeUtils.getClassicButtonCornerRadius(
0875: SubstanceSizeUtils.getComponentFontSize(tabPane));
0876: if ((tabPlacement == TOP) || (tabPlacement == BOTTOM))
0877: width -= 1;
0878: else
0879: height -= 1;
0880: }
0881:
0882:// int borderDelta = (int) Math.floor(SubstanceSizeUtils
0883:// .getBorderStrokeWidth(tabPane) / 2.0);
0884: GeneralPath contour = BaseButtonShaper.getBaseOutline(width + dx,
0885: height + dy, cornerRadius, straightSides, borderInsets);
0886:
0887: result = gradientPainter.getContourBackground(width + dx, height
0888: + dy, contour, false, colorScheme, colorScheme2, cyclePos,
0889: true, colorScheme != colorScheme2);
0890:
0891: BufferedImage finalImage = SubstanceCoreUtilities.getBlankImage(
0892: width, height);
0893: Graphics2D finalGraphics = (Graphics2D) finalImage.getGraphics();
0894: finalGraphics.translate(ox, oy);
0895: if (!paintOnlyBorder)
0896: finalGraphics.drawImage(result, 0, 0, null);
0897:
0898: int borderThickness = (int) SubstanceSizeUtils
0899: .getBorderStrokeWidth(SubstanceSizeUtils
0900: .getComponentFontSize(tabPane));
0901: GeneralPath contourInner = BaseButtonShaper.getBaseOutline(width + dx,
0902: height + dy, cornerRadius, straightSides, borderThickness + borderInsets);
0903:
0904: borderPainter.paintBorder(finalGraphics, tabPane, width + dx, height + dy,
0905: contour, contourInner, borderScheme, borderScheme2, cyclePos,
0906: borderScheme != borderScheme2);
0907:
0908: SubstanceTabbedPaneUI.backgroundMap.put(key, finalImage);
0909:
0910: // fix for defect 138
0911: result = finalImage;
0912: }
0913: return result;
0914: }
0915:
0916: /**
0917: * Retrieves the image of the close button.
0918: *
0919: * @param tabPane
0920: * Tabbed pane.
0921: * @param width
0922: * Close button width.
0923: * @param height
0924: * Close button height.
0925: * @param cyclePos10
0926: * Tab cycle position (for rollover effects).
0927: * @param toPaintBackground
0928: * Indication whether the button background (including contour)
0929: * needs to be painted.
0930: * @param colorScheme
0931: * Color scheme for coloring the background.
0932: * @param colorScheme2
0933: * Second color scheme for coloring the background.
0934: * @return Image of the close button of specified parameters.
0935: */
0936: private static synchronized BufferedImage getCloseButtonImage(
0937: JTabbedPane tabPane, int width, int height, float cyclePos10,
0938: boolean toPaintBackground, ColorScheme colorScheme,
0939: ColorScheme colorScheme2) {
0940: SubstanceGradientPainter gradientPainter = SubstanceLookAndFeel
0941: .getCurrentGradientPainter();
0942: if (gradientPainter == null)
0943: return null;
0944:
0945: String key = width + ":" + height + ":" + toPaintBackground + ":"
0946: + cyclePos10 + ":" + gradientPainter.getDisplayName() + ":"
0947: + SubstanceCoreUtilities.getSchemeId(colorScheme) + ":"
0948: + SubstanceCoreUtilities.getSchemeId(colorScheme2);
0949: BufferedImage result = SubstanceTabbedPaneUI.closeButtonMap.get(key);
0950: if (result == null) {
0951: result = SubstanceCoreUtilities.getBlankImage(width, height);
0952: Graphics2D finalGraphics = (Graphics2D) result.getGraphics();
0953: finalGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
0954: RenderingHints.VALUE_ANTIALIAS_ON);
0955:
0956: if (toPaintBackground) {
0957: GeneralPath contour = BaseButtonShaper.getBaseOutline(width,
0958: height, 1, null);
0959: BufferedImage background = gradientPainter
0960: .getContourBackground(width, height, contour, false,
0961: colorScheme, colorScheme2, cyclePos10, true,
0962: colorScheme != colorScheme2);
0963: finalGraphics.drawImage(background, 0, 0, null);
0964: SubstanceBorderPainter borderPainter = SubstanceCoreUtilities
0965: .getBorderPainter(tabPane);
0966: borderPainter.paintBorder(finalGraphics, tabPane, width,
0967: height, contour, null, colorScheme, colorScheme2,
0968: cyclePos10, colorScheme != colorScheme2);
0969: }
0970:
0971: finalGraphics.setStroke(new BasicStroke(SubstanceSizeUtils
0972: .getTabCloseButtonStrokeWidth(
0973: SubstanceSizeUtils.getComponentFontSize(tabPane))));
0974:
0975: int delta = (int) (Math.floor(SubstanceSizeUtils.getBorderStrokeWidth(
0976: SubstanceSizeUtils.getComponentFontSize(tabPane))));
0977: if (delta % 2 == 1)
0978: delta--;
0979: int iconSize = width - delta;
0980:
0981: if (colorScheme == colorScheme2) {
0982: // little optimization
0983: cyclePos10 = 0.0f;
0984: }
0985: if (cyclePos10 > 0.0) {
0986: Icon closeIcon2 = SubstanceImageCreator.getCloseIcon(iconSize,
0987: colorScheme2);
0988: closeIcon2.paintIcon(tabPane, finalGraphics, delta / 2, delta / 2);
0989: }
0990:
0991: if (cyclePos10 < 1.0) {
0992: Icon closeIcon = SubstanceImageCreator.getCloseIcon(iconSize,
0993: colorScheme);
0994: finalGraphics.setComposite(AlphaComposite.getInstance(
0995: AlphaComposite.SRC_OVER, 1.0f - cyclePos10 / 10.0f));
0996: closeIcon.paintIcon(tabPane, finalGraphics, delta / 2, delta / 2);
0997: }
0998:
0999: SubstanceTabbedPaneUI.closeButtonMap.put(key, result);
1000: }
1001: return result;
1002: }
1003:
1004: /*
1005: * (non-Javadoc)
1006: *
1007: * @see javax.swing.plaf.basic.BasicTabbedPaneUI#paintTabBackground(java.awt.Graphics,
1008: * int, int, int, int, int, int, boolean)
1009: */
1010: @Override
1011: protected void paintTabBackground(Graphics g, int tabPlacement,
1012: final int tabIndex, final int x,
1013: final int y, int w, int h, boolean isSelected) {
1014:
1015: BufferedImage backgroundImage = null;
1016: Graphics2D graphics = (Graphics2D) g.create();
1017: graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
1018: RenderingHints.VALUE_ANTIALIAS_ON);
1019: graphics.setComposite(TransitionLayout.getAlphaComposite(this .tabPane,
1020: g));
1021:
1022: Component tabComponent = this .tabPane.getComponentAt(tabIndex);
1023: ComponentState prevState = this .getPrevTabState(tabIndex);
1024: ComponentState currState = this .getTabState(tabIndex);
1025: if (prevState == null)
1026: prevState = currState;
1027:
1028: SubstanceTheme theme = SubstanceThemeUtilities.getTheme(
1029: this .tabPane, tabIndex, currState);
1030: SubstanceTheme theme2 = SubstanceThemeUtilities.getTheme(
1031: this .tabPane, tabIndex, prevState);
1032:
1033:// Color background = this.tabPane.getBackgroundAt(tabIndex);
1034:// if (!(background instanceof UIResource)) {
1035:// // enhancement 256 - support for colorization of tabbed panes
1036:// double colorization =
1037:// SubstanceCoreUtilities.getColorizationFactor(this.tabPane);
1038:// if (!this.tabPane.isEnabledAt(tabIndex))
1039:// colorization /= 2.0;
1040:// if (colorization > 0.0) {
1041:// theme = SubstanceShiftTheme.getShiftedTheme(theme, background,
1042:// colorization, null, colorization);
1043:// theme2 = SubstanceShiftTheme.getShiftedTheme(theme2, background,
1044:// colorization, null, colorization);
1045:// }
1046:// }
1047:
1048: ColorScheme colorScheme = theme.getColorScheme();
1049: ColorScheme colorScheme2 = theme2.getColorScheme();
1050: ColorScheme borderScheme = theme.getBorderTheme().getColorScheme();
1051: ColorScheme borderScheme2 = theme2.getBorderTheme().getColorScheme();
1052:
1053: // fix for defect 138
1054: graphics.clip(new Rectangle(x, y, w, h));
1055:
1056: boolean isRollover = (this .getRolloverTab() == tabIndex);
1057: boolean isEnabled = this .tabPane.isEnabledAt(tabIndex);
1058:
1059: boolean hasActivePresence = isSelected || (isRollover && isEnabled);
1060: float cyclePos = (isRollover && isEnabled) ? 5 : 0;
1061:
1062: // check if have windowModified property
1063: Component comp = this .tabPane.getComponentAt(tabIndex);
1064: boolean isWindowModified = SubstanceCoreUtilities.isTabModified(comp);
1065: boolean toMarkModifiedCloseButton = SubstanceCoreUtilities
1066: .toAnimateCloseIconOfModifiedTab(this .tabPane, tabIndex);
1067: if (isWindowModified && isEnabled && !toMarkModifiedCloseButton) {
1068: colorScheme2 = SubstanceTheme.YELLOW;
1069: colorScheme = SubstanceTheme.ORANGE;
1070:
1071: cyclePos = FadeTracker.getInstance().getFade10(this .tabPane,
1072: tabIndex, ModifiedFadeStep.MARKED_MODIFIED_FADE_KIND);
1073: hasActivePresence = true;
1074: }
1075:
1076: // see if need to use fade animation. Important - don't do it
1077: // on pulsating tabs.
1078: FadeTracker fadeTracker = FadeTracker.getInstance();
1079: if (!isWindowModified) {
1080: FadeState fadeState = fadeTracker.getFadeState(this .tabPane,
1081: tabIndex, FadeKind.ROLLOVER);
1082: if (fadeState != null) {
1083: hasActivePresence = true;
1084: // ColorScheme defaultScheme = SubstanceCoreUtilities
1085: // .getDefaultScheme(tabComponent, this.tabPane);
1086: cyclePos = fadeState.getFadePosition();
1087: if (fadeState.isFadingIn()) {
1088: ColorScheme temp = colorScheme;
1089: colorScheme = colorScheme2;
1090: colorScheme2 = temp;
1091:
1092: temp = borderScheme;
1093: borderScheme = borderScheme2;
1094: borderScheme2 = temp;
1095: }
1096: }
1097: }
1098:
1099: boolean toSwap = SubstanceCoreUtilities
1100: .toLayoutVertically(this .tabPane);
1101:
1102: switch (tabPlacement) {
1103: case LEFT:
1104: backgroundImage = SubstanceTabbedPaneUI.getTabBackground(
1105: this .tabPane, w, h, isSelected, cyclePos,
1106: toSwap ? SwingConstants.TOP : tabPlacement,
1107: toSwap ? SubstanceConstants.Side.BOTTOM
1108: : SubstanceConstants.Side.RIGHT, colorScheme,
1109: colorScheme2, borderScheme, borderScheme2, false);
1110: // y++;
1111: if (isSelected) {
1112: int fw = backgroundImage.getWidth();
1113: int fh = backgroundImage.getHeight();
1114: SubstanceTheme tabTheme = SubstanceThemeUtilities.getNonColorizedTheme(
1115: this .tabPane, tabIndex);
1116: BufferedImage fade = SubstanceCoreUtilities.getBlankImage(fw,
1117: fh);
1118: Graphics2D fadeGraphics = fade.createGraphics();
1119: fadeGraphics.setColor((tabComponent != null) ? tabComponent
1120: .getBackground() : this .tabPane.getBackground());
1121: fadeGraphics.fillRect(0, 0, fw, fh);
1122: SubstanceLookAndFeel.getCurrentWatermark().drawWatermarkImage(
1123: fadeGraphics, this .tabPane, 0, 0, fw, fh);
1124: fadeGraphics.drawImage(SubstanceTabbedPaneUI.getTabBackground(
1125: this .tabPane, w, h, isSelected, cyclePos,
1126: toSwap ? SwingConstants.TOP : tabPlacement,
1127: toSwap ? SubstanceConstants.Side.BOTTOM
1128: : SubstanceConstants.Side.RIGHT, colorScheme,
1129: colorScheme2, borderScheme, borderScheme2, true), 0, 0, null);
1130:
1131: if (toSwap) {
1132: backgroundImage = SubstanceCoreUtilities
1133: .blendImagesVertical(backgroundImage, fade,
1134: tabTheme.getSelectedTabFadeStart(),
1135: tabTheme.getSelectedTabFadeEnd());
1136: } else {
1137: backgroundImage = SubstanceCoreUtilities
1138: .blendImagesHorizontal(backgroundImage, fade,
1139: tabTheme.getSelectedTabFadeStart(),
1140: tabTheme.getSelectedTabFadeEnd());
1141: }
1142: }
1143: break;
1144: case RIGHT:
1145: backgroundImage = SubstanceTabbedPaneUI.getTabBackground(
1146: this .tabPane, w, h, isSelected, cyclePos,
1147: toSwap ? SwingConstants.BOTTOM : tabPlacement,
1148: toSwap ? SubstanceConstants.Side.BOTTOM
1149: : SubstanceConstants.Side.LEFT, colorScheme,
1150: colorScheme2, borderScheme, borderScheme2, false);
1151: if (isSelected) {
1152: int fw = backgroundImage.getWidth();
1153: int fh = backgroundImage.getHeight();
1154: SubstanceTheme tabTheme = SubstanceThemeUtilities.getNonColorizedTheme(
1155: this .tabPane, tabIndex);
1156: BufferedImage fade = SubstanceCoreUtilities.getBlankImage(fw,
1157: fh);
1158: Graphics2D fadeGraphics = fade.createGraphics();
1159: fadeGraphics.setColor((tabComponent != null) ? tabComponent
1160: .getBackground() : this .tabPane.getBackground());
1161: fadeGraphics.fillRect(0, 0, fw, fh);
1162: SubstanceLookAndFeel.getCurrentWatermark().drawWatermarkImage(
1163: fadeGraphics, this .tabPane, 0, 0, fw, fh);
1164: fadeGraphics.drawImage(SubstanceTabbedPaneUI.getTabBackground(
1165: this .tabPane, w, h, isSelected, cyclePos,
1166: toSwap ? SwingConstants.BOTTOM : tabPlacement,
1167: toSwap ? SubstanceConstants.Side.BOTTOM
1168: : SubstanceConstants.Side.LEFT, colorScheme,
1169: colorScheme2, borderScheme, borderScheme2, true), 0, 0, null);
1170:
1171: if (toSwap) {
1172: backgroundImage = SubstanceCoreUtilities
1173: .blendImagesVertical(backgroundImage, fade,
1174: tabTheme.getSelectedTabFadeStart(),
1175: tabTheme.getSelectedTabFadeEnd());
1176: } else {
1177: backgroundImage = SubstanceCoreUtilities
1178: .blendImagesHorizontal(fade, backgroundImage,
1179: 1.0 - tabTheme.getSelectedTabFadeEnd(),
1180: 1.0 - tabTheme.getSelectedTabFadeStart());
1181: }
1182: }
1183: break;
1184: case BOTTOM:
1185: backgroundImage = SubstanceTabbedPaneUI.getTabBackground(
1186: this .tabPane, w, h, isSelected, cyclePos, tabPlacement,
1187: SubstanceConstants.Side.BOTTOM, colorScheme, colorScheme2,
1188: borderScheme, borderScheme2, false);
1189: backgroundImage = SubstanceImageCreator.getRotated(backgroundImage,
1190: 2);
1191: if (isSelected) {
1192: int fw = backgroundImage.getWidth();
1193: int fh = backgroundImage.getHeight();
1194: SubstanceTheme tabTheme = SubstanceThemeUtilities.getNonColorizedTheme(
1195: this .tabPane, tabIndex);
1196: BufferedImage fade = SubstanceCoreUtilities.getBlankImage(fw,
1197: fh);
1198: Graphics2D fadeGraphics = fade.createGraphics();
1199: fadeGraphics.setColor((tabComponent != null) ? tabComponent
1200: .getBackground() : this .tabPane.getBackground());
1201: fadeGraphics.fillRect(0, 0, fw, fh);
1202: SubstanceLookAndFeel.getCurrentWatermark().drawWatermarkImage(
1203: fadeGraphics, this .tabPane, 0, 0, fw, fh);
1204: fadeGraphics.drawImage(SubstanceImageCreator.getRotated(
1205: SubstanceTabbedPaneUI.getTabBackground(
1206: this .tabPane, w, h, isSelected, cyclePos, tabPlacement,
1207: SubstanceConstants.Side.BOTTOM, colorScheme, colorScheme2,
1208: borderScheme, borderScheme2, true), 2), 0, 0, null);
1209:
1210: backgroundImage = SubstanceCoreUtilities.blendImagesVertical(
1211: fade, backgroundImage, 1.0 - tabTheme
1212: .getSelectedTabFadeEnd(), 1.0 - tabTheme
1213: .getSelectedTabFadeStart());
1214: }
1215: break;
1216: case TOP:
1217: default:
1218: backgroundImage = SubstanceTabbedPaneUI.getTabBackground(
1219: this .tabPane, w, h, isSelected, cyclePos, tabPlacement,
1220: SubstanceConstants.Side.BOTTOM, colorScheme, colorScheme2,
1221: borderScheme, borderScheme2, false);
1222: if (isSelected) {
1223: int fw = backgroundImage.getWidth();
1224: int fh = backgroundImage.getHeight();
1225: SubstanceTheme tabTheme = SubstanceThemeUtilities.getNonColorizedTheme(
1226: this .tabPane, tabIndex);
1227: BufferedImage fade = SubstanceCoreUtilities.getBlankImage(fw,
1228: fh);
1229: Graphics2D fadeGraphics = fade.createGraphics();
1230: fadeGraphics.setColor((tabComponent != null) ? tabComponent
1231: .getBackground() : this .tabPane.getBackground());
1232: fadeGraphics.fillRect(0, 0, fw, fh);
1233: SubstanceLookAndFeel.getCurrentWatermark().drawWatermarkImage(
1234: fadeGraphics, this .tabPane, 0, 0, fw, fh);
1235: fadeGraphics.drawImage(SubstanceTabbedPaneUI.getTabBackground(
1236: this .tabPane, w, h, isSelected, cyclePos, tabPlacement,
1237: SubstanceConstants.Side.BOTTOM, colorScheme, colorScheme2,
1238: borderScheme, borderScheme2, true), 0, 0, null);
1239:
1240: backgroundImage = SubstanceCoreUtilities.blendImagesVertical(
1241: backgroundImage, fade, tabTheme
1242: .getSelectedTabFadeStart(), tabTheme
1243: .getSelectedTabFadeEnd());
1244: }
1245: }
1246:
1247: final BufferedImage result = SubstanceCoreUtilities.getBlankImage(
1248: backgroundImage.getWidth(), backgroundImage.getHeight());
1249: Graphics2D resultGr = result.createGraphics();
1250:
1251: if (backgroundImage != null) {
1252: ControlBackgroundComposite composite = SubstanceCoreUtilities
1253: .getControlBackgroundComposite(this .tabPane);
1254: resultGr.setComposite(composite.getBackgroundComposite(
1255: tabComponent, this .tabPane, tabIndex, hasActivePresence));
1256: resultGr.drawImage(backgroundImage, 0, 0, null);
1257: }
1258:
1259: // Check if requested to paint close buttons.
1260: if (SubstanceCoreUtilities.hasCloseButton(this .tabPane, tabIndex)
1261: && isEnabled) {
1262:
1263: float alpha = (isSelected || isRollover) ? 1.0f : 0.0f;
1264: if (!isSelected
1265: && fadeTracker.isTracked(this .tabPane, tabIndex,
1266: FadeKind.ROLLOVER)) {
1267: alpha = fadeTracker.getFade10(this .tabPane, tabIndex,
1268: FadeKind.ROLLOVER) / 10.0f;
1269: }
1270: if (alpha > 0.0) {
1271: resultGr.setComposite(AlphaComposite.getInstance(
1272: AlphaComposite.SRC_OVER, alpha));
1273:
1274: // paint close button
1275: Rectangle orig = this .getCloseButtonRectangleForDraw(tabIndex,
1276: x, y, w, h);
1277:
1278: boolean toPaintCloseBorder = false;
1279: if (isRollover) {
1280: if (this .substanceMouseLocation != null) {
1281: Rectangle bounds = new Rectangle();
1282: bounds = this .getTabBounds(tabIndex, bounds);
1283: if (toSwap) {
1284: bounds = new Rectangle(bounds.x, bounds.y,
1285: bounds.height, bounds.width);
1286: }
1287: Rectangle rect = this .getCloseButtonRectangleForEvents(
1288: tabIndex, bounds.x, bounds.y, bounds.width,
1289: bounds.height);
1290: // System.out.println("paint " + bounds + " " + rect + "
1291: // "
1292: // + mouseLocation);
1293: if (rect.contains(this .substanceMouseLocation)) {
1294: toPaintCloseBorder = true;
1295: }
1296: }
1297: }
1298:
1299: if (isWindowModified && isEnabled && toMarkModifiedCloseButton) {
1300: colorScheme2 = SubstanceTheme.YELLOW;
1301: colorScheme = SubstanceTheme.ORANGE;
1302: cyclePos = FadeTracker.getInstance().getFade10(
1303: this .tabPane, tabIndex,
1304: ModifiedFadeStep.MARKED_MODIFIED_FADE_KIND);
1305: }
1306:// System.out.println("Close tab icon \n\t" +
1307:// SubstanceCoreUtilities.getSchemeId(colorScheme) + "\n\t" +
1308:// SubstanceCoreUtilities.getSchemeId(colorScheme2) + "\n\t" + cyclePos + ":" +
1309:// alpha + "\n\t" +
1310:// prevState.name() + "->" + currState.name());
1311:
1312: BufferedImage closeButtonImage = SubstanceTabbedPaneUI
1313: .getCloseButtonImage(this .tabPane, orig.width,
1314: orig.height, cyclePos, toPaintCloseBorder,
1315: colorScheme, colorScheme2);
1316: resultGr.drawImage(closeButtonImage, orig.x - x, orig.y - y,
1317: null);
1318: }
1319: }
1320:
1321: final ComponentState state = this .getTabState(tabIndex);
1322:
1323: SubstanceTextPainter textPainter = SubstanceLookAndFeel.getCurrentTextPainter();
1324: textPainter.init(this .tabPane,
1325: this .getTabRectangle(tabIndex), true);
1326: if (textPainter.needsBackgroundImage()) {
1327: textPainter.setBackgroundFill(this .tabPane,
1328: this .tabPane.getBackground(),
1329: true, x, y);
1330: textPainter.attachCallback(new SubstanceTextPainter.BackgroundPaintingCallback() {
1331: public void paintBackground(Graphics g) {
1332: Graphics2D g2d = (Graphics2D)g.create();
1333: g2d.setComposite(TransitionLayout.getAlphaComposite(tabPane,
1334: SubstanceThemeUtilities.getTheme(
1335: tabPane.getComponentAt(tabIndex))
1336: .getThemeAlpha(tabPane.getComponentAt(tabIndex),
1337: state), g));
1338: g2d.drawImage(result, x, y, null);
1339: g2d.dispose();
1340:
1341: // System.out.println("Painted background of " + tabIndex);
1342: }
1343: });
1344: }
1345: else {
1346: graphics.setComposite(TransitionLayout.getAlphaComposite(this .tabPane,
1347: SubstanceThemeUtilities.getTheme(
1348: this .tabPane.getComponentAt(tabIndex))
1349: .getThemeAlpha(this .tabPane.getComponentAt(tabIndex),
1350: state), g));
1351: graphics.drawImage(result, x, y, null);
1352: }
1353: resultGr.dispose();
1354: graphics.dispose();
1355: }
1356:
1357: /*
1358: * (non-Javadoc)
1359: *
1360: * @see javax.swing.plaf.basic.BasicTabbedPaneUI#paintFocusIndicator(java.awt.Graphics,
1361: * int, java.awt.Rectangle[], int, java.awt.Rectangle,
1362: * java.awt.Rectangle, boolean)
1363: */
1364: @Override
1365: protected void paintFocusIndicator(Graphics g, int tabPlacement,
1366: Rectangle[] rects, int tabIndex, Rectangle iconRect,
1367: Rectangle textRect, boolean isSelected) {
1368: // empty to remove Basic functionality
1369: }
1370:
1371: /*
1372: * (non-Javadoc)
1373: *
1374: * @see javax.swing.plaf.basic.BasicTabbedPaneUI#paintTabBorder(java.awt.Graphics,
1375: * int, int, int, int, int, int, boolean)
1376: */
1377: @Override
1378: protected void paintTabBorder(Graphics g, int tabPlacement, int tabIndex,
1379: int x, int y, int w, int h, boolean isSelected) {
1380: // empty to remove Basic functionality
1381: }
1382:
1383: /*
1384: * (non-Javadoc)
1385: *
1386: * @see javax.swing.plaf.basic.BasicTabbedPaneUI#createScrollButton(int)
1387: */
1388: @Override
1389: protected JButton createScrollButton(final int direction) {
1390: SubstanceScrollButton ssb = new SubstanceScrollButton(direction);
1391: Icon icon = new TransitionAwareIcon(ssb, new TransitionAwareIcon.Delegate() {
1392: public Icon getThemeIcon(SubstanceTheme theme) {
1393: // fix for defect 279 - tab pane might not yet have the
1394: // font installed.
1395: int fontSize = SubstanceSizeUtils.getComponentFontSize(tabPane);
1396: return SubstanceImageCreator.getArrowIcon(fontSize, direction,
1397: theme);
1398: }
1399: });
1400: ssb.setIcon(icon);
1401: return ssb;
1402: }
1403:
1404: /*
1405: * (non-Javadoc)
1406: *
1407: * @see javax.swing.plaf.basic.BasicTabbedPaneUI#createChangeListener()
1408: */
1409: @Override
1410: protected ChangeListener createChangeListener() {
1411: return new TabSelectionHandler();
1412: }
1413:
1414: /**
1415: * Handler for tab selection.
1416: *
1417: * @author Kirill Grouchnikov
1418: */
1419: protected class TabSelectionHandler implements ChangeListener {
1420: /*
1421: * (non-Javadoc)
1422: *
1423: * @see javax.swing.event.ChangeListener#stateChanged(javax.swing.event.ChangeEvent)
1424: */
1425: public void stateChanged(ChangeEvent e) {
1426: JTabbedPane tabbedPane = (JTabbedPane) e.getSource();
1427: tabbedPane.revalidate();
1428: tabbedPane.repaint();
1429: }
1430: }
1431:
1432: /*
1433: * (non-Javadoc)
1434: *
1435: * @see javax.swing.plaf.basic.BasicTabbedPaneUI#getTabAreaInsets(int)
1436: */
1437: @Override
1438: protected Insets getTabAreaInsets(int tabPlacement) {
1439: Insets result = super .getTabAreaInsets(tabPlacement);
1440: // result.right += 10;
1441: // result.left += 10;
1442: return result;
1443: }
1444:
1445: /*
1446: * (non-Javadoc)
1447: *
1448: * @see javax.swing.plaf.basic.BasicTabbedPaneUI#getTabInsets(int, int)
1449: */
1450: @Override
1451: protected Insets getTabInsets(int tabPlacement, int tabIndex) {
1452: Insets result = super .getTabInsets(tabPlacement, tabIndex);
1453: // result.right += 10;
1454: return result;
1455: }
1456:
1457: // @Override
1458: // protected int calculateTabAreaWidth(int tabPlacement, int vertRunCount,
1459: // int maxTabWidth) {
1460: // return 20 + super.calculateTabAreaWidth(tabPlacement, vertRunCount,
1461: // maxTabWidth);
1462: // }
1463:
1464: /*
1465: * (non-Javadoc)
1466: *
1467: * @see javax.swing.plaf.basic.BasicTabbedPaneUI#calculateTabHeight(int,
1468: * int, int)
1469: */
1470:
1471: @Override
1472: protected int calculateTabHeight(int tabPlacement, int tabIndex,
1473: int fontHeight) {
1474: boolean toSwap = SubstanceCoreUtilities
1475: .toLayoutVertically(this .tabPane);
1476: if (toSwap)
1477: return this .getTabExtraWidth(tabPlacement, tabIndex)
1478: + super .calculateTabWidth(tabPlacement, tabIndex, this
1479: .getFontMetrics());
1480: return super .calculateTabHeight(tabPlacement, tabIndex, fontHeight);
1481: }
1482:
1483: /*
1484: * (non-Javadoc)
1485: *
1486: * @see javax.swing.plaf.basic.BasicTabbedPaneUI#calculateTabWidth(int, int,
1487: * java.awt.FontMetrics)
1488: */
1489: @Override
1490: protected int calculateTabWidth(int tabPlacement, int tabIndex,
1491: FontMetrics metrics) {
1492: boolean toSwap = SubstanceCoreUtilities
1493: .toLayoutVertically(this .tabPane);
1494: if (toSwap)
1495: return super .calculateTabHeight(tabPlacement, tabIndex, metrics
1496: .getHeight());
1497: int result = this .getTabExtraWidth(tabPlacement, tabIndex)
1498: + super .calculateTabWidth(tabPlacement, tabIndex, metrics);
1499: return result;
1500: }
1501:
1502: /*
1503: * (non-Javadoc)
1504: *
1505: * @see javax.swing.plaf.basic.BasicTabbedPaneUI#calculateMaxTabHeight(int)
1506: */
1507: @Override
1508: protected int calculateMaxTabHeight(int tabPlacement) {
1509: if ((tabPlacement == SwingConstants.TOP)
1510: || (tabPlacement == SwingConstants.BOTTOM))
1511: return super .calculateMaxTabHeight(tabPlacement);
1512: int result = 0;
1513: for (int i = 0; i < this .tabPane.getTabCount(); i++)
1514: result = Math.max(result, this .calculateTabHeight(tabPlacement, i,
1515: this .getFontMetrics().getHeight()));
1516: return result;
1517: }
1518:
1519: /*
1520: * (non-Javadoc)
1521: *
1522: * @see javax.swing.plaf.basic.BasicTabbedPaneUI#getTabRunOverlay(int)
1523: */
1524: @Override
1525: protected int getTabRunOverlay(int tabPlacement) {
1526: boolean toSwap = SubstanceCoreUtilities
1527: .toLayoutVertically(this .tabPane);
1528: if (!toSwap)
1529: return super .getTabRunOverlay(tabPlacement);
1530:
1531: return 0;
1532: }
1533:
1534: /*
1535: * (non-Javadoc)
1536: *
1537: * @see javax.swing.plaf.basic.BasicTabbedPaneUI#paintTab(java.awt.Graphics,
1538: * int, java.awt.Rectangle[], int, java.awt.Rectangle,
1539: * java.awt.Rectangle)
1540: */
1541: @Override
1542: protected void paintTab(Graphics g, int tabPlacement, Rectangle[] rects,
1543: int tabIndex, Rectangle iconRect, Rectangle textRect) {
1544: boolean toSwap = SubstanceCoreUtilities
1545: .toLayoutVertically(this .tabPane);
1546: if (toSwap) {
1547: Graphics2D tempG = (Graphics2D) g.create();
1548: Rectangle tabRect = rects[tabIndex];
1549: Rectangle correctRect = new Rectangle(tabRect.x, tabRect.y,
1550: tabRect.height, tabRect.width);
1551: if (tabPlacement == SwingConstants.LEFT) {
1552: // rotate 90 degrees counterclockwise for LEFT orientation
1553: tempG.rotate(-Math.PI / 2, tabRect.x, tabRect.y);
1554: tempG.translate(-tabRect.height, 0);
1555: } else {
1556: // rotate 90 degrees clockwise for RIGHT orientation
1557: tempG.rotate(Math.PI / 2, tabRect.x, tabRect.y);
1558: tempG.translate(0, -tabRect.getWidth());
1559: }
1560: tempG.setColor(Color.red);
1561: rects[tabIndex] = correctRect;
1562: super .paintTab(tempG, tabPlacement, rects, tabIndex, iconRect,
1563: textRect);
1564: rects[tabIndex] = tabRect;
1565: tempG.dispose();
1566: } else {
1567: super
1568: .paintTab(g, tabPlacement, rects, tabIndex, iconRect,
1569: textRect);
1570: }
1571: // Rectangle rect = new Rectangle();
1572: // rect = getTabBounds(tabIndex, rect);
1573: // Rectangle close = getCloseButtonRectangleForEvents(
1574: // tabIndex, rect.x, rect.y, rect.width,
1575: // rect.height);
1576: // g.setColor(Color.red);
1577: // g.drawRect(close.x, close.y, close.width, close.height);
1578: }
1579:
1580: /*
1581: * (non-Javadoc)
1582: *
1583: * @see javax.swing.plaf.basic.BasicTabbedPaneUI#paintTabArea(java.awt.Graphics,
1584: * int, int)
1585: */
1586: @Override
1587: protected void paintTabArea(Graphics g, int tabPlacement, int selectedIndex) {
1588: // System.out.println("Painting tab area");
1589: this .bgDelegate.updateIfOpaque(g, this .tabPane);
1590: super .paintTabArea(g, tabPlacement, selectedIndex);
1591: }
1592:
1593: /*
1594: * (non-Javadoc)
1595: *
1596: * @see javax.swing.plaf.basic.BasicTabbedPaneUI#getIconForTab(int)
1597: */
1598: @Override
1599: protected Icon getIconForTab(int tabIndex) {
1600: Icon super Result = super .getIconForTab(tabIndex);
1601: if (!SubstanceCoreUtilities.toLayoutVertically(this .tabPane))
1602: return super Result;
1603: if (!SubstanceCoreUtilities.toShowIconUnrotated(this .tabPane, tabIndex))
1604: return super Result;
1605:
1606: boolean rotateClockwise = (this .tabPane.getTabPlacement() == SwingConstants.LEFT);
1607: return new RotatableIcon(super Result, rotateClockwise);
1608: }
1609:
1610: /**
1611: * Retrieves the close button rectangle for drawing purposes.
1612: *
1613: * @param tabIndex
1614: * Tab index.
1615: * @param x
1616: * X coordinate of the tab.
1617: * @param y
1618: * Y coordinate of the tab.
1619: * @param width
1620: * The tab width.
1621: * @param height
1622: * The tab height.
1623: * @return The close button rectangle.
1624: */
1625: protected Rectangle getCloseButtonRectangleForDraw(int tabIndex, int x,
1626: int y, int width, int height) {
1627: int dimension = SubstanceCoreUtilities.getCloseButtonSize(this .tabPane,
1628: tabIndex);
1629:
1630: int borderDelta = (int) Math.ceil(3.0 * SubstanceSizeUtils.getBorderStrokeWidth(
1631: SubstanceSizeUtils.getComponentFontSize(this .tabPane)));
1632:
1633: int xs = this .tabPane.getComponentOrientation().isLeftToRight() ? (x
1634: + width - dimension - borderDelta) : (x + borderDelta);
1635: int ys = y + (height - dimension) / 2 + 1;
1636: return new Rectangle(xs, ys, dimension, dimension);
1637: }
1638:
1639: /**
1640: * Retrieves the close button rectangle for event handling.
1641: *
1642: * @param tabIndex
1643: * Tab index.
1644: * @param x
1645: * X coordinate of the tab.
1646: * @param y
1647: * Y coordinate of the tab.
1648: * @param w
1649: * The tab width.
1650: * @param h
1651: * The tab height.
1652: * @return The close button rectangle.
1653: */
1654: protected Rectangle getCloseButtonRectangleForEvents(int tabIndex, int x,
1655: int y, int w, int h) {
1656: int tabPlacement = this .tabPane.getTabPlacement();
1657: boolean toSwap = SubstanceCoreUtilities
1658: .toLayoutVertically(this .tabPane);
1659: if (!toSwap) {
1660: return this .getCloseButtonRectangleForDraw(tabIndex, x, y, w, h);
1661: }
1662: int dimension = SubstanceCoreUtilities.getCloseButtonSize(this .tabPane,
1663: tabIndex);
1664:
1665: Point2D transCorner = null;
1666: Rectangle rectForDraw = this .getCloseButtonRectangleForDraw(tabIndex,
1667: x, y, h, w);
1668: if (tabPlacement == SwingConstants.LEFT) {
1669: AffineTransform trans = new AffineTransform();
1670: trans.rotate(-Math.PI / 2, x, y);
1671: trans.translate(-h, 0);
1672: Point2D.Double origCorner = new Point2D.Double(rectForDraw
1673: .getMaxX(), rectForDraw.getMinY());
1674: transCorner = trans.transform(origCorner, null);
1675: } else {
1676: // rotate 90 degrees clockwise for RIGHT orientation
1677: AffineTransform trans = new AffineTransform();
1678: trans.rotate(Math.PI / 2, x, y);
1679: trans.translate(0, -w);
1680: Point2D.Double origCorner = new Point2D.Double(rectForDraw
1681: .getMinX(), rectForDraw.getMaxY());
1682: transCorner = trans.transform(origCorner, null);
1683: }
1684: return new Rectangle((int) transCorner.getX(),
1685: (int) transCorner.getY(), dimension, dimension);
1686: }
1687:
1688: /**
1689: * Implementation of the fade tracker callback that repaints a single tab.
1690: *
1691: * @author Kirill Grouchnikov
1692: */
1693: protected class TabRepaintCallback implements FadeTrackerCallback {
1694: /**
1695: * The associated tabbed pane.
1696: */
1697: protected JTabbedPane tabbedPane;
1698:
1699: /**
1700: * The associated tab index.
1701: */
1702: protected int tabIndex;
1703:
1704: /**
1705: * Creates new tab repaint callback.
1706: *
1707: * @param tabPane
1708: * The associated tabbed pane.
1709: * @param tabIndex
1710: * The associated tab index.
1711: */
1712: public TabRepaintCallback(JTabbedPane tabPane, int tabIndex) {
1713: this .tabbedPane = tabPane;
1714: this .tabIndex = tabIndex;
1715: }
1716:
1717: /*
1718: * (non-Javadoc)
1719: *
1720: * @see org.jvnet.lafwidget.utils.FadeTracker$FadeTrackerCallback#fadePerformed(org.jvnet.lafwidget.utils.FadeTracker.FadeKind,
1721: * float)
1722: */
1723: public void fadePerformed(FadeKind fadeKind, float fade10) {
1724: if ((SubstanceTabbedPaneUI.this .tabPane == this .tabbedPane)
1725: && (this .tabIndex < this .tabbedPane.getTabCount())) {
1726: SubstanceTabbedPaneUI.this .nextStateMap.put(this .tabIndex,
1727: SubstanceTabbedPaneUI.this .getTabState(this .tabIndex));
1728: }
1729: this .repaintTab();
1730: }
1731:
1732: /*
1733: * (non-Javadoc)
1734: *
1735: * @see org.jvnet.lafwidget.animation.FadeTrackerAdapter#fadeEnded(org.jvnet.lafwidget.animation.FadeKind)
1736: */
1737: public void fadeEnded(FadeKind fadeKind) {
1738: if ((SubstanceTabbedPaneUI.this .tabPane == this .tabbedPane)
1739: && (this .tabIndex < this .tabbedPane.getTabCount())) {
1740: SubstanceTabbedPaneUI.this .prevStateMap.put(this .tabIndex,
1741: SubstanceTabbedPaneUI.this .getTabState(this .tabIndex));
1742: SubstanceTabbedPaneUI.this .nextStateMap.put(this .tabIndex,
1743: SubstanceTabbedPaneUI.this .getTabState(this .tabIndex));
1744: // System.out.println(tabIndex + "->"
1745: // + prevStateMap.get(tabIndex).name());
1746: }
1747: this .repaintTab();
1748: }
1749:
1750: /*
1751: * (non-Javadoc)
1752: *
1753: * @see org.jvnet.lafwidget.animation.FadeTrackerAdapter#fadeReversed(org.jvnet.lafwidget.animation.FadeKind,
1754: * boolean, float)
1755: */
1756: public void fadeReversed(FadeKind fadeKind, boolean isFadingIn,
1757: float fadeCycle10) {
1758: if ((SubstanceTabbedPaneUI.this .tabPane == this .tabbedPane)
1759: && (this .tabIndex < this .tabbedPane.getTabCount())) {
1760: ComponentState nextState = SubstanceTabbedPaneUI.this .nextStateMap
1761: .get(this .tabIndex);
1762: SubstanceTabbedPaneUI.this .prevStateMap.put(this .tabIndex,
1763: nextState);
1764: // System.out.println(tabIndex + "->"
1765: // + prevStateMap.get(tabIndex).name());
1766: }
1767: this .repaintTab();
1768: }
1769:
1770: /**
1771: * Repaints the relevant tab.
1772: */
1773: protected void repaintTab() {
1774: SwingUtilities.invokeLater(new Runnable() {
1775: public void run() {
1776: if (SubstanceTabbedPaneUI.this .tabPane == null) {
1777: // may happen if the LAF was switched in the meantime
1778: return;
1779: }
1780: SubstanceTabbedPaneUI.this .ensureCurrentLayout();
1781: int tabCount = SubstanceTabbedPaneUI.this .tabPane
1782: .getTabCount();
1783: if ((tabCount > 0)
1784: && (TabRepaintCallback.this .tabIndex < tabCount)
1785: && (TabRepaintCallback.this .tabIndex < SubstanceTabbedPaneUI.this .rects.length)) {
1786: // need to retrieve the tab rectangle since the tabs
1787: // can be moved while animating (especially when the
1788: // current layout is SCROLL_LAYOUT)
1789: Rectangle rect = SubstanceTabbedPaneUI.this
1790: .getTabBounds(
1791: SubstanceTabbedPaneUI.this .tabPane,
1792: TabRepaintCallback.this .tabIndex);
1793: // System.out.println("Repainting " + tabIndex);
1794: SubstanceTabbedPaneUI.this .tabPane.repaint(rect);
1795: }
1796: }
1797: });
1798: }
1799: }
1800:
1801: /**
1802: * Ensures the current layout.
1803: */
1804: protected void ensureCurrentLayout() {
1805: if (!this .tabPane.isValid()) {
1806: this .tabPane.validate();
1807: }
1808: /*
1809: * If tabPane doesn't have a peer yet, the validate() call will silently
1810: * fail. We handle that by forcing a layout if tabPane is still invalid.
1811: * See bug 4237677.
1812: */
1813: if (!this .tabPane.isValid()) {
1814: LayoutManager lm = this .tabPane.getLayout();
1815: if (lm instanceof BasicTabbedPaneUI.TabbedPaneLayout) {
1816: BasicTabbedPaneUI.TabbedPaneLayout layout = (BasicTabbedPaneUI.TabbedPaneLayout) lm;
1817: layout.calculateLayoutInfo();
1818: } else {
1819: if (lm instanceof TransitionLayout) {
1820: lm = ((TransitionLayout) lm).getDelegate();
1821: if (lm instanceof TabbedPaneLayout) {
1822: TabbedPaneLayout layout = (TabbedPaneLayout) lm;
1823: layout.calculateLayoutInfo();
1824: }
1825: }
1826: }
1827: }
1828: }
1829:
1830: /**
1831: * Returns the repaint callback for the specified tab index.
1832: *
1833: * @param tabIndex
1834: * Tab index.
1835: * @return Repaint callback for the specified tab index.
1836: */
1837: public FadeTrackerCallback getCallback(int tabIndex) {
1838: return new TabRepaintCallback(this .tabPane, tabIndex);
1839: }
1840:
1841: /**
1842: * Tries closing tabs based on the specified tab index and tab close kind.
1843: *
1844: * @param tabIndex
1845: * Tab index.
1846: * @param tabCloseKind
1847: * Tab close kind.
1848: */
1849: protected void tryCloseTabs(int tabIndex, TabCloseKind tabCloseKind) {
1850: if (tabCloseKind == null)
1851: return;
1852: if (tabCloseKind == TabCloseKind.NONE)
1853: return;
1854:
1855: if (tabCloseKind == TabCloseKind.ALL_BUT_THIS) {
1856: // close all but this
1857: Set<Integer> indexes = new HashSet<Integer>();
1858: for (int i = 0; i < this .tabPane.getTabCount(); i++)
1859: if (i != tabIndex)
1860: indexes.add(i);
1861: this .tryCloseTabs(indexes);
1862: return;
1863: }
1864: if (tabCloseKind == TabCloseKind.ALL) {
1865: // close all
1866: Set<Integer> indexes = new HashSet<Integer>();
1867: for (int i = 0; i < this .tabPane.getTabCount(); i++)
1868: indexes.add(i);
1869: this .tryCloseTabs(indexes);
1870: return;
1871: }
1872: this .tryCloseTab(tabIndex);
1873: }
1874:
1875: /**
1876: * Tries closing a single tab.
1877: *
1878: * @param tabIndex
1879: * Tab index.
1880: */
1881: protected void tryCloseTab(int tabIndex) {
1882: Component component = this .tabPane.getComponentAt(tabIndex);
1883: Set<Component> componentSet = new HashSet<Component>();
1884: componentSet.add(component);
1885:
1886: // check if there's at least one listener
1887: // that vetoes the closing
1888: boolean isVetoed = false;
1889: for (BaseTabCloseListener listener : SubstanceLookAndFeel
1890: .getAllTabCloseListeners(this .tabPane)) {
1891: if (listener instanceof VetoableTabCloseListener) {
1892: VetoableTabCloseListener vetoableListener = (VetoableTabCloseListener) listener;
1893: isVetoed = isVetoed
1894: || vetoableListener.vetoTabClosing(this .tabPane,
1895: component);
1896: }
1897: if (listener instanceof VetoableMultipleTabCloseListener) {
1898: VetoableMultipleTabCloseListener vetoableListener = (VetoableMultipleTabCloseListener) listener;
1899: isVetoed = isVetoed
1900: || vetoableListener.vetoTabsClosing(this .tabPane,
1901: componentSet);
1902: }
1903: }
1904: if (isVetoed)
1905: return;
1906:
1907: for (BaseTabCloseListener listener : SubstanceLookAndFeel
1908: .getAllTabCloseListeners(this .tabPane)) {
1909: if (listener instanceof TabCloseListener)
1910: ((TabCloseListener) listener).tabClosing(this .tabPane,
1911: component);
1912: if (listener instanceof MultipleTabCloseListener)
1913: ((MultipleTabCloseListener) listener).tabsClosing(this .tabPane,
1914: componentSet);
1915: }
1916:
1917: this .tabPane.remove(tabIndex);
1918: if (this .tabPane.getTabCount() > 0) {
1919: this .selectPreviousTab(0);
1920: this .selectNextTab(this .tabPane.getSelectedIndex());
1921: }
1922: this .tabPane.repaint();
1923:
1924: for (BaseTabCloseListener listener : SubstanceLookAndFeel
1925: .getAllTabCloseListeners(this .tabPane)) {
1926: if (listener instanceof TabCloseListener)
1927: ((TabCloseListener) listener)
1928: .tabClosed(this .tabPane, component);
1929: if (listener instanceof MultipleTabCloseListener)
1930: ((MultipleTabCloseListener) listener).tabsClosed(this .tabPane,
1931: componentSet);
1932: }
1933: }
1934:
1935: /**
1936: * Tries closing the specified tabs.
1937: *
1938: * @param tabIndexes
1939: * Tab indexes.
1940: */
1941: protected void tryCloseTabs(Set<Integer> tabIndexes) {
1942: Set<Component> componentSet = new HashSet<Component>();
1943: for (int tabIndex : tabIndexes) {
1944: componentSet.add(this .tabPane.getComponentAt(tabIndex));
1945: }
1946:
1947: // check if there's at least one listener
1948: // that vetoes the closing
1949: boolean isVetoed = false;
1950: for (BaseTabCloseListener listener : SubstanceLookAndFeel
1951: .getAllTabCloseListeners(this .tabPane)) {
1952: if (listener instanceof VetoableMultipleTabCloseListener) {
1953: VetoableMultipleTabCloseListener vetoableListener = (VetoableMultipleTabCloseListener) listener;
1954: isVetoed = isVetoed
1955: || vetoableListener.vetoTabsClosing(this .tabPane,
1956: componentSet);
1957: }
1958: }
1959: if (isVetoed)
1960: return;
1961:
1962: for (BaseTabCloseListener listener : SubstanceLookAndFeel
1963: .getAllTabCloseListeners(this .tabPane)) {
1964: if (listener instanceof MultipleTabCloseListener)
1965: ((MultipleTabCloseListener) listener).tabsClosing(this .tabPane,
1966: componentSet);
1967: }
1968:
1969: for (Component toRemove : componentSet) {
1970: this .tabPane.remove(toRemove);
1971: }
1972:
1973: if (this .tabPane.getTabCount() > 0) {
1974: this .selectPreviousTab(0);
1975: this .selectNextTab(this .tabPane.getSelectedIndex());
1976: }
1977: this .tabPane.repaint();
1978:
1979: for (BaseTabCloseListener listener : SubstanceLookAndFeel
1980: .getAllTabCloseListeners(this .tabPane)) {
1981: if (listener instanceof MultipleTabCloseListener)
1982: ((MultipleTabCloseListener) listener).tabsClosed(this .tabPane,
1983: componentSet);
1984: }
1985: }
1986:
1987: /*
1988: * (non-Javadoc)
1989: *
1990: * @see javax.swing.plaf.basic.BasicTabbedPaneUI#layoutLabel(int,
1991: * java.awt.FontMetrics, int, java.lang.String, javax.swing.Icon,
1992: * java.awt.Rectangle, java.awt.Rectangle, java.awt.Rectangle, boolean)
1993: */
1994: @Override
1995: protected void layoutLabel(int tabPlacement, FontMetrics metrics,
1996: int tabIndex, String title, Icon icon, Rectangle tabRect,
1997: Rectangle iconRect, Rectangle textRect, boolean isSelected) {
1998: textRect.x = textRect.y = iconRect.x = iconRect.y = 0;
1999:
2000: View v = this .getTextViewForTab(tabIndex);
2001: if (v != null) {
2002: this .tabPane.putClientProperty("html", v);
2003: }
2004:
2005: SwingUtilities.layoutCompoundLabel(this .tabPane, metrics, title, icon,
2006: SwingConstants.CENTER, this .getTextAlignment(tabPlacement),
2007: SwingConstants.CENTER, SwingConstants.TRAILING, tabRect,
2008: iconRect, textRect, this .textIconGap);
2009:
2010: this .tabPane.putClientProperty("html", null);
2011:
2012: int xNudge = this .getTabLabelShiftX(tabPlacement, tabIndex, isSelected);
2013: int yNudge = this .getTabLabelShiftY(tabPlacement, tabIndex, isSelected);
2014: iconRect.x += xNudge;
2015: iconRect.y += yNudge;
2016: textRect.x += xNudge;
2017: textRect.y += yNudge;
2018: }
2019:
2020: /**
2021: * Returns the text alignment for the specified tab placement.
2022: *
2023: * @param tabPlacement
2024: * Tab placement.
2025: * @return Tab text alignment.
2026: */
2027: protected int getTextAlignment(int tabPlacement) {
2028: TabTextAlignmentKind textAlignmentKind = SubstanceCoreUtilities
2029: .getTabTextAlignmentKind(this .tabPane);
2030:
2031: if (SubstanceCoreUtilities.toLayoutVertically(this .tabPane)) {
2032: return SwingConstants.CENTER;
2033: }
2034:
2035: if ((tabPlacement == SwingConstants.LEFT)) {
2036: switch (textAlignmentKind) {
2037: case ALWAYS_LEFT:
2038: case FOLLOW_PLACEMENT:
2039: return SwingConstants.LEFT;
2040: case FOLLOW_ORIENTATION:
2041: if (this .tabPane.getComponentOrientation().isLeftToRight())
2042: return SwingConstants.LEFT;
2043: else
2044: return SwingConstants.RIGHT;
2045: case ALWAYS_RIGHT:
2046: return SwingConstants.RIGHT;
2047: }
2048: }
2049: if ((tabPlacement == SwingConstants.RIGHT)) {
2050: switch (textAlignmentKind) {
2051: case ALWAYS_RIGHT:
2052: case FOLLOW_PLACEMENT:
2053: return SwingConstants.RIGHT;
2054: case FOLLOW_ORIENTATION:
2055: if (this .tabPane.getComponentOrientation().isLeftToRight())
2056: return SwingConstants.LEFT;
2057: else
2058: return SwingConstants.RIGHT;
2059: case ALWAYS_LEFT:
2060: return SwingConstants.LEFT;
2061: }
2062: }
2063:
2064: return SwingConstants.CENTER;
2065: }
2066:
2067: /*
2068: * (non-Javadoc)
2069: *
2070: * @see javax.swing.plaf.basic.BasicTabbedPaneUI#getTabLabelShiftX(int, int,
2071: * boolean)
2072: */
2073: @Override
2074: protected int getTabLabelShiftX(int tabPlacement, int tabIndex,
2075: boolean isSelected) {
2076: int textAlignment = this .getTextAlignment(tabPlacement);
2077: int delta = 0;
2078: if (textAlignment == SwingConstants.LEFT)
2079: delta = 10;
2080: if (textAlignment == SwingConstants.RIGHT)
2081: delta = -10
2082: - SubstanceCoreUtilities.getCloseButtonSize(this .tabPane,
2083: tabIndex);
2084: if ((textAlignment == SwingConstants.CENTER)
2085: && (SubstanceCoreUtilities.hasCloseButton(this .tabPane,
2086: tabIndex))) {
2087: if (this .tabPane.getComponentOrientation().isLeftToRight()) {
2088: delta = 5 - SubstanceCoreUtilities.getCloseButtonSize(
2089: this .tabPane, tabIndex);
2090: } else {
2091: delta = SubstanceCoreUtilities.getCloseButtonSize(this .tabPane,
2092: tabIndex) - 5;
2093: }
2094: }
2095:// System.out.println("Delta for " + tabPane.getTitleAt(tabIndex) + ":" +
2096:// delta);
2097: return delta
2098: + super .getTabLabelShiftX(tabPlacement, tabIndex, isSelected);
2099: }
2100:
2101: /*
2102: * (non-Javadoc)
2103: *
2104: * @see javax.swing.plaf.basic.BasicTabbedPaneUI#getTabLabelShiftY(int, int,
2105: * boolean)
2106: */
2107: @Override
2108: protected int getTabLabelShiftY(int tabPlacement, int tabIndex,
2109: boolean isSelected) {
2110: int result = 0;
2111: if (tabPlacement == SwingConstants.BOTTOM)
2112: result = -1;
2113: else
2114: result = 1;
2115: return result;
2116: }
2117:
2118: /**
2119: * Returns extra width for the specified tab.
2120: *
2121: * @param tabPlacement
2122: * Tab placement.
2123: * @param tabIndex
2124: * Tab index.
2125: * @return Extra width for the specified tab.
2126: */
2127: protected int getTabExtraWidth(int tabPlacement, int tabIndex) {
2128: int extraWidth = 0;
2129: SubstanceButtonShaper shaper = SubstanceLookAndFeel
2130: .getCurrentButtonShaper();
2131: if (shaper instanceof ClassicButtonShaper)
2132: extraWidth = (int) (2.0 * SubstanceSizeUtils.getClassicButtonCornerRadius(
2133: SubstanceSizeUtils.getComponentFontSize(this .tabPane)));
2134: else
2135: extraWidth = super .calculateTabHeight(tabPlacement, tabIndex, this
2136: .getFontMetrics().getHeight()) / 3;
2137:
2138: if (SubstanceCoreUtilities.hasCloseButton(this .tabPane, tabIndex)
2139: && this .tabPane.isEnabledAt(tabIndex)) {
2140: extraWidth += (4 + SubstanceCoreUtilities.getCloseButtonSize(
2141: this .tabPane, tabIndex));
2142: }
2143:
2144: // System.out.println(tabPane.getTitleAt(tabIndex) + ":" + extraWidth);
2145: return extraWidth;
2146: }
2147:
2148: /**
2149: * Returns the index of the tab currently being rolled-over.
2150: *
2151: * @return Index of the tab currently being rolled-over.
2152: */
2153: public int getRolloverTabIndex() {
2154: return this .getRolloverTab();
2155: }
2156:
2157: /**
2158: * Sets new value for tab area insets.
2159: *
2160: * @param insets
2161: * Tab area insets.
2162: */
2163: public void setTabAreaInsets(Insets insets) {
2164: this .tabAreaInsets = insets;
2165: }
2166:
2167: /**
2168: * Returns tab area insets.
2169: *
2170: * @return Tab area insets.
2171: */
2172: public Insets getTabAreaInsets() {
2173: return this .tabAreaInsets;
2174: }
2175:
2176: /**
2177: * Returns the tab rectangle for the specified tab.
2178: *
2179: * @param tabIndex
2180: * Index of a tab.
2181: * @return The tab rectangle for the specified parameters.
2182: */
2183: public Rectangle getTabRectangle(int tabIndex) {
2184: return this .rects[tabIndex];
2185: }
2186:
2187: /**
2188: * Returns the memory usage string.
2189: *
2190: * @return The memory usage string.
2191: */
2192: public static String getMemoryUsage() {
2193: StringBuffer sb = new StringBuffer();
2194: sb.append("SubstanceTabbedPaneUI: \n");
2195: sb.append("\t" + SubstanceTabbedPaneUI.backgroundMap.size()
2196: + " backgrounds");
2197: return sb.toString();
2198: }
2199:
2200: /*
2201: * (non-Javadoc)
2202: *
2203: * @see javax.swing.plaf.basic.BasicTabbedPaneUI#shouldPadTabRun(int, int)
2204: */
2205: @Override
2206: protected boolean shouldPadTabRun(int tabPlacement, int run) {
2207: // Don't pad last run
2208: return this .runCount > 1 && run < this .runCount - 1;
2209: }
2210:
2211: /*
2212: * (non-Javadoc)
2213: *
2214: * @see javax.swing.plaf.basic.BasicTabbedPaneUI#createLayoutManager()
2215: */
2216: @Override
2217: protected LayoutManager createLayoutManager() {
2218: if (this .tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) {
2219: return super .createLayoutManager();
2220: }
2221: return new TabbedPaneLayout();
2222: }
2223:
2224: /**
2225: * Layout for the tabbed pane.
2226: *
2227: * @author Kirill Grouchnikov
2228: */
2229: public class TabbedPaneLayout extends BasicTabbedPaneUI.TabbedPaneLayout {
2230: /**
2231: * Creates a new layout.
2232: */
2233: public TabbedPaneLayout() {
2234: SubstanceTabbedPaneUI.this .super ();
2235: }
2236:
2237: /*
2238: * (non-Javadoc)
2239: *
2240: * @see javax.swing.plaf.basic.BasicTabbedPaneUI$TabbedPaneLayout#normalizeTabRuns(int,
2241: * int, int, int)
2242: */
2243: @Override
2244: protected void normalizeTabRuns(int tabPlacement, int tabCount,
2245: int start, int max) {
2246: // Only normalize the runs for top & bottom; normalizing
2247: // doesn't look right for Metal's vertical tabs
2248: // because the last run isn't padded and it looks odd to have
2249: // fat tabs in the first vertical runs, but slimmer ones in the
2250: // last (this effect isn't noticeable for horizontal tabs).
2251: if (tabPlacement == TOP || tabPlacement == BOTTOM) {
2252: super .normalizeTabRuns(tabPlacement, tabCount, start, max);
2253: }
2254: }
2255:
2256: /*
2257: * (non-Javadoc)
2258: *
2259: * @see javax.swing.plaf.basic.BasicTabbedPaneUI$TabbedPaneLayout#rotateTabRuns(int,
2260: * int)
2261: */
2262: @Override
2263: protected void rotateTabRuns(int tabPlacement, int selectedRun) {
2264: // Don't rotate runs!
2265: }
2266:
2267: /*
2268: * (non-Javadoc)
2269: *
2270: * @see javax.swing.plaf.basic.BasicTabbedPaneUI$TabbedPaneLayout#padSelectedTab(int,
2271: * int)
2272: */
2273: @Override
2274: protected void padSelectedTab(int tabPlacement, int selectedIndex) {
2275: // Don't pad selected tab
2276: }
2277: }
2278:
2279: /*
2280: * (non-Javadoc)
2281: *
2282: * @see javax.swing.plaf.basic.BasicTabbedPaneUI#getContentBorderInsets(int)
2283: */
2284: @Override
2285: protected Insets getContentBorderInsets(int tabPlacement) {
2286: Insets insets = SubstanceSizeUtils.getTabbedPaneContentInsets(SubstanceSizeUtils.getComponentFontSize(this .tabPane));
2287:
2288: TabContentPaneBorderKind kind = SubstanceCoreUtilities
2289: .getContentBorderKind(this .tabPane);
2290: boolean isDouble = (kind == TabContentPaneBorderKind.DOUBLE_FULL) ||
2291: (kind == TabContentPaneBorderKind.DOUBLE_PLACEMENT);
2292: boolean isPlacement = (kind == TabContentPaneBorderKind.SINGLE_PLACEMENT) ||
2293: (kind == TabContentPaneBorderKind.DOUBLE_PLACEMENT);
2294: int delta = isDouble ? (int) (3.0 * SubstanceSizeUtils.getBorderStrokeWidth(SubstanceSizeUtils
2295: .getComponentFontSize(tabPane)))
2296: : 0;
2297:
2298: if (isPlacement) {
2299: switch (tabPlacement) {
2300: case TOP:
2301: return new Insets(insets.top + delta, 0, 0, 0);
2302: case LEFT:
2303: return new Insets(0, insets.left + delta, 0, 0);
2304: case RIGHT:
2305: return new Insets(0, 0, 0, insets.right + delta);
2306: case BOTTOM:
2307: return new Insets(0, 0, insets.bottom + delta, 0);
2308: }
2309: } else {
2310: switch (tabPlacement) {
2311: case TOP:
2312: return new Insets(insets.top + delta, insets.left,
2313: insets.bottom, insets.right);
2314: case LEFT:
2315: return new Insets(insets.top, insets.left + delta,
2316: insets.bottom, insets.right);
2317: case RIGHT:
2318: return new Insets(insets.top, insets.left, insets.bottom,
2319: insets.right + delta);
2320: case BOTTOM:
2321: return new Insets(insets.top, insets.left, insets.bottom
2322: + delta, insets.right);
2323: }
2324: }
2325: return insets;
2326: }
2327:
2328: /*
2329: * (non-Javadoc)
2330: *
2331: * @see javax.swing.plaf.basic.BasicTabbedPaneUI#paintContentBorderBottomEdge(java.awt.Graphics,
2332: * int, int, int, int, int, int)
2333: */
2334: @Override
2335: protected void paintContentBorderBottomEdge(Graphics g, int tabPlacement,
2336: int selectedIndex, int x, int y, int w, int h) {
2337: TabContentPaneBorderKind kind = SubstanceCoreUtilities
2338: .getContentBorderKind(this .tabPane);
2339: boolean isDouble = (kind == TabContentPaneBorderKind.DOUBLE_FULL) ||
2340: (kind == TabContentPaneBorderKind.DOUBLE_PLACEMENT);
2341: boolean isPlacement = (kind == TabContentPaneBorderKind.SINGLE_PLACEMENT) ||
2342: (kind == TabContentPaneBorderKind.DOUBLE_PLACEMENT);
2343: if (isPlacement) {
2344: if (tabPlacement != SwingConstants.BOTTOM)
2345: return;
2346: }
2347: int ribbonDelta = (int) (2.0 * SubstanceSizeUtils.getBorderStrokeWidth(SubstanceSizeUtils
2348: .getComponentFontSize(tabPane)));
2349:
2350: Rectangle selRect = selectedIndex < 0 ? null : this .getTabBounds(
2351: selectedIndex, this .calcRect);
2352:
2353: Graphics2D g2d = (Graphics2D) g.create();
2354: g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
2355: RenderingHints.VALUE_ANTIALIAS_ON);
2356: g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
2357: RenderingHints.VALUE_STROKE_NORMALIZE);
2358: float strokeWidth = SubstanceSizeUtils.getBorderStrokeWidth(SubstanceSizeUtils
2359: .getComponentFontSize(tabPane));
2360: int joinKind = BasicStroke.JOIN_ROUND;
2361: int capKind = BasicStroke.CAP_BUTT;
2362: g2d.setStroke(new BasicStroke(strokeWidth, capKind, joinKind));
2363: int offset = (int) (strokeWidth / 2.0);
2364:
2365: boolean isUnbroken = (tabPlacement != BOTTOM || selectedIndex < 0 || (selRect.y - 1 > h)
2366: || (selRect.x < x || selRect.x > x + w));
2367:
2368: x += offset;
2369: y += offset;
2370: w -= 2 * offset;
2371: h -= 2 * offset;
2372:
2373: // Draw unbroken line if tabs are not on BOTTOM, OR
2374: // selected tab is not in run adjacent to content, OR
2375: // selected tab is not visible (SCROLL_TAB_LAYOUT)
2376: if (isUnbroken) {
2377: g2d.setColor(this .highlight);
2378: g2d.drawLine(x, y + h - 1, x + w - 1, y + h - 1);
2379: } else {
2380: // Break line to show visual connection to selected tab
2381: SubstanceButtonShaper shaper = SubstanceLookAndFeel
2382: .getCurrentButtonShaper();
2383: int delta = (shaper instanceof ClassicButtonShaper) ? 1 : 0;
2384: int borderInsets = (int) Math.floor(SubstanceSizeUtils
2385: .getBorderStrokeWidth(SubstanceSizeUtils
2386: .getComponentFontSize(tabPane)) / 2.0);
2387: GeneralPath bottomOutline = new GeneralPath();
2388: bottomOutline.moveTo(x, y + h - 1);
2389: bottomOutline.lineTo(selRect.x + borderInsets, y + h - 1);
2390: int bumpHeight = super .calculateTabHeight(tabPlacement, 0,
2391: SubstanceSizeUtils.getComponentFontSize(this .tabPane)) / 2;
2392: bottomOutline.lineTo(selRect.x + borderInsets, y + h + bumpHeight);
2393: if (selRect.x + selRect.width < x + w - 1) {
2394: int selectionEndX = selRect.x + selRect.width - delta - 1 - borderInsets;
2395: bottomOutline.lineTo(selectionEndX, y + h - 1 + bumpHeight);
2396: bottomOutline.lineTo(selectionEndX, y + h - 1);
2397: bottomOutline.lineTo(x + w - 1, y + h - 1);
2398: }
2399: g2d.setPaint(new GradientPaint(x, y + h - 1, this .darkShadow, x,
2400: y + h - 1 + bumpHeight,
2401: SubstanceColorUtilities.getAlphaColor(this .darkShadow, 0)));
2402: g2d.draw(bottomOutline);
2403: }
2404:
2405: if (isDouble) {
2406: if (tabPlacement == BOTTOM) {
2407: g2d.setColor(this .highlight);
2408: // g2d.drawLine(x+1, y + h - 2 - ribbonDelta, x + w - 2,
2409: // y + h - 2 - ribbonDelta);
2410: g2d.setColor(this .darkShadow);
2411: g2d.drawLine(x, y + h - 1 - ribbonDelta, x + w - 1,
2412: y + h - 1 - ribbonDelta);
2413: }
2414: if (tabPlacement == LEFT) {
2415: g2d.setPaint(new GradientPaint(x, y + h - 1, this .darkShadow,
2416: x + 4 * ribbonDelta, y + h - 1, this .highlight));
2417: g2d.drawLine(x, y + h - 1, x + 4 * ribbonDelta, y + h - 1);
2418: }
2419: if (tabPlacement == RIGHT) {
2420: g2d.setPaint(new GradientPaint(x + w - 1 - 4 * ribbonDelta,
2421: y + h - 1,
2422: this .highlight,
2423: x + w - 1, y + h - 1, this .darkShadow));
2424: g2d.drawLine(x + w - 1 - 4 * ribbonDelta, y + h - 1,
2425: x + w - 1, y + h - 1);
2426: }
2427: }
2428:
2429: g2d.dispose();
2430: }
2431:
2432: /*
2433: * (non-Javadoc)
2434: *
2435: * @see javax.swing.plaf.basic.BasicTabbedPaneUI#paintContentBorderLeftEdge(java.awt.Graphics,
2436: * int, int, int, int, int, int)
2437: */
2438: @Override
2439: protected void paintContentBorderLeftEdge(Graphics g, int tabPlacement,
2440: int selectedIndex, int x, int y, int w, int h) {
2441: TabContentPaneBorderKind kind = SubstanceCoreUtilities
2442: .getContentBorderKind(this .tabPane);
2443: boolean isDouble = (kind == TabContentPaneBorderKind.DOUBLE_FULL) ||
2444: (kind == TabContentPaneBorderKind.DOUBLE_PLACEMENT);
2445: boolean isPlacement = (kind == TabContentPaneBorderKind.SINGLE_PLACEMENT) ||
2446: (kind == TabContentPaneBorderKind.DOUBLE_PLACEMENT);
2447: if (isPlacement) {
2448: if (tabPlacement != SwingConstants.LEFT)
2449: return;
2450: }
2451: int ribbonDelta = (int) (3.0 * SubstanceSizeUtils.getBorderStrokeWidth(SubstanceSizeUtils
2452: .getComponentFontSize(tabPane)));
2453:
2454: Rectangle selRect = selectedIndex < 0 ? null : this .getTabBounds(
2455: selectedIndex, this .calcRect);
2456:
2457: Graphics2D g2d = (Graphics2D) g.create();
2458: g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
2459: RenderingHints.VALUE_ANTIALIAS_ON);
2460: g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
2461: RenderingHints.VALUE_STROKE_NORMALIZE);
2462: float strokeWidth = SubstanceSizeUtils.getBorderStrokeWidth(SubstanceSizeUtils
2463: .getComponentFontSize(tabPane));
2464: int joinKind = BasicStroke.JOIN_ROUND;
2465: int capKind = BasicStroke.CAP_BUTT;
2466: g2d.setStroke(new BasicStroke(strokeWidth, capKind, joinKind));
2467: int offset = (int) (strokeWidth / 2.0);
2468:
2469: boolean isUnbroken = (tabPlacement != LEFT || selectedIndex < 0
2470: || (selRect.x + selRect.width + 1 < x)
2471: || (selRect.y < y || selRect.y > y + h));
2472:
2473: x += offset;
2474: y += offset;
2475: w -= 2 * offset;
2476: h -= 2 * offset;
2477:
2478: // Draw unbroken line if tabs are not on LEFT, OR
2479: // selected tab is not in run adjacent to content, OR
2480: // selected tab is not visible (SCROLL_TAB_LAYOUT)
2481: if (isUnbroken) {
2482: g2d.setColor(this .highlight);
2483: g2d.drawLine(x, y, x, y + h);
2484: } else {
2485: // Break line to show visual connection to selected tab
2486: SubstanceButtonShaper shaper = SubstanceLookAndFeel
2487: .getCurrentButtonShaper();
2488: int delta = (shaper instanceof ClassicButtonShaper) ? 1 : 0;
2489:
2490: int borderInsets = (int) Math.floor(SubstanceSizeUtils
2491: .getBorderStrokeWidth(SubstanceSizeUtils
2492: .getComponentFontSize(tabPane)) / 2.0);
2493: GeneralPath leftOutline = new GeneralPath();
2494: leftOutline.moveTo(x, y);
2495: leftOutline.lineTo(x, selRect.y + borderInsets);
2496: int bumpWidth = super .calculateTabHeight(tabPlacement, 0,
2497: SubstanceSizeUtils.getComponentFontSize(this .tabPane)) / 2;
2498: leftOutline.lineTo(x - bumpWidth, selRect.y + borderInsets);
2499: if (selRect.y + selRect.height < y + h) {
2500: int selectionEndY = selRect.y + selRect.height - delta - 1 - borderInsets;
2501: leftOutline.lineTo(x - bumpWidth, selectionEndY);
2502: leftOutline.lineTo(x, selectionEndY);
2503: leftOutline.lineTo(x, y + h);
2504: }
2505: g2d.setPaint(new GradientPaint(x, y, this .darkShadow, x - bumpWidth, y,
2506: SubstanceColorUtilities.getAlphaColor(this .darkShadow, 0)));
2507: g2d.draw(leftOutline);
2508:
2509: }
2510:
2511: if (isDouble) {
2512: if (tabPlacement == LEFT) {
2513: g2d.setColor(this .darkShadow);
2514: g2d.drawLine(x + ribbonDelta, y, x + ribbonDelta, y + h);
2515: // g2d.setColor(this.highlight);
2516: // g2d.drawLine(x + 1 + ribbonDelta, y + 1, x + 1 + ribbonDelta,
2517: // y +
2518: // h - 1);
2519: }
2520: if (tabPlacement == TOP) {
2521: g2d.setPaint(new GradientPaint(x, y, this .darkShadow,
2522: x, y + 4 * ribbonDelta, this .highlight));
2523: g2d.drawLine(x, y, x, y + 4 * ribbonDelta);
2524: }
2525: if (tabPlacement == BOTTOM) {
2526: g2d.setPaint(new GradientPaint(x, y + h - 1 - 4 * ribbonDelta,
2527: this .highlight,
2528: x, y + h - 1, this .darkShadow));
2529: g2d.drawLine(x, y + h - 1 - 4 * ribbonDelta, x, y + h - 1);
2530: }
2531: }
2532: g2d.dispose();
2533: }
2534:
2535: /*
2536: * (non-Javadoc)
2537: *
2538: * @see javax.swing.plaf.basic.BasicTabbedPaneUI#paintContentBorderRightEdge(java.awt.Graphics,
2539: * int, int, int, int, int, int)
2540: */
2541: @Override
2542: protected void paintContentBorderRightEdge(Graphics g, int tabPlacement,
2543: int selectedIndex, int x, int y, int w, int h) {
2544: TabContentPaneBorderKind kind = SubstanceCoreUtilities
2545: .getContentBorderKind(this .tabPane);
2546: boolean isDouble = (kind == TabContentPaneBorderKind.DOUBLE_FULL) ||
2547: (kind == TabContentPaneBorderKind.DOUBLE_PLACEMENT);
2548: boolean isPlacement = (kind == TabContentPaneBorderKind.SINGLE_PLACEMENT) ||
2549: (kind == TabContentPaneBorderKind.DOUBLE_PLACEMENT);
2550: if (isPlacement) {
2551: if (tabPlacement != SwingConstants.RIGHT)
2552: return;
2553: }
2554: int ribbonDelta = (int) (3.0 * SubstanceSizeUtils.getBorderStrokeWidth(SubstanceSizeUtils
2555: .getComponentFontSize(tabPane)));
2556:
2557: Rectangle selRect = selectedIndex < 0 ? null : this .getTabBounds(
2558: selectedIndex, this .calcRect);
2559:
2560: Graphics2D g2d = (Graphics2D) g.create();
2561: g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
2562: RenderingHints.VALUE_ANTIALIAS_ON);
2563: g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
2564: RenderingHints.VALUE_STROKE_NORMALIZE);
2565: float strokeWidth = SubstanceSizeUtils.getBorderStrokeWidth(SubstanceSizeUtils
2566: .getComponentFontSize(tabPane));
2567: int joinKind = BasicStroke.JOIN_ROUND;
2568: int capKind = BasicStroke.CAP_BUTT;
2569: g2d.setStroke(new BasicStroke(strokeWidth, capKind, joinKind));
2570: int offset = (int) (strokeWidth / 2.0);
2571:
2572: boolean isUnbroken = (tabPlacement != RIGHT || selectedIndex < 0 || (selRect.x - 1 > w)
2573: || (selRect.y < y || selRect.y > y + h));
2574:
2575: x += offset;
2576: y += offset;
2577: w -= 2 * offset;
2578: h -= 2 * offset;
2579:
2580: // Draw unbroken line if tabs are not on RIGHT, OR
2581: // selected tab is not in run adjacent to content, OR
2582: // selected tab is not visible (SCROLL_TAB_LAYOUT)
2583: if (isUnbroken) {
2584: g2d.setColor(this .highlight);
2585: g2d.drawLine(x + w - 1, y, x + w - 1, y + h);
2586: } else {
2587: // Break line to show visual connection to selected tab
2588: SubstanceButtonShaper shaper = SubstanceLookAndFeel
2589: .getCurrentButtonShaper();
2590: int delta = (shaper instanceof ClassicButtonShaper) ? 1 : 0;
2591:
2592: int borderInsets = (int) Math.floor(SubstanceSizeUtils
2593: .getBorderStrokeWidth(SubstanceSizeUtils
2594: .getComponentFontSize(tabPane)) / 2.0);
2595: GeneralPath rightOutline = new GeneralPath();
2596: rightOutline.moveTo(x + w - 1, y);
2597: rightOutline.lineTo(x + w - 1, selRect.y + borderInsets);
2598: int bumpWidth = super .calculateTabHeight(tabPlacement, 0,
2599: SubstanceSizeUtils.getComponentFontSize(this .tabPane)) / 2;
2600: rightOutline.lineTo(x + w - 1 + bumpWidth, selRect.y + borderInsets);
2601: if (selRect.y + selRect.height < y + h) {
2602: int selectionEndY = selRect.y + selRect.height - delta - 1 - borderInsets;
2603: rightOutline.lineTo(x + w - 1 + bumpWidth, selectionEndY);
2604: rightOutline.lineTo(x + w - 1, selectionEndY);
2605: rightOutline.lineTo(x + w - 1, y + h);
2606: }
2607: g2d.setPaint(new GradientPaint(x + w - 1, y, this .darkShadow,
2608: x + w - 1 + bumpWidth, y,
2609: SubstanceColorUtilities.getAlphaColor(this .darkShadow, 0)));
2610: g2d.draw(rightOutline);
2611: }
2612:
2613: if (isDouble) {
2614: if (tabPlacement == RIGHT) {
2615: g2d.setColor(this .highlight);
2616: // g2d.drawLine(x + w - 2 - ribbonDelta, y + 1, x + w - 2 -
2617: // ribbonDelta, y + h - 1);
2618: g2d.setColor(this .darkShadow);
2619: g2d.drawLine(x + w - 1 - ribbonDelta, y, x + w - 1 - ribbonDelta, y + h);
2620: }
2621: if (tabPlacement == TOP) {
2622: g2d.setPaint(new GradientPaint(x + w - 1, y, this .darkShadow,
2623: x + w - 1, y + 4 * ribbonDelta, this .highlight));
2624: g2d.drawLine(x + w - 1, y, x + w - 1, y + 4 * ribbonDelta);
2625: }
2626: if (tabPlacement == BOTTOM) {
2627: g2d.setPaint(new GradientPaint(x + w - 1, y + h - 1 - 4 * ribbonDelta,
2628: this .highlight,
2629: x + w - 1, y + h - 1, this .darkShadow));
2630: g2d.drawLine(x + w - 1, y + h - 1 - 4 * ribbonDelta, x + w - 1, y + h - 1);
2631: }
2632: }
2633: g2d.dispose();
2634: }
2635:
2636: /*
2637: * (non-Javadoc)
2638: *
2639: * @see javax.swing.plaf.basic.BasicTabbedPaneUI#paintContentBorderTopEdge(java.awt.Graphics,
2640: * int, int, int, int, int, int)
2641: */
2642: @Override
2643: protected void paintContentBorderTopEdge(Graphics g, int tabPlacement,
2644: int selectedIndex, int x, int y, int w, int h) {
2645: TabContentPaneBorderKind kind = SubstanceCoreUtilities
2646: .getContentBorderKind(this .tabPane);
2647: boolean isDouble = (kind == TabContentPaneBorderKind.DOUBLE_FULL) ||
2648: (kind == TabContentPaneBorderKind.DOUBLE_PLACEMENT);
2649: boolean isPlacement = (kind == TabContentPaneBorderKind.SINGLE_PLACEMENT) ||
2650: (kind == TabContentPaneBorderKind.DOUBLE_PLACEMENT);
2651: if (isPlacement) {
2652: if (tabPlacement != SwingConstants.TOP)
2653: return;
2654: }
2655: int ribbonDelta = (int) (3.0 * SubstanceSizeUtils.getBorderStrokeWidth(SubstanceSizeUtils
2656: .getComponentFontSize(tabPane)));
2657:
2658: Rectangle selRect = selectedIndex < 0 ? null : this .getTabBounds(
2659: selectedIndex, this .calcRect);
2660:
2661: Graphics2D g2d = (Graphics2D) g.create();
2662: g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
2663: RenderingHints.VALUE_ANTIALIAS_ON);
2664: g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
2665: RenderingHints.VALUE_STROKE_NORMALIZE);
2666: float strokeWidth = SubstanceSizeUtils.getBorderStrokeWidth(SubstanceSizeUtils
2667: .getComponentFontSize(tabPane));
2668: int joinKind = BasicStroke.JOIN_ROUND;
2669: int capKind = BasicStroke.CAP_BUTT;
2670: g2d.setStroke(new BasicStroke(strokeWidth, capKind, joinKind));
2671: int offset = (int) (strokeWidth / 2.0);
2672:
2673: boolean isUnbroken = (tabPlacement != TOP || selectedIndex < 0
2674: || (selRect.y + selRect.height + 1 < y)
2675: || (selRect.x < x || selRect.x > x + w));
2676:
2677: x += offset;
2678: y += offset;
2679: w -= 2 * offset;
2680: h -= 2 * offset;
2681:
2682: // Draw unbroken line if tabs are not on TOP, OR
2683: // selected tab is not in run adjacent to content, OR
2684: // selected tab is not visible (SCROLL_TAB_LAYOUT)
2685: if (isUnbroken) {
2686: g2d.setColor(this .highlight);
2687: g2d.drawLine(x, y, x + w - 1, y);
2688: } else {
2689: // Break line to show visual connection to selected tab
2690: SubstanceButtonShaper shaper = SubstanceLookAndFeel
2691: .getCurrentButtonShaper();
2692: int delta = (shaper instanceof ClassicButtonShaper) ? 1 : 0;
2693: int borderInsets = (int) Math.floor(SubstanceSizeUtils
2694: .getBorderStrokeWidth(SubstanceSizeUtils
2695: .getComponentFontSize(tabPane)) / 2.0);
2696: GeneralPath topOutline = new GeneralPath();
2697: topOutline.moveTo(x, y);
2698: topOutline.lineTo(selRect.x + borderInsets, y);
2699: int bumpHeight = super .calculateTabHeight(tabPlacement, 0,
2700: SubstanceSizeUtils.getComponentFontSize(this .tabPane)) / 2;
2701: topOutline.lineTo(selRect.x + borderInsets, y - bumpHeight);
2702: if (selRect.x + selRect.width < x + w - 1) {
2703: int selectionEndX = selRect.x + selRect.width - delta - 1 - borderInsets;
2704: topOutline.lineTo(selectionEndX, y - bumpHeight);
2705: topOutline.lineTo(selectionEndX, y);
2706: topOutline.lineTo(x + w - 1, y);
2707: }
2708: g2d.setPaint(new GradientPaint(x, y, this .darkShadow, x, y - bumpHeight,
2709: SubstanceColorUtilities.getAlphaColor(this .darkShadow, 0)));
2710: g2d.draw(topOutline);
2711: }
2712:
2713: if (isDouble) {
2714: if (tabPlacement == TOP) {
2715: g2d.setColor(this .darkShadow);
2716: g2d.drawLine(x, y + ribbonDelta, x + w - 1, y + ribbonDelta);
2717: g2d.setColor(this .highlight);
2718: // g2d.drawLine(x, y + 1 + ribbonDelta, x + w - 1, y + 1 +
2719: // ribbonDelta);
2720: }
2721: if (tabPlacement == LEFT) {
2722: g2d.setPaint(new GradientPaint(x, y, this .darkShadow,
2723: x + 4 * ribbonDelta, y, this .highlight));
2724: g2d.drawLine(x, y, x + 4 * ribbonDelta, y);
2725: }
2726: if (tabPlacement == RIGHT) {
2727: g2d.setPaint(new GradientPaint(x + w - 1 - 4 * ribbonDelta, y,
2728: this .highlight,
2729: x + w - 1, y, this .darkShadow));
2730: g2d.drawLine(x + w - 1 - 4 * ribbonDelta, y, x + w - 1, y);
2731: }
2732: }
2733:
2734: g2d.dispose();
2735: }
2736:
2737: @Override
2738: public Rectangle getTabBounds(JTabbedPane pane, int i) {
2739: this .ensureCurrentLayout();
2740: Rectangle tabRect = new Rectangle();
2741: return this .getTabBounds(i, tabRect);
2742: }
2743:
2744: /**
2745: * Returns the previous state for the specified tab.
2746: *
2747: * @param tabIndex
2748: * Tab index.
2749: * @return The previous state for the specified tab.
2750: */
2751: protected ComponentState getPrevTabState(int tabIndex) {
2752: if (this .prevStateMap.containsKey(tabIndex))
2753: return this .prevStateMap.get(tabIndex);
2754: return ComponentState.DEFAULT;
2755: }
2756:
2757: /**
2758: * Returns the current state for the specified tab.
2759: *
2760: * @param tabIndex
2761: * Tab index.
2762: * @return The current state for the specified tab.
2763: */
2764: protected ComponentState getTabState(int tabIndex) {
2765: ButtonModel synthModel = new DefaultButtonModel();
2766: synthModel.setEnabled(this .tabPane.isEnabledAt(tabIndex));
2767: synthModel.setRollover(this .getRolloverTabIndex() == tabIndex);
2768: synthModel.setSelected(this .tabPane.getSelectedIndex() == tabIndex);
2769: return ComponentState.getState(synthModel, null);
2770: }
2771:
2772: /*
2773: * (non-Javadoc)
2774: *
2775: * @see javax.swing.plaf.basic.BasicTabbedPaneUI#paintText(java.awt.Graphics,
2776: * int, java.awt.Font, java.awt.FontMetrics, int, java.lang.String,
2777: * java.awt.Rectangle, boolean)
2778: */
2779: @Override
2780: protected void paintText(Graphics g, int tabPlacement, Font font,
2781: FontMetrics metrics, int tabIndex, String title,
2782: Rectangle textRect, boolean isSelected) {
2783: g.setFont(font);
2784:
2785: View v = this .getTextViewForTab(tabIndex);
2786: if (v != null) {
2787: // html
2788: v.paint(g, textRect);
2789: } else {
2790: // plain text
2791: int mnemIndex = this .tabPane.getDisplayedMnemonicIndexAt(tabIndex);
2792: ComponentState state = this .getTabState(tabIndex);
2793: ComponentState prevState = this .getPrevTabState(tabIndex);
2794: if (prevState == null)
2795: prevState = state;
2796: Color fg = SubstanceCoreUtilities.getInterpolatedForegroundColor(
2797: this .tabPane, tabIndex, SubstanceThemeUtilities.getTheme(
2798: this .tabPane, tabIndex, state),
2799: state, prevState,
2800: FadeKind.ROLLOVER, FadeKind.SELECTION, FadeKind.PRESS);
2801: Graphics2D graphics = (Graphics2D) g.create();
2802: if (!state.isKindActive(FadeKind.ENABLE)) {
2803: graphics.setComposite(TransitionLayout.getAlphaComposite(
2804: this .tabPane, SubstanceThemeUtilities.getTheme(
2805: this .tabPane.getComponentAt(tabIndex))
2806: .getThemeAlpha(
2807: this .tabPane.getComponentAt(tabIndex),
2808: state), g));
2809: }
2810: // graphics.setColor(fg);
2811:
2812: SubstanceTextPainter textPainter = SubstanceLookAndFeel.getCurrentTextPainter();
2813: // System.out.println("Painted text of " + tabIndex);
2814:
2815: graphics.clip(getTabRectangle(tabIndex));
2816: textPainter.attachText(this .tabPane, textRect, title, mnemIndex,
2817: graphics.getFont(), fg, null);
2818: textPainter.renderSurface(graphics);
2819:
2820: graphics.dispose();
2821: }
2822: }
2823:
2824: @Override
2825: protected MouseListener createMouseListener() {
2826: return null;
2827: }
2828:}
|