API  0.9.9
CPTableView.j
Go to the documentation of this file.
1 /*
2  * CPTableView.j
3  * AppKit
4  *
5  * Created by Francisco Tolmasky.
6  * Copyright 2009, 280 North, Inc.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21  */
22 
23 
24 
25 
26 @global CPApp
27 
28 
29 CPTableViewColumnDidMoveNotification = @"CPTableViewColumnDidMoveNotification";
30 CPTableViewColumnDidResizeNotification = @"CPTableViewColumnDidResizeNotification";
31 CPTableViewSelectionDidChangeNotification = @"CPTableViewSelectionDidChangeNotification";
32 CPTableViewSelectionIsChangingNotification = @"CPTableViewSelectionIsChangingNotification";
33 
42 
69 
70 //CPTableViewDraggingDestinationFeedbackStyles
74 
75 //CPTableViewDropOperations
78 
79 CPSourceListGradient = @"CPSourceListGradient";
80 CPSourceListTopLineColor = @"CPSourceListTopLineColor";
81 CPSourceListBottomLineColor = @"CPSourceListBottomLineColor";
82 
83 // TODO: add docs
84 
88 
92 
94 CPTableViewUniformColumnAutoresizingStyle = 1; // FIX ME: This is FUBAR
99 
100 #define NUMBER_OF_COLUMNS() (_tableColumns.length)
101 #define UPDATE_COLUMN_RANGES_IF_NECESSARY() \
102  if (_dirtyTableColumnRangeIndex !== CPNotFound) \
103  [self _recalculateTableColumnRanges];
104 #define FULL_ROW_HEIGHT() (_rowHeight + _intercellSpacing.height)
105 #define ROW_BOTTOM(__heightInfo) (__heightInfo.y + __heightInfo.height + _intercellSpacing.height)
106 #define HAS_VARIABLE_ROW_HEIGHTS() (_implementedDelegateMethods & CPTableViewDelegate_tableView_heightOfRow_)
107 
108 
110 
111 @optional
112 - (BOOL)tableView:(CPTableView)aTableView acceptDrop:(id <CPDraggingInfo>)info row:(CPInteger)aRowIndex dropOperation:(CPTableViewDropOperation)operation;
113 - (BOOL)tableView:(CPTableView)aTableView writeRowsWithIndexes:(CPIndexSet)rowIndexes toPasteboard:(CPPasteboard)pboard;
114 - (CPArray)tableView:(CPTableView)aTableView namesOfPromisedFilesDroppedAtDestination:(CPURL)dropDestination forDraggedRowsWithIndexes:(CPIndexSet)anIndexSet;
115 - (CPDragOperation)tableView:(CPTableView)aTableView validateDrop:(id <CPDraggingInfo>)info proposedRow:(CPInteger)aRowIndex proposedDropOperation:(CPTableViewDropOperation)anOperation;
116 - (CPInteger)numberOfRowsInTableView:(CPTableView)aTableView;
117 - (id)tableView:(CPTableView)aTableView objectValueForTableColumn:(CPTableColumn)aTableColumn row:(CPInteger)aRowIndex;
118 - (void)tableView:(CPTableView)aTableView setObjectValue:(id)anObjectValue forTableColumn:(CPTableColumn)aTableColumn row:(CPInteger)aRowIndex;
119 - (void)tableView:(CPTableView)aTableView sortDescriptorsDidChange:(CPArray)oldDescriptors;
120 
121 @end
122 
124 
125 @optional
126 - (BOOL)selectionShouldChangeInTableView:(CPTableView)aTableView;
127 - (BOOL)tableView:(CPTableView)aTableView isGroupRow:(CPInteger)aRowIndex;
128 - (BOOL)tableView:(CPTableView)aTableView shouldEditTableColumn:(CPTableColumn)aTableView row:(CPInteger)aRowIndex;
129 - (BOOL)tableView:(CPTableView)aTableView shouldReorderColumn:(CPInteger)columnIndex toColumn:(NSInteger)newColumnIndex;
130 - (BOOL)tableView:(CPTableView)aTableView shouldSelectRow:(CPInteger)aRowIndex;
131 - (BOOL)tableView:(CPTableView)aTableView shouldSelectTableColumn:(CPTableColumn)aTableColumn;
132 - (BOOL)tableView:(CPTableView)aTableView shouldShowViewExpansionForTableColumn:(CPTableColumn)aTableColumn row:(CPInteger)aRowIndex;
133 - (BOOL)tableView:(CPTableView)aTableView shouldTrackView:(CPView)aView forTableColumn:(CPTableColumn)aTableColumn row:(CPInteger)aRowIndex;
134 - (BOOL)tableView:(CPTableView)aTableView shouldTypeSelectForEvent:(CPEvent)anEvent withCurrentSearchString:(CPString)searchString;
135 - (CPIndexSet)tableView:(CPTableView)aTableView selectionIndexesForProposedSelection:(CPIndexSet)proposedSelectionIndexes;
136 - (CPInteger)tableView:(CPTableView)aTableView nextTypeSelectMatchFromRow:(CPInteger)startRow toRow:(CPInteger)endRow forString:(CPString)searchString;
137 - (CPMenu)tableViewMenuForTableColumn:(CPTableColumn)aTableColumn row:(CPInteger)aRowIndex;
138 - (CPString)tableView:(CPTableView)aTableView toolTipForView:(CPView)aView rect:(CGRect)aRect tableColumn:(CPTableColumn)aTableColumn row:(CPInteger)aRowIndex mouseLocation:(CGPoint)mouseLocation;
139 - (CPString)tableView:(CPTableView)aTableView typeSelectStringForTableColumn:(CPTableColumn)aTableColumn row:(CPInteger)aRowIndex;
140 - (CPView)tableView:(CPTableView)aTableView dataViewForTableColumn:(CPTableColumn)aTableColumn row:(CPInteger)aRowIndex;
141 - (CPView)tableView:(CPTableView)aTableView viewForTableColumn:(CPTableColumn)aTableColumn row:(CPInteger)aRowIndex;
142 - (float)tableView:(CPTableView)aTableView heightOfRow:(CPInteger)aRowIndex;
143 - (void)tableView:(CPTableView)aTableView didClickTableColumn:(CPTableColumn)aTableColumn;
144 - (void)tableView:(CPTableView)aTableView didDragTableColumn:(CPTableColumn)aTableColumn;
145 - (void)tableView:(CPTableView)aTableView mouseDownInHeaderOfTableColumn:(CPTableColumn)aTableColumn;
146 - (void)tableView:(CPTableView)aTableView willDisplayView:(CPView)aView forTableColumn:(CPTableColumn)aTableColumn row:(CPInteger)aRowIndex;
147 - (void)tableView:(CPTableView)aTableView willRemoveView:(CPView)aView forTableColumn:(CPTableColumn)aTableColumn row:(CPInteger)aRowIndex;
148 - (void)tableViewSelectionDidChange:(CPNotification)aNotification;
149 - (void)tableViewSelectionIsChanging:(CPNotification)aNotification;
150 
151 @end
152 
153 @implementation _CPTableDrawView : CPView
154 {
155  id _tableView;
156 }
157 
158 - (id)initWithTableView:(CPTableView)aTableView
159 {
160  self = [super init];
161 
162  if (self)
163  _tableView = aTableView;
164 
165  return self;
166 }
167 
168 - (void)drawRect:(CGRect)aRect
169 {
170  var frame = [self frame],
172 
173  CGContextTranslateCTM(context, -CGRectGetMinX(frame), -CGRectGetMinY(frame));
174 
175  [_tableView _drawRect:aRect];
176 }
177 
178 @end
179 
202 @implementation CPTableView : CPControl
203 {
204  id <CPTableViewDataSource> _dataSource;
205  CPInteger _implementedDataSourceMethods;
206 
207  id <CPTableViewDelegate> _delegate;
208  CPInteger _implementedDelegateMethods;
209 
210  CPArray _tableColumns;
211  CPArray _tableColumnRanges;
212  CPInteger _dirtyTableColumnRangeIndex;
213  CPInteger _numberOfHiddenColumns;
214 
215  BOOL _reloadAllRows;
216  BOOL _invalidateObjectValuesCache;
217  Object _objectValues;
218 
219  CGRect _exposedRect;
220  CPIndexSet _exposedRows;
221  CPIndexSet _exposedColumns;
222 
223  Object _dataViewsForRows;
224  Object _cachedDataViews;
225  CPDictionary _archivedDataViews;
226  Object _unavailable_custom_cibs;
227 
228  //Configuring Behavior
229  BOOL _allowsColumnReordering;
230  BOOL _allowsColumnResizing;
231  BOOL _allowsColumnSelection;
232  BOOL _allowsMultipleSelection;
233  BOOL _allowsEmptySelection;
234 
235  CPArray _sortDescriptors;
236 
237  //Setting Display Attributes
238  CGSize _intercellSpacing;
239  float _rowHeight;
240 
241  BOOL _usesAlternatingRowBackgroundColors;
242  CPArray _alternatingRowBackgroundColors;
243 
244  unsigned _selectionHighlightStyle;
245  CPColor _unfocusedSelectionHighlightColor;
246  CPDictionary _unfocusedSourceListSelectionColor;
247  CPTableColumn _currentHighlightedTableColumn;
248  unsigned _gridStyleMask;
249 
250  unsigned _numberOfRows;
251  CPIndexSet _groupRows;
252 
253  CPArray _cachedRowHeights;
254 
255  // Persistence
256  CPString _autosaveName;
257  BOOL _autosaveTableColumns;
258 
259  CPTableHeaderView _headerView;
260  _CPCornerView _cornerView;
261 
262  CPIndexSet _selectedColumnIndexes;
263  CPIndexSet _selectedRowIndexes;
264  CPInteger _selectionAnchorRow;
265  CPInteger _lastSelectedRow;
266  CPIndexSet _previouslySelectedRowIndexes;
267  CGPoint _startTrackingPoint;
268  CPDate _startTrackingTimestamp;
269  BOOL _trackingPointMovedOutOfClickSlop;
270  CPInteger _editingRow;
271  CPInteger _editingColumn;
272 
273  _CPTableDrawView _tableDrawView;
274 
275  SEL _doubleAction;
276  CPInteger _clickedRow;
277  CPInteger _clickedColumn;
278  unsigned _columnAutoResizingStyle;
279 
280  int _lastTrackedRowIndex;
281  CGPoint _originalMouseDownPoint;
282  BOOL _verticalMotionCanDrag;
283  unsigned _destinationDragStyle;
284  BOOL _isSelectingSession;
285  CPIndexSet _draggedRowIndexes;
286  BOOL _wasSelectionBroken;
287 
288  _CPDropOperationDrawingView _dropOperationFeedbackView;
289  CPDragOperation _dragOperationDefaultMask;
290  int _retargetedDropRow;
291  CPDragOperation _retargetedDropOperation;
292 
293  BOOL _disableAutomaticResizing;
294  BOOL _lastColumnShouldSnap;
295  BOOL _implementsCustomDrawRow;
296  BOOL _isViewBased;
297  BOOL _contentBindingExplicitlySet;
298 
299  SEL _viewForTableColumnRowSelector;
300 
301  CPInteger _draggedColumnIndex;
302  BOOL _draggedColumnIsSelected;
303  BOOL _needsDifferedTableColumnRemove;
304  CPArray _differedColumnDataToRemove;
305 
306  Function _BlockDeselectView;
307  Function _BlockSelectView;
308 
309  CPView _observedClipView;
310 }
311 
315 + (CPString)defaultThemeClass
316 {
317  return @"tableview";
318 }
319 
323 + (CPDictionary)themeAttributes
324 {
325  return @{
326  @"alternating-row-colors": [CPNull null],
327  @"grid-color": [CPNull null],
328  @"grid-line-thickness": 1.0,
329  @"highlighted-grid-color": [CPNull null],
330  @"selection-color": [CPNull null],
331  @"sourcelist-selection-color": [CPNull null],
332  @"sort-image": [CPNull null],
333  @"sort-image-reversed": [CPNull null],
334  @"selection-radius": [CPNull null],
335  @"image-generic-file": [CPNull null],
336  @"default-row-height": 25.0,
337  @"dropview-on-background-color": [CPNull null],
338  @"dropview-on-border-color": [CPNull null],
339  @"dropview-on-border-width": [CPNull null],
340  @"dropview-on-border-radius": [CPNull null],
341  @"dropview-on-selected-background-color": [CPNull null],
342  @"dropview-on-selected-border-color": [CPNull null],
343  @"dropview-on-selected-border-width": [CPNull null],
344  @"dropview-on-selected-border-radius": [CPNull null],
345  @"dropview-above-border-color": [CPNull null],
346  @"dropview-above-border-width": [CPNull null],
347  @"dropview-above-selected-border-color": [CPNull null],
348  @"dropview-above-selected-border-width": [CPNull null]
349  };
350 }
351 
352 - (id)initWithFrame:(CGRect)aFrame
353 {
354  self = [super initWithFrame:aFrame];
355 
356  if (self)
357  {
358  //Configuring Behavior
359  _allowsColumnReordering = YES;
360  _allowsColumnResizing = YES;
361  _allowsMultipleSelection = NO;
362  _allowsEmptySelection = YES;
363  _allowsColumnSelection = NO;
364  _disableAutomaticResizing = NO;
365 
366  //Setting Display Attributes
367  _selectionHighlightStyle = CPTableViewSelectionHighlightStyleRegular;
368 
371  [[CPColor whiteColor], [CPColor colorWithRed:245.0 / 255.0 green:249.0 / 255.0 blue:252.0 / 255.0 alpha:1.0]]];
372 
373  _tableColumns = [];
374  _tableColumnRanges = [];
375  _dirtyTableColumnRangeIndex = CPNotFound;
376  _numberOfHiddenColumns = 0;
377 
378  _intercellSpacing = CGSizeMake(3.0, 2.0);
379  _rowHeight = [self valueForThemeAttribute:@"default-row-height"];
380 
381  [self setGridColor:[CPColor colorWithHexString:@"dce0e2"]];
382  [self setGridStyleMask:CPTableViewGridNone];
383 
384  [self setHeaderView:[[CPTableHeaderView alloc] initWithFrame:CGRectMake(0, 0, [self bounds].size.width, _rowHeight)]];
385  [self setCornerView:[[_CPCornerView alloc] initWithFrame:CGRectMake(0, 0, [CPScroller scrollerWidth], CGRectGetHeight([_headerView frame]))]];
386 
387  _currentHighlightedTableColumn = nil;
388 
389  _draggedRowIndexes = [CPIndexSet indexSet];
390  _verticalMotionCanDrag = YES;
391  _isSelectingSession = NO;
392  _retargetedDropRow = nil;
393  _retargetedDropOperation = nil;
394  _dragOperationDefaultMask = nil;
396  _contentBindingExplicitlySet = NO;
397 
399  [self _init];
400  }
401 
402  return self;
403 }
404 
405 
411 - (void)_init
412 {
413  _lastSelectedRow = _clickedColumn = _clickedRow = -1;
414 
415  _selectedColumnIndexes = [CPIndexSet indexSet];
416  _selectedRowIndexes = [CPIndexSet indexSet];
417 
418  _dropOperationFeedbackView = [[_CPDropOperationDrawingView alloc] initWithFrame:CGRectMakeZero()];
419  [_dropOperationFeedbackView setTableView:self];
420 
421  _lastColumnShouldSnap = NO;
422 
423  if (!_alternatingRowBackgroundColors)
424  _alternatingRowBackgroundColors = [[CPColor whiteColor], [CPColor colorWithHexString:@"e4e7ff"]];
425 
426  _tableColumnRanges = [];
427  _dirtyTableColumnRangeIndex = 0;
428  _numberOfHiddenColumns = 0;
429 
430  _objectValues = { };
431  _invalidateObjectValuesCache = NO;
432  _dataViewsForRows = { };
433  _numberOfRows = 0;
434  _exposedRows = [CPIndexSet indexSet];
435  _exposedColumns = [CPIndexSet indexSet];
436  _cachedDataViews = { };
437  _archivedDataViews = nil;
438  _viewForTableColumnRowSelector = nil;
439  _unavailable_custom_cibs = { };
440  _cachedRowHeights = [];
441 
442  _groupRows = [CPIndexSet indexSet];
443 
444  _tableDrawView = [[_CPTableDrawView alloc] initWithTableView:self];
445  [_tableDrawView setBackgroundColor:[CPColor clearColor]];
446  [self addSubview:_tableDrawView];
447 
448  _draggedColumnIndex = -1;
449  _draggedColumnIsSelected = NO;
450 
451  _editingRow = CPNotFound;
452  _editingColumn = CPNotFound;
453 
454 /* //gradients for the source list when CPTableView is NOT first responder or the window is NOT key
455  // FIX ME: we need to actually implement this.
456  _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);
457  _sourceListInactiveTopLineColor = [CPColor colorWithCalibratedRed:(173.0/255.0) green:(187.0/255.0) blue:(209.0/255.0) alpha:1.0];
458  _sourceListInactiveBottomLineColor = [CPColor colorWithCalibratedRed:(150.0/255.0) green:(161.0/255.0) blue:(183.0/255.0) alpha:1.0];*/
459  _differedColumnDataToRemove = [];
460  _needsDifferedTableColumnRemove = NO;
461  _implementsCustomDrawRow = [self implementsSelector:@selector(drawRow:clipRect:)];
462 
463  if (!_sortDescriptors)
464  _sortDescriptors = [];
465 
466  [self _initSubclass];
467 }
468 
469 - (void)_initSubclass
470 {
471  _BlockDeselectView = function(view, row, column)
472  {
473  [view unsetThemeState:CPThemeStateSelectedDataView];
474  };
475 
476  _BlockSelectView = function(view, row, column)
477  {
478  [view setThemeState:CPThemeStateSelectedDataView];
479  };
480 }
481 
547 - (void)setDataSource:(id <CPTableViewDataSource>)aDataSource
548 {
549  if (_dataSource === aDataSource)
550  return;
551 
552  _dataSource = aDataSource;
553  _implementedDataSourceMethods = 0;
554 
555  if (!_dataSource)
556  return;
557 
558  var hasContentBinding = !![self infoForBinding:@"content"];
559 
560  if ([_dataSource respondsToSelector:@selector(numberOfRowsInTableView:)])
561  _implementedDataSourceMethods |= CPTableViewDataSource_numberOfRowsInTableView_;
562 
563  if ([_dataSource respondsToSelector:@selector(tableView:objectValueForTableColumn:row:)])
565 
566  if ([_dataSource respondsToSelector:@selector(tableView:setObjectValue:forTableColumn:row:)])
568 
569  if ([_dataSource respondsToSelector:@selector(tableView:acceptDrop:row:dropOperation:)])
570  _implementedDataSourceMethods |= CPTableViewDataSource_tableView_acceptDrop_row_dropOperation_;
571 
572  if ([_dataSource respondsToSelector:@selector(tableView:namesOfPromisedFilesDroppedAtDestination:forDraggedRowsWithIndexes:)])
574 
575  if ([_dataSource respondsToSelector:@selector(tableView:validateDrop:proposedRow:proposedDropOperation:)])
577 
578  if ([_dataSource respondsToSelector:@selector(tableView:writeRowsWithIndexes:toPasteboard:)])
580 
581  if ([_dataSource respondsToSelector:@selector(tableView:sortDescriptorsDidChange:)])
582  _implementedDataSourceMethods |= CPTableViewDataSource_tableView_sortDescriptorsDidChange_;
583 
584  [self _reloadDataViews];
585 }
586 
590 - (id)dataSource
591 {
592  return _dataSource;
593 }
594 
595 //Loading Data
599 - (void)reloadData
600 {
601  [self _reloadDataViews];
602 }
603 
609 - (void)reloadDataForRowIndexes:(CPIndexSet)rowIndexes columnIndexes:(CPIndexSet)columnIndexes
610 {
611  [self reloadData];
612 }
613 
614 // Reloads the data, not the views
615 - (void)_reloadDataForRowIndexes:(CPIndexSet)rowIndexes columnIndexes:(CPIndexSet)columnIndexes
616 {
617  [self _enumerateViewsInRows:rowIndexes columns:columnIndexes usingBlock:function(view, row, column, stop)
618  {
619  var tableColumn = [_tableColumns objectAtIndex:column];
620  [self _setObjectValueForTableColumn:tableColumn row:row forView:view useCache:NO];
621  }];
622 }
623 
624 - (void)_setupReload
625 {
626  _reloadAllRows = YES;
627  _objectValues = { };
628  _cachedRowHeights = [];
629 
630  // Otherwise, if we have a row marked as group with a
631  // index greater than the new number or rows
632  // it keeps the the graphical group style.
633  [_groupRows removeAllIndexes];
634 
635  // This updates the size too.
637 }
638 
639 // Reloads the views AND the data
640 - (void)_reloadDataViews
641 {
642  [self _setupReload];
643  [self setNeedsLayout];
644  [self setNeedsDisplay:YES];
645 }
646 
647 // Reloads the views AND the data
648 - (void)_reloadDataViewsSynchronously
649 {
650  [self _setupReload];
651  [self layout];
652  [self display];
653 }
654 
655 //Target-action Behavior
662 - (void)setDoubleAction:(SEL)anAction
663 {
664  _doubleAction = anAction;
665 }
666 
670 - (SEL)doubleAction
671 {
672  return _doubleAction;
673 }
674 
675 /*
676  Returns the index of the the column the user clicked to trigger an action, or -1 if no column was clicked.
677 */
678 - (CPInteger)clickedColumn
679 {
680  return _clickedColumn;
681 }
682 
686 - (CPInteger)clickedRow
687 {
688  return _clickedRow;
689 }
690 
691 //Configuring Behavior
692 
696 - (void)setAllowsColumnReordering:(BOOL)shouldAllowColumnReordering
697 {
698  _allowsColumnReordering = !!shouldAllowColumnReordering;
699 }
700 
704 - (BOOL)allowsColumnReordering
705 {
706  return _allowsColumnReordering;
707 }
708 
713 - (void)setAllowsColumnResizing:(BOOL)shouldAllowColumnResizing
714 {
715  _allowsColumnResizing = !!shouldAllowColumnResizing;
716 }
717 
721 - (BOOL)allowsColumnResizing
722 {
723  return _allowsColumnResizing;
724 }
725 
730 - (void)setAllowsMultipleSelection:(BOOL)shouldAllowMultipleSelection
731 {
732  _allowsMultipleSelection = !!shouldAllowMultipleSelection;
733 }
734 
740 - (BOOL)allowsMultipleSelection
741 {
742  return _allowsMultipleSelection;
743 }
744 
749 - (void)setAllowsEmptySelection:(BOOL)shouldAllowEmptySelection
750 {
751  _allowsEmptySelection = !!shouldAllowEmptySelection;
752 }
753 
757 - (BOOL)allowsEmptySelection
758 {
759  return _allowsEmptySelection;
760 }
761 
767 - (void)setAllowsColumnSelection:(BOOL)shouldAllowColumnSelection
768 {
769  _allowsColumnSelection = !!shouldAllowColumnSelection;
770 }
771 
772 
776 - (BOOL)allowsColumnSelection
777 {
778  return _allowsColumnSelection;
779 }
780 
781 //Setting Display Attributes
788 - (void)setIntercellSpacing:(CGSize)aSize
789 {
790  if (CGSizeEqualToSize(_intercellSpacing, aSize))
791  return;
792 
793  _intercellSpacing = CGSizeMakeCopy(aSize);
794 
795  _dirtyTableColumnRangeIndex = 0; // so that _recalculateTableColumnRanges will work
796  [self _recalculateTableColumnRanges];
797 
798  [_headerView setNeedsDisplay:YES];
799  [_headerView setNeedsLayout];
800 
801  [self _reloadDataViews];
802 }
803 
807 - (CGSize)intercellSpacing
808 {
809  return CGSizeMakeCopy(_intercellSpacing);
810 }
811 
818 - (void)setRowHeight:(unsigned)aRowHeight
819 {
820  // Accept row heights such as "0".
821  aRowHeight = +aRowHeight;
822 
823  if (_rowHeight === aRowHeight)
824  return;
825 
826  _rowHeight = MAX(0.0, aRowHeight);
827 
828  [self setNeedsLayout];
829 }
830 
834 - (unsigned)rowHeight
835 {
836  return _rowHeight;
837 }
838 
844 - (void)setUsesAlternatingRowBackgroundColors:(BOOL)shouldUseAlternatingRowBackgroundColors
845 {
846  _usesAlternatingRowBackgroundColors = shouldUseAlternatingRowBackgroundColors;
847 }
848 
852 - (BOOL)usesAlternatingRowBackgroundColors
853 {
854  return _usesAlternatingRowBackgroundColors;
855 }
856 
862 - (void)setAlternatingRowBackgroundColors:(CPArray)alternatingRowBackgroundColors
863 {
864  [self setValue:alternatingRowBackgroundColors forThemeAttribute:@"alternating-row-colors"];
865 
866  [self setNeedsDisplay:YES];
867 }
868 
872 - (CPArray)alternatingRowBackgroundColors
873 {
874  return [self currentValueForThemeAttribute:@"alternating-row-colors"];
875 }
876 
888 - (unsigned)selectionHighlightStyle
889 {
890  return _selectionHighlightStyle;
891 }
892 
904 - (void)setSelectionHighlightStyle:(unsigned)aSelectionHighlightStyle
905 {
906  _selectionHighlightStyle = aSelectionHighlightStyle;
907 
908  if (aSelectionHighlightStyle === CPTableViewSelectionHighlightStyleSourceList)
910  else
912 
913  [self _updateHighlightWithOldRows:[CPIndexSet indexSet] newRows:_selectedRowIndexes];
914  [self _updateHighlightWithOldColumns:[CPIndexSet indexSet] newColumns:_selectedColumnIndexes];
915  [self setNeedsDisplay:YES];
916 }
917 
923 - (void)setSelectionHighlightColor:(CPColor)aColor
924 {
925  if ([[self selectionHighlightColor] isEqual:aColor])
926  return;
927 
928  [self setValue:aColor forThemeAttribute:@"selection-color"];
929  [self setNeedsDisplay:YES];
930 }
931 
935 - (CPColor)selectionHighlightColor
936 {
937  return [self currentValueForThemeAttribute:@"selection-color"];
938 }
939 
943 - (CPColor)unfocusedSelectionHighlightColor
944 {
945  if (!_unfocusedSelectionHighlightColor)
946  _unfocusedSelectionHighlightColor = [self _unfocusedSelectionColorFromColor:[self selectionHighlightColor] saturation:0];
947 
948  return _unfocusedSelectionHighlightColor;
949 }
950 
962 - (void)setSelectionGradientColors:(CPDictionary)aDictionary
963 {
964  [self setValue:aDictionary forThemeAttribute:@"sourcelist-selection-color"];
965  [self setNeedsDisplay:YES];
966 }
967 
976 - (CPDictionary)selectionGradientColors
977 {
978  return [self currentValueForThemeAttribute:@"sourcelist-selection-color"];
979 }
980 
990 - (CPColor)unfocusedSelectionGradientColors
991 {
992  if (!_unfocusedSourceListSelectionColor)
993  {
994  var sourceListColors = [self selectionGradientColors];
995 
996  _unfocusedSourceListSelectionColor = @{
997  CPSourceListGradient: [self _unfocusedGradientFromGradient:[sourceListColors objectForKey:CPSourceListGradient]],
998  CPSourceListTopLineColor: [self _unfocusedSelectionColorFromColor:[sourceListColors objectForKey:CPSourceListTopLineColor] saturation:0.2],
999  CPSourceListBottomLineColor: [self _unfocusedSelectionColorFromColor:[sourceListColors objectForKey:CPSourceListBottomLineColor] saturation:0.2]
1000  };
1001  }
1002 
1003  return _unfocusedSourceListSelectionColor;
1004 }
1005 
1006 - (CPColor)_unfocusedSelectionColorFromColor:(CPColor)aColor saturation:(float)saturation
1007 {
1008  var hsb = [aColor hsbComponents];
1009 
1010  return [CPColor colorWithHue:hsb[0] saturation:hsb[1] * saturation brightness:hsb[2]];
1011 }
1012 
1013 - (CGGradient)_unfocusedGradientFromGradient:(CGGradient)aGradient
1014 {
1015  var colors = [aGradient.colors copy],
1016  count = [colors count];
1017 
1018  while (count--)
1019  {
1020  var rgba = colors[count].components,
1021  hsb = [self _unfocusedSelectionColorFromColor:[CPColor colorWithRed:rgba[0] green:rgba[1] blue:rgba[2] alpha:rgba[3]] saturation:0.2];
1022 
1023  colors[count] = CGColorCreate(aGradient.colorspace, [[hsb components] copy]);
1024  }
1025 
1026  return CGGradientCreateWithColors(aGradient.colorspace, colors, aGradient.locations);
1027 }
1028 
1033 - (void)setGridColor:(CPColor)aColor
1034 {
1035  [self setValue:aColor forThemeAttribute:@"grid-color"];
1036 
1037  [self setNeedsDisplay:YES];
1038 }
1039 
1043 - (CPColor)gridColor
1044 {
1045  return [self currentValueForThemeAttribute:@"grid-color"];;
1046 }
1047 
1053 - (void)setGridStyleMask:(unsigned)aGrideStyleMask
1054 {
1055  if (_gridStyleMask === aGrideStyleMask)
1056  return;
1057 
1058  _gridStyleMask = aGrideStyleMask;
1059 
1060  [self setNeedsDisplay:YES];
1061 }
1062 
1066 - (unsigned)gridStyleMask
1067 {
1068  return _gridStyleMask;
1069 }
1070 
1071 //Column Management
1072 
1077 - (void)addTableColumn:(CPTableColumn)aTableColumn
1078 {
1079  [_tableColumns addObject:aTableColumn];
1080  [aTableColumn setTableView:self];
1081 
1082  if (_dirtyTableColumnRangeIndex < 0)
1083  _dirtyTableColumnRangeIndex = NUMBER_OF_COLUMNS() - 1;
1084  else
1085  _dirtyTableColumnRangeIndex = MIN(NUMBER_OF_COLUMNS() - 1, _dirtyTableColumnRangeIndex);
1086 
1087  if ([[self sortDescriptors] count] > 0)
1088  {
1089  var mainSortDescriptor = [[self sortDescriptors] objectAtIndex:0];
1090 
1091  if (aTableColumn === [self _tableColumnForSortDescriptor:mainSortDescriptor])
1092  {
1093  var image = [mainSortDescriptor ascending] ? [self _tableHeaderSortImage] : [self _tableHeaderReverseSortImage];
1094  [self setIndicatorImage:image inTableColumn:aTableColumn];
1095  }
1096  }
1097 
1098  [self tile];
1099  [self setNeedsLayout];
1100 }
1101 
1106 - (void)removeTableColumn:(CPTableColumn)aTableColumn
1107 {
1108  if ([aTableColumn tableView] !== self)
1109  return;
1110 
1111  var index = [_tableColumns indexOfObjectIdenticalTo:aTableColumn];
1112 
1113  if (index === CPNotFound)
1114  return;
1115 
1116  // we defer the actual removal until the end of the runloop in order to keep a reference to the column.
1117  [_differedColumnDataToRemove addObject:{"column":aTableColumn, "shouldBeHidden": [aTableColumn isHidden]}];
1118  _needsDifferedTableColumnRemove = YES;
1119 
1120  [aTableColumn setHidden:YES];
1121  [aTableColumn setTableView:nil];
1122 
1123  var tableColumnUID = [aTableColumn UID];
1124 
1125  if (_objectValues[tableColumnUID])
1126  _objectValues[tableColumnUID] = nil;
1127 
1128  if (_dirtyTableColumnRangeIndex < 0)
1129  _dirtyTableColumnRangeIndex = index;
1130  else
1131  _dirtyTableColumnRangeIndex = MIN(index, _dirtyTableColumnRangeIndex);
1132 
1133  [self reloadData];
1134  [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
1135 }
1136 
1141 - (void)_setDraggedColumn:(CPInteger)columnIndex
1142 {
1143  if (_draggedColumnIndex === columnIndex)
1144  return;
1145 
1146  // If ending a column drag, reselect the column if it was selected before the drag
1147  if (columnIndex === -1 && _draggedColumnIsSelected)
1148  [_selectedColumnIndexes addIndex:_draggedColumnIndex];
1149 
1150  _draggedColumnIndex = columnIndex;
1151 }
1152 
1153 /*
1154  @ignore
1155  Same as moveColumn:toColumn: but doesn't trigger an autosave
1156 */
1157 - (void)_moveColumn:(unsigned)fromIndex toColumn:(unsigned)toIndex
1158 {
1159  // Convert parameters such as "0" to 0.
1160  fromIndex = +fromIndex;
1161  toIndex = +toIndex;
1162 
1163  if (fromIndex === toIndex)
1164  return;
1165 
1166  if (_dirtyTableColumnRangeIndex < 0)
1167  _dirtyTableColumnRangeIndex = MIN(fromIndex, toIndex);
1168  else
1169  _dirtyTableColumnRangeIndex = MIN(fromIndex, toIndex, _dirtyTableColumnRangeIndex);
1170 
1171  var tableColumn = _tableColumns[fromIndex],
1172  selectedTableColumns = [_tableColumns objectsAtIndexes:_selectedColumnIndexes];
1173 
1174  [_tableColumns removeObjectAtIndex:fromIndex];
1175  [_tableColumns insertObject:tableColumn atIndex:toIndex];
1176 
1177  [[self headerView] setNeedsLayout];
1178  [[self headerView] setNeedsDisplay:YES];
1179 
1180  var range = CPMakeRange(MIN(fromIndex, toIndex), ABS(fromIndex - toIndex) + 1),
1181  layoutColumnIndexes = [CPIndexSet indexSetWithIndexesInRange:range],
1182  selectedColumnIndexes = [CPIndexSet indexSet];
1183 
1184  [_tableColumns enumerateObjectsUsingBlock:function(tableColumn, idx, stop)
1185  {
1186  if ([selectedTableColumns containsObjectIdenticalTo:tableColumn])
1188  }];
1189 
1190  if ([_selectedColumnIndexes containsIndex:fromIndex])
1191  [selectedColumnIndexes addIndex:toIndex];
1192 
1193  if (_draggedColumnIndex !== -1)
1194  [layoutColumnIndexes removeIndex:toIndex];
1195 
1196  [self _layoutViewsForRowIndexes:_exposedRows columnIndexes:layoutColumnIndexes];
1197  [self selectColumnIndexes:selectedColumnIndexes byExtendingSelection:NO];
1198  [self setNeedsDisplay:YES];
1199 
1200  // Notify even if programmatically moving a column as in Cocoa.
1201  // TODO Only notify when a column drag operation ends, not each time a column reaches a new slot?
1202  [[CPNotificationCenter defaultCenter] postNotificationName:CPTableViewColumnDidMoveNotification
1203  object:self
1204  userInfo:@{ @"CPOldColumn": fromIndex, @"CPNewColumn": toIndex }];
1205 
1206  if (_implementedDelegateMethods & CPTableViewDelegate_tableViewColumnDidMove_)
1207  [_delegate tableViewColumnDidMove:[[CPNotification alloc] initWithName:CPTableViewColumnDidMoveNotification object:self userInfo:@{ @"CPOldColumn": fromIndex, @"CPNewColumn": toIndex }]];
1208 }
1209 
1215 - (void)moveColumn:(CPInteger)theColumnIndex toColumn:(CPInteger)theToIndex
1216 {
1217  [self _moveColumn:theColumnIndex toColumn:theToIndex];
1218  [self _autosave];
1219 }
1220 
1225 - (void)_tableColumnVisibilityDidChange:(CPTableColumn)aColumn
1226 {
1227  var columnIndex = [[self tableColumns] indexOfObjectIdenticalTo:aColumn];
1228 
1229  if (_dirtyTableColumnRangeIndex < 0)
1230  _dirtyTableColumnRangeIndex = columnIndex;
1231  else
1232  _dirtyTableColumnRangeIndex = MIN(columnIndex, _dirtyTableColumnRangeIndex);
1233 
1234  [[self headerView] setNeedsLayout];
1235  [[self headerView] setNeedsDisplay:YES];
1236 
1237  var rowIndexes = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, [self numberOfRows])];
1238  [self _layoutViewsForRowIndexes:rowIndexes columnIndexes:[CPIndexSet indexSetWithIndex:columnIndex]];
1239 }
1240 
1244 - (CPArray)tableColumns
1245 {
1246  return _tableColumns;
1247 }
1248 
1255 - (CPInteger)columnWithIdentifier:(CPString)anIdentifier
1256 {
1257  var index = 0,
1258  count = NUMBER_OF_COLUMNS();
1259 
1260  for (; index < count; ++index)
1261  if ([_tableColumns[index] identifier] === anIdentifier)
1262  return index;
1263 
1264  return CPNotFound;
1265 }
1266 
1273 - (CPTableColumn)tableColumnWithIdentifier:(CPString)anIdentifier
1274 {
1275  var index = [self columnWithIdentifier:anIdentifier];
1276 
1277  if (index === CPNotFound)
1278  return nil;
1279 
1280  return _tableColumns[index];
1281 }
1282 
1286 - (void)_didResizeTableColumn:(CPTableColumn)theColumn oldWidth:(int)oldWidth
1287 {
1288  [self _autosave];
1289 
1291  postNotificationName:CPTableViewColumnDidResizeNotification
1292  object:self
1293  userInfo:@{ @"CPTableColumn": theColumn, @"CPOldWidth": oldWidth }];
1294 
1295  if (_implementedDelegateMethods & CPTableViewDelegate_tableViewColumnDidResize_)
1296  [_delegate tableViewColumnDidResize:[[CPNotification alloc] initWithName:CPTableViewColumnDidResizeNotification object:self userInfo:@{ @"CPTableColumn": theColumn, @"CPOldWidth": oldWidth }]];
1297 }
1298 
1299 //Selecting Columns and Rows
1300 
1307 - (void)selectColumnIndexes:(CPIndexSet)columns byExtendingSelection:(BOOL)shouldExtendSelection
1308 {
1309  // If we're out of range, just return
1310  if (([columns firstIndex] != CPNotFound && [columns firstIndex] < 0) || [columns lastIndex] >= [self numberOfColumns] || (!shouldExtendSelection && [columns isEqualToIndexSet:_selectedColumnIndexes]) || (shouldExtendSelection && [columns count] === 0))
1311  return;
1312 
1313  // We deselect all rows when selecting columns.
1314  if ([_selectedRowIndexes count] > 0)
1315  {
1316  [self _updateHighlightWithOldRows:_selectedRowIndexes newRows:[CPIndexSet indexSet]];
1317  _selectedRowIndexes = [CPIndexSet indexSet];
1318  }
1319 
1320  var previousSelectedIndexes = [_selectedColumnIndexes copy];
1321 
1322  if (shouldExtendSelection)
1323  [_selectedColumnIndexes addIndexes:columns];
1324  else
1325  _selectedColumnIndexes = [columns copy];
1326 
1327  [self _updateHighlightWithOldColumns:previousSelectedIndexes newColumns:_selectedColumnIndexes];
1328  [self setNeedsDisplay:YES]; // FIXME: should be setNeedsDisplayInRect:enclosing rect of new (de)selected columns
1329  // but currently -drawRect: is not implemented here
1330  if (_headerView)
1331  [_headerView setNeedsDisplay:YES];
1332 
1333  [self _noteSelectionDidChange];
1334 }
1335 
1339 - (void)_setSelectedRowIndexes:(CPIndexSet)rows
1340 {
1341  if ([_selectedRowIndexes isEqualToIndexSet:rows])
1342  return;
1343 
1344  var previousSelectedIndexes = _selectedRowIndexes;
1345 
1346  _lastSelectedRow = ([rows count] > 0) ? [rows lastIndex] : -1;
1347  _selectedRowIndexes = [rows copy];
1348 
1349  [self _updateHighlightWithOldRows:previousSelectedIndexes newRows:_selectedRowIndexes];
1350  [self setNeedsDisplay:YES]; // FIXME: should be setNeedsDisplayInRect:enclosing rect of new (de)selected rows
1351  // but currently -drawRect: is not implemented here
1352 
1353  var binderClass = [[self class] _binderClassForBinding:@"selectionIndexes"];
1354  [[binderClass getBinding:@"selectionIndexes" forObject:self] reverseSetValueFor:@"selectedRowIndexes"];
1355 
1356  [self _noteSelectionDidChange];
1357 }
1358 
1365 - (void)selectRowIndexes:(CPIndexSet)rows byExtendingSelection:(BOOL)shouldExtendSelection
1366 {
1367  if ([rows isEqualToIndexSet:_selectedRowIndexes] ||
1368  (([rows firstIndex] != CPNotFound && [rows firstIndex] < 0) || [rows lastIndex] >= [self numberOfRows]) ||
1369  [self numberOfColumns] <= 0)
1370  return;
1371 
1372  // We deselect all columns when selecting rows.
1373  if ([_selectedColumnIndexes count] > 0)
1374  {
1375  [self _updateHighlightWithOldColumns:_selectedColumnIndexes newColumns:[CPIndexSet indexSet]];
1376  _selectedColumnIndexes = [CPIndexSet indexSet];
1377  if (_headerView)
1378  [_headerView setNeedsDisplay:YES];
1379  }
1380 
1381  var newSelectedIndexes;
1382  if (shouldExtendSelection)
1383  {
1384  newSelectedIndexes = [_selectedRowIndexes copy];
1385  [newSelectedIndexes addIndexes:rows];
1386  }
1387  else
1388  newSelectedIndexes = [rows copy];
1389 
1390  [self _setSelectedRowIndexes:newSelectedIndexes];
1391 }
1392 
1398 - (CPIndexSet)_cleanUpSelectionRowIndexes:(CPIndexSet)anIndexSet
1399 {
1400  if ([self _delegateRespondsToSelectionIndexesForProposedSelection])
1401  {
1402  return [self _sendDelegateSelectionIndexesForProposedSelection:anIndexSet];
1403  }
1404  else if ([self _delegateRespondsToShouldSelectRow])
1405  {
1406  var indexesToRemove = [CPIndexSet new],
1407  currentIndex = [anIndexSet firstIndex];
1408 
1409  while (currentIndex != CPNotFound)
1410  {
1411  if (![self _sendDelegateShouldSelectRow:currentIndex])
1412  [indexesToRemove addIndex:currentIndex];
1413 
1414  currentIndex = [anIndexSet indexGreaterThanIndex:currentIndex];
1415  }
1416 
1417  [anIndexSet removeIndexes:indexesToRemove];
1418 
1419  return anIndexSet;
1420  }
1421  else
1422  return anIndexSet;
1423 }
1424 
1428 - (void)_updateHighlightWithOldRows:(CPIndexSet)oldRows newRows:(CPIndexSet)newRows
1429 {
1430  [self _enumerateViewsInRows:oldRows columns:_exposedColumns usingBlock:_BlockDeselectView];
1431 
1432  if (_selectionHighlightStyle !== CPTableViewSelectionHighlightStyleNone)
1433  [self _enumerateViewsInRows:newRows columns:_exposedColumns usingBlock:_BlockSelectView];
1434 }
1435 
1439 - (void)_updateHighlightWithOldColumns:(CPIndexSet)oldColumns newColumns:(CPIndexSet)newColumns
1440 {
1441  var blockDeselectHeader = function(column, stop)
1442  {
1443  var headerView = [_tableColumns[column] headerView];
1444  [headerView unsetThemeState:CPThemeStateSelected];
1445  };
1446 
1447  var showSelection = _selectionHighlightStyle !== CPTableViewSelectionHighlightStyleNone;
1448 
1449  [self _enumerateViewsInRows:_exposedRows columns:oldColumns usingBlock:_BlockDeselectView];
1450  [oldColumns enumerateIndexesUsingBlock:blockDeselectHeader];
1451 
1452  if (_selectionHighlightStyle !== CPTableViewSelectionHighlightStyleNone)
1453  {
1454  [self _enumerateViewsInRows:_exposedRows columns:newColumns usingBlock:_BlockSelectView];
1455  [newColumns enumerateIndexesUsingBlock:function(column, stop)
1456  {
1457  var headerView = [_tableColumns[column] headerView];
1458  [headerView setThemeState:CPThemeStateSelected];
1459  }];
1460  }
1461 }
1462 
1466 - (int)selectedColumn
1467 {
1468  return [_selectedColumnIndexes lastIndex];
1469 }
1470 
1474 - (CPIndexSet)selectedColumnIndexes
1475 {
1476  return _selectedColumnIndexes;
1477 }
1478 
1482 - (int)selectedRow
1483 {
1484  return _lastSelectedRow;
1485 }
1486 
1490 - (CPIndexSet)selectedRowIndexes
1491 {
1492  return [_selectedRowIndexes copy];
1493 }
1494 
1500 - (void)deselectColumn:(CPInteger)anIndex
1501 {
1502  var selectedColumnIndexes = [_selectedColumnIndexes copy];
1504  [self selectColumnIndexes:selectedColumnIndexes byExtendingSelection:NO];
1505  [self _noteSelectionDidChange];
1506 }
1507 
1513 - (void)deselectRow:(CPInteger)aRow
1514 {
1515  var selectedRowIndexes = [_selectedRowIndexes copy];
1517  [self selectRowIndexes:selectedRowIndexes byExtendingSelection:NO];
1518  [self _noteSelectionDidChange];
1519 }
1520 
1524 - (CPInteger)numberOfSelectedColumns
1525 {
1526  return [_selectedColumnIndexes count];
1527 }
1528 
1532 - (CPInteger)numberOfSelectedRows
1533 {
1534  return [_selectedRowIndexes count];
1535 }
1536 
1543 - (BOOL)isColumnSelected:(CPInteger)anIndex
1544 {
1545  return [_selectedColumnIndexes containsIndex:anIndex];
1546 }
1547 
1554 - (BOOL)isRowSelected:(CPInteger)aRow
1555 {
1556  return [_selectedRowIndexes containsIndex:aRow];
1557 }
1558 
1559 
1564 - (void)deselectAll
1565 {
1568 }
1569 
1570 - (void)selectAll:(id)sender
1571 {
1572  if (_allowsMultipleSelection)
1573  {
1574  if (![self _sendDelegateSelectionShouldChangeInTableView])
1575  return;
1576 
1577  if ([[self selectedColumnIndexes] count])
1579  else
1580  {
1581  var range = [self _cleanUpSelectionRowIndexes:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, [self numberOfRows])]];
1582  [self selectRowIndexes:range byExtendingSelection:NO];
1583  }
1584  }
1585 }
1586 
1587 - (void)deselectAll:(id)sender
1588 {
1589  if ([self allowsEmptySelection])
1590  {
1591  if (![self _sendDelegateSelectionShouldChangeInTableView])
1592  return;
1593 
1594  [self deselectAll];
1595  }
1596 }
1597 
1601 - (int)numberOfColumns
1602 {
1603  return NUMBER_OF_COLUMNS();
1604 }
1605 
1609 - (int)numberOfRows
1610 {
1611  return _numberOfRows;
1612 }
1613 
1614 - (int)_numberOfRows
1615 {
1616  var numberOfRows,
1617  contentBindingInfo = [self infoForBinding:@"content"];
1618 
1619  if (contentBindingInfo)
1620  {
1621  var destination = [contentBindingInfo objectForKey:CPObservedObjectKey],
1622  keyPath = [contentBindingInfo objectForKey:CPObservedKeyPathKey];
1623 
1624  numberOfRows = [[destination valueForKeyPath:keyPath] count];
1625  }
1626  else if (_dataSource && (_implementedDataSourceMethods & CPTableViewDataSource_numberOfRowsInTableView_))
1627  numberOfRows = [_dataSource numberOfRowsInTableView:self] || 0;
1628  else
1629  {
1630  if (_dataSource)
1631  CPLog(@"no content binding established and data source " + [_dataSource description] + " does not implement numberOfRowsInTableView:");
1632  numberOfRows = 0;
1633  }
1634 
1635  return numberOfRows;
1636 }
1637 
1638 
1642 - (CPView)cornerView
1643 {
1644  return _cornerView;
1645 }
1646 
1650 - (void)setCornerView:(CPView)aView
1651 {
1652  if (_cornerView === aView)
1653  return;
1654 
1655  _cornerView = aView;
1656 
1657  var scrollView = [self enclosingScrollView];
1658 
1659  if ([scrollView isKindOfClass:[CPScrollView class]] && [scrollView documentView] === self)
1660  [scrollView _updateCornerAndHeaderView];
1661 }
1662 
1666 - (CPView)headerView
1667 {
1668  return _headerView;
1669 }
1670 
1671 
1679 - (void)setHeaderView:(CPView)aHeaderView
1680 {
1681  if (_headerView === aHeaderView)
1682  return;
1683 
1684  [_headerView setTableView:nil];
1685 
1686  _headerView = aHeaderView;
1687 
1688  if (_headerView)
1689  {
1690  [_headerView setTableView:self];
1691  [_headerView setFrameSize:CGSizeMake(CGRectGetWidth([self frame]), CGRectGetHeight([_headerView frame]))];
1692  }
1693  else
1694  {
1695  // If there is no header view, there should be no corner view
1696  [_cornerView removeFromSuperview];
1697  _cornerView = nil;
1698  }
1699 
1700  var scrollView = [self enclosingScrollView];
1701 
1702  if ([scrollView isKindOfClass:[CPScrollView class]] && [scrollView documentView] === self)
1703  [scrollView _updateCornerAndHeaderView];
1704 
1705  [self setNeedsLayout];
1706 }
1707 
1711 - (void)_recalculateTableColumnRanges
1712 {
1713  // Complexity:
1714  // O(Columns)
1715 
1716  if (_dirtyTableColumnRangeIndex < 0)
1717  return;
1718 
1719  _numberOfHiddenColumns = 0;
1720 
1721  var index = _dirtyTableColumnRangeIndex,
1722  count = NUMBER_OF_COLUMNS(),
1723  x = index === 0 ? 0.0 : CPMaxRange(_tableColumnRanges[index - 1]);
1724 
1725  for (; index < count; ++index)
1726  {
1727  var tableColumn = _tableColumns[index];
1728 
1729  if ([tableColumn isHidden])
1730  {
1731  _numberOfHiddenColumns += 1;
1732  _tableColumnRanges[index] = CPMakeRange(x, 0.0);
1733  }
1734  else
1735  {
1736  var width = [_tableColumns[index] width] + _intercellSpacing.width;
1737 
1738  _tableColumnRanges[index] = CPMakeRange(x, width);
1739 
1740  x += width;
1741  }
1742  }
1743 
1744  _tableColumnRanges.length = count;
1745  _dirtyTableColumnRangeIndex = CPNotFound;
1746 }
1747 
1754 - (CGRect)rectOfColumn:(CPInteger)aColumnIndex
1755 {
1756  // Complexity:
1757  // O(1)
1758 
1759  // Coerce aColumnIndex to a number in case it is a string.
1760  aColumnIndex = +aColumnIndex;
1761 
1762  if (aColumnIndex < 0 || aColumnIndex >= NUMBER_OF_COLUMNS())
1763  return CGRectMakeZero();
1764 
1765  if ([[_tableColumns objectAtIndex:aColumnIndex] isHidden])
1766  return CGRectMakeZero();
1767 
1769 
1770  var range = _tableColumnRanges[aColumnIndex];
1771 
1772  return CGRectMake(range.location, 0.0, range.length, CGRectGetHeight([self bounds]));
1773 }
1774 
1782 - (CGRect)_rectOfRow:(CPInteger)aRowIndex checkRange:(BOOL)checkRange
1783 {
1784  // Complexity:
1785  // O(1)
1786 
1787  var lastIndex = [self numberOfRows] - 1,
1788  validIndex = aRowIndex >= 0 && aRowIndex <= lastIndex;
1789 
1790  if (checkRange && !validIndex)
1791  return CGRectMakeZero();
1792 
1793  var y = 0,
1794  height,
1795  fixedHeightRows = 0;
1796 
1798  {
1799  [self _populateRowHeightCacheIfNeeded];
1800 
1801  // If the index is valid, we use the y and height of the given row.
1802  // If the index is invalid, we start from the bottom of the last row and use the default row height.
1803  var heightInfo;
1804 
1805  if (validIndex)
1806  {
1807  heightInfo = _cachedRowHeights[aRowIndex];
1808  y = heightInfo.y;
1809  height = heightInfo.height + _intercellSpacing.height;
1810  }
1811  else
1812  {
1813  height = FULL_ROW_HEIGHT();
1814 
1815  if (_numberOfRows > 0)
1816  {
1817  heightInfo = _cachedRowHeights[lastIndex];
1818  y = ROW_BOTTOM(heightInfo);
1819 
1820  // y is now at the top of the first row beyond the last valid row.
1821  // Add the height of any rows beyond that.
1822  fixedHeightRows = aRowIndex - _numberOfRows;
1823  }
1824  }
1825  }
1826  else
1827  {
1828  fixedHeightRows = aRowIndex;
1829  height = FULL_ROW_HEIGHT();
1830  }
1831 
1832  y += fixedHeightRows * FULL_ROW_HEIGHT();
1833 
1834  return CGRectMake(0.0, y, CGRectGetWidth([self bounds]), height);
1835 }
1836 
1842 - (CGRect)rectOfRow:(CPInteger)aRowIndex
1843 {
1844  return [self _rectOfRow:aRowIndex checkRange:YES];
1845 }
1846 
1852 - (CPRange)rowsInRect:(CGRect)aRect
1853 {
1854  // Complexity:
1855  // O(1)
1856 
1857  // If we have no rows, then we won't intersect anything.
1858  if (_numberOfRows <= 0)
1859  return CPMakeRange(0, 0);
1860 
1861  var bounds = [self bounds];
1862 
1863  // No rows if the rect doesn't even intersect us.
1864  if (!CGRectIntersectsRect(aRect, bounds))
1865  return CPMakeRange(0, 0);
1866 
1867  var firstRow = [self rowAtPoint:aRect.origin];
1868 
1869  // first row has to be undershot, because if not we wouldn't be intersecting.
1870  if (firstRow < 0)
1871  firstRow = 0;
1872 
1873  var lastRow = [self rowAtPoint:CGPointMake(0.0, CGRectGetMaxY(aRect))];
1874 
1875  // last row has to be overshot, because if not we wouldn't be intersecting.
1876  if (lastRow < 0)
1877  lastRow = _numberOfRows - 1;
1878 
1879  return CPMakeRange(firstRow, lastRow - firstRow + 1);
1880 }
1881 
1882 /*
1883  Return the range of rows that lie wholly or partially within aRect.
1884  If the bottom of the last real row is above the bottom of aRect,
1885  synthesized rows of the default height are added to fill aRect.
1886 */
1887 - (CPRange)_exposedRowsInRect:(CGRect)aRect
1888 {
1889  var rowRange = [self rowsInRect:aRect],
1890  lastRealRow = CPMaxRange(rowRange) - 1,
1891  rectOfLastRealRow = [self _rectOfRow:lastRealRow checkRange:NO],
1892  bottomOfRealRows = CGRectGetMaxY(rectOfLastRealRow),
1893  rectBottom = CGRectGetMaxY(aRect);
1894 
1895  // If the bottom of the last real row is at or below the bottom of aRect, we are done
1896  if (bottomOfRealRows >= rectBottom)
1897  return rowRange;
1898 
1899  var numberOfSynthesizedRows = CEIL((rectBottom - bottomOfRealRows) / FULL_ROW_HEIGHT());
1900 
1901  rowRange.length += numberOfSynthesizedRows;
1902 
1903  return rowRange;
1904 }
1905 
1911 - (CPIndexSet)columnIndexesInRect:(CGRect)aRect
1912 {
1913  // Complexity:
1914  // O(log numberOfColumns) if table view contains no hidden columns
1915  // O(numberOfColumns) if table view contains hidden columns
1916 
1917  var column = MAX(0, [self columnAtPoint:CGPointMake(aRect.origin.x, 0.0)]),
1918  lastColumn = [self columnAtPoint:CGPointMake(CGRectGetMaxX(aRect), 0.0)];
1919 
1920  if (lastColumn === CPNotFound)
1921  lastColumn = NUMBER_OF_COLUMNS() - 1;
1922 
1923  // Don't bother doing the expensive removal of hidden indexes if we have no hidden columns.
1924  if (_numberOfHiddenColumns <= 0)
1925  return [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(column, lastColumn - column + 1)];
1926 
1927  var indexSet = [CPIndexSet indexSet];
1928 
1929  for (; column <= lastColumn; ++column)
1930  {
1931  var tableColumn = _tableColumns[column];
1932 
1933  if (![tableColumn isHidden])
1934  [indexSet addIndex:column];
1935  }
1936 
1937  return indexSet;
1938 }
1939 
1945 - (CPInteger)columnAtPoint:(CGPoint)aPoint
1946 {
1947  // Complexity:
1948  // O(log numberOfColumns) if table view contains no hidden columns
1949  // O(numberOfColumns) if table view contains hidden columns
1950 
1951  var bounds = [self bounds];
1952 
1953  if (!CGRectContainsPoint(bounds, aPoint))
1954  return CPNotFound;
1955 
1957 
1958  var x = aPoint.x,
1959  low = 0,
1960  high = _tableColumnRanges.length - 1;
1961 
1962  while (low <= high)
1963  {
1964  var middle = FLOOR(low + (high - low) / 2),
1965  range = _tableColumnRanges[middle];
1966 
1967  if (x < range.location)
1968  high = middle - 1;
1969 
1970  else if (x >= CPMaxRange(range))
1971  low = middle + 1;
1972 
1973  else
1974  {
1975  var numberOfColumns = _tableColumnRanges.length;
1976 
1977  while (middle < numberOfColumns && [_tableColumns[middle] isHidden])
1978  ++middle;
1979 
1980  if (middle < numberOfColumns)
1981  return middle;
1982 
1983  return CPNotFound;
1984  }
1985  }
1986 
1987  return CPNotFound;
1988 }
1989 
1995 - (CPInteger)rowAtPoint:(CGPoint)aPoint
1996 {
1997  // Complexity:
1998  // O(1) for fixed height rows or point out of bounds
1999  // O(log numberOfRows) for variable height rows
2000 
2001  // aPoint.x must be within our bounds
2002  var bounds = [self bounds];
2003 
2004  if (aPoint.x < CGRectGetMinX(bounds) || aPoint.x >= CGRectGetMaxX(bounds))
2005  return -1;
2006 
2007  // aPoint.x is in bounds, now we just have to check aPoint.y
2008 
2010  {
2011  // First make sure aPoint.y is above the bottom of the last row, otherwise we might
2012  // search the (potentially large number of) rows for nothing.
2013  var heightInfo = [_cachedRowHeights lastObject];
2014 
2015  if (!heightInfo || aPoint.y >= ROW_BOTTOM(heightInfo))
2016  return -1;
2017 
2018  return [_cachedRowHeights indexOfObject:aPoint
2019  inSortedRange:nil
2020  options:0
2021  usingComparator:function(aPoint, heightInfo)
2022  {
2023  if (aPoint.y < heightInfo.y)
2024  return CPOrderedAscending;
2025 
2026  if (aPoint.y > ROW_BOTTOM(heightInfo))
2027  return CPOrderedDescending;
2028 
2029  return CPOrderedSame;
2030  }];
2031  }
2032  else
2033  {
2034  var row = FLOOR(aPoint.y / FULL_ROW_HEIGHT());
2035 
2036  return row >= _numberOfRows ? -1 : row;
2037  }
2038 }
2039 
2048 - (CPInteger)rowForView:(CPView)aView
2049 {
2050  var row;
2051 
2052  [self getColumn:nil row:@ref(row) forView:aView];
2053 
2054  return row;
2055 }
2056 
2065 - (CPInteger)columnForView:(CPView)aView
2066 {
2067  var column;
2068 
2069  [self getColumn:@ref(column) row:nil forView:aView];
2070 
2071  return column;
2072 }
2073 
2077 - (void)getColumn:(Function)columnRef row:(Function)rowRef forView:(CPView)aView
2078 {
2079  var columnResult = -1,
2080  rowResult = -1;
2081 
2082  if (aView && [aView isKindOfClass:[CPView class]] && ![aView isKindOfClass:[CPTableView class]])
2083  {
2084  var cellView = aView,
2085  contentView = [[self window] contentView],
2086  found = NO,
2087  max_rec = 100;
2088 
2089  while (max_rec--)
2090  {
2091  if (!cellView || cellView === contentView)
2092  {
2093  found = NO;
2094  break;
2095  }
2096  else
2097  {
2098  var superview = [cellView superview];
2099 
2100  if ([superview isKindOfClass:[CPTableView class]])
2101  {
2102  found = YES;
2103  break;
2104  }
2105 
2106  cellView = superview;
2107  }
2108  }
2109 
2110  if (found)
2111  {
2112  [self _enumerateViewsInRows:_exposedRows columns:_exposedColumns usingBlock:function(view, row, column, stop)
2113  {
2114 
2115  if (view === cellView)
2116  {
2117  columnResult = column;
2118  rowResult = row;
2119  stop(YES);
2120  }
2121  }];
2122  }
2123  }
2124 
2125  if (columnRef)
2126  columnRef(columnResult);
2127 
2128  if (rowRef)
2129  rowRef(rowResult);
2130 }
2131 
2139 - (CGRect)frameOfDataViewAtColumn:(CPInteger)aColumn row:(CPInteger)aRow
2140 {
2142 
2143  if (aColumn > [self numberOfColumns] || aRow > [self numberOfRows])
2144  return CGRectMakeZero();
2145 
2146  var tableColumnRange = _tableColumnRanges[aColumn],
2147  rectOfRow = [self rectOfRow:aRow],
2148  leftInset = FLOOR(_intercellSpacing.width / 2.0),
2149  topInset = FLOOR(_intercellSpacing.height / 2.0);
2150 
2151  return CGRectMake(tableColumnRange.location + leftInset, CGRectGetMinY(rectOfRow) + topInset, tableColumnRange.length - _intercellSpacing.width, CGRectGetHeight(rectOfRow) - _intercellSpacing.height);
2152 }
2153 
2157 - (void)resizeWithOldSuperviewSize:(CGSize)aSize
2158 {
2159  [super resizeWithOldSuperviewSize:aSize];
2160 
2161  if (_disableAutomaticResizing)
2162  return;
2163 
2164  var mask = _columnAutoResizingStyle;
2165 
2166  // should we actually do some resizing?
2167  if (!_lastColumnShouldSnap)
2168  {
2169  // did the clip view intersect the old tablesize?
2170  var superview = [self superview];
2171 
2172  if (!superview || ![superview isKindOfClass:[CPClipView class]])
2173  return;
2174 
2175  var superviewWidth = [superview bounds].size.width,
2176  lastColumnMaxX = CGRectGetMaxX([self rectOfColumn:[self numberOfColumns] -1]);
2177 
2178  // Fix me: this fires on the table setup at times
2179  if (lastColumnMaxX >= superviewWidth && lastColumnMaxX <= aSize.width || lastColumnMaxX <= superviewWidth && lastColumnMaxX >= aSize.width)
2180  _lastColumnShouldSnap = YES;
2182  return;
2183  }
2184 
2186  [self _resizeAllColumnUniformlyWithOldSize:aSize];
2188  [self sizeLastColumnToFit];
2190  [self _autoResizeFirstColumn];
2191 }
2192 
2196 - (void)_autoResizeFirstColumn
2197 {
2198  var superview = [self superview];
2199 
2200  if (!superview)
2201  return;
2202 
2204 
2205  var count = NUMBER_OF_COLUMNS(),
2206  columnToResize = nil,
2207  totalWidth = 0,
2208  i = 0;
2209 
2210  for (; i < count; i++)
2211  {
2212  var column = _tableColumns[i];
2213 
2214  if (![column isHidden])
2215  {
2216  if (!columnToResize)
2217  columnToResize = column;
2218  totalWidth += [column width] + _intercellSpacing.width;
2219  }
2220  }
2221 
2222  // If there is a visible column
2223  if (columnToResize)
2224  {
2225  var superviewSize = [superview bounds].size,
2226  newWidth = superviewSize.width - totalWidth;
2227 
2228  newWidth += [columnToResize width];
2229  [columnToResize _tryToResizeToWidth:newWidth];
2230  }
2231 
2232  [self setNeedsLayout];
2233 }
2234 
2235 
2240 - (void)_resizeAllColumnUniformlyWithOldSize:(CGSize)oldSize
2241 {
2242  // what we care about is the superview clip rect
2243  // FIX ME: if it's not in a scrollview this doesn't really work
2244  var superview = [self superview];
2245 
2246  if (!superview || ![superview isKindOfClass:[CPClipView class]])
2247  return;
2248 
2250 
2251  var superviewWidth = [superview bounds].size.width,
2252  count = NUMBER_OF_COLUMNS(),
2253  resizableColumns = [CPIndexSet indexSet],
2254  remainingSpace = 0.0,
2255  i = 0;
2256 
2257  // find resizable columns
2258  // FIX ME: we could cache resizableColumns after this loop and reuse it during the resize
2259  for (; i < count; i++)
2260  {
2261  var tableColumn = _tableColumns[i];
2262  if (![tableColumn isHidden] && ([tableColumn resizingMask] & CPTableColumnAutoresizingMask))
2263  [resizableColumns addIndex:i];
2264  }
2265 
2266  var maxXofColumns = CGRectGetMaxX([self rectOfColumn:[resizableColumns lastIndex]]),
2267  remainingSpace = superviewWidth - maxXofColumns,
2268  resizeableColumnsCount = [resizableColumns count],
2269  proportionate = 0;
2270 
2271  while (remainingSpace && resizeableColumnsCount)
2272  {
2273  // Divy out the space.
2274  proportionate += remainingSpace / resizeableColumnsCount;
2275 
2276  // Reset the remaining space to 0
2277  remainingSpace = 0.0;
2278 
2279  var index = CPNotFound;
2280 
2281  while ((index = [resizableColumns indexGreaterThanIndex:index]) !== CPNotFound)
2282  {
2283  var item = _tableColumns[index],
2284  proposedWidth = [item width] + proportionate,
2285  resizeLeftovers = [item _tryToResizeToWidth:proposedWidth];
2286 
2287  if (resizeLeftovers)
2288  {
2289  [resizableColumns removeIndex:index];
2290 
2291  remainingSpace += resizeLeftovers;
2292  }
2293  }
2294  }
2295 
2296  // now that we've reached the end we know there are likely rounding errors
2297  // so we should size the last resized to fit
2298 
2299  // find the last visisble column
2300  while (count-- && [_tableColumns[count] isHidden]);
2301 
2302  // find the max x, but subtract a single pixel since the spacing isn't applicable here.
2303  var delta = superviewWidth - CGRectGetMaxX([self rectOfColumn:count]) - ([self intercellSpacing].width || 1),
2304  newSize = [item width] + delta;
2305 
2306  [item _tryToResizeToWidth:newSize];
2307 }
2308 
2320 - (void)setColumnAutoresizingStyle:(unsigned)style
2321 {
2322  //FIX ME: CPTableViewSequentialColumnAutoresizingStyle and CPTableViewReverseSequentialColumnAutoresizingStyle are not yet implemented
2323  _columnAutoResizingStyle = style;
2324 }
2325 
2329 - (unsigned)columnAutoresizingStyle
2330 {
2331  return _columnAutoResizingStyle;
2332 }
2333 
2337 - (void)sizeLastColumnToFit
2338 {
2339  _lastColumnShouldSnap = YES;
2340 
2341  var superview = [self superview];
2342 
2343  if (!superview)
2344  return;
2345 
2346  var superviewSize = [superview bounds].size;
2347 
2349 
2350  var count = NUMBER_OF_COLUMNS();
2351 
2352  // Decrement the counter until we get to the last column that's not hidden
2353  while (count-- && [_tableColumns[count] isHidden]);
2354 
2355  // If the last column exists
2356  if (count >= 0)
2357  {
2358  var columnToResize = _tableColumns[count],
2359  newSize = MAX(0.0, superviewSize.width - CGRectGetMinX([self rectOfColumn:count]) - _intercellSpacing.width);
2360 
2361  [columnToResize _tryToResizeToWidth:newSize];
2362  }
2363 
2364  [self setNeedsLayout];
2365 }
2366 
2370 - (void)noteNumberOfRowsChanged
2371 {
2372  var oldNumberOfRows = _numberOfRows;
2373 
2374  _numberOfRows = [self _numberOfRows];
2375 
2376  _cachedRowHeights = [];
2377 
2378  // this line serves two purposes
2379  // 1. it updates the _numberOfRows cache with the -numberOfRows call
2380  // 2. it updates the row height cache if needed
2381  [self _noteHeightOfRowsWithIndexesChanged:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, _numberOfRows)]];
2382 
2383  // remove row indexes from the selection if they no longer exist
2384  var hangingSelections = oldNumberOfRows - _numberOfRows;
2385 
2386  if (hangingSelections > 0)
2387  {
2388  var previousSelectionCount = [_selectedRowIndexes count];
2389  [_selectedRowIndexes removeIndexesInRange:CPMakeRange(_numberOfRows, hangingSelections)];
2390 
2391  if (![_selectedRowIndexes containsIndex:[self selectedRow]])
2392  _lastSelectedRow = CPNotFound;
2393 
2394  // For optimal performance, only send a notification if indices were actually removed.
2395  if (previousSelectionCount > [_selectedRowIndexes count])
2396  [self _noteSelectionDidChange];
2397  }
2398 
2399  [self tile];
2400 }
2401 
2402 /*
2403  Populates the row height cache if necessary.
2404 */
2405 - (void)_populateRowHeightCacheIfNeeded
2406 {
2407  if ([self numberOfRows] !== _cachedRowHeights.length)
2408  [self noteHeightOfRowsWithIndexesChanged:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, _numberOfRows)]];
2409 }
2410 
2416 - (void)_noteHeightOfRowsWithIndexesChanged:(CPIndexSet)anIndexSet
2417 {
2418  if (!HAS_VARIABLE_ROW_HEIGHTS())
2419  return;
2420 
2421  // Update the height of the given rows by calling the delegate. Since the row height cache also contains
2422  // y coordinates, we have to update the y coordinates of all rows below the first valid row in the range.
2423  var i = [anIndexSet indexGreaterThanOrEqualToIndex:0];
2424 
2425  if (i === CPNotFound)
2426  return;
2427 
2428  var y = i < _cachedRowHeights.length ? _cachedRowHeights[i].y : 0;
2429 
2430  for (var count = [self numberOfRows]; i < count; ++i)
2431  {
2432  var height;
2433 
2434  if ([anIndexSet containsIndex:i])
2435  height = [self _sendDelegateHeightOfRow:i];
2436  else
2437  height = _cachedRowHeights[i].height || _rowHeight; // in case the cache entry is empty
2438 
2439  _cachedRowHeights[i] = {y:y, height:height};
2440  y += height + _intercellSpacing.height;
2441  }
2442 }
2443 
2449 - (void)noteHeightOfRowsWithIndexesChanged:(CPIndexSet)anIndexSet
2450 {
2451  [self _noteHeightOfRowsWithIndexesChanged:anIndexSet];
2452  [self _reloadDataViews];
2453 }
2454 
2458 - (void)tile
2459 {
2461 
2462  var width = _tableColumnRanges.length > 0 ? CPMaxRange([_tableColumnRanges lastObject]) : 0.0,
2463  superview = [self superview],
2464  height = 0;
2465 
2466  if (!HAS_VARIABLE_ROW_HEIGHTS())
2467  height = FULL_ROW_HEIGHT() * _numberOfRows;
2468  else if (_numberOfRows > 0)
2469  {
2470  [self _populateRowHeightCacheIfNeeded];
2471 
2472  var heightInfo = _cachedRowHeights[_cachedRowHeights.length - 1];
2473 
2474  height = ROW_BOTTOM(heightInfo);
2475  }
2476 
2477  if ([superview isKindOfClass:[CPClipView class]])
2478  {
2479  var superviewSize = [superview bounds].size;
2480 
2481  width = MAX(superviewSize.width, width);
2482  height = MAX(superviewSize.height, height);
2483  }
2484 
2485  [self setFrameSize:CGSizeMake(width, height)];
2486 
2487  [self setNeedsLayout];
2488  [self setNeedsDisplay:YES];
2489 }
2490 
2491 
2497 - (void)scrollRowToVisible:(int)rowIndex
2498 {
2499  var visible = [self visibleRect],
2500  rowRect = [self rectOfRow:rowIndex];
2501 
2502  visible.origin.y = rowRect.origin.y;
2503  visible.size.height = rowRect.size.height;
2504 
2505  [self scrollRectToVisible:visible];
2506 }
2507 
2513 - (void)scrollColumnToVisible:(int)columnIndex
2514 {
2515  var visible = [self visibleRect],
2516  colRect = [self rectOfColumn:columnIndex];
2517 
2518  visible.origin.x = colRect.origin.x;
2519  visible.size.width = colRect.size.width;
2520 
2521  [self scrollRectToVisible:visible];
2522  [_headerView scrollRectToVisible:colRect];
2523 }
2524 
2531 - (void)setAutosaveName:(CPString)theAutosaveName
2532 {
2533  if (_autosaveName === theAutosaveName)
2534  return;
2535 
2536  _autosaveName = theAutosaveName;
2537 
2538  [self setAutosaveTableColumns:!!theAutosaveName];
2539  [self _restoreFromAutosave];
2540 }
2541 
2545 - (CPString)autosaveName
2546 {
2547  return _autosaveName;
2548 }
2549 
2556 - (void)setAutosaveTableColumns:(BOOL)shouldAutosave
2557 {
2558  _autosaveTableColumns = shouldAutosave;
2559 }
2560 
2564 - (BOOL)autosaveTableColumns
2565 {
2566  return _autosaveTableColumns;
2567 }
2568 
2572 - (CPString)_columnsKeyForAutosaveName:(CPString)theAutosaveName
2573 {
2574  return @"CPTableView Columns " + theAutosaveName;
2575 }
2576 
2580 - (BOOL)_autosaveEnabled
2581 {
2582  return [self autosaveName] && [self autosaveTableColumns];
2583 }
2584 
2591 - (void)_autosave
2592 {
2593  if (![self _autosaveEnabled])
2594  return;
2595 
2596  var userDefaults = [CPUserDefaults standardUserDefaults],
2597  autosaveName = [self autosaveName];
2598 
2599  var columns = [self tableColumns],
2600  columnsSetup = [];
2601 
2602  for (var i = 0; i < [columns count]; i++)
2603  {
2604  var column = [columns objectAtIndex:i],
2605  metaData = @{
2606  @"identifier": [column identifier],
2607  @"width": [column width]
2608  };
2609 
2610  [columnsSetup addObject:metaData];
2611  }
2612 
2613  [userDefaults setObject:columnsSetup forKey:[self _columnsKeyForAutosaveName:autosaveName]];
2614 }
2615 
2619 - (void)_restoreFromAutosave
2620 {
2621  if (![self _autosaveEnabled])
2622  return;
2623 
2624  var userDefaults = [CPUserDefaults standardUserDefaults],
2625  autosaveName = [self autosaveName],
2626  tableColumns = [userDefaults objectForKey:[self _columnsKeyForAutosaveName:autosaveName]];
2627 
2628  if ([tableColumns count] != [[self tableColumns] count])
2629  return;
2630 
2631  for (var i = 0; i < [tableColumns count]; i++)
2632  {
2633  var metaData = [tableColumns objectAtIndex:i],
2634  columnIdentifier = [metaData objectForKey:@"identifier"],
2635  column = [self columnWithIdentifier:columnIdentifier],
2636  tableColumn = [self tableColumnWithIdentifier:columnIdentifier];
2637 
2638  if (tableColumn && column != CPNotFound)
2639  {
2640  [self _moveColumn:column toColumn:i];
2641  [tableColumn setWidth:[metaData objectForKey:@"width"]];
2642  }
2643  }
2644 }
2645 
2793 - (void)setDelegate:(id <CPTableViewDelegate>)aDelegate
2794 {
2795  if (_delegate === aDelegate)
2796  return;
2797 
2798  _delegate = aDelegate;
2799  _implementedDelegateMethods = 0;
2800 
2801  if ([_delegate respondsToSelector:@selector(selectionShouldChangeInTableView:)])
2802  _implementedDelegateMethods |= CPTableViewDelegate_selectionShouldChangeInTableView_;
2803 
2804  if ([_delegate respondsToSelector:@selector(tableView:viewForTableColumn:row:)])
2805  _implementedDelegateMethods |= CPTableViewDelegate_tableView_viewForTableColumn_row_;
2806  else if ([_delegate respondsToSelector:@selector(tableView:dataViewForTableColumn:row:)])
2807  {
2808  _implementedDelegateMethods |= CPTableViewDelegate_tableView_dataViewForTableColumn_row_;
2809  CPLog.warn("tableView:dataViewForTableColumn: is deprecated. You should use -tableView:viewForTableColumn: where you can request the view with -makeViewWithIdentifier:owner:");
2810  }
2811 
2812  [self _updateIsViewBased];
2813 
2814  if ([_delegate respondsToSelector:@selector(tableView:didClickTableColumn:)])
2815  _implementedDelegateMethods |= CPTableViewDelegate_tableView_didClickTableColumn_;
2816 
2817  if ([_delegate respondsToSelector:@selector(tableView:didDragTableColumn:)])
2818  _implementedDelegateMethods |= CPTableViewDelegate_tableView_didDragTableColumn_;
2819 
2820  if ([_delegate respondsToSelector:@selector(tableView:heightOfRow:)])
2821  _implementedDelegateMethods |= CPTableViewDelegate_tableView_heightOfRow_;
2822 
2823  if ([_delegate respondsToSelector:@selector(tableView:isGroupRow:)])
2824  _implementedDelegateMethods |= CPTableViewDelegate_tableView_isGroupRow_;
2825 
2826  if ([_delegate respondsToSelector:@selector(tableView:mouseDownInHeaderOfTableColumn:)])
2828 
2829  if ([_delegate respondsToSelector:@selector(tableView:nextTypeSelectMatchFromRow:toRow:forString:)])
2831 
2832  if ([_delegate respondsToSelector:@selector(tableView:selectionIndexesForProposedSelection:)])
2834 
2835  if ([_delegate respondsToSelector:@selector(tableView:shouldEditTableColumn:row:)])
2836  _implementedDelegateMethods |= CPTableViewDelegate_tableView_shouldEditTableColumn_row_;
2837 
2838  if ([_delegate respondsToSelector:@selector(tableView:shouldSelectRow:)])
2839  _implementedDelegateMethods |= CPTableViewDelegate_tableView_shouldSelectRow_;
2840 
2841  if ([_delegate respondsToSelector:@selector(tableView:shouldSelectTableColumn:)])
2842  _implementedDelegateMethods |= CPTableViewDelegate_tableView_shouldSelectTableColumn_;
2843 
2844  if ([_delegate respondsToSelector:@selector(tableView:shouldShowViewExpansionForTableColumn:row:)])
2846 
2847  if ([_delegate respondsToSelector:@selector(tableView:shouldTrackView:forTableColumn:row:)])
2849 
2850  if ([_delegate respondsToSelector:@selector(tableView:shouldTypeSelectForEvent:withCurrentSearchString:)])
2852 
2853  if ([_delegate respondsToSelector:@selector(tableView:toolTipForView:rect:tableColumn:row:mouseLocation:)])
2855 
2856  if ([_delegate respondsToSelector:@selector(tableView:typeSelectStringForTableColumn:row:)])
2858 
2859  if ([_delegate respondsToSelector:@selector(tableView:willDisplayView:forTableColumn:row:)])
2861 
2862  if ([_delegate respondsToSelector:@selector(tableView:willRemoveView:forTableColumn:row:)])
2864 
2865  if ([_delegate respondsToSelector:@selector(tableView:menuForTableColumn:row:)])
2866  _implementedDelegateMethods |= CPTableViewDelegate_tableViewMenuForTableColumn_row_;
2867 
2868  if ([_delegate respondsToSelector:@selector(tableView:shouldReorderColumn:toColumn:)])
2869  _implementedDelegateMethods |= CPTableViewDelegate_tableView_shouldReorderColumn_toColumn_;
2870 
2871  if ([_delegate respondsToSelector:@selector(tableViewColumnDidMove:)])
2872  _implementedDelegateMethods |= CPTableViewDelegate_tableViewColumnDidMove_;
2873 
2874  if ([_delegate respondsToSelector:@selector(tableViewColumnDidResize:)])
2875  _implementedDelegateMethods |= CPTableViewDelegate_tableViewColumnDidResize_;
2876 
2877  if ([_delegate respondsToSelector:@selector(tableViewSelectionDidChange:)])
2878  _implementedDelegateMethods |= CPTableViewDelegate_tableViewSelectionDidChange_;
2879 
2880  if ([_delegate respondsToSelector:@selector(tableViewSelectionIsChanging:)])
2881  _implementedDelegateMethods |= CPTableViewDelegate_tableViewSelectionIsChanging_;
2882 }
2883 
2888 {
2889  return _delegate;
2890 }
2891 
2895 - (void)_didClickTableColumn:(CPInteger)clickedColumn modifierFlags:(unsigned)modifierFlags
2896 {
2897  [self _changeSortDescriptorsForClickOnColumn:clickedColumn];
2898 
2899  if (_allowsColumnSelection)
2900  {
2901  if ([self _sendDelegateSelectionShouldChangeInTableView] && [self _sendDelegateShouldSelectTableColumn:clickedColumn])
2902  {
2903  [self _noteSelectionIsChanging];
2904  if (modifierFlags & CPPlatformActionKeyMask)
2905  {
2906  if ([self isColumnSelected:clickedColumn])
2907  [self deselectColumn:clickedColumn];
2908  else if ([self allowsMultipleSelection] == YES)
2910 
2911  return;
2912  }
2913  else if (modifierFlags & CPShiftKeyMask)
2914  {
2915  // should be from clickedColumn to lastClickedColum with extending:(direction == previous selection)
2916  var startColumn = MIN(clickedColumn, [_selectedColumnIndexes lastIndex]),
2917  endColumn = MAX(clickedColumn, [_selectedColumnIndexes firstIndex]);
2918 
2919  [self selectColumnIndexes:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(startColumn, endColumn - startColumn + 1)]
2920  byExtendingSelection:YES];
2921 
2922  return;
2923  }
2924  else
2926  }
2927  }
2928 
2929  [self _sendDelegateDidClickTableColumn:clickedColumn];
2930 }
2931 
2932 // From GNUSTEP
2937 - (void)_changeSortDescriptorsForClickOnColumn:(CPInteger)column
2938 {
2939  var tableColumn = [_tableColumns objectAtIndex:column],
2940  newMainSortDescriptor = [tableColumn sortDescriptorPrototype];
2941 
2942  if (!newMainSortDescriptor)
2943  return;
2944 
2945  var oldMainSortDescriptor = nil,
2946  oldSortDescriptors = [self sortDescriptors],
2947  newSortDescriptors = [CPArray arrayWithArray:oldSortDescriptors],
2948 
2949  e = [newSortDescriptors objectEnumerator],
2950  descriptor = nil,
2951  outdatedDescriptors = [CPArray array];
2952 
2953  if ([_sortDescriptors count] > 0)
2954  oldMainSortDescriptor = [[self sortDescriptors] objectAtIndex: 0];
2955 
2956  // Remove every main descriptor equivalents (normally only one)
2957  while ((descriptor = [e nextObject]) !== nil)
2958  {
2959  if ([[descriptor key] isEqual: [newMainSortDescriptor key]])
2960  [outdatedDescriptors addObject:descriptor];
2961  }
2962 
2963  // Invert the sort direction when the same column header is clicked twice
2964  if ([[newMainSortDescriptor key] isEqual:[oldMainSortDescriptor key]])
2965  newMainSortDescriptor = [oldMainSortDescriptor reversedSortDescriptor];
2966 
2967  [newSortDescriptors removeObjectsInArray:outdatedDescriptors];
2968  [newSortDescriptors insertObject:newMainSortDescriptor atIndex:0];
2969 
2970  [self setHighlightedTableColumn:tableColumn];
2971  [self setSortDescriptors:newSortDescriptors];
2972 }
2973 
2982 - (void)setIndicatorImage:(CPImage)anImage inTableColumn:(CPTableColumn)aTableColumn
2983 {
2984  if (aTableColumn)
2985  {
2986  var headerView = [aTableColumn headerView];
2987  if ([headerView respondsToSelector:@selector(_setIndicatorImage:)])
2988  [headerView _setIndicatorImage:anImage];
2989  }
2990 }
2991 
2995 - (CPImage)_tableHeaderSortImage
2996 {
2997  return [self currentValueForThemeAttribute:@"sort-image"];
2998 }
2999 
3003 - (CPImage)_tableHeaderReverseSortImage
3004 {
3005  return [self currentValueForThemeAttribute:@"sort-image-reversed"];
3006 }
3007 
3011 - (CPTableColumn)highlightedTableColumn
3012 {
3013  return _currentHighlightedTableColumn;
3014 }
3015 
3019 - (void)setHighlightedTableColumn:(CPTableColumn)aTableColumn
3020 {
3021  if (_currentHighlightedTableColumn == aTableColumn)
3022  return;
3023 
3024  if (_headerView)
3025  {
3026  if (_currentHighlightedTableColumn != nil)
3027  [[_currentHighlightedTableColumn headerView] unsetThemeState:CPThemeStateSelected];
3028 
3029  if (aTableColumn != nil)
3030  [[aTableColumn headerView] setThemeState:CPThemeStateSelected];
3031  }
3032 
3033  _currentHighlightedTableColumn = aTableColumn;
3034 }
3035 
3042 - (BOOL)canDragRowsWithIndexes:(CPIndexSet)rowIndexes atPoint:(CGPoint)mouseDownPoint
3043 {
3044  return [rowIndexes count] > 0 && [self numberOfRows] > 0 && [self numberOfColumns] > 0;
3045 }
3046 
3057 - (CPImage)dragImageForRowsWithIndexes:(CPIndexSet)dragRows tableColumns:(CPArray)theTableColumns event:(CPEvent)dragEvent offset:(CGPoint)dragImageOffset
3058 {
3059  return [self valueForThemeAttribute:@"image-generic-file"];
3060 }
3061 
3074 - (CPView)dragViewForRowsWithIndexes:(CPIndexSet)theDraggedRows tableColumns:(CPArray)theTableColumns event:(CPEvent)theDragEvent offset:(CGPoint)dragViewOffset
3075 {
3076  var bounds = [self bounds],
3077  dragView = [[CPView alloc] initWithFrame:bounds];
3078 
3079  [dragView setAlphaValue:0.7];
3080 
3081  // We have to fetch all the data views for the selected rows and columns
3082  // After that we can copy these add them to a transparent drag view and use that drag view
3083  // to make it appear we are dragging images of those rows (as you would do in regular Cocoa)
3084  [self _enumerateViewsInRows:theDraggedRows tableColumns:theTableColumns usingBlock:function(v, row, tableColumn, stop)
3085  {
3086  var column = [_tableColumns indexOfObjectIdenticalTo:tableColumn],
3087  newDataView = [self preparedViewAtColumn:column row:row];
3088 
3089  [dragView addSubview:newDataView];
3090  }];
3091 
3092  var dragPoint = [self convertPoint:[theDragEvent locationInWindow] fromView:nil];
3093  dragViewOffset.x = CGRectGetWidth(bounds) / 2 - dragPoint.x;
3094  dragViewOffset.y = CGRectGetHeight(bounds) / 2 - dragPoint.y;
3095 
3096  return dragView;
3097 }
3098 
3099 - (CPView)_dragViewForColumn:(CPInteger)columnIndex
3100 {
3101  var headerFrame = [_headerView frame],
3102  visibleRect = [self visibleRect],
3103  visibleRows = [self rowsInRect:visibleRect],
3104  columnRect = [self rectOfColumn:columnIndex],
3105  tableColumn = [[self tableColumns] objectAtIndex:columnIndex],
3106  tableColumnUID = [tableColumn UID],
3107  columnHeaderView = [tableColumn headerView],
3108  columnHeaderFrame = [columnHeaderView frame],
3109  frame = CGRectMake(MAX(CGRectGetMinX(columnRect) - CGRectGetMinX(visibleRect), 0.0),
3110  0.0,
3111  CGRectGetWidth(columnHeaderFrame),
3112  CGRectGetHeight(visibleRect) + CGRectGetHeight(headerFrame));
3113 
3114  // We need a wrapper view around the header and column, this is what will be dragged
3115  var dragView = [[_CPColumnDragDrawingView alloc] initWithFrame:frame];
3116 
3117  [dragView setTableView:self];
3118  [dragView setColumnIndex:columnIndex];
3119  [dragView setBackgroundColor:[CPColor clearColor]];
3120  [dragView setAlphaValue:0.6];
3121 
3122  // Now a view that clips the column data views, which itself is clipped to the content view
3123  var columnVisRect = CGRectIntersection(columnRect, visibleRect);
3124 
3125  frame = CGRectMake(0.0, CGRectGetHeight(headerFrame), CGRectGetWidth(columnVisRect), CGRectGetHeight(columnVisRect));
3126 
3127  var columnClipView = [[CPView alloc] initWithFrame:frame];
3128 
3129  [dragView addSubview:columnClipView];
3130  [dragView setColumnClipView:columnClipView];
3131  _draggedColumnIsSelected = [self isColumnSelected:columnIndex];
3132 
3133  var columnLeft = CGRectGetMinX(columnRect),
3134  offset = CGPointMake(columnLeft, CGRectGetMinY(visibleRect));
3135 
3136  [self _enumerateViewsInRows:_exposedRows columns:[CPIndexSet indexSetWithIndex:columnIndex] usingBlock:function(dataView, row, column, stop)
3137  {
3138  [self _addDraggedDataView:dataView toView:columnClipView forColumn:column row:row offset:offset];
3139 
3140  delete (_dataViewsForRows[row][tableColumnUID]);
3141  }];
3142 
3143  // Add the column header view
3144  columnHeaderFrame.origin = CGPointMakeZero();
3145 
3146  var dragColumnHeaderView = [[_CPTableColumnHeaderView alloc] initWithFrame:columnHeaderFrame],
3147  image = [columnHeaderView _indicatorImage];
3148 
3149  [dragColumnHeaderView setStringValue:[columnHeaderView stringValue]];
3150  [dragColumnHeaderView setThemeState:[columnHeaderView themeState]];
3151  [dragColumnHeaderView _setIndicatorImage:image];
3152 
3153  // Give it a tag so it can be found later
3154  [dragColumnHeaderView setTag:1];
3155 
3156  [dragView addSubview:dragColumnHeaderView];
3157 
3158  // While dragging, the column is deselected in the table view
3159  [_selectedColumnIndexes removeIndex:columnIndex];
3160 
3161  return dragView;
3162 }
3163 
3164 - (void)_addDraggedDataView:(CPView)aDataView toView:(CPView)aSuperview forColumn:(CPInteger)column row:(CPInteger)row offset:(CGPoint)offset
3165 {
3166  var dataViewFrame = [self frameOfDataViewAtColumn:column row:row];
3167 
3168  dataViewFrame.origin.x -= offset.x;
3169  // Offset by table header height - scroll position
3170  dataViewFrame.origin.y -= offset.y;
3171 
3172  [aDataView setFrame:dataViewFrame];
3173 
3174  [aSuperview addSubview:aDataView];
3175 }
3176 
3181 - (void)setDraggingSourceOperationMask:(CPDragOperation)mask forLocal:(BOOL)isLocal
3182 {
3183  //ignore local for the time being since only one capp app can run at a time...
3184  _dragOperationDefaultMask = mask;
3185 }
3186 
3192 - (void)setDropRow:(CPInteger)row dropOperation:(CPTableViewDropOperation)operation
3193 {
3194  if (row > [self numberOfRows] && operation === CPTableViewDropOn)
3195  {
3196  var numberOfRows = [self numberOfRows] + 1,
3197  reason = @"Attempt to set dropRow=" + row +
3198  " dropOperation=CPTableViewDropOn when [0 - " + numberOfRows + "] is valid range of rows.";
3199 
3200  [[CPException exceptionWithName:@"Error" reason:reason userInfo:nil] raise];
3201  }
3202 
3203  _retargetedDropRow = row;
3204  _retargetedDropOperation = operation;
3205 }
3206 
3218 - (void)setDraggingDestinationFeedbackStyle:(CPTableViewDraggingDestinationFeedbackStyle)aStyle
3219 {
3220  //FIX ME: this should vary up the highlight color, currently nothing is being done with it
3221  _destinationDragStyle = aStyle;
3222 }
3223 
3234 - (CPTableViewDraggingDestinationFeedbackStyle)draggingDestinationFeedbackStyle
3235 {
3236  return _destinationDragStyle;
3237 }
3238 
3244 - (void)setVerticalMotionCanBeginDrag:(BOOL)aFlag
3245 {
3246  _verticalMotionCanDrag = aFlag;
3247 }
3248 
3252 - (BOOL)verticalMotionCanBeginDrag
3253 {
3254  return _verticalMotionCanDrag;
3255 }
3256 
3257 - (CPTableColumn)_tableColumnForSortDescriptor:(CPSortDescriptor)theSortDescriptor
3258 {
3259  var tableColumns = [self tableColumns];
3260 
3261  for (var i = 0; i < [tableColumns count]; i++)
3262  {
3263  var tableColumn = [tableColumns objectAtIndex:i],
3264  sortDescriptorPrototype = [tableColumn sortDescriptorPrototype];
3265 
3266  if (!sortDescriptorPrototype)
3267  continue;
3268 
3269  if ([sortDescriptorPrototype key] === [theSortDescriptor key]
3270  && [sortDescriptorPrototype selector] === [theSortDescriptor selector])
3271  {
3272  return tableColumn;
3273  }
3274  }
3275 
3276  return nil;
3277 }
3278 
3284 - (void)setSortDescriptors:(CPArray)sortDescriptors
3285 {
3286  var oldSortDescriptors = [[self sortDescriptors] copy],
3287  newSortDescriptors = [CPArray array];
3288 
3289  if (sortDescriptors !== nil)
3290  [newSortDescriptors addObjectsFromArray:sortDescriptors];
3291 
3292  if ([newSortDescriptors isEqual:oldSortDescriptors])
3293  return;
3294 
3295  _sortDescriptors = newSortDescriptors;
3296 
3297  var oldColumn = nil,
3298  newColumn = nil;
3299 
3300  if ([newSortDescriptors count] > 0)
3301  {
3302  var newMainSortDescriptor = [newSortDescriptors objectAtIndex:0];
3303  newColumn = [self _tableColumnForSortDescriptor:newMainSortDescriptor];
3304  }
3305 
3306  if ([oldSortDescriptors count] > 0)
3307  {
3308  var oldMainSortDescriptor = [oldSortDescriptors objectAtIndex:0];
3309  oldColumn = [self _tableColumnForSortDescriptor:oldMainSortDescriptor];
3310  }
3311 
3312  var image = [newMainSortDescriptor ascending] ? [self _tableHeaderSortImage] : [self _tableHeaderReverseSortImage];
3313  [self setIndicatorImage:nil inTableColumn:oldColumn];
3314  [self setIndicatorImage:image inTableColumn:newColumn];
3315 
3316  [self _sendDataSourceSortDescriptorsDidChange:oldSortDescriptors];
3317 
3318  var binderClass = [[self class] _binderClassForBinding:@"sortDescriptors"];
3319  [[binderClass getBinding:@"sortDescriptors" forObject:self] reverseSetValueFor:@"sortDescriptors"];
3320 }
3321 
3325 - (CPArray)sortDescriptors
3326 {
3327  return _sortDescriptors;
3328 }
3329 
3333 - (id)_objectValueForTableColumn:(CPTableColumn)aTableColumn row:(CPInteger)aRowIndex useCache:(BOOL)useCache
3334 {
3335  var objectValue;
3336 
3337  var tableColumnUID = [aTableColumn UID],
3338  tableColumnObjectValues = _objectValues[tableColumnUID];
3339 
3340  if (!tableColumnObjectValues)
3341  {
3342  tableColumnObjectValues = [];
3343  _objectValues[tableColumnUID] = tableColumnObjectValues;
3344  }
3345 
3346  if (useCache)
3347  objectValue = tableColumnObjectValues[aRowIndex];
3348 
3349  // tableView:objectValueForTableColumn:row: is optional if content bindings are in place.
3350  if (objectValue === undefined)
3351  {
3352  if ([self _dataSourceRespondsToObjectValueForTableColumn])
3353  {
3354  objectValue = [self _sendDataSourceObjectValueForTableColumn:aTableColumn row:aRowIndex];
3355  tableColumnObjectValues[aRowIndex] = objectValue;
3356  }
3357  else if (!_isViewBased && ![self infoForBinding:@"content"])
3358  {
3359  CPLog.warn(@"no content binding established and data source " + [_dataSource description] + " does not implement tableView:objectValueForTableColumn:row:");
3360  }
3361  }
3362 
3363  return objectValue;
3364 }
3365 
3366 
3370 - (CGRect)exposedRect
3371 {
3372  if (!_exposedRect)
3373  {
3374  var superview = [self superview];
3375 
3376  // FIXME: Should we be rect intersecting in case
3377  // there are multiple views in the clip view?
3378  if ([superview isKindOfClass:[CPClipView class]])
3379  _exposedRect = [superview bounds];
3380  else
3381  _exposedRect = [self bounds];
3382  }
3383 
3384  return _exposedRect;
3385 }
3386 
3390 - (void)load
3391 {
3392  if (_reloadAllRows)
3393  {
3394  [self _unloadDataViewsInRows:_exposedRows columns:_exposedColumns];
3395 
3396  _exposedRows = [CPIndexSet indexSet];
3397  _exposedColumns = [CPIndexSet indexSet];
3398 
3399  _reloadAllRows = NO;
3400  }
3401 
3402  var exposedRect = [self exposedRect],
3403  exposedRows = [CPIndexSet indexSetWithIndexesInRange:[self rowsInRect:exposedRect]],
3404  exposedColumns = [self columnIndexesInRect:exposedRect],
3405  obscuredRows = [_exposedRows copy],
3406  obscuredColumns = [_exposedColumns copy];
3407 
3408  [obscuredRows removeIndexes:exposedRows];
3409  [obscuredColumns removeIndexes:exposedColumns];
3410 
3411  var newlyExposedRows = [exposedRows copy],
3412  newlyExposedColumns = [exposedColumns copy];
3413 
3414  [newlyExposedRows removeIndexes:_exposedRows];
3415  [newlyExposedColumns removeIndexes:_exposedColumns];
3416 
3417  var previouslyExposedRows = [exposedRows copy],
3418  previouslyExposedColumns = [exposedColumns copy];
3419 
3420  [previouslyExposedRows removeIndexes:newlyExposedRows];
3421  [previouslyExposedColumns removeIndexes:newlyExposedColumns];
3422 
3423  [self _unloadDataViewsInRows:previouslyExposedRows columns:obscuredColumns];
3424  [self _unloadDataViewsInRows:obscuredRows columns:previouslyExposedColumns];
3425  [self _unloadDataViewsInRows:obscuredRows columns:obscuredColumns];
3426  [self _unloadDataViewsInRows:newlyExposedRows columns:newlyExposedColumns];
3427 
3428  [self _loadDataViewsInRows:previouslyExposedRows columns:newlyExposedColumns];
3429  [self _loadDataViewsInRows:newlyExposedRows columns:previouslyExposedColumns];
3430  [self _loadDataViewsInRows:newlyExposedRows columns:newlyExposedColumns];
3431 
3432  _exposedRows = exposedRows;
3433  _exposedColumns = exposedColumns;
3434 
3435  [_tableDrawView setFrame:exposedRect];
3436 
3437  [self setNeedsDisplay:YES];
3438 
3439  // if we have any columns to remove do that here
3440 
3441  if (_needsDifferedTableColumnRemove)
3442  {
3443  var removeCount = [_differedColumnDataToRemove count],
3444  removeIndexes = [CPIndexSet indexSet];
3445 
3446  for (var i = 0; i < removeCount; i++)
3447  {
3448  var data = _differedColumnDataToRemove[i],
3449  tableColumn = data.column,
3450  columnIdx = [_tableColumns indexOfObject:tableColumn];
3451 
3452  if (columnIdx !== CPNotFound)
3453  [removeIndexes addIndex:columnIdx];
3454  }
3455 
3456  var rowIndexes = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, [self numberOfRows])];
3457  [self _unloadDataViewsInRows:rowIndexes columns:removeIndexes];
3458 
3459  [_tableColumns removeObjectsAtIndexes:removeIndexes];
3460 
3461  _dirtyTableColumnRangeIndex = 0;
3462  [self _recalculateTableColumnRanges];
3463 
3464  [_differedColumnDataToRemove removeAllObjects];
3465  _needsDifferedTableColumnRemove = NO;
3466  }
3467 
3468  // Now clear all the leftovers
3469  // FIXME: this could be faster!
3470  for (var identifier in _cachedDataViews)
3471  {
3472  var dataViews = _cachedDataViews[identifier],
3473  count = dataViews.length;
3474 
3475  while (count--)
3476  [dataViews[count] removeFromSuperview];
3477  }
3478 }
3479 
3483 - (void)_unloadDataViewsInRows:(CPIndexSet)rowIndexes columns:(CPIndexSet)columnIndexes
3484 {
3485  if (![rowIndexes count] || ![columnIndexes count] || [columnIndexes lastIndex] >= [_tableColumns count])
3486  return;
3487 
3488  // If and edited view is about to be unloaded, cleanup its state before enqueuing.
3489  if ([columnIndexes containsIndex:_editingColumn] && [rowIndexes containsIndex:_editingRow])
3490  [self _resignEditedView];
3491 
3492  var tableColumns = [_tableColumns objectsAtIndexes:columnIndexes];
3493 
3494  [self _enumerateViewsInRows:rowIndexes tableColumns:tableColumns usingBlock:function(dataView, row, tableColumn, stop)
3495  {
3496  var dataViewsForRows = _dataViewsForRows[row],
3497  tableColumnUID = [tableColumn UID];
3498 
3499  delete (dataViewsForRows[tableColumnUID]);
3500 
3501  [self _sendDelegateWillRemoveView:dataView forTableColumn:tableColumn row:row];
3502  [self _enqueueReusableDataView:dataView];
3503  }];
3504 }
3505 
3509 - (void)_loadDataViewsInRows:(CPIndexSet)rowIndexes columns:(CPIndexSet)columnIndexes
3510 {
3511  if (![rowIndexes count] || ![columnIndexes count])
3512  return;
3513 
3515 
3516  if (_numberOfHiddenColumns > 0)
3517  columnIndexes = [columnIndexes indexesPassingTest:function(idx, stop)
3518  {
3519  return ![_tableColumns[idx] isHidden];
3520  }];
3521 
3522  [rowIndexes enumerateIndexesUsingBlock:function(rowIndex, stopRow)
3523  {
3524  if (!_dataViewsForRows[rowIndex])
3525  _dataViewsForRows[rowIndex] = {};
3526 
3527  var dataViewsForRow = _dataViewsForRows[rowIndex],
3528  isRowSelected = [self isRowSelected:rowIndex],
3529  row = rowIndex;
3530 
3531  [columnIndexes enumerateIndexesUsingBlock:function(columnIndex, stopCol)
3532  {
3533  var tableColumn = _tableColumns[columnIndex],
3534  tableColumnUID = [tableColumn UID],
3535  dataView = [self _preparedViewAtColumn:columnIndex row:row isRowSelected:isRowSelected];
3536 
3537  if ([dataView superview] !== self)
3538  [self addSubview:dataView];
3539 
3540  dataViewsForRow[tableColumnUID] = dataView;
3541  }];
3542  }];
3543 }
3544 
3545 - (CPView)preparedViewAtColumn:(CPInteger)column row:(CPInteger)row
3546 {
3547  return [self _preparedViewAtColumn:column row:row isRowSelected:[self isRowSelected:row]];
3548 }
3549 
3550 - (CPView)_preparedViewAtColumn:(CPInteger)column row:(CPInteger)row isRowSelected:(BOOL)isRowSelected
3551 {
3552  var tableColumn = _tableColumns[column],
3553  tableColumnUID = [tableColumn UID],
3554  dataView = [self _newDataViewForRow:row tableColumn:tableColumn];
3555 
3556  [dataView setFrame:[self frameOfDataViewAtColumn:column row:row]];
3557 
3558  [self _setObjectValueForTableColumn:tableColumn row:row forView:dataView];
3559 
3560  if (_selectionHighlightStyle !== CPTableViewSelectionHighlightStyleNone && (isRowSelected || [self isColumnSelected:column]))
3561  _BlockSelectView(dataView, row, column);
3562  else
3563  _BlockDeselectView(dataView, row, column);
3564 
3565  // FIX ME: for performance reasons we might consider diverging from cocoa and moving this to the reloadData method
3566  if (_implementedDelegateMethods & CPTableViewDelegate_tableView_isGroupRow_)
3567  {
3568  if ([_delegate tableView:self isGroupRow:row])
3569  {
3570  [_groupRows addIndex:row];
3571  [dataView setThemeState:CPThemeStateGroupRow];
3572  }
3573  else
3574  {
3575  [_groupRows removeIndexesInRange:CPMakeRange(row, 1)];
3576  [dataView unsetThemeState:CPThemeStateGroupRow];
3577  }
3578 
3579  [self setNeedsDisplay:YES];
3580  }
3581 
3582  if (!_isViewBased)
3583  [self _setEditingState:NO forView:dataView];
3584 
3585  [self _sendDelegateWillDisplayView:dataView forTableColumn:tableColumn row:row];
3586 
3587  return dataView;
3588 }
3589 
3590 - (void)_setObjectValueForTableColumn:(CPTableColumn)aTableColumn row:(CPInteger)aRow forView:(CPView)aDataView
3591 {
3592  [self _setObjectValueForTableColumn:aTableColumn row:aRow forView:aDataView useCache:!_invalidateObjectValuesCache];
3593 }
3594 
3595 - (void)_setObjectValueForTableColumn:(CPTableColumn)aTableColumn row:(CPInteger)aRow forView:(CPView)aDataView useCache:(BOOL)useCache
3596 {
3597  if (_implementedDataSourceMethods & CPTableViewDataSource_tableView_objectValueForTableColumn_row_)
3598  [aDataView setObjectValue:[self _objectValueForTableColumn:aTableColumn row:aRow useCache:useCache]];
3599 
3600  // This gives the table column an opportunity to apply its bindings.
3601  // It will override the value set above if there is a binding.
3602 
3603  if (_contentBindingExplicitlySet)
3604  [self _prepareContentBindedDataView:aDataView forRow:aRow];
3605  else
3606  // For both cell-based and view-based
3607  [aTableColumn _prepareDataView:aDataView forRow:aRow];
3608 }
3609 
3610 - (void)_prepareContentBindedDataView:(CPView)dataView forRow:(CPInteger)aRow
3611 {
3612  var binder = [CPTableContentBinder getBinding:@"content" forObject:self],
3613  content = [binder content],
3614  rowContent = [content objectAtIndex:aRow];
3615 
3616  [dataView setObjectValue:rowContent];
3617 }
3618 
3622 - (void)_layoutViewsForRowIndexes:(CPIndexSet)rowIndexes columnIndexes:(CPIndexSet)columnIndexes
3623 {
3624  [self _enumerateViewsInRows:rowIndexes columns:columnIndexes usingBlock:function(view, row, column, stop)
3625  {
3626  [view setFrame:[self frameOfDataViewAtColumn:column row:row]];
3627  }];
3628 }
3629 
3633 - (CPView)_newDataViewForRow:(CPInteger)aRow tableColumn:(CPTableColumn)aTableColumn
3634 {
3635  var view = nil;
3636 
3637  if (_viewForTableColumnRowSelector)
3638  view = self.isa.objj_msgSend2(self, _viewForTableColumnRowSelector, aTableColumn, aRow);
3639 
3640  if (!view)
3641  {
3642  // For cell-based tables, use the dataView prototype identifier as view identifier
3643  // instead of column identifier because 1/ the column identifier may be nil
3644  // and 2/ the tableColumn dataView may change after the column identifier was set.
3645  var identifier;
3646 
3647  if (_isViewBased)
3648  {
3649  identifier = [aTableColumn identifier];
3650  view = [self makeViewWithIdentifier:identifier owner:_delegate];
3651  }
3652  else
3653  {
3654  identifier = [[aTableColumn dataView] UID];
3655  view = [self makeViewWithIdentifier:identifier owner:_delegate];
3656 
3657  if (!view)
3658  view = [aTableColumn _newDataView];
3659  }
3660 
3661  [view setIdentifier:identifier];
3662  }
3663 
3664  return view;
3665 }
3666 
3667 /*
3668  Returns a view with the specified identifier.
3669 
3670  @param identifier The view identifier. Must not be nil.
3671  @param owner The owner of the CIB that may be loaded and instantiated to create a new view with the particular identifier.
3672  @return A view for the row.
3673 
3674  @discussion
3675  Typically identifier is associated with an external CIB and the table view will automatically instantiate the CIB with the provided owner. The owner of the CIB that may be loaded and instantiated to create a new view with the particular identifier is typically the table view’s delegate. The owner is useful in setting up outlets and target and actions from the view.
3676 
3677  This method will typically be called by the delegate in tableView:viewForTableColumn:row:, but it can also be overridden to provide custom views for the identifier. This method may also return a reused view with the same identifier that was no longer available on screen.
3678 */
3679 - (id)makeViewWithIdentifier:(CPString)anIdentifier owner:(id)anOwner
3680 {
3681  if (!anIdentifier)
3682  return nil;
3683 
3684  var view = nil,
3685  // See if we have some reusable views available
3686  reusableViews = _cachedDataViews[anIdentifier];
3687 
3688  if (reusableViews && reusableViews.length)
3689  view = reusableViews.pop();
3690  // Otherwise see if we have a view in the cib with this identifier
3691  else if (_isViewBased)
3692  view = [self _unarchiveViewWithIdentifier:anIdentifier owner:anOwner];
3693 
3694  return view;
3695 }
3696 
3709 - (id)viewAtColumn:(CPInteger)column row:(CPInteger)row makeIfNecessary:(BOOL)makeIfNecessary
3710 {
3711  if (row > (_numberOfRows - 1))
3712  [CPException raise:CPInvalidArgumentException
3713  reason:@"Row " + row + " out of row range [0-" + (_numberOfRows - 1) + "] for rowViewAtRow:createIfNeeded:"];
3714 
3715  if (column > (NUMBER_OF_COLUMNS() - 1))
3716  [CPException raise:CPInvalidArgumentException
3717  reason:@"Column " + column + " out of row range [0-" + (NUMBER_OF_COLUMNS ()- 1) + "] for rowViewAtRow:createIfNeeded:"];
3718 
3719  var dataViewsForRow = _dataViewsForRows[row],
3720  tableColumn = _tableColumns[column],
3721  tableColumnUID = [tableColumn UID],
3722  view = dataViewsForRow ? dataViewsForRow[tableColumnUID] : nil;
3723 
3724  if (!makeIfNecessary)
3725  return view || nil;
3726 
3727  if (!view)
3728  {
3729  if (!dataViewsForRow)
3730  {
3731  dataViewsForRow = {}
3732  _dataViewsForRows[row] = dataViewsForRow;
3733  }
3734 
3735  // Here we will add this view to the tableView and add it to the exposedRows adn columns.
3736  // The view will be deleted if necessary during the next run loop cycle of the table view
3737  // This is how cocoa works
3738  view = [self preparedViewAtColumn:column row:row];
3739 
3740  if ([view superview] !== self)
3741  [self addSubview:view];
3742 
3743  dataViewsForRow[tableColumnUID] = view;
3744 
3745  [_exposedRows addIndex:row];
3746  [_exposedColumns addIndex:column];
3747 
3748  // Make sure to layout the tableView again
3749  [self setNeedsLayout];
3750  }
3751 
3752  return view;
3753 }
3754 
3758 - (CPView)_unarchiveViewWithIdentifier:(CPString)anIdentifier owner:(id)anOwner
3759 {
3760  var cib = [_archivedDataViews objectForKey:anIdentifier];
3761 
3762  if (!cib && !_unavailable_custom_cibs[anIdentifier])
3763  {
3764  var bundle = anOwner ? [CPBundle bundleForClass:[anOwner class]] : [CPBundle mainBundle];
3765  cib = [[CPCib alloc] initWithCibNamed:anIdentifier bundle:bundle];
3766  }
3767 
3768  if (!cib)
3769  {
3770  _unavailable_custom_cibs[anIdentifier] = YES;
3771  return nil;
3772  }
3773 
3774  var objects = [],
3775  load = [cib instantiateCibWithOwner:anOwner topLevelObjects:objects];
3776 
3777  if (!load)
3778  return nil;
3779 
3780  var count = objects.length;
3781 
3782  while (count--)
3783  {
3784  var obj = objects[count];
3785 
3786  if ([obj isKindOfClass:[CPView class]])
3787  {
3788  [obj setIdentifier:anIdentifier];
3789  [obj setAutoresizingMask:CPViewNotSizable];
3790 
3791  return obj;
3792  }
3793  }
3794 
3795  return nil;
3796 }
3797 
3798 - (void)_updateIsViewBased
3799 {
3800  if ([self _delegateRespondsToViewForTableColumn])
3801  _viewForTableColumnRowSelector = @selector(_sendDelegateViewForTableColumn:row:);
3802  else if ([self _delegateRespondsToDataViewForTableColumn])
3803  _viewForTableColumnRowSelector = @selector(_sendDelegateDataViewForTableColumn:row:);
3804 
3805  _isViewBased = (_viewForTableColumnRowSelector !== nil || _archivedDataViews !== nil);
3806 }
3807 
3823 - (void)enumerateAvailableViewsUsingBlock:(Function/*CPView *dataView, CPInteger row, CPInteger column*, @ref stop*/)handler
3824 {
3825  [self reloadData];
3826 
3827  [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
3828 
3829  [self _enumerateViewsInRows:_exposedRows columns:_exposedColumns usingBlock:handler];
3830 }
3831 
3836 - (void)_enumerateViewsInRows:(CPIndexSet)rowIndexes columns:(CPIndexSet)columnIndexes usingBlock:(Function/*CPView dataView, CPInteger row, CPInteger column, @ref stop*/)handler
3837 {
3838  [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
3839 
3840  [rowIndexes enumerateIndexesUsingBlock:function(rowIndex, stopRow)
3841  {
3842  var dataViewsForRow = _dataViewsForRows[rowIndex];
3843 
3844  if (!dataViewsForRow)
3845  return;
3846 
3847  var row = rowIndex;
3848 
3849  [columnIndexes enumerateIndexesUsingBlock:function(columnIndex, stopCol)
3850  {
3851  var tableColumnUID = [_tableColumns[columnIndex] UID],
3852  view = dataViewsForRow[tableColumnUID];
3853 
3854  if (view)
3855  handler(view, row, columnIndex, stopCol);
3856 
3857  if (stopCol())
3858  stopRow(YES);
3859  }];
3860  }];
3861 }
3862 
3863 - (void)_enumerateViewsInRows:(CPIndexSet)rowIndexes tableColumns:(CPArray)tableColumns usingBlock:(Function/*CPView dataView, CPInteger row, CPtableColumn tableColumn, CPInteger column, @ref stop*/)handler
3864 {
3865  [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
3866 
3867  [rowIndexes enumerateIndexesUsingBlock:function(rowIndex, stopRow)
3868  {
3869  var dataViewsForRow = _dataViewsForRows[rowIndex];
3870 
3871  if (!dataViewsForRow)
3872  return;
3873 
3874  var row = rowIndex;
3875 
3876  [tableColumns enumerateObjectsUsingBlock:function(tableColumn, idx, stopCol)
3877  {
3878  var tableColumnUID = [tableColumn UID],
3879  view = dataViewsForRow[tableColumnUID];
3880 
3881  if (view)
3882  handler(view, row, tableColumn, stopCol);
3883 
3884  if (stopCol())
3885  stopRow(YES);
3886  }];
3887  }];
3888 }
3889 
3893 - (void)_enqueueReusableDataView:(CPView)aDataView
3894 {
3895  if (!aDataView)
3896  return;
3897 
3898  // FIXME: yuck!
3899  var identifier = [aDataView identifier];
3900 
3901  if ([aDataView hasThemeState:CPThemeStateSelectedDataView])
3902  [aDataView unsetThemeState:CPThemeStateSelectedDataView];
3903 
3904  if (!_cachedDataViews[identifier])
3905  _cachedDataViews[identifier] = [aDataView];
3906  else
3907  _cachedDataViews[identifier].push(aDataView);
3908 }
3909 
3914 - (void)setFrameSize:(CGSize)aSize
3915 {
3916  [super setFrameSize:aSize];
3917 
3918  if (_headerView)
3919  [_headerView setFrameSize:CGSizeMake(CGRectGetWidth([self frame]), CGRectGetHeight([_headerView frame]))];
3920 
3921  _exposedRect = nil;
3922 }
3923 
3927 - (void)setFrameOrigin:(CGPoint)aPoint
3928 {
3929  [super setFrameOrigin:aPoint];
3930 
3931  _exposedRect = nil;
3932 }
3933 
3937 - (void)setBoundsOrigin:(CGPoint)aPoint
3938 {
3939  [super setBoundsOrigin:aPoint];
3940 
3941  _exposedRect = nil;
3942 }
3943 
3947 - (void)setBoundsSize:(CGSize)aSize
3948 {
3949  [super setBoundsSize:aSize];
3950 
3951  _exposedRect = nil;
3952 }
3953 
3957 - (void)setNeedsDisplay:(BOOL)aFlag
3958 {
3959  [super setNeedsDisplay:aFlag];
3960  [_tableDrawView setNeedsDisplay:aFlag];
3961 
3962  [[self headerView] setNeedsDisplay:YES];
3963 }
3964 
3968 - (void)setNeedsLayout
3969 {
3970  [super setNeedsLayout];
3971  [[self headerView] setNeedsLayout];
3972 }
3973 
3977 - (BOOL)_isFocused
3978 {
3979  return [[self window] isKeyWindow] && ([[self window] firstResponder] === self || _editingRow !== CPNotFound);
3980 }
3981 
3985 - (void)_drawRect:(CGRect)aRect
3986 {
3987  // FIX ME: All three of these methods will likely need to be rewritten for 1.0
3988  // We've got grid drawing in highlightSelection and crap everywhere.
3989 
3990  var exposedRect = [self exposedRect];
3991 
3992  [self drawBackgroundInClipRect:exposedRect];
3993  [self highlightSelectionInClipRect:exposedRect];
3994  [self drawGridInClipRect:exposedRect];
3995 
3996  if (_implementsCustomDrawRow)
3997  [self _drawRows:_exposedRows clipRect:exposedRect];
3998 }
3999 
4005 - (void)drawBackgroundInClipRect:(CGRect)aRect
4006 {
4007  if (!_usesAlternatingRowBackgroundColors)
4008  return;
4009 
4010  var rowColors = [self alternatingRowBackgroundColors],
4011  colorCount = [rowColors count];
4012 
4013  if (colorCount === 0)
4014  return;
4015 
4016  var context = [[CPGraphicsContext currentContext] graphicsPort];
4017 
4018  if (colorCount === 1)
4019  {
4020  CGContextSetFillColor(context, rowColors[0]);
4021  CGContextFillRect(context, aRect);
4022 
4023  return;
4024  }
4025 
4026  var exposedRows = [self _exposedRowsInRect:aRect],
4027  firstRow = FLOOR(exposedRows.location / colorCount) * colorCount,
4028  lastRow = CPMaxRange(exposedRows) - 1,
4029  colorIndex = 0,
4030  groupRowRects = [];
4031 
4032  //loop through each color so we only draw once for each color
4033  while (colorIndex < colorCount)
4034  {
4035  CGContextBeginPath(context);
4036 
4037  for (var row = firstRow + colorIndex; row <= lastRow; row += colorCount)
4038  {
4039  // if it's not a group row draw it otherwise we draw it later
4040  if (![_groupRows containsIndex:row])
4041  CGContextAddRect(context, CGRectIntersection(aRect, [self _rectOfRow:row checkRange:NO]));
4042  else
4043  groupRowRects.push(CGRectIntersection(aRect, [self _rectOfRow:row checkRange:NO]));
4044  }
4045 
4046  CGContextClosePath(context);
4047 
4048  CGContextSetFillColor(context, rowColors[colorIndex]);
4049  CGContextFillPath(context);
4050 
4051  colorIndex++;
4052  }
4053 
4054  [self _drawGroupRowsForRects:groupRowRects];
4055 }
4056 
4061 - (void)drawGridInClipRect:(CGRect)aRect
4062 {
4063  var context = [[CPGraphicsContext currentContext] graphicsPort],
4064  gridStyleMask = [self gridStyleMask],
4065  lineThickness = [self currentValueForThemeAttribute:@"grid-line-thickness"];
4066 
4068  return;
4069 
4070  CGContextBeginPath(context);
4071 
4072  if (gridStyleMask & CPTableViewSolidHorizontalGridLineMask)
4073  {
4074  var exposedRows = [self _exposedRowsInRect:aRect],
4075  row = exposedRows.location,
4076  lastRow = CPMaxRange(exposedRows) - 1,
4077  rowY = -lineThickness / 2,
4078  minX = CGRectGetMinX(aRect),
4079  maxX = CGRectGetMaxX(aRect);
4080 
4081  for (; row <= lastRow; ++row)
4082  {
4083  // grab each row rect and add the top and bottom lines
4084  var rowRect = [self _rectOfRow:row checkRange:NO],
4085  rowY = CGRectGetMaxY(rowRect) - lineThickness / 2;
4086 
4087  CGContextMoveToPoint(context, minX, rowY);
4088  CGContextAddLineToPoint(context, maxX, rowY);
4089  }
4090 
4091  if (_rowHeight > 0.0)
4092  {
4093  var rowHeight = FULL_ROW_HEIGHT(),
4094  totalHeight = CGRectGetMaxY(aRect) - lineThickness / 2;
4095 
4096  while (rowY < totalHeight)
4097  {
4098  rowY += rowHeight;
4099 
4100  CGContextMoveToPoint(context, minX, rowY);
4101  CGContextAddLineToPoint(context, maxX, rowY);
4102  }
4103  }
4104  }
4105 
4106  if (gridStyleMask & CPTableViewSolidVerticalGridLineMask)
4107  {
4108  var exposedColumnIndexes = [self columnIndexesInRect:aRect],
4109  columnsArray = [];
4110 
4111  [exposedColumnIndexes getIndexes:columnsArray maxCount:-1 inIndexRange:nil];
4112 
4113  var columnArrayIndex = 0,
4114  columnArrayCount = columnsArray.length,
4115  minY = CGRectGetMinY(aRect),
4116  maxY = CGRectGetMaxY(aRect);
4117 
4118  for (; columnArrayIndex < columnArrayCount; ++columnArrayIndex)
4119  {
4120  var columnRect = [self rectOfColumn:columnsArray[columnArrayIndex]],
4121  columnX = CGRectGetMaxX(columnRect) - lineThickness / 2;
4122 
4123  CGContextMoveToPoint(context, columnX, minY);
4124  CGContextAddLineToPoint(context, columnX, maxY);
4125  }
4126  }
4127 
4128  CGContextClosePath(context);
4129  CGContextSetStrokeColor(context, [self gridColor]);
4130  CGContextSetLineWidth(context, lineThickness);
4131  CGContextStrokePath(context);
4132 }
4133 
4139 - (void)highlightSelectionInClipRect:(CGRect)aRect
4140 {
4141  if (_selectionHighlightStyle === CPTableViewSelectionHighlightStyleNone)
4142  return;
4143 
4144  var context = [[CPGraphicsContext currentContext] graphicsPort],
4145  indexes = [],
4146  rectSelector = @selector(rectOfRow:);
4147 
4148  if ([_selectedRowIndexes count] >= 1)
4149  {
4150  var exposedRows = [CPIndexSet indexSetWithIndexesInRange:[self rowsInRect:aRect]],
4151  firstRow = [exposedRows firstIndex],
4152  exposedRange = CPMakeRange(firstRow, [exposedRows lastIndex] - firstRow + 1);
4153 
4154  [_selectedRowIndexes getIndexes:indexes maxCount:-1 inIndexRange:exposedRange];
4155  }
4156  else if ([_selectedColumnIndexes count] >= 1)
4157  {
4158  rectSelector = @selector(rectOfColumn:);
4159 
4160  var exposedColumns = [self columnIndexesInRect:aRect],
4161  firstColumn = [exposedColumns firstIndex],
4162  exposedRange = CPMakeRange(firstColumn, [exposedColumns lastIndex] - firstColumn + 1);
4163 
4164  [_selectedColumnIndexes getIndexes:indexes maxCount:-1 inIndexRange:exposedRange];
4165  }
4166 
4167  var count,
4168  count2 = count = [indexes count];
4169 
4170  if (!count)
4171  return;
4172 
4173  var drawGradient = (CPFeatureIsCompatible(CPHTMLCanvasFeature) && _selectionHighlightStyle === CPTableViewSelectionHighlightStyleSourceList && [_selectedRowIndexes count] >= 1),
4174  deltaHeight = 0.5 * (_gridStyleMask & CPTableViewSolidHorizontalGridLineMask),
4175  focused = [self _isFocused];
4176 
4177  CGContextBeginPath(context);
4178 
4179  if (drawGradient)
4180  {
4181  var gradientCache = focused ? [self selectionGradientColors] : [self unfocusedSelectionGradientColors],
4182  topLineColor = [gradientCache objectForKey:CPSourceListTopLineColor],
4183  bottomLineColor = [gradientCache objectForKey:CPSourceListBottomLineColor],
4184  gradientColor = [gradientCache objectForKey:CPSourceListGradient];
4185  }
4186 
4187  var normalSelectionHighlightColor = focused ? [self selectionHighlightColor] : [self unfocusedSelectionHighlightColor];
4188 
4189  // don't do these lookups if there are no group rows
4190  if ([_groupRows count])
4191  {
4192  var topGroupLineColor = [CPColor colorWithCalibratedWhite:212.0 / 255.0 alpha:1.0],
4193  bottomGroupLineColor = [CPColor colorWithCalibratedWhite:185.0 / 255.0 alpha:1.0],
4194  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);
4195  }
4196 
4197  while (count--)
4198  {
4199  var currentIndex = indexes[count],
4200  rowRect = CGRectIntersection(self.isa.objj_msgSend1(self, rectSelector, currentIndex), aRect);
4201 
4202  // group rows get the same highlight style as other rows if they're source list...
4203  if (!drawGradient)
4204  var shouldUseGroupGradient = [_groupRows containsIndex:currentIndex];
4205 
4206  if (drawGradient || shouldUseGroupGradient)
4207  {
4208  var minX = CGRectGetMinX(rowRect),
4209  minY = CGRectGetMinY(rowRect),
4210  maxX = CGRectGetMaxX(rowRect),
4211  maxY = CGRectGetMaxY(rowRect) - deltaHeight;
4212 
4213  if (!drawGradient)
4214  {
4215  //If there is no source list gradient we need to close the selection path and fill it now
4216  [normalSelectionHighlightColor setFill];
4217  CGContextClosePath(context);
4218  CGContextFillPath(context);
4219  CGContextBeginPath(context);
4220  }
4221 
4222  CGContextAddRect(context, rowRect);
4223 
4224  CGContextDrawLinearGradient(context, (shouldUseGroupGradient) ? gradientGroupColor : gradientColor, rowRect.origin, CGPointMake(minX, maxY), 0);
4225 
4226  CGContextBeginPath(context);
4227  CGContextMoveToPoint(context, minX, minY + .5);
4228  CGContextAddLineToPoint(context, maxX, minY + .5);
4229  CGContextSetStrokeColor(context, (shouldUseGroupGradient) ? topGroupLineColor : topLineColor);
4230  CGContextStrokePath(context);
4231 
4232  CGContextBeginPath(context);
4233  CGContextMoveToPoint(context, minX, maxY - .5);
4234  CGContextAddLineToPoint(context, maxX, maxY - .5);
4235  CGContextSetStrokeColor(context, (shouldUseGroupGradient) ? bottomGroupLineColor : bottomLineColor);
4236  CGContextStrokePath(context);
4237  }
4238  else
4239  {
4240  var radius = [self currentValueForThemeAttribute:@"selection-radius"];
4241 
4242  if (radius > 0)
4243  {
4244  var minX = CGRectGetMinX(rowRect),
4245  maxX = CGRectGetMaxX(rowRect),
4246  minY = CGRectGetMinY(rowRect),
4247  maxY = CGRectGetMaxY(rowRect);
4248 
4249  CGContextMoveToPoint(context, minX + radius, minY);
4250  CGContextAddArcToPoint(context, maxX, minY, maxX, minY + radius, radius);
4251  CGContextAddArcToPoint(context, maxX, maxY, maxX - radius, maxY, radius);
4252  CGContextAddArcToPoint(context, minX, maxY, minX, maxY - radius, radius);
4253  CGContextAddArcToPoint(context, minX, minY, minX + radius, minY, radius);
4254  }
4255  else
4256  CGContextAddRect(context, rowRect);
4257  }
4258  }
4259 
4260  CGContextClosePath(context);
4261 
4262  if (!drawGradient)
4263  {
4264  [normalSelectionHighlightColor setFill];
4265  CGContextFillPath(context);
4266  }
4267 
4268  CGContextBeginPath(context);
4269 
4270  var gridStyleMask = [self gridStyleMask];
4271 
4272  for (var i = 0; i < count2; i++)
4273  {
4274  var rect = self.isa.objj_msgSend1(self, rectSelector, indexes[i]),
4275  minX = CGRectGetMinX(rect) - 0.5,
4276  maxX = CGRectGetMaxX(rect) - 0.5,
4277  minY = CGRectGetMinY(rect) - 0.5,
4278  maxY = CGRectGetMaxY(rect) - 0.5;
4279 
4280  if ([_selectedRowIndexes count] >= 1 && gridStyleMask & CPTableViewSolidVerticalGridLineMask)
4281  {
4282  var exposedColumns = [self columnIndexesInRect:aRect],
4283  exposedColumnIndexes = [],
4284  firstExposedColumn = [exposedColumns firstIndex],
4285  exposedRange = CPMakeRange(firstExposedColumn, [exposedColumns lastIndex] - firstExposedColumn + 1);
4286 
4287  [exposedColumns getIndexes:exposedColumnIndexes maxCount:-1 inIndexRange:exposedRange];
4288 
4289  var exposedColumnCount = [exposedColumnIndexes count];
4290 
4291  for (var c = firstExposedColumn; c < exposedColumnCount; c++)
4292  {
4293  var colRect = [self rectOfColumn:exposedColumnIndexes[c]],
4294  colX = CGRectGetMaxX(colRect) + 0.5;
4295 
4296  CGContextMoveToPoint(context, colX, minY);
4297  CGContextAddLineToPoint(context, colX, maxY);
4298  }
4299  }
4300 
4301  //if the row after the current row is not selected then there is no need to draw the bottom grid line white.
4302  if ([indexes containsObject:indexes[i] + 1])
4303  {
4304  CGContextMoveToPoint(context, minX, maxY);
4305  CGContextAddLineToPoint(context, maxX, maxY);
4306  }
4307  }
4308 
4309  CGContextClosePath(context);
4310  CGContextSetStrokeColor(context, [self currentValueForThemeAttribute:@"highlighted-grid-color"]);
4311  CGContextStrokePath(context);
4312 }
4313 
4319 - (void)_drawGroupRowsForRects:(CPArray)rects
4320 {
4321  if ((CPFeatureIsCompatible(CPHTMLCanvasFeature) && _selectionHighlightStyle === CPTableViewSelectionHighlightStyleSourceList) || !rects.length)
4322  return;
4323 
4324  var context = [[CPGraphicsContext currentContext] graphicsPort],
4325  i = rects.length;
4326 
4327  CGContextBeginPath(context);
4328 
4329  var gradientCache = [self selectionGradientColors],
4330  topLineColor = [CPColor colorWithHexString:"d3d3d3"],
4331  bottomLineColor = [CPColor colorWithHexString:"bebebd"],
4332  gradientColor = CGGradientCreateWithColorComponents(CGColorSpaceCreateDeviceRGB(), [220.0 / 255.0, 220.0 / 255.0, 220.0 / 255.0, 1.0,
4333  199.0 / 255.0, 199.0 / 255.0, 199.0 / 255.0, 1.0], [0, 1], 2),
4334  drawGradient = YES;
4335 
4336  while (i--)
4337  {
4338  var rowRect = rects[i];
4339 
4340  CGContextAddRect(context, rowRect);
4341 
4342  if (drawGradient)
4343  {
4344  var minX = CGRectGetMinX(rowRect),
4345  minY = CGRectGetMinY(rowRect),
4346  maxX = CGRectGetMaxX(rowRect),
4347  maxY = CGRectGetMaxY(rowRect);
4348 
4349  CGContextDrawLinearGradient(context, gradientColor, rowRect.origin, CGPointMake(minX, maxY), 0);
4350 
4351  CGContextBeginPath(context);
4352  CGContextMoveToPoint(context, minX, minY);
4353  CGContextAddLineToPoint(context, maxX, minY);
4354  CGContextSetStrokeColor(context, topLineColor);
4355  CGContextStrokePath(context);
4356 
4357  CGContextBeginPath(context);
4358  CGContextMoveToPoint(context, minX, maxY);
4359  CGContextAddLineToPoint(context, maxX, maxY - 1);
4360  CGContextSetStrokeColor(context, bottomLineColor);
4361  CGContextStrokePath(context);
4362  }
4363  }
4364 }
4365 
4369 - (void)_drawRows:(CPIndexSet)rowsIndexes clipRect:(CGRect)clipRect
4370 {
4371  var row = [rowsIndexes firstIndex];
4372 
4373  while (row !== CPNotFound)
4374  {
4375  [self drawRow:row clipRect:CGRectIntersection(clipRect, [self rectOfRow:row])];
4376  row = [rowsIndexes indexGreaterThanIndex:row];
4377  }
4378 }
4379 
4386 - (void)drawRow:(CPInteger)row clipRect:(CGRect)rect
4387 {
4388  // This method does currently nothing in cappuccino. Can be overridden by subclasses.
4389 
4390 }
4391 
4395 - (void)layoutSubviews
4396 {
4397  [self load];
4398 }
4399 
4403 - (void)viewWillMoveToSuperview:(CPView)aView
4404 {
4405  if ([aView isKindOfClass:[CPClipView class]])
4406  {
4407  _observedClipView = aView;
4408  }
4409  else
4410  {
4411  [self _stopObservingClipView];
4412  _observedClipView = nil;
4413  }
4414 
4415  [super viewWillMoveToSuperview:aView];
4416 }
4417 
4421 - (void)superviewBoundsChanged:(CPNotification)aNotification
4422 {
4423  _exposedRect = nil;
4424 
4425  [self setNeedsDisplay:YES];
4426  [self setNeedsLayout];
4427 }
4428 
4432 - (void)superviewFrameChanged:(CPNotification)aNotification
4433 {
4434  _exposedRect = nil;
4435 
4436  [self tile];
4437 }
4438 
4439 /*
4440  @ignore
4441 */
4442 - (BOOL)tracksMouseOutsideOfFrame
4443 {
4444  return YES;
4445 }
4446 
4447 /*
4448  @ignore
4449 */
4450 - (BOOL)startTrackingAt:(CGPoint)aPoint
4451 {
4452  // Try to become the first responder, but if we can't, that's okay.
4453  [[self window] makeFirstResponder:self];
4454 
4455  var row = [self rowAtPoint:aPoint];
4456 
4457  _clickedRow = row;
4458  _clickedColumn = [self columnAtPoint:aPoint];
4459 
4460  // If the user clicks outside a row then deselect everything.
4461  if (row < 0 && _allowsEmptySelection)
4462  {
4463  if ([self _sendDelegateSelectionShouldChangeInTableView])
4464  {
4465  var indexSet = [self _sendDelegateSelectionIndexesForProposedSelection:[CPIndexSet indexSet]];
4466 
4467  [self _noteSelectionIsChanging];
4468  [self selectRowIndexes:indexSet byExtendingSelection:NO];
4469  }
4470  }
4471 
4472  if ([self mouseDownFlags] & CPShiftKeyMask)
4473  _selectionAnchorRow = (ABS([_selectedRowIndexes firstIndex] - row) < ABS([_selectedRowIndexes lastIndex] - row)) ?
4474  [_selectedRowIndexes firstIndex] : [_selectedRowIndexes lastIndex];
4475  else
4476  _selectionAnchorRow = row;
4477 
4478  //set ivars for startTrackingPoint and time...
4479  _startTrackingPoint = aPoint;
4480  _startTrackingTimestamp = new Date();
4481 
4482  if ([self _dataSourceRespondsToSetObjectValueForTableColumnRow])
4483  _trackingPointMovedOutOfClickSlop = NO;
4484 
4485  // if the table has drag support then we use mouseUp to select a single row.
4486  // otherwise it uses mouse down.
4487  if (row >= 0 && !([self _dataSourceRespondsToWriteRowsWithIndexesToPasteboard]))
4488  [self _updateSelectionWithMouseAtRow:row];
4489 
4490  return YES;
4491 }
4492 
4496 - (CPMenu)menuForEvent:(CPEvent)theEvent
4497 {
4498  if (!([self _delegateRespondsToMenuForTableColumnRow]))
4499  return [super menuForEvent:theEvent];
4500 
4501  var location = [self convertPoint:[theEvent locationInWindow] fromView:nil],
4502  row = [self rowAtPoint:location],
4503  column = [self columnAtPoint:location],
4504  tableColumn = [[self tableColumns] objectAtIndex:column];
4505 
4506  return [self _sendDelegateMenuForTableColumn:tableColumn row:row];
4507 }
4508 
4509 /*
4510  @ignore
4511 */
4512 - (void)trackMouse:(CPEvent)anEvent
4513 {
4514  // Prevent CPControl from eating the mouse events when we are in a drag session
4515  if (![_draggedRowIndexes count])
4516  {
4517  [self autoscroll:anEvent];
4518  [super trackMouse:anEvent];
4519  }
4520  else
4521  [CPApp sendEvent:anEvent];
4522 
4523  if ([anEvent type] == CPLeftMouseUp)
4524  {
4525  _clickedRow = CPNotFound;
4526  _clickedColumn = CPNotFound;
4527  }
4528 }
4529 
4530 /*
4531  @ignore
4532 */
4533 - (BOOL)continueTracking:(CGPoint)lastPoint at:(CGPoint)aPoint
4534 {
4535  var row = [self rowAtPoint:aPoint];
4536 
4537  _clickedRow = row;
4538  _clickedColumn = [self columnAtPoint:aPoint];
4539 
4540  // begin the drag is the datasource lets us, we've move at least +-3px vertical or horizontal,
4541  // or we're dragging from selected rows and we haven't begun a drag session
4542  if (!_isSelectingSession && [self _dataSourceRespondsToWriteRowsWithIndexesToPasteboard])
4543  {
4544  if (row >= 0 && (ABS(_startTrackingPoint.x - aPoint.x) > 3 || (_verticalMotionCanDrag && ABS(_startTrackingPoint.y - aPoint.y) > 3)) ||
4545  ([_selectedRowIndexes containsIndex:row]))
4546  {
4547  if ([_selectedRowIndexes containsIndex:row])
4548  _draggedRowIndexes = [[CPIndexSet alloc] initWithIndexSet:_selectedRowIndexes];
4549  else
4550  _draggedRowIndexes = [CPIndexSet indexSetWithIndex:row];
4551 
4552  //ask the datasource for the data
4553  var pboard = [CPPasteboard pasteboardWithName:CPDragPboard];
4554 
4555  if ([self canDragRowsWithIndexes:_draggedRowIndexes atPoint:aPoint] && [self _sendDataSourceWriteRowsWithIndexes:_draggedRowIndexes toPasteboard:pboard])
4556  {
4557  var currentEvent = [CPApp currentEvent],
4558  offset = CGPointMakeZero(),
4559  tableColumns = [_tableColumns objectsAtIndexes:_exposedColumns];
4560 
4561  // We deviate from the default Cocoa implementation here by asking for a view in stead of an image
4562  // We support both, but the view preferred over the image because we can mimic the rows we are dragging
4563  // by re-creating the data views for the dragged rows
4564  var view = [self dragViewForRowsWithIndexes:_draggedRowIndexes
4565  tableColumns:tableColumns
4566  event:currentEvent
4567  offset:offset];
4568 
4569  if (!view)
4570  {
4571  var image = [self dragImageForRowsWithIndexes:_draggedRowIndexes
4572  tableColumns:tableColumns
4573  event:currentEvent
4574  offset:offset];
4575  view = [[CPImageView alloc] initWithFrame:CGRectMake(0, 0, [image size].width, [image size].height)];
4576  [view setImage:image];
4577  }
4578 
4579  var bounds = [view bounds],
4580  viewLocation = CGPointMake(aPoint.x - CGRectGetWidth(bounds) / 2 + offset.x, aPoint.y - CGRectGetHeight(bounds) / 2 + offset.y);
4581  [self dragView:view at:viewLocation offset:CGPointMakeZero() event:[CPApp currentEvent] pasteboard:pboard source:self slideBack:YES];
4582  _startTrackingPoint = nil;
4583 
4584  return NO;
4585  }
4586 
4587  // The delegate disallowed the drag so clear the dragged row indexes
4588  _draggedRowIndexes = [CPIndexSet indexSet];
4589  }
4590  else if (ABS(_startTrackingPoint.x - aPoint.x) < 5 && ABS(_startTrackingPoint.y - aPoint.y) < 5)
4591  return YES;
4592  }
4593 
4594  _isSelectingSession = YES;
4595  if (row >= 0 && row !== _lastTrackedRowIndex)
4596  {
4597  _lastTrackedRowIndex = row;
4598  [self _updateSelectionWithMouseAtRow:row];
4599  }
4600 
4601  if ([self _dataSourceRespondsToSetObjectValueForTableColumnRow]
4602  && !_trackingPointMovedOutOfClickSlop)
4603  {
4604  var CLICK_SPACE_DELTA = 5.0; // Stolen from AppKit/Platform/DOM/CPPlatformWindow+DOM.j
4605  if (ABS(aPoint.x - _startTrackingPoint.x) > CLICK_SPACE_DELTA
4606  || ABS(aPoint.y - _startTrackingPoint.y) > CLICK_SPACE_DELTA)
4607  {
4608  _trackingPointMovedOutOfClickSlop = YES;
4609  }
4610  }
4611 
4612  return YES;
4613 }
4614 
4618 - (void)stopTracking:(CGPoint)lastPoint at:(CGPoint)aPoint mouseIsUp:(BOOL)mouseIsUp
4619 {
4620  _isSelectingSession = NO;
4621 
4622  var CLICK_TIME_DELTA = 1000,
4623  columnIndex = -1,
4624  column,
4625  rowIndex,
4626  shouldEdit = YES;
4627 
4628  _clickedRow = [self rowAtPoint:aPoint];
4629  _clickedColumn = [self columnAtPoint:aPoint];
4630 
4631  if ([self _dataSourceRespondsToWriteRowsWithIndexesToPasteboard])
4632  {
4633  rowIndex = _clickedRow;
4634 
4635  if (rowIndex !== -1)
4636  {
4637  if ([_draggedRowIndexes count] > 0)
4638  {
4639  _draggedRowIndexes = [CPIndexSet indexSet];
4640  return;
4641  }
4642  // if the table has drag support then we use mouseUp to select a single row.
4643  _previouslySelectedRowIndexes = [_selectedRowIndexes copy];
4644  [self _updateSelectionWithMouseAtRow:rowIndex];
4645  }
4646  }
4647 
4648  // Accept either tableView:setObjectValue:forTableColumn:row: delegate method, or a binding.
4649  if (!_isViewBased && mouseIsUp
4650  && !_trackingPointMovedOutOfClickSlop
4651  && ([[CPApp currentEvent] clickCount] > 1)
4652  && ([self _dataSourceRespondsToSetObjectValueForTableColumnRow]
4653  || [self infoForBinding:@"content"]))
4654  {
4655  columnIndex = _clickedColumn;
4656 
4657  if (columnIndex !== -1)
4658  {
4659  column = _tableColumns[columnIndex];
4660 
4661  if ([column isEditable])
4662  {
4663  rowIndex = [self rowAtPoint:aPoint];
4664 
4665  if (rowIndex !== -1)
4666  {
4667  if ([self _sendDelegateShouldEditTableColumn:column row:rowIndex])
4668  {
4669  [self editColumn:columnIndex row:rowIndex withEvent:[CPApp currentEvent] select:YES];
4670  return;
4671  }
4672  }
4673  }
4674  }
4675 
4676  } //end of editing conditional
4677 
4678  //double click actions
4679  if ([[CPApp currentEvent] clickCount] === 2 && _doubleAction)
4680  [self sendAction:_doubleAction to:_target];
4681 }
4682 
4683 /*
4684  @ignore
4685 */
4686 - (CPDragOperation)draggingEntered:(id)sender
4687 {
4688  var location = [self convertPoint:[sender draggingLocation] fromView:nil],
4689  dropOperation = [self _proposedDropOperationAtPoint:location],
4690  row = [self _proposedRowAtPoint:location];
4691 
4692  if (_retargetedDropRow !== nil)
4693  row = _retargetedDropRow;
4694 
4695  var draggedTypes = [self registeredDraggedTypes],
4696  count = [draggedTypes count],
4697  i = 0;
4698 
4699  for (; i < count; i++)
4700  {
4701  if ([[[sender draggingPasteboard] types] containsObject:[draggedTypes objectAtIndex: i]])
4702  return [self _sendDataSourceValidateDrop:sender proposedRow:row proposedDropOperation:dropOperation];
4703  }
4704 
4705  return CPDragOperationNone;
4706 }
4707 
4708 /*
4709  @ignore
4710 */
4711 - (void)draggingExited:(id)sender
4712 {
4713  [_dropOperationFeedbackView removeFromSuperview];
4714 }
4715 
4716 /*
4717  @ignore
4718 */
4719 - (void)draggingEnded:(id)sender
4720 {
4721  [self _draggingEnded];
4722 }
4723 
4727 - (void)_draggingEnded
4728 {
4729  _retargetedDropOperation = nil;
4730  _retargetedDropRow = nil;
4731  _draggedRowIndexes = [CPIndexSet indexSet];
4732  [_dropOperationFeedbackView removeFromSuperview];
4733 }
4734 
4735 /*
4736  @ignore
4737 */
4738 - (BOOL)wantsPeriodicDraggingUpdates
4739 {
4740  return YES;
4741 }
4742 
4743 /*
4744  @ignore
4745 */
4746 - (CPTableViewDropOperation)_proposedDropOperationAtPoint:(CGPoint)theDragPoint
4747 {
4748  if (_retargetedDropOperation !== nil)
4749  return _retargetedDropOperation;
4750 
4751  var row = [self _proposedRowAtPoint:theDragPoint],
4752  rowRect = [self rectOfRow:row];
4753 
4754  // If there is no (the default) or too little inter-cell spacing we create some room for the CPTableViewDropAbove indicator
4755  // This probably doesn't work if the row height is smaller than or around 5.0
4756  if ([self intercellSpacing].height < 5.0)
4757  rowRect = CGRectInset(rowRect, 0.0, 5.0 - [self intercellSpacing].height);
4758 
4759  // If the altered row rect contains the drag point we show the drop on
4760  // We don't show the drop on indicator if we are dragging below the last row
4761  // in that case we always want to show the drop above indicator
4762  if (CGRectContainsPoint(rowRect, theDragPoint) && row < _numberOfRows)
4763  return CPTableViewDropOn;
4764 
4765  return CPTableViewDropAbove;
4766 }
4767 
4768 /*
4769  @ignore
4770 */
4771 - (CPInteger)_proposedRowAtPoint:(CGPoint)dragPoint
4772 {
4773  var row = [self rowAtPoint:dragPoint],
4774  // Determine if the mouse is currently closer to this row or the row below it
4775  lowerRow = row + 1,
4776  rect = [self rectOfRow:row],
4777  bottomPoint = CGRectGetMaxY(rect),
4778  bottomThirty = bottomPoint - ((bottomPoint - CGRectGetMinY(rect)) * 0.3),
4779  numberOfRows = [self numberOfRows];
4780 
4781  if (row < 0)
4782  row = (CGRectGetMaxY(rect) < dragPoint.y) ? numberOfRows : row;
4783  else if (dragPoint.y > MAX(bottomThirty, bottomPoint - 6))
4784  row = lowerRow;
4785 
4786  row = MIN(numberOfRows, row);
4787 
4788  return row;
4789 }
4790 
4794 - (CGRect)_rectForDropHighlightViewOnRow:(CPInteger)theRowIndex
4795 {
4796  if (theRowIndex >= [self numberOfRows])
4797  theRowIndex = [self numberOfRows] - 1;
4798 
4799  return [self _rectOfRow:theRowIndex checkRange:NO];
4800 }
4801 
4805 - (CGRect)_rectForDropHighlightViewBetweenUpperRow:(CPInteger)theUpperRowIndex andLowerRow:(CPInteger)theLowerRowIndex offset:(CGPoint)theOffset
4806 {
4807  if (theLowerRowIndex > [self numberOfRows])
4808  theLowerRowIndex = [self numberOfRows];
4809 
4810  return [self _rectOfRow:theLowerRowIndex checkRange:NO];
4811 }
4812 
4816 - (CPDragOperation)draggingUpdated:(id)sender
4817 {
4818  _retargetedDropRow = nil;
4819  _retargetedDropOperation = nil;
4820 
4821  var location = [self convertPoint:[sender draggingLocation] fromView:nil],
4822  dropOperation = [self _proposedDropOperationAtPoint:location],
4823  numberOfRows = [self numberOfRows],
4824  row = [self _proposedRowAtPoint:location],
4825  dragOperation = [self _sendDataSourceValidateDrop:sender proposedRow:row proposedDropOperation:dropOperation];
4826 
4827  if (_retargetedDropRow !== nil)
4828  row = _retargetedDropRow;
4829 
4830  if (_retargetedDropOperation !== nil)
4831  dropOperation = _retargetedDropOperation;
4832 
4833  if (dropOperation === CPTableViewDropOn && row >= numberOfRows)
4834  row = numberOfRows - 1;
4835 
4836  var rect = CGRectMakeZero();
4837 
4838  if (row === -1)
4839  rect = [self exposedRect];
4840  else if (dropOperation === CPTableViewDropAbove)
4841  rect = [self _rectForDropHighlightViewBetweenUpperRow:row - 1 andLowerRow:row offset:location];
4842  else
4843  rect = [self _rectForDropHighlightViewOnRow:row];
4844 
4845  [_dropOperationFeedbackView setDropOperation:row !== -1 ? dropOperation : CPDragOperationNone];
4846  [_dropOperationFeedbackView setHidden:(dragOperation == CPDragOperationNone)];
4847  [_dropOperationFeedbackView setFrame:rect];
4848  [_dropOperationFeedbackView setCurrentRow:row];
4849  [self addSubview:_dropOperationFeedbackView];
4850 
4851  return dragOperation;
4852 }
4853 
4854 /*
4855  @ignore
4856 */
4857 - (BOOL)prepareForDragOperation:(id)sender
4858 {
4859  // FIX ME: is there anything else that needs to happen here?
4860  // actual validation is called in draggingUpdated:
4861  [_dropOperationFeedbackView removeFromSuperview];
4862 
4863  return [self _dataSourceRespondsToValidateDropProposedRowProposedDropOperation];
4864 }
4865 
4866 /*
4867  @ignore
4868 */
4869 - (BOOL)performDragOperation:(id)sender
4870 {
4871  var location = [self convertPoint:[sender draggingLocation] fromView:nil],
4872  operation = [self _proposedDropOperationAtPoint:location],
4873  row = _retargetedDropRow;
4874 
4875  if (row === nil)
4876  var row = [self _proposedRowAtPoint:location];
4877 
4878  return [self _sendDataSourceAcceptDrop:sender row:row dropOperation:operation];
4879 }
4880 
4881 /*
4882  @ignore
4883 */
4884 - (void)concludeDragOperation:(id)sender
4885 {
4886  [self _reloadDataViews];
4887 }
4888 
4889 /*
4890  @ignore
4891  We're using this because we drag views instead of images so we can get the rows themselves to actually drag.
4892 */
4893 - (void)draggedView:(CPImage)aView endedAt:(CGPoint)aLocation operation:(CPDragOperation)anOperation
4894 {
4895  [self _draggingEnded];
4896  [self draggedImage:aView endedAt:aLocation operation:anOperation];
4897 }
4898 
4902 - (void)_updateSelectionWithMouseAtRow:(CPInteger)aRow
4903 {
4904  //check to make sure the row exists
4905  if (aRow < 0)
4906  return;
4907 
4908  var newSelection,
4909  shouldExtendSelection = NO;
4910 
4911  // If cmd/ctrl was held down XOR the old selection with the proposed selection
4912  if ([self mouseDownFlags] & (CPCommandKeyMask | CPControlKeyMask | CPAlternateKeyMask))
4913  {
4914  if ([_selectedRowIndexes containsIndex:aRow])
4915  {
4916  newSelection = [_selectedRowIndexes copy];
4917 
4918  [newSelection removeIndex:aRow];
4919  }
4920  else if (_allowsMultipleSelection)
4921  {
4922  newSelection = [_selectedRowIndexes copy];
4923 
4924  [newSelection addIndex:aRow];
4925  }
4926  else
4927  newSelection = [CPIndexSet indexSetWithIndex:aRow];
4928  }
4929  else if (_allowsMultipleSelection)
4930  {
4931  if (_selectionAnchorRow == CPNotFound)
4932  _selectionAnchorRow = [self numberOfRows] - 1;
4933 
4934  newSelection = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(MIN(aRow, _selectionAnchorRow), ABS(aRow - _selectionAnchorRow) + 1)];
4935  shouldExtendSelection = [self mouseDownFlags] & CPShiftKeyMask &&
4936  ((_lastSelectedRow == [_selectedRowIndexes lastIndex] && aRow > _lastSelectedRow) ||
4937  (_lastSelectedRow == [_selectedRowIndexes firstIndex] && aRow < _lastSelectedRow));
4938  }
4939  else if (aRow >= 0 && aRow < _numberOfRows)
4940  newSelection = [CPIndexSet indexSetWithIndex:aRow];
4941  else
4942  newSelection = [CPIndexSet indexSet];
4943 
4944  if ([newSelection isEqualToIndexSet:_selectedRowIndexes])
4945  return;
4946 
4947  if (![self _sendDelegateSelectionShouldChangeInTableView])
4948  return;
4949 
4950  newSelection = [self _sendDelegateSelectionIndexesForProposedSelection:newSelection];
4951 
4952  if (![self _delegateRespondsToSelectionIndexesForProposedSelection] && [self _delegateRespondsToShouldSelectRow])
4953  {
4954  var indexArray = [];
4955 
4956  [newSelection getIndexes:indexArray maxCount:-1 inIndexRange:nil];
4957 
4958  var indexCount = indexArray.length;
4959 
4960  while (indexCount--)
4961  {
4962  var index = indexArray[indexCount];
4963 
4964  if (![self _sendDelegateShouldSelectRow:index])
4965  [newSelection removeIndex:index];
4966  }
4967 
4968  // as per cocoa
4969  if ([newSelection count] === 0)
4970  return;
4971  }
4972 
4973  // if empty selection is not allowed and the new selection has nothing selected, abort
4974  if (!_allowsEmptySelection && [newSelection count] === 0)
4975  return;
4976 
4977  if ([newSelection isEqualToIndexSet:_selectedRowIndexes])
4978  return;
4979 
4980  [self _noteSelectionIsChanging];
4981  [self selectRowIndexes:newSelection byExtendingSelection:shouldExtendSelection];
4982 
4983  _lastSelectedRow = [newSelection containsIndex:aRow] ? aRow : [newSelection lastIndex];
4984 }
4985 
4989 - (void)_noteSelectionIsChanging
4990 {
4992  postNotificationName:CPTableViewSelectionIsChangingNotification
4993  object:self
4994  userInfo:nil];
4995 
4996  if (_implementedDelegateMethods & CPTableViewDelegate_tableViewSelectionIsChanging_)
4997  [_delegate tableViewSelectionIsChanging:[[CPNotification alloc] initWithName:CPTableViewSelectionIsChangingNotification object:self userInfo:nil]];
4998 }
4999 
5003 - (void)_noteSelectionDidChange
5004 {
5006  postNotificationName:CPTableViewSelectionDidChangeNotification
5007  object:self
5008  userInfo:nil];
5009 
5010  if (_implementedDelegateMethods & CPTableViewDelegate_tableViewSelectionDidChange_)
5011  [_delegate tableViewSelectionDidChange:[[CPNotification alloc] initWithName:CPTableViewSelectionDidChangeNotification object:self userInfo:nil]];
5012 }
5013 
5017 - (void)becomeKeyWindow
5018 {
5019  [self setNeedsDisplay:YES];
5020 }
5021 
5025 - (void)resignKeyWindow
5026 {
5027  [self setNeedsDisplay:YES];
5028 }
5029 
5033 - (BOOL)acceptsFirstResponder
5034 {
5035  return YES;
5036 }
5037 
5041 - (BOOL)needsPanelToBecomeKey
5042 {
5043  return YES;
5044 }
5045 
5049 - (CPView)hitTest:(CGPoint)aPoint
5050 {
5051  var hit = [super hitTest:aPoint];
5052 
5053  if ([[CPApp currentEvent] type] !== CPLeftMouseDown)
5054  return hit;
5055 
5056  return [self _hitTest:hit];
5057 }
5058 
5059 - (id)_hitTest:(CPView)aView
5060 {
5061  var column,
5062  row;
5063 
5064  if ([aView acceptsFirstResponder])
5065  {
5066  [self getColumn:@ref(column) row:@ref(row) forView:aView];
5067 
5068  if (![self isRowSelected:row])
5069  {
5070  if (_selectionHighlightStyle == CPTableViewSelectionHighlightStyleNone)
5071  return aView;
5072 
5073  return self;
5074  }
5075  }
5076  else if (!_isViewBased && [aView isKindOfClass:[CPControl class]] && ![aView isKindOfClass:[CPTextField class]])
5077  {
5078  [self getColumn:@ref(column) row:@ref(row) forView:aView];
5079 
5080  _editingColumn = column;
5081  _editingRow = row;
5082 
5083  [aView addObserver:self forKeyPath:@"objectValue" options:CPKeyValueObservingOptionOld | CPKeyValueObservingOptionNew context:"editing"];
5084  }
5085 
5086  return aView;
5087 }
5088 
5089 - (void)_removeObservers
5090 {
5091  if (!_isObserving)
5092  return;
5093 
5094  [self _stopObservingClipView];
5095  [super _removeObservers];
5096 }
5097 
5098 - (void)_addObservers
5099 {
5100  if (_isObserving)
5101  return;
5102 
5103  [self _startObservingClipView];
5104  [super _addObservers];
5105 }
5106 
5111 - (void)viewWillMoveToWindow:(CPWindow)aWindow
5112 {
5113  [super viewWillMoveToWindow:aWindow];
5114 
5115  [self _stopObservingFirstResponderForWindow:aWindow];
5116 
5117  if (aWindow)
5118  [self _startObservingFirstResponderForWindow:aWindow];
5119 }
5120 
5121 - (void)_startObservingClipView
5122 {
5123  if (!_observedClipView)
5124  return;
5125 
5126  var defaultCenter = [CPNotificationCenter defaultCenter];
5127 
5128  [_observedClipView setPostsFrameChangedNotifications:YES];
5129  [_observedClipView setPostsBoundsChangedNotifications:YES];
5130 
5131  [defaultCenter addObserver:self
5132  selector:@selector(superviewFrameChanged:)
5133  name:CPViewFrameDidChangeNotification
5134  object:_observedClipView];
5135 
5136  [defaultCenter addObserver:self
5137  selector:@selector(superviewBoundsChanged:)
5138  name:CPViewBoundsDidChangeNotification
5139  object:_observedClipView];
5140 }
5141 
5142 - (void)_stopObservingClipView
5143 {
5144  if (!_observedClipView)
5145  return;
5146 
5147  var defaultCenter = [CPNotificationCenter defaultCenter];
5148 
5149  [defaultCenter removeObserver:self
5150  name:CPViewFrameDidChangeNotification
5151  object:_observedClipView];
5152 
5153  [defaultCenter removeObserver:self
5154  name:CPViewBoundsDidChangeNotification
5155  object:_observedClipView];
5156 }
5157 
5158 - (void)_startObservingFirstResponderForWindow:(CPWindow)aWindow
5159 {
5160  [[CPNotificationCenter defaultCenter] addObserver:self selector:@selector(_firstResponderDidChange:) name:_CPWindowDidChangeFirstResponderNotification object:aWindow];
5161 }
5162 
5163 - (void)_stopObservingFirstResponderForWindow:(CPWindow)aWindow
5164 {
5165  [[CPNotificationCenter defaultCenter] removeObserver:self name:_CPWindowDidChangeFirstResponderNotification object:aWindow];
5166 }
5167 
5168 - (void)_firstResponderDidChange:(CPNotification)aNotification
5169 {
5170  var responder = [[self window] firstResponder],
5171  column,
5172  row;
5173 
5174  [self getColumn:@ref(column) row:@ref(row) forView:responder];
5175 
5176  _editingRow = row;
5177  _editingColumn = column;
5178 
5179  // We want to keep the 'First Responder' theme state for the table view as a whole, even when a subview is being edited.
5180  // This makes sure the theming effects of a focused table remain in effect even as cells are being edited in it.
5181  // Check if the firstResponder is outside the tableview:
5182  if (responder !== self && _editingRow == CPNotFound && _editingColumn == CPNotFound)
5183  [self _notifyViewDidResignFirstResponder];
5184  else
5185  [self _notifyViewDidBecomeFirstResponder];
5186 
5187  // This is for cell-based tables only. In view-based mode, we do not change the textfield apprearence during an edit.
5188  if (!_isViewBased && _editingRow !== CPNotFound && [responder isKindOfClass:[CPTextField class]] && [responder isEditable] && [responder superview] == self)
5189  {
5190  [responder setBezeled:YES];
5191  [self _registerForEndEditingNote:responder];
5192  }
5193 }
5194 
5195 - (void)_registerForEndEditingNote:(CPView)aTextField
5196 {
5197  [[CPNotificationCenter defaultCenter] addObserver:self selector:@selector(_textFieldEditingDidEnd:) name:CPControlTextDidEndEditingNotification object:aTextField];
5198 }
5199 
5200 - (void)_unregisterForEndEditingNote:(CPView)aTextField
5201 {
5202  [[CPNotificationCenter defaultCenter] removeObserver:self name:CPControlTextDidEndEditingNotification object:aTextField];
5203 }
5204 
5205 - (void)_textFieldEditingDidEnd:(CPNotification)aNote
5206 {
5207  // FIXME: When you edit a text field and hit enter without any text modification, the CPControlTextDidEndEditingNotification
5208  // is NOT sent. This is a bug in CPTextField or CPControl according to cocoa.
5209  var textField = [aNote object];
5210 
5211  [self _unregisterForEndEditingNote:textField];
5212 
5213  if (!_isViewBased)
5214  {
5215  [self _setEditingState:NO forView:textField];
5216  [self _commitDataViewObjectValue:textField];
5217  }
5218  else
5219  [textField setBezeled:NO];
5220 
5221  [self _resignFirstResponderWithoutSendingAction:textField];
5222 }
5223 
5224 - (void)_resignFirstResponderWithoutSendingAction:(CPView)aView
5225 {
5226  var action = [self _disableActionIfExists:aView];
5227 
5228  [[self window] makeFirstResponder:self];
5229 
5230  if (action)
5231  [aView setAction:action];
5232 }
5233 
5234 - (void)_resignEditedView
5235 {
5236  var view = [[self window] firstResponder];
5237 
5238  if ([view respondsToSelector:@selector(selectText:)])
5239  [view selectText:nil];
5240 
5241  if (!_isViewBased)
5242  {
5243  [self _unregisterForEndEditingNote:view];
5244  [self _setEditingState:NO forView:view];
5245  }
5246 
5247  [self _resignFirstResponderWithoutSendingAction:view];
5248 }
5249 
5250 - (SEL)_disableActionIfExists:(CPView)aView
5251 {
5252  // TODO: We disable action to prevent it from beeing sent twice when we resign the FR inside a textEndEditing notification.
5253  // Check if this is due to a bug in CPTextField.
5254  var action = nil;
5255  if ([aView respondsToSelector:@selector(action)] && (action = [aView action]))
5256  [aView setAction:nil];
5257 
5258  return action;
5259 }
5260 
5266 - (void)_commitDataViewObjectValue:(id)aDataView
5267 {
5268  var editingTableColumn = _tableColumns[_editingColumn];
5269 
5270  if (_implementedDataSourceMethods & CPTableViewDataSource_tableView_setObjectValue_forTableColumn_row_)
5271  [_dataSource tableView:self setObjectValue:[aDataView objectValue] forTableColumn:editingTableColumn row:_editingRow];
5272 
5273  // Allow the column binding to do a reverse set. Note that we do this even if the data source method above
5274  // is implemented.
5275  [editingTableColumn _reverseSetDataView:aDataView forRow:_editingRow];
5276 
5277  if (_editingRow !== CPNotFound && _editingColumn !== CPNotFound)
5279 }
5280 
5281 - (void)_setEditingState:(BOOL)editingState forView:(CPView)aView
5282 {
5283  if ([aView respondsToSelector:@selector(setEditable:)])
5284  [aView setEditable:editingState];
5285 
5286  if ([aView respondsToSelector:@selector(setSelectable:)])
5287  [aView setSelectable:editingState];
5288 
5289  if ([aView isKindOfClass:[CPTextField class]])
5290  [aView setBezeled:editingState];
5291 }
5292 
5302 - (void)editColumn:(CPInteger)columnIndex row:(CPInteger)rowIndex withEvent:(CPEvent)theEvent select:(BOOL)flag
5303 {
5304  if (![self isRowSelected:rowIndex])
5305  [[CPException exceptionWithName:@"Error" reason:@"Attempt to edit row " + rowIndex + " when not selected." userInfo:nil] raise];
5306 
5307  [self _reloadDataViewsSynchronously];
5308  [self scrollRowToVisible:rowIndex];
5309  [self scrollColumnToVisible:columnIndex];
5310 
5311  // TODO Do something with flag.
5312 
5313  _editingRow = rowIndex;
5314  _editingColumn = columnIndex;
5315 
5316  var editingTableColumnUID = [_tableColumns[_editingColumn] UID],
5317  editingView = _dataViewsForRows[_editingRow][editingTableColumnUID];
5318 
5319  [self _setEditingState:YES forView:editingView];
5320  [[self window] makeFirstResponder:editingView];
5321 }
5322 
5323 - (void)observeValueForKeyPath:(CPString)keyPath ofObject:(id)object change:(CPDictionary)change context:(void)context
5324 {
5325  if (context === "editing" && [object superview] === self)
5326  {
5327  [object removeObserver:self forKeyPath:keyPath];
5328  [self _commitDataViewObjectValue:object];
5329  _editingRow = CPNotFound;
5330  _editingColumn = CPNotFound;
5331  }
5332 }
5333 
5337 - (CPInteger)editedColumn
5338 {
5339  return _editingColumn;
5340 }
5341 
5345 - (CPInteger)editedRow
5346 {
5347  return _editingRow;
5348 }
5349 
5353 - (void)keyDown:(CPEvent)anEvent
5354 {
5355  var character = [anEvent charactersIgnoringModifiers],
5356  modifierFlags = [anEvent modifierFlags];
5357 
5358  // Check for the key events manually, as opposed to waiting for CPWindow to sent the actual action message
5359  // in _processKeyboardUIKey:, because we might not want to handle the arrow events.
5360  if (character === CPUpArrowFunctionKey || character === CPDownArrowFunctionKey)
5361  {
5362  // We're not interested in the arrow keys if there are no rows.
5363  // Technically we should also not be interested if we can't scroll,
5364  // but Cocoa doesn't handle that situation either.
5365  if ([self numberOfRows] !== 0)
5366  {
5367  [self _moveSelectionWithEvent:anEvent upward:(character === CPUpArrowFunctionKey)];
5368 
5369  return;
5370  }
5371  }
5372  else if (character === CPDeleteCharacter || character === CPDeleteFunctionKey)
5373  {
5374  // Don't call super if the delegate is interested in the delete key
5375  if ([self _sendDelegateDeleteKeyPressed])
5376  return;
5377  }
5378 
5379  [super keyDown:anEvent];
5380 }
5381 
5387 - (BOOL)_selectionIsBroken
5388 {
5389  return [self selectedRowIndexes]._ranges.length !== 1;
5390 }
5391 
5397 - (void)_moveSelectionWithEvent:(CPEvent)theEvent upward:(BOOL)shouldGoUpward
5398 {
5399  if (![self _sendDelegateSelectionShouldChangeInTableView])
5400  return;
5401 
5402  var selectedIndexes = [self selectedRowIndexes];
5403 
5404  if ([selectedIndexes count] > 0)
5405  {
5406  var extend = (([theEvent modifierFlags] & CPShiftKeyMask) && _allowsMultipleSelection),
5407  i = [self selectedRow];
5408 
5409  if ([self _selectionIsBroken])
5410  {
5411  while ([selectedIndexes containsIndex:i])
5412  {
5413  shouldGoUpward ? i-- : i++;
5414  }
5415  _wasSelectionBroken = true;
5416  }
5417  else if (_wasSelectionBroken && ((shouldGoUpward && i !== [selectedIndexes firstIndex]) || (!shouldGoUpward && i !== [selectedIndexes lastIndex])))
5418  {
5419  shouldGoUpward ? i = [selectedIndexes firstIndex] - 1 : i = [selectedIndexes lastIndex];
5420  _wasSelectionBroken = false;
5421  }
5422  else
5423  {
5424  shouldGoUpward ? i-- : i++;
5425  }
5426  }
5427  else
5428  {
5429  var extend = NO;
5430  //no rows are currently selected
5431  if ([self numberOfRows] > 0)
5432  var i = shouldGoUpward ? [self numberOfRows] - 1 : 0; // if we select upward select the last row, otherwise select the first row
5433  }
5434 
5435  if (i >= [self numberOfRows] || i < 0)
5436  return;
5437 
5438  if ([self _delegateRespondsToSelectionIndexesForProposedSelection] || [self _delegateRespondsToShouldSelectRow])
5439  {
5440  var shouldSelect = !![[self _cleanUpSelectionRowIndexes:[CPIndexSet indexSetWithIndex:i]] count];
5441 
5442  /* If shouldSelect returns NO it means this row cannot be selected.
5443  The proper behaviour is to then try to see if the next/previous
5444  row(s) can be selected, until we hit the first one that can be.
5445  */
5446  while (!shouldSelect && (i < [self numberOfRows] && i > 0))
5447  {
5448  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.
5449  shouldSelect = !![[self _cleanUpSelectionRowIndexes:[CPIndexSet indexSetWithIndex:i]] count];
5450  }
5451 
5452  if (!shouldSelect)
5453  return;
5454  }
5455 
5456  // If we go upward and see that this row is already selected we should deselect the row below.
5457  if (extend && [selectedIndexes containsIndex:i])
5458  {
5459  // The row we're on is the last to be selected.
5460  var differedLastSelectedRow = i;
5461 
5462  // no remove the one before/after it
5463  shouldGoUpward ? i++ : i--;
5464 
5465  [selectedIndexes removeIndex:i];
5466 
5467  //we're going to replace the selection
5468  extend = NO;
5469  }
5470  else if (extend)
5471  {
5472  if ([selectedIndexes containsIndex:i])
5473  {
5474  i = shouldGoUpward ? [selectedIndexes firstIndex] -1 : [selectedIndexes lastIndex] + 1;
5475  i = MIN(MAX(i, 0), [self numberOfRows] - 1);
5476  }
5477 
5478  [selectedIndexes addIndex:i];
5479  var differedLastSelectedRow = i;
5480  }
5481  else
5482  {
5483  selectedIndexes = [CPIndexSet indexSetWithIndex:i];
5484  var differedLastSelectedRow = i;
5485  }
5486 
5487  selectedIndexes = [self _sendDelegateSelectionIndexesForProposedSelection:selectedIndexes];
5488 
5489  [self selectRowIndexes:selectedIndexes byExtendingSelection:extend];
5490 
5491  // we differ because selectRowIndexes: does its own thing which would set the wrong index
5492  _lastSelectedRow = differedLastSelectedRow;
5493 
5494  if (i !== CPNotFound)
5495  [self scrollRowToVisible:i];
5496 }
5497 
5498 @end
5499 
5500 
5502 
5507 - (BOOL)_dataSourceRespondsToObjectValueForTableColumn
5508 {
5509  return _implementedDataSourceMethods & CPTableViewDataSource_tableView_objectValueForTableColumn_row_;
5510 }
5511 
5516 - (BOOL)_dataSourceRespondsToWriteRowsWithIndexesToPasteboard
5517 {
5518  return _implementedDataSourceMethods & CPTableViewDataSource_tableView_writeRowsWithIndexes_toPasteboard_;
5519 }
5520 
5525 - (BOOL)_dataSourceRespondsToSetObjectValueForTableColumnRow
5526 {
5528 }
5529 
5534 - (BOOL)_dataSourceRespondsToValidateDropProposedRowProposedDropOperation
5535 {
5537 }
5538 
5543 - (BOOL)_dataSourceRespondsToNumberOfRowsinTableView
5544 {
5545  return _implementedDataSourceMethods & CPTableViewDataSource_numberOfRowsInTableView_;
5546 }
5547 
5553 - (int)_sendDataSourceNumberOfRowsInTableView
5554 {
5555  if (!(_implementedDataSourceMethods & CPTableViewDataSource_numberOfRowsInTableView_))
5556  return 0;
5557 
5558  return [_dataSource numberOfRowsInTableView:self];
5559 }
5560 
5566 - (id)_sendDataSourceObjectValueForTableColumn:(CPTableColumn)aTableColumn row:(CPInteger)aRowIndex
5567 {
5568  if (!(_implementedDataSourceMethods & CPTableViewDataSource_tableView_objectValueForTableColumn_row_))
5569  return nil;
5570 
5571  return [_dataSource tableView:self objectValueForTableColumn:aTableColumn row:aRowIndex];
5572 }
5573 
5578 - (void)_sendDataSourceSetObjectValue:(id)anObject forTableColumn:(CPTableColumn)aTableColumn row:(CPInteger)aRowIndex
5579 {
5580  if (!(_implementedDataSourceMethods & CPTableViewDataSource_tableView_setObjectValue_forTableColumn_row_))
5581  return;
5582 
5583  [_dataSource tableView:self setObjectValue:anObject forTableColumn:aTableColumn row:aRowIndex];
5584 }
5585 
5590 - (void)_sendDataSourceSortDescriptorsDidChange:(CPArray)descriptors
5591 {
5592  if (!(_implementedDataSourceMethods & CPTableViewDataSource_tableView_sortDescriptorsDidChange_))
5593  return;
5594 
5595  [_dataSource tableView:self sortDescriptorsDidChange:descriptors];
5596 }
5597 
5602 - (BOOL)_sendDataSourceAcceptDrop:(id)info row:(CPInteger)aRowIndex dropOperation:(CPTableViewDropOperation)operation
5603 {
5604  if (!(_implementedDataSourceMethods & CPTableViewDataSource_tableView_acceptDrop_row_dropOperation_))
5605  return NO;
5606 
5607  return [_dataSource tableView:self acceptDrop:info row:aRowIndex dropOperation:operation];
5608 }
5609 
5614 - (CPDragOperation)_sendDataSourceValidateDrop:(id)info proposedRow:(CPInteger)aRowIndex proposedDropOperation:(CPTableViewDropOperation)operation
5615 {
5617  return CPDragOperationNone;
5618 
5619  return [_dataSource tableView:self validateDrop:info proposedRow:aRowIndex proposedDropOperation:operation];
5620 }
5621 
5626 - (BOOL)_sendDataSourceWriteRowsWithIndexes:(CPIndexSet)rowIndexes toPasteboard:(CPPasteboard)pboard
5627 {
5628  if (!(_implementedDataSourceMethods & CPTableViewDataSource_tableView_writeRowsWithIndexes_toPasteboard_))
5629  return NO;
5630 
5631  return [_dataSource tableView:self writeRowsWithIndexes:rowIndexes toPasteboard:pboard];
5632 }
5633 
5634 /*
5635  This method is sent to the data source for convenience...
5636 */
5637 - (void)draggedImage:(CPImage)anImage endedAt:(CGPoint)aLocation operation:(CPDragOperation)anOperation
5638 {
5639  if ([_dataSource respondsToSelector:@selector(tableView:didEndDraggedImage:atPosition:operation:)])
5640  [_dataSource tableView:self didEndDraggedImage:anImage atPosition:aLocation operation:anOperation];
5641 }
5642 
5643 
5644 #pragma mark -
5645 #pragma mark DataSource methods to implement
5646 
5651 - (CPArray)_sendDataSourceNamesOfPromisedFilesDroppedAtDestination:(CPURL)dropDestination forDraggedRowsWithIndexes:(CPIndexSet)indexSet
5652 {
5654  return [];
5655 
5656  return [_dataSource tableView:self namesOfPromisedFilesDroppedAtDestination:dropDestination forDraggedRowsWithIndexes:indexSet];
5657 }
5658 
5659 @end
5660 
5661 
5663 
5668 - (BOOL)_delegateRespondsToDataViewForTableColumn
5669 {
5670  return _implementedDelegateMethods & CPTableViewDelegate_tableView_dataViewForTableColumn_row_;
5671 }
5672 
5677 - (BOOL)_delegateRespondsToViewForTableColumn
5678 {
5679  return _implementedDelegateMethods & CPTableViewDelegate_tableView_viewForTableColumn_row_;
5680 }
5681 
5686 - (BOOL)_delegateRespondsToShouldSelectRow
5687 {
5688  return _implementedDelegateMethods & CPTableViewDelegate_tableView_shouldSelectRow_;
5689 }
5690 
5695 - (BOOL)_delegateRespondsToSelectionShouldChangeInTableView
5696 {
5697  return _implementedDelegateMethods & CPTableViewDelegate_selectionShouldChangeInTableView_;
5698 }
5699 
5704 - (BOOL)_delegateRespondsToSelectionIndexesForProposedSelection
5705 {
5706  return _implementedDelegateMethods & CPTableViewDelegate_tableView_selectionIndexesForProposedSelection_;
5707 }
5708 
5713 - (BOOL)_delegateRespondsToMenuForTableColumnRow
5714 {
5715  return _implementedDelegateMethods & CPTableViewDelegate_tableViewMenuForTableColumn_row_;
5716 }
5717 
5722 - (void)_sendDelegateDidClickTableColumn:(CPInteger)column
5723 {
5724  if (_implementedDelegateMethods & CPTableViewDelegate_tableView_didClickTableColumn_)
5725  [_delegate tableView:self didClickTableColumn:_tableColumns[column]];
5726 }
5727 
5732 - (void)_sendDelegateDidDragTableColumn:(CPInteger)column
5733 {
5734  if (_implementedDelegateMethods & CPTableViewDelegate_tableView_didDragTableColumn_)
5735  [_delegate tableView:self didDragTableColumn:_tableColumns[column]];
5736 }
5737 
5742 - (void)_sendDelegateMouseDownInHeaderOfTableColumn:(CPInteger)column
5743 {
5744  if (_implementedDelegateMethods & CPTableViewDelegate_tableView_mouseDownInHeaderOfTableColumn_)
5745  [_delegate tableView:self mouseDownInHeaderOfTableColumn:_tableColumns[column]];
5746 }
5747 
5748 /*
5749  @ignore
5750  Call the delegate tableViewDeleteKeyPressed
5751 */
5752 - (BOOL)_sendDelegateDeleteKeyPressed
5753 {
5754  if ([_delegate respondsToSelector: @selector(tableViewDeleteKeyPressed:)])
5755  {
5756  [_delegate tableViewDeleteKeyPressed:self];
5757  return YES;
5758  }
5759 
5760  return NO;
5761 }
5762 
5767 - (BOOL)_sendDelegateSelectionShouldChangeInTableView
5768 {
5769  if (!(_implementedDelegateMethods & CPTableViewDelegate_selectionShouldChangeInTableView_))
5770  return YES;
5771 
5772  return [_delegate selectionShouldChangeInTableView:self];
5773 }
5774 
5779 - (BOOL)_sendDelegateIsGroupRow:(CPInteger)anIndex
5780 {
5781  if (!(_implementedDelegateMethods & CPTableViewDelegate_tableView_isGroupRow_))
5782  return NO;
5783 
5784  return [_delegate tableView:self isGroupRow:anIndex];
5785 }
5786 
5791 - (BOOL)_sendDelegateShouldSelectRow:(CPInteger)anIndex
5792 {
5793  if (!(_implementedDelegateMethods & CPTableViewDelegate_tableView_shouldSelectRow_))
5794  return YES;
5795 
5796  return [_delegate tableView:self shouldSelectRow:anIndex];
5797 }
5798 
5803 - (void)_sendDelegateWillDisplayView:(id)aCell forTableColumn:(CPTableColumn)aTableColumn row:(CPInteger)aRowIndex
5804 {
5805  if (!(_implementedDelegateMethods & CPTableViewDelegate_tableView_willDisplayView_forTableColumn_row_))
5806  return;
5807 
5808  [_delegate tableView:self willDisplayView:aCell forTableColumn:aTableColumn row:aRowIndex];
5809 }
5810 
5815 - (void)_sendDelegateWillRemoveView:(id)aCell forTableColumn:(CPTableColumn)aTableColumn row:(CPInteger)aRowIndex
5816 {
5817  if (!(_implementedDelegateMethods & CPTableViewDelegate_tableView_willRemoveView_forTableColumn_row_))
5818  return;
5819 
5820  [_delegate tableView:self willRemoveView:aCell forTableColumn:aTableColumn row:aRowIndex];
5821 }
5822 
5827 - (CPMenu)_sendDelegateMenuForTableColumn:(CPTableColumn)aTableColumn row:aRowIndex
5828 {
5829  if (!(_implementedDelegateMethods & CPTableViewDelegate_tableViewMenuForTableColumn_row_))
5830  return nil;
5831 
5832  return [_delegate tableView:self menuForTableColumn:aTableColumn row:aRowIndex];
5833 }
5834 
5835 /*
5836  @ignore
5837  Returns YES if the column at columnIndex can be reordered.
5838  It can be possible if column reordering is allowed and if the tableview
5839  delegate also accept the reordering
5840 */
5841 - (BOOL)_sendDelegateShouldReorderColumn:(CPInteger)columnIndex toColumn:(CPInteger)newColumnIndex
5842 {
5843  if ([self allowsColumnReordering] &&
5845  {
5846  return [_delegate tableView:self shouldReorderColumn:columnIndex toColumn:newColumnIndex];
5847  }
5848 
5849  return [self allowsColumnReordering];
5850 }
5851 
5856 - (float)_sendDelegateHeightOfRow:(CPInteger)anIndex
5857 {
5858  if (!(_implementedDelegateMethods & CPTableViewDelegate_tableView_heightOfRow_))
5859  return [self rowHeight];
5860 
5861  return [_delegate tableView:self heightOfRow:anIndex];
5862 }
5863 
5868 - (BOOL)_sendDelegateShouldEditTableColumn:(CPTableColumn)aTableColumn row:(CPInteger)aRowIndex
5869 {
5870  if (!(_implementedDelegateMethods & CPTableViewDelegate_tableView_shouldEditTableColumn_row_))
5871  return YES;
5872 
5873  return [_delegate tableView:self shouldEditTableColumn:aTableColumn row:aRowIndex];
5874 }
5875 
5880 - (CPIndexSet)_sendDelegateSelectionIndexesForProposedSelection:(CPIndexSet)anIndexSet
5881 {
5882  if (!(_implementedDelegateMethods & CPTableViewDelegate_tableView_selectionIndexesForProposedSelection_))
5883  return anIndexSet;
5884 
5885  return [_delegate tableView:self selectionIndexesForProposedSelection:anIndexSet];
5886 }
5887 
5892 - (CPView)_sendDelegateViewForTableColumn:(CPTableColumn)aTableColumn row:(CPInteger)aRowIndex
5893 {
5894  if (!(_implementedDelegateMethods & CPTableViewDelegate_tableView_viewForTableColumn_row_))
5895  return nil;
5896 
5897  return [_delegate tableView:self viewForTableColumn:aTableColumn row:aRowIndex];
5898 }
5899 
5904 - (CPView)_sendDelegateDataViewForTableColumn:(CPTableColumn)aTableColumn row:(CPInteger)aRowIndex
5905 {
5906  if (!(_implementedDelegateMethods & CPTableViewDelegate_tableView_dataViewForTableColumn_row_))
5907  return nil;
5908 
5909  return [_delegate tableView:self dataViewForTableColumn:aTableColumn row:aRowIndex];
5910 }
5911 
5912 
5913 #pragma mark -
5914 #pragma mark Delegate methods to implement
5915 
5920 - (BOOL)_sendDelegateShouldSelectTableColumn:(CPTableColumn)aTableColumn
5921 {
5922  if (!(_implementedDelegateMethods & CPTableViewDelegate_tableView_shouldSelectTableColumn_))
5923  return YES;
5924 
5925  return [_delegate tableView:self shouldSelectTableColumn:aTableColumn];
5926 }
5927 
5932 - (CPString)_sendDelegateToolTipForView:(id)aView rect:(CGRect)aRect tableColumn:(CPTableColumn)aTableColumn row:(CPInteger)aRowIndex mouseLocation:(CGPoint)aPoint
5933 {
5935  return nil;
5936 
5937  return [_delegate tableView:self toolTipForView:aView rect:aRect tableColumn:aTableColumn row:aRowIndex mouseLocation:aPoint];
5938 }
5939 
5944 - (BOOL)_sendDelegateShouldTrackView:(id)aView forTableColumn:(CPTableColumn)aTableColumn row:(CPInteger)aRowIndex
5945 {
5946  if (!(_implementedDelegateMethods & CPTableViewDelegate_tableView_shouldTrackView_forTableColumn_row_))
5947  return YES;
5948 
5949  return [_delegate tableView:self shouldTrackView:aView forTableColumn:aTableColumn row:aRowIndex];
5950 }
5951 
5956 - (BOOL)_sendDelegateShouldShowViewExpansionForTableColumn:(CPTableColumn)aTableColumn row:(CPInteger)aRowIndex
5957 {
5959  return YES;
5960 
5961  return [_delegate tableView:self shouldShowViewExpansionForTableColumn:aTableColumn row:aRowIndex];
5962 }
5963 
5968 - (BOOL)_sendDelegateShouldTypeSelectForEvent:(CPEvent)anEvent withCurrentSearchString:(CPString)aString
5969 {
5971  return NO;
5972 
5973  return [_delegate tableView:self shouldTypeSelectForEvent:anEvent withCurrentSearchString:aString];
5974 }
5975 
5980 - (CPString)_sendDelegateTypeSelectStringForTableColumn:(CPTableColumn)aTableColumn row:(CPInteger)aRowIndex
5981 {
5982  if (!(_implementedDelegateMethods & CPTableViewDelegate_tableView_typeSelectStringForTableColumn_row_))
5983  return nil;
5984 
5985  return [_delegate tableView:self typeSelectStringForTableColumn:aTableColumn row:aRowIndex];
5986 }
5987 
5992 - (int)_sendDelegateNextTypeSelectMatchFromRow:(CPInteger)aRowIndex toRow:(CPInteger)aSecondRowIndex forString:(CPString)aString
5993 {
5995  return -1;
5996 
5997  return [_delegate tableView:self nextTypeSelectMatchFromRow:aRowIndex toRow:aSecondRowIndex forString:aString];
5998 }
5999 
6000 @end
6001 
6002 @implementation CPTableView (Bindings)
6003 
6004 + (Class)_binderClassForBinding:(CPString)aBinding
6005 {
6006  if (aBinding == @"content")
6007  return [CPTableContentBinder class];
6008 
6009  return [super _binderClassForBinding:aBinding];
6010 }
6011 
6015 - (CPString)_replacementKeyPathForBinding:(CPString)aBinding
6016 {
6017  if (aBinding === @"selectionIndexes")
6018  return @"selectedRowIndexes";
6019 
6020  return [super _replacementKeyPathForBinding:aBinding];
6021 }
6022 
6026 - (void)_establishBindingsIfUnbound:(id)destination
6027 {
6028  if ([[self infoForBinding:@"content"] objectForKey:CPObservedObjectKey] !== destination)
6029  {
6030  [super bind:@"content" toObject:destination withKeyPath:@"arrangedObjects" options:nil];
6031  _contentBindingExplicitlySet = NO;
6032  }
6033 
6034  // If the content binding was set manually assume the user is taking manual control of establishing bindings.
6035  if (!_contentBindingExplicitlySet)
6036  {
6037  if ([[self infoForBinding:@"selectionIndexes"] objectForKey:CPObservedObjectKey] !== destination)
6038  [self bind:@"selectionIndexes" toObject:destination withKeyPath:@"selectionIndexes" options:nil];
6039 
6040  if ([[self infoForBinding:@"sortDescriptors"] objectForKey:CPObservedObjectKey] !== destination)
6041  [self bind:@"sortDescriptors" toObject:destination withKeyPath:@"sortDescriptors" options:nil];
6042  }
6043 }
6044 
6045 - (void)bind:(CPString)aBinding toObject:(id)anObject withKeyPath:(CPString)aKeyPath options:(CPDictionary)options
6046 {
6047  if (aBinding == @"content")
6048  _contentBindingExplicitlySet = YES;
6049 
6050  [super bind:aBinding toObject:anObject withKeyPath:aKeyPath options:options];
6051 }
6052 
6053 @end
6054 
6055 
6056 @implementation CPTableContentBinder : CPBinder
6057 {
6058  id _content;
6059 }
6060 
6061 - (void)setValueFor:(CPString)aBinding
6062 {
6063  var destination = [_info objectForKey:CPObservedObjectKey],
6064  keyPath = [_info objectForKey:CPObservedKeyPathKey];
6065 
6066  _content = [destination valueForKey:keyPath];
6067  // FIXME: reload data for all rows or just exposed rows ?
6068  [_source _reloadDataViews];
6069 }
6070 
6071 @end
6072 
6073 
6074 var CPTableViewDataSourceKey = @"CPTableViewDataSourceKey",
6075  CPTableViewDelegateKey = @"CPTableViewDelegateKey",
6076  CPTableViewHeaderViewKey = @"CPTableViewHeaderViewKey",
6077  CPTableViewTableColumnsKey = @"CPTableViewTableColumnsKey",
6078  CPTableViewRowHeightKey = @"CPTableViewRowHeightKey",
6079  CPTableViewIntercellSpacingKey = @"CPTableViewIntercellSpacingKey",
6080  CPTableViewSelectionHighlightStyleKey = @"CPTableViewSelectionHighlightStyleKey",
6081  CPTableViewMultipleSelectionKey = @"CPTableViewMultipleSelectionKey",
6082  CPTableViewEmptySelectionKey = @"CPTableViewEmptySelectionKey",
6083  CPTableViewColumnReorderingKey = @"CPTableViewColumnReorderingKey",
6084  CPTableViewColumnResizingKey = @"CPTableViewColumnResizingKey",
6085  CPTableViewColumnSelectionKey = @"CPTableViewColumnSelectionKey",
6086  CPTableViewColumnAutoresizingStyleKey = @"CPTableViewColumnAutoresizingStyleKey",
6087  CPTableViewGridColorKey = @"CPTableViewGridColorKey",
6088  CPTableViewGridStyleMaskKey = @"CPTableViewGridStyleMaskKey",
6089  CPTableViewUsesAlternatingBackgroundKey = @"CPTableViewUsesAlternatingBackgroundKey",
6090  CPTableViewAlternatingRowColorsKey = @"CPTableViewAlternatingRowColorsKey",
6091  CPTableViewHeaderViewKey = @"CPTableViewHeaderViewKey",
6092  CPTableViewCornerViewKey = @"CPTableViewCornerViewKey",
6093  CPTableViewAutosaveNameKey = @"CPTableViewAutosaveNameKey",
6094  CPTableViewArchivedReusableViewsKey = @"CPTableViewArchivedReusableViewsKey";
6095 
6096 @implementation CPTableView (CPCoding)
6097 
6098 - (id)initWithCoder:(CPCoder)aCoder
6099 {
6100  self = [super initWithCoder:aCoder];
6101 
6102  if (self)
6103  {
6104  //Configuring Behavior
6105  _allowsColumnReordering = [aCoder decodeBoolForKey:CPTableViewColumnReorderingKey];
6106  _allowsColumnResizing = [aCoder decodeBoolForKey:CPTableViewColumnResizingKey];
6107  _allowsMultipleSelection = [aCoder decodeBoolForKey:CPTableViewMultipleSelectionKey];
6108  _allowsEmptySelection = [aCoder decodeBoolForKey:CPTableViewEmptySelectionKey];
6109  _allowsColumnSelection = [aCoder decodeBoolForKey:CPTableViewColumnSelectionKey];
6110 
6111  //Setting Display Attributes
6112  _selectionHighlightStyle = [aCoder decodeIntForKey:CPTableViewSelectionHighlightStyleKey];
6113  _columnAutoResizingStyle = [aCoder decodeIntForKey:CPTableViewColumnAutoresizingStyleKey];
6114 
6115  _tableColumns = [aCoder decodeObjectForKey:CPTableViewTableColumnsKey] || [];
6116  [_tableColumns makeObjectsPerformSelector:@selector(setTableView:) withObject:self];
6117 
6118  _rowHeight = [aCoder decodeFloatForKey:CPTableViewRowHeightKey] || [self valueForThemeAttribute:@"default-row-height"];
6119  _intercellSpacing = [aCoder decodeSizeForKey:CPTableViewIntercellSpacingKey];
6120 
6121  if (CGSizeEqualToSize(_intercellSpacing, CGSizeMakeZero()))
6122  _intercellSpacing = CGSizeMake(3.0, 2.0);
6123 
6124  [self setGridColor:[aCoder decodeObjectForKey:CPTableViewGridColorKey]];
6125  _gridStyleMask = [aCoder decodeIntForKey:CPTableViewGridStyleMaskKey];
6126 
6127  _usesAlternatingRowBackgroundColors = [aCoder decodeObjectForKey:CPTableViewUsesAlternatingBackgroundKey];
6128  [self setAlternatingRowBackgroundColors:[aCoder decodeObjectForKey:CPTableViewAlternatingRowColorsKey]];
6129 
6130  _headerView = [aCoder decodeObjectForKey:CPTableViewHeaderViewKey];
6131  _cornerView = [aCoder decodeObjectForKey:CPTableViewCornerViewKey];
6132 
6133  [self setDataSource:[aCoder decodeObjectForKey:CPTableViewDataSourceKey]];
6134  [self setDelegate:[aCoder decodeObjectForKey:CPTableViewDelegateKey]];
6135 
6136  [self _init];
6137 
6138  if ([aCoder containsValueForKey:CPTableViewArchivedReusableViewsKey])
6139  _archivedDataViews = [aCoder decodeObjectForKey:CPTableViewArchivedReusableViewsKey];
6140 
6141  [self _updateIsViewBased];
6142 
6143  [self viewWillMoveToSuperview:[self superview]];
6144 
6145  // Do this as late as possible to make sure the tableview is fully configured
6146  [self setAutosaveName:[aCoder decodeObjectForKey:CPTableViewAutosaveNameKey]];
6147  }
6148 
6149  return self;
6150 }
6151 
6152 - (void)encodeWithCoder:(CPCoder)aCoder
6153 {
6154  [super encodeWithCoder:aCoder];
6155 
6156  // We do this in order to avoid encoding the _tableDrawView, which
6157  // should just automatically be created programmatically as needed.
6158  if (_tableDrawView)
6159  [_tableDrawView removeFromSuperview];
6160 
6161  [aCoder encodeObject:_dataSource forKey:CPTableViewDataSourceKey];
6162  [aCoder encodeObject:_delegate forKey:CPTableViewDelegateKey];
6163 
6164  [aCoder encodeFloat:_rowHeight forKey:CPTableViewRowHeightKey];
6165  [aCoder encodeSize:_intercellSpacing forKey:CPTableViewIntercellSpacingKey];
6166 
6167  [aCoder encodeInt:_selectionHighlightStyle forKey:CPTableViewSelectionHighlightStyleKey];
6168  [aCoder encodeInt:_columnAutoResizingStyle forKey:CPTableViewColumnAutoresizingStyleKey];
6169 
6170  [aCoder encodeBool:_allowsMultipleSelection forKey:CPTableViewMultipleSelectionKey];
6171  [aCoder encodeBool:_allowsEmptySelection forKey:CPTableViewEmptySelectionKey];
6172  [aCoder encodeBool:_allowsColumnReordering forKey:CPTableViewColumnReorderingKey];
6173  [aCoder encodeBool:_allowsColumnResizing forKey:CPTableViewColumnResizingKey];
6174  [aCoder encodeBool:_allowsColumnSelection forKey:CPTableViewColumnSelectionKey];
6175 
6176  [aCoder encodeObject:_tableColumns forKey:CPTableViewTableColumnsKey];
6177 
6178  [aCoder encodeObject:[self gridColor] forKey:CPTableViewGridColorKey];
6179  [aCoder encodeInt:_gridStyleMask forKey:CPTableViewGridStyleMaskKey];
6180 
6181  [aCoder encodeBool:_usesAlternatingRowBackgroundColors forKey:CPTableViewUsesAlternatingBackgroundKey];
6182  [aCoder encodeObject:[self alternatingRowBackgroundColors] forKey:CPTableViewAlternatingRowColorsKey];
6183 
6184  [aCoder encodeObject:_cornerView forKey:CPTableViewCornerViewKey];
6185  [aCoder encodeObject:_headerView forKey:CPTableViewHeaderViewKey];
6186 
6187  [aCoder encodeObject:_autosaveName forKey:CPTableViewAutosaveNameKey];
6188 
6189  if (_archivedDataViews)
6190  [aCoder encodeObject:_archivedDataViews forKey:CPTableViewArchivedReusableViewsKey];
6191 }
6192 
6193 @end
6194 
6195 @implementation _CPDropOperationDrawingView : CPView
6196 {
6197  unsigned dropOperation;
6201 }
6202 
6203 - (void)drawRect:(CGRect)aRect
6204 {
6205  if (tableView._destinationDragStyle === CPTableViewDraggingDestinationFeedbackStyleNone || isBlinking)
6206  return;
6207 
6208  var context = [[CPGraphicsContext currentContext] graphicsPort],
6209  borderRadius,
6210  borderColor,
6211  borderWidth,
6213 
6214  if (currentRow === -1)
6215  {
6216  borderColor = [tableView valueForThemeAttribute:@"dropview-on-border-color"];
6217  borderWidth = [tableView valueForThemeAttribute:@"dropview-on-border-width"];
6218 
6219  CGContextSetStrokeColor(context, borderColor);
6220  CGContextSetLineWidth(context, borderWidth);
6221  CGContextStrokeRect(context, [self bounds]);
6222  }
6223 
6224  else if (dropOperation === CPTableViewDropOn)
6225  {
6226  //if row is selected don't fill and stroke white
6227  var selectedRows = [tableView selectedRowIndexes],
6228  newRect = CGRectMake(aRect.origin.x + 2, aRect.origin.y + 2, aRect.size.width - 4, aRect.size.height - 5);
6229 
6230  if ([selectedRows containsIndex:currentRow])
6231  {
6232  borderRadius = [tableView valueForThemeAttribute:@"dropview-on-selected-border-radius"];
6233  borderColor = [tableView valueForThemeAttribute:@"dropview-on-selected-border-color"];
6234  borderWidth = [tableView valueForThemeAttribute:@"dropview-on-selected-border-width"];
6235  backgroundColor = [tableView valueForThemeAttribute:@"dropview-on-selected-background-color"];
6236  }
6237  else
6238  {
6239  borderRadius = [tableView valueForThemeAttribute:@"dropview-on-border-radius"];
6240  borderColor = [tableView valueForThemeAttribute:@"dropview-on-border-color"];
6241  borderWidth = [tableView valueForThemeAttribute:@"dropview-on-border-width"];
6242  backgroundColor = [tableView valueForThemeAttribute:@"dropview-on-background-color"];
6243  }
6244 
6245  CGContextSetStrokeColor(context, borderColor);
6246  CGContextSetLineWidth(context, borderWidth);
6248  CGContextFillRoundedRectangleInRect(context, newRect, borderRadius, YES, YES, YES, YES);
6249  CGContextStrokeRoundedRectangleInRect(context, newRect, borderRadius, YES, YES, YES, YES);
6250 
6251  }
6252  else if (dropOperation === CPTableViewDropAbove)
6253  {
6254  //reposition the view up a tad
6255  [self setFrameOrigin:CGPointMake(_frame.origin.x, _frame.origin.y - 8)];
6256 
6257  var selectedRows = [tableView selectedRowIndexes];
6258 
6259  if ([selectedRows containsIndex:currentRow - 1] || [selectedRows containsIndex:currentRow])
6260  {
6261  borderColor = [tableView valueForThemeAttribute:@"dropview-above-selected-border-color"];
6262  borderWidth = [tableView valueForThemeAttribute:@"dropview-above-selected-border-width"];
6263  }
6264  else
6265  {
6266  borderColor = [tableView valueForThemeAttribute:@"dropview-above-border-color"];
6267  borderWidth = [tableView valueForThemeAttribute:@"dropview-above-border-width"];
6268  }
6269 
6270  CGContextSetStrokeColor(context, borderColor);
6271  CGContextSetLineWidth(context, borderWidth);
6272  CGContextStrokeEllipseInRect(context, CGRectMake(aRect.origin.x + 4, aRect.origin.y + 4, 8, 8)); // circle
6273 
6274  CGContextBeginPath(context);
6275  CGContextMoveToPoint(context, 10, aRect.origin.y + 8);
6276  CGContextAddLineToPoint(context, aRect.size.width - aRect.origin.y - 8, aRect.origin.y + 8);
6277  CGContextStrokePath(context);
6278  }
6279 }
6280 
6281 - (void)blink
6282 {
6283  if (dropOperation !== CPTableViewDropOn)
6284  return;
6285 
6286  isBlinking = YES;
6287 
6288  var showCallback = function()
6289  {
6290  [self setHidden: NO];
6291  isBlinking = NO;
6292  };
6293 
6294  var hideCallback = function()
6295  {
6296  [self setHidden: YES];
6297  isBlinking = YES;
6298  };
6299 
6300  [self setHidden: YES];
6302  [CPTimer scheduledTimerWithTimeInterval:0.19 callback:hideCallback repeats:NO];
6303  [CPTimer scheduledTimerWithTimeInterval:0.27 callback:showCallback repeats:NO];
6304 }
6305 
6306 @end
6307 
6308 
6309 @implementation _CPColumnDragDrawingView : CPView
6310 {
6314 }
6315 
6316 - (void)drawRect:(CGRect)dirtyRect
6317 {
6318  var context = [[CPGraphicsContext currentContext] graphicsPort],
6319  columnRect = [tableView rectOfColumn:columnIndex],
6320  headerHeight = CGRectGetHeight([[tableView headerView] frame]),
6321  bounds = [columnClipView bounds],
6322  visibleRect = [tableView visibleRect],
6323  xScroll = CGRectGetMinX(visibleRect),
6324  yScroll = CGRectGetMinY(visibleRect);
6325 
6326  // Because we are sharing drawing code with regular table drawing,
6327  // we have to play a few tricks to fool the drawing code into thinking
6328  // our drag column is in the same place as the real column.
6329 
6330  // Shift the bounds origin to align with the column rect, and extend it vertically to ensure
6331  // it reaches the bottom of the tableView when scrolled.
6332  bounds.origin.x = CGRectGetMinX(columnRect) - xScroll;
6333  bounds.size.height += yScroll;
6334 
6335  // Fix up the CTM to account for the header and scroll
6336  CGContextTranslateCTM(context, -bounds.origin.x, headerHeight - yScroll);
6337 
6338  //[tableView drawBackgroundInClipRect:bounds];
6339 
6340  if (tableView._draggedColumnIsSelected)
6341  {
6342  CGContextSetFillColor(context, [tableView selectionHighlightColor]);
6343  CGContextFillRect(context, bounds);
6344  }
6345  else
6346  [tableView highlightSelectionInClipRect:bounds];
6347 
6348  //[tableView _drawHorizontalGridInClipRect:bounds];
6349 
6350  var minX = CGRectGetMinX(bounds) + 0.5,
6351  maxX = CGRectGetMaxX(bounds) - 0.5;
6352 
6353  CGContextSetLineWidth(context, 1.0);
6354  CGContextSetAlpha(context, 1.0);
6355  CGContextSetStrokeColor(context, [tableView gridColor]);
6356 
6357  CGContextBeginPath(context);
6358 
6359  CGContextMoveToPoint(context, minX, CGRectGetMinY(bounds));
6360  CGContextAddLineToPoint(context, minX, CGRectGetMaxY(bounds));
6361 
6362  CGContextMoveToPoint(context, maxX, CGRectGetMinY(bounds));
6363  CGContextAddLineToPoint(context, maxX, CGRectGetMaxY(bounds));
6364 
6365  CGContextStrokePath(context);
6366 }
6367 
6368 @end
6369 
6370 @implementation CPTableCellView : CPView
6371 {
6372  id _objectValue;
6373 
6374  CPTextField _textField;
6375  CPImageView _imageView;
6376 }
6377 
6378 - (void)awakeFromCib
6379 {
6380  [super awakeFromCib];
6381  [self setThemeState:CPThemeStateTableDataView];
6382 }
6383 
6384 - (BOOL)setThemeState:(ThemeState)aState
6385 {
6386  [super setThemeState:aState];
6387  [self recursivelyPerformSelector:@selector(setThemeState:) withObject:aState startingFrom:self];
6388 }
6389 
6390 - (BOOL)unsetThemeState:(ThemeState)aState
6391 {
6392  [super unsetThemeState:aState];
6393  [self recursivelyPerformSelector:@selector(unsetThemeState:) withObject:aState startingFrom:self];
6394 }
6395 
6396 - (void)recursivelyPerformSelector:(SEL)selector withObject:(id)anObject startingFrom:(id)aView
6397 {
6398  // Avoid infinite loop if a subview is a CPTableCellView.[[aView subviews] enumerateObjectsUsingBlock:function(view, idx)
6399  {
6400  [view performSelector:selector withObject:anObject];
6401 
6402  if (![view isKindOfClass:[self class]])
6403  [self recursivelyPerformSelector:selector withObject:anObject startingFrom:view];
6404  }];
6405 }
6406 
6407 - (CPString)description
6408 {
6409  return "<" + [self className] + " 0x" + [CPString stringWithHash:[self UID]] + " identifier=" + [self identifier] + ">";
6410 }
6411 
6412 @end
6413 
6415 
6419 - (BOOL)disableAutomaticResizing
6420 {
6421  return _disableAutomaticResizing;
6422 }
6423 
6427 - (void)setDisableAutomaticResizing:(BOOL)aValue
6428 {
6429  _disableAutomaticResizing = aValue;
6430 }
6431 
6432 @end
6433 
6435 
6439 - (id)content
6440 {
6441  return _content;
6442 }
6443 
6447 - (void)setContent:(id)aValue
6448 {
6449  _content = aValue;
6450 }
6451 
6452 @end
6453 
6455 
6459 - (id)objectValue
6460 {
6461  return _objectValue;
6462 }
6463 
6467 - (void)setObjectValue:(id)aValue
6468 {
6469  _objectValue = aValue;
6470 }
6471 
6475 - (CPTextField)textField
6476 {
6477  return _textField;
6478 }
6479 
6483 - (void)setTextField:(CPTextField)aValue
6484 {
6485  _textField = aValue;
6486 }
6487 
6491 - (CPImageView)imageView
6492 {
6493  return _imageView;
6494 }
6495 
6499 - (void)setImageView:(CPImageView)aValue
6500 {
6501  _imageView = aValue;
6502 }
6503 
6504 @end
CPColor selectionHighlightColor()
Definition: CPTableView.j:935
CPColor unfocusedSelectionHighlightColor()
Definition: CPTableView.j:943
int numberOfRows()
Definition: CPTableView.j:1609
var CPTableViewDelegate_tableView_shouldReorderColumn_toColumn_
Definition: CPTableView.j:68
Used to implement exception handling (creating & raising).
Definition: CPException.h:2
var CPTableViewDelegate_tableView_shouldEditTableColumn_row_
Definition: CPTableView.j:53
function CGContextTranslateCTM(aContext, tx, ty)
Definition: CGContext.j:472
CPTableViewSolidHorizontalGridLineMask
Definition: CPTableView.j:91
global CPApp CPTableViewColumnDidMoveNotification
Definition: CPTableView.j:29
BOOL makeFirstResponder:(CPResponder aResponder)
Definition: CPWindow.j:1618
#define FULL_ROW_HEIGHT()
Definition: CPTableView.j:104
void getColumn:row:forView:(Function columnRef, [row] Function rowRef, [forView] CPView aView)
Definition: CPTableView.j:2077
function CGGradientCreateWithColorComponents(aColorSpace, components, locations, count)
Definition: CGGradient.j:29
function CGContextStrokeEllipseInRect(aContext, aRect)
Definition: CGContext.j:606
CPSourceListTopLineColor
Definition: CPTableView.j:80
Definition: CPMenu.h:2
function CGColorSpaceCreateDeviceRGB()
Definition: CGColorSpace.j:107
void drawGridInClipRect:(CGRect aRect)
Definition: CPTableView.j:4061
var CPTableViewDataSource_numberOfRowsInTableView_
Definition: CPTableView.j:34
BOOL isHidden()
Definition: CALayer.j:597
CPInteger lastIndex()
Definition: CPIndexSet.j:289
CPIndexSet selectedRowIndexes()
Definition: CPTableView.j:1490
void addSubview:(CPView aSubview)
Definition: CPView.j:495
void dragView:at:offset:event:pasteboard:source:slideBack:(CPView aView, [at] CGPoint aLocation, [offset] CGSize mouseOffset, [event] CPEvent anEvent, [pasteboard] CPPasteboard aPasteboard, [source] id aSourceObject, [slideBack] BOOL slideBack)
Definition: CPView.j:2348
A CALayer is similar to a CPView
Definition: CALayer.j:46
var CPTableViewDelegate_tableView_shouldSelectRow_
Definition: CPTableView.j:54
CPColor colorWithHexString:(string hex)
Definition: CPColor.j:261
void setAutosaveTableColumns:(BOOL shouldAutosave)
Definition: CPTableView.j:2556
BOOL setThemeState:(ThemeState aState)
Definition: CPView.j:3148
CGRect frame
var CPTableViewDataSource_tableView_sortDescriptorsDidChange_
Definition: CPTableView.j:41
var CPTableViewDelegate_tableView_dataViewForTableColumn_row_
Definition: CPTableView.j:45
CGRect frameOfDataViewAtColumn:row:(CPInteger aColumn, [row] CPInteger aRow)
Definition: CPTableView.j:2139
void setGridColor:(CPColor aColor)
Definition: CPTableView.j:1033
void setAutosaveName:(CPString theAutosaveName)
Definition: CPTableView.j:2531
void trackMouse:(CPEvent anEvent)
Definition: CPControl.j:367
var CPTableViewCornerViewKey
Definition: CPTableView.j:6092
function CGContextAddArcToPoint(aContext, x1, y1, x2, y2, radius)
Definition: CGContext.j:207
var isEqual
A representation of a single point in time.
Definition: CPDate.h:2
CPGraphicsContext currentContext()
void postNotificationName:object:userInfo:(CPString aNotificationName, [object] id anObject, [userInfo] CPDictionary aUserInfo)
CPIndexSet indexesPassingTest:(Function/*(int anIndex)*/aPredicate)
Definition: CPIndexSet.j:559
An object representation of nil.
Definition: CPNull.h:2
CPArray sortDescriptors()
Definition: CPTableView.j:3325
CPInteger columnWithIdentifier:(CPString anIdentifier)
Definition: CPTableView.j:1255
void setGridStyleMask:(unsigned aGrideStyleMask)
Definition: CPTableView.j:1053
int columnIndex
Definition: CPTableView.j:6312
var CPTableViewDelegate_tableView_didClickTableColumn_
Definition: CPTableView.j:46
float scrollerWidth()
Definition: CPScroller.j:124
void layout()
Definition: CPView.j:2664
CPDragOperation CPDragOperationNone
CGSize intercellSpacing()
Definition: CPTableView.j:807
var CPTableViewGridColorKey
Definition: CPTableView.j:6087
BOOL setThemeState:(ThemeState aState)
Definition: CPTableView.j:6384
CPTableViewFirstColumnOnlyAutoresizingStyle
Definition: CPTableView.j:98
CPColor clearColor()
Definition: CPColor.j:448
CPView headerView()
void reloadData()
Definition: CPTableView.j:599
function CGContextDrawLinearGradient(aContext, aGradient, aStartPoint, anEndPoint, options)
void setHighlightedTableColumn:(CPTableColumn aTableColumn)
Definition: CPTableView.j:3019
BOOL autoscroll:(CPEvent anEvent)
Definition: CPView.j:2834
#define ROW_BOTTOM(__heightInfo)
Definition: CPTableView.j:105
The main run loop for the application.
Definition: CPRunLoop.h:2
function CGContextSetStrokeColor(aContext, aColor)
Definition: CGContext.j:675
void recursivelyPerformSelector:withObject:startingFrom:(SEL selector, [withObject] id anObject, [startingFrom] id aView)
Definition: CPTableView.j:6396
CPArray tableColumns()
Definition: CPTableView.j:1244
void deselectColumn:(CPInteger anIndex)
Definition: CPTableView.j:1500
var CPTableViewMultipleSelectionKey
Definition: CPTableView.j:6081
void addObserver:selector:name:object:(id anObserver, [selector] SEL aSelector, [name] CPString aNotificationName, [object] id anObject)
CFData prototype isa
Definition: CPData.j:214
unsigned mouseDownFlags()
Definition: CPControl.j:463
CPResponder firstResponder()
Definition: CPWindow.j:1643
Definition: CPCib.h:2
var CPTableViewSelectionHighlightStyleKey
Definition: CPTableView.j:6080
CGPoint locationInWindow()
Definition: CPEvent.j:287
CPInteger indexGreaterThanOrEqualToIndex:(CPInteger anIndex)
Definition: CPIndexSet.j:363
BOOL autosaveTableColumns()
Definition: CPTableView.j:2564
var CPTableViewColumnResizingKey
Definition: CPTableView.j:6084
CPArray hsbComponents()
Definition: CPColor.j:666
CPColor whiteColor()
Definition: CPColor.j:358
BOOL isColumnSelected:(CPInteger anIndex)
Definition: CPTableView.j:1543
id delegate()
Definition: CALayer.j:965
int width
id initWithFrame:(CGRect aFrame)
Definition: CPControl.j:183
CGRect bounds()
Definition: CPView.j:1287
var CPTableViewDelegate_tableView_selectionIndexesForProposedSelection_
Definition: CPTableView.j:52
CPDeleteFunctionKey
void raise:reason:(CPString aName, [reason] CPString aReason)
Definition: CPException.j:66
var CPTableViewDataSource_tableView_validateDrop_proposedRow_proposedDropOperation_
Definition: CPTableView.j:39
var CPTableViewDelegate_tableView_shouldSelectTableColumn_
Definition: CPTableView.j:55
CPArray registeredDraggedTypes()
Definition: CPView.j:2375
#define NUMBER_OF_COLUMNS()
Definition: CPTableView.j:100
A collection of unique integers.
Definition: CPIndexSet.h:2
CPString charactersIgnoringModifiers()
Definition: CPEvent.j:381
CPView columnClipView
Definition: CPTableView.j:6313
void setHidden:(BOOL shouldBeHidden)
CPInteger firstIndex()
Definition: CPIndexSet.j:278
void viewWillMoveToWindow:(CPWindow aWindow)
Definition: CPView.j:878
CPTimer scheduledTimerWithTimeInterval:callback:repeats:(CPTimeInterval seconds, [callback] Function aFunction, [repeats] BOOL shouldRepeat)
Definition: CPTimer.j:70
void reloadDataForRowIndexes:columnIndexes:(CPIndexSet rowIndexes, [columnIndexes] CPIndexSet columnIndexes)
Definition: CPTableView.j:609
unsigned modifierFlags()
Definition: CPEvent.j:306
CPView hitTest:(CGPoint aPoint)
Definition: CPView.j:1769
CPColor unfocusedSelectionGradientColors()
Definition: CPTableView.j:990
function CGContextAddLineToPoint(aContext, x, y)
Definition: CGContext.j:247
var CPTableViewIntercellSpacingKey
Definition: CPTableView.j:6079
var CPTableViewTableColumnsKey
Definition: CPTableView.j:6077
void setValue:forThemeAttribute:(id aValue, [forThemeAttribute] CPString aName)
Definition: CPView.j:3277
var CPTableViewDataSourceKey
Definition: CPTableView.j:6074
function CGContextStrokePath(aContext)
Definition: CGContext.j:619
var CPTableViewDelegate_tableView_typeSelectStringForTableColumn_row_
Definition: CPTableView.j:60
CPTableViewSelectionIsChangingNotification
Definition: CPTableView.j:32
CPNotificationCenter defaultCenter()
A mutable key-value pair collection.
Definition: CPDictionary.h:2
CPScrollView enclosingScrollView()
Definition: CPView.j:2862
var CPTableViewDelegate_tableViewColumnDidMove_
Definition: CPTableView.j:63
function ThemeState(stateNames)
Definition: CPTheme.j:314
function CGContextSetLineWidth(aContext, aLineWidth)
Definition: CGContext.j:177
void setUsesAlternatingRowBackgroundColors:(BOOL shouldUseAlternatingRowBackgroundColors)
Definition: CPTableView.j:844
CPRunLoop currentRunLoop()
Definition: CPRunLoop.j:230
function CGContextClosePath(aContext)
Definition: CGContext.j:322
CPTableViewDraggingDestinationFeedbackStyleSourceList
Definition: CPTableView.j:73
void setNeedsDisplay()
Definition: CALayer.j:830
BOOL isBlinking
Definition: CPTableView.j:6200
var CPTableViewDataSource_tableView_setObjectValue_forTableColumn_row_
Definition: CPTableView.j:36
CPArray components()
Definition: CPColor.j:624
void setBoundsOrigin:(CGPoint aPoint)
Definition: CPView.j:1308
function CGColorCreate(aColorSpace, components)
Definition: CGColor.j:69
var CPTableViewDelegate_tableView_willDisplayView_forTableColumn_row_
Definition: CPTableView.j:61
CGRect bounds()
Definition: CALayer.j:203
id initWithIndexSet:(CPIndexSet anIndexSet)
Definition: CPIndexSet.j:115
CGRect rectOfColumn:(CPInteger aColumnIndex)
Definition: CPTableView.j:1754
CPWindow window()
Definition: CPView.j:486
void selectRowIndexes:byExtendingSelection:(CPIndexSet rows, [byExtendingSelection] BOOL shouldExtendSelection)
Definition: CPTableView.j:1365
var CPTableViewDelegate_tableView_mouseDownInHeaderOfTableColumn_
Definition: CPTableView.j:50
void setSortDescriptors:(CPArray sortDescriptors)
Definition: CPTableView.j:3284
void setNeedsDisplay:(BOOL aFlag)
Definition: CPTableView.j:3957
CPTableViewGridNone
Definition: CPTableView.j:89
var CPTableViewDelegate_tableView_willRemoveView_forTableColumn_row_
Definition: CPTableView.j:62
CPTableColumn tableColumnWithIdentifier:(CPString anIdentifier)
Definition: CPTableView.j:1273
void viewWillMoveToSuperview:(CPView aView)
Definition: CPTableView.j:4403
void viewWillMoveToSuperview:(CPView aView)
Definition: CPView.j:864
int currentRow
Definition: CPTableView.j:6199
var CPTableViewRowHeightKey
Definition: CPTableView.j:6078
CPTableViewSequentialColumnAutoresizingStyle
Definition: CPTableView.j:95
void deselectAll()
Definition: CPTableView.j:1564
CPTableViewSelectionHighlightStyleRegular
Definition: CPTableView.j:86
void resizeWithOldSuperviewSize:(CGSize aSize)
Definition: CPView.j:1400
var CPTableViewDelegate_tableView_didDragTableColumn_
Definition: CPTableView.j:47
CPAlternateKeyMask
id initWithName:object:userInfo:(CPString aNotificationName, [object] id anObject, [userInfo] CPDictionary aUserInfo)
function CPMaxRange(aRange)
Definition: CPRange.j:70
An immutable string (collection of characters).
Definition: CPString.h:2
CPNull null()
Definition: CPNull.j:51
CGPoint convertPoint:fromView:(CGPoint aPoint, [fromView] CPView aView)
Definition: CPView.j:2142
void setTableView:(CPTableView aTableView)
BOOL isRowSelected:(CPInteger aRow)
Definition: CPTableView.j:1554
CPBundle mainBundle()
Definition: CPBundle.j:82
void setFrameSize:(CGSize aSize)
Definition: CPTableView.j:3914
CPInteger indexGreaterThanIndex:(CPInteger anIndex)
Definition: CPIndexSet.j:301
void drawRow:clipRect:(CPInteger row, [clipRect] CGRect rect)
Definition: CPTableView.j:4386
CPDictionary selectionGradientColors()
Definition: CPTableView.j:976
Definition: CPImage.h:2
var CPTableViewDataSource_tableView_writeRowsWithIndexes_toPasteboard_
Definition: CPTableView.j:40
CPInteger columnAtPoint:(CGPoint aPoint)
Definition: CPTableView.j:1945
CPCommandKeyMask
void highlightSelectionInClipRect:(CGRect aRect)
Definition: CPTableView.j:4139
function CGContextSetAlpha(aContext, anAlpha)
Definition: CGContext.j:519
BOOL sendAction:to:(SEL anAction, [to] id anObject)
Definition: CPControl.j:319
function CPFeatureIsCompatible(aFeature)
CGRect exposedRect()
Definition: CPTableView.j:3370
CPColor colorWithCalibratedWhite:alpha:(float white, [alpha] float alpha)
Definition: CPColor.j:170
id initWithCoder:(CPCoder aCoder)
Definition: CPControl.j:1085
void addIndex:(CPInteger anIndex)
Definition: CPIndexSet.j:709
CPColor colorWithRed:green:blue:alpha:(float red, [green] float green, [blue] float blue, [alpha] float alpha)
Definition: CPColor.j:119
Holds attributes necessary to describe how to sort a set of objects.
var CPTableViewDelegate_tableView_isGroupRow_
Definition: CPTableView.j:49
CPString autosaveName()
Definition: CPTableView.j:2545
function CGContextSetFillColor(aContext, aColor)
Definition: CGContext.j:663
CPBinder getBinding:forObject:(CPString aBinding, [forObject] id anObject)
CPTableView tableView
Definition: CPTableView.j:6198
CPTableViewDropAbove
Definition: CPTableView.j:77
CPTableColumnAutoresizingMask
Definition: CPTableColumn.j:27
void setNeedsDisplay:(BOOL aFlag)
Definition: CPView.j:2490
CPView dragViewForRowsWithIndexes:tableColumns:event:offset:(CPIndexSet theDraggedRows, [tableColumns] CPArray theTableColumns, [event] CPEvent theDragEvent, [offset] CGPoint dragViewOffset)
Definition: CPTableView.j:3074
CPMenu menuForEvent:(CPEvent anEvent)
Definition: CPView.j:1850
void setNeedsLayout()
Definition: CPTableView.j:3968
CPTableViewNoColumnAutoresizing
Definition: CPTableView.j:93
CPTableViewDropOn
Definition: CPTableView.j:76
CPInteger rowAtPoint:(CGPoint aPoint)
Definition: CPTableView.j:1995
void enumerateIndexesUsingBlock:(Function/*(int idx,@ref BOOL stop)*/aFunction)
Definition: CPIndexSet.j:487
void addObserver:forKeyPath:options:context:(id anObserver, [forKeyPath] CPString aPath, [options] CPKeyValueObservingOptions options, [context] id aContext)
var CPTableViewDataSource_tableView_namesOfPromisedFilesDroppedAtDestination_forDraggedRowsWithIndexes_
Definition: CPTableView.j:38
var CPTableViewEmptySelectionKey
Definition: CPTableView.j:6082
function CGContextStrokeRoundedRectangleInRect(aContext, aRect, aRadius, ne, se, sw, nw)
Definition: CGContext.j:711
function CGContextBeginPath(aContext)
Definition: CGContext.j:311
id stringWithHash:(unsigned aHash)
Definition: CPString.j:107
CPLeftMouseUp
CPShiftKeyMask
CPView preparedViewAtColumn:row:(CPInteger column, [row] CPInteger row)
Definition: CPTableView.j:3545
void setDelegate:(id< CPTableViewDelegate > aDelegate)
Definition: CPTableView.j:2793
CPColor backgroundColor()
Definition: CALayer.j:629
CPThemeStateSelectedDataView
Definition: CPTheme.j:550
A notification that can be posted to a CPNotificationCenter.
Definition: CPNotification.h:2
var CPTableViewArchivedReusableViewsKey
Definition: CPTableView.j:6094
void selectColumnIndexes:byExtendingSelection:(CPIndexSet columns, [byExtendingSelection] BOOL shouldExtendSelection)
Definition: CPTableView.j:1307
CPHTMLCanvasFeature
CPDate limitDateForMode:(CPString aMode)
Definition: CPRunLoop.j:340
CPTableViewLastColumnOnlyAutoresizingStyle
Definition: CPTableView.j:97
void setNeedsLayout()
Definition: CPView.j:2641
id makeViewWithIdentifier:owner:(CPString anIdentifier, [owner] id anOwner)
Definition: CPTableView.j:3679
var CPTableViewDelegateKey
Definition: CPTableView.j:6075
var CPTableViewDataSource_tableView_objectValueForTableColumn_row_
Definition: CPTableView.j:35
CPUpArrowFunctionKey
var CPTableViewGridStyleMaskKey
Definition: CPTableView.j:6088
CPColor colorWithHue:saturation:brightness:(float hue, [saturation] float saturation, [brightness] float brightness)
Definition: CPColor.j:188
var CPTableViewDelegate_tableViewSelectionIsChanging_
Definition: CPTableView.j:66
CPTableViewSelectionDidChangeNotification
Definition: CPTableView.j:31
var CPTableViewDelegate_tableViewColumnDidResize_
Definition: CPTableView.j:64
void noteHeightOfRowsWithIndexesChanged:(CPIndexSet anIndexSet)
Definition: CPTableView.j:2449
CPPlatformActionKeyMask
BOOL isKeyWindow()
Definition: CPWindow.j:2052
var CPTableViewDelegate_tableView_shouldTypeSelectForEvent_withCurrentSearchString_
Definition: CPTableView.j:58
A timer object that can send a message after the given time interval.
Definition: CPTimer.h:2
Defines methods for use when archiving & restoring (enc/decoding).
Definition: CPCoder.h:2
CPNotFound
Definition: CPObjJRuntime.j:62
void editColumn:row:withEvent:select:(CPInteger columnIndex, [row] CPInteger rowIndex, [withEvent] CPEvent theEvent, [select] BOOL flag)
Definition: CPTableView.j:5302
function CGContextStrokeRect(aContext, aRect)
Definition: CGContext.j:388
BOOL unsetThemeState:(ThemeState aState)
Definition: CPView.j:3161
BOOL allowsColumnReordering()
Definition: CPTableView.j:704
Sends messages (CPNotification) between objects.
CPIndexSet selectedColumnIndexes()
Definition: CPTableView.j:1474
#define HAS_VARIABLE_ROW_HEIGHTS()
Definition: CPTableView.j:106
id new()
Definition: CPObject.j:122
unsigned gridStyleMask()
Definition: CPTableView.j:1066
var CPTableViewDelegate_tableView_shouldTrackView_forTableColumn_row_
Definition: CPTableView.j:57
void setIndicatorImage:inTableColumn:(CPImage anImage, [inTableColumn] CPTableColumn aTableColumn)
Definition: CPTableView.j:2982
CPImage dragImageForRowsWithIndexes:tableColumns:event:offset:(CPIndexSet dragRows, [tableColumns] CPArray theTableColumns, [event] CPEvent dragEvent, [offset] CGPoint dragImageOffset)
Definition: CPTableView.j:3057
var CPTableViewDelegate_tableViewSelectionDidChange_
Definition: CPTableView.j:65
CPDownArrowFunctionKey
#define UPDATE_COLUMN_RANGES_IF_NECESSARY()
Definition: CPTableView.j:101
var CPTableViewAutosaveNameKey
Definition: CPTableView.j:6093
void setBackgroundColor:(CPColor aColor)
Definition: CPView.j:1859
var CPTableViewDelegate_selectionShouldChangeInTableView_
Definition: CPTableView.j:43
CPLeftMouseDown
void setBoundsSize:(CGSize aSize)
Definition: CPView.j:1357
function CGContextFillRoundedRectangleInRect(aContext, aRect, aRadius, ne, se, sw, nw)
Definition: CGContext.j:692
void scrollColumnToVisible:(int columnIndex)
Definition: CPTableView.j:2513
CPBundle bundleForClass:(Class aClass)
Definition: CPBundle.j:77
void setFrameSize:(CGSize aSize)
Definition: CPView.j:1086
var CPTableViewColumnSelectionKey
Definition: CPTableView.j:6085
CPSourceListGradient
Definition: CPTableView.j:79
void sizeLastColumnToFit()
Definition: CPTableView.j:2337
function CGContextFillRect(aContext, aRect)
Definition: CGContext.j:358
void bind:toObject:withKeyPath:options:(CPString aBinding, [toObject] id anObject, [withKeyPath] CPString aKeyPath, [options] CPDictionary options)
Definition: CPTableView.j:6045
var CPTableViewColumnReorderingKey
Definition: CPTableView.j:6083
CPTableViewSelectionHighlightStyleNone
Definition: CPTableView.j:85
var CPTableViewDelegate_tableView_toolTipForView_rect_tableColumn_row_mouseLocation_
Definition: CPTableView.j:59
var CPTableViewHeaderViewKey
Definition: CPTableView.j:6076
var CPTableViewDataSource_tableView_acceptDrop_row_dropOperation_
Definition: CPTableView.j:37
CPControlKeyMask
void removeObserver:name:object:(id anObserver, [name] CPString aNotificationName, [object] id anObject)
id indexSetWithIndexesInRange:(CPRange aRange)
Definition: CPIndexSet.j:60
CPTableViewUniformColumnAutoresizingStyle
Definition: CPTableView.j:94
var CPTableViewUsesAlternatingBackgroundKey
Definition: CPTableView.j:6089
Definition: CPEvent.h:2
Class class()
Definition: CPObject.j:179
CPView contentView()
Definition: CPWindow.j:1212
var CPTableViewDelegate_tableView_heightOfRow_
Definition: CPTableView.j:48
CPTableViewSelectionHighlightStyleSourceList
Definition: CPTableView.j:87
CPView superview()
Definition: CPView.j:469
CGRect rectOfRow:(CPInteger aRowIndex)
Definition: CPTableView.j:1842
void drawBackgroundInClipRect:(CGRect aRect)
Definition: CPTableView.j:4005
CPArray alternatingRowBackgroundColors()
Definition: CPTableView.j:872
CGRect frame()
Definition: CPView.j:1011
var CPTableViewDelegate_tableView_nextTypeSelectMatchFromRow_toRow_forString_
Definition: CPTableView.j:51
void draggedImage:endedAt:operation:(CPImage anImage, [endedAt] CGPoint aLocation, [operation] CPDragOperation anOperation)
Definition: CPTableView.j:5637
var CPTableViewDelegate_tableViewMenuForTableColumn_row_
Definition: CPTableView.j:67
void removeIndexes:(CPIndexSet anIndexSet)
Definition: CPIndexSet.j:814
void setAlternatingRowBackgroundColors:(CPArray alternatingRowBackgroundColors)
Definition: CPTableView.j:862
CPRange rowsInRect:(CGRect aRect)
Definition: CPTableView.j:1852
void scrollRowToVisible:(int rowIndex)
Definition: CPTableView.j:2497
CPDeleteCharacter
Definition: CPText.j:30
CPIndexSet columnIndexesInRect:(CGRect aRect)
Definition: CPTableView.j:1911
void removeIndex:(CPInteger anIndex)
Definition: CPIndexSet.j:804
BOOL isEqualToIndexSet:(CPIndexSet anIndexSet)
Definition: CPIndexSet.j:151
BOOL scrollRectToVisible:(CGRect aRect)
Definition: CPView.j:2755
function CGContextFillPath(aContext)
Definition: CGContext.j:548
void setCornerView:(CPView aView)
Definition: CPTableView.j:1650
Definition: CPURL.h:2
void setDataSource:(id< CPTableViewDataSource > aDataSource)
Definition: CPTableView.j:547
id indexSetWithIndex:(int anIndex)
Definition: CPIndexSet.j:51
CGRect visibleRect()
Definition: CPView.j:2715
var CPTableViewAlternatingRowColorsKey
Definition: CPTableView.j:6090
var CPTableViewDelegate_tableView_viewForTableColumn_row_
Definition: CPTableView.j:44
function CGContextMoveToPoint(aContext, x, y)
Definition: CGContext.j:344
CPView headerView()
Definition: CPTableView.j:1666
void encodeWithCoder:(CPCoder aCoder)
Definition: CPControl.j:1114
void setFrameOrigin:(CGPoint aPoint)
Definition: CPView.j:1054
void setHeaderView:(CPView aHeaderView)
Definition: CPTableView.j:1679
CPObservedObjectKey
CPColor gridColor()
Definition: CPTableView.j:1043
CPTableViewReverseSequentialColumnAutoresizingStyle
Definition: CPTableView.j:96
CPRange function CPMakeRange(location, length)
Definition: CPRange.j:37
void display()
Definition: CPView.j:2536
id indexSet()
Definition: CPIndexSet.j:43
CPString UID()
Definition: CPObject.j:542
id initWithCibNamed:bundle:(CPString aName, [bundle] CPBundle aBundle)
Definition: CPCib.j:95
function CGContextAddRect(aContext, aRect)
Definition: CGContext.j:289
var CPTableViewDelegate_tableView_shouldShowViewExpansionForTableColumn_row_
Definition: CPTableView.j:56
void noteNumberOfRowsChanged()
Definition: CPTableView.j:2370
id pasteboardWithName:(CPString aName)
Definition: CPPasteboard.j:97
CPException exceptionWithName:reason:userInfo:(CPString aName, [reason] CPString aReason, [userInfo] CPDictionary aUserInfo)
Definition: CPException.j:94
CPTableViewColumnDidResizeNotification
Definition: CPTableView.j:30
id alloc()
Definition: CPObject.j:130
Definition: CPView.j:131
CPTableViewDraggingDestinationFeedbackStyleNone
Definition: CPTableView.j:71
CPSourceListBottomLineColor
Definition: CPTableView.j:81
var CPTableViewColumnAutoresizingStyleKey
Definition: CPTableView.j:6086
int numberOfColumns()
Definition: CPTableView.j:1601
CPTableViewSolidVerticalGridLineMask
Definition: CPTableView.j:90
CPTableViewDraggingDestinationFeedbackStyleRegular
Definition: CPTableView.j:72
function CGGradientCreateWithColors(aColorSpace, colors, locations)
Definition: CGGradient.j:54