001    /* BasicProgressBarUI.java --
002       Copyright (C) 2004, 2005  Free Software Foundation, Inc.
003    
004    This file is part of GNU Classpath.
005    
006    GNU Classpath is free software; you can redistribute it and/or modify
007    it under the terms of the GNU General Public License as published by
008    the Free Software Foundation; either version 2, or (at your option)
009    any later version.
010    
011    GNU Classpath is distributed in the hope that it will be useful, but
012    WITHOUT ANY WARRANTY; without even the implied warranty of
013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014    General Public License for more details.
015    
016    You should have received a copy of the GNU General Public License
017    along with GNU Classpath; see the file COPYING.  If not, write to the
018    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019    02110-1301 USA.
020    
021    Linking this library statically or dynamically with other modules is
022    making a combined work based on this library.  Thus, the terms and
023    conditions of the GNU General Public License cover the whole
024    combination.
025    
026    As a special exception, the copyright holders of this library give you
027    permission to link this library with independent modules to produce an
028    executable, regardless of the license terms of these independent
029    modules, and to copy and distribute the resulting executable under
030    terms of your choice, provided that you also meet, for each linked
031    independent module, the terms and conditions of the license of that
032    module.  An independent module is a module which is not derived from
033    or based on this library.  If you modify this library, you may extend
034    this exception to your version of the library, but you are not
035    obligated to do so.  If you do not wish to do so, delete this
036    exception statement from your version. */
037    
038    
039    package javax.swing.plaf.basic;
040    
041    import java.awt.Color;
042    import java.awt.Dimension;
043    import java.awt.Font;
044    import java.awt.FontMetrics;
045    import java.awt.Graphics;
046    import java.awt.Insets;
047    import java.awt.Point;
048    import java.awt.Rectangle;
049    import java.awt.Shape;
050    import java.awt.event.ActionEvent;
051    import java.awt.event.ActionListener;
052    import java.awt.event.ComponentAdapter;
053    import java.awt.event.ComponentEvent;
054    import java.awt.event.ComponentListener;
055    import java.awt.geom.AffineTransform;
056    import java.beans.PropertyChangeEvent;
057    import java.beans.PropertyChangeListener;
058    
059    import javax.swing.JComponent;
060    import javax.swing.JProgressBar;
061    import javax.swing.LookAndFeel;
062    import javax.swing.SwingConstants;
063    import javax.swing.SwingUtilities;
064    import javax.swing.Timer;
065    import javax.swing.UIManager;
066    import javax.swing.event.AncestorEvent;
067    import javax.swing.event.AncestorListener;
068    import javax.swing.event.ChangeEvent;
069    import javax.swing.event.ChangeListener;
070    import javax.swing.plaf.ComponentUI;
071    import javax.swing.plaf.ProgressBarUI;
072    
073    /**
074     * The Basic Look and Feel UI delegate for the
075     * JProgressBar.
076     */
077    public 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    }