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 }