Coverage Report - net.wotonomy.ui.swing.ListAssociation
 
Classes in this File Line Coverage Branch Coverage Complexity
ListAssociation
0% 
0% 
1.429
 
 1  0
 /*
 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.Enumeration;
 22  
 
 23  
 import javax.swing.DefaultListModel;
 24  
 import javax.swing.JList;
 25  
 import javax.swing.SwingUtilities;
 26  
 import javax.swing.event.ListDataEvent;
 27  
 import javax.swing.event.ListDataListener;
 28  
 import javax.swing.event.ListSelectionEvent;
 29  
 import javax.swing.event.ListSelectionListener;
 30  
 
 31  
 import net.wotonomy.foundation.NSArray;
 32  
 import net.wotonomy.foundation.NSMutableArray;
 33  
 import net.wotonomy.ui.EOAssociation;
 34  
 import net.wotonomy.ui.EODisplayGroup;
 35  
 
 36  
 /**
 37  
 * TextAssociation binds a JList to a display group's
 38  
 * list of displayable objects.  Bindings are: 
 39  
 * <ul>
 40  
 * <li>titles: a property convertable to a string for
 41  
 * display in the cells of the list</li> 
 42  
 * </ul>
 43  
 *
 44  
 * @author michael@mpowers.net
 45  
 * @author $Author: cgruber $
 46  
 * @version $Revision: 904 $
 47  
 */
 48  0
 public class ListAssociation extends EOAssociation
 49  
                           implements ListSelectionListener,
 50  
                                      ListDataListener
 51  
 {
 52  0
     static final NSArray aspects = 
 53  0
         new NSArray( new Object[] {
 54  0
             TitlesAspect
 55  
         } );
 56  0
     static final NSArray aspectSignatures = 
 57  0
         new NSArray( new Object[] {
 58  0
             AttributeToOneAspectSignature
 59  
         } );
 60  0
     static final NSArray objectKeysTaken = 
 61  0
         new NSArray( new Object[] {
 62  0
             "items" 
 63  
         } );
 64  
                 
 65  
     protected DefaultListModel model;
 66  
 
 67  
     /**
 68  
     * Constructor expecting a JList. Throws an
 69  
         * exception if it doesn't receive one.
 70  
         * Note: This sets the JList's model to a DefaultListModel.
 71  
     */
 72  
     public ListAssociation ( Object anObject )
 73  
     {
 74  0
         super( anObject );
 75  0
         model = new DefaultListModel();
 76  0
         ((JList)anObject).setModel( model );
 77  0
     }
 78  
     
 79  
     /**
 80  
     * Returns a List of aspect signatures whose contents
 81  
     * correspond with the aspects list.  Each element is 
 82  
     * a string whose characters represent a capability of
 83  
     * the corresponding aspect. <ul>
 84  
     * <li>"A" attribute: the aspect can be bound to
 85  
     * an attribute.</li>
 86  
     * <li>"1" to-one: the aspect can be bound to a
 87  
     * property that returns a single object.</li>
 88  
     * <li>"M" to-one: the aspect can be bound to a
 89  
     * property that returns multiple objects.</li>
 90  
     * </ul> 
 91  
     * An empty signature "" means that the aspect can
 92  
     * bind without needing a key.
 93  
     * This implementation returns "A1M" for each
 94  
     * element in the aspects array.
 95  
     */
 96  
     public static NSArray aspectSignatures ()
 97  
     {
 98  0
         return aspectSignatures;
 99  
     }
 100  
     
 101  
     /**
 102  
     * Returns a List that describes the aspects supported
 103  
     * by this class.  Each element in the list is the string
 104  
     * name of the aspect.  This implementation returns an
 105  
     * empty list.
 106  
     */
 107  
     public static NSArray aspects ()
 108  
     {
 109  0
         return aspects;
 110  
     }
 111  
     
 112  
     /**
 113  
     * Returns a List of EOAssociation subclasses that,
 114  
     * for the objects that are usable for this association,
 115  
     * are less suitable than this association.
 116  
     */
 117  
     public static NSArray associationClassesSuperseded ()
 118  
     {
 119  0
         return new NSArray();
 120  
     }
 121  
     
 122  
     /**
 123  
     * Returns whether this class can control the specified 
 124  
     * object. 
 125  
     */
 126  
     public static boolean isUsableWithObject ( Object anObject )
 127  
     {
 128  0
         return ( anObject instanceof JList );
 129  
     }
 130  
     
 131  
     /**
 132  
     * Returns a List of properties of the controlled object
 133  
     * that are controlled by this class.  For example,
 134  
     * "stringValue", or "selected".
 135  
     */
 136  
     public static NSArray objectKeysTaken ()
 137  
     {
 138  0
         return objectKeysTaken;
 139  
     }
 140  
     
 141  
     /**
 142  
     * Returns the aspect that is considered primary
 143  
     * or default.  This is typically "value" or somesuch.
 144  
     */
 145  
     public static String primaryAspect ()
 146  
     {
 147  0
         return TitlesAspect;
 148  
     }
 149  
         
 150  
     /**
 151  
     * Returns whether this association can bind to the
 152  
     * specified display group on the specified key for
 153  
     * the specified aspect.  
 154  
     */
 155  
     public boolean canBindAspect ( 
 156  
         String anAspect, EODisplayGroup aDisplayGroup, String aKey)
 157  
     {
 158  0
         return ( aspects.containsObject( anAspect ) );
 159  
     }
 160  
     
 161  
     /**
 162  
     * Establishes a connection between this association
 163  
     * and the controlled object.  Subclasses should begin
 164  
     * listening for events from their controlled object here.
 165  
     */
 166  
     public void establishConnection ()
 167  
     {
 168  0
         addAsListener();
 169  0
         super.establishConnection();
 170  0
                 populateFromDisplayGroup(); 
 171  0
         selectFromDisplayGroup();
 172  0
     }
 173  
     
 174  
     /**
 175  
     * Breaks the connection between this association and 
 176  
     * its object.  Override to stop listening for events
 177  
     * from the object.
 178  
     */
 179  
     public void breakConnection ()
 180  
     {
 181  0
         removeAsListener();
 182  0
         super.breakConnection();
 183  0
     }
 184  
 
 185  
     protected void addAsListener()
 186  
     { // System.out.println( "ListAssociation.addAsListener: " + ++count );
 187  0
         component().getModel().addListDataListener( this );
 188  0
         component().addListSelectionListener( this );
 189  0
     }
 190  
 
 191  
     protected void removeAsListener()
 192  
     { // System.out.println( "ListAssociation.removeAsListener: " + --count );
 193  0
         component().getModel().removeListDataListener( this );
 194  0
         component().removeListSelectionListener( this );
 195  0
     }
 196  
 
 197  
     /**
 198  
     * Called when either the selection or the contents 
 199  
     * of an associated display group have changed.
 200  
     */
 201  
     public void subjectChanged ()
 202  
     {
 203  
                 EODisplayGroup displayGroup;
 204  
         
 205  
                 // titles aspect
 206  0
                 displayGroup = displayGroupForAspect( TitlesAspect );
 207  0
                 if ( displayGroup != null )
 208  
                 {
 209  
 //System.out.println( "subjectChanged: " + 
 210  
 //displayGroup.contentsChanged() + " : " + displayGroup.selectionChanged() 
 211  
 //+ " : " + displayGroup.updatedObjectIndex() );
 212  
 //new net.wotonomy.ui.swing.util.StackTraceInspector();
 213  0
                         if ( displayGroup.contentsChanged() )
 214  
                         {
 215  0
                                 populateFromDisplayGroup();
 216  
                         }
 217  
 
 218  0
                         if ( displayGroup.selectionChanged() )
 219  
                         {
 220  0
                                 selectFromDisplayGroup();
 221  
                         }
 222  
                 }
 223  
 
 224  0
     }
 225  
         
 226  
         private void populateFromDisplayGroup()
 227  
         {
 228  0
                 JList component = component();
 229  0
                 EODisplayGroup displayGroup = displayGroupForAspect( TitlesAspect );
 230  0
                 String key = displayGroupKeyForAspect( TitlesAspect );
 231  
                         
 232  0
                 removeAsListener();
 233  
                 
 234  
                 // remember selection
 235  0
                 int[] selectedIndices = component().getSelectedIndices();
 236  
                 
 237  
                 // clear the model
 238  0
                 model.removeAllElements();
 239  
 
 240  
                 // populate the model
 241  
                 Object value;
 242  0
                 int size = displayGroup.displayedObjects().count();
 243  
 //System.out.println( "populateFromDisplayGroup: " + size );
 244  0
                 for ( int i = 0; i < size; i++ )
 245  
                 {
 246  0
                         value = displayGroup.valueForObjectAtIndex( i, key );
 247  0
                         if ( value == null ) value = "[null]";
 248  0
                         model.addElement( value );
 249  
                 }
 250  
         
 251  
                 // select the same indexes
 252  0
                 for ( int i = 0; i < selectedIndices.length; i++ )
 253  
                 {
 254  0
                         component.addSelectionInterval(
 255  0
                                 selectedIndices[i], selectedIndices[i] ); // adds one row
 256  
                 }
 257  
 
 258  0
                 addAsListener();
 259  0
         }
 260  
             
 261  
         private void selectFromDisplayGroup()
 262  
         {
 263  0
                 JList component = component();
 264  0
                 EODisplayGroup displayGroup = displayGroupForAspect( TitlesAspect );
 265  
 
 266  0
                 removeAsListener();
 267  
                 
 268  
                 int index;
 269  0
                 component.clearSelection();
 270  0
                 Enumeration e = 
 271  0
                         displayGroup.selectionIndexes().objectEnumerator();
 272  
 
 273  0
                 while ( e.hasMoreElements() )
 274  
                 {   // add selections one-by-one to support non-contiguous
 275  0
                         index = ((Number)e.nextElement()).intValue();
 276  0
                         component.addSelectionInterval(
 277  0
                                 index, index ); // adds one row
 278  0
                 }
 279  
                 
 280  0
                 addAsListener();
 281  0
         }
 282  
         
 283  
         // interface ListSelectionListener
 284  
 
 285  
     public void valueChanged(ListSelectionEvent e)
 286  
     {
 287  0
         final EODisplayGroup displayGroup = 
 288  0
                         displayGroupForAspect( TitlesAspect );
 289  0
         if ( ( displayGroup != null ) && ( ! e.getValueIsAdjusting() ) )
 290  
         {
 291  0
             int[] selectedIndices = component().getSelectedIndices();
 292  0
                         final NSMutableArray indexList = new NSMutableArray();
 293  0
                         for ( int i = 0; i < selectedIndices.length; i++ )
 294  
                         {
 295  0
                                 indexList.addObject( new Integer( selectedIndices[i] ) );
 296  
                         }
 297  
             
 298  
             // invoke later so the component is repainted before
 299  
             //   any potentially lengthy second-order effects happen:
 300  
             //   this improves user-perceived responsiveness of big apps
 301  0
             SwingUtilities.invokeLater( new Runnable() {
 302  0
                 public void run()
 303  
                 {
 304  0
                                 displayGroup.setSelectionIndexes( indexList );
 305  0
                 }
 306  
              });
 307  
         }
 308  0
     }
 309  
 
 310  
     // interface ListDataListener
 311  
 
 312  
     public void intervalAdded(ListDataEvent e)
 313  
     {
 314  
         // System.out.println( "intervalAdded" );
 315  0
         contentsChanged(e);
 316  0
     }
 317  
     public void intervalRemoved(ListDataEvent e)
 318  
     {
 319  
         // System.out.println( "intervalRemoved" );
 320  0
         contentsChanged(e);
 321  0
     }
 322  
     public void contentsChanged(ListDataEvent e)
 323  
     {
 324  
         // System.out.println( "contentsChanged" );
 325  
                 
 326  
         // if we were editing a property,
 327  
                 // we'd notify our display group now.
 328  0
     }
 329  
         
 330  
         // convenience
 331  
 
 332  
     private JList component()
 333  
     {
 334  0
         return (JList) object();
 335  
     }
 336  
 }
 337  
 
 338  
 /*
 339  
  * $Log$
 340  
  * Revision 1.2  2006/02/18 23:19:05  cgruber
 341  
  * Update imports and maven dependencies.
 342  
  *
 343  
  * Revision 1.1  2006/02/16 13:22:22  cgruber
 344  
  * Check in all sources in eclipse-friendly maven-enabled packages.
 345  
  *
 346  
  * Revision 1.5  2003/08/06 23:07:52  chochos
 347  
  * general code cleanup (mostly, removing unused imports)
 348  
  *
 349  
  * Revision 1.4  2002/05/15 14:05:55  mpowers
 350  
  * Now appropriately selectingFromDisplayGroup on establishConnection.
 351  
  *
 352  
  * Revision 1.3  2001/09/14 13:40:26  mpowers
 353  
  * User-initiated selection changes are now handled on the next event loop
 354  
  * so that the component repaints the new selection before any potentially
 355  
  * lengthy logic is triggered by the selection change.
 356  
  *
 357  
  * Revision 1.2  2001/02/17 16:52:05  mpowers
 358  
  * Changes in imports to support building with jdk1.1 collections.
 359  
  *
 360  
  * Revision 1.1.1.1  2000/12/21 15:48:49  mpowers
 361  
  * Contributing wotonomy.
 362  
  *
 363  
  * Revision 1.5  2000/12/20 16:25:41  michael
 364  
  * Added log to all files.
 365  
  *
 366  
  *
 367  
  */
 368