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.ScrollBarCG;
016:
017: import javax.swing.*;
018: import javax.swing.event.ChangeEvent;
019: import javax.swing.event.ChangeListener;
020: import java.awt.*;
021: import java.awt.event.AdjustmentEvent;
022: import java.awt.event.AdjustmentListener;
023: import java.io.Serializable;
024:
025: /**
026: * Base class for adjustable elements like {@link SScrollBar} and {@link SPageScroller}
027: *
028: * @author <a href="mailto:haaf@mercatis.de">Armin Haaf</a>
029: */
030: public abstract class SAbstractAdjustable extends SComponent implements
031: Adjustable, LowLevelEventListener {
032: public static final int UNIT = 0;
033:
034: public static final int BLOCK = 1;
035:
036: public static final int MARGIN = 2;
037:
038: protected boolean changeFromEvent;
039:
040: /**
041: * All changes from the model are treated as though the user moved
042: * the scrollbar knob.
043: */
044: private final ChangeListener fwdAdjustmentEvents = new ModelListener();
045:
046: /**
047: * The model that represents the scrollbar's minimum, maximum, extent
048: * (aka "visibleAmount") and current value.
049: *
050: * @see #setModel
051: */
052: protected SBoundedRangeModel model;
053:
054: /**
055: * @see #setUnitIncrement
056: */
057: protected int unitIncrement;
058:
059: /**
060: * @see #setBlockIncrement
061: */
062: protected int blockIncrement;
063:
064: /**
065: * @see #setBlockIncrement
066: */
067: protected int orientation;
068:
069: /**
070: * Creates a scrollbar with the specified orientation,
071: * value, extent, mimimum, and maximum.
072: * The "extent" is the size of the viewable area. It is also known
073: * as the "visible amount".
074: * <p/>
075: * Note: Use <code>setBlockIncrement</code> to set the block
076: * increment to a size slightly smaller than the view's extent.
077: * That way, when the user jumps the knob to an adjacent position,
078: * one or two lines of the original contents remain in view.
079: *
080: * @throws IllegalArgumentException if orientation is not one of VERTICAL, HORIZONTAL
081: * @see #setOrientation
082: * @see #setValue
083: * @see #setVisibleAmount
084: * @see #setMinimum
085: * @see #setMaximum
086: */
087: public SAbstractAdjustable(int value, int extent, int min, int max) {
088: this (new SDefaultBoundedRangeModel(value, extent, min, max));
089: }
090:
091: public SAbstractAdjustable(SBoundedRangeModel model) {
092: this .model = model;
093: this .model.addChangeListener(fwdAdjustmentEvents);
094: this .unitIncrement = 1;
095: this .blockIncrement = (model.getExtent() == 0) ? 1 : model
096: .getExtent();
097: }
098:
099: /**
100: * Creates a scrollbar with the specified orientation
101: * and the following initial values:
102: * <pre>
103: * minimum = 0
104: * maximum = 100
105: * value = 0
106: * extent = 10
107: * </pre>
108: */
109: public SAbstractAdjustable() {
110: this (0, 10, 0, 100);
111: }
112:
113: /**
114: * Returns data model that handles the scrollbar's four
115: * fundamental properties: minimum, maximum, value, extent.
116: *
117: * @see #setModel
118: */
119: public final SBoundedRangeModel getModel() {
120: return model;
121: }
122:
123: /**
124: * Sets the model that handles the scrollbar's four
125: * fundamental properties: minimum, maximum, value, extent.
126: * @see #getModel
127: */
128: public void setModel(SBoundedRangeModel newModel) {
129: reloadIfChange(this .model, newModel);
130: if (model != null) {
131: model.removeChangeListener(fwdAdjustmentEvents);
132: }
133: model = newModel;
134: if (model != null) {
135: model.addChangeListener(fwdAdjustmentEvents);
136: }
137: }
138:
139: /**
140: * Returns the amount to change the scrollbar's value by,
141: * given a unit up/down request. A ScrollBarUI implementation
142: * typically calls this method when the user clicks on a scrollbar
143: * up/down arrow and uses the result to update the scrollbar's
144: * value. Subclasses my override this method to compute
145: * a value, e.g. the change required to scroll up or down one
146: * (variable height) line text or one row in a table.
147: * <p/>
148: * The JScrollPane component creates scrollbars (by default)
149: * that override this method and delegate to the viewports
150: * Scrollable view, if it has one. The Scrollable interface
151: * provides a more specialized version of this method.
152: *
153: * @param direction is -1 or 1 for up/down respectively
154: * @return the value of the unitIncrement property
155: * @see #setUnitIncrement
156: * @see #setValue
157: */
158: public int getUnitIncrement(int direction) {
159: return unitIncrement;
160: }
161:
162: /**
163: * Sets the unitIncrement property.
164: *
165: * @see #getUnitIncrement
166: */
167: public void setUnitIncrement(int unitIncrement) {
168: reloadIfChange(this .unitIncrement, unitIncrement);
169: this .unitIncrement = unitIncrement;
170: }
171:
172: /**
173: * Returns the amount to change the scrollbar's value by,
174: * given a block (usually "page") up/down request. A ScrollBarUI
175: * implementation typically calls this method when the user clicks
176: * above or below the scrollbar "knob" to change the value
177: * up or down by large amount. Subclasses my override this
178: * method to compute a value, e.g. the change required to scroll
179: * up or down one paragraph in a text document.
180: * <p/>
181: * The JScrollPane component creates scrollbars (by default)
182: * that override this method and delegate to the viewports
183: * Scrollable view, if it has one. The Scrollable interface
184: * provides a more specialized version of this method.
185: *
186: * @param direction is -1 or 1 for up/down respectively
187: * @return the value of the blockIncrement property
188: * @see #setBlockIncrement
189: * @see #setValue
190: */
191: public int getBlockIncrement(int direction) {
192: return blockIncrement;
193: }
194:
195: /**
196: * Sets the blockIncrement property.
197: * The scrollbar's block increment.
198: * @see #getBlockIncrement()
199: */
200: public void setBlockIncrement(int blockIncrement) {
201: reloadIfChange(this .blockIncrement, blockIncrement);
202: this .blockIncrement = blockIncrement;
203: }
204:
205: /**
206: * For backwards compatibility with java.awt.Scrollbar.
207: *
208: * @see Adjustable#getUnitIncrement
209: * @see #getUnitIncrement(int)
210: */
211: public final int getUnitIncrement() {
212: return unitIncrement;
213: }
214:
215: /**
216: * For backwards compatibility with java.awt.Scrollbar.
217: *
218: * @see Adjustable#getBlockIncrement
219: * @see #getBlockIncrement(int)
220: */
221: public final int getBlockIncrement() {
222: return blockIncrement;
223: }
224:
225: /**
226: * Returns the scrollbar's value.
227: *
228: * @return the model's value property
229: * @see #setValue
230: */
231: public final int getValue() {
232: return getModel().getValue();
233: }
234:
235: /**
236: * Sets the scrollbar's value. This method just forwards the value
237: * to the model.
238: *
239: * @see #getValue
240: * @see BoundedRangeModel#setValue
241: */
242: public void setValue(int value) {
243: getModel().setValue(value);
244: }
245:
246: public final int getExtent() {
247: return getModel().getExtent();
248: }
249:
250: public void setExtent(int value) {
251: getModel().setExtent(value);
252: }
253:
254: /**
255: * Returns the scrollbar's extent, aka its "visibleAmount". In many
256: * scrollbar look and feel implementations the size of the
257: * scrollbar "knob" or "thumb" is proportional to the extent.
258: *
259: * @return the value of the model's extent property
260: * @see #setVisibleAmount
261: */
262: public final int getVisibleAmount() {
263: return getModel().getExtent();
264: }
265:
266: /**
267: * Set the model's extent property:
268: * The amount of the view that is currently visible.
269: *
270: * @see #getVisibleAmount
271: * @see BoundedRangeModel#setExtent
272: */
273: public void setVisibleAmount(int extent) {
274: getModel().setExtent(extent);
275: }
276:
277: /**
278: * Returns the minimum value supported by the scrollbar
279: * (usually zero).
280: *
281: * @return the value of the model's minimum property
282: * @see #setMinimum
283: */
284: public final int getMinimum() {
285: return getModel().getMinimum();
286: }
287:
288: /**
289: * Sets the scrollbar's minimum value..
290: *
291: *
292: * @see #getMinimum
293: * @see BoundedRangeModel#setMinimum
294: */
295: public void setMinimum(int minimum) {
296: getModel().setMinimum(minimum);
297: }
298:
299: /**
300: * The maximum value of the scrollbar is maximum - extent.
301: *
302: * @return the value of the model's maximum property
303: * @see #setMaximum
304: */
305: public final int getMaximum() {
306: return getModel().getMaximum();
307: }
308:
309: /**
310: * Sets the model's maximum property. Note that the scrollbar's value
311: * can only be set to maximum - extent. The scrollbar's maximum value.
312: *
313: * @see #getMaximum
314: * @see BoundedRangeModel#setMaximum
315: */
316: public void setMaximum(int maximum) {
317: getModel().setMaximum(maximum);
318: }
319:
320: /**
321: * Returns the adjustable's orientation (horizontal or vertical).
322: *
323: * @return VERTICAL or HORIZONTAL
324: * @see #setOrientation
325: * @see java.awt.Adjustable#getOrientation
326: */
327: public final int getOrientation() {
328: return orientation;
329: }
330:
331: /**
332: * Set the scrollbar's orientation to either VERTICAL or
333: * HORIZONTAL.
334: *
335: * @throws IllegalArgumentException if orientation is not one of VERTICAL, HORIZONTAL
336: * @see #getOrientation
337: */
338: public void setOrientation(int orientation) {
339: switch (orientation) {
340: case SConstants.VERTICAL:
341: this .orientation = orientation;
342: setPreferredSize(SDimension.FULLHEIGHT);
343: break;
344: case SConstants.HORIZONTAL:
345: this .orientation = orientation;
346: setPreferredSize(SDimension.FULLWIDTH);
347: break;
348: default:
349: throw new IllegalArgumentException(
350: "orientation must be one of: VERTICAL, HORIZONTAL");
351: }
352: }
353:
354: /**
355: * True if the scrollbar knob is being dragged.
356: *
357: * @return the value of the model's valueIsAdjusting property
358: * @see #setValueIsAdjusting
359: */
360: public final boolean getValueIsAdjusting() {
361: return getModel().getValueIsAdjusting();
362: }
363:
364: /**
365: * Sets the model's valueIsAdjusting property. Scrollbar look and
366: * feel implementations should set this property to true when
367: * a knob drag begins, and to false when the drag ends. The
368: * scrollbar model will not generate ChangeEvents while
369: * valueIsAdjusting is true. True if the scrollbar thumb is being dragged.
370: *
371: * @see #getValueIsAdjusting
372: * @see BoundedRangeModel#setValueIsAdjusting
373: */
374: public void setValueIsAdjusting(boolean b) {
375: getModel().setValueIsAdjusting(b);
376: }
377:
378: /**
379: * Sets the four BoundedRangeModel properties after forcing
380: * the arguments to obey the usual constraints:
381: * <pre>
382: * minimum <= value <= value+extent <= maximum
383: * </pre>
384: * <p/>
385: *
386: * @see BoundedRangeModel#setRangeProperties
387: * @see #setValue
388: * @see #setVisibleAmount
389: * @see #setMinimum
390: * @see #setMaximum
391: */
392: public void setValues(int newValue, int newExtent, int newMin,
393: int newMax) {
394: BoundedRangeModel m = getModel();
395: m.setRangeProperties(newValue, newExtent, newMin, newMax, m
396: .getValueIsAdjusting());
397: }
398:
399: public void processLowLevelEvent(String action, String[] values) {
400: processKeyEvents(values);
401: if (action.endsWith("_keystroke"))
402: return;
403:
404: getModel().setDelayEvents(true);
405: for (int i = 0; i < values.length; i++) {
406: try {
407: setValue(Integer.parseInt(values[i]));
408: } catch (NumberFormatException ex) {
409: // ignore
410: }
411: }
412:
413: SForm.addArmedComponent(this );
414: getModel().setDelayEvents(false);
415: }
416:
417: public void fireIntermediateEvents() {
418: changeFromEvent = true;
419: getModel().fireDelayedIntermediateEvents();
420: changeFromEvent = false;
421: }
422:
423: public void fireFinalEvents() {
424: super .fireFinalEvents();
425: changeFromEvent = true;
426: getModel().fireDelayedFinalEvents();
427: changeFromEvent = false;
428: }
429:
430: public boolean isChangeFromEvent() {
431: return changeFromEvent;
432: }
433:
434: /**
435: * Adds an AdjustmentListener. Adjustment listeners are notified
436: * each time the scrollbar's model changes. Adjustment events are
437: * provided for backwards compatability with java.awt.Scrollbar.
438: * <p/>
439: * Note that the AdjustmentEvents type property will always have a
440: * placeholder value of AdjustmentEvent.TRACK because all changes
441: * to a BoundedRangeModels value are considered equivalent. To change
442: * the value of a BoundedRangeModel one just sets its value property,
443: * i.e. model.setValue(123). No information about the origin of the
444: * change, e.g. it's a block decrement, is provided. We don't try
445: * fabricate the origin of the change here.
446: *
447: * @param l the AdjustmentLister to add
448: * @see #removeAdjustmentListener
449: * @see BoundedRangeModel#addChangeListener
450: */
451: public void addAdjustmentListener(AdjustmentListener l) {
452: addEventListener(AdjustmentListener.class, l);
453: }
454:
455: /**
456: * Removes an AdjustmentEvent listener.
457: *
458: * @param l the AdjustmentLister to remove
459: * @see #addAdjustmentListener
460: */
461: public void removeAdjustmentListener(AdjustmentListener l) {
462: removeEventListener(AdjustmentListener.class, l);
463: }
464:
465: /*
466: * Notify listeners that the scrollbar's model has changed.
467: *
468: * @see #addAdjustmentListener
469: * @see EventListenerList
470: */
471: protected void fireAdjustmentValueChanged(int id, int type,
472: int value) {
473: AdjustmentEvent e = null;
474:
475: Object[] listeners = getListenerList();
476: for (int i = listeners.length - 2; i >= 0; i -= 2) {
477: if (listeners[i] == AdjustmentListener.class) {
478: if (e == null) {
479: e = new AdjustmentEvent(this , id, type, value);
480: }
481: ((AdjustmentListener) listeners[i + 1])
482: .adjustmentValueChanged(e);
483: }
484: }
485: }
486:
487: /**
488: * This class listens to ChangeEvents on the model and forwards
489: * AdjustmentEvents for the sake of backwards compatibility.
490: * Unfortunately there's no way to determine the proper
491: * type of the AdjustmentEvent as all updates to the model's
492: * value are considered equivalent.
493: */
494: private class ModelListener implements ChangeListener, Serializable {
495: public void stateChanged(ChangeEvent e) {
496: int id = AdjustmentEvent.ADJUSTMENT_VALUE_CHANGED;
497: int type = AdjustmentEvent.TRACK;
498: fireAdjustmentValueChanged(id, type, getValue());
499: adjust();
500: }
501: }
502:
503: protected abstract void adjust();
504:
505: /** @see LowLevelEventListener#isEpochCheckEnabled() */
506: private boolean epochCheckEnabled = true;
507:
508: /** @see LowLevelEventListener#isEpochCheckEnabled() */
509: public boolean isEpochCheckEnabled() {
510: return epochCheckEnabled;
511: }
512:
513: /** @see LowLevelEventListener#isEpochCheckEnabled() */
514: public void setEpochCheckEnabled(boolean epochCheckEnabled) {
515: this.epochCheckEnabled = epochCheckEnabled;
516: }
517:
518: }
|