~ubuntu-branches/debian/stretch/insubstantial/stretch

« back to all changes in this revision

Viewing changes to substance/src/main/java/org/pushingpixels/substance/internal/ui/SubstanceTabbedPaneUI.java

  • Committer: Package Import Robot
  • Author(s): Felix Natter
  • Date: 2016-01-18 20:58:45 UTC
  • Revision ID: package-import@ubuntu.com-20160118205845-crbmrkda61qsi5qa
Tags: upstream-7.3+dfsg2
ImportĀ upstreamĀ versionĀ 7.3+dfsg2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (c) 2005-2010 Substance Kirill Grouchnikov. All Rights Reserved.
 
3
 *
 
4
 * Redistribution and use in source and binary forms, with or without
 
5
 * modification, are permitted provided that the following conditions are met:
 
6
 *
 
7
 *  o Redistributions of source code must retain the above copyright notice,
 
8
 *    this list of conditions and the following disclaimer.
 
9
 *
 
10
 *  o Redistributions in binary form must reproduce the above copyright notice,
 
11
 *    this list of conditions and the following disclaimer in the documentation
 
12
 *    and/or other materials provided with the distribution.
 
13
 *
 
14
 *  o Neither the name of Substance Kirill Grouchnikov nor the names of
 
15
 *    its contributors may be used to endorse or promote products derived
 
16
 *    from this software without specific prior written permission.
 
17
 *
 
18
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 
19
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 
20
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 
21
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 
22
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 
23
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 
24
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 
25
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 
26
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 
27
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 
28
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
29
 */
 
30
package org.pushingpixels.substance.internal.ui;
 
31
 
 
32
import java.awt.*;
 
33
import java.awt.event.*;
 
34
import java.awt.geom.*;
 
35
import java.awt.image.BufferedImage;
 
36
import java.beans.PropertyChangeEvent;
 
37
import java.beans.PropertyChangeListener;
 
38
import java.util.*;
 
39
import java.util.List;
 
40
 
 
41
import javax.swing.*;
 
42
import javax.swing.event.ChangeEvent;
 
43
import javax.swing.event.ChangeListener;
 
44
import javax.swing.plaf.ComponentUI;
 
45
import javax.swing.plaf.UIResource;
 
46
import javax.swing.plaf.basic.BasicTabbedPaneUI;
 
47
import javax.swing.text.View;
 
48
 
 
49
import org.pushingpixels.lafwidget.LafWidgetUtilities;
 
50
import org.pushingpixels.lafwidget.animation.AnimationConfigurationManager;
 
51
import org.pushingpixels.lafwidget.animation.AnimationFacet;
 
52
import org.pushingpixels.lafwidget.utils.RenderingUtils;
 
53
import org.pushingpixels.substance.api.*;
 
54
import org.pushingpixels.substance.api.SubstanceConstants.*;
 
55
import org.pushingpixels.substance.api.painter.border.SubstanceBorderPainter;
 
56
import org.pushingpixels.substance.api.painter.fill.SubstanceFillPainter;
 
57
import org.pushingpixels.substance.api.shaper.ClassicButtonShaper;
 
58
import org.pushingpixels.substance.api.shaper.SubstanceButtonShaper;
 
59
import org.pushingpixels.substance.api.tabbed.*;
 
60
import org.pushingpixels.substance.internal.animation.StateTransitionMultiTracker;
 
61
import org.pushingpixels.substance.internal.animation.StateTransitionTracker;
 
62
import org.pushingpixels.substance.internal.animation.StateTransitionTracker.RepaintCallback;
 
63
import org.pushingpixels.substance.internal.animation.StateTransitionTracker.StateContributionInfo;
 
64
import org.pushingpixels.substance.internal.painter.BackgroundPaintingUtils;
 
65
import org.pushingpixels.substance.internal.utils.*;
 
66
import org.pushingpixels.substance.internal.utils.icon.TransitionAwareIcon;
 
67
import org.pushingpixels.substance.internal.utils.scroll.SubstanceScrollButton;
 
68
import org.pushingpixels.trident.Timeline;
 
69
import org.pushingpixels.trident.Timeline.RepeatBehavior;
 
70
import org.pushingpixels.trident.Timeline.TimelineState;
 
71
import org.pushingpixels.trident.callback.TimelineCallback;
 
72
import org.pushingpixels.trident.callback.UIThreadTimelineCallbackAdapter;
 
73
 
 
74
/**
 
75
 * UI for tabbed panes in <b>Substance</b> look and feel.
 
76
 * 
 
77
 * @author Kirill Grouchnikov
 
78
 */
 
79
public class SubstanceTabbedPaneUI extends BasicTabbedPaneUI {
 
80
        /**
 
81
         * Current mouse location.
 
82
         */
 
83
        protected Point substanceMouseLocation;
 
84
 
 
85
        /**
 
86
         * Hash map for storing already computed backgrounds.
 
87
         */
 
88
        private static LazyResettableHashMap<BufferedImage> backgroundMap = new LazyResettableHashMap<BufferedImage>(
 
89
                        "SubstanceTabbedPaneUI.background");
 
90
 
 
91
        /**
 
92
         * Hash map for storing already computed backgrounds.
 
93
         */
 
94
        private static LazyResettableHashMap<BufferedImage> closeButtonMap = new LazyResettableHashMap<BufferedImage>(
 
95
                        "SubstanceTabbedPaneUI.closeButton");
 
96
 
 
97
        /**
 
98
         * Key - tab component. Value - the looping timeline that animates the tab
 
99
         * component when it's marked as modified (with
 
100
         * {@link SubstanceLookAndFeel#WINDOW_MODIFIED} property).
 
101
         */
 
102
        private Map<Component, Timeline> modifiedTimelines;
 
103
 
 
104
        /**
 
105
         * Currently selected index (for selection animations).
 
106
         */
 
107
        private int currSelectedIndex;
 
108
 
 
109
        private StateTransitionMultiTracker<Integer> stateTransitionMultiTracker;
 
110
 
 
111
        /*
 
112
         * (non-Javadoc)
 
113
         * 
 
114
         * @see javax.swing.plaf.ComponentUI#createUI(javax.swing.JComponent)
 
115
         */
 
116
        public static ComponentUI createUI(JComponent comp) {
 
117
                SubstanceCoreUtilities.testComponentCreationThreadingViolation(comp);
 
118
                return new SubstanceTabbedPaneUI();
 
119
        }
 
120
 
 
121
        /**
 
122
         * Mouse handler for rollover effects.
 
123
         */
 
124
        protected MouseRolloverHandler substanceRolloverHandler;
 
125
 
 
126
        /**
 
127
         * Tracks changes to the tabbed pane contents. Each tab component is tracked
 
128
         * for changes on the {@link SubstanceLookAndFeel#WINDOW_MODIFIED} property.
 
129
         */
 
130
        protected TabbedContainerListener substanceContainerListener;
 
131
 
 
132
        /**
 
133
         * Listener for animation effects on tab selection.
 
134
         */
 
135
        protected ChangeListener substanceSelectionListener;
 
136
 
 
137
        private boolean substanceContentOpaque;
 
138
 
 
139
        /**
 
140
         * Tracks changes to the tabbed pane contents. Each tab component is tracked
 
141
         * for changes on the {@link SubstanceLookAndFeel#WINDOW_MODIFIED} property.
 
142
         * 
 
143
         * @author Kirill Grouchnikov
 
144
         */
 
145
        protected final class TabbedContainerListener extends ContainerAdapter {
 
146
                /**
 
147
                 * Property change listeners on the tab components.
 
148
                 * <p/>
 
149
                 * Fixes defect 135 - memory leaks on tabbed panes.
 
150
                 */
 
151
                private Map<Component, List<PropertyChangeListener>> listeners = new HashMap<Component, List<PropertyChangeListener>>();
 
152
 
 
153
                /**
 
154
                 * Creates a new container listener.
 
155
                 */
 
156
                public TabbedContainerListener() {
 
157
                }
 
158
 
 
159
                /**
 
160
                 * Tracks all existing tab component.
 
161
                 */
 
162
                protected void trackExistingTabs() {
 
163
                        // register listeners on all existing tabs
 
164
                        for (int i = 0; i < SubstanceTabbedPaneUI.this.tabPane
 
165
                                        .getTabCount(); i++) {
 
166
                                this.trackTab(SubstanceTabbedPaneUI.this.tabPane
 
167
                                                .getComponentAt(i));
 
168
                        }
 
169
                }
 
170
 
 
171
                /**
 
172
                 * Tracks changes in a single tab component.
 
173
                 * 
 
174
                 * @param tabComponent
 
175
                 *            Tab component.
 
176
                 */
 
177
                protected void trackTab(final Component tabComponent) {
 
178
                        if (tabComponent == null)
 
179
                                return;
 
180
 
 
181
                        PropertyChangeListener tabModifiedListener = new PropertyChangeListener() {
 
182
                                @Override
 
183
                public void propertyChange(PropertyChangeEvent evt) {
 
184
                                        if (SubstanceLookAndFeel.WINDOW_MODIFIED.equals(evt
 
185
                                                        .getPropertyName())) {
 
186
                                                Object oldValue = evt.getOldValue();
 
187
                                                Object newValue = evt.getNewValue();
 
188
                                                boolean wasModified = Boolean.TRUE.equals(oldValue);
 
189
                                                boolean isModified = Boolean.TRUE.equals(newValue);
 
190
 
 
191
                                                if (wasModified) {
 
192
                                                        if (!isModified) {
 
193
                                                                Timeline modifiedTimeline = modifiedTimelines
 
194
                                                                                .get(tabComponent);
 
195
                                                                modifiedTimeline.cancel();
 
196
                                                                modifiedTimelines.remove(tabComponent);
 
197
                                                        }
 
198
                                                } else {
 
199
                                                        if (isModified) {
 
200
                                                                int tabIndex = SubstanceTabbedPaneUI.this.tabPane
 
201
                                                                                .indexOfComponent(tabComponent);
 
202
                                                                if (tabIndex >= 0) {
 
203
                                                                        trackTabModification(tabIndex, tabComponent);
 
204
                                                                }
 
205
                                                        }
 
206
                                                }
 
207
                                        }
 
208
                                }
 
209
                        };
 
210
                        tabComponent.addPropertyChangeListener(tabModifiedListener);
 
211
                        // fix for defect 135 - memory leaks on tabbed panes
 
212
                        List<PropertyChangeListener> currList = this.listeners
 
213
                                        .get(tabComponent);
 
214
                        if (currList == null)
 
215
                                currList = new LinkedList<PropertyChangeListener>();
 
216
                        currList.add(tabModifiedListener);
 
217
                        // System.err.println(this.hashCode() + " adding for " +
 
218
                        // tabComponent.hashCode());
 
219
                        this.listeners.put(tabComponent, currList);
 
220
                        // Fix for defect 104 - a 'modified' component is added to
 
221
                        // the tabbed pane. In this case it should be animated from the
 
222
                        // beginning.
 
223
                        if (tabComponent instanceof JComponent) {
 
224
                                if (Boolean.TRUE
 
225
                                                .equals(((JComponent) tabComponent)
 
226
                                                                .getClientProperty(SubstanceLookAndFeel.WINDOW_MODIFIED))) {
 
227
                                        int tabIndex = SubstanceTabbedPaneUI.this.tabPane
 
228
                                                        .indexOfComponent(tabComponent);
 
229
                                        if (tabIndex >= 0) {
 
230
                                                trackTabModification(tabIndex, tabComponent);
 
231
                                        }
 
232
                                }
 
233
                        }
 
234
                }
 
235
 
 
236
                /**
 
237
                 * Stops tracking changes to a single tab component.
 
238
                 * 
 
239
                 * @param tabComponent
 
240
                 *            Tab component.
 
241
                 */
 
242
                protected void stopTrackTab(final Component tabComponent) {
 
243
                        if (tabComponent == null)
 
244
                                return;
 
245
 
 
246
                        List<PropertyChangeListener> pclList = this.listeners
 
247
                                        .get(tabComponent);
 
248
                        if (pclList != null) {
 
249
                                for (PropertyChangeListener pcl : pclList)
 
250
                                        tabComponent.removePropertyChangeListener(pcl);
 
251
                        }
 
252
 
 
253
                        this.listeners.put(tabComponent, null);
 
254
                }
 
255
 
 
256
                /**
 
257
                 * Stops tracking all tab components.
 
258
                 */
 
259
                protected void stopTrackExistingTabs() {
 
260
                        // register listeners on all existing tabs
 
261
                        for (int i = 0; i < SubstanceTabbedPaneUI.this.tabPane
 
262
                                        .getTabCount(); i++) {
 
263
                                this.stopTrackTab(SubstanceTabbedPaneUI.this.tabPane
 
264
                                                .getComponentAt(i));
 
265
                        }
 
266
                }
 
267
 
 
268
                /*
 
269
                 * (non-Javadoc)
 
270
                 * 
 
271
                 * @seejava.awt.event.ContainerAdapter#componentAdded(java.awt.event.
 
272
                 * ContainerEvent)
 
273
                 */
 
274
                @Override
 
275
                public void componentAdded(final ContainerEvent e) {
 
276
                        final Component tabComponent = e.getChild();
 
277
                        if (tabComponent instanceof UIResource)
 
278
                                return;
 
279
                        this.trackTab(tabComponent);
 
280
                }
 
281
 
 
282
                /*
 
283
                 * (non-Javadoc)
 
284
                 * 
 
285
                 * @seejava.awt.event.ContainerAdapter#componentRemoved(java.awt.event.
 
286
                 * ContainerEvent)
 
287
                 */
 
288
                @Override
 
289
                public void componentRemoved(ContainerEvent e) {
 
290
                        // fix for defect 135 - memory leaks on tabbed panes
 
291
                        final Component tabComponent = e.getChild();
 
292
                        if (tabComponent == null)
 
293
                                return;
 
294
                        // System.err.println(this.hashCode() + " removing for " +
 
295
                        // tabComponent.hashCode());
 
296
                        if (tabComponent instanceof UIResource)
 
297
                                return;
 
298
                        for (PropertyChangeListener pcl : this.listeners.get(tabComponent))
 
299
                                tabComponent.removePropertyChangeListener(pcl);
 
300
                        this.listeners.get(tabComponent).clear();
 
301
                        this.listeners.remove(tabComponent);
 
302
 
 
303
                        // has running timeline?
 
304
                        Timeline timeline = modifiedTimelines.get(tabComponent);
 
305
                        if (timeline != null) {
 
306
                                timeline.cancel();
 
307
                                modifiedTimelines.remove(tabComponent);
 
308
                        }
 
309
 
 
310
                        // this.cleanListeners(tabComponent);
 
311
                }
 
312
 
 
313
        }
 
314
 
 
315
        /**
 
316
         * Listener for rollover animation effects.
 
317
         * 
 
318
         * @author Kirill Grouchnikov
 
319
         */
 
320
        protected class MouseRolloverHandler implements MouseListener,
 
321
                        MouseMotionListener {
 
322
                /**
 
323
                 * Index of the tab that was rolloed over on the previous mouse event.
 
324
                 */
 
325
                int prevRolledOver = -1;
 
326
 
 
327
                /**
 
328
                 * Indicates whether the previous mouse event was located in a close
 
329
                 * button.
 
330
                 */
 
331
                boolean prevInCloseButton = false;
 
332
 
 
333
                /**
 
334
                 * Tab index of the last mouse pressed event that happened in a close
 
335
                 * button.
 
336
                 */
 
337
                int tabOfPressedCloseButton = -1;
 
338
 
 
339
                /*
 
340
                 * (non-Javadoc)
 
341
                 * 
 
342
                 * @see
 
343
                 * java.awt.event.MouseListener#mouseClicked(java.awt.event.MouseEvent)
 
344
                 */
 
345
                @Override
 
346
        public void mouseClicked(final MouseEvent e) {
 
347
                        final int tabIndex = SubstanceTabbedPaneUI.this.tabForCoordinate(
 
348
                                        SubstanceTabbedPaneUI.this.tabPane, e.getX(), e.getY());
 
349
                        TabCloseCallback closeCallback = SubstanceCoreUtilities
 
350
                                        .getTabCloseCallback(e, SubstanceTabbedPaneUI.this.tabPane,
 
351
                                                        tabIndex);
 
352
                        if (closeCallback == null)
 
353
                                return;
 
354
 
 
355
                        final TabCloseKind tabCloseKind = closeCallback.onAreaClick(
 
356
                                        SubstanceTabbedPaneUI.this.tabPane, tabIndex, e);
 
357
                        if (tabCloseKind == TabCloseKind.NONE)
 
358
                                return;
 
359
 
 
360
                        SwingUtilities.invokeLater(new Runnable() {
 
361
                                @Override
 
362
                public void run() {
 
363
                                        SubstanceTabbedPaneUI.this.tryCloseTabs(tabIndex,
 
364
                                                        tabCloseKind);
 
365
                                }
 
366
                        });
 
367
                }
 
368
 
 
369
                /*
 
370
                 * (non-Javadoc)
 
371
                 * 
 
372
                 * @see
 
373
                 * java.awt.event.MouseMotionListener#mouseDragged(java.awt.event.MouseEvent
 
374
                 * )
 
375
                 */
 
376
                @Override
 
377
        public void mouseDragged(MouseEvent e) {
 
378
                        this.handleMouseMoveDrag(e);
 
379
                }
 
380
 
 
381
                /*
 
382
                 * (non-Javadoc)
 
383
                 * 
 
384
                 * @see
 
385
                 * java.awt.event.MouseListener#mouseEntered(java.awt.event.MouseEvent)
 
386
                 */
 
387
                @Override
 
388
        public void mouseEntered(MouseEvent e) {
 
389
                        setRolloverTab(tabForCoordinate(tabPane, e.getX(), e.getY()));
 
390
                }
 
391
 
 
392
                /*
 
393
                 * (non-Javadoc)
 
394
                 * 
 
395
                 * @see
 
396
                 * java.awt.event.MouseListener#mousePressed(java.awt.event.MouseEvent)
 
397
                 */
 
398
                @Override
 
399
        public void mousePressed(MouseEvent e) {
 
400
                        if (!tabPane.isEnabled()) {
 
401
                                return;
 
402
                        }
 
403
                        int tabIndex = tabForCoordinate(tabPane, e.getX(), e.getY());
 
404
                        if (tabIndex >= 0 && tabPane.isEnabledAt(tabIndex)) {
 
405
                                Rectangle rect = new Rectangle();
 
406
                                rect = getTabBounds(tabIndex, rect);
 
407
                                Rectangle close = getCloseButtonRectangleForEvents(tabIndex,
 
408
                                                rect.x, rect.y, rect.width, rect.height);
 
409
                                boolean inCloseButton = close.contains(e.getPoint());
 
410
                                this.tabOfPressedCloseButton = inCloseButton ? tabIndex : -1;
 
411
                                if (tabIndex != tabPane.getSelectedIndex()) {
 
412
                                        // enhancement 307 - don't select tab on pressing its
 
413
                                        // close button
 
414
                                        if (inCloseButton) {
 
415
                                                return;
 
416
                                        }
 
417
                                        // Clicking on unselected tab, change selection, do NOT
 
418
                                        // request focus.
 
419
                                        // This will trigger the focusIndex to change by way
 
420
                                        // of stateChanged.
 
421
                                        tabPane.setSelectedIndex(tabIndex);
 
422
                                } else if (tabPane.isRequestFocusEnabled()) {
 
423
                                        // Clicking on selected tab, try and give the tabbedpane
 
424
                                        // focus. Repaint will occur in focusGained.
 
425
                                        tabPane.requestFocus();
 
426
                                }
 
427
                        }
 
428
                }
 
429
 
 
430
                /*
 
431
                 * (non-Javadoc)
 
432
                 * 
 
433
                 * @see
 
434
                 * java.awt.event.MouseMotionListener#mouseMoved(java.awt.event.MouseEvent
 
435
                 * )
 
436
                 */
 
437
                @Override
 
438
        public void mouseMoved(MouseEvent e) {
 
439
                        this.handleMouseMoveDrag(e);
 
440
                }
 
441
 
 
442
                /**
 
443
                 * Handles the move and drag mouse events.
 
444
                 * 
 
445
                 * @param e
 
446
                 *            Mouse event to handle.
 
447
                 */
 
448
                private void handleMouseMoveDrag(MouseEvent e) {
 
449
                        if (e.getSource() != tabPane)
 
450
                                return;
 
451
 
 
452
                        setRolloverTab(tabForCoordinate(tabPane, e.getX(), e.getY()));
 
453
                        if (!AnimationConfigurationManager.getInstance()
 
454
                                        .isAnimationAllowed(AnimationFacet.ROLLOVER, tabPane)) {
 
455
                                return;
 
456
                        }
 
457
 
 
458
                        SubstanceTabbedPaneUI.this.substanceMouseLocation = e.getPoint();
 
459
                        int currRolledOver = SubstanceTabbedPaneUI.this.getRolloverTab();
 
460
                        TabCloseCallback tabCloseCallback = SubstanceCoreUtilities
 
461
                                        .getTabCloseCallback(e, tabPane, currRolledOver);
 
462
                        // System.err.println("Mouse moved " + currRolledOver + ":" +
 
463
                        // prevRolledOver);
 
464
                        if (currRolledOver == this.prevRolledOver) {
 
465
                                if (currRolledOver >= 0) {
 
466
                                        Rectangle rect = new Rectangle();
 
467
                                        rect = getTabBounds(currRolledOver, rect);
 
468
                                        Rectangle close = getCloseButtonRectangleForEvents(
 
469
                                                        currRolledOver, rect.x, rect.y, rect.width,
 
470
                                                        rect.height);
 
471
                                        // System.out.println("move " + rect + " " + close + " "
 
472
                                        // + e.getPoint());
 
473
                                        boolean inCloseButton = close.contains(e.getPoint());
 
474
                                        if (this.prevInCloseButton == inCloseButton)
 
475
                                                return;
 
476
                                        this.prevInCloseButton = inCloseButton;
 
477
                                        if (tabCloseCallback != null) {
 
478
                                                if (inCloseButton) {
 
479
                                                        String closeButtonTooltip = tabCloseCallback
 
480
                                                                        .getCloseButtonTooltip(tabPane,
 
481
                                                                                        currRolledOver);
 
482
                                                        tabPane.setToolTipTextAt(currRolledOver,
 
483
                                                                        closeButtonTooltip);
 
484
                                                } else {
 
485
                                                        String areaTooltip = tabCloseCallback
 
486
                                                                        .getAreaTooltip(tabPane, currRolledOver);
 
487
                                                        tabPane.setToolTipTextAt(currRolledOver,
 
488
                                                                        areaTooltip);
 
489
                                                }
 
490
                                        }
 
491
                                        if ((currRolledOver >= 0)
 
492
                                                        && (currRolledOver < tabPane.getTabCount())) {
 
493
                                                StateTransitionTracker tracker = getTracker(
 
494
                                                                currRolledOver, true,
 
495
                                                                currRolledOver == currSelectedIndex);
 
496
                                                tracker.getModel().setRollover(false);
 
497
                                                tracker.endTransition();
 
498
                                        }
 
499
                                }
 
500
                        } else {
 
501
                                if ((this.prevRolledOver >= 0)
 
502
                                                && (this.prevRolledOver < tabPane.getTabCount())
 
503
                                                && tabPane.isEnabledAt(this.prevRolledOver)) {
 
504
                                        StateTransitionTracker tracker = getTracker(prevRolledOver,
 
505
                                                        true, prevRolledOver == currSelectedIndex);
 
506
                                        tracker.getModel().setRollover(false);
 
507
                                }
 
508
                                if ((currRolledOver >= 0)
 
509
                                                && (currRolledOver < tabPane.getTabCount())
 
510
                                                && tabPane.isEnabledAt(currRolledOver)) {
 
511
                                        StateTransitionTracker tracker = getTracker(currRolledOver,
 
512
                                                        false, currRolledOver == currSelectedIndex);
 
513
                                        tracker.getModel().setRollover(true);
 
514
                                }
 
515
                        }
 
516
                        this.prevRolledOver = currRolledOver;
 
517
                }
 
518
 
 
519
                /*
 
520
                 * (non-Javadoc)
 
521
                 * 
 
522
                 * @see
 
523
                 * java.awt.event.MouseListener#mouseExited(java.awt.event.MouseEvent)
 
524
                 */
 
525
                @Override
 
526
        public void mouseExited(MouseEvent e) {
 
527
                        setRolloverTab(-1);
 
528
                        // fix for bug 69 - non-selected non-rollover tab
 
529
                        // may remain with close button after moving mouse quickly
 
530
                        // to inner JTabbedPane
 
531
                        if ((this.prevRolledOver >= 0)
 
532
                                        && (this.prevRolledOver < SubstanceTabbedPaneUI.this.tabPane
 
533
                                                        .getTabCount())
 
534
                                        && SubstanceTabbedPaneUI.this.tabPane
 
535
                                                        .isEnabledAt(this.prevRolledOver)) {
 
536
                                // only the previously rolled-over tab needs to be
 
537
                                // repainted (fade-out) instead of repainting the
 
538
                                // whole tab as before.
 
539
                                StateTransitionTracker tracker = getTracker(prevRolledOver,
 
540
                                                true, prevRolledOver == currSelectedIndex);
 
541
                                tracker.getModel().setRollover(false);
 
542
 
 
543
                                if (SubstanceCoreUtilities.getTabCloseCallback(e, tabPane,
 
544
                                                this.prevRolledOver) != null) {
 
545
                                        tabPane.setToolTipTextAt(this.prevRolledOver, null);
 
546
                                }
 
547
                        }
 
548
                        this.prevRolledOver = -1;
 
549
                }
 
550
 
 
551
                /*
 
552
                 * (non-Javadoc)
 
553
                 * 
 
554
                 * @see
 
555
                 * java.awt.event.MouseListener#mouseReleased(java.awt.event.MouseEvent)
 
556
                 */
 
557
                @Override
 
558
        public void mouseReleased(final MouseEvent e) {
 
559
                        // enhancement 307 - moving the tab close to be on mouse release
 
560
                        // and not on mouse press.
 
561
                        final int tabIndex = SubstanceTabbedPaneUI.this.tabForCoordinate(
 
562
                                        SubstanceTabbedPaneUI.this.tabPane, e.getX(), e.getY());
 
563
                        // check that the mouse release is on the same tab as
 
564
                        // mouse press, and that the tab has close button
 
565
                        if (SubstanceCoreUtilities.hasCloseButton(
 
566
                                        SubstanceTabbedPaneUI.this.tabPane, tabIndex)
 
567
                                        && (tabIndex == this.tabOfPressedCloseButton)) {
 
568
                                SwingUtilities.invokeLater(new Runnable() {
 
569
                                        @Override
 
570
                    public void run() {
 
571
                                                if ((tabIndex >= 0)
 
572
                                                                && SubstanceTabbedPaneUI.this.tabPane
 
573
                                                                                .isEnabledAt(tabIndex)) {
 
574
                                                        Rectangle rect = new Rectangle();
 
575
                                                        rect = SubstanceTabbedPaneUI.this.getTabBounds(
 
576
                                                                        tabIndex, rect);
 
577
 
 
578
                                                        Rectangle close = SubstanceTabbedPaneUI.this
 
579
                                                                        .getCloseButtonRectangleForEvents(tabIndex,
 
580
                                                                                        rect.x, rect.y, rect.width,
 
581
                                                                                        rect.height);
 
582
                                                        // System.out.println("press " + close + " "
 
583
                                                        // + e.getPoint());
 
584
                                                        if (close.contains(e.getPoint())) {
 
585
                                                                TabCloseCallback closeCallback = SubstanceCoreUtilities
 
586
                                                                                .getTabCloseCallback(
 
587
                                                                                                e,
 
588
                                                                                                SubstanceTabbedPaneUI.this.tabPane,
 
589
                                                                                                tabIndex);
 
590
 
 
591
                                                                TabCloseKind tabCloseKind = (closeCallback == null) ? TabCloseKind.THIS
 
592
                                                                                : closeCallback
 
593
                                                                                                .onCloseButtonClick(
 
594
                                                                                                                SubstanceTabbedPaneUI.this.tabPane,
 
595
                                                                                                                tabIndex, e);
 
596
 
 
597
                                                                SubstanceTabbedPaneUI.this.tryCloseTabs(
 
598
                                                                                tabIndex, tabCloseKind);
 
599
                                                        }
 
600
                                                }
 
601
                                        }
 
602
                                });
 
603
                                this.tabOfPressedCloseButton = -1;
 
604
                        }
 
605
                }
 
606
        }
 
607
 
 
608
        /**
 
609
         * Creates the new UI delegate.
 
610
         */
 
611
        public SubstanceTabbedPaneUI() {
 
612
                super();
 
613
                this.stateTransitionMultiTracker = new StateTransitionMultiTracker<Integer>();
 
614
                this.currSelectedIndex = -1;
 
615
        }
 
616
 
 
617
        /*
 
618
         * (non-Javadoc)
 
619
         * 
 
620
         * @see javax.swing.plaf.basic.BasicTabbedPaneUI#installListeners()
 
621
         */
 
622
        @Override
 
623
        protected void installListeners() {
 
624
                super.installListeners();
 
625
                // Install listener to repaint the tabbed pane
 
626
                // on mouse move (for rollover effects).
 
627
                this.substanceRolloverHandler = new MouseRolloverHandler();
 
628
                this.tabPane.addMouseMotionListener(this.substanceRolloverHandler);
 
629
                this.tabPane.addMouseListener(this.substanceRolloverHandler);
 
630
 
 
631
                // Add container listener to wire property change listener
 
632
                // on each tab in the tabbed pane.
 
633
                this.substanceContainerListener = new TabbedContainerListener();
 
634
                this.substanceContainerListener.trackExistingTabs();
 
635
 
 
636
                for (int i = 0; i < this.tabPane.getTabCount(); i++) {
 
637
                        Component tabComp = this.tabPane.getComponentAt(i);
 
638
                        if (SubstanceCoreUtilities.isTabModified(tabComp)) {
 
639
                                trackTabModification(i, tabComp);
 
640
                        }
 
641
                }
 
642
 
 
643
                this.tabPane.addContainerListener(this.substanceContainerListener);
 
644
 
 
645
                this.substanceSelectionListener = new ChangeListener() {
 
646
                        @Override
 
647
            public void stateChanged(ChangeEvent e) {
 
648
                                SwingUtilities.invokeLater(new Runnable() {
 
649
                                        @Override
 
650
                    public void run() {
 
651
                                                if (SubstanceTabbedPaneUI.this.tabPane == null)
 
652
                                                        return;
 
653
                                                int selected = SubstanceTabbedPaneUI.this.tabPane
 
654
                                                                .getSelectedIndex();
 
655
 
 
656
                                                // fix for issue 437 - track the selection change,
 
657
                                                // fading out the previously selected tab
 
658
                                                if ((currSelectedIndex >= 0)
 
659
                                                                && (currSelectedIndex < SubstanceTabbedPaneUI.this.tabPane
 
660
                                                                                .getTabCount())
 
661
                                                                && SubstanceTabbedPaneUI.this.tabPane
 
662
                                                                                .isEnabledAt(currSelectedIndex)) {
 
663
                                                        StateTransitionTracker tracker = getTracker(
 
664
                                                                        currSelectedIndex,
 
665
                                                                        getRolloverTabIndex() == currSelectedIndex,
 
666
                                                                        true);
 
667
                                                        tracker.getModel().setSelected(false);
 
668
                                                }
 
669
                                                currSelectedIndex = selected;
 
670
                                                if ((selected >= 0)
 
671
                                                                && (selected < SubstanceTabbedPaneUI.this.tabPane
 
672
                                                                                .getTabCount())
 
673
                                                                && SubstanceTabbedPaneUI.this.tabPane
 
674
                                                                                .isEnabledAt(selected)) {
 
675
                                                        StateTransitionTracker tracker = getTracker(
 
676
                                                                        selected,
 
677
                                                                        getRolloverTabIndex() == selected, false);
 
678
                                                        tracker.getModel().setSelected(true);
 
679
                                                }
 
680
                                        }
 
681
                                });
 
682
                        }
 
683
                };
 
684
                this.tabPane.getModel().addChangeListener(
 
685
                                this.substanceSelectionListener);
 
686
        }
 
687
 
 
688
        /*
 
689
         * (non-Javadoc)
 
690
         * 
 
691
         * @see javax.swing.plaf.basic.BasicTabbedPaneUI#uninstallListeners()
 
692
         */
 
693
        @Override
 
694
        protected void uninstallListeners() {
 
695
                super.uninstallListeners();
 
696
                if (this.substanceRolloverHandler != null) {
 
697
                        this.tabPane
 
698
                                        .removeMouseMotionListener(this.substanceRolloverHandler);
 
699
                        this.tabPane.removeMouseListener(this.substanceRolloverHandler);
 
700
                        this.substanceRolloverHandler = null;
 
701
                }
 
702
                if (this.substanceContainerListener != null) {
 
703
                        for (Map.Entry<Component, List<PropertyChangeListener>> entry : this.substanceContainerListener.listeners
 
704
                                        .entrySet()) {
 
705
                                Component comp = entry.getKey();
 
706
                                // System.out.println(this.containerListener.hashCode() +"
 
707
                                // removing all for" + comp.hashCode());
 
708
                                for (PropertyChangeListener pcl : entry.getValue()) {
 
709
                                        comp.removePropertyChangeListener(pcl);
 
710
                                }
 
711
                        }
 
712
                        this.substanceContainerListener.listeners.clear();
 
713
 
 
714
                        this.tabPane
 
715
                                        .removeContainerListener(this.substanceContainerListener);
 
716
                        this.substanceContainerListener = null;
 
717
                }
 
718
                this.tabPane.getModel().removeChangeListener(
 
719
                                this.substanceSelectionListener);
 
720
                this.substanceSelectionListener = null;
 
721
        }
 
722
 
 
723
        /*
 
724
         * (non-Javadoc)
 
725
         * 
 
726
         * @see javax.swing.plaf.basic.BasicTabbedPaneUI#installDefaults()
 
727
         */
 
728
        @Override
 
729
        protected void installDefaults() {
 
730
                super.installDefaults();
 
731
 
 
732
                this.substanceContentOpaque = UIManager
 
733
                                .getBoolean("TabbedPane.contentOpaque");
 
734
 
 
735
                this.modifiedTimelines = new HashMap<Component, Timeline>();
 
736
                this.currSelectedIndex = this.tabPane.getSelectedIndex();
 
737
        }
 
738
 
 
739
        /*
 
740
         * (non-Javadoc)
 
741
         * 
 
742
         * @see javax.swing.plaf.basic.BasicTabbedPaneUI#uninstallDefaults()
 
743
         */
 
744
        @Override
 
745
        protected void uninstallDefaults() {
 
746
                for (Timeline timeline : this.modifiedTimelines.values())
 
747
                        timeline.cancel();
 
748
                this.modifiedTimelines.clear();
 
749
                super.uninstallDefaults();
 
750
        }
 
751
 
 
752
        /**
 
753
         * Retrieves tab background.
 
754
         * 
 
755
         * @param tabPane
 
756
         *            Tabbed pane.
 
757
         * @param width
 
758
         *            Tab width.
 
759
         * @param height
 
760
         *            Tab height.
 
761
         * @param tabPlacement
 
762
         *            Tab placement.
 
763
         * @param fillScheme
 
764
         *            Color scheme for coloring the background.
 
765
         * @param borderScheme
 
766
         *            Color scheme for coloring the border.
 
767
         * @param paintOnlyBorder
 
768
         *            If <code>true</code>, only the border will be painted.
 
769
         * @return Tab background of specified parameters.
 
770
         */
 
771
        private static BufferedImage getTabBackground(JTabbedPane tabPane,
 
772
                        int width, int height, int tabPlacement,
 
773
                        SubstanceColorScheme fillScheme, SubstanceColorScheme borderScheme,
 
774
                        boolean paintOnlyBorder) {
 
775
                SubstanceFillPainter fillPainter = SubstanceCoreUtilities
 
776
                                .getFillPainter(tabPane);
 
777
                SubstanceBorderPainter borderPainter = SubstanceCoreUtilities
 
778
                                .getBorderPainter(tabPane);
 
779
                SubstanceButtonShaper shaper = SubstanceCoreUtilities
 
780
                                .getButtonShaper(tabPane);
 
781
 
 
782
                int borderDelta = (int) Math.ceil(2.0 * SubstanceSizeUtils
 
783
                                .getBorderStrokeWidth(SubstanceSizeUtils
 
784
                                                .getComponentFontSize(tabPane)));
 
785
                int borderInsets = (int) Math.floor(SubstanceSizeUtils
 
786
                                .getBorderStrokeWidth(SubstanceSizeUtils
 
787
                                                .getComponentFontSize(tabPane)) / 2.0);
 
788
                int dy = 2 + borderDelta;
 
789
                Set<Side> straightSides = EnumSet.of(Side.BOTTOM);
 
790
 
 
791
                int cornerRadius = height / 3;
 
792
                if (shaper instanceof ClassicButtonShaper) {
 
793
                        cornerRadius = (int) SubstanceSizeUtils
 
794
                                        .getClassicButtonCornerRadius(SubstanceSizeUtils
 
795
                                                        .getComponentFontSize(tabPane));
 
796
                        if ((tabPlacement == TOP) || (tabPlacement == BOTTOM))
 
797
                                width -= 1;
 
798
                        else
 
799
                                height -= 1;
 
800
                }
 
801
 
 
802
                GeneralPath contour = SubstanceOutlineUtilities.getBaseOutline(width,
 
803
                                height + dy, cornerRadius, straightSides, borderInsets);
 
804
 
 
805
                BufferedImage result = SubstanceCoreUtilities.getBlankImage(width,
 
806
                                height);
 
807
                Graphics2D resGraphics = result.createGraphics();
 
808
 
 
809
                if (!paintOnlyBorder) {
 
810
                        fillPainter.paintContourBackground(resGraphics, tabPane, width,
 
811
                                        height + dy, contour, false, fillScheme, true);
 
812
                }
 
813
 
 
814
                int borderThickness = (int) SubstanceSizeUtils
 
815
                                .getBorderStrokeWidth(SubstanceSizeUtils
 
816
                                                .getComponentFontSize(tabPane));
 
817
                GeneralPath contourInner = borderPainter.isPaintingInnerContour() ? SubstanceOutlineUtilities
 
818
                                .getBaseOutline(width, height + dy, cornerRadius
 
819
                                                - borderThickness, straightSides, borderThickness
 
820
                                                + borderInsets)
 
821
                                : null;
 
822
 
 
823
                borderPainter.paintBorder(resGraphics, tabPane, width, height + dy,
 
824
                                contour, contourInner, borderScheme);
 
825
 
 
826
                resGraphics.dispose();
 
827
                return result;
 
828
        }
 
829
 
 
830
        /**
 
831
         * Retrieves tab background that will be shown on the screen. Unlike
 
832
         *
 
833
     * , the result is rotated as necessary (for {@link SwingConstants#LEFT} and
 
834
         * {@link SwingConstants#RIGHT} placement) and blended for selected tabs.
 
835
         * 
 
836
         * @param tabPane
 
837
         *            Tabbed pane.
 
838
         * @param tabIndex
 
839
         *            Tab index.
 
840
         * @param width
 
841
         *            Tab width.
 
842
         * @param height
 
843
         *            Tab height.
 
844
         * @param isSelected
 
845
         *            Indication whether the tab is selected.
 
846
         * @param tabPlacement
 
847
         *            Tab placement.
 
848
         * @param side
 
849
         *            Tab open side.
 
850
         * @param colorScheme
 
851
         *            Color scheme for coloring the background.
 
852
         * @param borderScheme
 
853
         *            Color scheme for coloring the border.
 
854
         * @return Tab background of specified parameters.
 
855
         */
 
856
        private static BufferedImage getFinalTabBackgroundImage(
 
857
                        JTabbedPane tabPane, int tabIndex, int x, int y, int width,
 
858
                        int height, boolean isSelected, int tabPlacement,
 
859
                        SubstanceConstants.Side side, SubstanceColorScheme colorScheme,
 
860
                        SubstanceColorScheme borderScheme) {
 
861
 
 
862
                SubstanceFillPainter fillPainter = SubstanceCoreUtilities
 
863
                                .getFillPainter(tabPane);
 
864
                SubstanceBorderPainter borderPainter = SubstanceCoreUtilities
 
865
                                .getBorderPainter(tabPane);
 
866
                SubstanceButtonShaper shaper = SubstanceCoreUtilities
 
867
                                .getButtonShaper(tabPane);
 
868
                Component compForBackground = tabPane.getTabComponentAt(tabIndex);
 
869
                if (compForBackground == null)
 
870
                        compForBackground = tabPane.getComponentAt(tabIndex);
 
871
                if (compForBackground == null)
 
872
                        compForBackground = tabPane;
 
873
                Color tabColor = compForBackground.getBackground();
 
874
                if (isSelected && (tabColor instanceof UIResource)) {
 
875
                        // special handling of tabs placed in decoration areas
 
876
                        tabColor = SubstanceColorUtilities
 
877
                                        .getBackgroundFillColor(compForBackground);
 
878
                }
 
879
                HashMapKey key = SubstanceCoreUtilities.getHashKey(width, height,
 
880
                                isSelected, tabPlacement, fillPainter.getDisplayName(),
 
881
                                borderPainter.getDisplayName(), shaper.getDisplayName(),
 
882
                                tabPlacement == SwingConstants.BOTTOM, side.name(), colorScheme
 
883
                                                .getDisplayName(), borderScheme.getDisplayName(),
 
884
                                tabColor);
 
885
 
 
886
                SubstanceSkin skin = SubstanceCoreUtilities.getSkin(tabPane);
 
887
                BufferedImage result = SubstanceTabbedPaneUI.backgroundMap.get(key);
 
888
                if (result == null) {
 
889
                        BufferedImage backgroundImage = null;
 
890
 
 
891
                        switch (tabPlacement) {
 
892
                        case BOTTOM:
 
893
                                return SubstanceImageCreator.getRotated(
 
894
                                                getFinalTabBackgroundImage(tabPane, tabIndex, x, y,
 
895
                                                                width, height, isSelected, SwingConstants.TOP,
 
896
                                                                side, colorScheme, borderScheme), 2);
 
897
                        case TOP:
 
898
                        case LEFT:
 
899
                        case RIGHT:
 
900
                                backgroundImage = SubstanceTabbedPaneUI.getTabBackground(
 
901
                                                tabPane, width, height, SwingConstants.TOP,
 
902
                                                colorScheme, borderScheme, false);
 
903
                                if (isSelected) {
 
904
                                        int fw = backgroundImage.getWidth();
 
905
                                        int fh = backgroundImage.getHeight();
 
906
                                        BufferedImage fade = SubstanceCoreUtilities.getBlankImage(
 
907
                                                        fw, fh);
 
908
                                        Graphics2D fadeGraphics = fade.createGraphics();
 
909
                                        fadeGraphics.setColor(tabColor);
 
910
                                        fadeGraphics.fillRect(0, 0, fw, fh);
 
911
                                        if (skin.getWatermark() != null) {
 
912
                                                fadeGraphics.translate(-x, -y);
 
913
                                                skin.getWatermark().drawWatermarkImage(fadeGraphics,
 
914
                                                                tabPane, x, y, fw, fh);
 
915
                                                fadeGraphics.translate(x, y);
 
916
                                        }
 
917
                                        fadeGraphics.drawImage(SubstanceTabbedPaneUI
 
918
                                                        .getTabBackground(tabPane, width, height,
 
919
                                                                        tabPlacement, colorScheme, borderScheme,
 
920
                                                                        true), 0, 0, null);
 
921
 
 
922
                                        backgroundImage = SubstanceCoreUtilities
 
923
                                                        .blendImagesVertical(backgroundImage, fade, skin
 
924
                                                                        .getSelectedTabFadeStart(), skin
 
925
                                                                        .getSelectedTabFadeEnd());
 
926
                                }
 
927
                        }
 
928
                        SubstanceTabbedPaneUI.backgroundMap.put(key, backgroundImage);
 
929
                }
 
930
                return backgroundMap.get(key);
 
931
        }
 
932
 
 
933
        /**
 
934
         * Retrieves the image of the close button.
 
935
         * 
 
936
         * @param tabPane
 
937
         *            Tabbed pane.
 
938
         * @param width
 
939
         *            Close button width.
 
940
         * @param height
 
941
         *            Close button height.
 
942
         * @param toPaintBorder
 
943
         *            Indication whether the button background (including contour)
 
944
         *            needs to be painted.
 
945
         * @param fillScheme
 
946
         *            Color scheme for coloring the background.
 
947
         * @param markScheme
 
948
         *            Color scheme for painting the close mark.
 
949
         * @return Image of the close button of specified parameters.
 
950
         */
 
951
        private static BufferedImage getCloseButtonImage(JTabbedPane tabPane,
 
952
                        int width, int height, boolean toPaintBorder,
 
953
                        SubstanceColorScheme fillScheme, SubstanceColorScheme markScheme) {
 
954
                SubstanceFillPainter fillPainter = SubstanceCoreUtilities
 
955
                                .getFillPainter(tabPane);
 
956
                if (fillPainter == null)
 
957
                        return null;
 
958
 
 
959
                HashMapKey key = SubstanceCoreUtilities.getHashKey(width, height,
 
960
                                toPaintBorder, fillPainter.getDisplayName(), fillScheme
 
961
                                                .getDisplayName(), markScheme.getDisplayName());
 
962
                BufferedImage result = SubstanceTabbedPaneUI.closeButtonMap.get(key);
 
963
                if (result == null) {
 
964
                        result = SubstanceCoreUtilities.getBlankImage(width, height);
 
965
                        Graphics2D finalGraphics = (Graphics2D) result.getGraphics();
 
966
 
 
967
                        if (toPaintBorder) {
 
968
                                GeneralPath contour = SubstanceOutlineUtilities.getBaseOutline(
 
969
                                                width, height, 1, null);
 
970
                                fillPainter.paintContourBackground(finalGraphics, tabPane,
 
971
                                                width, height, contour, false, fillScheme, true);
 
972
                                // finalGraphics.drawImage(background, 0, 0, null);
 
973
                                SubstanceBorderPainter borderPainter = SubstanceCoreUtilities
 
974
                                                .getBorderPainter(tabPane);
 
975
                                finalGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
 
976
                                                RenderingHints.VALUE_ANTIALIAS_ON);
 
977
                                borderPainter.paintBorder(finalGraphics, tabPane, width,
 
978
                                                height, contour, null, markScheme);
 
979
                        }
 
980
 
 
981
                        finalGraphics.setStroke(new BasicStroke(SubstanceSizeUtils
 
982
                                        .getTabCloseButtonStrokeWidth(SubstanceSizeUtils
 
983
                                                        .getComponentFontSize(tabPane))));
 
984
 
 
985
                        int delta = (int) (Math.floor(SubstanceSizeUtils
 
986
                                        .getBorderStrokeWidth(SubstanceSizeUtils
 
987
                                                        .getComponentFontSize(tabPane))));
 
988
                        if (delta % 2 != 0)
 
989
                                delta--;
 
990
                        int iconSize = width - delta;
 
991
 
 
992
                        Icon closeIcon = SubstanceImageCreator.getCloseIcon(iconSize,
 
993
                                        markScheme, markScheme);
 
994
                        closeIcon.paintIcon(tabPane, finalGraphics, delta / 2, delta / 2);
 
995
 
 
996
                        SubstanceTabbedPaneUI.closeButtonMap.put(key, result);
 
997
                }
 
998
                return result;
 
999
        }
 
1000
 
 
1001
        /*
 
1002
         * (non-Javadoc)
 
1003
         * 
 
1004
         * @see
 
1005
         * javax.swing.plaf.basic.BasicTabbedPaneUI#paintTabBackground(java.awt.
 
1006
         * Graphics, int, int, int, int, int, int, boolean)
 
1007
         */
 
1008
        @Override
 
1009
        protected void paintTabBackground(Graphics g, int tabPlacement,
 
1010
                        final int tabIndex, final int x, final int y, int w, int h,
 
1011
                        boolean isSelected) {
 
1012
                Graphics2D graphics = (Graphics2D) g.create();
 
1013
                graphics.setComposite(LafWidgetUtilities.getAlphaComposite(
 
1014
                                this.tabPane, g));
 
1015
 
 
1016
                boolean isEnabled = this.tabPane.isEnabledAt(tabIndex);
 
1017
                ComponentState currState = this.getTabState(tabIndex);
 
1018
                StateTransitionTracker.ModelStateInfo modelStateInfo = this
 
1019
                                .getModelStateInfo(tabIndex);
 
1020
 
 
1021
                SubstanceColorScheme baseBorderScheme = SubstanceColorSchemeUtilities
 
1022
                                .getColorScheme(this.tabPane, tabIndex,
 
1023
                                                ColorSchemeAssociationKind.TAB_BORDER, currState);
 
1024
                SubstanceColorScheme baseColorScheme = SubstanceColorSchemeUtilities
 
1025
                                .getColorScheme(this.tabPane, tabIndex,
 
1026
                                                ColorSchemeAssociationKind.TAB, currState);
 
1027
                BufferedImage fullOpacity = null;
 
1028
 
 
1029
                // check if have windowModified property
 
1030
                Component comp = this.tabPane.getComponentAt(tabIndex);
 
1031
                boolean isWindowModified = SubstanceCoreUtilities.isTabModified(comp);
 
1032
                boolean toMarkModifiedCloseButton = SubstanceCoreUtilities
 
1033
                                .toAnimateCloseIconOfModifiedTab(this.tabPane, tabIndex);
 
1034
                if (isWindowModified && isEnabled && !toMarkModifiedCloseButton) {
 
1035
                        SubstanceColorScheme colorScheme2 = SubstanceColorSchemeUtilities.YELLOW;
 
1036
                        SubstanceColorScheme colorScheme = SubstanceColorSchemeUtilities.ORANGE;
 
1037
 
 
1038
                        float cyclePos = this.modifiedTimelines.get(comp)
 
1039
                                        .getTimelinePosition();
 
1040
 
 
1041
                        BufferedImage layer1 = SubstanceTabbedPaneUI
 
1042
                                        .getFinalTabBackgroundImage(this.tabPane, tabIndex, x, y,
 
1043
                                                        w, h, isSelected, tabPlacement,
 
1044
                                                        SubstanceConstants.Side.BOTTOM, colorScheme,
 
1045
                                                        baseBorderScheme);
 
1046
                        BufferedImage layer2 = SubstanceTabbedPaneUI
 
1047
                                        .getFinalTabBackgroundImage(this.tabPane, tabIndex, x, y,
 
1048
                                                        w, h, isSelected, tabPlacement,
 
1049
                                                        SubstanceConstants.Side.BOTTOM, colorScheme2,
 
1050
                                                        baseBorderScheme);
 
1051
 
 
1052
                        fullOpacity = SubstanceCoreUtilities.getBlankImage(w, h);
 
1053
                        Graphics2D g2d = fullOpacity.createGraphics();
 
1054
                        if (cyclePos < 1.0f)
 
1055
                                g2d.drawImage(layer1, 0, 0, null);
 
1056
                        if (cyclePos > 0.0f) {
 
1057
                                g2d.setComposite(AlphaComposite.SrcOver.derive(cyclePos));
 
1058
                                g2d.drawImage(layer2, 0, 0, null);
 
1059
                        }
 
1060
                        g2d.dispose();
 
1061
                } else {
 
1062
                        BufferedImage layerBase = SubstanceTabbedPaneUI
 
1063
                                        .getFinalTabBackgroundImage(this.tabPane, tabIndex, x, y,
 
1064
                                                        w, h, isSelected, tabPlacement,
 
1065
                                                        SubstanceConstants.Side.BOTTOM, baseColorScheme,
 
1066
                                                        baseBorderScheme);
 
1067
 
 
1068
                        if ((modelStateInfo == null) || currState.isDisabled()
 
1069
                                        || (modelStateInfo.getStateContributionMap().size() == 1)) {
 
1070
                                fullOpacity = layerBase;
 
1071
                        } else {
 
1072
                                fullOpacity = SubstanceCoreUtilities.getBlankImage(w, h);
 
1073
                                Graphics2D g2d = fullOpacity.createGraphics();
 
1074
                                // draw the base layer
 
1075
                                g2d.drawImage(layerBase, 0, 0, null);
 
1076
 
 
1077
                                // draw the other active layers
 
1078
                                for (Map.Entry<ComponentState, StateTransitionTracker.StateContributionInfo> activeEntry : modelStateInfo
 
1079
                                                .getStateContributionMap().entrySet()) {
 
1080
                                        ComponentState activeState = activeEntry.getKey();
 
1081
                                        if (activeState == currState)
 
1082
                                                continue;
 
1083
 
 
1084
                                        float stateContribution = activeEntry.getValue()
 
1085
                                                        .getContribution();
 
1086
                                        if (stateContribution > 0.0f) {
 
1087
                                                g2d.setComposite(AlphaComposite.SrcOver
 
1088
                                                                .derive(stateContribution));
 
1089
                                                SubstanceColorScheme fillScheme = SubstanceColorSchemeUtilities
 
1090
                                                                .getColorScheme(this.tabPane, tabIndex,
 
1091
                                                                                ColorSchemeAssociationKind.TAB,
 
1092
                                                                                activeState);
 
1093
                                                SubstanceColorScheme borderScheme = SubstanceColorSchemeUtilities
 
1094
                                                                .getColorScheme(this.tabPane, tabIndex,
 
1095
                                                                                ColorSchemeAssociationKind.TAB_BORDER,
 
1096
                                                                                activeState);
 
1097
                                                BufferedImage layer = SubstanceTabbedPaneUI
 
1098
                                                                .getFinalTabBackgroundImage(this.tabPane,
 
1099
                                                                                tabIndex, x, y, w, h, isSelected,
 
1100
                                                                                tabPlacement,
 
1101
                                                                                SubstanceConstants.Side.BOTTOM,
 
1102
                                                                                fillScheme, borderScheme);
 
1103
                                                g2d.drawImage(layer, 0, 0, null);
 
1104
                                        }
 
1105
                                }
 
1106
                        }
 
1107
                }
 
1108
 
 
1109
                // at this point the 'fillOpacity' has all the relevant layers for the
 
1110
                // fill + border
 
1111
 
 
1112
                SubstanceColorScheme baseMarkScheme = SubstanceColorSchemeUtilities
 
1113
                                .getColorScheme(this.tabPane, tabIndex,
 
1114
                                                ColorSchemeAssociationKind.MARK, currState);
 
1115
 
 
1116
                // fix for defect 138
 
1117
                graphics.clip(new Rectangle(x, y, w, h));
 
1118
 
 
1119
                boolean isRollover = (this.getRolloverTab() == tabIndex);
 
1120
 
 
1121
                float finalAlpha = 0.5f;
 
1122
                StateTransitionTracker tabTracker = this.stateTransitionMultiTracker
 
1123
                                .getTracker(tabIndex);
 
1124
                if (modelStateInfo != null) {
 
1125
                        finalAlpha += 0.5f * tabTracker
 
1126
                                        .getFacetStrength(ComponentStateFacet.ROLLOVER);
 
1127
            if (tabTracker.getFacetStrength(ComponentStateFacet.SELECTION) == 1.0f) {
 
1128
                finalAlpha = 1.0f;
 
1129
            }
 
1130
        } else {
 
1131
                        ComponentState tabState = getTabState(tabIndex);
 
1132
                        if (tabState.isFacetActive(ComponentStateFacet.ROLLOVER)
 
1133
                                        || tabState.isFacetActive(ComponentStateFacet.SELECTION)) {
 
1134
                                finalAlpha = 1.0f;
 
1135
                        }
 
1136
                }
 
1137
 
 
1138
                finalAlpha *= SubstanceColorSchemeUtilities.getAlpha(this.tabPane
 
1139
                                .getComponentAt(tabIndex), currState);
 
1140
 
 
1141
                graphics.setComposite(LafWidgetUtilities.getAlphaComposite(
 
1142
                                this.tabPane, finalAlpha, g));
 
1143
                graphics.drawImage(fullOpacity, x, y, null);
 
1144
 
 
1145
                // Check if requested to paint close buttons.
 
1146
                if (SubstanceCoreUtilities.hasCloseButton(this.tabPane, tabIndex)
 
1147
                                && isEnabled) {
 
1148
 
 
1149
                        float alpha = (isSelected || isRollover) ? 1.0f : 0.0f;
 
1150
                        if (!isSelected) {
 
1151
                                if (tabTracker != null) {
 
1152
                                        alpha = tabTracker
 
1153
                                                        .getFacetStrength(ComponentStateFacet.ROLLOVER);
 
1154
                                }
 
1155
                        }
 
1156
                        if (alpha > 0.0) {
 
1157
                                graphics.setComposite(LafWidgetUtilities.getAlphaComposite(
 
1158
                                                this.tabPane, finalAlpha * alpha, g));
 
1159
 
 
1160
                                // paint close button
 
1161
                                Rectangle orig = this.getCloseButtonRectangleForDraw(tabIndex,
 
1162
                                                x, y, w, h);
 
1163
 
 
1164
                                boolean toPaintCloseBorder = false;
 
1165
                                if (isRollover) {
 
1166
                                        if (this.substanceMouseLocation != null) {
 
1167
                                                Rectangle bounds = new Rectangle();
 
1168
                                                bounds = this.getTabBounds(tabIndex, bounds);
 
1169
                                                if (toRotateTabsOnPlacement(tabPlacement)) {
 
1170
                                                        bounds = new Rectangle(bounds.x, bounds.y,
 
1171
                                                                        bounds.height, bounds.width);
 
1172
                                                }
 
1173
                                                Rectangle rect = this.getCloseButtonRectangleForEvents(
 
1174
                                                                tabIndex, bounds.x, bounds.y, bounds.width,
 
1175
                                                                bounds.height);
 
1176
                                                // System.out.println("paint " + bounds + " " + rect +"
 
1177
                                                // "
 
1178
                                                // + mouseLocation);
 
1179
                                                if (rect.contains(this.substanceMouseLocation)) {
 
1180
                                                        toPaintCloseBorder = true;
 
1181
                                                }
 
1182
                                        }
 
1183
                                }
 
1184
 
 
1185
                                if (isWindowModified && isEnabled && toMarkModifiedCloseButton) {
 
1186
                                        SubstanceColorScheme colorScheme2 = SubstanceColorSchemeUtilities.YELLOW;
 
1187
                                        SubstanceColorScheme colorScheme = SubstanceColorSchemeUtilities.ORANGE;
 
1188
 
 
1189
                                        float cyclePos = this.modifiedTimelines.get(comp)
 
1190
                                                        .getTimelinePosition();
 
1191
 
 
1192
                                        BufferedImage layer1 = SubstanceTabbedPaneUI
 
1193
                                                        .getCloseButtonImage(this.tabPane, orig.width,
 
1194
                                                                        orig.height, toPaintCloseBorder,
 
1195
                                                                        colorScheme, baseMarkScheme);
 
1196
                                        BufferedImage layer2 = SubstanceTabbedPaneUI
 
1197
                                                        .getCloseButtonImage(this.tabPane, orig.width,
 
1198
                                                                        orig.height, toPaintCloseBorder,
 
1199
                                                                        colorScheme2, baseMarkScheme);
 
1200
 
 
1201
                                        if (cyclePos < 1.0f) {
 
1202
                                                graphics.drawImage(layer1, orig.x, orig.y, null);
 
1203
                                        }
 
1204
                                        if (cyclePos > 0.0f) {
 
1205
                                                graphics.setComposite(AlphaComposite.SrcOver
 
1206
                                                                .derive(cyclePos));
 
1207
                                                graphics.drawImage(layer2, orig.x, orig.y, null);
 
1208
                                        }
 
1209
                                } else {
 
1210
                                        BufferedImage layerBase = SubstanceTabbedPaneUI
 
1211
                                                        .getCloseButtonImage(this.tabPane, orig.width,
 
1212
                                                                        orig.height, toPaintCloseBorder,
 
1213
                                                                        baseColorScheme, baseMarkScheme);
 
1214
 
 
1215
                                        if ((modelStateInfo == null)
 
1216
                                                        || currState.isDisabled()
 
1217
                                                        || (modelStateInfo.getStateContributionMap().size() == 1)) {
 
1218
                                                graphics.drawImage(layerBase, orig.x, orig.y, null);
 
1219
                                        } else {
 
1220
                                                BufferedImage complete = SubstanceCoreUtilities
 
1221
                                                                .getBlankImage(orig.width, orig.height);
 
1222
                                                Graphics2D g2d = complete.createGraphics();
 
1223
                                                // draw the base layer
 
1224
                                                g2d.drawImage(layerBase, 0, 0, null);
 
1225
 
 
1226
                                                // draw the other active layers
 
1227
                                                for (Map.Entry<ComponentState, StateTransitionTracker.StateContributionInfo> activeEntry : modelStateInfo
 
1228
                                                                .getStateContributionMap().entrySet()) {
 
1229
                                                        ComponentState activeState = activeEntry.getKey();
 
1230
                                                        if (activeState == currState)
 
1231
                                                                continue;
 
1232
 
 
1233
                                                        float stateContribution = activeEntry.getValue()
 
1234
                                                                        .getContribution();
 
1235
                                                        if (stateContribution > 0.0f) {
 
1236
                                                                g2d.setComposite(AlphaComposite.SrcOver
 
1237
                                                                                .derive(stateContribution));
 
1238
                                                                SubstanceColorScheme fillScheme = SubstanceColorSchemeUtilities
 
1239
                                                                                .getColorScheme(this.tabPane, tabIndex,
 
1240
                                                                                                ColorSchemeAssociationKind.TAB,
 
1241
                                                                                                activeState);
 
1242
                                                                SubstanceColorScheme markScheme = SubstanceColorSchemeUtilities
 
1243
                                                                                .getColorScheme(
 
1244
                                                                                                this.tabPane,
 
1245
                                                                                                tabIndex,
 
1246
                                                                                                ColorSchemeAssociationKind.MARK,
 
1247
                                                                                                activeState);
 
1248
                                                                BufferedImage layer = SubstanceTabbedPaneUI
 
1249
                                                                                .getCloseButtonImage(this.tabPane,
 
1250
                                                                                                orig.width, orig.height,
 
1251
                                                                                                toPaintCloseBorder, fillScheme,
 
1252
                                                                                                markScheme);
 
1253
                                                                g2d.drawImage(layer, 0, 0, null);
 
1254
                                                        }
 
1255
                                                }
 
1256
                                                g2d.dispose();
 
1257
                                                graphics.drawImage(complete, orig.x, orig.y, null);
 
1258
                                        }
 
1259
                                }
 
1260
                        }
 
1261
                }
 
1262
 
 
1263
                graphics.dispose();
 
1264
        }
 
1265
 
 
1266
        /*
 
1267
         * (non-Javadoc)
 
1268
         * 
 
1269
         * @see
 
1270
         * javax.swing.plaf.basic.BasicTabbedPaneUI#paintFocusIndicator(java.awt
 
1271
         * .Graphics, int, java.awt.Rectangle[], int, java.awt.Rectangle,
 
1272
         * java.awt.Rectangle, boolean)
 
1273
         */
 
1274
        @Override
 
1275
        protected void paintFocusIndicator(Graphics g, int tabPlacement,
 
1276
                        Rectangle[] rects, int tabIndex, Rectangle iconRect,
 
1277
                        Rectangle textRect, boolean isSelected) {
 
1278
                // empty to remove Basic functionality
 
1279
        }
 
1280
 
 
1281
        /*
 
1282
         * (non-Javadoc)
 
1283
         * 
 
1284
         * @see
 
1285
         * javax.swing.plaf.basic.BasicTabbedPaneUI#paintTabBorder(java.awt.Graphics
 
1286
         * , int, int, int, int, int, int, boolean)
 
1287
         */
 
1288
        @Override
 
1289
        protected void paintTabBorder(Graphics g, int tabPlacement, int tabIndex,
 
1290
                        int x, int y, int w, int h, boolean isSelected) {
 
1291
                // empty to remove Basic functionality
 
1292
        }
 
1293
 
 
1294
        /*
 
1295
         * (non-Javadoc)
 
1296
         * 
 
1297
         * @see javax.swing.plaf.basic.BasicTabbedPaneUI#createScrollButton(int)
 
1298
         */
 
1299
        @Override
 
1300
        protected JButton createScrollButton(final int direction) {
 
1301
                SubstanceScrollButton ssb = new SubstanceScrollButton(direction);
 
1302
                Icon icon = new TransitionAwareIcon(ssb,
 
1303
                                new TransitionAwareIcon.Delegate() {
 
1304
                                        @Override
 
1305
                    public Icon getColorSchemeIcon(SubstanceColorScheme scheme) {
 
1306
                                                // fix for defect 279 - tab pane might not yet have the
 
1307
                                                // font installed.
 
1308
                                                int fontSize = SubstanceSizeUtils
 
1309
                                                                .getComponentFontSize(tabPane);
 
1310
                                                return SubstanceImageCreator.getArrowIcon(fontSize,
 
1311
                                                                direction, scheme);
 
1312
                                        }
 
1313
                                }, "substance.tabbedpane.scroll." + direction);
 
1314
                ssb.setIcon(icon);
 
1315
                return ssb;
 
1316
        }
 
1317
 
 
1318
        /*
 
1319
         * (non-Javadoc)
 
1320
         * 
 
1321
         * @see javax.swing.plaf.basic.BasicTabbedPaneUI#calculateTabHeight(int,
 
1322
         * int, int)
 
1323
         */
 
1324
        @Override
 
1325
        protected int calculateTabHeight(int tabPlacement, int tabIndex,
 
1326
                        int fontHeight) {
 
1327
                boolean toSwap = toRotateTabsOnPlacement(tabPlacement);
 
1328
                if (toSwap)
 
1329
                        return this.getTabExtraWidth(tabPlacement, tabIndex)
 
1330
                                        + super.calculateTabWidth(tabPlacement, tabIndex, this
 
1331
                                                        .getFontMetrics());
 
1332
                return super.calculateTabHeight(tabPlacement, tabIndex, fontHeight);
 
1333
        }
 
1334
 
 
1335
        /*
 
1336
         * (non-Javadoc)
 
1337
         * 
 
1338
         * @see javax.swing.plaf.basic.BasicTabbedPaneUI#calculateTabWidth(int, int,
 
1339
         * java.awt.FontMetrics)
 
1340
         */
 
1341
        @Override
 
1342
        protected int calculateTabWidth(int tabPlacement, int tabIndex,
 
1343
                        FontMetrics metrics) {
 
1344
                boolean toSwap = toRotateTabsOnPlacement(tabPlacement);
 
1345
                if (toSwap)
 
1346
                        return super.calculateTabHeight(tabPlacement, tabIndex, metrics
 
1347
                                        .getHeight());
 
1348
                int result = this.getTabExtraWidth(tabPlacement, tabIndex)
 
1349
                                + super.calculateTabWidth(tabPlacement, tabIndex, metrics);
 
1350
                return result;
 
1351
        }
 
1352
 
 
1353
        /*
 
1354
         * (non-Javadoc)
 
1355
         * 
 
1356
         * @see javax.swing.plaf.basic.BasicTabbedPaneUI#calculateMaxTabHeight(int)
 
1357
         */
 
1358
        @Override
 
1359
        protected int calculateMaxTabHeight(int tabPlacement) {
 
1360
                if (toRotateTabsOnPlacement(tabPlacement))
 
1361
                        return super.calculateMaxTabHeight(tabPlacement);
 
1362
                int result = 0;
 
1363
                for (int i = 0; i < this.tabPane.getTabCount(); i++)
 
1364
                        result = Math.max(result, this.calculateTabHeight(tabPlacement, i,
 
1365
                                        this.getFontMetrics().getHeight()));
 
1366
                return result;
 
1367
        }
 
1368
 
 
1369
        /*
 
1370
         * (non-Javadoc)
 
1371
         * 
 
1372
         * @see javax.swing.plaf.basic.BasicTabbedPaneUI#getTabRunOverlay(int)
 
1373
         */
 
1374
        @Override
 
1375
        protected int getTabRunOverlay(int tabPlacement) {
 
1376
                boolean toSwap = this.toRotateTabsOnPlacement(tabPlacement);
 
1377
                if (toSwap)
 
1378
                        return super.getTabRunOverlay(tabPlacement);
 
1379
 
 
1380
                return 0;
 
1381
        }
 
1382
 
 
1383
        @Override
 
1384
        public void paint(Graphics g, JComponent c) {
 
1385
                int selectedIndex = tabPane.getSelectedIndex();
 
1386
                int tabPlacement = tabPane.getTabPlacement();
 
1387
 
 
1388
                ensureCurrentLayout();
 
1389
 
 
1390
                // If scrollable tabs are enabled, the tab area will be
 
1391
                // painted by the scrollable tab panel instead.
 
1392
                if (tabPane.getLayout().getClass() == TabbedPaneLayout.class) {
 
1393
                        paintTabArea(g, tabPlacement, selectedIndex);
 
1394
                }
 
1395
 
 
1396
                int width = tabPane.getWidth();
 
1397
                int height = tabPane.getHeight();
 
1398
                Insets insets = tabPane.getInsets();
 
1399
 
 
1400
                int x = insets.left;
 
1401
                int y = insets.top;
 
1402
                int w = width - insets.right - insets.left;
 
1403
                int h = height - insets.top - insets.bottom;
 
1404
 
 
1405
                switch (tabPlacement) {
 
1406
                case LEFT:
 
1407
                        x += calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth);
 
1408
                        w -= (x - insets.left);
 
1409
                        break;
 
1410
                case RIGHT:
 
1411
                        w -= calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth);
 
1412
                        break;
 
1413
                case BOTTOM:
 
1414
                        h -= calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight);
 
1415
                        break;
 
1416
                case TOP:
 
1417
                default:
 
1418
                        y += calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight);
 
1419
                        h -= (y - insets.top);
 
1420
                }
 
1421
 
 
1422
                Graphics2D g2d = (Graphics2D) g.create(x, y, w, h);
 
1423
                BackgroundPaintingUtils.update(g2d, c, false);
 
1424
 
 
1425
                paintContentBorder(g, tabPlacement, selectedIndex);
 
1426
        }
 
1427
 
 
1428
        /*
 
1429
         * (non-Javadoc)
 
1430
         * 
 
1431
         * @see javax.swing.plaf.basic.BasicTabbedPaneUI#paintTab(java.awt.Graphics,
 
1432
         * int, java.awt.Rectangle[], int, java.awt.Rectangle, java.awt.Rectangle)
 
1433
         */
 
1434
        @Override
 
1435
        protected void paintTab(Graphics g, int tabPlacement, Rectangle[] rects,
 
1436
                        int tabIndex, Rectangle iconRect, Rectangle textRect) {
 
1437
                boolean toSwap = toRotateTabsOnPlacement(tabPlacement);
 
1438
                if (toSwap) {
 
1439
                        Graphics2D tempG = (Graphics2D) g.create();
 
1440
                        Rectangle tabRect = rects[tabIndex];
 
1441
                        Rectangle correctRect = new Rectangle(tabRect.x, tabRect.y,
 
1442
                                        tabRect.height, tabRect.width);
 
1443
                        if (tabPlacement == SwingConstants.LEFT) {
 
1444
                                // rotate 90 degrees counterclockwise for LEFT orientation
 
1445
                                tempG.rotate(-Math.PI / 2, tabRect.x, tabRect.y);
 
1446
                                tempG.translate(-tabRect.height, 0);
 
1447
                        } else {
 
1448
                                // rotate 90 degrees clockwise for RIGHT orientation
 
1449
                                tempG.rotate(Math.PI / 2, tabRect.x, tabRect.y);
 
1450
                                tempG.translate(0, -tabRect.getWidth());
 
1451
                        }
 
1452
                        tempG.setColor(Color.red);
 
1453
                        rects[tabIndex] = correctRect;
 
1454
                        super.paintTab(tempG, tabPlacement, rects, tabIndex, iconRect,
 
1455
                                        textRect);
 
1456
                        rects[tabIndex] = tabRect;
 
1457
                        tempG.dispose();
 
1458
                } else {
 
1459
                        if (tabPane.getLayout().getClass() == TabbedPaneLayout.class) {
 
1460
                                super.paintTab(g, tabPlacement, rects, tabIndex, iconRect,
 
1461
                                                textRect);
 
1462
                        } else {
 
1463
                                // scrolled tabs are painted by
 
1464
                                // BasicTabbedPaneUI.ScrollableTabPanel
 
1465
                                // which does not have the right rendering hints
 
1466
                                Graphics2D g2d = (Graphics2D) g.create();
 
1467
                                RenderingUtils.installDesktopHints(g2d, tabPane);
 
1468
                                super.paintTab(g2d, tabPlacement, rects, tabIndex, iconRect,
 
1469
                                                textRect);
 
1470
                                g2d.dispose();
 
1471
                        }
 
1472
                }
 
1473
        }
 
1474
 
 
1475
        /*
 
1476
         * (non-Javadoc)
 
1477
         * 
 
1478
         * @see
 
1479
         * javax.swing.plaf.basic.BasicTabbedPaneUI#paintTabArea(java.awt.Graphics,
 
1480
         * int, int)
 
1481
         */
 
1482
        @Override
 
1483
        protected void paintTabArea(Graphics g, int tabPlacement, int selectedIndex) {
 
1484
                if (this.substanceContentOpaque) {
 
1485
                        int width = calculateTabAreaWidth(tabPlacement, runCount,
 
1486
                                        maxTabWidth);
 
1487
                        if ((tabPlacement == SwingConstants.TOP)
 
1488
                                        || (tabPlacement == SwingConstants.BOTTOM))
 
1489
                                width = Math.max(width, tabPane.getWidth());
 
1490
                        int height = calculateTabAreaHeight(tabPlacement, runCount,
 
1491
                                        maxTabHeight);
 
1492
                        if (toRotateTabsOnPlacement(tabPlacement))
 
1493
                                height = Math.max(height, tabPane.getHeight());
 
1494
 
 
1495
                        // restrict the painting to the tab area only
 
1496
                        Graphics2D g2d = (Graphics2D) g.create(0, 0, width, height);
 
1497
                        BackgroundPaintingUtils.update(g2d, this.tabPane, true);
 
1498
                        g2d.dispose();
 
1499
                }
 
1500
                super.paintTabArea(g, tabPlacement, selectedIndex);
 
1501
        }
 
1502
 
 
1503
        /**
 
1504
         * Retrieves the close button rectangle for drawing purposes.
 
1505
         * 
 
1506
         * @param tabIndex
 
1507
         *            Tab index.
 
1508
         * @param x
 
1509
         *            X coordinate of the tab.
 
1510
         * @param y
 
1511
         *            Y coordinate of the tab.
 
1512
         * @param width
 
1513
         *            The tab width.
 
1514
         * @param height
 
1515
         *            The tab height.
 
1516
         * @return The close button rectangle.
 
1517
         */
 
1518
        protected Rectangle getCloseButtonRectangleForDraw(int tabIndex, int x,
 
1519
                        int y, int width, int height) {
 
1520
                int dimension = SubstanceCoreUtilities.getCloseButtonSize(this.tabPane,
 
1521
                                tabIndex);
 
1522
 
 
1523
                int borderDelta = (int) Math.ceil(3.0 * SubstanceSizeUtils
 
1524
                                .getBorderStrokeWidth(SubstanceSizeUtils
 
1525
                                                .getComponentFontSize(this.tabPane)));
 
1526
 
 
1527
                int xs = this.tabPane.getComponentOrientation().isLeftToRight() ? (x
 
1528
                                + width - dimension - borderDelta) : (x + borderDelta);
 
1529
                int ys = y + (height - dimension) / 2 + 1;
 
1530
                return new Rectangle(xs, ys, dimension, dimension);
 
1531
        }
 
1532
 
 
1533
        /**
 
1534
         * Retrieves the close button rectangle for event handling.
 
1535
         * 
 
1536
         * @param tabIndex
 
1537
         *            Tab index.
 
1538
         * @param x
 
1539
         *            X coordinate of the tab.
 
1540
         * @param y
 
1541
         *            Y coordinate of the tab.
 
1542
         * @param w
 
1543
         *            The tab width.
 
1544
         * @param h
 
1545
         *            The tab height.
 
1546
         * @return The close button rectangle.
 
1547
         */
 
1548
        protected Rectangle getCloseButtonRectangleForEvents(int tabIndex, int x,
 
1549
                        int y, int w, int h) {
 
1550
                int tabPlacement = this.tabPane.getTabPlacement();
 
1551
                boolean toSwap = toRotateTabsOnPlacement(tabPlacement);
 
1552
                if (!toSwap) {
 
1553
                        return this.getCloseButtonRectangleForDraw(tabIndex, x, y, w, h);
 
1554
                }
 
1555
                int dimension = SubstanceCoreUtilities.getCloseButtonSize(this.tabPane,
 
1556
                                tabIndex);
 
1557
 
 
1558
                Point2D transCorner = null;
 
1559
                Rectangle rectForDraw = this.getCloseButtonRectangleForDraw(tabIndex,
 
1560
                                x, y, h, w);
 
1561
                if (tabPlacement == SwingConstants.LEFT) {
 
1562
                        AffineTransform trans = new AffineTransform();
 
1563
                        trans.rotate(-Math.PI / 2, x, y);
 
1564
                        trans.translate(-h, 0);
 
1565
                        Point2D.Double origCorner = new Point2D.Double(rectForDraw
 
1566
                                        .getMaxX(), rectForDraw.getMinY());
 
1567
                        transCorner = trans.transform(origCorner, null);
 
1568
                } else {
 
1569
                        // rotate 90 degrees clockwise for RIGHT orientation
 
1570
                        AffineTransform trans = new AffineTransform();
 
1571
                        trans.rotate(Math.PI / 2, x, y);
 
1572
                        trans.translate(0, -w);
 
1573
                        Point2D.Double origCorner = new Point2D.Double(rectForDraw
 
1574
                                        .getMinX(), rectForDraw.getMaxY());
 
1575
                        transCorner = trans.transform(origCorner, null);
 
1576
                }
 
1577
                return new Rectangle((int) transCorner.getX(),
 
1578
                                (int) transCorner.getY(), dimension, dimension);
 
1579
        }
 
1580
 
 
1581
        /**
 
1582
         * Implementation of the fade tracker callback that repaints a single tab.
 
1583
         * 
 
1584
         * @author Kirill Grouchnikov
 
1585
         */
 
1586
        protected class TabRepaintCallback extends UIThreadTimelineCallbackAdapter {
 
1587
                /**
 
1588
                 * The associated tabbed pane.
 
1589
                 */
 
1590
                protected JTabbedPane tabbedPane;
 
1591
 
 
1592
                /**
 
1593
                 * The associated tab index.
 
1594
                 */
 
1595
                protected int tabIndex;
 
1596
 
 
1597
                /**
 
1598
                 * Creates new tab repaint callback.
 
1599
                 * 
 
1600
                 * @param tabPane
 
1601
                 *            The associated tabbed pane.
 
1602
                 * @param tabIndex
 
1603
                 *            The associated tab index.
 
1604
                 */
 
1605
                public TabRepaintCallback(JTabbedPane tabPane, int tabIndex) {
 
1606
                        this.tabbedPane = tabPane;
 
1607
                        this.tabIndex = tabIndex;
 
1608
                }
 
1609
 
 
1610
                @Override
 
1611
                public void onTimelinePulse(float durationFraction,
 
1612
                                float timelinePosition) {
 
1613
                        this.repaintTab();
 
1614
                }
 
1615
 
 
1616
                @Override
 
1617
                public void onTimelineStateChanged(TimelineState oldState,
 
1618
                                TimelineState newState, float durationFraction,
 
1619
                                float timelinePosition) {
 
1620
                        this.repaintTab();
 
1621
                }
 
1622
 
 
1623
                /**
 
1624
                 * Repaints the relevant tab.
 
1625
                 */
 
1626
                protected void repaintTab() {
 
1627
                        SwingUtilities.invokeLater(new Runnable() {
 
1628
                                @Override
 
1629
                public void run() {
 
1630
                                        if (SubstanceTabbedPaneUI.this.tabPane == null) {
 
1631
                                                // may happen if the LAF was switched in the meantime
 
1632
                                                return;
 
1633
                                        }
 
1634
                                        SubstanceTabbedPaneUI.this.ensureCurrentLayout();
 
1635
                                        int tabCount = SubstanceTabbedPaneUI.this.tabPane
 
1636
                                                        .getTabCount();
 
1637
                                        if ((tabCount > 0)
 
1638
                                                        && (TabRepaintCallback.this.tabIndex < tabCount)
 
1639
                                                        && (TabRepaintCallback.this.tabIndex < SubstanceTabbedPaneUI.this.rects.length)) {
 
1640
                                                // need to retrieve the tab rectangle since the tabs
 
1641
                                                // can be moved while animating (especially when the
 
1642
                                                // current layout is SCROLL_LAYOUT)
 
1643
                                                Rectangle rect = SubstanceTabbedPaneUI.this
 
1644
                                                                .getTabBounds(
 
1645
                                                                                SubstanceTabbedPaneUI.this.tabPane,
 
1646
                                                                                TabRepaintCallback.this.tabIndex);
 
1647
                                                // System.out.println("Repainting " + tabIndex);
 
1648
                                                SubstanceTabbedPaneUI.this.tabPane.repaint(rect);
 
1649
                                        }
 
1650
                                }
 
1651
                        });
 
1652
                }
 
1653
        }
 
1654
 
 
1655
        /**
 
1656
         * Ensures the current layout.
 
1657
         */
 
1658
        protected void ensureCurrentLayout() {
 
1659
                if (!this.tabPane.isValid()) {
 
1660
                        this.tabPane.validate();
 
1661
                }
 
1662
                /*
 
1663
                 * If tabPane doesn't have a peer yet, the validate() call will silently
 
1664
                 * fail. We handle that by forcing a layout if tabPane is still invalid.
 
1665
                 * See bug 4237677.
 
1666
                 */
 
1667
                if (!this.tabPane.isValid()) {
 
1668
                        LayoutManager lm = this.tabPane.getLayout();
 
1669
                        if (lm instanceof BasicTabbedPaneUI.TabbedPaneLayout) {
 
1670
                                BasicTabbedPaneUI.TabbedPaneLayout layout = (BasicTabbedPaneUI.TabbedPaneLayout) lm;
 
1671
                                layout.calculateLayoutInfo();
 
1672
                        }
 
1673
                }
 
1674
        }
 
1675
 
 
1676
        /**
 
1677
         * Tries closing tabs based on the specified tab index and tab close kind.
 
1678
         * 
 
1679
         * @param tabIndex
 
1680
         *            Tab index.
 
1681
         * @param tabCloseKind
 
1682
         *            Tab close kind.
 
1683
         */
 
1684
        protected void tryCloseTabs(int tabIndex, TabCloseKind tabCloseKind) {
 
1685
                if (tabCloseKind == null)
 
1686
                        return;
 
1687
                if (tabCloseKind == TabCloseKind.NONE)
 
1688
                        return;
 
1689
 
 
1690
                if (tabCloseKind == TabCloseKind.ALL_BUT_THIS) {
 
1691
                        // close all but this
 
1692
                        Set<Integer> indexes = new HashSet<Integer>();
 
1693
                        for (int i = 0; i < this.tabPane.getTabCount(); i++)
 
1694
                                if (i != tabIndex)
 
1695
                                        indexes.add(i);
 
1696
                        this.tryCloseTabs(indexes);
 
1697
                        return;
 
1698
                }
 
1699
                if (tabCloseKind == TabCloseKind.ALL) {
 
1700
                        // close all
 
1701
                        Set<Integer> indexes = new HashSet<Integer>();
 
1702
                        for (int i = 0; i < this.tabPane.getTabCount(); i++)
 
1703
                                indexes.add(i);
 
1704
                        this.tryCloseTabs(indexes);
 
1705
                        return;
 
1706
                }
 
1707
                this.tryCloseTab(tabIndex);
 
1708
        }
 
1709
 
 
1710
        /**
 
1711
         * Tries closing a single tab.
 
1712
         * 
 
1713
         * @param tabIndex
 
1714
         *            Tab index.
 
1715
         */
 
1716
        protected void tryCloseTab(int tabIndex) {
 
1717
                Component component = this.tabPane.getComponentAt(tabIndex);
 
1718
                Set<Component> componentSet = new HashSet<Component>();
 
1719
                componentSet.add(component);
 
1720
 
 
1721
                // check if there's at least one listener
 
1722
                // that vetoes the closing
 
1723
                boolean isVetoed = false;
 
1724
                for (BaseTabCloseListener listener : SubstanceLookAndFeel
 
1725
                                .getAllTabCloseListeners(this.tabPane)) {
 
1726
                        if (listener instanceof VetoableTabCloseListener) {
 
1727
                                VetoableTabCloseListener vetoableListener = (VetoableTabCloseListener) listener;
 
1728
                                isVetoed = isVetoed
 
1729
                                                || vetoableListener.vetoTabClosing(this.tabPane,
 
1730
                                                                component);
 
1731
                        }
 
1732
                        if (listener instanceof VetoableMultipleTabCloseListener) {
 
1733
                                VetoableMultipleTabCloseListener vetoableListener = (VetoableMultipleTabCloseListener) listener;
 
1734
                                isVetoed = isVetoed
 
1735
                                                || vetoableListener.vetoTabsClosing(this.tabPane,
 
1736
                                                                componentSet);
 
1737
                        }
 
1738
                }
 
1739
                if (isVetoed)
 
1740
                        return;
 
1741
 
 
1742
                for (BaseTabCloseListener listener : SubstanceLookAndFeel
 
1743
                                .getAllTabCloseListeners(this.tabPane)) {
 
1744
                        if (listener instanceof TabCloseListener)
 
1745
                                ((TabCloseListener) listener).tabClosing(this.tabPane,
 
1746
                                                component);
 
1747
                        if (listener instanceof MultipleTabCloseListener)
 
1748
                                ((MultipleTabCloseListener) listener).tabsClosing(this.tabPane,
 
1749
                                                componentSet);
 
1750
                }
 
1751
 
 
1752
                this.tabPane.remove(tabIndex);
 
1753
                if (this.tabPane.getTabCount() > 0) {
 
1754
                        this.selectPreviousTab(0);
 
1755
                        this.selectNextTab(this.tabPane.getSelectedIndex());
 
1756
                }
 
1757
                this.tabPane.repaint();
 
1758
 
 
1759
                for (BaseTabCloseListener listener : SubstanceLookAndFeel
 
1760
                                .getAllTabCloseListeners(this.tabPane)) {
 
1761
                        if (listener instanceof TabCloseListener)
 
1762
                                ((TabCloseListener) listener)
 
1763
                                                .tabClosed(this.tabPane, component);
 
1764
                        if (listener instanceof MultipleTabCloseListener)
 
1765
                                ((MultipleTabCloseListener) listener).tabsClosed(this.tabPane,
 
1766
                                                componentSet);
 
1767
                }
 
1768
        }
 
1769
 
 
1770
        /**
 
1771
         * Tries closing the specified tabs.
 
1772
         * 
 
1773
         * @param tabIndexes
 
1774
         *            Tab indexes.
 
1775
         */
 
1776
        protected void tryCloseTabs(Set<Integer> tabIndexes) {
 
1777
                Set<Component> componentSet = new HashSet<Component>();
 
1778
                for (int tabIndex : tabIndexes) {
 
1779
                        componentSet.add(this.tabPane.getComponentAt(tabIndex));
 
1780
                }
 
1781
 
 
1782
                // check if there's at least one listener
 
1783
                // that vetoes the closing
 
1784
                boolean isVetoed = false;
 
1785
                for (BaseTabCloseListener listener : SubstanceLookAndFeel
 
1786
                                .getAllTabCloseListeners(this.tabPane)) {
 
1787
                        if (listener instanceof VetoableMultipleTabCloseListener) {
 
1788
                                VetoableMultipleTabCloseListener vetoableListener = (VetoableMultipleTabCloseListener) listener;
 
1789
                                isVetoed = isVetoed
 
1790
                                                || vetoableListener.vetoTabsClosing(this.tabPane,
 
1791
                                                                componentSet);
 
1792
                        }
 
1793
                }
 
1794
                if (isVetoed)
 
1795
                        return;
 
1796
 
 
1797
                for (BaseTabCloseListener listener : SubstanceLookAndFeel
 
1798
                                .getAllTabCloseListeners(this.tabPane)) {
 
1799
                        if (listener instanceof MultipleTabCloseListener)
 
1800
                                ((MultipleTabCloseListener) listener).tabsClosing(this.tabPane,
 
1801
                                                componentSet);
 
1802
                }
 
1803
 
 
1804
                for (Component toRemove : componentSet) {
 
1805
                        this.tabPane.remove(toRemove);
 
1806
                }
 
1807
 
 
1808
                if (this.tabPane.getTabCount() > 0) {
 
1809
                        this.selectPreviousTab(0);
 
1810
                        this.selectNextTab(this.tabPane.getSelectedIndex());
 
1811
                }
 
1812
                this.tabPane.repaint();
 
1813
 
 
1814
                for (BaseTabCloseListener listener : SubstanceLookAndFeel
 
1815
                                .getAllTabCloseListeners(this.tabPane)) {
 
1816
                        if (listener instanceof MultipleTabCloseListener)
 
1817
                                ((MultipleTabCloseListener) listener).tabsClosed(this.tabPane,
 
1818
                                                componentSet);
 
1819
                }
 
1820
        }
 
1821
 
 
1822
        /*
 
1823
         * (non-Javadoc)
 
1824
         * 
 
1825
         * @see javax.swing.plaf.basic.BasicTabbedPaneUI#getTabLabelShiftX(int, int,
 
1826
         * boolean)
 
1827
         */
 
1828
        @Override
 
1829
        protected int getTabLabelShiftX(int tabPlacement, int tabIndex,
 
1830
                        boolean isSelected) {
 
1831
                int delta = 0;
 
1832
                if (SubstanceCoreUtilities.hasCloseButton(this.tabPane, tabIndex)) {
 
1833
                        if (this.tabPane.getComponentOrientation().isLeftToRight()) {
 
1834
                                delta = 5 - SubstanceCoreUtilities.getCloseButtonSize(
 
1835
                                                this.tabPane, tabIndex);
 
1836
                        } else {
 
1837
                                delta = SubstanceCoreUtilities.getCloseButtonSize(this.tabPane,
 
1838
                                                tabIndex) - 5;
 
1839
                        }
 
1840
                }
 
1841
                return delta
 
1842
                                + super.getTabLabelShiftX(tabPlacement, tabIndex, isSelected);
 
1843
        }
 
1844
 
 
1845
        /*
 
1846
         * (non-Javadoc)
 
1847
         * 
 
1848
         * @see javax.swing.plaf.basic.BasicTabbedPaneUI#getTabLabelShiftY(int, int,
 
1849
         * boolean)
 
1850
         */
 
1851
        @Override
 
1852
        protected int getTabLabelShiftY(int tabPlacement, int tabIndex,
 
1853
                        boolean isSelected) {
 
1854
                int result = 0;
 
1855
                if (tabPlacement == SwingConstants.BOTTOM)
 
1856
                        result = -1;
 
1857
                else
 
1858
                        result = 1;
 
1859
                return result;
 
1860
        }
 
1861
 
 
1862
        /**
 
1863
         * Returns extra width for the specified tab.
 
1864
         * 
 
1865
         * @param tabPlacement
 
1866
         *            Tab placement.
 
1867
         * @param tabIndex
 
1868
         *            Tab index.
 
1869
         * @return Extra width for the specified tab.
 
1870
         */
 
1871
        protected int getTabExtraWidth(int tabPlacement, int tabIndex) {
 
1872
                int extraWidth = 0;
 
1873
                SubstanceButtonShaper shaper = SubstanceCoreUtilities
 
1874
                                .getButtonShaper(this.tabPane);
 
1875
                if (shaper instanceof ClassicButtonShaper)
 
1876
                        extraWidth = (int) (2.0 * SubstanceSizeUtils
 
1877
                                        .getClassicButtonCornerRadius(SubstanceSizeUtils
 
1878
                                                        .getComponentFontSize(this.tabPane)));
 
1879
                else
 
1880
                        extraWidth = super.calculateTabHeight(tabPlacement, tabIndex, this
 
1881
                                        .getFontMetrics().getHeight()) / 3;
 
1882
 
 
1883
                if (SubstanceCoreUtilities.hasCloseButton(this.tabPane, tabIndex)
 
1884
                                && this.tabPane.isEnabledAt(tabIndex)) {
 
1885
                        extraWidth += (4 + SubstanceCoreUtilities.getCloseButtonSize(
 
1886
                                        this.tabPane, tabIndex));
 
1887
                }
 
1888
 
 
1889
                // System.out.println(tabPane.getTitleAt(tabIndex) + ":" + extraWidth);
 
1890
                return extraWidth;
 
1891
        }
 
1892
 
 
1893
        /**
 
1894
         * Returns the index of the tab currently being rolled-over.
 
1895
         * 
 
1896
         * @return Index of the tab currently being rolled-over.
 
1897
         */
 
1898
        public int getRolloverTabIndex() {
 
1899
                return this.getRolloverTab();
 
1900
        }
 
1901
 
 
1902
        /**
 
1903
         * Sets new value for tab area insets.
 
1904
         * 
 
1905
         * @param insets
 
1906
         *            Tab area insets.
 
1907
         */
 
1908
        public void setTabAreaInsets(Insets insets) {
 
1909
                Insets old = this.tabAreaInsets;
 
1910
                this.tabAreaInsets = insets;
 
1911
                // Fire a property change event so that the tabbed
 
1912
                // pane can revalidate itself
 
1913
                LafWidgetUtilities.firePropertyChangeEvent(this.tabPane,
 
1914
                                "tabAreaInsets", old, tabAreaInsets);
 
1915
        }
 
1916
 
 
1917
        /**
 
1918
         * Returns tab area insets.
 
1919
         * 
 
1920
         * @return Tab area insets.
 
1921
         */
 
1922
        public Insets getTabAreaInsets() {
 
1923
                return this.tabAreaInsets;
 
1924
        }
 
1925
 
 
1926
        /**
 
1927
         * Returns the tab rectangle for the specified tab.
 
1928
         * 
 
1929
         * @param tabIndex
 
1930
         *            Index of a tab.
 
1931
         * @return The tab rectangle for the specified parameters.
 
1932
         */
 
1933
        public Rectangle getTabRectangle(int tabIndex) {
 
1934
                return this.rects[tabIndex];
 
1935
        }
 
1936
 
 
1937
        /**
 
1938
         * Returns the memory usage string.
 
1939
         * 
 
1940
         * @return The memory usage string.
 
1941
         */
 
1942
        public static String getMemoryUsage() {
 
1943
                StringBuffer sb = new StringBuffer();
 
1944
                sb.append("SubstanceTabbedPaneUI: \n");
 
1945
                sb.append("\t" + SubstanceTabbedPaneUI.backgroundMap.size()
 
1946
                                + " backgrounds");
 
1947
                return sb.toString();
 
1948
        }
 
1949
 
 
1950
        /*
 
1951
         * (non-Javadoc)
 
1952
         * 
 
1953
         * @see javax.swing.plaf.basic.BasicTabbedPaneUI#shouldPadTabRun(int, int)
 
1954
         */
 
1955
        @Override
 
1956
        protected boolean shouldPadTabRun(int tabPlacement, int run) {
 
1957
                // Don't pad last run
 
1958
                return this.runCount > 1 && run < this.runCount - 1;
 
1959
        }
 
1960
 
 
1961
        /*
 
1962
         * (non-Javadoc)
 
1963
         * 
 
1964
         * @see javax.swing.plaf.basic.BasicTabbedPaneUI#createLayoutManager()
 
1965
         */
 
1966
        @Override
 
1967
        protected LayoutManager createLayoutManager() {
 
1968
                if (this.tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) {
 
1969
                        return super.createLayoutManager();
 
1970
                }
 
1971
                return new TabbedPaneLayout();
 
1972
        }
 
1973
 
 
1974
        /**
 
1975
         * Layout for the tabbed pane.
 
1976
         * 
 
1977
         * @author Kirill Grouchnikov
 
1978
         */
 
1979
        public class TabbedPaneLayout extends BasicTabbedPaneUI.TabbedPaneLayout {
 
1980
                /**
 
1981
                 * Creates a new layout.
 
1982
                 */
 
1983
                public TabbedPaneLayout() {
 
1984
                        SubstanceTabbedPaneUI.this.super();
 
1985
                }
 
1986
 
 
1987
                /*
 
1988
                 * (non-Javadoc)
 
1989
                 * 
 
1990
                 * @seejavax.swing.plaf.basic.BasicTabbedPaneUI$TabbedPaneLayout#
 
1991
                 * normalizeTabRuns(int, int, int, int)
 
1992
                 */
 
1993
                @Override
 
1994
                protected void normalizeTabRuns(int tabPlacement, int tabCount,
 
1995
                                int start, int max) {
 
1996
                        // Only normalize the runs for top & bottom; normalizing
 
1997
                        // doesn't look right for Metal's vertical tabs
 
1998
                        // because the last run isn't padded and it looks odd to have
 
1999
                        // fat tabs in the first vertical runs, but slimmer ones in the
 
2000
                        // last (this effect isn't noticeable for horizontal tabs).
 
2001
                        if (tabPlacement == TOP || tabPlacement == BOTTOM) {
 
2002
                                super.normalizeTabRuns(tabPlacement, tabCount, start, max);
 
2003
                        }
 
2004
                }
 
2005
 
 
2006
                /*
 
2007
                 * (non-Javadoc)
 
2008
                 * 
 
2009
                 * @see
 
2010
                 * javax.swing.plaf.basic.BasicTabbedPaneUI$TabbedPaneLayout#rotateTabRuns
 
2011
                 * (int, int)
 
2012
                 */
 
2013
                @Override
 
2014
                protected void rotateTabRuns(int tabPlacement, int selectedRun) {
 
2015
                        // Don't rotate runs!
 
2016
                }
 
2017
 
 
2018
                /*
 
2019
                 * (non-Javadoc)
 
2020
                 * 
 
2021
                 * @see
 
2022
                 * javax.swing.plaf.basic.BasicTabbedPaneUI$TabbedPaneLayout#padSelectedTab
 
2023
                 * (int, int)
 
2024
                 */
 
2025
                @Override
 
2026
                protected void padSelectedTab(int tabPlacement, int selectedIndex) {
 
2027
                        // Don't pad selected tab
 
2028
                }
 
2029
        }
 
2030
 
 
2031
        /*
 
2032
         * (non-Javadoc)
 
2033
         * 
 
2034
         * @see javax.swing.plaf.basic.BasicTabbedPaneUI#getContentBorderInsets(int)
 
2035
         */
 
2036
        @Override
 
2037
        protected Insets getContentBorderInsets(int tabPlacement) {
 
2038
                Insets insets = SubstanceSizeUtils
 
2039
                                .getTabbedPaneContentInsets(SubstanceSizeUtils
 
2040
                                                .getComponentFontSize(this.tabPane));
 
2041
 
 
2042
                TabContentPaneBorderKind kind = SubstanceCoreUtilities
 
2043
                                .getContentBorderKind(this.tabPane);
 
2044
                boolean isDouble = (kind == TabContentPaneBorderKind.DOUBLE_FULL)
 
2045
                                || (kind == TabContentPaneBorderKind.DOUBLE_PLACEMENT);
 
2046
                boolean isPlacement = (kind == TabContentPaneBorderKind.SINGLE_PLACEMENT)
 
2047
                                || (kind == TabContentPaneBorderKind.DOUBLE_PLACEMENT);
 
2048
                int delta = isDouble ? (int) (3.0 * SubstanceSizeUtils
 
2049
                                .getBorderStrokeWidth(SubstanceSizeUtils
 
2050
                                                .getComponentFontSize(tabPane))) : 0;
 
2051
 
 
2052
                if (isPlacement) {
 
2053
                        switch (tabPlacement) {
 
2054
                        case TOP:
 
2055
                                return new Insets(insets.top + delta, 0, 0, 0);
 
2056
                        case LEFT:
 
2057
                                return new Insets(0, insets.left + delta, 0, 0);
 
2058
                        case RIGHT:
 
2059
                                return new Insets(0, 0, 0, insets.right + delta);
 
2060
                        case BOTTOM:
 
2061
                                return new Insets(0, 0, insets.bottom + delta, 0);
 
2062
                        }
 
2063
                } else {
 
2064
                        switch (tabPlacement) {
 
2065
                        case TOP:
 
2066
                                return new Insets(insets.top + delta, insets.left,
 
2067
                                                insets.bottom, insets.right);
 
2068
                        case LEFT:
 
2069
                                return new Insets(insets.top, insets.left + delta,
 
2070
                                                insets.bottom, insets.right);
 
2071
                        case RIGHT:
 
2072
                                return new Insets(insets.top, insets.left, insets.bottom,
 
2073
                                                insets.right + delta);
 
2074
                        case BOTTOM:
 
2075
                                return new Insets(insets.top, insets.left, insets.bottom
 
2076
                                                + delta, insets.right);
 
2077
                        }
 
2078
                }
 
2079
                return insets;
 
2080
        }
 
2081
 
 
2082
        /*
 
2083
         * (non-Javadoc)
 
2084
         * 
 
2085
         * @see
 
2086
         * javax.swing.plaf.basic.BasicTabbedPaneUI#paintContentBorder(java.awt.
 
2087
         * Graphics, int, int)
 
2088
         */
 
2089
        @Override
 
2090
        protected void paintContentBorder(Graphics g, int tabPlacement,
 
2091
                        int selectedIndex) {
 
2092
                SubstanceColorScheme scheme = SubstanceColorSchemeUtilities
 
2093
                                .getColorScheme(this.tabPane, selectedIndex,
 
2094
                                                ColorSchemeAssociationKind.TAB, ComponentState.ENABLED);
 
2095
                this.highlight = scheme.isDark() ? SubstanceColorUtilities
 
2096
                                .getAlphaColor(scheme.getUltraDarkColor(), 100) : scheme
 
2097
                                .getLightColor();
 
2098
                super.paintContentBorder(g, tabPlacement, selectedIndex);
 
2099
        }
 
2100
 
 
2101
        /*
 
2102
         * (non-Javadoc)
 
2103
         * 
 
2104
         * @see
 
2105
         * javax.swing.plaf.basic.BasicTabbedPaneUI#paintContentBorderBottomEdge
 
2106
         * (java.awt.Graphics, int, int, int, int, int, int)
 
2107
         */
 
2108
        @Override
 
2109
        protected void paintContentBorderBottomEdge(Graphics g, int tabPlacement,
 
2110
                        int selectedIndex, int x, int y, int w, int h) {
 
2111
                TabContentPaneBorderKind kind = SubstanceCoreUtilities
 
2112
                                .getContentBorderKind(this.tabPane);
 
2113
                boolean isDouble = (kind == TabContentPaneBorderKind.DOUBLE_FULL)
 
2114
                                || (kind == TabContentPaneBorderKind.DOUBLE_PLACEMENT);
 
2115
                boolean isPlacement = (kind == TabContentPaneBorderKind.SINGLE_PLACEMENT)
 
2116
                                || (kind == TabContentPaneBorderKind.DOUBLE_PLACEMENT);
 
2117
                if (isPlacement) {
 
2118
                        if (tabPlacement != SwingConstants.BOTTOM)
 
2119
                                return;
 
2120
                }
 
2121
                int ribbonDelta = (int) (2.0 * SubstanceSizeUtils
 
2122
                                .getBorderStrokeWidth(SubstanceSizeUtils
 
2123
                                                .getComponentFontSize(tabPane)));
 
2124
 
 
2125
                Rectangle selRect = selectedIndex < 0 ? null : this.getTabBounds(
 
2126
                                selectedIndex, this.calcRect);
 
2127
 
 
2128
                Graphics2D g2d = (Graphics2D) g.create();
 
2129
                g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
 
2130
                                RenderingHints.VALUE_ANTIALIAS_ON);
 
2131
                g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
 
2132
                                RenderingHints.VALUE_STROKE_NORMALIZE);
 
2133
                float strokeWidth = SubstanceSizeUtils
 
2134
                                .getBorderStrokeWidth(SubstanceSizeUtils
 
2135
                                                .getComponentFontSize(tabPane));
 
2136
                int joinKind = BasicStroke.JOIN_ROUND;
 
2137
                int capKind = BasicStroke.CAP_BUTT;
 
2138
                g2d.setStroke(new BasicStroke(strokeWidth, capKind, joinKind));
 
2139
                int offset = (int) (strokeWidth / 2.0);
 
2140
 
 
2141
                boolean isUnbroken = (tabPlacement != BOTTOM || selectedIndex < 0
 
2142
                                || (selRect.y - 1 > h) || (selRect.x < x || selRect.x > x + w));
 
2143
 
 
2144
                x += offset;
 
2145
                y += offset;
 
2146
                w -= 2 * offset;
 
2147
                h -= 2 * offset;
 
2148
 
 
2149
                // Draw unbroken line if tabs are not on BOTTOM, OR
 
2150
                // selected tab is not in run adjacent to content, OR
 
2151
                // selected tab is not visible (SCROLL_TAB_LAYOUT)
 
2152
                SubstanceColorScheme borderScheme = SubstanceColorSchemeUtilities
 
2153
                                .getColorScheme(this.tabPane, selectedIndex,
 
2154
                                                ColorSchemeAssociationKind.TAB_BORDER,
 
2155
                                                ComponentState.SELECTED);
 
2156
                Color darkShadowColor = SubstanceColorUtilities
 
2157
                                .getMidBorderColor(borderScheme);
 
2158
                if (isUnbroken) {
 
2159
                        g2d.setColor(this.highlight);
 
2160
                        g2d.drawLine(x, y + h - 1, x + w - 1, y + h - 1);
 
2161
                } else {
 
2162
                        // Break line to show visual connection to selected tab
 
2163
                        SubstanceButtonShaper shaper = SubstanceCoreUtilities
 
2164
                                        .getButtonShaper(this.tabPane);
 
2165
                        int delta = (shaper instanceof ClassicButtonShaper) ? 1 : 0;
 
2166
                        int borderInsets = (int) Math.floor(SubstanceSizeUtils
 
2167
                                        .getBorderStrokeWidth(SubstanceSizeUtils
 
2168
                                                        .getComponentFontSize(tabPane)) / 2.0);
 
2169
                        GeneralPath bottomOutline = new GeneralPath();
 
2170
                        bottomOutline.moveTo(x, y + h - 1);
 
2171
                        bottomOutline.lineTo(selRect.x + borderInsets, y + h - 1);
 
2172
                        int bumpHeight = super.calculateTabHeight(tabPlacement, 0,
 
2173
                                        SubstanceSizeUtils.getComponentFontSize(this.tabPane)) / 2;
 
2174
                        bottomOutline.lineTo(selRect.x + borderInsets, y + h + bumpHeight);
 
2175
                        if (selRect.x + selRect.width < x + w - 1) {
 
2176
                                int selectionEndX = selRect.x + selRect.width - delta - 1
 
2177
                                                - borderInsets;
 
2178
                                bottomOutline.lineTo(selectionEndX, y + h - 1 + bumpHeight);
 
2179
                                bottomOutline.lineTo(selectionEndX, y + h - 1);
 
2180
                                bottomOutline.lineTo(x + w - 1, y + h - 1);
 
2181
                        }
 
2182
                        g2d.setPaint(new GradientPaint(x, y + h - 1, darkShadowColor, x, y
 
2183
                                        + h - 1 + bumpHeight, SubstanceColorUtilities
 
2184
                                        .getAlphaColor(darkShadowColor, 0)));
 
2185
                        g2d.draw(bottomOutline);
 
2186
                }
 
2187
 
 
2188
                if (isDouble) {
 
2189
                        if (tabPlacement == BOTTOM) {
 
2190
                                g2d.setColor(this.highlight);
 
2191
                                // g2d.drawLine(x+1, y + h - 2 - ribbonDelta, x + w - 2,
 
2192
                                // y + h - 2 - ribbonDelta);
 
2193
                                g2d.setColor(darkShadowColor);
 
2194
                                g2d.drawLine(x, y + h - 1 - ribbonDelta, x + w - 1, y + h - 1
 
2195
                                                - ribbonDelta);
 
2196
                        }
 
2197
                        if (tabPlacement == LEFT) {
 
2198
                                g2d.setPaint(new GradientPaint(x, y + h - 1, darkShadowColor, x
 
2199
                                                + 4 * ribbonDelta, y + h - 1, this.highlight));
 
2200
                                g2d.drawLine(x, y + h - 1, x + 4 * ribbonDelta, y + h - 1);
 
2201
                        }
 
2202
                        if (tabPlacement == RIGHT) {
 
2203
                                g2d.setPaint(new GradientPaint(x + w - 1 - 4 * ribbonDelta, y
 
2204
                                                + h - 1, this.highlight, x + w - 1, y + h - 1,
 
2205
                                                darkShadowColor));
 
2206
                                g2d.drawLine(x + w - 1 - 4 * ribbonDelta, y + h - 1, x + w - 1,
 
2207
                                                y + h - 1);
 
2208
                        }
 
2209
                }
 
2210
 
 
2211
                g2d.dispose();
 
2212
        }
 
2213
 
 
2214
        /*
 
2215
         * (non-Javadoc)
 
2216
         * 
 
2217
         * @see
 
2218
         * javax.swing.plaf.basic.BasicTabbedPaneUI#paintContentBorderLeftEdge(java
 
2219
         * .awt.Graphics, int, int, int, int, int, int)
 
2220
         */
 
2221
        @Override
 
2222
        protected void paintContentBorderLeftEdge(Graphics g, int tabPlacement,
 
2223
                        int selectedIndex, int x, int y, int w, int h) {
 
2224
                TabContentPaneBorderKind kind = SubstanceCoreUtilities
 
2225
                                .getContentBorderKind(this.tabPane);
 
2226
                boolean isDouble = (kind == TabContentPaneBorderKind.DOUBLE_FULL)
 
2227
                                || (kind == TabContentPaneBorderKind.DOUBLE_PLACEMENT);
 
2228
                boolean isPlacement = (kind == TabContentPaneBorderKind.SINGLE_PLACEMENT)
 
2229
                                || (kind == TabContentPaneBorderKind.DOUBLE_PLACEMENT);
 
2230
                if (isPlacement) {
 
2231
                        if (tabPlacement != SwingConstants.LEFT)
 
2232
                                return;
 
2233
                }
 
2234
                int ribbonDelta = (int) (3.0 * SubstanceSizeUtils
 
2235
                                .getBorderStrokeWidth(SubstanceSizeUtils
 
2236
                                                .getComponentFontSize(tabPane)));
 
2237
 
 
2238
                Rectangle selRect = selectedIndex < 0 ? null : this.getTabBounds(
 
2239
                                selectedIndex, this.calcRect);
 
2240
 
 
2241
                Graphics2D g2d = (Graphics2D) g.create();
 
2242
                g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
 
2243
                                RenderingHints.VALUE_ANTIALIAS_ON);
 
2244
                g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
 
2245
                                RenderingHints.VALUE_STROKE_NORMALIZE);
 
2246
                float strokeWidth = SubstanceSizeUtils
 
2247
                                .getBorderStrokeWidth(SubstanceSizeUtils
 
2248
                                                .getComponentFontSize(tabPane));
 
2249
                int joinKind = BasicStroke.JOIN_ROUND;
 
2250
                int capKind = BasicStroke.CAP_BUTT;
 
2251
                g2d.setStroke(new BasicStroke(strokeWidth, capKind, joinKind));
 
2252
                int offset = (int) (strokeWidth / 2.0);
 
2253
 
 
2254
                boolean isUnbroken = (tabPlacement != LEFT || selectedIndex < 0
 
2255
                                || (selRect.x + selRect.width + 1 < x) || (selRect.y < y || selRect.y > y
 
2256
                                + h));
 
2257
 
 
2258
                x += offset;
 
2259
                y += offset;
 
2260
                // w -= 2 * offset;
 
2261
                h -= 2 * offset;
 
2262
 
 
2263
                // Draw unbroken line if tabs are not on LEFT, OR
 
2264
                // selected tab is not in run adjacent to content, OR
 
2265
                // selected tab is not visible (SCROLL_TAB_LAYOUT)
 
2266
                SubstanceColorScheme borderScheme = SubstanceColorSchemeUtilities
 
2267
                                .getColorScheme(this.tabPane, selectedIndex,
 
2268
                                                ColorSchemeAssociationKind.TAB_BORDER,
 
2269
                                                ComponentState.SELECTED);
 
2270
                Color darkShadowColor = SubstanceColorUtilities
 
2271
                                .getMidBorderColor(borderScheme);
 
2272
                if (isUnbroken) {
 
2273
                        g2d.setColor(this.highlight);
 
2274
                        g2d.drawLine(x, y, x, y + h);
 
2275
                } else {
 
2276
                        // Break line to show visual connection to selected tab
 
2277
                        SubstanceButtonShaper shaper = SubstanceCoreUtilities
 
2278
                                        .getButtonShaper(this.tabPane);
 
2279
                        int delta = (shaper instanceof ClassicButtonShaper) ? 1 : 0;
 
2280
 
 
2281
                        int borderInsets = (int) Math.floor(SubstanceSizeUtils
 
2282
                                        .getBorderStrokeWidth(SubstanceSizeUtils
 
2283
                                                        .getComponentFontSize(tabPane)) / 2.0);
 
2284
                        GeneralPath leftOutline = new GeneralPath();
 
2285
                        leftOutline.moveTo(x, y);
 
2286
                        leftOutline.lineTo(x, selRect.y + borderInsets);
 
2287
                        int bumpWidth = super.calculateTabHeight(tabPlacement, 0,
 
2288
                                        SubstanceSizeUtils.getComponentFontSize(this.tabPane)) / 2;
 
2289
                        leftOutline.lineTo(x - bumpWidth, selRect.y + borderInsets);
 
2290
                        if (selRect.y + selRect.height < y + h) {
 
2291
                                int selectionEndY = selRect.y + selRect.height - delta - 1
 
2292
                                                - borderInsets;
 
2293
                                leftOutline.lineTo(x - bumpWidth, selectionEndY);
 
2294
                                leftOutline.lineTo(x, selectionEndY);
 
2295
                                leftOutline.lineTo(x, y + h);
 
2296
                        }
 
2297
                        g2d.setPaint(new GradientPaint(x, y, darkShadowColor,
 
2298
                                        x - bumpWidth, y, SubstanceColorUtilities.getAlphaColor(
 
2299
                                                        darkShadowColor, 0)));
 
2300
                        g2d.draw(leftOutline);
 
2301
 
 
2302
                }
 
2303
 
 
2304
                if (isDouble) {
 
2305
                        if (tabPlacement == LEFT) {
 
2306
                                g2d.setColor(darkShadowColor);
 
2307
                                g2d.drawLine(x + ribbonDelta, y, x + ribbonDelta, y + h);
 
2308
                                // g2d.setColor(this.highlight);
 
2309
                                // g2d.drawLine(x + 1 + ribbonDelta, y + 1, x + 1 + ribbonDelta,
 
2310
                                // y +
 
2311
                                // h - 1);
 
2312
                        }
 
2313
                        if (tabPlacement == TOP) {
 
2314
                                g2d.setPaint(new GradientPaint(x, y, darkShadowColor, x, y + 4
 
2315
                                                * ribbonDelta, this.highlight));
 
2316
                                g2d.drawLine(x, y, x, y + 4 * ribbonDelta);
 
2317
                        }
 
2318
                        if (tabPlacement == BOTTOM) {
 
2319
                                g2d.setPaint(new GradientPaint(x, y + h - 1 - 4 * ribbonDelta,
 
2320
                                                this.highlight, x, y + h - 1, darkShadowColor));
 
2321
                                g2d.drawLine(x, y + h - 1 - 4 * ribbonDelta, x, y + h - 1);
 
2322
                        }
 
2323
                }
 
2324
                g2d.dispose();
 
2325
        }
 
2326
 
 
2327
        /*
 
2328
         * (non-Javadoc)
 
2329
         * 
 
2330
         * @see
 
2331
         * javax.swing.plaf.basic.BasicTabbedPaneUI#paintContentBorderRightEdge(
 
2332
         * java.awt.Graphics, int, int, int, int, int, int)
 
2333
         */
 
2334
        @Override
 
2335
        protected void paintContentBorderRightEdge(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.RIGHT)
 
2345
                                return;
 
2346
                }
 
2347
                int ribbonDelta = (int) (3.0 * SubstanceSizeUtils
 
2348
                                .getBorderStrokeWidth(SubstanceSizeUtils
 
2349
                                                .getComponentFontSize(tabPane)));
 
2350
 
 
2351
                Rectangle selRect = selectedIndex < 0 ? null : this.getTabBounds(
 
2352
                                selectedIndex, this.calcRect);
 
2353
 
 
2354
                Graphics2D g2d = (Graphics2D) g.create();
 
2355
                g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
 
2356
                                RenderingHints.VALUE_ANTIALIAS_ON);
 
2357
                g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
 
2358
                                RenderingHints.VALUE_STROKE_NORMALIZE);
 
2359
                float strokeWidth = SubstanceSizeUtils
 
2360
                                .getBorderStrokeWidth(SubstanceSizeUtils
 
2361
                                                .getComponentFontSize(tabPane));
 
2362
                int joinKind = BasicStroke.JOIN_ROUND;
 
2363
                int capKind = BasicStroke.CAP_BUTT;
 
2364
                g2d.setStroke(new BasicStroke(strokeWidth, capKind, joinKind));
 
2365
                int offset = (int) (strokeWidth / 2.0);
 
2366
 
 
2367
                boolean isUnbroken = (tabPlacement != RIGHT || selectedIndex < 0
 
2368
                                || (selRect.x - 1 > w) || (selRect.y < y || selRect.y > y + h));
 
2369
 
 
2370
                x += offset;
 
2371
                y += offset;
 
2372
                w -= 2 * offset;
 
2373
                h -= 2 * offset;
 
2374
 
 
2375
                // Draw unbroken line if tabs are not on RIGHT, OR
 
2376
                // selected tab is not in run adjacent to content, OR
 
2377
                // selected tab is not visible (SCROLL_TAB_LAYOUT)
 
2378
                SubstanceColorScheme borderScheme = SubstanceColorSchemeUtilities
 
2379
                                .getColorScheme(this.tabPane, selectedIndex,
 
2380
                                                ColorSchemeAssociationKind.TAB_BORDER,
 
2381
                                                ComponentState.SELECTED);
 
2382
                Color darkShadowColor = SubstanceColorUtilities
 
2383
                                .getMidBorderColor(borderScheme);
 
2384
                if (isUnbroken) {
 
2385
                        g2d.setColor(this.highlight);
 
2386
                        g2d.drawLine(x + w - 1, y, x + w - 1, y + h);
 
2387
                } else {
 
2388
                        // Break line to show visual connection to selected tab
 
2389
                        SubstanceButtonShaper shaper = SubstanceCoreUtilities
 
2390
                                        .getButtonShaper(this.tabPane);
 
2391
                        int delta = (shaper instanceof ClassicButtonShaper) ? 1 : 0;
 
2392
 
 
2393
                        int borderInsets = (int) Math.floor(SubstanceSizeUtils
 
2394
                                        .getBorderStrokeWidth(SubstanceSizeUtils
 
2395
                                                        .getComponentFontSize(tabPane)) / 2.0);
 
2396
                        GeneralPath rightOutline = new GeneralPath();
 
2397
                        rightOutline.moveTo(x + w - 1, y);
 
2398
                        rightOutline.lineTo(x + w - 1, selRect.y + borderInsets);
 
2399
                        int bumpWidth = super.calculateTabHeight(tabPlacement, 0,
 
2400
                                        SubstanceSizeUtils.getComponentFontSize(this.tabPane)) / 2;
 
2401
                        rightOutline
 
2402
                                        .lineTo(x + w - 1 + bumpWidth, selRect.y + borderInsets);
 
2403
                        if (selRect.y + selRect.height < y + h) {
 
2404
                                int selectionEndY = selRect.y + selRect.height - delta - 1
 
2405
                                                - borderInsets;
 
2406
                                rightOutline.lineTo(x + w - 1 + bumpWidth, selectionEndY);
 
2407
                                rightOutline.lineTo(x + w - 1, selectionEndY);
 
2408
                                rightOutline.lineTo(x + w - 1, y + h);
 
2409
                        }
 
2410
                        g2d.setPaint(new GradientPaint(x + w - 1, y, darkShadowColor, x + w
 
2411
                                        - 1 + bumpWidth, y, SubstanceColorUtilities.getAlphaColor(
 
2412
                                        darkShadowColor, 0)));
 
2413
                        g2d.draw(rightOutline);
 
2414
                }
 
2415
 
 
2416
                if (isDouble) {
 
2417
                        if (tabPlacement == RIGHT) {
 
2418
                                g2d.setColor(this.highlight);
 
2419
                                // g2d.drawLine(x + w - 2 - ribbonDelta, y + 1, x + w - 2 -
 
2420
                                // ribbonDelta, y + h - 1);
 
2421
                                g2d.setColor(darkShadowColor);
 
2422
                                g2d.drawLine(x + w - 1 - ribbonDelta, y, x + w - 1
 
2423
                                                - ribbonDelta, y + h);
 
2424
                        }
 
2425
                        if (tabPlacement == TOP) {
 
2426
                                g2d.setPaint(new GradientPaint(x + w - 1, y, darkShadowColor, x
 
2427
                                                + w - 1, y + 4 * ribbonDelta, this.highlight));
 
2428
                                g2d.drawLine(x + w - 1, y, x + w - 1, y + 4 * ribbonDelta);
 
2429
                        }
 
2430
                        if (tabPlacement == BOTTOM) {
 
2431
                                g2d.setPaint(new GradientPaint(x + w - 1, y + h - 1 - 4
 
2432
                                                * ribbonDelta, this.highlight, x + w - 1, y + h - 1,
 
2433
                                                darkShadowColor));
 
2434
                                g2d.drawLine(x + w - 1, y + h - 1 - 4 * ribbonDelta, x + w - 1,
 
2435
                                                y + h - 1);
 
2436
                        }
 
2437
                }
 
2438
                g2d.dispose();
 
2439
        }
 
2440
 
 
2441
        /*
 
2442
         * (non-Javadoc)
 
2443
         * 
 
2444
         * @see
 
2445
         * javax.swing.plaf.basic.BasicTabbedPaneUI#paintContentBorderTopEdge(java
 
2446
         * .awt.Graphics, int, int, int, int, int, int)
 
2447
         */
 
2448
        @Override
 
2449
        protected void paintContentBorderTopEdge(Graphics g, int tabPlacement,
 
2450
                        int selectedIndex, int x, int y, int w, int h) {
 
2451
                TabContentPaneBorderKind kind = SubstanceCoreUtilities
 
2452
                                .getContentBorderKind(this.tabPane);
 
2453
                boolean isDouble = (kind == TabContentPaneBorderKind.DOUBLE_FULL)
 
2454
                                || (kind == TabContentPaneBorderKind.DOUBLE_PLACEMENT);
 
2455
                boolean isPlacement = (kind == TabContentPaneBorderKind.SINGLE_PLACEMENT)
 
2456
                                || (kind == TabContentPaneBorderKind.DOUBLE_PLACEMENT);
 
2457
                if (isPlacement) {
 
2458
                        if (tabPlacement != SwingConstants.TOP)
 
2459
                                return;
 
2460
                }
 
2461
                int ribbonDelta = (int) (3.0 * SubstanceSizeUtils
 
2462
                                .getBorderStrokeWidth(SubstanceSizeUtils
 
2463
                                                .getComponentFontSize(tabPane)));
 
2464
 
 
2465
                Rectangle selRect = selectedIndex < 0 ? null : this.getTabBounds(
 
2466
                                selectedIndex, this.calcRect);
 
2467
 
 
2468
                Graphics2D g2d = (Graphics2D) g.create();
 
2469
                g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
 
2470
                                RenderingHints.VALUE_ANTIALIAS_ON);
 
2471
                g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
 
2472
                                RenderingHints.VALUE_STROKE_NORMALIZE);
 
2473
                float strokeWidth = SubstanceSizeUtils
 
2474
                                .getBorderStrokeWidth(SubstanceSizeUtils
 
2475
                                                .getComponentFontSize(tabPane));
 
2476
                int joinKind = BasicStroke.JOIN_ROUND;
 
2477
                int capKind = BasicStroke.CAP_BUTT;
 
2478
                g2d.setStroke(new BasicStroke(strokeWidth, capKind, joinKind));
 
2479
                int offset = (int) (strokeWidth / 2.0);
 
2480
 
 
2481
                boolean isUnbroken = (tabPlacement != TOP || selectedIndex < 0
 
2482
                                || (selRect.y + selRect.height + 1 < y) || (selRect.x < x || selRect.x > x
 
2483
                                + w));
 
2484
 
 
2485
                x += offset;
 
2486
                y += offset;
 
2487
                w -= 2 * offset;
 
2488
                // h -= 2 * offset;
 
2489
 
 
2490
                // Draw unbroken line if tabs are not on TOP, OR
 
2491
                // selected tab is not in run adjacent to content, OR
 
2492
                // selected tab is not visible (SCROLL_TAB_LAYOUT)
 
2493
                SubstanceColorScheme borderScheme = SubstanceColorSchemeUtilities
 
2494
                                .getColorScheme(this.tabPane, selectedIndex,
 
2495
                                                ColorSchemeAssociationKind.TAB_BORDER,
 
2496
                                                ComponentState.SELECTED);
 
2497
                Color darkShadowColor = SubstanceColorUtilities
 
2498
                                .getMidBorderColor(borderScheme);
 
2499
                if (isUnbroken) {
 
2500
                        g2d.setColor(this.highlight);
 
2501
                        g2d.drawLine(x, y, x + w - 1, y);
 
2502
                } else {
 
2503
                        // Break line to show visual connection to selected tab
 
2504
                        SubstanceButtonShaper shaper = SubstanceCoreUtilities
 
2505
                                        .getButtonShaper(this.tabPane);
 
2506
                        int delta = (shaper instanceof ClassicButtonShaper) ? 1 : 0;
 
2507
                        int borderInsets = (int) Math.floor(SubstanceSizeUtils
 
2508
                                        .getBorderStrokeWidth(SubstanceSizeUtils
 
2509
                                                        .getComponentFontSize(tabPane)) / 2.0);
 
2510
                        GeneralPath topOutline = new GeneralPath();
 
2511
                        topOutline.moveTo(x, y);
 
2512
                        topOutline.lineTo(selRect.x + borderInsets, y);
 
2513
                        int bumpHeight = super.calculateTabHeight(tabPlacement, 0,
 
2514
                                        SubstanceSizeUtils.getComponentFontSize(this.tabPane)) / 2;
 
2515
                        topOutline.lineTo(selRect.x + borderInsets, y - bumpHeight);
 
2516
                        if (selRect.x + selRect.width < x + w - 1) {
 
2517
                                int selectionEndX = selRect.x + selRect.width - delta - 1
 
2518
                                                - borderInsets;
 
2519
                                topOutline.lineTo(selectionEndX, y - bumpHeight);
 
2520
                                topOutline.lineTo(selectionEndX, y);
 
2521
                                topOutline.lineTo(x + w - 1, y);
 
2522
                        }
 
2523
                        g2d.setPaint(new GradientPaint(x, y, darkShadowColor, x, y
 
2524
                                        - bumpHeight, SubstanceColorUtilities.getAlphaColor(
 
2525
                                        darkShadowColor, 0)));
 
2526
                        g2d.draw(topOutline);
 
2527
                }
 
2528
 
 
2529
                if (isDouble) {
 
2530
                        if (tabPlacement == TOP) {
 
2531
                                g2d.setColor(darkShadowColor);
 
2532
                                g2d.drawLine(x, y + ribbonDelta, x + w - 1, y + ribbonDelta);
 
2533
                                g2d.setColor(this.highlight);
 
2534
                                // g2d.drawLine(x, y + 1 + ribbonDelta, x + w - 1, y + 1 +
 
2535
                                // ribbonDelta);
 
2536
                        }
 
2537
                        if (tabPlacement == LEFT) {
 
2538
                                g2d.setPaint(new GradientPaint(x, y, darkShadowColor, x + 4
 
2539
                                                * ribbonDelta, y, this.highlight));
 
2540
                                g2d.drawLine(x, y, x + 4 * ribbonDelta, y);
 
2541
                        }
 
2542
                        if (tabPlacement == RIGHT) {
 
2543
                                g2d.setPaint(new GradientPaint(x + w - 1 - 4 * ribbonDelta, y,
 
2544
                                                this.highlight, x + w - 1, y, darkShadowColor));
 
2545
                                g2d.drawLine(x + w - 1 - 4 * ribbonDelta, y, x + w - 1, y);
 
2546
                        }
 
2547
                }
 
2548
 
 
2549
                g2d.dispose();
 
2550
        }
 
2551
 
 
2552
        @Override
 
2553
        public Rectangle getTabBounds(JTabbedPane pane, int i) {
 
2554
                this.ensureCurrentLayout();
 
2555
                Rectangle tabRect = new Rectangle();
 
2556
                return this.getTabBounds(i, tabRect);
 
2557
        }
 
2558
 
 
2559
        // /**
 
2560
        // * Returns the previous state for the specified tab.
 
2561
        // *
 
2562
        // * @param tabIndex
 
2563
        // * Tab index.
 
2564
        // * @return The previous state for the specified tab.
 
2565
        // */
 
2566
        // protected ComponentState getPrevTabState(int tabIndex) {
 
2567
        // StateTransitionTracker tracker = this.stateTransitionMultiTracker
 
2568
        // .getTracker(tabIndex);
 
2569
        // if (tracker == null) {
 
2570
        // return getTabState(tabIndex);
 
2571
        // } else {
 
2572
        // ComponentState fromTracker = tracker.getPrevModelState();
 
2573
        // boolean isEnabled = this.tabPane.isEnabledAt(tabIndex);
 
2574
        // return ComponentState.getState(isEnabled, fromTracker
 
2575
        // .isFacetActive(AnimationFacet.ROLLOVER), fromTracker
 
2576
        // .isFacetActive(AnimationFacet.SELECTION));
 
2577
        // }
 
2578
        // }
 
2579
 
 
2580
        protected StateTransitionTracker.ModelStateInfo getModelStateInfo(
 
2581
                        int tabIndex) {
 
2582
                if (this.stateTransitionMultiTracker.size() == 0)
 
2583
                        return null;
 
2584
                StateTransitionTracker tracker = this.stateTransitionMultiTracker
 
2585
                                .getTracker(tabIndex);
 
2586
                if (tracker == null) {
 
2587
                        return null;
 
2588
                } else {
 
2589
                        return tracker.getModelStateInfo();
 
2590
                }
 
2591
        }
 
2592
 
 
2593
        /**
 
2594
         * Returns the current state for the specified tab.
 
2595
         * 
 
2596
         * @param tabIndex
 
2597
         *            Tab index.
 
2598
         * @return The current state for the specified tab.
 
2599
         */
 
2600
        protected ComponentState getTabState(int tabIndex) {
 
2601
                boolean isEnabled = this.tabPane.isEnabledAt(tabIndex);
 
2602
                StateTransitionTracker tracker = this.stateTransitionMultiTracker
 
2603
                                .getTracker(tabIndex);
 
2604
                if (tracker == null) {
 
2605
                        boolean isRollover = this.getRolloverTabIndex() == tabIndex;
 
2606
                        boolean isSelected = this.tabPane.getSelectedIndex() == tabIndex;
 
2607
                        return ComponentState.getState(isEnabled, isRollover, isSelected);
 
2608
                } else {
 
2609
                        ComponentState fromTracker = tracker.getModelStateInfo()
 
2610
                                        .getCurrModelState();
 
2611
                        return ComponentState.getState(isEnabled, fromTracker
 
2612
                                        .isFacetActive(ComponentStateFacet.ROLLOVER), fromTracker
 
2613
                                        .isFacetActive(ComponentStateFacet.SELECTION));
 
2614
                }
 
2615
        }
 
2616
 
 
2617
        /*
 
2618
         * (non-Javadoc)
 
2619
         * 
 
2620
         * @see
 
2621
         * javax.swing.plaf.basic.BasicTabbedPaneUI#paintText(java.awt.Graphics,
 
2622
         * int, java.awt.Font, java.awt.FontMetrics, int, java.lang.String,
 
2623
         * java.awt.Rectangle, boolean)
 
2624
         */
 
2625
        @Override
 
2626
        protected void paintText(Graphics g, int tabPlacement, Font font,
 
2627
                        FontMetrics metrics, int tabIndex, String title,
 
2628
                        Rectangle textRect, boolean isSelected) {
 
2629
                g.setFont(font);
 
2630
 
 
2631
                View v = this.getTextViewForTab(tabIndex);
 
2632
                if (v != null) {
 
2633
                        // html
 
2634
                        v.paint(g, textRect);
 
2635
                } else {
 
2636
                        // plain text
 
2637
                        int mnemIndex = this.tabPane.getDisplayedMnemonicIndexAt(tabIndex);
 
2638
                        StateTransitionTracker.ModelStateInfo modelStateInfo = this
 
2639
                                        .getModelStateInfo(tabIndex);
 
2640
                        ComponentState currState = this.getTabState(tabIndex);
 
2641
 
 
2642
                        // System.out.println("Tab " + title + ":" + currState);
 
2643
                        Color fg = null;
 
2644
                        if (modelStateInfo != null) {
 
2645
                                Map<ComponentState, StateContributionInfo> activeStates = modelStateInfo
 
2646
                                                .getStateContributionMap();
 
2647
                                SubstanceColorScheme colorScheme = SubstanceColorSchemeUtilities
 
2648
                                                .getColorScheme(tabPane, tabIndex,
 
2649
                                                                ColorSchemeAssociationKind.TAB, currState);
 
2650
                                if (currState.isDisabled() || (activeStates == null)
 
2651
                                                || (activeStates.size() == 1)) {
 
2652
                                        fg = colorScheme.getForegroundColor();
 
2653
                                } else {
 
2654
                                        float aggrRed = 0;
 
2655
                                        float aggrGreen = 0;
 
2656
                                        float aggrBlue = 0;
 
2657
 
 
2658
                                        for (Map.Entry<ComponentState, StateTransitionTracker.StateContributionInfo> activeEntry : activeStates
 
2659
                                                        .entrySet()) {
 
2660
                                                ComponentState activeState = activeEntry.getKey();
 
2661
                                                SubstanceColorScheme scheme = SubstanceColorSchemeUtilities
 
2662
                                                                .getColorScheme(tabPane, tabIndex,
 
2663
                                                                                ColorSchemeAssociationKind.TAB,
 
2664
                                                                                activeState);
 
2665
                                                Color schemeFg = scheme.getForegroundColor();
 
2666
                                                float contribution = activeEntry.getValue()
 
2667
                                                                .getContribution();
 
2668
                                                // // System.out.println("\t" + activeState + ":"
 
2669
                                                // + contribution + ":" + scheme.getDisplayName()
 
2670
                                                // + ":" + schemeFg);
 
2671
                                                aggrRed += schemeFg.getRed() * contribution;
 
2672
                                                aggrGreen += schemeFg.getGreen() * contribution;
 
2673
                                                aggrBlue += schemeFg.getBlue() * contribution;
 
2674
                                        }
 
2675
                                        fg = new Color((int) aggrRed, (int) aggrGreen,
 
2676
                                                        (int) aggrBlue);
 
2677
                                }
 
2678
                        } else {
 
2679
                                SubstanceColorScheme scheme = SubstanceColorSchemeUtilities
 
2680
                                                .getColorScheme(tabPane, tabIndex,
 
2681
                                                                ColorSchemeAssociationKind.TAB, currState);
 
2682
                                fg = scheme.getForegroundColor();
 
2683
                        }
 
2684
 
 
2685
                        Graphics2D graphics = (Graphics2D) g.create();
 
2686
                        if (currState.isDisabled()) {
 
2687
                                Color bgFillColor = SubstanceColorUtilities
 
2688
                                                .getBackgroundFillColor(this.tabPane);
 
2689
                                fg = SubstanceColorUtilities.getInterpolatedColor(fg,
 
2690
                                                bgFillColor, SubstanceColorSchemeUtilities.getAlpha(
 
2691
                                                                this.tabPane.getComponentAt(tabIndex),
 
2692
                                                                currState));
 
2693
                        }
 
2694
                        graphics.clip(getTabRectangle(tabIndex));
 
2695
                        SubstanceTextUtilities.paintText(graphics, this.tabPane, textRect,
 
2696
                                        title, mnemIndex, graphics.getFont(), fg, null);
 
2697
                        graphics.dispose();
 
2698
                }
 
2699
        }
 
2700
 
 
2701
        @Override
 
2702
        protected void paintIcon(Graphics g, int tabPlacement, int tabIndex,
 
2703
                        Icon icon, Rectangle iconRect, boolean isSelected) {
 
2704
                if (icon == null)
 
2705
                        return;
 
2706
 
 
2707
                if (SubstanceCoreUtilities.useThemedDefaultIcon(this.tabPane)) {
 
2708
                        ComponentState currState = this.getTabState(tabIndex);
 
2709
                        StateTransitionTracker tabTracker = stateTransitionMultiTracker
 
2710
                                        .getTracker(tabIndex);
 
2711
 
 
2712
                        if (tabTracker == null) {
 
2713
                                if (currState.isFacetActive(ComponentStateFacet.ROLLOVER)
 
2714
                                                || currState
 
2715
                                                                .isFacetActive(ComponentStateFacet.SELECTION)
 
2716
                                                || currState.isDisabled()) {
 
2717
                                        // use the original (full color or disabled) icon
 
2718
                                        super.paintIcon(g, tabPlacement, tabIndex, icon, iconRect,
 
2719
                                                        isSelected);
 
2720
                                        return;
 
2721
                                }
 
2722
                        }
 
2723
 
 
2724
                        Icon themed = SubstanceCoreUtilities.getThemedIcon(this.tabPane,
 
2725
                                        tabIndex, icon);
 
2726
                        if (tabTracker == null) {
 
2727
                                super.paintIcon(g, tabPlacement, tabIndex, themed, iconRect,
 
2728
                                                isSelected);
 
2729
                        } else {
 
2730
                                Graphics2D g2d = (Graphics2D) g.create();
 
2731
                                super.paintIcon(g2d, tabPlacement, tabIndex, icon, iconRect,
 
2732
                                                isSelected);
 
2733
                                g2d
 
2734
                                                .setComposite(LafWidgetUtilities
 
2735
                                                                .getAlphaComposite(
 
2736
                                                                                this.tabPane,
 
2737
                                                                                1.0f - tabTracker
 
2738
                                                                                                .getFacetStrength(ComponentStateFacet.ROLLOVER),
 
2739
                                                                                g2d));
 
2740
                                super.paintIcon(g2d, tabPlacement, tabIndex, themed, iconRect,
 
2741
                                                isSelected);
 
2742
                                g2d.dispose();
 
2743
                        }
 
2744
                } else {
 
2745
                        super.paintIcon(g, tabPlacement, tabIndex, icon, iconRect,
 
2746
                                        isSelected);
 
2747
                }
 
2748
        }
 
2749
 
 
2750
        @Override
 
2751
        protected MouseListener createMouseListener() {
 
2752
                return null;
 
2753
        }
 
2754
 
 
2755
        /**
 
2756
         * Extension point to allow horizontal orientation of left / right placed
 
2757
         * tabs.
 
2758
         * 
 
2759
         * @param tabPlacement
 
2760
         *            Tab placement.
 
2761
         * @return Indication whether the tabs in the specified placement should be
 
2762
         *         rotated.
 
2763
         */
 
2764
        protected boolean toRotateTabsOnPlacement(int tabPlacement) {
 
2765
        Object rotateProperty = tabPane.getClientProperty(SubstanceLookAndFeel.TABBED_PANE_ROTATE_SIDE_TABS);
 
2766
        if (!(rotateProperty instanceof Boolean)) {
 
2767
            rotateProperty = UIManager.get(SubstanceLookAndFeel.TABBED_PANE_ROTATE_SIDE_TABS);
 
2768
 
 
2769
        }
 
2770
        boolean rotate = (rotateProperty instanceof Boolean) ? (Boolean)rotateProperty : true;
 
2771
 
 
2772
        return  rotate && ( (tabPlacement == SwingConstants.LEFT) || (tabPlacement == SwingConstants.RIGHT) );
 
2773
        }
 
2774
 
 
2775
        private StateTransitionTracker getTracker(final int tabIndex,
 
2776
                        boolean initialRollover, boolean initialSelected) {
 
2777
                StateTransitionTracker tracker = stateTransitionMultiTracker
 
2778
                                .getTracker(tabIndex);
 
2779
                if (tracker == null) {
 
2780
                        ButtonModel model = new DefaultButtonModel();
 
2781
                        model.setSelected(initialSelected);
 
2782
                        model.setRollover(initialRollover);
 
2783
                        tracker = new StateTransitionTracker(tabPane, model);
 
2784
                        tracker.registerModelListeners();
 
2785
                        tracker.setRepaintCallback(new RepaintCallback() {
 
2786
                                @Override
 
2787
                                public TimelineCallback getRepaintCallback() {
 
2788
                                        return new TabRepaintCallback(tabPane, tabIndex);
 
2789
                                }
 
2790
                        });
 
2791
                        stateTransitionMultiTracker.addTracker(tabIndex, tracker);
 
2792
                }
 
2793
                return tracker;
 
2794
        }
 
2795
 
 
2796
        private void trackTabModification(int tabIndex, Component tabComponent) {
 
2797
                Timeline modifiedTimeline = new Timeline(tabPane);
 
2798
                AnimationConfigurationManager.getInstance().configureModifiedTimeline(
 
2799
                                modifiedTimeline);
 
2800
                modifiedTimeline.addCallback(new TabRepaintCallback(tabPane, tabIndex));
 
2801
                modifiedTimeline.playLoop(RepeatBehavior.REVERSE);
 
2802
                modifiedTimelines.put(tabComponent, modifiedTimeline);
 
2803
        }
 
2804
}
 
 
b'\\ No newline at end of file'