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:     int len = tickLength + TICK_BUFFER + 1;
 356:     if (slider.getOrientation() == JSlider.VERTICAL)
 357:       len += 2;
 358:     return len;
 359:   }
 360:   
 361:   /**
 362:    * Returns the track width.
 363:    * 
 364:    * @return The track width.
 365:    */
 366:   protected int getTrackWidth()
 367:   {
 368:     return trackWidth;
 369:   }
 370:   
 371:   /**
 372:    * Returns the track length.
 373:    * 
 374:    * @return The track length.
 375:    */
 376:   protected int getTrackLength()
 377:   {
 378:     return slider.getOrientation() == JSlider.HORIZONTAL 
 379:            ? tickRect.width : tickRect.height;
 380:   }
 381:   
 382:   /**
 383:    * Returns the thumb overhang.
 384:    * 
 385:    * @return The thumb overhang.
 386:    */
 387:   protected int getThumbOverhang()
 388:   {
 389:     // FIXME:  for what might this method be used?
 390:     return 0;
 391:   }
 392:   
 393:   protected void scrollDueToClickInTrack(int dir)
 394:   {
 395:     // FIXME:  for what might this method be overridden?
 396:     super.scrollDueToClickInTrack(dir);
 397:   }
 398:   
 399:   /**
 400:    * Paints the minor ticks for a slider with a horizontal orientation.
 401:    * 
 402:    * @param g  the graphics device.
 403:    * @param tickBounds  the tick bounds.
 404:    * @param x  the x value for the tick.
 405:    */
 406:   protected void paintMinorTickForHorizSlider(Graphics g, Rectangle tickBounds,
 407:                                               int x)
 408:   {
 409:     // Note the incoming 'g' has a translation in place to get us to the 
 410:     // start of the tick rect already...
 411:     if (slider.isEnabled())
 412:       g.setColor(slider.getForeground());
 413:     else
 414:       g.setColor(MetalLookAndFeel.getControlShadow());
 415:     g.drawLine(x, TICK_BUFFER, x, TICK_BUFFER + tickLength / 2);
 416:   }
 417:  
 418:   /**
 419:    * Paints the major ticks for a slider with a horizontal orientation.
 420:    * 
 421:    * @param g  the graphics device.
 422:    * @param tickBounds  the tick bounds.
 423:    * @param x  the x value for the tick.
 424:    */
 425:   protected void paintMajorTickForHorizSlider(Graphics g, Rectangle tickBounds,
 426:                                               int x)
 427:   {
 428:     // Note the incoming 'g' has a translation in place to get us to the 
 429:     // start of the tick rect already...
 430:     if (slider.isEnabled())
 431:       g.setColor(slider.getForeground());
 432:     else
 433:       g.setColor(MetalLookAndFeel.getControlShadow());
 434:     g.drawLine(x, TICK_BUFFER, x, TICK_BUFFER + tickLength - 1);
 435:   }
 436:   
 437:   /**
 438:    * Paints the minor ticks for a slider with a vertical orientation.
 439:    * 
 440:    * @param g  the graphics device.
 441:    * @param tickBounds  the tick bounds.
 442:    * @param y  the y value for the tick.
 443:    */
 444:   protected void paintMinorTickForVertSlider(Graphics g, Rectangle tickBounds,
 445:                                              int y)
 446:   {
 447:     // Note the incoming 'g' has a translation in place to get us to the 
 448:     // start of the tick rect already...
 449:     if (slider.isEnabled())
 450:       g.setColor(slider.getForeground());
 451:     else
 452:       g.setColor(MetalLookAndFeel.getControlShadow());
 453:     g.drawLine(TICK_BUFFER, y, TICK_BUFFER + tickLength / 2, y);
 454:   }
 455:   
 456:   /**
 457:    * Paints the major ticks for a slider with a vertical orientation.
 458:    * 
 459:    * @param g  the graphics device.
 460:    * @param tickBounds  the tick bounds.
 461:    * @param y  the y value for the tick.
 462:    */
 463:   protected void paintMajorTickForVertSlider(Graphics g, Rectangle tickBounds,
 464:                                              int y)
 465:   {
 466:     // Note the incoming 'g' has a translation in place to get us to the 
 467:     // start of the tick rect already...
 468:     if (slider.isEnabled())
 469:       g.setColor(slider.getForeground());
 470:     else
 471:       g.setColor(MetalLookAndFeel.getControlShadow());
 472:     g.drawLine(TICK_BUFFER, y, TICK_BUFFER + tickLength, y);
 473:   }
 474:   
 475: }