2
* Copyright (c) 2005-2010 Substance Kirill Grouchnikov. All Rights Reserved.
4
* Redistribution and use in source and binary forms, with or without
5
* modification, are permitted provided that the following conditions are met:
7
* o Redistributions of source code must retain the above copyright notice,
8
* this list of conditions and the following disclaimer.
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.
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.
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.
30
package org.pushingpixels.substance.internal.ui;
32
import java.awt.Adjustable;
33
import java.awt.AlphaComposite;
34
import java.awt.Color;
35
import java.awt.Component;
36
import java.awt.Dimension;
37
import java.awt.Graphics;
38
import java.awt.Graphics2D;
39
import java.awt.Insets;
40
import java.awt.Rectangle;
41
import java.awt.Shape;
42
import java.awt.event.AdjustmentEvent;
43
import java.awt.event.AdjustmentListener;
44
import java.awt.event.MouseAdapter;
45
import java.awt.event.MouseEvent;
46
import java.awt.event.MouseListener;
47
import java.awt.geom.AffineTransform;
48
import java.awt.geom.GeneralPath;
49
import java.awt.image.BufferedImage;
50
import java.beans.PropertyChangeEvent;
51
import java.beans.PropertyChangeListener;
52
import java.util.EnumSet;
53
import java.util.LinkedList;
54
import java.util.List;
58
import javax.swing.AbstractButton;
59
import javax.swing.BoundedRangeModel;
60
import javax.swing.ButtonModel;
61
import javax.swing.DefaultButtonModel;
62
import javax.swing.Icon;
63
import javax.swing.JButton;
64
import javax.swing.JComponent;
65
import javax.swing.JScrollBar;
66
import javax.swing.JScrollPane;
67
import javax.swing.SwingUtilities;
68
import javax.swing.event.ChangeEvent;
69
import javax.swing.event.ChangeListener;
70
import javax.swing.plaf.ComponentUI;
71
import javax.swing.plaf.UIResource;
72
import javax.swing.plaf.basic.BasicScrollBarUI;
74
import org.pushingpixels.lafwidget.LafWidgetUtilities;
75
import org.pushingpixels.substance.api.ColorSchemeAssociationKind;
76
import org.pushingpixels.substance.api.ComponentState;
77
import org.pushingpixels.substance.api.SubstanceColorScheme;
78
import org.pushingpixels.substance.api.SubstanceConstants;
79
import org.pushingpixels.substance.api.SubstanceLookAndFeel;
80
import org.pushingpixels.substance.api.SubstanceConstants.ScrollPaneButtonPolicyKind;
81
import org.pushingpixels.substance.api.SubstanceConstants.Side;
82
import org.pushingpixels.substance.api.painter.border.SubstanceBorderPainter;
83
import org.pushingpixels.substance.api.painter.fill.SubstanceFillPainter;
84
import org.pushingpixels.substance.api.shaper.ClassicButtonShaper;
85
import org.pushingpixels.substance.api.shaper.SubstanceButtonShaper;
86
import org.pushingpixels.substance.internal.animation.StateTransitionTracker;
87
import org.pushingpixels.substance.internal.animation.TransitionAwareUI;
88
import org.pushingpixels.substance.internal.painter.BackgroundPaintingUtils;
89
import org.pushingpixels.substance.internal.painter.SimplisticFillPainter;
90
import org.pushingpixels.substance.internal.painter.SimplisticSoftBorderPainter;
91
import org.pushingpixels.substance.internal.utils.HashMapKey;
92
import org.pushingpixels.substance.internal.utils.LazyResettableHashMap;
93
import org.pushingpixels.substance.internal.utils.RolloverControlListener;
94
import org.pushingpixels.substance.internal.utils.SubstanceColorSchemeUtilities;
95
import org.pushingpixels.substance.internal.utils.SubstanceCoreUtilities;
96
import org.pushingpixels.substance.internal.utils.SubstanceImageCreator;
97
import org.pushingpixels.substance.internal.utils.SubstanceOutlineUtilities;
98
import org.pushingpixels.substance.internal.utils.SubstanceSizeUtils;
99
import org.pushingpixels.substance.internal.utils.icon.ArrowButtonTransitionAwareIcon;
100
import org.pushingpixels.substance.internal.utils.scroll.SubstanceScrollButton;
103
* UI for scroll bars in <b>Substance </b> look and feel.
105
* @author Kirill Grouchnikov
107
public class SubstanceScrollBarUI extends BasicScrollBarUI implements
110
* The second decrease button. Is shown under
111
* {@link org.pushingpixels.substance.api.SubstanceConstants.ScrollPaneButtonPolicyKind#ADJACENT},
112
* {@link org.pushingpixels.substance.api.SubstanceConstants.ScrollPaneButtonPolicyKind#MULTIPLE} and
113
* {@link org.pushingpixels.substance.api.SubstanceConstants.ScrollPaneButtonPolicyKind#MULTIPLE_BOTH}
118
protected JButton mySecondDecreaseButton;
121
* The second increase button. Is shown only under
122
* {@link org.pushingpixels.substance.api.SubstanceConstants.ScrollPaneButtonPolicyKind#MULTIPLE_BOTH} mode.
126
protected JButton mySecondIncreaseButton;
129
* Surrogate button model for tracking the thumb transitions.
131
private ButtonModel thumbModel;
134
* Stores computed images for vertical thumbs.
136
private static LazyResettableHashMap<BufferedImage> thumbVerticalMap = new LazyResettableHashMap<BufferedImage>(
137
"SubstanceScrollBarUI.thumbVertical");
140
* Stores computed images for horizontal thumbs.
142
private static LazyResettableHashMap<BufferedImage> thumbHorizontalMap = new LazyResettableHashMap<BufferedImage>(
143
"SubstanceScrollBarUI.thumbHorizontal");
146
* Mouse listener on the associated scroll bar.
148
private MouseListener substanceMouseListener;
151
* Listener for thumb transition animations.
153
private RolloverControlListener substanceThumbRolloverListener;
155
protected StateTransitionTracker compositeStateTransitionTracker;
158
* Property change listener.
161
private PropertyChangeListener substancePropertyListener;
166
protected int scrollBarWidth;
169
* Cache of images for horizontal tracks.
171
private static LazyResettableHashMap<BufferedImage> trackHorizontalMap = new LazyResettableHashMap<BufferedImage>(
172
"SubstanceScrollBarUI.trackHorizontal");
175
* Cache of images for vertical tracks.
177
private static LazyResettableHashMap<BufferedImage> trackVerticalMap = new LazyResettableHashMap<BufferedImage>(
178
"SubstanceScrollBarUI.trackVertical");
181
* Listener on adjustments made to the scrollbar model - this is for
182
* repaiting both scrollbars with the viewport.
186
protected AdjustmentListener substanceAdjustmentListener;
189
* Surrogate model to sync between rollover effects of scroll buttons and
190
* scroll track / scroll thumb.
194
protected CompositeButtonModel compositeScrollTrackModel;
199
* @see javax.swing.plaf.ComponentUI#createUI(javax.swing.JComponent)
201
public static ComponentUI createUI(JComponent comp) {
202
SubstanceCoreUtilities.testComponentCreationThreadingViolation(comp);
203
return new SubstanceScrollBarUI(comp);
207
* Simple constructor.
210
* Associated component.
212
protected SubstanceScrollBarUI(JComponent b) {
214
this.thumbModel = new DefaultButtonModel();
215
this.thumbModel.setArmed(false);
216
this.thumbModel.setSelected(false);
217
this.thumbModel.setPressed(false);
218
this.thumbModel.setRollover(false);
224
* Creates a decrease button.
227
* Button orientation.
229
* if <code>true</code>, the regular (upper / left) decrease
230
* button is created, if <code>false</code>, the additional
231
* (lower / right) decrease button is created for
232
* {@link org.pushingpixels.substance.api.SubstanceConstants.ScrollPaneButtonPolicyKind#ADJACENT}
234
* {@link org.pushingpixels.substance.api.SubstanceConstants.ScrollPaneButtonPolicyKind#MULTIPLE}
236
* {@link org.pushingpixels.substance.api.SubstanceConstants.ScrollPaneButtonPolicyKind#MULTIPLE_BOTH}
238
* @return Decrease button.
240
protected JButton createGeneralDecreaseButton(final int orientation,
242
JButton result = new SubstanceScrollButton(orientation);
243
result.setName("Decrease " + (isRegular ? "regular" : "additional"));
244
result.setFont(this.scrollbar.getFont());
245
Icon icon = new ArrowButtonTransitionAwareIcon(result, orientation);
246
result.setIcon(icon);
247
result.setFont(scrollbar.getFont());
249
result.setPreferredSize(new Dimension(this.scrollBarWidth,
250
this.scrollBarWidth));
252
Set<Side> openSides = EnumSet.noneOf(Side.class);
253
Set<Side> straightSides = EnumSet.noneOf(Side.class);
254
switch (orientation) {
256
openSides.add(Side.BOTTOM);
258
openSides.add(Side.TOP);
260
straightSides.add(Side.TOP);
263
openSides.add(Side.LEFT);
265
openSides.add(Side.RIGHT);
267
straightSides.add(Side.RIGHT);
270
openSides.add(Side.RIGHT);
272
openSides.add(Side.LEFT);
274
straightSides.add(Side.LEFT);
277
result.putClientProperty(
278
SubstanceLookAndFeel.BUTTON_OPEN_SIDE_PROPERTY, openSides);
279
result.putClientProperty(SubstanceLookAndFeel.BUTTON_SIDE_PROPERTY,
288
* @see javax.swing.plaf.basic.BasicScrollBarUI#createDecreaseButton(int)
291
protected JButton createDecreaseButton(int orientation) {
292
return this.createGeneralDecreaseButton(orientation, true);
298
* @see javax.swing.plaf.basic.BasicScrollBarUI#createIncreaseButton(int)
301
protected JButton createIncreaseButton(int orientation) {
302
return this.createGeneralIncreaseButton(orientation, true);
306
* Creates a increase button.
309
* Button orientation.
311
* if <code>true</code>, the regular (lower / right) increase
312
* button is created, if <code>false</code>, the additional
313
* (upper / left) increase button is created for
314
* {@link org.pushingpixels.substance.api.SubstanceConstants.ScrollPaneButtonPolicyKind#MULTIPLE_BOTH}
316
* @return Increase button.
318
protected JButton createGeneralIncreaseButton(final int orientation,
320
JButton result = new SubstanceScrollButton(orientation);
321
result.setName("Increase " + (isRegular ? "regular" : "additional"));
322
result.setFont(this.scrollbar.getFont());
323
Icon icon = new ArrowButtonTransitionAwareIcon(result, orientation);
324
result.setIcon(icon);
325
result.setFont(scrollbar.getFont());
326
// JButton result = new SubstanceScrollBarButton(icon, orientation);
327
result.setPreferredSize(new Dimension(this.scrollBarWidth,
328
this.scrollBarWidth));
330
Set<Side> openSides = EnumSet.noneOf(Side.class);
331
Set<Side> straightSides = EnumSet.noneOf(Side.class);
332
switch (orientation) {
334
openSides.add(Side.TOP);
336
openSides.add(Side.BOTTOM);
338
straightSides.add(Side.BOTTOM);
341
openSides.add(Side.LEFT);
343
openSides.add(Side.RIGHT);
345
straightSides.add(Side.RIGHT);
348
openSides.add(Side.RIGHT);
350
openSides.add(Side.LEFT);
352
straightSides.add(Side.LEFT);
355
result.putClientProperty(
356
SubstanceLookAndFeel.BUTTON_OPEN_SIDE_PROPERTY, openSides);
357
result.putClientProperty(SubstanceLookAndFeel.BUTTON_SIDE_PROPERTY,
363
* Returns the image for a horizontal track.
367
* @param leftActiveButton
368
* The closest left button in the scroll bar. May be
370
* @param rightActiveButton
371
* The closest right button in the scroll bar. May be
372
* <code>null</code> .
373
* @return Horizontal track image.
375
private void paintTrackHorizontal(Graphics g, Rectangle trackBounds,
376
SubstanceScrollButton leftActiveButton,
377
SubstanceScrollButton rightActiveButton) {
378
int width = Math.max(1, trackBounds.width);
379
int height = Math.max(1, trackBounds.height);
381
paintTrackBackHorizontal(g, this.scrollbar, leftActiveButton,
382
rightActiveButton, width, height);
383
BufferedImage horizontalTrack = getTrackHorizontal(this.scrollbar,
385
g.drawImage(horizontalTrack, 0, 0, null);
389
* Returns the image for a horizontal track.
394
* Scroll track width.
396
* Scroll track height.
397
* @return Horizontal track image.
399
private static BufferedImage getTrackHorizontal(JScrollBar scrollBar,
400
int width, int height) {
401
SubstanceButtonShaper shaper = SubstanceCoreUtilities
402
.getButtonShaper(scrollBar);
403
SubstanceColorScheme mainScheme = SubstanceColorSchemeUtilities
404
.getColorScheme(scrollBar,
405
scrollBar.isEnabled() ? ComponentState.ENABLED
406
: ComponentState.DISABLED_UNSELECTED);
407
SubstanceColorScheme mainBorderScheme = SubstanceColorSchemeUtilities
408
.getColorScheme(scrollBar, ColorSchemeAssociationKind.BORDER,
409
scrollBar.isEnabled() ? ComponentState.ENABLED
410
: ComponentState.DISABLED_UNSELECTED);
411
HashMapKey key = SubstanceCoreUtilities.getHashKey(mainScheme
412
.getDisplayName(), mainBorderScheme.getDisplayName(), width,
413
height, shaper.getDisplayName());
414
float radius = height / 2;
415
if (shaper instanceof ClassicButtonShaper)
416
radius = SubstanceSizeUtils
417
.getClassicButtonCornerRadius(SubstanceSizeUtils
418
.getComponentFontSize(scrollBar));
420
int borderDelta = (int) Math.floor(SubstanceSizeUtils
421
.getBorderStrokeWidth(SubstanceSizeUtils
422
.getComponentFontSize(scrollBar)) / 2.0);
423
Shape contour = SubstanceOutlineUtilities.getBaseOutline(width, height,
424
radius, null, borderDelta);
425
BufferedImage result = SubstanceScrollBarUI.trackHorizontalMap.get(key);
426
if (result == null) {
427
result = SubstanceCoreUtilities.getBlankImage(width, height);
428
SimplisticFillPainter.INSTANCE.paintContourBackground(result
429
.createGraphics(), scrollBar, width, height, contour,
430
false, mainScheme, true);
432
SubstanceBorderPainter borderPainter = new SimplisticSoftBorderPainter();
433
borderPainter.paintBorder(result.getGraphics(), scrollBar, width,
434
height, contour, null, mainBorderScheme);
436
SubstanceScrollBarUI.trackHorizontalMap.put(key, result);
442
* Returns the image for a horizontal track.
446
* @param leftActiveButton
447
* The closest left button in the scroll bar. May be
449
* @param rightActiveButton
450
* The closest right button in the scroll bar. May be
451
* <code>null</code> .
453
* Scroll track width.
455
* Scroll track height.
456
* @return Horizontal track image.
458
private static void paintTrackBackHorizontal(Graphics g,
459
JScrollBar scrollBar, AbstractButton leftActiveButton,
460
AbstractButton rightActiveButton, int width, int height) {
461
SubstanceButtonShaper shaper = SubstanceCoreUtilities
462
.getButtonShaper(scrollBar);
463
int radius = height / 2;
464
if (shaper instanceof ClassicButtonShaper)
466
SubstanceImageCreator.paintCompositeRoundedBackground(scrollBar, g,
467
width, height, radius, leftActiveButton, rightActiveButton,
472
* Returns the image for a vertical track.
476
* @param topActiveButton
477
* The closest top button in the scroll bar. May be
479
* @param bottomActiveButton
480
* The closest bottom button in the scroll bar. May be
482
* @return Vertical track image.
484
private void paintTrackVertical(Graphics g, Rectangle trackBounds,
485
SubstanceScrollButton topActiveButton,
486
SubstanceScrollButton bottomActiveButton) {
488
int width = Math.max(1, trackBounds.width);
489
int height = Math.max(1, trackBounds.height);
491
paintTrackBackVertical(g, this.scrollbar, topActiveButton,
492
bottomActiveButton, width, height);
493
BufferedImage horizontalTrack = getTrackVertical(this.scrollbar, width,
495
g.drawImage(horizontalTrack, 0, 0, null);
499
* Returns the image for a vertical track.
504
* Scroll track width.
506
* Scroll track height.
507
* @return Vertical track image.
509
private static BufferedImage getTrackVertical(JScrollBar scrollBar,
510
int width, int height) {
511
SubstanceButtonShaper shaper = SubstanceCoreUtilities
512
.getButtonShaper(scrollBar);
513
SubstanceColorScheme mainScheme = SubstanceColorSchemeUtilities
514
.getColorScheme(scrollBar,
515
scrollBar.isEnabled() ? ComponentState.ENABLED
516
: ComponentState.DISABLED_UNSELECTED);
517
SubstanceColorScheme mainBorderScheme = SubstanceColorSchemeUtilities
518
.getColorScheme(scrollBar, ColorSchemeAssociationKind.BORDER,
519
scrollBar.isEnabled() ? ComponentState.ENABLED
520
: ComponentState.DISABLED_UNSELECTED);
521
HashMapKey key = SubstanceCoreUtilities.getHashKey(mainScheme
522
.getDisplayName(), mainBorderScheme.getDisplayName(), width,
523
height, shaper.getDisplayName());
524
BufferedImage result = SubstanceScrollBarUI.trackVerticalMap.get(key);
525
if (result == null) {
526
float radius = width / 2;
527
if (shaper instanceof ClassicButtonShaper)
528
radius = SubstanceSizeUtils
529
.getClassicButtonCornerRadius(SubstanceSizeUtils
530
.getComponentFontSize(scrollBar));
532
int borderDelta = (int) Math.floor(SubstanceSizeUtils
533
.getBorderStrokeWidth(SubstanceSizeUtils
534
.getComponentFontSize(scrollBar)) / 2.0);
535
Shape contour = SubstanceOutlineUtilities.getBaseOutline(height,
536
width, radius, null, borderDelta);
538
result = SubstanceCoreUtilities.getBlankImage(height, width);
539
SimplisticFillPainter.INSTANCE.paintContourBackground(result
540
.createGraphics(), scrollBar, height, width, contour,
541
false, mainScheme, true);
543
SubstanceBorderPainter borderPainter = new SimplisticSoftBorderPainter();
544
borderPainter.paintBorder(result.getGraphics(), scrollBar, height,
545
width, contour, null, mainBorderScheme);
546
result = SubstanceImageCreator.getRotated(result, 3);
548
SubstanceScrollBarUI.trackVerticalMap.put(key, result);
554
* Returns the image for a vertical track.
558
* @param topActiveButton
559
* The closest top button in the scroll bar. May be
561
* @param bottomActiveButton
562
* The closest bottom button in the scroll bar. May be
565
* Scroll track width.
567
* Scroll track height.
568
* @return Vertical track image.
570
private static void paintTrackBackVertical(Graphics g,
571
JScrollBar scrollBar, AbstractButton topActiveButton,
572
AbstractButton bottomActiveButton, int width, int height) {
573
SubstanceButtonShaper shaper = SubstanceCoreUtilities
574
.getButtonShaper(scrollBar);
575
int radius = width / 2;
576
if (shaper instanceof ClassicButtonShaper)
579
Graphics2D g2d = (Graphics2D) g.create();
580
AffineTransform at = AffineTransform.getTranslateInstance(0, height);
581
at.rotate(-Math.PI / 2);
583
SubstanceImageCreator.paintCompositeRoundedBackground(scrollBar, g2d,
584
height, width, radius, topActiveButton, bottomActiveButton,
590
* Retrieves image for vertical thumb.
593
* Thumb bounding rectangle.
594
* @return Image for vertical thumb.
596
private BufferedImage getThumbVertical(Rectangle thumbBounds) {
597
int width = Math.max(1, thumbBounds.width);
598
int height = Math.max(1, thumbBounds.height);
600
StateTransitionTracker.ModelStateInfo modelStateInfo = this.compositeStateTransitionTracker
601
.getModelStateInfo();
602
ComponentState currState = modelStateInfo.getCurrModelState();
604
// enabled scroll bar is always painted as active
605
SubstanceColorScheme baseFillScheme = (currState != ComponentState.ENABLED) ? SubstanceColorSchemeUtilities
606
.getColorScheme(this.scrollbar, currState)
607
: SubstanceColorSchemeUtilities.getActiveColorScheme(
608
this.scrollbar, currState);
609
SubstanceColorScheme baseBorderScheme = SubstanceColorSchemeUtilities
610
.getColorScheme(this.scrollbar,
611
ColorSchemeAssociationKind.BORDER, currState);
612
BufferedImage baseLayer = getThumbVertical(this.scrollbar, width,
613
height, baseFillScheme, baseBorderScheme);
615
Map<ComponentState, StateTransitionTracker.StateContributionInfo> activeStates = modelStateInfo
616
.getStateContributionMap();
617
if (currState.isDisabled() || (activeStates.size() == 1)) {
621
BufferedImage result = SubstanceCoreUtilities.getBlankImage(baseLayer
622
.getWidth(), baseLayer.getHeight());
623
Graphics2D g2d = result.createGraphics();
624
g2d.drawImage(baseLayer, 0, 0, null);
626
for (Map.Entry<ComponentState, StateTransitionTracker.StateContributionInfo> activeEntry : activeStates
628
ComponentState activeState = activeEntry.getKey();
629
if (activeState == modelStateInfo.getCurrModelState())
632
float contribution = activeEntry.getValue().getContribution();
633
if (contribution == 0.0f)
636
g2d.setComposite(AlphaComposite.SrcOver.derive(contribution));
638
SubstanceColorScheme fillScheme = (activeState != ComponentState.ENABLED) ? SubstanceColorSchemeUtilities
639
.getColorScheme(this.scrollbar, activeState)
640
: SubstanceColorSchemeUtilities.getActiveColorScheme(
641
this.scrollbar, activeState);
642
SubstanceColorScheme borderScheme = SubstanceColorSchemeUtilities
643
.getColorScheme(this.scrollbar,
644
ColorSchemeAssociationKind.BORDER, activeState);
645
BufferedImage layer = getThumbVertical(this.scrollbar, width,
646
height, fillScheme, borderScheme);
647
g2d.drawImage(layer, 0, 0, null);
655
* Retrieves image for vertical thumb.
664
* The first color scheme.
665
* @param borderScheme
666
* The first border color scheme.
667
* @return Image for vertical thumb.
669
private static BufferedImage getThumbVertical(JScrollBar scrollBar,
670
int width, int height, SubstanceColorScheme scheme,
671
SubstanceColorScheme borderScheme) {
672
SubstanceFillPainter painter = SubstanceCoreUtilities
673
.getFillPainter(scrollBar);
674
SubstanceButtonShaper shaper = SubstanceCoreUtilities
675
.getButtonShaper(scrollBar);
676
SubstanceBorderPainter borderPainter = SubstanceCoreUtilities
677
.getBorderPainter(scrollBar);
678
HashMapKey key = SubstanceCoreUtilities.getHashKey(width, height,
679
scheme.getDisplayName(), borderScheme.getDisplayName(), painter
680
.getDisplayName(), shaper.getDisplayName(),
681
borderPainter.getDisplayName());
682
BufferedImage result = SubstanceScrollBarUI.thumbVerticalMap.get(key);
683
if (result == null) {
684
// System.out.println("Cache miss - computing");
685
// System.out.println("New image for vertical thumb");
686
float radius = width / 2;
687
if (shaper instanceof ClassicButtonShaper)
688
radius = SubstanceSizeUtils
689
.getClassicButtonCornerRadius(SubstanceSizeUtils
690
.getComponentFontSize(scrollBar));
692
int borderDelta = (int) Math.floor(SubstanceSizeUtils
693
.getBorderStrokeWidth(SubstanceSizeUtils
694
.getComponentFontSize(scrollBar)) / 2.0);
695
GeneralPath contour = SubstanceOutlineUtilities.getBaseOutline(
696
height, width, radius, null, borderDelta);
698
result = SubstanceCoreUtilities.getBlankImage(height, width);
699
painter.paintContourBackground(result.createGraphics(), scrollBar,
700
height, width, contour, false, scheme, true);
702
// int borderThickness = (int) SubstanceSizeUtils
703
// .getBorderStrokeWidth(SubstanceSizeUtils
704
// .getComponentFontSize(scrollBar));
705
// GeneralPath contourInner = SubstanceOutlineUtilities
706
// .getBaseOutline(height, width, radius, null,
707
// borderThickness + borderDelta);
708
borderPainter.paintBorder(result.getGraphics(), scrollBar, height,
709
width, contour, null, borderScheme);
710
result = SubstanceImageCreator.getRotated(result, 3);
711
// System.out.println(key);
712
SubstanceScrollBarUI.thumbVerticalMap.put(key, result);
719
* Retrieves image for horizontal thumb.
722
* Thumb bounding rectangle.
723
* @return Image for horizontal thumb.
725
private BufferedImage getThumbHorizontal(Rectangle thumbBounds) {
726
int width = Math.max(1, thumbBounds.width);
727
int height = Math.max(1, thumbBounds.height);
729
StateTransitionTracker.ModelStateInfo modelStateInfo = this.compositeStateTransitionTracker
730
.getModelStateInfo();
731
ComponentState currState = modelStateInfo.getCurrModelState();
732
// enabled scroll bar is always painted as active
733
// if (currState == ComponentState.ENABLED)
734
// currState = ComponentState.SELECTED;
736
SubstanceColorScheme baseFillScheme = (currState != ComponentState.ENABLED) ? SubstanceColorSchemeUtilities
737
.getColorScheme(this.scrollbar, currState)
738
: SubstanceColorSchemeUtilities.getActiveColorScheme(
739
this.scrollbar, currState);
740
SubstanceColorScheme baseBorderScheme = SubstanceColorSchemeUtilities
741
.getColorScheme(this.scrollbar,
742
ColorSchemeAssociationKind.BORDER, currState);
743
BufferedImage baseLayer = getThumbHorizontal(this.scrollbar, width,
744
height, baseFillScheme, baseBorderScheme);
746
Map<ComponentState, StateTransitionTracker.StateContributionInfo> activeStates = modelStateInfo
747
.getStateContributionMap();
748
if (currState.isDisabled() || (activeStates.size() == 1)) {
752
BufferedImage result = SubstanceCoreUtilities.getBlankImage(baseLayer
753
.getWidth(), baseLayer.getHeight());
754
Graphics2D g2d = result.createGraphics();
755
g2d.drawImage(baseLayer, 0, 0, null);
757
for (Map.Entry<ComponentState, StateTransitionTracker.StateContributionInfo> activeEntry : activeStates
759
ComponentState activeState = activeEntry.getKey();
760
if (activeState == modelStateInfo.getCurrModelState())
762
// if (activeState == ComponentState.ENABLED)
763
// activeState = ComponentState.SELECTED;
765
float contribution = activeEntry.getValue().getContribution();
766
if (contribution == 0.0f)
769
g2d.setComposite(AlphaComposite.SrcOver.derive(contribution));
771
SubstanceColorScheme fillScheme = (activeState != ComponentState.ENABLED) ? SubstanceColorSchemeUtilities
772
.getColorScheme(this.scrollbar, activeState)
773
: SubstanceColorSchemeUtilities.getActiveColorScheme(
774
this.scrollbar, activeState);
775
SubstanceColorScheme borderScheme = SubstanceColorSchemeUtilities
776
.getColorScheme(this.scrollbar,
777
ColorSchemeAssociationKind.BORDER, activeState);
778
BufferedImage layer = getThumbHorizontal(this.scrollbar, width,
779
height, fillScheme, borderScheme);
780
g2d.drawImage(layer, 0, 0, null);
788
* Retrieves image for horizontal thumb.
797
* The first color scheme.
798
* @param borderScheme
799
* The first border color scheme.
800
* @return Image for horizontal thumb.
802
private static BufferedImage getThumbHorizontal(JScrollBar scrollBar,
803
int width, int height, SubstanceColorScheme scheme,
804
SubstanceColorScheme borderScheme) {
805
SubstanceFillPainter painter = SubstanceCoreUtilities
806
.getFillPainter(scrollBar);
807
SubstanceButtonShaper shaper = SubstanceCoreUtilities
808
.getButtonShaper(scrollBar);
809
SubstanceBorderPainter borderPainter = SubstanceCoreUtilities
810
.getBorderPainter(scrollBar);
811
HashMapKey key = SubstanceCoreUtilities.getHashKey(width, height,
812
scheme.getDisplayName(), borderScheme.getDisplayName(), painter
813
.getDisplayName(), shaper.getDisplayName(),
814
borderPainter.getDisplayName());
816
float radius = height / 2;
817
if (shaper instanceof ClassicButtonShaper)
818
radius = SubstanceSizeUtils
819
.getClassicButtonCornerRadius(SubstanceSizeUtils
820
.getComponentFontSize(scrollBar));
821
int borderDelta = (int) Math.floor(SubstanceSizeUtils
822
.getBorderStrokeWidth(SubstanceSizeUtils
823
.getComponentFontSize(scrollBar)) / 2.0);
824
GeneralPath contour = SubstanceOutlineUtilities.getBaseOutline(width,
825
height, radius, null, borderDelta);
826
BufferedImage opaque = SubstanceScrollBarUI.thumbHorizontalMap.get(key);
827
if (opaque == null) {
828
// System.out.println("New image for horizontal thumb");
830
opaque = SubstanceCoreUtilities.getBlankImage(width, height);
831
painter.paintContourBackground(opaque.createGraphics(), scrollBar,
832
width, height, contour, false, scheme, true);
834
borderPainter.paintBorder(opaque.getGraphics(), scrollBar, width,
835
height, contour, null, borderScheme);
836
SubstanceScrollBarUI.thumbHorizontalMap.put(key, opaque);
843
* Returns the scroll button state.
845
* @param scrollButton
847
* @return Scroll button state.
849
protected ComponentState getState(JButton scrollButton) {
850
if (scrollButton == null)
853
ComponentState result = ((TransitionAwareUI) scrollButton.getUI())
854
.getTransitionTracker().getModelStateInfo().getCurrModelState();
855
if ((result == ComponentState.ENABLED)
856
&& SubstanceCoreUtilities.hasFlatAppearance(this.scrollbar,
860
if (SubstanceCoreUtilities.isButtonNeverPainted(scrollButton)) {
870
* javax.swing.plaf.basic.BasicScrollBarUI#paintTrack(java.awt.Graphics,
871
* javax.swing.JComponent, java.awt.Rectangle)
874
protected void paintTrack(Graphics g, JComponent c, Rectangle trackBounds) {
875
Graphics2D graphics = (Graphics2D) g.create();
877
// System.out.println("Track");
878
ScrollPaneButtonPolicyKind buttonPolicy = SubstanceCoreUtilities
879
.getScrollPaneButtonsPolicyKind(this.scrollbar);
880
SubstanceScrollButton compTopState = null;
881
SubstanceScrollButton compBottomState = null;
882
if (this.decrButton.isShowing() && this.incrButton.isShowing()
883
&& this.mySecondDecreaseButton.isShowing()
884
&& this.mySecondIncreaseButton.isShowing()) {
885
switch (buttonPolicy) {
887
compTopState = (SubstanceScrollButton) this.decrButton;
888
compBottomState = (SubstanceScrollButton) this.incrButton;
891
compBottomState = (SubstanceScrollButton) this.mySecondDecreaseButton;
894
compTopState = (SubstanceScrollButton) this.decrButton;
895
compBottomState = (SubstanceScrollButton) this.mySecondDecreaseButton;
898
compTopState = (SubstanceScrollButton) this.mySecondIncreaseButton;
899
compBottomState = (SubstanceScrollButton) this.mySecondDecreaseButton;
904
graphics.translate(trackBounds.x, trackBounds.y);
905
if (this.scrollbar.getOrientation() == Adjustable.VERTICAL) {
906
paintTrackVertical(graphics, trackBounds, compTopState,
909
if (this.scrollbar.getComponentOrientation().isLeftToRight()) {
910
paintTrackHorizontal(graphics, trackBounds, compTopState,
913
paintTrackHorizontal(graphics, trackBounds, compBottomState,
916
// BufferedImage bi = this.scrollbar.getComponentOrientation()
917
// .isLeftToRight() ? this.getTrackHorizontal(trackBounds,
918
// compTopState, compBottomState) : this.getTrackHorizontal(
919
// trackBounds, compBottomState, compTopState);
920
// graphics.drawImage(bi, trackBounds.x, trackBounds.y, null);
930
* javax.swing.plaf.basic.BasicScrollBarUI#paintThumb(java.awt.Graphics,
931
* javax.swing.JComponent, java.awt.Rectangle)
934
protected void paintThumb(Graphics g, JComponent c, Rectangle thumbBounds) {
935
// System.out.println("Thumb");
936
Graphics2D graphics = (Graphics2D) g.create();
937
// ControlBackgroundComposite composite = SubstanceCoreUtilities
938
// .getControlBackgroundComposite(this.scrollbar);
940
// JScrollBar scrollBar = (JScrollBar) c;
941
this.thumbModel.setSelected(this.thumbModel.isSelected()
943
this.thumbModel.setEnabled(c.isEnabled());
944
boolean isVertical = (this.scrollbar.getOrientation() == Adjustable.VERTICAL);
946
Rectangle adjustedBounds = new Rectangle(thumbBounds.x,
947
thumbBounds.y, thumbBounds.width, thumbBounds.height);
948
BufferedImage thumbImage = this.getThumbVertical(adjustedBounds);
949
graphics.drawImage(thumbImage, adjustedBounds.x, adjustedBounds.y,
952
Rectangle adjustedBounds = new Rectangle(thumbBounds.x,
953
thumbBounds.y, thumbBounds.width, thumbBounds.height);
954
BufferedImage thumbImage = this.getThumbHorizontal(adjustedBounds);
955
graphics.drawImage(thumbImage, adjustedBounds.x, adjustedBounds.y,
962
public void paint(Graphics g, JComponent c) {
963
Graphics2D graphics = (Graphics2D) g.create();
964
BackgroundPaintingUtils.update(graphics, c, false);
965
float alpha = SubstanceColorSchemeUtilities.getAlpha(this.scrollbar,
966
ComponentState.getState(this.thumbModel, this.scrollbar));
968
.setComposite(LafWidgetUtilities.getAlphaComposite(c, alpha, g));
969
super.paint(graphics, c);
976
* @see javax.swing.plaf.basic.BasicScrollBarUI#installDefaults()
979
protected void installDefaults() {
980
super.installDefaults();
981
this.scrollBarWidth = SubstanceSizeUtils
982
.getScrollBarWidth(SubstanceSizeUtils
983
.getComponentFontSize(this.scrollbar));
989
* @see javax.swing.plaf.basic.BasicScrollBarUI#installComponents()
992
protected void installComponents() {
993
super.installComponents();
994
switch (this.scrollbar.getOrientation()) {
995
case JScrollBar.VERTICAL:
996
this.mySecondDecreaseButton = this.createGeneralDecreaseButton(
998
this.mySecondIncreaseButton = this.createGeneralIncreaseButton(
1002
case JScrollBar.HORIZONTAL:
1003
if (this.scrollbar.getComponentOrientation().isLeftToRight()) {
1004
this.mySecondDecreaseButton = this.createGeneralDecreaseButton(
1006
this.mySecondIncreaseButton = this.createGeneralIncreaseButton(
1009
this.mySecondDecreaseButton = this.createGeneralDecreaseButton(
1011
this.mySecondIncreaseButton = this.createGeneralIncreaseButton(
1016
this.scrollbar.add(this.mySecondDecreaseButton);
1017
this.scrollbar.add(this.mySecondIncreaseButton);
1019
this.compositeScrollTrackModel = new CompositeButtonModel(
1020
this.thumbModel, this.incrButton, this.decrButton,
1021
this.mySecondDecreaseButton, this.mySecondIncreaseButton);
1022
this.compositeScrollTrackModel.registerListeners();
1024
this.compositeStateTransitionTracker = new StateTransitionTracker(
1025
this.scrollbar, this.compositeScrollTrackModel);
1026
this.compositeStateTransitionTracker.registerModelListeners();
1032
* @see javax.swing.plaf.basic.BasicScrollBarUI#uninstallComponents()
1035
protected void uninstallComponents() {
1036
this.compositeScrollTrackModel.unregisterListeners();
1037
this.compositeStateTransitionTracker.unregisterModelListeners();
1039
this.scrollbar.remove(this.mySecondDecreaseButton);
1040
this.scrollbar.remove(this.mySecondIncreaseButton);
1041
super.uninstallComponents();
1047
* @see javax.swing.plaf.basic.BasicScrollBarUI#installListeners()
1050
protected void installListeners() {
1051
super.installListeners();
1052
this.substanceMouseListener = new MouseAdapter() {
1054
public void mouseEntered(MouseEvent e) {
1055
SubstanceScrollBarUI.this.scrollbar.repaint();
1059
public void mouseExited(MouseEvent e) {
1060
SubstanceScrollBarUI.this.scrollbar.repaint();
1064
public void mousePressed(MouseEvent e) {
1065
SubstanceScrollBarUI.this.scrollbar.repaint();
1069
public void mouseReleased(MouseEvent e) {
1070
SubstanceScrollBarUI.this.scrollbar.repaint();
1074
this.incrButton.addMouseListener(this.substanceMouseListener);
1075
this.decrButton.addMouseListener(this.substanceMouseListener);
1076
this.mySecondDecreaseButton
1077
.addMouseListener(this.substanceMouseListener);
1078
this.mySecondIncreaseButton
1079
.addMouseListener(this.substanceMouseListener);
1081
this.substanceThumbRolloverListener = new RolloverControlListener(this,
1083
this.scrollbar.addMouseListener(this.substanceThumbRolloverListener);
1085
.addMouseMotionListener(this.substanceThumbRolloverListener);
1087
// this.thumbStateTransitionTracker.registerModelListeners();
1089
this.substancePropertyListener = new PropertyChangeListener() {
1091
public void propertyChange(PropertyChangeEvent evt) {
1092
if ("font".equals(evt.getPropertyName())) {
1093
SwingUtilities.invokeLater(new Runnable() {
1096
if ( scrollbar != null ) scrollbar.updateUI();
1100
if ("background".equals(evt.getPropertyName())) {
1101
// propagate application-specific background color to the
1103
Color newBackgr = (Color) evt.getNewValue();
1104
if (!(newBackgr instanceof UIResource)) {
1105
if (mySecondDecreaseButton != null) {
1106
if (mySecondDecreaseButton.getBackground() instanceof UIResource) {
1107
mySecondDecreaseButton.setBackground(newBackgr);
1110
if (mySecondIncreaseButton != null) {
1111
if (mySecondIncreaseButton.getBackground() instanceof UIResource) {
1112
mySecondIncreaseButton.setBackground(newBackgr);
1115
if (incrButton != null) {
1116
if (incrButton.getBackground() instanceof UIResource) {
1117
incrButton.setBackground(newBackgr);
1120
if (decrButton != null) {
1121
if (decrButton.getBackground() instanceof UIResource) {
1122
decrButton.setBackground(newBackgr);
1130
.addPropertyChangeListener(this.substancePropertyListener);
1132
this.mySecondDecreaseButton.addMouseListener(this.buttonListener);
1133
this.mySecondIncreaseButton.addMouseListener(this.buttonListener);
1135
this.substanceAdjustmentListener = new AdjustmentListener() {
1137
public void adjustmentValueChanged(AdjustmentEvent e) {
1138
SubstanceCoreUtilities
1139
.testComponentStateChangeThreadingViolation(scrollbar);
1140
Component parent = SubstanceScrollBarUI.this.scrollbar
1142
if (parent instanceof JScrollPane) {
1143
JScrollPane jsp = (JScrollPane) parent;
1144
JScrollBar hor = jsp.getHorizontalScrollBar();
1145
JScrollBar ver = jsp.getVerticalScrollBar();
1147
JScrollBar other = null;
1148
if (SubstanceScrollBarUI.this.scrollbar == hor) {
1151
if (SubstanceScrollBarUI.this.scrollbar == ver) {
1155
if ((other != null) && other.isVisible())
1157
SubstanceScrollBarUI.this.scrollbar.repaint();
1161
this.scrollbar.addAdjustmentListener(this.substanceAdjustmentListener);
1167
* @see javax.swing.plaf.basic.BasicScrollBarUI#uninstallListeners()
1170
protected void uninstallListeners() {
1171
// fix for defect 109 - memory leak on changing skin
1172
this.incrButton.removeMouseListener(this.substanceMouseListener);
1173
this.decrButton.removeMouseListener(this.substanceMouseListener);
1174
this.mySecondDecreaseButton
1175
.removeMouseListener(this.substanceMouseListener);
1176
this.mySecondIncreaseButton
1177
.removeMouseListener(this.substanceMouseListener);
1178
this.substanceMouseListener = null;
1180
this.scrollbar.removeMouseListener(this.substanceThumbRolloverListener);
1182
.removeMouseMotionListener(this.substanceThumbRolloverListener);
1183
this.substanceThumbRolloverListener = null;
1186
.removePropertyChangeListener(this.substancePropertyListener);
1187
this.substancePropertyListener = null;
1189
this.mySecondDecreaseButton.removeMouseListener(this.buttonListener);
1190
this.mySecondIncreaseButton.removeMouseListener(this.buttonListener);
1193
.removeAdjustmentListener(this.substanceAdjustmentListener);
1194
this.substanceAdjustmentListener = null;
1196
super.uninstallListeners();
1200
public boolean isInside(MouseEvent me) {
1201
Rectangle trackB = this.getTrackBounds();
1204
return trackB.contains(me.getX(), me.getY());
1208
public StateTransitionTracker getTransitionTracker() {
1209
return this.compositeStateTransitionTracker;
1215
* @see javax.swing.plaf.basic.BasicScrollBarUI#scrollByBlock(int)
1218
public void scrollByBlock(int direction) {
1219
// This method is called from SubstanceScrollPaneUI to implement wheel
1221
int oldValue = this.scrollbar.getValue();
1222
int blockIncrement = this.scrollbar.getBlockIncrement(direction);
1223
int delta = blockIncrement * ((direction > 0) ? +1 : -1);
1224
int newValue = oldValue + delta;
1226
// Check for overflow.
1227
if ((delta > 0) && (newValue < oldValue)) {
1228
newValue = this.scrollbar.getMaximum();
1229
} else if ((delta < 0) && (newValue > oldValue)) {
1230
newValue = this.scrollbar.getMinimum();
1233
this.scrollbar.setValue(newValue);
1237
* Scrolls the associated scroll bar.
1244
public void scrollByUnits(int direction, int units) {
1245
// This method is called from SubstanceScrollPaneUI to implement wheel
1249
for (int i = 0; i < units; i++) {
1250
if (direction > 0) {
1251
delta = this.scrollbar.getUnitIncrement(direction);
1253
delta = -this.scrollbar.getUnitIncrement(direction);
1256
int oldValue = this.scrollbar.getValue();
1257
int newValue = oldValue + delta;
1259
// Check for overflow.
1260
if ((delta > 0) && (newValue < oldValue)) {
1261
newValue = this.scrollbar.getMaximum();
1262
} else if ((delta < 0) && (newValue > oldValue)) {
1263
newValue = this.scrollbar.getMinimum();
1265
if (oldValue == newValue) {
1268
this.scrollbar.setValue(newValue);
1276
* javax.swing.plaf.basic.BasicScrollBarUI#layoutVScrollbar(javax.swing.
1280
protected void layoutVScrollbar(JScrollBar sb) {
1281
ScrollPaneButtonPolicyKind buttonPolicy = SubstanceCoreUtilities
1282
.getScrollPaneButtonsPolicyKind(this.scrollbar);
1283
this.mySecondDecreaseButton.setBounds(0, 0, 0, 0);
1284
this.mySecondIncreaseButton.setBounds(0, 0, 0, 0);
1285
switch (buttonPolicy) {
1287
super.layoutVScrollbar(sb);
1290
this.layoutVScrollbarNone(sb);
1293
this.layoutVScrollbarAdjacent(sb);
1296
this.layoutVScrollbarMultiple(sb);
1299
this.layoutVScrollbarMultipleBoth(sb);
1308
* javax.swing.plaf.basic.BasicScrollBarUI#layoutHScrollbar(javax.swing.
1312
protected void layoutHScrollbar(JScrollBar sb) {
1313
this.mySecondDecreaseButton.setBounds(0, 0, 0, 0);
1314
this.mySecondIncreaseButton.setBounds(0, 0, 0, 0);
1315
ScrollPaneButtonPolicyKind buttonPolicy = SubstanceCoreUtilities
1316
.getScrollPaneButtonsPolicyKind(this.scrollbar);
1317
switch (buttonPolicy) {
1319
super.layoutHScrollbar(sb);
1322
this.layoutHScrollbarNone(sb);
1325
this.layoutHScrollbarAdjacent(sb);
1328
this.layoutHScrollbarMultiple(sb);
1331
this.layoutHScrollbarMultipleBoth(sb);
1337
* Lays out the vertical scroll bar when the button policy is
1338
* {@link org.pushingpixels.substance.api.SubstanceConstants.ScrollPaneButtonPolicyKind#ADJACENT}.
1343
protected void layoutVScrollbarAdjacent(JScrollBar sb) {
1344
Dimension sbSize = sb.getSize();
1345
Insets sbInsets = sb.getInsets();
1348
* Width and left edge of the buttons and thumb.
1350
int itemW = sbSize.width - (sbInsets.left + sbInsets.right);
1351
int itemX = sbInsets.left;
1354
* Nominal locations of the buttons, assuming their preferred size will
1357
int incrButtonH = itemW;
1358
int incrButtonY = sbSize.height - (sbInsets.bottom + incrButtonH);
1360
int decrButton2H = itemW;
1361
int decrButton2Y = incrButtonY - decrButton2H;
1364
* The thumb must fit within the height left over after we subtract the
1365
* preferredSize of the buttons and the insets.
1367
int sbInsetsH = sbInsets.top + sbInsets.bottom;
1368
int sbButtonsH = decrButton2H + incrButtonH;
1369
float trackH = sbSize.height - (sbInsetsH + sbButtonsH);
1372
* Compute the height and origin of the thumb. The case where the thumb
1373
* is at the bottom edge is handled specially to avoid numerical
1374
* problems in computing thumbY. Enforce the thumbs min/max dimensions.
1375
* If the thumb doesn't fit in the track (trackH) we'll hide it later.
1377
float min = sb.getMinimum();
1378
float extent = sb.getVisibleAmount();
1379
float range = sb.getMaximum() - min;
1380
float value = sb.getValue();
1382
int thumbH = (range <= 0) ? this.getMaximumThumbSize().height
1383
: (int) (trackH * (extent / range));
1384
thumbH = Math.max(thumbH, this.getMinimumThumbSize().height);
1385
thumbH = Math.min(thumbH, this.getMaximumThumbSize().height);
1387
int thumbY = decrButton2Y - thumbH;
1388
if (value < (sb.getMaximum() - sb.getVisibleAmount())) {
1389
float thumbRange = trackH - thumbH;
1390
thumbY = (int) (0.5f + (thumbRange * ((value - min) / (range - extent))));
1394
* If the buttons don't fit, allocate half of the available space to
1395
* each and move the lower one (incrButton) down.
1397
int sbAvailButtonH = (sbSize.height - sbInsetsH);
1398
if (sbAvailButtonH < sbButtonsH) {
1399
incrButtonH = decrButton2H = sbAvailButtonH / 2;
1400
incrButtonY = sbSize.height - (sbInsets.bottom + incrButtonH);
1402
this.mySecondIncreaseButton.setBounds(0, 0, 0, 0);
1403
this.decrButton.setBounds(0, 0, 0, 0);
1404
this.mySecondDecreaseButton.setBounds(itemX,
1405
incrButtonY - decrButton2H, itemW, decrButton2H);
1406
this.incrButton.setBounds(itemX, incrButtonY - 1, itemW,
1410
* Update the trackRect field.
1413
int itrackH = decrButton2Y - itrackY;
1414
this.trackRect.setBounds(itemX, itrackY, itemW, itrackH);
1417
* If the thumb isn't going to fit, zero it's bounds. Otherwise make
1418
* sure it fits between the buttons. Note that setting the thumbs bounds
1419
* will cause a repaint.
1421
if (thumbH >= (int) trackH) {
1422
this.setThumbBounds(0, 0, 0, 0);
1424
if ((thumbY + thumbH) > decrButton2Y) {
1425
thumbY = decrButton2Y - thumbH;
1430
this.setThumbBounds(itemX, thumbY, itemW, thumbH);
1435
* Lays out the vertical scroll bar when the button policy is
1436
* {@link org.pushingpixels.substance.api.SubstanceConstants.ScrollPaneButtonPolicyKind#ADJACENT}.
1441
protected void layoutVScrollbarNone(JScrollBar sb) {
1442
Dimension sbSize = sb.getSize();
1443
Insets sbInsets = sb.getInsets();
1446
* Width and left edge of the buttons and thumb.
1448
int itemW = sbSize.width - (sbInsets.left + sbInsets.right);
1449
int itemX = sbInsets.left;
1452
* Nominal locations of the buttons, assuming their preferred size will
1455
int incrButtonH = 0;
1456
int incrButtonY = sbSize.height - (sbInsets.bottom + incrButtonH);
1458
int decrButton2H = 0;
1459
int decrButton2Y = incrButtonY - decrButton2H;
1462
* The thumb must fit within the height left over after we subtract the
1463
* preferredSize of the buttons and the insets.
1465
int sbInsetsH = sbInsets.top + sbInsets.bottom;
1466
int sbButtonsH = decrButton2H + incrButtonH;
1467
float trackH = sbSize.height - (sbInsetsH + sbButtonsH);
1470
* Compute the height and origin of the thumb. The case where the thumb
1471
* is at the bottom edge is handled specially to avoid numerical
1472
* problems in computing thumbY. Enforce the thumbs min/max dimensions.
1473
* If the thumb doesn't fit in the track (trackH) we'll hide it later.
1475
float min = sb.getMinimum();
1476
float extent = sb.getVisibleAmount();
1477
float range = sb.getMaximum() - min;
1478
float value = sb.getValue();
1480
int thumbH = (range <= 0) ? this.getMaximumThumbSize().height
1481
: (int) (trackH * (extent / range));
1482
thumbH = Math.max(thumbH, this.getMinimumThumbSize().height);
1483
thumbH = Math.min(thumbH, this.getMaximumThumbSize().height);
1485
int thumbY = decrButton2Y - thumbH;
1486
if (value < (sb.getMaximum() - sb.getVisibleAmount())) {
1487
float thumbRange = trackH - thumbH;
1488
thumbY = (int) (0.5f + (thumbRange * ((value - min) / (range - extent))));
1492
* If the buttons don't fit, allocate half of the available space to
1493
* each and move the lower one (incrButton) down.
1495
int sbAvailButtonH = (sbSize.height - sbInsetsH);
1496
if (sbAvailButtonH < sbButtonsH) {
1497
incrButtonH = 0;// decrButton2H = 0;
1498
// incrButtonY = sbSize.height - (sbInsets.bottom + incrButtonH);
1500
this.decrButton.setBounds(0, 0, 0, 0);
1501
this.mySecondDecreaseButton.setBounds(0, 0, 0, 0);
1502
this.incrButton.setBounds(0, 0, 0, 0);
1503
this.mySecondIncreaseButton.setBounds(0, 0, 0, 0);
1506
* Update the trackRect field.
1509
int itrackH = decrButton2Y - itrackY;
1510
this.trackRect.setBounds(itemX, itrackY, itemW, itrackH);
1513
* If the thumb isn't going to fit, zero it's bounds. Otherwise make
1514
* sure it fits between the buttons. Note that setting the thumbs bounds
1515
* will cause a repaint.
1517
if (thumbH >= (int) trackH) {
1518
this.setThumbBounds(0, 0, 0, 0);
1520
if ((thumbY + thumbH) > decrButton2Y) {
1521
thumbY = decrButton2Y - thumbH;
1526
this.setThumbBounds(itemX, thumbY, itemW, thumbH);
1531
* Lays out the vertical scroll bar when the button policy is
1532
* {@link org.pushingpixels.substance.api.SubstanceConstants.ScrollPaneButtonPolicyKind#MULTIPLE}.
1537
protected void layoutVScrollbarMultiple(JScrollBar sb) {
1538
Dimension sbSize = sb.getSize();
1539
Insets sbInsets = sb.getInsets();
1542
* Width and left edge of the buttons and thumb.
1544
int itemW = sbSize.width - (sbInsets.left + sbInsets.right);
1545
int itemX = sbInsets.left;
1548
* Nominal locations of the buttons, assuming their preferred size will
1551
int incrButtonH = itemW;
1552
int incrButtonY = sbSize.height - (sbInsets.bottom + incrButtonH);
1554
int decrButton2H = itemW;
1555
int decrButton2Y = incrButtonY - decrButton2H;
1557
int decrButtonH = itemW;
1558
int decrButtonY = sbInsets.top;
1561
* The thumb must fit within the height left over after we subtract the
1562
* preferredSize of the buttons and the insets.
1564
int sbInsetsH = sbInsets.top + sbInsets.bottom;
1565
int sbButtonsH = decrButton2H + incrButtonH + decrButtonH;
1566
float trackH = sbSize.height - (sbInsetsH + sbButtonsH);
1569
* Compute the height and origin of the thumb. The case where the thumb
1570
* is at the bottom edge is handled specially to avoid numerical
1571
* problems in computing thumbY. Enforce the thumbs min/max dimensions.
1572
* If the thumb doesn't fit in the track (trackH) we'll hide it later.
1574
float min = sb.getMinimum();
1575
float extent = sb.getVisibleAmount();
1576
float range = sb.getMaximum() - min;
1577
float value = sb.getValue();
1579
int thumbH = (range <= 0) ? this.getMaximumThumbSize().height
1580
: (int) (trackH * (extent / range));
1581
thumbH = Math.max(thumbH, this.getMinimumThumbSize().height);
1582
thumbH = Math.min(thumbH, this.getMaximumThumbSize().height);
1584
int thumbY = decrButton2Y - thumbH;
1585
if (value < (sb.getMaximum() - sb.getVisibleAmount())) {
1586
float thumbRange = trackH - thumbH;
1587
thumbY = (int) (0.5f + (thumbRange * ((value - min) / (range - extent))));
1588
thumbY += decrButtonY + decrButtonH;
1592
* If the buttons don't fit, allocate half of the available space to
1593
* each and move the lower one (incrButton) down.
1595
int sbAvailButtonH = (sbSize.height - sbInsetsH);
1596
if (sbAvailButtonH < sbButtonsH) {
1597
incrButtonH = decrButton2H = decrButtonH = sbAvailButtonH / 2;
1598
incrButtonY = sbSize.height - (sbInsets.bottom + incrButtonH);
1600
this.decrButton.setBounds(itemX, decrButtonY, itemW, decrButtonH);
1601
this.mySecondDecreaseButton.setBounds(itemX,
1602
incrButtonY - decrButton2H, itemW, decrButton2H);
1603
this.incrButton.setBounds(itemX, incrButtonY - 1, itemW,
1605
this.mySecondIncreaseButton.setBounds(0, 0, 0, 0);
1608
* Update the trackRect field.
1610
int itrackY = decrButtonY + decrButtonH;
1611
int itrackH = decrButton2Y - itrackY;
1612
this.trackRect.setBounds(itemX, itrackY, itemW, itrackH);
1615
* If the thumb isn't going to fit, zero it's bounds. Otherwise make
1616
* sure it fits between the buttons. Note that setting the thumbs bounds
1617
* will cause a repaint.
1619
if (thumbH >= (int) trackH) {
1620
this.setThumbBounds(0, 0, 0, 0);
1622
if ((thumbY + thumbH) > decrButton2Y) {
1623
thumbY = decrButton2Y - thumbH;
1625
if (thumbY < (decrButtonY + decrButtonH)) {
1626
thumbY = decrButtonY + decrButtonH + 1;
1628
this.setThumbBounds(itemX, thumbY, itemW, thumbH);
1633
* Lays out the vertical scroll bar when the button policy is
1634
* {@link org.pushingpixels.substance.api.SubstanceConstants.ScrollPaneButtonPolicyKind#MULTIPLE_BOTH}.
1639
protected void layoutVScrollbarMultipleBoth(JScrollBar sb) {
1640
Dimension sbSize = sb.getSize();
1641
Insets sbInsets = sb.getInsets();
1644
* Width and left edge of the buttons and thumb.
1646
int itemW = sbSize.width - (sbInsets.left + sbInsets.right);
1647
int itemX = sbInsets.left;
1650
* Nominal locations of the buttons, assuming their preferred size will
1653
int incrButtonH = itemW;
1654
int incrButtonY = sbSize.height - (sbInsets.bottom + incrButtonH);
1656
int decrButton2H = itemW;
1657
int decrButton2Y = incrButtonY - decrButton2H;
1659
int decrButtonH = itemW;
1660
int decrButtonY = sbInsets.top;
1662
int incrButton2H = itemW;
1663
int incrButton2Y = decrButtonY + decrButtonH;
1666
* The thumb must fit within the height left over after we subtract the
1667
* preferredSize of the buttons and the insets.
1669
int sbInsetsH = sbInsets.top + sbInsets.bottom;
1670
int sbButtonsH = decrButton2H + incrButtonH + decrButtonH
1672
float trackH = sbSize.height - (sbInsetsH + sbButtonsH);
1675
* Compute the height and origin of the thumb. The case where the thumb
1676
* is at the bottom edge is handled specially to avoid numerical
1677
* problems in computing thumbY. Enforce the thumbs min/max dimensions.
1678
* If the thumb doesn't fit in the track (trackH) we'll hide it later.
1680
float min = sb.getMinimum();
1681
float extent = sb.getVisibleAmount();
1682
float range = sb.getMaximum() - min;
1683
float value = sb.getValue();
1685
int thumbH = (range <= 0) ? this.getMaximumThumbSize().height
1686
: (int) (trackH * (extent / range));
1687
thumbH = Math.max(thumbH, this.getMinimumThumbSize().height);
1688
thumbH = Math.min(thumbH, this.getMaximumThumbSize().height);
1690
int thumbY = decrButton2Y - thumbH;
1691
if (value < (sb.getMaximum() - sb.getVisibleAmount())) {
1692
float thumbRange = trackH - thumbH;
1693
thumbY = (int) (0.5f + (thumbRange * ((value - min) / (range - extent))));
1694
thumbY += incrButton2Y + incrButton2H;
1698
* If the buttons don't fit, allocate half of the available space to
1699
* each and move the lower one (incrButton) down.
1701
int sbAvailButtonH = (sbSize.height - sbInsetsH);
1702
if (sbAvailButtonH < sbButtonsH) {
1703
incrButtonH = decrButton2H = decrButtonH = incrButton2H = sbAvailButtonH / 4;
1704
incrButtonY = sbSize.height - (sbInsets.bottom + incrButtonH);
1706
this.decrButton.setBounds(itemX, decrButtonY, itemW, decrButtonH);
1707
this.mySecondDecreaseButton.setBounds(itemX,
1708
incrButtonY - decrButton2H, itemW, decrButton2H);
1709
this.incrButton.setBounds(itemX, incrButtonY - 1, itemW,
1711
this.mySecondIncreaseButton.setBounds(itemX, decrButtonY + decrButtonH
1712
- 1, itemW, incrButton2H + 1);
1715
* Update the trackRect field.
1717
int itrackY = incrButton2Y + incrButton2H;
1718
int itrackH = decrButton2Y - itrackY;
1719
this.trackRect.setBounds(itemX, itrackY, itemW, itrackH);
1722
* If the thumb isn't going to fit, zero it's bounds. Otherwise make
1723
* sure it fits between the buttons. Note that setting the thumbs bounds
1724
* will cause a repaint.
1726
if (thumbH >= (int) trackH) {
1727
this.setThumbBounds(0, 0, 0, 0);
1729
if ((thumbY + thumbH) > decrButton2Y) {
1730
thumbY = decrButton2Y - thumbH;
1732
if (thumbY < (incrButton2Y + incrButton2H)) {
1733
thumbY = incrButton2Y + incrButton2H + 1;
1735
this.setThumbBounds(itemX, thumbY, itemW, thumbH);
1740
* Lays out the horizontal scroll bar when the button policy is
1741
* {@link org.pushingpixels.substance.api.SubstanceConstants.ScrollPaneButtonPolicyKind#ADJACENT}.
1746
protected void layoutHScrollbarAdjacent(JScrollBar sb) {
1747
Dimension sbSize = sb.getSize();
1748
Insets sbInsets = sb.getInsets();
1751
* Height and top edge of the buttons and thumb.
1753
int itemH = sbSize.height - (sbInsets.top + sbInsets.bottom);
1754
int itemY = sbInsets.top;
1756
boolean ltr = sb.getComponentOrientation().isLeftToRight();
1759
* Nominal locations of the buttons, assuming their preferred size will
1762
int decrButton2W = itemH;
1763
int incrButtonW = itemH;
1764
int incrButtonX = ltr ? sbSize.width - (sbInsets.right + incrButtonW)
1766
int decrButton2X = ltr ? incrButtonX - decrButton2W : incrButtonX
1770
* The thumb must fit within the width left over after we subtract the
1771
* preferredSize of the buttons and the insets.
1773
int sbInsetsW = sbInsets.left + sbInsets.right;
1774
int sbButtonsW = decrButton2W + incrButtonW;
1775
float trackW = sbSize.width - (sbInsetsW + sbButtonsW);
1778
* Compute the width and origin of the thumb. Enforce the thumbs min/max
1779
* dimensions. The case where the thumb is at the right edge is handled
1780
* specially to avoid numerical problems in computing thumbX. If the
1781
* thumb doesn't fit in the track (trackH) we'll hide it later.
1783
float min = sb.getMinimum();
1784
float max = sb.getMaximum();
1785
float extent = sb.getVisibleAmount();
1786
float range = max - min;
1787
float value = sb.getValue();
1789
int thumbW = (range <= 0) ? this.getMaximumThumbSize().width
1790
: (int) (trackW * (extent / range));
1791
thumbW = Math.max(thumbW, this.getMinimumThumbSize().width);
1792
thumbW = Math.min(thumbW, this.getMaximumThumbSize().width);
1794
int thumbX = ltr ? decrButton2X - thumbW : sbInsets.left;
1795
if (value < (max - sb.getVisibleAmount())) {
1796
float thumbRange = trackW - thumbW;
1798
thumbX = (int) (0.5f + (thumbRange * ((value - min) / (range - extent))));
1800
thumbX = (int) (0.5f + (thumbRange * ((max - extent - value) / (range - extent))));
1801
thumbX += decrButton2X + decrButton2W;
1806
* If the buttons don't fit, allocate half of the available space to
1807
* each and move the right one over.
1809
int sbAvailButtonW = (sbSize.width - sbInsetsW);
1810
if (sbAvailButtonW < sbButtonsW) {
1811
incrButtonW = decrButton2W = sbAvailButtonW / 2;
1812
incrButtonX = ltr ? sbSize.width - (sbInsets.right + incrButtonW)
1816
this.mySecondDecreaseButton.setBounds(decrButton2X + (ltr ? 0 : -1),
1817
itemY, decrButton2W + 1, itemH);
1818
this.incrButton.setBounds(incrButtonX, itemY, incrButtonW, itemH);
1819
this.decrButton.setBounds(0, 0, 0, 0);
1820
this.mySecondIncreaseButton.setBounds(0, 0, 0, 0);
1823
* Update the trackRect field.
1826
int itrackX = sbInsets.left;
1827
int itrackW = decrButton2X - itrackX;
1828
this.trackRect.setBounds(itrackX, itemY, itrackW, itemH);
1830
int itrackX = decrButton2X + decrButton2W;
1831
int itrackW = sbSize.width - itrackX;
1832
this.trackRect.setBounds(itrackX, itemY, itrackW, itemH);
1836
* Make sure the thumb fits between the buttons. Note that setting the
1837
* thumbs bounds causes a repaint.
1839
if (thumbW >= (int) trackW) {
1840
this.setThumbBounds(0, 0, 0, 0);
1843
if (thumbX + thumbW > decrButton2X) {
1844
thumbX = decrButton2X - thumbW;
1850
if (thumbX + thumbW > (sbSize.width - sbInsets.left)) {
1851
thumbX = sbSize.width - sbInsets.left - thumbW;
1853
if (thumbX < (decrButton2X + decrButton2W)) {
1854
thumbX = decrButton2X + decrButton2W + 1;
1857
this.setThumbBounds(thumbX, itemY, thumbW, itemH);
1862
* Lays out the horizontal scroll bar when the button policy is
1863
* {@link org.pushingpixels.substance.api.SubstanceConstants.ScrollPaneButtonPolicyKind#NONE}.
1868
protected void layoutHScrollbarNone(JScrollBar sb) {
1869
Dimension sbSize = sb.getSize();
1870
Insets sbInsets = sb.getInsets();
1873
* Height and top edge of the buttons and thumb.
1875
int itemH = sbSize.height - (sbInsets.top + sbInsets.bottom);
1876
int itemY = sbInsets.top;
1878
boolean ltr = sb.getComponentOrientation().isLeftToRight();
1881
* Nominal locations of the buttons, assuming their preferred size will
1884
int decrButton2W = 0;
1885
int incrButtonW = 0;
1886
int incrButtonX = ltr ? sbSize.width - (sbInsets.right + incrButtonW)
1888
int decrButton2X = ltr ? incrButtonX - decrButton2W : incrButtonX
1892
* The thumb must fit within the width left over after we subtract the
1893
* preferredSize of the buttons and the insets.
1895
int sbInsetsW = sbInsets.left + sbInsets.right;
1896
int sbButtonsW = decrButton2W + incrButtonW;
1897
float trackW = sbSize.width - (sbInsetsW + sbButtonsW);
1900
* Compute the width and origin of the thumb. Enforce the thumbs min/max
1901
* dimensions. The case where the thumb is at the right edge is handled
1902
* specially to avoid numerical problems in computing thumbX. If the
1903
* thumb doesn't fit in the track (trackH) we'll hide it later.
1905
float min = sb.getMinimum();
1906
float max = sb.getMaximum();
1907
float extent = sb.getVisibleAmount();
1908
float range = max - min;
1909
float value = sb.getValue();
1911
int thumbW = (range <= 0) ? this.getMaximumThumbSize().width
1912
: (int) (trackW * (extent / range));
1913
thumbW = Math.max(thumbW, this.getMinimumThumbSize().width);
1914
thumbW = Math.min(thumbW, this.getMaximumThumbSize().width);
1916
int thumbX = ltr ? decrButton2X - thumbW : sbInsets.left;
1917
if (value < (max - sb.getVisibleAmount())) {
1918
float thumbRange = trackW - thumbW;
1920
thumbX = (int) (0.5f + (thumbRange * ((value - min) / (range - extent))));
1922
thumbX = (int) (0.5f + (thumbRange * ((max - extent - value) / (range - extent))));
1923
thumbX += decrButton2X + decrButton2W;
1928
* If the buttons don't fit, allocate half of the available space to
1929
* each and move the right one over.
1931
int sbAvailButtonW = (sbSize.width - sbInsetsW);
1932
if (sbAvailButtonW < sbButtonsW) {
1933
incrButtonW = decrButton2W = 0;
1934
// incrButtonX = ltr ? sbSize.width - (sbInsets.right + incrButtonW)
1938
this.incrButton.setBounds(0, 0, 0, 0);
1939
this.decrButton.setBounds(0, 0, 0, 0);
1940
this.mySecondIncreaseButton.setBounds(0, 0, 0, 0);
1941
this.mySecondDecreaseButton.setBounds(0, 0, 0, 0);
1944
* Update the trackRect field.
1947
int itrackX = sbInsets.left;
1948
int itrackW = decrButton2X - itrackX;
1949
this.trackRect.setBounds(itrackX, itemY, itrackW, itemH);
1951
int itrackX = decrButton2X + decrButton2W;
1952
int itrackW = sbSize.width - itrackX;
1953
this.trackRect.setBounds(itrackX, itemY, itrackW, itemH);
1957
* Make sure the thumb fits between the buttons. Note that setting the
1958
* thumbs bounds causes a repaint.
1960
if (thumbW >= (int) trackW) {
1961
this.setThumbBounds(0, 0, 0, 0);
1964
if (thumbX + thumbW > decrButton2X) {
1965
thumbX = decrButton2X - thumbW;
1971
if (thumbX + thumbW > (sbSize.width - sbInsets.left)) {
1972
thumbX = sbSize.width - sbInsets.left - thumbW;
1974
if (thumbX < (decrButton2X + decrButton2W)) {
1975
thumbX = decrButton2X + decrButton2W + 1;
1978
this.setThumbBounds(thumbX, itemY, thumbW, itemH);
1983
* Lays out the horizontal scroll bar when the button policy is
1984
* {@link org.pushingpixels.substance.api.SubstanceConstants.ScrollPaneButtonPolicyKind#MULTIPLE}.
1989
protected void layoutHScrollbarMultiple(JScrollBar sb) {
1990
Dimension sbSize = sb.getSize();
1991
Insets sbInsets = sb.getInsets();
1994
* Height and top edge of the buttons and thumb.
1996
int itemH = sbSize.height - (sbInsets.top + sbInsets.bottom);
1997
int itemY = sbInsets.top;
1999
boolean ltr = sb.getComponentOrientation().isLeftToRight();
2002
* Nominal locations of the buttons, assuming their preferred size will
2005
int decrButton2W = itemH;
2006
int decrButtonW = itemH;
2007
int incrButtonW = itemH;
2008
int incrButtonX = ltr ? sbSize.width - (sbInsets.right + incrButtonW)
2010
int decrButton2X = ltr ? incrButtonX - decrButton2W : incrButtonX
2012
int decrButtonX = ltr ? sbInsets.left : sbSize.width - sbInsets.right
2016
* The thumb must fit within the width left over after we subtract the
2017
* preferredSize of the buttons and the insets.
2019
int sbInsetsW = sbInsets.left + sbInsets.right;
2020
int sbButtonsW = decrButton2W + incrButtonW + decrButtonW;
2021
float trackW = sbSize.width - (sbInsetsW + sbButtonsW);
2024
* Compute the width and origin of the thumb. Enforce the thumbs min/max
2025
* dimensions. The case where the thumb is at the right edge is handled
2026
* specially to avoid numerical problems in computing thumbX. If the
2027
* thumb doesn't fit in the track (trackH) we'll hide it later.
2029
float min = sb.getMinimum();
2030
float max = sb.getMaximum();
2031
float extent = sb.getVisibleAmount();
2032
float range = max - min;
2033
float value = sb.getValue();
2035
int thumbW = (range <= 0) ? this.getMaximumThumbSize().width
2036
: (int) (trackW * (extent / range));
2037
thumbW = Math.max(thumbW, this.getMinimumThumbSize().width);
2038
thumbW = Math.min(thumbW, this.getMaximumThumbSize().width);
2040
int thumbX = ltr ? decrButton2X - thumbW : sbInsets.left;
2041
if (value < (max - sb.getVisibleAmount())) {
2042
float thumbRange = trackW - thumbW;
2044
thumbX = (int) (0.5f + (thumbRange * ((value - min) / (range - extent))));
2045
thumbX += decrButtonX + decrButtonW;
2047
thumbX = (int) (0.5f + (thumbRange * ((max - extent - value) / (range - extent))));
2048
thumbX += decrButton2X + decrButton2W;
2053
* If the buttons don't fit, allocate half of the available space to
2054
* each and move the right one over.
2056
int sbAvailButtonW = (sbSize.width - sbInsetsW);
2057
if (sbAvailButtonW < sbButtonsW) {
2058
incrButtonW = decrButton2W = decrButtonW = sbAvailButtonW / 2;
2059
incrButtonX = ltr ? sbSize.width - (sbInsets.right + incrButtonW)
2063
this.mySecondDecreaseButton.setBounds(decrButton2X + (ltr ? 0 : -1),
2064
itemY, decrButton2W + 1, itemH);
2065
this.incrButton.setBounds(incrButtonX, itemY, incrButtonW, itemH);
2066
this.decrButton.setBounds(decrButtonX, itemY, decrButtonW, itemH);
2067
this.mySecondIncreaseButton.setBounds(0, 0, 0, 0);
2070
* Update the trackRect field.
2073
int itrackX = decrButtonX + decrButtonW;
2074
int itrackW = decrButton2X - itrackX;
2075
this.trackRect.setBounds(itrackX, itemY, itrackW, itemH);
2077
int itrackX = decrButton2X + decrButton2W;
2078
int itrackW = decrButtonX - itrackX;
2079
this.trackRect.setBounds(itrackX, itemY, itrackW, itemH);
2083
* Make sure the thumb fits between the buttons. Note that setting the
2084
* thumbs bounds causes a repaint.
2086
if (thumbW >= (int) trackW) {
2087
this.setThumbBounds(0, 0, 0, 0);
2090
if (thumbX + thumbW > decrButton2X) {
2091
thumbX = decrButton2X - thumbW;
2093
if (thumbX < (decrButtonX + decrButtonW)) {
2094
thumbX = decrButtonX + decrButtonW + 1;
2097
if (thumbX + thumbW > decrButtonX) {
2098
thumbX = decrButtonX - thumbW;
2100
if (thumbX < (decrButton2X + decrButton2W)) {
2101
thumbX = decrButton2X + decrButton2W + 1;
2104
this.setThumbBounds(thumbX, itemY, thumbW, itemH);
2109
* Lays out the horizontal scroll bar when the button policy is
2110
* {@link org.pushingpixels.substance.api.SubstanceConstants.ScrollPaneButtonPolicyKind#MULTIPLE}.
2115
protected void layoutHScrollbarMultipleBoth(JScrollBar sb) {
2116
Dimension sbSize = sb.getSize();
2117
Insets sbInsets = sb.getInsets();
2120
* Height and top edge of the buttons and thumb.
2122
int itemH = sbSize.height - (sbInsets.top + sbInsets.bottom);
2123
int itemY = sbInsets.top;
2125
boolean ltr = sb.getComponentOrientation().isLeftToRight();
2128
* Nominal locations of the buttons, assuming their preferred size will
2131
int decrButton2W = itemH;
2132
int incrButton2W = itemH;
2133
int decrButtonW = itemH;
2134
int incrButtonW = itemH;
2136
int incrButtonX = ltr ? sbSize.width - (sbInsets.right + incrButtonW)
2138
int decrButton2X = ltr ? incrButtonX - decrButton2W : incrButtonX
2140
int decrButtonX = ltr ? sbInsets.left : sbSize.width - sbInsets.right
2142
int incrButton2X = ltr ? decrButtonX + decrButtonW : decrButtonX
2146
* The thumb must fit within the width left over after we subtract the
2147
* preferredSize of the buttons and the insets.
2149
int sbInsetsW = sbInsets.left + sbInsets.right;
2150
int sbButtonsW = decrButton2W + incrButtonW + decrButtonW
2152
float trackW = sbSize.width - (sbInsetsW + sbButtonsW);
2155
* Compute the width and origin of the thumb. Enforce the thumbs min/max
2156
* dimensions. The case where the thumb is at the right edge is handled
2157
* specially to avoid numerical problems in computing thumbX. If the
2158
* thumb doesn't fit in the track (trackH) we'll hide it later.
2160
float min = sb.getMinimum();
2161
float max = sb.getMaximum();
2162
float extent = sb.getVisibleAmount();
2163
float range = max - min;
2164
float value = sb.getValue();
2166
int thumbW = (range <= 0) ? this.getMaximumThumbSize().width
2167
: (int) (trackW * (extent / range));
2168
thumbW = Math.max(thumbW, this.getMinimumThumbSize().width);
2169
thumbW = Math.min(thumbW, this.getMaximumThumbSize().width);
2171
int thumbX = ltr ? decrButton2X - thumbW : sbInsets.left;
2172
if (value < (max - sb.getVisibleAmount())) {
2173
float thumbRange = trackW - thumbW;
2175
thumbX = (int) (0.5f + (thumbRange * ((value - min) / (range - extent))));
2176
thumbX += incrButton2X + incrButton2W;
2178
thumbX = (int) (0.5f + (thumbRange * ((max - extent - value) / (range - extent))));
2179
thumbX += decrButton2X + decrButton2W;
2184
* If the buttons don't fit, allocate half of the available space to
2185
* each and move the right one over.
2187
int sbAvailButtonW = (sbSize.width - sbInsetsW);
2188
if (sbAvailButtonW < sbButtonsW) {
2189
incrButtonW = decrButton2W = decrButtonW = incrButton2W = sbAvailButtonW / 4;
2190
incrButtonX = ltr ? sbSize.width - (sbInsets.right + incrButtonW)
2194
this.mySecondDecreaseButton.setBounds(decrButton2X + (ltr ? 0 : -1),
2195
itemY, decrButton2W + 1, itemH);
2196
this.mySecondIncreaseButton.setBounds(incrButton2X + (ltr ? -1 : 0),
2197
itemY, incrButton2W + 1, itemH);
2198
this.incrButton.setBounds(incrButtonX, itemY, incrButtonW, itemH);
2199
this.decrButton.setBounds(decrButtonX, itemY, decrButtonW, itemH);
2202
* Update the trackRect field.
2205
int itrackX = incrButton2X + incrButton2W;
2206
int itrackW = decrButton2X - itrackX;
2207
this.trackRect.setBounds(itrackX, itemY, itrackW, itemH);
2209
int itrackX = decrButton2X + decrButton2W;
2210
int itrackW = incrButton2X - itrackX;
2211
this.trackRect.setBounds(itrackX, itemY, itrackW, itemH);
2215
* Make sure the thumb fits between the buttons. Note that setting the
2216
* thumbs bounds causes a repaint.
2218
if (thumbW >= (int) trackW) {
2219
this.setThumbBounds(0, 0, 0, 0);
2222
if (thumbX + thumbW > decrButton2X) {
2223
thumbX = decrButton2X - thumbW;
2225
if (thumbX < (incrButton2X + incrButton2W)) {
2226
thumbX = incrButton2X + incrButton2W + 1;
2229
if (thumbX + thumbW > incrButton2X) {
2230
thumbX = incrButton2X - thumbW;
2232
if (thumbX < (decrButton2X + decrButton2W)) {
2233
thumbX = decrButton2X + decrButton2W + 1;
2236
this.setThumbBounds(thumbX, itemY, thumbW, itemH);
2241
* Returns the memory usage string.
2243
* @return The memory usage string.
2245
public static String getMemoryUsage() {
2246
StringBuffer sb = new StringBuffer();
2247
sb.append("SubstanceScrollBarUI: \n");
2248
sb.append("\t" + thumbHorizontalMap.size() + " thumb horizontal, "
2249
+ thumbVerticalMap.size() + " thumb vertical");
2250
sb.append("\t" + trackHorizontalMap.size() + " track horizontal, "
2251
+ trackVerticalMap.size() + " track vertical");
2252
return sb.toString();
2258
* @see javax.swing.plaf.basic.BasicScrollBarUI#createTrackListener()
2261
protected TrackListener createTrackListener() {
2262
return new SubstanceTrackListener();
2266
* Track mouse drags. Had to take this one from BasicScrollBarUI since the
2267
* setValueForm method is private.
2269
protected class SubstanceTrackListener extends TrackListener {
2271
* Current scroll direction.
2273
private transient int direction = +1;
2279
* javax.swing.plaf.basic.BasicScrollBarUI$TrackListener#mouseReleased
2280
* (java.awt.event.MouseEvent)
2283
public void mouseReleased(MouseEvent e) {
2284
if (SubstanceScrollBarUI.this.isDragging) {
2285
SubstanceScrollBarUI.this.updateThumbState(e.getX(), e.getY());
2287
if (SwingUtilities.isRightMouseButton(e)
2288
|| (!SubstanceScrollBarUI.this
2289
.getSupportsAbsolutePositioning() && SwingUtilities
2290
.isMiddleMouseButton(e)))
2292
if (!SubstanceScrollBarUI.this.scrollbar.isEnabled())
2295
Rectangle r = SubstanceScrollBarUI.this.getTrackBounds();
2296
SubstanceScrollBarUI.this.scrollbar.repaint(r.x, r.y, r.width,
2299
SubstanceScrollBarUI.this.trackHighlight = NO_HIGHLIGHT;
2300
SubstanceScrollBarUI.this.isDragging = false;
2302
SubstanceScrollBarUI.this.scrollTimer.stop();
2303
SubstanceScrollBarUI.this.scrollbar.setValueIsAdjusting(false);
2310
* javax.swing.plaf.basic.BasicScrollBarUI$TrackListener#mousePressed
2311
* (java.awt.event.MouseEvent)
2314
public void mousePressed(MouseEvent e) {
2315
// If the mouse is pressed above the "thumb" component then reduce
2316
// the scrollbars value by one page ("page up"), otherwise increase
2317
// it by one page. If there is no thumb then page up if the mouse is
2318
// in the upper half of the track.
2319
if (SwingUtilities.isRightMouseButton(e)
2320
|| (!SubstanceScrollBarUI.this
2321
.getSupportsAbsolutePositioning() && SwingUtilities
2322
.isMiddleMouseButton(e)))
2324
if (!SubstanceScrollBarUI.this.scrollbar.isEnabled())
2327
if (!SubstanceScrollBarUI.this.scrollbar.hasFocus()
2328
&& SubstanceScrollBarUI.this.scrollbar
2329
.isRequestFocusEnabled()) {
2330
SubstanceScrollBarUI.this.scrollbar.requestFocus();
2333
SubstanceScrollBarUI.this.scrollbar.setValueIsAdjusting(true);
2335
this.currentMouseX = e.getX();
2336
this.currentMouseY = e.getY();
2338
// Clicked in the Thumb area?
2339
if (SubstanceScrollBarUI.this.getThumbBounds().contains(
2340
this.currentMouseX, this.currentMouseY)) {
2341
switch (SubstanceScrollBarUI.this.scrollbar.getOrientation()) {
2342
case JScrollBar.VERTICAL:
2343
this.offset = this.currentMouseY
2344
- SubstanceScrollBarUI.this.getThumbBounds().y;
2346
case JScrollBar.HORIZONTAL:
2347
this.offset = this.currentMouseX
2348
- SubstanceScrollBarUI.this.getThumbBounds().x;
2351
SubstanceScrollBarUI.this.isDragging = true;
2353
} else if (SubstanceScrollBarUI.this
2354
.getSupportsAbsolutePositioning()
2355
&& SwingUtilities.isMiddleMouseButton(e)) {
2356
switch (SubstanceScrollBarUI.this.scrollbar.getOrientation()) {
2357
case JScrollBar.VERTICAL:
2358
this.offset = SubstanceScrollBarUI.this.getThumbBounds().height / 2;
2360
case JScrollBar.HORIZONTAL:
2361
this.offset = SubstanceScrollBarUI.this.getThumbBounds().width / 2;
2364
SubstanceScrollBarUI.this.isDragging = true;
2365
this.setValueFrom(e);
2368
SubstanceScrollBarUI.this.isDragging = false;
2370
Dimension sbSize = SubstanceScrollBarUI.this.scrollbar.getSize();
2371
this.direction = +1;
2373
switch (SubstanceScrollBarUI.this.scrollbar.getOrientation()) {
2374
case JScrollBar.VERTICAL:
2375
if (SubstanceScrollBarUI.this.getThumbBounds().isEmpty()) {
2376
int scrollbarCenter = sbSize.height / 2;
2377
this.direction = (this.currentMouseY < scrollbarCenter) ? -1
2380
int thumbY = SubstanceScrollBarUI.this.getThumbBounds().y;
2381
this.direction = (this.currentMouseY < thumbY) ? -1 : +1;
2384
case JScrollBar.HORIZONTAL:
2385
if (SubstanceScrollBarUI.this.getThumbBounds().isEmpty()) {
2386
int scrollbarCenter = sbSize.width / 2;
2387
this.direction = (this.currentMouseX < scrollbarCenter) ? -1
2390
int thumbX = SubstanceScrollBarUI.this.getThumbBounds().x;
2391
this.direction = (this.currentMouseX < thumbX) ? -1 : +1;
2393
if (!SubstanceScrollBarUI.this.scrollbar
2394
.getComponentOrientation().isLeftToRight()) {
2395
this.direction = -this.direction;
2399
SubstanceScrollBarUI.this.scrollByBlock(this.direction);
2401
SubstanceScrollBarUI.this.scrollTimer.stop();
2402
SubstanceScrollBarUI.this.scrollListener
2403
.setDirection(this.direction);
2404
SubstanceScrollBarUI.this.scrollListener.setScrollByBlock(true);
2405
this.startScrollTimerIfNecessary();
2412
* javax.swing.plaf.basic.BasicScrollBarUI$TrackListener#mouseDragged
2413
* (java.awt.event.MouseEvent)
2416
public void mouseDragged(MouseEvent e) {
2417
// Set the models value to the position of the thumb's top of
2418
// Vertical scrollbar, or the left/right of Horizontal scrollbar in
2419
// LTR / RTL scrollbar relative to the origin of
2421
if (SwingUtilities.isRightMouseButton(e)
2422
|| (!SubstanceScrollBarUI.this
2423
.getSupportsAbsolutePositioning() && SwingUtilities
2424
.isMiddleMouseButton(e)))
2426
if (!SubstanceScrollBarUI.this.scrollbar.isEnabled()
2427
|| SubstanceScrollBarUI.this.getThumbBounds().isEmpty()) {
2430
if (SubstanceScrollBarUI.this.isDragging) {
2431
this.setValueFrom(e);
2433
this.currentMouseX = e.getX();
2434
this.currentMouseY = e.getY();
2435
SubstanceScrollBarUI.this.updateThumbState(this.currentMouseX,
2436
this.currentMouseY);
2437
this.startScrollTimerIfNecessary();
2442
* Sets the scrollbar value based on the specified mouse event.
2447
private void setValueFrom(MouseEvent e) {
2448
boolean active = SubstanceScrollBarUI.this.isThumbRollover();
2449
BoundedRangeModel model = SubstanceScrollBarUI.this.scrollbar
2451
Rectangle thumbR = SubstanceScrollBarUI.this.getThumbBounds();
2452
int thumbMin = 0, thumbMax = 0, thumbPos;
2454
ScrollPaneButtonPolicyKind buttonPolicy = SubstanceCoreUtilities
2455
.getScrollPaneButtonsPolicyKind(SubstanceScrollBarUI.this.scrollbar);
2457
if (SubstanceScrollBarUI.this.scrollbar.getOrientation() == JScrollBar.VERTICAL) {
2458
switch (buttonPolicy) {
2460
thumbMin = SubstanceScrollBarUI.this.decrButton.getY()
2461
+ SubstanceScrollBarUI.this.decrButton.getHeight();
2462
thumbMax = SubstanceScrollBarUI.this.incrButton.getY()
2467
thumbMax = SubstanceScrollBarUI.this.mySecondDecreaseButton
2473
thumbMax = SubstanceScrollBarUI.this.scrollbar.getSize().height
2474
- SubstanceScrollBarUI.this.scrollbar.getInsets().bottom
2478
thumbMin = SubstanceScrollBarUI.this.decrButton.getY()
2479
+ SubstanceScrollBarUI.this.decrButton.getHeight();
2480
thumbMax = SubstanceScrollBarUI.this.mySecondDecreaseButton
2485
thumbMin = SubstanceScrollBarUI.this.mySecondIncreaseButton
2487
+ SubstanceScrollBarUI.this.mySecondIncreaseButton
2489
thumbMax = SubstanceScrollBarUI.this.mySecondDecreaseButton
2495
thumbPos = Math.min(thumbMax, Math.max(thumbMin,
2496
(e.getY() - this.offset)));
2497
SubstanceScrollBarUI.this.setThumbBounds(thumbR.x, thumbPos,
2498
thumbR.width, thumbR.height);
2500
if (SubstanceScrollBarUI.this.scrollbar
2501
.getComponentOrientation().isLeftToRight()) {
2502
switch (buttonPolicy) {
2504
thumbMin = SubstanceScrollBarUI.this.decrButton.getX()
2505
+ SubstanceScrollBarUI.this.decrButton
2507
thumbMax = SubstanceScrollBarUI.this.incrButton.getX()
2512
thumbMax = SubstanceScrollBarUI.this.mySecondDecreaseButton
2517
thumbMin = SubstanceScrollBarUI.this.decrButton.getX()
2518
+ SubstanceScrollBarUI.this.decrButton
2520
thumbMax = SubstanceScrollBarUI.this.mySecondDecreaseButton
2525
thumbMin = SubstanceScrollBarUI.this.mySecondIncreaseButton
2527
+ SubstanceScrollBarUI.this.mySecondIncreaseButton
2529
thumbMax = SubstanceScrollBarUI.this.mySecondDecreaseButton
2535
thumbMax = SubstanceScrollBarUI.this.scrollbar
2537
- SubstanceScrollBarUI.this.scrollbar
2538
.getInsets().right - thumbR.width;
2542
switch (buttonPolicy) {
2544
thumbMin = SubstanceScrollBarUI.this.incrButton.getX()
2545
+ SubstanceScrollBarUI.this.incrButton
2547
thumbMax = SubstanceScrollBarUI.this.decrButton.getX()
2551
thumbMin = SubstanceScrollBarUI.this.mySecondDecreaseButton
2553
+ SubstanceScrollBarUI.this.mySecondDecreaseButton
2555
thumbMax = SubstanceScrollBarUI.this.scrollbar
2557
- SubstanceScrollBarUI.this.scrollbar
2558
.getInsets().right - thumbR.width;
2561
thumbMin = SubstanceScrollBarUI.this.mySecondDecreaseButton
2563
+ SubstanceScrollBarUI.this.mySecondDecreaseButton
2565
thumbMax = SubstanceScrollBarUI.this.decrButton.getX()
2569
thumbMin = SubstanceScrollBarUI.this.mySecondDecreaseButton
2571
+ SubstanceScrollBarUI.this.mySecondDecreaseButton
2573
thumbMax = SubstanceScrollBarUI.this.mySecondIncreaseButton
2579
thumbMax = SubstanceScrollBarUI.this.scrollbar
2581
- SubstanceScrollBarUI.this.scrollbar
2582
.getInsets().right - thumbR.width;
2586
// System.out.println(thumbMin + " : " + thumbMax + " : "
2587
// + (e.getX() - offset));
2588
thumbPos = Math.min(thumbMax, Math.max(thumbMin,
2589
(e.getX() - this.offset)));
2590
SubstanceScrollBarUI.this.setThumbBounds(thumbPos, thumbR.y,
2591
thumbR.width, thumbR.height);
2595
* Set the scrollbars value. If the thumb has reached the end of the
2596
* scrollbar, then just set the value to its maximum. Otherwise
2597
* compute the value as accurately as possible.
2599
if (thumbPos == thumbMax) {
2600
if (SubstanceScrollBarUI.this.scrollbar.getOrientation() == JScrollBar.VERTICAL
2601
|| SubstanceScrollBarUI.this.scrollbar
2602
.getComponentOrientation().isLeftToRight()) {
2603
SubstanceScrollBarUI.this.scrollbar.setValue(model
2605
- model.getExtent());
2607
SubstanceScrollBarUI.this.scrollbar.setValue(model
2611
float valueMax = model.getMaximum() - model.getExtent();
2612
float valueRange = valueMax - model.getMinimum();
2613
float thumbValue = thumbPos - thumbMin;
2614
float thumbRange = thumbMax - thumbMin;
2616
if (SubstanceScrollBarUI.this.scrollbar.getOrientation() == JScrollBar.VERTICAL
2617
|| SubstanceScrollBarUI.this.scrollbar
2618
.getComponentOrientation().isLeftToRight()) {
2619
value = (int) (0.5 + ((thumbValue / thumbRange) * valueRange));
2621
value = (int) (0.5 + (((thumbMax - thumbPos) / thumbRange) * valueRange));
2624
SubstanceScrollBarUI.this.scrollbar.setValue(value
2625
+ model.getMinimum());
2627
SubstanceScrollBarUI.this.setThumbRollover(active);
2631
* If necessary, starts the scroll timer.
2633
private void startScrollTimerIfNecessary() {
2634
if (SubstanceScrollBarUI.this.scrollTimer.isRunning()) {
2637
switch (SubstanceScrollBarUI.this.scrollbar.getOrientation()) {
2638
case JScrollBar.VERTICAL:
2639
if (this.direction > 0) {
2640
if (SubstanceScrollBarUI.this.getThumbBounds().y
2641
+ SubstanceScrollBarUI.this.getThumbBounds().height < ((SubstanceTrackListener) SubstanceScrollBarUI.this.trackListener).currentMouseY) {
2642
SubstanceScrollBarUI.this.scrollTimer.start();
2644
} else if (SubstanceScrollBarUI.this.getThumbBounds().y > ((SubstanceTrackListener) SubstanceScrollBarUI.this.trackListener).currentMouseY) {
2645
SubstanceScrollBarUI.this.scrollTimer.start();
2648
case JScrollBar.HORIZONTAL:
2649
if (this.direction > 0) {
2650
if (SubstanceScrollBarUI.this.getThumbBounds().x
2651
+ SubstanceScrollBarUI.this.getThumbBounds().width < ((SubstanceTrackListener) SubstanceScrollBarUI.this.trackListener).currentMouseX) {
2652
SubstanceScrollBarUI.this.scrollTimer.start();
2654
} else if (SubstanceScrollBarUI.this.getThumbBounds().x > ((SubstanceTrackListener) SubstanceScrollBarUI.this.trackListener).currentMouseX) {
2655
SubstanceScrollBarUI.this.scrollTimer.start();
2665
* javax.swing.plaf.basic.BasicScrollBarUI$TrackListener#mouseMoved(
2666
* java.awt.event.MouseEvent)
2669
public void mouseMoved(MouseEvent e) {
2670
if (!SubstanceScrollBarUI.this.isDragging) {
2671
SubstanceScrollBarUI.this.updateThumbState(e.getX(), e.getY());
2679
* javax.swing.plaf.basic.BasicScrollBarUI$TrackListener#mouseExited
2680
* (java.awt.event.MouseEvent)
2683
public void mouseExited(MouseEvent e) {
2684
if (!SubstanceScrollBarUI.this.isDragging) {
2685
SubstanceScrollBarUI.this.setThumbRollover(false);
2693
* @see javax.swing.plaf.basic.BasicScrollBarUI#createArrowButtonListener()
2696
protected ArrowButtonListener createArrowButtonListener() {
2697
return new SubstanceArrowButtonListener();
2701
* Listener on arrow buttons. Need to override the super implementation for
2702
* the {@link ScrollPaneButtonPolicyKind#MULTIPLE_BOTH} policy.
2704
* @author Kirill Grouchnikov
2706
protected class SubstanceArrowButtonListener extends ArrowButtonListener {
2708
* Because we are handling both mousePressed and Actions we need to make
2709
* sure we don't fire under both conditions. (keyfocus on scrollbars
2710
* causes action without mousePress
2712
boolean handledEvent;
2718
* javax.swing.plaf.basic.BasicScrollBarUI$ArrowButtonListener#mousePressed
2719
* (java.awt.event.MouseEvent)
2722
public void mousePressed(MouseEvent e) {
2723
if (!SubstanceScrollBarUI.this.scrollbar.isEnabled()) {
2726
// not an unmodified left mouse button
2727
// if(e.getModifiers() != InputEvent.BUTTON1_MASK) {return; }
2728
if (!SwingUtilities.isLeftMouseButton(e)) {
2732
int direction = ((e.getSource() == SubstanceScrollBarUI.this.incrButton) || (e
2733
.getSource() == SubstanceScrollBarUI.this.mySecondIncreaseButton)) ? 1
2736
SubstanceScrollBarUI.this.scrollByUnit(direction);
2737
SubstanceScrollBarUI.this.scrollTimer.stop();
2738
SubstanceScrollBarUI.this.scrollListener.setDirection(direction);
2739
SubstanceScrollBarUI.this.scrollListener.setScrollByBlock(false);
2740
SubstanceScrollBarUI.this.scrollTimer.start();
2742
this.handledEvent = true;
2743
if (!SubstanceScrollBarUI.this.scrollbar.hasFocus()
2744
&& SubstanceScrollBarUI.this.scrollbar
2745
.isRequestFocusEnabled()) {
2746
SubstanceScrollBarUI.this.scrollbar.requestFocus();
2754
* javax.swing.plaf.basic.BasicScrollBarUI$ArrowButtonListener#mouseReleased
2755
* (java.awt.event.MouseEvent)
2758
public void mouseReleased(MouseEvent e) {
2759
SubstanceScrollBarUI.this.scrollTimer.stop();
2760
this.handledEvent = false;
2761
SubstanceScrollBarUI.this.scrollbar.setValueIsAdjusting(false);
2766
* Updates the thumb state based on the coordinates.
2773
private void updateThumbState(int x, int y) {
2774
Rectangle rect = this.getThumbBounds();
2776
this.setThumbRollover(rect.contains(x, y));
2783
* javax.swing.plaf.basic.BasicScrollBarUI#getPreferredSize(javax.swing.
2787
public Dimension getPreferredSize(JComponent c) {
2788
if (scrollbar.getOrientation() == JScrollBar.VERTICAL) {
2789
return new Dimension(scrollBarWidth, Math.max(48,
2790
5 * scrollBarWidth));
2792
return new Dimension(Math.max(48, 5 * scrollBarWidth),
2798
* Composite button model that tracks changes to one primary and any number
2799
* of secondary button models for composite rollover effects. This model can
2800
* be used to "simulate" rollover effects on the primary component when the
2801
* actual rollover happens on one of the secondary components. An example is
2802
* a scroll bar. When the mouse enters one of the scroll buttons, the scroll
2803
* track is highlighted as well.
2805
* @author Kirill Grouchnikov
2807
private class CompositeButtonModel extends DefaultButtonModel {
2809
* The primary model.
2811
protected ButtonModel primaryModel;
2814
* The secondary models.
2816
protected ButtonModel[] secondaryModels;
2818
protected ChangeListener listener;
2821
* Creates a new composite button model.
2823
* @param primaryModel
2824
* The primary model.
2825
* @param secondaryButtons
2826
* The secondary buttons.
2828
public CompositeButtonModel(ButtonModel primaryModel,
2829
AbstractButton... secondaryButtons) {
2830
this.primaryModel = primaryModel;
2831
List<ButtonModel> bmList = new LinkedList<ButtonModel>();
2832
for (AbstractButton secondary : secondaryButtons) {
2833
if (secondary != null) {
2834
bmList.add(secondary.getModel());
2837
this.secondaryModels = bmList.toArray(new ButtonModel[0]);
2839
this.listener = new ChangeListener() {
2841
public void stateChanged(ChangeEvent e) {
2848
private void syncModels() {
2849
this.setEnabled(this.primaryModel.isEnabled());
2850
this.setSelected(this.primaryModel.isSelected());
2852
boolean isArmed = this.primaryModel.isArmed();
2853
for (ButtonModel secondary : this.secondaryModels) {
2854
isArmed = isArmed || secondary.isArmed();
2856
this.setArmed(isArmed);
2858
boolean isPressed = this.primaryModel.isPressed();
2859
for (ButtonModel secondary : this.secondaryModels) {
2860
isPressed = isPressed || secondary.isPressed();
2862
this.setPressed(isPressed);
2864
boolean isRollover = this.primaryModel.isRollover();
2865
for (ButtonModel secondary : this.secondaryModels) {
2866
isRollover = isRollover || secondary.isRollover();
2868
this.setRollover(isRollover);
2871
public void registerListeners() {
2872
this.primaryModel.addChangeListener(this.listener);
2873
for (ButtonModel secondary : this.secondaryModels) {
2874
secondary.addChangeListener(this.listener);
2878
public void unregisterListeners() {
2879
this.primaryModel.removeChangeListener(this.listener);
2880
for (ButtonModel secondary : this.secondaryModels) {
2881
secondary.removeChangeListener(this.listener);
2883
this.listener = null;
b'\\ No newline at end of file'