001: /*
002: * Copyright 2000,2005 wingS development team.
003: *
004: * This file is part of wingS (http://wingsframework.org).
005: *
006: * wingS is free software; you can redistribute it and/or modify
007: * it under the terms of the GNU Lesser General Public License
008: * as published by the Free Software Foundation; either version 2.1
009: * of the License, or (at your option) any later version.
010: *
011: * Please see COPYING for the complete licence.
012: */
013: package org.wings;
014:
015: import org.wings.plaf.ProgressBarCG;
016:
017: import javax.swing.event.ChangeEvent;
018: import javax.swing.event.ChangeListener;
019: import javax.swing.*;
020: import java.io.Serializable;
021: import java.text.Format;
022: import java.text.NumberFormat;
023: import java.awt.*;
024:
025: /**
026: * Graphical bar which can be used to visualize the progress of an operation.
027: *
028: * @author <a href="mailto:armin.haaf@mercatis.de">Armin Haaf</a>
029: */
030: public class SProgressBar extends SComponent {
031: /**
032: * Whether the progress bar is horizontal or vertical.
033: * The default is <code>HORIZONTAL</code>.
034: *
035: * @see #setOrientation
036: */
037: protected int orientation;
038:
039: /**
040: * The object that holds the data for the progress bar.
041: *
042: * @see #setModel
043: */
044: protected BoundedRangeModel model;
045:
046: /**
047: * An optional string that can be displayed on the progress bar.
048: * The default is <code>null</code>. Setting this to a non-<code>null</code>
049: * value does not imply that the string will be displayed.
050: *
051: * @see #setString
052: */
053: protected String progressString;
054:
055: /**
056: * Whether to textually display a string on the progress bar.
057: * The default is <code>false</code>.
058: * Setting this to <code>true</code> causes a textual
059: * display of the progress to be rendered on the progress bar. If
060: * the <code>progressString</code> is <code>null</code>,
061: * the percentage of completion is displayed on the progress bar.
062: * Otherwise, the <code>progressString</code> is
063: * rendered on the progress bar.
064: *
065: * @see #setStringPainted
066: */
067: protected boolean paintString;
068:
069: /**
070: * The default minimum for a progress bar is 0.
071: */
072: static final private int defaultMinimum = 0;
073: /**
074: * The default maximum for a progress bar is 100.
075: */
076: static final private int defaultMaximum = 100;
077: /**
078: * The default orientation for a progress bar is <code>HORIZONTAL</code>.
079: */
080: static final private int defaultOrientation = SConstants.HORIZONTAL;
081:
082: /**
083: * Only one <code>ChangeEvent</code> is needed per instance since the
084: * event's only interesting property is the immutable source, which
085: * is the progress bar.
086: */
087: protected transient ChangeEvent changeEvent = null;
088:
089: /**
090: * Listens for change events sent by the progress bar's model,
091: * redispatching them
092: * to change-event listeners registered upon
093: * this progress bar.
094: *
095: * @see #createChangeListener
096: */
097: protected ChangeListener changeListener = null;
098:
099: /**
100: * Format used when displaying percent complete.
101: */
102: private transient Format format;
103:
104: /**
105: * Whether the progress bar is indeterminate (<code>true</code>) or
106: * normal (<code>false</code>); the default is <code>false</code>.
107: *
108: * @see #setIndeterminate
109: * @since 1.4
110: */
111: private boolean indeterminate;
112:
113: /**
114: * The color in which the filled region is painted
115: */
116: private Color filledColor;
117:
118: /**
119: * The color in which the unfilled region is painted
120: */
121: private Color unfilledColor;
122:
123: /**
124: * The Dimension of the ProgressBar. We can't use the component size.
125: */
126: private SDimension progressBarDimension;
127:
128: /**
129: * Creates a horizontal progress bar
130: * that displays a border but no progress string.
131: * The initial and minimum values are 0,
132: * and the maximum is 100.
133: *
134: * @see #setOrientation
135: * @see #setStringPainted
136: * @see #setString
137: * @see #setIndeterminate
138: */
139: public SProgressBar() {
140: this (defaultOrientation);
141: }
142:
143: /**
144: * Creates a progress bar with the specified orientation,
145: * which can be
146: * either <code>SProgressBar.VERTICAL</code> or
147: * <code>SProgressBar.HORIZONTAL</code>.
148: * By default, a border is painted but a progress string is not.
149: * The initial and minimum values are 0,
150: * and the maximum is 100.
151: *
152: * @param orient the desired orientation of the progress bar
153: * @see #setOrientation
154: * @see #setStringPainted
155: * @see #setString
156: * @see #setIndeterminate
157: */
158: public SProgressBar(int orient) {
159: this (orient, defaultMinimum, defaultMaximum);
160: }
161:
162: /**
163: * Creates a horizontal progress bar
164: * with the specified minimum and maximum.
165: * Sets the initial value of the progress bar to the specified minimum.
166: * By default, a border is painted but a progress string is not.
167: * The <code>BoundedRangeModel</code> that holds the progress bar's data
168: * handles any issues that may arise from improperly setting the
169: * minimum, initial, and maximum values on the progress bar.
170: *
171: * @param min the minimum value of the progress bar
172: * @param max the maximum value of the progress bar
173: * @see BoundedRangeModel
174: * @see #setOrientation
175: * @see #setStringPainted
176: * @see #setString
177: * @see #setIndeterminate
178: */
179: public SProgressBar(int min, int max) {
180: this (defaultOrientation, min, max);
181: }
182:
183: /**
184: * Creates a progress bar using the specified orientation,
185: * minimum, and maximum.
186: * By default, a border is painted but a progress string is not.
187: * Sets the initial value of the progress bar to the specified minimum.
188: * The <code>BoundedRangeModel</code> that holds the progress bar's data
189: * handles any issues that may arise from improperly setting the
190: * minimum, initial, and maximum values on the progress bar.
191: *
192: * @param orient the desired orientation of the progress bar
193: * @param min the minimum value of the progress bar
194: * @param max the maximum value of the progress bar
195: * @see BoundedRangeModel
196: * @see #setOrientation
197: * @see #setStringPainted
198: * @see #setString
199: * @see #setIndeterminate
200: */
201: public SProgressBar(int orient, int min, int max) {
202: // Creating the model this way is a bit simplistic, but
203: // I believe that it is the the most common usage of this
204: // component - it's what people will expect.
205: setModel(new DefaultBoundedRangeModel(min, 0, min, max));
206:
207: setOrientation(orient); // documented with set/getOrientation()
208: setStringPainted(false); // see setStringPainted
209: setString(null); // see getString
210: setIndeterminate(false); // see setIndeterminate
211: }
212:
213: /**
214: * Creates a horizontal progress bar
215: * that uses the specified model
216: * to hold the progress bar's data.
217: * By default, a border is painted but a progress string is not.
218: *
219: * @param newModel the data model for the progress bar
220: * @see #setOrientation
221: * @see #setStringPainted
222: * @see #setString
223: * @see #setIndeterminate
224: */
225: public SProgressBar(BoundedRangeModel newModel) {
226: setModel(newModel);
227:
228: setOrientation(defaultOrientation); // see setOrientation()
229: setStringPainted(false); // see setStringPainted
230: setString(null); // see getString
231: setIndeterminate(false); // see setIndeterminate
232: }
233:
234: /**
235: * Returns <code>SProgressBar.VERTICAL</code> or
236: * <code>SProgressBar.HORIZONTAL</code>, depending on the orientation
237: * of the progress bar. The default orientation is
238: * <code>HORIZONTAL</code>.
239: *
240: * @return <code>HORIZONTAL</code> or <code>VERTICAL</code>
241: * @see #setOrientation
242: */
243: public int getOrientation() {
244: return orientation;
245: }
246:
247: /**
248: * Sets the progress bar's orientation to <code>newOrientation</code>,
249: * which must be <code>SProgressBar.VERTICAL</code> or
250: * <code>SProgressBar.HORIZONTAL</code>. The default orientation
251: * is <code>HORIZONTAL</code>.
252: *
253: * @param newOrientation <code>HORIZONTAL</code> or <code>VERTICAL</code>
254: * @throws IllegalArgumentException if <code>newOrientation</code>
255: * is an illegal value
256: * @see #getOrientation
257: */
258: public void setOrientation(int newOrientation) {
259: if (orientation != newOrientation) {
260: switch (newOrientation) {
261: case SConstants.VERTICAL:
262: case SConstants.HORIZONTAL:
263: int oldOrientation = orientation;
264: orientation = newOrientation;
265: reloadIfChange(oldOrientation, newOrientation);
266: break;
267: default:
268: throw new IllegalArgumentException(newOrientation
269: + " is not a legal orientation");
270: }
271: }
272: }
273:
274: /**
275: * Returns the value of the <code>stringPainted</code> property.
276: *
277: * @return the value of the <code>stringPainted</code> property
278: * @see #setStringPainted
279: * @see #setString
280: */
281: public boolean isStringPainted() {
282: return paintString;
283: }
284:
285: /**
286: * Sets the value of the <code>stringPainted</code> property,
287: * which determines whether the progress bar
288: * should render a progress string.
289: * The default is <code>false</code>:
290: * no string is painted.
291: * Some look and feels might not support progress strings
292: * or might support them only when the progress bar is in determinate mode.
293: *
294: * @param b <code>true</code> if the progress bar should render a string
295: * @see #isStringPainted
296: * @see #setString
297: */
298: public void setStringPainted(boolean b) {
299: //PENDING: specify that string not painted when in indeterminate mode?
300: // or just leave that to the L&F?
301: boolean oldValue = paintString;
302: paintString = b;
303: reloadIfChange(oldValue, paintString);
304: }
305:
306: /**
307: * Returns the current value of the progress string.
308: * If you are providing a custom progress string
309: * by overriding this method,
310: * make sure your implementation calls <code>setString</code> before
311: * calling <code>super.getString</code>.
312: *
313: * @return the value of the percent string
314: * @see #setString
315: */
316: public String getString() {
317: if (progressString != null) {
318: return progressString;
319: } else {
320: if (format == null) {
321: format = NumberFormat.getPercentInstance();
322: }
323: return format.format(new Double(getPercentComplete()));
324: }
325: }
326:
327: /**
328: * Sets the value of the progress string. By default,
329: * this string is <code>null</code>.
330: * If you have provided a custom progress string and want to revert to
331: * the built-in behavior, set the string back to <code>null</code>.
332: * If you are providing a custom progress string
333: * by overriding this method,
334: * make sure that you call <code>setString</code> before
335: * calling <code>getString</code>.
336: * The progress string is painted only if
337: * the <code>isStringPainted</code> method returns <code>true</code>.
338: *
339: * @param s the value of the percent string
340: * @see #getString
341: * @see #setStringPainted
342: * @see #isStringPainted
343: */
344: public void setString(String s) {
345: String oldValue = progressString;
346: progressString = s;
347: reloadIfChange(oldValue, progressString);
348: }
349:
350: /**
351: * Returns the percent complete for the progress bar.
352: * Note that this number is between 0.0 and 1.0.
353: *
354: * @return the percent complete for this progress bar
355: */
356: public double getPercentComplete() {
357: long span = model.getMaximum() - model.getMinimum();
358: double currentValue = model.getValue();
359: return (currentValue - model.getMinimum()) / span;
360: }
361:
362: /**
363: * Sets the color in which the fille region is painted
364: *
365: * @param c a <code>Color</code> value
366: */
367: public void setFilledColor(Color c) {
368: filledColor = c;
369: }
370:
371: /**
372: * Returns the color in which the fille region is painted
373: */
374: public Color getFilledColor() {
375: return filledColor;
376: }
377:
378: /**
379: * Sets the color in which the unfilled region is painted
380: *
381: * @param c a <code>Color</code> value
382: */
383: public void setUnfilledColor(Color c) {
384: unfilledColor = c;
385: }
386:
387: /**
388: * Returns the color in which the unfilled region is painted
389: */
390: public Color getUnfilledColor() {
391: return unfilledColor;
392: }
393:
394: /**
395: * Sets the look-and-feel object that renders this component.
396: *
397: * @param cg a <code>ProgressBarCG</code> object
398: */
399: public void setCG(ProgressBarCG cg) {
400: super .setCG(cg);
401: }
402:
403: /* We pass each Change event to the listeners with the
404: * the progress bar as the event source.
405: * <p>
406: * <strong>Warning:</strong>
407: * Serialized objects of this class will not be compatible with
408: * future Swing releases. The current serialization support is
409: * appropriate for short term storage or RMI between applications running
410: * the same version of Swing. As of 1.4, support for long term storage
411: * of all JavaBeans<sup><font size="-2">TM</font></sup>
412: * has been added to the <code>java.beans</code> package.
413: * Please see {@link java.beans.XMLEncoder}.
414: */
415: private class ModelListener implements ChangeListener, Serializable {
416: public void stateChanged(ChangeEvent e) {
417: reload();
418: fireStateChanged();
419: }
420: }
421:
422: /**
423: * Subclasses that want to handle change events
424: * from the model differently
425: * can override this to return
426: * an instance of a custom <code>ChangeListener</code> implementation.
427: *
428: * @see #changeListener
429: * @see javax.swing.event.ChangeListener
430: * @see javax.swing.BoundedRangeModel
431: */
432: protected ChangeListener createChangeListener() {
433: return new ModelListener();
434: }
435:
436: /**
437: * Adds the specified <code>ChangeListener</code> to the progress bar.
438: *
439: * @param l the <code>ChangeListener</code> to add
440: */
441: public void addChangeListener(ChangeListener l) {
442: addEventListener(ChangeListener.class, l);
443: }
444:
445: /**
446: * Removes a <code>ChangeListener</code> from the progress bar.
447: *
448: * @param l the <code>ChangeListener</code> to remove
449: */
450: public void removeChangeListener(ChangeListener l) {
451: removeEventListener(ChangeListener.class, l);
452: }
453:
454: /**
455: * Returns an array of all the <code>ChangeListener</code>s added
456: * to this progress bar with <code>addChangeListener</code>.
457: *
458: * @return all of the <code>ChangeListener</code>s added or an empty
459: * array if no listeners have been added
460: * @since 1.4
461: */
462: public ChangeListener[] getChangeListeners() {
463: return (ChangeListener[]) getListeners(ChangeListener.class);
464: }
465:
466: /**
467: * Notifies all listeners that have registered interest in
468: * <code>ChangeEvent</code>s.
469: * The event instance
470: * is created if necessary.
471: *
472: * @see javax.swing.event.EventListenerList
473: */
474: protected void fireStateChanged() {
475: // Guaranteed to return a non-null array
476: Object[] listeners = getListenerList();
477: // Process the listeners last to first, notifying
478: // those that are interested in this event
479: for (int i = listeners.length - 2; i >= 0; i -= 2) {
480: if (listeners[i] == ChangeListener.class) {
481: // Lazily create the event:
482: if (changeEvent == null)
483: changeEvent = new ChangeEvent(this );
484: ((ChangeListener) listeners[i + 1])
485: .stateChanged(changeEvent);
486: }
487: }
488: }
489:
490: /**
491: * Returns the data model used by this progress bar.
492: *
493: * @return the <code>BoundedRangeModel</code> currently in use
494: * @see BoundedRangeModel
495: */
496: public BoundedRangeModel getModel() {
497: return model;
498: }
499:
500: /**
501: * Sets the data model used by the <code>SProgressBar</code>.
502: *
503: * @param newModel the <code>BoundedRangeModel</code> to use
504: * description: The data model used by the SProgressBar.
505: */
506: public void setModel(BoundedRangeModel newModel) {
507: // PENDING(???) setting the same model to multiple bars is broken; listeners
508: BoundedRangeModel oldModel = getModel();
509:
510: if (newModel != oldModel) {
511: if (oldModel != null) {
512: oldModel.removeChangeListener(changeListener);
513: changeListener = null;
514: }
515:
516: model = newModel;
517:
518: if (newModel != null) {
519: changeListener = createChangeListener();
520: newModel.addChangeListener(changeListener);
521: }
522:
523: if (model != null) {
524: model.setExtent(0);
525: }
526: reload();
527: }
528: }
529:
530: /* All of the model methods are implemented by delegation. */
531:
532: /**
533: * Returns the progress bar's current value,
534: * which is stored in the progress bar's <code>BoundedRangeModel</code>.
535: * The value is always between the
536: * minimum and maximum values, inclusive. By default, the
537: * value is initialized to be equal to the minimum value.
538: *
539: * @return the current value of the progress bar
540: * @see #setValue
541: * @see BoundedRangeModel#getValue
542: */
543: public int getValue() {
544: return getModel().getValue();
545: }
546:
547: /**
548: * Returns the progress bar's minimum value,
549: * which is stored in the progress bar's <code>BoundedRangeModel</code>.
550: * By default, the minimum value is <code>0</code>.
551: *
552: * @return the progress bar's minimum value
553: * @see #setMinimum
554: * @see BoundedRangeModel#getMinimum
555: */
556: public int getMinimum() {
557: return getModel().getMinimum();
558: }
559:
560: /**
561: * Returns the progress bar's maximum value,
562: * which is stored in the progress bar's <code>BoundedRangeModel</code>.
563: * By default, the maximum value is <code>100</code>.
564: *
565: * @return the progress bar's maximum value
566: * @see #setMaximum
567: * @see BoundedRangeModel#getMaximum
568: */
569: public int getMaximum() {
570: return getModel().getMaximum();
571: }
572:
573: /**
574: * Sets the progress bar's current value
575: * (stored in the progress bar's data model) to <code>n</code>.
576: * The data model (a <code>BoundedRangeModel</code> instance)
577: * handles any mathematical
578: * issues arising from assigning faulty values.
579: * <p/>
580: * If the new value is different from the previous value,
581: * all change listeners are notified.
582: *
583: * @param n the new value
584: * description: The progress bar's current value.
585: * @see #getValue
586: * @see BoundedRangeModel#setValue
587: */
588: public void setValue(int n) {
589: BoundedRangeModel brm = getModel();
590: brm.setValue(n);
591: }
592:
593: /**
594: * Sets the progress bar's minimum value
595: * (stored in the progress bar's data model) to <code>n</code>.
596: * The data model (a <code>BoundedRangeModel</code> instance)
597: * handles any mathematical
598: * issues arising from assigning faulty values.
599: * <p/>
600: * If the minimum value is different from the previous minimum,
601: * all change listeners are notified.
602: *
603: * @param n the new minimum
604: * description: The progress bar's minimum value.
605: * @see #getMinimum
606: * @see #addChangeListener
607: * @see BoundedRangeModel#setMinimum
608: */
609: public void setMinimum(int n) {
610: getModel().setMinimum(n);
611: }
612:
613: /**
614: * Sets the progress bar's maximum value
615: * (stored in the progress bar's data model) to <code>n</code>.
616: * The underlying <code>BoundedRangeModel</code> handles any mathematical
617: * issues arising from assigning faulty values.
618: * <p/>
619: * If the maximum value is different from the previous maximum,
620: * all change listeners are notified.
621: *
622: * @param n the new maximum
623: * description: The progress bar's maximum value.
624: * @see #getMaximum
625: * @see #addChangeListener
626: * @see BoundedRangeModel#setMaximum
627: */
628: public void setMaximum(int n) {
629: getModel().setMaximum(n);
630: }
631:
632: /**
633: * Sets the <code>indeterminate</code> property of the progress bar,
634: * which determines whether the progress bar is in determinate
635: * or indeterminate mode.
636: * By default, the progress bar is determinate.
637: * An indeterminate progress bar continuously displays animation
638: * indicating that an operation of unknown length is occurring.
639: * Some look and feels might not support indeterminate progress bars;
640: * they will ignore this property.
641:
642: * @param newValue <code>true</code> if the progress bar should change to indeterminate mode;
643: * <code>false</code> if it should revert to normal.
644: * @see #isIndeterminate()
645: */
646: public void setIndeterminate(boolean newValue) {
647: indeterminate = newValue;
648: }
649:
650: /**
651: * Returns the value of the <code>indeterminate</code> property.
652: *
653: * @return the value of the <code>indeterminate</code> property or normal (false)?
654: * @see #setIndeterminate
655: */
656: public boolean isIndeterminate() {
657: return indeterminate;
658: }
659:
660: /**
661: * Sets the size of the graphically rendered progress bar element.
662: * @param dimension the size as dimension
663: */
664: public void setProgressBarDimension(SDimension dimension) {
665: progressBarDimension = dimension;
666:
667: }
668:
669: /**
670: * @return The size of the graphically rendered progress bar element if it has been set, else null
671: */
672: public SDimension getProgressBarDimension() {
673: return progressBarDimension;
674: }
675: }
|