1/*
2 * Copyright (c) 1998, 2014, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package javax.swing.plaf.metal;
27
28import java.awt.Color;
29import java.awt.Dimension;
30import java.awt.Graphics;
31import java.awt.Rectangle;
32import java.beans.PropertyChangeEvent;
33import java.beans.PropertyChangeListener;
34
35import javax.swing.JButton;
36import javax.swing.JComponent;
37import javax.swing.JScrollBar;
38import javax.swing.UIManager;
39import javax.swing.plaf.ComponentUI;
40import javax.swing.plaf.basic.BasicScrollBarUI;
41
42import static sun.swing.SwingUtilities2.drawHLine;
43import static sun.swing.SwingUtilities2.drawRect;
44import static sun.swing.SwingUtilities2.drawVLine;
45
46
47/**
48 * Implementation of ScrollBarUI for the Metal Look and Feel
49 *
50 * @author Tom Santos
51 * @author Steve Wilson
52 */
53public class MetalScrollBarUI extends BasicScrollBarUI
54{
55    private static Color shadowColor;
56    private static Color highlightColor;
57    private static Color darkShadowColor;
58    private static Color thumbColor;
59    private static Color thumbShadow;
60    private static Color thumbHighlightColor;
61
62    /**
63     * The metal bumps.
64     */
65    private MetalBumps bumps;
66
67    /**
68     * The increase button.
69     */
70    protected MetalScrollButton increaseButton;
71
72    /**
73     * The decrease button.
74     */
75    protected MetalScrollButton decreaseButton;
76
77    /**
78     * The width of the scroll bar.
79     */
80    protected  int scrollBarWidth;
81
82    /**
83     * The property {@code JScrollBar.isFreeStanding}.
84     */
85    public static final String FREE_STANDING_PROP = "JScrollBar.isFreeStanding";
86
87    /**
88     * The value of the property {@code JScrollBar.isFreeStanding}.
89     */
90    protected boolean isFreeStanding = true;
91
92    /**
93     * Constructs a new {@code MetalScrollBarUI} instance.
94     *
95     * @param c a component
96     * @return a new {@code MetalScrollBarUI} instance
97     */
98    public static ComponentUI createUI( JComponent c )
99    {
100        return new MetalScrollBarUI();
101    }
102
103    protected void installDefaults() {
104        scrollBarWidth = ((Integer)(UIManager.get( "ScrollBar.width" ))).intValue();
105        super.installDefaults();
106        bumps = new MetalBumps( 10, 10, thumbHighlightColor, thumbShadow, thumbColor );
107    }
108
109    protected void installListeners(){
110        super.installListeners();
111        ((ScrollBarListener)propertyChangeListener).handlePropertyChange( scrollbar.getClientProperty( FREE_STANDING_PROP ) );
112    }
113
114    protected PropertyChangeListener createPropertyChangeListener(){
115        return new ScrollBarListener();
116    }
117
118    protected void configureScrollBarColors()
119    {
120        super.configureScrollBarColors();
121        shadowColor         = UIManager.getColor("ScrollBar.shadow");
122        highlightColor      = UIManager.getColor("ScrollBar.highlight");
123        darkShadowColor     = UIManager.getColor("ScrollBar.darkShadow");
124        thumbColor          = UIManager.getColor("ScrollBar.thumb");
125        thumbShadow         = UIManager.getColor("ScrollBar.thumbShadow");
126        thumbHighlightColor = UIManager.getColor("ScrollBar.thumbHighlight");
127
128
129    }
130
131    public Dimension getPreferredSize( JComponent c )
132    {
133        if ( scrollbar.getOrientation() == JScrollBar.VERTICAL )
134        {
135            return new Dimension( scrollBarWidth, scrollBarWidth * 3 + 10 );
136        }
137        else  // Horizontal
138        {
139            return new Dimension( scrollBarWidth * 3 + 10, scrollBarWidth );
140        }
141
142    }
143
144    /** Returns the view that represents the decrease view.
145      */
146    protected JButton createDecreaseButton( int orientation )
147    {
148        decreaseButton = new MetalScrollButton( orientation, scrollBarWidth, isFreeStanding );
149        return decreaseButton;
150    }
151
152    /** Returns the view that represents the increase view. */
153    protected JButton createIncreaseButton( int orientation )
154    {
155        increaseButton =  new MetalScrollButton( orientation, scrollBarWidth, isFreeStanding );
156        return increaseButton;
157    }
158
159    protected void paintTrack( Graphics g, JComponent c, Rectangle trackBounds )
160    {
161        g.translate( trackBounds.x, trackBounds.y );
162
163        boolean leftToRight = MetalUtils.isLeftToRight(c);
164
165        if ( scrollbar.getOrientation() == JScrollBar.VERTICAL )
166        {
167            if ( !isFreeStanding ) {
168                trackBounds.width += 2;
169                if ( !leftToRight ) {
170                    g.translate( -1, 0 );
171                }
172            }
173
174            if ( c.isEnabled() ) {
175                g.setColor( darkShadowColor );
176                drawVLine(g, 0, 0, trackBounds.height - 1);
177                drawVLine(g, trackBounds.width - 2, 0, trackBounds.height - 1);
178                drawHLine(g, 2, trackBounds.width - 1, trackBounds.height - 1);
179                drawHLine(g, 2, trackBounds.width - 2, 0);
180
181                g.setColor( shadowColor );
182                //      g.setColor( Color.red);
183                drawVLine(g, 1, 1, trackBounds.height - 2);
184                drawHLine(g, 1, trackBounds.width - 3, 1);
185                if (scrollbar.getValue() != scrollbar.getMaximum()) {  // thumb shadow
186                    int y = thumbRect.y + thumbRect.height - trackBounds.y;
187                    drawHLine(g, 1, trackBounds.width - 1, y);
188                }
189                g.setColor(highlightColor);
190                drawVLine(g, trackBounds.width - 1, 0, trackBounds.height - 1);
191            } else {
192                MetalUtils.drawDisabledBorder(g, 0, 0, trackBounds.width, trackBounds.height );
193            }
194
195            if ( !isFreeStanding ) {
196                trackBounds.width -= 2;
197                if ( !leftToRight ) {
198                    g.translate( 1, 0 );
199                }
200            }
201        }
202        else  // HORIZONTAL
203        {
204            if ( !isFreeStanding ) {
205                trackBounds.height += 2;
206            }
207
208            if ( c.isEnabled() ) {
209                g.setColor( darkShadowColor );
210                drawHLine(g, 0, trackBounds.width - 1, 0);  // top
211                drawVLine(g, 0, 2, trackBounds.height - 2); // left
212                drawHLine(g, 0, trackBounds.width - 1, trackBounds.height - 2 ); // bottom
213                drawVLine(g, trackBounds.width - 1, 2,  trackBounds.height - 1 ); // right
214
215                g.setColor( shadowColor );
216                //      g.setColor( Color.red);
217                drawHLine(g, 1, trackBounds.width - 2, 1 );  // top
218                drawVLine(g, 1, 1, trackBounds.height - 3 ); // left
219                drawHLine(g, 0, trackBounds.width - 1, trackBounds.height - 1 ); // bottom
220                if (scrollbar.getValue() != scrollbar.getMaximum()) {  // thumb shadow
221                    int x = thumbRect.x + thumbRect.width - trackBounds.x;
222                    drawVLine(g, x, 1, trackBounds.height-1);
223                }
224            } else {
225                MetalUtils.drawDisabledBorder(g, 0, 0, trackBounds.width, trackBounds.height );
226            }
227
228            if ( !isFreeStanding ) {
229                trackBounds.height -= 2;
230            }
231        }
232
233        g.translate( -trackBounds.x, -trackBounds.y );
234    }
235
236    protected void paintThumb( Graphics g, JComponent c, Rectangle thumbBounds )
237    {
238        if (!c.isEnabled()) {
239            return;
240        }
241
242        if (MetalLookAndFeel.usingOcean()) {
243            oceanPaintThumb(g, c, thumbBounds);
244            return;
245        }
246
247        boolean leftToRight = MetalUtils.isLeftToRight(c);
248
249        g.translate( thumbBounds.x, thumbBounds.y );
250
251        if ( scrollbar.getOrientation() == JScrollBar.VERTICAL )
252        {
253            if ( !isFreeStanding ) {
254                thumbBounds.width += 2;
255                if ( !leftToRight ) {
256                    g.translate( -1, 0 );
257                }
258            }
259
260            g.setColor( thumbColor );
261            g.fillRect( 0, 0, thumbBounds.width - 2, thumbBounds.height - 1 );
262
263            g.setColor( thumbShadow );
264            drawRect(g, 0, 0, thumbBounds.width - 2, thumbBounds.height - 1);
265
266            g.setColor( thumbHighlightColor );
267            drawHLine(g, 1, thumbBounds.width - 3, 1);
268            drawVLine(g, 1, 1, thumbBounds.height - 2);
269
270            bumps.setBumpArea( thumbBounds.width - 6, thumbBounds.height - 7 );
271            bumps.paintIcon( c, g, 3, 4 );
272
273            if ( !isFreeStanding ) {
274                thumbBounds.width -= 2;
275                if ( !leftToRight ) {
276                    g.translate( 1, 0 );
277                }
278            }
279        }
280        else  // HORIZONTAL
281        {
282            if ( !isFreeStanding ) {
283                thumbBounds.height += 2;
284            }
285
286            g.setColor( thumbColor );
287            g.fillRect( 0, 0, thumbBounds.width - 1, thumbBounds.height - 2 );
288
289            g.setColor( thumbShadow );
290            drawRect(g, 0, 0, thumbBounds.width - 1, thumbBounds.height - 2);
291
292            g.setColor( thumbHighlightColor );
293            drawHLine(g, 1, thumbBounds.width - 3, 1);
294            drawVLine(g, 1, 1, thumbBounds.height - 3);
295
296            bumps.setBumpArea( thumbBounds.width - 7, thumbBounds.height - 6 );
297            bumps.paintIcon( c, g, 4, 3 );
298
299            if ( !isFreeStanding ) {
300                thumbBounds.height -= 2;
301            }
302        }
303
304        g.translate( -thumbBounds.x, -thumbBounds.y );
305    }
306
307    private void oceanPaintThumb(Graphics g, JComponent c,
308                                   Rectangle thumbBounds) {
309        boolean leftToRight = MetalUtils.isLeftToRight(c);
310
311        g.translate(thumbBounds.x, thumbBounds.y);
312
313        if (scrollbar.getOrientation() == JScrollBar.VERTICAL) {
314            if (!isFreeStanding) {
315                thumbBounds.width += 2;
316                if (!leftToRight) {
317                    g.translate(-1, 0);
318                }
319            }
320
321            if (thumbColor != null) {
322                g.setColor(thumbColor);
323                g.fillRect(0, 0, thumbBounds.width - 2,thumbBounds.height - 1);
324            }
325
326            g.setColor(thumbShadow);
327            drawRect(g, 0, 0, thumbBounds.width - 2, thumbBounds.height - 1);
328
329            g.setColor(thumbHighlightColor);
330            drawHLine(g, 1, thumbBounds.width - 3, 1);
331            drawVLine(g, 1, 1, thumbBounds.height - 2);
332
333            MetalUtils.drawGradient(c, g, "ScrollBar.gradient", 2, 2,
334                                    thumbBounds.width - 4,
335                                    thumbBounds.height - 3, false);
336
337            int gripSize = thumbBounds.width - 8;
338            if (gripSize > 2 && thumbBounds.height >= 10) {
339                g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow());
340                int gripY = thumbBounds.height / 2 - 2;
341                for (int counter = 0; counter < 6; counter += 2) {
342                    g.fillRect(4, counter + gripY, gripSize, 1);
343                }
344
345                g.setColor(MetalLookAndFeel.getWhite());
346                gripY++;
347                for (int counter = 0; counter < 6; counter += 2) {
348                    g.fillRect(5, counter + gripY, gripSize, 1);
349                }
350            }
351            if (!isFreeStanding) {
352                thumbBounds.width -= 2;
353                if (!leftToRight) {
354                    g.translate(1, 0);
355                }
356            }
357        }
358        else { // HORIZONTAL
359            if (!isFreeStanding) {
360                thumbBounds.height += 2;
361            }
362
363            if (thumbColor != null) {
364                g.setColor(thumbColor);
365                g.fillRect(0, 0, thumbBounds.width - 1,thumbBounds.height - 2);
366            }
367
368            g.setColor(thumbShadow);
369            drawRect(g, 0, 0, thumbBounds.width - 1, thumbBounds.height - 2);
370
371            g.setColor(thumbHighlightColor);
372            drawHLine(g, 1, thumbBounds.width - 2, 1);
373            drawVLine(g, 1, 1, thumbBounds.height - 3);
374
375            MetalUtils.drawGradient(c, g, "ScrollBar.gradient", 2, 2,
376                                    thumbBounds.width - 3,
377                                    thumbBounds.height - 4, true);
378
379            int gripSize = thumbBounds.height - 8;
380            if (gripSize > 2 && thumbBounds.width >= 10) {
381                g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow());
382                int gripX = thumbBounds.width / 2 - 2;
383                for (int counter = 0; counter < 6; counter += 2) {
384                    g.fillRect(gripX + counter, 4, 1, gripSize);
385                }
386
387                g.setColor(MetalLookAndFeel.getWhite());
388                gripX++;
389                for (int counter = 0; counter < 6; counter += 2) {
390                    g.fillRect(gripX + counter, 5, 1, gripSize);
391                }
392            }
393
394            if (!isFreeStanding) {
395                thumbBounds.height -= 2;
396            }
397        }
398
399        g.translate( -thumbBounds.x, -thumbBounds.y );
400    }
401
402    protected Dimension getMinimumThumbSize()
403    {
404        return new Dimension( scrollBarWidth, scrollBarWidth );
405    }
406
407    /**
408      * This is overridden only to increase the invalid area.  This
409      * ensures that the "Shadow" below the thumb is invalidated
410      */
411    protected void setThumbBounds(int x, int y, int width, int height)
412    {
413        /* If the thumbs bounds haven't changed, we're done.
414         */
415        if ((thumbRect.x == x) &&
416            (thumbRect.y == y) &&
417            (thumbRect.width == width) &&
418            (thumbRect.height == height)) {
419            return;
420        }
421
422        /* Update thumbRect, and repaint the union of x,y,w,h and
423         * the old thumbRect.
424         */
425        int minX = Math.min(x, thumbRect.x);
426        int minY = Math.min(y, thumbRect.y);
427        int maxX = Math.max(x + width, thumbRect.x + thumbRect.width);
428        int maxY = Math.max(y + height, thumbRect.y + thumbRect.height);
429
430        thumbRect.setBounds(x, y, width, height);
431        scrollbar.repaint(minX, minY, (maxX - minX)+1, (maxY - minY)+1);
432    }
433
434
435
436    class ScrollBarListener extends BasicScrollBarUI.PropertyChangeHandler
437    {
438        public void propertyChange(PropertyChangeEvent e)
439        {
440            String name = e.getPropertyName();
441            if ( name.equals( FREE_STANDING_PROP ) )
442            {
443                handlePropertyChange( e.getNewValue() );
444            }
445            else {
446                super.propertyChange( e );
447            }
448        }
449
450        public void handlePropertyChange( Object newValue )
451        {
452            if ( newValue != null )
453            {
454                boolean temp = ((Boolean)newValue).booleanValue();
455                boolean becameFlush = temp == false && isFreeStanding == true;
456                boolean becameNormal = temp == true && isFreeStanding == false;
457
458                isFreeStanding = temp;
459
460                if ( becameFlush ) {
461                    toFlush();
462                }
463                else if ( becameNormal ) {
464                    toFreeStanding();
465                }
466            }
467            else
468            {
469
470                if ( !isFreeStanding ) {
471                    isFreeStanding = true;
472                    toFreeStanding();
473                }
474
475                // This commented-out block is used for testing flush scrollbars.
476/*
477                if ( isFreeStanding ) {
478                    isFreeStanding = false;
479                    toFlush();
480                }
481*/
482            }
483
484            if ( increaseButton != null )
485            {
486                increaseButton.setFreeStanding( isFreeStanding );
487            }
488            if ( decreaseButton != null )
489            {
490                decreaseButton.setFreeStanding( isFreeStanding );
491            }
492        }
493
494        protected void toFlush() {
495            scrollBarWidth -= 2;
496        }
497
498        protected void toFreeStanding() {
499            scrollBarWidth += 2;
500        }
501    } // end class ScrollBarListener
502}
503