Source for javax.swing.plaf.metal.MetalScrollBarUI

   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:      */
  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:      */
  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:    */
 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:    */
 155:   public static ComponentUI createUI(JComponent component)
 156:   {
 157:     return new MetalScrollBarUI();
 158:   }
 159: 
 160:   /**
 161:    * Installs the defaults.
 162:    */
 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:    */
 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:    */
 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:    */
 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:    */
 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:    */
 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:    */
 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:    */
 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:    */
 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:    */
 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:    */
 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:    */
 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: }