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.awt.event.ActionEvent;
23 import java.awt.event.ActionListener;
24 import java.util.Enumeration;
25
26 import net.wotonomy.foundation.NSArray;
27 import net.wotonomy.foundation.NSSelector;
28 import net.wotonomy.foundation.internal.ValueConverter;
29 import net.wotonomy.foundation.internal.WotonomyException;
30 import net.wotonomy.ui.EOAssociation;
31 import net.wotonomy.ui.EODisplayGroup;
32
33 /***
34 * ActionAssociation binds any ActionEvent broadcaster
35 * (typically Buttons and the like) to a display group.
36 * Actions are invoked on all selected objects in the
37 * display group bound to the action aspect.
38 * Bindings are:
39 * <ul>
40 * <li>action: a method to be invoked on selected objects.
41 * If the argument aspect is bound, the method must take
42 * one argument. Otherwise, the method must take no arguments.</li>
43 * <li>argument: the attribute of the selected object(s) (possibly
44 * from a different display group) that will be used as an argument
45 * to the action method</li>
46 * <li>enabled: a boolean property that determines whether
47 * the controlled component is enabled</li>
48 * <li>visible: a boolean property that determines whether
49 * the controlled component is visible</li>
50 * </ul>
51 *
52 * @author michael@mpowers.net
53 * @author $Author: cgruber $
54 * @version $Revision: 904 $
55 */
56 public class ActionAssociation extends EOAssociation
57 implements ActionListener
58 {
59 static final NSArray aspects =
60 new NSArray( new Object[] {
61 ActionAspect, ArgumentAspect, EnabledAspect, VisibleAspect
62 } );
63 static final NSArray aspectSignatures =
64 new NSArray( new Object[] {
65 AttributeToOneAspectSignature,
66 AttributeToOneAspectSignature,
67 AttributeToOneAspectSignature,
68 AttributeToOneAspectSignature
69 } );
70 static final NSArray objectKeysTaken =
71 new NSArray( new Object[] {
72 "target"
73 } );
74
75 static NSSelector addActionListener =
76 new NSSelector( "addActionListener",
77 new Class[] { ActionListener.class } );
78 static NSSelector removeActionListener =
79 new NSSelector( "removeActionListener",
80 new Class[] { ActionListener.class } );
81
82 /***
83 * Constructor specifying the object to be controlled by this
84 * association. Does not establish connection.
85 */
86 public ActionAssociation ( Object anObject )
87 {
88 super( anObject );
89 }
90
91 /***
92 * Returns a List of aspect signatures whose contents
93 * correspond with the aspects list. Each element is
94 * a string whose characters represent a capability of
95 * the corresponding aspect. <ul>
96 * <li>"A" attribute: the aspect can be bound to
97 * an attribute.</li>
98 * <li>"1" to-one: the aspect can be bound to a
99 * property that returns a single object.</li>
100 * <li>"M" to-one: the aspect can be bound to a
101 * property that returns multiple objects.</li>
102 * </ul>
103 * An empty signature "" means that the aspect can
104 * bind without needing a key.
105 * This implementation returns "A1M" for each
106 * element in the aspects array.
107 */
108 public static NSArray aspectSignatures ()
109 {
110 return aspectSignatures;
111 }
112
113 /***
114 * Returns a List that describes the aspects supported
115 * by this class. Each element in the list is the string
116 * name of the aspect. This implementation returns an
117 * empty list.
118 */
119 public static NSArray aspects ()
120 {
121 return aspects;
122 }
123
124 /***
125 * Returns a List of EOAssociation subclasses that,
126 * for the objects that are usable for this association,
127 * are less suitable than this association.
128 */
129 public static NSArray associationClassesSuperseded ()
130 {
131 return new NSArray();
132 }
133
134 /***
135 * Returns whether this class can control the specified
136 * object.
137 */
138 public static boolean isUsableWithObject ( Object anObject )
139 {
140 return
141 ( addActionListener.implementedByObject( anObject ) )
142 && ( removeActionListener.implementedByObject( anObject ) );
143 }
144
145 /***
146 * Returns a List of properties of the controlled object
147 * that are controlled by this class. For example,
148 * "stringValue", or "selected".
149 */
150 public static NSArray objectKeysTaken ()
151 {
152 return objectKeysTaken;
153 }
154
155 /***
156 * Returns the aspect that is considered primary
157 * or default.
158 */
159 public static String primaryAspect ()
160 {
161 return ActionAspect;
162 }
163
164 /***
165 * Returns whether this association can bind to the
166 * specified display group on the specified key for
167 * the specified aspect.
168 */
169 public boolean canBindAspect (
170 String anAspect, EODisplayGroup aDisplayGroup, String aKey)
171 {
172 return ( aspects.containsObject( anAspect ) );
173 }
174
175 /***
176 * Establishes a connection between this association
177 * and the controlled object. Subclasses should begin
178 * listening for events from their controlled object here.
179 */
180 public void establishConnection ()
181 {
182 try
183 {
184 addActionListener.invoke( object(), this );
185 }
186 catch ( Exception exc )
187 {
188 throw new WotonomyException( "EOActionAssociation: " +
189 "could not add action listener to object:" + object() );
190 }
191 super.establishConnection();
192 }
193
194 /***
195 * Breaks the connection between this association and
196 * its object. Override to stop listening for events
197 * from the object.
198 */
199 public void breakConnection ()
200 {
201 try
202 {
203 removeActionListener.invoke( object(), this );
204 }
205 catch ( Exception exc )
206 {
207 throw new WotonomyException( "EOActionAssociation: " +
208 "could not add action listener to object:" + object() );
209 }
210 super.breakConnection();
211 }
212
213 /***
214 * Called when either the selection or the contents
215 * of an associated display group have changed.
216 * This implementation does nothing.
217 */
218 public void subjectChanged ()
219 {
220 Object component = object();
221 EODisplayGroup displayGroup;
222 String key;
223
224 if ( component instanceof Component )
225 {
226
227 displayGroup = displayGroupForAspect( EnabledAspect );
228 if ( displayGroup != null )
229 {
230 key = displayGroupKeyForAspect( EnabledAspect );
231 ((Component)component).setEnabled(
232 displayGroup.enabledToSetSelectedObjectValueForKey( key ) );
233 Object value =
234 displayGroup.selectedObjectValueForKey( key );
235 Boolean converted = null;
236 if ( value != null )
237 {
238 converted = (Boolean)
239 ValueConverter.convertObjectToClass(
240 value, Boolean.class );
241 }
242 if ( converted == null ) converted = Boolean.FALSE;
243 if ( converted.booleanValue() !=
244 ((Component)component).isEnabled() )
245 {
246 ((Component)component).setEnabled(
247 converted.booleanValue() );
248 }
249 }
250
251
252 displayGroup = displayGroupForAspect( VisibleAspect );
253 if ( displayGroup != null )
254 {
255 key = displayGroupKeyForAspect( VisibleAspect );
256 Object value =
257 displayGroup.selectedObjectValueForKey( key );
258 Boolean converted = (Boolean)
259 ValueConverter.convertObjectToClass(
260 value, Boolean.class );
261 if ( converted != null )
262 {
263 if ( converted.booleanValue() !=
264 ((Component)component).isVisible() )
265 {
266 ((Component)component).setVisible(
267 converted.booleanValue() );
268 }
269 }
270 }
271 }
272 }
273
274
275
276 public void actionPerformed( ActionEvent evt )
277 {
278 EODisplayGroup actionDisplayGroup = null;
279 String actionKey = null;
280
281
282 actionDisplayGroup = displayGroupForAspect( ActionAspect );
283 if ( actionDisplayGroup != null )
284 {
285 actionKey = displayGroupKeyForAspect( ActionAspect );
286
287
288
289 try
290 {
291
292 NSSelector selector = new NSSelector( actionKey );
293 Enumeration e =
294 actionDisplayGroup.selectedObjects().objectEnumerator();
295 while ( e.hasMoreElements() )
296 {
297 selector.invoke( e.nextElement() );
298 }
299 }
300 catch ( Exception exc )
301 {
302 throw new WotonomyException(
303 "ActionAssociation: error invoking action: " + actionKey, exc );
304 }
305 }
306 }
307 }
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335