API  1.0.0
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 
109 @protocol CPTableViewDataSource <CPObject>
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 
123 @protocol CPTableViewDelegate <CPObject>
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": @[[CPColor whiteColor], [CPColor colorWithRed:245.0 / 255.0 green:249.0 / 255.0 blue:252.0 / 255.0 alpha:1.0]],
327  @"grid-color": [CPColor colorWithHexString:@"dce0e2"],
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  @"header-view-height": 25.0
350  };
351 }
352 
353 - (id)initWithFrame:(CGRect)aFrame
354 {
355  self = [super initWithFrame:aFrame];
356 
357  if (self)
358  {
359  //Configuring Behavior
360  _allowsColumnReordering = YES;
361  _allowsColumnResizing = YES;
362  _allowsMultipleSelection = NO;
363  _allowsEmptySelection = YES;
364  _allowsColumnSelection = NO;
365  _disableAutomaticResizing = NO;
366 
367  //Setting Display Attributes
368  _selectionHighlightStyle = CPTableViewSelectionHighlightStyleRegular;
369 
371  [self setAlternatingRowBackgroundColors:[self valueForThemeAttribute:@"alternating-row-colors"]];
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:[self valueForThemeAttribute:@"grid-color"]];
382  [self setGridStyleMask:CPTableViewGridNone];
383 
384  [self setHeaderView:[[CPTableHeaderView alloc] initWithFrame:CGRectMake(0, 0, [self bounds].size.width, [self valueForThemeAttribute:@"header-view-height"])]];
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.
636  [self noteNumberOfRowsChanged];
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])
1187  [selectedColumnIndexes addIndex:idx];
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)
2909  [self selectColumnIndexes:[CPIndexSet indexSetWithIndex:clickedColumn] byExtendingSelection: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
2925  [self selectColumnIndexes:[CPIndexSet indexSetWithIndex:clickedColumn] byExtendingSelection:NO];
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 firstRowFrame = [self frameOfDataViewAtColumn:0 row:[theDraggedRows firstIndex]],
3077  lastRowFrame = [self frameOfDataViewAtColumn:0 row:[theDraggedRows lastIndex]],
3078  dragViewHeight = CGRectGetMaxY(lastRowFrame) - CGRectGetMinY(firstRowFrame);
3079 
3080  var bounds = CGRectMake(0, 0, CGRectGetWidth([self bounds]), dragViewHeight),
3081  dragView = [[CPView alloc] initWithFrame:bounds];
3082 
3083  [dragView setAlphaValue:0.7];
3084 // [dragView setBackgroundColor:[CPColor colorWithWhite:1 alpha:0.5]];
3085 
3086  // We have to fetch all the data views for the selected rows and columns
3087  // After that we can copy these add them to a transparent drag view and use that drag view
3088  // to make it appear we are dragging images of those rows (as you would do in regular Cocoa)
3089  [self _enumerateViewsInRows:theDraggedRows tableColumns:theTableColumns usingBlock:function(v, row, tableColumn, stop)
3090  {
3091  var column = [_tableColumns indexOfObjectIdenticalTo:tableColumn],
3092  newDataView = [self preparedViewAtColumn:column row:row],
3093  origin = [newDataView frame].origin;
3094 
3095  origin.y -= CGRectGetMinY(firstRowFrame);
3096  [newDataView setFrameOrigin:origin];
3097 
3098  [dragView addSubview:newDataView];
3099  }];
3100 
3101  var dragPoint = [self convertPoint:[theDragEvent locationInWindow] fromView:nil];
3102  dragViewOffset.x = CGRectGetWidth(bounds) / 2 - dragPoint.x;
3103  dragViewOffset.y = CGRectGetHeight(bounds) / 2 - dragPoint.y;
3104 
3105  return dragView;
3106 }
3107 
3108 - (CPView)_dragViewForColumn:(CPInteger)columnIndex
3109 {
3110  var headerFrame = [_headerView frame],
3111  visibleRect = [self visibleRect],
3112  visibleRows = [self rowsInRect:visibleRect],
3113  columnRect = [self rectOfColumn:columnIndex],
3114  tableColumn = [[self tableColumns] objectAtIndex:columnIndex],
3115  tableColumnUID = [tableColumn UID],
3116  columnHeaderView = [tableColumn headerView],
3117  columnHeaderFrame = [columnHeaderView frame],
3118  frame = CGRectMake(MAX(CGRectGetMinX(columnRect) - CGRectGetMinX(visibleRect), 0.0),
3119  0.0,
3120  CGRectGetWidth(columnHeaderFrame),
3121  CGRectGetHeight(visibleRect) + CGRectGetHeight(headerFrame));
3122 
3123  // We need a wrapper view around the header and column, this is what will be dragged
3124  var dragView = [[_CPColumnDragDrawingView alloc] initWithFrame:frame];
3125 
3126  [dragView setTableView:self];
3127  [dragView setColumnIndex:columnIndex];
3128  [dragView setBackgroundColor:[CPColor clearColor]];
3129  [dragView setAlphaValue:0.6];
3130 
3131  // Now a view that clips the column data views, which itself is clipped to the content view
3132  var columnVisRect = CGRectIntersection(columnRect, visibleRect);
3133 
3134  frame = CGRectMake(0.0, CGRectGetHeight(headerFrame), CGRectGetWidth(columnRect), CGRectGetHeight(columnVisRect));
3135 
3136  var columnClipView = [[CPView alloc] initWithFrame:frame];
3137 
3138  [dragView addSubview:columnClipView];
3139  [dragView setColumnClipView:columnClipView];
3140  _draggedColumnIsSelected = [self isColumnSelected:columnIndex];
3141 
3142  var columnLeft = CGRectGetMinX(columnRect),
3143  offset = CGPointMake(columnLeft, CGRectGetMinY(visibleRect));
3144 
3145  [self _enumerateViewsInRows:_exposedRows columns:[CPIndexSet indexSetWithIndex:columnIndex] usingBlock:function(dataView, row, column, stop)
3146  {
3147  [self _addDraggedDataView:dataView toView:columnClipView forColumn:column row:row offset:offset];
3148 
3149  delete (_dataViewsForRows[row][tableColumnUID]);
3150  }];
3151 
3152  // Add the column header view
3153  columnHeaderFrame.origin = CGPointMakeZero();
3154 
3155  var dragColumnHeaderView = [[_CPTableColumnHeaderView alloc] initWithFrame:columnHeaderFrame],
3156  image = [columnHeaderView _indicatorImage];
3157 
3158  [dragColumnHeaderView setStringValue:[columnHeaderView stringValue]];
3159  [dragColumnHeaderView setThemeState:[columnHeaderView themeState]];
3160  [dragColumnHeaderView _setIndicatorImage:image];
3161 
3162  // Give it a tag so it can be found later
3163  [dragColumnHeaderView setTag:1];
3164 
3165  [dragView addSubview:dragColumnHeaderView];
3166 
3167  // While dragging, the column is deselected in the table view
3168  [_selectedColumnIndexes removeIndex:columnIndex];
3169  [self setNeedsDisplay:YES];
3170 
3171  return dragView;
3172 }
3173 
3174 - (void)_addDraggedDataView:(CPView)aDataView toView:(CPView)aSuperview forColumn:(CPInteger)column row:(CPInteger)row offset:(CGPoint)offset
3175 {
3176  var dataViewFrame = [self frameOfDataViewAtColumn:column row:row];
3177 
3178  dataViewFrame.origin.x -= offset.x;
3179  // Offset by table header height - scroll position
3180  dataViewFrame.origin.y -= offset.y;
3181 
3182  [aDataView setFrame:dataViewFrame];
3183 
3184  [aSuperview addSubview:aDataView];
3185 }
3186 
3191 - (void)setDraggingSourceOperationMask:(CPDragOperation)mask forLocal:(BOOL)isLocal
3192 {
3193  //ignore local for the time being since only one capp app can run at a time...
3194  _dragOperationDefaultMask = mask;
3195 }
3196 
3202 - (void)setDropRow:(CPInteger)row dropOperation:(CPTableViewDropOperation)operation
3203 {
3204  if (row > [self numberOfRows] && operation === CPTableViewDropOn)
3205  {
3206  var numberOfRows = [self numberOfRows] + 1,
3207  reason = @"Attempt to set dropRow=" + row +
3208  " dropOperation=CPTableViewDropOn when [0 - " + numberOfRows + "] is valid range of rows.";
3209 
3210  [[CPException exceptionWithName:@"Error" reason:reason userInfo:nil] raise];
3211  }
3212 
3213  _retargetedDropRow = row;
3214  _retargetedDropOperation = operation;
3215 }
3216 
3228 - (void)setDraggingDestinationFeedbackStyle:(CPTableViewDraggingDestinationFeedbackStyle)aStyle
3229 {
3230  //FIX ME: this should vary up the highlight color, currently nothing is being done with it
3231  _destinationDragStyle = aStyle;
3232 }
3233 
3244 - (CPTableViewDraggingDestinationFeedbackStyle)draggingDestinationFeedbackStyle
3245 {
3246  return _destinationDragStyle;
3247 }
3248 
3254 - (void)setVerticalMotionCanBeginDrag:(BOOL)aFlag
3255 {
3256  _verticalMotionCanDrag = aFlag;
3257 }
3258 
3262 - (BOOL)verticalMotionCanBeginDrag
3263 {
3264  return _verticalMotionCanDrag;
3265 }
3266 
3267 - (CPTableColumn)_tableColumnForSortDescriptor:(CPSortDescriptor)theSortDescriptor
3268 {
3269  var tableColumns = [self tableColumns];
3270 
3271  for (var i = 0; i < [tableColumns count]; i++)
3272  {
3273  var tableColumn = [tableColumns objectAtIndex:i],
3274  sortDescriptorPrototype = [tableColumn sortDescriptorPrototype];
3275 
3276  if (!sortDescriptorPrototype)
3277  continue;
3278 
3279  if ([sortDescriptorPrototype key] === [theSortDescriptor key]
3280  && [sortDescriptorPrototype selector] === [theSortDescriptor selector])
3281  {
3282  return tableColumn;
3283  }
3284  }
3285 
3286  return nil;
3287 }
3288 
3294 - (void)setSortDescriptors:(CPArray)sortDescriptors
3295 {
3296  var oldSortDescriptors = [[self sortDescriptors] copy],
3297  newSortDescriptors = [CPArray array];
3298 
3299  if (sortDescriptors !== nil)
3300  [newSortDescriptors addObjectsFromArray:sortDescriptors];
3301 
3302  if ([newSortDescriptors isEqual:oldSortDescriptors])
3303  return;
3304 
3305  _sortDescriptors = newSortDescriptors;
3306 
3307  var oldColumn = nil,
3308  newColumn = nil;
3309 
3310  if ([newSortDescriptors count] > 0)
3311  {
3312  var newMainSortDescriptor = [newSortDescriptors objectAtIndex:0];
3313  newColumn = [self _tableColumnForSortDescriptor:newMainSortDescriptor];
3314  }
3315 
3316  if ([oldSortDescriptors count] > 0)
3317  {
3318  var oldMainSortDescriptor = [oldSortDescriptors objectAtIndex:0];
3319  oldColumn = [self _tableColumnForSortDescriptor:oldMainSortDescriptor];
3320  }
3321 
3322  var image = [newMainSortDescriptor ascending] ? [self _tableHeaderSortImage] : [self _tableHeaderReverseSortImage];
3323  [self setIndicatorImage:nil inTableColumn:oldColumn];
3324  [self setIndicatorImage:image inTableColumn:newColumn];
3325 
3326  [self _sendDataSourceSortDescriptorsDidChange:oldSortDescriptors];
3327 
3328  var binderClass = [[self class] _binderClassForBinding:@"sortDescriptors"];
3329  [[binderClass getBinding:@"sortDescriptors" forObject:self] reverseSetValueFor:@"sortDescriptors"];
3330 }
3331 
3335 - (CPArray)sortDescriptors
3336 {
3337  return _sortDescriptors;
3338 }
3339 
3343 - (id)_objectValueForTableColumn:(CPTableColumn)aTableColumn row:(CPInteger)aRowIndex useCache:(BOOL)useCache
3344 {
3345  var objectValue;
3346 
3347  var tableColumnUID = [aTableColumn UID],
3348  tableColumnObjectValues = _objectValues[tableColumnUID];
3349 
3350  if (!tableColumnObjectValues)
3351  {
3352  tableColumnObjectValues = [];
3353  _objectValues[tableColumnUID] = tableColumnObjectValues;
3354  }
3355 
3356  if (useCache)
3357  objectValue = tableColumnObjectValues[aRowIndex];
3358 
3359  // tableView:objectValueForTableColumn:row: is optional if content bindings are in place.
3360  if (objectValue === undefined)
3361  {
3362  if ([self _dataSourceRespondsToObjectValueForTableColumn])
3363  {
3364  objectValue = [self _sendDataSourceObjectValueForTableColumn:aTableColumn row:aRowIndex];
3365  tableColumnObjectValues[aRowIndex] = objectValue;
3366  }
3367  else if (!_isViewBased && ![self infoForBinding:@"content"])
3368  {
3369  CPLog.warn(@"no content binding established and data source " + [_dataSource description] + " does not implement tableView:objectValueForTableColumn:row:");
3370  }
3371  }
3372 
3373  return objectValue;
3374 }
3375 
3376 
3380 - (CGRect)exposedRect
3381 {
3382  if (!_exposedRect)
3383  {
3384  var superview = [self superview];
3385 
3386  // FIXME: Should we be rect intersecting in case
3387  // there are multiple views in the clip view?
3388  if ([superview isKindOfClass:[CPClipView class]])
3389  _exposedRect = [superview bounds];
3390  else
3391  _exposedRect = [self bounds];
3392  }
3393 
3394  return _exposedRect;
3395 }
3396 
3400 - (void)load
3401 {
3402  if (_reloadAllRows)
3403  {
3404  [self _unloadDataViewsInRows:_exposedRows columns:_exposedColumns];
3405 
3406  _exposedRows = [CPIndexSet indexSet];
3407  _exposedColumns = [CPIndexSet indexSet];
3408 
3409  _reloadAllRows = NO;
3410  }
3411 
3412  var exposedRect = [self exposedRect],
3413  exposedRows = [CPIndexSet indexSetWithIndexesInRange:[self rowsInRect:exposedRect]],
3414  exposedColumns = [self columnIndexesInRect:exposedRect],
3415  obscuredRows = [_exposedRows copy],
3416  obscuredColumns = [_exposedColumns copy];
3417 
3418  [obscuredRows removeIndexes:exposedRows];
3419  [obscuredColumns removeIndexes:exposedColumns];
3420 
3421  var newlyExposedRows = [exposedRows copy],
3422  newlyExposedColumns = [exposedColumns copy];
3423 
3424  [newlyExposedRows removeIndexes:_exposedRows];
3425  [newlyExposedColumns removeIndexes:_exposedColumns];
3426 
3427  var previouslyExposedRows = [exposedRows copy],
3428  previouslyExposedColumns = [exposedColumns copy];
3429 
3430  [previouslyExposedRows removeIndexes:newlyExposedRows];
3431  [previouslyExposedColumns removeIndexes:newlyExposedColumns];
3432 
3433  [self _unloadDataViewsInRows:previouslyExposedRows columns:obscuredColumns];
3434  [self _unloadDataViewsInRows:obscuredRows columns:previouslyExposedColumns];
3435  [self _unloadDataViewsInRows:obscuredRows columns:obscuredColumns];
3436  [self _unloadDataViewsInRows:newlyExposedRows columns:newlyExposedColumns];
3437 
3438  [self _loadDataViewsInRows:previouslyExposedRows columns:newlyExposedColumns];
3439  [self _loadDataViewsInRows:newlyExposedRows columns:previouslyExposedColumns];
3440  [self _loadDataViewsInRows:newlyExposedRows columns:newlyExposedColumns];
3441 
3442  _exposedRows = exposedRows;
3443  _exposedColumns = exposedColumns;
3444 
3445  [_tableDrawView setFrame:exposedRect];
3446 
3447  [self setNeedsDisplay:YES];
3448 
3449  // if we have any columns to remove do that here
3450 
3451  if (_needsDifferedTableColumnRemove)
3452  {
3453  var removeCount = [_differedColumnDataToRemove count],
3454  removeIndexes = [CPIndexSet indexSet];
3455 
3456  for (var i = 0; i < removeCount; i++)
3457  {
3458  var data = _differedColumnDataToRemove[i],
3459  tableColumn = data.column,
3460  columnIdx = [_tableColumns indexOfObject:tableColumn];
3461 
3462  if (columnIdx !== CPNotFound)
3463  [removeIndexes addIndex:columnIdx];
3464  }
3465 
3466  var rowIndexes = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, [self numberOfRows])];
3467  [self _unloadDataViewsInRows:rowIndexes columns:removeIndexes];
3468 
3469  [_tableColumns removeObjectsAtIndexes:removeIndexes];
3470 
3471  _dirtyTableColumnRangeIndex = 0;
3472  [self _recalculateTableColumnRanges];
3473 
3474  [_differedColumnDataToRemove removeAllObjects];
3475  _needsDifferedTableColumnRemove = NO;
3476  }
3477 
3478  // Now clear all the leftovers
3479  // FIXME: this could be faster!
3480  for (var identifier in _cachedDataViews)
3481  {
3482  var dataViews = _cachedDataViews[identifier],
3483  count = dataViews.length;
3484 
3485  while (count--)
3486  [dataViews[count] removeFromSuperview];
3487  }
3488 }
3489 
3493 - (void)_unloadDataViewsInRows:(CPIndexSet)rowIndexes columns:(CPIndexSet)columnIndexes
3494 {
3495  if (![rowIndexes count] || ![columnIndexes count] || [columnIndexes lastIndex] >= [_tableColumns count])
3496  return;
3497 
3498  // If and edited view is about to be unloaded, cleanup its state before enqueuing.
3499  if ([columnIndexes containsIndex:_editingColumn] && [rowIndexes containsIndex:_editingRow])
3500  [self _resignEditedView];
3501 
3502  var tableColumns = [_tableColumns objectsAtIndexes:columnIndexes];
3503 
3504  [self _enumerateViewsInRows:rowIndexes tableColumns:tableColumns usingBlock:function(dataView, row, tableColumn, stop)
3505  {
3506  var dataViewsForRows = _dataViewsForRows[row],
3507  tableColumnUID = [tableColumn UID];
3508 
3509  delete (dataViewsForRows[tableColumnUID]);
3510 
3511  [self _sendDelegateWillRemoveView:dataView forTableColumn:tableColumn row:row];
3512  [self _enqueueReusableDataView:dataView];
3513  }];
3514 }
3515 
3519 - (void)_loadDataViewsInRows:(CPIndexSet)rowIndexes columns:(CPIndexSet)columnIndexes
3520 {
3521  if (![rowIndexes count] || ![columnIndexes count])
3522  return;
3523 
3525 
3526  if (_numberOfHiddenColumns > 0)
3527  columnIndexes = [columnIndexes indexesPassingTest:function(idx, stop)
3528  {
3529  return ![_tableColumns[idx] isHidden];
3530  }];
3531 
3532  [rowIndexes enumerateIndexesUsingBlock:function(rowIndex, stopRow)
3533  {
3534  if (!_dataViewsForRows[rowIndex])
3535  _dataViewsForRows[rowIndex] = {};
3536 
3537  var dataViewsForRow = _dataViewsForRows[rowIndex],
3538  isRowSelected = [self isRowSelected:rowIndex],
3539  row = rowIndex;
3540 
3541  [columnIndexes enumerateIndexesUsingBlock:function(columnIndex, stopCol)
3542  {
3543  var tableColumn = _tableColumns[columnIndex],
3544  tableColumnUID = [tableColumn UID],
3545  dataView = [self _preparedViewAtColumn:columnIndex row:row isRowSelected:isRowSelected];
3546 
3547  if ([dataView superview] !== self)
3548  [self addSubview:dataView];
3549 
3550  dataViewsForRow[tableColumnUID] = dataView;
3551  }];
3552  }];
3553 }
3554 
3555 - (CPView)preparedViewAtColumn:(CPInteger)column row:(CPInteger)row
3556 {
3557  return [self _preparedViewAtColumn:column row:row isRowSelected:[self isRowSelected:row]];
3558 }
3559 
3560 - (CPView)_preparedViewAtColumn:(CPInteger)column row:(CPInteger)row isRowSelected:(BOOL)isRowSelected
3561 {
3562  var tableColumn = _tableColumns[column],
3563  tableColumnUID = [tableColumn UID],
3564  dataView = [self _newDataViewForRow:row tableColumn:tableColumn];
3565 
3566  [dataView setFrame:[self frameOfDataViewAtColumn:column row:row]];
3567 
3568  [self _setObjectValueForTableColumn:tableColumn row:row forView:dataView];
3569 
3570  if (_selectionHighlightStyle !== CPTableViewSelectionHighlightStyleNone && (isRowSelected || [self isColumnSelected:column]))
3571  _BlockSelectView(dataView, row, column);
3572  else
3573  _BlockDeselectView(dataView, row, column);
3574 
3575  // FIX ME: for performance reasons we might consider diverging from cocoa and moving this to the reloadData method
3576  if (_implementedDelegateMethods & CPTableViewDelegate_tableView_isGroupRow_)
3577  {
3578  if ([_delegate tableView:self isGroupRow:row])
3579  {
3580  [_groupRows addIndex:row];
3581  [dataView setThemeState:CPThemeStateGroupRow];
3582  }
3583  else
3584  {
3585  [_groupRows removeIndexesInRange:CPMakeRange(row, 1)];
3586  [dataView unsetThemeState:CPThemeStateGroupRow];
3587  }
3588 
3589  [self setNeedsDisplay:YES];
3590  }
3591 
3592  if (!_isViewBased)
3593  [self _setEditingState:NO forView:dataView];
3594 
3595  [self _sendDelegateWillDisplayView:dataView forTableColumn:tableColumn row:row];
3596 
3597  return dataView;
3598 }
3599 
3600 - (void)_setObjectValueForTableColumn:(CPTableColumn)aTableColumn row:(CPInteger)aRow forView:(CPView)aDataView
3601 {
3602  [self _setObjectValueForTableColumn:aTableColumn row:aRow forView:aDataView useCache:!_invalidateObjectValuesCache];
3603 }
3604 
3605 - (void)_setObjectValueForTableColumn:(CPTableColumn)aTableColumn row:(CPInteger)aRow forView:(CPView)aDataView useCache:(BOOL)useCache
3606 {
3607  if (_implementedDataSourceMethods & CPTableViewDataSource_tableView_objectValueForTableColumn_row_)
3608  [aDataView setObjectValue:[self _objectValueForTableColumn:aTableColumn row:aRow useCache:useCache]];
3609 
3610  // This gives the table column an opportunity to apply its bindings.
3611  // It will override the value set above if there is a binding.
3612 
3613  if (_contentBindingExplicitlySet)
3614  [self _prepareContentBindedDataView:aDataView forRow:aRow];
3615  else
3616  // For both cell-based and view-based
3617  [aTableColumn _prepareDataView:aDataView forRow:aRow];
3618 }
3619 
3620 - (void)_prepareContentBindedDataView:(CPView)dataView forRow:(CPInteger)aRow
3621 {
3622  var binder = [CPTableContentBinder getBinding:@"content" forObject:self],
3623  content = [binder content],
3624  rowContent = [content objectAtIndex:aRow];
3625 
3626  [dataView setObjectValue:rowContent];
3627 }
3628 
3632 - (void)_layoutViewsForRowIndexes:(CPIndexSet)rowIndexes columnIndexes:(CPIndexSet)columnIndexes
3633 {
3634  [self _enumerateViewsInRows:rowIndexes columns:columnIndexes usingBlock:function(view, row, column, stop)
3635  {
3636  [view setFrame:[self frameOfDataViewAtColumn:column row:row]];
3637  }];
3638 }
3639 
3643 - (CPView)_newDataViewForRow:(CPInteger)aRow tableColumn:(CPTableColumn)aTableColumn
3644 {
3645  var view = nil;
3646 
3647  if (_viewForTableColumnRowSelector)
3648  view = self.isa.objj_msgSend2(self, _viewForTableColumnRowSelector, aTableColumn, aRow);
3649 
3650  if (!view)
3651  {
3652  // For cell-based tables, use the dataView prototype identifier as view identifier
3653  // instead of column identifier because 1/ the column identifier may be nil
3654  // and 2/ the tableColumn dataView may change after the column identifier was set.
3655  var identifier;
3656 
3657  if (_isViewBased)
3658  {
3659  identifier = [aTableColumn identifier];
3660  view = [self makeViewWithIdentifier:identifier owner:_delegate];
3661  }
3662  else
3663  {
3664  identifier = [[aTableColumn dataView] UID];
3665  view = [self makeViewWithIdentifier:identifier owner:_delegate];
3666 
3667  if (!view)
3668  view = [aTableColumn _newDataView];
3669  }
3670 
3671  [view setIdentifier:identifier];
3672  }
3673 
3674  return view;
3675 }
3676 
3677 /*
3678  Returns a view with the specified identifier.
3679 
3680  @param identifier The view identifier. Must not be nil.
3681  @param owner The owner of the CIB that may be loaded and instantiated to create a new view with the particular identifier.
3682  @return A view for the row.
3683 
3684  @discussion
3685  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.
3686 
3687  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.
3688 */
3689 - (id)makeViewWithIdentifier:(CPString)anIdentifier owner:(id)anOwner
3690 {
3691  if (!anIdentifier)
3692  return nil;
3693 
3694  var view = nil,
3695  // See if we have some reusable views available
3696  reusableViews = _cachedDataViews[anIdentifier];
3697 
3698  if (reusableViews && reusableViews.length)
3699  view = reusableViews.pop();
3700  // Otherwise see if we have a view in the cib with this identifier
3701  else if (_isViewBased)
3702  view = [self _unarchiveViewWithIdentifier:anIdentifier owner:anOwner];
3703 
3704  return view;
3705 }
3706 
3719 - (id)viewAtColumn:(CPInteger)column row:(CPInteger)row makeIfNecessary:(BOOL)makeIfNecessary
3720 {
3721  if (row > (_numberOfRows - 1))
3722  [CPException raise:CPInvalidArgumentException
3723  reason:@"Row " + row + " out of row range [0-" + (_numberOfRows - 1) + "] for rowViewAtRow:createIfNeeded:"];
3724 
3725  if (column > (NUMBER_OF_COLUMNS() - 1))
3726  [CPException raise:CPInvalidArgumentException
3727  reason:@"Column " + column + " out of row range [0-" + (NUMBER_OF_COLUMNS ()- 1) + "] for rowViewAtRow:createIfNeeded:"];
3728 
3729  var dataViewsForRow = _dataViewsForRows[row],
3730  tableColumn = _tableColumns[column],
3731  tableColumnUID = [tableColumn UID],
3732  view = dataViewsForRow ? dataViewsForRow[tableColumnUID] : nil;
3733 
3734  if (!makeIfNecessary)
3735  return view || nil;
3736 
3737  if (!view)
3738  {
3739  if (!dataViewsForRow)
3740  {
3741  dataViewsForRow = {};
3742  _dataViewsForRows[row] = dataViewsForRow;
3743  }
3744 
3745  // Here we will add this view to the tableView and add it to the exposedRows adn columns.
3746  // The view will be deleted if necessary during the next run loop cycle of the table view
3747  // This is how cocoa works
3748  view = [self preparedViewAtColumn:column row:row];
3749 
3750  if ([view superview] !== self)
3751  [self addSubview:view];
3752 
3753  dataViewsForRow[tableColumnUID] = view;
3754 
3755  [_exposedRows addIndex:row];
3756  [_exposedColumns addIndex:column];
3757 
3758  // Make sure to layout the tableView again
3759  [self setNeedsLayout];
3760  }
3761 
3762  return view;
3763 }
3764 
3768 - (CPView)_unarchiveViewWithIdentifier:(CPString)anIdentifier owner:(id)anOwner
3769 {
3770  var cib = [_archivedDataViews objectForKey:anIdentifier];
3771 
3772  if (!cib && !_unavailable_custom_cibs[anIdentifier])
3773  {
3774  var bundle = anOwner ? [CPBundle bundleForClass:[anOwner class]] : [CPBundle mainBundle];
3775  cib = [[CPCib alloc] initWithCibNamed:anIdentifier bundle:bundle];
3776  }
3777 
3778  if (!cib)
3779  {
3780  _unavailable_custom_cibs[anIdentifier] = YES;
3781  return nil;
3782  }
3783 
3784  var objects = [],
3785  load = [cib instantiateCibWithOwner:anOwner topLevelObjects:objects];
3786 
3787  if (!load)
3788  return nil;
3789 
3790  var count = objects.length;
3791 
3792  while (count--)
3793  {
3794  var obj = objects[count];
3795 
3796  if ([obj isKindOfClass:[CPView class]])
3797  {
3798  [obj setIdentifier:anIdentifier];
3799  [obj setAutoresizingMask:CPViewNotSizable];
3800 
3801  return obj;
3802  }
3803  }
3804 
3805  return nil;
3806 }
3807 
3808 - (void)_updateIsViewBased
3809 {
3810  if ([self _delegateRespondsToViewForTableColumn])
3811  _viewForTableColumnRowSelector = @selector(_sendDelegateViewForTableColumn:row:);
3812  else if ([self _delegateRespondsToDataViewForTableColumn])
3813  _viewForTableColumnRowSelector = @selector(_sendDelegateDataViewForTableColumn:row:);
3814 
3815  _isViewBased = (_viewForTableColumnRowSelector !== nil || _archivedDataViews !== nil);
3816 }
3817 
3833 - (void)enumerateAvailableViewsUsingBlock:(Function/*CPView *dataView, CPInteger row, CPInteger column*, @ref stop*/)handler
3834 {
3835  [self reloadData];
3836 
3837  [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
3838 
3839  [self _enumerateViewsInRows:_exposedRows columns:_exposedColumns usingBlock:handler];
3840 }
3841 
3846 - (void)_enumerateViewsInRows:(CPIndexSet)rowIndexes columns:(CPIndexSet)columnIndexes usingBlock:(Function/*CPView dataView, CPInteger row, CPInteger column, @ref stop*/)handler
3847 {
3848  [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
3849 
3850  [rowIndexes enumerateIndexesUsingBlock:function(rowIndex, stopRow)
3851  {
3852  var dataViewsForRow = _dataViewsForRows[rowIndex];
3853 
3854  if (!dataViewsForRow)
3855  return;
3856 
3857  var row = rowIndex;
3858 
3859  [columnIndexes enumerateIndexesUsingBlock:function(columnIndex, stopCol)
3860  {
3861  var tableColumnUID = [_tableColumns[columnIndex] UID],
3862  view = dataViewsForRow[tableColumnUID];
3863 
3864  if (view)
3865  handler(view, row, columnIndex, stopCol);
3866 
3867  if (stopCol())
3868  stopRow(YES);
3869  }];
3870  }];
3871 }
3872 
3873 - (void)_enumerateViewsInRows:(CPIndexSet)rowIndexes tableColumns:(CPArray)tableColumns usingBlock:(Function/*CPView dataView, CPInteger row, CPtableColumn tableColumn, CPInteger column, @ref stop*/)handler
3874 {
3875  [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
3876 
3877  [rowIndexes enumerateIndexesUsingBlock:function(rowIndex, stopRow)
3878  {
3879  var dataViewsForRow = _dataViewsForRows[rowIndex];
3880 
3881  if (!dataViewsForRow)
3882  return;
3883 
3884  var row = rowIndex;
3885 
3886  [tableColumns enumerateObjectsUsingBlock:function(tableColumn, idx, stopCol)
3887  {
3888  var tableColumnUID = [tableColumn UID],
3889  view = dataViewsForRow[tableColumnUID];
3890 
3891  if (view)
3892  handler(view, row, tableColumn, stopCol);
3893 
3894  if (stopCol())
3895  stopRow(YES);
3896  }];
3897  }];
3898 }
3899 
3903 - (void)_enqueueReusableDataView:(CPView)aDataView
3904 {
3905  if (!aDataView)
3906  return;
3907 
3908  // FIXME: yuck!
3909  var identifier = [aDataView identifier];
3910 
3911  if ([aDataView hasThemeState:CPThemeStateSelectedDataView])
3912  [aDataView unsetThemeState:CPThemeStateSelectedDataView];
3913 
3914  if (!_cachedDataViews[identifier])
3915  _cachedDataViews[identifier] = [aDataView];
3916  else
3917  _cachedDataViews[identifier].push(aDataView);
3918 }
3919 
3924 - (void)setFrameSize:(CGSize)aSize
3925 {
3926  [super setFrameSize:aSize];
3927 
3928  if (_headerView)
3929  [_headerView setFrameSize:CGSizeMake(CGRectGetWidth([self frame]), CGRectGetHeight([_headerView frame]))];
3930 
3931  _exposedRect = nil;
3932 }
3933 
3937 - (void)setFrameOrigin:(CGPoint)aPoint
3938 {
3939  [super setFrameOrigin:aPoint];
3940 
3941  _exposedRect = nil;
3942 }
3943 
3947 - (void)setBoundsOrigin:(CGPoint)aPoint
3948 {
3949  [super setBoundsOrigin:aPoint];
3950 
3951  _exposedRect = nil;
3952 }
3953 
3957 - (void)setBoundsSize:(CGSize)aSize
3958 {
3959  [super setBoundsSize:aSize];
3960 
3961  _exposedRect = nil;
3962 }
3963 
3967 - (void)setNeedsDisplay:(BOOL)aFlag
3968 {
3969  [super setNeedsDisplay:aFlag];
3970  [_tableDrawView setNeedsDisplay:aFlag];
3971 
3972  [[self headerView] setNeedsDisplay:YES];
3973 }
3974 
3978 - (void)setNeedsLayout
3979 {
3980  [super setNeedsLayout];
3981  [[self headerView] setNeedsLayout];
3982 }
3983 
3987 - (BOOL)_isFocused
3988 {
3989  return [[self window] isKeyWindow] && ([[self window] firstResponder] === self || _editingRow !== CPNotFound);
3990 }
3991 
3995 - (void)_drawRect:(CGRect)aRect
3996 {
3997  // FIX ME: All three of these methods will likely need to be rewritten for 1.0
3998  // We've got grid drawing in highlightSelection and crap everywhere.
3999 
4000  var exposedRect = [self exposedRect];
4001 
4002  [self drawBackgroundInClipRect:exposedRect];
4003  [self highlightSelectionInClipRect:exposedRect];
4004  [self drawGridInClipRect:exposedRect];
4005 
4006  if (_implementsCustomDrawRow)
4007  [self _drawRows:_exposedRows clipRect:exposedRect];
4008 }
4009 
4015 - (void)drawBackgroundInClipRect:(CGRect)aRect
4016 {
4017  if (!_usesAlternatingRowBackgroundColors)
4018  return;
4019 
4020  var rowColors = [self alternatingRowBackgroundColors],
4021  colorCount = [rowColors count];
4022 
4023  if (colorCount === 0)
4024  return;
4025 
4026  var context = [[CPGraphicsContext currentContext] graphicsPort];
4027 
4028  if (colorCount === 1)
4029  {
4030  CGContextSetFillColor(context, rowColors[0]);
4031  CGContextFillRect(context, aRect);
4032 
4033  return;
4034  }
4035 
4036  var exposedRows = [self _exposedRowsInRect:aRect],
4037  firstRow = FLOOR(exposedRows.location / colorCount) * colorCount,
4038  lastRow = CPMaxRange(exposedRows) - 1,
4039  colorIndex = 0,
4040  groupRowRects = [];
4041 
4042  //loop through each color so we only draw once for each color
4043  while (colorIndex < colorCount)
4044  {
4045  CGContextBeginPath(context);
4046 
4047  for (var row = firstRow + colorIndex; row <= lastRow; row += colorCount)
4048  {
4049  // if it's not a group row draw it otherwise we draw it later
4050  if (![_groupRows containsIndex:row])
4051  CGContextAddRect(context, CGRectIntersection(aRect, [self _rectOfRow:row checkRange:NO]));
4052  else
4053  groupRowRects.push(CGRectIntersection(aRect, [self _rectOfRow:row checkRange:NO]));
4054  }
4055 
4056  CGContextClosePath(context);
4057 
4058  CGContextSetFillColor(context, rowColors[colorIndex]);
4059  CGContextFillPath(context);
4060 
4061  colorIndex++;
4062  }
4063 
4064  [self _drawGroupRowsForRects:groupRowRects];
4065 }
4066 
4071 - (void)drawGridInClipRect:(CGRect)aRect
4072 {
4073  var context = [[CPGraphicsContext currentContext] graphicsPort],
4074  gridStyleMask = [self gridStyleMask],
4075  lineThickness = [self currentValueForThemeAttribute:@"grid-line-thickness"];
4076 
4078  return;
4079 
4080  CGContextBeginPath(context);
4081 
4082  if (gridStyleMask & CPTableViewSolidHorizontalGridLineMask)
4083  {
4084  var exposedRows = [self _exposedRowsInRect:aRect],
4085  row = exposedRows.location,
4086  lastRow = CPMaxRange(exposedRows) - 1,
4087  rowY = -lineThickness / 2,
4088  minX = CGRectGetMinX(aRect),
4089  maxX = CGRectGetMaxX(aRect);
4090 
4091  for (; row <= lastRow; ++row)
4092  {
4093  // grab each row rect and add the top and bottom lines
4094  var rowRect = [self _rectOfRow:row checkRange:NO],
4095  rowY = CGRectGetMaxY(rowRect) - lineThickness / 2;
4096 
4097  CGContextMoveToPoint(context, minX, rowY);
4098  CGContextAddLineToPoint(context, maxX, rowY);
4099  }
4100 
4101  if (_rowHeight > 0.0)
4102  {
4103  var rowHeight = FULL_ROW_HEIGHT(),
4104  totalHeight = CGRectGetMaxY(aRect) - lineThickness / 2;
4105 
4106  while (rowY < totalHeight)
4107  {
4108  rowY += rowHeight;
4109 
4110  CGContextMoveToPoint(context, minX, rowY);
4111  CGContextAddLineToPoint(context, maxX, rowY);
4112  }
4113  }
4114  }
4115 
4116  if (gridStyleMask & CPTableViewSolidVerticalGridLineMask)
4117  {
4118  var exposedColumnIndexes = [self columnIndexesInRect:aRect],
4119  columnsArray = [];
4120 
4121  [exposedColumnIndexes getIndexes:columnsArray maxCount:-1 inIndexRange:nil];
4122 
4123  var columnArrayIndex = 0,
4124  columnArrayCount = columnsArray.length,
4125  minY = CGRectGetMinY(aRect),
4126  maxY = CGRectGetMaxY(aRect);
4127 
4128  for (; columnArrayIndex < columnArrayCount; ++columnArrayIndex)
4129  {
4130  var columnRect = [self rectOfColumn:columnsArray[columnArrayIndex]],
4131  columnX = CGRectGetMaxX(columnRect) - lineThickness / 2;
4132 
4133  CGContextMoveToPoint(context, columnX, minY);
4134  CGContextAddLineToPoint(context, columnX, maxY);
4135  }
4136  }
4137 
4138  CGContextClosePath(context);
4139  CGContextSetStrokeColor(context, [self gridColor]);
4140  CGContextSetLineWidth(context, lineThickness);
4141  CGContextStrokePath(context);
4142 }
4143 
4149 - (void)highlightSelectionInClipRect:(CGRect)aRect
4150 {
4151  if (_selectionHighlightStyle === CPTableViewSelectionHighlightStyleNone)
4152  return;
4153 
4154  var context = [[CPGraphicsContext currentContext] graphicsPort],
4155  indexes = [],
4156  rectSelector = @selector(rectOfRow:);
4157 
4158  if ([_selectedRowIndexes count] >= 1)
4159  {
4160  var exposedRows = [CPIndexSet indexSetWithIndexesInRange:[self rowsInRect:aRect]],
4161  firstRow = [exposedRows firstIndex],
4162  exposedRange = CPMakeRange(firstRow, [exposedRows lastIndex] - firstRow + 1);
4163 
4164  [_selectedRowIndexes getIndexes:indexes maxCount:-1 inIndexRange:exposedRange];
4165  }
4166  else if ([_selectedColumnIndexes count] >= 1)
4167  {
4168  rectSelector = @selector(rectOfColumn:);
4169 
4170  var exposedColumns = [self columnIndexesInRect:aRect],
4171  firstColumn = [exposedColumns firstIndex],
4172  exposedRange = CPMakeRange(firstColumn, [exposedColumns lastIndex] - firstColumn + 1);
4173 
4174  [_selectedColumnIndexes getIndexes:indexes maxCount:-1 inIndexRange:exposedRange];
4175  }
4176 
4177  var count,
4178  count2 = count = [indexes count];
4179 
4180  if (!count)
4181  return;
4182 
4183  var drawGradient = (CPFeatureIsCompatible(CPHTMLCanvasFeature) && _selectionHighlightStyle === CPTableViewSelectionHighlightStyleSourceList && [_selectedRowIndexes count] >= 1),
4184  deltaHeight = 0.5 * (_gridStyleMask & CPTableViewSolidHorizontalGridLineMask),
4185  focused = [self _isFocused];
4186 
4187  CGContextBeginPath(context);
4188 
4189  if (drawGradient)
4190  {
4191  var gradientCache = focused ? [self selectionGradientColors] : [self unfocusedSelectionGradientColors],
4192  topLineColor = [gradientCache objectForKey:CPSourceListTopLineColor],
4193  bottomLineColor = [gradientCache objectForKey:CPSourceListBottomLineColor],
4194  gradientColor = [gradientCache objectForKey:CPSourceListGradient];
4195  }
4196 
4197  var normalSelectionHighlightColor = focused ? [self selectionHighlightColor] : [self unfocusedSelectionHighlightColor];
4198 
4199  // don't do these lookups if there are no group rows
4200  if ([_groupRows count])
4201  {
4202  var topGroupLineColor = [CPColor colorWithCalibratedWhite:212.0 / 255.0 alpha:1.0],
4203  bottomGroupLineColor = [CPColor colorWithCalibratedWhite:185.0 / 255.0 alpha:1.0],
4204  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);
4205  }
4206 
4207  while (count--)
4208  {
4209  var currentIndex = indexes[count],
4210  rowRect = CGRectIntersection(self.isa.objj_msgSend1(self, rectSelector, currentIndex), aRect);
4211 
4212  // group rows get the same highlight style as other rows if they're source list...
4213  if (!drawGradient)
4214  var shouldUseGroupGradient = [_groupRows containsIndex:currentIndex];
4215 
4216  if (drawGradient || shouldUseGroupGradient)
4217  {
4218  var minX = CGRectGetMinX(rowRect),
4219  minY = CGRectGetMinY(rowRect),
4220  maxX = CGRectGetMaxX(rowRect),
4221  maxY = CGRectGetMaxY(rowRect) - deltaHeight;
4222 
4223  if (!drawGradient)
4224  {
4225  //If there is no source list gradient we need to close the selection path and fill it now
4226  [normalSelectionHighlightColor setFill];
4227  CGContextClosePath(context);
4228  CGContextFillPath(context);
4229  CGContextBeginPath(context);
4230  }
4231 
4232  CGContextAddRect(context, rowRect);
4233 
4234  CGContextDrawLinearGradient(context, (shouldUseGroupGradient) ? gradientGroupColor : gradientColor, rowRect.origin, CGPointMake(minX, maxY), 0);
4235 
4236  CGContextBeginPath(context);
4237  CGContextMoveToPoint(context, minX, minY + .5);
4238  CGContextAddLineToPoint(context, maxX, minY + .5);
4239  CGContextSetStrokeColor(context, (shouldUseGroupGradient) ? topGroupLineColor : topLineColor);
4240  CGContextStrokePath(context);
4241 
4242  CGContextBeginPath(context);
4243  CGContextMoveToPoint(context, minX, maxY - .5);
4244  CGContextAddLineToPoint(context, maxX, maxY - .5);
4245  CGContextSetStrokeColor(context, (shouldUseGroupGradient) ? bottomGroupLineColor : bottomLineColor);
4246  CGContextStrokePath(context);
4247  }
4248  else
4249  {
4250  var radius = [self currentValueForThemeAttribute:@"selection-radius"];
4251 
4252  if (radius > 0)
4253  {
4254  var minX = CGRectGetMinX(rowRect),
4255  maxX = CGRectGetMaxX(rowRect),
4256  minY = CGRectGetMinY(rowRect),
4257  maxY = CGRectGetMaxY(rowRect);
4258 
4259  CGContextMoveToPoint(context, minX + radius, minY);
4260  CGContextAddArcToPoint(context, maxX, minY, maxX, minY + radius, radius);
4261  CGContextAddArcToPoint(context, maxX, maxY, maxX - radius, maxY, radius);
4262  CGContextAddArcToPoint(context, minX, maxY, minX, maxY - radius, radius);
4263  CGContextAddArcToPoint(context, minX, minY, minX + radius, minY, radius);
4264  }
4265  else
4266  CGContextAddRect(context, rowRect);
4267  }
4268  }
4269 
4270  CGContextClosePath(context);
4271 
4272  if (!drawGradient)
4273  {
4274  [normalSelectionHighlightColor setFill];
4275  CGContextFillPath(context);
4276  }
4277 
4278  CGContextBeginPath(context);
4279 
4280  var gridStyleMask = [self gridStyleMask];
4281 
4282  for (var i = 0; i < count2; i++)
4283  {
4284  var rect = self.isa.objj_msgSend1(self, rectSelector, indexes[i]),
4285  minX = CGRectGetMinX(rect) - 0.5,
4286  maxX = CGRectGetMaxX(rect) - 0.5,
4287  minY = CGRectGetMinY(rect) - 0.5,
4288  maxY = CGRectGetMaxY(rect) - 0.5;
4289 
4290  if ([_selectedRowIndexes count] >= 1 && gridStyleMask & CPTableViewSolidVerticalGridLineMask)
4291  {
4292  var exposedColumns = [self columnIndexesInRect:aRect],
4293  exposedColumnIndexes = [],
4294  firstExposedColumn = [exposedColumns firstIndex],
4295  exposedRange = CPMakeRange(firstExposedColumn, [exposedColumns lastIndex] - firstExposedColumn + 1);
4296 
4297  [exposedColumns getIndexes:exposedColumnIndexes maxCount:-1 inIndexRange:exposedRange];
4298 
4299  var exposedColumnCount = [exposedColumnIndexes count];
4300 
4301  for (var c = firstExposedColumn; c < exposedColumnCount; c++)
4302  {
4303  var colRect = [self rectOfColumn:exposedColumnIndexes[c]],
4304  colX = CGRectGetMaxX(colRect) + 0.5;
4305 
4306  CGContextMoveToPoint(context, colX, minY);
4307  CGContextAddLineToPoint(context, colX, maxY);
4308  }
4309  }
4310 
4311  //if the row after the current row is not selected then there is no need to draw the bottom grid line white.
4312  if ([indexes containsObject:indexes[i] + 1])
4313  {
4314  CGContextMoveToPoint(context, minX, maxY);
4315  CGContextAddLineToPoint(context, maxX, maxY);
4316  }
4317  }
4318 
4319  CGContextClosePath(context);
4320  CGContextSetStrokeColor(context, [self currentValueForThemeAttribute:@"highlighted-grid-color"]);
4321  CGContextStrokePath(context);
4322 }
4323 
4329 - (void)_drawGroupRowsForRects:(CPArray)rects
4330 {
4331  if ((CPFeatureIsCompatible(CPHTMLCanvasFeature) && _selectionHighlightStyle === CPTableViewSelectionHighlightStyleSourceList) || !rects.length)
4332  return;
4333 
4334  var context = [[CPGraphicsContext currentContext] graphicsPort],
4335  i = rects.length;
4336 
4337  CGContextBeginPath(context);
4338 
4339  var gradientCache = [self selectionGradientColors],
4340  topLineColor = [CPColor colorWithHexString:"d3d3d3"],
4341  bottomLineColor = [CPColor colorWithHexString:"bebebd"],
4342  gradientColor = CGGradientCreateWithColorComponents(CGColorSpaceCreateDeviceRGB(), [220.0 / 255.0, 220.0 / 255.0, 220.0 / 255.0, 1.0,
4343  199.0 / 255.0, 199.0 / 255.0, 199.0 / 255.0, 1.0], [0, 1], 2),
4344  drawGradient = YES;
4345 
4346  while (i--)
4347  {
4348  var rowRect = rects[i];
4349 
4350  CGContextAddRect(context, rowRect);
4351 
4352  if (drawGradient)
4353  {
4354  var minX = CGRectGetMinX(rowRect),
4355  minY = CGRectGetMinY(rowRect),
4356  maxX = CGRectGetMaxX(rowRect),
4357  maxY = CGRectGetMaxY(rowRect);
4358 
4359  CGContextDrawLinearGradient(context, gradientColor, rowRect.origin, CGPointMake(minX, maxY), 0);
4360 
4361  CGContextBeginPath(context);
4362  CGContextMoveToPoint(context, minX, minY);
4363  CGContextAddLineToPoint(context, maxX, minY);
4364  CGContextSetStrokeColor(context, topLineColor);
4365  CGContextStrokePath(context);
4366 
4367  CGContextBeginPath(context);
4368  CGContextMoveToPoint(context, minX, maxY);
4369  CGContextAddLineToPoint(context, maxX, maxY - 1);
4370  CGContextSetStrokeColor(context, bottomLineColor);
4371  CGContextStrokePath(context);
4372  }
4373  }
4374 }
4375 
4379 - (void)_drawRows:(CPIndexSet)rowsIndexes clipRect:(CGRect)clipRect
4380 {
4381  var row = [rowsIndexes firstIndex];
4382 
4383  while (row !== CPNotFound)
4384  {
4385  [self drawRow:row clipRect:CGRectIntersection(clipRect, [self rectOfRow:row])];
4386  row = [rowsIndexes indexGreaterThanIndex:row];
4387  }
4388 }
4389 
4396 - (void)drawRow:(CPInteger)row clipRect:(CGRect)rect
4397 {
4398  // This method does currently nothing in cappuccino. Can be overridden by subclasses.
4399 
4400 }
4401 
4405 - (void)layoutSubviews
4406 {
4407  [self load];
4408 }
4409 
4413 - (void)viewWillMoveToSuperview:(CPView)aView
4414 {
4415  if ([aView isKindOfClass:[CPClipView class]])
4416  {
4417  _observedClipView = aView;
4418  }
4419  else
4420  {
4421  [self _stopObservingClipView];
4422  _observedClipView = nil;
4423  }
4424 
4425  [super viewWillMoveToSuperview:aView];
4426 }
4427 
4431 - (void)superviewBoundsChanged:(CPNotification)aNotification
4432 {
4433  _exposedRect = nil;
4434 
4435  [self setNeedsDisplay:YES];
4436  [self setNeedsLayout];
4437 }
4438 
4442 - (void)superviewFrameChanged:(CPNotification)aNotification
4443 {
4444  _exposedRect = nil;
4445 
4446  [self tile];
4447 }
4448 
4449 /*
4450  @ignore
4451 */
4452 - (BOOL)tracksMouseOutsideOfFrame
4453 {
4454  return YES;
4455 }
4456 
4457 /*
4458  @ignore
4459 */
4460 - (BOOL)startTrackingAt:(CGPoint)aPoint
4461 {
4462  // Try to become the first responder, but if we can't, that's okay.
4463  [[self window] makeFirstResponder:self];
4464 
4465  var row = [self rowAtPoint:aPoint];
4466 
4467  _clickedRow = row;
4468  _clickedColumn = [self columnAtPoint:aPoint];
4469 
4470  // If the user clicks outside a row then deselect everything.
4471  if (row < 0 && _allowsEmptySelection)
4472  {
4473  if ([self _sendDelegateSelectionShouldChangeInTableView])
4474  {
4475  var indexSet = [self _sendDelegateSelectionIndexesForProposedSelection:[CPIndexSet indexSet]];
4476 
4477  [self _noteSelectionIsChanging];
4478  [self selectRowIndexes:indexSet byExtendingSelection:NO];
4479  }
4480  }
4481 
4482  if ([self mouseDownFlags] & CPShiftKeyMask)
4483  _selectionAnchorRow = (ABS([_selectedRowIndexes firstIndex] - row) < ABS([_selectedRowIndexes lastIndex] - row)) ?
4484  [_selectedRowIndexes firstIndex] : [_selectedRowIndexes lastIndex];
4485  else
4486  _selectionAnchorRow = row;
4487 
4488  //set ivars for startTrackingPoint and time...
4489  _startTrackingPoint = aPoint;
4490  _startTrackingTimestamp = new Date();
4491 
4492  if ([self _dataSourceRespondsToSetObjectValueForTableColumnRow])
4493  _trackingPointMovedOutOfClickSlop = NO;
4494 
4495  // if the table has drag support then we use mouseUp to select a single row.
4496  // otherwise it uses mouse down.
4497  if (row >= 0 && !([self _dataSourceRespondsToWriteRowsWithIndexesToPasteboard]))
4498  [self _updateSelectionWithMouseAtRow:row];
4499 
4500  return YES;
4501 }
4502 
4506 - (CPMenu)menuForEvent:(CPEvent)theEvent
4507 {
4508  if (!([self _delegateRespondsToMenuForTableColumnRow]))
4509  return [super menuForEvent:theEvent];
4510 
4511  var location = [self convertPoint:[theEvent locationInWindow] fromView:nil],
4512  row = [self rowAtPoint:location],
4513  column = [self columnAtPoint:location],
4514  tableColumn = [[self tableColumns] objectAtIndex:column];
4515 
4516  return [self _sendDelegateMenuForTableColumn:tableColumn row:row];
4517 }
4518 
4519 /*
4520  @ignore
4521 */
4522 - (void)trackMouse:(CPEvent)anEvent
4523 {
4524  // Prevent CPControl from eating the mouse events when we are in a drag session
4525  if (![_draggedRowIndexes count])
4526  {
4527  [self autoscroll:anEvent];
4528  [super trackMouse:anEvent];
4529  }
4530  else
4531  [CPApp sendEvent:anEvent];
4532 
4533  if ([anEvent type] == CPLeftMouseUp)
4534  {
4535  _clickedRow = CPNotFound;
4536  _clickedColumn = CPNotFound;
4537  }
4538 }
4539 
4540 /*
4541  @ignore
4542 */
4543 - (BOOL)continueTracking:(CGPoint)lastPoint at:(CGPoint)aPoint
4544 {
4545  var row = [self rowAtPoint:aPoint];
4546 
4547  _clickedRow = row;
4548  _clickedColumn = [self columnAtPoint:aPoint];
4549 
4550  // begin the drag is the datasource lets us, we've move at least +-3px vertical or horizontal,
4551  // or we're dragging from selected rows and we haven't begun a drag session
4552  if (!_isSelectingSession && [self _dataSourceRespondsToWriteRowsWithIndexesToPasteboard])
4553  {
4554  if (row >= 0 && (ABS(_startTrackingPoint.x - aPoint.x) > 3 || (_verticalMotionCanDrag && ABS(_startTrackingPoint.y - aPoint.y) > 3)) ||
4555  ([_selectedRowIndexes containsIndex:row]))
4556  {
4557  if ([_selectedRowIndexes containsIndex:row])
4558  _draggedRowIndexes = [[CPIndexSet alloc] initWithIndexSet:_selectedRowIndexes];
4559  else
4560  _draggedRowIndexes = [CPIndexSet indexSetWithIndex:row];
4561 
4562  //ask the datasource for the data
4563  var pboard = [CPPasteboard pasteboardWithName:CPDragPboard];
4564 
4565  if ([self canDragRowsWithIndexes:_draggedRowIndexes atPoint:aPoint] && [self _sendDataSourceWriteRowsWithIndexes:_draggedRowIndexes toPasteboard:pboard])
4566  {
4567  var currentEvent = [CPApp currentEvent],
4568  offset = CGPointMakeZero(),
4569  tableColumns = [_tableColumns objectsAtIndexes:_exposedColumns];
4570 
4571  // We deviate from the default Cocoa implementation here by asking for a view in stead of an image
4572  // We support both, but the view preferred over the image because we can mimic the rows we are dragging
4573  // by re-creating the data views for the dragged rows
4574  var view = [self dragViewForRowsWithIndexes:_draggedRowIndexes
4575  tableColumns:tableColumns
4576  event:currentEvent
4577  offset:offset];
4578 
4579  if (!view)
4580  {
4581  var image = [self dragImageForRowsWithIndexes:_draggedRowIndexes
4582  tableColumns:tableColumns
4583  event:currentEvent
4584  offset:offset];
4585  view = [[CPImageView alloc] initWithFrame:CGRectMake(0, 0, [image size].width, [image size].height)];
4586  [view setImage:image];
4587  }
4588 
4589  var bounds = [view bounds],
4590  firstRowFrame = [self frameOfDataViewAtColumn:0 row:[_draggedRowIndexes firstIndex]],
4591  viewLocation = CGPointMake(aPoint.x - CGRectGetWidth(bounds) / 2 + offset.x, aPoint.y - CGRectGetHeight(bounds) / 2 + offset.y + CGRectGetMinY(firstRowFrame));
4592 
4593  [self dragView:view at:viewLocation offset:CGPointMakeZero() event:[CPApp currentEvent] pasteboard:pboard source:self slideBack:YES];
4594  _startTrackingPoint = nil;
4595 
4596  return NO;
4597  }
4598 
4599  // The delegate disallowed the drag so clear the dragged row indexes
4600  _draggedRowIndexes = [CPIndexSet indexSet];
4601  }
4602  else if (ABS(_startTrackingPoint.x - aPoint.x) < 5 && ABS(_startTrackingPoint.y - aPoint.y) < 5)
4603  return YES;
4604  }
4605 
4606  _isSelectingSession = YES;
4607  if (row >= 0 && row !== _lastTrackedRowIndex)
4608  {
4609  _lastTrackedRowIndex = row;
4610  [self _updateSelectionWithMouseAtRow:row];
4611  }
4612 
4613  if ([self _dataSourceRespondsToSetObjectValueForTableColumnRow]
4614  && !_trackingPointMovedOutOfClickSlop)
4615  {
4616  var CLICK_SPACE_DELTA = 5.0; // Stolen from AppKit/Platform/DOM/CPPlatformWindow+DOM.j
4617  if (ABS(aPoint.x - _startTrackingPoint.x) > CLICK_SPACE_DELTA
4618  || ABS(aPoint.y - _startTrackingPoint.y) > CLICK_SPACE_DELTA)
4619  {
4620  _trackingPointMovedOutOfClickSlop = YES;
4621  }
4622  }
4623 
4624  return YES;
4625 }
4626 
4630 - (void)stopTracking:(CGPoint)lastPoint at:(CGPoint)aPoint mouseIsUp:(BOOL)mouseIsUp
4631 {
4632  _isSelectingSession = NO;
4633 
4634  var CLICK_TIME_DELTA = 1000,
4635  columnIndex = -1,
4636  column,
4637  rowIndex,
4638  shouldEdit = YES;
4639 
4640  _clickedRow = [self rowAtPoint:aPoint];
4641  _clickedColumn = [self columnAtPoint:aPoint];
4642 
4643  if ([self _dataSourceRespondsToWriteRowsWithIndexesToPasteboard])
4644  {
4645  rowIndex = _clickedRow;
4646 
4647  if (rowIndex !== -1)
4648  {
4649  if ([_draggedRowIndexes count] > 0)
4650  {
4651  _draggedRowIndexes = [CPIndexSet indexSet];
4652  return;
4653  }
4654  // if the table has drag support then we use mouseUp to select a single row.
4655  _previouslySelectedRowIndexes = [_selectedRowIndexes copy];
4656  [self _updateSelectionWithMouseAtRow:rowIndex];
4657  }
4658  }
4659 
4660  // Accept either tableView:setObjectValue:forTableColumn:row: delegate method, or a binding.
4661  if (!_isViewBased && mouseIsUp
4662  && !_trackingPointMovedOutOfClickSlop
4663  && ([[CPApp currentEvent] clickCount] > 1)
4664  && ([self _dataSourceRespondsToSetObjectValueForTableColumnRow]
4665  || [self infoForBinding:@"content"]))
4666  {
4667  columnIndex = _clickedColumn;
4668 
4669  if (columnIndex !== -1)
4670  {
4671  column = _tableColumns[columnIndex];
4672 
4673  if ([column isEditable])
4674  {
4675  rowIndex = [self rowAtPoint:aPoint];
4676 
4677  if (rowIndex !== -1)
4678  {
4679  if ([self _sendDelegateShouldEditTableColumn:column row:rowIndex])
4680  {
4681  [self editColumn:columnIndex row:rowIndex withEvent:[CPApp currentEvent] select:YES];
4682  return;
4683  }
4684  }
4685  }
4686  }
4687 
4688  } //end of editing conditional
4689 
4690  //double click actions
4691  if ([[CPApp currentEvent] clickCount] === 2 && _doubleAction)
4692  [self sendAction:_doubleAction to:_target];
4693 }
4694 
4695 /*
4696  @ignore
4697 */
4698 - (CPDragOperation)draggingEntered:(id)sender
4699 {
4700  var location = [self convertPoint:[sender draggingLocation] fromView:nil],
4701  dropOperation = [self _proposedDropOperationAtPoint:location],
4702  row = [self _proposedRowAtPoint:location];
4703 
4704  if (_retargetedDropRow !== nil)
4705  row = _retargetedDropRow;
4706 
4707  var draggedTypes = [self registeredDraggedTypes],
4708  count = [draggedTypes count],
4709  i = 0;
4710 
4711  for (; i < count; i++)
4712  {
4713  if ([[[sender draggingPasteboard] types] containsObject:[draggedTypes objectAtIndex: i]])
4714  return [self _sendDataSourceValidateDrop:sender proposedRow:row proposedDropOperation:dropOperation];
4715  }
4716 
4717  return CPDragOperationNone;
4718 }
4719 
4720 /*
4721  @ignore
4722 */
4723 - (void)draggingExited:(id)sender
4724 {
4725  [_dropOperationFeedbackView removeFromSuperview];
4726 }
4727 
4728 /*
4729  @ignore
4730 */
4731 - (void)draggingEnded:(id)sender
4732 {
4733  [self _draggingEnded];
4734 }
4735 
4739 - (void)_draggingEnded
4740 {
4741  _retargetedDropOperation = nil;
4742  _retargetedDropRow = nil;
4743  _draggedRowIndexes = [CPIndexSet indexSet];
4744  [_dropOperationFeedbackView removeFromSuperview];
4745 }
4746 
4747 /*
4748  @ignore
4749 */
4750 - (BOOL)wantsPeriodicDraggingUpdates
4751 {
4752  return YES;
4753 }
4754 
4755 /*
4756  @ignore
4757 */
4758 - (CPTableViewDropOperation)_proposedDropOperationAtPoint:(CGPoint)theDragPoint
4759 {
4760  if (_retargetedDropOperation !== nil)
4761  return _retargetedDropOperation;
4762 
4763  var row = [self _proposedRowAtPoint:theDragPoint],
4764  rowRect = [self rectOfRow:row];
4765 
4766  // If there is no (the default) or too little inter-cell spacing we create some room for the CPTableViewDropAbove indicator
4767  // This probably doesn't work if the row height is smaller than or around 5.0
4768  if ([self intercellSpacing].height < 5.0)
4769  rowRect = CGRectInset(rowRect, 0.0, 5.0 - [self intercellSpacing].height);
4770 
4771  // If the altered row rect contains the drag point we show the drop on
4772  // We don't show the drop on indicator if we are dragging below the last row
4773  // in that case we always want to show the drop above indicator
4774  if (CGRectContainsPoint(rowRect, theDragPoint) && row < _numberOfRows)
4775  return CPTableViewDropOn;
4776 
4777  return CPTableViewDropAbove;
4778 }
4779 
4780 /*
4781  @ignore
4782 */
4783 - (CPInteger)_proposedRowAtPoint:(CGPoint)dragPoint
4784 {
4785  var row = [self rowAtPoint:dragPoint],
4786  // Determine if the mouse is currently closer to this row or the row below it
4787  lowerRow = row + 1,
4788  rect = [self rectOfRow:row],
4789  bottomPoint = CGRectGetMaxY(rect),
4790  bottomThirty = bottomPoint - ((bottomPoint - CGRectGetMinY(rect)) * 0.3),
4791  numberOfRows = [self numberOfRows];
4792 
4793  if (row < 0)
4794  row = (CGRectGetMaxY(rect) < dragPoint.y) ? numberOfRows : row;
4795  else if (dragPoint.y > MAX(bottomThirty, bottomPoint - 6))
4796  row = lowerRow;
4797 
4798  row = MIN(numberOfRows, row);
4799 
4800  return row;
4801 }
4802 
4806 - (CGRect)_rectForDropHighlightViewOnRow:(CPInteger)theRowIndex
4807 {
4808  if (theRowIndex >= [self numberOfRows])
4809  theRowIndex = [self numberOfRows] - 1;
4810 
4811  return [self _rectOfRow:theRowIndex checkRange:NO];
4812 }
4813 
4817 - (CGRect)_rectForDropHighlightViewBetweenUpperRow:(CPInteger)theUpperRowIndex andLowerRow:(CPInteger)theLowerRowIndex offset:(CGPoint)theOffset
4818 {
4819  if (theLowerRowIndex > [self numberOfRows])
4820  theLowerRowIndex = [self numberOfRows];
4821 
4822  return [self _rectOfRow:theLowerRowIndex checkRange:NO];
4823 }
4824 
4828 - (CPDragOperation)draggingUpdated:(id)sender
4829 {
4830  _retargetedDropRow = nil;
4831  _retargetedDropOperation = nil;
4832 
4833  var location = [self convertPoint:[sender draggingLocation] fromView:nil],
4834  dropOperation = [self _proposedDropOperationAtPoint:location],
4835  numberOfRows = [self numberOfRows],
4836  row = [self _proposedRowAtPoint:location],
4837  dragOperation = [self _sendDataSourceValidateDrop:sender proposedRow:row proposedDropOperation:dropOperation];
4838 
4839  if (_retargetedDropRow !== nil)
4840  row = _retargetedDropRow;
4841 
4842  if (_retargetedDropOperation !== nil)
4843  dropOperation = _retargetedDropOperation;
4844 
4845  if (dropOperation === CPTableViewDropOn && row >= numberOfRows)
4846  row = numberOfRows - 1;
4847 
4848  var rect = CGRectMakeZero();
4849 
4850  if (row === -1)
4851  rect = [self exposedRect];
4852  else if (dropOperation === CPTableViewDropAbove)
4853  rect = [self _rectForDropHighlightViewBetweenUpperRow:row - 1 andLowerRow:row offset:location];
4854  else
4855  rect = [self _rectForDropHighlightViewOnRow:row];
4856 
4857  [_dropOperationFeedbackView setDropOperation:row !== -1 ? dropOperation : CPDragOperationNone];
4858  [_dropOperationFeedbackView setHidden:(dragOperation == CPDragOperationNone)];
4859  [_dropOperationFeedbackView setFrame:rect];
4860  [_dropOperationFeedbackView setCurrentRow:row];
4861  [self addSubview:_dropOperationFeedbackView];
4862 
4863  return dragOperation;
4864 }
4865 
4866 /*
4867  @ignore
4868 */
4869 - (BOOL)prepareForDragOperation:(id)sender
4870 {
4871  // FIX ME: is there anything else that needs to happen here?
4872  // actual validation is called in draggingUpdated:
4873  [_dropOperationFeedbackView removeFromSuperview];
4874 
4875  return [self _dataSourceRespondsToValidateDropProposedRowProposedDropOperation];
4876 }
4877 
4878 /*
4879  @ignore
4880 */
4881 - (BOOL)performDragOperation:(id)sender
4882 {
4883  var location = [self convertPoint:[sender draggingLocation] fromView:nil],
4884  operation = [self _proposedDropOperationAtPoint:location],
4885  row = _retargetedDropRow;
4886 
4887  if (row === nil)
4888  row = [self _proposedRowAtPoint:location];
4889 
4890  return [self _sendDataSourceAcceptDrop:sender row:row dropOperation:operation];
4891 }
4892 
4893 /*
4894  @ignore
4895 */
4896 - (void)concludeDragOperation:(id)sender
4897 {
4898  [self _reloadDataViews];
4899 }
4900 
4901 /*
4902  @ignore
4903  We're using this because we drag views instead of images so we can get the rows themselves to actually drag.
4904 */
4905 - (void)draggedView:(CPImage)aView endedAt:(CGPoint)aLocation operation:(CPDragOperation)anOperation
4906 {
4907  [self _draggingEnded];
4908  [self draggedImage:aView endedAt:aLocation operation:anOperation];
4909 }
4910 
4914 - (void)_updateSelectionWithMouseAtRow:(CPInteger)aRow
4915 {
4916  //check to make sure the row exists
4917  if (aRow < 0)
4918  return;
4919 
4920  var newSelection,
4921  shouldExtendSelection = NO;
4922 
4923  // If cmd/ctrl was held down XOR the old selection with the proposed selection
4924  if ([self mouseDownFlags] & (CPCommandKeyMask | CPControlKeyMask | CPAlternateKeyMask))
4925  {
4926  if ([_selectedRowIndexes containsIndex:aRow])
4927  {
4928  newSelection = [_selectedRowIndexes copy];
4929 
4930  [newSelection removeIndex:aRow];
4931  }
4932  else if (_allowsMultipleSelection)
4933  {
4934  newSelection = [_selectedRowIndexes copy];
4935 
4936  [newSelection addIndex:aRow];
4937  }
4938  else
4939  newSelection = [CPIndexSet indexSetWithIndex:aRow];
4940  }
4941  else if (_allowsMultipleSelection)
4942  {
4943  if (_selectionAnchorRow == CPNotFound)
4944  _selectionAnchorRow = [self numberOfRows] - 1;
4945 
4946  newSelection = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(MIN(aRow, _selectionAnchorRow), ABS(aRow - _selectionAnchorRow) + 1)];
4947  shouldExtendSelection = [self mouseDownFlags] & CPShiftKeyMask &&
4948  ((_lastSelectedRow == [_selectedRowIndexes lastIndex] && aRow > _lastSelectedRow) ||
4949  (_lastSelectedRow == [_selectedRowIndexes firstIndex] && aRow < _lastSelectedRow));
4950  }
4951  else if (aRow >= 0 && aRow < _numberOfRows)
4952  newSelection = [CPIndexSet indexSetWithIndex:aRow];
4953  else
4954  newSelection = [CPIndexSet indexSet];
4955 
4956  if ([newSelection isEqualToIndexSet:_selectedRowIndexes])
4957  return;
4958 
4959  if (![self _sendDelegateSelectionShouldChangeInTableView])
4960  return;
4961 
4962  newSelection = [self _sendDelegateSelectionIndexesForProposedSelection:newSelection];
4963 
4964  if (![self _delegateRespondsToSelectionIndexesForProposedSelection] && [self _delegateRespondsToShouldSelectRow])
4965  {
4966  var indexArray = [];
4967 
4968  [newSelection getIndexes:indexArray maxCount:-1 inIndexRange:nil];
4969 
4970  var indexCount = indexArray.length;
4971 
4972  while (indexCount--)
4973  {
4974  var index = indexArray[indexCount];
4975 
4976  if (![self _sendDelegateShouldSelectRow:index])
4977  [newSelection removeIndex:index];
4978  }
4979 
4980  // as per cocoa
4981  if ([newSelection count] === 0)
4982  return;
4983  }
4984 
4985  // if empty selection is not allowed and the new selection has nothing selected, abort
4986  if (!_allowsEmptySelection && [newSelection count] === 0)
4987  return;
4988 
4989  if ([newSelection isEqualToIndexSet:_selectedRowIndexes])
4990  return;
4991 
4992  [self _noteSelectionIsChanging];
4993  [self selectRowIndexes:newSelection byExtendingSelection:shouldExtendSelection];
4994 
4995  _lastSelectedRow = [newSelection containsIndex:aRow] ? aRow : [newSelection lastIndex];
4996 }
4997 
5001 - (void)_noteSelectionIsChanging
5002 {
5004  postNotificationName:CPTableViewSelectionIsChangingNotification
5005  object:self
5006  userInfo:nil];
5007 
5008  if (_implementedDelegateMethods & CPTableViewDelegate_tableViewSelectionIsChanging_)
5009  [_delegate tableViewSelectionIsChanging:[[CPNotification alloc] initWithName:CPTableViewSelectionIsChangingNotification object:self userInfo:nil]];
5010 }
5011 
5015 - (void)_noteSelectionDidChange
5016 {
5018  postNotificationName:CPTableViewSelectionDidChangeNotification
5019  object:self
5020  userInfo:nil];
5021 
5022  if (_implementedDelegateMethods & CPTableViewDelegate_tableViewSelectionDidChange_)
5023  [_delegate tableViewSelectionDidChange:[[CPNotification alloc] initWithName:CPTableViewSelectionDidChangeNotification object:self userInfo:nil]];
5024 }
5025 
5029 - (void)becomeKeyWindow
5030 {
5031  [self setNeedsDisplay:YES];
5032 }
5033 
5037 - (void)resignKeyWindow
5038 {
5039  [self setNeedsDisplay:YES];
5040 }
5041 
5045 - (BOOL)acceptsFirstResponder
5046 {
5047  return YES;
5048 }
5049 
5053 - (BOOL)needsPanelToBecomeKey
5054 {
5055  return YES;
5056 }
5057 
5061 - (CPView)hitTest:(CGPoint)aPoint
5062 {
5063  var hit = [super hitTest:aPoint];
5064 
5065  if ([[CPApp currentEvent] type] !== CPLeftMouseDown)
5066  return hit;
5067 
5068  return [self _hitTest:hit];
5069 }
5070 
5071 - (id)_hitTest:(CPView)aView
5072 {
5073  var column,
5074  row;
5075 
5076  if ([aView acceptsFirstResponder])
5077  {
5078  [self getColumn:@ref(column) row:@ref(row) forView:aView];
5079 
5080  if (![self isRowSelected:row])
5081  {
5082  if (_selectionHighlightStyle == CPTableViewSelectionHighlightStyleNone)
5083  return aView;
5084 
5085  return self;
5086  }
5087  }
5088  else if (!_isViewBased && [aView isKindOfClass:[CPControl class]] && ![aView isKindOfClass:[CPTextField class]])
5089  [aView addObserver:self forKeyPath:@"objectValue" options:CPKeyValueObservingOptionOld | CPKeyValueObservingOptionNew context:"editing"];
5090 
5091  return aView;
5092 }
5093 
5094 - (void)_removeObservers
5095 {
5096  if (!_isObserving)
5097  return;
5098 
5099  [self _stopObservingClipView];
5100  [super _removeObservers];
5101 }
5102 
5103 - (void)_addObservers
5104 {
5105  if (_isObserving)
5106  return;
5107 
5108  [self _startObservingClipView];
5109  [super _addObservers];
5110 }
5111 
5116 - (void)viewWillMoveToWindow:(CPWindow)aWindow
5117 {
5118  [super viewWillMoveToWindow:aWindow];
5119 
5120  [self _stopObservingFirstResponderForWindow:[self window]];
5121 
5122  if (aWindow)
5123  [self _startObservingFirstResponderForWindow:aWindow];
5124 }
5125 
5126 - (void)_startObservingClipView
5127 {
5128  if (!_observedClipView)
5129  return;
5130 
5131  var defaultCenter = [CPNotificationCenter defaultCenter];
5132 
5133  [_observedClipView setPostsFrameChangedNotifications:YES];
5134  [_observedClipView setPostsBoundsChangedNotifications:YES];
5135 
5136  [defaultCenter addObserver:self
5137  selector:@selector(superviewFrameChanged:)
5138  name:CPViewFrameDidChangeNotification
5139  object:_observedClipView];
5140 
5141  [defaultCenter addObserver:self
5142  selector:@selector(superviewBoundsChanged:)
5143  name:CPViewBoundsDidChangeNotification
5144  object:_observedClipView];
5145 }
5146 
5147 - (void)_stopObservingClipView
5148 {
5149  if (!_observedClipView)
5150  return;
5151 
5152  var defaultCenter = [CPNotificationCenter defaultCenter];
5153 
5154  [defaultCenter removeObserver:self
5155  name:CPViewFrameDidChangeNotification
5156  object:_observedClipView];
5157 
5158  [defaultCenter removeObserver:self
5159  name:CPViewBoundsDidChangeNotification
5160  object:_observedClipView];
5161 }
5162 
5163 - (void)_startObservingFirstResponderForWindow:(CPWindow)aWindow
5164 {
5165  [[CPNotificationCenter defaultCenter] addObserver:self selector:@selector(_firstResponderDidChange:) name:_CPWindowDidChangeFirstResponderNotification object:aWindow];
5166 }
5167 
5168 - (void)_stopObservingFirstResponderForWindow:(CPWindow)aWindow
5169 {
5170  [[CPNotificationCenter defaultCenter] removeObserver:self name:_CPWindowDidChangeFirstResponderNotification object:aWindow];
5171 }
5172 
5173 - (void)_firstResponderDidChange:(CPNotification)aNotification
5174 {
5175  var responder = [[self window] firstResponder],
5176  column,
5177  row;
5178 
5179  [self getColumn:@ref(column) row:@ref(row) forView:responder];
5180 
5181  _editingRow = row;
5182  _editingColumn = column;
5183 
5184  // We want to keep the 'First Responder' theme state for the table view as a whole, even when a subview is being edited.
5185  // This makes sure the theming effects of a focused table remain in effect even as cells are being edited in it.
5186  // Check if the firstResponder is outside the tableview:
5187  if (responder !== self && _editingRow == CPNotFound && _editingColumn == CPNotFound)
5188  [self _notifyViewDidResignFirstResponder];
5189  else
5190  [self _notifyViewDidBecomeFirstResponder];
5191 
5192  // This is for cell-based tables only. In view-based mode, we do not change the textfield apprearence during an edit.
5193  if (!_isViewBased && _editingRow !== CPNotFound && [responder isKindOfClass:[CPTextField class]] && [responder isEditable] && [responder superview] == self)
5194  {
5195  [responder setBezeled:YES];
5196  [self _registerForEndEditingNote:responder];
5197  }
5198 }
5199 
5200 - (void)_registerForEndEditingNote:(CPView)aTextField
5201 {
5202  [[CPNotificationCenter defaultCenter] addObserver:self selector:@selector(_textFieldEditingDidEnd:) name:CPControlTextDidEndEditingNotification object:aTextField];
5203 }
5204 
5205 - (void)_unregisterForEndEditingNote:(CPView)aTextField
5206 {
5207  [[CPNotificationCenter defaultCenter] removeObserver:self name:CPControlTextDidEndEditingNotification object:aTextField];
5208 }
5209 
5210 - (void)_textFieldEditingDidEnd:(CPNotification)aNote
5211 {
5212  // FIXME: When you edit a text field and hit enter without any text modification, the CPControlTextDidEndEditingNotification
5213  // is NOT sent. This is a bug in CPTextField or CPControl according to cocoa.
5214  var textField = [aNote object];
5215 
5216  [self _unregisterForEndEditingNote:textField];
5217 
5218  if (!_isViewBased)
5219  {
5220  [self _setEditingState:NO forView:textField];
5221  [self _commitDataViewObjectValue:textField forColumn:_editingColumn andRow:_editingRow];
5222  }
5223  else
5224  [textField setBezeled:NO];
5225 
5226  [self _resignFirstResponderWithoutSendingAction:textField];
5227 }
5228 
5229 - (void)_resignFirstResponderWithoutSendingAction:(CPView)aView
5230 {
5231  var action = [self _disableActionIfExists:aView];
5232 
5233  [[self window] makeFirstResponder:self];
5234 
5235  if (action)
5236  [aView setAction:action];
5237 }
5238 
5239 - (void)_resignEditedView
5240 {
5241  var view = [[self window] firstResponder];
5242 
5243  if ([view respondsToSelector:@selector(selectText:)])
5244  [view selectText:nil];
5245 
5246  if (!_isViewBased)
5247  {
5248  [self _unregisterForEndEditingNote:view];
5249  [self _setEditingState:NO forView:view];
5250  }
5251 
5252  [self _resignFirstResponderWithoutSendingAction:view];
5253 }
5254 
5255 - (SEL)_disableActionIfExists:(CPView)aView
5256 {
5257  // TODO: We disable action to prevent it from beeing sent twice when we resign the FR inside a textEndEditing notification.
5258  // Check if this is due to a bug in CPTextField.
5259  var action = nil;
5260  if ([aView respondsToSelector:@selector(action)] && (action = [aView action]))
5261  [aView setAction:nil];
5262 
5263  return action;
5264 }
5265 
5271 - (void)_commitDataViewObjectValue:(id)aDataView forColumn:(CPInteger)column andRow:(CPInteger)row
5272 {
5273  var editingTableColumn = _tableColumns[column];
5274 
5275  if (_implementedDataSourceMethods & CPTableViewDataSource_tableView_setObjectValue_forTableColumn_row_)
5276  [_dataSource tableView:self setObjectValue:[aDataView objectValue] forTableColumn:editingTableColumn row:row];
5277 
5278  // Allow the column binding to do a reverse set. Note that we do this even if the data source method above
5279  // is implemented.
5280  [editingTableColumn _reverseSetDataView:aDataView forRow:row];
5281 
5282  if (row !== CPNotFound && column !== CPNotFound)
5283  [self _reloadDataForRowIndexes:[CPIndexSet indexSetWithIndex:row] columnIndexes:[CPIndexSet indexSetWithIndex:column]];
5284 }
5285 
5286 - (void)_setEditingState:(BOOL)editingState forView:(CPView)aView
5287 {
5288  if ([aView respondsToSelector:@selector(setEditable:)])
5289  [aView setEditable:editingState];
5290 
5291  if ([aView respondsToSelector:@selector(setSelectable:)])
5292  [aView setSelectable:editingState];
5293 
5294  if ([aView isKindOfClass:[CPTextField class]])
5295  [aView setBezeled:editingState];
5296 }
5297 
5307 - (void)editColumn:(CPInteger)columnIndex row:(CPInteger)rowIndex withEvent:(CPEvent)theEvent select:(BOOL)flag
5308 {
5309  if (![self isRowSelected:rowIndex])
5310  [[CPException exceptionWithName:@"Error" reason:@"Attempt to edit row " + rowIndex + " when not selected." userInfo:nil] raise];
5311 
5312  [self _reloadDataViewsSynchronously];
5313  [self scrollRowToVisible:rowIndex];
5314  [self scrollColumnToVisible:columnIndex];
5315 
5316  // TODO Do something with flag.
5317 
5318  _editingRow = rowIndex;
5319  _editingColumn = columnIndex;
5320 
5321  var editingTableColumnUID = [_tableColumns[_editingColumn] UID],
5322  editingView = _dataViewsForRows[_editingRow][editingTableColumnUID];
5323 
5324  [self _setEditingState:YES forView:editingView];
5325  [[self window] makeFirstResponder:editingView];
5326 }
5327 
5328 - (void)observeValueForKeyPath:(CPString)keyPath ofObject:(id)object change:(CPDictionary)change context:(void)context
5329 {
5330  if (context === "editing" && [object superview] === self)
5331  {
5332  [object removeObserver:self forKeyPath:keyPath];
5333 
5334  var row,
5335  column;
5336 
5337  [self getColumn:@ref(column) row:@ref(row) forView:object];
5338  [self _commitDataViewObjectValue:object forColumn:column andRow:row];
5339  }
5340 }
5341 
5345 - (CPInteger)editedColumn
5346 {
5347  return _editingColumn;
5348 }
5349 
5353 - (CPInteger)editedRow
5354 {
5355  return _editingRow;
5356 }
5357 
5361 - (void)keyDown:(CPEvent)anEvent
5362 {
5363  var character = [anEvent charactersIgnoringModifiers],
5364  modifierFlags = [anEvent modifierFlags];
5365 
5366  // Check for the key events manually, as opposed to waiting for CPWindow to sent the actual action message
5367  // in _processKeyboardUIKey:, because we might not want to handle the arrow events.
5368  if (character === CPUpArrowFunctionKey || character === CPDownArrowFunctionKey)
5369  {
5370  // We're not interested in the arrow keys if there are no rows.
5371  // Technically we should also not be interested if we can't scroll,
5372  // but Cocoa doesn't handle that situation either.
5373  if ([self numberOfRows] !== 0)
5374  {
5375  [self _moveSelectionWithEvent:anEvent upward:(character === CPUpArrowFunctionKey)];
5376 
5377  return;
5378  }
5379  }
5380  else if (character === CPDeleteCharacter || character === CPDeleteFunctionKey)
5381  {
5382  // Don't call super if the delegate is interested in the delete key
5383  if ([self _sendDelegateDeleteKeyPressed])
5384  return;
5385  }
5386 
5387  [super keyDown:anEvent];
5388 }
5389 
5395 - (BOOL)_selectionIsBroken
5396 {
5397  return [self selectedRowIndexes]._ranges.length !== 1;
5398 }
5399 
5405 - (void)_moveSelectionWithEvent:(CPEvent)theEvent upward:(BOOL)shouldGoUpward
5406 {
5407  if (![self _sendDelegateSelectionShouldChangeInTableView])
5408  return;
5409 
5410  var selectedIndexes = [self selectedRowIndexes];
5411 
5412  if ([selectedIndexes count] > 0)
5413  {
5414  var extend = (([theEvent modifierFlags] & CPShiftKeyMask) && _allowsMultipleSelection),
5415  i = [self selectedRow];
5416 
5417  if ([self _selectionIsBroken])
5418  {
5419  while ([selectedIndexes containsIndex:i])
5420  {
5421  shouldGoUpward ? i-- : i++;
5422  }
5423  _wasSelectionBroken = true;
5424  }
5425  else if (_wasSelectionBroken && ((shouldGoUpward && i !== [selectedIndexes firstIndex]) || (!shouldGoUpward && i !== [selectedIndexes lastIndex])))
5426  {
5427  shouldGoUpward ? i = [selectedIndexes firstIndex] - 1 : i = [selectedIndexes lastIndex];
5428  _wasSelectionBroken = false;
5429  }
5430  else
5431  {
5432  shouldGoUpward ? i-- : i++;
5433  }
5434  }
5435  else
5436  {
5437  var extend = NO;
5438  //no rows are currently selected
5439  if ([self numberOfRows] > 0)
5440  var i = shouldGoUpward ? [self numberOfRows] - 1 : 0; // if we select upward select the last row, otherwise select the first row
5441  }
5442 
5443  if (i >= [self numberOfRows] || i < 0)
5444  return;
5445 
5446  if ([self _delegateRespondsToSelectionIndexesForProposedSelection] || [self _delegateRespondsToShouldSelectRow])
5447  {
5448  var shouldSelect = !![[self _cleanUpSelectionRowIndexes:[CPIndexSet indexSetWithIndex:i]] count];
5449 
5450  /* If shouldSelect returns NO it means this row cannot be selected.
5451  The proper behaviour is to then try to see if the next/previous
5452  row(s) can be selected, until we hit the first one that can be.
5453  */
5454  while (!shouldSelect && (i < [self numberOfRows] && i > 0))
5455  {
5456  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.
5457  shouldSelect = !![[self _cleanUpSelectionRowIndexes:[CPIndexSet indexSetWithIndex:i]] count];
5458  }
5459 
5460  if (!shouldSelect)
5461  return;
5462  }
5463 
5464  // If we go upward and see that this row is already selected we should deselect the row below.
5465  if (extend && [selectedIndexes containsIndex:i])
5466  {
5467  // The row we're on is the last to be selected.
5468  var differedLastSelectedRow = i;
5469 
5470  // no remove the one before/after it
5471  shouldGoUpward ? i++ : i--;
5472 
5473  [selectedIndexes removeIndex:i];
5474 
5475  //we're going to replace the selection
5476  extend = NO;
5477  }
5478  else if (extend)
5479  {
5480  if ([selectedIndexes containsIndex:i])
5481  {
5482  i = shouldGoUpward ? [selectedIndexes firstIndex] -1 : [selectedIndexes lastIndex] + 1;
5483  i = MIN(MAX(i, 0), [self numberOfRows] - 1);
5484  }
5485 
5486  [selectedIndexes addIndex:i];
5487  var differedLastSelectedRow = i;
5488  }
5489  else
5490  {
5491  selectedIndexes = [CPIndexSet indexSetWithIndex:i];
5492  var differedLastSelectedRow = i;
5493  }
5494 
5495  selectedIndexes = [self _sendDelegateSelectionIndexesForProposedSelection:selectedIndexes];
5496 
5497  [self selectRowIndexes:selectedIndexes byExtendingSelection:extend];
5498 
5499  // we differ because selectRowIndexes: does its own thing which would set the wrong index
5500  _lastSelectedRow = differedLastSelectedRow;
5501 
5502  if (i !== CPNotFound)
5503  [self scrollRowToVisible:i];
5504 }
5505 
5506 @end
5507 
5508 
5510 
5515 - (BOOL)_dataSourceRespondsToObjectValueForTableColumn
5516 {
5517  return _implementedDataSourceMethods & CPTableViewDataSource_tableView_objectValueForTableColumn_row_;
5518 }
5519 
5524 - (BOOL)_dataSourceRespondsToWriteRowsWithIndexesToPasteboard
5525 {
5526  return _implementedDataSourceMethods & CPTableViewDataSource_tableView_writeRowsWithIndexes_toPasteboard_;
5527 }
5528 
5533 - (BOOL)_dataSourceRespondsToSetObjectValueForTableColumnRow
5534 {
5536 }
5537 
5542 - (BOOL)_dataSourceRespondsToValidateDropProposedRowProposedDropOperation
5543 {
5545 }
5546 
5551 - (BOOL)_dataSourceRespondsToNumberOfRowsinTableView
5552 {
5553  return _implementedDataSourceMethods & CPTableViewDataSource_numberOfRowsInTableView_;
5554 }
5555 
5561 - (int)_sendDataSourceNumberOfRowsInTableView
5562 {
5563  if (!(_implementedDataSourceMethods & CPTableViewDataSource_numberOfRowsInTableView_))
5564  return 0;
5565 
5566  return [_dataSource numberOfRowsInTableView:self];
5567 }
5568 
5574 - (id)_sendDataSourceObjectValueForTableColumn:(CPTableColumn)aTableColumn row:(CPInteger)aRowIndex
5575 {
5576  if (!(_implementedDataSourceMethods & CPTableViewDataSource_tableView_objectValueForTableColumn_row_))
5577  return nil;
5578 
5579  return [_dataSource tableView:self objectValueForTableColumn:aTableColumn row:aRowIndex];
5580 }
5581 
5586 - (void)_sendDataSourceSetObjectValue:(id)anObject forTableColumn:(CPTableColumn)aTableColumn row:(CPInteger)aRowIndex
5587 {
5588  if (!(_implementedDataSourceMethods & CPTableViewDataSource_tableView_setObjectValue_forTableColumn_row_))
5589  return;
5590 
5591  [_dataSource tableView:self setObjectValue:anObject forTableColumn:aTableColumn row:aRowIndex];
5592 }
5593 
5598 - (void)_sendDataSourceSortDescriptorsDidChange:(CPArray)descriptors
5599 {
5600  if (!(_implementedDataSourceMethods & CPTableViewDataSource_tableView_sortDescriptorsDidChange_))
5601  return;
5602 
5603  [_dataSource tableView:self sortDescriptorsDidChange:descriptors];
5604 }
5605 
5610 - (BOOL)_sendDataSourceAcceptDrop:(id)info row:(CPInteger)aRowIndex dropOperation:(CPTableViewDropOperation)operation
5611 {
5612  if (!(_implementedDataSourceMethods & CPTableViewDataSource_tableView_acceptDrop_row_dropOperation_))
5613  return NO;
5614 
5615  return [_dataSource tableView:self acceptDrop:info row:aRowIndex dropOperation:operation];
5616 }
5617 
5622 - (CPDragOperation)_sendDataSourceValidateDrop:(id)info proposedRow:(CPInteger)aRowIndex proposedDropOperation:(CPTableViewDropOperation)operation
5623 {
5625  return CPDragOperationNone;
5626 
5627  return [_dataSource tableView:self validateDrop:info proposedRow:aRowIndex proposedDropOperation:operation];
5628 }
5629 
5634 - (BOOL)_sendDataSourceWriteRowsWithIndexes:(CPIndexSet)rowIndexes toPasteboard:(CPPasteboard)pboard
5635 {
5636  if (!(_implementedDataSourceMethods & CPTableViewDataSource_tableView_writeRowsWithIndexes_toPasteboard_))
5637  return NO;
5638 
5639  return [_dataSource tableView:self writeRowsWithIndexes:rowIndexes toPasteboard:pboard];
5640 }
5641 
5642 /*
5643  This method is sent to the data source for convenience...
5644 */
5645 - (void)draggedImage:(CPImage)anImage endedAt:(CGPoint)aLocation operation:(CPDragOperation)anOperation
5646 {
5647  if ([_dataSource respondsToSelector:@selector(tableView:didEndDraggedImage:atPosition:operation:)])
5648  [_dataSource tableView:self didEndDraggedImage:anImage atPosition:aLocation operation:anOperation];
5649 }
5650 
5651 
5652 #pragma mark -
5653 #pragma mark DataSource methods to implement
5654 
5659 - (CPArray)_sendDataSourceNamesOfPromisedFilesDroppedAtDestination:(CPURL)dropDestination forDraggedRowsWithIndexes:(CPIndexSet)indexSet
5660 {
5662  return [];
5663 
5664  return [_dataSource tableView:self namesOfPromisedFilesDroppedAtDestination:dropDestination forDraggedRowsWithIndexes:indexSet];
5665 }
5666 
5667 @end
5668 
5669 
5671 
5676 - (BOOL)_delegateRespondsToDataViewForTableColumn
5677 {
5678  return _implementedDelegateMethods & CPTableViewDelegate_tableView_dataViewForTableColumn_row_;
5679 }
5680 
5685 - (BOOL)_delegateRespondsToViewForTableColumn
5686 {
5687  return _implementedDelegateMethods & CPTableViewDelegate_tableView_viewForTableColumn_row_;
5688 }
5689 
5694 - (BOOL)_delegateRespondsToShouldSelectRow
5695 {
5696  return _implementedDelegateMethods & CPTableViewDelegate_tableView_shouldSelectRow_;
5697 }
5698 
5703 - (BOOL)_delegateRespondsToSelectionShouldChangeInTableView
5704 {
5705  return _implementedDelegateMethods & CPTableViewDelegate_selectionShouldChangeInTableView_;
5706 }
5707 
5712 - (BOOL)_delegateRespondsToSelectionIndexesForProposedSelection
5713 {
5714  return _implementedDelegateMethods & CPTableViewDelegate_tableView_selectionIndexesForProposedSelection_;
5715 }
5716 
5721 - (BOOL)_delegateRespondsToMenuForTableColumnRow
5722 {
5723  return _implementedDelegateMethods & CPTableViewDelegate_tableViewMenuForTableColumn_row_;
5724 }
5725 
5730 - (void)_sendDelegateDidClickTableColumn:(CPInteger)column
5731 {
5732  if (_implementedDelegateMethods & CPTableViewDelegate_tableView_didClickTableColumn_)
5733  [_delegate tableView:self didClickTableColumn:_tableColumns[column]];
5734 }
5735 
5740 - (void)_sendDelegateDidDragTableColumn:(CPTableColumn)aTableColumn
5741 {
5742  if (_implementedDelegateMethods & CPTableViewDelegate_tableView_didDragTableColumn_)
5743  [_delegate tableView:self didDragTableColumn:aTableColumn];
5744 }
5745 
5750 - (void)_sendDelegateMouseDownInHeaderOfTableColumn:(CPInteger)column
5751 {
5752  if (_implementedDelegateMethods & CPTableViewDelegate_tableView_mouseDownInHeaderOfTableColumn_)
5753  [_delegate tableView:self mouseDownInHeaderOfTableColumn:_tableColumns[column]];
5754 }
5755 
5756 /*
5757  @ignore
5758  Call the delegate tableViewDeleteKeyPressed
5759 */
5760 - (BOOL)_sendDelegateDeleteKeyPressed
5761 {
5762  if ([_delegate respondsToSelector: @selector(tableViewDeleteKeyPressed:)])
5763  {
5764  [_delegate tableViewDeleteKeyPressed:self];
5765  return YES;
5766  }
5767 
5768  return NO;
5769 }
5770 
5775 - (BOOL)_sendDelegateSelectionShouldChangeInTableView
5776 {
5777  if (!(_implementedDelegateMethods & CPTableViewDelegate_selectionShouldChangeInTableView_))
5778  return YES;
5779 
5780  return [_delegate selectionShouldChangeInTableView:self];
5781 }
5782 
5787 - (BOOL)_sendDelegateIsGroupRow:(CPInteger)anIndex
5788 {
5789  if (!(_implementedDelegateMethods & CPTableViewDelegate_tableView_isGroupRow_))
5790  return NO;
5791 
5792  return [_delegate tableView:self isGroupRow:anIndex];
5793 }
5794 
5799 - (BOOL)_sendDelegateShouldSelectRow:(CPInteger)anIndex
5800 {
5801  if (!(_implementedDelegateMethods & CPTableViewDelegate_tableView_shouldSelectRow_))
5802  return YES;
5803 
5804  return [_delegate tableView:self shouldSelectRow:anIndex];
5805 }
5806 
5811 - (void)_sendDelegateWillDisplayView:(id)aCell forTableColumn:(CPTableColumn)aTableColumn row:(CPInteger)aRowIndex
5812 {
5813  if (!(_implementedDelegateMethods & CPTableViewDelegate_tableView_willDisplayView_forTableColumn_row_))
5814  return;
5815 
5816  [_delegate tableView:self willDisplayView:aCell forTableColumn:aTableColumn row:aRowIndex];
5817 }
5818 
5823 - (void)_sendDelegateWillRemoveView:(id)aCell forTableColumn:(CPTableColumn)aTableColumn row:(CPInteger)aRowIndex
5824 {
5825  if (!(_implementedDelegateMethods & CPTableViewDelegate_tableView_willRemoveView_forTableColumn_row_))
5826  return;
5827 
5828  [_delegate tableView:self willRemoveView:aCell forTableColumn:aTableColumn row:aRowIndex];
5829 }
5830 
5835 - (CPMenu)_sendDelegateMenuForTableColumn:(CPTableColumn)aTableColumn row:aRowIndex
5836 {
5837  if (!(_implementedDelegateMethods & CPTableViewDelegate_tableViewMenuForTableColumn_row_))
5838  return nil;
5839 
5840  return [_delegate tableView:self menuForTableColumn:aTableColumn row:aRowIndex];
5841 }
5842 
5843 /*
5844  @ignore
5845  Returns YES if the column at columnIndex can be reordered.
5846  It can be possible if column reordering is allowed and if the tableview
5847  delegate also accept the reordering
5848 */
5849 - (BOOL)_sendDelegateShouldReorderColumn:(CPInteger)columnIndex toColumn:(CPInteger)newColumnIndex
5850 {
5851  if ([self allowsColumnReordering] &&
5853  {
5854  return [_delegate tableView:self shouldReorderColumn:columnIndex toColumn:newColumnIndex];
5855  }
5856 
5857  return [self allowsColumnReordering];
5858 }
5859 
5864 - (float)_sendDelegateHeightOfRow:(CPInteger)anIndex
5865 {
5866  if (!(_implementedDelegateMethods & CPTableViewDelegate_tableView_heightOfRow_))
5867  return [self rowHeight];
5868 
5869  return [_delegate tableView:self heightOfRow:anIndex];
5870 }
5871 
5876 - (BOOL)_sendDelegateShouldEditTableColumn:(CPTableColumn)aTableColumn row:(CPInteger)aRowIndex
5877 {
5878  if (!(_implementedDelegateMethods & CPTableViewDelegate_tableView_shouldEditTableColumn_row_))
5879  return YES;
5880 
5881  return [_delegate tableView:self shouldEditTableColumn:aTableColumn row:aRowIndex];
5882 }
5883 
5888 - (CPIndexSet)_sendDelegateSelectionIndexesForProposedSelection:(CPIndexSet)anIndexSet
5889 {
5890  if (!(_implementedDelegateMethods & CPTableViewDelegate_tableView_selectionIndexesForProposedSelection_))
5891  return anIndexSet;
5892 
5893  return [_delegate tableView:self selectionIndexesForProposedSelection:anIndexSet];
5894 }
5895 
5900 - (CPView)_sendDelegateViewForTableColumn:(CPTableColumn)aTableColumn row:(CPInteger)aRowIndex
5901 {
5902  if (!(_implementedDelegateMethods & CPTableViewDelegate_tableView_viewForTableColumn_row_))
5903  return nil;
5904 
5905  return [_delegate tableView:self viewForTableColumn:aTableColumn row:aRowIndex];
5906 }
5907 
5912 - (CPView)_sendDelegateDataViewForTableColumn:(CPTableColumn)aTableColumn row:(CPInteger)aRowIndex
5913 {
5914  if (!(_implementedDelegateMethods & CPTableViewDelegate_tableView_dataViewForTableColumn_row_))
5915  return nil;
5916 
5917  return [_delegate tableView:self dataViewForTableColumn:aTableColumn row:aRowIndex];
5918 }
5919 
5920 
5921 #pragma mark -
5922 #pragma mark Delegate methods to implement
5923 
5928 - (BOOL)_sendDelegateShouldSelectTableColumn:(CPTableColumn)aTableColumn
5929 {
5930  if (!(_implementedDelegateMethods & CPTableViewDelegate_tableView_shouldSelectTableColumn_))
5931  return YES;
5932 
5933  return [_delegate tableView:self shouldSelectTableColumn:aTableColumn];
5934 }
5935 
5940 - (CPString)_sendDelegateToolTipForView:(id)aView rect:(CGRect)aRect tableColumn:(CPTableColumn)aTableColumn row:(CPInteger)aRowIndex mouseLocation:(CGPoint)aPoint
5941 {
5943  return nil;
5944 
5945  return [_delegate tableView:self toolTipForView:aView rect:aRect tableColumn:aTableColumn row:aRowIndex mouseLocation:aPoint];
5946 }
5947 
5952 - (BOOL)_sendDelegateShouldTrackView:(id)aView forTableColumn:(CPTableColumn)aTableColumn row:(CPInteger)aRowIndex
5953 {
5954  if (!(_implementedDelegateMethods & CPTableViewDelegate_tableView_shouldTrackView_forTableColumn_row_))
5955  return YES;
5956 
5957  return [_delegate tableView:self shouldTrackView:aView forTableColumn:aTableColumn row:aRowIndex];
5958 }
5959 
5964 - (BOOL)_sendDelegateShouldShowViewExpansionForTableColumn:(CPTableColumn)aTableColumn row:(CPInteger)aRowIndex
5965 {
5967  return YES;
5968 
5969  return [_delegate tableView:self shouldShowViewExpansionForTableColumn:aTableColumn row:aRowIndex];
5970 }
5971 
5976 - (BOOL)_sendDelegateShouldTypeSelectForEvent:(CPEvent)anEvent withCurrentSearchString:(CPString)aString
5977 {
5979  return NO;
5980 
5981  return [_delegate tableView:self shouldTypeSelectForEvent:anEvent withCurrentSearchString:aString];
5982 }
5983 
5988 - (CPString)_sendDelegateTypeSelectStringForTableColumn:(CPTableColumn)aTableColumn row:(CPInteger)aRowIndex
5989 {
5990  if (!(_implementedDelegateMethods & CPTableViewDelegate_tableView_typeSelectStringForTableColumn_row_))
5991  return nil;
5992 
5993  return [_delegate tableView:self typeSelectStringForTableColumn:aTableColumn row:aRowIndex];
5994 }
5995 
6000 - (int)_sendDelegateNextTypeSelectMatchFromRow:(CPInteger)aRowIndex toRow:(CPInteger)aSecondRowIndex forString:(CPString)aString
6001 {
6003  return -1;
6004 
6005  return [_delegate tableView:self nextTypeSelectMatchFromRow:aRowIndex toRow:aSecondRowIndex forString:aString];
6006 }
6007 
6008 @end
6009 
6010 @implementation CPTableView (Bindings)
6011 
6012 + (Class)_binderClassForBinding:(CPString)aBinding
6013 {
6014  if (aBinding == @"content")
6015  return [CPTableContentBinder class];
6016 
6017  return [super _binderClassForBinding:aBinding];
6018 }
6019 
6023 - (CPString)_replacementKeyPathForBinding:(CPString)aBinding
6024 {
6025  if (aBinding === @"selectionIndexes")
6026  return @"selectedRowIndexes";
6027 
6028  return [super _replacementKeyPathForBinding:aBinding];
6029 }
6030 
6034 - (void)_establishBindingsIfUnbound:(id)destination
6035 {
6036  if ([[self infoForBinding:@"content"] objectForKey:CPObservedObjectKey] !== destination)
6037  {
6038  [super bind:@"content" toObject:destination withKeyPath:@"arrangedObjects" options:nil];
6039  _contentBindingExplicitlySet = NO;
6040  }
6041 
6042  // If the content binding was set manually assume the user is taking manual control of establishing bindings.
6043  if (!_contentBindingExplicitlySet)
6044  {
6045  if ([[self infoForBinding:@"selectionIndexes"] objectForKey:CPObservedObjectKey] !== destination)
6046  [self bind:@"selectionIndexes" toObject:destination withKeyPath:@"selectionIndexes" options:nil];
6047 
6048  if ([[self infoForBinding:@"sortDescriptors"] objectForKey:CPObservedObjectKey] !== destination)
6049  [self bind:@"sortDescriptors" toObject:destination withKeyPath:@"sortDescriptors" options:nil];
6050  }
6051 }
6052 
6053 - (void)bind:(CPString)aBinding toObject:(id)anObject withKeyPath:(CPString)aKeyPath options:(CPDictionary)options
6054 {
6055  if (aBinding == @"content")
6056  _contentBindingExplicitlySet = YES;
6057 
6058  [super bind:aBinding toObject:anObject withKeyPath:aKeyPath options:options];
6059 }
6060 
6061 @end
6062 
6063 
6064 @implementation CPTableContentBinder : CPBinder
6065 {
6066  id _content;
6067 }
6068 
6069 - (void)setValueFor:(CPString)aBinding
6070 {
6071  var destination = [_info objectForKey:CPObservedObjectKey],
6072  keyPath = [_info objectForKey:CPObservedKeyPathKey];
6073 
6074  _content = [destination valueForKey:keyPath];
6075  // FIXME: reload data for all rows or just exposed rows ?
6076  [_source _reloadDataViews];
6077 }
6078 
6079 @end
6080 
6081 
6082 var CPTableViewDataSourceKey = @"CPTableViewDataSourceKey",
6083  CPTableViewDelegateKey = @"CPTableViewDelegateKey",
6084  CPTableViewHeaderViewKey = @"CPTableViewHeaderViewKey",
6085  CPTableViewTableColumnsKey = @"CPTableViewTableColumnsKey",
6086  CPTableViewRowHeightKey = @"CPTableViewRowHeightKey",
6087  CPTableViewIntercellSpacingKey = @"CPTableViewIntercellSpacingKey",
6088  CPTableViewSelectionHighlightStyleKey = @"CPTableViewSelectionHighlightStyleKey",
6089  CPTableViewMultipleSelectionKey = @"CPTableViewMultipleSelectionKey",
6090  CPTableViewEmptySelectionKey = @"CPTableViewEmptySelectionKey",
6091  CPTableViewColumnReorderingKey = @"CPTableViewColumnReorderingKey",
6092  CPTableViewColumnResizingKey = @"CPTableViewColumnResizingKey",
6093  CPTableViewColumnSelectionKey = @"CPTableViewColumnSelectionKey",
6094  CPTableViewColumnAutoresizingStyleKey = @"CPTableViewColumnAutoresizingStyleKey",
6095  CPTableViewGridColorKey = @"CPTableViewGridColorKey",
6096  CPTableViewGridStyleMaskKey = @"CPTableViewGridStyleMaskKey",
6097  CPTableViewUsesAlternatingBackgroundKey = @"CPTableViewUsesAlternatingBackgroundKey",
6098  CPTableViewAlternatingRowColorsKey = @"CPTableViewAlternatingRowColorsKey",
6099  CPTableViewCornerViewKey = @"CPTableViewCornerViewKey",
6100  CPTableViewAutosaveNameKey = @"CPTableViewAutosaveNameKey",
6101  CPTableViewArchivedReusableViewsKey = @"CPTableViewArchivedReusableViewsKey";
6102 
6103 @implementation CPTableView (CPCoding)
6104 
6105 - (id)initWithCoder:(CPCoder)aCoder
6106 {
6107  self = [super initWithCoder:aCoder];
6108 
6109  if (self)
6110  {
6111  //Configuring Behavior
6112  _allowsColumnReordering = [aCoder decodeBoolForKey:CPTableViewColumnReorderingKey];
6113  _allowsColumnResizing = [aCoder decodeBoolForKey:CPTableViewColumnResizingKey];
6114  _allowsMultipleSelection = [aCoder decodeBoolForKey:CPTableViewMultipleSelectionKey];
6115  _allowsEmptySelection = [aCoder decodeBoolForKey:CPTableViewEmptySelectionKey];
6116  _allowsColumnSelection = [aCoder decodeBoolForKey:CPTableViewColumnSelectionKey];
6117 
6118  //Setting Display Attributes
6119  _selectionHighlightStyle = [aCoder decodeIntForKey:CPTableViewSelectionHighlightStyleKey];
6120  _columnAutoResizingStyle = [aCoder decodeIntForKey:CPTableViewColumnAutoresizingStyleKey];
6121 
6122  _tableColumns = [aCoder decodeObjectForKey:CPTableViewTableColumnsKey] || [];
6123  [_tableColumns makeObjectsPerformSelector:@selector(setTableView:) withObject:self];
6124 
6125  _rowHeight = [aCoder decodeFloatForKey:CPTableViewRowHeightKey] || [self valueForThemeAttribute:@"default-row-height"];
6126  _intercellSpacing = [aCoder decodeSizeForKey:CPTableViewIntercellSpacingKey];
6127 
6128  if (CGSizeEqualToSize(_intercellSpacing, CGSizeMakeZero()))
6129  _intercellSpacing = CGSizeMake(3.0, 2.0);
6130 
6131  [self setGridColor:[aCoder decodeObjectForKey:CPTableViewGridColorKey]];
6132  _gridStyleMask = [aCoder decodeIntForKey:CPTableViewGridStyleMaskKey];
6133 
6134  _usesAlternatingRowBackgroundColors = [aCoder decodeObjectForKey:CPTableViewUsesAlternatingBackgroundKey];
6135  [self setAlternatingRowBackgroundColors:[aCoder decodeObjectForKey:CPTableViewAlternatingRowColorsKey]];
6136 
6137  _headerView = [aCoder decodeObjectForKey:CPTableViewHeaderViewKey];
6138  _cornerView = [aCoder decodeObjectForKey:CPTableViewCornerViewKey];
6139 
6140  [self setDataSource:[aCoder decodeObjectForKey:CPTableViewDataSourceKey]];
6141  [self setDelegate:[aCoder decodeObjectForKey:CPTableViewDelegateKey]];
6142 
6143  [self _init];
6144 
6145  if ([aCoder containsValueForKey:CPTableViewArchivedReusableViewsKey])
6146  _archivedDataViews = [aCoder decodeObjectForKey:CPTableViewArchivedReusableViewsKey];
6147 
6148  [self _updateIsViewBased];
6149 
6150  [self viewWillMoveToSuperview:[self superview]];
6151 
6152  // Do this as late as possible to make sure the tableview is fully configured
6153  [self setAutosaveName:[aCoder decodeObjectForKey:CPTableViewAutosaveNameKey]];
6154  }
6155 
6156  return self;
6157 }
6158 
6159 - (void)encodeWithCoder:(CPCoder)aCoder
6160 {
6161  [super encodeWithCoder:aCoder];
6162 
6163  // We do this in order to avoid encoding the _tableDrawView, which
6164  // should just automatically be created programmatically as needed.
6165  if (_tableDrawView)
6166  [_tableDrawView removeFromSuperview];
6167 
6168  [aCoder encodeObject:_dataSource forKey:CPTableViewDataSourceKey];
6169  [aCoder encodeObject:_delegate forKey:CPTableViewDelegateKey];
6170 
6171  [aCoder encodeFloat:_rowHeight forKey:CPTableViewRowHeightKey];
6172  [aCoder encodeSize:_intercellSpacing forKey:CPTableViewIntercellSpacingKey];
6173 
6174  [aCoder encodeInt:_selectionHighlightStyle forKey:CPTableViewSelectionHighlightStyleKey];
6175  [aCoder encodeInt:_columnAutoResizingStyle forKey:CPTableViewColumnAutoresizingStyleKey];
6176 
6177  [aCoder encodeBool:_allowsMultipleSelection forKey:CPTableViewMultipleSelectionKey];
6178  [aCoder encodeBool:_allowsEmptySelection forKey:CPTableViewEmptySelectionKey];
6179  [aCoder encodeBool:_allowsColumnReordering forKey:CPTableViewColumnReorderingKey];
6180  [aCoder encodeBool:_allowsColumnResizing forKey:CPTableViewColumnResizingKey];
6181  [aCoder encodeBool:_allowsColumnSelection forKey:CPTableViewColumnSelectionKey];
6182 
6183  [aCoder encodeObject:_tableColumns forKey:CPTableViewTableColumnsKey];
6184 
6185  [aCoder encodeObject:[self gridColor] forKey:CPTableViewGridColorKey];
6186  [aCoder encodeInt:_gridStyleMask forKey:CPTableViewGridStyleMaskKey];
6187 
6188  [aCoder encodeBool:_usesAlternatingRowBackgroundColors forKey:CPTableViewUsesAlternatingBackgroundKey];
6189  [aCoder encodeObject:[self alternatingRowBackgroundColors] forKey:CPTableViewAlternatingRowColorsKey];
6190 
6191  [aCoder encodeObject:_cornerView forKey:CPTableViewCornerViewKey];
6192  [aCoder encodeObject:_headerView forKey:CPTableViewHeaderViewKey];
6193 
6194  [aCoder encodeObject:_autosaveName forKey:CPTableViewAutosaveNameKey];
6195 
6196  if (_archivedDataViews)
6197  [aCoder encodeObject:_archivedDataViews forKey:CPTableViewArchivedReusableViewsKey];
6198 }
6199 
6200 @end
6201 
6202 @implementation _CPDropOperationDrawingView : CPView
6203 {
6204  unsigned dropOperation;
6208 }
6209 
6210 - (void)drawRect:(CGRect)aRect
6211 {
6212  if (tableView._destinationDragStyle === CPTableViewDraggingDestinationFeedbackStyleNone || isBlinking)
6213  return;
6214 
6215  var context = [[CPGraphicsContext currentContext] graphicsPort],
6216  borderRadius,
6217  borderColor,
6218  borderWidth,
6220 
6221  if (currentRow === -1)
6222  {
6223  borderColor = [tableView valueForThemeAttribute:@"dropview-on-border-color"];
6224  borderWidth = [tableView valueForThemeAttribute:@"dropview-on-border-width"];
6225 
6226  CGContextSetStrokeColor(context, borderColor);
6227  CGContextSetLineWidth(context, borderWidth);
6228  CGContextStrokeRect(context, [self bounds]);
6229  }
6230 
6231  else if (dropOperation === CPTableViewDropOn)
6232  {
6233  //if row is selected don't fill and stroke white
6234  var selectedRows = [tableView selectedRowIndexes],
6235  newRect = CGRectMake(aRect.origin.x + 2, aRect.origin.y + 2, aRect.size.width - 4, aRect.size.height - 5);
6236 
6237  if ([selectedRows containsIndex:currentRow])
6238  {
6239  borderRadius = [tableView valueForThemeAttribute:@"dropview-on-selected-border-radius"];
6240  borderColor = [tableView valueForThemeAttribute:@"dropview-on-selected-border-color"];
6241  borderWidth = [tableView valueForThemeAttribute:@"dropview-on-selected-border-width"];
6242  backgroundColor = [tableView valueForThemeAttribute:@"dropview-on-selected-background-color"];
6243  }
6244  else
6245  {
6246  borderRadius = [tableView valueForThemeAttribute:@"dropview-on-border-radius"];
6247  borderColor = [tableView valueForThemeAttribute:@"dropview-on-border-color"];
6248  borderWidth = [tableView valueForThemeAttribute:@"dropview-on-border-width"];
6249  backgroundColor = [tableView valueForThemeAttribute:@"dropview-on-background-color"];
6250  }
6251 
6252  CGContextSetStrokeColor(context, borderColor);
6253  CGContextSetLineWidth(context, borderWidth);
6255  CGContextFillRoundedRectangleInRect(context, newRect, borderRadius, YES, YES, YES, YES);
6256  CGContextStrokeRoundedRectangleInRect(context, newRect, borderRadius, YES, YES, YES, YES);
6257 
6258  }
6259  else if (dropOperation === CPTableViewDropAbove)
6260  {
6261  //reposition the view up a tad
6262  [self setFrameOrigin:CGPointMake(_frame.origin.x, _frame.origin.y - 8)];
6263 
6264  var selectedRows = [tableView selectedRowIndexes];
6265 
6266  if ([selectedRows containsIndex:currentRow - 1] || [selectedRows containsIndex:currentRow])
6267  {
6268  borderColor = [tableView valueForThemeAttribute:@"dropview-above-selected-border-color"];
6269  borderWidth = [tableView valueForThemeAttribute:@"dropview-above-selected-border-width"];
6270  }
6271  else
6272  {
6273  borderColor = [tableView valueForThemeAttribute:@"dropview-above-border-color"];
6274  borderWidth = [tableView valueForThemeAttribute:@"dropview-above-border-width"];
6275  }
6276 
6277  CGContextSetStrokeColor(context, borderColor);
6278  CGContextSetLineWidth(context, borderWidth);
6279  CGContextStrokeEllipseInRect(context, CGRectMake(aRect.origin.x + 4, aRect.origin.y + 4, 8, 8)); // circle
6280 
6281  CGContextBeginPath(context);
6282  CGContextMoveToPoint(context, 10, aRect.origin.y + 8);
6283  CGContextAddLineToPoint(context, aRect.size.width - aRect.origin.y - 8, aRect.origin.y + 8);
6284  CGContextStrokePath(context);
6285  }
6286 }
6287 
6288 - (void)blink
6289 {
6290  if (dropOperation !== CPTableViewDropOn)
6291  return;
6292 
6293  isBlinking = YES;
6294 
6295  var showCallback = function()
6296  {
6297  [self setHidden: NO];
6298  isBlinking = NO;
6299  };
6300 
6301  var hideCallback = function()
6302  {
6303  [self setHidden: YES];
6304  isBlinking = YES;
6305  };
6306 
6307  [self setHidden: YES];
6309  [CPTimer scheduledTimerWithTimeInterval:0.19 callback:hideCallback repeats:NO];
6310  [CPTimer scheduledTimerWithTimeInterval:0.27 callback:showCallback repeats:NO];
6311 }
6312 
6313 @end
6314 
6315 
6316 @implementation _CPColumnDragDrawingView : CPView
6317 {
6321 }
6322 
6323 - (void)drawRect:(CGRect)dirtyRect
6324 {
6325  var context = [[CPGraphicsContext currentContext] graphicsPort],
6326  columnRect = [tableView rectOfColumn:columnIndex],
6327  headerHeight = CGRectGetHeight([[tableView headerView] frame]),
6328  bounds = [columnClipView bounds],
6329  visibleRect = [tableView visibleRect],
6330  xScroll = CGRectGetMinX(visibleRect),
6331  yScroll = CGRectGetMinY(visibleRect);
6332 
6333  // Because we are sharing drawing code with regular table drawing,
6334  // we have to play a few tricks to fool the drawing code into thinking
6335  // our drag column is in the same place as the real column.
6336 
6337  // Shift the bounds origin to align with the column rect, and extend it vertically to ensure
6338  // it reaches the bottom of the tableView when scrolled.
6339  bounds.origin.x = CGRectGetMinX(columnRect) - xScroll;
6340  bounds.size.height += yScroll;
6341 
6342  // Fix up the CTM to account for the header and scroll
6343  CGContextTranslateCTM(context, -bounds.origin.x, headerHeight - yScroll);
6344 
6345  //[tableView drawBackgroundInClipRect:bounds];
6346 
6347  if (tableView._draggedColumnIsSelected)
6348  {
6349  CGContextSetFillColor(context, [tableView _isFocused] ? [tableView selectionHighlightColor] : [tableView unfocusedSelectionHighlightColor]);
6350  CGContextFillRect(context, bounds);
6351  }
6352  else
6353  [tableView highlightSelectionInClipRect:bounds];
6354 
6355  //[tableView _drawHorizontalGridInClipRect:bounds];
6356 
6357  var minX = CGRectGetMinX(bounds) + 0.5,
6358  maxX = CGRectGetMaxX(bounds) - 0.5;
6359 
6360  CGContextSetLineWidth(context, 1.0);
6361  CGContextSetAlpha(context, 1.0);
6362  CGContextSetStrokeColor(context, [tableView gridColor]);
6363 
6364  CGContextBeginPath(context);
6365 
6366  CGContextMoveToPoint(context, minX, CGRectGetMinY(bounds));
6367  CGContextAddLineToPoint(context, minX, CGRectGetMaxY(bounds));
6368 
6369  CGContextMoveToPoint(context, maxX, CGRectGetMinY(bounds));
6370  CGContextAddLineToPoint(context, maxX, CGRectGetMaxY(bounds));
6371 
6372  CGContextStrokePath(context);
6373 }
6374 
6375 @end
6376 
6377 @implementation CPTableCellView : CPView
6378 {
6379  id _objectValue;
6380 
6381  CPTextField _textField;
6382  CPImageView _imageView;
6383 }
6384 
6385 - (void)awakeFromCib
6386 {
6387  [super awakeFromCib];
6388  [self setThemeState:CPThemeStateTableDataView];
6389 }
6390 
6391 - (BOOL)setThemeState:(ThemeState)aState
6392 {
6393  [super setThemeState:aState];
6394  [self recursivelyPerformSelector:@selector(setThemeState:) withObject:aState startingFrom:self];
6395 }
6396 
6397 - (BOOL)unsetThemeState:(ThemeState)aState
6398 {
6399  [super unsetThemeState:aState];
6400  [self recursivelyPerformSelector:@selector(unsetThemeState:) withObject:aState startingFrom:self];
6401 }
6402 
6403 - (void)recursivelyPerformSelector:(SEL)selector withObject:(id)anObject startingFrom:(id)aView
6404 {
6405  [[aView subviews] enumerateObjectsUsingBlock:function(view, idx)
6406  {
6407  [view performSelector:selector withObject:anObject];
6408 
6409  if (![view isKindOfClass:[self class]]) // Avoid infinite loop if a subview is a CPTableCellView.
6410  [self recursivelyPerformSelector:selector withObject:anObject startingFrom:view];
6411  }];
6412 }
6413 
6415 {
6416  return "<" + [self className] + " 0x" + [CPString stringWithHash:[self UID]] + " identifier=" + [self identifier] + ">";
6417 }
6418 
6419 @end
6420 
6422 
6426 - (BOOL)disableAutomaticResizing
6427 {
6428  return _disableAutomaticResizing;
6429 }
6430 
6434 - (void)setDisableAutomaticResizing:(BOOL)aValue
6435 {
6436  _disableAutomaticResizing = aValue;
6437 }
6438 
6439 @end
6440 
6442 
6446 - (id)content
6447 {
6448  return _content;
6449 }
6450 
6454 - (void)setContent:(id)aValue
6455 {
6456  _content = aValue;
6457 }
6458 
6459 @end
6460 
6462 
6466 - (id)objectValue
6467 {
6468  return _objectValue;
6469 }
6470 
6474 - (void)setObjectValue:(id)aValue
6475 {
6476  _objectValue = aValue;
6477 }
6478 
6482 - (CPTextField)textField
6483 {
6484  return _textField;
6485 }
6486 
6490 - (void)setTextField:(CPTextField)aValue
6491 {
6492  _textField = aValue;
6493 }
6494 
6498 - (CPImageView)imageView
6499 {
6500  return _imageView;
6501 }
6502 
6506 - (void)setImageView:(CPImageView)aValue
6507 {
6508  _imageView = aValue;
6509 }
6510 
6511 @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:1632
#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
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:536
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:2455
var CPTableViewDelegate_tableView_shouldSelectRow_
Definition: CPTableView.j:54
CPColor colorWithHexString:(string hex)
Definition: CPColor.j:264
void setAutosaveTableColumns:(BOOL shouldAutosave)
Definition: CPTableView.j:2556
BOOL setThemeState:(ThemeState aState)
Definition: CPView.j:3255
FrameUpdater prototype identifier
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:6099
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)
An object representation of nil.
Definition: CPNull.h:2
CPArray sortDescriptors()
Definition: CPTableView.j:3335
CPInteger columnWithIdentifier:(CPString anIdentifier)
Definition: CPTableView.j:1255
void setGridStyleMask:(unsigned aGrideStyleMask)
Definition: CPTableView.j:1053
int columnIndex
Definition: CPTableView.j:6319
var CPTableViewDelegate_tableView_didClickTableColumn_
Definition: CPTableView.j:46
float scrollerWidth()
Definition: CPScroller.j:124
CPDragOperation CPDragOperationNone
var CPTableViewGridColorKey
Definition: CPTableView.j:6095
BOOL setThemeState:(ThemeState aState)
Definition: CPTableView.j:6391
CPTableViewFirstColumnOnlyAutoresizingStyle
Definition: CPTableView.j:98
CPColor clearColor()
Definition: CPColor.j:451
CPView headerView()
void reloadData()
Definition: CPTableView.j:599
function CGContextDrawLinearGradient(aContext, aGradient, aStartPoint, anEndPoint, options)
BOOL autoscroll:(CPEvent anEvent)
Definition: CPView.j:2941
#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:6403
CPArray tableColumns()
Definition: CPTableView.j:1244
var CPTableViewMultipleSelectionKey
Definition: CPTableView.j:6089
void addObserver:selector:name:object:(id anObserver, [selector] SEL aSelector, [name] CPString aNotificationName, [object] id anObject)
CFData prototype isa
Definition: CPData.j:214
Definition: CPCib.h:2
var CPTableViewSelectionHighlightStyleKey
Definition: CPTableView.j:6088
void setFrame:(CGRect aFrame)
Definition: CPView.j:1020
CGPoint locationInWindow()
Definition: CPEvent.j:290
CPInteger indexGreaterThanOrEqualToIndex:(CPInteger anIndex)
Definition: CPIndexSet.j:363
var CPTableViewColumnResizingKey
Definition: CPTableView.j:6092
CPArray hsbComponents()
Definition: CPColor.j:679
CPColor whiteColor()
Definition: CPColor.j:361
id delegate()
Definition: CALayer.j:965
int width
id initWithFrame:(CGRect aFrame)
Definition: CPControl.j:183
CGRect bounds()
Definition: CPView.j:1326
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:2482
#define NUMBER_OF_COLUMNS()
Definition: CPTableView.j:100
A collection of unique integers.
Definition: CPIndexSet.h:2
CPString charactersIgnoringModifiers()
Definition: CPEvent.j:392
CPView columnClipView
Definition: CPTableView.j:6320
void setHidden:(BOOL shouldBeHidden)
CPInteger firstIndex()
Definition: CPIndexSet.j:278
void viewWillMoveToWindow:(CPWindow aWindow)
Definition: CPView.j:913
CPTimer scheduledTimerWithTimeInterval:callback:repeats:(CPTimeInterval seconds, [callback] Function aFunction, [repeats] BOOL shouldRepeat)
Definition: CPTimer.j:70
unsigned modifierFlags()
Definition: CPEvent.j:309
CPView hitTest:(CGPoint aPoint)
Definition: CPView.j:1857
CPColor unfocusedSelectionGradientColors()
Definition: CPTableView.j:990
function CGContextAddLineToPoint(aContext, x, y)
Definition: CGContext.j:247
var CPTableViewIntercellSpacingKey
Definition: CPTableView.j:6087
var CPTableViewTableColumnsKey
Definition: CPTableView.j:6085
void setValue:forThemeAttribute:(id aValue, [forThemeAttribute] CPString aName)
Definition: CPView.j:3384
var CPTableViewDataSourceKey
Definition: CPTableView.j:6082
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:2969
var CPTableViewDelegate_tableViewColumnDidMove_
Definition: CPTableView.j:63
function ThemeState(stateNames)
Definition: CPTheme.j:379
function CGContextSetLineWidth(aContext, aLineWidth)
Definition: CGContext.j:177
void setUsesAlternatingRowBackgroundColors:(BOOL shouldUseAlternatingRowBackgroundColors)
Definition: CPTableView.j:844
CPRunLoop currentRunLoop()
Definition: CPRunLoop.j:232
function CGContextClosePath(aContext)
Definition: CGContext.j:322
CPTableViewDraggingDestinationFeedbackStyleSourceList
Definition: CPTableView.j:73
void setNeedsDisplay()
Definition: CALayer.j:830
BOOL isBlinking
Definition: CPTableView.j:6207
var CPTableViewDataSource_tableView_setObjectValue_forTableColumn_row_
Definition: CPTableView.j:36
CPArray components()
Definition: CPColor.j:637
void setBoundsOrigin:(CGPoint aPoint)
Definition: CPView.j:1347
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:527
void selectRowIndexes:byExtendingSelection:(CPIndexSet rows, [byExtendingSelection] BOOL shouldExtendSelection)
Definition: CPTableView.j:1365
var CPTableViewDelegate_tableView_mouseDownInHeaderOfTableColumn_
Definition: CPTableView.j:50
void setNeedsDisplay:(BOOL aFlag)
Definition: CPTableView.j:3967
CPTableViewGridNone
Definition: CPTableView.j:89
var CPTableViewDelegate_tableView_willRemoveView_forTableColumn_row_
Definition: CPTableView.j:62
void viewWillMoveToSuperview:(CPView aView)
Definition: CPTableView.j:4413
void viewWillMoveToSuperview:(CPView aView)
Definition: CPView.j:899
FrameUpdater prototype stop
int currentRow
Definition: CPTableView.j:6206
var CPTableViewRowHeightKey
Definition: CPTableView.j:6086
CPTableViewSequentialColumnAutoresizingStyle
Definition: CPTableView.j:95
void deselectAll()
Definition: CPTableView.j:1564
CPTableViewSelectionHighlightStyleRegular
Definition: CPTableView.j:86
void resizeWithOldSuperviewSize:(CGSize aSize)
Definition: CPView.j:1439
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:2249
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:3924
CPInteger indexGreaterThanIndex:(CPInteger anIndex)
Definition: CPIndexSet.j:301
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
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:3380
CPColor colorWithCalibratedWhite:alpha:(float white, [alpha] float alpha)
Definition: CPColor.j:173
id initWithCoder:(CPCoder aCoder)
Definition: CPControl.j:1092
CPIndexSet indexesPassingTest:(Function/*(int anIndex) */aPredicate)
Definition: CPIndexSet.j:559
CPColor colorWithRed:green:blue:alpha:(float red, [green] float green, [blue] float blue, [alpha] float alpha)
Definition: CPColor.j:122
Holds attributes necessary to describe how to sort a set of objects.
var CPTableViewDelegate_tableView_isGroupRow_
Definition: CPTableView.j:49
function CGContextSetFillColor(aContext, aColor)
Definition: CGContext.j:663
CPBinder getBinding:forObject:(CPString aBinding, [forObject] id anObject)
CPTableView tableView
Definition: CPTableView.j:6205
CPTableViewDropAbove
Definition: CPTableView.j:77
CPTableColumnAutoresizingMask
Definition: CPTableColumn.j:27
void setNeedsDisplay:(BOOL aFlag)
Definition: CPView.j:2597
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:1938
void setNeedsLayout()
Definition: CPTableView.j:3978
CPTableViewNoColumnAutoresizing
Definition: CPTableView.j:93
CPTableViewDropOn
Definition: CPTableView.j:76
CPInteger rowAtPoint:(CGPoint aPoint)
Definition: CPTableView.j:1995
var CPTableViewDataSource_tableView_namesOfPromisedFilesDroppedAtDestination_forDraggedRowsWithIndexes_
Definition: CPTableView.j:38
var CPTableViewEmptySelectionKey
Definition: CPTableView.j:6090
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:3555
void setDelegate:(id< CPTableViewDelegate > aDelegate)
Definition: CPTableView.j:2793
CPColor backgroundColor()
Definition: CALayer.j:629
CPThemeStateSelectedDataView
Definition: CPTheme.j:615
A notification that can be posted to a CPNotificationCenter.
Definition: CPNotification.h:2
var CPTableViewArchivedReusableViewsKey
Definition: CPTableView.j:6101
void selectColumnIndexes:byExtendingSelection:(CPIndexSet columns, [byExtendingSelection] BOOL shouldExtendSelection)
Definition: CPTableView.j:1307
CPHTMLCanvasFeature
CPDate limitDateForMode:(CPString aMode)
Definition: CPRunLoop.j:342
CPTableViewLastColumnOnlyAutoresizingStyle
Definition: CPTableView.j:97
void setNeedsLayout()
Definition: CPView.j:2748
var CPTableViewDelegateKey
Definition: CPTableView.j:6083
var CPTableViewDataSource_tableView_objectValueForTableColumn_row_
Definition: CPTableView.j:35
CPUpArrowFunctionKey
var CPTableViewGridStyleMaskKey
Definition: CPTableView.j:6096
CPColor colorWithHue:saturation:brightness:(float hue, [saturation] float saturation, [brightness] float brightness)
Definition: CPColor.j:191
var CPTableViewDelegate_tableViewSelectionIsChanging_
Definition: CPTableView.j:66
CPTableViewSelectionDidChangeNotification
Definition: CPTableView.j:31
var CPTableViewDelegate_tableViewColumnDidResize_
Definition: CPTableView.j:64
CPPlatformActionKeyMask
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:5307
function CGContextStrokeRect(aContext, aRect)
Definition: CGContext.j:388
BOOL unsetThemeState:(ThemeState aState)
Definition: CPView.j:3268
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:6100
void setBackgroundColor:(CPColor aColor)
Definition: CPView.j:1947
var CPTableViewDelegate_selectionShouldChangeInTableView_
Definition: CPTableView.j:43
CPLeftMouseDown
void setBoundsSize:(CGSize aSize)
Definition: CPView.j:1396
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:1124
var CPTableViewColumnSelectionKey
Definition: CPTableView.j:6093
CPSourceListGradient
Definition: CPTableView.j:79
void sizeLastColumnToFit()
Definition: CPTableView.j:2337
function CGContextFillRect(aContext, aRect)
Definition: CGContext.j:358
var CPTableViewColumnReorderingKey
Definition: CPTableView.j:6091
CPTableViewSelectionHighlightStyleNone
Definition: CPTableView.j:85
var CPTableViewDelegate_tableView_toolTipForView_rect_tableColumn_row_mouseLocation_
Definition: CPTableView.j:59
var CPTableViewHeaderViewKey
Definition: CPTableView.j:6084
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:6097
Definition: CPEvent.h:2
Class class()
Definition: CPObject.j:179
CPView contentView()
Definition: CPWindow.j:1226
var CPTableViewDelegate_tableView_heightOfRow_
Definition: CPTableView.j:48
CPTableViewSelectionHighlightStyleSourceList
Definition: CPTableView.j:87
CPView superview()
Definition: CPView.j:510
CGRect rectOfRow:(CPInteger aRowIndex)
Definition: CPTableView.j:1842
void enumerateIndexesUsingBlock:(Function/*(int idx, @ref BOOL stop) */aFunction)
Definition: CPIndexSet.j:487
CPArray alternatingRowBackgroundColors()
Definition: CPTableView.j:872
CGRect frame()
Definition: CPView.j:1046
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:5645
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:56
CPIndexSet columnIndexesInRect:(CGRect aRect)
Definition: CPTableView.j:1911
void removeIndex:(CPInteger anIndex)
Definition: CPIndexSet.j:804
BOOL scrollRectToVisible:(CGRect aRect)
Definition: CPView.j:2862
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:2822
var CPTableViewAlternatingRowColorsKey
Definition: CPTableView.j:6098
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:1121
void setFrameOrigin:(CGPoint aPoint)
Definition: CPView.j:1089
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
id indexSet()
Definition: CPIndexSet.j:43
CPString UID()
Definition: CPObject.j:552
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
id pasteboardWithName:(CPString aName)
Definition: CPPasteboard.j:93
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:137
CPTableViewDraggingDestinationFeedbackStyleNone
Definition: CPTableView.j:71
CPSourceListBottomLineColor
Definition: CPTableView.j:81
var CPTableViewColumnAutoresizingStyleKey
Definition: CPTableView.j:6094
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
FrameUpdater prototype description