org.wings.SAbstractAdjustable Maven / Gradle / Ivy
The newest version!
/*
* Copyright 2000,2005 wingS development team.
*
* This file is part of wingS (http://wingsframework.org).
*
* wingS is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*
* Please see COPYING for the complete licence.
*/
package org.wings;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.*;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.io.Serializable;
/**
* Base class for adjustable elements like {@link SScrollBar} and {@link SPageScroller}
*
* @author Armin Haaf
*/
public abstract class SAbstractAdjustable extends SComponent implements Adjustable, LowLevelEventListener {
public static final int UNIT = 0;
public static final int BLOCK = 1;
public static final int MARGIN = 2;
protected boolean changeFromEvent;
/**
* All changes from the model are treated as though the user moved
* the scrollbar knob.
*/
private final ChangeListener fwdAdjustmentEvents = new ModelListener();
/**
* The model that represents the scrollbar's minimum, maximum, extent
* (aka "visibleAmount") and current value.
*
* @see #setModel
*/
protected SBoundedRangeModel model;
/**
* @see #setUnitIncrement
*/
protected int unitIncrement;
/**
* @see #setBlockIncrement
*/
protected int blockIncrement;
/**
* @see #setBlockIncrement
*/
protected int orientation;
/**
* Creates a scrollbar with the specified orientation,
* value, extent, mimimum, and maximum.
* The "extent" is the size of the viewable area. It is also known
* as the "visible amount".
*
* Note: Use setBlockIncrement
to set the block
* increment to a size slightly smaller than the view's extent.
* That way, when the user jumps the knob to an adjacent position,
* one or two lines of the original contents remain in view.
*
* @throws IllegalArgumentException if orientation is not one of VERTICAL, HORIZONTAL
* @see #setOrientation
* @see #setValue
* @see #setVisibleAmount
* @see #setMinimum
* @see #setMaximum
*/
public SAbstractAdjustable(int value, int extent, int min, int max) {
this(new SDefaultBoundedRangeModel(value, extent, min, max));
}
public SAbstractAdjustable(SBoundedRangeModel model) {
this.model = model;
this.model.addChangeListener(fwdAdjustmentEvents);
this.unitIncrement = 1;
this.blockIncrement = (model.getExtent() == 0) ? 1 : model.getExtent();
}
/**
* Creates a scrollbar with the specified orientation
* and the following initial values:
*
* minimum = 0
* maximum = 100
* value = 0
* extent = 10
*
*/
public SAbstractAdjustable() {
this(0, 10, 0, 100);
}
/**
* Returns data model that handles the scrollbar's four
* fundamental properties: minimum, maximum, value, extent.
*
* @see #setModel
*/
public final SBoundedRangeModel getModel() {
return model;
}
/**
* Sets the model that handles the scrollbar's four
* fundamental properties: minimum, maximum, value, extent.
* @see #getModel
*/
public void setModel(SBoundedRangeModel newModel) {
SBoundedRangeModel oldVal = this.model;
reloadIfChange(this.model, newModel);
if (model != null) {
model.removeChangeListener(fwdAdjustmentEvents);
}
model = newModel;
if (model != null) {
model.addChangeListener(fwdAdjustmentEvents);
}
propertyChangeSupport.firePropertyChange("model", oldVal, this.model);
}
/**
* Returns the amount to change the scrollbar's value by,
* given a unit up/down request. A ScrollBarUI implementation
* typically calls this method when the user clicks on a scrollbar
* up/down arrow and uses the result to update the scrollbar's
* value. Subclasses my override this method to compute
* a value, e.g. the change required to scroll up or down one
* (variable height) line text or one row in a table.
*
* The JScrollPane component creates scrollbars (by default)
* that override this method and delegate to the viewports
* Scrollable view, if it has one. The Scrollable interface
* provides a more specialized version of this method.
*
* @param direction is -1 or 1 for up/down respectively
* @return the value of the unitIncrement property
* @see #setUnitIncrement
* @see #setValue
*/
public int getUnitIncrement(int direction) {
return unitIncrement;
}
/**
* Sets the unitIncrement property.
*
* @see #getUnitIncrement
*/
@Override
public void setUnitIncrement(int unitIncrement) {
int oldVal = this.unitIncrement;
reloadIfChange(this.unitIncrement, unitIncrement);
this.unitIncrement = unitIncrement;
propertyChangeSupport.firePropertyChange("unitIncrement", oldVal, this.unitIncrement);
}
/**
* Returns the amount to change the scrollbar's value by,
* given a block (usually "page") up/down request. A ScrollBarUI
* implementation typically calls this method when the user clicks
* above or below the scrollbar "knob" to change the value
* up or down by large amount. Subclasses my override this
* method to compute a value, e.g. the change required to scroll
* up or down one paragraph in a text document.
*
* The JScrollPane component creates scrollbars (by default)
* that override this method and delegate to the viewports
* Scrollable view, if it has one. The Scrollable interface
* provides a more specialized version of this method.
*
* @param direction is -1 or 1 for up/down respectively
* @return the value of the blockIncrement property
* @see #setBlockIncrement
* @see #setValue
*/
public int getBlockIncrement(int direction) {
return blockIncrement;
}
/**
* Sets the blockIncrement property.
* The scrollbar's block increment.
* @see #getBlockIncrement()
*/
@Override
public void setBlockIncrement(int blockIncrement) {
int oldVal = this.blockIncrement;
reloadIfChange(this.blockIncrement, blockIncrement);
this.blockIncrement = blockIncrement;
propertyChangeSupport.firePropertyChange("blockIncrement", oldVal, this.blockIncrement);
}
/**
* For backwards compatibility with java.awt.Scrollbar.
*
* @see Adjustable#getUnitIncrement
* @see #getUnitIncrement(int)
*/
@Override
public final int getUnitIncrement() {
return unitIncrement;
}
/**
* For backwards compatibility with java.awt.Scrollbar.
*
* @see Adjustable#getBlockIncrement
* @see #getBlockIncrement(int)
*/
@Override
public final int getBlockIncrement() {
return blockIncrement;
}
/**
* Returns the scrollbar's value.
*
* @return the model's value property
* @see #setValue
*/
@Override
public final int getValue() {
return model.getValue();
}
/**
* Sets the scrollbar's value. This method just forwards the value
* to the model.
*
* @see #getValue
* @see BoundedRangeModel#setValue
*/
@Override
public void setValue(int value) {
int oldVal = model.getValue();
model.setValue(value);
propertyChangeSupport.firePropertyChange("value", oldVal, model.getValue());
}
public final int getExtent() {
return model.getExtent();
}
public void setExtent(int value) {
int oldVal = model.getExtent();
model.setExtent(value);
propertyChangeSupport.firePropertyChange("extent", oldVal, model.getExtent());
}
/**
* Returns the scrollbar's extent, aka its "visibleAmount". In many
* scrollbar look and feel implementations the size of the
* scrollbar "knob" or "thumb" is proportional to the extent.
*
* @return the value of the model's extent property
* @see #setVisibleAmount
*/
@Override
public final int getVisibleAmount() {
return model.getExtent();
}
/**
* Set the model's extent property:
* The amount of the view that is currently visible.
*
* @see #getVisibleAmount
* @see BoundedRangeModel#setExtent
*/
@Override
public void setVisibleAmount(int extent) {
int oldVal = model.getExtent();
model.setExtent(extent);
propertyChangeSupport.firePropertyChange("visibleAmount", oldVal, model.getExtent());
}
/**
* Returns the minimum value supported by the scrollbar
* (usually zero).
*
* @return the value of the model's minimum property
* @see #setMinimum
*/
@Override
public final int getMinimum() {
return model.getMinimum();
}
/**
* Sets the scrollbar's minimum value..
*
*
* @see #getMinimum
* @see BoundedRangeModel#setMinimum
*/
@Override
public void setMinimum(int minimum) {
int oldVal = model.getMinimum();
model.setMinimum(minimum);
propertyChangeSupport.firePropertyChange("minimum", oldVal, model.getMinimum());
}
/**
* The maximum value of the scrollbar is maximum - extent.
*
* @return the value of the model's maximum property
* @see #setMaximum
*/
@Override
public final int getMaximum() {
return model.getMaximum();
}
/**
* Sets the model's maximum property. Note that the scrollbar's value
* can only be set to maximum - extent. The scrollbar's maximum value.
*
* @see #getMaximum
* @see BoundedRangeModel#setMaximum
*/
@Override
public void setMaximum(int maximum) {
int oldVal = model.getMaximum();
model.setMaximum(maximum);
propertyChangeSupport.firePropertyChange("maximum", oldVal, model.getMaximum());
}
/**
* Returns the adjustable's orientation (horizontal or vertical).
*
* @return VERTICAL or HORIZONTAL
* @see #setOrientation
* @see java.awt.Adjustable#getOrientation
*/
@Override
public final int getOrientation() {
return orientation;
}
/**
* Set the scrollbar's orientation to either VERTICAL or
* HORIZONTAL.
*
* @throws IllegalArgumentException if orientation is not one of VERTICAL, HORIZONTAL
* @see #getOrientation
*/
public void setOrientation(int orientation) {
int oldVal = this.orientation;
switch (orientation) {
case SConstants.VERTICAL:
this.orientation = orientation;
setPreferredSize(SDimension.FULLHEIGHT);
break;
case SConstants.HORIZONTAL:
this.orientation = orientation;
setPreferredSize(SDimension.FULLWIDTH);
break;
default:
throw new IllegalArgumentException("orientation must be one of: VERTICAL, HORIZONTAL");
}
propertyChangeSupport.firePropertyChange("orientation", oldVal, this.orientation);
}
/**
* True if the scrollbar knob is being dragged.
*
* @return the value of the model's valueIsAdjusting property
* @see #setValueIsAdjusting
*/
public final boolean getValueIsAdjusting() {
return model.getValueIsAdjusting();
}
/**
* Sets the model's valueIsAdjusting property. Scrollbar look and
* feel implementations should set this property to true when
* a knob drag begins, and to false when the drag ends. The
* scrollbar model will not generate ChangeEvents while
* valueIsAdjusting is true. True if the scrollbar thumb is being dragged.
*
* @see #getValueIsAdjusting
* @see BoundedRangeModel#setValueIsAdjusting
*/
public void setValueIsAdjusting(boolean b) {
boolean oldVal = model.getValueIsAdjusting();
model.setValueIsAdjusting(b);
propertyChangeSupport.firePropertyChange("valueIsAdjusting", oldVal, model.getValueIsAdjusting());
}
/**
* Sets the four BoundedRangeModel properties after forcing
* the arguments to obey the usual constraints:
*
* minimum <= value <= value+extent <= maximum
*
*
*
* @see BoundedRangeModel#setRangeProperties
* @see #setValue
* @see #setVisibleAmount
* @see #setMinimum
* @see #setMaximum
*/
public void setValues(int newValue, int newExtent, int newMin, int newMax) {
int[] oldVal = {newValue, newExtent, newMin, newMax};
BoundedRangeModel m = model;
m.setRangeProperties(newValue, newExtent, newMin, newMax, m.getValueIsAdjusting());
int[] newVal = {m.getValue(), m.getExtent(), m.getMinimum(), m.getMaximum()};
propertyChangeSupport.firePropertyChange("values", oldVal, newVal);
}
@Override
public void processLowLevelEvent(String action, String... values) {
processKeyEvents(values);
if (action.endsWith("_keystroke"))
return;
model.setDelayEvents(true);
for (String value : values) {
try {
setValue(Integer.parseInt(value));
} catch (NumberFormatException ex) {
// ignore
}
}
SForm.addArmedComponent(this);
model.setDelayEvents(false);
}
@Override
public void fireIntermediateEvents() {
changeFromEvent = true;
model.fireDelayedIntermediateEvents();
changeFromEvent = false;
}
@Override
public void fireFinalEvents() {
super.fireFinalEvents();
changeFromEvent = true;
model.fireDelayedFinalEvents();
changeFromEvent = false;
}
public boolean isChangeFromEvent() {
return changeFromEvent;
}
/**
* Adds an AdjustmentListener. Adjustment listeners are notified
* each time the scrollbar's model changes. Adjustment events are
* provided for backwards compatability with java.awt.Scrollbar.
*
* Note that the AdjustmentEvents type property will always have a
* placeholder value of AdjustmentEvent.TRACK because all changes
* to a BoundedRangeModels value are considered equivalent. To change
* the value of a BoundedRangeModel one just sets its value property,
* i.e. model.setValue(123). No information about the origin of the
* change, e.g. it's a block decrement, is provided. We don't try
* fabricate the origin of the change here.
*
* @param l the AdjustmentLister to add
* @see #removeAdjustmentListener
* @see BoundedRangeModel#addChangeListener
*/
@Override
public void addAdjustmentListener(AdjustmentListener l) {
addEventListener(AdjustmentListener.class, l);
}
/**
* Removes an AdjustmentEvent listener.
*
* @param l the AdjustmentLister to remove
* @see #addAdjustmentListener
*/
@Override
public void removeAdjustmentListener(AdjustmentListener l) {
removeEventListener(AdjustmentListener.class, l);
}
/*
* Notify listeners that the scrollbar's model has changed.
*
* @see #addAdjustmentListener
* @see EventListenerList
*/
protected void fireAdjustmentValueChanged(int id, int type, int value) {
AdjustmentEvent e = null;
Object[] listeners = getListenerList();
for (int i = listeners.length - 2; i >= 0; i -= 2) {
if (listeners[i] == AdjustmentListener.class) {
if (e == null) {
e = new AdjustmentEvent(this, id, type, value);
}
((AdjustmentListener) listeners[i + 1]).adjustmentValueChanged(e);
}
}
}
/**
* This class listens to ChangeEvents on the model and forwards
* AdjustmentEvents for the sake of backwards compatibility.
* Unfortunately there's no way to determine the proper
* type of the AdjustmentEvent as all updates to the model's
* value are considered equivalent.
*/
private class ModelListener implements ChangeListener, Serializable {
@Override
public void stateChanged(ChangeEvent e) {
int id = AdjustmentEvent.ADJUSTMENT_VALUE_CHANGED;
int type = AdjustmentEvent.TRACK;
fireAdjustmentValueChanged(id, type, getValue());
adjust();
}
}
protected abstract void adjust();
/** @see LowLevelEventListener#isEpochCheckEnabled() */
private boolean epochCheckEnabled = true;
/** @see LowLevelEventListener#isEpochCheckEnabled() */
@Override
public boolean isEpochCheckEnabled() {
return epochCheckEnabled;
}
/** @see LowLevelEventListener#isEpochCheckEnabled() */
public void setEpochCheckEnabled(boolean epochCheckEnabled) {
boolean oldVal = this.epochCheckEnabled;
this.epochCheckEnabled = epochCheckEnabled;
propertyChangeSupport.firePropertyChange("epochCheckEnabled", oldVal, this.epochCheckEnabled);
}
}