Compare commits

..
19 Commits
Author SHA1 Message Date
release a7994c1480 Changed version number to 1.0.0.018 2011-09-16 13:44:51 -05:00
release c605ca4531 Changed version number to 1.0.0.018-SNAPSHOT 2011-09-16 12:37:07 -05:00
release c288930a3c Changed version number to 1.0.0.017 2011-09-16 12:36:59 -05:00
release 596676a562 Changed version number to 1.0.0.017-SNAPSHOT 2011-09-16 06:01:02 -05:00
release cc3d9f337c Changed version number to 1.0.0.016 2011-09-16 06:00:55 -05:00
Aparajita Fishman 8d210100a5 Fix for symlink creation sometimes failing. 2011-09-15 16:41:52 -07:00
Aparajita Fishman d25e96902f Added missing statement separator, concatenated var block 2011-09-15 14:56:24 -07:00
Aparajita Fishman ad792d9772 Fixed problem with FILE.remove sometimes failing to delete the symbolic link. 2011-09-15 14:28:57 -07:00
Aparajita Fishman 9e7b737960 Merge branch 'master' of github.com:intalio/cappuccino 2011-09-13 12:36:53 -07:00
release 9bc6a69b76 Changed version number to 1.0.0.016-SNAPSHOT 2011-09-10 05:58:01 -05:00
Aparajita Fishman bacc50c271 Merge remote branch 'refs/remotes/cappuccino/master'
Conflicts:
	AppKit/CPArrayController.j
	Foundation/CPKeyValueObserving.j
	Tests/Manual/Bindings/AppController.j
2011-09-07 22:04:42 -04:00
Alexander Ljungberg 9f459f053e Fixed: several CPArrayController methods returned undefined instead of booleans. 2011-09-07 18:17:35 +01:00
Alexander Ljungberg 4195f3b8c6 Test that CPArrayController setSelectionIndexes: does not modify its input. 2011-09-07 18:02:53 +01:00
Alexander Ljungberg 4926824482 Fixed: CPArrayController setSelectionIndexes: sometimes modified its input.
Also very slightly improved performance as sometimes the input index set would be instantiated internally and then needlessly copied.
2011-09-07 17:53:48 +01:00
Alexander Ljungberg cb2f539cf7 Send didChange in reverse order of willChange in CPObjectController setContent:. 2011-09-07 17:32:19 +01:00
Alexander Ljungberg 7f31be7cb1 Fixed: array replaceObjectsInRange:withObjectsFromArray: always failed with a "'null' is not an object' error.
This was caused by the new range checks in f8404c7a97.
2011-09-07 16:18:08 +01:00
Alexander Ljungberg 78b9c5b576 Fixes #1343. Support nested willChange… didChange… calls.
When multiple calls are nested, only the first willChange… and the last didChange… will notify observers, avoiding duplicate notifications for the same change.
2011-09-07 16:14:01 +01:00
Alexander Ljungberg 01e35efdbb Refs #1343. Recreate manual bindings test as an automatic test. 2011-09-07 16:04:35 +01:00
Aparajita FishmanandAlexander Ljungberg bef1687b1f Test app for bindings fixes. 2011-09-07 12:51:36 +01:00
8 changed files with 191 additions and 186 deletions
+70 -108
View File
@@ -27,7 +27,6 @@
@import "CPObjectController.j"
@import "CPKeyValueBinding.j"
/*!
@class CPArrayController
@@ -285,7 +284,8 @@
if (value === nil)
value = [];
else if (![value isKindOfClass:[CPArray class]])
if (![value isKindOfClass:[CPArray class]])
value = [value];
var oldSelectedObjects = nil,
@@ -339,6 +339,14 @@
[self setContent:anArray];
}
/*!
@ignore
*/
- (void)_setContentSet:(id)aSet
{
[self setContent:[aSet allObjects]];
}
/*!
Returns the content array of the controller.
@return id the content array of the receiver
@@ -348,14 +356,6 @@
return [self content];
}
/*!
@ignore
*/
- (void)_setContentSet:(id)aSet
{
[self setContent:[aSet allObjects]];
}
/*!
Returns the content of the receiver as a CPSet.
@@ -564,13 +564,9 @@
- (BOOL)setSelectionIndexes:(CPIndexSet)indexes
{
[self _selectionWillChange]
// When explicitly setting the selection, ignore avoidsEmptySelection
var changed = [self __setSelectionIndexes:indexes obeyAvoidsEmptySelection:NO];
[self _selectionDidChangeNotify:NO];
return changed;
var r = [self __setSelectionIndexes:indexes];
[self _selectionDidChange];
return r;
}
/*
@@ -579,7 +575,7 @@
*/
- (BOOL)__setSelectionIndex:(int)theIndex
{
return [self __setSelectionIndexes:[CPIndexSet indexSetWithIndex:theIndex] obeyAvoidsEmptySelection:YES];
return [self __setSelectionIndexes:[CPIndexSet indexSetWithIndex:theIndex]];
}
/*
@@ -588,37 +584,38 @@
*/
- (BOOL)__setSelectionIndexes:(CPIndexSet)indexes
{
[self __setSelectionIndexes:indexes obeyAvoidsEmptySelection:YES];
}
var newIndexes = indexes;
/*
Like setSelectionIndexes but don't fire any change notifications.
@ignore
*/
- (BOOL)__setSelectionIndexes:(CPIndexSet)indexes obeyAvoidsEmptySelection:(BOOL)obeyAvoidsEmptySelection
{
if (!indexes)
indexes = [CPIndexSet indexSet];
if (!newIndexes)
newIndexes = [CPIndexSet indexSet];
if (![indexes count])
if (![newIndexes count])
{
if (obeyAvoidsEmptySelection && _avoidsEmptySelection && [[self arrangedObjects] count])
indexes = [CPIndexSet indexSetWithIndex:0];
if (_avoidsEmptySelection && [[self arrangedObjects] count])
newIndexes = [CPIndexSet indexSetWithIndex:0];
}
else
{
var objectsCount = [[self arrangedObjects] count];
// Don't trash the input - the caller might depend on it or we might have been
// given _selectionIndexes as the input in which case the equality test below
// would always succeed despite our change below.
newIndexes = [newIndexes copy];
// Remove out of bounds indexes.
[indexes removeIndexesInRange:CPMakeRange(objectsCount, [indexes lastIndex] + 1)];
[newIndexes removeIndexesInRange:CPMakeRange(objectsCount, [newIndexes lastIndex] + 1)];
// When avoiding empty selection and the deleted selection was at the bottom, select the last item.
if (![indexes count] && _avoidsEmptySelection && objectsCount)
indexes = [CPIndexSet indexSetWithIndex:objectsCount - 1];
if (![newIndexes count] && _avoidsEmptySelection && objectsCount)
newIndexes = [CPIndexSet indexSetWithIndex:objectsCount - 1];
}
if ([_selectionIndexes isEqualToIndexSet:indexes])
if ([_selectionIndexes isEqualToIndexSet:newIndexes])
return NO;
_selectionIndexes = [indexes copy];
// If we haven't already created our own index instance, make sure to copy it here so that
// the copy the user sent in is decoupled from our internal copy.
_selectionIndexes = indexes === newIndexes ? [indexes copy] : newIndexes;
// Push back the new selection to the model for selectionIndexes if we have one.
// There won't be an infinite loop because of the equality check above.
@@ -650,10 +647,11 @@
[self willChangeValueForKey:@"selectionIndexes"];
[self _selectionWillChange];
[self __setSelectedObjects:objects obeyAvoidsEmptySelection:NO];
var r = [self __setSelectedObjects:objects];
[self didChangeValueForKey:@"selectionIndexes"];
[self _selectionDidChangeNotify:NO];
[self _selectionDidChange];
return r;
}
/*
@@ -661,15 +659,6 @@
@ignore
*/
- (BOOL)__setSelectedObjects:(CPArray)objects
{
[self __setSelectedObjects:objects obeyAvoidsEmptySelection:YES];
}
/*
Like setSelectedObjects but don't fire any change notifications.
@ignore
*/
- (BOOL)__setSelectedObjects:(CPArray)objects obeyAvoidsEmptySelection:(BOOL)obeyAvoidsEmptySelection
{
var set = [CPIndexSet indexSet],
count = [objects count],
@@ -683,7 +672,7 @@
[set addIndex:index];
}
[self __setSelectionIndexes:set obeyAvoidsEmptySelection:obeyAvoidsEmptySelection];
[self __setSelectionIndexes:set];
return YES;
}
@@ -752,13 +741,21 @@
}
[self willChangeValueForKey:@"content"];
[self _willModifyContent];
/*
If the content array is bound then our addObject: message below will cause the observed
array to change. The binding will call setContent:_contentObject on this array
controller to let it know about the change. We want to ignore that message since we
A) already have the right _contentObject and B) properly update _arrangedObjects
by hand below.
*/
_disableSetContent = YES;
[_contentObject addObject:object];
// Allow handlesContentAsCompoundValue reverse sets to trigger.
[[CPBinder getBinding:@"contentArray" forObject:self] _contentArrayDidChange];
[self _didModifyContent];
_disableSetContent = NO;
if (willClearPredicate)
{
@@ -779,15 +776,14 @@
[_selectionIndexes shiftIndexesStartingAtIndex:pos by:1];
}
/*
else if (_filterPredicate !== nil)
...
Implies _filterPredicate && ![_filterPredicate evaluateWithObject:object], so the new object does
not appear in arrangedObjects and we do not have to update at all.
else if (_filterPredicate !== nil)
...
// Implies _filterPredicate && ![_filterPredicate evaluateWithObject:object], so the new object does
// not appear in arrangedObjects and we do not have to update at all.
*/
// This will also send notificaitons for arrangedObjects.
[self didChangeValueForKey:@"content"];
if (willClearPredicate)
[self didChangeValueForKey:@"filterPredicate"];
}
@@ -813,7 +809,10 @@
[self willChangeValueForKey:@"content"];
[self _willModifyContent];
/*
See _disableSetContent explanation in addObject:.
*/
_disableSetContent = YES;
// The atArrangedObjectIndex: part of this method's name only refers to where the
// object goes in arrangedObjects, not in the content array. So use addObject:,
@@ -822,7 +821,7 @@
// Allow handlesContentAsCompoundValue reverse sets to trigger.
[[CPBinder getBinding:@"contentArray" forObject:self] _contentArrayDidChange];
[self _didModifyContent];
_disableSetContent = NO;
if (willClearPredicate)
[self __setFilterPredicate:nil];
@@ -840,7 +839,6 @@
[self __setSelectionIndexes:[CPIndexSet indexSetWithIndex:0]];
[self didChangeValueForKey:@"content"];
if (willClearPredicate)
[self didChangeValueForKey:@"filterPredicate"];
}
@@ -853,13 +851,15 @@
- (void)removeObject:(id)object
{
[self willChangeValueForKey:@"content"];
[self _willModifyContent];
// See _disableSetContent explanation in addObject:.
_disableSetContent = YES;
[_contentObject removeObject:object];
// Allow handlesContentAsCompoundValue reverse sets to trigger.
[[CPBinder getBinding:@"contentArray" forObject:self] _contentArrayDidChange];
[self _didModifyContent];
_disableSetContent = NO;
if (_filterPredicate === nil || [_filterPredicate evaluateWithObject:object])
{
@@ -910,15 +910,6 @@
[self removeObjectsAtArrangedObjectIndexes:_selectionIndexes];
}
/*!
Removes the object at the specified index in the controller's arranged objects from the content array.
@param int index - index of the object to remove.
*/
- (void)removeObjectAtArrangedObjectIndex:(int)index
{
[self removeObjectsAtArrangedObjectIndexes:[CPIndexSet indexSetWithIndex:index]];
}
/*!
Removes the objects at the specified indexes in the controller's arranged objects from the content array.
@param CPIndexSet indexes - indexes of the objects to remove.
@@ -927,21 +918,20 @@
{
[self willChangeValueForKey:@"content"];
/*
See _disableSetContent explanation in addObject:.
*/
_disableSetContent = YES;
var arrangedObjects = [self arrangedObjects],
index = [anIndexSet lastIndex],
position = CPNotFound,
newSelectionIndexes = [_selectionIndexes copy],
proxy = [_CPKVOProxy proxyForObject:self];
newSelectionIndexes = [_selectionIndexes copy];
while (index !== CPNotFound)
{
var object = [arrangedObjects objectAtIndex:index];
// Make sure arrangedObjects does not notify because of content change
[proxy suppressNotificationsForKeyPath:@"arrangedObjects"];
// First try the simple case which should work if there are no sort descriptors.
if ([_contentObject objectAtIndex:index] === object)
[_contentObject removeObjectAtIndex:index];
@@ -955,9 +945,6 @@
contentIndex = [_contentObject indexOfObjectIdenticalTo:object];
[_contentObject removeObjectAtIndex:contentIndex];
}
// Now arrangedObjects can notify
[proxy unsuppressNotificationsForKeyPath:@"arrangedObjects"];
[arrangedObjects removeObjectAtIndex:index];
// Deselect this row if it was selected, and either way shift all selection indexes
@@ -967,7 +954,6 @@
index = [anIndexSet indexLessThanIndex:index];
}
// Allow handlesContentAsCompoundValue reverse sets to trigger.
[[CPBinder getBinding:@"contentArray" forObject:self] _contentArrayDidChange];
_disableSetContent = NO;
@@ -1014,13 +1000,15 @@
- (void)_removeObjects:(CPArray)objects
{
[self willChangeValueForKey:@"content"];
[self _willModifyContent];
// See _disableSetContent explanation in addObject:.
_disableSetContent = YES;
[_contentObject removeObjectsInArray:objects];
// Allow handlesContentAsCompoundValue reverse sets to trigger.
[[CPBinder getBinding:@"contentArray" forObject:self] _contentArrayDidChange];
[self _didModifyContent];
_disableSetContent = NO;
var arrangedObjects = [self arrangedObjects],
position = [arrangedObjects indexOfObject:[objects objectAtIndex:0]];
@@ -1057,39 +1045,13 @@
return [self isEditable];
}
// Internal
- (void)_willModifyContent
{
/*
If the content array is bound then our addObject: message below will cause the observed
array to change. The binding will call setContent:_contentObject on this array
controller to let it know about the change. We want to ignore that message since we
A) already have the right _contentObject and B) properly update _arrangedObjects
by hand below.
*/
_disableSetContent = YES;
/*
When mutating arrangedObjects, we mutate the content first. But mutating the content
will trigger a notification that the arrangedObjects have changed, which we want to
suppress, since the arrangedObjects mutation will do the correct notification.
*/
[[_CPKVOProxy proxyForObject:self] suppressNotificationsForKeyPath:@"arrangedObjects"];
}
- (void)_didModifyContent
{
_disableSetContent = NO;
[[_CPKVOProxy proxyForObject:self] unsuppressNotificationsForKeyPath:@"arrangedObjects"];
}
@end
@implementation CPArrayController (CPBinder)
+ (Class)_binderClassForBinding:(CPString)theBinding
{
if (theBinding === @"content" || theBinding === @"contentArray")
if (theBinding == @"contentArray")
return [_CPArrayControllerContentBinder class];
return [super _binderClassForBinding:theBinding];
@@ -1104,7 +1066,7 @@
var destination = [_info objectForKey:CPObservedObjectKey],
keyPath = [_info objectForKey:CPObservedKeyPathKey],
options = [_info objectForKey:CPOptionsKey],
isCompound = [self handlesContentAsCompoundValue] || keyPath.indexOf("@") !== -1;
isCompound = [self handlesContentAsCompoundValue];
if (!isCompound)
{
+1 -1
View File
@@ -131,8 +131,8 @@
_contentObject = aContent;
[self didChangeValueForKey:@"contentObject"];
[self _selectionDidChange];
[self didChangeValueForKey:@"contentObject"];
}
/*!
+1 -1
View File
@@ -2,7 +2,7 @@ require File.join(File.dirname(__FILE__), "repositories.rb")
require File.join(File.dirname(__FILE__), "dependencies.rb")
# Keep this structure to allow the build system to update version numbers.
VERSION_NUMBER = "1.0.0.015"
VERSION_NUMBER = "1.0.0.018"
# Shorten expressions
+1 -1
View File
@@ -259,7 +259,7 @@ var concat = Array.prototype.concat,
if (aRange.location < 0 || CPMaxRange(aRange) > self.length)
[CPException raise:CPRangeException reason:_cmd + " aRange out of bounds"];
if (otherRange.location < 0 || CPMaxRange(otherRange) > anArray.length)
if (otherRange && (otherRange.location < 0 || CPMaxRange(otherRange) > anArray.length))
[CPException raise:CPRangeException reason:_cmd + " otherRange out of bounds"];
if (otherRange && (otherRange.location !== 0 || otherRange.length !== [anArray count]))
+39 -26
View File
@@ -228,10 +228,10 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew | CPKeyValueObservingOpti
id _targetObject;
Class _nativeClass;
CPDictionary _changesForKey;
CPDictionary _nestingForKey;
Object _observersForKey;
int _observersForKeyLength;
CPSet _replacedKeys;
CPSet _suppressedNotifications;
}
+ (id)proxyForObject:(CPObject)anObject
@@ -248,12 +248,12 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew | CPKeyValueObservingOpti
{
if (self = [super init])
{
_targetObject = aTarget;
_nativeClass = [aTarget class];
_observersForKey = {};
_changesForKey = {};
_observersForKeyLength = 0;
_suppressedNotifications = [CPSet set];
_targetObject = aTarget;
_nativeClass = [aTarget class];
_observersForKey = {};
_changesForKey = {};
_nestingForKey = {};
_observersForKeyLength = 0;
[self _replaceClass];
aTarget[KVOProxyKey] = self;
@@ -631,12 +631,12 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew | CPKeyValueObservingOpti
{
// Fire change events for the dependent keys
var dependentKeysForClass = _nativeClass[DependentKeysKey],
dependentKeys = [dependentKeysForClass[theKeyPath] allObjects],
isBeforeFlag = !![theChanges objectForKey:CPKeyValueChangeNotificationIsPriorKey];
dependantKeys = [dependentKeysForClass[theKeyPath] allObjects];
for (var i = 0; i < [dependentKeys count]; i++)
var isBeforeFlag = !![theChanges objectForKey:CPKeyValueChangeNotificationIsPriorKey];
for (var i = 0; i < [dependantKeys count]; i++)
{
var dependantKey = [dependentKeys objectAtIndex:i];
var dependantKey = [dependantKeys objectAtIndex:i];
[self _sendNotificationsForKey:dependantKey changeOptions:theChanges isBefore:isBeforeFlag];
}
}
@@ -717,6 +717,18 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew | CPKeyValueObservingOpti
if (isBefore)
{
if (changes)
{
// "willChange:X" nesting.
var level = _nestingForKey[aKey];
if (!level)
[CPException raise:CPInternalInconsistencyException reason:@"_changesForKey without _nestingForKey"];
_nestingForKey[aKey] = level + 1;
// Only notify on the first willChange..., silently note any following nested calls.
return;
}
_nestingForKey[aKey] = 1;
changes = changeOptions;
var indexes = [changes objectForKey:CPKeyValueChangeIndexesKey],
@@ -774,9 +786,19 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew | CPKeyValueObservingOpti
}
else
{
if (!changes)
var level = _nestingForKey[aKey];
if (!changes || !level)
[CPException raise:@"CPKeyValueObservingException" reason:@"'didChange...' message called without prior call of 'willChange...'"];
_nestingForKey[aKey] = level - 1;
if (level - 1 > 0)
{
// willChange... was called multiple times. Only fire observation notifications when
// didChange... has been called an equal number of times.
return;
}
delete _nestingForKey[aKey];
[changes removeObjectForKey:CPKeyValueChangeNotificationIsPriorKey];
var indexes = [changes objectForKey:CPKeyValueChangeIndexesKey],
@@ -814,6 +836,8 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew | CPKeyValueObservingOpti
[changes setObject:newValue forKey:CPKeyValueChangeNewKey];
}
delete _changesForKey[aKey];
}
var observers = [_observersForKey[aKey] allValues],
@@ -844,23 +868,12 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew | CPKeyValueObservingOpti
{
var keyPath = dependentKeyPaths[index];
if (![_suppressedNotifications containsObject:keyPath])
[self _sendNotificationsForKey:keyPath
changeOptions:isBefore ? [changeOptions copy] : _changesForKey[keyPath]
isBefore:isBefore];
[self _sendNotificationsForKey:keyPath
changeOptions:isBefore ? [changeOptions copy] : _changesForKey[keyPath]
isBefore:isBefore];
}
}
- (void)suppressNotificationsForKeyPath:(CPString)keyPath
{
[_suppressedNotifications addObject:keyPath];
}
- (void)unsuppressNotificationsForKeyPath:(CPString)keyPath
{
[_suppressedNotifications removeObject:keyPath];
}
@end
@implementation _CPKVOModelSubclass
+74 -1
View File
@@ -464,6 +464,45 @@
[self assert:1 equals:[observations count] message:@"exactly 1 notification for insertObject x 2 (clearsFilterPredicate YES)"];
}
/**
Replicate the situation found in the Bindings manual test where one array controller's
contents depend on the selection of another.
*/
- (void)testObservationBetweenBoundControllers
{
var companies = [Companies new],
companiesController = [CPArrayController new],
employeesController = [CPArrayController new];
[companiesController bind:@"contentArray" toObject:companies withKeyPath:@"items" options:nil];
[employeesController bind:@"contentArray" toObject:companiesController withKeyPath:@"selection.employees" options:nil];
[companiesController addObserver:self forKeyPath:@"selection" options:0 context:@"companies.selection"];
[companiesController addObserver:self forKeyPath:@"selectionIndexes" options:0 context:@"companies.selectionIndexes"];
[companiesController addObserver:self forKeyPath:@"arrangedObjects" options:0 context:@"companies.arrangedObjects"];
[employeesController addObserver:self forKeyPath:@"selectionIndexes" options:0 context:@"employees.selectionIndexes"];
[employeesController addObserver:self forKeyPath:@"selection" options:0 context:@"employees.selection"];
[employeesController addObserver:self forKeyPath:@"arrangedObjects" options:0 context:@"employees.arrangedObjects"];
observations = [];
[companiesController setSelectionIndexes:[CPIndexSet indexSetWithIndex:1]];
//for (var i = 0; i < [observations count]; i++)
// CPLog.error(observations[i].context);
// There should be exactly one observation of each kind, and they should be in the right
// order.
[self assert:5 equals:[observations count]];
[self assert:@"companies.selectionIndexes" equals:observations[0].context];
[self assert:@"employees.arrangedObjects" equals:observations[1].context];
[self assert:@"employees.selectionIndexes" equals:observations[2].context];
[self assert:@"employees.selection" equals:observations[3].context];
// This observation registers after the employees update because we are later in the
// observation queue than the binding.
[self assert:@"companies.selection" equals:observations[4].context];
}
- (void)testCompoundKeyPaths
{
var departmentNameField = [[CPTextField alloc] init];
@@ -524,6 +563,18 @@
[self assert:[1, 2, 1] equals:[ac arrangedObjects] message:"only one copy of 1 removed + the right copy should be removed"];
}
- (void)testSetOutOfBoundsSelectedIndexes
{
var ac = [CPArrayController new],
contentArray = [1, 2, 3],
indexes = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, 4)];
[ac setContent:contentArray];
[ac setSelectionIndexes:indexes];
[self assert:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, 3)] equals:[ac selectionIndexes]];
[self assert:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, 4)] equals:indexes];
}
- (void)observeValueForKeyPath:keyPath
ofObject:anActivity
change:change
@@ -534,6 +585,7 @@
[observations addObject:{
keyPath: keyPath,
context: context,
oldValue: [change valueForKey:CPKeyValueChangeOldKey],
newValue: [change valueForKey:CPKeyValueChangeNewKey]
}];
@@ -620,4 +672,25 @@
return self;
}
@end
@end
@implementation Companies : CPObject
{
CPMutableArray items @accessors;
}
- (id)init
{
self = [super init];
if (self)
{
items = [CPMutableArray array];
[items addObject:[CPDictionary dictionaryWithObjectsAndKeys:@"Spacely Sprockets", @"name", [CPArray arrayWithObjects:@"Tom", @"Dick", @"Harry"], @"employees"]];
[items addObject:[CPDictionary dictionaryWithObjectsAndKeys:@"Cogswell Cogs", @"name", [CPArray arrayWithObjects:@"Jane", @"Mary", @"Vic"], @"employees"]];
}
return self;
}
@end
-41
View File
@@ -78,27 +78,6 @@
[contentView addSubview:employeesScrollView];
[contentView addSubview:text];
var button = [CPButton buttonWithTitle:@"Add Item"];
[button setFrameOrigin:CGPointMake(500, 30)];
[button setTarget:self];
[button setAction:@selector(addItem:)];
[contentView addSubview:button];
button = [CPButton buttonWithTitle:@"Insert Item"];
[button setFrameOrigin:CGPointMake(500, 70)];
[button setTarget:self];
[button setAction:@selector(insertItem:)];
[contentView addSubview:button];
button = [CPButton buttonWithTitle:@"Remove Item"];
[button setFrameOrigin:CGPointMake(500, 110)];
[button setTarget:self];
[button setAction:@selector(removeItem:)];
[contentView addSubview:button];
companiesController = [CPArrayController new];
[companiesController bind:@"contentArray" toObject:companies withKeyPath:@"items" options:nil];
@@ -129,24 +108,4 @@
console.log("\nkeyPath: %s\ncontext: %s\nnew: %s\nold: %s", keyPath, context, [[change valueForKey:CPKeyValueChangeNewKey] description], [[change valueForKey:CPKeyValueChangeOldKey] description]);
}
- (void)addItem:(id)sender
{
var item = [CPDictionary dictionaryWithObjectsAndKeys:@"Seven Dwarves", @"name", [CPArray arrayWithObjects:@"Sneezy", @"Dopey", @"Sleepy", @"Doc", @"Bashful", @"Happy", @"Grumpy"], @"employees"];
[companiesController addObject:item];
}
- (void)insertItem:(id)sender
{
var item = [CPDictionary dictionaryWithObjectsAndKeys:@"Acme Corp", @"name", [CPArray arrayWithObjects:@"Road Runner", @"Wile E. Coyote"], @"employees"];
[companiesController insertObject:item atArrangedObjectIndex:1];
}
- (void)removeItem:(id)sender
{
if ([[companiesController arrangedObjects] count])
[companiesController removeObjectAtArrangedObjectIndex:0];
}
@end
+5 -7
View File
@@ -1,10 +1,10 @@
require("../../common.jake");
var OS = require("os");
var task = require("jake").task
var stream = require("narwhal/term").stream;
var applicationName = "xcodecapp-cocoa.app";
var OS = require("os"),
task = require("jake").task,
stream = require("narwhal/term").stream,
applicationName = "xcodecapp-cocoa.app";
task ("build", function()
{
@@ -22,9 +22,7 @@ task ("build", function()
cp_r(FILE.join("build", "Release", "xcodecapp-cocoa.app"), supportPath);
FILE.chmod(FILE.join(supportPath, "Contents", "MacOS", "xcodecapp-cocoa"), 0755);
if (FILE.exists(installPath))
FILE.remove(installPath);
FILE.symlink(supportPath, installPath);
OS.system(["ln", "-sf", supportPath, installPath]);
}
else
{