summaryrefslogtreecommitdiff
path: root/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/TableColumnAssociation.java
diff options
context:
space:
mode:
Diffstat (limited to 'projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/TableColumnAssociation.java')
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/TableColumnAssociation.java1085
1 files changed, 479 insertions, 606 deletions
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.
*
*
*/
-