Source for javax.swing.plaf.metal.MetalSliderUI

   1: /* MetalSliderUI.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.Rectangle;
  45: import java.beans.PropertyChangeEvent;
  46: import java.beans.PropertyChangeListener;
  47: 
  48: import javax.swing.Icon;
  49: import javax.swing.JComponent;
  50: import javax.swing.JSlider;
  51: import javax.swing.UIManager;
  52: import javax.swing.plaf.ComponentUI;
  53: import javax.swing.plaf.basic.BasicGraphicsUtils;
  54: import javax.swing.plaf.basic.BasicSliderUI;
  55: 
  56: /**
  57:  * A UI delegate for the {@link JSlider} component.
  58:  */
  59: public class MetalSliderUI extends BasicSliderUI
  60: {
  61:   /**
  62:    * A property change handler that updates the rendered component in response
  63:    * to specific property change events.  This custom handler is used to
  64:    * intercept the "JSlider.isFilled" property, which is only recognised by
  65:    * the {@link MetalLookAndFeel}.
  66:    */
  67:   protected class MetalPropertyListener
  68:     extends BasicSliderUI.PropertyChangeHandler
  69:   {
  70:     /**
  71:      * Creates a new listener.
  72:      */
  73:     protected MetalPropertyListener()
  74:     {
  75:       // Nothing to do here.
  76:     }
  77:     
  78:     /**
  79:      * Handles property change events.  Events with the name "JSlider.isFilled"
  80:      * are handled here, and other events are passed to the superclass.
  81:      * 
  82:      * @param e  the property change event.
  83:      */
  84:     public void propertyChange(PropertyChangeEvent e)
  85:     {
  86:       if (e.getPropertyName().equals(SLIDER_FILL))
  87:       {
  88:         Boolean b = (Boolean) e.getNewValue();
  89:         if (b == null)
  90:           filledSlider = false;
  91:         else
  92:           filledSlider = b.booleanValue();   
  93:       }
  94:       else
  95:         super.propertyChange(e);
  96:     }
  97:   }
  98:   
  99:   /** The thumb color (unused, because an icon is used to draw the thumb). */
 100:   protected static Color thumbColor;
 101:   
 102:   /** 
 103:    * The highlight color used for drawing the track rect when the slider is
 104:    * enabled.
 105:    */
 106:   protected static Color highlightColor;
 107:   
 108:   /**
 109:    * The shadow color used for drawing the track rect when the slider is
 110:    * enabled.
 111:    */
 112:   protected static Color darkShadowColor;
 113:   
 114:   /** The track width. */
 115:   protected static int trackWidth = UIManager.getInt("Slider.trackWidth");
 116:   
 117:   /** The length of the major tick marks. */
 118:   protected static int tickLength = UIManager.getInt("Slider.majorTickLength");
 119:   
 120:   /** The icon used for the thumb control of horizontally oriented sliders. */
 121:   protected static Icon horizThumbIcon = UIManager.getIcon(
 122:           "Slider.horizontalThumbIcon");
 123:   
 124:   /** The icon used for the thumb control of vertically oriented sliders. */
 125:   protected static Icon vertThumbIcon = UIManager.getIcon(
 126:           "Slider.verticalThumbIcon");
 127: 
 128:   /** The gap between the track and the tick marks. */
 129:   protected final int TICK_BUFFER = 4;
 130: 
 131:   /** A key to look up the filledSlider setting in the {@link UIManager}. */
 132:   protected final String SLIDER_FILL = "JSlider.isFilled";
 133:   
 134:   /** 
 135:    * A flag that controls whether or not the track is filled up to the value
 136:    * of the slider.
 137:    */
 138:   protected boolean filledSlider;
 139: 
 140:   /**
 141:    * Constructs a new instance.
 142:    */
 143:   public MetalSliderUI()
 144:   {
 145:     super(null);
 146:     filledSlider = UIManager.getBoolean(SLIDER_FILL);
 147:     darkShadowColor = MetalLookAndFeel.getControlDarkShadow();
 148:     highlightColor = MetalLookAndFeel.getControlHighlight();
 149:   }
 150: 
 151:   /**
 152:    * Returns a new instance of <code>MetalSliderUI</code>.
 153:    *
 154:    * @param component the component (ignored).
 155:    *
 156:    * @return A new instance of <code>MetalSliderUI</code>.
 157:    */
 158:   public static ComponentUI createUI(JComponent component)
 159:   {
 160:     return new MetalSliderUI();
 161:   }
 162:   
 163:   /**
 164:    * Installs the default for this UI delegate in the supplied component.
 165:    * 
 166:    * @param c  the component.
 167:    */
 168:   public void installUI(JComponent c)
 169:   {
 170:     super.installUI(c);
 171:     Boolean b = (Boolean) c.getClientProperty(SLIDER_FILL);
 172:     if (b != null) 
 173:       filledSlider = b.booleanValue();
 174:   }
 175: 
 176:   /**
 177:    * Creates a property change listener for the slider.  
 178:    * 
 179:    * @param slider  the slider.
 180:    * 
 181:    * @return A new instance of {@link MetalPropertyListener}.
 182:    */
 183:   protected PropertyChangeListener createPropertyChangeListener(JSlider slider)
 184:   {
 185:     return new MetalPropertyListener();    
 186:   }
 187:   
 188:   /**
 189:    * Paints the thumb icon for the slider.
 190:    * 
 191:    * @param g  the graphics device.
 192:    */
 193:   public void paintThumb(Graphics g) 
 194:   {
 195:     Color save = g.getColor();
 196:     g.setColor(thumbColor);
 197:     if (slider.getOrientation() == JSlider.HORIZONTAL)
 198:       horizThumbIcon.paintIcon(slider, g, thumbRect.x, thumbRect.y);
 199:     else
 200:       vertThumbIcon.paintIcon(slider, g, thumbRect.x, thumbRect.y);
 201:     g.setColor(save);
 202:   }
 203:   
 204:   /**
 205:    * Paints the track along which the thumb control moves.
 206:    * 
 207:    * @param g  the graphics device.
 208:    */
 209:   public void paintTrack(Graphics g)
 210:   {
 211:     Color shadowColor = MetalLookAndFeel.getControlShadow();
 212:     if (slider.getOrientation() == JSlider.HORIZONTAL)
 213:       {
 214:         int trackX = trackRect.x;
 215:         int trackY = trackRect.y + (trackRect.height - getTrackWidth()) / 2;
 216:         int trackW = trackRect.width;
 217:         int trackH = getTrackWidth();
 218:         
 219:         // draw border
 220:         if (slider.isEnabled())
 221:           BasicGraphicsUtils.drawEtchedRect(g, trackX, trackY, trackW, trackH, 
 222:               darkShadowColor, shadowColor, darkShadowColor, highlightColor);
 223:         else
 224:           {
 225:             g.setColor(MetalLookAndFeel.getControlShadow());
 226:             g.drawRect(trackX, trackY, trackW - 2, trackH - 2);
 227:           }
 228: 
 229:         // fill track (if required)
 230:         if (MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme)
 231:           {
 232:             if (slider.isEnabled())
 233:               {
 234:                 int xPos = xPositionForValue(slider.getValue());
 235:                 int x = slider.getInverted() ? xPos : trackRect.x;
 236:                 int w = slider.getInverted() ? trackX + trackW - xPos 
 237:                                              : xPos - trackRect.x;
 238:                 g.setColor(MetalLookAndFeel.getWhite());
 239:                 g.drawLine(x + 1, trackY + 1, x + w - 3, trackY + 1);
 240:                 g.setColor(UIManager.getColor("Slider.altTrackColor"));
 241:                 g.drawLine(x + 1, trackY + 2, x + w - 3, trackY + 2);
 242:                 g.setColor(MetalLookAndFeel.getControlShadow());
 243:                 g.drawLine(x + 1, trackY + 3, x + w - 3, trackY + 3);
 244:                 g.setColor(MetalLookAndFeel.getPrimaryControlShadow());
 245:                 g.drawLine(x + 1, trackY + 4, x + w - 3, trackY + 4);
 246:               }
 247:           }
 248:         else if (filledSlider) 
 249:           {
 250:             int xPos = xPositionForValue(slider.getValue());
 251:             int x = slider.getInverted() ? xPos : trackRect.x;
 252:             int w = slider.getInverted() ? trackX + trackW - xPos 
 253:                                          : xPos - trackRect.x;
 254:             g.setColor(MetalLookAndFeel.getControlShadow());
 255:             g.fillRect(x + 1, trackY + 1, w - 3, getTrackWidth() - 3);
 256:             if (slider.isEnabled())
 257:               {
 258:                 g.setColor(MetalLookAndFeel.getControl());
 259:                 g.drawLine(x + 1, trackY + 1, x + w - 3, trackY + 1);
 260:                 g.drawLine(x + 1, trackY + 1, x + 1, 
 261:                            trackY + getTrackWidth() - 3);
 262:               }
 263:           }
 264:       }
 265:     else
 266:       {
 267:         int trackX = trackRect.x  + (trackRect.width - getTrackWidth()) / 2;
 268:         int trackY = trackRect.y;
 269:         int trackW = getTrackWidth();
 270:         int trackH = trackRect.height;
 271:         if (slider.isEnabled())
 272:           BasicGraphicsUtils.drawEtchedRect(g, trackX, trackY, trackW, trackH, 
 273:               darkShadowColor, shadowColor, darkShadowColor, highlightColor);
 274:         else
 275:           {
 276:             g.setColor(MetalLookAndFeel.getControlShadow());
 277:             g.drawRect(trackX, trackY, trackW - 2, trackH - 2);
 278:           }
 279: 
 280:         // Fill track if necessary.
 281:         if (MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme)
 282:           {
 283:             if (slider.isEnabled())
 284:               {
 285:                 int yPos = yPositionForValue(slider.getValue());
 286:                 int y = slider.getInverted() ? trackY : yPos;
 287:                 int h = slider.getInverted() ? yPos - trackY 
 288:                         : trackY + trackH - yPos;
 289:                 
 290:                 g.setColor(MetalLookAndFeel.getWhite());
 291:                 g.drawLine(trackX + 1, y + 1, trackX + 1, y + h - 3);
 292:                 g.setColor(UIManager.getColor("Slider.altTrackColor"));
 293:                 g.drawLine(trackX + 2, y + 1, trackX + 2, y + h - 3);
 294:                 g.setColor(MetalLookAndFeel.getControlShadow());
 295:                 g.drawLine(trackX + 3, y + 1, trackX + 3, y + h - 3);
 296:                 g.setColor(MetalLookAndFeel.getPrimaryControlShadow());
 297:                 g.drawLine(trackX + 4, y + 1, trackX + 4, y + h - 3);
 298:               }
 299:           }
 300:         else if (filledSlider) 
 301:           {
 302:           int yPos = yPositionForValue(slider.getValue());
 303:           int y = slider.getInverted() ? trackY : yPos;
 304:           int h = slider.getInverted() ? yPos - trackY 
 305:                   : trackY + trackH - yPos;
 306:           g.setColor(MetalLookAndFeel.getControlShadow());
 307:           g.fillRect(trackX + 1, y + 1, getTrackWidth() - 3, h - 3);
 308:           if (slider.isEnabled())
 309:             {
 310:               g.setColor(MetalLookAndFeel.getControl());
 311:               g.drawLine(trackX + 1, y + 1, trackX + trackW - 3, y + 1);
 312:               g.drawLine(trackX + 1, y + 1, trackX + 1, y + h - 3);
 313:             }
 314:           }
 315:       }
 316:   }
 317:   
 318:   /**
 319:    * Draws the focus rectangle for the slider.  The Metal look and feel 
 320:    * indicates that the {@link JSlider} has the focus by changing the color of 
 321:    * the thumb control - this is handled elsewhere and so this method is empty 
 322:    * (it overrides the method in the {@link BasicSliderUI} class to prevent
 323:    * a default focus highlight from being drawn).
 324:    * 
 325:    * @param g  the graphics device.
 326:    */
 327:   public void paintFocus(Graphics g)
 328:   {
 329:     thumbColor = getFocusColor();
 330:     paintThumb(g);
 331:   }
 332:   
 333:   /**
 334:    * Returns the size of the thumb icon.
 335:    * 
 336:    * @return The size of the thumb icon.
 337:    */
 338:   protected Dimension getThumbSize()
 339:   {
 340:     if (slider.getOrientation() == JSlider.HORIZONTAL)
 341:       return new Dimension(horizThumbIcon.getIconWidth(), 
 342:               horizThumbIcon.getIconHeight());
 343:     else
 344:       return new Dimension(vertThumbIcon.getIconWidth(), 
 345:               vertThumbIcon.getIconHeight());
 346:   }
 347:   
 348:   /**
 349:    * Returns the length of the major tick marks.
 350:    * 
 351:    * @return The length of the major tick marks.
 352:    */
 353:   public int getTickLength()
 354:   {
 355:     return tickLength + TICK_BUFFER;
 356:   }
 357:   
 358:   /**
 359:    * Returns the track width.
 360:    * 
 361:    * @return The track width.
 362:    */
 363:   protected int getTrackWidth()
 364:   {
 365:     return trackWidth;
 366:   }
 367:   
 368:   /**
 369:    * Returns the track length.
 370:    * 
 371:    * @return The track length.
 372:    */
 373:   protected int getTrackLength()
 374:   {
 375:     return slider.getOrientation() == JSlider.HORIZONTAL 
 376:            ? tickRect.width : tickRect.height;
 377:   }
 378:   
 379:   /**
 380:    * Returns the thumb overhang.
 381:    * 
 382:    * @return The thumb overhang.
 383:    */
 384:   protected int getThumbOverhang()
 385:   {
 386:     // FIXME:  for what might this method be used?
 387:     return 0;
 388:   }
 389:   
 390:   protected void scrollDueToClickInTrack(int dir)
 391:   {
 392:     // FIXME:  for what might this method be overridden?
 393:     super.scrollDueToClickInTrack(dir);
 394:   }
 395:   
 396:   /**
 397:    * Paints the minor ticks for a slider with a horizontal orientation.
 398:    * 
 399:    * @param g  the graphics device.
 400:    * @param tickBounds  the tick bounds.
 401:    * @param x  the x value for the tick.
 402:    */
 403:   protected void paintMinorTickForHorizSlider(Graphics g, Rectangle tickBounds,
 404:                                               int x)
 405:   {
 406:     // Note the incoming 'g' has a translation in place to get us to the 
 407:     // start of the tick rect already...
 408:     if (slider.isEnabled())
 409:       g.setColor(MetalLookAndFeel.getPrimaryControlShadow());
 410:     else
 411:       g.setColor(MetalLookAndFeel.getControlDisabled());
 412:     g.drawLine(x, TICK_BUFFER, x, TICK_BUFFER + tickLength / 2);
 413:   }
 414:  
 415:   /**
 416:    * Paints the major ticks for a slider with a horizontal orientation.
 417:    * 
 418:    * @param g  the graphics device.
 419:    * @param tickBounds  the tick bounds.
 420:    * @param x  the x value for the tick.
 421:    */
 422:   protected void paintMajorTickForHorizSlider(Graphics g, Rectangle tickBounds,
 423:                                               int x)
 424:   {
 425:     // Note the incoming 'g' has a translation in place to get us to the 
 426:     // start of the tick rect already...
 427:     if (slider.isEnabled())
 428:       g.setColor(MetalLookAndFeel.getPrimaryControlShadow());
 429:     else
 430:       g.setColor(MetalLookAndFeel.getControlDisabled());
 431:     g.drawLine(x, TICK_BUFFER, x, TICK_BUFFER + tickLength);
 432:   }
 433:   
 434:   /**
 435:    * Paints the minor ticks for a slider with a vertical orientation.
 436:    * 
 437:    * @param g  the graphics device.
 438:    * @param tickBounds  the tick bounds.
 439:    * @param y  the y value for the tick.
 440:    */
 441:   protected void paintMinorTickForVertSlider(Graphics g, Rectangle tickBounds,
 442:                                              int y)
 443:   {
 444:     // Note the incoming 'g' has a translation in place to get us to the 
 445:     // start of the tick rect already...
 446:     if (slider.isEnabled())
 447:       g.setColor(MetalLookAndFeel.getPrimaryControlShadow());
 448:     else
 449:       g.setColor(MetalLookAndFeel.getControlDisabled());
 450:     g.drawLine(TICK_BUFFER - 1, y, TICK_BUFFER - 1 + tickLength / 2, y);
 451:   }
 452:   
 453:   /**
 454:    * Paints the major ticks for a slider with a vertical orientation.
 455:    * 
 456:    * @param g  the graphics device.
 457:    * @param tickBounds  the tick bounds.
 458:    * @param y  the y value for the tick.
 459:    */
 460:   protected void paintMajorTickForVertSlider(Graphics g, Rectangle tickBounds,
 461:                                              int y)
 462:   {
 463:     // Note the incoming 'g' has a translation in place to get us to the 
 464:     // start of the tick rect already...
 465:     if (slider.isEnabled())
 466:       g.setColor(MetalLookAndFeel.getPrimaryControlShadow());
 467:     else
 468:       g.setColor(MetalLookAndFeel.getControlDisabled());
 469:     g.drawLine(TICK_BUFFER - 1, y, TICK_BUFFER - 1 + tickLength, y);
 470:   }
 471:   
 472: }