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 }