1. /*
  2. * @(#)DefaultBoundedRangeModel.java 1.46 04/05/05
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.swing;
  8. import javax.swing.event.*;
  9. import java.io.Serializable;
  10. import java.util.EventListener;
  11. /**
  12. * A generic implementation of BoundedRangeModel.
  13. * <p>
  14. * <strong>Warning:</strong>
  15. * Serialized objects of this class will not be compatible with
  16. * future Swing releases. The current serialization support is
  17. * appropriate for short term storage or RMI between applications running
  18. * the same version of Swing. As of 1.4, support for long term storage
  19. * of all JavaBeans<sup><font size="-2">TM</font></sup>
  20. * has been added to the <code>java.beans</code> package.
  21. * Please see {@link java.beans.XMLEncoder}.
  22. *
  23. * @version 1.46 05/05/04
  24. * @author David Kloba
  25. * @author Hans Muller
  26. * @see BoundedRangeModel
  27. */
  28. public class DefaultBoundedRangeModel implements BoundedRangeModel, Serializable
  29. {
  30. /**
  31. * Only one <code>ChangeEvent</code> is needed per model instance since the
  32. * event's only (read-only) state is the source property. The source
  33. * of events generated here is always "this".
  34. */
  35. protected transient ChangeEvent changeEvent = null;
  36. /** The listeners waiting for model changes. */
  37. protected EventListenerList listenerList = new EventListenerList();
  38. private int value = 0;
  39. private int extent = 0;
  40. private int min = 0;
  41. private int max = 100;
  42. private boolean isAdjusting = false;
  43. /**
  44. * Initializes all of the properties with default values.
  45. * Those values are:
  46. * <ul>
  47. * <li><code>value</code> = 0
  48. * <li><code>extent</code> = 0
  49. * <li><code>minimum</code> = 0
  50. * <li><code>maximum</code> = 100
  51. * <li><code>adjusting</code> = false
  52. * </ul>
  53. */
  54. public DefaultBoundedRangeModel() {
  55. }
  56. /**
  57. * Initializes value, extent, minimum and maximum. Adjusting is false.
  58. * Throws an <code>IllegalArgumentException</code> if the following
  59. * constraints aren't satisfied:
  60. * <pre>
  61. * min <= value <= value+extent <= max
  62. * </pre>
  63. */
  64. public DefaultBoundedRangeModel(int value, int extent, int min, int max)
  65. {
  66. if ((max >= min) &&
  67. (value >= min) &&
  68. ((value + extent) >= value) &&
  69. ((value + extent) <= max)) {
  70. this.value = value;
  71. this.extent = extent;
  72. this.min = min;
  73. this.max = max;
  74. }
  75. else {
  76. throw new IllegalArgumentException("invalid range properties");
  77. }
  78. }
  79. /**
  80. * Returns the model's current value.
  81. * @return the model's current value
  82. * @see #setValue
  83. * @see BoundedRangeModel#getValue
  84. */
  85. public int getValue() {
  86. return value;
  87. }
  88. /**
  89. * Returns the model's extent.
  90. * @return the model's extent
  91. * @see #setExtent
  92. * @see BoundedRangeModel#getExtent
  93. */
  94. public int getExtent() {
  95. return extent;
  96. }
  97. /**
  98. * Returns the model's minimum.
  99. * @return the model's minimum
  100. * @see #setMinimum
  101. * @see BoundedRangeModel#getMinimum
  102. */
  103. public int getMinimum() {
  104. return min;
  105. }
  106. /**
  107. * Returns the model's maximum.
  108. * @return the model's maximum
  109. * @see #setMaximum
  110. * @see BoundedRangeModel#getMaximum
  111. */
  112. public int getMaximum() {
  113. return max;
  114. }
  115. /**
  116. * Sets the current value of the model. For a slider, that
  117. * determines where the knob appears. Ensures that the new
  118. * value, <I>n</I> falls within the model's constraints:
  119. * <pre>
  120. * minimum <= value <= value+extent <= maximum
  121. * </pre>
  122. *
  123. * @see BoundedRangeModel#setValue
  124. */
  125. public void setValue(int n) {
  126. n = Math.min(n, Integer.MAX_VALUE - extent);
  127. int newValue = Math.max(n, min);
  128. if (newValue + extent > max) {
  129. newValue = max - extent;
  130. }
  131. setRangeProperties(newValue, extent, min, max, isAdjusting);
  132. }
  133. /**
  134. * Sets the extent to <I>n</I> after ensuring that <I>n</I>
  135. * is greater than or equal to zero and falls within the model's
  136. * constraints:
  137. * <pre>
  138. * minimum <= value <= value+extent <= maximum
  139. * </pre>
  140. * @see BoundedRangeModel#setExtent
  141. */
  142. public void setExtent(int n) {
  143. int newExtent = Math.max(0, n);
  144. if(value + newExtent > max) {
  145. newExtent = max - value;
  146. }
  147. setRangeProperties(value, newExtent, min, max, isAdjusting);
  148. }
  149. /**
  150. * Sets the minimum to <I>n</I> after ensuring that <I>n</I>
  151. * that the other three properties obey the model's constraints:
  152. * <pre>
  153. * minimum <= value <= value+extent <= maximum
  154. * </pre>
  155. * @see #getMinimum
  156. * @see BoundedRangeModel#setMinimum
  157. */
  158. public void setMinimum(int n) {
  159. int newMax = Math.max(n, max);
  160. int newValue = Math.max(n, value);
  161. int newExtent = Math.min(newMax - newValue, extent);
  162. setRangeProperties(newValue, newExtent, n, newMax, isAdjusting);
  163. }
  164. /**
  165. * Sets the maximum to <I>n</I> after ensuring that <I>n</I>
  166. * that the other three properties obey the model's constraints:
  167. * <pre>
  168. * minimum <= value <= value+extent <= maximum
  169. * </pre>
  170. * @see BoundedRangeModel#setMaximum
  171. */
  172. public void setMaximum(int n) {
  173. int newMin = Math.min(n, min);
  174. int newExtent = Math.min(n - newMin, extent);
  175. int newValue = Math.min(n - newExtent, value);
  176. setRangeProperties(newValue, newExtent, newMin, n, isAdjusting);
  177. }
  178. /**
  179. * Sets the <code>valueIsAdjusting</code> property.
  180. *
  181. * @see #getValueIsAdjusting
  182. * @see #setValue
  183. * @see BoundedRangeModel#setValueIsAdjusting
  184. */
  185. public void setValueIsAdjusting(boolean b) {
  186. setRangeProperties(value, extent, min, max, b);
  187. }
  188. /**
  189. * Returns true if the value is in the process of changing
  190. * as a result of actions being taken by the user.
  191. *
  192. * @return the value of the <code>valueIsAdjusting</code> property
  193. * @see #setValue
  194. * @see BoundedRangeModel#getValueIsAdjusting
  195. */
  196. public boolean getValueIsAdjusting() {
  197. return isAdjusting;
  198. }
  199. /**
  200. * Sets all of the <code>BoundedRangeModel</code> properties after forcing
  201. * the arguments to obey the usual constraints:
  202. * <pre>
  203. * minimum <= value <= value+extent <= maximum
  204. * </pre>
  205. * <p>
  206. * At most, one <code>ChangeEvent</code> is generated.
  207. *
  208. * @see BoundedRangeModel#setRangeProperties
  209. * @see #setValue
  210. * @see #setExtent
  211. * @see #setMinimum
  212. * @see #setMaximum
  213. * @see #setValueIsAdjusting
  214. */
  215. public void setRangeProperties(int newValue, int newExtent, int newMin, int newMax, boolean adjusting)
  216. {
  217. if (newMin > newMax) {
  218. newMin = newMax;
  219. }
  220. if (newValue > newMax) {
  221. newMax = newValue;
  222. }
  223. if (newValue < newMin) {
  224. newMin = newValue;
  225. }
  226. /* Convert the addends to long so that extent can be
  227. * Integer.MAX_VALUE without rolling over the sum.
  228. * A JCK test covers this, see bug 4097718.
  229. */
  230. if (((long)newExtent + (long)newValue) > newMax) {
  231. newExtent = newMax - newValue;
  232. }
  233. if (newExtent < 0) {
  234. newExtent = 0;
  235. }
  236. boolean isChange =
  237. (newValue != value) ||
  238. (newExtent != extent) ||
  239. (newMin != min) ||
  240. (newMax != max) ||
  241. (adjusting != isAdjusting);
  242. if (isChange) {
  243. value = newValue;
  244. extent = newExtent;
  245. min = newMin;
  246. max = newMax;
  247. isAdjusting = adjusting;
  248. fireStateChanged();
  249. }
  250. }
  251. /**
  252. * Adds a <code>ChangeListener</code>. The change listeners are run each
  253. * time any one of the Bounded Range model properties changes.
  254. *
  255. * @param l the ChangeListener to add
  256. * @see #removeChangeListener
  257. * @see BoundedRangeModel#addChangeListener
  258. */
  259. public void addChangeListener(ChangeListener l) {
  260. listenerList.add(ChangeListener.class, l);
  261. }
  262. /**
  263. * Removes a <code>ChangeListener</code>.
  264. *
  265. * @param l the <code>ChangeListener</code> to remove
  266. * @see #addChangeListener
  267. * @see BoundedRangeModel#removeChangeListener
  268. */
  269. public void removeChangeListener(ChangeListener l) {
  270. listenerList.remove(ChangeListener.class, l);
  271. }
  272. /**
  273. * Returns an array of all the change listeners
  274. * registered on this <code>DefaultBoundedRangeModel</code>.
  275. *
  276. * @return all of this model's <code>ChangeListener</code>s
  277. * or an empty
  278. * array if no change listeners are currently registered
  279. *
  280. * @see #addChangeListener
  281. * @see #removeChangeListener
  282. *
  283. * @since 1.4
  284. */
  285. public ChangeListener[] getChangeListeners() {
  286. return (ChangeListener[])listenerList.getListeners(
  287. ChangeListener.class);
  288. }
  289. /**
  290. * Runs each <code>ChangeListener</code>'s <code>stateChanged</code> method.
  291. *
  292. * @see #setRangeProperties
  293. * @see EventListenerList
  294. */
  295. protected void fireStateChanged()
  296. {
  297. Object[] listeners = listenerList.getListenerList();
  298. for (int i = listeners.length - 2; i >= 0; i -=2 ) {
  299. if (listeners[i] == ChangeListener.class) {
  300. if (changeEvent == null) {
  301. changeEvent = new ChangeEvent(this);
  302. }
  303. ((ChangeListener)listeners[i+1]).stateChanged(changeEvent);
  304. }
  305. }
  306. }
  307. /**
  308. * Returns a string that displays all of the
  309. * <code>BoundedRangeModel</code> properties.
  310. */
  311. public String toString() {
  312. String modelString =
  313. "value=" + getValue() + ", " +
  314. "extent=" + getExtent() + ", " +
  315. "min=" + getMinimum() + ", " +
  316. "max=" + getMaximum() + ", " +
  317. "adj=" + getValueIsAdjusting();
  318. return getClass().getName() + "[" + modelString + "]";
  319. }
  320. /**
  321. * Returns an array of all the objects currently registered as
  322. * <code><em>Foo</em>Listener</code>s
  323. * upon this model.
  324. * <code><em>Foo</em>Listener</code>s
  325. * are registered using the <code>add<em>Foo</em>Listener</code> method.
  326. * <p>
  327. * You can specify the <code>listenerType</code> argument
  328. * with a class literal, such as <code><em>Foo</em>Listener.class</code>.
  329. * For example, you can query a <code>DefaultBoundedRangeModel</code>
  330. * instance <code>m</code>
  331. * for its change listeners
  332. * with the following code:
  333. *
  334. * <pre>ChangeListener[] cls = (ChangeListener[])(m.getListeners(ChangeListener.class));</pre>
  335. *
  336. * If no such listeners exist,
  337. * this method returns an empty array.
  338. *
  339. * @param listenerType the type of listeners requested;
  340. * this parameter should specify an interface
  341. * that descends from <code>java.util.EventListener</code>
  342. * @return an array of all objects registered as
  343. * <code><em>Foo</em>Listener</code>s
  344. * on this model,
  345. * or an empty array if no such
  346. * listeners have been added
  347. * @exception ClassCastException if <code>listenerType</code> doesn't
  348. * specify a class or interface that implements
  349. * <code>java.util.EventListener</code>
  350. *
  351. * @see #getChangeListeners
  352. *
  353. * @since 1.3
  354. */
  355. public <T extends EventListener> T[] getListeners(Class<T> listenerType) {
  356. return listenerList.getListeners(listenerType);
  357. }
  358. }