1. /*
  2. * @(#)DefaultBoundedRangeModel.java 1.43 03/01/23
  3. *
  4. * Copyright 2003 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.43 01/23/03
  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. int newValue = Math.max(n, min);
  127. if(newValue + extent > max) {
  128. newValue = max - extent;
  129. }
  130. setRangeProperties(newValue, extent, min, max, isAdjusting);
  131. }
  132. /**
  133. * Sets the extent to <I>n</I> after ensuring that <I>n</I>
  134. * is greater than or equal to zero and falls within the model's
  135. * constraints:
  136. * <pre>
  137. * minimum <= value <= value+extent <= maximum
  138. * </pre>
  139. * @see BoundedRangeModel#setExtent
  140. */
  141. public void setExtent(int n) {
  142. int newExtent = Math.max(0, n);
  143. if(value + newExtent > max) {
  144. newExtent = max - value;
  145. }
  146. setRangeProperties(value, newExtent, min, max, isAdjusting);
  147. }
  148. /**
  149. * Sets the minimum to <I>n</I> after ensuring that <I>n</I>
  150. * that the other three properties obey the model's constraints:
  151. * <pre>
  152. * minimum <= value <= value+extent <= maximum
  153. * </pre>
  154. * @see #getMinimum
  155. * @see BoundedRangeModel#setMinimum
  156. */
  157. public void setMinimum(int n) {
  158. int newMax = Math.max(n, max);
  159. int newValue = Math.max(n, value);
  160. int newExtent = Math.min(newMax - newValue, extent);
  161. setRangeProperties(newValue, newExtent, n, newMax, isAdjusting);
  162. }
  163. /**
  164. * Sets the maximum to <I>n</I> after ensuring that <I>n</I>
  165. * that the other three properties obey the model's constraints:
  166. * <pre>
  167. * minimum <= value <= value+extent <= maximum
  168. * </pre>
  169. * @see BoundedRangeModel#setMaximum
  170. */
  171. public void setMaximum(int n) {
  172. int newMin = Math.min(n, min);
  173. int newExtent = Math.min(n - newMin, extent);
  174. int newValue = Math.min(n - newExtent, value);
  175. setRangeProperties(newValue, newExtent, newMin, n, isAdjusting);
  176. }
  177. /**
  178. * Sets the <code>valueIsAdjusting</code> property.
  179. *
  180. * @see #getValueIsAdjusting
  181. * @see #setValue
  182. * @see BoundedRangeModel#setValueIsAdjusting
  183. */
  184. public void setValueIsAdjusting(boolean b) {
  185. setRangeProperties(value, extent, min, max, b);
  186. }
  187. /**
  188. * Returns true if the value is in the process of changing
  189. * as a result of actions being taken by the user.
  190. *
  191. * @return the value of the <code>valueIsAdjusting</code> property
  192. * @see #setValue
  193. * @see BoundedRangeModel#getValueIsAdjusting
  194. */
  195. public boolean getValueIsAdjusting() {
  196. return isAdjusting;
  197. }
  198. /**
  199. * Sets all of the <code>BoundedRangeModel</code> properties after forcing
  200. * the arguments to obey the usual constraints:
  201. * <pre>
  202. * minimum <= value <= value+extent <= maximum
  203. * </pre>
  204. * <p>
  205. * At most, one <code>ChangeEvent</code> is generated.
  206. *
  207. * @see BoundedRangeModel#setRangeProperties
  208. * @see #setValue
  209. * @see #setExtent
  210. * @see #setMinimum
  211. * @see #setMaximum
  212. * @see #setValueIsAdjusting
  213. */
  214. public void setRangeProperties(int newValue, int newExtent, int newMin, int newMax, boolean adjusting)
  215. {
  216. if (newMin > newMax) {
  217. newMin = newMax;
  218. }
  219. if (newValue > newMax) {
  220. newMax = newValue;
  221. }
  222. if (newValue < newMin) {
  223. newMin = newValue;
  224. }
  225. /* Convert the addends to long so that extent can be
  226. * Integer.MAX_VALUE without rolling over the sum.
  227. * A JCK test covers this, see bug 4097718.
  228. */
  229. if (((long)newExtent + (long)newValue) > newMax) {
  230. newExtent = newMax - newValue;
  231. }
  232. if (newExtent < 0) {
  233. newExtent = 0;
  234. }
  235. boolean isChange =
  236. (newValue != value) ||
  237. (newExtent != extent) ||
  238. (newMin != min) ||
  239. (newMax != max) ||
  240. (adjusting != isAdjusting);
  241. if (isChange) {
  242. value = newValue;
  243. extent = newExtent;
  244. min = newMin;
  245. max = newMax;
  246. isAdjusting = adjusting;
  247. fireStateChanged();
  248. }
  249. }
  250. /**
  251. * Adds a <code>ChangeListener</code>. The change listeners are run each
  252. * time any one of the Bounded Range model properties changes.
  253. *
  254. * @param l the ChangeListener to add
  255. * @see #removeChangeListener
  256. * @see BoundedRangeModel#addChangeListener
  257. */
  258. public void addChangeListener(ChangeListener l) {
  259. listenerList.add(ChangeListener.class, l);
  260. }
  261. /**
  262. * Removes a <code>ChangeListener</code>.
  263. *
  264. * @param l the <code>ChangeListener</code> to remove
  265. * @see #addChangeListener
  266. * @see BoundedRangeModel#removeChangeListener
  267. */
  268. public void removeChangeListener(ChangeListener l) {
  269. listenerList.remove(ChangeListener.class, l);
  270. }
  271. /**
  272. * Returns an array of all the change listeners
  273. * registered on this <code>DefaultBoundedRangeModel</code>.
  274. *
  275. * @return all of this model's <code>ChangeListener</code>s
  276. * or an empty
  277. * array if no change listeners are currently registered
  278. *
  279. * @see #addChangeListener
  280. * @see #removeChangeListener
  281. *
  282. * @since 1.4
  283. */
  284. public ChangeListener[] getChangeListeners() {
  285. return (ChangeListener[])listenerList.getListeners(
  286. ChangeListener.class);
  287. }
  288. /**
  289. * Runs each <code>ChangeListener</code>'s <code>stateChanged</code> method.
  290. *
  291. * @see #setRangeProperties
  292. * @see EventListenerList
  293. */
  294. protected void fireStateChanged()
  295. {
  296. Object[] listeners = listenerList.getListenerList();
  297. for (int i = listeners.length - 2; i >= 0; i -=2 ) {
  298. if (listeners[i] == ChangeListener.class) {
  299. if (changeEvent == null) {
  300. changeEvent = new ChangeEvent(this);
  301. }
  302. ((ChangeListener)listeners[i+1]).stateChanged(changeEvent);
  303. }
  304. }
  305. }
  306. /**
  307. * Returns a string that displays all of the
  308. * <code>BoundedRangeModel</code> properties.
  309. */
  310. public String toString() {
  311. String modelString =
  312. "value=" + getValue() + ", " +
  313. "extent=" + getExtent() + ", " +
  314. "min=" + getMinimum() + ", " +
  315. "max=" + getMaximum() + ", " +
  316. "adj=" + getValueIsAdjusting();
  317. return getClass().getName() + "[" + modelString + "]";
  318. }
  319. /**
  320. * Returns an array of all the objects currently registered as
  321. * <code><em>Foo</em>Listener</code>s
  322. * upon this model.
  323. * <code><em>Foo</em>Listener</code>s
  324. * are registered using the <code>add<em>Foo</em>Listener</code> method.
  325. * <p>
  326. * You can specify the <code>listenerType</code> argument
  327. * with a class literal, such as <code><em>Foo</em>Listener.class</code>.
  328. * For example, you can query a <code>DefaultBoundedRangeModel</code>
  329. * instance <code>m</code>
  330. * for its change listeners
  331. * with the following code:
  332. *
  333. * <pre>ChangeListener[] cls = (ChangeListener[])(m.getListeners(ChangeListener.class));</pre>
  334. *
  335. * If no such listeners exist,
  336. * this method returns an empty array.
  337. *
  338. * @param listenerType the type of listeners requested;
  339. * this parameter should specify an interface
  340. * that descends from <code>java.util.EventListener</code>
  341. * @return an array of all objects registered as
  342. * <code><em>Foo</em>Listener</code>s
  343. * on this model,
  344. * or an empty array if no such
  345. * listeners have been added
  346. * @exception ClassCastException if <code>listenerType</code> doesn't
  347. * specify a class or interface that implements
  348. * <code>java.util.EventListener</code>
  349. *
  350. * @see #getChangeListeners
  351. *
  352. * @since 1.3
  353. */
  354. public EventListener[] getListeners(Class listenerType) {
  355. return listenerList.getListeners(listenerType);
  356. }
  357. }