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