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.Rectangle;
  45: import java.beans.PropertyChangeEvent;
  46: import java.beans.PropertyChangeListener;
  47: 
  48: import javax.swing.JButton;
  49: import javax.swing.JComponent;
  50: import javax.swing.JScrollBar;
  51: import javax.swing.UIDefaults;
  52: import javax.swing.UIManager;
  53: import javax.swing.plaf.ComponentUI;
  54: import javax.swing.plaf.basic.BasicScrollBarUI;
  55: 
  56: /**
  57:  * A UI delegate for the {@link JScrollBar} component.
  58:  */
  59: public class MetalScrollBarUI extends BasicScrollBarUI
  60: {
  61:   
  62:   /**
  63:    * A property change handler for the UI delegate that monitors for
  64:    * changes to the "JScrollBar.isFreeStanding" property, and updates
  65:    * the buttons and track rendering as appropriate.
  66:    */
  67:   class MetalScrollBarPropertyChangeHandler 
  68:     extends BasicScrollBarUI.PropertyChangeHandler
  69:   {
  70:     /**
  71:      * Creates a new handler.
  72:      * 
  73:      * @see #createPropertyChangeListener()
  74:      */
  75:     public MetalScrollBarPropertyChangeHandler()
  76:     {
  77:       // Nothing to do here.
  78:     }
  79:     
  80:     /**
  81:      * Handles a property change event.  If the event name is
  82:      * <code>JSlider.isFreeStanding</code>, this method updates the 
  83:      * delegate, otherwise the event is passed up to the super class.
  84:      * 
  85:      * @param e  the property change event.
  86:      */
  87:     public void propertyChange(PropertyChangeEvent e)
  88:     {
  89:       if (e.getPropertyName().equals(FREE_STANDING_PROP))
  90:         {
  91:           Boolean prop = (Boolean) e.getNewValue();
  92:           isFreeStanding = (prop == null ? true : prop.booleanValue());
  93:       if (increaseButton != null)
  94:         increaseButton.setFreeStanding(isFreeStanding);
  95:       if (decreaseButton != null)
  96:         decreaseButton.setFreeStanding(isFreeStanding);
  97:         }
  98:       else
  99:     super.propertyChange(e);
 100:     }
 101:   }
 102:   
 103:   /** The name for the 'free standing' property. */
 104:   public static final String FREE_STANDING_PROP = "JScrollBar.isFreeStanding";
 105: 
 106:   /** The minimum thumb size for a scroll bar that is not free standing. */
 107:   private static final Dimension MIN_THUMB_SIZE = new Dimension(15, 15);
 108: 
 109:   /** The minimum thumb size for a scroll bar that is free standing. */
 110:   private static final Dimension MIN_THUMB_SIZE_FREE_STANDING 
 111:     = new Dimension(17, 17);
 112:   
 113:   /** The button that increases the value in the scroll bar. */
 114:   protected MetalScrollButton increaseButton;
 115:   
 116:   /** The button that decreases the value in the scroll bar. */
 117:   protected MetalScrollButton decreaseButton;
 118:   
 119:   /** 
 120:    * The scroll bar width. 
 121:    */
 122:   protected int scrollBarWidth;
 123:   
 124:   /** 
 125:    * A flag that indicates whether the scroll bar is "free standing", which 
 126:    * means it has complete borders and can be used anywhere in the UI.  A 
 127:    * scroll bar which is not free standing has borders missing from one
 128:    * side, and relies on being part of another container with its own borders
 129:    * to look right visually. */
 130:   protected boolean isFreeStanding = true;
 131:   
 132:   /** 
 133:    * The color for the scroll bar shadow (this is read from the UIDefaults in 
 134:    * the installDefaults() method).
 135:    */
 136:   Color scrollBarShadowColor;
 137:   
 138:   /**
 139:    * Constructs a new instance of <code>MetalScrollBarUI</code>, with no
 140:    * specific initialisation.
 141:    */
 142:   public MetalScrollBarUI()
 143:   {
 144:     super();
 145:   }
 146: 
 147:   /**
 148:    * Returns a new instance of <code>MetalScrollBarUI</code>.
 149:    *
 150:    * @param component the component for which we return an UI instance
 151:    *
 152:    * @return An instance of MetalScrollBarUI
 153:    */
 154:   public static ComponentUI createUI(JComponent component)
 155:   {
 156:     return new MetalScrollBarUI();
 157:   }
 158: 
 159:   /**
 160:    * Installs the defaults.
 161:    */
 162:   protected void installDefaults()
 163:   {    
 164:     // need to initialise isFreeStanding before calling the super class, 
 165:     // so that the value is set when createIncreaseButton() and 
 166:     // createDecreaseButton() are called (unless there is somewhere earlier
 167:     // that we can do this).
 168:     Boolean prop = (Boolean) scrollbar.getClientProperty(FREE_STANDING_PROP);
 169:     isFreeStanding = (prop == null ? true : prop.booleanValue());
 170:     scrollBarShadowColor = UIManager.getColor("ScrollBar.shadow");
 171:     super.installDefaults();
 172:   }
 173:     
 174:   /**
 175:    * Creates a property change listener for the delegate to use.  This
 176:    * overrides the method to provide a custom listener for the 
 177:    * {@link MetalLookAndFeel} that can handle the 
 178:    * <code>JScrollBar.isFreeStanding</code> property.
 179:    * 
 180:    * @return A property change listener.
 181:    */
 182:   protected PropertyChangeListener createPropertyChangeListener()
 183:   {
 184:     return new MetalScrollBarPropertyChangeHandler();
 185:   }
 186:   
 187:   /**
 188:    * Creates a new button to use as the control at the lower end of the
 189:    * {@link JScrollBar}.
 190:    * 
 191:    * @param orientation  the orientation of the button ({@link #NORTH},
 192:    *                     {@link #SOUTH}, {@link #EAST} or {@link #WEST}).
 193:    * 
 194:    * @return The button.
 195:    */
 196:   protected JButton createDecreaseButton(int orientation)
 197:   {
 198:     UIDefaults defaults = UIManager.getLookAndFeelDefaults();
 199:     scrollBarWidth = defaults.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:     UIDefaults defaults = UIManager.getLookAndFeelDefaults();
 217:     scrollBarWidth = defaults.getInt("ScrollBar.width");
 218:     increaseButton = new MetalScrollButton(orientation, scrollBarWidth, 
 219:             isFreeStanding);
 220:     return increaseButton;
 221:   }
 222:   
 223:   /**
 224:    * Paints the track for the scrollbar.
 225:    * 
 226:    * @param g  the graphics device.
 227:    * @param c  the component.
 228:    * @param trackBounds  the track bounds.
 229:    */
 230:   protected void paintTrack(Graphics g, JComponent c, Rectangle trackBounds)
 231:   {
 232:     g.setColor(MetalLookAndFeel.getControl());
 233:     g.fillRect(trackBounds.x, trackBounds.y, trackBounds.width, 
 234:             trackBounds.height);
 235:     if (scrollbar.getOrientation() == HORIZONTAL) 
 236:       paintTrackHorizontal(g, c, trackBounds.x, trackBounds.y, 
 237:           trackBounds.width, trackBounds.height);
 238:     else 
 239:       paintTrackVertical(g, c, trackBounds.x, trackBounds.y, 
 240:           trackBounds.width, trackBounds.height);
 241:     
 242:   }
 243:   
 244:   /**
 245:    * Paints the track for a horizontal scrollbar.
 246:    * 
 247:    * @param g  the graphics device.
 248:    * @param c  the component.
 249:    * @param x  the x-coordinate for the track bounds.
 250:    * @param y  the y-coordinate for the track bounds.
 251:    * @param w  the width for the track bounds.
 252:    * @param h  the height for the track bounds.
 253:    */
 254:   private void paintTrackHorizontal(Graphics g, JComponent c, 
 255:       int x, int y, int w, int h)
 256:   {
 257:     if (c.isEnabled())
 258:       {
 259:         g.setColor(MetalLookAndFeel.getControlDarkShadow());
 260:         g.drawLine(x, y, x, y + h - 1);
 261:         g.drawLine(x, y, x + w - 1, y);
 262:         g.drawLine(x + w - 1, y, x + w - 1, y + h - 1);
 263:         
 264:         g.setColor(scrollBarShadowColor);
 265:         g.drawLine(x + 1, y + 1, x + 1, y + h - 1);
 266:         g.drawLine(x + 1, y + 1, x + w - 2, y + 1);
 267:         
 268:         if (isFreeStanding) 
 269:           {
 270:             g.setColor(MetalLookAndFeel.getControlDarkShadow());
 271:             g.drawLine(x, y + h - 2, x + w - 1, y + h - 2);
 272:             g.setColor(scrollBarShadowColor);
 273:             g.drawLine(x, y + h - 1, x + w - 1, y + h - 1);
 274:           }
 275:       }
 276:     else
 277:       {
 278:         g.setColor(MetalLookAndFeel.getControlDisabled());
 279:         if (isFreeStanding)
 280:           g.drawRect(x, y, w - 1, h - 1);
 281:         else
 282:           {
 283:             g.drawLine(x, y, x + w - 1, y);
 284:             g.drawLine(x, y, x, y + h - 1);
 285:             g.drawLine(x + w - 1, y, x + w - 1, y + h - 1);
 286:           }
 287:       }
 288:   }
 289:     
 290:   /**
 291:    * Paints the track for a vertical scrollbar.
 292:    * 
 293:    * @param g  the graphics device.
 294:    * @param c  the component.
 295:    * @param x  the x-coordinate for the track bounds.
 296:    * @param y  the y-coordinate for the track bounds.
 297:    * @param w  the width for the track bounds.
 298:    * @param h  the height for the track bounds.
 299:    */
 300:   private void paintTrackVertical(Graphics g, JComponent c, 
 301:       int x, int y, int w, int h)
 302:   {
 303:     if (c.isEnabled())
 304:       {
 305:         g.setColor(MetalLookAndFeel.getControlDarkShadow());
 306:         g.drawLine(x, y, x, y + h - 1);
 307:         g.drawLine(x, y, x + w - 1, y);
 308:         g.drawLine(x, y + h - 1, x + w - 1, y + h - 1);
 309:         
 310:         g.setColor(scrollBarShadowColor);
 311:         g.drawLine(x + 1, y + 1, x + w - 1, y + 1);
 312:         g.drawLine(x + 1, y + 1, x + 1, y + h - 2);
 313:         
 314:         if (isFreeStanding) 
 315:           {
 316:             g.setColor(MetalLookAndFeel.getControlDarkShadow());
 317:             g.drawLine(x + w - 2, y, x + w - 2, y + h - 1);
 318:             g.setColor(MetalLookAndFeel.getControlHighlight());
 319:             g.drawLine(x + w - 1, y, x + w - 1, y + h - 1);
 320:           }
 321:       }
 322:     else
 323:       {
 324:         g.setColor(MetalLookAndFeel.getControlDisabled());
 325:         if (isFreeStanding)
 326:           g.drawRect(x, y, w - 1, h - 1);
 327:         else
 328:           {
 329:             g.drawLine(x, y, x + w - 1, y);
 330:             g.drawLine(x, y, x, y + h - 1);
 331:             g.drawLine(x, y + h - 1, x + w - 1, y + h - 1);
 332:           }
 333:       }
 334:   }
 335: 
 336:   /**
 337:    * Paints the slider button of the ScrollBar.
 338:    *
 339:    * @param g the Graphics context to use
 340:    * @param c the JComponent on which we paint
 341:    * @param thumbBounds the rectangle that is the slider button
 342:    */
 343:   protected void paintThumb(Graphics g, JComponent c, Rectangle thumbBounds)
 344:   {
 345:     // a disabled scrollbar has no thumb in the metal look and feel
 346:     if (!c.isEnabled())
 347:       return;
 348:     if (scrollbar.getOrientation() == HORIZONTAL)
 349:       paintThumbHorizontal(g, c, thumbBounds);
 350:     else 
 351:       paintThumbVertical(g, c, thumbBounds);
 352: 
 353:     // draw the pattern
 354:     MetalUtils.fillMetalPattern(c, g, thumbBounds.x + 3, thumbBounds.y + 3,
 355:             thumbBounds.width - 6, thumbBounds.height - 6,
 356:             thumbHighlightColor, thumbLightShadowColor);
 357:   }
 358: 
 359:   /**
 360:    * Paints the thumb for a horizontal scroll bar.
 361:    * 
 362:    * @param g  the graphics device.
 363:    * @param c  the scroll bar component.
 364:    * @param thumbBounds  the thumb bounds.
 365:    */
 366:   private void paintThumbHorizontal(Graphics g, JComponent c, 
 367:           Rectangle thumbBounds) 
 368:   {
 369:     int x = thumbBounds.x;
 370:     int y = thumbBounds.y;
 371:     int w = thumbBounds.width;
 372:     int h = thumbBounds.height;
 373:     
 374:     // first we fill the background
 375:     g.setColor(thumbColor);
 376:     if (isFreeStanding)
 377:       g.fillRect(x, y, w, h - 1);
 378:     else
 379:       g.fillRect(x, y, w, h);
 380:     
 381:     // then draw the dark box
 382:     g.setColor(thumbLightShadowColor);
 383:     if (isFreeStanding)
 384:       g.drawRect(x, y, w - 1, h - 2);
 385:     else
 386:       {
 387:         g.drawLine(x, y, x + w - 1, y);
 388:         g.drawLine(x, y, x, y + h - 1);
 389:         g.drawLine(x + w - 1, y, x + w - 1, y + h -1);
 390:       }
 391:     
 392:     // then the highlight
 393:     g.setColor(thumbHighlightColor);
 394:     if (isFreeStanding)
 395:       {
 396:         g.drawLine(x + 1, y + 1, x + w - 3, y + 1);
 397:         g.drawLine(x + 1, y + 1, x + 1, y + h - 3);
 398:       }
 399:     else
 400:       {
 401:         g.drawLine(x + 1, y + 1, x + w - 3, y + 1);
 402:         g.drawLine(x + 1, y + 1, x + 1, y + h - 1);
 403:       }
 404:     
 405:     // draw the shadow line
 406:     UIDefaults def = UIManager.getLookAndFeelDefaults();
 407:     g.setColor(def.getColor("ScrollBar.shadow"));
 408:     g.drawLine(x + w, y + 1, x + w, y + h - 1);
 409: 
 410:   }
 411:   
 412:   /**
 413:    * Paints the thumb for a vertical scroll bar.
 414:    * 
 415:    * @param g  the graphics device.
 416:    * @param c  the scroll bar component.
 417:    * @param thumbBounds  the thumb bounds.
 418:    */
 419:   private void paintThumbVertical(Graphics g, JComponent c, 
 420:           Rectangle thumbBounds)
 421:   {
 422:     int x = thumbBounds.x;
 423:     int y = thumbBounds.y;
 424:     int w = thumbBounds.width;
 425:     int h = thumbBounds.height;
 426:     
 427:     // first we fill the background
 428:     g.setColor(thumbColor);
 429:     if (isFreeStanding)
 430:       g.fillRect(x, y, w - 1, h);
 431:     else
 432:       g.fillRect(x, y, w, h);
 433:      
 434:     // then draw the dark box
 435:     g.setColor(thumbLightShadowColor);
 436:     if (isFreeStanding)
 437:       g.drawRect(x, y, w - 2, h - 1);
 438:     else
 439:       {
 440:         g.drawLine(x, y, x + w - 1, y);
 441:         g.drawLine(x, y, x, y + h - 1);
 442:         g.drawLine(x, y + h - 1, x + w - 1, y + h - 1);
 443:       }
 444:     
 445:     // then the highlight
 446:     g.setColor(thumbHighlightColor);
 447:     if (isFreeStanding)
 448:       {
 449:         g.drawLine(x + 1, y + 1, x + w - 3, y + 1);
 450:         g.drawLine(x + 1, y + 1, x + 1, y + h - 3);
 451:       }
 452:     else
 453:       {
 454:         g.drawLine(x + 1, y + 1, x + w - 1, y + 1);
 455:         g.drawLine(x + 1, y + 1, x + 1, y + h - 3);
 456:       }
 457:     
 458:     // draw the shadow line
 459:     UIDefaults def = UIManager.getLookAndFeelDefaults();
 460:     g.setColor(def.getColor("ScrollBar.shadow"));
 461:     g.drawLine(x + 1, y + h, x + w - 2, y + h);
 462:   }
 463:   
 464:   /**
 465:    * Returns the minimum thumb size.  For a free standing scroll bar the 
 466:    * minimum size is <code>17 x 17</code> pixels, whereas for a non free 
 467:    * standing scroll bar the minimum size is <code>15 x 15</code> pixels.
 468:    *
 469:    * @return The minimum thumb size.
 470:    */
 471:   protected Dimension getMinimumThumbSize()
 472:   {
 473:     if (isFreeStanding)
 474:       return MIN_THUMB_SIZE_FREE_STANDING;
 475:     else
 476:       return MIN_THUMB_SIZE;
 477:   }
 478:   
 479: }