summaryrefslogtreecommitdiff
path: root/projects/net.wotonomy.ui.swing/src/main
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.swing/src/main
parentff072dfe782f6f22123cd4ba050828d35c0d0fbd (diff)
Formatting pass
Diffstat (limited to 'projects/net.wotonomy.ui.swing/src/main')
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/ActionAssociation.java463
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/AdjustableAssociation.java443
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/ButtonAssociation.java684
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/ComboBoxAssociation.java1137
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/DateAssociation.java856
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/DisplayGroupActionAssociation.java148
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/DisplayGroupInspector.java135
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/DisplayGroupNode.java2452
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/ListAssociation.java491
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/MutableDisplayGroupNode.java296
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/NotificationInspector.java497
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/RadioPanelAssociation.java593
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/ReferenceInspector.java417
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/SliderAssociation.java587
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/TableAssociation.java1370
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/TableColumnAssociation.java1085
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/TextAssociation.java1980
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/TimedTextAssociation.java1430
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/TreeAssociation.java876
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/TreeColumnAssociation.java488
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/TreeModelAssociation.java2930
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/AbsoluteLayout.java61
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/AlphaTextField.java593
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/AlternatingRowCellRenderer.java157
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/BetterFlowLayout.java952
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/BetterRootLayout.java268
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/BetterTableUI.java176
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/ButtonPanel.java1066
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/CheckButtonPanel.java455
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/ColorCellEditor.java88
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/ColorCellRenderer.java79
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/ComboBoxCellRenderer.java44
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/DateTextField.java1098
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/FormattedCellRenderer.java460
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/IconCellRenderer.java1272
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/ImagePanel.java91
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/InfoPanel.java2908
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/KeyDelayTimer.java301
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/KeyableCellEditor.java566
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/LineWrappingRenderer.java225
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/MultiLineLabel.java163
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/NumericTextField.java754
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/PropertyEditorTable.java927
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/PropertyEditorTableModel.java538
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/RadioButtonPanel.java278
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/SmartPasswordField.java456
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/SmartTextField.java398
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/StatusButtonPanel.java435
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/TintedImageFilter.java122
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/TreeChooser.java1134
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/TreeTableCellRenderer.java305
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/ClassGrabber.java108
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/ComponentHighlighter.java212
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/GIFEncoder.java890
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/ObjectInspector.java300
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/PositionComparator.java84
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/StackTraceInspector.java686
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/TextInputRangeChecker.java609
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/WindowGrabber.java229
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/WindowUtilities.java890
60 files changed, 18040 insertions, 21696 deletions
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/ActionAssociation.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/ActionAssociation.java
index cc3af69..ae3fa50 100644
--- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/ActionAssociation.java
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/ActionAssociation.java
@@ -31,305 +31,228 @@ 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:
-* <ul>
-* <li>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.</li>
-* <li>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</li>
-* <li>enabled: a boolean property that determines whether
-* the controlled component is enabled</li>
-* <li>visible: a boolean property that determines whether
-* the controlled component is visible</li>
-* </ul>
-*
-* @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 } );
+ * 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:
+ * <ul>
+ * <li>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.</li>
+ * <li>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</li>
+ * <li>enabled: a boolean property that determines whether the controlled
+ * component is enabled</li>
+ * <li>visible: a boolean property that determines whether the controlled
+ * component is visible</li>
+ * </ul>
+ *
+ * @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" });
- /**
- * 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. <ul>
- * <li>"A" attribute: the aspect can be bound to
- * an attribute.</li>
- * <li>"1" to-one: the aspect can be bound to a
- * property that returns a single object.</li>
- * <li>"M" to-one: the aspect can be bound to a
- * property that returns multiple objects.</li>
- * </ul>
- * 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 );
+ 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.
+ * <ul>
+ * <li>"A" attribute: the aspect can be bound to an attribute.</li>
+ * <li>"1" to-one: the aspect can be bound to a property that returns a single
+ * object.</li>
+ * <li>"M" to-one: the aspect can be bound to a property that returns multiple
+ * objects.</li>
+ * </ul>
+ * 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());
}
- 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 ()
- {
+ 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 )
- {
+
+ 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() );
- }
+ 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() );
+ 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 )
- {
+ }
+
+ // interface ActionListener
+
+ public void actionPerformed(ActionEvent evt) {
EODisplayGroup actionDisplayGroup = null;
String actionKey = null;
-
+
// action aspect
- actionDisplayGroup = displayGroupForAspect( ActionAspect );
- if ( actionDisplayGroup != null )
- {
- actionKey = displayGroupKeyForAspect( ActionAspect );
+ actionDisplayGroup = displayGroupForAspect(ActionAspect);
+ if (actionDisplayGroup != null) {
+ actionKey = displayGroupKeyForAspect(ActionAspect);
- //TODO: argument aspect not implemented
+ // TODO: argument aspect not implemented
- try
- {
-
- NSSelector selector = new NSSelector( actionKey );
- Enumeration e =
- actionDisplayGroup.selectedObjects().objectEnumerator();
- while ( e.hasMoreElements() )
- {
- selector.invoke( e.nextElement() );
+ 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 );
+ } 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.
+ * $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.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.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.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.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.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.
+ * Revision 1.5 2000/12/20 16:25:40 michael Added log to all files.
*
*
*/
-
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/AdjustableAssociation.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/AdjustableAssociation.java
index 2dc7fec..4f90c79 100644
--- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/AdjustableAssociation.java
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/AdjustableAssociation.java
@@ -29,299 +29,234 @@ import net.wotonomy.ui.EOAssociation;
import net.wotonomy.ui.EODisplayGroup;
/**
-* AdjustableAssociation binds any Adjustable component to
-* a display group. Components implementing the adjustable
-* interface include: ScrollBar and JScrollBar.
-* Bindings are:
-* <ul>
-* <li>value: a property convertable to/from a string</li>
-* <li>enabled: a boolean property that determines whether
-* the user can select the text in the field</li>
-* </ul>
-*
-* @author michael@mpowers.net
-* @author $Author: cgruber $
-* @version $Revision: 904 $
-*/
-public class AdjustableAssociation extends EOAssociation
- implements AdjustmentListener
-{
- static final NSArray aspects =
- new NSArray( new Object[] {
- ValueAspect, EnabledAspect
- } );
- static final NSArray aspectSignatures =
- new NSArray( new Object[] {
- AttributeToOneAspectSignature,
- AttributeToOneAspectSignature,
- AttributeToOneAspectSignature,
- AttributeToOneAspectSignature
- } );
- static final NSArray objectKeysTaken =
- new NSArray( new Object[] {
- "value"
- } );
-
- /**
- * Constructor specifying the object to be controlled by this
- * association. Does not establish connection.
- */
- public AdjustableAssociation ( 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. <ul>
- * <li>"A" attribute: the aspect can be bound to
- * an attribute.</li>
- * <li>"1" to-one: the aspect can be bound to a
- * property that returns a single object.</li>
- * <li>"M" to-one: the aspect can be bound to a
- * property that returns multiple objects.</li>
- * </ul>
- * 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 ( anObject instanceof Adjustable );
- }
-
- /**
- * 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. This is typically "value" or somesuch.
- */
- public static String primaryAspect ()
- {
- return ValueAspect;
- }
-
- /**
- * 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. This implementation
- * attempts to add this class as an ActionListener
- * and as a FocusListener to the specified object.
- */
- public void establishConnection ()
- {
- component().addAdjustmentListener( this );
- super.establishConnection();
-
+ * AdjustableAssociation binds any Adjustable component to a display group.
+ * Components implementing the adjustable interface include: ScrollBar and
+ * JScrollBar. Bindings are:
+ * <ul>
+ * <li>value: a property convertable to/from a string</li>
+ * <li>enabled: a boolean property that determines whether the user can select
+ * the text in the field</li>
+ * </ul>
+ *
+ * @author michael@mpowers.net
+ * @author $Author: cgruber $
+ * @version $Revision: 904 $
+ */
+public class AdjustableAssociation extends EOAssociation implements AdjustmentListener {
+ static final NSArray aspects = new NSArray(new Object[] { ValueAspect, EnabledAspect });
+ static final NSArray aspectSignatures = new NSArray(new Object[] { AttributeToOneAspectSignature,
+ AttributeToOneAspectSignature, AttributeToOneAspectSignature, AttributeToOneAspectSignature });
+ static final NSArray objectKeysTaken = new NSArray(new Object[] { "value" });
+
+ /**
+ * Constructor specifying the object to be controlled by this association. Does
+ * not establish connection.
+ */
+ public AdjustableAssociation(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.
+ * <ul>
+ * <li>"A" attribute: the aspect can be bound to an attribute.</li>
+ * <li>"1" to-one: the aspect can be bound to a property that returns a single
+ * object.</li>
+ * <li>"M" to-one: the aspect can be bound to a property that returns multiple
+ * objects.</li>
+ * </ul>
+ * 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 (anObject instanceof Adjustable);
+ }
+
+ /**
+ * 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. This is typically
+ * "value" or somesuch.
+ */
+ public static String primaryAspect() {
+ return ValueAspect;
+ }
+
+ /**
+ * 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.
+ * This implementation attempts to add this class as an ActionListener and as a
+ * FocusListener to the specified object.
+ */
+ public void establishConnection() {
+ component().addAdjustmentListener(this);
+ super.establishConnection();
+
// forces update from bindings
subjectChanged();
- }
-
- /**
- * Breaks the connection between this association and
- * its object. Override to stop listening for events
- * from the object.
- */
- public void breakConnection ()
- {
- component().removeAdjustmentListener( this );
- super.breakConnection();
- }
+ }
- /**
- * Called when either the selection or the contents
- * of an associated display group have changed.
- */
- public void subjectChanged ()
- {
+ /**
+ * Breaks the connection between this association and its object. Override to
+ * stop listening for events from the object.
+ */
+ public void breakConnection() {
+ component().removeAdjustmentListener(this);
+ super.breakConnection();
+ }
+
+ /**
+ * Called when either the selection or the contents of an associated display
+ * group have changed.
+ */
+ public void subjectChanged() {
Adjustable component = component();
EODisplayGroup displayGroup;
String key;
Object value;
-
+
// value aspect
- displayGroup = displayGroupForAspect( ValueAspect );
- if ( displayGroup != null )
- {
- key = displayGroupKeyForAspect( ValueAspect );
- if ( component instanceof Component )
- {
- ((Component)component).setEnabled(
- displayGroup.enabledToSetSelectedObjectValueForKey( key ) );
- }
- value = displayGroup.selectedObjectValueForKey( key );
-
+ displayGroup = displayGroupForAspect(ValueAspect);
+ if (displayGroup != null) {
+ key = displayGroupKeyForAspect(ValueAspect);
+ if (component instanceof Component) {
+ ((Component) component).setEnabled(displayGroup.enabledToSetSelectedObjectValueForKey(key));
+ }
+ value = displayGroup.selectedObjectValueForKey(key);
+
// convert value to int
- value = ValueConverter.convertObjectToClass(
- value, Integer.class );
+ value = ValueConverter.convertObjectToClass(value, Integer.class);
int intValue;
- if ( value == null )
- {
+ if (value == null) {
intValue = 0;
+ } else {
+ intValue = ((Integer) value).intValue();
}
- else
- {
- intValue = ((Integer)value).intValue();
- }
-
- if ( component.getValue() != intValue )
- {
- component.setValue( intValue );
+
+ if (component.getValue() != intValue) {
+ component.setValue(intValue);
}
}
// enabled aspect
- displayGroup = displayGroupForAspect( EnabledAspect );
- key = displayGroupKeyForAspect( EnabledAspect );
- if ( ( ( displayGroup != null ) || ( key != null ) )
- && ( component instanceof Component ) )
- {
- if ( displayGroup != null )
- {
- value =
- displayGroup.selectedObjectValueForKey( key );
- }
- else
- {
+ displayGroup = displayGroupForAspect(EnabledAspect);
+ key = displayGroupKeyForAspect(EnabledAspect);
+ if (((displayGroup != null) || (key != null)) && (component instanceof Component)) {
+ if (displayGroup != null) {
+ value = displayGroup.selectedObjectValueForKey(key);
+ } else {
// treat bound key without display group as a value
- value = key;
+ value = 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());
}
- 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() );
- }
}
- }
-
- /**
- * Forces this association to cause the object to
- * stop editing and validate the user's input.
- * @return false if there were problems validating,
- * or true to continue.
- */
- public boolean endEditing ()
- {
+ }
+
+ /**
+ * Forces this association to cause the object to stop editing and validate the
+ * user's input.
+ *
+ * @return false if there were problems validating, or true to continue.
+ */
+ public boolean endEditing() {
return writeValueToDisplayGroup();
- }
-
+ }
+
/**
- * Writes the value currently in the component
- * to the selected object in the display group
- * bound to the value aspect.
- * @return false if there were problems validating,
- * or true to continue.
- */
- protected boolean writeValueToDisplayGroup()
- {
- EODisplayGroup displayGroup =
- displayGroupForAspect( ValueAspect );
- if ( displayGroup != null )
- {
- String key = displayGroupKeyForAspect( ValueAspect );
- Object value = new Integer( component().getValue() );
- return displayGroup.setSelectedObjectValue( value, key );
+ * Writes the value currently in the component to the selected object in the
+ * display group bound to the value aspect.
+ *
+ * @return false if there were problems validating, or true to continue.
+ */
+ protected boolean writeValueToDisplayGroup() {
+ EODisplayGroup displayGroup = displayGroupForAspect(ValueAspect);
+ if (displayGroup != null) {
+ String key = displayGroupKeyForAspect(ValueAspect);
+ Object value = new Integer(component().getValue());
+ return displayGroup.setSelectedObjectValue(value, key);
}
return false;
}
- // interface AdjustmentListener
-
+ // interface AdjustmentListener
+
/**
- * Updates object on action performed.
- */
- public void adjustmentValueChanged(AdjustmentEvent e)
- {
+ * Updates object on action performed.
+ */
+ public void adjustmentValueChanged(AdjustmentEvent e) {
writeValueToDisplayGroup();
}
-
- private Adjustable component()
- {
- return (Adjustable) object();
+
+ private Adjustable component() {
+ return (Adjustable) object();
}
}
/*
- * $Log$
- * Revision 1.2 2006/02/18 23:19:05 cgruber
- * Update imports and maven dependencies.
+ * $Log$ Revision 1.2 2006/02/18 23:19:05 cgruber Update imports and maven
+ * dependencies.
*
- * Revision 1.1 2006/02/16 13:22:23 cgruber
- * Check in all sources in eclipse-friendly maven-enabled packages.
+ * Revision 1.1 2006/02/16 13:22:23 cgruber Check in all sources in
+ * eclipse-friendly maven-enabled packages.
*
- * Revision 1.3 2004/01/28 18:34:57 mpowers
- * Better handling for enabling.
- * Now respecting enabledToSetSelectedObjectValueForKey from display group.
+ * Revision 1.3 2004/01/28 18:34:57 mpowers Better handling for enabling. Now
+ * respecting enabledToSetSelectedObjectValueForKey from display group.
*
- * Revision 1.2 2003/08/06 23:07:52 chochos
- * general code cleanup (mostly, removing unused imports)
+ * Revision 1.2 2003/08/06 23:07:52 chochos general code cleanup (mostly,
+ * removing unused imports)
*
- * Revision 1.1.1.1 2000/12/21 15:48:35 mpowers
- * Contributing wotonomy.
+ * Revision 1.1.1.1 2000/12/21 15:48:35 mpowers Contributing wotonomy.
*
- * Revision 1.2 2000/12/20 16:25:40 michael
- * Added log to all files.
+ * Revision 1.2 2000/12/20 16:25:40 michael Added log to all files.
*
*
*/
-
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/ButtonAssociation.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/ButtonAssociation.java
index 38cf38b..5a1c85c 100644
--- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/ButtonAssociation.java
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/ButtonAssociation.java
@@ -33,412 +33,312 @@ import net.wotonomy.ui.EOAssociation;
import net.wotonomy.ui.EODisplayGroup;
/**
-* ButtonAssociation binds any component that uses a ButtonModel
-* (all Swing button classes) to a display group. This association
-* should be used to handle individual JRadioButtons and JCheckBoxes.
-* Bindings are:
-* <ul>
-* <li>value: a boolean property that determines the
-* selected state of the button model. This will set
-* the value for radio buttons and check boxes.</li>
-* <li>enabled: a boolean property that determines the
-* enabled state of the button model.</li>
-* <li>visible: a boolean property that determines the
-* visible state of the button model.</li>
-* </ul>
-*
-* @author michael@mpowers.net
-* @author $Author: cgruber $
-* @version $Revision: 904 $
-*/
-public class ButtonAssociation extends EOAssociation
- implements ChangeListener
-{
- static final NSArray aspects =
- new NSArray( new Object[] {
- ValueAspect, EnabledAspect, VisibleAspect
- } );
- static final NSArray aspectSignatures =
- new NSArray( new Object[] {
- AttributeToOneAspectSignature,
- AttributeToOneAspectSignature,
- AttributeToOneAspectSignature
- } );
- static final NSArray objectKeysTaken =
- new NSArray( new Object[] {
- "model.selected"
- } );
-
- static NSSelector getModel =
- new NSSelector( "getModel", new Class[] {} );
-
- protected ButtonModel buttonModel;
- protected boolean lastKnownValue;
-
- /**
- * Constructor specifying the object to be controlled by this
- * association. Does not establish connection.
- * This implementation expects a ButtonModel or a class
- * that has a "getModel" method that returns a ButtonModel.
- */
- public ButtonAssociation ( Object anObject )
- {
- super( anObject );
-
- if ( anObject instanceof ButtonModel )
- {
- buttonModel = (ButtonModel) anObject;
- }
- else
- {
- try
- {
- buttonModel = (ButtonModel) getModel.invoke( anObject );
- }
- catch ( Exception exc )
- {
- throw new WotonomyException( "EOButtonAssociation: " +
- "could not retrieve a button model from object:" + 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. <ul>
- * <li>"A" attribute: the aspect can be bound to
- * an attribute.</li>
- * <li>"1" to-one: the aspect can be bound to a
- * property that returns a single object.</li>
- * <li>"M" to-one: the aspect can be bound to a
- * property that returns multiple objects.</li>
- * </ul>
- * 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.
- * This implementation expects a ButtonModel or a class
- * that has a "getModel" method that returns a ButtonModel.
- */
- public static boolean isUsableWithObject ( Object anObject )
- {
- return
- ( anObject instanceof ButtonModel )
- || ( getModel.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 ValueAspect;
- }
-
- /**
- * 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 ()
- {
- buttonModel.addChangeListener( this );
- super.establishConnection();
- subjectChanged();
- }
-
- /**
- * Breaks the connection between this association and
- * its object. Override to stop listening for events
- * from the object.
- */
- public void breakConnection ()
- {
- buttonModel.removeChangeListener( this );
- 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;
-
- // value aspect
- displayGroup = displayGroupForAspect( ValueAspect );
-
- if ( displayGroup != null )
- {
- key = displayGroupKeyForAspect( ValueAspect );
- if ( component instanceof Component )
- {
- ((Component)component).setEnabled(
- displayGroup.enabledToSetSelectedObjectValueForKey( key ) );
- }
-
- Object value;
- if ( displayGroup.selectedObjects().size() > 1 )
- {
- // if there're more than one object selected, set
- // the value to blank for all of them.
- Object previousValue;
-
- Iterator indexIterator = displayGroup.selectionIndexes().
- iterator();
-
- // get value for the first selected object.
- int initialIndex = ( (Integer)indexIterator.next() ).intValue();
- previousValue = displayGroup.valueForObjectAtIndex(
- initialIndex, key );
- value = null;
-
- // go through the rest of the selected objects, compare each
- // value with the previous one. continue comparing if two
- // values are equal, break the while loop if they're different.
- // the final value will be the common value of all selected objects
- // if there is one, or be blank if there is not.
- while ( indexIterator.hasNext() )
- {
- int index = ( (Integer)indexIterator.next() ).intValue();
- Object currentValue = displayGroup.valueForObjectAtIndex(
- index, key );
- if ( currentValue != null && !currentValue.equals( previousValue ) )
- {
- value = null;
- break;
- }
- else
- {
- // currentValue is the same as the previous one
- value = currentValue;
- }
-
- } // end while
-
- }
- else // displayGroup has only one object
- {
- value =
- displayGroup.selectedObjectValueForKey( key );
- } // end checking size of displayGroup
-
- buttonModel.setArmed( false );
- buttonModel.setPressed( false );
-
- if ( value != null )
- {
- Boolean converted = (Boolean)
- ValueConverter.convertObjectToClass(
- value, Boolean.class );
- if ( converted != null )
- {
- lastKnownValue = converted.booleanValue();
- if ( converted.booleanValue() !=
- buttonModel.isSelected() )
- {
- buttonModel.removeChangeListener( this );
- buttonModel.setSelected(
- converted.booleanValue() );
- buttonModel.addChangeListener( this );
- }
- } // end checking converted == null
- }
- else
- {
- buttonModel.setArmed( true );
- buttonModel.setPressed( true );
- }
- }
-
- // enabled aspect
- displayGroup = displayGroupForAspect( EnabledAspect );
- if ( displayGroup != null )
- {
- key = displayGroupKeyForAspect( EnabledAspect );
- 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() !=
- buttonModel.isEnabled() )
- {
- buttonModel.removeChangeListener( this );
- buttonModel.setEnabled(
- converted.booleanValue() );
- buttonModel.addChangeListener( this );
- }
- }
-
- // 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() );
- }
- }
- }
- }
-
- /**
- * Writes the value currently in the component
- * to the selected object in the display group
- * bound to the value aspect.
- * @return false if there were problems validating,
- * or true to continue.
- */
- protected boolean writeValueToDisplayGroup()
- {
- EODisplayGroup displayGroup =
- displayGroupForAspect( ValueAspect );
- if ( displayGroup != null )
- {
- boolean returnValue = true;
- String key = displayGroupKeyForAspect( ValueAspect );
- Object value = new Boolean( buttonModel.isSelected() );
-
- Iterator selectedIterator = displayGroup.selectionIndexes().iterator();
- while ( selectedIterator.hasNext() )
- {
- int index = ( (Integer)selectedIterator.next() ).intValue();
-
- if ( !displayGroup.setValueForObjectAtIndex( value, index, key ) )
- {
- returnValue = false;
- }
- }
- return returnValue;
-
- }
- return false;
- }
- // interface ChangeListener
-
- public void stateChanged(ChangeEvent e)
- {
- if ( buttonModel.isSelected() != lastKnownValue )
- {
- lastKnownValue = buttonModel.isSelected();
- writeValueToDisplayGroup();
- }
- }
+ * ButtonAssociation binds any component that uses a ButtonModel (all Swing
+ * button classes) to a display group. This association should be used to handle
+ * individual JRadioButtons and JCheckBoxes. Bindings are:
+ * <ul>
+ * <li>value: a boolean property that determines the selected state of the
+ * button model. This will set the value for radio buttons and check boxes.</li>
+ * <li>enabled: a boolean property that determines the enabled state of the
+ * button model.</li>
+ * <li>visible: a boolean property that determines the visible state of the
+ * button model.</li>
+ * </ul>
+ *
+ * @author michael@mpowers.net
+ * @author $Author: cgruber $
+ * @version $Revision: 904 $
+ */
+public class ButtonAssociation extends EOAssociation implements ChangeListener {
+ static final NSArray aspects = new NSArray(new Object[] { ValueAspect, EnabledAspect, VisibleAspect });
+ static final NSArray aspectSignatures = new NSArray(new Object[] { AttributeToOneAspectSignature,
+ AttributeToOneAspectSignature, AttributeToOneAspectSignature });
+ static final NSArray objectKeysTaken = new NSArray(new Object[] { "model.selected" });
+
+ static NSSelector getModel = new NSSelector("getModel", new Class[] {});
+
+ protected ButtonModel buttonModel;
+ protected boolean lastKnownValue;
+
+ /**
+ * Constructor specifying the object to be controlled by this association. Does
+ * not establish connection. This implementation expects a ButtonModel or a
+ * class that has a "getModel" method that returns a ButtonModel.
+ */
+ public ButtonAssociation(Object anObject) {
+ super(anObject);
+
+ if (anObject instanceof ButtonModel) {
+ buttonModel = (ButtonModel) anObject;
+ } else {
+ try {
+ buttonModel = (ButtonModel) getModel.invoke(anObject);
+ } catch (Exception exc) {
+ throw new WotonomyException(
+ "EOButtonAssociation: " + "could not retrieve a button model from object:" + 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.
+ * <ul>
+ * <li>"A" attribute: the aspect can be bound to an attribute.</li>
+ * <li>"1" to-one: the aspect can be bound to a property that returns a single
+ * object.</li>
+ * <li>"M" to-one: the aspect can be bound to a property that returns multiple
+ * objects.</li>
+ * </ul>
+ * 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. This
+ * implementation expects a ButtonModel or a class that has a "getModel" method
+ * that returns a ButtonModel.
+ */
+ public static boolean isUsableWithObject(Object anObject) {
+ return (anObject instanceof ButtonModel) || (getModel.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 ValueAspect;
+ }
+
+ /**
+ * 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() {
+ buttonModel.addChangeListener(this);
+ super.establishConnection();
+ subjectChanged();
+ }
+
+ /**
+ * Breaks the connection between this association and its object. Override to
+ * stop listening for events from the object.
+ */
+ public void breakConnection() {
+ buttonModel.removeChangeListener(this);
+ 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;
+
+ // value aspect
+ displayGroup = displayGroupForAspect(ValueAspect);
+
+ if (displayGroup != null) {
+ key = displayGroupKeyForAspect(ValueAspect);
+ if (component instanceof Component) {
+ ((Component) component).setEnabled(displayGroup.enabledToSetSelectedObjectValueForKey(key));
+ }
+
+ Object value;
+ if (displayGroup.selectedObjects().size() > 1) {
+ // if there're more than one object selected, set
+ // the value to blank for all of them.
+ Object previousValue;
+
+ Iterator indexIterator = displayGroup.selectionIndexes().iterator();
+
+ // get value for the first selected object.
+ int initialIndex = ((Integer) indexIterator.next()).intValue();
+ previousValue = displayGroup.valueForObjectAtIndex(initialIndex, key);
+ value = null;
+
+ // go through the rest of the selected objects, compare each
+ // value with the previous one. continue comparing if two
+ // values are equal, break the while loop if they're different.
+ // the final value will be the common value of all selected objects
+ // if there is one, or be blank if there is not.
+ while (indexIterator.hasNext()) {
+ int index = ((Integer) indexIterator.next()).intValue();
+ Object currentValue = displayGroup.valueForObjectAtIndex(index, key);
+ if (currentValue != null && !currentValue.equals(previousValue)) {
+ value = null;
+ break;
+ } else {
+ // currentValue is the same as the previous one
+ value = currentValue;
+ }
+
+ } // end while
+
+ } else // displayGroup has only one object
+ {
+ value = displayGroup.selectedObjectValueForKey(key);
+ } // end checking size of displayGroup
+
+ buttonModel.setArmed(false);
+ buttonModel.setPressed(false);
+
+ if (value != null) {
+ Boolean converted = (Boolean) ValueConverter.convertObjectToClass(value, Boolean.class);
+ if (converted != null) {
+ lastKnownValue = converted.booleanValue();
+ if (converted.booleanValue() != buttonModel.isSelected()) {
+ buttonModel.removeChangeListener(this);
+ buttonModel.setSelected(converted.booleanValue());
+ buttonModel.addChangeListener(this);
+ }
+ } // end checking converted == null
+ } else {
+ buttonModel.setArmed(true);
+ buttonModel.setPressed(true);
+ }
+ }
+
+ // enabled aspect
+ displayGroup = displayGroupForAspect(EnabledAspect);
+ if (displayGroup != null) {
+ key = displayGroupKeyForAspect(EnabledAspect);
+ 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() != buttonModel.isEnabled()) {
+ buttonModel.removeChangeListener(this);
+ buttonModel.setEnabled(converted.booleanValue());
+ buttonModel.addChangeListener(this);
+ }
+ }
+
+ // 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());
+ }
+ }
+ }
+ }
+
+ /**
+ * Writes the value currently in the component to the selected object in the
+ * display group bound to the value aspect.
+ *
+ * @return false if there were problems validating, or true to continue.
+ */
+ protected boolean writeValueToDisplayGroup() {
+ EODisplayGroup displayGroup = displayGroupForAspect(ValueAspect);
+ if (displayGroup != null) {
+ boolean returnValue = true;
+ String key = displayGroupKeyForAspect(ValueAspect);
+ Object value = new Boolean(buttonModel.isSelected());
+
+ Iterator selectedIterator = displayGroup.selectionIndexes().iterator();
+ while (selectedIterator.hasNext()) {
+ int index = ((Integer) selectedIterator.next()).intValue();
+
+ if (!displayGroup.setValueForObjectAtIndex(value, index, key)) {
+ returnValue = false;
+ }
+ }
+ return returnValue;
+
+ }
+ return false;
+ }
+ // interface ChangeListener
+
+ public void stateChanged(ChangeEvent e) {
+ if (buttonModel.isSelected() != lastKnownValue) {
+ lastKnownValue = buttonModel.isSelected();
+ writeValueToDisplayGroup();
+ }
+ }
}
/*
- * $Log$
- * Revision 1.2 2006/02/18 23:19:05 cgruber
- * Update imports and maven dependencies.
+ * $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.1 2006/02/16 13:22:22 cgruber Check in all sources in
+ * eclipse-friendly maven-enabled packages.
*
- * Revision 1.8 2004/01/28 18:34:57 mpowers
- * Better handling for enabling.
- * Now respecting enabledToSetSelectedObjectValueForKey from display group.
+ * Revision 1.8 2004/01/28 18:34:57 mpowers Better handling for enabling. Now
+ * respecting enabledToSetSelectedObjectValueForKey from display group.
*
- * Revision 1.7 2003/08/06 23:07:52 chochos
- * general code cleanup (mostly, removing unused imports)
+ * Revision 1.7 2003/08/06 23:07:52 chochos general code cleanup (mostly,
+ * removing unused imports)
*
- * Revision 1.6 2001/07/30 16:32:55 mpowers
- * Implemented support for bulk-editing. Detail associations will now
- * apply changes to all selected objects.
+ * Revision 1.6 2001/07/30 16:32:55 mpowers Implemented support for
+ * bulk-editing. Detail associations will now apply changes to all selected
+ * objects.
*
- * Revision 1.5 2001/06/29 22:28:19 mpowers
- * Tabs to spaces.
+ * Revision 1.5 2001/06/29 22:28:19 mpowers Tabs to spaces.
*
- * Revision 1.4 2001/06/29 22:17:31 mpowers
- * Now updating the component on establishConnection.
+ * Revision 1.4 2001/06/29 22:17:31 mpowers Now updating the component on
+ * establishConnection.
*
- * Revision 1.3 2001/02/27 02:10:38 mpowers
- * No longer updating values to the display group if the value
- * has not changed.
+ * Revision 1.3 2001/02/27 02:10:38 mpowers No longer updating values to the
+ * display group if the value has not changed.
*
- * Revision 1.2 2001/02/21 20:33:01 mpowers
- * Fixed bug with change listener.
+ * Revision 1.2 2001/02/21 20:33:01 mpowers Fixed bug with change listener.
*
- * Revision 1.1.1.1 2000/12/21 15:48:38 mpowers
- * Contributing wotonomy.
+ * Revision 1.1.1.1 2000/12/21 15:48:38 mpowers Contributing wotonomy.
*
- * Revision 1.3 2000/12/20 16:25:40 michael
- * Added log to all files.
+ * Revision 1.3 2000/12/20 16:25:40 michael Added log to all files.
*
*
*/
-
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/ComboBoxAssociation.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/ComboBoxAssociation.java
index d0a087e..e6cd0aa 100644
--- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/ComboBoxAssociation.java
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/ComboBoxAssociation.java
@@ -37,664 +37,529 @@ import net.wotonomy.ui.EOAssociation;
import net.wotonomy.ui.EODisplayGroup;
/**
-* ComboBoxAssociation binds JComboBoxes to
-* display groups. Bindings are:
-* <ul>
-*
-* <li>value: optional - a property of the selected object in the
-* display group that will be bind to the item the user
-* selects or the text that the user enters in the field.
-* If the value aspect is not bound, then the combo box works
-* as an "overview" assocation and changing the selected object
-* in the combobox will modify the selection of the display group
-* bound to the objects or the titles display groups (in that order).</li>
-*
-* <li>titles: optional - a property of the objects in the bound
-* display group that will appear in the list. If the
-* objects aspect is not bound, this property is also
-* used to populate the value binding. If the titles
-* aspect itself is not bound, the items already in the
-* combobox will be used to update the value in the
-* selected object in the bound display group.</li>
-*
-* <li>objects: optional - if specified, when the user
-* selects a title in the list, the property of the
-* object at the corresponding index of the bound display
-* group will be used to populate the value binding.
-* If the objects aspect is used with an editable combo
-* box, any value entered that does not match one of the
-* titles in the list will produce a null value.</li>
-*
-* <li>enabled: optional - a boolean property of the
-* selected object in the display group that determines whether
-* the user can edit the field.</li>
-*
-* </ul>
-*
-* @author michael@mpowers.net
-* @author $Author: cgruber $
-* @version $Revision: 904 $
-*/
-public class ComboBoxAssociation extends EOAssociation
- implements FocusListener, ActionListener
-{
- static final NSArray aspects =
- new NSArray( new Object[] {
- TitlesAspect, ValueAspect,
- ObjectsAspect, EnabledAspect
- } );
- static final NSArray aspectSignatures =
- new NSArray( new Object[] {
- AttributeToOneAspectSignature,
- AttributeToOneAspectSignature,
- AttributeToOneAspectSignature,
- AttributeToOneAspectSignature
- } );
- static final NSArray objectKeysTaken =
- new NSArray( new Object[] {
- "text"
- } );
-
- private boolean wasNull;
- private static final String EMPTY_STRING = "";
-
- /**
- * Constructor specifying the object to be controlled by this
- * association. Does not establish connection.
- */
- public ComboBoxAssociation ( 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. <ul>
- * <li>"A" attribute: the aspect can be bound to
- * an attribute.</li>
- * <li>"1" to-one: the aspect can be bound to a
- * property that returns a single object.</li>
- * <li>"M" to-one: the aspect can be bound to a
- * property that returns multiple objects.</li>
- * </ul>
- * 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 ( anObject instanceof JComboBox );
- }
-
- /**
- * 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. This is typically "value" or somesuch.
- */
- public static String primaryAspect ()
- {
- return ValueAspect;
- }
-
- /**
- * 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 ()
- {
- super.establishConnection();
-
- // prepopulate titles
- EODisplayGroup displayGroup =
- displayGroupForAspect( TitlesAspect );
- if ( displayGroup != null )
- {
- String key = displayGroupKeyForAspect( TitlesAspect );
- populateTitles( displayGroup, key );
- }
- addAsListener();
- subjectChanged();
- }
-
- protected void addAsListener()
- {
- component().addActionListener( this );
- component().addFocusListener( this );
- }
-
- /**
- * Breaks the connection between this association and
- * its object. Override to stop listening for events
- * from the object.
- */
- public void breakConnection ()
- {
- removeAsListener();
- super.breakConnection();
- }
-
- protected void removeAsListener()
- {
- component().removeActionListener( this );
- component().removeFocusListener( this );
- }
-
- /**
- * Called when either the selection or the contents
- * of an associated display group have changed.
- */
- public void subjectChanged ()
- {
- removeAsListener();
-
- JComboBox component = component();
- EODisplayGroup displayGroup;
- String key;
-
- // titles aspect
- displayGroup = displayGroupForAspect( TitlesAspect );
- if ( displayGroup != null )
- {
- ComboBoxModel model = component().getModel();
- // if first time, or if backing group has changed
+ * ComboBoxAssociation binds JComboBoxes to display groups. Bindings are:
+ * <ul>
+ *
+ * <li>value: optional - a property of the selected object in the display group
+ * that will be bind to the item the user selects or the text that the user
+ * enters in the field. If the value aspect is not bound, then the combo box
+ * works as an "overview" assocation and changing the selected object in the
+ * combobox will modify the selection of the display group bound to the objects
+ * or the titles display groups (in that order).</li>
+ *
+ * <li>titles: optional - a property of the objects in the bound display group
+ * that will appear in the list. If the objects aspect is not bound, this
+ * property is also used to populate the value binding. If the titles aspect
+ * itself is not bound, the items already in the combobox will be used to update
+ * the value in the selected object in the bound display group.</li>
+ *
+ * <li>objects: optional - if specified, when the user selects a title in the
+ * list, the property of the object at the corresponding index of the bound
+ * display group will be used to populate the value binding. If the objects
+ * aspect is used with an editable combo box, any value entered that does not
+ * match one of the titles in the list will produce a null value.</li>
+ *
+ * <li>enabled: optional - a boolean property of the selected object in the
+ * display group that determines whether the user can edit the field.</li>
+ *
+ * </ul>
+ *
+ * @author michael@mpowers.net
+ * @author $Author: cgruber $
+ * @version $Revision: 904 $
+ */
+public class ComboBoxAssociation extends EOAssociation implements FocusListener, ActionListener {
+ static final NSArray aspects = new NSArray(
+ new Object[] { TitlesAspect, ValueAspect, ObjectsAspect, EnabledAspect });
+ static final NSArray aspectSignatures = new NSArray(new Object[] { AttributeToOneAspectSignature,
+ AttributeToOneAspectSignature, AttributeToOneAspectSignature, AttributeToOneAspectSignature });
+ static final NSArray objectKeysTaken = new NSArray(new Object[] { "text" });
+
+ private boolean wasNull;
+ private static final String EMPTY_STRING = "";
+
+ /**
+ * Constructor specifying the object to be controlled by this association. Does
+ * not establish connection.
+ */
+ public ComboBoxAssociation(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.
+ * <ul>
+ * <li>"A" attribute: the aspect can be bound to an attribute.</li>
+ * <li>"1" to-one: the aspect can be bound to a property that returns a single
+ * object.</li>
+ * <li>"M" to-one: the aspect can be bound to a property that returns multiple
+ * objects.</li>
+ * </ul>
+ * 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 (anObject instanceof JComboBox);
+ }
+
+ /**
+ * 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. This is typically
+ * "value" or somesuch.
+ */
+ public static String primaryAspect() {
+ return ValueAspect;
+ }
+
+ /**
+ * 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() {
+ super.establishConnection();
+
+ // prepopulate titles
+ EODisplayGroup displayGroup = displayGroupForAspect(TitlesAspect);
+ if (displayGroup != null) {
+ String key = displayGroupKeyForAspect(TitlesAspect);
+ populateTitles(displayGroup, key);
+ }
+ addAsListener();
+ subjectChanged();
+ }
+
+ protected void addAsListener() {
+ component().addActionListener(this);
+ component().addFocusListener(this);
+ }
+
+ /**
+ * Breaks the connection between this association and its object. Override to
+ * stop listening for events from the object.
+ */
+ public void breakConnection() {
+ removeAsListener();
+ super.breakConnection();
+ }
+
+ protected void removeAsListener() {
+ component().removeActionListener(this);
+ component().removeFocusListener(this);
+ }
+
+ /**
+ * Called when either the selection or the contents of an associated display
+ * group have changed.
+ */
+ public void subjectChanged() {
+ removeAsListener();
+
+ JComboBox component = component();
+ EODisplayGroup displayGroup;
+ String key;
+
+ // titles aspect
+ displayGroup = displayGroupForAspect(TitlesAspect);
+ if (displayGroup != null) {
+ ComboBoxModel model = component().getModel();
+ // if first time, or if backing group has changed
// if ( ( ! ( model instanceof ComboBoxAssociationModel ) )
// || ( displayGroup.contentsChanged() ) )
// {
- key = displayGroupKeyForAspect( TitlesAspect );
- populateTitles( displayGroup, key );
+ key = displayGroupKeyForAspect(TitlesAspect);
+ populateTitles(displayGroup, key);
// }
- }
-
- // value aspect
- displayGroup = displayGroupForAspect( ValueAspect );
- if ( displayGroup != null )
- {
- key = displayGroupKeyForAspect( ValueAspect );
- component.setEnabled(
- displayGroup.enabledToSetSelectedObjectValueForKey( key ) );
- //Object value = displayGroup.selectedObjectValueForKey( key );
- Object value;
-
-
- if ( displayGroup.selectedObjects().size() > 1 )
- {
-
- Object previousValue;
-
- Iterator indexIterator = displayGroup.selectionIndexes().
- iterator();
-
- // get value for the first selected object.
- int initialIndex = ( (Integer)indexIterator.next() ).intValue();
- previousValue = displayGroup.valueForObjectAtIndex(
- initialIndex, key );
- value = null;
-
- // go through the rest of the selected objects, compare each
- // value with the previous one. continue comparing if two
- // values are equal, break the while loop if they're different.
- // the final value will be the common value of all selected objects
- // if there is one, or be blank if there is not.
- while ( indexIterator.hasNext() )
- {
- int index = ( (Integer)indexIterator.next() ).intValue();
- Object currentValue = displayGroup.valueForObjectAtIndex(
- index, key );
-
- if ( currentValue != null && !currentValue.equals( previousValue ) )
- {
- value = null;
- break;
- }
- else
- {
- // currentValue is the same as the previous one
- value = currentValue;
- }
-
- } // end while
-
- } else {
- // if there's only one object selected.
- value = displayGroup.selectedObjectValueForKey( key );
- } // end checking the size of selected objects in displayGroup
-
-
- // objects aspect
- EODisplayGroup objectsDisplayGroup =
- displayGroupForAspect( ObjectsAspect );
- if ( ( objectsDisplayGroup != null ) && ( value != null ) )
- {
- String objectKey = displayGroupKeyForAspect( ObjectsAspect );
- Object match;
- int index = NSArray.NotFound;
- int count = objectsDisplayGroup.displayedObjects().count();
- for ( int i = 0; i < count; i++ )
- {
- match = objectsDisplayGroup.valueForObjectAtIndex( i, objectKey );
- if ( value.equals( match ) )
- {
- index = i;
- }
- }
- if ( index == NSArray.NotFound )
- {
- if ( component.getSelectedItem() != null )
- {
- component.setSelectedItem( null );
- }
- }
- else
- {
- if ( component.getSelectedIndex() != index )
- {
- component.setSelectedIndex( index );
- }
- }
- }
- else
- {
- component.setSelectedItem( value );
- }
- }
- else // values aspect not bound
- {
- // use objects group if specified
- EODisplayGroup sourceGroup =
- displayGroupForAspect( ObjectsAspect );
- if ( sourceGroup == null )
- {
- // fall back on titles group
- sourceGroup = displayGroupForAspect( TitlesAspect );
- }
-
- if ( sourceGroup != null )
- {
- List selection = sourceGroup.selectionIndexes();
- if ( ( selection != null ) && ( selection.size() > 0 ) )
- {
- component.setSelectedIndex( ((Integer)selection.get(0)).intValue() );
- }
- else
- {
- // the combo box model decides what to do with this value
- component.setSelectedItem( null );
- }
- }
- }
-
- // enabled aspect
- displayGroup = displayGroupForAspect( EnabledAspect );
- if ( displayGroup != null )
- {
- key = displayGroupKeyForAspect( EnabledAspect );
- 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.isEnabled() )
- {
- component.setEnabled( converted.booleanValue() );
- }
- }
-
- addAsListener();
- }
-
- /**
- * Called to repopulate the title list from the
- * specified display group.
- */
- protected void populateTitles(
- EODisplayGroup displayGroup, String key )
- {
- component().setModel(
- new ComboBoxAssociationModel( displayGroup, key ) );
- }
-
- /**
- * Forces this association to cause the object to
- * stop editing and validate the user's input.
- * @return false if there were problems validating,
- * or true to continue.
- */
- public boolean endEditing ()
- {
- return writeValueToDisplayGroup();
- }
-
- /**
- * Writes the value currently in the component
- * to the selected object in the display group
- * bound to the value aspect.
- * @return false if there were problems validating,
- * or true to continue.
- */
- protected boolean writeValueToDisplayGroup()
- {
- JComboBox component = component();
- EODisplayGroup displayGroup;
- String key;
-
- // selected title aspect
- displayGroup = displayGroupForAspect( ValueAspect );
- if ( displayGroup != null )
- {
- key = displayGroupKeyForAspect( ValueAspect );
- Object value = null;
-
- // selected object aspect, if any
- EODisplayGroup objectsGroup =
- displayGroupForAspect( ObjectsAspect );
- if ( objectsGroup != null )
- {
- try
- {
- String objectKey = displayGroupKeyForAspect( ObjectsAspect );
- int index = component.getSelectedIndex();
- if ( index != -1 )
- {
- value = objectsGroup
- .valueForObjectAtIndex( index, objectKey );
- }
- else // selected index is -1
- {
- // the combo box is probably editable,
- // so there is no corresponding object.
- value = null;
- }
- }
- catch ( NullPointerException npe )
- {
- // catches NPE on line 436 of JComboBox.java:
- // this is a common developer error
- throw new WotonomyException( "ComboBoxAssociation: " +
- "The object in the VALUE property may not have been found in the " +
- "objects in the TITLES group.", npe );
- }
- }
- else // just use the selected item
- {
- value = component.getSelectedItem();
- }
-
- boolean returnValue = true;
- if ( displayGroup.selectedObjects().size() == 1 )
- { // displayGroup has only one object
- // only set value if changed
- Object existingValue = displayGroup.selectedObjectValueForKey( key );
- if ( value == existingValue ) return true;
- if ( ( existingValue != null ) && ( existingValue.equals( value ) ) ) return true;
-
- // value has changed: update the value.
- return displayGroup.setSelectedObjectValue( value, key );
- }
- else if ( displayGroup.selectedObjects().size() > 1 )
- {
- // displayGroup has more than one object
- Iterator selectedIterator = displayGroup.selectionIndexes().iterator();
- while ( selectedIterator.hasNext() )
- {
- int index = ( (Integer)selectedIterator.next() ).intValue();
-
- if ( !displayGroup.setValueForObjectAtIndex( value, index, key ) )
- {
- returnValue = false;
- }
- }
- return returnValue;
-
- } // end checking size of displayGroup
-
- }
- else // values aspect not bound
- {
- // use objects group if specified
- EODisplayGroup sourceGroup =
- displayGroupForAspect( ObjectsAspect );
- if ( sourceGroup == null )
- {
- // fall back on titles group
- sourceGroup = displayGroupForAspect( TitlesAspect );
- }
-
- if ( sourceGroup != null )
- {
- int index = component.getSelectedIndex();
- if ( index != -1 )
- {
- sourceGroup.setSelectionIndexes( new NSArray( new Integer( index ) ) );
- }
- else
- {
- sourceGroup.setSelectedObject( null );
- }
- return true;
- }
- }
-
- return false;
- }
-
- // interface ActionListener
-
- /**
- * Updates object on action performed.
- */
- public void actionPerformed( ActionEvent evt )
- {
- writeValueToDisplayGroup();
- }
-
- // interface FocusListener
-
- /**
- * Notifies of beginning of edit.
- */
- public void focusGained(FocusEvent evt)
- {
- Object o;
- EODisplayGroup displayGroup;
- Enumeration e = aspects().objectEnumerator();
- while ( e.hasMoreElements() )
- {
- displayGroup =
- displayGroupForAspect( e.nextElement().toString() );
- if ( displayGroup != null )
- {
- displayGroup.associationDidBeginEditing( this );
- }
- }
- }
-
- /**
- * Updates object on focus lost and notifies of end of edit.
- */
- public void focusLost(FocusEvent evt)
- {
- if ( component().isEditable() )
- {
- if ( endEditing() )
- {
- Object o;
- EODisplayGroup displayGroup;
- Enumeration e = aspects().objectEnumerator();
- while ( e.hasMoreElements() )
- {
- displayGroup =
- displayGroupForAspect( e.nextElement().toString() );
- if ( displayGroup != null )
- {
- displayGroup.associationDidEndEditing( this );
- }
- }
- }
- }
- }
-
- // convenience
-
- private JComboBox component()
- {
- return (JComboBox) object();
- }
-
- /**
- * Used as the data model for the controlled combo box.
- */
- private class ComboBoxAssociationModel extends AbstractListModel
- implements ComboBoxModel
- {
- EODisplayGroup displayGroup;
- String key;
- Object selectedItem;
-
- ComboBoxAssociationModel(
- EODisplayGroup aDisplayGroup, String aKey )
- {
- displayGroup = aDisplayGroup;
- key = aKey;
- selectedItem = null;
- }
-
- public Object getElementAt(int index)
- {
- return displayGroup.valueForObjectAtIndex( index, key );
- }
-
- public int getSize()
- {
- return displayGroup.displayedObjects().count();
- }
-
- public void setSelectedItem(Object anItem)
- { //System.out.println( "setSelectedItem: " + anItem );
- selectedItem = anItem;
-
- // must do this to notify an editable combo,
- // otherwise the wrong value appears.
- fireContentsChanged( this, -1, -1 );
- }
-
- public Object getSelectedItem()
- { //System.out.println( "getSelectedItem: " + selectedItem );
- return selectedItem;
- }
- }
+ }
+
+ // value aspect
+ displayGroup = displayGroupForAspect(ValueAspect);
+ if (displayGroup != null) {
+ key = displayGroupKeyForAspect(ValueAspect);
+ component.setEnabled(displayGroup.enabledToSetSelectedObjectValueForKey(key));
+ // Object value = displayGroup.selectedObjectValueForKey( key );
+ Object value;
+
+ if (displayGroup.selectedObjects().size() > 1) {
+
+ Object previousValue;
+
+ Iterator indexIterator = displayGroup.selectionIndexes().iterator();
+
+ // get value for the first selected object.
+ int initialIndex = ((Integer) indexIterator.next()).intValue();
+ previousValue = displayGroup.valueForObjectAtIndex(initialIndex, key);
+ value = null;
+
+ // go through the rest of the selected objects, compare each
+ // value with the previous one. continue comparing if two
+ // values are equal, break the while loop if they're different.
+ // the final value will be the common value of all selected objects
+ // if there is one, or be blank if there is not.
+ while (indexIterator.hasNext()) {
+ int index = ((Integer) indexIterator.next()).intValue();
+ Object currentValue = displayGroup.valueForObjectAtIndex(index, key);
+
+ if (currentValue != null && !currentValue.equals(previousValue)) {
+ value = null;
+ break;
+ } else {
+ // currentValue is the same as the previous one
+ value = currentValue;
+ }
+
+ } // end while
+
+ } else {
+ // if there's only one object selected.
+ value = displayGroup.selectedObjectValueForKey(key);
+ } // end checking the size of selected objects in displayGroup
+
+ // objects aspect
+ EODisplayGroup objectsDisplayGroup = displayGroupForAspect(ObjectsAspect);
+ if ((objectsDisplayGroup != null) && (value != null)) {
+ String objectKey = displayGroupKeyForAspect(ObjectsAspect);
+ Object match;
+ int index = NSArray.NotFound;
+ int count = objectsDisplayGroup.displayedObjects().count();
+ for (int i = 0; i < count; i++) {
+ match = objectsDisplayGroup.valueForObjectAtIndex(i, objectKey);
+ if (value.equals(match)) {
+ index = i;
+ }
+ }
+ if (index == NSArray.NotFound) {
+ if (component.getSelectedItem() != null) {
+ component.setSelectedItem(null);
+ }
+ } else {
+ if (component.getSelectedIndex() != index) {
+ component.setSelectedIndex(index);
+ }
+ }
+ } else {
+ component.setSelectedItem(value);
+ }
+ } else // values aspect not bound
+ {
+ // use objects group if specified
+ EODisplayGroup sourceGroup = displayGroupForAspect(ObjectsAspect);
+ if (sourceGroup == null) {
+ // fall back on titles group
+ sourceGroup = displayGroupForAspect(TitlesAspect);
+ }
+
+ if (sourceGroup != null) {
+ List selection = sourceGroup.selectionIndexes();
+ if ((selection != null) && (selection.size() > 0)) {
+ component.setSelectedIndex(((Integer) selection.get(0)).intValue());
+ } else {
+ // the combo box model decides what to do with this value
+ component.setSelectedItem(null);
+ }
+ }
+ }
+
+ // enabled aspect
+ displayGroup = displayGroupForAspect(EnabledAspect);
+ if (displayGroup != null) {
+ key = displayGroupKeyForAspect(EnabledAspect);
+ 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.isEnabled()) {
+ component.setEnabled(converted.booleanValue());
+ }
+ }
+
+ addAsListener();
+ }
+
+ /**
+ * Called to repopulate the title list from the specified display group.
+ */
+ protected void populateTitles(EODisplayGroup displayGroup, String key) {
+ component().setModel(new ComboBoxAssociationModel(displayGroup, key));
+ }
+
+ /**
+ * Forces this association to cause the object to stop editing and validate the
+ * user's input.
+ *
+ * @return false if there were problems validating, or true to continue.
+ */
+ public boolean endEditing() {
+ return writeValueToDisplayGroup();
+ }
+
+ /**
+ * Writes the value currently in the component to the selected object in the
+ * display group bound to the value aspect.
+ *
+ * @return false if there were problems validating, or true to continue.
+ */
+ protected boolean writeValueToDisplayGroup() {
+ JComboBox component = component();
+ EODisplayGroup displayGroup;
+ String key;
+
+ // selected title aspect
+ displayGroup = displayGroupForAspect(ValueAspect);
+ if (displayGroup != null) {
+ key = displayGroupKeyForAspect(ValueAspect);
+ Object value = null;
+
+ // selected object aspect, if any
+ EODisplayGroup objectsGroup = displayGroupForAspect(ObjectsAspect);
+ if (objectsGroup != null) {
+ try {
+ String objectKey = displayGroupKeyForAspect(ObjectsAspect);
+ int index = component.getSelectedIndex();
+ if (index != -1) {
+ value = objectsGroup.valueForObjectAtIndex(index, objectKey);
+ } else // selected index is -1
+ {
+ // the combo box is probably editable,
+ // so there is no corresponding object.
+ value = null;
+ }
+ } catch (NullPointerException npe) {
+ // catches NPE on line 436 of JComboBox.java:
+ // this is a common developer error
+ throw new WotonomyException(
+ "ComboBoxAssociation: " + "The object in the VALUE property may not have been found in the "
+ + "objects in the TITLES group.",
+ npe);
+ }
+ } else // just use the selected item
+ {
+ value = component.getSelectedItem();
+ }
+
+ boolean returnValue = true;
+ if (displayGroup.selectedObjects().size() == 1) { // displayGroup has only one object
+ // only set value if changed
+ Object existingValue = displayGroup.selectedObjectValueForKey(key);
+ if (value == existingValue)
+ return true;
+ if ((existingValue != null) && (existingValue.equals(value)))
+ return true;
+
+ // value has changed: update the value.
+ return displayGroup.setSelectedObjectValue(value, key);
+ } else if (displayGroup.selectedObjects().size() > 1) {
+ // displayGroup has more than one object
+ Iterator selectedIterator = displayGroup.selectionIndexes().iterator();
+ while (selectedIterator.hasNext()) {
+ int index = ((Integer) selectedIterator.next()).intValue();
+
+ if (!displayGroup.setValueForObjectAtIndex(value, index, key)) {
+ returnValue = false;
+ }
+ }
+ return returnValue;
+
+ } // end checking size of displayGroup
+
+ } else // values aspect not bound
+ {
+ // use objects group if specified
+ EODisplayGroup sourceGroup = displayGroupForAspect(ObjectsAspect);
+ if (sourceGroup == null) {
+ // fall back on titles group
+ sourceGroup = displayGroupForAspect(TitlesAspect);
+ }
+
+ if (sourceGroup != null) {
+ int index = component.getSelectedIndex();
+ if (index != -1) {
+ sourceGroup.setSelectionIndexes(new NSArray(new Integer(index)));
+ } else {
+ sourceGroup.setSelectedObject(null);
+ }
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ // interface ActionListener
+
+ /**
+ * Updates object on action performed.
+ */
+ public void actionPerformed(ActionEvent evt) {
+ writeValueToDisplayGroup();
+ }
+
+ // interface FocusListener
+
+ /**
+ * Notifies of beginning of edit.
+ */
+ public void focusGained(FocusEvent evt) {
+ Object o;
+ EODisplayGroup displayGroup;
+ Enumeration e = aspects().objectEnumerator();
+ while (e.hasMoreElements()) {
+ displayGroup = displayGroupForAspect(e.nextElement().toString());
+ if (displayGroup != null) {
+ displayGroup.associationDidBeginEditing(this);
+ }
+ }
+ }
+
+ /**
+ * Updates object on focus lost and notifies of end of edit.
+ */
+ public void focusLost(FocusEvent evt) {
+ if (component().isEditable()) {
+ if (endEditing()) {
+ Object o;
+ EODisplayGroup displayGroup;
+ Enumeration e = aspects().objectEnumerator();
+ while (e.hasMoreElements()) {
+ displayGroup = displayGroupForAspect(e.nextElement().toString());
+ if (displayGroup != null) {
+ displayGroup.associationDidEndEditing(this);
+ }
+ }
+ }
+ }
+ }
+
+ // convenience
+
+ private JComboBox component() {
+ return (JComboBox) object();
+ }
+
+ /**
+ * Used as the data model for the controlled combo box.
+ */
+ private class ComboBoxAssociationModel extends AbstractListModel implements ComboBoxModel {
+ EODisplayGroup displayGroup;
+ String key;
+ Object selectedItem;
+
+ ComboBoxAssociationModel(EODisplayGroup aDisplayGroup, String aKey) {
+ displayGroup = aDisplayGroup;
+ key = aKey;
+ selectedItem = null;
+ }
+
+ public Object getElementAt(int index) {
+ return displayGroup.valueForObjectAtIndex(index, key);
+ }
+
+ public int getSize() {
+ return displayGroup.displayedObjects().count();
+ }
+
+ public void setSelectedItem(Object anItem) { // System.out.println( "setSelectedItem: " + anItem );
+ selectedItem = anItem;
+
+ // must do this to notify an editable combo,
+ // otherwise the wrong value appears.
+ fireContentsChanged(this, -1, -1);
+ }
+
+ public Object getSelectedItem() { // System.out.println( "getSelectedItem: " + selectedItem );
+ return selectedItem;
+ }
+ }
}
/*
- * $Log$
- * Revision 1.2 2006/02/18 23:19:05 cgruber
- * Update imports and maven dependencies.
+ * $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.1 2006/02/16 13:22:22 cgruber Check in all sources in
+ * eclipse-friendly maven-enabled packages.
*
- * Revision 1.13 2004/01/28 18:34:57 mpowers
- * Better handling for enabling.
- * Now respecting enabledToSetSelectedObjectValueForKey from display group.
+ * Revision 1.13 2004/01/28 18:34:57 mpowers Better handling for enabling. Now
+ * respecting enabledToSetSelectedObjectValueForKey from display group.
*
- * Revision 1.12 2003/08/06 23:07:52 chochos
- * general code cleanup (mostly, removing unused imports)
+ * Revision 1.12 2003/08/06 23:07:52 chochos general code cleanup (mostly,
+ * removing unused imports)
*
- * Revision 1.11 2001/07/30 16:32:55 mpowers
- * Implemented support for bulk-editing. Detail associations will now
- * apply changes to all selected objects.
+ * Revision 1.11 2001/07/30 16:32:55 mpowers Implemented support for
+ * bulk-editing. Detail associations will now apply changes to all selected
+ * objects.
*
- * Revision 1.10 2001/07/23 20:17:56 mpowers
- * Now works as an overview association if the values aspect is not bound.
+ * Revision 1.10 2001/07/23 20:17:56 mpowers Now works as an overview
+ * association if the values aspect is not bound.
*
- * Revision 1.9 2001/06/30 14:57:29 mpowers
- * Removed a println.
+ * Revision 1.9 2001/06/30 14:57:29 mpowers Removed a println.
*
- * Revision 1.8 2001/06/29 22:28:19 mpowers
- * Tabs to spaces.
+ * Revision 1.8 2001/06/29 22:28:19 mpowers Tabs to spaces.
*
- * Revision 1.7 2001/06/29 22:17:31 mpowers
- * Now updating the component on establishConnection.
+ * Revision 1.7 2001/06/29 22:17:31 mpowers Now updating the component on
+ * establishConnection.
*
- * Revision 1.6 2001/05/14 15:24:49 mpowers
- * Only updating if change was made. Feels like I had fixed this here before.
+ * Revision 1.6 2001/05/14 15:24:49 mpowers Only updating if change was made.
+ * Feels like I had fixed this here before.
*
- * Revision 1.5 2001/04/09 21:41:08 mpowers
- * Fixed a bug I thought that I had fixed before.
+ * Revision 1.5 2001/04/09 21:41:08 mpowers Fixed a bug I thought that I had
+ * fixed before.
*
- * Revision 1.4 2001/03/01 20:37:17 mpowers
- * Updated docs to emphasize that titles aspect is optional.
+ * Revision 1.4 2001/03/01 20:37:17 mpowers Updated docs to emphasize that
+ * titles aspect is optional.
*
- * Revision 1.3 2001/02/17 16:52:05 mpowers
- * Changes in imports to support building with jdk1.1 collections.
+ * Revision 1.3 2001/02/17 16:52:05 mpowers Changes in imports to support
+ * building with jdk1.1 collections.
*
- * Revision 1.2 2001/01/10 17:01:08 mpowers
- * Caught a common developer error.
+ * Revision 1.2 2001/01/10 17:01:08 mpowers Caught a common developer error.
*
- * Revision 1.1.1.1 2000/12/21 15:48:43 mpowers
- * Contributing wotonomy.
+ * Revision 1.1.1.1 2000/12/21 15:48:43 mpowers Contributing wotonomy.
*
- * Revision 1.8 2000/12/20 16:25:40 michael
- * Added log to all files.
+ * Revision 1.8 2000/12/20 16:25:40 michael Added log to all files.
*
*
*/
-
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/DateAssociation.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/DateAssociation.java
index ba50879..c201ae3 100644
--- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/DateAssociation.java
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/DateAssociation.java
@@ -36,578 +36,430 @@ import net.wotonomy.ui.EOAssociation;
import net.wotonomy.ui.EODisplayGroup;
/**
-* DateAssociation binds any component that has a set and get Date methods and
-* fire actions events when the date has been changed. Bindings are:
-* <ul>
-* <li>value: a property convertable to/from a date</li>
-* <li>editable: a boolean property that determines whether
-* the user can edit the date in the component</li>
-* <li>enabled: a boolean property that determines whether
-* the component is enabled or disabled</li>
-* </ul>
-*
-* @author rob@yahoo.com
-* @version $Revision: 904 $
-*/
-public class DateAssociation extends EOAssociation
- implements ActionListener, FocusListener
-{
- static final NSArray aspects =
- new NSArray( new Object[] {
- ValueAspect, EnabledAspect, EditableAspect
- } );
- static final NSArray aspectSignatures =
- new NSArray( new Object[] {
- AttributeToOneAspectSignature,
- AttributeToOneAspectSignature,
- AttributeToOneAspectSignature
- } );
- static final NSArray objectKeysTaken =
- new NSArray( new Object[] {
- "date", "enabled", "editable"
- } );
-
- private final static NSSelector getDate =
- new NSSelector( "getDate" );
- private final static NSSelector setDate =
- new NSSelector( "setDate",
- new Class[] { Date.class } );
- private final static NSSelector addActionListener =
- new NSSelector( "addActionListener",
- new Class[] { ActionListener.class } );
- private final static NSSelector removeActionListener =
- new NSSelector( "removeActionListener",
- new Class[] { ActionListener.class } );
- private final static NSSelector addFocusListener =
- new NSSelector( "addFocusListener",
- new Class[] { FocusListener.class } );
- private final static NSSelector removeFocusListener =
- new NSSelector( "removeFocusListener",
- new Class[] { FocusListener.class } );
- private final static NSSelector setEditable =
- new NSSelector( "setEditable",
- new Class[] { boolean.class } );
+ * DateAssociation binds any component that has a set and get Date methods and
+ * fire actions events when the date has been changed. Bindings are:
+ * <ul>
+ * <li>value: a property convertable to/from a date</li>
+ * <li>editable: a boolean property that determines whether the user can edit
+ * the date in the component</li>
+ * <li>enabled: a boolean property that determines whether the component is
+ * enabled or disabled</li>
+ * </ul>
+ *
+ * @author rob@yahoo.com
+ * @version $Revision: 904 $
+ */
+public class DateAssociation extends EOAssociation implements ActionListener, FocusListener {
+ static final NSArray aspects = new NSArray(new Object[] { ValueAspect, EnabledAspect, EditableAspect });
+ static final NSArray aspectSignatures = new NSArray(new Object[] { AttributeToOneAspectSignature,
+ AttributeToOneAspectSignature, AttributeToOneAspectSignature });
+ static final NSArray objectKeysTaken = new NSArray(new Object[] { "date", "enabled", "editable" });
+
+ private final static NSSelector getDate = new NSSelector("getDate");
+ private final static NSSelector setDate = new NSSelector("setDate", new Class[] { Date.class });
+ private final static NSSelector addActionListener = new NSSelector("addActionListener",
+ new Class[] { ActionListener.class });
+ private final static NSSelector removeActionListener = new NSSelector("removeActionListener",
+ new Class[] { ActionListener.class });
+ private final static NSSelector addFocusListener = new NSSelector("addFocusListener",
+ new Class[] { FocusListener.class });
+ private final static NSSelector removeFocusListener = new NSSelector("removeFocusListener",
+ new Class[] { FocusListener.class });
+ private final static NSSelector setEditable = new NSSelector("setEditable", new Class[] { boolean.class });
// dirty handling
private boolean needsUpdate;
- private Date nullValue; // placeholder for null value flag
-
- /**
- * Constructor specifying the object to be controlled by this
- * association. Does not establish connection.
- */
- public DateAssociation ( Object anObject )
- {
- super( anObject );
+ private Date nullValue; // placeholder for null value flag
+
+ /**
+ * Constructor specifying the object to be controlled by this association. Does
+ * not establish connection.
+ */
+ public DateAssociation(Object anObject) {
+ super(anObject);
needsUpdate = false;
- nullValue = null;
- }
-
- /**
- * 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. <ul>
- * <li>"A" attribute: the aspect can be bound to
- * an attribute.</li>
- * <li>"1" to-one: the aspect can be bound to a
- * property that returns a single object.</li>
- * <li>"M" to-one: the aspect can be bound to a
- * property that returns multiple objects.</li>
- * </ul>
- * 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 setDate.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. This is typically "value" or somesuch.
- */
- public static String primaryAspect ()
- {
- return ValueAspect;
- }
-
- /**
- * 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. This implementation
- * attempts to add this class as an ActionListener
- * and Focus Listener to the specified object.
- */
- public void establishConnection ()
- {
+ nullValue = null;
+ }
+
+ /**
+ * 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.
+ * <ul>
+ * <li>"A" attribute: the aspect can be bound to an attribute.</li>
+ * <li>"1" to-one: the aspect can be bound to a property that returns a single
+ * object.</li>
+ * <li>"M" to-one: the aspect can be bound to a property that returns multiple
+ * objects.</li>
+ * </ul>
+ * 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 setDate.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. This is typically
+ * "value" or somesuch.
+ */
+ public static String primaryAspect() {
+ return ValueAspect;
+ }
+
+ /**
+ * 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.
+ * This implementation attempts to add this class as an ActionListener and Focus
+ * Listener to the specified object.
+ */
+ public void establishConnection() {
Object component = object();
- try
- {
- if ( addActionListener.implementedByObject( component ) )
- {
- addActionListener.invoke( component, this );
+ try {
+ if (addActionListener.implementedByObject(component)) {
+ addActionListener.invoke(component, this);
}
- if ( addFocusListener.implementedByObject( component ) )
- {
- addFocusListener.invoke( component, this );
+ if (addFocusListener.implementedByObject(component)) {
+ addFocusListener.invoke(component, this);
}
- }
- catch ( Exception exc )
- {
- throw new WotonomyException(
- "Error while establishing connection", exc );
+ } catch (Exception exc) {
+ throw new WotonomyException("Error while establishing connection", exc);
}
- super.establishConnection();
+ super.establishConnection();
// forces update from bindings
subjectChanged();
- }
-
- /**
- * Breaks the connection between this association and
- * its object. Override to stop listening for events
- * from the object.
- */
- public void breakConnection ()
- {
+ }
+
+ /**
+ * Breaks the connection between this association and its object. Override to
+ * stop listening for events from the object.
+ */
+ public void breakConnection() {
Object component = object();
- try
- {
- if ( removeActionListener.implementedByObject( component ) )
- {
- removeActionListener.invoke( component, this );
+ try {
+ if (removeActionListener.implementedByObject(component)) {
+ removeActionListener.invoke(component, this);
}
- if ( removeFocusListener.implementedByObject( component ) )
- {
- removeFocusListener.invoke( component, this );
+ if (removeFocusListener.implementedByObject(component)) {
+ removeFocusListener.invoke(component, this);
}
+ } catch (Exception exc) {
+ throw new WotonomyException("Error while breaking connection", exc);
}
- catch ( Exception exc )
- {
- throw new WotonomyException(
- "Error while breaking connection", exc );
- }
- super.breakConnection();
- }
-
- /**
- * Called when either the selection or the contents
- * of an associated display group have changed.
- */
- public void subjectChanged ()
- {
+ super.breakConnection();
+ }
+
+ /**
+ * Called when either the selection or the contents of an associated display
+ * group have changed.
+ */
+ public void subjectChanged() {
Object component = object();
EODisplayGroup displayGroup;
String key;
Object value;
// value aspect
- displayGroup = displayGroupForAspect( ValueAspect );
- if ( displayGroup != null )
- {
- key = displayGroupKeyForAspect( ValueAspect );
- if ( component instanceof Component )
- {
- ((Component)component).setEnabled(
- displayGroup.enabledToSetSelectedObjectValueForKey( key ) );
- }
- if ( displayGroup.selectedObjects().size() > 1 )
- {
- // if there're more than one object selected, set
- // the value to blank for all of them.
- Object previousValue;
-
- Iterator indexIterator = displayGroup.selectionIndexes().
- iterator();
-
- // get value for the first selected object.
- int initialIndex = ( (Integer)indexIterator.next() ).intValue();
- previousValue = displayGroup.valueForObjectAtIndex(
- initialIndex, key );
- value = null;
-
- // go through the rest of the selected objects, compare each
- // value with the previous one. continue comparing if two
- // values are equal, break the while loop if they're different.
- // the final value will be the common value of all selected objects
- // if there is one, or be blank if there is not.
- while ( indexIterator.hasNext() )
- {
- int index = ( (Integer)indexIterator.next() ).intValue();
- Object currentValue = displayGroup.valueForObjectAtIndex(
- index, key );
- if ( currentValue != null && !currentValue.equals( previousValue ) )
- {
- value = null;
- break;
- }
- else
- {
- // currentValue is the same as the previous one
- value = currentValue;
- }
-
- } // end while
-
- } else {
-
- value = displayGroup.selectedObjectValueForKey( key );
- } // end checking size of displayGroup
-
- // convert value to date
- try
- {
- Date dateValue = null;
- // (Date) ValueConverter.convertObjectToClass( value, Date.class );
-
- if ( value instanceof Date )
- {
- dateValue = (Date) value;
- }
-
- if ( ( dateValue == null ) && ( value instanceof Calendar ) )
- {
- dateValue = ( ( Calendar )value ).getTime();
- }
-
- if ( dateValue == null )
- {
- // current time (placeholder)
- nullValue = new Date();
- dateValue = nullValue;
- }
- else
- {
- nullValue = null;
- }
-
- if ( !dateValue.equals( getDate.invoke( component ) ) )
- { // No need to update if there is no change.
- setDate.invoke( component, dateValue );
- needsUpdate = false;
- }
-
- }
- catch ( Exception exc )
- {
- throw new WotonomyException(
- "Error while updating component connection", exc );
- }
+ displayGroup = displayGroupForAspect(ValueAspect);
+ if (displayGroup != null) {
+ key = displayGroupKeyForAspect(ValueAspect);
+ if (component instanceof Component) {
+ ((Component) component).setEnabled(displayGroup.enabledToSetSelectedObjectValueForKey(key));
+ }
+ if (displayGroup.selectedObjects().size() > 1) {
+ // if there're more than one object selected, set
+ // the value to blank for all of them.
+ Object previousValue;
+
+ Iterator indexIterator = displayGroup.selectionIndexes().iterator();
+
+ // get value for the first selected object.
+ int initialIndex = ((Integer) indexIterator.next()).intValue();
+ previousValue = displayGroup.valueForObjectAtIndex(initialIndex, key);
+ value = null;
+
+ // go through the rest of the selected objects, compare each
+ // value with the previous one. continue comparing if two
+ // values are equal, break the while loop if they're different.
+ // the final value will be the common value of all selected objects
+ // if there is one, or be blank if there is not.
+ while (indexIterator.hasNext()) {
+ int index = ((Integer) indexIterator.next()).intValue();
+ Object currentValue = displayGroup.valueForObjectAtIndex(index, key);
+ if (currentValue != null && !currentValue.equals(previousValue)) {
+ value = null;
+ break;
+ } else {
+ // currentValue is the same as the previous one
+ value = currentValue;
+ }
+
+ } // end while
+
+ } else {
+
+ value = displayGroup.selectedObjectValueForKey(key);
+ } // end checking size of displayGroup
+
+ // convert value to date
+ try {
+ Date dateValue = null;
+ // (Date) ValueConverter.convertObjectToClass( value, Date.class );
+
+ if (value instanceof Date) {
+ dateValue = (Date) value;
+ }
+
+ if ((dateValue == null) && (value instanceof Calendar)) {
+ dateValue = ((Calendar) value).getTime();
+ }
+
+ if (dateValue == null) {
+ // current time (placeholder)
+ nullValue = new Date();
+ dateValue = nullValue;
+ } else {
+ nullValue = null;
+ }
+
+ if (!dateValue.equals(getDate.invoke(component))) { // No need to update if there is no change.
+ setDate.invoke(component, dateValue);
+ needsUpdate = false;
+ }
+
+ } catch (Exception exc) {
+ throw new WotonomyException("Error while updating component connection", exc);
+ }
}
// enabled aspect
- displayGroup = displayGroupForAspect( EnabledAspect );
- key = displayGroupKeyForAspect( EnabledAspect );
- if ( ( ( displayGroup != null ) || ( key != null ) )
- && ( component instanceof Component ) )
- {
- if ( displayGroup != null )
- {
- value =
- displayGroup.selectedObjectValueForKey( key );
- }
- else
- {
+ displayGroup = displayGroupForAspect(EnabledAspect);
+ key = displayGroupKeyForAspect(EnabledAspect);
+ if (((displayGroup != null) || (key != null)) && (component instanceof Component)) {
+ if (displayGroup != null) {
+ value = displayGroup.selectedObjectValueForKey(key);
+ } else {
// treat bound key without display group as a value
value = 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() );
- }
+ 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());
+ }
}
// editable aspect
- displayGroup = displayGroupForAspect( EditableAspect );
- key = displayGroupKeyForAspect( EditableAspect );
- if ( ( ( displayGroup != null ) || ( key != null ) )
- && ( setEditable.implementedByObject( component ) ) )
- {
- try
- {
- if ( displayGroup != null )
- {
- value =
- displayGroup.selectedObjectValueForKey( key );
- }
- else
- {
- // treat bound key without display group as a value
- value = key;
- }
- Boolean converted = (Boolean)
- ValueConverter.convertObjectToClass(
- value, Boolean.class );
-
- if ( converted != null )
- {
- setEditable.invoke( component, converted );
- }
- }
- catch ( Exception exc )
- {
- throw new WotonomyException(
- "Error while updating component connection (editable aspect)", exc );
+ displayGroup = displayGroupForAspect(EditableAspect);
+ key = displayGroupKeyForAspect(EditableAspect);
+ if (((displayGroup != null) || (key != null)) && (setEditable.implementedByObject(component))) {
+ try {
+ if (displayGroup != null) {
+ value = displayGroup.selectedObjectValueForKey(key);
+ } else {
+ // treat bound key without display group as a value
+ value = key;
+ }
+ Boolean converted = (Boolean) ValueConverter.convertObjectToClass(value, Boolean.class);
+
+ if (converted != null) {
+ setEditable.invoke(component, converted);
+ }
+ } catch (Exception exc) {
+ throw new WotonomyException("Error while updating component connection (editable aspect)", exc);
}
}
- }
-
- /**
- * Forces this association to cause the object to
- * stop editing and validate the user's input.
- * @return false if there were problems validating,
- * or true to continue.
- */
- public boolean endEditing ()
- {
+ }
+
+ /**
+ * Forces this association to cause the object to stop editing and validate the
+ * user's input.
+ *
+ * @return false if there were problems validating, or true to continue.
+ */
+ public boolean endEditing() {
return writeValueToDisplayGroup();
- }
+ }
/**
- * Writes the value currently in the component
- * to the selected object in the display group
- * bound to the value aspect.
- * @return false if there were problems validating,
- * or true to continue.
- */
- protected boolean writeValueToDisplayGroup()
- {
- if ( !needsUpdate ) return true;
-
- EODisplayGroup displayGroup =
- displayGroupForAspect( ValueAspect );
- if ( displayGroup != null )
- {
- String key = displayGroupKeyForAspect( ValueAspect );
- Object component = object();
- Object value = null;
- try
- {
- if ( getDate.implementedByObject( component ) )
- {
- value = getDate.invoke( component );
- }
- if ( nullValue != null )
- {
- if ( nullValue.equals( value ) )
- {
- value = null;
- }
- }
- }
- catch ( Exception exc )
- {
- throw new WotonomyException(
- "Error updating display group", exc );
- }
-
- needsUpdate = false;
-
- boolean returnValue = true;
- Iterator selectedIterator = displayGroup.selectionIndexes().iterator();
- while ( selectedIterator.hasNext() )
- {
- int index = ( (Integer)selectedIterator.next() ).intValue();
-
- if ( !displayGroup.setValueForObjectAtIndex( value, index, key ) )
- {
- returnValue = false;
- }
- }
- return returnValue;
-
- }
- return false;
+ * Writes the value currently in the component to the selected object in the
+ * display group bound to the value aspect.
+ *
+ * @return false if there were problems validating, or true to continue.
+ */
+ protected boolean writeValueToDisplayGroup() {
+ if (!needsUpdate)
+ return true;
+
+ EODisplayGroup displayGroup = displayGroupForAspect(ValueAspect);
+ if (displayGroup != null) {
+ String key = displayGroupKeyForAspect(ValueAspect);
+ Object component = object();
+ Object value = null;
+ try {
+ if (getDate.implementedByObject(component)) {
+ value = getDate.invoke(component);
+ }
+ if (nullValue != null) {
+ if (nullValue.equals(value)) {
+ value = null;
+ }
+ }
+ } catch (Exception exc) {
+ throw new WotonomyException("Error updating display group", exc);
+ }
+
+ needsUpdate = false;
+
+ boolean returnValue = true;
+ Iterator selectedIterator = displayGroup.selectionIndexes().iterator();
+ while (selectedIterator.hasNext()) {
+ int index = ((Integer) selectedIterator.next()).intValue();
+
+ if (!displayGroup.setValueForObjectAtIndex(value, index, key)) {
+ returnValue = false;
+ }
+ }
+ return returnValue;
+
+ }
+ return false;
}
- // interface ActionListener
+ // interface ActionListener
/**
- * Updates object on action performed.
- */
- public void actionPerformed( ActionEvent evt )
- {
- needsUpdate = true;
- writeValueToDisplayGroup(); // TODO: Should we do this here or on focus lost?
+ * Updates object on action performed.
+ */
+ public void actionPerformed(ActionEvent evt) {
+ needsUpdate = true;
+ writeValueToDisplayGroup(); // TODO: Should we do this here or on focus lost?
}
- // interface FocusListener
+ // interface FocusListener
/**
- * Notifies of beginning of edit.
- */
- public void focusGained(FocusEvent evt)
- {
+ * Notifies of beginning of edit.
+ */
+ public void focusGained(FocusEvent evt) {
Object o;
EODisplayGroup displayGroup;
- Enumeration e = aspects().objectEnumerator();
- while ( e.hasMoreElements() )
- {
- displayGroup =
- displayGroupForAspect( e.nextElement().toString() );
- if ( displayGroup != null )
- {
- displayGroup.associationDidBeginEditing( this );
+ Enumeration e = aspects().objectEnumerator();
+ while (e.hasMoreElements()) {
+ displayGroup = displayGroupForAspect(e.nextElement().toString());
+ if (displayGroup != null) {
+ displayGroup.associationDidBeginEditing(this);
}
}
- }
+ }
/**
- * Updates object on focus lost and notifies of end of edit.
- */
- public void focusLost(FocusEvent evt)
- {
- if ( endEditing() )
- {
+ * Updates object on focus lost and notifies of end of edit.
+ */
+ public void focusLost(FocusEvent evt) {
+ if (endEditing()) {
Object o;
EODisplayGroup displayGroup;
Enumeration e = aspects().objectEnumerator();
- while ( e.hasMoreElements() )
- {
- displayGroup =
- displayGroupForAspect( e.nextElement().toString() );
- if ( displayGroup != null )
- {
- displayGroup.associationDidEndEditing( this );
+ while (e.hasMoreElements()) {
+ displayGroup = displayGroupForAspect(e.nextElement().toString());
+ if (displayGroup != null) {
+ displayGroup.associationDidEndEditing(this);
}
}
- }
- else
- {
+ } else {
// probably should notify of a validation error here,
// but how to also handle actionPerformed without copying code?
-/*
- Object value = null;
- try
- {
- if ( getText.implementedByObject( object() ) )
- {
- value = getText.invoke( object() );
- }
- }
- catch ( Exception exc )
- {
- throw new WotonomyException(
- "Error updating display group", exc );
- }
-
- EODisplayGroup displayGroup =
- displayGroupForAspect( ValueAspect );
- String key = displayGroupKeyForAspect( ValueAspect );
- if ( displayGroup != null )
- {
- if ( displayGroup.associationFailedToValidateValue(
- this, (String) value, key, object(),
- "That format was not recognized." ) )
- {
- new net.wotonomy.ui.swing.util.StackTraceInspector();
- }
- if ( object() instanceof Component )
- {
- ((Component)object()).requestFocus();
- }
- }
-*/
+ /*
+ * Object value = null; try { if ( getText.implementedByObject( object() ) ) {
+ * value = getText.invoke( object() ); } } catch ( Exception exc ) { throw new
+ * WotonomyException( "Error updating display group", exc ); }
+ *
+ * EODisplayGroup displayGroup = displayGroupForAspect( ValueAspect ); String
+ * key = displayGroupKeyForAspect( ValueAspect ); if ( displayGroup != null ) {
+ * if ( displayGroup.associationFailedToValidateValue( this, (String) value,
+ * key, object(), "That format was not recognized." ) ) { new
+ * net.wotonomy.ui.swing.util.StackTraceInspector(); } if ( object() instanceof
+ * Component ) { ((Component)object()).requestFocus(); } }
+ */
}
- }
+ }
}
/*
- * $Log$
- * Revision 1.2 2006/02/18 23:19:05 cgruber
- * Update imports and maven dependencies.
+ * $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.1 2006/02/16 13:22:22 cgruber Check in all sources in
+ * eclipse-friendly maven-enabled packages.
*
- * Revision 1.7 2004/01/28 18:34:57 mpowers
- * Better handling for enabling.
- * Now respecting enabledToSetSelectedObjectValueForKey from display group.
+ * Revision 1.7 2004/01/28 18:34:57 mpowers Better handling for enabling. Now
+ * respecting enabledToSetSelectedObjectValueForKey from display group.
*
- * Revision 1.6 2003/08/06 23:07:52 chochos
- * general code cleanup (mostly, removing unused imports)
+ * Revision 1.6 2003/08/06 23:07:52 chochos general code cleanup (mostly,
+ * removing unused imports)
*
- * Revision 1.5 2001/07/30 16:32:55 mpowers
- * Implemented support for bulk-editing. Detail associations will now
- * apply changes to all selected objects.
+ * Revision 1.5 2001/07/30 16:32:55 mpowers Implemented support for
+ * bulk-editing. Detail associations will now apply changes to all selected
+ * objects.
*
- * Revision 1.4 2001/02/17 17:23:49 mpowers
- * More changes to support compiling with jdk1.1 collections.
+ * Revision 1.4 2001/02/17 17:23:49 mpowers More changes to support compiling
+ * with jdk1.1 collections.
*
- * Revision 1.3 2001/02/17 16:52:05 mpowers
- * Changes in imports to support building with jdk1.1 collections.
+ * Revision 1.3 2001/02/17 16:52:05 mpowers Changes in imports to support
+ * building with jdk1.1 collections.
*
- * Revision 1.2 2001/01/17 16:25:26 mpowers
- * Now catching null values from data object.
+ * Revision 1.2 2001/01/17 16:25:26 mpowers Now catching null values from data
+ * object.
*
- * Revision 1.1 2001/01/10 22:26:32 mpowers
- * Contributing DateAssociation.
+ * Revision 1.1 2001/01/10 22:26:32 mpowers Contributing DateAssociation.
*
- * Revision 1.1 2001/01/10 21:30:27 rglista
- * Initial checkin
+ * Revision 1.1 2001/01/10 21:30:27 rglista Initial checkin
*
*
*/
-
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/DisplayGroupActionAssociation.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/DisplayGroupActionAssociation.java
index 290480d..f891ede 100644
--- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/DisplayGroupActionAssociation.java
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/DisplayGroupActionAssociation.java
@@ -27,108 +27,82 @@ import net.wotonomy.foundation.internal.WotonomyException;
import net.wotonomy.ui.EODisplayGroup;
/**
-* ActionAssociation binds any ActionEvent broadcaster
-* (typically Buttons and the like) to a display group,
-* but invokes actions directly on the bound display
-* group rather than the selected objects.
-* Bindings are:
-* <ul>
-* <li>action: a method to be invoked on the bound display group.
-* If the argument aspect is bound, the method must take
-* one argument. Otherwise, the method must take no arguments.</li>
-* <li>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</li>
-* <li>enabled: a boolean property that determines whether
-* the controlled component is enabled</li>
-* <li>visible: a boolean property that determines whether
-* the controlled component is visible</li>
-* </ul>
-*
-* @author michael@mpowers.net
-* @author $Author: cgruber $
-* @version $Revision: 904 $
-*/
-public class DisplayGroupActionAssociation extends ActionAssociation
-{
- 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 DisplayGroupActionAssociation ( Object anObject )
- {
- super( anObject );
- }
-
- // interface ActionListener
-
- public void actionPerformed( ActionEvent evt )
- {
+ * ActionAssociation binds any ActionEvent broadcaster (typically Buttons and
+ * the like) to a display group, but invokes actions directly on the bound
+ * display group rather than the selected objects. Bindings are:
+ * <ul>
+ * <li>action: a method to be invoked on the bound display group. If the
+ * argument aspect is bound, the method must take one argument. Otherwise, the
+ * method must take no arguments.</li>
+ * <li>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</li>
+ * <li>enabled: a boolean property that determines whether the controlled
+ * component is enabled</li>
+ * <li>visible: a boolean property that determines whether the controlled
+ * component is visible</li>
+ * </ul>
+ *
+ * @author michael@mpowers.net
+ * @author $Author: cgruber $
+ * @version $Revision: 904 $
+ */
+public class DisplayGroupActionAssociation extends ActionAssociation {
+ 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 DisplayGroupActionAssociation(Object anObject) {
+ super(anObject);
+ }
+
+ // interface ActionListener
+
+ public void actionPerformed(ActionEvent evt) {
EODisplayGroup actionDisplayGroup = null;
String actionKey = null;
-
+
// action aspect
- actionDisplayGroup = displayGroupForAspect( ActionAspect );
- if ( actionDisplayGroup != null )
- {
- actionKey = displayGroupKeyForAspect( ActionAspect );
+ actionDisplayGroup = displayGroupForAspect(ActionAspect);
+ if (actionDisplayGroup != null) {
+ actionKey = displayGroupKeyForAspect(ActionAspect);
- //TODO: argument aspect not implemented
+ // TODO: argument aspect not implemented
- try
- {
- NSSelector.invoke( actionKey, actionDisplayGroup );
- }
- catch ( Exception exc )
- {
- throw new WotonomyException( "DisplayGroupActionAssociation: "
- + "error invoking action: " + actionKey, exc );
+ try {
+ NSSelector.invoke(actionKey, actionDisplayGroup);
+ } catch (Exception exc) {
+ throw new WotonomyException("DisplayGroupActionAssociation: " + "error invoking action: " + actionKey,
+ exc);
}
}
}
-
+
}
/*
- * $Log$
- * Revision 1.2 2006/02/18 23:19:05 cgruber
- * Update imports and maven dependencies.
+ * $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.1 2006/02/16 13:22:22 cgruber Check in all sources in
+ * eclipse-friendly maven-enabled packages.
*
- * Revision 1.2 2003/08/06 23:07:52 chochos
- * general code cleanup (mostly, removing unused imports)
+ * Revision 1.2 2003/08/06 23:07:52 chochos general code cleanup (mostly,
+ * removing unused imports)
*
- * Revision 1.1.1.1 2000/12/21 15:48:46 mpowers
- * Contributing wotonomy.
+ * Revision 1.1.1.1 2000/12/21 15:48:46 mpowers Contributing wotonomy.
*
- * Revision 1.3 2000/12/20 16:25:40 michael
- * Added log to all files.
+ * Revision 1.3 2000/12/20 16:25:40 michael Added log to all files.
*
*
*/
-
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/DisplayGroupInspector.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/DisplayGroupInspector.java
index c8ecd36..d782252 100644
--- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/DisplayGroupInspector.java
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/DisplayGroupInspector.java
@@ -35,86 +35,75 @@ import net.wotonomy.ui.swing.util.ObjectInspector;
import net.wotonomy.ui.swing.util.WindowUtilities;
/**
-* The DisplayGroupInspector displays a JFrame that
-* shows allows you to view and manipulate a display group.
-*
-* @author michael@mpowers.net
-* @version $Revision: 904 $
-*/
+ * The DisplayGroupInspector displays a JFrame that shows allows you to view and
+ * manipulate a display group.
+ *
+ * @author michael@mpowers.net
+ * @version $Revision: 904 $
+ */
-public class DisplayGroupInspector
-{
- protected JList list;
- protected EODisplayGroup displayGroup;
-
-/**
-* Displays and manipulats the specified display group.
-*/
- public DisplayGroupInspector( EODisplayGroup aDisplayGroup )
- {
- displayGroup = aDisplayGroup;
-
- list = new JList();
- list.addMouseListener( new MouseAdapter()
- {
- public void mouseClicked( MouseEvent e )
- {
- if ( e.getClickCount() == 2 )
- {
- Object selection = displayGroup.selectedObject();
- if ( selection != null )
- {
- new ObjectInspector( selection );
- }
- }
- }
- } );
-
- EOAssociation assoc = new ListAssociation( list );
- assoc.bindAspect( EOAssociation.TitlesAspect, displayGroup, "" );
- assoc.establishConnection();
-
- initLayout();
-
- }
-
- protected void initLayout()
- {
- JPanel panel = new JPanel();
- panel.setLayout( new BorderLayout() );
- panel.setBorder( new EmptyBorder( 10, 10, 10, 10 ) );
-
- JScrollPane scrollPane = new JScrollPane( list );
- scrollPane.setPreferredSize( new Dimension( 200, 200 ) );
- panel.add( scrollPane, BorderLayout.CENTER );
-
- JFrame window = new JFrame();
- window.setTitle( "Display Group Inspector" );
- window.getContentPane().add( panel );
-
- window.pack();
- WindowUtilities.cascade( window );
- window.show();
- }
+public class DisplayGroupInspector {
+ protected JList list;
+ protected EODisplayGroup displayGroup;
+
+ /**
+ * Displays and manipulats the specified display group.
+ */
+ public DisplayGroupInspector(EODisplayGroup aDisplayGroup) {
+ displayGroup = aDisplayGroup;
+
+ list = new JList();
+ list.addMouseListener(new MouseAdapter() {
+ public void mouseClicked(MouseEvent e) {
+ if (e.getClickCount() == 2) {
+ Object selection = displayGroup.selectedObject();
+ if (selection != null) {
+ new ObjectInspector(selection);
+ }
+ }
+ }
+ });
+
+ EOAssociation assoc = new ListAssociation(list);
+ assoc.bindAspect(EOAssociation.TitlesAspect, displayGroup, "");
+ assoc.establishConnection();
+
+ initLayout();
+
+ }
+
+ protected void initLayout() {
+ JPanel panel = new JPanel();
+ panel.setLayout(new BorderLayout());
+ panel.setBorder(new EmptyBorder(10, 10, 10, 10));
+
+ JScrollPane scrollPane = new JScrollPane(list);
+ scrollPane.setPreferredSize(new Dimension(200, 200));
+ panel.add(scrollPane, BorderLayout.CENTER);
+
+ JFrame window = new JFrame();
+ window.setTitle("Display Group Inspector");
+ window.getContentPane().add(panel);
+
+ window.pack();
+ WindowUtilities.cascade(window);
+ window.show();
+ }
}
-
+
/*
- * $Log$
- * Revision 1.2 2006/02/18 23:19:05 cgruber
- * Update imports and maven dependencies.
+ * $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.1 2006/02/16 13:22:22 cgruber Check in all sources in
+ * eclipse-friendly maven-enabled packages.
*
- * Revision 1.3 2003/08/06 23:07:52 chochos
- * general code cleanup (mostly, removing unused imports)
+ * Revision 1.3 2003/08/06 23:07:52 chochos general code cleanup (mostly,
+ * removing unused imports)
*
- * Revision 1.2 2003/06/26 23:28:00 mpowers
- * Added double click.
+ * Revision 1.2 2003/06/26 23:28:00 mpowers Added double click.
*
- * Revision 1.1 2001/05/29 19:57:47 mpowers
- * Added some neglected files.
+ * Revision 1.1 2001/05/29 19:57:47 mpowers Added some neglected files.
*
*
*/
-
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/DisplayGroupNode.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/DisplayGroupNode.java
index 1756285..d530f36 100644
--- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/DisplayGroupNode.java
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/DisplayGroupNode.java
@@ -46,901 +46,727 @@ import net.wotonomy.ui.EODisplayGroup;
import net.wotonomy.ui.swing.TreeModelAssociation.DelegatingTreeDataSource;
/**
-* DisplayGroupNodes are used as nodes in the
-* TreeModelAssociation's implementation of TreeModel,
-* and is tightly coupled with TreeModelAssociation
-* and MasterDetailAssociation. <br><br>
-*
-* Even though it is no longer package access,
-* don't rely on this class because we want to
-* have the option of completely replacing this
-* approach in the future.
-*
-* @author michael@mpowers.net
-* @author $Author: cgruber $
-* @version $Revision: 904 $
-*/
- abstract public class DisplayGroupNode
- extends EODisplayGroup
- {
- protected TreeModelAssociation parentAssociation;
- protected EODelayedObserver targetObserver;
- protected NSMutableDictionary childNodes;
- protected EODisplayGroup parentGroup;
- protected Object target;
- protected boolean isFetched;
- protected boolean isFetchNeeded;
- protected boolean useParentOrderings;
- protected boolean useParentQualifier;
-
- /**
- * Constructor for all nodes.
- * Root node must have a null target.
- */
- public DisplayGroupNode(
- TreeModelAssociation aParentAssociation,
- EODisplayGroup aParentGroup,
- Object aTarget )
- {
+ * DisplayGroupNodes are used as nodes in the TreeModelAssociation's
+ * implementation of TreeModel, and is tightly coupled with TreeModelAssociation
+ * and MasterDetailAssociation. <br>
+ * <br>
+ *
+ * Even though it is no longer package access, don't rely on this class because
+ * we want to have the option of completely replacing this approach in the
+ * future.
+ *
+ * @author michael@mpowers.net
+ * @author $Author: cgruber $
+ * @version $Revision: 904 $
+ */
+abstract public class DisplayGroupNode extends EODisplayGroup {
+ protected TreeModelAssociation parentAssociation;
+ protected EODelayedObserver targetObserver;
+ protected NSMutableDictionary childNodes;
+ protected EODisplayGroup parentGroup;
+ protected Object target;
+ protected boolean isFetched;
+ protected boolean isFetchNeeded;
+ protected boolean useParentOrderings;
+ protected boolean useParentQualifier;
+
+ /**
+ * Constructor for all nodes. Root node must have a null target.
+ */
+ public DisplayGroupNode(TreeModelAssociation aParentAssociation, EODisplayGroup aParentGroup, Object aTarget) {
//new net.wotonomy.ui.swing.util.StackTraceInspector( ""+aTarget );
//System.out.println( "DisplayGroupNode.new: " + aTarget );
- parentAssociation = aParentAssociation;
- target = null;
- targetObserver = null;
- parentGroup = aParentGroup;
- childNodes = new NSMutableDictionary();
- isFetched = false;
- isFetchNeeded = false;
- useParentOrderings = true;
- useParentQualifier = true;
-
- EODataSource parentSource = null;
- if ( parentGroup != null )
- {
- parentSource = parentGroup.dataSource();
- }
- else
- if ( parentAssociation.titlesDisplayGroup != null )
- {
- parentSource = parentAssociation.titlesDisplayGroup.dataSource();
- }
-
- // create child datasource
- if ( aTarget != null ) // not root node
- {
- if ( parentAssociation.childrenKey != null )
- {
- if ( parentSource == null )
- {
- throw new WotonomyException(
- "Need a data source when children aspect is bound." );
- }
-
- NSArray displayedObjects = parentGroup.displayedObjects();
- EODataSource childSource = parentSource.dataSourceQualifiedByKey(
- parentAssociation.childrenKey );
- childSource.qualifyWithRelationshipKey(
- parentAssociation.childrenKey, aTarget );
-
- // create new display group using child data source
- this.setDataSource( childSource );
-
- // establish observer for target object
- setTarget( aTarget );
- }
- else // only titles is bound
- {
- // establish observer for target object
- setTarget( aTarget );
-
- setDataSource( new PropertyDataSource()
- {
- public NSArray fetchObjects()
- {
- return new NSArray();
- }
- } );
- }
- }
- else // else root node
- {
- // root node uses PropertyDataSource by default
- if ( parentSource == null )
- {
- setDataSource( new PropertyDataSource()
- {
- public NSArray fetchObjects()
- {
- if ( parentGroup != null )
- {
- return parentGroup.displayedObjects();
- }
- return null;
- }
- } );
- }
- else
- {
- // root node uses parent source directly
- setDataSource( parentSource );
- }
- }
- }
-
- /**
- * Overridden to unregister as an editor of the editing context,
- * since we don't directly present a user interface.
- */
- public void setDataSource ( EODataSource aDataSource )
- {
- super.setDataSource( aDataSource );
- if ( ( aDataSource != null )
- && ( aDataSource.editingContext() != null ) )
- {
- aDataSource.editingContext().removeEditor( this );
- }
- }
-
- /**
- * Returns whether the node should call fetch().
- */
- protected boolean isFetched()
- {
- if ( isFetchNeeded() )
- {
- setFetchNeeded( false );
- fetch();
- }
- return isFetched;
- }
-
- /**
- * Sets whether the node should call fetch().
- */
- protected void setFetched( boolean fetched )
- {
+ parentAssociation = aParentAssociation;
+ target = null;
+ targetObserver = null;
+ parentGroup = aParentGroup;
+ childNodes = new NSMutableDictionary();
+ isFetched = false;
+ isFetchNeeded = false;
+ useParentOrderings = true;
+ useParentQualifier = true;
+
+ EODataSource parentSource = null;
+ if (parentGroup != null) {
+ parentSource = parentGroup.dataSource();
+ } else if (parentAssociation.titlesDisplayGroup != null) {
+ parentSource = parentAssociation.titlesDisplayGroup.dataSource();
+ }
+
+ // create child datasource
+ if (aTarget != null) // not root node
+ {
+ if (parentAssociation.childrenKey != null) {
+ if (parentSource == null) {
+ throw new WotonomyException("Need a data source when children aspect is bound.");
+ }
+
+ NSArray displayedObjects = parentGroup.displayedObjects();
+ EODataSource childSource = parentSource.dataSourceQualifiedByKey(parentAssociation.childrenKey);
+ childSource.qualifyWithRelationshipKey(parentAssociation.childrenKey, aTarget);
+
+ // create new display group using child data source
+ this.setDataSource(childSource);
+
+ // establish observer for target object
+ setTarget(aTarget);
+ } else // only titles is bound
+ {
+ // establish observer for target object
+ setTarget(aTarget);
+
+ setDataSource(new PropertyDataSource() {
+ public NSArray fetchObjects() {
+ return new NSArray();
+ }
+ });
+ }
+ } else // else root node
+ {
+ // root node uses PropertyDataSource by default
+ if (parentSource == null) {
+ setDataSource(new PropertyDataSource() {
+ public NSArray fetchObjects() {
+ if (parentGroup != null) {
+ return parentGroup.displayedObjects();
+ }
+ return null;
+ }
+ });
+ } else {
+ // root node uses parent source directly
+ setDataSource(parentSource);
+ }
+ }
+ }
+
+ /**
+ * Overridden to unregister as an editor of the editing context, since we don't
+ * directly present a user interface.
+ */
+ public void setDataSource(EODataSource aDataSource) {
+ super.setDataSource(aDataSource);
+ if ((aDataSource != null) && (aDataSource.editingContext() != null)) {
+ aDataSource.editingContext().removeEditor(this);
+ }
+ }
+
+ /**
+ * Returns whether the node should call fetch().
+ */
+ protected boolean isFetched() {
+ if (isFetchNeeded()) {
+ setFetchNeeded(false);
+ fetch();
+ }
+ return isFetched;
+ }
+
+ /**
+ * Sets whether the node should call fetch().
+ */
+ protected void setFetched(boolean fetched) {
//System.out.println( "DisplayGroupNode.setFetched: " + fetched + " : " + this + " : " + target );
//net.wotonomy.ui.swing.util.StackTraceInspector.printShortStackTrace();
- isFetched = fetched;
- }
-
- /**
- * Returns whether the node is in need of a refetch.
- */
- protected boolean isFetchNeeded()
- {
- return isFetchNeeded;
- }
-
- /**
- * Returns whether the node should call fetch().
- */
- protected void setFetchNeeded( boolean fetchNeeded )
- {
+ isFetched = fetched;
+ }
+
+ /**
+ * Returns whether the node is in need of a refetch.
+ */
+ protected boolean isFetchNeeded() {
+ return isFetchNeeded;
+ }
+
+ /**
+ * Returns whether the node should call fetch().
+ */
+ protected void setFetchNeeded(boolean fetchNeeded) {
//System.out.println( "DisplayGroupNode.setFetchNeeded: " + fetchNeeded + " : " + this + " : " + target );
//net.wotonomy.ui.swing.util.StackTraceInspector.printShortStackTrace();
- isFetchNeeded = fetchNeeded;
- }
-
- /**
- * Subclasses should override this method to fire an appropriate insertion event.
- */
- protected void fireNodesInserted( Object[] path, int[] indexes, Object[] objects )
- {
+ isFetchNeeded = fetchNeeded;
+ }
+
+ /**
+ * Subclasses should override this method to fire an appropriate insertion
+ * event.
+ */
+ protected void fireNodesInserted(Object[] path, int[] indexes, Object[] objects) {
//System.out.println( "fireNodesInserted: " + this );
- parentAssociation.fireTreeNodesInserted(
- this, path, indexes, objects );
- }
-
- /**
- * Subclasses should override this method to fire an appropriate change event.
- */
- protected void fireNodesChanged( Object[] path, int[] indexes, Object[] objects )
- {
+ parentAssociation.fireTreeNodesInserted(this, path, indexes, objects);
+ }
+
+ /**
+ * Subclasses should override this method to fire an appropriate change event.
+ */
+ protected void fireNodesChanged(Object[] path, int[] indexes, Object[] objects) {
//System.out.println( "fireNodesChanged: " + this );
- parentAssociation.fireTreeNodesChanged(
- this, path, indexes, objects );
- }
-
- /**
- * Subclasses should override this method to fire an appropriate deletion event.
- */
- protected void fireNodesRemoved( Object[] path, int[] indexes, Object[] objects )
- {
+ parentAssociation.fireTreeNodesChanged(this, path, indexes, objects);
+ }
+
+ /**
+ * Subclasses should override this method to fire an appropriate deletion event.
+ */
+ protected void fireNodesRemoved(Object[] path, int[] indexes, Object[] objects) {
//System.out.println( "fireNodesRemoved: " + this );
- parentAssociation.fireTreeNodesRemoved(
- this, path, indexes, objects );
- }
-
- /**
- * Subclasses should override this method to fire an appropriate event.
- */
- protected void fireStructureChanged( Object[] path, int[] indexes, Object[] objects )
- {
- parentAssociation.fireTreeStructureChanged(
- this, path, indexes, objects );
- }
-
- /**
- * Overridden to broadcast a tree event after super executes.
- */
- public void insertObjectAtIndex ( Object anObject, int anIndex )
- {
- int count = getChildCount(); // gets old count
- if ( target == null )
- {
- // if root node, forward to parent:
- // circumventing delegating data source, if any
- EODataSource dataSource = parentGroup.dataSource();
- if ( dataSource instanceof DelegatingTreeDataSource )
- {
- parentGroup.setDataSource(
- ((DelegatingTreeDataSource)dataSource).delegateDataSource );
- }
- parentGroup.insertObjectAtIndex( anObject, anIndex );
- if ( dataSource instanceof DelegatingTreeDataSource )
- {
- parentGroup.setDataSource( dataSource );
- }
- return; // prevent event from firing (?)
- }
- else // not root node
- {
- super.insertObjectAtIndex( anObject, anIndex );
- }
- }
-
- /**
- * Overridden to broadcast a tree event after super executes.
- */
- public boolean deleteObjectAtIndex ( int anIndex )
- {
- boolean result;
- Object node = getChildNodeAt( anIndex );
- if ( target == null )
- {
- // if root node, forward to parent:
- result = parentGroup.deleteObjectAtIndex( anIndex );
- }
- else // not root node
- {
- result = super.deleteObjectAtIndex( anIndex );
- }
-
- return result;
- }
-
- /**
- * Returns the child node that corresponds to the
- * specified index, creating it if necessary.
- * The index must be within bounds or an exception
- * is thrown.
- */
- public DisplayGroupNode getChildNodeAt( int anIndex )
- {
- boolean wasFetched = isFetched();
- if ( ! wasFetched ) fetch();
- Object o = displayedObjects.objectAtIndex( anIndex );
- DisplayGroupNode result = getChildNodeForObject( o );
- if ( result == null )
- {
- result = createChildNodeForObject( o );
- }
- return result;
- }
-
- /**
- * Returns a child node that corresponds to the
- * specified object, returning null if not found.
- */
- protected DisplayGroupNode getChildNodeForObject( Object anObject )
- {
- return (DisplayGroupNode)
- childNodes.objectForKey( new ReferenceKey( anObject ) );
- }
-
- /**
- * Creates a child node that corresponds to the
- * specified object.
- */
- private DisplayGroupNode createChildNodeForObject( Object anObject )
- {
- DisplayGroupNode result = parentAssociation.createNode( this, anObject );
- childNodes.setObjectForKey( result, new ReferenceKey( anObject ) );
- return result;
- }
-
- /**
- * Returns a tree path of all DisplayGroupNodes leading
- * to this node, including the root node (but excluding the
- * titles display group).
- */
- public TreePath treePath()
- {
- List path = new LinkedList();
- EODisplayGroup node = this;
- while ( node instanceof DisplayGroupNode )
- {
- // insert at head of list
- path.add( 0, node );
- node = ((DisplayGroupNode)node).parentGroup;
- }
- return new TreePath( path.toArray() );
- }
-
- /**
- * Overridden to return the parent group's
- * sort ordering if useParentOrderings is true.
- * useParentOrderings is true by default.
- */
- public NSArray sortOrderings()
- {
- if ( ( useParentOrderings )
- && ( parentGroup != null ) )
- {
- return parentGroup.sortOrderings();
- }
- return super.sortOrderings();
- }
-
- /**
- * Overridden to set useParentOrderings to false,
- * or true if aList is null.
- */
- public void setSortOrderings ( List aList )
- {
- if ( aList == null )
- {
- useParentOrderings = true;
- }
- else
- {
- useParentOrderings = false;
- super.setSortOrderings( aList );
- }
- }
-
- /**
- * Overridden to return the parent group's
- * qualifier if useParentQualifier is true.
- * useParentQualifier is true by default.
- */
- public EOQualifier qualifier()
- {
- if ( ( useParentQualifier )
- && ( parentGroup != null ) )
- {
- return parentGroup.qualifier();
- }
- return super.qualifier();
- }
-
- /**
- * Overridden to set useParentQualifier to false,
- * or true if aList is null.
- */
- public void setQualifier ( EOQualifier aQualifier )
- {
- if ( aQualifier == null )
- {
- useParentQualifier = true;
- }
- else
- {
- useParentQualifier = false;
- super.setQualifier( aQualifier );
- }
- }
-
- /**
- * Overridden to set isFetched to true.
- */
- public boolean fetch()
- {
+ parentAssociation.fireTreeNodesRemoved(this, path, indexes, objects);
+ }
+
+ /**
+ * Subclasses should override this method to fire an appropriate event.
+ */
+ protected void fireStructureChanged(Object[] path, int[] indexes, Object[] objects) {
+ parentAssociation.fireTreeStructureChanged(this, path, indexes, objects);
+ }
+
+ /**
+ * Overridden to broadcast a tree event after super executes.
+ */
+ public void insertObjectAtIndex(Object anObject, int anIndex) {
+ int count = getChildCount(); // gets old count
+ if (target == null) {
+ // if root node, forward to parent:
+ // circumventing delegating data source, if any
+ EODataSource dataSource = parentGroup.dataSource();
+ if (dataSource instanceof DelegatingTreeDataSource) {
+ parentGroup.setDataSource(((DelegatingTreeDataSource) dataSource).delegateDataSource);
+ }
+ parentGroup.insertObjectAtIndex(anObject, anIndex);
+ if (dataSource instanceof DelegatingTreeDataSource) {
+ parentGroup.setDataSource(dataSource);
+ }
+ return; // prevent event from firing (?)
+ } else // not root node
+ {
+ super.insertObjectAtIndex(anObject, anIndex);
+ }
+ }
+
+ /**
+ * Overridden to broadcast a tree event after super executes.
+ */
+ public boolean deleteObjectAtIndex(int anIndex) {
+ boolean result;
+ Object node = getChildNodeAt(anIndex);
+ if (target == null) {
+ // if root node, forward to parent:
+ result = parentGroup.deleteObjectAtIndex(anIndex);
+ } else // not root node
+ {
+ result = super.deleteObjectAtIndex(anIndex);
+ }
+
+ return result;
+ }
+
+ /**
+ * Returns the child node that corresponds to the specified index, creating it
+ * if necessary. The index must be within bounds or an exception is thrown.
+ */
+ public DisplayGroupNode getChildNodeAt(int anIndex) {
+ boolean wasFetched = isFetched();
+ if (!wasFetched)
+ fetch();
+ Object o = displayedObjects.objectAtIndex(anIndex);
+ DisplayGroupNode result = getChildNodeForObject(o);
+ if (result == null) {
+ result = createChildNodeForObject(o);
+ }
+ return result;
+ }
+
+ /**
+ * Returns a child node that corresponds to the specified object, returning null
+ * if not found.
+ */
+ protected DisplayGroupNode getChildNodeForObject(Object anObject) {
+ return (DisplayGroupNode) childNodes.objectForKey(new ReferenceKey(anObject));
+ }
+
+ /**
+ * Creates a child node that corresponds to the specified object.
+ */
+ private DisplayGroupNode createChildNodeForObject(Object anObject) {
+ DisplayGroupNode result = parentAssociation.createNode(this, anObject);
+ childNodes.setObjectForKey(result, new ReferenceKey(anObject));
+ return result;
+ }
+
+ /**
+ * Returns a tree path of all DisplayGroupNodes leading to this node, including
+ * the root node (but excluding the titles display group).
+ */
+ public TreePath treePath() {
+ List path = new LinkedList();
+ EODisplayGroup node = this;
+ while (node instanceof DisplayGroupNode) {
+ // insert at head of list
+ path.add(0, node);
+ node = ((DisplayGroupNode) node).parentGroup;
+ }
+ return new TreePath(path.toArray());
+ }
+
+ /**
+ * Overridden to return the parent group's sort ordering if useParentOrderings
+ * is true. useParentOrderings is true by default.
+ */
+ public NSArray sortOrderings() {
+ if ((useParentOrderings) && (parentGroup != null)) {
+ return parentGroup.sortOrderings();
+ }
+ return super.sortOrderings();
+ }
+
+ /**
+ * Overridden to set useParentOrderings to false, or true if aList is null.
+ */
+ public void setSortOrderings(List aList) {
+ if (aList == null) {
+ useParentOrderings = true;
+ } else {
+ useParentOrderings = false;
+ super.setSortOrderings(aList);
+ }
+ }
+
+ /**
+ * Overridden to return the parent group's qualifier if useParentQualifier is
+ * true. useParentQualifier is true by default.
+ */
+ public EOQualifier qualifier() {
+ if ((useParentQualifier) && (parentGroup != null)) {
+ return parentGroup.qualifier();
+ }
+ return super.qualifier();
+ }
+
+ /**
+ * Overridden to set useParentQualifier to false, or true if aList is null.
+ */
+ public void setQualifier(EOQualifier aQualifier) {
+ if (aQualifier == null) {
+ useParentQualifier = true;
+ } else {
+ useParentQualifier = false;
+ super.setQualifier(aQualifier);
+ }
+ }
+
+ /**
+ * Overridden to set isFetched to true.
+ */
+ public boolean fetch() {
//System.out.println( "DisplayGroupNode.fetch: " + this + " : " );
//if ( getClass().getName().indexOf( "Activity" ) != -1 )
//{
// new net.wotonomy.ui.swing.util.StackTraceInspector( this.toString() );
//}
- // set flag
- setFetched( true );
+ // set flag
+ setFetched(true);
- // skip root node
- if ( target == null ) return true;
+ // skip root node
+ if (target == null)
+ return true;
- // requalify
- dataSource().qualifyWithRelationshipKey(
- parentAssociation.childrenKey, target );
+ // requalify
+ dataSource().qualifyWithRelationshipKey(parentAssociation.childrenKey, target);
- // call to super
- return super.fetch();
+ // call to super
+ return super.fetch();
//boolean result = super.fetch();
//System.out.println( displayedObjects() );
//return result;
- }
-
- /**
- * Returns the object at the appropriate index
- * in the parent display group.
- */
- public Object object()
- {
- // if root node
- if ( target == null )
- {
- return parentAssociation.rootLabel();
- }
- return target;
- }
-
- /**
- * Returns the string value of the title property
- * on the object in the parent display group corresponding
- * to this index. The tree renderer asks JTrees to
- * call this method to retrieve a value for display.
- */
- public String toString()
- {
- Object result = getUserObject();
- if ( result == null ) result = "[null]";
- return result.toString();
- }
-
- // parts of interface TreeNode
-
- public int getChildCount()
- {
- if ( ! isFetched() ) fetch();
+ }
+
+ /**
+ * Returns the object at the appropriate index in the parent display group.
+ */
+ public Object object() {
+ // if root node
+ if (target == null) {
+ return parentAssociation.rootLabel();
+ }
+ return target;
+ }
+
+ /**
+ * Returns the string value of the title property on the object in the parent
+ * display group corresponding to this index. The tree renderer asks JTrees to
+ * call this method to retrieve a value for display.
+ */
+ public String toString() {
+ Object result = getUserObject();
+ if (result == null)
+ result = "[null]";
+ return result.toString();
+ }
+
+ // parts of interface TreeNode
+
+ public int getChildCount() {
+ if (!isFetched())
+ fetch();
//if ( toString().indexOf("154.16406")!=-1){
//System.out.println( "getChildCount: " + displayedObjects.count() + " : " + this );
//new RuntimeException().printStackTrace();
//net.wotonomy.ui.swing.util.StackTraceInspector.printShortStackTrace();
//}
- return displayedObjects.count();
- }
-
- public int getIndex(DisplayGroupNode node)
- {
- if ( ! isFetched() ) fetch();
- return displayedObjects.indexOfObject(
- ((DisplayGroupNode)node).target );
- }
-
- public boolean getAllowsChildren()
- {
- return true;
- }
-
- public boolean isLeaf()
- {
- // if not root node and isLeaf aspect is bound
- if ( ( target != null )
- && ( parentGroup != null )
- && ( parentAssociation.leafKey != null ) )
- {
- Object value;
- if ( parentAssociation.leafDisplayGroup != null )
- {
- value = parentGroup.valueForObject(
- target, parentAssociation.leafKey );
- }
- else
- {
- value = parentAssociation.leafKey;
- }
-
- // getBoolean returns true for zero, among other things
- Object result = ValueConverter.getBoolean( value );
- if ( result != null )
- {
- return ((Boolean)result).booleanValue();
- }
- }
-
- // otherwise, we have to fetch and return count
- return ( getChildCount() == 0 );
- }
-
- public Enumeration children()
- {
- int count = getChildCount();
- Vector v = new Vector();
- for ( int i = 0; i < count; i++ )
- {
- v.add( getChildNodeAt( i ) );
- }
- return v.elements();
- }
-
- // parts of interface MutableTreeNode
-
- public void insert(DisplayGroupNode aChild, int anIndex)
- {
- insertObjectAtIndex(
- ((DisplayGroupNode)aChild).object(), anIndex );
- }
-
- public void remove(int index)
- {
- deleteObjectAtIndex( index );
- }
-
- /**
- * Removes the node at the index corresponding
- * to the index of the object.
- */
- public void remove(DisplayGroupNode node)
- {
- remove( getIndex( node ) );
- }
-
- /**
- * Removes our object from the parent display group.
- */
- public void removeFromParent()
- {
- int index = parentGroup.displayedObjects().indexOfIdenticalObject( target );
- if ( index != NSArray.NotFound )
- {
- parentGroup.deleteObjectAtIndex( index );
- }
- else
- {
- throw new WotonomyException(
- "Object not found in parent group: " + target );
- }
- }
-
- /**
- * Removes our object from the parent display group
- * and adds it to the end of the specified node's children.
- */
- public void setParent(DisplayGroupNode newParent)
- {
- removeFromParent();
- newParent.insertObjectAtIndex(
- object(), newParent.displayedObjects.size() );
- }
-
- /**
- * Returns the value of the displayed property in the parent display group
- * at the index that corresponds to the index of this node.
- */
- public Object getUserObject()
- {
- return valueForKey( parentAssociation.titlesKey );
- }
-
- /**
- * Sets the value of the displayed property in the parent display group
- * at the index that corresponds to the index of this node.
- */
- public void setUserObject( Object aValue )
- {
- setValueForKey( aValue, parentAssociation.titlesKey );
- }
-
- /**
- * Returns a value from the object in the parent display group
- * at the index that corresponds to the index of this node.
- * For the root node, if the titles key is specified, the root
- * label is returned, otherwise null is returned.
- */
- public Object valueForKey( String aKey )
- {
- // if root node
- if ( target == null )
- {
- // compare by ref is okay for strings
- if ( aKey == parentAssociation.titlesKey )
- {
- return parentAssociation.rootLabel();
- }
- return null;
- }
- return parentGroup.valueForObject( target, aKey );
- }
-
- /**
- * Sets a value on the object in the parent display group
- * at the index that corresponds to the index of this node.
- * For the root node, this method only works if aKey is the
- * titlesAspect's key, otherwise does nothing.
- */
- public void setValueForKey(Object aValue, String aKey)
- {
- // if root node, return.
- if ( target == null )
- {
- // compare by ref is okay for strings
- if ( aKey == parentAssociation.titlesKey )
- {
- parentAssociation.setRootLabel( aValue );
-
- // how to handle root node? tree event docs don't say.
- fireNodesChanged ( treePath().getPath(),
- new int[] { 0 },
- new Object[] { this } );
- }
- return;
- }
-
- parentGroup.setValueForObject(
- aValue, target, aKey );
- }
-
- /**
- * Perform any clean up in this method.
- * The node will not be reused after this method is called.
- * This implementation removes itself from the parent's
- * set of child nodes, sets target and datasource to null,
- * and then calls disposeChildNodes().
- */
- protected void dispose()
- { //System.out.println( "dispose: " + this.getClass().getName() + " : " + this );
- if ( parentGroup != null )
- {
- ((DisplayGroupNode)parentGroup).childNodes.remove(
- new ReferenceKey( target ) );
- }
- setTarget( (Object) null );
- setDataSource( null );
- disposeChildNodes();
- }
-
- /**
- * Calls dispose() on all child nodes.
- */
- protected void disposeChildNodes()
- {
- Iterator i = new LinkedList(childNodes.values()).iterator();
- while ( i.hasNext() )
- {
- ((DisplayGroupNode) i.next()).dispose();
- }
- }
-
- /**
- * Called after the target object posts a change notification.
- * This implementation re-fetches which triggers
- * updateDisplayedObjects to broadcast any tree events.
- * This method marks the parent object as changed if:
- * (1) this object is not registered in the editing context
- * of the titles display group's data source (if any), AND
- * (2) the children key is not in the list of attributes
- * of the parent object's EOClassDescription.
- */
- public void targetChanged()
- {
- // if not root node
- if ( target != null )
- {
- // if we're not root and not fetched, stop here.
- //FIXME: with this, some nodes have old values when moved.
- //FIXME: without this, nodes are unnecessarily fetched.
- //FIXME: might have parent modify isFetched of certain child nodes.
- if ( isFetched() )
- {
- fetch();
- }
- else // not fetched - just update the display
- {
- updateDisplayedObjects();
- }
-/*
-//disabling this for performance reasons:
-//might reenable later or find an alternate approach
- // check to see if we need to mark the parent object as changed
- EOEditingContext context = dataSource().editingContext();
- if ( ( context == null )
- || ( context.globalIDForObject( target ) == null ) )
- {
- DisplayGroupNode parentNode = (DisplayGroupNode) parentGroup;
- if ( parentNode.target != null )
- {
- // only notify if childrenKey is an attribute of parentDesc
- // (and therefore not a toOne or toMany relationship)
- EOClassDescription parentDesc =
- EOClassDescription.classDescriptionForClass(
- parentNode.target.getClass() );
- if ( parentDesc.attributeKeys().contains( parentAssociation.childrenKey ) )
- {
- // only notify if no context is already observing the object
- // and we are an attribute key
- EOObserverCenter.notifyObserversObjectWillChange( parentNode.target );
- }
- }
- }
-*/
- }
- else // root node
- {
- setObjectArray( parentAssociation.titlesDisplayGroup.displayedObjects() );
- }
-
- // finally, broadcast change event for this node
- // even though we're not sure if the displayed value changed.
- fireNodeChanged();
- }
-
- /**
- * Fires a change event for this node.
- */
- public void fireNodeChanged()
- {
- // if not root node
- if ( target != null )
- {
- int index = ((DisplayGroupNode)parentGroup).getIndex( this );
- if ( ( index != -1 )
- && ( treePath().getParentPath() != null ) )
- {
- fireNodesChanged (
- treePath().getParentPath().getPath(),
- new int[] { index },
- new Object[] { this } );
- }
- }
- }
-
-Object[] previouslyDisplayedObjects = new Object[0];
- /**
- * Overridden to call to super, fire any tree events, and then
- * call updateDisplayedObjects on all fetched child nodes.
- * This method compares this node's displayed objects against
- * the list of child nodes, synchronizes them, and then broadcasts
- * only the necessary events to bring the view component up to date.
- */
- public void updateDisplayedObjects()
- {
+ return displayedObjects.count();
+ }
+
+ public int getIndex(DisplayGroupNode node) {
+ if (!isFetched())
+ fetch();
+ return displayedObjects.indexOfObject(((DisplayGroupNode) node).target);
+ }
+
+ public boolean getAllowsChildren() {
+ return true;
+ }
+
+ public boolean isLeaf() {
+ // if not root node and isLeaf aspect is bound
+ if ((target != null) && (parentGroup != null) && (parentAssociation.leafKey != null)) {
+ Object value;
+ if (parentAssociation.leafDisplayGroup != null) {
+ value = parentGroup.valueForObject(target, parentAssociation.leafKey);
+ } else {
+ value = parentAssociation.leafKey;
+ }
+
+ // getBoolean returns true for zero, among other things
+ Object result = ValueConverter.getBoolean(value);
+ if (result != null) {
+ return ((Boolean) result).booleanValue();
+ }
+ }
+
+ // otherwise, we have to fetch and return count
+ return (getChildCount() == 0);
+ }
+
+ public Enumeration children() {
+ int count = getChildCount();
+ Vector v = new Vector();
+ for (int i = 0; i < count; i++) {
+ v.add(getChildNodeAt(i));
+ }
+ return v.elements();
+ }
+
+ // parts of interface MutableTreeNode
+
+ public void insert(DisplayGroupNode aChild, int anIndex) {
+ insertObjectAtIndex(((DisplayGroupNode) aChild).object(), anIndex);
+ }
+
+ public void remove(int index) {
+ deleteObjectAtIndex(index);
+ }
+
+ /**
+ * Removes the node at the index corresponding to the index of the object.
+ */
+ public void remove(DisplayGroupNode node) {
+ remove(getIndex(node));
+ }
+
+ /**
+ * Removes our object from the parent display group.
+ */
+ public void removeFromParent() {
+ int index = parentGroup.displayedObjects().indexOfIdenticalObject(target);
+ if (index != NSArray.NotFound) {
+ parentGroup.deleteObjectAtIndex(index);
+ } else {
+ throw new WotonomyException("Object not found in parent group: " + target);
+ }
+ }
+
+ /**
+ * Removes our object from the parent display group and adds it to the end of
+ * the specified node's children.
+ */
+ public void setParent(DisplayGroupNode newParent) {
+ removeFromParent();
+ newParent.insertObjectAtIndex(object(), newParent.displayedObjects.size());
+ }
+
+ /**
+ * Returns the value of the displayed property in the parent display group at
+ * the index that corresponds to the index of this node.
+ */
+ public Object getUserObject() {
+ return valueForKey(parentAssociation.titlesKey);
+ }
+
+ /**
+ * Sets the value of the displayed property in the parent display group at the
+ * index that corresponds to the index of this node.
+ */
+ public void setUserObject(Object aValue) {
+ setValueForKey(aValue, parentAssociation.titlesKey);
+ }
+
+ /**
+ * Returns a value from the object in the parent display group at the index that
+ * corresponds to the index of this node. For the root node, if the titles key
+ * is specified, the root label is returned, otherwise null is returned.
+ */
+ public Object valueForKey(String aKey) {
+ // if root node
+ if (target == null) {
+ // compare by ref is okay for strings
+ if (aKey == parentAssociation.titlesKey) {
+ return parentAssociation.rootLabel();
+ }
+ return null;
+ }
+ return parentGroup.valueForObject(target, aKey);
+ }
+
+ /**
+ * Sets a value on the object in the parent display group at the index that
+ * corresponds to the index of this node. For the root node, this method only
+ * works if aKey is the titlesAspect's key, otherwise does nothing.
+ */
+ public void setValueForKey(Object aValue, String aKey) {
+ // if root node, return.
+ if (target == null) {
+ // compare by ref is okay for strings
+ if (aKey == parentAssociation.titlesKey) {
+ parentAssociation.setRootLabel(aValue);
+
+ // how to handle root node? tree event docs don't say.
+ fireNodesChanged(treePath().getPath(), new int[] { 0 }, new Object[] { this });
+ }
+ return;
+ }
+
+ parentGroup.setValueForObject(aValue, target, aKey);
+ }
+
+ /**
+ * Perform any clean up in this method. The node will not be reused after this
+ * method is called. This implementation removes itself from the parent's set of
+ * child nodes, sets target and datasource to null, and then calls
+ * disposeChildNodes().
+ */
+ protected void dispose() { // System.out.println( "dispose: " + this.getClass().getName() + " : " + this );
+ if (parentGroup != null) {
+ ((DisplayGroupNode) parentGroup).childNodes.remove(new ReferenceKey(target));
+ }
+ setTarget((Object) null);
+ setDataSource(null);
+ disposeChildNodes();
+ }
+
+ /**
+ * Calls dispose() on all child nodes.
+ */
+ protected void disposeChildNodes() {
+ Iterator i = new LinkedList(childNodes.values()).iterator();
+ while (i.hasNext()) {
+ ((DisplayGroupNode) i.next()).dispose();
+ }
+ }
+
+ /**
+ * Called after the target object posts a change notification. This
+ * implementation re-fetches which triggers updateDisplayedObjects to broadcast
+ * any tree events. This method marks the parent object as changed if: (1) this
+ * object is not registered in the editing context of the titles display group's
+ * data source (if any), AND (2) the children key is not in the list of
+ * attributes of the parent object's EOClassDescription.
+ */
+ public void targetChanged() {
+ // if not root node
+ if (target != null) {
+ // if we're not root and not fetched, stop here.
+ // FIXME: with this, some nodes have old values when moved.
+ // FIXME: without this, nodes are unnecessarily fetched.
+ // FIXME: might have parent modify isFetched of certain child nodes.
+ if (isFetched()) {
+ fetch();
+ } else // not fetched - just update the display
+ {
+ updateDisplayedObjects();
+ }
+ /*
+ * //disabling this for performance reasons: //might reenable later or find an
+ * alternate approach // check to see if we need to mark the parent object as
+ * changed EOEditingContext context = dataSource().editingContext(); if ( (
+ * context == null ) || ( context.globalIDForObject( target ) == null ) ) {
+ * DisplayGroupNode parentNode = (DisplayGroupNode) parentGroup; if (
+ * parentNode.target != null ) { // only notify if childrenKey is an attribute
+ * of parentDesc // (and therefore not a toOne or toMany relationship)
+ * EOClassDescription parentDesc = EOClassDescription.classDescriptionForClass(
+ * parentNode.target.getClass() ); if ( parentDesc.attributeKeys().contains(
+ * parentAssociation.childrenKey ) ) { // only notify if no context is already
+ * observing the object // and we are an attribute key
+ * EOObserverCenter.notifyObserversObjectWillChange( parentNode.target ); } } }
+ */
+ } else // root node
+ {
+ setObjectArray(parentAssociation.titlesDisplayGroup.displayedObjects());
+ }
+
+ // finally, broadcast change event for this node
+ // even though we're not sure if the displayed value changed.
+ fireNodeChanged();
+ }
+
+ /**
+ * Fires a change event for this node.
+ */
+ public void fireNodeChanged() {
+ // if not root node
+ if (target != null) {
+ int index = ((DisplayGroupNode) parentGroup).getIndex(this);
+ if ((index != -1) && (treePath().getParentPath() != null)) {
+ fireNodesChanged(treePath().getParentPath().getPath(), new int[] { index }, new Object[] { this });
+ }
+ }
+ }
+
+ Object[] previouslyDisplayedObjects = new Object[0];
+
+ /**
+ * Overridden to call to super, fire any tree events, and then call
+ * updateDisplayedObjects on all fetched child nodes. This method compares this
+ * node's displayed objects against the list of child nodes, synchronizes them,
+ * and then broadcasts only the necessary events to bring the view component up
+ * to date.
+ */
+ public void updateDisplayedObjects() {
//System.out.println( "updateDisplayedObjects: " + " : " + this );
//net.wotonomy.ui.swing.util.StackTraceInspector.printShortStackTrace();
//new RuntimeException().printStackTrace();
- super.updateDisplayedObjects();
-
- // diff lists
- boolean proceed = true;
- Object[] oldObjects = previouslyDisplayedObjects;
- Object[] newObjects = displayedObjects.toArray();
- if ( oldObjects.length == newObjects.length )
- {
- proceed = false;
- for ( int i = 0; i < newObjects.length; i++ )
- {
- if ( oldObjects[i] != newObjects[i] )
- {
- proceed = true;
- break;
- }
- }
- }
-
- // this should be set before firing the change events
- // in case some clients end up calling this again.
- previouslyDisplayedObjects = newObjects;
-
- DisplayGroupNode node;
- Iterator i = childNodes.values().iterator();
- while ( i.hasNext() )
- {
- node = (DisplayGroupNode) i.next();
- if ( !node.isFetchNeeded() )
- {
- node.updateDisplayedObjects();
- }
- }
-
- if ( proceed )
- {
+ super.updateDisplayedObjects();
+
+ // diff lists
+ boolean proceed = true;
+ Object[] oldObjects = previouslyDisplayedObjects;
+ Object[] newObjects = displayedObjects.toArray();
+ if (oldObjects.length == newObjects.length) {
+ proceed = false;
+ for (int i = 0; i < newObjects.length; i++) {
+ if (oldObjects[i] != newObjects[i]) {
+ proceed = true;
+ break;
+ }
+ }
+ }
+
+ // this should be set before firing the change events
+ // in case some clients end up calling this again.
+ previouslyDisplayedObjects = newObjects;
+
+ DisplayGroupNode node;
+ Iterator i = childNodes.values().iterator();
+ while (i.hasNext()) {
+ node = (DisplayGroupNode) i.next();
+ if (!node.isFetchNeeded()) {
+ node.updateDisplayedObjects();
+ }
+ }
+
+ if (proceed) {
//System.out.println( "DisplayGroupNode.firingEventsForChanges: " );
//new RuntimeException().printStackTrace();
- fireEventsForChanges( oldObjects, newObjects );
- }
-
- }
-
- /**
- * Called by processRecentChanges to analyze the
- * differences between the lists and broadcast the
- * appropriate events.
- */
- protected void fireEventsForChanges(
- Object[] oldObjects, Object[] newObjects )
- {
- // structure changed causes havoc while
- // establishing connection in some cases
- //if ( oldObjects.length == 0 || newObjects.length == 0 )
- //{
- // fireStructureChanged( treePath().getPath(), null, null );
- // return;
- //}
-
- int insertCount = 0;
- int deleteCount = 0;
- Object[] inserts = new Object[ newObjects.length ];
- Object[] deletes = new Object[ oldObjects.length ];
-
- int i;
- int n = -1, o = -1; // last match
- int n1 = 0, o1 = 0; // current match test
- int n2 = 0, o2 = 0; // scan ahead
-
- while ( o1 < oldObjects.length && n1 < newObjects.length )
- {
- if ( newObjects[n1] == oldObjects[o1] )
- {
- // mark as match and continue
- o = o1;
- n = n1;
- }
- else
- {
- // scan ahead for the next match, if any
- o2 = o1;
- n2 = n1;
-
- while ( o2 < oldObjects.length || n2 < newObjects.length )
- {
- if ( o2 < oldObjects.length && newObjects[n1] == oldObjects[o2] )
- {
- // run o1 to o2: mark as deletes
- for ( i = o1; i < o2; i++ )
- { // System.out.println( "delete : " + i );
- deletes[i] = oldObjects[i];
- deleteCount++;
- }
- o1 = o2; // reset test
- o = o1; // set match
- n = n1; // set match
- break;
- }
- if ( n2 < newObjects.length && newObjects[n2] == oldObjects[o1] )
- {
- // run n1 to n2: mark as inserts
- for ( i = n1; i < n2; i++ )
- { // System.out.println( "insert : " + i );
- inserts[i] = newObjects[i];
- insertCount++;
- }
- n1 = n2; // reset test
- n = n1; // set match
- o = o1; // set match
- break;
- }
- o2++;
- n2++;
- }
- }
- if (n != n1)
- {
- inserts[n1] = newObjects[n1];
- insertCount++;
- deletes[o1] = oldObjects[o1];
- deleteCount++;
- //increment even though no match:
- //the new object was marked as inserted and
- //the old object was marked as deleted.
- n = n1;
- o = o1;
- }
- o1++;
- n1++;
- }
-
- // run o to end of oldObjects: mark as deletes
- for ( i = o+1; i < oldObjects.length; i++ )
- { // System.out.println( "delete : " + i );
- deletes[i] = oldObjects[i];
- deleteCount++;
- }
-
- // run n to end of newObjects: mark as inserts
- for ( i = n+1; i < newObjects.length; i++ )
- { // System.out.println( "insert : " + i );
- inserts[i] = newObjects[i];
- insertCount++;
- }
+ fireEventsForChanges(oldObjects, newObjects);
+ }
+
+ }
+
+ /**
+ * Called by processRecentChanges to analyze the differences between the lists
+ * and broadcast the appropriate events.
+ */
+ protected void fireEventsForChanges(Object[] oldObjects, Object[] newObjects) {
+ // structure changed causes havoc while
+ // establishing connection in some cases
+ // if ( oldObjects.length == 0 || newObjects.length == 0 )
+ // {
+ // fireStructureChanged( treePath().getPath(), null, null );
+ // return;
+ // }
+
+ int insertCount = 0;
+ int deleteCount = 0;
+ Object[] inserts = new Object[newObjects.length];
+ Object[] deletes = new Object[oldObjects.length];
+
+ int i;
+ int n = -1, o = -1; // last match
+ int n1 = 0, o1 = 0; // current match test
+ int n2 = 0, o2 = 0; // scan ahead
+
+ while (o1 < oldObjects.length && n1 < newObjects.length) {
+ if (newObjects[n1] == oldObjects[o1]) {
+ // mark as match and continue
+ o = o1;
+ n = n1;
+ } else {
+ // scan ahead for the next match, if any
+ o2 = o1;
+ n2 = n1;
+
+ while (o2 < oldObjects.length || n2 < newObjects.length) {
+ if (o2 < oldObjects.length && newObjects[n1] == oldObjects[o2]) {
+ // run o1 to o2: mark as deletes
+ for (i = o1; i < o2; i++) { // System.out.println( "delete : " + i );
+ deletes[i] = oldObjects[i];
+ deleteCount++;
+ }
+ o1 = o2; // reset test
+ o = o1; // set match
+ n = n1; // set match
+ break;
+ }
+ if (n2 < newObjects.length && newObjects[n2] == oldObjects[o1]) {
+ // run n1 to n2: mark as inserts
+ for (i = n1; i < n2; i++) { // System.out.println( "insert : " + i );
+ inserts[i] = newObjects[i];
+ insertCount++;
+ }
+ n1 = n2; // reset test
+ n = n1; // set match
+ o = o1; // set match
+ break;
+ }
+ o2++;
+ n2++;
+ }
+ }
+ if (n != n1) {
+ inserts[n1] = newObjects[n1];
+ insertCount++;
+ deletes[o1] = oldObjects[o1];
+ deleteCount++;
+ // increment even though no match:
+ // the new object was marked as inserted and
+ // the old object was marked as deleted.
+ n = n1;
+ o = o1;
+ }
+ o1++;
+ n1++;
+ }
+
+ // run o to end of oldObjects: mark as deletes
+ for (i = o + 1; i < oldObjects.length; i++) { // System.out.println( "delete : " + i );
+ deletes[i] = oldObjects[i];
+ deleteCount++;
+ }
+
+ // run n to end of newObjects: mark as inserts
+ for (i = n + 1; i < newObjects.length; i++) { // System.out.println( "insert : " + i );
+ inserts[i] = newObjects[i];
+ insertCount++;
+ }
//System.out.println( "done : "
//+ o + " : " + o1 + " : " + o2 + " :: " + n + " : " + n1 + " : " + n2 );
@@ -948,571 +774,481 @@ Object[] previouslyDisplayedObjects = new Object[0];
//System.out.println( new NSArray( inserts ) );
//System.out.println( new NSArray( deletes ) );
//System.out.println( new NSArray( oldObjects ) );
-
- int c;
- Object[] nodes;
- int[] indices;
-
- // broadcast delete event
- c = 0;
- nodes = new Object[ deleteCount ];
- indices = new int[ deleteCount ];
- for ( i = 0; i < deletes.length; i++ )
- {
- if ( deletes[i] != null )
- {
- indices[c] = i;
- nodes[c] = getChildNodeForObject( deletes[i] );
- c++;
- }
- }
- if ( c > 0 )
- {
+
+ int c;
+ Object[] nodes;
+ int[] indices;
+
+ // broadcast delete event
+ c = 0;
+ nodes = new Object[deleteCount];
+ indices = new int[deleteCount];
+ for (i = 0; i < deletes.length; i++) {
+ if (deletes[i] != null) {
+ indices[c] = i;
+ nodes[c] = getChildNodeForObject(deletes[i]);
+ c++;
+ }
+ }
+ if (c > 0) {
// fireNodeChanged(); // force the jtree to get the correct child count
- fireNodesRemoved( treePath().getPath(), indices, nodes );
- }
- deletes = nodes; // retain for dispose check
-
- // broadcast insert event
- c = 0;
- nodes = new Object[ insertCount ];
- indices = new int[ insertCount ];
- for ( i = 0; i < inserts.length; i++ )
- {
- if ( inserts[i] != null )
- {
- indices[c] = i;
- nodes[c] = getChildNodeForObject( inserts[i] );
- if ( nodes[c] == null )
- {
- nodes[c] = createChildNodeForObject( newObjects[i] );
- }
- c++;
- }
- }
- if ( c > 0 )
- {
- fireNodesInserted( treePath().getPath(), indices, nodes );
- }
-
- // dispose any delete nodes not on insert list
- int j;
- boolean found;
- for ( i = 0; i < deletes.length; i++ )
- {
- for ( j = 0; j < nodes.length; j++ )
- {
- if ( deletes[i] == nodes[j] ) break;
- }
-
- // did not break early, so not found, so dispose
- if ( j == nodes.length )
- {
- ((DisplayGroupNode)deletes[i]).dispose();
- }
- }
- }
-
- /**
- * Sets the target object and creates an registers a target observer.
- * If target was not previously null, the existing observer is unregistered.
- * Protected access so subclasses and TreeModelAssociation can update our target.
- */
- public void setTarget( Object aTarget )
- {
- if ( target != null )
- {
- EOObserverCenter.removeObserver( targetObserver, target );
- targetObserver.discardPendingNotification();
- }
-
- if ( aTarget != null )
- {
- target = aTarget;
- targetObserver = new TargetObserver( this );
- EOObserverCenter.addObserver( targetObserver, target );
- }
- }
-
- /**
- * Returns the parent display group, or null if parent is root.
- */
- public DisplayGroupNode getParentGroup()
- {
- if ( parentGroup instanceof DisplayGroupNode )
- {
- return (DisplayGroupNode)parentGroup;
- }
- // presumably the root node
- return null;
- }
-
- /**
- * Gets all descendants of the this node.
- */
- public List getDescendants()
- {
- return getDescendants( this, true );
- }
-
- /**
- * Gets only the descendants of the this node
- * whose children has been loaded - no fetching
- * will occur. Useful for load-on-demand trees.
- */
- public List getLoadedDescendants()
- {
- return getDescendants( this, false );
- }
-
- // breadth first traversal implementation
-
- /**
- * Returns a list of all descendants of the
- * specified node. Unfetched nodes are traversed
- * only if forceLoad is true.
- * This implementation is a breadth-first traversal
- * of the nodes starting at the specified node.
- */
- static private List getDescendants( DisplayGroupNode aNode, boolean forceLoad )
- {
- if ( !forceLoad && !aNode.isFetched ) return NSArray.EmptyArray;
-
- LinkedList result = new LinkedList();
- LinkedList queue = new LinkedList();
-
- queue.add( aNode );
- while ( ! queue.isEmpty() )
- {
- checkNode( (DisplayGroupNode) queue.removeFirst(),
- queue, result, forceLoad );
- }
-
- return result;
- }
-
- /**
- * Adds each fetched child node of the specified node to
- * the result set (optionally forcing the child node to load)
- * and adding child node to the end of the queue.
- */
- static private void checkNode( DisplayGroupNode aNode,
- LinkedList aQueue, LinkedList aResult, boolean forceLoad )
- {
- DisplayGroupNode child;
- int count = aNode.getChildCount();
-
- for ( int i = 0; i < count; i++ )
- {
- child = aNode.getChildNodeAt( i );
-
- // add to queue if node has fetched children
- if ( ( !child.isFetched ) && ( forceLoad ) )
- {
- child.fetch();
- }
- if ( child.isFetched )
- {
- aQueue.addLast( child );
- }
-
- aResult.add( child );
- }
- }
-
- /**
- * Overridden to not fetch on InvalidateAllObjectsInStoreNotification
- * unless we've already been fetched, preserving the load-on-demand
- * functionality.
- */
- public void objectsInvalidatedInEditingContext( NSNotification aNotification )
- {
- if ( EOObjectStore.InvalidatedAllObjectsInStoreNotification
- .equals( aNotification.name() ) )
- {
+ fireNodesRemoved(treePath().getPath(), indices, nodes);
+ }
+ deletes = nodes; // retain for dispose check
+
+ // broadcast insert event
+ c = 0;
+ nodes = new Object[insertCount];
+ indices = new int[insertCount];
+ for (i = 0; i < inserts.length; i++) {
+ if (inserts[i] != null) {
+ indices[c] = i;
+ nodes[c] = getChildNodeForObject(inserts[i]);
+ if (nodes[c] == null) {
+ nodes[c] = createChildNodeForObject(newObjects[i]);
+ }
+ c++;
+ }
+ }
+ if (c > 0) {
+ fireNodesInserted(treePath().getPath(), indices, nodes);
+ }
+
+ // dispose any delete nodes not on insert list
+ int j;
+ boolean found;
+ for (i = 0; i < deletes.length; i++) {
+ for (j = 0; j < nodes.length; j++) {
+ if (deletes[i] == nodes[j])
+ break;
+ }
+
+ // did not break early, so not found, so dispose
+ if (j == nodes.length) {
+ ((DisplayGroupNode) deletes[i]).dispose();
+ }
+ }
+ }
+
+ /**
+ * Sets the target object and creates an registers a target observer. If target
+ * was not previously null, the existing observer is unregistered. Protected
+ * access so subclasses and TreeModelAssociation can update our target.
+ */
+ public void setTarget(Object aTarget) {
+ if (target != null) {
+ EOObserverCenter.removeObserver(targetObserver, target);
+ targetObserver.discardPendingNotification();
+ }
+
+ if (aTarget != null) {
+ target = aTarget;
+ targetObserver = new TargetObserver(this);
+ EOObserverCenter.addObserver(targetObserver, target);
+ }
+ }
+
+ /**
+ * Returns the parent display group, or null if parent is root.
+ */
+ public DisplayGroupNode getParentGroup() {
+ if (parentGroup instanceof DisplayGroupNode) {
+ return (DisplayGroupNode) parentGroup;
+ }
+ // presumably the root node
+ return null;
+ }
+
+ /**
+ * Gets all descendants of the this node.
+ */
+ public List getDescendants() {
+ return getDescendants(this, true);
+ }
+
+ /**
+ * Gets only the descendants of the this node whose children has been loaded -
+ * no fetching will occur. Useful for load-on-demand trees.
+ */
+ public List getLoadedDescendants() {
+ return getDescendants(this, false);
+ }
+
+ // breadth first traversal implementation
+
+ /**
+ * Returns a list of all descendants of the specified node. Unfetched nodes are
+ * traversed only if forceLoad is true. This implementation is a breadth-first
+ * traversal of the nodes starting at the specified node.
+ */
+ static private List getDescendants(DisplayGroupNode aNode, boolean forceLoad) {
+ if (!forceLoad && !aNode.isFetched)
+ return NSArray.EmptyArray;
+
+ LinkedList result = new LinkedList();
+ LinkedList queue = new LinkedList();
+
+ queue.add(aNode);
+ while (!queue.isEmpty()) {
+ checkNode((DisplayGroupNode) queue.removeFirst(), queue, result, forceLoad);
+ }
+
+ return result;
+ }
+
+ /**
+ * Adds each fetched child node of the specified node to the result set
+ * (optionally forcing the child node to load) and adding child node to the end
+ * of the queue.
+ */
+ static private void checkNode(DisplayGroupNode aNode, LinkedList aQueue, LinkedList aResult, boolean forceLoad) {
+ DisplayGroupNode child;
+ int count = aNode.getChildCount();
+
+ for (int i = 0; i < count; i++) {
+ child = aNode.getChildNodeAt(i);
+
+ // add to queue if node has fetched children
+ if ((!child.isFetched) && (forceLoad)) {
+ child.fetch();
+ }
+ if (child.isFetched) {
+ aQueue.addLast(child);
+ }
+
+ aResult.add(child);
+ }
+ }
+
+ /**
+ * Overridden to not fetch on InvalidateAllObjectsInStoreNotification unless
+ * we've already been fetched, preserving the load-on-demand functionality.
+ */
+ public void objectsInvalidatedInEditingContext(NSNotification aNotification) {
+ if (EOObjectStore.InvalidatedAllObjectsInStoreNotification.equals(aNotification.name())) {
//System.out.println( "DisplayGroupNode.objectsInvalidatedInEditingContext: " + aNotification.name() );
- if ( parentAssociation.isVisible( this ) && targetObserver != null )
- {
- targetObserver.objectWillChange( target ); // force ui to update
- fireNodeChanged();
- }
- else // make sure we fetch children when we do become visible
- setFetchNeeded( true );
- return;
- }
- else
- if ( ( EOEditingContext.ObjectsChangedInEditingContextNotification
- .equals( aNotification.name() ) )
- || ( EOEditingContext.EditingContextDidSaveChangesNotification
- .equals( aNotification.name() ) ) )
- {
- int index;
- Enumeration e;
- boolean didChange = false;
- NSDictionary userInfo = aNotification.userInfo();
-
- // if our target object was deleted
- NSArray deletes = (NSArray) userInfo.objectForKey(
- EOObjectStore.DeletedKey );
- if ( deletes.indexOfIdenticalObject( target ) != NSArray.NotFound )
- {
+ if (parentAssociation.isVisible(this) && targetObserver != null) {
+ targetObserver.objectWillChange(target); // force ui to update
+ fireNodeChanged();
+ } else // make sure we fetch children when we do become visible
+ setFetchNeeded(true);
+ return;
+ } else if ((EOEditingContext.ObjectsChangedInEditingContextNotification.equals(aNotification.name()))
+ || (EOEditingContext.EditingContextDidSaveChangesNotification.equals(aNotification.name()))) {
+ int index;
+ Enumeration e;
+ boolean didChange = false;
+ NSDictionary userInfo = aNotification.userInfo();
+
+ // if our target object was deleted
+ NSArray deletes = (NSArray) userInfo.objectForKey(EOObjectStore.DeletedKey);
+ if (deletes.indexOfIdenticalObject(target) != NSArray.NotFound) {
//System.out.println( "DisplayGroupNode.objectsInvalidatedInEditingContext: delete: " + this + " : " + aNotification.name() );
- if ( parentAssociation.isVisible( this ) && targetObserver != null )
- {
- targetObserver.objectWillChange( target ); // force ui to update
- fireNodeChanged();
- }
- else // make sure we fetch children when we do become visible
- setFetchNeeded( true );
- return;
- }
-
- // if our target object was invalidated
- NSArray invalidates = (NSArray) userInfo.objectForKey(
- EOObjectStore.InvalidatedKey );
- if ( invalidates != null &&
- invalidates.indexOfIdenticalObject( target ) != NSArray.NotFound )
- {
+ if (parentAssociation.isVisible(this) && targetObserver != null) {
+ targetObserver.objectWillChange(target); // force ui to update
+ fireNodeChanged();
+ } else // make sure we fetch children when we do become visible
+ setFetchNeeded(true);
+ return;
+ }
+
+ // if our target object was invalidated
+ NSArray invalidates = (NSArray) userInfo.objectForKey(EOObjectStore.InvalidatedKey);
+ if (invalidates != null && invalidates.indexOfIdenticalObject(target) != NSArray.NotFound) {
//System.out.println( "DisplayGroupNode.objectsInvalidatedInEditingContext: invalidate: " + this + " : " + aNotification.name() );
- if ( parentAssociation.isVisible( this ) && targetObserver != null )
- {
- targetObserver.objectWillChange( target ); // force ui to update
- fireNodeChanged();
- }
- else // make sure we fetch children when we do become visible
- setFetchNeeded( true );
- return;
- }
-
- // if our target object was updated, set fetchNeeded plus fire changed event
- NSArray updates = (NSArray) userInfo.objectForKey(
- EOObjectStore.UpdatedKey );
- if ( updates.indexOfIdenticalObject( target ) != NSArray.NotFound )
- {
- if ( parentAssociation.isVisible( this ) && targetObserver != null )
- {
- targetObserver.objectWillChange( target ); // force ui to update
- fireNodeChanged();
- if ( object() instanceof Component ) ((Component)object()).repaint();
- }
- else // make sure we fetch children when we do become visible
- setFetchNeeded( true );
- return;
- }
- }
-
- super.objectsInvalidatedInEditingContext( aNotification );
-
- }
-
- // inner classes
-
- /**
- * Private class used to force a hashmap to
- * perform key comparisons by reference.
- */
- private class ReferenceKey
- {
- private int hashCode;
- private Object referent;
-
- public ReferenceKey( Object anObject )
- {
- referent = anObject;
- hashCode = anObject.hashCode();
- }
-
- /**
- * Returns the actual key's hash code.
- */
- public int hashCode()
- {
- return hashCode;
- }
-
- /**
- * Compares by reference.
- */
- public boolean equals( Object anObject )
- {
- if ( anObject instanceof ReferenceKey )
- {
- return ((ReferenceKey)anObject).referent == referent;
- }
- return false;
- }
- }
-
- /**
- * A private class to observe the target object of this node.
- */
- private class TargetObserver extends EODelayedObserver
- {
- Reference ref;
-
- /**
- * Pass in the display group node that will be updated
- * when the target changes.
- */
- public TargetObserver( DisplayGroupNode aDisplayGroup )
- {
- ref = new WeakReference( aDisplayGroup );
- }
-
- /**
- * Repopulate our display group, and calculate the deltas
- * so we can broadcast appropriate events.
- */
- public void subjectChanged ()
- {
- DisplayGroupNode node = (DisplayGroupNode) ref.get();
- if ( node == null ) return; // node is null if gc'd.
- //FIXME: should un-register self from observer center??
-
- node.targetChanged();
- }
- }
+ if (parentAssociation.isVisible(this) && targetObserver != null) {
+ targetObserver.objectWillChange(target); // force ui to update
+ fireNodeChanged();
+ } else // make sure we fetch children when we do become visible
+ setFetchNeeded(true);
+ return;
+ }
+
+ // if our target object was updated, set fetchNeeded plus fire changed event
+ NSArray updates = (NSArray) userInfo.objectForKey(EOObjectStore.UpdatedKey);
+ if (updates.indexOfIdenticalObject(target) != NSArray.NotFound) {
+ if (parentAssociation.isVisible(this) && targetObserver != null) {
+ targetObserver.objectWillChange(target); // force ui to update
+ fireNodeChanged();
+ if (object() instanceof Component)
+ ((Component) object()).repaint();
+ } else // make sure we fetch children when we do become visible
+ setFetchNeeded(true);
+ return;
+ }
+ }
+
+ super.objectsInvalidatedInEditingContext(aNotification);
+
+ }
+
+ // inner classes
+
+ /**
+ * Private class used to force a hashmap to perform key comparisons by
+ * reference.
+ */
+ private class ReferenceKey {
+ private int hashCode;
+ private Object referent;
+
+ public ReferenceKey(Object anObject) {
+ referent = anObject;
+ hashCode = anObject.hashCode();
+ }
+
+ /**
+ * Returns the actual key's hash code.
+ */
+ public int hashCode() {
+ return hashCode;
+ }
+
+ /**
+ * Compares by reference.
+ */
+ public boolean equals(Object anObject) {
+ if (anObject instanceof ReferenceKey) {
+ return ((ReferenceKey) anObject).referent == referent;
+ }
+ return false;
+ }
+ }
+
+ /**
+ * A private class to observe the target object of this node.
+ */
+ private class TargetObserver extends EODelayedObserver {
+ Reference ref;
+
+ /**
+ * Pass in the display group node that will be updated when the target changes.
+ */
+ public TargetObserver(DisplayGroupNode aDisplayGroup) {
+ ref = new WeakReference(aDisplayGroup);
+ }
+
+ /**
+ * Repopulate our display group, and calculate the deltas so we can broadcast
+ * appropriate events.
+ */
+ public void subjectChanged() {
+ DisplayGroupNode node = (DisplayGroupNode) ref.get();
+ if (node == null)
+ return; // node is null if gc'd.
+ // FIXME: should un-register self from observer center??
+
+ node.targetChanged();
+ }
+ }
}
/*
- * $Log$
- * Revision 1.2 2006/02/18 23:19:05 cgruber
- * Update imports and maven dependencies.
+ * $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.1 2006/02/16 13:22:22 cgruber Check in all sources in
+ * eclipse-friendly maven-enabled packages.
*
- * Revision 1.64 2003/08/06 23:07:52 chochos
- * general code cleanup (mostly, removing unused imports)
+ * Revision 1.64 2003/08/06 23:07:52 chochos general code cleanup (mostly,
+ * removing unused imports)
*
- * Revision 1.63 2003/06/06 14:20:07 mpowers
- * getLoadedDescendants was forcing a fetch of the node it was called on.
+ * Revision 1.63 2003/06/06 14:20:07 mpowers getLoadedDescendants was forcing a
+ * fetch of the node it was called on.
*
- * Revision 1.62 2003/06/03 14:48:33 mpowers
- * Clean-up of notification handling for updates/invalidation/etc.
- * Now fetching immediately on notification if the node is visible.
- * This averts the infamous IndexOutOfBoundsException that occurs
- * if fetching happens during repaint, because the BasicTreeUI is
- * caching the number of child nodes before painting begins.
+ * Revision 1.62 2003/06/03 14:48:33 mpowers Clean-up of notification handling
+ * for updates/invalidation/etc. Now fetching immediately on notification if the
+ * node is visible. This averts the infamous IndexOutOfBoundsException that
+ * occurs if fetching happens during repaint, because the BasicTreeUI is caching
+ * the number of child nodes before painting begins.
*
- * Revision 1.61 2003/01/18 23:33:29 mpowers
- * Fixing the build.
+ * Revision 1.61 2003/01/18 23:33:29 mpowers Fixing the build.
*
- * Revision 1.60 2002/05/31 15:03:10 mpowers
- * Fixes for the previous fix. Fat props to yjcheung.
+ * Revision 1.60 2002/05/31 15:03:10 mpowers Fixes for the previous fix. Fat
+ * props to yjcheung.
*
- * Revision 1.59 2002/05/28 15:31:36 mpowers
- * Fix for updateDisplayedObjects for a subtle case where a node appears in
- * the position that another node was moved from.
+ * Revision 1.59 2002/05/28 15:31:36 mpowers Fix for updateDisplayedObjects for
+ * a subtle case where a node appears in the position that another node was
+ * moved from.
*
- * Revision 1.58 2002/05/24 14:42:02 mpowers
- * Prevent repeat events from firing if firing events loops back.
+ * Revision 1.58 2002/05/24 14:42:02 mpowers Prevent repeat events from firing
+ * if firing events loops back.
*
- * Revision 1.57 2002/04/23 19:12:28 mpowers
- * Reimplemented fireEventsForChanges. Fitter and happier.
+ * Revision 1.57 2002/04/23 19:12:28 mpowers Reimplemented fireEventsForChanges.
+ * Fitter and happier.
*
- * Revision 1.56 2002/04/19 21:18:45 mpowers
- * Removed tree event coalescing, which was causing way too many problems.
- * The fireChangeEvent algorithm is way faster than before, so we should
- * still be better off than before. At least now, we don't have to track
- * whether the view component has encountered a particular node.
+ * Revision 1.56 2002/04/19 21:18:45 mpowers Removed tree event coalescing,
+ * which was causing way too many problems. The fireChangeEvent algorithm is way
+ * faster than before, so we should still be better off than before. At least
+ * now, we don't have to track whether the view component has encountered a
+ * particular node.
*
- * Revision 1.55 2002/04/19 20:53:22 mpowers
- * Now firing event fewer events in fireEventsForChanges.
+ * Revision 1.55 2002/04/19 20:53:22 mpowers Now firing event fewer events in
+ * fireEventsForChanges.
*
- * Revision 1.54 2002/04/15 21:52:50 mpowers
- * Tightening up TreeModelAssociation and DisplayGroupNode.
- * Now only firing root structure changed once.
- * Now disposing of root's children.
- * Better event coalescing.
+ * Revision 1.54 2002/04/15 21:52:50 mpowers Tightening up TreeModelAssociation
+ * and DisplayGroupNode. Now only firing root structure changed once. Now
+ * disposing of root's children. Better event coalescing.
*
- * Revision 1.53 2002/04/12 20:35:20 mpowers
- * Now correctly setting parent display group and data source on creation.
+ * Revision 1.53 2002/04/12 20:35:20 mpowers Now correctly setting parent
+ * display group and data source on creation.
*
- * Revision 1.52 2002/04/10 21:20:04 mpowers
- * Better handling for tree nodes when working with editing contexts.
- * Better handling for invalidation. No longer broadcasting events
- * when nodes have not been "registered" in the tree.
+ * Revision 1.52 2002/04/10 21:20:04 mpowers Better handling for tree nodes when
+ * working with editing contexts. Better handling for invalidation. No longer
+ * broadcasting events when nodes have not been "registered" in the tree.
*
- * Revision 1.51 2002/04/03 20:13:36 mpowers
- * Now differentiating between node instantiation caused by model expansion
- * (user initiated) and by modifications to the model.
- * Dispose now disposes all children.
+ * Revision 1.51 2002/04/03 20:13:36 mpowers Now differentiating between node
+ * instantiation caused by model expansion (user initiated) and by modifications
+ * to the model. Dispose now disposes all children.
*
- * Revision 1.50 2002/03/23 16:20:27 mpowers
- * Optimized processRecentChanges, minimized tree events.
+ * Revision 1.50 2002/03/23 16:20:27 mpowers Optimized processRecentChanges,
+ * minimized tree events.
*
- * Revision 1.49 2002/03/11 03:15:06 mpowers
- * Optimized processRecentChanges, minimize event firing, coalescing changes.
- * Still need a better diff algorithm to avoid removing nodes.
+ * Revision 1.49 2002/03/11 03:15:06 mpowers Optimized processRecentChanges,
+ * minimize event firing, coalescing changes. Still need a better diff algorithm
+ * to avoid removing nodes.
*
- * Revision 1.48 2002/03/10 00:59:39 mpowers
- * Interim version: coalesces calls to process recent changes.
- * Still does not handle rearranged nodes.
+ * Revision 1.48 2002/03/10 00:59:39 mpowers Interim version: coalesces calls to
+ * process recent changes. Still does not handle rearranged nodes.
*
- * Revision 1.47 2002/03/09 17:33:45 mpowers
- * Nodes now track their child nodes by reference, not index.
+ * Revision 1.47 2002/03/09 17:33:45 mpowers Nodes now track their child nodes
+ * by reference, not index.
*
- * Revision 1.46 2002/03/08 23:19:07 mpowers
- * Added getParentGroup to DisplayGroupNode.
+ * Revision 1.46 2002/03/08 23:19:07 mpowers Added getParentGroup to
+ * DisplayGroupNode.
*
- * Revision 1.45 2002/03/06 13:04:16 mpowers
- * Implemented cascading qualifiers in tree nodes.
+ * Revision 1.45 2002/03/06 13:04:16 mpowers Implemented cascading qualifiers in
+ * tree nodes.
*
- * Revision 1.44 2002/02/27 23:19:17 mpowers
- * Refactoring of TreeAssociation to create TreeModelAssociation parent.
+ * Revision 1.44 2002/02/27 23:19:17 mpowers Refactoring of TreeAssociation to
+ * create TreeModelAssociation parent.
*
- * Revision 1.42 2002/02/19 22:28:46 mpowers
- * DisplayGroupNodes immediately unregister themselves as editors.
+ * Revision 1.42 2002/02/19 22:28:46 mpowers DisplayGroupNodes immediately
+ * unregister themselves as editors.
*
- * Revision 1.41 2002/02/13 16:27:38 mpowers
- * Exposing setTarget.
+ * Revision 1.41 2002/02/13 16:27:38 mpowers Exposing setTarget.
*
- * Revision 1.40 2001/11/02 20:55:46 mpowers
- * Now using fixed index to send node removed events. This preserves the
- * expanded state of the nodes in the corresponding jtree.
+ * Revision 1.40 2001/11/02 20:55:46 mpowers Now using fixed index to send node
+ * removed events. This preserves the expanded state of the nodes in the
+ * corresponding jtree.
*
- * Revision 1.39 2001/09/21 21:09:25 mpowers
- * Exposed more fields as protected.
+ * Revision 1.39 2001/09/21 21:09:25 mpowers Exposed more fields as protected.
*
- * Revision 1.38 2001/09/19 15:36:08 mpowers
- * Refined behavior for isFetched after notification handling.
+ * Revision 1.38 2001/09/19 15:36:08 mpowers Refined behavior for isFetched
+ * after notification handling.
*
- * Revision 1.37 2001/09/13 14:51:18 mpowers
- * DisplayGroupNodes now dispose themselves and mark their parent for update
- * when they receive notification that their target has been deleted.
+ * Revision 1.37 2001/09/13 14:51:18 mpowers DisplayGroupNodes now dispose
+ * themselves and mark their parent for update when they receive notification
+ * that their target has been deleted.
*
- * Revision 1.36 2001/09/10 14:10:24 mpowers
- * Fix for notification handling.
+ * Revision 1.36 2001/09/10 14:10:24 mpowers Fix for notification handling.
*
- * Revision 1.35 2001/07/30 16:17:01 mpowers
- * Minor code cleanup.
+ * Revision 1.35 2001/07/30 16:17:01 mpowers Minor code cleanup.
*
- * Revision 1.34 2001/07/18 22:13:39 mpowers
- * getLoadedDescendants now works as advertised.
- * Now correctly handling invalidateAllObjects notification.
+ * Revision 1.34 2001/07/18 22:13:39 mpowers getLoadedDescendants now works as
+ * advertised. Now correctly handling invalidateAllObjects notification.
*
- * Revision 1.33 2001/07/18 13:03:32 mpowers
- * TreeNodes now refetch only on demand. Previously, once a node had
- * been fetched, it was always refetched after an invalidate, even if
- * the node was not being displayed.
+ * Revision 1.33 2001/07/18 13:03:32 mpowers TreeNodes now refetch only on
+ * demand. Previously, once a node had been fetched, it was always refetched
+ * after an invalidate, even if the node was not being displayed.
*
- * Revision 1.32 2001/06/18 14:10:28 mpowers
- * Cleaned up event firing: no longer firing insert or remove events twice.
+ * Revision 1.32 2001/06/18 14:10:28 mpowers Cleaned up event firing: no longer
+ * firing insert or remove events twice.
*
- * Revision 1.31 2001/06/09 16:15:39 mpowers
- * Revised the targetChanged scheme because oldObjects and newObjects were
- * identical after the target object is invalidated.
+ * Revision 1.31 2001/06/09 16:15:39 mpowers Revised the targetChanged scheme
+ * because oldObjects and newObjects were identical after the target object is
+ * invalidated.
*
- * Revision 1.30 2001/05/21 22:17:19 mpowers
- * Fix for tree out-of-synch problems when nodes are inserted.
+ * Revision 1.30 2001/05/21 22:17:19 mpowers Fix for tree out-of-synch problems
+ * when nodes are inserted.
*
- * Revision 1.29 2001/05/18 21:07:46 mpowers
- * Playing with refresh options.
+ * Revision 1.29 2001/05/18 21:07:46 mpowers Playing with refresh options.
*
- * Revision 1.28 2001/05/14 15:25:43 mpowers
- * DisplayGroupNodes now only respond to InvalidateAllObjectsInStore
- * if they are already fetched.
+ * Revision 1.28 2001/05/14 15:25:43 mpowers DisplayGroupNodes now only respond
+ * to InvalidateAllObjectsInStore if they are already fetched.
*
- * Revision 1.27 2001/05/08 19:55:58 mpowers
- * Fix for node children not refreshing after sibling was inserted.
+ * Revision 1.27 2001/05/08 19:55:58 mpowers Fix for node children not
+ * refreshing after sibling was inserted.
*
- * Revision 1.26 2001/05/08 18:47:34 mpowers
- * Minor fixes for d3.
+ * Revision 1.26 2001/05/08 18:47:34 mpowers Minor fixes for d3.
*
- * Revision 1.25 2001/05/06 22:22:55 mpowers
- * Debugging.
+ * Revision 1.25 2001/05/06 22:22:55 mpowers Debugging.
*
- * Revision 1.24 2001/05/04 14:42:58 mpowers
- * Now getting stored values in KeyValueCoding.
- * MasterDetail now marks dirty based on whether it's an attribute
- * or relation.
- * Implemented editing context marker.
+ * Revision 1.24 2001/05/04 14:42:58 mpowers Now getting stored values in
+ * KeyValueCoding. MasterDetail now marks dirty based on whether it's an
+ * attribute or relation. Implemented editing context marker.
*
- * Revision 1.23 2001/05/02 18:00:43 mpowers
- * Removed debug code.
+ * Revision 1.23 2001/05/02 18:00:43 mpowers Removed debug code.
*
- * Revision 1.22 2001/05/02 17:31:20 mpowers
- * DisplayGroupNode now does a better job determining when to mark its
- * parent dirty.
+ * Revision 1.22 2001/05/02 17:31:20 mpowers DisplayGroupNode now does a better
+ * job determining when to mark its parent dirty.
*
- * Revision 1.21 2001/05/01 00:52:32 mpowers
- * Implemented breadth-first traversal of tree for node.
+ * Revision 1.21 2001/05/01 00:52:32 mpowers Implemented breadth-first traversal
+ * of tree for node.
*
- * Revision 1.20 2001/04/26 01:15:19 mpowers
- * Major clean-up of DisplayGroupNode: fitter, happier, more productive.
+ * Revision 1.20 2001/04/26 01:15:19 mpowers Major clean-up of DisplayGroupNode:
+ * fitter, happier, more productive.
*
- * Revision 1.19 2001/04/22 23:13:35 mpowers
- * Minor bug.
+ * Revision 1.19 2001/04/22 23:13:35 mpowers Minor bug.
*
- * Revision 1.18 2001/04/22 23:05:33 mpowers
- * Totally revised DisplayGroupNode so each object gets its own node
- * (so the nodes are no longer fixed by index).
+ * Revision 1.18 2001/04/22 23:05:33 mpowers Totally revised DisplayGroupNode so
+ * each object gets its own node (so the nodes are no longer fixed by index).
*
- * Revision 1.17 2001/04/21 23:05:12 mpowers
- * A fairly major revisiting. I've decided to scrap the pass-thru approach
- * where every node simply represents an index and not an object.
- * The next update will have each node correspond to a specific object.
+ * Revision 1.17 2001/04/21 23:05:12 mpowers A fairly major revisiting. I've
+ * decided to scrap the pass-thru approach where every node simply represents an
+ * index and not an object. The next update will have each node correspond to a
+ * specific object.
*
- * Revision 1.16 2001/04/13 16:37:37 mpowers
- * Handling bounds checking.
+ * Revision 1.16 2001/04/13 16:37:37 mpowers Handling bounds checking.
*
- * Revision 1.15 2001/04/03 20:36:01 mpowers
- * Fixed refaulting/reverting/invalidating to be self-consistent.
+ * Revision 1.15 2001/04/03 20:36:01 mpowers Fixed
+ * refaulting/reverting/invalidating to be self-consistent.
*
- * Revision 1.14 2001/03/27 17:45:51 mpowers
- * More index bounds checking.
+ * Revision 1.14 2001/03/27 17:45:51 mpowers More index bounds checking.
*
- * Revision 1.13 2001/03/22 21:25:42 mpowers
- * Fixed some nasty issues with jtree's internal state and array bounds.
+ * Revision 1.13 2001/03/22 21:25:42 mpowers Fixed some nasty issues with
+ * jtree's internal state and array bounds.
*
- * Revision 1.12 2001/03/19 22:18:58 mpowers
- * Root node now mirrors contents of titles display group.
+ * Revision 1.12 2001/03/19 22:18:58 mpowers Root node now mirrors contents of
+ * titles display group.
*
- * Revision 1.11 2001/03/19 21:38:36 mpowers
- * Improved redisplay after edit. Editing nodes off root now works.
+ * Revision 1.11 2001/03/19 21:38:36 mpowers Improved redisplay after edit.
+ * Editing nodes off root now works.
*
- * Revision 1.10 2001/03/09 22:08:38 mpowers
- * Removed unused line.
+ * Revision 1.10 2001/03/09 22:08:38 mpowers Removed unused line.
*
- * Revision 1.9 2001/03/07 16:41:04 mpowers
- * Now checking size of parent displayed objects array so that we don't
- * get array out of bounds execeptions from isLeaf() or object() when
- * those messages are called after the TreeAssociation fires a
- * nodesDeleted event. I believe that JTree is mistakenly rendering
- * those nodes one last time before erasing them.
+ * Revision 1.9 2001/03/07 16:41:04 mpowers Now checking size of parent
+ * displayed objects array so that we don't get array out of bounds execeptions
+ * from isLeaf() or object() when those messages are called after the
+ * TreeAssociation fires a nodesDeleted event. I believe that JTree is
+ * mistakenly rendering those nodes one last time before erasing them.
*
- * Revision 1.8 2001/03/06 23:21:27 mpowers
- * Now only notifying parent if the object is not registered in the
- * editing context, if any.
+ * Revision 1.8 2001/03/06 23:21:27 mpowers Now only notifying parent if the
+ * object is not registered in the editing context, if any.
*
- * Revision 1.7 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.7 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.6 2001/02/17 16:52:05 mpowers
- * Changes in imports to support building with jdk1.1 collections.
+ * Revision 1.6 2001/02/17 16:52:05 mpowers Changes in imports to support
+ * building with jdk1.1 collections.
*
- * Revision 1.5 2001/01/31 17:59:52 mpowers
- * Fixed isLeaf aspect of TreeAssociation.
+ * Revision 1.5 2001/01/31 17:59:52 mpowers Fixed isLeaf aspect of
+ * TreeAssociation.
*
- * Revision 1.4 2001/01/25 02:16:25 mpowers
- * TreeAssociation now returns DisplayGroupNode.getUserObject.
+ * Revision 1.4 2001/01/25 02:16:25 mpowers TreeAssociation now returns
+ * DisplayGroupNode.getUserObject.
*
- * Revision 1.3 2001/01/24 18:14:40 mpowers
- * Fixed problem with leaving children aspect unspecified.
+ * Revision 1.3 2001/01/24 18:14:40 mpowers Fixed problem with leaving children
+ * aspect unspecified.
*
- * Revision 1.2 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.2 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.1 2001/01/24 14:17:12 mpowers
- * Major revision to TreeAssociation. Can now add and remove nodes.
- * DisplayGroupNode is now it's own class.
+ * Revision 1.1 2001/01/24 14:17:12 mpowers Major revision to TreeAssociation.
+ * Can now add and remove nodes. DisplayGroupNode is now it's own class.
*
*
*/
-
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/ListAssociation.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/ListAssociation.java
index ec22bfd..440765c 100644
--- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/ListAssociation.java
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/ListAssociation.java
@@ -34,206 +34,166 @@ import net.wotonomy.ui.EOAssociation;
import net.wotonomy.ui.EODisplayGroup;
/**
-* TextAssociation binds a JList to a display group's
-* list of displayable objects. Bindings are:
-* <ul>
-* <li>titles: a property convertable to a string for
-* display in the cells of the list</li>
-* </ul>
-*
-* @author michael@mpowers.net
-* @author $Author: cgruber $
-* @version $Revision: 904 $
-*/
-public class ListAssociation extends EOAssociation
- implements ListSelectionListener,
- ListDataListener
-{
- static final NSArray aspects =
- new NSArray( new Object[] {
- TitlesAspect
- } );
- static final NSArray aspectSignatures =
- new NSArray( new Object[] {
- AttributeToOneAspectSignature
- } );
- static final NSArray objectKeysTaken =
- new NSArray( new Object[] {
- "items"
- } );
-
- protected DefaultListModel model;
-
- /**
- * Constructor expecting a JList. Throws an
- * exception if it doesn't receive one.
- * Note: This sets the JList's model to a DefaultListModel.
- */
- public ListAssociation ( Object anObject )
- {
- super( anObject );
- model = new DefaultListModel();
- ((JList)anObject).setModel( model );
- }
-
- /**
- * 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. <ul>
- * <li>"A" attribute: the aspect can be bound to
- * an attribute.</li>
- * <li>"1" to-one: the aspect can be bound to a
- * property that returns a single object.</li>
- * <li>"M" to-one: the aspect can be bound to a
- * property that returns multiple objects.</li>
- * </ul>
- * 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 ( anObject instanceof JList );
- }
-
- /**
- * 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. This is typically "value" or somesuch.
- */
- public static String primaryAspect ()
- {
- return TitlesAspect;
- }
-
- /**
- * 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 ()
- {
- addAsListener();
- super.establishConnection();
- populateFromDisplayGroup();
- selectFromDisplayGroup();
- }
-
- /**
- * Breaks the connection between this association and
- * its object. Override to stop listening for events
- * from the object.
- */
- public void breakConnection ()
- {
- removeAsListener();
- super.breakConnection();
- }
-
- protected void addAsListener()
- { // System.out.println( "ListAssociation.addAsListener: " + ++count );
- component().getModel().addListDataListener( this );
- component().addListSelectionListener( this );
- }
-
- protected void removeAsListener()
- { // System.out.println( "ListAssociation.removeAsListener: " + --count );
- component().getModel().removeListDataListener( this );
- component().removeListSelectionListener( this );
- }
-
- /**
- * Called when either the selection or the contents
- * of an associated display group have changed.
- */
- public void subjectChanged ()
- {
+ * TextAssociation binds a JList to a display group's list of displayable
+ * objects. Bindings are:
+ * <ul>
+ * <li>titles: a property convertable to a string for display in the cells of
+ * the list</li>
+ * </ul>
+ *
+ * @author michael@mpowers.net
+ * @author $Author: cgruber $
+ * @version $Revision: 904 $
+ */
+public class ListAssociation extends EOAssociation implements ListSelectionListener, ListDataListener {
+ static final NSArray aspects = new NSArray(new Object[] { TitlesAspect });
+ static final NSArray aspectSignatures = new NSArray(new Object[] { AttributeToOneAspectSignature });
+ static final NSArray objectKeysTaken = new NSArray(new Object[] { "items" });
+
+ protected DefaultListModel model;
+
+ /**
+ * Constructor expecting a JList. Throws an exception if it doesn't receive one.
+ * Note: This sets the JList's model to a DefaultListModel.
+ */
+ public ListAssociation(Object anObject) {
+ super(anObject);
+ model = new DefaultListModel();
+ ((JList) anObject).setModel(model);
+ }
+
+ /**
+ * 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.
+ * <ul>
+ * <li>"A" attribute: the aspect can be bound to an attribute.</li>
+ * <li>"1" to-one: the aspect can be bound to a property that returns a single
+ * object.</li>
+ * <li>"M" to-one: the aspect can be bound to a property that returns multiple
+ * objects.</li>
+ * </ul>
+ * 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 (anObject instanceof JList);
+ }
+
+ /**
+ * 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. This is typically
+ * "value" or somesuch.
+ */
+ public static String primaryAspect() {
+ return TitlesAspect;
+ }
+
+ /**
+ * 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() {
+ addAsListener();
+ super.establishConnection();
+ populateFromDisplayGroup();
+ selectFromDisplayGroup();
+ }
+
+ /**
+ * Breaks the connection between this association and its object. Override to
+ * stop listening for events from the object.
+ */
+ public void breakConnection() {
+ removeAsListener();
+ super.breakConnection();
+ }
+
+ protected void addAsListener() { // System.out.println( "ListAssociation.addAsListener: " + ++count );
+ component().getModel().addListDataListener(this);
+ component().addListSelectionListener(this);
+ }
+
+ protected void removeAsListener() { // System.out.println( "ListAssociation.removeAsListener: " + --count );
+ component().getModel().removeListDataListener(this);
+ component().removeListSelectionListener(this);
+ }
+
+ /**
+ * Called when either the selection or the contents of an associated display
+ * group have changed.
+ */
+ public void subjectChanged() {
EODisplayGroup displayGroup;
-
+
// titles aspect
- displayGroup = displayGroupForAspect( TitlesAspect );
- if ( displayGroup != null )
- {
+ displayGroup = displayGroupForAspect(TitlesAspect);
+ if (displayGroup != null) {
//System.out.println( "subjectChanged: " +
//displayGroup.contentsChanged() + " : " + displayGroup.selectionChanged()
//+ " : " + displayGroup.updatedObjectIndex() );
//new net.wotonomy.ui.swing.util.StackTraceInspector();
- if ( displayGroup.contentsChanged() )
- {
+ if (displayGroup.contentsChanged()) {
populateFromDisplayGroup();
}
- if ( displayGroup.selectionChanged() )
- {
+ if (displayGroup.selectionChanged()) {
selectFromDisplayGroup();
}
}
- }
-
- private void populateFromDisplayGroup()
- {
+ }
+
+ private void populateFromDisplayGroup() {
JList component = component();
- EODisplayGroup displayGroup = displayGroupForAspect( TitlesAspect );
- String key = displayGroupKeyForAspect( TitlesAspect );
-
+ EODisplayGroup displayGroup = displayGroupForAspect(TitlesAspect);
+ String key = displayGroupKeyForAspect(TitlesAspect);
+
removeAsListener();
-
+
// remember selection
int[] selectedIndices = component().getSelectedIndices();
-
+
// clear the model
model.removeAllElements();
@@ -241,128 +201,111 @@ public class ListAssociation extends EOAssociation
Object value;
int size = displayGroup.displayedObjects().count();
//System.out.println( "populateFromDisplayGroup: " + size );
- for ( int i = 0; i < size; i++ )
- {
- value = displayGroup.valueForObjectAtIndex( i, key );
- if ( value == null ) value = "[null]";
- model.addElement( value );
+ for (int i = 0; i < size; i++) {
+ value = displayGroup.valueForObjectAtIndex(i, key);
+ if (value == null)
+ value = "[null]";
+ model.addElement(value);
}
-
+
// select the same indexes
- for ( int i = 0; i < selectedIndices.length; i++ )
- {
- component.addSelectionInterval(
- selectedIndices[i], selectedIndices[i] ); // adds one row
+ for (int i = 0; i < selectedIndices.length; i++) {
+ component.addSelectionInterval(selectedIndices[i], selectedIndices[i]); // adds one row
}
addAsListener();
}
-
- private void selectFromDisplayGroup()
- {
+
+ private void selectFromDisplayGroup() {
JList component = component();
- EODisplayGroup displayGroup = displayGroupForAspect( TitlesAspect );
+ EODisplayGroup displayGroup = displayGroupForAspect(TitlesAspect);
removeAsListener();
-
+
int index;
component.clearSelection();
- Enumeration e =
- displayGroup.selectionIndexes().objectEnumerator();
-
- while ( e.hasMoreElements() )
- { // add selections one-by-one to support non-contiguous
- index = ((Number)e.nextElement()).intValue();
- component.addSelectionInterval(
- index, index ); // adds one row
+ Enumeration e = displayGroup.selectionIndexes().objectEnumerator();
+
+ while (e.hasMoreElements()) { // add selections one-by-one to support non-contiguous
+ index = ((Number) e.nextElement()).intValue();
+ component.addSelectionInterval(index, index); // adds one row
}
-
+
addAsListener();
}
-
+
// interface ListSelectionListener
- public void valueChanged(ListSelectionEvent e)
- {
- final EODisplayGroup displayGroup =
- displayGroupForAspect( TitlesAspect );
- if ( ( displayGroup != null ) && ( ! e.getValueIsAdjusting() ) )
- {
- int[] selectedIndices = component().getSelectedIndices();
+ public void valueChanged(ListSelectionEvent e) {
+ final EODisplayGroup displayGroup = displayGroupForAspect(TitlesAspect);
+ if ((displayGroup != null) && (!e.getValueIsAdjusting())) {
+ int[] selectedIndices = component().getSelectedIndices();
final NSMutableArray indexList = new NSMutableArray();
- for ( int i = 0; i < selectedIndices.length; i++ )
- {
- indexList.addObject( new Integer( selectedIndices[i] ) );
+ for (int i = 0; i < selectedIndices.length; i++) {
+ indexList.addObject(new Integer(selectedIndices[i]));
}
-
- // invoke later so the component is repainted before
- // any potentially lengthy second-order effects happen:
- // this improves user-perceived responsiveness of big apps
- SwingUtilities.invokeLater( new Runnable() {
- public void run()
- {
- displayGroup.setSelectionIndexes( indexList );
- }
- });
- }
- }
-
- // interface ListDataListener
-
- public void intervalAdded(ListDataEvent e)
- {
- // System.out.println( "intervalAdded" );
- contentsChanged(e);
- }
- public void intervalRemoved(ListDataEvent e)
- {
- // System.out.println( "intervalRemoved" );
- contentsChanged(e);
- }
- public void contentsChanged(ListDataEvent e)
- {
- // System.out.println( "contentsChanged" );
-
- // if we were editing a property,
+
+ // invoke later so the component is repainted before
+ // any potentially lengthy second-order effects happen:
+ // this improves user-perceived responsiveness of big apps
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ displayGroup.setSelectionIndexes(indexList);
+ }
+ });
+ }
+ }
+
+ // interface ListDataListener
+
+ public void intervalAdded(ListDataEvent e) {
+ // System.out.println( "intervalAdded" );
+ contentsChanged(e);
+ }
+
+ public void intervalRemoved(ListDataEvent e) {
+ // System.out.println( "intervalRemoved" );
+ contentsChanged(e);
+ }
+
+ public void contentsChanged(ListDataEvent e) {
+ // System.out.println( "contentsChanged" );
+
+ // if we were editing a property,
// we'd notify our display group now.
- }
-
+ }
+
// convenience
- private JList component()
- {
- return (JList) object();
- }
+ private JList component() {
+ return (JList) object();
+ }
}
/*
- * $Log$
- * Revision 1.2 2006/02/18 23:19:05 cgruber
- * Update imports and maven dependencies.
+ * $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.1 2006/02/16 13:22:22 cgruber Check in all sources in
+ * eclipse-friendly maven-enabled packages.
*
- * Revision 1.5 2003/08/06 23:07:52 chochos
- * general code cleanup (mostly, removing unused imports)
+ * Revision 1.5 2003/08/06 23:07:52 chochos general code cleanup (mostly,
+ * removing unused imports)
*
- * Revision 1.4 2002/05/15 14:05:55 mpowers
- * Now appropriately selectingFromDisplayGroup on establishConnection.
+ * Revision 1.4 2002/05/15 14:05:55 mpowers Now appropriately
+ * selectingFromDisplayGroup on establishConnection.
*
- * Revision 1.3 2001/09/14 13:40:26 mpowers
- * User-initiated selection changes are now handled on the next event loop
- * so that the component repaints the new selection before any potentially
- * lengthy logic is triggered by the selection change.
+ * Revision 1.3 2001/09/14 13:40:26 mpowers User-initiated selection changes are
+ * now handled on the next event loop so that the component repaints the new
+ * selection before any potentially lengthy logic is triggered by the selection
+ * change.
*
- * Revision 1.2 2001/02/17 16:52:05 mpowers
- * Changes in imports to support building with jdk1.1 collections.
+ * 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:49 mpowers
- * Contributing wotonomy.
+ * Revision 1.1.1.1 2000/12/21 15:48:49 mpowers Contributing wotonomy.
*
- * Revision 1.5 2000/12/20 16:25:41 michael
- * Added log to all files.
+ * Revision 1.5 2000/12/20 16:25:41 michael Added log to all files.
*
*
*/
-
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/MutableDisplayGroupNode.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/MutableDisplayGroupNode.java
index f1568ec..5293bc9 100644
--- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/MutableDisplayGroupNode.java
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/MutableDisplayGroupNode.java
@@ -32,185 +32,143 @@ import net.wotonomy.foundation.internal.WotonomyException;
import net.wotonomy.ui.EODisplayGroup;
/**
-* A DisplayGroupNode that exposes the MutableTreeNode interface.
-* This was required so that other subclasses of DisplayGroupNode
-* could opt out of supporting MutableTreeNode (so that they can
-* implement IlvActivity, for example).
-*
-* @author michael@mpowers.net
-* @author $Author: cgruber $
-* @version $Revision: 904 $
-*/
- public class MutableDisplayGroupNode
- extends DisplayGroupNode implements MutableTreeNode
- {
- /**
- * Constructor for all nodes.
- * Root node must have a null delegate.
- */
- public MutableDisplayGroupNode(
- TreeModelAssociation aParentAssociation,
- EODisplayGroup aParentGroup,
- Object anObject )
- {
- super( aParentAssociation, aParentGroup, anObject );
- }
-
- public int getIndex(TreeNode node)
- {
- return getIndex( (DisplayGroupNode) node );
- }
-
- public TreeNode getChildAt(int childIndex)
- {
- return (TreeNode) getChildNodeAt( childIndex );
- }
-
- public TreeNode getParent()
- {
- Object parent = getParentGroup();
- if ( parent instanceof TreeNode )
- {
- return (TreeNode) parent;
- }
- return null;
- }
-
- public void insert(MutableTreeNode aChild, int anIndex)
- {
- if ( aChild instanceof DisplayGroupNode )
- {
- insertObjectAtIndex(
- ((DisplayGroupNode)aChild).object(), anIndex );
- }
- else // not a display group node
- {
- throw new WotonomyException(
- "Cannot insert nodes of type: " + aChild );
- }
- }
-
- /**
- * Removes the node at the index corresponding
- * to the index of the object.
- */
- public void remove(MutableTreeNode node)
- {
- if ( node instanceof DisplayGroupNode )
- {
- remove((DisplayGroupNode)node);
- }
- else // not a display group node
- {
- throw new WotonomyException(
- "Cannot insert nodes of type: " + node );
- }
- }
-
- /**
- * Removes the value in the parent display group
- * at the index that corresponds to the index of this node
- * and add it to the end of the display group that corresponds
- * to the user value of the specified node.
- */
- public void setParent(MutableTreeNode newParent)
- {
- if ( newParent instanceof DisplayGroupNode )
- {
- setParent((DisplayGroupNode)newParent);
- }
- else // not a display group node
- {
- throw new WotonomyException(
- "Cannot set parent to nodes of type: " + newParent );
- }
- }
-
- /**
- * Overridden to remember expanded state for nodes
- * after nodes have been rearranged.
- */
- protected void fireEventsForChanges(
- Object[] oldObjects, Object[] newObjects )
- {
- if ( !( parentAssociation.object() instanceof JTree ) )
- {
- super.fireEventsForChanges( oldObjects, newObjects );
- return;
- }
-
- JTree tree = (JTree) parentAssociation.object();
- Map expansionMap = new HashMap();
- DisplayGroupNode node;
- TreePath path;
- for ( int i = 0; i < oldObjects.length; i++ )
- {
- node = (DisplayGroupNode)
- getChildNodeForObject( oldObjects[i] );
- if ( node != null && ! node.isLeaf() )
- {
- expansionMap.put( node, new Boolean(
- tree.isExpanded( node.treePath() ) ) );
- }
- }
-
- super.fireEventsForChanges( oldObjects, newObjects );
-
- Object value;
- Iterator iterator = new LinkedList( childNodes.values() ).iterator();
- while ( iterator.hasNext() )
- {
- node = (DisplayGroupNode) iterator.next();
- value = expansionMap.get( node );
- if ( value != null )
- {
- if ( Boolean.TRUE.equals( value ) )
- {
- tree.expandPath( node.treePath() );
- }
- else
- {
- tree.collapsePath( node.treePath() );
- }
- }
- }
- }
- }
+ * A DisplayGroupNode that exposes the MutableTreeNode interface. This was
+ * required so that other subclasses of DisplayGroupNode could opt out of
+ * supporting MutableTreeNode (so that they can implement IlvActivity, for
+ * example).
+ *
+ * @author michael@mpowers.net
+ * @author $Author: cgruber $
+ * @version $Revision: 904 $
+ */
+public class MutableDisplayGroupNode extends DisplayGroupNode implements MutableTreeNode {
+ /**
+ * Constructor for all nodes. Root node must have a null delegate.
+ */
+ public MutableDisplayGroupNode(TreeModelAssociation aParentAssociation, EODisplayGroup aParentGroup,
+ Object anObject) {
+ super(aParentAssociation, aParentGroup, anObject);
+ }
+
+ public int getIndex(TreeNode node) {
+ return getIndex((DisplayGroupNode) node);
+ }
+
+ public TreeNode getChildAt(int childIndex) {
+ return (TreeNode) getChildNodeAt(childIndex);
+ }
+
+ public TreeNode getParent() {
+ Object parent = getParentGroup();
+ if (parent instanceof TreeNode) {
+ return (TreeNode) parent;
+ }
+ return null;
+ }
+
+ public void insert(MutableTreeNode aChild, int anIndex) {
+ if (aChild instanceof DisplayGroupNode) {
+ insertObjectAtIndex(((DisplayGroupNode) aChild).object(), anIndex);
+ } else // not a display group node
+ {
+ throw new WotonomyException("Cannot insert nodes of type: " + aChild);
+ }
+ }
+
+ /**
+ * Removes the node at the index corresponding to the index of the object.
+ */
+ public void remove(MutableTreeNode node) {
+ if (node instanceof DisplayGroupNode) {
+ remove((DisplayGroupNode) node);
+ } else // not a display group node
+ {
+ throw new WotonomyException("Cannot insert nodes of type: " + node);
+ }
+ }
+
+ /**
+ * Removes the value in the parent display group at the index that corresponds
+ * to the index of this node and add it to the end of the display group that
+ * corresponds to the user value of the specified node.
+ */
+ public void setParent(MutableTreeNode newParent) {
+ if (newParent instanceof DisplayGroupNode) {
+ setParent((DisplayGroupNode) newParent);
+ } else // not a display group node
+ {
+ throw new WotonomyException("Cannot set parent to nodes of type: " + newParent);
+ }
+ }
+
+ /**
+ * Overridden to remember expanded state for nodes after nodes have been
+ * rearranged.
+ */
+ protected void fireEventsForChanges(Object[] oldObjects, Object[] newObjects) {
+ if (!(parentAssociation.object() instanceof JTree)) {
+ super.fireEventsForChanges(oldObjects, newObjects);
+ return;
+ }
+
+ JTree tree = (JTree) parentAssociation.object();
+ Map expansionMap = new HashMap();
+ DisplayGroupNode node;
+ TreePath path;
+ for (int i = 0; i < oldObjects.length; i++) {
+ node = (DisplayGroupNode) getChildNodeForObject(oldObjects[i]);
+ if (node != null && !node.isLeaf()) {
+ expansionMap.put(node, new Boolean(tree.isExpanded(node.treePath())));
+ }
+ }
+
+ super.fireEventsForChanges(oldObjects, newObjects);
+
+ Object value;
+ Iterator iterator = new LinkedList(childNodes.values()).iterator();
+ while (iterator.hasNext()) {
+ node = (DisplayGroupNode) iterator.next();
+ value = expansionMap.get(node);
+ if (value != null) {
+ if (Boolean.TRUE.equals(value)) {
+ tree.expandPath(node.treePath());
+ } else {
+ tree.collapsePath(node.treePath());
+ }
+ }
+ }
+ }
+}
/*
- * $Log$
- * Revision 1.2 2006/02/18 23:19:05 cgruber
- * Update imports and maven dependencies.
+ * $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.1 2006/02/16 13:22:22 cgruber Check in all sources in
+ * eclipse-friendly maven-enabled packages.
*
- * Revision 1.8 2003/08/06 23:07:52 chochos
- * general code cleanup (mostly, removing unused imports)
+ * Revision 1.8 2003/08/06 23:07:52 chochos general code cleanup (mostly,
+ * removing unused imports)
*
- * Revision 1.7 2002/04/23 19:12:28 mpowers
- * Reimplemented fireEventsForChanges. Fitter and happier.
+ * Revision 1.7 2002/04/23 19:12:28 mpowers Reimplemented fireEventsForChanges.
+ * Fitter and happier.
*
- * Revision 1.6 2002/04/10 21:20:04 mpowers
- * Better handling for tree nodes when working with editing contexts.
- * Better handling for invalidation. No longer broadcasting events
- * when nodes have not been "registered" in the tree.
+ * Revision 1.6 2002/04/10 21:20:04 mpowers Better handling for tree nodes when
+ * working with editing contexts. Better handling for invalidation. No longer
+ * broadcasting events when nodes have not been "registered" in the tree.
*
- * Revision 1.5 2002/04/03 20:01:47 mpowers
- * Now remembers expanded state.
+ * Revision 1.5 2002/04/03 20:01:47 mpowers Now remembers expanded state.
*
- * Revision 1.4 2002/03/08 23:19:07 mpowers
- * Added getParentGroup to DisplayGroupNode.
+ * Revision 1.4 2002/03/08 23:19:07 mpowers Added getParentGroup to
+ * DisplayGroupNode.
*
- * Revision 1.3 2002/02/27 23:19:17 mpowers
- * Refactoring of TreeAssociation to create TreeModelAssociation parent.
+ * Revision 1.3 2002/02/27 23:19:17 mpowers Refactoring of TreeAssociation to
+ * create TreeModelAssociation parent.
*
- * Revision 1.2 2001/04/22 23:05:33 mpowers
- * Totally revised DisplayGroupNode so each object gets its own node
- * (so the nodes are no longer fixed by index).
+ * Revision 1.2 2001/04/22 23:05:33 mpowers Totally revised DisplayGroupNode so
+ * each object gets its own node (so the nodes are no longer fixed by index).
*
- * Revision 1.1 2001/04/21 23:05:56 mpowers
- * Contributing the tree-specific concrete subclass of DisplayGroupNode.
+ * Revision 1.1 2001/04/21 23:05:56 mpowers Contributing the tree-specific
+ * concrete subclass of DisplayGroupNode.
*
*
*/
-
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/NotificationInspector.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/NotificationInspector.java
index d105d89..4f65672 100644
--- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/NotificationInspector.java
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/NotificationInspector.java
@@ -49,285 +49,252 @@ import net.wotonomy.ui.swing.util.WindowUtilities;
import net.wotonomy.control.internal.Surrogate;
/**
-* The NotificationInspector displays a JFrame that
-* displays notifications as they occur. <br><br>
-*
-* @author michael@mpowers.net
-* @version $Revision: 904 $
-*/
-
-public class NotificationInspector
- implements MouseListener, ActionListener
-{
- protected JTable table;
- protected JPopupMenu popupMenu;
- protected EODisplayGroup displayGroup;
-
- // key command to copy contents to clipboard
- static public final String COPY = "COPY";
-
- protected static final String CLEAR_ALL = "Clear All";
- protected static final String CLEAR_SELECTED = "Clear Selected";
-
-
-/**
-* Displays all notifications on the default notification center.
-*/
- public NotificationInspector()
- {
- this( NSNotificationCenter.defaultCenter() );
- }
-
-/**
-* Displays all notifications from the specified notification center.
-*/
- public NotificationInspector( NSNotificationCenter aCenter )
- {
- this( aCenter, null, null );
- }
-
-/**
-* Displays notifications from the default notification center
-* using the specified name and object filters.
-*/
- public NotificationInspector( String notificationName, Object anObject )
- {
- this( NSNotificationCenter.defaultCenter(),
- notificationName, anObject );
- }
+ * The NotificationInspector displays a JFrame that displays notifications as
+ * they occur. <br>
+ * <br>
+ *
+ * @author michael@mpowers.net
+ * @version $Revision: 904 $
+ */
-/**
-* Displays notifications on the specified notification center
-* using the specified name and object filters.
-*/
- public NotificationInspector( NSNotificationCenter aCenter,
- String notificationName, Object anObject )
- {
- // show stack traces
- NSNotification.showStack = true;
-
- // register for notifications
- NSSelector handleNotification =
- new NSSelector( "handleNotification",
- new Class[] { NSNotification.class } );
- aCenter.addObserver(
- this, handleNotification, notificationName, anObject );
-
- table = new JTable();
-
- popupMenu = new JPopupMenu();
- JMenuItem menuItem = popupMenu.add( CLEAR_SELECTED );
- menuItem.addActionListener( this );
- menuItem = popupMenu.add( CLEAR_ALL );
- menuItem.addActionListener( this );
-
- displayGroup = new EODisplayGroup();
-
- TableColumn column;
- TableColumnAssociation assoc;
-
- column = new TableColumn();
- column.setHeaderValue( "Time" );
- column.setCellRenderer( new FormattedCellRenderer( new SimpleDateFormat( "hh:mm:ss:SS" ) ) );
- column.setPreferredWidth( 90 );
- column.setMaxWidth( 90 );
-
- assoc = new TableColumnAssociation( column );
- assoc.bindAspect( EOAssociation.ValueAspect, displayGroup, "time" );
- assoc.setTable( table );
- assoc.establishConnection();
-
- column = new TableColumn();
- column.setHeaderValue( "Type" );
-
- assoc = new TableColumnAssociation( column );
- assoc.bindAspect( EOAssociation.ValueAspect, displayGroup, "name" );
- assoc.setTable( table );
- assoc.establishConnection();
-
- column = new TableColumn();
- column.setHeaderValue( "Object" );
-
- assoc = new TableColumnAssociation( column );
- assoc.bindAspect( EOAssociation.ValueAspect, displayGroup, "objectString" );
- assoc.setTable( table );
- assoc.establishConnection();
-
- column = new TableColumn();
- column.setHeaderValue( "Info" );
-
- assoc = new TableColumnAssociation( column );
- assoc.bindAspect( EOAssociation.ValueAspect, displayGroup, "userInfoString" );
- assoc.setTable( table );
- assoc.establishConnection();
-
- initLayout();
- }
-
- protected void initLayout()
- {
- table.addMouseListener( this ); // listen for double-clicks
-
- JPanel panel = new JPanel();
- panel.setBorder( new EmptyBorder( new Insets( 10, 10, 10, 10 ) ) );
- panel.setLayout( new BorderLayout( 10, 10 ) );
-
- JScrollPane scrollPane = new JScrollPane( table );
- //scrollPane.setPreferredSize( new Dimension( 500, 250 ) );
- panel.add( scrollPane, BorderLayout.CENTER );
-
- JFrame window = new JFrame();
- window.setTitle( "Notification Inspector" );
- window.getContentPane().add( panel );
-
- //window.pack();
- window.setSize( 800, 400 );
- WindowUtilities.cascade( window );
-
- // size the columns.
+public class NotificationInspector implements MouseListener, ActionListener {
+ protected JTable table;
+ protected JPopupMenu popupMenu;
+ protected EODisplayGroup displayGroup;
+
+ // key command to copy contents to clipboard
+ static public final String COPY = "COPY";
+
+ protected static final String CLEAR_ALL = "Clear All";
+ protected static final String CLEAR_SELECTED = "Clear Selected";
+
+ /**
+ * Displays all notifications on the default notification center.
+ */
+ public NotificationInspector() {
+ this(NSNotificationCenter.defaultCenter());
+ }
+
+ /**
+ * Displays all notifications from the specified notification center.
+ */
+ public NotificationInspector(NSNotificationCenter aCenter) {
+ this(aCenter, null, null);
+ }
+
+ /**
+ * Displays notifications from the default notification center using the
+ * specified name and object filters.
+ */
+ public NotificationInspector(String notificationName, Object anObject) {
+ this(NSNotificationCenter.defaultCenter(), notificationName, anObject);
+ }
+
+ /**
+ * Displays notifications on the specified notification center using the
+ * specified name and object filters.
+ */
+ public NotificationInspector(NSNotificationCenter aCenter, String notificationName, Object anObject) {
+ // show stack traces
+ NSNotification.showStack = true;
+
+ // register for notifications
+ NSSelector handleNotification = new NSSelector("handleNotification", new Class[] { NSNotification.class });
+ aCenter.addObserver(this, handleNotification, notificationName, anObject);
+
+ table = new JTable();
+
+ popupMenu = new JPopupMenu();
+ JMenuItem menuItem = popupMenu.add(CLEAR_SELECTED);
+ menuItem.addActionListener(this);
+ menuItem = popupMenu.add(CLEAR_ALL);
+ menuItem.addActionListener(this);
+
+ displayGroup = new EODisplayGroup();
+
+ TableColumn column;
+ TableColumnAssociation assoc;
+
+ column = new TableColumn();
+ column.setHeaderValue("Time");
+ column.setCellRenderer(new FormattedCellRenderer(new SimpleDateFormat("hh:mm:ss:SS")));
+ column.setPreferredWidth(90);
+ column.setMaxWidth(90);
+
+ assoc = new TableColumnAssociation(column);
+ assoc.bindAspect(EOAssociation.ValueAspect, displayGroup, "time");
+ assoc.setTable(table);
+ assoc.establishConnection();
+
+ column = new TableColumn();
+ column.setHeaderValue("Type");
+
+ assoc = new TableColumnAssociation(column);
+ assoc.bindAspect(EOAssociation.ValueAspect, displayGroup, "name");
+ assoc.setTable(table);
+ assoc.establishConnection();
+
+ column = new TableColumn();
+ column.setHeaderValue("Object");
+
+ assoc = new TableColumnAssociation(column);
+ assoc.bindAspect(EOAssociation.ValueAspect, displayGroup, "objectString");
+ assoc.setTable(table);
+ assoc.establishConnection();
+
+ column = new TableColumn();
+ column.setHeaderValue("Info");
+
+ assoc = new TableColumnAssociation(column);
+ assoc.bindAspect(EOAssociation.ValueAspect, displayGroup, "userInfoString");
+ assoc.setTable(table);
+ assoc.establishConnection();
+
+ initLayout();
+ }
+
+ protected void initLayout() {
+ table.addMouseListener(this); // listen for double-clicks
+
+ JPanel panel = new JPanel();
+ panel.setBorder(new EmptyBorder(new Insets(10, 10, 10, 10)));
+ panel.setLayout(new BorderLayout(10, 10));
+
+ JScrollPane scrollPane = new JScrollPane(table);
+ // scrollPane.setPreferredSize( new Dimension( 500, 250 ) );
+ panel.add(scrollPane, BorderLayout.CENTER);
+
+ JFrame window = new JFrame();
+ window.setTitle("Notification Inspector");
+ window.getContentPane().add(panel);
+
+ // window.pack();
+ window.setSize(800, 400);
+ WindowUtilities.cascade(window);
+
+ // size the columns.
// table.getColumnModel().getColumn( 0 ).setPreferredWidth( 100 );
- window.show();
- }
-
-/**
-* Handles the notification.
-*/
- public void handleNotification( NSNotification aNotification )
- {
- Surrogate s = new Surrogate( new Object[] { aNotification } );
- s.directPut( "time", new Date() );
- s.directPut( "objectString", ""+aNotification.object() ); // snapshot of state
- s.directPut( "userInfoString", ""+aNotification.userInfo() ); // snapshot of info
- displayGroup.insertObjectAtIndex( s, 0 );
- }
-
- // interface ActionListener
-
- /**
- * Method used to listen for the action event from the popup menu items.
- */
- public void actionPerformed( ActionEvent e )
- {
- if ( CLEAR_SELECTED.equals( e.getActionCommand() ) )
- {
- NSMutableArray objects = new NSMutableArray( displayGroup.allObjects() );
- objects.removeAll( displayGroup.selectedObjects() );
- displayGroup.setObjectArray( objects );
- displayGroup.updateDisplayedObjects();
- }
- else if ( CLEAR_ALL.equals( e.getActionCommand() ) )
- {
- displayGroup.setObjectArray( null );
- displayGroup.updateDisplayedObjects();
- }
- }
-
- // interface MouseListener
-
- /**
- * Double click to launch object inspector.
- */
-
- public void mouseClicked(MouseEvent e)
- {
- if ( e.getSource() == table )
- {
- if ( e.getClickCount() > 1 )
- {
- int row = table.rowAtPoint( e.getPoint() );
- int col = table.columnAtPoint( e.getPoint() );
- col = table.convertColumnIndexToModel( col );
-
- if ( row == -1 ) return;
-
- if ( col == 0 ) // time
- {
- new StackTraceInspector( ( (NSNotification) ( (Surrogate)
- displayGroup.displayedObjects().objectAtIndex( row ) ).getDelegate() ).stackTrace() );
- }
- else
- if ( col == 2 ) // object
- {
- new ObjectInspector( ( (NSNotification) ( (Surrogate)
- displayGroup.displayedObjects().objectAtIndex( row ) ).getDelegate() ).object() );
- }
- else
- if ( col == 3 ) // info
- {
- new ObjectInspector( ( (NSNotification) ( (Surrogate)
- displayGroup.displayedObjects().objectAtIndex( row ) ).getDelegate() ).userInfo() );
- }
- else
- {
- new ObjectInspector( ( (Surrogate)
- displayGroup.displayedObjects().objectAtIndex( row ) ).getDelegate() );
- }
- }
- else
- {
- // Click count is 1 then, check for popup trigger.
- if ( e.isPopupTrigger() )
- {
- popupMenu.show( table, e.getX(), e.getY() );
- }
- }
- }
- }
-
- public void mouseReleased(MouseEvent e)
- {
- if ( e.getSource() == table )
- {
- if ( e.isPopupTrigger() && ( e.getClickCount() == 1 ) )
- {
- popupMenu.show( table, e.getX(), e.getY() );
- }
- }
- }
-
- public void mousePressed(MouseEvent e)
- {
- if ( e.getSource() == table )
- {
- if ( e.isPopupTrigger() && ( e.getClickCount() == 1 ) )
- {
- popupMenu.show( table, e.getX(), e.getY() );
- }
- }
- }
-
- public void mouseEntered(MouseEvent e) {}
- public void mouseExited(MouseEvent e) {}
+ window.show();
+ }
+
+ /**
+ * Handles the notification.
+ */
+ public void handleNotification(NSNotification aNotification) {
+ Surrogate s = new Surrogate(new Object[] { aNotification });
+ s.directPut("time", new Date());
+ s.directPut("objectString", "" + aNotification.object()); // snapshot of state
+ s.directPut("userInfoString", "" + aNotification.userInfo()); // snapshot of info
+ displayGroup.insertObjectAtIndex(s, 0);
+ }
+
+ // interface ActionListener
+
+ /**
+ * Method used to listen for the action event from the popup menu items.
+ */
+ public void actionPerformed(ActionEvent e) {
+ if (CLEAR_SELECTED.equals(e.getActionCommand())) {
+ NSMutableArray objects = new NSMutableArray(displayGroup.allObjects());
+ objects.removeAll(displayGroup.selectedObjects());
+ displayGroup.setObjectArray(objects);
+ displayGroup.updateDisplayedObjects();
+ } else if (CLEAR_ALL.equals(e.getActionCommand())) {
+ displayGroup.setObjectArray(null);
+ displayGroup.updateDisplayedObjects();
+ }
+ }
+
+ // interface MouseListener
+
+ /**
+ * Double click to launch object inspector.
+ */
+
+ public void mouseClicked(MouseEvent e) {
+ if (e.getSource() == table) {
+ if (e.getClickCount() > 1) {
+ int row = table.rowAtPoint(e.getPoint());
+ int col = table.columnAtPoint(e.getPoint());
+ col = table.convertColumnIndexToModel(col);
+
+ if (row == -1)
+ return;
+
+ if (col == 0) // time
+ {
+ new StackTraceInspector(
+ ((NSNotification) ((Surrogate) displayGroup.displayedObjects().objectAtIndex(row))
+ .getDelegate()).stackTrace());
+ } else if (col == 2) // object
+ {
+ new ObjectInspector(
+ ((NSNotification) ((Surrogate) displayGroup.displayedObjects().objectAtIndex(row))
+ .getDelegate()).object());
+ } else if (col == 3) // info
+ {
+ new ObjectInspector(
+ ((NSNotification) ((Surrogate) displayGroup.displayedObjects().objectAtIndex(row))
+ .getDelegate()).userInfo());
+ } else {
+ new ObjectInspector(((Surrogate) displayGroup.displayedObjects().objectAtIndex(row)).getDelegate());
+ }
+ } else {
+ // Click count is 1 then, check for popup trigger.
+ if (e.isPopupTrigger()) {
+ popupMenu.show(table, e.getX(), e.getY());
+ }
+ }
+ }
+ }
+
+ public void mouseReleased(MouseEvent e) {
+ if (e.getSource() == table) {
+ if (e.isPopupTrigger() && (e.getClickCount() == 1)) {
+ popupMenu.show(table, e.getX(), e.getY());
+ }
+ }
+ }
+
+ public void mousePressed(MouseEvent e) {
+ if (e.getSource() == table) {
+ if (e.isPopupTrigger() && (e.getClickCount() == 1)) {
+ popupMenu.show(table, e.getX(), e.getY());
+ }
+ }
+ }
+
+ public void mouseEntered(MouseEvent e) {
+ }
+
+ public void mouseExited(MouseEvent e) {
+ }
}
/*
- * $Log$
- * Revision 1.2 2006/02/18 23:19:05 cgruber
- * Update imports and maven dependencies.
+ * $Log$ Revision 1.2 2006/02/18 23:19:05 cgruber Update imports and maven
+ * dependencies.
*
- * Revision 1.1 2006/02/16 13:22:23 cgruber
- * Check in all sources in eclipse-friendly maven-enabled packages.
+ * Revision 1.1 2006/02/16 13:22:23 cgruber Check in all sources in
+ * eclipse-friendly maven-enabled packages.
*
- * Revision 1.6 2003/08/06 23:07:52 chochos
- * general code cleanup (mostly, removing unused imports)
+ * Revision 1.6 2003/08/06 23:07:52 chochos general code cleanup (mostly,
+ * removing unused imports)
*
- * Revision 1.5 2002/11/18 22:11:51 mpowers
- * rglista's long-overdue enhancements: can now clear the display!
+ * Revision 1.5 2002/11/18 22:11:51 mpowers rglista's long-overdue enhancements:
+ * can now clear the display!
*
- * Revision 1.4 2002/10/24 18:19:03 mpowers
- * Now telling NSNotification to generate stack traces.
+ * Revision 1.4 2002/10/24 18:19:03 mpowers Now telling NSNotification to
+ * generate stack traces.
*
- * Revision 1.3 2001/04/29 22:02:45 mpowers
- * Work on id transposing between editing contexts.
+ * Revision 1.3 2001/04/29 22:02:45 mpowers Work on id transposing between
+ * editing contexts.
*
- * Revision 1.2 2001/04/09 21:40:25 mpowers
- * Numerous usability enhancements.
+ * Revision 1.2 2001/04/09 21:40:25 mpowers Numerous usability enhancements.
*
- * Revision 1.1 2001/04/08 20:58:45 mpowers
- * Contributing notification inspector.
+ * Revision 1.1 2001/04/08 20:58:45 mpowers Contributing notification inspector.
*
*
*/
-
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/RadioPanelAssociation.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/RadioPanelAssociation.java
index d2e51ed..b287691 100644
--- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/RadioPanelAssociation.java
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/RadioPanelAssociation.java
@@ -28,430 +28,343 @@ import net.wotonomy.ui.EODisplayGroup;
import net.wotonomy.ui.swing.components.RadioButtonPanel;
/**
-* RadioPanelAssociation binds RadioButtonPanels to
-* display groups. It works exactly like a
-* ComboBoxAssociation. Bindings are:
-* <ul>
-*
-* <li>value: a property of the selected object in the
-* display group that will be bind to the item the user
-* selects or the text that the user enters in the field.</li>
-*
-* <li>titles: a property of the objects in the bound
-* display group that will appear in the list. If the
-* objects aspect is not bound, this property is also
-* used to populate the value binding.</li>
-*
-* <li>objects: optional - if specified, when the user
-* selects an title in the list, the property of the
-* object at the corresponding index of the bound display
-* group will be used to populate the value binding.</li>
-*
-* <li>enabled: a boolean property of the selected object in the
-* display group that determines whether
-* the user can edit the field.</li>
-*
-* </ul>
-*
-* @author michael@mpowers.net
-* @author $Author: cgruber $
-* @version $Revision: 904 $
-*/
-public class RadioPanelAssociation extends EOAssociation
- implements ActionListener
-{
- static final NSArray aspects =
- new NSArray( new Object[] {
- TitlesAspect, ValueAspect,
- ObjectsAspect, EnabledAspect
- } );
- static final NSArray aspectSignatures =
- new NSArray( new Object[] {
- AttributeToOneAspectSignature,
- AttributeToOneAspectSignature,
- AttributeToOneAspectSignature,
- AttributeToOneAspectSignature
- } );
- static final NSArray objectKeysTaken =
- new NSArray( new Object[] {
- "text"
- } );
-
- /**
- * Constructor specifying the object to be controlled by this
- * association. Does not establish connection.
- */
- public RadioPanelAssociation ( 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. <ul>
- * <li>"A" attribute: the aspect can be bound to
- * an attribute.</li>
- * <li>"1" to-one: the aspect can be bound to a
- * property that returns a single object.</li>
- * <li>"M" to-one: the aspect can be bound to a
- * property that returns multiple objects.</li>
- * </ul>
- * 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 ( anObject instanceof RadioButtonPanel );
- }
-
- /**
- * 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. This is typically "value" or somesuch.
- */
- public static String primaryAspect ()
- {
- return ValueAspect;
- }
-
- /**
- * 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 ()
- {
+ * RadioPanelAssociation binds RadioButtonPanels to display groups. It works
+ * exactly like a ComboBoxAssociation. Bindings are:
+ * <ul>
+ *
+ * <li>value: a property of the selected object in the display group that will
+ * be bind to the item the user selects or the text that the user enters in the
+ * field.</li>
+ *
+ * <li>titles: a property of the objects in the bound display group that will
+ * appear in the list. If the objects aspect is not bound, this property is also
+ * used to populate the value binding.</li>
+ *
+ * <li>objects: optional - if specified, when the user selects an title in the
+ * list, the property of the object at the corresponding index of the bound
+ * display group will be used to populate the value binding.</li>
+ *
+ * <li>enabled: a boolean property of the selected object in the display group
+ * that determines whether the user can edit the field.</li>
+ *
+ * </ul>
+ *
+ * @author michael@mpowers.net
+ * @author $Author: cgruber $
+ * @version $Revision: 904 $
+ */
+public class RadioPanelAssociation extends EOAssociation implements ActionListener {
+ static final NSArray aspects = new NSArray(
+ new Object[] { TitlesAspect, ValueAspect, ObjectsAspect, EnabledAspect });
+ static final NSArray aspectSignatures = new NSArray(new Object[] { AttributeToOneAspectSignature,
+ AttributeToOneAspectSignature, AttributeToOneAspectSignature, AttributeToOneAspectSignature });
+ static final NSArray objectKeysTaken = new NSArray(new Object[] { "text" });
+
+ /**
+ * Constructor specifying the object to be controlled by this association. Does
+ * not establish connection.
+ */
+ public RadioPanelAssociation(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.
+ * <ul>
+ * <li>"A" attribute: the aspect can be bound to an attribute.</li>
+ * <li>"1" to-one: the aspect can be bound to a property that returns a single
+ * object.</li>
+ * <li>"M" to-one: the aspect can be bound to a property that returns multiple
+ * objects.</li>
+ * </ul>
+ * 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 (anObject instanceof RadioButtonPanel);
+ }
+
+ /**
+ * 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. This is typically
+ * "value" or somesuch.
+ */
+ public static String primaryAspect() {
+ return ValueAspect;
+ }
+
+ /**
+ * 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() {
super.establishConnection();
// prepopulate titles
- EODisplayGroup displayGroup =
- displayGroupForAspect( TitlesAspect );
- if ( displayGroup != null )
- {
- String key = displayGroupKeyForAspect( TitlesAspect );
- populateTitles( displayGroup, key );
+ EODisplayGroup displayGroup = displayGroupForAspect(TitlesAspect);
+ if (displayGroup != null) {
+ String key = displayGroupKeyForAspect(TitlesAspect);
+ populateTitles(displayGroup, key);
}
- populateValue();
- addAsListener();
- }
-
- protected void addAsListener()
- {
- component().addActionListener( this );
+ populateValue();
+ addAsListener();
+ }
+
+ protected void addAsListener() {
+ component().addActionListener(this);
}
-
- /**
- * Breaks the connection between this association and
- * its object. Override to stop listening for events
- * from the object.
- */
- public void breakConnection ()
- {
+
+ /**
+ * Breaks the connection between this association and its object. Override to
+ * stop listening for events from the object.
+ */
+ public void breakConnection() {
removeAsListener();
- super.breakConnection();
- }
+ super.breakConnection();
+ }
- protected void removeAsListener()
- {
- component().removeActionListener( this );
+ protected void removeAsListener() {
+ component().removeActionListener(this);
}
-
- /**
- * Called when either the selection or the contents
- * of an associated display group have changed.
- */
- public void subjectChanged ()
- {
+
+ /**
+ * Called when either the selection or the contents of an associated display
+ * group have changed.
+ */
+ public void subjectChanged() {
removeAsListener();
-
+
RadioButtonPanel component = component();
EODisplayGroup displayGroup;
String key;
-
+
// titles aspect
- displayGroup = displayGroupForAspect( TitlesAspect );
- if ( displayGroup != null )
- {
+ displayGroup = displayGroupForAspect(TitlesAspect);
+ if (displayGroup != null) {
// if backing group has changed
- if ( displayGroup.contentsChanged() )
- {
- key = displayGroupKeyForAspect( TitlesAspect );
- populateTitles( displayGroup, key );
+ if (displayGroup.contentsChanged()) {
+ key = displayGroupKeyForAspect(TitlesAspect);
+ populateTitles(displayGroup, key);
}
}
- // value aspect
- populateValue();
-
+ // value aspect
+ populateValue();
+
// enabled aspect
- displayGroup = displayGroupForAspect( EnabledAspect );
- if ( displayGroup != null )
- {
- key = displayGroupKeyForAspect( EnabledAspect );
- 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.isEnabled() )
- {
- component.setEnabled( converted.booleanValue() );
- }
+ displayGroup = displayGroupForAspect(EnabledAspect);
+ if (displayGroup != null) {
+ key = displayGroupKeyForAspect(EnabledAspect);
+ 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.isEnabled()) {
+ component.setEnabled(converted.booleanValue());
+ }
}
-
+
addAsListener();
- }
-
+ }
+
/**
- * Called to repopulate the title list from the
- * specified display group.
- */
- protected void populateTitles(
- EODisplayGroup displayGroup, String key )
- {
+ * Called to repopulate the title list from the specified display group.
+ */
+ protected void populateTitles(EODisplayGroup displayGroup, String key) {
Object value;
int count = displayGroup.displayedObjects().count();
- String[] titles = new String[ count ];
- for ( int i = 0; i < count; i++ )
- {
- value = displayGroup.valueForObjectAtIndex( i, key );
- if ( value != null )
- {
+ String[] titles = new String[count];
+ for (int i = 0; i < count; i++) {
+ value = displayGroup.valueForObjectAtIndex(i, key);
+ if (value != null) {
titles[i] = value.toString();
- }
- else
- {
- titles[i] = "";
+ } else {
+ titles[i] = "";
}
}
- component().setLabels( titles );
+ component().setLabels(titles);
}
-
+
/**
- * Called to populate the value from the display group.
- */
- protected void populateValue()
- {
+ * Called to populate the value from the display group.
+ */
+ protected void populateValue() {
RadioButtonPanel component = component();
EODisplayGroup displayGroup;
String key;
// value aspect
- displayGroup = displayGroupForAspect( ValueAspect );
- if ( displayGroup != null )
- {
- key = displayGroupKeyForAspect( ValueAspect );
- component.setEnabled(
- displayGroup.enabledToSetSelectedObjectValueForKey( key ) );
-
- Object value = displayGroup.selectedObjectValueForKey( key );
+ displayGroup = displayGroupForAspect(ValueAspect);
+ if (displayGroup != null) {
+ key = displayGroupKeyForAspect(ValueAspect);
+ component.setEnabled(displayGroup.enabledToSetSelectedObjectValueForKey(key));
+
+ Object value = displayGroup.selectedObjectValueForKey(key);
// objects aspect
- EODisplayGroup objectsDisplayGroup =
- displayGroupForAspect( ObjectsAspect );
- if ( ( objectsDisplayGroup != null ) && ( value != null ) )
- {
- String objectKey = displayGroupKeyForAspect( ObjectsAspect );
+ EODisplayGroup objectsDisplayGroup = displayGroupForAspect(ObjectsAspect);
+ if ((objectsDisplayGroup != null) && (value != null)) {
+ String objectKey = displayGroupKeyForAspect(ObjectsAspect);
Object match;
int index = NSArray.NotFound;
int count = objectsDisplayGroup.displayedObjects().count();
- for ( int i = 0; i < count; i++ )
- {
- match = objectsDisplayGroup.valueForObjectAtIndex( i, objectKey );
- if ( value.equals( match ) )
- {
+ for (int i = 0; i < count; i++) {
+ match = objectsDisplayGroup.valueForObjectAtIndex(i, objectKey);
+ if (value.equals(match)) {
index = i;
}
}
- if ( index == NSArray.NotFound )
- {
- if ( component.getValue() != null )
- {
- component.setValue( null );
+ if (index == NSArray.NotFound) {
+ if (component.getValue() != null) {
+ component.setValue(null);
}
- }
- else
- {
+ } else {
String[] titles = component().getLabels();
- component.setValue( titles[ index ] );
+ component.setValue(titles[index]);
}
- }
- else
- {
- if ( value != null ) value = value.toString();
- component.setValue( (String) value );
+ } else {
+ if (value != null)
+ value = value.toString();
+ component.setValue((String) value);
}
}
}
-
- /**
- * Forces this association to cause the object to
- * stop editing and validate the user's input.
- * @return false if there were problems validating,
- * or true to continue.
- */
- public boolean endEditing ()
- {
+
+ /**
+ * Forces this association to cause the object to stop editing and validate the
+ * user's input.
+ *
+ * @return false if there were problems validating, or true to continue.
+ */
+ public boolean endEditing() {
return writeValueToDisplayGroup();
- }
-
+ }
+
/**
- * Writes the value currently in the component
- * to the selected object in the display group
- * bound to the value aspect.
- * @return false if there were problems validating,
- * or true to continue.
- */
- protected boolean writeValueToDisplayGroup()
- {
+ * Writes the value currently in the component to the selected object in the
+ * display group bound to the value aspect.
+ *
+ * @return false if there were problems validating, or true to continue.
+ */
+ protected boolean writeValueToDisplayGroup() {
RadioButtonPanel component = component();
EODisplayGroup displayGroup;
String key;
-
+
// selected title aspect
- displayGroup = displayGroupForAspect( ValueAspect );
- if ( displayGroup != null )
- {
- key = displayGroupKeyForAspect( ValueAspect );
+ displayGroup = displayGroupForAspect(ValueAspect);
+ if (displayGroup != null) {
+ key = displayGroupKeyForAspect(ValueAspect);
Object value = null;
// selected object aspect, if any
- EODisplayGroup objectsGroup =
- displayGroupForAspect( ObjectsAspect );
- if ( objectsGroup != null )
- {
- String objectKey = displayGroupKeyForAspect( ObjectsAspect );
- String selectedValue = component.getValue();
- if ( selectedValue != null )
- {
+ EODisplayGroup objectsGroup = displayGroupForAspect(ObjectsAspect);
+ if (objectsGroup != null) {
+ String objectKey = displayGroupKeyForAspect(ObjectsAspect);
+ String selectedValue = component.getValue();
+ if (selectedValue != null) {
String[] titles = component.getLabels();
int index = -1;
- for ( int i = 0; i < titles.length; i++ )
- {
- if ( selectedValue.equals( titles[i] ) )
- {
- index = i;
+ for (int i = 0; i < titles.length; i++) {
+ if (selectedValue.equals(titles[i])) {
+ index = i;
}
}
- if ( index != -1 )
- {
- value = objectsGroup
- .valueForObjectAtIndex( index, objectKey );
+ if (index != -1) {
+ value = objectsGroup.valueForObjectAtIndex(index, objectKey);
}
}
- }
- else // just use the selected item
+ } else // just use the selected item
{
- value = component.getValue();
+ value = component.getValue();
}
- return displayGroup.setSelectedObjectValue( value, key );
+ return displayGroup.setSelectedObjectValue(value, key);
}
-
+
return false;
}
-
- // interface ActionListener
-
+
+ // interface ActionListener
+
/**
- * Updates object on action performed.
- */
- public void actionPerformed( ActionEvent evt )
- {
+ * Updates object on action performed.
+ */
+ public void actionPerformed(ActionEvent evt) {
writeValueToDisplayGroup();
}
-
+
// convenience
- private RadioButtonPanel component()
- {
+ private RadioButtonPanel component() {
return (RadioButtonPanel) object();
}
}
/*
- * $Log$
- * Revision 1.2 2006/02/18 23:19:05 cgruber
- * Update imports and maven dependencies.
+ * $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.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.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.3 2003/08/06 23:07:52 chochos general code cleanup (mostly,
+ * removing unused imports)
*
- * Revision 1.2 2001/02/16 17:48:07 mpowers
- * Populating titles or data not longer marks target object as changed.
+ * Revision 1.2 2001/02/16 17:48:07 mpowers Populating titles or data not longer
+ * marks target object as changed.
*
- * Revision 1.1.1.1 2000/12/21 15:48:52 mpowers
- * Contributing wotonomy.
+ * Revision 1.1.1.1 2000/12/21 15:48:52 mpowers Contributing wotonomy.
*
- * Revision 1.3 2000/12/20 16:25:41 michael
- * Added log to all files.
+ * Revision 1.3 2000/12/20 16:25:41 michael Added log to all files.
*
*
*/
-
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/ReferenceInspector.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/ReferenceInspector.java
index 7194f23..afa5909 100644
--- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/ReferenceInspector.java
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/ReferenceInspector.java
@@ -43,241 +43,220 @@ import net.wotonomy.ui.swing.components.ButtonPanel;
import net.wotonomy.ui.swing.util.WindowUtilities;
/**
-* ReferenceInspector tracks objects until they are garbage collected.
-* Use it to track objects that you suspect are not being GCed.
-* ReferenceInspector retains only weak references to the objects that
-* it tracks, so when those weak references cannot be resolved, the
-* object has been garbage collected. Note that under some GC
-* implementations, adding a weak reference to an object will delay
-* garbage collection for that object.
-*
-* @author michael@mpowers.net
-* @version $Revision: 904 $
-*/
+ * ReferenceInspector tracks objects until they are garbage collected. Use it to
+ * track objects that you suspect are not being GCed. ReferenceInspector retains
+ * only weak references to the objects that it tracks, so when those weak
+ * references cannot be resolved, the object has been garbage collected. Note
+ * that under some GC implementations, adding a weak reference to an object will
+ * delay garbage collection for that object.
+ *
+ * @author michael@mpowers.net
+ * @version $Revision: 904 $
+ */
-public class ReferenceInspector
- implements MouseListener, ActionListener
-{
- protected JTable table;
- protected JLabel memoryLabel;
-
- static protected EODisplayGroup displayGroup;
- static protected JFrame window;
-
- /* Reference queue for cleared WeakKeys */
- private static ReferenceQueue queue = new ReferenceQueue();
-
- // key command to copy contents to clipboard
- static public final String COPY = "COPY";
+public class ReferenceInspector implements MouseListener, ActionListener {
+ protected JTable table;
+ protected JLabel memoryLabel;
-/**
-* Launches a new ReferenceInspector if one does not already exist.
-*/
- public ReferenceInspector()
- {
- if ( window == null )
- {
- table = new JTable();
- memoryLabel = new JLabel();
-
- displayGroup = new EODisplayGroup();
-
- TableColumn column;
- TableColumnAssociation assoc;
-
- column = new TableColumn();
- column.setHeaderValue( "Object" );
-
- assoc = new TableColumnAssociation( column );
- assoc.bindAspect( EOAssociation.ValueAspect, displayGroup, "" );
- assoc.setTable( table );
- assoc.establishConnection();
-
- column = new TableColumn();
- column.setHeaderValue( "Address" );
- column.setMaxWidth( 100 );
-
- assoc = new TableColumnAssociation( column );
- assoc.bindAspect( EOAssociation.ValueAspect, displayGroup, "identityHashCode" );
- assoc.setTable( table );
- assoc.establishConnection();
-
- initLayout();
- }
- window.show();
- }
+ static protected EODisplayGroup displayGroup;
+ static protected JFrame window;
-/**
-* Adds the specified object to the ReferenceInspector, launching
-* a new ReferenceInspector if one does not already exist.
-*/
- public ReferenceInspector( Object anObject )
- {
- this();
- displayGroup.insertObjectAtIndex( new ExtendedWeakReference( anObject, queue ), 0 );
- window.show();
- }
-
- protected void initLayout()
- {
- table.addMouseListener( this ); // listen for double-clicks
-
- JPanel panel = new JPanel();
- panel.setBorder( new EmptyBorder( new Insets( 10, 10, 10, 10 ) ) );
- panel.setLayout( new BorderLayout( 10, 10 ) );
-
- JScrollPane scrollPane = new JScrollPane( table );
- scrollPane.setPreferredSize( new Dimension( 500, 250 ) );
- panel.add( scrollPane, BorderLayout.CENTER );
-
- ButtonPanel buttonPanel = new ButtonPanel( new String[] { "Update" } );
- buttonPanel.addActionListener( this );
-
- JPanel bottomPanel = new JPanel();
- bottomPanel.setLayout( new BorderLayout() );
- bottomPanel.add( buttonPanel, BorderLayout.EAST );
- bottomPanel.add( memoryLabel, BorderLayout.CENTER );
- panel.add( bottomPanel, BorderLayout.SOUTH );
-
- window = new JFrame();
- window.setTitle( "Reference Inspector" );
- window.getContentPane().add( panel );
+ /* Reference queue for cleared WeakKeys */
+ private static ReferenceQueue queue = new ReferenceQueue();
+
+ // key command to copy contents to clipboard
+ static public final String COPY = "COPY";
+
+ /**
+ * Launches a new ReferenceInspector if one does not already exist.
+ */
+ public ReferenceInspector() {
+ if (window == null) {
+ table = new JTable();
+ memoryLabel = new JLabel();
+
+ displayGroup = new EODisplayGroup();
+
+ TableColumn column;
+ TableColumnAssociation assoc;
+
+ column = new TableColumn();
+ column.setHeaderValue("Object");
+
+ assoc = new TableColumnAssociation(column);
+ assoc.bindAspect(EOAssociation.ValueAspect, displayGroup, "");
+ assoc.setTable(table);
+ assoc.establishConnection();
+
+ column = new TableColumn();
+ column.setHeaderValue("Address");
+ column.setMaxWidth(100);
+
+ assoc = new TableColumnAssociation(column);
+ assoc.bindAspect(EOAssociation.ValueAspect, displayGroup, "identityHashCode");
+ assoc.setTable(table);
+ assoc.establishConnection();
+
+ initLayout();
+ }
+ window.show();
+ }
+
+ /**
+ * Adds the specified object to the ReferenceInspector, launching a new
+ * ReferenceInspector if one does not already exist.
+ */
+ public ReferenceInspector(Object anObject) {
+ this();
+ displayGroup.insertObjectAtIndex(new ExtendedWeakReference(anObject, queue), 0);
+ window.show();
+ }
+
+ protected void initLayout() {
+ table.addMouseListener(this); // listen for double-clicks
+
+ JPanel panel = new JPanel();
+ panel.setBorder(new EmptyBorder(new Insets(10, 10, 10, 10)));
+ panel.setLayout(new BorderLayout(10, 10));
+
+ JScrollPane scrollPane = new JScrollPane(table);
+ scrollPane.setPreferredSize(new Dimension(500, 250));
+ panel.add(scrollPane, BorderLayout.CENTER);
+
+ ButtonPanel buttonPanel = new ButtonPanel(new String[] { "Update" });
+ buttonPanel.addActionListener(this);
+
+ JPanel bottomPanel = new JPanel();
+ bottomPanel.setLayout(new BorderLayout());
+ bottomPanel.add(buttonPanel, BorderLayout.EAST);
+ bottomPanel.add(memoryLabel, BorderLayout.CENTER);
+ panel.add(bottomPanel, BorderLayout.SOUTH);
+
+ window = new JFrame();
+ window.setTitle("Reference Inspector");
+ window.getContentPane().add(panel);
// javax.swing.Timer timer = new javax.swing.Timer( 10000, this );
// timer.restart();
-
- window.pack();
- WindowUtilities.cascade( window );
- window.show();
- }
-
- /* Remove all invalidated entries from the map, that is, remove all entries
- whose keys have been discarded. This method should be invoked once by
- each public mutator in this class. We don't invoke this method in
- public accessors because that can lead to surprising
- ConcurrentModificationExceptions. */
- private static void processQueue()
- {
+
+ window.pack();
+ WindowUtilities.cascade(window);
+ window.show();
+ }
+
+ /*
+ * Remove all invalidated entries from the map, that is, remove all entries
+ * whose keys have been discarded. This method should be invoked once by each
+ * public mutator in this class. We don't invoke this method in public accessors
+ * because that can lead to surprising ConcurrentModificationExceptions.
+ */
+ private static void processQueue() {
// System.out.println( "ReferenceInspector.processQueue:");
- synchronized ( displayGroup )
- {
- int idx;
- WeakReference rk;
- while ((rk = (WeakReference)queue.poll()) != null)
- {
+ synchronized (displayGroup) {
+ int idx;
+ WeakReference rk;
+ while ((rk = (WeakReference) queue.poll()) != null) {
// System.out.println( "ReferenceInspector.processQueue: removing object: " + rk );
- if ( rk != null )
- {
- idx = displayGroup.displayedObjects().indexOfIdenticalObject( rk );
- if ( idx != NSArray.NotFound )
- {
- displayGroup.deleteObjectAtIndex( idx );
- }
- }
- }
- displayGroup.updateDisplayedObjects();
- }
- }
-
- // interface ActionListener
-
- public void actionPerformed( ActionEvent evt )
- {
- Runtime runtime = Runtime.getRuntime();
- runtime.gc();
- processQueue();
-
- long totalMemory = runtime.totalMemory() / 1024;
- long freeMemory = runtime.freeMemory() / 1024;
- memoryLabel.setText(
- Long.toString( totalMemory - freeMemory ) + "K / " +
- Long.toString( totalMemory ) + "K" );
- }
-
- // interface MouseListener
-
- /**
- * Double click to launch object inspector.
- */
-
- public void mouseClicked(MouseEvent e)
- {
- if ( e.getSource() == table )
- {
- if ( e.getClickCount() > 1 )
- {
- int row = table.rowAtPoint( e.getPoint() );
- int col = table.columnAtPoint( e.getPoint() );
- col = table.convertColumnIndexToModel( col );
-
- if ( row == -1 ) return;
-
- if ( col == 0 ) // time
- {
- }
- else
- {
- }
- }
- }
- }
-
- public void mouseReleased(MouseEvent e) {}
- public void mousePressed(MouseEvent e) {}
- public void mouseEntered(MouseEvent e) {}
- public void mouseExited(MouseEvent e) {}
-
- public class ExtendedWeakReference extends WeakReference
- {
- public ExtendedWeakReference(Object referent, ReferenceQueue q)
- {
- super( referent, q );
- }
-
- public String toString()
- {
- if ( get() != null )
- {
- return get().toString();
- }
- return null;
- }
-
- public String identityHashCode()
- {
- if ( get() != null )
- {
- return Integer.toHexString( System.identityHashCode( get() ) );
- }
- return null;
- }
-
- }
+ if (rk != null) {
+ idx = displayGroup.displayedObjects().indexOfIdenticalObject(rk);
+ if (idx != NSArray.NotFound) {
+ displayGroup.deleteObjectAtIndex(idx);
+ }
+ }
+ }
+ displayGroup.updateDisplayedObjects();
+ }
+ }
+
+ // interface ActionListener
+
+ public void actionPerformed(ActionEvent evt) {
+ Runtime runtime = Runtime.getRuntime();
+ runtime.gc();
+ processQueue();
+
+ long totalMemory = runtime.totalMemory() / 1024;
+ long freeMemory = runtime.freeMemory() / 1024;
+ memoryLabel.setText(Long.toString(totalMemory - freeMemory) + "K / " + Long.toString(totalMemory) + "K");
+ }
+
+ // interface MouseListener
+
+ /**
+ * Double click to launch object inspector.
+ */
+
+ public void mouseClicked(MouseEvent e) {
+ if (e.getSource() == table) {
+ if (e.getClickCount() > 1) {
+ int row = table.rowAtPoint(e.getPoint());
+ int col = table.columnAtPoint(e.getPoint());
+ col = table.convertColumnIndexToModel(col);
+
+ if (row == -1)
+ return;
+
+ if (col == 0) // time
+ {
+ } else {
+ }
+ }
+ }
+ }
+
+ public void mouseReleased(MouseEvent e) {
+ }
+
+ public void mousePressed(MouseEvent e) {
+ }
+
+ public void mouseEntered(MouseEvent e) {
+ }
+
+ public void mouseExited(MouseEvent e) {
+ }
+
+ public class ExtendedWeakReference extends WeakReference {
+ public ExtendedWeakReference(Object referent, ReferenceQueue q) {
+ super(referent, q);
+ }
+
+ public String toString() {
+ if (get() != null) {
+ return get().toString();
+ }
+ return null;
+ }
+
+ public String identityHashCode() {
+ if (get() != null) {
+ return Integer.toHexString(System.identityHashCode(get()));
+ }
+ return null;
+ }
+
+ }
}
-
+
/*
- * $Log$
- * Revision 1.2 2006/02/18 23:19:05 cgruber
- * Update imports and maven dependencies.
+ * $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.1 2006/02/16 13:22:22 cgruber Check in all sources in
+ * eclipse-friendly maven-enabled packages.
*
- * Revision 1.5 2003/08/06 23:07:52 chochos
- * general code cleanup (mostly, removing unused imports)
+ * Revision 1.5 2003/08/06 23:07:52 chochos general code cleanup (mostly,
+ * removing unused imports)
*
- * Revision 1.4 2002/03/22 22:39:59 mpowers
- * Now shows the window even if previously hidden.
+ * Revision 1.4 2002/03/22 22:39:59 mpowers Now shows the window even if
+ * previously hidden.
*
- * Revision 1.3 2001/10/02 14:22:39 mpowers
- * Now shows used and heap memory usage.
+ * Revision 1.3 2001/10/02 14:22:39 mpowers Now shows used and heap memory
+ * usage.
*
- * Revision 1.2 2001/07/10 16:39:32 mpowers
- * Removed printlns.
+ * Revision 1.2 2001/07/10 16:39:32 mpowers Removed printlns.
*
- * Revision 1.1 2001/07/10 16:32:50 mpowers
- * Adding the reference inspector.
+ * Revision 1.1 2001/07/10 16:32:50 mpowers Adding the reference inspector.
*
*
*/
-
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/SliderAssociation.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/SliderAssociation.java
index b836dcc..5a83240 100644
--- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/SliderAssociation.java
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/SliderAssociation.java
@@ -30,390 +30,309 @@ import net.wotonomy.ui.EOAssociation;
import net.wotonomy.ui.EODisplayGroup;
/**
-* SliderAssociation binds a JSlider component to
-* a display group. Bindings are:
-* <ul>
-* <li>value: a property convertable to/from a string</li>
-* <li>enabled: a boolean property that determines whether
-* the user can select the text in the field</li>
-* </ul>
-*
-* @author michael@mpowers.net
-* @author $Author: cgruber $
-* @version $Revision: 904 $
-*/
-public class SliderAssociation extends EOAssociation
- implements ChangeListener
-{
- static final NSArray aspects =
- new NSArray( new Object[] {
- ValueAspect, EnabledAspect, VisibleAspect
- } );
- static final NSArray aspectSignatures =
- new NSArray( new Object[] {
- AttributeToOneAspectSignature,
- AttributeToOneAspectSignature,
- } );
- static final NSArray objectKeysTaken =
- new NSArray( new Object[] {
- "value"
- } );
-
- /**
- * Constructor specifying the object to be controlled by this
- * association. Does not establish connection.
- */
- public SliderAssociation ( 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. <ul>
- * <li>"A" attribute: the aspect can be bound to
- * an attribute.</li>
- * <li>"1" to-one: the aspect can be bound to a
- * property that returns a single object.</li>
- * <li>"M" to-one: the aspect can be bound to a
- * property that returns multiple objects.</li>
- * </ul>
- * 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 ( anObject instanceof JSlider );
- }
-
- /**
- * 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. This is typically "value" or somesuch.
- */
- public static String primaryAspect ()
- {
- return ValueAspect;
- }
-
- /**
- * 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. This implementation
- * attempts to add this class as an ActionListener
- * and as a FocusListener to the specified object.
- */
- public void establishConnection ()
- {
- component().addChangeListener( this );
- super.establishConnection();
-
+ * SliderAssociation binds a JSlider component to a display group. Bindings are:
+ * <ul>
+ * <li>value: a property convertable to/from a string</li>
+ * <li>enabled: a boolean property that determines whether the user can select
+ * the text in the field</li>
+ * </ul>
+ *
+ * @author michael@mpowers.net
+ * @author $Author: cgruber $
+ * @version $Revision: 904 $
+ */
+public class SliderAssociation extends EOAssociation implements ChangeListener {
+ static final NSArray aspects = new NSArray(new Object[] { ValueAspect, EnabledAspect, VisibleAspect });
+ static final NSArray aspectSignatures = new NSArray(
+ new Object[] { AttributeToOneAspectSignature, AttributeToOneAspectSignature, });
+ static final NSArray objectKeysTaken = new NSArray(new Object[] { "value" });
+
+ /**
+ * Constructor specifying the object to be controlled by this association. Does
+ * not establish connection.
+ */
+ public SliderAssociation(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.
+ * <ul>
+ * <li>"A" attribute: the aspect can be bound to an attribute.</li>
+ * <li>"1" to-one: the aspect can be bound to a property that returns a single
+ * object.</li>
+ * <li>"M" to-one: the aspect can be bound to a property that returns multiple
+ * objects.</li>
+ * </ul>
+ * 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 (anObject instanceof JSlider);
+ }
+
+ /**
+ * 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. This is typically
+ * "value" or somesuch.
+ */
+ public static String primaryAspect() {
+ return ValueAspect;
+ }
+
+ /**
+ * 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.
+ * This implementation attempts to add this class as an ActionListener and as a
+ * FocusListener to the specified object.
+ */
+ public void establishConnection() {
+ component().addChangeListener(this);
+ super.establishConnection();
+
// forces update from bindings
subjectChanged();
- }
-
- /**
- * Breaks the connection between this association and
- * its object. Override to stop listening for events
- * from the object.
- */
- public void breakConnection ()
- {
- component().removeChangeListener( this );
- super.breakConnection();
- }
-
- /**
- * Called when either the selection or the contents
- * of an associated display group have changed.
- */
- public void subjectChanged ()
- {
+ }
+
+ /**
+ * Breaks the connection between this association and its object. Override to
+ * stop listening for events from the object.
+ */
+ public void breakConnection() {
+ component().removeChangeListener(this);
+ super.breakConnection();
+ }
+
+ /**
+ * Called when either the selection or the contents of an associated display
+ * group have changed.
+ */
+ public void subjectChanged() {
JSlider component = component();
EODisplayGroup displayGroup;
String key;
Object value;
-
+
// value aspect
- displayGroup = displayGroupForAspect( ValueAspect );
- if ( displayGroup != null )
- {
- key = displayGroupKeyForAspect( ValueAspect );
- component.setEnabled(
- displayGroup.enabledToSetSelectedObjectValueForKey( key ) );
-
- if ( displayGroup.selectedObjects().size() > 1 )
- {
- // if there're more than one object selected, set
- // the value to blank for all of them.
- Object previousValue;
-
- Iterator indexIterator = displayGroup.selectionIndexes().
- iterator();
-
- // get value for the first selected object.
- int initialIndex = ( (Integer)indexIterator.next() ).intValue();
- previousValue = displayGroup.valueForObjectAtIndex(
- initialIndex, key );
- value = null;
-
- // go through the rest of the selected objects, compare each
- // value with the previous one. continue comparing if two
- // values are equal, break the while loop if they're different.
- // the final value will be the common value of all selected objects
- // if there is one, or be blank if there is not.
- while ( indexIterator.hasNext() )
- {
- int index = ( (Integer)indexIterator.next() ).intValue();
- Object currentValue = displayGroup.valueForObjectAtIndex(
- index, key );
- if ( currentValue != null && !currentValue.equals( previousValue ) )
- {
- value = null;
- break;
- }
- else
- {
- // currentValue is the same as the previous one
- value = currentValue;
- }
-
- } // end while
-
- } else {
-
- value = displayGroup.selectedObjectValueForKey( key );
- } // end checking size of displayGroup
+ displayGroup = displayGroupForAspect(ValueAspect);
+ if (displayGroup != null) {
+ key = displayGroupKeyForAspect(ValueAspect);
+ component.setEnabled(displayGroup.enabledToSetSelectedObjectValueForKey(key));
+
+ if (displayGroup.selectedObjects().size() > 1) {
+ // if there're more than one object selected, set
+ // the value to blank for all of them.
+ Object previousValue;
+
+ Iterator indexIterator = displayGroup.selectionIndexes().iterator();
+
+ // get value for the first selected object.
+ int initialIndex = ((Integer) indexIterator.next()).intValue();
+ previousValue = displayGroup.valueForObjectAtIndex(initialIndex, key);
+ value = null;
+
+ // go through the rest of the selected objects, compare each
+ // value with the previous one. continue comparing if two
+ // values are equal, break the while loop if they're different.
+ // the final value will be the common value of all selected objects
+ // if there is one, or be blank if there is not.
+ while (indexIterator.hasNext()) {
+ int index = ((Integer) indexIterator.next()).intValue();
+ Object currentValue = displayGroup.valueForObjectAtIndex(index, key);
+ if (currentValue != null && !currentValue.equals(previousValue)) {
+ value = null;
+ break;
+ } else {
+ // currentValue is the same as the previous one
+ value = currentValue;
+ }
+
+ } // end while
+
+ } else {
+
+ value = displayGroup.selectedObjectValueForKey(key);
+ } // end checking size of displayGroup
// convert value to int
- value = ValueConverter.convertObjectToClass( value, Integer.class );
+ value = ValueConverter.convertObjectToClass(value, Integer.class);
int intValue;
- if ( value == null )
- {
+ if (value == null) {
intValue = 0;
+ } else {
+ intValue = ((Integer) value).intValue();
}
- else
- {
- intValue = ((Integer)value).intValue();
- }
-
- if ( component.getValue() != intValue )
- {
- component().removeChangeListener( this );
- component.setValue( intValue );
- component().addChangeListener( this );
+
+ if (component.getValue() != intValue) {
+ component().removeChangeListener(this);
+ component.setValue(intValue);
+ component().addChangeListener(this);
}
}
// enabled aspect
- displayGroup = displayGroupForAspect( EnabledAspect );
- key = displayGroupKeyForAspect( EnabledAspect );
- if ( ( displayGroup != null ) || ( key != null ) )
- {
- if ( displayGroup != null )
- {
- value =
- displayGroup.selectedObjectValueForKey( key );
- }
- else
- {
+ displayGroup = displayGroupForAspect(EnabledAspect);
+ key = displayGroupKeyForAspect(EnabledAspect);
+ if ((displayGroup != null) || (key != null)) {
+ if (displayGroup != null) {
+ value = displayGroup.selectedObjectValueForKey(key);
+ } else {
// treat bound key without display group as a value
- value = key;
+ value = key;
+ }
+ Boolean converted = null;
+ if (value != null) {
+ converted = (Boolean) ValueConverter.convertObjectToClass(value, Boolean.class);
+ }
+ if (converted == null)
+ converted = Boolean.FALSE;
+ if (converted.booleanValue() != component.isEnabled()) {
+ component.setEnabled(converted.booleanValue());
}
- Boolean converted = null;
- if ( value != null )
- {
- converted = (Boolean)
- ValueConverter.convertObjectToClass(
- value, Boolean.class );
- }
- if ( converted == null ) converted = Boolean.FALSE;
- if ( converted.booleanValue() != component.isEnabled() )
- {
- component.setEnabled( converted.booleanValue() );
- }
}
// visible aspect
- displayGroup = displayGroupForAspect( VisibleAspect );
- key = displayGroupKeyForAspect( VisibleAspect );
- if ( ( displayGroup != null ) || ( key != null ) )
- {
- if ( displayGroup != null )
- {
- value =
- displayGroup.selectedObjectValueForKey( key );
- }
- else
- {
+ displayGroup = displayGroupForAspect(VisibleAspect);
+ key = displayGroupKeyForAspect(VisibleAspect);
+ if ((displayGroup != null) || (key != null)) {
+ if (displayGroup != null) {
+ value = displayGroup.selectedObjectValueForKey(key);
+ } else {
// treat bound key without display group as a value
- value = key;
+ value = key;
}
- Boolean converted = (Boolean)
- ValueConverter.convertObjectToClass(
- value, Boolean.class );
-
- if ( converted != null )
- {
- if ( converted.booleanValue() != component.isVisible() )
- {
- component.setVisible( converted.booleanValue() );
+ Boolean converted = (Boolean) ValueConverter.convertObjectToClass(value, Boolean.class);
+
+ if (converted != null) {
+ if (converted.booleanValue() != component.isVisible()) {
+ component.setVisible(converted.booleanValue());
}
- }
+ }
}
- }
-
- /**
- * Forces this association to cause the object to
- * stop editing and validate the user's input.
- * @return false if there were problems validating,
- * or true to continue.
- */
- public boolean endEditing ()
- {
+ }
+
+ /**
+ * Forces this association to cause the object to stop editing and validate the
+ * user's input.
+ *
+ * @return false if there were problems validating, or true to continue.
+ */
+ public boolean endEditing() {
return writeValueToDisplayGroup();
- }
-
+ }
+
/**
- * Writes the value currently in the component
- * to the selected object in the display group
- * bound to the value aspect.
- * @return false if there were problems validating,
- * or true to continue.
- */
- protected boolean writeValueToDisplayGroup()
- {
- EODisplayGroup displayGroup =
- displayGroupForAspect( ValueAspect );
- if ( displayGroup != null )
- {
- String key = displayGroupKeyForAspect( ValueAspect );
- Object value = new Integer( component().getValue() );
-
- boolean returnValue = true;
- Iterator selectedIterator = displayGroup.selectionIndexes().iterator();
- while ( selectedIterator.hasNext() )
- {
- int index = ( (Integer)selectedIterator.next() ).intValue();
-
- if ( !displayGroup.setValueForObjectAtIndex( value, index, key ) )
- {
- returnValue = false;
- }
- }
- return returnValue;
+ * Writes the value currently in the component to the selected object in the
+ * display group bound to the value aspect.
+ *
+ * @return false if there were problems validating, or true to continue.
+ */
+ protected boolean writeValueToDisplayGroup() {
+ EODisplayGroup displayGroup = displayGroupForAspect(ValueAspect);
+ if (displayGroup != null) {
+ String key = displayGroupKeyForAspect(ValueAspect);
+ Object value = new Integer(component().getValue());
+
+ boolean returnValue = true;
+ Iterator selectedIterator = displayGroup.selectionIndexes().iterator();
+ while (selectedIterator.hasNext()) {
+ int index = ((Integer) selectedIterator.next()).intValue();
+
+ if (!displayGroup.setValueForObjectAtIndex(value, index, key)) {
+ returnValue = false;
+ }
+ }
+ return returnValue;
}
return false;
}
- // interface ChangeListener
-
+ // interface ChangeListener
+
/**
- * Updates object on change.
- */
- public void stateChanged(ChangeEvent e)
- {
+ * Updates object on change.
+ */
+ public void stateChanged(ChangeEvent e) {
writeValueToDisplayGroup();
}
-
- private JSlider component()
- {
- return (JSlider) object();
+
+ private JSlider component() {
+ return (JSlider) object();
}
}
/*
- * $Log$
- * Revision 1.2 2006/02/18 23:19:05 cgruber
- * Update imports and maven dependencies.
+ * $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.1 2006/02/16 13:22:22 cgruber Check in all sources in
+ * eclipse-friendly maven-enabled packages.
*
- * Revision 1.8 2004/01/28 18:34:57 mpowers
- * Better handling for enabling.
- * Now respecting enabledToSetSelectedObjectValueForKey from display group.
+ * Revision 1.8 2004/01/28 18:34:57 mpowers Better handling for enabling. Now
+ * respecting enabledToSetSelectedObjectValueForKey from display group.
*
- * Revision 1.7 2003/08/06 23:07:52 chochos
- * general code cleanup (mostly, removing unused imports)
+ * Revision 1.7 2003/08/06 23:07:52 chochos general code cleanup (mostly,
+ * removing unused imports)
*
- * Revision 1.6 2002/10/11 20:12:58 mpowers
- * Updated aspect signature.
+ * Revision 1.6 2002/10/11 20:12:58 mpowers Updated aspect signature.
*
- * Revision 1.5 2002/10/11 20:08:14 mpowers
- * Added visible aspect to slider association.
+ * Revision 1.5 2002/10/11 20:08:14 mpowers Added visible aspect to slider
+ * association.
*
- * Revision 1.4 2001/11/01 15:54:37 mpowers
- * Minor update to aspect signature.
+ * Revision 1.4 2001/11/01 15:54:37 mpowers Minor update to aspect signature.
*
- * Revision 1.3 2001/07/30 16:32:55 mpowers
- * Implemented support for bulk-editing. Detail associations will now
- * apply changes to all selected objects.
+ * Revision 1.3 2001/07/30 16:32:55 mpowers Implemented support for
+ * bulk-editing. Detail associations will now apply changes to all selected
+ * objects.
*
- * Revision 1.2 2001/02/16 15:03:34 mpowers
- * Fixed: slider sets value in display group after selection changed.
+ * Revision 1.2 2001/02/16 15:03:34 mpowers Fixed: slider sets value in display
+ * group after selection changed.
*
- * Revision 1.1.1.1 2000/12/21 15:48:55 mpowers
- * Contributing wotonomy.
+ * Revision 1.1.1.1 2000/12/21 15:48:55 mpowers Contributing wotonomy.
*
- * Revision 1.2 2000/12/20 16:25:41 michael
- * Added log to all files.
+ * Revision 1.2 2000/12/20 16:25:41 michael Added log to all files.
*
*
*/
-
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/TableAssociation.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/TableAssociation.java
index bc02f7d..edba674 100644
--- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/TableAssociation.java
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/TableAssociation.java
@@ -52,754 +52,619 @@ import net.wotonomy.ui.EOAssociation;
import net.wotonomy.ui.EODisplayGroup;
/**
-* TableAssociation binds one or more TableColumnAssociations
-* to a display group. You should not instantiate this class
-* directly; use TableColumnAssociation.setTable() instead.
-* Note that TableAssociation inserts itself as the controlled
-* JTable's TableModel.
-*
-* Bindings are:
-* <ul>
-* <li>source: a property convertable to a string for
-* display in the cells of the table column</li>
-* <li>enabled: a property convertable to a string for
-* display in the cells of the table column.
-* Note that you can bind this aspect to a key equal to
-* "true" or "false" and leave the display group null.</li>
-* </ul>
-*
-* @author michael@mpowers.net
-* @author $Author: cgruber $
-* @version $Revision: 904 $
-*/
-public class TableAssociation extends EOAssociation
- implements ActionListener, ListSelectionListener, FocusListener
-{
- static final NSArray aspects =
- new NSArray( new Object[] {
- SourceAspect, EnabledAspect
- } );
- static final NSArray aspectSignatures =
- new NSArray( new Object[] {
- AttributeToOneAspectSignature
- } );
- static final NSArray objectKeysTaken =
- new NSArray( new Object[] {
- "tableModel", "tableHeader"
- } );
-
- // key command to copy contents to clipboard
- static public final String COPY = "COPY";
+ * TableAssociation binds one or more TableColumnAssociations to a display
+ * group. You should not instantiate this class directly; use
+ * TableColumnAssociation.setTable() instead. Note that TableAssociation inserts
+ * itself as the controlled JTable's TableModel.
+ *
+ * Bindings are:
+ * <ul>
+ * <li>source: a property convertable to a string for display in the cells of
+ * the table column</li>
+ * <li>enabled: a property convertable to a string for display in the cells of
+ * the table column. Note that you can bind this aspect to a key equal to "true"
+ * or "false" and leave the display group null.</li>
+ * </ul>
+ *
+ * @author michael@mpowers.net
+ * @author $Author: cgruber $
+ * @version $Revision: 904 $
+ */
+public class TableAssociation extends EOAssociation implements ActionListener, ListSelectionListener, FocusListener {
+ static final NSArray aspects = new NSArray(new Object[] { SourceAspect, EnabledAspect });
+ static final NSArray aspectSignatures = new NSArray(new Object[] { AttributeToOneAspectSignature });
+ static final NSArray objectKeysTaken = new NSArray(new Object[] { "tableModel", "tableHeader" });
+
+ // key command to copy contents to clipboard
+ static public final String COPY = "COPY";
EODisplayGroup source;
- EODisplayGroup sortTarget; // used by TreeColumnAssociation
+ EODisplayGroup sortTarget; // used by TreeColumnAssociation
NSMutableArray columns;
- JTableHeader tableHeader;
-
- boolean pleaseIgnore;
- boolean selectionPaintedImmediately;
- boolean selectionTracking;
-
- /**
- * Constructor specifying the object to be controlled by this
- * association. Throws an exception if the object is not
- * a TableColumn. setTable() must be called before
- * establishing the connection.
- */
- public TableAssociation ( Object anObject )
- {
- super( anObject );
+ JTableHeader tableHeader;
+
+ boolean pleaseIgnore;
+ boolean selectionPaintedImmediately;
+ boolean selectionTracking;
+
+ /**
+ * Constructor specifying the object to be controlled by this association.
+ * Throws an exception if the object is not a TableColumn. setTable() must be
+ * called before establishing the connection.
+ */
+ public TableAssociation(Object anObject) {
+ super(anObject);
source = null;
columns = new NSMutableArray();
- JTable aTable = ((JTable)anObject);
- aTable.addFocusListener( this );
- aTable.setModel(
- new TableAssociationModel( this ) );
- // set up keyboard events for cut-copy: Ctrl-C, Ctrl-X
-
- // why did sun make this harder to use?
- //aTable.getInputMap( JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT ).put(
- // KeyStroke.getKeyStroke( KeyEvent.VK_C, java.awt.Event.CTRL_MASK ), COPY);
- //aTable.getActionMap().put(COPY, this);
-
- aTable.registerKeyboardAction( this, COPY,
- KeyStroke.getKeyStroke( KeyEvent.VK_C,
- Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() ),
- JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT );
- aTable.registerKeyboardAction( this, COPY,
- KeyStroke.getKeyStroke( KeyEvent.VK_X,
- Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() ),
- JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT );
- tableHeader = new SortedTableHeader();
- tableHeader.setColumnModel( aTable.getColumnModel() );
- aTable.setTableHeader( tableHeader );
- tableHeader.addMouseListener( new TableHeaderListener() );
- pleaseIgnore = false;
- selectionPaintedImmediately = false;
- selectionTracking = false;
- }
-
- /**
- * 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. <ul>
- * <li>"A" attribute: the aspect can be bound to
- * an attribute.</li>
- * <li>"1" to-one: the aspect can be bound to a
- * property that returns a single object.</li>
- * <li>"M" to-one: the aspect can be bound to a
- * property that returns multiple objects.</li>
- * </ul>
- * 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 ( anObject instanceof JTable );
- }
-
- /**
- * 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. This is typically "value" or somesuch.
- */
- public static String primaryAspect ()
- {
- return ValueAspect;
- }
-
- /**
- * 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 ) );
- }
-
- /**
- * Binds the specified aspect of this association to the
- * specified key on the specified display group.
- */
- public void bindAspect (
- String anAspect, EODisplayGroup aDisplayGroup, String aKey )
- {
- if ( SourceAspect.equals( anAspect ) )
- {
+ JTable aTable = ((JTable) anObject);
+ aTable.addFocusListener(this);
+ aTable.setModel(new TableAssociationModel(this));
+ // set up keyboard events for cut-copy: Ctrl-C, Ctrl-X
+
+ // why did sun make this harder to use?
+ // aTable.getInputMap( JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT ).put(
+ // KeyStroke.getKeyStroke( KeyEvent.VK_C, java.awt.Event.CTRL_MASK ), COPY);
+ // aTable.getActionMap().put(COPY, this);
+
+ aTable.registerKeyboardAction(this, COPY,
+ KeyStroke.getKeyStroke(KeyEvent.VK_C, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()),
+ JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
+ aTable.registerKeyboardAction(this, COPY,
+ KeyStroke.getKeyStroke(KeyEvent.VK_X, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()),
+ JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
+ tableHeader = new SortedTableHeader();
+ tableHeader.setColumnModel(aTable.getColumnModel());
+ aTable.setTableHeader(tableHeader);
+ tableHeader.addMouseListener(new TableHeaderListener());
+ pleaseIgnore = false;
+ selectionPaintedImmediately = false;
+ selectionTracking = false;
+ }
+
+ /**
+ * 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.
+ * <ul>
+ * <li>"A" attribute: the aspect can be bound to an attribute.</li>
+ * <li>"1" to-one: the aspect can be bound to a property that returns a single
+ * object.</li>
+ * <li>"M" to-one: the aspect can be bound to a property that returns multiple
+ * objects.</li>
+ * </ul>
+ * 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 (anObject instanceof JTable);
+ }
+
+ /**
+ * 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. This is typically
+ * "value" or somesuch.
+ */
+ public static String primaryAspect() {
+ return ValueAspect;
+ }
+
+ /**
+ * 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));
+ }
+
+ /**
+ * Binds the specified aspect of this association to the specified key on the
+ * specified display group.
+ */
+ public void bindAspect(String anAspect, EODisplayGroup aDisplayGroup, String aKey) {
+ if (SourceAspect.equals(anAspect)) {
source = aDisplayGroup;
}
- super.bindAspect( anAspect, aDisplayGroup, aKey );
+ super.bindAspect(anAspect, aDisplayGroup, aKey);
}
- /**
- * Establishes a connection between this association
- * and the controlled object. Subclasses should begin
- * listening for events from their controlled object here.
- */
- public void establishConnection ()
- {
- if ( source == null )
- {
- throw new WotonomyException( "No display group: " +
- "please ensure that the TableColumnAssociation " +
- "has a display group bound to the ValueAspect" );
- }
- super.establishConnection();
- selectFromDisplayGroup();
- addAsListener();
- }
-
- /**
- * Breaks the connection between this association and
- * its object. Override to stop listening for events
- * from the object.
- */
- public void breakConnection ()
- {
- removeAsListener();
- super.breakConnection();
- }
-
- protected void addAsListener()
- {
- component().getSelectionModel()
- .addListSelectionListener( this );
- }
-
- protected void removeAsListener()
- {
- component().getSelectionModel()
- .removeListSelectionListener( this );
- }
-
- /**
- * Forces this association to cause the object to
- * stop editing and validate the user's input.
- * @return false if there were problems validating,
- * or true to continue.
- */
- public boolean endEditing ()
- {
- // stop any cell editing
- CellEditor editor = component().getCellEditor();
- if ( editor != null )
- {
- return editor.stopCellEditing();
- }
- return true;
- }
-
- /**
- * Called when either the selection or the contents
- * of an associated display group have changed.
- */
- public void subjectChanged ()
- {
- if ( source != null )
- {
- if ( source.contentsChanged() )
- {
- removeAsListener();
- ((TableAssociationModel)component().getModel()).
- fireTableDataChanged();
+ /**
+ * Establishes a connection between this association and the controlled object.
+ * Subclasses should begin listening for events from their controlled object
+ * here.
+ */
+ public void establishConnection() {
+ if (source == null) {
+ throw new WotonomyException("No display group: " + "please ensure that the TableColumnAssociation "
+ + "has a display group bound to the ValueAspect");
+ }
+ super.establishConnection();
+ selectFromDisplayGroup();
+ addAsListener();
+ }
+
+ /**
+ * Breaks the connection between this association and its object. Override to
+ * stop listening for events from the object.
+ */
+ public void breakConnection() {
+ removeAsListener();
+ super.breakConnection();
+ }
+
+ protected void addAsListener() {
+ component().getSelectionModel().addListSelectionListener(this);
+ }
+
+ protected void removeAsListener() {
+ component().getSelectionModel().removeListSelectionListener(this);
+ }
+
+ /**
+ * Forces this association to cause the object to stop editing and validate the
+ * user's input.
+ *
+ * @return false if there were problems validating, or true to continue.
+ */
+ public boolean endEditing() {
+ // stop any cell editing
+ CellEditor editor = component().getCellEditor();
+ if (editor != null) {
+ return editor.stopCellEditing();
+ }
+ return true;
+ }
+
+ /**
+ * Called when either the selection or the contents of an associated display
+ * group have changed.
+ */
+ public void subjectChanged() {
+ if (source != null) {
+ if (source.contentsChanged()) {
+ removeAsListener();
+ ((TableAssociationModel) component().getModel()).fireTableDataChanged();
selectFromDisplayGroup();
- addAsListener();
-
- // if we caused this change, do nothing
- if ( pleaseIgnore )
- {
- pleaseIgnore = false;
- }
- else // otherwise, update the sort indicator
- {
- tableHeader.repaint();
-
- // cancel any cell editing
- CellEditor editor = component().getCellEditor();
- if ( editor != null )
- {
- editor.cancelCellEditing();
- }
- }
- }
- else
- if ( source.selectionChanged() )
- {
- removeAsListener();
+ addAsListener();
+
+ // if we caused this change, do nothing
+ if (pleaseIgnore) {
+ pleaseIgnore = false;
+ } else // otherwise, update the sort indicator
+ {
+ tableHeader.repaint();
+
+ // cancel any cell editing
+ CellEditor editor = component().getCellEditor();
+ if (editor != null) {
+ editor.cancelCellEditing();
+ }
+ }
+ } else if (source.selectionChanged()) {
+ removeAsListener();
selectFromDisplayGroup();
- addAsListener();
+ addAsListener();
}
}
- }
+ }
- private void selectFromDisplayGroup()
- {
+ private void selectFromDisplayGroup() {
JTable component = component();
int index;
component.getSelectionModel().clearSelection();
- Enumeration e =
- source.selectionIndexes().objectEnumerator();
-
- while ( e.hasMoreElements() )
- { // add selections one-by-one to support non-contiguous
- index = ((Number)e.nextElement()).intValue();
- component.getSelectionModel().addSelectionInterval(
- index, index ); // adds one row
+ Enumeration e = source.selectionIndexes().objectEnumerator();
+
+ while (e.hasMoreElements()) { // add selections one-by-one to support non-contiguous
+ index = ((Number) e.nextElement()).intValue();
+ component.getSelectionModel().addSelectionInterval(index, index); // adds one row
}
}
// interface ListSelectionListener
- public void valueChanged(ListSelectionEvent e)
- {
- if ( source != null )
- {
- if ( selectionTracking || !e.getValueIsAdjusting() )
- {
- int[] selectedIndices = component().getSelectedRows();
- final NSMutableArray indexList = new NSMutableArray();
- for ( int i = 0; i < selectedIndices.length; i++ )
- {
- indexList.addObject( new Integer( selectedIndices[i] ) );
- }
-
- // invoke later so the component is repainted before
- // any potentially lengthy second-order effects happen:
- // this improves user-perceived responsiveness of big apps
- Runnable select = new Runnable()
- {
- public void run()
- {
- pleaseIgnore = true;
- source.setSelectionIndexes( indexList );
- }
- };
- if ( selectionPaintedImmediately )
- {
- EventQueue.invokeLater( select );
- }
- else
- {
- select.run();
- }
- }
- }
- }
-
- /**
- * Determines whether the selection should be painted
- * immediately after the user clicks and therefore
- * before the children display group is updated.
- * When the children group is bound to many associations
- * or is bound to a master-detail association, updating
- * the display group can take a perceptibly long time.
- * This property defaults to false.
- * @see #setSelectionPaintedImmediately
- */
- public boolean isSelectionPaintedImmediately()
- {
- return selectionPaintedImmediately;
- }
-
- /**
- * Sets whether the selection should be painted immediately.
- * Setting this property to true will let the table paint
- * first before the display group is updated.
- */
- public void setSelectionPaintedImmediately( boolean isImmediate )
- {
- selectionPaintedImmediately = isImmediate;
- }
-
- /**
- * Determines whether the selection is actively tracking
- * the selection as the user moves the mouse.
- * If true, selection will not be updated while the
- * list selection event returns true for isValueAdjusting().
- * This property defaults to false.
- * @see #setSelectionTracking
- */
- public boolean isSelectionTracking()
- {
- return selectionTracking;
- }
-
- /**
- * Sets whether the selection is actively tracking
- * the selection as the user moves the mouse.
- * Setting this property to true will update the display
- * group with each change to the selection.
- * This means that any tree selection listers will
- * also be notified before the display group is updated
- * and will have to invokeLater if they want to see the
- * updated display group.
- */
- public void setSelectionTracking( boolean isTracking )
- {
- selectionTracking = isTracking;
- }
-
- // convenience
- private JTable component()
- {
- return (JTable) object();
- }
+ public void valueChanged(ListSelectionEvent e) {
+ if (source != null) {
+ if (selectionTracking || !e.getValueIsAdjusting()) {
+ int[] selectedIndices = component().getSelectedRows();
+ final NSMutableArray indexList = new NSMutableArray();
+ for (int i = 0; i < selectedIndices.length; i++) {
+ indexList.addObject(new Integer(selectedIndices[i]));
+ }
+
+ // invoke later so the component is repainted before
+ // any potentially lengthy second-order effects happen:
+ // this improves user-perceived responsiveness of big apps
+ Runnable select = new Runnable() {
+ public void run() {
+ pleaseIgnore = true;
+ source.setSelectionIndexes(indexList);
+ }
+ };
+ if (selectionPaintedImmediately) {
+ EventQueue.invokeLater(select);
+ } else {
+ select.run();
+ }
+ }
+ }
+ }
/**
- * Copies the contents of the table to the clipboard as a tab-delimited string.
- */
- public void copyToClipboard()
- {
- Toolkit toolkit = Toolkit.getDefaultToolkit();
- Clipboard clipboard = toolkit.getSystemClipboard();
- StringSelection selection =
- new StringSelection( getTabDelimitedString() );
- clipboard.setContents( selection, selection );
- }
+ * Determines whether the selection should be painted immediately after the user
+ * clicks and therefore before the children display group is updated. When the
+ * children group is bound to many associations or is bound to a master-detail
+ * association, updating the display group can take a perceptibly long time.
+ * This property defaults to false.
+ *
+ * @see #setSelectionPaintedImmediately
+ */
+ public boolean isSelectionPaintedImmediately() {
+ return selectionPaintedImmediately;
+ }
/**
- * Converts the contents of the table to a tab-delimited string.
- * @return A String containing the text contents of the table.
- */
- public String getTabDelimitedString()
- {
- StringBuffer result = new StringBuffer(64);
-
- TableModel model = component().getModel();
- int cols = model.getColumnCount();
- int rows = model.getRowCount();
-
- Object o = null;
- for ( int y = 0; y < rows; y++ )
- {
- for ( int x = 0; x < cols; x++ )
- {
- o = model.getValueAt( y, x );
- if ( o == null ) o = "";
- result.append( o );
- result.append( '\t' );
- }
- result.append( '\n' );
- }
-
- return result.toString();
- }
-
- // interface ActionEventListener
-
- public void actionPerformed(ActionEvent evt)
- {
- String cmd = evt.getActionCommand();
-
- if ( COPY.equals( cmd ) )
- {
- copyToClipboard();
- return;
- }
- }
-
- /**
- * Used to render the little triangle over the sorted column(s).
- */
- class SortedTableHeader extends JTableHeader
- {
- public void paint(Graphics g)
- {
- super.paint( g );
- Rectangle r;
- TableColumnAssociation association;
- int size = columns.size();
- NSArray orderings;
- if ( sortTarget != null )
- {
- orderings = sortTarget.sortOrderings();
- }
- else
- {
- orderings = source.sortOrderings();
- }
- for ( int i = 0; i < size; i++ )
- {
- r = getHeaderRect( component().convertColumnIndexToView( i ) );
- association = (TableColumnAssociation) columns.objectAtIndex( i );
- association.drawSortIndicator( r, g, orderings );
- }
- }
- }
-
- /**
- * Used to listen for clicks on the table header.
- */
- class TableHeaderListener extends MouseAdapter
- {
- public void mouseClicked( MouseEvent evt )
- {
- EODisplayGroup displayGroup = sortTarget;
- if ( displayGroup == null ) displayGroup = source;
-
- if ( evt.getClickCount() > 0 )
- {
- int columnClicked = tableHeader.columnAtPoint( evt.getPoint() );
- if ( columnClicked != -1 )
- {
- columnClicked = component().convertColumnIndexToModel( columnClicked );
- TableColumnAssociation association = (TableColumnAssociation)
- columns.objectAtIndex( columnClicked );
- if ( association.isSortable() )
- {
- NSMutableArray newOrder =
- new NSMutableArray( displayGroup.sortOrderings() );
-
- // click once = asc, twice = desc, thrice = clear
- EOSortOrdering sortOrdering;
- int index = association.getIndexOfMatchingOrdering( newOrder );
- if ( index == -1 ) sortOrdering = null;
- else if ( index == 1 ) sortOrdering = association.getSortOrdering( false );
- else sortOrdering = association.getSortOrdering( true );
-
- pleaseIgnore = true;
- tableHeader.repaint();
-
- // stop any cell editing
- CellEditor editor = component().getCellEditor();
- if ( editor != null )
- {
- editor.stopCellEditing();
- }
-
- // remove existing key
- if ( index != 0 )
- {
- newOrder.removeObjectAtIndex( Math.abs( index ) - 1 );
- }
-
- // put new key at front of list
- if ( sortOrdering != null )
- {
- newOrder.insertObjectAtIndex( sortOrdering, 0 );
- }
-
- displayGroup.setSortOrderings( newOrder );
- displayGroup.updateDisplayedObjects();
- }
- }
- }
- }
- }
+ * Sets whether the selection should be painted immediately. Setting this
+ * property to true will let the table paint first before the display group is
+ * updated.
+ */
+ public void setSelectionPaintedImmediately(boolean isImmediate) {
+ selectionPaintedImmediately = isImmediate;
+ }
/**
- * Notifies of beginning of edit.
- */
- public void focusGained(FocusEvent evt)
- {
+ * Determines whether the selection is actively tracking the selection as the
+ * user moves the mouse. If true, selection will not be updated while the list
+ * selection event returns true for isValueAdjusting(). This property defaults
+ * to false.
+ *
+ * @see #setSelectionTracking
+ */
+ public boolean isSelectionTracking() {
+ return selectionTracking;
+ }
+
+ /**
+ * Sets whether the selection is actively tracking the selection as the user
+ * moves the mouse. Setting this property to true will update the display group
+ * with each change to the selection. This means that any tree selection listers
+ * will also be notified before the display group is updated and will have to
+ * invokeLater if they want to see the updated display group.
+ */
+ public void setSelectionTracking(boolean isTracking) {
+ selectionTracking = isTracking;
+ }
+
+ // convenience
+ private JTable component() {
+ return (JTable) object();
+ }
+
+ /**
+ * Copies the contents of the table to the clipboard as a tab-delimited string.
+ */
+ public void copyToClipboard() {
+ Toolkit toolkit = Toolkit.getDefaultToolkit();
+ Clipboard clipboard = toolkit.getSystemClipboard();
+ StringSelection selection = new StringSelection(getTabDelimitedString());
+ clipboard.setContents(selection, selection);
+ }
+
+ /**
+ * Converts the contents of the table to a tab-delimited string.
+ *
+ * @return A String containing the text contents of the table.
+ */
+ public String getTabDelimitedString() {
+ StringBuffer result = new StringBuffer(64);
+
+ TableModel model = component().getModel();
+ int cols = model.getColumnCount();
+ int rows = model.getRowCount();
+
+ Object o = null;
+ for (int y = 0; y < rows; y++) {
+ for (int x = 0; x < cols; x++) {
+ o = model.getValueAt(y, x);
+ if (o == null)
+ o = "";
+ result.append(o);
+ result.append('\t');
+ }
+ result.append('\n');
+ }
+
+ return result.toString();
+ }
+
+ // interface ActionEventListener
+
+ public void actionPerformed(ActionEvent evt) {
+ String cmd = evt.getActionCommand();
+
+ if (COPY.equals(cmd)) {
+ copyToClipboard();
+ return;
+ }
+ }
+
+ /**
+ * Used to render the little triangle over the sorted column(s).
+ */
+ class SortedTableHeader extends JTableHeader {
+ public void paint(Graphics g) {
+ super.paint(g);
+ Rectangle r;
+ TableColumnAssociation association;
+ int size = columns.size();
+ NSArray orderings;
+ if (sortTarget != null) {
+ orderings = sortTarget.sortOrderings();
+ } else {
+ orderings = source.sortOrderings();
+ }
+ for (int i = 0; i < size; i++) {
+ r = getHeaderRect(component().convertColumnIndexToView(i));
+ association = (TableColumnAssociation) columns.objectAtIndex(i);
+ association.drawSortIndicator(r, g, orderings);
+ }
+ }
+ }
+
+ /**
+ * Used to listen for clicks on the table header.
+ */
+ class TableHeaderListener extends MouseAdapter {
+ public void mouseClicked(MouseEvent evt) {
+ EODisplayGroup displayGroup = sortTarget;
+ if (displayGroup == null)
+ displayGroup = source;
+
+ if (evt.getClickCount() > 0) {
+ int columnClicked = tableHeader.columnAtPoint(evt.getPoint());
+ if (columnClicked != -1) {
+ columnClicked = component().convertColumnIndexToModel(columnClicked);
+ TableColumnAssociation association = (TableColumnAssociation) columns.objectAtIndex(columnClicked);
+ if (association.isSortable()) {
+ NSMutableArray newOrder = new NSMutableArray(displayGroup.sortOrderings());
+
+ // click once = asc, twice = desc, thrice = clear
+ EOSortOrdering sortOrdering;
+ int index = association.getIndexOfMatchingOrdering(newOrder);
+ if (index == -1)
+ sortOrdering = null;
+ else if (index == 1)
+ sortOrdering = association.getSortOrdering(false);
+ else
+ sortOrdering = association.getSortOrdering(true);
+
+ pleaseIgnore = true;
+ tableHeader.repaint();
+
+ // stop any cell editing
+ CellEditor editor = component().getCellEditor();
+ if (editor != null) {
+ editor.stopCellEditing();
+ }
+
+ // remove existing key
+ if (index != 0) {
+ newOrder.removeObjectAtIndex(Math.abs(index) - 1);
+ }
+
+ // put new key at front of list
+ if (sortOrdering != null) {
+ newOrder.insertObjectAtIndex(sortOrdering, 0);
+ }
+
+ displayGroup.setSortOrderings(newOrder);
+ displayGroup.updateDisplayedObjects();
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Notifies of beginning of edit.
+ */
+ public void focusGained(FocusEvent evt) {
Object o;
EODisplayGroup displayGroup;
- Enumeration e = aspects().objectEnumerator();
- while ( e.hasMoreElements() )
- {
- displayGroup =
- displayGroupForAspect( e.nextElement().toString() );
- if ( displayGroup != null )
- {
- displayGroup.associationDidBeginEditing( this );
+ Enumeration e = aspects().objectEnumerator();
+ while (e.hasMoreElements()) {
+ displayGroup = displayGroupForAspect(e.nextElement().toString());
+ if (displayGroup != null) {
+ displayGroup.associationDidBeginEditing(this);
}
}
- }
+ }
/**
- * Updates object on focus lost and notifies of end of edit.
- */
- public void focusLost(FocusEvent evt)
- {
- if ( ! component().isEditing() )
- {
- Object o;
- EODisplayGroup displayGroup;
- Enumeration e = aspects().objectEnumerator();
- while ( e.hasMoreElements() )
- {
- displayGroup =
- displayGroupForAspect( e.nextElement().toString() );
- if ( displayGroup != null )
- {
- displayGroup.associationDidEndEditing( this );
- }
- }
- }
- }
+ * Updates object on focus lost and notifies of end of edit.
+ */
+ public void focusLost(FocusEvent evt) {
+ if (!component().isEditing()) {
+ Object o;
+ EODisplayGroup displayGroup;
+ Enumeration e = aspects().objectEnumerator();
+ while (e.hasMoreElements()) {
+ displayGroup = displayGroupForAspect(e.nextElement().toString());
+ if (displayGroup != null) {
+ displayGroup.associationDidEndEditing(this);
+ }
+ }
+ }
+ }
/**
- * Used as the model for the controlled table.
- * Package access so TableColumnAssociation can recognize
- * it and use the addColumnAssociation() method.
- * Extends AbstractTableModel just so we get event
- * broadcasting for free.
- */
- static class TableAssociationModel extends AbstractTableModel
- {
+ * Used as the model for the controlled table. Package access so
+ * TableColumnAssociation can recognize it and use the addColumnAssociation()
+ * method. Extends AbstractTableModel just so we get event broadcasting for
+ * free.
+ */
+ static class TableAssociationModel extends AbstractTableModel {
private TableAssociation parent;
- private TableAssociationModel( TableAssociation aParent )
- {
+ private TableAssociationModel(TableAssociation aParent) {
parent = aParent;
}
-
- public TableAssociation getAssociation()
- {
- return parent;
- }
+
+ public TableAssociation getAssociation() {
+ return parent;
+ }
/**
- * Adds the column to the list of ColumnAssociations,
- * and adds the corresponding column to the table
- * at the next available index, setting the value of
- * the column's model index accordingly.
- * Establishes connection if no columns are currently
- * associated.
- */
- public void addColumnAssociation(
- TableColumnAssociation aColumnAssociation )
- {
- // establish connection if necessary
- if ( parent.columns.size() == 0 )
- {
- parent.establishConnection();
- }
-
+ * Adds the column to the list of ColumnAssociations, and adds the corresponding
+ * column to the table at the next available index, setting the value of the
+ * column's model index accordingly. Establishes connection if no columns are
+ * currently associated.
+ */
+ public void addColumnAssociation(TableColumnAssociation aColumnAssociation) {
+ // establish connection if necessary
+ if (parent.columns.size() == 0) {
+ parent.establishConnection();
+ }
+
int newIndex = parent.columns.count();
- parent.columns.add( aColumnAssociation );
+ parent.columns.add(aColumnAssociation);
// add column to table
TableColumn column = (TableColumn) aColumnAssociation.object();
- column.setModelIndex( newIndex );
- parent.component().addColumn( column );
-
+ column.setModelIndex(newIndex);
+ parent.component().addColumn(column);
+
}
/**
- * Removes the column from the list of ColumnAssociations,
- * and removes the corresponding column from the table.
- * Breaks connection if no more columns are associated.
- */
- public void removeColumnAssociation(
- TableColumnAssociation aColumnAssociation )
- {
- int index = parent.columns.indexOfIdenticalObject( aColumnAssociation );
- if ( index == NSArray.NotFound ) return;
-
- parent.columns.removeObjectAtIndex( index );
+ * Removes the column from the list of ColumnAssociations, and removes the
+ * corresponding column from the table. Breaks connection if no more columns are
+ * associated.
+ */
+ public void removeColumnAssociation(TableColumnAssociation aColumnAssociation) {
+ int index = parent.columns.indexOfIdenticalObject(aColumnAssociation);
+ if (index == NSArray.NotFound)
+ return;
+
+ parent.columns.removeObjectAtIndex(index);
// remove column from table
TableColumn column = (TableColumn) aColumnAssociation.object();
- parent.component().removeColumn( column );
-
- // break connection if necessary
- if ( parent.columns.size() == 0 )
- {
- parent.breakConnection();
- }
+ parent.component().removeColumn(column);
+
+ // break connection if necessary
+ if (parent.columns.size() == 0) {
+ parent.breakConnection();
+ }
}
- public int getRowCount()
- {
- if ( parent.source == null ) return 0;
+ public int getRowCount() {
+ if (parent.source == null)
+ return 0;
return parent.source.displayedObjects().count();
}
- public int getColumnCount()
- {
+ public int getColumnCount() {
return parent.columns.count();
}
/**
- * Attempts to retrieve the header value from the specified column,
- * or returns " " if the value is null.
- */
- public String getColumnName(int columnIndex)
- {
- TableColumnAssociation association = (TableColumnAssociation)
- parent.columns.objectAtIndex( columnIndex );
- Object value = ((TableColumn)association.object()).getHeaderValue();
- if ( value != null ) return value.toString();
- return " ";
+ * Attempts to retrieve the header value from the specified column, or returns "
+ * " if the value is null.
+ */
+ public String getColumnName(int columnIndex) {
+ TableColumnAssociation association = (TableColumnAssociation) parent.columns.objectAtIndex(columnIndex);
+ Object value = ((TableColumn) association.object()).getHeaderValue();
+ if (value != null)
+ return value.toString();
+ return " ";
}
/**
- * Returns the class of the first item in the
- * display group bound to the column.
- */
- public Class getColumnClass(int columnIndex)
- {
- Object value;
- int count = getRowCount();
- for( int i = 0; i < count; i++ )
- { //First row in column is null find one that is not.
- value = ((TableColumnAssociation)parent.columns.
- objectAtIndex( columnIndex ) ).valueAtIndex( i );
- if ( value != null ) return value.getClass();
- }
- return Object.class;
+ * Returns the class of the first item in the display group bound to the column.
+ */
+ public Class getColumnClass(int columnIndex) {
+ Object value;
+ int count = getRowCount();
+ for (int i = 0; i < count; i++) { // First row in column is null find one that is not.
+ value = ((TableColumnAssociation) parent.columns.objectAtIndex(columnIndex)).valueAtIndex(i);
+ if (value != null)
+ return value.getClass();
+ }
+ return Object.class;
}
- /**
- * Calls the column association's isEditableAtRow method.
- */
- public boolean isCellEditable(int rowIndex,
- int columnIndex)
- {
- return
- ((TableColumnAssociation)parent.columns.objectAtIndex(
- columnIndex ) ).isEditableAtRow( rowIndex );
+ /**
+ * Calls the column association's isEditableAtRow method.
+ */
+ public boolean isCellEditable(int rowIndex, int columnIndex) {
+ return ((TableColumnAssociation) parent.columns.objectAtIndex(columnIndex)).isEditableAtRow(rowIndex);
}
- /**
- * Calls the column association's valueAtIndex method.
- */
- public Object getValueAt(int rowIndex,
- int columnIndex)
- {
- return
- ((TableColumnAssociation)parent.columns.objectAtIndex(
- columnIndex ) ).valueAtIndex( rowIndex );
+ /**
+ * Calls the column association's valueAtIndex method.
+ */
+ public Object getValueAt(int rowIndex, int columnIndex) {
+ return ((TableColumnAssociation) parent.columns.objectAtIndex(columnIndex)).valueAtIndex(rowIndex);
}
- /**
- * Calls the column association's setValueAtIndex method.
- */
- public void setValueAt(Object aValue,
- int rowIndex,
- int columnIndex)
- {
- Object existingValue = getValueAt( rowIndex, columnIndex );
-
- // don't update display group if value is the same as before
- if ( aValue == existingValue ) return; // handles null case
- if ( existingValue != null ) // both are not null
- {
- Object newValue = aValue;
-
- // if different types
- if ( newValue.getClass() != existingValue.getClass() )
- {
- // convert to string since most editors do anyway
- newValue = newValue.toString();
- existingValue = existingValue.toString();
- }
- if ( newValue.equals( existingValue ) )
- {
- // same value - do nothing
- return;
- }
- }
-
- ((TableColumnAssociation)parent.columns.objectAtIndex(
- columnIndex ) ).setValueAtIndex( aValue, rowIndex );
+ /**
+ * Calls the column association's setValueAtIndex method.
+ */
+ public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
+ Object existingValue = getValueAt(rowIndex, columnIndex);
+
+ // don't update display group if value is the same as before
+ if (aValue == existingValue)
+ return; // handles null case
+ if (existingValue != null) // both are not null
+ {
+ Object newValue = aValue;
+
+ // if different types
+ if (newValue.getClass() != existingValue.getClass()) {
+ // convert to string since most editors do anyway
+ newValue = newValue.toString();
+ existingValue = existingValue.toString();
+ }
+ if (newValue.equals(existingValue)) {
+ // same value - do nothing
+ return;
+ }
+ }
+
+ ((TableColumnAssociation) parent.columns.objectAtIndex(columnIndex)).setValueAtIndex(aValue, rowIndex);
}
}
@@ -807,121 +672,106 @@ public class TableAssociation extends EOAssociation
}
/*
- * $Log$
- * Revision 1.2 2006/02/18 23:19:05 cgruber
- * Update imports and maven dependencies.
+ * $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.1 2006/02/16 13:22:22 cgruber Check in all sources in
+ * eclipse-friendly maven-enabled packages.
*
- * Revision 1.31 2003/08/06 23:07:52 chochos
- * general code cleanup (mostly, removing unused imports)
+ * Revision 1.31 2003/08/06 23:07:52 chochos general code cleanup (mostly,
+ * removing unused imports)
*
- * Revision 1.30 2002/11/16 16:33:31 mpowers
- * Now using platform-specific accelerator key for shortcuts.
+ * Revision 1.30 2002/11/16 16:33:31 mpowers Now using platform-specific
+ * accelerator key for shortcuts.
*
- * Revision 1.29 2002/05/24 14:41:29 mpowers
- * Throwing an exception for clarity.
+ * Revision 1.29 2002/05/24 14:41:29 mpowers Throwing an exception for clarity.
*
- * Revision 1.28 2002/04/10 21:20:04 mpowers
- * Better handling for tree nodes when working with editing contexts.
- * Better handling for invalidation. No longer broadcasting events
- * when nodes have not been "registered" in the tree.
+ * Revision 1.28 2002/04/10 21:20:04 mpowers Better handling for tree nodes when
+ * working with editing contexts. Better handling for invalidation. No longer
+ * broadcasting events when nodes have not been "registered" in the tree.
*
- * Revision 1.27 2002/03/05 23:18:28 mpowers
- * Added documentation.
- * Added isSelectionPaintedImmediate and isSelectionTracking attributes
- * to TableAssociation.
- * Added getTableAssociation to TableColumnAssociation.
+ * Revision 1.27 2002/03/05 23:18:28 mpowers Added documentation. Added
+ * isSelectionPaintedImmediate and isSelectionTracking attributes to
+ * TableAssociation. Added getTableAssociation to TableColumnAssociation.
*
- * Revision 1.25 2002/03/04 22:49:53 mpowers
- * Now working with TreeColumnAssociation to delegate sorting behavior.
+ * Revision 1.25 2002/03/04 22:49:53 mpowers Now working with
+ * TreeColumnAssociation to delegate sorting behavior.
*
- * Revision 1.23 2002/03/04 03:58:17 mpowers
- * Refined table header click behavior.
+ * Revision 1.23 2002/03/04 03:58:17 mpowers Refined table header click
+ * behavior.
*
- * Revision 1.22 2002/03/01 23:41:39 mpowers
- * Added facility to get table association from model.
+ * Revision 1.22 2002/03/01 23:41:39 mpowers Added facility to get table
+ * association from model.
*
- * Revision 1.21 2002/03/01 20:41:22 mpowers
- * Cleaned up unused code.
+ * Revision 1.21 2002/03/01 20:41:22 mpowers Cleaned up unused code.
*
- * Revision 1.20 2002/03/01 15:42:00 mpowers
- * Table column headers now always show their sort indicator.
- * A third table-column click clears the sort for that column.
+ * Revision 1.20 2002/03/01 15:42:00 mpowers Table column headers now always
+ * show their sort indicator. A third table-column click clears the sort for
+ * that column.
*
- * Revision 1.19 2002/02/28 23:01:39 mpowers
- * TableColumnAssociations add and remove themselves from the TableAssociation
- * when their connection is established and broken respectively.
- * TableAssociations now break connection if they have no column associations.
+ * Revision 1.19 2002/02/28 23:01:39 mpowers TableColumnAssociations add and
+ * remove themselves from the TableAssociation when their connection is
+ * established and broken respectively. TableAssociations now break connection
+ * if they have no column associations.
*
- * Revision 1.18 2002/02/28 22:45:27 mpowers
- * Now registers as editing association when table gets focus, so that
- * endEditing can appropriate commit any table cell editors.
+ * Revision 1.18 2002/02/28 22:45:27 mpowers Now registers as editing
+ * association when table gets focus, so that endEditing can appropriate commit
+ * any table cell editors.
*
- * Revision 1.17 2001/10/26 20:01:50 mpowers
- * We're again cancelling instead of stopping editing on subjectChanged.
- * I believe that the introduction of NSRunLoop has allowed us to return
- * to the correct behavior.
+ * Revision 1.17 2001/10/26 20:01:50 mpowers We're again cancelling instead of
+ * stopping editing on subjectChanged. I believe that the introduction of
+ * NSRunLoop has allowed us to return to the correct behavior.
*
- * Revision 1.16 2001/09/14 13:40:26 mpowers
- * User-initiated selection changes are now handled on the next event loop
- * so that the component repaints the new selection before any potentially
- * lengthy logic is triggered by the selection change.
+ * Revision 1.16 2001/09/14 13:40:26 mpowers User-initiated selection changes
+ * are now handled on the next event loop so that the component repaints the new
+ * selection before any potentially lengthy logic is triggered by the selection
+ * change.
*
- * Revision 1.15 2001/07/25 15:03:01 mpowers
- * getColumnName now checks for a column header value before defaulting " ".
+ * Revision 1.15 2001/07/25 15:03:01 mpowers getColumnName now checks for a
+ * column header value before defaulting " ".
*
- * Revision 1.14 2001/06/05 19:09:25 mpowers
- * Fixed broken build.
+ * Revision 1.14 2001/06/05 19:09:25 mpowers Fixed broken build.
*
- * Revision 1.13 2001/06/05 19:08:12 mpowers
- * Better determination of column class. Previously, if the first object
- * was null, Object.class was used. Now we get the first non-null object.
+ * Revision 1.13 2001/06/05 19:08:12 mpowers Better determination of column
+ * class. Previously, if the first object was null, Object.class was used. Now
+ * we get the first non-null object.
*
- * Revision 1.12 2001/05/02 17:39:01 mpowers
- * Now selects from display group after initial population.
+ * Revision 1.12 2001/05/02 17:39:01 mpowers Now selects from display group
+ * after initial population.
*
- * Revision 1.11 2001/03/29 23:05:22 mpowers
- * Fixes for editing table cells.
+ * Revision 1.11 2001/03/29 23:05:22 mpowers Fixes for editing table cells.
*
- * Revision 1.10 2001/03/09 22:09:22 mpowers
- * Now better handling jdk1.1 for rendering the column header.
+ * Revision 1.10 2001/03/09 22:09:22 mpowers Now better handling jdk1.1 for
+ * rendering the column header.
*
- * Revision 1.9 2001/02/27 02:10:38 mpowers
- * No longer updating values to the display group if the value
- * has not changed.
+ * Revision 1.9 2001/02/27 02:10:38 mpowers No longer updating values to the
+ * display group if the value has not changed.
*
- * 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/02/16 17:47:17 mpowers
- * Now cancelling any cell editing on subjectChanged().
+ * Revision 1.7 2001/02/16 17:47:17 mpowers Now cancelling any cell editing on
+ * subjectChanged().
*
- * Revision 1.6 2001/01/12 19:11:56 mpowers
- * Fixed table column click sorting.
+ * Revision 1.6 2001/01/12 19:11:56 mpowers Fixed table column click sorting.
*
- * Revision 1.5 2001/01/12 17:20:30 mpowers
- * Moved EOSortOrdering creation to ColumnAssociation.
+ * Revision 1.5 2001/01/12 17:20:30 mpowers Moved EOSortOrdering creation to
+ * ColumnAssociation.
*
- * Revision 1.4 2001/01/11 21:55:57 mpowers
- * Implemented sort indicator for table column headers.
+ * Revision 1.4 2001/01/11 21:55:57 mpowers Implemented sort indicator for table
+ * column headers.
*
- * Revision 1.3 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/11 20:34:26 mpowers Implemented EOSortOrdering and added
+ * support in framework. Added header-click to sort table columns.
*
- * Revision 1.2 2001/01/08 20:40:51 mpowers
- * JTables in 1.3 clear their selection when the data model changes,
- * which sends a list selection event. We now reset the selection again
- * after the data changes so we're compatible across 1.2 and 1.3.
+ * Revision 1.2 2001/01/08 20:40:51 mpowers JTables in 1.3 clear their selection
+ * when the data model changes, which sends a list selection event. We now reset
+ * the selection again after the data changes so we're compatible across 1.2 and
+ * 1.3.
*
- * Revision 1.1.1.1 2000/12/21 15:49:00 mpowers
- * Contributing wotonomy.
+ * Revision 1.1.1.1 2000/12/21 15:49:00 mpowers Contributing wotonomy.
*
- * Revision 1.7 2000/12/20 16:25:41 michael
- * Added log to all files.
+ * Revision 1.7 2000/12/20 16:25:41 michael Added log to all files.
*
*
*/
-
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/TableColumnAssociation.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/TableColumnAssociation.java
index e1c32f3..f279926 100644
--- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/TableColumnAssociation.java
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/TableColumnAssociation.java
@@ -38,671 +38,544 @@ import net.wotonomy.ui.EOAssociation;
import net.wotonomy.ui.EODisplayGroup;
import net.wotonomy.ui.swing.TableAssociation.TableAssociationModel;
-
/**
-* TableColumnAssociation binds a column of a JTable
-* to a property of the elements of a display group.
-* Bindings are:
-* <ul>
-* <li>value: a property convertable to a string for
-* display in the cells of the table column</li>
-* <li>editable: a property convertable to a boolean
-* that determines the editability of the corresponding
-* cells in the column.</li>
-* </ul>
-
-* Because TableColumns do not have a handle to their
-* containing JTable, setTable() must be called before
-* calling establishConnection(). This will add the
-* controlled TableColumn to the specified JTable.
-*
-* Columns appear in the table in the order in which
-* setTable is called on the corresponding association.
-* The original table model index is ignored.
-*
-* Column names appear in the table based on the value
-* of TableColumn.getHeaderValue().
-*
-* @author michael@mpowers.net
-* @author $Author: cgruber $
-* @version $Revision: 904 $
-*/
-public class TableColumnAssociation extends EOAssociation
-{
- static final NSArray aspects =
- new NSArray( new Object[] {
- ValueAspect, EditableAspect
- } );
- static final NSArray aspectSignatures =
- new NSArray( new Object[] {
- AttributeToOneAspectSignature
- } );
- static final NSArray objectKeysTaken =
- new NSArray( new Object[] {
- "table"
- } );
-
- static Color[] sortIndicatorColorList;
-
+ * TableColumnAssociation binds a column of a JTable to a property of the
+ * elements of a display group. Bindings are:
+ * <ul>
+ * <li>value: a property convertable to a string for display in the cells of the
+ * table column</li>
+ * <li>editable: a property convertable to a boolean that determines the
+ * editability of the corresponding cells in the column.</li>
+ * </ul>
+ *
+ * Because TableColumns do not have a handle to their containing JTable,
+ * setTable() must be called before calling establishConnection(). This will add
+ * the controlled TableColumn to the specified JTable.
+ *
+ * Columns appear in the table in the order in which setTable is called on the
+ * corresponding association. The original table model index is ignored.
+ *
+ * Column names appear in the table based on the value of
+ * TableColumn.getHeaderValue().
+ *
+ * @author michael@mpowers.net
+ * @author $Author: cgruber $
+ * @version $Revision: 904 $
+ */
+public class TableColumnAssociation extends EOAssociation {
+ static final NSArray aspects = new NSArray(new Object[] { ValueAspect, EditableAspect });
+ static final NSArray aspectSignatures = new NSArray(new Object[] { AttributeToOneAspectSignature });
+ static final NSArray objectKeysTaken = new NSArray(new Object[] { "table" });
+
+ static Color[] sortIndicatorColorList;
+
EODisplayGroup valueDisplayGroup, editableDisplayGroup;
String valueKey, editableKey;
- boolean sortable;
- boolean sortCaseSensitive;
- JTable table;
-
- /**
- * Constructor specifying the object to be controlled by this
- * association. Throws an exception if the object is not
- * a TableColumn. setTable() must be called before
- * establishing the connection.
- */
- public TableColumnAssociation ( Object anObject )
- {
- super( anObject );
+ boolean sortable;
+ boolean sortCaseSensitive;
+ JTable table;
+
+ /**
+ * Constructor specifying the object to be controlled by this association.
+ * Throws an exception if the object is not a TableColumn. setTable() must be
+ * called before establishing the connection.
+ */
+ public TableColumnAssociation(Object anObject) {
+ super(anObject);
valueDisplayGroup = null;
valueKey = null;
editableDisplayGroup = null;
editableKey = null;
- sortable = true;
- sortCaseSensitive = true;
- table = null;
- }
-
+ sortable = true;
+ sortCaseSensitive = true;
+ table = null;
+ }
+
/**
- * Sets the table to be used for this column association.
- * If no TableAssociation exists for the specified table,
- * one will be created automatically. The controlled
- * table column will be added to the table. Note that
- * the table column's model index is ignored: table columns
- * appear in the table in the order in which setTable is
- * called on their corresponding associations.
- */
- public void setTable( JTable aTable )
- {
- table = aTable;
- if ( table == null ) return;
-
- // creates table association if not existing
- getTableAssociation();
+ * Sets the table to be used for this column association. If no TableAssociation
+ * exists for the specified table, one will be created automatically. The
+ * controlled table column will be added to the table. Note that the table
+ * column's model index is ignored: table columns appear in the table in the
+ * order in which setTable is called on their corresponding associations.
+ */
+ public void setTable(JTable aTable) {
+ table = aTable;
+ if (table == null)
+ return;
+
+ // creates table association if not existing
+ getTableAssociation();
}
-
- /**
- * Returns the table association for this table column,
- * or null if no table has been set. This method will
- * create the association if none exists for the table.
- */
- public TableAssociation getTableAssociation()
- {
- if ( table == null ) return null;
-
- TableAssociation result;
- if ( ! ( table.getModel() instanceof TableAssociationModel ) )
- {
- result = new TableAssociation( table );
- result.bindAspect(
- SourceAspect, displayGroupForAspect( ValueAspect ), "" );
+
+ /**
+ * Returns the table association for this table column, or null if no table has
+ * been set. This method will create the association if none exists for the
+ * table.
+ */
+ public TableAssociation getTableAssociation() {
+ if (table == null)
+ return null;
+
+ TableAssociation result;
+ if (!(table.getModel() instanceof TableAssociationModel)) {
+ result = new TableAssociation(table);
+ result.bindAspect(SourceAspect, displayGroupForAspect(ValueAspect), "");
+ } else {
+ result = ((TableAssociationModel) table.getModel()).getAssociation();
}
- else
- {
- result = ((TableAssociationModel)table.getModel()).getAssociation();
- }
- return result;
- }
-
- /**
- * 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. <ul>
- * <li>"A" attribute: the aspect can be bound to
- * an attribute.</li>
- * <li>"1" to-one: the aspect can be bound to a
- * property that returns a single object.</li>
- * <li>"M" to-one: the aspect can be bound to a
- * property that returns multiple objects.</li>
- * </ul>
- * 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 ( anObject instanceof TableColumn );
- }
-
- /**
- * 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. This is typically "value" or somesuch.
- */
- public static String primaryAspect ()
- {
- return ValueAspect;
- }
-
- /**
- * 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 ) );
- }
-
- /**
- * Binds the specified aspect of this association to the
- * specified key on the specified display group.
- */
- public void bindAspect (
- String anAspect, EODisplayGroup aDisplayGroup, String aKey )
- {
- if ( ValueAspect.equals( anAspect ) )
- {
- valueDisplayGroup = aDisplayGroup;
+ return result;
+ }
+
+ /**
+ * 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.
+ * <ul>
+ * <li>"A" attribute: the aspect can be bound to an attribute.</li>
+ * <li>"1" to-one: the aspect can be bound to a property that returns a single
+ * object.</li>
+ * <li>"M" to-one: the aspect can be bound to a property that returns multiple
+ * objects.</li>
+ * </ul>
+ * 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 (anObject instanceof TableColumn);
+ }
+
+ /**
+ * 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. This is typically
+ * "value" or somesuch.
+ */
+ public static String primaryAspect() {
+ return ValueAspect;
+ }
+
+ /**
+ * 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));
+ }
+
+ /**
+ * Binds the specified aspect of this association to the specified key on the
+ * specified display group.
+ */
+ public void bindAspect(String anAspect, EODisplayGroup aDisplayGroup, String aKey) {
+ if (ValueAspect.equals(anAspect)) {
+ valueDisplayGroup = aDisplayGroup;
valueKey = aKey;
}
- if ( EditableAspect.equals( anAspect ) )
- {
+ if (EditableAspect.equals(anAspect)) {
editableDisplayGroup = aDisplayGroup;
editableKey = aKey;
}
- super.bindAspect( anAspect, aDisplayGroup, aKey );
+ super.bindAspect(anAspect, aDisplayGroup, aKey);
}
-
- /**
- * Establishes a connection between this association
- * and the controlled object. Subclasses should begin
- * listening for events from their controlled object here.
- */
- public void establishConnection ()
- {
- addAsListener();
-
- if ( table == null ) throw new WotonomyException(
- "A table must be specified by calling setTable()" );
+
+ /**
+ * Establishes a connection between this association and the controlled object.
+ * Subclasses should begin listening for events from their controlled object
+ * here.
+ */
+ public void establishConnection() {
+ addAsListener();
+
+ if (table == null)
+ throw new WotonomyException("A table must be specified by calling setTable()");
// add association to model
- TableAssociationModel model =
- (TableAssociationModel) table.getModel();
- model.addColumnAssociation( this );
-
- super.establishConnection();
- }
-
- /**
- * Breaks the connection between this association and
- * its object. Override to stop listening for events
- * from the object.
- */
- public void breakConnection ()
- {
- removeAsListener();
-
- if ( table == null ) throw new WotonomyException(
- "TableColumnAssociation's table may not be null" );
+ TableAssociationModel model = (TableAssociationModel) table.getModel();
+ model.addColumnAssociation(this);
+
+ super.establishConnection();
+ }
+
+ /**
+ * Breaks the connection between this association and its object. Override to
+ * stop listening for events from the object.
+ */
+ public void breakConnection() {
+ removeAsListener();
+
+ if (table == null)
+ throw new WotonomyException("TableColumnAssociation's table may not be null");
// remove association from model
- TableAssociationModel model =
- (TableAssociationModel) table.getModel();
- model.removeColumnAssociation( this );
-
- super.breakConnection();
- }
-
- protected void addAsListener()
- {
- }
-
- protected void removeAsListener()
- {
- }
-
+ TableAssociationModel model = (TableAssociationModel) table.getModel();
+ model.removeColumnAssociation(this);
+
+ super.breakConnection();
+ }
+
+ protected void addAsListener() {
+ }
+
+ protected void removeAsListener() {
+ }
+
/**
- * Returns the value to be displayed at the specified index.
- * This method is called by the TableAssocation to populate
- * the table model.
- * This implementation simply retrieves the value from the
- * display group bound to the value aspect.
- */
- public Object valueAtIndex( int aRowIndex )
- {
- if ( valueDisplayGroup != null )
- {
- return valueDisplayGroup.valueForObjectAtIndex(
- aRowIndex, valueKey );
+ * Returns the value to be displayed at the specified index. This method is
+ * called by the TableAssocation to populate the table model. This
+ * implementation simply retrieves the value from the display group bound to the
+ * value aspect.
+ */
+ public Object valueAtIndex(int aRowIndex) {
+ if (valueDisplayGroup != null) {
+ return valueDisplayGroup.valueForObjectAtIndex(aRowIndex, valueKey);
}
return null;
}
-
+
/**
- * Sets a value for the specified index. This method is
- * called by the TableAssocation after a cell has been
- * edited.
- * This implementation simply sets the value in the
- * display group bound to the value aspect.
- */
- public void setValueAtIndex( Object aValue, int aRowIndex )
- {
- if ( valueDisplayGroup != null )
- {
- valueDisplayGroup.setValueForObjectAtIndex(
- aValue, aRowIndex, valueKey );
+ * Sets a value for the specified index. This method is called by the
+ * TableAssocation after a cell has been edited. This implementation simply sets
+ * the value in the display group bound to the value aspect.
+ */
+ public void setValueAtIndex(Object aValue, int aRowIndex) {
+ if (valueDisplayGroup != null) {
+ valueDisplayGroup.setValueForObjectAtIndex(aValue, aRowIndex, valueKey);
}
}
-
- /**
- * Returns whether this column should be sorted when the
- * user clicks on the column header. Defaults to true.
- */
- public boolean isSortable()
- {
- return sortable;
- }
-
- /**
- * Sets whether this column should be sorted when the
- * user clicks on the column header.
- */
- public void setSortable( boolean isSortable )
- {
- sortable = isSortable;
- }
-
- /**
- * Returns whether this column should be sorted
- * in a case sensitive manner. Defaults to true.
- */
- public boolean isSortCaseSensitive()
- {
- return sortCaseSensitive;
- }
-
- /**
- * Sets whether this column should be sorted when
- * in a case sensitive manner.
- * If false, the column contents should be string values.
- */
- public void setSortCaseSensitive( boolean isCaseSensitive )
- {
- sortCaseSensitive = isCaseSensitive;
- }
/**
- * Called by the TableAssociation to determine whether
- * the value at the specified row is editable.
- * This is determined by the binding of the Editable aspect,
- * looking at the value of the corresponding index in that
- * display group. Note: because the display group may
- * not have the same number if items, the selected index is
- * used if the editable display group is not the same as the
- * the value display group.
- */
- public boolean isEditableAtRow( int aRowIndex )
- {
- if ( editableKey == null ) return false;
+ * Returns whether this column should be sorted when the user clicks on the
+ * column header. Defaults to true.
+ */
+ public boolean isSortable() {
+ return sortable;
+ }
+
+ /**
+ * Sets whether this column should be sorted when the user clicks on the column
+ * header.
+ */
+ public void setSortable(boolean isSortable) {
+ sortable = isSortable;
+ }
+
+ /**
+ * Returns whether this column should be sorted in a case sensitive manner.
+ * Defaults to true.
+ */
+ public boolean isSortCaseSensitive() {
+ return sortCaseSensitive;
+ }
+
+ /**
+ * Sets whether this column should be sorted when in a case sensitive manner. If
+ * false, the column contents should be string values.
+ */
+ public void setSortCaseSensitive(boolean isCaseSensitive) {
+ sortCaseSensitive = isCaseSensitive;
+ }
+
+ /**
+ * Called by the TableAssociation to determine whether the value at the
+ * specified row is editable. This is determined by the binding of the Editable
+ * aspect, looking at the value of the corresponding index in that display
+ * group. Note: because the display group may not have the same number if items,
+ * the selected index is used if the editable display group is not the same as
+ * the the value display group.
+ */
+ public boolean isEditableAtRow(int aRowIndex) {
+ if (editableKey == null)
+ return false;
Object value = null;
- if ( editableDisplayGroup != null )
- {
+ if (editableDisplayGroup != null) {
// if using the same group for both, return the value for the index
- if ( editableDisplayGroup.equals( valueDisplayGroup ) )
- {
- value =
- editableDisplayGroup.valueForObjectAtIndex( aRowIndex, editableKey );
- }
- else // using an external display group to determine editability
+ if (editableDisplayGroup.equals(valueDisplayGroup)) {
+ value = editableDisplayGroup.valueForObjectAtIndex(aRowIndex, editableKey);
+ } else // using an external display group to determine editability
{
// ignore index and use the selected object value from display group
- value =
- editableDisplayGroup.selectedObjectValueForKey( editableKey );
+ value = editableDisplayGroup.selectedObjectValueForKey(editableKey);
}
- }
- else
- {
+ } else {
// treat bound key without display group as a value
- value = editableKey;
+ value = editableKey;
}
- if ( value == null ) return false; // null defaults to false
- Boolean result = (Boolean)
- ValueConverter.convertObjectToClass( value, Boolean.class );
- if ( result == null ) return true; // non-null defaults to true
+ if (value == null)
+ return false; // null defaults to false
+ Boolean result = (Boolean) ValueConverter.convertObjectToClass(value, Boolean.class);
+ if (result == null)
+ return true; // non-null defaults to true
return result.booleanValue();
}
-
- // convenience
-
- private TableColumn component()
- {
- return (TableColumn) object();
- }
-
- /**
- * Called by TableAssociation to get a EOSortOrdering suitable
- * for the information in this column.
- * This implementation returns a EOSortOrdering with the key
- * equal to the value aspect's key and the appropriate selector
- * for the specified ascending value and the case sensitivity
- * of this column.
- * Override to customize the sort for your column.
- */
- public EOSortOrdering getSortOrdering( boolean isAscending )
- {
- if ( isAscending )
- {
- if ( isSortCaseSensitive() )
- {
- return new EOSortOrdering(
- valueKey,
- EOSortOrdering.CompareAscending ) ;
- }
- else
- {
- return new EOSortOrdering(
- valueKey,
- EOSortOrdering.CompareCaseInsensitiveAscending ) ;
- }
- }
- else
- {
- if ( isSortCaseSensitive() )
- {
- return new EOSortOrdering(
- valueKey,
- EOSortOrdering.CompareDescending ) ;
- }
- else
- {
- return new EOSortOrdering(
- valueKey,
- EOSortOrdering.CompareCaseInsensitiveDescending ) ;
- }
- }
- }
-
- /**
- * Returns the one-based index of this assocation's sort ordering
- * in the specified list of orderings. If the sign of the returned
- * value is negative, the ordering is descending. If the return
- * value is zero, no matching ordering was found.
- */
- protected int getIndexOfMatchingOrdering( List orderings )
- {
- // find index of matching ordering
- int index = 0;
- EOSortOrdering ordering = null;
- Iterator i = orderings.iterator();
- while ( i.hasNext() )
- {
- index++;
- ordering = (EOSortOrdering) i.next();
- if ( ordering.key().equals( valueKey ) )
- {
- // determine ascending or descending
- if ( getSortOrdering( true ).equals( ordering ) )
- {
- return index;
- }
- else
- if ( getSortOrdering( false ).equals( ordering ) )
- {
- return -index;
- }
- }
- }
- return 0;
-
- }
-
- /**
- * Called by TableAssociation to draw some indicator in the
- * specified rectangle using the specified graphics to indicate
- * the specified sort state. The rectangle corresponds to the
- * bounds of the column header.
- * This implementation draws a small transparent gray triangle at
- * the right edge of the bounding rectangle.
- * Override to do something different or to do nothing at all.
- */
- protected void drawSortIndicator( Rectangle aBoundingRectangle,
- Graphics aGraphicsContext, List orderings )
- {
- int index = getIndexOfMatchingOrdering( orderings );
- if ( index == 0 ) return;
-
- boolean isAscending = ( index > 0 );
- index = Math.abs( index );
-
- // turn on anti-aliasing
- if ( aGraphicsContext instanceof Graphics2D )
- {
- ((Graphics2D)aGraphicsContext).setRenderingHint(
- RenderingHints.KEY_ANTIALIASING,
- RenderingHints.VALUE_ANTIALIAS_ON );
- }
-
- Rectangle r = new Rectangle( aBoundingRectangle );
-
- // resize to a right-justified square, sides equal to height
- r.setBounds( r.x + r.width - r.height, r.y, r.height, r.height );
-
- // resize to about a third smaller
- int portion = r.height / 3;
- r.grow( -portion, -portion );
-
- // transparencies cause java2d printing to rasterize,
- // resulting in excessive memory usage and print time.
- // aGraphicsContext.setColor( new Color( 0, 0, 0, 255 / (index*2) ) );
- aGraphicsContext.setColor( getSortIndicatorColor( index ) );
-
- Polygon triangle;
- if ( !isAscending )
- {
- triangle = new Polygon(
- new int[] { r.x, r.x+r.width/2, r.x+r.width },
- new int[] { r.y, r.y+r.height, r.y }, 3 );
- }
- else
- {
- triangle = new Polygon(
- new int[] { r.x, r.x+r.width/2, r.x+r.width },
- new int[] { r.y+r.height, r.y, r.y+r.height }, 3 );
- }
- aGraphicsContext.fillPolygon( triangle );
- }
-
- /**
- * Returns a color to be used by the sort indicator based on the index
- * of the sorting column. The goal of this method is to make the color
- * appear lighter and lighter, the "less" primary the sort order for this
- * column is. This can be acheives simply though a "transparent" color,
- * however, during printing of the corresponding table, java print
- * kicks into "raster" based printing when printing a component with
- * a transparent color instead of "vector" based printing. Raster
- * based printing can take up to 20-30 times longer to print than
- * vector printing and consume several times the amount of memory.
- * Raster-based printing should be avoided at all costs if the a component
- * is to be printed (as of Java 1.3.1).
- * @param index The "sort" index of the associated table column. The higher
- * the index, the lighter the color will be. An index of 0 will
- * return null.
- * @return The color to use when rendering the sort indicator.
- */
- protected static Color getSortIndicatorColor( int index )
- {
- if ( index == 0 ) return null;
-
- // Create the color list if not already created.
- if ( sortIndicatorColorList == null )
- {
- // Default size to 13 elements, it would be extremely rare that a
- // user sorts more than 12 columns at a time (although possible).
- // (Index 0 is not used.)
- sortIndicatorColorList = new Color[ 13 ];
- }
-
- // Get the color out of the color list. Use the index directly as
- // an index into an ordered list. If the color has already been
- // created for that index, then return it, otherwise create the color.
- if ( ( index < sortIndicatorColorList.length ) &&
- ( sortIndicatorColorList[ index ] != null ) )
- {
- return sortIndicatorColorList[ index ];
- }
-
- // The following logic performs the same affect as the above
- // transparent color, without actually using a transparent color.
- // Start with the table header's background color and derive a color
- // that is "darker" than that color. Any color this logic creates will
- // be between those two colors.
- Color lightColor = java.awt.SystemColor.control;
- Color darkColor = lightColor.darker().darker();
-
- // Make the light color (the upper bound) a little darker, so that even
- // the lightest triangle will still be slightly visible.
- lightColor = new Color(
- Math.max( ( int )( lightColor.getRed() * 0.9), 0 ),
- Math.max( ( int )( lightColor.getGreen() * 0.9), 0 ),
- Math.max( ( int )( lightColor.getBlue() * 0.9), 0) );
-
- // Subtract the light color from the dark color. This is the range
- // between the two colors.
- Color difference = new Color( lightColor.getRed() - darkColor.getRed(),
- lightColor.getGreen() - darkColor.getGreen(),
- lightColor.getBlue() - darkColor.getBlue() );
-
- // If the index is 1, user the dark color as is. Otherwise scale the
- // color closer and closer to the lighter color as the index gets
- // biggger and bigger.
- if ( index > 1 )
- {
- float factor = ( float )Math.pow( 0.5, ( index - 1 ) );
- darkColor = new Color(
- Math.max( lightColor.getRed() - ( int )( difference.getRed() * factor ), 0 ),
- Math.max( lightColor.getGreen() - ( int )( difference.getGreen() * factor ), 0 ),
- Math.max( lightColor.getBlue() - ( int )( difference.getBlue() * factor ), 0 ) );
- }
-
- // Cache the created color in the color list for this index.
- if ( index >= sortIndicatorColorList.length )
- {
- // The color list is too small, create a new larger list with
- // some padding for even larger indicies.
- Color[] oldList = sortIndicatorColorList;
- sortIndicatorColorList = new Color[ index + 5 ];
- System.arraycopy( oldList, 0, sortIndicatorColorList, 0, oldList.length );
- }
- sortIndicatorColorList[ index ] = darkColor;
-
- return darkColor;
- }
+
+ // convenience
+
+ private TableColumn component() {
+ return (TableColumn) object();
+ }
+
+ /**
+ * Called by TableAssociation to get a EOSortOrdering suitable for the
+ * information in this column. This implementation returns a EOSortOrdering with
+ * the key equal to the value aspect's key and the appropriate selector for the
+ * specified ascending value and the case sensitivity of this column. Override
+ * to customize the sort for your column.
+ */
+ public EOSortOrdering getSortOrdering(boolean isAscending) {
+ if (isAscending) {
+ if (isSortCaseSensitive()) {
+ return new EOSortOrdering(valueKey, EOSortOrdering.CompareAscending);
+ } else {
+ return new EOSortOrdering(valueKey, EOSortOrdering.CompareCaseInsensitiveAscending);
+ }
+ } else {
+ if (isSortCaseSensitive()) {
+ return new EOSortOrdering(valueKey, EOSortOrdering.CompareDescending);
+ } else {
+ return new EOSortOrdering(valueKey, EOSortOrdering.CompareCaseInsensitiveDescending);
+ }
+ }
+ }
+
+ /**
+ * Returns the one-based index of this assocation's sort ordering in the
+ * specified list of orderings. If the sign of the returned value is negative,
+ * the ordering is descending. If the return value is zero, no matching ordering
+ * was found.
+ */
+ protected int getIndexOfMatchingOrdering(List orderings) {
+ // find index of matching ordering
+ int index = 0;
+ EOSortOrdering ordering = null;
+ Iterator i = orderings.iterator();
+ while (i.hasNext()) {
+ index++;
+ ordering = (EOSortOrdering) i.next();
+ if (ordering.key().equals(valueKey)) {
+ // determine ascending or descending
+ if (getSortOrdering(true).equals(ordering)) {
+ return index;
+ } else if (getSortOrdering(false).equals(ordering)) {
+ return -index;
+ }
+ }
+ }
+ return 0;
+
+ }
+
+ /**
+ * Called by TableAssociation to draw some indicator in the specified rectangle
+ * using the specified graphics to indicate the specified sort state. The
+ * rectangle corresponds to the bounds of the column header. This implementation
+ * draws a small transparent gray triangle at the right edge of the bounding
+ * rectangle. Override to do something different or to do nothing at all.
+ */
+ protected void drawSortIndicator(Rectangle aBoundingRectangle, Graphics aGraphicsContext, List orderings) {
+ int index = getIndexOfMatchingOrdering(orderings);
+ if (index == 0)
+ return;
+
+ boolean isAscending = (index > 0);
+ index = Math.abs(index);
+
+ // turn on anti-aliasing
+ if (aGraphicsContext instanceof Graphics2D) {
+ ((Graphics2D) aGraphicsContext).setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+ RenderingHints.VALUE_ANTIALIAS_ON);
+ }
+
+ Rectangle r = new Rectangle(aBoundingRectangle);
+
+ // resize to a right-justified square, sides equal to height
+ r.setBounds(r.x + r.width - r.height, r.y, r.height, r.height);
+
+ // resize to about a third smaller
+ int portion = r.height / 3;
+ r.grow(-portion, -portion);
+
+ // transparencies cause java2d printing to rasterize,
+ // resulting in excessive memory usage and print time.
+ // aGraphicsContext.setColor( new Color( 0, 0, 0, 255 / (index*2) ) );
+ aGraphicsContext.setColor(getSortIndicatorColor(index));
+
+ Polygon triangle;
+ if (!isAscending) {
+ triangle = new Polygon(new int[] { r.x, r.x + r.width / 2, r.x + r.width },
+ new int[] { r.y, r.y + r.height, r.y }, 3);
+ } else {
+ triangle = new Polygon(new int[] { r.x, r.x + r.width / 2, r.x + r.width },
+ new int[] { r.y + r.height, r.y, r.y + r.height }, 3);
+ }
+ aGraphicsContext.fillPolygon(triangle);
+ }
+
+ /**
+ * Returns a color to be used by the sort indicator based on the index of the
+ * sorting column. The goal of this method is to make the color appear lighter
+ * and lighter, the "less" primary the sort order for this column is. This can
+ * be acheives simply though a "transparent" color, however, during printing of
+ * the corresponding table, java print kicks into "raster" based printing when
+ * printing a component with a transparent color instead of "vector" based
+ * printing. Raster based printing can take up to 20-30 times longer to print
+ * than vector printing and consume several times the amount of memory.
+ * Raster-based printing should be avoided at all costs if the a component is to
+ * be printed (as of Java 1.3.1).
+ *
+ * @param index The "sort" index of the associated table column. The higher the
+ * index, the lighter the color will be. An index of 0 will return
+ * null.
+ * @return The color to use when rendering the sort indicator.
+ */
+ protected static Color getSortIndicatorColor(int index) {
+ if (index == 0)
+ return null;
+
+ // Create the color list if not already created.
+ if (sortIndicatorColorList == null) {
+ // Default size to 13 elements, it would be extremely rare that a
+ // user sorts more than 12 columns at a time (although possible).
+ // (Index 0 is not used.)
+ sortIndicatorColorList = new Color[13];
+ }
+
+ // Get the color out of the color list. Use the index directly as
+ // an index into an ordered list. If the color has already been
+ // created for that index, then return it, otherwise create the color.
+ if ((index < sortIndicatorColorList.length) && (sortIndicatorColorList[index] != null)) {
+ return sortIndicatorColorList[index];
+ }
+
+ // The following logic performs the same affect as the above
+ // transparent color, without actually using a transparent color.
+ // Start with the table header's background color and derive a color
+ // that is "darker" than that color. Any color this logic creates will
+ // be between those two colors.
+ Color lightColor = java.awt.SystemColor.control;
+ Color darkColor = lightColor.darker().darker();
+
+ // Make the light color (the upper bound) a little darker, so that even
+ // the lightest triangle will still be slightly visible.
+ lightColor = new Color(Math.max((int) (lightColor.getRed() * 0.9), 0),
+ Math.max((int) (lightColor.getGreen() * 0.9), 0), Math.max((int) (lightColor.getBlue() * 0.9), 0));
+
+ // Subtract the light color from the dark color. This is the range
+ // between the two colors.
+ Color difference = new Color(lightColor.getRed() - darkColor.getRed(),
+ lightColor.getGreen() - darkColor.getGreen(), lightColor.getBlue() - darkColor.getBlue());
+
+ // If the index is 1, user the dark color as is. Otherwise scale the
+ // color closer and closer to the lighter color as the index gets
+ // biggger and bigger.
+ if (index > 1) {
+ float factor = (float) Math.pow(0.5, (index - 1));
+ darkColor = new Color(Math.max(lightColor.getRed() - (int) (difference.getRed() * factor), 0),
+ Math.max(lightColor.getGreen() - (int) (difference.getGreen() * factor), 0),
+ Math.max(lightColor.getBlue() - (int) (difference.getBlue() * factor), 0));
+ }
+
+ // Cache the created color in the color list for this index.
+ if (index >= sortIndicatorColorList.length) {
+ // The color list is too small, create a new larger list with
+ // some padding for even larger indicies.
+ Color[] oldList = sortIndicatorColorList;
+ sortIndicatorColorList = new Color[index + 5];
+ System.arraycopy(oldList, 0, sortIndicatorColorList, 0, oldList.length);
+ }
+ sortIndicatorColorList[index] = darkColor;
+
+ return darkColor;
+ }
}
/*
- * $Log$
- * Revision 1.2 2006/02/18 23:19:05 cgruber
- * Update imports and maven dependencies.
+ * $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.1 2006/02/16 13:22:22 cgruber Check in all sources in
+ * eclipse-friendly maven-enabled packages.
*
- * Revision 1.16 2003/08/06 23:07:52 chochos
- * general code cleanup (mostly, removing unused imports)
+ * Revision 1.16 2003/08/06 23:07:52 chochos general code cleanup (mostly,
+ * removing unused imports)
*
- * Revision 1.15 2002/08/22 15:42:49 mpowers
- * No longer using transparency to render sort indicator (see comments).
+ * Revision 1.15 2002/08/22 15:42:49 mpowers No longer using transparency to
+ * render sort indicator (see comments).
*
- * Revision 1.14 2002/04/12 21:05:57 mpowers
- * Now distinguishing changes in titles group even better.
+ * Revision 1.14 2002/04/12 21:05:57 mpowers Now distinguishing changes in
+ * titles group even better.
*
- * Revision 1.13 2002/03/05 23:18:28 mpowers
- * Added documentation.
- * Added isSelectionPaintedImmediate and isSelectionTracking attributes
- * to TableAssociation.
- * Added getTableAssociation to TableColumnAssociation.
+ * Revision 1.13 2002/03/05 23:18:28 mpowers Added documentation. Added
+ * isSelectionPaintedImmediate and isSelectionTracking attributes to
+ * TableAssociation. Added getTableAssociation to TableColumnAssociation.
*
- * Revision 1.12 2002/03/04 22:11:43 mpowers
- * Darkened the sort indicator to better differentiate the first sort.
+ * Revision 1.12 2002/03/04 22:11:43 mpowers Darkened the sort indicator to
+ * better differentiate the first sort.
*
- * Revision 1.11 2002/03/04 03:58:17 mpowers
- * Refined table header click behavior.
+ * Revision 1.11 2002/03/04 03:58:17 mpowers Refined table header click
+ * behavior.
*
- * Revision 1.10 2002/03/01 15:42:00 mpowers
- * Table column headers now always show their sort indicator.
- * A third table-column click clears the sort for that column.
+ * Revision 1.10 2002/03/01 15:42:00 mpowers Table column headers now always
+ * show their sort indicator. A third table-column click clears the sort for
+ * that column.
*
- * Revision 1.9 2002/02/28 23:01:39 mpowers
- * TableColumnAssociations add and remove themselves from the TableAssociation
- * when their connection is established and broken respectively.
- * TableAssociations now break connection if they have no column associations.
+ * Revision 1.9 2002/02/28 23:01:39 mpowers TableColumnAssociations add and
+ * remove themselves from the TableAssociation when their connection is
+ * established and broken respectively. TableAssociations now break connection
+ * if they have no column associations.
*
- * Revision 1.8 2001/06/05 16:03:56 mpowers
- * Flipped the triangle to be consistent with Aqua.
+ * Revision 1.8 2001/06/05 16:03:56 mpowers Flipped the triangle to be
+ * consistent with Aqua.
*
- * Revision 1.7 2001/03/09 22:09:22 mpowers
- * Now better handling jdk1.1 for rendering the column header.
+ * Revision 1.7 2001/03/09 22:09:22 mpowers Now better handling jdk1.1 for
+ * rendering the column header.
*
- * Revision 1.6 2001/02/17 16:52:05 mpowers
- * Changes in imports to support building with jdk1.1 collections.
+ * Revision 1.6 2001/02/17 16:52:05 mpowers Changes in imports to support
+ * building with jdk1.1 collections.
*
- * Revision 1.5 2001/01/12 19:11:56 mpowers
- * Fixed table column click sorting.
+ * Revision 1.5 2001/01/12 19:11:56 mpowers Fixed table column click sorting.
*
- * Revision 1.4 2001/01/12 17:20:30 mpowers
- * Moved EOSortOrdering creation to ColumnAssociation.
+ * Revision 1.4 2001/01/12 17:20:30 mpowers Moved EOSortOrdering creation to
+ * ColumnAssociation.
*
- * Revision 1.3 2001/01/11 21:55:57 mpowers
- * Implemented sort indicator for table column headers.
+ * Revision 1.3 2001/01/11 21:55:57 mpowers Implemented sort indicator for table
+ * column headers.
*
- * Revision 1.2 2001/01/11 20:34:26 mpowers
- * Implemented EOSortOrdering and added support in framework.
- * Added header-click to sort table columns.
+ * Revision 1.2 2001/01/11 20:34:26 mpowers Implemented EOSortOrdering and added
+ * support in framework. Added header-click to sort table columns.
*
- * Revision 1.1.1.1 2000/12/21 15:49:03 mpowers
- * Contributing wotonomy.
+ * Revision 1.1.1.1 2000/12/21 15:49:03 mpowers Contributing wotonomy.
*
- * Revision 1.5 2000/12/20 16:25:41 michael
- * Added log to all files.
+ * Revision 1.5 2000/12/20 16:25:41 michael Added log to all files.
*
*
*/
-
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/TextAssociation.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/TextAssociation.java
index 6aa27c3..ea2b47a 100644
--- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/TextAssociation.java
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/TextAssociation.java
@@ -54,1159 +54,923 @@ import net.wotonomy.ui.EOAssociation;
import net.wotonomy.ui.EODisplayGroup;
/**
-* TextAssociation binds JTextComponents and other objects
-* with getText() and setText() methods to a display group.
-* Note that JLabels are supported with both the Text and
-* Icon aspects.
-* Bindings are:
-* <ul>
-* <li>value: a property convertable to/from a string</li>
-* <li>editable: a boolean property that determines whether
-* the user can edit the text in the field</li>
-* <li>enabled: a boolean property that determines whether
-* the user can select the text in the field</li>
-* <li>visible: a boolean property that determines whether
-* the field is visible</li>
-* <li>label: a boolean property that determines whether
-* field should appear as a read-only, selectable label</li>
-* <li>icon: a property that returns a Swing icon, for use
-* with JLabels and other components with setIcon() methods.
-* If bound to a static string, the string will be used to
-* load an image resource from the selected object's class.</li>
-* </ul>
-*
-* @author michael@mpowers.net
-* @author $Author: cgruber $
-* @version $Revision: 904 $
-*/
-public class TextAssociation extends EOAssociation
- implements FocusListener, ActionListener, DocumentListener
-{
- static final NSArray aspects =
- new NSArray( new Object[] {
- ValueAspect, EnabledAspect, EditableAspect, VisibleAspect, LabelAspect, IconAspect
- } );
- static final NSArray aspectSignatures =
- new NSArray( new Object[] {
- AttributeToOneAspectSignature,
- AttributeToOneAspectSignature,
- AttributeToOneAspectSignature,
- AttributeToOneAspectSignature,
- AttributeToOneAspectSignature,
- AttributeToOneAspectSignature
- } );
- static final NSArray objectKeysTaken =
- new NSArray( new Object[] {
- "text", "enabled", "editable", "visible"
- } );
-
- private final static NSSelector getText =
- new NSSelector( "getText" );
- private final static NSSelector setText =
- new NSSelector( "setText",
- new Class[] { String.class } );
- private final static NSSelector getDocument =
- new NSSelector( "getDocument" );
- private final static NSSelector setIcon =
- new NSSelector( "setIcon",
- new Class[] { Icon.class } );
- private final static NSSelector addActionListener =
- new NSSelector( "addActionListener",
- new Class[] { ActionListener.class } );
- private final static NSSelector removeActionListener =
- new NSSelector( "removeActionListener",
- new Class[] { ActionListener.class } );
- private final static NSSelector addFocusListener =
- new NSSelector( "addFocusListener",
- new Class[] { FocusListener.class } );
- private final static NSSelector removeFocusListener =
- new NSSelector( "removeFocusListener",
- new Class[] { FocusListener.class } );
-
- // null handling
- protected boolean wasNull;
- protected static final String EMPTY_STRING = "";
-
- // dirty handling
- protected boolean needsUpdate;
- protected boolean hasDocument;
- protected boolean isListening;
-
- // formatting
- protected Format format;
-
- // on-the-fly validation
- protected boolean activeUpdate;
-
- // type conversion
- protected Class lastKnownType;
-
- // cache the value aspect
- private EODisplayGroup valueDisplayGroup;
- private String valueKey;
-
- // hacky flags needed for no activeUpdate
- private boolean pleaseIgnoreNextChange = false;
- private boolean pleaseAcceptNextChange = false;
- private boolean externallyChanged = true;
-
-
- /**
- * Constructor specifying the object to be controlled by this
- * association. Does not establish connection.
- */
- public TextAssociation ( Object anObject )
- {
- super( anObject );
- wasNull = false;
- needsUpdate = false;
- activeUpdate = true;
- hasDocument = false;
- isListening = true;
- valueDisplayGroup = null;
- valueKey = null;
- format = null;
- lastKnownType = null;
-
- // register for idle notifications
- NSSelector handleNotification =
- new NSSelector( "handleNotification",
- new Class[] { NSNotification.class } );
- NSNotificationCenter.defaultCenter().addObserver(
- this, handleNotification, null, this );
- }
-
- /**
- * 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. <ul>
- * <li>"A" attribute: the aspect can be bound to
- * an attribute.</li>
- * <li>"1" to-one: the aspect can be bound to a
- * property that returns a single object.</li>
- * <li>"M" to-one: the aspect can be bound to a
- * property that returns multiple objects.</li>
- * </ul>
- * 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 setText.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. This is typically "value" or somesuch.
- */
- public static String primaryAspect ()
- {
- return ValueAspect;
- }
-
- /**
- * 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 ) );
- }
-
- /**
- * Binds the specified aspect of this association to the
- * specified key on the specified display group.
- */
- public void bindAspect (
- String anAspect, EODisplayGroup aDisplayGroup, String aKey )
- {
- if ( ValueAspect.equals( anAspect ) )
- {
- valueDisplayGroup = aDisplayGroup;
- valueKey = aKey;
- }
- super.bindAspect( anAspect, aDisplayGroup, aKey );
- }
-
- /**
- * Establishes a connection between this association
- * and the controlled object. This implementation
- * attempts to add this class as an ActionListener
- * and as a FocusListener to the specified object.
- */
- public void establishConnection ()
- {
- Object component = object();
- try
- {
- if ( addActionListener.implementedByObject( component ) )
- {
- addActionListener.invoke( component, this );
- }
- if ( addFocusListener.implementedByObject( component ) )
- {
- addFocusListener.invoke( component, this );
- }
- hasDocument = false;
- if ( getDocument.implementedByObject( component ) )
- {
- Object document = getDocument.invoke( component );
- if ( document instanceof Document )
- {
- ((Document)document).addDocumentListener( this );
- hasDocument = true;
- }
- }
- }
- catch ( Exception exc )
- {
- throw new WotonomyException(
- "Error while establishing connection", exc );
- }
-
- super.establishConnection();
-
- // forces update from bindings
- subjectChanged();
- }
-
- /**
- * Breaks the connection between this association and
- * its object. Override to stop listening for events
- * from the object.
- */
- public void breakConnection ()
- {
- Object component = object();
- try
- {
- if ( removeActionListener.implementedByObject( component ) )
- {
- removeActionListener.invoke( component, this );
- }
- if ( removeFocusListener.implementedByObject( component ) )
- {
- removeFocusListener.invoke( component, this );
- }
- if ( getDocument.implementedByObject( component ) )
- {
- Object document = getDocument.invoke( component );
- if ( document instanceof Document )
- {
- ((Document)document).removeDocumentListener( this );
- }
- }
- }
- catch ( Exception exc )
- {
- throw new WotonomyException(
- "Error while breaking connection", exc );
- }
- super.breakConnection();
- }
-
- public void objectWillChange( Object anObject )
- {
- super.objectWillChange( anObject );
- externallyChanged = true;
- }
-
- /**
- * Called when either the selection or the contents
- * of an associated display group have changed.
- */
- public void subjectChanged()
- {
- if ( pleaseIgnoreNextChange )
- {
- pleaseIgnoreNextChange = false;
- externallyChanged = false;
- return;
- }
-
- externallyChanged = true;
-
- Object component = object();
- EODisplayGroup displayGroup;
- String key;
- Object value;
-
- // value aspect
- displayGroup = valueDisplayGroup;
- if ( displayGroup != null )
- {
- if ( component instanceof Component )
- {
- ((Component)component).setEnabled(
- displayGroup.enabledToSetSelectedObjectValueForKey( valueKey ) );
- }
-
- // if activeUpdate or we are not the editing association
- if ( activeUpdate || displayGroup.editingAssociation() != this || pleaseAcceptNextChange )
- {
- pleaseAcceptNextChange = false;
- key = valueKey;
-
- if ( displayGroup.selectedObjects().size() > 1 )
- {
- // if there're more than one object selected, set
- // the value to blank for all of them.
- Object previousValue;
-
- Iterator indexIterator = displayGroup.selectionIndexes().
- iterator();
-
- // get value for the first selected object.
- int initialIndex = ( (Integer)indexIterator.next() ).intValue();
- previousValue = displayGroup.valueForObjectAtIndex(
- initialIndex, key );
- value = previousValue;
-
- // go through the rest of the selected objects, compare each
- // value with the previous one. continue comparing if two
- // values are equal, break the while loop if they're different.
- // the final value will be the common value of all selected objects
- // if there is one, or be blank if there is not.
- while ( indexIterator.hasNext() )
- {
- int index = ( (Integer)indexIterator.next() ).intValue();
- Object currentValue = displayGroup.valueForObjectAtIndex(
- index, key );
- if ( currentValue != null && previousValue != null
- && !currentValue.toString().equals( previousValue.toString() ) ) {
- value = null;
- break;
- }
-
- } // end while
-
- } else {
-
- // if there's only one object selected.
- value = displayGroup.selectedObjectValueForKey( key );
- } // end checking the size of selected objects in displayGroup
-
- // null handling
- if ( value == null )
- {
- wasNull = true;
- value = EMPTY_STRING;
- lastKnownType = null;
- }
- else
- {
- wasNull = false;
- lastKnownType = value.getClass();
- if ( format() != null )
- {
- try
- {
- value = format().format( value );
- }
- catch ( IllegalArgumentException exc )
- {
- value = value.toString();
- }
- }
- }
-
-
- try
- {
- if ( needToReadValueFromDisplayGroup( value.toString(), getText ) )
- {
- // No need to listen for any events that might get fired
- // while setting the text since we are the one setting it.
- boolean wasListening = isListening;
- isListening = false;
-
- // setText is an expensive operation
- setText.invoke( component, value.toString() );
-
- isListening = wasListening;
- needsUpdate = false;
- }
- }
- catch ( Exception exc )
- {
- throw new WotonomyException(
- "Error while updating component connection", exc );
- }
- }
- }
-
- // icon aspect
- displayGroup = displayGroupForAspect( IconAspect );
- key = displayGroupKeyForAspect( IconAspect );
- if ( key != null )
- {
- if ( displayGroup != null )
- {
- value =
- displayGroup.selectedObjectValueForKey( key );
- }
- else
- {
- // treat bound key without display group
- // as a resource to be loaded from the selected class.
- value = null;
- Object o = displayGroup.selectedObject();
- if ( o != null )
- {
- URL url = o.getClass().getResource( key );
- if ( url != null )
- {
- value = new ImageIcon( url );
- }
- }
- }
-
- try
- {
- setIcon.invoke( component, value );
- }
- catch ( Exception exc )
- {
- throw new WotonomyException(
- "Error while updating component connection", exc );
- }
- }
-
- // enabled aspect
- displayGroup = displayGroupForAspect( EnabledAspect );
- key = displayGroupKeyForAspect( EnabledAspect );
- if ( ( key != null )
- && ( component instanceof Component ) )
- {
- if ( displayGroup != null )
- {
- value =
- displayGroup.selectedObjectValueForKey( key );
- }
- else
- {
- // treat bound key without display group as a value
- value = key;
- }
- Boolean converted = null;
- if ( value != null )
- {
- converted = (Boolean)
- ValueConverter.convertObjectToClass(
- value, Boolean.class );
- }
- if ( converted == null ) converted = Boolean.FALSE;
- if ( ((Component)component).isEnabled() != converted.booleanValue() )
- {
- ((Component)component).setEnabled( converted.booleanValue() );
- }
- }
-
- // editable aspect
- displayGroup = displayGroupForAspect( EditableAspect );
- key = displayGroupKeyForAspect( EditableAspect );
- if ( ( key != null )
- && ( component instanceof JTextComponent ) )
- {
- if ( displayGroup != null )
- {
- value =
- displayGroup.selectedObjectValueForKey( key );
- }
- else
- {
- // treat bound key without display group as a value
- value = key;
- }
- Boolean converted = (Boolean)
- ValueConverter.convertObjectToClass(
- value, Boolean.class );
-
- if ( converted != null )
- {
- if ( converted.booleanValue() != ((JTextComponent)component).isEditable() )
- {
- ((JTextComponent)component).setEditable( converted.booleanValue() );
- }
- }
- }
-
- // visible aspect
- displayGroup = displayGroupForAspect( VisibleAspect );
- key = displayGroupKeyForAspect( VisibleAspect );
- if ( ( key != null )
- && ( component instanceof Component ) )
- {
- if ( displayGroup != null )
- {
- value =
- displayGroup.selectedObjectValueForKey( key );
- }
- else
- {
- // treat bound key without display group as a value
- value = key;
- }
- Boolean converted = (Boolean)
- ValueConverter.convertObjectToClass(
- value, Boolean.class );
-
- if ( converted != null )
- {
- if ( converted.booleanValue() != ((Component)component).isVisible() )
- {
- ((Component)component).setVisible( converted.booleanValue() );
- }
- }
- }
-
- // label aspect
- displayGroup = displayGroupForAspect( LabelAspect );
- key = displayGroupKeyForAspect( LabelAspect );
-
- if ( ( key != null )
- && ( component instanceof JTextComponent ) )
- {
- if ( displayGroup != null )
- {
- value =
- displayGroup.selectedObjectValueForKey( key );
- }
- else
- {
- // treat bound key without display group as a value
- value = key;
- }
- Boolean converted = (Boolean)
- ValueConverter.convertObjectToClass(
- value, Boolean.class );
-
- if ( converted != null )
- {
- if ( converted.booleanValue() )
- {
- if ( component instanceof JTextComponent )
- {
- if ( component instanceof JTextArea )
- {
- areaToLabel( (JTextArea) component );
- }
- else
- {
- fieldToLabel( (JTextComponent) component );
- }
- }
- }
- else
- {
- if ( component instanceof JTextComponent )
- {
- if ( component instanceof JTextArea )
- {
- labelToArea( (JTextArea) component );
- }
- else
- {
- labelToField( (JTextComponent ) component );
- }
- }
- }
- }
- }
- }
-
- private void fieldToLabel( JTextComponent aTextField )
- {
- // turn on wrapping and disable editing and highlighting
-
- aTextField.setEditable(false);
- aTextField.setOpaque(false);
-
- // Set the border, colors and font to that of a label
-
- //LookAndFeel.installBorder(aTextField, "Label.border");
- aTextField.setBorder( null );
-
- LookAndFeel.installColorsAndFont(aTextField,
- "Label.background",
- "Label.foreground",
- "Label.font");
- }
-
- private void labelToField( JTextComponent aTextField )
- {
- // turn on wrapping and disable editing and highlighting
-
- aTextField.setEditable(true);
- aTextField.setOpaque(true);
-
- // Set the border, colors and font to that of a label
-
- LookAndFeel.installBorder(aTextField, "TextField.border");
-
- LookAndFeel.installColorsAndFont(aTextField,
- "TextField.background",
- "TextField.foreground",
- "TextField.font");
- }
-
- private void areaToLabel( JTextArea aTextArea )
- {
- // turn on wrapping and disable editing and highlighting
-
- aTextArea.setLineWrap(true);
- aTextArea.setWrapStyleWord(true);
- aTextArea.setEditable(false);
-
- // Set the text area's border, colors and font to
- // that of a label
-
- //LookAndFeel.installBorder(aTextArea, "Label.border");
- aTextArea.setBorder( null );
-
- LookAndFeel.installColorsAndFont(aTextArea,
- "Label.background",
- "Label.foreground",
- "Label.font");
-
- }
-
- private void labelToArea( JTextArea aTextArea )
- {
- // turn on wrapping and disable editing and highlighting
-
- aTextArea.setEditable(true);
-
- // Set the border, colors and font to that of a label
-
- LookAndFeel.installBorder(aTextArea, "TextArea.border");
-
- LookAndFeel.installColorsAndFont(aTextArea,
- "TextArea.background",
- "TextArea.foreground",
- "TextArea.font");
- }
-
-
- /**
- * Forces this association to cause the object to
- * stop editing and validate the user's input.
- * @return false if there were problems validating,
- * or true to continue.
- */
- public boolean endEditing()
- {
- pleaseAcceptNextChange = true;
- pleaseIgnoreNextChange = false;
- return writeValueToDisplayGroup();
- }
-
- /**
- * Writes the value currently in the component
- * to the selected object in the display group
- * bound to the value aspect.
- * @return false if there were problems validating,
- * or true to continue.
- */
- protected boolean writeValueToDisplayGroup()
- {
- boolean returnValue = true;
- if ( hasDocument && !needsUpdate ) return true;
-
- EODisplayGroup displayGroup = valueDisplayGroup;
- if ( displayGroup != null )
- {
- String key = valueKey;
- Object component = object();
- Object value = null;
- try
- {
- //if ( getText.implementedByObject( component ) )
- //{
- value = getText.invoke( component );
- //}
- }
- catch ( Exception exc )
- {
- throw new WotonomyException(
- "Error updating display group", exc );
- }
-
- if ( ( wasNull ) && ( EMPTY_STRING.equals( value ) ) )
- {
- value = null;
- }
- else
- if ( format() != null )
- {
- try
- {
- value = format().parseObject( value.toString() );
- }
- catch ( ParseException exc )
- {
- String message = exc.getMessage();
- //"That format was not recognized.";
- if ( displayGroup.associationFailedToValidateValue(
- this, value.toString(), key, exc, message ) )
- {
- boolean wasListening = isListening;
- isListening = false;
- JOptionPane.showMessageDialog(
- (Component)component, message );
- isListening = wasListening;
- }
- needsUpdate = false;
- return false;
- }
- }
-
- if ( ( lastKnownType != null ) && ( value != null ) )
- {
- // convert back to last known type, if necessary/possible
- Class type = value.getClass();
- if ( ( type != null ) && ( type != lastKnownType ) )
- {
- Object converted =
- ValueConverter.convertObjectToClass(
- value, lastKnownType );
- if ( converted != null )
- {
- value = converted;
- }
- // else: not possible, ignore
- }
- }
-
- needsUpdate = false;
-
- // only update if the value is different from the one in the display group
- if ( ! needToWriteValueToDisplayGroup( value, displayGroup ) ) return true;
-
- // we might lose focus if display group displays a validation message
- boolean wasListening = isListening;
- isListening = false;
-
- Iterator selectedIterator = displayGroup.selectionIndexes().iterator();
- while ( selectedIterator.hasNext() )
- {
- int index = ( (Integer)selectedIterator.next() ).intValue();
-
- if ( displayGroup.setValueForObjectAtIndex( value, index, key ) )
- {
- needsUpdate = false;
- }
- else
- {
- needsUpdate = false;
- returnValue = false;
- }
- }
- isListening = wasListening;
-
- }
- return returnValue;
- }
-
- /**
- * Called to determine whether the display group needs to be
- * updated. This implementation reads the value from the display
- * group and only returns true if the specified value is different.
- * This is done as an optimization since writes are more expensive
- * than reads. Override to customize this behavior.
- */
- protected boolean needToWriteValueToDisplayGroup(
- Object aValue, EODisplayGroup aDisplayGroup )
- {
- Object existingValue = aDisplayGroup.selectedObjectValueForKey( valueKey );
- if ( aDisplayGroup.selectedObjects().size() == 1 )
- {
- if ( existingValue == aValue ) return false;
- if ( ( existingValue != null ) && ( existingValue.equals( aValue ) ) ) return false;
- if ( ( aValue != null ) && ( aValue.equals( existingValue ) ) ) return false;
- }
- return true;
- }
-
- /**
- * Called to determine whether the controlled component needs to be
- * updated. This implementation reads the value from the selector
- * and only returns true if the specified value is different.
- * This is done as an optimization since updating the component
- * can be an expensive operation. Override to customize this behavior.
- */
- protected boolean needToReadValueFromDisplayGroup(
- Object aValue, NSSelector aSelector )
- throws IllegalAccessException, InvocationTargetException, NoSuchMethodException
- {
- return !aValue.toString().equals( aSelector.invoke( object() ) );
- }
-
- /**
- * Sets the Format that is used to convert values from the display
- * group to and from text that is displayed in the component.
- */
- public void setFormat( Format aFormat )
- {
- format = aFormat;
- }
-
- /**
- * Gets the Format that is used to convert values from the display
- * group to and from text that is displayed in the component.
- */
- public Format format()
- {
- return format;
- }
-
- /**
- * Returns whether the text association is configured to actively
- * update the model in response to changes in the component.
- */
- public boolean isActiveUpdate()
- {
- return activeUpdate;
- }
-
- /**
- * Sets whether the text association should actively
- * update the model in response to changes in the component.
- * Default is true. False indicates that the model will be updated
- * only when the component loses focus or fires an action event.
- */
- public void setActiveUpdate( boolean isActiveUpdate )
- {
- activeUpdate = isActiveUpdate;
- }
-
- // interface ActionListener
-
- /**
- * Updates object on action performed.
- */
- public void actionPerformed( ActionEvent evt )
- {
- if ( ! isListening ) return;
- if ( needsUpdate )
- {
- pleaseAcceptNextChange = true; // needed if activeUpdate = false
- writeValueToDisplayGroup();
- }
- }
-
- // interface FocusListener
-
- /**
- * Notifies of beginning of edit.
- */
- public void focusGained(FocusEvent evt)
- {
- if ( ! isListening ) return;
-
- pleaseAcceptNextChange = true;
- externallyChanged = true;
-
- Object o;
- EODisplayGroup displayGroup;
- Enumeration e = aspects().objectEnumerator();
- while ( e.hasMoreElements() )
- {
- displayGroup =
- displayGroupForAspect( e.nextElement().toString() );
- if ( displayGroup != null )
- {
- displayGroup.associationDidBeginEditing( this );
- }
- }
- }
-
- /**
- * Updates object on focus lost and notifies of end of edit.
- */
- public void focusLost(FocusEvent evt)
- {
- if ( ! isListening ) return;
- if ( endEditing() )
- {
- Object o;
- EODisplayGroup displayGroup;
- Enumeration e = aspects().objectEnumerator();
- while ( e.hasMoreElements() )
- {
- displayGroup =
- displayGroupForAspect( e.nextElement().toString() );
- if ( displayGroup != null )
- {
- displayGroup.associationDidEndEditing( this );
- }
- }
- }
- else
- {
- // probably should notify of a validation error here,
- }
- }
-
- /**
- * Queues a notification to PostWhenIdle.
- */
- protected void queueUpdate(DocumentEvent e)
- {
- if ( e.getDocument() instanceof DefaultStyledDocument )
- {
- if ( e instanceof AbstractDocument.DefaultDocumentEvent )
- {
- int docLength = e.getDocument().getLength();
-
- if ( ( e.getType().equals( DocumentEvent.EventType.CHANGE ) ) )
- {
- if ( e.getOffset() == 0 && e.getLength() == docLength )
- {
- // ignore document events for the whole document
- // since default styled document broadcasts these
- // using invokeLater, and we've already received
- // notification about the actual style change.
- // see: DefaultStyledDocument.ChangeUpdateRunnable
- return;
- }
- }
- }
- }
-
- NSNotificationQueue.defaultQueue().enqueueNotification(
- new NSNotification( "TextAssociation.DocumentChanged", this,
- new NSDictionary( new Object[] { "event" }, new Object[] { e } ) ),
- NSNotificationQueue.PostWhenIdle );
- }
-
- /**
- * Handles idle notification.
- */
- public void handleNotification( NSNotification aNotification )
- {
- if ( activeUpdate )
- {
- writeValueToDisplayGroup();
- }
- }
-
- // interface DocumentListener
-
- public void insertUpdate(DocumentEvent e)
- {
- if ( ! isListening ) return;
- needsUpdate = true;
- queueUpdate( e );
- }
-
- public void removeUpdate(DocumentEvent e)
- {
- if ( ! isListening ) return;
- needsUpdate = true;
- queueUpdate( e );
- }
-
- public void changedUpdate(DocumentEvent e)
- {
- if ( ! isListening ) return;
- needsUpdate = true;
- queueUpdate( e );
- }
+ * TextAssociation binds JTextComponents and other objects with getText() and
+ * setText() methods to a display group. Note that JLabels are supported with
+ * both the Text and Icon aspects. Bindings are:
+ * <ul>
+ * <li>value: a property convertable to/from a string</li>
+ * <li>editable: a boolean property that determines whether the user can edit
+ * the text in the field</li>
+ * <li>enabled: a boolean property that determines whether the user can select
+ * the text in the field</li>
+ * <li>visible: a boolean property that determines whether the field is
+ * visible</li>
+ * <li>label: a boolean property that determines whether field should appear as
+ * a read-only, selectable label</li>
+ * <li>icon: a property that returns a Swing icon, for use with JLabels and
+ * other components with setIcon() methods. If bound to a static string, the
+ * string will be used to load an image resource from the selected object's
+ * class.</li>
+ * </ul>
+ *
+ * @author michael@mpowers.net
+ * @author $Author: cgruber $
+ * @version $Revision: 904 $
+ */
+public class TextAssociation extends EOAssociation implements FocusListener, ActionListener, DocumentListener {
+ static final NSArray aspects = new NSArray(
+ new Object[] { ValueAspect, EnabledAspect, EditableAspect, VisibleAspect, LabelAspect, IconAspect });
+ static final NSArray aspectSignatures = new NSArray(
+ new Object[] { AttributeToOneAspectSignature, AttributeToOneAspectSignature, AttributeToOneAspectSignature,
+ AttributeToOneAspectSignature, AttributeToOneAspectSignature, AttributeToOneAspectSignature });
+ static final NSArray objectKeysTaken = new NSArray(new Object[] { "text", "enabled", "editable", "visible" });
+
+ private final static NSSelector getText = new NSSelector("getText");
+ private final static NSSelector setText = new NSSelector("setText", new Class[] { String.class });
+ private final static NSSelector getDocument = new NSSelector("getDocument");
+ private final static NSSelector setIcon = new NSSelector("setIcon", new Class[] { Icon.class });
+ private final static NSSelector addActionListener = new NSSelector("addActionListener",
+ new Class[] { ActionListener.class });
+ private final static NSSelector removeActionListener = new NSSelector("removeActionListener",
+ new Class[] { ActionListener.class });
+ private final static NSSelector addFocusListener = new NSSelector("addFocusListener",
+ new Class[] { FocusListener.class });
+ private final static NSSelector removeFocusListener = new NSSelector("removeFocusListener",
+ new Class[] { FocusListener.class });
+
+ // null handling
+ protected boolean wasNull;
+ protected static final String EMPTY_STRING = "";
+
+ // dirty handling
+ protected boolean needsUpdate;
+ protected boolean hasDocument;
+ protected boolean isListening;
+
+ // formatting
+ protected Format format;
+
+ // on-the-fly validation
+ protected boolean activeUpdate;
+
+ // type conversion
+ protected Class lastKnownType;
+
+ // cache the value aspect
+ private EODisplayGroup valueDisplayGroup;
+ private String valueKey;
+
+ // hacky flags needed for no activeUpdate
+ private boolean pleaseIgnoreNextChange = false;
+ private boolean pleaseAcceptNextChange = false;
+ private boolean externallyChanged = true;
+
+ /**
+ * Constructor specifying the object to be controlled by this association. Does
+ * not establish connection.
+ */
+ public TextAssociation(Object anObject) {
+ super(anObject);
+ wasNull = false;
+ needsUpdate = false;
+ activeUpdate = true;
+ hasDocument = false;
+ isListening = true;
+ valueDisplayGroup = null;
+ valueKey = null;
+ format = null;
+ lastKnownType = null;
+
+ // register for idle notifications
+ NSSelector handleNotification = new NSSelector("handleNotification", new Class[] { NSNotification.class });
+ NSNotificationCenter.defaultCenter().addObserver(this, handleNotification, null, this);
+ }
+
+ /**
+ * 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.
+ * <ul>
+ * <li>"A" attribute: the aspect can be bound to an attribute.</li>
+ * <li>"1" to-one: the aspect can be bound to a property that returns a single
+ * object.</li>
+ * <li>"M" to-one: the aspect can be bound to a property that returns multiple
+ * objects.</li>
+ * </ul>
+ * 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 setText.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. This is typically
+ * "value" or somesuch.
+ */
+ public static String primaryAspect() {
+ return ValueAspect;
+ }
+
+ /**
+ * 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));
+ }
+
+ /**
+ * Binds the specified aspect of this association to the specified key on the
+ * specified display group.
+ */
+ public void bindAspect(String anAspect, EODisplayGroup aDisplayGroup, String aKey) {
+ if (ValueAspect.equals(anAspect)) {
+ valueDisplayGroup = aDisplayGroup;
+ valueKey = aKey;
+ }
+ super.bindAspect(anAspect, aDisplayGroup, aKey);
+ }
+
+ /**
+ * Establishes a connection between this association and the controlled object.
+ * This implementation attempts to add this class as an ActionListener and as a
+ * FocusListener to the specified object.
+ */
+ public void establishConnection() {
+ Object component = object();
+ try {
+ if (addActionListener.implementedByObject(component)) {
+ addActionListener.invoke(component, this);
+ }
+ if (addFocusListener.implementedByObject(component)) {
+ addFocusListener.invoke(component, this);
+ }
+ hasDocument = false;
+ if (getDocument.implementedByObject(component)) {
+ Object document = getDocument.invoke(component);
+ if (document instanceof Document) {
+ ((Document) document).addDocumentListener(this);
+ hasDocument = true;
+ }
+ }
+ } catch (Exception exc) {
+ throw new WotonomyException("Error while establishing connection", exc);
+ }
+
+ super.establishConnection();
+
+ // forces update from bindings
+ subjectChanged();
+ }
+
+ /**
+ * Breaks the connection between this association and its object. Override to
+ * stop listening for events from the object.
+ */
+ public void breakConnection() {
+ Object component = object();
+ try {
+ if (removeActionListener.implementedByObject(component)) {
+ removeActionListener.invoke(component, this);
+ }
+ if (removeFocusListener.implementedByObject(component)) {
+ removeFocusListener.invoke(component, this);
+ }
+ if (getDocument.implementedByObject(component)) {
+ Object document = getDocument.invoke(component);
+ if (document instanceof Document) {
+ ((Document) document).removeDocumentListener(this);
+ }
+ }
+ } catch (Exception exc) {
+ throw new WotonomyException("Error while breaking connection", exc);
+ }
+ super.breakConnection();
+ }
+
+ public void objectWillChange(Object anObject) {
+ super.objectWillChange(anObject);
+ externallyChanged = true;
+ }
+
+ /**
+ * Called when either the selection or the contents of an associated display
+ * group have changed.
+ */
+ public void subjectChanged() {
+ if (pleaseIgnoreNextChange) {
+ pleaseIgnoreNextChange = false;
+ externallyChanged = false;
+ return;
+ }
+
+ externallyChanged = true;
+
+ Object component = object();
+ EODisplayGroup displayGroup;
+ String key;
+ Object value;
+
+ // value aspect
+ displayGroup = valueDisplayGroup;
+ if (displayGroup != null) {
+ if (component instanceof Component) {
+ ((Component) component).setEnabled(displayGroup.enabledToSetSelectedObjectValueForKey(valueKey));
+ }
+
+ // if activeUpdate or we are not the editing association
+ if (activeUpdate || displayGroup.editingAssociation() != this || pleaseAcceptNextChange) {
+ pleaseAcceptNextChange = false;
+ key = valueKey;
+
+ if (displayGroup.selectedObjects().size() > 1) {
+ // if there're more than one object selected, set
+ // the value to blank for all of them.
+ Object previousValue;
+
+ Iterator indexIterator = displayGroup.selectionIndexes().iterator();
+
+ // get value for the first selected object.
+ int initialIndex = ((Integer) indexIterator.next()).intValue();
+ previousValue = displayGroup.valueForObjectAtIndex(initialIndex, key);
+ value = previousValue;
+
+ // go through the rest of the selected objects, compare each
+ // value with the previous one. continue comparing if two
+ // values are equal, break the while loop if they're different.
+ // the final value will be the common value of all selected objects
+ // if there is one, or be blank if there is not.
+ while (indexIterator.hasNext()) {
+ int index = ((Integer) indexIterator.next()).intValue();
+ Object currentValue = displayGroup.valueForObjectAtIndex(index, key);
+ if (currentValue != null && previousValue != null
+ && !currentValue.toString().equals(previousValue.toString())) {
+ value = null;
+ break;
+ }
+
+ } // end while
+
+ } else {
+
+ // if there's only one object selected.
+ value = displayGroup.selectedObjectValueForKey(key);
+ } // end checking the size of selected objects in displayGroup
+
+ // null handling
+ if (value == null) {
+ wasNull = true;
+ value = EMPTY_STRING;
+ lastKnownType = null;
+ } else {
+ wasNull = false;
+ lastKnownType = value.getClass();
+ if (format() != null) {
+ try {
+ value = format().format(value);
+ } catch (IllegalArgumentException exc) {
+ value = value.toString();
+ }
+ }
+ }
+
+ try {
+ if (needToReadValueFromDisplayGroup(value.toString(), getText)) {
+ // No need to listen for any events that might get fired
+ // while setting the text since we are the one setting it.
+ boolean wasListening = isListening;
+ isListening = false;
+
+ // setText is an expensive operation
+ setText.invoke(component, value.toString());
+
+ isListening = wasListening;
+ needsUpdate = false;
+ }
+ } catch (Exception exc) {
+ throw new WotonomyException("Error while updating component connection", exc);
+ }
+ }
+ }
+
+ // icon aspect
+ displayGroup = displayGroupForAspect(IconAspect);
+ key = displayGroupKeyForAspect(IconAspect);
+ if (key != null) {
+ if (displayGroup != null) {
+ value = displayGroup.selectedObjectValueForKey(key);
+ } else {
+ // treat bound key without display group
+ // as a resource to be loaded from the selected class.
+ value = null;
+ Object o = displayGroup.selectedObject();
+ if (o != null) {
+ URL url = o.getClass().getResource(key);
+ if (url != null) {
+ value = new ImageIcon(url);
+ }
+ }
+ }
+
+ try {
+ setIcon.invoke(component, value);
+ } catch (Exception exc) {
+ throw new WotonomyException("Error while updating component connection", exc);
+ }
+ }
+
+ // enabled aspect
+ displayGroup = displayGroupForAspect(EnabledAspect);
+ key = displayGroupKeyForAspect(EnabledAspect);
+ if ((key != null) && (component instanceof Component)) {
+ if (displayGroup != null) {
+ value = displayGroup.selectedObjectValueForKey(key);
+ } else {
+ // treat bound key without display group as a value
+ value = key;
+ }
+ Boolean converted = null;
+ if (value != null) {
+ converted = (Boolean) ValueConverter.convertObjectToClass(value, Boolean.class);
+ }
+ if (converted == null)
+ converted = Boolean.FALSE;
+ if (((Component) component).isEnabled() != converted.booleanValue()) {
+ ((Component) component).setEnabled(converted.booleanValue());
+ }
+ }
+
+ // editable aspect
+ displayGroup = displayGroupForAspect(EditableAspect);
+ key = displayGroupKeyForAspect(EditableAspect);
+ if ((key != null) && (component instanceof JTextComponent)) {
+ if (displayGroup != null) {
+ value = displayGroup.selectedObjectValueForKey(key);
+ } else {
+ // treat bound key without display group as a value
+ value = key;
+ }
+ Boolean converted = (Boolean) ValueConverter.convertObjectToClass(value, Boolean.class);
+
+ if (converted != null) {
+ if (converted.booleanValue() != ((JTextComponent) component).isEditable()) {
+ ((JTextComponent) component).setEditable(converted.booleanValue());
+ }
+ }
+ }
+
+ // visible aspect
+ displayGroup = displayGroupForAspect(VisibleAspect);
+ key = displayGroupKeyForAspect(VisibleAspect);
+ if ((key != null) && (component instanceof Component)) {
+ if (displayGroup != null) {
+ value = displayGroup.selectedObjectValueForKey(key);
+ } else {
+ // treat bound key without display group as a value
+ value = key;
+ }
+ Boolean converted = (Boolean) ValueConverter.convertObjectToClass(value, Boolean.class);
+
+ if (converted != null) {
+ if (converted.booleanValue() != ((Component) component).isVisible()) {
+ ((Component) component).setVisible(converted.booleanValue());
+ }
+ }
+ }
+
+ // label aspect
+ displayGroup = displayGroupForAspect(LabelAspect);
+ key = displayGroupKeyForAspect(LabelAspect);
+
+ if ((key != null) && (component instanceof JTextComponent)) {
+ if (displayGroup != null) {
+ value = displayGroup.selectedObjectValueForKey(key);
+ } else {
+ // treat bound key without display group as a value
+ value = key;
+ }
+ Boolean converted = (Boolean) ValueConverter.convertObjectToClass(value, Boolean.class);
+
+ if (converted != null) {
+ if (converted.booleanValue()) {
+ if (component instanceof JTextComponent) {
+ if (component instanceof JTextArea) {
+ areaToLabel((JTextArea) component);
+ } else {
+ fieldToLabel((JTextComponent) component);
+ }
+ }
+ } else {
+ if (component instanceof JTextComponent) {
+ if (component instanceof JTextArea) {
+ labelToArea((JTextArea) component);
+ } else {
+ labelToField((JTextComponent) component);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private void fieldToLabel(JTextComponent aTextField) {
+ // turn on wrapping and disable editing and highlighting
+
+ aTextField.setEditable(false);
+ aTextField.setOpaque(false);
+
+ // Set the border, colors and font to that of a label
+
+ // LookAndFeel.installBorder(aTextField, "Label.border");
+ aTextField.setBorder(null);
+
+ LookAndFeel.installColorsAndFont(aTextField, "Label.background", "Label.foreground", "Label.font");
+ }
+
+ private void labelToField(JTextComponent aTextField) {
+ // turn on wrapping and disable editing and highlighting
+
+ aTextField.setEditable(true);
+ aTextField.setOpaque(true);
+
+ // Set the border, colors and font to that of a label
+
+ LookAndFeel.installBorder(aTextField, "TextField.border");
+
+ LookAndFeel.installColorsAndFont(aTextField, "TextField.background", "TextField.foreground", "TextField.font");
+ }
+
+ private void areaToLabel(JTextArea aTextArea) {
+ // turn on wrapping and disable editing and highlighting
+
+ aTextArea.setLineWrap(true);
+ aTextArea.setWrapStyleWord(true);
+ aTextArea.setEditable(false);
+
+ // Set the text area's border, colors and font to
+ // that of a label
+
+ // LookAndFeel.installBorder(aTextArea, "Label.border");
+ aTextArea.setBorder(null);
+
+ LookAndFeel.installColorsAndFont(aTextArea, "Label.background", "Label.foreground", "Label.font");
+
+ }
+
+ private void labelToArea(JTextArea aTextArea) {
+ // turn on wrapping and disable editing and highlighting
+
+ aTextArea.setEditable(true);
+
+ // Set the border, colors and font to that of a label
+
+ LookAndFeel.installBorder(aTextArea, "TextArea.border");
+
+ LookAndFeel.installColorsAndFont(aTextArea, "TextArea.background", "TextArea.foreground", "TextArea.font");
+ }
+
+ /**
+ * Forces this association to cause the object to stop editing and validate the
+ * user's input.
+ *
+ * @return false if there were problems validating, or true to continue.
+ */
+ public boolean endEditing() {
+ pleaseAcceptNextChange = true;
+ pleaseIgnoreNextChange = false;
+ return writeValueToDisplayGroup();
+ }
+
+ /**
+ * Writes the value currently in the component to the selected object in the
+ * display group bound to the value aspect.
+ *
+ * @return false if there were problems validating, or true to continue.
+ */
+ protected boolean writeValueToDisplayGroup() {
+ boolean returnValue = true;
+ if (hasDocument && !needsUpdate)
+ return true;
+
+ EODisplayGroup displayGroup = valueDisplayGroup;
+ if (displayGroup != null) {
+ String key = valueKey;
+ Object component = object();
+ Object value = null;
+ try {
+ // if ( getText.implementedByObject( component ) )
+ // {
+ value = getText.invoke(component);
+ // }
+ } catch (Exception exc) {
+ throw new WotonomyException("Error updating display group", exc);
+ }
+
+ if ((wasNull) && (EMPTY_STRING.equals(value))) {
+ value = null;
+ } else if (format() != null) {
+ try {
+ value = format().parseObject(value.toString());
+ } catch (ParseException exc) {
+ String message = exc.getMessage();
+ // "That format was not recognized.";
+ if (displayGroup.associationFailedToValidateValue(this, value.toString(), key, exc, message)) {
+ boolean wasListening = isListening;
+ isListening = false;
+ JOptionPane.showMessageDialog((Component) component, message);
+ isListening = wasListening;
+ }
+ needsUpdate = false;
+ return false;
+ }
+ }
+
+ if ((lastKnownType != null) && (value != null)) {
+ // convert back to last known type, if necessary/possible
+ Class type = value.getClass();
+ if ((type != null) && (type != lastKnownType)) {
+ Object converted = ValueConverter.convertObjectToClass(value, lastKnownType);
+ if (converted != null) {
+ value = converted;
+ }
+ // else: not possible, ignore
+ }
+ }
+
+ needsUpdate = false;
+
+ // only update if the value is different from the one in the display group
+ if (!needToWriteValueToDisplayGroup(value, displayGroup))
+ return true;
+
+ // we might lose focus if display group displays a validation message
+ boolean wasListening = isListening;
+ isListening = false;
+
+ Iterator selectedIterator = displayGroup.selectionIndexes().iterator();
+ while (selectedIterator.hasNext()) {
+ int index = ((Integer) selectedIterator.next()).intValue();
+
+ if (displayGroup.setValueForObjectAtIndex(value, index, key)) {
+ needsUpdate = false;
+ } else {
+ needsUpdate = false;
+ returnValue = false;
+ }
+ }
+ isListening = wasListening;
+
+ }
+ return returnValue;
+ }
+
+ /**
+ * Called to determine whether the display group needs to be updated. This
+ * implementation reads the value from the display group and only returns true
+ * if the specified value is different. This is done as an optimization since
+ * writes are more expensive than reads. Override to customize this behavior.
+ */
+ protected boolean needToWriteValueToDisplayGroup(Object aValue, EODisplayGroup aDisplayGroup) {
+ Object existingValue = aDisplayGroup.selectedObjectValueForKey(valueKey);
+ if (aDisplayGroup.selectedObjects().size() == 1) {
+ if (existingValue == aValue)
+ return false;
+ if ((existingValue != null) && (existingValue.equals(aValue)))
+ return false;
+ if ((aValue != null) && (aValue.equals(existingValue)))
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Called to determine whether the controlled component needs to be updated.
+ * This implementation reads the value from the selector and only returns true
+ * if the specified value is different. This is done as an optimization since
+ * updating the component can be an expensive operation. Override to customize
+ * this behavior.
+ */
+ protected boolean needToReadValueFromDisplayGroup(Object aValue, NSSelector aSelector)
+ throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
+ return !aValue.toString().equals(aSelector.invoke(object()));
+ }
+
+ /**
+ * Sets the Format that is used to convert values from the display group to and
+ * from text that is displayed in the component.
+ */
+ public void setFormat(Format aFormat) {
+ format = aFormat;
+ }
+
+ /**
+ * Gets the Format that is used to convert values from the display group to and
+ * from text that is displayed in the component.
+ */
+ public Format format() {
+ return format;
+ }
+
+ /**
+ * Returns whether the text association is configured to actively update the
+ * model in response to changes in the component.
+ */
+ public boolean isActiveUpdate() {
+ return activeUpdate;
+ }
+
+ /**
+ * Sets whether the text association should actively update the model in
+ * response to changes in the component. Default is true. False indicates that
+ * the model will be updated only when the component loses focus or fires an
+ * action event.
+ */
+ public void setActiveUpdate(boolean isActiveUpdate) {
+ activeUpdate = isActiveUpdate;
+ }
+
+ // interface ActionListener
+
+ /**
+ * Updates object on action performed.
+ */
+ public void actionPerformed(ActionEvent evt) {
+ if (!isListening)
+ return;
+ if (needsUpdate) {
+ pleaseAcceptNextChange = true; // needed if activeUpdate = false
+ writeValueToDisplayGroup();
+ }
+ }
+
+ // interface FocusListener
+
+ /**
+ * Notifies of beginning of edit.
+ */
+ public void focusGained(FocusEvent evt) {
+ if (!isListening)
+ return;
+
+ pleaseAcceptNextChange = true;
+ externallyChanged = true;
+
+ Object o;
+ EODisplayGroup displayGroup;
+ Enumeration e = aspects().objectEnumerator();
+ while (e.hasMoreElements()) {
+ displayGroup = displayGroupForAspect(e.nextElement().toString());
+ if (displayGroup != null) {
+ displayGroup.associationDidBeginEditing(this);
+ }
+ }
+ }
+
+ /**
+ * Updates object on focus lost and notifies of end of edit.
+ */
+ public void focusLost(FocusEvent evt) {
+ if (!isListening)
+ return;
+ if (endEditing()) {
+ Object o;
+ EODisplayGroup displayGroup;
+ Enumeration e = aspects().objectEnumerator();
+ while (e.hasMoreElements()) {
+ displayGroup = displayGroupForAspect(e.nextElement().toString());
+ if (displayGroup != null) {
+ displayGroup.associationDidEndEditing(this);
+ }
+ }
+ } else {
+ // probably should notify of a validation error here,
+ }
+ }
+
+ /**
+ * Queues a notification to PostWhenIdle.
+ */
+ protected void queueUpdate(DocumentEvent e) {
+ if (e.getDocument() instanceof DefaultStyledDocument) {
+ if (e instanceof AbstractDocument.DefaultDocumentEvent) {
+ int docLength = e.getDocument().getLength();
+
+ if ((e.getType().equals(DocumentEvent.EventType.CHANGE))) {
+ if (e.getOffset() == 0 && e.getLength() == docLength) {
+ // ignore document events for the whole document
+ // since default styled document broadcasts these
+ // using invokeLater, and we've already received
+ // notification about the actual style change.
+ // see: DefaultStyledDocument.ChangeUpdateRunnable
+ return;
+ }
+ }
+ }
+ }
+
+ NSNotificationQueue.defaultQueue().enqueueNotification(
+ new NSNotification("TextAssociation.DocumentChanged", this,
+ new NSDictionary(new Object[] { "event" }, new Object[] { e })),
+ NSNotificationQueue.PostWhenIdle);
+ }
+
+ /**
+ * Handles idle notification.
+ */
+ public void handleNotification(NSNotification aNotification) {
+ if (activeUpdate) {
+ writeValueToDisplayGroup();
+ }
+ }
+
+ // interface DocumentListener
+
+ public void insertUpdate(DocumentEvent e) {
+ if (!isListening)
+ return;
+ needsUpdate = true;
+ queueUpdate(e);
+ }
+
+ public void removeUpdate(DocumentEvent e) {
+ if (!isListening)
+ return;
+ needsUpdate = true;
+ queueUpdate(e);
+ }
+
+ public void changedUpdate(DocumentEvent e) {
+ if (!isListening)
+ return;
+ needsUpdate = true;
+ queueUpdate(e);
+ }
}
/*
- * $Log$
- * Revision 1.2 2006/02/18 23:19:05 cgruber
- * Update imports and maven dependencies.
+ * $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.1 2006/02/16 13:22:22 cgruber Check in all sources in
+ * eclipse-friendly maven-enabled packages.
*
- * Revision 1.41 2004/02/05 02:18:18 mpowers
- * Now setting border to null to new Aqua LAF behaves.
+ * Revision 1.41 2004/02/05 02:18:18 mpowers Now setting border to null to new
+ * Aqua LAF behaves.
*
- * Revision 1.40 2004/01/28 22:47:56 mpowers
- * un-activeUpdate was brokne.
+ * Revision 1.40 2004/01/28 22:47:56 mpowers un-activeUpdate was brokne.
*
- * Revision 1.39 2004/01/28 18:34:57 mpowers
- * Better handling for enabling.
- * Now respecting enabledToSetSelectedObjectValueForKey from display group.
+ * Revision 1.39 2004/01/28 18:34:57 mpowers Better handling for enabling. Now
+ * respecting enabledToSetSelectedObjectValueForKey from display group.
*
- * Revision 1.38 2003/08/06 23:07:52 chochos
- * general code cleanup (mostly, removing unused imports)
+ * Revision 1.38 2003/08/06 23:07:52 chochos general code cleanup (mostly,
+ * removing unused imports)
*
- * Revision 1.37 2003/02/06 16:21:34 mpowers
- * Fix for activeUpdate: no longer bothering with editing context's changes.
+ * Revision 1.37 2003/02/06 16:21:34 mpowers Fix for activeUpdate: no longer
+ * bothering with editing context's changes.
*
- * Revision 1.36 2002/10/24 18:19:24 mpowers
- * Bug fix - thanks to dwang.
+ * Revision 1.36 2002/10/24 18:19:24 mpowers Bug fix - thanks to dwang.
*
- * Revision 1.35 2002/08/02 19:19:30 mpowers
- * Added control points for when to read or write from the display group.
- * Added flags needed to fix problems with non-activeUpdate and commit key.
+ * Revision 1.35 2002/08/02 19:19:30 mpowers Added control points for when to
+ * read or write from the display group. Added flags needed to fix problems with
+ * non-activeUpdate and commit key.
*
- * Revision 1.33 2002/03/08 23:18:01 mpowers
- * Added visible aspect.
+ * Revision 1.33 2002/03/08 23:18:01 mpowers Added visible aspect.
*
- * Revision 1.32 2002/03/06 16:13:53 mpowers
- * Yet another fix for style document changes: swing's DefaultStyledDocument
- * using an invoke later to launch a final StyleChanged event, which occurs
- * after the TextAssociation has reestablished itself as a document listener,
- * causing the item to be marked dirty. We're now handling this case.
+ * Revision 1.32 2002/03/06 16:13:53 mpowers Yet another fix for style document
+ * changes: swing's DefaultStyledDocument using an invoke later to launch a
+ * final StyleChanged event, which occurs after the TextAssociation has
+ * reestablished itself as a document listener, causing the item to be marked
+ * dirty. We're now handling this case.
*
- * Revision 1.31 2002/03/04 22:10:37 mpowers
- * Supressing active update only marks dirty when contents have changed.
+ * Revision 1.31 2002/03/04 22:10:37 mpowers Supressing active update only marks
+ * dirty when contents have changed.
*
- * Revision 1.30 2002/02/23 16:19:12 mpowers
- * Now only marking an editing context as dirty if it's not already dirty.
+ * Revision 1.30 2002/02/23 16:19:12 mpowers Now only marking an editing context
+ * as dirty if it's not already dirty.
*
- * Revision 1.29 2002/02/19 18:38:29 mpowers
- * Minor optimization: activeUpdate now checked in handleNotification.
+ * Revision 1.29 2002/02/19 18:38:29 mpowers Minor optimization: activeUpdate
+ * now checked in handleNotification.
*
- * Revision 1.28 2002/02/19 16:36:47 mpowers
- * Better support for active update: objects are now marked as changed
- * even though the model itself is not updated -- this allows editing
- * context itself to be marked as having changes to be saved.
+ * Revision 1.28 2002/02/19 16:36:47 mpowers Better support for active update:
+ * objects are now marked as changed even though the model itself is not updated
+ * -- this allows editing context itself to be marked as having changes to be
+ * saved.
*
- * Revision 1.27 2002/01/23 19:50:11 mpowers
- * Fix for a null pointer when value is null and last known type is not.
- * (from dwang)
+ * Revision 1.27 2002/01/23 19:50:11 mpowers Fix for a null pointer when value
+ * is null and last known type is not. (from dwang)
*
- * Revision 1.26 2002/01/14 19:37:22 mpowers
- * Fix for NPE when value is null and auto update is false.
+ * Revision 1.26 2002/01/14 19:37:22 mpowers Fix for NPE when value is null and
+ * auto update is false.
*
- * Revision 1.25 2001/12/10 03:16:11 mpowers
- * Fixed bug with isListening when no items are in display group.
+ * Revision 1.25 2001/12/10 03:16:11 mpowers Fixed bug with isListening when no
+ * items are in display group.
*
- * Revision 1.24 2001/11/16 19:14:51 mpowers
- * Brought back the idea of configuring whether updates occur on each change.
+ * Revision 1.24 2001/11/16 19:14:51 mpowers Brought back the idea of
+ * configuring whether updates occur on each change.
*
- * Revision 1.23 2001/11/08 20:06:06 mpowers
- * Now performing type-conversion as a convenience.
+ * Revision 1.23 2001/11/08 20:06:06 mpowers Now performing type-conversion as a
+ * convenience.
*
- * Revision 1.22 2001/11/04 18:24:20 mpowers
- * Better handling for non-string values when bulk-editing.
+ * Revision 1.22 2001/11/04 18:24:20 mpowers Better handling for non-string
+ * values when bulk-editing.
*
- * Revision 1.21 2001/11/01 15:53:34 mpowers
- * Now that NSNotificationQueue correctly implements PostWhenIdle, we can
- * finally discard our use of Swing's Timer in favor of using the queue
- * to coalesce document changed events.
+ * Revision 1.21 2001/11/01 15:53:34 mpowers Now that NSNotificationQueue
+ * correctly implements PostWhenIdle, we can finally discard our use of Swing's
+ * Timer in favor of using the queue to coalesce document changed events.
*
- * Revision 1.20 2001/10/26 19:58:06 mpowers
- * Better handling for non-string types. We were testing with equals with the
- * new value against the existing value in the component. Now we convert
- * the new value to a string before comparing. Fixes case for properties
- * of non-String types, like StringBuffer.
+ * Revision 1.20 2001/10/26 19:58:06 mpowers Better handling for non-string
+ * types. We were testing with equals with the new value against the existing
+ * value in the component. Now we convert the new value to a string before
+ * comparing. Fixes case for properties of non-String types, like StringBuffer.
*
- * Revision 1.19 2001/09/30 21:57:14 mpowers
- * Timers were not getting cleaned up if breakConnection was called
- * before the timer got a chance to fire.
+ * Revision 1.19 2001/09/30 21:57:14 mpowers Timers were not getting cleaned up
+ * if breakConnection was called before the timer got a chance to fire.
*
- * Revision 1.18 2001/08/22 15:42:26 mpowers
- * Added support for JTextComponent label-izing.
+ * Revision 1.18 2001/08/22 15:42:26 mpowers Added support for JTextComponent
+ * label-izing.
*
- * Revision 1.17 2001/07/30 16:32:55 mpowers
- * Implemented support for bulk-editing. Detail associations will now
- * apply changes to all selected objects.
+ * Revision 1.17 2001/07/30 16:32:55 mpowers Implemented support for
+ * bulk-editing. Detail associations will now apply changes to all selected
+ * objects.
*
- * Revision 1.16 2001/07/17 19:53:37 mpowers
- * Made some private fields protected for benefit of subclassers.
+ * Revision 1.16 2001/07/17 19:53:37 mpowers Made some private fields protected
+ * for benefit of subclassers.
*
- * Revision 1.15 2001/06/30 14:59:36 mpowers
- * LabelAspect now sets the text field's opaque setting.
+ * Revision 1.15 2001/06/30 14:59:36 mpowers LabelAspect now sets the text
+ * field's opaque setting.
*
- * Revision 1.14 2001/06/29 14:54:08 mpowers
- * Another fix for timers - timers were definitely causing a memory leak.
+ * Revision 1.14 2001/06/29 14:54:08 mpowers Another fix for timers - timers
+ * were definitely causing a memory leak.
*
- * Revision 1.13 2001/06/26 21:37:19 mpowers
- * Fixed a null pointer in the new key timer scheme.
+ * Revision 1.13 2001/06/26 21:37:19 mpowers Fixed a null pointer in the new key
+ * timer scheme.
*
- * Revision 1.12 2001/06/25 14:46:03 mpowers
- * Fixed a memory leak involving the use of timers.
+ * Revision 1.12 2001/06/25 14:46:03 mpowers Fixed a memory leak involving the
+ * use of timers.
*
- * Revision 1.11 2001/06/01 19:14:59 mpowers
- * Text association's enabled aspect is now more discriminating.
+ * Revision 1.11 2001/06/01 19:14:59 mpowers Text association's enabled aspect
+ * is now more discriminating.
*
- * Revision 1.10 2001/05/18 21:07:24 mpowers
- * Changed the way we handle failure to update object value.
+ * Revision 1.10 2001/05/18 21:07:24 mpowers Changed the way we handle failure
+ * to update object value.
*
- * Revision 1.9 2001/03/13 21:39:58 mpowers
- * Improved validation handling.
+ * Revision 1.9 2001/03/13 21:39:58 mpowers Improved validation handling.
*
- * Revision 1.8 2001/03/12 12:49:10 mpowers
- * Improved validation handling.
- * Having a formatter disables auto-updating.
+ * Revision 1.8 2001/03/12 12:49:10 mpowers Improved validation handling. Having
+ * a formatter disables auto-updating.
*
- * Revision 1.7 2001/03/09 22:08:13 mpowers
- * Now handling any objects that have a valid Document.
- * No longer checking enabled before updating the enabled state.
+ * Revision 1.7 2001/03/09 22:08:13 mpowers Now handling any objects that have a
+ * valid Document. No longer checking enabled before updating the enabled state.
*
- * Revision 1.6 2001/03/07 19:57:32 mpowers
- * Fixed paste error in IconAspect.
+ * Revision 1.6 2001/03/07 19:57:32 mpowers Fixed paste error in IconAspect.
*
- * Revision 1.4 2001/02/17 16:52:05 mpowers
- * Changes in imports to support building with jdk1.1 collections.
+ * Revision 1.4 2001/02/17 16:52:05 mpowers Changes in imports to support
+ * building with jdk1.1 collections.
*
- * Revision 1.3 2001/01/31 19:12:33 mpowers
- * Implemented auto-updating in TextComponent.
+ * Revision 1.3 2001/01/31 19:12:33 mpowers Implemented auto-updating in
+ * TextComponent.
*
- * Revision 1.2 2001/01/10 15:53:58 mpowers
- * Preventing a null pointer exception if getText were to return null,
- * which doesn't happen for JTextFields but might happen for other objects.
+ * Revision 1.2 2001/01/10 15:53:58 mpowers Preventing a null pointer exception
+ * if getText were to return null, which doesn't happen for JTextFields but
+ * might happen for other objects.
*
- * Revision 1.1.1.1 2000/12/21 15:49:08 mpowers
- * Contributing wotonomy.
+ * Revision 1.1.1.1 2000/12/21 15:49:08 mpowers Contributing wotonomy.
*
- * Revision 1.13 2000/12/20 16:25:41 michael
- * Added log to all files.
+ * Revision 1.13 2000/12/20 16:25:41 michael Added log to all files.
*
*
*/
-
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/TimedTextAssociation.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/TimedTextAssociation.java
index 49879e9..a6e993c 100644
--- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/TimedTextAssociation.java
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/TimedTextAssociation.java
@@ -48,75 +48,51 @@ import net.wotonomy.ui.EOAssociation;
import net.wotonomy.ui.EODisplayGroup;
/**
-* TimedTextAssociation works like TextAssociation,
-* but instead of using a delayed event to update the
-* model, it uses a timer so that the model is only
-* updated if the user pauses typing for some short interval.
-* This is useful when the update and/or re-read of the model
-* is a costly operation.
-* Bindings are:
-* <ul>
-* <li>value: a property convertable to/from a string</li>
-* <li>editable: a boolean property that determines whether
-* the user can edit the text in the field</li>
-* <li>enabled: a boolean property that determines whether
-* the user can select the text in the field</li>
-* <li>label: a boolean property that determines whether
-* field should appear as a read-only, selectable label</li>
-* <li>icon: a property that returns a Swing icon, for use
-* with JLabels and other components with setIcon() methods.
-* If bound to a static string, the string will be used to
-* load an image resource from the selected object's class.</li>
-* </ul>
-*
-* @author michael@mpowers.net
-* @author $Author: cgruber $
-* @version $Revision: 904 $
-*/
-public class TimedTextAssociation extends EOAssociation
- implements FocusListener, ActionListener, DocumentListener
-{
- //TODO: need to refactor this so that it can subclass text association.
- //This implementation is basically a branch from the v1.20 TextAssociation.
-
- static final NSArray aspects =
- new NSArray( new Object[] {
- ValueAspect, EnabledAspect, EditableAspect, LabelAspect, IconAspect
- } );
- static final NSArray aspectSignatures =
- new NSArray( new Object[] {
- AttributeToOneAspectSignature,
- AttributeToOneAspectSignature,
- AttributeToOneAspectSignature,
- AttributeToOneAspectSignature
- } );
- static final NSArray objectKeysTaken =
- new NSArray( new Object[] {
- "text", "enabled", "editable"
- } );
-
- private final static NSSelector getText =
- new NSSelector( "getText" );
- private final static NSSelector setText =
- new NSSelector( "setText",
- new Class[] { String.class } );
- private final static NSSelector getDocument =
- new NSSelector( "getDocument" );
- private final static NSSelector setIcon =
- new NSSelector( "setIcon",
- new Class[] { Icon.class } );
- private final static NSSelector addActionListener =
- new NSSelector( "addActionListener",
- new Class[] { ActionListener.class } );
- private final static NSSelector removeActionListener =
- new NSSelector( "removeActionListener",
- new Class[] { ActionListener.class } );
- private final static NSSelector addFocusListener =
- new NSSelector( "addFocusListener",
- new Class[] { FocusListener.class } );
- private final static NSSelector removeFocusListener =
- new NSSelector( "removeFocusListener",
- new Class[] { FocusListener.class } );
+ * TimedTextAssociation works like TextAssociation, but instead of using a
+ * delayed event to update the model, it uses a timer so that the model is only
+ * updated if the user pauses typing for some short interval. This is useful
+ * when the update and/or re-read of the model is a costly operation. Bindings
+ * are:
+ * <ul>
+ * <li>value: a property convertable to/from a string</li>
+ * <li>editable: a boolean property that determines whether the user can edit
+ * the text in the field</li>
+ * <li>enabled: a boolean property that determines whether the user can select
+ * the text in the field</li>
+ * <li>label: a boolean property that determines whether field should appear as
+ * a read-only, selectable label</li>
+ * <li>icon: a property that returns a Swing icon, for use with JLabels and
+ * other components with setIcon() methods. If bound to a static string, the
+ * string will be used to load an image resource from the selected object's
+ * class.</li>
+ * </ul>
+ *
+ * @author michael@mpowers.net
+ * @author $Author: cgruber $
+ * @version $Revision: 904 $
+ */
+public class TimedTextAssociation extends EOAssociation implements FocusListener, ActionListener, DocumentListener {
+ // TODO: need to refactor this so that it can subclass text association.
+ // This implementation is basically a branch from the v1.20 TextAssociation.
+
+ static final NSArray aspects = new NSArray(
+ new Object[] { ValueAspect, EnabledAspect, EditableAspect, LabelAspect, IconAspect });
+ static final NSArray aspectSignatures = new NSArray(new Object[] { AttributeToOneAspectSignature,
+ AttributeToOneAspectSignature, AttributeToOneAspectSignature, AttributeToOneAspectSignature });
+ static final NSArray objectKeysTaken = new NSArray(new Object[] { "text", "enabled", "editable" });
+
+ private final static NSSelector getText = new NSSelector("getText");
+ private final static NSSelector setText = new NSSelector("setText", new Class[] { String.class });
+ private final static NSSelector getDocument = new NSSelector("getDocument");
+ private final static NSSelector setIcon = new NSSelector("setIcon", new Class[] { Icon.class });
+ private final static NSSelector addActionListener = new NSSelector("addActionListener",
+ new Class[] { ActionListener.class });
+ private final static NSSelector removeActionListener = new NSSelector("removeActionListener",
+ new Class[] { ActionListener.class });
+ private final static NSSelector addFocusListener = new NSSelector("addFocusListener",
+ new Class[] { FocusListener.class });
+ private final static NSSelector removeFocusListener = new NSSelector("removeFocusListener",
+ new Class[] { FocusListener.class });
// null handling
protected boolean wasNull;
@@ -125,227 +101,186 @@ public class TimedTextAssociation extends EOAssociation
// dirty handling
protected boolean needsUpdate;
protected boolean hasDocument;
- protected boolean isListening;
+ protected boolean isListening;
// formatting
protected Format format;
- // cache the value aspect
- private EODisplayGroup valueDisplayGroup;
- private String valueKey;
-
- // coalescing document events
- protected boolean autoUpdating;
- protected int interval = 400; // adjust as needed
- protected Timer keyTimer;
-
- // NOTE: a new key timer is created for each use and
- // is disposed when the timer is stopped.
- // Swing's Timer class is kept in a static list of timers
- // and each retains a strong reference to their listeners.
- // This caused a memory leak as associations typically
- // refer to their controlled component which is referred
- // to by its parents and so on until no application window
- // will ever get garbage collected. yikes.
-
- /**
- * Constructor specifying the object to be controlled by this
- * association. Does not establish connection.
- */
- public TimedTextAssociation ( Object anObject )
- {
- super( anObject );
+ // cache the value aspect
+ private EODisplayGroup valueDisplayGroup;
+ private String valueKey;
+
+ // coalescing document events
+ protected boolean autoUpdating;
+ protected int interval = 400; // adjust as needed
+ protected Timer keyTimer;
+
+ // NOTE: a new key timer is created for each use and
+ // is disposed when the timer is stopped.
+ // Swing's Timer class is kept in a static list of timers
+ // and each retains a strong reference to their listeners.
+ // This caused a memory leak as associations typically
+ // refer to their controlled component which is referred
+ // to by its parents and so on until no application window
+ // will ever get garbage collected. yikes.
+
+ /**
+ * Constructor specifying the object to be controlled by this association. Does
+ * not establish connection.
+ */
+ public TimedTextAssociation(Object anObject) {
+ super(anObject);
wasNull = false;
needsUpdate = false;
hasDocument = false;
- isListening = true;
- valueDisplayGroup = null;
- valueKey = null;
-
- autoUpdating = true;
- keyTimer = null;
- }
-
- /**
- * 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. <ul>
- * <li>"A" attribute: the aspect can be bound to
- * an attribute.</li>
- * <li>"1" to-one: the aspect can be bound to a
- * property that returns a single object.</li>
- * <li>"M" to-one: the aspect can be bound to a
- * property that returns multiple objects.</li>
- * </ul>
- * 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 setText.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. This is typically "value" or somesuch.
- */
- public static String primaryAspect ()
- {
- return ValueAspect;
- }
-
- /**
- * 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 ) );
- }
-
- /**
- * Binds the specified aspect of this association to the
- * specified key on the specified display group.
- */
- public void bindAspect (
- String anAspect, EODisplayGroup aDisplayGroup, String aKey )
- {
- if ( ValueAspect.equals( anAspect ) )
- {
+ isListening = true;
+ valueDisplayGroup = null;
+ valueKey = null;
+
+ autoUpdating = true;
+ keyTimer = null;
+ }
+
+ /**
+ * 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.
+ * <ul>
+ * <li>"A" attribute: the aspect can be bound to an attribute.</li>
+ * <li>"1" to-one: the aspect can be bound to a property that returns a single
+ * object.</li>
+ * <li>"M" to-one: the aspect can be bound to a property that returns multiple
+ * objects.</li>
+ * </ul>
+ * 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 setText.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. This is typically
+ * "value" or somesuch.
+ */
+ public static String primaryAspect() {
+ return ValueAspect;
+ }
+
+ /**
+ * 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));
+ }
+
+ /**
+ * Binds the specified aspect of this association to the specified key on the
+ * specified display group.
+ */
+ public void bindAspect(String anAspect, EODisplayGroup aDisplayGroup, String aKey) {
+ if (ValueAspect.equals(anAspect)) {
valueDisplayGroup = aDisplayGroup;
valueKey = aKey;
}
- super.bindAspect( anAspect, aDisplayGroup, aKey );
+ super.bindAspect(anAspect, aDisplayGroup, aKey);
}
- /**
- * Establishes a connection between this association
- * and the controlled object. This implementation
- * attempts to add this class as an ActionListener
- * and as a FocusListener to the specified object.
- */
- public void establishConnection ()
- {
+ /**
+ * Establishes a connection between this association and the controlled object.
+ * This implementation attempts to add this class as an ActionListener and as a
+ * FocusListener to the specified object.
+ */
+ public void establishConnection() {
Object component = object();
- try
- {
- if ( addActionListener.implementedByObject( component ) )
- {
- addActionListener.invoke( component, this );
+ try {
+ if (addActionListener.implementedByObject(component)) {
+ addActionListener.invoke(component, this);
}
- if ( addFocusListener.implementedByObject( component ) )
- {
- addFocusListener.invoke( component, this );
+ if (addFocusListener.implementedByObject(component)) {
+ addFocusListener.invoke(component, this);
}
- hasDocument = false;
- if ( getDocument.implementedByObject( component ) )
- {
- Object document = getDocument.invoke( component );
- if ( document instanceof Document )
- {
- ((Document)document).addDocumentListener( this );
- hasDocument = true;
- }
+ hasDocument = false;
+ if (getDocument.implementedByObject(component)) {
+ Object document = getDocument.invoke(component);
+ if (document instanceof Document) {
+ ((Document) document).addDocumentListener(this);
+ hasDocument = true;
+ }
}
- }
- catch ( Exception exc )
- {
- throw new WotonomyException(
- "Error while establishing connection", exc );
+ } catch (Exception exc) {
+ throw new WotonomyException("Error while establishing connection", exc);
}
- super.establishConnection();
+ super.establishConnection();
// forces update from bindings
subjectChanged();
- }
-
- /**
- * Breaks the connection between this association and
- * its object. Override to stop listening for events
- * from the object.
- */
- public void breakConnection ()
- {
+ }
+
+ /**
+ * Breaks the connection between this association and its object. Override to
+ * stop listening for events from the object.
+ */
+ public void breakConnection() {
Object component = object();
- try
- {
- if ( removeActionListener.implementedByObject( component ) )
- {
- removeActionListener.invoke( component, this );
+ try {
+ if (removeActionListener.implementedByObject(component)) {
+ removeActionListener.invoke(component, this);
}
- if ( removeFocusListener.implementedByObject( component ) )
- {
- removeFocusListener.invoke( component, this );
+ if (removeFocusListener.implementedByObject(component)) {
+ removeFocusListener.invoke(component, this);
}
- if ( getDocument.implementedByObject( component ) )
- {
- Object document = getDocument.invoke( component );
- if ( document instanceof Document )
- {
- ((Document)document).removeDocumentListener( this );
- }
+ if (getDocument.implementedByObject(component)) {
+ Object document = getDocument.invoke(component);
+ if (document instanceof Document) {
+ ((Document) document).removeDocumentListener(this);
+ }
}
+ } catch (Exception exc) {
+ throw new WotonomyException("Error while breaking connection", exc);
}
- catch ( Exception exc )
- {
- throw new WotonomyException(
- "Error while breaking connection", exc );
- }
- super.breakConnection();
- }
-
- /**
- * Called when either the selection or the contents
- * of an associated display group have changed.
- */
- public void subjectChanged ()
- {
+ super.breakConnection();
+ }
+
+ /**
+ * Called when either the selection or the contents of an associated display
+ * group have changed.
+ */
+ public void subjectChanged() {
Object component = object();
EODisplayGroup displayGroup;
String key;
@@ -353,677 +288,538 @@ public class TimedTextAssociation extends EOAssociation
// value aspect
displayGroup = valueDisplayGroup;
- if ( displayGroup != null )
- {
- if ( component instanceof Component )
- {
- ((Component)component).setEnabled(
- displayGroup.enabledToSetSelectedObjectValueForKey( valueKey ) );
- }
-
+ if (displayGroup != null) {
+ if (component instanceof Component) {
+ ((Component) component).setEnabled(displayGroup.enabledToSetSelectedObjectValueForKey(valueKey));
+ }
+
key = valueKey;
- if ( displayGroup.selectedObjects().size() > 1 )
- {
- // if there're more than one object selected, set
- // the value to blank for all of them.
- Object previousValue;
-
- Iterator indexIterator = displayGroup.selectionIndexes().
- iterator();
-
- // get value for the first selected object.
- int initialIndex = ( (Integer)indexIterator.next() ).intValue();
- previousValue = displayGroup.valueForObjectAtIndex(
- initialIndex, key );
- value = null;
-
- // go through the rest of the selected objects, compare each
- // value with the previous one. continue comparing if two
- // values are equal, break the while loop if they're different.
- // the final value will be the common value of all selected objects
- // if there is one, or be blank if there is not.
- while ( indexIterator.hasNext() )
- {
- int index = ( (Integer)indexIterator.next() ).intValue();
- Object currentValue = displayGroup.valueForObjectAtIndex(
- index, key );
- if ( currentValue != null && !currentValue.equals( previousValue ) )
- {
- value = null;
- break;
- }
- else
- {
- // currentValue is the same as the previous one
- value = currentValue;
- }
-
- } // end while
-
- } else {
-
- // if there's only one object selected.
- value = displayGroup.selectedObjectValueForKey( key );
- } // end checking the size of selected objects in displayGroup
-
- // convert value to string
- if ( value == null )
- {
- wasNull = true;
- value = EMPTY_STRING;
- }
- else
- {
- wasNull = false;
- if ( format() != null )
- {
- try
- {
- value = format().format( value );
- }
- catch ( IllegalArgumentException exc )
- {
- value = value.toString();
- }
- }
- }
-
-
- try
- {
- if ( ! value.toString().equals( getText.invoke( component ) ) )
- {
- // No need to listen for any events that might get fired
- // while setting the text since we are the one setting it.
- boolean wasListening = isListening;
- isListening = false;
-
- // setText is an expensive operation
- setText.invoke( component, value.toString() );
-
- isListening = wasListening;
- needsUpdate = false;
- }
+ if (displayGroup.selectedObjects().size() > 1) {
+ // if there're more than one object selected, set
+ // the value to blank for all of them.
+ Object previousValue;
+
+ Iterator indexIterator = displayGroup.selectionIndexes().iterator();
+
+ // get value for the first selected object.
+ int initialIndex = ((Integer) indexIterator.next()).intValue();
+ previousValue = displayGroup.valueForObjectAtIndex(initialIndex, key);
+ value = null;
+
+ // go through the rest of the selected objects, compare each
+ // value with the previous one. continue comparing if two
+ // values are equal, break the while loop if they're different.
+ // the final value will be the common value of all selected objects
+ // if there is one, or be blank if there is not.
+ while (indexIterator.hasNext()) {
+ int index = ((Integer) indexIterator.next()).intValue();
+ Object currentValue = displayGroup.valueForObjectAtIndex(index, key);
+ if (currentValue != null && !currentValue.equals(previousValue)) {
+ value = null;
+ break;
+ } else {
+ // currentValue is the same as the previous one
+ value = currentValue;
+ }
+
+ } // end while
+
+ } else {
+
+ // if there's only one object selected.
+ value = displayGroup.selectedObjectValueForKey(key);
+ } // end checking the size of selected objects in displayGroup
+
+ // convert value to string
+ if (value == null) {
+ wasNull = true;
+ value = EMPTY_STRING;
+ } else {
+ wasNull = false;
+ if (format() != null) {
+ try {
+ value = format().format(value);
+ } catch (IllegalArgumentException exc) {
+ value = value.toString();
+ }
+ }
}
- catch ( Exception exc )
- {
- throw new WotonomyException(
- "Error while updating component connection", exc );
+
+ try {
+ if (!value.toString().equals(getText.invoke(component))) {
+ // No need to listen for any events that might get fired
+ // while setting the text since we are the one setting it.
+ boolean wasListening = isListening;
+ isListening = false;
+
+ // setText is an expensive operation
+ setText.invoke(component, value.toString());
+
+ isListening = wasListening;
+ needsUpdate = false;
+ }
+ } catch (Exception exc) {
+ throw new WotonomyException("Error while updating component connection", exc);
}
}
// icon aspect
- displayGroup = displayGroupForAspect( IconAspect );
- key = displayGroupKeyForAspect( IconAspect );
- if ( key != null )
- {
- if ( displayGroup != null )
- {
- value =
- displayGroup.selectedObjectValueForKey( key );
- }
- else
- {
- // treat bound key without display group
- // as a resource to be loaded from the selected class.
- value = null;
- Object o = displayGroup.selectedObject();
- if ( o != null )
- {
- URL url = o.getClass().getResource( key );
- if ( url != null )
- {
- value = new ImageIcon( url );
- }
- }
+ displayGroup = displayGroupForAspect(IconAspect);
+ key = displayGroupKeyForAspect(IconAspect);
+ if (key != null) {
+ if (displayGroup != null) {
+ value = displayGroup.selectedObjectValueForKey(key);
+ } else {
+ // treat bound key without display group
+ // as a resource to be loaded from the selected class.
+ value = null;
+ Object o = displayGroup.selectedObject();
+ if (o != null) {
+ URL url = o.getClass().getResource(key);
+ if (url != null) {
+ value = new ImageIcon(url);
+ }
+ }
}
- try
- {
- setIcon.invoke( component, value );
- }
- catch ( Exception exc )
- {
- throw new WotonomyException(
- "Error while updating component connection", exc );
+ try {
+ setIcon.invoke(component, value);
+ } catch (Exception exc) {
+ throw new WotonomyException("Error while updating component connection", exc);
}
}
// enabled aspect
- displayGroup = displayGroupForAspect( EnabledAspect );
- key = displayGroupKeyForAspect( EnabledAspect );
- if ( ( key != null )
- && ( component instanceof Component ) )
- {
- if ( displayGroup != null )
- {
- value =
- displayGroup.selectedObjectValueForKey( key );
- }
- else
- {
+ displayGroup = displayGroupForAspect(EnabledAspect);
+ key = displayGroupKeyForAspect(EnabledAspect);
+ if ((key != null) && (component instanceof Component)) {
+ if (displayGroup != null) {
+ value = displayGroup.selectedObjectValueForKey(key);
+ } else {
// treat bound key without display group as a value
value = key;
}
- Boolean converted = null;
- if ( value != null )
- {
- converted = (Boolean)
- ValueConverter.convertObjectToClass(
- value, Boolean.class );
- }
- if ( converted == null ) converted = Boolean.FALSE;
- if ( ((Component)component).isEnabled() != converted.booleanValue() )
- {
- ((Component)component).setEnabled( converted.booleanValue() );
- }
+ Boolean converted = null;
+ if (value != null) {
+ converted = (Boolean) ValueConverter.convertObjectToClass(value, Boolean.class);
+ }
+ if (converted == null)
+ converted = Boolean.FALSE;
+ if (((Component) component).isEnabled() != converted.booleanValue()) {
+ ((Component) component).setEnabled(converted.booleanValue());
+ }
}
// editable aspect
- displayGroup = displayGroupForAspect( EditableAspect );
- key = displayGroupKeyForAspect( EditableAspect );
- if ( ( key != null )
- && ( component instanceof JTextComponent ) )
- {
- if ( displayGroup != null )
- {
- value =
- displayGroup.selectedObjectValueForKey( key );
- }
- else
- {
+ displayGroup = displayGroupForAspect(EditableAspect);
+ key = displayGroupKeyForAspect(EditableAspect);
+ if ((key != null) && (component instanceof JTextComponent)) {
+ if (displayGroup != null) {
+ value = displayGroup.selectedObjectValueForKey(key);
+ } else {
// treat bound key without display group as a value
value = key;
}
- Boolean converted = (Boolean)
- ValueConverter.convertObjectToClass(
- value, Boolean.class );
-
- if ( converted != null )
- {
- if ( converted.booleanValue() != ((JTextComponent)component).isEditable() )
- {
- ((JTextComponent)component).setEditable( converted.booleanValue() );
+ Boolean converted = (Boolean) ValueConverter.convertObjectToClass(value, Boolean.class);
+
+ if (converted != null) {
+ if (converted.booleanValue() != ((JTextComponent) component).isEditable()) {
+ ((JTextComponent) component).setEditable(converted.booleanValue());
}
}
}
// label aspect
- displayGroup = displayGroupForAspect( LabelAspect );
- key = displayGroupKeyForAspect( LabelAspect );
-
- if ( ( key != null )
- && ( component instanceof JTextComponent ) )
- {
- if ( displayGroup != null )
- {
- value =
- displayGroup.selectedObjectValueForKey( key );
- }
- else
- {
+ displayGroup = displayGroupForAspect(LabelAspect);
+ key = displayGroupKeyForAspect(LabelAspect);
+
+ if ((key != null) && (component instanceof JTextComponent)) {
+ if (displayGroup != null) {
+ value = displayGroup.selectedObjectValueForKey(key);
+ } else {
// treat bound key without display group as a value
value = key;
}
- Boolean converted = (Boolean)
- ValueConverter.convertObjectToClass(
- value, Boolean.class );
-
- if ( converted != null )
- {
- if ( converted.booleanValue() )
- {
- if ( component instanceof JTextComponent )
- {
- if ( component instanceof JTextArea )
- {
- areaToLabel( (JTextArea) component );
- }
- else
- {
- fieldToLabel( (JTextComponent) component );
- }
- }
+ Boolean converted = (Boolean) ValueConverter.convertObjectToClass(value, Boolean.class);
+
+ if (converted != null) {
+ if (converted.booleanValue()) {
+ if (component instanceof JTextComponent) {
+ if (component instanceof JTextArea) {
+ areaToLabel((JTextArea) component);
+ } else {
+ fieldToLabel((JTextComponent) component);
+ }
+ }
+ } else {
+ if (component instanceof JTextComponent) {
+ if (component instanceof JTextArea) {
+ labelToArea((JTextArea) component);
+ } else {
+ labelToField((JTextComponent) component);
+ }
+ }
}
- else
- {
- if ( component instanceof JTextComponent )
- {
- if ( component instanceof JTextArea )
- {
- labelToArea( (JTextArea) component );
- }
- else
- {
- labelToField( (JTextComponent ) component );
- }
- }
- }
}
}
- }
+ }
- private void fieldToLabel( JTextComponent aTextField )
- {
- // turn on wrapping and disable editing and highlighting
+ private void fieldToLabel(JTextComponent aTextField) {
+ // turn on wrapping and disable editing and highlighting
- aTextField.setEditable(false);
- aTextField.setOpaque(false);
+ aTextField.setEditable(false);
+ aTextField.setOpaque(false);
- // Set the border, colors and font to that of a label
+ // Set the border, colors and font to that of a label
- LookAndFeel.installBorder(aTextField, "Label.border");
+ LookAndFeel.installBorder(aTextField, "Label.border");
- LookAndFeel.installColorsAndFont(aTextField,
- "Label.background",
- "Label.foreground",
- "Label.font");
- }
+ LookAndFeel.installColorsAndFont(aTextField, "Label.background", "Label.foreground", "Label.font");
+ }
- private void labelToField( JTextComponent aTextField )
- {
- // turn on wrapping and disable editing and highlighting
+ private void labelToField(JTextComponent aTextField) {
+ // turn on wrapping and disable editing and highlighting
- aTextField.setEditable(true);
- aTextField.setOpaque(true);
+ aTextField.setEditable(true);
+ aTextField.setOpaque(true);
- // Set the border, colors and font to that of a label
+ // Set the border, colors and font to that of a label
- LookAndFeel.installBorder(aTextField, "TextField.border");
+ LookAndFeel.installBorder(aTextField, "TextField.border");
- LookAndFeel.installColorsAndFont(aTextField,
- "TextField.background",
- "TextField.foreground",
- "TextField.font");
- }
+ LookAndFeel.installColorsAndFont(aTextField, "TextField.background", "TextField.foreground", "TextField.font");
+ }
- private void areaToLabel( JTextArea aTextArea )
- {
- // turn on wrapping and disable editing and highlighting
+ private void areaToLabel(JTextArea aTextArea) {
+ // turn on wrapping and disable editing and highlighting
- aTextArea.setLineWrap(true);
- aTextArea.setWrapStyleWord(true);
- aTextArea.setEditable(false);
+ aTextArea.setLineWrap(true);
+ aTextArea.setWrapStyleWord(true);
+ aTextArea.setEditable(false);
- // Set the text area's border, colors and font to
- // that of a label
+ // Set the text area's border, colors and font to
+ // that of a label
- LookAndFeel.installBorder(aTextArea, "Label.border");
+ LookAndFeel.installBorder(aTextArea, "Label.border");
- LookAndFeel.installColorsAndFont(aTextArea,
- "Label.background",
- "Label.foreground",
- "Label.font");
+ LookAndFeel.installColorsAndFont(aTextArea, "Label.background", "Label.foreground", "Label.font");
}
- private void labelToArea( JTextArea aTextArea )
- {
- // turn on wrapping and disable editing and highlighting
+ private void labelToArea(JTextArea aTextArea) {
+ // turn on wrapping and disable editing and highlighting
- aTextArea.setEditable(true);
+ aTextArea.setEditable(true);
- // Set the border, colors and font to that of a label
+ // Set the border, colors and font to that of a label
- LookAndFeel.installBorder(aTextArea, "TextArea.border");
+ LookAndFeel.installBorder(aTextArea, "TextArea.border");
- LookAndFeel.installColorsAndFont(aTextArea,
- "TextArea.background",
- "TextArea.foreground",
- "TextArea.font");
+ LookAndFeel.installColorsAndFont(aTextArea, "TextArea.background", "TextArea.foreground", "TextArea.font");
}
-
- /**
- * Forces this association to cause the object to
- * stop editing and validate the user's input.
- * @return false if there were problems validating,
- * or true to continue.
- */
- public boolean endEditing ()
- {
- if ( keyTimer != null )
- {
- keyTimer.stop();
- keyTimer.removeActionListener( this );
- keyTimer = null;
- }
+ /**
+ * Forces this association to cause the object to stop editing and validate the
+ * user's input.
+ *
+ * @return false if there were problems validating, or true to continue.
+ */
+ public boolean endEditing() {
+ if (keyTimer != null) {
+ keyTimer.stop();
+ keyTimer.removeActionListener(this);
+ keyTimer = null;
+ }
return writeValueToDisplayGroup();
- }
+ }
/**
- * Writes the value currently in the component
- * to the selected object in the display group
- * bound to the value aspect.
- * @return false if there were problems validating,
- * or true to continue.
- */
- protected boolean writeValueToDisplayGroup()
- {
- boolean returnValue = true;
- if ( hasDocument && !needsUpdate ) return true;
-
- EODisplayGroup displayGroup = valueDisplayGroup;
- if ( displayGroup != null )
- {
- String key = valueKey;
- Object component = object();
- Object value = null;
- try
- {
- //if ( getText.implementedByObject( component ) )
- //{
- value = getText.invoke( component );
- //}
- }
- catch ( Exception exc )
- {
- throw new WotonomyException(
- "Error updating display group", exc );
- }
-
- if ( ( wasNull ) && ( EMPTY_STRING.equals( value ) ) )
- {
- value = null;
- }
- else
- if ( format() != null )
- {
- try
- {
- value = format().parseObject( value.toString() );
- }
- catch ( ParseException exc )
- {
- String message = exc.getMessage();
- //"That format was not recognized.";
- if ( displayGroup.associationFailedToValidateValue(
- this, value.toString(), key, exc, message ) )
- {
- boolean wasListening = isListening;
- isListening = false;
- JOptionPane.showMessageDialog(
- (Component)component, message );
- isListening = wasListening;
- }
- needsUpdate = false;
- return false;
- }
- }
-
- needsUpdate = false;
-
- // only update if the value is different from the one in the display group
- Object existingValue = displayGroup.selectedObjectValueForKey( key );
- if ( displayGroup.selectedObjects().size() == 1 )
- {
- if ( existingValue == value ) return true;
- if ( ( existingValue != null ) && ( existingValue.equals( value ) ) ) return true;
- if ( ( value != null ) && ( value.equals( existingValue ) ) ) return true;
- }
-
- // we might lose focus if display group displays a validation message
- boolean wasListening = isListening;
- isListening = false;
-
- Iterator selectedIterator = displayGroup.selectionIndexes().iterator();
- while ( selectedIterator.hasNext() )
- {
- int index = ( (Integer)selectedIterator.next() ).intValue();
-
- if ( displayGroup.setValueForObjectAtIndex( value, index, key ) )
- {
- isListening = wasListening;
- needsUpdate = false;
- }
- else
- {
- isListening = wasListening;
- needsUpdate = false;
- returnValue = false;
- }
- }
-
- }
- return returnValue;
+ * Writes the value currently in the component to the selected object in the
+ * display group bound to the value aspect.
+ *
+ * @return false if there were problems validating, or true to continue.
+ */
+ protected boolean writeValueToDisplayGroup() {
+ boolean returnValue = true;
+ if (hasDocument && !needsUpdate)
+ return true;
+
+ EODisplayGroup displayGroup = valueDisplayGroup;
+ if (displayGroup != null) {
+ String key = valueKey;
+ Object component = object();
+ Object value = null;
+ try {
+ // if ( getText.implementedByObject( component ) )
+ // {
+ value = getText.invoke(component);
+ // }
+ } catch (Exception exc) {
+ throw new WotonomyException("Error updating display group", exc);
+ }
+
+ if ((wasNull) && (EMPTY_STRING.equals(value))) {
+ value = null;
+ } else if (format() != null) {
+ try {
+ value = format().parseObject(value.toString());
+ } catch (ParseException exc) {
+ String message = exc.getMessage();
+ // "That format was not recognized.";
+ if (displayGroup.associationFailedToValidateValue(this, value.toString(), key, exc, message)) {
+ boolean wasListening = isListening;
+ isListening = false;
+ JOptionPane.showMessageDialog((Component) component, message);
+ isListening = wasListening;
+ }
+ needsUpdate = false;
+ return false;
+ }
+ }
+
+ needsUpdate = false;
+
+ // only update if the value is different from the one in the display group
+ Object existingValue = displayGroup.selectedObjectValueForKey(key);
+ if (displayGroup.selectedObjects().size() == 1) {
+ if (existingValue == value)
+ return true;
+ if ((existingValue != null) && (existingValue.equals(value)))
+ return true;
+ if ((value != null) && (value.equals(existingValue)))
+ return true;
+ }
+
+ // we might lose focus if display group displays a validation message
+ boolean wasListening = isListening;
+ isListening = false;
+
+ Iterator selectedIterator = displayGroup.selectionIndexes().iterator();
+ while (selectedIterator.hasNext()) {
+ int index = ((Integer) selectedIterator.next()).intValue();
+
+ if (displayGroup.setValueForObjectAtIndex(value, index, key)) {
+ isListening = wasListening;
+ needsUpdate = false;
+ } else {
+ isListening = wasListening;
+ needsUpdate = false;
+ returnValue = false;
+ }
+ }
+
+ }
+ return returnValue;
}
/**
- * Sets the Format that is used to convert values from the display
- * group to and from text that is displayed in the component.
- * Having a formatter disables auto-updating.
- */
- public void setFormat( Format aFormat )
- {
+ * Sets the Format that is used to convert values from the display group to and
+ * from text that is displayed in the component. Having a formatter disables
+ * auto-updating.
+ */
+ public void setFormat(Format aFormat) {
format = aFormat;
}
/**
- * Gets the Format that is used to convert values from the display
- * group to and from text that is displayed in the component.
- */
- public Format format()
- {
+ * Gets the Format that is used to convert values from the display group to and
+ * from text that is displayed in the component.
+ */
+ public Format format() {
return format;
}
- // interface ActionListener
+ // interface ActionListener
/**
- * Updates object on action performed.
- */
- public void actionPerformed( ActionEvent evt )
- {
- if ( keyTimer != null )
- {
- keyTimer.stop();
- keyTimer.removeActionListener( this );
- keyTimer = null;
- }
- if ( ! isListening ) return;
- if ( needsUpdate )
- {
- writeValueToDisplayGroup();
- }
+ * Updates object on action performed.
+ */
+ public void actionPerformed(ActionEvent evt) {
+ if (keyTimer != null) {
+ keyTimer.stop();
+ keyTimer.removeActionListener(this);
+ keyTimer = null;
+ }
+ if (!isListening)
+ return;
+ if (needsUpdate) {
+ writeValueToDisplayGroup();
+ }
}
- // interface FocusListener
+ // interface FocusListener
/**
- * Notifies of beginning of edit.
- */
- public void focusGained(FocusEvent evt)
- {
- if ( ! isListening ) return;
+ * Notifies of beginning of edit.
+ */
+ public void focusGained(FocusEvent evt) {
+ if (!isListening)
+ return;
Object o;
EODisplayGroup displayGroup;
- Enumeration e = aspects().objectEnumerator();
- while ( e.hasMoreElements() )
- {
- displayGroup =
- displayGroupForAspect( e.nextElement().toString() );
- if ( displayGroup != null )
- {
- displayGroup.associationDidBeginEditing( this );
+ Enumeration e = aspects().objectEnumerator();
+ while (e.hasMoreElements()) {
+ displayGroup = displayGroupForAspect(e.nextElement().toString());
+ if (displayGroup != null) {
+ displayGroup.associationDidBeginEditing(this);
}
}
- }
+ }
/**
- * Updates object on focus lost and notifies of end of edit.
- */
- public void focusLost(FocusEvent evt)
- {
- if ( ! isListening ) return;
- if ( endEditing() )
- {
+ * Updates object on focus lost and notifies of end of edit.
+ */
+ public void focusLost(FocusEvent evt) {
+ if (!isListening)
+ return;
+ if (endEditing()) {
Object o;
EODisplayGroup displayGroup;
Enumeration e = aspects().objectEnumerator();
- while ( e.hasMoreElements() )
- {
- displayGroup =
- displayGroupForAspect( e.nextElement().toString() );
- if ( displayGroup != null )
- {
- displayGroup.associationDidEndEditing( this );
+ while (e.hasMoreElements()) {
+ displayGroup = displayGroupForAspect(e.nextElement().toString());
+ if (displayGroup != null) {
+ displayGroup.associationDidEndEditing(this);
}
}
- }
- else
- {
+ } else {
// probably should notify of a validation error here,
}
- }
-
- /**
- * Returns whether the data model is updated for every change
- * in the controlled component. If false, the data is only
- * updated on focus lost or the enter key. Default is true.
- */
- public boolean isAutoUpdating()
- {
- if ( format() != null ) return false;
- return autoUpdating;
- }
-
- /**
- * Sets whether the data model is updated for every change
- * in the controlled component.
- */
- public void setAutoUpdating( boolean isAutoUpdating )
- {
- autoUpdating = isAutoUpdating;
- }
-
- /**
- * Triggers the key timer to start.
- */
- protected void queueUpdate()
- {
- if ( isAutoUpdating() )
- {
- if ( keyTimer == null )
- {
- keyTimer = new Timer( interval, this );
- }
- keyTimer.restart();
- }
- }
-
- // interface DocumentListener
-
- public void insertUpdate(DocumentEvent e)
- {
- if ( ! isListening ) return;
- needsUpdate = true;
- queueUpdate();
- }
-
- public void removeUpdate(DocumentEvent e)
- {
- if ( ! isListening ) return;
- needsUpdate = true;
- queueUpdate();
- }
-
- public void changedUpdate(DocumentEvent e)
- {
- if ( ! isListening ) return;
- needsUpdate = true;
- queueUpdate();
- }
+ }
+
+ /**
+ * Returns whether the data model is updated for every change in the controlled
+ * component. If false, the data is only updated on focus lost or the enter key.
+ * Default is true.
+ */
+ public boolean isAutoUpdating() {
+ if (format() != null)
+ return false;
+ return autoUpdating;
+ }
+
+ /**
+ * Sets whether the data model is updated for every change in the controlled
+ * component.
+ */
+ public void setAutoUpdating(boolean isAutoUpdating) {
+ autoUpdating = isAutoUpdating;
+ }
+
+ /**
+ * Triggers the key timer to start.
+ */
+ protected void queueUpdate() {
+ if (isAutoUpdating()) {
+ if (keyTimer == null) {
+ keyTimer = new Timer(interval, this);
+ }
+ keyTimer.restart();
+ }
+ }
+
+ // interface DocumentListener
+
+ public void insertUpdate(DocumentEvent e) {
+ if (!isListening)
+ return;
+ needsUpdate = true;
+ queueUpdate();
+ }
+
+ public void removeUpdate(DocumentEvent e) {
+ if (!isListening)
+ return;
+ needsUpdate = true;
+ queueUpdate();
+ }
+
+ public void changedUpdate(DocumentEvent e) {
+ if (!isListening)
+ return;
+ needsUpdate = true;
+ queueUpdate();
+ }
}
/*
- * $Log$
- * Revision 1.2 2006/02/18 23:19:05 cgruber
- * Update imports and maven dependencies.
+ * $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.1 2006/02/16 13:22:22 cgruber Check in all sources in
+ * eclipse-friendly maven-enabled packages.
*
- * Revision 1.3 2004/01/28 18:34:57 mpowers
- * Better handling for enabling.
- * Now respecting enabledToSetSelectedObjectValueForKey from display group.
+ * Revision 1.3 2004/01/28 18:34:57 mpowers Better handling for enabling. Now
+ * respecting enabledToSetSelectedObjectValueForKey from display group.
*
- * Revision 1.2 2003/08/06 23:07:52 chochos
- * general code cleanup (mostly, removing unused imports)
+ * Revision 1.2 2003/08/06 23:07:52 chochos general code cleanup (mostly,
+ * removing unused imports)
*
- * Revision 1.1 2001/12/20 18:57:24 mpowers
- * (Re-)Contributing TimedTextAssociation. Just like TA, except uses timers.
+ * Revision 1.1 2001/12/20 18:57:24 mpowers (Re-)Contributing
+ * TimedTextAssociation. Just like TA, except uses timers.
*
- * Revision 1.20 2001/10/26 19:58:06 mpowers
- * Better handling for non-string types. We were testing with equals with the
- * new value against the existing value in the component. Now we convert
- * the new value to a string before comparing. Fixes case for properties
- * of non-String types, like StringBuffer.
+ * Revision 1.20 2001/10/26 19:58:06 mpowers Better handling for non-string
+ * types. We were testing with equals with the new value against the existing
+ * value in the component. Now we convert the new value to a string before
+ * comparing. Fixes case for properties of non-String types, like StringBuffer.
*
- * Revision 1.19 2001/09/30 21:57:14 mpowers
- * Timers were not getting cleaned up if breakConnection was called
- * before the timer got a chance to fire.
+ * Revision 1.19 2001/09/30 21:57:14 mpowers Timers were not getting cleaned up
+ * if breakConnection was called before the timer got a chance to fire.
*
- * Revision 1.18 2001/08/22 15:42:26 mpowers
- * Added support for JTextComponent label-izing.
+ * Revision 1.18 2001/08/22 15:42:26 mpowers Added support for JTextComponent
+ * label-izing.
*
- * Revision 1.17 2001/07/30 16:32:55 mpowers
- * Implemented support for bulk-editing. Detail associations will now
- * apply changes to all selected objects.
+ * Revision 1.17 2001/07/30 16:32:55 mpowers Implemented support for
+ * bulk-editing. Detail associations will now apply changes to all selected
+ * objects.
*
- * Revision 1.16 2001/07/17 19:53:37 mpowers
- * Made some private fields protected for benefit of subclassers.
+ * Revision 1.16 2001/07/17 19:53:37 mpowers Made some private fields protected
+ * for benefit of subclassers.
*
- * Revision 1.15 2001/06/30 14:59:36 mpowers
- * LabelAspect now sets the text field's opaque setting.
+ * Revision 1.15 2001/06/30 14:59:36 mpowers LabelAspect now sets the text
+ * field's opaque setting.
*
- * Revision 1.14 2001/06/29 14:54:08 mpowers
- * Another fix for timers - timers were definitely causing a memory leak.
+ * Revision 1.14 2001/06/29 14:54:08 mpowers Another fix for timers - timers
+ * were definitely causing a memory leak.
*
- * Revision 1.13 2001/06/26 21:37:19 mpowers
- * Fixed a null pointer in the new key timer scheme.
+ * Revision 1.13 2001/06/26 21:37:19 mpowers Fixed a null pointer in the new key
+ * timer scheme.
*
- * Revision 1.12 2001/06/25 14:46:03 mpowers
- * Fixed a memory leak involving the use of timers.
+ * Revision 1.12 2001/06/25 14:46:03 mpowers Fixed a memory leak involving the
+ * use of timers.
*
- * Revision 1.11 2001/06/01 19:14:59 mpowers
- * Text association's enabled aspect is now more discriminating.
+ * Revision 1.11 2001/06/01 19:14:59 mpowers Text association's enabled aspect
+ * is now more discriminating.
*
- * Revision 1.10 2001/05/18 21:07:24 mpowers
- * Changed the way we handle failure to update object value.
+ * Revision 1.10 2001/05/18 21:07:24 mpowers Changed the way we handle failure
+ * to update object value.
*
- * Revision 1.9 2001/03/13 21:39:58 mpowers
- * Improved validation handling.
+ * Revision 1.9 2001/03/13 21:39:58 mpowers Improved validation handling.
*
- * Revision 1.8 2001/03/12 12:49:10 mpowers
- * Improved validation handling.
- * Having a formatter disables auto-updating.
+ * Revision 1.8 2001/03/12 12:49:10 mpowers Improved validation handling. Having
+ * a formatter disables auto-updating.
*
- * Revision 1.7 2001/03/09 22:08:13 mpowers
- * Now handling any objects that have a valid Document.
- * No longer checking enabled before updating the enabled state.
+ * Revision 1.7 2001/03/09 22:08:13 mpowers Now handling any objects that have a
+ * valid Document. No longer checking enabled before updating the enabled state.
*
- * Revision 1.6 2001/03/07 19:57:32 mpowers
- * Fixed paste error in IconAspect.
+ * Revision 1.6 2001/03/07 19:57:32 mpowers Fixed paste error in IconAspect.
*
- * Revision 1.4 2001/02/17 16:52:05 mpowers
- * Changes in imports to support building with jdk1.1 collections.
+ * Revision 1.4 2001/02/17 16:52:05 mpowers Changes in imports to support
+ * building with jdk1.1 collections.
*
- * Revision 1.3 2001/01/31 19:12:33 mpowers
- * Implemented auto-updating in TextComponent.
+ * Revision 1.3 2001/01/31 19:12:33 mpowers Implemented auto-updating in
+ * TextComponent.
*
- * Revision 1.2 2001/01/10 15:53:58 mpowers
- * Preventing a null pointer exception if getText were to return null,
- * which doesn't happen for JTextFields but might happen for other objects.
+ * Revision 1.2 2001/01/10 15:53:58 mpowers Preventing a null pointer exception
+ * if getText were to return null, which doesn't happen for JTextFields but
+ * might happen for other objects.
*
- * Revision 1.1.1.1 2000/12/21 15:49:08 mpowers
- * Contributing wotonomy.
+ * Revision 1.1.1.1 2000/12/21 15:49:08 mpowers Contributing wotonomy.
*
- * Revision 1.13 2000/12/20 16:25:41 michael
- * Added log to all files.
+ * Revision 1.13 2000/12/20 16:25:41 michael Added log to all files.
*
*
*/
-
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/TreeAssociation.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/TreeAssociation.java
index 728643b..b0e3b09 100644
--- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/TreeAssociation.java
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/TreeAssociation.java
@@ -39,172 +39,144 @@ import net.wotonomy.foundation.NSMutableArray;
import net.wotonomy.ui.EODisplayGroup;
/**
-* TreeAssociation is a TreeModelAssociation further
-* customized for JTrees. It binds a JTree to a display group's
-* list of displayable objects, each of which may have
-* a list of child objects managed by another display
-* group, and so on. TreeAssociation works exactly
-* like a ListAssociation, with the additional capability
-* to specify a "Children" aspect, that will allow child
-* objects to be retrieved from a parent display group.
-* Note that the children aspect requires the bound
-* display group to have a DataSource that can vend a
-* DataSource appropriate for the bound key That data
-* source is then used to create data sources for
-* child nodes, and so on.
-*
-* <ul>
-*
-* <li>titles: a property convertable to a string for
-* display in the nodes of the tree. The objects in
-* the bound display group will be used to populate the
-* initial root nodes of the tree (more accurately,
-* children of the offscreen root node in the tree).</li>
-*
-* <li>children: a property of a node value that returns
-* zero, one or many objects, each of which will correspond
-* to a child node for the corresponding node in the tree.
-* The data source of the bound display group is replaced
-* a data source that populates the display group with
-* the visible nodes in the tree component as determined by
-* calling fetchObjectsIntoChildrenGroup.
-* If this aspect is not bound, the tree behaves like a list.
-* <br><br>
-* Binding this aspect with a null display group is the same
-* as binding it with the titles display group.
-* In this configuration the contents of the titles
-* display group will be replaced with the visible nodes in the
-* tree component, as specified above, replacing the existing
-* data source.
-* <br><br>
-* In that case, the display groups for the nodes in
-* the tree will still use the original data source
-* for resolving their children key, and programmatically
-* setting the contents of the display group will still
-* repopulate the root nodes of the tree.
-* </li>
-*
-* <li>isLeaf: a property of a node value that returns
-* a value convertable to a boolean value (aside from
-* an actual boolean value, zeroes evaluate to true,
-* as does any String containing "yes" or "true" or that
-* is convertable to a number equal to zero; other values
-* evaluate to false).
-* <br><br>
-* If the isLeaf aspect is not bound,
-* the tree must force nodes to load their children to
-* determine whether they are leaf nodes (in effect
-* loading the grandchildren for any expanded node).
-* If bound, child loading is deferred until the node
-* is actually expanded.
-* <br><br>
-* For example, binding this value to a null
-* display group and the key "false" will result in a
-* deferred-loading tree that works much like Windows
-* Explorer's network volume browser - all nodes appear
-* with "pluses" until they are expanded.
-* <br><br>
-* Note that the display group is ignored: the property
-* will be applied directly to the object corresponding
-* to the node.</li>
-*
-* </ul>
-*
-* All other usage is as TreeModelAssociation.
-*
-* @author michael@mpowers.net
-* @author $Author: cgruber $
-* @version $Revision: 904 $
-*/
+ * TreeAssociation is a TreeModelAssociation further customized for JTrees. It
+ * binds a JTree to a display group's list of displayable objects, each of which
+ * may have a list of child objects managed by another display group, and so on.
+ * TreeAssociation works exactly like a ListAssociation, with the additional
+ * capability to specify a "Children" aspect, that will allow child objects to
+ * be retrieved from a parent display group. Note that the children aspect
+ * requires the bound display group to have a DataSource that can vend a
+ * DataSource appropriate for the bound key That data source is then used to
+ * create data sources for child nodes, and so on.
+ *
+ * <ul>
+ *
+ * <li>titles: a property convertable to a string for display in the nodes of
+ * the tree. The objects in the bound display group will be used to populate the
+ * initial root nodes of the tree (more accurately, children of the offscreen
+ * root node in the tree).</li>
+ *
+ * <li>children: a property of a node value that returns zero, one or many
+ * objects, each of which will correspond to a child node for the corresponding
+ * node in the tree. The data source of the bound display group is replaced a
+ * data source that populates the display group with the visible nodes in the
+ * tree component as determined by calling fetchObjectsIntoChildrenGroup. If
+ * this aspect is not bound, the tree behaves like a list. <br>
+ * <br>
+ * Binding this aspect with a null display group is the same as binding it with
+ * the titles display group. In this configuration the contents of the titles
+ * display group will be replaced with the visible nodes in the tree component,
+ * as specified above, replacing the existing data source. <br>
+ * <br>
+ * In that case, the display groups for the nodes in the tree will still use the
+ * original data source for resolving their children key, and programmatically
+ * setting the contents of the display group will still repopulate the root
+ * nodes of the tree.</li>
+ *
+ * <li>isLeaf: a property of a node value that returns a value convertable to a
+ * boolean value (aside from an actual boolean value, zeroes evaluate to true,
+ * as does any String containing "yes" or "true" or that is convertable to a
+ * number equal to zero; other values evaluate to false). <br>
+ * <br>
+ * If the isLeaf aspect is not bound, the tree must force nodes to load their
+ * children to determine whether they are leaf nodes (in effect loading the
+ * grandchildren for any expanded node). If bound, child loading is deferred
+ * until the node is actually expanded. <br>
+ * <br>
+ * For example, binding this value to a null display group and the key "false"
+ * will result in a deferred-loading tree that works much like Windows
+ * Explorer's network volume browser - all nodes appear with "pluses" until they
+ * are expanded. <br>
+ * <br>
+ * Note that the display group is ignored: the property will be applied directly
+ * to the object corresponding to the node.</li>
+ *
+ * </ul>
+ *
+ * All other usage is as TreeModelAssociation.
+ *
+ * @author michael@mpowers.net
+ * @author $Author: cgruber $
+ * @version $Revision: 904 $
+ */
public class TreeAssociation extends TreeModelAssociation
- implements FocusListener, TreeExpansionListener, TreeWillExpandListener, Runnable
-{
- private boolean isExpanding;
- private Vector nodeQueue;
- private Vector structureQueue;
- private boolean isRunning;
-
- /**
- * Constructor expecting a JTree.
- */
- public TreeAssociation ( Object anObject )
- {
- super( anObject );
- init();
- }
-
- /**
- * Constructor expecting a JTree or similar component
- * and specifying a label for the root node.
- */
- public TreeAssociation( Object anObject, Object aRootLabel )
- {
- super( anObject );
- init();
- rootLabel = aRootLabel;
- rootNode.setUserObject( aRootLabel );
- }
-
- // convenience
- private JTree component()
- {
- return (JTree) object();
- }
-
- /**
- * Called by both constructors.
- */
- protected void init()
- {
- isExpanding = false;
- isRunning = false;
- nodeQueue = new Vector();
- structureQueue = new Vector();
- component().addFocusListener( this );
- component().addTreeExpansionListener( this );
- component().addTreeWillExpandListener( this );
- }
-
- /**
- * Returns whether this class can control the specified
- * object.
- */
- public static boolean isUsableWithObject ( Object anObject )
- {
- return ( anObject instanceof JTree );
- }
-
- /**
- * Overridden to not fire events during initial population.
- */
- public void establishConnection ()
- {
- isExpanding = true;
- super.establishConnection();
- isExpanding = false;
- }
-
- // interface TreeSelectionListener
-
- public void valueChanged(TreeSelectionEvent e)
- {
- if ( ! isListening ) return;
+ implements FocusListener, TreeExpansionListener, TreeWillExpandListener, Runnable {
+ private boolean isExpanding;
+ private Vector nodeQueue;
+ private Vector structureQueue;
+ private boolean isRunning;
+
+ /**
+ * Constructor expecting a JTree.
+ */
+ public TreeAssociation(Object anObject) {
+ super(anObject);
+ init();
+ }
+
+ /**
+ * Constructor expecting a JTree or similar component and specifying a label for
+ * the root node.
+ */
+ public TreeAssociation(Object anObject, Object aRootLabel) {
+ super(anObject);
+ init();
+ rootLabel = aRootLabel;
+ rootNode.setUserObject(aRootLabel);
+ }
+
+ // convenience
+ private JTree component() {
+ return (JTree) object();
+ }
+
+ /**
+ * Called by both constructors.
+ */
+ protected void init() {
+ isExpanding = false;
+ isRunning = false;
+ nodeQueue = new Vector();
+ structureQueue = new Vector();
+ component().addFocusListener(this);
+ component().addTreeExpansionListener(this);
+ component().addTreeWillExpandListener(this);
+ }
+
+ /**
+ * Returns whether this class can control the specified object.
+ */
+ public static boolean isUsableWithObject(Object anObject) {
+ return (anObject instanceof JTree);
+ }
+
+ /**
+ * Overridden to not fire events during initial population.
+ */
+ public void establishConnection() {
+ isExpanding = true;
+ super.establishConnection();
+ isExpanding = false;
+ }
+
+ // interface TreeSelectionListener
+
+ public void valueChanged(TreeSelectionEvent e) {
+ if (!isListening)
+ return;
// NOTE: This approach causes focus rectangle to perceptibly trail
// the selection rectangle, presumably because we're called after the
// new selection has been processed but before the focus is processed.
// Users don't like it, so we're going with a preference to use
// invokeLater.
-/*
- // paint immediately before updating the display group and
- // before any potentially lengthy second-order effects happen:
- // this improves user-perceived responsiveness of big apps
- if ( object() instanceof javax.swing.JComponent )
- {
- javax.swing.JComponent component = (javax.swing.JComponent)object();
- component.paintImmediately( component.getBounds() );
- }
- selectFromSelectionModel();
-*/
+ /*
+ * // paint immediately before updating the display group and // before any
+ * potentially lengthy second-order effects happen: // this improves
+ * user-perceived responsiveness of big apps if ( object() instanceof
+ * javax.swing.JComponent ) { javax.swing.JComponent component =
+ * (javax.swing.JComponent)object(); component.paintImmediately(
+ * component.getBounds() ); } selectFromSelectionModel();
+ */
// NOTE: This approach uses invoke later to cause the update of
// the display group (which could be lengthly if that in turn
@@ -214,369 +186,291 @@ public class TreeAssociation extends TreeModelAssociation
// will have to do a similar invoke later if they check the display
// group.
- Runnable select = new Runnable()
- {
- public void run()
- {
- selectFromSelectionModel();
- }
- };
-
- if ( selectionPaintedImmediately )
- {
- if ( object() instanceof java.awt.Component )
- {
- ((java.awt.Component)object()).repaint();
- }
- EventQueue.invokeLater( select );
- }
- else
- {
- select.run();
- }
- }
-
- /**
- * Overridden to check whether the node is visible
- * in the tree on screen. Offscreen in a scrollpane
- * does not count.
- */
- public boolean isVisible(Object node)
- {
- JTree tree = (JTree) object();
- TreePath path = ((DisplayGroupNode)node).treePath();
- if ( tree.isVisible( path ) )
- {
- Rectangle rowRect = tree.getPathBounds( path );
- if ( rowRect != null )
- {
- Rectangle visible = tree.getVisibleRect();
- if ( visible != null )
- {
+ Runnable select = new Runnable() {
+ public void run() {
+ selectFromSelectionModel();
+ }
+ };
+
+ if (selectionPaintedImmediately) {
+ if (object() instanceof java.awt.Component) {
+ ((java.awt.Component) object()).repaint();
+ }
+ EventQueue.invokeLater(select);
+ } else {
+ select.run();
+ }
+ }
+
+ /**
+ * Overridden to check whether the node is visible in the tree on screen.
+ * Offscreen in a scrollpane does not count.
+ */
+ public boolean isVisible(Object node) {
+ JTree tree = (JTree) object();
+ TreePath path = ((DisplayGroupNode) node).treePath();
+ if (tree.isVisible(path)) {
+ Rectangle rowRect = tree.getPathBounds(path);
+ if (rowRect != null) {
+ Rectangle visible = tree.getVisibleRect();
+ if (visible != null) {
//System.out.println( "isVisible: intersects: " + visible.intersects( rowRect ) );
- return visible.intersects( rowRect );
- }
- }
- }
+ return visible.intersects(rowRect);
+ }
+ }
+ }
//System.out.println( "isVisible: false" );
- return false;
- }
-
- /**
- * Fires a tree nodes changed event to all listeners.
- * Provided as a convenience if you need to make manual
- * changes to the tree model.
- */
- public void fireTreeNodesChanged(final Object source,
- final Object[] path,
- final int[] childIndices,
- final Object[] children)
- {
- if ( !isExpanding )
- {
- for ( int i = 0; i < children.length; i++ )
- {
- nodeQueue.add( children[i] );
- }
- if ( !isRunning )
- {
- isRunning = true;
- EventQueue.invokeLater( this );
- }
- }
- }
-
- /**
- * Fires a tree nodes inserted event to all listeners.
- * Provided as a convenience if you need to make manual
- * changes to the tree model.
- */
- public void fireTreeNodesInserted(Object source,
- Object[] path,
- int[] childIndices,
- Object[] children)
- {
- if ( !isExpanding )
- {
- super.fireTreeNodesInserted( source, path, childIndices, children );
- EventQueue.invokeLater( this );
- }
- }
-
- /**
- * Fires a tree nodes removed event to all listeners.
- * Provided as a convenience if you need to make manual
- * changes to the tree model.
- */
- public void fireTreeNodesRemoved(Object source,
- Object[] path,
- int[] childIndices,
- Object[] children)
- {
- if ( !isExpanding )
- {
- super.fireTreeNodesRemoved( source, path, childIndices, children );
- EventQueue.invokeLater( this );
- }
- }
-
- /**
- * Fires a tree structure changed event to all listeners.
- * Provided as a convenience if you need to make manual
- * changes to the tree model.
- */
- public void fireTreeStructureChanged(Object source,
- Object[] path,
- int[] childIndices,
- Object[] children)
- {
- if ( !isExpanding )
- {
- structureQueue.add( path[path.length-1] );
- if ( !isRunning )
- {
- isRunning = true;
- EventQueue.invokeLater( this );
- }
- }
- }
-
- /**
- * Overridden to return all visible rows in the tree.
- */
- public NSArray objectsFetchedIntoChildrenGroup()
- {
- JTree tree = (JTree) object();
- NSMutableArray objectList = new NSMutableArray();
-
- int count = tree.getRowCount();
- for ( int i = 0; i < count; i++ )
- {
- objectList.add(
- ((DisplayGroupNode) tree.getPathForRow( i ).getLastPathComponent()).object() );
- }
+ return false;
+ }
+
+ /**
+ * Fires a tree nodes changed event to all listeners. Provided as a convenience
+ * if you need to make manual changes to the tree model.
+ */
+ public void fireTreeNodesChanged(final Object source, final Object[] path, final int[] childIndices,
+ final Object[] children) {
+ if (!isExpanding) {
+ for (int i = 0; i < children.length; i++) {
+ nodeQueue.add(children[i]);
+ }
+ if (!isRunning) {
+ isRunning = true;
+ EventQueue.invokeLater(this);
+ }
+ }
+ }
+
+ /**
+ * Fires a tree nodes inserted event to all listeners. Provided as a convenience
+ * if you need to make manual changes to the tree model.
+ */
+ public void fireTreeNodesInserted(Object source, Object[] path, int[] childIndices, Object[] children) {
+ if (!isExpanding) {
+ super.fireTreeNodesInserted(source, path, childIndices, children);
+ EventQueue.invokeLater(this);
+ }
+ }
+
+ /**
+ * Fires a tree nodes removed event to all listeners. Provided as a convenience
+ * if you need to make manual changes to the tree model.
+ */
+ public void fireTreeNodesRemoved(Object source, Object[] path, int[] childIndices, Object[] children) {
+ if (!isExpanding) {
+ super.fireTreeNodesRemoved(source, path, childIndices, children);
+ EventQueue.invokeLater(this);
+ }
+ }
+
+ /**
+ * Fires a tree structure changed event to all listeners. Provided as a
+ * convenience if you need to make manual changes to the tree model.
+ */
+ public void fireTreeStructureChanged(Object source, Object[] path, int[] childIndices, Object[] children) {
+ if (!isExpanding) {
+ structureQueue.add(path[path.length - 1]);
+ if (!isRunning) {
+ isRunning = true;
+ EventQueue.invokeLater(this);
+ }
+ }
+ }
+
+ /**
+ * Overridden to return all visible rows in the tree.
+ */
+ public NSArray objectsFetchedIntoChildrenGroup() {
+ JTree tree = (JTree) object();
+ NSMutableArray objectList = new NSMutableArray();
+
+ int count = tree.getRowCount();
+ for (int i = 0; i < count; i++) {
+ objectList.add(((DisplayGroupNode) tree.getPathForRow(i).getLastPathComponent()).object());
+ }
//new net.wotonomy.ui.swing.util.StackTraceInspector( Integer.toString( objectList.size() ) );
- return objectList;
- }
+ return objectList;
+ }
+
+ // interface FocusListener
- // interface FocusListener
-
/**
- * Notifies of beginning of edit.
- */
- public void focusGained(FocusEvent evt)
- {
+ * Notifies of beginning of edit.
+ */
+ public void focusGained(FocusEvent evt) {
Object o;
EODisplayGroup displayGroup;
- Enumeration e = aspects().objectEnumerator();
- while ( e.hasMoreElements() )
- {
- displayGroup =
- displayGroupForAspect( e.nextElement().toString() );
- if ( displayGroup != null )
- {
- displayGroup.associationDidBeginEditing( this );
+ Enumeration e = aspects().objectEnumerator();
+ while (e.hasMoreElements()) {
+ displayGroup = displayGroupForAspect(e.nextElement().toString());
+ if (displayGroup != null) {
+ displayGroup.associationDidBeginEditing(this);
}
}
- }
+ }
/**
- * Updates object on focus lost and notifies of end of edit.
- */
- public void focusLost(FocusEvent evt)
- {
- if ( ! component().isEditing() )
- {
- Object o;
- EODisplayGroup displayGroup;
- Enumeration e = aspects().objectEnumerator();
- while ( e.hasMoreElements() )
- {
- displayGroup =
- displayGroupForAspect( e.nextElement().toString() );
- if ( displayGroup != null )
- {
- displayGroup.associationDidEndEditing( this );
- }
- }
- }
- }
-
- // interface TreeWillExpandListener
-
- public void treeWillExpand(TreeExpansionEvent event)
- throws ExpandVetoException
- {
- isExpanding = true;
- }
-
- public void treeWillCollapse(TreeExpansionEvent event)
- throws ExpandVetoException
- {
- // do nothing
- }
-
- // interface TreeExpansionListener
-
- /**
- * Updates the children display group, if any.
- */
- public void treeExpanded(TreeExpansionEvent event)
- { //System.out.println( "treeExpanded: " + event.getPath().getLastPathComponent() );
- isExpanding = false;
- if ( childrenDisplayGroup != null )
- {
- removeAsListener(); // prevent data source refetch: see fetchObjects()
- childrenDisplayGroup.fetch();
- addAsListener();
- }
- }
-
- /**
- * Updates the children display group, if any.
- */
- public void treeCollapsed(TreeExpansionEvent event)
- {
- if ( childrenDisplayGroup != null )
- {
- removeAsListener(); // prevent data source refetch: see fetchObjects()
- childrenDisplayGroup.fetch();
- addAsListener();
- }
- }
-
- // interface Runnable
-
- /**
- * Fires any queued node changed and structure changed events.
- * Typically invoked on a delayed event loop.
- */
- public void run()
- {
- DisplayGroupNode node;
- int index;
- Iterator i;
-
- i = nodeQueue.iterator();
- while ( i.hasNext() )
- {
- node = (DisplayGroupNode) i.next();
- index = ((DisplayGroupNode)node.parentGroup).getIndex( node );
- if ( ( index != -1 )
- && ( node.treePath().getParentPath() != null ) )
- {
- super.fireTreeNodesChanged(
- node,
- node.treePath().getParentPath().getPath(),
- new int[] { index },
- new Object[] { node } );
- }
- }
- nodeQueue.clear();
-
- i = structureQueue.iterator();
- while ( i.hasNext() )
- {
- node = (DisplayGroupNode) i.next();
- super.fireTreeStructureChanged(
- node,
- node.treePath().getPath(),
- null,
- null );
- }
- structureQueue.clear();
-
- isRunning = false;
-/*
- EventQueue.invokeLater( new Runnable() { public void run() {
- ((JTree)object()).treeDidChange();
- ((JTree)object()).getParent().invalidate();
- ((JTree)object()).getParent().validate();
- ((JTree)object()).getParent().update( ((JTree)object()).getGraphics() );
-
-// ((JTree)object()).getParent().doLayout();
-// ((JTree)object()).getParent().repaint();
-// ((JTree)object()).repaint();
-// ((JTree)object()).updateUI();
- } } );
-*/
- }
+ * Updates object on focus lost and notifies of end of edit.
+ */
+ public void focusLost(FocusEvent evt) {
+ if (!component().isEditing()) {
+ Object o;
+ EODisplayGroup displayGroup;
+ Enumeration e = aspects().objectEnumerator();
+ while (e.hasMoreElements()) {
+ displayGroup = displayGroupForAspect(e.nextElement().toString());
+ if (displayGroup != null) {
+ displayGroup.associationDidEndEditing(this);
+ }
+ }
+ }
+ }
+
+ // interface TreeWillExpandListener
+
+ public void treeWillExpand(TreeExpansionEvent event) throws ExpandVetoException {
+ isExpanding = true;
+ }
+
+ public void treeWillCollapse(TreeExpansionEvent event) throws ExpandVetoException {
+ // do nothing
+ }
+
+ // interface TreeExpansionListener
+
+ /**
+ * Updates the children display group, if any.
+ */
+ public void treeExpanded(TreeExpansionEvent event) { // System.out.println( "treeExpanded: " +
+ // event.getPath().getLastPathComponent() );
+ isExpanding = false;
+ if (childrenDisplayGroup != null) {
+ removeAsListener(); // prevent data source refetch: see fetchObjects()
+ childrenDisplayGroup.fetch();
+ addAsListener();
+ }
+ }
+
+ /**
+ * Updates the children display group, if any.
+ */
+ public void treeCollapsed(TreeExpansionEvent event) {
+ if (childrenDisplayGroup != null) {
+ removeAsListener(); // prevent data source refetch: see fetchObjects()
+ childrenDisplayGroup.fetch();
+ addAsListener();
+ }
+ }
+
+ // interface Runnable
+
+ /**
+ * Fires any queued node changed and structure changed events. Typically invoked
+ * on a delayed event loop.
+ */
+ public void run() {
+ DisplayGroupNode node;
+ int index;
+ Iterator i;
+
+ i = nodeQueue.iterator();
+ while (i.hasNext()) {
+ node = (DisplayGroupNode) i.next();
+ index = ((DisplayGroupNode) node.parentGroup).getIndex(node);
+ if ((index != -1) && (node.treePath().getParentPath() != null)) {
+ super.fireTreeNodesChanged(node, node.treePath().getParentPath().getPath(), new int[] { index },
+ new Object[] { node });
+ }
+ }
+ nodeQueue.clear();
+
+ i = structureQueue.iterator();
+ while (i.hasNext()) {
+ node = (DisplayGroupNode) i.next();
+ super.fireTreeStructureChanged(node, node.treePath().getPath(), null, null);
+ }
+ structureQueue.clear();
+
+ isRunning = false;
+ /*
+ * EventQueue.invokeLater( new Runnable() { public void run() {
+ * ((JTree)object()).treeDidChange();
+ * ((JTree)object()).getParent().invalidate();
+ * ((JTree)object()).getParent().validate();
+ * ((JTree)object()).getParent().update( ((JTree)object()).getGraphics() );
+ *
+ * // ((JTree)object()).getParent().doLayout(); //
+ * ((JTree)object()).getParent().repaint(); // ((JTree)object()).repaint(); //
+ * ((JTree)object()).updateUI(); } } );
+ */
+ }
}
/*
- * $Log$
- * Revision 1.2 2006/02/18 23:19:05 cgruber
- * Update imports and maven dependencies.
+ * $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.1 2006/02/16 13:22:22 cgruber Check in all sources in
+ * eclipse-friendly maven-enabled packages.
*
- * Revision 1.55 2004/02/05 02:18:50 mpowers
- * Super was calling back into this class before init() was called.
+ * Revision 1.55 2004/02/05 02:18:50 mpowers Super was calling back into this
+ * class before init() was called.
*
- * Revision 1.54 2003/08/06 23:07:52 chochos
- * general code cleanup (mostly, removing unused imports)
+ * Revision 1.54 2003/08/06 23:07:52 chochos general code cleanup (mostly,
+ * removing unused imports)
*
- * Revision 1.53 2003/06/03 14:49:48 mpowers
- * Now correctly calculating isVisible based on the component visible rect.
- * Now deferring node changed events to a later event queue to allow repaints
- * to happen after all changes have taken effect.
+ * Revision 1.53 2003/06/03 14:49:48 mpowers Now correctly calculating isVisible
+ * based on the component visible rect. Now deferring node changed events to a
+ * later event queue to allow repaints to happen after all changes have taken
+ * effect.
*
- * Revision 1.52 2002/05/03 21:41:18 mpowers
- * No longer clearing the selection model when updating from display group:
- * we now only modify if a change needs to be made.
- * No longer listening for selection change during firing of delete events:
- * delete events cause JTree's to update their selection model.
- * Fix for paintsSelectionImmediately: TreeAssociation.processRecentChanges()
- * must happen after the screen is painted, or the selection is not displayed.
+ * Revision 1.52 2002/05/03 21:41:18 mpowers No longer clearing the selection
+ * model when updating from display group: we now only modify if a change needs
+ * to be made. No longer listening for selection change during firing of delete
+ * events: delete events cause JTree's to update their selection model. Fix for
+ * paintsSelectionImmediately: TreeAssociation.processRecentChanges() must
+ * happen after the screen is painted, or the selection is not displayed.
*
- * Revision 1.51 2002/04/19 21:18:45 mpowers
- * Removed tree event coalescing, which was causing way too many problems.
- * The fireChangeEvent algorithm is way faster than before, so we should
- * still be better off than before. At least now, we don't have to track
- * whether the view component has encountered a particular node.
+ * Revision 1.51 2002/04/19 21:18:45 mpowers Removed tree event coalescing,
+ * which was causing way too many problems. The fireChangeEvent algorithm is way
+ * faster than before, so we should still be better off than before. At least
+ * now, we don't have to track whether the view component has encountered a
+ * particular node.
*
- * Revision 1.49 2002/04/12 21:05:58 mpowers
- * Now distinguishing changes in titles group even better.
+ * Revision 1.49 2002/04/12 21:05:58 mpowers Now distinguishing changes in
+ * titles group even better.
*
- * Revision 1.48 2002/04/12 20:36:31 mpowers
- * Now distinguishing between changes made on titles group by tree expansion
- * versus external changes which should cause us to repopulate root nodes.
+ * Revision 1.48 2002/04/12 20:36:31 mpowers Now distinguishing between changes
+ * made on titles group by tree expansion versus external changes which should
+ * cause us to repopulate root nodes.
*
- * Revision 1.47 2002/04/10 21:20:04 mpowers
- * Better handling for tree nodes when working with editing contexts.
- * Better handling for invalidation. No longer broadcasting events
- * when nodes have not been "registered" in the tree.
+ * Revision 1.47 2002/04/10 21:20:04 mpowers Better handling for tree nodes when
+ * working with editing contexts. Better handling for invalidation. No longer
+ * broadcasting events when nodes have not been "registered" in the tree.
*
- * Revision 1.46 2002/03/27 20:44:53 mpowers
- * Added isVisible test for node.
+ * Revision 1.46 2002/03/27 20:44:53 mpowers Added isVisible test for node.
*
- * Revision 1.45 2002/03/08 23:19:57 mpowers
- * Refactoring of DelegatingTreeDataSource to facilitate binding of titles
- * and children aspects to the same display group.
+ * Revision 1.45 2002/03/08 23:19:57 mpowers Refactoring of
+ * DelegatingTreeDataSource to facilitate binding of titles and children aspects
+ * to the same display group.
*
- * Revision 1.44 2002/03/07 23:04:36 mpowers
- * Refining TreeColumnAssociation.
+ * Revision 1.44 2002/03/07 23:04:36 mpowers Refining TreeColumnAssociation.
*
- * Revision 1.43 2002/03/06 13:04:15 mpowers
- * Implemented cascading qualifiers in tree nodes.
+ * Revision 1.43 2002/03/06 13:04:15 mpowers Implemented cascading qualifiers in
+ * tree nodes.
*
- * Revision 1.42 2002/03/05 23:18:28 mpowers
- * Added documentation.
- * Added isSelectionPaintedImmediate and isSelectionTracking attributes
- * to TableAssociation.
- * Added getTableAssociation to TableColumnAssociation.
+ * Revision 1.42 2002/03/05 23:18:28 mpowers Added documentation. Added
+ * isSelectionPaintedImmediate and isSelectionTracking attributes to
+ * TableAssociation. Added getTableAssociation to TableColumnAssociation.
*
- * Revision 1.41 2002/03/01 23:42:08 mpowers
- * Implemented TreeColumnAssociation, and updated documentation.
+ * Revision 1.41 2002/03/01 23:42:08 mpowers Implemented TreeColumnAssociation,
+ * and updated documentation.
*
- * Revision 1.40 2002/03/01 20:41:39 mpowers
- * Now a focus listener and an expansion listener.
+ * Revision 1.40 2002/03/01 20:41:39 mpowers Now a focus listener and an
+ * expansion listener.
*
- * Revision 1.39 2002/02/27 23:19:17 mpowers
- * Refactoring of TreeAssociation to create TreeModelAssociation parent.
+ * Revision 1.39 2002/02/27 23:19:17 mpowers Refactoring of TreeAssociation to
+ * create TreeModelAssociation parent.
*
*
*/
-
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/TreeColumnAssociation.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/TreeColumnAssociation.java
index f6c90d0..26f2eda 100644
--- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/TreeColumnAssociation.java
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/TreeColumnAssociation.java
@@ -26,306 +26,254 @@ import net.wotonomy.ui.EODisplayGroup;
import net.wotonomy.ui.swing.components.TreeTableCellRenderer;
/**
-* TreeColumnAssociation is a TableColumnAssocation
-* that works like a TreeAssociation, allowing any
-* table to display hierarchical data in a tabular format.
-* This class is mainly a convenience for connecting a
-* TreeAssociation to a JTree to a TreeTableCellRenderer
-* to a TableColumn.<br><br>
-*
-* Like TableColumnAssociation, you must call setTable()
-* to specify the JTable to be used. (The corresponding
-* table association will direct all column header sorting
-* to the root node of the tree association.)
-*
-* You may also optionally call setTree() to specify a
-* customized JTree to be used. If not specified, a
-* slightly customized JTree will be used (see createTree()
-* and configureColumn()).
-*
-* TreeColumnAssociation supports the following bindings,
-* just as TableColumnAssociation does:
-* <ul>
-* <li>value: a property convertable to a string for
-* display in the cells of the table column. This
-* binding is equivalent to the titles binding of
-* TreeAssociation.</li>
-*
-* <li>editable: a property convertable to a boolean
-* that determines the editability of the corresponding
-* cells in the column.</li>
-* </ul>
-*
-* TreeColumnAssociation additionally supports the following
-* bindings, just as TreeAssociation does, except that the
-* value binding is used instead of the titles binding.
-* <ul>
-* <li>children: a property of a node value that returns
-* zero, one or many objects, each of which will correspond
-* to a child node for the corresponding node in the tree.
-* If this aspect is not bound, the tree behaves like a list.</li>
-*
-* <li>isLeaf: a property of a node value that returns
-* a value convertable to a boolean value.
-* If the isLeaf aspect is not bound, the tree will force
-* nodes to load their children to determine whether they
-* are leaf nodes (in effect loading the grandchildren for
-* any expanded node).
-* </li>
-* </ul>
-*
-* @author michael@mpowers.net
-* @author $Author: cgruber $
-* @version $Revision: 904 $
-*/
-public class TreeColumnAssociation extends TableColumnAssociation
-{
- static final NSArray aspects =
- new NSArray( new Object[] {
- ValueAspect, EditableAspect, ChildrenAspect, IsLeafAspect
- } );
- static final NSArray aspectSignatures =
- new NSArray( new Object[] {
- AttributeToOneAspectSignature
- } );
- static final NSArray objectKeysTaken =
- new NSArray( new Object[] {
- "table"
- } );
-
+ * TreeColumnAssociation is a TableColumnAssocation that works like a
+ * TreeAssociation, allowing any table to display hierarchical data in a tabular
+ * format. This class is mainly a convenience for connecting a TreeAssociation
+ * to a JTree to a TreeTableCellRenderer to a TableColumn.<br>
+ * <br>
+ *
+ * Like TableColumnAssociation, you must call setTable() to specify the JTable
+ * to be used. (The corresponding table association will direct all column
+ * header sorting to the root node of the tree association.)
+ *
+ * You may also optionally call setTree() to specify a customized JTree to be
+ * used. If not specified, a slightly customized JTree will be used (see
+ * createTree() and configureColumn()).
+ *
+ * TreeColumnAssociation supports the following bindings, just as
+ * TableColumnAssociation does:
+ * <ul>
+ * <li>value: a property convertable to a string for display in the cells of the
+ * table column. This binding is equivalent to the titles binding of
+ * TreeAssociation.</li>
+ *
+ * <li>editable: a property convertable to a boolean that determines the
+ * editability of the corresponding cells in the column.</li>
+ * </ul>
+ *
+ * TreeColumnAssociation additionally supports the following bindings, just as
+ * TreeAssociation does, except that the value binding is used instead of the
+ * titles binding.
+ * <ul>
+ * <li>children: a property of a node value that returns zero, one or many
+ * objects, each of which will correspond to a child node for the corresponding
+ * node in the tree. If this aspect is not bound, the tree behaves like a
+ * list.</li>
+ *
+ * <li>isLeaf: a property of a node value that returns a value convertable to a
+ * boolean value. If the isLeaf aspect is not bound, the tree will force nodes
+ * to load their children to determine whether they are leaf nodes (in effect
+ * loading the grandchildren for any expanded node).</li>
+ * </ul>
+ *
+ * @author michael@mpowers.net
+ * @author $Author: cgruber $
+ * @version $Revision: 904 $
+ */
+public class TreeColumnAssociation extends TableColumnAssociation {
+ static final NSArray aspects = new NSArray(
+ new Object[] { ValueAspect, EditableAspect, ChildrenAspect, IsLeafAspect });
+ static final NSArray aspectSignatures = new NSArray(new Object[] { AttributeToOneAspectSignature });
+ static final NSArray objectKeysTaken = new NSArray(new Object[] { "table" });
+
EODisplayGroup childrenDisplayGroup, leafDisplayGroup;
String childrenKey, leafKey;
-
- TreeModelAssociation treeAssociation;
- JTree tree;
-
- /**
- * Constructor specifying the object to be controlled by this
- * association. Throws an exception if the object is not
- * a TableColumn.
- */
- public TreeColumnAssociation ( 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. <ul>
- * <li>"A" attribute: the aspect can be bound to
- * an attribute.</li>
- * <li>"1" to-one: the aspect can be bound to a
- * property that returns a single object.</li>
- * <li>"M" to-one: the aspect can be bound to a
- * property that returns multiple objects.</li>
- * </ul>
- * 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;
- }
-
- /**
- * Binds the specified aspect of this association to the
- * specified key on the specified display group.
- */
- public void bindAspect (
- String anAspect, EODisplayGroup aDisplayGroup, String aKey )
- {
- if ( ChildrenAspect.equals( anAspect ) )
- {
- childrenDisplayGroup = aDisplayGroup;
+
+ TreeModelAssociation treeAssociation;
+ JTree tree;
+
+ /**
+ * Constructor specifying the object to be controlled by this association.
+ * Throws an exception if the object is not a TableColumn.
+ */
+ public TreeColumnAssociation(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.
+ * <ul>
+ * <li>"A" attribute: the aspect can be bound to an attribute.</li>
+ * <li>"1" to-one: the aspect can be bound to a property that returns a single
+ * object.</li>
+ * <li>"M" to-one: the aspect can be bound to a property that returns multiple
+ * objects.</li>
+ * </ul>
+ * 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;
+ }
+
+ /**
+ * Binds the specified aspect of this association to the specified key on the
+ * specified display group.
+ */
+ public void bindAspect(String anAspect, EODisplayGroup aDisplayGroup, String aKey) {
+ if (ChildrenAspect.equals(anAspect)) {
+ childrenDisplayGroup = aDisplayGroup;
childrenKey = aKey;
}
- if ( IsLeafAspect.equals( anAspect ) )
- {
+ if (IsLeafAspect.equals(anAspect)) {
leafDisplayGroup = aDisplayGroup;
leafKey = aKey;
}
- super.bindAspect( anAspect, aDisplayGroup, aKey );
+ super.bindAspect(anAspect, aDisplayGroup, aKey);
}
-
- /**
- * Overridden to call createTree if necessary,
- * call configureColumn, call createTreeAssociation
- * if necessary, and then call to super.
- */
- public void establishConnection ()
- {
- if ( tree == null )
- {
- tree = createTree();
- }
-
- configureColumn( (TableColumn) object() );
-
- if ( treeAssociation == null )
- {
- treeAssociation = createTreeAssociation( tree );
- }
-
- treeAssociation.bindAspect( TitlesAspect, valueDisplayGroup, valueKey );
- if ( childrenKey != null )
- {
- treeAssociation.bindAspect( ChildrenAspect, childrenDisplayGroup, childrenKey );
- }
- if ( leafKey != null )
- {
- treeAssociation.bindAspect( IsLeafAspect, leafDisplayGroup, leafKey );
- }
-
- // ensure table association's source is tree asssociation's child group
- getTableAssociation().bindAspect(
- SourceAspect, treeAssociation.childrenDisplayGroup, "" );
-
- treeAssociation.establishConnection();
-
- table.setRowHeight( tree.getRowHeight() );
-
- super.establishConnection();
-
- // cause sort ordering to apply to root node of tree
- if ( childrenKey != null )
- {
- getTableAssociation().sortTarget =
- (EODisplayGroup) treeAssociation.getRoot();
- }
- }
-
- /**
- * Breaks the connection between this association and
- * its object. Override to stop listening for events
- * from the object.
- */
- public void breakConnection ()
- {
- super.breakConnection();
- treeAssociation.breakConnection();
-
- // restore original source display group
- getTableAssociation().sortTarget = null;
- }
-
- /**
- * Called by establishConnection if setTree was not called.
- * This implementation returns a stock JTree. Override
- * to provide your own customized JTree. Note that
- * TreeTableCellRenderer will further customize this tree
- * when configureColumn is called.
- */
- protected JTree createTree()
- {
- return new JTree();
- }
-
- /**
- * Called by establishConnection to create a tree association
- * only if no tree association has already been created.
- * This implementation returns a stock TreeAssociation.
- * Override to return your own customized TreeAssociation.
- */
- protected TreeModelAssociation createTreeAssociation( JTree aTree )
- {
- return new TreeAssociation( aTree );
- }
-
- /**
- * Called by establishConnection to configure the column
- * with a TreeTableCellRenderer using the current JTree.
- * Override to further customize the column, or customize
- * your column yourself after the call to establishConnection.
- */
- protected void configureColumn( TableColumn aColumn )
- {
- aColumn.setCellRenderer( new TreeTableCellRenderer( tree ) );
- }
-
+
/**
- * Gets the JTree currently used for the column renderer.
- * If not specified, returns null.
- */
- public JTree getTree()
- {
- return tree;
+ * Overridden to call createTree if necessary, call configureColumn, call
+ * createTreeAssociation if necessary, and then call to super.
+ */
+ public void establishConnection() {
+ if (tree == null) {
+ tree = createTree();
+ }
+
+ configureColumn((TableColumn) object());
+
+ if (treeAssociation == null) {
+ treeAssociation = createTreeAssociation(tree);
+ }
+
+ treeAssociation.bindAspect(TitlesAspect, valueDisplayGroup, valueKey);
+ if (childrenKey != null) {
+ treeAssociation.bindAspect(ChildrenAspect, childrenDisplayGroup, childrenKey);
+ }
+ if (leafKey != null) {
+ treeAssociation.bindAspect(IsLeafAspect, leafDisplayGroup, leafKey);
+ }
+
+ // ensure table association's source is tree asssociation's child group
+ getTableAssociation().bindAspect(SourceAspect, treeAssociation.childrenDisplayGroup, "");
+
+ treeAssociation.establishConnection();
+
+ table.setRowHeight(tree.getRowHeight());
+
+ super.establishConnection();
+
+ // cause sort ordering to apply to root node of tree
+ if (childrenKey != null) {
+ getTableAssociation().sortTarget = (EODisplayGroup) treeAssociation.getRoot();
+ }
+ }
+
+ /**
+ * Breaks the connection between this association and its object. Override to
+ * stop listening for events from the object.
+ */
+ public void breakConnection() {
+ super.breakConnection();
+ treeAssociation.breakConnection();
+
+ // restore original source display group
+ getTableAssociation().sortTarget = null;
}
/**
- * Gets the TreeModelAssociation currently used for the tree.
- * If not tree is not specified, returns null.
- */
- public TreeModelAssociation getTreeModelAssociation()
- {
- if ( tree == null ) return null;
- if (!( tree.getModel() instanceof TreeModelAssociation )) return null;
- return (TreeModelAssociation) tree.getModel();
+ * Called by establishConnection if setTree was not called. This implementation
+ * returns a stock JTree. Override to provide your own customized JTree. Note
+ * that TreeTableCellRenderer will further customize this tree when
+ * configureColumn is called.
+ */
+ protected JTree createTree() {
+ return new JTree();
}
/**
- * Sets the JTree to be used for the column renderer.
- * If not specified, createTree() will be called to create a JTree.
- */
- public void setTree( JTree aTree )
- {
- tree = aTree;
+ * Called by establishConnection to create a tree association only if no tree
+ * association has already been created. This implementation returns a stock
+ * TreeAssociation. Override to return your own customized TreeAssociation.
+ */
+ protected TreeModelAssociation createTreeAssociation(JTree aTree) {
+ return new TreeAssociation(aTree);
+ }
+
+ /**
+ * Called by establishConnection to configure the column with a
+ * TreeTableCellRenderer using the current JTree. Override to further customize
+ * the column, or customize your column yourself after the call to
+ * establishConnection.
+ */
+ protected void configureColumn(TableColumn aColumn) {
+ aColumn.setCellRenderer(new TreeTableCellRenderer(tree));
+ }
+
+ /**
+ * Gets the JTree currently used for the column renderer. If not specified,
+ * returns null.
+ */
+ public JTree getTree() {
+ return tree;
+ }
+
+ /**
+ * Gets the TreeModelAssociation currently used for the tree. If not tree is not
+ * specified, returns null.
+ */
+ public TreeModelAssociation getTreeModelAssociation() {
+ if (tree == null)
+ return null;
+ if (!(tree.getModel() instanceof TreeModelAssociation))
+ return null;
+ return (TreeModelAssociation) tree.getModel();
+ }
+
+ /**
+ * Sets the JTree to be used for the column renderer. If not specified,
+ * createTree() will be called to create a JTree.
+ */
+ public void setTree(JTree aTree) {
+ tree = aTree;
}
}
/*
- * $Log$
- * Revision 1.2 2006/02/18 23:19:05 cgruber
- * Update imports and maven dependencies.
+ * $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.1 2006/02/16 13:22:22 cgruber Check in all sources in
+ * eclipse-friendly maven-enabled packages.
*
- * Revision 1.9 2003/08/06 23:07:52 chochos
- * general code cleanup (mostly, removing unused imports)
+ * Revision 1.9 2003/08/06 23:07:52 chochos general code cleanup (mostly,
+ * removing unused imports)
*
- * Revision 1.8 2002/05/03 21:31:50 mpowers
- * Actually works better without selectionPaintedImmediately.
+ * Revision 1.8 2002/05/03 21:31:50 mpowers Actually works better without
+ * selectionPaintedImmediately.
*
- * Revision 1.7 2002/04/12 21:05:58 mpowers
- * Now distinguishing changes in titles group even better.
+ * Revision 1.7 2002/04/12 21:05:58 mpowers Now distinguishing changes in titles
+ * group even better.
*
- * Revision 1.6 2002/03/08 23:18:48 mpowers
- * Added accessor for tree association.
+ * Revision 1.6 2002/03/08 23:18:48 mpowers Added accessor for tree association.
*
- * Revision 1.5 2002/03/07 23:04:36 mpowers
- * Refining TreeColumnAssociation.
+ * Revision 1.5 2002/03/07 23:04:36 mpowers Refining TreeColumnAssociation.
*
- * Revision 1.4 2002/03/06 13:04:16 mpowers
- * Implemented cascading qualifiers in tree nodes.
+ * Revision 1.4 2002/03/06 13:04:16 mpowers Implemented cascading qualifiers in
+ * tree nodes.
*
- * Revision 1.3 2002/03/05 23:18:28 mpowers
- * Added documentation.
- * Added isSelectionPaintedImmediate and isSelectionTracking attributes
- * to TableAssociation.
- * Added getTableAssociation to TableColumnAssociation.
+ * Revision 1.3 2002/03/05 23:18:28 mpowers Added documentation. Added
+ * isSelectionPaintedImmediate and isSelectionTracking attributes to
+ * TableAssociation. Added getTableAssociation to TableColumnAssociation.
*
- * Revision 1.2 2002/03/04 22:48:22 mpowers
- * Now working with table association to sort the root node.
+ * Revision 1.2 2002/03/04 22:48:22 mpowers Now working with table association
+ * to sort the root node.
*
- * Revision 1.1 2002/03/01 23:42:09 mpowers
- * Implemented TreeColumnAssociation, and updated documentation.
+ * Revision 1.1 2002/03/01 23:42:09 mpowers Implemented TreeColumnAssociation,
+ * and updated documentation.
*
*
*/
-
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/TreeModelAssociation.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/TreeModelAssociation.java
index 86bfa69..b0070d4 100644
--- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/TreeModelAssociation.java
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/TreeModelAssociation.java
@@ -48,1704 +48,1396 @@ import net.wotonomy.ui.EOAssociation;
import net.wotonomy.ui.EODisplayGroup;
/**
-* TreeModelAssociation binds a JTree or similar component
-* that uses a TreeModel to a display group's
-* list of displayable objects, each of which may have
-* a list of child objects managed by another display
-* group, and so on. TreeModelAssociation works exactly
-* like a ListAssociation, with the additional capability
-* to specify a "Children" aspect, that will allow child
-* objects to be retrieved from a parent display group.
-*
-* <ul>
-*
-* <li>titles: a property convertable to a string for
-* display in the nodes of the tree. The objects in
-* the bound display group will be used to populate the
-* initial root nodes of the tree (more accurately,
-* children of the offscreen root node in the tree).</li>
-*
-* <li>children: a property of a node value that returns
-* zero, one or many objects, each of which will correspond
-* to a child node for the corresponding node in the tree.
-* The data source of the bound display group is replaced
-* a data source that populates the display group with
-* the selection in the tree component as determined by
-* calling fetchObjectsIntoChildrenGroup.
-* If this aspect is not bound, the tree behaves like a list.
-* <br><br>
-* Binding this aspect with a null display group is the same
-* as binding it with the titles display group.
-* In this configuration the contents of the titles
-* display group will be replaced with the selection in the
-* tree component, as specified above, replacing the existing
-* data source.
-* <br><br>
-* In that case, the display groups for the nodes in
-* the tree will still use the original data source
-* for resolving their children key, and programmatically
-* setting the contents of the display group will still
-* repopulate the root nodes of the tree.
-* </li>
-*
-* <li>isLeaf: a property of a node value that returns
-* a value convertable to a boolean value (aside from
-* an actual boolean value, zeroes evaluate to true,
-* as does any String containing "yes" or "true" or that
-* is convertable to a number equal to zero; other values
-* evaluate to false).
-* <br><br>
-* If the isLeaf aspect is not bound,
-* the tree must force nodes to load their children to
-* determine whether they are leaf nodes (in effect
-* loading the grandchildren for any expanded node).
-* If bound, child loading is deferred until the node
-* is actually expanded.
-* <br><br>
-* For example, binding this value to a null
-* display group and the key "false" will result in a
-* deferred-loading tree that works much like Windows
-* Explorer's network volume browser - all nodes appear
-* with "pluses" until they are expanded.
-* <br><br>
-* Note that the display group is ignored: the property
-* will be applied directly to the object corresponding
-* to the node.</li>
-*
-* </ul>
-*
-* This class acts as the TreeModel for the controlled
-* component: calling yourcomponent.getModel() will
-* return this association. The tree model methods on
-* this class are public and may be used to affect changes
-* on the controlled components.<br><br>
-*
-* The titles display group's contents are inserted
-* into a new display group that acts as the root node.
-* After that point, changes in the titles display group
-* will cause the tree model to reset itself, creating
-* a new display group for the root node.
-* <br><br>
-*
-* If a separate display group is bound to the children
-* aspect, it will
-* be used to hold the selected objects and their siblings
-* and selection will be maintained there, and the titles
-* display group selection will not be updated.
-* Any editing or detail associations should in that case
-* be attached to the children display group, not the titles
-* group. <br><br>
-*
-* Each node in the tree is an EODisplayGroup that
-* contains the child objects of the object it represents
-* in the tree. These objects can be programmatically
-* inserted, updated, or removed using DisplayGroup
-* methods. Each node's takes its parent group's
-* sortOrderings until a sort ordering is explicitly
-* specified - setting a sort ordering to null will resume
-* using the parent group's sort ordering.<br><br>
-*
-* Each node in the tree also implements MutableTreeNode.
-* The value that a node represents is the titles property
-* value of the object in the parent's displayed objects
-* list at the index corresponding to the index of the node.
-* Calling toString on a node returns the string representation
-* of the titles property value, and setUserObject will update
-* that value directly in the corresponding object.
-* Moving a node from one parent to another will remove the
-* actual object in the parent display group and insert it
-* into the destination display group.<br><br>
-*
-* In short, any nodes obtained from this class'
-* implementation of TreeModel may be cast as either
-* EODisplayGroup or MutableTreeNode and maybe be
-* programmatically manipulated in either manner.
-*
-* @author michael@mpowers.net
-* @author $Author: cgruber $
-* @version $Revision: 904 $
-*/
-public class TreeModelAssociation extends EOAssociation
- implements TreeModel, TreeSelectionListener
-{
- static final NSArray aspects =
- new NSArray( new Object[] {
- TitlesAspect, ChildrenAspect, IsLeafAspect
- } );
- static final NSArray aspectSignatures =
- new NSArray( new Object[] {
- AttributeToOneAspectSignature
- } );
- static final NSArray objectKeysTaken =
- new NSArray( new Object[] {
- "model"
- } );
-
- private final static NSSelector getSelectionModel =
- new NSSelector( "getSelectionModel" );
- private final static NSSelector setModel =
- new NSSelector( "setModel",
- new Class[] { TreeModel.class } );
-
- EODisplayGroup titlesDisplayGroup, childrenDisplayGroup, leafDisplayGroup;
- String titlesKey, childrenKey, leafKey;
- DisplayGroupNode rootNode;
- Vector listeners;
- Object rootLabel;
-
- TreeSelectionModel selectionModel;
- boolean selectionPaintedImmediately;
-
- boolean insertingChild;
- boolean insertingAfter;
-
- EOObserverProxy recentChangesObserver;
-
- private boolean pleaseSelectRootNode;
-
- /**
- * Constructor expecting a JTree or any other object
- * that has void setModel(TreeModel) and TreeModel getSelectionModel()
- * methods. This tree association will be used for the TreeModel.
- * The root node will be labeled "Root". <br><br>
- *
- * As an alternate way to use a TreeModelAssociation, you may pass a
- * TreeSelectionModel to the constructor and then manually set your
- * component to use this class as its TreeModel.
- */
- public TreeModelAssociation ( Object anObject )
- {
- super( anObject );
-
- titlesDisplayGroup = null;
- titlesKey = null;
- childrenDisplayGroup = null;
- childrenKey = null;
- leafDisplayGroup = null;
- leafKey = null;
- listeners = new Vector();
-
- selectionPaintedImmediately = false;
-
- // after display group nodes process recent changes
- recentChangesObserver = new EOObserverProxy(
- this, new NSSelector( "processRecentChanges" ),
- EODelayedObserver.ObserverPrioritySixth );
- EOObserverCenter.addObserver( recentChangesObserver, this );
-
- insertingChild = true;
- insertingAfter = true;
-
- pleaseSelectRootNode = false;
-
- rootLabel = "Root";
- rootNode = createNode( null, null );
- }
-
- /**
- * Constructor expecting a JTree or similar component
- * and specifying a label for the root node.
- */
- public TreeModelAssociation( Object anObject, Object aRootLabel )
- {
- this( anObject );
- rootLabel = aRootLabel;
- rootNode.setUserObject( aRootLabel );
- }
-
- /**
- * Gets the current root label.
- */
- public Object rootLabel()
- {
- return rootLabel;
- }
-
- /**
- * Gets the current root label.
- */
- public Object getRootLabel()
- {
- return rootLabel();
- }
-
- /**
- * Sets the root label.
- */
- public void setRootLabel( Object aLabel )
- {
- rootLabel = aLabel;
- }
-
- /**
- * 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. <ul>
- * <li>"A" attribute: the aspect can be bound to
- * an attribute.</li>
- * <li>"1" to-one: the aspect can be bound to a
- * property that returns a single object.</li>
- * <li>"M" to-one: the aspect can be bound to a
- * property that returns multiple objects.</li>
- * </ul>
- * 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 setModel.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. This is typically "value" or somesuch.
- */
- public static String primaryAspect ()
- {
- return TitlesAspect;
- }
-
- /**
- * 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 ) );
- }
-
- /**
- * Binds the specified aspect of this association to the
- * specified key on the specified display group.
- */
- public void bindAspect (
- String anAspect, EODisplayGroup aDisplayGroup, String aKey )
- {
- if ( TitlesAspect.equals( anAspect ) )
- {
- titlesDisplayGroup = aDisplayGroup;
- titlesKey = aKey;
- }
- if ( ChildrenAspect.equals( anAspect ) )
- {
- childrenDisplayGroup = aDisplayGroup;
- childrenKey = aKey;
- }
- if ( IsLeafAspect.equals( anAspect ) )
- {
- leafDisplayGroup = aDisplayGroup;
- leafKey = aKey;
- }
- if ( childrenDisplayGroup == null )
- {
- childrenDisplayGroup = titlesDisplayGroup;
- }
- super.bindAspect( anAspect, aDisplayGroup, aKey );
- }
-
- /**
- * Establishes a connection between this association
- * and the controlled object. Subclasses should begin
- * listening for events from their controlled object here.
- */
- public void establishConnection ()
- {
- if ( titlesDisplayGroup == null )
- {
- throw new WotonomyException(
- "TreeModelAssociation: Titles aspect must be bound" );
- }
-
- // populate the root node
- rootNode = createNode( titlesDisplayGroup, null );
- rootNode.setObjectArray( titlesDisplayGroup.displayedObjects() );
- rootNode.setSortOrderings( titlesDisplayGroup.sortOrderings() );
-
- EODataSource dataSource = childrenDisplayGroup.dataSource();
- if ( dataSource == null ) dataSource = titlesDisplayGroup.dataSource();
- while ( dataSource instanceof DelegatingTreeDataSource )
- { // unwrap any existing delegating data sources
- dataSource = ((DelegatingTreeDataSource)dataSource).delegateDataSource;
- }
- // create a new delegating data source
- childrenDisplayGroup.setDataSource(
- new DelegatingTreeDataSource( this, dataSource ) );
-
- //TODO: find out why omitting this line causes weird selection behavior
- childrenDisplayGroup.setSortOrderings( new NSArray() );
-
- // check for alternate usage
- if ( object() instanceof TreeSelectionModel )
- {
- selectionModel = (TreeSelectionModel) object();
- }
- else // use specified object
- {
- try
- {
- setModel.invoke( object(), new Object[] { this } );
- selectionModel = (TreeSelectionModel)
- getSelectionModel.invoke( object(), new Object[] {} );
- }
- catch ( Exception exc )
- {
- throw new WotonomyException( exc );
- }
- }
-
- addAsListener();
- super.establishConnection();
-/*
- fireRootStructureChanged();
-
-// titlesGroupChanged = true;
-// subjectChanged();
-
- // update the children group
- removeAsListener();
- childrenDisplayGroup.fetch();
- addAsListener();
-
- // update selection
- selectFromDisplayGroup( titlesDisplayGroup );
-*/
- }
-
- protected void fireRootStructureChanged()
- {
- int count = rootNode.displayedObjects().count();
- int[] childIndices = new int[ count ];
- Object[] children = new Object[ count ];
- for ( int i = 0; i < count; i++ )
- {
- childIndices[i] = i;
- children[i] = rootNode.getChildNodeAt( i );
- }
-
- // must fire a tree structure changed with children,
- // otherwise the tree gets weird selection behavior
- fireTreeStructureChanged( this, new Object[] { rootNode },
- childIndices, children );
- }
-
- /**
- * Breaks the connection between this association and
- * its object. Override to stop listening for events
- * from the object.
- */
- public void breakConnection ()
- {
- if ( childrenDisplayGroup != null )
- {
- if ( childrenDisplayGroup.dataSource() instanceof DelegatingTreeDataSource )
- {
- if ( titlesDisplayGroup == childrenDisplayGroup )
- {
- titlesDisplayGroup.setDataSource( ((DelegatingTreeDataSource)
- childrenDisplayGroup.dataSource()).delegateDataSource );
- }
- else
- {
- childrenDisplayGroup.setDataSource( null );
- }
- }
- }
-
- removeAsListener();
- super.breakConnection();
- }
-
- protected void addAsListener()
- {
- isListening = true;
- selectionModel.addTreeSelectionListener( this );
- }
-
- protected void removeAsListener()
- {
- isListening = false;
- selectionModel.removeTreeSelectionListener( this );
- }
-
- protected boolean isListening = false;
- private boolean pleaseIgnore = false;
- protected boolean titlesGroupChanged = false;
- protected boolean childrenGroupChanged = false;
-
- /**
- * Overridden to better discriminate what is changed.
- */
- public void objectWillChange( Object anObject )
- {
- if ( ! isListening ) return;
-
- if ( anObject == titlesDisplayGroup )
- {
- titlesGroupChanged = true;
- }
- if ( anObject == childrenDisplayGroup )
- {
- childrenGroupChanged = true;
- if ( childrenDisplayGroup.qualifier() != null )
- {
- if ( ( rootNode.qualifier() == null ) ||
- ! childrenDisplayGroup.qualifier().equals( rootNode.qualifier() ) )
- {
- // quietly move qualifier from children group to root node
- rootNode.setQualifier( childrenDisplayGroup.qualifier() );
- childrenDisplayGroup.setQualifier( null );
- rootNode.updateDisplayedObjects();
- }
- }
- }
- super.objectWillChange( anObject );
- }
-
- /**
- * Called when either the selection or the contents
- * of an associated display group have changed.
- */
- public void subjectChanged ()
- {
- // titles aspect
- if ( titlesGroupChanged )
- {
- if ( titlesDisplayGroup.contentsChanged() )
- {
- NSArray displayedObjects = titlesDisplayGroup.displayedObjects();
- NSArray childrenObjects;
- if ( titlesDisplayGroup != childrenDisplayGroup
- || displayedObjects.count()
- != (childrenObjects = objectsFetchedIntoChildrenGroup()).count()
- || ! displayedObjects.containsAll( childrenObjects ) )
- {
- populateFromDisplayGroup( displayedObjects );
- }
- }
- }
-
- if ( childrenDisplayGroup.selectionChanged() && !childrenDisplayGroup.contentsChanged() )
- {
- selectFromDisplayGroup( childrenDisplayGroup );
- }
-
- titlesGroupChanged = false;
- childrenGroupChanged = false;
- }
-
- /**
- * Called by subjectChanged() in response to an external change in the titles display group.
- */
- void populateFromDisplayGroup( List displayedObjects )
- {
- // trigger processRecentChanges
- willChange();
-
- // workaround: see below
- int previousCount = rootNode.previouslyDisplayedObjects.length;
-
- // update the root node
- rootNode.setObjectArray( displayedObjects );
-
- //FIXME: workaround for what appears to be a bug in JTree:
- // if root node is not visible and has no children, insert events are ignored
- if ( previousCount == 0 )
- {
- fireRootStructureChanged();
- }
- }
-
- /**
- * Package access so DisplayGroupNode can replace the selection after an update.
- */
- void selectFromDisplayGroup( EODisplayGroup aDisplayGroup )
- { // System.out.println( "selectFromDisplayGroup: " + aDisplayGroup.selectedObjects() );
-
- removeAsListener();
-
- TreePath[] paths = selectionModel.getSelectionPaths();
- NSArray selectedObjects = aDisplayGroup.selectedObjects();
-
- // assemble current selection list
- List treeSelection = new LinkedList();
- if ( paths != null )
- {
- for ( int i = 0; i < paths.length; i ++ )
- {
- treeSelection.add(
- ((DisplayGroupNode)paths[i].getLastPathComponent()).getUserObject() );
- }
- }
-
- if ( ! ( selectedObjects.size() == treeSelection.size()
- && treeSelection.containsAll( selectedObjects ) ) )
- {
- selectionModel.clearSelection();
-
- // workaround to select root node from valueChanged()
- if ( pleaseSelectRootNode )
- {
- selectionModel.addSelectionPath( new TreePath( this.getRoot() ) );
- pleaseSelectRootNode = false;
- }
-
- //FIXME: display group is assumed to have only one instance of each object
- for ( int i = 0; i < selectedObjects.count(); i++ )
- {
- //FIXME: selects only the first instance for now
- //selectionModel.addSelectionPaths(
- // getPathsForObject(
- // selectedObjects.objectAtIndex( i ) ) );
- selectionModel.addSelectionPath(
- getPathForObject(
- selectedObjects.objectAtIndex( i ) ) );
- }
- }
-
- addAsListener();
- }
-
- /**
- * Returns the first node found that represents the
- * specified object, or null if not found.
- * This implementation simply calls getPathForObject.
- */
- public Object getNodeForObject( Object anObject )
- {
- TreePath result = getPathForObject( anObject );
- if ( result != null )
- {
- return result.getLastPathComponent();
- }
- return null;
- }
-
- /**
- * Returns the object represented by the specified node
- * which must be a display group node from this tree.
- */
- public Object getObjectForNode( Object aNode )
- {
- if ( aNode instanceof DisplayGroupNode )
- {
- return ((DisplayGroupNode)aNode).getUserObject();
- }
-
- // not a display group node
- throw new WotonomyException(
- "Not a display group node: " + aNode );
- }
-
- /**
- * Returns the tree path for the specified node,
- * which must be a display group node from this tree.
- */
- public TreePath getPathForNode( Object aNode )
- {
- if ( aNode instanceof DisplayGroupNode )
- {
- return ((DisplayGroupNode)aNode).treePath();
- }
-
- // not a display group node
- throw new WotonomyException(
- "Not a display group node: " + aNode );
- }
-
- /**
- * Returns the first tree path for the node that represents
- * the specified object, or null if the object does not exist in this tree.
- * This implementation does a breadth-first search of the tree
- * for the object, looking only at nodes that have been loaded.
- * This means that if the object does not exist in the tree,
- * the entire tree must be traversed.
- */
- public TreePath getPathForObject( Object anObject )
- {
- return getPathForObjectInPath( anObject, new TreePath( this.getRoot() ) );
- }
-
- /**
- * Returns the tree path for the node that represents
- * the specified object,
- * or null if the object does not exist in this tree.
- * This implementation does a breadth-first search of the tree
- * for the object, looking only at nodes that have been loaded.
- * This means that the entire tree is traversed.
- */
- public TreePath[] getPathsForObject( Object anObject )
- {
- return getPathsForObjectInPath( anObject, new TreePath( this.getRoot() ) );
- }
-
- /**
- * A breadth-first search of the tree starting
- * at the specified tree path, comparing by reference.
- * Returns immediately with the first match.
- */
- private TreePath getPathForObjectInPath( Object anObject, TreePath aPath )
- {
- LinkedList queue = new LinkedList();
-
- // add the specified path
- queue.addLast( aPath );
-
- return processQueue( anObject, queue, null );
- }
-
- /**
- * A breadth-first search of the tree starting
- * at the specified tree path, comparing by reference.
- * The entire branch is searched before returning
- * an array of all matches.
- */
- private TreePath[] getPathsForObjectInPath( Object anObject, TreePath aPath )
- {
- LinkedList queue = new LinkedList();
-
- // add the specified path
- queue.addLast( aPath );
-
- List result = new LinkedList();
- processQueue( anObject, queue, result );
- TreePath[] paths = new TreePath[ result.size() ];
- for ( int i = 0; i < paths.length; i++ )
- {
- paths[i] = (TreePath) result.get(i);
- }
- return paths;
- }
-
- /**
- * Processes the specified queue, appending results to aResult if it exists,
- * or returning immediately with a TreePath is aResult is null.
- */
- private TreePath processQueue( Object anObject, LinkedList aQueue, List aResult )
- {
- TreePath path;
- while ( ! aQueue.isEmpty() )
- {
- path = (TreePath) aQueue.removeFirst();
- path = checkNode( anObject, path, aQueue );
- if ( path != null )
- {
- if ( aResult != null )
- {
- aResult.add( path );
- }
- else
- {
- return path;
- }
- }
- }
- return null;
- }
-
- /**
- * Compares the specified object by reference each of the children of
- * the node at the end of the specified tree path, adding nodes that
- * do not match but have fetched object to the end of the specified queue.
- * Returns the path of the first child node that matches the specified object,
- * or null if no match was found.
- */
- private TreePath checkNode( Object anObject, TreePath aPath, LinkedList aQueue )
- {
- TreePath result = null;
- Object child;
- Object parent = aPath.getLastPathComponent();
- int count = getChildCount( parent );
-
- for ( int i = 0; i < count; i++ )
- {
- child = getChild( parent, i );
-
- // add to queue if node has fetched children
- if ( ((DisplayGroupNode)child).isFetched )
- {
- aQueue.addLast( aPath.pathByAddingChild( child ) );
- }
-
- // compares by reference
- if ( ((DisplayGroupNode)child).object() == anObject )
- {
- // assumes same object cannot be in display group twice
- result = aPath.pathByAddingChild( child );
+ * TreeModelAssociation binds a JTree or similar component that uses a TreeModel
+ * to a display group's list of displayable objects, each of which may have a
+ * list of child objects managed by another display group, and so on.
+ * TreeModelAssociation works exactly like a ListAssociation, with the
+ * additional capability to specify a "Children" aspect, that will allow child
+ * objects to be retrieved from a parent display group.
+ *
+ * <ul>
+ *
+ * <li>titles: a property convertable to a string for display in the nodes of
+ * the tree. The objects in the bound display group will be used to populate the
+ * initial root nodes of the tree (more accurately, children of the offscreen
+ * root node in the tree).</li>
+ *
+ * <li>children: a property of a node value that returns zero, one or many
+ * objects, each of which will correspond to a child node for the corresponding
+ * node in the tree. The data source of the bound display group is replaced a
+ * data source that populates the display group with the selection in the tree
+ * component as determined by calling fetchObjectsIntoChildrenGroup. If this
+ * aspect is not bound, the tree behaves like a list. <br>
+ * <br>
+ * Binding this aspect with a null display group is the same as binding it with
+ * the titles display group. In this configuration the contents of the titles
+ * display group will be replaced with the selection in the tree component, as
+ * specified above, replacing the existing data source. <br>
+ * <br>
+ * In that case, the display groups for the nodes in the tree will still use the
+ * original data source for resolving their children key, and programmatically
+ * setting the contents of the display group will still repopulate the root
+ * nodes of the tree.</li>
+ *
+ * <li>isLeaf: a property of a node value that returns a value convertable to a
+ * boolean value (aside from an actual boolean value, zeroes evaluate to true,
+ * as does any String containing "yes" or "true" or that is convertable to a
+ * number equal to zero; other values evaluate to false). <br>
+ * <br>
+ * If the isLeaf aspect is not bound, the tree must force nodes to load their
+ * children to determine whether they are leaf nodes (in effect loading the
+ * grandchildren for any expanded node). If bound, child loading is deferred
+ * until the node is actually expanded. <br>
+ * <br>
+ * For example, binding this value to a null display group and the key "false"
+ * will result in a deferred-loading tree that works much like Windows
+ * Explorer's network volume browser - all nodes appear with "pluses" until they
+ * are expanded. <br>
+ * <br>
+ * Note that the display group is ignored: the property will be applied directly
+ * to the object corresponding to the node.</li>
+ *
+ * </ul>
+ *
+ * This class acts as the TreeModel for the controlled component: calling
+ * yourcomponent.getModel() will return this association. The tree model methods
+ * on this class are public and may be used to affect changes on the controlled
+ * components.<br>
+ * <br>
+ *
+ * The titles display group's contents are inserted into a new display group
+ * that acts as the root node. After that point, changes in the titles display
+ * group will cause the tree model to reset itself, creating a new display group
+ * for the root node. <br>
+ * <br>
+ *
+ * If a separate display group is bound to the children aspect, it will be used
+ * to hold the selected objects and their siblings and selection will be
+ * maintained there, and the titles display group selection will not be updated.
+ * Any editing or detail associations should in that case be attached to the
+ * children display group, not the titles group. <br>
+ * <br>
+ *
+ * Each node in the tree is an EODisplayGroup that contains the child objects of
+ * the object it represents in the tree. These objects can be programmatically
+ * inserted, updated, or removed using DisplayGroup methods. Each node's takes
+ * its parent group's sortOrderings until a sort ordering is explicitly
+ * specified - setting a sort ordering to null will resume using the parent
+ * group's sort ordering.<br>
+ * <br>
+ *
+ * Each node in the tree also implements MutableTreeNode. The value that a node
+ * represents is the titles property value of the object in the parent's
+ * displayed objects list at the index corresponding to the index of the node.
+ * Calling toString on a node returns the string representation of the titles
+ * property value, and setUserObject will update that value directly in the
+ * corresponding object. Moving a node from one parent to another will remove
+ * the actual object in the parent display group and insert it into the
+ * destination display group.<br>
+ * <br>
+ *
+ * In short, any nodes obtained from this class' implementation of TreeModel may
+ * be cast as either EODisplayGroup or MutableTreeNode and maybe be
+ * programmatically manipulated in either manner.
+ *
+ * @author michael@mpowers.net
+ * @author $Author: cgruber $
+ * @version $Revision: 904 $
+ */
+public class TreeModelAssociation extends EOAssociation implements TreeModel, TreeSelectionListener {
+ static final NSArray aspects = new NSArray(new Object[] { TitlesAspect, ChildrenAspect, IsLeafAspect });
+ static final NSArray aspectSignatures = new NSArray(new Object[] { AttributeToOneAspectSignature });
+ static final NSArray objectKeysTaken = new NSArray(new Object[] { "model" });
+
+ private final static NSSelector getSelectionModel = new NSSelector("getSelectionModel");
+ private final static NSSelector setModel = new NSSelector("setModel", new Class[] { TreeModel.class });
+
+ EODisplayGroup titlesDisplayGroup, childrenDisplayGroup, leafDisplayGroup;
+ String titlesKey, childrenKey, leafKey;
+ DisplayGroupNode rootNode;
+ Vector listeners;
+ Object rootLabel;
+
+ TreeSelectionModel selectionModel;
+ boolean selectionPaintedImmediately;
+
+ boolean insertingChild;
+ boolean insertingAfter;
+
+ EOObserverProxy recentChangesObserver;
+
+ private boolean pleaseSelectRootNode;
+
+ /**
+ * Constructor expecting a JTree or any other object that has void
+ * setModel(TreeModel) and TreeModel getSelectionModel() methods. This tree
+ * association will be used for the TreeModel. The root node will be labeled
+ * "Root". <br>
+ * <br>
+ *
+ * As an alternate way to use a TreeModelAssociation, you may pass a
+ * TreeSelectionModel to the constructor and then manually set your component to
+ * use this class as its TreeModel.
+ */
+ public TreeModelAssociation(Object anObject) {
+ super(anObject);
+
+ titlesDisplayGroup = null;
+ titlesKey = null;
+ childrenDisplayGroup = null;
+ childrenKey = null;
+ leafDisplayGroup = null;
+ leafKey = null;
+ listeners = new Vector();
+
+ selectionPaintedImmediately = false;
+
+ // after display group nodes process recent changes
+ recentChangesObserver = new EOObserverProxy(this, new NSSelector("processRecentChanges"),
+ EODelayedObserver.ObserverPrioritySixth);
+ EOObserverCenter.addObserver(recentChangesObserver, this);
+
+ insertingChild = true;
+ insertingAfter = true;
+
+ pleaseSelectRootNode = false;
+
+ rootLabel = "Root";
+ rootNode = createNode(null, null);
+ }
+
+ /**
+ * Constructor expecting a JTree or similar component and specifying a label for
+ * the root node.
+ */
+ public TreeModelAssociation(Object anObject, Object aRootLabel) {
+ this(anObject);
+ rootLabel = aRootLabel;
+ rootNode.setUserObject(aRootLabel);
+ }
+
+ /**
+ * Gets the current root label.
+ */
+ public Object rootLabel() {
+ return rootLabel;
+ }
+
+ /**
+ * Gets the current root label.
+ */
+ public Object getRootLabel() {
+ return rootLabel();
+ }
+
+ /**
+ * Sets the root label.
+ */
+ public void setRootLabel(Object aLabel) {
+ rootLabel = aLabel;
+ }
+
+ /**
+ * 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.
+ * <ul>
+ * <li>"A" attribute: the aspect can be bound to an attribute.</li>
+ * <li>"1" to-one: the aspect can be bound to a property that returns a single
+ * object.</li>
+ * <li>"M" to-one: the aspect can be bound to a property that returns multiple
+ * objects.</li>
+ * </ul>
+ * 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 setModel.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. This is typically
+ * "value" or somesuch.
+ */
+ public static String primaryAspect() {
+ return TitlesAspect;
+ }
+
+ /**
+ * 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));
+ }
+
+ /**
+ * Binds the specified aspect of this association to the specified key on the
+ * specified display group.
+ */
+ public void bindAspect(String anAspect, EODisplayGroup aDisplayGroup, String aKey) {
+ if (TitlesAspect.equals(anAspect)) {
+ titlesDisplayGroup = aDisplayGroup;
+ titlesKey = aKey;
+ }
+ if (ChildrenAspect.equals(anAspect)) {
+ childrenDisplayGroup = aDisplayGroup;
+ childrenKey = aKey;
+ }
+ if (IsLeafAspect.equals(anAspect)) {
+ leafDisplayGroup = aDisplayGroup;
+ leafKey = aKey;
+ }
+ if (childrenDisplayGroup == null) {
+ childrenDisplayGroup = titlesDisplayGroup;
+ }
+ super.bindAspect(anAspect, aDisplayGroup, aKey);
+ }
+
+ /**
+ * Establishes a connection between this association and the controlled object.
+ * Subclasses should begin listening for events from their controlled object
+ * here.
+ */
+ public void establishConnection() {
+ if (titlesDisplayGroup == null) {
+ throw new WotonomyException("TreeModelAssociation: Titles aspect must be bound");
+ }
+
+ // populate the root node
+ rootNode = createNode(titlesDisplayGroup, null);
+ rootNode.setObjectArray(titlesDisplayGroup.displayedObjects());
+ rootNode.setSortOrderings(titlesDisplayGroup.sortOrderings());
+
+ EODataSource dataSource = childrenDisplayGroup.dataSource();
+ if (dataSource == null)
+ dataSource = titlesDisplayGroup.dataSource();
+ while (dataSource instanceof DelegatingTreeDataSource) { // unwrap any existing delegating data sources
+ dataSource = ((DelegatingTreeDataSource) dataSource).delegateDataSource;
+ }
+ // create a new delegating data source
+ childrenDisplayGroup.setDataSource(new DelegatingTreeDataSource(this, dataSource));
+
+ // TODO: find out why omitting this line causes weird selection behavior
+ childrenDisplayGroup.setSortOrderings(new NSArray());
+
+ // check for alternate usage
+ if (object() instanceof TreeSelectionModel) {
+ selectionModel = (TreeSelectionModel) object();
+ } else // use specified object
+ {
+ try {
+ setModel.invoke(object(), new Object[] { this });
+ selectionModel = (TreeSelectionModel) getSelectionModel.invoke(object(), new Object[] {});
+ } catch (Exception exc) {
+ throw new WotonomyException(exc);
+ }
+ }
+
+ addAsListener();
+ super.establishConnection();
+ /*
+ * fireRootStructureChanged();
+ *
+ * // titlesGroupChanged = true; // subjectChanged();
+ *
+ * // update the children group removeAsListener();
+ * childrenDisplayGroup.fetch(); addAsListener();
+ *
+ * // update selection selectFromDisplayGroup( titlesDisplayGroup );
+ */
+ }
+
+ protected void fireRootStructureChanged() {
+ int count = rootNode.displayedObjects().count();
+ int[] childIndices = new int[count];
+ Object[] children = new Object[count];
+ for (int i = 0; i < count; i++) {
+ childIndices[i] = i;
+ children[i] = rootNode.getChildNodeAt(i);
+ }
+
+ // must fire a tree structure changed with children,
+ // otherwise the tree gets weird selection behavior
+ fireTreeStructureChanged(this, new Object[] { rootNode }, childIndices, children);
+ }
+
+ /**
+ * Breaks the connection between this association and its object. Override to
+ * stop listening for events from the object.
+ */
+ public void breakConnection() {
+ if (childrenDisplayGroup != null) {
+ if (childrenDisplayGroup.dataSource() instanceof DelegatingTreeDataSource) {
+ if (titlesDisplayGroup == childrenDisplayGroup) {
+ titlesDisplayGroup.setDataSource(
+ ((DelegatingTreeDataSource) childrenDisplayGroup.dataSource()).delegateDataSource);
+ } else {
+ childrenDisplayGroup.setDataSource(null);
+ }
+ }
+ }
+
+ removeAsListener();
+ super.breakConnection();
+ }
+
+ protected void addAsListener() {
+ isListening = true;
+ selectionModel.addTreeSelectionListener(this);
+ }
+
+ protected void removeAsListener() {
+ isListening = false;
+ selectionModel.removeTreeSelectionListener(this);
+ }
+
+ protected boolean isListening = false;
+ private boolean pleaseIgnore = false;
+ protected boolean titlesGroupChanged = false;
+ protected boolean childrenGroupChanged = false;
+
+ /**
+ * Overridden to better discriminate what is changed.
+ */
+ public void objectWillChange(Object anObject) {
+ if (!isListening)
+ return;
+
+ if (anObject == titlesDisplayGroup) {
+ titlesGroupChanged = true;
+ }
+ if (anObject == childrenDisplayGroup) {
+ childrenGroupChanged = true;
+ if (childrenDisplayGroup.qualifier() != null) {
+ if ((rootNode.qualifier() == null) || !childrenDisplayGroup.qualifier().equals(rootNode.qualifier())) {
+ // quietly move qualifier from children group to root node
+ rootNode.setQualifier(childrenDisplayGroup.qualifier());
+ childrenDisplayGroup.setQualifier(null);
+ rootNode.updateDisplayedObjects();
+ }
+ }
+ }
+ super.objectWillChange(anObject);
+ }
+
+ /**
+ * Called when either the selection or the contents of an associated display
+ * group have changed.
+ */
+ public void subjectChanged() {
+ // titles aspect
+ if (titlesGroupChanged) {
+ if (titlesDisplayGroup.contentsChanged()) {
+ NSArray displayedObjects = titlesDisplayGroup.displayedObjects();
+ NSArray childrenObjects;
+ if (titlesDisplayGroup != childrenDisplayGroup
+ || displayedObjects.count() != (childrenObjects = objectsFetchedIntoChildrenGroup()).count()
+ || !displayedObjects.containsAll(childrenObjects)) {
+ populateFromDisplayGroup(displayedObjects);
+ }
+ }
+ }
+
+ if (childrenDisplayGroup.selectionChanged() && !childrenDisplayGroup.contentsChanged()) {
+ selectFromDisplayGroup(childrenDisplayGroup);
+ }
+
+ titlesGroupChanged = false;
+ childrenGroupChanged = false;
+ }
+
+ /**
+ * Called by subjectChanged() in response to an external change in the titles
+ * display group.
+ */
+ void populateFromDisplayGroup(List displayedObjects) {
+ // trigger processRecentChanges
+ willChange();
+
+ // workaround: see below
+ int previousCount = rootNode.previouslyDisplayedObjects.length;
+
+ // update the root node
+ rootNode.setObjectArray(displayedObjects);
+
+ // FIXME: workaround for what appears to be a bug in JTree:
+ // if root node is not visible and has no children, insert events are ignored
+ if (previousCount == 0) {
+ fireRootStructureChanged();
+ }
+ }
+
+ /**
+ * Package access so DisplayGroupNode can replace the selection after an update.
+ */
+ void selectFromDisplayGroup(EODisplayGroup aDisplayGroup) { // System.out.println( "selectFromDisplayGroup: " +
+ // aDisplayGroup.selectedObjects() );
+
+ removeAsListener();
+
+ TreePath[] paths = selectionModel.getSelectionPaths();
+ NSArray selectedObjects = aDisplayGroup.selectedObjects();
+
+ // assemble current selection list
+ List treeSelection = new LinkedList();
+ if (paths != null) {
+ for (int i = 0; i < paths.length; i++) {
+ treeSelection.add(((DisplayGroupNode) paths[i].getLastPathComponent()).getUserObject());
+ }
+ }
+
+ if (!(selectedObjects.size() == treeSelection.size() && treeSelection.containsAll(selectedObjects))) {
+ selectionModel.clearSelection();
+
+ // workaround to select root node from valueChanged()
+ if (pleaseSelectRootNode) {
+ selectionModel.addSelectionPath(new TreePath(this.getRoot()));
+ pleaseSelectRootNode = false;
+ }
+
+ // FIXME: display group is assumed to have only one instance of each object
+ for (int i = 0; i < selectedObjects.count(); i++) {
+ // FIXME: selects only the first instance for now
+ // selectionModel.addSelectionPaths(
+ // getPathsForObject(
+ // selectedObjects.objectAtIndex( i ) ) );
+ selectionModel.addSelectionPath(getPathForObject(selectedObjects.objectAtIndex(i)));
+ }
+ }
+
+ addAsListener();
+ }
+
+ /**
+ * Returns the first node found that represents the specified object, or null if
+ * not found. This implementation simply calls getPathForObject.
+ */
+ public Object getNodeForObject(Object anObject) {
+ TreePath result = getPathForObject(anObject);
+ if (result != null) {
+ return result.getLastPathComponent();
+ }
+ return null;
+ }
+
+ /**
+ * Returns the object represented by the specified node which must be a display
+ * group node from this tree.
+ */
+ public Object getObjectForNode(Object aNode) {
+ if (aNode instanceof DisplayGroupNode) {
+ return ((DisplayGroupNode) aNode).getUserObject();
+ }
+
+ // not a display group node
+ throw new WotonomyException("Not a display group node: " + aNode);
+ }
+
+ /**
+ * Returns the tree path for the specified node, which must be a display group
+ * node from this tree.
+ */
+ public TreePath getPathForNode(Object aNode) {
+ if (aNode instanceof DisplayGroupNode) {
+ return ((DisplayGroupNode) aNode).treePath();
+ }
+
+ // not a display group node
+ throw new WotonomyException("Not a display group node: " + aNode);
+ }
+
+ /**
+ * Returns the first tree path for the node that represents the specified
+ * object, or null if the object does not exist in this tree. This
+ * implementation does a breadth-first search of the tree for the object,
+ * looking only at nodes that have been loaded. This means that if the object
+ * does not exist in the tree, the entire tree must be traversed.
+ */
+ public TreePath getPathForObject(Object anObject) {
+ return getPathForObjectInPath(anObject, new TreePath(this.getRoot()));
+ }
+
+ /**
+ * Returns the tree path for the node that represents the specified object, or
+ * null if the object does not exist in this tree. This implementation does a
+ * breadth-first search of the tree for the object, looking only at nodes that
+ * have been loaded. This means that the entire tree is traversed.
+ */
+ public TreePath[] getPathsForObject(Object anObject) {
+ return getPathsForObjectInPath(anObject, new TreePath(this.getRoot()));
+ }
+
+ /**
+ * A breadth-first search of the tree starting at the specified tree path,
+ * comparing by reference. Returns immediately with the first match.
+ */
+ private TreePath getPathForObjectInPath(Object anObject, TreePath aPath) {
+ LinkedList queue = new LinkedList();
+
+ // add the specified path
+ queue.addLast(aPath);
+
+ return processQueue(anObject, queue, null);
+ }
+
+ /**
+ * A breadth-first search of the tree starting at the specified tree path,
+ * comparing by reference. The entire branch is searched before returning an
+ * array of all matches.
+ */
+ private TreePath[] getPathsForObjectInPath(Object anObject, TreePath aPath) {
+ LinkedList queue = new LinkedList();
+
+ // add the specified path
+ queue.addLast(aPath);
+
+ List result = new LinkedList();
+ processQueue(anObject, queue, result);
+ TreePath[] paths = new TreePath[result.size()];
+ for (int i = 0; i < paths.length; i++) {
+ paths[i] = (TreePath) result.get(i);
+ }
+ return paths;
+ }
+
+ /**
+ * Processes the specified queue, appending results to aResult if it exists, or
+ * returning immediately with a TreePath is aResult is null.
+ */
+ private TreePath processQueue(Object anObject, LinkedList aQueue, List aResult) {
+ TreePath path;
+ while (!aQueue.isEmpty()) {
+ path = (TreePath) aQueue.removeFirst();
+ path = checkNode(anObject, path, aQueue);
+ if (path != null) {
+ if (aResult != null) {
+ aResult.add(path);
+ } else {
+ return path;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Compares the specified object by reference each of the children of the node
+ * at the end of the specified tree path, adding nodes that do not match but
+ * have fetched object to the end of the specified queue. Returns the path of
+ * the first child node that matches the specified object, or null if no match
+ * was found.
+ */
+ private TreePath checkNode(Object anObject, TreePath aPath, LinkedList aQueue) {
+ TreePath result = null;
+ Object child;
+ Object parent = aPath.getLastPathComponent();
+ int count = getChildCount(parent);
+
+ for (int i = 0; i < count; i++) {
+ child = getChild(parent, i);
+
+ // add to queue if node has fetched children
+ if (((DisplayGroupNode) child).isFetched) {
+ aQueue.addLast(aPath.pathByAddingChild(child));
+ }
+
+ // compares by reference
+ if (((DisplayGroupNode) child).object() == anObject) {
+ // assumes same object cannot be in display group twice
+ result = aPath.pathByAddingChild(child);
//System.out.println( "TRUE: " + ((DisplayGroupNode)child).object() + " == " + anObject );
- }
+ }
// else
// {
//System.out.println( ((DisplayGroupNode)child).object() + " != " + anObject );
// }
- }
- return result;
- }
-
- // interface TreeSelectionListener
-
- public void valueChanged(TreeSelectionEvent e)
- {
- if ( ! isListening ) return;
- selectFromSelectionModel();
- }
-
- /**
- * Determines whether the selection should be painted
- * immediately after the user clicks and therefore
- * before the children display group is updated.
- * When the children group is bound to many associations
- * or is bound to a master-detail association, updating
- * the display group can take a perceptibly long time.
- * This property defaults to false.
- * @see #setSelectionPaintedImmediately
- */
- public boolean isSelectionPaintedImmediately()
- {
- return selectionPaintedImmediately;
- }
-
- /**
- * Sets whether the selection should be painted immediately.
- * Setting this property to true will let the tree paint
- * first before the display group is updated.
- * This means that any tree selection listers will
- * also be notified before the display group is updated
- * and will have to invokeLater if they want to see the
- * updated display group.
- */
- public void setSelectionPaintedImmediately( boolean isImmediate )
- {
- selectionPaintedImmediately = isImmediate;
- }
-
- /**
- * Package access so DisplayGroupNode can replace the selection.
- * Returns the display group containing the current selection: titles or children.
- */
- EODisplayGroup selectFromSelectionModel()
- { // System.out.print( "selectFromSelectionModel: " );
- removeAsListener();
- DisplayGroupNode node;
- TreePath parentPath;
- TreePath[] selectedPaths = selectionModel.getSelectionPaths();
- NSMutableArray selectionList = new NSMutableArray();
- if ( selectedPaths != null )
- {
- for ( int i = 0; i < selectedPaths.length; i++ )
- {
- // root node is zero - ignore root node
- if ( ( selectedPaths[i].getLastPathComponent() == rootNode ) )
- {
- // select root in selectFromDisplayGroup()
- pleaseSelectRootNode = true;
- }
- else
- {
- node = (DisplayGroupNode)
- selectedPaths[i].getLastPathComponent();
- Object o = node.object();
-
- if ( selectionList.indexOfIdenticalObject(o) == NSArray.NotFound )
- {
- selectionList.addObject( o );
- }
- }
- }
- }
- childrenDisplayGroup.fetch(); //note that we're not currently listening for changes
- if ( ! childrenDisplayGroup.selectObjectsIdenticalTo( selectionList ) )
- {
- addAsListener(); // because we don't have a listener stack
- selectFromDisplayGroup( childrenDisplayGroup );
- removeAsListener();
- }
- addAsListener();
- return childrenDisplayGroup; // titles is now children if children not explicitly set
- }
-
- // interface TreeModel
-
- public Object getRoot()
- {
- return rootNode;
- }
-
- public Object getChild(Object parent, int index)
- {
- // interestingly, this gets called by
- // BasicTreeUI.paintVerticalPartOfLeg for
- // the last child of each expanded tree node.
- Object result = ((DisplayGroupNode)parent).getChildNodeAt( index );
+ }
+ return result;
+ }
+
+ // interface TreeSelectionListener
+
+ public void valueChanged(TreeSelectionEvent e) {
+ if (!isListening)
+ return;
+ selectFromSelectionModel();
+ }
+
+ /**
+ * Determines whether the selection should be painted immediately after the user
+ * clicks and therefore before the children display group is updated. When the
+ * children group is bound to many associations or is bound to a master-detail
+ * association, updating the display group can take a perceptibly long time.
+ * This property defaults to false.
+ *
+ * @see #setSelectionPaintedImmediately
+ */
+ public boolean isSelectionPaintedImmediately() {
+ return selectionPaintedImmediately;
+ }
+
+ /**
+ * Sets whether the selection should be painted immediately. Setting this
+ * property to true will let the tree paint first before the display group is
+ * updated. This means that any tree selection listers will also be notified
+ * before the display group is updated and will have to invokeLater if they want
+ * to see the updated display group.
+ */
+ public void setSelectionPaintedImmediately(boolean isImmediate) {
+ selectionPaintedImmediately = isImmediate;
+ }
+
+ /**
+ * Package access so DisplayGroupNode can replace the selection. Returns the
+ * display group containing the current selection: titles or children.
+ */
+ EODisplayGroup selectFromSelectionModel() { // System.out.print( "selectFromSelectionModel: " );
+ removeAsListener();
+ DisplayGroupNode node;
+ TreePath parentPath;
+ TreePath[] selectedPaths = selectionModel.getSelectionPaths();
+ NSMutableArray selectionList = new NSMutableArray();
+ if (selectedPaths != null) {
+ for (int i = 0; i < selectedPaths.length; i++) {
+ // root node is zero - ignore root node
+ if ((selectedPaths[i].getLastPathComponent() == rootNode)) {
+ // select root in selectFromDisplayGroup()
+ pleaseSelectRootNode = true;
+ } else {
+ node = (DisplayGroupNode) selectedPaths[i].getLastPathComponent();
+ Object o = node.object();
+
+ if (selectionList.indexOfIdenticalObject(o) == NSArray.NotFound) {
+ selectionList.addObject(o);
+ }
+ }
+ }
+ }
+ childrenDisplayGroup.fetch(); // note that we're not currently listening for changes
+ if (!childrenDisplayGroup.selectObjectsIdenticalTo(selectionList)) {
+ addAsListener(); // because we don't have a listener stack
+ selectFromDisplayGroup(childrenDisplayGroup);
+ removeAsListener();
+ }
+ addAsListener();
+ return childrenDisplayGroup; // titles is now children if children not explicitly set
+ }
+
+ // interface TreeModel
+
+ public Object getRoot() {
+ return rootNode;
+ }
+
+ public Object getChild(Object parent, int index) {
+ // interestingly, this gets called by
+ // BasicTreeUI.paintVerticalPartOfLeg for
+ // the last child of each expanded tree node.
+ Object result = ((DisplayGroupNode) parent).getChildNodeAt(index);
//((DisplayGroupNode)parent).suppressRecentChangeProcessing();
-return result;
+ return result;
// return ((DisplayGroupNode)parent).getChildNodeAt( index );
- }
+ }
- public int getChildCount(Object parent)
- {
- int result = ((DisplayGroupNode)parent).getChildCount();
+ public int getChildCount(Object parent) {
+ int result = ((DisplayGroupNode) parent).getChildCount();
//((DisplayGroupNode)parent).suppressRecentChangeProcessing();
-return result;
+ return result;
// return ((DisplayGroupNode)parent).getChildCount();
- }
+ }
- public boolean isLeaf(Object node)
- {
- boolean result = ((DisplayGroupNode)node).isLeaf();
+ public boolean isLeaf(Object node) {
+ boolean result = ((DisplayGroupNode) node).isLeaf();
//((DisplayGroupNode)node).suppressRecentChangeProcessing();
-return result;
+ return result;
// return ((DisplayGroupNode)node).isLeaf();
- }
-
- /**
- * Returns whether this node is visible in the UI.
- * This implementation returns true.
- * <br><br>
- * Subclasses should return false if they can
- * determine that the node is not displayed or
- * expanded or otherwise visible. Non-visible
- * nodes will fetch only when they are shown.
- */
- public boolean isVisible(Object node)
- {
- return true;
- }
-
- public void valueForPathChanged(TreePath path, Object newValue)
- {
- ((DisplayGroupNode)path.getLastPathComponent()).setUserObject( newValue );
- }
-
- public int getIndexOfChild(Object parent, Object child)
- {
- int result = ((DisplayGroupNode)parent).getIndex( (DisplayGroupNode) child );
+ }
+
+ /**
+ * Returns whether this node is visible in the UI. This implementation returns
+ * true. <br>
+ * <br>
+ * Subclasses should return false if they can determine that the node is not
+ * displayed or expanded or otherwise visible. Non-visible nodes will fetch only
+ * when they are shown.
+ */
+ public boolean isVisible(Object node) {
+ return true;
+ }
+
+ public void valueForPathChanged(TreePath path, Object newValue) {
+ ((DisplayGroupNode) path.getLastPathComponent()).setUserObject(newValue);
+ }
+
+ public int getIndexOfChild(Object parent, Object child) {
+ int result = ((DisplayGroupNode) parent).getIndex((DisplayGroupNode) child);
//((DisplayGroupNode)parent).suppressRecentChangeProcessing();
-return result;
+ return result;
// return ((DisplayGroupNode)parent).getIndex( (DisplayGroupNode) child );
- }
-
- public void addTreeModelListener(TreeModelListener aListener)
- {
- listeners.add( aListener );
- }
- public void removeTreeModelListener(TreeModelListener aListener)
- {
- listeners.remove( aListener );
- }
-
- /**
- * Fires a tree nodes changed event to all listeners.
- * Provided as a convenience if you need to make manual
- * changes to the tree model.
- */
- public void fireTreeNodesChanged(Object source,
- Object[] path,
- int[] childIndices,
- Object[] children)
- {
-
- willChange(); // queue processRecentChanges
- TreeModelEvent event = new TreeModelEvent(
- source, path, childIndices, children );
+ }
+
+ public void addTreeModelListener(TreeModelListener aListener) {
+ listeners.add(aListener);
+ }
+
+ public void removeTreeModelListener(TreeModelListener aListener) {
+ listeners.remove(aListener);
+ }
+
+ /**
+ * Fires a tree nodes changed event to all listeners. Provided as a convenience
+ * if you need to make manual changes to the tree model.
+ */
+ public void fireTreeNodesChanged(Object source, Object[] path, int[] childIndices, Object[] children) {
+
+ willChange(); // queue processRecentChanges
+ TreeModelEvent event = new TreeModelEvent(source, path, childIndices, children);
//System.out.println( "fireTreeNodesChanged: " + event );
- Enumeration it = listeners.elements();
- while ( it.hasMoreElements() )
- {
- try
- {
- ((TreeModelListener)it.nextElement()).treeNodesChanged( event );
- }
- catch ( Exception exc )
- {
- System.out.println( "TreeModelAssociation.fireTreeNodesChanged: caught: " + exc );
- System.out.println( "Source:" + source );
- System.out.println( "Path:" );
- for ( int i = 0; i < path.length; i++ )
- {
- System.out.print( path[i] + "-" );
- }
- System.out.println();
- System.out.println( "Indices:" );
- for ( int i = 0; i < childIndices.length; i++ )
- {
- System.out.print( childIndices[i] + "-" );
- }
- System.out.println();
- System.out.println( "Children:" );
- for ( int i = 0; i < children.length; i++ )
- {
- System.out.print( children[i] + "-" );
- }
- System.out.println();
- exc.printStackTrace();
- }
- }
- }
-
- /**
- * Fires a tree nodes inserted event to all listeners.
- * Provided as a convenience if you need to make manual
- * changes to the tree model.
- */
- public void fireTreeNodesInserted(Object source,
- Object[] path,
- int[] childIndices,
- Object[] children)
- {
-
- willChange(); // queue processRecentChanges
- TreeModelEvent event = new TreeModelEvent(
- source, path, childIndices, children );
+ Enumeration it = listeners.elements();
+ while (it.hasMoreElements()) {
+ try {
+ ((TreeModelListener) it.nextElement()).treeNodesChanged(event);
+ } catch (Exception exc) {
+ System.out.println("TreeModelAssociation.fireTreeNodesChanged: caught: " + exc);
+ System.out.println("Source:" + source);
+ System.out.println("Path:");
+ for (int i = 0; i < path.length; i++) {
+ System.out.print(path[i] + "-");
+ }
+ System.out.println();
+ System.out.println("Indices:");
+ for (int i = 0; i < childIndices.length; i++) {
+ System.out.print(childIndices[i] + "-");
+ }
+ System.out.println();
+ System.out.println("Children:");
+ for (int i = 0; i < children.length; i++) {
+ System.out.print(children[i] + "-");
+ }
+ System.out.println();
+ exc.printStackTrace();
+ }
+ }
+ }
+
+ /**
+ * Fires a tree nodes inserted event to all listeners. Provided as a convenience
+ * if you need to make manual changes to the tree model.
+ */
+ public void fireTreeNodesInserted(Object source, Object[] path, int[] childIndices, Object[] children) {
+
+ willChange(); // queue processRecentChanges
+ TreeModelEvent event = new TreeModelEvent(source, path, childIndices, children);
//System.out.println( "fireTreeNodesInserted: " + event );
- Enumeration it = listeners.elements();
- while ( it.hasMoreElements() )
- {
- try
- {
- ((TreeModelListener)it.nextElement()).treeNodesInserted( event );
- }
- catch ( Exception exc )
- {
- System.out.println( "TreeModelAssociation.fireTreeNodesInserted: caught: " + exc );
- }
- }
- }
-
- /**
- * Fires a tree nodes removed event to all listeners.
- * Provided as a convenience if you need to make manual
- * changes to the tree model.
- */
- public void fireTreeNodesRemoved(Object source,
- Object[] path,
- int[] childIndices,
- Object[] children)
- {
-
- willChange(); // queue processRecentChanges
- TreeModelEvent event = new TreeModelEvent(
- source, path, childIndices, children );
+ Enumeration it = listeners.elements();
+ while (it.hasMoreElements()) {
+ try {
+ ((TreeModelListener) it.nextElement()).treeNodesInserted(event);
+ } catch (Exception exc) {
+ System.out.println("TreeModelAssociation.fireTreeNodesInserted: caught: " + exc);
+ }
+ }
+ }
+
+ /**
+ * Fires a tree nodes removed event to all listeners. Provided as a convenience
+ * if you need to make manual changes to the tree model.
+ */
+ public void fireTreeNodesRemoved(Object source, Object[] path, int[] childIndices, Object[] children) {
+
+ willChange(); // queue processRecentChanges
+ TreeModelEvent event = new TreeModelEvent(source, path, childIndices, children);
//System.out.println( "fireTreeNodesRemoved: " + event );
- Enumeration it = listeners.elements();
- while ( it.hasMoreElements() )
- {
- try
- {
- //NOTE: removing nodes causes tree to fire selection change event
- // which confuses us if we're rearranging nodes (when sorting, for example).
- boolean wasListening = isListening;
- if ( wasListening ) isListening = false;
- ((TreeModelListener)it.nextElement()).treeNodesRemoved( event );
- if ( wasListening ) isListening = true;
- }
- catch ( Exception exc )
- {
- System.out.println( "TreeModelAssociation.fireTreeNodesRemoved: caught: " + exc );
- }
- }
- }
-
- /**
- * Fires a tree structure changed event to all listeners.
- * Provided as a convenience if you need to make manual
- * changes to the tree model.
- */
- public void fireTreeStructureChanged(Object source,
- Object[] path,
- int[] childIndices,
- Object[] children)
- {
-
- willChange(); // queue processRecentChanges
- TreeModelEvent event = new TreeModelEvent(
- source, path, childIndices, children );
+ Enumeration it = listeners.elements();
+ while (it.hasMoreElements()) {
+ try {
+ // NOTE: removing nodes causes tree to fire selection change event
+ // which confuses us if we're rearranging nodes (when sorting, for example).
+ boolean wasListening = isListening;
+ if (wasListening)
+ isListening = false;
+ ((TreeModelListener) it.nextElement()).treeNodesRemoved(event);
+ if (wasListening)
+ isListening = true;
+ } catch (Exception exc) {
+ System.out.println("TreeModelAssociation.fireTreeNodesRemoved: caught: " + exc);
+ }
+ }
+ }
+
+ /**
+ * Fires a tree structure changed event to all listeners. Provided as a
+ * convenience if you need to make manual changes to the tree model.
+ */
+ public void fireTreeStructureChanged(Object source, Object[] path, int[] childIndices, Object[] children) {
+
+ willChange(); // queue processRecentChanges
+ TreeModelEvent event = new TreeModelEvent(source, path, childIndices, children);
//System.out.println( "fireStructureChanged: " + event );
- Enumeration it = listeners.elements();
- while ( it.hasMoreElements() )
- {
- ((TreeModelListener)it.nextElement()).treeStructureChanged( event );
- }
- }
-
- /**
- * Creates and returns a new display group node.
- */
- public DisplayGroupNode createNode( EODisplayGroup aParentGroup, Object anObject )
- {
- return new MutableDisplayGroupNode( this, aParentGroup, anObject );
- }
-
- /**
- * Gets whether new objects programmatically inserted into the children
- * display group should be inserted as a child of the first selected node.
- * If false, new objects are inserted as siblings of the first selected node.
- * Default value is true.
- */
- public boolean isInsertingChild()
- {
- return insertingChild;
- }
-
- /**
- * Sets whether new objects programmatically inserted into the children
- * display group should be inserted as a child of the first selected node.
- * If false, new objects are inserted as siblings of the first selected node.
- * Default value is true.
- */
- public void setInsertingChild( boolean asChild )
- {
- insertingChild = asChild;
- }
-
- /**
- * Determines where new objects programmatically inserted into the children
- * display group should be inserted, based on the value of insertingChild.
- * If insertingChild, isInsertingAfter causes objects to be inserted at
- * the end of the selected node's child list; otherwise, objects are inserted
- * at the beginning of the list.
- * If inserting as a sibling, isInsertingAfter causes objects to be inserted
- * before the selected node in the selected node's parent's child list;
- * otherwise, objects are inserted after the selected node in the child list.
- * Default value is true.
- */
- public boolean isInsertingAfter()
- {
- return insertingAfter;
- }
-
- /**
- * Determines where new objects programmatically inserted into the children
- * display group should be inserted, based on the value of insertingChild.
- * If insertingChild, isInsertingAfter causes objects to be inserted at
- * the end of the selected node's child list; otherwise, objects are inserted
- * at the beginning of the list.
- * If inserting as a sibling, isInsertingAfter causes objects to be inserted
- * before the selected node in the selected node's parent's child list;
- * otherwise, objects are inserted after the selected node in the child list.
- * Default value is true.
- */
- public void setInsertingAfter( boolean after )
- {
- insertingAfter = after;
- }
-
- /**
- * Called to by the children group's data source when it receives
- * an insertObject message, usually after an object has been inserted
- * into the children display group.
- * Return the object that should be passed to the titles display
- * group's data source's implementation of insertObject, or return
- * null to prevent that method from being called. <br><br>
- * This implementation inserts the specified object into the tree
- * as determined by calling isInsertingChild and isInsertingAfter,
- * then returns the unmodified object. If there's no selection, or
- * no selection model, the root node is assumed to be selected.
- * And if the root node is selected, the new node will obviously be
- * inserted as a child. Override to customize.
- */
- protected Object objectInsertedIntoChildrenGroup( Object anObject )
- {
- // determine selection
- DisplayGroupNode selectedNode = (DisplayGroupNode) getRoot();
- if ( selectionModel != null )
- {
- // get selected path
- TreePath path = selectionModel.getSelectionPath();
-
- // get selected node
- if ( path != null )
- {
- selectedNode = (DisplayGroupNode) path.getLastPathComponent();
- }
- }
- // determine location of insertion
- int index = 0;
- if ( ( isInsertingChild() ) || ( selectedNode == getRoot() ) )
- {
- if ( isInsertingAfter() )
- {
- index = selectedNode.getChildCount();
- }
- }
- else // inserting as sibling
- {
- DisplayGroupNode parentNode = selectedNode.getParentGroup();
- index = parentNode.getIndex( selectedNode );
- if ( isInsertingAfter() )
- {
- index++;
- }
- selectedNode = parentNode;
- }
-
- // insert and return
- selectedNode.insertObjectAtIndex( anObject, index );
- return anObject;
- }
-
- /**
- * Called to by the children group's data source when it receives
- * a deleteObject message, usually after an object has been deleted
- * from the children display group.
- * Return the object that should be passed to the titles display
- * group's data source's implementation of deleteObject, or return
- * null to prevent that method from being called. <br><br>
- * This implementation deletes all instances of the selected object
- * from the tree nodes that are currently loaded, and returns the
- * unmodified object. Override to customize.
- */
- protected Object objectDeletedFromChildrenGroup( Object anObject )
- {
- TreePath[] paths = getPathsForObject( anObject );
- if ( paths != null )
- {
- for ( int i = 0; i < paths.length; i++ )
- {
- ((DisplayGroupNode)paths[i].getLastPathComponent()).removeFromParent();
- }
- }
- return anObject;
- }
-
- /**
- * Called to by the children group's data source to populate it
- * with all selected nodes and their siblings. To customize,
- * override this method, or specify a different data source for
- * the children display group.
- */
- protected NSArray objectsFetchedIntoChildrenGroup()
- {
- DisplayGroupNode node;
- TreePath parentPath;
- TreePath[] selectedPaths = selectionModel.getSelectionPaths();
- NSMutableArray objectList = new NSMutableArray();
- if ( selectedPaths != null )
- {
- for ( int i = 0; i < selectedPaths.length; i++ )
- {
- // root node is zero - ignore root node
- if ( ( selectedPaths[i].getLastPathComponent() == rootNode ) )
- {
- // select root in selectFromDisplayGroup()
- pleaseSelectRootNode = true;
- }
- else
- {
- node = (DisplayGroupNode)
- selectedPaths[i].getLastPathComponent();
- Object o = node.object();
-
- // add all children of parent to object list - includes self
- if ( node.parentGroup != null )
- {
- Enumeration e =
- node.parentGroup.displayedObjects().objectEnumerator();
- while ( e.hasMoreElements() )
- {
- // add only if not already in list
- o = e.nextElement();
- if ( objectList.indexOfIdenticalObject(o) == NSArray.NotFound )
- {
- objectList.addObject( o );
- }
- }
- }
- else // no parent node - add the node by itself
- {
- // add only if not already in list
- if ( objectList.indexOfIdenticalObject(o) == NSArray.NotFound )
- {
- objectList.addObject( o );
- }
- }
- }
- }
- }
-
- // if no selection
- if ( objectList.size() == 0 )
- {
- // populate with children of root
- objectList.addAll( rootNode.displayedObjects() );
- }
- return objectList;
- }
-
- /**
- * Queues processRecentChanges to be run in the event queue.
- */
- private void willChange()
- {
- EOObserverCenter.notifyObserversObjectWillChange( this );
- }
-
- /**
- * Tells the children display group to refetch, so that it reflects
- * any changes that were made in the node tree,
- * and then updates the selection in the selection model.
- * Triggered in response to willChange().
- */
- public void processRecentChanges()
- {
- Runnable update = new Runnable()
- {
- public void run()
- {
- removeAsListener(); // prevent data source refetch: see fetchObjects()
- childrenDisplayGroup.fetch();
- addAsListener();
- selectFromDisplayGroup( childrenDisplayGroup );
- }
- };
- if ( isListening )
- {
- if ( selectionPaintedImmediately )
- {
- // if painting selection immediately, run even later
- // so that AWT's repaint event fires before we do.
- SwingUtilities.invokeLater( update );
- }
- else
- {
- // otherwise run now
- update.run();
- }
- }
- }
-
- /**
- * Delegates most behaviors to the specified data source,
- * except fetchObjects, which calls fetchObjectsIntoChildrenGroup
- * on the tree model association. If delegate is null,
- * calls are passed to the superclass which is a PropertyDataSource.
- */
- static class DelegatingTreeDataSource extends PropertyDataSource
- {
- TreeModelAssociation parentAssociation;
- EODataSource delegateDataSource;
-
- public DelegatingTreeDataSource(
- TreeModelAssociation aTreeModelAssociation, EODataSource aDataSource )
- {
- parentAssociation = aTreeModelAssociation;
- delegateDataSource = aDataSource;
- }
-
- /**
- * Calls to delegateDataSource if it exists, otherwise
- * calls to super.
- */
- public Object createObject()
- {
- if ( delegateDataSource != null )
- {
- return delegateDataSource.createObject();
- }
- return super.createObject();
- }
-
- /**
- * Calls objectInsertedIntoChildrenGroup, and if not null
- * calls to delegateDataSource.insertObject if it exists,
- * and super.insertObjectAtIndex if not.
- */
- public void insertObjectAtIndex( Object anObject, int anIndex )
- {
- anObject =
- parentAssociation.objectInsertedIntoChildrenGroup(
- anObject );
- if ( anObject != null )
- {
- if ( delegateDataSource != null )
- {
- if ( delegateDataSource instanceof OrderedDataSource )
- {
- ((OrderedDataSource)delegateDataSource).insertObjectAtIndex( anObject, anIndex );
- }
- else
- {
- delegateDataSource.insertObject( anObject );
- }
- }
- else
- {
- super.insertObjectAtIndex( anObject, anIndex );
- }
- }
- }
-
- /**
- * Calls objectDeletedIntoChildrenGroup, and if not null
- * calls to delegateDataSource if it exists.
- */
- public void deleteObject( Object anObject )
- {
- anObject =
- parentAssociation.objectDeletedFromChildrenGroup(
- anObject );
- if ( anObject != null )
- {
- if ( delegateDataSource != null )
- {
- delegateDataSource.deleteObject( anObject );
- }
- super.deleteObject( anObject );
- }
- }
-
- /**
- * Overridden to return the delegate's editing context,
- * the titles display group's editing context,
- * and failing that calling to super.
- */
- public EOEditingContext editingContext ()
- {
- EOEditingContext result = null;
- if ( delegateDataSource != null )
- {
- result = delegateDataSource.editingContext();
- }
- if ( result == null )
- {
- EODataSource parentDataSource =
- parentAssociation.titlesDisplayGroup.dataSource();
- if ( parentDataSource != this && parentDataSource != null )
- {
- result = parentAssociation.titlesDisplayGroup.
- dataSource().editingContext();
- }
- }
- if ( result == null )
- {
- result = super.editingContext();
- }
- return result;
- }
-
- /**
- * Returns a List containing the objects in this
- * data source.
- */
- public NSArray fetchObjects ()
- {
- // if titles group is doing double-duty as children group
- if ( parentAssociation.titlesDisplayGroup == parentAssociation.childrenDisplayGroup )
- {
- // if we're not initiating this fetch
- if ( parentAssociation.isListening )
- {
- // need to call to delegate to see if we should update values
- if ( delegateDataSource != null )
- {
+ Enumeration it = listeners.elements();
+ while (it.hasMoreElements()) {
+ ((TreeModelListener) it.nextElement()).treeStructureChanged(event);
+ }
+ }
+
+ /**
+ * Creates and returns a new display group node.
+ */
+ public DisplayGroupNode createNode(EODisplayGroup aParentGroup, Object anObject) {
+ return new MutableDisplayGroupNode(this, aParentGroup, anObject);
+ }
+
+ /**
+ * Gets whether new objects programmatically inserted into the children display
+ * group should be inserted as a child of the first selected node. If false, new
+ * objects are inserted as siblings of the first selected node. Default value is
+ * true.
+ */
+ public boolean isInsertingChild() {
+ return insertingChild;
+ }
+
+ /**
+ * Sets whether new objects programmatically inserted into the children display
+ * group should be inserted as a child of the first selected node. If false, new
+ * objects are inserted as siblings of the first selected node. Default value is
+ * true.
+ */
+ public void setInsertingChild(boolean asChild) {
+ insertingChild = asChild;
+ }
+
+ /**
+ * Determines where new objects programmatically inserted into the children
+ * display group should be inserted, based on the value of insertingChild. If
+ * insertingChild, isInsertingAfter causes objects to be inserted at the end of
+ * the selected node's child list; otherwise, objects are inserted at the
+ * beginning of the list. If inserting as a sibling, isInsertingAfter causes
+ * objects to be inserted before the selected node in the selected node's
+ * parent's child list; otherwise, objects are inserted after the selected node
+ * in the child list. Default value is true.
+ */
+ public boolean isInsertingAfter() {
+ return insertingAfter;
+ }
+
+ /**
+ * Determines where new objects programmatically inserted into the children
+ * display group should be inserted, based on the value of insertingChild. If
+ * insertingChild, isInsertingAfter causes objects to be inserted at the end of
+ * the selected node's child list; otherwise, objects are inserted at the
+ * beginning of the list. If inserting as a sibling, isInsertingAfter causes
+ * objects to be inserted before the selected node in the selected node's
+ * parent's child list; otherwise, objects are inserted after the selected node
+ * in the child list. Default value is true.
+ */
+ public void setInsertingAfter(boolean after) {
+ insertingAfter = after;
+ }
+
+ /**
+ * Called to by the children group's data source when it receives an
+ * insertObject message, usually after an object has been inserted into the
+ * children display group. Return the object that should be passed to the titles
+ * display group's data source's implementation of insertObject, or return null
+ * to prevent that method from being called. <br>
+ * <br>
+ * This implementation inserts the specified object into the tree as determined
+ * by calling isInsertingChild and isInsertingAfter, then returns the unmodified
+ * object. If there's no selection, or no selection model, the root node is
+ * assumed to be selected. And if the root node is selected, the new node will
+ * obviously be inserted as a child. Override to customize.
+ */
+ protected Object objectInsertedIntoChildrenGroup(Object anObject) {
+ // determine selection
+ DisplayGroupNode selectedNode = (DisplayGroupNode) getRoot();
+ if (selectionModel != null) {
+ // get selected path
+ TreePath path = selectionModel.getSelectionPath();
+
+ // get selected node
+ if (path != null) {
+ selectedNode = (DisplayGroupNode) path.getLastPathComponent();
+ }
+ }
+ // determine location of insertion
+ int index = 0;
+ if ((isInsertingChild()) || (selectedNode == getRoot())) {
+ if (isInsertingAfter()) {
+ index = selectedNode.getChildCount();
+ }
+ } else // inserting as sibling
+ {
+ DisplayGroupNode parentNode = selectedNode.getParentGroup();
+ index = parentNode.getIndex(selectedNode);
+ if (isInsertingAfter()) {
+ index++;
+ }
+ selectedNode = parentNode;
+ }
+
+ // insert and return
+ selectedNode.insertObjectAtIndex(anObject, index);
+ return anObject;
+ }
+
+ /**
+ * Called to by the children group's data source when it receives a deleteObject
+ * message, usually after an object has been deleted from the children display
+ * group. Return the object that should be passed to the titles display group's
+ * data source's implementation of deleteObject, or return null to prevent that
+ * method from being called. <br>
+ * <br>
+ * This implementation deletes all instances of the selected object from the
+ * tree nodes that are currently loaded, and returns the unmodified object.
+ * Override to customize.
+ */
+ protected Object objectDeletedFromChildrenGroup(Object anObject) {
+ TreePath[] paths = getPathsForObject(anObject);
+ if (paths != null) {
+ for (int i = 0; i < paths.length; i++) {
+ ((DisplayGroupNode) paths[i].getLastPathComponent()).removeFromParent();
+ }
+ }
+ return anObject;
+ }
+
+ /**
+ * Called to by the children group's data source to populate it with all
+ * selected nodes and their siblings. To customize, override this method, or
+ * specify a different data source for the children display group.
+ */
+ protected NSArray objectsFetchedIntoChildrenGroup() {
+ DisplayGroupNode node;
+ TreePath parentPath;
+ TreePath[] selectedPaths = selectionModel.getSelectionPaths();
+ NSMutableArray objectList = new NSMutableArray();
+ if (selectedPaths != null) {
+ for (int i = 0; i < selectedPaths.length; i++) {
+ // root node is zero - ignore root node
+ if ((selectedPaths[i].getLastPathComponent() == rootNode)) {
+ // select root in selectFromDisplayGroup()
+ pleaseSelectRootNode = true;
+ } else {
+ node = (DisplayGroupNode) selectedPaths[i].getLastPathComponent();
+ Object o = node.object();
+
+ // add all children of parent to object list - includes self
+ if (node.parentGroup != null) {
+ Enumeration e = node.parentGroup.displayedObjects().objectEnumerator();
+ while (e.hasMoreElements()) {
+ // add only if not already in list
+ o = e.nextElement();
+ if (objectList.indexOfIdenticalObject(o) == NSArray.NotFound) {
+ objectList.addObject(o);
+ }
+ }
+ } else // no parent node - add the node by itself
+ {
+ // add only if not already in list
+ if (objectList.indexOfIdenticalObject(o) == NSArray.NotFound) {
+ objectList.addObject(o);
+ }
+ }
+ }
+ }
+ }
+
+ // if no selection
+ if (objectList.size() == 0) {
+ // populate with children of root
+ objectList.addAll(rootNode.displayedObjects());
+ }
+ return objectList;
+ }
+
+ /**
+ * Queues processRecentChanges to be run in the event queue.
+ */
+ private void willChange() {
+ EOObserverCenter.notifyObserversObjectWillChange(this);
+ }
+
+ /**
+ * Tells the children display group to refetch, so that it reflects any changes
+ * that were made in the node tree, and then updates the selection in the
+ * selection model. Triggered in response to willChange().
+ */
+ public void processRecentChanges() {
+ Runnable update = new Runnable() {
+ public void run() {
+ removeAsListener(); // prevent data source refetch: see fetchObjects()
+ childrenDisplayGroup.fetch();
+ addAsListener();
+ selectFromDisplayGroup(childrenDisplayGroup);
+ }
+ };
+ if (isListening) {
+ if (selectionPaintedImmediately) {
+ // if painting selection immediately, run even later
+ // so that AWT's repaint event fires before we do.
+ SwingUtilities.invokeLater(update);
+ } else {
+ // otherwise run now
+ update.run();
+ }
+ }
+ }
+
+ /**
+ * Delegates most behaviors to the specified data source, except fetchObjects,
+ * which calls fetchObjectsIntoChildrenGroup on the tree model association. If
+ * delegate is null, calls are passed to the superclass which is a
+ * PropertyDataSource.
+ */
+ static class DelegatingTreeDataSource extends PropertyDataSource {
+ TreeModelAssociation parentAssociation;
+ EODataSource delegateDataSource;
+
+ public DelegatingTreeDataSource(TreeModelAssociation aTreeModelAssociation, EODataSource aDataSource) {
+ parentAssociation = aTreeModelAssociation;
+ delegateDataSource = aDataSource;
+ }
+
+ /**
+ * Calls to delegateDataSource if it exists, otherwise calls to super.
+ */
+ public Object createObject() {
+ if (delegateDataSource != null) {
+ return delegateDataSource.createObject();
+ }
+ return super.createObject();
+ }
+
+ /**
+ * Calls objectInsertedIntoChildrenGroup, and if not null calls to
+ * delegateDataSource.insertObject if it exists, and super.insertObjectAtIndex
+ * if not.
+ */
+ public void insertObjectAtIndex(Object anObject, int anIndex) {
+ anObject = parentAssociation.objectInsertedIntoChildrenGroup(anObject);
+ if (anObject != null) {
+ if (delegateDataSource != null) {
+ if (delegateDataSource instanceof OrderedDataSource) {
+ ((OrderedDataSource) delegateDataSource).insertObjectAtIndex(anObject, anIndex);
+ } else {
+ delegateDataSource.insertObject(anObject);
+ }
+ } else {
+ super.insertObjectAtIndex(anObject, anIndex);
+ }
+ }
+ }
+
+ /**
+ * Calls objectDeletedIntoChildrenGroup, and if not null calls to
+ * delegateDataSource if it exists.
+ */
+ public void deleteObject(Object anObject) {
+ anObject = parentAssociation.objectDeletedFromChildrenGroup(anObject);
+ if (anObject != null) {
+ if (delegateDataSource != null) {
+ delegateDataSource.deleteObject(anObject);
+ }
+ super.deleteObject(anObject);
+ }
+ }
+
+ /**
+ * Overridden to return the delegate's editing context, the titles display
+ * group's editing context, and failing that calling to super.
+ */
+ public EOEditingContext editingContext() {
+ EOEditingContext result = null;
+ if (delegateDataSource != null) {
+ result = delegateDataSource.editingContext();
+ }
+ if (result == null) {
+ EODataSource parentDataSource = parentAssociation.titlesDisplayGroup.dataSource();
+ if (parentDataSource != this && parentDataSource != null) {
+ result = parentAssociation.titlesDisplayGroup.dataSource().editingContext();
+ }
+ }
+ if (result == null) {
+ result = super.editingContext();
+ }
+ return result;
+ }
+
+ /**
+ * Returns a List containing the objects in this data source.
+ */
+ public NSArray fetchObjects() {
+ // if titles group is doing double-duty as children group
+ if (parentAssociation.titlesDisplayGroup == parentAssociation.childrenDisplayGroup) {
+ // if we're not initiating this fetch
+ if (parentAssociation.isListening) {
+ // need to call to delegate to see if we should update values
+ if (delegateDataSource != null) {
// System.out.println( "fetching from delegate (slow!)" );
- NSArray result = delegateDataSource.fetchObjects();
- NSArray rootObjects = parentAssociation.rootNode.displayedObjects();
- // if titles data source has different objects, return them
- if ( rootObjects.count() != result.count()
- || ! rootObjects.containsAll( result ) )
- {
- // this will force the root node to repopulate in subjectChanged()
+ NSArray result = delegateDataSource.fetchObjects();
+ NSArray rootObjects = parentAssociation.rootNode.displayedObjects();
+ // if titles data source has different objects, return them
+ if (rootObjects.count() != result.count() || !rootObjects.containsAll(result)) {
+ // this will force the root node to repopulate in subjectChanged()
//System.out.println( "fetchObjects: data source" );
- return result;
- }
- }
- }
- }
- // otherwise: just repopulate the titles group
+ return result;
+ }
+ }
+ }
+ }
+ // otherwise: just repopulate the titles group
//System.out.println( "fetchObjects: objectsFetchedIntoChildrenGroup" );
- return parentAssociation.objectsFetchedIntoChildrenGroup();
- }
-
- /**
- * Returns a data source that is capable of
- * manipulating objects of the type returned by
- * applying the specified key to objects
- * vended by this data source.
- * @see #qualifyWithRelationshipKey
- */
- public EODataSource
- dataSourceQualifiedByKey ( String aKey )
- {
- if ( delegateDataSource != null )
- {
- return delegateDataSource.dataSourceQualifiedByKey( aKey );
- }
- return null;
- }
-
- /**
- * Restricts this data source to vend those
- * objects that are associated with the specified
- * key on the specified object.
- */
- public void
- qualifyWithRelationshipKey (
- String aKey, Object anObject )
- {
- if ( delegateDataSource != null )
- {
- delegateDataSource.qualifyWithRelationshipKey( aKey, anObject );
- }
- }
-
- /**
- * Returns the value from the delegateDataSource, if it exists.
- * Otherwise calls super.
- */
- public EOClassDescription classDescriptionForObjects()
- {
- if ( delegateDataSource != null )
- {
- return delegateDataSource.classDescriptionForObjects();
- }
- return super.classDescriptionForObjects();
- }
-
- }
-
+ return parentAssociation.objectsFetchedIntoChildrenGroup();
+ }
+
+ /**
+ * Returns a data source that is capable of manipulating objects of the type
+ * returned by applying the specified key to objects vended by this data source.
+ *
+ * @see #qualifyWithRelationshipKey
+ */
+ public EODataSource dataSourceQualifiedByKey(String aKey) {
+ if (delegateDataSource != null) {
+ return delegateDataSource.dataSourceQualifiedByKey(aKey);
+ }
+ return null;
+ }
+
+ /**
+ * Restricts this data source to vend those objects that are associated with the
+ * specified key on the specified object.
+ */
+ public void qualifyWithRelationshipKey(String aKey, Object anObject) {
+ if (delegateDataSource != null) {
+ delegateDataSource.qualifyWithRelationshipKey(aKey, anObject);
+ }
+ }
+
+ /**
+ * Returns the value from the delegateDataSource, if it exists. Otherwise calls
+ * super.
+ */
+ public EOClassDescription classDescriptionForObjects() {
+ if (delegateDataSource != null) {
+ return delegateDataSource.classDescriptionForObjects();
+ }
+ return super.classDescriptionForObjects();
+ }
+
+ }
+
}
/*
- * $Log$
- * Revision 1.2 2006/02/18 23:19:05 cgruber
- * Update imports and maven dependencies.
+ * $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.1 2006/02/16 13:22:22 cgruber Check in all sources in
+ * eclipse-friendly maven-enabled packages.
*
- * Revision 1.20 2003/08/06 23:07:52 chochos
- * general code cleanup (mostly, removing unused imports)
+ * Revision 1.20 2003/08/06 23:07:52 chochos general code cleanup (mostly,
+ * removing unused imports)
*
- * Revision 1.19 2002/05/03 21:41:18 mpowers
- * No longer clearing the selection model when updating from display group:
- * we now only modify if a change needs to be made.
- * No longer listening for selection change during firing of delete events:
- * delete events cause JTree's to update their selection model.
- * Fix for paintsSelectionImmediately: TreeAssociation.processRecentChanges()
- * must happen after the screen is painted, or the selection is not displayed.
+ * Revision 1.19 2002/05/03 21:41:18 mpowers No longer clearing the selection
+ * model when updating from display group: we now only modify if a change needs
+ * to be made. No longer listening for selection change during firing of delete
+ * events: delete events cause JTree's to update their selection model. Fix for
+ * paintsSelectionImmediately: TreeAssociation.processRecentChanges() must
+ * happen after the screen is painted, or the selection is not displayed.
*
- * Revision 1.18 2002/04/23 19:12:28 mpowers
- * Reimplemented fireEventsForChanges. Fitter and happier.
+ * Revision 1.18 2002/04/23 19:12:28 mpowers Reimplemented fireEventsForChanges.
+ * Fitter and happier.
*
- * Revision 1.17 2002/04/19 21:18:46 mpowers
- * Removed tree event coalescing, which was causing way too many problems.
- * The fireChangeEvent algorithm is way faster than before, so we should
- * still be better off than before. At least now, we don't have to track
- * whether the view component has encountered a particular node.
+ * Revision 1.17 2002/04/19 21:18:46 mpowers Removed tree event coalescing,
+ * which was causing way too many problems. The fireChangeEvent algorithm is way
+ * faster than before, so we should still be better off than before. At least
+ * now, we don't have to track whether the view component has encountered a
+ * particular node.
*
- * Revision 1.16 2002/04/18 20:36:11 mpowers
- * TreeModelAssociation now populates children group before selected objects.
- * Got rid of the forceOnSync workaround for cancelled selection change.
+ * Revision 1.16 2002/04/18 20:36:11 mpowers TreeModelAssociation now populates
+ * children group before selected objects. Got rid of the forceOnSync workaround
+ * for cancelled selection change.
*
- * Revision 1.15 2002/04/15 21:52:50 mpowers
- * Tightening up TreeModelAssociation and DisplayGroupNode.
- * Now only firing root structure changed once.
- * Now disposing of root's children.
- * Better event coalescing.
+ * Revision 1.15 2002/04/15 21:52:50 mpowers Tightening up TreeModelAssociation
+ * and DisplayGroupNode. Now only firing root structure changed once. Now
+ * disposing of root's children. Better event coalescing.
*
- * Revision 1.14 2002/04/12 21:05:58 mpowers
- * Now distinguishing changes in titles group even better.
+ * Revision 1.14 2002/04/12 21:05:58 mpowers Now distinguishing changes in
+ * titles group even better.
*
- * Revision 1.11 2002/04/10 21:20:04 mpowers
- * Better handling for tree nodes when working with editing contexts.
- * Better handling for invalidation. No longer broadcasting events
- * when nodes have not been "registered" in the tree.
+ * Revision 1.11 2002/04/10 21:20:04 mpowers Better handling for tree nodes when
+ * working with editing contexts. Better handling for invalidation. No longer
+ * broadcasting events when nodes have not been "registered" in the tree.
*
- * Revision 1.10 2002/04/03 20:01:24 mpowers
- * Removed printlns.
+ * Revision 1.10 2002/04/03 20:01:24 mpowers Removed printlns.
*
- * Revision 1.8 2002/03/11 03:16:28 mpowers
- * Better handling of change events; coalescing changes to children group.
+ * Revision 1.8 2002/03/11 03:16:28 mpowers Better handling of change events;
+ * coalescing changes to children group.
*
- * Revision 1.7 2002/03/08 23:19:57 mpowers
- * Refactoring of DelegatingTreeDataSource to facilitate binding of titles
- * and children aspects to the same display group.
+ * Revision 1.7 2002/03/08 23:19:57 mpowers Refactoring of
+ * DelegatingTreeDataSource to facilitate binding of titles and children aspects
+ * to the same display group.
*
- * Revision 1.6 2002/03/07 23:04:36 mpowers
- * Refining TreeColumnAssociation.
+ * Revision 1.6 2002/03/07 23:04:36 mpowers Refining TreeColumnAssociation.
*
- * Revision 1.5 2002/03/06 13:04:16 mpowers
- * Implemented cascading qualifiers in tree nodes.
+ * Revision 1.5 2002/03/06 13:04:16 mpowers Implemented cascading qualifiers in
+ * tree nodes.
*
- * Revision 1.4 2002/03/04 22:47:48 mpowers
- * Fixed sort ordering for titles group. Optimization for delegate selection.
+ * Revision 1.4 2002/03/04 22:47:48 mpowers Fixed sort ordering for titles
+ * group. Optimization for delegate selection.
*
- * Revision 1.3 2002/03/04 12:28:47 mpowers
- * Revised case where children and titles are bound to same display group.
+ * Revision 1.3 2002/03/04 12:28:47 mpowers Revised case where children and
+ * titles are bound to same display group.
*
- * Revision 1.2 2002/03/01 23:42:09 mpowers
- * Implemented TreeColumnAssociation, and updated documentation.
+ * Revision 1.2 2002/03/01 23:42:09 mpowers Implemented TreeColumnAssociation,
+ * and updated documentation.
*
- * Revision 1.1 2002/02/27 23:19:17 mpowers
- * Refactoring of TreeAssociation to create TreeModelAssociation parent.
+ * Revision 1.1 2002/02/27 23:19:17 mpowers Refactoring of TreeAssociation to
+ * create TreeModelAssociation parent.
*
- * Revision 1.38 2002/02/18 03:46:08 mpowers
- * Implemented TreeTableCellRenderer.
+ * Revision 1.38 2002/02/18 03:46:08 mpowers Implemented TreeTableCellRenderer.
*
- * Revision 1.37 2002/02/13 21:20:15 mpowers
- * Updated comments.
+ * Revision 1.37 2002/02/13 21:20:15 mpowers Updated comments.
*
- * Revision 1.36 2001/11/21 15:13:25 mpowers
- * Better repainting for selectionPaintedImmediately.
- * Better handling for selection with multiple instances of the same
- * object in the tree (from yjcheung).
+ * Revision 1.36 2001/11/21 15:13:25 mpowers Better repainting for
+ * selectionPaintedImmediately. Better handling for selection with multiple
+ * instances of the same object in the tree (from yjcheung).
*
- * Revision 1.35 2001/11/20 19:13:51 mpowers
- * Finished implementation of children group's specialized data source.
+ * Revision 1.35 2001/11/20 19:13:51 mpowers Finished implementation of children
+ * group's specialized data source.
*
- * Revision 1.34 2001/11/19 16:30:37 mpowers
- * Tree repaint strategy is now a preference: selectionPaintedImmediately.
+ * Revision 1.34 2001/11/19 16:30:37 mpowers Tree repaint strategy is now a
+ * preference: selectionPaintedImmediately.
*
- * Revision 1.33 2001/11/15 17:56:41 mpowers
- * Initial implementation of data source for the children display group.
+ * Revision 1.33 2001/11/15 17:56:41 mpowers Initial implementation of data
+ * source for the children display group.
*
- * Revision 1.32 2001/11/14 00:05:54 mpowers
- * Eliminated the run later in favor of repainting the component immediately.
- * This makes things more predictable for users of the association that
- * want to listen to mouse or selection events on the tree.
+ * Revision 1.32 2001/11/14 00:05:54 mpowers Eliminated the run later in favor
+ * of repainting the component immediately. This makes things more predictable
+ * for users of the association that want to listen to mouse or selection events
+ * on the tree.
*
- * Revision 1.31 2001/11/02 20:43:15 mpowers
- * Fixes for delegate's shouldChangeSelection veto (from yjcheung).
+ * Revision 1.31 2001/11/02 20:43:15 mpowers Fixes for delegate's
+ * shouldChangeSelection veto (from yjcheung).
*
- * Revision 1.30 2001/10/29 20:42:56 mpowers
- * On selection change, repainting tree before notifying display group;
- * using NSRunLoop instead of SwingUtilities.
+ * Revision 1.30 2001/10/29 20:42:56 mpowers On selection change, repainting
+ * tree before notifying display group; using NSRunLoop instead of
+ * SwingUtilities.
*
- * Revision 1.29 2001/10/12 20:12:53 mpowers
- * Better handling of selection change vetoing when changing selection
- * to a node that is not the sibling of the originally selected node.
+ * Revision 1.29 2001/10/12 20:12:53 mpowers Better handling of selection change
+ * vetoing when changing selection to a node that is not the sibling of the
+ * originally selected node.
*
- * Revision 1.28 2001/09/14 13:40:26 mpowers
- * User-initiated selection changes are now handled on the next event loop
- * so that the component repaints the new selection before any potentially
- * lengthy logic is triggered by the selection change.
+ * Revision 1.28 2001/09/14 13:40:26 mpowers User-initiated selection changes
+ * are now handled on the next event loop so that the component repaints the new
+ * selection before any potentially lengthy logic is triggered by the selection
+ * change.
*
- * Revision 1.27 2001/09/10 14:10:03 mpowers
- * Tree now handles multiple instances of the same object.
+ * Revision 1.27 2001/09/10 14:10:03 mpowers Tree now handles multiple instances
+ * of the same object.
*
- * Revision 1.26 2001/07/18 13:03:32 mpowers
- * TreeNodes now refetch only on demand. Previously, once a node had
- * been fetched, it was always refetched after an invalidate, even if
- * the node was not being displayed.
+ * Revision 1.26 2001/07/18 13:03:32 mpowers TreeNodes now refetch only on
+ * demand. Previously, once a node had been fetched, it was always refetched
+ * after an invalidate, even if the node was not being displayed.
*
- * Revision 1.25 2001/05/14 15:25:35 mpowers
- * No longer copying titles group's data source to children group.
+ * Revision 1.25 2001/05/14 15:25:35 mpowers No longer copying titles group's
+ * data source to children group.
*
- * Revision 1.24 2001/05/08 18:47:34 mpowers
- * Minor fixes for d3.
+ * Revision 1.24 2001/05/08 18:47:34 mpowers Minor fixes for d3.
*
- * Revision 1.23 2001/05/01 00:52:32 mpowers
- * Implemented breadth-first traversal of tree for node.
+ * Revision 1.23 2001/05/01 00:52:32 mpowers Implemented breadth-first traversal
+ * of tree for node.
*
- * Revision 1.22 2001/04/26 01:15:19 mpowers
- * Major clean-up of DisplayGroupNode: fitter, happier, more productive.
+ * Revision 1.22 2001/04/26 01:15:19 mpowers Major clean-up of DisplayGroupNode:
+ * fitter, happier, more productive.
*
- * Revision 1.21 2001/04/22 23:13:35 mpowers
- * Minor bug.
+ * Revision 1.21 2001/04/22 23:13:35 mpowers Minor bug.
*
- * Revision 1.20 2001/04/22 23:05:33 mpowers
- * Totally revised DisplayGroupNode so each object gets its own node
- * (so the nodes are no longer fixed by index).
+ * Revision 1.20 2001/04/22 23:05:33 mpowers Totally revised DisplayGroupNode so
+ * each object gets its own node (so the nodes are no longer fixed by index).
*
- * Revision 1.19 2001/04/21 23:06:33 mpowers
- * A major revisiting to support the revising of DisplayGroupNode.
+ * Revision 1.19 2001/04/21 23:06:33 mpowers A major revisiting to support the
+ * revising of DisplayGroupNode.
*
- * 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 21:35:08 mpowers
- * Now handling circular references in the graph.
+ * Revision 1.17 2001/03/29 21:35:08 mpowers Now handling circular references in
+ * the graph.
*
- * Revision 1.16 2001/03/22 21:25:42 mpowers
- * Fixed some nasty issues with jtree's internal state and array bounds.
+ * Revision 1.16 2001/03/22 21:25:42 mpowers Fixed some nasty issues with
+ * jtree's internal state and array bounds.
*
- * Revision 1.15 2001/03/19 21:37:58 mpowers
- * Improved refresh of titles display group.
- * Fixed dangling selection problem after refresh.
+ * Revision 1.15 2001/03/19 21:37:58 mpowers Improved refresh of titles display
+ * group. Fixed dangling selection problem after refresh.
*
- * Revision 1.14 2001/03/09 22:08:57 mpowers
- * Trying to handle the dangling reference problem after an update.
+ * Revision 1.14 2001/03/09 22:08:57 mpowers Trying to handle the dangling
+ * reference problem after an update.
*
- * Revision 1.13 2001/02/17 17:23:49 mpowers
- * More changes to support compiling with jdk1.1 collections.
+ * Revision 1.13 2001/02/17 17:23:49 mpowers More changes to support compiling
+ * with jdk1.1 collections.
*
- * Revision 1.12 2001/01/25 02:16:25 mpowers
- * TreeModelAssociation now returns DisplayGroupNode.getUserObject.
+ * Revision 1.12 2001/01/25 02:16:25 mpowers TreeModelAssociation now returns
+ * DisplayGroupNode.getUserObject.
*
- * Revision 1.11 2001/01/24 18:14:40 mpowers
- * Fixed problem with leaving children aspect unspecified.
+ * Revision 1.11 2001/01/24 18:14:40 mpowers Fixed problem with leaving children
+ * aspect unspecified.
*
- * Revision 1.10 2001/01/24 17:49:15 mpowers
- * Added getObjectForNode and getNodeForObject convenience methods.
+ * Revision 1.10 2001/01/24 17:49:15 mpowers Added getObjectForNode and
+ * getNodeForObject convenience methods.
*
- * Revision 1.9 2001/01/24 17:44:11 mpowers
- * Renamed getPathForNode to getPathForObject to be more precise.
- * And created a new getPathForNode method.
+ * Revision 1.9 2001/01/24 17:44:11 mpowers Renamed getPathForNode to
+ * getPathForObject to be more precise. And created a new getPathForNode method.
*
- * Revision 1.8 2001/01/24 17:20:29 mpowers
- * Children display group now holds siblings of selected objects
- * in addition to the selected objects.
+ * Revision 1.8 2001/01/24 17:20:29 mpowers Children display group now holds
+ * siblings of selected objects in addition to the selected objects.
*
- * Revision 1.5 2001/01/19 23:21:15 mpowers
- * Fine tuning events broadcast from TreeModelAssociation.
+ * Revision 1.5 2001/01/19 23:21:15 mpowers Fine tuning events broadcast from
+ * TreeModelAssociation.
*
- * Revision 1.4 2001/01/18 21:27:29 mpowers
- * Major rework of TreeModelAssociation.
+ * Revision 1.4 2001/01/18 21:27:29 mpowers Major rework of
+ * TreeModelAssociation.
*
- * Revision 1.2 2001/01/11 20:29:19 mpowers
- * Expanded access to tree event firing methods.
+ * Revision 1.2 2001/01/11 20:29:19 mpowers Expanded access to tree event firing
+ * methods.
*
- * Revision 1.1.1.1 2000/12/21 15:49:18 mpowers
- * Contributing wotonomy.
+ * Revision 1.1.1.1 2000/12/21 15:49:18 mpowers Contributing wotonomy.
*
- * Revision 1.20 2000/12/20 16:25:42 michael
- * Added log to all files.
+ * Revision 1.20 2000/12/20 16:25:42 michael Added log to all files.
*
*
*/
-
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/AbsoluteLayout.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/AbsoluteLayout.java
index 1fef587..579a595 100644
--- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/AbsoluteLayout.java
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/AbsoluteLayout.java
@@ -25,50 +25,43 @@ import java.awt.LayoutManager;
import java.io.Serializable;
/**
- * AbsoluteLayout specifies that all components in the
- * container will be placed according to their size
- * and their location relative to the container's origin. <br><br>
+ * AbsoluteLayout specifies that all components in the container will be placed
+ * according to their size and their location relative to the container's
+ * origin. <br>
+ * <br>
*
- * You can achieve the same effect by setting a container's
- * layout manager to null, but this class allows you to subclass
- * it if you need specific control or functionality.
+ * You can achieve the same effect by setting a container's layout manager to
+ * null, but this class allows you to subclass it if you need specific control
+ * or functionality.
*
* @author michael@mpowers.net
* @author $Author: cgruber $
* @version $Revision: 904 $
*/
-public class AbsoluteLayout implements LayoutManager, Serializable
-{
- public void addLayoutComponent(String name,
- Component comp)
- {
- }
+public class AbsoluteLayout implements LayoutManager, Serializable {
+ public void addLayoutComponent(String name, Component comp) {
+ }
- public void removeLayoutComponent(Component comp)
- {
- }
+ public void removeLayoutComponent(Component comp) {
+ }
- public Dimension preferredLayoutSize(Container parent)
- {
- return minimumLayoutSize( parent );
- }
+ public Dimension preferredLayoutSize(Container parent) {
+ return minimumLayoutSize(parent);
+ }
- public Dimension minimumLayoutSize(Container parent)
- {
- int width = 0;
- int height = 0;
+ public Dimension minimumLayoutSize(Container parent) {
+ int width = 0;
+ int height = 0;
- Component[] c = parent.getComponents();
- for ( int i = 0; i < c.length; i++ )
- {
- width = Math.max( width, c[i].getLocation().x + c[i].getBounds().width );
- height = Math.max( height, c[i].getLocation().y + c[i].getBounds().height );
- }
+ Component[] c = parent.getComponents();
+ for (int i = 0; i < c.length; i++) {
+ width = Math.max(width, c[i].getLocation().x + c[i].getBounds().width);
+ height = Math.max(height, c[i].getLocation().y + c[i].getBounds().height);
+ }
- return new Dimension( width, height );
- }
+ return new Dimension(width, height);
+ }
- public void layoutContainer(Container parent)
- {
- }
+ public void layoutContainer(Container parent) {
+ }
}
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/AlphaTextField.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/AlphaTextField.java
index c36f5e2..9300d35 100644
--- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/AlphaTextField.java
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/AlphaTextField.java
@@ -19,317 +19,286 @@ License along with this library; if not, see http://www.gnu.org
package net.wotonomy.ui.swing.components;
/**
-* AlphaTextField is a "smart" text field that restricts the user's input. The
-* input can be restricted to alphabetic, alphanumeric, or all characters. The
-* maximum number of characters can also be limited.
-* The defaults for this component is alphabetic only string of unlimited length.
-*
-* @author rob@straylight.princeton.com
-* @author $Author: cgruber $
-* @version $Revision: 893 $
-*/
-public class AlphaTextField extends SmartTextField
-{
-
-/*******************************
-* CONSTANTS
-*******************************/
-
-/**
-* Sets the input to alphabetic characters only. The characters "a-z" and "A-Z"
-* are the only valid characters. All other characters will be ignored.
-* @see #getAlphaType()
-*/
- public static final int ALPHABETIC = 0;
-
-/**
-* Sets the input to alphanumeric characters only. The characters "a-z", "A-Z"
-* and "0-9" are the only valid characters. All other characters will be ignored.
-* @see #getAlphaType()
-*/
- public static final int ALPHANUMERIC = 1;
-
-/**
-* Sets the input to alphanumeric characters and a few special characters only.
-* The valid characters are "a-z", "A-Z", "0-9", space, "-", "_", "\", and ":".
-* This is helpful for file names (with paths) as input strings.
-* All other characters will be ignored.
-* @see #getAlphaType()
-*/
- public static final int ALPHANUMERIC_PLUS = 2;
-
-/**
-* Sets the input to all characters without restriction.
-* @see #getAlphaType()
-*/
- public static final int ALL = 3;
-
-
-/*******************************
-* DATA MEMBERS
-*******************************/
-
- // The level of input restrictions, defaults to ALPHABETIC
- private int alphaType;
-
- // The maximum length of the input string, defaults to 0, no maximum
- private int stringLength;
-
-
-/*******************************
-* PUBLIC METHODS
-*******************************/
-
-/**
-* The default constructor of this class. The default string of this text
-* field is set to the empty string (""). The maximum length is set to 0,
-* which specifies no limit.
-*/
- public AlphaTextField()
- {
- this("", 0);
- }
-
-/**
-* Constructor of this class with the initial text of the text field specified.
-* The maximum length is set to 0, which specifies no limit.
-* @param text Initial text of the text field.
-*/
- public AlphaTextField(String text)
- {
- this(text, 0);
- }
-
-/**
-* Constructor of this class with width (in columns) of the text field specified.
-* The initial text is set to the empty string (""). The maximum length is set
-* to 0, which specifies no limit.
-* @param columns The width of the text field in characters.
-*/
- public AlphaTextField(int columns)
- {
- this("", columns);
- }
-
-/**
-* Constructor of this class with width (in columns) and initial text of the
-* text field specified. The maximum length is set to 0, which specifies no limit.
-* @param text Initial text of the text field.
-* @param columns The width of the text field in characters.
-*/
- public AlphaTextField(String text, int columns)
- {
- super(text, columns);
- }
-
-/**
-* Constructor that allows the user to set the Alpha type of the text field
-* and the maximum string length.
-* @param anAlphaType The character restriction type.
-* @param aLength The maximum number of characters allowed in the string.
-*/
- public AlphaTextField(int anAlphaType, int aLength)
- {
- super( "", 0 );
- setAlphaType( anAlphaType );
- setStringLength( aLength );
- }
-
-/**
-* Gets the current restriction type of this text field.
-* @see #ALPHABETIC
-* @see #ALPHANUMERIC
-* @see #ALPHANUMERIC_PLUS
-* @see #ALL
-* @return The current restriction type as defined by the constansts of this class.
-*/
- public int getAlphaType()
- {
- return alphaType;
- }
-
-/**
-* Sets the restriction type of this text field.
-* @see #ALPHABETIC
-* @see #ALPHANUMERIC
-* @see #ALPHANUMERIC_PLUS
-* @see #ALL
-* @param newAlphaType The restriction of this text field.
-*/
- public void setAlphaType(int newAlphaType)
- {
- switch (newAlphaType)
- {
- case ALPHABETIC:
- case ALPHANUMERIC:
- case ALPHANUMERIC_PLUS:
- case ALL:
- {
- alphaType = newAlphaType;
- break;
- }
- default:
- {
- alphaType = ALPHABETIC;
- break;
- }
- }
- }
-
-/**
-* Sets the maximum string length of this text field. If the length is set to
-* zero, then there is no limit. The default string length is zero. Negative
-* sizes will set the length to zero.
-* @param newStringLength The maximum length of the string that the user can input.
-*/
- public void setStringLength(int newStringLength)
- {
- if (newStringLength < 0)
- {
- stringLength = 0;
- }
- else
- {
- stringLength = newStringLength;
- }
- }
-
-/**
-* Gets the current length of the maximum string size the user can enter.
-* @return The maximum length the string of the text field can be.
-*/
- public int getStringLength()
- {
- return stringLength;
- }
-
-
-/*******************************
-* PROTECTED METHODS
-*******************************/
-
- protected boolean isValidCharacter(char aChar)
- {
- // if its a non-printable character, then its ok
- if ((aChar < ' ') || (aChar > '~'))
- {
- return true;
- }
-
- // can only be a printable character now, check it for validation
- return isValidCharacterType(aChar);
- }
-
- protected boolean isValidString(String aString)
- {
- if (aString.length() > stringLength)
- {
- return false;
- }
-
- for (int i = 0; i < aString.length(); ++i)
- {
- if (!(isValidCharacterType(aString.charAt(i))))
- {
- return false;
- }
- }
-
- return true;
- }
-
- protected void postProcessing()
- {
- // No need to do anything.
- }
-
-
-/*******************************
-* PROTECTED METHODS
-*******************************/
-
- private boolean isValidCharacterType(char aChar)
- {
- switch (alphaType)
- {
- case ALPHABETIC:
- {
- if (!(isValidAlphabeticCharacter(aChar)))
- {
- return false;
- }
- break;
- }
- case ALPHANUMERIC:
- {
- if (!(isValidAlphanumericCharacter(aChar)))
- {
- return false;
- }
- break;
- }
- case ALPHANUMERIC_PLUS:
- {
- if (!(isValidAlphanumericPlusCharacter(aChar)))
- {
- return false;
- }
- break;
- }
- case ALL:
- {
- if (!(isValidAllCharacter(aChar)))
- {
- return false;
- }
- break;
- }
- default:
- {
- return false;
- }
- }
-
- return true;
- }
-
- private boolean isValidAlphabeticCharacter(char aChar)
- {
- if (((aChar < 'A') || (aChar > 'Z')) && ((aChar < 'a') || (aChar > 'z')))
- {
- return false;
- }
- return true;
- }
-
- private boolean isValidAlphanumericCharacter(char aChar)
- {
- if (((aChar < 'A') || (aChar > 'Z')) && ((aChar < 'a') || (aChar > 'z')) && ((aChar < '0') || (aChar > '9')))
- {
- return false;
- }
- return true;
- }
-
- private boolean isValidAlphanumericPlusCharacter(char aChar)
- {
- if (((aChar < 'A') || (aChar > 'Z')) && ((aChar < 'a') || (aChar > 'z')) && ((aChar < '0') || (aChar > '9')))
- {
- if ((aChar != ' ') && (aChar != '_') && (aChar != '-') && (aChar != ':') && (aChar != '\\'))
- {
- return false;
- }
- }
- return true;
- }
-
- private boolean isValidAllCharacter(char aChar)
- {
- if ((aChar < ' ') || (aChar > '~'))
- {
- return false;
- }
- return true;
- }
+ * AlphaTextField is a "smart" text field that restricts the user's input. The
+ * input can be restricted to alphabetic, alphanumeric, or all characters. The
+ * maximum number of characters can also be limited. The defaults for this
+ * component is alphabetic only string of unlimited length.
+ *
+ * @author rob@straylight.princeton.com
+ * @author $Author: cgruber $
+ * @version $Revision: 893 $
+ */
+public class AlphaTextField extends SmartTextField {
+
+ /*******************************
+ * CONSTANTS
+ *******************************/
+
+ /**
+ * Sets the input to alphabetic characters only. The characters "a-z" and "A-Z"
+ * are the only valid characters. All other characters will be ignored.
+ *
+ * @see #getAlphaType()
+ */
+ public static final int ALPHABETIC = 0;
+
+ /**
+ * Sets the input to alphanumeric characters only. The characters "a-z", "A-Z"
+ * and "0-9" are the only valid characters. All other characters will be
+ * ignored.
+ *
+ * @see #getAlphaType()
+ */
+ public static final int ALPHANUMERIC = 1;
+
+ /**
+ * Sets the input to alphanumeric characters and a few special characters only.
+ * The valid characters are "a-z", "A-Z", "0-9", space, "-", "_", "\", and ":".
+ * This is helpful for file names (with paths) as input strings. All other
+ * characters will be ignored.
+ *
+ * @see #getAlphaType()
+ */
+ public static final int ALPHANUMERIC_PLUS = 2;
+
+ /**
+ * Sets the input to all characters without restriction.
+ *
+ * @see #getAlphaType()
+ */
+ public static final int ALL = 3;
+
+ /*******************************
+ * DATA MEMBERS
+ *******************************/
+
+ // The level of input restrictions, defaults to ALPHABETIC
+ private int alphaType;
+
+ // The maximum length of the input string, defaults to 0, no maximum
+ private int stringLength;
+
+ /*******************************
+ * PUBLIC METHODS
+ *******************************/
+
+ /**
+ * The default constructor of this class. The default string of this text field
+ * is set to the empty string (""). The maximum length is set to 0, which
+ * specifies no limit.
+ */
+ public AlphaTextField() {
+ this("", 0);
+ }
+
+ /**
+ * Constructor of this class with the initial text of the text field specified.
+ * The maximum length is set to 0, which specifies no limit.
+ *
+ * @param text Initial text of the text field.
+ */
+ public AlphaTextField(String text) {
+ this(text, 0);
+ }
+
+ /**
+ * Constructor of this class with width (in columns) of the text field
+ * specified. The initial text is set to the empty string (""). The maximum
+ * length is set to 0, which specifies no limit.
+ *
+ * @param columns The width of the text field in characters.
+ */
+ public AlphaTextField(int columns) {
+ this("", columns);
+ }
+
+ /**
+ * Constructor of this class with width (in columns) and initial text of the
+ * text field specified. The maximum length is set to 0, which specifies no
+ * limit.
+ *
+ * @param text Initial text of the text field.
+ * @param columns The width of the text field in characters.
+ */
+ public AlphaTextField(String text, int columns) {
+ super(text, columns);
+ }
+
+ /**
+ * Constructor that allows the user to set the Alpha type of the text field and
+ * the maximum string length.
+ *
+ * @param anAlphaType The character restriction type.
+ * @param aLength The maximum number of characters allowed in the string.
+ */
+ public AlphaTextField(int anAlphaType, int aLength) {
+ super("", 0);
+ setAlphaType(anAlphaType);
+ setStringLength(aLength);
+ }
+
+ /**
+ * Gets the current restriction type of this text field.
+ *
+ * @see #ALPHABETIC
+ * @see #ALPHANUMERIC
+ * @see #ALPHANUMERIC_PLUS
+ * @see #ALL
+ * @return The current restriction type as defined by the constansts of this
+ * class.
+ */
+ public int getAlphaType() {
+ return alphaType;
+ }
+
+ /**
+ * Sets the restriction type of this text field.
+ *
+ * @see #ALPHABETIC
+ * @see #ALPHANUMERIC
+ * @see #ALPHANUMERIC_PLUS
+ * @see #ALL
+ * @param newAlphaType The restriction of this text field.
+ */
+ public void setAlphaType(int newAlphaType) {
+ switch (newAlphaType) {
+ case ALPHABETIC:
+ case ALPHANUMERIC:
+ case ALPHANUMERIC_PLUS:
+ case ALL: {
+ alphaType = newAlphaType;
+ break;
+ }
+ default: {
+ alphaType = ALPHABETIC;
+ break;
+ }
+ }
+ }
+
+ /**
+ * Sets the maximum string length of this text field. If the length is set to
+ * zero, then there is no limit. The default string length is zero. Negative
+ * sizes will set the length to zero.
+ *
+ * @param newStringLength The maximum length of the string that the user can
+ * input.
+ */
+ public void setStringLength(int newStringLength) {
+ if (newStringLength < 0) {
+ stringLength = 0;
+ } else {
+ stringLength = newStringLength;
+ }
+ }
+
+ /**
+ * Gets the current length of the maximum string size the user can enter.
+ *
+ * @return The maximum length the string of the text field can be.
+ */
+ public int getStringLength() {
+ return stringLength;
+ }
+
+ /*******************************
+ * PROTECTED METHODS
+ *******************************/
+
+ protected boolean isValidCharacter(char aChar) {
+ // if its a non-printable character, then its ok
+ if ((aChar < ' ') || (aChar > '~')) {
+ return true;
+ }
+
+ // can only be a printable character now, check it for validation
+ return isValidCharacterType(aChar);
+ }
+
+ protected boolean isValidString(String aString) {
+ if (aString.length() > stringLength) {
+ return false;
+ }
+
+ for (int i = 0; i < aString.length(); ++i) {
+ if (!(isValidCharacterType(aString.charAt(i)))) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ protected void postProcessing() {
+ // No need to do anything.
+ }
+
+ /*******************************
+ * PROTECTED METHODS
+ *******************************/
+
+ private boolean isValidCharacterType(char aChar) {
+ switch (alphaType) {
+ case ALPHABETIC: {
+ if (!(isValidAlphabeticCharacter(aChar))) {
+ return false;
+ }
+ break;
+ }
+ case ALPHANUMERIC: {
+ if (!(isValidAlphanumericCharacter(aChar))) {
+ return false;
+ }
+ break;
+ }
+ case ALPHANUMERIC_PLUS: {
+ if (!(isValidAlphanumericPlusCharacter(aChar))) {
+ return false;
+ }
+ break;
+ }
+ case ALL: {
+ if (!(isValidAllCharacter(aChar))) {
+ return false;
+ }
+ break;
+ }
+ default: {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private boolean isValidAlphabeticCharacter(char aChar) {
+ if (((aChar < 'A') || (aChar > 'Z')) && ((aChar < 'a') || (aChar > 'z'))) {
+ return false;
+ }
+ return true;
+ }
+
+ private boolean isValidAlphanumericCharacter(char aChar) {
+ if (((aChar < 'A') || (aChar > 'Z')) && ((aChar < 'a') || (aChar > 'z')) && ((aChar < '0') || (aChar > '9'))) {
+ return false;
+ }
+ return true;
+ }
+
+ private boolean isValidAlphanumericPlusCharacter(char aChar) {
+ if (((aChar < 'A') || (aChar > 'Z')) && ((aChar < 'a') || (aChar > 'z')) && ((aChar < '0') || (aChar > '9'))) {
+ if ((aChar != ' ') && (aChar != '_') && (aChar != '-') && (aChar != ':') && (aChar != '\\')) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private boolean isValidAllCharacter(char aChar) {
+ if ((aChar < ' ') || (aChar > '~')) {
+ return false;
+ }
+ return true;
+ }
}
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/AlternatingRowCellRenderer.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/AlternatingRowCellRenderer.java
index 46d2693..fb4824c 100644
--- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/AlternatingRowCellRenderer.java
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/AlternatingRowCellRenderer.java
@@ -28,102 +28,79 @@ import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellRenderer;
/**
-* A TableCellRenderer that wraps another TableCellRenderer
-* and sets the background to the specified color for odd-numbered rows.
-* This makes every other row appear to be a different color,
-* which helps users distinguish rows of data in densely-packed
-* tables.
-*
-* @author michael@mpowers.net
-* @author $Author: cgruber $
-* @version $Revision: 904 $
-*/
+ * A TableCellRenderer that wraps another TableCellRenderer and sets the
+ * background to the specified color for odd-numbered rows. This makes every
+ * other row appear to be a different color, which helps users distinguish rows
+ * of data in densely-packed tables.
+ *
+ * @author michael@mpowers.net
+ * @author $Author: cgruber $
+ * @version $Revision: 904 $
+ */
public class AlternatingRowCellRenderer implements TableCellRenderer {
- protected TableCellRenderer wrappedRenderer;
- protected Color alternateColor;
-
- /**
- * Default constructor uses a lighter shade of the system control color
- * and wraps a DefaultTableCellRenderer.
- */
- public AlternatingRowCellRenderer()
- {
- this( new DefaultTableCellRenderer() );
- }
-
- /**
- * Uses the specified color for the background of the alternating rows,
- * and wraps a DefaultTableCellRenderer.
- */
- public AlternatingRowCellRenderer(
- Color aColor )
- {
- this( aColor, new DefaultTableCellRenderer() );
- }
+ protected TableCellRenderer wrappedRenderer;
+ protected Color alternateColor;
- /**
- * Uses the uses a lighter shade of the system control color
- * for the background of the alternating rows,
- * and wraps the specified TableCellRenderer.
- */
- public AlternatingRowCellRenderer(
- TableCellRenderer aRenderer )
- {
- Color c = UIManager.getColor( "control" );
- c = new Color( // lighten this color just slightly
- (int) ( c.getRed() + ( ( 255 - c.getRed() ) / 1.5 ) ),
- (int) ( c.getGreen() + ( ( 255 - c.getGreen() ) / 1.5 ) ),
- (int) ( c.getBlue() + ( ( 255 - c.getBlue() ) / 1.5 ) ) );
+ /**
+ * Default constructor uses a lighter shade of the system control color and
+ * wraps a DefaultTableCellRenderer.
+ */
+ public AlternatingRowCellRenderer() {
+ this(new DefaultTableCellRenderer());
+ }
- alternateColor = c;
- wrappedRenderer = aRenderer;
- }
+ /**
+ * Uses the specified color for the background of the alternating rows, and
+ * wraps a DefaultTableCellRenderer.
+ */
+ public AlternatingRowCellRenderer(Color aColor) {
+ this(aColor, new DefaultTableCellRenderer());
+ }
- /**
- * Uses the specified color for the background of the alternating rows,
- * and wraps the specified TableCellRenderer.
- */
- public AlternatingRowCellRenderer(
- Color aColor, TableCellRenderer aRenderer )
- {
- alternateColor = aColor;
- wrappedRenderer = aRenderer;
- }
+ /**
+ * Uses the uses a lighter shade of the system control color for the background
+ * of the alternating rows, and wraps the specified TableCellRenderer.
+ */
+ public AlternatingRowCellRenderer(TableCellRenderer aRenderer) {
+ Color c = UIManager.getColor("control");
+ c = new Color( // lighten this color just slightly
+ (int) (c.getRed() + ((255 - c.getRed()) / 1.5)), (int) (c.getGreen() + ((255 - c.getGreen()) / 1.5)),
+ (int) (c.getBlue() + ((255 - c.getBlue()) / 1.5)));
- public Component getTableCellRendererComponent(
- JTable table, Object value,
- boolean isSelected, boolean hasFocus,
- int row, int column)
- {
- Component result = wrappedRenderer.getTableCellRendererComponent(
- table, value, isSelected, hasFocus, row, column );
- if ( ! isSelected )
- {
- if ( row % 2 == 0 )
- {
- if ( ! result.getBackground().equals( table.getBackground() ) )
- {
- result.setBackground( table.getBackground() );
- }
- }
- else
- {
- if ( ! result.getBackground().equals( alternateColor ) )
- {
- // jdk1.3's default renderer is opaque
- if ( result instanceof JComponent )
- {
- ((JComponent)result).setOpaque( true );
- }
-
- result.setBackground( alternateColor );
- }
- }
- }
- return result;
- }
-}
+ alternateColor = c;
+ wrappedRenderer = aRenderer;
+ }
+ /**
+ * Uses the specified color for the background of the alternating rows, and
+ * wraps the specified TableCellRenderer.
+ */
+ public AlternatingRowCellRenderer(Color aColor, TableCellRenderer aRenderer) {
+ alternateColor = aColor;
+ wrappedRenderer = aRenderer;
+ }
+ public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
+ int row, int column) {
+ Component result = wrappedRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row,
+ column);
+ if (!isSelected) {
+ if (row % 2 == 0) {
+ if (!result.getBackground().equals(table.getBackground())) {
+ result.setBackground(table.getBackground());
+ }
+ } else {
+ if (!result.getBackground().equals(alternateColor)) {
+ // jdk1.3's default renderer is opaque
+ if (result instanceof JComponent) {
+ ((JComponent) result).setOpaque(true);
+ }
+ result.setBackground(alternateColor);
+ }
+ }
+ }
+ return result;
+ }
+}
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/BetterFlowLayout.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/BetterFlowLayout.java
index 1c438b6..632fb59 100644
--- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/BetterFlowLayout.java
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/BetterFlowLayout.java
@@ -25,491 +25,505 @@ import java.awt.FlowLayout;
import java.awt.Insets;
/**
- * BetterFlowLayout works just like FlowLayout, except that
- * you can specify a vertical orientation in addition to the
- * usual horizontal orientations. You can also specify that
- * all the components be sized to the same height and/or width.
- * By default, the behavior is identical to FlowLayout.
+ * BetterFlowLayout works just like FlowLayout, except that you can specify a
+ * vertical orientation in addition to the usual horizontal orientations. You
+ * can also specify that all the components be sized to the same height and/or
+ * width. By default, the behavior is identical to FlowLayout.
*
* @author michael@mpowers.net
* @author $Author: cgruber $
- * @version $Revision: 904 $
- * $Date: 2006-02-18 18:19:05 -0500 (Sat, 18 Feb 2006) $
+ * @version $Revision: 904 $ $Date: 2006-02-18 18:19:05 -0500 (Sat, 18 Feb 2006)
+ * $
*/
public class BetterFlowLayout extends FlowLayout {
- /**
- * This value indicates vertical orientation and
- * that each column of components should be top-justified.
- */
- public static final int TOP = 32;
-
- /**
- * This value indicates vertical orientation and
- * that each column of components should be centered.
- */
- public static final int CENTER_VERTICAL = 16;
-
- /**
- * This value indicates vertical orientation and
- * that each column of components should be bottom-justified.
- */
- public static final int BOTTOM = 8;
-
- /**
- * Tracks orientation.
- */
- protected boolean isHorizontal = true;
-
- /**
- * Tracks component sizing of width.
- */
- protected boolean isWidthUniform = false;
- /**
- * Tracks component sizing of height.
- */
- protected boolean isHeightUniform = false;
-
- /**
- * Constructs a new Flow Layout with a centered alignment and a
- * default 5-unit horizontal and vertical gap.
- */
- public BetterFlowLayout() {
- this(CENTER, 5, 5);
- }
-
- /**
- * Constructs a new Flow Layout with the specified alignment and a
- * default 5-unit horizontal and vertical gap.
- * The value of the alignment argument must be one of
- * <code>BetterFlowLayout.LEFT</code>, <code>BetterFlowLayout.RIGHT</code>,
- * or <code>BetterFlowLayout.CENTER</code>.
- * @param align the alignment value
- */
- public BetterFlowLayout(int align) {
- this(align, 5, 5);
- }
-
- /**
- * Creates a new flow layout manager with the indicated alignment
- * and the indicated horizontal and vertical gaps.
- * <p>
- * The value of the alignment argument must be one of
- * <code>BetterFlowLayout.LEFT</code>, <code>BetterFlowLayout.RIGHT</code>,
- * or <code>BetterFlowLayout.CENTER</code>.
- * @param align the alignment value.
- * @param hgap the horizontal gap between components.
- * @param vgap the vertical gap between components.
- */
- public BetterFlowLayout(int align, int hgap, int vgap) {
- setHgap(hgap);
- setVgap(vgap);
- setAlignment(align);
- }
-
- /**
- * Sets whether all components should have the same height.
- * @param isUniform the new value.
- * @see #isHeightUniform
- */
- public void setHeightUniform(boolean isUniform) {
- isHeightUniform = isUniform;
- }
-
- /**
- * Sets whether all components should have the same width.
- * @param isUniform the new value.
- * @see #isWidthUniform
- */
- public void setWidthUniform(boolean isUniform) {
- isWidthUniform = isUniform;
- }
-
- /**
- * Determines whether all components will have the same height.
- * The uniform height will be the maximum of the preferred heights
- * of all the components in the container.
- * This value defaults to false.
- * @return whether components will have the same height.
- */
- public boolean isHeightUniform() {
- return isHeightUniform;
- }
-
- /**
- * Determines whether all components will have the same width.
- * The uniform height will be the maximum of the preferred widths
- * of all the components in the container.
- * This value defaults to false.
- * @return whether components will have the same width.
- */
- public boolean isWidthUniform() {
- return isWidthUniform;
- }
-
- /**
- * Sets the alignment for this layout.
- * Possible values for horizontal orientation are <code>LEFT</code>,
- * <code>RIGHT</code>, and <code>CENTER</code>.
- * Possible values for vertical orientation are <code>TOP</code>,
- * <code>BOTTOM</code>, and <code>CENTER_VERTICAL</code>.
- * @param align the alignment value.
- * @see java.awt.FlowLayout#getAlignment
- */
- public void setAlignment(int align) {
- if ( ( align == TOP ) || ( align == BOTTOM ) || ( align == CENTER_VERTICAL ) )
- {
- isHorizontal = false;
- }
- else
- {
- isHorizontal = true;
- }
-
- super.setAlignment( align );
- }
-
- /**
- * Returns the preferred dimensions for this layout given the components
- * in the specified target container.
- * @param target the component which needs to be laid out
- * @return the preferred dimensions to lay out the
- * subcomponents of the specified container.
- * @see Container
- * @see #minimumLayoutSize
- * @see java.awt.Container#getPreferredSize
- */
- public Dimension preferredLayoutSize(Container target) {
- if ( isHorizontal ) {
- return preferredLayoutSizeHorizontal( target );
- } else {
- return preferredLayoutSizeVertical( target );
- }
- }
-
- /**
- * Returns the preferred dimensions for this layout given the components
- * in the specified target container.
- * @param target the component which needs to be laid out
- * @return the preferred dimensions to lay out the
- * subcomponents of the specified container.
- * @see Container
- * @see #minimumLayoutSize
- * @see java.awt.Container#getPreferredSize
- */
- public Dimension preferredLayoutSizeHorizontal(Container target) {
- synchronized (target.getTreeLock()) {
- Dimension dim = new Dimension(0, 0);
- int nmembers = target.getComponentCount();
- int maxWidth = 0;
-
- for (int i = 0 ; i < nmembers ; i++) {
- Component m = target.getComponent(i);
- if (m.isVisible()) {
- Dimension d = m.getPreferredSize();
- dim.height = Math.max(dim.height, d.height);
- maxWidth = Math.max(maxWidth, d.width);
- if (i > 0) {
- dim.width += getHgap();
- }
- dim.width += d.width;
- }
- }
- if ( isWidthUniform )
- dim.width = ( maxWidth + getHgap() ) * nmembers - getHgap();
- Insets insets = target.getInsets();
- dim.width += insets.left + insets.right + getHgap()*2;
- dim.height += insets.top + insets.bottom + getVgap()*2;
- return dim;
- }
- }
-
- /**
- * Returns the preferred dimensions for this layout given the components
- * in the specified target container.
- * @param target the component which needs to be laid out
- * @return the preferred dimensions to lay out the
- * subcomponents of the specified container.
- * @see Container
- * @see #minimumLayoutSize
- * @see java.awt.Container#getPreferredSize
- */
- public Dimension preferredLayoutSizeVertical(Container target) {
- synchronized (target.getTreeLock()) {
- Dimension dim = new Dimension(0, 0);
- int nmembers = target.getComponentCount();
- int maxHeight = 0;
-
- for (int i = 0 ; i < nmembers ; i++) {
- Component m = target.getComponent(i);
- if (m.isVisible()) {
- Dimension d = m.getPreferredSize();
- dim.width = Math.max(dim.width, d.width);
- maxHeight = Math.max(maxHeight, d.height);
- if (i > 0) {
- dim.height += getVgap();
- }
- dim.height += d.height;
- }
- }
- if ( isHeightUniform )
- dim.height = ( maxHeight + getVgap() ) * nmembers - getVgap();
- Insets insets = target.getInsets();
- dim.width += insets.left + insets.right + getHgap()*2;
- dim.height += insets.top + insets.bottom + getVgap()*2;
- return dim;
- }
- }
-
- /**
- * Returns the minimum dimensions needed to layout the components
- * contained in the specified target container.
- * @param target the component which needs to be laid out
- * @return the minimum dimensions to lay out the
- * subcomponents of the specified container.
- * @see #preferredLayoutSize
- * @see java.awt.Container
- * @see java.awt.Container#doLayout
- */
- public Dimension minimumLayoutSize(Container target) {
- // preferred size is also the minimum size
- if ( isHorizontal ) {
- return preferredLayoutSizeHorizontal( target );
- } else {
- return preferredLayoutSizeVertical( target );
- }
- }
-
- /**
- * Lays out the container. This method lets each component take
- * its preferred size by reshaping the components in the
- * target container in order to satisfy the constraints of
- * this <code>BetterFlowLayout</code> object.
- * @param target the specified component being laid out.
- * @see Container
- * @see java.awt.Container#doLayout
- */
- public void layoutContainer(Container target) {
- if ( isHorizontal ) {
- layoutContainerHorizontal( target );
- } else {
- layoutContainerVertical( target );
- }
- }
-
- /**
- * Lays out the container. This method lets each component take
- * its preferred size by reshaping the components in the
- * target container in order to satisfy the constraints of
- * this <code>BetterFlowLayout</code> object.
- * @param target the specified component being laid out.
- * @see Container
- * @see java.awt.Container#doLayout
- */
- protected void layoutContainerHorizontal(Container target) {
- synchronized (target.getTreeLock()) {
- Insets insets = target.getInsets();
- int maxwidth = target.getSize().width - (insets.left + insets.right + getHgap()*2);
- int nmembers = target.getComponentCount();
- int x = 0, y = insets.top + getVgap();
- int rowh = 0, start = 0;
-
- boolean ltr = true; // target.getComponentOrientation().isLeftToRight();
- Dimension uniform = getUniformDimension( target );
-
- for (int i = 0 ; i < nmembers ; i++) {
- Component m = target.getComponent(i);
- if (m.isVisible()) {
- Dimension d = m.getPreferredSize();
- if ( isWidthUniform )
- d.width = uniform.width;
- if ( isHeightUniform )
- d.height = uniform.height;
- m.setSize(d.width, d.height);
-
- if ((x == 0) || ((x + d.width) <= maxwidth)) {
- if (x > 0) {
- x += getHgap();
- }
- x += d.width;
- rowh = Math.max(rowh, d.height);
- } else {
- moveComponentsHorizontal(target, insets.left + getHgap(), y, maxwidth - x, rowh, start, i, ltr);
- x = d.width;
- y += getVgap() + rowh;
- rowh = d.height;
- start = i;
- }
- }
- }
- moveComponentsHorizontal(target, insets.left + getHgap(), y, maxwidth - x, rowh, start, nmembers, ltr);
- }
- }
-
- /**
- * Centers the elements in the specified row, if there is any slack.
- * @param target the component which needs to be moved
- * @param x the x coordinate
- * @param y the y coordinate
- * @param width the width dimensions
- * @param height the height dimensions
- * @param rowStart the beginning of the row
- * @param rowEnd the the ending of the row
- */
- private void moveComponentsHorizontal(Container target, int x, int y, int width, int height,
- int rowStart, int rowEnd, boolean ltr) {
- synchronized (target.getTreeLock()) {
- switch (getAlignment()) {
- case LEFT:
- x += ltr ? 0 : width;
- break;
- case CENTER:
- x += width / 2;
- break;
- case RIGHT:
- x += ltr ? width : 0;
- break;
+ /**
+ * This value indicates vertical orientation and that each column of components
+ * should be top-justified.
+ */
+ public static final int TOP = 32;
+
+ /**
+ * This value indicates vertical orientation and that each column of components
+ * should be centered.
+ */
+ public static final int CENTER_VERTICAL = 16;
+
+ /**
+ * This value indicates vertical orientation and that each column of components
+ * should be bottom-justified.
+ */
+ public static final int BOTTOM = 8;
+
+ /**
+ * Tracks orientation.
+ */
+ protected boolean isHorizontal = true;
+
+ /**
+ * Tracks component sizing of width.
+ */
+ protected boolean isWidthUniform = false;
+ /**
+ * Tracks component sizing of height.
+ */
+ protected boolean isHeightUniform = false;
+
+ /**
+ * Constructs a new Flow Layout with a centered alignment and a default 5-unit
+ * horizontal and vertical gap.
+ */
+ public BetterFlowLayout() {
+ this(CENTER, 5, 5);
+ }
+
+ /**
+ * Constructs a new Flow Layout with the specified alignment and a default
+ * 5-unit horizontal and vertical gap. The value of the alignment argument must
+ * be one of <code>BetterFlowLayout.LEFT</code>,
+ * <code>BetterFlowLayout.RIGHT</code>, or <code>BetterFlowLayout.CENTER</code>.
+ *
+ * @param align the alignment value
+ */
+ public BetterFlowLayout(int align) {
+ this(align, 5, 5);
+ }
+
+ /**
+ * Creates a new flow layout manager with the indicated alignment and the
+ * indicated horizontal and vertical gaps.
+ * <p>
+ * The value of the alignment argument must be one of
+ * <code>BetterFlowLayout.LEFT</code>, <code>BetterFlowLayout.RIGHT</code>, or
+ * <code>BetterFlowLayout.CENTER</code>.
+ *
+ * @param align the alignment value.
+ * @param hgap the horizontal gap between components.
+ * @param vgap the vertical gap between components.
+ */
+ public BetterFlowLayout(int align, int hgap, int vgap) {
+ setHgap(hgap);
+ setVgap(vgap);
+ setAlignment(align);
+ }
+
+ /**
+ * Sets whether all components should have the same height.
+ *
+ * @param isUniform the new value.
+ * @see #isHeightUniform
+ */
+ public void setHeightUniform(boolean isUniform) {
+ isHeightUniform = isUniform;
+ }
+
+ /**
+ * Sets whether all components should have the same width.
+ *
+ * @param isUniform the new value.
+ * @see #isWidthUniform
+ */
+ public void setWidthUniform(boolean isUniform) {
+ isWidthUniform = isUniform;
+ }
+
+ /**
+ * Determines whether all components will have the same height. The uniform
+ * height will be the maximum of the preferred heights of all the components in
+ * the container. This value defaults to false.
+ *
+ * @return whether components will have the same height.
+ */
+ public boolean isHeightUniform() {
+ return isHeightUniform;
+ }
+
+ /**
+ * Determines whether all components will have the same width. The uniform
+ * height will be the maximum of the preferred widths of all the components in
+ * the container. This value defaults to false.
+ *
+ * @return whether components will have the same width.
+ */
+ public boolean isWidthUniform() {
+ return isWidthUniform;
+ }
+
+ /**
+ * Sets the alignment for this layout. Possible values for horizontal
+ * orientation are <code>LEFT</code>, <code>RIGHT</code>, and
+ * <code>CENTER</code>. Possible values for vertical orientation are
+ * <code>TOP</code>, <code>BOTTOM</code>, and <code>CENTER_VERTICAL</code>.
+ *
+ * @param align the alignment value.
+ * @see java.awt.FlowLayout#getAlignment
+ */
+ public void setAlignment(int align) {
+ if ((align == TOP) || (align == BOTTOM) || (align == CENTER_VERTICAL)) {
+ isHorizontal = false;
+ } else {
+ isHorizontal = true;
+ }
+
+ super.setAlignment(align);
+ }
+
+ /**
+ * Returns the preferred dimensions for this layout given the components in the
+ * specified target container.
+ *
+ * @param target the component which needs to be laid out
+ * @return the preferred dimensions to lay out the subcomponents of the
+ * specified container.
+ * @see Container
+ * @see #minimumLayoutSize
+ * @see java.awt.Container#getPreferredSize
+ */
+ public Dimension preferredLayoutSize(Container target) {
+ if (isHorizontal) {
+ return preferredLayoutSizeHorizontal(target);
+ } else {
+ return preferredLayoutSizeVertical(target);
+ }
+ }
+
+ /**
+ * Returns the preferred dimensions for this layout given the components in the
+ * specified target container.
+ *
+ * @param target the component which needs to be laid out
+ * @return the preferred dimensions to lay out the subcomponents of the
+ * specified container.
+ * @see Container
+ * @see #minimumLayoutSize
+ * @see java.awt.Container#getPreferredSize
+ */
+ public Dimension preferredLayoutSizeHorizontal(Container target) {
+ synchronized (target.getTreeLock()) {
+ Dimension dim = new Dimension(0, 0);
+ int nmembers = target.getComponentCount();
+ int maxWidth = 0;
+
+ for (int i = 0; i < nmembers; i++) {
+ Component m = target.getComponent(i);
+ if (m.isVisible()) {
+ Dimension d = m.getPreferredSize();
+ dim.height = Math.max(dim.height, d.height);
+ maxWidth = Math.max(maxWidth, d.width);
+ if (i > 0) {
+ dim.width += getHgap();
+ }
+ dim.width += d.width;
+ }
+ }
+ if (isWidthUniform)
+ dim.width = (maxWidth + getHgap()) * nmembers - getHgap();
+ Insets insets = target.getInsets();
+ dim.width += insets.left + insets.right + getHgap() * 2;
+ dim.height += insets.top + insets.bottom + getVgap() * 2;
+ return dim;
+ }
+ }
+
+ /**
+ * Returns the preferred dimensions for this layout given the components in the
+ * specified target container.
+ *
+ * @param target the component which needs to be laid out
+ * @return the preferred dimensions to lay out the subcomponents of the
+ * specified container.
+ * @see Container
+ * @see #minimumLayoutSize
+ * @see java.awt.Container#getPreferredSize
+ */
+ public Dimension preferredLayoutSizeVertical(Container target) {
+ synchronized (target.getTreeLock()) {
+ Dimension dim = new Dimension(0, 0);
+ int nmembers = target.getComponentCount();
+ int maxHeight = 0;
+
+ for (int i = 0; i < nmembers; i++) {
+ Component m = target.getComponent(i);
+ if (m.isVisible()) {
+ Dimension d = m.getPreferredSize();
+ dim.width = Math.max(dim.width, d.width);
+ maxHeight = Math.max(maxHeight, d.height);
+ if (i > 0) {
+ dim.height += getVgap();
+ }
+ dim.height += d.height;
+ }
+ }
+ if (isHeightUniform)
+ dim.height = (maxHeight + getVgap()) * nmembers - getVgap();
+ Insets insets = target.getInsets();
+ dim.width += insets.left + insets.right + getHgap() * 2;
+ dim.height += insets.top + insets.bottom + getVgap() * 2;
+ return dim;
+ }
+ }
+
+ /**
+ * Returns the minimum dimensions needed to layout the components contained in
+ * the specified target container.
+ *
+ * @param target the component which needs to be laid out
+ * @return the minimum dimensions to lay out the subcomponents of the specified
+ * container.
+ * @see #preferredLayoutSize
+ * @see java.awt.Container
+ * @see java.awt.Container#doLayout
+ */
+ public Dimension minimumLayoutSize(Container target) {
+ // preferred size is also the minimum size
+ if (isHorizontal) {
+ return preferredLayoutSizeHorizontal(target);
+ } else {
+ return preferredLayoutSizeVertical(target);
+ }
+ }
+
+ /**
+ * Lays out the container. This method lets each component take its preferred
+ * size by reshaping the components in the target container in order to satisfy
+ * the constraints of this <code>BetterFlowLayout</code> object.
+ *
+ * @param target the specified component being laid out.
+ * @see Container
+ * @see java.awt.Container#doLayout
+ */
+ public void layoutContainer(Container target) {
+ if (isHorizontal) {
+ layoutContainerHorizontal(target);
+ } else {
+ layoutContainerVertical(target);
+ }
+ }
+
+ /**
+ * Lays out the container. This method lets each component take its preferred
+ * size by reshaping the components in the target container in order to satisfy
+ * the constraints of this <code>BetterFlowLayout</code> object.
+ *
+ * @param target the specified component being laid out.
+ * @see Container
+ * @see java.awt.Container#doLayout
+ */
+ protected void layoutContainerHorizontal(Container target) {
+ synchronized (target.getTreeLock()) {
+ Insets insets = target.getInsets();
+ int maxwidth = target.getSize().width - (insets.left + insets.right + getHgap() * 2);
+ int nmembers = target.getComponentCount();
+ int x = 0, y = insets.top + getVgap();
+ int rowh = 0, start = 0;
+
+ boolean ltr = true; // target.getComponentOrientation().isLeftToRight();
+ Dimension uniform = getUniformDimension(target);
+
+ for (int i = 0; i < nmembers; i++) {
+ Component m = target.getComponent(i);
+ if (m.isVisible()) {
+ Dimension d = m.getPreferredSize();
+ if (isWidthUniform)
+ d.width = uniform.width;
+ if (isHeightUniform)
+ d.height = uniform.height;
+ m.setSize(d.width, d.height);
+
+ if ((x == 0) || ((x + d.width) <= maxwidth)) {
+ if (x > 0) {
+ x += getHgap();
+ }
+ x += d.width;
+ rowh = Math.max(rowh, d.height);
+ } else {
+ moveComponentsHorizontal(target, insets.left + getHgap(), y, maxwidth - x, rowh, start, i, ltr);
+ x = d.width;
+ y += getVgap() + rowh;
+ rowh = d.height;
+ start = i;
+ }
+ }
+ }
+ moveComponentsHorizontal(target, insets.left + getHgap(), y, maxwidth - x, rowh, start, nmembers, ltr);
+ }
+ }
+
+ /**
+ * Centers the elements in the specified row, if there is any slack.
+ *
+ * @param target the component which needs to be moved
+ * @param x the x coordinate
+ * @param y the y coordinate
+ * @param width the width dimensions
+ * @param height the height dimensions
+ * @param rowStart the beginning of the row
+ * @param rowEnd the the ending of the row
+ */
+ private void moveComponentsHorizontal(Container target, int x, int y, int width, int height, int rowStart,
+ int rowEnd, boolean ltr) {
+ synchronized (target.getTreeLock()) {
+ switch (getAlignment()) {
+ case LEFT:
+ x += ltr ? 0 : width;
+ break;
+ case CENTER:
+ x += width / 2;
+ break;
+ case RIGHT:
+ x += ltr ? width : 0;
+ break;
//1.2 case LEADING:
//1.2 break;
//1.2 case TRAILING:
//1.2 x += width;
//1.2 break;
+ }
+ for (int i = rowStart; i < rowEnd; i++) {
+ Component m = target.getComponent(i);
+ if (m.isVisible()) {
+ if (ltr) {
+ m.setLocation(x, y + (height - m.getBounds().height) / 2);
+ } else {
+ m.setLocation(target.getBounds().width - x - m.getBounds().width,
+ y + (height - m.getBounds().height) / 2);
+ }
+ x += m.getBounds().width + getHgap();
+ }
+ }
+ }
}
- for (int i = rowStart ; i < rowEnd ; i++) {
- Component m = target.getComponent(i);
- if (m.isVisible()) {
- if (ltr) {
- m.setLocation(x, y + (height - m.getBounds().height) / 2);
- } else {
- m.setLocation(target.getBounds().width - x - m.getBounds().width, y + (height - m.getBounds().height) / 2);
- }
- x += m.getBounds().width + getHgap();
- }
+
+ /**
+ * Lays out the container. This method lets each component take its preferred
+ * size by reshaping the components in the target container in order to satisfy
+ * the constraints of this <code>BetterFlowLayout</code> object.
+ *
+ * @param target the specified component being laid out.
+ * @see Container
+ * @see java.awt.Container#doLayout
+ */
+ protected void layoutContainerVertical(Container target) {
+ synchronized (target.getTreeLock()) {
+
+ Insets insets = target.getInsets();
+ int maxheight = target.getBounds().height - (insets.top + insets.bottom + getVgap() * 2);
+ int nmembers = target.getComponentCount();
+ int y = 0, x = insets.left + getHgap();
+ int colw = 0, start = 0;
+
+ Dimension uniform = getUniformDimension(target);
+ for (int i = 0; i < nmembers; i++) {
+ Component m = target.getComponent(i);
+ if (m.isVisible()) {
+ Dimension d = m.getPreferredSize();
+ if (isWidthUniform)
+ d.width = uniform.width;
+ if (isHeightUniform)
+ d.height = uniform.height;
+ m.setSize(d.width, d.height);
+
+ if ((y == 0) || ((y + d.height) <= maxheight)) {
+ if (y > 0) {
+ y += getVgap();
+ }
+ y += d.height;
+ colw = Math.max(colw, d.width);
+ } else {
+ moveComponentsVertical(target, x, insets.top + getVgap(), colw, maxheight - y, start, i);
+ y = d.height;
+ x += getHgap() + colw;
+ colw = d.width;
+ start = i;
+ }
+ }
+ }
+ moveComponentsVertical(target, x, insets.top + getVgap(), colw, maxheight - y, start, nmembers);
+ }
}
- }
- }
-
- /**
- * Lays out the container. This method lets each component take
- * its preferred size by reshaping the components in the
- * target container in order to satisfy the constraints of
- * this <code>BetterFlowLayout</code> object.
- * @param target the specified component being laid out.
- * @see Container
- * @see java.awt.Container#doLayout
- */
- protected void layoutContainerVertical(Container target) {
- synchronized (target.getTreeLock()) {
-
- Insets insets = target.getInsets();
- int maxheight = target.getBounds().height - (insets.top + insets.bottom + getVgap()*2);
- int nmembers = target.getComponentCount();
- int y = 0, x = insets.left + getHgap();
- int colw = 0, start = 0;
-
- Dimension uniform = getUniformDimension( target );
- for (int i = 0 ; i < nmembers ; i++) {
- Component m = target.getComponent(i);
- if (m.isVisible()) {
- Dimension d = m.getPreferredSize();
- if ( isWidthUniform )
- d.width = uniform.width;
- if ( isHeightUniform )
- d.height = uniform.height;
- m.setSize(d.width, d.height);
-
- if ((y == 0) || ((y + d.height) <= maxheight)) {
- if (y > 0) {
- y += getVgap();
- }
- y += d.height;
- colw = Math.max(colw, d.width);
- } else {
- moveComponentsVertical(target, x, insets.top + getVgap(), colw, maxheight - y, start, i );
- y = d.height;
- x += getHgap() + colw;
- colw = d.width;
- start = i;
- }
- }
- }
- moveComponentsVertical(target, x, insets.top + getVgap(), colw, maxheight - y, start, nmembers );
- }
- }
-
- /**
- * Centers the elements in the specified row, if there is any slack.
- * @param target the component which needs to be moved
- * @param x the x coordinate
- * @param y the y coordinate
- * @param width the width dimensions
- * @param height the height dimensions
- * @param colStart the beginning of the column
- * @param colEnd the the ending of the column
- */
- private void moveComponentsVertical(Container target, int x, int y, int width, int height,
- int colStart, int colEnd) {
- synchronized (target.getTreeLock()) {
- switch (getAlignment()) {
- case TOP:
- y += 0;
- break;
- case CENTER_VERTICAL:
- y += ( height / 2 ); // - preferredLayoutSize( target ).height ) / 2 );
- break;
- case BOTTOM:
- y += height;
- break;
- }
- for (int i = colStart ; i < colEnd ; i++) {
- Component m = target.getComponent(i);
- if (m.isVisible()) {
- m.setLocation(x + (width - m.getBounds().width) / 2, y );
+
+ /**
+ * Centers the elements in the specified row, if there is any slack.
+ *
+ * @param target the component which needs to be moved
+ * @param x the x coordinate
+ * @param y the y coordinate
+ * @param width the width dimensions
+ * @param height the height dimensions
+ * @param colStart the beginning of the column
+ * @param colEnd the the ending of the column
+ */
+ private void moveComponentsVertical(Container target, int x, int y, int width, int height, int colStart,
+ int colEnd) {
+ synchronized (target.getTreeLock()) {
+ switch (getAlignment()) {
+ case TOP:
+ y += 0;
+ break;
+ case CENTER_VERTICAL:
+ y += (height / 2); // - preferredLayoutSize( target ).height ) / 2 );
+ break;
+ case BOTTOM:
+ y += height;
+ break;
+ }
+ for (int i = colStart; i < colEnd; i++) {
+ Component m = target.getComponent(i);
+ if (m.isVisible()) {
+ m.setLocation(x + (width - m.getBounds().width) / 2, y);
// m.setLocation(x, y );
// m.setSize( width, m.getBounds().height ); //!
- y += m.getBounds().height + getVgap();
- }
- }
- }
- }
-
- /**
- * Returns a dimension representing the maximum preferred
- * height and width of all the components in the container.
- * @param target the container to scan.
- * @return a dimension containing the maximum values.
- */
- protected Dimension getUniformDimension(Container target) {
- Component m = null;
- Dimension preferred = null;
- int maxWidth = 0, maxHeight = 0;
- int nmembers = target.getComponentCount();
- for ( int i = 0; i < nmembers; i++ ) {
- m = target.getComponent( i );
- if ( m.isVisible() ) {
- preferred = m.getPreferredSize();
- maxWidth = Math.max( maxWidth, preferred.width );
- maxHeight = Math.max( maxHeight, preferred.height );
- }
- }
- return new Dimension( maxWidth, maxHeight );
- }
-
- /**
- * Returns a string representation of this <code>BetterFlowLayout</code>
- * object and its values.
- * @return a string representation of this layout.
- */
- public String toString() {
- String str = "";
- switch (getAlignment()) {
- case TOP: str = ",align=top"; break;
- case CENTER_VERTICAL: str = ",align=vertical"; break;
- case BOTTOM: str = ",align=bottom"; break;
- default: return super.toString();
+ y += m.getBounds().height + getVgap();
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns a dimension representing the maximum preferred height and width of
+ * all the components in the container.
+ *
+ * @param target the container to scan.
+ * @return a dimension containing the maximum values.
+ */
+ protected Dimension getUniformDimension(Container target) {
+ Component m = null;
+ Dimension preferred = null;
+ int maxWidth = 0, maxHeight = 0;
+ int nmembers = target.getComponentCount();
+ for (int i = 0; i < nmembers; i++) {
+ m = target.getComponent(i);
+ if (m.isVisible()) {
+ preferred = m.getPreferredSize();
+ maxWidth = Math.max(maxWidth, preferred.width);
+ maxHeight = Math.max(maxHeight, preferred.height);
+ }
+ }
+ return new Dimension(maxWidth, maxHeight);
}
- return getClass().getName() + "[hgap=" + getHgap() + ",vgap=" + getVgap() + str + "]";
- }
+ /**
+ * Returns a string representation of this <code>BetterFlowLayout</code> object
+ * and its values.
+ *
+ * @return a string representation of this layout.
+ */
+ public String toString() {
+ String str = "";
+ switch (getAlignment()) {
+ case TOP:
+ str = ",align=top";
+ break;
+ case CENTER_VERTICAL:
+ str = ",align=vertical";
+ break;
+ case BOTTOM:
+ str = ",align=bottom";
+ break;
+ default:
+ return super.toString();
+ }
+ return getClass().getName() + "[hgap=" + getHgap() + ",vgap=" + getVgap() + str + "]";
+ }
}
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/BetterRootLayout.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/BetterRootLayout.java
index 6e23ca1..238dd14 100644
--- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/BetterRootLayout.java
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/BetterRootLayout.java
@@ -28,247 +28,209 @@ import java.awt.Rectangle;
import javax.swing.JPanel;
import javax.swing.JRootPane;
-/**
-* A custom layout for a JRootPane that handles the the layout of a
-* JRootPane's layeredPane, glassPane, and menuBar, and in addition
-* handles four decorative components arranged in a border layout.
-* Add the decorative components to the JRootPane using the directional
-* constants; CENTER is reserved for the content pane and menu bar.
-*
-* @author michael@mpowers.net
-* @version $Revision: 904 $
-*/
-public class BetterRootLayout extends BorderLayout
-{
+/**
+ * A custom layout for a JRootPane that handles the the layout of a JRootPane's
+ * layeredPane, glassPane, and menuBar, and in addition handles four decorative
+ * components arranged in a border layout. Add the decorative components to the
+ * JRootPane using the directional constants; CENTER is reserved for the content
+ * pane and menu bar.
+ *
+ * @author michael@mpowers.net
+ * @version $Revision: 904 $
+ */
+public class BetterRootLayout extends BorderLayout {
/**
* Returns the amount of space the layout would like to have.
*
* @param the Container for which this layout manager is being used
* @return a Dimension object containing the layout's preferred size
* @throws ClassCastException if parent is not a JRootPane
- */
- public Dimension preferredLayoutSize(Container parent)
- {
+ */
+ public Dimension preferredLayoutSize(Container parent) {
JRootPane rootPane = (JRootPane) parent;
-
+
JPanel proxyPanel = new JPanel();
- proxyPanel.setLayout( new BorderLayout() );
-
+ proxyPanel.setLayout(new BorderLayout());
+
JPanel contentProxy = null;
- if(rootPane.getContentPane() != null) {
+ if (rootPane.getContentPane() != null) {
contentProxy = new JPanel();
- contentProxy.setMinimumSize(
- rootPane.getContentPane().getMinimumSize() );
- contentProxy.setMaximumSize(
- rootPane.getContentPane().getMaximumSize() );
- contentProxy.setPreferredSize(
- rootPane.getContentPane().getPreferredSize() );
- proxyPanel.add( contentProxy, CENTER );
+ contentProxy.setMinimumSize(rootPane.getContentPane().getMinimumSize());
+ contentProxy.setMaximumSize(rootPane.getContentPane().getMaximumSize());
+ contentProxy.setPreferredSize(rootPane.getContentPane().getPreferredSize());
+ proxyPanel.add(contentProxy, CENTER);
}
JPanel menuProxy = null;
- if(rootPane.getJMenuBar() != null) {
+ if (rootPane.getJMenuBar() != null) {
menuProxy = new JPanel();
- menuProxy.setMinimumSize(
- rootPane.getJMenuBar().getMinimumSize() );
- menuProxy.setMaximumSize(
- rootPane.getJMenuBar().getMaximumSize() );
- menuProxy.setPreferredSize(
- rootPane.getJMenuBar().getPreferredSize() );
- proxyPanel.add( menuProxy, NORTH );
+ menuProxy.setMinimumSize(rootPane.getJMenuBar().getMinimumSize());
+ menuProxy.setMaximumSize(rootPane.getJMenuBar().getMaximumSize());
+ menuProxy.setPreferredSize(rootPane.getJMenuBar().getPreferredSize());
+ proxyPanel.add(menuProxy, NORTH);
}
-
- this.addLayoutComponent( proxyPanel, CENTER );
-
- Dimension result = super.preferredLayoutSize( parent );
- this.removeLayoutComponent( proxyPanel );
-
+ this.addLayoutComponent(proxyPanel, CENTER);
+
+ Dimension result = super.preferredLayoutSize(parent);
+
+ this.removeLayoutComponent(proxyPanel);
+
proxyPanel.removeAll();
-
+
return result;
}
-
+
/**
* Returns the minimum amount of space the layout needs.
*
* @param the Container for which this layout manager is being used
* @return a Dimension object containing the layout's minimum size
* @throws ClassCastException if parent is not a JRootPane
- */
- public Dimension minimumLayoutSize(Container parent)
- {
+ */
+ public Dimension minimumLayoutSize(Container parent) {
JRootPane rootPane = (JRootPane) parent;
-
+
JPanel proxyPanel = new JPanel();
- proxyPanel.setLayout( new BorderLayout() );
-
+ proxyPanel.setLayout(new BorderLayout());
+
JPanel contentProxy = null;
- if(rootPane.getContentPane() != null) {
+ if (rootPane.getContentPane() != null) {
contentProxy = new JPanel();
- contentProxy.setMinimumSize(
- rootPane.getContentPane().getMinimumSize() );
- contentProxy.setMaximumSize(
- rootPane.getContentPane().getMaximumSize() );
- contentProxy.setPreferredSize(
- rootPane.getContentPane().getPreferredSize() );
- proxyPanel.add( contentProxy, CENTER );
+ contentProxy.setMinimumSize(rootPane.getContentPane().getMinimumSize());
+ contentProxy.setMaximumSize(rootPane.getContentPane().getMaximumSize());
+ contentProxy.setPreferredSize(rootPane.getContentPane().getPreferredSize());
+ proxyPanel.add(contentProxy, CENTER);
}
JPanel menuProxy = null;
- if(rootPane.getJMenuBar() != null) {
+ if (rootPane.getJMenuBar() != null) {
menuProxy = new JPanel();
- menuProxy.setMinimumSize(
- rootPane.getJMenuBar().getMinimumSize() );
- menuProxy.setMaximumSize(
- rootPane.getJMenuBar().getMaximumSize() );
- menuProxy.setPreferredSize(
- rootPane.getJMenuBar().getPreferredSize() );
- proxyPanel.add( menuProxy, NORTH );
+ menuProxy.setMinimumSize(rootPane.getJMenuBar().getMinimumSize());
+ menuProxy.setMaximumSize(rootPane.getJMenuBar().getMaximumSize());
+ menuProxy.setPreferredSize(rootPane.getJMenuBar().getPreferredSize());
+ proxyPanel.add(menuProxy, NORTH);
}
-
- this.addLayoutComponent( proxyPanel, CENTER );
-
- Dimension result = super.minimumLayoutSize( parent );
- this.removeLayoutComponent( proxyPanel );
-
+ this.addLayoutComponent(proxyPanel, CENTER);
+
+ Dimension result = super.minimumLayoutSize(parent);
+
+ this.removeLayoutComponent(proxyPanel);
+
proxyPanel.removeAll();
-
+
return result;
}
-
+
/**
* Returns the maximum amount of space the layout can use.
*
* @param the Container for which this layout manager is being used
* @return a Dimension object containing the layout's maximum size
* @throws ClassCastException if parent is not a JRootPane
- */
- public Dimension maximumLayoutSize(Container target)
- {
+ */
+ public Dimension maximumLayoutSize(Container target) {
JRootPane rootPane = (JRootPane) target;
-
+
JPanel proxyPanel = new JPanel();
- proxyPanel.setLayout( new BorderLayout() );
-
+ proxyPanel.setLayout(new BorderLayout());
+
JPanel contentProxy = null;
- if(rootPane.getContentPane() != null) {
+ if (rootPane.getContentPane() != null) {
contentProxy = new JPanel();
- contentProxy.setMinimumSize(
- rootPane.getContentPane().getMinimumSize() );
- contentProxy.setMaximumSize(
- rootPane.getContentPane().getMaximumSize() );
- contentProxy.setPreferredSize(
- rootPane.getContentPane().getPreferredSize() );
- proxyPanel.add( contentProxy, CENTER );
+ contentProxy.setMinimumSize(rootPane.getContentPane().getMinimumSize());
+ contentProxy.setMaximumSize(rootPane.getContentPane().getMaximumSize());
+ contentProxy.setPreferredSize(rootPane.getContentPane().getPreferredSize());
+ proxyPanel.add(contentProxy, CENTER);
}
JPanel menuProxy = null;
- if(rootPane.getJMenuBar() != null) {
+ if (rootPane.getJMenuBar() != null) {
menuProxy = new JPanel();
- menuProxy.setMinimumSize(
- rootPane.getJMenuBar().getMinimumSize() );
- menuProxy.setMaximumSize(
- rootPane.getJMenuBar().getMaximumSize() );
- menuProxy.setPreferredSize(
- rootPane.getJMenuBar().getPreferredSize() );
- proxyPanel.add( menuProxy, NORTH );
+ menuProxy.setMinimumSize(rootPane.getJMenuBar().getMinimumSize());
+ menuProxy.setMaximumSize(rootPane.getJMenuBar().getMaximumSize());
+ menuProxy.setPreferredSize(rootPane.getJMenuBar().getPreferredSize());
+ proxyPanel.add(menuProxy, NORTH);
}
-
- this.addLayoutComponent( proxyPanel, CENTER );
-
- Dimension result = super.maximumLayoutSize( target );
- this.removeLayoutComponent( proxyPanel );
-
+ this.addLayoutComponent(proxyPanel, CENTER);
+
+ Dimension result = super.maximumLayoutSize(target);
+
+ this.removeLayoutComponent(proxyPanel);
+
proxyPanel.removeAll();
-
+
return result;
}
-
+
/**
* Instructs the layout manager to perform the layout for the specified
* container.
*
* @param the Container for which this layout manager is being used
* @throws ClassCastException if parent is not a JRootPane
- */
- public void layoutContainer(Container parent)
- {
+ */
+ public void layoutContainer(Container parent) {
JRootPane rootPane = (JRootPane) parent;
-
+
Rectangle b = parent.getBounds();
Insets i = rootPane.getInsets();
int w = b.width - i.right - i.left;
int h = b.height - i.top - i.bottom;
-
+
// layout panes
- if(rootPane.getLayeredPane() != null) {
+ if (rootPane.getLayeredPane() != null) {
rootPane.getLayeredPane().setBounds(i.left, i.top, w, h);
}
- if(rootPane.getGlassPane() != null) {
+ if (rootPane.getGlassPane() != null) {
rootPane.getGlassPane().setBounds(i.left, i.top, w, h);
}
-
+
// handle proxy panel
-
+
JPanel proxyPanel = new JPanel();
- proxyPanel.setLayout( new BorderLayout() );
-
- this.addLayoutComponent( proxyPanel, CENTER );
-
- super.layoutContainer( parent );
+ proxyPanel.setLayout(new BorderLayout());
+
+ this.addLayoutComponent(proxyPanel, CENTER);
+
+ super.layoutContainer(parent);
// use proxy sizes to set sizes of layeredPane's children
Rectangle proxyRect = proxyPanel.getBounds();
- if(rootPane.getJMenuBar() != null) {
+ if (rootPane.getJMenuBar() != null) {
Rectangle menuRect = proxyPanel.getBounds();
menuRect.height = rootPane.getJMenuBar().getPreferredSize().height;
- rootPane.getJMenuBar().setBounds( menuRect );
+ rootPane.getJMenuBar().setBounds(menuRect);
proxyRect.y += menuRect.height;
proxyRect.height -= menuRect.height;
}
- if(rootPane.getContentPane() != null) {
- rootPane.getContentPane().setBounds( proxyRect );
+ if (rootPane.getContentPane() != null) {
+ rootPane.getContentPane().setBounds(proxyRect);
}
- this.removeLayoutComponent( proxyPanel );
-
+ this.removeLayoutComponent(proxyPanel);
+
proxyPanel.removeAll();
}
-
+
/**
- * Passes NORTH, SOUTH, EAST, WEST and CENTER to super implementation,
- * and ignores all others.
- */
- public void addLayoutComponent(Component comp, Object constraints)
- {
- if ( NORTH.equals( constraints ) )
- {
- super.addLayoutComponent( comp, constraints );
- }
- else
- if ( SOUTH.equals( constraints ) )
- {
- super.addLayoutComponent( comp, constraints );
- }
- else
- if ( EAST.equals( constraints ) )
- {
- super.addLayoutComponent( comp, constraints );
+ * Passes NORTH, SOUTH, EAST, WEST and CENTER to super implementation, and
+ * ignores all others.
+ */
+ public void addLayoutComponent(Component comp, Object constraints) {
+ if (NORTH.equals(constraints)) {
+ super.addLayoutComponent(comp, constraints);
+ } else if (SOUTH.equals(constraints)) {
+ super.addLayoutComponent(comp, constraints);
+ } else if (EAST.equals(constraints)) {
+ super.addLayoutComponent(comp, constraints);
+ } else if (WEST.equals(constraints)) {
+ super.addLayoutComponent(comp, constraints);
+ } else if (CENTER.equals(constraints)) {
+ super.addLayoutComponent(comp, constraints);
}
- else
- if ( WEST.equals( constraints ) )
- {
- super.addLayoutComponent( comp, constraints );
- }
- else
- if ( CENTER.equals( constraints ) )
- {
- super.addLayoutComponent( comp, constraints );
- }
-
+
// otherwise, ignore
}
}
-
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/BetterTableUI.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/BetterTableUI.java
index deb0eb6..cb1d48a 100644
--- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/BetterTableUI.java
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/BetterTableUI.java
@@ -24,100 +24,84 @@ import javax.swing.event.MouseInputListener;
import javax.swing.plaf.basic.BasicTableUI;
/**
-* BetterTableUI allows a JTable to be disabled by
-* listening for MouseEvents and then forwarding them
-* to the usual MouseInputHandler only if the table
-* is enabled. <BR><BR>
-*
-* This class also works around a bug where an editable
-* table's selection is changed when clicking in an edit
-* cell while the control key is down. This typically
-* happened while users were copying/pasting data from
-* cell to cell. <BR><BR>
-*
-* To use, call <code>JTable.setUI()</code> on any
-* JTable with a BetterTableUI as the parameter.
-*
-* @author michael@mpowers.net
-* @version $Revision: 904 $
-*/
-public class BetterTableUI extends BasicTableUI implements MouseInputListener
-{
-/**
-* The listener to get all mouse events when the table is enabled.
-*/
- protected MouseInputListener delegateHandler;
-
-/**
-* Overridden to set self as mouse listener and create delegate.
-*/
- protected MouseInputListener createMouseInputListener()
- {
- // normal handler is a protected inner class of parent
- delegateHandler = new MouseInputHandler();
-
- return this;
- }
-
- // interface MouseInputListener
-
- public void mouseClicked(MouseEvent event)
- {
- if ( (table!=null) && (table.isEnabled()) )
- {
- delegateHandler.mouseClicked(event);
- }
- }
-
- public void mouseDragged(MouseEvent event)
- {
- if ( (table!=null) && (table.isEnabled()) )
- {
- delegateHandler.mouseDragged(event);
- }
- }
-
- public void mouseEntered(MouseEvent event)
- {
- if ( (table!=null) && (table.isEnabled()) )
- {
- delegateHandler.mouseEntered(event);
- }
- }
-
- public void mouseExited(MouseEvent event)
- {
- if ( (table!=null) && (table.isEnabled()) )
- {
- delegateHandler.mouseExited(event);
- }
- }
-
- public void mouseMoved(MouseEvent event)
- {
- if ( (table!=null) && (table.isEnabled()) )
- {
- delegateHandler.mouseMoved(event);
- }
- }
-
- public void mousePressed(MouseEvent event)
- {
- if ( (table!=null) && (table.isEnabled()) )
- {
- // workaround bug - control key removes an existing selection
- if ( table.isEditing() && event.isControlDown() ) return;
-
- delegateHandler.mousePressed(event);
- }
- }
-
- public void mouseReleased(MouseEvent event)
- {
- if ( (table!=null) && (table.isEnabled()) )
- {
- delegateHandler.mouseReleased(event);
- }
- }
+ * BetterTableUI allows a JTable to be disabled by listening for MouseEvents and
+ * then forwarding them to the usual MouseInputHandler only if the table is
+ * enabled. <BR>
+ * <BR>
+ *
+ * This class also works around a bug where an editable table's selection is
+ * changed when clicking in an edit cell while the control key is down. This
+ * typically happened while users were copying/pasting data from cell to cell.
+ * <BR>
+ * <BR>
+ *
+ * To use, call <code>JTable.setUI()</code> on any JTable with a BetterTableUI
+ * as the parameter.
+ *
+ * @author michael@mpowers.net
+ * @version $Revision: 904 $
+ */
+public class BetterTableUI extends BasicTableUI implements MouseInputListener {
+ /**
+ * The listener to get all mouse events when the table is enabled.
+ */
+ protected MouseInputListener delegateHandler;
+
+ /**
+ * Overridden to set self as mouse listener and create delegate.
+ */
+ protected MouseInputListener createMouseInputListener() {
+ // normal handler is a protected inner class of parent
+ delegateHandler = new MouseInputHandler();
+
+ return this;
+ }
+
+ // interface MouseInputListener
+
+ public void mouseClicked(MouseEvent event) {
+ if ((table != null) && (table.isEnabled())) {
+ delegateHandler.mouseClicked(event);
+ }
+ }
+
+ public void mouseDragged(MouseEvent event) {
+ if ((table != null) && (table.isEnabled())) {
+ delegateHandler.mouseDragged(event);
+ }
+ }
+
+ public void mouseEntered(MouseEvent event) {
+ if ((table != null) && (table.isEnabled())) {
+ delegateHandler.mouseEntered(event);
+ }
+ }
+
+ public void mouseExited(MouseEvent event) {
+ if ((table != null) && (table.isEnabled())) {
+ delegateHandler.mouseExited(event);
+ }
+ }
+
+ public void mouseMoved(MouseEvent event) {
+ if ((table != null) && (table.isEnabled())) {
+ delegateHandler.mouseMoved(event);
+ }
+ }
+
+ public void mousePressed(MouseEvent event) {
+ if ((table != null) && (table.isEnabled())) {
+ // workaround bug - control key removes an existing selection
+ if (table.isEditing() && event.isControlDown())
+ return;
+
+ delegateHandler.mousePressed(event);
+ }
+ }
+
+ public void mouseReleased(MouseEvent event) {
+ if ((table != null) && (table.isEnabled())) {
+ delegateHandler.mouseReleased(event);
+ }
+ }
}
-
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/ButtonPanel.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/ButtonPanel.java
index 769e866..7d662ae 100644
--- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/ButtonPanel.java
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/ButtonPanel.java
@@ -46,565 +46,527 @@ import javax.swing.JPanel;
import javax.swing.UIManager;
/**
-* ButtonPanel handles display and event broadcasting of standard buttons like
-* OK/Cancel/Save/etc. The constructor takes a list or array of strings, each
-* representing a button to appear on the panel from left to right.
-* Any button click will send an action event to all listeners with the action
-* command containing the corresponding string. Note action events are simply
-* forwarded from the buttons themselves, so the source of the event will be
-* the button, not the button panel. The button panel is the source of the
-* STATE_CHANGED events that notify about changes to the panel itself.<BR><BR>
-*
-* @author michael@mpowers.net
-* @author $Author: cgruber $
-* @version $Revision: 904 $
-*/
-public class ButtonPanel extends JPanel implements ActionListener, MouseMotionListener
-{
- // TODO: Button text should be read from resources.
-/**
-* Specifies a "OK" button.
-* This is also the action command sent by the OK button.
-*/
- public static final String OK = "OK";
-/**
-* Specifies a "Save" button.
-* This is also the action command sent by the Save button.
-*/
- public static final String SAVE = "Save";
-/**
-* Specifies a "Refresh" button.
-* This is also the action command sent by the Refresh button.
-*/
- public static final String REFRESH = "Refresh";
-/**
-* Specifies a "Clear All" button.
-* This is also the action command sent by the Clear All button.
-*/
- public static final String CLEAR_ALL = "Clear All";
-/**
-* Specifies a "Refresh" button.
-* This is also the action command sent by the Cancel button.
-*/
- public static final String CANCEL = "Cancel";
-/**
-* Specifies a "Yes" button.
-* This is also the action command sent by the Yes button.
-*/
- public static final String YES = "Yes";
-/**
-* Specifies a "No" button.
-* This is also the action command sent by the No button.
-*/
- public static final String NO = "No";
-/**
-* Specifies an "Add" button.
-* This is also the action command sent by the Add button.
-*/
- public static final String ADD = "Add";
-/**
-* Specifies a "Remove" button.
-* This is also the action command sent by the Remove button.
-*/
- public static final String REMOVE = "Remove";
-/**
-* This is the action command to all listeners when the button state is changed.
-*/
- public static final String STATE_CHANGED = "STATE_CHANGED";
-
-/**
-* This is the container to which buttons are added.
-*/
- protected Container buttonContainer = null; // useful for subclasses
-/**
-* This is the list of all buttons on the panel.
-*/
- protected Vector buttonList = null;
-/**
-* The insets for this panel, so they can be modified.
-*/
- protected Insets insets = new Insets( 5, 5, 5, 5 );
-
-/**
-* This is the layout manager - which must be a FlowLayout or subclass.
-*/
- protected FlowLayout buttonPanelLayout = null;
-
- // for action multicasting
- protected ActionListener actionListener = null;
-
-
-/**
-* Constructs a ButtonPanel. Three buttons are created
-* so the panel is filled when used in a GUI-builder environment.
-*/
- public ButtonPanel()
- {
- buttonList = new Vector();
- initLayout();
-
- // default labels for bean layout
- setLabels( new String[] { "One", "Two", "Three" } );
- }
-
-/**
-* This method is responsible for the initial layout of the panel.
-* Subclasses can implement different layouts, but this method
-* is responsible for initializing buttonContainer and buttonPanelLayout
-* and setting the container to use the layout.
-*/
- protected void initLayout()
- {
- this.setInsets( super.getInsets() );
- buttonContainer = this;
- buttonPanelLayout = new BetterFlowLayout( BetterFlowLayout.RIGHT );
- buttonContainer.setLayout(buttonPanelLayout);
- ((BetterFlowLayout)buttonPanelLayout).setWidthUniform( true );
-
- // setBackground( Color.blue ); // useful for debugging
- }
-
-/**
-* Constructs a ButtonPanel using specified buttons.
-* @param buttonList An array containing the strings to be used in labeling the buttons.
-*/
- public ButtonPanel( String[] buttonList )
- {
- this();
- setLabels( buttonList );
- }
-
-/**
-* Constructs a ButtonPane using specified actions. For each action, a button
-* is created, that when pressed the corresponding action is activated. The
-* "name" of the action is used as the title of the button.
-* @param actionList An array of actions to be used to create buttons with.
-*/
- public ButtonPanel( Action[] actionList )
- {
- this();
- setLabels( actionList );
- }
-
-/**
-* Creates the buttons to appear on the panel. Any existing buttons
-* are replaced. The labels are used as names and action commands
-* in addition to labels.
-* @param labels An array of strings to be used in labeling the buttons.
-* If null, all buttons will be removed.
-*/
- public void setLabels( String[] labels )
- {
- if ( labels == null )
- {
- labels = new String[] {};
- }
-
- buttonContainer.removeAll();
- this.buttonList = new Vector( labels.length );
-
- String item = null;
- Component button;
- for ( int i = 0; i < labels.length; i++ )
- {
- item = labels[i];
- if ( item != null )
- {
- button = createComponentWithLabel( item.toString() );
- this.buttonList.addElement( item );
- addComponentToPanel( button );
- button.setEnabled( this.isEnabled() );
-/*
- if ( i == 0 )
- {
- JRootPane root = SwingUtilities.getRootPane( button );
- if ( root != null )
- root.setDefaultButton( button );
- }
-*/
- }
- else
- {
- throw new IllegalArgumentException( "ButtonPanel.setButtons: nulls are not allowed." );
- }
- }
-
- this.revalidate();
- this.repaint();
- broadcastEvent( new ActionEvent( this, ActionEvent.ACTION_PERFORMED, STATE_CHANGED ) );
- }
-
-/**
-*
-*/
- public void setLabels( Action[] actions )
- {
- if ( actions == null )
- {
- actions = new Action[] {};
- }
-
- buttonContainer.removeAll();
- this.buttonList = new Vector( actions.length );
-
- Action action = null;
- Component button;
- for ( int i = 0; i < actions.length; i++ )
- {
- action = actions[i];
- if ( action != null )
- {
- String name = ( String )action.getValue( Action.NAME );
- button = createComponentWithLabel( name );
- this.buttonList.addElement( name );
- addComponentToPanel( button );
- button.setEnabled( this.isEnabled() ? action.isEnabled() : false );
-
- // Add the action to the "button" if it knows about action listeners.
- try
- {
- Method addActionListenerMethod =
- button.getClass().getMethod( "addActionListener", new Class[] { ActionListener.class } );
- addActionListenerMethod.invoke( button, new Object[] { action } );
- }
- catch ( NoSuchMethodException e ) { /* Do Nothing */ }
- catch ( IllegalAccessException e ) { e.printStackTrace(); /* TODO: Do Something? */ }
- catch ( InvocationTargetException e ) { e.printStackTrace(); /* TODO: Do Something? */ }
-
- // Create a new listener for property change events and have
- // the action broadcast to that listener.
- PropertyChangeListener pcListener = new ActionChangeListener( button );
- action.addPropertyChangeListener( pcListener );
- }
- else
- {
- throw new IllegalArgumentException( "ButtonPanel.setButtons: nulls are not allowed." );
- }
- }
-
- this.revalidate();
- this.repaint();
- broadcastEvent( new ActionEvent( this, ActionEvent.ACTION_PERFORMED, STATE_CHANGED ) );
- }
-
-
-/**
-* Gets the labels of the buttons that appear on the panel, ordered from left to right.
-* @return A new list containing strings used in labeling the buttons.
-*/
- public String[] getLabels()
- {
- String[] labels = new String[ buttonList.size() ];
- int i = 0;
- for ( Enumeration it = buttonList.elements(); it.hasMoreElements(); )
- {
- labels[i++] = it.nextElement().toString();
- }
- return labels;
- }
-
-/**
-* Gets the first component having the specified name.
-* @return A component with the specified name, or null if none match.
-*/
- public Component getButton( String aLabel )
- {
- if ( aLabel == null ) return null;
-
- Component c = null;
- int count = buttonContainer.getComponentCount();
- for ( int i = 0; i < count; i++ )
- {
- c = buttonContainer.getComponent( i );
- if ( aLabel.equals( c.getName() ) )
- {
- return c;
- }
- }
- return null;
- }
-
-/**
-* Creates a new component with the specified label.
-* The label is also used for the component's name
-* and action command, if any.
-* (This implementation returns a JButton.)
-* @param aLabel The label for the component that will be created.
-* @return The newly created component.
-*/
- protected Component createComponentWithLabel( String aLabel )
- {
- String buttonLabel = aLabel; // TODO: get string from resource
- JButton newButton = new JButton(); // might allow other types in future
- newButton.setName( aLabel );
- newButton.setText( buttonLabel );
- newButton.setActionCommand( aLabel );
- newButton.addActionListener( this );
- return newButton;
- }
-
-/**
-* Adds a component to the right-most side of the layout.
-* @param aComponent The component to be added to the layout.
-*/
- protected void addComponentToPanel( Component aComponent )
- {
- buttonContainer.add( aComponent );
- }
-
-
-/**
-* Changes the alignment of the buttons in the panel. Defaults to right-justified.
-* @param alignment A valid alignment code, per BetterFlowLayout implementation.
-* @see BetterFlowLayout
-*/
- public void setAlignment( int alignment )
- {
- buttonPanelLayout.setAlignment(alignment);
- buttonContainer.doLayout();
- }
-/**
-* Gets the alignment of the buttons in the panel.
-* @return An alignment code, per FlowLayout implementation.
-* @see FlowLayout
-*/
- public int getAlignment()
- {
- return buttonPanelLayout.getAlignment();
- }
-
-/**
-* Changes the horizontal spacing between components in the panel.
-* @param newHgap the new spacing, in pixels. May not be negative.
-*/
- public void setHgap( int newHgap )
- {
- if ( newHgap < 0 ) return; // may not be negative
- buttonPanelLayout.setHgap( newHgap );
- }
-
-/**
-* Gets the current horizontal spacing between components.
-* @return the current horizontal spacing, in pixels.
-*/
- public int getHgap()
- {
- return buttonPanelLayout.getHgap();
- }
-
-/**
-* Changes the vertical spacing between components in the panel.
-* @param newVgap the new spacing, in pixels. May not be negative.
-*/
- public void setVgap( int newVgap )
- {
- if ( newVgap < 0 ) return; // may not be negative
- buttonPanelLayout.setVgap( newVgap );
- }
-
-/**
-* Gets the current vertical spacing between components.
-* @return the current vertical spacing, in pixels.
-*/
- public int getVgap()
- {
- return buttonPanelLayout.getVgap();
- }
-
-/**
-* Changes the insets for this panel.
-* @param newInsets the new insets.
-*/
- public void setInsets( Insets newInsets )
- {
- insets = newInsets;
- }
-
-/**
-* Overridden to return the user-specified insets for this panel.
-* @return the current insets for this panel.
-*/
- public Insets getInsets()
- {
- return insets;
- }
-
-/**
-* Overridden to call setEnabled on all components on panel.
-* @param isEnabled whether to enable the panel and all components on it.
-*/
- public void setEnabled( boolean isEnabled )
- {
- super.setEnabled( isEnabled );
- int count = buttonContainer.getComponentCount();
- for ( int i = 0; i < count; i++ )
- {
- buttonContainer.getComponent( i ).setEnabled( isEnabled );
- }
- }
-
- // Action Multicast methods
-
-/**
-* Adds an action listener to the list that will be
-* notified by button events and changes in button state.
-* @param l An action listener to be notified.
-*/
- public void addActionListener(ActionListener l)
- {
- actionListener = AWTEventMulticaster.add(actionListener, l);
- }
-/**
-* Removes an action listener from the list that will be
-* notified by button events and changes in button state.
-* @param l An action listener to be removed.
-*/
- public void removeActionListener(ActionListener l)
- {
- actionListener = AWTEventMulticaster.remove(actionListener, l);
- }
-/**
-* Notifies all registered action listeners of a pending Action Event.
-* @param e An action event to be broadcast.
-*/
- protected void broadcastEvent(ActionEvent e)
- {
- if (actionListener != null)
- {
- actionListener.actionPerformed(e);
- }
- }
-
- // interface ActionListener
-
-/**
-* Called by buttons on panel and by other components that
-* might be set to broadcast events to this listener.
-* @param e An action event to be received.
-*/
- public void actionPerformed(ActionEvent e)
- {
- broadcastEvent(e);
- }
-
-/**
-* A property change listener that listens specifically for property changes
-* from action objects. This is the class that ties in the action to the
-* button. This class is added to an action as a property change listener.
-* The corresponding component is referenced by this class toe easily handle
-* updates to the component caused by changes to the action.
-*/
- public class ActionChangeListener implements PropertyChangeListener
- {
- /** The UI component that is affected by the action's changes. */
- Component theComponent;
-
- /**
- * Constructs an ActionChangeListener with the given component being
- * the recipient of the action's changes.
- * @param The component to bind with the action.
- */
- public ActionChangeListener( Component aComponent )
- {
- super();
- theComponent = aComponent;
- }
-
- /**
- * Called whenever a property changes on the action object.
- * @pram e The property change event generated by the action.
- */
- public void propertyChange( PropertyChangeEvent e )
- {
- String propertyName = e.getPropertyName();
- if ( propertyName.equals( Action.NAME ) )
- {
- String name = ( String )e.getNewValue();
- if ( theComponent instanceof AbstractButton )
- {
- AbstractButton button = ( AbstractButton )theComponent;
- String oldName = button.getName();
- button.setText( name );
- button.setName( name );
- button.setActionCommand( name );
-
- // Replace the old name of the component with the new name
- // in the ButtonPanel's list of components.
- buttonList.setElementAt( name, buttonList.indexOf( oldName ) );
- }
-
- // TODO: If component is not a button (or doesn't define the getText()
- // then what should be done.
- }
- else if ( propertyName.equals( "enabled" ) )
- {
- Boolean enabled = ( Boolean )e.getNewValue();
- theComponent.setEnabled( ButtonPanel.this.isEnabled() ? enabled.booleanValue() : false );
- }
-
- // TODO: Icon?
- }
- }
-
-
- // for testing
-
- public static void main( String[] argv )
- {
- try
- {
- UIManager.setLookAndFeel( UIManager.getSystemLookAndFeelClassName() );
- }
- catch (Exception exc)
- {
-
- }
-
- JFrame dialog = new JFrame();
- BorderLayout bl = new BorderLayout( 20, 20 );
-
- ButtonPanel panel = new ButtonPanel();
+ * ButtonPanel handles display and event broadcasting of standard buttons like
+ * OK/Cancel/Save/etc. The constructor takes a list or array of strings, each
+ * representing a button to appear on the panel from left to right. Any button
+ * click will send an action event to all listeners with the action command
+ * containing the corresponding string. Note action events are simply forwarded
+ * from the buttons themselves, so the source of the event will be the button,
+ * not the button panel. The button panel is the source of the STATE_CHANGED
+ * events that notify about changes to the panel itself.<BR>
+ * <BR>
+ *
+ * @author michael@mpowers.net
+ * @author $Author: cgruber $
+ * @version $Revision: 904 $
+ */
+public class ButtonPanel extends JPanel implements ActionListener, MouseMotionListener {
+ // TODO: Button text should be read from resources.
+ /**
+ * Specifies a "OK" button. This is also the action command sent by the OK
+ * button.
+ */
+ public static final String OK = "OK";
+ /**
+ * Specifies a "Save" button. This is also the action command sent by the Save
+ * button.
+ */
+ public static final String SAVE = "Save";
+ /**
+ * Specifies a "Refresh" button. This is also the action command sent by the
+ * Refresh button.
+ */
+ public static final String REFRESH = "Refresh";
+ /**
+ * Specifies a "Clear All" button. This is also the action command sent by the
+ * Clear All button.
+ */
+ public static final String CLEAR_ALL = "Clear All";
+ /**
+ * Specifies a "Refresh" button. This is also the action command sent by the
+ * Cancel button.
+ */
+ public static final String CANCEL = "Cancel";
+ /**
+ * Specifies a "Yes" button. This is also the action command sent by the Yes
+ * button.
+ */
+ public static final String YES = "Yes";
+ /**
+ * Specifies a "No" button. This is also the action command sent by the No
+ * button.
+ */
+ public static final String NO = "No";
+ /**
+ * Specifies an "Add" button. This is also the action command sent by the Add
+ * button.
+ */
+ public static final String ADD = "Add";
+ /**
+ * Specifies a "Remove" button. This is also the action command sent by the
+ * Remove button.
+ */
+ public static final String REMOVE = "Remove";
+ /**
+ * This is the action command to all listeners when the button state is changed.
+ */
+ public static final String STATE_CHANGED = "STATE_CHANGED";
+
+ /**
+ * This is the container to which buttons are added.
+ */
+ protected Container buttonContainer = null; // useful for subclasses
+ /**
+ * This is the list of all buttons on the panel.
+ */
+ protected Vector buttonList = null;
+ /**
+ * The insets for this panel, so they can be modified.
+ */
+ protected Insets insets = new Insets(5, 5, 5, 5);
+
+ /**
+ * This is the layout manager - which must be a FlowLayout or subclass.
+ */
+ protected FlowLayout buttonPanelLayout = null;
+
+ // for action multicasting
+ protected ActionListener actionListener = null;
+
+ /**
+ * Constructs a ButtonPanel. Three buttons are created so the panel is filled
+ * when used in a GUI-builder environment.
+ */
+ public ButtonPanel() {
+ buttonList = new Vector();
+ initLayout();
+
+ // default labels for bean layout
+ setLabels(new String[] { "One", "Two", "Three" });
+ }
+
+ /**
+ * This method is responsible for the initial layout of the panel. Subclasses
+ * can implement different layouts, but this method is responsible for
+ * initializing buttonContainer and buttonPanelLayout and setting the container
+ * to use the layout.
+ */
+ protected void initLayout() {
+ this.setInsets(super.getInsets());
+ buttonContainer = this;
+ buttonPanelLayout = new BetterFlowLayout(BetterFlowLayout.RIGHT);
+ buttonContainer.setLayout(buttonPanelLayout);
+ ((BetterFlowLayout) buttonPanelLayout).setWidthUniform(true);
+
+ // setBackground( Color.blue ); // useful for debugging
+ }
+
+ /**
+ * Constructs a ButtonPanel using specified buttons.
+ *
+ * @param buttonList An array containing the strings to be used in labeling the
+ * buttons.
+ */
+ public ButtonPanel(String[] buttonList) {
+ this();
+ setLabels(buttonList);
+ }
+
+ /**
+ * Constructs a ButtonPane using specified actions. For each action, a button is
+ * created, that when pressed the corresponding action is activated. The "name"
+ * of the action is used as the title of the button.
+ *
+ * @param actionList An array of actions to be used to create buttons with.
+ */
+ public ButtonPanel(Action[] actionList) {
+ this();
+ setLabels(actionList);
+ }
+
+ /**
+ * Creates the buttons to appear on the panel. Any existing buttons are
+ * replaced. The labels are used as names and action commands in addition to
+ * labels.
+ *
+ * @param labels An array of strings to be used in labeling the buttons. If
+ * null, all buttons will be removed.
+ */
+ public void setLabels(String[] labels) {
+ if (labels == null) {
+ labels = new String[] {};
+ }
+
+ buttonContainer.removeAll();
+ this.buttonList = new Vector(labels.length);
+
+ String item = null;
+ Component button;
+ for (int i = 0; i < labels.length; i++) {
+ item = labels[i];
+ if (item != null) {
+ button = createComponentWithLabel(item.toString());
+ this.buttonList.addElement(item);
+ addComponentToPanel(button);
+ button.setEnabled(this.isEnabled());
+ /*
+ * if ( i == 0 ) { JRootPane root = SwingUtilities.getRootPane( button ); if (
+ * root != null ) root.setDefaultButton( button ); }
+ */
+ } else {
+ throw new IllegalArgumentException("ButtonPanel.setButtons: nulls are not allowed.");
+ }
+ }
+
+ this.revalidate();
+ this.repaint();
+ broadcastEvent(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, STATE_CHANGED));
+ }
+
+ /**
+ *
+ */
+ public void setLabels(Action[] actions) {
+ if (actions == null) {
+ actions = new Action[] {};
+ }
+
+ buttonContainer.removeAll();
+ this.buttonList = new Vector(actions.length);
+
+ Action action = null;
+ Component button;
+ for (int i = 0; i < actions.length; i++) {
+ action = actions[i];
+ if (action != null) {
+ String name = (String) action.getValue(Action.NAME);
+ button = createComponentWithLabel(name);
+ this.buttonList.addElement(name);
+ addComponentToPanel(button);
+ button.setEnabled(this.isEnabled() ? action.isEnabled() : false);
+
+ // Add the action to the "button" if it knows about action listeners.
+ try {
+ Method addActionListenerMethod = button.getClass().getMethod("addActionListener",
+ new Class[] { ActionListener.class });
+ addActionListenerMethod.invoke(button, new Object[] { action });
+ } catch (NoSuchMethodException e) {
+ /* Do Nothing */ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ /* TODO: Do Something? */ } catch (InvocationTargetException e) {
+ e.printStackTrace();
+ /* TODO: Do Something? */ }
+
+ // Create a new listener for property change events and have
+ // the action broadcast to that listener.
+ PropertyChangeListener pcListener = new ActionChangeListener(button);
+ action.addPropertyChangeListener(pcListener);
+ } else {
+ throw new IllegalArgumentException("ButtonPanel.setButtons: nulls are not allowed.");
+ }
+ }
+
+ this.revalidate();
+ this.repaint();
+ broadcastEvent(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, STATE_CHANGED));
+ }
+
+ /**
+ * Gets the labels of the buttons that appear on the panel, ordered from left to
+ * right.
+ *
+ * @return A new list containing strings used in labeling the buttons.
+ */
+ public String[] getLabels() {
+ String[] labels = new String[buttonList.size()];
+ int i = 0;
+ for (Enumeration it = buttonList.elements(); it.hasMoreElements();) {
+ labels[i++] = it.nextElement().toString();
+ }
+ return labels;
+ }
+
+ /**
+ * Gets the first component having the specified name.
+ *
+ * @return A component with the specified name, or null if none match.
+ */
+ public Component getButton(String aLabel) {
+ if (aLabel == null)
+ return null;
+
+ Component c = null;
+ int count = buttonContainer.getComponentCount();
+ for (int i = 0; i < count; i++) {
+ c = buttonContainer.getComponent(i);
+ if (aLabel.equals(c.getName())) {
+ return c;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Creates a new component with the specified label. The label is also used for
+ * the component's name and action command, if any. (This implementation returns
+ * a JButton.)
+ *
+ * @param aLabel The label for the component that will be created.
+ * @return The newly created component.
+ */
+ protected Component createComponentWithLabel(String aLabel) {
+ String buttonLabel = aLabel; // TODO: get string from resource
+ JButton newButton = new JButton(); // might allow other types in future
+ newButton.setName(aLabel);
+ newButton.setText(buttonLabel);
+ newButton.setActionCommand(aLabel);
+ newButton.addActionListener(this);
+ return newButton;
+ }
+
+ /**
+ * Adds a component to the right-most side of the layout.
+ *
+ * @param aComponent The component to be added to the layout.
+ */
+ protected void addComponentToPanel(Component aComponent) {
+ buttonContainer.add(aComponent);
+ }
+
+ /**
+ * Changes the alignment of the buttons in the panel. Defaults to
+ * right-justified.
+ *
+ * @param alignment A valid alignment code, per BetterFlowLayout implementation.
+ * @see BetterFlowLayout
+ */
+ public void setAlignment(int alignment) {
+ buttonPanelLayout.setAlignment(alignment);
+ buttonContainer.doLayout();
+ }
+
+ /**
+ * Gets the alignment of the buttons in the panel.
+ *
+ * @return An alignment code, per FlowLayout implementation.
+ * @see FlowLayout
+ */
+ public int getAlignment() {
+ return buttonPanelLayout.getAlignment();
+ }
+
+ /**
+ * Changes the horizontal spacing between components in the panel.
+ *
+ * @param newHgap the new spacing, in pixels. May not be negative.
+ */
+ public void setHgap(int newHgap) {
+ if (newHgap < 0)
+ return; // may not be negative
+ buttonPanelLayout.setHgap(newHgap);
+ }
+
+ /**
+ * Gets the current horizontal spacing between components.
+ *
+ * @return the current horizontal spacing, in pixels.
+ */
+ public int getHgap() {
+ return buttonPanelLayout.getHgap();
+ }
+
+ /**
+ * Changes the vertical spacing between components in the panel.
+ *
+ * @param newVgap the new spacing, in pixels. May not be negative.
+ */
+ public void setVgap(int newVgap) {
+ if (newVgap < 0)
+ return; // may not be negative
+ buttonPanelLayout.setVgap(newVgap);
+ }
+
+ /**
+ * Gets the current vertical spacing between components.
+ *
+ * @return the current vertical spacing, in pixels.
+ */
+ public int getVgap() {
+ return buttonPanelLayout.getVgap();
+ }
+
+ /**
+ * Changes the insets for this panel.
+ *
+ * @param newInsets the new insets.
+ */
+ public void setInsets(Insets newInsets) {
+ insets = newInsets;
+ }
+
+ /**
+ * Overridden to return the user-specified insets for this panel.
+ *
+ * @return the current insets for this panel.
+ */
+ public Insets getInsets() {
+ return insets;
+ }
+
+ /**
+ * Overridden to call setEnabled on all components on panel.
+ *
+ * @param isEnabled whether to enable the panel and all components on it.
+ */
+ public void setEnabled(boolean isEnabled) {
+ super.setEnabled(isEnabled);
+ int count = buttonContainer.getComponentCount();
+ for (int i = 0; i < count; i++) {
+ buttonContainer.getComponent(i).setEnabled(isEnabled);
+ }
+ }
+
+ // Action Multicast methods
+
+ /**
+ * Adds an action listener to the list that will be notified by button events
+ * and changes in button state.
+ *
+ * @param l An action listener to be notified.
+ */
+ public void addActionListener(ActionListener l) {
+ actionListener = AWTEventMulticaster.add(actionListener, l);
+ }
+
+ /**
+ * Removes an action listener from the list that will be notified by button
+ * events and changes in button state.
+ *
+ * @param l An action listener to be removed.
+ */
+ public void removeActionListener(ActionListener l) {
+ actionListener = AWTEventMulticaster.remove(actionListener, l);
+ }
+
+ /**
+ * Notifies all registered action listeners of a pending Action Event.
+ *
+ * @param e An action event to be broadcast.
+ */
+ protected void broadcastEvent(ActionEvent e) {
+ if (actionListener != null) {
+ actionListener.actionPerformed(e);
+ }
+ }
+
+ // interface ActionListener
+
+ /**
+ * Called by buttons on panel and by other components that might be set to
+ * broadcast events to this listener.
+ *
+ * @param e An action event to be received.
+ */
+ public void actionPerformed(ActionEvent e) {
+ broadcastEvent(e);
+ }
+
+ /**
+ * A property change listener that listens specifically for property changes
+ * from action objects. This is the class that ties in the action to the button.
+ * This class is added to an action as a property change listener. The
+ * corresponding component is referenced by this class toe easily handle updates
+ * to the component caused by changes to the action.
+ */
+ public class ActionChangeListener implements PropertyChangeListener {
+ /** The UI component that is affected by the action's changes. */
+ Component theComponent;
+
+ /**
+ * Constructs an ActionChangeListener with the given component being the
+ * recipient of the action's changes.
+ *
+ * @param The component to bind with the action.
+ */
+ public ActionChangeListener(Component aComponent) {
+ super();
+ theComponent = aComponent;
+ }
+
+ /**
+ * Called whenever a property changes on the action object.
+ *
+ * @pram e The property change event generated by the action.
+ */
+ public void propertyChange(PropertyChangeEvent e) {
+ String propertyName = e.getPropertyName();
+ if (propertyName.equals(Action.NAME)) {
+ String name = (String) e.getNewValue();
+ if (theComponent instanceof AbstractButton) {
+ AbstractButton button = (AbstractButton) theComponent;
+ String oldName = button.getName();
+ button.setText(name);
+ button.setName(name);
+ button.setActionCommand(name);
+
+ // Replace the old name of the component with the new name
+ // in the ButtonPanel's list of components.
+ buttonList.setElementAt(name, buttonList.indexOf(oldName));
+ }
+
+ // TODO: If component is not a button (or doesn't define the getText()
+ // then what should be done.
+ } else if (propertyName.equals("enabled")) {
+ Boolean enabled = (Boolean) e.getNewValue();
+ theComponent.setEnabled(ButtonPanel.this.isEnabled() ? enabled.booleanValue() : false);
+ }
+
+ // TODO: Icon?
+ }
+ }
+
+ // for testing
+
+ public static void main(String[] argv) {
+ try {
+ UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
+ } catch (Exception exc) {
+
+ }
+
+ JFrame dialog = new JFrame();
+ BorderLayout bl = new BorderLayout(20, 20);
+
+ ButtonPanel panel = new ButtonPanel();
// ButtonPanel panel = new ButtonPanel( new String[] { "OkayOkay", "CancelCancel" } );
- dialog.getContentPane().setLayout( bl );
- dialog.getContentPane().add( panel, BorderLayout.CENTER );
- dialog.setLocation( 50, 50 );
- // dialog.setSize( 450, 150 );
-
- panel.setAlignment( BetterFlowLayout.CENTER_VERTICAL );
- panel.getButton( "One" ).setEnabled( false );
-
- dialog.pack();
- dialog.setVisible( true );
-
- try
- {
- BeanInfo info = Introspector.getBeanInfo( ButtonPanel.class );
- PropertyDescriptor[] props = info.getPropertyDescriptors();
- for ( int i = 0; i < props.length; i++ )
- {
- System.out.println( props[i].getName() );
- }
- }
- catch (Exception exc)
- {
- System.out.println( exc );
- }
+ dialog.getContentPane().setLayout(bl);
+ dialog.getContentPane().add(panel, BorderLayout.CENTER);
+ dialog.setLocation(50, 50);
+ // dialog.setSize( 450, 150 );
+ panel.setAlignment(BetterFlowLayout.CENTER_VERTICAL);
+ panel.getButton("One").setEnabled(false);
+ dialog.pack();
+ dialog.setVisible(true);
+ try {
+ BeanInfo info = Introspector.getBeanInfo(ButtonPanel.class);
+ PropertyDescriptor[] props = info.getPropertyDescriptors();
+ for (int i = 0; i < props.length; i++) {
+ System.out.println(props[i].getName());
+ }
+ } catch (Exception exc) {
+ System.out.println(exc);
+ }
- }
-
- public void mouseDragged(MouseEvent e)
- {
- }
-
- public void mouseMoved(MouseEvent e)
- {
- }
+ }
+ public void mouseDragged(MouseEvent e) {
+ }
+ public void mouseMoved(MouseEvent e) {
+ }
}
-
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/CheckButtonPanel.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/CheckButtonPanel.java
index 5e847ae..8116678 100644
--- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/CheckButtonPanel.java
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/CheckButtonPanel.java
@@ -27,246 +27,223 @@ import javax.swing.JCheckBox;
import javax.swing.border.EmptyBorder;
/**
-* CheckButtonPanel is a simple extension of ButtonPanel.
-* Differences are that it uses JCheckBoxes and the
-* default alignment is vertical. The panel defaults to having
-* no buttons selected.
-*
-* @author michael@mpowers.net
-* @author $Author: cgruber $
-* @version $Revision: 904 $
-*/
-public class CheckButtonPanel extends ButtonPanel
-{
-/**
-* Constructs a CheckButtonPanel. Three buttons are created
-* so the panel is filled when used in a GUI-builder environment.
-*/
- public CheckButtonPanel()
- {
- super();
- }
-
-/**
-* Constructs a ButtonPanel using specified buttons.
-* @param buttonList An array containing the strings to be used in labeling the buttons.
-*/
- public CheckButtonPanel( String[] buttonList )
- {
- super( buttonList );
- }
-
-/**
-* Overridden to set vertical-center alignment and zero vgap.
-*/
- protected void initLayout()
- {
- super.initLayout();
- buttonPanelLayout.setAlignment( BetterFlowLayout.CENTER_VERTICAL );
- buttonPanelLayout.setVgap( 0 );
- }
-
-/**
-* Overridden to return a JRadioButton.
-* @param aLabel The label for the component that will be created.
-* @return The newly created component.
-*/
- protected Component createComponentWithLabel( String aLabel )
- {
- String buttonLabel = aLabel;
- JCheckBox newButton = new JCheckBox();
- newButton.setName( aLabel );
- newButton.setText( buttonLabel );
- newButton.setActionCommand( aLabel );
- newButton.addActionListener( this );
-
- // reduce insets per java l&f guidelines (was 4 on each side)
- newButton.setBorder( new EmptyBorder( 1, 4, 1, 4 ) );
-
- return newButton;
- }
-
-/**
-* Sets the value of the button whose name matches the given text value.
-* @param aName A String matching the name of one of the buttons.
-* If null, empty, or not matching, nothing happens.
-* @param aValue A value to set the button.
-*/
- public void setValue(String aName, boolean aValue)
- {
- if ( aName != null )
- {
- Component c = null;
- int count = buttonContainer.getComponentCount();
- for ( int i = 0; i < count; i++ )
- {
- c = buttonContainer.getComponent( i );
- if ( c instanceof AbstractButton )
- {
- if ( c.getName().equals( aName ) )
- {
- ((AbstractButton)c).setSelected( aValue );
- c.repaint();
- return;
- }
- }
- }
- }
- // null, empty, or not matching - exit.
- System.out.println( "CheckButtonPanel.setValue: not found: " + aName );
- }
-
-/**
-* Sets the state of the specified buttons to the specified value.
-* @param aLabelArray An Array of Strings listing the buttons to be set.
-* @param aValue The value to which the specified buttons will be set.
-*/
- public void setValues(String[] aLabelArray, boolean aValue)
- {
- if ( aLabelArray != null )
- {
- for ( int i = 0; i < aLabelArray.length; i++ )
- {
- setValue( aLabelArray[i], aValue );
- }
- }
- }
-
-/**
-* Convenience method to set all checkboxes on the panel.
-* @param aValue The value to which all checkboxes on the panel will be set.
-*/
- public void setAllValues(boolean aValue)
- {
- setValues( getLabels(), aValue );
- }
-
-/**
-* Convenience method to check all boxes on the panel.
-*/
- public void checkAll()
- {
- setAllValues( true );
- }
-
-/**
-* Convenience method to clear all boxes on the panel.
-*/
- public void clearAll()
- {
- setAllValues( false );
- }
-
-/**
-* A convenience method to set only those buttons on the entire
-* panel that should be checked. Buttons not in the list are unchecked.
-* @param aLabelArray An Array of Strings listing the buttons to be set.
-*/
- public void setCheckedValues(String[] aLabelArray)
- {
- setAllValues( false );
- setValues( aLabelArray, true );
- }
-
-/**
-* Gets the labels of all checkboxes that are checked.
-* @return A List of Strings containing the labels of the boxes that are checked.
-*/
- public List getCheckedValueList()
- {
- Vector v = new Vector();
- Component c = null;
- int count = buttonContainer.getComponentCount();
- for ( int i = 0; i < count; i++ )
- {
- c = buttonContainer.getComponent( i );
- if ( c instanceof AbstractButton )
- {
- if ( ((AbstractButton)c).isSelected() )
- {
- v.addElement(c.getName());
- }
- }
- }
- return v;
- }
-
-/**
-* Gets the labels of all checkboxes that are checked.
-* @return A String Array containing the labels of the boxes that are checked.
-*/
- public String[] getCheckedValues()
- {
- List v = getCheckedValueList();
- String[] result = new String[ v.size() ];
- for ( int i = 0; i < v.size(); i++ )
- {
- result[i] = (String) v.get(i);
- }
- return result;
- }
-
-/**
-* Gets the value of the specified button.
-* @param aName A String matching the name of one of the buttons.
-* @return True if the button is checked, False if it is not checked.
-* NOTE: If the button is not found in the list, False is returned.
-*/
- public boolean getValue( String aName )
- {
- Component c = null;
- int count = buttonContainer.getComponentCount();
- for ( int i = 0; i < count; i++ )
- {
- c = buttonContainer.getComponent( i );
- if ( ( c instanceof AbstractButton ) && ( ((AbstractButton)c).isSelected() ) )
- {
- if ( ((AbstractButton)c).getText().equals( aName ) )
- {
- return ((AbstractButton)c).isSelected();
- }
- }
- }
- return false;
- }
-
- // for testing
-
- public static void main( String[] argv )
- {
- try
- {
- javax.swing.UIManager.setLookAndFeel( javax.swing.UIManager.getSystemLookAndFeelClassName() );
- }
- catch (Exception exc)
- {
-
- }
-
- javax.swing.JFrame dialog = new javax.swing.JFrame();
- java.awt.BorderLayout bl = new java.awt.BorderLayout( 20, 20 );
-
- CheckButtonPanel panel = new CheckButtonPanel( new String[] { "One", "Two", "Three" } );
-
- dialog.getContentPane().setLayout( bl );
- dialog.getContentPane().add( panel, java.awt.BorderLayout.CENTER );
- dialog.setLocation( 50, 50 );
- // dialog.setSize( 450, 150 );
-
- panel.setAlignment( BetterFlowLayout.CENTER_VERTICAL );
- panel.getButton( "One" ).setEnabled( false );
- panel.setValues( new String[] { "One" }, true );
- panel.setValue( "Three", true );
+ * CheckButtonPanel is a simple extension of ButtonPanel. Differences are that
+ * it uses JCheckBoxes and the default alignment is vertical. The panel defaults
+ * to having no buttons selected.
+ *
+ * @author michael@mpowers.net
+ * @author $Author: cgruber $
+ * @version $Revision: 904 $
+ */
+public class CheckButtonPanel extends ButtonPanel {
+ /**
+ * Constructs a CheckButtonPanel. Three buttons are created so the panel is
+ * filled when used in a GUI-builder environment.
+ */
+ public CheckButtonPanel() {
+ super();
+ }
+
+ /**
+ * Constructs a ButtonPanel using specified buttons.
+ *
+ * @param buttonList An array containing the strings to be used in labeling the
+ * buttons.
+ */
+ public CheckButtonPanel(String[] buttonList) {
+ super(buttonList);
+ }
+
+ /**
+ * Overridden to set vertical-center alignment and zero vgap.
+ */
+ protected void initLayout() {
+ super.initLayout();
+ buttonPanelLayout.setAlignment(BetterFlowLayout.CENTER_VERTICAL);
+ buttonPanelLayout.setVgap(0);
+ }
+
+ /**
+ * Overridden to return a JRadioButton.
+ *
+ * @param aLabel The label for the component that will be created.
+ * @return The newly created component.
+ */
+ protected Component createComponentWithLabel(String aLabel) {
+ String buttonLabel = aLabel;
+ JCheckBox newButton = new JCheckBox();
+ newButton.setName(aLabel);
+ newButton.setText(buttonLabel);
+ newButton.setActionCommand(aLabel);
+ newButton.addActionListener(this);
+
+ // reduce insets per java l&f guidelines (was 4 on each side)
+ newButton.setBorder(new EmptyBorder(1, 4, 1, 4));
+
+ return newButton;
+ }
+
+ /**
+ * Sets the value of the button whose name matches the given text value.
+ *
+ * @param aName A String matching the name of one of the buttons. If null,
+ * empty, or not matching, nothing happens.
+ * @param aValue A value to set the button.
+ */
+ public void setValue(String aName, boolean aValue) {
+ if (aName != null) {
+ Component c = null;
+ int count = buttonContainer.getComponentCount();
+ for (int i = 0; i < count; i++) {
+ c = buttonContainer.getComponent(i);
+ if (c instanceof AbstractButton) {
+ if (c.getName().equals(aName)) {
+ ((AbstractButton) c).setSelected(aValue);
+ c.repaint();
+ return;
+ }
+ }
+ }
+ }
+ // null, empty, or not matching - exit.
+ System.out.println("CheckButtonPanel.setValue: not found: " + aName);
+ }
+
+ /**
+ * Sets the state of the specified buttons to the specified value.
+ *
+ * @param aLabelArray An Array of Strings listing the buttons to be set.
+ * @param aValue The value to which the specified buttons will be set.
+ */
+ public void setValues(String[] aLabelArray, boolean aValue) {
+ if (aLabelArray != null) {
+ for (int i = 0; i < aLabelArray.length; i++) {
+ setValue(aLabelArray[i], aValue);
+ }
+ }
+ }
+
+ /**
+ * Convenience method to set all checkboxes on the panel.
+ *
+ * @param aValue The value to which all checkboxes on the panel will be set.
+ */
+ public void setAllValues(boolean aValue) {
+ setValues(getLabels(), aValue);
+ }
+
+ /**
+ * Convenience method to check all boxes on the panel.
+ */
+ public void checkAll() {
+ setAllValues(true);
+ }
+
+ /**
+ * Convenience method to clear all boxes on the panel.
+ */
+ public void clearAll() {
+ setAllValues(false);
+ }
+
+ /**
+ * A convenience method to set only those buttons on the entire panel that
+ * should be checked. Buttons not in the list are unchecked.
+ *
+ * @param aLabelArray An Array of Strings listing the buttons to be set.
+ */
+ public void setCheckedValues(String[] aLabelArray) {
+ setAllValues(false);
+ setValues(aLabelArray, true);
+ }
+
+ /**
+ * Gets the labels of all checkboxes that are checked.
+ *
+ * @return A List of Strings containing the labels of the boxes that are
+ * checked.
+ */
+ public List getCheckedValueList() {
+ Vector v = new Vector();
+ Component c = null;
+ int count = buttonContainer.getComponentCount();
+ for (int i = 0; i < count; i++) {
+ c = buttonContainer.getComponent(i);
+ if (c instanceof AbstractButton) {
+ if (((AbstractButton) c).isSelected()) {
+ v.addElement(c.getName());
+ }
+ }
+ }
+ return v;
+ }
+
+ /**
+ * Gets the labels of all checkboxes that are checked.
+ *
+ * @return A String Array containing the labels of the boxes that are checked.
+ */
+ public String[] getCheckedValues() {
+ List v = getCheckedValueList();
+ String[] result = new String[v.size()];
+ for (int i = 0; i < v.size(); i++) {
+ result[i] = (String) v.get(i);
+ }
+ return result;
+ }
+
+ /**
+ * Gets the value of the specified button.
+ *
+ * @param aName A String matching the name of one of the buttons.
+ * @return True if the button is checked, False if it is not checked. NOTE: If
+ * the button is not found in the list, False is returned.
+ */
+ public boolean getValue(String aName) {
+ Component c = null;
+ int count = buttonContainer.getComponentCount();
+ for (int i = 0; i < count; i++) {
+ c = buttonContainer.getComponent(i);
+ if ((c instanceof AbstractButton) && (((AbstractButton) c).isSelected())) {
+ if (((AbstractButton) c).getText().equals(aName)) {
+ return ((AbstractButton) c).isSelected();
+ }
+ }
+ }
+ return false;
+ }
+
+ // for testing
+
+ public static void main(String[] argv) {
+ try {
+ javax.swing.UIManager.setLookAndFeel(javax.swing.UIManager.getSystemLookAndFeelClassName());
+ } catch (Exception exc) {
+
+ }
+
+ javax.swing.JFrame dialog = new javax.swing.JFrame();
+ java.awt.BorderLayout bl = new java.awt.BorderLayout(20, 20);
+
+ CheckButtonPanel panel = new CheckButtonPanel(new String[] { "One", "Two", "Three" });
+
+ dialog.getContentPane().setLayout(bl);
+ dialog.getContentPane().add(panel, java.awt.BorderLayout.CENTER);
+ dialog.setLocation(50, 50);
+ // dialog.setSize( 450, 150 );
+
+ panel.setAlignment(BetterFlowLayout.CENTER_VERTICAL);
+ panel.getButton("One").setEnabled(false);
+ panel.setValues(new String[] { "One" }, true);
+ panel.setValue("Three", true);
// panel.setCheckedValues( new String[] { "Two" } );
- String[] values = panel.getCheckedValues();
- for ( int i = 0; i < values.length; i++ )
- {
- System.out.println( values[i] );
- }
+ String[] values = panel.getCheckedValues();
+ for (int i = 0; i < values.length; i++) {
+ System.out.println(values[i]);
+ }
- dialog.pack();
- dialog.setVisible( true );
+ dialog.pack();
+ dialog.setVisible(true);
- }
+ }
}
-
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/ColorCellEditor.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/ColorCellEditor.java
index a0a14ac..10feef7 100644
--- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/ColorCellEditor.java
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/ColorCellEditor.java
@@ -29,56 +29,42 @@ import javax.swing.JCheckBox;
import javax.swing.JTable;
/**
-* A TableCellEditor that edits colors - it launches a color dialog when clicked.
-*
-* @author michael@mpowers.net
-* @author $Author: cgruber $
-* @version $Revision: 904 $
-*/
+ * A TableCellEditor that edits colors - it launches a color dialog when
+ * clicked.
+ *
+ * @author michael@mpowers.net
+ * @author $Author: cgruber $
+ * @version $Revision: 904 $
+ */
class ColorCellEditor extends DefaultCellEditor {
- Color currentColor = null;
-
- public ColorCellEditor(JButton b)
- {
- super(new JCheckBox()); // unfortunately, the constructor
- // expects a check box, combo box,
- // or text field.
- editorComponent = b;
- setClickCountToStart(1); // this is usually 1 or 2.
-
- // must do this so that editing stops when appropriate.
- b.addActionListener(new ActionListener()
- {
- public void actionPerformed(ActionEvent e)
- {
- fireEditingStopped();
- }
- }
- );
- }
-
- protected void fireEditingStopped()
- {
- super.fireEditingStopped();
- }
-
- public Object getCellEditorValue()
- {
- return currentColor;
- }
-
- public Component getTableCellEditorComponent(JTable table,
- Object value,
- boolean isSelected,
- int row,
- int column)
- {
- ((JButton)editorComponent).setText(value.toString());
- currentColor = (Color)value;
- return editorComponent;
- }
+ Color currentColor = null;
+
+ public ColorCellEditor(JButton b) {
+ super(new JCheckBox()); // unfortunately, the constructor
+ // expects a check box, combo box,
+ // or text field.
+ editorComponent = b;
+ setClickCountToStart(1); // this is usually 1 or 2.
+
+ // must do this so that editing stops when appropriate.
+ b.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ fireEditingStopped();
+ }
+ });
+ }
+
+ protected void fireEditingStopped() {
+ super.fireEditingStopped();
+ }
+
+ public Object getCellEditorValue() {
+ return currentColor;
+ }
+
+ public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
+ ((JButton) editorComponent).setText(value.toString());
+ currentColor = (Color) value;
+ return editorComponent;
+ }
}
-
-
-
-
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/ColorCellRenderer.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/ColorCellRenderer.java
index 0552183..2fc97bb 100644
--- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/ColorCellRenderer.java
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/ColorCellRenderer.java
@@ -28,54 +28,39 @@ import javax.swing.border.Border;
import javax.swing.table.TableCellRenderer;
/**
-* A TableCellRenderer that renders colors.
-*
-* @author michael@mpowers.net
-* @author $Author: cgruber $
-* @version $Revision: 904 $
-*/
+ * A TableCellRenderer that renders colors.
+ *
+ * @author michael@mpowers.net
+ * @author $Author: cgruber $
+ * @version $Revision: 904 $
+ */
public class ColorCellRenderer extends JLabel implements TableCellRenderer {
- Border unselectedBorder = null;
- Border selectedBorder = null;
- boolean isBordered = true;
+ Border unselectedBorder = null;
+ Border selectedBorder = null;
+ boolean isBordered = true;
- public ColorCellRenderer(boolean isBordered)
- {
- super();
- this.isBordered = isBordered;
- setOpaque(true); // must do this for background to show up.
- }
+ public ColorCellRenderer(boolean isBordered) {
+ super();
+ this.isBordered = isBordered;
+ setOpaque(true); // must do this for background to show up.
+ }
- public Component getTableCellRendererComponent(
- JTable table, Object color,
- boolean isSelected, boolean hasFocus,
- int row, int column)
- {
- setBackground((Color)color);
- if (isBordered)
- {
- if (isSelected)
- {
- if (selectedBorder == null)
- {
- selectedBorder = BorderFactory.createMatteBorder(2,5,2,5,
- table.getSelectionBackground());
- }
- setBorder(selectedBorder);
- }
- else
- {
- if (unselectedBorder == null)
- {
- unselectedBorder = BorderFactory.createMatteBorder(2,5,2,5,
- table.getBackground());
- }
- setBorder(unselectedBorder);
- }
- }
- return this;
- }
+ public Component getTableCellRendererComponent(JTable table, Object color, boolean isSelected, boolean hasFocus,
+ int row, int column) {
+ setBackground((Color) color);
+ if (isBordered) {
+ if (isSelected) {
+ if (selectedBorder == null) {
+ selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5, table.getSelectionBackground());
+ }
+ setBorder(selectedBorder);
+ } else {
+ if (unselectedBorder == null) {
+ unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5, table.getBackground());
+ }
+ setBorder(unselectedBorder);
+ }
+ }
+ return this;
+ }
}
-
-
-
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/ComboBoxCellRenderer.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/ComboBoxCellRenderer.java
index 2bf8dd6..67cb726 100644
--- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/ComboBoxCellRenderer.java
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/ComboBoxCellRenderer.java
@@ -26,32 +26,24 @@ import javax.swing.JTable;
import javax.swing.table.TableCellRenderer;
/**
-* A TableCellRenderer that paints a JComboBox. Useful if
-* you want to visibly display the JComboBox before the
-* user clicks on the cell.
-*
-* @author bsafa@intersectsoft.com
-* @author $Author: cgruber $
-* @version $Revision: 904 $
-*/
+ * A TableCellRenderer that paints a JComboBox. Useful if you want to visibly
+ * display the JComboBox before the user clicks on the cell.
+ *
+ * @author bsafa@intersectsoft.com
+ * @author $Author: cgruber $
+ * @version $Revision: 904 $
+ */
public class ComboBoxCellRenderer extends JComboBox implements TableCellRenderer {
- public ComboBoxCellRenderer()
- {
- super();
- setOpaque(true);
- }
-
- public Component getTableCellRendererComponent(
- JTable table, Object value,
- boolean isSelected, boolean hasFocus,
- int row, int column)
- {
- setBackground(Color.white);
- setSelectedItem(value);
- return this;
- }
+ public ComboBoxCellRenderer() {
+ super();
+ setOpaque(true);
+ }
+
+ public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
+ int row, int column) {
+ setBackground(Color.white);
+ setSelectedItem(value);
+ return this;
+ }
}
-
-
-
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/DateTextField.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/DateTextField.java
index 18ed035..8a3b08c 100644
--- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/DateTextField.java
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/DateTextField.java
@@ -30,601 +30,507 @@ import java.util.Vector;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
-
-/**
-* DateTextField is a "smart" text field that restricts the user's input. The
-* input is restructed to a string representing a date format.
-*
-* @author rob@straylight.princeton.com
-* @author $Author: cgruber $
-* @version $Revision: 904 $
-*/
-public class DateTextField extends JTextField
-{
-
-/*******************************
-* CONSTANTS
-*******************************/
-
-/**
-* Use the current date for this text field.
-*/
- public static final int CURRENT_DATE = 0;
-
/**
-* Use blanks for this text field.
-*/
- public static final int BLANKS = 1;
-
-/**
-* Use underscores for this text field.
-*/
- public static final int UNDERSCORES = 2;
-
-/**
-* Use just a 4-digit year for this text field.
-*/
- public static final int YEAR = 3;
-
- private static final int BACKSPACE = 8;
- private static final int DELETE = 127;
- private static final int PASTE = 22; // Ctl-V
- private static final int CUT = 24; // Ctl-X
-
-
-/*******************************
-* DATA MEMEBERS
-*******************************/
- private int defaultType = CURRENT_DATE;
-
- private boolean warningMessageActive = false;
-
-
-/*******************************
-* PUBLIC METHODS
-*******************************/
-
-/**
-* Default Constructor.
-*/
- public DateTextField()
- {
- this(1, 1, 1999, 0);
-
- Calendar rightNow = Calendar.getInstance();
-
- super.setText(createDateString(rightNow.get(Calendar.MONTH) + 1,
- rightNow.get(Calendar.DATE),
- rightNow.get(Calendar.YEAR)));
- }
-
-/**
-* Constructor.
-* @param month Number of the month, January being 1.
-* @param data The day of the month.
-* @param year The year.
-*/
- public DateTextField(int month, int date, int year)
- {
- this(month, date, year, 0);
- }
-
-/**
-* Constructor.
-* @param columns Width of the text field (in characters).
-*/
- public DateTextField(int columns)
- {
- this(1, 1, 1998, columns);
-
- Calendar rightNow = Calendar.getInstance();
-
- super.setText(createDateString(rightNow.get(Calendar.MONTH) + 1,
- rightNow.get(Calendar.DATE),
- rightNow.get(Calendar.YEAR)));
- }
-
-/**
-* Constructor.
-* @param month Number of the month, January being 1.
-* @param data The day of the month.
-* @param year The year.
-* @param columns Width of the text field (in characters).
-*/
- public DateTextField(int month, int date, int year, int columns)
- {
- super("", columns);
-
- super.setText(createDateString(month, date, year));
-
- this.addFocusListener(new FocusAdapter()
- {
- public void focusLost(FocusEvent e)
- {
- if (!(e.isTemporary()))
- {
- validateDateString(e);
- }
- }
- });
- }
-
-/**
-* Sets the date type to display when the user has not entered any date yet.
-* Default is the current date.
-* @see #CURRENT_DATE
-* @see #BLANKS
-* @see #UNDERSCORES
-* @param newDefaultType The type of date to display when there is no date data.
-*/
- public void setDefaultType(int newDefaultType)
- {
- if (newDefaultType == BLANKS)
- {
- defaultType = BLANKS;
- super.setText(" / / ");
- }
- else if (newDefaultType == UNDERSCORES)
- {
- defaultType = UNDERSCORES;
- super.setText("__/__/____");
- }
- else if (newDefaultType == YEAR)
- {
- defaultType = YEAR;
- super.setText("0000");
- }
- else
- {
- defaultType = CURRENT_DATE;
-
- Calendar rightNow = Calendar.getInstance();
-
- super.setText(createDateString(rightNow.get(Calendar.MONTH) + 1,
- rightNow.get(Calendar.DATE),
- rightNow.get(Calendar.YEAR)));
- }
- }
-
-/**
-* Returns the type of date to display when there is no user input.
-* @see #CURRENT_DATE
-* @see #BLANKS
-* @see #UNDERSCORES
-* @return The type of date to display when there is no date to display.
-*/
- public int getDefaultType()
- {
- return defaultType;
- }
-
-/**
-* Sets the text field to the string representation of the specified date.
-* @param aDate The date to set the text field to.
-*/
- public void setDate(Date aDate)
- {
- Calendar aCalendar = Calendar.getInstance();
-
- aCalendar.setTime(aDate);
-
- super.setText(createDateString(aCalendar.get(Calendar.MONTH) + 1,
- aCalendar.get(Calendar.DATE),
- aCalendar.get(Calendar.YEAR)));
- }
-
-/**
-* Sets the text field directly from a Date object.
-* @param aDate The date to set the text field to.
-*/
- public void setText( Date aDate )
- {
- setDate( aDate );
- }
-
-/**
-* Sets the text field to the date specified in the string. This is overridden
-* from the parent class to insure a valid date is inputted. The format of the
-* date expected is the type of date format this text field is currently set to.
-* @param aString A string representing a date in this text field current format.
-*/
- public void setText( String aString )
- {
- Date testDate = null;
-
- if ( aString != null )
- {
- ParsePosition position = new ParsePosition( 0 );
-
- if ( defaultType == YEAR )
- {
- SimpleDateFormat yearFormatter = new SimpleDateFormat( "yyyy" );
- testDate = yearFormatter.parse( aString, position );
- }
- else
- {
- SimpleDateFormat fullDateFormatter = new SimpleDateFormat( "MM/dd/yyyy" );
- testDate = fullDateFormatter.parse( aString, position );
- }
- }
-
- // The string is not a valid date, use default value for date then.
- if ( testDate == null )
- {
- Calendar aCalendar = Calendar.getInstance();
-
- testDate = aCalendar.getTime();
- }
-
- setDate( testDate );
- }
-
-/**
-* Returns the date as represented by the date string in the text field.
-* @return The date in the text field.
-*/
- public Date getDate() throws NumberFormatException
- {
- Calendar aCalendar = Calendar.getInstance();
- int year = 1980;
- int month = 0;
- int date = 1;
- int[] tempArray = {1,3,5,7,8,10,12};
- Vector monthsWith31Days = new Vector(7);
-
- for (int i = 0; i < tempArray.length; ++i)
- {
- monthsWith31Days.addElement(new Integer(tempArray[i]));
- }
-
- aCalendar.set(year, month, date, 12, 0, 0);
-
- try
- {
- String dateString = getText();
- NumberFormatException nfException = new NumberFormatException(new String("Invalid Date String: " + dateString));
-
- if (defaultType == YEAR)
- {
- year = Integer.parseInt(dateString);
-
- aCalendar.set(year, 0, 1, 12, 0, 0);
-
- return aCalendar.getTime();
- }
-
- month = Integer.parseInt(dateString.substring(0, 2).trim());
- date = Integer.parseInt(dateString.substring(3, 5).trim());
- year = Integer.parseInt(dateString.substring(6).trim());
-
- if ((month < 1) || (month > 12))
- {
- throw nfException;
- }
-
- if ((date < 1) || (date > 31))
- {
- throw nfException;
- }
-
- if ((date == 31) && (!(monthsWith31Days.contains(new Integer(month)))))
- {
- throw nfException;
- }
-
- if ((date == 30) && (month == 2))
- {
- throw nfException;
- }
-
- if ((date == 29) && (month == 2))
- {
- if ((year % 100) == 0)
- {
- if ((year % 400) != 0)
- {
- throw nfException;
- }
- }
- else
- {
- if ((year % 4) != 0)
- {
- throw nfException;
- }
- }
- }
- }
- catch (IndexOutOfBoundsException ioobe)
- {
- NumberFormatException nfException = new NumberFormatException(new String("Invalid Date String: " + getText()));
- throw nfException;
- }
- catch (NumberFormatException nfe)
- {
- NumberFormatException nfException = new NumberFormatException(new String("Invalid Date String: " + getText()));
- throw nfException;
- }
-
- aCalendar.set(year, (month - 1), date, 12, 0, 0);
-
- return aCalendar.getTime();
- }
-
- public void processKeyEvent(KeyEvent e)
- {
- String currentString = "";
- String testString = "";
- char newChar = e.getKeyChar();
- int currentLength = 0;
- int currentCaretPosition = 0;
- int selectionStart = 0;
- int selectionEnd = 0;
- int modifierPosition = 0;
- int modifierDirection = 1;
- char modifierCharacter;
- boolean backspace = false;
- boolean delete = false;
- boolean paste = false;
- boolean cut = false;
- boolean keyPressed = false;
-
- backspace = (newChar == BACKSPACE);
- delete = (newChar == DELETE);
- paste = (newChar == PASTE);
- cut = (newChar == CUT);
-
- keyPressed = (e.paramString().startsWith("KEY_PRESSED"));
-
- if ((e.getKeyCode() == KeyEvent.VK_UNDEFINED) || ((backspace) || (delete) || (paste) || (cut))) // A "key-typed" event
- {
- if (isValidCharacter(newChar))
- {
- if ((isPrintableCharacter(newChar)) || (backspace) || (delete))
- {
- // Both the key "pressed" and key "released" events get passed
- // in here for the delete and backspace key. Only processes
- // these keys if the event is key "pressed".
- if (((backspace) || (delete)) && (!(keyPressed)))
- {
- // Don't do anything, pass through to consumption.
- }
- else
- {
- // Analyze the current contents of the field
- currentString = getText();
- currentLength = currentString.length();
-
- char[] tempText = new char[currentLength];
-
- currentCaretPosition = getCaretPosition();
-
- selectionStart = getSelectionStart();
- selectionEnd = getSelectionEnd();
-
- // if a range is selected, then get rid of it and place the caret
- // at the begginning of the range and continue processing.
- if (selectionStart != selectionEnd)
- {
- selectionEnd = selectionStart;
- setSelectionEnd(selectionEnd);
-
- currentCaretPosition = selectionStart;
- setCaretPosition(currentCaretPosition);
- }
-
- if (currentCaretPosition <= currentLength)
- {
- // a number of delete or backspace was pressed, delete and
- // backspace deletes a number and places a "space" there
-
- // if caret at start of string and the backspace pressed OR
- // caret at end of string and delete or number pressed THEN
- // don't do anything, otherwise process key stroke
- if (((currentCaretPosition == 0) && (backspace)) ||
- ((currentCaretPosition == currentLength) && (!(backspace))))
- {
- // Don't do any processing.
- }
- else
- {
- modifierPosition = currentCaretPosition;
- if (backspace)
- {
- modifierDirection = -1;
- modifierPosition += modifierDirection;
- }
-
- // Overwrite the current position with the new character
- // inputted or overwrite using a space or underscore if
- // the backspace or delete key was pressed.
- if (defaultType != YEAR)
- {
- modifierCharacter =
- ((delete)||(backspace)) ?
- ((defaultType == UNDERSCORES) ? '_' : ' ') :
- newChar;
- }
- else
- {
- // We are dealing with a 4-digit year. Overwrite
- // with new character or "0" if delete or backspace
- // was pressed.
- modifierCharacter = ((delete)||(backspace)) ? ('0') : newChar;
- }
-
- if (currentString.charAt(modifierPosition) == '/')
- {
- modifierPosition += modifierDirection;
- }
-
- for (int i = 0; i < currentLength; ++i)
- {
- if (i == modifierPosition)
- {
- tempText[i] = modifierCharacter;
- }
- else
- {
- tempText[i] = currentString.charAt(i);
- }
- }
-
- testString = new String(tempText);
- if (isValidString(testString))
- {
- super.setText(testString);
- if (backspace)
- {
- setCaretPosition(modifierPosition);
- }
- else
- {
- setCaretPosition(modifierPosition + 1);
- }
- }
- }
- }
- }
-
- e.consume();
- }
- else if ((cut) || (paste))
- {
- e.consume();
- }
- // else its a non-printable character, let it pass through
- }
- else
- {
- e.consume();
- }
- }
-
- super.processKeyEvent(e);
- }
-
- private boolean isValidCharacter(char aChar)
- {
- if (((aChar >= '!') && (aChar <= '/')) || ((aChar >= ':') && (aChar <= '~')))
- {
- return false;
- }
- return true;
- }
-
- private boolean isPrintableCharacter(char inputChar)
- {
- if ((inputChar >= ' ') && (inputChar <= '~'))
- {
- return true;
- }
- return false;
- }
-
- private boolean isValidDate(int month, int date, int year)
- {
- if ((month < 1) || (month > 12))
- {
- return false;
- }
-
- if ((date < 1) || (date > 31))
- {
- return false;
- }
-
- if ((year < 0) || (year > 9999))
- {
- return false;
- }
-
- return true;
- }
-
- private boolean isValidString(String aString)
- {
- return true;
- }
-
- private String createDateString(int month, int date, int year)
- {
- String dateString = "";
-
- if (isValidDate(month, date, year))
- {
- if (defaultType != YEAR)
- {
- if (month < 10)
- {
- dateString = "0";
- }
-
- dateString += String.valueOf(month);
- dateString += "/";
-
- if (date < 10)
- {
- dateString += "0";
- }
-
- dateString += String.valueOf(date);
- dateString += "/";
- }
-
- if (year < 1000)
- {
- dateString += "0";
- if (year < 100)
- {
- dateString += "0";
- if (year < 10)
- {
- dateString += "0";
- }
- }
- }
-
- dateString += String.valueOf(year);
- }
- else
- {
- if (defaultType == YEAR)
- {
- dateString = "1999";
- }
- else
- {
- dateString = "01/01/1999";
- }
- }
-
- return dateString;
- }
-
- private void validateDateString(FocusEvent e)
- {
- if (!(warningMessageActive))
- {
- try
- {
- getDate();
- }
- catch (NumberFormatException nfe)
- {
- System.out.println("Invalid Date String!!!");
- warningMessageActive = true;
- JOptionPane.showMessageDialog(this, "Invald Date: " + getText(), "Warning", JOptionPane.WARNING_MESSAGE);
- warningMessageActive = false;
- if (defaultType == YEAR)
- {
- super.setText("1999");
- }
- else
- {
- super.setText("01/01/1999");
- }
- }
- }
- }
+ * DateTextField is a "smart" text field that restricts the user's input. The
+ * input is restructed to a string representing a date format.
+ *
+ * @author rob@straylight.princeton.com
+ * @author $Author: cgruber $
+ * @version $Revision: 904 $
+ */
+public class DateTextField extends JTextField {
+
+ /*******************************
+ * CONSTANTS
+ *******************************/
+
+ /**
+ * Use the current date for this text field.
+ */
+ public static final int CURRENT_DATE = 0;
+
+ /**
+ * Use blanks for this text field.
+ */
+ public static final int BLANKS = 1;
+
+ /**
+ * Use underscores for this text field.
+ */
+ public static final int UNDERSCORES = 2;
+
+ /**
+ * Use just a 4-digit year for this text field.
+ */
+ public static final int YEAR = 3;
+
+ private static final int BACKSPACE = 8;
+ private static final int DELETE = 127;
+ private static final int PASTE = 22; // Ctl-V
+ private static final int CUT = 24; // Ctl-X
+
+ /*******************************
+ * DATA MEMEBERS
+ *******************************/
+ private int defaultType = CURRENT_DATE;
+
+ private boolean warningMessageActive = false;
+
+ /*******************************
+ * PUBLIC METHODS
+ *******************************/
+
+ /**
+ * Default Constructor.
+ */
+ public DateTextField() {
+ this(1, 1, 1999, 0);
+
+ Calendar rightNow = Calendar.getInstance();
+
+ super.setText(createDateString(rightNow.get(Calendar.MONTH) + 1, rightNow.get(Calendar.DATE),
+ rightNow.get(Calendar.YEAR)));
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param month Number of the month, January being 1.
+ * @param data The day of the month.
+ * @param year The year.
+ */
+ public DateTextField(int month, int date, int year) {
+ this(month, date, year, 0);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param columns Width of the text field (in characters).
+ */
+ public DateTextField(int columns) {
+ this(1, 1, 1998, columns);
+
+ Calendar rightNow = Calendar.getInstance();
+
+ super.setText(createDateString(rightNow.get(Calendar.MONTH) + 1, rightNow.get(Calendar.DATE),
+ rightNow.get(Calendar.YEAR)));
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param month Number of the month, January being 1.
+ * @param data The day of the month.
+ * @param year The year.
+ * @param columns Width of the text field (in characters).
+ */
+ public DateTextField(int month, int date, int year, int columns) {
+ super("", columns);
+
+ super.setText(createDateString(month, date, year));
+
+ this.addFocusListener(new FocusAdapter() {
+ public void focusLost(FocusEvent e) {
+ if (!(e.isTemporary())) {
+ validateDateString(e);
+ }
+ }
+ });
+ }
+
+ /**
+ * Sets the date type to display when the user has not entered any date yet.
+ * Default is the current date.
+ *
+ * @see #CURRENT_DATE
+ * @see #BLANKS
+ * @see #UNDERSCORES
+ * @param newDefaultType The type of date to display when there is no date data.
+ */
+ public void setDefaultType(int newDefaultType) {
+ if (newDefaultType == BLANKS) {
+ defaultType = BLANKS;
+ super.setText(" / / ");
+ } else if (newDefaultType == UNDERSCORES) {
+ defaultType = UNDERSCORES;
+ super.setText("__/__/____");
+ } else if (newDefaultType == YEAR) {
+ defaultType = YEAR;
+ super.setText("0000");
+ } else {
+ defaultType = CURRENT_DATE;
+
+ Calendar rightNow = Calendar.getInstance();
+
+ super.setText(createDateString(rightNow.get(Calendar.MONTH) + 1, rightNow.get(Calendar.DATE),
+ rightNow.get(Calendar.YEAR)));
+ }
+ }
+
+ /**
+ * Returns the type of date to display when there is no user input.
+ *
+ * @see #CURRENT_DATE
+ * @see #BLANKS
+ * @see #UNDERSCORES
+ * @return The type of date to display when there is no date to display.
+ */
+ public int getDefaultType() {
+ return defaultType;
+ }
+
+ /**
+ * Sets the text field to the string representation of the specified date.
+ *
+ * @param aDate The date to set the text field to.
+ */
+ public void setDate(Date aDate) {
+ Calendar aCalendar = Calendar.getInstance();
+
+ aCalendar.setTime(aDate);
+
+ super.setText(createDateString(aCalendar.get(Calendar.MONTH) + 1, aCalendar.get(Calendar.DATE),
+ aCalendar.get(Calendar.YEAR)));
+ }
+
+ /**
+ * Sets the text field directly from a Date object.
+ *
+ * @param aDate The date to set the text field to.
+ */
+ public void setText(Date aDate) {
+ setDate(aDate);
+ }
+
+ /**
+ * Sets the text field to the date specified in the string. This is overridden
+ * from the parent class to insure a valid date is inputted. The format of the
+ * date expected is the type of date format this text field is currently set to.
+ *
+ * @param aString A string representing a date in this text field current
+ * format.
+ */
+ public void setText(String aString) {
+ Date testDate = null;
+
+ if (aString != null) {
+ ParsePosition position = new ParsePosition(0);
+
+ if (defaultType == YEAR) {
+ SimpleDateFormat yearFormatter = new SimpleDateFormat("yyyy");
+ testDate = yearFormatter.parse(aString, position);
+ } else {
+ SimpleDateFormat fullDateFormatter = new SimpleDateFormat("MM/dd/yyyy");
+ testDate = fullDateFormatter.parse(aString, position);
+ }
+ }
+
+ // The string is not a valid date, use default value for date then.
+ if (testDate == null) {
+ Calendar aCalendar = Calendar.getInstance();
+
+ testDate = aCalendar.getTime();
+ }
+
+ setDate(testDate);
+ }
+
+ /**
+ * Returns the date as represented by the date string in the text field.
+ *
+ * @return The date in the text field.
+ */
+ public Date getDate() throws NumberFormatException {
+ Calendar aCalendar = Calendar.getInstance();
+ int year = 1980;
+ int month = 0;
+ int date = 1;
+ int[] tempArray = { 1, 3, 5, 7, 8, 10, 12 };
+ Vector monthsWith31Days = new Vector(7);
+
+ for (int i = 0; i < tempArray.length; ++i) {
+ monthsWith31Days.addElement(new Integer(tempArray[i]));
+ }
+
+ aCalendar.set(year, month, date, 12, 0, 0);
+
+ try {
+ String dateString = getText();
+ NumberFormatException nfException = new NumberFormatException(
+ new String("Invalid Date String: " + dateString));
+
+ if (defaultType == YEAR) {
+ year = Integer.parseInt(dateString);
+
+ aCalendar.set(year, 0, 1, 12, 0, 0);
+
+ return aCalendar.getTime();
+ }
+
+ month = Integer.parseInt(dateString.substring(0, 2).trim());
+ date = Integer.parseInt(dateString.substring(3, 5).trim());
+ year = Integer.parseInt(dateString.substring(6).trim());
+
+ if ((month < 1) || (month > 12)) {
+ throw nfException;
+ }
+
+ if ((date < 1) || (date > 31)) {
+ throw nfException;
+ }
+
+ if ((date == 31) && (!(monthsWith31Days.contains(new Integer(month))))) {
+ throw nfException;
+ }
+
+ if ((date == 30) && (month == 2)) {
+ throw nfException;
+ }
+
+ if ((date == 29) && (month == 2)) {
+ if ((year % 100) == 0) {
+ if ((year % 400) != 0) {
+ throw nfException;
+ }
+ } else {
+ if ((year % 4) != 0) {
+ throw nfException;
+ }
+ }
+ }
+ } catch (IndexOutOfBoundsException ioobe) {
+ NumberFormatException nfException = new NumberFormatException(
+ new String("Invalid Date String: " + getText()));
+ throw nfException;
+ } catch (NumberFormatException nfe) {
+ NumberFormatException nfException = new NumberFormatException(
+ new String("Invalid Date String: " + getText()));
+ throw nfException;
+ }
+
+ aCalendar.set(year, (month - 1), date, 12, 0, 0);
+
+ return aCalendar.getTime();
+ }
+
+ public void processKeyEvent(KeyEvent e) {
+ String currentString = "";
+ String testString = "";
+ char newChar = e.getKeyChar();
+ int currentLength = 0;
+ int currentCaretPosition = 0;
+ int selectionStart = 0;
+ int selectionEnd = 0;
+ int modifierPosition = 0;
+ int modifierDirection = 1;
+ char modifierCharacter;
+ boolean backspace = false;
+ boolean delete = false;
+ boolean paste = false;
+ boolean cut = false;
+ boolean keyPressed = false;
+
+ backspace = (newChar == BACKSPACE);
+ delete = (newChar == DELETE);
+ paste = (newChar == PASTE);
+ cut = (newChar == CUT);
+
+ keyPressed = (e.paramString().startsWith("KEY_PRESSED"));
+
+ if ((e.getKeyCode() == KeyEvent.VK_UNDEFINED) || ((backspace) || (delete) || (paste) || (cut))) // A "key-typed"
+ // event
+ {
+ if (isValidCharacter(newChar)) {
+ if ((isPrintableCharacter(newChar)) || (backspace) || (delete)) {
+ // Both the key "pressed" and key "released" events get passed
+ // in here for the delete and backspace key. Only processes
+ // these keys if the event is key "pressed".
+ if (((backspace) || (delete)) && (!(keyPressed))) {
+ // Don't do anything, pass through to consumption.
+ } else {
+ // Analyze the current contents of the field
+ currentString = getText();
+ currentLength = currentString.length();
+
+ char[] tempText = new char[currentLength];
+
+ currentCaretPosition = getCaretPosition();
+
+ selectionStart = getSelectionStart();
+ selectionEnd = getSelectionEnd();
+
+ // if a range is selected, then get rid of it and place the caret
+ // at the begginning of the range and continue processing.
+ if (selectionStart != selectionEnd) {
+ selectionEnd = selectionStart;
+ setSelectionEnd(selectionEnd);
+
+ currentCaretPosition = selectionStart;
+ setCaretPosition(currentCaretPosition);
+ }
+
+ if (currentCaretPosition <= currentLength) {
+ // a number of delete or backspace was pressed, delete and
+ // backspace deletes a number and places a "space" there
+
+ // if caret at start of string and the backspace pressed OR
+ // caret at end of string and delete or number pressed THEN
+ // don't do anything, otherwise process key stroke
+ if (((currentCaretPosition == 0) && (backspace))
+ || ((currentCaretPosition == currentLength) && (!(backspace)))) {
+ // Don't do any processing.
+ } else {
+ modifierPosition = currentCaretPosition;
+ if (backspace) {
+ modifierDirection = -1;
+ modifierPosition += modifierDirection;
+ }
+
+ // Overwrite the current position with the new character
+ // inputted or overwrite using a space or underscore if
+ // the backspace or delete key was pressed.
+ if (defaultType != YEAR) {
+ modifierCharacter = ((delete) || (backspace))
+ ? ((defaultType == UNDERSCORES) ? '_' : ' ')
+ : newChar;
+ } else {
+ // We are dealing with a 4-digit year. Overwrite
+ // with new character or "0" if delete or backspace
+ // was pressed.
+ modifierCharacter = ((delete) || (backspace)) ? ('0') : newChar;
+ }
+
+ if (currentString.charAt(modifierPosition) == '/') {
+ modifierPosition += modifierDirection;
+ }
+
+ for (int i = 0; i < currentLength; ++i) {
+ if (i == modifierPosition) {
+ tempText[i] = modifierCharacter;
+ } else {
+ tempText[i] = currentString.charAt(i);
+ }
+ }
+
+ testString = new String(tempText);
+ if (isValidString(testString)) {
+ super.setText(testString);
+ if (backspace) {
+ setCaretPosition(modifierPosition);
+ } else {
+ setCaretPosition(modifierPosition + 1);
+ }
+ }
+ }
+ }
+ }
+
+ e.consume();
+ } else if ((cut) || (paste)) {
+ e.consume();
+ }
+ // else its a non-printable character, let it pass through
+ } else {
+ e.consume();
+ }
+ }
+
+ super.processKeyEvent(e);
+ }
+
+ private boolean isValidCharacter(char aChar) {
+ if (((aChar >= '!') && (aChar <= '/')) || ((aChar >= ':') && (aChar <= '~'))) {
+ return false;
+ }
+ return true;
+ }
+
+ private boolean isPrintableCharacter(char inputChar) {
+ if ((inputChar >= ' ') && (inputChar <= '~')) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean isValidDate(int month, int date, int year) {
+ if ((month < 1) || (month > 12)) {
+ return false;
+ }
+
+ if ((date < 1) || (date > 31)) {
+ return false;
+ }
+
+ if ((year < 0) || (year > 9999)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ private boolean isValidString(String aString) {
+ return true;
+ }
+
+ private String createDateString(int month, int date, int year) {
+ String dateString = "";
+
+ if (isValidDate(month, date, year)) {
+ if (defaultType != YEAR) {
+ if (month < 10) {
+ dateString = "0";
+ }
+
+ dateString += String.valueOf(month);
+ dateString += "/";
+
+ if (date < 10) {
+ dateString += "0";
+ }
+
+ dateString += String.valueOf(date);
+ dateString += "/";
+ }
+
+ if (year < 1000) {
+ dateString += "0";
+ if (year < 100) {
+ dateString += "0";
+ if (year < 10) {
+ dateString += "0";
+ }
+ }
+ }
+
+ dateString += String.valueOf(year);
+ } else {
+ if (defaultType == YEAR) {
+ dateString = "1999";
+ } else {
+ dateString = "01/01/1999";
+ }
+ }
+
+ return dateString;
+ }
+
+ private void validateDateString(FocusEvent e) {
+ if (!(warningMessageActive)) {
+ try {
+ getDate();
+ } catch (NumberFormatException nfe) {
+ System.out.println("Invalid Date String!!!");
+ warningMessageActive = true;
+ JOptionPane.showMessageDialog(this, "Invald Date: " + getText(), "Warning",
+ JOptionPane.WARNING_MESSAGE);
+ warningMessageActive = false;
+ if (defaultType == YEAR) {
+ super.setText("1999");
+ } else {
+ super.setText("01/01/1999");
+ }
+ }
+ }
+ }
}
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/FormattedCellRenderer.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/FormattedCellRenderer.java
index b3e2a76..10b1b89 100644
--- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/FormattedCellRenderer.java
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/FormattedCellRenderer.java
@@ -27,101 +27,92 @@ import javax.swing.JTable;
import javax.swing.table.DefaultTableCellRenderer;
/**
-* A cell renderer for dealing with formatted content.
-* Subclasses can specify formats or colors or styles for specific values
-* or locations in the table by overridding getFormatForContext(),
-* getForegroundForContext() and/or getBackgroundForContext().
-*
-* @author michael@mpowers.net
-* @version $Revision: 904 $
-* $Date: 2006-02-18 18:19:05 -0500 (Sat, 18 Feb 2006) $
-*/
-public class FormattedCellRenderer extends DefaultTableCellRenderer
-{
- protected Format currentFormat, defaultFormat;
- protected Color defaultForeground, defaultBackground;
- protected Font defaultFont;
-
-/**
-* Default constructor with no specified format.
-*/
- public FormattedCellRenderer()
- {
- this( (Format) null );
- }
-
-/**
-* Constructor specifying a format for renderered content.
-*/
- public FormattedCellRenderer( Format aFormat )
- {
- currentFormat = null;
- defaultFormat = aFormat;
- defaultForeground = super.getForeground();
- defaultBackground = super.getForeground();
- }
-
-/**
-* Returns the format currently in use to format cell content.
-* @return The Format that is currently being used.
-*/
- public Format getFormat()
- {
- return defaultFormat;
- }
-
-/**
-* Sets the format to be used to format cell content.
-*/
- public void setFormat( Format aFormat )
- {
- defaultFormat = aFormat;
- }
-
-/**
-* Overrides to retain the default foreground color,
-* much the same as the DefaultCellRenderer does.
-* We have to do this because DefaultCellRenderer's
-* ivars are private.
-*/
- public void setForeground(Color c) {
- super.setForeground(c);
- defaultForeground = c;
- }
-
-/**
-* Overrides to retain the default background color,
-* much the same as the DefaultCellRenderer does.
-* We have to do this because DefaultCellRenderer's
-* ivars are private.
-*/
- public void setBackground(Color c) {
- super.setBackground(c);
- defaultBackground = c;
- }
-
-/**
-* Overrides to retain the default font,
-* much the same as the DefaultCellRenderer does.
-* We have to do this because DefaultCellRenderer's
-* ivars are private.
-*/
- public void setFont(Font f) {
- super.setFont(f);
- defaultFont = f;
- }
-
-/**
-* Overridden to format the value with the appropriate Format. If the
-* value cannot be formatted with the Format, the superclass method is called.
-* @param value An Object to be formatted.
-*/
- protected void setValue(Object value)
- {
- if ( currentFormat != null )
- {
- try
- {
+ * A cell renderer for dealing with formatted content. Subclasses can specify
+ * formats or colors or styles for specific values or locations in the table by
+ * overridding getFormatForContext(), getForegroundForContext() and/or
+ * getBackgroundForContext().
+ *
+ * @author michael@mpowers.net
+ * @version $Revision: 904 $ $Date: 2006-02-18 18:19:05 -0500 (Sat, 18 Feb 2006)
+ * $
+ */
+public class FormattedCellRenderer extends DefaultTableCellRenderer {
+ protected Format currentFormat, defaultFormat;
+ protected Color defaultForeground, defaultBackground;
+ protected Font defaultFont;
+
+ /**
+ * Default constructor with no specified format.
+ */
+ public FormattedCellRenderer() {
+ this((Format) null);
+ }
+
+ /**
+ * Constructor specifying a format for renderered content.
+ */
+ public FormattedCellRenderer(Format aFormat) {
+ currentFormat = null;
+ defaultFormat = aFormat;
+ defaultForeground = super.getForeground();
+ defaultBackground = super.getForeground();
+ }
+
+ /**
+ * Returns the format currently in use to format cell content.
+ *
+ * @return The Format that is currently being used.
+ */
+ public Format getFormat() {
+ return defaultFormat;
+ }
+
+ /**
+ * Sets the format to be used to format cell content.
+ */
+ public void setFormat(Format aFormat) {
+ defaultFormat = aFormat;
+ }
+
+ /**
+ * Overrides to retain the default foreground color, much the same as the
+ * DefaultCellRenderer does. We have to do this because DefaultCellRenderer's
+ * ivars are private.
+ */
+ public void setForeground(Color c) {
+ super.setForeground(c);
+ defaultForeground = c;
+ }
+
+ /**
+ * Overrides to retain the default background color, much the same as the
+ * DefaultCellRenderer does. We have to do this because DefaultCellRenderer's
+ * ivars are private.
+ */
+ public void setBackground(Color c) {
+ super.setBackground(c);
+ defaultBackground = c;
+ }
+
+ /**
+ * Overrides to retain the default font, much the same as the
+ * DefaultCellRenderer does. We have to do this because DefaultCellRenderer's
+ * ivars are private.
+ */
+ public void setFont(Font f) {
+ super.setFont(f);
+ defaultFont = f;
+ }
+
+ /**
+ * Overridden to format the value with the appropriate Format. If the value
+ * cannot be formatted with the Format, the superclass method is called.
+ *
+ * @param value An Object to be formatted.
+ */
+ protected void setValue(Object value) {
+ if (currentFormat != null) {
+ try {
// if ( ( value instanceof Number ) && ( value.toString().indexOf( "E" ) != -1 ) )
// {
@@ -132,153 +123,140 @@ public class FormattedCellRenderer extends DefaultTableCellRenderer
// System.out.println( "FormattedCellRenderer.setValue: converted = '" + currentFormat.format( value ) + "'" );
// }
- // WORKAROUND: This works around what may be a rounding bug in DecimalFormat. (PR 256/297)
- currentFormat.format( ZERO );
-
- // DEBUG: code to test for weird one/zero problem (PR 256/297)
- String result = currentFormat.format( value );
-/* above workaround seems to be working
- if ( result.equals( "1" ) )
- {
- System.out.println( "FormattedCellRenderer.setValue: Could be the ONE/ZERO problem!" );
- System.out.println( "FormattedCellRenderer.setValue: format = '" + currentFormat.getClass() + "'" );
- System.out.println( "FormattedCellRenderer.setValue: original value = '" + value + "'" );
- System.out.println( "FormattedCellRenderer.setValue: result = '" + result + "'" );
- }
-*/
- setText( result );
-
+ // WORKAROUND: This works around what may be a rounding bug in DecimalFormat.
+ // (PR 256/297)
+ currentFormat.format(ZERO);
+
+ // DEBUG: code to test for weird one/zero problem (PR 256/297)
+ String result = currentFormat.format(value);
+ /*
+ * above workaround seems to be working if ( result.equals( "1" ) ) {
+ * System.out.println(
+ * "FormattedCellRenderer.setValue: Could be the ONE/ZERO problem!" );
+ * System.out.println( "FormattedCellRenderer.setValue: format = '" +
+ * currentFormat.getClass() + "'" ); System.out.println(
+ * "FormattedCellRenderer.setValue: original value = '" + value + "'" );
+ * System.out.println( "FormattedCellRenderer.setValue: result = '" + result +
+ * "'" ); }
+ */
+ setText(result);
// setText( currentFormat.format( value ) );
- return;
- }
- catch ( IllegalArgumentException exc )
- {
- // fall back on superclass implementation
- }
- }
- super.setValue( value );
- }
-
- // FIXME: remove this when possible
- private static Double ZERO = new Double( 0.0 );
-
-/**
-* Overridden to call context delegate methods.
-*/
- public Component getTableCellRendererComponent(JTable table, Object value,
- boolean isSelected, boolean hasFocus, int row, int column)
- {
- Format format;
-
- // allow for context-sensitve formatting
- format = getFormatForContext( table, value, isSelected, hasFocus, row, column );
- if ( format != null )
- {
- currentFormat = format;
- }
- else
- {
- currentFormat = defaultFormat;
- }
-
- Color color;
-
- // allow for context-sensitve foreground color
- color = getForegroundForContext( table, value, isSelected, hasFocus, row, column );
- if ( color != null )
- {
- super.setForeground( color );
- }
- else
- {
- super.setForeground( defaultForeground );
- }
-
- // allow for context-sensitve background color
- color = getBackgroundForContext( table, value, isSelected, hasFocus, row, column );
- if ( color != null )
- {
- super.setBackground( color );
- }
- else
- {
- super.setBackground( defaultBackground );
- }
-
- // have to call this here because super defaults to table's font
- Component result =
- super.getTableCellRendererComponent( table, value, isSelected, hasFocus, row, column );
- // NOTE: DefaultTableCellRenderer returns itself.
-
- // allow for context-sensitve font
- Font font = getFontForContext( table, value, isSelected, hasFocus, row, column );
- if ( font != null )
- {
- result.setFont( font );
- }
- else
- {
- result.setFont( defaultFont );
- }
-
- return result;
-
- }
-
-/**
-* Override this method to provide a specific format for the
-* specific cell to be rendered by this component. Any format
-* returned by this method will take precedence of the format
-* specified by setFormat(). <br><br>
-* This default implementation returns null.
-* @return A Format for this cell, or null to rely on the the
-* format specified by setFormat().
-*/
- public Format getFormatForContext(JTable table, Object value,
- boolean isSelected, boolean hasFocus, int row, int column)
- {
- return null;
- }
-
-/**
-* Override this method to provide a foreground color for the renderer.
-* Because the table specifies colors for selected cells,
-* these colors will only be used when renderering unselected cells. <br><br>
-* This default implementation returns null.
-* @return A Color for the foreground of the cell, or null to rely on
-* the table's default color scheme.
-*/
- public Color getForegroundForContext(JTable table, Object value,
- boolean isSelected, boolean hasFocus, int row, int column)
- {
- return null;
- }
-
-/**
-* Override this method to provide a background color for the renderer.
-* Because the table specifies colors for selected cells,
-* these colors will only be used when renderering unselected cells. <br><br>
-* This default implementation returns null.
-* @return A Color for the background of the cell, or null to rely on
-* the table's default color scheme.
-*/
- public Color getBackgroundForContext(JTable table, Object value,
- boolean isSelected, boolean hasFocus, int row, int column)
- {
- return null;
- }
-
-/**
-* Override this method to provide a font for the renderer.<br><br>
-* This default implementation returns null.
-* @return A Font for the cell, or null to rely on the table's default font.
-*/
- public Font getFontForContext(JTable table, Object value,
- boolean isSelected, boolean hasFocus, int row, int column)
- {
- return null;
- }
+ return;
+ } catch (IllegalArgumentException exc) {
+ // fall back on superclass implementation
+ }
+ }
+ super.setValue(value);
+ }
+
+ // FIXME: remove this when possible
+ private static Double ZERO = new Double(0.0);
+
+ /**
+ * Overridden to call context delegate methods.
+ */
+ public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
+ int row, int column) {
+ Format format;
+
+ // allow for context-sensitve formatting
+ format = getFormatForContext(table, value, isSelected, hasFocus, row, column);
+ if (format != null) {
+ currentFormat = format;
+ } else {
+ currentFormat = defaultFormat;
+ }
+
+ Color color;
+
+ // allow for context-sensitve foreground color
+ color = getForegroundForContext(table, value, isSelected, hasFocus, row, column);
+ if (color != null) {
+ super.setForeground(color);
+ } else {
+ super.setForeground(defaultForeground);
+ }
+
+ // allow for context-sensitve background color
+ color = getBackgroundForContext(table, value, isSelected, hasFocus, row, column);
+ if (color != null) {
+ super.setBackground(color);
+ } else {
+ super.setBackground(defaultBackground);
+ }
+
+ // have to call this here because super defaults to table's font
+ Component result = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
+ // NOTE: DefaultTableCellRenderer returns itself.
+
+ // allow for context-sensitve font
+ Font font = getFontForContext(table, value, isSelected, hasFocus, row, column);
+ if (font != null) {
+ result.setFont(font);
+ } else {
+ result.setFont(defaultFont);
+ }
+
+ return result;
+
+ }
+
+ /**
+ * Override this method to provide a specific format for the specific cell to be
+ * rendered by this component. Any format returned by this method will take
+ * precedence of the format specified by setFormat(). <br>
+ * <br>
+ * This default implementation returns null.
+ *
+ * @return A Format for this cell, or null to rely on the the format specified
+ * by setFormat().
+ */
+ public Format getFormatForContext(JTable table, Object value, boolean isSelected, boolean hasFocus, int row,
+ int column) {
+ return null;
+ }
+
+ /**
+ * Override this method to provide a foreground color for the renderer. Because
+ * the table specifies colors for selected cells, these colors will only be used
+ * when renderering unselected cells. <br>
+ * <br>
+ * This default implementation returns null.
+ *
+ * @return A Color for the foreground of the cell, or null to rely on the
+ * table's default color scheme.
+ */
+ public Color getForegroundForContext(JTable table, Object value, boolean isSelected, boolean hasFocus, int row,
+ int column) {
+ return null;
+ }
+
+ /**
+ * Override this method to provide a background color for the renderer. Because
+ * the table specifies colors for selected cells, these colors will only be used
+ * when renderering unselected cells. <br>
+ * <br>
+ * This default implementation returns null.
+ *
+ * @return A Color for the background of the cell, or null to rely on the
+ * table's default color scheme.
+ */
+ public Color getBackgroundForContext(JTable table, Object value, boolean isSelected, boolean hasFocus, int row,
+ int column) {
+ return null;
+ }
+
+ /**
+ * Override this method to provide a font for the renderer.<br>
+ * <br>
+ * This default implementation returns null.
+ *
+ * @return A Font for the cell, or null to rely on the table's default font.
+ */
+ public Font getFontForContext(JTable table, Object value, boolean isSelected, boolean hasFocus, int row,
+ int column) {
+ return null;
+ }
}
-
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/IconCellRenderer.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/IconCellRenderer.java
index 8320d08..2d9531d 100644
--- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/IconCellRenderer.java
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/IconCellRenderer.java
@@ -54,792 +54,654 @@ import javax.swing.tree.TreeCellEditor;
import javax.swing.tree.TreeCellRenderer;
/**
-* A cell renderer that displays icons in addition to text,
-* and additionally is an editor in case you want to click
-* the icon to trigger some kind of action.
-* You probably should override both getStringForContext and
-* getIconForContext to achieve your desired results.
-* To receive mouse clicks, set the same instance of the
-* renderer as the editor for the same component.<br><br>
-*
-* One notable addition is that this class is an action event
-* broadcaster. ActionEvents are broadcast when the mouse is
-* clicked on the button with an action event containing a
-* user-configurable string that defaults to CLICKED. <br><br>
-*
-* The renderer itself can be used as a JComponent if
-* you need something like a JLabel that allows you to click
-* on the icon. You will want to call setIcon and setText
-* to configure the component since the renderer method would
-* not be called. (If you add an instance of the renderer
-* to a container, you cannnot use the same instance as an
-* editor in a table, tree, or list.)
-*
-* @author michael@mpowers.net
-* @version $Revision: 904 $
-* $Date: 2006-02-18 18:19:05 -0500 (Sat, 18 Feb 2006) $
-*/
-public class IconCellRenderer extends JPanel
- implements TableCellRenderer, TableCellEditor,
- TreeCellRenderer, TreeCellEditor, ListCellRenderer,
- Runnable, ActionListener, MouseListener
-{
+ * A cell renderer that displays icons in addition to text, and additionally is
+ * an editor in case you want to click the icon to trigger some kind of action.
+ * You probably should override both getStringForContext and getIconForContext
+ * to achieve your desired results. To receive mouse clicks, set the same
+ * instance of the renderer as the editor for the same component.<br>
+ * <br>
+ *
+ * One notable addition is that this class is an action event broadcaster.
+ * ActionEvents are broadcast when the mouse is clicked on the button with an
+ * action event containing a user-configurable string that defaults to CLICKED.
+ * <br>
+ * <br>
+ *
+ * The renderer itself can be used as a JComponent if you need something like a
+ * JLabel that allows you to click on the icon. You will want to call setIcon
+ * and setText to configure the component since the renderer method would not be
+ * called. (If you add an instance of the renderer to a container, you cannnot
+ * use the same instance as an editor in a table, tree, or list.)
+ *
+ * @author michael@mpowers.net
+ * @version $Revision: 904 $ $Date: 2006-02-18 18:19:05 -0500 (Sat, 18 Feb 2006)
+ * $
+ */
+public class IconCellRenderer extends JPanel implements TableCellRenderer, TableCellEditor, TreeCellRenderer,
+ TreeCellEditor, ListCellRenderer, Runnable, ActionListener, MouseListener {
public static final String CLICKED = "CLICKED";
-
+
/**
- * The panel that is re-used to render everything.
- * This is returned by getRendererForContext.
- */
- protected JPanel rendererPanel;
+ * The panel that is re-used to render everything. This is returned by
+ * getRendererForContext.
+ */
+ protected JPanel rendererPanel;
protected JLabel rendererLabel;
- protected JButton rendererButton;
+ protected JButton rendererButton;
/**
- * The panel that is used to receive mouse clicks.
- * It must be a different component from rendererPanel.
- * This is returned by getEditorForContext.
- */
- protected JPanel editorPanel;
+ * The panel that is used to receive mouse clicks. It must be a different
+ * component from rendererPanel. This is returned by getEditorForContext.
+ */
+ protected JPanel editorPanel;
protected JLabel editorLabel;
- protected JButton editorButton;
-
+ protected JButton editorButton;
+
private Object lastKnownValue;
- private JComponent lastKnownComponent;
-
+ private JComponent lastKnownComponent;
+
// do as DefaultTableCellRenderer does
private Border noFocusBorder;
private Border treeFocusBorder;
private Color unselectedForeground;
private Color unselectedBackground;
-
+
private Vector actionListeners;
private String actionCommand;
private Vector cellEditorListeners;
-
- private boolean editable;
- private boolean clickable;
-
- /**
- * Default constructor.
- */
- public IconCellRenderer()
- {
- editable = true;
- clickable = true;
-
- noFocusBorder = new EmptyBorder(1, 1, 1, 1);
- treeFocusBorder = new LineBorder(
- UIManager.getColor("Tree.selectionBorderColor") );
- setActionCommand( CLICKED );
-
- rendererPanel = new JPanel();
- rendererPanel.setLayout( new GridBagLayout() );
-
- editorPanel = this;
- editorPanel.setLayout( new GridBagLayout() );
-
- // set up constraints
- GridBagConstraints imageConstraints = new GridBagConstraints();
- imageConstraints.gridx = 0;
- GridBagConstraints labelConstraints = new GridBagConstraints();
- labelConstraints.fill = GridBagConstraints.HORIZONTAL;
- labelConstraints.gridx = 1;
- labelConstraints.weightx = 1.0;
- labelConstraints.ipadx = 1;
- labelConstraints.insets = new Insets( 0, 1, 0, 0 ); // sweat the pixel
-
- // make the editor panel go away when not in use
- // and pass through all mouse events to container
-
- //this is not very useful since editorLabel and editorButton
- //get all of the events
- editorPanel.addMouseListener( this );
-
+
+ private boolean editable;
+ private boolean clickable;
+
+ /**
+ * Default constructor.
+ */
+ public IconCellRenderer() {
+ editable = true;
+ clickable = true;
+
+ noFocusBorder = new EmptyBorder(1, 1, 1, 1);
+ treeFocusBorder = new LineBorder(UIManager.getColor("Tree.selectionBorderColor"));
+ setActionCommand(CLICKED);
+
+ rendererPanel = new JPanel();
+ rendererPanel.setLayout(new GridBagLayout());
+
+ editorPanel = this;
+ editorPanel.setLayout(new GridBagLayout());
+
+ // set up constraints
+ GridBagConstraints imageConstraints = new GridBagConstraints();
+ imageConstraints.gridx = 0;
+ GridBagConstraints labelConstraints = new GridBagConstraints();
+ labelConstraints.fill = GridBagConstraints.HORIZONTAL;
+ labelConstraints.gridx = 1;
+ labelConstraints.weightx = 1.0;
+ labelConstraints.ipadx = 1;
+ labelConstraints.insets = new Insets(0, 1, 0, 0); // sweat the pixel
+
+ // make the editor panel go away when not in use
+ // and pass through all mouse events to container
+
+ // this is not very useful since editorLabel and editorButton
+ // get all of the events
+ editorPanel.addMouseListener(this);
+
rendererLabel = new JLabel();
- rendererLabel.setOpaque( false );
- rendererPanel.add( rendererLabel, labelConstraints );
+ rendererLabel.setOpaque(false);
+ rendererPanel.add(rendererLabel, labelConstraints);
editorLabel = new JLabel();
- editorLabel.setText( "" ); // default state
- editorLabel.setOpaque( false );
- editorPanel.add( editorLabel, labelConstraints );
+ editorLabel.setText(""); // default state
+ editorLabel.setOpaque(false);
+ editorPanel.add(editorLabel, labelConstraints);
unselectedForeground = rendererLabel.getForeground();
unselectedBackground = rendererLabel.getBackground();
-
- rendererButton = new JButton();
- rendererButton.setBorder( null );
- rendererButton.setBorderPainted( false );
- rendererButton.setContentAreaFilled( false );
- rendererButton.setFocusPainted( false );
- rendererButton.setMargin( new Insets( 0, 0, 0, 0 ) );
- rendererPanel.add( rendererButton, imageConstraints );
-
- editorButton = new JButton();
- editorButton.setEnabled( clickable ); // default state
- editorButton.setIcon( null ); // default state
- editorButton.setBorder( null );
- editorButton.setBorderPainted( false );
- editorButton.setContentAreaFilled( false );
- editorButton.setFocusPainted( false );
- editorButton.setMargin( new Insets( 0, 0, 0, 0 ) );
- editorPanel.add( editorButton, imageConstraints );
-
- editorButton.addActionListener( this );
-
- //add these in order to dispatch the MouseEvents
- //to the lastKnownComponent, and proper management of
- //DnD operations
- editorLabel.addMouseListener( this );
- editorButton.addMouseListener( this );
- }
-
-/**
-* Returns the text string currently displayed in the editor component.
-*/
- public String getText()
- {
- return editorLabel.getText();
- }
-
-/**
-* Sets the text string displayed in the editor component.
-* Default is an empty string.
-*/
- public void setText( String aString )
- {
- editorLabel.setText( aString );
- }
-/**
-* Returns the icon currently displayed in the editor component.
-*/
- public Icon getIcon()
- {
- return editorButton.getIcon();
- }
-
-/**
-* Sets the icon currently displayed in the editor component.
-* Default is null.
-*/
- public void setIcon( Icon anIcon )
- {
- editorButton.setIcon( anIcon );
- if ( !isClickable() )
- {
- editorButton.setDisabledIcon( anIcon );
- }
- }
+ rendererButton = new JButton();
+ rendererButton.setBorder(null);
+ rendererButton.setBorderPainted(false);
+ rendererButton.setContentAreaFilled(false);
+ rendererButton.setFocusPainted(false);
+ rendererButton.setMargin(new Insets(0, 0, 0, 0));
+ rendererPanel.add(rendererButton, imageConstraints);
+
+ editorButton = new JButton();
+ editorButton.setEnabled(clickable); // default state
+ editorButton.setIcon(null); // default state
+ editorButton.setBorder(null);
+ editorButton.setBorderPainted(false);
+ editorButton.setContentAreaFilled(false);
+ editorButton.setFocusPainted(false);
+ editorButton.setMargin(new Insets(0, 0, 0, 0));
+ editorPanel.add(editorButton, imageConstraints);
+
+ editorButton.addActionListener(this);
+
+ // add these in order to dispatch the MouseEvents
+ // to the lastKnownComponent, and proper management of
+ // DnD operations
+ editorLabel.addMouseListener(this);
+ editorButton.addMouseListener(this);
+ }
-/**
-* Returns whether the editor component's label text is editable.
-*/
- public boolean isEditable()
- {
- return editable;
- }
-
-/**
-* Sets whether the editor component's label text is editable.
-* Default is true. Editable text is not yet implemented.
-*/
- public void setEditable( boolean isEditable )
- {
- editable = isEditable;
- }
+ /**
+ * Returns the text string currently displayed in the editor component.
+ */
+ public String getText() {
+ return editorLabel.getText();
+ }
-/**
-* Returns whether the editor component's icon is clickable.
-*/
- public boolean isClickable()
- {
- return clickable;
- }
-
-/**
-* Sets whether the editor component's icon is clickable.
-* Default is true.
-*/
- public void setClickable( boolean isClickable )
- {
- clickable = isClickable;
- editorButton.setEnabled( clickable );
- }
+ /**
+ * Sets the text string displayed in the editor component. Default is an empty
+ * string.
+ */
+ public void setText(String aString) {
+ editorLabel.setText(aString);
+ }
-/**
-* Returns the component from getRendererForContext.
-*/
- public Component getListCellRendererComponent(JList list,
- Object value,
- int index,
- boolean isSelected,
- boolean cellHasFocus)
- {
- lastKnownComponent = list;
- return getRendererForContext(
- list, value, index, 0, isSelected, cellHasFocus, false, true );
- }
+ /**
+ * Returns the icon currently displayed in the editor component.
+ */
+ public Icon getIcon() {
+ return editorButton.getIcon();
+ }
-/**
-* Returns the component from getRendererForContext.
-*/
- public Component getTableCellRendererComponent(JTable table, Object value,
- boolean isSelected, boolean hasFocus, int row, int column)
- {
- lastKnownComponent = table;
- return getRendererForContext(
- table, value, row, column, isSelected, hasFocus, false, true );
- }
+ /**
+ * Sets the icon currently displayed in the editor component. Default is null.
+ */
+ public void setIcon(Icon anIcon) {
+ editorButton.setIcon(anIcon);
+ if (!isClickable()) {
+ editorButton.setDisabledIcon(anIcon);
+ }
+ }
-/**
-* Returns the component from getRendererForContext.
-*/
- public Component getTreeCellRendererComponent(JTree tree,
- Object value,
- boolean selected,
- boolean expanded,
- boolean leaf,
- int row,
- boolean hasFocus)
- {
- lastKnownComponent = tree;
- return getRendererForContext(
- tree, value, row, 0, selected, hasFocus, expanded, leaf );
- }
-
-/**
-* Returns getEditorForContext with the same parameters with hasFocus true.
-*/
- public Component getTableCellEditorComponent(JTable table,
- Object value, boolean isSelected, int row, int column)
- {
+ /**
+ * Returns whether the editor component's label text is editable.
+ */
+ public boolean isEditable() {
+ return editable;
+ }
+
+ /**
+ * Sets whether the editor component's label text is editable. Default is true.
+ * Editable text is not yet implemented.
+ */
+ public void setEditable(boolean isEditable) {
+ editable = isEditable;
+ }
+
+ /**
+ * Returns whether the editor component's icon is clickable.
+ */
+ public boolean isClickable() {
+ return clickable;
+ }
+
+ /**
+ * Sets whether the editor component's icon is clickable. Default is true.
+ */
+ public void setClickable(boolean isClickable) {
+ clickable = isClickable;
+ editorButton.setEnabled(clickable);
+ }
+
+ /**
+ * Returns the component from getRendererForContext.
+ */
+ public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected,
+ boolean cellHasFocus) {
+ lastKnownComponent = list;
+ return getRendererForContext(list, value, index, 0, isSelected, cellHasFocus, false, true);
+ }
+
+ /**
+ * Returns the component from getRendererForContext.
+ */
+ public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
+ int row, int column) {
+ lastKnownComponent = table;
+ return getRendererForContext(table, value, row, column, isSelected, hasFocus, false, true);
+ }
+
+ /**
+ * Returns the component from getRendererForContext.
+ */
+ public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded,
+ boolean leaf, int row, boolean hasFocus) {
+ lastKnownComponent = tree;
+ return getRendererForContext(tree, value, row, 0, selected, hasFocus, expanded, leaf);
+ }
+
+ /**
+ * Returns getEditorForContext with the same parameters with hasFocus true.
+ */
+ public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
lastKnownValue = value;
- lastKnownComponent = table;
- return getEditorForContext(
- table, value, row, column, isSelected, true, false, true );
+ lastKnownComponent = table;
+ return getEditorForContext(table, value, row, column, isSelected, true, false, true);
}
-/**
-* Returns the component from getEditorForContext with hasFocus true.
-*/
- public Component getTreeCellEditorComponent(JTree tree,
- Object value,
- boolean isSelected,
- boolean expanded,
- boolean leaf,
- int row)
- {
-
-
- lastKnownValue = value;
- lastKnownComponent = tree;
-
- return getEditorForContext(
- tree, value, row, 0, isSelected, true, expanded, leaf );
- }
-
-/**
-* This default implementation returns a JPanel that is configured by
-* calling configureComponentForContext.
-* @return An component that is used to render content.
-*/
- public Component getRendererForContext(
- JComponent container, Object value,
- int row, int column,
- boolean isSelected, boolean hasFocus,
- boolean isExpanded, boolean isLeaf )
- {
-
-
- configureComponentForContext( rendererPanel, rendererButton, rendererLabel,
- container, value, row, column,
- isSelected, hasFocus, isExpanded, isLeaf );
+ /**
+ * Returns the component from getEditorForContext with hasFocus true.
+ */
+ public Component getTreeCellEditorComponent(JTree tree, Object value, boolean isSelected, boolean expanded,
+ boolean leaf, int row) {
+
+ lastKnownValue = value;
+ lastKnownComponent = tree;
+
+ return getEditorForContext(tree, value, row, 0, isSelected, true, expanded, leaf);
+ }
+
+ /**
+ * This default implementation returns a JPanel that is configured by calling
+ * configureComponentForContext.
+ *
+ * @return An component that is used to render content.
+ */
+ public Component getRendererForContext(JComponent container, Object value, int row, int column, boolean isSelected,
+ boolean hasFocus, boolean isExpanded, boolean isLeaf) {
+
+ configureComponentForContext(rendererPanel, rendererButton, rendererLabel, container, value, row, column,
+ isSelected, hasFocus, isExpanded, isLeaf);
return rendererPanel;
- }
+ }
-/**
-* This method returns a separate component that should be visually
-* identical to the renderer component. We can't simply reuse the
-* renderer component because the renderer is still used to paint
-* the table while the editor component is displayed. Clicks are
-* received on this component.
-* This default implementation returns a JPanel that is configured by
-* calling configureComponentForContext.
-* @return A component used to receive clicks on the cell.
-*/
- public Component getEditorForContext(
- JComponent container, Object value,
- int row, int column,
- boolean isSelected, boolean hasFocus,
- boolean isExpanded, boolean isLeaf )
- {
- configureComponentForContext( editorPanel, editorButton, editorLabel,
- container, value, row, column,
- true, hasFocus, isExpanded, isLeaf ); // editor should always be selected
+ /**
+ * This method returns a separate component that should be visually identical to
+ * the renderer component. We can't simply reuse the renderer component because
+ * the renderer is still used to paint the table while the editor component is
+ * displayed. Clicks are received on this component. This default implementation
+ * returns a JPanel that is configured by calling configureComponentForContext.
+ *
+ * @return A component used to receive clicks on the cell.
+ */
+ public Component getEditorForContext(JComponent container, Object value, int row, int column, boolean isSelected,
+ boolean hasFocus, boolean isExpanded, boolean isLeaf) {
+ configureComponentForContext(editorPanel, editorButton, editorLabel, container, value, row, column, true,
+ hasFocus, isExpanded, isLeaf); // editor should always be selected
return editorPanel;
- }
+ }
-/**
-* Called to configure components
-*/
- protected void configureComponentForContext(
- JPanel component, JButton iconButton, JLabel label,
- JComponent container, Object value,
- int row, int column,
- boolean isSelected, boolean hasFocus,
- boolean isExpanded, boolean isLeaf )
- {
- if (hasFocus)
- {
- if ( container instanceof JTable )
- {
- component.setBorder(
- UIManager.getBorder("Table.focusCellHighlightBorder") );
- }
- else
- {
- component.setBorder( noFocusBorder );
- }
-
- if ( container instanceof JTree ) // was: (false)
- {
- label.setBorder( treeFocusBorder );
- }
- else
- {
- label.setBorder( noFocusBorder );
- }
- }
- else
- {
- label.setBorder(noFocusBorder);
- component.setBorder(noFocusBorder);
- }
-
- if (isSelected)
- {
- if ( container instanceof JTree )
- {
- label.setOpaque( true );
- label.setForeground(UIManager.getColor("Tree.selectionForeground"));
- label.setBackground(UIManager.getColor("Tree.selectionBackground"));
- component.setBackground(container.getBackground());
- }
- else if ( container instanceof JTable )
- {
- label.setOpaque( false );
- label.setForeground( ((JTable)container).getSelectionForeground() );
- component.setBackground(((JTable)container).getSelectionBackground());
- }
- else
- {
- label.setOpaque( false );
- label.setForeground(UIManager.getColor("Table.selectionForeground"));
- component.setBackground(UIManager.getColor("Table.selectionBackground"));
- }
- }
- else
- {
- label.setOpaque( false );
- label.setForeground(container.getForeground());
- component.setBackground(container.getBackground());
- }
-
- label.setFont(container.getFont());
-
- Icon icon = getIconForContext(
- container, value, row, column, isSelected, hasFocus, isExpanded, isLeaf );
- iconButton.setIcon( icon );
- if ( !isClickable() )
- {
- iconButton.setDisabledIcon( icon );
- }
-
- String text = getStringForContext(
- container, value, row, column, isSelected, hasFocus, isExpanded, isLeaf );
-
- if ( ( text == null ) || ( "".equals( text ) ) )
- {
- if ( ! label.getText().equals( "" ) )
- label.setText( "" );
+ /**
+ * Called to configure components
+ */
+ protected void configureComponentForContext(JPanel component, JButton iconButton, JLabel label,
+ JComponent container, Object value, int row, int column, boolean isSelected, boolean hasFocus,
+ boolean isExpanded, boolean isLeaf) {
+ if (hasFocus) {
+ if (container instanceof JTable) {
+ component.setBorder(UIManager.getBorder("Table.focusCellHighlightBorder"));
+ } else {
+ component.setBorder(noFocusBorder);
+ }
+
+ if (container instanceof JTree) // was: (false)
+ {
+ label.setBorder(treeFocusBorder);
+ } else {
+ label.setBorder(noFocusBorder);
+ }
+ } else {
+ label.setBorder(noFocusBorder);
+ component.setBorder(noFocusBorder);
}
- else
- {
- if ( ! label.getText().equals( text ) )
- label.setText( text );
+
+ if (isSelected) {
+ if (container instanceof JTree) {
+ label.setOpaque(true);
+ label.setForeground(UIManager.getColor("Tree.selectionForeground"));
+ label.setBackground(UIManager.getColor("Tree.selectionBackground"));
+ component.setBackground(container.getBackground());
+ } else if (container instanceof JTable) {
+ label.setOpaque(false);
+ label.setForeground(((JTable) container).getSelectionForeground());
+ component.setBackground(((JTable) container).getSelectionBackground());
+ } else {
+ label.setOpaque(false);
+ label.setForeground(UIManager.getColor("Table.selectionForeground"));
+ component.setBackground(UIManager.getColor("Table.selectionBackground"));
+ }
+ } else {
+ label.setOpaque(false);
+ label.setForeground(container.getForeground());
+ component.setBackground(container.getBackground());
+ }
+
+ label.setFont(container.getFont());
+
+ Icon icon = getIconForContext(container, value, row, column, isSelected, hasFocus, isExpanded, isLeaf);
+ iconButton.setIcon(icon);
+ if (!isClickable()) {
+ iconButton.setDisabledIcon(icon);
+ }
+
+ String text = getStringForContext(container, value, row, column, isSelected, hasFocus, isExpanded, isLeaf);
+
+ if ((text == null) || ("".equals(text))) {
+ if (!label.getText().equals(""))
+ label.setText("");
+ } else {
+ if (!label.getText().equals(text))
+ label.setText(text);
}
}
-
-/**
-* Override this method to provide an icon for the renderer.
-* This default implementation returns null.
-* @return An icon to be displayed in the cell, or null to omit the
-* icon from the cell.
-*/
- public Icon getIconForContext(
- JComponent container, Object value,
- int row, int column,
- boolean isSelected, boolean hasFocus,
- boolean isExpanded, boolean isLeaf )
- {
- return null;
- }
-/**
-* Override this method to provide a string for the renderer.
-* This default implementation returns toString on the value parameter,
-* or null if the value is null.
-* @return A string to be displayed in the cell.
-*/
- public String getStringForContext(
- JComponent container, Object value,
- int row, int column,
- boolean isSelected, boolean hasFocus,
- boolean isExpanded, boolean isLeaf )
- {
- if ( value == null ) return null;
- return value.toString();
- }
-
- /**
- * Adds the specified listener to the list of listeners
- * to be notified when the button receives a click.
- */
- public void addActionListener( ActionListener aListener )
- {
- if ( actionListeners == null )
- {
- actionListeners = new Vector( 2 );
+ /**
+ * Override this method to provide an icon for the renderer. This default
+ * implementation returns null.
+ *
+ * @return An icon to be displayed in the cell, or null to omit the icon from
+ * the cell.
+ */
+ public Icon getIconForContext(JComponent container, Object value, int row, int column, boolean isSelected,
+ boolean hasFocus, boolean isExpanded, boolean isLeaf) {
+ return null;
+ }
+
+ /**
+ * Override this method to provide a string for the renderer. This default
+ * implementation returns toString on the value parameter, or null if the value
+ * is null.
+ *
+ * @return A string to be displayed in the cell.
+ */
+ public String getStringForContext(JComponent container, Object value, int row, int column, boolean isSelected,
+ boolean hasFocus, boolean isExpanded, boolean isLeaf) {
+ if (value == null)
+ return null;
+ return value.toString();
+ }
+
+ /**
+ * Adds the specified listener to the list of listeners to be notified when the
+ * button receives a click.
+ */
+ public void addActionListener(ActionListener aListener) {
+ if (actionListeners == null) {
+ actionListeners = new Vector(2);
}
- actionListeners.add( aListener );
+ actionListeners.add(aListener);
}
-
+
/**
- * Removes the specified listener from the list of listeners
- * to be notified when the button receives a click.
- */
- public void removeActionListener( ActionListener aListener )
- {
- actionListeners.remove( aListener );
+ * Removes the specified listener from the list of listeners to be notified when
+ * the button receives a click.
+ */
+ public void removeActionListener(ActionListener aListener) {
+ actionListeners.remove(aListener);
}
-
+
/**
- * Broadcasts the specified action event to all listeners.
- */
- protected void fireActionEvent( ActionEvent anActionEvent )
- {
- if ( actionListeners == null ) return;
+ * Broadcasts the specified action event to all listeners.
+ */
+ protected void fireActionEvent(ActionEvent anActionEvent) {
+ if (actionListeners == null)
+ return;
// vector's enumeration is not fail-fast
Enumeration e = actionListeners.elements();
- while ( e.hasMoreElements() )
- {
- ((ActionListener)e.nextElement()).actionPerformed( anActionEvent );
+ while (e.hasMoreElements()) {
+ ((ActionListener) e.nextElement()).actionPerformed(anActionEvent);
}
}
-
+
/**
- * Returns the action command broadcast when this icon
- * receives a click. Defaults to CLICKED.
- */
- public String getActionCommand()
- {
+ * Returns the action command broadcast when this icon receives a click.
+ * Defaults to CLICKED.
+ */
+ public String getActionCommand() {
return actionCommand;
}
/**
- * Sets the action command broadcast when this table
- * receives a double click.
- */
- public void setActionCommand( String anActionCommand )
- {
- actionCommand = anActionCommand;
+ * Sets the action command broadcast when this table receives a double click.
+ */
+ public void setActionCommand(String anActionCommand) {
+ actionCommand = anActionCommand;
}
-
+
// interface CellEditor
/**
- * Returns lastKnownValue, although this should not be called.
- */
- public Object getCellEditorValue()
- {
+ * Returns lastKnownValue, although this should not be called.
+ */
+ public Object getCellEditorValue() {
return lastKnownValue;
}
-
+
/**
- * Returns true.
- */
- public boolean isCellEditable(EventObject anEvent)
- {
- return true;
+ * Returns true.
+ */
+ public boolean isCellEditable(EventObject anEvent) {
+ return true;
}
-
+
/**
- * Returns true.
- */
- public boolean shouldSelectCell(EventObject anEvent)
- {
+ * Returns true.
+ */
+ public boolean shouldSelectCell(EventObject anEvent) {
return true;
}
-
+
/**
- * Fires an editing stopped event and returns true.
- */
- public boolean stopCellEditing()
- {
- ChangeEvent event = new ChangeEvent( this );
- if ( cellEditorListeners != null )
- {
+ * Fires an editing stopped event and returns true.
+ */
+ public boolean stopCellEditing() {
+ ChangeEvent event = new ChangeEvent(this);
+ if (cellEditorListeners != null) {
// vector's enumeration is not fail-fast
Enumeration e = cellEditorListeners.elements();
- while ( e.hasMoreElements() )
- {
- // broadcast editing cancelled since no value is edited
- ((CellEditorListener)e.nextElement()).editingCanceled( event );
+ while (e.hasMoreElements()) {
+ // broadcast editing cancelled since no value is edited
+ ((CellEditorListener) e.nextElement()).editingCanceled(event);
}
}
- lastKnownComponent = null;
+ lastKnownComponent = null;
return true;
}
-
- /**
- * Fires an editing cancelled event and returns true.
- */
- public void cancelCellEditing()
- {
- //HACK: cancelCellEditing() causes for the dragGesture
- //to be NOT recognized AT ALL since on the next MOUSE_PRESSED
- //the cell editor first needs to startEditing() [if in the tree
- //the CellEditorListener is a BasicTreeUI class]
- //(before the drag gesture event can be recognized).
- //Also the lastKnownComponent should not be set to null,
- //none of the mouse events won't dispathced to the lastKnownComponent
- //in that case.
-
- //Not calling it at all does seem to fix it, but what are the
- //consequences???
- //Trying to workaround this might solve it, but it introduces having
- //an extra listener (a MouseMotionListnener), which might be wasteful
- //(i.e. only if a Mouse_dragged event has been initiated, but DragGesture
- //hasn't been recognized, postpone calling this till finish the DnD event)
- //But what if do DnD and not exited ??? The mouseExited() is not called
- //anyway until the DnD event is done.
-
- ChangeEvent event = new ChangeEvent( this );
- if ( cellEditorListeners == null ) return;
+
+ /**
+ * Fires an editing cancelled event and returns true.
+ */
+ public void cancelCellEditing() {
+ // HACK: cancelCellEditing() causes for the dragGesture
+ // to be NOT recognized AT ALL since on the next MOUSE_PRESSED
+ // the cell editor first needs to startEditing() [if in the tree
+ // the CellEditorListener is a BasicTreeUI class]
+ // (before the drag gesture event can be recognized).
+ // Also the lastKnownComponent should not be set to null,
+ // none of the mouse events won't dispathced to the lastKnownComponent
+ // in that case.
+
+ // Not calling it at all does seem to fix it, but what are the
+ // consequences???
+ // Trying to workaround this might solve it, but it introduces having
+ // an extra listener (a MouseMotionListnener), which might be wasteful
+ // (i.e. only if a Mouse_dragged event has been initiated, but DragGesture
+ // hasn't been recognized, postpone calling this till finish the DnD event)
+ // But what if do DnD and not exited ??? The mouseExited() is not called
+ // anyway until the DnD event is done.
+
+ ChangeEvent event = new ChangeEvent(this);
+ if (cellEditorListeners == null)
+ return;
// vector's enumeration is not fail-fast
Enumeration e = cellEditorListeners.elements();
-
- while ( e.hasMoreElements() )
- {
- ((CellEditorListener)e.nextElement()).editingCanceled( event );
- }
-
- //DO not nullify this
- lastKnownComponent = null;
- }
-
- /**
- * Adds the specified listener to the list of listeners
- * to be notified when the table receives a double click.
- */
- public void addCellEditorListener( CellEditorListener aListener )
- {
- if ( cellEditorListeners == null )
- {
- cellEditorListeners = new Vector( 2 );
+
+ while (e.hasMoreElements()) {
+ ((CellEditorListener) e.nextElement()).editingCanceled(event);
+ }
+
+ // DO not nullify this
+ lastKnownComponent = null;
+ }
+
+ /**
+ * Adds the specified listener to the list of listeners to be notified when the
+ * table receives a double click.
+ */
+ public void addCellEditorListener(CellEditorListener aListener) {
+ if (cellEditorListeners == null) {
+ cellEditorListeners = new Vector(2);
}
- cellEditorListeners.add( aListener );
+ cellEditorListeners.add(aListener);
}
-
+
/**
- * Removes the specified listener from the list of listeners
- * to be notified when the table receives a double click.
- */
- public void removeCellEditorListener( CellEditorListener aListener )
- {
- cellEditorListeners.remove( aListener );
+ * Removes the specified listener from the list of listeners to be notified when
+ * the table receives a double click.
+ */
+ public void removeCellEditorListener(CellEditorListener aListener) {
+ cellEditorListeners.remove(aListener);
}
-
+
// interface ActionListener
-
- /**
- * Puts ourself on the end of the event queue for
- * firing our action event to all listeners.
- */
- public void actionPerformed( ActionEvent evt )
- {
- //commented out in order NOT to set lastKnownComponent to null, since
- //if this object is inside a table or tree, relying on getCellEditorValue()
- //to return the currently edited object
- //cancelCellEditing();
-
- SwingUtilities.invokeLater( this );
- }
-
+
+ /**
+ * Puts ourself on the end of the event queue for firing our action event to all
+ * listeners.
+ */
+ public void actionPerformed(ActionEvent evt) {
+ // commented out in order NOT to set lastKnownComponent to null, since
+ // if this object is inside a table or tree, relying on getCellEditorValue()
+ // to return the currently edited object
+ // cancelCellEditing();
+
+ SwingUtilities.invokeLater(this);
+ }
+
// interface Runnable
-
+
/**
- * Fires the action event to all listeners.
- * This is triggered by a click on the icon.
- */
- public void run()
- {
- fireActionEvent( new ActionEvent( this, 0, getActionCommand() ) );
+ * Fires the action event to all listeners. This is triggered by a click on the
+ * icon.
+ */
+ public void run() {
+ fireActionEvent(new ActionEvent(this, 0, getActionCommand()));
}
// interface MouseListener
-
- /**
- * Passes through editor mouse clicks to last known component.
- * (left click only)
- */
- public void mouseClicked(MouseEvent e)
- {
- if(lastKnownComponent != null){
- Object source = e.getSource();
- if(source != null)
- {
- if(source == editorPanel)
- {
- lastKnownComponent.dispatchEvent(
- SwingUtilities.convertMouseEvent(
- editorPanel, e, lastKnownComponent ) );
-
- }
- else if(source == editorLabel)
- {
- lastKnownComponent.dispatchEvent(
- SwingUtilities.convertMouseEvent(
- editorLabel, e, lastKnownComponent ) );
- }
-
- else if(source == editorButton)
- {
- lastKnownComponent.dispatchEvent(
- SwingUtilities.convertMouseEvent(
- editorButton, e, lastKnownComponent ) );
- }
- }
- }
- }
-
- /**
- * Passes through editor right-mouse (popup trigger) mouse events to last known component.
- * Needed for possible displaying of popup menus on right click
- */
- public void mousePressed(MouseEvent e)
- {
- if ( e.isPopupTrigger() )
- {
- if(lastKnownComponent != null)
- {
- Object source = e.getSource();
- if(source != null)
- {
- if(source == editorPanel)
- {
- lastKnownComponent.dispatchEvent(
- SwingUtilities.convertMouseEvent(
- editorPanel, e, lastKnownComponent ) );
- }
- else if(source == editorLabel)
- {
- lastKnownComponent.dispatchEvent(
- SwingUtilities.convertMouseEvent(
- editorLabel, e, lastKnownComponent ) );
- }
-
- else if(source == editorButton)
- {
- lastKnownComponent.dispatchEvent(
- SwingUtilities.convertMouseEvent(
- editorButton, e, lastKnownComponent ) );
- }
- }
- }
- }
- }
-
- /**
- * Does nothing.
- */
- public void mouseReleased(MouseEvent e)
- {
- if ( e.isPopupTrigger() )
- {
- if(lastKnownComponent != null){
-
- Object source = e.getSource();
- if(source != null)
- {
- if(source == editorPanel)
- {
- lastKnownComponent.dispatchEvent(
- SwingUtilities.convertMouseEvent(
- editorPanel, e, lastKnownComponent ) );
- }
-
- else if(source == editorLabel)
- {
- lastKnownComponent.dispatchEvent(
- SwingUtilities.convertMouseEvent(
- editorLabel, e, lastKnownComponent ) );
- }
-
- else if(source == editorButton)
- {
- lastKnownComponent.dispatchEvent(
- SwingUtilities.convertMouseEvent(
- editorButton, e, lastKnownComponent ) );
- }
- }
- }
- }
- }
-
- /**
- * Does nothing.
- */
- public void mouseEntered(MouseEvent e)
- {
- }
-
- /**
- * Cancels cell editing.
- */
- public void mouseExited(MouseEvent e)
- {
- Object source = e.getSource();
- if(source != null && source instanceof JComponent){
- //need to convert the Point from the source's coordinate system to editorPanel's coordinate system.
- //(note that simple editorPanel.contains(e.getPoint()) fails if source is editorButton)
-
- Point convertedPoint = SwingUtilities.convertPoint((JComponent) source, e.getPoint(), editorPanel);
-
- //check if exited from editorButton, but still inside the editorPanel (works for editorLabel as well)
- if(!editorPanel.contains(convertedPoint)){
-
- //This was getting called before, but it interfers with the DnD operation
- cancelCellEditing();
- }
- }
- }
-
- /* This might be redundant
- public void cleanUp(){
-
- //since cancelCellEditing() was never called call it now
- cancelCellEditing();
- stopCellEditing();
-
- editorButton.removeActionListener( this );
- editorPanel.removeMouseListener( this );
- editorLabel.removeMouseListener( this );
- editorButton.removeMouseListener( this );
- lastKnownComponent = null;
- lastKnownValue = null;
- }
- */
+
+ /**
+ * Passes through editor mouse clicks to last known component. (left click only)
+ */
+ public void mouseClicked(MouseEvent e) {
+ if (lastKnownComponent != null) {
+ Object source = e.getSource();
+ if (source != null) {
+ if (source == editorPanel) {
+ lastKnownComponent
+ .dispatchEvent(SwingUtilities.convertMouseEvent(editorPanel, e, lastKnownComponent));
+
+ } else if (source == editorLabel) {
+ lastKnownComponent
+ .dispatchEvent(SwingUtilities.convertMouseEvent(editorLabel, e, lastKnownComponent));
+ }
+
+ else if (source == editorButton) {
+ lastKnownComponent
+ .dispatchEvent(SwingUtilities.convertMouseEvent(editorButton, e, lastKnownComponent));
+ }
+ }
+ }
+ }
+
+ /**
+ * Passes through editor right-mouse (popup trigger) mouse events to last known
+ * component. Needed for possible displaying of popup menus on right click
+ */
+ public void mousePressed(MouseEvent e) {
+ if (e.isPopupTrigger()) {
+ if (lastKnownComponent != null) {
+ Object source = e.getSource();
+ if (source != null) {
+ if (source == editorPanel) {
+ lastKnownComponent
+ .dispatchEvent(SwingUtilities.convertMouseEvent(editorPanel, e, lastKnownComponent));
+ } else if (source == editorLabel) {
+ lastKnownComponent
+ .dispatchEvent(SwingUtilities.convertMouseEvent(editorLabel, e, lastKnownComponent));
+ }
+
+ else if (source == editorButton) {
+ lastKnownComponent
+ .dispatchEvent(SwingUtilities.convertMouseEvent(editorButton, e, lastKnownComponent));
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Does nothing.
+ */
+ public void mouseReleased(MouseEvent e) {
+ if (e.isPopupTrigger()) {
+ if (lastKnownComponent != null) {
+
+ Object source = e.getSource();
+ if (source != null) {
+ if (source == editorPanel) {
+ lastKnownComponent
+ .dispatchEvent(SwingUtilities.convertMouseEvent(editorPanel, e, lastKnownComponent));
+ }
+
+ else if (source == editorLabel) {
+ lastKnownComponent
+ .dispatchEvent(SwingUtilities.convertMouseEvent(editorLabel, e, lastKnownComponent));
+ }
+
+ else if (source == editorButton) {
+ lastKnownComponent
+ .dispatchEvent(SwingUtilities.convertMouseEvent(editorButton, e, lastKnownComponent));
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Does nothing.
+ */
+ public void mouseEntered(MouseEvent e) {
+ }
+
+ /**
+ * Cancels cell editing.
+ */
+ public void mouseExited(MouseEvent e) {
+ Object source = e.getSource();
+ if (source != null && source instanceof JComponent) {
+ // need to convert the Point from the source's coordinate system to
+ // editorPanel's coordinate system.
+ // (note that simple editorPanel.contains(e.getPoint()) fails if source is
+ // editorButton)
+
+ Point convertedPoint = SwingUtilities.convertPoint((JComponent) source, e.getPoint(), editorPanel);
+
+ // check if exited from editorButton, but still inside the editorPanel (works
+ // for editorLabel as well)
+ if (!editorPanel.contains(convertedPoint)) {
+
+ // This was getting called before, but it interfers with the DnD operation
+ cancelCellEditing();
+ }
+ }
+ }
+
+ /*
+ * This might be redundant public void cleanUp(){
+ *
+ * //since cancelCellEditing() was never called call it now cancelCellEditing();
+ * stopCellEditing();
+ *
+ * editorButton.removeActionListener( this ); editorPanel.removeMouseListener(
+ * this ); editorLabel.removeMouseListener( this );
+ * editorButton.removeMouseListener( this ); lastKnownComponent = null;
+ * lastKnownValue = null; }
+ */
}
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/ImagePanel.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/ImagePanel.java
index cdaa218..4fa8e04 100644
--- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/ImagePanel.java
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/ImagePanel.java
@@ -26,79 +26,58 @@ import java.awt.image.ImageObserver;
import javax.swing.JPanel;
/**
-* A JPanel that renders an image, tiling as necessary to
-* fill the panel.
-* The preferred size of the panel is the size of the image
-* and will change until the image is fully loaded, so using
-* a media tracker is recommended.
-*
-* @author michael@mpowers.net
-* @version $Revision: 904 $
-*/
-public class ImagePanel extends JPanel implements ImageObserver
-{
+ * A JPanel that renders an image, tiling as necessary to fill the panel. The
+ * preferred size of the panel is the size of the image and will change until
+ * the image is fully loaded, so using a media tracker is recommended.
+ *
+ * @author michael@mpowers.net
+ * @version $Revision: 904 $
+ */
+public class ImagePanel extends JPanel implements ImageObserver {
protected Image image;
protected int imageWidth, imageHeight;
-
- public ImagePanel()
- {
- this( null );
- }
-
- public ImagePanel( Image anImage )
- {
- image = anImage;
- if ( anImage != null )
- {
- prepareImage( image, this );
+
+ public ImagePanel() {
+ this(null);
+ }
+
+ public ImagePanel(Image anImage) {
+ image = anImage;
+ if (anImage != null) {
+ prepareImage(image, this);
// these may return -1
- imageWidth = image.getWidth( this );
- imageHeight = image.getHeight( this );
- }
- else
- {
+ imageWidth = image.getWidth(this);
+ imageHeight = image.getHeight(this);
+ } else {
imageWidth = 0;
imageHeight = 0;
}
}
-
- protected void paintComponent(Graphics g)
- {
- if ( ( image != null ) && ( imageWidth > 0 ) && ( imageHeight > 0 ) )
- {
+
+ protected void paintComponent(Graphics g) {
+ if ((image != null) && (imageWidth > 0) && (imageHeight > 0)) {
int width = getWidth();
int height = getHeight();
-
- for ( int x = 0; x < width; x += imageWidth )
- {
- for ( int y = 0; y < height; y += imageHeight )
- {
- g.drawImage( image, x, y,
- imageWidth, imageHeight,
- getBackground(), this );
+
+ for (int x = 0; x < width; x += imageWidth) {
+ for (int y = 0; y < height; y += imageHeight) {
+ g.drawImage(image, x, y, imageWidth, imageHeight, getBackground(), this);
}
}
}
}
-
- public boolean imageUpdate(Image img,
- int infoflags,
- int x,
- int y,
- int width,
- int height)
- {
+
+ public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) {
imageWidth = width;
imageHeight = height;
- setPreferredSize( new Dimension( width, height ) );
- revalidate();
+ setPreferredSize(new Dimension(width, height));
+ revalidate();
repaint();
-
- if ( ( infoflags & ImageObserver.ALLBITS ) == ImageObserver.ALLBITS )
- {
- return false;
+
+ if ((infoflags & ImageObserver.ALLBITS) == ImageObserver.ALLBITS) {
+ return false;
}
return true;
}
-
+
}
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/InfoPanel.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/InfoPanel.java
index 55c1e36..a2c0182 100644
--- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/InfoPanel.java
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/InfoPanel.java
@@ -49,1645 +49,1521 @@ import javax.swing.JTextField;
import javax.swing.SwingConstants;
/**
-* InfoPanel uses labels and textfields (or any other component - see below)
-* to display a list of keys and values in a well-aligned and consistent manner,
-* conforming to alignment and pixel spacing in the java look and feel
-* <a href="http://java.sun.com/products/jlf/dg/higg.htm#55417">design guidelines</a>.
-* <BR><BR>
-*
-* Each key is displayed in a label to the left of the component that contains
-* the corresponding value. Each row is displayed starting at the top of the
-* component's available area. Each row's height is the maximum preferred
-* height of its components and the field itself gets as much of the width as
-* it can, dependent on the length of the longest label. <BR><BR>
-*
-* The values in the fields can be editable, and the
-* current value can be retrieved using the key - for this reason, unique keys
-* are recommended. <BR><BR>
-*
-* As a convenience, push buttons may be placed across the
-* bottom of the panel in a manner similar to ButtonPanel. <BR><BR>
-*
-* The panel forwards any ActionEvents generated by the components and
-* buttons on it to all registered listeners. <BR><BR>
-*
-* Optionally, any component can be used instead of a textfield.
-* However, <code>get/setValueForKey()</code> and <code>get/setEditable()</code>
-* may not work for those components. Use <code>getComponentForKey()</code> to
-* access them instead.
-*
-* @author michael@mpowers.net
-* @author $Author: cgruber $
-* @version $Revision: 904 $
-* $Date: 2006-02-18 18:19:05 -0500 (Sat, 18 Feb 2006) $
-*/
-public class InfoPanel extends JPanel implements ActionListener
-{
-/**
-* Special label for an empty pair - a label and component
-* that take up space but are hidden from view. This might
-* be useful for achieving certain layouts.
-*/
- public static final String HIDDEN = "(hidden)";
-
- /** Cache for the introspectComponent method */
- private static Map _method_cache =
- Collections.synchronizedMap( new HashMap(30) );
-
- protected Container listContainer = null;
- protected int hgap; // set in constructor
- protected int vgap; // set in constructor
- protected int margin; // set in constructor
- protected int columns; // set in constructor
- protected List fields = null;
- protected List labels = null;
- protected List fieldSpacers = null;
- protected ButtonPanel buttonPanel = null;
- protected boolean isEditable = true;
- protected String prefix;
- protected String postfix;
+ * InfoPanel uses labels and textfields (or any other component - see below) to
+ * display a list of keys and values in a well-aligned and consistent manner,
+ * conforming to alignment and pixel spacing in the java look and feel
+ * <a href="http://java.sun.com/products/jlf/dg/higg.htm#55417">design
+ * guidelines</a>. <BR>
+ * <BR>
+ *
+ * Each key is displayed in a label to the left of the component that contains
+ * the corresponding value. Each row is displayed starting at the top of the
+ * component's available area. Each row's height is the maximum preferred height
+ * of its components and the field itself gets as much of the width as it can,
+ * dependent on the length of the longest label. <BR>
+ * <BR>
+ *
+ * The values in the fields can be editable, and the current value can be
+ * retrieved using the key - for this reason, unique keys are recommended. <BR>
+ * <BR>
+ *
+ * As a convenience, push buttons may be placed across the bottom of the panel
+ * in a manner similar to ButtonPanel. <BR>
+ * <BR>
+ *
+ * The panel forwards any ActionEvents generated by the components and buttons
+ * on it to all registered listeners. <BR>
+ * <BR>
+ *
+ * Optionally, any component can be used instead of a textfield. However,
+ * <code>get/setValueForKey()</code> and <code>get/setEditable()</code> may not
+ * work for those components. Use <code>getComponentForKey()</code> to access
+ * them instead.
+ *
+ * @author michael@mpowers.net
+ * @author $Author: cgruber $
+ * @version $Revision: 904 $ $Date: 2006-02-18 18:19:05 -0500 (Sat, 18 Feb 2006)
+ * $
+ */
+public class InfoPanel extends JPanel implements ActionListener {
+ /**
+ * Special label for an empty pair - a label and component that take up space
+ * but are hidden from view. This might be useful for achieving certain layouts.
+ */
+ public static final String HIDDEN = "(hidden)";
+
+ /** Cache for the introspectComponent method */
+ private static Map _method_cache = Collections.synchronizedMap(new HashMap(30));
+
+ protected Container listContainer = null;
+ protected int hgap; // set in constructor
+ protected int vgap; // set in constructor
+ protected int margin; // set in constructor
+ protected int columns; // set in constructor
+ protected List fields = null;
+ protected List labels = null;
+ protected List fieldSpacers = null;
+ protected ButtonPanel buttonPanel = null;
+ protected boolean isEditable = true;
+ protected String prefix;
+ protected String postfix;
protected int labelAnchor;
protected int labelAlign;
// protected Component marginStrut = null;
- // for action multicasting
- protected ActionListener actionListener = null;
-
-/**
-* Constructs an empty InfoPanel.
-*/
- public InfoPanel()
- {
- hgap = 12; // per java l&f guidelines
- vgap = 6; // java l&f says 11
- columns = 1; // default columns
- margin = 0; // default margin: none
- prefix = ""; // default prefix: none
- postfix = ":"; // per java l&f guidelines
- fields = new ArrayList();
- labels = new ArrayList();
- labelAnchor = GridBagConstraints.NORTHWEST;
- // per java l&f guidelines (CENTER is nicer)
- labelAlign = SwingConstants.LEFT;
- // per java l&f guidelines
-
- doInitialLayout();
- }
+ // for action multicasting
+ protected ActionListener actionListener = null;
+
+ /**
+ * Constructs an empty InfoPanel.
+ */
+ public InfoPanel() {
+ hgap = 12; // per java l&f guidelines
+ vgap = 6; // java l&f says 11
+ columns = 1; // default columns
+ margin = 0; // default margin: none
+ prefix = ""; // default prefix: none
+ postfix = ":"; // per java l&f guidelines
+ fields = new ArrayList();
+ labels = new ArrayList();
+ labelAnchor = GridBagConstraints.NORTHWEST;
+ // per java l&f guidelines (CENTER is nicer)
+ labelAlign = SwingConstants.LEFT;
+ // per java l&f guidelines
+
+ doInitialLayout();
+ }
-/**
-* Constructs an InfoPanel with the specified labels
-* each paired with a blank textfield.
-* @param labelArray An Array containing the labels in the
-* order in which they should appear from top to bottom.
-* A null value produces an empty panel.
-*/
- public InfoPanel( String[] labelArray )
- {
- this();
- setLabels( labelArray );
- }
+ /**
+ * Constructs an InfoPanel with the specified labels each paired with a blank
+ * textfield.
+ *
+ * @param labelArray An Array containing the labels in the order in which they
+ * should appear from top to bottom. A null value produces an
+ * empty panel.
+ */
+ public InfoPanel(String[] labelArray) {
+ this();
+ setLabels(labelArray);
+ }
-/**
-* Creates a set of labels and empty textfields after first
-* clearing all existing components on the panel.
-* @param labelArray An Array containing the labels in the order
-* in which they should appear from top to bottom. A null
-* value will clear the panel.
-*/
- public void setLabels( String[] labelArray )
- {
- removeAll();
- if ( labelArray == null ) return; // null clears panel
- for ( int i = 0; i < labelArray.length; i++ )
- {
- addPair( labelArray[i], new JTextField() );
- }
- }
+ /**
+ * Creates a set of labels and empty textfields after first clearing all
+ * existing components on the panel.
+ *
+ * @param labelArray An Array containing the labels in the order in which they
+ * should appear from top to bottom. A null value will clear
+ * the panel.
+ */
+ public void setLabels(String[] labelArray) {
+ removeAll();
+ if (labelArray == null)
+ return; // null clears panel
+ for (int i = 0; i < labelArray.length; i++) {
+ addPair(labelArray[i], new JTextField());
+ }
+ }
-/**
-* Retrieves the labls for the components on the panel
-* in the order in which they are displayed from top WIDTH bottom.
-* These are the keys used to reference values or to reference
-* the components directly.
-* @return An Array of Strings containing the labels.
-*/
- public String[] getLabels()
- {
- int length = fields.size();
- String[] labelArray = new String[ length ];
- for ( int i = 0; i < length; i++ )
- {
- labelArray[i] = ((Component)fields.get(i)).getName();
- }
- return labelArray;
- }
-
-/**
-* Retrieves the constant used to anchor the labels in place.
-* The default value is GridBagConstraints.NORTHWEST.
-*/
- public int getLabelAnchor()
- {
+ /**
+ * Retrieves the labls for the components on the panel in the order in which
+ * they are displayed from top WIDTH bottom. These are the keys used to
+ * reference values or to reference the components directly.
+ *
+ * @return An Array of Strings containing the labels.
+ */
+ public String[] getLabels() {
+ int length = fields.size();
+ String[] labelArray = new String[length];
+ for (int i = 0; i < length; i++) {
+ labelArray[i] = ((Component) fields.get(i)).getName();
+ }
+ return labelArray;
+ }
+
+ /**
+ * Retrieves the constant used to anchor the labels in place. The default value
+ * is GridBagConstraints.NORTHWEST.
+ */
+ public int getLabelAnchor() {
return labelAnchor;
}
-/**
-* Sets the constant used to anchor the labels in place
-* and reflows the layout.
-* @param anAnchorConstant An anchor constant from
-* GridBagConstraints.
-*/
- public void setLabelAnchor( int anAnchorConstant )
- {
- labelAnchor = anAnchorConstant;
+ /**
+ * Sets the constant used to anchor the labels in place and reflows the layout.
+ *
+ * @param anAnchorConstant An anchor constant from GridBagConstraints.
+ */
+ public void setLabelAnchor(int anAnchorConstant) {
+ labelAnchor = anAnchorConstant;
updateLabels();
}
-/**
-* Retrieves the constant used to align the labels in place.
-* The default value is GridBagConstraints.CENTER.
-*/
- public int getLabelAlignment()
- {
+ /**
+ * Retrieves the constant used to align the labels in place. The default value
+ * is GridBagConstraints.CENTER.
+ */
+ public int getLabelAlignment() {
return labelAlign;
}
-/**
-* Sets the constant used to align the labels in place
-* and reflows the layout.
-* @param anAlignmentConstant LEFT, CENTER, or RIGHT constants
-* from SwingUtilities.
-*/
- public void setLabelAlignment( int anAlignmentConstant )
- {
- labelAlign = anAlignmentConstant;
+ /**
+ * Sets the constant used to align the labels in place and reflows the layout.
+ *
+ * @param anAlignmentConstant LEFT, CENTER, or RIGHT constants from
+ * SwingUtilities.
+ */
+ public void setLabelAlignment(int anAlignmentConstant) {
+ labelAlign = anAlignmentConstant;
updateLabels();
}
-
-/**
-* Factory method for creating panel spacers.
-* This implementation returns a JPanel with
-* opaque set to false. Override to customize.
-*/
- public JPanel createPanel()
- {
- JPanel result = new JPanel();
- result.setOpaque( false );
- return result;
- }
-/**
-* This method is responsible for the initial layout of the panel.
-* All labels and textfields will later added to listContainer.
-* This method is responsible for initializing listContainer.
-*/
- protected void doInitialLayout()
- {
- listContainer = createPanel();
- listContainer.setLayout( new BetterGridBagLayout() );
- this.setLayout( new BorderLayout() );
- this.add( listContainer, BorderLayout.NORTH );
+ /**
+ * Factory method for creating panel spacers. This implementation returns a
+ * JPanel with opaque set to false. Override to customize.
+ */
+ public JPanel createPanel() {
+ JPanel result = new JPanel();
+ result.setOpaque(false);
+ return result;
+ }
- //listContainer.setBackground( Color.blue ); // useful for testing
- //this.setBackground( Color.red );
- }
+ /**
+ * This method is responsible for the initial layout of the panel. All labels
+ * and textfields will later added to listContainer. This method is responsible
+ * for initializing listContainer.
+ */
+ protected void doInitialLayout() {
+ listContainer = createPanel();
+ listContainer.setLayout(new BetterGridBagLayout());
+ this.setLayout(new BorderLayout());
+ this.add(listContainer, BorderLayout.NORTH);
+
+ // listContainer.setBackground( Color.blue ); // useful for testing
+ // this.setBackground( Color.red );
+ }
-/**
-* Changes the horizontal spacing between the label and the components in the panel.
-* Note: Assumes listContainer uses a GridBagLayout.
-* @param newHgap the new spacing, in pixels. May not be negative.
-*/
- public void setHgap( int newHgap )
- {
- if ( newHgap < 0 ) return; // may not be negative
- this.hgap = newHgap;
- updateGaps();
- this.revalidate();
- this.repaint();
+ /**
+ * Changes the horizontal spacing between the label and the components in the
+ * panel. Note: Assumes listContainer uses a GridBagLayout.
+ *
+ * @param newHgap the new spacing, in pixels. May not be negative.
+ */
+ public void setHgap(int newHgap) {
+ if (newHgap < 0)
+ return; // may not be negative
+ this.hgap = newHgap;
+ updateGaps();
+ this.revalidate();
+ this.repaint();
- }
+ }
-/**
-* Gets the current horizontal spacing between components.
-* @return the current horizontal spacing, in pixels.
-*/
- public int getHgap()
- {
- return this.hgap;
- }
+ /**
+ * Gets the current horizontal spacing between components.
+ *
+ * @return the current horizontal spacing, in pixels.
+ */
+ public int getHgap() {
+ return this.hgap;
+ }
-/**
-* Changes the vertical spacing between components in the panel.
-* Note: Assumes listContainer uses a GridBagLayout.
-* @param newVgap the new spacing, in pixels. May not be negative.
-*/
- public void setVgap( int newVgap )
- {
- if ( newVgap < 0 ) return; // may not be negative
- this.vgap = newVgap;
- updateGaps();
- this.revalidate();
- this.repaint();
+ /**
+ * Changes the vertical spacing between components in the panel. Note: Assumes
+ * listContainer uses a GridBagLayout.
+ *
+ * @param newVgap the new spacing, in pixels. May not be negative.
+ */
+ public void setVgap(int newVgap) {
+ if (newVgap < 0)
+ return; // may not be negative
+ this.vgap = newVgap;
+ updateGaps();
+ this.revalidate();
+ this.repaint();
- }
+ }
-/**
-* Gets the current vertical spacing between components.
-* @return the current vertical spacing, in pixels.
-*/
- public int getVgap()
- {
- return this.vgap;
- }
+ /**
+ * Gets the current vertical spacing between components.
+ *
+ * @return the current vertical spacing, in pixels.
+ */
+ public int getVgap() {
+ return this.vgap;
+ }
-/**
-* Sets the minimum width for the labels column.
-* This left margin will grow if one of the labels
-* is wider than this value.
-* Note: assumes GridBagLayout.
-* @param newMargin the new minimum margin in pixels. May not be negative.
-*/
- public void setMargin( int newMargin )
- {
- if ( newMargin < 0 ) return; // may not be negative
- this.margin = newMargin;
-
- if ( listContainer.getLayout() instanceof GridBagLayout )
- {
- GridBagLayout gridBag = (GridBagLayout) listContainer.getLayout();
- GridBagConstraints constraints = null;
- Component c = null;
- int count = listContainer.getComponentCount();
- for ( int i = 0; i < count; i++ )
- {
- c = listContainer.getComponent( i );
- constraints = gridBag.getConstraints( c );
- if ( constraints.gridy == 0 && constraints.gridx % 2 == 0 )
- { // if this is a label spacer
- // replace it with an appropriately sized box
- listContainer.remove( c );
- listContainer.add( Box.createHorizontalStrut( this.margin ), constraints );
- }
- }
- }
-
- this.revalidate();
- this.repaint();
-
- }
+ /**
+ * Sets the minimum width for the labels column. This left margin will grow if
+ * one of the labels is wider than this value. Note: assumes GridBagLayout.
+ *
+ * @param newMargin the new minimum margin in pixels. May not be negative.
+ */
+ public void setMargin(int newMargin) {
+ if (newMargin < 0)
+ return; // may not be negative
+ this.margin = newMargin;
+
+ if (listContainer.getLayout() instanceof GridBagLayout) {
+ GridBagLayout gridBag = (GridBagLayout) listContainer.getLayout();
+ GridBagConstraints constraints = null;
+ Component c = null;
+ int count = listContainer.getComponentCount();
+ for (int i = 0; i < count; i++) {
+ c = listContainer.getComponent(i);
+ constraints = gridBag.getConstraints(c);
+ if (constraints.gridy == 0 && constraints.gridx % 2 == 0) { // if this is a label spacer
+ // replace it with an appropriately sized
+ // box
+ listContainer.remove(c);
+ listContainer.add(Box.createHorizontalStrut(this.margin), constraints);
+ }
+ }
+ }
-/**
-* Gets the current minimum margin for the labels column.
-* @return the current minimum margin in pixels.
-*/
- public int getMargin()
- {
- return this.margin;
- }
+ this.revalidate();
+ this.repaint();
-/**
-* Sets the number of columns for the panel.
-* Label/Component pairs will start from the top left
-* and fill in to the right before wrapping to the
-* next row. The default number of columns is one.
-* Note: assumes GridBagLayout.
-* @param newColumns the new number of columns. May not be less than one.
-*/
- public void setColumns( int newColumns )
- {
- if ( newColumns < 1 ) return; // may not be less than one.
- int oldColumns = this.columns;
- this.columns = newColumns;
-
- if ( listContainer.getLayout() instanceof GridBagLayout )
- {
- GridBagLayout gridBag = (GridBagLayout) listContainer.getLayout();
- int count = listContainer.getComponentCount();
- Component[] components = listContainer.getComponents();
- GridBagConstraints[] constraints = new GridBagConstraints[ components.length ];
- for ( int i = 0; i < components.length; i++ )
- {
- constraints[i] = gridBag.getConstraints( components[i] );
- }
- listContainer.removeAll();
- for ( int i = 0; i < components.length; i++ )
- {
- if ( constraints[i].gridy != 0 )
- { // ignore first row which is reserved for spacers.
-
- // translate component to new position
- // (columns*2 accounts for two grid columns for one "actual" column)
- int index = ( constraints[i].gridy - 1 ) * oldColumns*2 + constraints[i].gridx;
- constraints[i].gridy = ( index / (newColumns*2) ) + 1;
- constraints[i].gridx = index % (newColumns*2) ;
- listContainer.add( components[i], constraints[i] );
- }
- }
- createSpacers(); // replace the spacers
- updateGaps();
- }
-
- this.revalidate();
- this.repaint();
-
- }
+ }
-/**
-* Sets the vertical weight used for determining how to distribute additional
-* vertical space in the component.
-* @param aComponent Key that exists in the layout.
-* @return weighty The weight of the component, or -1.0 if not found.
-*/
- public double getVerticalWeightForKey( String key )
- {
- Container c = getCompositeComponentForKey( key );
- if ( c == null ) return -1.0;
- if ( ! ( listContainer.getLayout() instanceof GridBagLayout ) ) return -1.0;
- GridBagLayout layout = (GridBagLayout) listContainer.getLayout();
- GridBagConstraints gbc = layout.getConstraints( c );
- return gbc.weighty;
- }
-
-/**
-* Sets the vertical weight used for determining how to distribute additional
-* vertical space in the component. By default, all weights are zero, so each
-* component gets its preferred height. If any weights are specified, then
-* additional space is allocated to those components proportionately.
-* @param aComponent Key that exists in the layout.
-* @param weighty The new weight.
-*/
- public void setVerticalWeightForKey( String key, double weighty )
- {
- Container c = getCompositeComponentForKey( key );
- if ( c == null ) return;
- if ( ! ( listContainer.getLayout() instanceof GridBagLayout ) ) return;
- GridBagLayout layout = (GridBagLayout) listContainer.getLayout();
- GridBagConstraints gbc = layout.getConstraints( c );
- gbc.weighty = weighty;
- layout.setConstraints( c, gbc );
- // handle adding on-the-fly
- updateGaps();
- this.revalidate();
- this.repaint();
- }
-
-/**
-* Gets the current number of columns.
-* @return the current number of columns.
-*/
- public int getColumns()
- {
- return this.columns;
- }
+ /**
+ * Gets the current minimum margin for the labels column.
+ *
+ * @return the current minimum margin in pixels.
+ */
+ public int getMargin() {
+ return this.margin;
+ }
+
+ /**
+ * Sets the number of columns for the panel. Label/Component pairs will start
+ * from the top left and fill in to the right before wrapping to the next row.
+ * The default number of columns is one. Note: assumes GridBagLayout.
+ *
+ * @param newColumns the new number of columns. May not be less than one.
+ */
+ public void setColumns(int newColumns) {
+ if (newColumns < 1)
+ return; // may not be less than one.
+ int oldColumns = this.columns;
+ this.columns = newColumns;
+
+ if (listContainer.getLayout() instanceof GridBagLayout) {
+ GridBagLayout gridBag = (GridBagLayout) listContainer.getLayout();
+ int count = listContainer.getComponentCount();
+ Component[] components = listContainer.getComponents();
+ GridBagConstraints[] constraints = new GridBagConstraints[components.length];
+ for (int i = 0; i < components.length; i++) {
+ constraints[i] = gridBag.getConstraints(components[i]);
+ }
+ listContainer.removeAll();
+ for (int i = 0; i < components.length; i++) {
+ if (constraints[i].gridy != 0) { // ignore first row which is reserved for spacers.
+
+ // translate component to new position
+ // (columns*2 accounts for two grid columns for one "actual" column)
+ int index = (constraints[i].gridy - 1) * oldColumns * 2 + constraints[i].gridx;
+ constraints[i].gridy = (index / (newColumns * 2)) + 1;
+ constraints[i].gridx = index % (newColumns * 2);
+ listContainer.add(components[i], constraints[i]);
+ }
+ }
+ createSpacers(); // replace the spacers
+ updateGaps();
+ }
+
+ this.revalidate();
+ this.repaint();
-/**
-* Appends a label containing a key and the specified component
-* to the bottom of the panel. Any registered action listeners
-* will receive action events from the component - the key corresponding
-* to the component will be used as the action command.
-* @param key A string that will be displayed in a label, preferrably unique.
-* @param component A component that will be placed next to the label.
-* If null, a blank JPanel will be used.
-*/
- public void addPair( String key, Component component )
- {
- addRow( key, new Component[] { component } );
}
-
-/**
-* Appends a label containing a key and the specified component
-* to the bottom of the panel. Any registered action listeners
-* will receive action events from the component - the key corresponding
-* to the component will be used as the action command.
-* @param key A string that will be displayed in a label, preferrably unique.
-* @param component A component that will be placed next to the label.
-* If null, a blank JPanel will appear.
-*/
- public void addRow( String key, Component component )
- {
- addRow( key, new Component[] { component } );
+
+ /**
+ * Sets the vertical weight used for determining how to distribute additional
+ * vertical space in the component.
+ *
+ * @param aComponent Key that exists in the layout.
+ * @return weighty The weight of the component, or -1.0 if not found.
+ */
+ public double getVerticalWeightForKey(String key) {
+ Container c = getCompositeComponentForKey(key);
+ if (c == null)
+ return -1.0;
+ if (!(listContainer.getLayout() instanceof GridBagLayout))
+ return -1.0;
+ GridBagLayout layout = (GridBagLayout) listContainer.getLayout();
+ GridBagConstraints gbc = layout.getConstraints(c);
+ return gbc.weighty;
}
-
-/**
-* Appends a label containing a key and the specified components
-* to the bottom of the panel. Any registered action listeners
-* will receive action events from the component - the key corresponding
-* to the component will be used as the action command.
-* @param key A string that will be displayed in a label, preferrably unique.
-* @param components An array of components that will be placed next to the label.
-* Any nulls in the list will be replaced with blank JPanels.
-*/
- public void addRow(
- String key, Component[] components )
- {
- addCompositeComponent( key, makeCompositeComponent( key, components ) );
+
+ /**
+ * Sets the vertical weight used for determining how to distribute additional
+ * vertical space in the component. By default, all weights are zero, so each
+ * component gets its preferred height. If any weights are specified, then
+ * additional space is allocated to those components proportionately.
+ *
+ * @param aComponent Key that exists in the layout.
+ * @param weighty The new weight.
+ */
+ public void setVerticalWeightForKey(String key, double weighty) {
+ Container c = getCompositeComponentForKey(key);
+ if (c == null)
+ return;
+ if (!(listContainer.getLayout() instanceof GridBagLayout))
+ return;
+ GridBagLayout layout = (GridBagLayout) listContainer.getLayout();
+ GridBagConstraints gbc = layout.getConstraints(c);
+ gbc.weighty = weighty;
+ layout.setConstraints(c, gbc);
+ // handle adding on-the-fly
+ updateGaps();
+ this.revalidate();
+ this.repaint();
}
-/**
-* Appends a label containing a key and the specified components
-* to the bottom of the panel. Any registered action listeners
-* will receive action events from the components - the key corresponding
-* to the component will be used as the action command.
-* @param key A string that will be displayed in a label, preferrably unique.
-* @param west A component that will appear to the left of the other components,
-* as wide as its preferred width and as tall as the tallest of the other components.
-* A null will be replaced with a blank JPanel.
-* @param center A component that will appear between the other components,
-* taking up available space.
-* A null will be replaced with a blank JPanel.
-* @param east A component that will appear to the right of the other components,
-* as wide as its preferred width and as tall as the tallest of the other components.
-* A null will be replaced with a blank JPanel.
-*/
- public void addRow(
- String key, Component west, Component center, Component east )
- {
- addCompositeComponent( key,
- makeCompositeComponent( key,
- west, center, east ) );
+ /**
+ * Gets the current number of columns.
+ *
+ * @return the current number of columns.
+ */
+ public int getColumns() {
+ return this.columns;
}
-/**
-* Appends a label containing a key and the specified components
-* to the bottom of the panel. Any registered action listeners
-* will receive action events from the components - the key corresponding
-* to the component will be used as the action command.
-* @param key A string that will be displayed in a label, preferrably unique.
-* @param west A component that will appear to the left of the other components,
-* as wide as its preferred width and as tall as the tallest of the other components.
-* A null will be replaced with a blank JPanel.
-* @param north A component that will appear above all the other components,
-* as tall as its preferred height and as wide as the info panel itself.
-* @param center A component that will appear between the other components,
-* taking up available space. A null will be replaced with a blank JPanel.
-* @param south A component that will appear below all the other components,
-* as tall as its preferred height and as wide as the info panel itself.
-* @param east A component that will appear to the right of the other components,
-* as wide as its preferred width and as tall as the tallest of the other components.
-* A null will be replaced with a blank JPanel.
-*/
- public void addRow(
- String key, Component west, Component north,
- Component center, Component south, Component east )
- {
- addCompositeComponent( key,
- makeCompositeComponent( key,
- west, north, center, south, east ) );
+ /**
+ * Appends a label containing a key and the specified component to the bottom of
+ * the panel. Any registered action listeners will receive action events from
+ * the component - the key corresponding to the component will be used as the
+ * action command.
+ *
+ * @param key A string that will be displayed in a label, preferrably
+ * unique.
+ * @param component A component that will be placed next to the label. If null,
+ * a blank JPanel will be used.
+ */
+ public void addPair(String key, Component component) {
+ addRow(key, new Component[] { component });
}
-/**
-* Produces a container that contains the specified components,
-* using GridLayout. Nulls are ignored.
-* This implementation returns a JPanel.
-*/
- protected Container makeCompositeComponent(
- String key, Component[] components )
- {
+ /**
+ * Appends a label containing a key and the specified component to the bottom of
+ * the panel. Any registered action listeners will receive action events from
+ * the component - the key corresponding to the component will be used as the
+ * action command.
+ *
+ * @param key A string that will be displayed in a label, preferrably
+ * unique.
+ * @param component A component that will be placed next to the label. If null,
+ * a blank JPanel will appear.
+ */
+ public void addRow(String key, Component component) {
+ addRow(key, new Component[] { component });
+ }
+
+ /**
+ * Appends a label containing a key and the specified components to the bottom
+ * of the panel. Any registered action listeners will receive action events from
+ * the component - the key corresponding to the component will be used as the
+ * action command.
+ *
+ * @param key A string that will be displayed in a label, preferrably
+ * unique.
+ * @param components An array of components that will be placed next to the
+ * label. Any nulls in the list will be replaced with blank
+ * JPanels.
+ */
+ public void addRow(String key, Component[] components) {
+ addCompositeComponent(key, makeCompositeComponent(key, components));
+ }
+
+ /**
+ * Appends a label containing a key and the specified components to the bottom
+ * of the panel. Any registered action listeners will receive action events from
+ * the components - the key corresponding to the component will be used as the
+ * action command.
+ *
+ * @param key A string that will be displayed in a label, preferrably unique.
+ * @param west A component that will appear to the left of the other
+ * components, as wide as its preferred width and as tall as the
+ * tallest of the other components. A null will be replaced with a
+ * blank JPanel.
+ * @param center A component that will appear between the other components,
+ * taking up available space. A null will be replaced with a blank
+ * JPanel.
+ * @param east A component that will appear to the right of the other
+ * components, as wide as its preferred width and as tall as the
+ * tallest of the other components. A null will be replaced with a
+ * blank JPanel.
+ */
+ public void addRow(String key, Component west, Component center, Component east) {
+ addCompositeComponent(key, makeCompositeComponent(key, west, center, east));
+ }
+
+ /**
+ * Appends a label containing a key and the specified components to the bottom
+ * of the panel. Any registered action listeners will receive action events from
+ * the components - the key corresponding to the component will be used as the
+ * action command.
+ *
+ * @param key A string that will be displayed in a label, preferrably unique.
+ * @param west A component that will appear to the left of the other
+ * components, as wide as its preferred width and as tall as the
+ * tallest of the other components. A null will be replaced with a
+ * blank JPanel.
+ * @param north A component that will appear above all the other components, as
+ * tall as its preferred height and as wide as the info panel
+ * itself.
+ * @param center A component that will appear between the other components,
+ * taking up available space. A null will be replaced with a blank
+ * JPanel.
+ * @param south A component that will appear below all the other components, as
+ * tall as its preferred height and as wide as the info panel
+ * itself.
+ * @param east A component that will appear to the right of the other
+ * components, as wide as its preferred width and as tall as the
+ * tallest of the other components. A null will be replaced with a
+ * blank JPanel.
+ */
+ public void addRow(String key, Component west, Component north, Component center, Component south, Component east) {
+ addCompositeComponent(key, makeCompositeComponent(key, west, north, center, south, east));
+ }
+
+ /**
+ * Produces a container that contains the specified components, using
+ * GridLayout. Nulls are ignored. This implementation returns a JPanel.
+ */
+ protected Container makeCompositeComponent(String key, Component[] components) {
JPanel panel = createPanel();
- if ( components.length != 0 )
- {
- panel.setLayout( new GridLayout( 1, components.length, hgap, vgap ) );
+ if (components.length != 0) {
+ panel.setLayout(new GridLayout(1, components.length, hgap, vgap));
Component c;
- for ( int i = 0; i < components.length; i++ )
- {
+ for (int i = 0; i < components.length; i++) {
c = components[i];
- if ( c != null )
- {
- introspectComponent( c, key );
- panel.add( c );
+ if (c != null) {
+ introspectComponent(c, key);
+ panel.add(c);
}
}
}
return panel;
}
-/**
-* Produces a container that contains the specified components,
-* using BorderLayout. Nulls are ignored.
-* This implementation returns a JPanel.
-*/
- protected Container makeCompositeComponent(
- String key, Component west, Component center, Component east )
- {
+ /**
+ * Produces a container that contains the specified components, using
+ * BorderLayout. Nulls are ignored. This implementation returns a JPanel.
+ */
+ protected Container makeCompositeComponent(String key, Component west, Component center, Component east) {
JPanel panel = createPanel();
- panel.setLayout( new BorderLayout( hgap, vgap ) );
+ panel.setLayout(new BorderLayout(hgap, vgap));
- if ( west != null )
- {
- introspectComponent( west, key );
- panel.add( west, BorderLayout.WEST );
+ if (west != null) {
+ introspectComponent(west, key);
+ panel.add(west, BorderLayout.WEST);
}
-
- if ( center != null )
- {
- introspectComponent( center, key );
- panel.add( center, BorderLayout.CENTER );
+
+ if (center != null) {
+ introspectComponent(center, key);
+ panel.add(center, BorderLayout.CENTER);
}
-
- if ( east != null )
- {
- introspectComponent( east, key );
- panel.add( east, BorderLayout.EAST );
+
+ if (east != null) {
+ introspectComponent(east, key);
+ panel.add(east, BorderLayout.EAST);
}
-
+
return panel;
}
-/**
-* Produces a container that contains the specified components,
-* using BorderLayout. Nulls are ignored.
-* This implementation returns a JPanel.
-*/
- protected Container makeCompositeComponent(
- String key, Component west, Component north,
- Component center, Component south, Component east )
- {
+ /**
+ * Produces a container that contains the specified components, using
+ * BorderLayout. Nulls are ignored. This implementation returns a JPanel.
+ */
+ protected Container makeCompositeComponent(String key, Component west, Component north, Component center,
+ Component south, Component east) {
JPanel panel = createPanel();
- panel.setLayout( new BorderLayout( hgap, vgap ) );
+ panel.setLayout(new BorderLayout(hgap, vgap));
- if ( west != null )
- {
- introspectComponent( west, key );
- panel.add( west, BorderLayout.WEST );
+ if (west != null) {
+ introspectComponent(west, key);
+ panel.add(west, BorderLayout.WEST);
}
-
- if ( north != null )
- {
- introspectComponent( north, key );
- panel.add( north, BorderLayout.WEST );
+
+ if (north != null) {
+ introspectComponent(north, key);
+ panel.add(north, BorderLayout.WEST);
}
-
- if ( center != null )
- {
- introspectComponent( center, key );
- panel.add( center, BorderLayout.CENTER );
+
+ if (center != null) {
+ introspectComponent(center, key);
+ panel.add(center, BorderLayout.CENTER);
}
-
- if ( south != null )
- {
- introspectComponent( south, key );
- panel.add( south, BorderLayout.CENTER );
+
+ if (south != null) {
+ introspectComponent(south, key);
+ panel.add(south, BorderLayout.CENTER);
}
-
- if ( east != null )
- {
- introspectComponent( east, key );
- panel.add( east, BorderLayout.EAST );
+
+ if (east != null) {
+ introspectComponent(east, key);
+ panel.add(east, BorderLayout.EAST);
}
-
+
return panel;
}
-/**
-* Override to return a specific component to be used
-* as a label. This implementation calls createLabel().
-*/
- protected Component createLabelForKey( String aKey )
- {
- return createLabel();
+ /**
+ * Override to return a specific component to be used as a label. This
+ * implementation calls createLabel().
+ */
+ protected Component createLabelForKey(String aKey) {
+ return createLabel();
}
-/**
-* Provided for backwards compatibility, and called by
-* the default implementation of createLabelForKey.
-* This implementation returns a JLabel.
-*/
- protected JLabel createLabel()
- {
- return new JLabel();
+ /**
+ * Provided for backwards compatibility, and called by the default
+ * implementation of createLabelForKey. This implementation returns a JLabel.
+ */
+ protected JLabel createLabel() {
+ return new JLabel();
}
-/**
-* Appends a label containing a key and the specified component
-* to the bottom of the panel. Any registered action listeners
-* will receive action events from the component - the key corresponding
-* to the component will be used as the action command.
-* @param key A string that will be displayed in a label, preferrably unique.
-* @param component A component that will be placed next to the label.
-* If null, a stock JTextField will be used.
-*/
- protected void addCompositeComponent( String key, Component component )
- {
- if ( key == null )
- {
- key = "";
- }
- Component label = createLabelForKey( key );
- Component field = component;
- if ( field == null )
- {
- field = new JTextField( 15 ); // default to 15 columns
- }
- field.setName( key ); // for association and reference
- label.setName( key ); // ditto
- if ( label instanceof JLabel )
- {
- ((JLabel)label).setHorizontalAlignment( labelAlign );
- ((JLabel)label).setLabelFor( field ); // for accessibility
- }
- if ( "".equals( key ) )
- {
- setText( label, "" );
- }
- else
- {
- setText( label, prefix + key + postfix );
- }
- field.setEnabled( this.isEditable ); // was: setEditable
-
- GridBagConstraints gbc = new GridBagConstraints();
-
- if ( listContainer.getComponentCount() == 0 )
- { // we've just initialized or called removeAll
- createSpacers();
- }
-
- gbc.gridx = ( fields.size() % this.columns ) * 2;
- gbc.gridy = ( fields.size() / this.columns ) + 1; // spacer is at index zero
- gbc.weightx = 0.0;
- gbc.weighty = 0.0;
- gbc.anchor = this.labelAnchor;
- gbc.fill = GridBagConstraints.HORIZONTAL;
- listContainer.add( label, gbc );
+ /**
+ * Appends a label containing a key and the specified component to the bottom of
+ * the panel. Any registered action listeners will receive action events from
+ * the component - the key corresponding to the component will be used as the
+ * action command.
+ *
+ * @param key A string that will be displayed in a label, preferrably
+ * unique.
+ * @param component A component that will be placed next to the label. If null,
+ * a stock JTextField will be used.
+ */
+ protected void addCompositeComponent(String key, Component component) {
+ if (key == null) {
+ key = "";
+ }
+ Component label = createLabelForKey(key);
+ Component field = component;
+ if (field == null) {
+ field = new JTextField(15); // default to 15 columns
+ }
+ field.setName(key); // for association and reference
+ label.setName(key); // ditto
+ if (label instanceof JLabel) {
+ ((JLabel) label).setHorizontalAlignment(labelAlign);
+ ((JLabel) label).setLabelFor(field); // for accessibility
+ }
+ if ("".equals(key)) {
+ setText(label, "");
+ } else {
+ setText(label, prefix + key + postfix);
+ }
+ field.setEnabled(this.isEditable); // was: setEditable
+
+ GridBagConstraints gbc = new GridBagConstraints();
+
+ if (listContainer.getComponentCount() == 0) { // we've just initialized or called removeAll
+ createSpacers();
+ }
+
+ gbc.gridx = (fields.size() % this.columns) * 2;
+ gbc.gridy = (fields.size() / this.columns) + 1; // spacer is at index zero
+ gbc.weightx = 0.0;
+ gbc.weighty = 0.0;
+ gbc.anchor = this.labelAnchor;
+ gbc.fill = GridBagConstraints.HORIZONTAL;
+ listContainer.add(label, gbc);
gbc.fill = GridBagConstraints.BOTH;
- gbc.gridx = gbc.gridx + 1;
- //FIXME: components default to the labelAnchor - should be different?
- gbc.weightx = 1.0;
- gbc.weighty = 0.0;
-
- listContainer.add( field, gbc );
-
- if ( key.equals( HIDDEN ) )
- { // these components are not to be shown
- setText( label, " " );
- field.setVisible( false );
- }
-
- fields.add( field ); // using list not map to allow for duplicate keys
- labels.add( label ); // ditto
-
- // handle adding on-the-fly
- updateGaps();
- this.revalidate();
- this.repaint();
- }
+ gbc.gridx = gbc.gridx + 1;
+ // FIXME: components default to the labelAnchor - should be different?
+ gbc.weightx = 1.0;
+ gbc.weighty = 0.0;
-/**
-* Introspects a component to set the action command and to add the
-* InfoPanel to its list of ActionListeners.
-* @param aComponent The Component to be introspected.
-* @param aKey The action command to be set.
-*/
- protected void introspectComponent( Component aComponent, String aKey )
- {
- // try to set properties of whatever component this might be
- try {
- Method [] methods =
- (Method []) _method_cache.get( aComponent.getClass() );
- if (methods == null) {
- Class componentClass = aComponent.getClass();
- BeanInfo info =
- Introspector.getBeanInfo( componentClass );
-
- MethodDescriptor[] descriptors =
- info.getMethodDescriptors();
- Method setMethod = null;
- Method addMethod = null;
- for ( int i = 0;
- ((setMethod == null || addMethod == null) &&
- i < descriptors.length);
- i++ )
- {
- Method m = descriptors[i].getMethod();
- String name = m.getName ();
- if ( setMethod == null &&
- name.equals( "setActionCommand" ) )
- {
- setMethod = m;
- }
- else if ( addMethod == null &&
- name.equals( "addActionListener" ) )
- {
- addMethod = m;
- }
- }
-
- methods = new Method [] {setMethod, addMethod};
- _method_cache.put (componentClass, methods);
- }
- if (methods [0] != null) {
- methods [0].invoke( aComponent, new Object[] { aKey } );
- }
- if (methods [1] != null) {
- methods [1].invoke( aComponent, new Object[] { this } );
- listenedToComponents.add( aComponent );
- }
- }
- catch ( Exception exc )
- { // error occured while introspecting... move along.
- System.out.println( "InfoPanel.introspectComponent: " + exc );
- }
- }
-
-/**
-* Called to populate a label component with the specified text.
-* This implementation attempts to call setText(String) on the component.
-* Override to customize.
-*/
- protected void setText( Component c, String text )
- {
- try
- {
- Method m = c.getClass().getMethod( "setText", new Class[] { String.class } );
- if ( m != null )
- {
- m.invoke( c, new Object[] { text } );
- }
- }
- catch ( Exception exc )
- {
- // no such method: ignore
- }
- }
+ listContainer.add(field, gbc);
-/**
-* Creates spacer components on the reserved first grid row
-* for each column of labels and fields.
-* This allows us to set the margin for those label columns,
-* and set the preferred width of the field columns.
-* A list containing the field spacers should be assigned to
-* the fieldSpacers instance variable.
-*/
- private void createSpacers()
- {
- if ( listContainer.getLayout() instanceof GridBagLayout )
- {
- // insert spacers for labels column
- GridBagLayout gridBag = (GridBagLayout) listContainer.getLayout();
- GridBagConstraints constraints = new GridBagConstraints();
- constraints.gridy = 0;
- constraints.fill = GridBagConstraints.HORIZONTAL;
-
- fieldSpacers = new LinkedList();
- Component fieldSpacer;
- for ( int i = 0; i < this.columns; i++ )
- {
- constraints.gridx = i * 2;
- listContainer.add( Box.createHorizontalStrut( this.margin ), constraints );
-
- constraints.gridx = i * 2 + 1;
- fieldSpacer = Box.createHorizontalStrut( 0 );
- fieldSpacers.add( fieldSpacer );
- listContainer.add( fieldSpacer, constraints );
- }
- }
- }
+ if (key.equals(HIDDEN)) { // these components are not to be shown
+ setText(label, " ");
+ field.setVisible(false);
+ }
-/**
-* Updates the insets for all components.
-*/
- protected void updateGaps()
- {
- if ( listContainer.getLayout() instanceof GridBagLayout )
- {
- GridBagLayout layout = (GridBagLayout) listContainer.getLayout();
- Component c = null;
- GridBagConstraints gbc = null;
- double totalWeightY = 0.0;
- int count = listContainer.getComponentCount();
- int i;
- for ( i = 0; i < count; i++ )
- {
- c = listContainer.getComponent( i );
- gbc = layout.getConstraints( c );
- totalWeightY += gbc.weighty;
- if ( (gbc.gridx + 1) % ( this.columns * 2 ) == 0 )
- { // if last component in row
- gbc.insets = new Insets( 0, 0, this.vgap, 0 );
- }
- else
- {
- if ( gbc.gridx % 2 == 0 )
- { // is a label column - NOTE: uses eleven pixels before component, per l&f guide
- gbc.insets = new Insets( 0, 0, this.vgap, 11 );
- }
- else
- { // is a component column
- if ( gbc.gridy != 0 )
- {
- if ( c instanceof JPanel ) ((JPanel)c).setPreferredSize( null );
- gbc.insets = new Insets( 0, 0, this.vgap, this.hgap );
- }
- }
- }
- layout.setConstraints( c, gbc );
- }
-
- //hack: gridbag clumps components in center if weighty is zero
- // if sum of weighty is zero, top-justify the list container
- this.remove( listContainer );
- if ( totalWeightY == 0.0 )
- {
- this.add( listContainer, BorderLayout.NORTH );
- }
- else // put list container in center so it will grow
- {
- this.add( listContainer, BorderLayout.CENTER );
- }
- }
- }
+ fields.add(field); // using list not map to allow for duplicate keys
+ labels.add(label); // ditto
-/**
-* Updates the label alignment.
-*/
- protected void updateLabels()
- {
- if ( listContainer.getLayout() instanceof GridBagLayout )
- {
- GridBagLayout layout = (GridBagLayout) listContainer.getLayout();
- Component c = null;
- GridBagConstraints gbc = null;
+ // handle adding on-the-fly
+ updateGaps();
+ this.revalidate();
+ this.repaint();
+ }
+
+ /**
+ * Introspects a component to set the action command and to add the InfoPanel to
+ * its list of ActionListeners.
+ *
+ * @param aComponent The Component to be introspected.
+ * @param aKey The action command to be set.
+ */
+ protected void introspectComponent(Component aComponent, String aKey) {
+ // try to set properties of whatever component this might be
+ try {
+ Method[] methods = (Method[]) _method_cache.get(aComponent.getClass());
+ if (methods == null) {
+ Class componentClass = aComponent.getClass();
+ BeanInfo info = Introspector.getBeanInfo(componentClass);
+
+ MethodDescriptor[] descriptors = info.getMethodDescriptors();
+ Method setMethod = null;
+ Method addMethod = null;
+ for (int i = 0; ((setMethod == null || addMethod == null) && i < descriptors.length); i++) {
+ Method m = descriptors[i].getMethod();
+ String name = m.getName();
+ if (setMethod == null && name.equals("setActionCommand")) {
+ setMethod = m;
+ } else if (addMethod == null && name.equals("addActionListener")) {
+ addMethod = m;
+ }
+ }
+
+ methods = new Method[] { setMethod, addMethod };
+ _method_cache.put(componentClass, methods);
+ }
+ if (methods[0] != null) {
+ methods[0].invoke(aComponent, new Object[] { aKey });
+ }
+ if (methods[1] != null) {
+ methods[1].invoke(aComponent, new Object[] { this });
+ listenedToComponents.add(aComponent);
+ }
+ } catch (Exception exc) { // error occured while introspecting... move along.
+ System.out.println("InfoPanel.introspectComponent: " + exc);
+ }
+ }
+
+ /**
+ * Called to populate a label component with the specified text. This
+ * implementation attempts to call setText(String) on the component. Override to
+ * customize.
+ */
+ protected void setText(Component c, String text) {
+ try {
+ Method m = c.getClass().getMethod("setText", new Class[] { String.class });
+ if (m != null) {
+ m.invoke(c, new Object[] { text });
+ }
+ } catch (Exception exc) {
+ // no such method: ignore
+ }
+ }
+
+ /**
+ * Creates spacer components on the reserved first grid row for each column of
+ * labels and fields. This allows us to set the margin for those label columns,
+ * and set the preferred width of the field columns. A list containing the field
+ * spacers should be assigned to the fieldSpacers instance variable.
+ */
+ private void createSpacers() {
+ if (listContainer.getLayout() instanceof GridBagLayout) {
+ // insert spacers for labels column
+ GridBagLayout gridBag = (GridBagLayout) listContainer.getLayout();
+ GridBagConstraints constraints = new GridBagConstraints();
+ constraints.gridy = 0;
+ constraints.fill = GridBagConstraints.HORIZONTAL;
+
+ fieldSpacers = new LinkedList();
+ Component fieldSpacer;
+ for (int i = 0; i < this.columns; i++) {
+ constraints.gridx = i * 2;
+ listContainer.add(Box.createHorizontalStrut(this.margin), constraints);
+
+ constraints.gridx = i * 2 + 1;
+ fieldSpacer = Box.createHorizontalStrut(0);
+ fieldSpacers.add(fieldSpacer);
+ listContainer.add(fieldSpacer, constraints);
+ }
+ }
+ }
+
+ /**
+ * Updates the insets for all components.
+ */
+ protected void updateGaps() {
+ if (listContainer.getLayout() instanceof GridBagLayout) {
+ GridBagLayout layout = (GridBagLayout) listContainer.getLayout();
+ Component c = null;
+ GridBagConstraints gbc = null;
+ double totalWeightY = 0.0;
+ int count = listContainer.getComponentCount();
+ int i;
+ for (i = 0; i < count; i++) {
+ c = listContainer.getComponent(i);
+ gbc = layout.getConstraints(c);
+ totalWeightY += gbc.weighty;
+ if ((gbc.gridx + 1) % (this.columns * 2) == 0) { // if last component in row
+ gbc.insets = new Insets(0, 0, this.vgap, 0);
+ } else {
+ if (gbc.gridx % 2 == 0) { // is a label column - NOTE: uses eleven pixels before component, per l&f
+ // guide
+ gbc.insets = new Insets(0, 0, this.vgap, 11);
+ } else { // is a component column
+ if (gbc.gridy != 0) {
+ if (c instanceof JPanel)
+ ((JPanel) c).setPreferredSize(null);
+ gbc.insets = new Insets(0, 0, this.vgap, this.hgap);
+ }
+ }
+ }
+ layout.setConstraints(c, gbc);
+ }
+
+ // hack: gridbag clumps components in center if weighty is zero
+ // if sum of weighty is zero, top-justify the list container
+ this.remove(listContainer);
+ if (totalWeightY == 0.0) {
+ this.add(listContainer, BorderLayout.NORTH);
+ } else // put list container in center so it will grow
+ {
+ this.add(listContainer, BorderLayout.CENTER);
+ }
+ }
+ }
+
+ /**
+ * Updates the label alignment.
+ */
+ protected void updateLabels() {
+ if (listContainer.getLayout() instanceof GridBagLayout) {
+ GridBagLayout layout = (GridBagLayout) listContainer.getLayout();
+ Component c = null;
+ GridBagConstraints gbc = null;
Iterator it = labels.iterator();
- while ( it.hasNext() )
- {
- c = (Component) it.next();
- if ( c instanceof JLabel )
- {
- ((JLabel)c).setHorizontalAlignment( labelAlign );
- }
- gbc = layout.getConstraints( c );
- gbc.anchor = this.labelAnchor;
- layout.setConstraints( c, gbc );
- }
- }
- }
+ while (it.hasNext()) {
+ c = (Component) it.next();
+ if (c instanceof JLabel) {
+ ((JLabel) c).setHorizontalAlignment(labelAlign);
+ }
+ gbc = layout.getConstraints(c);
+ gbc.anchor = this.labelAnchor;
+ layout.setConstraints(c, gbc);
+ }
+ }
+ }
-/**
-* Convenience method that uses a stock JTextField.
-* @param key A string that will be displayed in a label, preferrably unique.
-* @param value A string that will be displayed in a textfield.
-*/
- public void addPair( String key, String value )
- {
- addPair( key, value, null );
- }
+ /**
+ * Convenience method that uses a stock JTextField.
+ *
+ * @param key A string that will be displayed in a label, preferrably unique.
+ * @param value A string that will be displayed in a textfield.
+ */
+ public void addPair(String key, String value) {
+ addPair(key, value, null);
+ }
-/**
-* Convenience method that uses the specified JTextField or subclass
-* and sets it to the specified value.
-* @param key A string that will be displayed in a label, preferrably unique.
-* @param value A string that will be displayed in a textfield.
-* @param textField A JTextField or subclass that will be used to display the value.
-* If null, a stock JTextField will be used.
-*/
- public void addPair( String key, String value, JTextField textField )
- {
- if ( value == null )
- {
- value = "";
- }
- JTextField field = textField;
- if ( field == null )
- {
- field = new JTextField( 15 ); // default to 15 columns
- }
- else
- {
- field = textField;
- }
- field.setText( value );
-
- addPair( key, (Component) field );
- }
+ /**
+ * Convenience method that uses the specified JTextField or subclass and sets it
+ * to the specified value.
+ *
+ * @param key A string that will be displayed in a label, preferrably
+ * unique.
+ * @param value A string that will be displayed in a textfield.
+ * @param textField A JTextField or subclass that will be used to display the
+ * value. If null, a stock JTextField will be used.
+ */
+ public void addPair(String key, String value, JTextField textField) {
+ if (value == null) {
+ value = "";
+ }
+ JTextField field = textField;
+ if (field == null) {
+ field = new JTextField(15); // default to 15 columns
+ } else {
+ field = textField;
+ }
+ field.setText(value);
-/**
-* Removes all components from the list. Buttons, if any,
-* will remain unchanged - use setButtons( null ) to remove
-* them. NOTE: does not call super.removeAll().
-*/
- public void removeAll()
- {
- Object component;
- Method method;
- Class[] paramClasses = new Class[] { ActionListener.class };
- Object[] paramObjects = new Object[] { this };
-
- Iterator iterator = listenedToComponents.iterator();
- while ( iterator.hasNext() )
- {
- component = iterator.next();
- try
- {
- method = component.getClass().getMethod( "removeActionListener", paramClasses );
- if ( method != null )
- {
- method.invoke( component, paramObjects );
- }
- }
- catch ( Exception exception )
- {
- // No removeActionListener() method, move along.
- }
- }
-
- listenedToComponents.clear();
-
- listContainer.removeAll();
- fields.clear();
- labels.clear();
- this.revalidate();
- this.repaint();
-
- //FIXME: It is very confusing that this
- // implementation does not call super.removeAll().
- }
+ addPair(key, (Component) field);
+ }
-/**
-* Adds one or buttons to the bottom of the panel with the specified labels
-* from left to right. Any action listeners will receive action events
-* from clicks on these buttons - the supplied label will be used as the action command.
-* @param buttons A string array containing the strings to be used for the button labels
-* and action commands. A null value will remove the button panel.
-* @see ButtonPanel
-*/
- public void setButtons( String[] buttons )
- {
- if ( buttonPanel == null )
- {
- buttonPanel = new ButtonPanel();
- buttonPanel.setInsets( new Insets( 6, 0, 0, 0 ) );
- // button panel has a 11-pixel top inset
- // and java l&f guide says 17-pixels before command buttons
- buttonPanel.addActionListener( this );
- this.add( buttonPanel, BorderLayout.SOUTH );
- }
- if ( buttons == null )
- {
- this.remove( buttonPanel );
- buttonPanel = null;
- }
- else
- {
- buttonPanel.setLabels( buttons );
- }
-
- this.revalidate();
- this.repaint();
- }
- protected Collection listenedToComponents = new LinkedList();
+ /**
+ * Removes all components from the list. Buttons, if any, will remain unchanged
+ * - use setButtons( null ) to remove them. NOTE: does not call
+ * super.removeAll().
+ */
+ public void removeAll() {
+ Object component;
+ Method method;
+ Class[] paramClasses = new Class[] { ActionListener.class };
+ Object[] paramObjects = new Object[] { this };
+
+ Iterator iterator = listenedToComponents.iterator();
+ while (iterator.hasNext()) {
+ component = iterator.next();
+ try {
+ method = component.getClass().getMethod("removeActionListener", paramClasses);
+ if (method != null) {
+ method.invoke(component, paramObjects);
+ }
+ } catch (Exception exception) {
+ // No removeActionListener() method, move along.
+ }
+ }
-/**
-* Retrieves the names of the buttons that are displayed, if any.
-* @return A string array containing the strings used for the button labels
-* and action commands, or null if no buttons have been created.
-* @see ButtonPanel
-*/
- public String[] getButtons()
- {
- if ( buttonPanel == null )
- {
- return null; // none created
- }
+ listenedToComponents.clear();
- return buttonPanel.getLabels();
- }
+ listContainer.removeAll();
+ fields.clear();
+ labels.clear();
+ this.revalidate();
+ this.repaint();
-/**
-* Retrieves the actual button panel, if any.
-* @return A button panel, or null if none has been created.
-* @see ButtonPanel
-*/
- public ButtonPanel getButtonPanel()
- {
- return buttonPanel;
- }
+ // FIXME: It is very confusing that this
+ // implementation does not call super.removeAll().
+ }
+ /**
+ * Adds one or buttons to the bottom of the panel with the specified labels from
+ * left to right. Any action listeners will receive action events from clicks on
+ * these buttons - the supplied label will be used as the action command.
+ *
+ * @param buttons A string array containing the strings to be used for the
+ * button labels and action commands. A null value will remove
+ * the button panel.
+ * @see ButtonPanel
+ */
+ public void setButtons(String[] buttons) {
+ if (buttonPanel == null) {
+ buttonPanel = new ButtonPanel();
+ buttonPanel.setInsets(new Insets(6, 0, 0, 0));
+ // button panel has a 11-pixel top inset
+ // and java l&f guide says 17-pixels before command buttons
+ buttonPanel.addActionListener(this);
+ this.add(buttonPanel, BorderLayout.SOUTH);
+ }
+ if (buttons == null) {
+ this.remove(buttonPanel);
+ buttonPanel = null;
+ } else {
+ buttonPanel.setLabels(buttons);
+ }
-/**
-* Sets whether the values displayed in the panel should be editable. Defaults to true.
-* @param isEditable Whether the values should be editable.
-*/
- public void setEditable( boolean isEditable )
- {
- this.isEditable = isEditable;
- Iterator enumeration = fields.iterator();
- while ( enumeration.hasNext() )
- {
- ( (Component) enumeration.next() ).setEnabled( isEditable );
- }
- }
+ this.revalidate();
+ this.repaint();
+ }
-/**
-* Gets whether the values displayed in the panel are editable.
-* @return Whether the values should be editable.
-*/
- public boolean isEditable()
- {
- return this.isEditable;
- }
+ protected Collection listenedToComponents = new LinkedList();
+
+ /**
+ * Retrieves the names of the buttons that are displayed, if any.
+ *
+ * @return A string array containing the strings used for the button labels and
+ * action commands, or null if no buttons have been created.
+ * @see ButtonPanel
+ */
+ public String[] getButtons() {
+ if (buttonPanel == null) {
+ return null; // none created
+ }
-/**
-* Sets the field associated with the key to the specified value.
-* Note: If the component does not respond to setText() or setString()
-* or setValue() the value will not be set. JTextFields and the like will work.
-* @param key A string representing the key associated with the field. Nulls are converted to an empty string.
-* @param value A object to be displayed in the specified field. Nulls are converted to an empty string.
-*/
- public void setValueForKey( String key, Object value )
- {
- setValueForKey( key, value, 0 );
+ return buttonPanel.getLabels();
}
-
-/**
-* Sets the field associated with the key to the specified value.
-* Note: If the component does not respond to setText() or setString()
-* or setValue() the value will not be set. JTextFields and the like will work.
-* @param key A string representing the key associated with the field. Nulls are converted to an empty string.
-* @param value A object to be displayed in the specified field. Nulls are converted to an empty string.
-*/
- public void setValueForKey( String key, Object value, int index )
- {
- if ( key == null )
- {
- key = "";
- }
-
- Container field = null;
- for ( int i = 0; i < fields.size(); i++ )
- {
- field = (Container) fields.get(i);
- if ( key.equals( field.getName() ) )
- {
- setValueForIndex( index, i, value );
+
+ /**
+ * Retrieves the actual button panel, if any.
+ *
+ * @return A button panel, or null if none has been created.
+ * @see ButtonPanel
+ */
+ public ButtonPanel getButtonPanel() {
+ return buttonPanel;
+ }
+
+ /**
+ * Sets whether the values displayed in the panel should be editable. Defaults
+ * to true.
+ *
+ * @param isEditable Whether the values should be editable.
+ */
+ public void setEditable(boolean isEditable) {
+ this.isEditable = isEditable;
+ Iterator enumeration = fields.iterator();
+ while (enumeration.hasNext()) {
+ ((Component) enumeration.next()).setEnabled(isEditable);
+ }
+ }
+
+ /**
+ * Gets whether the values displayed in the panel are editable.
+ *
+ * @return Whether the values should be editable.
+ */
+ public boolean isEditable() {
+ return this.isEditable;
+ }
+
+ /**
+ * Sets the field associated with the key to the specified value. Note: If the
+ * component does not respond to setText() or setString() or setValue() the
+ * value will not be set. JTextFields and the like will work.
+ *
+ * @param key A string representing the key associated with the field. Nulls
+ * are converted to an empty string.
+ * @param value A object to be displayed in the specified field. Nulls are
+ * converted to an empty string.
+ */
+ public void setValueForKey(String key, Object value) {
+ setValueForKey(key, value, 0);
+ }
+
+ /**
+ * Sets the field associated with the key to the specified value. Note: If the
+ * component does not respond to setText() or setString() or setValue() the
+ * value will not be set. JTextFields and the like will work.
+ *
+ * @param key A string representing the key associated with the field. Nulls
+ * are converted to an empty string.
+ * @param value A object to be displayed in the specified field. Nulls are
+ * converted to an empty string.
+ */
+ public void setValueForKey(String key, Object value, int index) {
+ if (key == null) {
+ key = "";
+ }
+
+ Container field = null;
+ for (int i = 0; i < fields.size(); i++) {
+ field = (Container) fields.get(i);
+ if (key.equals(field.getName())) {
+ setValueForIndex(index, i, value);
return;
- }
+ }
}
- // else not found - ignore
- }
-
-/**
-* Sets the first field at the specified row index to the specified value.
-* Note: If the component does not respond to setText() or setString()
-* or setValue() the value will not be set. JTextFields and the like will work.
-* @param row The row index of the component.
-* @param value A object to be displayed in the specified field.
-* Nulls are converted to an empty string.
-*/
- public void setValueForIndex( int row, Object value )
- {
- setValueForIndex( row, 0, value );
+ // else not found - ignore
}
-
-/**
-* Sets the field at the specified row index and column index to the specified value.
-* Note: If the component does not respond to setText() or setString()
-* or setValue() the value will not be set. JTextFields and the like will work.
-* @param row The row index of the component.
-* @param index The column index of the component.
-* @param value A object to be displayed in the specified field.
-* Nulls are converted to an empty string.
-*/
- public void setValueForIndex( int row, int col, Object value )
- {
- Container field = (Container) fields.get( row );
- Component c = field.getComponent( col );
- setValueForComponent( c, value );
+
+ /**
+ * Sets the first field at the specified row index to the specified value. Note:
+ * If the component does not respond to setText() or setString() or setValue()
+ * the value will not be set. JTextFields and the like will work.
+ *
+ * @param row The row index of the component.
+ * @param value A object to be displayed in the specified field. Nulls are
+ * converted to an empty string.
+ */
+ public void setValueForIndex(int row, Object value) {
+ setValueForIndex(row, 0, value);
}
-
+ /**
+ * Sets the field at the specified row index and column index to the specified
+ * value. Note: If the component does not respond to setText() or setString() or
+ * setValue() the value will not be set. JTextFields and the like will work.
+ *
+ * @param row The row index of the component.
+ * @param index The column index of the component.
+ * @param value A object to be displayed in the specified field. Nulls are
+ * converted to an empty string.
+ */
+ public void setValueForIndex(int row, int col, Object value) {
+ Container field = (Container) fields.get(row);
+ Component c = field.getComponent(col);
+ setValueForComponent(c, value);
+ }
-/**
-* Sets the value in the field at the specified index.
-* Note: If the component does not respond to setText() or setString()
-* or setValue() this method will return null. JTextFields and the like will work.
-* @param A valid index.
-* @param value A object to be displayed in the specified field.
-*/
- protected void setValueForComponent( Component aComponent, Object value )
- {
- // try to set a text or string property
- try {
- BeanInfo info = Introspector.getBeanInfo( aComponent.getClass() );
- MethodDescriptor[] methods = info.getMethodDescriptors();
- for ( int i = 0; i < methods.length; i++ )
- {
- Method m = methods[i].getMethod();
- Class[] paramTypes = m.getParameterTypes();
- if ( paramTypes.length == 1 )
- {
- if ( m.getName().equals( "setText" ) )
- {
- if ( paramTypes[0].getName().equals( String.class.getName() ) )
- {
- m.invoke( aComponent, new Object[] { value } );
- }
- }
- if ( m.getName().equals( "setString" ) )
- {
- if ( paramTypes[0].getName().equals( String.class.getName() ) )
- {
- m.invoke( aComponent, new Object[] { value } );
- }
- }
- if ( m.getName().equals( "setValue" ) )
- {
- if ( paramTypes[0].getName().equals( Object.class.getName() ) )
- {
- m.invoke( aComponent, new Object[] { value } );
- }
- }
- }
- }
- }
- catch ( Exception exc )
- { // error occured while introspecting... move along.
- // FIXME: should log error in ErrorManager
- System.out.println( "InfoPanel.setValueForComponent: " + exc );
- }
- }
+ /**
+ * Sets the value in the field at the specified index. Note: If the component
+ * does not respond to setText() or setString() or setValue() this method will
+ * return null. JTextFields and the like will work.
+ *
+ * @param A valid index.
+ * @param value A object to be displayed in the specified field.
+ */
+ protected void setValueForComponent(Component aComponent, Object value) {
+ // try to set a text or string property
+ try {
+ BeanInfo info = Introspector.getBeanInfo(aComponent.getClass());
+ MethodDescriptor[] methods = info.getMethodDescriptors();
+ for (int i = 0; i < methods.length; i++) {
+ Method m = methods[i].getMethod();
+ Class[] paramTypes = m.getParameterTypes();
+ if (paramTypes.length == 1) {
+ if (m.getName().equals("setText")) {
+ if (paramTypes[0].getName().equals(String.class.getName())) {
+ m.invoke(aComponent, new Object[] { value });
+ }
+ }
+ if (m.getName().equals("setString")) {
+ if (paramTypes[0].getName().equals(String.class.getName())) {
+ m.invoke(aComponent, new Object[] { value });
+ }
+ }
+ if (m.getName().equals("setValue")) {
+ if (paramTypes[0].getName().equals(Object.class.getName())) {
+ m.invoke(aComponent, new Object[] { value });
+ }
+ }
+ }
+ }
+ } catch (Exception exc) { // error occured while introspecting... move along.
+ // FIXME: should log error in ErrorManager
+ System.out.println("InfoPanel.setValueForComponent: " + exc);
+ }
+ }
-/**
-* Gets the value in the field at the specified index.
-* Note: If the component does not respond to getText() or getString()
-* or getSelectedItem() this method will return null. JTextFields and the like will work.
-* @param A valid index.
-* @return An object representing the value in the field at the specified index,
-* or null if the component does not have a text property or if the index is out of bounds.
-*/
- public Object getValueForIndex( int anIndex )
- {
- return getValueForIndex( anIndex, 0 );
- }
+ /**
+ * Gets the value in the field at the specified index. Note: If the component
+ * does not respond to getText() or getString() or getSelectedItem() this method
+ * will return null. JTextFields and the like will work.
+ *
+ * @param A valid index.
+ * @return An object representing the value in the field at the specified index,
+ * or null if the component does not have a text property or if the
+ * index is out of bounds.
+ */
+ public Object getValueForIndex(int anIndex) {
+ return getValueForIndex(anIndex, 0);
+ }
-/**
-* Gets the value in the field at the specified row and column.
-* Note: If the component does not respond to getText() or getString()
-* or getSelectedItem() this method will return null. JTextFields and the like will work.
-* @param A valid index.
-* @return An object representing the value in the field at the specified index,
-* or null if the component does not have a text property or if the index is out of bounds.
-*/
- public Object getValueForIndex( int row, int col )
- {
- if ( ( row >= fields.size() ) || ( row < 0 ) )
- { // out of bounds
- return null;
- }
-
- Container field = (Container) fields.get( row );
- Component c = field.getComponent( col );
- return getValueForComponent( c );
- }
+ /**
+ * Gets the value in the field at the specified row and column. Note: If the
+ * component does not respond to getText() or getString() or getSelectedItem()
+ * this method will return null. JTextFields and the like will work.
+ *
+ * @param A valid index.
+ * @return An object representing the value in the field at the specified index,
+ * or null if the component does not have a text property or if the
+ * index is out of bounds.
+ */
+ public Object getValueForIndex(int row, int col) {
+ if ((row >= fields.size()) || (row < 0)) { // out of bounds
+ return null;
+ }
-/**
-* Gets the value in the field associated with the key.
-* Note: If the component does not respond to getText() or getString()
-* or getSelectedItem() this method will return null. JTextFields and the like will work.
-* @param key An string representing the key associated with the field. Nulls are converted to an empty string.
-* @return An object representing the value in the field associated with the key,
-* or null if the key does not exist or if the component does not have a text property.
-*/
- public Object getValueForKey( String key )
- {
- return getValueForKey( key, 0 );
+ Container field = (Container) fields.get(row);
+ Component c = field.getComponent(col);
+ return getValueForComponent(c);
}
-/**
-* Gets the value in the field associated with the key.
-* Note: If the component does not respond to getText() or getString()
-* or getSelectedItem() this method will return null. JTextFields and the like will work.
-* @param key An string representing the key associated with the field. Nulls are converted to an empty string.
-* @return An object representing the value in the field associated with the key,
-* or null if the key does not exist or if the component does not have a text property.
-*/
- public Object getValueForKey( String key, int index )
- {
- if ( key == null )
- {
- key = "";
- }
-
- Container field = null;
- Iterator enumeration = fields.iterator();
- while ( enumeration.hasNext() )
- { // finds first value in list with specified key
- field = (Container) enumeration.next();
- if ( key.equals( field.getName() ) )
- {
- Component c = field.getComponent( index );
- if ( c != null )
- {
- return getValueForComponent( c );
+ /**
+ * Gets the value in the field associated with the key. Note: If the component
+ * does not respond to getText() or getString() or getSelectedItem() this method
+ * will return null. JTextFields and the like will work.
+ *
+ * @param key An string representing the key associated with the field. Nulls
+ * are converted to an empty string.
+ * @return An object representing the value in the field associated with the
+ * key, or null if the key does not exist or if the component does not
+ * have a text property.
+ */
+ public Object getValueForKey(String key) {
+ return getValueForKey(key, 0);
+ }
+
+ /**
+ * Gets the value in the field associated with the key. Note: If the component
+ * does not respond to getText() or getString() or getSelectedItem() this method
+ * will return null. JTextFields and the like will work.
+ *
+ * @param key An string representing the key associated with the field. Nulls
+ * are converted to an empty string.
+ * @return An object representing the value in the field associated with the
+ * key, or null if the key does not exist or if the component does not
+ * have a text property.
+ */
+ public Object getValueForKey(String key, int index) {
+ if (key == null) {
+ key = "";
+ }
+
+ Container field = null;
+ Iterator enumeration = fields.iterator();
+ while (enumeration.hasNext()) { // finds first value in list with specified key
+ field = (Container) enumeration.next();
+ if (key.equals(field.getName())) {
+ Component c = field.getComponent(index);
+ if (c != null) {
+ return getValueForComponent(c);
}
- }
- }
- // else not found
- return null;
- }
+ }
+ }
+ // else not found
+ return null;
+ }
-/**
-* Gets the value in the specified component.
-* Note: If the component does not respond to getText() or getString()
-* or getSelectedItem() this method will return null. JTextFields and the like will work.
-* @param aComponent The specified component.
-* @return An object representing the value in the component.
-* or null if the component does not have a text property.
-*/
- protected Object getValueForComponent( Component aComponent )
- {
- // try to get a text or string property
- try
- {
- BeanInfo info = Introspector.getBeanInfo( aComponent.getClass() );
- MethodDescriptor[] methods = info.getMethodDescriptors();
- for ( int i = 0; i < methods.length; i++ )
- {
- Method m = methods[i].getMethod();
- Class[] paramTypes = m.getParameterTypes();
- if ( m.getName().equals( "getText" ) )
- {
- if ( paramTypes.length == 0 )
- {
- return m.invoke( aComponent, new Object[] {} );
- }
- }
- if ( m.getName().equals( "getString" ) )
- {
- if ( paramTypes.length == 0 )
- {
- return m.invoke( aComponent, new Object[] {} );
- }
- }
- if ( m.getName().equals( "getSelectedItem" ) )
- {
- if ( paramTypes.length == 0 )
- {
- return m.invoke( aComponent, new Object[] {} );
- }
- }
- // TODO: should also handle variants of setValue()
- }
- }
- catch ( Exception exc )
- { // error occured while introspecting... move along.
- System.out.println( "InfoPanel.getValueFromComponent: " + exc );
- }
-
- // not found
- return null;
- }
+ /**
+ * Gets the value in the specified component. Note: If the component does not
+ * respond to getText() or getString() or getSelectedItem() this method will
+ * return null. JTextFields and the like will work.
+ *
+ * @param aComponent The specified component.
+ * @return An object representing the value in the component. or null if the
+ * component does not have a text property.
+ */
+ protected Object getValueForComponent(Component aComponent) {
+ // try to get a text or string property
+ try {
+ BeanInfo info = Introspector.getBeanInfo(aComponent.getClass());
+ MethodDescriptor[] methods = info.getMethodDescriptors();
+ for (int i = 0; i < methods.length; i++) {
+ Method m = methods[i].getMethod();
+ Class[] paramTypes = m.getParameterTypes();
+ if (m.getName().equals("getText")) {
+ if (paramTypes.length == 0) {
+ return m.invoke(aComponent, new Object[] {});
+ }
+ }
+ if (m.getName().equals("getString")) {
+ if (paramTypes.length == 0) {
+ return m.invoke(aComponent, new Object[] {});
+ }
+ }
+ if (m.getName().equals("getSelectedItem")) {
+ if (paramTypes.length == 0) {
+ return m.invoke(aComponent, new Object[] {});
+ }
+ }
+ // TODO: should also handle variants of setValue()
+ }
+ } catch (Exception exc) { // error occured while introspecting... move along.
+ System.out.println("InfoPanel.getValueFromComponent: " + exc);
+ }
-/**
-* Gets the component associated with the key as a JTextField, for backwards compatibility.
-* @param key A string representing the key associated with the component. Nulls are converted to an empty string.
-* @return A JTextField that contains the value associated with the key,
-* or null if the key does not exist or if the component is not a JTextField.
-*/
- public JTextField getFieldForKey( String key )
- {
- Component c = getComponentForKey( key );
- if ( c instanceof JTextField )
- {
- return (JTextField) c;
- }
- return null;
- }
+ // not found
+ return null;
+ }
-/**
-* Gets the component associated with the key. If more than one component is associated
-* with the key, returns the first such component.
-* @param key A string representing the key associated with the component.
-* Nulls are converted to an empty string.
-* @return A component that contains the value associated with the key,
-* or null if the key does not exist.
-*/
- public Component getComponentForKey( String key )
- {
- return getComponentForKey( key, 0 );
+ /**
+ * Gets the component associated with the key as a JTextField, for backwards
+ * compatibility.
+ *
+ * @param key A string representing the key associated with the component. Nulls
+ * are converted to an empty string.
+ * @return A JTextField that contains the value associated with the key, or null
+ * if the key does not exist or if the component is not a JTextField.
+ */
+ public JTextField getFieldForKey(String key) {
+ Component c = getComponentForKey(key);
+ if (c instanceof JTextField) {
+ return (JTextField) c;
+ }
+ return null;
}
-/**
-* Gets the component associated with the key and index.
-* @param key A string representing the key associated with the component.
-* Nulls are converted to an empty string.
-* @return A component that contains the value associated with the key,
-* or null if the key does not exist.
-*/
- public Component getComponentForKey( String key, int index )
- {
- Container c = getCompositeComponentForKey( key );
- if ( c == null ) return null;
- return c.getComponent( index );
- }
+ /**
+ * Gets the component associated with the key. If more than one component is
+ * associated with the key, returns the first such component.
+ *
+ * @param key A string representing the key associated with the component. Nulls
+ * are converted to an empty string.
+ * @return A component that contains the value associated with the key, or null
+ * if the key does not exist.
+ */
+ public Component getComponentForKey(String key) {
+ return getComponentForKey(key, 0);
+ }
-/**
-* Gets the component at the specified row. If more than one component exists
-* on that row, returns the first such component.
-* @return A component or null if the row does not exist.
-*/
- public Object getComponentForIndex( int row )
- {
- return getComponentForIndex( row, 0 );
- }
+ /**
+ * Gets the component associated with the key and index.
+ *
+ * @param key A string representing the key associated with the component. Nulls
+ * are converted to an empty string.
+ * @return A component that contains the value associated with the key, or null
+ * if the key does not exist.
+ */
+ public Component getComponentForKey(String key, int index) {
+ Container c = getCompositeComponentForKey(key);
+ if (c == null)
+ return null;
+ return c.getComponent(index);
+ }
-/**
-* Gets the component at the specified row and column.
-* @return A component or null if the index is out of bounds.
-*/
- public Object getComponentForIndex( int row, int col )
- {
- if ( ( row > fields.size() ) || ( row < 0 ) )
- { // out of bounds
- return null;
- }
-
- Container field = (Container) fields.get( row );
- return field.getComponent( col );
- }
+ /**
+ * Gets the component at the specified row. If more than one component exists on
+ * that row, returns the first such component.
+ *
+ * @return A component or null if the row does not exist.
+ */
+ public Object getComponentForIndex(int row) {
+ return getComponentForIndex(row, 0);
+ }
-/**
-* Gets the container associated with the key.
-* @param key A string representing the key associated with the component.
-* Nulls are converted to an empty string.
-* @return A component that contains the value associated with the key,
-* or null if the key does not exist.
-*/
- protected Container getCompositeComponentForKey( String key )
- {
- if ( key == null )
- {
- key = "";
- }
-
- JPanel field = null;
- Iterator enumeration = fields.iterator();
- while ( enumeration.hasNext() )
- { // finds first value in list with specified key
- field = (JPanel) enumeration.next();
- if ( key.equals( field.getName() ) )
- {
- return field;
- }
- }
-
- // else not found
- return null;
- }
+ /**
+ * Gets the component at the specified row and column.
+ *
+ * @return A component or null if the index is out of bounds.
+ */
+ public Object getComponentForIndex(int row, int col) {
+ if ((row > fields.size()) || (row < 0)) { // out of bounds
+ return null;
+ }
-/**
-* Provided for backwards compatibility: calls getLabelComponentForKey.
-* @param key A string representing the key associated with the compoent.
-* Nulls are converted to an empty string.
-* @return Component label object associated with the key, or null if the key does not exist
-* or if the label component is not an instance of JLabel.
-*/
- public JLabel getLabelForKey( String key )
- {
- Component result = getLabelComponentForKey( key );
- if ( result instanceof JLabel ) return (JLabel) result;
- return null;
- }
-
-/**
-* Get the label component associated with the key.
-* @param key A string representing the key associated with the compoent.
-* Nulls are converted to an empty string.
-* @return Component label object associated with the key, or null if the key does not exist.
-*/
- public Component getLabelComponentForKey( String key )
- {
- if ( key == null )
- {
- key = "";
- }
-
- Component label = null;
- Iterator enumeration = labels.iterator();
- while ( enumeration.hasNext() )
- { // finds first value in list with specified key
- label = (Component) enumeration.next();
- if ( key.equals( label.getName() ) )
- {
- return label;
- }
- }
-
- // else not found
- return null;
- }
+ Container field = (Container) fields.get(row);
+ return field.getComponent(col);
+ }
-/**
-* Replaces the first component associated with the key. Any value in the existing
-* component will be copied to the new component.
-* @param key A string representing the key to be associated with the component.
-* Nulls are converted to an empty string.
-* @param c A component to be placed next to the label corresponding to the key.
-* Nulls are converted to a JTextField.
-*/
- public void setComponentForKey( String key, Component c )
- {
- setComponentForKey( key, c, 0 );
+ /**
+ * Gets the container associated with the key.
+ *
+ * @param key A string representing the key associated with the component. Nulls
+ * are converted to an empty string.
+ * @return A component that contains the value associated with the key, or null
+ * if the key does not exist.
+ */
+ protected Container getCompositeComponentForKey(String key) {
+ if (key == null) {
+ key = "";
+ }
+
+ JPanel field = null;
+ Iterator enumeration = fields.iterator();
+ while (enumeration.hasNext()) { // finds first value in list with specified key
+ field = (JPanel) enumeration.next();
+ if (key.equals(field.getName())) {
+ return field;
+ }
+ }
+
+ // else not found
+ return null;
}
-
-/**
-* Replaces the component associated with the key. Any value in the existing
-* component will be copied to the new component.
-* @param key A string representing the key to be associated with the component.
-* Nulls are converted to an empty string.
-* @param c A component to be placed next to the label corresponding to the key.
-* Nulls are converted to a JTextField.
-*/
- public void setComponentForKey( String key, Component c, int index )
- {
- if ( c == null )
- {
- c = new JTextField( 15 );
- }
- if ( key == null )
- {
- key = "";
- }
-
- Container container = this.getCompositeComponentForKey( key );
- Component field = container.getComponent( index );
- Object value = this.getValueForKey( key, index );
- if ( field != null )
- {
- container.remove( index );
- container.add( c, index );
- c.setEnabled( this.isEditable );
- introspectComponent( c, key );
- setValueForComponent( c, value );
- }
- }
-/**
-* Replaces the first component in the specified row. Any value in the existing
-* component will be copied to the new component.
-* @param row A valid index.
-* @param c A component to be placed next to the label corresponding to the key.
-*/
- public void setComponentForIndex( int row, Component c )
- {
- setComponentForIndex( row, 0, c );
+ /**
+ * Provided for backwards compatibility: calls getLabelComponentForKey.
+ *
+ * @param key A string representing the key associated with the compoent. Nulls
+ * are converted to an empty string.
+ * @return Component label object associated with the key, or null if the key
+ * does not exist or if the label component is not an instance of
+ * JLabel.
+ */
+ public JLabel getLabelForKey(String key) {
+ Component result = getLabelComponentForKey(key);
+ if (result instanceof JLabel)
+ return (JLabel) result;
+ return null;
}
-
-/**
-* Replaces the component associated with the key. Any value in the existing
-* component will be copied to the new component.
-* @param row A valid index.
-* @param c A component to be placed next to the label corresponding to the key.
-*/
- public void setComponentForIndex( int row, int col, Component c )
- {
- setComponentForKey( getLabels()[row], c, col );
- }
-/**
-* Sets the string that appears before each label's text on the panel.
-* @param aString A String to be used as the label prefix.
-*/
- public void setLabelPrefix( String aString )
- {
- prefix = aString;
- setLabels( getLabels() ); // force refresh
- }
+ /**
+ * Get the label component associated with the key.
+ *
+ * @param key A string representing the key associated with the compoent. Nulls
+ * are converted to an empty string.
+ * @return Component label object associated with the key, or null if the key
+ * does not exist.
+ */
+ public Component getLabelComponentForKey(String key) {
+ if (key == null) {
+ key = "";
+ }
-/**
-* Gets the string that appears before each label's text on the panel.
-* Defaults to "", an empty string.
-* @return A String that is currently used as the label prefix.
-*/
- public String getLabelPrefix()
- {
- return prefix;
- }
+ Component label = null;
+ Iterator enumeration = labels.iterator();
+ while (enumeration.hasNext()) { // finds first value in list with specified key
+ label = (Component) enumeration.next();
+ if (key.equals(label.getName())) {
+ return label;
+ }
+ }
-/**
-* Sets the string that appears after each label's text on the panel.
-* Defaults to ": ", a colon followed by a space.
-* @param aString A String to be used as the label postfix.
-*/
- public void setLabelPostfix( String aString )
- {
- postfix = aString;
- setLabels( getLabels() ); // force refresh
- }
+ // else not found
+ return null;
+ }
-/**
-* Gets the string that appears after each label's text on the panel.
-* @return A String that is currently used as the label postfix.
-*/
- public String getLabelPostfix()
- {
- return postfix;
- }
+ /**
+ * Replaces the first component associated with the key. Any value in the
+ * existing component will be copied to the new component.
+ *
+ * @param key A string representing the key to be associated with the component.
+ * Nulls are converted to an empty string.
+ * @param c A component to be placed next to the label corresponding to the
+ * key. Nulls are converted to a JTextField.
+ */
+ public void setComponentForKey(String key, Component c) {
+ setComponentForKey(key, c, 0);
+ }
-/**
-* Adds an action listener to the list that will be
-* notified by events occurring in the panel.
-* @param l An action listener to be notified.
-*/
- public void addActionListener(ActionListener l)
- {
- actionListener = AWTEventMulticaster.add(actionListener, l);
- }
-/**
-* Removes an action listener from the list that will be
-* notified by events occurring in the panel.
-* @param l An action listener to be removed.
-*/
- public void removeActionListener(ActionListener l)
- {
- actionListener = AWTEventMulticaster.remove(actionListener, l);
- }
-/**
-* Notifies all registered action listeners of a pending Action Event.
-* @param e An action event to be broadcast.
-*/
- protected void broadcastEvent(ActionEvent e)
- {
- if (actionListener != null)
- {
- actionListener.actionPerformed(e);
- }
- }
+ /**
+ * Replaces the component associated with the key. Any value in the existing
+ * component will be copied to the new component.
+ *
+ * @param key A string representing the key to be associated with the component.
+ * Nulls are converted to an empty string.
+ * @param c A component to be placed next to the label corresponding to the
+ * key. Nulls are converted to a JTextField.
+ */
+ public void setComponentForKey(String key, Component c, int index) {
+ if (c == null) {
+ c = new JTextField(15);
+ }
+ if (key == null) {
+ key = "";
+ }
- // interface ActionListener
+ Container container = this.getCompositeComponentForKey(key);
+ Component field = container.getComponent(index);
+ Object value = this.getValueForKey(key, index);
+ if (field != null) {
+ container.remove(index);
+ container.add(c, index);
+ c.setEnabled(this.isEditable);
+ introspectComponent(c, key);
+ setValueForComponent(c, value);
+ }
+ }
-/**
-* Called by buttons on panel and by other components that
-* might be set to broadcast events to this listener.
-* Simply forwards the action event unchanged.
-* @param e An action event to be received.
-*/
- public void actionPerformed(ActionEvent e)
- {
+ /**
+ * Replaces the first component in the specified row. Any value in the existing
+ * component will be copied to the new component.
+ *
+ * @param row A valid index.
+ * @param c A component to be placed next to the label corresponding to the
+ * key.
+ */
+ public void setComponentForIndex(int row, Component c) {
+ setComponentForIndex(row, 0, c);
+ }
+
+ /**
+ * Replaces the component associated with the key. Any value in the existing
+ * component will be copied to the new component.
+ *
+ * @param row A valid index.
+ * @param c A component to be placed next to the label corresponding to the
+ * key.
+ */
+ public void setComponentForIndex(int row, int col, Component c) {
+ setComponentForKey(getLabels()[row], c, col);
+ }
+
+ /**
+ * Sets the string that appears before each label's text on the panel.
+ *
+ * @param aString A String to be used as the label prefix.
+ */
+ public void setLabelPrefix(String aString) {
+ prefix = aString;
+ setLabels(getLabels()); // force refresh
+ }
+
+ /**
+ * Gets the string that appears before each label's text on the panel. Defaults
+ * to "", an empty string.
+ *
+ * @return A String that is currently used as the label prefix.
+ */
+ public String getLabelPrefix() {
+ return prefix;
+ }
+
+ /**
+ * Sets the string that appears after each label's text on the panel. Defaults
+ * to ": ", a colon followed by a space.
+ *
+ * @param aString A String to be used as the label postfix.
+ */
+ public void setLabelPostfix(String aString) {
+ postfix = aString;
+ setLabels(getLabels()); // force refresh
+ }
+
+ /**
+ * Gets the string that appears after each label's text on the panel.
+ *
+ * @return A String that is currently used as the label postfix.
+ */
+ public String getLabelPostfix() {
+ return postfix;
+ }
+
+ /**
+ * Adds an action listener to the list that will be notified by events occurring
+ * in the panel.
+ *
+ * @param l An action listener to be notified.
+ */
+ public void addActionListener(ActionListener l) {
+ actionListener = AWTEventMulticaster.add(actionListener, l);
+ }
+
+ /**
+ * Removes an action listener from the list that will be notified by events
+ * occurring in the panel.
+ *
+ * @param l An action listener to be removed.
+ */
+ public void removeActionListener(ActionListener l) {
+ actionListener = AWTEventMulticaster.remove(actionListener, l);
+ }
+
+ /**
+ * Notifies all registered action listeners of a pending Action Event.
+ *
+ * @param e An action event to be broadcast.
+ */
+ protected void broadcastEvent(ActionEvent e) {
+ if (actionListener != null) {
+ actionListener.actionPerformed(e);
+ }
+ }
+
+ // interface ActionListener
+
+ /**
+ * Called by buttons on panel and by other components that might be set to
+ * broadcast events to this listener. Simply forwards the action event
+ * unchanged.
+ *
+ * @param e An action event to be received.
+ */
+ public void actionPerformed(ActionEvent e) {
// if ( e.getSource() instanceof AbstractButton )
// {
- broadcastEvent(e);
+ broadcastEvent(e);
// }
- }
-
- /**
- * GridBagLayout allocates weightx only after considering
- * the preferred width of the components in a column.
- * We'd prefer that preferred width wasn't considered,
- * so that the layout worked more like a html-table.
- * GridBagLayout is poorly factored for subclassing,
- * so this code is going to get a little bit ugly.
- * Really, what good is a protected method that returns
- * a private class? Would have liked to just override
- * getLayoutInfo and be done with it.
- */
- private class BetterGridBagLayout extends GridBagLayout
- {
- public Dimension preferredLayoutSize(Container parent)
- {
- preprocess();
- return super.preferredLayoutSize( parent );
- }
-
- public Dimension minimumLayoutSize(Container parent)
- {
- preprocess();
- return super.minimumLayoutSize( parent );
- }
-
-
- public void layoutContainer(Container parent)
- {
- preprocess();
- super.layoutContainer( parent );
- }
-
- protected void preprocess()
- {
- if ( fieldSpacers == null ) return;
- Iterator i;
-
- // find the field with the widest preferred size
- Component c;
- int maxWidth = 0;
- i = fields.iterator();
- while ( i.hasNext() )
- {
- c = (Component) i.next();
- maxWidth = Math.max( maxWidth,
- Math.max( c.getPreferredSize().width, c.getMinimumSize().width ) );
- }
-
- // set each column's spacers to that preferred size
- Dimension min = new Dimension( 0, 0 );
- Dimension pref = new Dimension( maxWidth, 0 );
- i = fieldSpacers.iterator();
- while ( i.hasNext() )
- {
- ((Box.Filler)i.next()).changeShape( min, pref, pref );
- }
- }
- }
-}
+ }
+
+ /**
+ * GridBagLayout allocates weightx only after considering the preferred width of
+ * the components in a column. We'd prefer that preferred width wasn't
+ * considered, so that the layout worked more like a html-table. GridBagLayout
+ * is poorly factored for subclassing, so this code is going to get a little bit
+ * ugly. Really, what good is a protected method that returns a private class?
+ * Would have liked to just override getLayoutInfo and be done with it.
+ */
+ private class BetterGridBagLayout extends GridBagLayout {
+ public Dimension preferredLayoutSize(Container parent) {
+ preprocess();
+ return super.preferredLayoutSize(parent);
+ }
+
+ public Dimension minimumLayoutSize(Container parent) {
+ preprocess();
+ return super.minimumLayoutSize(parent);
+ }
+
+ public void layoutContainer(Container parent) {
+ preprocess();
+ super.layoutContainer(parent);
+ }
+
+ protected void preprocess() {
+ if (fieldSpacers == null)
+ return;
+ Iterator i;
+
+ // find the field with the widest preferred size
+ Component c;
+ int maxWidth = 0;
+ i = fields.iterator();
+ while (i.hasNext()) {
+ c = (Component) i.next();
+ maxWidth = Math.max(maxWidth, Math.max(c.getPreferredSize().width, c.getMinimumSize().width));
+ }
+ // set each column's spacers to that preferred size
+ Dimension min = new Dimension(0, 0);
+ Dimension pref = new Dimension(maxWidth, 0);
+ i = fieldSpacers.iterator();
+ while (i.hasNext()) {
+ ((Box.Filler) i.next()).changeShape(min, pref, pref);
+ }
+ }
+ }
+}
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/KeyDelayTimer.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/KeyDelayTimer.java
index b73c74d..31bdb70 100644
--- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/KeyDelayTimer.java
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/KeyDelayTimer.java
@@ -28,161 +28,150 @@ import java.awt.event.KeyListener;
import javax.swing.Timer;
/**
-* KeyDelayTimer is a utility that listens for KeyEvents from one
-* or more components. After receiving a KeyEvents the timer will
-* broadcast an action event if a specified time interval passes without
-* a subsequent KeyEvent.<BR><BR>
-*
-* This utility is useful for implementing any kind of auto-complete
-* feature in a user interface.
-*
-* @author michael@mpowers.net
-* @author $Author: cgruber $
-* @version $Revision: 904 $
-* $Date: 2006-02-18 18:19:05 -0500 (Sat, 18 Feb 2006) $
-*/
-public class KeyDelayTimer implements ActionListener, KeyListener
-{
- // delay timer for keypress-sensitve events
- protected Timer keyTimer = null;
- protected Component lastFieldTouched = null;
- protected long timeLastFieldTouched = 0;
- protected int interval = 400; // adjust as needed
-
- // for action multicasting
- protected ActionListener actionListener = null;
-
-/**
-* Default constructor.
-*/
- public KeyDelayTimer()
- {
- keyTimer = new Timer( interval, this );
- }
-
-/**
-* Convenience constructor.
-* @param listener An action listener to be notified of delay events.
-*/
- public KeyDelayTimer( ActionListener listener )
- {
- this();
- addActionListener( listener );
- }
-
-/**
-* Returns the last component that generated a KeyEvent.
-* @return The component that sent the most recent KeyEvent.
-*/
- public Component getComponent()
- {
- return lastFieldTouched;
- }
-
-/**
-* Returns the number of milliseconds before an ActionEvent is generated.
-* The default is 400.
-* @return The current delay interval in milliseconds.
-*/
- public int getInterval()
- {
- return interval;
- }
-
-/**
-* Sets the number of milliseconds before an ActionEvent will be generated
-* after a KeyEvent is received.
-* @param millis The new delay interval in milliseconds.
-*/
- public void setInterval( int millis )
- {
- interval = millis;
- keyTimer.setDelay( interval / 2 );
- }
-
- // interface KeyListener
-
- public void keyTyped(KeyEvent e)
- {
- }
- public void keyPressed(KeyEvent e)
- {
- }
-
-/**
-* Receives key events from one or more components.
-* Records the component and the time this event was received,
-* then starts the timer.
-* @param e The key event in question.
-*/
- public void keyReleased(KeyEvent e)
- { // handles keystrokes in the textfields (except ENTER and ESCAPE)
- if ( ( Character.isLetterOrDigit( e.getKeyChar() ) )
- || ( e.getKeyCode() == KeyEvent.VK_SPACE )
- || ( e.getKeyCode() == KeyEvent.VK_DELETE )
- || ( e.getKeyCode() == KeyEvent.VK_BACK_SPACE ) )
- {
- this.lastFieldTouched = e.getComponent();
- this.timeLastFieldTouched = System.currentTimeMillis();
- this.keyTimer.start();
- return;
- }
- }
-
- // interface ActionListener
-
-/**
-* Receives ActionEvents from the internal timer.
-* If the interval has passed without another KeyEvent,
-* an ActionEvent is broadcast, with the name of this class
-* as the ActionCommand, and the internal timer is stopped.
-* @param e The action event in question.
-*/
- public void actionPerformed(ActionEvent e)
- {
- if ( e.getSource() == keyTimer )
- {
- if ( System.currentTimeMillis() - this.timeLastFieldTouched > interval )
- {
- this.keyTimer.stop();
- broadcastEvent( new ActionEvent( this, ActionEvent.ACTION_PERFORMED, this.getClass().getName() ) );
- }
- return;
- }
- }
-
- // Action Multicast methods
-
-/**
-* Adds an action listener to the list that will be
-* notified by button events and changes in button state.
-* @param l An action listener to be notified.
-*/
- public void addActionListener(ActionListener l)
- {
- actionListener = AWTEventMulticaster.add(actionListener, l);
- }
-/**
-* Removes an action listener from the list that will be
-* notified by button events and changes in button state.
-* @param l An action listener to be removed.
-*/
- public void removeActionListener(ActionListener l)
- {
- actionListener = AWTEventMulticaster.remove(actionListener, l);
- }
-/**
-* Notifies all registered action listeners of a pending Action Event.
-* @param e An action event to be broadcast.
-*/
- protected void broadcastEvent(ActionEvent e)
- {
- if (actionListener != null)
- {
- actionListener.actionPerformed(e);
- }
- }
+ * KeyDelayTimer is a utility that listens for KeyEvents from one or more
+ * components. After receiving a KeyEvents the timer will broadcast an action
+ * event if a specified time interval passes without a subsequent KeyEvent.<BR>
+ * <BR>
+ *
+ * This utility is useful for implementing any kind of auto-complete feature in
+ * a user interface.
+ *
+ * @author michael@mpowers.net
+ * @author $Author: cgruber $
+ * @version $Revision: 904 $ $Date: 2006-02-18 18:19:05 -0500 (Sat, 18 Feb 2006)
+ * $
+ */
+public class KeyDelayTimer implements ActionListener, KeyListener {
+ // delay timer for keypress-sensitve events
+ protected Timer keyTimer = null;
+ protected Component lastFieldTouched = null;
+ protected long timeLastFieldTouched = 0;
+ protected int interval = 400; // adjust as needed
+
+ // for action multicasting
+ protected ActionListener actionListener = null;
+
+ /**
+ * Default constructor.
+ */
+ public KeyDelayTimer() {
+ keyTimer = new Timer(interval, this);
+ }
+
+ /**
+ * Convenience constructor.
+ *
+ * @param listener An action listener to be notified of delay events.
+ */
+ public KeyDelayTimer(ActionListener listener) {
+ this();
+ addActionListener(listener);
+ }
+
+ /**
+ * Returns the last component that generated a KeyEvent.
+ *
+ * @return The component that sent the most recent KeyEvent.
+ */
+ public Component getComponent() {
+ return lastFieldTouched;
+ }
+
+ /**
+ * Returns the number of milliseconds before an ActionEvent is generated. The
+ * default is 400.
+ *
+ * @return The current delay interval in milliseconds.
+ */
+ public int getInterval() {
+ return interval;
+ }
+
+ /**
+ * Sets the number of milliseconds before an ActionEvent will be generated after
+ * a KeyEvent is received.
+ *
+ * @param millis The new delay interval in milliseconds.
+ */
+ public void setInterval(int millis) {
+ interval = millis;
+ keyTimer.setDelay(interval / 2);
+ }
+
+ // interface KeyListener
+
+ public void keyTyped(KeyEvent e) {
+ }
+
+ public void keyPressed(KeyEvent e) {
+ }
+
+ /**
+ * Receives key events from one or more components. Records the component and
+ * the time this event was received, then starts the timer.
+ *
+ * @param e The key event in question.
+ */
+ public void keyReleased(KeyEvent e) { // handles keystrokes in the textfields (except ENTER and ESCAPE)
+ if ((Character.isLetterOrDigit(e.getKeyChar())) || (e.getKeyCode() == KeyEvent.VK_SPACE)
+ || (e.getKeyCode() == KeyEvent.VK_DELETE) || (e.getKeyCode() == KeyEvent.VK_BACK_SPACE)) {
+ this.lastFieldTouched = e.getComponent();
+ this.timeLastFieldTouched = System.currentTimeMillis();
+ this.keyTimer.start();
+ return;
+ }
+ }
+
+ // interface ActionListener
+
+ /**
+ * Receives ActionEvents from the internal timer. If the interval has passed
+ * without another KeyEvent, an ActionEvent is broadcast, with the name of this
+ * class as the ActionCommand, and the internal timer is stopped.
+ *
+ * @param e The action event in question.
+ */
+ public void actionPerformed(ActionEvent e) {
+ if (e.getSource() == keyTimer) {
+ if (System.currentTimeMillis() - this.timeLastFieldTouched > interval) {
+ this.keyTimer.stop();
+ broadcastEvent(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, this.getClass().getName()));
+ }
+ return;
+ }
+ }
+
+ // Action Multicast methods
+
+ /**
+ * Adds an action listener to the list that will be notified by button events
+ * and changes in button state.
+ *
+ * @param l An action listener to be notified.
+ */
+ public void addActionListener(ActionListener l) {
+ actionListener = AWTEventMulticaster.add(actionListener, l);
+ }
+
+ /**
+ * Removes an action listener from the list that will be notified by button
+ * events and changes in button state.
+ *
+ * @param l An action listener to be removed.
+ */
+ public void removeActionListener(ActionListener l) {
+ actionListener = AWTEventMulticaster.remove(actionListener, l);
+ }
+
+ /**
+ * Notifies all registered action listeners of a pending Action Event.
+ *
+ * @param e An action event to be broadcast.
+ */
+ protected void broadcastEvent(ActionEvent e) {
+ if (actionListener != null) {
+ actionListener.actionPerformed(e);
+ }
+ }
}
-
-
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/KeyableCellEditor.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/KeyableCellEditor.java
index 95b8a19..f64e607 100644
--- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/KeyableCellEditor.java
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/KeyableCellEditor.java
@@ -40,311 +40,265 @@ import javax.swing.event.ChangeEvent;
import javax.swing.table.TableCellEditor;
/**
-* A table cell editor customized for keyboard navigation, much like
-* working with a spreadsheet. The default cell editor unfortunately
-* does none of these things:
-* <ul>
-* <li> Selects text on start of editing.
-* <li> Up and down keys move edit cell up and down.
-* <li> Right and left keys move cell when selection caret is at end of text.
-* <li> Escape cancels editing.
-* <li> Enter commits edit.
-* <li> Edits are properly committed on lost focus.
-* <li> Tab and shift-tab work as expected.
-* <li> Cell selection moves with the edit cell.
-* </ul>
-*
-* @author michael@mpowers.net
-* @author $Author: cgruber $
-* @version $Revision: 904 $
-* $Date: 2006-02-18 18:19:05 -0500 (Sat, 18 Feb 2006) $
-*/
-public class KeyableCellEditor implements TableCellEditor, FocusListener,
- KeyListener, Serializable
-{
- List listeners;
- JTextField textField;
- Object lastValue;
- Format currentFormat;
-
- JTable table;
-
-/**
-* Default constructor - a standard JTextField will be used for editing.
-*/
- public KeyableCellEditor()
- {
- this( (JTextField) null );
- }
-
-/**
-* Constructor specifying a type of JTextField to be used for editing.
-* The JTextField will have its border replaced with a black line border.
-* @param aTextField A JTextField or subclass for editing values.
-*/
- public KeyableCellEditor( JTextField aTextField )
- {
- listeners = new Vector();
- lastValue = null;
-
- // default to stock JTextField
- textField = aTextField;
- if ( textField == null )
- {
- textField = new JTextField();
- }
-
- textField.setBorder(new LineBorder(Color.black));
-
- // handle arrow keys while caret is showing
- textField.addKeyListener( this );
-
- // handle lost focus
- textField.addFocusListener( this );
- }
-
- public Component getTableCellEditorComponent(JTable table,
- Object value,
- boolean isSelected,
- int row,
- int column)
- {
- this.table = table;
- table.removeKeyListener( this ); // if any
- table.addKeyListener( this );
- return getEditorComponent( value );
- }
-
- protected Component getEditorComponent( Object value )
- {
- if ( value != null )
- {
- textField.setText( value.toString() );
- }
- else
- {
- textField.setText( "" );
- }
-
- if ( value instanceof Number )
- {
- textField.setHorizontalAlignment(JTextField.RIGHT);
- }
- else
- {
- textField.setHorizontalAlignment(JTextField.LEFT);
- }
-
- // remember original value
- lastValue = value;
-
- // select all text and get focus
- textField.selectAll();
- textField.requestFocus();
-
- return textField;
- }
-
- public Object getCellEditorValue()
- {
- return lastValue;
- }
-
- public boolean isCellEditable(EventObject anEvent)
- {
- // key events should replace the selection
- // NOTE: For whatever reason, key events trigger result in a null parameter
- if ( anEvent == null )
- {
- textField.setText("");
- textField.requestFocus();
- return true;
- }
-
- return true;
- }
-
- public boolean shouldSelectCell(EventObject anEvent)
- { // System.out.println( "KeyableCellEditor.shouldSelectCell: " + anEvent );
-
- // key events should replace the selection
- // NOTE: For whatever reason, key events are not generated
- if ( anEvent instanceof KeyEvent )
- {
- textField.setText("");
- textField.requestFocus();
- return true;
- }
-
- // otherwise, select all text and continue
- textField.selectAll();
- textField.requestFocus();
-
- return true;
- }
-
- public boolean stopCellEditing()
- {
- lastValue = textField.getText();
- fireEditingStopped();
- table.removeKeyListener( this ); // if any
- return true;
- }
-
- public void cancelCellEditing()
- {
- fireEditingCanceled();
- table.removeKeyListener( this ); // if any
- }
-
- public void addCellEditorListener(CellEditorListener l)
- {
- listeners.add( l );
- }
-
- public void removeCellEditorListener(CellEditorListener l)
- {
- listeners.remove( l );
- }
-
- protected void fireEditingCanceled()
- {
- ChangeEvent event = new ChangeEvent( this );
- Iterator it = new ArrayList( listeners ).iterator(); // copy to prevent modification exception
- while ( it.hasNext() )
- {
- ((CellEditorListener)it.next()).editingCanceled( event );
- }
- }
-
- protected void fireEditingStopped()
- {
- ChangeEvent event = new ChangeEvent( this );
- Iterator it = new ArrayList( listeners ).iterator(); // copy to prevent modification exception
- while ( it.hasNext() )
- {
- ((CellEditorListener)it.next()).editingStopped( event );
- }
- }
-
- protected void onEnterKey()
- {
- stopCellEditing();
- }
-
- protected void onEscapeKey()
- {
- cancelCellEditing();
- }
-
- protected void moveEditCell( int dRow, int dCol )
- {
- if ( table == null ) return;
- int row = table.getSelectedRow() + dRow;
- int col = table.getSelectedColumn() + dCol;
-
- row = Math.max( 0, row );
- row = Math.min( row, table.getRowCount() - 1 );
- col = Math.max( 0, col );
- col = Math.min( col, table.getColumnCount() - 1 );
-
- stopCellEditing();
- table.setRowSelectionInterval( row, row );
- table.setColumnSelectionInterval( col, col );
- table.editCellAt( row, col );
- textField.selectAll();
- textField.requestFocus();
- }
-
- // interface KeyListener
-
- public void keyTyped(KeyEvent e)
- { // System.out.println( "KeyableCellEditor.keyTyped: " + KeyEvent.getKeyText( e.getKeyCode() ) );
- }
-
- public void keyPressed(KeyEvent e)
- { // System.out.println( "KeyableCellEditor.keyPressed: " + KeyEvent.getKeyText( e.getKeyCode() ) );
-
- // catch LEFT and RIGHT here before JTextField consumes them
-
- int keyCode = e.getKeyCode();
- if ( keyCode == KeyEvent.VK_LEFT )
- {
- if ( textField.getSelectionStart() == 0 )
- {
- moveEditCell( 0, -1 );
- e.consume();
- return;
- }
- }
- if ( keyCode == KeyEvent.VK_RIGHT )
- {
- if ( textField.getSelectionEnd() == textField.getText().length() )
- {
- moveEditCell( 0, 1 );
- e.consume();
- return;
- }
- }
- if ( keyCode == KeyEvent.VK_UP )
- {
- moveEditCell( -1, 0 );
- e.consume();
- return;
- }
- if ( keyCode == KeyEvent.VK_DOWN )
- {
- moveEditCell( 1, 0 );
- e.consume();
- return;
- }
- }
-
- public void keyReleased(KeyEvent e)
- { // System.out.println( "KeyableCellEditor.keyReleased: " + KeyEvent.getKeyText( e.getKeyCode() ) );
-
- // catch ENTER here to allow JTextField to process it as well
-
- int keyCode = e.getKeyCode();
- if ( keyCode == KeyEvent.VK_ENTER )
- {
- onEnterKey();
- return;
- }
- if ( keyCode == KeyEvent.VK_ESCAPE )
- {
- onEscapeKey();
- return;
- }
-
- // tabs are apparently only received on key release
- if ( keyCode == KeyEvent.VK_TAB )
- {
- if ( e.isShiftDown() )
- {
- moveEditCell( 0, -1 );
- }
- else
- {
- moveEditCell( 0, 1 );
- }
- e.consume();
- return;
- }
-
- }
-
- // interface FocusListener
-
- public void focusGained(FocusEvent e)
- { // System.out.println( "focusGained: " );
- }
-
- public void focusLost(FocusEvent e)
- { // System.out.println( "focusLost: " );
- stopCellEditing();
- }
+ * A table cell editor customized for keyboard navigation, much like working
+ * with a spreadsheet. The default cell editor unfortunately does none of these
+ * things:
+ * <ul>
+ * <li>Selects text on start of editing.
+ * <li>Up and down keys move edit cell up and down.
+ * <li>Right and left keys move cell when selection caret is at end of text.
+ * <li>Escape cancels editing.
+ * <li>Enter commits edit.
+ * <li>Edits are properly committed on lost focus.
+ * <li>Tab and shift-tab work as expected.
+ * <li>Cell selection moves with the edit cell.
+ * </ul>
+ *
+ * @author michael@mpowers.net
+ * @author $Author: cgruber $
+ * @version $Revision: 904 $ $Date: 2006-02-18 18:19:05 -0500 (Sat, 18 Feb 2006)
+ * $
+ */
+public class KeyableCellEditor implements TableCellEditor, FocusListener, KeyListener, Serializable {
+ List listeners;
+ JTextField textField;
+ Object lastValue;
+ Format currentFormat;
+
+ JTable table;
+
+ /**
+ * Default constructor - a standard JTextField will be used for editing.
+ */
+ public KeyableCellEditor() {
+ this((JTextField) null);
+ }
+
+ /**
+ * Constructor specifying a type of JTextField to be used for editing. The
+ * JTextField will have its border replaced with a black line border.
+ *
+ * @param aTextField A JTextField or subclass for editing values.
+ */
+ public KeyableCellEditor(JTextField aTextField) {
+ listeners = new Vector();
+ lastValue = null;
+
+ // default to stock JTextField
+ textField = aTextField;
+ if (textField == null) {
+ textField = new JTextField();
+ }
+
+ textField.setBorder(new LineBorder(Color.black));
+
+ // handle arrow keys while caret is showing
+ textField.addKeyListener(this);
+
+ // handle lost focus
+ textField.addFocusListener(this);
+ }
+
+ public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
+ this.table = table;
+ table.removeKeyListener(this); // if any
+ table.addKeyListener(this);
+ return getEditorComponent(value);
+ }
+
+ protected Component getEditorComponent(Object value) {
+ if (value != null) {
+ textField.setText(value.toString());
+ } else {
+ textField.setText("");
+ }
+
+ if (value instanceof Number) {
+ textField.setHorizontalAlignment(JTextField.RIGHT);
+ } else {
+ textField.setHorizontalAlignment(JTextField.LEFT);
+ }
+
+ // remember original value
+ lastValue = value;
+
+ // select all text and get focus
+ textField.selectAll();
+ textField.requestFocus();
+
+ return textField;
+ }
+
+ public Object getCellEditorValue() {
+ return lastValue;
+ }
+
+ public boolean isCellEditable(EventObject anEvent) {
+ // key events should replace the selection
+ // NOTE: For whatever reason, key events trigger result in a null parameter
+ if (anEvent == null) {
+ textField.setText("");
+ textField.requestFocus();
+ return true;
+ }
+
+ return true;
+ }
+
+ public boolean shouldSelectCell(EventObject anEvent) { // System.out.println( "KeyableCellEditor.shouldSelectCell: "
+ // + anEvent );
+
+ // key events should replace the selection
+ // NOTE: For whatever reason, key events are not generated
+ if (anEvent instanceof KeyEvent) {
+ textField.setText("");
+ textField.requestFocus();
+ return true;
+ }
+
+ // otherwise, select all text and continue
+ textField.selectAll();
+ textField.requestFocus();
+
+ return true;
+ }
+
+ public boolean stopCellEditing() {
+ lastValue = textField.getText();
+ fireEditingStopped();
+ table.removeKeyListener(this); // if any
+ return true;
+ }
+
+ public void cancelCellEditing() {
+ fireEditingCanceled();
+ table.removeKeyListener(this); // if any
+ }
+
+ public void addCellEditorListener(CellEditorListener l) {
+ listeners.add(l);
+ }
+
+ public void removeCellEditorListener(CellEditorListener l) {
+ listeners.remove(l);
+ }
+
+ protected void fireEditingCanceled() {
+ ChangeEvent event = new ChangeEvent(this);
+ Iterator it = new ArrayList(listeners).iterator(); // copy to prevent modification exception
+ while (it.hasNext()) {
+ ((CellEditorListener) it.next()).editingCanceled(event);
+ }
+ }
+
+ protected void fireEditingStopped() {
+ ChangeEvent event = new ChangeEvent(this);
+ Iterator it = new ArrayList(listeners).iterator(); // copy to prevent modification exception
+ while (it.hasNext()) {
+ ((CellEditorListener) it.next()).editingStopped(event);
+ }
+ }
+
+ protected void onEnterKey() {
+ stopCellEditing();
+ }
+
+ protected void onEscapeKey() {
+ cancelCellEditing();
+ }
+
+ protected void moveEditCell(int dRow, int dCol) {
+ if (table == null)
+ return;
+ int row = table.getSelectedRow() + dRow;
+ int col = table.getSelectedColumn() + dCol;
+
+ row = Math.max(0, row);
+ row = Math.min(row, table.getRowCount() - 1);
+ col = Math.max(0, col);
+ col = Math.min(col, table.getColumnCount() - 1);
+
+ stopCellEditing();
+ table.setRowSelectionInterval(row, row);
+ table.setColumnSelectionInterval(col, col);
+ table.editCellAt(row, col);
+ textField.selectAll();
+ textField.requestFocus();
+ }
+
+ // interface KeyListener
+
+ public void keyTyped(KeyEvent e) { // System.out.println( "KeyableCellEditor.keyTyped: " + KeyEvent.getKeyText(
+ // e.getKeyCode() ) );
+ }
+
+ public void keyPressed(KeyEvent e) { // System.out.println( "KeyableCellEditor.keyPressed: " + KeyEvent.getKeyText(
+ // e.getKeyCode() ) );
+
+ // catch LEFT and RIGHT here before JTextField consumes them
+
+ int keyCode = e.getKeyCode();
+ if (keyCode == KeyEvent.VK_LEFT) {
+ if (textField.getSelectionStart() == 0) {
+ moveEditCell(0, -1);
+ e.consume();
+ return;
+ }
+ }
+ if (keyCode == KeyEvent.VK_RIGHT) {
+ if (textField.getSelectionEnd() == textField.getText().length()) {
+ moveEditCell(0, 1);
+ e.consume();
+ return;
+ }
+ }
+ if (keyCode == KeyEvent.VK_UP) {
+ moveEditCell(-1, 0);
+ e.consume();
+ return;
+ }
+ if (keyCode == KeyEvent.VK_DOWN) {
+ moveEditCell(1, 0);
+ e.consume();
+ return;
+ }
+ }
+
+ public void keyReleased(KeyEvent e) { // System.out.println( "KeyableCellEditor.keyReleased: " +
+ // KeyEvent.getKeyText( e.getKeyCode() ) );
+
+ // catch ENTER here to allow JTextField to process it as well
+
+ int keyCode = e.getKeyCode();
+ if (keyCode == KeyEvent.VK_ENTER) {
+ onEnterKey();
+ return;
+ }
+ if (keyCode == KeyEvent.VK_ESCAPE) {
+ onEscapeKey();
+ return;
+ }
+
+ // tabs are apparently only received on key release
+ if (keyCode == KeyEvent.VK_TAB) {
+ if (e.isShiftDown()) {
+ moveEditCell(0, -1);
+ } else {
+ moveEditCell(0, 1);
+ }
+ e.consume();
+ return;
+ }
+
+ }
+
+ // interface FocusListener
+
+ public void focusGained(FocusEvent e) { // System.out.println( "focusGained: " );
+ }
+
+ public void focusLost(FocusEvent e) { // System.out.println( "focusLost: " );
+ stopCellEditing();
+ }
}
-
-
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/LineWrappingRenderer.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/LineWrappingRenderer.java
index 4a7f07e..326d825 100644
--- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/LineWrappingRenderer.java
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/LineWrappingRenderer.java
@@ -31,124 +31,111 @@ import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
/**
-* A list cell renderer that wraps its text to subsequent lines
-* depending on the length of text string and the width of the
-* parent list.
-*
-* This renderer depends on listening to the parent list's viewport
-* and fixing the list's width to match the viewport's size.
-*
-* @author michael@mpowers.net
-* @author $Author: cgruber $
-* @date $Date: 2006-02-18 18:19:05 -0500 (Sat, 18 Feb 2006) $
-* @revision $Revision: 904 $
-*/
-public class LineWrappingRenderer extends MultiLineLabel
- implements ListCellRenderer, ChangeListener
-{
- protected static Border noFocusBorder;
-
- protected JList list;
- protected JViewport viewport;
- protected int preferredWidth;
-
-/**
-* Required constructor. The renderer keeps a reference to
-* the list in which it is used and its viewport. This list
-* is the only list that may use this renderer. The renderer
-* will use the current size of the list to determine where
-* lines will initially break.
-* @param containerList The list that will be using this renderer.
-*/
- public LineWrappingRenderer( JList containerList )
- {
- super();
- setLineWrap(true);
- noFocusBorder = new EmptyBorder(1, 1, 1, 1);
-
- list = containerList;
- preferredWidth = 400;
- if ( list.getParent() instanceof JViewport )
- {
- viewport = (JViewport) list.getParent();
- viewport.addChangeListener( this );
- int newWidth = viewport.getExtentSize().width;
- if ( newWidth > 0 ) preferredWidth = newWidth;
- }
- else
- {
- // should function adequately in absence of a viewport
- // System.err.println( "LineWrappingRenderer.init: list.getParent = " + list.getParent() );
- }
- }
-
-/**
-* Returns the preferred size of the label, with width
-* constrained to the current width.
-* @return the size
-*/
- public Dimension getPreferredSize()
- {
- int width = getWidth();
- if ( width != preferredWidth )
- {
- // if component has not yet been placed within the list
- if ( width < list.getWidth() / 2 ) width = list.getWidth();
- preferredWidth = width;
- }
- return new Dimension( preferredWidth, super.getPreferredSize().height );
- }
-
-/**
-* Returns this component with the width set to the
-* width of the specified JList.
-* @return this component.
-*/
- public Component getListCellRendererComponent (
- JList list,
- Object value,
- int index,
- boolean isSelected,
- boolean cellHasFocus )
- { // System.out.println( "LineWrappingRenderer.getListCellRendererComponent:" );
-
- if ( list != this.list )
- {
- System.err.println( "LineWrappingRenderer.getListCellRendererComponent: " +
- "warning: the list using the renderer is not the list specified in the constructor." );
- }
-
- if (isSelected)
- {
- setBackground(this.list.getSelectionBackground());
- setForeground(this.list.getSelectionForeground());
- }
- else
- {
- setBackground(this.list.getBackground());
- setForeground(this.list.getForeground());
- }
-
- setText((value == null) ? "" : value.toString());
-
- setEnabled(this.list.isEnabled());
- setFont(this.list.getFont());
- setBorder( (cellHasFocus) ? UIManager.getBorder("List.focusCellHighlightBorder") : noFocusBorder );
-
- return this;
- }
-
-/**
-* Overridden to respond to viewport changes.
-*/
- public void stateChanged(ChangeEvent e)
- {
- int newWidth = viewport.getExtentSize().width;
- if ( newWidth > 0 ) preferredWidth = newWidth;
-
- // set fixed width on list
- list.setFixedCellWidth( preferredWidth );
- setSize( preferredWidth, super.getSize().height );
- }
+ * A list cell renderer that wraps its text to subsequent lines depending on the
+ * length of text string and the width of the parent list.
+ *
+ * This renderer depends on listening to the parent list's viewport and fixing
+ * the list's width to match the viewport's size.
+ *
+ * @author michael@mpowers.net
+ * @author $Author: cgruber $
+ * @date $Date: 2006-02-18 18:19:05 -0500 (Sat, 18 Feb 2006) $
+ * @revision $Revision: 904 $
+ */
+public class LineWrappingRenderer extends MultiLineLabel implements ListCellRenderer, ChangeListener {
+ protected static Border noFocusBorder;
+
+ protected JList list;
+ protected JViewport viewport;
+ protected int preferredWidth;
+
+ /**
+ * Required constructor. The renderer keeps a reference to the list in which it
+ * is used and its viewport. This list is the only list that may use this
+ * renderer. The renderer will use the current size of the list to determine
+ * where lines will initially break.
+ *
+ * @param containerList The list that will be using this renderer.
+ */
+ public LineWrappingRenderer(JList containerList) {
+ super();
+ setLineWrap(true);
+ noFocusBorder = new EmptyBorder(1, 1, 1, 1);
+
+ list = containerList;
+ preferredWidth = 400;
+ if (list.getParent() instanceof JViewport) {
+ viewport = (JViewport) list.getParent();
+ viewport.addChangeListener(this);
+ int newWidth = viewport.getExtentSize().width;
+ if (newWidth > 0)
+ preferredWidth = newWidth;
+ } else {
+ // should function adequately in absence of a viewport
+ // System.err.println( "LineWrappingRenderer.init: list.getParent = " +
+ // list.getParent() );
+ }
+ }
+
+ /**
+ * Returns the preferred size of the label, with width constrained to the
+ * current width.
+ *
+ * @return the size
+ */
+ public Dimension getPreferredSize() {
+ int width = getWidth();
+ if (width != preferredWidth) {
+ // if component has not yet been placed within the list
+ if (width < list.getWidth() / 2)
+ width = list.getWidth();
+ preferredWidth = width;
+ }
+ return new Dimension(preferredWidth, super.getPreferredSize().height);
+ }
+
+ /**
+ * Returns this component with the width set to the width of the specified
+ * JList.
+ *
+ * @return this component.
+ */
+ public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected,
+ boolean cellHasFocus) { // System.out.println( "LineWrappingRenderer.getListCellRendererComponent:" );
+
+ if (list != this.list) {
+ System.err.println("LineWrappingRenderer.getListCellRendererComponent: "
+ + "warning: the list using the renderer is not the list specified in the constructor.");
+ }
+
+ if (isSelected) {
+ setBackground(this.list.getSelectionBackground());
+ setForeground(this.list.getSelectionForeground());
+ } else {
+ setBackground(this.list.getBackground());
+ setForeground(this.list.getForeground());
+ }
+
+ setText((value == null) ? "" : value.toString());
+
+ setEnabled(this.list.isEnabled());
+ setFont(this.list.getFont());
+ setBorder((cellHasFocus) ? UIManager.getBorder("List.focusCellHighlightBorder") : noFocusBorder);
+
+ return this;
+ }
+
+ /**
+ * Overridden to respond to viewport changes.
+ */
+ public void stateChanged(ChangeEvent e) {
+ int newWidth = viewport.getExtentSize().width;
+ if (newWidth > 0)
+ preferredWidth = newWidth;
+
+ // set fixed width on list
+ list.setFixedCellWidth(preferredWidth);
+ setSize(preferredWidth, super.getSize().height);
+ }
}
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/MultiLineLabel.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/MultiLineLabel.java
index b5f8a9b..ce362c9 100644
--- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/MultiLineLabel.java
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/MultiLineLabel.java
@@ -23,113 +23,100 @@ import javax.swing.LookAndFeel;
import javax.swing.text.Highlighter;
/**
-* A custom JTextArea that looks and feels like a JLabel, but supports
-* line wrapping. This works a lot more like the IFC label component.
-* NOTE: doesn't support icons (yet).
-*
-* @author michael@mpowers.net
-* @author $Author: cgruber $
-* @date $Date: 2006-02-18 18:19:05 -0500 (Sat, 18 Feb 2006) $
-* @revision $Revision: 904 $
-*/
-public class MultiLineLabel extends JTextArea
-{
+ * A custom JTextArea that looks and feels like a JLabel, but supports line
+ * wrapping. This works a lot more like the IFC label component. NOTE: doesn't
+ * support icons (yet).
+ *
+ * @author michael@mpowers.net
+ * @author $Author: cgruber $
+ * @date $Date: 2006-02-18 18:19:05 -0500 (Sat, 18 Feb 2006) $
+ * @revision $Revision: 904 $
+ */
+public class MultiLineLabel extends JTextArea {
/**
- * Saves a reference to the original highlighter
- * to enable/disable text selection.
- */
+ * Saves a reference to the original highlighter to enable/disable text
+ * selection.
+ */
protected Highlighter originalHighlighter;
-
-/*
-* Creates a MultiLineLabel instance with an empty string for the title.
-*/
- public MultiLineLabel()
- {
- super();
- // turn on wrapping and disable editing and highlighting
+ /*
+ * Creates a MultiLineLabel instance with an empty string for the title.
+ */
+ public MultiLineLabel() {
+ super();
- setLineWrap(true);
- setWrapStyleWord(true);
- setEditable(false);
- setSelectable( false );
- }
+ // turn on wrapping and disable editing and highlighting
-/*
-* Creates a MultiLineLabel instance with the specified text.
-* @param text The specified text.
-*/
- public MultiLineLabel( String text )
- {
- super( text );
+ setLineWrap(true);
+ setWrapStyleWord(true);
+ setEditable(false);
+ setSelectable(false);
+ }
- // turn on wrapping and disable editing and highlighting
+ /*
+ * Creates a MultiLineLabel instance with the specified text.
+ *
+ * @param text The specified text.
+ */
+ public MultiLineLabel(String text) {
+ super(text);
- setLineWrap(true);
- setWrapStyleWord(true);
- setEditable(false);
- setSelectable( false );
- }
+ // turn on wrapping and disable editing and highlighting
-/*
-* Overridden to look like a label.
-* @param text The specified text.
-*/
- public void updateUI()
- {
- // got the implementation idea from usenet
+ setLineWrap(true);
+ setWrapStyleWord(true);
+ setEditable(false);
+ setSelectable(false);
+ }
- super.updateUI();
+ /*
+ * Overridden to look like a label.
+ *
+ * @param text The specified text.
+ */
+ public void updateUI() {
+ // got the implementation idea from usenet
- // turn on wrapping and disable editing and highlighting
+ super.updateUI();
- setLineWrap(true);
- setWrapStyleWord(true);
- setEditable(false);
- setSelectable( false );
+ // turn on wrapping and disable editing and highlighting
- // Set the text area's border, colors and font to
- // that of a label
+ setLineWrap(true);
+ setWrapStyleWord(true);
+ setEditable(false);
+ setSelectable(false);
- LookAndFeel.installBorder(this, "Label.border");
+ // Set the text area's border, colors and font to
+ // that of a label
- LookAndFeel.installColorsAndFont(this,
- "Label.background",
- "Label.foreground",
- "Label.font");
- }
+ LookAndFeel.installBorder(this, "Label.border");
-/**
-* Sets whether text is selectable.
-* Default is non-selectable text.
-*/
- public void setSelectable( boolean selectable )
- {
- if ( selectable )
- {
- setHighlighter( originalHighlighter );
- }
- else
- {
+ LookAndFeel.installColorsAndFont(this, "Label.background", "Label.foreground", "Label.font");
+ }
+
+ /**
+ * Sets whether text is selectable. Default is non-selectable text.
+ */
+ public void setSelectable(boolean selectable) {
+ if (selectable) {
+ setHighlighter(originalHighlighter);
+ } else {
originalHighlighter = getHighlighter();
- setHighlighter( null );
+ setHighlighter(null);
}
}
-
-/**
-* Gets whether text is selectable.
-* Default is non-selectable text.
-*/
- public boolean isSelectable()
- {
- return ( getHighlighter() != null );
+
+ /**
+ * Gets whether text is selectable. Default is non-selectable text.
+ */
+ public boolean isSelectable() {
+ return (getHighlighter() != null);
}
-/**
-* Overridden to return false.
-*/
- public boolean isFocusTraversable()
- {
+ /**
+ * Overridden to return false.
+ */
+ public boolean isFocusTraversable() {
return false;
- }
+ }
}
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/NumericTextField.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/NumericTextField.java
index b3d2d03..dee8f27 100644
--- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/NumericTextField.java
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/NumericTextField.java
@@ -19,416 +19,346 @@ License along with this library; if not, see http://www.gnu.org
package net.wotonomy.ui.swing.components;
/**
-* NumericTextField is a "smart" text field that restricts the user's input. The
-* input is restructed to numeric only wich can be of two types: integer and real
-* numbers. A range can also be placed on the text field. The default type is
-* integer, with the being (Integer.MIN_VALUE, Integer.MAX_VALUE).
-*
-* @author rob@straylight.princeton.com
-* @author $Author: cgruber $
-* @version $Revision: 893 $
-*/
-public class NumericTextField extends SmartTextField
-{
-
-/*******************************
-* CONSTANTS
-*******************************/
-
-/**
-* Restrict the text input to integers (whole numbers) only.
-*/
- public final static int INTEGER = 0;
-
-/**
-* Restrict the text input to floating-point numbers only.
-*/
- public final static int FLOAT = 1;
-
- private Number maximumValue = null;
- private Number minimumValue = null;
-
- private boolean sign = false;
- private int newCaretPosition = 0;
- private int valueType = INTEGER;
-
-
-/*******************************
-* PUBLIC METHODS
-*******************************/
-
-/**
-* Default constructor.
-*/
- public NumericTextField()
- {
- this("", 0);
- }
-
-/**
-* Constructor.
-* @param text The initial string the text field is set to.
-*/
- public NumericTextField(String text)
- {
- this(text, 0);
- }
-
-/**
-* Constructor.
-* @param columns Width of the text field (in characters).
-*/
- public NumericTextField(int columns)
- {
- this("", columns);
- }
-
-/**
-* Constructor.
-* @param text The initial string the text field is set to.
-* @param columns Width of the text field (in characters).
-*/
- public NumericTextField(String text, int columns)
- {
- super(text, columns);
- }
-
-/**
-* Sets the upper limit of the range of numbers to accept.
-* @param newMaximumValue The maximum number accepted by the text field.
-*/
- public void setMaximumValue(double newMaximumValue)
- {
- if (newMaximumValue >= 0)
- {
- maximumValue = new Double( newMaximumValue );
- }
- else
- {
- maximumValue = null;
- }
- }
-
-/**
-* Returns the upper limit of the range of numbers to accept.
-* @return The maximum number accepted by this text field.
-*/
- public double getMaximumValue()
- {
- if ( valueType == INTEGER )
- {
- return (maximumValue == null) ? (double) Integer.MAX_VALUE : maximumValue.doubleValue();
- }
- else
- {
- return (maximumValue == null) ? Double.MAX_VALUE : maximumValue.doubleValue();
- }
- }
-
-/**
-* Sets the lower limit of the range of numbers to accept.
-* @param newMinimumValue The minimum number accepted by the text field.
-*/
- public void setMinimumValue(double newMinimumValue)
- {
- if (newMinimumValue <= 0)
- {
- minimumValue = new Double( newMinimumValue );
- }
- else
- {
- minimumValue = null;
- }
- }
-
-/**
-* Returns the lower limit of the range of numbers to accept.
-* @return The minimum number accepted by this text field.
-*/
- public double getMinimumValue()
- {
- if ( valueType == INTEGER )
- {
- return (minimumValue == null) ? (double) Integer.MIN_VALUE : minimumValue.doubleValue();
- }
- else
- {
- return (minimumValue == null) ? -1.0*Double.MAX_VALUE : minimumValue.doubleValue();
- // NOTE: Double.MIN_VALUE returns the smallest positive value - oooops.
- }
- }
-
-/**
-* Sets which type of number this text field can accept.
-* @see #INTEGER
-* @see #FLOAT
-* @param newValueType The type of number to accept.
-*/
- public void setValueType(int newValueType)
- {
- if ((newValueType != INTEGER) && (newValueType != FLOAT))
- {
- valueType = INTEGER;
- }
- else
- {
- valueType = newValueType;
- }
- }
-
-/**
-* Returns which type of number this text field accepts. The default is
-* integer.
-* @see #INTEGER
-* @see #FLOAT
-* @return The type of number to accept.
-*/
- public int getValueType()
- {
- return valueType;
- }
-
-/**
-* Returns the integer numeric value of the string in the text field. The type
-* can be either integer of float.
-* @return The current value in the text field.
-*/
- public int getIntValue()
- {
- int value = 0;
-
- try
- {
- value = Integer.parseInt(getText());
- }
- catch (NumberFormatException e)
- {
- try
- {
- Double dValue = Double.valueOf(getText());
- value = dValue.intValue();
- }
- catch (NumberFormatException ignored) {}
- }
-
- return value;
- }
-
-/**
-* Sets the text field to integer value specified.
-* @param aValue An integer value to display in the text field.
-*/
- public void setIntValue(int aValue)
- {
- setText(Integer.toString(aValue));
- }
-
-/**
-* Returns the real number numeric value of the string in the text field. The type
-* can be either integer of float.
-* @return The current value in the text field.
-*/
- public double getDoubleValue()
- {
- Double value = new Double(0);
-
- try
- {
- value = Double.valueOf(getText());
- }
- catch (NumberFormatException ignored) {}
-
- return value.doubleValue();
- }
-
-/**
-* Sets the text field to the double value specified. If the text field type is
-* FLOAT then the the number is display as a real number. If the text field
-* type is INTEGER then the number is converted to a whole number for displaying.
-* @param aValue A double value to display in the text field.
-*/
- public void setDoubleValue(double aValue)
- {
- Double temp = new Double(aValue);
-
- if (valueType == FLOAT)
- {
- setText(temp.toString());
- }
- else
- {
- setText(Integer.toString(temp.intValue()));
- }
- }
-
-/*******************************
-* PROTECTED METHODS
-*******************************/
-
- protected boolean isValidCharacter(char aChar)
- {
- if (((aChar >= ' ') && (aChar <= '/')) || ((aChar >= ':') && (aChar <= '~')))
- {
- if (aChar == '.')
- {
- if ( valueType == FLOAT )
- {
- return true;
- }
- else
- {
- return false;
- }
- }
- else if (aChar == '-')
- {
- if ( getMinimumValue() < 0 )
- {
- return true;
- }
- else
- {
- return false;
- }
- }
- else if (aChar == '+')
- {
- if ( getMaximumValue() >= 0 )
- {
- return true;
- }
- else
- {
- return false;
- }
- }
- return false;
- }
- return true;
- }
-
- protected boolean isValidString(String aString)
- {
- int iValue = 0;
- double dValue = 0.0;
-
- String tempString = new String(scanForSignChar(aString));
-
- if ( valueType == INTEGER )
- {
- try
- {
- iValue = Integer.parseInt(tempString);
- }
- catch (NumberFormatException e1)
- {
- if ((tempString.compareTo("-") == 0) && (getMinimumValue() < 0.0))
- {
- iValue = 0;
- }
- else
- {
- return false;
- }
- }
- if ((((double)iValue) < getMinimumValue()) || (((double)iValue) > getMaximumValue()))
- {
- return false;
- }
- }
- else
- {
- // Double.valueOf requires a zero before the decimal point
- if ( tempString.startsWith( "." ) )
- {
- tempString = "0" + tempString;
- }
- try
- {
- dValue = Double.valueOf(tempString).doubleValue();
- }
- catch (NumberFormatException e2)
- {
- if ((tempString.compareTo("-") == 0) && (getMinimumValue() < 0.0))
- {
- dValue = 0.0;
- }
- else
- {
- return false;
- }
- }
-
- if ((dValue < getMinimumValue()) || (dValue > getMaximumValue()))
- {
- return false;
- }
- }
-
- return true;
- }
-
- protected void postProcessing()
- {
- if (sign)
- {
- setText(scanForSignChar(getText()));
- setCaretPosition(newCaretPosition);
- }
- sign = false;
- }
-
-
-/*******************************
-* PRIVATE METHODS
-*******************************/
-
- private String scanForSignChar(String aString)
- {
- String newString = "";
- boolean positive = false;
- boolean negative = false;
- int oldCaretPosition = getCaretPosition();
- int charactersAdded = 0;
-
- newCaretPosition = 0;
-
- if (aString.length() <= 0)
- {
- return aString;
- }
-
- for (int i = 0; i < aString.length(); ++i)
- {
- switch (aString.charAt(i))
- {
- case '+': positive = true;
- break;
- case '-': negative = true;
- break;
- default: newString += aString.charAt(i);
- charactersAdded++;
- break;
- }
-
- if ((i + 1) == oldCaretPosition)
- {
- newCaretPosition = charactersAdded;
- }
- }
-
- if ((!(positive)) && (negative))
- {
- newString = "-" + newString;
- newCaretPosition++;
- }
-
- if (positive || negative)
- {
- sign = true;
- }
-
- return newString;
- }
+ * NumericTextField is a "smart" text field that restricts the user's input. The
+ * input is restructed to numeric only wich can be of two types: integer and
+ * real numbers. A range can also be placed on the text field. The default type
+ * is integer, with the being (Integer.MIN_VALUE, Integer.MAX_VALUE).
+ *
+ * @author rob@straylight.princeton.com
+ * @author $Author: cgruber $
+ * @version $Revision: 893 $
+ */
+public class NumericTextField extends SmartTextField {
+
+ /*******************************
+ * CONSTANTS
+ *******************************/
+
+ /**
+ * Restrict the text input to integers (whole numbers) only.
+ */
+ public final static int INTEGER = 0;
+
+ /**
+ * Restrict the text input to floating-point numbers only.
+ */
+ public final static int FLOAT = 1;
+
+ private Number maximumValue = null;
+ private Number minimumValue = null;
+
+ private boolean sign = false;
+ private int newCaretPosition = 0;
+ private int valueType = INTEGER;
+
+ /*******************************
+ * PUBLIC METHODS
+ *******************************/
+
+ /**
+ * Default constructor.
+ */
+ public NumericTextField() {
+ this("", 0);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param text The initial string the text field is set to.
+ */
+ public NumericTextField(String text) {
+ this(text, 0);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param columns Width of the text field (in characters).
+ */
+ public NumericTextField(int columns) {
+ this("", columns);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param text The initial string the text field is set to.
+ * @param columns Width of the text field (in characters).
+ */
+ public NumericTextField(String text, int columns) {
+ super(text, columns);
+ }
+
+ /**
+ * Sets the upper limit of the range of numbers to accept.
+ *
+ * @param newMaximumValue The maximum number accepted by the text field.
+ */
+ public void setMaximumValue(double newMaximumValue) {
+ if (newMaximumValue >= 0) {
+ maximumValue = new Double(newMaximumValue);
+ } else {
+ maximumValue = null;
+ }
+ }
+
+ /**
+ * Returns the upper limit of the range of numbers to accept.
+ *
+ * @return The maximum number accepted by this text field.
+ */
+ public double getMaximumValue() {
+ if (valueType == INTEGER) {
+ return (maximumValue == null) ? (double) Integer.MAX_VALUE : maximumValue.doubleValue();
+ } else {
+ return (maximumValue == null) ? Double.MAX_VALUE : maximumValue.doubleValue();
+ }
+ }
+
+ /**
+ * Sets the lower limit of the range of numbers to accept.
+ *
+ * @param newMinimumValue The minimum number accepted by the text field.
+ */
+ public void setMinimumValue(double newMinimumValue) {
+ if (newMinimumValue <= 0) {
+ minimumValue = new Double(newMinimumValue);
+ } else {
+ minimumValue = null;
+ }
+ }
+
+ /**
+ * Returns the lower limit of the range of numbers to accept.
+ *
+ * @return The minimum number accepted by this text field.
+ */
+ public double getMinimumValue() {
+ if (valueType == INTEGER) {
+ return (minimumValue == null) ? (double) Integer.MIN_VALUE : minimumValue.doubleValue();
+ } else {
+ return (minimumValue == null) ? -1.0 * Double.MAX_VALUE : minimumValue.doubleValue();
+ // NOTE: Double.MIN_VALUE returns the smallest positive value - oooops.
+ }
+ }
+
+ /**
+ * Sets which type of number this text field can accept.
+ *
+ * @see #INTEGER
+ * @see #FLOAT
+ * @param newValueType The type of number to accept.
+ */
+ public void setValueType(int newValueType) {
+ if ((newValueType != INTEGER) && (newValueType != FLOAT)) {
+ valueType = INTEGER;
+ } else {
+ valueType = newValueType;
+ }
+ }
+
+ /**
+ * Returns which type of number this text field accepts. The default is integer.
+ *
+ * @see #INTEGER
+ * @see #FLOAT
+ * @return The type of number to accept.
+ */
+ public int getValueType() {
+ return valueType;
+ }
+
+ /**
+ * Returns the integer numeric value of the string in the text field. The type
+ * can be either integer of float.
+ *
+ * @return The current value in the text field.
+ */
+ public int getIntValue() {
+ int value = 0;
+
+ try {
+ value = Integer.parseInt(getText());
+ } catch (NumberFormatException e) {
+ try {
+ Double dValue = Double.valueOf(getText());
+ value = dValue.intValue();
+ } catch (NumberFormatException ignored) {
+ }
+ }
+
+ return value;
+ }
+
+ /**
+ * Sets the text field to integer value specified.
+ *
+ * @param aValue An integer value to display in the text field.
+ */
+ public void setIntValue(int aValue) {
+ setText(Integer.toString(aValue));
+ }
+
+ /**
+ * Returns the real number numeric value of the string in the text field. The
+ * type can be either integer of float.
+ *
+ * @return The current value in the text field.
+ */
+ public double getDoubleValue() {
+ Double value = new Double(0);
+
+ try {
+ value = Double.valueOf(getText());
+ } catch (NumberFormatException ignored) {
+ }
+
+ return value.doubleValue();
+ }
+
+ /**
+ * Sets the text field to the double value specified. If the text field type is
+ * FLOAT then the the number is display as a real number. If the text field type
+ * is INTEGER then the number is converted to a whole number for displaying.
+ *
+ * @param aValue A double value to display in the text field.
+ */
+ public void setDoubleValue(double aValue) {
+ Double temp = new Double(aValue);
+
+ if (valueType == FLOAT) {
+ setText(temp.toString());
+ } else {
+ setText(Integer.toString(temp.intValue()));
+ }
+ }
+
+ /*******************************
+ * PROTECTED METHODS
+ *******************************/
+
+ protected boolean isValidCharacter(char aChar) {
+ if (((aChar >= ' ') && (aChar <= '/')) || ((aChar >= ':') && (aChar <= '~'))) {
+ if (aChar == '.') {
+ if (valueType == FLOAT) {
+ return true;
+ } else {
+ return false;
+ }
+ } else if (aChar == '-') {
+ if (getMinimumValue() < 0) {
+ return true;
+ } else {
+ return false;
+ }
+ } else if (aChar == '+') {
+ if (getMaximumValue() >= 0) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+ return false;
+ }
+ return true;
+ }
+
+ protected boolean isValidString(String aString) {
+ int iValue = 0;
+ double dValue = 0.0;
+
+ String tempString = new String(scanForSignChar(aString));
+
+ if (valueType == INTEGER) {
+ try {
+ iValue = Integer.parseInt(tempString);
+ } catch (NumberFormatException e1) {
+ if ((tempString.compareTo("-") == 0) && (getMinimumValue() < 0.0)) {
+ iValue = 0;
+ } else {
+ return false;
+ }
+ }
+ if ((((double) iValue) < getMinimumValue()) || (((double) iValue) > getMaximumValue())) {
+ return false;
+ }
+ } else {
+ // Double.valueOf requires a zero before the decimal point
+ if (tempString.startsWith(".")) {
+ tempString = "0" + tempString;
+ }
+ try {
+ dValue = Double.valueOf(tempString).doubleValue();
+ } catch (NumberFormatException e2) {
+ if ((tempString.compareTo("-") == 0) && (getMinimumValue() < 0.0)) {
+ dValue = 0.0;
+ } else {
+ return false;
+ }
+ }
+
+ if ((dValue < getMinimumValue()) || (dValue > getMaximumValue())) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ protected void postProcessing() {
+ if (sign) {
+ setText(scanForSignChar(getText()));
+ setCaretPosition(newCaretPosition);
+ }
+ sign = false;
+ }
+
+ /*******************************
+ * PRIVATE METHODS
+ *******************************/
+
+ private String scanForSignChar(String aString) {
+ String newString = "";
+ boolean positive = false;
+ boolean negative = false;
+ int oldCaretPosition = getCaretPosition();
+ int charactersAdded = 0;
+
+ newCaretPosition = 0;
+
+ if (aString.length() <= 0) {
+ return aString;
+ }
+
+ for (int i = 0; i < aString.length(); ++i) {
+ switch (aString.charAt(i)) {
+ case '+':
+ positive = true;
+ break;
+ case '-':
+ negative = true;
+ break;
+ default:
+ newString += aString.charAt(i);
+ charactersAdded++;
+ break;
+ }
+
+ if ((i + 1) == oldCaretPosition) {
+ newCaretPosition = charactersAdded;
+ }
+ }
+
+ if ((!(positive)) && (negative)) {
+ newString = "-" + newString;
+ newCaretPosition++;
+ }
+
+ if (positive || negative) {
+ sign = true;
+ }
+
+ return newString;
+ }
}
-
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/PropertyEditorTable.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/PropertyEditorTable.java
index 9db2834..996f8e0 100644
--- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/PropertyEditorTable.java
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/PropertyEditorTable.java
@@ -47,233 +47,235 @@ import javax.swing.table.TableColumnModel;
import javax.swing.table.TableModel;
/**
-* PropertyEditorTable is a table designed to display and edit the properties
-* of an object. Because JTable assumes all cells in a column display
-* the same data type, we have to subclass to determine the class
-* based on the cell contents.
-*
-* @author michael@mpowers.net
-* @author $Author: cgruber $
-* @version $Revision: 904 $
-*/
+ * PropertyEditorTable is a table designed to display and edit the properties of
+ * an object. Because JTable assumes all cells in a column display the same data
+ * type, we have to subclass to determine the class based on the cell contents.
+ *
+ * @author michael@mpowers.net
+ * @author $Author: cgruber $
+ * @version $Revision: 904 $
+ */
public class PropertyEditorTable extends JTable {
//
// Constructors
//
- /**
- * Constructs a default JTable which is initialized with a default
- * data model, a default column model, and a default selection
- * model.
- *
- * @see #createDefaultDataModel
- * @see #createDefaultColumnModel
- * @see #createDefaultSelectionModel
- */
- public PropertyEditorTable() {
- super(null, null, null);
- }
-
- /**
- * Constructs a JTable which is initialized with <i>dm</i> as the
- * data model, a default column model, and a default selection
- * model.
- *
- * @param dm The data model for the table
- * @see #createDefaultColumnModel
- * @see #createDefaultSelectionModel
- */
- public PropertyEditorTable(TableModel dm) {
- super(dm, null, null);
- }
-
- /**
- * Constructs a JTable which is initialized with <i>dm</i> as the
- * data model, <i>cm</i> as the column model, and a default selection
- * model.
- *
- * @param dm The data model for the table
- * @param cm The column model for the table
- * @see #createDefaultSelectionModel
- */
- public PropertyEditorTable(TableModel dm, TableColumnModel cm) {
- super(dm, cm, null);
- }
-
- /**
- * Constructs a JTable which is initialized with <i>dm</i> as the
- * data model, <i>cm</i> as the column model, and <i>sm</i> as the
- * selection model. If any of the parameters are <b>null</b> this
- * method will initialize the table with the corresponding
- * default model. The <i>autoCreateColumnsFromModel</i> flag is set
- * to false if <i>cm</i> is non-null, otherwise it is set to true
- * and the column model is populated with suitable TableColumns
- * for the columns in <i>dm</i>.
- *
- * @param dm The data model for the table
- * @param cm The column model for the table
- * @param sm The row selection model for the table
- * @see #createDefaultDataModel
- * @see #createDefaultColumnModel
- * @see #createDefaultSelectionModel
- */
- public PropertyEditorTable(TableModel dm, TableColumnModel cm, ListSelectionModel sm) {
- super( dm, cm, sm );
+ /**
+ * Constructs a default JTable which is initialized with a default data model, a
+ * default column model, and a default selection model.
+ *
+ * @see #createDefaultDataModel
+ * @see #createDefaultColumnModel
+ * @see #createDefaultSelectionModel
+ */
+ public PropertyEditorTable() {
+ super(null, null, null);
}
- /**
- * Constructs a JTable with <i>numRows</i> and <i>numColumns</i> of
- * empty cells using the DefaultTableModel. The columns will have
- * names of the form "A", "B", "C", etc.
- *
- * @param numRows The number of rows the table holds
- * @param numColumns The number of columns the table holds
- */
- public PropertyEditorTable(int numRows, int numColumns) {
- super( numRows, numColumns );
- }
-
- /**
- * Constructs a JTable to display the values in the Vector of Vectors,
- * <i>rowData</i>, with column names, <i>columnNames</i>.
- * The Vectors contained in <i>rowData</i> should contain the values
- * for that row. In other words, the value of the cell at row 1,
- * column 5 can be obtained with the following code:
- * <p>
- * <pre>((Vector)rowData.elementAt(1)).elementAt(5);</pre>
- * <p>
- * All rows must be of the same length as <i>columnNames</i>.
- * <p>
- * @param rowData The data for the new table
- * @param columnNames Names of each column
- */
- public PropertyEditorTable(final Vector rowData, final Vector columnNames) {
- super( rowData, columnNames );
- }
-
- /**
- * Constructs a JTable to display the values in the two dimensional array,
- * <i>rowData</i>, with column names, <i>columnNames</i>.
- * <i>rowData</i> is an Array of rows, so the value of the cell at row 1,
- * column 5 can be obtained with the following code:
- * <p>
- * <pre> rowData[1][5]; </pre>
- * <p>
- * All rows must be of the same length as <i>columnNames</i>.
- * <p>
- * @param rowData The data for the new table
- * @param columnNames Names of each column
- */
- public PropertyEditorTable(final Object[][] rowData, final Object[] columnNames) {
- super( rowData, columnNames );
+ /**
+ * Constructs a JTable which is initialized with <i>dm</i> as the data model, a
+ * default column model, and a default selection model.
+ *
+ * @param dm The data model for the table
+ * @see #createDefaultColumnModel
+ * @see #createDefaultSelectionModel
+ */
+ public PropertyEditorTable(TableModel dm) {
+ super(dm, null, null);
}
- /**
- * Returns the type of the column at the specified view position.
- *
- * @return the type of the column at position <I>column</I> in the view
- * where the first column is column 0.
+ /**
+ * Constructs a JTable which is initialized with <i>dm</i> as the data model,
+ * <i>cm</i> as the column model, and a default selection model.
*
- * Modified mln: now a wrapper for getCellClass()
+ * @param dm The data model for the table
+ * @param cm The column model for the table
+ * @see #createDefaultSelectionModel
+ */
+ public PropertyEditorTable(TableModel dm, TableColumnModel cm) {
+ super(dm, cm, null);
+ }
+
+ /**
+ * Constructs a JTable which is initialized with <i>dm</i> as the data model,
+ * <i>cm</i> as the column model, and <i>sm</i> as the selection model. If any
+ * of the parameters are <b>null</b> this method will initialize the table with
+ * the corresponding default model. The <i>autoCreateColumnsFromModel</i> flag
+ * is set to false if <i>cm</i> is non-null, otherwise it is set to true and the
+ * column model is populated with suitable TableColumns for the columns in
+ * <i>dm</i>.
*
- */
- public Class getColumnClass(int column) {
- return getCellClass( 0, column );
- }
-
- /**
- * Returns the type of the cell at the specified view position.
- *
- * @return the type of the cell at position <I>row</I>, <I>column</I> in the view
- * where the first column is column 0.
+ * @param dm The data model for the table
+ * @param cm The column model for the table
+ * @param sm The row selection model for the table
+ * @see #createDefaultDataModel
+ * @see #createDefaultColumnModel
+ * @see #createDefaultSelectionModel
+ */
+ public PropertyEditorTable(TableModel dm, TableColumnModel cm, ListSelectionModel sm) {
+ super(dm, cm, sm);
+ }
+
+ /**
+ * Constructs a JTable with <i>numRows</i> and <i>numColumns</i> of empty cells
+ * using the DefaultTableModel. The columns will have names of the form "A",
+ * "B", "C", etc.
*
- * Modified mln: new methods
+ * @param numRows The number of rows the table holds
+ * @param numColumns The number of columns the table holds
+ */
+ public PropertyEditorTable(int numRows, int numColumns) {
+ super(numRows, numColumns);
+ }
+
+ /**
+ * Constructs a JTable to display the values in the Vector of Vectors,
+ * <i>rowData</i>, with column names, <i>columnNames</i>. The Vectors contained
+ * in <i>rowData</i> should contain the values for that row. In other words, the
+ * value of the cell at row 1, column 5 can be obtained with the following code:
+ * <p>
+ *
+ * <pre>
+ * ((Vector) rowData.elementAt(1)).elementAt(5);
+ * </pre>
+ * <p>
+ * All rows must be of the same length as <i>columnNames</i>.
+ * <p>
+ *
+ * @param rowData The data for the new table
+ * @param columnNames Names of each column
+ */
+ public PropertyEditorTable(final Vector rowData, final Vector columnNames) {
+ super(rowData, columnNames);
+ }
+
+ /**
+ * Constructs a JTable to display the values in the two dimensional array,
+ * <i>rowData</i>, with column names, <i>columnNames</i>. <i>rowData</i> is an
+ * Array of rows, so the value of the cell at row 1, column 5 can be obtained
+ * with the following code:
+ * <p>
+ *
+ * <pre>
+ * rowData[1][5];
+ * </pre>
+ * <p>
+ * All rows must be of the same length as <i>columnNames</i>.
+ * <p>
+ *
+ * @param rowData The data for the new table
+ * @param columnNames Names of each column
+ */
+ public PropertyEditorTable(final Object[][] rowData, final Object[] columnNames) {
+ super(rowData, columnNames);
+ }
+
+ /**
+ * Returns the type of the column at the specified view position.
+ *
+ * @return the type of the column at position <I>column</I> in the view where
+ * the first column is column 0.
+ *
+ * Modified mln: now a wrapper for getCellClass()
+ *
+ */
+ public Class getColumnClass(int column) {
+ return getCellClass(0, column);
+ }
+
+ /**
+ * Returns the type of the cell at the specified view position.
+ *
+ * @return the type of the cell at position <I>row</I>, <I>column</I> in the
+ * view where the first column is column 0.
*
- */
- public Class getCellClass(int row, int column) {
+ * Modified mln: new methods
+ *
+ */
+ public Class getCellClass(int row, int column) {
TableModel model = getModel();
- if ( model instanceof PropertyEditorTableModel )
- return ((PropertyEditorTableModel)model).getCellClass( row, column );
- else
+ if (model instanceof PropertyEditorTableModel)
+ return ((PropertyEditorTableModel) model).getCellClass(row, column);
+ else
return model.getColumnClass(convertColumnIndexToModel(column));
- }
-
- /**
- * Return an appropriate renderer for the cell specified by this this row and
- * column. If the TableColumn for this column has a non-null renderer, return that.
- * If not, find the class of the data in this column (using getColumnClass())
- * and return the default renderer for this type of data.
- *
- * @param row the row of the cell to render, where 0 is the first
- * @param column the column of the cell to render, where 0 is the first
+ }
+
+ /**
+ * Return an appropriate renderer for the cell specified by this this row and
+ * column. If the TableColumn for this column has a non-null renderer, return
+ * that. If not, find the class of the data in this column (using
+ * getColumnClass()) and return the default renderer for this type of data.
+ *
+ * @param row the row of the cell to render, where 0 is the first
+ * @param column the column of the cell to render, where 0 is the first
+ *
+ * Modified mln: calls getCellClass if there's no column model
*
- * Modified mln: calls getCellClass if there's no column model
+ */
+ public TableCellRenderer getCellRenderer(int row, int column) {
+ TableColumn tableColumn = getColumnModel().getColumn(column);
+ TableCellRenderer renderer = tableColumn.getCellRenderer();
+ if (renderer == null) {
+ renderer = getDefaultRenderer(getCellClass(row, column));
+ }
+ return renderer;
+ }
+
+ /**
+ * Return an appropriate editor for the cell specified by this this row and
+ * column. If the TableColumn for this column has a non-null editor, return
+ * that. If not, find the class of the data in this column (using
+ * getColumnClass()) and return the default editor for this type of data.
*
- */
- public TableCellRenderer getCellRenderer(int row, int column) {
- TableColumn tableColumn = getColumnModel().getColumn(column);
- TableCellRenderer renderer = tableColumn.getCellRenderer();
- if (renderer == null) {
- renderer = getDefaultRenderer(getCellClass(row, column));
- }
- return renderer;
- }
-
-
- /**
- * Return an appropriate editor for the cell specified by this this row and
- * column. If the TableColumn for this column has a non-null editor, return that.
- * If not, find the class of the data in this column (using getColumnClass())
- * and return the default editor for this type of data.
- *
- * @param row the row of the cell to edit, where 0 is the first
- * @param column the column of the cell to edit, where 0 is the first
+ * @param row the row of the cell to edit, where 0 is the first
+ * @param column the column of the cell to edit, where 0 is the first
*
- * Modified mp: calls getCellClass if there's no column model
+ * Modified mp: calls getCellClass if there's no column model
*
- */
- public TableCellEditor getCellEditor(int row, int column) {
- TableColumn tableColumn = getColumnModel().getColumn(column);
- TableCellEditor editor = tableColumn.getCellEditor();
- if (editor == null) {
- editor = getDefaultEditor(getCellClass(row, column));
- }
- return editor;
- }
+ */
+ public TableCellEditor getCellEditor(int row, int column) {
+ TableColumn tableColumn = getColumnModel().getColumn(column);
+ TableCellEditor editor = tableColumn.getCellEditor();
+ if (editor == null) {
+ editor = getDefaultEditor(getCellClass(row, column));
+ }
+ return editor;
+ }
protected void createDefaultRenderers() {
super.createDefaultRenderers();
-/* // copying this code here as a sample of creating a renderer
- // Dates
- DefaultTableCellRenderer dateRenderer = new DefaultTableCellRenderer() {
- DateFormat formatter = DateFormat.getDateInstance();
- public void setValue(Object value) {
- setText((value == null) ? "" : formatter.format(value)); }
- };
- dateRenderer.setHorizontalAlignment(JLabel.RIGHT);
- setDefaultRenderer(Date.class, dateRenderer);
-*/
-
- DefaultTableCellRenderer fontRenderer = new DefaultTableCellRenderer() {
- public void setValue(Object value) {
- setText( getFontDescription( (Font) value ) );
+ /*
+ * // copying this code here as a sample of creating a renderer // Dates
+ * DefaultTableCellRenderer dateRenderer = new DefaultTableCellRenderer() {
+ * DateFormat formatter = DateFormat.getDateInstance(); public void
+ * setValue(Object value) { setText((value == null) ? "" :
+ * formatter.format(value)); } };
+ * dateRenderer.setHorizontalAlignment(JLabel.RIGHT);
+ * setDefaultRenderer(Date.class, dateRenderer);
+ */
+
+ DefaultTableCellRenderer fontRenderer = new DefaultTableCellRenderer() {
+ public void setValue(Object value) {
+ setText(getFontDescription((Font) value));
}
};
- fontRenderer.setHorizontalAlignment(JLabel.RIGHT);
- setDefaultRenderer(Font.class, fontRenderer);
+ fontRenderer.setHorizontalAlignment(JLabel.RIGHT);
+ setDefaultRenderer(Font.class, fontRenderer);
- setUpColorRenderer( this );
- setUpMethodRenderer( this );
- }
+ setUpColorRenderer(this);
+ setUpMethodRenderer(this);
+ }
- protected String getFontDescription( Font f ) {
+ protected String getFontDescription(Font f) {
String s;
- if ( f != null ) {
+ if (f != null) {
s = f.getName();
- if ( f.isBold() ) s += " Bold";
- if ( f.isItalic() ) s += " Italic";
+ if (f.isBold())
+ s += " Bold";
+ if (f.isItalic())
+ s += " Italic";
s += " " + f.getSize();
} else {
s = "";
@@ -281,292 +283,263 @@ public class PropertyEditorTable extends JTable {
return s;
}
- protected void createDefaultEditors() {
+ protected void createDefaultEditors() {
super.createDefaultEditors();
-/* // copying this code here as a sample of creating an editor
- // Numbers
- JTextField rightAlignedTextField = new JTextField();
- rightAlignedTextField.setHorizontalAlignment(JTextField.RIGHT);
- rightAlignedTextField.setBorder(new LineBorder(Color.black));
- setDefaultEditor(Number.class, new DefaultCellEditor(rightAlignedTextField));
-
- // Booleans
- JCheckBox centeredCheckBox = new JCheckBox();
- centeredCheckBox.setHorizontalAlignment(JCheckBox.CENTER);
- setDefaultEditor(Boolean.class, new DefaultCellEditor(centeredCheckBox));
-*/
- setUpColorEditor( this );
- setUpMethodEditor( this );
+ /*
+ * // copying this code here as a sample of creating an editor // Numbers
+ * JTextField rightAlignedTextField = new JTextField();
+ * rightAlignedTextField.setHorizontalAlignment(JTextField.RIGHT);
+ * rightAlignedTextField.setBorder(new LineBorder(Color.black));
+ * setDefaultEditor(Number.class, new DefaultCellEditor(rightAlignedTextField));
+ *
+ * // Booleans JCheckBox centeredCheckBox = new JCheckBox();
+ * centeredCheckBox.setHorizontalAlignment(JCheckBox.CENTER);
+ * setDefaultEditor(Boolean.class, new DefaultCellEditor(centeredCheckBox));
+ */
+ setUpColorEditor(this);
+ setUpMethodEditor(this);
}
-
- // following code lifted from:
+ // following code lifted from:
// http://java.sun.com/docs/books/tutorial/ui/swing/example-swing/TableDialogEditDemo.java
- class ColorRenderer extends JLabel
- implements TableCellRenderer {
- Border unselectedBorder = null;
- Border selectedBorder = null;
- boolean isBordered = true;
-
- public ColorRenderer(boolean isBordered) {
- super();
- this.isBordered = isBordered;
- this.setOpaque(true); //MUST do this for background to show up.
- }
-
- public Component getTableCellRendererComponent(
- JTable table, Object color,
- boolean isSelected, boolean hasFocus,
- int row, int column) {
- this.setBackground((Color)color);
- if (isBordered) {
- if (isSelected) {
- if (selectedBorder == null) {
- selectedBorder = BorderFactory.createMatteBorder(2,5,2,5,
- table.getSelectionBackground());
- }
- this.setBorder(selectedBorder);
- } else {
- if (unselectedBorder == null) {
- unselectedBorder = BorderFactory.createMatteBorder(2,5,2,5,
- table.getBackground());
- }
- this.setBorder(unselectedBorder);
- }
- }
- return this;
- }
- }
-
- private void setUpColorRenderer(JTable table) {
- table.setDefaultRenderer(Color.class,
- new ColorRenderer(true));
- }
-
- //Set up the editor for the Color cells.
- private void setUpColorEditor(JTable table) {
- //First, set up the button that brings up the dialog.
- final JButton button = new JButton("") {
- public void setText(String s) {
- //Button never shows text -- only color.
- }
- };
- button.setBackground(Color.white);
- button.setBorderPainted(false);
- button.setMargin(new Insets(0,0,0,0));
-
- //Now create an editor to encapsulate the button, and
- //set it up as the editor for all Color cells.
- final ColorEditor colorEditor = new ColorEditor(button);
- table.setDefaultEditor(Color.class, colorEditor);
-
- //Set up the dialog that the button brings up.
- final JColorChooser colorChooser = new JColorChooser();
- //XXX: PENDING: add the following when setPreviewPanel
- //XXX: starts working.
- //JComponent preview = new ColorRenderer(false);
- //preview.setPreferredSize(new Dimension(50, 10));
- //colorChooser.setPreviewPanel(preview);
- ActionListener okListener = new ActionListener() {
- public void actionPerformed(ActionEvent e) {
- colorEditor.currentColor = colorChooser.getColor();
- }
- };
- final JDialog dialog = JColorChooser.createDialog(button,
- "Pick a Color",
- true,
- colorChooser,
- okListener,
- null); //XXXDoublecheck this is OK
-
- //Here's the code that brings up the dialog.
- button.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e) {
- button.setBackground(colorEditor.currentColor);
- colorChooser.setColor(colorEditor.currentColor);
- //Without the following line, the dialog comes up
- //in the middle of the screen.
- //dialog.setLocationRelativeTo(button);
- dialog.show();
- }
- });
- }
-
- /*
- * The editor button that brings up the dialog.
- * We extend DefaultCellEditor for convenience,
- * even though it mean we have to create a dummy
- * check box. Another approach would be to copy
- * the implementation of TableCellEditor methods
- * from the source code for DefaultCellEditor.
- */
- class ColorEditor extends DefaultCellEditor {
- Color currentColor = null;
-
- public ColorEditor(JButton b) {
- super(new JCheckBox()); //Unfortunately, the constructor
- //expects a check box, combo box,
- //or text field.
- editorComponent = b;
- setClickCountToStart(1); //This is usually 1 or 2.
-
- //Must do this so that editing stops when appropriate.
- b.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e) {
- fireEditingStopped();
- }
- });
- }
-
- protected void fireEditingStopped() {
- super.fireEditingStopped();
- }
-
- public Object getCellEditorValue() {
- return currentColor;
- }
-
- public Component getTableCellEditorComponent(JTable table,
- Object value,
- boolean isSelected,
- int row,
- int column) {
- ((JButton)editorComponent).setText(value.toString());
- currentColor = (Color)value;
- return editorComponent;
- }
- }
-
- class MethodRenderer extends JLabel
- implements TableCellRenderer {
-
- Method theMethod = null;
- JTable theTable = null;
-
- public MethodRenderer() {
- super();
- }
-
- public Component getTableCellRendererComponent(
- JTable table, Object method,
- boolean isSelected, boolean hasFocus,
- int row, int column) {
- theMethod = (Method) method;
- theTable = table;
- setText( " " + theMethod.getReturnType().getName() );
- return this;
- }
- }
-
- private void setUpMethodRenderer(JTable table) {
- table.setDefaultRenderer(Method.class,
- new MethodRenderer());
- }
-
- /*
- * We extend DefaultCellEditor for convenience,
- * as with ColorEditor.
- */
- class MethodEditor extends DefaultCellEditor {
- Method theMethod = null;
- JTable theTable = null;
-
- public MethodEditor(JButton b) {
- super(new JCheckBox()); //Unfortunately, the constructor
- //expects a check box, combo box,
- //or text field.
- editorComponent = b;
- setClickCountToStart(1); //This is usually 1 or 2.
-
- //Must do this so that editing stops when appropriate.
- b.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e) {
- fireEditingStopped();
- }
- });
- }
-
- protected void fireEditingStopped() {
- super.fireEditingStopped();
- }
-
- public Object getCellEditorValue() {
- return theMethod;
- }
-
- public Component getTableCellEditorComponent(JTable table,
- Object value,
- boolean isSelected,
- int row,
- int column) {
- ((JButton)editorComponent).setText(value.toString());
- theMethod = (Method)value;
- theTable = table;
- return editorComponent;
- }
- }
-
- //Set up the editor for the Method cells.
- private void setUpMethodEditor(PropertyEditorTable table) {
- //First, set up the button that brings up the dialog.
- final JButton button = new JButton("invoking method") {
- public void setText(String s) {
- //Button never shows text -- only color.
- }
- };
- button.setBackground(Color.white);
- button.setBorderPainted(false);
- button.setMargin(new Insets(0,0,0,0));
-
- //Now create an editor to encapsulate the button, and
- //set it up as the editor for all Color cells.
- final MethodEditor methodEditor = new MethodEditor(button);
- table.setDefaultEditor(Method.class, methodEditor);
-
- // handle the button-click
- final PropertyEditorTable theTable = table;
- button.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e) {
-
- Component parent = SwingUtilities.getRoot( theTable );
- if ( parent == null ) parent = theTable;
-
- Cursor oldCursor = parent.getCursor();
- parent.setCursor( Cursor.getPredefinedCursor( Cursor.WAIT_CURSOR ) );
-
- Object result = null;
- Object inspectedObject = ((PropertyEditorTableModel)
- methodEditor.theTable.getModel()).inspectedObject;
- try
- {
- methodEditor.theMethod.setAccessible( true );
- result = methodEditor.theMethod.invoke(
- inspectedObject, (Object[])null );
- }
- catch ( Exception exc )
- {
- System.err.println( "PropertyEditorTable.MethodRenderer.actionPerformed: " +
- "Error occurred: " + exc );
- }
- theTable.methodInvoked( inspectedObject, methodEditor.theMethod, result );
-
- parent.setCursor( oldCursor );
- }
- });
- }
+ class ColorRenderer extends JLabel implements TableCellRenderer {
+ Border unselectedBorder = null;
+ Border selectedBorder = null;
+ boolean isBordered = true;
-/**
-* Called by the method cell editor when a method is invoked.
-* @param anObject The object upon which the method was invoked.
-* @param aMethod The method that was invoked.
-* @param aResult The result of the method invocation; may be null.
-*/
- public void methodInvoked( Object anObject, Method aMethod, Object aResult )
- {
- System.out.println( aMethod.getName() + ": " + aResult );
- }
-}
+ public ColorRenderer(boolean isBordered) {
+ super();
+ this.isBordered = isBordered;
+ this.setOpaque(true); // MUST do this for background to show up.
+ }
+ public Component getTableCellRendererComponent(JTable table, Object color, boolean isSelected, boolean hasFocus,
+ int row, int column) {
+ this.setBackground((Color) color);
+ if (isBordered) {
+ if (isSelected) {
+ if (selectedBorder == null) {
+ selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5, table.getSelectionBackground());
+ }
+ this.setBorder(selectedBorder);
+ } else {
+ if (unselectedBorder == null) {
+ unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5, table.getBackground());
+ }
+ this.setBorder(unselectedBorder);
+ }
+ }
+ return this;
+ }
+ }
+
+ private void setUpColorRenderer(JTable table) {
+ table.setDefaultRenderer(Color.class, new ColorRenderer(true));
+ }
+ // Set up the editor for the Color cells.
+ private void setUpColorEditor(JTable table) {
+ // First, set up the button that brings up the dialog.
+ final JButton button = new JButton("") {
+ public void setText(String s) {
+ // Button never shows text -- only color.
+ }
+ };
+ button.setBackground(Color.white);
+ button.setBorderPainted(false);
+ button.setMargin(new Insets(0, 0, 0, 0));
+
+ // Now create an editor to encapsulate the button, and
+ // set it up as the editor for all Color cells.
+ final ColorEditor colorEditor = new ColorEditor(button);
+ table.setDefaultEditor(Color.class, colorEditor);
+
+ // Set up the dialog that the button brings up.
+ final JColorChooser colorChooser = new JColorChooser();
+ // XXX: PENDING: add the following when setPreviewPanel
+ // XXX: starts working.
+ // JComponent preview = new ColorRenderer(false);
+ // preview.setPreferredSize(new Dimension(50, 10));
+ // colorChooser.setPreviewPanel(preview);
+ ActionListener okListener = new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ colorEditor.currentColor = colorChooser.getColor();
+ }
+ };
+ final JDialog dialog = JColorChooser.createDialog(button, "Pick a Color", true, colorChooser, okListener, null); // XXXDoublecheck
+ // this
+ // is
+ // OK
+
+ // Here's the code that brings up the dialog.
+ button.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ button.setBackground(colorEditor.currentColor);
+ colorChooser.setColor(colorEditor.currentColor);
+ // Without the following line, the dialog comes up
+ // in the middle of the screen.
+ // dialog.setLocationRelativeTo(button);
+ dialog.show();
+ }
+ });
+ }
+
+ /*
+ * The editor button that brings up the dialog. We extend DefaultCellEditor for
+ * convenience, even though it mean we have to create a dummy check box. Another
+ * approach would be to copy the implementation of TableCellEditor methods from
+ * the source code for DefaultCellEditor.
+ */
+ class ColorEditor extends DefaultCellEditor {
+ Color currentColor = null;
+
+ public ColorEditor(JButton b) {
+ super(new JCheckBox()); // Unfortunately, the constructor
+ // expects a check box, combo box,
+ // or text field.
+ editorComponent = b;
+ setClickCountToStart(1); // This is usually 1 or 2.
+
+ // Must do this so that editing stops when appropriate.
+ b.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ fireEditingStopped();
+ }
+ });
+ }
+
+ protected void fireEditingStopped() {
+ super.fireEditingStopped();
+ }
+
+ public Object getCellEditorValue() {
+ return currentColor;
+ }
+
+ public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row,
+ int column) {
+ ((JButton) editorComponent).setText(value.toString());
+ currentColor = (Color) value;
+ return editorComponent;
+ }
+ }
+
+ class MethodRenderer extends JLabel implements TableCellRenderer {
+
+ Method theMethod = null;
+ JTable theTable = null;
+
+ public MethodRenderer() {
+ super();
+ }
+
+ public Component getTableCellRendererComponent(JTable table, Object method, boolean isSelected,
+ boolean hasFocus, int row, int column) {
+ theMethod = (Method) method;
+ theTable = table;
+ setText(" " + theMethod.getReturnType().getName());
+ return this;
+ }
+ }
+
+ private void setUpMethodRenderer(JTable table) {
+ table.setDefaultRenderer(Method.class, new MethodRenderer());
+ }
+
+ /*
+ * We extend DefaultCellEditor for convenience, as with ColorEditor.
+ */
+ class MethodEditor extends DefaultCellEditor {
+ Method theMethod = null;
+ JTable theTable = null;
+
+ public MethodEditor(JButton b) {
+ super(new JCheckBox()); // Unfortunately, the constructor
+ // expects a check box, combo box,
+ // or text field.
+ editorComponent = b;
+ setClickCountToStart(1); // This is usually 1 or 2.
+
+ // Must do this so that editing stops when appropriate.
+ b.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ fireEditingStopped();
+ }
+ });
+ }
+
+ protected void fireEditingStopped() {
+ super.fireEditingStopped();
+ }
+
+ public Object getCellEditorValue() {
+ return theMethod;
+ }
+
+ public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row,
+ int column) {
+ ((JButton) editorComponent).setText(value.toString());
+ theMethod = (Method) value;
+ theTable = table;
+ return editorComponent;
+ }
+ }
+
+ // Set up the editor for the Method cells.
+ private void setUpMethodEditor(PropertyEditorTable table) {
+ // First, set up the button that brings up the dialog.
+ final JButton button = new JButton("invoking method") {
+ public void setText(String s) {
+ // Button never shows text -- only color.
+ }
+ };
+ button.setBackground(Color.white);
+ button.setBorderPainted(false);
+ button.setMargin(new Insets(0, 0, 0, 0));
+
+ // Now create an editor to encapsulate the button, and
+ // set it up as the editor for all Color cells.
+ final MethodEditor methodEditor = new MethodEditor(button);
+ table.setDefaultEditor(Method.class, methodEditor);
+
+ // handle the button-click
+ final PropertyEditorTable theTable = table;
+ button.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+
+ Component parent = SwingUtilities.getRoot(theTable);
+ if (parent == null)
+ parent = theTable;
+
+ Cursor oldCursor = parent.getCursor();
+ parent.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
+
+ Object result = null;
+ Object inspectedObject = ((PropertyEditorTableModel) methodEditor.theTable.getModel()).inspectedObject;
+ try {
+ methodEditor.theMethod.setAccessible(true);
+ result = methodEditor.theMethod.invoke(inspectedObject, (Object[]) null);
+ } catch (Exception exc) {
+ System.err
+ .println("PropertyEditorTable.MethodRenderer.actionPerformed: " + "Error occurred: " + exc);
+ }
+ theTable.methodInvoked(inspectedObject, methodEditor.theMethod, result);
+
+ parent.setCursor(oldCursor);
+ }
+ });
+ }
+
+ /**
+ * Called by the method cell editor when a method is invoked.
+ *
+ * @param anObject The object upon which the method was invoked.
+ * @param aMethod The method that was invoked.
+ * @param aResult The result of the method invocation; may be null.
+ */
+ public void methodInvoked(Object anObject, Method aMethod, Object aResult) {
+ System.out.println(aMethod.getName() + ": " + aResult);
+ }
+}
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/PropertyEditorTableModel.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/PropertyEditorTableModel.java
index f6a2a8d..f6f80f2 100644
--- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/PropertyEditorTableModel.java
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/PropertyEditorTableModel.java
@@ -31,30 +31,28 @@ import javax.swing.Timer;
import javax.swing.table.AbstractTableModel;
/**
-* PropertyEditorTableModel introspects an object to facilitate
-* editing it in a PropertyTable.
-*
-* Because the model always reflects the current state of the
-* inspected object, it is useful to have a table update at
-* automated intervals. By default, this feature is turned off.
-* If you turn it on, you'll want to remember to turn it off
-* when you're done with the table model.
-*
-* @author michael@mpowers.net
-* @author $Author: cgruber $
-* @version $Revision: 904 $
-*/
-public class PropertyEditorTableModel extends AbstractTableModel implements ActionListener
-{
+ * PropertyEditorTableModel introspects an object to facilitate editing it in a
+ * PropertyTable.
+ *
+ * Because the model always reflects the current state of the inspected object,
+ * it is useful to have a table update at automated intervals. By default, this
+ * feature is turned off. If you turn it on, you'll want to remember to turn it
+ * off when you're done with the table model.
+ *
+ * @author michael@mpowers.net
+ * @author $Author: cgruber $
+ * @version $Revision: 904 $
+ */
+public class PropertyEditorTableModel extends AbstractTableModel implements ActionListener {
protected Object inspectedObject = null;
final String[] columnNames = { "Property", "Value" };
- static final String METHOD_TAG = " ";
+ static final String METHOD_TAG = " ";
Vector properties = new Vector();
Hashtable methods = new Hashtable(0);
- public void setObject( Object o ) {
+ public void setObject(Object o) {
inspectedObject = o;
Class c = o.getClass();
Method[] m = c.getMethods();
@@ -62,67 +60,56 @@ public class PropertyEditorTableModel extends AbstractTableModel implements Acti
properties = new Vector();
methods = new Hashtable(m.length, 1F);
String name, propertyName;
- for ( int i = 0; i < m.length; i++ ) {
+ for (int i = 0; i < m.length; i++) {
// if ( m[i].getName().startsWith( "get" ) )
// System.out.println( m[i].getName() + ": " + m[i].getReturnType().getName() );
// get methods
- if (
- ( ( m[i].getName().startsWith( "get" ) ) || ( m[i].getName().startsWith( "is" ) ) )
- && ( m[i].getParameterTypes().length == 0 )
- ) {
- name = m[i].getName();
- if ( m[i].getName().startsWith( "is" ) ) {
- propertyName = name.substring( 2, name.length() );
- // probably should only add "is" methods if accompanied by "set" method
- } else { // "get"
- propertyName = name.substring( 3, name.length() );
- }
- if (
- ( m[i].getReturnType().getName().equals( String.class.getName() ) )
- || ( m[i].getReturnType().getName().equals( "boolean" ) )
- || ( m[i].getReturnType().getName().equals( "int" ) )
- || ( m[i].getReturnType().getName().equals( "float" ) )
- || ( m[i].getReturnType().getName().equals( "char" ) )
- || ( m[i].getReturnType().getName().equals( Color.class.getName() ) )
- || ( m[i].getReturnType().getName().equals( Font.class.getName() ) )
- ) {
- properties.addElement( propertyName ); // put property
- methods.put( name, m[i] ); // put method
+ if (((m[i].getName().startsWith("get")) || (m[i].getName().startsWith("is")))
+ && (m[i].getParameterTypes().length == 0)) {
+ name = m[i].getName();
+ if (m[i].getName().startsWith("is")) {
+ propertyName = name.substring(2, name.length());
+ // probably should only add "is" methods if accompanied by "set" method
+ } else { // "get"
+ propertyName = name.substring(3, name.length());
}
- else
- {
- // handle unknown types as invokable methods:
- properties.addElement( propertyName + METHOD_TAG );
- methods.put( propertyName + METHOD_TAG, m[i] );
- }
- }
- else
+ if ((m[i].getReturnType().getName().equals(String.class.getName()))
+ || (m[i].getReturnType().getName().equals("boolean"))
+ || (m[i].getReturnType().getName().equals("int"))
+ || (m[i].getReturnType().getName().equals("float"))
+ || (m[i].getReturnType().getName().equals("char"))
+ || (m[i].getReturnType().getName().equals(Color.class.getName()))
+ || (m[i].getReturnType().getName().equals(Font.class.getName()))) {
+ properties.addElement(propertyName); // put property
+ methods.put(name, m[i]); // put method
+ } else {
+ // handle unknown types as invokable methods:
+ properties.addElement(propertyName + METHOD_TAG);
+ methods.put(propertyName + METHOD_TAG, m[i]);
+ }
+ } else
// set methods
- if ( ( m[i].getName().startsWith( "set" ) ) &&
- ( m[i].getParameterTypes().length == 1 ) ) {
+ if ((m[i].getName().startsWith("set")) && (m[i].getParameterTypes().length == 1)) {
name = m[i].getName();
- if (
- ( m[i].getParameterTypes()[0].getName().equals( String.class.getName() ) )
- || ( m[i].getParameterTypes()[0].getName().equals( "boolean" ) )
- || ( m[i].getParameterTypes()[0].getName().equals( "int" ) )
- || ( m[i].getParameterTypes()[0].getName().equals( "float" ) )
- || ( m[i].getParameterTypes()[0].getName().equals( Color.class.getName() ) )
+ if ((m[i].getParameterTypes()[0].getName().equals(String.class.getName()))
+ || (m[i].getParameterTypes()[0].getName().equals("boolean"))
+ || (m[i].getParameterTypes()[0].getName().equals("int"))
+ || (m[i].getParameterTypes()[0].getName().equals("float"))
+ || (m[i].getParameterTypes()[0].getName().equals(Color.class.getName()))
// || ( m[i].getParameterTypes()[0].getName().equals( Font.class.getName() ) )
) {
// System.out.println( "Accepted: " + name + ": " + m[i].getParameterTypes()[0].getName() );
- methods.put( name, m[i] ); // set method
+ methods.put(name, m[i]); // set method
} else {
// System.out.println( "Rejected: " + name + ": " + m[i].getParameterTypes()[0].getName() );
}
+ } else
+ // zero-parameter methods to be invoked on click
+ if (m[i].getParameterTypes().length == 0) {
+ properties.addElement(m[i].getName() + METHOD_TAG);
+ methods.put(m[i].getName() + METHOD_TAG, m[i]);
}
- else
- // zero-parameter methods to be invoked on click
- if ( m[i].getParameterTypes().length == 0 )
- {
- properties.addElement( m[i].getName() + METHOD_TAG );
- methods.put( m[i].getName() + METHOD_TAG, m[i] );
- }
}
@@ -131,288 +118,259 @@ public class PropertyEditorTableModel extends AbstractTableModel implements Acti
}
public int getColumnCount() {
- return columnNames.length;
+ return columnNames.length;
}
public int getRowCount() {
- return properties.size();
+ return properties.size();
}
public String getColumnName(int col) {
- return columnNames[col];
+ return columnNames[col];
}
public Object getValueAt(int row, int col) {
- if ( col == 0 )
- return properties.elementAt( row );
- else
- {
+ if (col == 0)
+ return properties.elementAt(row);
+ else {
Method m = null;
- m = (Method) methods.get( "get" + ( (String) properties.elementAt( row ) ) ) ;
- if ( m == null ) // try "is"
- m = (Method) methods.get( "is" + ( (String) properties.elementAt( row ) ) ) ;
- if ( m == null ) { // try entire method name
- m = (Method) methods.get( (String) properties.elementAt( row ) ) ;
- if ( m != null ) return m;
- }
+ m = (Method) methods.get("get" + ((String) properties.elementAt(row)));
+ if (m == null) // try "is"
+ m = (Method) methods.get("is" + ((String) properties.elementAt(row)));
+ if (m == null) { // try entire method name
+ m = (Method) methods.get((String) properties.elementAt(row));
+ if (m != null)
+ return m;
+ }
try {
- return m.invoke( inspectedObject, (Object[])null );
- } catch ( Exception exc ) {
- System.out.println( "InspectorFrame.tableModel.getValueAt: error occured while reflecting: " );
- System.out.println( exc );
+ return m.invoke(inspectedObject, (Object[]) null);
+ } catch (Exception exc) {
+ System.out.println("InspectorFrame.tableModel.getValueAt: error occured while reflecting: ");
+ System.out.println(exc);
}
return null;
}
}
- public Class getColumnClass( int col ) {
+ public Class getColumnClass(int col) {
// System.out.println( "getColumnClass" );
-/* try {
- throw new Exception();
- } catch ( Exception exc ) {
- exc.printStackTrace( System.out );
- }
-*/ return new String().getClass();
+ /*
+ * try { throw new Exception(); } catch ( Exception exc ) { exc.printStackTrace(
+ * System.out ); }
+ */ return new String().getClass();
}
public Class getCellClass(int row, int col) {
// System.out.println( "getCellClass" );
-/*
-
- Class c;
- Method m = (Method) methods.get( "set" + ( (String) properties.elementAt( row ) ) ) ;
- if ( m == null )
- c = new Object().getClass();
- else {
- c = m.getParameterTypes()[0];
-
- // special case for boolean
- if ( c.getName().equals( "boolean" ) )
- c = new Boolean(true).getClass();
-
- // special case for int
- if ( c.getName().equals( "int" ) )
- c = new Integer(0).getClass();
- }
- System.out.println( row + ": " + c.getName() );
- return c;
-*/
+ /*
+ *
+ * Class c; Method m = (Method) methods.get( "set" + ( (String)
+ * properties.elementAt( row ) ) ) ; if ( m == null ) c = new
+ * Object().getClass(); else { c = m.getParameterTypes()[0];
+ *
+ * // special case for boolean if ( c.getName().equals( "boolean" ) ) c = new
+ * Boolean(true).getClass();
+ *
+ * // special case for int if ( c.getName().equals( "int" ) ) c = new
+ * Integer(0).getClass(); } System.out.println( row + ": " + c.getName() );
+ * return c;
+ */
// return new String().getClass();
- Object o = getValueAt( row, col );
- if ( o == null ) o = "null";
+ Object o = getValueAt(row, col);
+ if (o == null)
+ o = "null";
return o.getClass();
}
/*
- * Don't need to implement this method unless your table's
- * editable.
- */
+ * Don't need to implement this method unless your table's editable.
+ */
public boolean isCellEditable(int row, int col) {
- //Note that the data/cell address is constant,
- //no matter where the cell appears onscreen.
- if (col < 1) {
- return false;
- } else {
- // handle method invocation
- if ( ((String)properties.elementAt(row)).endsWith(METHOD_TAG) ) return true;
- // handle read-only properties
- Method m = (Method) methods.get( "set" + ( (String) properties.elementAt( row ) ) ) ;
- if ( m == null )
+ // Note that the data/cell address is constant,
+ // no matter where the cell appears onscreen.
+ if (col < 1) {
return false;
- else
- return true;
- }
+ } else {
+ // handle method invocation
+ if (((String) properties.elementAt(row)).endsWith(METHOD_TAG))
+ return true;
+ // handle read-only properties
+ Method m = (Method) methods.get("set" + ((String) properties.elementAt(row)));
+ if (m == null)
+ return false;
+ else
+ return true;
+ }
}
/*
- * Don't need to implement this method unless your table's
- * data can change.
- */
+ * Don't need to implement this method unless your table's data can change.
+ */
public void setValueAt(Object value, int row, int col) {
- // test for inspected object
- if ( inspectedObject == null ) return;
- // handle method invocation - no need to update values
- if ( ((String)properties.elementAt(row)).endsWith( METHOD_TAG ) )
- {
- fireTableDataChanged();
- return;
- };
-
- // handle writable properties
- Method m = (Method) methods.get( "set" + ( (String) properties.elementAt( row ) ) ) ;
+ // test for inspected object
+ if (inspectedObject == null)
+ return;
+ // handle method invocation - no need to update values
+ if (((String) properties.elementAt(row)).endsWith(METHOD_TAG)) {
+ fireTableDataChanged();
+ return;
+ }
+ ;
+
+ // handle writable properties
+ Method m = (Method) methods.get("set" + ((String) properties.elementAt(row)));
String parameterType = m.getParameterTypes()[0].getName();
// ugly cast code
- if (
- ( parameterType.equals( "int" ) )
- || ( parameterType.equals( "java.lang.Integer" ) )
- )
- {
- try {
- value = new Integer((String)value);
- } catch (NumberFormatException e) {
- System.out.println("PropertyEditorTableModel.setValueAt: User attempted to enter non-integer"
- + " value (" + value
- + ") into an integer-only column.");
- }
+ if ((parameterType.equals("int")) || (parameterType.equals("java.lang.Integer"))) {
+ try {
+ value = new Integer((String) value);
+ } catch (NumberFormatException e) {
+ System.out.println("PropertyEditorTableModel.setValueAt: User attempted to enter non-integer"
+ + " value (" + value + ") into an integer-only column.");
+ }
}
Object[] parameters = { value };
try {
- m.invoke( inspectedObject, parameters );
- if ( inspectedObject instanceof Component ) {
- Component c = (Component)inspectedObject;
- if ( c.getParent() != null )
+ m.invoke(inspectedObject, parameters);
+ if (inspectedObject instanceof Component) {
+ Component c = (Component) inspectedObject;
+ if (c.getParent() != null)
c.getParent().repaint();
}
- } catch ( Exception exc ) {
- System.out.println( "PropertyEditorTableModel.setValueAt: error occured while reflecting: " );
- System.out.println( exc );
+ } catch (Exception exc) {
+ System.out.println("PropertyEditorTableModel.setValueAt: error occured while reflecting: ");
+ System.out.println(exc);
}
fireTableDataChanged();
}
+ protected void sort(Vector v) {
+ quickSort(v, 0, v.size() - 1);
+ }
- protected void sort(Vector v){
- quickSort(v, 0, v.size()-1);
- }
-
-
- // Liberated from the BasicDirectoryModel which was...
- // Liberated from the 1.1 SortDemo
-
- // This is a generic version of C.A.R Hoare's Quick Sort
- // algorithm. This will handle arrays that are already
- // sorted, and arrays with duplicate keys.<BR>
- //
- // If you think of a one dimensional array as going from
- // the lowest index on the left to the highest index on the right
- // then the parameters to this function are lowest index or
- // left and highest index or right. The first time you call
- // this function it will be with the parameters 0, a.length - 1.
- //
- // @param a an integer array
- // @param lo0 left boundary of array partition
- // @param hi0 right boundary of array partition
- private void quickSort(Vector v, int lo0, int hi0) {
- int lo = lo0;
- int hi = hi0;
- String mid;
-
- if (hi0 > lo0) {
- // Arbitrarily establishing partition element as the midpoint of
- // the array.
- mid = (String) v.elementAt((lo0 + hi0) / 2);
-
- // loop through the array until indices cross
- while(lo <= hi) {
- // find the first element that is greater than or equal to
- // the partition element starting from the left Index.
- //
- // Nasty to have to cast here. Would it be quicker
- // to copy the vectors into arrays and sort the arrays?
- while((lo < hi0) && lt((String)v.elementAt(lo), mid)) {
- ++lo;
- }
+ // Liberated from the BasicDirectoryModel which was...
+ // Liberated from the 1.1 SortDemo
+
+ // This is a generic version of C.A.R Hoare's Quick Sort
+ // algorithm. This will handle arrays that are already
+ // sorted, and arrays with duplicate keys.<BR>
+ //
+ // If you think of a one dimensional array as going from
+ // the lowest index on the left to the highest index on the right
+ // then the parameters to this function are lowest index or
+ // left and highest index or right. The first time you call
+ // this function it will be with the parameters 0, a.length - 1.
+ //
+ // @param a an integer array
+ // @param lo0 left boundary of array partition
+ // @param hi0 right boundary of array partition
+ private void quickSort(Vector v, int lo0, int hi0) {
+ int lo = lo0;
+ int hi = hi0;
+ String mid;
+
+ if (hi0 > lo0) {
+ // Arbitrarily establishing partition element as the midpoint of
+ // the array.
+ mid = (String) v.elementAt((lo0 + hi0) / 2);
+
+ // loop through the array until indices cross
+ while (lo <= hi) {
+ // find the first element that is greater than or equal to
+ // the partition element starting from the left Index.
+ //
+ // Nasty to have to cast here. Would it be quicker
+ // to copy the vectors into arrays and sort the arrays?
+ while ((lo < hi0) && lt((String) v.elementAt(lo), mid)) {
+ ++lo;
+ }
+
+ // find an element that is smaller than or equal to
+ // the partition element starting from the right Index.
+ while ((hi > lo0) && lt(mid, (String) v.elementAt(hi))) {
+ --hi;
+ }
+
+ // if the indexes have not crossed, swap
+ if (lo <= hi) {
+ swap(v, lo, hi);
+ ++lo;
+ --hi;
+ }
+ }
+
+ // If the right index has not reached the left side of array
+ // must now sort the left partition.
+ if (lo0 < hi) {
+ quickSort(v, lo0, hi);
+ }
+
+ // If the left index has not reached the right side of array
+ // must now sort the right partition.
+ if (lo < hi0) {
+ quickSort(v, lo, hi0);
+ }
- // find an element that is smaller than or equal to
- // the partition element starting from the right Index.
- while((hi > lo0) && lt(mid, (String)v.elementAt(hi))) {
- --hi;
}
+ }
+
+ private void swap(Vector a, int i, int j) {
+ Object T = a.elementAt(i);
+ a.setElementAt(a.elementAt(j), i);
+ a.setElementAt(T, j);
+ }
+
+ protected boolean lt(String a, String b) {
+ return a.compareTo(b) < 0;
+ }
+
+ // automated updates
- // if the indexes have not crossed, swap
- if(lo <= hi) {
- swap(v, lo, hi);
- ++lo;
- --hi;
+ private boolean autoUpdating = false;
+ private int updateInterval = 2000; // one-second delay on average
+ protected Timer timer = null;
+
+ public boolean isAutoUpdating() {
+ return autoUpdating;
+ }
+
+ public void setAutoUpdating(boolean shouldAutoUpdate) {
+ if (shouldAutoUpdate) {
+ if (timer == null) {
+ timer = new Timer(updateInterval, this);
+ timer.setRepeats(true);
+ timer.start();
+ }
+ } else {
+ if (timer != null) {
+ timer.stop();
+ timer = null;
+ }
}
- }
+ autoUpdating = shouldAutoUpdate;
+ }
+
+ public int getUpdateInterval() {
+ return updateInterval;
+ }
- // If the right index has not reached the left side of array
- // must now sort the left partition.
- if(lo0 < hi) {
- quickSort(v, lo0, hi);
- }
+ public void setUpdateInterval(int anInterval) {
+ if (timer != null) {
+ timer.setDelay(anInterval);
+ }
- // If the left index has not reached the right side of array
- // must now sort the right partition.
- if(lo < hi0) {
- quickSort(v, lo, hi0);
- }
+ updateInterval = anInterval;
+ }
+ public void actionPerformed(ActionEvent evt) {
+ if (evt.getSource() == timer) {
+ fireTableDataChanged();
+ }
}
- }
-
- private void swap(Vector a, int i, int j) {
- Object T = a.elementAt(i);
- a.setElementAt(a.elementAt(j), i);
- a.setElementAt(T, j);
- }
-
- protected boolean lt(String a, String b) {
- return a.compareTo(b) < 0;
- }
-
- // automated updates
-
- private boolean autoUpdating = false;
- private int updateInterval = 2000; // one-second delay on average
- protected Timer timer = null;
-
- public boolean isAutoUpdating()
- {
- return autoUpdating;
- }
-
- public void setAutoUpdating( boolean shouldAutoUpdate )
- {
- if ( shouldAutoUpdate )
- {
- if ( timer == null )
- {
- timer = new Timer( updateInterval, this );
- timer.setRepeats( true );
- timer.start();
- }
- }
- else
- {
- if ( timer != null )
- {
- timer.stop();
- timer = null;
- }
- }
-
- autoUpdating = shouldAutoUpdate;
- }
-
- public int getUpdateInterval()
- {
- return updateInterval;
- }
-
- public void setUpdateInterval( int anInterval )
- {
- if ( timer != null )
- {
- timer.setDelay( anInterval );
- }
-
- updateInterval = anInterval;
- }
-
- public void actionPerformed( ActionEvent evt )
- {
- if ( evt.getSource() == timer )
- {
- fireTableDataChanged();
- }
- }
}
-
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/RadioButtonPanel.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/RadioButtonPanel.java
index 2956c71..62cbc2c 100644
--- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/RadioButtonPanel.java
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/RadioButtonPanel.java
@@ -26,149 +26,139 @@ import javax.swing.JRadioButton;
import javax.swing.border.EmptyBorder;
/**
-* RadioButtonPanel is a simple extension of ButtonPanel.
-* Differences are that it uses radio buttons and the
-* default alignment is vertical. The radio buttons are
-* placed in a ButtonGroup and the panel defaults to having
-* no buttons selected.
-*
-* @author michael@mpowers.net
-* @author $Author: cgruber $
-* @version $Revision: 904 $
-* $Date: 2006-02-18 18:19:05 -0500 (Sat, 18 Feb 2006) $
-*/
-public class RadioButtonPanel extends ButtonPanel
-{
-/**
-* A ButtonGroup to help manage button state.
-*/
- protected ButtonGroup buttonGroup;
-
-/**
-* ButtonGroup does not make it easy to unselect all buttons.
-* The preferred way to do it is actually to create a hidden button.
-*/
- protected JRadioButton hiddenButton;
-
-/**
-* Constructs a RadioButtonPanel. Three buttons are created
-* so the panel is filled when used in a GUI-builder environment.
-*/
- public RadioButtonPanel()
- {
- super();
- }
-
-/**
-* Constructs a ButtonPanel using specified buttons.
-* @param buttonList An array containing the strings to be used in labeling the buttons.
-*/
- public RadioButtonPanel( String[] buttonList )
- {
- super( buttonList );
- }
-
-/**
-* Overridden to set vertical-center alignment and 2-pixel vgap.
-*/
- protected void initLayout()
- {
- super.initLayout();
- buttonPanelLayout.setAlignment( BetterFlowLayout.CENTER_VERTICAL );
- buttonPanelLayout.setVgap( 2 ); // looks nicer than java l&f recommendation (imho)
- }
-
-/**
-* Overridden to return a JRadioButton.
-* @param aLabel The label for the component that will be created.
-* @return The newly created component.
-*/
- protected Component createComponentWithLabel( String aLabel )
- {
- String buttonLabel = aLabel;
- JRadioButton newButton = new JRadioButton();
- newButton.setName( aLabel );
- newButton.setText( buttonLabel );
- newButton.setActionCommand( aLabel );
- newButton.addActionListener( this );
-
- // reduce insets per java l&f guidelines (was 4 on each side)
- newButton.setBorder( new EmptyBorder( 0, 4, 0, 4 ) );
-
- if ( buttonGroup == null )
- {
- buttonGroup = new ButtonGroup();
-
- // cheesy hack to allow a buttongroup to have no items selected.
- // note that the button is not added to container or buttonList.
- hiddenButton = new JRadioButton( "Hidden Button" );
- buttonGroup.add( hiddenButton );
- }
- buttonGroup.add( newButton );
-
- return newButton;
- }
-
-/**
-* Selects the button whose name matches the given text value.
-* @param newText A String matching the name of one of the buttons.
-* If null, empty, or not matching, all buttons are deselected.
-*/
- public void setValue(String aName)
- {
- if ( aName != null )
- {
- Component c = null;
- int count = buttonContainer.getComponentCount();
- for ( int i = 0; i < count; i++ )
- {
- c = buttonContainer.getComponent( i );
- if ( c instanceof AbstractButton )
- {
- if ( c.getName().equals( aName ) )
- {
- ((AbstractButton)c).setSelected( true );
- return;
- }
- }
- }
- }
-
- // null, empty, or not matching - deselect all
- hiddenButton.setSelected( true );
- }
-
-/**
-* Gets the name of the currently selected button.
-* @return A string matching the name of the currently selected button,
-* or null of no button is selected.
-*/
- public String getValue()
- {
- String result = null;
- Component c = null;
- int count = buttonContainer.getComponentCount();
- for ( int i = 0; i < count; i++ )
- {
- c = buttonContainer.getComponent( i );
- if ( ( c instanceof AbstractButton ) && ( ((AbstractButton)c).isSelected() ) )
- {
- return c.getName();
- }
- }
- return result;
- }
-
-/**
-* Tests whether the specified value is checked.
-* @param aValue A value to be tested.
-* @return True if the specified value is checked, otherwise false.
-*/
- public boolean getValue( String aValue )
- {
- if ( aValue == null ) return false;
- return aValue.equals( getValue() );
- }
+ * RadioButtonPanel is a simple extension of ButtonPanel. Differences are that
+ * it uses radio buttons and the default alignment is vertical. The radio
+ * buttons are placed in a ButtonGroup and the panel defaults to having no
+ * buttons selected.
+ *
+ * @author michael@mpowers.net
+ * @author $Author: cgruber $
+ * @version $Revision: 904 $ $Date: 2006-02-18 18:19:05 -0500 (Sat, 18 Feb 2006)
+ * $
+ */
+public class RadioButtonPanel extends ButtonPanel {
+ /**
+ * A ButtonGroup to help manage button state.
+ */
+ protected ButtonGroup buttonGroup;
+
+ /**
+ * ButtonGroup does not make it easy to unselect all buttons. The preferred way
+ * to do it is actually to create a hidden button.
+ */
+ protected JRadioButton hiddenButton;
+
+ /**
+ * Constructs a RadioButtonPanel. Three buttons are created so the panel is
+ * filled when used in a GUI-builder environment.
+ */
+ public RadioButtonPanel() {
+ super();
+ }
+
+ /**
+ * Constructs a ButtonPanel using specified buttons.
+ *
+ * @param buttonList An array containing the strings to be used in labeling the
+ * buttons.
+ */
+ public RadioButtonPanel(String[] buttonList) {
+ super(buttonList);
+ }
+
+ /**
+ * Overridden to set vertical-center alignment and 2-pixel vgap.
+ */
+ protected void initLayout() {
+ super.initLayout();
+ buttonPanelLayout.setAlignment(BetterFlowLayout.CENTER_VERTICAL);
+ buttonPanelLayout.setVgap(2); // looks nicer than java l&f recommendation (imho)
+ }
+
+ /**
+ * Overridden to return a JRadioButton.
+ *
+ * @param aLabel The label for the component that will be created.
+ * @return The newly created component.
+ */
+ protected Component createComponentWithLabel(String aLabel) {
+ String buttonLabel = aLabel;
+ JRadioButton newButton = new JRadioButton();
+ newButton.setName(aLabel);
+ newButton.setText(buttonLabel);
+ newButton.setActionCommand(aLabel);
+ newButton.addActionListener(this);
+
+ // reduce insets per java l&f guidelines (was 4 on each side)
+ newButton.setBorder(new EmptyBorder(0, 4, 0, 4));
+
+ if (buttonGroup == null) {
+ buttonGroup = new ButtonGroup();
+
+ // cheesy hack to allow a buttongroup to have no items selected.
+ // note that the button is not added to container or buttonList.
+ hiddenButton = new JRadioButton("Hidden Button");
+ buttonGroup.add(hiddenButton);
+ }
+ buttonGroup.add(newButton);
+
+ return newButton;
+ }
+
+ /**
+ * Selects the button whose name matches the given text value.
+ *
+ * @param newText A String matching the name of one of the buttons. If null,
+ * empty, or not matching, all buttons are deselected.
+ */
+ public void setValue(String aName) {
+ if (aName != null) {
+ Component c = null;
+ int count = buttonContainer.getComponentCount();
+ for (int i = 0; i < count; i++) {
+ c = buttonContainer.getComponent(i);
+ if (c instanceof AbstractButton) {
+ if (c.getName().equals(aName)) {
+ ((AbstractButton) c).setSelected(true);
+ return;
+ }
+ }
+ }
+ }
+
+ // null, empty, or not matching - deselect all
+ hiddenButton.setSelected(true);
+ }
+
+ /**
+ * Gets the name of the currently selected button.
+ *
+ * @return A string matching the name of the currently selected button, or null
+ * of no button is selected.
+ */
+ public String getValue() {
+ String result = null;
+ Component c = null;
+ int count = buttonContainer.getComponentCount();
+ for (int i = 0; i < count; i++) {
+ c = buttonContainer.getComponent(i);
+ if ((c instanceof AbstractButton) && (((AbstractButton) c).isSelected())) {
+ return c.getName();
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Tests whether the specified value is checked.
+ *
+ * @param aValue A value to be tested.
+ * @return True if the specified value is checked, otherwise false.
+ */
+ public boolean getValue(String aValue) {
+ if (aValue == null)
+ return false;
+ return aValue.equals(getValue());
+ }
}
-
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/SmartPasswordField.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/SmartPasswordField.java
index 6914cf6..55ba2f7 100644
--- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/SmartPasswordField.java
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/SmartPasswordField.java
@@ -26,249 +26,227 @@ import java.awt.event.KeyEvent;
import javax.swing.JPasswordField;
/**
- * SmartPasswordField is an extention of JPasswordField. It does everything
- * a JPassword does, as well as limit the number of characters. The user
- * of this class can specify that a password can only have a maximum of
- * 10 characters for instance.
+ * SmartPasswordField is an extention of JPasswordField. It does everything a
+ * JPassword does, as well as limit the number of characters. The user of this
+ * class can specify that a password can only have a maximum of 10 characters
+ * for instance.
*
* @author rob@straylight.princeton.com
* @author $Author: cgruber $
* @version $Revision: 904 $
*/
-public class SmartPasswordField extends JPasswordField
-{
-
-/*******************************
-* CONSTANTS
-*******************************/
- private static final int BACKSPACE = 8;
- private static final int DELETE = 127;
- private static final int SPACE = 32;
- private static final int DASH = 45;
- private static final int UNDERSCORE = 95;
- private static final int PERIOD = 46;
- private static final int PASTE = 22; // Ctl-V
-
- private int passwordLength = Integer.MAX_VALUE;
-
-/*******************************
-* PUBLIC METHODS
-*******************************/
-
-/**
-* Default constructor.
-*/
- public SmartPasswordField()
- {
- super();
- }
-
-/**
-* This constructor allows the user to set the maximum length of the password.
-* @param aLength The maximum length of the password.
-*/
- public SmartPasswordField( int aLength )
- {
- this();
- setPasswordLength( aLength );
- }
-
-/**
-* Sets the maximum lenght of the password. The value must be 0 or greater.
-* If the length specified is less than 0, then no action occurs.
-* @param aLength The maximum lenght of the password.
-*/
- public void setPasswordLength( int aLength )
- {
- if ( aLength >= 0 )
- {
- passwordLength = aLength;
- }
- }
-
-/**
-* Returns the current maximum length of the password.
-* @return The current maximum length of the password.
-*/
- public int getPasswordLength()
- {
- return passwordLength;
- }
-
-/**
-* This method processes a key event. This event is generated by input from the
-* keyboard when this text field has the focus. This method is called for every
-* key that is pressed and released on the keyboard. This includes modifier
-* keys like the shift and alt keys. This class looks at the key and determines
-* if the key is valid input given the restrictions of this class. <BR> <BR>
-* @param e A key event generated by a keyboard action.
-*/
- public void processKeyEvent(KeyEvent e)
- {
- String currentText = "";
- String testString = "";
- char newChar = e.getKeyChar();
- int currentLength = 0;
- int selectionStart = 0;
- int selectionEnd = 0;
- int endOfHead = 0;
- int startOfTail = 0;
- boolean backspace = false;
- boolean delete = false;
- boolean paste = false;
- boolean insertionPoint = false;
- boolean selectionAtStart = false;
- boolean selectionAtEnd = false;
-
- backspace = (newChar == BACKSPACE);
- delete = (newChar == DELETE);
- paste = (newChar == PASTE);
-
- if ((e.getKeyCode() == KeyEvent.VK_UNDEFINED) || ((backspace) || (delete) || (paste))) // A "key-typed" event
- {
- if (isValidCharacter(newChar))
- {
-
- if ((isPrintableCharacter(newChar)) || (backspace) || (delete) || (paste))
- {
- // Analyze the current contents of the field
- currentText = new String( getPassword() );
- currentLength = currentText.length();
-
- selectionStart = getSelectionStart();
- selectionEnd = getSelectionEnd();
-
- insertionPoint = (selectionStart == selectionEnd);
- selectionAtStart = (selectionStart == 0);
- selectionAtEnd = (selectionEnd >= currentLength);
- if (selectionEnd > currentLength)
- {
- setSelectionEnd(currentLength);
- }
-
- // Generate new string
- if (selectionStart > 0) // Create head of test string
- {
- endOfHead = selectionStart;
- if (insertionPoint && backspace)
- {
- endOfHead -= 1;
- }
- testString += currentText.substring(0, endOfHead);
- }
-
- if (!(backspace || delete || paste)) // Add the new character
- {
- testString += newChar;
- }
-
- if (paste) // Add the string from the clipboard
- {
- Transferable data = getToolkit().getSystemClipboard().getContents(this);
- if (data != null)
- {
- try
- {
- String clipString = (String)data.getTransferData(DataFlavor.stringFlavor);
- testString += clipString;
- }
- catch (java.io.IOException ioe)
- {
- // Do nothing
- }
- catch (UnsupportedFlavorException ufe)
- {
- // Do nothing
- }
- }
- }
-
- if (selectionEnd < currentLength) // Add the tail of the string
- {
- startOfTail = selectionEnd;
- if (insertionPoint && delete)
- {
- startOfTail += 1;
- }
- testString += currentText.substring(startOfTail);
- }
-
- }
-
- if (testString.compareTo("") != 0) // Null string is OK
- {
- if (!(isValidString(testString)))
- {
- e.consume();
- }
- }
- }
- else
- {
- e.consume();
- }
- }
- super.processKeyEvent(e);
-
- postProcessing();
- }
-
-
-/*******************************
-* PROTECTED METHODS
-*******************************/
-
-/**
-* Returns whether the inputted character is valid or not. In this case all
-* characters are valid input.
-* @param aChar A character to perform the validity test with.
-* @return True if the character is valid for this subclassed text field. <BR>
-* False is the character is not valid.
-*/
- protected boolean isValidCharacter(char aChar)
- {
- return true;
- }
-
-/**
-* Returns whether a string is valid for this text field. As the user types from
-* the keyboard, this method is called to determine if the new string in the text
-* field is valid based upon the restriction of this class. The length of the
-* new string is checked against the maximum password length.
-* @param aString The string to perform the validity check with.
-* @return True if the length of the string is less than or equal to the maximum length.
-* False if the character is not valud.
-*/
- protected boolean isValidString(String aString)
- {
- if ( aString.length() > passwordLength )
- {
- return false;
- }
-
- return true;
- }
-
-/**
-* This class does not need any post processing.
-*/
- protected void postProcessing()
- {
- /* Do Nothing */
- }
-
-
-/*******************************
-* PRIVATE METHODS
-*******************************/
-
- private boolean isPrintableCharacter(char inputChar)
- {
- if ((inputChar >= ' ') && (inputChar <= '~'))
- {
- return true;
- }
- return false;
- }
+public class SmartPasswordField extends JPasswordField {
+
+ /*******************************
+ * CONSTANTS
+ *******************************/
+ private static final int BACKSPACE = 8;
+ private static final int DELETE = 127;
+ private static final int SPACE = 32;
+ private static final int DASH = 45;
+ private static final int UNDERSCORE = 95;
+ private static final int PERIOD = 46;
+ private static final int PASTE = 22; // Ctl-V
+
+ private int passwordLength = Integer.MAX_VALUE;
+
+ /*******************************
+ * PUBLIC METHODS
+ *******************************/
+
+ /**
+ * Default constructor.
+ */
+ public SmartPasswordField() {
+ super();
+ }
+
+ /**
+ * This constructor allows the user to set the maximum length of the password.
+ *
+ * @param aLength The maximum length of the password.
+ */
+ public SmartPasswordField(int aLength) {
+ this();
+ setPasswordLength(aLength);
+ }
+
+ /**
+ * Sets the maximum lenght of the password. The value must be 0 or greater. If
+ * the length specified is less than 0, then no action occurs.
+ *
+ * @param aLength The maximum lenght of the password.
+ */
+ public void setPasswordLength(int aLength) {
+ if (aLength >= 0) {
+ passwordLength = aLength;
+ }
+ }
+
+ /**
+ * Returns the current maximum length of the password.
+ *
+ * @return The current maximum length of the password.
+ */
+ public int getPasswordLength() {
+ return passwordLength;
+ }
+
+ /**
+ * This method processes a key event. This event is generated by input from the
+ * keyboard when this text field has the focus. This method is called for every
+ * key that is pressed and released on the keyboard. This includes modifier keys
+ * like the shift and alt keys. This class looks at the key and determines if
+ * the key is valid input given the restrictions of this class. <BR>
+ * <BR>
+ *
+ * @param e A key event generated by a keyboard action.
+ */
+ public void processKeyEvent(KeyEvent e) {
+ String currentText = "";
+ String testString = "";
+ char newChar = e.getKeyChar();
+ int currentLength = 0;
+ int selectionStart = 0;
+ int selectionEnd = 0;
+ int endOfHead = 0;
+ int startOfTail = 0;
+ boolean backspace = false;
+ boolean delete = false;
+ boolean paste = false;
+ boolean insertionPoint = false;
+ boolean selectionAtStart = false;
+ boolean selectionAtEnd = false;
+
+ backspace = (newChar == BACKSPACE);
+ delete = (newChar == DELETE);
+ paste = (newChar == PASTE);
+
+ if ((e.getKeyCode() == KeyEvent.VK_UNDEFINED) || ((backspace) || (delete) || (paste))) // A "key-typed" event
+ {
+ if (isValidCharacter(newChar)) {
+
+ if ((isPrintableCharacter(newChar)) || (backspace) || (delete) || (paste)) {
+ // Analyze the current contents of the field
+ currentText = new String(getPassword());
+ currentLength = currentText.length();
+
+ selectionStart = getSelectionStart();
+ selectionEnd = getSelectionEnd();
+
+ insertionPoint = (selectionStart == selectionEnd);
+ selectionAtStart = (selectionStart == 0);
+ selectionAtEnd = (selectionEnd >= currentLength);
+ if (selectionEnd > currentLength) {
+ setSelectionEnd(currentLength);
+ }
+
+ // Generate new string
+ if (selectionStart > 0) // Create head of test string
+ {
+ endOfHead = selectionStart;
+ if (insertionPoint && backspace) {
+ endOfHead -= 1;
+ }
+ testString += currentText.substring(0, endOfHead);
+ }
+
+ if (!(backspace || delete || paste)) // Add the new character
+ {
+ testString += newChar;
+ }
+
+ if (paste) // Add the string from the clipboard
+ {
+ Transferable data = getToolkit().getSystemClipboard().getContents(this);
+ if (data != null) {
+ try {
+ String clipString = (String) data.getTransferData(DataFlavor.stringFlavor);
+ testString += clipString;
+ } catch (java.io.IOException ioe) {
+ // Do nothing
+ } catch (UnsupportedFlavorException ufe) {
+ // Do nothing
+ }
+ }
+ }
+
+ if (selectionEnd < currentLength) // Add the tail of the string
+ {
+ startOfTail = selectionEnd;
+ if (insertionPoint && delete) {
+ startOfTail += 1;
+ }
+ testString += currentText.substring(startOfTail);
+ }
+
+ }
+
+ if (testString.compareTo("") != 0) // Null string is OK
+ {
+ if (!(isValidString(testString))) {
+ e.consume();
+ }
+ }
+ } else {
+ e.consume();
+ }
+ }
+ super.processKeyEvent(e);
+
+ postProcessing();
+ }
+
+ /*******************************
+ * PROTECTED METHODS
+ *******************************/
+
+ /**
+ * Returns whether the inputted character is valid or not. In this case all
+ * characters are valid input.
+ *
+ * @param aChar A character to perform the validity test with.
+ * @return True if the character is valid for this subclassed text field. <BR>
+ * False is the character is not valid.
+ */
+ protected boolean isValidCharacter(char aChar) {
+ return true;
+ }
+
+ /**
+ * Returns whether a string is valid for this text field. As the user types from
+ * the keyboard, this method is called to determine if the new string in the
+ * text field is valid based upon the restriction of this class. The length of
+ * the new string is checked against the maximum password length.
+ *
+ * @param aString The string to perform the validity check with.
+ * @return True if the length of the string is less than or equal to the maximum
+ * length. False if the character is not valud.
+ */
+ protected boolean isValidString(String aString) {
+ if (aString.length() > passwordLength) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * This class does not need any post processing.
+ */
+ protected void postProcessing() {
+ /* Do Nothing */
+ }
+
+ /*******************************
+ * PRIVATE METHODS
+ *******************************/
+
+ private boolean isPrintableCharacter(char inputChar) {
+ if ((inputChar >= ' ') && (inputChar <= '~')) {
+ return true;
+ }
+ return false;
+ }
}
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/SmartTextField.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/SmartTextField.java
index cee37e1..5092179 100644
--- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/SmartTextField.java
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/SmartTextField.java
@@ -27,218 +27,204 @@ import javax.swing.JTextField;
/**
* SmartTextField is an abstract class for that allows the text field to
- * intelligently analyze the user's input in real-time. As the user enters
+ * intelligently analyze the user's input in real-time. As the user enters
* keystrokes, the generated string is analyzed to determine if the new string
* is valid based on the criteria of the concrete classes that extend this
- * class. An invalid keystroke is rejected and not displayed in the text
- * field. This class can be extended to to create smart text fields that only
- * accept integers or floating points number or alphabetic strings of maximum
- * length. These are several examples.
+ * class. An invalid keystroke is rejected and not displayed in the text field.
+ * This class can be extended to to create smart text fields that only accept
+ * integers or floating points number or alphabetic strings of maximum length.
+ * These are several examples.
*
* @author rob@straylight.princeton.com
* @author $Author: cgruber $
* @version $Revision: 904 $
*/
-public abstract class SmartTextField extends JTextField
-{
-
-/*******************************
-* CONSTANTS
-*******************************/
- private static final int BACKSPACE = 8;
- private static final int DELETE = 127;
- private static final int SPACE = 32;
- private static final int DASH = 45;
- private static final int UNDERSCORE = 95;
- private static final int PERIOD = 46;
- private static final int PASTE = 22; // Ctl-V
-
-
-/*******************************
-* PUBLIC METHODS
-*******************************/
-
-/**
-* This method processes a key event. This event is generated by input from the
-* keyboard when this text field has the focus. This method is called for every
-* key that is pressed and released on the keyboard. This includes modifier
-* keys like the shift and alt keys. This class looks at the key and determines
-* if the key is valid input given the restrictions of the concrete sub-classes. <BR> <BR>
-* Example - A smart text field only allows alphabetic characters. If the key
-* pressed is a "2" then this method will determine that the key is invalid and
-* "consume" the key event. <BR> <BR>
-* Note - Every printable character has a "TYPED" key event. Currentlt under
-* Java 1.2.1 this does not happen. Bug 4186905 relating this bug has been
-* fixed and is awaiting release.
-* @param e A key event generated by a keyboard action.
-*/
- public void processKeyEvent(KeyEvent e)
- {
- String currentText = "";
- String testString = "";
- char newChar = e.getKeyChar();
- int currentLength = 0;
- int selectionStart = 0;
- int selectionEnd = 0;
- int endOfHead = 0;
- int startOfTail = 0;
- boolean backspace = false;
- boolean delete = false;
- boolean paste = false;
- boolean insertionPoint = false;
- boolean selectionAtStart = false;
- boolean selectionAtEnd = false;
-
- backspace = (newChar == BACKSPACE);
- delete = (newChar == DELETE);
- paste = (newChar == PASTE);
-
- if ((e.getKeyCode() == KeyEvent.VK_UNDEFINED) || ((backspace) || (delete) || (paste))) // A "key-typed" event
- {
- if (isValidCharacter(newChar))
- {
-
- if ((isPrintableCharacter(newChar)) || (backspace) || (delete) || (paste))
- {
- // Analyze the current contents of the field
- currentText = getText();
- currentLength = currentText.length();
-
- selectionStart = getSelectionStart();
- selectionEnd = getSelectionEnd();
-
- insertionPoint = (selectionStart == selectionEnd);
- selectionAtStart = (selectionStart == 0);
- selectionAtEnd = (selectionEnd >= currentLength);
- if (selectionEnd > currentLength)
- {
- setSelectionEnd(currentLength);
- }
-
- // Generate new string
- if (selectionStart > 0) // Create head of test string
- {
- endOfHead = selectionStart;
- if (insertionPoint && backspace)
- {
- endOfHead -= 1;
- }
- testString += currentText.substring(0, endOfHead);
- }
-
- if (!(backspace || delete || paste)) // Add the new character
- {
- testString += newChar;
- }
-
- if (paste) // Add the string from the clipboard
- {
- Transferable data = getToolkit().getSystemClipboard().getContents(this);
- if (data != null)
- {
- try
- {
- String clipString = (String)data.getTransferData(DataFlavor.stringFlavor);
- testString += clipString;
- }
- catch (java.io.IOException ioe)
- {
- // Do nothing
- }
- catch (UnsupportedFlavorException ufe)
- {
- // Do nothing
- }
- }
- }
-
- if (selectionEnd < currentLength) // Add the tail of the string
- {
- startOfTail = selectionEnd;
- if (insertionPoint && delete)
- {
- startOfTail += 1;
- }
- testString += currentText.substring(startOfTail);
- }
-
- }
-
- if (testString.compareTo("") != 0) // Null string is OK
- {
- if (!(isValidString(testString)))
- {
- e.consume();
- }
- }
- }
- else
- {
- e.consume();
- }
- }
- super.processKeyEvent(e);
-
- postProcessing();
- }
-
-
-/*******************************
-* PROTECTED METHODS
-*******************************/
-
-/**
-* Default constructor for this class. The initial text of the smart text field
-* can be specified as well as the size (in characters) of the text field.
-* @param text The initial string that is displayed in the text field.
-* @param columns THe width of the text field in characters.
-*/
- protected SmartTextField(String text, int columns)
- {
- super(text, columns);
- }
-
-/**
-* Returns whether a character is valid for this text field. As the user types
-* from the keyboard, this method is called to determine if the character is a
-* valid character based in the restrictions of the subclass.
-* @param aChar A character to perform the validity test with.
-* @return True if the character is valid for this subclassed text field. <BR>
-* False is the character is not valid.
-*/
- abstract protected boolean isValidCharacter(char aChar);
-
-/**
-* Returns whether a string is valid for this text field. As the user types from
-* the keyboard, this method is called to determine if the new string in the text
-* field is valid based upon the restriction of the subclass. This is done after
-* the character has been determined to be valid since there can be restrictions
-* placed on the text string as a whole, such a maximum length or date format.
-* @param aString The string to perform the validity check with.
-* @return True if the string is valid for this subclassed text field. <BR>
-* False if the character is not valud.
-*/
- abstract protected boolean isValidString(String aString);
-
-/**
-* This method is used by the any subclass that need to complete any processing
-* of the text string in the text field after all the requirement checks have
-* been performed.
-*/
- abstract protected void postProcessing();
-
-
-/*******************************
-* PRIVATE METHODS
-*******************************/
-
- private boolean isPrintableCharacter(char inputChar)
- {
- if ((inputChar >= ' ') && (inputChar <= '~'))
- {
- return true;
- }
- return false;
- }
+public abstract class SmartTextField extends JTextField {
+
+ /*******************************
+ * CONSTANTS
+ *******************************/
+ private static final int BACKSPACE = 8;
+ private static final int DELETE = 127;
+ private static final int SPACE = 32;
+ private static final int DASH = 45;
+ private static final int UNDERSCORE = 95;
+ private static final int PERIOD = 46;
+ private static final int PASTE = 22; // Ctl-V
+
+ /*******************************
+ * PUBLIC METHODS
+ *******************************/
+
+ /**
+ * This method processes a key event. This event is generated by input from the
+ * keyboard when this text field has the focus. This method is called for every
+ * key that is pressed and released on the keyboard. This includes modifier keys
+ * like the shift and alt keys. This class looks at the key and determines if
+ * the key is valid input given the restrictions of the concrete sub-classes.
+ * <BR>
+ * <BR>
+ * Example - A smart text field only allows alphabetic characters. If the key
+ * pressed is a "2" then this method will determine that the key is invalid and
+ * "consume" the key event. <BR>
+ * <BR>
+ * Note - Every printable character has a "TYPED" key event. Currentlt under
+ * Java 1.2.1 this does not happen. Bug 4186905 relating this bug has been fixed
+ * and is awaiting release.
+ *
+ * @param e A key event generated by a keyboard action.
+ */
+ public void processKeyEvent(KeyEvent e) {
+ String currentText = "";
+ String testString = "";
+ char newChar = e.getKeyChar();
+ int currentLength = 0;
+ int selectionStart = 0;
+ int selectionEnd = 0;
+ int endOfHead = 0;
+ int startOfTail = 0;
+ boolean backspace = false;
+ boolean delete = false;
+ boolean paste = false;
+ boolean insertionPoint = false;
+ boolean selectionAtStart = false;
+ boolean selectionAtEnd = false;
+
+ backspace = (newChar == BACKSPACE);
+ delete = (newChar == DELETE);
+ paste = (newChar == PASTE);
+
+ if ((e.getKeyCode() == KeyEvent.VK_UNDEFINED) || ((backspace) || (delete) || (paste))) // A "key-typed" event
+ {
+ if (isValidCharacter(newChar)) {
+
+ if ((isPrintableCharacter(newChar)) || (backspace) || (delete) || (paste)) {
+ // Analyze the current contents of the field
+ currentText = getText();
+ currentLength = currentText.length();
+
+ selectionStart = getSelectionStart();
+ selectionEnd = getSelectionEnd();
+
+ insertionPoint = (selectionStart == selectionEnd);
+ selectionAtStart = (selectionStart == 0);
+ selectionAtEnd = (selectionEnd >= currentLength);
+ if (selectionEnd > currentLength) {
+ setSelectionEnd(currentLength);
+ }
+
+ // Generate new string
+ if (selectionStart > 0) // Create head of test string
+ {
+ endOfHead = selectionStart;
+ if (insertionPoint && backspace) {
+ endOfHead -= 1;
+ }
+ testString += currentText.substring(0, endOfHead);
+ }
+
+ if (!(backspace || delete || paste)) // Add the new character
+ {
+ testString += newChar;
+ }
+
+ if (paste) // Add the string from the clipboard
+ {
+ Transferable data = getToolkit().getSystemClipboard().getContents(this);
+ if (data != null) {
+ try {
+ String clipString = (String) data.getTransferData(DataFlavor.stringFlavor);
+ testString += clipString;
+ } catch (java.io.IOException ioe) {
+ // Do nothing
+ } catch (UnsupportedFlavorException ufe) {
+ // Do nothing
+ }
+ }
+ }
+
+ if (selectionEnd < currentLength) // Add the tail of the string
+ {
+ startOfTail = selectionEnd;
+ if (insertionPoint && delete) {
+ startOfTail += 1;
+ }
+ testString += currentText.substring(startOfTail);
+ }
+
+ }
+
+ if (testString.compareTo("") != 0) // Null string is OK
+ {
+ if (!(isValidString(testString))) {
+ e.consume();
+ }
+ }
+ } else {
+ e.consume();
+ }
+ }
+ super.processKeyEvent(e);
+
+ postProcessing();
+ }
+
+ /*******************************
+ * PROTECTED METHODS
+ *******************************/
+
+ /**
+ * Default constructor for this class. The initial text of the smart text field
+ * can be specified as well as the size (in characters) of the text field.
+ *
+ * @param text The initial string that is displayed in the text field.
+ * @param columns THe width of the text field in characters.
+ */
+ protected SmartTextField(String text, int columns) {
+ super(text, columns);
+ }
+
+ /**
+ * Returns whether a character is valid for this text field. As the user types
+ * from the keyboard, this method is called to determine if the character is a
+ * valid character based in the restrictions of the subclass.
+ *
+ * @param aChar A character to perform the validity test with.
+ * @return True if the character is valid for this subclassed text field. <BR>
+ * False is the character is not valid.
+ */
+ abstract protected boolean isValidCharacter(char aChar);
+
+ /**
+ * Returns whether a string is valid for this text field. As the user types from
+ * the keyboard, this method is called to determine if the new string in the
+ * text field is valid based upon the restriction of the subclass. This is done
+ * after the character has been determined to be valid since there can be
+ * restrictions placed on the text string as a whole, such a maximum length or
+ * date format.
+ *
+ * @param aString The string to perform the validity check with.
+ * @return True if the string is valid for this subclassed text field. <BR>
+ * False if the character is not valud.
+ */
+ abstract protected boolean isValidString(String aString);
+
+ /**
+ * This method is used by the any subclass that need to complete any processing
+ * of the text string in the text field after all the requirement checks have
+ * been performed.
+ */
+ abstract protected void postProcessing();
+
+ /*******************************
+ * PRIVATE METHODS
+ *******************************/
+
+ private boolean isPrintableCharacter(char inputChar) {
+ if ((inputChar >= ' ') && (inputChar <= '~')) {
+ return true;
+ }
+ return false;
+ }
}
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/StatusButtonPanel.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/StatusButtonPanel.java
index 3d9a85b..ba9f361 100644
--- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/StatusButtonPanel.java
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/StatusButtonPanel.java
@@ -34,243 +34,228 @@ import javax.swing.UIManager;
import javax.swing.border.EmptyBorder;
/**
-* StatusButtonPanel extends ButtonPanel to provide a space
-* to display status messages in a consistent manner.<BR><BR>
-* Messages are erased after a certain predefined interval,
-* defaulting to 10 seconds.
-*
-* @author michael@mpowers.net
-* @author $Author: cgruber $
-* @version $Revision: 904 $
-*/
-public class StatusButtonPanel extends ButtonPanel
-{
-/**
-* This is the action command to all listeners when the status text is changed.
-*/
- public static final String STATUS_CHANGED = "STATUS_CHANGED";
-
- // note: weirdness happens if you initialize
- // this variable. Because it is set by initLayout
- // and initLayout is called by the superclass constructor,
- // this variable would get initialized after initLayout
- // is called...
- protected Component statusComponent; // = null;
-
- protected Timer timer = null;
- protected int interval = 10000; // adjust as needed
-
-/**
-* Constructs a StatusButtonPanel. Three buttons are created
-* so the panel is filled when used in a GUI-builder environment.
-*/
- public StatusButtonPanel()
- {
- super();
- setupTimer();
- }
-
-/**
-* Constructs a StatusButtonPanel using specified buttons.
-* @param buttonList An array containing the strings to be used in labeling the buttons.
-*/
- public StatusButtonPanel( String[] buttonList )
- {
- super( buttonList );
- setupTimer();
- }
-
-/**
-* Initializes the timer instance variable.
-*/
- protected void setupTimer()
- {
- timer = new Timer( interval, this );
- timer.addActionListener( this );
- timer.setRepeats( false );
- timer.start();
- }
-
-/**
-* Returns the number of milliseconds before the status message is cleared.
-* The default is 10000.
-* @return The current delay interval in milliseconds.
-*/
- public int getDelayInterval()
- {
- return interval;
- }
-
-/**
-* Sets the number of milliseconds before the status message is cleared.
-* @param millis The new delay interval in milliseconds.
-*/
- public void setDelayInterval( int millis )
- {
- interval = millis;
- timer.setDelay( interval );
- }
-
-/**
-* Returns the visual component used to display the status.
-* @return A component used for displaying status.
-*/
- public Component getStatusComponent()
- {
- return statusComponent;
-
- }
-/**
-* Receives ActionEvents from the internal timer.
-* @param e The action event in question.
-*/
- public void actionPerformed(ActionEvent e)
- {
- if ( e.getSource() == timer )
- {
- setText( "" );
- return;
- }
-
- // otherwise continue with superclass implementation
- super.actionPerformed( e );
- }
-
-/**
-* This method is responsible for the initial layout of the panel.
-* Subclasses can implement different layouts, but this method
-* is responsible for initializing buttonPanelLayout to a valid
-* layout manager and setting this panel to use it. This method
-* must should initialize statusComponent to a component that ideally
-* has get/setText methods, although this is not required.
-*/
- protected void initLayout()
- {
-
- statusComponent = new JTextField();
- JTextField textField = (JTextField) statusComponent;
- textField.setColumns( 20 );
- textField.setBackground( getBackground() );
- textField.setEditable( false );
+ * StatusButtonPanel extends ButtonPanel to provide a space to display status
+ * messages in a consistent manner.<BR>
+ * <BR>
+ * Messages are erased after a certain predefined interval, defaulting to 10
+ * seconds.
+ *
+ * @author michael@mpowers.net
+ * @author $Author: cgruber $
+ * @version $Revision: 904 $
+ */
+public class StatusButtonPanel extends ButtonPanel {
+ /**
+ * This is the action command to all listeners when the status text is changed.
+ */
+ public static final String STATUS_CHANGED = "STATUS_CHANGED";
+
+ // note: weirdness happens if you initialize
+ // this variable. Because it is set by initLayout
+ // and initLayout is called by the superclass constructor,
+ // this variable would get initialized after initLayout
+ // is called...
+ protected Component statusComponent; // = null;
+
+ protected Timer timer = null;
+ protected int interval = 10000; // adjust as needed
+
+ /**
+ * Constructs a StatusButtonPanel. Three buttons are created so the panel is
+ * filled when used in a GUI-builder environment.
+ */
+ public StatusButtonPanel() {
+ super();
+ setupTimer();
+ }
+
+ /**
+ * Constructs a StatusButtonPanel using specified buttons.
+ *
+ * @param buttonList An array containing the strings to be used in labeling the
+ * buttons.
+ */
+ public StatusButtonPanel(String[] buttonList) {
+ super(buttonList);
+ setupTimer();
+ }
+
+ /**
+ * Initializes the timer instance variable.
+ */
+ protected void setupTimer() {
+ timer = new Timer(interval, this);
+ timer.addActionListener(this);
+ timer.setRepeats(false);
+ timer.start();
+ }
+
+ /**
+ * Returns the number of milliseconds before the status message is cleared. The
+ * default is 10000.
+ *
+ * @return The current delay interval in milliseconds.
+ */
+ public int getDelayInterval() {
+ return interval;
+ }
+
+ /**
+ * Sets the number of milliseconds before the status message is cleared.
+ *
+ * @param millis The new delay interval in milliseconds.
+ */
+ public void setDelayInterval(int millis) {
+ interval = millis;
+ timer.setDelay(interval);
+ }
+
+ /**
+ * Returns the visual component used to display the status.
+ *
+ * @return A component used for displaying status.
+ */
+ public Component getStatusComponent() {
+ return statusComponent;
+
+ }
+
+ /**
+ * Receives ActionEvents from the internal timer.
+ *
+ * @param e The action event in question.
+ */
+ public void actionPerformed(ActionEvent e) {
+ if (e.getSource() == timer) {
+ setText("");
+ return;
+ }
+
+ // otherwise continue with superclass implementation
+ super.actionPerformed(e);
+ }
+
+ /**
+ * This method is responsible for the initial layout of the panel. Subclasses
+ * can implement different layouts, but this method is responsible for
+ * initializing buttonPanelLayout to a valid layout manager and setting this
+ * panel to use it. This method must should initialize statusComponent to a
+ * component that ideally has get/setText methods, although this is not
+ * required.
+ */
+ protected void initLayout() {
+
+ statusComponent = new JTextField();
+ JTextField textField = (JTextField) statusComponent;
+ textField.setColumns(20);
+ textField.setBackground(getBackground());
+ textField.setEditable(false);
// statusComponent = new PickListPanel(); // for testing
- this.setLayout( new GridBagLayout() );
-
- GridBagConstraints gbc =
- new GridBagConstraints();
- gbc.gridx = GridBagConstraints.RELATIVE;
- gbc.gridy = GridBagConstraints.RELATIVE;
- gbc.gridwidth = 1;
- gbc.gridheight = 1;
- gbc.weightx = 1.0;
- gbc.weighty = 0.0;
- gbc.anchor = GridBagConstraints.CENTER;
- gbc.fill = GridBagConstraints.HORIZONTAL;
- gbc.insets = new Insets(0, 5, 0, 10);
- gbc.ipadx = 0;
- gbc.ipady = 0;
+ this.setLayout(new GridBagLayout());
+
+ GridBagConstraints gbc = new GridBagConstraints();
+ gbc.gridx = GridBagConstraints.RELATIVE;
+ gbc.gridy = GridBagConstraints.RELATIVE;
+ gbc.gridwidth = 1;
+ gbc.gridheight = 1;
+ gbc.weightx = 1.0;
+ gbc.weighty = 0.0;
+ gbc.anchor = GridBagConstraints.CENTER;
+ gbc.fill = GridBagConstraints.HORIZONTAL;
+ gbc.insets = new Insets(0, 5, 0, 10);
+ gbc.ipadx = 0;
+ gbc.ipady = 0;
//1.2 new GridBagConstraints(GridBagConstraints.RELATIVE, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0,
//1.2 GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 5, 0, 10), 0, 0 );
- this.add( statusComponent, gbc );
-
- buttonContainer = new JPanel();
- buttonPanelLayout = new BetterFlowLayout();
- buttonContainer.setLayout(buttonPanelLayout);
- buttonPanelLayout.setAlignment( BetterFlowLayout.RIGHT );
- ((BetterFlowLayout)buttonPanelLayout).setWidthUniform( true );
- gbc.weightx = 0.0;
- gbc.insets = new Insets( 0, 0, 0, 0 );
- this.add( buttonContainer, gbc );
- }
-
-/**
-* Sets the text to appear in the status area.
-* @param newText A string to appear in the status area. Nulls are allowed.
-*/
- public void setText(String newText)
- {
- // TODO: should use property introspection instead
-
- // use reflection to call the "setText" method, if any.
- try
- {
- Class c = statusComponent.getClass();
- Method m = c.getMethod( "setText", new Class[] { new String().getClass() } );
- m.invoke( statusComponent, new Object[] { newText } );
- broadcastEvent( new ActionEvent( this, ActionEvent.ACTION_PERFORMED, STATUS_CHANGED ) );
- statusComponent.paint( statusComponent.getGraphics() );
- }
- catch ( Exception exc )
- {
- // "setText" method does not exist; do nothing.
- }
-
- // if non-empty string, start the timer
- if ( ! "".equals( newText ) )
- {
- timer.restart();
- }
- }
-
-/**
-* Gets the text in the status area.
-* @return The string being displayed in the status area.
-*/
- public String getText()
- {
- // TODO: should use property introspection instead
-
- String value = "";
- // use reflection to call the "setText" method, if any.
- try
- {
- Class c = statusComponent.getClass();
- Method m = c.getMethod( "getText", (Class[])null );
- value = (String) m.invoke( statusComponent, (Object[])null );
- }
- catch ( Exception exc )
- {
- // "getText" method does not exist; do nothing.
- }
- return value;
- }
-
- // for testing
-
- public static void main( String[] argv )
- {
- try
- {
- UIManager.setLookAndFeel( UIManager.getSystemLookAndFeelClassName() );
- }
- catch (Exception exc)
- {
-
- }
-
- JFrame dialog = new JFrame();
- BorderLayout bl = new BorderLayout( 20, 20 );
+ this.add(statusComponent, gbc);
+
+ buttonContainer = new JPanel();
+ buttonPanelLayout = new BetterFlowLayout();
+ buttonContainer.setLayout(buttonPanelLayout);
+ buttonPanelLayout.setAlignment(BetterFlowLayout.RIGHT);
+ ((BetterFlowLayout) buttonPanelLayout).setWidthUniform(true);
+ gbc.weightx = 0.0;
+ gbc.insets = new Insets(0, 0, 0, 0);
+ this.add(buttonContainer, gbc);
+ }
+
+ /**
+ * Sets the text to appear in the status area.
+ *
+ * @param newText A string to appear in the status area. Nulls are allowed.
+ */
+ public void setText(String newText) {
+ // TODO: should use property introspection instead
+
+ // use reflection to call the "setText" method, if any.
+ try {
+ Class c = statusComponent.getClass();
+ Method m = c.getMethod("setText", new Class[] { new String().getClass() });
+ m.invoke(statusComponent, new Object[] { newText });
+ broadcastEvent(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, STATUS_CHANGED));
+ statusComponent.paint(statusComponent.getGraphics());
+ } catch (Exception exc) {
+ // "setText" method does not exist; do nothing.
+ }
+
+ // if non-empty string, start the timer
+ if (!"".equals(newText)) {
+ timer.restart();
+ }
+ }
+
+ /**
+ * Gets the text in the status area.
+ *
+ * @return The string being displayed in the status area.
+ */
+ public String getText() {
+ // TODO: should use property introspection instead
+
+ String value = "";
+ // use reflection to call the "setText" method, if any.
+ try {
+ Class c = statusComponent.getClass();
+ Method m = c.getMethod("getText", (Class[]) null);
+ value = (String) m.invoke(statusComponent, (Object[]) null);
+ } catch (Exception exc) {
+ // "getText" method does not exist; do nothing.
+ }
+ return value;
+ }
+
+ // for testing
+
+ public static void main(String[] argv) {
+ try {
+ UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
+ } catch (Exception exc) {
+
+ }
+
+ JFrame dialog = new JFrame();
+ BorderLayout bl = new BorderLayout(20, 20);
// StatusButtonPanel panel = new StatusButtonPanel();
// System.out.println( panel.statusComponent );
- StatusButtonPanel panel = new StatusButtonPanel( new String[] { "Okay", "Cancel" } );
+ StatusButtonPanel panel = new StatusButtonPanel(new String[] { "Okay", "Cancel" });
- dialog.getContentPane().setLayout( bl );
- dialog.getContentPane().add( panel, BorderLayout.SOUTH );
- dialog.setLocation( 50, 50 );
- // dialog.setSize( 450, 150 );
- dialog.pack();
- dialog.setVisible( true );
+ dialog.getContentPane().setLayout(bl);
+ dialog.getContentPane().add(panel, BorderLayout.SOUTH);
+ dialog.setLocation(50, 50);
+ // dialog.setSize( 450, 150 );
+ dialog.pack();
+ dialog.setVisible(true);
- panel.setBorder( new EmptyBorder( 5, 5, 5, 5 ) );
- panel.setAlignment( BetterFlowLayout.RIGHT );
+ panel.setBorder(new EmptyBorder(5, 5, 5, 5));
+ panel.setAlignment(BetterFlowLayout.RIGHT);
// panel.getButton( "One" ).setEnabled( false );
- panel.setText( "File saved." );
- System.out.println( panel.getText() );
- }
+ panel.setText("File saved.");
+ System.out.println(panel.getText());
+ }
}
-
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/TintedImageFilter.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/TintedImageFilter.java
index a51ed16..b15a660 100644
--- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/TintedImageFilter.java
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/TintedImageFilter.java
@@ -22,79 +22,75 @@ import java.awt.Color;
import java.awt.image.RGBImageFilter;
/**
- * TintedImageFilter tints all gray pixels half-way towards
- * the value passed into the constructor. This "tints" a
- * mostly grayscale image. This has proven useful for tinting
- * user interface decorative images towards one of the SystemColor
- * constants to better mesh with a platform look and feel.
+ * TintedImageFilter tints all gray pixels half-way towards the value passed
+ * into the constructor. This "tints" a mostly grayscale image. This has proven
+ * useful for tinting user interface decorative images towards one of the
+ * SystemColor constants to better mesh with a platform look and feel.
*
* @author michael@mpowers.net
* @author $Author: cgruber $
* @version $Revision: 893 $
*/
- public class TintedImageFilter extends RGBImageFilter
- {
- double redOffset, greenOffset, blueOffset;
-
- public TintedImageFilter( Color aColor )
- {
- canFilterIndexColorModel = true;
- redOffset = getOffset( aColor.getRed() );
- greenOffset = getOffset( aColor.getGreen() );
- blueOffset = getOffset( aColor.getBlue() );
- }
-
- /**
- * Calculates the offset used to modify color
- * values. This method returns half the difference
- * between the specified color level and 192.
- */
- protected double getOffset( int colorValue )
- {
- return ( colorValue - 192 ) / 2;
- }
+public class TintedImageFilter extends RGBImageFilter {
+ double redOffset, greenOffset, blueOffset;
- public int filterRGB(int x, int y, int rgb)
- {
+ public TintedImageFilter(Color aColor) {
+ canFilterIndexColorModel = true;
+ redOffset = getOffset(aColor.getRed());
+ greenOffset = getOffset(aColor.getGreen());
+ blueOffset = getOffset(aColor.getBlue());
+ }
+
+ /**
+ * Calculates the offset used to modify color values. This method returns half
+ * the difference between the specified color level and 192.
+ */
+ protected double getOffset(int colorValue) {
+ return (colorValue - 192) / 2;
+ }
+
+ public int filterRGB(int x, int y, int rgb) {
+
+ int red = (rgb & 0xff0000) >> 16;
+ int green = (rgb & 0x00ff00) >> 8;
+ int blue = (rgb & 0x0000ff);
+
+ // if roughly black
+ if (red + green + blue < 30)
+ return rgb;
+
+ // if roughly gray
+ if ((Math.abs(red - green) < 10) && (Math.abs(red - blue) < 10)) {
+ red += redOffset;
+ if (red < 0)
+ red = 0;
+ if (red > 255)
+ red = 255;
+ green += greenOffset;
+ if (green < 0)
+ green = 0;
+ if (green > 255)
+ green = 255;
+ blue += blueOffset;
+ if (blue < 0)
+ blue = 0;
+ if (blue > 255)
+ blue = 255;
+
+ return new Color(red, green, blue).getRGB();
+ }
+
+ return rgb;
+ }
+}
- int red = ( rgb & 0xff0000 ) >> 16;
- int green = ( rgb & 0x00ff00 ) >> 8;
- int blue = ( rgb & 0x0000ff );
-
- // if roughly black
- if ( red + green + blue < 30 ) return rgb;
-
- // if roughly gray
- if ( ( Math.abs( red - green ) < 10 )
- && ( Math.abs( red - blue ) < 10 ) )
- {
- red += redOffset;
- if ( red < 0 ) red = 0;
- if ( red > 255 ) red = 255;
- green += greenOffset;
- if ( green < 0 ) green = 0;
- if ( green > 255 ) green = 255;
- blue += blueOffset;
- if ( blue < 0 ) blue = 0;
- if ( blue > 255 ) blue = 255;
-
- return new Color( red, green, blue ).getRGB();
- }
-
- return rgb;
- }
- }
-
/*
- * $Log$
- * Revision 1.1 2006/02/16 13:22:22 cgruber
- * Check in all sources in eclipse-friendly maven-enabled packages.
+ * $Log$ Revision 1.1 2006/02/16 13:22:22 cgruber Check in all sources in
+ * eclipse-friendly maven-enabled packages.
*
- * Revision 1.2 2001/01/18 21:27:04 mpowers
- * Made the tinting a little darker.
+ * Revision 1.2 2001/01/18 21:27:04 mpowers Made the tinting a little darker.
*
- * Revision 1.1 2001/01/12 17:36:27 mpowers
- * Contributing TintedImageFilter.
+ * Revision 1.1 2001/01/12 17:36:27 mpowers Contributing TintedImageFilter.
*
*
*/
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/TreeChooser.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/TreeChooser.java
index f0bb6c2..f5ab50c 100644
--- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/TreeChooser.java
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/TreeChooser.java
@@ -66,662 +66,538 @@ import javax.swing.tree.TreeSelectionModel;
import net.wotonomy.foundation.internal.WotonomyException;
/**
-* TreeChooser is a FileChooser-like panel that
-* uses a TreeModel as a data source. It basically
-* provides an alternative to JTree for rendering
-* and manipulating tree-like data.
-*
-* @author michael@mpowers.net
-* @author $Author: cgruber $
-* @version $Revision: 904 $
-*/
-public class TreeChooser extends JPanel
- implements ActionListener, ListSelectionListener,
- TreeSelectionListener, TreeModelListener, ListCellRenderer
-{
- /**
- * The TreeChooser responds to this action command
- * by calling displayPrevious().
- */
+ * TreeChooser is a FileChooser-like panel that uses a TreeModel as a data
+ * source. It basically provides an alternative to JTree for rendering and
+ * manipulating tree-like data.
+ *
+ * @author michael@mpowers.net
+ * @author $Author: cgruber $
+ * @version $Revision: 904 $
+ */
+public class TreeChooser extends JPanel
+ implements ActionListener, ListSelectionListener, TreeSelectionListener, TreeModelListener, ListCellRenderer {
+ /**
+ * The TreeChooser responds to this action command by calling displayPrevious().
+ */
public static final String BACK = "Back";
- /**
- * The TreeChooser responds to this action command
- * by calling displayHome().
- */
+ /**
+ * The TreeChooser responds to this action command by calling displayHome().
+ */
public static final String HOME = "Home";
- /**
- * The TreeChooser responds to this action command
- * by calling displayParent().
- */
+ /**
+ * The TreeChooser responds to this action command by calling displayParent().
+ */
public static final String UP = "Up";
- /**
- * The TreeChooser responds to this action command
- * by attempting to navigate to the first node in
- * the current selection and display that node's children.
- */
+ /**
+ * The TreeChooser responds to this action command by attempting to navigate to
+ * the first node in the current selection and display that node's children.
+ */
public static final String SELECT = "Select";
protected JList contents;
- protected JComboBox pathCombo;
+ protected JComboBox pathCombo;
protected JToolBar toolBar;
-
+
protected TreeModel model;
protected TreeSelectionModel selectionModel;
- protected TreeCellRenderer renderer;
- protected TreePath displayPath;
- protected Stack pathStack;
- protected int pathIndent;
-
- private ChooserComboBoxModel comboBoxModel;
- private JTree bogusJTree; // needed for tree cell renderer
- private Dimension preferredSize;
-
- public TreeChooser()
- {
- preferredSize = new Dimension( 300, 200 );
- model = new DefaultTreeModel( new DefaultMutableTreeNode( "Root" ) );
- displayPath = new TreePath( model.getRoot() );
- selectionModel = new DefaultTreeSelectionModel();
- renderer = new DefaultTreeCellRenderer();
- pathStack = new Stack();
- pathIndent = 0; // 16;
- comboBoxModel = new ChooserComboBoxModel( this );
-
- bogusJTree = new JTree();
- bogusJTree.setModel( model );
+ protected TreeCellRenderer renderer;
+ protected TreePath displayPath;
+ protected Stack pathStack;
+ protected int pathIndent;
+
+ private ChooserComboBoxModel comboBoxModel;
+ private JTree bogusJTree; // needed for tree cell renderer
+ private Dimension preferredSize;
+
+ public TreeChooser() {
+ preferredSize = new Dimension(300, 200);
+ model = new DefaultTreeModel(new DefaultMutableTreeNode("Root"));
+ displayPath = new TreePath(model.getRoot());
+ selectionModel = new DefaultTreeSelectionModel();
+ renderer = new DefaultTreeCellRenderer();
+ pathStack = new Stack();
+ pathIndent = 0; // 16;
+ comboBoxModel = new ChooserComboBoxModel(this);
+
+ bogusJTree = new JTree();
+ bogusJTree.setModel(model);
init();
- displayHome();
-
- stopListening(); // clear existing listeners
- startListening();
- }
-
- public Dimension getPreferredSize()
- {
- return preferredSize;
- }
-
- protected void init()
- {
- this.setLayout( new BorderLayout( 10, 10 ) );
-
+ displayHome();
+
+ stopListening(); // clear existing listeners
+ startListening();
+ }
+
+ public Dimension getPreferredSize() {
+ return preferredSize;
+ }
+
+ protected void init() {
+ this.setLayout(new BorderLayout(10, 10));
+
contents = initList();
- contents.getSelectionModel().setSelectionMode(
- ListSelectionModel.MULTIPLE_INTERVAL_SELECTION );
- // synchs with DefaultTreeSelectionModel
-
- JScrollPane scrollPane = new JScrollPane( contents );
- scrollPane.setPreferredSize( new Dimension( 200, 150 ) );
- this.add( scrollPane, BorderLayout.CENTER );
-
- Component previewPane = initPreviewPane();
- if ( previewPane != null )
- {
- this.add( previewPane, BorderLayout.EAST );
- }
-
+ contents.getSelectionModel().setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
+ // synchs with DefaultTreeSelectionModel
+
+ JScrollPane scrollPane = new JScrollPane(contents);
+ scrollPane.setPreferredSize(new Dimension(200, 150));
+ this.add(scrollPane, BorderLayout.CENTER);
+
+ Component previewPane = initPreviewPane();
+ if (previewPane != null) {
+ this.add(previewPane, BorderLayout.EAST);
+ }
+
JPanel navigationPanel = new JPanel();
- navigationPanel.setLayout( new BorderLayout( 10, 10 ) );
- this.add( navigationPanel, BorderLayout.NORTH );
-
+ navigationPanel.setLayout(new BorderLayout(10, 10));
+ this.add(navigationPanel, BorderLayout.NORTH);
+
pathCombo = initComboBox();
- if ( pathCombo != null )
- {
- pathCombo.setModel( comboBoxModel );
+ if (pathCombo != null) {
+ pathCombo.setModel(comboBoxModel);
// put combo in a grid bag to handle varying
- // heights of JToolBars across platforms
+ // heights of JToolBars across platforms
JPanel panel = new JPanel();
- panel.setLayout( new GridBagLayout() );
+ panel.setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.weightx = 1.0;
- panel.add( pathCombo, gbc );
- navigationPanel.add( panel, BorderLayout.CENTER );
- }
-
+ panel.add(pathCombo, gbc);
+ navigationPanel.add(panel, BorderLayout.CENTER);
+ }
+
Component toolBar = initToolBar();
- if ( toolBar != null )
- {
- navigationPanel.add( toolBar, BorderLayout.EAST );
- }
-
- }
-
- /**
- * Creates tool bar or return null if no tool bar is desired.
- * This implementation returns a JToolBar containing buttons
- * for BACK, UP, and HOME.
- */
- protected Component initToolBar()
- {
+ if (toolBar != null) {
+ navigationPanel.add(toolBar, BorderLayout.EAST);
+ }
+
+ }
+
+ /**
+ * Creates tool bar or return null if no tool bar is desired. This
+ * implementation returns a JToolBar containing buttons for BACK, UP, and HOME.
+ */
+ protected Component initToolBar() {
JToolBar toolBar = new JToolBar();
- toolBar.setFloatable( false );
- JButton button;
- button = new JButton( UIManager.getIcon("FileChooser.upFolderIcon") );
- button.setActionCommand( UP );
- button.addActionListener( this );
- toolBar.add( button );
- button = new JButton( UIManager.getIcon("FileChooser.homeFolderIcon") );
- button.setActionCommand( HOME );
- button.addActionListener( this );
- toolBar.add( button );
-/*
- button = new JButton( UIManager.getIcon("FileChooser.newFolderIcon") );
- button.setActionCommand( BACK );
- button.addActionListener( this );
- toolBar.add( button );
-*/
- return toolBar;
- }
-
- /**
- * Creates the component that is used to display a preview of the
- * selected item(s) in the content area. This component would listen
- * to the selection model to update itself when the selected items change.
- * Return null to omit this component.
- * This implementation returns null.
- */
- protected Component initPreviewPane()
- {
- return null;
- }
-
- /**
- * Creates the JComboBox that is used to render the path leading to
- * the displayed contents. Return null to omit this combo box.
- * This implementation returns a stock JComboBox that uses this
- * class as its cell renderer.
- */
- protected JComboBox initComboBox()
- {
- JComboBox comboBox = new JComboBox();
- comboBox.setRenderer( this );
- return comboBox;
- }
-
- /**
- * Creates the JList that is used to render the path leading to
- * the displayed contents. This method may not return null.
- * This implementation returns a stock JList that uses this
- * class as its cell renderer and fires a SELECT action event
- * on double click.
- */
- protected JList initList()
- {
- JList list = new JList();
- list.setCellRenderer( this );
- list.addMouseListener( new MouseAdapter()
- {
- public void mouseClicked( MouseEvent evt )
- {
- if ( evt.getClickCount() > 1 )
- {
- actionPerformed( new ActionEvent( this, 0, SELECT ) );
- }
- }
- });
- return list;
- }
-
- /**
- * Begins listening to the specified tree model
- * and tree selection model.
- */
- protected void startListening()
- {
- model.addTreeModelListener( this );
- selectionModel.addTreeSelectionListener( this );
- contents.addListSelectionListener( this );
- }
-
- /**
- * Stops listening to the specified tree model
- * and tree selection model.
- */
- protected void stopListening()
- {
- model.removeTreeModelListener( this );
- selectionModel.removeTreeSelectionListener( this );
- contents.removeListSelectionListener( this );
- }
-
- /**
- * Returns the TreeModel used by the TreeChooser.
- */
- public TreeModel getModel()
- {
- return model;
- }
-
- /**
- * Sets the TreeModel used by the TreeChooser.
- */
- public void setModel( TreeModel aTreeModel )
- {
- stopListening();
- model = aTreeModel;
- bogusJTree.setModel( aTreeModel );
- pathStack.removeAllElements();
- startListening();
- displayHome();
- }
-
- /**
- * Returns the TreeSelectionModel used by the TreeChooser.
- */
- public TreeSelectionModel getSelectionModel()
- {
- return selectionModel;
- }
-
- /**
- * Sets the TreeSelectionModel used by the TreeChooser.
- */
- public void setSelectionModel( TreeSelectionModel aSelectionModel )
- {
- selectionModel = aSelectionModel;
- if ( aSelectionModel.getSelectionMode() ==
- TreeSelectionModel.SINGLE_TREE_SELECTION )
- {
- contents.getSelectionModel().setSelectionMode(
- ListSelectionModel.SINGLE_SELECTION );
- }
- else
- {
- contents.getSelectionModel().setSelectionMode(
- ListSelectionModel.MULTIPLE_INTERVAL_SELECTION );
- }
- updateSelection();
- }
-
- /**
- * Returns the TreeCellRenderer used by the TreeChooser.
- */
- public TreeCellRenderer getRenderer()
- {
- return renderer;
- }
-
- /**
- * Sets the TreeCellRenderer used by the TreeChooser.
- */
- public void setRenderer( TreeCellRenderer aRenderer )
- {
- renderer = aRenderer;
- updateContents();
- }
-
- /**
- * Displays the "home" directory.
- * This implementation displays the root node's children.
- */
- public void displayHome()
- {
- setDisplayPath( null );
- }
-
- /**
- * Displays the parent path of the currently displayed path.
- */
- public void displayParent()
- {
- setDisplayPath( displayPath.getParentPath() );
- }
-
- /**
- * Displays the last displayed path before the current one,
- * emulating the behavior of a "back" button.
- */
- public void displayPrevious()
- {
- if ( pathStack.empty() )
- {
- displayHome();
- }
- else
- {
- setDisplayPathDirect( (TreePath) pathStack.pop() );
- updateContents();
- }
- }
-
- /**
- * Pushes the previous item onto the stack, sets
- * the display path, and then updates the contents.
- * If aPath is null, the root node's children are displayed.
- */
- public void setDisplayPath( TreePath aPath )
- {
- if ( aPath == null )
- {
- aPath = new TreePath( getModel().getRoot() );
- }
- if ( ! displayPath.equals ( aPath ) )
- {
- pathStack.push( displayPath );
- setDisplayPathDirect( aPath );
- }
- updateContents();
- }
-
- /**
- * Sets the displayPath field and does not
- * update the stack nor update the contents.
- */
- protected void setDisplayPathDirect( TreePath aPath )
- {
- displayPath = aPath;
- }
-
- /**
- * Gets the currently displayed path.
- */
- public TreePath getDisplayPath()
- {
- return displayPath;
- }
-
- /**
- * Called when selected path changes or when model indicates
- * that the displayed path has changed.
- */
- protected void updateContents()
- {
- stopListening();
-
- // update combo box
- comboBoxModel.fireContentsChanged();
-
- // update list contents
- Object displayedObject = displayPath.getLastPathComponent();
-/*
-//FIXME: this display group doesn't seem to be getting the sort orderings from parent
-if ( displayedObject instanceof net.wotonomy.ui.EODisplayGroup )
-System.out.println( ((net.wotonomy.ui.EODisplayGroup)displayedObject).displayedObjects() );
-*/
- int count = model.getChildCount( displayedObject );
- Object[] children = new Object[ count ];
- for ( int i = 0; i < count; i++ )
- {
- children[i] = model.getChild( displayedObject, i );
- }
- contents.setListData( children );
-
- startListening();
-
- // synchronize the selection
- updateSelection();
- }
-
- /**
- * Updates the selection in the list to reflect the
- * selection in the tree selection model.
- */
- public void updateSelection()
- {
- int index;
- Object last = displayPath.getLastPathComponent();
- TreePath[] selectionPaths = selectionModel.getSelectionPaths();
- if ( selectionPaths != null )
- {
- List selectedIndices = new LinkedList();
- for ( int i = 0; i < selectionPaths.length; i++ )
- {
- if ( displayPath.equals( selectionPaths[i].getParentPath() ) )
- {
- index = getModel().getIndexOfChild(
- last, selectionPaths[i].getLastPathComponent() );
- if ( index != -1 )
- {
- selectedIndices.add( new Integer( index ) );
- }
- else // should never happen
- {
- throw new WotonomyException(
- "Could not find child of displayed node." );
- }
- }
- }
- int[] selected = new int[ selectedIndices.size() ];
- for ( int i = 0; i < selected.length; i++ )
- {
- selected[i] = ((Integer)selectedIndices.get(i)).intValue();
- }
- stopListening();
- contents.setSelectedIndices( selected );
- startListening();
- }
- }
-
- // interface TreeModelListener
-
- public void treeNodesChanged( TreeModelEvent evt )
- {
-/*
- if ( displayPath.getLastPathComponent().toString().equals(
- evt.getTreePath().getLastPathComponent().toString() ) )
- {
-System.out.println( "TreeChooser.treeNodesChanged: " + count++ );
-*/
- updateContents();
-/*
- }
- else
- {
- System.out.println( evt.getTreePath() + " != " + displayPath );
- }
-*/
- }
-
- public void treeNodesInserted( TreeModelEvent evt )
- {
+ toolBar.setFloatable(false);
+ JButton button;
+ button = new JButton(UIManager.getIcon("FileChooser.upFolderIcon"));
+ button.setActionCommand(UP);
+ button.addActionListener(this);
+ toolBar.add(button);
+ button = new JButton(UIManager.getIcon("FileChooser.homeFolderIcon"));
+ button.setActionCommand(HOME);
+ button.addActionListener(this);
+ toolBar.add(button);
+ /*
+ * button = new JButton( UIManager.getIcon("FileChooser.newFolderIcon") );
+ * button.setActionCommand( BACK ); button.addActionListener( this );
+ * toolBar.add( button );
+ */
+ return toolBar;
+ }
+
+ /**
+ * Creates the component that is used to display a preview of the selected
+ * item(s) in the content area. This component would listen to the selection
+ * model to update itself when the selected items change. Return null to omit
+ * this component. This implementation returns null.
+ */
+ protected Component initPreviewPane() {
+ return null;
+ }
+
+ /**
+ * Creates the JComboBox that is used to render the path leading to the
+ * displayed contents. Return null to omit this combo box. This implementation
+ * returns a stock JComboBox that uses this class as its cell renderer.
+ */
+ protected JComboBox initComboBox() {
+ JComboBox comboBox = new JComboBox();
+ comboBox.setRenderer(this);
+ return comboBox;
+ }
+
+ /**
+ * Creates the JList that is used to render the path leading to the displayed
+ * contents. This method may not return null. This implementation returns a
+ * stock JList that uses this class as its cell renderer and fires a SELECT
+ * action event on double click.
+ */
+ protected JList initList() {
+ JList list = new JList();
+ list.setCellRenderer(this);
+ list.addMouseListener(new MouseAdapter() {
+ public void mouseClicked(MouseEvent evt) {
+ if (evt.getClickCount() > 1) {
+ actionPerformed(new ActionEvent(this, 0, SELECT));
+ }
+ }
+ });
+ return list;
+ }
+
+ /**
+ * Begins listening to the specified tree model and tree selection model.
+ */
+ protected void startListening() {
+ model.addTreeModelListener(this);
+ selectionModel.addTreeSelectionListener(this);
+ contents.addListSelectionListener(this);
+ }
+
+ /**
+ * Stops listening to the specified tree model and tree selection model.
+ */
+ protected void stopListening() {
+ model.removeTreeModelListener(this);
+ selectionModel.removeTreeSelectionListener(this);
+ contents.removeListSelectionListener(this);
+ }
+
+ /**
+ * Returns the TreeModel used by the TreeChooser.
+ */
+ public TreeModel getModel() {
+ return model;
+ }
+
+ /**
+ * Sets the TreeModel used by the TreeChooser.
+ */
+ public void setModel(TreeModel aTreeModel) {
+ stopListening();
+ model = aTreeModel;
+ bogusJTree.setModel(aTreeModel);
+ pathStack.removeAllElements();
+ startListening();
+ displayHome();
+ }
+
+ /**
+ * Returns the TreeSelectionModel used by the TreeChooser.
+ */
+ public TreeSelectionModel getSelectionModel() {
+ return selectionModel;
+ }
+
+ /**
+ * Sets the TreeSelectionModel used by the TreeChooser.
+ */
+ public void setSelectionModel(TreeSelectionModel aSelectionModel) {
+ selectionModel = aSelectionModel;
+ if (aSelectionModel.getSelectionMode() == TreeSelectionModel.SINGLE_TREE_SELECTION) {
+ contents.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+ } else {
+ contents.getSelectionModel().setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
+ }
+ updateSelection();
+ }
+
+ /**
+ * Returns the TreeCellRenderer used by the TreeChooser.
+ */
+ public TreeCellRenderer getRenderer() {
+ return renderer;
+ }
+
+ /**
+ * Sets the TreeCellRenderer used by the TreeChooser.
+ */
+ public void setRenderer(TreeCellRenderer aRenderer) {
+ renderer = aRenderer;
+ updateContents();
+ }
+
+ /**
+ * Displays the "home" directory. This implementation displays the root node's
+ * children.
+ */
+ public void displayHome() {
+ setDisplayPath(null);
+ }
+
+ /**
+ * Displays the parent path of the currently displayed path.
+ */
+ public void displayParent() {
+ setDisplayPath(displayPath.getParentPath());
+ }
+
+ /**
+ * Displays the last displayed path before the current one, emulating the
+ * behavior of a "back" button.
+ */
+ public void displayPrevious() {
+ if (pathStack.empty()) {
+ displayHome();
+ } else {
+ setDisplayPathDirect((TreePath) pathStack.pop());
+ updateContents();
+ }
+ }
+
+ /**
+ * Pushes the previous item onto the stack, sets the display path, and then
+ * updates the contents. If aPath is null, the root node's children are
+ * displayed.
+ */
+ public void setDisplayPath(TreePath aPath) {
+ if (aPath == null) {
+ aPath = new TreePath(getModel().getRoot());
+ }
+ if (!displayPath.equals(aPath)) {
+ pathStack.push(displayPath);
+ setDisplayPathDirect(aPath);
+ }
+ updateContents();
+ }
+
+ /**
+ * Sets the displayPath field and does not update the stack nor update the
+ * contents.
+ */
+ protected void setDisplayPathDirect(TreePath aPath) {
+ displayPath = aPath;
+ }
+
+ /**
+ * Gets the currently displayed path.
+ */
+ public TreePath getDisplayPath() {
+ return displayPath;
+ }
+
+ /**
+ * Called when selected path changes or when model indicates that the displayed
+ * path has changed.
+ */
+ protected void updateContents() {
+ stopListening();
+
+ // update combo box
+ comboBoxModel.fireContentsChanged();
+
+ // update list contents
+ Object displayedObject = displayPath.getLastPathComponent();
+ /*
+ * //FIXME: this display group doesn't seem to be getting the sort orderings
+ * from parent if ( displayedObject instanceof net.wotonomy.ui.EODisplayGroup )
+ * System.out.println(
+ * ((net.wotonomy.ui.EODisplayGroup)displayedObject).displayedObjects() );
+ */
+ int count = model.getChildCount(displayedObject);
+ Object[] children = new Object[count];
+ for (int i = 0; i < count; i++) {
+ children[i] = model.getChild(displayedObject, i);
+ }
+ contents.setListData(children);
+
+ startListening();
+
+ // synchronize the selection
+ updateSelection();
+ }
+
+ /**
+ * Updates the selection in the list to reflect the selection in the tree
+ * selection model.
+ */
+ public void updateSelection() {
+ int index;
+ Object last = displayPath.getLastPathComponent();
+ TreePath[] selectionPaths = selectionModel.getSelectionPaths();
+ if (selectionPaths != null) {
+ List selectedIndices = new LinkedList();
+ for (int i = 0; i < selectionPaths.length; i++) {
+ if (displayPath.equals(selectionPaths[i].getParentPath())) {
+ index = getModel().getIndexOfChild(last, selectionPaths[i].getLastPathComponent());
+ if (index != -1) {
+ selectedIndices.add(new Integer(index));
+ } else // should never happen
+ {
+ throw new WotonomyException("Could not find child of displayed node.");
+ }
+ }
+ }
+ int[] selected = new int[selectedIndices.size()];
+ for (int i = 0; i < selected.length; i++) {
+ selected[i] = ((Integer) selectedIndices.get(i)).intValue();
+ }
+ stopListening();
+ contents.setSelectedIndices(selected);
+ startListening();
+ }
+ }
+
+ // interface TreeModelListener
+
+ public void treeNodesChanged(TreeModelEvent evt) {
+ /*
+ * if ( displayPath.getLastPathComponent().toString().equals(
+ * evt.getTreePath().getLastPathComponent().toString() ) ) { System.out.println(
+ * "TreeChooser.treeNodesChanged: " + count++ );
+ */
+ updateContents();
+ /*
+ * } else { System.out.println( evt.getTreePath() + " != " + displayPath ); }
+ */
+ }
+
+ public void treeNodesInserted(TreeModelEvent evt) {
// updateContents();
- }
-
- public void treeNodesRemoved( TreeModelEvent evt )
- {
+ }
+
+ public void treeNodesRemoved(TreeModelEvent evt) {
// updateContents();
- }
-
- public void treeStructureChanged( TreeModelEvent evt )
- {
- if ( ( evt.getTreePath().equals( displayPath ) )
- || ( evt.getTreePath().isDescendant( displayPath ) ) )
- {
+ }
+
+ public void treeStructureChanged(TreeModelEvent evt) {
+ if ((evt.getTreePath().equals(displayPath)) || (evt.getTreePath().isDescendant(displayPath))) {
// setDisplayPath( evt.getTreePath() );
- }
-
- displayHome();
- }
-
- // interface TreeSelectionListener
-
- /**
- * Called when the tree selection model's value changes.
- * This is presumably an external change, so this calls
- * updateSelection.
- */
- public void valueChanged( TreeSelectionEvent evt )
- {
- updateSelection();
- }
-
- // interface ListSelectionListener
-
- /**
- * Called when user changes the selection in the list.
- * This implementation updates the tree selection model
- * with the corresponding selection.
- */
- public void valueChanged( ListSelectionEvent evt )
- {
- if ( ! evt.getValueIsAdjusting() )
- {
- Object last = displayPath.getLastPathComponent();
- int[] selection = contents.getSelectedIndices();
- TreePath[] selectionPaths = new TreePath[ selection.length ];
- for ( int i = 0; i < selection.length; i++ )
- {
- selectionPaths[i] = displayPath.pathByAddingChild(
- getModel().getChild( last, selection[i] ) );
- }
- selectionModel.setSelectionPaths( selectionPaths );
- }
-
- }
-
- // interface ListCellRenderer
-
- /**
- * This method returns the component returned by the tree cell renderer.
- */
- public Component getListCellRendererComponent(
- JList list,
- Object value,
- int index,
- boolean isSelected,
- boolean cellHasFocus )
- {
- boolean isLeaf = ( model.isLeaf( value ) );
-
- bogusJTree.setForeground( list.getForeground() );
- bogusJTree.setBackground( list.getBackground() );
-
- JComponent result = (JComponent) renderer.getTreeCellRendererComponent(
- bogusJTree, value, isSelected, (list != contents),
- isLeaf, index, cellHasFocus );
-/*
- if ( ( list != contents ) && ( index > -1 ) )
- {
- result.setBorder(
- BorderFactory.createEmptyBorder( 0, index*pathIndent, 0, 0 ) );
- }
- else
- {
- result.setBorder(
- BorderFactory.createEmptyBorder() );
- }
-*/
- return result;
- }
-
- // interface ActionListener
-
- public void actionPerformed( ActionEvent evt )
- {
- String command = evt.getActionCommand();
-
- if ( HOME.equals( command ) )
- {
- displayHome();
- }
- else
- if ( UP.equals( command ) )
- {
- displayParent();
- }
- else
- if ( BACK.equals( command ) )
- {
- displayPrevious();
- }
- else
- if ( SELECT.equals( command ) )
- {
- Cursor oldCursor = getCursor();
- setCursor( Cursor.getPredefinedCursor( Cursor.WAIT_CURSOR ) );
-
- int index = contents.getSelectedIndex();
- // if selection
- if ( index != -1 )
- {
- Object parent = displayPath.getLastPathComponent();
- Object child = getModel().getChild( parent, index );
- // if selected item is not a leaf
- if ( getModel().getChildCount( child ) > 0 )
- {
- // navigate to selected item
- setDisplayPath( displayPath.pathByAddingChild( child ) );
- }
- }
-
- setCursor( oldCursor );
- }
-
- }
-
- private class ChooserComboBoxModel implements ComboBoxModel
- {
- TreeChooser treeChooser;
- Vector listeners;
-
- ChooserComboBoxModel( TreeChooser aTreeChooser )
- {
- treeChooser = aTreeChooser;
- listeners = new Vector();
- }
-
- public int getSize()
- {
- return treeChooser.displayPath.getPathCount();
- }
-
- public Object getElementAt(int index)
- {
- return treeChooser.displayPath.getPathComponent( index );
- }
-
- public Object getSelectedItem()
- {
- return treeChooser.displayPath.getLastPathComponent();
- }
-
- public void setSelectedItem(Object anItem)
- {
- if ( ! (
- treeChooser.displayPath.getLastPathComponent().equals( anItem ) ) )
- {
- Object[] items = treeChooser.displayPath.getPath();
- TreePath path = new TreePath( getModel().getRoot() );
- for ( int i = 1; i < items.length; i++ )
- {
- if ( path.getLastPathComponent() == anItem )
- {
- treeChooser.setDisplayPath( path );
- return;
- }
- path = path.pathByAddingChild( items[i] );
- }
- }
- }
-
- public void addListDataListener(ListDataListener l)
- {
- listeners.add( l );
- }
-
- public void removeListDataListener(ListDataListener l)
- {
- listeners.remove( l );
- }
-
- public void fireContentsChanged()
- {
- Enumeration e = listeners.elements();
- while ( e.hasMoreElements() )
- {
- ((ListDataListener)e.nextElement()).contentsChanged(
- new ListDataEvent(
- this, ListDataEvent.CONTENTS_CHANGED, 0, getSize() ) );
- }
- }
- }
-
-}
+ }
+
+ displayHome();
+ }
+
+ // interface TreeSelectionListener
+
+ /**
+ * Called when the tree selection model's value changes. This is presumably an
+ * external change, so this calls updateSelection.
+ */
+ public void valueChanged(TreeSelectionEvent evt) {
+ updateSelection();
+ }
+ // interface ListSelectionListener
+ /**
+ * Called when user changes the selection in the list. This implementation
+ * updates the tree selection model with the corresponding selection.
+ */
+ public void valueChanged(ListSelectionEvent evt) {
+ if (!evt.getValueIsAdjusting()) {
+ Object last = displayPath.getLastPathComponent();
+ int[] selection = contents.getSelectedIndices();
+ TreePath[] selectionPaths = new TreePath[selection.length];
+ for (int i = 0; i < selection.length; i++) {
+ selectionPaths[i] = displayPath.pathByAddingChild(getModel().getChild(last, selection[i]));
+ }
+ selectionModel.setSelectionPaths(selectionPaths);
+ }
+
+ }
+
+ // interface ListCellRenderer
+
+ /**
+ * This method returns the component returned by the tree cell renderer.
+ */
+ public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected,
+ boolean cellHasFocus) {
+ boolean isLeaf = (model.isLeaf(value));
+
+ bogusJTree.setForeground(list.getForeground());
+ bogusJTree.setBackground(list.getBackground());
+
+ JComponent result = (JComponent) renderer.getTreeCellRendererComponent(bogusJTree, value, isSelected,
+ (list != contents), isLeaf, index, cellHasFocus);
+ /*
+ * if ( ( list != contents ) && ( index > -1 ) ) { result.setBorder(
+ * BorderFactory.createEmptyBorder( 0, index*pathIndent, 0, 0 ) ); } else {
+ * result.setBorder( BorderFactory.createEmptyBorder() ); }
+ */
+ return result;
+ }
+
+ // interface ActionListener
+
+ public void actionPerformed(ActionEvent evt) {
+ String command = evt.getActionCommand();
+
+ if (HOME.equals(command)) {
+ displayHome();
+ } else if (UP.equals(command)) {
+ displayParent();
+ } else if (BACK.equals(command)) {
+ displayPrevious();
+ } else if (SELECT.equals(command)) {
+ Cursor oldCursor = getCursor();
+ setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
+
+ int index = contents.getSelectedIndex();
+ // if selection
+ if (index != -1) {
+ Object parent = displayPath.getLastPathComponent();
+ Object child = getModel().getChild(parent, index);
+ // if selected item is not a leaf
+ if (getModel().getChildCount(child) > 0) {
+ // navigate to selected item
+ setDisplayPath(displayPath.pathByAddingChild(child));
+ }
+ }
+
+ setCursor(oldCursor);
+ }
+
+ }
+
+ private class ChooserComboBoxModel implements ComboBoxModel {
+ TreeChooser treeChooser;
+ Vector listeners;
+
+ ChooserComboBoxModel(TreeChooser aTreeChooser) {
+ treeChooser = aTreeChooser;
+ listeners = new Vector();
+ }
+
+ public int getSize() {
+ return treeChooser.displayPath.getPathCount();
+ }
+
+ public Object getElementAt(int index) {
+ return treeChooser.displayPath.getPathComponent(index);
+ }
+
+ public Object getSelectedItem() {
+ return treeChooser.displayPath.getLastPathComponent();
+ }
+
+ public void setSelectedItem(Object anItem) {
+ if (!(treeChooser.displayPath.getLastPathComponent().equals(anItem))) {
+ Object[] items = treeChooser.displayPath.getPath();
+ TreePath path = new TreePath(getModel().getRoot());
+ for (int i = 1; i < items.length; i++) {
+ if (path.getLastPathComponent() == anItem) {
+ treeChooser.setDisplayPath(path);
+ return;
+ }
+ path = path.pathByAddingChild(items[i]);
+ }
+ }
+ }
+
+ public void addListDataListener(ListDataListener l) {
+ listeners.add(l);
+ }
+
+ public void removeListDataListener(ListDataListener l) {
+ listeners.remove(l);
+ }
+
+ public void fireContentsChanged() {
+ Enumeration e = listeners.elements();
+ while (e.hasMoreElements()) {
+ ((ListDataListener) e.nextElement())
+ .contentsChanged(new ListDataEvent(this, ListDataEvent.CONTENTS_CHANGED, 0, getSize()));
+ }
+ }
+ }
+
+}
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/TreeTableCellRenderer.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/TreeTableCellRenderer.java
index fbf3791..c6e1a99 100644
--- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/TreeTableCellRenderer.java
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/TreeTableCellRenderer.java
@@ -32,193 +32,158 @@ import javax.swing.JViewport;
import javax.swing.table.TableCellRenderer;
/**
-* A TableCellRenderer that paints a portion of a JTree.
-* Extends JViewport to take advantage of buffering and
-* fast blitting (avoids repeated clipping and repainting).
-* Defaults opaque to false: to see selection background
-* painted, call setOpaque( true ).
-*
-* @author michael@mpowers.net
-* @author $Author: cgruber $
-* @version $Revision: 904 $
-*/
+ * A TableCellRenderer that paints a portion of a JTree. Extends JViewport to
+ * take advantage of buffering and fast blitting (avoids repeated clipping and
+ * repainting). Defaults opaque to false: to see selection background painted,
+ * call setOpaque( true ).
+ *
+ * @author michael@mpowers.net
+ * @author $Author: cgruber $
+ * @version $Revision: 904 $
+ */
public class TreeTableCellRenderer extends JViewport implements TableCellRenderer, MouseListener {
- JTree tree;
- Component emptyComponent;
- JTable delegateTable;
- int lastKnownColumn;
-
- /**
- * Constructor takes a JTree and modifies it by setting
- * rootVisible to false, showsRootHandles to true,
- * opaque to false, and border to null.
- */
- public TreeTableCellRenderer( JTree aTree )
- {
- setView( aTree );
- setBorder( null );
- tree = aTree;
- tree.setRootVisible( false );
- tree.setShowsRootHandles( true );
- tree.setBorder( null );
- tree.setOpaque( false );
-
- Object renderer = tree.getCellRenderer();
- if ( renderer instanceof JComponent )
- {
- ((JComponent)renderer).setOpaque( false );
- }
- Object editor = tree.getCellEditor();
- if ( editor instanceof JComponent )
- {
- ((JComponent)editor).setOpaque( false );
- }
-
- this.setOpaque( false );
- emptyComponent = new JLabel();
- }
-
- public Component getTableCellRendererComponent(
- JTable table, Object value,
- boolean isSelected, boolean hasFocus,
- int row, int column)
- {
- if ( isSelected )
- {
- setForeground( table.getSelectionForeground() );
- setBackground( table.getSelectionBackground() );
- }
- else
- {
- setForeground( table.getForeground() );
- setBackground( table.getBackground() );
- }
-
- lastKnownColumn = column;
- if ( delegateTable != table )
- {
- if ( delegateTable != null )
- {
- delegateTable.removeMouseListener( this );
- }
- table.addMouseListener( this );
- delegateTable = table;
- }
-
- Rectangle rect = tree.getRowBounds( row );
- if ( rect != null )
- {
- setViewPosition( new Point( 0 /*rect.x*/, rect.y ) );
-
- //FIXME: this causes problems for some LAFs (like Metal):
- // in particular, the table height seems to get stuck.
- //if ( table.getRowHeight( row ) != rect.height )
- //{
- // table.setRowHeight( row, rect.height );
- //}
- return this;
- }
- else
- {
- return emptyComponent;
- }
- }
-
- public void mouseClicked(MouseEvent e)
- {
- delegateToTree( e );
- }
-
- public void mousePressed(MouseEvent e)
- {
- delegateToTree( e );
- }
-
- public void mouseReleased(MouseEvent e)
- {
- delegateToTree( e );
- }
-
- public void mouseEntered(MouseEvent e)
- {
- delegateToTree( e );
- }
-
- public void mouseExited(MouseEvent e)
- {
- delegateToTree( e );
- }
-
- protected void delegateToTree(MouseEvent e)
- {
- int col = delegateTable.getColumnModel().getColumnIndexAtX( e.getX() );
- if ( col == lastKnownColumn )
- {
- Rectangle nodeRect = tree.getRowBounds( 0 );
- Rectangle cellRect = delegateTable.getCellRect( -1, col, false );
- if ( nodeRect != null )
- {
- e.translatePoint( -cellRect.x, nodeRect.y );
- tree.dispatchEvent( // e );
- new MouseEvent( tree, e.getID(), e.getWhen(), e.getModifiers(),
- e.getX(), e.getY(), e.getClickCount(), e.isPopupTrigger() ) );
- }
- }
- }
-
- public void repaint()
- {
- //if ( delegateTable != null ) delegateTable.repaint();
-
- // not calling super.repaint() does not seem to cause
- // any problems so we're not doing it.
- }
+ JTree tree;
+ Component emptyComponent;
+ JTable delegateTable;
+ int lastKnownColumn;
+
+ /**
+ * Constructor takes a JTree and modifies it by setting rootVisible to false,
+ * showsRootHandles to true, opaque to false, and border to null.
+ */
+ public TreeTableCellRenderer(JTree aTree) {
+ setView(aTree);
+ setBorder(null);
+ tree = aTree;
+ tree.setRootVisible(false);
+ tree.setShowsRootHandles(true);
+ tree.setBorder(null);
+ tree.setOpaque(false);
+
+ Object renderer = tree.getCellRenderer();
+ if (renderer instanceof JComponent) {
+ ((JComponent) renderer).setOpaque(false);
+ }
+ Object editor = tree.getCellEditor();
+ if (editor instanceof JComponent) {
+ ((JComponent) editor).setOpaque(false);
+ }
+
+ this.setOpaque(false);
+ emptyComponent = new JLabel();
+ }
+
+ public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
+ int row, int column) {
+ if (isSelected) {
+ setForeground(table.getSelectionForeground());
+ setBackground(table.getSelectionBackground());
+ } else {
+ setForeground(table.getForeground());
+ setBackground(table.getBackground());
+ }
+
+ lastKnownColumn = column;
+ if (delegateTable != table) {
+ if (delegateTable != null) {
+ delegateTable.removeMouseListener(this);
+ }
+ table.addMouseListener(this);
+ delegateTable = table;
+ }
+
+ Rectangle rect = tree.getRowBounds(row);
+ if (rect != null) {
+ setViewPosition(new Point(0 /* rect.x */, rect.y));
+
+ // FIXME: this causes problems for some LAFs (like Metal):
+ // in particular, the table height seems to get stuck.
+ // if ( table.getRowHeight( row ) != rect.height )
+ // {
+ // table.setRowHeight( row, rect.height );
+ // }
+ return this;
+ } else {
+ return emptyComponent;
+ }
+ }
+
+ public void mouseClicked(MouseEvent e) {
+ delegateToTree(e);
+ }
+
+ public void mousePressed(MouseEvent e) {
+ delegateToTree(e);
+ }
+
+ public void mouseReleased(MouseEvent e) {
+ delegateToTree(e);
+ }
+
+ public void mouseEntered(MouseEvent e) {
+ delegateToTree(e);
+ }
+
+ public void mouseExited(MouseEvent e) {
+ delegateToTree(e);
+ }
+
+ protected void delegateToTree(MouseEvent e) {
+ int col = delegateTable.getColumnModel().getColumnIndexAtX(e.getX());
+ if (col == lastKnownColumn) {
+ Rectangle nodeRect = tree.getRowBounds(0);
+ Rectangle cellRect = delegateTable.getCellRect(-1, col, false);
+ if (nodeRect != null) {
+ e.translatePoint(-cellRect.x, nodeRect.y);
+ tree.dispatchEvent( // e );
+ new MouseEvent(tree, e.getID(), e.getWhen(), e.getModifiers(), e.getX(), e.getY(),
+ e.getClickCount(), e.isPopupTrigger()));
+ }
+ }
+ }
+
+ public void repaint() {
+ // if ( delegateTable != null ) delegateTable.repaint();
+
+ // not calling super.repaint() does not seem to cause
+ // any problems so we're not doing it.
+ }
}
/*
- * $Log$
- * Revision 1.2 2006/02/18 23:19:05 cgruber
- * Update imports and maven dependencies.
+ * $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.1 2006/02/16 13:22:22 cgruber Check in all sources in
+ * eclipse-friendly maven-enabled packages.
*
- * Revision 1.11 2003/08/06 23:07:53 chochos
- * general code cleanup (mostly, removing unused imports)
+ * Revision 1.11 2003/08/06 23:07:53 chochos general code cleanup (mostly,
+ * removing unused imports)
*
- * Revision 1.10 2002/04/12 20:07:35 mpowers
- * Fixed cool/annoying view position.
+ * Revision 1.10 2002/04/12 20:07:35 mpowers Fixed cool/annoying view position.
*
- * Revision 1.9 2002/04/09 18:12:21 mpowers
- * Fixes for 1.4.
+ * Revision 1.9 2002/04/09 18:12:21 mpowers Fixes for 1.4.
*
- * Revision 1.8 2002/03/22 22:39:24 mpowers
- * Can now move column to any position in the table.
+ * Revision 1.8 2002/03/22 22:39:24 mpowers Can now move column to any position
+ * in the table.
*
- * Revision 1.7 2002/03/11 03:13:22 mpowers
- * Adjusting for viewport position; no longer responding to repaint().
+ * Revision 1.7 2002/03/11 03:13:22 mpowers Adjusting for viewport position; no
+ * longer responding to repaint().
*
- * Revision 1.6 2002/03/07 23:04:36 mpowers
- * Refining TreeColumnAssociation.
+ * Revision 1.6 2002/03/07 23:04:36 mpowers Refining TreeColumnAssociation.
*
- * Revision 1.5 2002/03/05 23:18:28 mpowers
- * Added documentation.
- * Added isSelectionPaintedImmediate and isSelectionTracking attributes
- * to TableAssociation.
- * Added getTableAssociation to TableColumnAssociation.
+ * Revision 1.5 2002/03/05 23:18:28 mpowers Added documentation. Added
+ * isSelectionPaintedImmediate and isSelectionTracking attributes to
+ * TableAssociation. Added getTableAssociation to TableColumnAssociation.
*
- * Revision 1.3 2002/02/27 23:19:17 mpowers
- * Refactoring of TreeAssociation to create TreeModelAssociation parent.
+ * Revision 1.3 2002/02/27 23:19:17 mpowers Refactoring of TreeAssociation to
+ * create TreeModelAssociation parent.
*
- * Revision 1.2 2002/02/18 23:13:55 mpowers
- * Only setting row height when needed.
+ * Revision 1.2 2002/02/18 23:13:55 mpowers Only setting row height when needed.
*
- * Revision 1.1 2002/02/18 03:46:08 mpowers
- * Implemented TreeTableCellRenderer.
+ * Revision 1.1 2002/02/18 03:46:08 mpowers Implemented TreeTableCellRenderer.
*
*
*/
-
-
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/ClassGrabber.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/ClassGrabber.java
index 4412dbc..0e95d2d 100644
--- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/ClassGrabber.java
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/ClassGrabber.java
@@ -25,79 +25,67 @@ import java.io.InputStream;
import java.util.Hashtable;
/**
- * ClassGrabber is a class loader used by WindowGrabber.
- * It simply loads classes by filename and nothing more.
- * It exists mainly because the java 1.1 class loading
- * framework doesn't easily allow the creation of class
- * loaders nor the loading of arbitrary classes.
+ * ClassGrabber is a class loader used by WindowGrabber. It simply loads classes
+ * by filename and nothing more. It exists mainly because the java 1.1 class
+ * loading framework doesn't easily allow the creation of class loaders nor the
+ * loading of arbitrary classes.
*
* @author michael@mpowers.net
- * @version $Revision: 904 $
- * $Date: 2006-02-18 18:19:05 -0500 (Sat, 18 Feb 2006) $
+ * @version $Revision: 904 $ $Date: 2006-02-18 18:19:05 -0500 (Sat, 18 Feb 2006)
+ * $
*/
-public class ClassGrabber extends ClassLoader
-{
+public class ClassGrabber extends ClassLoader {
Hashtable classMap = new Hashtable();
- public ClassGrabber()
- {
+ public ClassGrabber() {
super();
}
- protected Class loadClass(String name, boolean resolve)
- throws ClassNotFoundException
- {
- Class c = (Class) classMap.get( name );
+ protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
+ Class c = (Class) classMap.get(name);
- if ( c != null ) return c;
+ if (c != null)
+ return c;
- try
- {
- c = findSystemClass( name );
- }
- catch ( Exception exc1 )
- {
+ try {
+ c = findSystemClass(name);
+ } catch (Exception exc1) {
// System.err.print( "findSystemClass: " + name + ": " );
// System.err.println( exc1 );
}
- if ( c != null ) return c;
+ if (c != null)
+ return c;
- try
- {
- c = findLoadedClass( name );
- }
- catch ( Exception exc1 )
- {
+ try {
+ c = findLoadedClass(name);
+ } catch (Exception exc1) {
// System.err.print( "findLoadedClass: " + name + ": " );
// System.err.println( exc1 );
}
-
- if ( c != null ) return c;
-
- try
- {
- InputStream input = new BufferedInputStream( new FileInputStream( name ) );
- ByteArrayOutputStream output = new ByteArrayOutputStream( 200 );
+
+ if (c != null)
+ return c;
+
+ try {
+ InputStream input = new BufferedInputStream(new FileInputStream(name));
+ ByteArrayOutputStream output = new ByteArrayOutputStream(200);
int ch;
- while ( ( ch = input.read() ) != -1 )
- {
- output.write( ch );
+ while ((ch = input.read()) != -1) {
+ output.write(ch);
}
byte[] data = output.toByteArray();
- c = defineClass( null, data, 0, data.length );
- }
- catch ( Exception exc )
- {
- System.err.print( "getResource: " + name + ": " );
- System.err.println( exc );
- c = null;
+ c = defineClass(null, data, 0, data.length);
+ } catch (Exception exc) {
+ System.err.print("getResource: " + name + ": ");
+ System.err.println(exc);
+ c = null;
}
- if ( c != null )
- {
- classMap.put( name, c );
- if ( resolve ) resolveClass( c );
+ if (c != null) {
+ classMap.put(name, c);
+ if (resolve)
+ resolveClass(c);
}
return c;
@@ -105,22 +93,18 @@ public class ClassGrabber extends ClassLoader
}
/*
- * $Log$
- * Revision 1.2 2006/02/18 23:19:05 cgruber
- * Update imports and maven dependencies.
+ * $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.1 2006/02/16 13:22:22 cgruber Check in all sources in
+ * eclipse-friendly maven-enabled packages.
*
- * Revision 1.2 2003/08/06 23:07:53 chochos
- * general code cleanup (mostly, removing unused imports)
+ * Revision 1.2 2003/08/06 23:07:53 chochos general code cleanup (mostly,
+ * removing unused imports)
*
- * Revision 1.1.1.1 2000/12/21 15:51:18 mpowers
- * Contributing wotonomy.
+ * Revision 1.1.1.1 2000/12/21 15:51:18 mpowers Contributing wotonomy.
*
- * Revision 1.2 2000/12/20 16:25:45 michael
- * Added log to all files.
+ * Revision 1.2 2000/12/20 16:25:45 michael Added log to all files.
*
*
*/
-
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/ComponentHighlighter.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/ComponentHighlighter.java
index c63157d..08e6216 100644
--- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/ComponentHighlighter.java
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/ComponentHighlighter.java
@@ -36,125 +36,115 @@ import javax.swing.SwingUtilities;
import javax.swing.Timer;
/**
-* Visually highlights a component with the specified image for a brief period.
-*
-* @author michael@mpowers.net
-* @author $Author: cgruber $
-* @version $Revision: 904 $
-* $Date: 2006-02-18 18:19:05 -0500 (Sat, 18 Feb 2006) $
-*/
-public class ComponentHighlighter implements ActionListener
-{
- // lots of state to track
- JRootPane rootPane;
- JComponent component;
- Component oldGlassPane;
- JLabel imageLabel;
- Timer timer;
- JPanel glassPane;
-
-/**
-* Alternate "Fire-and-forget" constructor loads an image from a URL.
-* @param aComponent A Component that will be highlighted.
-* @param aURL A URL pointing to an image.
-*/
- public ComponentHighlighter( JComponent aComponent, URL aURL )
- {
- if ( aURL == null ) return;
- init( aComponent, Toolkit.getDefaultToolkit().getImage( aURL ) );
- }
-
-/**
-* "Fire-and-forget" constructor.
-* @param aComponent A Component that will be highlighted.
-* @param anImage An image, preferably an animated GIF with transparency,
-* that will slide along the length of the component.
-*/
- public ComponentHighlighter( JComponent aComponent, Image anImage )
- {
- init( aComponent, anImage );
- }
-
- protected void init( JComponent aComponent, Image anImage )
- {
- if ( ( aComponent == null ) || ( anImage == null ) ) return;
-
- component = aComponent;
- rootPane = SwingUtilities.getRootPane( component );
- oldGlassPane = rootPane.getGlassPane();
+ * Visually highlights a component with the specified image for a brief period.
+ *
+ * @author michael@mpowers.net
+ * @author $Author: cgruber $
+ * @version $Revision: 904 $ $Date: 2006-02-18 18:19:05 -0500 (Sat, 18 Feb 2006)
+ * $
+ */
+public class ComponentHighlighter implements ActionListener {
+ // lots of state to track
+ JRootPane rootPane;
+ JComponent component;
+ Component oldGlassPane;
+ JLabel imageLabel;
+ Timer timer;
+ JPanel glassPane;
+
+ /**
+ * Alternate "Fire-and-forget" constructor loads an image from a URL.
+ *
+ * @param aComponent A Component that will be highlighted.
+ * @param aURL A URL pointing to an image.
+ */
+ public ComponentHighlighter(JComponent aComponent, URL aURL) {
+ if (aURL == null)
+ return;
+ init(aComponent, Toolkit.getDefaultToolkit().getImage(aURL));
+ }
+
+ /**
+ * "Fire-and-forget" constructor.
+ *
+ * @param aComponent A Component that will be highlighted.
+ * @param anImage An image, preferably an animated GIF with transparency,
+ * that will slide along the length of the component.
+ */
+ public ComponentHighlighter(JComponent aComponent, Image anImage) {
+ init(aComponent, anImage);
+ }
+
+ protected void init(JComponent aComponent, Image anImage) {
+ if ((aComponent == null) || (anImage == null))
+ return;
+
+ component = aComponent;
+ rootPane = SwingUtilities.getRootPane(component);
+ oldGlassPane = rootPane.getGlassPane();
glassPane = new JPanel();
- rootPane.setGlassPane( glassPane );
- glassPane.setVisible( true );
- glassPane.setOpaque( false );
- glassPane.setLayout( null );
-
- ImageIcon icon = new ImageIcon( anImage );
-
- imageLabel = new JLabel();
- imageLabel.setIconTextGap( 0 );
- imageLabel.setIcon( icon );
- imageLabel.setSize( icon.getIconWidth(), icon.getIconHeight() );
- glassPane.add( imageLabel );
-
- Rectangle bounds = component.getBounds();
- if ( component.getParent() instanceof Component )
- {
- bounds = SwingUtilities.convertRectangle( (Container) component.getParent(),
- bounds, rootPane.getContentPane() );
- }
- imageLabel.setLocation(
- bounds.x, bounds.y + bounds.height - imageLabel.getBounds().height );
-
- glassPane.revalidate();
- glassPane.repaint();
-
- component.transferFocus(); // halts a caret, if necessary
-
- timer = new Timer( 80, this );
- timer.setRepeats( true );
- timer.start();
- }
-
- public void actionPerformed( ActionEvent evt )
- {
- Rectangle bounds = imageLabel.getBounds();
- Rectangle target = component.getBounds();
- if ( component.getParent() instanceof Component )
- {
- target = SwingUtilities.convertRectangle( (Container) component.getParent(),
- target, rootPane.getContentPane() );
- }
-
- if ( bounds.x + bounds.width > target.x + target.width )
- { // clean up and end
- timer.stop();
- rootPane.setGlassPane( oldGlassPane );
- component.requestFocus();
- return;
- }
-
- // else, slide to the right and continue
- imageLabel.setLocation(
- bounds.x + Math.max( bounds.width / 12, 1 ), bounds.y );
- imageLabel.repaint();
- }
+ rootPane.setGlassPane(glassPane);
+ glassPane.setVisible(true);
+ glassPane.setOpaque(false);
+ glassPane.setLayout(null);
+
+ ImageIcon icon = new ImageIcon(anImage);
+
+ imageLabel = new JLabel();
+ imageLabel.setIconTextGap(0);
+ imageLabel.setIcon(icon);
+ imageLabel.setSize(icon.getIconWidth(), icon.getIconHeight());
+ glassPane.add(imageLabel);
+
+ Rectangle bounds = component.getBounds();
+ if (component.getParent() instanceof Component) {
+ bounds = SwingUtilities.convertRectangle((Container) component.getParent(), bounds,
+ rootPane.getContentPane());
+ }
+ imageLabel.setLocation(bounds.x, bounds.y + bounds.height - imageLabel.getBounds().height);
+
+ glassPane.revalidate();
+ glassPane.repaint();
+
+ component.transferFocus(); // halts a caret, if necessary
+
+ timer = new Timer(80, this);
+ timer.setRepeats(true);
+ timer.start();
+ }
+
+ public void actionPerformed(ActionEvent evt) {
+ Rectangle bounds = imageLabel.getBounds();
+ Rectangle target = component.getBounds();
+ if (component.getParent() instanceof Component) {
+ target = SwingUtilities.convertRectangle((Container) component.getParent(), target,
+ rootPane.getContentPane());
+ }
+
+ if (bounds.x + bounds.width > target.x + target.width) { // clean up and end
+ timer.stop();
+ rootPane.setGlassPane(oldGlassPane);
+ component.requestFocus();
+ return;
+ }
+
+ // else, slide to the right and continue
+ imageLabel.setLocation(bounds.x + Math.max(bounds.width / 12, 1), bounds.y);
+ imageLabel.repaint();
+ }
}
/*
- * $Log$
- * Revision 1.2 2006/02/18 23:19:05 cgruber
- * Update imports and maven dependencies.
+ * $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.1 2006/02/16 13:22:22 cgruber Check in all sources in
+ * eclipse-friendly maven-enabled packages.
*
- * Revision 1.1.1.1 2000/12/21 15:51:18 mpowers
- * Contributing wotonomy.
+ * Revision 1.1.1.1 2000/12/21 15:51:18 mpowers Contributing wotonomy.
*
- * Revision 1.2 2000/12/20 16:25:45 michael
- * Added log to all files.
+ * Revision 1.2 2000/12/20 16:25:45 michael Added log to all files.
*
*
*/
-
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/GIFEncoder.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/GIFEncoder.java
index 82fd897..9ff7496 100644
--- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/GIFEncoder.java
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/GIFEncoder.java
@@ -12,509 +12,445 @@ import java.io.IOException;
import java.io.OutputStream;
/**
- * GIFEncoder is a class which takes an image and saves it to a stream
- * using the GIF file format. A GIFEncoder is constructed with either
- * an AWT Image (which must be fully loaded) or a set of RGB arrays.
- * The image can be written out with a call to <CODE>write</CODE>.<br><br>
+ * GIFEncoder is a class which takes an image and saves it to a stream using the
+ * GIF file format. A GIFEncoder is constructed with either an AWT Image (which
+ * must be fully loaded) or a set of RGB arrays. The image can be written out
+ * with a call to <CODE>write</CODE>.<br>
+ * <br>
*
* Three caveats:
* <UL>
- * <LI>GIFEncoder will convert the image to indexed color upon
- * construction, and this is not fast.
+ * <LI>GIFEncoder will convert the image to indexed color upon construction, and
+ * this is not fast.
*
- * <LI>The image cannot have more than 256 colors, since GIF is an 8
- * bit format.
+ * <LI>The image cannot have more than 256 colors, since GIF is an 8 bit format.
*
- * <LI>Since the image must be completely loaded into memory,
- * there may be problems with large images.
+ * <LI>Since the image must be completely loaded into memory, there may be
+ * problems with large images.
* </UL>
*
- * This implementation is heavily based on code made available by
- * Adam Doppelt, which was based upon gifsave.c, which was written
- * and released by Sverre H. Huseby.
+ * This implementation is heavily based on code made available by Adam Doppelt,
+ * which was based upon gifsave.c, which was written and released by Sverre H.
+ * Huseby.
*
* @author amd@brown.edu
* @author sverrehu@ifi.uio.no
* @author michael@mpowers.net
*/
-public class GIFEncoder
-{
- short width_, height_;
- int numColors_;
- byte pixels_[], colors_[];
-
- ScreenDescriptor sd_;
- ImageDescriptor id_;
-
-/**
- * Construct a GIFEncoder. The constructor will convert the image to
- * an indexed color array. This may take some time. If more than 256
- * colors are encountered, all subsequent colors are mapped to the first
- * color encountered.
- * @param image The image to encode. The image must be completely loaded.
- * @exception AWTException Will be thrown if the pixel grab fails. This
- * can happen if Java runs out of memory.
- * */
- public GIFEncoder(Image image) throws AWTException
- {
- width_ = (short)image.getWidth(null);
- height_ = (short)image.getHeight(null);
-
- int values[] = new int[width_ * height_];
- PixelGrabber grabber = new PixelGrabber(
- image, 0, 0, width_, height_, values, 0, width_);
-
- try
- {
- if(grabber.grabPixels() != true)
- throw new AWTException("Grabber returned false: " +
- grabber.status());
- }
- catch (InterruptedException e)
- {
- }
-
- byte r[][] = new byte[width_][height_];
- byte g[][] = new byte[width_][height_];
- byte b[][] = new byte[width_][height_];
- int index = 0;
- for (int y = 0; y < height_; ++y)
- {
- for (int x = 0; x < width_; ++x)
- {
- r[x][y] = (byte)((values[index] >> 16) & 0xFF);
- g[x][y] = (byte)((values[index] >> 8) & 0xFF);
- b[x][y] = (byte)((values[index]) & 0xFF);
- ++index;
- }
- }
- toIndexedColor(r, g, b);
- }
-
-/**
- * Construct a GIFEncoder. The constructor will convert the image to
- * an indexed color array. This may take some time. <br><br>
- * Each array stores intensity values for the image. In other words,
- * r[x][y] refers to the red intensity of the pixel at column x, row y.
- * @param r An array containing the red intensity values.
- * @param g An array containing the green intensity values.
- * @param b An array containing the blue intensity values.
- *
- * @exception AWTException Will be thrown if the image contains more than
- * 256 colors.
- * */
- public GIFEncoder(byte r[][], byte g[][], byte b[][]) throws AWTException
- {
- width_ = (short)(r.length);
- height_ = (short)(r[0].length);
-
- toIndexedColor(r, g, b);
- }
-
-/**
- * Writes the image out to a stream in the GIF file format. This will
- * be a single GIF87a image, non-interlaced, with no background color.
- * <B>This may take some time.</B><P>
- *
- * @param output The stream to output to. This should probably be a
- * buffered stream.
- *
- * @exception IOException Will be thrown if a write operation fails.
- * */
- public void write(OutputStream output) throws IOException
- {
- BitUtils.writeString(output, "GIF87a");
- ScreenDescriptor sd = new ScreenDescriptor(width_, height_,
- numColors_);
- sd.write(output);
-
- output.write(colors_, 0, colors_.length);
-
- ImageDescriptor id = new ImageDescriptor(width_, height_, ',');
- id.write(output);
-
- byte codesize = BitUtils.bitsNeeded(numColors_);
- if (codesize == 1)
- ++codesize;
- output.write(codesize);
-
- LZWCompressor.LZWCompress(output, codesize, pixels_);
- output.write(0);
- id = new ImageDescriptor((byte)0, (byte)0, ';');
- id.write(output);
- output.flush();
- }
-
- void toIndexedColor(byte r[][], byte g[][],
- byte b[][]) throws AWTException
- {
- pixels_ = new byte[width_ * height_];
- colors_ = new byte[256 * 3];
- int colornum = 0;
- for (int x = 0; x < width_; ++x)
- {
- for (int y = 0; y < height_; ++y)
- {
- int search;
- for (search = 0; search < colornum; ++search)
- if (colors_[search * 3] == r[x][y] &&
- colors_[search * 3 + 1] == g[x][y] &&
- colors_[search * 3 + 2] == b[x][y])
- break;
-
- if (search > 255)
- search = 0;
- //throw new AWTException("Too many colors.");
-
- pixels_[y * width_ + x] = (byte)search;
-
- if (search == colornum) {
- colors_[search * 3] = r[x][y];
- colors_[search * 3 + 1] = g[x][y];
- colors_[search * 3 + 2] = b[x][y];
- ++colornum;
- }
- }
- }
-
- numColors_ = 1 << BitUtils.bitsNeeded(colornum);
- byte copy[] = new byte[numColors_ * 3];
- System.arraycopy(colors_, 0, copy, 0, numColors_ * 3);
- colors_ = copy;
- }
+public class GIFEncoder {
+ short width_, height_;
+ int numColors_;
+ byte pixels_[], colors_[];
+
+ ScreenDescriptor sd_;
+ ImageDescriptor id_;
+
+ /**
+ * Construct a GIFEncoder. The constructor will convert the image to an indexed
+ * color array. This may take some time. If more than 256 colors are
+ * encountered, all subsequent colors are mapped to the first color encountered.
+ *
+ * @param image The image to encode. The image must be completely loaded.
+ * @exception AWTException Will be thrown if the pixel grab fails. This can
+ * happen if Java runs out of memory.
+ */
+ public GIFEncoder(Image image) throws AWTException {
+ width_ = (short) image.getWidth(null);
+ height_ = (short) image.getHeight(null);
+
+ int values[] = new int[width_ * height_];
+ PixelGrabber grabber = new PixelGrabber(image, 0, 0, width_, height_, values, 0, width_);
+
+ try {
+ if (grabber.grabPixels() != true)
+ throw new AWTException("Grabber returned false: " + grabber.status());
+ } catch (InterruptedException e) {
+ }
+
+ byte r[][] = new byte[width_][height_];
+ byte g[][] = new byte[width_][height_];
+ byte b[][] = new byte[width_][height_];
+ int index = 0;
+ for (int y = 0; y < height_; ++y) {
+ for (int x = 0; x < width_; ++x) {
+ r[x][y] = (byte) ((values[index] >> 16) & 0xFF);
+ g[x][y] = (byte) ((values[index] >> 8) & 0xFF);
+ b[x][y] = (byte) ((values[index]) & 0xFF);
+ ++index;
+ }
+ }
+ toIndexedColor(r, g, b);
+ }
+
+ /**
+ * Construct a GIFEncoder. The constructor will convert the image to an indexed
+ * color array. This may take some time. <br>
+ * <br>
+ * Each array stores intensity values for the image. In other words, r[x][y]
+ * refers to the red intensity of the pixel at column x, row y.
+ *
+ * @param r An array containing the red intensity values.
+ * @param g An array containing the green intensity values.
+ * @param b An array containing the blue intensity values.
+ *
+ * @exception AWTException Will be thrown if the image contains more than 256
+ * colors.
+ */
+ public GIFEncoder(byte r[][], byte g[][], byte b[][]) throws AWTException {
+ width_ = (short) (r.length);
+ height_ = (short) (r[0].length);
+
+ toIndexedColor(r, g, b);
+ }
+
+ /**
+ * Writes the image out to a stream in the GIF file format. This will be a
+ * single GIF87a image, non-interlaced, with no background color. <B>This may
+ * take some time.</B>
+ * <P>
+ *
+ * @param output The stream to output to. This should probably be a buffered
+ * stream.
+ *
+ * @exception IOException Will be thrown if a write operation fails.
+ */
+ public void write(OutputStream output) throws IOException {
+ BitUtils.writeString(output, "GIF87a");
+ ScreenDescriptor sd = new ScreenDescriptor(width_, height_, numColors_);
+ sd.write(output);
+
+ output.write(colors_, 0, colors_.length);
+
+ ImageDescriptor id = new ImageDescriptor(width_, height_, ',');
+ id.write(output);
+
+ byte codesize = BitUtils.bitsNeeded(numColors_);
+ if (codesize == 1)
+ ++codesize;
+ output.write(codesize);
+
+ LZWCompressor.LZWCompress(output, codesize, pixels_);
+ output.write(0);
+ id = new ImageDescriptor((byte) 0, (byte) 0, ';');
+ id.write(output);
+ output.flush();
+ }
+
+ void toIndexedColor(byte r[][], byte g[][], byte b[][]) throws AWTException {
+ pixels_ = new byte[width_ * height_];
+ colors_ = new byte[256 * 3];
+ int colornum = 0;
+ for (int x = 0; x < width_; ++x) {
+ for (int y = 0; y < height_; ++y) {
+ int search;
+ for (search = 0; search < colornum; ++search)
+ if (colors_[search * 3] == r[x][y] && colors_[search * 3 + 1] == g[x][y]
+ && colors_[search * 3 + 2] == b[x][y])
+ break;
+
+ if (search > 255)
+ search = 0;
+ // throw new AWTException("Too many colors.");
+
+ pixels_[y * width_ + x] = (byte) search;
+
+ if (search == colornum) {
+ colors_[search * 3] = r[x][y];
+ colors_[search * 3 + 1] = g[x][y];
+ colors_[search * 3 + 2] = b[x][y];
+ ++colornum;
+ }
+ }
+ }
+
+ numColors_ = 1 << BitUtils.bitsNeeded(colornum);
+ byte copy[] = new byte[numColors_ * 3];
+ System.arraycopy(colors_, 0, copy, 0, numColors_ * 3);
+ colors_ = copy;
+ }
}
-class BitFile
-{
- OutputStream output_;
- byte buffer_[];
- int index_, bitsLeft_;
-
- public BitFile(OutputStream output)
- {
- output_ = output;
- buffer_ = new byte[256];
- index_ = 0;
- bitsLeft_ = 8;
- }
-
- public void flush() throws IOException
- {
- int numBytes = index_ + (bitsLeft_ == 8 ? 0 : 1);
- if (numBytes > 0)
- {
- output_.write(numBytes);
- output_.write(buffer_, 0, numBytes);
- buffer_[0] = 0;
- index_ = 0;
- bitsLeft_ = 8;
- }
- }
-
- public void writeBits(int bits, int numbits) throws IOException {
- int bitsWritten = 0;
- int numBytes = 255;
- do
- {
- if ((index_ == 254 && bitsLeft_ == 0) || index_ > 254)
- {
- output_.write(numBytes);
- output_.write(buffer_, 0, numBytes);
-
- buffer_[0] = 0;
- index_ = 0;
- bitsLeft_ = 8;
- }
-
- if (numbits <= bitsLeft_)
- {
- buffer_[index_] |= (bits & ((1 << numbits) - 1)) <<
- (8 - bitsLeft_);
- bitsWritten += numbits;
- bitsLeft_ -= numbits;
- numbits = 0;
- }
- else
- {
- buffer_[index_] |= (bits & ((1 << bitsLeft_) - 1)) <<
- (8 - bitsLeft_);
- bitsWritten += bitsLeft_;
- bits >>= bitsLeft_;
- numbits -= bitsLeft_;
- buffer_[++index_] = 0;
- bitsLeft_ = 8;
- }
-
- }
- while (numbits != 0);
- }
+class BitFile {
+ OutputStream output_;
+ byte buffer_[];
+ int index_, bitsLeft_;
+
+ public BitFile(OutputStream output) {
+ output_ = output;
+ buffer_ = new byte[256];
+ index_ = 0;
+ bitsLeft_ = 8;
+ }
+
+ public void flush() throws IOException {
+ int numBytes = index_ + (bitsLeft_ == 8 ? 0 : 1);
+ if (numBytes > 0) {
+ output_.write(numBytes);
+ output_.write(buffer_, 0, numBytes);
+ buffer_[0] = 0;
+ index_ = 0;
+ bitsLeft_ = 8;
+ }
+ }
+
+ public void writeBits(int bits, int numbits) throws IOException {
+ int bitsWritten = 0;
+ int numBytes = 255;
+ do {
+ if ((index_ == 254 && bitsLeft_ == 0) || index_ > 254) {
+ output_.write(numBytes);
+ output_.write(buffer_, 0, numBytes);
+
+ buffer_[0] = 0;
+ index_ = 0;
+ bitsLeft_ = 8;
+ }
+
+ if (numbits <= bitsLeft_) {
+ buffer_[index_] |= (bits & ((1 << numbits) - 1)) << (8 - bitsLeft_);
+ bitsWritten += numbits;
+ bitsLeft_ -= numbits;
+ numbits = 0;
+ } else {
+ buffer_[index_] |= (bits & ((1 << bitsLeft_) - 1)) << (8 - bitsLeft_);
+ bitsWritten += bitsLeft_;
+ bits >>= bitsLeft_;
+ numbits -= bitsLeft_;
+ buffer_[++index_] = 0;
+ bitsLeft_ = 8;
+ }
+
+ } while (numbits != 0);
+ }
}
-class LZWStringTable
-{
- private final static int RES_CODES = 2;
- private final static short HASH_FREE = (short)0xFFFF;
- private final static short NEXT_FIRST = (short)0xFFFF;
- private final static int MAXBITS = 12;
- private final static int MAXSTR = (1 << MAXBITS);
- private final static short HASHSIZE = 9973;
- private final static short HASHSTEP = 2039;
-
- byte strChr_[];
- short strNxt_[];
- short strHsh_[];
- short numStrings_;
-
- public LZWStringTable()
- {
- strChr_ = new byte[MAXSTR];
- strNxt_ = new short[MAXSTR];
- strHsh_ = new short[HASHSIZE];
- }
-
- public int addCharString(short index, byte b)
- {
- int hshidx;
-
- if (numStrings_ >= MAXSTR)
- return 0xFFFF;
-
- hshidx = Hash(index, b);
- while (strHsh_[hshidx] != HASH_FREE)
- hshidx = (hshidx + HASHSTEP) % HASHSIZE;
-
- strHsh_[hshidx] = numStrings_;
- strChr_[numStrings_] = b;
- strNxt_[numStrings_] = (index != HASH_FREE) ? index : NEXT_FIRST;
-
- return numStrings_++;
- }
-
- public short findCharString(short index, byte b)
- {
- int hshidx, nxtidx;
-
- if (index == HASH_FREE)
- return b;
-
- hshidx = Hash(index, b);
- while ((nxtidx = strHsh_[hshidx]) != HASH_FREE)
- {
- if (strNxt_[nxtidx] == index && strChr_[nxtidx] == b)
- return (short)nxtidx;
- hshidx = (hshidx + HASHSTEP) % HASHSIZE;
- }
-
- return (short)0xFFFF;
- }
-
- public void clearTable(int codesize)
- {
- numStrings_ = 0;
-
- for (int q = 0; q < HASHSIZE; q++)
- {
- strHsh_[q] = HASH_FREE;
- }
-
- int w = (1 << codesize) + RES_CODES;
- for (int q = 0; q < w; q++)
- {
- addCharString((short)0xFFFF, (byte)q);
- }
- }
-
- static public int Hash(short index, byte lastbyte)
- {
- return ((int)((short)(lastbyte << 8) ^ index) & 0xFFFF) % HASHSIZE;
- }
+class LZWStringTable {
+ private final static int RES_CODES = 2;
+ private final static short HASH_FREE = (short) 0xFFFF;
+ private final static short NEXT_FIRST = (short) 0xFFFF;
+ private final static int MAXBITS = 12;
+ private final static int MAXSTR = (1 << MAXBITS);
+ private final static short HASHSIZE = 9973;
+ private final static short HASHSTEP = 2039;
+
+ byte strChr_[];
+ short strNxt_[];
+ short strHsh_[];
+ short numStrings_;
+
+ public LZWStringTable() {
+ strChr_ = new byte[MAXSTR];
+ strNxt_ = new short[MAXSTR];
+ strHsh_ = new short[HASHSIZE];
+ }
+
+ public int addCharString(short index, byte b) {
+ int hshidx;
+
+ if (numStrings_ >= MAXSTR)
+ return 0xFFFF;
+
+ hshidx = Hash(index, b);
+ while (strHsh_[hshidx] != HASH_FREE)
+ hshidx = (hshidx + HASHSTEP) % HASHSIZE;
+
+ strHsh_[hshidx] = numStrings_;
+ strChr_[numStrings_] = b;
+ strNxt_[numStrings_] = (index != HASH_FREE) ? index : NEXT_FIRST;
+
+ return numStrings_++;
+ }
+
+ public short findCharString(short index, byte b) {
+ int hshidx, nxtidx;
+
+ if (index == HASH_FREE)
+ return b;
+
+ hshidx = Hash(index, b);
+ while ((nxtidx = strHsh_[hshidx]) != HASH_FREE) {
+ if (strNxt_[nxtidx] == index && strChr_[nxtidx] == b)
+ return (short) nxtidx;
+ hshidx = (hshidx + HASHSTEP) % HASHSIZE;
+ }
+
+ return (short) 0xFFFF;
+ }
+
+ public void clearTable(int codesize) {
+ numStrings_ = 0;
+
+ for (int q = 0; q < HASHSIZE; q++) {
+ strHsh_[q] = HASH_FREE;
+ }
+
+ int w = (1 << codesize) + RES_CODES;
+ for (int q = 0; q < w; q++) {
+ addCharString((short) 0xFFFF, (byte) q);
+ }
+ }
+
+ static public int Hash(short index, byte lastbyte) {
+ return ((int) ((short) (lastbyte << 8) ^ index) & 0xFFFF) % HASHSIZE;
+ }
}
class LZWCompressor {
- public static void LZWCompress(OutputStream output, int codesize,
- byte toCompress[]) throws IOException
- {
- byte c;
- short index;
- int clearcode, endofinfo, numbits, limit, errcode;
- short prefix = (short)0xFFFF;
-
- BitFile bitFile = new BitFile(output);
- LZWStringTable strings = new LZWStringTable();
-
- clearcode = 1 << codesize;
- endofinfo = clearcode + 1;
-
- numbits = codesize + 1;
- limit = (1 << numbits) - 1;
- strings.clearTable(codesize);
- bitFile.writeBits(clearcode, numbits);
-
- for (int loop = 0; loop < toCompress.length; ++loop)
- {
- c = toCompress[loop];
- if ((index = strings.findCharString(prefix, c)) != -1)
- {
- prefix = index;
- }
- else
- {
- bitFile.writeBits(prefix, numbits);
- if (strings.addCharString(prefix, c) > limit) {
- if (++numbits > 12) {
- bitFile.writeBits(clearcode, numbits - 1);
- strings.clearTable(codesize);
- numbits = codesize + 1;
- }
- limit = (1 << numbits) - 1;
- }
-
- prefix = (short)((short)c & 0xFF);
- }
- }
-
- if (prefix != -1)
- bitFile.writeBits(prefix, numbits);
-
- bitFile.writeBits(endofinfo, numbits);
- bitFile.flush();
- }
+ public static void LZWCompress(OutputStream output, int codesize, byte toCompress[]) throws IOException {
+ byte c;
+ short index;
+ int clearcode, endofinfo, numbits, limit, errcode;
+ short prefix = (short) 0xFFFF;
+
+ BitFile bitFile = new BitFile(output);
+ LZWStringTable strings = new LZWStringTable();
+
+ clearcode = 1 << codesize;
+ endofinfo = clearcode + 1;
+
+ numbits = codesize + 1;
+ limit = (1 << numbits) - 1;
+ strings.clearTable(codesize);
+ bitFile.writeBits(clearcode, numbits);
+
+ for (int loop = 0; loop < toCompress.length; ++loop) {
+ c = toCompress[loop];
+ if ((index = strings.findCharString(prefix, c)) != -1) {
+ prefix = index;
+ } else {
+ bitFile.writeBits(prefix, numbits);
+ if (strings.addCharString(prefix, c) > limit) {
+ if (++numbits > 12) {
+ bitFile.writeBits(clearcode, numbits - 1);
+ strings.clearTable(codesize);
+ numbits = codesize + 1;
+ }
+ limit = (1 << numbits) - 1;
+ }
+
+ prefix = (short) ((short) c & 0xFF);
+ }
+ }
+
+ if (prefix != -1)
+ bitFile.writeBits(prefix, numbits);
+
+ bitFile.writeBits(endofinfo, numbits);
+ bitFile.flush();
+ }
}
-class ScreenDescriptor
-{
- public short localScreenWidth_, localScreenHeight_;
- private byte byte_;
- public byte backgroundColorIndex_, pixelAspectRatio_;
-
- public ScreenDescriptor(short width, short height, int numColors)
- {
- localScreenWidth_ = width;
- localScreenHeight_ = height;
- setGlobalColorTableSize((byte)(BitUtils.bitsNeeded(numColors) - 1));
- setGlobalColorTableFlag((byte)1);
- setSortFlag((byte)0);
- setColorResolution((byte)7);
- backgroundColorIndex_ = 0;
- pixelAspectRatio_ = 0;
- }
-
- public void write(OutputStream output) throws IOException
- {
- BitUtils.writeWord(output, localScreenWidth_);
- BitUtils.writeWord(output, localScreenHeight_);
- output.write(byte_);
- output.write(backgroundColorIndex_);
- output.write(pixelAspectRatio_);
- }
-
- public void setGlobalColorTableSize(byte num)
- {
- byte_ |= (num & 7);
- }
-
- public void setSortFlag(byte num)
- {
- byte_ |= (num & 1) << 3;
- }
-
- public void setColorResolution(byte num)
- {
- byte_ |= (num & 7) << 4;
- }
-
- public void setGlobalColorTableFlag(byte num)
- {
- byte_ |= (num & 1) << 7;
- }
+class ScreenDescriptor {
+ public short localScreenWidth_, localScreenHeight_;
+ private byte byte_;
+ public byte backgroundColorIndex_, pixelAspectRatio_;
+
+ public ScreenDescriptor(short width, short height, int numColors) {
+ localScreenWidth_ = width;
+ localScreenHeight_ = height;
+ setGlobalColorTableSize((byte) (BitUtils.bitsNeeded(numColors) - 1));
+ setGlobalColorTableFlag((byte) 1);
+ setSortFlag((byte) 0);
+ setColorResolution((byte) 7);
+ backgroundColorIndex_ = 0;
+ pixelAspectRatio_ = 0;
+ }
+
+ public void write(OutputStream output) throws IOException {
+ BitUtils.writeWord(output, localScreenWidth_);
+ BitUtils.writeWord(output, localScreenHeight_);
+ output.write(byte_);
+ output.write(backgroundColorIndex_);
+ output.write(pixelAspectRatio_);
+ }
+
+ public void setGlobalColorTableSize(byte num) {
+ byte_ |= (num & 7);
+ }
+
+ public void setSortFlag(byte num) {
+ byte_ |= (num & 1) << 3;
+ }
+
+ public void setColorResolution(byte num) {
+ byte_ |= (num & 7) << 4;
+ }
+
+ public void setGlobalColorTableFlag(byte num) {
+ byte_ |= (num & 1) << 7;
+ }
}
-class ImageDescriptor
-{
- public byte separator_;
- public short leftPosition_, topPosition_, width_, height_;
- private byte byte_;
-
- public ImageDescriptor(short width, short height, char separator)
- {
- separator_ = (byte)separator;
- leftPosition_ = 0;
- topPosition_ = 0;
- width_ = width;
- height_ = height;
- setLocalColorTableSize((byte)0);
- setReserved((byte)0);
- setSortFlag((byte)0);
- setInterlaceFlag((byte)0);
- setLocalColorTableFlag((byte)0);
- }
-
- public void write(OutputStream output) throws IOException
- {
- output.write(separator_);
- BitUtils.writeWord(output, leftPosition_);
- BitUtils.writeWord(output, topPosition_);
- BitUtils.writeWord(output, width_);
- BitUtils.writeWord(output, height_);
- output.write(byte_);
- }
-
- public void setLocalColorTableSize(byte num)
- {
- byte_ |= (num & 7);
- }
-
- public void setReserved(byte num)
- {
- byte_ |= (num & 3) << 3;
- }
-
- public void setSortFlag(byte num)
- {
- byte_ |= (num & 1) << 5;
- }
-
- public void setInterlaceFlag(byte num)
- {
- byte_ |= (num & 1) << 6;
- }
-
- public void setLocalColorTableFlag(byte num)
- {
- byte_ |= (num & 1) << 7;
- }
+class ImageDescriptor {
+ public byte separator_;
+ public short leftPosition_, topPosition_, width_, height_;
+ private byte byte_;
+
+ public ImageDescriptor(short width, short height, char separator) {
+ separator_ = (byte) separator;
+ leftPosition_ = 0;
+ topPosition_ = 0;
+ width_ = width;
+ height_ = height;
+ setLocalColorTableSize((byte) 0);
+ setReserved((byte) 0);
+ setSortFlag((byte) 0);
+ setInterlaceFlag((byte) 0);
+ setLocalColorTableFlag((byte) 0);
+ }
+
+ public void write(OutputStream output) throws IOException {
+ output.write(separator_);
+ BitUtils.writeWord(output, leftPosition_);
+ BitUtils.writeWord(output, topPosition_);
+ BitUtils.writeWord(output, width_);
+ BitUtils.writeWord(output, height_);
+ output.write(byte_);
+ }
+
+ public void setLocalColorTableSize(byte num) {
+ byte_ |= (num & 7);
+ }
+
+ public void setReserved(byte num) {
+ byte_ |= (num & 3) << 3;
+ }
+
+ public void setSortFlag(byte num) {
+ byte_ |= (num & 1) << 5;
+ }
+
+ public void setInterlaceFlag(byte num) {
+ byte_ |= (num & 1) << 6;
+ }
+
+ public void setLocalColorTableFlag(byte num) {
+ byte_ |= (num & 1) << 7;
+ }
}
-class BitUtils
-{
- public static byte bitsNeeded(int n)
- {
- byte ret = 1;
-
- if (n-- == 0)
- return 0;
-
- while ((n >>= 1) != 0)
- ++ret;
-
- return ret;
- }
-
- public static void writeWord(OutputStream output,
- short w) throws IOException
- {
- output.write(w & 0xFF);
- output.write((w >> 8) & 0xFF);
- }
-
- static void writeString(OutputStream output,
- String string) throws IOException
- {
- for (int loop = 0; loop < string.length(); ++loop)
- output.write((byte)(string.charAt(loop)));
- }
-}
+class BitUtils {
+ public static byte bitsNeeded(int n) {
+ byte ret = 1;
+ if (n-- == 0)
+ return 0;
+ while ((n >>= 1) != 0)
+ ++ret;
+
+ return ret;
+ }
+
+ public static void writeWord(OutputStream output, short w) throws IOException {
+ output.write(w & 0xFF);
+ output.write((w >> 8) & 0xFF);
+ }
+
+ static void writeString(OutputStream output, String string) throws IOException {
+ for (int loop = 0; loop < string.length(); ++loop)
+ output.write((byte) (string.charAt(loop)));
+ }
+}
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/ObjectInspector.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/ObjectInspector.java
index 6c8d7ee..ad94ddd 100644
--- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/ObjectInspector.java
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/ObjectInspector.java
@@ -44,183 +44,165 @@ import net.wotonomy.ui.swing.components.PropertyEditorTable;
import net.wotonomy.ui.swing.components.PropertyEditorTableModel;
/**
-* The ObjectInspector displays a JFrame containing
-* a PropertyEditorTable that displays an object. <br><br>
-*
-* @author michael@mpowers.net
-* @author $Author: cgruber $
-* @version $Revision: 904 $
-*/
+ * The ObjectInspector displays a JFrame containing a PropertyEditorTable that
+ * displays an object. <br>
+ * <br>
+ *
+ * @author michael@mpowers.net
+ * @author $Author: cgruber $
+ * @version $Revision: 904 $
+ */
-public class ObjectInspector implements ActionListener, MouseListener
-{
- protected JTable table = null;
+public class ObjectInspector implements ActionListener, MouseListener {
+ protected JTable table = null;
- // key command to copy contents to clipboard
- static public final String COPY = "COPY";
+ // key command to copy contents to clipboard
+ static public final String COPY = "COPY";
/**
- * Displays the specified object in a frame.
- */
- public ObjectInspector( Object anObject )
- {
- initLayout( anObject );
- }
-
- protected void initLayout( Object aTargetObject )
- {
- PropertyEditorTableModel model =
- new PropertyEditorTableModel();
- model.setObject( aTargetObject );
- table = new PropertyEditorTable()
- {
- public void methodInvoked( Object anObject, Method aMethod, Object aResult )
- {
- if
- ( ( aResult == null )
- || ( aResult instanceof Number )
- || ( aResult instanceof Boolean )
- || ( aResult instanceof String ) )
- {
- System.out.println( aMethod.getName() + ": " + aResult );
- }
- else
- {
- new ObjectInspector( aResult );
+ * Displays the specified object in a frame.
+ */
+ public ObjectInspector(Object anObject) {
+ initLayout(anObject);
+ }
+
+ protected void initLayout(Object aTargetObject) {
+ PropertyEditorTableModel model = new PropertyEditorTableModel();
+ model.setObject(aTargetObject);
+ table = new PropertyEditorTable() {
+ public void methodInvoked(Object anObject, Method aMethod, Object aResult) {
+ if ((aResult == null) || (aResult instanceof Number) || (aResult instanceof Boolean)
+ || (aResult instanceof String)) {
+ System.out.println(aMethod.getName() + ": " + aResult);
+ } else {
+ new ObjectInspector(aResult);
}
}
};
- table.setModel( model );
- table.addMouseListener( this ); // listen for double-clicks
-
- // set up keyboard events for cut-copy: Ctrl-C, Ctrl-X
- table.registerKeyboardAction( this, COPY,
- KeyStroke.getKeyStroke( KeyEvent.VK_C,
- Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() ),
- JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT );
- table.registerKeyboardAction( this, COPY,
- KeyStroke.getKeyStroke( KeyEvent.VK_X,
- Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() ),
- JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT );
-
- JPanel panel = new JPanel();
- panel.setBorder( new EmptyBorder( new Insets( 10, 10, 10, 10 ) ) );
- panel.setLayout( new BorderLayout( 10, 10 ) );
-
- JScrollPane scrollPane = new JScrollPane( table );
- scrollPane.setPreferredSize( new Dimension( 325, 350 ) );
- panel.add( scrollPane, BorderLayout.CENTER );
-
- JFrame window = new JFrame();
- window.setTitle( aTargetObject.getClass().getName() );
- window.getContentPane().add( panel );
-
- window.pack();
- WindowUtilities.cascade( window );
- window.show();
- }
-
- // interface MouseListener
-
- /**
- * Double click to call invokeFileFromString.
- */
-
- public void mouseClicked(MouseEvent e)
- {
- if ( e.getSource() == table )
- {
- if ( e.getClickCount() > 1 )
- {
- int row = table.rowAtPoint( e.getPoint() );
- int col = table.columnAtPoint( e.getPoint() );
-
- if ( ( row == -1 ) || ( col != 0 ) ) return;
-
- /* do something here */
- }
- }
- }
-
- public void mouseReleased(MouseEvent e) {}
- public void mousePressed(MouseEvent e) {}
- public void mouseEntered(MouseEvent e) {}
- public void mouseExited(MouseEvent e) {}
-
-
- // interface ActionEventListener - for listening to key commands
-
- public void actionPerformed(ActionEvent evt)
- {
- if ( COPY.equals( evt.getActionCommand() ) )
- {
- copyToClipboard();
- return;
- }
- }
+ table.setModel(model);
+ table.addMouseListener(this); // listen for double-clicks
+
+ // set up keyboard events for cut-copy: Ctrl-C, Ctrl-X
+ table.registerKeyboardAction(this, COPY,
+ KeyStroke.getKeyStroke(KeyEvent.VK_C, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()),
+ JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
+ table.registerKeyboardAction(this, COPY,
+ KeyStroke.getKeyStroke(KeyEvent.VK_X, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()),
+ JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
+
+ JPanel panel = new JPanel();
+ panel.setBorder(new EmptyBorder(new Insets(10, 10, 10, 10)));
+ panel.setLayout(new BorderLayout(10, 10));
+
+ JScrollPane scrollPane = new JScrollPane(table);
+ scrollPane.setPreferredSize(new Dimension(325, 350));
+ panel.add(scrollPane, BorderLayout.CENTER);
+
+ JFrame window = new JFrame();
+ window.setTitle(aTargetObject.getClass().getName());
+ window.getContentPane().add(panel);
+
+ window.pack();
+ WindowUtilities.cascade(window);
+ window.show();
+ }
+
+ // interface MouseListener
-/**
-* Copies the contents of the table to the clipboard as a tab-delimited string.
-*/
- public void copyToClipboard()
- {
- Toolkit toolkit = Toolkit.getDefaultToolkit();
- Clipboard clipboard = toolkit.getSystemClipboard();
- StringSelection selection =
- new StringSelection( getTabDelimitedString() );
- clipboard.setContents( selection, selection );
- }
-
/**
- * Converts the contents of the table to a tab-delimited string.
- * @return A String containing the text contents of the table.
- */
- public String getTabDelimitedString()
- {
- StringBuffer result = new StringBuffer(64);
-
- TableModel model = table.getModel();
- int cols = model.getColumnCount();
- int rows = model.getRowCount();
-
- Object o = null;
- for ( int y = 0; y < rows; y++ )
- {
- for ( int x = 0; x < cols; x++ )
- {
- o = model.getValueAt( y, x );
- if ( o == null ) o = "";
- result.append( o );
- result.append( '\t' );
- }
- result.append( '\n' );
- }
-
- return result.toString();
- }
+ * Double click to call invokeFileFromString.
+ */
+
+ public void mouseClicked(MouseEvent e) {
+ if (e.getSource() == table) {
+ if (e.getClickCount() > 1) {
+ int row = table.rowAtPoint(e.getPoint());
+ int col = table.columnAtPoint(e.getPoint());
+
+ if ((row == -1) || (col != 0))
+ return;
+
+ /* do something here */
+ }
+ }
+ }
+
+ public void mouseReleased(MouseEvent e) {
+ }
+
+ public void mousePressed(MouseEvent e) {
+ }
+
+ public void mouseEntered(MouseEvent e) {
+ }
+
+ public void mouseExited(MouseEvent e) {
+ }
+
+ // interface ActionEventListener - for listening to key commands
+
+ public void actionPerformed(ActionEvent evt) {
+ if (COPY.equals(evt.getActionCommand())) {
+ copyToClipboard();
+ return;
+ }
+ }
+
+ /**
+ * Copies the contents of the table to the clipboard as a tab-delimited string.
+ */
+ public void copyToClipboard() {
+ Toolkit toolkit = Toolkit.getDefaultToolkit();
+ Clipboard clipboard = toolkit.getSystemClipboard();
+ StringSelection selection = new StringSelection(getTabDelimitedString());
+ clipboard.setContents(selection, selection);
+ }
+
+ /**
+ * Converts the contents of the table to a tab-delimited string.
+ *
+ * @return A String containing the text contents of the table.
+ */
+ public String getTabDelimitedString() {
+ StringBuffer result = new StringBuffer(64);
+
+ TableModel model = table.getModel();
+ int cols = model.getColumnCount();
+ int rows = model.getRowCount();
+
+ Object o = null;
+ for (int y = 0; y < rows; y++) {
+ for (int x = 0; x < cols; x++) {
+ o = model.getValueAt(y, x);
+ if (o == null)
+ o = "";
+ result.append(o);
+ result.append('\t');
+ }
+ result.append('\n');
+ }
+
+ return result.toString();
+ }
}
/*
- * $Log$
- * Revision 1.2 2006/02/18 23:19:05 cgruber
- * Update imports and maven dependencies.
+ * $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.1 2006/02/16 13:22:22 cgruber Check in all sources in
+ * eclipse-friendly maven-enabled packages.
*
- * Revision 1.3 2003/08/06 23:07:53 chochos
- * general code cleanup (mostly, removing unused imports)
+ * Revision 1.3 2003/08/06 23:07:53 chochos general code cleanup (mostly,
+ * removing unused imports)
*
- * Revision 1.2 2002/11/16 16:33:31 mpowers
- * Now using platform-specific accelerator key for shortcuts.
+ * Revision 1.2 2002/11/16 16:33:31 mpowers Now using platform-specific
+ * accelerator key for shortcuts.
*
- * Revision 1.1.1.1 2000/12/21 15:51:27 mpowers
- * Contributing wotonomy.
+ * Revision 1.1.1.1 2000/12/21 15:51:27 mpowers Contributing wotonomy.
*
- * Revision 1.3 2000/12/20 16:25:45 michael
- * Added log to all files.
+ * Revision 1.3 2000/12/20 16:25:45 michael Added log to all files.
*
*
*/
-
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/PositionComparator.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/PositionComparator.java
index f5fe3e4..7f2adb8 100644
--- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/PositionComparator.java
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/PositionComparator.java
@@ -27,63 +27,55 @@ import java.util.Comparator;
import javax.swing.SwingUtilities;
/**
-* A Comparator that will sort components in a common container
-* based first on their y-coordinate and then on their x-coordinate,
-* producing a list sorted from top to bottom and left to right.
-* If all components are not in the same container, the resulting
-* sort is undefined.
-*
-* @author michael@mpowers.net
-* @author $Author: cgruber $
-* @version $Revision: 904 $
-*/
-public class PositionComparator implements Comparator, Serializable
-{
- private Container rootContainer;
- private transient Component c1, c2;
- private transient Point p1, p2;
+ * A Comparator that will sort components in a common container based first on
+ * their y-coordinate and then on their x-coordinate, producing a list sorted
+ * from top to bottom and left to right. If all components are not in the same
+ * container, the resulting sort is undefined.
+ *
+ * @author michael@mpowers.net
+ * @author $Author: cgruber $
+ * @version $Revision: 904 $
+ */
+public class PositionComparator implements Comparator, Serializable {
+ private Container rootContainer;
+ private transient Component c1, c2;
+ private transient Point p1, p2;
-/**
-* Standard constructor to configure the comparator.
-* @param aContainer The common container for all the objects to be compared.
-*/
- public PositionComparator( Container aContainer )
- {
- rootContainer = aContainer;
- }
+ /**
+ * Standard constructor to configure the comparator.
+ *
+ * @param aContainer The common container for all the objects to be compared.
+ */
+ public PositionComparator(Container aContainer) {
+ rootContainer = aContainer;
+ }
- // interface Comparable
+ // interface Comparable
- public int compare(Object o1, Object o2)
- {
- c1 = (Component) o1;
- c2 = (Component) o2;
+ public int compare(Object o1, Object o2) {
+ c1 = (Component) o1;
+ c2 = (Component) o2;
- p1 = SwingUtilities.convertPoint( c1.getParent(), c1.getLocation(), rootContainer );
- p2 = SwingUtilities.convertPoint( c2.getParent(), c2.getLocation(), rootContainer );
+ p1 = SwingUtilities.convertPoint(c1.getParent(), c1.getLocation(), rootContainer);
+ p2 = SwingUtilities.convertPoint(c2.getParent(), c2.getLocation(), rootContainer);
- if ( p1.y != p2.y )
- {
- return p1.y - p2.y;
- }
- return p1.x - p2.x;
- }
+ if (p1.y != p2.y) {
+ return p1.y - p2.y;
+ }
+ return p1.x - p2.x;
+ }
}
/*
- * $Log$
- * Revision 1.2 2006/02/18 23:19:05 cgruber
- * Update imports and maven dependencies.
+ * $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.1 2006/02/16 13:22:22 cgruber Check in all sources in
+ * eclipse-friendly maven-enabled packages.
*
- * Revision 1.1.1.1 2000/12/21 15:51:27 mpowers
- * Contributing wotonomy.
+ * Revision 1.1.1.1 2000/12/21 15:51:27 mpowers Contributing wotonomy.
*
- * Revision 1.2 2000/12/20 16:25:45 michael
- * Added log to all files.
+ * Revision 1.2 2000/12/20 16:25:45 michael Added log to all files.
*
*
*/
-
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/StackTraceInspector.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/StackTraceInspector.java
index 7e61411..ae4bb2d 100644
--- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/StackTraceInspector.java
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/StackTraceInspector.java
@@ -50,408 +50,364 @@ import javax.swing.table.TableModel;
import net.wotonomy.ui.swing.components.MultiLineLabel;
/**
-* The StackTraceInspector displays a JFrame containing
-* stack trace information for a Throwable. <br><br>
-*
-* There are also a few static methods for obtaining
-* information about the current stack, which is useful
-* for determining who's calling you at runtime.
-*
-* @author michael@mpowers.net
-* @version $Revision: 904 $
-*/
+ * The StackTraceInspector displays a JFrame containing stack trace information
+ * for a Throwable. <br>
+ * <br>
+ *
+ * There are also a few static methods for obtaining information about the
+ * current stack, which is useful for determining who's calling you at runtime.
+ *
+ * @author michael@mpowers.net
+ * @version $Revision: 904 $
+ */
-public class StackTraceInspector
- implements TableModel, MouseListener, ActionListener
-{
- protected JTable table = null;
- protected List tableModelListeners = null;
- protected List methodNames = new ArrayList();
+public class StackTraceInspector implements TableModel, MouseListener, ActionListener {
+ protected JTable table = null;
+ protected List tableModelListeners = null;
+ protected List methodNames = new ArrayList();
+
+ // key command to copy contents to clipboard
+ static public final String COPY = "COPY";
+
+ /**
+ * Displays the current stack trace at the time of instantiation in a table on a
+ * frame.
+ */
+ public StackTraceInspector() {
+ initLayout(parseStackTrace(new RuntimeException()), null);
+ }
+
+ /**
+ * Displays the current stack trace at the time of instantiation in a table on a
+ * frame annotated with the specified message.
+ */
+ public StackTraceInspector(String aMessage) {
+ initLayout(parseStackTrace(new RuntimeException()), aMessage);
+ }
+
+ /**
+ * Displays the stack trace for the given throwable in a table on a frame.
+ *
+ * @param aThrowable A Throwable whose stack will be examined.
+ */
+ public StackTraceInspector(Throwable aThrowable) {
+ initLayout(parseStackTrace(aThrowable), aThrowable.getClass() + ": " + aThrowable.getMessage());
+ }
+
+ /**
+ * Simply displays the list items in a dialog. Presumably (but not necessarily)
+ * called from the other constructors. (I guess if you just want a frame with
+ * strings in table, you can call this.)
+ *
+ * @param aStringList A List containing Strings.
+ */
+ public StackTraceInspector(List aStringList) {
+ initLayout(aStringList, null);
+ }
+
+ protected void initLayout(List items, String message) {
+ methodNames = new ArrayList(items);
+ table = new JTable(this); // this class is the table model
+ table.addMouseListener(this); // listen for double-clicks
+
+ // set up keyboard events for cut-copy: Ctrl-C, Ctrl-X
+ table.registerKeyboardAction(this, COPY,
+ KeyStroke.getKeyStroke(KeyEvent.VK_C, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()),
+ JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
+ table.registerKeyboardAction(this, COPY,
+ KeyStroke.getKeyStroke(KeyEvent.VK_X, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()),
+ JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
+
+ JPanel panel = new JPanel();
+ panel.setBorder(new EmptyBorder(new Insets(10, 10, 10, 10)));
+ panel.setLayout(new BorderLayout(10, 10));
+
+ if (message != null) {
+ panel.add(new MultiLineLabel(message), BorderLayout.NORTH);
+ }
- // key command to copy contents to clipboard
- static public final String COPY = "COPY";
+ JScrollPane scrollPane = new JScrollPane(table);
+ scrollPane.setPreferredSize(new Dimension(325, 350));
+ panel.add(scrollPane, BorderLayout.CENTER);
-/**
-* Displays the current stack trace at the time
-* of instantiation in a table on a frame.
-*/
- public StackTraceInspector()
- {
- initLayout( parseStackTrace( new RuntimeException() ), null );
- }
+ JFrame window = new JFrame();
+ window.setTitle("Stack Trace Inspector");
+ window.getContentPane().add(panel);
-/**
-* Displays the current stack trace at the time
-* of instantiation in a table on a frame
-* annotated with the specified message.
-*/
- public StackTraceInspector( String aMessage )
- {
- initLayout( parseStackTrace( new RuntimeException() ), aMessage );
- }
+ window.pack();
+ WindowUtilities.cascade(window);
+ window.show();
+ }
-/**
-* Displays the stack trace for the given throwable
-* in a table on a frame.
-* @param aThrowable A Throwable whose stack will be examined.
-*/
- public StackTraceInspector( Throwable aThrowable )
- {
- initLayout( parseStackTrace( aThrowable ),
- aThrowable.getClass() + ": " + aThrowable.getMessage() );
- }
+ // interface TableModel
-/**
-* Simply displays the list items in a dialog.
-* Presumably (but not necessarily) called from
-* the other constructors. (I guess if you just
-* want a frame with strings in table, you can
-* call this.)
-* @param aStringList A List containing Strings.
-*/
- public StackTraceInspector( List aStringList )
- {
- initLayout( aStringList, null );
- }
-
- protected void initLayout( List items, String message )
- {
- methodNames = new ArrayList( items );
- table = new JTable( this ); // this class is the table model
- table.addMouseListener( this ); // listen for double-clicks
-
- // set up keyboard events for cut-copy: Ctrl-C, Ctrl-X
- table.registerKeyboardAction( this, COPY,
- KeyStroke.getKeyStroke( KeyEvent.VK_C,
- Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() ),
- JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT );
- table.registerKeyboardAction( this, COPY,
- KeyStroke.getKeyStroke( KeyEvent.VK_X,
- Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() ),
- JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT );
-
- JPanel panel = new JPanel();
- panel.setBorder( new EmptyBorder( new Insets( 10, 10, 10, 10 ) ) );
- panel.setLayout( new BorderLayout( 10, 10 ) );
-
- if ( message != null )
- {
- panel.add( new MultiLineLabel( message ), BorderLayout.NORTH );
+ public int getRowCount() {
+ return methodNames.size();
+ }
+
+ public int getColumnCount() {
+ return 1;
+ }
+
+ public String getColumnName(int columnIndex) {
+ switch (columnIndex) {
+ case 0:
+ return "Methods";
+ case 1:
+ return "Property";
}
+ System.out.println("StackTraceInspector.getColumnName: unknown column: " + columnIndex);
+ return "";
+ }
+
+ public Class getColumnClass(int columnIndex) {
+ switch (columnIndex) {
+ case 0:
+ return String.class;
+ case 1:
+ return String.class;
+ }
+ System.out.println("StackTraceInspector.getColumnClass: unknown column: " + columnIndex);
+ return Object.class;
+ }
- JScrollPane scrollPane = new JScrollPane( table );
- scrollPane.setPreferredSize( new Dimension( 325, 350 ) );
- panel.add( scrollPane, BorderLayout.CENTER );
-
- JFrame window = new JFrame();
- window.setTitle( "Stack Trace Inspector" );
- window.getContentPane().add( panel );
-
- window.pack();
- WindowUtilities.cascade( window );
- window.show();
- }
-
- // interface TableModel
-
- public int getRowCount()
- {
- return methodNames.size();
- }
-
- public int getColumnCount()
- {
- return 1;
- }
-
- public String getColumnName(int columnIndex)
- {
- switch ( columnIndex )
- {
- case 0:
- return "Methods";
- case 1:
- return "Property";
- }
- System.out.println( "StackTraceInspector.getColumnName: unknown column: " + columnIndex );
- return "";
- }
-
- public Class getColumnClass(int columnIndex)
- {
- switch ( columnIndex )
- {
- case 0:
- return String.class;
- case 1:
- return String.class;
- }
- System.out.println( "StackTraceInspector.getColumnClass: unknown column: " + columnIndex );
- return Object.class;
- }
-
- public boolean isCellEditable(int rowIndex,
- int columnIndex)
- {
- return false;
- }
-
- public Object getValueAt(int rowIndex,
- int columnIndex)
- {
- return methodNames.get( rowIndex );
- }
-
- public void setValueAt(Object aValue,
- int rowIndex,
- int columnIndex)
- {
- }
-
- public void addTableModelListener(TableModelListener l)
- {
- if ( tableModelListeners == null )
- {
- tableModelListeners = new ArrayList();
- }
- tableModelListeners.add( l );
- }
-
- public void removeTableModelListener(TableModelListener l)
- {
- if ( tableModelListeners != null )
- {
- tableModelListeners.remove( l );
- }
- }
-
- // interface MouseListener
-
- /**
- * Double click to call invokeFileFromString.
- */
-
- public void mouseClicked(MouseEvent e)
- {
- if ( e.getSource() == table )
- {
- if ( e.getClickCount() > 1 )
- {
- int row = table.rowAtPoint( e.getPoint() );
- int col = table.columnAtPoint( e.getPoint() );
-
- if ( ( row == -1 ) || ( col != 0 ) ) return;
-
- invokeFileFromString( methodNames.get( row ).toString() );
- }
- }
- }
-
- public void mouseReleased(MouseEvent e) {}
- public void mousePressed(MouseEvent e) {}
- public void mouseEntered(MouseEvent e) {}
- public void mouseExited(MouseEvent e) {}
-
-
- // interface ActionEventListener - for listening to key commands
-
- public void actionPerformed(ActionEvent evt)
- {
- if ( COPY.equals( evt.getActionCommand() ) )
- {
- copyToClipboard();
- return;
- }
- }
+ public boolean isCellEditable(int rowIndex, int columnIndex) {
+ return false;
+ }
-/**
-* Copies the contents of the table to the clipboard as a tab-delimited string.
-*/
- public void copyToClipboard()
- {
- Toolkit toolkit = Toolkit.getDefaultToolkit();
- Clipboard clipboard = toolkit.getSystemClipboard();
- StringSelection selection =
- new StringSelection( getSelectedStackString() );
- clipboard.setContents( selection, selection );
- }
+ public Object getValueAt(int rowIndex, int columnIndex) {
+ return methodNames.get(rowIndex);
+ }
-/**
-* Converts the selected contents of the table to a string.
-* @return A String containing the text contents of the table.
-*/
- public String getSelectedStackString()
- {
- StringBuffer result = new StringBuffer(64);
+ public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
+ }
- TableModel model = table.getModel();
+ public void addTableModelListener(TableModelListener l) {
+ if (tableModelListeners == null) {
+ tableModelListeners = new ArrayList();
+ }
+ tableModelListeners.add(l);
+ }
- Object o;
- int[] selectedRows = table.getSelectedRows();
- for ( int i = 0; i < selectedRows.length; i++ )
- {
- o = model.getValueAt( selectedRows[i], 0 );
- if ( o == null ) o = "";
- result.append( o );
- result.append( '\n' );
- }
+ public void removeTableModelListener(TableModelListener l) {
+ if (tableModelListeners != null) {
+ tableModelListeners.remove(l);
+ }
+ }
- return result.toString();
- }
+ // interface MouseListener
+ /**
+ * Double click to call invokeFileFromString.
+ */
- // static methods
+ public void mouseClicked(MouseEvent e) {
+ if (e.getSource() == table) {
+ if (e.getClickCount() > 1) {
+ int row = table.rowAtPoint(e.getPoint());
+ int col = table.columnAtPoint(e.getPoint());
-/**
-* Obtains a list of strings representing the stack trace
-* associated with this throwable starting with the most recent call.
-* @param aThrowable A Throwable whose stack trace is parsed.
-* @return a List containing the method names as Strings.
-*/
- static public List parseStackTrace( Throwable aThrowable )
- {
- String trace = null;
+ if ((row == -1) || (col != 0))
+ return;
- // create new stream
- ByteArrayOutputStream os = new ByteArrayOutputStream( 256 );
- PrintStream newErr = new PrintStream( os );
- aThrowable.printStackTrace( newErr ); // prints to System.err
+ invokeFileFromString(methodNames.get(row).toString());
+ }
+ }
+ }
- // convert to string
- trace = os.toString();
+ public void mouseReleased(MouseEvent e) {
+ }
- List result = new ArrayList();
+ public void mousePressed(MouseEvent e) {
+ }
- // populate list with parsed trace, starting from top
- String token;
- StringTokenizer tokens = new StringTokenizer( trace, "\n" );
- tokens.nextToken(); // strip off description of throwable
- while ( tokens.hasMoreTokens() )
- {
- token = tokens.nextToken();
- if ( token.indexOf( StackTraceInspector.class.getName() ) == -1 )
- { // add only those methods not from this class
+ public void mouseEntered(MouseEvent e) {
+ }
- // strip whitespace, "at " from front, and \r from end
- token.trim();
- token = token.substring( 4, token.length() - 1 );
+ public void mouseExited(MouseEvent e) {
+ }
- result.add( token );
- }
- }
+ // interface ActionEventListener - for listening to key commands
- return result;
- }
+ public void actionPerformed(ActionEvent evt) {
+ if (COPY.equals(evt.getActionCommand())) {
+ copyToClipboard();
+ return;
+ }
+ }
+
+ /**
+ * Copies the contents of the table to the clipboard as a tab-delimited string.
+ */
+ public void copyToClipboard() {
+ Toolkit toolkit = Toolkit.getDefaultToolkit();
+ Clipboard clipboard = toolkit.getSystemClipboard();
+ StringSelection selection = new StringSelection(getSelectedStackString());
+ clipboard.setContents(selection, selection);
+ }
+
+ /**
+ * Converts the selected contents of the table to a string.
+ *
+ * @return A String containing the text contents of the table.
+ */
+ public String getSelectedStackString() {
+ StringBuffer result = new StringBuffer(64);
+
+ TableModel model = table.getModel();
+
+ Object o;
+ int[] selectedRows = table.getSelectedRows();
+ for (int i = 0; i < selectedRows.length; i++) {
+ o = model.getValueAt(selectedRows[i], 0);
+ if (o == null)
+ o = "";
+ result.append(o);
+ result.append('\n');
+ }
-/**
-* Convenience method that obtains a String representing
-* the caller's caller.
-* @return a String representing a method in stack trace format.
-*/
- static public String getMyCaller()
- {
- List trace = parseStackTrace( new RuntimeException() );
- if ( trace.size() > 1 )
- {
- return trace.get( 1 ).toString();
- }
+ return result.toString();
+ }
+
+ // static methods
+
+ /**
+ * Obtains a list of strings representing the stack trace associated with this
+ * throwable starting with the most recent call.
+ *
+ * @param aThrowable A Throwable whose stack trace is parsed.
+ * @return a List containing the method names as Strings.
+ */
+ static public List parseStackTrace(Throwable aThrowable) {
+ String trace = null;
+
+ // create new stream
+ ByteArrayOutputStream os = new ByteArrayOutputStream(256);
+ PrintStream newErr = new PrintStream(os);
+ aThrowable.printStackTrace(newErr); // prints to System.err
+
+ // convert to string
+ trace = os.toString();
+
+ List result = new ArrayList();
+
+ // populate list with parsed trace, starting from top
+ String token;
+ StringTokenizer tokens = new StringTokenizer(trace, "\n");
+ tokens.nextToken(); // strip off description of throwable
+ while (tokens.hasMoreTokens()) {
+ token = tokens.nextToken();
+ if (token.indexOf(StackTraceInspector.class.getName()) == -1) { // add only those methods not from this
+ // class
+
+ // strip whitespace, "at " from front, and \r from end
+ token.trim();
+ token = token.substring(4, token.length() - 1);
+
+ result.add(token);
+ }
+ }
- return null;
- }
+ return result;
+ }
+
+ /**
+ * Convenience method that obtains a String representing the caller's caller.
+ *
+ * @return a String representing a method in stack trace format.
+ */
+ static public String getMyCaller() {
+ List trace = parseStackTrace(new RuntimeException());
+ if (trace.size() > 1) {
+ return trace.get(1).toString();
+ }
-/**
-* Prints a stack trace up to the first method whose fully
-* qualified class name begins with "java" to System.out.
-*/
- static public void printShortStackTrace()
- {
- String s;
- Iterator i = parseStackTrace( new RuntimeException() ).iterator();
- while ( i.hasNext() )
- {
- System.out.println( " " + ( s = i.next().toString() ) );
- if ( s.startsWith( "java" ) ) break;
- }
- }
-
- protected void invokeFileFromString( String aString )
- {
- // strip off parentheses, if any
- int openParam = aString.indexOf( "(" );
- if ( openParam != -1 )
- {
- aString = aString.substring( 0, openParam );
- }
-
- // separate class name from method name
- int lastDot = aString.lastIndexOf( "." );
- if ( lastDot == -1 ) return;
- String className = aString.substring( 0, lastDot );
- String methodName = aString.substring( lastDot + 1 );
-
- // convert "."s to file separator characters
- StringBuffer buf = new StringBuffer();
- StringTokenizer tokens = new StringTokenizer( className, "." );
- while ( true )
- {
- buf.append( tokens.nextToken() );
- if ( ! tokens.hasMoreTokens() ) break;
- buf.append( File.separator );
- }
- String path = buf.toString();
- java.net.URL url = ClassLoader.getSystemResource( path + ".java" );
- if ( url == null ) return; // do nothing
-
- String name = url.getFile();
-
- // try to launch the document
- try
- {
- // NOTE: This is Windows-dependent!
- String args[] = new String[] {
- "cmd", "/c", "\"start \"\" \"" + name.substring(1) + "\"\"" };
- // this translates to: cmd /c "start "" "path""
- // apparently an array is more reliable for calling exec().
- // all the extra quotes are to handle paths with spaces.
- // trims off the first "/" before the drive letter.
- // needed a dummy title for the console or it wouldn't work.
- Runtime.getRuntime().exec( args );
- }
- catch ( Exception exc )
- {
- System.out.println( "DocumentLinkPanel.invokeDocument: " + exc );
- }
- return;
- }
+ return null;
+ }
+
+ /**
+ * Prints a stack trace up to the first method whose fully qualified class name
+ * begins with "java" to System.out.
+ */
+ static public void printShortStackTrace() {
+ String s;
+ Iterator i = parseStackTrace(new RuntimeException()).iterator();
+ while (i.hasNext()) {
+ System.out.println(" " + (s = i.next().toString()));
+ if (s.startsWith("java"))
+ break;
+ }
+ }
+
+ protected void invokeFileFromString(String aString) {
+ // strip off parentheses, if any
+ int openParam = aString.indexOf("(");
+ if (openParam != -1) {
+ aString = aString.substring(0, openParam);
+ }
+
+ // separate class name from method name
+ int lastDot = aString.lastIndexOf(".");
+ if (lastDot == -1)
+ return;
+ String className = aString.substring(0, lastDot);
+ String methodName = aString.substring(lastDot + 1);
+
+ // convert "."s to file separator characters
+ StringBuffer buf = new StringBuffer();
+ StringTokenizer tokens = new StringTokenizer(className, ".");
+ while (true) {
+ buf.append(tokens.nextToken());
+ if (!tokens.hasMoreTokens())
+ break;
+ buf.append(File.separator);
+ }
+ String path = buf.toString();
+ java.net.URL url = ClassLoader.getSystemResource(path + ".java");
+ if (url == null)
+ return; // do nothing
+
+ String name = url.getFile();
+
+ // try to launch the document
+ try {
+ // NOTE: This is Windows-dependent!
+ String args[] = new String[] { "cmd", "/c", "\"start \"\" \"" + name.substring(1) + "\"\"" };
+ // this translates to: cmd /c "start "" "path""
+ // apparently an array is more reliable for calling exec().
+ // all the extra quotes are to handle paths with spaces.
+ // trims off the first "/" before the drive letter.
+ // needed a dummy title for the console or it wouldn't work.
+ Runtime.getRuntime().exec(args);
+ } catch (Exception exc) {
+ System.out.println("DocumentLinkPanel.invokeDocument: " + exc);
+ }
+ return;
+ }
}
/*
- * $Log$
- * Revision 1.2 2006/02/18 23:19:05 cgruber
- * Update imports and maven dependencies.
+ * $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.1 2006/02/16 13:22:22 cgruber Check in all sources in
+ * eclipse-friendly maven-enabled packages.
*
- * Revision 1.5 2003/08/06 23:07:53 chochos
- * general code cleanup (mostly, removing unused imports)
+ * Revision 1.5 2003/08/06 23:07:53 chochos general code cleanup (mostly,
+ * removing unused imports)
*
- * Revision 1.4 2002/11/16 16:33:31 mpowers
- * Now using platform-specific accelerator key for shortcuts.
+ * Revision 1.4 2002/11/16 16:33:31 mpowers Now using platform-specific
+ * accelerator key for shortcuts.
*
- * Revision 1.3 2001/07/18 21:53:33 mpowers
- * Added a string argument for display as a message.
+ * Revision 1.3 2001/07/18 21:53:33 mpowers Added a string argument for display
+ * as a message.
*
- * Revision 1.2 2001/07/17 14:01:43 mpowers
- * Added short stack trace method.
+ * Revision 1.2 2001/07/17 14:01:43 mpowers Added short stack trace method.
*
- * Revision 1.1.1.1 2000/12/21 15:51:34 mpowers
- * Contributing wotonomy.
+ * Revision 1.1.1.1 2000/12/21 15:51:34 mpowers Contributing wotonomy.
*
- * Revision 1.5 2000/12/20 16:25:45 michael
- * Added log to all files.
+ * Revision 1.5 2000/12/20 16:25:45 michael Added log to all files.
*
*
*/
-
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/TextInputRangeChecker.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/TextInputRangeChecker.java
index 36dbacf..20f37c1 100644
--- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/TextInputRangeChecker.java
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/TextInputRangeChecker.java
@@ -30,339 +30,298 @@ import javax.swing.SwingUtilities;
import javax.swing.text.JTextComponent;
/**
-* This class will actively check the inputs of 2 numbers in seperate text
-* components. The number in the text components represent an upper and lower
-* bound to some range. This class checks to make sure the user inputs values
-* in the lower bound text field that are less than the value of the upper
-* bound and vice versa for the upper bound text field. This class will also
-* check to make sure the bounds fall within a given range if specified.
-*
-* The checks are automatically performed when the focus is lost on either
-* component. If the inputs are correct then no event occurs. If the inputs
-* are not correct, then a dialog message is displayed stating the reason why
-* the bounds are invalid, and the original correct value is restored into the
-* text components.
-*
-* @author rglista
-* @author $Author: cgruber $
-* @version $Revision: 904 $
-*/
-public class TextInputRangeChecker implements FocusListener
-{
- protected static final int NONE = 0;
- protected static final int LOWER = 1;
- protected static final int UPPER = 2;
-
- private JTextComponent lowerComponent;
- private JTextComponent upperComponent;
- private double maxRange;
- private double lowerNumber;
- private double upperNumber;
- private Collection focusListeners;
-
- private String invalidLowerMessage;
- private String invalidUpperMessage;
- private String invalidEitherMessage;
- private String invalidRangeMessage;
-
-
-/**
-* Constructor with some of the settable parameters. No range checking is used.
-* @param aLowerTextComponent A text component for the lower bound.
-* @param anUpperTextComponent A text component for the upper bound.
-*/
- public TextInputRangeChecker( JTextComponent aLowerTextComponent,
- JTextComponent anUpperTextComponent )
- {
- this( aLowerTextComponent, anUpperTextComponent, null, null, 0.0 );
- }
-
-/**
-* Constructor with some of the settable parameters. No range checking is
-* used.
-* @param aLowerTextComponent A text component for the lower bound.
-* @param anUpperTextComponent A text component for the upper bound.
-* @param lowerTextName The name of the lower bound, eg - start year.
-* @param upperTextName The name of the upper bound, eg - end year.
-* is used.
-*/
- public TextInputRangeChecker( JTextComponent aLowerTextComponent,
- JTextComponent anUpperTextComponent,
- String lowerTextName, String upperTextName )
- {
- this( aLowerTextComponent, anUpperTextComponent, lowerTextName, upperTextName, 0.0 );
- }
-
-/**
-* Constructor with some of the settable parameters.
-* @param aLowerTextComponent A text component for the lower bound.
-* @param anUpperTextComponent A text component for the upper bound.
-* @param aMaxRange The range the bounds muist fall between, if 0 then no range
-* is used.
-*/
- public TextInputRangeChecker( JTextComponent aLowerTextComponent,
- JTextComponent anUpperTextComponent,
- double aMaxRange )
- {
- this( aLowerTextComponent, anUpperTextComponent, null, null, aMaxRange );
- }
-
-/**
-* Constructor with all the settable parameters.
-* @param aLowerTextComponent A text component for the lower bound.
-* @param anUpperTextComponent A text component for the upper bound.
-* @param lowerTextName The name of the lower bound, eg - start year.
-* @param upperTextName The name of the upper bound, eg - end year.
-* @param aMaxRange The range the bounds muist fall between, if 0 then no range
-* is used.
-*/
- public TextInputRangeChecker( JTextComponent aLowerTextComponent,
- JTextComponent anUpperTextComponent,
- String lowerTextName, String upperTextName,
- double aMaxRange )
- {
- lowerComponent = aLowerTextComponent;
- upperComponent = anUpperTextComponent;
- maxRange = aMaxRange;
-
- focusListeners = new ArrayList( 1 ); // For most cases, there will be only 1 listener.
-
- lowerComponent.addFocusListener( this );
- upperComponent.addFocusListener( this );
-
- lowerNumber = getNumber( lowerComponent );
- upperNumber = getNumber( upperComponent );
-
- if ( ( lowerTextName != null ) && ( upperTextName != null ) )
- {
- invalidLowerMessage = "The " + lowerTextName + " must be less than or equal to the " + upperTextName + ".";
- invalidUpperMessage = "The " + upperTextName + " must be greater than or equal to the " + lowerTextName + ".";
- invalidEitherMessage = "The " + lowerTextName + " and/or the " + upperTextName + " are not correct.";
- invalidRangeMessage = "The maximum range for the " + lowerTextName + " and " + upperTextName + " is " + maxRange + ".";
- }
- else
- {
- invalidLowerMessage = "The lower bound must be less than or equal to the upper bound.";
- invalidUpperMessage = "The upper bound must be greater than or equal to the lower bound.";
- invalidEitherMessage = "The upper and/or lower bounds are not correct.";
- invalidRangeMessage = "The maximum range is " + maxRange + ".";
- }
- }
-
-/**
-* Allows the caller to perform the validation of the bounds programatically.
-* The lower bound is compared to the upper bound and range checking is performed.
-* If the lower bound is greater than the upper bound, or the range between the
-* bounds is greater than the max range, then validation fails.
-* @return TRUE is validation is successfull, FALSE if it fails.
-*/
- public boolean performCheck()
- {
- return validate( null );
- }
-
-/**
-* Adds the listener to the lists of focus listener maintened by this object.
-* When one of the 2 text components receives a focus event, this object will
-* fire that focus event to any of its listeners. This is useful when the
-* calling object wants to be notified of the components focus events, but wants
-* to ensure that the validation has occured first.
-* <br><br>
-* NOTE: The focus is only fired if the validation was successful. This might
-* have to be changed.
-* @param aListener A Focus Listener to receive Focus Events.
-*/
- public void addFocusListener( FocusListener aListener )
- {
- focusListeners.add( aListener );
- }
-
-/**
-* Returns the last valid value of the lower bound. If this is called while
-* the user is updating the text component but before the focus is lost, the
-* value returned will be the original value before the user started updating
-* the bound.
-* @return The last valid value of the lower bound.
-*/
- public double getLastValidatedLowerNumber()
- {
- return lowerNumber;
- }
-
-/**
-* Returns the last valid value of the upper bound. If this is called while
-* the user is updating the text component but before the focus is lost, the
-* value returned will be the original value before the user started updating
-* the bound.
-* @return The last valid value of the upper bound.
-*/
- public double getLastValidatedUpperNumber()
- {
- return upperNumber;
- }
-
-/**
-* Method used to be notified when one of the text components has gained its
-* focus.
-*/
- public void focusGained( FocusEvent e )
- {
- lowerNumber = getNumber( lowerComponent );
- upperNumber = getNumber( upperComponent );
- }
-
-/**
-* Method used to be notified when one of the text components has lost its
-* focus. Automatic validation occurs here.
-*/
- public void focusLost( FocusEvent e )
- {
- if ( e.isTemporary() )
- {
- return;
- }
-
- if ( validate( e.getSource() ) )
- {
- fireFocusEvent( e );
- }
- }
-
-/**
-* Fires a focus lost event if the validation was successfull.
-*/
- protected void fireFocusEvent( FocusEvent e )
- {
- for ( Iterator it = focusListeners.iterator(); it.hasNext(); )
- {
- ( ( FocusListener )it.next() ).focusLost( e );
- }
- }
-
-/**
-* Validates the bounds inputed by the user.
-* @param aComponent The component to use to display a dialog window, if neccessray.
-* If null, then the parent window of the text componets will be used.
-* @return TRUE if validation was successful, FALSE otherwise.
-*/
- protected boolean validate( Object aComponent )
- {
- int componentUsed = NONE;
- if ( aComponent == lowerComponent )
- {
- componentUsed = LOWER;
- }
- else if ( aComponent == upperComponent )
- {
- componentUsed = UPPER;
- }
-
- double lower = getNumber( lowerComponent );
- double upper = getNumber( upperComponent );
-
- if ( lower > upper )
- {
- if ( componentUsed == LOWER )
- {
- lowerComponent.setText( Double.toString( lowerNumber ) );
- displayMessage( invalidLowerMessage, lowerComponent );
- }
- else if ( componentUsed == UPPER )
- {
- upperComponent.setText( Double.toString( upperNumber ) );
- displayMessage( invalidUpperMessage, upperComponent );
- }
- else
- {
- upperComponent.setText( Double.toString( upperNumber ) );
- lowerComponent.setText( Double.toString( lowerNumber ) );
- displayMessage( invalidEitherMessage, lowerComponent.getTopLevelAncestor() );
- }
-
- return false;
- }
-
- if ( maxRange != 0.0 )
- {
- if ( ( upper - lower ) > maxRange )
- {
- if ( componentUsed == LOWER )
- {
- lowerComponent.setText( Double.toString( lowerNumber ) );
- displayMessage( invalidRangeMessage, lowerComponent );
- }
- else if ( componentUsed == UPPER )
- {
- upperComponent.setText( Double.toString( upperNumber ) );
- displayMessage( invalidRangeMessage, upperComponent );
- }
- else
- {
- upperComponent.setText( Double.toString( upperNumber ) );
- lowerComponent.setText( Double.toString( lowerNumber ) );
- displayMessage( invalidRangeMessage, lowerComponent.getTopLevelAncestor() );
- }
-
- return false;
- }
- }
-
- lowerNumber = lower;
- upperNumber = upper;
- return true;
- }
-
-/**
-* Creates a JOptionPane to display the reason why the bounds failed validation.
-*/
- protected void displayMessage( final String message, final Component parent )
- {
- SwingUtilities.invokeLater( new Runnable()
- {
- public void run()
- {
- JOptionPane.showMessageDialog( parent, message, "Data Entry Error",
- JOptionPane.ERROR_MESSAGE );
- }
- } );
- }
-
-/**
-* Gets the number represented in the text component. If the text does not
-* represent a number, then zero is returned.
-*/
- protected double getNumber( JTextComponent aComponent )
- {
- try
- {
- return Double.valueOf( aComponent.getText() ).doubleValue();
+ * This class will actively check the inputs of 2 numbers in seperate text
+ * components. The number in the text components represent an upper and lower
+ * bound to some range. This class checks to make sure the user inputs values in
+ * the lower bound text field that are less than the value of the upper bound
+ * and vice versa for the upper bound text field. This class will also check to
+ * make sure the bounds fall within a given range if specified.
+ *
+ * The checks are automatically performed when the focus is lost on either
+ * component. If the inputs are correct then no event occurs. If the inputs are
+ * not correct, then a dialog message is displayed stating the reason why the
+ * bounds are invalid, and the original correct value is restored into the text
+ * components.
+ *
+ * @author rglista
+ * @author $Author: cgruber $
+ * @version $Revision: 904 $
+ */
+public class TextInputRangeChecker implements FocusListener {
+ protected static final int NONE = 0;
+ protected static final int LOWER = 1;
+ protected static final int UPPER = 2;
+
+ private JTextComponent lowerComponent;
+ private JTextComponent upperComponent;
+ private double maxRange;
+ private double lowerNumber;
+ private double upperNumber;
+ private Collection focusListeners;
+
+ private String invalidLowerMessage;
+ private String invalidUpperMessage;
+ private String invalidEitherMessage;
+ private String invalidRangeMessage;
+
+ /**
+ * Constructor with some of the settable parameters. No range checking is used.
+ *
+ * @param aLowerTextComponent A text component for the lower bound.
+ * @param anUpperTextComponent A text component for the upper bound.
+ */
+ public TextInputRangeChecker(JTextComponent aLowerTextComponent, JTextComponent anUpperTextComponent) {
+ this(aLowerTextComponent, anUpperTextComponent, null, null, 0.0);
+ }
+
+ /**
+ * Constructor with some of the settable parameters. No range checking is used.
+ *
+ * @param aLowerTextComponent A text component for the lower bound.
+ * @param anUpperTextComponent A text component for the upper bound.
+ * @param lowerTextName The name of the lower bound, eg - start year.
+ * @param upperTextName The name of the upper bound, eg - end year. is
+ * used.
+ */
+ public TextInputRangeChecker(JTextComponent aLowerTextComponent, JTextComponent anUpperTextComponent,
+ String lowerTextName, String upperTextName) {
+ this(aLowerTextComponent, anUpperTextComponent, lowerTextName, upperTextName, 0.0);
+ }
+
+ /**
+ * Constructor with some of the settable parameters.
+ *
+ * @param aLowerTextComponent A text component for the lower bound.
+ * @param anUpperTextComponent A text component for the upper bound.
+ * @param aMaxRange The range the bounds muist fall between, if 0
+ * then no range is used.
+ */
+ public TextInputRangeChecker(JTextComponent aLowerTextComponent, JTextComponent anUpperTextComponent,
+ double aMaxRange) {
+ this(aLowerTextComponent, anUpperTextComponent, null, null, aMaxRange);
+ }
+
+ /**
+ * Constructor with all the settable parameters.
+ *
+ * @param aLowerTextComponent A text component for the lower bound.
+ * @param anUpperTextComponent A text component for the upper bound.
+ * @param lowerTextName The name of the lower bound, eg - start year.
+ * @param upperTextName The name of the upper bound, eg - end year.
+ * @param aMaxRange The range the bounds muist fall between, if 0
+ * then no range is used.
+ */
+ public TextInputRangeChecker(JTextComponent aLowerTextComponent, JTextComponent anUpperTextComponent,
+ String lowerTextName, String upperTextName, double aMaxRange) {
+ lowerComponent = aLowerTextComponent;
+ upperComponent = anUpperTextComponent;
+ maxRange = aMaxRange;
+
+ focusListeners = new ArrayList(1); // For most cases, there will be only 1 listener.
+
+ lowerComponent.addFocusListener(this);
+ upperComponent.addFocusListener(this);
+
+ lowerNumber = getNumber(lowerComponent);
+ upperNumber = getNumber(upperComponent);
+
+ if ((lowerTextName != null) && (upperTextName != null)) {
+ invalidLowerMessage = "The " + lowerTextName + " must be less than or equal to the " + upperTextName + ".";
+ invalidUpperMessage = "The " + upperTextName + " must be greater than or equal to the " + lowerTextName
+ + ".";
+ invalidEitherMessage = "The " + lowerTextName + " and/or the " + upperTextName + " are not correct.";
+ invalidRangeMessage = "The maximum range for the " + lowerTextName + " and " + upperTextName + " is "
+ + maxRange + ".";
+ } else {
+ invalidLowerMessage = "The lower bound must be less than or equal to the upper bound.";
+ invalidUpperMessage = "The upper bound must be greater than or equal to the lower bound.";
+ invalidEitherMessage = "The upper and/or lower bounds are not correct.";
+ invalidRangeMessage = "The maximum range is " + maxRange + ".";
+ }
+ }
+
+ /**
+ * Allows the caller to perform the validation of the bounds programatically.
+ * The lower bound is compared to the upper bound and range checking is
+ * performed. If the lower bound is greater than the upper bound, or the range
+ * between the bounds is greater than the max range, then validation fails.
+ *
+ * @return TRUE is validation is successfull, FALSE if it fails.
+ */
+ public boolean performCheck() {
+ return validate(null);
+ }
+
+ /**
+ * Adds the listener to the lists of focus listener maintened by this object.
+ * When one of the 2 text components receives a focus event, this object will
+ * fire that focus event to any of its listeners. This is useful when the
+ * calling object wants to be notified of the components focus events, but wants
+ * to ensure that the validation has occured first. <br>
+ * <br>
+ * NOTE: The focus is only fired if the validation was successful. This might
+ * have to be changed.
+ *
+ * @param aListener A Focus Listener to receive Focus Events.
+ */
+ public void addFocusListener(FocusListener aListener) {
+ focusListeners.add(aListener);
+ }
+
+ /**
+ * Returns the last valid value of the lower bound. If this is called while the
+ * user is updating the text component but before the focus is lost, the value
+ * returned will be the original value before the user started updating the
+ * bound.
+ *
+ * @return The last valid value of the lower bound.
+ */
+ public double getLastValidatedLowerNumber() {
+ return lowerNumber;
+ }
+
+ /**
+ * Returns the last valid value of the upper bound. If this is called while the
+ * user is updating the text component but before the focus is lost, the value
+ * returned will be the original value before the user started updating the
+ * bound.
+ *
+ * @return The last valid value of the upper bound.
+ */
+ public double getLastValidatedUpperNumber() {
+ return upperNumber;
+ }
+
+ /**
+ * Method used to be notified when one of the text components has gained its
+ * focus.
+ */
+ public void focusGained(FocusEvent e) {
+ lowerNumber = getNumber(lowerComponent);
+ upperNumber = getNumber(upperComponent);
+ }
+
+ /**
+ * Method used to be notified when one of the text components has lost its
+ * focus. Automatic validation occurs here.
+ */
+ public void focusLost(FocusEvent e) {
+ if (e.isTemporary()) {
+ return;
+ }
+
+ if (validate(e.getSource())) {
+ fireFocusEvent(e);
+ }
+ }
+
+ /**
+ * Fires a focus lost event if the validation was successfull.
+ */
+ protected void fireFocusEvent(FocusEvent e) {
+ for (Iterator it = focusListeners.iterator(); it.hasNext();) {
+ ((FocusListener) it.next()).focusLost(e);
+ }
+ }
+
+ /**
+ * Validates the bounds inputed by the user.
+ *
+ * @param aComponent The component to use to display a dialog window, if
+ * neccessray. If null, then the parent window of the text
+ * componets will be used.
+ * @return TRUE if validation was successful, FALSE otherwise.
+ */
+ protected boolean validate(Object aComponent) {
+ int componentUsed = NONE;
+ if (aComponent == lowerComponent) {
+ componentUsed = LOWER;
+ } else if (aComponent == upperComponent) {
+ componentUsed = UPPER;
+ }
+
+ double lower = getNumber(lowerComponent);
+ double upper = getNumber(upperComponent);
+
+ if (lower > upper) {
+ if (componentUsed == LOWER) {
+ lowerComponent.setText(Double.toString(lowerNumber));
+ displayMessage(invalidLowerMessage, lowerComponent);
+ } else if (componentUsed == UPPER) {
+ upperComponent.setText(Double.toString(upperNumber));
+ displayMessage(invalidUpperMessage, upperComponent);
+ } else {
+ upperComponent.setText(Double.toString(upperNumber));
+ lowerComponent.setText(Double.toString(lowerNumber));
+ displayMessage(invalidEitherMessage, lowerComponent.getTopLevelAncestor());
+ }
+
+ return false;
+ }
+
+ if (maxRange != 0.0) {
+ if ((upper - lower) > maxRange) {
+ if (componentUsed == LOWER) {
+ lowerComponent.setText(Double.toString(lowerNumber));
+ displayMessage(invalidRangeMessage, lowerComponent);
+ } else if (componentUsed == UPPER) {
+ upperComponent.setText(Double.toString(upperNumber));
+ displayMessage(invalidRangeMessage, upperComponent);
+ } else {
+ upperComponent.setText(Double.toString(upperNumber));
+ lowerComponent.setText(Double.toString(lowerNumber));
+ displayMessage(invalidRangeMessage, lowerComponent.getTopLevelAncestor());
+ }
+
+ return false;
+ }
+ }
+
+ lowerNumber = lower;
+ upperNumber = upper;
+ return true;
+ }
+
+ /**
+ * Creates a JOptionPane to display the reason why the bounds failed validation.
+ */
+ protected void displayMessage(final String message, final Component parent) {
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ JOptionPane.showMessageDialog(parent, message, "Data Entry Error", JOptionPane.ERROR_MESSAGE);
+ }
+ });
+ }
+
+ /**
+ * Gets the number represented in the text component. If the text does not
+ * represent a number, then zero is returned.
+ */
+ protected double getNumber(JTextComponent aComponent) {
+ try {
+ return Double.valueOf(aComponent.getText()).doubleValue();
//1.2 return Double.parseDouble( aComponent.getText() );
- }
- catch ( NumberFormatException e )
- {
- System.out.println("[GUI] TextInputRangeChecker.getNumber(): The text is NOT a number: " + aComponent.getText() );
- return 0.0;
- }
- }
+ } catch (NumberFormatException e) {
+ System.out.println(
+ "[GUI] TextInputRangeChecker.getNumber(): The text is NOT a number: " + aComponent.getText());
+ return 0.0;
+ }
+ }
}
/*
- * $Log$
- * Revision 1.2 2006/02/18 23:19:05 cgruber
- * Update imports and maven dependencies.
+ * $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.1 2006/02/16 13:22:22 cgruber Check in all sources in
+ * eclipse-friendly maven-enabled packages.
*
- * Revision 1.2 2003/08/06 23:07:53 chochos
- * general code cleanup (mostly, removing unused imports)
+ * Revision 1.2 2003/08/06 23:07:53 chochos general code cleanup (mostly,
+ * removing unused imports)
*
- * Revision 1.1.1.1 2000/12/21 15:51:49 mpowers
- * Contributing wotonomy.
+ * Revision 1.1.1.1 2000/12/21 15:51:49 mpowers Contributing wotonomy.
*
- * Revision 1.2 2000/12/20 16:25:46 michael
- * Added log to all files.
+ * Revision 1.2 2000/12/20 16:25:46 michael Added log to all files.
*
*
*/
-
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/WindowGrabber.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/WindowGrabber.java
index c360105..4ac8374 100644
--- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/WindowGrabber.java
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/WindowGrabber.java
@@ -31,173 +31,140 @@ import javax.swing.JFrame;
import javax.swing.JWindow;
/**
- * WindowGrabber is a collection of static utility methods
- * for taking screen shots of lightweight containers. All
- * components that are not native peers will be drawn to a
- * bitmap that will be run-length compressed (LZW encoding, GIF format)
- * and written to the specified file or output stream. <br><br>
+ * WindowGrabber is a collection of static utility methods for taking screen
+ * shots of lightweight containers. All components that are not native peers
+ * will be drawn to a bitmap that will be run-length compressed (LZW encoding,
+ * GIF format) and written to the specified file or output stream. <br>
+ * <br>
*
- * Any Swing/JFC or IFC window is a good candidate for use with these
- * methods. The component is not expected to contain more than 256 colors
- * (the maximum that GIF allows). While there is a color depth limitation,
- * the compression is lossless, with no blurs or bleeding color.
+ * Any Swing/JFC or IFC window is a good candidate for use with these methods.
+ * The component is not expected to contain more than 256 colors (the maximum
+ * that GIF allows). While there is a color depth limitation, the compression is
+ * lossless, with no blurs or bleeding color.
*
* @author michael@mpowers.net
- * @version $Revision: 904 $
- * $Date: 2006-02-18 18:19:05 -0500 (Sat, 18 Feb 2006) $
+ * @version $Revision: 904 $ $Date: 2006-02-18 18:19:05 -0500 (Sat, 18 Feb 2006)
+ * $
*/
-public class WindowGrabber
-{
-/**
- * Captures the screen contents of the specified component,
- * and write it to a file with the specified name.
- *
- * @param aComponent a lightweight component or container.
- * @param aFileName the name of the file to write, optionally preceded by path.
- * @return True if the file was successfully written, false if there was an error.
- * Errors are written to the standard error stream.
- */
- static public boolean grab( Component aComponent, String aFileName )
- {
+public class WindowGrabber {
+ /**
+ * Captures the screen contents of the specified component, and write it to a
+ * file with the specified name.
+ *
+ * @param aComponent a lightweight component or container.
+ * @param aFileName the name of the file to write, optionally preceded by path.
+ * @return True if the file was successfully written, false if there was an
+ * error. Errors are written to the standard error stream.
+ */
+ static public boolean grab(Component aComponent, String aFileName) {
OutputStream output = null;
- try
- {
- output = new FileOutputStream( aFileName );
- }
- catch ( Exception exc )
- {
- System.err.println( exc );
+ try {
+ output = new FileOutputStream(aFileName);
+ } catch (Exception exc) {
+ System.err.println(exc);
return false;
}
- return grab( aComponent, output );
+ return grab(aComponent, output);
}
-/**
- * Captures the screen contents of the specified component,
- * and write it to a file with the specified name.
- *
- * @param aComponent a lightweight component or container.
- * @param anOutputStream an output stream to which the image will be written.
- * @return True if the image was successfully written, false if there was an error.
- * Errors are written to the standard error stream.
- */
- static public boolean grab( Component aComponent, OutputStream anOutputStream )
- {
- Image img = aComponent.createImage(
- aComponent.getSize().width, aComponent.getSize().height );
- aComponent.paintAll( img.getGraphics() );
+ /**
+ * Captures the screen contents of the specified component, and write it to a
+ * file with the specified name.
+ *
+ * @param aComponent a lightweight component or container.
+ * @param anOutputStream an output stream to which the image will be written.
+ * @return True if the image was successfully written, false if there was an
+ * error. Errors are written to the standard error stream.
+ */
+ static public boolean grab(Component aComponent, OutputStream anOutputStream) {
+ Image img = aComponent.createImage(aComponent.getSize().width, aComponent.getSize().height);
+ aComponent.paintAll(img.getGraphics());
try {
- GIFEncoder encoder = new GIFEncoder( img );
- encoder.write( anOutputStream );
+ GIFEncoder encoder = new GIFEncoder(img);
+ encoder.write(anOutputStream);
anOutputStream.flush();
- } catch ( Exception exc ) {
- System.err.println( exc );
+ } catch (Exception exc) {
+ System.err.println(exc);
return false;
}
return true;
}
- protected static void processFilenames( String path, String[] filenames )
- {
+ protected static void processFilenames(String path, String[] filenames) {
ClassLoader loader = new ClassGrabber();
File f;
- for ( int i = 0; i < filenames.length; i++ )
- {
- try
- {
- f = new File( path + filenames[i] );
- if ( f.isDirectory() )
- {
- processFilenames (
- path + filenames[i] + "/",
- f.list( new FilenameFilter()
- {
- public boolean accept(File dir, String name)
- {
- return name.endsWith( ".class" );
+ for (int i = 0; i < filenames.length; i++) {
+ try {
+ f = new File(path + filenames[i]);
+ if (f.isDirectory()) {
+ processFilenames(path + filenames[i] + "/", f.list(new FilenameFilter() {
+ public boolean accept(File dir, String name) {
+ return name.endsWith(".class");
+ }
+ }));
+ } else {
+ System.out.println("Loading " + filenames[i] + ": ");
+ Class c = loader.loadClass(path + filenames[i]);
+
+ System.out.println(c);
+
+ if (JWindow.class.isAssignableFrom(c) || JDialog.class.isAssignableFrom(c)
+ || JFrame.class.isAssignableFrom(c)) {
+ try {
+ Window w = (Window) c.newInstance();
+ if (w.getBounds().width * w.getBounds().height == 0) { // if size not specified or set, pack
+ // it
+ w.pack();
}
- })
- );
- }
- else
- {
- System.out.println( "Loading " + filenames[i] + ": " );
- Class c = loader.loadClass( path + filenames[i] );
-
- System.out.println( c );
-
- if ( JWindow.class.isAssignableFrom( c ) ||
- JDialog.class.isAssignableFrom( c ) ||
- JFrame.class.isAssignableFrom( c ) )
- {
- try
- {
- Window w = (Window) c.newInstance();
- if ( w.getBounds().width * w.getBounds().height == 0 )
- { // if size not specified or set, pack it
- w.pack();
- }
- String gifName = // replace .class with .gif
- filenames[i].substring(
- 0, filenames[i].length() - 5 ) + "gif";
- w.addNotify();
- w.repaint();
- grab( w, gifName );
- System.out.println( "wrote: " + gifName );
- }
- catch ( Exception exc )
- {
- System.err.println( "WindowGrab failed for " + filenames[i] + ": " );
- exc.printStackTrace();
- }
-
- }
- else
- {
- System.out.println( "not a JWindow, JDialog, or JFrame; ignored." );
- }
+ String gifName = // replace .class with .gif
+ filenames[i].substring(0, filenames[i].length() - 5) + "gif";
+ w.addNotify();
+ w.repaint();
+ grab(w, gifName);
+ System.out.println("wrote: " + gifName);
+ } catch (Exception exc) {
+ System.err.println("WindowGrab failed for " + filenames[i] + ": ");
+ exc.printStackTrace();
+ }
+
+ } else {
+ System.out.println("not a JWindow, JDialog, or JFrame; ignored.");
+ }
}
- }
- catch ( Exception exc )
- {
- System.err.println( filenames[i] + ": " + exc );
+ } catch (Exception exc) {
+ System.err.println(filenames[i] + ": " + exc);
}
}
}
-/**
- * Captures images of any Swing window component classes specified
- * as parameters. If created, image file will have the same name
- * as the corresponding class file, but with a ".gif" extension.
- *
- * @param argv a list of filenames or directory names.
- */
- public static void main( String[] argv )
- {
- processFilenames( "", argv );
- System.exit( 0 );
+ /**
+ * Captures images of any Swing window component classes specified as
+ * parameters. If created, image file will have the same name as the
+ * corresponding class file, but with a ".gif" extension.
+ *
+ * @param argv a list of filenames or directory names.
+ */
+ public static void main(String[] argv) {
+ processFilenames("", argv);
+ System.exit(0);
}
}
/*
- * $Log$
- * Revision 1.2 2006/02/18 23:19:05 cgruber
- * Update imports and maven dependencies.
+ * $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.1 2006/02/16 13:22:22 cgruber Check in all sources in
+ * eclipse-friendly maven-enabled packages.
*
- * Revision 1.1.1.1 2000/12/21 15:51:49 mpowers
- * Contributing wotonomy.
+ * Revision 1.1.1.1 2000/12/21 15:51:49 mpowers Contributing wotonomy.
*
- * Revision 1.2 2000/12/20 16:25:46 michael
- * Added log to all files.
+ * Revision 1.2 2000/12/20 16:25:46 michael Added log to all files.
*
*
*/
-
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/WindowUtilities.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/WindowUtilities.java
index a36ba12..e9d3329 100644
--- a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/WindowUtilities.java
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/WindowUtilities.java
@@ -34,488 +34,440 @@ import java.util.List;
import java.util.Map;
/**
-* A collection of window-related utilities.
-*
-* @author michael@mpowers.net
-* @author $Author: cgruber $
-* @version $Revision: 904 $
-* $Date: 2006-02-18 18:19:05 -0500 (Sat, 18 Feb 2006) $
-*
-*/
-public class WindowUtilities
-{
-
-/**
-* Place frame at center (vertically and horizontally) of screen.
-*/
- public static final int CENTER = 0;
-
-/**
-* Center dialog on frame area of parent, if any.
-*/
- public static final int CENTER_PARENT = 1;
-
-/**
-* Place lower and to the right of the last window
-* placed in this manner. Will wrap to top and left
-* of screen as necessary.
-*/
- public static final int CASCADE = 10;
-
- // cascade state
- private static int lastX = 0;
- private static int lastY = 0;
- private static int incrementX = 20;
- private static int incrementY = 20;
-
-/**
-* Place the window in the center of the screen.
-* Note: don't forget to first set the size of your window.
-* This is a convenience method and simply calls place() with
-* the CENTER parameter.
-* @param aWindow The window to be centered.
-* @see #place
-*/
- public static void center( Window aWindow )
- {
- place( aWindow, CENTER );
- }
-
-/**
-* Place lower and to the right of the last window
-* placed in this manner. Will wrap to top and left
-* of screen as necessary.
-* This is a convenience method and simply calls place() with
-* the CASCADE parameter.
-* @param aWindow The window to be cascaded.
-* @see #place
-*/
- public static void cascade( Window aWindow )
- {
- place( aWindow, CASCADE );
- }
-
-/**
-* Place the window in the specified location.
-* Note: don't forget to first set the size of your window.
-* @param aWindow The window to be placed.
-* @param location Where on screen to place the frame.
-*/
- public static void place( Window aWindow, int location)
- {
- Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
- Dimension mySize = aWindow.getSize();
- int x = (aWindow.getLocation()).x;
- int y = (aWindow.getLocation()).y;
- float aspectRatio = (float)screenSize.height/(float)screenSize.width;
-
- // hack to make windows appear on left monitor if dual monitor
- // if aspect ratio is less than 0.6, assume dual monitor
- if (aspectRatio < 0.6)
- {
- screenSize.width = screenSize.width/2;
- }
-
- switch (location)
- {
- case CENTER_PARENT:
- if ( ( ! ( aWindow instanceof Dialog ) )
- || ( ((Dialog)aWindow).getParent() == null ) )
+ * A collection of window-related utilities.
+ *
+ * @author michael@mpowers.net
+ * @author $Author: cgruber $
+ * @version $Revision: 904 $ $Date: 2006-02-18 18:19:05 -0500 (Sat, 18 Feb 2006)
+ * $
+ *
+ */
+public class WindowUtilities {
+
+ /**
+ * Place frame at center (vertically and horizontally) of screen.
+ */
+ public static final int CENTER = 0;
+
+ /**
+ * Center dialog on frame area of parent, if any.
+ */
+ public static final int CENTER_PARENT = 1;
+
+ /**
+ * Place lower and to the right of the last window placed in this manner. Will
+ * wrap to top and left of screen as necessary.
+ */
+ public static final int CASCADE = 10;
+
+ // cascade state
+ private static int lastX = 0;
+ private static int lastY = 0;
+ private static int incrementX = 20;
+ private static int incrementY = 20;
+
+ /**
+ * Place the window in the center of the screen. Note: don't forget to first set
+ * the size of your window. This is a convenience method and simply calls
+ * place() with the CENTER parameter.
+ *
+ * @param aWindow The window to be centered.
+ * @see #place
+ */
+ public static void center(Window aWindow) {
+ place(aWindow, CENTER);
+ }
+
+ /**
+ * Place lower and to the right of the last window placed in this manner. Will
+ * wrap to top and left of screen as necessary. This is a convenience method and
+ * simply calls place() with the CASCADE parameter.
+ *
+ * @param aWindow The window to be cascaded.
+ * @see #place
+ */
+ public static void cascade(Window aWindow) {
+ place(aWindow, CASCADE);
+ }
+
+ /**
+ * Place the window in the specified location. Note: don't forget to first set
+ * the size of your window.
+ *
+ * @param aWindow The window to be placed.
+ * @param location Where on screen to place the frame.
+ */
+ public static void place(Window aWindow, int location) {
+ Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
+ Dimension mySize = aWindow.getSize();
+ int x = (aWindow.getLocation()).x;
+ int y = (aWindow.getLocation()).y;
+ float aspectRatio = (float) screenSize.height / (float) screenSize.width;
+
+ // hack to make windows appear on left monitor if dual monitor
+ // if aspect ratio is less than 0.6, assume dual monitor
+ if (aspectRatio < 0.6) {
+ screenSize.width = screenSize.width / 2;
+ }
+
+ switch (location) {
+ case CENTER_PARENT:
+ if ((!(aWindow instanceof Dialog)) || (((Dialog) aWindow).getParent() == null))
//1.2 || ( ((Dialog)aWindow).getOwner() == null ) )
- {
- place( aWindow, CENTER );
- return;
- }
- Point parentLocation = (((Dialog)aWindow).getParent()).getLocation();
+ {
+ place(aWindow, CENTER);
+ return;
+ }
+ Point parentLocation = (((Dialog) aWindow).getParent()).getLocation();
//1.2 (((Dialog)aWindow).getOwner()).getLocation();
- Dimension parentSize = (((Dialog)aWindow).getParent()).getSize();
+ Dimension parentSize = (((Dialog) aWindow).getParent()).getSize();
//1.2 Dimension parentSize = (((Dialog)aWindow).getOwner()).getSize();
- if (parentSize.width > mySize.width)
- {
- x = ((parentSize.width - mySize.width)/2) + parentLocation.x;
- }
- if (parentSize.height > mySize.height)
- {
- y = ((parentSize.height - mySize.height)/2) + parentLocation.y;
- }
- break;
- case CENTER:
- if (screenSize.width > mySize.width)
- {
- x = (screenSize.width - mySize.width)/2;
- }
- if (screenSize.height > mySize.height)
- {
- y = (screenSize.height - mySize.height)/2;
- }
- break;
- case CASCADE:
- x = lastX + incrementX;
- if ( x + mySize.width > screenSize.width )
- {
- x = incrementX;
- }
- y = lastY + incrementY;
- if ( y + mySize.height > screenSize.height )
- {
- y = incrementY;
- }
- lastX = x;
- lastY = y;
- break;
- default:
- // don't move the frame
- Point p = aWindow.getLocation();
- x = p.x;
- y = p.y;
- break;
- }
- aWindow.setLocation(x,y);
- }
-
-/**
- * Returns the first parent Window of the specified component.
- *
- * @param c the Component whose parent will be found.
- * @return the Window that contains the component,
- * or null if the component does not have a valid Frame parent.
- */
- public static Window getWindowForComponent(Component c)
- {
- for(Component p = c; p != null; p = p.getParent()) {
- if (p instanceof Window) {
- return (Window) p;
- }
- }
- return null;
- }
-
-
-
-/**
-* Prints out a list of all components to System.out.
-* @param aContainer the Container whose components will be listed.
-*/
- public static void dumpComponents( Container aContainer )
- {
- dumpComponents( aContainer, "" );
- }
-
- protected static void dumpComponents( Container aContainer, String padding )
- {
- Component c = null;
- int count = aContainer.getComponentCount();
- for ( int i = 0; i < count; i++ )
- {
- c = aContainer.getComponent( i );
- if ( c instanceof javax.swing.JComponent )
- {
- System.out.println( padding + c.getClass() + ": "
- + ((javax.swing.JComponent)c).getAccessibleContext().getAccessibleName() );
- }
- else
- {
- System.out.println( padding + c.getClass() + ": " + c.getName() );
- }
- if ( c instanceof Container )
- {
- dumpComponents( (Container) c, padding + " " );
- }
- }
- }
-
-/**
-* Gets a list of all children of a specified container, sorted by position.
-* Components are sorted from top to bottom and then left to right.
-* @param aContainer The container whose children are to be returned.
-* @result A List containing the sorted components.
-*/
- public static List getSortedChildComponents( Container aContainer )
- {
- List result = new ArrayList( getAllChildComponents( aContainer ) );
- Collections.sort( result, new PositionComparator( aContainer ) );
- return result;
- }
-
- public static void dumpSortedChildComponents( Container aContainer )
- {
- Component c = null;
- Iterator it = getSortedChildComponents( aContainer ).iterator();
- while ( it.hasNext() )
- {
- c = (Component) it.next();
- System.out.println( c.getLocation() + " : " + c.getClass() );
- }
- }
-
- public static void dumpNamedChildComponents( Container aContainer )
- {
- Iterator it = getUniqueNameMap( getSortedChildComponents( aContainer ) ).values().iterator();
- while ( it.hasNext() )
- {
- System.out.println( it.next() );
- }
- }
-
-/**
-* Generates a unique name for each object in a list and returns
-* a map that maps the objects to the names. The name is based
-* on the class of the object in the object list.
-* @param anObjectList A List of objects.
-* @return A Map that maps the objects in the list to the generated names.
-*/
- public static Map getUniqueNameMap( List anObjectList )
- {
- Map namesToObjects = new HashMap(anObjectList.size(), 1F);
- Map objectsToNames = new HashMap(anObjectList.size(), 1F);
-
- Object o = null;
- String name = null;
- int lastIndex = 0;
- Iterator it = anObjectList.iterator();
- while ( it.hasNext() )
- {
- o = it.next();
- name = o.getClass().getName();
- lastIndex = name.lastIndexOf( "." );
- if ( lastIndex != -1 )
- {
- name = name.substring( lastIndex+1 );
- }
- name = incrementString( name );
- while ( namesToObjects.get( name ) != null )
- {
- name = incrementString( name );
- }
- namesToObjects.put( name, o );
- objectsToNames.put( o, name );
- }
-
- return objectsToNames;
- }
-
-/**
-* Numerically increments a string. For example, "hello" becomes "hello1"
-* and "hello1" becomes "hello2" while "hello999" becomes "hello1000".
-* @param aString a String to be incremented.
-* @return The incremented String.
-*/
- public static String incrementString( String aString )
- {
- int i = aString.length()-1;
- while ( ( i >= 0 ) && ( Character.isDigit( aString.charAt( i ) ) ) )
- {
- i--;
- }
-
- if ( i == aString.length()-1 )
- { // no numerics at end of string, increment manually
- return aString + "1";
- }
-
- String alpha = aString.substring( 0, i+1 );
- String numeric = aString.substring( i+1 );
- numeric = Integer.toString( Integer.parseInt( numeric ) + 1 );
-
- return alpha + numeric;
-
- }
-
-/**
-* Gets all children of the specified container.
-* @param aContainer the Container to be searched.
-* @return A Collection containing all of the child components of the container.
-*/
- public static Collection getAllChildComponents( Container aContainer )
- {
- Collection result = new ArrayList();
- addAllChildComponents( aContainer, result );
- return result;
- }
-
-/**
-* Adds all children of the specified container to the specified collection.
-* @param aContainer the Container to be searched.
-* @param aCollection the Collection to which the child components will be added.
-*/
- protected static void addAllChildComponents( Container aContainer, Collection aCollection )
- {
- Component c = null;
- int count = aContainer.getComponentCount();
- for ( int i = 0; i < count; i++ )
- {
- c = aContainer.getComponent( i );
- aCollection.add( c );
- if ( c instanceof Container )
- {
- addAllChildComponents( (Container) c, aCollection );
- }
- }
- }
-
-/**
-* Sets each child component's tooltip to show the name generated from
-* getComponentNameMap().
-* (We're doing this so the tooltip authors can know how to reference
-* the components.)
-* @param aContainer the Container whose components will be labeled.
-*/
- public static void labelComponents( Container aContainer )
- {
- Map nameToComponent = getNameToComponentMap( aContainer );
- Map nameToName = new HashMap(nameToComponent.size(), 1F);
- Iterator it = nameToComponent.keySet().iterator();
- String key;
- while ( it.hasNext() )
- {
- key = it.next().toString();
- nameToName.put( key, key );
- }
- labelComponents( aContainer, nameToName );
- }
-
-/**
-* Sets each child component's tooltip to show a given string retrieved
-* from a map using the component's generated name as a key.
-* @param aContainer the Container whose components will be labeled.
-* @param aNameMap a Map of generated names to string values.
-*/
- public static void labelComponents( Container aContainer, Map aNameMap )
- {
- if ( aNameMap == null ) return;
-
- String key;
- Object o ;
- Iterator it = aNameMap.keySet().iterator();
- Map nameToComponent = getNameToComponentMap( aContainer );
- while ( it.hasNext() )
- {
- key = it.next().toString();
- o = nameToComponent.get( key );
- if ( o instanceof javax.swing.JComponent )
- {
- ((javax.swing.JComponent)o).setToolTipText( aNameMap.get( key ).toString() );
- }
- }
- }
-
-/**
-* Generates a deterministically unique name for each component in a
-* container. The name is based on the name of the class of the component
-* followed by a number. Each class of component is numbered based on it's
-* position in the container, sorted from top to bottom and left to right.
-* @param aContainer the Container whose components will named.
-* @return a Map that maps each component to its name.
-*/
- public static Map getComponentToNameMap( Container aContainer )
- {
- return getUniqueNameMap( getSortedChildComponents( aContainer ) );
- }
-
-/**
-* Maps a deterministically unique name to a component in a
-* container. The name is based on the name of the class of the component
-* followed by a number. Each class of component is numbered based on it's
-* position in the container, sorted from top to bottom and left to right.
-* @param aContainer the Container whose components will named.
-* @return a Map that maps each component to its name.
-*/
- public static Map getNameToComponentMap( Container aContainer )
- {
- Map componentToName = getComponentToNameMap( aContainer );
- Map result = new HashMap(componentToName.size(), 1F);
- Iterator it = componentToName.keySet().iterator();
- Object key;
- while ( it.hasNext() )
- {
- key = it.next();
- result.put( componentToName.get( key ), key );
- }
- return result;
- }
-
-/**
-* Sets the tooltips of all components in a container to the
-* respective names of those components. (We're using this
-* so the tooltip authors can know how to reference the components.)
-* @param aContainer the Container whose components will be labeled.
-*/
- public static void nameComponents( Container aContainer )
- {
- nameComponents( aContainer, "" );
- }
-
- protected static void nameComponents( Container aContainer, String path )
- {
- Component c = null;
- String className = null;
- int index = 0;
- int count = aContainer.getComponentCount();
- for ( int i = 0; i < count; i++ )
- {
- c = aContainer.getComponent( i );
- className = c.getClass().getName();
- className = className.substring( className.lastIndexOf( '.' ) + 1 );
- System.out.println( path + className );
- if ( c instanceof javax.swing.JComponent )
- {
- // ((javax.swing.JComponent)c).setToolTipText( path + className + " (" + c.getName() + ")" );
- ((javax.swing.JComponent)c).setToolTipText( c.getName() );
- }
- if ( c instanceof Container )
- {
- nameComponents( (Container) c, path + className + "." );
- }
- }
- }
-
-/**
-* Sets the enabled state of a container and all of its components.
-* @param aContainer the Container whose components will be enabled.
-* @param isEnabled True if enabled, false id disabled.
-*/
- public static void enableComponents( Container aContainer, boolean isEnabled )
- {
- Component c = null;
- String className = null;
- int index = 0;
- int count = aContainer.getComponentCount();
- for ( int i = 0; i < count; i++ )
- {
- c = aContainer.getComponent( i );
- if ( c instanceof Container )
- {
- enableComponents( (Container) c, isEnabled );
- }
- else
- {
- c.setEnabled( isEnabled );
- }
- }
- aContainer.setEnabled( isEnabled );
- }
+ if (parentSize.width > mySize.width) {
+ x = ((parentSize.width - mySize.width) / 2) + parentLocation.x;
+ }
+ if (parentSize.height > mySize.height) {
+ y = ((parentSize.height - mySize.height) / 2) + parentLocation.y;
+ }
+ break;
+ case CENTER:
+ if (screenSize.width > mySize.width) {
+ x = (screenSize.width - mySize.width) / 2;
+ }
+ if (screenSize.height > mySize.height) {
+ y = (screenSize.height - mySize.height) / 2;
+ }
+ break;
+ case CASCADE:
+ x = lastX + incrementX;
+ if (x + mySize.width > screenSize.width) {
+ x = incrementX;
+ }
+ y = lastY + incrementY;
+ if (y + mySize.height > screenSize.height) {
+ y = incrementY;
+ }
+ lastX = x;
+ lastY = y;
+ break;
+ default:
+ // don't move the frame
+ Point p = aWindow.getLocation();
+ x = p.x;
+ y = p.y;
+ break;
+ }
+ aWindow.setLocation(x, y);
+ }
+
+ /**
+ * Returns the first parent Window of the specified component.
+ *
+ * @param c the Component whose parent will be found.
+ * @return the Window that contains the component, or null if the component does
+ * not have a valid Frame parent.
+ */
+ public static Window getWindowForComponent(Component c) {
+ for (Component p = c; p != null; p = p.getParent()) {
+ if (p instanceof Window) {
+ return (Window) p;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Prints out a list of all components to System.out.
+ *
+ * @param aContainer the Container whose components will be listed.
+ */
+ public static void dumpComponents(Container aContainer) {
+ dumpComponents(aContainer, "");
+ }
+
+ protected static void dumpComponents(Container aContainer, String padding) {
+ Component c = null;
+ int count = aContainer.getComponentCount();
+ for (int i = 0; i < count; i++) {
+ c = aContainer.getComponent(i);
+ if (c instanceof javax.swing.JComponent) {
+ System.out.println(padding + c.getClass() + ": "
+ + ((javax.swing.JComponent) c).getAccessibleContext().getAccessibleName());
+ } else {
+ System.out.println(padding + c.getClass() + ": " + c.getName());
+ }
+ if (c instanceof Container) {
+ dumpComponents((Container) c, padding + " ");
+ }
+ }
+ }
+
+ /**
+ * Gets a list of all children of a specified container, sorted by position.
+ * Components are sorted from top to bottom and then left to right.
+ *
+ * @param aContainer The container whose children are to be returned.
+ * @result A List containing the sorted components.
+ */
+ public static List getSortedChildComponents(Container aContainer) {
+ List result = new ArrayList(getAllChildComponents(aContainer));
+ Collections.sort(result, new PositionComparator(aContainer));
+ return result;
+ }
+
+ public static void dumpSortedChildComponents(Container aContainer) {
+ Component c = null;
+ Iterator it = getSortedChildComponents(aContainer).iterator();
+ while (it.hasNext()) {
+ c = (Component) it.next();
+ System.out.println(c.getLocation() + " : " + c.getClass());
+ }
+ }
+
+ public static void dumpNamedChildComponents(Container aContainer) {
+ Iterator it = getUniqueNameMap(getSortedChildComponents(aContainer)).values().iterator();
+ while (it.hasNext()) {
+ System.out.println(it.next());
+ }
+ }
+
+ /**
+ * Generates a unique name for each object in a list and returns a map that maps
+ * the objects to the names. The name is based on the class of the object in the
+ * object list.
+ *
+ * @param anObjectList A List of objects.
+ * @return A Map that maps the objects in the list to the generated names.
+ */
+ public static Map getUniqueNameMap(List anObjectList) {
+ Map namesToObjects = new HashMap(anObjectList.size(), 1F);
+ Map objectsToNames = new HashMap(anObjectList.size(), 1F);
+
+ Object o = null;
+ String name = null;
+ int lastIndex = 0;
+ Iterator it = anObjectList.iterator();
+ while (it.hasNext()) {
+ o = it.next();
+ name = o.getClass().getName();
+ lastIndex = name.lastIndexOf(".");
+ if (lastIndex != -1) {
+ name = name.substring(lastIndex + 1);
+ }
+ name = incrementString(name);
+ while (namesToObjects.get(name) != null) {
+ name = incrementString(name);
+ }
+ namesToObjects.put(name, o);
+ objectsToNames.put(o, name);
+ }
+
+ return objectsToNames;
+ }
+
+ /**
+ * Numerically increments a string. For example, "hello" becomes "hello1" and
+ * "hello1" becomes "hello2" while "hello999" becomes "hello1000".
+ *
+ * @param aString a String to be incremented.
+ * @return The incremented String.
+ */
+ public static String incrementString(String aString) {
+ int i = aString.length() - 1;
+ while ((i >= 0) && (Character.isDigit(aString.charAt(i)))) {
+ i--;
+ }
+
+ if (i == aString.length() - 1) { // no numerics at end of string, increment manually
+ return aString + "1";
+ }
+
+ String alpha = aString.substring(0, i + 1);
+ String numeric = aString.substring(i + 1);
+ numeric = Integer.toString(Integer.parseInt(numeric) + 1);
+
+ return alpha + numeric;
+
+ }
+
+ /**
+ * Gets all children of the specified container.
+ *
+ * @param aContainer the Container to be searched.
+ * @return A Collection containing all of the child components of the container.
+ */
+ public static Collection getAllChildComponents(Container aContainer) {
+ Collection result = new ArrayList();
+ addAllChildComponents(aContainer, result);
+ return result;
+ }
+
+ /**
+ * Adds all children of the specified container to the specified collection.
+ *
+ * @param aContainer the Container to be searched.
+ * @param aCollection the Collection to which the child components will be
+ * added.
+ */
+ protected static void addAllChildComponents(Container aContainer, Collection aCollection) {
+ Component c = null;
+ int count = aContainer.getComponentCount();
+ for (int i = 0; i < count; i++) {
+ c = aContainer.getComponent(i);
+ aCollection.add(c);
+ if (c instanceof Container) {
+ addAllChildComponents((Container) c, aCollection);
+ }
+ }
+ }
+
+ /**
+ * Sets each child component's tooltip to show the name generated from
+ * getComponentNameMap(). (We're doing this so the tooltip authors can know how
+ * to reference the components.)
+ *
+ * @param aContainer the Container whose components will be labeled.
+ */
+ public static void labelComponents(Container aContainer) {
+ Map nameToComponent = getNameToComponentMap(aContainer);
+ Map nameToName = new HashMap(nameToComponent.size(), 1F);
+ Iterator it = nameToComponent.keySet().iterator();
+ String key;
+ while (it.hasNext()) {
+ key = it.next().toString();
+ nameToName.put(key, key);
+ }
+ labelComponents(aContainer, nameToName);
+ }
+
+ /**
+ * Sets each child component's tooltip to show a given string retrieved from a
+ * map using the component's generated name as a key.
+ *
+ * @param aContainer the Container whose components will be labeled.
+ * @param aNameMap a Map of generated names to string values.
+ */
+ public static void labelComponents(Container aContainer, Map aNameMap) {
+ if (aNameMap == null)
+ return;
+
+ String key;
+ Object o;
+ Iterator it = aNameMap.keySet().iterator();
+ Map nameToComponent = getNameToComponentMap(aContainer);
+ while (it.hasNext()) {
+ key = it.next().toString();
+ o = nameToComponent.get(key);
+ if (o instanceof javax.swing.JComponent) {
+ ((javax.swing.JComponent) o).setToolTipText(aNameMap.get(key).toString());
+ }
+ }
+ }
+
+ /**
+ * Generates a deterministically unique name for each component in a container.
+ * The name is based on the name of the class of the component followed by a
+ * number. Each class of component is numbered based on it's position in the
+ * container, sorted from top to bottom and left to right.
+ *
+ * @param aContainer the Container whose components will named.
+ * @return a Map that maps each component to its name.
+ */
+ public static Map getComponentToNameMap(Container aContainer) {
+ return getUniqueNameMap(getSortedChildComponents(aContainer));
+ }
+
+ /**
+ * Maps a deterministically unique name to a component in a container. The name
+ * is based on the name of the class of the component followed by a number. Each
+ * class of component is numbered based on it's position in the container,
+ * sorted from top to bottom and left to right.
+ *
+ * @param aContainer the Container whose components will named.
+ * @return a Map that maps each component to its name.
+ */
+ public static Map getNameToComponentMap(Container aContainer) {
+ Map componentToName = getComponentToNameMap(aContainer);
+ Map result = new HashMap(componentToName.size(), 1F);
+ Iterator it = componentToName.keySet().iterator();
+ Object key;
+ while (it.hasNext()) {
+ key = it.next();
+ result.put(componentToName.get(key), key);
+ }
+ return result;
+ }
+
+ /**
+ * Sets the tooltips of all components in a container to the respective names of
+ * those components. (We're using this so the tooltip authors can know how to
+ * reference the components.)
+ *
+ * @param aContainer the Container whose components will be labeled.
+ */
+ public static void nameComponents(Container aContainer) {
+ nameComponents(aContainer, "");
+ }
+
+ protected static void nameComponents(Container aContainer, String path) {
+ Component c = null;
+ String className = null;
+ int index = 0;
+ int count = aContainer.getComponentCount();
+ for (int i = 0; i < count; i++) {
+ c = aContainer.getComponent(i);
+ className = c.getClass().getName();
+ className = className.substring(className.lastIndexOf('.') + 1);
+ System.out.println(path + className);
+ if (c instanceof javax.swing.JComponent) {
+ // ((javax.swing.JComponent)c).setToolTipText( path + className + " (" +
+ // c.getName() + ")" );
+ ((javax.swing.JComponent) c).setToolTipText(c.getName());
+ }
+ if (c instanceof Container) {
+ nameComponents((Container) c, path + className + ".");
+ }
+ }
+ }
+
+ /**
+ * Sets the enabled state of a container and all of its components.
+ *
+ * @param aContainer the Container whose components will be enabled.
+ * @param isEnabled True if enabled, false id disabled.
+ */
+ public static void enableComponents(Container aContainer, boolean isEnabled) {
+ Component c = null;
+ String className = null;
+ int index = 0;
+ int count = aContainer.getComponentCount();
+ for (int i = 0; i < count; i++) {
+ c = aContainer.getComponent(i);
+ if (c instanceof Container) {
+ enableComponents((Container) c, isEnabled);
+ } else {
+ c.setEnabled(isEnabled);
+ }
+ }
+ aContainer.setEnabled(isEnabled);
+ }
}
/*
- * $Log$
- * Revision 1.2 2006/02/18 23:19:05 cgruber
- * Update imports and maven dependencies.
+ * $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.1 2006/02/16 13:22:22 cgruber Check in all sources in
+ * eclipse-friendly maven-enabled packages.
*
- * Revision 1.2 2001/02/17 16:52:05 mpowers
- * Changes in imports to support building with jdk1.1 collections.
+ * 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:51:55 mpowers
- * Contributing wotonomy.
+ * Revision 1.1.1.1 2000/12/21 15:51:55 mpowers Contributing wotonomy.
*
- * Revision 1.2 2000/12/20 16:25:46 michael
- * Added log to all files.
+ * Revision 1.2 2000/12/20 16:25:46 michael Added log to all files.
*
*
*/
-