mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-09-04 18:03:41 +00:00
Merge branch 'master' into fontinfo-path-fix
This commit is contained in:
@@ -589,9 +589,7 @@
|
||||
_mouseDownEvent = anEvent;
|
||||
|
||||
var location = [self convertPoint:[anEvent locationInWindow] fromView:nil],
|
||||
row = FLOOR(location.y / (_itemSize.height + _verticalMargin)),
|
||||
column = FLOOR(location.x / (_itemSize.width + _horizontalMargin)),
|
||||
index = row * _numberOfColumns + column;
|
||||
index = [self _indexAtPoint:location];
|
||||
|
||||
if (index >= 0 && index < _items.length)
|
||||
{
|
||||
@@ -727,6 +725,28 @@
|
||||
return _delegate;
|
||||
}
|
||||
|
||||
/*!
|
||||
@ignore
|
||||
*/
|
||||
- (CPMenu)menuForEvent:(CPEvent)theEvent
|
||||
{
|
||||
if (![[self delegate] respondsToSelector:@selector(collectionView:menuForItemAtIndex:)])
|
||||
return [super menuForEvent:theEvent];
|
||||
|
||||
var location = [self convertPoint:[theEvent locationInWindow] fromView:nil],
|
||||
index = [self _indexAtPoint:location];
|
||||
|
||||
return [_delegate collectionView:self menuForItemAtIndex:index];
|
||||
}
|
||||
|
||||
- (int)_indexAtPoint:(CGPoint)thePoint
|
||||
{
|
||||
var row = FLOOR(thePoint.y / (_itemSize.height + _verticalMargin)),
|
||||
column = FLOOR(thePoint.x / (_itemSize.width + _horizontalMargin));
|
||||
|
||||
return row * _numberOfColumns + column;
|
||||
}
|
||||
|
||||
- (CPCollectionViewItem)itemAtIndex:(unsigned)anIndex
|
||||
{
|
||||
return [_items objectAtIndex:anIndex];
|
||||
|
||||
+13
-9
@@ -270,10 +270,10 @@ var CPDraggingSource_draggedImage_movedTo_ = 1 << 0,
|
||||
{
|
||||
if ([scrollView hasVerticalScroller])
|
||||
{
|
||||
if (eventLocation.y < CGRectGetMinY(insetBounds))
|
||||
deltaY = CGRectGetMinY(insetBounds) - eventLocation.y;
|
||||
else if (eventLocation.y > CGRectGetMaxY(insetBounds))
|
||||
deltaY = CGRectGetMaxY(insetBounds) - eventLocation.y;
|
||||
if (eventLocation.y < _CGRectGetMinY(insetBounds))
|
||||
deltaY = _CGRectGetMinY(insetBounds) - eventLocation.y;
|
||||
else if (eventLocation.y > _CGRectGetMaxY(insetBounds))
|
||||
deltaY = _CGRectGetMaxY(insetBounds) - eventLocation.y;
|
||||
if (deltaY < -insetBounds.size.height)
|
||||
deltaY = -insetBounds.size.height;
|
||||
if (deltaY > insetBounds.size.height)
|
||||
@@ -282,17 +282,21 @@ var CPDraggingSource_draggedImage_movedTo_ = 1 << 0,
|
||||
|
||||
if ([scrollView hasHorizontalScroller])
|
||||
{
|
||||
if (eventLocation.x < CGRectGetMinX(insetBounds))
|
||||
deltaX = CGRectGetMinX(insetBounds) - eventLocation.x;
|
||||
else if (eventLocation.x > CGRectGetMaxX(insetBounds))
|
||||
deltaX = CGRectGetMaxX(insetBounds) - eventLocation.x;
|
||||
if (eventLocation.x < _CGRectGetMinX(insetBounds))
|
||||
deltaX = _CGRectGetMinX(insetBounds) - eventLocation.x;
|
||||
else if (eventLocation.x > _CGRectGetMaxX(insetBounds))
|
||||
deltaX = _CGRectGetMaxX(insetBounds) - eventLocation.x;
|
||||
if (deltaX < -insetBounds.size.width)
|
||||
deltaX = -insetBounds.size.width;
|
||||
if (deltaX > insetBounds.size.width)
|
||||
deltaX = insetBounds.size.width;
|
||||
}
|
||||
|
||||
[contentView scrollToPoint:CGPointMake(bounds.origin.x - deltaX, bounds.origin.y - deltaY)];
|
||||
var scrollPoint = _CGPointMake(bounds.origin.x - deltaX, bounds.origin.y - deltaY);
|
||||
|
||||
[contentView scrollToPoint:scrollPoint];
|
||||
[[scrollView _headerView] scrollPoint:scrollPoint];
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+26
-2
@@ -536,7 +536,6 @@ var _CPMenuBarVisible = NO,
|
||||
*/
|
||||
- (void)submenuAction:(id)aSender
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -584,11 +583,34 @@ var _CPMenuBarVisible = NO,
|
||||
}
|
||||
|
||||
/*!
|
||||
Not implemented.
|
||||
Enables or disables the receiver’s menu items.
|
||||
If the target does not implement the menu item's action method the item is disabled.
|
||||
If the target responsds to selector validateMenuItem: or validateUserInterfaceItem: (in that order) the return value is used.
|
||||
*/
|
||||
- (void)update
|
||||
{
|
||||
if (![self autoenablesItems])
|
||||
return;
|
||||
|
||||
var items = [self itemArray];
|
||||
for (var i = 0; i < [items count]; i++)
|
||||
{
|
||||
var item = [items objectAtIndex:i];
|
||||
|
||||
if ([item hasSubmenu])
|
||||
continue;
|
||||
|
||||
var validator = [CPApp targetForAction:[item action] to:[item target] from:item];
|
||||
|
||||
if (!validator || ![validator respondsToSelector:[item action]])
|
||||
[item _setEnabled:NO];
|
||||
else if ([validator respondsToSelector:@selector(validateMenuItem:)])
|
||||
[item _setEnabled:[validator validateMenuItem:item]];
|
||||
else if ([validator respondsToSelector:@selector(validateUserInterfaceItem:)])
|
||||
[item _setEnabled:[validator validateUserInterfaceItem:item]];
|
||||
}
|
||||
|
||||
[[_menuWindow _menuView] tile];
|
||||
}
|
||||
|
||||
// Managing the Title
|
||||
@@ -1100,6 +1122,8 @@ var CPMenuTitleKey = @"CPMenuTitleKey",
|
||||
|
||||
_showsStateColumn = ![aCoder containsValueForKey:CPMenuShowsStateColumnKey] || [aCoder decodeBoolForKey:CPMenuShowsStateColumnKey];
|
||||
|
||||
_autoenablesItems = YES;
|
||||
|
||||
[self setMinimumWidth:0];
|
||||
}
|
||||
|
||||
|
||||
@@ -243,9 +243,15 @@ var STICKY_TIME_INTERVAL = 500,
|
||||
// Close the current menu item because we are going to select a new one after a short delay
|
||||
[self showMenu:nil fromMenu:activeMenu atPoint:CGPointMakeZero()];
|
||||
|
||||
_showTimerID = setTimeout(function() {
|
||||
|
||||
if (![activeMenuContainer isMenuBar])
|
||||
{
|
||||
_showTimerID = setTimeout(function() {
|
||||
[self showMenu:[activeItem submenu] fromMenu:[activeItem menu] atPoint:newMenuOrigin];
|
||||
}, 250);
|
||||
}
|
||||
else
|
||||
[self showMenu:[activeItem submenu] fromMenu:[activeItem menu] atPoint:newMenuOrigin];
|
||||
}, 250);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -208,8 +208,14 @@ var STICKY_TIME_INTERVAL = 500,
|
||||
return [_menuView menu];
|
||||
}
|
||||
|
||||
- (_CPMenuView)_menuView
|
||||
{
|
||||
return _menuView;
|
||||
}
|
||||
|
||||
- (void)orderFront:(id)aSender
|
||||
{
|
||||
[[self menu] update];
|
||||
[self setFrame:_unconstrainedFrame];
|
||||
|
||||
[super orderFront:aSender];
|
||||
|
||||
@@ -133,10 +133,17 @@
|
||||
if ([_menu autoenablesItems])
|
||||
return;
|
||||
|
||||
_isEnabled = isEnabled;
|
||||
[self _setEnabled:isEnabled];
|
||||
}
|
||||
|
||||
- (void)_setEnabled:(BOOL)isEnabled
|
||||
{
|
||||
if (_isEnabled === isEnabled)
|
||||
return;
|
||||
|
||||
_isEnabled = !!isEnabled;
|
||||
|
||||
[_menuItemView setDirty];
|
||||
|
||||
[_menu itemChanged:self];
|
||||
}
|
||||
|
||||
@@ -851,6 +858,11 @@ CPControlKeyMask
|
||||
return ![self submenu] && [self menu] === [CPApp mainMenu];
|
||||
}
|
||||
|
||||
- (CPString)description
|
||||
{
|
||||
return [super description] + @" target: " + [self target] + @" action: " + CPStringFromSelector([self action]);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPMenuItemIsSeparatorKey = @"CPMenuItemIsSeparatorKey",
|
||||
|
||||
+44
-26
@@ -739,41 +739,59 @@ var CPSplitViewHorizontalImage = nil,
|
||||
Sets the delegate of the receiver.
|
||||
Possible delegate methods to implement are listed below.
|
||||
|
||||
<pre>
|
||||
- (void)splitViewDidResizeSubviews:(CPSplitView)aSplitView;
|
||||
Notifies the delegate when the subviews have resized.
|
||||
Notifies the delegate when the subviews have resized.
|
||||
@code
|
||||
- (void)splitViewDidResizeSubviews:(CPNotification)aNotification;
|
||||
@endcode
|
||||
|
||||
- (void)splitViewWillResizeSubviews:(CPSplitView)aSplitView;
|
||||
Notifies the delegate when the subviews will be resized.
|
||||
Notifies the delegate when the subviews will be resized.
|
||||
@code
|
||||
- (void)splitViewWillResizeSubviews:(CPNotification)aNotification;
|
||||
@endcode
|
||||
|
||||
- (CGRect)splitView:(CPSplitView)aSplitView effectiveRect:(CGRect)aRect forDrawnRect:(CGRect)aDrawnRect ofDividerAtIndex:(int)aDividerIndex;
|
||||
Lets the delegate specify a different rect for which the user can drag the splitView divider.
|
||||
Lets the delegate specify a different rect for which the user can drag the splitView divider.
|
||||
@code
|
||||
- (CGRect)splitView:(CPSplitView)aSplitView effectiveRect:(CGRect)aRect forDrawnRect:(CGRect)aDrawnRect ofDividerAtIndex:(int)aDividerIndex;
|
||||
@endcode
|
||||
|
||||
- (CGRect)splitView:(CPSplitView)aSplitView additionalEffectiveRectOfDividerAtIndex:(int)indexOfDivider;
|
||||
Lets the delegate specify an additional rect for which the user can drag the splitview divider.
|
||||
Lets the delegate specify an additional rect for which the user can drag the splitview divider.
|
||||
@code
|
||||
- (CGRect)splitView:(CPSplitView)aSplitView additionalEffectiveRectOfDividerAtIndex:(int)indexOfDivider;
|
||||
@endcode
|
||||
|
||||
- (BOOL)splitView:(CPSplitView)aSplitView canCollapseSubview:(CPView)aSubview;
|
||||
Notifies the delegate that the splitview is about to be collapsed. This usually happens when the user
|
||||
Double clicks on the divider. Return YES if the subview can be collapsed, otherwise NO.
|
||||
Notifies the delegate that the splitview is about to be collapsed. This usually happens when the user
|
||||
Double clicks on the divider. Return YES if the subview can be collapsed, otherwise NO.
|
||||
@code
|
||||
- (BOOL)splitView:(CPSplitView)aSplitView canCollapseSubview:(CPView)aSubview;
|
||||
@endcode
|
||||
|
||||
- (BOOL)splitView:(CPSplitView)aSplitView shouldCollapseSubview:(CPView)aSubview forDoubleClickOnDividerAtIndex:(int)indexOfDivider;
|
||||
Notifies the delegate that the subview at indexOfDivider is about to be collapsed. This usually happens when the user
|
||||
Double clicks on the divider. Return YES if the subview should be collapsed, otherwise NO.
|
||||
Notifies the delegate that the subview at indexOfDivider is about to be collapsed. This usually happens when the user
|
||||
Double clicks on the divider. Return YES if the subview should be collapsed, otherwise NO.
|
||||
@code
|
||||
- (BOOL)splitView:(CPSplitView)aSplitView shouldCollapseSubview:(CPView)aSubview forDoubleClickOnDividerAtIndex:(int)indexOfDivider;
|
||||
@endcode
|
||||
|
||||
- (float)splitView:(CPSplitView)aSpiltView constrainSplitPosition:(float)proposedPosition ofSubviewAt:(int)subviewIndex;
|
||||
Allows the delegate to constrain the subview beings resized. This method is called continuously as the user resizes the divider.
|
||||
For example if the subview needs to have a width which is a multiple of a certain number you could return that multiple with this method.
|
||||
Allows the delegate to constrain the subview beings resized. This method is called continuously as the user resizes the divider.
|
||||
For example if the subview needs to have a width which is a multiple of a certain number you could return that multiple with this method.
|
||||
@code
|
||||
- (float)splitView:(CPSplitView)aSpiltView constrainSplitPosition:(float)proposedPosition ofSubviewAt:(int)subviewIndex;
|
||||
@endcode
|
||||
|
||||
- (float)splitView:(CPSplitView)aSplitView constrainMinCoordinate:(float)proposedMin ofSubviewAt:(int)subviewIndex;
|
||||
Allows the delegate to constrain the minimum position of a subview.
|
||||
Allows the delegate to constrain the minimum position of a subview.
|
||||
@code
|
||||
- (float)splitView:(CPSplitView)aSplitView constrainMinCoordinate:(float)proposedMin ofSubviewAt:(int)subviewIndex;
|
||||
@endcode
|
||||
|
||||
- (float)splitView:(CPSplitView)aSplitView constrainMaxCoordinate:(float)proposedMax ofSubviewAt:(int)subviewIndex;
|
||||
Allows the delegate to constrain the maximum position of a subview.
|
||||
Allows the delegate to constrain the maximum position of a subview.
|
||||
@code
|
||||
- (float)splitView:(CPSplitView)aSplitView constrainMaxCoordinate:(float)proposedMax ofSubviewAt:(int)subviewIndex;
|
||||
@endcode
|
||||
|
||||
- (void)splitView:(CPSplitView)aSplitView resizeSubviewsWithOldSize:(CGSize)oldSize;
|
||||
Allows the splitview to specify a custom resizing behavior. This is called when the splitview is resized.
|
||||
The sum of the views and the sum of the dividers should be equal to the size of the splitview.
|
||||
</pre>
|
||||
Allows the splitview to specify a custom resizing behavior. This is called when the splitview is resized.
|
||||
The sum of the views and the sum of the dividers should be equal to the size of the splitview.
|
||||
@code
|
||||
- (void)splitView:(CPSplitView)aSplitView resizeSubviewsWithOldSize:(CGSize)oldSize;
|
||||
@endcode
|
||||
|
||||
@param delegate - The delegate of the splitview.
|
||||
*/
|
||||
|
||||
+17
-13
@@ -193,24 +193,24 @@ var SHADOW_MARGIN_LEFT = 20.0,
|
||||
_CPWindowShadowColor = nil;
|
||||
|
||||
var CPWindowSaveImage = nil,
|
||||
CPWindowSavingImage = nil;
|
||||
CPWindowSavingImage = nil,
|
||||
|
||||
var CPWindowResizeTime = 0.2;
|
||||
CPWindowResizeTime = 0.2;
|
||||
|
||||
/*
|
||||
Keys for which action messages will be sent by default when unhandled, e.g. complete:.
|
||||
*/
|
||||
var CPWindowActionMessageKeys = [
|
||||
CPLeftArrowFunctionKey,
|
||||
CPRightArrowFunctionKey,
|
||||
CPUpArrowFunctionKey,
|
||||
CPDownArrowFunctionKey,
|
||||
CPPageUpFunctionKey,
|
||||
CPPageDownFunctionKey,
|
||||
CPHomeFunctionKey,
|
||||
CPEndFunctionKey,
|
||||
CPEscapeFunctionKey
|
||||
];
|
||||
CPLeftArrowFunctionKey,
|
||||
CPRightArrowFunctionKey,
|
||||
CPUpArrowFunctionKey,
|
||||
CPDownArrowFunctionKey,
|
||||
CPPageUpFunctionKey,
|
||||
CPPageDownFunctionKey,
|
||||
CPHomeFunctionKey,
|
||||
CPEndFunctionKey,
|
||||
CPEscapeFunctionKey
|
||||
];
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@@ -2377,7 +2377,9 @@ CPTexturedBackgroundWindowMask
|
||||
// an event going of the responder chain is passed to the input system as a last resort.
|
||||
// However, the only methods I could get Cocoa to call automatically are
|
||||
// moveUp: moveDown: moveLeft: moveRight: pageUp: pageDown: and complete:
|
||||
[self _processKeyboardUIKey:anEvent];
|
||||
// Unhandled events just travel further up the responder chain _past_ the window.
|
||||
if (![self _processKeyboardUIKey:anEvent])
|
||||
[super keyDown:anEvent];
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2412,6 +2414,8 @@ CPTexturedBackgroundWindowMask
|
||||
// The difference is that doCommandBySelector: will also send the action to the window and application delegates.
|
||||
[[self firstResponder] doCommandBySelector:@selector(complete:)];
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)_dirtyKeyViewLoop
|
||||
|
||||
@@ -47,12 +47,8 @@
|
||||
|
||||
// If the destination doesn't respond to this selector, warn but don't die.
|
||||
if (_destination && ![_destination respondsToSelector:selector])
|
||||
{
|
||||
CPLog.warn(@"Could not connect the action " + selector + @" to target of class " + [_destination className]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Not being able to set the action is a fatal error.
|
||||
if ([_source respondsToSelector:@selector(setAction:)])
|
||||
objj_msgSend(_source, @selector(setAction:), selector);
|
||||
|
||||
@@ -108,7 +108,7 @@ function CGColorCreateCopy(aColor)
|
||||
*/
|
||||
function CGColorCreateGenericGray(gray, alpha)
|
||||
{
|
||||
return CGColorCreate(0, [gray, alpha]);
|
||||
return CGColorCreate(CGColorSpaceCreateDeviceRGB(), [gray,gray,gray, alpha]);
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -122,7 +122,7 @@ function CGColorCreateGenericGray(gray, alpha)
|
||||
*/
|
||||
function CGColorCreateGenericRGB(red, green, blue, alpha)
|
||||
{
|
||||
return CGColorCreate(0, [red, green, blue, alpha]);
|
||||
return CGColorCreate(CGColorSpaceCreateDeviceRGB(), [red, green, blue, alpha]);
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -137,7 +137,8 @@ function CGColorCreateGenericRGB(red, green, blue, alpha)
|
||||
*/
|
||||
function CGColorCreateGenericCMYK(cyan, magenta, yellow, black, alpha)
|
||||
{
|
||||
return CGColorCreate(0, [cyan, magenta, yellow, black, alpha]);
|
||||
return CGColorCreate(CGColorSpaceCreateDeviceCMYK(),
|
||||
[cyan, magenta, yellow, black, alpha]);
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -149,19 +150,21 @@ function CGColorCreateGenericCMYK(cyan, magenta, yellow, black, alpha)
|
||||
*/
|
||||
function CGColorCreateCopyWithAlpha(aColor, anAlpha)
|
||||
{
|
||||
var components = aColor.components;
|
||||
if ( !aColor ) return aColor; // Avoid error null pointer in next line
|
||||
|
||||
if (!aColor || anAlpha == components[components.length - 1])
|
||||
var components = aColor.components.slice();
|
||||
|
||||
if (anAlpha == components[components.length - 1])
|
||||
return aColor;
|
||||
|
||||
// set new alpha value now so that a potentially a new cache entry is made and
|
||||
// not that an existing cache entry is mutated.
|
||||
components[components.length - 1] = anAlpha;
|
||||
|
||||
if (aColor.pattern)
|
||||
var copy = CGColorCreateWithPattern(aColor.colorspace, aColor.pattern, components);
|
||||
return CGColorCreateWithPattern(aColor.colorspace, aColor.pattern, components);
|
||||
else
|
||||
var copy = CGColorCreate(aColor.colorspace, components);
|
||||
|
||||
copy.components[components.length - 1] = anAlpha;
|
||||
|
||||
return copy;
|
||||
return CGColorCreate(aColor.colorspace, components);
|
||||
}
|
||||
|
||||
/*!
|
||||
|
||||
@@ -129,7 +129,7 @@ function CGGStateCreateCopy(aGState)
|
||||
return { alpha:aGState.alpha, strokeStyle:aGState.strokeStyle, fillStyle:aGState.fillStyle, lineWidth:aGState.lineWidth,
|
||||
lineJoin:aGState.lineJoin, lineCap:aGState.lineCap, miterLimit:aGState.miterLimit, globalAlpha:aGState.globalAlpha,
|
||||
blendMode:aGState.blendMode,
|
||||
shadowOffset:aGState.shadowOffset, shadowBlur:aGState.shadowBlur, shadowColor:aGState.shadowColor, CTM:_CGAffineTransformMakeCopy(aGState.CTM) };
|
||||
shadowOffset:_CGSizeMakeCopy(aGState.shadowOffset), shadowBlur:aGState.shadowBlur, shadowColor:aGState.shadowColor, CTM:_CGAffineTransformMakeCopy(aGState.CTM) };
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -606,7 +606,7 @@ function CGContextStrokeLineSegments(aContext, points, count)
|
||||
{
|
||||
var i = 0;
|
||||
|
||||
if (arguments["count"] == NULL)
|
||||
if (count === NULL)
|
||||
var count = points.length;
|
||||
|
||||
CGContextBeginPath(aContext);
|
||||
|
||||
@@ -144,7 +144,7 @@ function CGContextAddRects(aContext, rects, count)
|
||||
{
|
||||
var i = 0;
|
||||
|
||||
if (arguments["count"] == NULL)
|
||||
if (count === NULL)
|
||||
var count = rects.length;
|
||||
|
||||
for (; i < count; ++i)
|
||||
@@ -194,7 +194,7 @@ function CGContextFillRects(aContext, rects, count)
|
||||
{
|
||||
var i = 0;
|
||||
|
||||
if (arguments["count"] == NULL)
|
||||
if (count === NULL)
|
||||
var count = rects.length;
|
||||
|
||||
for (; i < count; ++i)
|
||||
@@ -225,7 +225,7 @@ function CGContextClipToRect(aContext, aRect)
|
||||
|
||||
function CGContextClipToRects(aContext, rects, count)
|
||||
{
|
||||
if (arguments["count"] == NULL)
|
||||
if (count === NULL)
|
||||
var count = rects.length;
|
||||
|
||||
_CGContextBeginPathCanvas(aContext);
|
||||
|
||||
@@ -29,10 +29,15 @@ kCGGradientDrawsAfterEndLocation = 1 << 1;
|
||||
|
||||
function CGGradientCreateWithColorComponents(aColorSpace, components, locations, count)
|
||||
{
|
||||
if (arguments["locations"] == NULL)
|
||||
var locations = [0.0, 1.0];
|
||||
if ( locations === undefined || locations === NULL )
|
||||
{
|
||||
var num_of_colors = components.length / 4,
|
||||
locations = [];
|
||||
for ( var idx = 0; idx < num_of_colors; idx++ )
|
||||
locations.push( idx / (num_of_colors - 1) );
|
||||
}
|
||||
|
||||
if (arguments["count"] == NULL)
|
||||
if ( count === undefined || count === NULL)
|
||||
var count = locations.length;
|
||||
|
||||
var colors = [];
|
||||
@@ -58,4 +63,4 @@ function CGGradientRelease()
|
||||
function CGGradientRetain(aGradient)
|
||||
{
|
||||
return aGradient;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,7 +141,7 @@ function CGPathAddLines(aPath, aTransform, points, count)
|
||||
{
|
||||
var i = 1;
|
||||
|
||||
if (arguments["count"] == NULL)
|
||||
if (count === NULL)
|
||||
var count = points.length;
|
||||
|
||||
if (!aPath || count < 2)
|
||||
@@ -224,7 +224,7 @@ function CGPathAddRects(aPath, aTransform, rects, count)
|
||||
{
|
||||
var i = 0;
|
||||
|
||||
if (arguments["count"] == NULL)
|
||||
if (count === NULL)
|
||||
var count = rects.length;
|
||||
|
||||
for (; i < count; ++i)
|
||||
|
||||
@@ -332,7 +332,7 @@
|
||||
[self replaceObjectAtIndex:otherIndex withObject:temporary];
|
||||
}
|
||||
|
||||
- (CPArray)sortUsingDescriptors:(CPArray)descriptors
|
||||
- (void)sortUsingDescriptors:(CPArray)descriptors
|
||||
{
|
||||
[self sortUsingFunction:compareObjectsUsingDescriptors context:descriptors];
|
||||
}
|
||||
|
||||
@@ -678,6 +678,14 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew | CPKeyValueObservingOpti
|
||||
{
|
||||
var observers = _observersForKey[aPath];
|
||||
|
||||
if (!observers)
|
||||
{
|
||||
CPLog.warn(@"Cannot remove an observer %@ for the key path \"%@\" from %@ because it is not registered as an observer.",
|
||||
_targetObject, aPath, anObserver);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (aPath.indexOf('.') != CPNotFound)
|
||||
{
|
||||
var forwarder = [observers objectForKey:[anObserver UID]].forwarder;
|
||||
|
||||
@@ -441,71 +441,48 @@ var _CPKeyedUnarchiverDecodeObjectAtIndex = function(self, anIndex)
|
||||
var object = self._objects[anIndex];
|
||||
|
||||
if (object)
|
||||
{
|
||||
if (object === self._objects[0])
|
||||
return nil;
|
||||
else
|
||||
return object;
|
||||
|
||||
var object,
|
||||
plistObject = self._plistObjects[anIndex],
|
||||
plistObjectClass = plistObject.isa;
|
||||
|
||||
if (plistObjectClass === CPDictionaryClass || plistObjectClass === CPMutableDictionaryClass)
|
||||
// Don't return immediately here. The _CPKeyedArchiverValueClass unwrapper code
|
||||
// hasn't executed yet.
|
||||
}
|
||||
else
|
||||
{
|
||||
var plistClass = self._plistObjects[plistObject.valueForKey(_CPKeyedArchiverClassKey).valueForKey(_CPKeyedArchiverUIDKey)],
|
||||
className = plistClass.valueForKey(_CPKeyedArchiverClassNameKey),
|
||||
classes = plistClass.valueForKey(_CPKeyedArchiverClassesKey),
|
||||
theClass = [self classForClassName:className];
|
||||
var plistObject = self._plistObjects[anIndex],
|
||||
plistObjectClass = plistObject.isa;
|
||||
|
||||
if (!theClass)
|
||||
theClass = CPClassFromString(className);
|
||||
|
||||
if (!theClass && (self._delegateSelectors & CPKeyedUnarchiverDelegate_unarchiver_cannotDecodeObjectOfClassName_originalClasses_))
|
||||
theClass = [_delegate unarchiver:self cannotDecodeObjectOfClassName:className originalClasses:classes];
|
||||
|
||||
if (!theClass)
|
||||
[CPException raise:CPInvalidUnarchiveOperationException reason:@"-[CPKeyedUnarchiver decodeObjectForKey:]: cannot decode object of class (" + className + @")"];
|
||||
|
||||
var savedPlistObject = self._plistObject;
|
||||
|
||||
self._plistObject = plistObject;
|
||||
|
||||
// Should we only call this on _CPCibClassSwapper? (currently the only class that makes use of this).
|
||||
object = [theClass allocWithCoder:self];
|
||||
|
||||
// It is important to do this before calling initWithCoder so that decoding can be self referential (something = self).
|
||||
self._objects[anIndex] = object;
|
||||
|
||||
var processedObject = [object initWithCoder:self];
|
||||
|
||||
self._plistObject = savedPlistObject;
|
||||
|
||||
if (processedObject !== object)
|
||||
if (plistObjectClass === CPDictionaryClass || plistObjectClass === CPMutableDictionaryClass)
|
||||
{
|
||||
if (self._delegateSelectors & _CPKeyedUnarchiverWillReplaceObjectWithObjectSelector)
|
||||
[self._delegate unarchiver:self willReplaceObject:object withObject:processedObject];
|
||||
var plistClass = self._plistObjects[plistObject.valueForKey(_CPKeyedArchiverClassKey).valueForKey(_CPKeyedArchiverUIDKey)],
|
||||
className = plistClass.valueForKey(_CPKeyedArchiverClassNameKey),
|
||||
classes = plistClass.valueForKey(_CPKeyedArchiverClassesKey),
|
||||
theClass = [self classForClassName:className];
|
||||
|
||||
object = processedObject;
|
||||
self._objects[anIndex] = processedObject;
|
||||
}
|
||||
if (!theClass)
|
||||
theClass = CPClassFromString(className);
|
||||
|
||||
processedObject = [object awakeAfterUsingCoder:self];
|
||||
if (!theClass && (self._delegateSelectors & CPKeyedUnarchiverDelegate_unarchiver_cannotDecodeObjectOfClassName_originalClasses_))
|
||||
theClass = [_delegate unarchiver:self cannotDecodeObjectOfClassName:className originalClasses:classes];
|
||||
|
||||
if (processedObject !== object)
|
||||
{
|
||||
if (self._delegateSelectors & _CPKeyedUnarchiverWillReplaceObjectWithObjectSelector)
|
||||
[self._delegate unarchiver:self willReplaceObject:object withObject:processedObject];
|
||||
if (!theClass)
|
||||
[CPException raise:CPInvalidUnarchiveOperationException reason:@"-[CPKeyedUnarchiver decodeObjectForKey:]: cannot decode object of class (" + className + @")"];
|
||||
|
||||
object = processedObject;
|
||||
self._objects[anIndex] = processedObject;
|
||||
}
|
||||
var savedPlistObject = self._plistObject;
|
||||
|
||||
if (self._delegate)
|
||||
{
|
||||
if (self._delegateSelectors & _CPKeyedUnarchiverDidDecodeObjectSelector)
|
||||
processedObject = [self._delegate unarchiver:self didDecodeObject:object];
|
||||
self._plistObject = plistObject;
|
||||
|
||||
if (processedObject && processedObject != object)
|
||||
// Should we only call this on _CPCibClassSwapper? (currently the only class that makes use of this).
|
||||
object = [theClass allocWithCoder:self];
|
||||
|
||||
// It is important to do this before calling initWithCoder so that decoding can be self referential (something = self).
|
||||
self._objects[anIndex] = object;
|
||||
|
||||
var processedObject = [object initWithCoder:self];
|
||||
|
||||
self._plistObject = savedPlistObject;
|
||||
|
||||
if (processedObject !== object)
|
||||
{
|
||||
if (self._delegateSelectors & _CPKeyedUnarchiverWillReplaceObjectWithObjectSelector)
|
||||
[self._delegate unarchiver:self willReplaceObject:object withObject:processedObject];
|
||||
@@ -513,22 +490,48 @@ var _CPKeyedUnarchiverDecodeObjectAtIndex = function(self, anIndex)
|
||||
object = processedObject;
|
||||
self._objects[anIndex] = processedObject;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
self._objects[anIndex] = object = plistObject;
|
||||
|
||||
if ([object class] === CPStringClass)
|
||||
{
|
||||
if (object === _CPKeyedArchiverNullString)
|
||||
processedObject = [object awakeAfterUsingCoder:self];
|
||||
|
||||
if (processedObject !== object)
|
||||
{
|
||||
self._objects[anIndex] = self._objects[0];
|
||||
if (self._delegateSelectors & _CPKeyedUnarchiverWillReplaceObjectWithObjectSelector)
|
||||
[self._delegate unarchiver:self willReplaceObject:object withObject:processedObject];
|
||||
|
||||
return nil;
|
||||
object = processedObject;
|
||||
self._objects[anIndex] = processedObject;
|
||||
}
|
||||
|
||||
if (self._delegate)
|
||||
{
|
||||
if (self._delegateSelectors & _CPKeyedUnarchiverDidDecodeObjectSelector)
|
||||
processedObject = [self._delegate unarchiver:self didDecodeObject:object];
|
||||
|
||||
if (processedObject && processedObject != object)
|
||||
{
|
||||
if (self._delegateSelectors & _CPKeyedUnarchiverWillReplaceObjectWithObjectSelector)
|
||||
[self._delegate unarchiver:self willReplaceObject:object withObject:processedObject];
|
||||
|
||||
object = processedObject;
|
||||
self._objects[anIndex] = processedObject;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
self._objects[anIndex] = object = plistObject;
|
||||
|
||||
if ([object class] === CPStringClass)
|
||||
{
|
||||
if (object === _CPKeyedArchiverNullString)
|
||||
{
|
||||
self._objects[anIndex] = self._objects[0];
|
||||
|
||||
return nil;
|
||||
}
|
||||
else
|
||||
self._objects[anIndex] = object = plistObject;
|
||||
}
|
||||
else
|
||||
self._objects[anIndex] = object = plistObject;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -227,7 +227,7 @@ CPURLCustomIconKey = @"CPURLCustomIconKey";
|
||||
return self.setResourcePropertyForKey(aKey, anObject);
|
||||
}
|
||||
|
||||
- (CPString)staticResourceData
|
||||
- (CPData)staticResourceData
|
||||
{
|
||||
return self.staticResourceData();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,511 @@
|
||||
@import <AppKit/CGAffineTransform.j>
|
||||
@import "CGTestCase.j"
|
||||
|
||||
@implementation CGAffineTransformTest : CGTestCase
|
||||
|
||||
- (void)testAffineTransformMake
|
||||
{
|
||||
[self compareTransform:CGAffineTransformMake(1, 2, 3, 4, 3.2, 5.4)
|
||||
with:{ a: 1, b: 2, c:3, d:4, tx:3.2, ty:5.4 }
|
||||
message:"transform make"];
|
||||
}
|
||||
|
||||
- (void)testCGAffineTransformMakeIdentity
|
||||
{
|
||||
[self compareTransform:CGAffineTransformMakeIdentity()
|
||||
with:{ a: 1, b: 0, c:0, d:1, tx:0, ty:0 }
|
||||
message:"transform make identity"];
|
||||
}
|
||||
|
||||
- (void)testAffineTransformMakeCopy
|
||||
{
|
||||
var transform = CGAffineTransformMake(1, 2, 3, 4, 3.2, 5.4),
|
||||
t2 = CGAffineTransformMakeCopy(transform);
|
||||
|
||||
transform.a = transform.b = transform.c = transform.d =
|
||||
transform.tx = transform.ty = 0;
|
||||
|
||||
[self compareTransform:t2
|
||||
with:CGAffineTransformMake( 1,2,3,4,3.2,5.4 )
|
||||
message:"copy correctly made"];
|
||||
|
||||
[self compareTransform:transform
|
||||
with:CGAffineTransformMake( 0, 0, 0, 0, 0, 0 )
|
||||
message:"original transform was changed"] ;
|
||||
}
|
||||
|
||||
- (void)testAffineTransformCreateCopy
|
||||
{
|
||||
// FIXME: CGAffineTransformCreateCopy seems to be working but code has a Fixme?
|
||||
var transform = CGAffineTransformMake(1, 2, 3, 4, 3.2, 5.4),
|
||||
t2 = CGAffineTransformCreateCopy(transform);
|
||||
|
||||
transform.a = transform.b = transform.c = transform.d =
|
||||
transform.tx = transform.ty = 0;
|
||||
|
||||
[self compareTransform:t2
|
||||
with:CGAffineTransformMake( 1,2,3,4,3.2,5.4 )
|
||||
message:"copy correctly made"];
|
||||
|
||||
[self compareTransform:transform
|
||||
with:CGAffineTransformMake( 0, 0, 0, 0, 0, 0 )
|
||||
message:"original transform was changed"] ;
|
||||
}
|
||||
|
||||
- (void)testAffineTransformMakeScale
|
||||
{
|
||||
[self compareTransform:CGAffineTransformMakeScale(3,4)
|
||||
with:CGAffineTransformMake(3, 0, 0, 4, 0, 0)
|
||||
message:"make scale"];
|
||||
}
|
||||
|
||||
- (void)testAffineTransformMakeTranslation
|
||||
{
|
||||
[self compareTransform:CGAffineTransformMakeTranslation(3,4)
|
||||
with:CGAffineTransformMake(1,0,0,1,3,4)
|
||||
message:"make translation"];
|
||||
}
|
||||
|
||||
- (void)testAffineTransformTranslate
|
||||
{
|
||||
var transform = CGAffineTransformMakeTranslation(3,4);
|
||||
|
||||
[self compareTransform:CGAffineTransformTranslate(transform,-3,-4)
|
||||
with:CGAffineTransformMakeIdentity()
|
||||
message:"translate to identity"];
|
||||
|
||||
[self compareTransform:CGAffineTransformTranslate(transform,0,0)
|
||||
with:transform
|
||||
message:"zero translate"];
|
||||
}
|
||||
|
||||
- (void)testAffineTransformScale
|
||||
{
|
||||
var transform = CGAffineTransformMakeScale(3,4);
|
||||
|
||||
[self compareTransform:CGAffineTransformScale( transform, 1/3, 1/4)
|
||||
with:CGAffineTransformMakeIdentity()
|
||||
message:"scale to identity"];
|
||||
|
||||
transform = CGAffineTransformMake(2, -2, -3, 3, 3.2, 5.4);
|
||||
[self compareTransform:CGAffineTransformScale(transform, 1, 1)
|
||||
with:transform
|
||||
message:"scale by 1"];
|
||||
|
||||
[self compareTransform:CGAffineTransformScale(transform, 2, 5)
|
||||
with:CGAffineTransformMake(4,-4,-15,15, 3.2,5.4)
|
||||
message:"random scale to somewhere"];
|
||||
}
|
||||
|
||||
- (void)testAffineTransformConcat
|
||||
{
|
||||
var testcases = {
|
||||
"identity concat" : {
|
||||
testdata: CGAffineTransformConcat( CGAffineTransformMakeIdentity(),
|
||||
CGAffineTransformMakeIdentity() ),
|
||||
expdata: CGAffineTransformMakeIdentity()
|
||||
},
|
||||
|
||||
"translation" : {
|
||||
testdata: CGAffineTransformConcat( CGAffineTransformMakeTranslation(3,4),
|
||||
CGAffineTransformMakeTranslation(-3,-4) ),
|
||||
expdata: CGAffineTransformMakeIdentity()
|
||||
},
|
||||
|
||||
"translation (reversed)" : {
|
||||
testdata: CGAffineTransformConcat( CGAffineTransformMakeTranslation(-3,-4),
|
||||
CGAffineTransformMakeTranslation(3,4) ),
|
||||
expdata: CGAffineTransformMakeIdentity()
|
||||
},
|
||||
|
||||
"scale" : {
|
||||
testdata: CGAffineTransformConcat(CGAffineTransformMakeScale(3,4),
|
||||
CGAffineTransformMakeScale(1/3,1/4)),
|
||||
expdata: CGAffineTransformMakeIdentity()
|
||||
},
|
||||
|
||||
"scale (reversed)" : {
|
||||
testdata: CGAffineTransformConcat(CGAffineTransformMakeScale(1/3,1/4),
|
||||
CGAffineTransformMakeScale(3,4)),
|
||||
expdata: CGAffineTransformMakeIdentity()
|
||||
},
|
||||
|
||||
"rotation" : {
|
||||
testdata: CGAffineTransformConcat(CGAffineTransformMakeRotation(Math.PI),
|
||||
CGAffineTransformMakeRotation(-Math.PI)),
|
||||
expdata: CGAffineTransformMakeIdentity()
|
||||
},
|
||||
};
|
||||
|
||||
for ( var key in testcases )
|
||||
[self compareTransform:testcases[key].testdata with:testcases[key].expdata message:key];
|
||||
}
|
||||
|
||||
- (void)testPointApplyAffineTransform
|
||||
{
|
||||
var testcases = {
|
||||
"translate to zero" : {
|
||||
testdata: CGPointApplyAffineTransform( CGPointMake( 3, 4 ),
|
||||
CGAffineTransformMakeTranslation(-3,-4)),
|
||||
expdata: CGPointMakeZero()
|
||||
},
|
||||
|
||||
"scale to 1,1" : {
|
||||
testdata: CGPointApplyAffineTransform( CGPointMake( 3, 4 ),
|
||||
CGAffineTransformMakeScale(1/3,1/4) ),
|
||||
expdata: CGPointMake(1,1)
|
||||
},
|
||||
|
||||
"scale and translate to zero" : {
|
||||
testdata: CGPointApplyAffineTransform( CGPointMake( 3, 4 ),
|
||||
CGAffineTransformConcat(
|
||||
CGAffineTransformMakeScale(1/3,1/4),
|
||||
CGAffineTransformMakeTranslation(-1,-1))),
|
||||
expdata: CGPointMakeZero()
|
||||
|
||||
},
|
||||
};
|
||||
|
||||
for ( var key in testcases )
|
||||
[self comparePoint:testcases[key].testdata with:testcases[key].expdata message:key];
|
||||
}
|
||||
|
||||
- (void)testSizeApplyAffineTransform
|
||||
{
|
||||
var testcases = {
|
||||
"translation on size should do nothing" : {
|
||||
testdata: CGSizeApplyAffineTransform( CGSizeMake(3, 12),
|
||||
CGAffineTransformMakeTranslation(-3,-4)),
|
||||
expdata: CGSizeMake(3, 12)
|
||||
},
|
||||
|
||||
"scale to 1,1" : {
|
||||
testdata: CGSizeApplyAffineTransform( CGSizeMake( 3, 4 ),
|
||||
CGAffineTransformMakeScale(1/3,1/4) ),
|
||||
expdata: CGSizeMake(1, 1)
|
||||
},
|
||||
|
||||
"scale and translate combined" : {
|
||||
testdata: CGSizeApplyAffineTransform( CGSizeMake( 3, 4 ),
|
||||
CGAffineTransformConcat(
|
||||
CGAffineTransformMakeScale(1/3,1/4),
|
||||
CGAffineTransformMakeTranslation(-1,-1))),
|
||||
expdata: CGSizeMake(1, 1)
|
||||
|
||||
},
|
||||
};
|
||||
|
||||
for ( var key in testcases )
|
||||
[self compareSize:testcases[key].testdata with:testcases[key].expdata message:key];
|
||||
}
|
||||
|
||||
- (void)testAffineTransformIsIdentityPositive
|
||||
{
|
||||
var testcases = {
|
||||
"identity is identity" : {
|
||||
testdata: CGAffineTransformMakeIdentity()
|
||||
},
|
||||
|
||||
"zero rotation is identity" : {
|
||||
testdata: CGAffineTransformMakeRotation(0),
|
||||
},
|
||||
|
||||
"zero translation is identity" : {
|
||||
testdata: CGAffineTransformMakeTranslation(0,0)
|
||||
},
|
||||
|
||||
"one scale is identity" : {
|
||||
testdata: CGAffineTransformMakeScale(1,1)
|
||||
},
|
||||
|
||||
"identity concat'ed" : {
|
||||
testdata: CGAffineTransformConcat( CGAffineTransformMakeIdentity(),
|
||||
CGAffineTransformMakeIdentity() ),
|
||||
},
|
||||
|
||||
"translation" : {
|
||||
testdata: CGAffineTransformConcat( CGAffineTransformMakeTranslation(3,4),
|
||||
CGAffineTransformMakeTranslation(-3,-4) ),
|
||||
},
|
||||
|
||||
"scale" : {
|
||||
testdata: CGAffineTransformConcat(CGAffineTransformMakeScale(3,4),
|
||||
CGAffineTransformMakeScale(1/3,1/4)),
|
||||
},
|
||||
|
||||
"rotation" : {
|
||||
testdata: CGAffineTransformConcat(CGAffineTransformMakeRotation(-Math.PI),
|
||||
CGAffineTransformMakeRotation(Math.PI)),
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
for ( var key in testcases )
|
||||
[self assert:YES
|
||||
equals:CGAffineTransformIsIdentity(testcases[key].testdata)
|
||||
message:key];
|
||||
}
|
||||
|
||||
- (void)testAffineTransformIsIdentityNegative
|
||||
{
|
||||
var testcases = {
|
||||
"some random transform" : {
|
||||
testdata: CGAffineTransformMake(1,1,1,1,1,1)
|
||||
},
|
||||
|
||||
"non-zero translation is not identity" : {
|
||||
testdata: CGAffineTransformMakeTranslation(1,1)
|
||||
},
|
||||
|
||||
"non-one scale is not identity" : {
|
||||
testdata: CGAffineTransformMakeScale(2,2)
|
||||
},
|
||||
|
||||
"rotation" : {
|
||||
testdata: CGAffineTransformMakeRotation(Math.PI),
|
||||
},
|
||||
|
||||
// TODO a two-pi rotation is actually identity
|
||||
"2PI rotation is NOT identity?" : {
|
||||
testdata: CGAffineTransformMakeRotation(Math.PI * 2),
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
for ( var key in testcases )
|
||||
[self assert:NO
|
||||
equals:CGAffineTransformIsIdentity(testcases[key].testdata)
|
||||
message:key];
|
||||
}
|
||||
|
||||
- (void)testAffineTransformEqualToTransform
|
||||
{
|
||||
var testcases = {
|
||||
"identity" : {
|
||||
lhs: CGAffineTransformMakeIdentity(),
|
||||
rhs: CGAffineTransformMakeIdentity(),
|
||||
expdata: YES
|
||||
},
|
||||
|
||||
"translate" : {
|
||||
lhs: CGAffineTransformMakeTranslation(1,1),
|
||||
rhs: CGAffineTransformMakeTranslation(1,1),
|
||||
expdata: YES
|
||||
},
|
||||
|
||||
"scale" : {
|
||||
lhs: CGAffineTransformMakeScale(1,1),
|
||||
rhs: CGAffineTransformMakeScale(1,1),
|
||||
expdata: YES
|
||||
},
|
||||
|
||||
"rotation" : {
|
||||
lhs: CGAffineTransformMakeRotation(Math.PI),
|
||||
rhs: CGAffineTransformMakeRotation(Math.PI),
|
||||
expdata: YES
|
||||
},
|
||||
|
||||
"translate and scale" : {
|
||||
lhs: CGAffineTransformMakeScale(1,1),
|
||||
rhs: CGAffineTransformMakeTranslation(1,1),
|
||||
expdata: NO
|
||||
},
|
||||
};
|
||||
|
||||
for ( var key in testcases )
|
||||
[self assert:testcases[key].expdata
|
||||
equals:CGAffineTransformEqualToTransform(testcases[key].lhs, testcases[key].rhs)
|
||||
message:key];
|
||||
}
|
||||
|
||||
- (void)testStringCreateWithCGAffineTransform
|
||||
{
|
||||
// FIXME?: should there be a leading space on these strings
|
||||
var testcases = {
|
||||
"identity" : {
|
||||
testdata: CGAffineTransformMakeIdentity(),
|
||||
expdata: " [[ 1, 0, 0 ], [ 0, 1, 0 ], [ 0, 0, 1]]"
|
||||
},
|
||||
|
||||
"scale" : {
|
||||
testdata: CGAffineTransformMakeScale(3,4),
|
||||
expdata: " [[ 3, 0, 0 ], [ 0, 4, 0 ], [ 0, 0, 1]]"
|
||||
},
|
||||
|
||||
"translation" : {
|
||||
testdata: CGAffineTransformMakeTranslation(3,4),
|
||||
expdata: " [[ 1, 0, 0 ], [ 0, 1, 0 ], [ 3, 4, 1]]"
|
||||
},
|
||||
|
||||
"scale and translation" : {
|
||||
testdata: CGAffineTransformTranslate(CGAffineTransformMakeScale(3,4),5,6),
|
||||
expdata: " [[ 3, 0, 0 ], [ 0, 4, 0 ], [ 15, 24, 1]]"
|
||||
},
|
||||
};
|
||||
|
||||
for ( var key in testcases )
|
||||
[self assert:testcases[key].expdata
|
||||
equals:CGStringCreateWithCGAffineTransform(testcases[key].testdata)
|
||||
message:key];
|
||||
}
|
||||
|
||||
- (void)testStringFromCGAffineTransform
|
||||
{
|
||||
var testcases = {
|
||||
"identity" : {
|
||||
testdata: CGAffineTransformMakeIdentity(),
|
||||
expdata: "{1, 0, 0, 1, 0, 0}"
|
||||
},
|
||||
|
||||
"scale" : {
|
||||
testdata: CGAffineTransformMakeScale(3,4),
|
||||
expdata: "{3, 0, 0, 4, 0, 0}"
|
||||
},
|
||||
|
||||
"translation" : {
|
||||
testdata: CGAffineTransformMakeTranslation(3,4),
|
||||
expdata: "{1, 0, 0, 1, 3, 4}"
|
||||
},
|
||||
|
||||
"rotation - zero" : {
|
||||
testdata: CGAffineTransformMakeRotation(0),
|
||||
expdata: "{1, 0, 0, 1, 0, 0}"
|
||||
},
|
||||
|
||||
"rotation - pi" : {
|
||||
testdata: CGAffineTransformMakeRotation(Math.PI),
|
||||
expdata: "{-1, 1.2246467991473532e-16, -1.2246467991473532e-16, -1, 0, 0}"
|
||||
},
|
||||
|
||||
"rotation - 2pi" : {
|
||||
testdata: CGAffineTransformMakeRotation(2 * Math.PI),
|
||||
expdata: "{1, -2.4492935982947064e-16, 2.4492935982947064e-16, 1, 0, 0}"
|
||||
},
|
||||
|
||||
"rotation - 3pi" : {
|
||||
testdata: CGAffineTransformMakeRotation(3 * Math.PI),
|
||||
expdata: "{-1, 3.6739403974420594e-16, -3.6739403974420594e-16, -1, 0, 0}"
|
||||
},
|
||||
|
||||
"scale and translation and rotate" : {
|
||||
testdata: CGAffineTransformRotate(CGAffineTransformTranslate(CGAffineTransformMakeScale(3,4),5,6),Math.PI),
|
||||
expdata: "{-3, 4.898587196589413e-16, -3.6739403974420594e-16, -4, 15, 24}"
|
||||
},
|
||||
};
|
||||
|
||||
for ( var key in testcases )
|
||||
[self assert:testcases[key].expdata
|
||||
equals:CPStringFromCGAffineTransform(testcases[key].testdata)
|
||||
message:key];
|
||||
}
|
||||
|
||||
- (void)testAffineTransformRotate
|
||||
{
|
||||
var ang = Math.PI + 0.5 * Math.PI,
|
||||
transform = CGAffineTransformMake( 1,2,3,4, 5,6 ),
|
||||
cos = COS(ang),
|
||||
sin = SIN(ang);
|
||||
|
||||
var testcases = {
|
||||
"affine transform rotate failed" : {
|
||||
testdata: CGAffineTransformRotate(transform, ang),
|
||||
expdata: CGAffineTransformMake( transform.a * cos + transform.c * sin,
|
||||
transform.b * cos + transform.d * sin,
|
||||
transform.c * cos - transform.a * sin,
|
||||
transform.d * cos - transform.b * sin,
|
||||
transform.tx,transform.ty )
|
||||
},
|
||||
|
||||
"rotation negation" : {
|
||||
testdata: CGAffineTransformRotate(CGAffineTransformRotate(CGAffineTransformMakeScale(3,4),Math.PI),-Math.PI),
|
||||
expdata: CGAffineTransformMakeScale(3,4)
|
||||
}
|
||||
};
|
||||
|
||||
for ( var key in testcases )
|
||||
[self compareTransform:testcases[key].testdata with:testcases[key].expdata message:key];
|
||||
}
|
||||
|
||||
- (void)testAffineTransformInvert
|
||||
{
|
||||
var transform = CGAffineTransformRotate(CGAffineTransformTranslate(CGAffineTransformMakeScale(3,4),5,6),Math.PI),
|
||||
determinant = 1 / (transform.a * transform.d - transform.b * transform.c),
|
||||
invertedtransform = CGAffineTransformMake(determinant * transform.d,
|
||||
-determinant * transform.b,
|
||||
-determinant * transform.c,
|
||||
determinant * transform.a,
|
||||
determinant * (transform.c * transform.ty -
|
||||
transform.d * transform.tx),
|
||||
determinant * (transform.b * transform.tx -
|
||||
transform.a * transform.ty));
|
||||
|
||||
var testcases = {
|
||||
"test invert algorithm" : {
|
||||
testdata: transform,
|
||||
expdata: invertedtransform
|
||||
},
|
||||
|
||||
"identity should be it self on inversion" : {
|
||||
testdata: CGAffineTransformMakeIdentity(),
|
||||
expdata: CGAffineTransformMakeIdentity()
|
||||
},
|
||||
|
||||
"rotation" : {
|
||||
testdata: CGAffineTransformMakeRotation(-Math.PI),
|
||||
expdata: CGAffineTransformMakeRotation(Math.PI),
|
||||
},
|
||||
|
||||
"translation" : {
|
||||
testdata: CGAffineTransformMakeTranslation(4,5),
|
||||
expdata: CGAffineTransformMakeTranslation(-4,-5)
|
||||
},
|
||||
|
||||
"scale" : {
|
||||
testdata: CGAffineTransformMakeScale(3,4),
|
||||
expdata: CGAffineTransformMakeScale(1/3,1/4)
|
||||
},
|
||||
};
|
||||
|
||||
for ( var key in testcases )
|
||||
[self compareTransform:CGAffineTransformInvert(testcases[key].testdata)
|
||||
with:testcases[key].expdata
|
||||
message:key];
|
||||
}
|
||||
|
||||
- (void)testRectApplyAffineTransform
|
||||
{
|
||||
var rect = CGRectMake( 3,4,5,6 );
|
||||
|
||||
var testcases = {
|
||||
"identity does nothing" : {
|
||||
testdata: CGRectApplyAffineTransform(rect, CGAffineTransformMakeIdentity() ),
|
||||
expdata: rect
|
||||
},
|
||||
|
||||
"rotation 90 degrees" : {
|
||||
testdata: CGRectApplyAffineTransform(rect, CGAffineTransformMakeRotation(Math.PI/2)),
|
||||
expdata: CGRectMake( -10,3.0000000000000004,6,5 )
|
||||
},
|
||||
|
||||
"translation" : {
|
||||
testdata: CGRectApplyAffineTransform(rect, CGAffineTransformMakeTranslation(3,4)),
|
||||
expdata: CGRectMake( 6,8,5,6 )
|
||||
},
|
||||
|
||||
"scale" : {
|
||||
testdata: CGRectApplyAffineTransform(rect, CGAffineTransformMakeScale(1,4)),
|
||||
expdata: CGRectMake( 3,16,5,24 )
|
||||
},
|
||||
|
||||
"rotate, translate and scale" : {
|
||||
testdata: CGRectApplyAffineTransform(rect, CGAffineTransformRotate(CGAffineTransformTranslate(CGAffineTransformMakeScale(3,4),5,6),Math.PI)),
|
||||
expdata: CGRectMake( -9.000000000000004, -16,
|
||||
15.000000000000002, 24.000000000000004 )
|
||||
},
|
||||
};
|
||||
|
||||
for ( var key in testcases )
|
||||
[self compareRect:testcases[key].testdata with:testcases[key].expdata message:key];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,168 @@
|
||||
@import <AppKit/CGColorSpace.j>
|
||||
|
||||
@implementation CGColorSpaceTest : OJTestCase
|
||||
|
||||
- (void)testColorSpaceCreateWithName
|
||||
{
|
||||
var testdata = {
|
||||
"CGColorSpaceGenericGray" : {
|
||||
model: kCGColorSpaceModelMonochrome,
|
||||
count: 1,
|
||||
base: NULL
|
||||
},
|
||||
"CGColorSpaceGenericRGB" : {
|
||||
model: kCGColorSpaceModelRGB,
|
||||
count: 3,
|
||||
base: NULL
|
||||
},
|
||||
"CGColorSpaceGenericCMYK" : {
|
||||
model: kCGColorSpaceModelCMYK,
|
||||
count: 4,
|
||||
base: NULL
|
||||
},
|
||||
"CGColorSpaceGenericRGBLinear" : {
|
||||
model: kCGColorSpaceModelRGB,
|
||||
count: 3,
|
||||
base: NULL
|
||||
},
|
||||
"CGColorSpaceGenericRGBHDR" : {
|
||||
model: kCGColorSpaceModelRGB,
|
||||
count: 3,
|
||||
base: NULL
|
||||
},
|
||||
"CGColorSpaceAdobeRGB1998" : {
|
||||
model: kCGColorSpaceModelRGB,
|
||||
count: 3,
|
||||
base: NULL
|
||||
},
|
||||
"CGColorSpaceSRGB" : {
|
||||
model: kCGColorSpaceModelRGB,
|
||||
count: 3,
|
||||
base: NULL
|
||||
},
|
||||
}
|
||||
|
||||
for ( var key in testdata ) {
|
||||
var clrsp = CGColorSpaceCreateWithName(key),
|
||||
expdata = testdata[key];
|
||||
[self assert:expdata.model equals:clrsp.model message:"model for "+key];
|
||||
[self assert:expdata.count equals:clrsp.count message:"count for "+key];
|
||||
[self assert:expdata.base equals:clrsp.base message:"base for "+key];
|
||||
}
|
||||
|
||||
[self assert:NULL
|
||||
equals:CGColorSpaceCreateWithName("doesnotexist")
|
||||
message:"should be missing"];
|
||||
}
|
||||
|
||||
- (void)testColorSpaceStandardizeComponents
|
||||
{
|
||||
var testcases = {
|
||||
"notsupported1" : {
|
||||
testdata : {
|
||||
model: kCGColorSpaceModelIndexed,
|
||||
count: 5
|
||||
},
|
||||
expected: [5,4,3,2,1,NaN] // alpha value normalisation adds value at index 5
|
||||
},
|
||||
"notsupported2" : {
|
||||
testdata : {
|
||||
model: kCGColorSpaceModelLab,
|
||||
count: 5
|
||||
},
|
||||
expected: [5,4,3,2,1,NaN]
|
||||
},
|
||||
"notsupported3" : {
|
||||
testdata : {
|
||||
model: kCGColorSpaceModelPattern,
|
||||
count: 4
|
||||
},
|
||||
expected: [5,4,3,2,1]
|
||||
},
|
||||
"base override with count value" : {
|
||||
testdata : {
|
||||
model: kCGColorSpaceModelPattern,
|
||||
base: { model: kCGColorSpaceModelMonochrome },
|
||||
count: 4
|
||||
},
|
||||
expected: [1,1,1,1,1],
|
||||
},
|
||||
"base override no count value" : {
|
||||
testdata : {
|
||||
model: kCGColorSpaceModelPattern,
|
||||
base: { model: kCGColorSpaceModelMonochrome, count: 4 },
|
||||
count: 0
|
||||
},
|
||||
expected: [1,4,3,2,1], // alpha value is assumed to be at index 0
|
||||
},
|
||||
"monochrome color space model" : {
|
||||
testdata : {
|
||||
model: kCGColorSpaceModelMonochrome,
|
||||
count: 2
|
||||
},
|
||||
expected: [1,1,1,2,1],
|
||||
},
|
||||
"rgb color space model" : {
|
||||
testdata : {
|
||||
model: kCGColorSpaceModelRGB,
|
||||
count: 2
|
||||
},
|
||||
expected: [1,1,1,2,1],
|
||||
},
|
||||
"cmyk color space model" : {
|
||||
testdata : {
|
||||
model: kCGColorSpaceModelCMYK,
|
||||
count: 2
|
||||
},
|
||||
expected: [1,1,1,2,1],
|
||||
},
|
||||
"devicen color space model" : {
|
||||
testdata : {
|
||||
model: kCGColorSpaceModelDeviceN,
|
||||
count: 2
|
||||
},
|
||||
expected: [1,1,1,2,1],
|
||||
},
|
||||
}
|
||||
|
||||
for ( var key in testcases ) {
|
||||
var components = [5,4,3,2,1];
|
||||
CGColorSpaceStandardizeComponents( testcases[key].testdata, components);
|
||||
[self assert:testcases[key].expected equals:components message:"Failed for "+key];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testRgbStandardize
|
||||
{
|
||||
var clrspc = CGColorSpaceCreateDeviceRGB(),
|
||||
testcases = {
|
||||
"negative values" : {
|
||||
components: [-5,-4,-3,-2,-1,-1,-1,-1],
|
||||
expected: [0,0,0,0,-1,-1,-1,-1],
|
||||
},
|
||||
// TODO: this happens because first the alpha value is normalised,
|
||||
// TODO: so the components array does not have length 0, rather 4.
|
||||
"missing values become undefined" : {
|
||||
components: [],
|
||||
expected: [undefined,undefined,undefined,1],
|
||||
},
|
||||
"values standarized if between 0 and 1" : {
|
||||
components: [0.3, 0.4, 0.5, 0.6],
|
||||
expected: [ROUND(0.3*255)/255, ROUND(0.4*255)/255,
|
||||
ROUND(0.5*255)/255, ROUND(0.6*255)/255],
|
||||
},
|
||||
"values 0 or 1 if zero or one" : {
|
||||
components: [0, 1, 0, 1],
|
||||
expected: [0,1,0,1],
|
||||
}
|
||||
};
|
||||
|
||||
for ( var key in testcases ) {
|
||||
CGColorSpaceStandardizeComponents( clrspc, testcases[key].components);
|
||||
[self assert:testcases[key].expected
|
||||
equals:testcases[key].components
|
||||
message:"Failed for "+key];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,217 @@
|
||||
@import <AppKit/CGColor.j>
|
||||
|
||||
@implementation CGColorTest : OJTestCase
|
||||
|
||||
- (void)testColorCreateReturnNullIfNull
|
||||
{
|
||||
[self assert:NULL equals:CGColorCreate( NULL, [1,2,3] ) message:"colorspace null failed"];
|
||||
[self assert:NULL
|
||||
equals:CGColorCreate( CGColorSpaceCreateDeviceRGB(), NULL )
|
||||
message:"components null failed"];
|
||||
}
|
||||
|
||||
- (void)testColorCreateEnsureComponentsAreCopied
|
||||
{
|
||||
var colorspace = CGColorSpaceCreateDeviceRGB(),
|
||||
components = [2,3,4,5],
|
||||
clr = CGColorCreate(colorspace, components);
|
||||
|
||||
components[0] = components[1] = components[2] = components[3] = 0;
|
||||
[self assert:[1,1,1,1] equals:clr.components];
|
||||
[self assert:[0,0,0,0] equals:components];
|
||||
}
|
||||
|
||||
- (void)testColorCreateCachesColorsAlphaChangeIsPropagated
|
||||
{
|
||||
var clr = CGColorCreate(CGColorSpaceCreateDeviceRGB(), [0.4, 0.3, 0.2, 0.2]),
|
||||
newclr = CGColorCreate(CGColorSpaceCreateDeviceRGB(), [0.4, 0.3, 0.2, 0.2]);
|
||||
newclr.components[newclr.components.length - 1] = 0.6;
|
||||
[self assert:0.6 equals:clr.components[clr.components.length-1]];
|
||||
}
|
||||
|
||||
- (void)testColorCreate
|
||||
{
|
||||
var colorspace = CGColorSpaceCreateDeviceRGB(),
|
||||
components = [2,3,4,5],
|
||||
clruid = CFHashCode(colorspace) + components.join("");
|
||||
|
||||
// Can't access the colormap cache, but it would be nice to
|
||||
// [self assert:NULL equals:_CGColorMap[clruid]];
|
||||
|
||||
var clr = CGColorCreate( colorspace, components );
|
||||
[self assert:colorspace equals:clr.colorspace message:"colorspace failed"];
|
||||
[self assert:[1,1,1,1] equals:clr.components message:"components failed"];
|
||||
[self assert:NULL equals:clr.pattern message:"pattern failed"];
|
||||
|
||||
// Can't access the colormap cache, but it would be nice to
|
||||
// [self assert:clr equals:_CGColorMap[clruid]];
|
||||
}
|
||||
|
||||
- (void)testColorCopy
|
||||
{
|
||||
// it returns it's argument, a little artifical but will fail if something
|
||||
// changes with the function.
|
||||
[self assert:"banana" equals:CGColorCreateCopy( "banana" )];
|
||||
|
||||
var colorspace = CGColorSpaceCreateDeviceRGB(),
|
||||
components = [2,3,4,5],
|
||||
clr = CGColorCreate( colorspace, components );
|
||||
[self assert:clr equals:CGColorCreateCopy(clr)];
|
||||
}
|
||||
|
||||
- (void)testColorCreateGenericGray
|
||||
{
|
||||
var clr = CGColorCreateGenericGray(0.3, 0.5);
|
||||
[self assert:CGColorSpaceCreateDeviceRGB()
|
||||
equals:clr.colorspace message:"colorspace failed"];
|
||||
[self assert:[ROUND(0.3*255)/255,ROUND(0.3*255)/255,ROUND(0.3*255)/255,0.5]
|
||||
equals:clr.components message:"components failed"];
|
||||
[self assert:NULL equals:clr.pattern message:"pattern failed"];
|
||||
}
|
||||
|
||||
- (void)testColorCreateGenericRGB
|
||||
{
|
||||
var clr = CGColorCreateGenericRGB(0.4,0.3,0.2,0.1);
|
||||
[self assert:CGColorSpaceCreateDeviceRGB()
|
||||
equals:clr.colorspace message:"colorspace failed"];
|
||||
[self assert:[ROUND(0.4*255)/255,ROUND(0.3*255)/255,ROUND(0.2*255)/255,0.1]
|
||||
equals:clr.components message:"components failed"];
|
||||
[self assert:NULL equals:clr.pattern message:"pattern failed"];
|
||||
}
|
||||
|
||||
- (void)testColorCreateGenericCMYK
|
||||
{
|
||||
var clr = CGColorCreateGenericCMYK(0.2, 0.3, 0.4, 0.5, 0.6)
|
||||
[self assert:CGColorSpaceCreateDeviceCMYK()
|
||||
equals:clr.colorspace message:"colorspace failed"];
|
||||
[self assert:[ROUND(0.2*255)/255,ROUND(0.3*255)/255,ROUND(0.4*255)/255,
|
||||
ROUND(0.5*255)/255, 0.6]
|
||||
equals:clr.components message:"components failed"];
|
||||
[self assert:NULL equals:clr.pattern message:"pattern failed"];
|
||||
}
|
||||
|
||||
- (void)testColorCreateWithPattern
|
||||
{
|
||||
var clrspc = CGColorSpaceCreateDeviceRGB(),
|
||||
pattern = "a new pattern",
|
||||
components = [0.2, 0.4, 0.6, 0.8],
|
||||
clr = CGColorCreateWithPattern(clrspc, pattern, components);
|
||||
|
||||
[self assert:clrspc equals:clr.colorspace];
|
||||
[self assert:pattern equals:clr.pattern];
|
||||
[self assert:components equals:clr.components];
|
||||
|
||||
// ensure that there was a copy made of the components
|
||||
components[0] = components[1] = components[2] = components[3] = 0.0;
|
||||
[self assert:[0.2, 0.4, 0.6, 0.8] equals:clr.components];
|
||||
|
||||
// ensure that null is returned
|
||||
[self assert:NULL equals:CGColorCreateWithPattern(NULL, "a new pattern", [])];
|
||||
[self assert:NULL equals:CGColorCreateWithPattern(CGColorSpaceCreateDeviceRGB(),
|
||||
NULL, [])];
|
||||
[self assert:NULL equals:CGColorCreateWithPattern(CGColorSpaceCreateDeviceRGB(),
|
||||
"a new pattern", NULL)];
|
||||
[self assert:NULL equals:CGColorCreateWithPattern(NULL, NULL, NULL)];
|
||||
}
|
||||
|
||||
- (void)testColorGetAlpha
|
||||
{
|
||||
var clr = CGColorCreateGenericCMYK(0.2, 0.3, 0.4, 0.5, 0.6);
|
||||
[self assert:0.6 equals:CGColorGetAlpha(clr)];
|
||||
}
|
||||
|
||||
- (void)testColorGetColorSpace
|
||||
{
|
||||
var clr = CGColorCreateGenericCMYK(0.2, 0.3, 0.4, 0.5, 0.6);
|
||||
[self assert:CGColorSpaceCreateDeviceCMYK()
|
||||
equals:CGColorGetColorSpace(clr)];
|
||||
}
|
||||
|
||||
- (void)testColorGetComponents
|
||||
{
|
||||
var clr = CGColorCreateGenericCMYK(0.2, 0.3, 0.4, 0.5, 0.6);
|
||||
[self assert:[ROUND(0.2*255)/255, ROUND(0.3*255)/255, ROUND(0.4*255)/255,
|
||||
ROUND(0.5*255)/255, ROUND(0.6*255)/255]
|
||||
equals:CGColorGetComponents(clr)];
|
||||
}
|
||||
|
||||
- (void)testColorGetNumberOfComponents
|
||||
{
|
||||
var clr = CGColorCreateGenericCMYK(0.2, 0.3, 0.4, 0.5, 0.6);
|
||||
[self assert:5 equals:CGColorGetNumberOfComponents(clr)];
|
||||
}
|
||||
|
||||
- (void)testColorGetPattern
|
||||
{
|
||||
var clr = CGColorCreateWithPattern(CGColorSpaceCreateDeviceRGB(), "a new pattern",
|
||||
[1,1,1,1]);
|
||||
[self assert:"a new pattern" equals:CGColorGetPattern(clr)];
|
||||
}
|
||||
|
||||
- (void)testColorCreateCopyWithAlphaWithPattern
|
||||
{
|
||||
var clr = CGColorCreateWithPattern(CGColorSpaceCreateDeviceRGB(), "a new pattern",
|
||||
[1,1,1,1]);
|
||||
[self assert:1 equals:CGColorGetAlpha(clr) message:"initial alpha"];
|
||||
|
||||
var newclr = CGColorCreateCopyWithAlpha(clr, 0.6);
|
||||
[self assert:0.6 equals:CGColorGetAlpha(newclr) message:"new color alpha value"];
|
||||
[self assert:1 equals:CGColorGetAlpha(clr) message:"alpha after copy"];
|
||||
|
||||
[self assert:clr.colorspace equals:newclr.colorspace message:"newclr colorspace failed"];
|
||||
[self assert:clr.pattern equals:newclr.pattern message:"newclr pattern failed"];
|
||||
}
|
||||
|
||||
- (void)testColorCreateCopyWithAlphaWithComponents
|
||||
{
|
||||
var clr = CGColorCreateGenericRGB(0.4, 0.3, 0.2, 0.3);
|
||||
[self assert:0.3 equals:CGColorGetAlpha(clr) message:"initial alpha"];
|
||||
|
||||
var newclr = CGColorCreateCopyWithAlpha(clr, 0.6);
|
||||
[self assert:0.6 equals:CGColorGetAlpha(newclr) message:"new color alpha value"];
|
||||
[self assert:0.3 equals:CGColorGetAlpha(clr) message:"alpha after copy"];
|
||||
|
||||
[self assert:CGColorSpaceCreateDeviceRGB()
|
||||
equals:newclr.colorspace message:"newclr colorspace failed"];
|
||||
[self assert:[ROUND(0.4*255)/255,ROUND(0.3*255)/255,ROUND(0.2*255)/255,0.6]
|
||||
equals:newclr.components message:"newclr components failed"];
|
||||
[self assert:NULL equals:newclr.pattern message:"newclr pattern failed"];
|
||||
|
||||
[self assert:CGColorSpaceCreateDeviceRGB()
|
||||
equals:clr.colorspace message:"orig clr colorspace failed"];
|
||||
[self assert:[ROUND(0.4*255)/255,ROUND(0.3*255)/255,ROUND(0.2*255)/255,0.3]
|
||||
equals:clr.components message:"orig clr components failed"];
|
||||
[self assert:NULL equals:clr.pattern message:"orig clr pattern failed"];
|
||||
}
|
||||
|
||||
- (void)testColorCreateCopyWithAlphaWithComponentsNullColor
|
||||
{
|
||||
[self assert:NULL
|
||||
equals:CGColorCreateCopyWithAlpha(NULL,0.5)
|
||||
message:"null color failed"];
|
||||
|
||||
var clr = CGColorCreateGenericRGB(0.4,0.3,0.2,0.1),
|
||||
newclr = CGColorCreateCopyWithAlpha(clr, 0.1);
|
||||
[self assert:clr equals:newclr];
|
||||
}
|
||||
|
||||
- (void)testColorEqualToColor
|
||||
{
|
||||
[self assert:true equals:CGColorEqualToColor(NULL,NULL)];
|
||||
|
||||
var clr1 = CGColorCreateGenericRGB(0.4, 0.3, 0.2, 0.3);
|
||||
[self assert:false equals:CGColorEqualToColor(NULL,clr1)];
|
||||
[self assert:true equals:CGColorEqualToColor(clr1,clr1)];
|
||||
[self assert:false equals:CGColorEqualToColor(clr1,NULL)];
|
||||
|
||||
var clr2 = CGColorCreateGenericRGB(0.4, 0.3, 0.2, 0.3);
|
||||
[self assert:true equals:CGColorEqualToColor(clr1,clr2)];
|
||||
[self assert:true equals:CGColorEqualToColor(clr2,clr1)];
|
||||
|
||||
clr2 = CGColorCreateGenericCMYK(0.2, 0.3, 0.4, 0.5, 0.6);
|
||||
[self assert:false equals:CGColorEqualToColor(clr1,clr2)];
|
||||
[self assert:false equals:CGColorEqualToColor(clr2,clr1)];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
@import <AppKit/CGContext.j>
|
||||
@import "CGTestCase.j"
|
||||
|
||||
@implementation CGContextTest : CGTestCase
|
||||
|
||||
- (void)testEnsureTestingCanvas
|
||||
{
|
||||
/*
|
||||
Ensure that we have no canvas nor vml support.
|
||||
*/
|
||||
[self assert:YES equals:!CPFeatureIsCompatible(CPHTMLCanvasFeature)];
|
||||
[self assert:YES equals:!CPFeatureIsCompatible(CPVMLFeature)];
|
||||
}
|
||||
|
||||
- (void)testGStateCreate
|
||||
{
|
||||
var gstate = CGGStateCreate(),
|
||||
testdata = { alpha: 1.0,
|
||||
strokeStyle: "#000",
|
||||
fillStyle: "#ccc",
|
||||
lineWidth: 1.0,
|
||||
lineJoin: kCGLineJoinMiter,
|
||||
lineCap: kCGLineCapButt,
|
||||
miterLimit: 10.0,
|
||||
globalAlpha: 1.0,
|
||||
blendMode: kCGBlendModeNormal,
|
||||
shadowBlur: 0.0,
|
||||
shadowColor: NULL };
|
||||
|
||||
for ( var key in testdata )
|
||||
[self assert:testdata[key] equals:gstate[key] message:"Failed for " + key];
|
||||
|
||||
[self compareSize:CGSizeMakeZero()
|
||||
with:gstate.shadowOffset
|
||||
message:"Failed for shadowOffset"];
|
||||
|
||||
[self compareTransform:CGAffineTransformMakeIdentity()
|
||||
with:gstate.CTM
|
||||
message:"Failed for CTM"];
|
||||
}
|
||||
|
||||
- (void)testGStateCreateCopy
|
||||
{
|
||||
var gstate = CGGStateCreate(),
|
||||
gstatecopy = CGGStateCreateCopy(gstate),
|
||||
testdata = { alpha: 1.0,
|
||||
strokeStyle: "#000",
|
||||
fillStyle: "#ccc",
|
||||
lineWidth: 1.0,
|
||||
lineJoin: kCGLineJoinMiter,
|
||||
lineCap: kCGLineCapButt,
|
||||
miterLimit: 10.0,
|
||||
globalAlpha: 1.0,
|
||||
blendMode: kCGBlendModeNormal,
|
||||
shadowBlur: 0.0,
|
||||
shadowColor: NULL };
|
||||
|
||||
for ( var key in testdata )
|
||||
[self assert:testdata[key] equals:gstatecopy[key] message:"Failed for " + key];
|
||||
|
||||
// ensure that the size is copied
|
||||
var sizecopy = CGSizeMakeCopy( gstate.shadowOffset );
|
||||
gstate.shadowOffset.width += 1000;
|
||||
gstate.shadowOffset.height += 2000;
|
||||
[self compareSize:sizecopy with:gstatecopy.shadowOffset message:"Failed for shadowOffset"];
|
||||
|
||||
// ensure that the transform is copied
|
||||
var transformcopy = CGAffineTransformMakeCopy( gstate.CTM );
|
||||
gstate.CTM.a += 1000;
|
||||
gstate.CTM.b += 1000;
|
||||
gstate.CTM.c += 1000;
|
||||
gstate.CTM.d += 1000;
|
||||
gstate.CTM.tx += 1000;
|
||||
gstate.CTM.ty += 1000;
|
||||
[self compareTransform:transformcopy with:gstatecopy.CTM message:"Failed with CTM"];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,100 @@
|
||||
@import <AppKit/CGGradient.j>
|
||||
|
||||
@implementation CGGradientTest : OJTestCase
|
||||
|
||||
/*
|
||||
Using default values for location and count.
|
||||
*/
|
||||
- (void)testCreateWithColorComponentsDefault
|
||||
{
|
||||
|
||||
// Test one color and no location
|
||||
var components = [ 0.2,0.2,0.4,0.4 ];
|
||||
|
||||
var tmp = CGGradientCreateWithColorComponents(CGColorSpaceCreateDeviceRGB(),
|
||||
components);
|
||||
[self assert:1 equals:tmp.colors.length];
|
||||
[self assert:1 equals:tmp.locations.length];
|
||||
|
||||
// TODO: This should not be supported since there is no gradient if only one color
|
||||
// TODO: NaN not really helpful...
|
||||
[self assert:NaN equals:tmp.locations[0]];
|
||||
|
||||
[self assert:components.slice(0,4) equals:tmp.colors[0].components];
|
||||
|
||||
// Test two colors and no location
|
||||
var components = [ 0.2,0.2,0.4,0.4,
|
||||
0.6,0.6,0.8,0.8 ];
|
||||
|
||||
var tmp = CGGradientCreateWithColorComponents(CGColorSpaceCreateDeviceRGB(),
|
||||
components);
|
||||
[self assert:2 equals:tmp.colors.length];
|
||||
[self assert:2 equals:tmp.locations.length];
|
||||
|
||||
[self assert:0 equals:tmp.locations[0]];
|
||||
[self assert:1.0 equals:tmp.locations[1]];
|
||||
|
||||
[self assert:components.slice(4,8) equals:tmp.colors[1].components];
|
||||
[self assert:components.slice(0,4) equals:tmp.colors[0].components];
|
||||
|
||||
// Test three colors and no location
|
||||
var components = [ 0.2,0.2,0.4,0.4,
|
||||
0.6,0.6,0.8,0.8,
|
||||
0.6,0.6,0.8,0.8 ];
|
||||
|
||||
var tmp = CGGradientCreateWithColorComponents(CGColorSpaceCreateDeviceRGB(),
|
||||
components);
|
||||
[self assert:3 equals:tmp.colors.length];
|
||||
[self assert:3 equals:tmp.locations.length];
|
||||
|
||||
[self assert:0 equals:tmp.locations[0]];
|
||||
[self assert:0.5 equals:tmp.locations[1]];
|
||||
[self assert:1.0 equals:tmp.locations[2]];
|
||||
|
||||
[self assert:components.slice(8,12) equals:tmp.colors[2].components];
|
||||
[self assert:components.slice(4,8) equals:tmp.colors[1].components];
|
||||
[self assert:components.slice(0,4) equals:tmp.colors[0].components];
|
||||
}
|
||||
|
||||
/*
|
||||
Using location and components to specify two color stops that aren't
|
||||
evenly spread out.
|
||||
*/
|
||||
- (void)testCreateWithColorComponentsUsingTwoColorStops
|
||||
{
|
||||
var components = [ 0.2,0.2,0.4,0.4,
|
||||
0.6,0.6,0.8,0.8 ];
|
||||
var locations = [0.3,0.6];
|
||||
|
||||
var tmp = CGGradientCreateWithColorComponents(CGColorSpaceCreateDeviceRGB(),
|
||||
components,locations);
|
||||
[self assert:2 equals:tmp.colors.length];
|
||||
[self assert:2 equals:tmp.locations.length];
|
||||
|
||||
[self assert:0.3 equals:tmp.locations[0]];
|
||||
[self assert:0.6 equals:tmp.locations[1]];
|
||||
|
||||
[self assert:components.slice(4,8) equals:tmp.colors[1].components];
|
||||
[self assert:components.slice(0,4) equals:tmp.colors[0].components];
|
||||
}
|
||||
|
||||
/*
|
||||
Define three color components and ensure that the Gradient defined contains 3 color stops.
|
||||
*/
|
||||
- (void)testCanHaveMoreThanTwoColorStops
|
||||
{
|
||||
var components = [ 1,2,3,4, 5,6,7,8, 9,10,11,12 ];
|
||||
var locations = [1,2,3];
|
||||
|
||||
var tmp = CGGradientCreateWithColorComponents(CGColorSpaceCreateDeviceRGB(),
|
||||
components, locations, 3);
|
||||
[self assert:3 equals:tmp.colors.length];
|
||||
[self assert:3 equals:tmp.locations.length];
|
||||
|
||||
[self assert:1 equals:tmp.locations[0]];
|
||||
[self assert:2 equals:tmp.locations[1]];
|
||||
[self assert:3 equals:tmp.locations[2]];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
Used for comparing geometry forms. Otherwise each unit test would repeat these
|
||||
comparisons.
|
||||
*/
|
||||
@implementation CGTestCase : OJTestCase
|
||||
|
||||
- (void)compareRect:(CGRect)aRect
|
||||
with:(id)anotherRect
|
||||
message:(CPString)aMsg
|
||||
{
|
||||
aMsg += " (via cmp. rect)";
|
||||
[self comparePoint:aRect.origin with:anotherRect.origin message:aMsg];
|
||||
[self compareSize:aRect.size with:anotherRect.size message:aMsg];
|
||||
}
|
||||
|
||||
- (void)compareSize:(CGSize)aSize
|
||||
with:(id)anotherSize
|
||||
message:(CPString)aMsg
|
||||
{
|
||||
[self assert:anotherSize.width equals:aSize.width message:aMsg + ": Failed for width"];
|
||||
[self assert:anotherSize.height equals:aSize.height message:aMsg + ": Failed for height"];
|
||||
}
|
||||
|
||||
- (void)comparePoint:(CGPoint)aPoint
|
||||
with:(id)anotherPoint
|
||||
message:(CPString)aMsg
|
||||
{
|
||||
[self assert:anotherPoint.x equals:aPoint.x message:aMsg + ": Failed for x"];
|
||||
[self assert:anotherPoint.y equals:aPoint.y message:aMsg + ": Failed for y"];
|
||||
}
|
||||
|
||||
- (void)compareTransform:(CGAffineTransform)aTransform
|
||||
with:(id)aDataSet
|
||||
message:(CPString)aMsg
|
||||
{
|
||||
[self assert:aDataSet.a equals:aTransform.a message:aMsg + ": Failed for a"];
|
||||
[self assert:aDataSet.b equals:aTransform.b message:aMsg + ": Failed for b"];
|
||||
[self assert:aDataSet.c equals:aTransform.c message:aMsg + ": Failed for c"];
|
||||
[self assert:aDataSet.d equals:aTransform.d message:aMsg + ": Failed for d"];
|
||||
[self assert:aDataSet.tx equals:aTransform.tx message:aMsg + ": Failed for tx"];
|
||||
[self assert:aDataSet.ty equals:aTransform.ty message:aMsg + ": Failed for ty"];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,106 +0,0 @@
|
||||
@import <Foundation/CPKeyValueObserving.j>
|
||||
|
||||
@implementation CPKeyValueObservingTest : OJTestCase
|
||||
{
|
||||
CPString _lastKeyPath;
|
||||
id _lastObject;
|
||||
CPDictionary _lastChange;
|
||||
id _lastContext;
|
||||
}
|
||||
|
||||
- (void)setup
|
||||
{
|
||||
_lastKeyPath = _lastObject = _lastChange = _lastContext = nil;
|
||||
}
|
||||
|
||||
- (void)testInitialObserving
|
||||
{
|
||||
var tester = [ObservingTester testerWithCheese:@"CHEESE!"];
|
||||
[tester addObserver:self forKeyPath:@"cheese" options:CPKeyValueObservingOptionNew | CPKeyValueObservingOptionInitial context:nil];
|
||||
|
||||
[self assert:@"cheese" equals:_lastKeyPath];
|
||||
[self assert:tester equals:_lastObject];
|
||||
[self assert:[CPDictionary dictionaryWithObject:@"CHEESE!" forKey:CPKeyValueChangeNewKey] equals:_lastChange];
|
||||
[self assert:nil equals:_lastContext];
|
||||
}
|
||||
|
||||
- (void)observeValueForKeyPath:(CPString)aKeyPath
|
||||
ofObject:(id)anObject
|
||||
change:(CPDictionary)aChange
|
||||
context:(id)aContext
|
||||
{
|
||||
_lastKeyPath = aKeyPath;
|
||||
_lastObject = anObject;
|
||||
_lastChange = aChange;
|
||||
_lastContext = aContext;
|
||||
}
|
||||
|
||||
- (void)testSendNotificationsForDependantKeyPaths
|
||||
{
|
||||
var observingTester = [ObservingTester testerWithCheese:@"cheese"],
|
||||
dependantKeyPathTester = [DependantKeyPathsTester testerWithObservingTester:observingTester];
|
||||
|
||||
[dependantKeyPathTester addObserver:self forKeyPath:@"observedCheese" options:CPKeyValueObservingOptionNew context:nil];
|
||||
[observingTester setCheese:@"changed cheese"];
|
||||
|
||||
[self assert:@"observedCheese" equals:_lastKeyPath]
|
||||
[self assert:dependantKeyPathTester equals:_lastObject];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation ObservingTester : CPObject
|
||||
{
|
||||
id cheese;
|
||||
}
|
||||
|
||||
+ (id)testerWithCheese:(id)aCheese
|
||||
{
|
||||
var tester = [[self alloc] init];
|
||||
[tester setCheese:aCheese];
|
||||
return tester;
|
||||
}
|
||||
|
||||
- (void)setCheese:(id)aCheese
|
||||
{
|
||||
cheese = aCheese;
|
||||
}
|
||||
|
||||
- (id)cheese
|
||||
{
|
||||
return cheese;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation DependantKeyPathsTester: CPObject
|
||||
{
|
||||
ObservingTester _observingTester @accessors(property=observingTester);
|
||||
}
|
||||
|
||||
+ (CPSet)keyPathsForValuesAffectingObservedCheese
|
||||
{
|
||||
return [CPSet setWithObjects:@"observingTester.cheese"];
|
||||
}
|
||||
|
||||
+ (id)testerWithObservingTester:(ObservingTester)theObservingTester
|
||||
{
|
||||
return [[self alloc] initWithObservingTester:theObservingTester];
|
||||
}
|
||||
|
||||
- (id)initWithObservingTester:(ObservingTester)theObservingTester
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
_observingTester = theObservingTester;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (CPString)observedCheese
|
||||
{
|
||||
return [[self observingTester] cheese];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,86 @@
|
||||
var CPMenuValidatedUserInterfaceItemTestValidatedItems = [];
|
||||
|
||||
@implementation CPMenuValidatedUserInterfaceItemTest : OJTestCase
|
||||
{
|
||||
CPMenu _menu @accessors(property=menu);
|
||||
|
||||
MenuTarget _menuTarget @accessors(property=menuTarget);
|
||||
}
|
||||
|
||||
- (void)setUp
|
||||
{
|
||||
_menuTarget = [[MenuTarget alloc] init];
|
||||
[[CPApplication sharedApplication] setDelegate:_menuTarget];
|
||||
|
||||
_menu = [[CPMenu alloc] init];
|
||||
|
||||
[_menu addItem:[[CPMenuItem alloc] initWithTitle:@"implemented" action:@selector(implementedAction:) keyEquivalent:nil]];
|
||||
[_menu addItem:[[CPMenuItem alloc] initWithTitle:@"disabled" action:@selector(disabledAction:) keyEquivalent:nil]];
|
||||
[_menu addItem:[[CPMenuItem alloc] initWithTitle:@"unimplemented" action:@selector(unimplementedAction:) keyEquivalent:nil]];
|
||||
|
||||
var parentItem = [[CPMenuItem alloc] initWithTitle:@"parent" action:nil keyEquivalent:nil];
|
||||
[_menu addItem:parentItem];
|
||||
|
||||
[parentItem setSubmenu:[[CPMenu alloc] init]];
|
||||
[[parentItem submenu] addItem:[[CPMenuItem alloc] initWithTitle:@"Submenu 1" action:nil keyEquivalent:nil]];
|
||||
|
||||
CPMenuValidatedUserInterfaceItemTestValidatedItems = [];
|
||||
|
||||
// Update is what performs the autoenabling so we need to call it manually.
|
||||
// It's normally called automatically just before a menu becomes visible.
|
||||
[_menu update];
|
||||
}
|
||||
|
||||
- (void)testAutoenable
|
||||
{
|
||||
[self assertTrue:[[[self menu] itemWithTitle:@"implemented"] isEnabled] message:@"The implemented action should be enabled"];
|
||||
[self assertFalse:[[[self menu] itemWithTitle:@"disabled"] isEnabled] message:@"The disbabled action should be disabled"];
|
||||
[self assertFalse:[[[self menu] itemWithTitle:@"unimplemented"] isEnabled] message:@"The unimplemented action should be disabled"];
|
||||
}
|
||||
|
||||
- (void)testThatParentMenusAreNotValidated
|
||||
{
|
||||
var parentItem = [[self menu] itemWithTitle:@"parent"];
|
||||
|
||||
[self assertTrue:[parentItem isEnabled] message:@"Parent items should never be disabled"];
|
||||
[self assertFalse:[[[self menuTarget] validatedItems] containsObject:parent] message:@"Parent items should never be validated"];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation MenuTarget : CPObject
|
||||
{
|
||||
CPArray _validatedItems @accessors(property=validatedItems, readonly);
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
_validatedItems = [];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)validateMenuItem:(CPMenuItem)theMenuItem
|
||||
{
|
||||
[_validatedItems addObject:theMenuItem];
|
||||
|
||||
if ([theMenuItem action] === @selector(disabledAction:))
|
||||
return NO;
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (@action)implementedAction:(id)theSender
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
- (@action)disabledAction:(id)theSender
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,41 @@
|
||||
@import <AppKit/CPTextField.j>
|
||||
|
||||
[CPApplication sharedApplication]
|
||||
|
||||
@implementation CPTextFieldTest : OJTestCase
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
Detect regressions with JS object theme attribute encoding in subclasses.
|
||||
|
||||
This test is not actually CPTextField specific at all. This just happens
|
||||
to be the first place the keyed archiving bug was discovered, and there is
|
||||
an additional, more specific, test in CPKeyedArchiverTest.
|
||||
*/
|
||||
- (void)testArchiveThemeAttributes
|
||||
{
|
||||
var view = [[CPTextFieldSubclass alloc] initWithFrame:CGRectMakeZero()],
|
||||
decoded = [CPKeyedUnarchiver unarchiveObjectWithData:[CPKeyedArchiver archivedDataWithRootObject:view]];
|
||||
|
||||
[self assert:[view valueForThemeAttribute:@"content-inset"].top equals:2 message:@"content-inset should initialise correctly"];
|
||||
[self assert:[view valueForThemeAttribute:@"content-inset"].top equals:[decoded valueForThemeAttribute:@"content-inset"].top message:@"content-inset should unarchive correctly"];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPTextFieldSubclass : CPTextField
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
- (id)initWithFrame:aFrame
|
||||
{
|
||||
if (self = [super initWithFrame:aFrame])
|
||||
{
|
||||
[self setValue:CGInsetMake(2.0, 2.0, 2.0, 2.0) forThemeAttribute:"content-inset"];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -22,6 +22,45 @@
|
||||
return [theClass new];
|
||||
}
|
||||
|
||||
- (void)setup
|
||||
{
|
||||
_lastKeyPath = _lastObject = _lastChange = _lastContext = nil;
|
||||
}
|
||||
|
||||
- (void)testInitialObserving
|
||||
{
|
||||
var tester = [ObservingTester testerWithCheese:@"CHEESE!"];
|
||||
[tester addObserver:self forKeyPath:@"cheese" options:CPKeyValueObservingOptionNew | CPKeyValueObservingOptionInitial context:nil];
|
||||
|
||||
[self assert:@"cheese" equals:_lastKeyPath];
|
||||
[self assert:tester equals:_lastObject];
|
||||
[self assert:[CPDictionary dictionaryWithObject:@"CHEESE!" forKey:CPKeyValueChangeNewKey] equals:_lastChange];
|
||||
[self assert:nil equals:_lastContext];
|
||||
}
|
||||
|
||||
- (void)observeValueForKeyPath:(CPString)aKeyPath
|
||||
ofObject:(id)anObject
|
||||
change:(CPDictionary)aChange
|
||||
context:(id)aContext
|
||||
{
|
||||
_lastKeyPath = aKeyPath;
|
||||
_lastObject = anObject;
|
||||
_lastChange = aChange;
|
||||
_lastContext = aContext;
|
||||
}
|
||||
|
||||
- (void)testSendNotificationsForDependantKeyPaths
|
||||
{
|
||||
var observingTester = [ObservingTester testerWithCheese:@"cheese"],
|
||||
dependantKeyPathTester = [DependantKeyPathsTester testerWithObservingTester:observingTester];
|
||||
|
||||
[dependantKeyPathTester addObserver:self forKeyPath:@"observedCheese" options:CPKeyValueObservingOptionNew context:nil];
|
||||
[observingTester setCheese:@"changed cheese"];
|
||||
|
||||
[self assert:@"observedCheese" equals:_lastKeyPath]
|
||||
[self assert:dependantKeyPathTester equals:_lastObject];
|
||||
}
|
||||
|
||||
- (void)testOnlyInsertObject_AtKeyIndex_Implemented
|
||||
{
|
||||
var insertSelector = @selector(insertObject:inObjectsAtIndex:),
|
||||
@@ -155,3 +194,59 @@
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation ObservingTester : CPObject
|
||||
{
|
||||
id cheese;
|
||||
}
|
||||
|
||||
+ (id)testerWithCheese:(id)aCheese
|
||||
{
|
||||
var tester = [[self alloc] init];
|
||||
[tester setCheese:aCheese];
|
||||
return tester;
|
||||
}
|
||||
|
||||
- (void)setCheese:(id)aCheese
|
||||
{
|
||||
cheese = aCheese;
|
||||
}
|
||||
|
||||
- (id)cheese
|
||||
{
|
||||
return cheese;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation DependantKeyPathsTester: CPObject
|
||||
{
|
||||
ObservingTester _observingTester @accessors(property=observingTester);
|
||||
}
|
||||
|
||||
+ (CPSet)keyPathsForValuesAffectingObservedCheese
|
||||
{
|
||||
return [CPSet setWithObjects:@"observingTester.cheese"];
|
||||
}
|
||||
|
||||
+ (id)testerWithObservingTester:(ObservingTester)theObservingTester
|
||||
{
|
||||
return [[self alloc] initWithObservingTester:theObservingTester];
|
||||
}
|
||||
|
||||
- (id)initWithObservingTester:(ObservingTester)theObservingTester
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
_observingTester = theObservingTester;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (CPString)observedCheese
|
||||
{
|
||||
return [[self observingTester] cheese];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
@import <Foundation/CPKeyedArchiver.j>
|
||||
@import <Foundation/CPKeyedUnarchiver.j>
|
||||
|
||||
@implementation CPKeyedArchiverTest : OJTestCase
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
- (void)testJavaScriptObject
|
||||
{
|
||||
var original = [Archivable new];
|
||||
|
||||
[original setAJsObject:{ 'top': 5 }];
|
||||
|
||||
var decoded = [CPKeyedUnarchiver unarchiveObjectWithData:[CPKeyedArchiver archivedDataWithRootObject:original]]
|
||||
|
||||
[self assert:5 equals:[decoded aJsObject].top message:"JS object encoded and decoded right"];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation Archivable : CPObject
|
||||
{
|
||||
JSObject aJsObject @accessors;
|
||||
}
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
// Note we decode this twice to expose a bug where the cached decoded
|
||||
// values did not properly unwrap JS objects.
|
||||
aJsObject = [aCoder decodeObjectForKey:@"aJsObject"];
|
||||
aJsObject = [aCoder decodeObjectForKey:@"aJsObject"];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[aCoder encodeObject:aJsObject forKey:@"aJsObject"];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -40,6 +40,8 @@ var FILE = require("file"),
|
||||
paths.exclude(FILE.join(FILE.dirname(aPath), "**", "*"));
|
||||
});
|
||||
|
||||
paths.exclude(FILE.join("Build", "**", "*"));
|
||||
|
||||
var mtimesForFilePaths = [CPMutableDictionary new];
|
||||
|
||||
paths.forEach(function(aPath)
|
||||
|
||||
Reference in New Issue
Block a user