Source Code Cross Referenced for SubstanceTabbedPaneUI.java in  » Swing-Library » substance-look-feel » org » jvnet » substance » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Swing Library » substance look feel » org.jvnet.substance 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


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:}
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.