Source for javax.swing.plaf.metal.MetalScrollBarUI

   1: /* MetalScrollBarUI.java
   2:    Copyright (C) 2005 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:     super.installDefaults();
 173:   }
 174:     
 175:   /**
 176:    * Creates a property change listener for the delegate to use.  This
 177:    * overrides the method to provide a custom listener for the 
 178:    * {@link MetalLookAndFeel} that can handle the 
 179:    * <code>JScrollBar.isFreeStanding</code> property.
 180:    * 
 181:    * @return A property change listener.
 182:    */
 183:   protected PropertyChangeListener createPropertyChangeListener()
 184:   {
 185:     return new MetalScrollBarPropertyChangeHandler();
 186:   }
 187:   
 188:   /**
 189:    * Creates a new button to use as the control at the lower end of the
 190:    * {@link JScrollBar}.
 191:    * 
 192:    * @param orientation  the orientation of the button ({@link #NORTH},
 193:    *                     {@link #SOUTH}, {@link #EAST} or {@link #WEST}).
 194:    * 
 195:    * @return The button.
 196:    */
 197:   protected JButton createDecreaseButton(int orientation)
 198:   {
 199:     scrollBarWidth = UIManager.getInt("ScrollBar.width");
 200:     decreaseButton = new MetalScrollButton(orientation, scrollBarWidth, 
 201:             isFreeStanding);
 202:     return decreaseButton;
 203:   }
 204: 
 205:   /**
 206:    * Creates a new button to use as the control at the upper end of the
 207:    * {@link JScrollBar}.
 208:    * 
 209:    * @param orientation  the orientation of the button ({@link #NORTH},
 210:    *                     {@link #SOUTH}, {@link #EAST} or {@link #WEST}).
 211:    * 
 212:    * @return The button.
 213:    */
 214:   protected JButton createIncreaseButton(int orientation)
 215:   {
 216:     scrollBarWidth = UIManager.getInt("ScrollBar.width");
 217:     increaseButton = new MetalScrollButton(orientation, scrollBarWidth, 
 218:             isFreeStanding);
 219:     return increaseButton;
 220:   }
 221:   
 222:   /**
 223:    * Paints the track for the scrollbar.
 224:    * 
 225:    * @param g  the graphics device.
 226:    * @param c  the component.
 227:    * @param trackBounds  the track bounds.
 228:    */
 229:   protected void paintTrack(Graphics g, JComponent c, Rectangle trackBounds)
 230:   {
 231:     g.setColor(MetalLookAndFeel.getControl());
 232:     g.fillRect(trackBounds.x, trackBounds.y, trackBounds.width, 
 233:             trackBounds.height);
 234:     if (scrollbar.getOrientation() == HORIZONTAL) 
 235:       paintTrackHorizontal(g, c, trackBounds.x, trackBounds.y, 
 236:           trackBounds.width, trackBounds.height);
 237:     else 
 238:       paintTrackVertical(g, c, trackBounds.x, trackBounds.y, 
 239:           trackBounds.width, trackBounds.height);
 240:     
 241:   }
 242:   
 243:   /**
 244:    * Paints the track for a horizontal scrollbar.
 245:    * 
 246:    * @param g  the graphics device.
 247:    * @param c  the component.
 248:    * @param x  the x-coordinate for the track bounds.
 249:    * @param y  the y-coordinate for the track bounds.
 250:    * @param w  the width for the track bounds.
 251:    * @param h  the height for the track bounds.
 252:    */
 253:   private void paintTrackHorizontal(Graphics g, JComponent c, 
 254:       int x, int y, int w, int h)
 255:   {
 256:     if (c.isEnabled())
 257:       {
 258:         g.setColor(MetalLookAndFeel.getControlDarkShadow());
 259:         g.drawLine(x, y, x, y + h - 1);
 260:         g.drawLine(x, y, x + w - 1, y);
 261:         g.drawLine(x + w - 1, y, x + w - 1, y + h - 1);
 262:         
 263:         g.setColor(scrollBarShadowColor);
 264:         g.drawLine(x + 1, y + 1, x + 1, y + h - 1);
 265:         g.drawLine(x + 1, y + 1, x + w - 2, y + 1);
 266:         
 267:         if (isFreeStanding) 
 268:           {
 269:             g.setColor(MetalLookAndFeel.getControlDarkShadow());
 270:             g.drawLine(x, y + h - 2, x + w - 1, y + h - 2);
 271:             g.setColor(scrollBarShadowColor);
 272:             g.drawLine(x, y + h - 1, x + w - 1, y + h - 1);
 273:           }
 274:       }
 275:     else
 276:       {
 277:         g.setColor(MetalLookAndFeel.getControlDisabled());
 278:         if (isFreeStanding)
 279:           g.drawRect(x, y, w - 1, h - 1);
 280:         else
 281:           {
 282:             g.drawLine(x, y, x + w - 1, y);
 283:             g.drawLine(x, y, x, y + h - 1);
 284:             g.drawLine(x + w - 1, y, x + w - 1, y + h - 1);
 285:           }
 286:       }
 287:   }
 288:     
 289:   /**
 290:    * Paints the track for a vertical scrollbar.
 291:    * 
 292:    * @param g  the graphics device.
 293:    * @param c  the component.
 294:    * @param x  the x-coordinate for the track bounds.
 295:    * @param y  the y-coordinate for the track bounds.
 296:    * @param w  the width for the track bounds.
 297:    * @param h  the height for the track bounds.
 298:    */
 299:   private void paintTrackVertical(Graphics g, JComponent c, 
 300:       int x, int y, int w, int h)
 301:   {
 302:     if (c.isEnabled())
 303:       {
 304:         g.setColor(MetalLookAndFeel.getControlDarkShadow());
 305:         g.drawLine(x, y, x, y + h - 1);
 306:         g.drawLine(x, y, x + w - 1, y);
 307:         g.drawLine(x, y + h - 1, x + w - 1, y + h - 1);
 308:         
 309:         g.setColor(scrollBarShadowColor);
 310:         g.drawLine(x + 1, y + 1, x + w - 1, y + 1);
 311:         g.drawLine(x + 1, y + 1, x + 1, y + h - 2);
 312:         
 313:         if (isFreeStanding) 
 314:           {
 315:             g.setColor(MetalLookAndFeel.getControlDarkShadow());
 316:             g.drawLine(x + w - 2, y, x + w - 2, y + h - 1);
 317:             g.setColor(MetalLookAndFeel.getControlHighlight());
 318:             g.drawLine(x + w - 1, y, x + w - 1, y + h - 1);
 319:           }
 320:       }
 321:     else
 322:       {
 323:         g.setColor(MetalLookAndFeel.getControlDisabled());
 324:         if (isFreeStanding)
 325:           g.drawRect(x, y, w - 1, h - 1);
 326:         else
 327:           {
 328:             g.drawLine(x, y, x + w - 1, y);
 329:             g.drawLine(x, y, x, y + h - 1);
 330:             g.drawLine(x, y + h - 1, x + w - 1, y + h - 1);
 331:           }
 332:       }
 333:   }
 334: 
 335:   /**
 336:    * Paints the slider button of the ScrollBar.
 337:    *
 338:    * @param g the Graphics context to use
 339:    * @param c the JComponent on which we paint
 340:    * @param thumbBounds the rectangle that is the slider button
 341:    */
 342:   protected void paintThumb(Graphics g, JComponent c, Rectangle thumbBounds)
 343:   {
 344:     // a disabled scrollbar has no thumb in the metal look and feel
 345:     if (!c.isEnabled())
 346:       return;
 347:     if (scrollbar.getOrientation() == HORIZONTAL)
 348:       paintThumbHorizontal(g, c, thumbBounds);
 349:     else 
 350:       paintThumbVertical(g, c, thumbBounds);
 351: 
 352:     // Draw the pattern when the theme is not Ocean.
 353:     if (! (MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme))
 354:       {
 355:         MetalUtils.fillMetalPattern(c, g, thumbBounds.x + 3, thumbBounds.y + 3,
 356:                                     thumbBounds.width - 6,
 357:                                     thumbBounds.height - 6,
 358:                                     thumbHighlightColor,
 359:                                     thumbLightShadowColor);
 360:       }
 361:   }
 362: 
 363:   /**
 364:    * Paints the thumb for a horizontal scroll bar.
 365:    * 
 366:    * @param g  the graphics device.
 367:    * @param c  the scroll bar component.
 368:    * @param thumbBounds  the thumb bounds.
 369:    */
 370:   private void paintThumbHorizontal(Graphics g, JComponent c, 
 371:           Rectangle thumbBounds) 
 372:   {
 373:     int x = thumbBounds.x;
 374:     int y = thumbBounds.y;
 375:     int w = thumbBounds.width;
 376:     int h = thumbBounds.height;
 377:     
 378:     // First we fill the background.
 379:     MetalTheme theme = MetalLookAndFeel.getCurrentTheme();
 380:     if (theme instanceof OceanTheme
 381:         && UIManager.get("ScrollBar.gradient") != null)
 382:       {
 383:         MetalUtils.paintGradient(g, x + 2, y + 2, w - 4, h - 2,
 384:                                  SwingConstants.VERTICAL,
 385:                                  "ScrollBar.gradient");
 386:       }
 387:     else
 388:       {
 389:         g.setColor(thumbColor);
 390:         if (isFreeStanding)
 391:           g.fillRect(x, y, w, h - 1);
 392:         else
 393:           g.fillRect(x, y, w, h);
 394:       }
 395: 
 396:     // then draw the dark box
 397:     g.setColor(thumbLightShadowColor);
 398:     if (isFreeStanding)
 399:       g.drawRect(x, y, w - 1, h - 2);
 400:     else
 401:       {
 402:         g.drawLine(x, y, x + w - 1, y);
 403:         g.drawLine(x, y, x, y + h - 1);
 404:         g.drawLine(x + w - 1, y, x + w - 1, y + h - 1);
 405:       }
 406:     
 407:     // then the highlight
 408:     g.setColor(thumbHighlightColor);
 409:     if (isFreeStanding)
 410:       {
 411:         g.drawLine(x + 1, y + 1, x + w - 3, y + 1);
 412:         g.drawLine(x + 1, y + 1, x + 1, y + h - 3);
 413:       }
 414:     else
 415:       {
 416:         g.drawLine(x + 1, y + 1, x + w - 3, y + 1);
 417:         g.drawLine(x + 1, y + 1, x + 1, y + h - 1);
 418:       }
 419:     
 420:     // draw the shadow line
 421:     g.setColor(UIManager.getColor("ScrollBar.shadow"));
 422:     g.drawLine(x + w, y + 1, x + w, y + h - 1);
 423: 
 424:     // For the OceanTheme, draw the 3 lines in the middle.
 425:     if (theme instanceof OceanTheme)
 426:       {
 427:         g.setColor(thumbLightShadowColor);
 428:         int middle = x + w / 2;
 429:         g.drawLine(middle - 2, y + 4, middle - 2, y + h - 5);
 430:         g.drawLine(middle, y + 4, middle, y + h - 5);
 431:         g.drawLine(middle + 2, y + 4, middle + 2, y + h - 5);
 432:         g.setColor(UIManager.getColor("ScrollBar.highlight"));
 433:         g.drawLine(middle - 1, y + 5, middle - 1, y + h - 4);
 434:         g.drawLine(middle + 1, y + 5, middle + 1, y + h - 4);
 435:         g.drawLine(middle + 3, y + 5, middle + 3, y + h - 4);
 436:       }
 437:   }
 438:   
 439:   /**
 440:    * Paints the thumb for a vertical scroll bar.
 441:    * 
 442:    * @param g  the graphics device.
 443:    * @param c  the scroll bar component.
 444:    * @param thumbBounds  the thumb bounds.
 445:    */
 446:   private void paintThumbVertical(Graphics g, JComponent c, 
 447:           Rectangle thumbBounds)
 448:   {
 449:     int x = thumbBounds.x;
 450:     int y = thumbBounds.y;
 451:     int w = thumbBounds.width;
 452:     int h = thumbBounds.height;
 453:     
 454:     // First we fill the background.
 455:     MetalTheme theme = MetalLookAndFeel.getCurrentTheme();
 456:     if (theme instanceof OceanTheme
 457:         && UIManager.get("ScrollBar.gradient") != null)
 458:       {
 459:         MetalUtils.paintGradient(g, x + 2, y + 2, w - 2, h - 4,
 460:                                  SwingConstants.HORIZONTAL,
 461:                                  "ScrollBar.gradient");
 462:       }
 463:     else
 464:       {
 465:         g.setColor(thumbColor);
 466:         if (isFreeStanding)
 467:           g.fillRect(x, y, w - 1, h);
 468:         else
 469:           g.fillRect(x, y, w, h);
 470:       }
 471: 
 472:     // then draw the dark box
 473:     g.setColor(thumbLightShadowColor);
 474:     if (isFreeStanding)
 475:       g.drawRect(x, y, w - 2, h - 1);
 476:     else
 477:       {
 478:         g.drawLine(x, y, x + w - 1, y);
 479:         g.drawLine(x, y, x, y + h - 1);
 480:         g.drawLine(x, y + h - 1, x + w - 1, y + h - 1);
 481:       }
 482:     
 483:     // then the highlight
 484:     g.setColor(thumbHighlightColor);
 485:     if (isFreeStanding)
 486:       {
 487:         g.drawLine(x + 1, y + 1, x + w - 3, y + 1);
 488:         g.drawLine(x + 1, y + 1, x + 1, y + h - 3);
 489:       }
 490:     else
 491:       {
 492:         g.drawLine(x + 1, y + 1, x + w - 1, y + 1);
 493:         g.drawLine(x + 1, y + 1, x + 1, y + h - 3);
 494:       }
 495:     
 496:     // draw the shadow line
 497:     g.setColor(UIManager.getColor("ScrollBar.shadow"));
 498:     g.drawLine(x + 1, y + h, x + w - 2, y + h);
 499: 
 500:     // For the OceanTheme, draw the 3 lines in the middle.
 501:     if (theme instanceof OceanTheme)
 502:       {
 503:         g.setColor(thumbLightShadowColor);
 504:         int middle = y + h / 2;
 505:         g.drawLine(x + 4, middle - 2, x + w - 5, middle - 2);
 506:         g.drawLine(x + 4, middle, x + w - 5, middle);
 507:         g.drawLine(x + 4, middle + 2, x + w - 5, middle + 2);
 508:         g.setColor(UIManager.getColor("ScrollBar.highlight"));
 509:         g.drawLine(x + 5, middle - 1, x + w - 4, middle - 1);
 510:         g.drawLine(x + 5, middle + 1, x + w - 4, middle + 1);
 511:         g.drawLine(x + 5, middle + 3, x + w - 4, middle + 3);
 512:       }
 513:   }
 514:   
 515:   /**
 516:    * Returns the minimum thumb size.  For a free standing scroll bar the 
 517:    * minimum size is <code>17 x 17</code> pixels, whereas for a non free 
 518:    * standing scroll bar the minimum size is <code>15 x 15</code> pixels.
 519:    *
 520:    * @return The minimum thumb size.
 521:    */
 522:   protected Dimension getMinimumThumbSize()
 523:   {
 524:     Dimension retVal;
 525:     if (scrollbar != null)
 526:       {
 527:         if (isFreeStanding)
 528:           retVal = MIN_THUMB_SIZE_FREE_STANDING;
 529:         else
 530:           retVal = MIN_THUMB_SIZE;
 531:       }
 532:     else
 533:       retVal = new Dimension(0, 0);
 534:     return retVal;
 535:   }
 536: 
 537:   /**
 538:    * Returns the <code>preferredSize</code> for the specified scroll bar.
 539:    * For a vertical scrollbar the height is the sum of the preferred heights
 540:    * of the buttons plus <code>30</code>. The width is fetched from the
 541:    * <code>UIManager</code> property <code>ScrollBar.width</code>.
 542:    *
 543:    * For horizontal scrollbars the width is the sum of the preferred widths
 544:    * of the buttons plus <code>30</code>. The height is fetched from the
 545:    * <code>UIManager</code> property <code>ScrollBar.height</code>.
 546:    *
 547:    * @param c the scrollbar for which to calculate the preferred size
 548:    *
 549:    * @return the <code>preferredSize</code> for the specified scroll bar
 550:    */
 551:   public Dimension getPreferredSize(JComponent c)
 552:   {
 553:     int height;
 554:     int width;
 555:     height = width = 0;
 556: 
 557:     if (scrollbar.getOrientation() == SwingConstants.HORIZONTAL)
 558:       {
 559:         width += incrButton.getPreferredSize().getWidth();
 560:         width += decrButton.getPreferredSize().getWidth();
 561:         width += 30;
 562:         height = UIManager.getInt("ScrollBar.width");
 563:       }
 564:     else
 565:       {
 566:         height += incrButton.getPreferredSize().getHeight();
 567:         height += decrButton.getPreferredSize().getHeight();
 568:         height += 30;
 569:         width = UIManager.getInt("ScrollBar.width");
 570:       }
 571: 
 572:     Insets insets = scrollbar.getInsets();
 573: 
 574:     height += insets.top + insets.bottom;
 575:     width += insets.left + insets.right;
 576: 
 577:     return new Dimension(width, height);
 578:   } 
 579: }