Source for javax.swing.plaf.metal.MetalSliderUI

   1: /* MetalSliderUI.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.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:     if (slider.getOrientation() == JSlider.HORIZONTAL)
 196:       horizThumbIcon.paintIcon(slider, g, thumbRect.x, thumbRect.y);
 197:     else
 198:       vertThumbIcon.paintIcon(slider, g, thumbRect.x, thumbRect.y);
 199:   }
 200:   
 201:   /**
 202:    * Paints the track along which the thumb control moves.
 203:    * 
 204:    * @param g  the graphics device.
 205:    */
 206:   public void paintTrack(Graphics g)
 207:   {
 208:     Color shadowColor = MetalLookAndFeel.getControlShadow();
 209:     if (slider.getOrientation() == JSlider.HORIZONTAL)
 210:       {
 211:         int trackX = trackRect.x;
 212:         int trackY = trackRect.y + (trackRect.height - getTrackWidth()) / 2;
 213:         int trackW = trackRect.width - 1;
 214:         int trackH = getTrackWidth();
 215:         
 216:         // draw border
 217:         if (slider.isEnabled())
 218:           BasicGraphicsUtils.drawEtchedRect(g, trackX, trackY, trackW, trackH, 
 219:               darkShadowColor, shadowColor, darkShadowColor, highlightColor);
 220:         else
 221:           {
 222:             g.setColor(MetalLookAndFeel.getControlShadow());
 223:             g.drawRect(trackX, trackY, trackW - 2, trackH - 2);
 224:           }
 225: 
 226:         // fill track (if required)
 227:         if (filledSlider) 
 228:         {
 229:           int xPos = xPositionForValue(slider.getValue());
 230:           int x = (slider.getInverted() ? xPos : trackRect.x);
 231:           int w = (slider.getInverted() ? trackX + trackW - xPos 
 232:                   : xPos - trackRect.x);
 233:           g.setColor(MetalLookAndFeel.getControlShadow());
 234:           g.fillRect(x + 1, trackY + 1, w - 3, getTrackWidth() - 3);
 235:           if (slider.isEnabled())
 236:             {
 237:               g.setColor(MetalLookAndFeel.getControl());
 238:               g.drawLine(x + 1, trackY + 1, x + w - 3, trackY + 1);
 239:               g.drawLine(x + 1, trackY + 1, x + 1, 
 240:                       trackY + getTrackWidth() - 3);
 241:             }
 242:         }
 243:       }
 244:     else
 245:       {
 246:         int trackX = trackRect.x  + (trackRect.width - getTrackWidth()) / 2;
 247:         int trackY = trackRect.y;
 248:         int trackW = getTrackWidth();
 249:         int trackH = trackRect.height - 1;
 250:         if (slider.isEnabled())
 251:           BasicGraphicsUtils.drawEtchedRect(g, trackX, trackY, trackW, trackH, 
 252:               darkShadowColor, shadowColor, darkShadowColor, highlightColor);
 253:         else
 254:           {
 255:             g.setColor(MetalLookAndFeel.getControlShadow());
 256:             g.drawRect(trackX, trackY, trackW - 2, trackH - 2);
 257:           }
 258:         
 259:         if (filledSlider) 
 260:           {
 261:           int yPos = yPositionForValue(slider.getValue());
 262:           int y = (slider.getInverted() ? trackY : yPos);
 263:           int h = (slider.getInverted() ? yPos - trackY 
 264:                   : trackY + trackH - yPos);
 265:           g.setColor(MetalLookAndFeel.getControlShadow());
 266:           g.fillRect(trackX + 1, y + 1, getTrackWidth() - 3, h - 3);
 267:           if (slider.isEnabled())
 268:             {
 269:               g.setColor(MetalLookAndFeel.getControl());
 270:               g.drawLine(trackX + 1, y + 1, trackX + trackW - 3, y + 1);
 271:               g.drawLine(trackX + 1, y + 1, trackX + 1, y + h - 3);
 272:             }
 273:           }
 274:       }
 275:   }
 276:   
 277:   /**
 278:    * Draws the focus rectangle for the slider.  The Metal look and feel 
 279:    * indicates that the {@link JSlider} has the focus by changing the color of 
 280:    * the thumb control - this is handled elsewhere and so this method is empty 
 281:    * (it overrides the method in the {@link BasicSliderUI} class to prevent
 282:    * a default focus highlight from being drawn).
 283:    * 
 284:    * @param g  the graphics device.
 285:    */
 286:   public void paintFocus(Graphics g)
 287:   {
 288:     // do nothing as focus is shown by different color on thumb control
 289:   }
 290:   
 291:   /**
 292:    * Returns the size of the thumb icon.
 293:    * 
 294:    * @return The size of the thumb icon.
 295:    */
 296:   protected Dimension getThumbSize()
 297:   {
 298:     if (slider.getOrientation() == JSlider.HORIZONTAL)
 299:       return new Dimension(horizThumbIcon.getIconWidth(), 
 300:               horizThumbIcon.getIconHeight());
 301:     else
 302:       return new Dimension(vertThumbIcon.getIconWidth(), 
 303:               vertThumbIcon.getIconHeight());
 304:   }
 305:   
 306:   /**
 307:    * Returns the length of the major tick marks.
 308:    * 
 309:    * @return The length of the major tick marks.
 310:    */
 311:   public int getTickLength()
 312:   {
 313:     return tickLength + TICK_BUFFER;
 314:   }
 315:   
 316:   /**
 317:    * Returns the track width.
 318:    * 
 319:    * @return The track width.
 320:    */
 321:   protected int getTrackWidth()
 322:   {
 323:     return trackWidth;
 324:   }
 325:   
 326:   /**
 327:    * Returns the track length.
 328:    * 
 329:    * @return The track length.
 330:    */
 331:   protected int getTrackLength()
 332:   {
 333:     return (slider.getOrientation() == JSlider.HORIZONTAL 
 334:             ? tickRect.width : tickRect.height);
 335:   }
 336:   
 337:   /**
 338:    * Returns the thumb overhang.
 339:    * 
 340:    * @return The thumb overhang.
 341:    */
 342:   protected int getThumbOverhang()
 343:   {
 344:     // FIXME:  for what might this method be used?
 345:     return 0;
 346:   }
 347:   
 348:   protected void scrollDueToClickInTrack(int dir)
 349:   {
 350:     // FIXME:  for what might this method be overridden?
 351:     super.scrollDueToClickInTrack(dir);
 352:   }
 353:   
 354:   /**
 355:    * Paints the minor ticks for a slider with a horizontal orientation.
 356:    * 
 357:    * @param g  the graphics device.
 358:    * @param tickBounds  the tick bounds.
 359:    * @param x  the x value for the tick.
 360:    */
 361:   protected void paintMinorTickForHorizSlider(Graphics g, Rectangle tickBounds,
 362:                                               int x)
 363:   {
 364:     // Note the incoming 'g' has a translation in place to get us to the 
 365:     // start of the tick rect already...
 366:     if (slider.isEnabled())
 367:       g.setColor(MetalLookAndFeel.getPrimaryControlShadow());
 368:     else
 369:       g.setColor(MetalLookAndFeel.getControlDisabled());
 370:     g.drawLine(x, TICK_BUFFER, x, TICK_BUFFER + tickLength / 2);
 371:   }
 372:  
 373:   /**
 374:    * Paints the major ticks for a slider with a horizontal orientation.
 375:    * 
 376:    * @param g  the graphics device.
 377:    * @param tickBounds  the tick bounds.
 378:    * @param x  the x value for the tick.
 379:    */
 380:   protected void paintMajorTickForHorizSlider(Graphics g, Rectangle tickBounds,
 381:                                               int x)
 382:   {
 383:     // Note the incoming 'g' has a translation in place to get us to the 
 384:     // start of the tick rect already...
 385:     if (slider.isEnabled())
 386:       g.setColor(MetalLookAndFeel.getPrimaryControlShadow());
 387:     else
 388:       g.setColor(MetalLookAndFeel.getControlDisabled());
 389:     g.drawLine(x, TICK_BUFFER, x, TICK_BUFFER + tickLength);
 390:   }
 391:   
 392:   /**
 393:    * Paints the minor ticks for a slider with a vertical orientation.
 394:    * 
 395:    * @param g  the graphics device.
 396:    * @param tickBounds  the tick bounds.
 397:    * @param y  the y value for the tick.
 398:    */
 399:   protected void paintMinorTickForVertSlider(Graphics g, Rectangle tickBounds,
 400:                                              int y)
 401:   {
 402:     // Note the incoming 'g' has a translation in place to get us to the 
 403:     // start of the tick rect already...
 404:     if (slider.isEnabled())
 405:       g.setColor(MetalLookAndFeel.getPrimaryControlShadow());
 406:     else
 407:       g.setColor(MetalLookAndFeel.getControlDisabled());
 408:     g.drawLine(TICK_BUFFER - 1, y, TICK_BUFFER - 1 + tickLength / 2, y);
 409:   }
 410:   
 411:   /**
 412:    * Paints the major ticks for a slider with a vertical orientation.
 413:    * 
 414:    * @param g  the graphics device.
 415:    * @param tickBounds  the tick bounds.
 416:    * @param y  the y value for the tick.
 417:    */
 418:   protected void paintMajorTickForVertSlider(Graphics g, Rectangle tickBounds,
 419:                                              int y)
 420:   {
 421:     // Note the incoming 'g' has a translation in place to get us to the 
 422:     // start of the tick rect already...
 423:     if (slider.isEnabled())
 424:       g.setColor(MetalLookAndFeel.getPrimaryControlShadow());
 425:     else
 426:       g.setColor(MetalLookAndFeel.getControlDisabled());
 427:     g.drawLine(TICK_BUFFER - 1, y, TICK_BUFFER - 1 + tickLength, y);
 428:   }
 429:   
 430: }