API 0.9.5
AppKit/CPTableView.j
Go to the documentation of this file.
00001 /*
00002  * CPTableView.j
00003  * AppKit
00004  *
00005  * Created by Francisco Tolmasky.
00006  * Copyright 2009, 280 North, Inc.
00007  *
00008  * This library is free software; you can redistribute it and/or
00009  * modify it under the terms of the GNU Lesser General Public
00010  * License as published by the Free Software Foundation; either
00011  * version 2.1 of the License, or (at your option) any later version.
00012  *
00013  * This library is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00016  * Lesser General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU Lesser General Public
00019  * License along with this library; if not, write to the Free Software
00020  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
00021  */
00022 
00023 
00024 
00025 
00026 
00027 CPTableViewColumnDidMoveNotification        = @"CPTableViewColumnDidMoveNotification";
00028 CPTableViewColumnDidResizeNotification      = @"CPTableViewColumnDidResizeNotification";
00029 CPTableViewSelectionDidChangeNotification   = @"CPTableViewSelectionDidChangeNotification";
00030 CPTableViewSelectionIsChangingNotification  = @"CPTableViewSelectionIsChangingNotification";
00031 
00032 var CPTableViewDataSource_numberOfRowsInTableView_                                                      = 1 << 0,
00033     CPTableViewDataSource_tableView_objectValueForTableColumn_row_                                      = 1 << 1,
00034     CPTableViewDataSource_tableView_setObjectValue_forTableColumn_row_                                  = 1 << 2,
00035     CPTableViewDataSource_tableView_acceptDrop_row_dropOperation_                                       = 1 << 3,
00036     CPTableViewDataSource_tableView_namesOfPromisedFilesDroppedAtDestination_forDraggedRowsWithIndexes_ = 1 << 4,
00037     CPTableViewDataSource_tableView_validateDrop_proposedRow_proposedDropOperation_                     = 1 << 5,
00038     CPTableViewDataSource_tableView_writeRowsWithIndexes_toPasteboard_                                  = 1 << 6,
00039 
00040     CPTableViewDataSource_tableView_sortDescriptorsDidChange_                                           = 1 << 7;
00041 
00042 var CPTableViewDelegate_selectionShouldChangeInTableView_                                               = 1 << 0,
00043     CPTableViewDelegate_tableView_dataViewForTableColumn_row_                                           = 1 << 1,
00044     CPTableViewDelegate_tableView_didClickTableColumn_                                                  = 1 << 2,
00045     CPTableViewDelegate_tableView_didDragTableColumn_                                                   = 1 << 3,
00046     CPTableViewDelegate_tableView_heightOfRow_                                                          = 1 << 4,
00047     CPTableViewDelegate_tableView_isGroupRow_                                                           = 1 << 5,
00048     CPTableViewDelegate_tableView_mouseDownInHeaderOfTableColumn_                                       = 1 << 6,
00049     CPTableViewDelegate_tableView_nextTypeSelectMatchFromRow_toRow_forString_                           = 1 << 7,
00050     CPTableViewDelegate_tableView_selectionIndexesForProposedSelection_                                 = 1 << 8,
00051     CPTableViewDelegate_tableView_shouldEditTableColumn_row_                                            = 1 << 9,
00052     CPTableViewDelegate_tableView_shouldSelectRow_                                                      = 1 << 10,
00053     CPTableViewDelegate_tableView_shouldSelectTableColumn_                                              = 1 << 11,
00054     CPTableViewDelegate_tableView_shouldShowViewExpansionForTableColumn_row_                            = 1 << 12,
00055     CPTableViewDelegate_tableView_shouldTrackView_forTableColumn_row_                                   = 1 << 13,
00056     CPTableViewDelegate_tableView_shouldTypeSelectForEvent_withCurrentSearchString_                     = 1 << 14,
00057     CPTableViewDelegate_tableView_toolTipForView_rect_tableColumn_row_mouseLocation_                    = 1 << 15,
00058     CPTableViewDelegate_tableView_typeSelectStringForTableColumn_row_                                   = 1 << 16,
00059     CPTableViewDelegate_tableView_willDisplayView_forTableColumn_row_                                   = 1 << 17,
00060     CPTableViewDelegate_tableViewSelectionDidChange_                                                    = 1 << 18,
00061     CPTableViewDelegate_tableViewSelectionIsChanging_                                                   = 1 << 19,
00062     CPTableViewDelegate_tableViewMenuForTableColumn_Row_                                                = 1 << 20;
00063 
00064 //CPTableViewDraggingDestinationFeedbackStyles
00065 CPTableViewDraggingDestinationFeedbackStyleNone = -1;
00066 CPTableViewDraggingDestinationFeedbackStyleRegular = 0;
00067 CPTableViewDraggingDestinationFeedbackStyleSourceList = 1;
00068 
00069 //CPTableViewDropOperations
00070 CPTableViewDropOn = 0;
00071 CPTableViewDropAbove = 1;
00072 
00073 CPSourceListGradient = "CPSourceListGradient";
00074 CPSourceListTopLineColor = "CPSourceListTopLineColor";
00075 CPSourceListBottomLineColor = "CPSourceListBottomLineColor";
00076 
00077 // TODO: add docs
00078 
00079 CPTableViewSelectionHighlightStyleNone = -1;
00080 CPTableViewSelectionHighlightStyleRegular = 0;
00081 CPTableViewSelectionHighlightStyleSourceList = 1;
00082 
00083 CPTableViewGridNone                    = 0;
00084 CPTableViewSolidVerticalGridLineMask   = 1 << 0;
00085 CPTableViewSolidHorizontalGridLineMask = 1 << 1;
00086 
00087 CPTableViewNoColumnAutoresizing = 0;
00088 CPTableViewUniformColumnAutoresizingStyle = 1; // FIX ME: This is FUBAR
00089 CPTableViewSequentialColumnAutoresizingStyle = 2;
00090 CPTableViewReverseSequentialColumnAutoresizingStyle = 3;
00091 CPTableViewLastColumnOnlyAutoresizingStyle = 4;
00092 CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
00093 
00094 #define NUMBER_OF_COLUMNS() (_tableColumns.length)
00095 #define UPDATE_COLUMN_RANGES_IF_NECESSARY() if (_dirtyTableColumnRangeIndex !== CPNotFound) [self _recalculateTableColumnRanges];
00096 
00097 
00098 @implementation _CPTableDrawView : CPView
00099 {
00100     CPTableView _tableView;
00101 }
00102 
00103 - (id)initWithTableView:(CPTableView)aTableView
00104 {
00105     self = [super init];
00106 
00107     if (self)
00108         _tableView = aTableView;
00109 
00110     return self;
00111 }
00112 
00113 - (void)drawRect:(CGRect)aRect
00114 {
00115     var frame = [self frame],
00116         context = [[CPGraphicsContext currentContext] graphicsPort];
00117 
00118     CGContextTranslateCTM(context, -_CGRectGetMinX(frame), -_CGRectGetMinY(frame));
00119 
00120     [_tableView _drawRect:aRect];
00121 }
00122 
00123 @end
00124 
00148 @implementation CPTableView : CPControl
00149 {
00150     id                  _dataSource;
00151     CPInteger           _implementedDataSourceMethods;
00152 
00153     id                  _delegate;
00154     CPInteger           _implementedDelegateMethods;
00155 
00156     CPArray             _tableColumns;
00157     CPArray             _tableColumnRanges;
00158     CPInteger           _dirtyTableColumnRangeIndex;
00159     CPInteger           _numberOfHiddenColumns;
00160 
00161     BOOL                _reloadAllRows;
00162     Object              _objectValues;
00163 
00164     CGRect              _exposedRect;
00165     CPIndexSet          _exposedRows;
00166     CPIndexSet          _exposedColumns;
00167 
00168     Object              _dataViewsForTableColumns;
00169     Object              _cachedDataViews;
00170 
00171     //Configuring Behavior
00172     BOOL                _allowsColumnReordering;
00173     BOOL                _allowsColumnResizing;
00174     BOOL                _allowsColumnSelection;
00175     BOOL                _allowsMultipleSelection;
00176     BOOL                _allowsEmptySelection;
00177 
00178     CPArray             _sortDescriptors;
00179 
00180     //Setting Display Attributes
00181     CGSize              _intercellSpacing;
00182     float               _rowHeight;
00183 
00184     BOOL                _usesAlternatingRowBackgroundColors;
00185     CPArray             _alternatingRowBackgroundColors;
00186 
00187     unsigned            _selectionHighlightStyle;
00188     CPTableColumn       _currentHighlightedTableColumn;
00189     unsigned            _gridStyleMask;
00190 
00191     unsigned            _numberOfRows;
00192     CPIndexSet          _groupRows;
00193 
00194     CPArray             _cachedRowHeights;
00195 
00196     // Persistence
00197     CPString            _autosaveName;
00198     BOOL                _autosaveTableColumns;
00199 
00200     CPTableHeaderView   _headerView;
00201     _CPCornerView       _cornerView;
00202 
00203     CPIndexSet          _selectedColumnIndexes;
00204     CPIndexSet          _selectedRowIndexes;
00205     CPInteger           _selectionAnchorRow;
00206     CPInteger           _lastSelectedRow;
00207     CPIndexSet          _previouslySelectedRowIndexes;
00208     CGPoint             _startTrackingPoint;
00209     CPDate              _startTrackingTimestamp;
00210     BOOL                _trackingPointMovedOutOfClickSlop;
00211     CGPoint             _editingCellIndex;
00212 
00213     _CPTableDrawView    _tableDrawView;
00214 
00215     SEL                 _doubleAction;
00216     CPInteger           _clickedRow;
00217     unsigned            _columnAutoResizingStyle;
00218 
00219     int                 _lastTrackedRowIndex;
00220     CGPoint             _originalMouseDownPoint;
00221     BOOL                _verticalMotionCanDrag;
00222     unsigned            _destinationDragStyle;
00223     BOOL                _isSelectingSession;
00224     CPIndexSet          _draggedRowIndexes;
00225     BOOL                _wasSelectionBroken;
00226 
00227     _CPDropOperationDrawingView _dropOperationFeedbackView;
00228     CPDragOperation     _dragOperationDefaultMask;
00229     int                 _retargetedDropRow;
00230     CPDragOperation     _retargetedDropOperation;
00231 
00232     BOOL                _disableAutomaticResizing;
00233     BOOL                _lastColumnShouldSnap;
00234     BOOL                _implementsCustomDrawRow;
00235 
00236     CPTableColumn       _draggedColumn;
00237     CPArray             _differedColumnDataToRemove;
00238 }
00239 
00243 + (CPString)defaultThemeClass
00244 {
00245     return @"tableview";
00246 }
00247 
00251 + (id)themeAttributes
00252 {
00253     return [CPDictionary dictionaryWithObjects:[[CPNull null], [CPNull null], [CPNull null], [CPNull null], [CPNull null], [CPNull null], [CPNull null]]
00254                                        forKeys:["alternating-row-colors", "grid-color", "highlighted-grid-color", "selection-color", "sourcelist-selection-color", "sort-image", "sort-image-reversed"]];
00255 }
00256 
00257 - (id)initWithFrame:(CGRect)aFrame
00258 {
00259     self = [super initWithFrame:aFrame];
00260 
00261     if (self)
00262     {
00263         //Configuring Behavior
00264         _allowsColumnReordering = YES;
00265         _allowsColumnResizing = YES;
00266         _allowsMultipleSelection = NO;
00267         _allowsEmptySelection = YES;
00268         _allowsColumnSelection = NO;
00269         _disableAutomaticResizing = NO;
00270 
00271         //Setting Display Attributes
00272         _selectionHighlightStyle = CPTableViewSelectionHighlightStyleRegular;
00273 
00274         [self setUsesAlternatingRowBackgroundColors:NO];
00275         [self setAlternatingRowBackgroundColors:
00276             [[CPColor whiteColor], [CPColor colorWithRed:245.0 / 255.0 green:249.0 / 255.0 blue:252.0 / 255.0 alpha:1.0]]];
00277 
00278         _tableColumns = [];
00279         _tableColumnRanges = [];
00280         _dirtyTableColumnRangeIndex = CPNotFound;
00281         _numberOfHiddenColumns = 0;
00282 
00283         _intercellSpacing = _CGSizeMake(3.0, 2.0);
00284         _rowHeight = 23.0;
00285 
00286         [self setGridColor:[CPColor colorWithHexString:@"dce0e2"]];
00287         [self setGridStyleMask:CPTableViewGridNone];
00288 
00289         [self setHeaderView:[[CPTableHeaderView alloc] initWithFrame:_CGRectMake(0, 0, [self bounds].size.width, _rowHeight)]];
00290         [self setCornerView:[[_CPCornerView alloc] initWithFrame:_CGRectMake(0, 0, [CPScroller scrollerWidth], _CGRectGetHeight([_headerView frame]))]];
00291 
00292         _currentHighlightedTableColumn = nil;
00293 
00294         _draggedRowIndexes = [CPIndexSet indexSet];
00295         _verticalMotionCanDrag = YES;
00296         _isSelectingSession = NO;
00297         _retargetedDropRow = nil;
00298         _retargetedDropOperation = nil;
00299         _dragOperationDefaultMask = nil;
00300         _destinationDragStyle = CPTableViewDraggingDestinationFeedbackStyleRegular;
00301 
00302         [self setBackgroundColor:[CPColor whiteColor]];
00303         [self _init];
00304     }
00305 
00306     return self;
00307 }
00308 
00309 
00315 - (void)_init
00316 {
00317     _tableViewFlags = 0;
00318     _lastSelectedRow = -1;
00319 
00320     _selectedColumnIndexes = [CPIndexSet indexSet];
00321     _selectedRowIndexes = [CPIndexSet indexSet];
00322 
00323     _dropOperationFeedbackView = [[_CPDropOperationDrawingView alloc] initWithFrame:_CGRectMakeZero()];
00324     [_dropOperationFeedbackView setTableView:self];
00325 
00326     _lastColumnShouldSnap = NO;
00327 
00328     if (!_alternatingRowBackgroundColors)
00329         _alternatingRowBackgroundColors = [[CPColor whiteColor], [CPColor colorWithHexString:@"e4e7ff"]];
00330 
00331     _selectionHighlightColor = [CPColor colorWithHexString:@"5f83b9"];
00332 
00333     _tableColumnRanges = [];
00334     _dirtyTableColumnRangeIndex = 0;
00335     _numberOfHiddenColumns = 0;
00336 
00337     _objectValues = { };
00338     _dataViewsForTableColumns = { };
00339     _dataViews =  [];
00340     _numberOfRows = 0;
00341     _exposedRows = [CPIndexSet indexSet];
00342     _exposedColumns = [CPIndexSet indexSet];
00343     _cachedDataViews = { };
00344     _cachedRowHeights = [];
00345 
00346     _groupRows = [CPIndexSet indexSet];
00347 
00348     _tableDrawView = [[_CPTableDrawView alloc] initWithTableView:self];
00349     [_tableDrawView setBackgroundColor:[CPColor clearColor]];
00350     [self addSubview:_tableDrawView];
00351 
00352     _draggedColumn = nil;
00353 
00354 /*      //gradients for the source list when CPTableView is NOT first responder or the window is NOT key
00355     // FIX ME: we need to actually implement this.
00356     _sourceListInactiveGradient = CGGradientCreateWithColorComponents(CGColorSpaceCreateDeviceRGB(), [168.0/255.0,183.0/255.0,205.0/255.0,1.0,157.0/255.0,174.0/255.0,199.0/255.0,1.0], [0,1], 2);
00357     _sourceListInactiveTopLineColor = [CPColor colorWithCalibratedRed:(173.0/255.0) green:(187.0/255.0) blue:(209.0/255.0) alpha:1.0];
00358     _sourceListInactiveBottomLineColor = [CPColor colorWithCalibratedRed:(150.0/255.0) green:(161.0/255.0) blue:(183.0/255.0) alpha:1.0];*/
00359     _differedColumnDataToRemove = [];
00360     _implementsCustomDrawRow = [self implementsSelector:@selector(drawRow:clipRect:)];
00361 
00362     if (!_sortDescriptors)
00363         _sortDescriptors = [];
00364 }
00365 
00431 - (void)setDataSource:(id)aDataSource
00432 {
00433     if (_dataSource === aDataSource)
00434         return;
00435 
00436     _dataSource = aDataSource;
00437     _implementedDataSourceMethods = 0;
00438 
00439     if (!_dataSource)
00440         return;
00441 
00442     var hasContentBinding = !![self infoForBinding:@"content"];
00443 
00444     if ([_dataSource respondsToSelector:@selector(numberOfRowsInTableView:)])
00445         _implementedDataSourceMethods |= CPTableViewDataSource_numberOfRowsInTableView_;
00446 
00447     if ([_dataSource respondsToSelector:@selector(tableView:objectValueForTableColumn:row:)])
00448         _implementedDataSourceMethods |= CPTableViewDataSource_tableView_objectValueForTableColumn_row_;
00449 
00450     if ([_dataSource respondsToSelector:@selector(tableView:setObjectValue:forTableColumn:row:)])
00451         _implementedDataSourceMethods |= CPTableViewDataSource_tableView_setObjectValue_forTableColumn_row_;
00452 
00453     if ([_dataSource respondsToSelector:@selector(tableView:acceptDrop:row:dropOperation:)])
00454         _implementedDataSourceMethods |= CPTableViewDataSource_tableView_acceptDrop_row_dropOperation_;
00455 
00456     if ([_dataSource respondsToSelector:@selector(tableView:namesOfPromisedFilesDroppedAtDestination:forDraggedRowsWithIndexes:)])
00457         _implementedDataSourceMethods |= CPTableViewDataSource_tableView_namesOfPromisedFilesDroppedAtDestination_forDraggedRowsWithIndexes_;
00458 
00459     if ([_dataSource respondsToSelector:@selector(tableView:validateDrop:proposedRow:proposedDropOperation:)])
00460         _implementedDataSourceMethods |= CPTableViewDataSource_tableView_validateDrop_proposedRow_proposedDropOperation_;
00461 
00462     if ([_dataSource respondsToSelector:@selector(tableView:writeRowsWithIndexes:toPasteboard:)])
00463         _implementedDataSourceMethods |= CPTableViewDataSource_tableView_writeRowsWithIndexes_toPasteboard_;
00464 
00465     if ([_dataSource respondsToSelector:@selector(tableView:sortDescriptorsDidChange:)])
00466         _implementedDataSourceMethods |= CPTableViewDataSource_tableView_sortDescriptorsDidChange_;
00467 
00468     [self reloadData];
00469 }
00470 
00474 - (id)dataSource
00475 {
00476     return _dataSource;
00477 }
00478 
00479 //Loading Data
00480 
00486 - (void)reloadDataForRowIndexes:(CPIndexSet)rowIndexes columnIndexes:(CPIndexSet)columnIndexes
00487 {
00488     [self reloadData];
00489 //    [_previouslyExposedRows removeIndexes:rowIndexes];
00490 //    [_previouslyExposedColumns removeIndexes:columnIndexes];
00491 }
00492 
00496 - (void)reloadData
00497 {
00498     //if (!_dataSource)
00499     //    return;
00500 
00501     _reloadAllRows = YES;
00502     _objectValues = { };
00503     _cachedRowHeights = [];
00504 
00505     // Otherwise, if we have a row marked as group with a
00506     // index greater than the new number or rows
00507     // it keeps the the graphical group style.
00508     [_groupRows removeAllIndexes];
00509 
00510     // This updates the size too.
00511     [self noteNumberOfRowsChanged];
00512 
00513     [self setNeedsLayout];
00514     [self setNeedsDisplay:YES];
00515 }
00516 
00517 //Target-action Behavior
00524 - (void)setDoubleAction:(SEL)anAction
00525 {
00526     _doubleAction = anAction;
00527 }
00528 
00532 - (SEL)doubleAction
00533 {
00534     return _doubleAction;
00535 }
00536 
00537 /*
00538     * - clickedColumn
00539 */
00540 
00544 - (CPInteger)clickedRow
00545 {
00546     return _clickedRow;
00547 }
00548 
00549 //Configuring Behavior
00550 
00554 - (void)setAllowsColumnReordering:(BOOL)shouldAllowColumnReordering
00555 {
00556     _allowsColumnReordering = !!shouldAllowColumnReordering;
00557 }
00558 
00562 - (BOOL)allowsColumnReordering
00563 {
00564     return _allowsColumnReordering;
00565 }
00566 
00571 - (void)setAllowsColumnResizing:(BOOL)shouldAllowColumnResizing
00572 {
00573     _allowsColumnResizing = !!shouldAllowColumnResizing;
00574 }
00575 
00579 - (BOOL)allowsColumnResizing
00580 {
00581     return _allowsColumnResizing;
00582 }
00583 
00588 - (void)setAllowsMultipleSelection:(BOOL)shouldAllowMultipleSelection
00589 {
00590     _allowsMultipleSelection = !!shouldAllowMultipleSelection;
00591 }
00592 
00598 - (BOOL)allowsMultipleSelection
00599 {
00600     return _allowsMultipleSelection;
00601 }
00602 
00607 - (void)setAllowsEmptySelection:(BOOL)shouldAllowEmptySelection
00608 {
00609     _allowsEmptySelection = !!shouldAllowEmptySelection;
00610 }
00611 
00615 - (BOOL)allowsEmptySelection
00616 {
00617     return _allowsEmptySelection;
00618 }
00619 
00625 - (void)setAllowsColumnSelection:(BOOL)shouldAllowColumnSelection
00626 {
00627     _allowsColumnSelection = !!shouldAllowColumnSelection;
00628 }
00629 
00630 
00634 - (BOOL)allowsColumnSelection
00635 {
00636     return _allowsColumnSelection;
00637 }
00638 
00639 //Setting Display Attributes
00646 - (void)setIntercellSpacing:(CGSize)aSize
00647 {
00648     if (_CGSizeEqualToSize(_intercellSpacing, aSize))
00649         return;
00650 
00651     _intercellSpacing = _CGSizeMakeCopy(aSize);
00652 
00653     _dirtyTableColumnRangeIndex = 0; // so that _recalculateTableColumnRanges will work
00654     [self _recalculateTableColumnRanges];
00655 
00656     [self setNeedsLayout];
00657     [_headerView setNeedsDisplay:YES];
00658     [_headerView setNeedsLayout];
00659 
00660     [self reloadData];
00661 }
00662 
00666 - (CGSize)intercellSpacing
00667 {
00668     return _CGSizeMakeCopy(_intercellSpacing);
00669 }
00670 
00677 - (void)setRowHeight:(unsigned)aRowHeight
00678 {
00679     aRowHeight = +aRowHeight;
00680 
00681     if (_rowHeight === aRowHeight)
00682         return;
00683 
00684     _rowHeight = MAX(0.0, aRowHeight);
00685 
00686     [self setNeedsLayout];
00687 }
00688 
00692 - (unsigned)rowHeight
00693 {
00694     return _rowHeight;
00695 }
00696 
00702 - (void)setUsesAlternatingRowBackgroundColors:(BOOL)shouldUseAlternatingRowBackgroundColors
00703 {
00704     _usesAlternatingRowBackgroundColors = shouldUseAlternatingRowBackgroundColors;
00705 }
00706 
00710 - (BOOL)usesAlternatingRowBackgroundColors
00711 {
00712     return _usesAlternatingRowBackgroundColors;
00713 }
00714 
00720 - (void)setAlternatingRowBackgroundColors:(CPArray)alternatingRowBackgroundColors
00721 {
00722     [self setValue:alternatingRowBackgroundColors forThemeAttribute:"alternating-row-colors"];
00723 
00724     [self setNeedsDisplay:YES];
00725 }
00726 
00730 - (CPArray)alternatingRowBackgroundColors
00731 {
00732     return [self currentValueForThemeAttribute:@"alternating-row-colors"];
00733 }
00734 
00746 - (unsigned)selectionHighlightStyle
00747 {
00748     return _selectionHighlightStyle;
00749 }
00750 
00762 - (void)setSelectionHighlightStyle:(unsigned)aSelectionHighlightStyle
00763 {
00764     //early return for IE.
00765     if (aSelectionHighlightStyle == CPTableViewSelectionHighlightStyleSourceList && !CPFeatureIsCompatible(CPHTMLCanvasFeature))
00766         return;
00767 
00768     _selectionHighlightStyle = aSelectionHighlightStyle;
00769     [self setNeedsDisplay:YES];
00770 
00771     if (aSelectionHighlightStyle === CPTableViewSelectionHighlightStyleSourceList)
00772         _destinationDragStyle = CPTableViewDraggingDestinationFeedbackStyleSourceList;
00773     else
00774         _destinationDragStyle = CPTableViewDraggingDestinationFeedbackStyleRegular;
00775 }
00776 
00782 - (void)setSelectionHighlightColor:(CPColor)aColor
00783 {
00784     [self setValue:aColor forThemeAttribute:"selection-color"];
00785 
00786     [self setNeedsDisplay:YES];
00787 }
00788 
00792 - (CPColor)selectionHighlightColor
00793 {
00794     return [self currentValueForThemeAttribute:@"selection-color"];
00795 }
00796 
00808 - (void)setSelectionGradientColors:(CPDictionary)aDictionary
00809 {
00810     [self setValue:aDictionary forThemeAttribute:"sourcelist-selection-color"];
00811 
00812     [self setNeedsDisplay:YES];
00813 }
00814 
00823 - (CPDictionary)selectionGradientColors
00824 {
00825     return [self currentValueForThemeAttribute:@"sourcelist-selection-color"];
00826 }
00827 
00832 - (void)setGridColor:(CPColor)aColor
00833 {
00834     [self setValue:aColor forThemeAttribute:"grid-color"];
00835 
00836     [self setNeedsDisplay:YES];
00837 }
00838 
00842 - (CPColor)gridColor
00843 {
00844     return [self currentValueForThemeAttribute:@"grid-color"];;
00845 }
00846 
00852 - (void)setGridStyleMask:(unsigned)aGrideStyleMask
00853 {
00854     if (_gridStyleMask === aGrideStyleMask)
00855         return;
00856 
00857     _gridStyleMask = aGrideStyleMask;
00858 
00859     [self setNeedsDisplay:YES];
00860 }
00861 
00865 - (unsigned)gridStyleMask
00866 {
00867     return _gridStyleMask;
00868 }
00869 
00870 //Column Management
00871 
00876 - (void)addTableColumn:(CPTableColumn)aTableColumn
00877 {
00878     [_tableColumns addObject:aTableColumn];
00879     [aTableColumn setTableView:self];
00880 
00881     if (_dirtyTableColumnRangeIndex < 0)
00882         _dirtyTableColumnRangeIndex = NUMBER_OF_COLUMNS() - 1;
00883     else
00884         _dirtyTableColumnRangeIndex = MIN(NUMBER_OF_COLUMNS() - 1, _dirtyTableColumnRangeIndex);
00885 
00886     if ([[self sortDescriptors] count] > 0)
00887     {
00888         var mainSortDescriptor = [[self sortDescriptors] objectAtIndex:0];
00889 
00890         if (aTableColumn === [self _tableColumnForSortDescriptor:mainSortDescriptor])
00891         {
00892             var image = [mainSortDescriptor ascending] ? [self _tableHeaderSortImage] : [self _tableHeaderReverseSortImage];
00893             [self setIndicatorImage:image inTableColumn:aTableColumn];
00894         }
00895     }
00896 
00897     [self tile];
00898     [self setNeedsLayout];
00899 }
00900 
00905 - (void)removeTableColumn:(CPTableColumn)aTableColumn
00906 {
00907     if ([aTableColumn tableView] !== self)
00908         return;
00909 
00910     var index = [_tableColumns indexOfObjectIdenticalTo:aTableColumn];
00911 
00912     if (index === CPNotFound)
00913         return;
00914 
00915     // we defer the actual removal until the end of the runloop in order to keep a reference to the column.
00916     [_differedColumnDataToRemove addObject:{"column":aTableColumn, "shouldBeHidden": [aTableColumn isHidden]}];
00917 
00918     [aTableColumn setHidden:YES];
00919     [aTableColumn setTableView:nil];
00920 
00921     var tableColumnUID = [aTableColumn UID];
00922 
00923     if (_objectValues[tableColumnUID])
00924         _objectValues[tableColumnUID] = nil;
00925 
00926     if (_dirtyTableColumnRangeIndex < 0)
00927         _dirtyTableColumnRangeIndex = index;
00928     else
00929         _dirtyTableColumnRangeIndex = MIN(index, _dirtyTableColumnRangeIndex);
00930 
00931     [self setNeedsLayout];
00932 }
00933 
00938 - (void)_setDraggedColumn:(CPTableColumn)aColumn
00939 {
00940     if (_draggedColumn === aColumn)
00941         return;
00942 
00943     _draggedColumn = aColumn;
00944 
00945     [self reloadDataForRowIndexes:_exposedRows columnIndexes:[CPIndexSet indexSetWithIndex:[_tableColumns indexOfObject:aColumn]]];
00946 }
00947 
00948 /*
00949     @ignore
00950     Same as moveColumn:toColumn: but doesn't trigger an autosave
00951 */
00952 - (void)_moveColumn:(unsigned)fromIndex toColumn:(unsigned)toIndex
00953 {
00954     fromIndex = +fromIndex;
00955     toIndex = +toIndex;
00956 
00957     if (fromIndex === toIndex)
00958         return;
00959 
00960     if (_dirtyTableColumnRangeIndex < 0)
00961         _dirtyTableColumnRangeIndex = MIN(fromIndex, toIndex);
00962     else
00963         _dirtyTableColumnRangeIndex = MIN(fromIndex, toIndex, _dirtyTableColumnRangeIndex);
00964 
00965     var tableColumn = _tableColumns[fromIndex];
00966 
00967     [_tableColumns removeObjectAtIndex:fromIndex];
00968     [_tableColumns insertObject:tableColumn atIndex:toIndex];
00969 
00970     [[self headerView] setNeedsLayout];
00971     [[self headerView] setNeedsDisplay:YES];
00972 
00973     var rowIndexes = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, [self numberOfRows])],
00974         columnIndexes = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(fromIndex, toIndex)];
00975 
00976     [self reloadDataForRowIndexes:rowIndexes columnIndexes:columnIndexes];
00977 }
00978 
00984 - (void)moveColumn:(int)theColumnIndex toColumn:(int)theToIndex
00985 {
00986     [self _moveColumn:theColumnIndex toColumn:theToIndex];
00987     [self _autosave];
00988 }
00989 
00994 - (void)_tableColumnVisibilityDidChange:(CPTableColumn)aColumn
00995 {
00996     var columnIndex = [[self tableColumns] indexOfObjectIdenticalTo:aColumn];
00997 
00998     if (_dirtyTableColumnRangeIndex < 0)
00999         _dirtyTableColumnRangeIndex = columnIndex;
01000     else
01001         _dirtyTableColumnRangeIndex = MIN(columnIndex, _dirtyTableColumnRangeIndex);
01002 
01003     [[self headerView] setNeedsLayout];
01004     [[self headerView] setNeedsDisplay:YES];
01005 
01006     var rowIndexes = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, [self numberOfRows])];
01007     [self reloadDataForRowIndexes:rowIndexes columnIndexes:[CPIndexSet indexSetWithIndex:columnIndex]];
01008 }
01009 
01013 - (CPArray)tableColumns
01014 {
01015     return _tableColumns;
01016 }
01017 
01024 - (CPInteger)columnWithIdentifier:(CPString)anIdentifier
01025 {
01026     var index = 0,
01027         count = NUMBER_OF_COLUMNS();
01028 
01029     for (; index < count; ++index)
01030         if ([_tableColumns[index] identifier] === anIdentifier)
01031             return index;
01032 
01033     return CPNotFound;
01034 }
01035 
01042 - (CPTableColumn)tableColumnWithIdentifier:(CPString)anIdentifier
01043 {
01044     var index = [self columnWithIdentifier:anIdentifier];
01045 
01046     if (index === CPNotFound)
01047         return nil;
01048 
01049     return _tableColumns[index];
01050 }
01051 
01055 - (void)_didResizeTableColumn:(CPTableColumn)theColumn
01056 {
01057     [self _autosave];
01058 }
01059 
01060 //Selecting Columns and Rows
01061 
01068 - (void)selectColumnIndexes:(CPIndexSet)columns byExtendingSelection:(BOOL)shouldExtendSelection
01069 {
01070     // If we're out of range, just return
01071     if (([columns firstIndex] != CPNotFound && [columns firstIndex] < 0) || [columns lastIndex] >= [self numberOfColumns])
01072         return;
01073 
01074     // We deselect all rows when selecting columns.
01075     if ([_selectedRowIndexes count] > 0)
01076     {
01077         [self _updateHighlightWithOldRows:_selectedRowIndexes newRows:[CPIndexSet indexSet]];
01078         _selectedRowIndexes = [CPIndexSet indexSet];
01079     }
01080 
01081     var previousSelectedIndexes = [_selectedColumnIndexes copy];
01082 
01083     if (shouldExtendSelection)
01084         [_selectedColumnIndexes addIndexes:columns];
01085     else
01086         _selectedColumnIndexes = [columns copy];
01087 
01088     [self _updateHighlightWithOldColumns:previousSelectedIndexes newColumns:_selectedColumnIndexes];
01089     [self setNeedsDisplay:YES]; // FIXME: should be setNeedsDisplayInRect:enclosing rect of new (de)selected columns
01090                               // but currently -drawRect: is not implemented here
01091     if (_headerView)
01092         [_headerView setNeedsDisplay:YES];
01093 
01094     [self _noteSelectionDidChange];
01095 }
01096 
01100 - (void)_setSelectedRowIndexes:(CPIndexSet)rows
01101 {
01102     if ([_selectedRowIndexes isEqualToIndexSet:rows])
01103         return;
01104 
01105     var previousSelectedIndexes = _selectedRowIndexes;
01106 
01107     _lastSelectedRow = ([rows count] > 0) ? [rows lastIndex] : -1;
01108     _selectedRowIndexes = [rows copy];
01109 
01110     [self _updateHighlightWithOldRows:previousSelectedIndexes newRows:_selectedRowIndexes];
01111     [self setNeedsDisplay:YES]; // FIXME: should be setNeedsDisplayInRect:enclosing rect of new (de)selected rows
01112                               // but currently -drawRect: is not implemented here
01113 
01114     var binderClass = [[self class] _binderClassForBinding:@"selectionIndexes"];
01115     [[binderClass getBinding:@"selectionIndexes" forObject:self] reverseSetValueFor:@"selectedRowIndexes"];
01116 
01117     [self _noteSelectionDidChange];
01118 }
01119 
01126 - (void)selectRowIndexes:(CPIndexSet)rows byExtendingSelection:(BOOL)shouldExtendSelection
01127 {
01128     if ([rows isEqualToIndexSet:_selectedRowIndexes] ||
01129         (([rows firstIndex] != CPNotFound && [rows firstIndex] < 0) || [rows lastIndex] >= [self numberOfRows]))
01130         return;
01131 
01132     // We deselect all columns when selecting rows.
01133     if ([_selectedColumnIndexes count] > 0)
01134     {
01135         [self _updateHighlightWithOldColumns:_selectedColumnIndexes newColumns:[CPIndexSet indexSet]];
01136         _selectedColumnIndexes = [CPIndexSet indexSet];
01137         if (_headerView)
01138             [_headerView setNeedsDisplay:YES];
01139     }
01140 
01141     var newSelectedIndexes;
01142     if (shouldExtendSelection)
01143     {
01144         newSelectedIndexes = [_selectedRowIndexes copy];
01145         [newSelectedIndexes addIndexes:rows];
01146     }
01147     else
01148         newSelectedIndexes = [rows copy];
01149 
01150     [self _setSelectedRowIndexes:newSelectedIndexes];
01151 }
01152 
01156 - (void)_updateHighlightWithOldRows:(CPIndexSet)oldRows newRows:(CPIndexSet)newRows
01157 {
01158     var firstExposedRow = [_exposedRows firstIndex],
01159         exposedLength = [_exposedRows lastIndex] - firstExposedRow + 1,
01160         deselectRows = [],
01161         selectRows = [],
01162         deselectRowIndexes = [oldRows copy],
01163         selectRowIndexes = [newRows copy];
01164 
01165     [deselectRowIndexes removeMatches:selectRowIndexes];
01166     [deselectRowIndexes getIndexes:deselectRows maxCount:-1 inIndexRange:CPMakeRange(firstExposedRow, exposedLength)];
01167     [selectRowIndexes getIndexes:selectRows maxCount:-1 inIndexRange:CPMakeRange(firstExposedRow, exposedLength)];
01168 
01169     for (var identifier in _dataViewsForTableColumns)
01170     {
01171         var dataViewsInTableColumn = _dataViewsForTableColumns[identifier],
01172             count = deselectRows.length;
01173         while (count--)
01174             [self _performSelection:NO forRow:deselectRows[count] context:dataViewsInTableColumn];
01175 
01176         count = selectRows.length;
01177         while (count--)
01178             [self _performSelection:YES forRow:selectRows[count] context:dataViewsInTableColumn];
01179     }
01180 }
01181 
01185 - (void)_performSelection:(BOOL)select forRow:(CPInteger)rowIndex context:(id)context
01186 {
01187     var view = context[rowIndex],
01188         selector = select ? @"setThemeState:" : @"unsetThemeState:";
01189 
01190     [view performSelector:CPSelectorFromString(selector) withObject:CPThemeStateSelectedDataView];
01191 }
01192 
01196 - (void)_updateHighlightWithOldColumns:(CPIndexSet)oldColumns newColumns:(CPIndexSet)newColumns
01197 {
01198     var firstExposedColumn = [_exposedColumns firstIndex],
01199         exposedLength = [_exposedColumns lastIndex] - firstExposedColumn  +1,
01200         deselectColumns  = [],
01201         selectColumns  = [],
01202         deselectColumnIndexes = [oldColumns copy],
01203         selectColumnIndexes = [newColumns copy],
01204         selectRows = [];
01205 
01206     [deselectColumnIndexes removeMatches:selectColumnIndexes];
01207     [deselectColumnIndexes getIndexes:deselectColumns maxCount:-1 inIndexRange:CPMakeRange(firstExposedColumn, exposedLength)];
01208     [selectColumnIndexes getIndexes:selectColumns maxCount:-1 inIndexRange:CPMakeRange(firstExposedColumn, exposedLength)];
01209     [_exposedRows getIndexes:selectRows maxCount:-1 inIndexRange:nil];
01210 
01211     var rowsCount = selectRows.length,
01212         count = deselectColumns.length;
01213     while (count--)
01214     {
01215         var columnIndex = deselectColumns[count],
01216             identifier = [_tableColumns[columnIndex] UID],
01217             dataViewsInTableColumn = _dataViewsForTableColumns[identifier];
01218 
01219         for (var i = 0; i < rowsCount; i++)
01220         {
01221             var rowIndex = selectRows[i],
01222                 dataView = dataViewsInTableColumn[rowIndex];
01223             [dataView unsetThemeState:CPThemeStateSelectedDataView];
01224         }
01225 
01226         if (_headerView)
01227         {
01228             var headerView = [_tableColumns[columnIndex] headerView];
01229             [headerView unsetThemeState:CPThemeStateSelected];
01230         }
01231     }
01232 
01233     count = selectColumns.length;
01234     while (count--)
01235     {
01236         var columnIndex = selectColumns[count],
01237             identifier = [_tableColumns[columnIndex] UID],
01238             dataViewsInTableColumn = _dataViewsForTableColumns[identifier];
01239 
01240         for (var i = 0; i < rowsCount; i++)
01241         {
01242             var rowIndex = selectRows[i],
01243                 dataView = dataViewsInTableColumn[rowIndex];
01244             [dataView setThemeState:CPThemeStateSelectedDataView];
01245         }
01246         if (_headerView)
01247         {
01248             var headerView = [_tableColumns[columnIndex] headerView];
01249             [headerView setThemeState:CPThemeStateSelected];
01250         }
01251     }
01252 }
01253 
01257 - (int)selectedColumn
01258 {
01259     [_selectedColumnIndexes lastIndex];
01260 }
01261 
01265 - (CPIndexSet)selectedColumnIndexes
01266 {
01267     return _selectedColumnIndexes;
01268 }
01269 
01273 - (int)selectedRow
01274 {
01275     return _lastSelectedRow;
01276 }
01277 
01281 - (CPIndexSet)selectedRowIndexes
01282 {
01283     return [_selectedRowIndexes copy];
01284 }
01285 
01291 - (void)deselectColumn:(CPInteger)anIndex
01292 {
01293     var selectedColumnIndexes = [_selectedColumnIndexes copy];
01294     [selectedColumnIndexes removeIndex:anIndex];
01295     [self selectColumnIndexes:selectedColumnIndexes byExtendingSelection:NO];
01296     [self _noteSelectionDidChange];
01297 }
01298 
01304 - (void)deselectRow:(CPInteger)aRow
01305 {
01306     var selectedRowIndexes = [_selectedRowIndexes copy];
01307     [selectedRowIndexes removeIndex:aRow];
01308     [self selectRowIndexes:selectedRowIndexes byExtendingSelection:NO];
01309     [self _noteSelectionDidChange];
01310 }
01311 
01315 - (CPInteger)numberOfSelectedColumns
01316 {
01317     return [_selectedColumnIndexes count];
01318 }
01319 
01323 - (CPInteger)numberOfSelectedRows
01324 {
01325     return [_selectedRowIndexes count];
01326 }
01327 
01334 - (BOOL)isColumnSelected:(CPInteger)anIndex
01335 {
01336     return [_selectedColumnIndexes containsIndex:anIndex];
01337 }
01338 
01345 - (BOOL)isRowSelected:(CPInteger)aRow
01346 {
01347     return [_selectedRowIndexes containsIndex:aRow];
01348 }
01349 
01350 
01354 - (void)deselectAll
01355 {
01356     [self selectRowIndexes:[CPIndexSet indexSet] byExtendingSelection:NO];
01357     [self selectColumnIndexes:[CPIndexSet indexSet] byExtendingSelection:NO];
01358 }
01359 
01363 - (int)numberOfColumns
01364 {
01365     return NUMBER_OF_COLUMNS();
01366 }
01367 
01371 - (int)numberOfRows
01372 {
01373     if (_numberOfRows !== nil)
01374         return _numberOfRows;
01375 
01376     var contentBindingInfo = [self infoForBinding:@"content"];
01377 
01378     if (contentBindingInfo)
01379     {
01380         var destination = [contentBindingInfo objectForKey:CPObservedObjectKey],
01381             keyPath = [contentBindingInfo objectForKey:CPObservedKeyPathKey];
01382 
01383         _numberOfRows = [[destination valueForKeyPath:keyPath] count];
01384     }
01385     else if (_dataSource && (_implementedDataSourceMethods & CPTableViewDataSource_numberOfRowsInTableView_))
01386         _numberOfRows = [_dataSource numberOfRowsInTableView:self] || 0;
01387     else
01388     {
01389         if (_dataSource)
01390             CPLog(@"no content binding established and data source " + [_dataSource description] + " does not implement numberOfRowsInTableView:");
01391         _numberOfRows = 0;
01392     }
01393 
01394     return _numberOfRows;
01395 }
01396 
01406 - (void)editColumn:(CPInteger)columnIndex row:(CPInteger)rowIndex withEvent:(CPEvent)theEvent select:(BOOL)flag
01407 {
01408     // FIX ME: Cocoa documentation says all this should be called in THIS method:
01409     // sets up the field editor, and sends selectWithFrame:inView:editor:delegate:start:length: and editWithFrame:inView:editor:delegate:event: to the field editor's NSCell object with the NSTableView as the text delegate.
01410 
01411     if (![self isRowSelected:rowIndex])
01412         [[CPException exceptionWithName:@"Error" reason:@"Attempt to edit row="+rowIndex+" when not selected." userInfo:nil] raise];
01413 
01414     [self scrollRowToVisible:rowIndex];
01415     [self scrollColumnToVisible:columnIndex];
01416 
01417     // TODO Do something with flag.
01418 
01419     _editingCellIndex = CGPointMake(columnIndex, rowIndex);
01420     _editingCellIndex._shouldSelect = flag;
01421 
01422     [self reloadDataForRowIndexes:[CPIndexSet indexSetWithIndex:rowIndex]
01423         columnIndexes:[CPIndexSet indexSetWithIndex:columnIndex]];
01424 }
01425 
01429 - (CPInteger)editedColumn
01430 {
01431     if (!_editingCellIndex)
01432         return CPNotFound;
01433     return _editingCellIndex.x;
01434 }
01435 
01439 - (CPInteger)editedRow
01440 {
01441     if (!_editingCellIndex)
01442         return CPNotFound;
01443     return _editingCellIndex.y;
01444 }
01445 
01449 - (CPView)cornerView
01450 {
01451     return _cornerView;
01452 }
01453 
01457 - (void)setCornerView:(CPView)aView
01458 {
01459     if (_cornerView === aView)
01460         return;
01461 
01462     _cornerView = aView;
01463 
01464     var scrollView = [self enclosingScrollView];
01465 
01466     if ([scrollView isKindOfClass:[CPScrollView class]] && [scrollView documentView] === self)
01467         [scrollView _updateCornerAndHeaderView];
01468 }
01469 
01473 - (CPView)headerView
01474 {
01475     return _headerView;
01476 }
01477 
01478 
01486 - (void)setHeaderView:(CPView)aHeaderView
01487 {
01488     if (_headerView === aHeaderView)
01489         return;
01490 
01491     [_headerView setTableView:nil];
01492 
01493     _headerView = aHeaderView;
01494 
01495     if (_headerView)
01496     {
01497         [_headerView setTableView:self];
01498         [_headerView setFrameSize:_CGSizeMake(_CGRectGetWidth([self frame]), _CGRectGetHeight([_headerView frame]))];
01499     }
01500     else
01501     {
01502         // If there is no header view, there should be no corner view
01503         [_cornerView removeFromSuperview];
01504         _cornerView = nil;
01505     }
01506 
01507     var scrollView = [self enclosingScrollView];
01508 
01509     if ([scrollView isKindOfClass:[CPScrollView class]] && [scrollView documentView] === self)
01510         [scrollView _updateCornerAndHeaderView];
01511 }
01512 
01513 // Complexity:
01514 // O(Columns)
01518 - (void)_recalculateTableColumnRanges
01519 {
01520     if (_dirtyTableColumnRangeIndex < 0)
01521         return;
01522 
01523     _numberOfHiddenColumns = 0;
01524 
01525     var index = _dirtyTableColumnRangeIndex,
01526         count = NUMBER_OF_COLUMNS(),
01527         x = index === 0 ? 0.0 : CPMaxRange(_tableColumnRanges[index - 1]);
01528 
01529     for (; index < count; ++index)
01530     {
01531         var tableColumn = _tableColumns[index];
01532 
01533         if ([tableColumn isHidden])
01534         {
01535             _numberOfHiddenColumns += 1;
01536             _tableColumnRanges[index] = CPMakeRange(x, 0.0);
01537         }
01538         else
01539         {
01540             var width = [_tableColumns[index] width] + _intercellSpacing.width;
01541 
01542             _tableColumnRanges[index] = CPMakeRange(x, width);
01543 
01544             x += width;
01545         }
01546     }
01547 
01548     _tableColumnRanges.length = count;
01549     _dirtyTableColumnRangeIndex = CPNotFound;
01550 }
01551 
01552 // Complexity:
01553 // O(1)
01560 - (CGRect)rectOfColumn:(CPInteger)aColumnIndex
01561 {
01562     aColumnIndex = +aColumnIndex;
01563 
01564     if (aColumnIndex < 0 || aColumnIndex >= NUMBER_OF_COLUMNS())
01565         return _CGRectMakeZero();
01566 
01567     var column = [[self tableColumns] objectAtIndex:aColumnIndex];
01568 
01569     if ([column isHidden])
01570         return _CGRectMakeZero();
01571 
01572     UPDATE_COLUMN_RANGES_IF_NECESSARY();
01573 
01574     var range = _tableColumnRanges[aColumnIndex];
01575 
01576     return _CGRectMake(range.location, 0.0, range.length, _CGRectGetHeight([self bounds]));
01577 }
01578 
01579 // Complexity:
01580 // O(1)
01588 - (CGRect)_rectOfRow:(CPInteger)aRowIndex checkRange:(BOOL)checkRange
01589 {
01590     var lastIndex = [self numberOfRows] - 1;
01591 
01592     if (checkRange && (aRowIndex > lastIndex || aRowIndex < 0))
01593         return _CGRectMakeZero();
01594 
01595     if (_implementedDelegateMethods & CPTableViewDelegate_tableView_heightOfRow_)
01596     {
01597         var rowToLookUp = MIN(aRowIndex, lastIndex);
01598 
01599         // if the row doesn't exist
01600         if (rowToLookUp !== CPNotFound)
01601         {
01602             var y = _cachedRowHeights[rowToLookUp].heightAboveRow,
01603                 height = _cachedRowHeights[rowToLookUp].height + _intercellSpacing.height,
01604                 rowDelta = aRowIndex - rowToLookUp;
01605         }
01606         else
01607         {
01608             y = aRowIndex * (_rowHeight + _intercellSpacing.height);
01609             height = _rowHeight + _intercellSpacing.height;
01610         }
01611 
01612         // if we need the rect of a row past the last index
01613         if (rowDelta > 0)
01614         {
01615             y += rowDelta * (_rowHeight + _intercellSpacing.height);
01616             height = _rowHeight + _intercellSpacing.height;
01617         }
01618     }
01619     else
01620     {
01621         var y = aRowIndex * (_rowHeight + _intercellSpacing.height),
01622             height = _rowHeight + _intercellSpacing.height;
01623     }
01624 
01625     return _CGRectMake(0.0, y, _CGRectGetWidth([self bounds]), height);
01626 }
01627 
01633 - (CGRect)rectOfRow:(CPInteger)aRowIndex
01634 {
01635     return [self _rectOfRow:aRowIndex checkRange:YES];
01636 }
01637 
01638 // Complexity:
01639 // O(1)
01645 - (CPRange)rowsInRect:(CGRect)aRect
01646 {
01647     // If we have no rows, then we won't intersect anything.
01648     if (_numberOfRows <= 0)
01649         return CPMakeRange(0, 0);
01650 
01651     var bounds = [self bounds];
01652 
01653     // No rows if the rect doesn't even intersect us.
01654     if (!CGRectIntersectsRect(aRect, bounds))
01655         return CPMakeRange(0, 0);
01656 
01657     var firstRow = [self rowAtPoint:aRect.origin];
01658 
01659     // first row has to be undershot, because if not we wouldn't be intersecting.
01660     if (firstRow < 0)
01661         firstRow = 0;
01662 
01663     var lastRow = [self rowAtPoint:_CGPointMake(0.0, _CGRectGetMaxY(aRect))];
01664 
01665     // last row has to be overshot, because if not we wouldn't be intersecting.
01666     if (lastRow < 0)
01667         lastRow = _numberOfRows - 1;
01668 
01669     return CPMakeRange(firstRow, lastRow - firstRow + 1);
01670 }
01671 
01676 - (CPRange)_unboundedRowsInRect:(CGRect)aRect
01677 {
01678     var boundedRange = [self rowsInRect:aRect],
01679         lastRow = CPMaxRange(boundedRange),
01680         rectOfLastRow = [self _rectOfRow:lastRow checkRange:NO],
01681         bottom = _CGRectGetMaxY(aRect),
01682         bottomOfBoundedRows = _CGRectGetMaxY(rectOfLastRow);
01683 
01684     // we only have to worry about the rows below the last...
01685     if (bottom <= bottomOfBoundedRows)
01686         return boundedRange;
01687 
01688     var numberOfNewRows = CEIL(bottom -  bottomOfBoundedRows) / ([self rowHeight] + _intercellSpacing.height);
01689 
01690     boundedRange.length += numberOfNewRows + 1;
01691 
01692     return boundedRange;
01693 }
01694 
01695 // Complexity:
01696 // O(lg Columns) if table view contains no hidden columns
01697 // O(Columns) if table view contains hidden columns
01698 
01704 - (CPIndexSet)columnIndexesInRect:(CGRect)aRect
01705 {
01706     var column = MAX(0, [self columnAtPoint:_CGPointMake(aRect.origin.x, 0.0)]),
01707         lastColumn = [self columnAtPoint:_CGPointMake(_CGRectGetMaxX(aRect), 0.0)];
01708 
01709     if (lastColumn === CPNotFound)
01710         lastColumn = NUMBER_OF_COLUMNS() - 1;
01711 
01712     // Don't bother doing the expensive removal of hidden indexes if we have no hidden columns.
01713     if (_numberOfHiddenColumns <= 0)
01714         return [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(column, lastColumn - column + 1)];
01715 
01716     //
01717     var indexSet = [CPIndexSet indexSet];
01718 
01719     for (; column <= lastColumn; ++column)
01720     {
01721         var tableColumn = _tableColumns[column];
01722 
01723         if (![tableColumn isHidden])
01724             [indexSet addIndex:column];
01725     }
01726 
01727     return indexSet;
01728 }
01729 
01730 // Complexity:
01731 // O(lg Columns) if table view contains now hidden columns
01732 // O(Columns) if table view contains hidden columns
01738 - (CPInteger)columnAtPoint:(CGPoint)aPoint
01739 {
01740     var bounds = [self bounds];
01741 
01742     if (!_CGRectContainsPoint(bounds, aPoint))
01743         return CPNotFound;
01744 
01745     UPDATE_COLUMN_RANGES_IF_NECESSARY();
01746 
01747     var x = aPoint.x,
01748         low = 0,
01749         high = _tableColumnRanges.length - 1;
01750 
01751     while (low <= high)
01752     {
01753         var middle = FLOOR(low + (high - low) / 2),
01754             range = _tableColumnRanges[middle];
01755 
01756         if (x < range.location)
01757             high = middle - 1;
01758 
01759         else if (x >= CPMaxRange(range))
01760             low = middle + 1;
01761 
01762         else
01763         {
01764             var numberOfColumns = _tableColumnRanges.length;
01765 
01766             while (middle < numberOfColumns && [_tableColumns[middle] isHidden])
01767                 ++middle;
01768 
01769             if (middle < numberOfColumns)
01770                 return middle;
01771 
01772             return CPNotFound;
01773         }
01774    }
01775 
01776    return CPNotFound;
01777 }
01778 
01779 //Complexity
01780 // O(1) for static row height
01781 // 0(lg Rows) for variable row heights
01787 - (CPInteger)rowAtPoint:(CGPoint)aPoint
01788 {
01789     if (_implementedDelegateMethods & CPTableViewDelegate_tableView_heightOfRow_)
01790     {
01791             return idx = [_cachedRowHeights indexOfObject:aPoint
01792                                             inSortedRange:nil
01793                                                   options:0
01794                                           usingComparator:function(aPoint, rowCache)
01795                     {
01796                           var upperBound = rowCache.heightAboveRow;
01797 
01798                           if (aPoint.y < upperBound)
01799                               return CPOrderedAscending;
01800 
01801                           if (aPoint.y > upperBound + rowCache.height + _intercellSpacing.height)
01802                               return CPOrderedDescending;
01803 
01804                           return CPOrderedSame;
01805                     }];
01806     }
01807 
01808     var y = aPoint.y,
01809         row = FLOOR(y / (_rowHeight + _intercellSpacing.height));
01810 
01811     if (row >= _numberOfRows)
01812         return CPNotFound;
01813 
01814     return row;
01815 }
01816 
01824 - (CGRect)frameOfDataViewAtColumn:(CPInteger)aColumn row:(CPInteger)aRow
01825 {
01826     UPDATE_COLUMN_RANGES_IF_NECESSARY();
01827 
01828     if (aColumn > [self numberOfColumns] || aRow > [self numberOfRows])
01829         return _CGRectMakeZero();
01830 
01831     var tableColumnRange = _tableColumnRanges[aColumn],
01832         rectOfRow = [self rectOfRow:aRow],
01833         leftInset = FLOOR(_intercellSpacing.width / 2.0),
01834         topInset = FLOOR(_intercellSpacing.height / 2.0);
01835 
01836     return _CGRectMake(tableColumnRange.location + leftInset, _CGRectGetMinY(rectOfRow) + topInset, tableColumnRange.length - _intercellSpacing.width, _CGRectGetHeight(rectOfRow) - _intercellSpacing.height);
01837 }
01838 
01842 - (void)resizeWithOldSuperviewSize:(CGSize)aSize
01843 {
01844     [super resizeWithOldSuperviewSize:aSize];
01845 
01846     if (_disableAutomaticResizing)
01847         return;
01848 
01849     var mask = _columnAutoResizingStyle;
01850 
01851     // should we actually do some resizing?
01852     if (!_lastColumnShouldSnap)
01853     {
01854         // did the clip view intersect the old tablesize?
01855         var superview = [self superview];
01856 
01857         if (!superview || ![superview isKindOfClass:[CPClipView class]])
01858             return;
01859 
01860         var superviewWidth = [superview bounds].size.width,
01861             lastColumnMaxX = _CGRectGetMaxX([self rectOfColumn:[self numberOfColumns] -1]);
01862 
01863         // Fix me: this fires on the table setup at times
01864         if (lastColumnMaxX >= superviewWidth && lastColumnMaxX <= aSize.width || lastColumnMaxX <= superviewWidth && lastColumnMaxX >= aSize.width)
01865             _lastColumnShouldSnap = YES;
01866         else if (mask === CPTableViewUniformColumnAutoresizingStyle)
01867             return;
01868     }
01869 
01870     if (mask === CPTableViewUniformColumnAutoresizingStyle)
01871        [self _resizeAllColumnUniformlyWithOldSize:aSize];
01872     else if (mask === CPTableViewLastColumnOnlyAutoresizingStyle)
01873         [self sizeLastColumnToFit];
01874     else if (mask === CPTableViewFirstColumnOnlyAutoresizingStyle)
01875         [self _autoResizeFirstColumn];
01876 }
01877 
01881 - (void)_autoResizeFirstColumn
01882 {
01883     var superview = [self superview];
01884 
01885     if (!superview)
01886         return;
01887 
01888     UPDATE_COLUMN_RANGES_IF_NECESSARY();
01889 
01890     var count = NUMBER_OF_COLUMNS(),
01891         columnToResize = nil,
01892         totalWidth = 0,
01893         i = 0;
01894 
01895     for (; i < count; i++)
01896     {
01897         var column = _tableColumns[i];
01898 
01899         if (![column isHidden])
01900         {
01901             if (!columnToResize)
01902                 columnToResize = column;
01903             totalWidth += [column width] + _intercellSpacing.width;
01904         }
01905     }
01906 
01907     // If there is a visible column
01908     if (columnToResize)
01909     {
01910         var superviewSize = [superview bounds].size,
01911             newWidth = superviewSize.width - totalWidth;
01912 
01913         newWidth += [columnToResize width];
01914         [columnToResize _tryToResizeToWidth:newWidth];
01915     }
01916 
01917     [self setNeedsLayout];
01918 }
01919 
01920 
01925 - (void)_resizeAllColumnUniformlyWithOldSize:(CGSize)oldSize
01926 {
01927     // what we care about is the superview clip rect
01928     // FIX ME: if it's not in a scrollview this doesn't really work
01929     var superview = [self superview];
01930 
01931     if (!superview || ![superview isKindOfClass:[CPClipView class]])
01932         return;
01933 
01934     UPDATE_COLUMN_RANGES_IF_NECESSARY();
01935 
01936     var superviewWidth = [superview bounds].size.width,
01937         count = NUMBER_OF_COLUMNS(),
01938         resizableColumns = [CPIndexSet indexSet],
01939         remainingSpace = 0.0,
01940         i = 0;
01941 
01942     // find resizable columns
01943     // FIX ME: we could cache resizableColumns after this loop and reuse it during the resize
01944     for (; i < count; i++)
01945     {
01946         var tableColumn = _tableColumns[i];
01947         if (![tableColumn isHidden] && ([tableColumn resizingMask] & CPTableColumnAutoresizingMask))
01948             [resizableColumns addIndex:i];
01949     }
01950 
01951     var maxXofColumns = _CGRectGetMaxX([self rectOfColumn:[resizableColumns lastIndex]]),
01952         remainingSpace = superviewWidth - maxXofColumns,
01953         resizeableColumnsCount = [resizableColumns count],
01954         proportionate = 0;
01955 
01956     while (remainingSpace && resizeableColumnsCount)
01957     {
01958         // Divy out the space.
01959         proportionate += remainingSpace / resizeableColumnsCount;
01960 
01961         // Reset the remaining space to 0
01962         remainingSpace = 0.0;
01963 
01964         var index = CPNotFound;
01965 
01966         while ((index = [resizableColumns indexGreaterThanIndex:index]) !== CPNotFound)
01967         {
01968             var item = _tableColumns[index],
01969                 proposedWidth = [item width] + proportionate,
01970                 resizeLeftovers = [item _tryToResizeToWidth:proposedWidth];
01971 
01972             if (resizeLeftovers)
01973             {
01974                 [resizableColumns removeIndex:index];
01975 
01976                 remainingSpace += resizeLeftovers;
01977             }
01978         }
01979     }
01980 
01981     // now that we've reached the end we know there are likely rounding errors
01982     // so we should size the last resized to fit
01983 
01984     // find the last visisble column
01985     while (count-- && [_tableColumns[count] isHidden]);
01986 
01987     // find the max x, but subtract a single pixel since the spacing isn't applicable here.
01988     var delta = superviewWidth - _CGRectGetMaxX([self rectOfColumn:count]) - ([self intercellSpacing].width || 1),
01989         newSize = [item width] + delta;
01990 
01991     [item _tryToResizeToWidth:newSize];
01992 }
01993 
02005 - (void)setColumnAutoresizingStyle:(unsigned)style
02006 {
02007     //FIX ME: CPTableViewSequentialColumnAutoresizingStyle and CPTableViewReverseSequentialColumnAutoresizingStyle are not yet implemented
02008     _columnAutoResizingStyle = style;
02009 }
02010 
02014 - (unsigned)columnAutoresizingStyle
02015 {
02016     return _columnAutoResizingStyle;
02017 }
02018 
02022 - (void)sizeLastColumnToFit
02023 {
02024     _lastColumnShouldSnap = YES;
02025 
02026     var superview = [self superview];
02027 
02028     if (!superview)
02029         return;
02030 
02031     var superviewSize = [superview bounds].size;
02032 
02033     UPDATE_COLUMN_RANGES_IF_NECESSARY();
02034 
02035     var count = NUMBER_OF_COLUMNS();
02036 
02037     // Decrement the counter until we get to the last column that's not hidden
02038     while (count-- && [_tableColumns[count] isHidden]);
02039 
02040     // If the last column exists
02041     if (count >= 0)
02042     {
02043         var columnToResize = _tableColumns[count],
02044             newSize = MAX(0.0, superviewSize.width - CGRectGetMinX([self rectOfColumn:count]) - _intercellSpacing.width);
02045 
02046         [columnToResize _tryToResizeToWidth:newSize];
02047     }
02048 
02049     [self setNeedsLayout];
02050 }
02051 
02055 - (void)noteNumberOfRowsChanged
02056 {
02057     var oldNumberOfRows = _numberOfRows;
02058 
02059     _numberOfRows = nil;
02060     _cachedRowHeights = [];
02061 
02062     // this line serves two purposes
02063     // 1. it updates the _numberOfRows cache with the -numberOfRows call
02064     // 2. it updates the row height cache if needed
02065     [self noteHeightOfRowsWithIndexesChanged:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, [self numberOfRows])]];
02066 
02067     // remove row indexes from the selection if they no longer exist
02068     var hangingSelections = oldNumberOfRows - _numberOfRows;
02069 
02070     if (hangingSelections > 0)
02071     {
02072 
02073         var previousSelectionCount = [_selectedRowIndexes count];
02074         [_selectedRowIndexes removeIndexesInRange:CPMakeRange(_numberOfRows, hangingSelections)];
02075 
02076         if (![_selectedRowIndexes containsIndex:[self selectedRow]])
02077             _lastSelectedRow = CPNotFound;
02078 
02079         // For optimal performance, only send a notification if indices were actually removed.
02080         if (previousSelectionCount > [_selectedRowIndexes count])
02081             [self _noteSelectionDidChange];
02082     }
02083 
02084     [self tile];
02085 }
02086 
02087 
02093 - (void)noteHeightOfRowsWithIndexesChanged:(CPIndexSet)anIndexSet
02094 {
02095     if (!(_implementedDelegateMethods & CPTableViewDelegate_tableView_heightOfRow_))
02096         return;
02097 
02098     // this method will update the height of those rows, but since the cached array also contains
02099     // the height above the row it needs to recalculate for the rows below it too
02100     var i = [anIndexSet firstIndex],
02101         count = _numberOfRows - i,
02102         heightAbove = (i > 0) ? _cachedRowHeights[i - 1].height + _cachedRowHeights[i - 1].heightAboveRow + _intercellSpacing.height : 0;
02103 
02104     for (; i < count; i++)
02105     {
02106         // update the cache if the user told us to
02107         if ([anIndexSet containsIndex:i])
02108             var height = [_delegate tableView:self heightOfRow:i];
02109 
02110             _cachedRowHeights[i] = {"height":height, "heightAboveRow":heightAbove};
02111 
02112         heightAbove += height + _intercellSpacing.height;
02113     }
02114 }
02115 
02119 - (void)tile
02120 {
02121     UPDATE_COLUMN_RANGES_IF_NECESSARY();
02122 
02123     var width = _tableColumnRanges.length > 0 ? CPMaxRange([_tableColumnRanges lastObject]) : 0.0,
02124         superview = [self superview];
02125 
02126     if (!(_implementedDelegateMethods & CPTableViewDelegate_tableView_heightOfRow_))
02127         var height =  (_rowHeight + _intercellSpacing.height) * _numberOfRows;
02128     else if ([self numberOfRows] === 0)
02129         var height = 0;
02130     else
02131     {
02132         // if this is the fist run we need to populate the cache
02133         if ([self numberOfRows] !== _cachedRowHeights.length)
02134             [self noteHeightOfRowsWithIndexesChanged:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, [self numberOfRows])]];
02135 
02136         var heightObject = _cachedRowHeights[_cachedRowHeights.length - 1],
02137             height = heightObject.heightAboveRow + heightObject.height + _intercellSpacing.height;
02138 
02139     }
02140 
02141 
02142     if ([superview isKindOfClass:[CPClipView class]])
02143     {
02144         var superviewSize = [superview bounds].size;
02145 
02146         width = MAX(superviewSize.width, width);
02147         height = MAX(superviewSize.height, height);
02148     }
02149 
02150     [self setFrameSize:_CGSizeMake(width, height)];
02151 
02152     [self setNeedsLayout];
02153     [self setNeedsDisplay:YES];
02154 }
02155 
02156 
02162 - (void)scrollRowToVisible:(int)rowIndex
02163 {
02164     var visible = [self visibleRect],
02165         rowRect = [self rectOfRow:rowIndex];
02166 
02167     visible.origin.y = rowRect.origin.y;
02168     visible.size.height = rowRect.size.height;
02169 
02170     [self scrollRectToVisible:visible];
02171 }
02172 
02178 - (void)scrollColumnToVisible:(int)columnIndex
02179 {
02180     var visible = [self visibleRect],
02181         colRect = [self rectOfColumn:columnIndex];
02182 
02183     visible.origin.x = colRect.origin.x;
02184     visible.size.width = colRect.size.width;
02185 
02186     [self scrollRectToVisible:visible];
02187     [_headerView scrollRectToVisible:colRect];
02188 }
02189 
02196 - (void)setAutosaveName:(CPString)theAutosaveName
02197 {
02198     if (_autosaveName === theAutosaveName)
02199         return;
02200 
02201     _autosaveName = theAutosaveName;
02202 
02203     [self setAutosaveTableColumns:!!theAutosaveName];
02204     [self _restoreFromAutosave];
02205 }
02206 
02210 - (CPString)autosaveName
02211 {
02212     return _autosaveName;
02213 }
02214 
02221 - (void)setAutosaveTableColumns:(BOOL)shouldAutosave
02222 {
02223     _autosaveTableColumns = shouldAutosave;
02224 }
02225 
02229 - (BOOL)autosaveTableColumns
02230 {
02231     return _autosaveTableColumns;
02232 }
02233 
02237 - (CPString)_columnsKeyForAutosaveName:(CPString)theAutosaveName
02238 {
02239     return @"CPTableView Columns " + theAutosaveName;
02240 }
02241 
02245 - (BOOL)_autosaveEnabled
02246 {
02247     return [self autosaveName] && [self autosaveTableColumns];
02248 }
02249 
02256 - (void)_autosave
02257 {
02258     if (![self _autosaveEnabled])
02259         return;
02260 
02261     var userDefaults = [CPUserDefaults standardUserDefaults],
02262         autosaveName = [self autosaveName];
02263 
02264     var columns = [self tableColumns],
02265         columnsSetup = [];
02266 
02267     for (var i = 0; i < [columns count]; i++)
02268     {
02269         var column = [columns objectAtIndex:i],
02270             metaData = [CPDictionary dictionaryWithJSObject:{
02271             @"identifier": [column identifier],
02272             @"width": [column width]
02273             }];
02274 
02275         [columnsSetup addObject:metaData];
02276     }
02277 
02278     [userDefaults setObject:columnsSetup forKey:[self _columnsKeyForAutosaveName:autosaveName]];
02279 }
02280 
02284 - (void)_restoreFromAutosave
02285 {
02286     if (![self _autosaveEnabled])
02287         return;
02288 
02289     var userDefaults = [CPUserDefaults standardUserDefaults],
02290         autosaveName = [self autosaveName],
02291         tableColumns = [userDefaults objectForKey:[self _columnsKeyForAutosaveName:autosaveName]];
02292 
02293     for (var i = 0; i < [tableColumns count]; i++)
02294     {
02295         var metaData = [tableColumns objectAtIndex:i],
02296             columnIdentifier = [metaData objectForKey:@"identifier"],
02297             column = [self columnWithIdentifier:columnIdentifier],
02298             tableColumn = [self tableColumnWithIdentifier:columnIdentifier];
02299 
02300         [self _moveColumn:column toColumn:i];
02301         [tableColumn setWidth:[metaData objectForKey:@"width"]];
02302     }
02303 }
02304 
02305 
02443 - (void)setDelegate:(id)aDelegate
02444 {
02445     if (_delegate === aDelegate)
02446         return;
02447 
02448     var defaultCenter = [CPNotificationCenter defaultCenter];
02449 
02450     if (_delegate)
02451     {
02452         if ([_delegate respondsToSelector:@selector(tableViewColumnDidMove:)])
02453             [defaultCenter
02454                 removeObserver:_delegate
02455                           name:CPTableViewColumnDidMoveNotification
02456                         object:self];
02457 
02458         if ([_delegate respondsToSelector:@selector(tableViewColumnDidResize:)])
02459             [defaultCenter
02460                 removeObserver:_delegate
02461                           name:CPTableViewColumnDidResizeNotification
02462                         object:self];
02463 
02464         if ([_delegate respondsToSelector:@selector(tableViewSelectionDidChange:)])
02465             [defaultCenter
02466                 removeObserver:_delegate
02467                           name:CPTableViewSelectionDidChangeNotification
02468                         object:self];
02469 
02470         if ([_delegate respondsToSelector:@selector(tableViewSelectionIsChanging:)])
02471             [defaultCenter
02472                 removeObserver:_delegate
02473                           name:CPTableViewSelectionIsChangingNotification
02474                         object:self];
02475     }
02476 
02477     _delegate = aDelegate;
02478     _implementedDelegateMethods = 0;
02479 
02480     if ([_delegate respondsToSelector:@selector(selectionShouldChangeInTableView:)])
02481         _implementedDelegateMethods |= CPTableViewDelegate_selectionShouldChangeInTableView_;
02482 
02483     if ([_delegate respondsToSelector:@selector(tableView:dataViewForTableColumn:row:)])
02484         _implementedDelegateMethods |= CPTableViewDelegate_tableView_dataViewForTableColumn_row_;
02485 
02486     if ([_delegate respondsToSelector:@selector(tableView:didClickTableColumn:)])
02487         _implementedDelegateMethods |= CPTableViewDelegate_tableView_didClickTableColumn_;
02488 
02489     if ([_delegate respondsToSelector:@selector(tableView:didDragTableColumn:)])
02490         _implementedDelegateMethods |= CPTableViewDelegate_tableView_didDragTableColumn_;
02491 
02492     if ([_delegate respondsToSelector:@selector(tableView:heightOfRow:)])
02493         _implementedDelegateMethods |= CPTableViewDelegate_tableView_heightOfRow_;
02494 
02495     if ([_delegate respondsToSelector:@selector(tableView:isGroupRow:)])
02496         _implementedDelegateMethods |= CPTableViewDelegate_tableView_isGroupRow_;
02497 
02498     if ([_delegate respondsToSelector:@selector(tableView:mouseDownInHeaderOfTableColumn:)])
02499         _implementedDelegateMethods |= CPTableViewDelegate_tableView_mouseDownInHeaderOfTableColumn_;
02500 
02501     if ([_delegate respondsToSelector:@selector(tableView:nextTypeSelectMatchFromRow:toRow:forString:)])
02502         _implementedDelegateMethods |= CPTableViewDelegate_tableView_nextTypeSelectMatchFromRow_toRow_forString_;
02503 
02504     if ([_delegate respondsToSelector:@selector(tableView:selectionIndexesForProposedSelection:)])
02505         _implementedDelegateMethods |= CPTableViewDelegate_tableView_selectionIndexesForProposedSelection_;
02506 
02507     if ([_delegate respondsToSelector:@selector(tableView:shouldEditTableColumn:row:)])
02508         _implementedDelegateMethods |= CPTableViewDelegate_tableView_shouldEditTableColumn_row_;
02509 
02510     if ([_delegate respondsToSelector:@selector(tableView:shouldSelectRow:)])
02511         _implementedDelegateMethods |= CPTableViewDelegate_tableView_shouldSelectRow_;
02512 
02513     if ([_delegate respondsToSelector:@selector(tableView:shouldSelectTableColumn:)])
02514         _implementedDelegateMethods |= CPTableViewDelegate_tableView_shouldSelectTableColumn_;
02515 
02516     if ([_delegate respondsToSelector:@selector(tableView:shouldShowViewExpansionForTableColumn:row:)])
02517         _implementedDelegateMethods |= CPTableViewDelegate_tableView_shouldShowViewExpansionForTableColumn_row_;
02518 
02519     if ([_delegate respondsToSelector:@selector(tableView:shouldTrackView:forTableColumn:row:)])
02520         _implementedDelegateMethods |= CPTableViewDelegate_tableView_shouldTrackView_forTableColumn_row_;
02521 
02522     if ([_delegate respondsToSelector:@selector(tableView:shouldTypeSelectForEvent:withCurrentSearchString:)])
02523         _implementedDelegateMethods |= CPTableViewDelegate_tableView_shouldTypeSelectForEvent_withCurrentSearchString_;
02524 
02525     if ([_delegate respondsToSelector:@selector(tableView:toolTipForView:rect:tableColumn:row:mouseLocation:)])
02526         _implementedDelegateMethods |= CPTableViewDelegate_tableView_toolTipForView_rect_tableColumn_row_mouseLocation_;
02527 
02528     if ([_delegate respondsToSelector:@selector(tableView:typeSelectStringForTableColumn:row:)])
02529         _implementedDelegateMethods |= CPTableViewDelegate_tableView_typeSelectStringForTableColumn_row_;
02530 
02531     if ([_delegate respondsToSelector:@selector(tableView:willDisplayView:forTableColumn:row:)])
02532         _implementedDelegateMethods |= CPTableViewDelegate_tableView_willDisplayView_forTableColumn_row_;
02533 
02534     if ([_delegate respondsToSelector:@selector(tableView:menuForTableColumn:row:)])
02535         _implementedDelegateMethods |= CPTableViewDelegate_tableViewMenuForTableColumn_Row_;
02536 
02537     if ([_delegate respondsToSelector:@selector(tableViewColumnDidMove:)])
02538         [defaultCenter
02539             addObserver:_delegate
02540             selector:@selector(tableViewColumnDidMove:)
02541             name:CPTableViewColumnDidMoveNotification
02542             object:self];
02543 
02544     if ([_delegate respondsToSelector:@selector(tableViewColumnDidResize:)])
02545         [defaultCenter
02546             addObserver:_delegate
02547             selector:@selector(tableViewColumnDidResize:)
02548             name:CPTableViewColumnDidResizeNotification
02549             object:self];
02550 
02551     if ([_delegate respondsToSelector:@selector(tableViewSelectionDidChange:)])
02552         [defaultCenter
02553             addObserver:_delegate
02554             selector:@selector(tableViewSelectionDidChange:)
02555             name:CPTableViewSelectionDidChangeNotification
02556             object:self];
02557 
02558     if ([_delegate respondsToSelector:@selector(tableViewSelectionIsChanging:)])
02559         [defaultCenter
02560             addObserver:_delegate
02561             selector:@selector(tableViewSelectionIsChanging:)
02562             name:CPTableViewSelectionIsChangingNotification
02563             object:self];
02564 }
02565 
02569 - (id)delegate
02570 {
02571     return _delegate;
02572 }
02573 
02577 - (void)_sendDelegateDidClickColumn:(int)column
02578 {
02579     if (_implementedDelegateMethods & CPTableViewDelegate_tableView_didClickTableColumn_)
02580             [_delegate tableView:self didClickTableColumn:_tableColumns[column]];
02581 }
02582 
02586 - (void)_sendDelegateDidDragColumn:(int)column
02587 {
02588     if (_implementedDelegateMethods & CPTableViewDelegate_tableView_didDragTableColumn_)
02589             [_delegate tableView:self didDragTableColumn:_tableColumns[column]];
02590 }
02591 
02592 - (void)_sendDelegateDidMouseDownInHeader:(int)column
02593 {
02594     if (_implementedDelegateMethods & CPTableViewDelegate_tableView_mouseDownInHeaderOfTableColumn_)
02595             [_delegate tableView:self mouseDownInHeaderOfTableColumn:_tableColumns[column]];
02596 }
02597 
02598 /*
02599     @ignore
02600 */
02601 - (BOOL)_sendDelegateDeleteKeyPressed
02602 {
02603     if ([_delegate respondsToSelector: @selector(tableViewDeleteKeyPressed:)])
02604     {
02605         [_delegate tableViewDeleteKeyPressed:self];
02606         return YES;
02607     }
02608 
02609     return NO;
02610 }
02611 
02612 
02616 - (void)_sendDataSourceSortDescriptorsDidChange:(CPArray)oldDescriptors
02617 {
02618     if (_implementedDataSourceMethods & CPTableViewDataSource_tableView_sortDescriptorsDidChange_)
02619         [_dataSource tableView:self sortDescriptorsDidChange:oldDescriptors];
02620 }
02621 
02622 
02626 - (void)_didClickTableColumn:(int)clickedColumn modifierFlags:(unsigned)modifierFlags
02627 {
02628     [self _sendDelegateDidClickColumn:clickedColumn];
02629 
02630     if (_allowsColumnSelection)
02631     {
02632         [self _noteSelectionIsChanging];
02633         if (modifierFlags & CPCommandKeyMask)
02634         {
02635             if ([self isColumnSelected:clickedColumn])
02636                 [self deselectColumn:clickedColumn];
02637             else if ([self allowsMultipleSelection] == YES)
02638                 [self selectColumnIndexes:[CPIndexSet indexSetWithIndex:clickedColumn] byExtendingSelection:YES];
02639 
02640             return;
02641         }
02642         else if (modifierFlags & CPShiftKeyMask)
02643         {
02644         // should be from clickedColumn to lastClickedColum with extending:(direction == previous selection)
02645             var startColumn = MIN(clickedColumn, [_selectedColumnIndexes lastIndex]),
02646                 endColumn = MAX(clickedColumn, [_selectedColumnIndexes firstIndex]);
02647 
02648             [self selectColumnIndexes:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(startColumn, endColumn - startColumn + 1)]
02649                  byExtendingSelection:YES];
02650 
02651             return;
02652         }
02653         else
02654             [self selectColumnIndexes:[CPIndexSet indexSetWithIndex:clickedColumn] byExtendingSelection:NO];
02655     }
02656 
02657     [self _changeSortDescriptorsForClickOnColumn:clickedColumn];
02658 }
02659 
02660 // From GNUSTEP
02664 - (void)_changeSortDescriptorsForClickOnColumn:(int)column
02665 {
02666     var tableColumn = [_tableColumns objectAtIndex:column],
02667         newMainSortDescriptor = [tableColumn sortDescriptorPrototype];
02668 
02669     if (!newMainSortDescriptor)
02670        return;
02671 
02672     var oldMainSortDescriptor = nil,
02673         oldSortDescriptors = [self sortDescriptors],
02674         newSortDescriptors = [CPArray arrayWithArray:oldSortDescriptors],
02675 
02676         e = [newSortDescriptors objectEnumerator],
02677         descriptor = nil,
02678         outdatedDescriptors = [CPArray array];
02679 
02680     if ([_sortDescriptors count] > 0)
02681         oldMainSortDescriptor = [[self sortDescriptors] objectAtIndex: 0];
02682 
02683     // Remove every main descriptor equivalents (normally only one)
02684     while ((descriptor = [e nextObject]) != nil)
02685     {
02686         if ([[descriptor key] isEqual: [newMainSortDescriptor key]])
02687             [outdatedDescriptors addObject:descriptor];
02688     }
02689 
02690     // Invert the sort direction when the same column header is clicked twice
02691     if ([[newMainSortDescriptor key] isEqual:[oldMainSortDescriptor key]])
02692         newMainSortDescriptor = [oldMainSortDescriptor reversedSortDescriptor];
02693 
02694     [newSortDescriptors removeObjectsInArray:outdatedDescriptors];
02695     [newSortDescriptors insertObject:newMainSortDescriptor atIndex:0];
02696 
02697     [self setHighlightedTableColumn:tableColumn];
02698     [self setSortDescriptors:newSortDescriptors];
02699 }
02700 
02709 - (void)setIndicatorImage:(CPImage)anImage inTableColumn:(CPTableColumn)aTableColumn
02710 {
02711     if (aTableColumn)
02712     {
02713         var headerView = [aTableColumn headerView];
02714         if ([headerView respondsToSelector:@selector(_setIndicatorImage:)])
02715             [headerView _setIndicatorImage:anImage];
02716     }
02717 }
02718 
02722 - (CPImage)_tableHeaderSortImage
02723 {
02724     return [self currentValueForThemeAttribute:"sort-image"];
02725 }
02726 
02730 - (CPImage)_tableHeaderReverseSortImage
02731 {
02732     return [self currentValueForThemeAttribute:"sort-image-reversed"];
02733 }
02734 
02738 - (CPTableColumn)highlightedTableColumn
02739 {
02740     return _currentHighlightedTableColumn;
02741 }
02742 
02746 - (void)setHighlightedTableColumn:(CPTableColumn)aTableColumn
02747 {
02748     if (_currentHighlightedTableColumn == aTableColumn)
02749         return;
02750 
02751     if (_headerView)
02752     {
02753         if (_currentHighlightedTableColumn != nil)
02754             [[_currentHighlightedTableColumn headerView] unsetThemeState:CPThemeStateSelected];
02755 
02756         if (aTableColumn != nil)
02757             [[aTableColumn headerView] setThemeState:CPThemeStateSelected];
02758     }
02759 
02760     _currentHighlightedTableColumn = aTableColumn;
02761 }
02762 
02769 - (BOOL)canDragRowsWithIndexes:(CPIndexSet)rowIndexes atPoint:(CGPoint)mouseDownPoint
02770 {
02771     return [rowIndexes count] > 0 && [self numberOfRows] > 0;
02772 }
02773 
02784 - (CPImage)dragImageForRowsWithIndexes:(CPIndexSet)dragRows tableColumns:(CPArray)theTableColumns event:(CPEvent)dragEvent offset:(CGPoint)dragImageOffset
02785 {
02786     return [[CPImage alloc] initWithContentsOfFile:@"Frameworks/AppKit/Resources/GenericFile.png" size:CGSizeMake(32,32)];
02787 }
02788 
02801 - (CPView)dragViewForRowsWithIndexes:(CPIndexSet)theDraggedRows tableColumns:(CPArray)theTableColumns event:(CPEvent)theDragEvent offset:(CGPoint)dragViewOffset
02802 {
02803     var bounds = [self bounds],
02804         view = [[CPView alloc] initWithFrame:bounds];
02805 
02806     [view setAlphaValue:0.7];
02807 
02808     // We have to fetch all the data views for the selected rows and columns
02809     // After that we can copy these add them to a transparent drag view and use that drag view
02810     // to make it appear we are dragging images of those rows (as you would do in regular Cocoa)
02811     var columnIndex = [theTableColumns count];
02812     while (columnIndex--)
02813     {
02814         var tableColumn = [theTableColumns objectAtIndex:columnIndex],
02815             row = [theDraggedRows firstIndex];
02816 
02817         while (row !== CPNotFound)
02818         {
02819             var dataView = [self _newDataViewForRow:row tableColumn:tableColumn];
02820 
02821             [dataView setFrame:[self frameOfDataViewAtColumn:columnIndex row:row]];
02822             [dataView setObjectValue:[self _objectValueForTableColumn:tableColumn row:row]];
02823 
02824             // If the column uses content bindings, allow them to override the objectValueForTableColumn.
02825             [tableColumn _prepareDataView:dataView forRow:row];
02826 
02827             [view addSubview:dataView];
02828 
02829             row = [theDraggedRows indexGreaterThanIndex:row];
02830         }
02831     }
02832 
02833     var dragPoint = [self convertPoint:[theDragEvent locationInWindow] fromView:nil];
02834     dragViewOffset.x = _CGRectGetWidth(bounds) / 2 - dragPoint.x;
02835     dragViewOffset.y = _CGRectGetHeight(bounds) / 2 - dragPoint.y;
02836 
02837     return view;
02838 }
02839 
02846 - (CPView)_dragViewForColumn:(int)theColumnIndex event:(CPEvent)theDragEvent offset:(CPPointPointer)theDragViewOffset
02847 {
02848     var dragView = [[_CPColumnDragView alloc] initWithLineColor:[self gridColor]],
02849         tableColumn = [[self tableColumns] objectAtIndex:theColumnIndex],
02850         bounds = _CGRectMake(0.0, 0.0, [tableColumn width], _CGRectGetHeight([self exposedRect]) + 23.0),
02851         columnRect = [self rectOfColumn:theColumnIndex],
02852         headerView = [tableColumn headerView],
02853         row = [_exposedRows firstIndex];
02854 
02855     while (row !== CPNotFound)
02856     {
02857         var dataView = [self _newDataViewForRow:row tableColumn:tableColumn],
02858             dataViewFrame = [self frameOfDataViewAtColumn:theColumnIndex row:row];
02859 
02860         // Only one column is ever dragged so we just place the view at
02861         dataViewFrame.origin.x = 0.0;
02862 
02863         // Offset by table header height - scroll position
02864         dataViewFrame.origin.y = ( _CGRectGetMinY(dataViewFrame) - _CGRectGetMinY([self exposedRect]) ) + 23.0;
02865         [dataView setFrame:dataViewFrame];
02866 
02867         [dataView setObjectValue:[self _objectValueForTableColumn:tableColumn row:row]];
02868         [dragView addSubview:dataView];
02869 
02870         row = [_exposedRows indexGreaterThanIndex:row];
02871     }
02872 
02873     // Add the column header view
02874     var headerFrame = [headerView frame];
02875     headerFrame.origin = _CGPointMakeZero();
02876 
02877     var columnHeaderView = [[_CPTableColumnHeaderView alloc] initWithFrame:headerFrame];
02878     [columnHeaderView setStringValue:[headerView stringValue]];
02879     [columnHeaderView setThemeState:[headerView themeState]];
02880     [dragView addSubview:columnHeaderView];
02881 
02882     [dragView setBackgroundColor:[CPColor whiteColor]];
02883     [dragView setAlphaValue:0.7];
02884     [dragView setFrame:bounds];
02885 
02886     return dragView;
02887 }
02888 
02893 - (void)setDraggingSourceOperationMask:(CPDragOperation)mask forLocal:(BOOL)isLocal
02894 {
02895     //ignore local for the time being since only one capp app can run at a time...
02896     _dragOperationDefaultMask = mask;
02897 }
02898 
02904 - (void)setDropRow:(CPInteger)row dropOperation:(CPTableViewDropOperation)operation
02905 {
02906     if (row > [self numberOfRows] && operation === CPTableViewDropOn)
02907     {
02908         var numberOfRows = [self numberOfRows] + 1,
02909             reason = @"Attempt to set dropRow=" + row +
02910                      " dropOperation=CPTableViewDropOn when [0 - " + numberOfRows + "] is valid range of rows.";
02911 
02912         [[CPException exceptionWithName:@"Error" reason:reason userInfo:nil] raise];
02913     }
02914 
02915 
02916     _retargetedDropRow = row;
02917     _retargetedDropOperation = operation;
02918 }
02919 
02931 - (void)setDraggingDestinationFeedbackStyle:(CPTableViewDraggingDestinationFeedbackStyle)aStyle
02932 {
02933     //FIX ME: this should vary up the highlight color, currently nothing is being done with it
02934     _destinationDragStyle = aStyle;
02935 }
02936 
02947 - (CPTableViewDraggingDestinationFeedbackStyle)draggingDestinationFeedbackStyle
02948 {
02949     return _destinationDragStyle;
02950 }
02951 
02957 - (void)setVerticalMotionCanBeginDrag:(BOOL)aFlag
02958 {
02959     _verticalMotionCanDrag = aFlag;
02960 }
02961 
02965 - (BOOL)verticalMotionCanBeginDrag
02966 {
02967     return _verticalMotionCanDrag;
02968 }
02969 
02970 - (CPTableColumn)_tableColumnForSortDescriptor:(CPSortDescriptor)theSortDescriptor
02971 {
02972     var tableColumns = [self tableColumns];
02973 
02974     for (var i = 0; i < [tableColumns count]; i++)
02975     {
02976         var tableColumn = [tableColumns objectAtIndex:i],
02977             sortDescriptorPrototype = [tableColumn sortDescriptorPrototype];
02978 
02979         if (!sortDescriptorPrototype)
02980             continue;
02981 
02982         if ([sortDescriptorPrototype key] === [theSortDescriptor key]
02983             && [sortDescriptorPrototype selector] === [theSortDescriptor selector])
02984         {
02985             return tableColumn;
02986         }
02987     }
02988 
02989     return nil;
02990 }
02991 
02997 - (void)setSortDescriptors:(CPArray)sortDescriptors
02998 {
02999     var oldSortDescriptors = [[self sortDescriptors] copy],
03000         newSortDescriptors = nil;
03001 
03002     if (sortDescriptors == nil)
03003         newSortDescriptors = [CPArray array];
03004     else
03005         newSortDescriptors = [CPArray arrayWithArray:sortDescriptors];
03006 
03007     if ([newSortDescriptors isEqual:oldSortDescriptors])
03008         return;
03009 
03010     _sortDescriptors = newSortDescriptors;
03011 
03012     var oldColumn = nil,
03013         newColumn = nil;
03014 
03015     if ([newSortDescriptors count] > 0)
03016     {
03017         var newMainSortDescriptor = [newSortDescriptors objectAtIndex:0];
03018         newColumn = [self _tableColumnForSortDescriptor:newMainSortDescriptor];
03019     }
03020 
03021     if ([oldSortDescriptors count] > 0)
03022     {
03023         var oldMainSortDescriptor = [oldSortDescriptors objectAtIndex:0];
03024         oldColumn = [self _tableColumnForSortDescriptor:oldMainSortDescriptor];
03025     }
03026 
03027     var image = [newMainSortDescriptor ascending] ? [self _tableHeaderSortImage] : [self _tableHeaderReverseSortImage];
03028     [self setIndicatorImage:nil inTableColumn:oldColumn];
03029     [self setIndicatorImage:image inTableColumn:newColumn];
03030 
03031     [self _sendDataSourceSortDescriptorsDidChange:oldSortDescriptors];
03032 
03033     var binderClass = [[self class] _binderClassForBinding:@"sortDescriptors"];
03034     [[binderClass getBinding:@"sortDescriptors" forObject:self] reverseSetValueFor:@"sortDescriptors"];
03035 }
03036 
03040 - (CPArray)sortDescriptors
03041 {
03042     return _sortDescriptors;
03043 }
03044 
03045 
03049 - (id)_objectValueForTableColumn:(CPTableColumn)aTableColumn row:(CPInteger)aRowIndex
03050 {
03051     var tableColumnUID = [aTableColumn UID],
03052         tableColumnObjectValues = _objectValues[tableColumnUID];
03053 
03054     if (!tableColumnObjectValues)
03055     {
03056         tableColumnObjectValues = [];
03057         _objectValues[tableColumnUID] = tableColumnObjectValues;
03058     }
03059 
03060     var objectValue = tableColumnObjectValues[aRowIndex];
03061 
03062     // tableView:objectValueForTableColumn:row: is optional if content bindings are in place.
03063     if (objectValue === undefined)
03064     {
03065         if (_implementedDataSourceMethods & CPTableViewDataSource_tableView_objectValueForTableColumn_row_)
03066         {
03067             objectValue = [_dataSource tableView:self objectValueForTableColumn:aTableColumn row:aRowIndex];
03068             tableColumnObjectValues[aRowIndex] = objectValue;
03069         }
03070         else if (![self infoForBinding:@"content"])
03071         {
03072             CPLog(@"no content binding established and data source " + [_dataSource description] + " does not implement tableView:objectValueForTableColumn:row:");
03073         }
03074     }
03075 
03076     return objectValue;
03077 }
03078 
03079 
03083 - (CGRect)exposedRect
03084 {
03085     if (!_exposedRect)
03086     {
03087         var superview = [self superview];
03088 
03089         // FIXME: Should we be rect intersecting in case
03090         // there are multiple views in the clip view?
03091         if ([superview isKindOfClass:[CPClipView class]])
03092             _exposedRect = [superview bounds];
03093 
03094         else
03095             _exposedRect = [self bounds];
03096     }
03097 
03098     return _exposedRect;
03099 }
03100 
03104 - (void)load
03105 {
03106     if (_reloadAllRows)
03107     {
03108         [self _unloadDataViewsInRows:_exposedRows columns:_exposedColumns];
03109 
03110         _exposedRows = [CPIndexSet indexSet];
03111         _exposedColumns = [CPIndexSet indexSet];
03112 
03113         _reloadAllRows = NO;
03114     }
03115 
03116     var exposedRect = [self exposedRect],
03117         exposedRows = [CPIndexSet indexSetWithIndexesInRange:[self rowsInRect:exposedRect]],
03118         exposedColumns = [self columnIndexesInRect:exposedRect],
03119         obscuredRows = [_exposedRows copy],
03120         obscuredColumns = [_exposedColumns copy];
03121 
03122     [obscuredRows removeIndexes:exposedRows];
03123     [obscuredColumns removeIndexes:exposedColumns];
03124 
03125     var newlyExposedRows = [exposedRows copy],
03126         newlyExposedColumns = [exposedColumns copy];
03127 
03128     [newlyExposedRows removeIndexes:_exposedRows];
03129     [newlyExposedColumns removeIndexes:_exposedColumns];
03130 
03131     var previouslyExposedRows = [exposedRows copy],
03132         previouslyExposedColumns = [exposedColumns copy];
03133 
03134     [previouslyExposedRows removeIndexes:newlyExposedRows];
03135     [previouslyExposedColumns removeIndexes:newlyExposedColumns];
03136 
03137     [self _unloadDataViewsInRows:previouslyExposedRows columns:obscuredColumns];
03138     [self _unloadDataViewsInRows:obscuredRows columns:previouslyExposedColumns];
03139     [self _unloadDataViewsInRows:obscuredRows columns:obscuredColumns];
03140     [self _unloadDataViewsInRows:newlyExposedRows columns:newlyExposedColumns];
03141 
03142     [self _loadDataViewsInRows:previouslyExposedRows columns:newlyExposedColumns];
03143     [self _loadDataViewsInRows:newlyExposedRows columns:previouslyExposedColumns];
03144     [self _loadDataViewsInRows:newlyExposedRows columns:newlyExposedColumns];
03145 
03146     _exposedRows = exposedRows;
03147     _exposedColumns = exposedColumns;
03148 
03149     [_tableDrawView setFrame:exposedRect];
03150 
03151     [self setNeedsDisplay:YES];
03152 
03153     // Now clear all the leftovers
03154     // FIXME: this could be faster!
03155     for (var identifier in _cachedDataViews)
03156     {
03157         var dataViews = _cachedDataViews[identifier],
03158             count = dataViews.length;
03159 
03160         while (count--)
03161             [dataViews[count] removeFromSuperview];
03162     }
03163 
03164     // if we have any columns to remove do that here
03165     if ([_differedColumnDataToRemove count])
03166     {
03167         for (var i = 0; i < _differedColumnDataToRemove.length; i++)
03168         {
03169             var data = _differedColumnDataToRemove[i],
03170                 column = data.column;
03171 
03172             [column setHidden:data.shouldBeHidden];
03173             [_tableColumns removeObject:column];
03174         }
03175         [_differedColumnDataToRemove removeAllObjects];
03176     }
03177 
03178 }
03179 
03183 - (void)_unloadDataViewsInRows:(CPIndexSet)rows columns:(CPIndexSet)columns
03184 {
03185     if (![rows count] || ![columns count])
03186         return;
03187 
03188     var rowArray = [],
03189         columnArray = [];
03190 
03191     [rows getIndexes:rowArray maxCount:-1 inIndexRange:nil];
03192     [columns getIndexes:columnArray maxCount:-1 inIndexRange:nil];
03193 
03194     var columnIndex = 0,
03195         columnsCount = columnArray.length;
03196 
03197     for (; columnIndex < columnsCount; ++columnIndex)
03198     {
03199         var column = columnArray[columnIndex],
03200             tableColumn = _tableColumns[column],
03201             tableColumnUID = [tableColumn UID],
03202             rowIndex = 0,
03203             rowsCount = rowArray.length;
03204 
03205         for (; rowIndex < rowsCount; ++rowIndex)
03206         {
03207             var row = rowArray[rowIndex],
03208                 dataViews = _dataViewsForTableColumns[tableColumnUID];
03209 
03210             if (!dataViews || row >= dataViews.length)
03211                 continue;
03212 
03213             var dataView = [dataViews objectAtIndex:row];
03214 
03215             [dataViews replaceObjectAtIndex:row withObject:nil];
03216 
03217             [self _enqueueReusableDataView:dataView];
03218         }
03219     }
03220 }
03221 
03225 - (void)_loadDataViewsInRows:(CPIndexSet)rows columns:(CPIndexSet)columns
03226 {
03227     if (![rows count] || ![columns count])
03228         return;
03229 
03230     var rowArray = [],
03231         rowRects = [],
03232         columnArray = [];
03233 
03234     [rows getIndexes:rowArray maxCount:-1 inIndexRange:nil];
03235     [columns getIndexes:columnArray maxCount:-1 inIndexRange:nil];
03236 
03237     UPDATE_COLUMN_RANGES_IF_NECESSARY();
03238 
03239     var columnIndex = 0,
03240         columnsCount = columnArray.length;
03241 
03242     for (; columnIndex < columnsCount; ++columnIndex)
03243     {
03244         var column = columnArray[columnIndex],
03245             tableColumn = _tableColumns[column];
03246 
03247         if ([tableColumn isHidden] || tableColumn === _draggedColumn)
03248             continue;
03249 
03250         var tableColumnUID = [tableColumn UID];
03251 
03252         if (!_dataViewsForTableColumns[tableColumnUID])
03253             _dataViewsForTableColumns[tableColumnUID] = [];
03254 
03255         var rowIndex = 0,
03256             rowsCount = rowArray.length,
03257             isColumnSelected = [_selectedColumnIndexes containsIndex:column];
03258 
03259         for (; rowIndex < rowsCount; ++rowIndex)
03260         {
03261             var row = rowArray[rowIndex],
03262                 dataView = [self _newDataViewForRow:row tableColumn:tableColumn],
03263                 isButton = [dataView isKindOfClass:[CPButton class]],
03264                 isTextField = [dataView isKindOfClass:[CPTextField class]];
03265 
03266             [dataView setFrame:[self frameOfDataViewAtColumn:column row:row]];
03267             [dataView setObjectValue:[self _objectValueForTableColumn:tableColumn row:row]];
03268 
03269             // This gives the table column an opportunity to apply its bindings.
03270             // It will override the value set above if there is a binding.
03271             [tableColumn _prepareDataView:dataView forRow:row];
03272 
03273             if (isColumnSelected || [self isRowSelected:row])
03274                 [dataView setThemeState:CPThemeStateSelectedDataView];
03275             else
03276                 [dataView unsetThemeState:CPThemeStateSelectedDataView];
03277 
03278             // FIX ME: for performance reasons we might consider diverging from cocoa and moving this to the reloadData method
03279             if (_implementedDelegateMethods & CPTableViewDelegate_tableView_isGroupRow_)
03280             {
03281                 if ([_delegate tableView:self isGroupRow:row])
03282                 {
03283                     [_groupRows addIndex:row];
03284                     [dataView setThemeState:CPThemeStateGroupRow];
03285                 }
03286                 else
03287                 {
03288                     [_groupRows removeIndexesInRange:CPMakeRange(row, 1)];
03289                     [dataView unsetThemeState:CPThemeStateGroupRow];
03290                 }
03291 
03292                 [self setNeedsDisplay:YES]
03293             }
03294 
03295             if (_implementedDelegateMethods & CPTableViewDelegate_tableView_willDisplayView_forTableColumn_row_)
03296                 [_delegate tableView:self willDisplayView:dataView forTableColumn:tableColumn row:row];
03297 
03298             if ([dataView superview] !== self)
03299                 [self addSubview:dataView];
03300 
03301             _dataViewsForTableColumns[tableColumnUID][row] = dataView;
03302 
03303             if (isButton || (_editingCellIndex && _editingCellIndex.x === column && _editingCellIndex.y === row))
03304             {
03305                 if (isTextField)
03306                 {
03307                     [dataView setEditable:YES];
03308                     [dataView setSendsActionOnEndEditing:YES];
03309                     [dataView setSelectable:YES];
03310                     [dataView selectText:nil];
03311                     [dataView setBezeled:YES];
03312                     [dataView setDelegate:self];
03313                 }
03314 
03315                 [dataView setTarget:self];
03316                 [dataView setAction:@selector(_commitDataViewObjectValue:)];
03317                 dataView.tableViewEditedColumnObj = tableColumn;
03318                 dataView.tableViewEditedRowIndex = row;
03319             }
03320             else if (isTextField)
03321             {
03322                 [dataView setEditable:NO];
03323                 [dataView setSelectable:NO];
03324             }
03325         }
03326     }
03327 }
03328 
03332 - (void)_layoutDataViewsInRows:(CPIndexSet)rows columns:(CPIndexSet)columns
03333 {
03334     var rowArray = [],
03335         columnArray = [];
03336 
03337     [rows getIndexes:rowArray maxCount:-1 inIndexRange:nil];
03338     [columns getIndexes:columnArray maxCount:-1 inIndexRange:nil];
03339 
03340     var columnIndex = 0,
03341         columnsCount = columnArray.length;
03342 
03343     for (; columnIndex < columnsCount; ++columnIndex)
03344     {
03345         var column = columnArray[columnIndex],
03346             tableColumn = _tableColumns[column],
03347             tableColumnUID = [tableColumn UID],
03348             dataViewsForTableColumn = _dataViewsForTableColumns[tableColumnUID],
03349             rowIndex = 0,
03350             rowsCount = rowArray.length;
03351 
03352         for (; rowIndex < rowsCount; ++rowIndex)
03353         {
03354             var row = rowArray[rowIndex],
03355                 dataView = dataViewsForTableColumn[row];
03356 
03357             [dataView setFrame:[self frameOfDataViewAtColumn:column row:row]];
03358         }
03359     }
03360 }
03361 
03367 - (void)_commitDataViewObjectValue:(id)sender
03368 {
03369     _editingCellIndex = nil;
03370 
03371     if (_implementedDataSourceMethods & CPTableViewDataSource_tableView_setObjectValue_forTableColumn_row_)
03372         [_dataSource tableView:self setObjectValue:[sender objectValue] forTableColumn:sender.tableViewEditedColumnObj row:sender.tableViewEditedRowIndex];
03373 
03374     // Allow the column binding to do a reverse set. Note that we do this even if the data source method above
03375     // is implemented.
03376     [sender.tableViewEditedColumnObj _reverseSetDataView:sender forRow:sender.tableViewEditedRowIndex];
03377 
03378     if ([sender respondsToSelector:@selector(setEditable:)])
03379         [sender setEditable:NO];
03380 
03381     if ([sender respondsToSelector:@selector(setSelectable:)])
03382         [sender setSelectable:NO];
03383 
03384     if ([sender isKindOfClass:[CPTextField class]])
03385         [sender setBezeled:NO];
03386 
03387     [self reloadDataForRowIndexes:[CPIndexSet indexSetWithIndex:sender.tableViewEditedRowIndex]
03388                     columnIndexes:[CPIndexSet indexSetWithIndex:[_tableColumns indexOfObject:sender.tableViewEditedColumnObj]]];
03389 
03390     [[self window] makeFirstResponder:self];
03391 
03392 }
03393 
03399 - (void)controlTextDidBlur:(CPNotification)theNotification
03400 {
03401     var dataView = [theNotification object];
03402 
03403     if ([dataView respondsToSelector:@selector(setEditable:)])
03404         [dataView setEditable:NO];
03405 
03406     if ([dataView respondsToSelector:@selector(setSelectable:)])
03407         [dataView setSelectable:NO];
03408 
03409     if ([dataView isKindOfClass:[CPTextField class]])
03410         [dataView setBezeled:NO];
03411 
03412     _editingCellIndex = nil;
03413 }
03414 
03418 - (CPView)_newDataViewForRow:(CPInteger)aRow tableColumn:(CPTableColumn)aTableColumn
03419 {
03420     if ((_implementedDelegateMethods & CPTableViewDelegate_tableView_dataViewForTableColumn_row_))
03421     {
03422         var dataView = [_delegate tableView:self dataViewForTableColumn:aTableColumn row:aRow];
03423         [aTableColumn setDataView:dataView];
03424     }
03425 
03426 
03427     return [aTableColumn _newDataViewForRow:aRow];
03428 }
03429 
03433 - (void)_enqueueReusableDataView:(CPView)aDataView
03434 {
03435     if (!aDataView)
03436         return;
03437 
03438     // FIXME: yuck!
03439     var identifier = aDataView.identifier;
03440 
03441     if (!_cachedDataViews[identifier])
03442         _cachedDataViews[identifier] = [aDataView];
03443     else
03444         _cachedDataViews[identifier].push(aDataView);
03445 }
03446 
03451 - (void)setFrameSize:(CGSize)aSize
03452 {
03453     [super setFrameSize:aSize];
03454 
03455     if (_headerView)
03456         [_headerView setFrameSize:_CGSizeMake(_CGRectGetWidth([self frame]), _CGRectGetHeight([_headerView frame]))];
03457 
03458     _exposedRect = nil;
03459 }
03460 
03464 - (void)setFrameOrigin:(CGPoint)aPoint
03465 {
03466     [super setFrameOrigin:aPoint];
03467 
03468     _exposedRect = nil;
03469 }
03470 
03474 - (void)setBoundsOrigin:(CGPoint)aPoint
03475 {
03476     [super setBoundsOrigin:aPoint];
03477 
03478     _exposedRect = nil;
03479 }
03480 
03484 - (void)setBoundsSize:(CGSize)aSize
03485 {
03486     [super setBoundsSize:aSize];
03487 
03488     _exposedRect = nil;
03489 }
03490 
03494 - (void)setNeedsDisplay:(BOOL)aFlag
03495 {
03496     [super setNeedsDisplay:aFlag];
03497     [_tableDrawView setNeedsDisplay:aFlag];
03498 }
03499 
03503 - (void)_drawRect:(CGRect)aRect
03504 {
03505     // FIX ME: All three of these methods will likely need to be rewritten for 1.0
03506     // We've got grid drawing in highlightSelection and crap everywhere.
03507 
03508     var exposedRect = [self exposedRect];
03509 
03510     [self drawBackgroundInClipRect:exposedRect];
03511     [self highlightSelectionInClipRect:exposedRect];
03512     [self drawGridInClipRect:exposedRect];
03513 
03514     if (_implementsCustomDrawRow)
03515         [self _drawRows:_exposedRows clipRect:exposedRect];
03516 }
03517 
03523 - (void)drawBackgroundInClipRect:(CGRect)aRect
03524 {
03525     if (!_usesAlternatingRowBackgroundColors)
03526         return;
03527 
03528     var rowColors = [self alternatingRowBackgroundColors],
03529         colorCount = [rowColors count];
03530 
03531     if (colorCount === 0)
03532         return;
03533 
03534     var context = [[CPGraphicsContext currentContext] graphicsPort];
03535 
03536     if (colorCount === 1)
03537     {
03538         CGContextSetFillColor(context, rowColors[0]);
03539         CGContextFillRect(context, aRect);
03540 
03541         return;
03542     }
03543 
03544     var exposedRows = [self _unboundedRowsInRect:aRect],
03545         firstRow = FLOOR(exposedRows.location / colorCount) * colorCount,
03546         lastRow = CPMaxRange(exposedRows),
03547         colorIndex = 0,
03548         groupRowRects = [];
03549 
03550     //loop through each color so we only draw once for each color
03551     while (colorIndex < colorCount)
03552     {
03553         CGContextBeginPath(context);
03554         for (var row = firstRow + colorIndex; row <= lastRow; row += colorCount)
03555         {
03556             // if it's not a group row draw it otherwise we draw it later
03557             if (![_groupRows containsIndex:row])
03558                 CGContextAddRect(context, CGRectIntersection(aRect, [self _rectOfRow:row checkRange:NO]));
03559             else
03560                 groupRowRects.push(CGRectIntersection(aRect, [self _rectOfRow:row checkRange:NO]));
03561         }
03562         CGContextClosePath(context);
03563 
03564         CGContextSetFillColor(context, rowColors[colorIndex]);
03565         CGContextFillPath(context);
03566 
03567         colorIndex++;
03568     }
03569 
03570     [self _drawGroupRowsForRects:groupRowRects];
03571 }
03572 
03577 - (void)drawGridInClipRect:(CGRect)aRect
03578 {
03579     var context = [[CPGraphicsContext currentContext] graphicsPort],
03580         gridStyleMask = [self gridStyleMask];
03581 
03582     if (!(gridStyleMask & (CPTableViewSolidHorizontalGridLineMask | CPTableViewSolidVerticalGridLineMask)))
03583         return;
03584 
03585     CGContextBeginPath(context);
03586 
03587     if (gridStyleMask & CPTableViewSolidHorizontalGridLineMask)
03588     {
03589         var exposedRows = [self _unboundedRowsInRect:aRect],
03590             row = exposedRows.location,
03591             lastRow = CPMaxRange(exposedRows) - 1,
03592             rowY = -0.5,
03593             minX = _CGRectGetMinX(aRect),
03594             maxX = _CGRectGetMaxX(aRect);
03595 
03596         for (; row <= lastRow; ++row)
03597         {
03598             // grab each row rect and add the top and bottom lines
03599             var rowRect = [self _rectOfRow:row checkRange:NO],
03600                 rowY = _CGRectGetMaxY(rowRect) - 0.5;
03601 
03602             CGContextMoveToPoint(context, minX, rowY);
03603             CGContextAddLineToPoint(context, maxX, rowY);
03604         }
03605 
03606         if (_rowHeight > 0.0)
03607         {
03608             var rowHeight = _rowHeight + _intercellSpacing.height,
03609                 totalHeight = _CGRectGetMaxY(aRect);
03610 
03611             while (rowY < totalHeight)
03612             {
03613                 rowY += rowHeight;
03614 
03615                 CGContextMoveToPoint(context, minX, rowY);
03616                 CGContextAddLineToPoint(context, maxX, rowY);
03617             }
03618         }
03619     }
03620 
03621     if (gridStyleMask & CPTableViewSolidVerticalGridLineMask)
03622     {
03623         var exposedColumnIndexes = [self columnIndexesInRect:aRect],
03624             columnsArray = [];
03625 
03626         [exposedColumnIndexes getIndexes:columnsArray maxCount:-1 inIndexRange:nil];
03627 
03628         var columnArrayIndex = 0,
03629             columnArrayCount = columnsArray.length,
03630             minY = _CGRectGetMinY(aRect),
03631             maxY = _CGRectGetMaxY(aRect);
03632 
03633         for (; columnArrayIndex < columnArrayCount; ++columnArrayIndex)
03634         {
03635             var columnRect = [self rectOfColumn:columnsArray[columnArrayIndex]],
03636                 columnX = _CGRectGetMaxX(columnRect) - 0.5;
03637 
03638             CGContextMoveToPoint(context, columnX, minY);
03639             CGContextAddLineToPoint(context, columnX, maxY);
03640         }
03641     }
03642 
03643     CGContextClosePath(context);
03644     CGContextSetStrokeColor(context, [self gridColor]);
03645     CGContextStrokePath(context);
03646 }
03647 
03653 - (void)highlightSelectionInClipRect:(CGRect)aRect
03654 {
03655     if (_selectionHighlightStyle === CPTableViewSelectionHighlightStyleNone)
03656         return;
03657 
03658     var context = [[CPGraphicsContext currentContext] graphicsPort],
03659         indexes = [],
03660         rectSelector = @selector(rectOfRow:);
03661 
03662     if ([_selectedRowIndexes count] >= 1)
03663     {
03664         var exposedRows = [CPIndexSet indexSetWithIndexesInRange:[self rowsInRect:aRect]],
03665             firstRow = [exposedRows firstIndex],
03666             exposedRange = CPMakeRange(firstRow, [exposedRows lastIndex] - firstRow + 1);
03667 
03668         [_selectedRowIndexes getIndexes:indexes maxCount:-1 inIndexRange:exposedRange];
03669     }
03670     else if ([_selectedColumnIndexes count] >= 1)
03671     {
03672         rectSelector = @selector(rectOfColumn:);
03673 
03674         var exposedColumns = [self columnIndexesInRect:aRect],
03675             firstColumn = [exposedColumns firstIndex],
03676             exposedRange = CPMakeRange(firstColumn, [exposedColumns lastIndex] - firstColumn + 1);
03677 
03678         [_selectedColumnIndexes getIndexes:indexes maxCount:-1 inIndexRange:exposedRange];
03679     }
03680 
03681     var count = count2 = [indexes count];
03682 
03683     if (!count)
03684         return;
03685 
03686     var drawGradient = (_selectionHighlightStyle === CPTableViewSelectionHighlightStyleSourceList && [_selectedRowIndexes count] >= 1),
03687         deltaHeight = 0.5 * (_gridStyleMask & CPTableViewSolidHorizontalGridLineMask);
03688 
03689     CGContextBeginPath(context);
03690 
03691     if (drawGradient)
03692     {
03693         var gradientCache = [self selectionGradientColors],
03694             topLineColor = [gradientCache objectForKey:CPSourceListTopLineColor],
03695             bottomLineColor = [gradientCache objectForKey:CPSourceListBottomLineColor],
03696             gradientColor = [gradientCache objectForKey:CPSourceListGradient];
03697     }
03698 
03699     var normalSelectionHighlightColor = [self selectionHighlightColor];
03700 
03701     // don't do these lookups if there are no group rows
03702     if ([_groupRows count])
03703     {
03704         var topGroupLineColor = [CPColor colorWithCalibratedWhite:212.0 / 255.0 alpha:1.0],
03705             bottomGroupLineColor = [CPColor colorWithCalibratedWhite:185.0 / 255.0 alpha:1.0],
03706             gradientGroupColor = CGGradientCreateWithColorComponents(CGColorSpaceCreateDeviceRGB(), [212.0 / 255.0, 212.0 / 255.0, 212.0 / 255.0,1.0, 197.0 / 255.0, 197.0 / 255.0, 197.0 / 255.0,1.0], [0,1], 2);
03707     }
03708 
03709     while (count--)
03710     {
03711         var currentIndex = indexes[count],
03712             rowRect = CGRectIntersection(objj_msgSend(self, rectSelector, currentIndex), aRect);
03713 
03714         // group rows get the same highlight style as other rows if they're source list...
03715         if (!drawGradient)
03716             var shouldUseGroupGradient = [_groupRows containsIndex:currentIndex];
03717 
03718         if (drawGradient || shouldUseGroupGradient)
03719         {
03720             var minX = _CGRectGetMinX(rowRect),
03721                 minY = _CGRectGetMinY(rowRect),
03722                 maxX = _CGRectGetMaxX(rowRect),
03723                 maxY = _CGRectGetMaxY(rowRect) - deltaHeight;
03724 
03725             if (!drawGradient)
03726             {
03727                 //If there is no source list gradient we need to close the selection path and fill it now
03728                 [normalSelectionHighlightColor setFill];
03729                 CGContextClosePath(context);
03730                 CGContextFillPath(context);
03731                 CGContextBeginPath(context);
03732             }
03733             CGContextAddRect(context, rowRect);
03734 
03735             CGContextDrawLinearGradient(context, (shouldUseGroupGradient) ? gradientGroupColor : gradientColor, rowRect.origin, _CGPointMake(minX, maxY), 0);
03736             CGContextClosePath(context);
03737 
03738             CGContextBeginPath(context);
03739             CGContextMoveToPoint(context, minX, minY + .5);
03740             CGContextAddLineToPoint(context, maxX, minY + .5);
03741             CGContextClosePath(context);
03742             CGContextSetStrokeColor(context, (shouldUseGroupGradient) ? topGroupLineColor : topLineColor);
03743             CGContextStrokePath(context);
03744 
03745             CGContextBeginPath(context);
03746             CGContextMoveToPoint(context, minX, maxY - .5);
03747             CGContextAddLineToPoint(context, maxX, maxY - .5);
03748             CGContextClosePath(context);
03749             CGContextSetStrokeColor(context, (shouldUseGroupGradient) ? bottomGroupLineColor : bottomLineColor);
03750             CGContextStrokePath(context);
03751         }
03752         else
03753             CGContextAddRect(context, rowRect);
03754     }
03755 
03756     CGContextClosePath(context);
03757 
03758     if (!drawGradient)
03759     {
03760         [normalSelectionHighlightColor setFill];
03761         CGContextFillPath(context);
03762     }
03763 
03764     CGContextBeginPath(context);
03765     var gridStyleMask = [self gridStyleMask];
03766     for (var i = 0; i < count2; i++)
03767     {
03768          var rect = objj_msgSend(self, rectSelector, indexes[i]),
03769              minX = _CGRectGetMinX(rect) - 0.5,
03770              maxX = _CGRectGetMaxX(rect) - 0.5,
03771              minY = _CGRectGetMinY(rect) - 0.5,
03772              maxY = _CGRectGetMaxY(rect) - 0.5;
03773 
03774         if ([_selectedRowIndexes count] >= 1 && gridStyleMask & CPTableViewSolidVerticalGridLineMask)
03775         {
03776             var exposedColumns = [self columnIndexesInRect:aRect],
03777                 exposedColumnIndexes = [],
03778                 firstExposedColumn = [exposedColumns firstIndex],
03779                 exposedRange = CPMakeRange(firstExposedColumn, [exposedColumns lastIndex] - firstExposedColumn + 1);
03780             [exposedColumns getIndexes:exposedColumnIndexes maxCount:-1 inIndexRange:exposedRange];
03781             var exposedColumnCount = [exposedColumnIndexes count];
03782 
03783             for (var c = firstExposedColumn; c < exposedColumnCount; c++)
03784             {
03785                 var colRect = [self rectOfColumn:exposedColumnIndexes[c]],
03786                     colX = _CGRectGetMaxX(colRect) + 0.5;
03787 
03788                 CGContextMoveToPoint(context, colX, minY);
03789                 CGContextAddLineToPoint(context, colX, maxY);
03790             }
03791         }
03792 
03793         //if the row after the current row is not selected then there is no need to draw the bottom grid line white.
03794         if ([indexes containsObject:indexes[i] + 1])
03795         {
03796             CGContextMoveToPoint(context, minX, maxY);
03797             CGContextAddLineToPoint(context, maxX, maxY);
03798         }
03799     }
03800 
03801     CGContextClosePath(context);
03802     CGContextSetStrokeColor(context, [self currentValueForThemeAttribute:"highlighted-grid-color"]);
03803     CGContextStrokePath(context);
03804 }
03805 
03811 - (void)_drawGroupRowsForRects:(CPArray)rects
03812 {
03813     if (_selectionHighlightStyle === CPTableViewSelectionHighlightStyleSourceList || !rects.length)
03814         return;
03815 
03816     var context = [[CPGraphicsContext currentContext] graphicsPort],
03817         i = rects.length;
03818 
03819     CGContextBeginPath(context);
03820 
03821     var gradientCache = [self selectionGradientColors],
03822         topLineColor = [CPColor colorWithHexString:"d3d3d3"],
03823         bottomLineColor = [CPColor colorWithHexString:"bebebd"],
03824         gradientColor = CGGradientCreateWithColorComponents(CGColorSpaceCreateDeviceRGB(), [220.0 / 255.0, 220.0 / 255.0, 220.0 / 255.0,1.0,
03825                                                                                             199.0 / 255.0, 199.0 / 255.0, 199.0 / 255.0,1.0], [0,1], 2),
03826         drawGradient = YES;
03827 
03828     while (i--)
03829     {
03830         var rowRect = rects[i];
03831 
03832         CGContextAddRect(context, rowRect);
03833 
03834         if (drawGradient)
03835         {
03836             var minX = CGRectGetMinX(rowRect),
03837                 minY = CGRectGetMinY(rowRect),
03838                 maxX = CGRectGetMaxX(rowRect),
03839                 maxY = CGRectGetMaxY(rowRect);
03840 
03841             CGContextDrawLinearGradient(context, gradientColor, rowRect.origin, CGPointMake(minX, maxY), 0);
03842             CGContextClosePath(context);
03843 
03844             CGContextBeginPath(context);
03845             CGContextMoveToPoint(context, minX, minY);
03846             CGContextAddLineToPoint(context, maxX, minY);
03847             CGContextClosePath(context);
03848             CGContextSetStrokeColor(context, topLineColor);
03849             CGContextStrokePath(context);
03850 
03851             CGContextBeginPath(context);
03852             CGContextMoveToPoint(context, minX, maxY);
03853             CGContextAddLineToPoint(context, maxX, maxY - 1);
03854             CGContextClosePath(context);
03855             CGContextSetStrokeColor(context, bottomLineColor);
03856             CGContextStrokePath(context);
03857         }
03858     }
03859 
03860     CGContextClosePath(context);
03861 }
03862 
03866 - (void)_drawRows:(CPIndexSet)rowsIndexes clipRect:(CGRect)clipRect
03867 {
03868     var row = [rowsIndexes firstIndex];
03869 
03870     while (row !== CPNotFound)
03871     {
03872         [self drawRow:row clipRect:CGRectIntersection(clipRect, [self rectOfRow:row])];
03873         row = [rowsIndexes indexGreaterThanIndex:row];
03874     }
03875 }
03876 
03883 - (void)drawRow:(CPInteger)row clipRect:(CGRect)rect
03884 {
03885     // This method does currently nothing in cappuccino. Can be overridden by subclasses.
03886 
03887 }
03888 
03892 - (void)layoutSubviews
03893 {
03894     [self load];
03895 }
03896 
03900 - (void)viewWillMoveToSuperview:(CPView)aView
03901 {
03902     var superview = [self superview],
03903         defaultCenter = [CPNotificationCenter defaultCenter];
03904 
03905     if (superview)
03906     {
03907         [defaultCenter
03908             removeObserver:self
03909                       name:CPViewFrameDidChangeNotification
03910                     object:superview];
03911 
03912         [defaultCenter
03913             removeObserver:self
03914                       name:CPViewBoundsDidChangeNotification
03915                     object:superview];
03916     }
03917 
03918     if ([aView isKindOfClass:[CPClipView class]])
03919     {
03920         [aView setPostsFrameChangedNotifications:YES];
03921         [aView setPostsBoundsChangedNotifications:YES];
03922 
03923         [defaultCenter
03924             addObserver:self
03925                selector:@selector(superviewFrameChanged:)
03926                    name:CPViewFrameDidChangeNotification
03927                  object:aView];
03928 
03929         [defaultCenter
03930             addObserver:self
03931                selector:@selector(superviewBoundsChanged:)
03932                    name:CPViewBoundsDidChangeNotification
03933                  object:aView];
03934     }
03935 }
03936 
03940 - (void)superviewBoundsChanged:(CPNotification)aNotification
03941 {
03942     _exposedRect = nil;
03943 
03944     [self setNeedsDisplay:YES];
03945     [self setNeedsLayout];
03946 }
03947 
03951 - (void)superviewFrameChanged:(CPNotification)aNotification
03952 {
03953     _exposedRect = nil;
03954 
03955     [self tile];
03956 }
03957 
03958 /*
03959     @ignore
03960 */
03961 - (BOOL)tracksMouseOutsideOfFrame
03962 {
03963     return YES;
03964 }
03965 
03966 /*
03967     @ignore
03968 */
03969 - (BOOL)startTrackingAt:(CGPoint)aPoint
03970 {
03971     var row = [self rowAtPoint:aPoint];
03972 
03973     //if the user clicks outside a row then deselect everything
03974     if (row < 0 && _allowsEmptySelection)
03975         [self selectRowIndexes:[CPIndexSet indexSet] byExtendingSelection:NO];
03976 
03977     [self _noteSelectionIsChanging];
03978 
03979     if ([self mouseDownFlags] & CPShiftKeyMask)
03980         _selectionAnchorRow = (ABS([_selectedRowIndexes firstIndex] - row) < ABS([_selectedRowIndexes lastIndex] - row)) ?
03981             [_selectedRowIndexes firstIndex] : [_selectedRowIndexes lastIndex];
03982     else
03983         _selectionAnchorRow = row;
03984 
03985 
03986     //set ivars for startTrackingPoint and time...
03987     _startTrackingPoint = aPoint;
03988     _startTrackingTimestamp = new Date();
03989 
03990     if (_implementedDataSourceMethods & CPTableViewDataSource_tableView_setObjectValue_forTableColumn_row_)
03991         _trackingPointMovedOutOfClickSlop = NO;
03992 
03993     // if the table has drag support then we use mouseUp to select a single row.
03994     // otherwise it uses mouse down.
03995     if (row >= 0 && !(_implementedDataSourceMethods & CPTableViewDataSource_tableView_writeRowsWithIndexes_toPasteboard_))
03996         [self _updateSelectionWithMouseAtRow:row];
03997 
03998     [[self window] makeFirstResponder:self];
03999     return YES;
04000 }
04001 
04005 - (CPMenu)menuForEvent:(CPEvent)theEvent
04006 {
04007     if (!(_implementedDelegateMethods & CPTableViewDelegate_tableViewMenuForTableColumn_Row_))
04008         return [super menuForEvent:theEvent];
04009 
04010     var location = [self convertPoint:[theEvent locationInWindow] fromView:nil],
04011         row = [self rowAtPoint:location],
04012         column = [self columnAtPoint:location],
04013         tableColumn = [[self tableColumns] objectAtIndex:column];
04014 
04015     return [_delegate tableView:self menuForTableColumn:tableColumn row:row];
04016 }
04017 
04018 /*
04019     @ignore
04020 */
04021 - (void)trackMouse:(CPEvent)anEvent
04022 {
04023     // Prevent CPControl from eating the mouse events when we are in a drag session
04024     if (![_draggedRowIndexes count])
04025     {
04026         [self autoscroll:anEvent];
04027         [super trackMouse:anEvent];
04028     }
04029     else
04030         [CPApp sendEvent:anEvent];
04031 }
04032 
04033 /*
04034     @ignore
04035 */
04036 - (BOOL)continueTracking:(CGPoint)lastPoint at:(CGPoint)aPoint
04037 {
04038     var row = [self rowAtPoint:aPoint];
04039 
04040     // begin the drag is the datasource lets us, we've move at least +-3px vertical or horizontal,
04041     // or we're dragging from selected rows and we haven't begun a drag session
04042     if (!_isSelectingSession && _implementedDataSourceMethods & CPTableViewDataSource_tableView_writeRowsWithIndexes_toPasteboard_)
04043     {
04044         if (row >= 0 && (ABS(_startTrackingPoint.x - aPoint.x) > 3 || (_verticalMotionCanDrag && ABS(_startTrackingPoint.y - aPoint.y) > 3)) ||
04045             ([_selectedRowIndexes containsIndex:row]))
04046         {
04047             if ([_selectedRowIndexes containsIndex:row])
04048                 _draggedRowIndexes = [[CPIndexSet alloc] initWithIndexSet:_selectedRowIndexes];
04049             else
04050                 _draggedRowIndexes = [CPIndexSet indexSetWithIndex:row];
04051 
04052 
04053             //ask the datasource for the data
04054             var pboard = [CPPasteboard pasteboardWithName:CPDragPboard];
04055 
04056             if ([self canDragRowsWithIndexes:_draggedRowIndexes atPoint:aPoint] && [_dataSource tableView:self writeRowsWithIndexes:_draggedRowIndexes toPasteboard:pboard])
04057             {
04058                 var currentEvent = [CPApp currentEvent],
04059                     offset = CPPointMakeZero(),
04060                     tableColumns = [_tableColumns objectsAtIndexes:_exposedColumns];
04061 
04062                 // We deviate from the default Cocoa implementation here by asking for a view in stead of an image
04063                 // We support both, but the view preferred over the image because we can mimic the rows we are dragging
04064                 // by re-creating the data views for the dragged rows
04065                 var view = [self dragViewForRowsWithIndexes:_draggedRowIndexes
04066                                                tableColumns:tableColumns
04067                                                       event:currentEvent
04068                                                      offset:offset];
04069 
04070                 if (!view)
04071                 {
04072                     var image = [self dragImageForRowsWithIndexes:_draggedRowIndexes
04073                                                      tableColumns:tableColumns
04074                                                             event:currentEvent
04075                                                            offset:offset];
04076                     view = [[CPImageView alloc] initWithFrame:CPMakeRect(0, 0, [image size].width, [image size].height)];
04077                     [view setImage:image];
04078                 }
04079 
04080                 var bounds = [view bounds],
04081                     viewLocation = CPPointMake(aPoint.x - CGRectGetWidth(bounds) / 2 + offset.x, aPoint.y - CGRectGetHeight(bounds) / 2 + offset.y);
04082                 [self dragView:view at:viewLocation offset:CPPointMakeZero() event:[CPApp currentEvent] pasteboard:pboard source:self slideBack:YES];
04083                 _startTrackingPoint = nil;
04084 
04085                 return NO;
04086             }
04087 
04088             // The delegate disallowed the drag so clear the dragged row indexes
04089             _draggedRowIndexes = [CPIndexSet indexSet];
04090         }
04091         else if (ABS(_startTrackingPoint.x - aPoint.x) < 5 && ABS(_startTrackingPoint.y - aPoint.y) < 5)
04092             return YES;
04093     }
04094 
04095     _isSelectingSession = YES;
04096     if (row >= 0 && row !== _lastTrackedRowIndex)
04097     {
04098         _lastTrackedRowIndex = row;
04099         [self _updateSelectionWithMouseAtRow:row];
04100     }
04101 
04102     if ((_implementedDataSourceMethods & CPTableViewDataSource_tableView_setObjectValue_forTableColumn_row_)
04103         && !_trackingPointMovedOutOfClickSlop)
04104     {
04105         var CLICK_SPACE_DELTA = 5.0; // Stolen from AppKit/Platform/DOM/CPPlatformWindow+DOM.j
04106         if (ABS(aPoint.x - _startTrackingPoint.x) > CLICK_SPACE_DELTA
04107             || ABS(aPoint.y - _startTrackingPoint.y) > CLICK_SPACE_DELTA)
04108         {
04109             _trackingPointMovedOutOfClickSlop = YES;
04110         }
04111     }
04112 
04113     return YES;
04114 }
04115 
04119 - (void)stopTracking:(CGPoint)lastPoint at:(CGPoint)aPoint mouseIsUp:(BOOL)mouseIsUp
04120 {
04121     _isSelectingSession = NO;
04122 
04123     var CLICK_TIME_DELTA = 1000,
04124         columnIndex,
04125         column,
04126         rowIndex,
04127         shouldEdit = YES;
04128 
04129     if (_implementedDataSourceMethods & CPTableViewDataSource_tableView_writeRowsWithIndexes_toPasteboard_)
04130     {
04131         rowIndex = [self rowAtPoint:aPoint];
04132         if (rowIndex !== -1)
04133         {
04134             if ([_draggedRowIndexes count] > 0)
04135             {
04136                 _draggedRowIndexes = [CPIndexSet indexSet];
04137                 return;
04138             }
04139             // if the table has drag support then we use mouseUp to select a single row.
04140              _previouslySelectedRowIndexes = [_selectedRowIndexes copy];
04141             [self _updateSelectionWithMouseAtRow:rowIndex];
04142         }
04143     }
04144 
04145     // Accept either tableView:setObjectValue:forTableColumn:row: delegate method, or a binding.
04146     if (mouseIsUp
04147         && !_trackingPointMovedOutOfClickSlop
04148         && ([[CPApp currentEvent] clickCount] > 1)
04149         && ((_implementedDataSourceMethods & CPTableViewDataSource_tableView_setObjectValue_forTableColumn_row_)
04150             || [self infoForBinding:@"content"]))
04151     {
04152         columnIndex = [self columnAtPoint:lastPoint];
04153         if (columnIndex !== -1)
04154         {
04155             column = _tableColumns[columnIndex];
04156             if ([column isEditable])
04157             {
04158                 rowIndex = [self rowAtPoint:aPoint];
04159                 if (rowIndex !== -1)
04160                 {
04161                     if (_implementedDelegateMethods & CPTableViewDelegate_tableView_shouldEditTableColumn_row_)
04162                         shouldEdit = [_delegate tableView:self shouldEditTableColumn:column row:rowIndex];
04163                     if (shouldEdit)
04164                     {
04165                         [self editColumn:columnIndex row:rowIndex withEvent:nil select:YES];
04166                         return;
04167                     }
04168                 }
04169             }
04170         }
04171 
04172     } //end of editing conditional
04173 
04174     //double click actions
04175     if ([[CPApp currentEvent] clickCount] === 2 && _doubleAction)
04176     {
04177         _clickedRow = [self rowAtPoint:aPoint];
04178         [self sendAction:_doubleAction to:_target];
04179     }
04180 }
04181 
04182 /*
04183     @ignore
04184 */
04185 - (CPDragOperation)draggingEntered:(id)sender
04186 {
04187     var location = [self convertPoint:[sender draggingLocation] fromView:nil],
04188         dropOperation = [self _proposedDropOperationAtPoint:location],
04189         row = [self _proposedRowAtPoint:location];
04190 
04191     if (_retargetedDropRow !== nil)
04192         row = _retargetedDropRow;
04193 
04194     var draggedTypes = [self registeredDraggedTypes],
04195         count = [draggedTypes count],
04196         i = 0;
04197 
04198     for (; i < count; i++)
04199     {
04200         if ([[[sender draggingPasteboard] types] containsObject:[draggedTypes objectAtIndex: i]])
04201             return [self _validateDrop:sender proposedRow:row proposedDropOperation:dropOperation];
04202     }
04203 
04204     return CPDragOperationNone;
04205 }
04206 
04207 /*
04208     @ignore
04209 */
04210 - (void)draggingExited:(id)sender
04211 {
04212     [_dropOperationFeedbackView removeFromSuperview];
04213 }
04214 
04215 /*
04216     @ignore
04217 */
04218 - (void)draggingEnded:(id)sender
04219 {
04220     [self _draggingEnded];
04221 }
04222 
04226 - (void)_draggingEnded
04227 {
04228     _retargetedDropOperation = nil;
04229     _retargetedDropRow = nil;
04230     _draggedRowIndexes = [CPIndexSet indexSet];
04231     [_dropOperationFeedbackView removeFromSuperview];
04232 }
04233 
04234 /*
04235     @ignore
04236 */
04237 - (BOOL)wantsPeriodicDraggingUpdates
04238 {
04239     return YES;
04240 }
04241 
04242 /*
04243     @ignore
04244 */
04245 - (CPTableViewDropOperation)_proposedDropOperationAtPoint:(CGPoint)theDragPoint
04246 {
04247     if (_retargetedDropOperation !== nil)
04248         return _retargetedDropOperation;
04249 
04250     var row = [self _proposedRowAtPoint:theDragPoint],
04251         rowRect = [self rectOfRow:row];
04252 
04253     // If there is no (the default) or to little inter cell spacing we create some room for the CPTableViewDropAbove indicator
04254     // This probably doesn't work if the row height is smaller than or around 5.0
04255     if ([self intercellSpacing].height < 5.0)
04256         rowRect = CPRectInset(rowRect, 0.0, 5.0 - [self intercellSpacing].height);
04257 
04258     // If the altered row rect contains the drag point we show the drop on
04259     // We don't show the drop on indicator if we are dragging below the last row
04260     // in that case we always want to show the drop above indicator
04261     if (CGRectContainsPoint(rowRect, theDragPoint) && row < _numberOfRows)
04262         return CPTableViewDropOn;
04263 
04264     return CPTableViewDropAbove;
04265 }
04266 
04267 /*
04268     @ignore
04269 */
04270 - (CPInteger)_proposedRowAtPoint:(CGPoint)dragPoint
04271 {
04272     var row = [self rowAtPoint:dragPoint],
04273     // Determine if the mouse is currently closer to this row or the row below it
04274         lowerRow = row + 1,
04275         rect = [self rectOfRow:row],
04276         bottomPoint = _CGRectGetMaxY(rect),
04277         bottomThirty = bottomPoint - ((bottomPoint - _CGRectGetMinY(rect)) * 0.3),
04278         numberOfRows = [self numberOfRows];
04279 
04280     if (row < 0)
04281         row = (_CGRectGetMaxY(rect) < dragPoint.y) ? numberOfRows : row;
04282     else if (dragPoint.y > MAX(bottomThirty, bottomPoint - 6))
04283         row = lowerRow;
04284 
04285     row = MIN(numberOfRows, row);
04286 
04287     return row;
04288 }
04289 
04293 - (void)_validateDrop:(id)info proposedRow:(CPInteger)row proposedDropOperation:(CPTableViewDropOperation)dropOperation
04294 {
04295     if (_implementedDataSourceMethods & CPTableViewDataSource_tableView_validateDrop_proposedRow_proposedDropOperation_)
04296         return [_dataSource tableView:self validateDrop:info proposedRow:row proposedDropOperation:dropOperation];
04297 
04298     return CPDragOperationNone;
04299 }
04300 
04304 - (CPRect)_rectForDropHighlightViewOnRow:(int)theRowIndex
04305 {
04306     if (theRowIndex >= [self numberOfRows])
04307         theRowIndex = [self numberOfRows] - 1;
04308 
04309     return [self _rectOfRow:theRowIndex checkRange:NO];
04310 }
04311 
04315 - (CPRect)_rectForDropHighlightViewBetweenUpperRow:(int)theUpperRowIndex andLowerRow:(int)theLowerRowIndex offset:(CPPoint)theOffset
04316 {
04317     if (theLowerRowIndex > [self numberOfRows])
04318         theLowerRowIndex = [self numberOfRows];
04319 
04320     return [self _rectOfRow:theLowerRowIndex checkRange:NO];
04321 }
04322 
04326 - (CPDragOperation)draggingUpdated:(id)sender
04327 {
04328     _retargetedDropRow = nil;
04329     _retargetedDropOperation = nil;
04330 
04331     var location = [self convertPoint:[sender draggingLocation] fromView:nil],
04332         dropOperation = [self _proposedDropOperationAtPoint:location],
04333         numberOfRows = [self numberOfRows],
04334         row = [self _proposedRowAtPoint:location],
04335         dragOperation = [self _validateDrop:sender proposedRow:row proposedDropOperation:dropOperation];
04336 
04337     if (_retargetedDropRow !== nil)
04338         row = _retargetedDropRow;
04339     if (_retargetedDropOperation !== nil)
04340         dropOperation = _retargetedDropOperation;
04341 
04342 
04343     if (dropOperation === CPTableViewDropOn && row >= numberOfRows)
04344         row = numberOfRows - 1;
04345 
04346     var rect = _CGRectMakeZero();
04347 
04348     if (row === -1)
04349         rect = [self exposedRect];
04350 
04351     else if (dropOperation === CPTableViewDropAbove)
04352         rect = [self _rectForDropHighlightViewBetweenUpperRow:row - 1 andLowerRow:row offset:location];
04353 
04354     else
04355         rect = [self _rectForDropHighlightViewOnRow:row];
04356 
04357     [_dropOperationFeedbackView setDropOperation:row !== -1 ? dropOperation : CPDragOperationNone];
04358     [_dropOperationFeedbackView setHidden:(dragOperation == CPDragOperationNone)];
04359     [_dropOperationFeedbackView setFrame:rect];
04360     [_dropOperationFeedbackView setCurrentRow:row];
04361     [self addSubview:_dropOperationFeedbackView];
04362 
04363     return dragOperation;
04364 }
04365 
04366 /*
04367     @ignore
04368 */
04369 - (BOOL)prepareForDragOperation:(id)sender
04370 {
04371     // FIX ME: is there anything else that needs to happen here?
04372     // actual validation is called in dragginUpdated:
04373     [_dropOperationFeedbackView removeFromSuperview];
04374 
04375     return (_implementedDataSourceMethods & CPTableViewDataSource_tableView_validateDrop_proposedRow_proposedDropOperation_);
04376 }
04377 
04378 /*
04379     @ignore
04380 */
04381 - (BOOL)performDragOperation:(id)sender
04382 {
04383     var location = [self convertPoint:[sender draggingLocation] fromView:nil],
04384         operation = [self _proposedDropOperationAtPoint:location],
04385         row = _retargetedDropRow;
04386 
04387     if (row === nil)
04388         var row = [self _proposedRowAtPoint:location];
04389 
04390     return [_dataSource tableView:self acceptDrop:sender row:row dropOperation:operation];
04391 }
04392 
04393 /*
04394     @ignore
04395 */
04396 - (void)concludeDragOperation:(id)sender
04397 {
04398     [self reloadData];
04399 }
04400 
04401 /*
04402     This method is sent to the data source for convenience...
04403 */
04404 - (void)draggedImage:(CPImage)anImage endedAt:(CGPoint)aLocation operation:(CPDragOperation)anOperation
04405 {
04406     if ([_dataSource respondsToSelector:@selector(tableView:didEndDraggedImage:atPosition:operation:)])
04407         [_dataSource tableView:self didEndDraggedImage:anImage atPosition:aLocation operation:anOperation];
04408 }
04409 
04410 /*
04411     @ignore
04412     We're using this because we drag views instead of images so we can get the rows themselves to actually drag.
04413 */
04414 - (void)draggedView:(CPImage)aView endedAt:(CGPoint)aLocation operation:(CPDragOperation)anOperation
04415 {
04416     [self _draggingEnded];
04417     [self draggedImage:aView endedAt:aLocation operation:anOperation];
04418 }
04419 
04423 - (void)_updateSelectionWithMouseAtRow:(CPInteger)aRow
04424 {
04425     //check to make sure the row exists
04426     if (aRow < 0)
04427         return;
04428 
04429     var newSelection,
04430         shouldExtendSelection = NO;
04431     // If cmd/ctrl was held down XOR the old selection with the proposed selection
04432     if ([self mouseDownFlags] & (CPCommandKeyMask | CPControlKeyMask | CPAlternateKeyMask))
04433     {
04434         if ([_selectedRowIndexes containsIndex:aRow])
04435         {
04436             newSelection = [_selectedRowIndexes copy];
04437 
04438             [newSelection removeIndex:aRow];
04439         }
04440 
04441         else if (_allowsMultipleSelection)
04442         {
04443             newSelection = [_selectedRowIndexes copy];
04444 
04445             [newSelection addIndex:aRow];
04446         }
04447 
04448         else
04449             newSelection = [CPIndexSet indexSetWithIndex:aRow];
04450     }
04451     else if (_allowsMultipleSelection)
04452     {
04453         newSelection = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(MIN(aRow, _selectionAnchorRow), ABS(aRow - _selectionAnchorRow) + 1)];
04454         shouldExtendSelection = [self mouseDownFlags] & CPShiftKeyMask &&
04455                                 ((_lastSelectedRow == [_selectedRowIndexes lastIndex] && aRow > _lastSelectedRow) ||
04456                                 (_lastSelectedRow == [_selectedRowIndexes firstIndex] && aRow < _lastSelectedRow));
04457     }
04458     else if (aRow >= 0 && aRow < _numberOfRows)
04459         newSelection = [CPIndexSet indexSetWithIndex:aRow];
04460     else
04461         newSelection = [CPIndexSet indexSet];
04462 
04463     if ([newSelection isEqualToIndexSet:_selectedRowIndexes])
04464         return;
04465 
04466     if (_implementedDelegateMethods & CPTableViewDelegate_selectionShouldChangeInTableView_ &&
04467         ![_delegate selectionShouldChangeInTableView:self])
04468         return;
04469 
04470     if (_implementedDelegateMethods & CPTableViewDelegate_tableView_selectionIndexesForProposedSelection_)
04471         newSelection = [_delegate tableView:self selectionIndexesForProposedSelection:newSelection];
04472 
04473     if (_implementedDelegateMethods & CPTableViewDelegate_tableView_shouldSelectRow_)
04474     {
04475         var indexArray = [];
04476 
04477         [newSelection getIndexes:indexArray maxCount:-1 inIndexRange:nil];
04478 
04479         var indexCount = indexArray.length;
04480 
04481         while (indexCount--)
04482         {
04483             var index = indexArray[indexCount];
04484 
04485             if (![_delegate tableView:self shouldSelectRow:index])
04486                 [newSelection removeIndex:index];
04487         }
04488 
04489         // as per cocoa
04490         if ([newSelection count] === 0)
04491             return;
04492     }
04493 
04494     // if empty selection is not allowed and the new selection has nothing selected, abort
04495     if (!_allowsEmptySelection && [newSelection count] === 0)
04496         return;
04497 
04498     if ([newSelection isEqualToIndexSet:_selectedRowIndexes])
04499         return;
04500 
04501     [self selectRowIndexes:newSelection byExtendingSelection:shouldExtendSelection];
04502 
04503     _lastSelectedRow = [newSelection containsIndex:aRow] ? aRow : [newSelection lastIndex];
04504 }
04505 
04509 - (void)_noteSelectionIsChanging
04510 {
04511     [[CPNotificationCenter defaultCenter]
04512         postNotificationName:CPTableViewSelectionIsChangingNotification
04513                       object:self
04514                     userInfo:nil];
04515 }
04516 
04520 - (void)_noteSelectionDidChange
04521 {
04522     [[CPNotificationCenter defaultCenter]
04523         postNotificationName:CPTableViewSelectionDidChangeNotification
04524                       object:self
04525                     userInfo:nil];
04526 }
04527 
04531 - (BOOL)becomeFirstResponder
04532 {
04533     return YES;
04534 }
04535 
04539 - (BOOL)acceptsFirstResponder
04540 {
04541     return YES;
04542 }
04543 
04547 - (void)keyDown:(CPEvent)anEvent
04548 {
04549     var character = [anEvent charactersIgnoringModifiers],
04550         modifierFlags = [anEvent modifierFlags];
04551 
04552     // Check for the key events manually, as opposed to waiting for CPWindow to sent the actual action message
04553     // in _processKeyboardUIKey:, because we might not want to handle the arrow events.
04554     if (character === CPUpArrowFunctionKey || character === CPDownArrowFunctionKey)
04555     {
04556         // We're not interested in the arrow keys if there are no rows.
04557         // Technically we should also not be interested if we can't scroll,
04558         // but Cocoa doesn't handle that situation either.
04559         if ([self numberOfRows] !== 0)
04560         {
04561             [self _moveSelectionWithEvent:anEvent upward:(character === CPUpArrowFunctionKey)];
04562 
04563             return;
04564         }
04565     }
04566     else if (character === CPDeleteCharacter || character === CPDeleteFunctionKey)
04567     {
04568         // Don't call super if the delegate is interested in the delete key
04569         if ([self _sendDelegateDeleteKeyPressed])
04570             return;
04571     }
04572 
04573     [super keyDown:anEvent];
04574 }
04575 
04581 - (BOOL)_selectionIsBroken
04582 {
04583     return [self selectedRowIndexes]._ranges.length !== 1;
04584 }
04585 
04591 - (void)_moveSelectionWithEvent:(CPEvent)theEvent upward:(BOOL)shouldGoUpward
04592 {
04593     if (_implementedDelegateMethods & CPTableViewDelegate_selectionShouldChangeInTableView_ && ![_delegate selectionShouldChangeInTableView:self])
04594         return;
04595     var selectedIndexes = [self selectedRowIndexes];
04596 
04597     if ([selectedIndexes count] > 0)
04598     {
04599         var extend = (([theEvent modifierFlags] & CPShiftKeyMask) && _allowsMultipleSelection),
04600             i = [self selectedRow];
04601 
04602         if ([self _selectionIsBroken])
04603         {
04604             while ([selectedIndexes containsIndex:i])
04605             {
04606                 shouldGoUpward ? i-- : i++;
04607             }
04608             _wasSelectionBroken = true;
04609         }
04610         else if (_wasSelectionBroken && ((shouldGoUpward && i !== [selectedIndexes firstIndex]) || (!shouldGoUpward && i !== [selectedIndexes lastIndex])))
04611         {
04612             shouldGoUpward ? i = [selectedIndexes firstIndex] - 1 : i = [selectedIndexes lastIndex];
04613             _wasSelectionBroken = false;
04614         }
04615         else
04616         {
04617             shouldGoUpward ? i-- : i++;
04618         }
04619     }
04620     else
04621     {
04622         var extend = NO;
04623         //no rows are currently selected
04624         if ([self numberOfRows] > 0)
04625             var i = shouldGoUpward ? [self numberOfRows] - 1 : 0; // if we select upward select the last row, otherwise select the first row
04626     }
04627 
04628     if (i >= [self numberOfRows] || i < 0)
04629         return;
04630 
04631 
04632     if (_implementedDelegateMethods & CPTableViewDelegate_tableView_shouldSelectRow_)
04633     {
04634 
04635         while (![_delegate tableView:self shouldSelectRow:i] && (i < [self numberOfRows] || i > 0))
04636             shouldGoUpward ? i-- : i++; //check to see if the row can be selected if it can't be then see if the next row can be selected
04637 
04638         //if the index still can be selected after the loop then just return
04639          if (![_delegate tableView:self shouldSelectRow:i])
04640              return;
04641     }
04642 
04643     // if we go upward and see that this row is already selected we should deselect the row below
04644     if ([selectedIndexes containsIndex:i] && extend)
04645     {
04646         // the row we're on is the last to be selected
04647         var differedLastSelectedRow = i;
04648 
04649         // no remove the one before/after it
04650         shouldGoUpward ? i++  : i--;
04651 
04652         [selectedIndexes removeIndex:i];
04653 
04654         //we're going to replace the selection
04655         extend = NO;
04656     }
04657     else if (extend)
04658     {
04659         if ([selectedIndexes containsIndex:i])
04660         {
04661             i = shouldGoUpward ? [selectedIndexes firstIndex] -1 : [selectedIndexes lastIndex] + 1;
04662             i = MIN(MAX(i,0), [self numberOfRows] - 1);
04663         }
04664 
04665         [selectedIndexes addIndex:i];
04666         var differedLastSelectedRow = i;
04667     }
04668     else
04669     {
04670         selectedIndexes = [CPIndexSet indexSetWithIndex:i];
04671         var differedLastSelectedRow = i;
04672     }
04673 
04674     [self selectRowIndexes:selectedIndexes byExtendingSelection:extend];
04675 
04676     // we differ because selectRowIndexes: does its own thing which would set the wrong index
04677     _lastSelectedRow = differedLastSelectedRow;
04678 
04679     if (i !== CPNotFound)
04680         [self scrollRowToVisible:i];
04681 }
04682 
04683 @end
04684 
04685 @implementation CPTableView (Bindings)
04686 
04690 - (CPString)_replacementKeyPathForBinding:(CPString)aBinding
04691 {
04692     if (aBinding === @"selectionIndexes")
04693         return @"selectedRowIndexes";
04694 
04695     return [super _replacementKeyPathForBinding:aBinding];
04696 }
04697 
04701 - (void)_establishBindingsIfUnbound:(id)destination
04702 {
04703     if ([[self infoForBinding:@"content"] objectForKey:CPObservedObjectKey] !== destination)
04704         [self bind:@"content" toObject:destination withKeyPath:@"arrangedObjects" options:nil];
04705 
04706     if ([[self infoForBinding:@"selectionIndexes"] objectForKey:CPObservedObjectKey] !== destination)
04707         [self bind:@"selectionIndexes" toObject:destination withKeyPath:@"selectionIndexes" options:nil];
04708 
04709     //[self bind:@"sortDescriptors" toObject:destination withKeyPath:@"sortDescriptors" options:nil];
04710 }
04711 
04712 - (void)setContent:(CPArray)content
04713 {
04714     [self reloadData];
04715 }
04716 
04717 @end
04718 
04719 var CPTableViewDataSourceKey                = @"CPTableViewDataSourceKey",
04720     CPTableViewDelegateKey                  = @"CPTableViewDelegateKey",
04721     CPTableViewHeaderViewKey                = @"CPTableViewHeaderViewKey",
04722     CPTableViewTableColumnsKey              = @"CPTableViewTableColumnsKey",
04723     CPTableViewRowHeightKey                 = @"CPTableViewRowHeightKey",
04724     CPTableViewIntercellSpacingKey          = @"CPTableViewIntercellSpacingKey",
04725     CPTableViewSelectionHighlightStyleKey   = @"CPTableViewSelectionHighlightStyleKey",
04726     CPTableViewMultipleSelectionKey         = @"CPTableViewMultipleSelectionKey",
04727     CPTableViewEmptySelectionKey            = @"CPTableViewEmptySelectionKey",
04728     CPTableViewColumnReorderingKey          = @"CPTableViewColumnReorderingKey",
04729     CPTableViewColumnResizingKey            = @"CPTableViewColumnResizingKey",
04730     CPTableViewColumnSelectionKey           = @"CPTableViewColumnSelectionKey",
04731     CPTableViewColumnAutoresizingStyleKey   = @"CPTableViewColumnAutoresizingStyleKey",
04732     CPTableViewGridColorKey                 = @"CPTableViewGridColorKey",
04733     CPTableViewGridStyleMaskKey             = @"CPTableViewGridStyleMaskKey",
04734     CPTableViewUsesAlternatingBackgroundKey = @"CPTableViewUsesAlternatingBackgroundKey",
04735     CPTableViewAlternatingRowColorsKey      = @"CPTableViewAlternatingRowColorsKey",
04736     CPTableViewHeaderViewKey                = @"CPTableViewHeaderViewKey",
04737     CPTableViewCornerViewKey                = @"CPTableViewCornerViewKey",
04738     CPTableViewAutosaveNameKey              = @"CPTableViewAutosaveNameKey";
04739 
04740 @implementation CPTableView (CPCoding)
04741 
04742 - (id)initWithCoder:(CPCoder)aCoder
04743 {
04744     self = [super initWithCoder:aCoder];
04745 
04746     if (self)
04747     {
04748         //Configuring Behavior
04749         _allowsColumnReordering = [aCoder decodeBoolForKey:CPTableViewColumnReorderingKey];
04750         _allowsColumnResizing = [aCoder decodeBoolForKey:CPTableViewColumnResizingKey];
04751         _allowsMultipleSelection = [aCoder decodeBoolForKey:CPTableViewMultipleSelectionKey];
04752         _allowsEmptySelection = [aCoder decodeBoolForKey:CPTableViewEmptySelectionKey];
04753         _allowsColumnSelection = [aCoder decodeBoolForKey:CPTableViewColumnSelectionKey];
04754 
04755         //Setting Display Attributes
04756         _selectionHighlightStyle = [aCoder decodeIntForKey:CPTableViewSelectionHighlightStyleKey];
04757         _columnAutoResizingStyle = [aCoder decodeIntForKey:CPTableViewColumnAutoresizingStyleKey];
04758 
04759         _tableColumns = [aCoder decodeObjectForKey:CPTableViewTableColumnsKey] || [];
04760         [_tableColumns makeObjectsPerformSelector:@selector(setTableView:) withObject:self];
04761 
04762         if ([aCoder containsValueForKey:CPTableViewRowHeightKey])
04763             _rowHeight = [aCoder decodeFloatForKey:CPTableViewRowHeightKey];
04764         else
04765             _rowHeight = 23.0;
04766 
04767         _intercellSpacing = [aCoder decodeSizeForKey:CPTableViewIntercellSpacingKey] || _CGSizeMake(3.0, 2.0);
04768 
04769         [self setGridColor:[aCoder decodeObjectForKey:CPTableViewGridColorKey]];
04770         _gridStyleMask = [aCoder decodeIntForKey:CPTableViewGridStyleMaskKey] || CPTableViewGridNone;
04771 
04772         _usesAlternatingRowBackgroundColors = [aCoder decodeObjectForKey:CPTableViewUsesAlternatingBackgroundKey];
04773         [self setAlternatingRowBackgroundColors:[aCoder decodeObjectForKey:CPTableViewAlternatingRowColorsKey]];
04774 
04775         _headerView = [aCoder decodeObjectForKey:CPTableViewHeaderViewKey];
04776         _cornerView = [aCoder decodeObjectForKey:CPTableViewCornerViewKey];
04777 
04778         [self setDataSource:[aCoder decodeObjectForKey:CPTableViewDataSourceKey]];
04779         [self setDelegate:[aCoder decodeObjectForKey:CPTableViewDelegateKey]];
04780 
04781         [self _init];
04782 
04783         [self viewWillMoveToSuperview:[self superview]];
04784 
04785         // Do this as late as possible to make sure the tableview is fully configured
04786         [self setAutosaveName:[aCoder decodeObjectForKey:CPTableViewAutosaveNameKey]];
04787     }
04788 
04789     return self;
04790 }
04791 
04792 - (void)encodeWithCoder:(CPCoder)aCoder
04793 {
04794     [super encodeWithCoder:aCoder];
04795 
04796     [aCoder encodeObject:_dataSource forKey:CPTableViewDataSourceKey];
04797     [aCoder encodeObject:_delegate forKey:CPTableViewDelegateKey];
04798 
04799     [aCoder encodeFloat:_rowHeight forKey:CPTableViewRowHeightKey];
04800     [aCoder encodeSize:_intercellSpacing forKey:CPTableViewIntercellSpacingKey];
04801 
04802     [aCoder encodeInt:_selectionHighlightStyle forKey:CPTableViewSelectionHighlightStyleKey];
04803     [aCoder encodeInt:_columnAutoResizingStyle forKey:CPTableViewColumnAutoresizingStyleKey];
04804 
04805     [aCoder encodeBool:_allowsMultipleSelection forKey:CPTableViewMultipleSelectionKey];
04806     [aCoder encodeBool:_allowsEmptySelection forKey:CPTableViewEmptySelectionKey];
04807     [aCoder encodeBool:_allowsColumnReordering forKey:CPTableViewColumnReorderingKey];
04808     [aCoder encodeBool:_allowsColumnResizing forKey:CPTableViewColumnResizingKey];
04809     [aCoder encodeBool:_allowsColumnSelection forKey:CPTableViewColumnSelectionKey];
04810 
04811     [aCoder encodeObject:_tableColumns forKey:CPTableViewTableColumnsKey];
04812 
04813     [aCoder encodeObject:[self gridColor] forKey:CPTableViewGridColorKey];
04814     [aCoder encodeInt:_gridStyleMask forKey:CPTableViewGridStyleMaskKey];
04815 
04816     [aCoder encodeBool:_usesAlternatingRowBackgroundColors forKey:CPTableViewUsesAlternatingBackgroundKey];
04817     [aCoder encodeObject:[self alternatingRowBackgroundColors] forKey:CPTableViewAlternatingRowColorsKey];
04818 
04819     [aCoder encodeObject:_cornerView forKey:CPTableViewCornerViewKey];
04820     [aCoder encodeObject:_headerView forKey:CPTableViewHeaderViewKey];
04821 
04822     [aCoder encodeObject:_autosaveName forKey:CPTableViewAutosaveNameKey];
04823 }
04824 
04825 @end
04826 
04827 @implementation CPIndexSet (tableview)
04828 
04829 - (void)removeMatches:(CPIndexSet)otherSet
04830 {
04831     var firstindex = [self firstIndex],
04832         index = MIN(firstindex, [otherSet firstIndex]),
04833         switchFlag = (index == firstindex);
04834 
04835     while (index != CPNotFound)
04836     {
04837         var indexSet = (switchFlag) ? otherSet : self;
04838 
04839         otherIndex = [indexSet indexGreaterThanOrEqualToIndex:index];
04840 
04841         if (otherIndex == index)
04842         {
04843             [self removeIndex:index];
04844             [otherSet removeIndex:index];
04845         }
04846 
04847         index = otherIndex;
04848         switchFlag = !switchFlag;
04849     }
04850 }
04851 
04852 @end
04853 
04854 @implementation _CPDropOperationDrawingView : CPView
04855 {
04856     unsigned    dropOperation;
04857     CPTableView tableView;
04858     int         currentRow;
04859     BOOL        isBlinking;
04860 }
04861 
04862 - (void)drawRect:(CGRect)aRect
04863 {
04864     if (tableView._destinationDragStyle === CPTableViewDraggingDestinationFeedbackStyleNone || isBlinking)
04865         return;
04866 
04867     var context = [[CPGraphicsContext currentContext] graphicsPort];
04868 
04869     CGContextSetStrokeColor(context, [CPColor colorWithHexString:@"4886ca"]);
04870     CGContextSetLineWidth(context, 3);
04871 
04872     if (currentRow === -1)
04873     {
04874         CGContextStrokeRect(context, [self bounds]);
04875     }
04876 
04877     else if (dropOperation === CPTableViewDropOn)
04878     {
04879         //if row is selected don't fill and stroke white
04880         var selectedRows = [tableView selectedRowIndexes],
04881             newRect = _CGRectMake(aRect.origin.x + 2, aRect.origin.y + 2, aRect.size.width - 4, aRect.size.height - 5);
04882 
04883         if ([selectedRows containsIndex:currentRow])
04884         {
04885             CGContextSetLineWidth(context, 2);
04886             CGContextSetStrokeColor(context, [CPColor whiteColor]);
04887         }
04888         else
04889         {
04890             CGContextSetFillColor(context, [CPColor colorWithRed:72 / 255 green:134 / 255 blue:202 / 255 alpha:0.25]);
04891             CGContextFillRoundedRectangleInRect(context, newRect, 8, YES, YES, YES, YES);
04892         }
04893         CGContextStrokeRoundedRectangleInRect(context, newRect, 8, YES, YES, YES, YES);
04894 
04895     }
04896     else if (dropOperation === CPTableViewDropAbove)
04897     {
04898         //reposition the view up a tad
04899         [self setFrameOrigin:_CGPointMake(_frame.origin.x, _frame.origin.y - 8)];
04900 
04901         var selectedRows = [tableView selectedRowIndexes];
04902 
04903         if ([selectedRows containsIndex:currentRow - 1] || [selectedRows containsIndex:currentRow])
04904         {
04905             CGContextSetStrokeColor(context, [CPColor whiteColor]);
04906             CGContextSetLineWidth(context, 4);
04907             //draw the circle thing
04908             CGContextStrokeEllipseInRect(context, _CGRectMake(aRect.origin.x + 4, aRect.origin.y + 4, 8, 8));
04909             //then draw the line
04910             CGContextBeginPath(context);
04911             CGContextMoveToPoint(context, 10, aRect.origin.y + 8);
04912             CGContextAddLineToPoint(context, aRect.size.width - aRect.origin.y - 8, aRect.origin.y + 8);
04913             CGContextClosePath(context);
04914             CGContextStrokePath(context);
04915 
04916             CGContextSetStrokeColor(context, [CPColor colorWithHexString:@"4886ca"]);
04917             CGContextSetLineWidth(context, 3);
04918         }
04919 
04920         //draw the circle thing
04921         CGContextStrokeEllipseInRect(context, _CGRectMake(aRect.origin.x + 4, aRect.origin.y + 4, 8, 8));
04922         //then draw the line
04923         CGContextBeginPath(context);
04924         CGContextMoveToPoint(context, 10, aRect.origin.y + 8);
04925         CGContextAddLineToPoint(context, aRect.size.width - aRect.origin.y - 8, aRect.origin.y + 8);
04926         CGContextClosePath(context);
04927         CGContextStrokePath(context);
04928         //CGContextStrokeLineSegments(context, [aRect.origin.x + 8,  aRect.origin.y + 8, 300 , aRect.origin.y + 8]);
04929     }
04930 }
04931 
04932 - (void)blink
04933 {
04934     if (dropOperation !== CPTableViewDropOn)
04935         return;
04936 
04937     isBlinking = YES;
04938 
04939     var showCallback = function() {
04940         objj_msgSend(self, "setHidden:", NO)
04941         isBlinking = NO;
04942     }
04943 
04944     var hideCallback = function() {
04945         objj_msgSend(self, "setHidden:", YES)
04946         isBlinking = YES;
04947     }
04948 
04949     objj_msgSend(self, "setHidden:", YES);
04950     [CPTimer scheduledTimerWithTimeInterval:0.1 callback:showCallback repeats:NO];
04951     [CPTimer scheduledTimerWithTimeInterval:0.19 callback:hideCallback repeats:NO];
04952     [CPTimer scheduledTimerWithTimeInterval:0.27 callback:showCallback repeats:NO];
04953 }
04954 
04955 @end
04956 
04957 @implementation _CPColumnDragView : CPView
04958 {
04959     CPColor _lineColor;
04960 }
04961 
04962 - (id)initWithLineColor:(CPColor)aColor
04963 {
04964     self = [super initWithFrame:_CGRectMakeZero()];
04965 
04966     if (self)
04967         _lineColor = aColor;
04968 
04969     return self;
04970 }
04971 
04972 - (void)drawRect:(CGRect)aRect
04973 {
04974     var context = [[CPGraphicsContext currentContext] graphicsPort];
04975 
04976     CGContextSetStrokeColor(context, _lineColor);
04977 
04978     var points = [
04979                     _CGPointMake(0.5, 0),
04980                     _CGPointMake(0.5, aRect.size.height)
04981                  ];
04982 
04983     CGContextStrokeLineSegments(context, points, 2);
04984 
04985     points = [
04986                 _CGPointMake(aRect.size.width - 0.5, 0),
04987                 _CGPointMake(aRect.size.width - 0.5, aRect.size.height)
04988              ];
04989 
04990     CGContextStrokeLineSegments(context, points, 2);
04991 }
04992 
04993 @end
04994 
04995 @implementation CPTableView (CPSynthesizedAccessors)
04996 
05000 - (BOOL)disableAutomaticResizing
05001 {
05002     return _disableAutomaticResizing;
05003 }
05004 
05008 - (void)setDisableAutomaticResizing:(BOOL)aValue
05009 {
05010     _disableAutomaticResizing = aValue;
05011 }
05012 
05013 @end
 All Classes Files Functions Variables Defines