001: /*
002: * $Id: SProgressBar.java 2419 2006-01-23 10:20:41Z hengels $
003: * Copyright 2000,2006 wingS development team.
004: *
005: * This file is part of wingS (http://wingsframework.org).
006: *
007: * wingS is free software; you can redistribute it and/or modify
008: * it under the terms of the GNU Lesser General Public License
009: * as published by the Free Software Foundation; either version 2.1
010: * of the License, or (at your option) any later version.
011: *
012: * Please see COPYING for the complete licence.
013: */
014:
015: package org.wings;
016:
017: import java.io.Serializable;
018: import javax.swing.BoundedRangeModel;
019: import javax.swing.DefaultBoundedRangeModel;
020: import javax.swing.event.ChangeEvent;
021: import javax.swing.event.ChangeListener;
022:
023: /**
024: * A component that lets the user graphically select a value by sliding
025: * a knob within a bounded interval.
026: * The displayed slider is highly dependent on the graphics used,
027: * see http://developer.yahoo.com/yui/slider/.
028: *
029: * @author Christian Schyma
030: */
031: public class SSlider extends SComponent implements SConstants,
032: LowLevelEventListener {
033:
034: /**
035: * The data model that handles the numeric maximum value,
036: * minimum value, and current-position value for the slider.
037: */
038: protected BoundedRangeModel sliderModel;
039:
040: /**
041: * The number of values between the major tick marks.
042: */
043: protected int majorTickSpacing = 1;
044:
045: /**
046: * If true, the knob (and the data value it represents)
047: * resolve to the closest tick mark next to where the user
048: * positioned the knob. The default is false.
049: * @see #setSnapToTicks
050: */
051: protected boolean snapToTicks = false;
052:
053: /**
054: * @see #setOrientation
055: */
056: protected int orientation;
057:
058: /**
059: * The changeListener (no suffix) is the listener we add to the
060: * Sliders model. By default this listener just forwards events
061: * to ChangeListeners (if any) added directly to the slider.
062: *
063: * @see #addChangeListener
064: * @see #createChangeListener
065: */
066: protected ChangeListener changeListener = createChangeListener();
067:
068: /**
069: * Creates a horizontal slider with the range 0 to 100 and
070: * an initial value of 50.
071: */
072: public SSlider() {
073: this (HORIZONTAL, 0, 100, 50);
074: }
075:
076: /**
077: * Creates a slider using the specified orientation with the
078: * range 0 to 100 and an initial value of 50.
079: */
080: public SSlider(int orientation) {
081: this (orientation, 0, 100, 50);
082: }
083:
084: /**
085: * Creates a horizontal slider using the specified min and max
086: * with an initial value equal to the average of the min plus max.
087: */
088: public SSlider(int min, int max) {
089: this (HORIZONTAL, min, max, (min + max) / 2);
090: }
091:
092: /**
093: * Creates a horizontal slider using the specified min, max and value.
094: */
095: public SSlider(int min, int max, int value) {
096: this (HORIZONTAL, min, max, value);
097: }
098:
099: /**
100: * Creates a slider with the specified orientation and the
101: * specified minimum, maximum, and initial values.
102: *
103: * @exception IllegalArgumentException if orientation is not one of VERTICAL, HORIZONTAL
104: *
105: * @see #setOrientation
106: * @see #setMinimum
107: * @see #setMaximum
108: * @see #setValue
109: */
110: public SSlider(int orientation, int min, int max, int value) {
111: checkOrientation(orientation);
112: this .orientation = orientation;
113: sliderModel = new DefaultBoundedRangeModel(value, 0, min, max);
114: sliderModel.addChangeListener(changeListener);
115: }
116:
117: private void checkOrientation(int orientation) {
118: switch (orientation) {
119: case VERTICAL:
120: case HORIZONTAL:
121: break;
122: default:
123: throw new IllegalArgumentException(
124: "orientation must be one of: VERTICAL, HORIZONTAL");
125: }
126: }
127:
128: /**
129: * Return this slider's vertical or horizontal orientation.
130: * @return VERTICAL or HORIZONTAL
131: * @see #setOrientation
132: */
133: public int getOrientation() {
134: return orientation;
135: }
136:
137: /**
138: * Set the scrollbars orientation to either VERTICAL or HORIZONTAL.
139: *
140: * @exception IllegalArgumentException if orientation is not one of VERTICAL, HORIZONTAL
141: * @see #getOrientation
142: */
143: public void setOrientation(int orientation) {
144: checkOrientation(orientation);
145: int oldValue = this .orientation;
146: this .orientation = orientation;
147: reloadIfChange(orientation, oldValue);
148: }
149:
150: /**
151: * We pass Change events along to the listeners with the
152: * the slider (instead of the model itself) as the event source.
153: */
154: private class ModelListener implements ChangeListener, Serializable {
155: public void stateChanged(ChangeEvent e) {
156: fireStateChanged();
157: }
158: }
159:
160: /**
161: * Subclasses that want to handle model ChangeEvents differently
162: * can override this method to return their own ChangeListener
163: * implementation. The default ChangeListener just forwards
164: * ChangeEvents to the ChangeListeners added directly to the slider.
165: *
166: * @see #fireStateChanged
167: */
168: protected ChangeListener createChangeListener() {
169: return new ModelListener();
170: }
171:
172: /**
173: * Adds a ChangeListener to the slider.
174: *
175: * @param l the ChangeListener to add
176: * @see #removeChangeListener
177: */
178: public void addChangeListener(ChangeListener l) {
179: addEventListener(ChangeListener.class, l);
180: }
181:
182: /**
183: * Removes a ChangeListener from the slider.
184: *
185: * @param l the ChangeListener to remove
186: * @see #addChangeListener
187: *
188: */
189: public void removeChangeListener(ChangeListener l) {
190: removeEventListener(ChangeListener.class, l);
191: }
192:
193: /**
194: * Returns an array of all the <code>ChangeListener</code>s added
195: * to this slider with <code>addChangeListener</code>.
196: *
197: * @return all of the <code>ChangeListener</code>s added or an empty
198: * array if no listeners have been added
199: */
200: public ChangeListener[] getChangeListeners() {
201: return (ChangeListener[]) getListeners(ChangeListener.class);
202: }
203:
204: /**
205: * Notifies all listeners that have registered interest in
206: * <code>ChangeEvent</code>s.
207: */
208: protected void fireStateChanged() {
209: ChangeListener[] listeners = (ChangeListener[]) getListeners(ChangeListener.class);
210: for (int i = 0; i < listeners.length; i++) {
211: ChangeListener listener = listeners[i];
212: listener.stateChanged(new ChangeEvent(this ));
213: }
214: }
215:
216: /**
217: * Returns data model that handles the sliders three
218: * fundamental properties: minimum, maximum, value.
219: *
220: * @see #setModel
221: */
222: public BoundedRangeModel getModel() {
223: return sliderModel;
224: }
225:
226: /**
227: * Sets the model that handles the sliders three
228: * fundamental properties: minimum, maximum, value.
229: *
230: * @see #getModel
231: */
232: public void setModel(BoundedRangeModel newModel) {
233: BoundedRangeModel oldModel = getModel();
234:
235: if (oldModel != null) {
236: oldModel.removeChangeListener(changeListener);
237: }
238:
239: sliderModel = newModel;
240:
241: if (newModel != null) {
242: newModel.addChangeListener(changeListener);
243:
244: }
245:
246: reloadIfChange(newModel, oldModel);
247: }
248:
249: /**
250: * Returns the sliders value.
251: * @return the models value property
252: * @see #setValue
253: */
254: public int getValue() {
255: return getModel().getValue();
256: }
257:
258: /**
259: * Sets the sliders current value. This method just forwards
260: * the value to the model.
261: *
262: * @see #getValue
263: */
264: public void setValue(int n) {
265: BoundedRangeModel m = getModel();
266: int oldValue = m.getValue();
267: m.setValue(n);
268: reloadIfChange(n, oldValue);
269: }
270:
271: /**
272: * Returns the minimum value supported by the slider.
273: *
274: * @return the value of the models minimum property
275: * @see #setMinimum
276: */
277: public int getMinimum() {
278: return getModel().getMinimum();
279: }
280:
281: /**
282: * Sets the models minimum property.
283: *
284: * @see #getMinimum
285: * @see BoundedRangeModel#setMinimum
286: */
287: public void setMinimum(int minimum) {
288: int oldMin = getModel().getMinimum();
289: getModel().setMinimum(minimum);
290: reloadIfChange(minimum, oldMin);
291: }
292:
293: /**
294: * Returns the maximum value supported by the slider.
295: *
296: * @return the value of the models maximum property
297: * @see #setMaximum
298: */
299: public int getMaximum() {
300: return getModel().getMaximum();
301: }
302:
303: /**
304: * Sets the models maximum property.
305: *
306: * @see #getMaximum
307: * @see BoundedRangeModel#setMaximum
308: */
309: public void setMaximum(int maximum) {
310: int oldMax = getModel().getMaximum();
311: getModel().setMaximum(maximum);
312: reloadIfChange(maximum, oldMax);
313: }
314:
315: public void fireFinalEvents() {
316: fireStateChanged();
317: }
318:
319: public void processLowLevelEvent(String action, String[] values) {
320: processKeyEvents(values);
321: if (action.endsWith("_keystroke"))
322: return;
323:
324: if (action.compareTo(getName() + "_val") == 0) {
325: setValue(Integer.parseInt(values[0]));
326: SForm.addArmedComponent(this );
327: }
328: }
329:
330: public boolean isEpochCheckEnabled() {
331: return true;
332: }
333:
334: public void fireIntermediateEvents() {
335: // nothing to do
336: }
337:
338: /**
339: * This method returns the major tick spacing. The number that is returned
340: * represents the distance, measured in values, between each major tick mark.
341: * If you have a slider with a range from 0 to 50 and the major tick spacing
342: * is set to 10, you will get major ticks next to the following values:
343: * 0, 10, 20, 30, 40, 50.
344: *
345: * @return the number of values between major ticks
346: * @see #setMajorTickSpacing
347: */
348: public int getMajorTickSpacing() {
349: return majorTickSpacing;
350: }
351:
352: /**
353: * This method sets the major tick spacing. The number that is passed-in
354: * represents the distance, measured in values, between each major tick mark.
355: * If you have a slider with a range from 0 to 50 and the major tick spacing
356: * is set to 10, you will get major ticks next to the following values:
357: * 0, 10, 20, 30, 40, 50.
358: *
359: * @see #getMajorTickSpacing
360: */
361: public void setMajorTickSpacing(int n) {
362: int oldValue = majorTickSpacing;
363: majorTickSpacing = n;
364: reloadIfChange(n, oldValue);
365: }
366:
367: /**
368: * Returns true if the knob (and the data value it represents)
369: * resolve to the closest tick mark next to where the user
370: * positioned the knob.
371: *
372: * @return true if the value snaps to the nearest tick mark, else false
373: * @see #setSnapToTicks
374: */
375: public boolean getSnapToTicks() {
376: return snapToTicks;
377: }
378:
379: /**
380: * Specifying true makes the knob (and the data value it represents)
381: * resolve to the closest tick mark next to where the user
382: * positioned the knob.
383: *
384: * @param b true to snap the knob to the nearest tick mark
385: * @see #getSnapToTicks
386: */
387: public void setSnapToTicks(boolean b) {
388: boolean oldValue = snapToTicks;
389: snapToTicks = b;
390: reloadIfChange(b, oldValue);
391: }
392:
393: /**
394: * The maximum number of pixels the slider thumb can be moved. Usefull when
395: * using different slider thumb or bar graphics.
396: * @param max pixels
397: */
398: public void setMaxPixelConstraint(int max) {
399: this .putClientProperty("maxPixelConstraint", new Integer(max));
400: }
401:
402: }
|