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     @SuppressWarnings("unexportedinapi")
  66     protected MetalBumps bumps;
  67 
  68     /**
  69      * The increase button.
  70      */
  71     protected MetalScrollButton increaseButton;
  72 
  73     /**
  74      * The decrease button.
  75      */
  76     protected MetalScrollButton decreaseButton;
  77 
  78     /**
  79      * The width of the scroll bar.
  80      */
  81     protected  int scrollBarWidth;
  82 
  83     /**
  84      * The property {@code JScrollBar.isFreeStanding}.
  85      */
  86     public static final String FREE_STANDING_PROP = "JScrollBar.isFreeStanding";
  87 
  88     /**
  89      * The value of the property {@code JScrollBar.isFreeStanding}.
  90      */
  91     protected boolean isFreeStanding = true;
  92 
  93     /**
  94      * Constructs a new {@code MetalScrollBarUI} instance.
  95      *
  96      * @param c a component
  97      * @return a new {@code MetalScrollBarUI} instance
  98      */
  99     public static ComponentUI createUI( JComponent c )
 100     {
 101         return new MetalScrollBarUI();
 102     }
 103 
 104     protected void installDefaults() {
 105         scrollBarWidth = ((Integer)(UIManager.get( "ScrollBar.width" ))).intValue();
 106         super.installDefaults();
 107         bumps = new MetalBumps( 10, 10, thumbHighlightColor, thumbShadow, thumbColor );
 108     }
 109 
 110     protected void installListeners(){
 111         super.installListeners();
 112         ((ScrollBarListener)propertyChangeListener).handlePropertyChange( scrollbar.getClientProperty( FREE_STANDING_PROP ) );
 113     }
 114 
 115     protected PropertyChangeListener createPropertyChangeListener(){
 116         return new ScrollBarListener();
 117     }
 118 
 119     protected void configureScrollBarColors()
 120     {
 121         super.configureScrollBarColors();
 122         shadowColor         = UIManager.getColor("ScrollBar.shadow");
 123         highlightColor      = UIManager.getColor("ScrollBar.highlight");
 124         darkShadowColor     = UIManager.getColor("ScrollBar.darkShadow");
 125         thumbColor          = UIManager.getColor("ScrollBar.thumb");
 126         thumbShadow         = UIManager.getColor("ScrollBar.thumbShadow");
 127         thumbHighlightColor = UIManager.getColor("ScrollBar.thumbHighlight");
 128 
 129 
 130     }
 131 
 132     public Dimension getPreferredSize( JComponent c )
 133     {
 134         if ( scrollbar.getOrientation() == JScrollBar.VERTICAL )
 135         {
 136             return new Dimension( scrollBarWidth, scrollBarWidth * 3 + 10 );
 137         }
 138         else  // Horizontal
 139         {
 140             return new Dimension( scrollBarWidth * 3 + 10, scrollBarWidth );
 141         }
 142 
 143     }
 144 
 145     /** Returns the view that represents the decrease view.
 146       */
 147     protected JButton createDecreaseButton( int orientation )
 148     {
 149         decreaseButton = new MetalScrollButton( orientation, scrollBarWidth, isFreeStanding );
 150         return decreaseButton;
 151     }
 152 
 153     /** Returns the view that represents the increase view. */
 154     protected JButton createIncreaseButton( int orientation )
 155     {
 156         increaseButton =  new MetalScrollButton( orientation, scrollBarWidth, isFreeStanding );
 157         return increaseButton;
 158     }
 159 
 160     protected void paintTrack( Graphics g, JComponent c, Rectangle trackBounds )
 161     {
 162         g.translate( trackBounds.x, trackBounds.y );
 163 
 164         boolean leftToRight = MetalUtils.isLeftToRight(c);
 165 
 166         if ( scrollbar.getOrientation() == JScrollBar.VERTICAL )
 167         {
 168             if ( !isFreeStanding ) {
 169                 trackBounds.width += 2;
 170                 if ( !leftToRight ) {
 171                     g.translate( -1, 0 );
 172                 }
 173             }
 174 
 175             if ( c.isEnabled() ) {
 176                 g.setColor( darkShadowColor );
 177                 drawVLine(g, 0, 0, trackBounds.height - 1);
 178                 drawVLine(g, trackBounds.width - 2, 0, trackBounds.height - 1);
 179                 drawHLine(g, 2, trackBounds.width - 1, trackBounds.height - 1);
 180                 drawHLine(g, 2, trackBounds.width - 2, 0);
 181 
 182                 g.setColor( shadowColor );
 183                 //      g.setColor( Color.red);
 184                 drawVLine(g, 1, 1, trackBounds.height - 2);
 185                 drawHLine(g, 1, trackBounds.width - 3, 1);
 186                 if (scrollbar.getValue() != scrollbar.getMaximum()) {  // thumb shadow
 187                     int y = thumbRect.y + thumbRect.height - trackBounds.y;
 188                     drawHLine(g, 1, trackBounds.width - 1, y);
 189                 }
 190                 g.setColor(highlightColor);
 191                 drawVLine(g, trackBounds.width - 1, 0, trackBounds.height - 1);
 192             } else {
 193                 MetalUtils.drawDisabledBorder(g, 0, 0, trackBounds.width, trackBounds.height );
 194             }
 195 
 196             if ( !isFreeStanding ) {
 197                 trackBounds.width -= 2;
 198                 if ( !leftToRight ) {
 199                     g.translate( 1, 0 );
 200                 }
 201             }
 202         }
 203         else  // HORIZONTAL
 204         {
 205             if ( !isFreeStanding ) {
 206                 trackBounds.height += 2;
 207             }
 208 
 209             if ( c.isEnabled() ) {
 210                 g.setColor( darkShadowColor );
 211                 drawHLine(g, 0, trackBounds.width - 1, 0);  // top
 212                 drawVLine(g, 0, 2, trackBounds.height - 2); // left
 213                 drawHLine(g, 0, trackBounds.width - 1, trackBounds.height - 2 ); // bottom
 214                 drawVLine(g, trackBounds.width - 1, 2,  trackBounds.height - 1 ); // right
 215 
 216                 g.setColor( shadowColor );
 217                 //      g.setColor( Color.red);
 218                 drawHLine(g, 1, trackBounds.width - 2, 1 );  // top
 219                 drawVLine(g, 1, 1, trackBounds.height - 3 ); // left
 220                 drawHLine(g, 0, trackBounds.width - 1, trackBounds.height - 1 ); // bottom
 221                 if (scrollbar.getValue() != scrollbar.getMaximum()) {  // thumb shadow
 222                     int x = thumbRect.x + thumbRect.width - trackBounds.x;
 223                     drawVLine(g, x, 1, trackBounds.height-1);
 224                 }
 225             } else {
 226                 MetalUtils.drawDisabledBorder(g, 0, 0, trackBounds.width, trackBounds.height );
 227             }
 228 
 229             if ( !isFreeStanding ) {
 230                 trackBounds.height -= 2;
 231             }
 232         }
 233 
 234         g.translate( -trackBounds.x, -trackBounds.y );
 235     }
 236 
 237     protected void paintThumb( Graphics g, JComponent c, Rectangle thumbBounds )
 238     {
 239         if (!c.isEnabled()) {
 240             return;
 241         }
 242 
 243         if (MetalLookAndFeel.usingOcean()) {
 244             oceanPaintThumb(g, c, thumbBounds);
 245             return;
 246         }
 247 
 248         boolean leftToRight = MetalUtils.isLeftToRight(c);
 249 
 250         g.translate( thumbBounds.x, thumbBounds.y );
 251 
 252         if ( scrollbar.getOrientation() == JScrollBar.VERTICAL )
 253         {
 254             if ( !isFreeStanding ) {
 255                 thumbBounds.width += 2;
 256                 if ( !leftToRight ) {
 257                     g.translate( -1, 0 );
 258                 }
 259             }
 260 
 261             g.setColor( thumbColor );
 262             g.fillRect( 0, 0, thumbBounds.width - 2, thumbBounds.height - 1 );
 263 
 264             g.setColor( thumbShadow );
 265             drawRect(g, 0, 0, thumbBounds.width - 2, thumbBounds.height - 1);
 266 
 267             g.setColor( thumbHighlightColor );
 268             drawHLine(g, 1, thumbBounds.width - 3, 1);
 269             drawVLine(g, 1, 1, thumbBounds.height - 2);
 270 
 271             bumps.setBumpArea( thumbBounds.width - 6, thumbBounds.height - 7 );
 272             bumps.paintIcon( c, g, 3, 4 );
 273 
 274             if ( !isFreeStanding ) {
 275                 thumbBounds.width -= 2;
 276                 if ( !leftToRight ) {
 277                     g.translate( 1, 0 );
 278                 }
 279             }
 280         }
 281         else  // HORIZONTAL
 282         {
 283             if ( !isFreeStanding ) {
 284                 thumbBounds.height += 2;
 285             }
 286 
 287             g.setColor( thumbColor );
 288             g.fillRect( 0, 0, thumbBounds.width - 1, thumbBounds.height - 2 );
 289 
 290             g.setColor( thumbShadow );
 291             drawRect(g, 0, 0, thumbBounds.width - 1, thumbBounds.height - 2);
 292 
 293             g.setColor( thumbHighlightColor );
 294             drawHLine(g, 1, thumbBounds.width - 3, 1);
 295             drawVLine(g, 1, 1, thumbBounds.height - 3);
 296 
 297             bumps.setBumpArea( thumbBounds.width - 7, thumbBounds.height - 6 );
 298             bumps.paintIcon( c, g, 4, 3 );
 299 
 300             if ( !isFreeStanding ) {
 301                 thumbBounds.height -= 2;
 302             }
 303         }
 304 
 305         g.translate( -thumbBounds.x, -thumbBounds.y );
 306     }
 307 
 308     private void oceanPaintThumb(Graphics g, JComponent c,
 309                                    Rectangle thumbBounds) {
 310         boolean leftToRight = MetalUtils.isLeftToRight(c);
 311 
 312         g.translate(thumbBounds.x, thumbBounds.y);
 313 
 314         if (scrollbar.getOrientation() == JScrollBar.VERTICAL) {
 315             if (!isFreeStanding) {
 316                 thumbBounds.width += 2;
 317                 if (!leftToRight) {
 318                     g.translate(-1, 0);
 319                 }
 320             }
 321 
 322             if (thumbColor != null) {
 323                 g.setColor(thumbColor);
 324                 g.fillRect(0, 0, thumbBounds.width - 2,thumbBounds.height - 1);
 325             }
 326 
 327             g.setColor(thumbShadow);
 328             drawRect(g, 0, 0, thumbBounds.width - 2, thumbBounds.height - 1);
 329 
 330             g.setColor(thumbHighlightColor);
 331             drawHLine(g, 1, thumbBounds.width - 3, 1);
 332             drawVLine(g, 1, 1, thumbBounds.height - 2);
 333 
 334             MetalUtils.drawGradient(c, g, "ScrollBar.gradient", 2, 2,
 335                                     thumbBounds.width - 4,
 336                                     thumbBounds.height - 3, false);
 337 
 338             int gripSize = thumbBounds.width - 8;
 339             if (gripSize > 2 && thumbBounds.height >= 10) {
 340                 g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow());
 341                 int gripY = thumbBounds.height / 2 - 2;
 342                 for (int counter = 0; counter < 6; counter += 2) {
 343                     g.fillRect(4, counter + gripY, gripSize, 1);
 344                 }
 345 
 346                 g.setColor(MetalLookAndFeel.getWhite());
 347                 gripY++;
 348                 for (int counter = 0; counter < 6; counter += 2) {
 349                     g.fillRect(5, counter + gripY, gripSize, 1);
 350                 }
 351             }
 352             if (!isFreeStanding) {
 353                 thumbBounds.width -= 2;
 354                 if (!leftToRight) {
 355                     g.translate(1, 0);
 356                 }
 357             }
 358         }
 359         else { // HORIZONTAL
 360             if (!isFreeStanding) {
 361                 thumbBounds.height += 2;
 362             }
 363 
 364             if (thumbColor != null) {
 365                 g.setColor(thumbColor);
 366                 g.fillRect(0, 0, thumbBounds.width - 1,thumbBounds.height - 2);
 367             }
 368 
 369             g.setColor(thumbShadow);
 370             drawRect(g, 0, 0, thumbBounds.width - 1, thumbBounds.height - 2);
 371 
 372             g.setColor(thumbHighlightColor);
 373             drawHLine(g, 1, thumbBounds.width - 2, 1);
 374             drawVLine(g, 1, 1, thumbBounds.height - 3);
 375 
 376             MetalUtils.drawGradient(c, g, "ScrollBar.gradient", 2, 2,
 377                                     thumbBounds.width - 3,
 378                                     thumbBounds.height - 4, true);
 379 
 380             int gripSize = thumbBounds.height - 8;
 381             if (gripSize > 2 && thumbBounds.width >= 10) {
 382                 g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow());
 383                 int gripX = thumbBounds.width / 2 - 2;
 384                 for (int counter = 0; counter < 6; counter += 2) {
 385                     g.fillRect(gripX + counter, 4, 1, gripSize);
 386                 }
 387 
 388                 g.setColor(MetalLookAndFeel.getWhite());
 389                 gripX++;
 390                 for (int counter = 0; counter < 6; counter += 2) {
 391                     g.fillRect(gripX + counter, 5, 1, gripSize);
 392                 }
 393             }
 394 
 395             if (!isFreeStanding) {
 396                 thumbBounds.height -= 2;
 397             }
 398         }
 399 
 400         g.translate( -thumbBounds.x, -thumbBounds.y );
 401     }
 402 
 403     protected Dimension getMinimumThumbSize()
 404     {
 405         return new Dimension( scrollBarWidth, scrollBarWidth );
 406     }
 407 
 408     /**
 409       * This is overridden only to increase the invalid area.  This
 410       * ensures that the "Shadow" below the thumb is invalidated
 411       */
 412     protected void setThumbBounds(int x, int y, int width, int height)
 413     {
 414         /* If the thumbs bounds haven't changed, we're done.
 415          */
 416         if ((thumbRect.x == x) &&
 417             (thumbRect.y == y) &&
 418             (thumbRect.width == width) &&
 419             (thumbRect.height == height)) {
 420             return;
 421         }
 422 
 423         /* Update thumbRect, and repaint the union of x,y,w,h and
 424          * the old thumbRect.
 425          */
 426         int minX = Math.min(x, thumbRect.x);
 427         int minY = Math.min(y, thumbRect.y);
 428         int maxX = Math.max(x + width, thumbRect.x + thumbRect.width);
 429         int maxY = Math.max(y + height, thumbRect.y + thumbRect.height);
 430 
 431         thumbRect.setBounds(x, y, width, height);
 432         scrollbar.repaint(minX, minY, (maxX - minX)+1, (maxY - minY)+1);
 433     }
 434 
 435 
 436 
 437     class ScrollBarListener extends BasicScrollBarUI.PropertyChangeHandler
 438     {
 439         public void propertyChange(PropertyChangeEvent e)
 440         {
 441             String name = e.getPropertyName();
 442             if ( name.equals( FREE_STANDING_PROP ) )
 443             {
 444                 handlePropertyChange( e.getNewValue() );
 445             }
 446             else {
 447                 super.propertyChange( e );
 448             }
 449         }
 450 
 451         public void handlePropertyChange( Object newValue )
 452         {
 453             if ( newValue != null )
 454             {
 455                 boolean temp = ((Boolean)newValue).booleanValue();
 456                 boolean becameFlush = temp == false && isFreeStanding == true;
 457                 boolean becameNormal = temp == true && isFreeStanding == false;
 458 
 459                 isFreeStanding = temp;
 460 
 461                 if ( becameFlush ) {
 462                     toFlush();
 463                 }
 464                 else if ( becameNormal ) {
 465                     toFreeStanding();
 466                 }
 467             }
 468             else
 469             {
 470 
 471                 if ( !isFreeStanding ) {
 472                     isFreeStanding = true;
 473                     toFreeStanding();
 474                 }
 475 
 476                 // This commented-out block is used for testing flush scrollbars.
 477 /*
 478                 if ( isFreeStanding ) {
 479                     isFreeStanding = false;
 480                     toFlush();
 481                 }
 482 */
 483             }
 484 
 485             if ( increaseButton != null )
 486             {
 487                 increaseButton.setFreeStanding( isFreeStanding );
 488             }
 489             if ( decreaseButton != null )
 490             {
 491                 decreaseButton.setFreeStanding( isFreeStanding );
 492             }
 493         }
 494 
 495         protected void toFlush() {
 496             scrollBarWidth -= 2;
 497         }
 498 
 499         protected void toFreeStanding() {
 500             scrollBarWidth += 2;
 501         }
 502     } // end class ScrollBarListener
 503 }