1 /* MetalScrollBarUI.java 2 Copyright (C) 2005, 2006, Free Software Foundation, Inc. 3 4 This file is part of GNU Classpath. 5 6 GNU Classpath is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 2, or (at your option) 9 any later version. 10 11 GNU Classpath is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with GNU Classpath; see the file COPYING. If not, write to the 18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19 02110-1301 USA. 20 21 Linking this library statically or dynamically with other modules is 22 making a combined work based on this library. Thus, the terms and 23 conditions of the GNU General Public License cover the whole 24 combination. 25 26 As a special exception, the copyright holders of this library give you 27 permission to link this library with independent modules to produce an 28 executable, regardless of the license terms of these independent 29 modules, and to copy and distribute the resulting executable under 30 terms of your choice, provided that you also meet, for each linked 31 independent module, the terms and conditions of the license of that 32 module. An independent module is a module which is not derived from 33 or based on this library. If you modify this library, you may extend 34 this exception to your version of the library, but you are not 35 obligated to do so. If you do not wish to do so, delete this 36 exception statement from your version. */ 37 38 39 package javax.swing.plaf.metal; 40 41 import java.awt.Color; 42 import java.awt.Dimension; 43 import java.awt.Graphics; 44 import java.awt.Insets; 45 import java.awt.Rectangle; 46 import java.beans.PropertyChangeEvent; 47 import java.beans.PropertyChangeListener; 48 49 import javax.swing.JButton; 50 import javax.swing.JComponent; 51 import javax.swing.JScrollBar; 52 import javax.swing.SwingConstants; 53 import javax.swing.UIManager; 54 import javax.swing.plaf.ComponentUI; 55 import javax.swing.plaf.basic.BasicScrollBarUI; 56 57 /** 58 * A UI delegate for the {@link JScrollBar} component. 59 */ 60 public class MetalScrollBarUI extends BasicScrollBarUI 61 { 62 63 /** 64 * A property change handler for the UI delegate that monitors for 65 * changes to the "JScrollBar.isFreeStanding" property, and updates 66 * the buttons and track rendering as appropriate. 67 */ 68 class MetalScrollBarPropertyChangeHandler 69 extends BasicScrollBarUI.PropertyChangeHandler 70 { 71 /** 72 * Creates a new handler. 73 * 74 * @see #createPropertyChangeListener() 75 */ MetalScrollBarPropertyChangeHandler()76 public MetalScrollBarPropertyChangeHandler() 77 { 78 // Nothing to do here. 79 } 80 81 /** 82 * Handles a property change event. If the event name is 83 * <code>JSlider.isFreeStanding</code>, this method updates the 84 * delegate, otherwise the event is passed up to the super class. 85 * 86 * @param e the property change event. 87 */ propertyChange(PropertyChangeEvent e)88 public void propertyChange(PropertyChangeEvent e) 89 { 90 if (e.getPropertyName().equals(FREE_STANDING_PROP)) 91 { 92 Boolean prop = (Boolean) e.getNewValue(); 93 isFreeStanding = prop == null ? true : prop.booleanValue(); 94 if (increaseButton != null) 95 increaseButton.setFreeStanding(isFreeStanding); 96 if (decreaseButton != null) 97 decreaseButton.setFreeStanding(isFreeStanding); 98 } 99 else 100 super.propertyChange(e); 101 } 102 } 103 104 /** The name for the 'free standing' property. */ 105 public static final String FREE_STANDING_PROP = "JScrollBar.isFreeStanding"; 106 107 /** The minimum thumb size for a scroll bar that is not free standing. */ 108 private static final Dimension MIN_THUMB_SIZE = new Dimension(15, 15); 109 110 /** The minimum thumb size for a scroll bar that is free standing. */ 111 private static final Dimension MIN_THUMB_SIZE_FREE_STANDING 112 = new Dimension(17, 17); 113 114 /** The button that increases the value in the scroll bar. */ 115 protected MetalScrollButton increaseButton; 116 117 /** The button that decreases the value in the scroll bar. */ 118 protected MetalScrollButton decreaseButton; 119 120 /** 121 * The scroll bar width. 122 */ 123 protected int scrollBarWidth; 124 125 /** 126 * A flag that indicates whether the scroll bar is "free standing", which 127 * means it has complete borders and can be used anywhere in the UI. A 128 * scroll bar which is not free standing has borders missing from one 129 * side, and relies on being part of another container with its own borders 130 * to look right visually. */ 131 protected boolean isFreeStanding = true; 132 133 /** 134 * The color for the scroll bar shadow (this is read from the UIDefaults in 135 * the installDefaults() method). 136 */ 137 Color scrollBarShadowColor; 138 139 /** 140 * Constructs a new instance of <code>MetalScrollBarUI</code>, with no 141 * specific initialisation. 142 */ MetalScrollBarUI()143 public MetalScrollBarUI() 144 { 145 super(); 146 } 147 148 /** 149 * Returns a new instance of <code>MetalScrollBarUI</code>. 150 * 151 * @param component the component for which we return an UI instance 152 * 153 * @return An instance of MetalScrollBarUI 154 */ createUI(JComponent component)155 public static ComponentUI createUI(JComponent component) 156 { 157 return new MetalScrollBarUI(); 158 } 159 160 /** 161 * Installs the defaults. 162 */ installDefaults()163 protected void installDefaults() 164 { 165 // need to initialise isFreeStanding before calling the super class, 166 // so that the value is set when createIncreaseButton() and 167 // createDecreaseButton() are called (unless there is somewhere earlier 168 // that we can do this). 169 Boolean prop = (Boolean) scrollbar.getClientProperty(FREE_STANDING_PROP); 170 isFreeStanding = prop == null ? true : prop.booleanValue(); 171 scrollBarShadowColor = UIManager.getColor("ScrollBar.shadow"); 172 scrollBarWidth = UIManager.getInt("ScrollBar.width"); 173 super.installDefaults(); 174 } 175 176 /** 177 * Creates a property change listener for the delegate to use. This 178 * overrides the method to provide a custom listener for the 179 * {@link MetalLookAndFeel} that can handle the 180 * <code>JScrollBar.isFreeStanding</code> property. 181 * 182 * @return A property change listener. 183 */ createPropertyChangeListener()184 protected PropertyChangeListener createPropertyChangeListener() 185 { 186 return new MetalScrollBarPropertyChangeHandler(); 187 } 188 189 /** 190 * Creates a new button to use as the control at the lower end of the 191 * {@link JScrollBar}. This method assigns the new button (an instance of 192 * {@link MetalScrollButton} to the {@link #decreaseButton} field, and also 193 * returns the button. The button width is determined by the 194 * <code>ScrollBar.width</code> setting in the UI defaults. 195 * 196 * @param orientation the orientation of the button ({@link #NORTH}, 197 * {@link #SOUTH}, {@link #EAST} or {@link #WEST}). 198 * 199 * @return The button. 200 */ createDecreaseButton(int orientation)201 protected JButton createDecreaseButton(int orientation) 202 { 203 decreaseButton = new MetalScrollButton(orientation, scrollBarWidth, 204 isFreeStanding); 205 return decreaseButton; 206 } 207 208 /** 209 * Creates a new button to use as the control at the upper end of the 210 * {@link JScrollBar}. This method assigns the new button (an instance of 211 * {@link MetalScrollButton} to the {@link #increaseButton} field, and also 212 * returns the button. The button width is determined by the 213 * <code>ScrollBar.width</code> setting in the UI defaults. 214 * 215 * @param orientation the orientation of the button ({@link #NORTH}, 216 * {@link #SOUTH}, {@link #EAST} or {@link #WEST}). 217 * 218 * @return The button. 219 */ createIncreaseButton(int orientation)220 protected JButton createIncreaseButton(int orientation) 221 { 222 increaseButton = new MetalScrollButton(orientation, scrollBarWidth, 223 isFreeStanding); 224 return increaseButton; 225 } 226 227 /** 228 * Paints the track for the scrollbar. 229 * 230 * @param g the graphics device. 231 * @param c the component. 232 * @param trackBounds the track bounds. 233 */ paintTrack(Graphics g, JComponent c, Rectangle trackBounds)234 protected void paintTrack(Graphics g, JComponent c, Rectangle trackBounds) 235 { 236 g.setColor(MetalLookAndFeel.getControl()); 237 g.fillRect(trackBounds.x, trackBounds.y, trackBounds.width, 238 trackBounds.height); 239 if (scrollbar.getOrientation() == HORIZONTAL) 240 paintTrackHorizontal(g, c, trackBounds.x, trackBounds.y, 241 trackBounds.width, trackBounds.height); 242 else 243 paintTrackVertical(g, c, trackBounds.x, trackBounds.y, 244 trackBounds.width, trackBounds.height); 245 246 } 247 248 /** 249 * Paints the track for a horizontal scrollbar. 250 * 251 * @param g the graphics device. 252 * @param c the component. 253 * @param x the x-coordinate for the track bounds. 254 * @param y the y-coordinate for the track bounds. 255 * @param w the width for the track bounds. 256 * @param h the height for the track bounds. 257 */ paintTrackHorizontal(Graphics g, JComponent c, int x, int y, int w, int h)258 private void paintTrackHorizontal(Graphics g, JComponent c, 259 int x, int y, int w, int h) 260 { 261 if (c.isEnabled()) 262 { 263 g.setColor(MetalLookAndFeel.getControlDarkShadow()); 264 g.drawLine(x, y, x, y + h - 1); 265 g.drawLine(x, y, x + w - 1, y); 266 g.drawLine(x + w - 1, y, x + w - 1, y + h - 1); 267 268 g.setColor(scrollBarShadowColor); 269 g.drawLine(x + 1, y + 1, x + 1, y + h - 1); 270 g.drawLine(x + 1, y + 1, x + w - 2, y + 1); 271 272 if (isFreeStanding) 273 { 274 g.setColor(MetalLookAndFeel.getControlDarkShadow()); 275 g.drawLine(x, y + h - 2, x + w - 1, y + h - 2); 276 g.setColor(scrollBarShadowColor); 277 g.drawLine(x, y + h - 1, x + w - 1, y + h - 1); 278 } 279 } 280 else 281 { 282 g.setColor(MetalLookAndFeel.getControlDisabled()); 283 if (isFreeStanding) 284 g.drawRect(x, y, w - 1, h - 1); 285 else 286 { 287 g.drawLine(x, y, x + w - 1, y); 288 g.drawLine(x, y, x, y + h - 1); 289 g.drawLine(x + w - 1, y, x + w - 1, y + h - 1); 290 } 291 } 292 } 293 294 /** 295 * Paints the track for a vertical scrollbar. 296 * 297 * @param g the graphics device. 298 * @param c the component. 299 * @param x the x-coordinate for the track bounds. 300 * @param y the y-coordinate for the track bounds. 301 * @param w the width for the track bounds. 302 * @param h the height for the track bounds. 303 */ paintTrackVertical(Graphics g, JComponent c, int x, int y, int w, int h)304 private void paintTrackVertical(Graphics g, JComponent c, 305 int x, int y, int w, int h) 306 { 307 if (c.isEnabled()) 308 { 309 g.setColor(MetalLookAndFeel.getControlDarkShadow()); 310 g.drawLine(x, y, x, y + h - 1); 311 g.drawLine(x, y, x + w - 1, y); 312 g.drawLine(x, y + h - 1, x + w - 1, y + h - 1); 313 314 g.setColor(scrollBarShadowColor); 315 g.drawLine(x + 1, y + 1, x + w - 1, y + 1); 316 g.drawLine(x + 1, y + 1, x + 1, y + h - 2); 317 318 if (isFreeStanding) 319 { 320 g.setColor(MetalLookAndFeel.getControlDarkShadow()); 321 g.drawLine(x + w - 2, y, x + w - 2, y + h - 1); 322 g.setColor(MetalLookAndFeel.getControlHighlight()); 323 g.drawLine(x + w - 1, y, x + w - 1, y + h - 1); 324 } 325 } 326 else 327 { 328 g.setColor(MetalLookAndFeel.getControlDisabled()); 329 if (isFreeStanding) 330 g.drawRect(x, y, w - 1, h - 1); 331 else 332 { 333 g.drawLine(x, y, x + w - 1, y); 334 g.drawLine(x, y, x, y + h - 1); 335 g.drawLine(x, y + h - 1, x + w - 1, y + h - 1); 336 } 337 } 338 } 339 340 /** 341 * Paints the slider button of the ScrollBar. 342 * 343 * @param g the Graphics context to use 344 * @param c the JComponent on which we paint 345 * @param thumbBounds the rectangle that is the slider button 346 */ paintThumb(Graphics g, JComponent c, Rectangle thumbBounds)347 protected void paintThumb(Graphics g, JComponent c, Rectangle thumbBounds) 348 { 349 // a disabled scrollbar has no thumb in the metal look and feel 350 if (!c.isEnabled()) 351 return; 352 if (scrollbar.getOrientation() == HORIZONTAL) 353 paintThumbHorizontal(g, c, thumbBounds); 354 else 355 paintThumbVertical(g, c, thumbBounds); 356 357 // Draw the pattern when the theme is not Ocean. 358 if (! (MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme)) 359 { 360 MetalUtils.fillMetalPattern(c, g, thumbBounds.x + 3, thumbBounds.y + 3, 361 thumbBounds.width - 6, 362 thumbBounds.height - 6, 363 thumbHighlightColor, 364 thumbLightShadowColor); 365 } 366 } 367 368 /** 369 * Paints the thumb for a horizontal scroll bar. 370 * 371 * @param g the graphics device. 372 * @param c the scroll bar component. 373 * @param thumbBounds the thumb bounds. 374 */ paintThumbHorizontal(Graphics g, JComponent c, Rectangle thumbBounds)375 private void paintThumbHorizontal(Graphics g, JComponent c, 376 Rectangle thumbBounds) 377 { 378 int x = thumbBounds.x; 379 int y = thumbBounds.y; 380 int w = thumbBounds.width; 381 int h = thumbBounds.height; 382 383 // First we fill the background. 384 MetalTheme theme = MetalLookAndFeel.getCurrentTheme(); 385 if (theme instanceof OceanTheme 386 && UIManager.get("ScrollBar.gradient") != null) 387 { 388 MetalUtils.paintGradient(g, x + 2, y + 2, w - 4, h - 2, 389 SwingConstants.VERTICAL, 390 "ScrollBar.gradient"); 391 } 392 else 393 { 394 g.setColor(thumbColor); 395 if (isFreeStanding) 396 g.fillRect(x, y, w, h - 1); 397 else 398 g.fillRect(x, y, w, h); 399 } 400 401 // then draw the dark box 402 g.setColor(thumbLightShadowColor); 403 if (isFreeStanding) 404 g.drawRect(x, y, w - 1, h - 2); 405 else 406 { 407 g.drawLine(x, y, x + w - 1, y); 408 g.drawLine(x, y, x, y + h - 1); 409 g.drawLine(x + w - 1, y, x + w - 1, y + h - 1); 410 } 411 412 // then the highlight 413 g.setColor(thumbHighlightColor); 414 if (isFreeStanding) 415 { 416 g.drawLine(x + 1, y + 1, x + w - 3, y + 1); 417 g.drawLine(x + 1, y + 1, x + 1, y + h - 3); 418 } 419 else 420 { 421 g.drawLine(x + 1, y + 1, x + w - 3, y + 1); 422 g.drawLine(x + 1, y + 1, x + 1, y + h - 1); 423 } 424 425 // draw the shadow line 426 g.setColor(UIManager.getColor("ScrollBar.shadow")); 427 g.drawLine(x + w, y + 1, x + w, y + h - 1); 428 429 // For the OceanTheme, draw the 3 lines in the middle. 430 if (theme instanceof OceanTheme) 431 { 432 g.setColor(thumbLightShadowColor); 433 int middle = x + w / 2; 434 g.drawLine(middle - 2, y + 4, middle - 2, y + h - 5); 435 g.drawLine(middle, y + 4, middle, y + h - 5); 436 g.drawLine(middle + 2, y + 4, middle + 2, y + h - 5); 437 g.setColor(UIManager.getColor("ScrollBar.highlight")); 438 g.drawLine(middle - 1, y + 5, middle - 1, y + h - 4); 439 g.drawLine(middle + 1, y + 5, middle + 1, y + h - 4); 440 g.drawLine(middle + 3, y + 5, middle + 3, y + h - 4); 441 } 442 } 443 444 /** 445 * Paints the thumb for a vertical scroll bar. 446 * 447 * @param g the graphics device. 448 * @param c the scroll bar component. 449 * @param thumbBounds the thumb bounds. 450 */ paintThumbVertical(Graphics g, JComponent c, Rectangle thumbBounds)451 private void paintThumbVertical(Graphics g, JComponent c, 452 Rectangle thumbBounds) 453 { 454 int x = thumbBounds.x; 455 int y = thumbBounds.y; 456 int w = thumbBounds.width; 457 int h = thumbBounds.height; 458 459 // First we fill the background. 460 MetalTheme theme = MetalLookAndFeel.getCurrentTheme(); 461 if (theme instanceof OceanTheme 462 && UIManager.get("ScrollBar.gradient") != null) 463 { 464 MetalUtils.paintGradient(g, x + 2, y + 2, w - 2, h - 4, 465 SwingConstants.HORIZONTAL, 466 "ScrollBar.gradient"); 467 } 468 else 469 { 470 g.setColor(thumbColor); 471 if (isFreeStanding) 472 g.fillRect(x, y, w - 1, h); 473 else 474 g.fillRect(x, y, w, h); 475 } 476 477 // then draw the dark box 478 g.setColor(thumbLightShadowColor); 479 if (isFreeStanding) 480 g.drawRect(x, y, w - 2, h - 1); 481 else 482 { 483 g.drawLine(x, y, x + w - 1, y); 484 g.drawLine(x, y, x, y + h - 1); 485 g.drawLine(x, y + h - 1, x + w - 1, y + h - 1); 486 } 487 488 // then the highlight 489 g.setColor(thumbHighlightColor); 490 if (isFreeStanding) 491 { 492 g.drawLine(x + 1, y + 1, x + w - 3, y + 1); 493 g.drawLine(x + 1, y + 1, x + 1, y + h - 3); 494 } 495 else 496 { 497 g.drawLine(x + 1, y + 1, x + w - 1, y + 1); 498 g.drawLine(x + 1, y + 1, x + 1, y + h - 3); 499 } 500 501 // draw the shadow line 502 g.setColor(UIManager.getColor("ScrollBar.shadow")); 503 g.drawLine(x + 1, y + h, x + w - 2, y + h); 504 505 // For the OceanTheme, draw the 3 lines in the middle. 506 if (theme instanceof OceanTheme) 507 { 508 g.setColor(thumbLightShadowColor); 509 int middle = y + h / 2; 510 g.drawLine(x + 4, middle - 2, x + w - 5, middle - 2); 511 g.drawLine(x + 4, middle, x + w - 5, middle); 512 g.drawLine(x + 4, middle + 2, x + w - 5, middle + 2); 513 g.setColor(UIManager.getColor("ScrollBar.highlight")); 514 g.drawLine(x + 5, middle - 1, x + w - 4, middle - 1); 515 g.drawLine(x + 5, middle + 1, x + w - 4, middle + 1); 516 g.drawLine(x + 5, middle + 3, x + w - 4, middle + 3); 517 } 518 } 519 520 /** 521 * Returns the minimum thumb size. For a free standing scroll bar the 522 * minimum size is <code>17 x 17</code> pixels, whereas for a non free 523 * standing scroll bar the minimum size is <code>15 x 15</code> pixels. 524 * 525 * @return The minimum thumb size. 526 */ getMinimumThumbSize()527 protected Dimension getMinimumThumbSize() 528 { 529 Dimension retVal; 530 if (scrollbar != null) 531 { 532 if (isFreeStanding) 533 retVal = MIN_THUMB_SIZE_FREE_STANDING; 534 else 535 retVal = MIN_THUMB_SIZE; 536 } 537 else 538 retVal = new Dimension(0, 0); 539 return retVal; 540 } 541 542 /** 543 * Returns the <code>preferredSize</code> for the specified scroll bar. 544 * For a vertical scrollbar the height is the sum of the preferred heights 545 * of the buttons plus <code>30</code>. The width is fetched from the 546 * <code>UIManager</code> property <code>ScrollBar.width</code>. 547 * 548 * For horizontal scrollbars the width is the sum of the preferred widths 549 * of the buttons plus <code>30</code>. The height is fetched from the 550 * <code>UIManager</code> property <code>ScrollBar.height</code>. 551 * 552 * @param c the scrollbar for which to calculate the preferred size 553 * 554 * @return the <code>preferredSize</code> for the specified scroll bar 555 */ getPreferredSize(JComponent c)556 public Dimension getPreferredSize(JComponent c) 557 { 558 int height; 559 int width; 560 height = width = 0; 561 562 if (scrollbar.getOrientation() == SwingConstants.HORIZONTAL) 563 { 564 width += incrButton.getPreferredSize().getWidth(); 565 width += decrButton.getPreferredSize().getWidth(); 566 width += 30; 567 height = UIManager.getInt("ScrollBar.width"); 568 } 569 else 570 { 571 height += incrButton.getPreferredSize().getHeight(); 572 height += decrButton.getPreferredSize().getHeight(); 573 height += 30; 574 width = UIManager.getInt("ScrollBar.width"); 575 } 576 577 Insets insets = scrollbar.getInsets(); 578 579 height += insets.top + insets.bottom; 580 width += insets.left + insets.right; 581 582 return new Dimension(width, height); 583 } 584 } 585