![]() |
API 0.9.5
|
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