1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package net.wotonomy.ui.swing;
20
21 import java.util.Iterator;
22
23 import javax.swing.JSlider;
24 import javax.swing.event.ChangeEvent;
25 import javax.swing.event.ChangeListener;
26
27 import net.wotonomy.foundation.NSArray;
28 import net.wotonomy.foundation.internal.ValueConverter;
29 import net.wotonomy.ui.EOAssociation;
30 import net.wotonomy.ui.EODisplayGroup;
31
32 /***
33 * SliderAssociation binds a JSlider component to
34 * a display group. Bindings are:
35 * <ul>
36 * <li>value: a property convertable to/from a string</li>
37 * <li>enabled: a boolean property that determines whether
38 * the user can select the text in the field</li>
39 * </ul>
40 *
41 * @author michael@mpowers.net
42 * @author $Author: cgruber $
43 * @version $Revision: 904 $
44 */
45 public class SliderAssociation extends EOAssociation
46 implements ChangeListener
47 {
48 static final NSArray aspects =
49 new NSArray( new Object[] {
50 ValueAspect, EnabledAspect, VisibleAspect
51 } );
52 static final NSArray aspectSignatures =
53 new NSArray( new Object[] {
54 AttributeToOneAspectSignature,
55 AttributeToOneAspectSignature,
56 } );
57 static final NSArray objectKeysTaken =
58 new NSArray( new Object[] {
59 "value"
60 } );
61
62 /***
63 * Constructor specifying the object to be controlled by this
64 * association. Does not establish connection.
65 */
66 public SliderAssociation ( Object anObject )
67 {
68 super( anObject );
69 }
70
71 /***
72 * Returns a List of aspect signatures whose contents
73 * correspond with the aspects list. Each element is
74 * a string whose characters represent a capability of
75 * the corresponding aspect. <ul>
76 * <li>"A" attribute: the aspect can be bound to
77 * an attribute.</li>
78 * <li>"1" to-one: the aspect can be bound to a
79 * property that returns a single object.</li>
80 * <li>"M" to-one: the aspect can be bound to a
81 * property that returns multiple objects.</li>
82 * </ul>
83 * An empty signature "" means that the aspect can
84 * bind without needing a key.
85 * This implementation returns "A1M" for each
86 * element in the aspects array.
87 */
88 public static NSArray aspectSignatures ()
89 {
90 return aspectSignatures;
91 }
92
93 /***
94 * Returns a List that describes the aspects supported
95 * by this class. Each element in the list is the string
96 * name of the aspect. This implementation returns an
97 * empty list.
98 */
99 public static NSArray aspects ()
100 {
101 return aspects;
102 }
103
104 /***
105 * Returns a List of EOAssociation subclasses that,
106 * for the objects that are usable for this association,
107 * are less suitable than this association.
108 */
109 public static NSArray associationClassesSuperseded ()
110 {
111 return new NSArray();
112 }
113
114 /***
115 * Returns whether this class can control the specified
116 * object.
117 */
118 public static boolean isUsableWithObject ( Object anObject )
119 {
120 return ( anObject instanceof JSlider );
121 }
122
123 /***
124 * Returns a List of properties of the controlled object
125 * that are controlled by this class. For example,
126 * "stringValue", or "selected".
127 */
128 public static NSArray objectKeysTaken ()
129 {
130 return objectKeysTaken;
131 }
132
133 /***
134 * Returns the aspect that is considered primary
135 * or default. This is typically "value" or somesuch.
136 */
137 public static String primaryAspect ()
138 {
139 return ValueAspect;
140 }
141
142 /***
143 * Returns whether this association can bind to the
144 * specified display group on the specified key for
145 * the specified aspect.
146 */
147 public boolean canBindAspect (
148 String anAspect, EODisplayGroup aDisplayGroup, String aKey)
149 {
150 return ( aspects.containsObject( anAspect ) );
151 }
152
153 /***
154 * Establishes a connection between this association
155 * and the controlled object. This implementation
156 * attempts to add this class as an ActionListener
157 * and as a FocusListener to the specified object.
158 */
159 public void establishConnection ()
160 {
161 component().addChangeListener( this );
162 super.establishConnection();
163
164
165 subjectChanged();
166 }
167
168 /***
169 * Breaks the connection between this association and
170 * its object. Override to stop listening for events
171 * from the object.
172 */
173 public void breakConnection ()
174 {
175 component().removeChangeListener( this );
176 super.breakConnection();
177 }
178
179 /***
180 * Called when either the selection or the contents
181 * of an associated display group have changed.
182 */
183 public void subjectChanged ()
184 {
185 JSlider component = component();
186 EODisplayGroup displayGroup;
187 String key;
188 Object value;
189
190
191 displayGroup = displayGroupForAspect( ValueAspect );
192 if ( displayGroup != null )
193 {
194 key = displayGroupKeyForAspect( ValueAspect );
195 component.setEnabled(
196 displayGroup.enabledToSetSelectedObjectValueForKey( key ) );
197
198 if ( displayGroup.selectedObjects().size() > 1 )
199 {
200
201
202 Object previousValue;
203
204 Iterator indexIterator = displayGroup.selectionIndexes().
205 iterator();
206
207
208 int initialIndex = ( (Integer)indexIterator.next() ).intValue();
209 previousValue = displayGroup.valueForObjectAtIndex(
210 initialIndex, key );
211 value = null;
212
213
214
215
216
217
218 while ( indexIterator.hasNext() )
219 {
220 int index = ( (Integer)indexIterator.next() ).intValue();
221 Object currentValue = displayGroup.valueForObjectAtIndex(
222 index, key );
223 if ( currentValue != null && !currentValue.equals( previousValue ) )
224 {
225 value = null;
226 break;
227 }
228 else
229 {
230
231 value = currentValue;
232 }
233
234 }
235
236 } else {
237
238 value = displayGroup.selectedObjectValueForKey( key );
239 }
240
241
242 value = ValueConverter.convertObjectToClass( value, Integer.class );
243
244 int intValue;
245 if ( value == null )
246 {
247 intValue = 0;
248 }
249 else
250 {
251 intValue = ((Integer)value).intValue();
252 }
253
254 if ( component.getValue() != intValue )
255 {
256 component().removeChangeListener( this );
257 component.setValue( intValue );
258 component().addChangeListener( this );
259 }
260 }
261
262
263 displayGroup = displayGroupForAspect( EnabledAspect );
264 key = displayGroupKeyForAspect( EnabledAspect );
265 if ( ( displayGroup != null ) || ( key != null ) )
266 {
267 if ( displayGroup != null )
268 {
269 value =
270 displayGroup.selectedObjectValueForKey( key );
271 }
272 else
273 {
274
275 value = key;
276 }
277 Boolean converted = null;
278 if ( value != null )
279 {
280 converted = (Boolean)
281 ValueConverter.convertObjectToClass(
282 value, Boolean.class );
283 }
284 if ( converted == null ) converted = Boolean.FALSE;
285 if ( converted.booleanValue() != component.isEnabled() )
286 {
287 component.setEnabled( converted.booleanValue() );
288 }
289 }
290
291
292 displayGroup = displayGroupForAspect( VisibleAspect );
293 key = displayGroupKeyForAspect( VisibleAspect );
294 if ( ( displayGroup != null ) || ( key != null ) )
295 {
296 if ( displayGroup != null )
297 {
298 value =
299 displayGroup.selectedObjectValueForKey( key );
300 }
301 else
302 {
303
304 value = key;
305 }
306 Boolean converted = (Boolean)
307 ValueConverter.convertObjectToClass(
308 value, Boolean.class );
309
310 if ( converted != null )
311 {
312 if ( converted.booleanValue() != component.isVisible() )
313 {
314 component.setVisible( converted.booleanValue() );
315 }
316 }
317 }
318
319 }
320
321 /***
322 * Forces this association to cause the object to
323 * stop editing and validate the user's input.
324 * @return false if there were problems validating,
325 * or true to continue.
326 */
327 public boolean endEditing ()
328 {
329 return writeValueToDisplayGroup();
330 }
331
332 /***
333 * Writes the value currently in the component
334 * to the selected object in the display group
335 * bound to the value aspect.
336 * @return false if there were problems validating,
337 * or true to continue.
338 */
339 protected boolean writeValueToDisplayGroup()
340 {
341 EODisplayGroup displayGroup =
342 displayGroupForAspect( ValueAspect );
343 if ( displayGroup != null )
344 {
345 String key = displayGroupKeyForAspect( ValueAspect );
346 Object value = new Integer( component().getValue() );
347
348 boolean returnValue = true;
349 Iterator selectedIterator = displayGroup.selectionIndexes().iterator();
350 while ( selectedIterator.hasNext() )
351 {
352 int index = ( (Integer)selectedIterator.next() ).intValue();
353
354 if ( !displayGroup.setValueForObjectAtIndex( value, index, key ) )
355 {
356 returnValue = false;
357 }
358 }
359 return returnValue;
360 }
361 return false;
362 }
363
364
365
366 /***
367 * Updates object on change.
368 */
369 public void stateChanged(ChangeEvent e)
370 {
371 writeValueToDisplayGroup();
372 }
373
374 private JSlider component()
375 {
376 return (JSlider) object();
377 }
378 }
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419