home *** CD-ROM | disk | FTP | other *** search
/ MacWorld UK 2005 May / MW_UK_2005_05.iso / 16 iCal utilities / quickCal.sit / quickCal / quickCal.app / Contents / Resources / RowResizableViewImplementation.h < prev    next >
Encoding:
Text File  |  2005-01-26  |  21.8 KB  |  595 lines

  1. /*
  2.  RowResizableViewImplementation.h
  3.  Written by Evan Jones <ejones@uwaterloo.ca>, 14-11-2002
  4.  http://www.eng.uwaterloo.ca/~ejones/
  5.  
  6.  Released under the GNU LGPL.
  7.  
  8.  That means that you can use this class in open source or commercial products, with the limitation that you must distribute the source code for this class, and any modifications you make. See http://www.gnu.org/ for more information.
  9.  
  10.  IMPORTANT NOTE:
  11.  
  12.  This file is included into both RowResizableTableView.m and RowResizableOutlineView.m. This is because these two classes share the implementation of the methods defined in this file.
  13.  
  14.  Yes, I know that this is an ugly hack, but it is the least ugly hack I could find. It is simple to use and to understand. Search the MacOSX-dev mailing list archives for the thread with the subject "Objective-C Multiple Inheritance Work Arounds?" for a detailed discussion. A short list of stuff I tried or thought about and rejected:
  15.  
  16.  - Hacking the classes so that RowResizableTableView could be both a subclass of NSTableView and NSOutlineView, and then RowResizeableOutlineView became a subclass of "RowResizableTableView-copy".
  17.  - Using the "concrete protocols" library.
  18.  
  19.  
  20.  CHANGES:
  21.  - ejones: 2003-05-28: Merged changes by Florent Pillet <florent.pillet@wanadoo.fr>:
  22.      - fixed setDelegate so that it recalculates the grid once after [super setDelegate]
  23.      - added setIntercellSpacing override to appropriately require a grid recalculation
  24.      - fixed a bug in rectOfRow which could be called for non-existent rows during drag and drop
  25.  */
  26.  
  27. -(id)initWithFrame:(NSRect)frame {
  28.     [super initWithFrame:frame];
  29.     if ( self ) {
  30.         // TODO: I should probably include the "init" code from "commonInitWithCoder" here
  31.     }
  32.     return self;
  33. }
  34.  
  35. - (id) commonInitWithCoder:(NSCoder *)decoder
  36. {
  37.     // Custom initialization: This must be done before calling super's method
  38.     // because that will in turn call recalculateGrid. When we get there, the
  39.     // arrays must exist
  40.     rowHeights = [[NSMutableArray alloc] init];
  41.     rowOrigins = [[NSMutableArray alloc] init];
  42.  
  43.     gridCalculated = NO;
  44.     respondsToWillDisplayCell = NO;
  45.  
  46.     self = [super initWithCoder: decoder];
  47.     if ( self )
  48.         {
  49.         // Now set the cells in all the columns to wrap text
  50.         int i = 0;
  51.         for ( i = 0; i < [[self tableColumns] count]; ++ i )
  52.             {
  53.             [[[[self tableColumns] objectAtIndex: i] dataCell] setWraps: YES];
  54.             }
  55.         }
  56.     else
  57.         {
  58.          // A problem occured during initialization, so we need to release the arrays
  59.         [rowHeights release];
  60.         [rowOrigins release];
  61.         }
  62.     return self;
  63. }
  64.  
  65. // Properly release all instance variables
  66. - (void) dealloc
  67. {
  68.     [rowHeights release];
  69.     [rowOrigins release];
  70.  
  71.     [super dealloc];
  72. }
  73.  
  74. // Override setDelegate to recalculate the grid if we need to
  75. - (void) setDelegate: (id) obj
  76. {
  77.     // Gross hack to allow code sharing between RowResizable*Views
  78.     BOOL doesRespond = [obj respondsToSelector:ROW_RESIZABLE_WILL_DISPLAY_CELL_SELECTOR];
  79.     BOOL recalc = NO;
  80.  
  81.     // If the delegate is different and it either responds to willDisplayCell, or else if the old delegate did,
  82.     // we need to recalculate the grid. But we can do this ONLY after setting the delegate
  83.     // which may implement the willDisplayCell selector
  84.     if ( obj != [self delegate] && ( doesRespond || respondsToWillDisplayCell ) )
  85.         {
  86.         gridCalculated = YES;    // don't want to be recalculated now
  87.         recalc = YES;            // .. but just after we set the delegate
  88.         }
  89.  
  90.     respondsToWillDisplayCell = doesRespond;
  91.  
  92.     [super setDelegate: obj];
  93.     
  94.     if (recalc)
  95.         {
  96.         gridCalculated = NO;
  97.         [self tile];
  98.         }
  99. }
  100.  
  101. // Override tile to recalculated the grid
  102. - (void) tile
  103. {
  104.     if ( ! gridCalculated )
  105.         {
  106.         // Avoid infinite loops!
  107.         [self recalculateGrid];
  108.         }
  109.     [super tile];
  110. }
  111.  
  112. // Override viewDidEndLiveResize to invalidate the grid, since the column widths
  113. // may have changed.
  114. // OPTIMISATION: Have recalculateGrid check to see if/what column was actually resized?
  115. - (void) viewDidEndLiveResize
  116. {
  117.     gridCalculated = NO;
  118.     [super viewDidEndLiveResize];
  119.  
  120.     [self tile];
  121. }
  122.  
  123. // Monitor columnDidResize to invalidate the grid, since the column widths
  124. // have changed.
  125. // OPTIMISATION: Intercept the super class's "live" column resize messages to avoid tiling twice?
  126. - (void)columnDidResize:(NSNotification*)aNotification
  127. {
  128.     //NSLog( @"tableViewColumnDidResize" );
  129.     gridCalculated = NO;
  130.  
  131.     [self tile];
  132. }
  133.  
  134. - (void)textDidEndEditing:(NSNotification *)aNotification
  135. {
  136.     //NSLog( @"ended editing" );
  137.     int editedRow = [self editedRow];
  138.     [super textDidEndEditing: aNotification];
  139.     [self setHeightOfRow: editedRow toHeight: [self maxHeightInRow: editedRow]];
  140. }
  141.  
  142. // NSTableView is NSText's delegate when editing
  143. - (void)textDidChange:(NSNotification *)notification
  144. {
  145.     //NSLog( @"textDidChange" );
  146.     int editedRow = [self editedRow];
  147.     int editedColumn = [self editedColumn];
  148.     //NSCell* cell = [self cellForRow: editedRow column:[self editedColumn]];
  149.  
  150.     //float intercellHeight = [self intercellSpacing].height;
  151.  
  152.     // Set the cell's value to the new string
  153.     // NOTE: I don't need to retain/release here, because i'm going to reset the value back again
  154.     // before the stack clears and the objects are freed. I know, not pretty, but hey, it works
  155.     //id obj = [cell objectValue];
  156.     //[cell setObjectValue: [[notification object] string]];
  157.  
  158.     // Now ask for the cell's height
  159.     //float columnWidth = [[[self tableColumns] objectAtIndex: [self editedColumn]] width];
  160.     float cellHeight = [self findHeightForColumn: editedColumn row: editedRow withValue: [[notification object] string]];
  161.  
  162.     // if the height does not equal to the current height, we have to ask the whole row how tall they are
  163.     // to determine if this cell is possibly the tallest in the table
  164.     float currentRowHeight = [[rowHeights objectAtIndex: editedRow] floatValue];
  165.     if ( cellHeight != currentRowHeight )
  166.         {
  167.         //NSLog( @"cell height = %f, row height = %f", cellHeight, currentRowHeight );
  168.         float rowHeight = [self maxHeightInRow: editedRow];
  169.         if ( rowHeight > cellHeight ) cellHeight = rowHeight;
  170.  
  171.         // If now after we have asked all the cells how tall they are, we still have a different height,
  172.         // we need to adjust all the row origins and then refresh the display
  173.         if ( cellHeight != currentRowHeight )
  174.             {
  175.             // Set the new row height
  176.             [self setHeightOfRow: editedRow toHeight: cellHeight];
  177.             }
  178.         }
  179.  
  180.     // Set the cell's value back
  181.     //[cell setObjectValue: obj];
  182. }
  183.  
  184. // Override rectOfRow so we can substitute our custom row rectangles
  185. - (NSRect)rectOfRow:(int)row
  186. {
  187.     // Special case for row<0, may highlight the whole table. Used by Drag and drop.
  188.     if (row < 0)
  189.         return [super rectOfRow:row];
  190.     
  191.     // Hmm, it turns out that this can sometimes get called before tile gets called, meaning that we
  192.     // need to recalculate the grid immediately. I wish there was some better place where I could put
  193.     // this call, so it gets called ONCE and ONLY ONCE when it needs to be. There must be logic in
  194.     // the NSTableView which does this already...
  195.     //NSAssert( gridCalculated, @"Logic error: grid should be calculated by this point" );
  196.     if ( ! gridCalculated )
  197.         [self recalculateGrid];
  198.  
  199.     NSRect frame = [self bounds];
  200.     int numRows = [self numberOfRows];
  201.     float y = 0;
  202.     float height = 0;
  203.  
  204.     // during DnD, we can get called to obtain the rect for a row pas
  205.     // the table bounds. Gracefully handle this case.
  206.     if (row >= numRows)
  207.         {
  208.         if (numRows == 0)
  209.             return NSZeroRect;
  210.  
  211.         height = [[rowHeights objectAtIndex:(numRows-1)] floatValue];
  212.         y = [[rowOrigins objectAtIndex:(numRows-1)] floatValue] + height;
  213.         }
  214.     else
  215.         {
  216.         height = [[rowHeights objectAtIndex: row] floatValue] + [self intercellSpacing].height;
  217.         y = [[rowOrigins objectAtIndex: row] floatValue];
  218.         }
  219.  
  220.     NSRect rowRect = NSMakeRect( 0, y, frame.size.width, height );
  221.  
  222.     /* FIXED HEIGHT TEST:
  223.         NSRect superRect = [super rectOfRow:row];
  224.     //NSLog( @"super's %d: (%f, %f) : %f X %f", row, DISPLAY_RECT( superRect ) );
  225.     //NSLog( @" self's %d: (%f, %f) : %f X %f", row, DISPLAY_RECT( rowRect ) );
  226.     NSAssert( NSEqualRects( superRect, rowRect ), @"super calculated a different rowRect" );
  227.     */
  228.  
  229.     return rowRect;
  230. }
  231.  
  232. // Override rowAtPoint so we can substitute our custom row rectangles
  233. - (int)rowAtPoint:(NSPoint)point
  234. {
  235.     //NSLog( @"rowAtPoint" );
  236.  
  237.     float intercellHeight = [self intercellSpacing].height;
  238.  
  239.     // WARNING: This first loop is copied from rowsInRect. If making changes here, be sure to make changes there
  240.     // or else link the two in some way
  241.     int i = 0;
  242.     while ( i < [rowOrigins count] )
  243.         {
  244.         float rowBottom = [[rowOrigins objectAtIndex: i] floatValue] + [[rowHeights objectAtIndex: i] floatValue] + intercellHeight;
  245.  
  246.         // if the row bottom is GREATER THAN than the bottom of the rect, we want this row
  247.         // if it is EQUAL, we don't want it
  248.         if ( rowBottom > point.y )
  249.             {
  250.             break;
  251.             }
  252.         ++ i;
  253.         }
  254.  
  255.     if ( i == [rowOrigins count] ) i = -1;
  256.  
  257.     //NSAssert( [super rowAtPoint: point] == i, @"Super's rowAtPoint does not match!" );
  258.     return i;
  259. }
  260.  
  261. // Override rowsInRect so we can substitute our custom row rectangles
  262. - (NSRange)rowsInRect:(NSRect)rect
  263. {
  264.     //NSLog( @"rowsInRect" );
  265.  
  266.     // If there are no rows, we can't be in a rect
  267.     if ( [rowHeights count] == 0 )
  268.         return NSMakeRange( 0, 0 );
  269.  
  270.     float rowBottom = 0.0;
  271.  
  272.     float intercellHeight = [self intercellSpacing].height;
  273.  
  274.     // Find the first row with a bottom GREATER THAN OR EQUAL TO the comparison height of the rectangle we are given
  275.     // WARNING: This first loop is copied into rowAtPoint. If making changes here, be sure to make changes there
  276.     // or else link the two in some way
  277.     int firstIndex = -1;
  278.     int i = 0;
  279.     while ( i < [rowOrigins count] )
  280.         {
  281.         rowBottom = [[rowOrigins objectAtIndex: i] floatValue] + [[rowHeights objectAtIndex: i] floatValue] + intercellHeight;
  282.  
  283.         //NSLog( @"%d: comparing %f to %f", i, rowBottom, rect.origin.y );
  284.         // if the row bottom is GREATER THAN than the bottom of the rect, we want this row
  285.         // if it is EQUAL, we don't want it
  286.         if ( rowBottom > rect.origin.y )
  287.             {
  288.             firstIndex = i;
  289.             break;
  290.             }
  291.         ++ i;
  292.         }
  293.  
  294.     // Search for the last index
  295.     // NOTE: No need to test the last index. Small optimization
  296.     while ( i < [rowOrigins count] - 1)
  297.         {
  298.         //NSLog( @"%d: comparing %f to %f", i, rowBottom, rect.origin.y + rect.size.height );
  299.         // if the row bottom is GREATER THAN OR EQUAL TO than the bottom of the rect, we want this row
  300.         if ( rowBottom >= rect.origin.y + rect.size.height )
  301.             {
  302.             break;
  303.             }
  304.  
  305.         // NOTE: We calculate the rowBottom at the END of the loop.
  306.         // this permits us to retest the row that is the first index without having to calculate it again
  307.         ++ i;
  308.         rowBottom = [[rowOrigins objectAtIndex: i] floatValue] + [[rowHeights objectAtIndex: i] floatValue] + intercellHeight;
  309.         }
  310.  
  311.     // If this occurs, that means the rectangle contains NO rows
  312.     if ( i >= [rowOrigins count] )
  313.         {
  314.         firstIndex = 0;
  315.         i = -1;
  316.         }
  317.  
  318.     NSAssert( i < (signed) [rowOrigins count], @"Error with indexes" );
  319.  
  320.     NSRange rowRange = NSMakeRange( firstIndex, i - firstIndex + 1 );
  321.  
  322.     /* FIXED HEIGHT TEST:
  323.         NSRange superRange = [super rowsInRect:rect];
  324.     //NSLog( @"For rect: (%f, %f) : %f X %f", DISPLAY_RECT( rect ) );
  325.     //NSLog( @"super: index: %d length: %d", superRange.location, superRange.length );
  326.     //NSLog( @" self: index: %d length: %d", rowRange.location, rowRange.length );
  327.     NSAssert( NSEqualRanges( superRange, rowRange ), @"super calculated different rows" );
  328.     */
  329.  
  330.     return rowRange;
  331. }
  332.  
  333. // Override reloadData so we can refresh the grid sizes
  334. - (void) reloadData
  335. {
  336.     //NSLog( @"Data reloaded: forgetting grid information." );
  337.     // By setting this to NO, we will recalculate the grid information when we need it
  338.     gridCalculated = NO;
  339.     //[self recalculateGrid];
  340.     [super reloadData];
  341. }
  342.  
  343. // Override setDataSource so we can forget the grid sizes if a new data source is being set
  344. - (void)setDataSource:(id)source
  345. {
  346.     if ( [super dataSource] != source )
  347.         gridCalculated = NO;
  348.  
  349.     [super setDataSource: source];
  350. }
  351.  
  352. // Override setIntercellSpaceing to recalculate the grid
  353. - (void)setIntercellSpacing:(NSSize)aSize
  354. {
  355.     if (!NSEqualSizes(aSize, [self intercellSpacing]))
  356.         gridCalculated = NO;
  357.     [super setIntercellSpacing:aSize];
  358. }
  359.  
  360. // Override addColumn to set the cell to wrap text by default and to recalculate table heights
  361. - (void) addTableColumn: (NSTableColumn*) col
  362. {
  363.     //NSLog( @"addColumn" );
  364.     [[col dataCell] setWraps: YES];
  365.     //[self recalculateGrid];
  366.     gridCalculated = NO;
  367.     [super addTableColumn: col];
  368. }
  369.  
  370. - (void) setHeightOfRow: (int) row toHeight: (float)height
  371. {
  372.     NSAssert( row >= 0 && row < [rowHeights count], @"Invalid row index" );
  373.     NSAssert( height > 0, @"Invalid height" );
  374.  
  375.     float difference = height - [[rowHeights objectAtIndex: row] floatValue];
  376.  
  377.     // If the height actually changed, go and adjust all the row origins
  378.     if ( difference != 0.0 )
  379.         {
  380.         [rowHeights replaceObjectAtIndex: row withObject: [NSNumber numberWithFloat: height]];
  381.  
  382.         int i = 0;
  383.         for ( i = row + 1; i < [rowHeights count]; ++ i )
  384.             {
  385.             float newValue = [[rowOrigins objectAtIndex: i] floatValue] + difference;
  386.             [rowOrigins replaceObjectAtIndex: i withObject: [NSNumber numberWithFloat: newValue]];
  387.             }
  388.  
  389.         NSText* editor = [self currentEditor];
  390.         // If we are editing a cell ...
  391.         if ( editor != nil )
  392.             {
  393.             int editedRow = [self editedRow];
  394.             NSView* superview = [editor superview];
  395.  
  396.             // And the edited cell just changed sizes: resize the editor
  397.             if ( row == editedRow )
  398.                 {
  399.                 // Now we need to also adjust the edit control's size. There are two ways of doing this
  400.                 // 1. The "correct" way: The notification object is the NSText object that is being edited.
  401.                 // it's superview is an _NSKeyboardFocusClipView which supports a method called "_adjustFocusRingSize".
  402.                 // We call that method and life is good.
  403.                 // 2. The hack, but the "API" way: Store the selection, stop editing, resume editing
  404.                 // We support both ways in case the API changes.
  405.                 if ( [superview respondsToSelector: @selector(_adjustFocusRingSize:) ] )
  406.                     {
  407.                     NSRect frame = [superview frame];
  408.                     //NSLog( @"frame: (%f, %f) : %f X %f", DISPLAY_RECT( frame ) );
  409.                     frame.size.height += difference;
  410.                     [superview setFrame: frame];
  411.  
  412.                     [(_NSKeyboardFocusClipView*)superview _adjustFocusRingSize: NSMakeSize( 0.0, difference )];
  413.                     //[superview _setKeyboardFocusRingNeedsDisplay];
  414.                     }
  415.                 // HACK: This is the fallback
  416.                 else
  417.                     {
  418.                     NSLog( @"WARNING: The cocoa API has changed. Enabling hacks mode!" );
  419.                     NSRange selection = [editor selectedRange];
  420.                     [[self window] endEditingFor:nil];
  421.                     [self editColumn: [self editedColumn] row: [self editedRow] withEvent: nil select: NO];
  422.                     [editor setSelectedRange: selection];
  423.                     }
  424.                 }
  425.             // otherwise if we are editing a row that is after the resized: reposition the editor
  426.             else if ( editor != nil && row < editedRow )
  427.                 {
  428.                 // Same hacks as above
  429.                 if ( [superview respondsToSelector: @selector(_adjustFocusRingSize:) ] )
  430.                     {
  431.                     NSRect frame = [superview frame];
  432.                     //NSLog( @"frame: (%f, %f) : %f X %f", DISPLAY_RECT( frame ) );
  433.                     //NSLog( @"  row: %f", [[rowOrigins objectAtIndex: editedRow] floatValue] );
  434.                     frame.origin.y = [[rowOrigins objectAtIndex: editedRow] floatValue];
  435.                     [superview setFrame: frame];
  436.  
  437.                     // Strange: we don't need to adjust the focus ring, but we do if we make the editor larger?
  438.                     //[(_NSKeyboardFocusClipView*)superview _adjustFocusRingLocation: NSMakePoint( 0.0, difference )];
  439.                     }
  440.                 else
  441.                     {
  442.                     NSLog( @"WARNING: The cocoa API has changed. Enabling hacks mode!" );
  443.                     NSRange selection = [editor selectedRange];
  444.                     [[self window] endEditingFor:nil];
  445.                     [self editColumn: [self editedColumn] row: [self editedRow] withEvent: nil select: NO];
  446.                     [editor setSelectedRange: selection];
  447.                     }
  448.                 }
  449.             }
  450.  
  451.         // Notify ourselves that we need to change our frame's height
  452.         // This will "do the right thing" to figure out how big it needs to be
  453.         [self tile];
  454.         }
  455.     // Otherwise, nothing changed!
  456.     else
  457.         {
  458.         //NSLog( @"setHeightOfRow called, but nothing changed!" );
  459.         }
  460. }
  461.  
  462. - (float) findHeightForColumn: (int) column row: (int) row withValue: (id) value
  463. {
  464.     NSAssert( column >= 0 && column < [[self tableColumns] count], @"Invalid arguments" );
  465.  
  466.     NSTableColumn* tabCol = [[self tableColumns] objectAtIndex:column];
  467.     NSCell* dataCell = [self cellForRow:row column:column];
  468.  
  469.     if ( value == nil )
  470.         value = [self getValueForTableColumn: tabCol row: row];
  471.  
  472.     // Grab the inital value so we don't lose the reference. I cheat here: I should
  473.     // probably do a retain and a release here, but because the reference gets put back in
  474.     // the cell before the stack gets cleared, life is good. I think.
  475.     id originalValue = [dataCell objectValue];
  476.     [dataCell setObjectValue: value];
  477.  
  478.     [self willDisplayCell: dataCell forTableColumn: tabCol row: row];
  479.  
  480.     NSRect rect = [self frameOfCellAtColumn: column row: row];
  481.     rect.size.height = 1000.0;
  482.     float cellHeight = [dataCell cellSizeForBounds: rect].height;
  483.  
  484.     [dataCell setObjectValue: originalValue];
  485.  
  486.     return cellHeight;
  487. }
  488.  
  489. - (void) recalculateGrid
  490. {
  491.     //NSLog( @"recalculateGrid" );
  492.     int i = 0;
  493.  
  494.     // We will be up to date shortly
  495.     gridCalculated = YES;
  496.  
  497.     BOOL somethingChanged = NO;
  498.  
  499.     // Loop through all of the cells, asking them for their height (for text that has wrapped)
  500.     float totalHeight = 0.0;
  501.     float intercellHeight = [self intercellSpacing].height;
  502.     int numRows = [self numberOfRows];
  503.     for ( i = 0; i < numRows; ++ i )
  504.         {
  505.         float rowHeight = [self maxHeightInRow: i];
  506.  
  507.         // If the row already exists ...
  508.         if ( [rowHeights count] > i )
  509.             {
  510.             // And the height has changed ...
  511.             if ( [[rowHeights objectAtIndex: i] floatValue] != rowHeight )
  512.                 {
  513.                 // Update the height
  514.                 somethingChanged = YES;
  515.                 [rowHeights replaceObjectAtIndex: i withObject: [NSNumber numberWithFloat: rowHeight]];
  516.                 }
  517.             }
  518.         // Otherwise, add the new height to the array
  519.         else
  520.             {
  521.             somethingChanged = YES;
  522.             [rowHeights addObject: [NSNumber numberWithFloat: rowHeight]];
  523.             }
  524.  
  525.         if ( [rowOrigins count] > i ) [rowOrigins replaceObjectAtIndex: i withObject: [NSNumber numberWithFloat: totalHeight]];
  526.         else [rowOrigins addObject: [NSNumber numberWithFloat: totalHeight]];
  527.  
  528.         // Adjust the height to accomodate the cell and the intercell height
  529.         totalHeight += rowHeight + intercellHeight;
  530.         }
  531.  
  532.     // Remove any excess rows
  533.     while ( [rowHeights count] > i )
  534.         {
  535.         [rowHeights removeLastObject];
  536.         }
  537.     while ( [rowOrigins count] > i )
  538.         {
  539.         [rowOrigins removeLastObject];
  540.         }
  541.  
  542.     // PHEW! Okay, now we may need to change the frame size to fit the content
  543.     // totalHeight = sum( rowHeights ) + interCellSpacing * numRows
  544.     // NSRect frame = [self frame];
  545.     //float totalHeight = totalRowHeight + [self intercellSpacing].height * [self numberOfRows];
  546.     /*
  547.      if ( frame.size.height != totalHeight )
  548.      {
  549.          NSLog( @"We aren't the correct height (%f instead of %f); resizing", frame.size.height, totalHeight );
  550.          frame.size.height = totalHeight;
  551.          [self setFrame: frame];
  552.  
  553.          // Notify the scrollview that we changed our own size
  554.          // TODO: Is this correct?
  555.          [[[self superview] superview] reflectScrolledClipView: (NSClipView*) [self superview]];
  556.          return;
  557.      }
  558.      */
  559.  
  560.     // CHANGED: We now call recalculateGrid directly from tile, if it is needed.
  561.     // everything else just sets gridCalculated = NO correctly, and all is well
  562.     // If we need to, tell NSTableView that it needs to repaint
  563.     /*if ( somethingChanged )
  564.         {
  565.         //NSLog( @"Recalculating grid determined that we need to repaint" );
  566.         //[self setNeedsDisplayInRect: [self visibleRect]];
  567.         [self tile];
  568.         }
  569.     */
  570.  
  571.     NSAssert( [rowHeights count] == [self numberOfRows] && [rowOrigins count] == [self numberOfRows], @"Inconsistent row numbers!" );
  572. }
  573.  
  574. // Determine maximum height of any cell in 'row'
  575. - (float)maxHeightInRow:(int)row
  576. {
  577.     float maxHeight = 0.0;
  578.  
  579.     int colIndex = 0;
  580.     for ( colIndex = 0; colIndex < [self numberOfColumns]; colIndex++ )
  581.         {
  582.         float colHeight = [self findHeightForColumn: colIndex row: row withValue: nil];
  583.         if ( colHeight > maxHeight ) maxHeight = colHeight;
  584.         }
  585.     return maxHeight;
  586. }
  587.  
  588. - (NSCell*)cellForRow:(int)row column:(int)col
  589. {
  590.     NSArray *tableCols = [self tableColumns];
  591.     NSTableColumn* tabCol = [tableCols objectAtIndex:col];
  592.     return [tabCol dataCellForRow:row];
  593. }
  594.  
  595.