OSDN Git Service

9398ab86c1b70880eb7be76ff5bdcde9d1022a2b
[pf3gnuchains/gcc-fork.git] / libjava / javax / swing / plaf / basic / BasicProgressBarUI.java
1 /* BasicProgressBarUI.java --
2    Copyright (C) 2004 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., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 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.Dimension;
43 import java.awt.Font;
44 import java.awt.FontMetrics;
45 import java.awt.Graphics;
46 import java.awt.Insets;
47 import java.awt.Point;
48 import java.awt.Rectangle;
49 import java.awt.event.ActionEvent;
50 import java.awt.event.ActionListener;
51 import java.beans.PropertyChangeEvent;
52 import java.beans.PropertyChangeListener;
53
54 import javax.swing.JComponent;
55 import javax.swing.JProgressBar;
56 import javax.swing.SwingConstants;
57 import javax.swing.SwingUtilities;
58 import javax.swing.Timer;
59 import javax.swing.UIDefaults;
60 import javax.swing.UIManager;
61 import javax.swing.event.ChangeEvent;
62 import javax.swing.event.ChangeListener;
63 import javax.swing.plaf.ComponentUI;
64 import javax.swing.plaf.ProgressBarUI;
65
66 /**
67  * The Basic Look and Feel UI delegate for the 
68  * JProgressBar.
69  */
70 public class BasicProgressBarUI extends ProgressBarUI
71 {
72   /**
73    * A helper class that listens for ChangeEvents 
74    * from the progressBar's model.
75    */
76   protected class ChangeHandler implements ChangeListener
77   {
78     /**
79      * Called every time the state of the model changes.
80      *
81      * @param e The ChangeEvent given by the model.
82      */
83     public void stateChanged(ChangeEvent e)
84     {
85       // Nothing to do but repaint.
86       progressBar.repaint();
87     }
88   }
89
90   /**
91    * This helper class is used to listen for 
92    * PropertyChangeEvents from the progressBar.
93    */
94   private class PropertyChangeHandler implements PropertyChangeListener
95   {
96     /**
97      * Called every time the properties of the 
98      * progressBar change.
99      *
100      * @param e The PropertyChangeEvent given by the progressBar.
101      */
102     public void propertyChange(PropertyChangeEvent e)
103     {
104       // Only need to listen for indeterminate changes.
105       // All other things are done on a repaint.
106       if (e.getPropertyName().equals(JProgressBar.INDETERMINATE_CHANGED_PROPERTY))
107         if (((Boolean) e.getNewValue()).booleanValue())
108           startAnimationTimer();
109         else
110           stopAnimationTimer();
111       else
112         progressBar.repaint();
113     }
114   }
115
116   /**
117    * This helper class is used to listen for 
118    * the animationTimer's intervals. On every interval,
119    * the bouncing box should move.
120    */
121   private class Animator implements ActionListener
122   {
123     /**
124      * Called every time the animationTimer reaches
125      * its interval.
126      *
127      * @param e The ActionEvent given by the timer.
128      */
129     public void actionPerformed(ActionEvent e)
130     {
131       // Incrementing the animation index will cause
132       // a repaint.
133       incrementAnimationIndex();
134     }
135   }
136
137   /** The timer used to move the bouncing box. */
138   private transient Timer animationTimer;
139
140   // The total number of frames must be an even number.
141   // The total number of frames is calculated from
142   // the cycleTime and repaintInterval given by
143   // the basic Look and Feel defaults.
144   //
145   // +-----------------------------------------------+
146   // | frame0 | frame1 | frame2 | frame 3 | frame 4  |
147   // |        | frame7 | frame6 | frame 5 |          |
148   // +-----------------------------------------------+
149   
150   /** The current animation index. */
151   private transient int animationIndex;
152
153   /** The total number of frames.*/
154   private transient int numFrames;
155
156   /** The helper that moves the bouncing box. */
157   private transient Animator animation;
158
159   /** The helper that listens for property change events. */
160   private transient PropertyChangeHandler propertyListener;
161
162   /** The Listener for the model. */
163   protected ChangeListener changeListener;
164
165   /** The progressBar for this UI. */
166   protected JProgressBar progressBar;
167
168   /** The length of the cell. The cell is the painted part. */
169   private transient int cellLength;
170
171   /** The gap between cells. */
172   private transient int cellSpacing;
173
174   /** The color of the text when the bar is not over it.*/
175   private transient Color selectionBackground;
176
177   /** The color of the text when the bar is over it. */
178   private transient Color selectionForeground;
179
180   /**
181    * Creates a new BasicProgressBarUI object.
182    */
183   public BasicProgressBarUI()
184   {
185     super();
186   }
187
188   /**
189    * Creates a new BasicProgressBarUI for the component.
190    *
191    * @param x The JComponent to create the UI for.
192    *
193    * @return A new BasicProgressBarUI.
194    */
195   public static ComponentUI createUI(JComponent x)
196   {
197     return new BasicProgressBarUI();
198   }
199
200   /**
201    * This method returns the length of the bar (from the minimum)
202    * in pixels (or units that the Graphics object draws in) based
203    * on the progressBar's getPercentComplete() value.
204    *
205    * @param b The insets of the progressBar.
206    * @param width The width of the progressBar.
207    * @param height The height of the progressBar.
208    *
209    * @return The length of the bar that should be painted in pixels.
210    */
211   protected int getAmountFull(Insets b, int width, int height)
212   {
213     double percentDone = progressBar.getPercentComplete();
214     if (progressBar.getOrientation() == JProgressBar.HORIZONTAL)
215       return (int) (percentDone * (width - b.left - b.right));
216     else
217       return (int) (percentDone * (height - b.top - b.bottom));
218   }
219
220   /**
221    * The current animation index.
222    *
223    * @return The current animation index.
224    */
225   protected int getAnimationIndex()
226   {
227     return animationIndex;
228   }
229
230   /**
231    * This method returns the size and position of the bouncing box
232    * for the current animation index. It stores the values in the 
233    * given rectangle and returns it. It returns null if no box should
234    * be drawn.
235    *
236    * @param r The bouncing box rectangle.
237    *
238    * @return The bouncing box rectangle.
239    */
240   protected Rectangle getBox(Rectangle r)
241   {
242     if (!progressBar.isIndeterminate())
243       return null;
244     //numFrames has to be an even number as defined by spec.
245     int iterations = numFrames / 2 + 1;
246
247     double boxDependent;
248     double boxIndependent;
249
250     if (progressBar.getOrientation() == JProgressBar.HORIZONTAL)
251       {
252         Dimension dims = getPreferredInnerHorizontal();
253         boxDependent = (double) dims.width / iterations;
254         boxIndependent = dims.height;
255       }
256     else
257       {
258         Dimension dims = getPreferredInnerVertical();
259         boxDependent = (double) dims.height / iterations;
260         boxIndependent = dims.width;
261       }
262
263     Rectangle vr = new Rectangle();
264     SwingUtilities.calculateInnerArea(progressBar, vr);
265
266     int index = getAnimationIndex();
267     if (animationIndex > (numFrames + 1) / 2)
268       index = numFrames - getAnimationIndex();
269
270     if (progressBar.getOrientation() == JProgressBar.HORIZONTAL)
271       {
272         r.x = vr.x + (int) (index * boxDependent);
273         r.y = vr.y;
274         r.width = (int) boxDependent;
275         r.height = (int) boxIndependent;
276       }
277     else
278       {
279         index++;
280         r.x = vr.x;
281         r.y = vr.height - (int) (index * boxDependent) + vr.y;
282         r.width = (int) boxIndependent;
283         r.height = (int) boxDependent;
284       }
285
286     return r;
287   }
288
289   /**
290    * This method returns the length of the cells.
291    *
292    * @return The cell length.
293    */
294   protected int getCellLength()
295   {
296     return cellLength;
297   }
298
299   /**
300    * This method returns the spacing between cells.
301    *
302    * @return The cell gap.
303    */
304   protected int getCellSpacing()
305   {
306     return cellSpacing;
307   }
308
309   /**
310    * This method returns the maximum size of the JComponent.
311    * If it returns null, it is up to the LayoutManager
312    * to give it a size.
313    *
314    * @param c The component to find a maximum size for.
315    *
316    * @return The maximum size.
317    */
318   public Dimension getMaximumSize(JComponent c)
319   {
320     return getPreferredSize(c);
321   }
322
323   /**
324    * This method returns the minimum size of the JComponent.
325    * If it returns null, it is up to the LayoutManager to
326    * give it a size.
327    *
328    * @param c The component to find a minimum size for.
329    *
330    * @return The minimum size.
331    */
332   public Dimension getMinimumSize(JComponent c)
333   {
334     return getPreferredSize(c);
335   }
336
337   /**
338    * This method returns the preferred size of the inner
339    * rectangle (the bounds without the insets) if the
340    * progressBar is horizontal.
341    *
342    * @return The preferred size of the progressBar minus 
343    *         insets if it's horizontal.
344    */
345   protected Dimension getPreferredInnerHorizontal()
346   {
347     Rectangle vr = new Rectangle();
348
349     SwingUtilities.calculateInnerArea(progressBar, vr);
350
351     return new Dimension(vr.width, vr.height);
352   }
353
354   /**
355    * This method returns the preferred size of the inner
356    * rectangle (the bounds without insets) if the 
357    * progressBar is vertical.
358    *
359    * @return The preferred size of the progressBar minus
360    *         insets if it's vertical.
361    */
362   protected Dimension getPreferredInnerVertical()
363   {
364     Rectangle vr = new Rectangle();
365
366     SwingUtilities.calculateInnerArea(progressBar, vr);
367
368     return new Dimension(vr.width, vr.height);
369   }
370
371   /**
372    * This method returns the preferred size of the 
373    * given JComponent. If it returns null, then it
374    * is up to the LayoutManager to give it a size.
375    *
376    * @param c The component to find the preferred size for.
377    *
378    * @return The preferred size of the component.
379    */
380   public Dimension getPreferredSize(JComponent c)
381   {
382     // The only thing we need to worry about is
383     // the text size.
384     Graphics g = progressBar.getGraphics();
385
386     Insets insets = c.getInsets();
387
388     FontMetrics fm = g.getFontMetrics(c.getFont());
389
390     int textW = fm.stringWidth(progressBar.getString());
391     int textH = fm.getHeight();
392
393     g.dispose();
394
395     if (progressBar.getOrientation() == JProgressBar.HORIZONTAL)
396       {
397         if (textH < 20)
398           textH = 20;
399         if (textW < 200)
400           textW = 200;
401       }
402     else
403       {
404         if (textH < 200)
405           textH = 200;
406         if (textW < 20)
407           textW = 20;
408       }
409     textW += insets.left + insets.right;
410     textH += insets.top + insets.bottom;
411     return new Dimension(textW, textH);
412   }
413
414   /**
415    * This method returns the Color that the text is shown in when the bar is
416    * not over the text.
417    *
418    * @return The color of the text when the bar is not over it.
419    */
420   protected Color getSelectionBackground()
421   {
422     return selectionBackground;
423   }
424
425   /**
426    * This method returns the Color that the text is shown in  when the bar is
427    * over the text.
428    *
429    * @return The color of the text when the bar is over it.
430    */
431   protected Color getSelectionForeground()
432   {
433     return selectionForeground;
434   }
435
436   /**
437    * This method returns the point (the top left of the bounding box)
438    * where the text should be painted. 
439    *
440    * @param g The Graphics object to measure FontMetrics with.
441    * @param progressString The string to paint.
442    * @param x The x coordinate of the overall bounds box.
443    * @param y The y coordinate of the overall bounds box.
444    * @param width The width of the overall bounds box.
445    * @param height The height of the overall bounds box.
446    *
447    * @return The top left of the bounding box where text should be painted.
448    */
449   protected Point getStringPlacement(Graphics g, String progressString, int x,
450                                      int y, int width, int height)
451   {
452     Rectangle tr = new Rectangle();
453     Rectangle vr = new Rectangle(x, y, width, height);
454     Rectangle ir = new Rectangle();
455
456     Font f = g.getFont();
457     FontMetrics fm = g.getFontMetrics(f);
458
459     SwingUtilities.layoutCompoundLabel(progressBar, fm, progressString, null,
460                                        SwingConstants.CENTER,
461                                        SwingConstants.CENTER,
462                                        SwingConstants.CENTER,
463                                        SwingConstants.CENTER, vr, ir, tr, 0);
464     return new Point(tr.x, tr.y);
465   }
466
467   /**
468    * This method increments the animation index.
469    */
470   public void incrementAnimationIndex()
471   {
472     animationIndex++;
473     //numFrames is like string length, it should be named numFrames or something
474     if (animationIndex >= numFrames)
475       animationIndex = 0;
476     progressBar.repaint();
477   }
478
479   /**
480    * This method paints the progressBar. It delegates its responsibilities
481    * to paintDeterminate and paintIndeterminate.
482    *
483    * @param g The Graphics object to paint with.
484    * @param c The JComponent to paint.
485    */
486   public void paint(Graphics g, JComponent c)
487   {
488     if (! progressBar.isIndeterminate())
489       paintDeterminate(g, c);
490     else
491       paintIndeterminate(g, c);
492       
493     if (progressBar.isBorderPainted())
494       progressBar.getBorder().paintBorder(progressBar, g, 0, 0,
495                                           progressBar.getWidth(),
496                                           progressBar.getHeight());
497   }
498
499   /**
500    * This method is called if the painting to be done is 
501    * for a determinate progressBar.
502    *
503    * @param g The Graphics object to paint with.
504    * @param c The JComponent to paint.
505    */
506   protected void paintDeterminate(Graphics g, JComponent c)
507   {
508     Color saved = g.getColor();
509     int space = getCellSpacing();
510     int len = getCellLength();
511     int max = progressBar.getMaximum();
512     int min = progressBar.getMinimum();
513     int value = progressBar.getValue();
514
515     Rectangle vr = new Rectangle();
516     SwingUtilities.calculateInnerArea(c, vr);
517
518     Rectangle or = c.getBounds();
519
520     Insets insets = c.getInsets();
521
522     int amountFull = getAmountFull(insets, or.width, or.height);
523
524     g.setColor(c.getBackground());
525     g.fill3DRect(vr.x, vr.y, vr.width, vr.height, false);
526
527     if (max != min && len != 0 && value > min)
528       {
529         int iterations = value / (space + len);
530
531         if (progressBar.getOrientation() == JProgressBar.HORIZONTAL)
532           {
533             double spaceInUnits = space * (double) vr.width / (max - min);
534             double lenInUnits = len * (double) vr.width / (max - min);
535             double currX = vr.x;
536
537             g.setColor(c.getForeground());
538             g.fill3DRect(vr.x, vr.y, amountFull, vr.height, true);
539
540             g.setColor(c.getBackground());
541             if (spaceInUnits != 0)
542               {
543                 for (int i = 0; i < iterations; i++)
544                   {
545                     currX += lenInUnits;
546                     g.fill3DRect((int) currX, vr.y, (int) spaceInUnits,
547                                  vr.height, true);
548                     currX += spaceInUnits;
549                   }
550               }
551           }
552         else
553           {
554             double currY = vr.y;
555             double spaceInUnits = space * (double) vr.height / (max - min);
556             double lenInUnits = len * (double) vr.height / (max - min);
557
558             g.setColor(c.getForeground());
559             g.fill3DRect(vr.x, vr.y + vr.height - amountFull, vr.width,
560                          amountFull, true);
561
562             g.setColor(c.getBackground());
563
564             if (spaceInUnits != 0)
565               {
566                 for (int i = 0; i < iterations; i++)
567                   {
568                     currY -= lenInUnits + spaceInUnits;
569                     g.fill3DRect(vr.x, (int) currY, vr.width,
570                                  (int) spaceInUnits, true);
571                   }
572               }
573           }
574       }
575
576     if (progressBar.isStringPainted() && !progressBar.getString().equals(""))
577       paintString(g, 0, 0, or.width, or.height, amountFull, insets);
578     g.setColor(saved);
579   }
580
581   /**
582    * This method is called if the painting to be done is for
583    * an indeterminate progressBar.
584    *
585    * @param g The Graphics object to paint with.
586    * @param c The JComponent to paint.
587    */
588   protected void paintIndeterminate(Graphics g, JComponent c)
589   {
590     //need to paint the box at it's current position. no text is painted since
591     //all we're doing is bouncing back and forth
592     Color saved = g.getColor();
593     Insets insets = c.getInsets();
594
595     Rectangle or = c.getBounds();
596     Rectangle vr = new Rectangle();
597     SwingUtilities.calculateInnerArea(c, vr);
598
599     g.setColor(c.getBackground());
600     g.fill3DRect(vr.x, vr.y, vr.width, vr.height, false);
601
602     Rectangle box = new Rectangle();
603     getBox(box);
604
605     g.setColor(c.getForeground());
606     g.fill3DRect(box.x, box.y, box.width, box.height, true);
607
608     if (progressBar.isStringPainted() && !progressBar.getString().equals(""))
609       paintString(g, 0, 0, or.width, or.height,
610                   getAmountFull(insets, or.width, or.height), insets);
611
612     g.setColor(saved);
613   }
614
615   /**
616    * This method paints the string for the progressBar.
617    *
618    * @param g The Graphics object to paint with.
619    * @param x The x coordinate of the progressBar.
620    * @param y The y coordinate of the progressBar.
621    * @param width The width of the progressBar.
622    * @param height The height of the progressBar.
623    * @param amountFull The amount of the progressBar that has its bar filled.
624    * @param b The insets of the progressBar.
625    */
626   protected void paintString(Graphics g, int x, int y, int width, int height,
627                              int amountFull, Insets b)
628   {
629     // We want to place in the exact center of the bar.
630     Point placement = getStringPlacement(g, progressBar.getString(),
631                                          x + b.left, y + b.top,
632                                          width - b.left - b.right,
633                                          height - b.top - b.bottom);
634     Color saved = g.getColor();
635
636     // FIXME: The Color of the text should use selectionForeground and selectionBackground
637     // but that can't be done right now, so we'll use white in the mean time.
638     g.setColor(Color.WHITE);
639
640     FontMetrics fm = g.getFontMetrics(progressBar.getFont());
641
642     g.drawString(progressBar.getString(), placement.x,
643                  placement.y + fm.getAscent());
644
645     g.setColor(saved);
646   }
647
648   /**
649    * This method sets the current animation index. If the index
650    * is greater than the number of frames, it resets to 0.
651    *
652    * @param newValue The new animation index.
653    */
654   protected void setAnimationIndex(int newValue)
655   {
656     animationIndex = (newValue <= numFrames) ? newValue : 0;
657     progressBar.repaint();
658   }
659
660   /**
661    * This method sets the cell length.
662    *
663    * @param cellLen The cell length.
664    */
665   protected void setCellLength(int cellLen)
666   {
667     cellLength = cellLen;
668   }
669
670   /**
671    * This method sets the cell spacing.
672    *
673    * @param cellSpace The cell spacing.
674    */
675   protected void setCellSpacing(int cellSpace)
676   {
677     cellSpacing = cellSpace;
678   }
679
680   /**
681    * This method starts the animation timer. It is called
682    * when the propertyChangeListener detects that the progressBar
683    * has changed to indeterminate mode.
684    *
685    * @since 1.4
686    */
687   protected void startAnimationTimer()
688   {
689     if (animationTimer != null)
690       animationTimer.start();
691   }
692
693   /**
694    * This method stops the animation timer. It is called when
695    * the propertyChangeListener detects that the progressBar
696    * has changed to determinate mode.
697    *
698    * @since 1.4
699    */
700   protected void stopAnimationTimer()
701   {
702     if (animationTimer != null)
703       animationTimer.stop();
704     setAnimationIndex(0);
705   }
706
707   /**
708    * This method changes the settings for the progressBar to
709    * the defaults provided by the current Look and Feel.
710    */
711   protected void installDefaults()
712   {
713     UIDefaults defaults = UIManager.getLookAndFeelDefaults();
714
715     progressBar.setFont(defaults.getFont("ProgressBar.font"));
716     progressBar.setForeground(defaults.getColor("ProgressBar.foreground"));
717     progressBar.setBackground(defaults.getColor("ProgressBar.background"));
718     progressBar.setBorder(defaults.getBorder("ProgressBar.border"));
719     progressBar.setOpaque(true);
720
721     selectionForeground = defaults.getColor("ProgressBar.selectionForeground");
722     selectionBackground = defaults.getColor("ProgressBar.selectionBackground");
723     cellLength = defaults.getInt("ProgressBar.cellLength");
724     cellSpacing = defaults.getInt("ProgressBar.cellSpacing");
725
726     int repaintInterval = defaults.getInt("ProgressBar.repaintInterval");
727     int cycleTime = defaults.getInt("ProgressBar.cycleTime");
728
729     if (cycleTime % repaintInterval != 0
730         && (cycleTime / repaintInterval) % 2 != 0)
731       {
732         int div = (cycleTime / repaintInterval) + 2;
733         div /= 2;
734         div *= 2;
735         cycleTime = div * repaintInterval;
736       }
737     setAnimationIndex(0);
738     numFrames = cycleTime / repaintInterval;
739     animationTimer.setDelay(repaintInterval);
740   }
741
742   /**
743    * The method uninstalls any defaults that were
744    * set by the current Look and Feel.
745    */
746   protected void uninstallDefaults()
747   {
748     progressBar.setFont(null);
749     progressBar.setForeground(null);
750     progressBar.setBackground(null);
751
752     selectionForeground = null;
753     selectionBackground = null;
754   }
755
756   /**
757    * This method registers listeners to all the 
758    * components that this UI delegate needs to listen to.
759    */
760   protected void installListeners()
761   {
762     changeListener = new ChangeHandler();
763     propertyListener = new PropertyChangeHandler();
764     animation = new Animator();
765
766     progressBar.addChangeListener(changeListener);
767     progressBar.addPropertyChangeListener(propertyListener);
768     animationTimer.addActionListener(animation);
769   }
770
771   /**
772    * This method unregisters listeners to all the 
773    * components that were listened to.
774    */
775   protected void uninstallListeners()
776   {
777     progressBar.removeChangeListener(changeListener);
778     progressBar.removePropertyChangeListener(propertyListener);
779     animationTimer.removeActionListener(animation);
780
781     changeListener = null;
782     propertyListener = null;
783     animation = null;
784   }
785
786   /**
787    * This method installs the UI for the given JComponent.
788    * This includes setting up defaults and listeners as
789    * well as initializing any values or objects that
790    * the UI may need.
791    *
792    * @param c The JComponent that is having this UI installed.
793    */
794   public void installUI(JComponent c)
795   {
796     super.installUI(c);
797     if (c instanceof JProgressBar)
798       {
799         progressBar = (JProgressBar) c;
800
801         animationTimer = new Timer(200, null);
802         animationTimer.setRepeats(true);
803
804         installDefaults();
805         installListeners();
806       }
807   }
808
809   /**
810    * This method removes the UI for the given JComponent.
811    * This includes removing any listeners or defaults
812    * that the installUI may have set up.
813    *
814    * @param c The JComponent that is having this UI uninstalled.
815    */
816   public void uninstallUI(JComponent c)
817   {
818     super.uninstallUI(c);
819     uninstallListeners();
820     uninstallDefaults();
821
822     animationTimer = null;
823     progressBar = null;
824   }
825 }