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