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     protected 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 }