001/* BasicProgressBarUI.java --
002   Copyright (C) 2004, 2005  Free Software Foundation, Inc.
003
004This file is part of GNU Classpath.
005
006GNU Classpath is free software; you can redistribute it and/or modify
007it under the terms of the GNU General Public License as published by
008the Free Software Foundation; either version 2, or (at your option)
009any later version.
010
011GNU Classpath is distributed in the hope that it will be useful, but
012WITHOUT ANY WARRANTY; without even the implied warranty of
013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014General Public License for more details.
015
016You should have received a copy of the GNU General Public License
017along with GNU Classpath; see the file COPYING.  If not, write to the
018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
01902110-1301 USA.
020
021Linking this library statically or dynamically with other modules is
022making a combined work based on this library.  Thus, the terms and
023conditions of the GNU General Public License cover the whole
024combination.
025
026As a special exception, the copyright holders of this library give you
027permission to link this library with independent modules to produce an
028executable, regardless of the license terms of these independent
029modules, and to copy and distribute the resulting executable under
030terms of your choice, provided that you also meet, for each linked
031independent module, the terms and conditions of the license of that
032module.  An independent module is a module which is not derived from
033or based on this library.  If you modify this library, you may extend
034this exception to your version of the library, but you are not
035obligated to do so.  If you do not wish to do so, delete this
036exception statement from your version. */
037
038
039package javax.swing.plaf.basic;
040
041import java.awt.Color;
042import java.awt.Dimension;
043import java.awt.Font;
044import java.awt.FontMetrics;
045import java.awt.Graphics;
046import java.awt.Insets;
047import java.awt.Point;
048import java.awt.Rectangle;
049import java.awt.Shape;
050import java.awt.event.ActionEvent;
051import java.awt.event.ActionListener;
052import java.awt.event.ComponentAdapter;
053import java.awt.event.ComponentEvent;
054import java.awt.event.ComponentListener;
055import java.awt.geom.AffineTransform;
056import java.beans.PropertyChangeEvent;
057import java.beans.PropertyChangeListener;
058
059import javax.swing.JComponent;
060import javax.swing.JProgressBar;
061import javax.swing.LookAndFeel;
062import javax.swing.SwingConstants;
063import javax.swing.SwingUtilities;
064import javax.swing.Timer;
065import javax.swing.UIManager;
066import javax.swing.event.AncestorEvent;
067import javax.swing.event.AncestorListener;
068import javax.swing.event.ChangeEvent;
069import javax.swing.event.ChangeListener;
070import javax.swing.plaf.ComponentUI;
071import javax.swing.plaf.ProgressBarUI;
072
073/**
074 * The Basic Look and Feel UI delegate for the
075 * JProgressBar.
076 */
077public class BasicProgressBarUI extends ProgressBarUI
078{
079  /**
080   * A helper class that listens for ChangeEvents
081   * from the progressBar's model.
082   *
083   * @specnote Apparently this class was intended to be protected,
084   *           but was made public by a compiler bug and is now
085   *           public for compatibility.
086   */
087  public class ChangeHandler implements ChangeListener
088  {
089    /**
090     * Called every time the state of the model changes.
091     *
092     * @param e The ChangeEvent given by the model.
093     */
094    public void stateChanged(ChangeEvent e)
095    {
096      // Nothing to do but repaint.
097      progressBar.repaint();
098    }
099  }
100
101  /**
102   * This helper class is used to listen for
103   * PropertyChangeEvents from the progressBar.
104   */
105  private class PropertyChangeHandler implements PropertyChangeListener
106  {
107    /**
108     * Called every time the properties of the
109     * progressBar change.
110     *
111     * @param e The PropertyChangeEvent given by the progressBar.
112     */
113    public void propertyChange(PropertyChangeEvent e)
114    {
115      // Only need to listen for indeterminate changes.
116      // All other things are done on a repaint.
117      if (e.getPropertyName().equals("indeterminate"))
118        if (((Boolean) e.getNewValue()).booleanValue()
119            && progressBar.isShowing())
120          startAnimationTimer();
121        else
122          stopAnimationTimer();
123    }
124  }
125
126  /**
127   * Receives notification when the progressbar is becoming visible or
128   * invisible and starts/stops the animation timer accordingly.
129   *
130   * @author Roman Kennke (kennke@aicas.com)
131   */
132  private class AncestorHandler implements AncestorListener
133  {
134
135    /**
136     * Receives notification when the progressbar is becoming visible. This
137     * starts the animation timer if the progressbar is indeterminate.
138     *
139     * @param event the ancestor event
140     */
141    public void ancestorAdded(AncestorEvent event)
142    {
143      if (progressBar.isIndeterminate())
144        startAnimationTimer();
145    }
146
147    /**
148     * Receives notification when the progressbar is becoming invisible. This
149     * stops the animation timer if the progressbar is indeterminate.
150     *
151     * @param event the ancestor event
152     */
153    public void ancestorRemoved(AncestorEvent event)
154    {
155      stopAnimationTimer();
156    }
157
158    /**
159     * Receives notification when an ancestor has been moved. We don't need to
160     * do anything here.
161     */
162    public void ancestorMoved(AncestorEvent event)
163    {
164      // Nothing to do here.
165    }
166
167  }
168
169  /**
170   * This helper class is used to listen for
171   * the animationTimer's intervals. On every interval,
172   * the bouncing box should move.
173   */
174  private class Animator implements ActionListener
175  {
176    /**
177     * Called every time the animationTimer reaches
178     * its interval.
179     *
180     * @param e The ActionEvent given by the timer.
181     */
182    public void actionPerformed(ActionEvent e)
183    {
184      // Incrementing the animation index will cause
185      // a repaint.
186      incrementAnimationIndex();
187    }
188  }
189
190  /**
191   * Receives notification when the size of the progress bar changes and
192   * invalidates the layout information for the box calculation in
193   * {@link BasicProgressBarUI#getBox(Rectangle)}.
194   *
195   * @author Roman Kennke (kennke@aicas.com)
196   */
197  private class ComponentHandler extends ComponentAdapter
198  {
199    /**
200     * Receives notification when the size of the progress bar changes and
201     * invalidates the layout information for the box calculation in
202     * {@link BasicProgressBarUI#getBox}.
203     *
204     * @param e the component event
205     */
206    public void componentResized(ComponentEvent e)
207    {
208      boxDependent = -1;
209      boxIndependent = -1;
210      incr = -1;
211    }
212  }
213
214  /**
215   * Holds the value of the bouncing box that is returned by {@link #getBox}.
216   *
217   * @since 1.5
218   */
219  protected Rectangle boxRect;
220
221  /** The timer used to move the bouncing box. */
222  private transient Timer animationTimer;
223
224  // The total number of frames must be an even number.
225  // The total number of frames is calculated from
226  // the cycleTime and repaintInterval given by
227  // the basic Look and Feel defaults.
228  //
229  // +-----------------------------------------------+
230  // | frame0 | frame1 | frame2 | frame 3 | frame 4  |
231  // |        | frame7 | frame6 | frame 5 |          |
232  // +-----------------------------------------------+
233
234  /** The current animation index. */
235  private transient int animationIndex;
236
237  /** The total number of frames.*/
238  private transient int numFrames;
239
240  /** The helper that moves the bouncing box. */
241  private transient Animator animation;
242
243  /** The helper that listens for property change events. */
244  private transient PropertyChangeHandler propertyListener;
245
246  /** The Listener for the model. */
247  protected ChangeListener changeListener;
248
249  /** The progressBar for this UI. */
250  protected JProgressBar progressBar;
251
252
253  /**
254   * The size of the box returned by {@link #getBox} in the orientation
255   * direction of the progress bar. This is package private to avoid accessor
256   * method.
257   */
258  transient double boxDependent = - 1;
259
260  /**
261   * The size of the box returned by {@link #getBox} against the orientation
262   * direction of the progress bar. This is package private to avoid accessor
263   * method.
264   */
265  transient int boxIndependent = - 1;
266
267  /**
268   * The increment for box animation. This is package private to avoid accessor
269   * method.
270   */
271  transient double incr = -1;
272
273  /** The length of the cell. The cell is the painted part. */
274  private transient int cellLength;
275
276  /** The gap between cells. */
277  private transient int cellSpacing;
278
279  /** The color of the text when the bar is not over it.*/
280  private transient Color selectionBackground;
281
282  /** The color of the text when the bar is over it. */
283  private transient Color selectionForeground;
284
285  /**
286   * Listens for notification when the component becomes showing and
287   * starts/stops the animation timer.
288   */
289  private AncestorListener ancestorListener;
290
291  /**
292   * Listens for resize events on the progress bar and invalidates some
293   * layout info.
294   */
295  private ComponentListener componentListener;
296
297  /**
298   * Creates a new BasicProgressBarUI object.
299   */
300  public BasicProgressBarUI()
301  {
302    super();
303  }
304
305  /**
306   * Creates a new BasicProgressBarUI for the component.
307   *
308   * @param x The JComponent to create the UI for.
309   *
310   * @return A new BasicProgressBarUI.
311   */
312  public static ComponentUI createUI(JComponent x)
313  {
314    return new BasicProgressBarUI();
315  }
316
317  /**
318   * This method returns the length of the bar (from the minimum)
319   * in pixels (or units that the Graphics object draws in) based
320   * on the progressBar's getPercentComplete() value.
321   *
322   * @param b The insets of the progressBar.
323   * @param width The width of the progressBar.
324   * @param height The height of the progressBar.
325   *
326   * @return The length of the bar that should be painted in pixels.
327   */
328  protected int getAmountFull(Insets b, int width, int height)
329  {
330    double percentDone = progressBar.getPercentComplete();
331    if (progressBar.getOrientation() == JProgressBar.HORIZONTAL)
332      return (int) (percentDone * (width - b.left - b.right));
333    else
334      return (int) (percentDone * (height - b.top - b.bottom));
335  }
336
337  /**
338   * The current animation index.
339   *
340   * @return The current animation index.
341   */
342  protected int getAnimationIndex()
343  {
344    return animationIndex;
345  }
346
347  /**
348   * This method returns the size and position of the bouncing box
349   * for the current animation index. It stores the values in the
350   * given rectangle and returns it. It returns null if no box should
351   * be drawn.
352   *
353   * @param r The bouncing box rectangle.
354   *
355   * @return The bouncing box rectangle.
356   */
357  protected Rectangle getBox(Rectangle r)
358  {
359    if (!progressBar.isIndeterminate())
360      return null;
361    if (r == null)
362      r = new Rectangle();
363
364    Rectangle vr = new Rectangle();
365    SwingUtilities.calculateInnerArea(progressBar, vr);
366
367    // Recalculate the metrics only when size of the progressbar has changed.
368    if (incr == -1 || boxDependent == -1 || boxIndependent == -1)
369      {
370        //numFrames has to be an even number as defined by spec.
371        int iterations = numFrames / 2;
372        if (progressBar.getOrientation() == JProgressBar.HORIZONTAL)
373          {
374            boxDependent = vr.width / 6.;
375            incr = ((double) (vr.width - boxDependent)) / (double) iterations;
376            boxIndependent = vr.height;
377          }
378        else
379          {
380            boxDependent = vr.height / 6.;
381            incr = ((double) (vr.height - boxDependent)) / (double) iterations;
382            boxIndependent = vr.width;
383          }
384      }
385
386    int index = getAnimationIndex();
387    if (animationIndex > numFrames / 2)
388      index = numFrames - getAnimationIndex();
389
390    if (progressBar.getOrientation() == JProgressBar.HORIZONTAL)
391      {
392        r.x = vr.x + (int) (incr * index);
393        r.y = vr.y;
394        r.width = (int) boxDependent;
395        r.height = (int) boxIndependent;
396      }
397    else
398      {
399        r.x = vr.x;
400        r.y = vr.height - (int) (incr * index) + vr.y - (int) boxDependent;
401        r.width = (int) boxIndependent;
402        r.height = (int) boxDependent;
403      }
404    return r;
405  }
406
407  /**
408   * This method returns the length of the cells.
409   *
410   * @return The cell length.
411   */
412  protected int getCellLength()
413  {
414    return cellLength;
415  }
416
417  /**
418   * This method returns the spacing between cells.
419   *
420   * @return The cell gap.
421   */
422  protected int getCellSpacing()
423  {
424    return cellSpacing;
425  }
426
427  /**
428   * This method returns the maximum size of the JComponent.
429   * If it returns null, it is up to the LayoutManager
430   * to give it a size.
431   *
432   * @param c The component to find a maximum size for.
433   *
434   * @return The maximum size.
435   */
436  public Dimension getMaximumSize(JComponent c)
437  {
438    Insets insets = c.getInsets();
439    Dimension ret;
440    int orientation = progressBar.getOrientation();
441    if (orientation == JProgressBar.VERTICAL)
442      {
443        ret = getPreferredInnerVertical();
444        ret.height = Short.MAX_VALUE;
445        ret.width += insets.left + insets.right;
446      }
447    else
448      {
449        ret = getPreferredInnerHorizontal();
450        ret.width = Short.MAX_VALUE;
451        ret.height += insets.top + insets.bottom;
452      }
453    return ret;
454  }
455
456  /**
457   * This method returns the minimum size of the JComponent.
458   * If it returns null, it is up to the LayoutManager to
459   * give it a size.
460   *
461   * @param c The component to find a minimum size for.
462   *
463   * @return The minimum size.
464   */
465  public Dimension getMinimumSize(JComponent c)
466  {
467    Insets insets = c.getInsets();
468    Dimension ret;
469    int orientation = progressBar.getOrientation();
470    if (orientation == JProgressBar.VERTICAL)
471      {
472        ret = getPreferredInnerVertical();
473        ret.height = 10;
474        ret.width += insets.left + insets.right;
475      }
476    else
477      {
478        ret = getPreferredInnerHorizontal();
479        ret.width = 10;
480        ret.height += insets.top + insets.bottom;
481      }
482    return ret;
483  }
484
485  /**
486   * This method returns the preferred size of the inner
487   * rectangle (the bounds without the insets) if the
488   * progressBar is horizontal.
489   *
490   * @return The preferred size of the progressBar minus
491   *         insets if it's horizontal.
492   */
493  protected Dimension getPreferredInnerHorizontal()
494  {
495    Font font = progressBar.getFont();
496    FontMetrics fm = progressBar.getFontMetrics(font);
497
498    int stringWidth = 0;
499    String str = progressBar.getString();
500    if (str != null)
501      stringWidth = fm.stringWidth(progressBar.getString());
502    Insets i = progressBar.getInsets();
503    int prefWidth = Math.max(200 - i.left - i.right, stringWidth);
504
505    int stringHeight = 0;
506    if (str != null)
507      stringHeight = fm.getHeight();
508    int prefHeight = Math.max(16 - i.top - i.bottom, stringHeight);
509
510    return new Dimension(prefWidth, prefHeight);
511  }
512
513  /**
514   * This method returns the preferred size of the inner
515   * rectangle (the bounds without insets) if the
516   * progressBar is vertical.
517   *
518   * @return The preferred size of the progressBar minus
519   *         insets if it's vertical.
520   */
521  protected Dimension getPreferredInnerVertical()
522  {
523    Font font = progressBar.getFont();
524    FontMetrics fm = progressBar.getFontMetrics(font);
525
526    int stringWidth = 0;
527    String str = progressBar.getString();
528    if (str != null)
529      stringWidth = fm.stringWidth(progressBar.getString());
530    Insets i = progressBar.getInsets();
531    int prefHeight = Math.max(200 - i.left - i.right, stringWidth);
532
533    int stringHeight = 0;
534    if (str != null)
535      stringHeight = fm.getHeight();
536    int prefWidth = Math.max(16 - i.top - i.bottom, stringHeight);
537
538    return new Dimension(prefWidth, prefHeight);
539  }
540
541  /**
542   * This method returns the preferred size of the
543   * given JComponent. If it returns null, then it
544   * is up to the LayoutManager to give it a size.
545   *
546   * @param c The component to find the preferred size for.
547   *
548   * @return The preferred size of the component.
549   */
550  public Dimension getPreferredSize(JComponent c)
551  {
552    Insets insets = c.getInsets();
553    Dimension ret;
554    int orientation = progressBar.getOrientation();
555    if (orientation == JProgressBar.VERTICAL)
556      ret = getPreferredInnerVertical();
557    else
558      ret = getPreferredInnerHorizontal();
559    ret.width += insets.left + insets.right;
560    ret.height += insets.top + insets.bottom;
561    return ret;
562  }
563
564  /**
565   * This method returns the Color that the text is shown in when the bar is
566   * not over the text.
567   *
568   * @return The color of the text when the bar is not over it.
569   */
570  protected Color getSelectionBackground()
571  {
572    return selectionBackground;
573  }
574
575  /**
576   * This method returns the Color that the text is shown in  when the bar is
577   * over the text.
578   *
579   * @return The color of the text when the bar is over it.
580   */
581  protected Color getSelectionForeground()
582  {
583    return selectionForeground;
584  }
585
586  /**
587   * This method returns the point (the top left of the bounding box)
588   * where the text should be painted.
589   *
590   * @param g The Graphics object to measure FontMetrics with.
591   * @param progressString The string to paint.
592   * @param x The x coordinate of the overall bounds box.
593   * @param y The y coordinate of the overall bounds box.
594   * @param width The width of the overall bounds box.
595   * @param height The height of the overall bounds box.
596   *
597   * @return The top left of the bounding box where text should be painted.
598   */
599  protected Point getStringPlacement(Graphics g, String progressString, int x,
600                                     int y, int width, int height)
601  {
602    Rectangle tr = new Rectangle();
603    Rectangle vr = new Rectangle();
604    Rectangle ir = new Rectangle();
605
606    if (progressBar.getOrientation() == JProgressBar.HORIZONTAL)
607      vr.setBounds(x, y, width, height);
608    else
609      vr.setBounds(y, x, height, width);
610
611    Font f = g.getFont();
612    FontMetrics fm = g.getFontMetrics(f);
613
614    SwingUtilities.layoutCompoundLabel(progressBar, fm, progressString, null,
615                                       SwingConstants.CENTER,
616                                       SwingConstants.CENTER,
617                                       SwingConstants.CENTER,
618                                       SwingConstants.CENTER, vr, ir, tr, 0);
619
620    if (progressBar.getOrientation() == JProgressBar.HORIZONTAL)
621      return new Point(tr.x, tr.y);
622    else
623      return new Point(tr.y, tr.x);
624  }
625
626  /**
627   * This method increments the animation index.
628   */
629  protected void incrementAnimationIndex()
630  {
631    animationIndex++;
632    //numFrames is like string length, it should be named numFrames or something
633    if (animationIndex >= numFrames)
634      animationIndex = 0;
635    progressBar.repaint();
636  }
637
638  /**
639   * This method paints the progressBar. It delegates its responsibilities
640   * to paintDeterminate and paintIndeterminate.
641   *
642   * @param g The Graphics object to paint with.
643   * @param c The JComponent to paint.
644   */
645  public void paint(Graphics g, JComponent c)
646  {
647    if (! progressBar.isIndeterminate())
648      paintDeterminate(g, c);
649    else
650      paintIndeterminate(g, c);
651  }
652
653  /**
654   * This method is called if the painting to be done is
655   * for a determinate progressBar.
656   *
657   * @param g The Graphics object to paint with.
658   * @param c The JComponent to paint.
659   */
660  protected void paintDeterminate(Graphics g, JComponent c)
661  {
662    Color saved = g.getColor();
663    int space = getCellSpacing();
664    int len = getCellLength();
665    int max = progressBar.getMaximum();
666    int min = progressBar.getMinimum();
667    int value = progressBar.getValue();
668
669    Rectangle vr = SwingUtilities.calculateInnerArea(c, new Rectangle());
670    Rectangle or = progressBar.getBounds();
671    Insets insets = c.getInsets();
672
673    int amountFull = getAmountFull(insets, or.width, or.height);
674
675        if (progressBar.getOrientation() == JProgressBar.HORIZONTAL)
676          {
677            g.setColor(c.getForeground());
678            g.fillRect(vr.x, vr.y, amountFull, vr.height);
679          }
680        else
681          {
682            g.setColor(c.getForeground());
683            g.fillRect(vr.x, vr.y + vr.height - amountFull, vr.width,
684                       amountFull);
685          }
686
687    if (progressBar.isStringPainted() && !progressBar.getString().equals(""))
688      paintString(g, 0, 0, or.width, or.height, amountFull, insets);
689    g.setColor(saved);
690  }
691
692  /**
693   * This method is called if the painting to be done is for
694   * an indeterminate progressBar.
695   *
696   * @param g The Graphics object to paint with.
697   * @param c The JComponent to paint.
698   */
699  protected void paintIndeterminate(Graphics g, JComponent c)
700  {
701    //need to paint the box at it's current position. no text is painted since
702    //all we're doing is bouncing back and forth
703    Color saved = g.getColor();
704    Insets insets = c.getInsets();
705
706    Rectangle or = c.getBounds();
707    Rectangle vr = new Rectangle();
708    SwingUtilities.calculateInnerArea(c, vr);
709
710    g.setColor(c.getBackground());
711    g.fillRect(vr.x, vr.y, vr.width, vr.height);
712
713    boxRect = getBox(boxRect);
714
715    g.setColor(c.getForeground());
716    g.fillRect(boxRect.x, boxRect.y, boxRect.width, boxRect.height);
717
718    if (progressBar.isStringPainted() && !progressBar.getString().equals(""))
719      paintString(g, 0, 0, or.width, or.height,
720                  getAmountFull(insets, or.width, or.height), insets);
721
722    g.setColor(saved);
723  }
724
725  /**
726   * This method paints the string for the progressBar.
727   *
728   * @param g The Graphics object to paint with.
729   * @param x The x coordinate of the progressBar.
730   * @param y The y coordinate of the progressBar.
731   * @param width The width of the progressBar.
732   * @param height The height of the progressBar.
733   * @param amountFull The amount of the progressBar that has its bar filled.
734   * @param b The insets of the progressBar.
735   */
736  protected void paintString(Graphics g, int x, int y, int width, int height,
737                             int amountFull, Insets b)
738  {
739    String str = progressBar.getString();
740    int full = getAmountFull(b, width, height);
741    Point placement = getStringPlacement(g, progressBar.getString(),
742                                         x + b.left, y + b.top,
743                                         width - b.left - b.right,
744                                         height - b.top - b.bottom);
745    Color savedColor = g.getColor();
746    Shape savedClip = g.getClip();
747    FontMetrics fm = g.getFontMetrics(progressBar.getFont());
748
749    if (progressBar.getOrientation() == JProgressBar.VERTICAL)
750      {
751        AffineTransform rotate = AffineTransform.getRotateInstance(Math.PI / 2);
752        g.setFont(progressBar.getFont().deriveFont(rotate));
753        placement.x = width - placement.x - fm.getAscent();
754      }
755    else
756      {
757        placement.y += fm.getAscent();
758      }
759
760    g.setColor(getSelectionForeground());
761    g.setClip(0, 0, full + b.left, height);
762    g.drawString(str, placement.x, placement.y);
763    g.setColor(getSelectionBackground());
764    g.setClip(full + b.left, 0, width - full, height);
765    g.drawString(str, placement.x, placement.y);
766    g.setClip(savedClip);
767    g.setColor(savedColor);
768  }
769
770  /**
771   * This method sets the current animation index. If the index is greater than
772   * the number of frames, it resets to 0.
773   *
774   * @param newValue The new animation index.
775   */
776  protected void setAnimationIndex(int newValue)
777  {
778    animationIndex = (newValue <= numFrames) ? newValue : 0;
779    progressBar.repaint();
780  }
781
782  /**
783   * This method sets the cell length.
784   *
785   * @param cellLen The cell length.
786   */
787  protected void setCellLength(int cellLen)
788  {
789    cellLength = cellLen;
790  }
791
792  /**
793   * This method sets the cell spacing.
794   *
795   * @param cellSpace The cell spacing.
796   */
797  protected void setCellSpacing(int cellSpace)
798  {
799    cellSpacing = cellSpace;
800  }
801
802  /**
803   * This method starts the animation timer. It is called
804   * when the propertyChangeListener detects that the progressBar
805   * has changed to indeterminate mode.
806   *
807   * @since 1.4
808   */
809  protected void startAnimationTimer()
810  {
811    if (animationTimer != null)
812      animationTimer.start();
813  }
814
815  /**
816   * This method stops the animation timer. It is called when
817   * the propertyChangeListener detects that the progressBar
818   * has changed to determinate mode.
819   *
820   * @since 1.4
821   */
822  protected void stopAnimationTimer()
823  {
824    if (animationTimer != null)
825      animationTimer.stop();
826    setAnimationIndex(0);
827  }
828
829  /**
830   * This method changes the settings for the progressBar to
831   * the defaults provided by the current Look and Feel.
832   */
833  protected void installDefaults()
834  {
835    LookAndFeel.installColorsAndFont(progressBar, "ProgressBar.background",
836                                     "ProgressBar.foreground",
837                                     "ProgressBar.font");
838    LookAndFeel.installBorder(progressBar, "ProgressBar.border");
839    progressBar.setOpaque(true);
840
841    selectionForeground = UIManager.getColor("ProgressBar.selectionForeground");
842    selectionBackground = UIManager.getColor("ProgressBar.selectionBackground");
843    cellLength = UIManager.getInt("ProgressBar.cellLength");
844    cellSpacing = UIManager.getInt("ProgressBar.cellSpacing");
845
846    int repaintInterval = UIManager.getInt("ProgressBar.repaintInterval");
847    int cycleTime = UIManager.getInt("ProgressBar.cycleTime");
848
849    if (cycleTime % repaintInterval != 0
850        && (cycleTime / repaintInterval) % 2 != 0)
851      {
852        int div = (cycleTime / repaintInterval) + 2;
853        div /= 2;
854        div *= 2;
855        cycleTime = div * repaintInterval;
856      }
857    setAnimationIndex(0);
858    numFrames = cycleTime / repaintInterval;
859    animationTimer.setDelay(repaintInterval);
860  }
861
862  /**
863   * The method uninstalls any defaults that were
864   * set by the current Look and Feel.
865   */
866  protected void uninstallDefaults()
867  {
868    progressBar.setFont(null);
869    progressBar.setForeground(null);
870    progressBar.setBackground(null);
871
872    selectionForeground = null;
873    selectionBackground = null;
874  }
875
876  /**
877   * This method registers listeners to all the
878   * components that this UI delegate needs to listen to.
879   */
880  protected void installListeners()
881  {
882    changeListener = new ChangeHandler();
883    propertyListener = new PropertyChangeHandler();
884    animation = new Animator();
885
886    progressBar.addChangeListener(changeListener);
887    progressBar.addPropertyChangeListener(propertyListener);
888    animationTimer.addActionListener(animation);
889
890    ancestorListener = new AncestorHandler();
891    progressBar.addAncestorListener(ancestorListener);
892
893    componentListener = new ComponentHandler();
894    progressBar.addComponentListener(componentListener);
895  }
896
897  /**
898   * This method unregisters listeners to all the
899   * components that were listened to.
900   */
901  protected void uninstallListeners()
902  {
903    progressBar.removeChangeListener(changeListener);
904    progressBar.removePropertyChangeListener(propertyListener);
905    animationTimer.removeActionListener(animation);
906
907    changeListener = null;
908    propertyListener = null;
909    animation = null;
910
911    if (ancestorListener != null)
912      progressBar.removeAncestorListener(ancestorListener);
913    ancestorListener = null;
914
915    if (componentListener != null)
916      progressBar.removeComponentListener(componentListener);
917    componentListener = null;
918  }
919
920  /**
921   * This method installs the UI for the given JComponent.
922   * This includes setting up defaults and listeners as
923   * well as initializing any values or objects that
924   * the UI may need.
925   *
926   * @param c The JComponent that is having this UI installed.
927   */
928  public void installUI(JComponent c)
929  {
930    super.installUI(c);
931    if (c instanceof JProgressBar)
932      {
933        progressBar = (JProgressBar) c;
934
935        animationTimer = new Timer(200, null);
936        animationTimer.setRepeats(true);
937
938        installDefaults();
939        installListeners();
940      }
941    if (progressBar.isIndeterminate())
942      startAnimationTimer();
943  }
944
945  /**
946   * This method removes the UI for the given JComponent.
947   * This includes removing any listeners or defaults
948   * that the installUI may have set up.
949   *
950   * @param c The JComponent that is having this UI uninstalled.
951   */
952  public void uninstallUI(JComponent c)
953  {
954    super.uninstallUI(c);
955    uninstallListeners();
956    uninstallDefaults();
957
958    animationTimer = null;
959    progressBar = null;
960  }
961
962}