Coverage Report - net.wotonomy.ui.swing.TableColumnAssociation
 
Classes in this File Line Coverage Branch Coverage Complexity
TableColumnAssociation
0% 
0% 
2.667
 
 1  
 /*
 2  
 Wotonomy: OpenStep design patterns for pure Java applications.
 3  
 Copyright (C) 2000 Intersect Software Corporation
 4  
 
 5  
 This library is free software; you can redistribute it and/or
 6  
 modify it under the terms of the GNU Lesser General Public
 7  
 License as published by the Free Software Foundation; either
 8  
 version 2.1 of the License, or (at your option) any later version.
 9  
 
 10  
 This library is distributed in the hope that it will be useful,
 11  
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 12  
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 13  
 Lesser General Public License for more details.
 14  
 
 15  
 You should have received a copy of the GNU Lesser General Public
 16  
 License along with this library; if not, see http://www.gnu.org
 17  
 */
 18  
 
 19  
 package net.wotonomy.ui.swing;
 20  
 
 21  
 import java.awt.Color;
 22  
 import java.awt.Graphics;
 23  
 import java.awt.Graphics2D;
 24  
 import java.awt.Polygon;
 25  
 import java.awt.Rectangle;
 26  
 import java.awt.RenderingHints;
 27  
 import java.util.Iterator;
 28  
 import java.util.List;
 29  
 
 30  
 import javax.swing.JTable;
 31  
 import javax.swing.table.TableColumn;
 32  
 
 33  
 import net.wotonomy.control.EOSortOrdering;
 34  
 import net.wotonomy.foundation.NSArray;
 35  
 import net.wotonomy.foundation.internal.ValueConverter;
 36  
 import net.wotonomy.foundation.internal.WotonomyException;
 37  
 import net.wotonomy.ui.EOAssociation;
 38  
 import net.wotonomy.ui.EODisplayGroup;
 39  
 import net.wotonomy.ui.swing.TableAssociation.TableAssociationModel;
 40  
 
 41  
 
 42  
 /**
 43  
 * TableColumnAssociation binds a column of a JTable
 44  
 * to a property of the elements of a display group.  
 45  
 * Bindings are: 
 46  
 * <ul>
 47  
 * <li>value: a property convertable to a string for
 48  
 * display in the cells of the table column</li> 
 49  
 * <li>editable: a property convertable to a boolean 
 50  
 * that determines the editability of the corresponding
 51  
 * cells in the column.</li> 
 52  
 * </ul>
 53  
 
 54  
 * Because TableColumns do not have a handle to their
 55  
 * containing JTable, setTable() must be called before
 56  
 * calling establishConnection().  This will add the 
 57  
 * controlled TableColumn to the specified JTable.
 58  
 *
 59  
 * Columns appear in the table in the order in which
 60  
 * setTable is called on the corresponding association.
 61  
 * The original table model index is ignored.
 62  
 *
 63  
 * Column names appear in the table based on the value
 64  
 * of TableColumn.getHeaderValue().
 65  
 *
 66  
 * @author michael@mpowers.net
 67  
 * @author $Author: cgruber $
 68  
 * @version $Revision: 904 $
 69  
 */
 70  0
 public class TableColumnAssociation extends EOAssociation
 71  
 {
 72  0
     static final NSArray aspects = 
 73  0
         new NSArray( new Object[] {
 74  0
             ValueAspect, EditableAspect
 75  
         } );
 76  0
     static final NSArray aspectSignatures = 
 77  0
         new NSArray( new Object[] {
 78  0
             AttributeToOneAspectSignature
 79  
         } );
 80  0
     static final NSArray objectKeysTaken = 
 81  0
         new NSArray( new Object[] {
 82  0
             "table" 
 83  
         } );
 84  
 
 85  
     static Color[] sortIndicatorColorList;
 86  
                 
 87  
         EODisplayGroup valueDisplayGroup, editableDisplayGroup;
 88  
         String valueKey, editableKey;
 89  
     boolean sortable;
 90  
     boolean sortCaseSensitive;
 91  
     JTable table;
 92  
         
 93  
     /**
 94  
     * Constructor specifying the object to be controlled by this
 95  
     * association.  Throws an exception if the object is not
 96  
         * a TableColumn.  setTable() must be called before 
 97  
         * establishing the connection.
 98  
     */
 99  
     public TableColumnAssociation ( Object anObject )
 100  
     {
 101  0
         super( anObject );
 102  0
                 valueDisplayGroup = null;
 103  0
                 valueKey = null;
 104  0
                 editableDisplayGroup = null;
 105  0
                 editableKey = null;
 106  0
         sortable = true;
 107  0
         sortCaseSensitive = true;
 108  0
         table = null;
 109  0
     }
 110  
         
 111  
         /**
 112  
         * Sets the table to be used for this column association.
 113  
         * If no TableAssociation exists for the specified table,
 114  
         * one will be created automatically.  The controlled
 115  
         * table column will be added to the table.  Note that
 116  
         * the table column's model index is ignored: table columns
 117  
         * appear in the table in the order in which setTable is
 118  
         * called on their corresponding associations.
 119  
         */
 120  
         public void setTable( JTable aTable )
 121  
         {
 122  0
         table = aTable;
 123  0
         if ( table == null ) return;
 124  
         
 125  
         // creates table association if not existing
 126  0
         getTableAssociation();        
 127  0
         }
 128  
     
 129  
     /**
 130  
     * Returns the table association for this table column,
 131  
     * or null if no table has been set.  This method will
 132  
     * create the association if none exists for the table.
 133  
     */
 134  
     public TableAssociation getTableAssociation()
 135  
     {
 136  0
         if ( table == null ) return null;
 137  
         
 138  
         TableAssociation result;        
 139  0
                 if ( ! ( table.getModel() instanceof TableAssociationModel ) )
 140  
                 {
 141  0
                         result = new TableAssociation( table );
 142  0
                         result.bindAspect( 
 143  0
                                 SourceAspect, displayGroupForAspect( ValueAspect ), "" );
 144  0
                 }
 145  
         else
 146  
         {
 147  0
             result = ((TableAssociationModel)table.getModel()).getAssociation();
 148  
         }
 149  0
         return result;
 150  
     }
 151  
     
 152  
     /**
 153  
     * Returns a List of aspect signatures whose contents
 154  
     * correspond with the aspects list.  Each element is 
 155  
     * a string whose characters represent a capability of
 156  
     * the corresponding aspect. <ul>
 157  
     * <li>"A" attribute: the aspect can be bound to
 158  
     * an attribute.</li>
 159  
     * <li>"1" to-one: the aspect can be bound to a
 160  
     * property that returns a single object.</li>
 161  
     * <li>"M" to-one: the aspect can be bound to a
 162  
     * property that returns multiple objects.</li>
 163  
     * </ul> 
 164  
     * An empty signature "" means that the aspect can
 165  
     * bind without needing a key.
 166  
     * This implementation returns "A1M" for each
 167  
     * element in the aspects array.
 168  
     */
 169  
     public static NSArray aspectSignatures ()
 170  
     {
 171  0
         return aspectSignatures;
 172  
     }
 173  
     
 174  
     /**
 175  
     * Returns a List that describes the aspects supported
 176  
     * by this class.  Each element in the list is the string
 177  
     * name of the aspect.  This implementation returns an
 178  
     * empty list.
 179  
     */
 180  
     public static NSArray aspects ()
 181  
     {
 182  0
         return aspects;
 183  
     }
 184  
     
 185  
     /**
 186  
     * Returns a List of EOAssociation subclasses that,
 187  
     * for the objects that are usable for this association,
 188  
     * are less suitable than this association.
 189  
     */
 190  
     public static NSArray associationClassesSuperseded ()
 191  
     {
 192  0
         return new NSArray();
 193  
     }
 194  
     
 195  
     /**
 196  
     * Returns whether this class can control the specified 
 197  
     * object. 
 198  
     */
 199  
     public static boolean isUsableWithObject ( Object anObject )
 200  
     {
 201  0
         return ( anObject instanceof TableColumn );
 202  
     }
 203  
     
 204  
     /**
 205  
     * Returns a List of properties of the controlled object
 206  
     * that are controlled by this class.  For example,
 207  
     * "stringValue", or "selected".
 208  
     */
 209  
     public static NSArray objectKeysTaken ()
 210  
     {
 211  0
         return objectKeysTaken;
 212  
     }
 213  
     
 214  
     /**
 215  
     * Returns the aspect that is considered primary
 216  
     * or default.  This is typically "value" or somesuch.
 217  
     */
 218  
     public static String primaryAspect ()
 219  
     {
 220  0
         return ValueAspect;
 221  
     }
 222  
         
 223  
     /**
 224  
     * Returns whether this association can bind to the
 225  
     * specified display group on the specified key for
 226  
     * the specified aspect.  
 227  
     */
 228  
     public boolean canBindAspect ( 
 229  
         String anAspect, EODisplayGroup aDisplayGroup, String aKey)
 230  
     {
 231  0
         return ( aspects.containsObject( anAspect ) );
 232  
     }
 233  
     
 234  
     /**
 235  
     * Binds the specified aspect of this association to the
 236  
     * specified key on the specified display group.
 237  
     */
 238  
     public void bindAspect ( 
 239  
         String anAspect, EODisplayGroup aDisplayGroup, String aKey )
 240  
         {
 241  0
                 if ( ValueAspect.equals( anAspect ) )
 242  
                 {
 243  0
                         valueDisplayGroup = aDisplayGroup;        
 244  0
                         valueKey = aKey;
 245  
                 }
 246  0
                 if ( EditableAspect.equals( anAspect ) )
 247  
                 {
 248  0
                         editableDisplayGroup = aDisplayGroup;
 249  0
                         editableKey = aKey;
 250  
                 }
 251  0
                 super.bindAspect( anAspect, aDisplayGroup, aKey );
 252  0
         }
 253  
         
 254  
     /**
 255  
     * Establishes a connection between this association
 256  
     * and the controlled object.  Subclasses should begin
 257  
     * listening for events from their controlled object here.
 258  
     */
 259  
     public void establishConnection ()
 260  
     {
 261  0
         addAsListener();
 262  
         
 263  0
         if ( table == null ) throw new WotonomyException(
 264  0
             "A table must be specified by calling setTable()" );
 265  
 
 266  
                 // add association to model
 267  0
                 TableAssociationModel model = 
 268  0
                         (TableAssociationModel) table.getModel();
 269  0
         model.addColumnAssociation( this );
 270  
         
 271  0
         super.establishConnection();
 272  0
     }
 273  
     
 274  
     /**
 275  
     * Breaks the connection between this association and 
 276  
     * its object.  Override to stop listening for events
 277  
     * from the object.
 278  
     */
 279  
     public void breakConnection ()
 280  
     {
 281  0
         removeAsListener();
 282  
 
 283  0
         if ( table == null ) throw new WotonomyException(
 284  0
             "TableColumnAssociation's table may not be null" );
 285  
 
 286  
                 // remove association from model
 287  0
                 TableAssociationModel model = 
 288  0
                         (TableAssociationModel) table.getModel();
 289  0
                 model.removeColumnAssociation( this );
 290  
         
 291  0
         super.breakConnection();
 292  0
     }
 293  
 
 294  
     protected void addAsListener()
 295  
     {
 296  0
     }
 297  
 
 298  
     protected void removeAsListener()
 299  
     {
 300  0
     }
 301  
         
 302  
         /**
 303  
         * Returns the value to be displayed at the specified index.
 304  
         * This method is called by the TableAssocation to populate
 305  
         * the table model.
 306  
         * This implementation simply retrieves the value from the
 307  
         * display group bound to the value aspect.
 308  
         */
 309  
         public Object valueAtIndex( int aRowIndex )
 310  
         {
 311  0
                 if ( valueDisplayGroup != null )
 312  
                 {
 313  0
                         return valueDisplayGroup.valueForObjectAtIndex( 
 314  0
                                 aRowIndex, valueKey );
 315  
                 }
 316  0
                 return null;
 317  
         }
 318  
         
 319  
         /**
 320  
         * Sets a value for the specified index.  This method is 
 321  
         * called by the TableAssocation after a cell has been
 322  
         * edited.
 323  
         * This implementation simply sets the value in the 
 324  
         * display group bound to the value aspect.
 325  
         */
 326  
         public void setValueAtIndex( Object aValue, int aRowIndex )
 327  
         {
 328  0
                 if ( valueDisplayGroup != null )
 329  
                 {
 330  0
                         valueDisplayGroup.setValueForObjectAtIndex( 
 331  0
                                 aValue, aRowIndex, valueKey );
 332  
                 }
 333  0
         }
 334  
     
 335  
     /**
 336  
     * Returns whether this column should be sorted when the
 337  
     * user clicks on the column header.  Defaults to true.
 338  
     */
 339  
     public boolean isSortable()
 340  
     {
 341  0
         return sortable;
 342  
     }
 343  
     
 344  
     /**
 345  
     * Sets whether this column should be sorted when the
 346  
     * user clicks on the column header.
 347  
     */
 348  
     public void setSortable( boolean isSortable )
 349  
     {
 350  0
         sortable = isSortable;   
 351  0
     }
 352  
 
 353  
     /**
 354  
     * Returns whether this column should be sorted 
 355  
     * in a case sensitive manner.  Defaults to true.
 356  
     */
 357  
     public boolean isSortCaseSensitive()
 358  
     {
 359  0
         return sortCaseSensitive;
 360  
     }
 361  
     
 362  
     /**
 363  
     * Sets whether this column should be sorted when
 364  
     * in a case sensitive manner.
 365  
     * If false, the column contents should be string values.
 366  
     */
 367  
     public void setSortCaseSensitive( boolean isCaseSensitive )
 368  
     {
 369  0
         sortCaseSensitive = isCaseSensitive;   
 370  0
     }
 371  
 
 372  
         /**
 373  
         * Called by the TableAssociation to determine whether 
 374  
         * the value at the specified row is editable.
 375  
         * This is determined by the binding of the Editable aspect,
 376  
         * looking at the value of the corresponding index in that
 377  
         * display group.  Note: because the display group may
 378  
         * not have the same number if items, the selected index is
 379  
         * used if the editable display group is not the same as the
 380  
         * the value display group.
 381  
         */
 382  
         public boolean isEditableAtRow( int aRowIndex )
 383  
         {
 384  0
                 if ( editableKey == null ) return false;
 385  0
                 Object value = null;
 386  0
                 if ( editableDisplayGroup != null )
 387  
                 {
 388  
                         // if using the same group for both, return the value for the index
 389  0
                         if ( editableDisplayGroup.equals( valueDisplayGroup ) )
 390  
                         {
 391  0
                                 value =
 392  0
                                         editableDisplayGroup.valueForObjectAtIndex( aRowIndex, editableKey );
 393  0
                         }
 394  
                         else // using an external display group to determine editability
 395  
                         {
 396  
                                 // ignore index and use the selected object value from display group
 397  0
                                 value =
 398  0
                                         editableDisplayGroup.selectedObjectValueForKey( editableKey );
 399  
                         }
 400  0
                 }
 401  
                 else
 402  
                 {
 403  
                         // treat bound key without display group as a value
 404  0
                         value = editableKey;        
 405  
                 }
 406  0
                 if ( value == null ) return false; // null defaults to false
 407  0
                 Boolean result = (Boolean)
 408  0
                         ValueConverter.convertObjectToClass( value, Boolean.class );
 409  0
                 if ( result == null ) return true; // non-null defaults to true
 410  0
                 return result.booleanValue();
 411  
         }
 412  
                 
 413  
     // convenience
 414  
 
 415  
     private TableColumn component()
 416  
     {
 417  0
         return (TableColumn) object();
 418  
     }
 419  
     
 420  
     /**
 421  
     * Called by TableAssociation to get a EOSortOrdering suitable 
 422  
     * for the information in this column.
 423  
     * This implementation returns a EOSortOrdering with the key
 424  
     * equal to the value aspect's key and the appropriate selector
 425  
     * for the specified ascending value and the case sensitivity
 426  
     * of this column.
 427  
     * Override to customize the sort for your column.
 428  
     */
 429  
     public EOSortOrdering getSortOrdering( boolean isAscending )
 430  
     {
 431  0
         if ( isAscending )
 432  
         {
 433  0
             if ( isSortCaseSensitive() )
 434  
             {
 435  0
                 return new EOSortOrdering(
 436  0
                     valueKey, 
 437  0
                     EOSortOrdering.CompareAscending ) ;
 438  
             }
 439  
             else
 440  
             {
 441  0
                 return new EOSortOrdering(
 442  0
                     valueKey, 
 443  0
                     EOSortOrdering.CompareCaseInsensitiveAscending ) ;
 444  
             }
 445  
         }
 446  
         else
 447  
         {
 448  0
             if ( isSortCaseSensitive() )
 449  
             {
 450  0
                 return new EOSortOrdering(
 451  0
                     valueKey, 
 452  0
                     EOSortOrdering.CompareDescending ) ;
 453  
             }
 454  
             else
 455  
             {
 456  0
                 return new EOSortOrdering(
 457  0
                     valueKey, 
 458  0
                     EOSortOrdering.CompareCaseInsensitiveDescending ) ;
 459  
             }
 460  
         }
 461  
     }
 462  
     
 463  
     /**
 464  
     * Returns the one-based index of this assocation's sort ordering
 465  
     * in the specified list of orderings.  If the sign of the returned
 466  
     * value is negative, the ordering is descending.  If the return
 467  
     * value is zero, no matching ordering was found.
 468  
     */
 469  
     protected int getIndexOfMatchingOrdering( List orderings )
 470  
     {
 471  
         // find index of matching ordering
 472  0
         int index = 0;
 473  0
         EOSortOrdering ordering = null;
 474  0
         Iterator i = orderings.iterator();
 475  0
         while ( i.hasNext() )
 476  
         {
 477  0
             index++;
 478  0
             ordering = (EOSortOrdering) i.next();
 479  0
             if ( ordering.key().equals( valueKey ) )
 480  
             {
 481  
                 // determine ascending or descending 
 482  0
                 if ( getSortOrdering( true ).equals( ordering ) )
 483  
                 {
 484  0
                     return index;
 485  
                 }
 486  
                 else
 487  0
                 if ( getSortOrdering( false ).equals( ordering ) )
 488  
                 {
 489  0
                     return -index;
 490  
                 }
 491  
             }
 492  
         }
 493  0
         return 0;
 494  
         
 495  
     }
 496  
     
 497  
     /**
 498  
     * Called by TableAssociation to draw some indicator in the
 499  
     * specified rectangle using the specified graphics to indicate
 500  
     * the specified sort state.  The rectangle corresponds to the
 501  
     * bounds of the column header.
 502  
     * This implementation draws a small transparent gray triangle at
 503  
     * the right edge of the bounding rectangle.
 504  
     * Override to do something different or to do nothing at all.
 505  
     */ 
 506  
     protected void drawSortIndicator( Rectangle aBoundingRectangle,
 507  
         Graphics aGraphicsContext, List orderings )
 508  
     {
 509  0
         int index = getIndexOfMatchingOrdering( orderings );
 510  0
         if ( index == 0 ) return;
 511  
         
 512  0
         boolean isAscending = ( index > 0 );
 513  0
         index = Math.abs( index );
 514  
         
 515  
         // turn on anti-aliasing
 516  0
         if ( aGraphicsContext instanceof Graphics2D )
 517  
         {
 518  0
             ((Graphics2D)aGraphicsContext).setRenderingHint( 
 519  0
                 RenderingHints.KEY_ANTIALIASING, 
 520  0
                 RenderingHints.VALUE_ANTIALIAS_ON );
 521  
         }
 522  
         
 523  0
         Rectangle r = new Rectangle( aBoundingRectangle );
 524  
         
 525  
         // resize to a right-justified square, sides equal to height
 526  0
         r.setBounds( r.x + r.width - r.height, r.y, r.height, r.height );
 527  
         
 528  
         // resize to about a third smaller
 529  0
         int portion = r.height / 3;
 530  0
         r.grow( -portion, -portion );
 531  
         
 532  
         // transparencies cause java2d printing to rasterize,
 533  
         //   resulting in excessive memory usage and print time.
 534  
         // aGraphicsContext.setColor( new Color( 0, 0, 0, 255 / (index*2) ) );
 535  0
         aGraphicsContext.setColor( getSortIndicatorColor( index ) );
 536  
         
 537  
         Polygon triangle;
 538  0
         if ( !isAscending )
 539  
         {
 540  0
             triangle = new Polygon( 
 541  0
                 new int[] { r.x, r.x+r.width/2, r.x+r.width },
 542  0
                 new int[] { r.y, r.y+r.height, r.y }, 3 );            
 543  0
         }
 544  
         else
 545  
         {
 546  0
             triangle = new Polygon( 
 547  0
                 new int[] { r.x, r.x+r.width/2, r.x+r.width },            
 548  0
                 new int[] { r.y+r.height, r.y, r.y+r.height }, 3 );
 549  
         }                
 550  0
         aGraphicsContext.fillPolygon( triangle );
 551  0
     }
 552  
     
 553  
     /**
 554  
      * Returns a color to be used by the sort indicator based on the index
 555  
      * of the sorting column.  The goal of this method is to make the color
 556  
      * appear lighter and lighter, the "less" primary the sort order for this
 557  
      * column is.  This can be acheives simply though a "transparent" color,
 558  
      * however, during printing of the corresponding table, java print
 559  
      * kicks into "raster" based printing when printing a component with
 560  
      * a transparent color instead of "vector" based printing.  Raster
 561  
      * based printing can take up to 20-30 times longer to print than
 562  
      * vector printing and consume several times the amount of memory.
 563  
      * Raster-based printing should be avoided at all costs if the a component
 564  
      * is to be printed (as of Java 1.3.1).
 565  
      * @param index The "sort" index of the associated table column.  The higher
 566  
      *        the index, the lighter the color will be.  An index of 0 will
 567  
      *        return null.
 568  
      * @return The color to use when rendering the sort indicator.
 569  
      */
 570  
     protected static Color getSortIndicatorColor( int index )
 571  
     {
 572  0
         if ( index == 0 ) return null;
 573  
 
 574  
         // Create the color list if not already created.
 575  0
         if ( sortIndicatorColorList == null )
 576  
         {
 577  
             // Default size to 13 elements, it would be extremely rare that a
 578  
             // user sorts more than 12 columns at a time (although possible).
 579  
             // (Index 0 is not used.)
 580  0
             sortIndicatorColorList = new Color[ 13 ];
 581  
         }
 582  
 
 583  
         // Get the color out of the color list.  Use the index directly as
 584  
         // an index into an ordered list.  If the color has already been
 585  
         // created for that index, then return it, otherwise create the color.
 586  0
         if ( ( index < sortIndicatorColorList.length ) &&
 587  0
              ( sortIndicatorColorList[ index ] != null ) )
 588  
         {
 589  0
             return sortIndicatorColorList[ index ];
 590  
         }
 591  
 
 592  
         // The following logic performs the same affect as the above
 593  
         // transparent color, without actually using a transparent color.
 594  
         // Start with the table header's background color and derive a color
 595  
         // that is "darker" than that color.  Any color this logic creates will
 596  
         // be between those two colors.
 597  0
         Color lightColor = java.awt.SystemColor.control;
 598  0
         Color darkColor = lightColor.darker().darker();
 599  
 
 600  
         // Make the light color (the upper bound) a little darker, so that even
 601  
         // the lightest triangle will still be slightly visible.
 602  0
         lightColor = new Color(
 603  0
             Math.max( ( int )( lightColor.getRed() * 0.9), 0 ),
 604  0
             Math.max( ( int )( lightColor.getGreen() * 0.9), 0 ),
 605  0
             Math.max( ( int )( lightColor.getBlue() * 0.9), 0) );
 606  
 
 607  
         // Subtract the light color from the dark color.  This is the range
 608  
         // between the two colors.
 609  0
         Color difference = new Color( lightColor.getRed() - darkColor.getRed(),
 610  0
             lightColor.getGreen() - darkColor.getGreen(),
 611  0
             lightColor.getBlue() - darkColor.getBlue() );
 612  
 
 613  
         // If the index is 1, user the dark color as is.  Otherwise scale the
 614  
         // color closer and closer to the lighter color as the index gets
 615  
         // biggger and bigger.
 616  0
         if ( index > 1 )
 617  
         {
 618  0
             float factor = ( float )Math.pow( 0.5, ( index - 1 ) );
 619  0
             darkColor = new Color(
 620  0
                 Math.max( lightColor.getRed() - ( int )( difference.getRed() * factor ), 0 ),
 621  0
                 Math.max( lightColor.getGreen() - ( int )( difference.getGreen() * factor ), 0 ),
 622  0
                 Math.max( lightColor.getBlue() - ( int )( difference.getBlue() * factor ), 0 ) );
 623  
         }
 624  
 
 625  
         // Cache the created color in the color list for this index.
 626  0
         if ( index >= sortIndicatorColorList.length )
 627  
         {
 628  
             // The color list is too small, create a new larger list with
 629  
             // some padding for even larger indicies.
 630  0
             Color[] oldList = sortIndicatorColorList;
 631  0
             sortIndicatorColorList = new Color[ index + 5 ];
 632  0
             System.arraycopy( oldList, 0, sortIndicatorColorList, 0, oldList.length );
 633  
         }
 634  0
         sortIndicatorColorList[ index ] = darkColor;
 635  
 
 636  0
         return darkColor;
 637  
     } 
 638  
 }
 639  
 
 640  
 /*
 641  
  * $Log$
 642  
  * Revision 1.2  2006/02/18 23:19:05  cgruber
 643  
  * Update imports and maven dependencies.
 644  
  *
 645  
  * Revision 1.1  2006/02/16 13:22:22  cgruber
 646  
  * Check in all sources in eclipse-friendly maven-enabled packages.
 647  
  *
 648  
  * Revision 1.16  2003/08/06 23:07:52  chochos
 649  
  * general code cleanup (mostly, removing unused imports)
 650  
  *
 651  
  * Revision 1.15  2002/08/22 15:42:49  mpowers
 652  
  * No longer using transparency to render sort indicator (see comments).
 653  
  *
 654  
  * Revision 1.14  2002/04/12 21:05:57  mpowers
 655  
  * Now distinguishing changes in titles group even better.
 656  
  *
 657  
  * Revision 1.13  2002/03/05 23:18:28  mpowers
 658  
  * Added documentation.
 659  
  * Added isSelectionPaintedImmediate and isSelectionTracking attributes
 660  
  * to TableAssociation.
 661  
  * Added getTableAssociation to TableColumnAssociation.
 662  
  *
 663  
  * Revision 1.12  2002/03/04 22:11:43  mpowers
 664  
  * Darkened the sort indicator to better differentiate the first sort.
 665  
  *
 666  
  * Revision 1.11  2002/03/04 03:58:17  mpowers
 667  
  * Refined table header click behavior.
 668  
  *
 669  
  * Revision 1.10  2002/03/01 15:42:00  mpowers
 670  
  * Table column headers now always show their sort indicator.
 671  
  * A third table-column click clears the sort for that column.
 672  
  *
 673  
  * Revision 1.9  2002/02/28 23:01:39  mpowers
 674  
  * TableColumnAssociations add and remove themselves from the TableAssociation
 675  
  * when their connection is established and broken respectively.
 676  
  * TableAssociations now break connection if they have no column associations.
 677  
  *
 678  
  * Revision 1.8  2001/06/05 16:03:56  mpowers
 679  
  * Flipped the triangle to be consistent with Aqua.
 680  
  *
 681  
  * Revision 1.7  2001/03/09 22:09:22  mpowers
 682  
  * Now better handling jdk1.1 for rendering the column header.
 683  
  *
 684  
  * Revision 1.6  2001/02/17 16:52:05  mpowers
 685  
  * Changes in imports to support building with jdk1.1 collections.
 686  
  *
 687  
  * Revision 1.5  2001/01/12 19:11:56  mpowers
 688  
  * Fixed table column click sorting.
 689  
  *
 690  
  * Revision 1.4  2001/01/12 17:20:30  mpowers
 691  
  * Moved EOSortOrdering creation to ColumnAssociation.
 692  
  *
 693  
  * Revision 1.3  2001/01/11 21:55:57  mpowers
 694  
  * Implemented sort indicator for table column headers.
 695  
  *
 696  
  * Revision 1.2  2001/01/11 20:34:26  mpowers
 697  
  * Implemented EOSortOrdering and added support in framework.
 698  
  * Added header-click to sort table columns.
 699  
  *
 700  
  * Revision 1.1.1.1  2000/12/21 15:49:03  mpowers
 701  
  * Contributing wotonomy.
 702  
  *
 703  
  * Revision 1.5  2000/12/20 16:25:41  michael
 704  
  * Added log to all files.
 705  
  *
 706  
  *
 707  
  */
 708