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 
26 package javax.swing.plaf.metal;
27 
28 import java.awt.Color;
29 import java.awt.Dimension;
30 import java.awt.Graphics;
31 import java.awt.Rectangle;
32 import java.beans.PropertyChangeEvent;
33 import java.beans.PropertyChangeListener;
34 
35 import javax.swing.JButton;
36 import javax.swing.JComponent;
37 import javax.swing.JScrollBar;
38 import javax.swing.UIManager;
39 import javax.swing.plaf.ComponentUI;
40 import javax.swing.plaf.basic.BasicScrollBarUI;
41 
42 import static sun.swing.SwingUtilities2.drawHLine;
43 import static sun.swing.SwingUtilities2.drawRect;
44 import 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  */
53 public 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      */
createUI( JComponent c )98     public static ComponentUI createUI( JComponent c )
99     {
100         return new MetalScrollBarUI();
101     }
102 
installDefaults()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 
installListeners()109     protected void installListeners(){
110         super.installListeners();
111         ((ScrollBarListener)propertyChangeListener).handlePropertyChange( scrollbar.getClientProperty( FREE_STANDING_PROP ) );
112     }
113 
createPropertyChangeListener()114     protected PropertyChangeListener createPropertyChangeListener(){
115         return new ScrollBarListener();
116     }
117 
configureScrollBarColors()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 
getPreferredSize( JComponent c )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       */
createDecreaseButton( int orientation )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. */
createIncreaseButton( int orientation )153     protected JButton createIncreaseButton( int orientation )
154     {
155         increaseButton =  new MetalScrollButton( orientation, scrollBarWidth, isFreeStanding );
156         return increaseButton;
157     }
158 
paintTrack( Graphics g, JComponent c, Rectangle trackBounds )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 
paintThumb( Graphics g, JComponent c, Rectangle thumbBounds )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 
oceanPaintThumb(Graphics g, JComponent c, Rectangle thumbBounds)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 
getMinimumThumbSize()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       */
setThumbBounds(int x, int y, int width, int height)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     {
propertyChange(PropertyChangeEvent e)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 
handlePropertyChange( Object newValue )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 
toFlush()494         protected void toFlush() {
495             scrollBarWidth -= 2;
496         }
497 
toFreeStanding()498         protected void toFreeStanding() {
499             scrollBarWidth += 2;
500         }
501     } // end class ScrollBarListener
502 }
503