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.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 		// forces update from bindings
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 		// value aspect
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                         // if there're more than one object selected, set
201                         // the value to blank for all of them.
202                         Object previousValue;
203 
204                         Iterator indexIterator = displayGroup.selectionIndexes().
205                                                   iterator();
206 
207                         // get value for the first selected object.
208                         int initialIndex = ( (Integer)indexIterator.next() ).intValue();
209                         previousValue = displayGroup.valueForObjectAtIndex(
210                                                   initialIndex, key );
211                         value = null;
212 
213                         // go through the rest of the selected objects, compare each
214                         // value with the previous one. continue comparing if two
215                         // values are equal, break the while loop if they're different.
216                         // the final value will be the common value of all selected objects
217                         // if there is one, or be blank if there is not.
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                                 // currentValue is the same as the previous one
231                                 value = currentValue;
232                             }
233 
234                         } // end while
235 
236                     } else {
237 
238                         value = displayGroup.selectedObjectValueForKey( key );
239                     } // end checking size of displayGroup
240 
241 			// convert value to int
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 		// enabled aspect
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 				// treat bound key without display group as a value
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 		// visible aspect
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 				// treat bound key without display group as a value
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     // interface ChangeListener
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  * $Log$
382  * Revision 1.2  2006/02/18 23:19:05  cgruber
383  * Update imports and maven dependencies.
384  *
385  * Revision 1.1  2006/02/16 13:22:22  cgruber
386  * Check in all sources in eclipse-friendly maven-enabled packages.
387  *
388  * Revision 1.8  2004/01/28 18:34:57  mpowers
389  * Better handling for enabling.
390  * Now respecting enabledToSetSelectedObjectValueForKey from display group.
391  *
392  * Revision 1.7  2003/08/06 23:07:52  chochos
393  * general code cleanup (mostly, removing unused imports)
394  *
395  * Revision 1.6  2002/10/11 20:12:58  mpowers
396  * Updated aspect signature.
397  *
398  * Revision 1.5  2002/10/11 20:08:14  mpowers
399  * Added visible aspect to slider association.
400  *
401  * Revision 1.4  2001/11/01 15:54:37  mpowers
402  * Minor update to aspect signature.
403  *
404  * Revision 1.3  2001/07/30 16:32:55  mpowers
405  * Implemented support for bulk-editing.  Detail associations will now
406  * apply changes to all selected objects.
407  *
408  * Revision 1.2  2001/02/16 15:03:34  mpowers
409  * Fixed: slider sets value in display group after selection changed.
410  *
411  * Revision 1.1.1.1  2000/12/21 15:48:55  mpowers
412  * Contributing wotonomy.
413  *
414  * Revision 1.2  2000/12/20 16:25:41  michael
415  * Added log to all files.
416  *
417  *
418  */
419