summaryrefslogtreecommitdiff
path: root/projects/net.wotonomy.ui/src/main/java/net/wotonomy/ui/EODisplayGroup.java
diff options
context:
space:
mode:
authorBenjamin Culkin <scorpress@gmail.com>2024-05-20 17:58:16 -0400
committerBenjamin Culkin <scorpress@gmail.com>2024-05-20 17:58:16 -0400
commit40a9d99496e098562f090fb7ffce9e749011b131 (patch)
tree437df24d65470582e943e494a52db8ed65a881ae /projects/net.wotonomy.ui/src/main/java/net/wotonomy/ui/EODisplayGroup.java
parentff072dfe782f6f22123cd4ba050828d35c0d0fbd (diff)
Formatting pass
Diffstat (limited to 'projects/net.wotonomy.ui/src/main/java/net/wotonomy/ui/EODisplayGroup.java')
-rw-r--r--projects/net.wotonomy.ui/src/main/java/net/wotonomy/ui/EODisplayGroup.java4040
1 files changed, 1799 insertions, 2241 deletions
diff --git a/projects/net.wotonomy.ui/src/main/java/net/wotonomy/ui/EODisplayGroup.java b/projects/net.wotonomy.ui/src/main/java/net/wotonomy/ui/EODisplayGroup.java
index ed65b1c..fd80713 100644
--- a/projects/net.wotonomy.ui/src/main/java/net/wotonomy/ui/EODisplayGroup.java
+++ b/projects/net.wotonomy.ui/src/main/java/net/wotonomy/ui/EODisplayGroup.java
@@ -51,2309 +51,1867 @@ import net.wotonomy.foundation.internal.Duplicator;
import net.wotonomy.foundation.internal.WotonomyException;
/**
-* EODisplayGroup provides an abstraction of a user interface,
-* comprising of an ordered collection of data objects, some
-* of which are displayed, and of those some are selected.
-*
-* @author michael@mpowers.net
-* @author $Author: cgruber $
-* @version $Revision: 904 $
-*/
-public class EODisplayGroup extends Observable
- implements EOObserving, EOEditingContext.Editor
-{
- /**
- * Notification sent when the display group is about to fetch.
- */
- public static final String DisplayGroupWillFetchNotification
- = "DisplayGroupWillFetchNotification";
-
- private static boolean
- globalDefaultForValidatesChangesImmediately = true;
- private static String
- globalDefaultStringMatchFormat = "caseInsensitiveLike";
- private static String
- globalDefaultStringMatchOperator = "%@*";
-
- protected NSMutableArray allObjects;
- protected NSArray allObjectsProxy;
- protected NSMutableArray displayedObjects;
- protected NSArray displayedObjectsProxy;
- protected NSMutableArray selectedObjects;
- protected NSArray selectedObjectsProxy;
- protected NSMutableArray selectedIndexes;
-
- private String defaultStringMatchOperator;
- private String defaultStringMatchFormat;
-
- private boolean validatesChangesImmediately;
- private Object delegate;
- private EODataSource dataSource;
- private EOAssociation editingAssociation;
- private EOQualifier qualifier;
- private NSMutableArray sortOrderings;
- private NSArray sortOrderingsProxy;
-
- private NSArray localKeys;
- private NSDictionary insertedObjectDefaultValues;
- private boolean fetchesOnLoad;
- private boolean selectsFirstObjectAfterFetch;
- private boolean usesOptimisticRefresh;
- private boolean inQueryMode;
-
- // change detection: package access for helper classes
- boolean contentsChanged;
- boolean selectionChanged;
- int updatedObjectIndex;
-
- // this property is not in the spec
- private boolean compareByReference = false;
-
- private EOObserving lastGroupObserver;
-
- /**
- * Creates a new display group.
- */
- public EODisplayGroup ()
- {
- validatesChangesImmediately =
- globalDefaultForValidatesChangesImmediately();
- defaultStringMatchOperator =
- globalDefaultStringMatchFormat();
- defaultStringMatchFormat =
- globalDefaultStringMatchOperator();
-
- allObjects = new ObservableArray( this );
- allObjectsProxy = NSArray.arrayBackedByList( allObjects );
- displayedObjects = new NSMutableArray();
- displayedObjectsProxy = NSArray.arrayBackedByList( displayedObjects );
- selectedObjects = new NSMutableArray();
- selectedObjectsProxy = NSArray.arrayBackedByList( selectedObjects );
- sortOrderings = new NSMutableArray();
- sortOrderingsProxy = NSArray.arrayBackedByList( sortOrderings );
- selectedIndexes = new NSMutableArray();
-
- delegate = null;
- dataSource = null;
- editingAssociation = null;
- qualifier = null;
-
- localKeys = new NSArray(); // not implemented
- insertedObjectDefaultValues = new NSDictionary();
- fetchesOnLoad = false; // not implemented
- selectsFirstObjectAfterFetch = false;
- usesOptimisticRefresh = false;
- inQueryMode = false; // not implemented
-
- contentsChanged = false;
- selectionChanged = false;
- updatedObjectIndex = -1;
-
- // create our private delayed observer
- lastGroupObserver = new LastGroupObserver( this );
- EOObserverCenter.addObserver( lastGroupObserver, this );
- }
-
-
-
- // specify optional data source
-
- /**
- * Sets the data source that will be used by
- * this display group.
- */
- public void setDataSource ( EODataSource aDataSource )
- {
- if ( ( dataSource != null )
- && ( dataSource.editingContext() != null ) )
- {
- // un-register for notifications from existing parent store
- NSNotificationCenter.defaultCenter().removeObserver(
- this, null, dataSource.editingContext() );
- dataSource.editingContext().removeEditor( this );
- if ( dataSource.editingContext().messageHandler() == this )
- {
- dataSource.editingContext().setMessageHandler( null );
- }
-
- }
-
- dataSource = aDataSource;
-
- if ( ( dataSource != null )
- && ( dataSource.editingContext() != null ) )
- {
- // register for notifications from parent store
- NSNotificationCenter.defaultCenter().addObserver(
- this, new NSSelector( "objectsInvalidatedInEditingContext",
- new Class[] { NSNotification.class } ),
- null, dataSource.editingContext() );
-
- // add ourselves as editor
- dataSource.editingContext().addEditor( this );
-
- // add ourselves as message handler if no such handler exists
- if ( dataSource.editingContext().messageHandler() == null )
- {
- dataSource.editingContext().setMessageHandler( this );
- }
- }
- }
-
- /**
- * Returns the current data source backing this display group,
- * or null if no dataSource is currently used.
- */
- public EODataSource dataSource ()
- {
- return dataSource;
- }
-
-
-
- // specify optional delegate
-
- /**
- * Sets the display group delegate that
- * will be used by this display group.
- */
- public void setDelegate ( Object aDelegate )
- {
- delegate = aDelegate;
- }
-
- /**
- * Returns the current delegate for this display group,
- * or null if no delegate is currently set.
- */
- public Object delegate ()
- {
- return delegate;
- }
-
-
-
- // display group configuration
-
- /**
- * Returns the current string matching format.
- * If not set, defaults to "%@*".
- */
- public String defaultStringMatchFormat ()
- {
- return defaultStringMatchFormat;
- }
-
- /**
- * Returns the current string matching operator.
- * If not set, defaults to "caseInsensitiveLike".
- */
- public String defaultStringMatchOperator ()
- {
- return defaultStringMatchOperator;
- }
-
- /**
- * Sets the display group and associations to edit a
- * "query by example" query object. This method is
- * used for target/action connections.
- */
- public void enterQueryMode ( Object aSender )
- {
- throw new RuntimeException( "Not implemented yet." );
- }
-
- /**
- * Returns whether this display group should immediate
- * fetch when loaded.
- */
- public boolean fetchesOnLoad ()
- {
- return fetchesOnLoad;
- }
-
- /**
- * Returns whether this display group is in "query by
- * example" mode.
- */
- public boolean inQueryMode ()
- {
- return inQueryMode;
- }
-
- /**
- * Returns a Map of default values that are applied
- * to new objects that are inserted into the list.
- */
- public NSDictionary insertedObjectDefaultValues ()
- {
- return insertedObjectDefaultValues;
- }
-
- /**
- * Returns the keys that were declared when read from
- * an external resource file.
- */
- public NSArray localKeys ()
- {
- return localKeys;
- }
-
- /**
- * Sets whether this display group will select the
- * first object in the list after a fetch.
- */
- public boolean selectsFirstObjectAfterFetch ()
- {
- return selectsFirstObjectAfterFetch;
- }
-
- /**
- * Sets the default string matching format that
- * will be used by this display group.
- */
- public void setDefaultStringMatchFormat ( String aFormat )
- {
- throw new RuntimeException( "Not implemented yet." );
- }
-
- /**
- * Sets the default string matching operator that
- * will be used by this display group.
- */
- public void setDefaultStringMatchOperator ( String anOperator )
- {
- throw new RuntimeException( "Not implemented yet." );
- }
-
- /**
- * Sets whether this display group will fetch objects
- * from its data source on load.
- */
- public void setFetchesOnLoad ( boolean willFetch )
- {
- fetchesOnLoad = willFetch;
- }
-
- /**
- * Sets whether this display group is in "query by example"
- * mode. If true, all associations will bind to a special
- * "example" object.
- */
- public void setInQueryMode ( boolean isInQueryMode )
- {
- inQueryMode = isInQueryMode;
- }
-
- /**
- * Sets the mapping that contains the values that will
- * be applied to new objects inserted into the display group.
- */
- public void setInsertedObjectDefaultValues ( Map aMap )
- {
- insertedObjectDefaultValues = new NSDictionary( aMap );
- }
-
- /**
- * Sets the keys that are declared when instantiated from
- * an external resource file.
- */
- public void setLocalKeys ( List aKeyList )
- {
- localKeys = new NSArray( (Collection) aKeyList );
- }
-
- /**
- * Sets whether the first object in the list will be
- * selected after a fetch.
- */
- public void setSelectsFirstObjectAfterFetch (
- boolean selectsFirst )
- {
- selectsFirstObjectAfterFetch = selectsFirst;
- }
-
- /**
- * Sets the order of the keys by which this display group
- * will be ordered after a fetch or after a call to
- * updateDisplayedObjects(). The elements in the display
- * group will be sorted first by the first key, within
- * the first key, by the second key, and so on.
- */
- public void setSortOrderings ( List aList )
- {
- sortOrderings.removeAllObjects();
-
- Object o;
- Iterator it = aList.iterator();
- while ( it.hasNext() )
- {
- o = it.next();
- // handle the convenience of specifying just a key
- if ( ! ( o instanceof EOSortOrdering ) )
- {
- o = new EOSortOrdering(
- o.toString(), EOSortOrdering.CompareAscending );
- }
- sortOrderings.add( o );
- }
- }
-
- /**
- * Sets whether only changed objects are refreshed (optimistic),
- * or whether all objects are refreshed (pessimistic, default).
- * By default, when the display group receives notification that
- * one of its objects has changed, updateDisplayedObjects is called.
- */
- public void setUsesOptimisticRefresh ( boolean isOptimistic )
- {
- usesOptimisticRefresh = isOptimistic;
- }
-
- /**
- * Sets whether changes made by associations are validated
- * immediately, or when changes are saved.
- */
- public void setValidatesChangesImmediately (
- boolean validatesImmediately )
- {
- validatesChangesImmediately = validatesImmediately;
- }
-
- /**
- * Returns a read-only List of sort orderings for this display group.
- */
- public NSArray sortOrderings ()
- {
- return sortOrderingsProxy;
- }
-
- /**
- * Returns whether this display group refreshes only
- * the changed objects or all objects on refresh.
- */
- public boolean usesOptimisticRefresh ()
- {
- return usesOptimisticRefresh;
- }
-
- /**
- * Returns whether this display group validates changes
- * immediately. Otherwise, validation should occur when
- * changes are saved. Default is the global default,
- * which is initially true.
- */
- public boolean validatesChangesImmediately ()
- {
- return validatesChangesImmediately;
- }
-
-
- // qualification
-
- /**
- * Returns a qualifier that will be applied all the objects
- * in this display group to determine which objects will
- * be displayed.
- */
- public EOQualifier qualifier ()
- {
- return qualifier;
- }
-
- /**
- * Returns a new qualifier built from the three query
- * value maps: greater than, equal to, and less than.
- */
- public EOQualifier qualifierFromQueryValues ()
- {
- //TODO: assemble qualifier from query values
-
- return new EOQualifier()
- {
- // use inner class until we actually implement one
- public EOQualifier qualifierWithBindings(
- Map aMap,
- boolean requireAll )
- {
- return null;
- }
- public Throwable
- validateKeysWithRootClassDescription( Class aClass )
- {
- return null;
- }
- public boolean evaluateWithObject(Object o)
- {
- return false;
- }
- };
- }
-
- /**
- * Calls qualifierFromQueryValues(), applies the result
- * to the data source, and calls fetch().
- */
- public void qualifyDataSource ()
- {
- throw new RuntimeException( "Not implemented yet." );
- }
-
- /**
- * Calls qualifierFromQueryValues(), sets the qualifier
- * with setQualifier(), and calls updateDisplayedObjects().
- */
- public void qualifyDisplayGroup ()
- {
- setQualifier( qualifierFromQueryValues() );
- updateDisplayedObjects();
- }
-
- /**
- * Returns a Map containing the mappings of keys
- * to binding query values.
- */
- public NSDictionary queryBindingValues ()
- {
- throw new RuntimeException( "Not implemented yet." );
- }
-
- /**
- * Returns a Map containing the mappings of keys
- * to operator values.
- */
- public NSDictionary queryOperatorValues ()
- {
- throw new RuntimeException( "Not implemented yet." );
- }
-
- /**
- * Sets the qualifier that will be used by
- * updateDisplayedObjects() to filter displayed objects.
- */
- public void setQualifier ( EOQualifier aQualifier )
- {
- qualifier = aQualifier;
- }
-
- /**
- * Sets the mapping that contains the mappings of keys
- * to binding values.
- */
- public void setQueryBindingValues ( Map aMap )
- {
- throw new RuntimeException( "Not implemented yet." );
- }
-
- /**
- * Sets the mapping that contains the mappings of keys
- * to operator values.
- */
- public void setQueryOperatorValues ( Map aMap )
- {
- throw new RuntimeException( "Not implemented yet." );
- }
-
-
-
- // qualifier query values
-
- /**
- * Returns a Map containing the mappings of keys
- * to query values that will be used to test for equality.
- */
- public NSDictionary equalToQueryValues ()
- {
- throw new RuntimeException( "Not implemented yet." );
- }
-
- /**
- * Returns a Map containing the mappings of keys
- * to query values that will be used to test for greater value.
- */
- public NSDictionary greaterThanQueryValues ()
- {
- throw new RuntimeException( "Not implemented yet." );
- }
-
- /**
- * Returns a Map containing the mappings of keys
- * to query values that will be used to test for lesser value.
- */
- public NSDictionary lessThanQueryValues ()
- {
- throw new RuntimeException( "Not implemented yet." );
- }
-
- /**
- * Sets the Map that contains the mappings of keys
- * to query values that will be used to test for equality.
- */
- public void setEqualToQueryValues ( Map aMap )
- {
- throw new RuntimeException( "Not implemented yet." );
- }
-
- /**
- * Sets the mapping that contains the mappings of keys
- * to query values that will be used to test for greater value.
- */
- public void setGreaterThanQueryValues ( Map aMap )
- {
- throw new RuntimeException( "Not implemented yet." );
- }
-
- /**
- * Sets the mapping that contains the mappings of keys
- * to query values that will be used to test for lesser value.
- */
- public void setLessThanQueryValues ( Map aMap )
- {
- throw new RuntimeException( "Not implemented yet." );
- }
-
-
- // interface to associations
-
- /**
- * Called by an association when it begins editing.
- */
- public void associationDidBeginEditing ( EOAssociation anAssociation )
- { // System.out.println( "EODisplayGroup.associationDidBeginEditing: " + anAssociation );
- if ( dataSource != null )
- {
- if ( dataSource.editingContext() != null )
- {
- dataSource.editingContext().setMessageHandler( this );
- }
- }
- editingAssociation = anAssociation;
- }
-
- /**
- * Called by an association when it is finished editing.
- */
- public void associationDidEndEditing ( EOAssociation anAssociation )
- { // System.out.println( "EODisplayGroup.associationDidEndEditing: " + anAssociation );
- editingAssociation = null;
- }
-
- /**
- * Called by associations to determine whether the contents
- * of any objects have been changed. Returns true if the
- * contents have changed and not all observers have been
- * notified.
- */
- public boolean contentsChanged ()
- {
- return contentsChanged;
- }
-
- /**
- * Called by associations to determine whether the
- * selection has been changed. Returns true if the
- * selection has changed and not all observers have
- * been notified.
- */
- public boolean selectionChanged ()
- {
- return selectionChanged;
- }
-
- /**
- * Called by an association when a user-specified value fails the association's
- * validation rules. This implementation returns true, unless the delegate
- * prevents this.
- * @return True to allow the association to handle user notification,
- * otherwise return false to let the association know that the
- * display group notified the user.
- */
- public boolean associationFailedToValidateValue (
- EOAssociation anAssociation,
- String aValue,
- String aKey,
- Object anObject,
- String anErrorDescription )
- {
- Object result = notifyDelegate(
- "displayGroupShouldDisplayAlert",
- new Class[] { EODisplayGroup.class, String.class, String.class },
- new Object[] { this, "Validation Failed", anErrorDescription } );
- if ( ( result == null ) || ( Boolean.TRUE.equals( result ) ) )
- {
- return true;
- }
- return false;
- }
-
- /**
- * Called by an association to determine whether it should enable
- * a component that displayes a value for the specified key.
- * @return true if an object is selected, or if
- * the specified key is a query key. Otherwise false.
- */
- public boolean enabledToSetSelectedObjectValueForKey ( String aKey )
- {
- if ( ( aKey != null ) && ( aKey.startsWith( "@" ) ) ) return true;
- return ( selectedObject() != null );
- }
-
-
- // association management
-
- /**
- * Returns the association that is currently being edited,
- * or null if no editing is taking place.
- */
- public EOAssociation editingAssociation ()
- {
- return editingAssociation;
- }
-
- /**
- * Asks the association currently editing to stop editing.
- * @returns true if editing was stopped, false if the
- * association refused to stop editing (if a modal dialog
- * is displayed or a value failed to validate).
- */
- public boolean endEditing ()
- {
- if ( editingAssociation == null ) return true;
- return editingAssociation.endEditing();
- }
-
- /**
- * Returns a read-only List of associations that are observing
- * this display group.
- */
- public NSArray observingAssociations ()
- {
- NSArray observers =
- EOObserverCenter.observersForObject( this );
- NSMutableArray result = new NSMutableArray();
-
- Object o;
- Enumeration e = observers.objectEnumerator();
- while ( e.hasMoreElements() )
- {
- o = e.nextElement();
- if ( o instanceof EOAssociation )
- {
- result.addObject( o );
- }
- }
- return result;
- }
-
-
-
- // object management
-
- /**
- * Returns a read-only List containing all objects managed by the display group.
- * This includes those objects not visible due to disqualification.
- */
- public NSArray allObjects ()
- { //System.out.println( "avoided allocation: allObjects" );
- return allObjectsProxy;
- }
-
- /**
- * Clears the current selection.
- * @return True is the selection was cleared,
- * False if the selection could not be cleared
- * @see #setSelectionIndexes
- */
- public boolean clearSelection ()
- {
- Object result = notifyDelegate(
- "displayGroupShouldChangeSelection",
- new Class[] { EODisplayGroup.class, List.class },
- new Object[] { this, new NSArray( selectedObjects ) } );
- if ( ( result != null ) && ( Boolean.FALSE.equals( result ) ) )
- {
- return false;
- }
-
- selectionChanged = true;
- willChange();
-
- selectedObjects.removeAllObjects();
- selectedIndexes.removeAllObjects();
-
- notifyDelegate(
- "displayGroupDidChangeSelection",
- new Class[] { EODisplayGroup.class },
- new Object[] { this } );
- notifyDelegate(
- "displayGroupDidChangeSelectedObjects",
- new Class[] { EODisplayGroup.class },
- new Object[] { this } );
-
- return true;
- }
-
- /**
- * Deletes the object at the specified index,
- * notifying the delegate before and after the operation,
- * and then updating the selection if needed.
- * @return True if delete was successful, false if the
- * object was not deleted.
- */
- public boolean deleteObjectAtIndex ( int anIndex )
- {
- Object target = displayedObjects.objectAtIndex( anIndex );
-
- Object result = notifyDelegate(
- "displayGroupShouldDeleteObject",
- new Class[] { EODisplayGroup.class, Object.class },
- new Object[] { this, target } );
- if ( ( result != null ) && ( Boolean.FALSE.equals( result ) ) )
- {
- return false;
- }
-
- contentsChanged = true;
-
- deleteObjectAtIndexNoNotify( anIndex );
-
- if ( dataSource != null )
- {
- dataSource.deleteObject( target );
- }
-
- notifyDelegate(
- "displayGroupDidDeleteObject",
- new Class[] { EODisplayGroup.class, Object.class },
- new Object[] { this, target } );
-
- return true;
- }
-
- private void deleteObjectAtIndexNoNotify ( int anIndex )
- {
- Object target = displayedObjects.objectAtIndex( anIndex );
-
- int i;
-
- // remove from selected objects if necessary
- i = indexOf( selectedObjects, target );
- if ( i != NSArray.NotFound )
- {
- selectionChanged = true;
- willChange(); // notify before removing
- selectedObjects.removeObjectAtIndex( i );
- selectedIndexes.remove( new Integer( i ) ); // comps by value
- }
- else // notify - no selection change needed
- {
- willChange();
- }
-
- // remove from all objects
- i = indexOf( allObjects, target );
- if ( i != NSArray.NotFound )
- {
- allObjects.removeObjectAtIndex( i );
- }
- else // otherwise should never happen
- {
+ * EODisplayGroup provides an abstraction of a user interface, comprising of an
+ * ordered collection of data objects, some of which are displayed, and of those
+ * some are selected.
+ *
+ * @author michael@mpowers.net
+ * @author $Author: cgruber $
+ * @version $Revision: 904 $
+ */
+public class EODisplayGroup extends Observable implements EOObserving, EOEditingContext.Editor {
+ /**
+ * Notification sent when the display group is about to fetch.
+ */
+ public static final String DisplayGroupWillFetchNotification = "DisplayGroupWillFetchNotification";
+
+ private static boolean globalDefaultForValidatesChangesImmediately = true;
+ private static String globalDefaultStringMatchFormat = "caseInsensitiveLike";
+ private static String globalDefaultStringMatchOperator = "%@*";
+
+ protected NSMutableArray allObjects;
+ protected NSArray allObjectsProxy;
+ protected NSMutableArray displayedObjects;
+ protected NSArray displayedObjectsProxy;
+ protected NSMutableArray selectedObjects;
+ protected NSArray selectedObjectsProxy;
+ protected NSMutableArray selectedIndexes;
+
+ private String defaultStringMatchOperator;
+ private String defaultStringMatchFormat;
+
+ private boolean validatesChangesImmediately;
+ private Object delegate;
+ private EODataSource dataSource;
+ private EOAssociation editingAssociation;
+ private EOQualifier qualifier;
+ private NSMutableArray sortOrderings;
+ private NSArray sortOrderingsProxy;
+
+ private NSArray localKeys;
+ private NSDictionary insertedObjectDefaultValues;
+ private boolean fetchesOnLoad;
+ private boolean selectsFirstObjectAfterFetch;
+ private boolean usesOptimisticRefresh;
+ private boolean inQueryMode;
+
+ // change detection: package access for helper classes
+ boolean contentsChanged;
+ boolean selectionChanged;
+ int updatedObjectIndex;
+
+ // this property is not in the spec
+ private boolean compareByReference = false;
+
+ private EOObserving lastGroupObserver;
+
+ /**
+ * Creates a new display group.
+ */
+ public EODisplayGroup() {
+ validatesChangesImmediately = globalDefaultForValidatesChangesImmediately();
+ defaultStringMatchOperator = globalDefaultStringMatchFormat();
+ defaultStringMatchFormat = globalDefaultStringMatchOperator();
+
+ allObjects = new ObservableArray(this);
+ allObjectsProxy = NSArray.arrayBackedByList(allObjects);
+ displayedObjects = new NSMutableArray();
+ displayedObjectsProxy = NSArray.arrayBackedByList(displayedObjects);
+ selectedObjects = new NSMutableArray();
+ selectedObjectsProxy = NSArray.arrayBackedByList(selectedObjects);
+ sortOrderings = new NSMutableArray();
+ sortOrderingsProxy = NSArray.arrayBackedByList(sortOrderings);
+ selectedIndexes = new NSMutableArray();
+
+ delegate = null;
+ dataSource = null;
+ editingAssociation = null;
+ qualifier = null;
+
+ localKeys = new NSArray(); // not implemented
+ insertedObjectDefaultValues = new NSDictionary();
+ fetchesOnLoad = false; // not implemented
+ selectsFirstObjectAfterFetch = false;
+ usesOptimisticRefresh = false;
+ inQueryMode = false; // not implemented
+
+ contentsChanged = false;
+ selectionChanged = false;
+ updatedObjectIndex = -1;
+
+ // create our private delayed observer
+ lastGroupObserver = new LastGroupObserver(this);
+ EOObserverCenter.addObserver(lastGroupObserver, this);
+ }
+
+ // specify optional data source
+
+ /**
+ * Sets the data source that will be used by this display group.
+ */
+ public void setDataSource(EODataSource aDataSource) {
+ if ((dataSource != null) && (dataSource.editingContext() != null)) {
+ // un-register for notifications from existing parent store
+ NSNotificationCenter.defaultCenter().removeObserver(this, null, dataSource.editingContext());
+ dataSource.editingContext().removeEditor(this);
+ if (dataSource.editingContext().messageHandler() == this) {
+ dataSource.editingContext().setMessageHandler(null);
+ }
+
+ }
+
+ dataSource = aDataSource;
+
+ if ((dataSource != null) && (dataSource.editingContext() != null)) {
+ // register for notifications from parent store
+ NSNotificationCenter.defaultCenter().addObserver(this,
+ new NSSelector("objectsInvalidatedInEditingContext", new Class[] { NSNotification.class }), null,
+ dataSource.editingContext());
+
+ // add ourselves as editor
+ dataSource.editingContext().addEditor(this);
+
+ // add ourselves as message handler if no such handler exists
+ if (dataSource.editingContext().messageHandler() == null) {
+ dataSource.editingContext().setMessageHandler(this);
+ }
+ }
+ }
+
+ /**
+ * Returns the current data source backing this display group, or null if no
+ * dataSource is currently used.
+ */
+ public EODataSource dataSource() {
+ return dataSource;
+ }
+
+ // specify optional delegate
+
+ /**
+ * Sets the display group delegate that will be used by this display group.
+ */
+ public void setDelegate(Object aDelegate) {
+ delegate = aDelegate;
+ }
+
+ /**
+ * Returns the current delegate for this display group, or null if no delegate
+ * is currently set.
+ */
+ public Object delegate() {
+ return delegate;
+ }
+
+ // display group configuration
+
+ /**
+ * Returns the current string matching format. If not set, defaults to "%@*".
+ */
+ public String defaultStringMatchFormat() {
+ return defaultStringMatchFormat;
+ }
+
+ /**
+ * Returns the current string matching operator. If not set, defaults to
+ * "caseInsensitiveLike".
+ */
+ public String defaultStringMatchOperator() {
+ return defaultStringMatchOperator;
+ }
+
+ /**
+ * Sets the display group and associations to edit a "query by example" query
+ * object. This method is used for target/action connections.
+ */
+ public void enterQueryMode(Object aSender) {
+ throw new RuntimeException("Not implemented yet.");
+ }
+
+ /**
+ * Returns whether this display group should immediate fetch when loaded.
+ */
+ public boolean fetchesOnLoad() {
+ return fetchesOnLoad;
+ }
+
+ /**
+ * Returns whether this display group is in "query by example" mode.
+ */
+ public boolean inQueryMode() {
+ return inQueryMode;
+ }
+
+ /**
+ * Returns a Map of default values that are applied to new objects that are
+ * inserted into the list.
+ */
+ public NSDictionary insertedObjectDefaultValues() {
+ return insertedObjectDefaultValues;
+ }
+
+ /**
+ * Returns the keys that were declared when read from an external resource file.
+ */
+ public NSArray localKeys() {
+ return localKeys;
+ }
+
+ /**
+ * Sets whether this display group will select the first object in the list
+ * after a fetch.
+ */
+ public boolean selectsFirstObjectAfterFetch() {
+ return selectsFirstObjectAfterFetch;
+ }
+
+ /**
+ * Sets the default string matching format that will be used by this display
+ * group.
+ */
+ public void setDefaultStringMatchFormat(String aFormat) {
+ throw new RuntimeException("Not implemented yet.");
+ }
+
+ /**
+ * Sets the default string matching operator that will be used by this display
+ * group.
+ */
+ public void setDefaultStringMatchOperator(String anOperator) {
+ throw new RuntimeException("Not implemented yet.");
+ }
+
+ /**
+ * Sets whether this display group will fetch objects from its data source on
+ * load.
+ */
+ public void setFetchesOnLoad(boolean willFetch) {
+ fetchesOnLoad = willFetch;
+ }
+
+ /**
+ * Sets whether this display group is in "query by example" mode. If true, all
+ * associations will bind to a special "example" object.
+ */
+ public void setInQueryMode(boolean isInQueryMode) {
+ inQueryMode = isInQueryMode;
+ }
+
+ /**
+ * Sets the mapping that contains the values that will be applied to new objects
+ * inserted into the display group.
+ */
+ public void setInsertedObjectDefaultValues(Map aMap) {
+ insertedObjectDefaultValues = new NSDictionary(aMap);
+ }
+
+ /**
+ * Sets the keys that are declared when instantiated from an external resource
+ * file.
+ */
+ public void setLocalKeys(List aKeyList) {
+ localKeys = new NSArray((Collection) aKeyList);
+ }
+
+ /**
+ * Sets whether the first object in the list will be selected after a fetch.
+ */
+ public void setSelectsFirstObjectAfterFetch(boolean selectsFirst) {
+ selectsFirstObjectAfterFetch = selectsFirst;
+ }
+
+ /**
+ * Sets the order of the keys by which this display group will be ordered after
+ * a fetch or after a call to updateDisplayedObjects(). The elements in the
+ * display group will be sorted first by the first key, within the first key, by
+ * the second key, and so on.
+ */
+ public void setSortOrderings(List aList) {
+ sortOrderings.removeAllObjects();
+
+ Object o;
+ Iterator it = aList.iterator();
+ while (it.hasNext()) {
+ o = it.next();
+ // handle the convenience of specifying just a key
+ if (!(o instanceof EOSortOrdering)) {
+ o = new EOSortOrdering(o.toString(), EOSortOrdering.CompareAscending);
+ }
+ sortOrderings.add(o);
+ }
+ }
+
+ /**
+ * Sets whether only changed objects are refreshed (optimistic), or whether all
+ * objects are refreshed (pessimistic, default). By default, when the display
+ * group receives notification that one of its objects has changed,
+ * updateDisplayedObjects is called.
+ */
+ public void setUsesOptimisticRefresh(boolean isOptimistic) {
+ usesOptimisticRefresh = isOptimistic;
+ }
+
+ /**
+ * Sets whether changes made by associations are validated immediately, or when
+ * changes are saved.
+ */
+ public void setValidatesChangesImmediately(boolean validatesImmediately) {
+ validatesChangesImmediately = validatesImmediately;
+ }
+
+ /**
+ * Returns a read-only List of sort orderings for this display group.
+ */
+ public NSArray sortOrderings() {
+ return sortOrderingsProxy;
+ }
+
+ /**
+ * Returns whether this display group refreshes only the changed objects or all
+ * objects on refresh.
+ */
+ public boolean usesOptimisticRefresh() {
+ return usesOptimisticRefresh;
+ }
+
+ /**
+ * Returns whether this display group validates changes immediately. Otherwise,
+ * validation should occur when changes are saved. Default is the global
+ * default, which is initially true.
+ */
+ public boolean validatesChangesImmediately() {
+ return validatesChangesImmediately;
+ }
+
+ // qualification
+
+ /**
+ * Returns a qualifier that will be applied all the objects in this display
+ * group to determine which objects will be displayed.
+ */
+ public EOQualifier qualifier() {
+ return qualifier;
+ }
+
+ /**
+ * Returns a new qualifier built from the three query value maps: greater than,
+ * equal to, and less than.
+ */
+ public EOQualifier qualifierFromQueryValues() {
+ // TODO: assemble qualifier from query values
+
+ return new EOQualifier() {
+ // use inner class until we actually implement one
+ public EOQualifier qualifierWithBindings(Map aMap, boolean requireAll) {
+ return null;
+ }
+
+ public Throwable validateKeysWithRootClassDescription(Class aClass) {
+ return null;
+ }
+
+ public boolean evaluateWithObject(Object o) {
+ return false;
+ }
+ };
+ }
+
+ /**
+ * Calls qualifierFromQueryValues(), applies the result to the data source, and
+ * calls fetch().
+ */
+ public void qualifyDataSource() {
+ throw new RuntimeException("Not implemented yet.");
+ }
+
+ /**
+ * Calls qualifierFromQueryValues(), sets the qualifier with setQualifier(), and
+ * calls updateDisplayedObjects().
+ */
+ public void qualifyDisplayGroup() {
+ setQualifier(qualifierFromQueryValues());
+ updateDisplayedObjects();
+ }
+
+ /**
+ * Returns a Map containing the mappings of keys to binding query values.
+ */
+ public NSDictionary queryBindingValues() {
+ throw new RuntimeException("Not implemented yet.");
+ }
+
+ /**
+ * Returns a Map containing the mappings of keys to operator values.
+ */
+ public NSDictionary queryOperatorValues() {
+ throw new RuntimeException("Not implemented yet.");
+ }
+
+ /**
+ * Sets the qualifier that will be used by updateDisplayedObjects() to filter
+ * displayed objects.
+ */
+ public void setQualifier(EOQualifier aQualifier) {
+ qualifier = aQualifier;
+ }
+
+ /**
+ * Sets the mapping that contains the mappings of keys to binding values.
+ */
+ public void setQueryBindingValues(Map aMap) {
+ throw new RuntimeException("Not implemented yet.");
+ }
+
+ /**
+ * Sets the mapping that contains the mappings of keys to operator values.
+ */
+ public void setQueryOperatorValues(Map aMap) {
+ throw new RuntimeException("Not implemented yet.");
+ }
+
+ // qualifier query values
+
+ /**
+ * Returns a Map containing the mappings of keys to query values that will be
+ * used to test for equality.
+ */
+ public NSDictionary equalToQueryValues() {
+ throw new RuntimeException("Not implemented yet.");
+ }
+
+ /**
+ * Returns a Map containing the mappings of keys to query values that will be
+ * used to test for greater value.
+ */
+ public NSDictionary greaterThanQueryValues() {
+ throw new RuntimeException("Not implemented yet.");
+ }
+
+ /**
+ * Returns a Map containing the mappings of keys to query values that will be
+ * used to test for lesser value.
+ */
+ public NSDictionary lessThanQueryValues() {
+ throw new RuntimeException("Not implemented yet.");
+ }
+
+ /**
+ * Sets the Map that contains the mappings of keys to query values that will be
+ * used to test for equality.
+ */
+ public void setEqualToQueryValues(Map aMap) {
+ throw new RuntimeException("Not implemented yet.");
+ }
+
+ /**
+ * Sets the mapping that contains the mappings of keys to query values that will
+ * be used to test for greater value.
+ */
+ public void setGreaterThanQueryValues(Map aMap) {
+ throw new RuntimeException("Not implemented yet.");
+ }
+
+ /**
+ * Sets the mapping that contains the mappings of keys to query values that will
+ * be used to test for lesser value.
+ */
+ public void setLessThanQueryValues(Map aMap) {
+ throw new RuntimeException("Not implemented yet.");
+ }
+
+ // interface to associations
+
+ /**
+ * Called by an association when it begins editing.
+ */
+ public void associationDidBeginEditing(EOAssociation anAssociation) { // System.out.println(
+ // "EODisplayGroup.associationDidBeginEditing:
+ // " + anAssociation );
+ if (dataSource != null) {
+ if (dataSource.editingContext() != null) {
+ dataSource.editingContext().setMessageHandler(this);
+ }
+ }
+ editingAssociation = anAssociation;
+ }
+
+ /**
+ * Called by an association when it is finished editing.
+ */
+ public void associationDidEndEditing(EOAssociation anAssociation) { // System.out.println(
+ // "EODisplayGroup.associationDidEndEditing: " +
+ // anAssociation );
+ editingAssociation = null;
+ }
+
+ /**
+ * Called by associations to determine whether the contents of any objects have
+ * been changed. Returns true if the contents have changed and not all observers
+ * have been notified.
+ */
+ public boolean contentsChanged() {
+ return contentsChanged;
+ }
+
+ /**
+ * Called by associations to determine whether the selection has been changed.
+ * Returns true if the selection has changed and not all observers have been
+ * notified.
+ */
+ public boolean selectionChanged() {
+ return selectionChanged;
+ }
+
+ /**
+ * Called by an association when a user-specified value fails the association's
+ * validation rules. This implementation returns true, unless the delegate
+ * prevents this.
+ *
+ * @return True to allow the association to handle user notification, otherwise
+ * return false to let the association know that the display group
+ * notified the user.
+ */
+ public boolean associationFailedToValidateValue(EOAssociation anAssociation, String aValue, String aKey,
+ Object anObject, String anErrorDescription) {
+ Object result = notifyDelegate("displayGroupShouldDisplayAlert",
+ new Class[] { EODisplayGroup.class, String.class, String.class },
+ new Object[] { this, "Validation Failed", anErrorDescription });
+ if ((result == null) || (Boolean.TRUE.equals(result))) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Called by an association to determine whether it should enable a component
+ * that displayes a value for the specified key.
+ *
+ * @return true if an object is selected, or if the specified key is a query
+ * key. Otherwise false.
+ */
+ public boolean enabledToSetSelectedObjectValueForKey(String aKey) {
+ if ((aKey != null) && (aKey.startsWith("@")))
+ return true;
+ return (selectedObject() != null);
+ }
+
+ // association management
+
+ /**
+ * Returns the association that is currently being edited, or null if no editing
+ * is taking place.
+ */
+ public EOAssociation editingAssociation() {
+ return editingAssociation;
+ }
+
+ /**
+ * Asks the association currently editing to stop editing.
+ *
+ * @returns true if editing was stopped, false if the association refused to
+ * stop editing (if a modal dialog is displayed or a value failed to
+ * validate).
+ */
+ public boolean endEditing() {
+ if (editingAssociation == null)
+ return true;
+ return editingAssociation.endEditing();
+ }
+
+ /**
+ * Returns a read-only List of associations that are observing this display
+ * group.
+ */
+ public NSArray observingAssociations() {
+ NSArray observers = EOObserverCenter.observersForObject(this);
+ NSMutableArray result = new NSMutableArray();
+
+ Object o;
+ Enumeration e = observers.objectEnumerator();
+ while (e.hasMoreElements()) {
+ o = e.nextElement();
+ if (o instanceof EOAssociation) {
+ result.addObject(o);
+ }
+ }
+ return result;
+ }
+
+ // object management
+
+ /**
+ * Returns a read-only List containing all objects managed by the display group.
+ * This includes those objects not visible due to disqualification.
+ */
+ public NSArray allObjects() { // System.out.println( "avoided allocation: allObjects" );
+ return allObjectsProxy;
+ }
+
+ /**
+ * Clears the current selection.
+ *
+ * @return True is the selection was cleared, False if the selection could not
+ * be cleared
+ * @see #setSelectionIndexes
+ */
+ public boolean clearSelection() {
+ Object result = notifyDelegate("displayGroupShouldChangeSelection",
+ new Class[] { EODisplayGroup.class, List.class }, new Object[] { this, new NSArray(selectedObjects) });
+ if ((result != null) && (Boolean.FALSE.equals(result))) {
+ return false;
+ }
+
+ selectionChanged = true;
+ willChange();
+
+ selectedObjects.removeAllObjects();
+ selectedIndexes.removeAllObjects();
+
+ notifyDelegate("displayGroupDidChangeSelection", new Class[] { EODisplayGroup.class }, new Object[] { this });
+ notifyDelegate("displayGroupDidChangeSelectedObjects", new Class[] { EODisplayGroup.class },
+ new Object[] { this });
+
+ return true;
+ }
+
+ /**
+ * Deletes the object at the specified index, notifying the delegate before and
+ * after the operation, and then updating the selection if needed.
+ *
+ * @return True if delete was successful, false if the object was not deleted.
+ */
+ public boolean deleteObjectAtIndex(int anIndex) {
+ Object target = displayedObjects.objectAtIndex(anIndex);
+
+ Object result = notifyDelegate("displayGroupShouldDeleteObject",
+ new Class[] { EODisplayGroup.class, Object.class }, new Object[] { this, target });
+ if ((result != null) && (Boolean.FALSE.equals(result))) {
+ return false;
+ }
+
+ contentsChanged = true;
+
+ deleteObjectAtIndexNoNotify(anIndex);
+
+ if (dataSource != null) {
+ dataSource.deleteObject(target);
+ }
+
+ notifyDelegate("displayGroupDidDeleteObject", new Class[] { EODisplayGroup.class, Object.class },
+ new Object[] { this, target });
+
+ return true;
+ }
+
+ private void deleteObjectAtIndexNoNotify(int anIndex) {
+ Object target = displayedObjects.objectAtIndex(anIndex);
+
+ int i;
+
+ // remove from selected objects if necessary
+ i = indexOf(selectedObjects, target);
+ if (i != NSArray.NotFound) {
+ selectionChanged = true;
+ willChange(); // notify before removing
+ selectedObjects.removeObjectAtIndex(i);
+ selectedIndexes.remove(new Integer(i)); // comps by value
+ } else // notify - no selection change needed
+ {
+ willChange();
+ }
+
+ // remove from all objects
+ i = indexOf(allObjects, target);
+ if (i != NSArray.NotFound) {
+ allObjects.removeObjectAtIndex(i);
+ } else // otherwise should never happen
+ {
// throw new WotonomyException(
// "Displayed object not found in allObjects" );
- }
-
- // remove from displayed objects
- displayedObjects.removeObjectAtIndex( anIndex );
- }
-
- /**
- * Deletes the currently selected objects.
- * This implementation calls deleteObjectAtIndex() for
- * each index in the selection list, immediately returning
- * false if any delete operation fails.
- * @return True if all selected objects were deleted,
- * false if any deletion failed.
- */
- public boolean deleteSelection ()
- {
- int i;
- boolean result = true;
-
- Enumeration e = new NSArray( selectedObjects ).objectEnumerator();
- while ( e.hasMoreElements() )
- {
- i = indexOf( displayedObjects, e.nextElement() );
- if ( i == NSArray.NotFound )
- {
- // should never happen
- throw new WotonomyException(
- "Selected object not found in displayedObjects" );
- }
- result = result && deleteObjectAtIndex( i );
- }
-
- return result;
- }
-
- /**
- * Returns a read-only List of all objects in the display group
- * that are currently displayed by the associations.
- */
- public NSArray displayedObjects ()
- { // System.out.println( "avoided allocation: displayedObjects" );
- return displayedObjectsProxy;
- }
-
- /**
- * Requests a list of objects from the DataSource
- * and calls setObjectArray to populate the list.
- * More specifically, calls endEditing(), asks the
- * delegate, fetches the objects, notifies the delegate,
- * and populates the list.
- */
- public boolean fetch ()
- {
- endEditing();
-
- if ( dataSource == null )
- {
- return false;
- }
-
- Object result = notifyDelegate(
- "displayGroupShouldFetch",
- new Class[] { EODisplayGroup.class },
- new Object[] { this } );
- if ( ( result != null ) && ( Boolean.FALSE.equals( result ) ) )
- {
- return false;
- }
-
- NSNotificationCenter.defaultCenter().postNotification(
- DisplayGroupWillFetchNotification, this, new NSDictionary() );
-
- NSArray objectList = dataSource.fetchObjects();
-
- notifyDelegate(
- "displayGroupDidFetchObjects",
- new Class[] { EODisplayGroup.class, List.class },
- new Object[] { this, objectList } );
-
- if ( selectsFirstObjectAfterFetch )
- {
- //note: there's a good chance this logic ought to be in master-detail assoc:
- // we're doing this because changes in the master object trigger a refetch
- // on the child display group which annoyingly changes the selection.
- NSArray original = new NSArray( allObjects );
- setObjectArray( objectList );
- if ( displayedObjects.size() > 0
- && !original.equals( allObjects ) ) // don't change if no change
- {
- setSelectionIndexes( new NSArray( new Integer( 0 ) ) );
- }
- }
- else
- {
- setObjectArray( objectList );
- }
-
- return true;
- }
-
- /**
- * Creates a new object at the specified index.
- * Calls insertObjectAtIndex() with the result
- * from sending createObject() to the data source.
- * Presents a JOptionPane if the create fails, unless
- * the delegate implements displayGroupCreateObjectFailed.
- * @return the newly created object.
- */
- public Object insertNewObjectAtIndex ( int anIndex )
- {
- Object result = null;
- if ( dataSource != null )
- {
- result = dataSource.createObject();
- }
- if ( result != null )
- {
- if ( insertedObjectDefaultValues != null )
- {
- Duplicator.writePropertiesForObject(
- insertedObjectDefaultValues, result );
- }
- insertObjectAtIndex( result, anIndex );
- }
- else // create failed
- {
- if ( delegate() != null )
- {
- NSSelector selector = new NSSelector(
- "displayGroupCreateObjectFailed",
- new Class[] { EODisplayGroup.class, EODataSource.class } );
- if ( selector.implementedByObject( delegate() ) )
- {
- try
- {
- selector.invoke( delegate(), new Object[] { this, dataSource } );
- return result;
- }
- catch ( Exception exc )
- {
- System.err.println( "Error notifying delegate: displayGroupCreateObjectFailed" );
- exc.printStackTrace();
- }
- }
- }
-
- // no delegate or delegate does not implement displayGroupCreateObjectFailed
-
- String message = "Data source could not create new object";
- Object delegateResult = notifyDelegate(
- "displayGroupShouldDisplayAlert",
- new Class[] { EODisplayGroup.class, String.class, String.class },
- new Object[] { this, "Error", message } );
- if ( ( delegateResult == null ) || ( Boolean.TRUE.equals( delegateResult ) ) )
- {
- JOptionPane.showMessageDialog( null, message );
- }
- }
- return result;
- }
-
- /**
- * Inserts the specified object into the list at
- * the specified index.
- */
- public void insertObjectAtIndex ( Object anObject, int anIndex )
- {
- Object result = notifyDelegate(
- "displayGroupShouldInsertObject",
- new Class[] { EODisplayGroup.class, Object.class, int.class },
- new Object[] { this, anObject, new Integer(anIndex) } );
- if ( ( result != null ) && ( Boolean.FALSE.equals( result ) ) )
- {
- return;
- }
-
- contentsChanged = true;
- updatedObjectIndex = anIndex;
- willChange();
-
-
- // add to all objects
- if ( anIndex == displayedObjects.size() )
- {
- allObjects.addObject( anObject );
- }
- else // insert before same object
- {
- Object target = displayedObjects.objectAtIndex( anIndex );
- int targetIndex = indexOf( allObjects, target );
- if ( targetIndex != NSArray.NotFound )
- {
- allObjects.insertObjectAtIndex( anObject, targetIndex );
- }
- else // should never happen
- {
- throw new WotonomyException(
- "Could not find displayed object in all objects list: "
- + target );
- }
- }
-
- // add to displayed objects
- displayedObjects.insertObjectAtIndex( anObject, anIndex );
-
- if ( dataSource != null )
- {
- if ( dataSource instanceof OrderedDataSource )
- {
- ((OrderedDataSource)dataSource).insertObjectAtIndex(
- anObject, anIndex );
- }
- else
- {
- dataSource.insertObject( anObject );
- }
- }
-
- notifyDelegate(
- "displayGroupDidInsertObject",
- new Class[] { EODisplayGroup.class, Object.class },
- new Object[] { this, anObject } );
- }
-
- /**
- * Sets contentsChanged to true and notifies all observers.
- */
- public void redisplay ()
- {
- contentsChanged = true;
- willChange();
- }
-
- /**
- * Sets the selection to the next displayed object after the current
- * selection. If the last object is selected, or if no object
- * is selected, then the first object becomes selected.
- * If multiple items are selected, the first selected item is
- * considered the selected item for the purposes of this method.
- * Does not call redisplay().
- * @return true if an object was selected.
- */
- public boolean selectNext ()
- {
- int count = displayedObjects.count();
- if ( count == 0 ) return false;
- if ( count == 1 )
- {
- selectObject( displayedObjects.objectAtIndex( 0 ) );
- return true;
- }
-
- int i = -1;
- Object selectedObject = selectedObject();
- if ( selectedObject != null )
- {
- i = indexOf( displayedObjects, selectedObject );
- }
- if ( i == NSArray.NotFound ) i = -1;
-
- // select next object
- i++;
- if ( i != displayedObjects.count() )
- {
- // set to next object
- selectedObject = displayedObjects.objectAtIndex( i );
- }
- else // out of range
- {
- // set to null
- selectedObject = displayedObjects.objectAtIndex( 0 );
- }
-
- return selectObject( selectedObject );
- }
-
- /**
- * Sets the selection to the specified object.
- * If the specified object is null or does not exist
- * in the list of displayed objects, the selection
- * will be cleared.
- * @return true if the object was selected.
- */
- public boolean selectObject ( Object anObject )
- {
- if ( ( anObject == null ) ||
- ( indexOf( displayedObjects, anObject )
- == NSArray.NotFound ) )
- {
- clearSelection();
- return false;
- }
-
- selectObjectsIdenticalTo( new NSArray( new Object[] { anObject } ) );
- return true;
- }
-
- /**
- * Sets the selection to the specified objects.
- * If the specified list is null or if none of the objects
- * in the list exist in the list of displayed objects, the
- * selection will be cleared.
- * @return true if all specified objects were selected.
- */
- public boolean selectObjectsIdenticalTo ( List anObjectList )
- {
- // optimization: check for resetting of selection
- if ( ( anObjectList != null ) && ( selectedObjects.size() == anObjectList.size() ) )
- {
- boolean identical = true;
- int size = selectedObjects.size();
- for ( int i = 0; ( i < size ) && identical; i++ )
- {
- // compare by reference
- if ( anObjectList.get( i ) != selectedObjects.get( i ) )
- {
- identical = false;
- }
- else if ( displayedObjects.indexOfIdenticalObject(
- anObjectList.get( i ) ) == NSArray.NotFound )
- {
- identical = false;
- }
- }
- if ( identical )
- {
- return true;
- }
- }
-
- Object result = notifyDelegate(
- "displayGroupShouldChangeSelection",
- new Class[] { EODisplayGroup.class, List.class },
- new Object[] { this, anObjectList } );
- if ( ( result != null ) && ( Boolean.FALSE.equals( result ) ) )
- {
- // need to notify the calling component
- // to revert back to the previous selection
- selectionChanged = true;
- willChange();
- return false;
- }
-
- int i;
- selectionChanged = true;
- willChange();
- Object o;
- selectedObjects.removeAllObjects();
- selectedIndexes.removeAllObjects();
- Iterator it = anObjectList.iterator();
- while ( it.hasNext() )
- {
- o = it.next();
- if ( ( i = displayedObjects.indexOfIdenticalObject( o ) )
- != NSArray.NotFound )
- {
- selectedObjects.addObject( o );
- selectedIndexes.addObject( new Integer( i ) );
- }
- }
-
- notifyDelegate(
- "displayGroupDidChangeSelection",
- new Class[] { EODisplayGroup.class },
- new Object[] { this } );
- notifyDelegate(
- "displayGroupDidChangeSelectedObjects",
- new Class[] { EODisplayGroup.class },
- new Object[] { this } );
-
- return true;
- }
-
- /**
- * Sets the selection to the previous displayed object before the current
- * selection. If the first object is selected, or if no object
- * is selected, then the last object becomes selected.
- * If multiple items are selected, the first selected item is
- * considered the selected item for the purposes of this method.
- * Does not call redisplay().
- * @return true if an object was selected.
- */
- public boolean selectPrevious ()
- {
- int i = displayedObjects.count();
- if ( i == 0 ) return false;
- if ( i == 1 )
- {
- selectObject( displayedObjects.objectAtIndex( 0 ) );
- return true;
- }
-
- Object selectedObject = selectedObject();
- if ( selectedObject != null )
- {
- i = indexOf( displayedObjects, selectedObject );
- }
- if ( i == NSArray.NotFound ) i = displayedObjects.count();
-
- // select next object
- i--;
- if ( i < 0 )
- {
- // out of range - select last object
- i = displayedObjects.count() - 1;
- }
-
- return selectObject( displayedObjects.objectAtIndex( i ) );
- }
-
- /**
- * Returns the currently selected object, or null if
- * there is no selection.
- */
- public Object selectedObject ()
- {
- if ( selectedObjects.count() == 0 )
- {
- return null;
- }
- return selectedObjects.objectAtIndex( 0 );
- }
-
- /**
- * Returns a read-only List containing all selected objects, if any.
- * Returns an empty list if no objects are selected.
- */
- public NSArray selectedObjects ()
- { // System.out.println( "avoided allocation: selectedObjects" );
- return selectedObjectsProxy;
- }
-
- /**
- * Returns a read-only List containing the indexes of all selected
- * objects, if any. The list contains instances of
- * java.lang.Number; call intValue() to retrieve the index.
- */
- public NSArray selectionIndexes ()
- {
+ }
+
+ // remove from displayed objects
+ displayedObjects.removeObjectAtIndex(anIndex);
+ }
+
+ /**
+ * Deletes the currently selected objects. This implementation calls
+ * deleteObjectAtIndex() for each index in the selection list, immediately
+ * returning false if any delete operation fails.
+ *
+ * @return True if all selected objects were deleted, false if any deletion
+ * failed.
+ */
+ public boolean deleteSelection() {
+ int i;
+ boolean result = true;
+
+ Enumeration e = new NSArray(selectedObjects).objectEnumerator();
+ while (e.hasMoreElements()) {
+ i = indexOf(displayedObjects, e.nextElement());
+ if (i == NSArray.NotFound) {
+ // should never happen
+ throw new WotonomyException("Selected object not found in displayedObjects");
+ }
+ result = result && deleteObjectAtIndex(i);
+ }
+
+ return result;
+ }
+
+ /**
+ * Returns a read-only List of all objects in the display group that are
+ * currently displayed by the associations.
+ */
+ public NSArray displayedObjects() { // System.out.println( "avoided allocation: displayedObjects" );
+ return displayedObjectsProxy;
+ }
+
+ /**
+ * Requests a list of objects from the DataSource and calls setObjectArray to
+ * populate the list. More specifically, calls endEditing(), asks the delegate,
+ * fetches the objects, notifies the delegate, and populates the list.
+ */
+ public boolean fetch() {
+ endEditing();
+
+ if (dataSource == null) {
+ return false;
+ }
+
+ Object result = notifyDelegate("displayGroupShouldFetch", new Class[] { EODisplayGroup.class },
+ new Object[] { this });
+ if ((result != null) && (Boolean.FALSE.equals(result))) {
+ return false;
+ }
+
+ NSNotificationCenter.defaultCenter().postNotification(DisplayGroupWillFetchNotification, this,
+ new NSDictionary());
+
+ NSArray objectList = dataSource.fetchObjects();
+
+ notifyDelegate("displayGroupDidFetchObjects", new Class[] { EODisplayGroup.class, List.class },
+ new Object[] { this, objectList });
+
+ if (selectsFirstObjectAfterFetch) {
+ // note: there's a good chance this logic ought to be in master-detail assoc:
+ // we're doing this because changes in the master object trigger a refetch
+ // on the child display group which annoyingly changes the selection.
+ NSArray original = new NSArray(allObjects);
+ setObjectArray(objectList);
+ if (displayedObjects.size() > 0 && !original.equals(allObjects)) // don't change if no change
+ {
+ setSelectionIndexes(new NSArray(new Integer(0)));
+ }
+ } else {
+ setObjectArray(objectList);
+ }
+
+ return true;
+ }
+
+ /**
+ * Creates a new object at the specified index. Calls insertObjectAtIndex() with
+ * the result from sending createObject() to the data source. Presents a
+ * JOptionPane if the create fails, unless the delegate implements
+ * displayGroupCreateObjectFailed.
+ *
+ * @return the newly created object.
+ */
+ public Object insertNewObjectAtIndex(int anIndex) {
+ Object result = null;
+ if (dataSource != null) {
+ result = dataSource.createObject();
+ }
+ if (result != null) {
+ if (insertedObjectDefaultValues != null) {
+ Duplicator.writePropertiesForObject(insertedObjectDefaultValues, result);
+ }
+ insertObjectAtIndex(result, anIndex);
+ } else // create failed
+ {
+ if (delegate() != null) {
+ NSSelector selector = new NSSelector("displayGroupCreateObjectFailed",
+ new Class[] { EODisplayGroup.class, EODataSource.class });
+ if (selector.implementedByObject(delegate())) {
+ try {
+ selector.invoke(delegate(), new Object[] { this, dataSource });
+ return result;
+ } catch (Exception exc) {
+ System.err.println("Error notifying delegate: displayGroupCreateObjectFailed");
+ exc.printStackTrace();
+ }
+ }
+ }
+
+ // no delegate or delegate does not implement displayGroupCreateObjectFailed
+
+ String message = "Data source could not create new object";
+ Object delegateResult = notifyDelegate("displayGroupShouldDisplayAlert",
+ new Class[] { EODisplayGroup.class, String.class, String.class },
+ new Object[] { this, "Error", message });
+ if ((delegateResult == null) || (Boolean.TRUE.equals(delegateResult))) {
+ JOptionPane.showMessageDialog(null, message);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Inserts the specified object into the list at the specified index.
+ */
+ public void insertObjectAtIndex(Object anObject, int anIndex) {
+ Object result = notifyDelegate("displayGroupShouldInsertObject",
+ new Class[] { EODisplayGroup.class, Object.class, int.class },
+ new Object[] { this, anObject, new Integer(anIndex) });
+ if ((result != null) && (Boolean.FALSE.equals(result))) {
+ return;
+ }
+
+ contentsChanged = true;
+ updatedObjectIndex = anIndex;
+ willChange();
+
+ // add to all objects
+ if (anIndex == displayedObjects.size()) {
+ allObjects.addObject(anObject);
+ } else // insert before same object
+ {
+ Object target = displayedObjects.objectAtIndex(anIndex);
+ int targetIndex = indexOf(allObjects, target);
+ if (targetIndex != NSArray.NotFound) {
+ allObjects.insertObjectAtIndex(anObject, targetIndex);
+ } else // should never happen
+ {
+ throw new WotonomyException("Could not find displayed object in all objects list: " + target);
+ }
+ }
+
+ // add to displayed objects
+ displayedObjects.insertObjectAtIndex(anObject, anIndex);
+
+ if (dataSource != null) {
+ if (dataSource instanceof OrderedDataSource) {
+ ((OrderedDataSource) dataSource).insertObjectAtIndex(anObject, anIndex);
+ } else {
+ dataSource.insertObject(anObject);
+ }
+ }
+
+ notifyDelegate("displayGroupDidInsertObject", new Class[] { EODisplayGroup.class, Object.class },
+ new Object[] { this, anObject });
+ }
+
+ /**
+ * Sets contentsChanged to true and notifies all observers.
+ */
+ public void redisplay() {
+ contentsChanged = true;
+ willChange();
+ }
+
+ /**
+ * Sets the selection to the next displayed object after the current selection.
+ * If the last object is selected, or if no object is selected, then the first
+ * object becomes selected. If multiple items are selected, the first selected
+ * item is considered the selected item for the purposes of this method. Does
+ * not call redisplay().
+ *
+ * @return true if an object was selected.
+ */
+ public boolean selectNext() {
+ int count = displayedObjects.count();
+ if (count == 0)
+ return false;
+ if (count == 1) {
+ selectObject(displayedObjects.objectAtIndex(0));
+ return true;
+ }
+
+ int i = -1;
+ Object selectedObject = selectedObject();
+ if (selectedObject != null) {
+ i = indexOf(displayedObjects, selectedObject);
+ }
+ if (i == NSArray.NotFound)
+ i = -1;
+
+ // select next object
+ i++;
+ if (i != displayedObjects.count()) {
+ // set to next object
+ selectedObject = displayedObjects.objectAtIndex(i);
+ } else // out of range
+ {
+ // set to null
+ selectedObject = displayedObjects.objectAtIndex(0);
+ }
+
+ return selectObject(selectedObject);
+ }
+
+ /**
+ * Sets the selection to the specified object. If the specified object is null
+ * or does not exist in the list of displayed objects, the selection will be
+ * cleared.
+ *
+ * @return true if the object was selected.
+ */
+ public boolean selectObject(Object anObject) {
+ if ((anObject == null) || (indexOf(displayedObjects, anObject) == NSArray.NotFound)) {
+ clearSelection();
+ return false;
+ }
+
+ selectObjectsIdenticalTo(new NSArray(new Object[] { anObject }));
+ return true;
+ }
+
+ /**
+ * Sets the selection to the specified objects. If the specified list is null or
+ * if none of the objects in the list exist in the list of displayed objects,
+ * the selection will be cleared.
+ *
+ * @return true if all specified objects were selected.
+ */
+ public boolean selectObjectsIdenticalTo(List anObjectList) {
+ // optimization: check for resetting of selection
+ if ((anObjectList != null) && (selectedObjects.size() == anObjectList.size())) {
+ boolean identical = true;
+ int size = selectedObjects.size();
+ for (int i = 0; (i < size) && identical; i++) {
+ // compare by reference
+ if (anObjectList.get(i) != selectedObjects.get(i)) {
+ identical = false;
+ } else if (displayedObjects.indexOfIdenticalObject(anObjectList.get(i)) == NSArray.NotFound) {
+ identical = false;
+ }
+ }
+ if (identical) {
+ return true;
+ }
+ }
+
+ Object result = notifyDelegate("displayGroupShouldChangeSelection",
+ new Class[] { EODisplayGroup.class, List.class }, new Object[] { this, anObjectList });
+ if ((result != null) && (Boolean.FALSE.equals(result))) {
+ // need to notify the calling component
+ // to revert back to the previous selection
+ selectionChanged = true;
+ willChange();
+ return false;
+ }
+
+ int i;
+ selectionChanged = true;
+ willChange();
+ Object o;
+ selectedObjects.removeAllObjects();
+ selectedIndexes.removeAllObjects();
+ Iterator it = anObjectList.iterator();
+ while (it.hasNext()) {
+ o = it.next();
+ if ((i = displayedObjects.indexOfIdenticalObject(o)) != NSArray.NotFound) {
+ selectedObjects.addObject(o);
+ selectedIndexes.addObject(new Integer(i));
+ }
+ }
+
+ notifyDelegate("displayGroupDidChangeSelection", new Class[] { EODisplayGroup.class }, new Object[] { this });
+ notifyDelegate("displayGroupDidChangeSelectedObjects", new Class[] { EODisplayGroup.class },
+ new Object[] { this });
+
+ return true;
+ }
+
+ /**
+ * Sets the selection to the previous displayed object before the current
+ * selection. If the first object is selected, or if no object is selected, then
+ * the last object becomes selected. If multiple items are selected, the first
+ * selected item is considered the selected item for the purposes of this
+ * method. Does not call redisplay().
+ *
+ * @return true if an object was selected.
+ */
+ public boolean selectPrevious() {
+ int i = displayedObjects.count();
+ if (i == 0)
+ return false;
+ if (i == 1) {
+ selectObject(displayedObjects.objectAtIndex(0));
+ return true;
+ }
+
+ Object selectedObject = selectedObject();
+ if (selectedObject != null) {
+ i = indexOf(displayedObjects, selectedObject);
+ }
+ if (i == NSArray.NotFound)
+ i = displayedObjects.count();
+
+ // select next object
+ i--;
+ if (i < 0) {
+ // out of range - select last object
+ i = displayedObjects.count() - 1;
+ }
+
+ return selectObject(displayedObjects.objectAtIndex(i));
+ }
+
+ /**
+ * Returns the currently selected object, or null if there is no selection.
+ */
+ public Object selectedObject() {
+ if (selectedObjects.count() == 0) {
+ return null;
+ }
+ return selectedObjects.objectAtIndex(0);
+ }
+
+ /**
+ * Returns a read-only List containing all selected objects, if any. Returns an
+ * empty list if no objects are selected.
+ */
+ public NSArray selectedObjects() { // System.out.println( "avoided allocation: selectedObjects" );
+ return selectedObjectsProxy;
+ }
+
+ /**
+ * Returns a read-only List containing the indexes of all selected objects, if
+ * any. The list contains instances of java.lang.Number; call intValue() to
+ * retrieve the index.
+ */
+ public NSArray selectionIndexes() {
// return selectedIndexes;
- int i;
- NSMutableArray result = new NSMutableArray();
- Enumeration e = selectedObjects.objectEnumerator();
- while ( e.hasMoreElements() )
- {
- i = indexOf( displayedObjects, e.nextElement() );
- if ( i != NSArray.NotFound )
- {
- result.addObject( new Integer( i ) );
- }
- else
- {
- System.err.println(
- "Should never happen: selected objects not in displayed objects" );
- new RuntimeException().printStackTrace( System.err );
- }
- }
- return result;
- }
-
- /**
- * Sets the objects managed by this display group.
- * updateDisplayedObjects() is called to filter the
- * display objects. The previous selection will be
- * maintained if possible. The data source is not
- * notified.
- */
- public void setObjectArray ( List anObjectList )
- {
- if ( anObjectList == null ) anObjectList = new NSArray();
-
- Object result = notifyDelegate(
- "displayGroupDisplayArrayForObjects",
- new Class[] { EODisplayGroup.class, List.class },
- new Object[] { this, anObjectList } );
- if ( result != null )
- {
- anObjectList = (List) result;
- }
-
- contentsChanged = true;
- willChange();
-
- NSArray oldSelectedObjects = new NSArray( selectedObjects ); // copy
-
- // reset allObjects to new list
- allObjects.removeAllObjects();
- allObjects.addObjectsFromArray( anObjectList );
-
- // update the displayed object list
- updateDisplayedObjects();
-
- // restore the selection if possible
- selectObjectsIdenticalTo( oldSelectedObjects );
- }
-
- /**
- * Sets the currently selected object, or clears the
- * selection if the object is not found or is null.
- * Note: it's not clear how this differs from
- * selectObject in the spec. It is recommended that
- * you call selectObject for now.
- */
- public void setSelectedObject ( Object anObject )
- {
- selectObject( anObject );
- }
-
- /**
- * Sets the current selection to the specified objects.
- * The previous selection is cleared, and any objects
- * in the display group that are in the specified list
- * are then selected. If no items in the specified list
- * are found in the display group, then the selection is
- * effectively cleared.
- * Note: it's not clear how this differs from
- * selectObjectsIdenticalTo in the spec.
- * It is recommended that you call that method for now.
- */
- public void setSelectedObjects ( List aList )
- {
- selectObjectsIdenticalTo( aList );
- }
-
- /**
- * Sets the current selection to the objects at the
- * specified indexes. Items in the list are assumed
- * to be instances of java.lang.Number.
- * The previous selection is cleared, and any objects
- * in the display group that are in the specified list
- * are then selected. If no items in the specified list
- * are found in the display group, then the selection is
- * effectively cleared.
- */
- public boolean setSelectionIndexes ( List aList )
- {
- Object o;
- int index;
- NSMutableArray objects = new NSMutableArray();
- Iterator it = aList.iterator();
- while ( it.hasNext() )
- {
- index = ((Number)it.next()).intValue();
- if ( index < displayedObjects.count() )
- {
- o = displayedObjects.objectAtIndex( index );
- if ( o != null )
- {
- objects.add( o );
- }
- }
- }
- return selectObjectsIdenticalTo( objects );
- }
-
- /**
- * Applies the qualifier to all objects and sorts
- * the results to update the list of displayed objects.
- * Observing associations are notified to reflect the changes.
- */
- public void updateDisplayedObjects ()
- {
- contentsChanged = true;
- updatedObjectIndex = -1;
- willChange();
-
- displayedObjects.removeAllObjects();
-
- displayedObjects.addObjectsFromArray( allObjects );
-
- // apply qualifier, if any
- if ( qualifier() != null )
- {
- EOQualifier.filterArrayWithQualifier(
- displayedObjects, qualifier() );
- }
-
- // apply sort orderings, if any
- NSArray orderings = sortOrderings();
- if ( orderings != null )
- {
- if ( orderings.count() > 0 )
- {
- selectionChanged = true;
- willChange();
- EOSortOrdering.sortArrayUsingKeyOrderArray(
- displayedObjects, orderings );
- }
- }
-
- // make sure the selectedObjects is a subset of displayedObjects
- int i;
- Object o;
- Iterator it = new LinkedList( selectedObjects ).iterator();
- boolean removeflag = false;
- selectedIndexes.removeAllObjects();
- while ( it.hasNext() )
- {
- o = it.next();
- if ( ( i = displayedObjects.indexOfIdenticalObject( o ) )
- == NSArray.NotFound )
- {
- selectedObjects.removeIdenticalObject( o );
- removeflag = true;
- }
- else
- {
- selectedIndexes.addObject( new Integer( i ) );
- }
- }
-
- //Note: it is important to put the
- //selectionChanged = true line below remove.
- if (removeflag)
- {
- selectionChanged = true;
- willChange();
-
- notifyDelegate(
- "displayGroupDidChangeSelection",
- new Class[] { EODisplayGroup.class },
- new Object[] { this } );
- notifyDelegate(
- "displayGroupDidChangeSelectedObjects",
- new Class[] { EODisplayGroup.class },
- new Object[] { this } );
- }
- }
-
- /**
- * Returns the index of the changed object. If more than
- * one object has changed, -1 is returned.
- */
- public int updatedObjectIndex ()
- {
- return updatedObjectIndex;
- }
-
- // getting and setting values in objects
-
- /**
- * Returns a value on the selected object for the specified key.
- */
- public Object selectedObjectValueForKey ( String aKey )
- {
- Object selectedObject = selectedObject();
- if ( selectedObject == null ) return null;
- return valueForObject( selectedObject, aKey );
- }
-
- /**
- * Sets the specified value for the specified key on
- * all selected objects.
- */
- public boolean setSelectedObjectValue (
- Object aValue, String aKey )
- {
- Object selectedObject = selectedObject();
- if ( selectedObject == null ) return false;
- return setValueForObject( aValue, selectedObject, aKey );
- }
-
- /**
- * Sets the specified value for the specified key on
- * the specified object. Validations may be triggered,
- * and error dialogs may appear to the user.
- * @return True if the value was set successfully,
- * false if the value could not be set and the update
- * operation should not continue.
- */
- public boolean setValueForObject (
- Object aValue, Object anObject, String aKey )
- {
- // notify object's observers:
- // this includes us, and will notify our observers
- EOObserverCenter.notifyObserversObjectWillChange( anObject );
-
- //TODO: if key is null, need to remove old object
- // and add new object instead of simply replacing it.
-
- try
- {
- if ( anObject instanceof EOKeyValueCoding )
- {
- ((EOKeyValueCoding)anObject).takeValueForKey( aValue, aKey );
- }
- else
- {
- EOKeyValueCodingSupport.takeValueForKey( anObject, aValue, aKey );
- }
- }
- catch ( RuntimeException exc )
- {
- Object result = notifyDelegate(
- "displayGroupShouldDisplayAlert",
- new Class[] { EODisplayGroup.class, String.class, String.class },
- new Object[] { this, "Error", exc.getMessage() } );
- if ( ( result == null ) || ( Boolean.TRUE.equals( result ) ) )
- {
- throw exc;
- }
- return false;
- }
-
- notifyDelegate(
- "displayGroupDidSetValueForObject",
- new Class[] { EODisplayGroup.class, Object.class, Object.class, String.class },
- new Object[] { this, aValue, anObject, aKey } );
-
- return true;
- }
-
- /**
- * Calls setValueForObject() for the object at
- * the specified index.
- */
- public boolean setValueForObjectAtIndex (
- Object aValue, int anIndex, String aKey )
- {
- return setValueForObject(
- aValue, displayedObjects.objectAtIndex( anIndex ), aKey );
- }
-
- /**
- * Returns the value for the specified key on the specified object.
- */
- public Object valueForObject ( Object anObject, String aKey )
- {
- // empty string is considered the identity property
- if ( aKey == null ) return anObject;
- if ( aKey.equals( "" ) ) return anObject;
-
- try
- {
- if ( anObject instanceof EOKeyValueCoding )
- {
- return ((EOKeyValueCoding)anObject).valueForKey( aKey );
- }
- else
- {
- return EOKeyValueCodingSupport.valueForKey( anObject, aKey );
- }
- }
- catch ( RuntimeException exc )
- {
- Object result = notifyDelegate(
- "displayGroupShouldDisplayAlert",
- new Class[] { EODisplayGroup.class, String.class, String.class },
- new Object[] { this, "Error", exc.getMessage() } );
- if ( ( result == null ) || ( Boolean.TRUE.equals( result ) ) )
- {
- throw exc;
- }
- return null;
- }
- }
-
- /**
- * Calls valueForObject() for the object at the specified index.
- */
- public Object valueForObjectAtIndex ( int anIndex, String aKey )
- {
- Object o = displayedObjects.objectAtIndex( anIndex );
- return valueForObject( o, aKey );
- }
-
- /**
- * Prints out the list of displayed objects.
- */
- public String toString()
- {
- return displayedObjects.toString();
- }
-
-
- /**
- * Handles notifications from the data source's editing context,
- * looking for InvalidatedAllObjectsInStoreNotification and
- * ObjectsChangedInEditingContextNotification, refetching in
- * the former case and updating displayed objects in the latter.
- * Note: This method is not in the public specification.
- */
- public void objectsInvalidatedInEditingContext( NSNotification aNotification )
- {
- if ( EOObjectStore.InvalidatedAllObjectsInStoreNotification
- .equals( aNotification.name() ) )
- {
- Object result = notifyDelegate(
- "displayGroupShouldRefetch",
- new Class[] { EODisplayGroup.class, NSNotification.class },
- new Object[] { this, aNotification } );
- if ( ( result == null ) || ( Boolean.TRUE.equals( result ) ) )
- {
- fetch();
- }
- }
- else
- if ( EOEditingContext.ObjectsChangedInEditingContextNotification
- .equals( aNotification.name() ) )
- {
- Object result = notifyDelegate(
- "displayGroupShouldRedisplay",
- new Class[] { EODisplayGroup.class, NSNotification.class },
- new Object[] { this, aNotification } );
- if ( ( result == null ) || ( Boolean.TRUE.equals( result ) ) )
- {
- int index;
- Enumeration e;
- boolean didChange = false;
- NSDictionary userInfo = aNotification.userInfo();
-
- // inserts are ignored
-
- // mark updated objects as updated
- NSArray updates = (NSArray) userInfo.objectForKey(
- EOObjectStore.UpdatedKey );
- e = updates.objectEnumerator();
- while ( e.hasMoreElements() )
- {
- index = indexOf( displayedObjects, e.nextElement() );
- if ( index != NSArray.NotFound )
- {
- //System.out.println( "EODisplayGroup: updated: " + index );
- if ( ! didChange )
- {
- didChange = true;
- contentsChanged = true;
- willChange();
- updatedObjectIndex = index;
- }
- else
- {
- updatedObjectIndex = -1;
- }
- }
- }
-
- // treat invalidated objects as updated
- NSArray invalidates = (NSArray) userInfo.objectForKey(
- EOObjectStore.InvalidatedKey );
- e = invalidates.objectEnumerator();
- while ( e.hasMoreElements() )
- {
- index = indexOf( displayedObjects, e.nextElement() );
- if ( index != NSArray.NotFound )
- {
- //System.out.println( "EODisplayGroup: invalidated: " + index );
- if ( ! didChange )
- {
- didChange = true;
- contentsChanged = true;
- willChange();
- updatedObjectIndex = index;
- }
- else
- {
- updatedObjectIndex = -1;
- }
- }
- }
-
- // remove deletes from display group if they exist
- NSArray deletes = (NSArray) userInfo.objectForKey(
- EOObjectStore.DeletedKey );
- e = deletes.objectEnumerator();
- Object o;
- while ( e.hasMoreElements() )
- {
- o = e.nextElement();
- index = indexOf( displayedObjects, o );
- if ( index != NSArray.NotFound )
- {
- //System.out.println( "EODisplayGroup: deleted: " + o );
- deleteObjectAtIndexNoNotify( index );
- }
- }
-
- if ( !usesOptimisticRefresh() )
- {
- updateDisplayedObjects();
- }
- }
- }
-
- }
-
- // static methods
-
- /**
- * Specifies the default behavior for whether changes
- * should be validated immediately for all display groups.
- */
- public static boolean
- globalDefaultForValidatesChangesImmediately ()
- {
- return globalDefaultForValidatesChangesImmediately;
- }
-
- /**
- * Specifies the default string matching format for all
- * display groups.
- */
- public static String globalDefaultStringMatchFormat ()
- {
- return globalDefaultStringMatchFormat;
- }
-
- /**
- * Specifies the default string matching operator for all
- * display groups.
- */
- public static String globalDefaultStringMatchOperator ()
- {
- return globalDefaultStringMatchOperator;
- }
-
- /**
- * Sets the default behavior for validating changes
- * for all display groups.
- */
- public static void
- setGlobalDefaultForValidatesChangesImmediately (
- boolean validatesImmediately )
- {
- globalDefaultForValidatesChangesImmediately =
- validatesImmediately;
- }
-
- /**
- * Sets the default string matching format that
- * will be used by all display groups.
- */
- public static void
- setGlobalDefaultStringMatchFormat ( String aFormat )
- {
- globalDefaultStringMatchFormat = aFormat;
- }
-
- /**
- * Sets the default string matching operator that
- * will be used by all display groups.
- */
- public static void
- setGlobalDefaultStringMatchOperator ( String anOperator )
- {
- globalDefaultStringMatchOperator = anOperator;
- }
-
- /**
- * Needed because we don't inherit from NSObject.
- * Calls EOObserverCenter.notifyObserversObjectWillChange.
- */
- protected void willChange()
- {
- EOObserverCenter.notifyObserversObjectWillChange( this );
- }
-
- /**
- * Called by LastGroupObserver to clear flags.
- */
- protected void processRecentChanges()
- {
- contentsChanged = false;
- selectionChanged = false;
- }
-
- /**
- * Returns the index of the specified object in the
- * specified NSArray, comparing by value or by reference
- * as determined by the private instance variable
- * compareByReference. If not found, returns NSArray.NotFound.
- */
- private int indexOf( NSArray anArray, Object anObject )
- {
- if ( compareByReference )
- {
- return anArray.indexOfIdenticalObject( anObject );
- }
- else
- {
- return anArray.indexOf( anObject );
- }
- }
-
- // interface EOObserving
-
- /**
- * Receives notifications of changes from objects that
- * are managed by this display group. This implementation
- * sets updatedObjectIndex and contentsChanged as appropriate.
- */
- public void objectWillChange(Object anObject)
- {
- int index = indexOf( displayedObjects, anObject );
- if ( index != NSArray.NotFound )
- {
- updatedObjectIndex = index;
- contentsChanged = true;
- willChange();
- }
- }
-
- // interface EOEditingContext.Editor
-
- /**
- * Called before the editing context begins to save changes.
- * This implementation calls endEditing().
- */
- public void editingContextWillSaveChanges(
- EOEditingContext anEditingContext )
- {
- endEditing();
- }
-
- /**
- * Called to determine whether this editor has changes
- * that have not been committed to the object in the context.
- */
- public boolean editorHasChangesForEditingContext(
- EOEditingContext anEditingContext )
- {
- return ( editingAssociation() != null );
- }
-
- // interface EOEditingContext.MessageHandler
-
- /**
- * Called to display a message for an error that occurred
- * in the specified editing context. If the delegate allows,
- * this implementation presents an informational JOptionPane.
- * Override to customize.
- */
- public void editingContextPresentErrorMessage(
- EOEditingContext anEditingContext,
- String aMessage )
- {
- Object result = notifyDelegate(
- "displayGroupShouldDisplayAlert",
- new Class[] { EODisplayGroup.class, String.class, String.class },
- new Object[] { this, "Error", aMessage } );
- if ( ( result == null ) || ( Boolean.TRUE.equals( result ) ) )
- {
- JOptionPane.showMessageDialog( null, aMessage );
- }
- }
-
- /**
- * Called by the specified object store to determine whether
- * fetching should continue, where count is the current count
- * and limit is the limit as specified by the fetch specification.
- * This implementation presents an JOptionPane allowing the user
- * to specify whether to continue. Override to customize.
- */
- public boolean editingContextShouldContinueFetching(
- EOEditingContext anEditingContext,
- int count,
- int limit,
- EOObjectStore anObjectStore )
- {
- return ( JOptionPane.showConfirmDialog( null,
- "Fetch limit reached: do you wish to continue?",
- "Continue?",
- JOptionPane.YES_NO_OPTION ) == JOptionPane.YES_OPTION );
- }
-
- /**
- * Sends the specified message to the delegate.
- * Returns the return value of the method,
- * or null if no return value or no delegate
- * or no implementation.
- */
- private Object notifyDelegate(
- String aMethodName, Class[] types, Object[] params )
- {
- try
- {
- Object delegate = delegate();
- if ( delegate == null ) return null;
- return NSSelector.invoke(
- aMethodName, types, delegate, params );
- }
- catch ( NoSuchMethodException e )
- {
- // ignore: not implemented
- }
- catch ( Exception exc )
- {
- // log to standard error
- System.err.println(
- "Error while messaging delegate: " +
- delegate + " : " + aMethodName );
- exc.printStackTrace();
- }
-
- return null;
- }
-
- /**
- * DisplayGroups can delegate important decisions to a Delegate.
- * Note that DisplayGroup doesn't require its delegates to implement
- * this interface: rather, this interface defines the methods that
- * DisplayGroup will attempt to invoke dynamically on its delegate.
- * The delegate may choose to implement only a subset of the methods
- * on the interface.
- */
- public interface Delegate
- {
- /**
- * Called when the specified data source fails
- * to create an object for the specified display group.
- */
- void displayGroupCreateObjectFailed (
- EODisplayGroup aDisplayGroup,
- EODataSource aDataSource );
-
- /**
- * Called after the specified display group's
- * data source is changed.
- */
- void displayGroupDidChangeDataSource (
- EODisplayGroup aDisplayGroup );
-
- /**
- * Called after a change occurs in the specified
- * display group's selected objects.
- */
- void displayGroupDidChangeSelectedObjects (
- EODisplayGroup aDisplayGroup );
-
- /**
- * Called after the specified display group's
- * selection has changed.
- */
- void displayGroupDidChangeSelection (
- EODisplayGroup aDisplayGroup );
-
- /**
- * Called after the specified display group has
- * deleted the specified object.
- */
- void displayGroupDidDeleteObject (
- EODisplayGroup aDisplayGroup,
- Object anObject );
-
- /**
- * Called after the specified display group
- * has fetched the specified object list.
- */
- void displayGroupDidFetchObjects (
- EODisplayGroup aDisplayGroup,
- List anObjectList );
-
- /**
- * Called after the specified display group
- * has inserted the specified object into
- * its internal object list.
- */
- void displayGroupDidInsertObject (
- EODisplayGroup aDisplayGroup,
- Object anObject );
-
- /**
- * Called after the specified display group
- * has set the specified value for the specified
- * object and key.
- */
- void displayGroupDidSetValueForObject (
- EODisplayGroup aDisplayGroup,
- Object aValue,
- Object anObject,
- String aKey );
-
- /**
- * Called by the specified display group to
- * determine what objects should be displayed
- * for the objects in the specified list.
- * @return An NSArray containing the objects
- * to be displayed for the objects in the
- * specified list.
- */
- NSArray displayGroupDisplayArrayForObjects (
- EODisplayGroup aDisplayGroup,
- List aList );
-
- /**
- * Called by the specified display group before
- * it attempts to change the selection.
- * @return True to allow the selection to change,
- * false otherwise.
- */
- boolean displayGroupShouldChangeSelection (
- EODisplayGroup aDisplayGroup,
- List aSelectionList );
-
- /**
- * Called by the specified display group before
- * it attempts to delete the specified object.
- * @return True to allow the object to be deleted
- * false to prevent the deletion.
- */
- boolean displayGroupShouldDeleteObject (
- EODisplayGroup aDisplayGroup,
- Object anObject );
-
- /**
- * Called by the specified display group before
- * it attempts display the specified alert to
- * the user.
- * @return True to allow the message to be
- * displayed, false if you want to handle the
- * alert yourself and suppress the display group's
- * notification.
- */
- boolean displayGroupShouldDisplayAlert (
- EODisplayGroup aDisplayGroup,
- String aTitle,
- String aMessage );
-
- /**
- * Called by the specified display group before
- * it attempts fetch objects.
- * @return True to allow the fetch to take place,
- * false to prevent the fetch.
- */
- boolean displayGroupShouldFetch (
- EODisplayGroup aDisplayGroup );
-
- /**
- * Called by the specified display group before
- * it attempts to insert the specified object.
- * @return True to allow the object to be inserted
- * false to prevent the insertion.
- */
- boolean displayGroupShouldInsertObject (
- EODisplayGroup aDisplayGroup,
- Object anObject,
- int anIndex );
-
- /**
- * Called by the specified display group when
- * it receives the specified
- * ObjectsChangedInEditingContextNotification.
- * @return True to allow the display group to
- * update the display (recommended), false
- * to prevent the update.
- */
- boolean displayGroupShouldRedisplay (
- EODisplayGroup aDisplayGroup,
- NSNotification aNotification );
-
- /**
- * Called by the specified display group when
- * it receives the specified
- * InvalidatedAllObjectsInStoreNotification.
- * @return True to allow the display group to
- * refetch (recommended), false to prevent the
- * refetch.
- */
- boolean displayGroupShouldRefetch (
- EODisplayGroup aDisplayGroup,
- NSNotification aNotification );
-
- }
+ int i;
+ NSMutableArray result = new NSMutableArray();
+ Enumeration e = selectedObjects.objectEnumerator();
+ while (e.hasMoreElements()) {
+ i = indexOf(displayedObjects, e.nextElement());
+ if (i != NSArray.NotFound) {
+ result.addObject(new Integer(i));
+ } else {
+ System.err.println("Should never happen: selected objects not in displayed objects");
+ new RuntimeException().printStackTrace(System.err);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Sets the objects managed by this display group. updateDisplayedObjects() is
+ * called to filter the display objects. The previous selection will be
+ * maintained if possible. The data source is not notified.
+ */
+ public void setObjectArray(List anObjectList) {
+ if (anObjectList == null)
+ anObjectList = new NSArray();
+
+ Object result = notifyDelegate("displayGroupDisplayArrayForObjects",
+ new Class[] { EODisplayGroup.class, List.class }, new Object[] { this, anObjectList });
+ if (result != null) {
+ anObjectList = (List) result;
+ }
+
+ contentsChanged = true;
+ willChange();
+
+ NSArray oldSelectedObjects = new NSArray(selectedObjects); // copy
+
+ // reset allObjects to new list
+ allObjects.removeAllObjects();
+ allObjects.addObjectsFromArray(anObjectList);
+
+ // update the displayed object list
+ updateDisplayedObjects();
+
+ // restore the selection if possible
+ selectObjectsIdenticalTo(oldSelectedObjects);
+ }
+
+ /**
+ * Sets the currently selected object, or clears the selection if the object is
+ * not found or is null. Note: it's not clear how this differs from selectObject
+ * in the spec. It is recommended that you call selectObject for now.
+ */
+ public void setSelectedObject(Object anObject) {
+ selectObject(anObject);
+ }
+
+ /**
+ * Sets the current selection to the specified objects. The previous selection
+ * is cleared, and any objects in the display group that are in the specified
+ * list are then selected. If no items in the specified list are found in the
+ * display group, then the selection is effectively cleared. Note: it's not
+ * clear how this differs from selectObjectsIdenticalTo in the spec. It is
+ * recommended that you call that method for now.
+ */
+ public void setSelectedObjects(List aList) {
+ selectObjectsIdenticalTo(aList);
+ }
+
+ /**
+ * Sets the current selection to the objects at the specified indexes. Items in
+ * the list are assumed to be instances of java.lang.Number. The previous
+ * selection is cleared, and any objects in the display group that are in the
+ * specified list are then selected. If no items in the specified list are found
+ * in the display group, then the selection is effectively cleared.
+ */
+ public boolean setSelectionIndexes(List aList) {
+ Object o;
+ int index;
+ NSMutableArray objects = new NSMutableArray();
+ Iterator it = aList.iterator();
+ while (it.hasNext()) {
+ index = ((Number) it.next()).intValue();
+ if (index < displayedObjects.count()) {
+ o = displayedObjects.objectAtIndex(index);
+ if (o != null) {
+ objects.add(o);
+ }
+ }
+ }
+ return selectObjectsIdenticalTo(objects);
+ }
+
+ /**
+ * Applies the qualifier to all objects and sorts the results to update the list
+ * of displayed objects. Observing associations are notified to reflect the
+ * changes.
+ */
+ public void updateDisplayedObjects() {
+ contentsChanged = true;
+ updatedObjectIndex = -1;
+ willChange();
+
+ displayedObjects.removeAllObjects();
+
+ displayedObjects.addObjectsFromArray(allObjects);
+
+ // apply qualifier, if any
+ if (qualifier() != null) {
+ EOQualifier.filterArrayWithQualifier(displayedObjects, qualifier());
+ }
+
+ // apply sort orderings, if any
+ NSArray orderings = sortOrderings();
+ if (orderings != null) {
+ if (orderings.count() > 0) {
+ selectionChanged = true;
+ willChange();
+ EOSortOrdering.sortArrayUsingKeyOrderArray(displayedObjects, orderings);
+ }
+ }
+
+ // make sure the selectedObjects is a subset of displayedObjects
+ int i;
+ Object o;
+ Iterator it = new LinkedList(selectedObjects).iterator();
+ boolean removeflag = false;
+ selectedIndexes.removeAllObjects();
+ while (it.hasNext()) {
+ o = it.next();
+ if ((i = displayedObjects.indexOfIdenticalObject(o)) == NSArray.NotFound) {
+ selectedObjects.removeIdenticalObject(o);
+ removeflag = true;
+ } else {
+ selectedIndexes.addObject(new Integer(i));
+ }
+ }
+
+ // Note: it is important to put the
+ // selectionChanged = true line below remove.
+ if (removeflag) {
+ selectionChanged = true;
+ willChange();
+
+ notifyDelegate("displayGroupDidChangeSelection", new Class[] { EODisplayGroup.class },
+ new Object[] { this });
+ notifyDelegate("displayGroupDidChangeSelectedObjects", new Class[] { EODisplayGroup.class },
+ new Object[] { this });
+ }
+ }
+
+ /**
+ * Returns the index of the changed object. If more than one object has changed,
+ * -1 is returned.
+ */
+ public int updatedObjectIndex() {
+ return updatedObjectIndex;
+ }
+
+ // getting and setting values in objects
+
+ /**
+ * Returns a value on the selected object for the specified key.
+ */
+ public Object selectedObjectValueForKey(String aKey) {
+ Object selectedObject = selectedObject();
+ if (selectedObject == null)
+ return null;
+ return valueForObject(selectedObject, aKey);
+ }
+
+ /**
+ * Sets the specified value for the specified key on all selected objects.
+ */
+ public boolean setSelectedObjectValue(Object aValue, String aKey) {
+ Object selectedObject = selectedObject();
+ if (selectedObject == null)
+ return false;
+ return setValueForObject(aValue, selectedObject, aKey);
+ }
+
+ /**
+ * Sets the specified value for the specified key on the specified object.
+ * Validations may be triggered, and error dialogs may appear to the user.
+ *
+ * @return True if the value was set successfully, false if the value could not
+ * be set and the update operation should not continue.
+ */
+ public boolean setValueForObject(Object aValue, Object anObject, String aKey) {
+ // notify object's observers:
+ // this includes us, and will notify our observers
+ EOObserverCenter.notifyObserversObjectWillChange(anObject);
+
+ // TODO: if key is null, need to remove old object
+ // and add new object instead of simply replacing it.
+
+ try {
+ if (anObject instanceof EOKeyValueCoding) {
+ ((EOKeyValueCoding) anObject).takeValueForKey(aValue, aKey);
+ } else {
+ EOKeyValueCodingSupport.takeValueForKey(anObject, aValue, aKey);
+ }
+ } catch (RuntimeException exc) {
+ Object result = notifyDelegate("displayGroupShouldDisplayAlert",
+ new Class[] { EODisplayGroup.class, String.class, String.class },
+ new Object[] { this, "Error", exc.getMessage() });
+ if ((result == null) || (Boolean.TRUE.equals(result))) {
+ throw exc;
+ }
+ return false;
+ }
+
+ notifyDelegate("displayGroupDidSetValueForObject",
+ new Class[] { EODisplayGroup.class, Object.class, Object.class, String.class },
+ new Object[] { this, aValue, anObject, aKey });
+
+ return true;
+ }
+
+ /**
+ * Calls setValueForObject() for the object at the specified index.
+ */
+ public boolean setValueForObjectAtIndex(Object aValue, int anIndex, String aKey) {
+ return setValueForObject(aValue, displayedObjects.objectAtIndex(anIndex), aKey);
+ }
+
+ /**
+ * Returns the value for the specified key on the specified object.
+ */
+ public Object valueForObject(Object anObject, String aKey) {
+ // empty string is considered the identity property
+ if (aKey == null)
+ return anObject;
+ if (aKey.equals(""))
+ return anObject;
+
+ try {
+ if (anObject instanceof EOKeyValueCoding) {
+ return ((EOKeyValueCoding) anObject).valueForKey(aKey);
+ } else {
+ return EOKeyValueCodingSupport.valueForKey(anObject, aKey);
+ }
+ } catch (RuntimeException exc) {
+ Object result = notifyDelegate("displayGroupShouldDisplayAlert",
+ new Class[] { EODisplayGroup.class, String.class, String.class },
+ new Object[] { this, "Error", exc.getMessage() });
+ if ((result == null) || (Boolean.TRUE.equals(result))) {
+ throw exc;
+ }
+ return null;
+ }
+ }
+
+ /**
+ * Calls valueForObject() for the object at the specified index.
+ */
+ public Object valueForObjectAtIndex(int anIndex, String aKey) {
+ Object o = displayedObjects.objectAtIndex(anIndex);
+ return valueForObject(o, aKey);
+ }
+
+ /**
+ * Prints out the list of displayed objects.
+ */
+ public String toString() {
+ return displayedObjects.toString();
+ }
+
+ /**
+ * Handles notifications from the data source's editing context, looking for
+ * InvalidatedAllObjectsInStoreNotification and
+ * ObjectsChangedInEditingContextNotification, refetching in the former case and
+ * updating displayed objects in the latter. Note: This method is not in the
+ * public specification.
+ */
+ public void objectsInvalidatedInEditingContext(NSNotification aNotification) {
+ if (EOObjectStore.InvalidatedAllObjectsInStoreNotification.equals(aNotification.name())) {
+ Object result = notifyDelegate("displayGroupShouldRefetch",
+ new Class[] { EODisplayGroup.class, NSNotification.class }, new Object[] { this, aNotification });
+ if ((result == null) || (Boolean.TRUE.equals(result))) {
+ fetch();
+ }
+ } else if (EOEditingContext.ObjectsChangedInEditingContextNotification.equals(aNotification.name())) {
+ Object result = notifyDelegate("displayGroupShouldRedisplay",
+ new Class[] { EODisplayGroup.class, NSNotification.class }, new Object[] { this, aNotification });
+ if ((result == null) || (Boolean.TRUE.equals(result))) {
+ int index;
+ Enumeration e;
+ boolean didChange = false;
+ NSDictionary userInfo = aNotification.userInfo();
+
+ // inserts are ignored
+
+ // mark updated objects as updated
+ NSArray updates = (NSArray) userInfo.objectForKey(EOObjectStore.UpdatedKey);
+ e = updates.objectEnumerator();
+ while (e.hasMoreElements()) {
+ index = indexOf(displayedObjects, e.nextElement());
+ if (index != NSArray.NotFound) {
+ // System.out.println( "EODisplayGroup: updated: " + index );
+ if (!didChange) {
+ didChange = true;
+ contentsChanged = true;
+ willChange();
+ updatedObjectIndex = index;
+ } else {
+ updatedObjectIndex = -1;
+ }
+ }
+ }
+
+ // treat invalidated objects as updated
+ NSArray invalidates = (NSArray) userInfo.objectForKey(EOObjectStore.InvalidatedKey);
+ e = invalidates.objectEnumerator();
+ while (e.hasMoreElements()) {
+ index = indexOf(displayedObjects, e.nextElement());
+ if (index != NSArray.NotFound) {
+ // System.out.println( "EODisplayGroup: invalidated: " + index );
+ if (!didChange) {
+ didChange = true;
+ contentsChanged = true;
+ willChange();
+ updatedObjectIndex = index;
+ } else {
+ updatedObjectIndex = -1;
+ }
+ }
+ }
+
+ // remove deletes from display group if they exist
+ NSArray deletes = (NSArray) userInfo.objectForKey(EOObjectStore.DeletedKey);
+ e = deletes.objectEnumerator();
+ Object o;
+ while (e.hasMoreElements()) {
+ o = e.nextElement();
+ index = indexOf(displayedObjects, o);
+ if (index != NSArray.NotFound) {
+ // System.out.println( "EODisplayGroup: deleted: " + o );
+ deleteObjectAtIndexNoNotify(index);
+ }
+ }
+
+ if (!usesOptimisticRefresh()) {
+ updateDisplayedObjects();
+ }
+ }
+ }
+
+ }
+
+ // static methods
+
+ /**
+ * Specifies the default behavior for whether changes should be validated
+ * immediately for all display groups.
+ */
+ public static boolean globalDefaultForValidatesChangesImmediately() {
+ return globalDefaultForValidatesChangesImmediately;
+ }
+
+ /**
+ * Specifies the default string matching format for all display groups.
+ */
+ public static String globalDefaultStringMatchFormat() {
+ return globalDefaultStringMatchFormat;
+ }
+
+ /**
+ * Specifies the default string matching operator for all display groups.
+ */
+ public static String globalDefaultStringMatchOperator() {
+ return globalDefaultStringMatchOperator;
+ }
+
+ /**
+ * Sets the default behavior for validating changes for all display groups.
+ */
+ public static void setGlobalDefaultForValidatesChangesImmediately(boolean validatesImmediately) {
+ globalDefaultForValidatesChangesImmediately = validatesImmediately;
+ }
+
+ /**
+ * Sets the default string matching format that will be used by all display
+ * groups.
+ */
+ public static void setGlobalDefaultStringMatchFormat(String aFormat) {
+ globalDefaultStringMatchFormat = aFormat;
+ }
+
+ /**
+ * Sets the default string matching operator that will be used by all display
+ * groups.
+ */
+ public static void setGlobalDefaultStringMatchOperator(String anOperator) {
+ globalDefaultStringMatchOperator = anOperator;
+ }
+
+ /**
+ * Needed because we don't inherit from NSObject. Calls
+ * EOObserverCenter.notifyObserversObjectWillChange.
+ */
+ protected void willChange() {
+ EOObserverCenter.notifyObserversObjectWillChange(this);
+ }
+
+ /**
+ * Called by LastGroupObserver to clear flags.
+ */
+ protected void processRecentChanges() {
+ contentsChanged = false;
+ selectionChanged = false;
+ }
+
+ /**
+ * Returns the index of the specified object in the specified NSArray, comparing
+ * by value or by reference as determined by the private instance variable
+ * compareByReference. If not found, returns NSArray.NotFound.
+ */
+ private int indexOf(NSArray anArray, Object anObject) {
+ if (compareByReference) {
+ return anArray.indexOfIdenticalObject(anObject);
+ } else {
+ return anArray.indexOf(anObject);
+ }
+ }
+
+ // interface EOObserving
+
+ /**
+ * Receives notifications of changes from objects that are managed by this
+ * display group. This implementation sets updatedObjectIndex and
+ * contentsChanged as appropriate.
+ */
+ public void objectWillChange(Object anObject) {
+ int index = indexOf(displayedObjects, anObject);
+ if (index != NSArray.NotFound) {
+ updatedObjectIndex = index;
+ contentsChanged = true;
+ willChange();
+ }
+ }
+
+ // interface EOEditingContext.Editor
+
+ /**
+ * Called before the editing context begins to save changes. This implementation
+ * calls endEditing().
+ */
+ public void editingContextWillSaveChanges(EOEditingContext anEditingContext) {
+ endEditing();
+ }
+
+ /**
+ * Called to determine whether this editor has changes that have not been
+ * committed to the object in the context.
+ */
+ public boolean editorHasChangesForEditingContext(EOEditingContext anEditingContext) {
+ return (editingAssociation() != null);
+ }
+
+ // interface EOEditingContext.MessageHandler
+
+ /**
+ * Called to display a message for an error that occurred in the specified
+ * editing context. If the delegate allows, this implementation presents an
+ * informational JOptionPane. Override to customize.
+ */
+ public void editingContextPresentErrorMessage(EOEditingContext anEditingContext, String aMessage) {
+ Object result = notifyDelegate("displayGroupShouldDisplayAlert",
+ new Class[] { EODisplayGroup.class, String.class, String.class },
+ new Object[] { this, "Error", aMessage });
+ if ((result == null) || (Boolean.TRUE.equals(result))) {
+ JOptionPane.showMessageDialog(null, aMessage);
+ }
+ }
+
+ /**
+ * Called by the specified object store to determine whether fetching should
+ * continue, where count is the current count and limit is the limit as
+ * specified by the fetch specification. This implementation presents an
+ * JOptionPane allowing the user to specify whether to continue. Override to
+ * customize.
+ */
+ public boolean editingContextShouldContinueFetching(EOEditingContext anEditingContext, int count, int limit,
+ EOObjectStore anObjectStore) {
+ return (JOptionPane.showConfirmDialog(null, "Fetch limit reached: do you wish to continue?", "Continue?",
+ JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION);
+ }
+
+ /**
+ * Sends the specified message to the delegate. Returns the return value of the
+ * method, or null if no return value or no delegate or no implementation.
+ */
+ private Object notifyDelegate(String aMethodName, Class[] types, Object[] params) {
+ try {
+ Object delegate = delegate();
+ if (delegate == null)
+ return null;
+ return NSSelector.invoke(aMethodName, types, delegate, params);
+ } catch (NoSuchMethodException e) {
+ // ignore: not implemented
+ } catch (Exception exc) {
+ // log to standard error
+ System.err.println("Error while messaging delegate: " + delegate + " : " + aMethodName);
+ exc.printStackTrace();
+ }
+
+ return null;
+ }
+
+ /**
+ * DisplayGroups can delegate important decisions to a Delegate. Note that
+ * DisplayGroup doesn't require its delegates to implement this interface:
+ * rather, this interface defines the methods that DisplayGroup will attempt to
+ * invoke dynamically on its delegate. The delegate may choose to implement only
+ * a subset of the methods on the interface.
+ */
+ public interface Delegate {
+ /**
+ * Called when the specified data source fails to create an object for the
+ * specified display group.
+ */
+ void displayGroupCreateObjectFailed(EODisplayGroup aDisplayGroup, EODataSource aDataSource);
+
+ /**
+ * Called after the specified display group's data source is changed.
+ */
+ void displayGroupDidChangeDataSource(EODisplayGroup aDisplayGroup);
+
+ /**
+ * Called after a change occurs in the specified display group's selected
+ * objects.
+ */
+ void displayGroupDidChangeSelectedObjects(EODisplayGroup aDisplayGroup);
+
+ /**
+ * Called after the specified display group's selection has changed.
+ */
+ void displayGroupDidChangeSelection(EODisplayGroup aDisplayGroup);
+
+ /**
+ * Called after the specified display group has deleted the specified object.
+ */
+ void displayGroupDidDeleteObject(EODisplayGroup aDisplayGroup, Object anObject);
+
+ /**
+ * Called after the specified display group has fetched the specified object
+ * list.
+ */
+ void displayGroupDidFetchObjects(EODisplayGroup aDisplayGroup, List anObjectList);
+
+ /**
+ * Called after the specified display group has inserted the specified object
+ * into its internal object list.
+ */
+ void displayGroupDidInsertObject(EODisplayGroup aDisplayGroup, Object anObject);
+
+ /**
+ * Called after the specified display group has set the specified value for the
+ * specified object and key.
+ */
+ void displayGroupDidSetValueForObject(EODisplayGroup aDisplayGroup, Object aValue, Object anObject,
+ String aKey);
+
+ /**
+ * Called by the specified display group to determine what objects should be
+ * displayed for the objects in the specified list.
+ *
+ * @return An NSArray containing the objects to be displayed for the objects in
+ * the specified list.
+ */
+ NSArray displayGroupDisplayArrayForObjects(EODisplayGroup aDisplayGroup, List aList);
+
+ /**
+ * Called by the specified display group before it attempts to change the
+ * selection.
+ *
+ * @return True to allow the selection to change, false otherwise.
+ */
+ boolean displayGroupShouldChangeSelection(EODisplayGroup aDisplayGroup, List aSelectionList);
+
+ /**
+ * Called by the specified display group before it attempts to delete the
+ * specified object.
+ *
+ * @return True to allow the object to be deleted false to prevent the deletion.
+ */
+ boolean displayGroupShouldDeleteObject(EODisplayGroup aDisplayGroup, Object anObject);
+
+ /**
+ * Called by the specified display group before it attempts display the
+ * specified alert to the user.
+ *
+ * @return True to allow the message to be displayed, false if you want to
+ * handle the alert yourself and suppress the display group's
+ * notification.
+ */
+ boolean displayGroupShouldDisplayAlert(EODisplayGroup aDisplayGroup, String aTitle, String aMessage);
+
+ /**
+ * Called by the specified display group before it attempts fetch objects.
+ *
+ * @return True to allow the fetch to take place, false to prevent the fetch.
+ */
+ boolean displayGroupShouldFetch(EODisplayGroup aDisplayGroup);
+
+ /**
+ * Called by the specified display group before it attempts to insert the
+ * specified object.
+ *
+ * @return True to allow the object to be inserted false to prevent the
+ * insertion.
+ */
+ boolean displayGroupShouldInsertObject(EODisplayGroup aDisplayGroup, Object anObject, int anIndex);
+
+ /**
+ * Called by the specified display group when it receives the specified
+ * ObjectsChangedInEditingContextNotification.
+ *
+ * @return True to allow the display group to update the display (recommended),
+ * false to prevent the update.
+ */
+ boolean displayGroupShouldRedisplay(EODisplayGroup aDisplayGroup, NSNotification aNotification);
+
+ /**
+ * Called by the specified display group when it receives the specified
+ * InvalidatedAllObjectsInStoreNotification.
+ *
+ * @return True to allow the display group to refetch (recommended), false to
+ * prevent the refetch.
+ */
+ boolean displayGroupShouldRefetch(EODisplayGroup aDisplayGroup, NSNotification aNotification);
+
+ }
}
- /**
- * A private class that will serve to clear the contentsChanged
- * and selectionChanged flags after all Associations have been
- * notified.
- */
- class LastGroupObserver extends EODelayedObserver
- {
- Reference ref;
-
- public LastGroupObserver( EODisplayGroup aDisplayGroup )
- {
- ref = new WeakReference( aDisplayGroup );
- }
-
- /**
- * We want to be informed last, after all Associations
- * have been notified to changes in the DisplayGroup.
- */
- public int priority()
- {
- return ObserverPrioritySixth;
- }
-
- /**
- * After all Associations have been notified,
- * clear the contentsChanged and selectionChanged flags.
- */
- public void subjectChanged ()
- {
- EODisplayGroup group = (EODisplayGroup) ref.get();
- if ( group != null )
- {
- group.processRecentChanges();
- }
- }
- }
+/**
+ * A private class that will serve to clear the contentsChanged and
+ * selectionChanged flags after all Associations have been notified.
+ */
+class LastGroupObserver extends EODelayedObserver {
+ Reference ref;
+
+ public LastGroupObserver(EODisplayGroup aDisplayGroup) {
+ ref = new WeakReference(aDisplayGroup);
+ }
+
+ /**
+ * We want to be informed last, after all Associations have been notified to
+ * changes in the DisplayGroup.
+ */
+ public int priority() {
+ return ObserverPrioritySixth;
+ }
+
+ /**
+ * After all Associations have been notified, clear the contentsChanged and
+ * selectionChanged flags.
+ */
+ public void subjectChanged() {
+ EODisplayGroup group = (EODisplayGroup) ref.get();
+ if (group != null) {
+ group.processRecentChanges();
+ }
+ }
+}
/*
- * $Log$
- * Revision 1.2 2006/02/18 23:14:35 cgruber
- * Update imports and maven dependencies.
+ * $Log$ Revision 1.2 2006/02/18 23:14:35 cgruber Update imports and maven
+ * dependencies.
*
- * Revision 1.1 2006/02/16 13:22:22 cgruber
- * Check in all sources in eclipse-friendly maven-enabled packages.
+ * Revision 1.1 2006/02/16 13:22:22 cgruber Check in all sources in
+ * eclipse-friendly maven-enabled packages.
*
- * Revision 1.51 2004/01/28 18:35:40 mpowers
- * Slight optimization: only comparing list in fetch if we have to.
+ * Revision 1.51 2004/01/28 18:35:40 mpowers Slight optimization: only comparing
+ * list in fetch if we have to.
*
- * Revision 1.50 2004/01/27 20:42:30 mpowers
- * No longer reselecting first after fetch if contents are identical.
+ * Revision 1.50 2004/01/27 20:42:30 mpowers No longer reselecting first after
+ * fetch if contents are identical.
*
- * Revision 1.49 2003/12/18 11:37:45 mpowers
- * Now calling qualifier internally.
+ * Revision 1.49 2003/12/18 11:37:45 mpowers Now calling qualifier internally.
*
- * Revision 1.48 2003/08/06 23:07:52 chochos
- * general code cleanup (mostly, removing unused imports)
+ * Revision 1.48 2003/08/06 23:07:52 chochos general code cleanup (mostly,
+ * removing unused imports)
*
- * Revision 1.47 2003/01/18 23:30:42 mpowers
- * WODisplayGroup now compiles.
+ * Revision 1.47 2003/01/18 23:30:42 mpowers WODisplayGroup now compiles.
*
- * Revision 1.46 2002/10/24 21:15:36 mpowers
- * New implementations of NSArray and subclasses.
+ * Revision 1.46 2002/10/24 21:15:36 mpowers New implementations of NSArray and
+ * subclasses.
*
- * Revision 1.45 2002/10/24 18:20:20 mpowers
- * Because NSArray is read-only, we are returning our internal representations
- * to callers of allObjects(), displayedObjects(), and selectedObjects().
+ * Revision 1.45 2002/10/24 18:20:20 mpowers Because NSArray is read-only, we
+ * are returning our internal representations to callers of allObjects(),
+ * displayedObjects(), and selectedObjects().
*
- * Revision 1.44 2002/08/06 18:20:25 mpowers
- * Now posting DisplayGroupWillFetch notifications before fetch.
- * Implemented support for usesOptimisticRefresh.
- * No longer supporting inserted/updated/deleted lists: not part of spec.
+ * Revision 1.44 2002/08/06 18:20:25 mpowers Now posting DisplayGroupWillFetch
+ * notifications before fetch. Implemented support for usesOptimisticRefresh. No
+ * longer supporting inserted/updated/deleted lists: not part of spec.
*
- * Revision 1.43 2002/05/17 15:01:49 mpowers
- * Implemented dynamic lookup of delegate methods so delegates no longer
- * need to implement the DisplayGroup.Delegate interface.
+ * Revision 1.43 2002/05/17 15:01:49 mpowers Implemented dynamic lookup of
+ * delegate methods so delegates no longer need to implement the
+ * DisplayGroup.Delegate interface.
*
- * Revision 1.42 2002/03/26 21:46:06 mpowers
- * Contributing EditingContext as a java-friendly convenience.
+ * Revision 1.42 2002/03/26 21:46:06 mpowers Contributing EditingContext as a
+ * java-friendly convenience.
*
- * Revision 1.41 2002/03/11 03:17:56 mpowers
- * Provided control point for coalesced changes.
+ * Revision 1.41 2002/03/11 03:17:56 mpowers Provided control point for
+ * coalesced changes.
*
- * Revision 1.40 2002/03/05 23:18:28 mpowers
- * Added documentation.
- * Added isSelectionPaintedImmediate and isSelectionTracking attributes
- * to TableAssociation.
- * Added getTableAssociation to TableColumnAssociation.
+ * Revision 1.40 2002/03/05 23:18:28 mpowers Added documentation. Added
+ * isSelectionPaintedImmediate and isSelectionTracking attributes to
+ * TableAssociation. Added getTableAssociation to TableColumnAssociation.
*
- * Revision 1.39 2002/02/19 22:26:04 mpowers
- * Implemented EOEditingContext.MessageHandler support.
+ * Revision 1.39 2002/02/19 22:26:04 mpowers Implemented
+ * EOEditingContext.MessageHandler support.
*
- * Revision 1.38 2002/02/19 16:37:38 mpowers
- * Implemented support for EOEditingContext.Editor
+ * Revision 1.38 2002/02/19 16:37:38 mpowers Implemented support for
+ * EOEditingContext.Editor
*
- * Revision 1.37 2001/12/11 22:17:48 mpowers
- * Now properly handling exceptions in valueForObject.
- * No longer trying to retain selection based only on index.
+ * Revision 1.37 2001/12/11 22:17:48 mpowers Now properly handling exceptions in
+ * valueForObject. No longer trying to retain selection based only on index.
*
- * Revision 1.36 2001/11/08 21:42:00 mpowers
- * Now we know what to do with shouldRefetch and shouldRedisplay.
+ * Revision 1.36 2001/11/08 21:42:00 mpowers Now we know what to do with
+ * shouldRefetch and shouldRedisplay.
*
- * Revision 1.35 2001/11/04 18:26:58 mpowers
- * Fixed bug where exceptions were not properly reported when updating
- * a value and the display group did not have a delegate.
+ * Revision 1.35 2001/11/04 18:26:58 mpowers Fixed bug where exceptions were not
+ * properly reported when updating a value and the display group did not have a
+ * delegate.
*
- * Revision 1.34 2001/11/02 20:59:36 mpowers
- * Now correctly ensuring selected objects are a subset of displayed objects.
+ * Revision 1.34 2001/11/02 20:59:36 mpowers Now correctly ensuring selected
+ * objects are a subset of displayed objects.
*
- * Revision 1.33 2001/10/30 22:56:45 mpowers
- * Added support for EOQualifier.
+ * Revision 1.33 2001/10/30 22:56:45 mpowers Added support for EOQualifier.
*
- * Revision 1.32 2001/10/23 22:27:53 mpowers
- * Now running at ObserverPrioritySixth.
+ * Revision 1.32 2001/10/23 22:27:53 mpowers Now running at
+ * ObserverPrioritySixth.
*
- * Revision 1.31 2001/10/23 18:45:05 mpowers
- * Rolling back changes.
+ * Revision 1.31 2001/10/23 18:45:05 mpowers Rolling back changes.
*
- * Revision 1.28 2001/08/22 19:23:41 mpowers
- * No longer asserting objects in all objects list.
+ * Revision 1.28 2001/08/22 19:23:41 mpowers No longer asserting objects in all
+ * objects list.
*
- * Revision 1.27 2001/07/30 16:17:01 mpowers
- * Minor code cleanup.
+ * Revision 1.27 2001/07/30 16:17:01 mpowers Minor code cleanup.
*
- * Revision 1.26 2001/07/10 22:49:07 mpowers
- * Fixed bug in optimization for selectObjectsIdenticalTo (found by Dongzhi).
+ * Revision 1.26 2001/07/10 22:49:07 mpowers Fixed bug in optimization for
+ * selectObjectsIdenticalTo (found by Dongzhi).
*
- * Revision 1.25 2001/06/19 15:40:21 mpowers
- * Now only changing the selection if the new selection is different
- * from the old.
+ * Revision 1.25 2001/06/19 15:40:21 mpowers Now only changing the selection if
+ * the new selection is different from the old.
*
- * Revision 1.24 2001/05/24 17:36:15 mpowers
- * Fixed problem with selectedObjectsIdenticalTo: it was using compare
- * by value instead of compare by reference.
+ * Revision 1.24 2001/05/24 17:36:15 mpowers Fixed problem with
+ * selectedObjectsIdenticalTo: it was using compare by value instead of compare
+ * by reference.
*
- * Revision 1.23 2001/05/18 21:09:19 mpowers
- * Now throwing exceptions if the delegate cannot handle error from update.
+ * Revision 1.23 2001/05/18 21:09:19 mpowers Now throwing exceptions if the
+ * delegate cannot handle error from update.
*
- * Revision 1.22 2001/05/14 15:26:12 mpowers
- * Now checking for null delegate before and after selection change.
+ * Revision 1.22 2001/05/14 15:26:12 mpowers Now checking for null delegate
+ * before and after selection change.
*
- * Revision 1.21 2001/05/08 18:47:34 mpowers
- * Minor fixes for d3.
+ * Revision 1.21 2001/05/08 18:47:34 mpowers Minor fixes for d3.
*
- * Revision 1.20 2001/04/29 22:02:45 mpowers
- * Work on id transposing between editing contexts.
+ * Revision 1.20 2001/04/29 22:02:45 mpowers Work on id transposing between
+ * editing contexts.
*
- * Revision 1.19 2001/04/13 16:38:09 mpowers
- * Alpha3 release.
+ * Revision 1.19 2001/04/13 16:38:09 mpowers Alpha3 release.
*
- * Revision 1.18 2001/04/03 20:36:01 mpowers
- * Fixed refaulting/reverting/invalidating to be self-consistent.
+ * Revision 1.18 2001/04/03 20:36:01 mpowers Fixed
+ * refaulting/reverting/invalidating to be self-consistent.
*
- * Revision 1.17 2001/03/29 03:31:13 mpowers
- * No longer using Introspector.
+ * Revision 1.17 2001/03/29 03:31:13 mpowers No longer using Introspector.
*
- * Revision 1.16 2001/02/27 03:32:18 mpowers
- * Implemented default values for new objects.
+ * Revision 1.16 2001/02/27 03:32:18 mpowers Implemented default values for new
+ * objects.
*
- * Revision 1.15 2001/02/27 02:11:17 mpowers
- * Now throwing exception when cloning fails.
- * Removed debugging printlns.
+ * Revision 1.15 2001/02/27 02:11:17 mpowers Now throwing exception when cloning
+ * fails. Removed debugging printlns.
*
- * Revision 1.14 2001/02/26 22:41:51 mpowers
- * Implemented null placeholder classes.
- * Duplicator now uses NSNull.
- * No longer catching base exception class.
+ * Revision 1.14 2001/02/26 22:41:51 mpowers Implemented null placeholder
+ * classes. Duplicator now uses NSNull. No longer catching base exception class.
*
- * Revision 1.13 2001/02/26 15:53:22 mpowers
- * Fine-tuning notification firing.
+ * Revision 1.13 2001/02/26 15:53:22 mpowers Fine-tuning notification firing.
* Child display groups now update properly after parent save or invalidate.
*
- * Revision 1.12 2001/02/22 20:55:06 mpowers
- * Implemented notification handling.
+ * Revision 1.12 2001/02/22 20:55:06 mpowers Implemented notification handling.
*
- * Revision 1.11 2001/02/21 20:40:42 mpowers
- * setObjectArray now falls back to index when trying to retain the
- * same selection.
+ * Revision 1.11 2001/02/21 20:40:42 mpowers setObjectArray now falls back to
+ * index when trying to retain the same selection.
*
- * Revision 1.10 2001/02/20 16:38:55 mpowers
- * MasterDetailAssociations now observe their controlled display group's
- * objects for changes to that the parent object will be marked as updated.
- * Before, only inserts and deletes to an object's items are registered.
- * Also, moved ObservableArray to package access.
+ * Revision 1.10 2001/02/20 16:38:55 mpowers MasterDetailAssociations now
+ * observe their controlled display group's objects for changes to that the
+ * parent object will be marked as updated. Before, only inserts and deletes to
+ * an object's items are registered. Also, moved ObservableArray to package
+ * access.
*
- * Revision 1.9 2001/02/17 17:23:49 mpowers
- * More changes to support compiling with jdk1.1 collections.
+ * Revision 1.9 2001/02/17 17:23:49 mpowers More changes to support compiling
+ * with jdk1.1 collections.
*
- * Revision 1.8 2001/02/17 16:52:05 mpowers
- * Changes in imports to support building with jdk1.1 collections.
+ * Revision 1.8 2001/02/17 16:52:05 mpowers Changes in imports to support
+ * building with jdk1.1 collections.
*
- * Revision 1.7 2001/01/24 16:35:37 mpowers
- * Improved documentation on TreeAssociation.
- * SortOrderings are now inherited from parent nodes.
- * Updates after sorting are still lost on TreeController.
+ * Revision 1.7 2001/01/24 16:35:37 mpowers Improved documentation on
+ * TreeAssociation. SortOrderings are now inherited from parent nodes. Updates
+ * after sorting are still lost on TreeController.
*
- * Revision 1.6 2001/01/24 14:23:05 mpowers
- * Added support for OrderedDataSource.
+ * Revision 1.6 2001/01/24 14:23:05 mpowers Added support for OrderedDataSource.
*
- * Revision 1.5 2001/01/12 17:21:37 mpowers
- * Implicit creation of EOSortOrderings now happens in setSortOrderings.
+ * Revision 1.5 2001/01/12 17:21:37 mpowers Implicit creation of EOSortOrderings
+ * now happens in setSortOrderings.
*
- * Revision 1.4 2001/01/11 20:34:26 mpowers
- * Implemented EOSortOrdering and added support in framework.
- * Added header-click to sort table columns.
+ * Revision 1.4 2001/01/11 20:34:26 mpowers Implemented EOSortOrdering and added
+ * support in framework. Added header-click to sort table columns.
*
- * Revision 1.3 2001/01/10 22:49:44 mpowers
- * Implemented similarly named selection methods instead of
- * throwing exceptions.
+ * Revision 1.3 2001/01/10 22:49:44 mpowers Implemented similarly named
+ * selection methods instead of throwing exceptions.
*
- * Revision 1.2 2001/01/09 20:12:52 mpowers
- * Moved inner classes to package access.
+ * Revision 1.2 2001/01/09 20:12:52 mpowers Moved inner classes to package
+ * access.
*
- * Revision 1.1.1.1 2000/12/21 15:48:20 mpowers
- * Contributing wotonomy.
+ * Revision 1.1.1.1 2000/12/21 15:48:20 mpowers Contributing wotonomy.
*
- * Revision 1.21 2000/12/20 16:25:39 michael
- * Added log to all files.
+ * Revision 1.21 2000/12/20 16:25:39 michael Added log to all files.
*
- * Revision 1.20 2000/12/15 15:04:42 michael
- * Added doc.
+ * Revision 1.20 2000/12/15 15:04:42 michael Added doc.
*
- * Revision 1.19 2000/12/11 13:32:48 michael
- * Finish the much better TreeAssociation implementation.
- * TreeAssociation now has no gui dependencies.
+ * Revision 1.19 2000/12/11 13:32:48 michael Finish the much better
+ * TreeAssociation implementation. TreeAssociation now has no gui dependencies.
*
- * Revision 1.18 2000/12/05 17:41:46 michael
- * Broadcasts selection change after delegate refuses selection change
- * so the initiating association gets refreshed.
+ * Revision 1.18 2000/12/05 17:41:46 michael Broadcasts selection change after
+ * delegate refuses selection change so the initiating association gets
+ * refreshed.
*
*/
-