diff options
| author | Benjamin Culkin <scorpress@gmail.com> | 2024-05-20 17:58:16 -0400 |
|---|---|---|
| committer | Benjamin Culkin <scorpress@gmail.com> | 2024-05-20 17:58:16 -0400 |
| commit | 40a9d99496e098562f090fb7ffce9e749011b131 (patch) | |
| tree | 437df24d65470582e943e494a52db8ed65a881ae /projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/TableColumnAssociation.java | |
| parent | ff072dfe782f6f22123cd4ba050828d35c0d0fbd (diff) | |
Formatting pass
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.java | 1085 |
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. * * */ - |
