OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-dev/] [or1k-gcc/] [libjava/] [classpath/] [javax/] [swing/] [plaf/] [basic/] [BasicSliderUI.java] - Blame information for rev 772

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 772 jeremybenn
/* BasicSliderUI.java --
2
   Copyright (C) 2004, 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.basic;
40
 
41
import java.awt.Color;
42
import java.awt.Component;
43
import java.awt.Dimension;
44
import java.awt.Graphics;
45
import java.awt.Insets;
46
import java.awt.Point;
47
import java.awt.Polygon;
48
import java.awt.Rectangle;
49
import java.awt.event.ActionEvent;
50
import java.awt.event.ActionListener;
51
import java.awt.event.ComponentAdapter;
52
import java.awt.event.ComponentEvent;
53
import java.awt.event.ComponentListener;
54
import java.awt.event.FocusEvent;
55
import java.awt.event.FocusListener;
56
import java.awt.event.MouseEvent;
57
import java.beans.PropertyChangeEvent;
58
import java.beans.PropertyChangeListener;
59
import java.util.Dictionary;
60
import java.util.Enumeration;
61
 
62
import javax.swing.AbstractAction;
63
import javax.swing.ActionMap;
64
import javax.swing.BoundedRangeModel;
65
import javax.swing.InputMap;
66
import javax.swing.JComponent;
67
import javax.swing.JSlider;
68
import javax.swing.LookAndFeel;
69
import javax.swing.SwingUtilities;
70
import javax.swing.Timer;
71
import javax.swing.UIManager;
72
import javax.swing.event.ChangeEvent;
73
import javax.swing.event.ChangeListener;
74
import javax.swing.event.MouseInputAdapter;
75
import javax.swing.plaf.ActionMapUIResource;
76
import javax.swing.plaf.ComponentUI;
77
import javax.swing.plaf.SliderUI;
78
 
79
/**
80
 * <p>
81
 * BasicSliderUI.java This is the UI delegate in the Basic look and feel that
82
 * paints JSliders.
83
 * </p>
84
 *
85
 * <p>
86
 * The UI delegate keeps track of 6 rectangles that place the various parts of
87
 * the JSlider inside the component.
88
 * </p>
89
 *
90
 * <p>
91
 * The rectangles are organized as follows:
92
 * </p>
93
 * <pre>
94
 *     +-------------------------------------------------------+ <-- focusRect
95
 *     |                                                       |
96
 *     |  +==+-------------------+==+--------------------+==+<------ contentRect
97
 *     |  |  |                   |  |<---thumbRect       |  |  |
98
 *     |  |  |    TRACK          |  |                    |<--------- trackRect
99
 *     |  |  +-------------------+==+--------------------+  |  |
100
 *     |  |  |                                           |  |  |
101
 *     |  |  |          TICKS GO HERE                    |<-------- tickRect
102
 *     |  |  |                                           |  |  |
103
 *     |  +==+-------------------------------------------+==+  |
104
 *     |  |  |                                           |  |  |
105
 *     |  |  |                                           |  |<----- labelRect
106
 *     |  |  |                 LABELS GO HERE            |  |  |
107
 *     |  |  |                                           |  |  |
108
 *     |  |  |                                           |  |  |
109
 *     |  |  |                                           |  |  |
110
 *     |  |  |                                           |  |  |
111
 *     |  |                                              |  |  |
112
 * </pre>
113
 *
114
 * <p>
115
 * The space between the contentRect and the focusRect are the FocusInsets.
116
 * </p>
117
 *
118
 * <p>
119
 * The space between the focusRect and the component bounds is the insetCache
120
 * which are the component's insets.
121
 * </p>
122
 *
123
 * <p>
124
 * The top of the thumb is the top of the contentRect. The trackRect has to be
125
 * as tall as the thumb.
126
 * </p>
127
 *
128
 * <p>
129
 * The trackRect and tickRect do not start from the left edge of the
130
 * focusRect. They are trackBuffer away from each side of the focusRect. This
131
 * is so that the thumb has room to move.
132
 * </p>
133
 *
134
 * <p>
135
 * The labelRect does start right against the contentRect's left and right
136
 * edges and it gets all remaining space.
137
 * </p>
138
 */
139
public class BasicSliderUI extends SliderUI
140
{
141
  /**
142
   * Helper class that listens to the {@link JSlider}'s model for changes.
143
   *
144
   * @specnote Apparently this class was intended to be protected,
145
   *           but was made public by a compiler bug and is now
146
   *           public for compatibility.
147
   */
148
  public class ChangeHandler implements ChangeListener
149
  {
150
    /**
151
     * Called when the slider's model has been altered. The UI delegate should
152
     * recalculate any rectangles that are dependent on the model for their
153
     * positions and repaint.
154
     *
155
     * @param e A static {@link ChangeEvent} passed from the model.
156
     */
157
    public void stateChanged(ChangeEvent e)
158
    {
159
      // Maximum, minimum, and extent values will be taken
160
      // care of automatically when the slider is repainted.
161
      // Only thing that needs recalculation is the thumb.
162
      calculateThumbLocation();
163
      slider.repaint();
164
    }
165
  }
166
 
167
  /**
168
   * Helper class that listens for resize events.
169
   *
170
   * @specnote Apparently this class was intended to be protected,
171
   *           but was made public by a compiler bug and is now
172
   *           public for compatibility.
173
   */
174
  public class ComponentHandler extends ComponentAdapter
175
  {
176
    /**
177
     * Called when the size of the component changes. The UI delegate should
178
     * recalculate any rectangles that are dependent on the model for their
179
     * positions and repaint.
180
     *
181
     * @param e A {@link ComponentEvent}.
182
     */
183
    public void componentResized(ComponentEvent e)
184
    {
185
      calculateGeometry();
186
      slider.repaint();
187
    }
188
  }
189
 
190
  /**
191
   * Helper class that listens for focus events.
192
   *
193
   * @specnote Apparently this class was intended to be protected,
194
   *           but was made public by a compiler bug and is now
195
   *           public for compatibility.
196
   */
197
  public class FocusHandler implements FocusListener
198
  {
199
    /**
200
     * Called when the {@link JSlider} has gained focus.  It should repaint
201
     * the slider with the focus drawn.
202
     *
203
     * @param e A {@link FocusEvent}.
204
     */
205
    public void focusGained(FocusEvent e)
206
    {
207
      slider.repaint();
208
    }
209
 
210
    /**
211
     * Called when the {@link JSlider} has lost focus. It  should repaint the
212
     * slider without the focus drawn.
213
     *
214
     * @param e A {@link FocusEvent}.
215
     */
216
    public void focusLost(FocusEvent e)
217
    {
218
      slider.repaint();
219
    }
220
  }
221
 
222
  /**
223
   * Helper class that listens for changes to the properties of the {@link
224
   * JSlider}.
225
   */
226
  public class PropertyChangeHandler implements PropertyChangeListener
227
  {
228
    /**
229
     * Called when one of the properties change. The UI should recalculate any
230
     * rectangles if necessary and repaint.
231
     *
232
     * @param e A {@link PropertyChangeEvent}.
233
     */
234
    public void propertyChange(PropertyChangeEvent e)
235
    {
236
      // Check for orientation changes.
237
      String prop = e.getPropertyName();
238
      if (prop.equals("orientation")
239
          || prop.equals("inverted")
240
          || prop.equals("labelTable")
241
          || prop.equals("majorTickSpacing")
242
          || prop.equals("minorTickSpacing")
243
          || prop.equals("paintTicks")
244
          || prop.equals("paintTrack")
245
          || prop.equals("paintLabels"))
246
        {
247
          calculateGeometry();
248
          slider.repaint();
249
        }
250
      else if (e.getPropertyName().equals("model"))
251
        {
252
          BoundedRangeModel oldModel = (BoundedRangeModel) e.getOldValue();
253
          oldModel.removeChangeListener(changeListener);
254
          slider.getModel().addChangeListener(changeListener);
255
          calculateThumbLocation();
256
          slider.repaint();
257
        }
258
    }
259
  }
260
 
261
  /**
262
   * Helper class that listens to our swing timer. This class is responsible
263
   * for listening to the timer and moving the thumb in the proper direction
264
   * every interval.
265
   *
266
   * @specnote Apparently this class was intended to be protected,
267
   *           but was made public by a compiler bug and is now
268
   *           public for compatibility.
269
   */
270
  public class ScrollListener implements ActionListener
271
  {
272
    /** Indicates which direction the thumb should scroll. */
273
    private transient int direction;
274
 
275
    /** Indicates whether we should scroll in blocks or in units. */
276
    private transient boolean block;
277
 
278
    /**
279
     * Creates a new ScrollListener object.
280
     */
281
    public ScrollListener()
282
    {
283
      direction = POSITIVE_SCROLL;
284
      block = false;
285
    }
286
 
287
    /**
288
     * Creates a new ScrollListener object.
289
     *
290
     * @param dir The direction to scroll in.
291
     * @param block If movement will be in blocks.
292
     */
293
    public ScrollListener(int dir, boolean block)
294
    {
295
      direction = dir;
296
      this.block = block;
297
    }
298
 
299
    /**
300
     * Called every time the swing timer reaches its interval. If the thumb
301
     * needs to move, then this method will move the thumb one block or  unit
302
     * in the direction desired. Otherwise, the timer can be stopped.
303
     *
304
     * @param e An {@link ActionEvent}.
305
     */
306
    public void actionPerformed(ActionEvent e)
307
    {
308
      if (! trackListener.shouldScroll(direction))
309
        {
310
          scrollTimer.stop();
311
          return;
312
        }
313
 
314
      if (block)
315
        scrollByBlock(direction);
316
      else
317
        scrollByUnit(direction);
318
    }
319
 
320
    /**
321
     * Sets the direction to scroll in.
322
     *
323
     * @param direction The direction to scroll in.
324
     */
325
    public void setDirection(int direction)
326
    {
327
      this.direction = direction;
328
    }
329
 
330
    /**
331
     * Sets whether movement will be in blocks.
332
     *
333
     * @param block If movement will be in blocks.
334
     */
335
    public void setScrollByBlock(boolean block)
336
    {
337
      this.block = block;
338
    }
339
  }
340
 
341
  /**
342
   * Helper class that listens for mouse events.
343
   *
344
   * @specnote Apparently this class was intended to be protected,
345
   *           but was made public by a compiler bug and is now
346
   *           public for compatibility.
347
   */
348
  public class TrackListener extends MouseInputAdapter
349
  {
350
    /** The current X position of the mouse. */
351
    protected int currentMouseX;
352
 
353
    /** The current Y position of the mouse. */
354
    protected int currentMouseY;
355
 
356
    /**
357
     * The offset between the current slider value and the cursor's position.
358
     */
359
    protected int offset;
360
 
361
    /**
362
     * Called when the mouse has been dragged. This should find the mouse's
363
     * current position and adjust the value of the {@link JSlider}
364
     * accordingly.
365
     *
366
     * @param e A {@link MouseEvent}
367
     */
368
    public void mouseDragged(MouseEvent e)
369
    {
370
      dragging = true;
371
      if (slider.isEnabled())
372
        {
373
          currentMouseX = e.getX();
374
          currentMouseY = e.getY();
375
          if (slider.getValueIsAdjusting())
376
            {
377
              int value;
378
              if (slider.getOrientation() == JSlider.HORIZONTAL)
379
                value = valueForXPosition(currentMouseX) - offset;
380
              else
381
                value = valueForYPosition(currentMouseY) - offset;
382
 
383
              slider.setValue(value);
384
            }
385
        }
386
    }
387
 
388
    /**
389
     * Called when the mouse has moved over a component but no buttons have
390
     * been pressed yet.
391
     *
392
     * @param e A {@link MouseEvent}
393
     */
394
    public void mouseMoved(MouseEvent e)
395
    {
396
      // Don't care that we're moved unless we're dragging.
397
    }
398
 
399
    /**
400
     * Called when the mouse is pressed. When the press occurs on the thumb
401
     * itself, the {@link JSlider} should have its value set to where the
402
     * mouse was pressed. If the press occurs on the track, then the thumb
403
     * should move one block towards the direction of the mouse.
404
     *
405
     * @param e A {@link MouseEvent}
406
     */
407
    public void mousePressed(MouseEvent e)
408
    {
409
      if (slider.isEnabled())
410
        {
411
          currentMouseX = e.getX();
412
          currentMouseY = e.getY();
413
 
414
          int value;
415
          if (slider.getOrientation() == JSlider.HORIZONTAL)
416
            value = valueForXPosition(currentMouseX);
417
          else
418
            value = valueForYPosition(currentMouseY);
419
 
420
          if (slider.getSnapToTicks())
421
            value = findClosestTick(value);
422
 
423
          // If the thumb is hit, then we don't need to set the timers to
424
          // move it.
425
          if (! thumbRect.contains(e.getPoint()))
426
            {
427
              // The mouse has hit some other part of the slider.
428
              // The value moves no matter where in the slider you hit.
429
              if (value > slider.getValue())
430
                scrollDueToClickInTrack(POSITIVE_SCROLL);
431
              else
432
                scrollDueToClickInTrack(NEGATIVE_SCROLL);
433
            }
434
          else
435
            {
436
              slider.setValueIsAdjusting(true);
437
              offset = value - slider.getValue();
438
            }
439
        }
440
    }
441
 
442
    /**
443
     * Called when the mouse is released.  This should stop the timer that
444
     * scrolls the thumb.
445
     *
446
     * @param e A {@link MouseEvent}
447
     */
448
    public void mouseReleased(MouseEvent e)
449
    {
450
      dragging = false;
451
      if (slider.isEnabled())
452
        {
453
          currentMouseX = e.getX();
454
          currentMouseY = e.getY();
455
 
456
          if (slider.getValueIsAdjusting())
457
            {
458
              slider.setValueIsAdjusting(false);
459
              if (slider.getSnapToTicks())
460
                slider.setValue(findClosestTick(slider.getValue()));
461
            }
462
          if (scrollTimer != null)
463
            scrollTimer.stop();
464
        }
465
      slider.repaint();
466
    }
467
 
468
    /**
469
     * Indicates whether the thumb should scroll in the given direction.
470
     *
471
     * @param direction The direction to check.
472
     *
473
     * @return True if the thumb should move in that direction.
474
     */
475
    public boolean shouldScroll(int direction)
476
    {
477
      int value;
478
      if (slider.getOrientation() == JSlider.HORIZONTAL)
479
        value = valueForXPosition(currentMouseX);
480
      else
481
        value = valueForYPosition(currentMouseY);
482
 
483
      if (direction == POSITIVE_SCROLL)
484
        return value > slider.getValue();
485
      else
486
        return value < slider.getValue();
487
    }
488
  }
489
 
490
  /**
491
   * This class is no longer used as of JDK1.3.
492
   */
493
  public class ActionScroller extends AbstractAction
494
  {
495
    /**
496
     * Not used.
497
     *
498
     * @param slider not used
499
     * @param dir not used
500
     * @param block not used
501
     */
502
    public ActionScroller(JSlider slider, int dir, boolean block)
503
    {
504
      // Not used.
505
    }
506
 
507
    /**
508
     * Not used.
509
     *
510
     * @param event not used
511
     */
512
    public void actionPerformed(ActionEvent event)
513
    {
514
      // Not used.
515
    }
516
  }
517
 
518
  /** Listener for changes from the model. */
519
  protected ChangeListener changeListener;
520
 
521
  /** Listener for changes to the {@link JSlider}. */
522
  protected PropertyChangeListener propertyChangeListener;
523
 
524
  /** Listener for the scrollTimer. */
525
  protected ScrollListener scrollListener;
526
 
527
  /** Listener for component resizing. */
528
  protected ComponentListener componentListener;
529
 
530
  /** Listener for focus handling. */
531
  protected FocusListener focusListener;
532
 
533
  /** Listener for mouse events. */
534
  protected TrackListener trackListener;
535
 
536
  /** The insets between the FocusRectangle and the ContentRectangle. */
537
  protected Insets focusInsets;
538
 
539
  /** The {@link JSlider}'s insets. */
540
  protected Insets insetCache;
541
 
542
  /** Rectangle describing content bounds. See diagram above. */
543
  protected Rectangle contentRect;
544
 
545
  /** Rectangle describing focus bounds. See diagram above. */
546
  protected Rectangle focusRect;
547
 
548
  /** Rectangle describing the thumb's bounds. See diagram above. */
549
  protected Rectangle thumbRect;
550
 
551
  /** Rectangle describing the tick bounds. See diagram above. */
552
  protected Rectangle tickRect;
553
 
554
  /** Rectangle describing the label bounds. See diagram above. */
555
  protected Rectangle labelRect;
556
 
557
  /** Rectangle describing the track bounds. See diagram above. */
558
  protected Rectangle trackRect;
559
 
560
  /** FIXME: use this somewhere. */
561
  public static final int MAX_SCROLL = 2;
562
 
563
  /** FIXME: use this somewhere. */
564
  public static final int MIN_SCROLL = -2;
565
 
566
  /** A constant describing scrolling towards the minimum. */
567
  public static final int NEGATIVE_SCROLL = -1;
568
 
569
  /** A constant describing scrolling towards the maximum. */
570
  public static final int POSITIVE_SCROLL = 1;
571
 
572
  /** The gap between the edges of the contentRect and trackRect. */
573
  protected int trackBuffer;
574
 
575
  /** Whether this slider is actually drawn left to right. */
576
  protected boolean leftToRightCache;
577
 
578
  /** A timer that periodically moves the thumb. */
579
  protected Timer scrollTimer;
580
 
581
  /** A reference to the {@link JSlider} that this UI was created for. */
582
  protected JSlider slider;
583
 
584
  /** The shadow color. */
585
  private transient Color shadowColor;
586
 
587
  /** The highlight color. */
588
  private transient Color highlightColor;
589
 
590
  /** The focus color. */
591
  private transient Color focusColor;
592
 
593
  /** True if the user is dragging the slider. */
594
  boolean dragging;
595
 
596
  /**
597
   * Creates a new Basic look and feel Slider UI.
598
   *
599
   * @param b The {@link JSlider} that this UI was created for.
600
   */
601
  public BasicSliderUI(JSlider b)
602
  {
603
    super();
604
  }
605
 
606
  /**
607
   * Returns true if the user is dragging the slider.
608
   *
609
   * @return true if the slider is being dragged.
610
   *
611
   * @since 1.5
612
   */
613
  protected boolean isDragging()
614
  {
615
    return dragging;
616
  }
617
 
618
  /**
619
   * Gets the shadow color to be used for this slider. The shadow color is the
620
   * color used for drawing the top and left edges of the track.
621
   *
622
   * @return The shadow color.
623
   */
624
  protected Color getShadowColor()
625
  {
626
    return shadowColor;
627
  }
628
 
629
  /**
630
   * Gets the highlight color to be used for this slider. The highlight color
631
   * is the color used for drawing the bottom and right edges of the track.
632
   *
633
   * @return The highlight color.
634
   */
635
  protected Color getHighlightColor()
636
  {
637
    return highlightColor;
638
  }
639
 
640
  /**
641
   * Gets the focus color to be used for this slider. The focus color is the
642
   * color used for drawing the focus rectangle when the component gains
643
   * focus.
644
   *
645
   * @return The focus color.
646
   */
647
  protected Color getFocusColor()
648
  {
649
    return focusColor;
650
  }
651
 
652
  /**
653
   * Factory method to create a BasicSliderUI for the given {@link
654
   * JComponent}, which should be a {@link JSlider}.
655
   *
656
   * @param b The {@link JComponent} a UI is being created for.
657
   *
658
   * @return A BasicSliderUI for the {@link JComponent}.
659
   */
660
  public static ComponentUI createUI(JComponent b)
661
  {
662
    return new BasicSliderUI((JSlider) b);
663
  }
664
 
665
  /**
666
   * Installs and initializes all fields for this UI delegate. Any properties
667
   * of the UI that need to be initialized and/or set to defaults will be
668
   * done now. It will also install any listeners necessary.
669
   *
670
   * @param c The {@link JComponent} that is having this UI installed.
671
   */
672
  public void installUI(JComponent c)
673
  {
674
    super.installUI(c);
675
    if (c instanceof JSlider)
676
      {
677
        slider = (JSlider) c;
678
 
679
        focusRect = new Rectangle();
680
        contentRect = new Rectangle();
681
        thumbRect = new Rectangle();
682
        trackRect = new Rectangle();
683
        tickRect = new Rectangle();
684
        labelRect = new Rectangle();
685
 
686
        insetCache = slider.getInsets();
687
        leftToRightCache = ! slider.getInverted();
688
 
689
        scrollTimer = new Timer(200, null);
690
        scrollTimer.setRepeats(true);
691
 
692
        installDefaults(slider);
693
        installListeners(slider);
694
        installKeyboardActions(slider);
695
 
696
        calculateFocusRect();
697
 
698
        calculateContentRect();
699
        calculateThumbSize();
700
        calculateTrackBuffer();
701
        calculateTrackRect();
702
        calculateThumbLocation();
703
 
704
        calculateTickRect();
705
        calculateLabelRect();
706
      }
707
  }
708
 
709
  /**
710
   * Performs the opposite of installUI. Any properties or resources that need
711
   * to be cleaned up will be done now. It will also uninstall any listeners
712
   * it has. In addition, any properties of this UI will be nulled.
713
   *
714
   * @param c The {@link JComponent} that is having this UI uninstalled.
715
   */
716
  public void uninstallUI(JComponent c)
717
  {
718
    super.uninstallUI(c);
719
 
720
    uninstallKeyboardActions(slider);
721
    uninstallListeners(slider);
722
 
723
    scrollTimer = null;
724
 
725
    focusRect = null;
726
    contentRect = null;
727
    thumbRect = null;
728
    trackRect = null;
729
    tickRect = null;
730
    labelRect = null;
731
 
732
    focusInsets = null;
733
  }
734
 
735
  /**
736
   * Initializes any default properties that this UI has from the defaults for
737
   * the Basic look and feel.
738
   *
739
   * @param slider The {@link JSlider} that is having this UI installed.
740
   */
741
  protected void installDefaults(JSlider slider)
742
  {
743
    LookAndFeel.installColors(slider, "Slider.background",
744
                              "Slider.foreground");
745
    LookAndFeel.installBorder(slider, "Slider.border");
746
    shadowColor = UIManager.getColor("Slider.shadow");
747
    highlightColor = UIManager.getColor("Slider.highlight");
748
    focusColor = UIManager.getColor("Slider.focus");
749
    focusInsets = UIManager.getInsets("Slider.focusInsets");
750
    slider.setOpaque(true);
751
  }
752
 
753
  /**
754
   * Creates a new {@link TrackListener}.
755
   *
756
   * @param slider The {@link JSlider} that this {@link TrackListener} is
757
   *        created for.
758
   *
759
   * @return A new {@link TrackListener}.
760
   */
761
  protected TrackListener createTrackListener(JSlider slider)
762
  {
763
    return new TrackListener();
764
  }
765
 
766
  /**
767
   * Creates a new {@link ChangeListener}.
768
   *
769
   * @param slider The {@link JSlider} that this {@link ChangeListener} is
770
   *        created for.
771
   *
772
   * @return A new {@link ChangeListener}.
773
   */
774
  protected ChangeListener createChangeListener(JSlider slider)
775
  {
776
    return new ChangeHandler();
777
  }
778
 
779
  /**
780
   * Creates a new {@link ComponentListener}.
781
   *
782
   * @param slider The {@link JSlider} that this {@link ComponentListener} is
783
   *        created for.
784
   *
785
   * @return A new {@link ComponentListener}.
786
   */
787
  protected ComponentListener createComponentListener(JSlider slider)
788
  {
789
    return new ComponentHandler();
790
  }
791
 
792
  /**
793
   * Creates a new {@link FocusListener}.
794
   *
795
   * @param slider The {@link JSlider} that this {@link FocusListener} is
796
   *        created for.
797
   *
798
   * @return A new {@link FocusListener}.
799
   */
800
  protected FocusListener createFocusListener(JSlider slider)
801
  {
802
    return new FocusHandler();
803
  }
804
 
805
  /**
806
   * Creates a new {@link ScrollListener}.
807
   *
808
   * @param slider The {@link JSlider} that this {@link ScrollListener} is
809
   *        created for.
810
   *
811
   * @return A new {@link ScrollListener}.
812
   */
813
  protected ScrollListener createScrollListener(JSlider slider)
814
  {
815
    return new ScrollListener();
816
  }
817
 
818
  /**
819
   * Creates a new {@link PropertyChangeListener}.
820
   *
821
   * @param slider The {@link JSlider} that this {@link
822
   *        PropertyChangeListener} is created for.
823
   *
824
   * @return A new {@link PropertyChangeListener}.
825
   */
826
  protected PropertyChangeListener createPropertyChangeListener(JSlider slider)
827
  {
828
    return new PropertyChangeHandler();
829
  }
830
 
831
  /**
832
   * Creates and registers all the listeners for this UI delegate. This
833
   * includes creating the ScrollListener and registering it to the timer.
834
   *
835
   * @param slider The {@link JSlider} is having listeners installed.
836
   */
837
  protected void installListeners(JSlider slider)
838
  {
839
    propertyChangeListener = createPropertyChangeListener(slider);
840
    componentListener = createComponentListener(slider);
841
    trackListener = createTrackListener(slider);
842
    focusListener = createFocusListener(slider);
843
    changeListener = createChangeListener(slider);
844
    scrollListener = createScrollListener(slider);
845
 
846
    slider.addPropertyChangeListener(propertyChangeListener);
847
    slider.addComponentListener(componentListener);
848
    slider.addMouseListener(trackListener);
849
    slider.addMouseMotionListener(trackListener);
850
    slider.addFocusListener(focusListener);
851
    slider.getModel().addChangeListener(changeListener);
852
 
853
    scrollTimer.addActionListener(scrollListener);
854
  }
855
 
856
  /**
857
   * Unregisters all the listeners that this UI delegate was using. In
858
   * addition, it will also null any listeners that it was using.
859
   *
860
   * @param slider The {@link JSlider} that is having listeners removed.
861
   */
862
  protected void uninstallListeners(JSlider slider)
863
  {
864
    slider.removePropertyChangeListener(propertyChangeListener);
865
    slider.removeComponentListener(componentListener);
866
    slider.removeMouseListener(trackListener);
867
    slider.removeMouseMotionListener(trackListener);
868
    slider.removeFocusListener(focusListener);
869
    slider.getModel().removeChangeListener(changeListener);
870
 
871
    scrollTimer.removeActionListener(scrollListener);
872
 
873
    propertyChangeListener = null;
874
    componentListener = null;
875
    trackListener = null;
876
    focusListener = null;
877
    changeListener = null;
878
    scrollListener = null;
879
  }
880
 
881
  /**
882
   * Installs any keyboard actions. The list of keys that need to be bound are
883
   * listed in Basic look and feel's defaults.
884
   *
885
   * @param slider The {@link JSlider} that is having keyboard actions
886
   *        installed.
887
   */
888
  protected void installKeyboardActions(JSlider slider)
889
  {
890
    InputMap keyMap = getInputMap(JComponent.WHEN_FOCUSED);
891
    SwingUtilities.replaceUIInputMap(slider, JComponent.WHEN_FOCUSED, keyMap);
892
    ActionMap map = getActionMap();
893
    SwingUtilities.replaceUIActionMap(slider, map);
894
  }
895
 
896
  /**
897
   * Uninstalls any keyboard actions. The list of keys used  are listed in
898
   * Basic look and feel's defaults.
899
   *
900
   * @param slider The {@link JSlider} that is having keyboard actions
901
   *        uninstalled.
902
   */
903
  protected void uninstallKeyboardActions(JSlider slider)
904
  {
905
    SwingUtilities.replaceUIActionMap(slider, null);
906
    SwingUtilities.replaceUIInputMap(slider, JComponent.WHEN_FOCUSED, null);
907
  }
908
 
909
  /* XXX: This is all after experimentation with SUN's implementation.
910
 
911
     PreferredHorizontalSize seems to be 200x21.
912
     PreferredVerticalSize seems to be 21x200.
913
 
914
     MinimumHorizontalSize seems to be 36x21.
915
     MinimumVerticalSize seems to be 21x36.
916
 
917
     PreferredSize seems to be 200x63. Or Components.getBounds?
918
 
919
     MinimumSize seems to be 36x63.
920
 
921
     MaximumSize seems to be 32767x63.
922
   */
923
 
924
  /**
925
   * This method returns the preferred size when the slider is horizontally
926
   * oriented.
927
   *
928
   * @return The dimensions of the preferred horizontal size.
929
   */
930
  public Dimension getPreferredHorizontalSize()
931
  {
932
    Dimension dim = UIManager.getDimension("Slider.horizontalSize");
933
    if (dim == null) // Just to be sure we mirror the default.
934
      dim = new Dimension(200, 21);
935
    return dim;
936
  }
937
 
938
  /**
939
   * This method returns the preferred size when the slider is vertically
940
   * oriented.
941
   *
942
   * @return The dimensions of the preferred vertical size.
943
   */
944
  public Dimension getPreferredVerticalSize()
945
  {
946
    Dimension dim = UIManager.getDimension("Slider.verticalSize");
947
    if (dim == null) // Just to be sure we mirror the default.
948
      dim = new Dimension(21, 200);
949
    return dim;
950
  }
951
 
952
  /**
953
   * This method returns the minimum size when the slider is horizontally
954
   * oriented.
955
   *
956
   * @return The dimensions of the minimum horizontal size.
957
   */
958
  public Dimension getMinimumHorizontalSize()
959
  {
960
    Dimension dim = UIManager.getDimension("Slider.minimumHorizontalSize");
961
    if (dim == null) // Just to be sure we mirror the default.
962
      dim = new Dimension(36, 21);
963
    return dim;
964
  }
965
 
966
  /**
967
   * This method returns the minimum size of the slider when it  is vertically
968
   * oriented.
969
   *
970
   * @return The dimensions of the minimum vertical size.
971
   */
972
  public Dimension getMinimumVerticalSize()
973
  {
974
    Dimension dim = UIManager.getDimension("Slider.minimumVerticalSize");
975
    if (dim == null) // Just to be sure we mirror the default.
976
      dim = new Dimension(21, 36);
977
    return dim;
978
  }
979
 
980
  /**
981
   * This method returns the preferred size of the component. If it returns
982
   * null, then it is up to the Layout Manager to give the {@link JComponent}
983
   * a size.
984
   *
985
   * @param c The {@link JComponent} to find the preferred size for.
986
   *
987
   * @return The dimensions of the preferred size.
988
   */
989
  public Dimension getPreferredSize(JComponent c)
990
  {
991
    recalculateIfInsetsChanged();
992
    Dimension dim;
993
    if (slider.getOrientation() == JSlider.HORIZONTAL)
994
      {
995
        // Create copy here to protect the UIManager value.
996
        dim = new Dimension(getPreferredHorizontalSize());
997
        dim.height = insetCache.top + insetCache.bottom;
998
        dim.height += focusInsets.top + focusInsets.bottom;
999
        dim.height += trackRect.height + tickRect.height + labelRect.height;
1000
      }
1001
    else
1002
      {
1003
        // Create copy here to protect the UIManager value.
1004
        dim = new Dimension(getPreferredVerticalSize());
1005
        dim.width = insetCache.left + insetCache.right;
1006
        dim.width += focusInsets.left + focusInsets.right;
1007
        dim.width += trackRect.width + tickRect.width + labelRect.width;
1008
      }
1009
    return dim;
1010
  }
1011
 
1012
  /**
1013
   * This method returns the minimum size for this {@link JSlider}  for this
1014
   * look and feel. If it returns null, then it is up to the Layout Manager
1015
   * to give the {@link JComponent} a size.
1016
   *
1017
   * @param c The {@link JComponent} to find the minimum size for.
1018
   *
1019
   * @return The dimensions of the minimum size.
1020
   */
1021
  public Dimension getMinimumSize(JComponent c)
1022
  {
1023
    recalculateIfInsetsChanged();
1024
    Dimension dim;
1025
    if (slider.getOrientation() == JSlider.HORIZONTAL)
1026
      {
1027
        // Create copy here to protect the UIManager value.
1028
        dim = new Dimension(getMinimumHorizontalSize());
1029
        dim.height = insetCache.top + insetCache.bottom;
1030
        dim.height += focusInsets.top + focusInsets.bottom;
1031
        dim.height += trackRect.height + tickRect.height + labelRect.height;
1032
      }
1033
    else
1034
      {
1035
        // Create copy here to protect the UIManager value.
1036
        dim = new Dimension(getMinimumVerticalSize());
1037
        dim.width = insetCache.left + insetCache.right;
1038
        dim.width += focusInsets.left + focusInsets.right;
1039
        dim.width += trackRect.width + tickRect.width + labelRect.width;
1040
      }
1041
    return dim;
1042
  }
1043
 
1044
  /**
1045
   * This method returns the maximum size for this {@link JSlider} for this
1046
   * look and feel.
1047
   *
1048
   * @param c The {@link JComponent} to find a maximum size for.
1049
   *
1050
   * @return The dimensions of the maximum size.
1051
   */
1052
  public Dimension getMaximumSize(JComponent c)
1053
  {
1054
    Dimension dim = getPreferredSize(c);
1055
    if (slider.getOrientation() == JSlider.HORIZONTAL)
1056
      dim.width = Short.MAX_VALUE;
1057
    else
1058
      dim.height = Short.MAX_VALUE;
1059
    return dim;
1060
  }
1061
 
1062
  /**
1063
   * This method calculates all the sizes of the rectangles by delegating to
1064
   * the helper methods calculateXXXRect.
1065
   */
1066
  protected void calculateGeometry()
1067
  {
1068
    calculateFocusRect();
1069
    calculateContentRect();
1070
    calculateThumbSize();
1071
    calculateTrackBuffer();
1072
    calculateTrackRect();
1073
    calculateTickRect();
1074
    calculateLabelRect();
1075
    calculateThumbLocation();
1076
  }
1077
 
1078
  /**
1079
   * This method calculates the size and position of the focusRect. This
1080
   * method does not need to be called if the orientation changes.
1081
   */
1082
  protected void calculateFocusRect()
1083
  {
1084
    focusRect.x = insetCache.left;
1085
    focusRect.y = insetCache.top;
1086
    focusRect.width = slider.getWidth() - insetCache.left - insetCache.right;
1087
    focusRect.height = slider.getHeight() - insetCache.top - insetCache.bottom;
1088
  }
1089
 
1090
  /**
1091
   * Sets the width and height of the <code>thumbRect</code> field, using the
1092
   * dimensions returned by {@link #getThumbSize()}.
1093
   */
1094
  protected void calculateThumbSize()
1095
  {
1096
    Dimension d = getThumbSize();
1097
    thumbRect.width = d.width;
1098
    thumbRect.height = d.height;
1099
  }
1100
 
1101
  /**
1102
   * Updates the <code>contentRect</code> field to an area inside the
1103
   * <code>focusRect</code>. This method does not need to be called if the
1104
   * orientation changes.
1105
   */
1106
  protected void calculateContentRect()
1107
  {
1108
    contentRect.x = focusRect.x + focusInsets.left;
1109
    contentRect.y = focusRect.y + focusInsets.top;
1110
 
1111
    contentRect.width = focusRect.width - focusInsets.left - focusInsets.right;
1112
    contentRect.height = focusRect.height - focusInsets.top
1113
                         - focusInsets.bottom;
1114
  }
1115
 
1116
  /**
1117
   * Calculates the position of the thumbRect based on the current value of
1118
   * the slider. It must take into  account the orientation of the slider.
1119
   */
1120
  protected void calculateThumbLocation()
1121
  {
1122
    int value = slider.getValue();
1123
 
1124
    if (slider.getOrientation() == JSlider.HORIZONTAL)
1125
      {
1126
        thumbRect.x = xPositionForValue(value) - thumbRect.width / 2;
1127
        thumbRect.y = trackRect.y + 1;
1128
      }
1129
    else
1130
      {
1131
        thumbRect.x = trackRect.x + 1;
1132
        thumbRect.y = yPositionForValue(value) - thumbRect.height / 2;
1133
      }
1134
  }
1135
 
1136
  /**
1137
   * Calculates the gap size between the edge of the <code>contentRect</code>
1138
   * and the edge of the <code>trackRect</code>, storing the result in the
1139
   * <code>trackBuffer</code> field.  Sufficient space needs to be reserved
1140
   * for the slider thumb and/or the labels at each end of the slider track.
1141
   */
1142
  protected void calculateTrackBuffer()
1143
  {
1144
    if (slider.getOrientation() == JSlider.HORIZONTAL)
1145
      {
1146
        int w = Math.max(getWidthOfLowValueLabel(), getWidthOfHighValueLabel());
1147
        trackBuffer = Math.max(thumbRect.width / 2, w / 2);
1148
 
1149
      }
1150
    else
1151
      {
1152
        int h = Math.max(getHeightOfLowValueLabel(),
1153
                         getHeightOfHighValueLabel());
1154
        trackBuffer = Math.max(thumbRect.height / 2, h / 2);
1155
      }
1156
  }
1157
 
1158
  /**
1159
   * Returns the size of the slider's thumb.  The size is hard coded to
1160
   * <code>11 x 20</code> for horizontal sliders, and <code>20 x 11</code> for
1161
   * vertical sliders. Note that a new instance of {@link Dimension} is
1162
   * returned for every call to this method (this seems wasteful, but
1163
   * {@link Dimension} instances are not immutable, so this is probably
1164
   * unavoidable).
1165
   *
1166
   * @return The size of the slider's thumb.
1167
   */
1168
  protected Dimension getThumbSize()
1169
  {
1170
    if (slider.getOrientation() == JSlider.HORIZONTAL)
1171
      return new Dimension(11, 20);
1172
    else
1173
      return new Dimension(20, 11);
1174
  }
1175
 
1176
  /**
1177
   * Calculates the size and position of the trackRect. It must take into
1178
   * account the orientation of the slider.
1179
   */
1180
  protected void calculateTrackRect()
1181
  {
1182
    if (slider.getOrientation() == JSlider.HORIZONTAL)
1183
      {
1184
        int center = thumbRect.height;
1185
        if (slider.getPaintTicks())
1186
          center += getTickLength();
1187
        if (slider.getPaintLabels())
1188
          center += getHeightOfTallestLabel();
1189
        trackRect.x = contentRect.x + trackBuffer;
1190
        trackRect.y = contentRect.y + (contentRect.height - center - 1) / 2;
1191
        trackRect.width = contentRect.width - 2 * trackBuffer;
1192
        trackRect.height = thumbRect.height;
1193
      }
1194
    else
1195
      {
1196
        int center = thumbRect.width;
1197
        if (slider.getPaintTicks())
1198
          center += getTickLength();
1199
        if (slider.getPaintLabels())
1200
          center += getWidthOfWidestLabel();
1201
        trackRect.x = contentRect.x + (contentRect.width - center - 1) / 2;
1202
        trackRect.y = contentRect.y + trackBuffer;
1203
        trackRect.width = thumbRect.width;
1204
        trackRect.height = contentRect.height - 2 * trackBuffer;
1205
      }
1206
  }
1207
 
1208
  /**
1209
   * This method returns the height of the tick area box if the slider  is
1210
   * horizontal and the width of the tick area box is the slider is vertical.
1211
   * It not necessarily how long the ticks will be. If a gap between the edge
1212
   * of tick box and the actual tick is desired, then that will need to be
1213
   * handled in the tick painting methods.
1214
   *
1215
   * @return The height (or width if the slider is vertical) of the tick
1216
   *         rectangle.
1217
   */
1218
  protected int getTickLength()
1219
  {
1220
    return 8;
1221
  }
1222
 
1223
  /**
1224
   * This method calculates the size and position of the tickRect. It must
1225
   * take into account the orientation of the slider.
1226
   */
1227
  protected void calculateTickRect()
1228
  {
1229
    if (slider.getOrientation() == JSlider.HORIZONTAL)
1230
      {
1231
        tickRect.x = trackRect.x;
1232
        tickRect.y = trackRect.y + trackRect.height;
1233
        tickRect.width = trackRect.width;
1234
        tickRect.height = getTickLength();
1235
 
1236
        // this makes our Mauve tests pass...can't explain it!
1237
        if (!slider.getPaintTicks())
1238
          {
1239
            tickRect.y--;
1240
            tickRect.height = 0;
1241
          }
1242
      }
1243
    else
1244
      {
1245
        tickRect.x = trackRect.x + trackRect.width;
1246
        tickRect.y = trackRect.y;
1247
        tickRect.width = getTickLength();
1248
        tickRect.height = trackRect.height;
1249
 
1250
        // this makes our Mauve tests pass...can't explain it!
1251
        if (!slider.getPaintTicks())
1252
          {
1253
            tickRect.x--;
1254
            tickRect.width = 0;
1255
          }
1256
      }
1257
  }
1258
 
1259
  /**
1260
   * Calculates the <code>labelRect</code> field, taking into account the
1261
   * orientation of the slider.
1262
   */
1263
  protected void calculateLabelRect()
1264
  {
1265
    if (slider.getOrientation() == JSlider.HORIZONTAL)
1266
      {
1267
        if (slider.getPaintLabels())
1268
          {
1269
            labelRect.x = tickRect.x - trackBuffer;
1270
            labelRect.y = tickRect.y + tickRect.height;
1271
            labelRect.width = tickRect.width + trackBuffer * 2;
1272
            labelRect.height = getHeightOfTallestLabel();
1273
          }
1274
        else
1275
          {
1276
            labelRect.x = tickRect.x;
1277
            labelRect.y = tickRect.y + tickRect.height;
1278
            labelRect.width = tickRect.width;
1279
            labelRect.height = 0;
1280
          }
1281
      }
1282
    else
1283
      {
1284
        if (slider.getPaintLabels())
1285
          {
1286
            labelRect.x = tickRect.x + tickRect.width;
1287
            labelRect.y = tickRect.y - trackBuffer;
1288
            labelRect.width = getWidthOfWidestLabel();
1289
            labelRect.height = tickRect.height + trackBuffer * 2;
1290
          }
1291
        else
1292
          {
1293
            labelRect.x = tickRect.x + tickRect.width;
1294
            labelRect.y = tickRect.y;
1295
            labelRect.width = 0;
1296
            labelRect.height = tickRect.height;
1297
          }
1298
      }
1299
  }
1300
 
1301
  /**
1302
   * This method returns the width of the widest label  in the slider's label
1303
   * table.
1304
   *
1305
   * @return The width of the widest label or 0 if no label table exists.
1306
   */
1307
  protected int getWidthOfWidestLabel()
1308
  {
1309
    int widest = 0;
1310
    Dictionary table = slider.getLabelTable();
1311
    if (table != null)
1312
      {
1313
        for (Enumeration list = slider.getLabelTable().elements();
1314
             list.hasMoreElements();)
1315
          {
1316
            Component label = (Component) list.nextElement();
1317
            widest = Math.max(label.getPreferredSize().width, widest);
1318
          }
1319
      }
1320
    return widest;
1321
  }
1322
 
1323
  /**
1324
   * This method returns the height of the tallest label in the slider's label
1325
   * table.
1326
   *
1327
   * @return The height of the tallest label or 0 if no label table exists.
1328
   */
1329
  protected int getHeightOfTallestLabel()
1330
  {
1331
    int tallest = 0;
1332
    Component label;
1333
 
1334
    if (slider.getLabelTable() == null)
1335
      return 0;
1336
    Dimension pref;
1337
    for (Enumeration list = slider.getLabelTable().elements();
1338
         list.hasMoreElements();)
1339
      {
1340
        Object comp = list.nextElement();
1341
        if (! (comp instanceof Component))
1342
          continue;
1343
        label = (Component) comp;
1344
        pref = label.getPreferredSize();
1345
        if (pref != null && pref.height > tallest)
1346
          tallest = pref.height;
1347
      }
1348
    return tallest;
1349
  }
1350
 
1351
  /**
1352
   * Returns the width of the label whose key has the highest value, or 0 if
1353
   * there are no labels.
1354
   *
1355
   * @return The width of the label whose key has the highest value.
1356
   *
1357
   * @see #getHighestValueLabel()
1358
   */
1359
  protected int getWidthOfHighValueLabel()
1360
  {
1361
    Component highValueLabel = getHighestValueLabel();
1362
    if (highValueLabel != null)
1363
      return highValueLabel.getPreferredSize().width;
1364
    else
1365
      return 0;
1366
  }
1367
 
1368
  /**
1369
   * Returns the width of the label whose key has the lowest value, or 0 if
1370
   * there are no labels.
1371
   *
1372
   * @return The width of the label whose key has the lowest value.
1373
   *
1374
   * @see #getLowestValueLabel()
1375
   */
1376
  protected int getWidthOfLowValueLabel()
1377
  {
1378
    Component lowValueLabel = getLowestValueLabel();
1379
    if (lowValueLabel != null)
1380
      return lowValueLabel.getPreferredSize().width;
1381
    else
1382
      return 0;
1383
  }
1384
 
1385
  /**
1386
   * Returns the height of the label whose key has the highest value, or 0 if
1387
   * there are no labels.
1388
   *
1389
   * @return The height of the high value label or 0 if no label table exists.
1390
   */
1391
  protected int getHeightOfHighValueLabel()
1392
  {
1393
    Component highValueLabel = getHighestValueLabel();
1394
    if (highValueLabel != null)
1395
      return highValueLabel.getPreferredSize().height;
1396
    else
1397
      return 0;
1398
  }
1399
 
1400
  /**
1401
   * Returns the height of the label whose key has the lowest value, or 0 if
1402
   * there are no labels.
1403
   *
1404
   * @return The height of the low value label or 0 if no label table exists.
1405
   */
1406
  protected int getHeightOfLowValueLabel()
1407
  {
1408
    Component lowValueLabel = getLowestValueLabel();
1409
    if (lowValueLabel != null)
1410
      return lowValueLabel.getPreferredSize().height;
1411
    else
1412
      return 0;
1413
  }
1414
 
1415
  /**
1416
   * Returns <code>true</code> if the slider scale is to be drawn inverted,
1417
   * and <code>false</code> if not.
1418
   *
1419
   * @return <code>true</code> if the slider is to be drawn inverted.
1420
   */
1421
  protected boolean drawInverted()
1422
  {
1423
    return slider.getInverted();
1424
  }
1425
 
1426
  /**
1427
   * This method returns the label whose key has the lowest value.
1428
   *
1429
   * @return The low value label or null if no label table exists.
1430
   */
1431
  protected Component getLowestValueLabel()
1432
  {
1433
    Integer key = new Integer(Integer.MAX_VALUE);
1434
    Integer tmpKey;
1435
    Dictionary labelTable = slider.getLabelTable();
1436
 
1437
    if (labelTable == null)
1438
      return null;
1439
 
1440
    for (Enumeration list = labelTable.keys(); list.hasMoreElements();)
1441
      {
1442
        Object value = list.nextElement();
1443
        if (! (value instanceof Integer))
1444
          continue;
1445
        tmpKey = (Integer) value;
1446
        if (tmpKey.intValue() < key.intValue())
1447
          key = tmpKey;
1448
      }
1449
    Object comp = labelTable.get(key);
1450
    if (! (comp instanceof Component))
1451
      return null;
1452
    return (Component) comp;
1453
  }
1454
 
1455
  /**
1456
   * Returns the label whose key has the highest value.
1457
   *
1458
   * @return The label whose key has the highest value or <code>null</code> if
1459
   *     no label table exists.
1460
   */
1461
  protected Component getHighestValueLabel()
1462
  {
1463
    Integer key = new Integer(Integer.MIN_VALUE);
1464
    Integer tmpKey;
1465
    Dictionary labelTable = slider.getLabelTable();
1466
 
1467
    if (labelTable == null)
1468
      return null;
1469
 
1470
    for (Enumeration list = labelTable.keys(); list.hasMoreElements();)
1471
      {
1472
        Object value = list.nextElement();
1473
        if (! (value instanceof Integer))
1474
          continue;
1475
        tmpKey = (Integer) value;
1476
        if (tmpKey.intValue() > key.intValue())
1477
          key = tmpKey;
1478
      }
1479
    Object comp = labelTable.get(key);
1480
    if (! (comp instanceof Component))
1481
      return null;
1482
    return (Component) comp;
1483
  }
1484
 
1485
  /**
1486
   * This method is used to paint the {@link JSlider}. It delegates all its
1487
   * duties to the various paint methods like paintTicks(),  paintTrack(),
1488
   * paintThumb(), etc.
1489
   *
1490
   * @param g The {@link Graphics} object to paint with.
1491
   * @param c The {@link JComponent} that is being painted.
1492
   */
1493
  public void paint(Graphics g, JComponent c)
1494
  {
1495
    recalculateIfInsetsChanged();
1496
    recalculateIfOrientationChanged();
1497
    if (slider.getPaintTrack() && hitClip(g, trackRect))
1498
      paintTrack(g);
1499
    if (slider.getPaintTicks() && hitClip(g, tickRect))
1500
      paintTicks(g);
1501
    if (slider.getPaintLabels() && hitClip(g, labelRect))
1502
      paintLabels(g);
1503
    if (slider.hasFocus() && hitClip(g, focusRect))
1504
      paintFocus(g);
1505
    if (hitClip(g, thumbRect))
1506
      paintThumb(g);
1507
  }
1508
 
1509
  /**
1510
   * This method recalculates any rectangles that need to be recalculated
1511
   * after the insets of the component have changed.
1512
   */
1513
  protected void recalculateIfInsetsChanged()
1514
  {
1515
    Insets insets = slider.getInsets();
1516
    if (! insets.equals(insetCache))
1517
      {
1518
        insetCache = insets;
1519
        calculateGeometry();
1520
      }
1521
  }
1522
 
1523
  /**
1524
   * This method recalculates any rectangles that need to be recalculated
1525
   * after the orientation of the slider changes.
1526
   */
1527
  protected void recalculateIfOrientationChanged()
1528
  {
1529
    // Examining a test program shows that either Sun calls private
1530
    // methods that we don't know about, or these don't do anything.
1531
    calculateThumbSize();
1532
    calculateTrackBuffer();
1533
    calculateTrackRect();
1534
    calculateThumbLocation();
1535
 
1536
    calculateTickRect();
1537
    calculateLabelRect();
1538
  }
1539
 
1540
  /**
1541
   * This method is called during a repaint if the slider has focus. It draws
1542
   * an outline of the  focusRect using the color returned by
1543
   * getFocusColor().
1544
   *
1545
   * @param g The {@link Graphics} object to draw with.
1546
   */
1547
  public void paintFocus(Graphics g)
1548
  {
1549
    Color saved_color = g.getColor();
1550
 
1551
    g.setColor(getFocusColor());
1552
 
1553
    g.drawRect(focusRect.x, focusRect.y, focusRect.width, focusRect.height);
1554
 
1555
    g.setColor(saved_color);
1556
  }
1557
 
1558
  /**
1559
   * <p>
1560
   * This method is called during a repaint if the  track is to be drawn. It
1561
   * draws a 3D rectangle to  represent the track. The track is not the size
1562
   * of the trackRect. The top and left edges of the track should be outlined
1563
   * with the shadow color. The bottom and right edges should be outlined
1564
   * with the highlight color.
1565
   * </p>
1566
   * <pre>
1567
   *    a---d
1568
   *    |   |
1569
   *    |   |   a------------------------d
1570
   *    |   |   |                        |
1571
   *    |   |   b------------------------c
1572
   *    |   |
1573
   *    |   |
1574
   *    b---c
1575
   * </pre>
1576
   *
1577
   * <p>
1578
   * The b-a-d path needs to be drawn with the shadow color and the b-c-d path
1579
   * needs to be drawn with the highlight color.
1580
   * </p>
1581
   *
1582
   * @param g The {@link Graphics} object to draw with.
1583
   */
1584
  public void paintTrack(Graphics g)
1585
  {
1586
    Color saved_color = g.getColor();
1587
    int width;
1588
    int height;
1589
 
1590
    Point a = new Point(trackRect.x, trackRect.y + 1);
1591
    Point b = new Point(a);
1592
    Point c = new Point(a);
1593
    Point d = new Point(a);
1594
 
1595
    if (slider.getOrientation() == JSlider.HORIZONTAL)
1596
      {
1597
        width = trackRect.width;
1598
        height = (thumbRect.height / 4 == 0) ? 1 : thumbRect.height / 4;
1599
 
1600
        a.translate(0, (trackRect.height / 2) - (height / 2));
1601
        b.translate(0, (trackRect.height / 2) + (height / 2));
1602
        c.translate(trackRect.width, (trackRect.height / 2) + (height / 2));
1603
        d.translate(trackRect.width, (trackRect.height / 2) - (height / 2));
1604
      }
1605
    else
1606
      {
1607
        width = (thumbRect.width / 4 == 0) ? 1 : thumbRect.width / 4;
1608
        height = trackRect.height;
1609
 
1610
        a.translate((trackRect.width / 2) - (width / 2), 0);
1611
        b.translate((trackRect.width / 2) - (width / 2), trackRect.height);
1612
        c.translate((trackRect.width / 2) + (width / 2), trackRect.height);
1613
        d.translate((trackRect.width / 2) + (width / 2), 0);
1614
      }
1615
    g.setColor(Color.GRAY);
1616
    g.fillRect(a.x, a.y, width, height);
1617
 
1618
    g.setColor(getHighlightColor());
1619
    g.drawLine(b.x, b.y, c.x, c.y);
1620
    g.drawLine(c.x, c.y, d.x, d.y);
1621
 
1622
    g.setColor(getShadowColor());
1623
    g.drawLine(b.x, b.y, a.x, a.y);
1624
    g.drawLine(a.x, a.y, d.x, d.y);
1625
 
1626
    g.setColor(saved_color);
1627
  }
1628
 
1629
  /**
1630
   * This method is called during a repaint if the ticks are to be drawn. This
1631
   * method must still verify that the majorTickSpacing and minorTickSpacing
1632
   * are greater than zero before drawing the ticks.
1633
   *
1634
   * @param g The {@link Graphics} object to draw with.
1635
   */
1636
  public void paintTicks(Graphics g)
1637
  {
1638
    int max = slider.getMaximum();
1639
    int min = slider.getMinimum();
1640
    int majorSpace = slider.getMajorTickSpacing();
1641
    int minorSpace = slider.getMinorTickSpacing();
1642
 
1643
    if (majorSpace > 0)
1644
      {
1645
        if (slider.getOrientation() == JSlider.HORIZONTAL)
1646
          {
1647
            g.translate(0, tickRect.y);
1648
            for (int i = min; i <= max; i += majorSpace)
1649
              paintMajorTickForHorizSlider(g, tickRect, xPositionForValue(i));
1650
            g.translate(0, -tickRect.y);
1651
          }
1652
        else // JSlider.VERTICAL
1653
          {
1654
            g.translate(tickRect.x, 0);
1655
            for (int i = min; i <= max; i += majorSpace)
1656
              paintMajorTickForVertSlider(g, tickRect, yPositionForValue(i));
1657
            g.translate(-tickRect.x, 0);
1658
          }
1659
      }
1660
    if (minorSpace > 0)
1661
      {
1662
        if (slider.getOrientation() == JSlider.HORIZONTAL)
1663
          {
1664
            g.translate(0, tickRect.y);
1665
            for (int i = min; i <= max; i += minorSpace)
1666
              paintMinorTickForHorizSlider(g, tickRect, xPositionForValue(i));
1667
            g.translate(0, -tickRect.y);
1668
          }
1669
        else
1670
          {
1671
            g.translate(tickRect.x, 0);
1672
            for (int i = min; i <= max; i += minorSpace)
1673
              paintMinorTickForVertSlider(g, tickRect, yPositionForValue(i));
1674
            g.translate(-tickRect.x, 0);
1675
          }
1676
      }
1677
  }
1678
 
1679
  /* Minor ticks start at 1/4 of the height (or width) of the tickRect and
1680
     extend to 1/2 of the tickRect.
1681
 
1682
     Major ticks start at 1/4 of the height and extend to 3/4.
1683
   */
1684
 
1685
  /**
1686
   * This method paints a minor tick for a horizontal slider at the given x
1687
   * value. x represents the x coordinate to paint at.
1688
   *
1689
   * @param g The {@link Graphics} object to draw with.
1690
   * @param tickBounds The tickRect rectangle.
1691
   * @param x The x coordinate to draw the tick at.
1692
   */
1693
  protected void paintMinorTickForHorizSlider(Graphics g,
1694
                                              Rectangle tickBounds, int x)
1695
  {
1696
    int y = tickRect.height / 4;
1697
    Color saved = g.getColor();
1698
    g.setColor(Color.BLACK);
1699
 
1700
    g.drawLine(x, y, x, y + tickRect.height / 4);
1701
    g.setColor(saved);
1702
  }
1703
 
1704
  /**
1705
   * This method paints a major tick for a horizontal slider at the given x
1706
   * value. x represents the x coordinate to paint at.
1707
   *
1708
   * @param g The {@link Graphics} object to draw with.
1709
   * @param tickBounds The tickRect rectangle.
1710
   * @param x The x coordinate to draw the tick at.
1711
   */
1712
  protected void paintMajorTickForHorizSlider(Graphics g,
1713
                                              Rectangle tickBounds, int x)
1714
  {
1715
    int y = tickRect.height / 4;
1716
    Color saved = g.getColor();
1717
    g.setColor(Color.BLACK);
1718
 
1719
    g.drawLine(x, y, x, y + tickRect.height / 2);
1720
    g.setColor(saved);
1721
  }
1722
 
1723
  /**
1724
   * This method paints a minor tick for a vertical slider at the given y
1725
   * value. y represents the y coordinate to paint at.
1726
   *
1727
   * @param g The {@link Graphics} object to draw with.
1728
   * @param tickBounds The tickRect rectangle.
1729
   * @param y The y coordinate to draw the tick at.
1730
   */
1731
  protected void paintMinorTickForVertSlider(Graphics g, Rectangle tickBounds,
1732
                                             int y)
1733
  {
1734
    int x = tickRect.width / 4;
1735
    Color saved = g.getColor();
1736
    g.setColor(Color.BLACK);
1737
 
1738
    g.drawLine(x, y, x + tickRect.width / 4, y);
1739
    g.setColor(saved);
1740
  }
1741
 
1742
  /**
1743
   * This method paints a major tick for a vertical slider at the given y
1744
   * value. y represents the y coordinate to paint at.
1745
   *
1746
   * @param g The {@link Graphics} object to draw with.
1747
   * @param tickBounds The tickRect rectangle.
1748
   * @param y The y coordinate to draw the tick at.
1749
   */
1750
  protected void paintMajorTickForVertSlider(Graphics g, Rectangle tickBounds,
1751
                                             int y)
1752
  {
1753
    int x = tickRect.width / 4;
1754
    Color saved = g.getColor();
1755
    g.setColor(Color.BLACK);
1756
 
1757
    g.drawLine(x, y, x + tickRect.width / 2, y);
1758
    g.setColor(saved);
1759
  }
1760
 
1761
  /**
1762
   * This method paints all the labels from the slider's label table. This
1763
   * method must make sure that the label table is not null before painting
1764
   * the labels. Each entry in the label table is a (integer, component)
1765
   * pair. Every label is painted at the value of the integer.
1766
   *
1767
   * @param g The {@link Graphics} object to draw with.
1768
   */
1769
  public void paintLabels(Graphics g)
1770
  {
1771
    Dictionary table = slider.getLabelTable();
1772
    if (table != null)
1773
      {
1774
        int min = slider.getMinimum();
1775
        int max = slider.getMaximum();
1776
        for (Enumeration list = table.keys(); list.hasMoreElements();)
1777
          {
1778
            Integer key = (Integer) list.nextElement();
1779
            int value = key.intValue();
1780
            if (value >= min && value <= max)
1781
              {
1782
                Component label = (Component) table.get(key);
1783
                if (slider.getOrientation() == JSlider.HORIZONTAL)
1784
                  {
1785
                    g.translate(0, labelRect.y);
1786
                    paintHorizontalLabel(g, value, label);
1787
                    g.translate(0, -labelRect.y);
1788
                  }
1789
                else
1790
                  {
1791
                    g.translate(labelRect.x, 0);
1792
                    paintVerticalLabel(g, value, label);
1793
                    g.translate(-labelRect.x, 0);
1794
                  }
1795
              }
1796
          }
1797
      }
1798
  }
1799
 
1800
  /**
1801
   * This method paints the label on the horizontal slider at the value
1802
   * specified. The value is not a coordinate. It is a value within the range
1803
   * of the  slider. If the value is not within the range of the slider, this
1804
   * method will do nothing. This method should not paint outside the
1805
   * boundaries of the labelRect.
1806
   *
1807
   * @param g The {@link Graphics} object to draw with.
1808
   * @param value The value to paint at.
1809
   * @param label The label to paint.
1810
   */
1811
  protected void paintHorizontalLabel(Graphics g, int value, Component label)
1812
  {
1813
    int center = xPositionForValue(value);
1814
    int left = center - label.getPreferredSize().width / 2;
1815
    g.translate(left, 0);
1816
    label.paint(g);
1817
    g.translate(-left, 0);
1818
  }
1819
 
1820
  /**
1821
   * This method paints the label on the vertical slider at the value
1822
   * specified. The value is not a coordinate. It is a value within the range
1823
   * of the  slider. If the value is not within the range of the slider, this
1824
   * method will do nothing. This method should not paint outside the
1825
   * boundaries of the labelRect.
1826
   *
1827
   * @param g The {@link Graphics} object to draw with.
1828
   * @param value The value to paint at.
1829
   * @param label The label to paint.
1830
   */
1831
  protected void paintVerticalLabel(Graphics g, int value, Component label)
1832
  {
1833
    int center = yPositionForValue(value);
1834
    int top = center - label.getPreferredSize().height / 2;
1835
    g.translate(0, top);
1836
    label.paint(g);
1837
    g.translate(0, -top);
1838
  }
1839
 
1840
  /**
1841
   * <p>
1842
   * This method paints a thumb. There are two types of thumb:
1843
   * </p>
1844
   * <pre>
1845
   *   Vertical         Horizontal
1846
   *    a---b            a-----b
1847
   *    |   |            |      \
1848
   *    e   c            |       c
1849
   *     \ /             |      /
1850
   *      d              e-----d
1851
   *  </pre>
1852
   *
1853
   * <p>
1854
   * In the case of vertical thumbs, we highlight the path b-a-e-d and shadow
1855
   * the path b-c-d. In the case of horizontal thumbs, we highlight the path
1856
   * c-b-a-e and shadow the path c-d-e. In both cases we fill the path
1857
   * a-b-c-d-e before shadows and highlights are drawn.
1858
   * </p>
1859
   *
1860
   * @param g The graphics object to paint with
1861
   */
1862
  public void paintThumb(Graphics g)
1863
  {
1864
    Color saved_color = g.getColor();
1865
 
1866
    Point a = new Point(thumbRect.x, thumbRect.y);
1867
    Point b = new Point(a);
1868
    Point c = new Point(a);
1869
    Point d = new Point(a);
1870
    Point e = new Point(a);
1871
 
1872
    Polygon bright;
1873
    Polygon light; // light shadow
1874
    Polygon dark; // dark shadow
1875
    Polygon all;
1876
 
1877
    // This will be in X-dimension if the slider is inverted and y if it isn't.
1878
    int turnPoint;
1879
 
1880
    if (slider.getOrientation() == JSlider.HORIZONTAL)
1881
      {
1882
        turnPoint = thumbRect.height * 3 / 4;
1883
 
1884
        b.translate(thumbRect.width - 1, 0);
1885
        c.translate(thumbRect.width - 1, turnPoint);
1886
        d.translate(thumbRect.width / 2 - 1, thumbRect.height - 1);
1887
        e.translate(0, turnPoint);
1888
 
1889
        bright = new Polygon(new int[] { b.x - 1, a.x, e.x, d.x },
1890
                             new int[] { b.y, a.y, e.y, d.y }, 4);
1891
 
1892
        dark = new Polygon(new int[] { b.x, c.x, d.x + 1 }, new int[] { b.y,
1893
                                                                       c.y - 1,
1894
                                                                       d.y }, 3);
1895
 
1896
        light = new Polygon(new int[] { b.x - 1, c.x - 1, d.x + 1 },
1897
                            new int[] { b.y + 1, c.y - 1, d.y - 1 }, 3);
1898
 
1899
        all = new Polygon(
1900
                          new int[] { a.x + 1, b.x - 2, c.x - 2, d.x, e.x + 1 },
1901
                          new int[] { a.y + 1, b.y + 1, c.y - 1, d.y - 1, e.y },
1902
                          5);
1903
      }
1904
    else
1905
      {
1906
        turnPoint = thumbRect.width * 3 / 4 - 1;
1907
 
1908
        b.translate(turnPoint, 0);
1909
        c.translate(thumbRect.width - 1, thumbRect.height / 2);
1910
        d.translate(turnPoint, thumbRect.height - 1);
1911
        e.translate(0, thumbRect.height - 1);
1912
 
1913
        bright = new Polygon(new int[] { c.x - 1, b.x, a.x, e.x },
1914
                             new int[] { c.y - 1, b.y, a.y, e.y - 1 }, 4);
1915
 
1916
        dark = new Polygon(new int[] { c.x, d.x, e.x }, new int[] { c.y, d.y,
1917
                                                                   e.y }, 3);
1918
 
1919
        light = new Polygon(new int[] { c.x - 1, d.x, e.x + 1 },
1920
                            new int[] { c.y, d.y - 1, e.y - 1 }, 3);
1921
        all = new Polygon(new int[] { a.x + 1, b.x, c.x - 2, c.x - 2, d.x,
1922
                                     e.x + 1 }, new int[] { a.y + 1, b.y + 1,
1923
                                                           c.y - 1, c.y,
1924
                                                           d.y - 2, e.y - 2 },
1925
                          6);
1926
      }
1927
 
1928
    g.setColor(Color.WHITE);
1929
    g.drawPolyline(bright.xpoints, bright.ypoints, bright.npoints);
1930
 
1931
    g.setColor(Color.BLACK);
1932
    g.drawPolyline(dark.xpoints, dark.ypoints, dark.npoints);
1933
 
1934
    g.setColor(Color.GRAY);
1935
    g.drawPolyline(light.xpoints, light.ypoints, light.npoints);
1936
 
1937
    g.setColor(Color.LIGHT_GRAY);
1938
    g.drawPolyline(all.xpoints, all.ypoints, all.npoints);
1939
    g.fillPolygon(all);
1940
 
1941
    g.setColor(saved_color);
1942
  }
1943
 
1944
  /**
1945
   * This method sets the position of the thumbRect.
1946
   *
1947
   * @param x The new x position.
1948
   * @param y The new y position.
1949
   */
1950
  public void setThumbLocation(int x, int y)
1951
  {
1952
    Rectangle union = new Rectangle(thumbRect);
1953
    thumbRect.setLocation(x, y);
1954
    SwingUtilities.computeUnion(thumbRect.x, thumbRect.y, thumbRect.width,
1955
                                thumbRect.height, union);
1956
    slider.repaint(union);
1957
  }
1958
 
1959
  /**
1960
   * Moves the thumb one block in the direction specified (a block is 1/10th
1961
   * of the slider range).   If the slider snaps to ticks, this method is
1962
   * responsible for snapping it to a tick after the thumb has been moved.
1963
   *
1964
   * @param direction  the direction (positive values increment the thumb
1965
   *   position by one block, zero/negative values decrement the thumb position
1966
   *   by one block).
1967
   */
1968
  public void scrollByBlock(int direction)
1969
  {
1970
    int unit = (slider.getMaximum() - slider.getMinimum()) / 10;
1971
    int moveTo = slider.getValue();
1972
    if (direction > 0)
1973
      moveTo += unit;
1974
    else
1975
      moveTo -= unit;
1976
 
1977
    if (slider.getSnapToTicks())
1978
      moveTo = findClosestTick(moveTo);
1979
 
1980
    slider.setValue(moveTo);
1981
  }
1982
 
1983
  /**
1984
   * Moves the thumb one unit in the specified direction. If the slider snaps
1985
   * to ticks, this method is responsible for snapping it to a tick after the
1986
   * thumb has been moved.
1987
   *
1988
   * @param direction  the direction (positive values increment the thumb
1989
   *   position by one, zero/negative values decrement the thumb position by
1990
   *   one).
1991
   */
1992
  public void scrollByUnit(int direction)
1993
  {
1994
    int moveTo = slider.getValue();
1995
    if (direction > 0)
1996
      moveTo++;
1997
    else
1998
      moveTo--;
1999
 
2000
    if (slider.getSnapToTicks())
2001
      moveTo = findClosestTick(moveTo);
2002
 
2003
    slider.setValue(moveTo);
2004
  }
2005
 
2006
  /**
2007
   * This method is called when there has been a click in the track and the
2008
   * thumb needs to be scrolled  on regular intervals. This method is only
2009
   * responsible  for starting the timer and not for stopping it.
2010
   *
2011
   * @param dir The direction to move in.
2012
   */
2013
  protected void scrollDueToClickInTrack(int dir)
2014
  {
2015
    scrollTimer.stop();
2016
 
2017
    scrollListener.setDirection(dir);
2018
    scrollListener.setScrollByBlock(true);
2019
 
2020
    scrollTimer.start();
2021
  }
2022
 
2023
  /**
2024
   * Returns the x-coordinate (relative to the component) for the given slider
2025
   * value.  This method assumes that the <code>trackRect</code> field is
2026
   * set up.
2027
   *
2028
   * @param value  the slider value.
2029
   *
2030
   * @return The x-coordinate.
2031
   */
2032
  protected int xPositionForValue(int value)
2033
  {
2034
    int min = slider.getMinimum();
2035
    int max = slider.getMaximum();
2036
    int len = trackRect.width;
2037
    double range = max - min;
2038
    double pixPerVal = len / range;
2039
    int left = trackRect.x;
2040
    int right = left + trackRect.width - 1;
2041
    int xpos;
2042
    if (! drawInverted())
2043
      xpos = left + (int) Math.round(pixPerVal * ((double) value - min));
2044
    else
2045
      xpos = right - (int) Math.round(pixPerVal * ((double) value - min));
2046
    xpos = Math.max(left, xpos);
2047
    xpos = Math.min(right, xpos);
2048
    return xpos;
2049
  }
2050
 
2051
  /**
2052
   * Returns the y-coordinate (relative to the component) for the given slider
2053
   * value.  This method assumes that the <code>trackRect</code> field is
2054
   * set up.
2055
   *
2056
   * @param value  the slider value.
2057
   *
2058
   * @return The y-coordinate.
2059
   */
2060
  protected int yPositionForValue(int value)
2061
  {
2062
    int min = slider.getMinimum();
2063
    int max = slider.getMaximum();
2064
    int len = trackRect.height;
2065
    double range = max - min;
2066
    double pixPerVal = len / range;
2067
    int top = trackRect.y;
2068
    int bottom = top + trackRect.height - 1;
2069
    int ypos;
2070
    if (! drawInverted())
2071
      ypos = top + (int) Math.round(pixPerVal * ((double) max - value));
2072
    else
2073
      ypos = top + (int) Math.round(pixPerVal * ((double) value - min));
2074
    ypos = Math.max(top, ypos);
2075
    ypos = Math.min(bottom, ypos);
2076
    return ypos;
2077
  }
2078
 
2079
  /**
2080
   * This method returns the value in the slider's range given the y
2081
   * coordinate. If the value is out of range, it will  return the closest
2082
   * legal value.
2083
   *
2084
   * @param yPos The y coordinate to calculate a value for.
2085
   *
2086
   * @return The value for the y coordinate.
2087
   */
2088
  public int valueForYPosition(int yPos)
2089
  {
2090
    int min = slider.getMinimum();
2091
    int max = slider.getMaximum();
2092
    int len = trackRect.height;
2093
 
2094
    int value;
2095
 
2096
    // If the length is 0, you shouldn't be able to even see where the slider
2097
    // is.  This really shouldn't ever happen, but just in case, we'll return
2098
    // the middle.
2099
    if (len == 0)
2100
      return (max - min) / 2;
2101
 
2102
    if (! drawInverted())
2103
      value = (len - (yPos - trackRect.y)) * (max - min) / len + min;
2104
    else
2105
      value = (yPos - trackRect.y) * (max - min) / len + min;
2106
 
2107
    // If this isn't a legal value, then we'll have to move to one now.
2108
    if (value > max)
2109
      value = max;
2110
    else if (value < min)
2111
      value = min;
2112
    return value;
2113
  }
2114
 
2115
  /**
2116
   * This method returns the value in the slider's range given the x
2117
   * coordinate. If the value is out of range, it will return the closest
2118
   * legal value.
2119
   *
2120
   * @param xPos The x coordinate to calculate a value for.
2121
   *
2122
   * @return The value for the x coordinate.
2123
   */
2124
  public int valueForXPosition(int xPos)
2125
  {
2126
    int min = slider.getMinimum();
2127
    int max = slider.getMaximum();
2128
    int len = trackRect.width;
2129
 
2130
    int value;
2131
 
2132
    // If the length is 0, you shouldn't be able to even see where the slider
2133
    // is.  This really shouldn't ever happen, but just in case, we'll return
2134
    // the middle.
2135
    if (len == 0)
2136
      return (max - min) / 2;
2137
 
2138
    if (! drawInverted())
2139
      value = (xPos - trackRect.x) * (max - min) / len + min;
2140
    else
2141
      value = (len - (xPos - trackRect.x)) * (max - min) / len + min;
2142
 
2143
    // If this isn't a legal value, then we'll have to move to one now.
2144
    if (value > max)
2145
      value = max;
2146
    else if (value < min)
2147
      value = min;
2148
    return value;
2149
  }
2150
 
2151
  /**
2152
   * This method finds the closest value that has a tick associated with it.
2153
   * This is package-private to avoid an accessor method.
2154
   *
2155
   * @param value The value to search from.
2156
   *
2157
   * @return The closest value that has a tick associated with it.
2158
   */
2159
  int findClosestTick(int value)
2160
  {
2161
    int min = slider.getMinimum();
2162
    int max = slider.getMaximum();
2163
    int majorSpace = slider.getMajorTickSpacing();
2164
    int minorSpace = slider.getMinorTickSpacing();
2165
 
2166
    // The default value to return is value + minor or
2167
    // value + major.
2168
    // Initializing at min - value leaves us with a default
2169
    // return value of min, which always has tick marks
2170
    // (if ticks are painted).
2171
    int minor = min - value;
2172
    int major = min - value;
2173
 
2174
    // If there are no major tick marks or minor tick marks
2175
    // e.g. snap is set to true but no ticks are set, then
2176
    // we can just return the value.
2177
    if (majorSpace <= 0 && minorSpace <= 0)
2178
      return value;
2179
 
2180
    // First check the major ticks.
2181
    if (majorSpace > 0)
2182
      {
2183
        int lowerBound = (value - min) / majorSpace;
2184
        int majLower = majorSpace * lowerBound + min;
2185
        int majHigher = majorSpace * (lowerBound + 1) + min;
2186
 
2187
        if (majHigher <= max && majHigher - value <= value - majLower)
2188
          major = majHigher - value;
2189
        else
2190
          major = majLower - value;
2191
      }
2192
 
2193
    if (minorSpace > 0)
2194
      {
2195
        int lowerBound = value / minorSpace;
2196
        int minLower = minorSpace * lowerBound;
2197
        int minHigher = minorSpace * (lowerBound + 1);
2198
 
2199
        if (minHigher <= max && minHigher - value <= value - minLower)
2200
          minor = minHigher - value;
2201
        else
2202
          minor = minLower - value;
2203
      }
2204
 
2205
    // Give preference to minor ticks
2206
    if (Math.abs(minor) > Math.abs(major))
2207
      return value + major;
2208
    else
2209
      return value + minor;
2210
  }
2211
 
2212
  InputMap getInputMap(int condition)
2213
  {
2214
    if (condition == JComponent.WHEN_FOCUSED)
2215
      return (InputMap) UIManager.get("Slider.focusInputMap");
2216
    return null;
2217
  }
2218
 
2219
  /**
2220
   * Returns the action map for the {@link JSlider}.  All sliders share
2221
   * a single action map which is created the first time this method is
2222
   * called, then stored in the UIDefaults table for subsequent access.
2223
   *
2224
   * @return The shared action map.
2225
   */
2226
  ActionMap getActionMap()
2227
  {
2228
    ActionMap map = (ActionMap) UIManager.get("Slider.actionMap");
2229
 
2230
    if (map == null) // first time here
2231
      {
2232
        map = createActionMap();
2233
        if (map != null)
2234
          UIManager.put("Slider.actionMap", map);
2235
      }
2236
    return map;
2237
  }
2238
 
2239
  /**
2240
   * Creates the action map shared by all {@link JSlider} instances.
2241
   * This method is called once by {@link #getActionMap()} when it
2242
   * finds no action map in the UIDefaults table...after the map is
2243
   * created, it gets added to the defaults table so that subsequent
2244
   * calls to {@link #getActionMap()} will return the same shared
2245
   * instance.
2246
   *
2247
   * @return The action map.
2248
   */
2249
  ActionMap createActionMap()
2250
  {
2251
    ActionMap map = new ActionMapUIResource();
2252
    map.put("positiveUnitIncrement",
2253
            new AbstractAction("positiveUnitIncrement") {
2254
              public void actionPerformed(ActionEvent event)
2255
              {
2256
                JSlider slider = (JSlider) event.getSource();
2257
                BasicSliderUI ui = (BasicSliderUI) slider.getUI();
2258
                if (slider.getInverted())
2259
                  ui.scrollByUnit(BasicSliderUI.NEGATIVE_SCROLL);
2260
                else
2261
                  ui.scrollByUnit(BasicSliderUI.POSITIVE_SCROLL);
2262
              }
2263
            }
2264
    );
2265
    map.put("negativeUnitIncrement",
2266
            new AbstractAction("negativeUnitIncrement") {
2267
              public void actionPerformed(ActionEvent event)
2268
              {
2269
                JSlider slider = (JSlider) event.getSource();
2270
                BasicSliderUI ui = (BasicSliderUI) slider.getUI();
2271
                if (slider.getInverted())
2272
                  ui.scrollByUnit(BasicSliderUI.POSITIVE_SCROLL);
2273
                else
2274
                  ui.scrollByUnit(BasicSliderUI.NEGATIVE_SCROLL);
2275
              }
2276
            }
2277
    );
2278
    map.put("positiveBlockIncrement",
2279
            new AbstractAction("positiveBlockIncrement") {
2280
              public void actionPerformed(ActionEvent event)
2281
              {
2282
                JSlider slider = (JSlider) event.getSource();
2283
                BasicSliderUI ui = (BasicSliderUI) slider.getUI();
2284
                if (slider.getInverted())
2285
                  ui.scrollByBlock(BasicSliderUI.NEGATIVE_SCROLL);
2286
                else
2287
                  ui.scrollByBlock(BasicSliderUI.POSITIVE_SCROLL);
2288
              }
2289
            }
2290
    );
2291
    map.put("negativeBlockIncrement",
2292
            new AbstractAction("negativeBlockIncrement") {
2293
              public void actionPerformed(ActionEvent event)
2294
              {
2295
                JSlider slider = (JSlider) event.getSource();
2296
                BasicSliderUI ui = (BasicSliderUI) slider.getUI();
2297
                if (slider.getInverted())
2298
                  ui.scrollByBlock(BasicSliderUI.POSITIVE_SCROLL);
2299
                else
2300
                  ui.scrollByBlock(BasicSliderUI.NEGATIVE_SCROLL);
2301
              }
2302
            }
2303
    );
2304
    map.put("minScroll",
2305
            new AbstractAction("minScroll") {
2306
              public void actionPerformed(ActionEvent event)
2307
              {
2308
                JSlider slider = (JSlider) event.getSource();
2309
                if (slider.getInverted())
2310
                  slider.setValue(slider.getMaximum());
2311
                else
2312
                  slider.setValue(slider.getMinimum());
2313
              }
2314
            }
2315
    );
2316
    map.put("maxScroll",
2317
            new AbstractAction("maxScroll") {
2318
              public void actionPerformed(ActionEvent event)
2319
              {
2320
                JSlider slider = (JSlider) event.getSource();
2321
                if (slider.getInverted())
2322
                  slider.setValue(slider.getMinimum());
2323
                else
2324
                  slider.setValue(slider.getMaximum());
2325
              }
2326
            }
2327
    );
2328
    return map;
2329
  }
2330
 
2331
  /**
2332
   * Small utility method to save me from typing the hell out of myself in
2333
   * paint().
2334
   */
2335
  private boolean hitClip(Graphics g, Rectangle r)
2336
  {
2337
    return g.hitClip(r.x, r.y, r.width, r.height);
2338
  }
2339
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.