View Javadoc

1   /*
2   Wotonomy: OpenStep design patterns for pure Java applications.
3   Copyright (C) 2000 Intersect Software Corporation
4   
5   This library is free software; you can redistribute it and/or
6   modify it under the terms of the GNU Lesser General Public
7   License as published by the Free Software Foundation; either
8   version 2.1 of the License, or (at your option) any later version.
9   
10  This library is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  Lesser General Public License for more details.
14  
15  You should have received a copy of the GNU Lesser General Public
16  License along with this library; if not, see http://www.gnu.org
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 			// enabled aspect
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 			// visible aspect
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     // interface ActionListener
275     
276     public void actionPerformed( ActionEvent evt )
277     {
278 		EODisplayGroup actionDisplayGroup = null;
279 		String actionKey = null;
280 		
281 		// action aspect
282 		actionDisplayGroup = displayGroupForAspect( ActionAspect );
283 		if ( actionDisplayGroup != null )
284 		{
285 			actionKey = displayGroupKeyForAspect( ActionAspect );
286 
287 			//TODO: argument aspect not implemented
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  * $Log$
311  * Revision 1.2  2006/02/18 23:19:05  cgruber
312  * Update imports and maven dependencies.
313  *
314  * Revision 1.1  2006/02/16 13:22:22  cgruber
315  * Check in all sources in eclipse-friendly maven-enabled packages.
316  *
317  * Revision 1.4  2004/01/28 18:34:57  mpowers
318  * Better handling for enabling.
319  * Now respecting enabledToSetSelectedObjectValueForKey from display group.
320  *
321  * Revision 1.3  2003/08/06 23:07:52  chochos
322  * general code cleanup (mostly, removing unused imports)
323  *
324  * Revision 1.2  2001/02/17 16:52:05  mpowers
325  * Changes in imports to support building with jdk1.1 collections.
326  *
327  * Revision 1.1.1.1  2000/12/21 15:48:28  mpowers
328  * Contributing wotonomy.
329  *
330  * Revision 1.5  2000/12/20 16:25:40  michael
331  * Added log to all files.
332  *
333  *
334  */
335