mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-08-31 16:03:37 +00:00
Merge branch 'tableview-merge'
This commit is contained in:
@@ -177,7 +177,7 @@
|
||||
*/
|
||||
- (BOOL)isFirstResponder
|
||||
{
|
||||
return [[self window] firstResponder] == self;
|
||||
return [[self window] firstResponder] === self;
|
||||
}
|
||||
|
||||
// Setting the Content
|
||||
|
||||
@@ -375,6 +375,16 @@ var cachedBlackColor,
|
||||
return cachedClearColor;
|
||||
}
|
||||
|
||||
+ (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]];
|
||||
}
|
||||
|
||||
/*!
|
||||
Creates a color using a tile pattern with \c anImage
|
||||
@param the image to tile
|
||||
|
||||
+157
-73
@@ -38,7 +38,8 @@
|
||||
@implementation CPScrollView : CPView
|
||||
{
|
||||
CPClipView _contentView;
|
||||
|
||||
CPClipView _headerClipView;
|
||||
|
||||
BOOL _hasVerticalScroller;
|
||||
BOOL _hasHorizontalScroller;
|
||||
BOOL _autohidesScrollers;
|
||||
@@ -57,23 +58,27 @@
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
{
|
||||
self = [super initWithFrame:aFrame];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
_verticalLineScroll = 10.0;
|
||||
_verticalPageScroll = 10.0;
|
||||
|
||||
|
||||
_horizontalLineScroll = 10.0;
|
||||
_horizontalPageScroll = 10.0;
|
||||
|
||||
_contentView = [[CPClipView alloc] initWithFrame:[self bounds]];
|
||||
|
||||
|
||||
[self addSubview:_contentView];
|
||||
|
||||
|
||||
_headerClipView = [[CPClipView alloc] init];
|
||||
|
||||
[self addSubview:_headerClipView];
|
||||
|
||||
[self setHasVerticalScroller:YES];
|
||||
[self setHasHorizontalScroller:YES];
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -100,24 +105,24 @@
|
||||
*/
|
||||
- (void)setContentView:(CPClipView)aContentView
|
||||
{
|
||||
if (!aContentView)
|
||||
if (_contentView !== aContentView || !aContentView)
|
||||
return;
|
||||
|
||||
|
||||
var documentView = [aContentView documentView];
|
||||
|
||||
|
||||
if (documentView)
|
||||
[documentView removeFromSuperview];
|
||||
|
||||
|
||||
[_contentView removeFromSuperview];
|
||||
|
||||
var size = [self contentSize];
|
||||
|
||||
|
||||
_contentView = aContentView;
|
||||
|
||||
[_contentView setFrame:CGRectMake(0.0, 0.0, size.width, size.height)];
|
||||
|
||||
[_contentView setDocumentView:documentView];
|
||||
|
||||
[self addSubview:_contentView];
|
||||
|
||||
// This will size the content view appropriately, so no need to size it in this method.
|
||||
[self reflectScrolledClipView:_contentView];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -134,8 +139,15 @@
|
||||
*/
|
||||
- (void)setDocumentView:(CPView)aView
|
||||
{
|
||||
[_contentView setDocumentView:aView];
|
||||
[self reflectScrolledClipView:_contentView];
|
||||
[_contentView setDocumentView:aView];
|
||||
[_headerClipView setDocumentView:[self _headerView]];
|
||||
|
||||
var cornerView = [self _cornerView];
|
||||
|
||||
if (cornerView)
|
||||
[self addSubview:cornerView];
|
||||
|
||||
[self reflectScrolledClipView:_contentView];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -166,72 +178,98 @@
|
||||
// [_verticalScroller setEnabled:NO];
|
||||
// [_horizontalScroller setEnabled:NO];
|
||||
}
|
||||
|
||||
|
||||
[_contentView setFrame:[self bounds]];
|
||||
|
||||
[_headerClipView setFrame:_CGRectMakeZero()];
|
||||
|
||||
--_recursionCount;
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var documentFrame = [documentView frame],
|
||||
contentViewFrame = [self bounds],
|
||||
scrollPoint = [_contentView bounds].origin,
|
||||
difference = _CGSizeMake(CPRectGetWidth(documentFrame) - CPRectGetWidth(contentViewFrame), CPRectGetHeight(documentFrame) - CPRectGetHeight(contentViewFrame)),
|
||||
shouldShowVerticalScroller = (!_autohidesScrollers || difference.height > 0.0) && _hasVerticalScroller,
|
||||
shouldShowHorizontalScroller = (!_autohidesScrollers || difference.width > 0.0) && _hasHorizontalScroller,
|
||||
wasShowingVerticalScroller = ![_verticalScroller isHidden],
|
||||
wasShowingHorizontalScroller = ![_horizontalScroller isHidden],
|
||||
verticalScrollerWidth = _CGRectGetWidth([_verticalScroller frame]);
|
||||
horizontalScrollerHeight = _CGRectGetHeight([_horizontalScroller frame]);
|
||||
var documentFrame = [documentView frame], // the size of the whole document
|
||||
contentFrame = [self bounds], // assume it takes up the entire size of the scrollview (no scrollers)
|
||||
headerClipViewFrame = [self headerClipViewFrame],
|
||||
headerClipViewHeight = _CGRectGetHeight(headerClipViewFrame);
|
||||
|
||||
if (_autohidesScrollers)
|
||||
{
|
||||
// Check to see if either affected the other!
|
||||
if (shouldShowVerticalScroller)
|
||||
shouldShowHorizontalScroller = (!_autohidesScrollers || difference.width > -verticalScrollerWidth) && _hasHorizontalScroller;
|
||||
contentFrame.origin.y += headerClipViewHeight;
|
||||
contentFrame.size.height -= headerClipViewHeight;
|
||||
|
||||
if (shouldShowHorizontalScroller)
|
||||
shouldShowVerticalScroller = (!_autohidesScrollers || difference.height > -horizontalScrollerHeight) && _hasVerticalScroller;
|
||||
}
|
||||
|
||||
[_verticalScroller setHidden:!shouldShowVerticalScroller];
|
||||
[_verticalScroller setEnabled:difference.height > 0.0];
|
||||
|
||||
[_horizontalScroller setHidden:!shouldShowHorizontalScroller];
|
||||
[_horizontalScroller setEnabled:difference.width > 0.0];
|
||||
var difference = _CGSizeMake(_CGRectGetWidth(documentFrame) - _CGRectGetWidth(contentFrame), _CGRectGetHeight(documentFrame) - _CGRectGetHeight(contentFrame)),
|
||||
verticalScrollerWidth = _CGRectGetWidth([_verticalScroller frame]),
|
||||
horizontalScrollerHeight = _CGRectGetHeight([_horizontalScroller frame]),
|
||||
hasVerticalScroll = difference.height > 0.0,
|
||||
hasHorizontalScroll = difference.width > 0.0,
|
||||
shouldShowVerticalScroller = _hasVerticalScroller && (!_autohidesScrollers || hasVerticalScroll),
|
||||
shouldShowHorizontalScroller = _hasHorizontalScroller && (!_autohidesScrollers || hasHorizontalScroll);
|
||||
|
||||
// Now we have to account for the shown scrollers affecting the deltas.
|
||||
if (shouldShowVerticalScroller)
|
||||
{
|
||||
var verticalScrollerHeight = CPRectGetHeight(contentViewFrame);
|
||||
|
||||
if (shouldShowHorizontalScroller)
|
||||
verticalScrollerHeight -= horizontalScrollerHeight;
|
||||
|
||||
difference.width += verticalScrollerWidth;
|
||||
contentViewFrame.size.width -= verticalScrollerWidth;
|
||||
|
||||
[_verticalScroller setFloatValue:(difference.height <= 0.0) ? 0.0 : scrollPoint.y / difference.height
|
||||
knobProportion:CPRectGetHeight(contentViewFrame) / CPRectGetHeight(documentFrame)];
|
||||
[_verticalScroller setFrame:CPRectMake(CPRectGetMaxX(contentViewFrame), 0.0, verticalScrollerWidth, verticalScrollerHeight)];
|
||||
hasHorizontalScroll = difference.width > 0.0;
|
||||
shouldShowHorizontalScroller = _hasHorizontalScroller && (!_autohidesScrollers || hasHorizontalScroll);
|
||||
}
|
||||
else if (wasShowingVerticalScroller)
|
||||
[_verticalScroller setFloatValue:0.0 knobProportion:1.0];
|
||||
|
||||
|
||||
if (shouldShowHorizontalScroller)
|
||||
{
|
||||
difference.height += horizontalScrollerHeight;
|
||||
contentViewFrame.size.height -= horizontalScrollerHeight;
|
||||
|
||||
[_horizontalScroller setFloatValue:(difference.width <= 0.0) ? 0.0 : scrollPoint.x / difference.width
|
||||
knobProportion:CPRectGetWidth(contentViewFrame) / CPRectGetWidth(documentFrame)];
|
||||
[_horizontalScroller setFrame:CPRectMake(0.0, CPRectGetMaxY(contentViewFrame), CPRectGetWidth(contentViewFrame), horizontalScrollerHeight)];
|
||||
hasVerticalScroll = difference.height > 0.0;
|
||||
shouldShowVerticalScroller = _hasVerticalScroller && (!_autohidesScrollers || hasVerticalScroll);
|
||||
}
|
||||
|
||||
// We now definitively know which scrollers are shown or not, as well as whether they are showing scroll values.
|
||||
[_verticalScroller setHidden:!shouldShowVerticalScroller];
|
||||
[_verticalScroller setEnabled:hasVerticalScroll];
|
||||
|
||||
[_horizontalScroller setHidden:!shouldShowHorizontalScroller];
|
||||
[_horizontalScroller setEnabled:hasHorizontalScroll];
|
||||
|
||||
// We can thus appropriately account for them changing the content size.
|
||||
if (shouldShowVerticalScroller)
|
||||
contentFrame.size.width -= verticalScrollerWidth;
|
||||
|
||||
if (shouldShowHorizontalScroller)
|
||||
contentFrame.size.height -= horizontalScrollerHeight;
|
||||
|
||||
var scrollPoint = [_contentView bounds].origin,
|
||||
wasShowingVerticalScroller = ![_verticalScroller isHidden],
|
||||
wasShowingHorizontalScroller = ![_horizontalScroller isHidden];
|
||||
|
||||
if (shouldShowVerticalScroller)
|
||||
{
|
||||
var verticalScrollerY = MAX(_CGRectGetHeight([self cornerViewFrame]), headerClipViewHeight),
|
||||
verticalScrollerHeight = _CGRectGetHeight([self bounds]) - verticalScrollerY;
|
||||
|
||||
if (shouldShowHorizontalScroller)
|
||||
verticalScrollerHeight -= horizontalScrollerHeight;
|
||||
|
||||
[_verticalScroller setFloatValue:(difference.height <= 0.0) ? 0.0 : scrollPoint.y / difference.height];
|
||||
[_verticalScroller setKnobProportion:_CGRectGetHeight(contentFrame) / _CGRectGetHeight(documentFrame)];
|
||||
[_verticalScroller setFrame:_CGRectMake(_CGRectGetMaxX(contentFrame), verticalScrollerY, verticalScrollerWidth, verticalScrollerHeight)];
|
||||
}
|
||||
else if (wasShowingVerticalScroller)
|
||||
{
|
||||
[_verticalScroller setFloatValue:0.0];
|
||||
[_verticalScroller setKnobProportion:1.0];
|
||||
}
|
||||
|
||||
if (shouldShowHorizontalScroller)
|
||||
{
|
||||
[_horizontalScroller setFloatValue:(difference.width <= 0.0) ? 0.0 : scrollPoint.x / difference.width];
|
||||
[_horizontalScroller setKnobProportion:_CGRectGetWidth(contentFrame) / _CGRectGetWidth(documentFrame)];
|
||||
[_horizontalScroller setFrame:_CGRectMake(0.0, _CGRectGetMaxY(contentFrame), _CGRectGetWidth(contentFrame), horizontalScrollerHeight)];
|
||||
}
|
||||
else if (wasShowingHorizontalScroller)
|
||||
[_horizontalScroller setFloatValue:0.0 knobProportion:1.0];
|
||||
|
||||
[_contentView setFrame:contentViewFrame];
|
||||
|
||||
{
|
||||
[_horizontalScroller setFloatValue:0.0];
|
||||
[_horizontalScroller setKnobProportion:1.0];
|
||||
}
|
||||
|
||||
[_contentView setFrame:contentFrame];
|
||||
[_headerClipView setFrame:headerClipViewFrame];
|
||||
[[self _cornerView] setFrame:[self cornerViewFrame]];
|
||||
|
||||
--_recursionCount;
|
||||
}
|
||||
|
||||
@@ -280,7 +318,7 @@
|
||||
_hasHorizontalScroller = shouldHaveHorizontalScroller;
|
||||
|
||||
if (_hasHorizontalScroller && !_horizontalScroller)
|
||||
[self setHorizontalScroller:[[CPScroller alloc] initWithFrame:CGRectMake(0.0, 0.0, CPRectGetWidth([self bounds]), [CPScroller scrollerWidth])]];
|
||||
[self setHorizontalScroller:[[CPScroller alloc] initWithFrame:CGRectMake(0.0, 0.0, _CGRectGetWidth([self bounds]), [CPScroller scrollerWidth])]];
|
||||
|
||||
else if (!_hasHorizontalScroller && _horizontalScroller)
|
||||
{
|
||||
@@ -343,7 +381,7 @@
|
||||
_hasVerticalScroller = shouldHaveVerticalScroller;
|
||||
|
||||
if (_hasVerticalScroller && !_verticalScroller)
|
||||
[self setVerticalScroller:[[CPScroller alloc] initWithFrame:CPRectMake(0.0, 0.0, [CPScroller scrollerWidth], CPRectGetHeight([self bounds]))]];
|
||||
[self setVerticalScroller:[[CPScroller alloc] initWithFrame:_CGRectMake(0.0, 0.0, [CPScroller scrollerWidth], _CGRectGetHeight([self bounds]))]];
|
||||
|
||||
else if (!_hasVerticalScroller && _verticalScroller)
|
||||
{
|
||||
@@ -384,13 +422,56 @@
|
||||
{
|
||||
return _autohidesScrollers;
|
||||
}
|
||||
/*
|
||||
- (void)setFrameSize:(CPRect)aSize
|
||||
|
||||
- (CPView)_headerView
|
||||
{
|
||||
[super setFrameSize:aSize];
|
||||
var documentView = [self documentView];
|
||||
|
||||
[self reflectScrolledClipView:_contentView];
|
||||
}*/
|
||||
if ([documentView respondsToSelector:@selector(headerView)])
|
||||
return [documentView headerView];
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (CPView)_cornerView
|
||||
{
|
||||
var documentView = [self documentView];
|
||||
|
||||
if ([documentView respondsToSelector:@selector(cornerView)])
|
||||
return [documentView cornerView];
|
||||
}
|
||||
|
||||
- (CGRect)cornerViewFrame
|
||||
{
|
||||
var cornerView = [self _cornerView],
|
||||
cornerBounds = [cornerView bounds],
|
||||
bounds = [self bounds];
|
||||
|
||||
if (!cornerView)
|
||||
return _CGRectMakeZero();
|
||||
|
||||
cornerBounds.origin.x = CGRectGetMaxX(bounds) - CGRectGetWidth(cornerBounds);
|
||||
cornerBounds.origin.y = 0;
|
||||
|
||||
return cornerBounds;
|
||||
}
|
||||
|
||||
- (CGRect)headerClipViewFrame
|
||||
{
|
||||
var headerView = [self _headerView],
|
||||
cornerView = [self _cornerView],
|
||||
bounds = [self bounds];
|
||||
|
||||
if (!headerView)
|
||||
return _CGRectMakeZero();
|
||||
|
||||
bounds.size.height = [headerView bounds].size.height;
|
||||
|
||||
if (cornerView)
|
||||
bounds.size.width -= CGRectGetWidth([cornerView bounds]);
|
||||
|
||||
return bounds;
|
||||
}
|
||||
|
||||
/* @ignore */
|
||||
- (void)_verticalScrollerDidScroll:(CPScroller)aScroller
|
||||
@@ -448,6 +529,7 @@
|
||||
}
|
||||
|
||||
[_contentView scrollToPoint:contentBounds.origin];
|
||||
[_headerClipView scrollToPoint:CGPointMake(contentBounds.origin.x, 0.0)];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -587,6 +669,7 @@
|
||||
contentBounds.origin.y += [anEvent deltaY] * _verticalLineScroll;
|
||||
|
||||
[_contentView scrollToPoint:contentBounds.origin];
|
||||
[_headerClipView scrollToPoint:CGPointMake(contentBounds.origin.x, 0.0)];
|
||||
}
|
||||
|
||||
- (void)keyDown:(CPEvent)anEvent
|
||||
@@ -625,6 +708,7 @@
|
||||
}
|
||||
|
||||
[_contentView scrollToPoint:contentBounds.origin];
|
||||
[_headerClipView scrollToPoint:CGPointMake(contentBounds.origin, 0)];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+31
-30
@@ -64,9 +64,9 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
|
||||
|
||||
BOOL _isVertical @accessors(readonly, getter=isVertical);
|
||||
float _knobProportion;
|
||||
|
||||
|
||||
CPScrollerPart _hitPart;
|
||||
|
||||
|
||||
CPScrollerPart _trackingPart;
|
||||
float _trackingFloatValue;
|
||||
CGPoint _trackingStartPoint;
|
||||
@@ -104,11 +104,12 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
|
||||
_controlSize = CPRegularControlSize;
|
||||
_partRects = [];
|
||||
|
||||
[self setFloatValue:0.0 knobProportion:1.0];
|
||||
[self setFloatValue:0.0];
|
||||
[self setKnobProportion:1.0];
|
||||
|
||||
_hitPart = CPScrollerNoPart;
|
||||
|
||||
[self _recalculateIsVertical];
|
||||
[self _calculateIsVertical];
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -155,28 +156,17 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
|
||||
return _controlSize;
|
||||
}
|
||||
|
||||
// Setting the Knob Position
|
||||
/*!
|
||||
Sets the scroller's knob position (ranges from 0.0 to 1.0).
|
||||
@param aValue the knob position (ranges from 0.0 to 1.0)
|
||||
*/
|
||||
- (void)setFloatValue:(float)aValue
|
||||
- (void)setObjectValue:(id)aValue
|
||||
{
|
||||
[super setFloatValue:MIN(1.0, MAX(0.0, aValue))];
|
||||
|
||||
[self setNeedsLayout];
|
||||
[super setObjectValue:MIN(1.0, MAX(0.0, +aValue))];
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the position and proportion of the knob.
|
||||
@param aValue the knob position (ranges from 0.0 to 1.0)
|
||||
@param aProportion the knob's proportion (ranges from 0.0 to 1.0)
|
||||
*/
|
||||
- (void)setFloatValue:(float)aValue knobProportion:(float)aProportion
|
||||
- (void)setKnobProportion:(float)aProportion
|
||||
{
|
||||
_knobProportion = MIN(1.0, MAX(0.0001, aProportion));
|
||||
|
||||
[self setFloatValue:aValue];
|
||||
[self setNeedsDisplay:YES];
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -533,15 +523,15 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
|
||||
|
||||
}
|
||||
|
||||
- (void)_recalculateIsVertical
|
||||
- (void)_calculateIsVertical
|
||||
{
|
||||
// Recalculate isVertical.
|
||||
var bounds = [self bounds],
|
||||
width = _CGRectGetWidth(bounds),
|
||||
height = _CGRectGetHeight(bounds);
|
||||
|
||||
|
||||
_isVertical = width < height ? 1 : (width > height ? 0 : -1);
|
||||
|
||||
|
||||
if (_isVertical === 1)
|
||||
[self setThemeState:CPThemeStateVertical];
|
||||
else if (_isVertical === 0)
|
||||
@@ -552,8 +542,6 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
|
||||
{
|
||||
[super setFrameSize:aSize];
|
||||
|
||||
[self _recalculateIsVertical];
|
||||
|
||||
[self checkSpaceForParts];
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
@@ -590,18 +578,16 @@ var CPScrollerControlSizeKey = "CPScrollerControlSize",
|
||||
_controlSize = CPRegularControlSize;
|
||||
if ([aCoder containsValueForKey:CPScrollerControlSizeKey])
|
||||
_controlSize = [aCoder decodeIntForKey:CPScrollerControlSizeKey];
|
||||
|
||||
|
||||
_knobProportion = 1.0;
|
||||
if ([aCoder containsValueForKey:CPScrollerKnobProportionKey])
|
||||
_knobProportion = [aCoder decodeFloatForKey:CPScrollerKnobProportionKey];
|
||||
|
||||
|
||||
_partRects = [];
|
||||
|
||||
_hitPart = CPScrollerNoPart;
|
||||
|
||||
[self _recalculateIsVertical];
|
||||
// [self checkSpaceForParts];
|
||||
// [self setNeedsLayout];
|
||||
[self _calculateIsVertical];
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -616,3 +602,18 @@ var CPScrollerControlSizeKey = "CPScrollerControlSize",
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPScroller (Deprecated)
|
||||
|
||||
/*!
|
||||
Sets the position and proportion of the knob.
|
||||
@param aValue the knob position (ranges from 0.0 to 1.0)
|
||||
@param aProportion the knob's proportion (ranges from 0.0 to 1.0)
|
||||
*/
|
||||
- (void)setFloatValue:(float)aValue knobProportion:(float)aProportion
|
||||
{
|
||||
[self setFloatValue:aValue];
|
||||
[self setKnobProportion:aProportion];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+262
-318
@@ -3,7 +3,7 @@
|
||||
* AppKit
|
||||
*
|
||||
* Created by Francisco Tolmasky.
|
||||
* Copyright 2008, 280 North, Inc.
|
||||
* Copyright 2009, 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
|
||||
@@ -20,12 +20,13 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
@import <Foundation/Foundation.j>
|
||||
@import <Foundation/CPDictionary.j>
|
||||
@import <Foundation/CPObject.j>
|
||||
@import <Foundation/CPSortDescriptor.j>
|
||||
@import <Foundation/CPString.j>
|
||||
|
||||
@import "CPTableHeaderView.j"
|
||||
|
||||
/*
|
||||
@ignore
|
||||
*/
|
||||
|
||||
/*
|
||||
@global
|
||||
@@ -43,389 +44,290 @@ CPTableColumnAutoresizingMask = 1;
|
||||
*/
|
||||
CPTableColumnUserResizingMask = 2;
|
||||
|
||||
#define PurgableInfoMake(aView, aRow) { view:(aView), row:(aRow) }
|
||||
#define PurgableInfoView(anInfo) ((anInfo).view)
|
||||
#define PurgableInfoRow(anInfo) ((anInfo).row)
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPTableColumn
|
||||
|
||||
An CPTableColumn object mainly keeps information about the width of the column, its minimum and maximum width; whether the column can be edited or resized; and the cells used to draw the column header and the data in the column. You can change all these attributes of the column by calling the appropriate methods. Please note that the table column does not hold nor has access to the data to be displayed in the column; this data is maintained in the table view's data source.</p>
|
||||
|
||||
<p>Each CPTableColumn object is identified by a CPString, called the column identifier. The reason is that, after a column has been added to a table view, the user might move the columns around, so there is a need to identify the columns regardless of its position in the table.
|
||||
|
||||
@ignore
|
||||
*/
|
||||
@implementation CPTableColumn : CPObject
|
||||
{
|
||||
CPString _identifier;
|
||||
CPView _headerView;
|
||||
|
||||
CPTableView _tableView;
|
||||
|
||||
float _width;
|
||||
float _minWidth;
|
||||
float _maxWidth;
|
||||
|
||||
unsigned _resizingMask;
|
||||
CPTableView _tableView;
|
||||
CPView _headerView;
|
||||
CPView _dataView;
|
||||
Object _dataViewData;
|
||||
|
||||
CPView _dataView; // default data view for this column
|
||||
float _width;
|
||||
float _minWidth;
|
||||
float _maxWidth;
|
||||
|
||||
Object _dataViewData; // cache of data view archives (key=data view UID, value=data view archive)
|
||||
Object _dataViewForView; // mapping from view instances back to their data view prototype (key=view instance UID, value=data view)
|
||||
Object _purgableInfosForDataView; // (key=data view UID, value=)
|
||||
id _identifier;
|
||||
BOOL _isEditable;
|
||||
CPSortDescriptor _sortDescriptorPrototype;
|
||||
BOOL _isHidden;
|
||||
CPString _headerToolTip;
|
||||
}
|
||||
|
||||
/*!
|
||||
Initializes the table column with the specified identifier.
|
||||
@param anIdentifier the identifier
|
||||
@return the initialized table column
|
||||
*/
|
||||
- (id)initWithIdentifier:(CPString)anIdentifier
|
||||
- (id)initWithIdentifier:(id)anIdentifier
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
[self _init];
|
||||
|
||||
_identifier = anIdentifier;
|
||||
|
||||
_width = 40.0;
|
||||
_minWidth = 8.0;
|
||||
_maxWidth = 1000.0;
|
||||
|
||||
var dataView = [[CPTextField alloc] initWithFrame:CPRectMakeZero()];
|
||||
[dataView setValue:[CPColor whiteColor] forThemeAttribute:"text-color" inState:CPThemeStateHighlighted];
|
||||
|
||||
[self setDataView:dataView];
|
||||
|
||||
_headerView = [[CPTextField alloc] initWithFrame:CPRectMakeZero()];
|
||||
[_headerView setBackgroundColor:[CPColor greenColor]];
|
||||
_dataViewData = { };
|
||||
|
||||
_width = 100.0;
|
||||
_minWidth = 10.0;
|
||||
_maxWidth = 1000000.0;
|
||||
|
||||
[self setIdentifier:anIdentifier];
|
||||
[self setHeaderView:[CPTextField new]];
|
||||
[self setDataView:[CPTextField new]];
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)_init
|
||||
{
|
||||
_dataViewData = {};
|
||||
_dataViewForView = {};
|
||||
_purgableInfosForDataView = {};
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the table column's identifier
|
||||
@param anIdentifier the new identifier
|
||||
*/
|
||||
- (void)setIdentifier:(CPString)anIdentifier
|
||||
{
|
||||
_identifier = anIdentifier;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the table column's identifier
|
||||
*/
|
||||
- (CPString)identifier
|
||||
{
|
||||
return _identifier;
|
||||
}
|
||||
|
||||
// Setting the CPTableView
|
||||
/*!
|
||||
Sets the table's view. This is called automatically by Cappuccino.
|
||||
@param aTableView the new table view
|
||||
*/
|
||||
- (void)setTableView:(CPTableView)aTableView
|
||||
{
|
||||
_tableView = aTableView;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the column's table view.
|
||||
*/
|
||||
- (CPTableView)tableView
|
||||
{
|
||||
return _tableView;
|
||||
}
|
||||
|
||||
// Controlling size
|
||||
/*!
|
||||
Sets the column's width.
|
||||
@param aWidth the new column width
|
||||
*/
|
||||
- (void)setWidth:(float)aWidth
|
||||
{
|
||||
_width = aWidth;
|
||||
aWidth = +aWidth;
|
||||
|
||||
if (_width === aWidth)
|
||||
return;
|
||||
|
||||
var newWidth = MIN(MAX(aWidth, [self minWidth]), [self maxWidth]);
|
||||
|
||||
if (_width === newWidth)
|
||||
return;
|
||||
|
||||
var oldWidth = _width;
|
||||
|
||||
_width = newWidth;
|
||||
|
||||
var tableView = [self tableView];
|
||||
|
||||
if (tableView)
|
||||
[[CPNotificationCenter defaultCenter]
|
||||
postNotificationName:CPTableViewColumnDidResizeNotification
|
||||
object:tableView
|
||||
userInfo:[CPDictionary dictionaryWithObjects:[self, oldWidth] forKeys:[@"CPTableColumn", "CPOldWidth"]]];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the column's width
|
||||
*/
|
||||
- (float)width
|
||||
{
|
||||
return _width;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets column's minimum width.
|
||||
@param aWidth the new minimum column width
|
||||
*/
|
||||
- (void)setMinWidth:(float)aWidth
|
||||
- (void)setMinWidth:(float)aMinWidth
|
||||
{
|
||||
if (_width < (_minWidth = aWidth))
|
||||
[self setWidth:_minWidth];
|
||||
aMinWidth = +aMinWidth;
|
||||
|
||||
if (_minWidth === aMinWidth)
|
||||
return;
|
||||
|
||||
_minWidth = aMinWidth;
|
||||
|
||||
var width = [self width],
|
||||
newWidth = MAX(width, [self minWidth]);
|
||||
|
||||
if (width !== newWidth)
|
||||
[self setWidth:newWidth];
|
||||
}
|
||||
|
||||
/*!
|
||||
The column's minimum width
|
||||
*/
|
||||
- (float)minWidth
|
||||
{
|
||||
return _minWidth;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the column's maximum width.
|
||||
@param aWidth the new maximum width
|
||||
*/
|
||||
- (void)setMaxWidth:(float)aWidth
|
||||
- (void)setMaxWidth:(float)aMaxWidth
|
||||
{
|
||||
if (_width > (_maxmimumWidth = aWidth))
|
||||
[self setWidth:_maxWidth];
|
||||
aMaxWidth = +aMaxWidth;
|
||||
|
||||
if (_maxWidth === aMaxWidth)
|
||||
return;
|
||||
|
||||
_maxWidth = aMaxWidth;
|
||||
|
||||
var width = [self width],
|
||||
newWidth = MAX(width, [self maxWidth]);
|
||||
|
||||
if (width !== newWidth)
|
||||
[self setWidth:newWidth];
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the resizing mask. The mask is one of:
|
||||
<pre>
|
||||
CPTableColumnNoResizing;
|
||||
CPTableColumnAutoresizingMask;
|
||||
CPTableColumnUserResizingMask;
|
||||
</pre>
|
||||
@param aMask the new resizing mask
|
||||
*/
|
||||
- (void)setResizingMask:(unsigned)aMask
|
||||
- (float)maxWidth
|
||||
{
|
||||
_resizingMask = aMask;
|
||||
return _maxWidth;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the column's resizing mask. One of:
|
||||
<pre>
|
||||
CPTableColumnNoResizing;
|
||||
CPTableColumnAutoresizingMask;
|
||||
CPTableColumnUserResizingMask;
|
||||
</pre>
|
||||
*/
|
||||
- (unsigned)resizingMask
|
||||
- (void)setResizingMask:(unsigned)aResizingMask
|
||||
{
|
||||
_resizingMask = aResizingMask;
|
||||
}
|
||||
|
||||
- (float)resizingMask
|
||||
{
|
||||
return _resizingMask;
|
||||
}
|
||||
|
||||
/*!
|
||||
Resizes the column according to the min, max and set width.
|
||||
*/
|
||||
- (void)sizeToFit
|
||||
{
|
||||
var width = CPRectGetWidth([_headerView frame]);
|
||||
|
||||
if (width < _minWidth)
|
||||
var width = _CGRectGetWidth([_headerView frame]);
|
||||
|
||||
if (width < [self minWidth])
|
||||
[self setMinWidth:width];
|
||||
else if (width > _maxWidth)
|
||||
else if (width > [self maxWidth])
|
||||
[self setMaxWidth:width]
|
||||
|
||||
if (_width != width)
|
||||
if (_width !== width)
|
||||
[self setWidth:width];
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets whether the column in this data is editable.
|
||||
@param aFlag \c YES means the column data is editable
|
||||
*/
|
||||
- (void)setEditable:(BOOL)aFlag
|
||||
//Setting Component Cells
|
||||
- (void)setHeaderView:(CPView)aView
|
||||
{
|
||||
_isEditable = aFlag;
|
||||
if (!aView)
|
||||
[CPException raise:CPInvalidArgumentException reason:@"Attempt to set nil header view on " + [self description]];
|
||||
|
||||
_headerView = aView;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns \c YES if the column data is editable.
|
||||
- (CPView)headerView
|
||||
{
|
||||
return _headerView;
|
||||
}
|
||||
|
||||
- (void)setDataView:(CPView)aView
|
||||
{
|
||||
if (_dataView === aView)
|
||||
return;
|
||||
|
||||
if (_dataView)
|
||||
_dataViewData[[_dataView UID]] = nil;
|
||||
|
||||
_dataView = aView;
|
||||
_dataViewData[[aView UID]] = [CPKeyedArchiver archivedDataWithRootObject:aView];
|
||||
}
|
||||
|
||||
- (CPView)dataView
|
||||
{
|
||||
return _dataView;
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the CPView object used by the CPTableView to draw values for the receiver.
|
||||
|
||||
By default, this method just calls dataView. Subclassers can override if they need to
|
||||
potentially use different cells for different rows. Subclasses should expect this method
|
||||
to be invoked with row equal to -1 in cases where no actual row is involved but the table
|
||||
view needs to get some generic cell info.
|
||||
*/
|
||||
- (id)dataViewForRow:(int)aRowIndex
|
||||
{
|
||||
return [self dataView];
|
||||
}
|
||||
|
||||
- (id)_newDataViewForRow:(int)aRowIndex
|
||||
{
|
||||
var dataView = [self dataViewForRow:aRowIndex],
|
||||
dataViewUID = [dataView UID];
|
||||
|
||||
var x = [self tableView]._cachedDataViews[dataViewUID];
|
||||
if (x && x.length)
|
||||
return x.pop();
|
||||
|
||||
// if we haven't cached an archive of the data view, do it now
|
||||
if (!_dataViewData[dataViewUID])
|
||||
_dataViewData[dataViewUID] = [CPKeyedArchiver archivedDataWithRootObject:dataView];
|
||||
|
||||
// unarchive the data view cache
|
||||
var newDataView = [CPKeyedUnarchiver unarchiveObjectWithData:_dataViewData[dataViewUID]];
|
||||
newDataView.identifier = dataViewUID;
|
||||
return newDataView;
|
||||
}
|
||||
|
||||
//Setting the Identifier
|
||||
|
||||
/*
|
||||
Sets the receiver identifier to anIdentifier.
|
||||
*/
|
||||
- (void)setIdentifier:(id)anIdentifier
|
||||
{
|
||||
_identifier = anIdentifier;
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the object used by the data source to identify the attribute corresponding to the receiver.
|
||||
*/
|
||||
- (id)identifier
|
||||
{
|
||||
return _identifier;
|
||||
}
|
||||
|
||||
//Controlling Editability
|
||||
|
||||
/*
|
||||
Controls whether the user can edit cells in the receiver by double-clicking them.
|
||||
*/
|
||||
- (void)setEditable:(BOOL)shouldBeEditable
|
||||
{
|
||||
_isEditable = shouldBeEditable;
|
||||
}
|
||||
|
||||
/*
|
||||
Returns YES if the user can edit cells associated with the receiver by double-clicking the
|
||||
column in the NSTableView, NO otherwise.
|
||||
*/
|
||||
- (BOOL)isEditable
|
||||
{
|
||||
return _isEditable;
|
||||
}
|
||||
|
||||
//Setting the column header view
|
||||
|
||||
/*!
|
||||
Sets the view that draws the column's header.
|
||||
@param aHeaderView the view that will draws the column header
|
||||
*/
|
||||
|
||||
- (void)setHeaderView:(CPView)aView
|
||||
//Sorting
|
||||
- (void)setSortDescriptorPrototype:(CPSortDescriptor)aSortDescriptor
|
||||
{
|
||||
_headerView = aView;
|
||||
_sortDescriptorPrototype = aSortDescriptor;
|
||||
}
|
||||
|
||||
/*!
|
||||
Return the view that draws the column's header
|
||||
*/
|
||||
- (CPView)headerView
|
||||
- (CPSortDescriptor)sortDescriptorPrototype
|
||||
{
|
||||
return _headerView;
|
||||
return _sortDescriptorPrototype;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the data cell that draws rows in this column.
|
||||
*/
|
||||
- (void)setDataCell:(CPView <CPCoding>)aView
|
||||
//Setting Column Visibility
|
||||
|
||||
- (void)setHidden:(BOOL)shouldBeHidden
|
||||
{
|
||||
[self setDataView:aView];
|
||||
_isHidden = shouldBeHidden;
|
||||
}
|
||||
|
||||
- (BOOL)isHidden
|
||||
{
|
||||
return _isHidden;
|
||||
}
|
||||
|
||||
//Setting Tool Tips
|
||||
|
||||
/*
|
||||
Sets the data view that draws rows in this column.
|
||||
Sets the tooltip string that is displayed when the cursor pauses over the
|
||||
header cell of the receiver.
|
||||
*/
|
||||
- (void)setDataView:(CPView <CPCoding>)aView
|
||||
- (void)setHeaderToolTip:(CPString)aToolTip
|
||||
{
|
||||
if (_dataView)
|
||||
_dataViewData[[_dataView UID]] = nil;
|
||||
|
||||
_dataView = aView;
|
||||
_dataViewData[[aView UID]] = [CPKeyedArchiver archivedDataWithRootObject:aView];
|
||||
_headerToolTip = aToolTip;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the data cell that draws rows in this column
|
||||
*/
|
||||
- (CPCell)dataCell
|
||||
- (CPString)headerToolTip
|
||||
{
|
||||
return _dataView;
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the data view that draws rows in this column
|
||||
*/
|
||||
- (CPView)dataView
|
||||
{
|
||||
return [self dataCell];
|
||||
}
|
||||
|
||||
/*!
|
||||
By default returns the value from \c -dataCell. This can
|
||||
be overridden by a subclass to return different cells for different
|
||||
rows.
|
||||
@param aRowIndex the index of the row to obtain the cell for
|
||||
*/
|
||||
- (CPCell)dataCellForRow:(int)aRowIndex
|
||||
{
|
||||
return [self dataView];
|
||||
}
|
||||
|
||||
- (CPView)dataViewForRow:(int)aRowIndex
|
||||
{
|
||||
return [self dataCellForRow:aRowIndex];
|
||||
}
|
||||
/*
|
||||
- (void)_markViewAsPurgable:(CPView)aView
|
||||
{
|
||||
var viewUID = [aView UID],
|
||||
dataViewUID = [_dataViewForView[viewUID] UID];
|
||||
|
||||
if (!_purgableInfosForDataView[dataViewUID])
|
||||
_purgableInfosForDataView[dataViewUID] = [CPDictionary dictionary];
|
||||
|
||||
[_purgableInfosForDataView[dataViewUID] setObject:aView forKey:viewUID];
|
||||
}
|
||||
*/
|
||||
- (void)_markView:(CPView)aView inRow:(unsigned)aRow asPurgable:(BOOL)isPurgable
|
||||
{
|
||||
var viewUID = [aView UID],
|
||||
dataViewUID = [_dataViewForView[viewUID] UID];
|
||||
|
||||
if (!_purgableInfosForDataView[dataViewUID])
|
||||
{
|
||||
if (!isPurgable)
|
||||
return;
|
||||
|
||||
_purgableInfosForDataView[dataViewUID] = {};
|
||||
}
|
||||
|
||||
if (!isPurgable) {
|
||||
if (_purgableInfosForDataView[dataViewUID][viewUID])
|
||||
CPLog.warn("removing unpurgable " + _purgableInfosForDataView[dataViewUID][viewUID]);
|
||||
delete _purgableInfosForDataView[dataViewUID][viewUID];
|
||||
}
|
||||
else
|
||||
_purgableInfosForDataView[dataViewUID][viewUID] = PurgableInfoMake(aView, aRow);
|
||||
}
|
||||
|
||||
- (CPView)_newDataViewForRow:(int)aRowIndex avoidingRows:(CPRange)rows
|
||||
{
|
||||
var view = [self dataViewForRow:aRowIndex],
|
||||
viewUID = [view UID],
|
||||
purgableInfos = _purgableInfosForDataView[viewUID];
|
||||
|
||||
if (purgableInfos)
|
||||
{
|
||||
for (var key in purgableInfos)
|
||||
{
|
||||
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);
|
||||
//}
|
||||
//else
|
||||
// CPLog.warn("avoiding");
|
||||
}
|
||||
}
|
||||
|
||||
// if we haven't cached an archive of the data view, do it now
|
||||
if (!_dataViewData[viewUID])
|
||||
_dataViewData[viewUID] = [CPKeyedArchiver archivedDataWithRootObject:view];
|
||||
|
||||
// unarchive the data view cache
|
||||
var newView = [CPKeyedUnarchiver unarchiveObjectWithData:_dataViewData[viewUID]];
|
||||
|
||||
// map the new view's UID to it's data view prototype
|
||||
_dataViewForView[[newView UID]] = view;
|
||||
|
||||
CPLog.warn("creating cell: %s", newView);
|
||||
|
||||
return newView;
|
||||
}
|
||||
|
||||
- (void)_purge
|
||||
{
|
||||
for (var viewUID in _purgableInfosForDataView)
|
||||
{
|
||||
var purgableInfos = _purgableInfosForDataView[viewUID];
|
||||
|
||||
for (var key in purgableInfos)
|
||||
{
|
||||
var view = PurgableInfoView(purgableInfos[key]);
|
||||
|
||||
if (!view)
|
||||
CPLog.info("key="+key+" view=" + view + " purgableInfos[key]="+purgableInfos[key])
|
||||
else if (view._superview) {
|
||||
//CPLog.error("PURGING: (removing)" + view);
|
||||
//[view removeFromSuperview];
|
||||
[view setHidden:YES];
|
||||
}
|
||||
//else
|
||||
// CPLog.warn("PURGING: (already removed)" + view);
|
||||
|
||||
//delete purgableInfos[key];
|
||||
}
|
||||
}
|
||||
return _headerToolTip;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
var CPTableColumnIdentifierKey = @"CPTableColumnIdentifierKey",
|
||||
CPTableColumnHeaderViewKey = @"CPTableColumnHeaderViewKey",
|
||||
CPTableColumnDataViewKey = @"CPTableColumnDataViewKey",
|
||||
@@ -438,18 +340,26 @@ var CPTableColumnIdentifierKey = @"CPTableColumnIdentifierKey",
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[self _init];
|
||||
|
||||
_identifier = [aCoder decodeObjectForKey:CPTableColumnIdentifierKey];
|
||||
self = [super init];
|
||||
|
||||
[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];
|
||||
if (self)
|
||||
{
|
||||
_dataViewData = { };
|
||||
|
||||
_width = [aCoder decodeFloatForKey:CPTableColumnWidthKey];
|
||||
_minWidth = [aCoder decodeFloatForKey:CPTableColumnMinWidthKey];
|
||||
_maxWidth = [aCoder decodeFloatForKey:CPTableColumnMaxWidthKey];
|
||||
|
||||
[self setIdentifier:[aCoder decodeObjectForKey:CPTableColumnIdentifierKey]];
|
||||
// [self setHeaderView:[aCoder decodeObjectForKey:CPTableColumnHeaderViewKey]];
|
||||
// [self setDataView:[aCoder decodeObjectForKey:CPTableColumnDataViewKey]];
|
||||
|
||||
[self setHeaderView:[CPTextField new]];
|
||||
[self setDataView:[CPTextField new]];
|
||||
|
||||
|
||||
// _resizingMask = [aCoder decodeBoolForKey:CPTableColumnResizingMaskKey];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
@@ -457,15 +367,49 @@ var CPTableColumnIdentifierKey = @"CPTableColumnIdentifierKey",
|
||||
- (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];
|
||||
|
||||
// [aCoder encodeObject:_headerView forKey:CPTableColumnHeaderViewKey];
|
||||
// [aCoder encodeObject:_dataView forKey:CPTableColumnDataViewKey];
|
||||
|
||||
// [aCoder encodeObject:_resizingMask forKey:CPTableColumnResizingMaskKey];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPTableColumn (NSInCompatibility)
|
||||
|
||||
- (void)setHeaderCell:(CPView)aView
|
||||
{
|
||||
[CPException raise:CPUnsupportedMethodException
|
||||
reason:@"setHeaderCell: is not supported. -setHeaderCell:aView instead."];
|
||||
}
|
||||
|
||||
- (CPView)headerCell
|
||||
{
|
||||
[CPException raise:CPUnsupportedMethodException
|
||||
reason:@"headCell is not supported. -headerView instead."];
|
||||
}
|
||||
|
||||
- (void)setDataCell:(CPView)aView
|
||||
{
|
||||
[CPException raise:CPUnsupportedMethodException
|
||||
reason:@"setDataCell: is not supported. Use -setHeaderCell:aView instead."];
|
||||
}
|
||||
|
||||
- (CPView)dataCell
|
||||
{
|
||||
[CPException raise:CPUnsupportedMethodException
|
||||
reason:@"dataCell is not supported. Use -dataCell instead."];
|
||||
}
|
||||
|
||||
- (id)dataCellForRow:(int)row
|
||||
{
|
||||
[CPException raise:CPUnsupportedMethodException
|
||||
reason:@"dataCellForRow: is not supported. Use -dataViewForRow:row instead."];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* CPTableHeaderView.j
|
||||
* AppKit
|
||||
*
|
||||
* Created by Ross Boucher.
|
||||
* Copyright 2009 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 "CPTableColumn.j"
|
||||
@import "CPTableView.j"
|
||||
@import "CPView.j"
|
||||
|
||||
|
||||
@implementation CPTableHeaderView : CPView
|
||||
{
|
||||
int _resizedColumn @accessors(readonly, property=resizedColumn);
|
||||
int _draggedColumn @accessors(readonly, property=draggedColumn);
|
||||
|
||||
float _draggedDistance @accessors(readonly, property=draggedDistance);
|
||||
|
||||
CPTableView _tableView @accessors(property=tableView);
|
||||
}
|
||||
|
||||
- (void)initWithFrame:(CGRect)aFrame
|
||||
{
|
||||
self = [super initWithFrame:aFrame];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_resizedColumn = CPNotFound;
|
||||
_draggedColumn = CPNotFound;
|
||||
_draggedDistance = 0.0;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (int)columnAtPoint:(CGPoint)aPoint
|
||||
{
|
||||
if (!CGRectContainsPoint([self bounds], aPoint))
|
||||
return CPNotFound;
|
||||
|
||||
// at this point, we can essentially ignore height, because all columns have equal heights
|
||||
// and that height is equal to the height of our own bounds, which we know we are inside of
|
||||
|
||||
var index = 0,
|
||||
count = [[_tableView tableColumns] count],
|
||||
tableSpacing = [_tableView intercellSpacing],
|
||||
tableColumns = [_tableView tableColumns],
|
||||
leftOffset = 0,
|
||||
pointX = aPoint.x;
|
||||
|
||||
for (; index < count; index++)
|
||||
{
|
||||
var width = [tableColumns[index] width] + tableSpacing.width;
|
||||
|
||||
if (pointX >= leftOffset && pointX < leftOffset + width)
|
||||
return index;
|
||||
|
||||
leftOffset += width;
|
||||
}
|
||||
|
||||
return CPNotFound;
|
||||
}
|
||||
|
||||
- (CGRect)headerRectOfColumn:(int)aColumnIndex
|
||||
{
|
||||
var tableColumns = [_tableView tableColumns],
|
||||
tableSpacing = [_tableView intercellSpacing],
|
||||
bounds = [self bounds];
|
||||
|
||||
if (aColumnIndex < 0 || aColumnIndex > [tableColumns count])
|
||||
[CPException raise:"invalid" reason:"tried to get headerRectOfColumn: on invalid column"];
|
||||
|
||||
bounds.size.width = [tableColumns[aColumnIndex] width] + tableSpacing.width;
|
||||
|
||||
while (--aColumnIndex >= 0)
|
||||
bounds.origin.x += [tableColumns[aColumnIndex] width] + tableSpacing.width;
|
||||
|
||||
return bounds;
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
var tableColumns = [_tableView tableColumns],
|
||||
count = [tableColumns count],
|
||||
columnRect = [self bounds],
|
||||
spacing = [_tableView intercellSpacing];
|
||||
|
||||
for (i = 0; i < count; ++i)
|
||||
{
|
||||
var column = [tableColumns objectAtIndex:i],
|
||||
headerView = [column headerView];
|
||||
|
||||
columnRect.size.width = [column width] + spacing.width;
|
||||
|
||||
[headerView setFrame:columnRect];
|
||||
|
||||
columnRect.origin.x += [column width] + spacing.width;
|
||||
|
||||
[self addSubview:headerView];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)drawRect:(CGRect)aRect
|
||||
{
|
||||
[[_tableView gridColor] setStroke];
|
||||
|
||||
var context = [[CPGraphicsContext currentContext] graphicsPort],
|
||||
exposedColumnIndexes = exposedColumnIndexes = [_tableView columnIndexesInRect:aRect],
|
||||
columnsArray = [];
|
||||
|
||||
[exposedColumnIndexes getIndexes:columnsArray maxCount:-1 inIndexRange:nil];
|
||||
|
||||
var columnArrayIndex = 0,
|
||||
columnArrayCount = columnsArray.length;
|
||||
|
||||
for(; columnArrayIndex < columnArrayCount; ++columnArrayIndex)
|
||||
{
|
||||
// grab each column rect and add horizontal lines
|
||||
var columnToStroke = [self headerRectOfColumn:columnArrayIndex];
|
||||
|
||||
CGContextBeginPath(context);
|
||||
CGContextMoveToPoint(context, ROUND(columnToStroke.origin.x + columnToStroke.size.width) - 0.5, ROUND(columnToStroke.origin.y) - 0.5);
|
||||
CGContextAddLineToPoint(context, ROUND(columnToStroke.origin.x + columnToStroke.size.width) - 0.5, ROUND(columnToStroke.origin.y + columnToStroke.size.height) - 0.5);
|
||||
CGContextSetLineWidth(context, 1);
|
||||
CGContextStrokePath(context);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
+1575
-1132
File diff suppressed because it is too large
Load Diff
@@ -692,7 +692,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
{
|
||||
var string = [self stringValue];
|
||||
|
||||
if ((!string || [string length] === 0) && ![self hasThemeState:CPThemeStateEditing])
|
||||
if ((!string || string.length === 0) && ![self hasThemeState:CPThemeStateEditing])
|
||||
[self setThemeState:CPTextFieldStatePlaceholder];
|
||||
else
|
||||
[self unsetThemeState:CPTextFieldStatePlaceholder];
|
||||
|
||||
+36
-28
@@ -94,12 +94,13 @@ var DOMElementPrototype = nil,
|
||||
BackgroundTrivialColor = 0,
|
||||
BackgroundVerticalThreePartImage = 1,
|
||||
BackgroundHorizontalThreePartImage = 2,
|
||||
BackgroundNinePartImage = 3,
|
||||
|
||||
CustomDrawRectViews = {},
|
||||
CustomLayoutSubviewsViews = {};
|
||||
BackgroundNinePartImage = 3;
|
||||
#endif
|
||||
|
||||
var CPViewFlags = { },
|
||||
CPViewHasCustomDrawRect = 1 << 0,
|
||||
CPViewHasCustomLayoutSubviews = 1 << 1;
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPView
|
||||
@@ -184,6 +185,8 @@ var DOMElementPrototype = nil,
|
||||
// Key View Support
|
||||
CPView _nextKeyView;
|
||||
CPView _previousKeyView;
|
||||
|
||||
unsigned _viewClassFlags;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -209,6 +212,27 @@ var DOMElementPrototype = nil,
|
||||
CachedNotificationCenter = [CPNotificationCenter defaultCenter];
|
||||
}
|
||||
|
||||
- (void)setupViewFlags
|
||||
{
|
||||
var theClass = [self class],
|
||||
classUID = [theClass UID];
|
||||
|
||||
if (CPViewFlags[classUID] === undefined)
|
||||
{
|
||||
var flags = 0;
|
||||
|
||||
if ([theClass instanceMethodForSelector:@selector(drawRect:)] !== [CPView instanceMethodForSelector:@selector(drawRect:)])
|
||||
flags |= CPViewHasCustomDrawRect;
|
||||
|
||||
if ([theClass instanceMethodForSelector:@selector(layoutSubviews)] !== [CPView instanceMethodForSelector:@selector(layoutSubviews)])
|
||||
flags |= CPViewHasCustomLayoutSubviews;
|
||||
|
||||
CPViewFlags[classUID] = flags;
|
||||
}
|
||||
|
||||
_viewClassFlags = CPViewFlags[classUID];
|
||||
}
|
||||
|
||||
+ (CPSet)keyPathsForValuesAffectingFrame
|
||||
{
|
||||
return [CPSet setWithObjects:@"frameOrigin", @"frameSize"];
|
||||
@@ -261,6 +285,8 @@ var DOMElementPrototype = nil,
|
||||
_theme = [CPTheme defaultTheme];
|
||||
_themeState = CPThemeStateNormal;
|
||||
|
||||
[self setupViewFlags];
|
||||
|
||||
[self _loadThemeAttributes];
|
||||
}
|
||||
|
||||
@@ -1549,16 +1575,7 @@ setBoundsOrigin:
|
||||
- (void)setNeedsDisplayInRect:(CPRect)aRect
|
||||
{
|
||||
#if PLATFORM(DOM)
|
||||
var UID = [[self class] UID],
|
||||
hasCustomDrawRect = CustomDrawRectViews[UID];
|
||||
|
||||
if (!hasCustomDrawRect && typeof hasCustomDrawRect === "undefined")
|
||||
{
|
||||
hasCustomDrawRect = [self methodForSelector:@selector(drawRect:)] != [CPView instanceMethodForSelector:@selector(drawRect:)];
|
||||
CustomDrawRectViews[UID] = hasCustomDrawRect;
|
||||
}
|
||||
|
||||
if (!hasCustomDrawRect)
|
||||
if (!(_viewClassFlags & CPViewHasCustomDrawRect))
|
||||
return;
|
||||
#endif
|
||||
|
||||
@@ -1680,22 +1697,10 @@ setBoundsOrigin:
|
||||
_needsLayout = YES;
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
var UID = [[self class] UID],
|
||||
hasCustomLayoutSubviews = CustomLayoutSubviewsViews[UID];
|
||||
|
||||
if (hasCustomLayoutSubviews === undefined)
|
||||
{
|
||||
hasCustomLayoutSubviews = [self methodForSelector:@selector(layoutSubviews)] != [CPView instanceMethodForSelector:@selector(layoutSubviews)];
|
||||
CustomLayoutSubviewsViews[UID] = hasCustomLayoutSubviews;
|
||||
}
|
||||
|
||||
if (!hasCustomLayoutSubviews)
|
||||
if (!(_viewClassFlags & CPViewHasCustomLayoutSubviews))
|
||||
return;
|
||||
|
||||
if (_needsLayout)
|
||||
{
|
||||
CPDOMDisplayServerAddView(self);
|
||||
}
|
||||
CPDOMDisplayServerAddView(self);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -2335,6 +2340,8 @@ var CPViewAutoresizingMaskKey = @"CPViewAutoresizingMask",
|
||||
|
||||
[self setBackgroundColor:[aCoder decodeObjectForKey:CPViewBackgroundColorKey]];
|
||||
|
||||
[self setupViewFlags];
|
||||
|
||||
_theme = [CPTheme defaultTheme];
|
||||
_themeState = CPThemeState([aCoder decodeIntForKey:CPViewThemeStateKey]);
|
||||
_themeAttributes = {};
|
||||
@@ -2450,6 +2457,7 @@ var _CPViewGetTransform = function(/*CPView*/ fromView, /*CPView */ toView)
|
||||
{
|
||||
var view = fromView;
|
||||
|
||||
// FIXME: This doesn't handle the case when the outside views are equal.
|
||||
// If we have a fromView, "climb up" the view tree until
|
||||
// we hit the root node or we hit the toLayer.
|
||||
while (view && view != toView)
|
||||
|
||||
@@ -0,0 +1,471 @@
|
||||
/*
|
||||
* CPTableColumn.j
|
||||
* AppKit
|
||||
*
|
||||
* Created by Francisco Tolmasky.
|
||||
* 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 <Foundation/Foundation.j>
|
||||
|
||||
|
||||
/*
|
||||
@ignore
|
||||
*/
|
||||
|
||||
/*
|
||||
@global
|
||||
@class CPTableColumn
|
||||
*/
|
||||
CPTableColumnNoResizing = 0;
|
||||
/*
|
||||
@global
|
||||
@class CPTableColumn
|
||||
*/
|
||||
CPTableColumnAutoresizingMask = 1;
|
||||
/*
|
||||
@global
|
||||
@class CPTableColumn
|
||||
*/
|
||||
CPTableColumnUserResizingMask = 2;
|
||||
|
||||
#define PurgableInfoMake(aView, aRow) { view:(aView), row:(aRow) }
|
||||
#define PurgableInfoView(anInfo) ((anInfo).view)
|
||||
#define PurgableInfoRow(anInfo) ((anInfo).row)
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPTableColumn
|
||||
|
||||
An CPTableColumn object mainly keeps information about the width of the column, its minimum and maximum width; whether the column can be edited or resized; and the cells used to draw the column header and the data in the column. You can change all these attributes of the column by calling the appropriate methods. Please note that the table column does not hold nor has access to the data to be displayed in the column; this data is maintained in the table view's data source.</p>
|
||||
|
||||
<p>Each CPTableColumn object is identified by a CPString, called the column identifier. The reason is that, after a column has been added to a table view, the user might move the columns around, so there is a need to identify the columns regardless of its position in the table.
|
||||
|
||||
@ignore
|
||||
*/
|
||||
@implementation CPTableColumn : CPObject
|
||||
{
|
||||
CPString _identifier;
|
||||
CPView _headerView;
|
||||
|
||||
CPTableView _tableView;
|
||||
|
||||
float _width;
|
||||
float _minWidth;
|
||||
float _maxWidth;
|
||||
|
||||
unsigned _resizingMask;
|
||||
|
||||
CPView _dataView; // default data view for this column
|
||||
|
||||
Object _dataViewData; // cache of data view archives (key=data view UID, value=data view archive)
|
||||
Object _dataViewForView; // mapping from view instances back to their data view prototype (key=view instance UID, value=data view)
|
||||
Object _purgableInfosForDataView; // (key=data view UID, value=)
|
||||
}
|
||||
|
||||
/*!
|
||||
Initializes the table column with the specified identifier.
|
||||
@param anIdentifier the identifier
|
||||
@return the initialized table column
|
||||
*/
|
||||
- (id)initWithIdentifier:(CPString)anIdentifier
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
[self _init];
|
||||
|
||||
_identifier = anIdentifier;
|
||||
|
||||
_width = 40.0;
|
||||
_minWidth = 8.0;
|
||||
_maxWidth = 1000.0;
|
||||
|
||||
var dataView = [[CPTextField alloc] initWithFrame:CPRectMakeZero()];
|
||||
[dataView setValue:[CPColor whiteColor] forThemeAttribute:"text-color" inState:CPThemeStateHighlighted];
|
||||
|
||||
[self setDataView:dataView];
|
||||
|
||||
_headerView = [[CPTextField alloc] initWithFrame:CPRectMakeZero()];
|
||||
[_headerView setBackgroundColor:[CPColor greenColor]];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)_init
|
||||
{
|
||||
_dataViewData = {};
|
||||
_dataViewForView = {};
|
||||
_purgableInfosForDataView = {};
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the table column's identifier
|
||||
@param anIdentifier the new identifier
|
||||
*/
|
||||
- (void)setIdentifier:(CPString)anIdentifier
|
||||
{
|
||||
_identifier = anIdentifier;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the table column's identifier
|
||||
*/
|
||||
- (CPString)identifier
|
||||
{
|
||||
return _identifier;
|
||||
}
|
||||
|
||||
// Setting the CPTableView
|
||||
/*!
|
||||
Sets the table's view. This is called automatically by Cappuccino.
|
||||
@param aTableView the new table view
|
||||
*/
|
||||
- (void)setTableView:(CPTableView)aTableView
|
||||
{
|
||||
_tableView = aTableView;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the column's table view.
|
||||
*/
|
||||
- (CPTableView)tableView
|
||||
{
|
||||
return _tableView;
|
||||
}
|
||||
|
||||
// Controlling size
|
||||
/*!
|
||||
Sets the column's width.
|
||||
@param aWidth the new column width
|
||||
*/
|
||||
- (void)setWidth:(float)aWidth
|
||||
{
|
||||
_width = aWidth;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the column's width
|
||||
*/
|
||||
- (float)width
|
||||
{
|
||||
return _width;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets column's minimum width.
|
||||
@param aWidth the new minimum column width
|
||||
*/
|
||||
- (void)setMinWidth:(float)aWidth
|
||||
{
|
||||
if (_width < (_minWidth = aWidth))
|
||||
[self setWidth:_minWidth];
|
||||
}
|
||||
|
||||
/*!
|
||||
The column's minimum width
|
||||
*/
|
||||
- (float)minWidth
|
||||
{
|
||||
return _minWidth;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the column's maximum width.
|
||||
@param aWidth the new maximum width
|
||||
*/
|
||||
- (void)setMaxWidth:(float)aWidth
|
||||
{
|
||||
if (_width > (_maxmimumWidth = aWidth))
|
||||
[self setWidth:_maxWidth];
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the resizing mask. The mask is one of:
|
||||
<pre>
|
||||
CPTableColumnNoResizing;
|
||||
CPTableColumnAutoresizingMask;
|
||||
CPTableColumnUserResizingMask;
|
||||
</pre>
|
||||
@param aMask the new resizing mask
|
||||
*/
|
||||
- (void)setResizingMask:(unsigned)aMask
|
||||
{
|
||||
_resizingMask = aMask;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the column's resizing mask. One of:
|
||||
<pre>
|
||||
CPTableColumnNoResizing;
|
||||
CPTableColumnAutoresizingMask;
|
||||
CPTableColumnUserResizingMask;
|
||||
</pre>
|
||||
*/
|
||||
- (unsigned)resizingMask
|
||||
{
|
||||
return _resizingMask;
|
||||
}
|
||||
|
||||
/*!
|
||||
Resizes the column according to the min, max and set width.
|
||||
*/
|
||||
- (void)sizeToFit
|
||||
{
|
||||
var width = CPRectGetWidth([_headerView frame]);
|
||||
|
||||
if (width < _minWidth)
|
||||
[self setMinWidth:width];
|
||||
else if (width > _maxWidth)
|
||||
[self setMaxWidth:width]
|
||||
|
||||
if (_width != width)
|
||||
[self setWidth:width];
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets whether the column in this data is editable.
|
||||
@param aFlag <code>YES</code> means the column data is editable
|
||||
*/
|
||||
- (void)setEditable:(BOOL)aFlag
|
||||
{
|
||||
_isEditable = aFlag;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns <code>YES</code> if the column data is editable.
|
||||
*/
|
||||
- (BOOL)isEditable
|
||||
{
|
||||
return _isEditable;
|
||||
}
|
||||
|
||||
//Setting the column header view
|
||||
|
||||
/*!
|
||||
Sets the view that draws the column's header.
|
||||
@param aHeaderView the view that will draws the column header
|
||||
*/
|
||||
|
||||
- (void)setHeaderView:(CPView)aView
|
||||
{
|
||||
_headerView = aView;
|
||||
}
|
||||
|
||||
/*!
|
||||
Return the view that draws the column's header
|
||||
*/
|
||||
- (CPView)headerView
|
||||
{
|
||||
return _headerView;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the data cell that draws rows in this column.
|
||||
*/
|
||||
- (void)setDataCell:(CPView <CPCoding>)aView
|
||||
{
|
||||
[self setDataView:aView];
|
||||
}
|
||||
|
||||
/*
|
||||
Sets the data view that draws rows in this column.
|
||||
*/
|
||||
- (void)setDataView:(CPView <CPCoding>)aView
|
||||
{
|
||||
if (_dataView)
|
||||
_dataViewData[[_dataView UID]] = nil;
|
||||
|
||||
_dataView = aView;
|
||||
_dataViewData[[aView UID]] = [CPKeyedArchiver archivedDataWithRootObject:aView];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the data cell that draws rows in this column
|
||||
*/
|
||||
- (CPCell)dataCell
|
||||
{
|
||||
return _dataView;
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the data view that draws rows in this column
|
||||
*/
|
||||
- (CPView)dataView
|
||||
{
|
||||
return [self dataCell];
|
||||
}
|
||||
|
||||
/*!
|
||||
By default returns the value from <code>dataCell</code>. This can
|
||||
be overridden by a subclass to return different cells for different
|
||||
rows.
|
||||
@param aRowIndex the index of the row to obtain the cell for
|
||||
*/
|
||||
- (CPCell)dataCellForRow:(int)aRowIndex
|
||||
{
|
||||
return [self dataView];
|
||||
}
|
||||
|
||||
- (CPView)dataViewForRow:(int)aRowIndex
|
||||
{
|
||||
return [self dataCellForRow:aRowIndex];
|
||||
}
|
||||
/*
|
||||
- (void)_markViewAsPurgable:(CPView)aView
|
||||
{
|
||||
var viewUID = [aView UID],
|
||||
dataViewUID = [_dataViewForView[viewUID] UID];
|
||||
|
||||
if (!_purgableInfosForDataView[dataViewUID])
|
||||
_purgableInfosForDataView[dataViewUID] = [CPDictionary dictionary];
|
||||
|
||||
[_purgableInfosForDataView[dataViewUID] setObject:aView forKey:viewUID];
|
||||
}
|
||||
*/
|
||||
- (void)_markView:(CPView)aView inRow:(unsigned)aRow asPurgable:(BOOL)isPurgable
|
||||
{
|
||||
var viewUID = [aView UID],
|
||||
dataViewUID = [_dataViewForView[viewUID] UID];
|
||||
|
||||
if (!_purgableInfosForDataView[dataViewUID])
|
||||
{
|
||||
if (!isPurgable)
|
||||
return;
|
||||
|
||||
_purgableInfosForDataView[dataViewUID] = {};
|
||||
}
|
||||
|
||||
if (!isPurgable) {
|
||||
if (_purgableInfosForDataView[dataViewUID][viewUID])
|
||||
CPLog.warn("removing unpurgable " + _purgableInfosForDataView[dataViewUID][viewUID]);
|
||||
delete _purgableInfosForDataView[dataViewUID][viewUID];
|
||||
}
|
||||
else
|
||||
_purgableInfosForDataView[dataViewUID][viewUID] = PurgableInfoMake(aView, aRow);
|
||||
}
|
||||
|
||||
- (CPView)_newDataViewForRow:(int)aRowIndex avoidingRows:(CPRange)rows
|
||||
{
|
||||
var view = [self dataViewForRow:aRowIndex],
|
||||
viewUID = [view UID],
|
||||
purgableInfos = _purgableInfosForDataView[viewUID];
|
||||
|
||||
if (purgableInfos)
|
||||
{
|
||||
for (var key in purgableInfos)
|
||||
{
|
||||
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);
|
||||
//}
|
||||
//else
|
||||
// CPLog.warn("avoiding");
|
||||
}
|
||||
}
|
||||
|
||||
// if we haven't cached an archive of the data view, do it now
|
||||
if (!_dataViewData[viewUID])
|
||||
_dataViewData[viewUID] = [CPKeyedArchiver archivedDataWithRootObject:view];
|
||||
|
||||
// unarchive the data view cache
|
||||
var newView = [CPKeyedUnarchiver unarchiveObjectWithData:_dataViewData[viewUID]];
|
||||
|
||||
// map the new view's UID to it's data view prototype
|
||||
_dataViewForView[[newView UID]] = view;
|
||||
|
||||
CPLog.warn("creating cell: %s", newView);
|
||||
|
||||
return newView;
|
||||
}
|
||||
|
||||
- (void)_purge
|
||||
{
|
||||
for (var viewUID in _purgableInfosForDataView)
|
||||
{
|
||||
var purgableInfos = _purgableInfosForDataView[viewUID];
|
||||
|
||||
for (var key in purgableInfos)
|
||||
{
|
||||
var view = PurgableInfoView(purgableInfos[key]);
|
||||
|
||||
if (!view)
|
||||
CPLog.info("key="+key+" view=" + view + " purgableInfos[key]="+purgableInfos[key])
|
||||
else if (view._superview) {
|
||||
//CPLog.error("PURGING: (removing)" + view);
|
||||
//[view removeFromSuperview];
|
||||
[view setHidden:YES];
|
||||
}
|
||||
//else
|
||||
// CPLog.warn("PURGING: (already removed)" + view);
|
||||
|
||||
//delete purgableInfos[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
var CPTableColumnIdentifierKey = @"CPTableColumnIdentifierKey",
|
||||
CPTableColumnHeaderViewKey = @"CPTableColumnHeaderViewKey",
|
||||
CPTableColumnDataViewKey = @"CPTableColumnDataViewKey",
|
||||
CPTableColumnWidthKey = @"CPTableColumnWidthKey",
|
||||
CPTableColumnMinWidthKey = @"CPTableColumnMinWidthKey",
|
||||
CPTableColumnMaxWidthKey = @"CPTableColumnMaxWidthKey",
|
||||
CPTableColumnResizingMaskKey = @"CPTableColumnResizingMaskKey";
|
||||
|
||||
@implementation CPTableColumn (CPCoding)
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[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
|
||||
File diff suppressed because it is too large
Load Diff
@@ -203,7 +203,8 @@
|
||||
|
||||
[scroller setValue:knobColor forThemeAttribute:@"knob-color" inState:CPThemeStateVertical];
|
||||
|
||||
[scroller setFloatValue:0.1 knobProportion:0.5];
|
||||
[scroller setFloatValue:0.1];
|
||||
[scroller setKnobProportion:0.5];
|
||||
|
||||
return scroller;
|
||||
}
|
||||
@@ -246,10 +247,11 @@
|
||||
[_CPCibCustomResource imageResourceWithName:"scroller-horizontal-knob-right.png" size:CGSizeMake(9.0, 15.0)]
|
||||
]
|
||||
isVertical:NO]);
|
||||
|
||||
|
||||
[scroller setValue:knobColor forThemeAttribute:@"knob-color"];
|
||||
|
||||
[scroller setFloatValue:0.1 knobProportion:0.5];
|
||||
|
||||
[scroller setFloatValue:0.1];
|
||||
[scroller setKnobProportion:0.5];
|
||||
|
||||
return scroller;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
|
||||
@import "CPView.j"
|
||||
|
||||
@implementation _CPCornerView : CPView
|
||||
{
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
{
|
||||
if (self = [super initWithFrame:aFrame])
|
||||
{
|
||||
[self setBackgroundColor:[CPColor purpleColor]];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
+466
-404
File diff suppressed because it is too large
Load Diff
@@ -108,7 +108,7 @@ var _CPKeyedUnarchiverArrayClass = Ni
|
||||
|
||||
CPDictionary _replacementClasses;
|
||||
|
||||
CPDictionary _objects;
|
||||
CPArray _objects;
|
||||
CPDictionary _archive;
|
||||
|
||||
CPDictionary _plistObject;
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* AppController.j
|
||||
* TableCibTest
|
||||
*
|
||||
* Created by Francisco Tolmasky on July 5, 2009.
|
||||
* Copyright 2009, 280 North, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
@import <Foundation/CPObject.j>
|
||||
|
||||
|
||||
@implementation AppController : CPObject
|
||||
{
|
||||
CPWindow theWindow; //this "outlet" is connected automatically by the Cib
|
||||
}
|
||||
|
||||
- (void)applicationDidFinishLaunching:(CPNotification)aNotification
|
||||
{
|
||||
// This is called when the application is done loading.
|
||||
}
|
||||
|
||||
- (void)awakeFromCib
|
||||
{
|
||||
// This is called when the cib is done loading.
|
||||
// You can implement this method on any object instantiated from a Cib.
|
||||
// It's a useful hook for setting up current UI values, and other things.
|
||||
|
||||
// In this case, we want the window from Cib to become our full browser window
|
||||
[theWindow setFullBridge:YES];
|
||||
}
|
||||
|
||||
- (int)numberOfRowsInTableView:(CPTableView)tableView
|
||||
{
|
||||
return 700000;
|
||||
}
|
||||
|
||||
- (id)tableView:(CPTableView)tableView objectValueForTableColumn:(CPTableColumn)tableColumn row:(int)row
|
||||
{
|
||||
if ([tableColumn identifier] === "icons")
|
||||
return iconImage;
|
||||
else
|
||||
return String((row + 1) * [[tableColumn identifier] intValue]);
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Main cib file base name</key>
|
||||
<string>MainMenu.cib</string>
|
||||
<key>CPBundleName</key>
|
||||
<string>TableCibTest</string>
|
||||
<key>CPPrincipalClass</key>
|
||||
<string>CPApplication</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,25 @@
|
||||
|
||||
require 'objective-j'
|
||||
require 'objective-j/bundletask'
|
||||
|
||||
if !ENV['CONFIG']
|
||||
ENV['CONFIG'] = 'Debug'
|
||||
end
|
||||
|
||||
ObjectiveJ::BundleTask.new(:TableCibTest) do |t|
|
||||
t.name = 'TableCibTest'
|
||||
t.identifier = 'com.280n.TableCibTest'
|
||||
t.version = '1.0'
|
||||
t.author = '280 North, Inc.'
|
||||
t.email = 'feedback @nospam@ 280north.com'
|
||||
t.summary = 'TableCibTest'
|
||||
t.sources = FileList['*.j']
|
||||
t.resources = FileList['Resources/*']
|
||||
t.index_file = 'index.html'
|
||||
t.info_plist = 'Info.plist'
|
||||
t.build_path = File.join('Build', ENV['CONFIG'], 'TableCibTest')
|
||||
t.flag = '-DDEBUG' if ENV['CONFIG'] == 'Debug'
|
||||
t.flag = '-O' if ENV['CONFIG'] == 'Release'
|
||||
end
|
||||
|
||||
task :default => [:TableCibTest]
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
After Width: | Height: | Size: 1.8 KiB |
@@ -0,0 +1,69 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE html
|
||||
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns = "http://www.w3.org/1999/xhtml" xml:lang = "en" lang = "en">
|
||||
<!--
|
||||
index-debug.html
|
||||
TableCibTest
|
||||
|
||||
Created by Francisco Tolmasky on July 5, 2009.
|
||||
Copyright 2009, 280 North, Inc. All rights reserved.
|
||||
-->
|
||||
<head>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" />
|
||||
|
||||
<title>TableCibTest</title>
|
||||
|
||||
<script type = "text/javascript">
|
||||
OBJJ_MAIN_FILE = "main.j";
|
||||
OBJJ_INCLUDE_PATHS = ["Frameworks/Debug", "Frameworks", "SomethingElse"];
|
||||
</script>
|
||||
|
||||
<script src = "Frameworks/Debug/Objective-J/Objective-J.js" type = "text/javascript"></script>
|
||||
|
||||
<style type = "text/css">
|
||||
body{margin:0; padding:0;}
|
||||
#container {position: absolute; top:50%; left:50%;}
|
||||
#content {width:800px; text-align:center; margin-left: -400px; height:50px; margin-top:-25px; line-height: 50px;}
|
||||
#content {font-family: "Helvetica", "Arial", sans-serif; font-size: 18px; color: black; text-shadow: 0px 1px 0px white; }
|
||||
#loadgraphic {margin-right: 0.2em; margin-bottom:-2px;}
|
||||
</style>
|
||||
|
||||
<!--[if lt IE 7]>
|
||||
<STYLE type="text/css">
|
||||
#container { position: relative; top: 50%; }
|
||||
#content { position: relative;}
|
||||
</STYLE>
|
||||
<![endif]-->
|
||||
|
||||
</head>
|
||||
|
||||
<body style="">
|
||||
<div id="loadingcontainer" style=" background-color: #eeeeee; overflow:hidden; width:100%; height:100%; position: absolute; top: 0; left: 0;">
|
||||
<script type = "text/javascript">
|
||||
document.write("<div id='container'><p id='content'>" +
|
||||
"<img id='loadgraphic' width='16' height='16' src='Resources/spinner.gif' /> " +
|
||||
"Loading TableCibTest...</p></div>");
|
||||
</script>
|
||||
|
||||
<noscript>
|
||||
<div id="container">
|
||||
<div style="width: 440px; padding: 10px 25px 20px 25px; font-family: sans-serif; background-color: #ffffff; position: relative; left: -245px; top: -120px; text-align: center; -moz-border-radius: 20px; -webkit-border-radius: 20px; color: #555555">
|
||||
<p style="line-height: 1.4em;">JavaScript is required for this site to work correctly but is either disabled or not supported by your browser.</p>
|
||||
<p style="font-size:120%; padding:10px;"><a href="http://cappuccino.org/noscript">Show me how to enable JavaScript</a></p>
|
||||
<p style="font-size:80%;">You may want to upgrade to a newer browser while you're at it:</p>
|
||||
<ul style="margin:0;padding:0; text-align: center; font-size:80%;" >
|
||||
<li style="display: inline;"><a href="http://www.apple.com/safari/download/">Safari</a></li>
|
||||
<li style="display: inline;"><a href="http://www.mozilla.com/en-US/firefox/">Firefox</a></li>
|
||||
<li style="display: inline;"><a href="http://www.google.com/chrome/">Chrome</a></li>
|
||||
<li style="display: inline;"><a href="http://www.opera.com/download/">Opera</a></li>
|
||||
<li style="display: inline;"><a href="http://www.microsoft.com/windows/downloads/ie/getitnow.mspx">Internet Explorer</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</noscript>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,68 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE html
|
||||
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns = "http://www.w3.org/1999/xhtml" xml:lang = "en" lang = "en">
|
||||
<!--
|
||||
index.html
|
||||
TableCibTest
|
||||
|
||||
Created by Francisco Tolmasky on July 5, 2009.
|
||||
Copyright 2009, 280 North, Inc. All rights reserved.
|
||||
-->
|
||||
<head>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" />
|
||||
|
||||
<title>TableCibTest</title>
|
||||
|
||||
<script type = "text/javascript">
|
||||
OBJJ_MAIN_FILE = "main.j";
|
||||
</script>
|
||||
|
||||
<script src = "Frameworks/Objective-J/Objective-J.js" type = "text/javascript"></script>
|
||||
|
||||
<style type = "text/css">
|
||||
body{margin:0; padding:0;}
|
||||
#container {position: absolute; top:50%; left:50%;}
|
||||
#content {width:800px; text-align:center; margin-left: -400px; height:50px; margin-top:-25px; line-height: 50px;}
|
||||
#content {font-family: "Helvetica", "Arial", sans-serif; font-size: 18px; color: black; text-shadow: 0px 1px 0px white; }
|
||||
#loadgraphic {margin-right: 0.2em; margin-bottom:-2px;}
|
||||
</style>
|
||||
|
||||
<!--[if lt IE 7]>
|
||||
<STYLE type="text/css">
|
||||
#container { position: relative; top: 50%; }
|
||||
#content { position: relative;}
|
||||
</STYLE>
|
||||
<![endif]-->
|
||||
|
||||
</head>
|
||||
|
||||
<body style="">
|
||||
<div id="loadingcontainer" style=" background-color: #eeeeee; overflow:hidden; width:100%; height:100%; position: absolute; top: 0; left: 0;">
|
||||
<script type = "text/javascript">
|
||||
document.write("<div id='container'><p id='content'>" +
|
||||
"<img id='loadgraphic' width='16' height='16' src='Resources/spinner.gif' /> " +
|
||||
"Loading TableCibTest...</p></div>");
|
||||
</script>
|
||||
|
||||
<noscript>
|
||||
<div id="container">
|
||||
<div style="width: 440px; padding: 10px 25px 20px 25px; font-family: sans-serif; background-color: #ffffff; position: relative; left: -245px; top: -120px; text-align: center; -moz-border-radius: 20px; -webkit-border-radius: 20px; color: #555555">
|
||||
<p style="line-height: 1.4em;">JavaScript is required for this site to work correctly but is either disabled or not supported by your browser.</p>
|
||||
<p style="font-size:120%; padding:10px;"><a href="http://cappuccino.org/noscript">Show me how to enable JavaScript</a></p>
|
||||
<p style="font-size:80%;">You may want to upgrade to a newer browser while you're at it:</p>
|
||||
<ul style="margin:0;padding:0; text-align: center; font-size:80%;" >
|
||||
<li style="display: inline;"><a href="http://www.apple.com/safari/download/">Safari</a></li>
|
||||
<li style="display: inline;"><a href="http://www.mozilla.com/en-US/firefox/">Firefox</a></li>
|
||||
<li style="display: inline;"><a href="http://www.google.com/chrome/">Chrome</a></li>
|
||||
<li style="display: inline;"><a href="http://www.opera.com/download/">Opera</a></li>
|
||||
<li style="display: inline;"><a href="http://www.microsoft.com/windows/downloads/ie/getitnow.mspx">Internet Explorer</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</noscript>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Executable
+18
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* AppController.j
|
||||
* TableCibTest
|
||||
*
|
||||
* Created by Francisco Tolmasky on July 5, 2009.
|
||||
* Copyright 2009, 280 North, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
@import <Foundation/Foundation.j>
|
||||
@import <AppKit/AppKit.j>
|
||||
|
||||
@import "AppController.j"
|
||||
|
||||
|
||||
function main(args, namedArgs)
|
||||
{
|
||||
CPApplicationMain(args, namedArgs);
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
|
||||
@import <Foundation/CPObject.j>
|
||||
|
||||
@import <Foundation/CPIndexSet.j>
|
||||
@import <AppKit/CPTableColumn.j>
|
||||
@import <AppKit/CPTableView.j>
|
||||
|
||||
|
||||
CPLogRegister(CPLogConsole);
|
||||
|
||||
@implementation AppController : CPObject
|
||||
{
|
||||
CPTableView tableView;
|
||||
CPImage iconImage;
|
||||
}
|
||||
|
||||
- (void)applicationDidFinishLaunching:(CPNotification)aNotification
|
||||
{
|
||||
var view = [[CPView alloc] initWithFrame:CGRectMake(0.0, 0.0, 100.0, 100.0)];
|
||||
|
||||
[view setBackgroundColor:[CPColor whiteColor]];
|
||||
[view enterFullScreenMode:nil withOptions:nil];
|
||||
|
||||
tableView = [[CPTableView alloc] initWithFrame:CGRectMake(0.0, 0.0, 500.0, 500.0)];//[view bounds]];
|
||||
|
||||
[tableView setAllowsMultipleSelection:YES];
|
||||
[tableView setUsesAlternatingRowBackgroundColors:YES];
|
||||
[tableView setGridStyleMask:CPTableViewSolidHorizontalGridLineMask | CPTableViewSolidVerticalGridLineMask];
|
||||
|
||||
// [tableView setBackgroundColor:[CPColor blueColor]];
|
||||
|
||||
var iconView = [[CPImageView alloc] initWithFrame:CGRectMake(16,16,0,0)];
|
||||
|
||||
[iconView setImageScaling:CPScaleNone];
|
||||
|
||||
var iconColumn = [[CPTableColumn alloc] initWithIdentifier:"icons"];
|
||||
|
||||
[iconColumn setWidth:32.0];
|
||||
[iconColumn setDataView:iconView];
|
||||
|
||||
[tableView addTableColumn:iconColumn];
|
||||
|
||||
iconImage = [[CPImage alloc] initWithContentsOfFile:"http://cappuccino.org/images/favicon.png" size:CGSizeMake(16,16)];
|
||||
|
||||
var textDataView = [CPTextField new];
|
||||
|
||||
// [textDataView setBackgroundColor:[[CPColor redColor] colorWithAlphaComponent:0.5]];
|
||||
|
||||
for (var i = 1; i <= 10; i++)
|
||||
{
|
||||
var column = [[CPTableColumn alloc] initWithIdentifier:String(i)];
|
||||
|
||||
[[column headerView] setStringValue:"Number "+i];
|
||||
[[column headerView] sizeToFit];
|
||||
//[column setWidth:[[column headerView] frame].size.width + 20];
|
||||
|
||||
[column setWidth:200.0];
|
||||
|
||||
[column setDataView:textDataView];
|
||||
|
||||
[tableView addTableColumn:column];
|
||||
}
|
||||
|
||||
[tableView selectColumnIndexes:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0,2)] byExtendingSelection:YES];
|
||||
|
||||
var scrollView = [[CPScrollView alloc] initWithFrame:[view bounds]];
|
||||
|
||||
[scrollView setDocumentView:tableView];
|
||||
[scrollView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
||||
|
||||
[view addSubview:scrollView];
|
||||
|
||||
[scrollView setAutohidesScrollers:YES];
|
||||
|
||||
[tableView setDelegate:self];
|
||||
[tableView setDataSource:self];
|
||||
}
|
||||
|
||||
- (int)numberOfRowsInTableView:(CPTableView)tableView
|
||||
{
|
||||
return 700000;
|
||||
}
|
||||
|
||||
- (id)tableView:(CPTableView)tableView objectValueForTableColumn:(CPTableColumn)tableColumn row:(int)row
|
||||
{
|
||||
if ([tableColumn identifier] === "icons")
|
||||
return iconImage;
|
||||
else
|
||||
return String((row + 1) * [[tableColumn identifier] intValue]);
|
||||
}
|
||||
/*
|
||||
- (id)tableView:(CPTableView)tableView heightOfRow:(int)row
|
||||
{
|
||||
//CPLog.info("heightOfRow:"+row);
|
||||
return 20.0 + ROUND(row * 0.5);
|
||||
}
|
||||
*/
|
||||
//- (void)tableViewSelectionIsChanging:(CPNotification)aNotification
|
||||
//{
|
||||
// CPLog.debug(@"changing! %@", [aNotification description]);
|
||||
//}
|
||||
//
|
||||
//- (void)tableViewSelectionDidChange:(CPNotification)aNotification
|
||||
//{
|
||||
// CPLog.debug(@"did change! %@", [aNotification description]);
|
||||
//}
|
||||
|
||||
- (BOOL)tableView:(CPTableView)aTableView shouldSelectRow:(int)rowIndex
|
||||
{
|
||||
//CPLog.debug(@"shouldSelectRow %d", rowIndex);
|
||||
for (var i = 2, sqrt = SQRT(rowIndex+1); i <= sqrt; i++)
|
||||
if ((rowIndex+1) % i === 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
- (BOOL)selectionShouldChangeInTableView:(CPTableView)aTableView
|
||||
{
|
||||
//CPLog.debug(@"selectionShouldChangeInTableView");
|
||||
return YES;
|
||||
}
|
||||
|
||||
//- (CPIndexSet)tableView:(CPTableView)tableView selectionIndexesForProposedSelection:(CPIndexSet)proposedSelectionIndexes
|
||||
//{
|
||||
// CPLog.debug(@"selectionIndexesForProposedSelection %@", [proposedSelectionIndexes description]);
|
||||
// return proposedSelectionIndexes;
|
||||
//}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CPApplicationDelegateClass</key>
|
||||
<string>AppController</string>
|
||||
<key>CPBundleName</key>
|
||||
<string>TableTest</string>
|
||||
<key>CPPrincipalClass</key>
|
||||
<string>CPApplication</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,25 @@
|
||||
|
||||
require 'objective-j'
|
||||
require 'objective-j/bundletask'
|
||||
|
||||
if !ENV['CONFIG']
|
||||
ENV['CONFIG'] = 'Debug'
|
||||
end
|
||||
|
||||
ObjectiveJ::BundleTask.new(:TableTest) do |t|
|
||||
t.name = 'TableTest'
|
||||
t.identifier = 'com.yourcompany.TableTest'
|
||||
t.version = '1.0'
|
||||
t.author = 'Your Company'
|
||||
t.email = 'feedback @nospam@ yourcompany.com'
|
||||
t.summary = 'TableTest'
|
||||
t.sources = FileList['*.j']
|
||||
t.resources = FileList['Resources/*']
|
||||
t.index_file = 'index.html'
|
||||
t.info_plist = 'Info.plist'
|
||||
t.build_path = File.join('Build', ENV['CONFIG'], 'TableTest')
|
||||
t.flag = '-DDEBUG' if ENV['CONFIG'] == 'Debug'
|
||||
t.flag = '-O' if ENV['CONFIG'] == 'Release'
|
||||
end
|
||||
|
||||
task :default => [:TableTest]
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.8 KiB |
@@ -0,0 +1,69 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE html
|
||||
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns = "http://www.w3.org/1999/xhtml" xml:lang = "en" lang = "en">
|
||||
<!--
|
||||
index-debug.html
|
||||
TableTest
|
||||
|
||||
Created by You on June 7, 2009.
|
||||
Copyright 2009, Your Company All rights reserved.
|
||||
-->
|
||||
<head>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" />
|
||||
|
||||
<title>TableTest</title>
|
||||
|
||||
<script type = "text/javascript">
|
||||
OBJJ_MAIN_FILE = "main.j";
|
||||
OBJJ_INCLUDE_PATHS = ["Frameworks/Debug", "Frameworks", "SomethingElse"];
|
||||
</script>
|
||||
|
||||
<script src = "Frameworks/Debug/Objective-J/Objective-J.js" type = "text/javascript"></script>
|
||||
|
||||
<style type = "text/css">
|
||||
body{margin:0; padding:0;}
|
||||
#container {position: absolute; top:50%; left:50%;}
|
||||
#content {width:800px; text-align:center; margin-left: -400px; height:50px; margin-top:-25px; line-height: 50px;}
|
||||
#content {font-family: "Helvetica", "Arial", sans-serif; font-size: 18px; color: black; text-shadow: 0px 1px 0px white; }
|
||||
#loadgraphic {margin-right: 0.2em; margin-bottom:-2px;}
|
||||
</style>
|
||||
|
||||
<!--[if lt IE 7]>
|
||||
<STYLE type="text/css">
|
||||
#container { position: relative; top: 50%; }
|
||||
#content { position: relative;}
|
||||
</STYLE>
|
||||
<![endif]-->
|
||||
|
||||
</head>
|
||||
|
||||
<body style="">
|
||||
<div id="loadingcontainer" style=" background-color: #eeeeee; overflow:hidden; width:100%; height:100%; position: absolute; top: 0; left: 0;">
|
||||
<script type = "text/javascript">
|
||||
document.write("<div id='container'><p id='content'>" +
|
||||
"<img id='loadgraphic' width='16' height='16' src='Resources/spinner.gif' /> " +
|
||||
"Loading TableTest...</p></div>");
|
||||
</script>
|
||||
|
||||
<noscript>
|
||||
<div id="container">
|
||||
<div style="width: 440px; padding: 10px 25px 20px 25px; font-family: sans-serif; background-color: #ffffff; position: relative; left: -245px; top: -120px; text-align: center; -moz-border-radius: 20px; -webkit-border-radius: 20px; color: #555555">
|
||||
<p style="line-height: 1.4em;">JavaScript is required for this site to work correctly but is either disabled or not supported by your browser.</p>
|
||||
<p style="font-size:120%; padding:10px;"><a href="http://cappuccino.org/noscript">Show me how to enable JavaScript</a></p>
|
||||
<p style="font-size:80%;">You may want to upgrade to a newer browser while you're at it:</p>
|
||||
<ul style="margin:0;padding:0; text-align: center; font-size:80%;" >
|
||||
<li style="display: inline;"><a href="http://www.apple.com/safari/download/">Safari</a></li>
|
||||
<li style="display: inline;"><a href="http://www.mozilla.com/en-US/firefox/">Firefox</a></li>
|
||||
<li style="display: inline;"><a href="http://www.google.com/chrome/">Chrome</a></li>
|
||||
<li style="display: inline;"><a href="http://www.opera.com/download/">Opera</a></li>
|
||||
<li style="display: inline;"><a href="http://www.microsoft.com/windows/downloads/ie/getitnow.mspx">Internet Explorer</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</noscript>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,69 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE html
|
||||
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns = "http://www.w3.org/1999/xhtml" xml:lang = "en" lang = "en">
|
||||
<!--
|
||||
index.html
|
||||
TableTest
|
||||
|
||||
Created by You on June 7, 2009.
|
||||
Copyright 2009, Your Company All rights reserved.
|
||||
-->
|
||||
<head>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" />
|
||||
|
||||
<title>TableTest</title>
|
||||
|
||||
<script type = "text/javascript">
|
||||
OBJJ_MAIN_FILE = "main.j";
|
||||
</script>
|
||||
|
||||
<script src = "Frameworks/Objective-J/Objective-J.js" type = "text/javascript"></script>
|
||||
|
||||
<style type = "text/css">
|
||||
body{margin:0; padding:0;}
|
||||
#container {position: absolute; top:50%; left:50%;}
|
||||
#content {width:800px; text-align:center; margin-left: -400px; height:50px; margin-top:-25px; line-height: 50px;}
|
||||
#content {font-family: "Helvetica", "Arial", sans-serif; font-size: 18px; color: black; text-shadow: 0px 1px 0px white; }
|
||||
#loadgraphic {margin-right: 0.2em; margin-bottom:-2px;}
|
||||
</style>
|
||||
|
||||
<!--[if lt IE 7]>
|
||||
<STYLE type="text/css">
|
||||
#container { position: relative; top: 50%; }
|
||||
#content { position: relative;}
|
||||
</STYLE>
|
||||
<![endif]-->
|
||||
|
||||
</head>
|
||||
|
||||
<body style="">
|
||||
<div id="loadingcontainer" style=" background-color: #eeeeee; overflow:hidden; width:100%; height:100%; position: absolute; top: 0; left: 0;">
|
||||
<script type = "text/javascript">
|
||||
document.write("<div id='container'><p id='content'>" +
|
||||
"<img id='loadgraphic' width='16' height='16' src='Resources/spinner.gif' /> " +
|
||||
"Loading TableTest...</p></div>");
|
||||
</script>
|
||||
|
||||
<noscript>
|
||||
<div id="container">
|
||||
<div style="width: 440px; padding: 10px 25px 20px 25px; font-family: sans-serif; background-color: #ffffff; position: relative; left: -245px; top: -120px; text-align: center; -moz-border-radius: 20px; -webkit-border-radius: 20px; color: #555555">
|
||||
<p style="line-height: 1.4em;">JavaScript is required for this site to work correctly but is either disabled or not supported by your browser.</p>
|
||||
<p style="font-size:120%; padding:10px;"><a href="http://cappuccino.org/noscript">Show me how to enable JavaScript</a></p>
|
||||
<p style="font-size:80%;">You may want to upgrade to a newer browser while you're at it:</p>
|
||||
<ul style="margin:0;padding:0; text-align: center; font-size:80%;" >
|
||||
<li style="display: inline;"><a href="http://www.apple.com/safari/download/">Safari</a></li>
|
||||
<li style="display: inline;"><a href="http://www.mozilla.com/en-US/firefox/">Firefox</a></li>
|
||||
<li style="display: inline;"><a href="http://www.google.com/chrome/">Chrome</a></li>
|
||||
<li style="display: inline;"><a href="http://www.opera.com/download/">Opera</a></li>
|
||||
<li style="display: inline;"><a href="http://www.microsoft.com/windows/downloads/ie/getitnow.mspx">Internet Explorer</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</noscript>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
Executable
+18
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* AppController.j
|
||||
* TableTest
|
||||
*
|
||||
* Created by You on June 7, 2009.
|
||||
* Copyright 2009, Your Company All rights reserved.
|
||||
*/
|
||||
|
||||
@import <Foundation/Foundation.j>
|
||||
@import <AppKit/AppKit.j>
|
||||
|
||||
@import "AppController.j"
|
||||
|
||||
|
||||
function main(args, namedArgs)
|
||||
{
|
||||
CPApplicationMain(args, namedArgs);
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
@import <AppKit/AppKit.j>
|
||||
|
||||
@implementation CPScrollViewTest : OJTestCase
|
||||
{
|
||||
}
|
||||
|
||||
- (void)testBothScrollersVisible
|
||||
{
|
||||
var scrollView = [[CPScrollView alloc] initWithFrame:CGRectMake(0.0, 0.0, 100.0, 100.0)];
|
||||
|
||||
[scrollView setAutohidesScrollers:NO];
|
||||
[scrollView setHasHorizontalScroller:YES];
|
||||
[scrollView setHasVerticalScroller:YES];
|
||||
|
||||
var documentView = [[CPView alloc] init];
|
||||
|
||||
[scrollView setDocumentView:documentView];
|
||||
|
||||
// Test document view size smaller than scroll view size.
|
||||
[documentView setFrameSize:CGSizeMake(50.0, 50.0)];
|
||||
|
||||
[self assert:[[scrollView horizontalScroller] isHidden] equals:NO];
|
||||
[self assert:[[scrollView horizontalScroller] isEnabled] equals:NO];
|
||||
|
||||
[self assert:[[scrollView verticalScroller] isHidden] equals:NO];
|
||||
[self assert:[[scrollView verticalScroller] isEnabled] equals:NO];
|
||||
|
||||
// Test document view size much larger than scroll view size.
|
||||
[documentView setFrameSize:CGSizeMake(1000.0, 1000.0)];
|
||||
|
||||
[self assert:[[scrollView horizontalScroller] isHidden] equals:NO];
|
||||
[self assert:[[scrollView horizontalScroller] isEnabled] equals:YES];
|
||||
|
||||
[self assert:[[scrollView verticalScroller] isHidden] equals:NO];
|
||||
[self assert:[[scrollView verticalScroller] isEnabled] equals:YES];
|
||||
|
||||
// Test document view size much taller than scroll view size.
|
||||
[documentView setFrameSize:CGSizeMake(50.0, 1000.0)];
|
||||
|
||||
[self assert:[[scrollView horizontalScroller] isHidden] equals:NO];
|
||||
[self assert:[[scrollView horizontalScroller] isEnabled] equals:NO];
|
||||
|
||||
[self assert:[[scrollView verticalScroller] isHidden] equals:NO];
|
||||
[self assert:[[scrollView verticalScroller] isEnabled] equals:YES];
|
||||
|
||||
// Test document view size much wider than scroll view size.
|
||||
[documentView setFrameSize:CGSizeMake(1000.0, 50.0)];
|
||||
|
||||
[self assert:[[scrollView horizontalScroller] isHidden] equals:NO];
|
||||
[self assert:[[scrollView horizontalScroller] isEnabled] equals:YES];
|
||||
|
||||
[self assert:[[scrollView verticalScroller] isHidden] equals:NO];
|
||||
[self assert:[[scrollView verticalScroller] isEnabled] equals:NO];
|
||||
|
||||
// Test document view size equal to scroll view size.
|
||||
[documentView setFrameSize:CGSizeMake(100.0, 100.0)];
|
||||
|
||||
[self assert:[[scrollView horizontalScroller] isHidden] equals:NO];
|
||||
[self assert:[[scrollView horizontalScroller] isEnabled] equals:YES];
|
||||
|
||||
[self assert:[[scrollView verticalScroller] isHidden] equals:NO];
|
||||
[self assert:[[scrollView verticalScroller] isEnabled] equals:YES];
|
||||
|
||||
// Test document view size taller than scroll view size only because of scrollers.
|
||||
[documentView setFrameSize:CGSizeMake(50.0, 100.0)];
|
||||
|
||||
[self assert:[[scrollView horizontalScroller] isHidden] equals:NO];
|
||||
[self assert:[[scrollView horizontalScroller] isEnabled] equals:NO];
|
||||
|
||||
[self assert:[[scrollView verticalScroller] isHidden] equals:NO];
|
||||
[self assert:[[scrollView verticalScroller] isEnabled] equals:YES];
|
||||
|
||||
// Test document view size wider than scroll view size only because of scrollers.
|
||||
[documentView setFrameSize:CGSizeMake(100.0, 50.0)];
|
||||
|
||||
[self assert:[[scrollView horizontalScroller] isHidden] equals:NO];
|
||||
[self assert:[[scrollView horizontalScroller] isEnabled] equals:YES];
|
||||
|
||||
[self assert:[[scrollView verticalScroller] isHidden] equals:NO];
|
||||
[self assert:[[scrollView verticalScroller] isEnabled] equals:NO];
|
||||
|
||||
// Test document view size exactly the right size relative to the scroll view size.
|
||||
[documentView setFrameSize:CGSizeMake(100.0 - 17.0, 100.0 - 17.0)];
|
||||
|
||||
[self assert:[[scrollView horizontalScroller] isHidden] equals:NO];
|
||||
[self assert:[[scrollView horizontalScroller] isEnabled] equals:NO];
|
||||
|
||||
[self assert:[[scrollView verticalScroller] isHidden] equals:NO];
|
||||
[self assert:[[scrollView verticalScroller] isEnabled] equals:NO];
|
||||
}
|
||||
|
||||
- (void)testAutoHidesScrollers
|
||||
{
|
||||
var scrollView = [[CPScrollView alloc] initWithFrame:CGRectMake(0.0, 0.0, 100.0, 100.0)];
|
||||
|
||||
[scrollView setAutohidesScrollers:YES];
|
||||
[scrollView setHasHorizontalScroller:YES];
|
||||
[scrollView setHasVerticalScroller:YES];
|
||||
|
||||
var documentView = [[CPView alloc] init];
|
||||
|
||||
[scrollView setDocumentView:documentView];
|
||||
|
||||
// Test document view size smaller than scroll view size.
|
||||
[documentView setFrameSize:CGSizeMake(50.0, 50.0)];
|
||||
|
||||
[self assert:[[scrollView horizontalScroller] isHidden] equals:YES];
|
||||
[self assert:[[scrollView horizontalScroller] isEnabled] equals:NO];
|
||||
|
||||
[self assert:[[scrollView verticalScroller] isHidden] equals:YES];
|
||||
[self assert:[[scrollView verticalScroller] isEnabled] equals:NO];
|
||||
|
||||
// Test document view size much larger than scroll view size.
|
||||
[documentView setFrameSize:CGSizeMake(1000.0, 1000.0)];
|
||||
|
||||
[self assert:[[scrollView horizontalScroller] isHidden] equals:NO];
|
||||
[self assert:[[scrollView horizontalScroller] isEnabled] equals:YES];
|
||||
|
||||
[self assert:[[scrollView verticalScroller] isHidden] equals:NO];
|
||||
[self assert:[[scrollView verticalScroller] isEnabled] equals:YES];
|
||||
|
||||
// Test document view size much taller than scroll view size.
|
||||
[documentView setFrameSize:CGSizeMake(50.0, 1000.0)];
|
||||
|
||||
[self assert:[[scrollView horizontalScroller] isHidden] equals:YES];
|
||||
[self assert:[[scrollView horizontalScroller] isEnabled] equals:NO];
|
||||
|
||||
[self assert:[[scrollView verticalScroller] isHidden] equals:NO];
|
||||
[self assert:[[scrollView verticalScroller] isEnabled] equals:YES];
|
||||
|
||||
// Test document view size much wider than scroll view size.
|
||||
[documentView setFrameSize:CGSizeMake(1000.0, 50.0)];
|
||||
|
||||
[self assert:[[scrollView horizontalScroller] isHidden] equals:NO];
|
||||
[self assert:[[scrollView horizontalScroller] isEnabled] equals:YES];
|
||||
|
||||
[self assert:[[scrollView verticalScroller] isHidden] equals:YES];
|
||||
[self assert:[[scrollView verticalScroller] isEnabled] equals:NO];
|
||||
|
||||
// Test document view size equal to scroll view size.
|
||||
[documentView setFrameSize:CGSizeMake(100.0, 100.0)];
|
||||
|
||||
[self assert:[[scrollView horizontalScroller] isHidden] equals:YES];
|
||||
[self assert:[[scrollView horizontalScroller] isEnabled] equals:NO];
|
||||
|
||||
[self assert:[[scrollView verticalScroller] isHidden] equals:YES];
|
||||
[self assert:[[scrollView verticalScroller] isEnabled] equals:NO];
|
||||
|
||||
// Test document view size taller than scroll view size only because of scrollers.
|
||||
[documentView setFrameSize:CGSizeMake(50.0, 100.0)];
|
||||
|
||||
[self assert:[[scrollView horizontalScroller] isHidden] equals:YES];
|
||||
[self assert:[[scrollView horizontalScroller] isEnabled] equals:NO];
|
||||
|
||||
[self assert:[[scrollView verticalScroller] isHidden] equals:YES];
|
||||
[self assert:[[scrollView verticalScroller] isEnabled] equals:NO];
|
||||
|
||||
// Test document view size wider than scroll view size only because of scrollers.
|
||||
[documentView setFrameSize:CGSizeMake(100.0, 50.0)];
|
||||
|
||||
[self assert:[[scrollView horizontalScroller] isHidden] equals:YES];
|
||||
[self assert:[[scrollView horizontalScroller] isEnabled] equals:NO];
|
||||
|
||||
[self assert:[[scrollView verticalScroller] isHidden] equals:YES];
|
||||
[self assert:[[scrollView verticalScroller] isEnabled] equals:NO];
|
||||
|
||||
// Test document view size exactly the right size relative to the scroll view size.
|
||||
[documentView setFrameSize:CGSizeMake(100.0 - 17.0, 100.0 - 17.0)];
|
||||
|
||||
[self assert:[[scrollView horizontalScroller] isHidden] equals:YES];
|
||||
[self assert:[[scrollView horizontalScroller] isEnabled] equals:NO];
|
||||
|
||||
[self assert:[[scrollView verticalScroller] isHidden] equals:YES];
|
||||
[self assert:[[scrollView verticalScroller] isEnabled] equals:NO];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,357 @@
|
||||
@import <Foundation/CPIndexSet.j>
|
||||
|
||||
function descriptionWithoutEntity(aString)
|
||||
{
|
||||
var descriptionWithEntity = [aString description];
|
||||
//print(descriptionWithEntity);
|
||||
return descriptionWithEntity.substr(descriptionWithEntity.indexOf('>') + 1);
|
||||
}
|
||||
|
||||
@implementation CPIndexSetTest : OJTestCase
|
||||
{
|
||||
CPIndexSet _set;
|
||||
}
|
||||
|
||||
- (void)testAddIndexes
|
||||
{
|
||||
var indexSet = [CPIndexSet indexSet];
|
||||
|
||||
// Test no indexes
|
||||
[self assert:descriptionWithoutEntity(indexSet) equals:@"(no indexes)"];
|
||||
|
||||
// Test adding initial range
|
||||
[indexSet addIndexesInRange:CPMakeRange(30,10)];
|
||||
|
||||
[self assert:@"[number of indexes: 10 (in 1 range), indexes: (30-39)]" equals:descriptionWithoutEntity(indexSet)];
|
||||
|
||||
// Test adding range after existing ranges.
|
||||
[indexSet addIndexesInRange:CPMakeRange(50,10)];
|
||||
|
||||
[self assert:@"[number of indexes: 20 (in 2 ranges), indexes: (30-39 50-59)]" equals:descriptionWithoutEntity(indexSet)];
|
||||
|
||||
// Test adding range before existing ranges.
|
||||
[indexSet addIndexesInRange:CPMakeRange(10,10)];
|
||||
|
||||
[self assert:@"[number of indexes: 30 (in 3 ranges), indexes: (10-19 30-39 50-59)]" equals:descriptionWithoutEntity(indexSet)];
|
||||
|
||||
// Test adding range inbetween existing ranges.
|
||||
[indexSet addIndexesInRange:CPMakeRange(45,2)];
|
||||
|
||||
[self assert:@"[number of indexes: 32 (in 4 ranges), indexes: (10-19 30-39 45-46 50-59)]" equals:descriptionWithoutEntity(indexSet)];
|
||||
|
||||
// Test adding single index inbetween existing ranges.
|
||||
[indexSet addIndexesInRange:CPMakeRange(23,1)];
|
||||
|
||||
[self assert:@"[number of indexes: 33 (in 5 ranges), indexes: (10-19 23 30-39 45-46 50-59)]" equals:descriptionWithoutEntity(indexSet)];
|
||||
|
||||
// Test adding range inbetween existing ranges that forces a combination
|
||||
[indexSet addIndexesInRange:CPMakeRange(47,3)];
|
||||
|
||||
[self assert:@"[number of indexes: 36 (in 4 ranges), indexes: (10-19 23 30-39 45-59)]" equals:descriptionWithoutEntity(indexSet)];
|
||||
|
||||
// Test adding range across ranges forcing a combination
|
||||
[indexSet addIndexesInRange:CPMakeRange(35,15)];
|
||||
|
||||
[self assert:@"[number of indexes: 41 (in 3 ranges), indexes: (10-19 23 30-59)]" equals:descriptionWithoutEntity(indexSet)];
|
||||
|
||||
// Test adding range across two empty slots forcing a combination
|
||||
[indexSet addIndexesInRange:CPMakeRange(0,70)];
|
||||
|
||||
[self assert:@"[number of indexes: 70 (in 1 range), indexes: (0-69)]" equals:descriptionWithoutEntity(indexSet)];
|
||||
}
|
||||
|
||||
- (void)testRemoveIndexes
|
||||
{
|
||||
var indexSet = [CPIndexSet indexSet];
|
||||
|
||||
// Test no indexes
|
||||
[self assert:descriptionWithoutEntity(indexSet) equals:@"(no indexes)"];
|
||||
|
||||
// Test adding initial range
|
||||
[indexSet addIndexesInRange:CPMakeRange(0, 70)];
|
||||
|
||||
[self assert:@"[number of indexes: 70 (in 1 range), indexes: (0-69)]" equals:descriptionWithoutEntity(indexSet)];
|
||||
|
||||
// Test remove range that is subset of existing range, causing a split.
|
||||
[indexSet removeIndexesInRange:CPMakeRange(30, 10)];
|
||||
|
||||
[self assert:@"[number of indexes: 60 (in 2 ranges), indexes: (0-29 40-69)]" equals:descriptionWithoutEntity(indexSet)];
|
||||
|
||||
// Test remove range that is subset of existing range, causing a split.
|
||||
[indexSet removeIndexesInRange:CPMakeRange(50, 5)];
|
||||
|
||||
[self assert:@"[number of indexes: 55 (in 3 ranges), indexes: (0-29 40-49 55-69)]" equals:descriptionWithoutEntity(indexSet)];
|
||||
|
||||
// Test remove index that is subset of existing range, causing a split.
|
||||
[indexSet removeIndex:57];
|
||||
|
||||
[self assert:@"[number of indexes: 54 (in 4 ranges), indexes: (0-29 40-49 55-56 58-69)]" equals:descriptionWithoutEntity(indexSet)];
|
||||
|
||||
// Test remove range that is an exactly represented in the set.
|
||||
[indexSet removeIndexesInRange:CPMakeRange(40, 10)];
|
||||
|
||||
[self assert:@"[number of indexes: 44 (in 3 ranges), indexes: (0-29 55-56 58-69)]" equals:descriptionWithoutEntity(indexSet)];
|
||||
|
||||
// Test remove range that isn't in the set.
|
||||
[indexSet removeIndexesInRange:CPMakeRange(35, 3)];
|
||||
|
||||
[self assert:@"[number of indexes: 44 (in 3 ranges), indexes: (0-29 55-56 58-69)]" equals:descriptionWithoutEntity(indexSet)];
|
||||
|
||||
// Test remove range that is partially in a left range.
|
||||
[indexSet removeIndexesInRange:CPMakeRange(25, 7)];
|
||||
|
||||
[self assert:@"[number of indexes: 39 (in 3 ranges), indexes: (0-24 55-56 58-69)]" equals:descriptionWithoutEntity(indexSet)];
|
||||
|
||||
// Test remove range that is partially in a left range.
|
||||
[indexSet removeIndexesInRange:CPMakeRange(57, 3)];
|
||||
|
||||
[self assert:@"[number of indexes: 37 (in 3 ranges), indexes: (0-24 55-56 60-69)]" equals:descriptionWithoutEntity(indexSet)];
|
||||
|
||||
// Test remove range that is partially in a left and right range.
|
||||
[indexSet removeIndexesInRange:CPMakeRange(20, 36)];
|
||||
|
||||
[self assert:@"[number of indexes: 31 (in 3 ranges), indexes: (0-19 56 60-69)]" equals:descriptionWithoutEntity(indexSet)];
|
||||
|
||||
// Remove single index that represents an entire range.
|
||||
[indexSet removeIndex:56];
|
||||
|
||||
[self assert:@"[number of indexes: 30 (in 2 ranges), indexes: (0-19 60-69)]" equals:descriptionWithoutEntity(indexSet)];
|
||||
|
||||
// Remove index set that is subset of existing range, causing a split.
|
||||
[indexSet removeIndexes:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(5, 10)]];
|
||||
|
||||
[self assert:@"[number of indexes: 20 (in 3 ranges), indexes: (0-4 15-19 60-69)]" equals:descriptionWithoutEntity(indexSet)];
|
||||
|
||||
// Remove indexes that are partially in 2 ranges and contains intermediate range.
|
||||
[indexSet removeIndexesInRange:CPMakeRange(2, 62)];
|
||||
|
||||
[self assert:@"[number of indexes: 8 (in 2 ranges), indexes: (0-1 64-69)]" equals:descriptionWithoutEntity(indexSet)];
|
||||
|
||||
// Remove indexes that fit exactly in 2 ranges.
|
||||
[indexSet removeIndexesInRange:CPMakeRange(0, 70)];
|
||||
|
||||
[self assert:@"(no indexes)" equals:descriptionWithoutEntity(indexSet)];
|
||||
|
||||
indexSet = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, 30)];
|
||||
|
||||
// Remove indexes from left hand of single range
|
||||
[indexSet removeIndexesInRange:CPMakeRange(0, 29)];
|
||||
|
||||
[self assert:@"[number of indexes: 1 (in 1 range), indexes: (29)]" equals:descriptionWithoutEntity(indexSet)];
|
||||
|
||||
indexSet = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, 30)];
|
||||
|
||||
// Remove indexes from right hand of single range
|
||||
[indexSet removeIndexesInRange:CPMakeRange(1, 29)];
|
||||
|
||||
[self assert:@"[number of indexes: 1 (in 1 range), indexes: (0)]" equals:descriptionWithoutEntity(indexSet)];
|
||||
}
|
||||
|
||||
- (void)testGetIndexes
|
||||
{
|
||||
var indexSet = [CPIndexSet indexSet];
|
||||
|
||||
// Test no indexes
|
||||
[self assert:descriptionWithoutEntity(indexSet) equals:@"(no indexes)"];
|
||||
|
||||
// Test adding initial range
|
||||
[indexSet addIndexesInRange:CPMakeRange(0, 10)];
|
||||
|
||||
[indexSet addIndexesInRange:CPMakeRange(15, 1)];
|
||||
|
||||
[indexSet addIndexesInRange:CPMakeRange(20, 10)];
|
||||
|
||||
[indexSet addIndexesInRange:CPMakeRange(50, 10)];
|
||||
|
||||
[self assert:@"[number of indexes: 31 (in 4 ranges), indexes: (0-9 15 20-29 50-59)]" equals:descriptionWithoutEntity(indexSet)];
|
||||
|
||||
var array = [];
|
||||
|
||||
[indexSet getIndexes:array maxCount:1000 inIndexRange:nil];
|
||||
|
||||
[self assert:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 15, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59] equals:array];
|
||||
}
|
||||
|
||||
- (void)setUp
|
||||
{
|
||||
_set = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(10, 10)];
|
||||
}
|
||||
|
||||
- (void)tearDown
|
||||
{
|
||||
_set = nil;
|
||||
}
|
||||
|
||||
- (void)testIndexSet:(CPIndexSet)set containsRange:(CPRange)range
|
||||
{
|
||||
[self assertFalse:[set containsIndex:range.location -1]];
|
||||
|
||||
for (var i=range.location, max=CPMaxRange(range); i<max; i++)
|
||||
{
|
||||
[self assertTrue:[set containsIndex:i]];
|
||||
}
|
||||
|
||||
[self assertFalse:[set containsIndex:i]];
|
||||
}
|
||||
|
||||
- (void)testIndexSet
|
||||
{
|
||||
[self assertNotNull:[CPIndexSet indexSet]];
|
||||
[self assert:[[CPIndexSet indexSet] class] equals:[CPIndexSet class]];
|
||||
}
|
||||
|
||||
- (void)testIndexSetWithIndex
|
||||
{
|
||||
[self assertTrue:[[CPIndexSet indexSetWithIndex:1] containsIndex:1]];
|
||||
[self assertTrue:[[CPIndexSet indexSetWithIndex:0] containsIndex:0]];
|
||||
[self assertFalse:[[CPIndexSet indexSetWithIndex:0] containsIndex:1]];
|
||||
}
|
||||
|
||||
- (void)testIndexSetWithIndexesInRange
|
||||
{
|
||||
var set = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, 7)];
|
||||
[self assertNotNull:set];
|
||||
[self testIndexSet:set containsRange:CPMakeRange(0, 7)];
|
||||
}
|
||||
|
||||
- (void)testInit
|
||||
{
|
||||
var set = [[CPIndexSet alloc] init];
|
||||
[self assertNotNull:set];
|
||||
[self assert:[set count] equals:0];
|
||||
}
|
||||
|
||||
- (void)testInitWithIndex
|
||||
{
|
||||
var set = [[CPIndexSet alloc] initWithIndex:234];
|
||||
[self assertNotNull:set];
|
||||
[self assertTrue:[set containsIndex:234]];
|
||||
[self assertFalse:[set containsIndex:5432]];
|
||||
}
|
||||
|
||||
- (void)testInitWithIndexesInRange
|
||||
{
|
||||
var set = [[CPIndexSet alloc] initWithIndexesInRange:CPMakeRange(0, 7)];
|
||||
[self assertNotNull:set];
|
||||
[self testIndexSet:set containsRange:CPMakeRange(0, 7)];
|
||||
[self assertTrue:[set containsIndexesInRange:CPMakeRange(0, 7)]];
|
||||
[self assertTrue:[set containsIndexesInRange:CPMakeRange(1, 6)]];
|
||||
[self assertFalse:[set containsIndexesInRange:CPMakeRange(2, 6)]];
|
||||
}
|
||||
|
||||
- (void)testInitWithIndexSet
|
||||
{
|
||||
var set1 = [[CPIndexSet alloc] initWithIndexesInRange:CPMakeRange(0, 7)];
|
||||
var set = [[CPIndexSet alloc] initWithIndexSet:set1];
|
||||
[self assertNotNull:set];
|
||||
[self testIndexSet:set containsRange:CPMakeRange(0, 7)];
|
||||
}
|
||||
|
||||
- (void)testIsEqualToIndexSet
|
||||
{
|
||||
var set1 = [CPIndexSet indexSetWithIndex:7];
|
||||
var set2 = [CPIndexSet indexSetWithIndex:7];
|
||||
var set3 = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(7, 2)];
|
||||
|
||||
[self assertTrue:[set1 isEqualToIndexSet:set2]];
|
||||
[self assertTrue:[set1 isEqualToIndexSet:set1]];
|
||||
[self assertFalse:[set1 isEqualToIndexSet:set3]];
|
||||
}
|
||||
|
||||
- (void)testCount
|
||||
{
|
||||
var set = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(7, 2)];
|
||||
var set2 = [CPIndexSet indexSetWithIndex:7];
|
||||
|
||||
[self assert:[set2 count] equals:1];
|
||||
[self assert:[set count] equals:2];
|
||||
}
|
||||
|
||||
- (void)testFirstIndex
|
||||
{
|
||||
[self assert:[_set firstIndex] equals:10];
|
||||
[self assert:[[CPIndexSet indexSet] firstIndex] equals:CPNotFound];
|
||||
}
|
||||
|
||||
- (void)testLastIndex
|
||||
{
|
||||
[self assert:[_set lastIndex] equals:19];
|
||||
[self assert:[[CPIndexSet indexSet] lastIndex] equals:CPNotFound];
|
||||
}
|
||||
|
||||
- (void)testAddSpeed
|
||||
{
|
||||
var startTime = [CPDate date];
|
||||
|
||||
for (var i = 0; i < 1000; i++)
|
||||
{
|
||||
[_set addIndex:ROUND(RAND()*100000)];
|
||||
}
|
||||
|
||||
print([startTime timeIntervalSinceNow]);
|
||||
//[self assertTrue: ABS([startTime timeIntervalSinceNow]) < 2];
|
||||
}
|
||||
|
||||
- (void)testIndexGreaterThanIndex
|
||||
{
|
||||
[self assert:[_set indexGreaterThanIndex:5] equals:10];
|
||||
[self assert:[_set indexGreaterThanIndex:11] equals:12];
|
||||
[self assert:[_set indexGreaterThanIndex:19] equals:CPNotFound];
|
||||
}
|
||||
|
||||
- (void)testIndexLessThanIndex
|
||||
{
|
||||
[self assert:[_set indexLessThanIndex:5] equals:CPNotFound];
|
||||
[self assert:[_set indexLessThanIndex:11] equals:10];
|
||||
[self assert:[_set indexLessThanIndex:20] equals:19];
|
||||
[self assert:[_set indexLessThanIndex:222] equals:19];
|
||||
}
|
||||
|
||||
- (void)testIndexGreaterThanOrEqualToIndex
|
||||
{
|
||||
[self assert:[_set indexGreaterThanOrEqualToIndex:5] equals:10];
|
||||
[self assert:[_set indexGreaterThanOrEqualToIndex:10] equals:10];
|
||||
[self assert:[_set indexGreaterThanOrEqualToIndex:19] equals:19];
|
||||
[self assert:[_set indexGreaterThanOrEqualToIndex:20] equals:CPNotFound];
|
||||
}
|
||||
|
||||
- (void)testIndexLessThanOrEqualToIndex
|
||||
{
|
||||
[self assert:[_set indexLessThanOrEqualToIndex:5] equals:CPNotFound];
|
||||
[self assert:[_set indexLessThanOrEqualToIndex:10] equals:10];
|
||||
[self assert:[_set indexLessThanOrEqualToIndex:19] equals:19];
|
||||
[self assert:[_set indexLessThanOrEqualToIndex:20] equals:19];
|
||||
}
|
||||
|
||||
- (void)testContainsIndex
|
||||
{
|
||||
[self assertTrue:[_set containsIndex:10]];
|
||||
[self assertTrue:[_set containsIndex:11]];
|
||||
[self assertTrue:[_set containsIndex:19]];
|
||||
[self assertFalse:[_set containsIndex:9]];
|
||||
[self assertFalse:[_set containsIndex:20]];
|
||||
}
|
||||
|
||||
- (void)testContainsIndexesInRange
|
||||
{
|
||||
[self assertTrue:[_set containsIndexesInRange:CPMakeRange(10, 10)]];
|
||||
[self assertFalse:[_set containsIndexesInRange:CPMakeRange(10, 0)]];
|
||||
[self assertTrue:[_set containsIndexesInRange:CPMakeRange(10, 1)]];
|
||||
[self assertTrue:[_set containsIndexesInRange:CPMakeRange(14, 2)]];
|
||||
[self assertFalse:[_set containsIndexesInRange:CPMakeRange(10, 11)]];
|
||||
[self assertFalse:[_set containsIndexesInRange:CPMakeRange(9, 2)]];
|
||||
[self assertFalse:[_set containsIndexesInRange:CPMakeRange(19, 2)]];
|
||||
}
|
||||
|
||||
- (void)testContainsIndexes
|
||||
{
|
||||
[self assertTrue:[_set containsIndexes:_set]];
|
||||
[self assertTrue:[_set containsIndexes:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(10, 1)]]];
|
||||
[self assertTrue:[_set containsIndexes:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(10, 10)]]];
|
||||
[self assertTrue:[_set containsIndexes:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(19, 1)]]];
|
||||
[self assertFalse:[_set containsIndexes:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(10, 11)]]];
|
||||
[self assertFalse:[_set containsIndexes:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(9, 2)]]];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Converter+Mac.j
|
||||
* nib2cib
|
||||
*
|
||||
* Created by Francisco Tolmasky.
|
||||
* 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 "Converter.j"
|
||||
|
||||
|
||||
@implementation Converter (Mac)
|
||||
|
||||
- (void)convertedDataFromMacData:(CPData)data resourcesPath:(CPString)aResourcesPath
|
||||
{
|
||||
// Unarchive the NS data
|
||||
var unarchiver = [[Nib2CibKeyedUnarchiver alloc] initForReadingWithData:data resourcesPath:aResourcesPath],
|
||||
objectData = [unarchiver decodeObjectForKey:@"IB.objectdata"];
|
||||
|
||||
// Perform a bit of post-processing on views since all CP views are flipped.
|
||||
// It's better to do this here (instead of say, in NSView::initWithCoder:),
|
||||
// because at this point all the objects an mappings are stabilized.
|
||||
var objects = [unarchiver allObjects],
|
||||
count = [objects count];
|
||||
|
||||
while (count--)
|
||||
{
|
||||
var object = objects[count];
|
||||
|
||||
if (![object isKindOfClass:[CPView class]])
|
||||
continue;
|
||||
|
||||
var superview = [object superview];
|
||||
|
||||
if (!superview || [superview NS_isFlipped])
|
||||
continue;
|
||||
|
||||
var superviewHeight = CGRectGetHeight([superview bounds]),
|
||||
frame = [object frame];
|
||||
|
||||
[object setFrameOrigin:CGPointMake(CGRectGetMinX(frame), superviewHeight - CGRectGetMaxY(frame))];
|
||||
|
||||
var NS_autoresizingMask = [object autoresizingMask];
|
||||
|
||||
autoresizingMask = NS_autoresizingMask & ~(CPViewMaxYMargin | CPViewMinYMargin);
|
||||
|
||||
if (!(NS_autoresizingMask & (CPViewMaxYMargin | CPViewMinYMargin | CPViewHeightSizable)))
|
||||
autoresizingMask |= CPViewMinYMargin;
|
||||
else
|
||||
{
|
||||
if (NS_autoresizingMask & CPViewMaxYMargin)
|
||||
autoresizingMask |= CPViewMinYMargin;
|
||||
if (NS_autoresizingMask & CPViewMinYMargin)
|
||||
autoresizingMask |= CPViewMaxYMargin;
|
||||
}
|
||||
|
||||
[object setAutoresizingMask:autoresizingMask];
|
||||
}
|
||||
|
||||
// Re-archive the CP data.
|
||||
var convertedData = [CPData data],
|
||||
archiver = [[CPKeyedArchiver alloc] initForWritingWithMutableData:convertedData];
|
||||
|
||||
[archiver encodeObject:objectData forKey:@"CPCibObjectDataKey"];
|
||||
[archiver finishEncoding];
|
||||
|
||||
return convertedData;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Converter.j
|
||||
* nib2cib
|
||||
*
|
||||
* Created by Francisco Tolmasky.
|
||||
* 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 <Foundation/CPObject.j>
|
||||
@import <Foundation/CPData.j>
|
||||
|
||||
var File = require("file"),
|
||||
popen = require("os").popen;
|
||||
|
||||
|
||||
NibFormatUndetermined = 0,
|
||||
NibFormatMac = 1,
|
||||
NibFormatIPhone = 2;
|
||||
|
||||
ConverterConversionException = @"ConverterConversionException";
|
||||
|
||||
@implementation Converter : CPObject
|
||||
{
|
||||
NibFormat format @accessors;
|
||||
CPString inputPath @accessors;
|
||||
CPString outputPath @accessors;
|
||||
CPString resourcesPath @accessors;
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
[self setFormat:NibFormatUndetermined];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)convert
|
||||
{
|
||||
try
|
||||
{
|
||||
if ([resourcesPath length] && !File.isReadable(resourcesPath))
|
||||
[CPException raise:ConverterConversionException reason:@"Could not read Resources at path \"" + resourcesPath + "\""];
|
||||
|
||||
var stringContents = File.read(inputPath, { charset:"UTF-8" }),
|
||||
inferredFormat = format;
|
||||
|
||||
if (inferredFormat === NibFormatUndetermined)
|
||||
{
|
||||
// Assume its a Mac file.
|
||||
inferredFormat = NibFormatMac;
|
||||
|
||||
// Some .xibs are iPhone nibs, check the actual contents in this case.
|
||||
if (File.extname(inputPath) !== ".nib" &&
|
||||
stringContents.indexOf("<archive type=\"com.apple.InterfaceBuilder3.CocoaTouch.XIB\"") !== -1)
|
||||
inferredFormat = NibFormatIPhone;
|
||||
|
||||
if (inferredFormat === NibFormatMac)
|
||||
CPLog("Auto-detected Cocoa Nib or Xib File");
|
||||
else
|
||||
CPLog("Auto-detected CocoaTouch Xib File");
|
||||
}
|
||||
|
||||
var nibData = [self CPCompliantNibDataAtFilePath:inputPath];
|
||||
|
||||
if (inferredFormat === NibFormatMac)
|
||||
var convertedData = [self convertedDataFromMacData:nibData resourcesPath:resourcesPath];
|
||||
else
|
||||
[CPException raise:ConverterConversionException reason:@"nib2cib does not understand this nib format."];
|
||||
|
||||
if (![outputPath length])
|
||||
outputPath = cibExtension(inputPath);
|
||||
|
||||
File.write(outputPath, [convertedData string], { charset:"UTF-8" });
|
||||
}
|
||||
catch(anException)
|
||||
{
|
||||
print(anException);
|
||||
}
|
||||
}
|
||||
|
||||
- (CPData)CPCompliantNibDataAtFilePath:(CPString)aFilePath
|
||||
{
|
||||
// Compile xib or nib to make sure we have a non-new format nib.
|
||||
var temporaryNibFilePath = File.createTempFile("temp", ".nib", true);
|
||||
|
||||
if (popen("/usr/bin/ibtool " + aFilePath + " --compile " + temporaryNibFilePath).wait() === 1)
|
||||
throw "Could not compile file at " + aFilePath;
|
||||
|
||||
// Convert from binary plist to XML plist
|
||||
var temporaryPlistFilePath = File.createTempFile("temp", ".plist", true);
|
||||
|
||||
if (popen("/usr/bin/plutil " + " -convert xml1 " + temporaryNibFilePath + " -o " + temporaryPlistFilePath).wait() === 1)
|
||||
throw "Could not convert to xml plist for file at " + aFilePath;
|
||||
|
||||
if (!File.isReadable(temporaryPlistFilePath))
|
||||
[CPException raise:ConverterConversionException reason:@"Unable to convert nib file."];
|
||||
|
||||
var plistContents = File.read(temporaryPlistFilePath, { charset:"UTF-8" });
|
||||
|
||||
// Minor NS keyed archive to CP keyed archive conversion.
|
||||
// Use Java directly because rhino's string.replace is *so slow*. 4 seconds vs. 1 millisecond.
|
||||
// plistContents = plistContents.replace(/\<key\>\s*CF\$UID\s*\<\/key\>/g, "<key>CP$UID</key>");
|
||||
plistContents = String(java.lang.String(plistContents).replaceAll("\\<key\\>\\s*CF\\$UID\\s*\\<\/key\\>", "<key>CP\\$UID</key>"));
|
||||
|
||||
return [CPData dataWithString:plistContents];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
File.createTempFile = function(aName, anExtension, shouldDeleteOnExit)
|
||||
{
|
||||
var tempFile = Packages.java.io.File.createTempFile(aName, anExtension),
|
||||
tempFilePath = String(tempFile.getAbsolutePath());
|
||||
|
||||
if (shouldDeleteOnExit)
|
||||
tempFile.deleteOnExit();
|
||||
|
||||
return String(tempFilePath);
|
||||
}
|
||||
|
||||
function cibExtension(aPath)
|
||||
{
|
||||
var start = aPath.length - 1;
|
||||
|
||||
while (aPath.charAt(start) === '/')
|
||||
start--;
|
||||
|
||||
aPath = aPath.substr(0, start + 1);
|
||||
|
||||
var dotIndex = aPath.lastIndexOf('.');
|
||||
|
||||
if (dotIndex == -1)
|
||||
return aPath + ".cib";
|
||||
|
||||
var slashIndex = aPath.lastIndexOf('/');
|
||||
|
||||
if (slashIndex > dotIndex)
|
||||
return aPath + ".cib";
|
||||
|
||||
return aPath.substr(0, dotIndex) + ".cib";
|
||||
}
|
||||
|
||||
@import "Converter+Mac.j"
|
||||
@@ -40,6 +40,11 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)NS_isFlipped
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation NSClipView : CPClipView
|
||||
|
||||
@@ -41,7 +41,7 @@ importClass(javax.imageio.ImageIO);
|
||||
|
||||
var size = CGSizeMakeZero();
|
||||
|
||||
if (![aCoder resourcesFile])
|
||||
if (![[aCoder resourcesPath] length])
|
||||
{
|
||||
CPLog.warn("***WARNING: Resources found in nib, but no resources path specified with -R option.");
|
||||
|
||||
@@ -49,19 +49,20 @@ importClass(javax.imageio.ImageIO);
|
||||
}
|
||||
else
|
||||
{
|
||||
var resourceFile = [aCoder resourceFileForName:_resourceName];
|
||||
|
||||
if (!resourceFile)
|
||||
var resourcePath = [aCoder resourcePathForName:_resourceName];
|
||||
|
||||
if (!resourcePath)
|
||||
CPLog.warn("***WARNING: Resource named " + _resourceName + " not found in supplied resources path.");
|
||||
|
||||
else
|
||||
{
|
||||
var imageStream = ImageIO.createImageInputStream(resourceFile),
|
||||
var imageStream = ImageIO.createImageInputStream(new Packages.java.io.File(resourcePath).getCanonicalFile()),
|
||||
readers = ImageIO.getImageReaders(imageStream),
|
||||
reader = null;
|
||||
|
||||
|
||||
if(readers.hasNext())
|
||||
reader = readers.next();
|
||||
|
||||
|
||||
else
|
||||
{
|
||||
imageStream.close();
|
||||
|
||||
@@ -20,20 +20,6 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/*#import "NSIBObjectData.h"
|
||||
#import <Foundation/NSArray.h>
|
||||
#import <Foundation/NSMutableIndexSet.h>
|
||||
#import <Foundation/NSString.h>
|
||||
#import <Foundation/NSSet.h>
|
||||
#import <Foundation/NSDebug.h>
|
||||
#import "NSNibKeyedUnarchiver.h"
|
||||
#import "NSCustomObject.h"
|
||||
#import <AppKit/NSNibConnector.h>
|
||||
#import <AppKit/NSFontManager.h>
|
||||
#import <AppKit/NSNib.h>*/
|
||||
|
||||
@import <Foundation/CPObject.j>
|
||||
|
||||
@import <AppKit/_CPCibObjectData.j>
|
||||
|
||||
|
||||
|
||||
@@ -45,6 +45,8 @@
|
||||
if ([aCoder containsValueForKey:"NSCurValue"])
|
||||
_value = [aCoder decodeFloatForKey:"NSCurValue"];
|
||||
|
||||
[self _calculateIsVertical];
|
||||
|
||||
var isVertical = [self isVertical];
|
||||
|
||||
if (CPStringFromSelector([self action]) === @"_doScroller:")
|
||||
|
||||
@@ -28,25 +28,23 @@
|
||||
- (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:CPThemeStateHighlighted];
|
||||
|
||||
[_dataView setValue:[CPColor whiteColor] forThemeAttribute:"text-color" inState:CPThemeStateHighlighted];
|
||||
|
||||
//_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"];
|
||||
}
|
||||
|
||||
|
||||
+7
-31
@@ -56,42 +56,13 @@
|
||||
_subviews = [];
|
||||
|
||||
var vFlags = [aCoder decodeIntForKey:@"NSvFlags"];
|
||||
|
||||
var autoresizingMaskNS = vFlags & 0x3F;
|
||||
|
||||
// swap Y coordinate flags:
|
||||
_autoresizingMask = autoresizingMaskNS & ~(CPViewMaxYMargin | CPViewMinYMargin)
|
||||
if (!(autoresizingMaskNS & (CPViewMaxYMargin | CPViewMinYMargin | CPViewHeightSizable)))
|
||||
{
|
||||
_autoresizingMask |= CPViewMinYMargin;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (autoresizingMaskNS & CPViewMaxYMargin)
|
||||
_autoresizingMask |= CPViewMinYMargin;
|
||||
if (autoresizingMaskNS & CPViewMinYMargin)
|
||||
_autoresizingMask |= CPViewMaxYMargin;
|
||||
}
|
||||
|
||||
|
||||
_autoresizingMask = vFlags & 0x3F;
|
||||
_autoresizesSubviews = vFlags & (1 << 8);
|
||||
|
||||
_hitTests = YES;
|
||||
_isHidden = NO;//[aCoder decodeObjectForKey:CPViewIsHiddenKey];
|
||||
_opacity = 1.0;//[aCoder decodeIntForKey:CPViewOpacityKey];
|
||||
|
||||
if (YES/*[_superview isFlipped]*/)
|
||||
{
|
||||
var height = CGRectGetHeight([self bounds]),
|
||||
count = [_subviews count];
|
||||
|
||||
while (count--)
|
||||
{
|
||||
var subview = _subviews[count],
|
||||
frame = [subview frame];
|
||||
|
||||
[subview setFrameOrigin:CGPointMake(CGRectGetMinX(frame), height - CGRectGetMaxY(frame))];
|
||||
}
|
||||
}
|
||||
|
||||
_themeAttributes = {};
|
||||
_themeState = CPThemeStateNormal;
|
||||
@@ -101,6 +72,11 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)NS_isFlipped
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation NSView : CPView
|
||||
|
||||
@@ -1,52 +1,87 @@
|
||||
/*
|
||||
* NSKeyedArchiver.j
|
||||
* nib2cib
|
||||
*
|
||||
* Created by Francisco Tolmasky.
|
||||
* 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 <Foundation/CPKeyedUnarchiver.j>
|
||||
|
||||
var File = require("file");
|
||||
|
||||
|
||||
@implementation Nib2CibKeyedUnarchiver : CPKeyedUnarchiver
|
||||
{
|
||||
File resourcesFile;
|
||||
CPString resourcesPath @accessors(readonly);
|
||||
}
|
||||
|
||||
- (id)initForReadingWithData:(CPData)data resourcesFile:(File)aResourcesFile
|
||||
- (id)initForReadingWithData:(CPData)data resourcesPath:(CPString)aResourcesPath
|
||||
{
|
||||
self = [super initForReadingWithData:data];
|
||||
|
||||
|
||||
if (self)
|
||||
resourcesFile = aResourcesFile;
|
||||
|
||||
resourcesPath = aResourcesPath;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (File)resourcesFile
|
||||
- (CPArray)allObjects
|
||||
{
|
||||
return resourcesFile;
|
||||
return _objects;
|
||||
}
|
||||
|
||||
- (File)resourceFileForName:(CPString)aName
|
||||
- (CPString)resourcePathForName:(CPString)aName
|
||||
{
|
||||
var moreFiles = [resourcesFile.listFiles()];
|
||||
if (!resourcesPath)
|
||||
return NULL;
|
||||
|
||||
do
|
||||
var pathGroups = [File.listPaths(resourcesPath)];
|
||||
|
||||
while (pathGroups.length > 0)
|
||||
{
|
||||
var files = moreFiles.shift(),
|
||||
var paths = pathGroups.shift(),
|
||||
index = 0,
|
||||
count = files.length;
|
||||
|
||||
count = paths.length;
|
||||
|
||||
for (; index < count; ++index)
|
||||
{
|
||||
var file = files[index].getCanonicalFile(),
|
||||
name = String(file.getName());
|
||||
|
||||
if (name === aName)
|
||||
return file;
|
||||
|
||||
if (file.isDirectory())
|
||||
moreFiles.push(file.listFiles());
|
||||
var path = paths[index];
|
||||
|
||||
if (File.basename(path) === aName)
|
||||
return path;
|
||||
|
||||
if (File.isDirectory(path))
|
||||
pathGroups.push(File.listPaths(path));
|
||||
}
|
||||
}
|
||||
while (moreFiles.length > 0)
|
||||
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
File.listPaths = function(aPath)
|
||||
{
|
||||
var paths = File.list(aPath),
|
||||
count = paths.length;
|
||||
|
||||
while (count--)
|
||||
paths[count] = File.join(aPath, paths[count]);
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
+46
-145
@@ -20,14 +20,15 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
|
||||
@import <Foundation/Foundation.j>
|
||||
|
||||
@import <AppKit/CPCib.j>
|
||||
|
||||
@import "NSFoundation.j"
|
||||
@import "NSAppKit.j"
|
||||
|
||||
@import "Nib2CibKeyedUnarchiver.j"
|
||||
@import "Converter.j"
|
||||
|
||||
var File = require("file");
|
||||
|
||||
@@ -35,154 +36,56 @@ importPackage(java.io);
|
||||
|
||||
CPLogRegister(CPLogPrint);
|
||||
|
||||
function exec(command)
|
||||
{
|
||||
var p = Packages.java.lang.Runtime.getRuntime().exec(command);
|
||||
var result = p.waitFor();
|
||||
|
||||
var reader = new Packages.java.io.BufferedReader(new Packages.java.io.InputStreamReader(p.getInputStream()));
|
||||
while (s = reader.readLine())
|
||||
print(s);
|
||||
|
||||
var reader = new Packages.java.io.BufferedReader(new Packages.java.io.InputStreamReader(p.getErrorStream()));
|
||||
while (s = reader.readLine())
|
||||
print(s);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function printUsage()
|
||||
{
|
||||
java.lang.System.out.println("usage: nib2cib INPUT_FILE [OUTPUT_FILE] [-F /path/to/required/framework]");
|
||||
print("usage: nib2cib INPUT_FILE [OUTPUT_FILE] [-F /path/to/required/framework] [-R path/to/resources]");
|
||||
java.lang.System.exit(1);
|
||||
}
|
||||
|
||||
function cibExtension(aPath)
|
||||
{
|
||||
var start = aPath.length - 1;
|
||||
|
||||
while (aPath.charAt(start) === '/')
|
||||
start--;
|
||||
|
||||
aPath = aPath.substr(0, start + 1);
|
||||
|
||||
var dotIndex = aPath.lastIndexOf('.');
|
||||
|
||||
if (dotIndex == -1)
|
||||
return aPath + ".cib";
|
||||
|
||||
var slashIndex = aPath.lastIndexOf('/');
|
||||
|
||||
if (slashIndex > dotIndex)
|
||||
return aPath + ".cib";
|
||||
|
||||
return aPath.substr(0, dotIndex) + ".cib";
|
||||
}
|
||||
|
||||
function convert(inputFileName, outputFileName, resourcesPath)
|
||||
{
|
||||
var resourcesFile = nil;
|
||||
|
||||
if (resourcesPath)
|
||||
{
|
||||
resourcesFile = new java.io.File(resourcesPath).getCanonicalFile();
|
||||
|
||||
if (!resourcesFile.canRead())
|
||||
{
|
||||
print("Could not find Resources at " + resourcesFile);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure we can read the file
|
||||
if (!(new Packages.java.io.File(inputFileName)).canRead())
|
||||
{
|
||||
print("Could not read file at " + inputFileName);
|
||||
return;
|
||||
}
|
||||
|
||||
// Compile xib or nib to make sure we have a non-new format nib.
|
||||
var temporaryNibFile = java.io.File.createTempFile("temp", ".nib"),
|
||||
temporaryNibFilePath = temporaryNibFile.getAbsolutePath();
|
||||
|
||||
temporaryNibFile.deleteOnExit();
|
||||
|
||||
if (exec(["/usr/bin/ibtool", inputFileName, "--compile", temporaryNibFilePath]))
|
||||
{
|
||||
print("Could not compile file at " + inputFileName);
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert from binary plist to XML plist
|
||||
var temporaryPlistFile = java.io.File.createTempFile("temp", ".plist"),
|
||||
temporaryPlistFilePath = String(temporaryPlistFile.getAbsolutePath());
|
||||
|
||||
temporaryPlistFile.deleteOnExit();
|
||||
|
||||
if (exec(["/usr/bin/plutil", "-convert", "xml1", temporaryNibFilePath, "-o", temporaryPlistFilePath]))
|
||||
{
|
||||
print("Could not convert to xml plist for file at " + inputFileName);
|
||||
return;
|
||||
}
|
||||
|
||||
var data = [CPData dataWithString:File.read(String(temporaryPlistFilePath), { charset:"UTF-8" })];
|
||||
|
||||
// Minor NSKeyedArchive to CPKeyedArchive conversion.
|
||||
[data setString:[data string].replace(/\<key\>\s*CF\$UID\s*\<\/key\>/g, "<key>CP$UID</key>")];
|
||||
|
||||
// Unarchive the NS data
|
||||
var unarchiver = [[Nib2CibKeyedUnarchiver alloc] initForReadingWithData:data resourcesFile:resourcesFile],
|
||||
objectData = [unarchiver decodeObjectForKey:@"IB.objectdata"],
|
||||
|
||||
data = [CPData data],
|
||||
archiver = [[CPKeyedArchiver alloc] initForWritingWithMutableData:data];
|
||||
|
||||
// Re-archive the CP data.
|
||||
[archiver encodeObject:objectData forKey:@"CPCibObjectDataKey"];
|
||||
[archiver finishEncoding];
|
||||
|
||||
File.write(outputFileName, [data string], { charset:"UTF-8" });
|
||||
}
|
||||
|
||||
function loadFrameworks(frameworkPaths, aCallback)
|
||||
{
|
||||
if (frameworkPaths.length === 0)
|
||||
if (!frameworkPaths || frameworkPaths.length === 0)
|
||||
return aCallback();
|
||||
|
||||
|
||||
var frameworkPath = frameworkPaths.shift(),
|
||||
|
||||
infoPlist = new java.io.File(frameworkPath + "/Info.plist");
|
||||
|
||||
if (!infoPlist.exists())
|
||||
infoPlistPath = frameworkPath + "/Info.plist";
|
||||
|
||||
if (!File.isReadable(infoPlistPath))
|
||||
{
|
||||
java.lang.System.out.println("'" + frameworkPath + "' is not a framework or could not be found.");
|
||||
print("'" + frameworkPath + "' is not a framework or could not be found.");
|
||||
java.lang.System.exit(1);
|
||||
}
|
||||
|
||||
var infoDictionary = CPPropertyListCreateFromData([CPData dataWithString:File.read(frameworkPath + "/Info.plist", { charset:"UTF-8" })]);
|
||||
|
||||
var infoDictionary = CPPropertyListCreateFromData([CPData dataWithString:File.read(infoPlistPath, { charset:"UTF-8" })]);
|
||||
|
||||
if ([infoDictionary objectForKey:@"CPBundlePackageType"] !== "FMWK")
|
||||
{
|
||||
java.lang.System.out.println("'" + frameworkPath + "' is not a framework.");
|
||||
print("'" + frameworkPath + "' is not a framework.");
|
||||
java.lang.System.exit(1);
|
||||
}
|
||||
|
||||
print("Loading " + [infoDictionary objectForKey:@"CPBundleName"]);
|
||||
|
||||
var files = [infoDictionary objectForKey:@"CPBundleReplacedFiles"],
|
||||
count = files.length;
|
||||
|
||||
|
||||
if (count)
|
||||
{
|
||||
var context = new objj_context();
|
||||
|
||||
context.didCompleteCallback = function() { loadFrameworks(frameworkPaths, aCallback) };
|
||||
|
||||
print("2he");
|
||||
while (count--)
|
||||
{
|
||||
print(frameworkPath + '/' + files[count]);
|
||||
context.pushFragment(fragment_create_file(frameworkPath + '/' + files[count], new objj_bundle(""), YES, NULL));
|
||||
|
||||
context.evaluate();
|
||||
}
|
||||
print("hmmm");
|
||||
context.evaluate();print("wha???");
|
||||
}
|
||||
else
|
||||
loadFrameworks(frameworkPaths, aCallback);
|
||||
print("so far so good...");
|
||||
}
|
||||
|
||||
function main()
|
||||
@@ -190,41 +93,39 @@ function main()
|
||||
var count = arguments.length;
|
||||
|
||||
if (count < 1)
|
||||
printUsage();
|
||||
return printUsage();
|
||||
|
||||
var index = 0,
|
||||
|
||||
inputFileName = nil,
|
||||
outputFileName = nil,
|
||||
resourcesPath = nil,
|
||||
frameworkPaths = [];
|
||||
|
||||
frameworkPaths = [],
|
||||
converter = [[Converter alloc] init];
|
||||
|
||||
for (; index < count; ++index)
|
||||
{
|
||||
switch(arguments[index])
|
||||
{
|
||||
case "-help":
|
||||
case "--help": printUsage();
|
||||
|
||||
case "-F": frameworkPaths.push(arguments[++index]);
|
||||
break;
|
||||
|
||||
case "-R": resourcesPath = arguments[++index];
|
||||
break;
|
||||
|
||||
default: if (inputFileName && inputFileName.length > 0)
|
||||
outputFileName = arguments[index];
|
||||
else
|
||||
inputFileName = arguments[index];
|
||||
case "--help": printUsage();
|
||||
break;
|
||||
|
||||
case "--mac": [converter setFormat:NibFormatMac];
|
||||
break;
|
||||
|
||||
case "-F": frameworkPaths.push(arguments[++index]);
|
||||
break;
|
||||
|
||||
case "-R": [converter setResourcesPath:arguments[++index]];
|
||||
break;
|
||||
|
||||
default: if ([converter inputPath])
|
||||
[converter setOutputPath:arguments[index]];
|
||||
else
|
||||
[converter setInputPath:arguments[index]];
|
||||
}
|
||||
}
|
||||
|
||||
if (!outputFileName || outputFileName.length < 1)
|
||||
outputFileName = cibExtension(inputFileName);
|
||||
|
||||
if (frameworkPaths.length)
|
||||
loadFrameworks(frameworkPaths, function() { convert(inputFileName, outputFileName, resourcesPath); });
|
||||
|
||||
else
|
||||
convert(inputFileName, outputFileName, resourcesPath);
|
||||
loadFrameworks(frameworkPaths, function()
|
||||
{
|
||||
[converter convert];
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user