mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-10-10 02:40:59 +00:00
Various CPTableView improvements and nib2cib support
This commit is contained in:
1 parent
aaa56f7c02
commit
00c6a8905d
9 files changed
+936
-166
No files matched your search
@@ -58,6 +58,8 @@
|
||||
@import "CPSlider.j"
|
||||
@import "CPSplitView.j"
|
||||
@import "CPTabView.j"
|
||||
@import "CPTableColumn.j"
|
||||
@import "CPTableView.j"
|
||||
@import "CPTextField.j"
|
||||
@import "CPToolbar.j"
|
||||
@import "CPToolbarItem.j"
|
||||
|
||||
@@ -11,9 +11,6 @@
|
||||
<string>AppKit</string>
|
||||
<key>Excluded</key>
|
||||
<array>
|
||||
<string>CPOutlineView.j</string>
|
||||
<string>CPTableColumn.j</string>
|
||||
<string>CPTableView.j</string>
|
||||
<!-- These get compiled into CGContext -->
|
||||
<string>Themes</string>
|
||||
<string>CoreGraphics/CGContextCanvas.j</string>
|
||||
|
||||
+1
-21
@@ -188,27 +188,7 @@ var CPClipViewDocumentViewKey = @"CPScrollViewDocumentView";
|
||||
{
|
||||
if (self = [super initWithCoder:aCoder])
|
||||
{
|
||||
_documentView = [aCoder decodeObjectForKey:CPClipViewDocumentViewKey];
|
||||
|
||||
if (_documentView)
|
||||
{
|
||||
[_documentView setPostsFrameChangedNotifications:YES];
|
||||
[_documentView setPostsBoundsChangedNotifications:YES];
|
||||
|
||||
var defaultCenter = [CPNotificationCenter defaultCenter];
|
||||
|
||||
[defaultCenter
|
||||
addObserver:self
|
||||
selector:@selector(viewFrameChanged:)
|
||||
name:CPViewFrameDidChangeNotification
|
||||
object:_documentView];
|
||||
|
||||
[defaultCenter
|
||||
addObserver:self
|
||||
selector:@selector(viewBoundsChanged:)
|
||||
name:CPViewBoundsDidChangeNotification
|
||||
object:_documentView];
|
||||
}
|
||||
[self setDocumentView:[aCoder decodeObjectForKey:CPClipViewDocumentViewKey]];
|
||||
}
|
||||
|
||||
return self;
|
||||
|
||||
+87
-41
@@ -68,11 +68,11 @@ CPTableColumnUserResizingMask = 2;
|
||||
|
||||
unsigned _resizingMask;
|
||||
|
||||
CPView _dataView;
|
||||
CPView _dataView; // default data view for this column
|
||||
|
||||
Object _dataViewData;
|
||||
Object _dataViewForView;
|
||||
Object _purgableInfosForDataView;
|
||||
Object _dataViewData; // cache of data view archives (key=data view hash, value=data view archive)
|
||||
Object _dataViewForView; // mapping from view instances back to their data view prototype (key=view instance hash, value=data view)
|
||||
Object _purgableInfosForDataView; // (key=data view hash, value=)
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -86,17 +86,18 @@ CPTableColumnUserResizingMask = 2;
|
||||
|
||||
if (self)
|
||||
{
|
||||
[self _init];
|
||||
|
||||
_identifier = anIdentifier;
|
||||
|
||||
_width = 40.0;
|
||||
_minWidth = 8.0;
|
||||
_maxWidth = 1000.0;
|
||||
|
||||
_dataViewData = {};
|
||||
_dataViewForView = {};
|
||||
_purgableInfosForDataView = {};
|
||||
var dataView = [[CPTextField alloc] initWithFrame:CPRectMakeZero()];
|
||||
[dataView setValue:[CPColor whiteColor] forThemedAttributeName:"text-color" inControlState:CPControlStateHighlighted];
|
||||
|
||||
[self setDataView:[[CPTextField alloc] initWithFrame:CPRectMakeZero()]];
|
||||
[self setDataView:dataView];
|
||||
|
||||
_headerView = [[CPTextField alloc] initWithFrame:CPRectMakeZero()];
|
||||
[_headerView setBackgroundColor:[CPColor greenColor]];
|
||||
@@ -105,6 +106,13 @@ CPTableColumnUserResizingMask = 2;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)_init
|
||||
{
|
||||
_dataViewData = {};
|
||||
_dataViewForView = {};
|
||||
_purgableInfosForDataView = {};
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the table column's identifier
|
||||
@param anIdentifier the new identifier
|
||||
@@ -317,7 +325,7 @@ CPTableColumnUserResizingMask;
|
||||
{
|
||||
return [self dataCellForRow:aRowIndex];
|
||||
}
|
||||
|
||||
/*
|
||||
- (void)_markViewAsPurgable:(CPView)aView
|
||||
{
|
||||
var viewHash = [aView hash],
|
||||
@@ -328,7 +336,7 @@ CPTableColumnUserResizingMask;
|
||||
|
||||
[_purgableInfosForDataView[dataViewHash] setObject:aView forKey:viewHash];
|
||||
}
|
||||
|
||||
*/
|
||||
- (void)_markView:(CPView)aView inRow:(unsigned)aRow asPurgable:(BOOL)isPurgable
|
||||
{
|
||||
var viewHash = [aView hash],
|
||||
@@ -339,55 +347,93 @@ CPTableColumnUserResizingMask;
|
||||
if (!isPurgable)
|
||||
return;
|
||||
|
||||
_purgableInfosForDataView[dataViewHash] = [CPDictionary dictionary];
|
||||
_purgableInfosForDataView[dataViewHash] = {};
|
||||
}
|
||||
|
||||
if (!isPurgable)
|
||||
[_purgableInfosForDataView[dataViewHash] removeObjectForKey:viewHash];
|
||||
delete _purgableInfosForDataView[dataViewHash][viewHash];
|
||||
else
|
||||
[_purgableInfosForDataView[dataViewHash] setObject:PurgableInfoMake(aView, aRow) forKey:viewHash];
|
||||
_purgableInfosForDataView[dataViewHash][viewHash] = PurgableInfoMake(aView, aRow);
|
||||
}
|
||||
|
||||
- (CPView)_newDataViewForRow:(int)aRowIndex avoidingRows:(CPRange)rows
|
||||
{
|
||||
var view = [self dataViewForRow:aRowIndex],
|
||||
viewHash = [view hash],
|
||||
dataViewHash = [_dataViewForView[viewHash] hash],
|
||||
purgableInfos = _purgableInfosForDataView[dataViewHash];
|
||||
//console.warn("ok, a cell is needed");
|
||||
if (purgableInfos && [purgableInfos count])
|
||||
{//console.warn("yes, inside");
|
||||
var keys = [purgableInfos allKeys],
|
||||
count = keys.length;
|
||||
|
||||
while (count--)
|
||||
purgableInfos = _purgableInfosForDataView[viewHash];
|
||||
|
||||
if (purgableInfos)
|
||||
{
|
||||
for (var key in purgableInfos)
|
||||
{
|
||||
var key = keys[count],
|
||||
info = [purgableInfos objectForKey:key];
|
||||
|
||||
[purgableInfos removeObjectForKey:key];
|
||||
|
||||
if (CPLocationInRange(PurgableInfoRow(info), rows))
|
||||
continue;
|
||||
//console.warn("yes, a purged view is usable, its called" + PurgableInfoView(info));
|
||||
return PurgableInfoView(info);
|
||||
var info = purgableInfos[key];
|
||||
if (!CPLocationInRange(PurgableInfoRow(info), rows))
|
||||
{
|
||||
//CPLog.debug("yes, a purged view is usable, its called: " + PurgableInfoView(info));
|
||||
delete purgableInfos[key];
|
||||
return PurgableInfoView(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var data = _dataViewData[viewHash];
|
||||
|
||||
if (!data)
|
||||
{
|
||||
// if we haven't cached an archive of the data view, do it now
|
||||
if (!_dataViewData[viewHash])
|
||||
_dataViewData[viewHash] = [CPKeyedArchiver archivedDataWithRootObject:view];
|
||||
data = _dataViewData[viewHash];
|
||||
}
|
||||
//console.warn("nope, time for creation");
|
||||
return [CPKeyedUnarchiver unarchiveObjectWithData:data];
|
||||
|
||||
// unarchive the data view cache
|
||||
var newView = [CPKeyedUnarchiver unarchiveObjectWithData:_dataViewData[viewHash]];
|
||||
|
||||
// map the new view's hash to it's data view prototype
|
||||
_dataViewForView[[newView hash]] = view;
|
||||
|
||||
//CPLog.warn("nope, time for creation: %s", newView);
|
||||
|
||||
return newView;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
_PurgableViewInfoMake = function(aView, aRow)
|
||||
|
||||
var CPTableColumnIdentifierKey = @"CPTableColumnIdentifierKey",
|
||||
CPTableColumnHeaderViewKey = @"CPTableColumnHeaderViewKey",
|
||||
CPTableColumnDataViewKey = @"CPTableColumnDataViewKey",
|
||||
CPTableColumnWidthKey = @"CPTableColumnWidthKey",
|
||||
CPTableColumnMinWidthKey = @"CPTableColumnMinWidthKey",
|
||||
CPTableColumnMaxWidthKey = @"CPTableColumnMaxWidthKey",
|
||||
CPTableColumnResizingMaskKey = @"CPTableColumnResizingMaskKey";
|
||||
|
||||
@implementation CPTableColumn (CPCoding)
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
return { view:aView, row:aRow};
|
||||
[self _init];
|
||||
|
||||
_identifier = [aCoder decodeObjectForKey:CPTableColumnIdentifierKey];
|
||||
|
||||
[self setHeaderView:[aCoder decodeObjectForKey:CPTableColumnHeaderViewKey]];
|
||||
[self setDataView:[aCoder decodeObjectForKey:CPTableColumnDataViewKey]];
|
||||
|
||||
_width = [aCoder decodeFloatForKey:CPTableColumnWidthKey];
|
||||
_minWidth = [aCoder decodeFloatForKey:CPTableColumnMinWidthKey];
|
||||
_maxWidth = [aCoder decodeFloatForKey:CPTableColumnMaxWidthKey];
|
||||
|
||||
_resizingMask = [aCoder decodeBoolForKey:CPTableColumnResizingMaskKey];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[aCoder encodeObject:_identifier forKey:CPTableColumnIdentifierKey];
|
||||
|
||||
[aCoder encodeObject:_headerView forKey:CPTableColumnHeaderViewKey];
|
||||
[aCoder encodeObject:_dataView forKey:CPTableColumnDataViewKey];
|
||||
|
||||
[aCoder encodeObject:_width forKey:CPTableColumnWidthKey];
|
||||
[aCoder encodeObject:_minWidth forKey:CPTableColumnMinWidthKey];
|
||||
[aCoder encodeObject:_maxWidth forKey:CPTableColumnMaxWidthKey];
|
||||
|
||||
[aCoder encodeObject:_resizingMask forKey:CPTableColumnResizingMaskKey];
|
||||
}
|
||||
|
||||
@end
|
||||
+697
-101
@@ -23,10 +23,25 @@
|
||||
@import "CPControl.j"
|
||||
@import "CPTableColumn.j"
|
||||
|
||||
//objj_backtrace_set_enable(true);
|
||||
@import "CPColor.j"
|
||||
@import "CPTextField.j"
|
||||
|
||||
#define ROW_HEIGHT(aRow) (_hasVariableHeightRows ? _rowHeights[aRow] : _rowHeight);
|
||||
#define ROW_HEIGHT(aRow) (_hasVariableHeightRows ? _rowHeights[aRow] : _rowHeight)
|
||||
#define ROW_MIN_Y(aRow) (_hasVariableHeightRows ? _rowMinYs[aRow] : (aRow * (_rowHeight + _intercellSpacing.height)))
|
||||
|
||||
CPTableViewColumnDidMoveNotification = "CPTableViewColumnDidMoveNotification";
|
||||
CPTableViewColumnDidResizeNotification = "CPTableViewColumnDidResizeNotification";
|
||||
CPTableViewSelectionDidChangeNotification = "CPTableViewSelectionDidChangeNotification";
|
||||
CPTableViewSelectionIsChangingNotification = "CPTableViewSelectionIsChangingNotification";
|
||||
|
||||
var _CPTableViewWillDisplayCellSelector = 1 << 0,
|
||||
_CPTableViewShouldSelectRowSelector = 1 << 1,
|
||||
_CPTableViewShouldSelectTableColumnSelector = 1 << 2,
|
||||
_CPTableViewSelectionShouldChangeSelector = 1 << 3,
|
||||
_CPTableViewShouldEditTableColumnSelector = 1 << 4,
|
||||
_CPTableViewSelectionIndexesForProposedSelectionSelector = 1 << 5,
|
||||
_CPTableViewHeightOfRowSelector = 1 << 6;
|
||||
|
||||
/*
|
||||
CPTableView is located within the AppKit framework and is used to display tables. It uses a delegate model for getting its data i.e. you give it an object that provides it with the data it should display.
|
||||
|
||||
@@ -34,31 +49,53 @@
|
||||
*/
|
||||
@implementation CPTableView : CPControl
|
||||
{
|
||||
id _dataSource;
|
||||
// Archived:
|
||||
|
||||
CPScrollView _scrollView;
|
||||
CPTableHeaderView _headerView;
|
||||
id _dataSource;
|
||||
id _delegate;
|
||||
|
||||
//CPTableHeaderView _headerView;
|
||||
//CPView _cornerView;
|
||||
|
||||
CPArray _tableColumns;
|
||||
|
||||
//
|
||||
CPIndexSet _selectedRowIndexes;
|
||||
//CPArray _selectedColumns;
|
||||
|
||||
float _rowHeight;
|
||||
CGSize _intercellSpacing;
|
||||
|
||||
BOOL _allowsMultipleSelection;
|
||||
BOOL _allowsEmptySelection;
|
||||
//BOOL _allowsColumnReordering;
|
||||
//BOOL _allowsColumnResizing;
|
||||
//BOOL _allowsColumnSelection;
|
||||
//BOOL _autoresizesAllColumnsToFit;
|
||||
|
||||
//CPColor _gridColor;
|
||||
//BOOL _drawsGrid;
|
||||
|
||||
|
||||
// Not archived:
|
||||
|
||||
int _delegateSelectorsCache;
|
||||
|
||||
//CPScrollView _scrollView;
|
||||
|
||||
unsigned _numberOfRows;
|
||||
unsigned _numberOfColumns;
|
||||
|
||||
BOOL _hasVariableHeightRows;
|
||||
|
||||
// Heights
|
||||
float _rowHeight;
|
||||
float _columnHeight; // calculated
|
||||
|
||||
CPArray _rowMinYs;
|
||||
CPArray _rowHeights;
|
||||
|
||||
BOOL _hasVariableHeightRows;
|
||||
CPArray _rowMinYs;
|
||||
|
||||
CPArray _tableCells;
|
||||
CPArray _tableColumnViews;
|
||||
|
||||
CGSize _intercellSpacing;
|
||||
|
||||
// Caching
|
||||
Object _dataViewCache;
|
||||
CPArray _objectValueCache;
|
||||
@@ -68,6 +105,20 @@
|
||||
|
||||
CPRange _populatedRows;
|
||||
CPRange _populatedColumns;
|
||||
|
||||
// Selection
|
||||
CPIndexSet _previousSelectedRowIndexes;
|
||||
int _selectionStartRow;
|
||||
int _selectionModifier;
|
||||
|
||||
CPIndexSet _currentlySelected;
|
||||
CPArray _selectionViews;
|
||||
CPArray _selectionViewsPool;
|
||||
}
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
@@ -75,45 +126,62 @@
|
||||
self = [super initWithFrame:aFrame];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_rowHeight = 17.0;
|
||||
|
||||
_tableCells = [];
|
||||
_tableColumns = [];
|
||||
_tableColumnViews = [];
|
||||
|
||||
_dataViewCache = {};
|
||||
_objectValueCache = {};
|
||||
|
||||
_intercellSpacing = CPSizeMake(3.0, 2.0);
|
||||
|
||||
_visibleRows = CPMakeRange(0, 0);
|
||||
_visibleColumns = CPMakeRange(0, 0);
|
||||
{
|
||||
[self _init];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)_init
|
||||
{
|
||||
_tableColumns = [];
|
||||
_numberOfColumns = 0;
|
||||
|
||||
_selectedRowIndexes = [CPIndexSet indexSet];
|
||||
|
||||
_rowHeight = 17.0;
|
||||
_intercellSpacing = CPSizeMake(3.0, 2.0);
|
||||
|
||||
_allowsMultipleSelection = YES;
|
||||
_allowsEmptySelection = YES;
|
||||
|
||||
|
||||
_tableCells = [];
|
||||
_tableColumnViews = [];
|
||||
|
||||
_dataViewCache = {};
|
||||
_objectValueCache = [];
|
||||
|
||||
_visibleRows = CPMakeRange(0, 0);
|
||||
_visibleColumns = CPMakeRange(0, 0);
|
||||
|
||||
_rowHeights = [];
|
||||
_rowMinYs = [];
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the table's column height
|
||||
*/
|
||||
- (float)_columnHeight
|
||||
{
|
||||
return _numberOfRows * (_rowHeight + _intercellSpacing.height);
|
||||
return _columnHeight;//_numberOfRows * (_rowHeight + _intercellSpacing.height);
|
||||
}
|
||||
|
||||
- (void)newCellForRow:(unsigned)aRowIndex column:(unsigned)aColumnIndex avoidingRows:(CPRange)rows
|
||||
{//console.warn("new cell please.");
|
||||
{
|
||||
var dataView = [_tableColumns[aColumnIndex] _newDataViewForRow:aRowIndex avoidingRows:rows];
|
||||
|
||||
[dataView setFrame:CGRectMake(0.0, aRowIndex * (_rowHeight + _intercellSpacing.height), [_tableColumns[aColumnIndex] width], _rowHeight)];
|
||||
[dataView setBackgroundColor:[CPColor greenColor]];
|
||||
|
||||
[dataView setFrame:CGRectMake(0.0, ROW_MIN_Y(aRowIndex), [_tableColumns[aColumnIndex] width], ROW_HEIGHT(aRowIndex))];
|
||||
|
||||
if ([dataView respondsToSelector:@selector(highlight:)])
|
||||
[dataView highlight:[_selectedRowIndexes containsIndex:aRowIndex]];
|
||||
|
||||
if (!_objectValueCache[aColumnIndex])
|
||||
_objectValueCache[aColumnIndex] = [];
|
||||
|
||||
// We may be storing 0 after all!
|
||||
if (typeof _objectValueCache[aColumnIndex][aRowIndex] === "undefined")
|
||||
if (_objectValueCache[aColumnIndex][aRowIndex] === undefined)
|
||||
_objectValueCache[aColumnIndex][aRowIndex] = [_dataSource tableView:self objectValueForTableColumn:_tableColumns[aColumnIndex] row:aRowIndex];
|
||||
|
||||
[dataView setObjectValue:_objectValueCache[aColumnIndex][aRowIndex]];
|
||||
@@ -128,52 +196,15 @@
|
||||
|
||||
// Determine new visible rows and columns.
|
||||
|
||||
// Use a ambitious estimate for our starting row.
|
||||
var rowStart = MAX(FLOOR((CGRectGetMinY(aRect) + _intercellSpacing.height) / (_rowHeight + _intercellSpacing.height)), 0),
|
||||
var rowStart = MAX(0, [self _rowAtY:CGRectGetMinY(aRect)] - 1),
|
||||
rowEnd = MIN(_numberOfRows, [self _rowAtY:CGRectGetMaxY(aRect)] + 1),
|
||||
|
||||
// Use a conservative estimate for the final row.
|
||||
rowEnd = MIN(_numberOfRows, CEIL(CGRectGetMaxY(aRect) / (_rowHeight + _intercellSpacing.height))),
|
||||
|
||||
visibleRows = CPMakeRange(rowStart, rowEnd - rowStart);
|
||||
|
||||
var columnStart = 0;
|
||||
|
||||
// Iterate through all our columns until we find the first one that intersects the rect.
|
||||
while (columnStart < _numberOfColumns && !CGRectIntersectsRect([_tableColumnViews[columnStart] frame], aRect))
|
||||
++columnStart;
|
||||
|
||||
// Now use a binary search to find the last visible column
|
||||
// O (lg n) < O (n), but O(n) (above), so O (n + lg n) = O (n) ?
|
||||
var first = columnStart + 1,
|
||||
last = _numberOfColumns - 1;
|
||||
columnEnd = columnStart;
|
||||
visibleRows = CPMakeRange(rowStart, rowEnd - rowStart),
|
||||
|
||||
while (first <= last)
|
||||
{
|
||||
// Assume this is the one.
|
||||
var columnEnd = FLOOR((first + last) / 2),
|
||||
columnIsVisible = CGRectIntersectsRect([_tableColumnViews[columnEnd] frame], aRect);
|
||||
|
||||
// If the column isn't visible, look left!
|
||||
if (!columnIsVisible)
|
||||
last = columnEnd - 1;
|
||||
columnStart = MAX(0, [self _columnAtX:CGRectGetMinX(aRect)]),
|
||||
columnEnd = MIN(_numberOfColumns, [self _columnAtX:CGRectGetMaxX(aRect)] + 1),
|
||||
|
||||
// Visible, nothing to the right, found it...
|
||||
if (columnEnd + 1 >= _numberOfColumns)
|
||||
break;
|
||||
|
||||
// Visible, column to the right is NOT visible, found it! (the good way)
|
||||
if (!CGRectIntersectsRect([_tableColumnViews[columnEnd + 1] frame], aRect))
|
||||
break;
|
||||
|
||||
// If not, look right! (2 since we checked the dude to the right already)
|
||||
first = columnEnd + 2;
|
||||
}
|
||||
|
||||
// columnEnd is our "count" in loops.
|
||||
++columnEnd;
|
||||
|
||||
var visibleColumns = CPMakeRange(columnStart, columnEnd - columnStart);
|
||||
visibleColumns = CPMakeRange(columnStart, columnEnd - columnStart);
|
||||
|
||||
if (CPEqualRanges(_visibleRows, visibleRows) && CPEqualRanges(_visibleColumns, visibleColumns))
|
||||
return;
|
||||
@@ -182,9 +213,9 @@
|
||||
unionVisibleColumns = CPUnionRange(_visibleColumns, visibleColumns);
|
||||
|
||||
// Determine whether to use 2 sweeps or one. If we have lots of overlap of cells, use just one.
|
||||
if (unionVisibleRows.length * unionVisibleColumns.length <=
|
||||
(_visibleRows.length + visibleRows.length) * (_visibleColumns.length + visibleColumns.length))
|
||||
{
|
||||
//if (unionVisibleRows.length * unionVisibleColumns.length <=
|
||||
// (_visibleRows.length * _visibleColumns.length) + (visibleRows.length * visibleColumns.length))
|
||||
//{
|
||||
var column = unionVisibleColumns.location,
|
||||
columnEnd = CPMaxRange(unionVisibleColumns),
|
||||
|
||||
@@ -222,11 +253,9 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
}
|
||||
//}
|
||||
//else
|
||||
// CPLog.error("CPTable double sweep not implemented");
|
||||
|
||||
_visibleRows = visibleRows;
|
||||
_visibleColumns = visibleColumns;
|
||||
@@ -318,6 +347,8 @@
|
||||
[_tableCells[i][j] setFrameOrigin:CPPointMake(0.0, y)];
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: variable height rows
|
||||
}
|
||||
|
||||
_intercellSpacing = CPSizeCreateCopy(aSize);
|
||||
@@ -343,11 +374,12 @@
|
||||
|
||||
_rowHeight = aRowHeight;
|
||||
|
||||
var row = 0,
|
||||
column = 0;
|
||||
|
||||
for (; row < _numberOfRows; ++row)
|
||||
for (column = 0; column < _numberOfColumns; ++column)
|
||||
// don't perform adjustments if we're using variable height rows
|
||||
if (_hasVariableHeightRows)
|
||||
return;
|
||||
|
||||
for (var row = 0; row < _numberOfRows; ++row)
|
||||
for (var column = 0; column < _numberOfColumns; ++column)
|
||||
[_tableCells[column][row] setFrameOrigin:CPPointMake(0.0, row * (_rowHeight + _intercellSpacing.height))];
|
||||
}
|
||||
|
||||
@@ -379,6 +411,7 @@
|
||||
|
||||
[_tableCells addObject:tableColumnCells];
|
||||
|
||||
// TODO: do we really need to initialize this, or is undefined good enough?
|
||||
for (; i < _numberOfRows; ++i)
|
||||
_tableCells[_numberOfColumns][i] = nil;
|
||||
|
||||
@@ -431,6 +464,18 @@
|
||||
return _tableColumns;
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the first CPTableColumn equal to the given object (as determined by the "isEqual:" selector), or nil if none exists.
|
||||
@param anObject the object to look for
|
||||
*/
|
||||
- (CPTableColumn)tableColumnWithIdentifier:(id)anObject
|
||||
{
|
||||
for (var i = 0; i < _tableColumns.length; i++)
|
||||
if ([_tableColumns[i] isEqual:anObject])
|
||||
return _tableColumns[i];
|
||||
return nil;
|
||||
}
|
||||
|
||||
// Getting the dimensions of the table
|
||||
/*
|
||||
Returns the number of columns in the table
|
||||
@@ -489,6 +534,67 @@
|
||||
return _dataSource;
|
||||
}
|
||||
|
||||
|
||||
- (id)delegate
|
||||
{
|
||||
return _delegate;
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
Sets the delegate for the tableview.
|
||||
*/
|
||||
- (void)setDelegate:(id)aDelegate
|
||||
{
|
||||
if (_delegate === aDelegate)
|
||||
return;
|
||||
|
||||
var notificationCenter = [CPNotificationCenter defaultCenter];
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(tableViewColumnDidMove:)])
|
||||
[notificationCenter removeObserver:_delegate name:CPTableViewColumnDidMoveNotification object:self];
|
||||
if ([_delegate respondsToSelector:@selector(tableViewColumnDidResize:)])
|
||||
[notificationCenter removeObserver:_delegate name:CPTableViewColumnDidResizeNotification object:self];
|
||||
if ([_delegate respondsToSelector:@selector(tableViewSelectionDidChange:)])
|
||||
[notificationCenter removeObserver:_delegate name:CPTableViewSelectionDidChangeNotification object:self];
|
||||
if ([_delegate respondsToSelector:@selector(tableViewSelectionIsChanging:)])
|
||||
[notificationCenter removeObserver:_delegate name:CPTableViewSelectionIsChangingNotification object:self];
|
||||
|
||||
_delegate = aDelegate;
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(tableViewColumnDidMove:)])
|
||||
[notificationCenter addObserver:_delegate selector:@selector(tableViewColumnDidMove:) name:CPTableViewColumnDidMoveNotification object:self];
|
||||
if ([_delegate respondsToSelector:@selector(tableViewColumnDidResize:)])
|
||||
[notificationCenter addObserver:_delegate selector:@selector(tableViewColumnDidResize:) name:CPTableViewColumnDidResizeNotification object:self];
|
||||
if ([_delegate respondsToSelector:@selector(tableViewSelectionDidChange:)])
|
||||
[notificationCenter addObserver:_delegate selector:@selector(tableViewSelectionDidChange:) name:CPTableViewSelectionDidChangeNotification object:self];
|
||||
if ([_delegate respondsToSelector:@selector(tableViewSelectionIsChanging:)])
|
||||
[notificationCenter addObserver:_delegate selector:@selector(tableViewSelectionIsChanging:) name:CPTableViewSelectionIsChangingNotification object:self];
|
||||
|
||||
_delegateSelectorsCache = 0;
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(tableView:willDisplayCell:forTableColumn:row:)])
|
||||
_delegateSelectorsCache |= _CPTableViewWillDisplayCellSelector;
|
||||
if ([_delegate respondsToSelector:@selector(tableView:shouldSelectRow:)])
|
||||
_delegateSelectorsCache |= _CPTableViewShouldSelectRowSelector;
|
||||
if ([_delegate respondsToSelector:@selector(tableView:shouldSelectTableColumn:)])
|
||||
_delegateSelectorsCache |= _CPTableViewShouldSelectTableColumnSelector;
|
||||
if ([_delegate respondsToSelector:@selector(selectionShouldChangeInTableView:)])
|
||||
_delegateSelectorsCache |= _CPTableViewSelectionShouldChangeSelector;
|
||||
if ([_delegate respondsToSelector:@selector(tableView:shouldEditTableColumn:row:)])
|
||||
_delegateSelectorsCache |= _CPTableViewShouldEditTableColumnSelector;
|
||||
if ([_delegate respondsToSelector:@selector(tableView:selectionIndexesForProposedSelection:)])
|
||||
_delegateSelectorsCache |= _CPTableViewSelectionIndexesForProposedSelectionSelector;
|
||||
if ([_delegate respondsToSelector:@selector(tableView:heightOfRow:)])
|
||||
{
|
||||
_delegateSelectorsCache |= _CPTableViewHeightOfRowSelector;
|
||||
_hasVariableHeightRows = YES;
|
||||
}
|
||||
else
|
||||
_hasVariableHeightRows = NO;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Tells the table view that the number of rows in the table
|
||||
has changed.
|
||||
@@ -505,6 +611,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
- (void)noteHeightOfRowsWithIndexesChanged:(CPIndexSet)indexSet
|
||||
{
|
||||
// FIXME: more efficient version is possible since we know which indexes changes
|
||||
[self _recalculateColumnHeight];
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the rectangle bounding the specified row.
|
||||
@param aRowIndex the row to obtain a rectangle for
|
||||
@@ -512,7 +624,7 @@
|
||||
*/
|
||||
- (CGRect)rectOfRow:(int)aRowIndex
|
||||
{
|
||||
return CPRectMake(0.0, aRowIndex * (_rowHeight + _intercellSpacing.height), CPRectGetWidth([self bounds]), _rowHeight);
|
||||
return CPRectMake(0.0, ROW_MIN_Y(aRowIndex), CPRectGetWidth([self bounds]), ROW_HEIGHT(aRowIndex));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -539,6 +651,13 @@
|
||||
|
||||
if (_hasVariableHeightRows)
|
||||
{
|
||||
_rowMinYs[0] = 0;
|
||||
for (var row = 0; row < _numberOfRows; row++)
|
||||
{
|
||||
_rowHeights[row] = [_delegate tableView:self heightOfRow:row];
|
||||
_rowMinYs[row+1] = _rowMinYs[row] + _rowHeights[row] + _intercellSpacing.height;
|
||||
}
|
||||
_columnHeight = _rowMinYs[_numberOfRows]; // last index is one more than last row, and is the total column height
|
||||
}
|
||||
else
|
||||
_columnHeight = _numberOfRows * (_rowHeight + _intercellSpacing.height);
|
||||
@@ -575,7 +694,9 @@
|
||||
{
|
||||
[self _recalculateColumnHeight];
|
||||
[self setFrameSize:CGSizeMake(CGRectGetWidth([self frame]), [self _columnHeight])];
|
||||
}
|
||||
}
|
||||
|
||||
_objectValueCache = [];
|
||||
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
@@ -583,22 +704,12 @@
|
||||
- (void)viewWillDraw
|
||||
{
|
||||
[self loadTableCellsInRect:[self visibleRectInParent]];
|
||||
|
||||
//alert("oh yes. " + CPStringFromRect([self visibleRect]));
|
||||
//[self reloadData];
|
||||
}
|
||||
|
||||
- (void)drawRect:(CGRect)aRect
|
||||
{
|
||||
}
|
||||
|
||||
- (void)setFrameSize:(CGSize)aFrameSize
|
||||
{
|
||||
[super setFrameSize:aFrameSize];
|
||||
|
||||
// [self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
- (void)displaySoon
|
||||
{
|
||||
// window.setTimeout();
|
||||
@@ -613,7 +724,6 @@
|
||||
selector:@selector(viewBoundsChanged:)
|
||||
name:CPViewBoundsDidChangeNotification
|
||||
object:[[self enclosingScrollView] contentView]];
|
||||
//console.warn("cheese");
|
||||
}
|
||||
|
||||
- (void)viewBoundsChanged:(CPNotification)aNotification
|
||||
@@ -623,4 +733,490 @@
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
/*
|
||||
- (void)setAllowsColumnReordering:(BOOL)allowsColumnReordering
|
||||
{
|
||||
if (_allowsColumnReordering === _allowsColumnReordering)
|
||||
return;
|
||||
|
||||
_allowsColumnReordering = allowsColumnReordering;
|
||||
}
|
||||
- (void)allowsColumnReordering
|
||||
{
|
||||
return _allowsColumnReordering;
|
||||
}
|
||||
|
||||
- (void)setAllowsColumnResizing:(BOOL)allowsColumnResizing
|
||||
{
|
||||
if (_allowsColumnResizing === allowsColumnResizing)
|
||||
return;
|
||||
|
||||
_allowsColumnResizing = allowsColumnResizing;
|
||||
}
|
||||
- (void)allowsColumnResizing
|
||||
{
|
||||
return _allowsColumnResizing;
|
||||
}
|
||||
|
||||
- (void)setAllowsColumnSelection:(BOOL)allowsColumnSelection
|
||||
{
|
||||
if (_allowsColumnSelection === allowsColumnSelection)
|
||||
return;
|
||||
|
||||
_allowsColumnSelection = allowsColumnSelection;
|
||||
}
|
||||
- (void)allowsColumnSelection
|
||||
{
|
||||
return _allowsColumnSelection;
|
||||
}
|
||||
*/
|
||||
|
||||
- (void)setAllowsMultipleSelection:(BOOL)allowsMultipleSelection
|
||||
{
|
||||
if (_allowsMultipleSelection === allowsMultipleSelection)
|
||||
return;
|
||||
|
||||
_allowsMultipleSelection = allowsMultipleSelection;
|
||||
|
||||
// TODO: more stuff?
|
||||
}
|
||||
- (void)allowsMultipleSelection
|
||||
{
|
||||
return _allowsMultipleSelection;
|
||||
}
|
||||
|
||||
- (void)setAllowsEmptySelection:(BOOL)allowsEmptySelection
|
||||
{
|
||||
if (_allowsEmptySelection === allowsEmptySelection)
|
||||
return;
|
||||
|
||||
_allowsEmptySelection = allowsEmptySelection;
|
||||
}
|
||||
- (void)allowsEmptySelection
|
||||
{
|
||||
return _allowsEmptySelection;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Returns the index of the row at the given point, or CPNotFound (-1) if it is out of range.
|
||||
@param aPoint the point
|
||||
@return the index of the row at aPoint
|
||||
*/
|
||||
- (int)rowAtPoint:(CGPoint)aPoint
|
||||
{
|
||||
var index = [self _rowAtY:aPoint.y]
|
||||
|
||||
if (index >= 0 && index < _numberOfRows)
|
||||
return index;
|
||||
else
|
||||
return CPNotFound;
|
||||
}
|
||||
|
||||
- (int)columnAtPoint:(CGPoint)aPoint
|
||||
{
|
||||
var index = [self _columnAtY:aPoint.x]
|
||||
|
||||
if (index >= 0 && index < _numberOfColumns)
|
||||
return index;
|
||||
else
|
||||
return CPNotFound;
|
||||
}
|
||||
|
||||
/*
|
||||
@ignore
|
||||
|
||||
Internal version takes a Y value, returns an index, or -1 if its beyond the min, or numberOfRows if it's beyond the max
|
||||
*/
|
||||
- (int)_rowAtY:(float)y
|
||||
{
|
||||
if (_hasVariableHeightRows)
|
||||
{
|
||||
var a = 0,
|
||||
b = _numberOfRows;
|
||||
|
||||
if (y < _rowMinYs[0])
|
||||
return -1;
|
||||
if (y >= _rowMinYs[_rowMinYs.length-1])
|
||||
return _numberOfRows;
|
||||
|
||||
// binary search
|
||||
while (true)
|
||||
{
|
||||
var half = a + Math.floor((b - a) / 2);
|
||||
|
||||
if (half === _numberOfRows - 1)
|
||||
return _numberOfRows - 1;
|
||||
|
||||
if (y >= _rowMinYs[half+1])
|
||||
a = half;
|
||||
else if (y < _rowMinYs[half])
|
||||
b = half;
|
||||
else
|
||||
return half;
|
||||
}
|
||||
}
|
||||
else
|
||||
return FLOOR(y / (_rowHeight + _intercellSpacing.height));
|
||||
}
|
||||
|
||||
/*
|
||||
@ignore
|
||||
|
||||
Internal version takes a X value, returns an index, or -1 if its beyond the min, or numberOfColumns if it's beyond the max
|
||||
*/
|
||||
- (int)_columnAtX:(float)y
|
||||
{
|
||||
var a = 0,
|
||||
b = _numberOfColumns;
|
||||
|
||||
var last = [_tableColumnViews[_numberOfColumns-1] frame];
|
||||
if (y < [_tableColumnViews[0] frame].origin.x)
|
||||
return -1;
|
||||
if (y >= last.origin.x + last.size.width)
|
||||
return _numberOfColumns;
|
||||
|
||||
// binary search
|
||||
while (true)
|
||||
{
|
||||
var half = a + Math.floor((b - a) / 2);
|
||||
|
||||
if (half === _numberOfColumns - 1)
|
||||
return _numberOfColumns - 1;
|
||||
|
||||
if (y >= [_tableColumnViews[half+1] frame].origin.x)
|
||||
a = half;
|
||||
else if (y < [_tableColumnViews[half] frame].origin.x)
|
||||
b = half;
|
||||
else
|
||||
return half;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Selects the specified row indexes, optionally adding to existing selection
|
||||
@param indexes the indexes to select
|
||||
@param extend whether or not to add to the existing selection
|
||||
*/
|
||||
- (void)selectRowIndexes:(CPIndexSet)indexes byExtendingSelection:(BOOL)extend
|
||||
{
|
||||
// FIXME: should this be subject to the delegate filters, etc?
|
||||
|
||||
if (extend)
|
||||
_selectedRowIndexes = [[_selectedRowIndexes copy] addIndexes:indexes];
|
||||
else if ([indexes count] > 0 || _allowsEmptySelection)
|
||||
_selectedRowIndexes = [indexes copy];
|
||||
|
||||
[self _drawSelection];
|
||||
}
|
||||
|
||||
/*
|
||||
Returns a CPIndexSet of the selected rows
|
||||
@return indexes of the selected rows
|
||||
*/
|
||||
- (CPIndexSet)selectedRowIndexes
|
||||
{
|
||||
return _selectedRowIndexes;
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the number of selected rows
|
||||
@return number of selected rows
|
||||
*/
|
||||
- (int)numberOfSelectedRows
|
||||
{
|
||||
return [_selectedRowIndexes count];
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Deselects all rows if allowsEmptySelection is true. If delegate responds to "selectionShouldChangeInTableView:", asks if it should chnage.
|
||||
Sends the CPTableViewSelectionDidChangeNotification on deselection.
|
||||
@param the sender in a target/action
|
||||
*/
|
||||
- (void)deselectAll:(id)sender
|
||||
{
|
||||
if (!_allowsEmptySelection || [_selectedRowIndexes count] === 0 ||
|
||||
((_delegateSelectorsCache & _CPTableViewSelectionShouldChangeSelector) && ![_delegate selectionShouldChangeInTableView:self]))
|
||||
return;
|
||||
|
||||
[self selectRowIndexes:[CPIndexSet indexSet] byExtendingSelection:NO];
|
||||
[[CPNotificationCenter defaultCenter] postNotificationName:CPTableViewSelectionDidChangeNotification object:self userInfo:nil];
|
||||
}
|
||||
|
||||
- (void)editColumn:(int)columnIndex row:(int)rowIndex withEvent:(CPEvent)theEvent select:(BOOL)flag
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
@ignore
|
||||
*/
|
||||
- (void)_updateSelectionWithMouseAtRow:(int)aRow
|
||||
{
|
||||
// Make a preliminary new selection
|
||||
var newSelection;
|
||||
if (_allowsMultipleSelection)
|
||||
newSelection = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(MIN(aRow, _selectionStartRow), ABS(aRow-_selectionStartRow)+1)];
|
||||
else if (aRow >= 0 && aRow < _numberOfRows)
|
||||
newSelection = [CPIndexSet indexSetWithIndex:aRow];
|
||||
else
|
||||
newSelection = [CPIndexSet indexSet];
|
||||
|
||||
// If cmd/ctrl was held down XOR the old selection with the proposed selection
|
||||
if (_allowsMultipleSelection && _selectionModifier & (CPCommandKeyMask | CPControlKeyMask | CPAlternateKeyMask))
|
||||
{
|
||||
// A = newSelection, B = _previousSelectedRowIndexes
|
||||
// (A intersection B) = (A - (A - B))
|
||||
var intersection = [newSelection copy],
|
||||
difference = [newSelection copy];
|
||||
[difference removeIndexes:_previousSelectedRowIndexes];
|
||||
[intersection removeIndexes:difference]
|
||||
|
||||
// (A xor B) = (A + B) - (A intersection B)
|
||||
[newSelection addIndexes:_previousSelectedRowIndexes];
|
||||
[newSelection removeIndexes:intersection];
|
||||
|
||||
// FIXME: if multiple selection is off, and we cmd/ctrl click the previously selected row, then deselect it.
|
||||
}
|
||||
|
||||
// if the new selection is different than the old selection
|
||||
if (![newSelection isEqualToIndexSet:_selectedRowIndexes])
|
||||
{
|
||||
// ask the delegate if we should change the selection
|
||||
if ((_delegateSelectorsCache & _CPTableViewSelectionShouldChangeSelector) && ![_delegate selectionShouldChangeInTableView:self])
|
||||
return;
|
||||
|
||||
// ask the delegate which indexes can be selected. selectionIndexesForProposedSelection is faster than shouldSelectRow
|
||||
if (_delegateSelectorsCache & _CPTableViewSelectionIndexesForProposedSelectionSelector)
|
||||
newSelection = [_delegate tableView:self selectionIndexesForProposedSelection:newSelection];
|
||||
else if (_delegateSelectorsCache & _CPTableViewShouldSelectRowSelector)
|
||||
{
|
||||
var indexes = [];
|
||||
[newSelection getIndexes:indexes maxCount:Number.MAX_VALUE inIndexRange:nil];
|
||||
for (var i = 0; i < indexes.length; i++)
|
||||
if (![_delegate tableView:self shouldSelectRow:indexes[i]])
|
||||
[newSelection removeIndex:indexes[i]];
|
||||
}
|
||||
}
|
||||
|
||||
// if empty selection is not allowed and the new selection has nothing selected, abort
|
||||
if (!_allowsEmptySelection && [newSelection count] === 0)
|
||||
return;
|
||||
|
||||
// if the new selection is *still* different, and update the selection and send a notification
|
||||
if (![newSelection isEqualToIndexSet:_selectedRowIndexes])
|
||||
{
|
||||
[self selectRowIndexes:newSelection byExtendingSelection:NO];
|
||||
[[CPNotificationCenter defaultCenter] postNotificationName:CPTableViewSelectionIsChangingNotification object:self userInfo:nil];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ignore
|
||||
*/
|
||||
- (void)mouseDown:(CPEvent)anEvent
|
||||
{
|
||||
[self trackSelection:anEvent];
|
||||
}
|
||||
|
||||
/*
|
||||
@ignore
|
||||
*/
|
||||
- (void)trackSelection:(CPEvent)anEvent
|
||||
{
|
||||
var type = [anEvent type],
|
||||
point = [self convertPoint:[anEvent locationInWindow] fromView:nil],
|
||||
currentRow = MAX(0, MIN(_numberOfRows-1, [self _rowAtY:point.y]));
|
||||
|
||||
if (type == CPLeftMouseUp)
|
||||
{
|
||||
if ([anEvent clickCount] === 2)
|
||||
{
|
||||
CPLog.warn("edit?!");
|
||||
}
|
||||
else if (![_previousSelectedRowIndexes isEqualToIndexSet:_selectedRowIndexes])
|
||||
{
|
||||
[[CPNotificationCenter defaultCenter] postNotificationName:CPTableViewSelectionDidChangeNotification object:self userInfo:nil];
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == CPLeftMouseDown)
|
||||
{
|
||||
_previousSelectedRowIndexes = _selectedRowIndexes;
|
||||
_selectionModifier = [anEvent modifierFlags];
|
||||
|
||||
if (_selectionModifier & CPShiftKeyMask)
|
||||
_selectionStartRow = (ABS([_previousSelectedRowIndexes firstIndex] - currentRow) < ABS([_previousSelectedRowIndexes lastIndex] - currentRow)) ?
|
||||
[_previousSelectedRowIndexes firstIndex] : [_previousSelectedRowIndexes lastIndex];
|
||||
else
|
||||
_selectionStartRow = currentRow;
|
||||
|
||||
[self _updateSelectionWithMouseAtRow:currentRow];
|
||||
}
|
||||
else if (type == CPLeftMouseDragged)
|
||||
{
|
||||
[self _updateSelectionWithMouseAtRow:currentRow];
|
||||
}
|
||||
|
||||
[CPApp setTarget:self selector:@selector(trackSelection:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
|
||||
}
|
||||
|
||||
/*
|
||||
@ignore
|
||||
*/
|
||||
- (void)_drawSelection
|
||||
{
|
||||
if (!_currentlySelected) {
|
||||
_currentlySelected = [CPIndexSet indexSet];
|
||||
_selectionViews = [];
|
||||
_selectionViewsPool = [];
|
||||
}
|
||||
|
||||
// TODO: we could also remove selections that aren't visible, but then we'll need to run this on every scroll/resize?
|
||||
|
||||
// get array of indexes we can remove
|
||||
var removeSet = [_currentlySelected copy],
|
||||
indexesToRemove = [];
|
||||
[removeSet removeIndexes:_selectedRowIndexes];
|
||||
[removeSet getIndexes:indexesToRemove maxCount:Number.MAX_VALUE inIndexRange:nil];
|
||||
|
||||
// get array of indexes we need to add
|
||||
var addSet = [_selectedRowIndexes copy],
|
||||
indexesToAdd = [];
|
||||
[addSet removeIndexes:_currentlySelected];
|
||||
[addSet getIndexes:indexesToAdd maxCount:Number.MAX_VALUE inIndexRange:nil];
|
||||
|
||||
for (var i = 0; i < indexesToRemove.length; i++)
|
||||
{
|
||||
var row = indexesToRemove[i];
|
||||
for (var column = 0; column < _numberOfColumns; column++)
|
||||
if ([_tableCells[column][row] respondsToSelector:@selector(highlight:)])
|
||||
[_tableCells[column][row] highlight:NO];
|
||||
}
|
||||
for (var i = 0; i < indexesToAdd.length; i++)
|
||||
{
|
||||
var row = indexesToAdd[i];
|
||||
for (var column = 0; column < _numberOfColumns; column++)
|
||||
if ([_tableCells[column][row] respondsToSelector:@selector(highlight:)])
|
||||
[_tableCells[column][row] highlight:YES];
|
||||
}
|
||||
|
||||
// add each one we need to add, taking the selection views from removed seelctions, the pool, or new
|
||||
for (var i = 0; i < indexesToAdd.length; i++)
|
||||
{
|
||||
var index = indexesToAdd[i],
|
||||
view;
|
||||
|
||||
if (indexesToRemove.length > 0)
|
||||
{
|
||||
view = _selectionViews[indexesToRemove.pop()];
|
||||
}
|
||||
else if (_selectionViewsPool.length > 0)
|
||||
{
|
||||
view = _selectionViewsPool.pop();
|
||||
[self addSubview:view positioned:CPWindowBelow relativeTo:nil];
|
||||
}
|
||||
else
|
||||
{
|
||||
view = [[CPView alloc] init];
|
||||
[view setBackgroundColor:[CPColor alternateSelectedControlColor]];
|
||||
|
||||
[self addSubview:view positioned:CPWindowBelow relativeTo:nil];
|
||||
}
|
||||
|
||||
_selectionViews[index] = view;
|
||||
|
||||
var frame = [self rectOfRow:index];
|
||||
frame.size.height += _intercellSpacing.height - 1;
|
||||
//frame.size.width += 500;
|
||||
|
||||
[view setFrame:frame];
|
||||
}
|
||||
|
||||
// remove any selections that weren't already reused
|
||||
for (var i = 0; i < indexesToRemove.length; i++)
|
||||
{
|
||||
var row = indexesToRemove[i],
|
||||
view = _selectionViews[row];
|
||||
|
||||
[view removeFromSuperview];
|
||||
_selectionViewsPool.push(view);
|
||||
}
|
||||
|
||||
// update the currently selected index set
|
||||
_currentlySelected = [_selectedRowIndexes copy];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
var CPTableViewDataSourceKey = @"CPTableViewDataSourceKey",
|
||||
CPTableViewDelegateKey = @"CPTableViewDelegateKey",
|
||||
CPTableViewHeaderViewKey = @"CPTableViewHeaderViewKey",
|
||||
CPTableViewTableColumnsKey = @"CPTableViewTableColumnsKey",
|
||||
CPTableViewRowHeightKey = @"CPTableViewRowHeightKey",
|
||||
CPTableViewIntercellSpacingKey = @"CPTableViewIntercellSpacingKey",
|
||||
CPTableViewMultipleSelectionKey = @"CPTableViewMultipleSelectionKey",
|
||||
CPTableViewEmptySelectionKey = @"CPTableViewEmptySelectionKey";
|
||||
|
||||
@implementation CPTableView (CPCoding)
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
if (self = [super initWithCoder:aCoder])
|
||||
{
|
||||
[self _init];
|
||||
|
||||
_dataSource = [aCoder decodeObjectForKey:CPTableViewDataSourceKey];
|
||||
_delegate = [aCoder decodeObjectForKey:CPTableViewDelegateKey];
|
||||
|
||||
_rowHeight = [aCoder decodeFloatForKey:CPTableViewRowHeightKey];
|
||||
_intercellSpacing = [aCoder decodeSizeForKey:CPTableViewIntercellSpacingKey];
|
||||
|
||||
_allowsMultipleSelection = [aCoder decodeBoolForKey:CPTableViewMultipleSelectionKey];
|
||||
_allowsEmptySelection = [aCoder decodeBoolForKey:CPTableViewEmptySelectionKey];
|
||||
|
||||
var tableColumns = [aCoder decodeObjectForKey:CPTableViewTableColumnsKey];
|
||||
for (var i = 0; i < tableColumns.length; i++)
|
||||
[self addTableColumn:tableColumns[i]];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[super encodeWithCoder:aCoder];
|
||||
|
||||
[aCoder encodeObject:_dataSource forKey:CPTableViewDataSourceKey];
|
||||
[aCoder encodeObject:_delegate forKey:CPTableViewDelegateKey];
|
||||
|
||||
[aCoder encodeObject:_tableColumns forKey:CPTableViewTableColumnsKey];
|
||||
|
||||
[aCoder encodeFloat:_rowHeight forKey:CPTableViewRowHeightKey];
|
||||
[aCoder encodeSize:_intercellSpacing forKey:CPTableViewIntercellSpacingKey];
|
||||
|
||||
[aCoder encodeBool:_allowsMultipleSelection forKey:CPTableViewMultipleSelectionKey];
|
||||
[aCoder encodeBool:_allowsEmptySelection forKey:CPTableViewEmptySelectionKey];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
@implementation CPColor (TableView)
|
||||
|
||||
+ (CPColor)alternateSelectedControlColor
|
||||
{
|
||||
return [[CPColor alloc] _initWithRGBA:[0.22, 0.46, 0.84, 1.0]];
|
||||
}
|
||||
|
||||
+ (CPColor)secondarySelectedControlColor
|
||||
{
|
||||
return [[CPColor alloc] _initWithRGBA:[0.83, 0.83, 0.83, 1.0]];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -43,6 +43,8 @@
|
||||
@import "NSSegmentedControl.j"
|
||||
@import "NSSlider.j"
|
||||
@import "NSSplitView.j"
|
||||
@import "NSTableColumn.j"
|
||||
@import "NSTableView.j"
|
||||
@import "NSTabView.j"
|
||||
@import "NSTabViewItem.j"
|
||||
@import "NSTextField.j"
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* NSTableColumn.j
|
||||
* nib2cib
|
||||
*
|
||||
* Created by Thomas Robinson.
|
||||
* Copyright 2008, 280 North, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
|
||||
@import <AppKit/CPTableColumn.j>
|
||||
|
||||
@implementation CPTableColumn (NSCoding)
|
||||
|
||||
- (id)NS_initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
self = [self init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_identifier = [aCoder decodeObjectForKey:@"NSIdentifier"];
|
||||
|
||||
var template = [[CPTableColumn alloc] ]
|
||||
|
||||
//var dataViewCell = [aCoder decodeObjectForKey:@"NSDataCell"];
|
||||
|
||||
_dataView = [[CPTextField alloc] initWithFrame:CPRectMakeZero()];
|
||||
[_dataView setValue:[CPColor whiteColor] forThemedAttributeName:"text-color" inControlState:CPControlStateHighlighted];
|
||||
|
||||
//_headerView = [aCoder decodeObjectForKey:@"NSHeaderCell"];
|
||||
_headerView = [[CPTextField alloc] initWithFrame:CPRectMakeZero()];
|
||||
|
||||
_width = [aCoder decodeFloatForKey:@"NSWidth"];
|
||||
_minWidth = [aCoder decodeFloatForKey:@"NSMinWidth"];
|
||||
_maxWidth = [aCoder decodeFloatForKey:@"NSMaxWidth"];
|
||||
|
||||
_resizingMask = [aCoder decodeBoolForKey:@"NSIsResizable"];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation NSTableColumn : CPTableColumn
|
||||
{
|
||||
}
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
return [self NS_initWithCoder:aCoder];
|
||||
}
|
||||
|
||||
- (Class)classForKeyedArchiver
|
||||
{
|
||||
return [CPTableColumn class];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* NSTableView.j
|
||||
* nib2cib
|
||||
*
|
||||
* Created by Thomas Robinson.
|
||||
* Copyright 2008, 280 North, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
|
||||
@import <AppKit/CPTableView.j>
|
||||
|
||||
@implementation CPTableView (NSCoding)
|
||||
|
||||
- (id)NS_initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
self = [super NS_initWithCoder:aCoder];
|
||||
|
||||
if (self)
|
||||
{
|
||||
var flags = [aCoder decodeIntForKey:@"NSTvFlags"];
|
||||
|
||||
//_dataSource = [aCoder decodeObjectForKey:CPTableViewDataSourceKey];
|
||||
//_delegate = [aCoder decodeObjectForKey:CPTableViewDelegateKey];
|
||||
|
||||
//_headerView = [aCoder decodeObjectForKey:@"NSHeaderView"];
|
||||
//[_headerView setTableView:self];
|
||||
|
||||
//_cornerView = [aCoder decodeObjectForKey:@"NSCornerView"];
|
||||
|
||||
_tableColumns = [aCoder decodeObjectForKey:@"NSTableColumns"];
|
||||
[_tableColumns makeObjectsPerformSelector:@selector(setTableView:) withObject:self];
|
||||
|
||||
_rowHeight = [aCoder decodeFloatForKey:@"NSRowHeight"];
|
||||
_intercellSpacing = CGSizeMake([aCoder decodeFloatForKey:"NSIntercellSpacingWidth"], [aCoder decodeFloatForKey:"NSIntercellSpacingHeight"]);
|
||||
|
||||
_allowsMultipleSelection = (flags & 0x08000000) ? YES : NO;
|
||||
_allowsEmptySelection = (flags & 0x10000000) ? YES : NO;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation NSTableView : CPTableView
|
||||
{
|
||||
}
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
return [self NS_initWithCoder:aCoder];
|
||||
}
|
||||
|
||||
- (Class)classForKeyedArchiver
|
||||
{
|
||||
return [CPTableView class];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -71,6 +71,9 @@ var NSBorderlessWindowMask = 0x00,
|
||||
(_windowStyleMask & NSResizableWindowMask ? CPResizableWindowMask : 0) |
|
||||
(_windowStyleMask & NSTexturedBackgroundWindowMask ? NSTexturedBackgroundWindowMask : 0) |
|
||||
(_windowStyleMask & NSHUDBackgroundWindowMask ? CPHUDBackgroundWindowMask : 0);
|
||||
|
||||
if ([aCoder decodeObjectForKey:"NSFrameAutosaveName"] === "CPBorderlessBridgeWindowMask")
|
||||
_windowStyleMask = CPBorderlessBridgeWindowMask;
|
||||
|
||||
/*if (![_windowClass isEqualToString:@"NSPanel"])
|
||||
_windowRect.origin.y -= [NSMainMenuView menuHeight]; // compensation for the additional menu bar
|
||||
|
||||
Reference in new issue
Block a user