/*
Wotonomy: OpenStep design patterns for pure Java applications.
Copyright (C) 2000 Intersect Software Corporation
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, see http://www.gnu.org
*/
package net.wotonomy.ui.swing;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Enumeration;
import net.wotonomy.foundation.NSArray;
import net.wotonomy.foundation.NSSelector;
import net.wotonomy.foundation.internal.ValueConverter;
import net.wotonomy.foundation.internal.WotonomyException;
import net.wotonomy.ui.EOAssociation;
import net.wotonomy.ui.EODisplayGroup;
/**
* ActionAssociation binds any ActionEvent broadcaster
* (typically Buttons and the like) to a display group.
* Actions are invoked on all selected objects in the
* display group bound to the action aspect.
* Bindings are:
*
* - action: a method to be invoked on selected objects.
* If the argument aspect is bound, the method must take
* one argument. Otherwise, the method must take no arguments.
* - argument: the attribute of the selected object(s) (possibly
* from a different display group) that will be used as an argument
* to the action method
* - enabled: a boolean property that determines whether
* the controlled component is enabled
* - visible: a boolean property that determines whether
* the controlled component is visible
*
*
* @author michael@mpowers.net
* @author $Author: cgruber $
* @version $Revision: 904 $
*/
public class ActionAssociation extends EOAssociation
implements ActionListener
{
static final NSArray aspects =
new NSArray( new Object[] {
ActionAspect, ArgumentAspect, EnabledAspect, VisibleAspect
} );
static final NSArray aspectSignatures =
new NSArray( new Object[] {
AttributeToOneAspectSignature,
AttributeToOneAspectSignature,
AttributeToOneAspectSignature,
AttributeToOneAspectSignature
} );
static final NSArray objectKeysTaken =
new NSArray( new Object[] {
"target"
} );
static NSSelector addActionListener =
new NSSelector( "addActionListener",
new Class[] { ActionListener.class } );
static NSSelector removeActionListener =
new NSSelector( "removeActionListener",
new Class[] { ActionListener.class } );
/**
* Constructor specifying the object to be controlled by this
* association. Does not establish connection.
*/
public ActionAssociation ( Object anObject )
{
super( anObject );
}
/**
* Returns a List of aspect signatures whose contents
* correspond with the aspects list. Each element is
* a string whose characters represent a capability of
* the corresponding aspect.
* - "A" attribute: the aspect can be bound to
* an attribute.
* - "1" to-one: the aspect can be bound to a
* property that returns a single object.
* - "M" to-one: the aspect can be bound to a
* property that returns multiple objects.
*
* An empty signature "" means that the aspect can
* bind without needing a key.
* This implementation returns "A1M" for each
* element in the aspects array.
*/
public static NSArray aspectSignatures ()
{
return aspectSignatures;
}
/**
* Returns a List that describes the aspects supported
* by this class. Each element in the list is the string
* name of the aspect. This implementation returns an
* empty list.
*/
public static NSArray aspects ()
{
return aspects;
}
/**
* Returns a List of EOAssociation subclasses that,
* for the objects that are usable for this association,
* are less suitable than this association.
*/
public static NSArray associationClassesSuperseded ()
{
return new NSArray();
}
/**
* Returns whether this class can control the specified
* object.
*/
public static boolean isUsableWithObject ( Object anObject )
{
return
( addActionListener.implementedByObject( anObject ) )
&& ( removeActionListener.implementedByObject( anObject ) );
}
/**
* Returns a List of properties of the controlled object
* that are controlled by this class. For example,
* "stringValue", or "selected".
*/
public static NSArray objectKeysTaken ()
{
return objectKeysTaken;
}
/**
* Returns the aspect that is considered primary
* or default.
*/
public static String primaryAspect ()
{
return ActionAspect;
}
/**
* Returns whether this association can bind to the
* specified display group on the specified key for
* the specified aspect.
*/
public boolean canBindAspect (
String anAspect, EODisplayGroup aDisplayGroup, String aKey)
{
return ( aspects.containsObject( anAspect ) );
}
/**
* Establishes a connection between this association
* and the controlled object. Subclasses should begin
* listening for events from their controlled object here.
*/
public void establishConnection ()
{
try
{
addActionListener.invoke( object(), this );
}
catch ( Exception exc )
{
throw new WotonomyException( "EOActionAssociation: " +
"could not add action listener to object:" + object() );
}
super.establishConnection();
}
/**
* Breaks the connection between this association and
* its object. Override to stop listening for events
* from the object.
*/
public void breakConnection ()
{
try
{
removeActionListener.invoke( object(), this );
}
catch ( Exception exc )
{
throw new WotonomyException( "EOActionAssociation: " +
"could not add action listener to object:" + object() );
}
super.breakConnection();
}
/**
* Called when either the selection or the contents
* of an associated display group have changed.
* This implementation does nothing.
*/
public void subjectChanged ()
{
Object component = object();
EODisplayGroup displayGroup;
String key;
if ( component instanceof Component )
{
// enabled aspect
displayGroup = displayGroupForAspect( EnabledAspect );
if ( displayGroup != null )
{
key = displayGroupKeyForAspect( EnabledAspect );
((Component)component).setEnabled(
displayGroup.enabledToSetSelectedObjectValueForKey( key ) );
Object value =
displayGroup.selectedObjectValueForKey( key );
Boolean converted = null;
if ( value != null )
{
converted = (Boolean)
ValueConverter.convertObjectToClass(
value, Boolean.class );
}
if ( converted == null ) converted = Boolean.FALSE;
if ( converted.booleanValue() !=
((Component)component).isEnabled() )
{
((Component)component).setEnabled(
converted.booleanValue() );
}
}
// visible aspect
displayGroup = displayGroupForAspect( VisibleAspect );
if ( displayGroup != null )
{
key = displayGroupKeyForAspect( VisibleAspect );
Object value =
displayGroup.selectedObjectValueForKey( key );
Boolean converted = (Boolean)
ValueConverter.convertObjectToClass(
value, Boolean.class );
if ( converted != null )
{
if ( converted.booleanValue() !=
((Component)component).isVisible() )
{
((Component)component).setVisible(
converted.booleanValue() );
}
}
}
}
}
// interface ActionListener
public void actionPerformed( ActionEvent evt )
{
EODisplayGroup actionDisplayGroup = null;
String actionKey = null;
// action aspect
actionDisplayGroup = displayGroupForAspect( ActionAspect );
if ( actionDisplayGroup != null )
{
actionKey = displayGroupKeyForAspect( ActionAspect );
//TODO: argument aspect not implemented
try
{
NSSelector selector = new NSSelector( actionKey );
Enumeration e =
actionDisplayGroup.selectedObjects().objectEnumerator();
while ( e.hasMoreElements() )
{
selector.invoke( e.nextElement() );
}
}
catch ( Exception exc )
{
throw new WotonomyException(
"ActionAssociation: error invoking action: " + actionKey, exc );
}
}
}
}
/*
* $Log$
* Revision 1.2 2006/02/18 23:19:05 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.4 2004/01/28 18:34:57 mpowers
* Better handling for enabling.
* Now respecting enabledToSetSelectedObjectValueForKey from display group.
*
* Revision 1.3 2003/08/06 23:07:52 chochos
* general code cleanup (mostly, removing unused imports)
*
* Revision 1.2 2001/02/17 16:52:05 mpowers
* Changes in imports to support building with jdk1.1 collections.
*
* Revision 1.1.1.1 2000/12/21 15:48:28 mpowers
* Contributing wotonomy.
*
* Revision 1.5 2000/12/20 16:25:40 michael
* Added log to all files.
*
*
*/