mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-09-09 12:17:13 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
be0b97cb56 | ||
|
|
6f7cbe21ef | ||
|
|
eff3dfc74a | ||
|
|
2ad1f4e4e5 | ||
|
|
4aec3f7466 | ||
|
|
c8991e07b9 | ||
|
|
48157db973 | ||
|
|
d070e5fce5 | ||
|
|
a8fae51ea8 | ||
|
|
2815d462be | ||
|
|
ed002d7d3e | ||
|
|
a7994c1480 | ||
|
|
85f8ca9241 | ||
|
|
b5fb9e8c23 | ||
|
|
c605ca4531 | ||
|
|
c288930a3c | ||
|
|
596676a562 | ||
|
|
cc3d9f337c | ||
|
|
1addc002d3 | ||
|
|
e99e2660e9 | ||
|
|
03d1bb8bce | ||
|
|
8d210100a5 | ||
|
|
d25e96902f | ||
|
|
ad792d9772 | ||
|
|
5b00f7f2e8 | ||
|
|
97b7f0901e | ||
|
|
67b2671dad | ||
|
|
9e7b737960 | ||
|
|
9bc6a69b76 | ||
|
|
1cb28e4206 | ||
|
|
5311689265 | ||
|
|
4228a2a146 | ||
|
|
7886d49e65 | ||
|
|
e5284e9b43 | ||
|
|
c6403baa04 | ||
|
|
662b1ec696 | ||
|
|
043df71185 | ||
|
|
8dab47f4da | ||
|
|
90d069df2b | ||
|
|
dcbe3ee650 | ||
|
|
e32489aba1 | ||
|
|
b619c0b793 | ||
|
|
fab61d14fa | ||
|
|
b4edc084f8 | ||
|
|
c7518d0c49 | ||
|
|
a40345c14d | ||
|
|
bacc50c271 | ||
|
|
909471a5d7 | ||
|
|
38f7ea00c2 | ||
|
|
9f459f053e | ||
|
|
4195f3b8c6 | ||
|
|
4926824482 | ||
|
|
cb2f539cf7 | ||
|
|
7f31be7cb1 | ||
|
|
78b9c5b576 | ||
|
|
01e35efdbb | ||
|
|
bef1687b1f | ||
|
|
da18a9eb24 | ||
|
|
d975e90407 | ||
|
|
abc8e14275 | ||
|
|
afa691376e | ||
|
|
be10bcf27a |
+70
-108
@@ -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)
|
||||
{
|
||||
|
||||
@@ -131,8 +131,8 @@
|
||||
|
||||
_contentObject = aContent;
|
||||
|
||||
[self didChangeValueForKey:@"contentObject"];
|
||||
[self _selectionDidChange];
|
||||
[self didChangeValueForKey:@"contentObject"];
|
||||
}
|
||||
|
||||
/*!
|
||||
|
||||
+30
-3
@@ -210,10 +210,25 @@ var CPThemeStateAutoCompleting = @"CPThemeStateAutoCompleting",
|
||||
if (shouldRemoveLastObject)
|
||||
[objectValue removeObjectAtIndex:_selectedRange.location];
|
||||
|
||||
[objectValue insertObject:token atIndex:_selectedRange.location];
|
||||
// Give the delegate a chance to confirm, replace or add to the list of tokens being added.
|
||||
var delegateApprovedObjects = [self tokenField:self shouldAddObjects:[CPArray arrayWithObject:token] atIndex:_selectedRange.location],
|
||||
delegateApprovedObjectsCount = [delegateApprovedObjects count];
|
||||
if (delegateApprovedObjects)
|
||||
{
|
||||
for (var i = 0; i < delegateApprovedObjectsCount; i++)
|
||||
{
|
||||
[objectValue insertObject:[delegateApprovedObjects objectAtIndex:i] atIndex:_selectedRange.location + i];
|
||||
}
|
||||
}
|
||||
|
||||
// Put the cursor after the last inserted token.
|
||||
var location = _selectedRange.location;
|
||||
|
||||
[self setObjectValue:objectValue];
|
||||
_selectedRange = CPMakeRange(location + 1, 0);
|
||||
|
||||
if (delegateApprovedObjectsCount)
|
||||
location += delegateApprovedObjectsCount;
|
||||
_selectedRange = CPMakeRange(location, 0);
|
||||
|
||||
[self _inputElement].value = @"";
|
||||
[self setNeedsLayout];
|
||||
@@ -1175,7 +1190,19 @@ var CPThemeStateAutoCompleting = @"CPThemeStateAutoCompleting",
|
||||
// // return an array of represented objects you want to add.
|
||||
// // If you want to reject the add, return an empty array.
|
||||
// // returning nil will cause an error.
|
||||
// - (NSArray *)tokenField:(NSTokenField *)tokenField shouldAddObjects:(NSArray *)tokens atIndex:(NSUInteger)index;
|
||||
- (CPArray)tokenField:(CPTokenField)tokenField shouldAddObjects:(CPArray)tokens atIndex:(int)index
|
||||
{
|
||||
var delegate = [self delegate];
|
||||
if ([delegate respondsToSelector:@selector(tokenField:shouldAddObjects:atIndex:)])
|
||||
{
|
||||
var approvedObjects = [delegate tokenField:tokenField shouldAddObjects:tokens atIndex:index];
|
||||
if (approvedObjects !== nil)
|
||||
return approvedObjects;
|
||||
}
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
//
|
||||
// // If you return nil or don't implement these delegate methods, we will assume
|
||||
// // editing string = display string = represented object
|
||||
|
||||
+63
-14
@@ -426,6 +426,7 @@ CPTexturedBackgroundWindowMask
|
||||
|
||||
// Create a generic content view.
|
||||
[self setContentView:[[CPView alloc] initWithFrame:CGRectMakeZero()]];
|
||||
[self setInitialFirstResponder:[self contentView]];
|
||||
|
||||
_firstResponder = self;
|
||||
|
||||
@@ -480,7 +481,7 @@ CPTexturedBackgroundWindowMask
|
||||
[self close];
|
||||
|
||||
_platformWindow = aPlatformWindow;
|
||||
[_platformWindow setTitle:_title];
|
||||
[_platformWindow _setTitle:_title window:self];
|
||||
|
||||
if (wasVisible)
|
||||
[self orderFront:self];
|
||||
@@ -950,6 +951,12 @@ CPTexturedBackgroundWindowMask
|
||||
|
||||
var bounds = CGRectMake(0.0, 0.0, CGRectGetWidth(_frame), CGRectGetHeight(_frame));
|
||||
|
||||
// During init the initial first responder is set to the contentView
|
||||
// if it hasn't changed in the mean time we need to update that reference
|
||||
// to the new contentView
|
||||
if ([self initialFirstResponder] === _contentView)
|
||||
[self setInitialFirstResponder:aView];
|
||||
|
||||
_contentView = aView;
|
||||
[_contentView setFrame:[self contentRectForFrameRect:bounds]];
|
||||
|
||||
@@ -1274,17 +1281,17 @@ CPTexturedBackgroundWindowMask
|
||||
|
||||
- (BOOL)acceptsFirstResponder
|
||||
{
|
||||
return YES;
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (id)initialFirstResponder
|
||||
- (CPView)initialFirstResponder
|
||||
{
|
||||
return _initialFirstResponder;
|
||||
}
|
||||
|
||||
- (void)setInitialFirstResponder:(id)aResponder
|
||||
- (void)setInitialFirstResponder:(CPView)aView
|
||||
{
|
||||
_initialFirstResponder = aResponder;
|
||||
_initialFirstResponder = aView;
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -1360,7 +1367,7 @@ CPTexturedBackgroundWindowMask
|
||||
_title = aTitle;
|
||||
|
||||
[_windowView setTitle:aTitle];
|
||||
[_platformWindow setTitle:_title];
|
||||
[_platformWindow _setTitle:_title window:self];
|
||||
|
||||
[self _synchronizeMenuBarTitleWithWindowTitle];
|
||||
}
|
||||
@@ -2487,30 +2494,70 @@ CPTexturedBackgroundWindowMask
|
||||
|
||||
- (void)selectNextKeyView:(id)sender
|
||||
{
|
||||
if (_keyViewLoopIsDirty && [self autorecalculatesKeyViewLoop])
|
||||
[self recalculateKeyViewLoop];
|
||||
|
||||
var nextValidKeyView = nil;
|
||||
|
||||
if ([_firstResponder isKindOfClass:[CPView class]])
|
||||
[self selectKeyViewFollowingView:_firstResponder];
|
||||
nextValidKeyView = [_firstResponder nextValidKeyView];
|
||||
|
||||
if (!nextValidKeyView)
|
||||
{
|
||||
var initialFirstResponder = [self initialFirstResponder];
|
||||
|
||||
if ([initialFirstResponder acceptsFirstResponder])
|
||||
nextValidKeyView = initialFirstResponder;
|
||||
else
|
||||
nextValidKeyView = [initialFirstResponder nextValidKeyView];
|
||||
}
|
||||
|
||||
[self makeFirstResponder:nextValidKeyView];
|
||||
}
|
||||
|
||||
- (void)selectPreviousKeyView:(id)sender
|
||||
{
|
||||
if (_keyViewLoopIsDirty && [self autorecalculatesKeyViewLoop])
|
||||
[self recalculateKeyViewLoop];
|
||||
|
||||
var previousValidKeyView = nil;
|
||||
|
||||
if ([_firstResponder isKindOfClass:[CPView class]])
|
||||
[self selectKeyViewPrecedingView:_firstResponder];
|
||||
previousValidKeyView = [_firstResponder previousValidKeyView];
|
||||
|
||||
if (!previousValidKeyView)
|
||||
{
|
||||
var initialFirstResponder = [self initialFirstResponder];
|
||||
|
||||
if ([initialFirstResponder acceptsFirstResponder])
|
||||
previousValidKeyView = initialFirstResponder;
|
||||
else
|
||||
previousValidKeyView = [initialFirstResponder previousValidKeyView];
|
||||
}
|
||||
|
||||
[self makeFirstResponder:previousValidKeyView];
|
||||
}
|
||||
|
||||
- (void)selectKeyViewFollowingView:(CPView)aView
|
||||
{
|
||||
if (_keyViewLoopIsDirty)
|
||||
if (_keyViewLoopIsDirty && [self autorecalculatesKeyViewLoop])
|
||||
[self recalculateKeyViewLoop];
|
||||
|
||||
[self makeFirstResponder:[aView nextValidKeyView]];
|
||||
var nextValidKeyView = [aView nextValidKeyView];
|
||||
|
||||
if ([nextValidKeyView isKindOfClass:[CPView class]])
|
||||
[self makeFirstResponder:nextValidKeyView];
|
||||
}
|
||||
|
||||
- (void)selectKeyViewPrecedingView:(CPView)aView
|
||||
{
|
||||
if (_keyViewLoopIsDirty)
|
||||
if (_keyViewLoopIsDirty && [self autorecalculatesKeyViewLoop])
|
||||
[self recalculateKeyViewLoop];
|
||||
|
||||
[self makeFirstResponder:[aView previousValidKeyView]];
|
||||
var previousValidKeyView = [aView previousValidKeyView];
|
||||
|
||||
if ([previousValidKeyView isKindOfClass:[CPView class]])
|
||||
[self makeFirstResponder:previousValidKeyView];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -2598,9 +2645,11 @@ CPTexturedBackgroundWindowMask
|
||||
|
||||
var allViews = function(aWindow)
|
||||
{
|
||||
var views = [[aWindow contentView] subviews],
|
||||
index = 0;
|
||||
var views = [CPArray arrayWithObject:[aWindow contentView]];
|
||||
|
||||
[views addObjectsFromArray:[[aWindow contentView] subviews]];
|
||||
|
||||
var index = 0;
|
||||
for (; index < views.length; ++index)
|
||||
views = views.concat([views[index] subviews]);
|
||||
|
||||
|
||||
@@ -259,12 +259,12 @@ var PrimaryPlatformWindow = NULL;
|
||||
return [CPPlatform isBrowser];
|
||||
}
|
||||
|
||||
- (void)setTitle:(CPString)aTitle
|
||||
- (void)_setTitle:(CPString)aTitle window:(CPWindow)aWindow
|
||||
{
|
||||
_title = aTitle;
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
if (_DOMWindow && _DOMWindow.document)
|
||||
if (_DOMWindow && _DOMWindow.document && (aWindow === [CPApp mainWindow]))
|
||||
_DOMWindow.document.title = _title;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -541,7 +541,8 @@ var ModifierKeyCodes = [
|
||||
// FIXME: cpSetFrame?
|
||||
_DOMWindow.document.write("<!DOCTYPE html><html lang='en'><head></head><body style='background-color:transparent;'></body></html>");
|
||||
_DOMWindow.document.close();
|
||||
_DOMWindow.document.title = _title;
|
||||
if (self != [CPPlatformWindow primaryPlatformWindow])
|
||||
_DOMWindow.document.title = _title;
|
||||
|
||||
if (![CPPlatform isBrowser])
|
||||
{
|
||||
|
||||
@@ -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.012"
|
||||
VERSION_NUMBER = "1.0.0.019"
|
||||
|
||||
|
||||
# Shorten expressions
|
||||
|
||||
+108
-19
@@ -131,7 +131,7 @@
|
||||
|
||||
_replaceManySEL = sel_getName(@"replace" + capitalizedKey + "AtIndexes:with" + capitalizedKey + ":");
|
||||
if ([_proxyObject respondsToSelector:_replaceManySEL])
|
||||
_replace = [_proxyObject methodForSelector:_replaceManySEL];
|
||||
_replaceMany = [_proxyObject methodForSelector:_replaceManySEL];
|
||||
|
||||
_objectAtIndexSEL = sel_getName(@"objectIn" + capitalizedKey + "AtIndex:");
|
||||
if ([_proxyObject respondsToSelector:_objectAtIndexSEL])
|
||||
@@ -233,10 +233,25 @@
|
||||
|
||||
- (id)objectAtIndex:(unsigned)anIndex
|
||||
{
|
||||
if (_objectAtIndex)
|
||||
return _objectAtIndex(_proxyObject, _objectAtIndexSEL, anIndex);
|
||||
return [[self objectsAtIndexes:[CPIndexSet indexSetWithIndex:anIndex]] firstObject];
|
||||
}
|
||||
|
||||
return [[self _representedObject] objectAtIndex:anIndex];
|
||||
- (CPArray)objectsAtIndexes:(CPIndexSet)theIndexes
|
||||
{
|
||||
if (_objectsAtIndexes)
|
||||
return _objectsAtIndexes(_proxyObject, _objectsAtIndexesSEL, theIndexes);
|
||||
if (_objectAtIndex)
|
||||
{
|
||||
var index = CPNotFound,
|
||||
objects = [];
|
||||
|
||||
while ((index = [theIndexes indexGreaterThanIndex:index]) !== CPNotFound)
|
||||
objects.push(_objectAtIndex(_proxyObject, _objectAtIndexSEL, index));
|
||||
|
||||
return objects;
|
||||
}
|
||||
|
||||
return [[self _representedObject] objectsAtIndexes:theIndexes];
|
||||
}
|
||||
|
||||
- (void)addObject:(id)anObject
|
||||
@@ -288,37 +303,111 @@
|
||||
[self removeObject:anObject inRange:CPMakeRange(0, [self count])];
|
||||
}
|
||||
|
||||
- (void)removeLastObject
|
||||
- (void)removeObjectsInArray:(CPArray)theObjects
|
||||
{
|
||||
if (_removeMany)
|
||||
{
|
||||
var indexes = [CPIndexSet indexSet],
|
||||
index = [theObjects count];
|
||||
|
||||
while (index--)
|
||||
[indexes addIndex:[self indexOfObject:[theObjects objectAtIndex:index]]];
|
||||
|
||||
_removeMany(_proxyObject, _removeManySEL, indexes);
|
||||
}
|
||||
else if (_remove)
|
||||
{
|
||||
var index = [theObjects count];
|
||||
while (index--)
|
||||
_remove(_proxyObject, _removeSEL, [self indexOfObject:[theObjects objectAtIndex:index]]);
|
||||
}
|
||||
else
|
||||
{
|
||||
var target = [[self _representedObject] copy];
|
||||
[target removeObjectsInArray:theObjects];
|
||||
[self _setRepresentedObject:target];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)removeObject:(id)theObject inRange:(CPRange)theRange
|
||||
{
|
||||
if (_remove)
|
||||
return _remove(_proxyObject, _removeSEL, [self count] - 1);
|
||||
_remove(_proxyObject, _removeSEL, [self indexOfObject:theObject inRange:theRange]);
|
||||
else if (_removeMany)
|
||||
{
|
||||
var index = [self indexOfObject:theObject inRange:theRange];
|
||||
_removeMany(_proxyObject, _removeManySEL, [CPIndexSet indexSetWithIndex:index]);
|
||||
}
|
||||
else
|
||||
{
|
||||
var index;
|
||||
|
||||
var target = [[self _representedObject] copy];
|
||||
while ((index = [self indexOfObject:theObject inRange:theRange]) !== CPNotFound)
|
||||
{
|
||||
[self removeObjectAtIndex:index];
|
||||
theRange = CPIntersectionRange(CPMakeRange(index, length - index), theRange);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[target removeLastObject];
|
||||
[self _setRepresentedObject:target];
|
||||
- (void)removeLastObject
|
||||
{
|
||||
[self removeObjectsAtIndexes:[CPIndexSet indexSetWithIndex:[self count] - 1]];
|
||||
}
|
||||
|
||||
- (void)removeObjectAtIndex:(unsigned)anIndex
|
||||
{
|
||||
if (_remove)
|
||||
return _remove(_proxyObject, _removeSEL, anIndex);
|
||||
[self removeObjectsAtIndexes:[CPIndexSet indexSetWithIndex:anIndex]];
|
||||
}
|
||||
|
||||
var target = [[self _representedObject] copy];
|
||||
- (void)removeObjectsAtIndexes:(CPIndexSet)theIndexes
|
||||
{
|
||||
if (_removeMany)
|
||||
_removeMany(_proxyObject, _removeManySEL, theIndexes);
|
||||
else if (_remove)
|
||||
{
|
||||
var index = [theIndexes lastIndex];
|
||||
|
||||
[target removeObjectAtIndex:anIndex];
|
||||
[self _setRepresentedObject:target];
|
||||
while (index !== CPNotFound)
|
||||
{
|
||||
_remove(_proxyObject, _removeSEL, index)
|
||||
index = [theIndexes indexLessThanIndex:index];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var target = [[self _representedObject] copy];
|
||||
[target removeObjectsAtIndexes:theIndexes];
|
||||
[self _setRepresentedObject:target];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)replaceObjectAtIndex:(unsigned)anIndex withObject:(id)anObject
|
||||
{
|
||||
if (_replace)
|
||||
return _replace(_proxyObject, _replaceSEL, anIndex, anObject);
|
||||
[self replaceObjectsAtIndexes:[CPIndexSet indexSetWithIndex:anIndex] withObjects:[anObject]]
|
||||
}
|
||||
|
||||
var target = [[self _representedObject] copy];
|
||||
- (void)replaceObjectsAtIndexes:(CPIndexSet)theIndexes withObjects:(CPArray)theObjects
|
||||
{
|
||||
if (_replaceMany)
|
||||
return _replaceMany(_proxyObject, _replaceManySEL, theIndexes, theObjects);
|
||||
else if (_replace)
|
||||
{
|
||||
var i = 0,
|
||||
index = [theIndexes firstIndex];
|
||||
|
||||
[target replaceObjectAtIndex:anIndex withObject:anObject];
|
||||
[self _setRepresentedObject:target];
|
||||
while (index !== CPNotFound)
|
||||
{
|
||||
_replace(_proxyObject, _replaceSEL, index, [theObjects objectAtIndex:i++]);
|
||||
index = [theIndexes indexGreaterThanIndex:index];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var target = [[self _representedObject] copy];
|
||||
[target replaceObjectsAtIndexes:theIndexes withObjects:theObjects];
|
||||
[self _setRepresentedObject:target];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -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]))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -59,7 +59,7 @@ task ("sudo-install", ["CommonJS"], function()
|
||||
if (OS.system(["sudo", "tusk", "install", "--force", $BUILD_CJS_OBJECTIVE_J, $BUILD_CJS_CAPPUCCINO]))
|
||||
{
|
||||
// Attempt a hackish work-around for sudo compiled with the --with-secure-path option
|
||||
sudo("tusk install --force " + $BUILD_CJS_OBJECTIVE_J + " " + $BUILD_CJS_CAPPUCCINO)
|
||||
sudo("tusk install --force " + $BUILD_CJS_OBJECTIVE_J + " " + $BUILD_CJS_CAPPUCCINO);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -71,10 +71,38 @@ task ("install-symlinks", function()
|
||||
|
||||
task ("install-debug-symlinks", function()
|
||||
{
|
||||
SYSTEM.env["CONFIG"] = "Debug";
|
||||
SYSTEM.env.CONFIG = "Debug";
|
||||
spawnJake("install-symlinks");
|
||||
});
|
||||
|
||||
task ("clean-sprites", function()
|
||||
{
|
||||
var f = new FileList(FILE.join(SYSTEM.env.CAPP_BUILD, "**/dataURLs.txt")),
|
||||
paths = f.items();
|
||||
|
||||
f = new FileList(FILE.join(SYSTEM.env.CAPP_BUILD, "**/MHTML*.txt"));
|
||||
paths = paths.concat(f.items());
|
||||
|
||||
paths.forEach(function(path)
|
||||
{
|
||||
FILE.remove(path);
|
||||
});
|
||||
});
|
||||
|
||||
task ("clobber-theme", function()
|
||||
{
|
||||
var f = new FileList(FILE.join(SYSTEM.env.CAPP_BUILD, "**/Aristo.blend")),
|
||||
paths = f.items();
|
||||
|
||||
f = new FileList(FILE.join(SYSTEM.env.CAPP_BUILD, "Aristo.build"));
|
||||
paths = paths.concat(f.items());
|
||||
|
||||
paths.forEach(function(path)
|
||||
{
|
||||
rm_rf(path);
|
||||
});
|
||||
});
|
||||
|
||||
// Documentation
|
||||
|
||||
$DOCUMENTATION_BUILD = FILE.join($BUILD_DIR, "Documentation");
|
||||
@@ -160,6 +188,10 @@ function generateDocs(/* boolean */ noFrame)
|
||||
rm_rf($DOCUMENTATION_BUILD);
|
||||
mv("debug.txt", FILE.join("Documentation", "debug.txt"));
|
||||
mv("Documentation", $DOCUMENTATION_BUILD);
|
||||
|
||||
// There is a bug in doxygen 1.7.x preventing loading correctly the custom CSS
|
||||
// So let's do it manually
|
||||
cp(FILE.join(documentationDir, "doxygen.css"), FILE.join($DOCUMENTATION_BUILD, "html", "doxygen.css"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,30 +279,30 @@ task ("demos", function()
|
||||
if (key)
|
||||
return this._plist.valueForKey(key);
|
||||
return this._plist;
|
||||
}
|
||||
};
|
||||
|
||||
Demo.prototype.name = function()
|
||||
{
|
||||
return this.plist("CPBundleName");
|
||||
}
|
||||
};
|
||||
|
||||
Demo.prototype.path = function()
|
||||
{
|
||||
return this._path;
|
||||
}
|
||||
};
|
||||
|
||||
Demo.prototype.excluded = function()
|
||||
{
|
||||
return !!this.plist("CPDemoExcluded");
|
||||
}
|
||||
};
|
||||
|
||||
Demo.prototype.toString = function()
|
||||
{
|
||||
return this.name();
|
||||
}
|
||||
};
|
||||
|
||||
FILE.glob(FILE.join(demosDir, "demos", "**/Info.plist")).map(function(demoPath){
|
||||
return new Demo(FILE.dirname(demoPath))
|
||||
return new Demo(FILE.dirname(demoPath));
|
||||
}).filter(function(demo){
|
||||
return !demo.excluded();
|
||||
}).forEach(function(demo)
|
||||
@@ -315,7 +347,7 @@ task("push-cappuccino", function() {
|
||||
pushPackage(
|
||||
$BUILD_CJS_CAPPUCCINO,
|
||||
"git@github.com:280north/cappuccino-package.git",
|
||||
SYSTEM.env["PACKAGE_BRANCH"]
|
||||
SYSTEM.env.PACKAGE_BRANCH
|
||||
);
|
||||
});
|
||||
|
||||
@@ -323,7 +355,7 @@ task("push-objective-j", function() {
|
||||
pushPackage(
|
||||
$BUILD_CJS_OBJECTIVE_J,
|
||||
"git@github.com:280north/objective-j-package.git",
|
||||
SYSTEM.env["PACKAGE_BRANCH"]
|
||||
SYSTEM.env.PACKAGE_BRANCH
|
||||
);
|
||||
});
|
||||
|
||||
@@ -361,7 +393,7 @@ function pushPackage(path, remote, branch)
|
||||
|
||||
var pkg = JSON.parse(packagePath.join("package.json").read({ charset : "UTF-8" }));
|
||||
|
||||
stream.print(" Version: " + colorize(pkg["version"], "purple"));
|
||||
stream.print(" Version: " + colorize(pkg.version, "purple"));
|
||||
stream.print(" Revision: " + colorize(pkg["cappuccino-revision"], "purple"));
|
||||
stream.print(" Timestamp: " + colorize(pkg["cappuccino-timestamp"], "purple"));
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -5,15 +5,108 @@
|
||||
|
||||
@implementation CPWindowTest : OJTestCase
|
||||
{
|
||||
CPWindow theWindow;
|
||||
CPWindow _window @accessors(property=window);
|
||||
}
|
||||
|
||||
-(void)testCanAllocWindow
|
||||
- (void)setUp
|
||||
{
|
||||
theWindow = [[CPWindow alloc] initWithContentRect:CGRectMake(0,0,200,150)
|
||||
styleMask:CPWindowNotSizable];
|
||||
_window = [[CPWindow alloc] initWithContentRect:CGRectMake(0.0, 0.0, 1024.0, 768.0)
|
||||
styleMask:CPWindowNotSizable];
|
||||
}
|
||||
|
||||
[self assertTrue:!!theWindow];
|
||||
- (void)testCanAllocWindow
|
||||
{
|
||||
[self assertTrue:!![self window]];
|
||||
}
|
||||
|
||||
- (void)testThatContentViewIsInitialFirstResponder
|
||||
{
|
||||
var contentView = [[self window] contentView];
|
||||
[self assert:contentView
|
||||
equals:[[self window] initialFirstResponder]
|
||||
message:@"The window's content view must be the initial first responder"];
|
||||
}
|
||||
|
||||
- (void)testKeyViewLoop
|
||||
{
|
||||
var contentView = [[self window] contentView],
|
||||
textField = [CPTextField textFieldWithStringValue:@"" placeholder:@"" width:100.0];
|
||||
|
||||
[textField setFrame:CGRectMake(100.0, 100.0, 100.0, 26.0)];
|
||||
[contentView addSubview:textField];
|
||||
[[self window] recalculateKeyViewLoop];
|
||||
|
||||
var nextTextField = [CPTextField textFieldWithStringValue:@"" placeholder:@"" width:100];
|
||||
[nextTextField setFrame:CGRectMake(100.0, 200.0, 100.0, 26.0)];
|
||||
[contentView addSubview:nextTextField];
|
||||
[[self window] recalculateKeyViewLoop];
|
||||
|
||||
// Test nextKeyView
|
||||
[self assert:textField
|
||||
equals:[contentView nextKeyView]
|
||||
message:@"textField should be the next key view of contentView"];
|
||||
|
||||
[self assert:nextTextField
|
||||
equals:[textField nextKeyView]
|
||||
message:@"nextTextField should be the next key view of textField"];
|
||||
|
||||
[self assert:contentView
|
||||
equals:[nextTextField nextKeyView]
|
||||
message:@"contentView should be the next key view of nextTextField"];
|
||||
|
||||
// Test previousKeyView
|
||||
[self assert:contentView
|
||||
equals:[textField previousKeyView]
|
||||
message:@"contentView should be the previous key view of textField"];
|
||||
|
||||
[self assert:textField
|
||||
equals:[nextTextField previousKeyView]
|
||||
message:@"textField should be the previous key view of nextTextField"];
|
||||
|
||||
[self assert:nextTextField
|
||||
equals:[contentView previousKeyView]
|
||||
message:@"nextTextField should be the previous key view of contentView"];
|
||||
}
|
||||
|
||||
- (void)testValiKeyViewLoop
|
||||
{
|
||||
var contentView = [[self window] contentView],
|
||||
textField = [CPTextField textFieldWithStringValue:@"" placeholder:@"" width:100.0];
|
||||
|
||||
[textField setFrame:CGRectMake(100.0, 100.0, 100.0, 26.0)];
|
||||
[contentView addSubview:textField];
|
||||
[[self window] recalculateKeyViewLoop];
|
||||
|
||||
var nextTextField = [CPTextField textFieldWithStringValue:@"" placeholder:@"" width:100];
|
||||
[nextTextField setFrame:CGRectMake(100.0, 200.0, 100.0, 26.0)];
|
||||
[contentView addSubview:nextTextField];
|
||||
[[self window] recalculateKeyViewLoop];
|
||||
|
||||
// Test nextValidKeyView
|
||||
[self assert:textField
|
||||
equals:[contentView nextValidKeyView]
|
||||
message:@"textField should be the next valid key view of contentView"];
|
||||
|
||||
[self assert:nextTextField
|
||||
equals:[textField nextValidKeyView]
|
||||
message:@"nextTextField should be the next valid key view of textField"];
|
||||
|
||||
[self assert:textField
|
||||
equals:[nextTextField nextValidKeyView]
|
||||
message:@"textField should be the next key valid view of nextTextField"];
|
||||
|
||||
// Test previousValidKeyView
|
||||
[self assert:nextTextField
|
||||
equals:[textField previousValidKeyView]
|
||||
message:@"nextTextField should be the previous valid key view of textField"];
|
||||
|
||||
[self assert:textField
|
||||
equals:[nextTextField previousValidKeyView]
|
||||
message:@"textField should be the previous key valid view of nextTextField"];
|
||||
|
||||
[self assert:nextTextField
|
||||
equals:[contentView previousValidKeyView]
|
||||
message:@"nextTextField should be the previous valid key view of contentView"];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,396 @@
|
||||
var COUNTER;
|
||||
|
||||
@implementation CPKVCArrayTest : OJTestCase
|
||||
{
|
||||
TestObject _object @accessors(property=object);
|
||||
ImplementedTestObject _implementedObject @accessors(property=implementedObject);
|
||||
}
|
||||
|
||||
- (void)setUp
|
||||
{
|
||||
_object = [[TestObject alloc] init];
|
||||
_implementedObject = [[ImplementedTestObject alloc] init];
|
||||
|
||||
COUNTER = 0;
|
||||
}
|
||||
|
||||
- (void)_patchSelector:(SEL)theSelector
|
||||
{
|
||||
var method_dtable = [[self object] class].method_dtable,
|
||||
method_list = [[self object] class].method_list,
|
||||
selectorsToRemove = [@selector(countOfValues), @selector(objectInValuesAtIndex),
|
||||
@selector(objectInValuesAtIndex:), @selector(valuesAtIndexes:),
|
||||
@selector(insertObject:inValuesAtIndex:), @selector(insertValues:atIndexes:),
|
||||
@selector(removeObjectFromValuesAtIndex:), @selector(removeValuesAtIndexes:),
|
||||
@selector(removeObjectFromValuesAtIndex:),
|
||||
@selector(replaceObjectInValuesAtIndex:withObject:),
|
||||
@selector(replaceValuesAtIndexes:withValues:)],
|
||||
selectorIndex = [selectorsToRemove count];
|
||||
|
||||
while (selectorIndex--)
|
||||
{
|
||||
var selector = [selectorsToRemove objectAtIndex:selectorIndex],
|
||||
implementation = method_dtable[selector];
|
||||
|
||||
if (!implementation)
|
||||
continue;
|
||||
|
||||
delete method_dtable[selector]
|
||||
[method_list removeObject:implementation];
|
||||
}
|
||||
|
||||
var method = class_getInstanceMethod([[self implementedObject] class], theSelector),
|
||||
implementation = method_getImplementation(method);
|
||||
|
||||
class_addMethod([[self object] class], theSelector, implementation);
|
||||
}
|
||||
|
||||
- (void)testUsesCountOfKey
|
||||
{
|
||||
[self _patchSelector:@selector(countOfValues)];
|
||||
|
||||
var count = [[[self object] mutableArrayValueForKey:@"values"] count];
|
||||
|
||||
[self assert:10 equals:count message:@"countOfValues should return 10"];
|
||||
[self assert:1 equals:COUNTER message:@"countOfValues should have been called once"]
|
||||
}
|
||||
|
||||
- (void)testObjectAtIndexUsesObjectInKeyAtIndex
|
||||
{
|
||||
[self _patchSelector:@selector(objectInValuesAtIndex:)];
|
||||
|
||||
var values = [[self object] mutableArrayValueForKey:@"values"],
|
||||
object = [values objectAtIndex:0];
|
||||
|
||||
[self assert:0 equals:object]
|
||||
[self assert:1 equals:COUNTER message:@"objectInValuesAtIndex: should have been called once"];
|
||||
}
|
||||
|
||||
- (void)testObjectsAtIndexesUsesObjectInKeyAtIndex
|
||||
{
|
||||
[self _patchSelector:@selector(objectInValuesAtIndex:)];
|
||||
|
||||
var values = [[self object] mutableArrayValueForKey:@"values"],
|
||||
objects = [values objectsAtIndexes:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, 2)]];
|
||||
|
||||
[self assert:[0, 1] equals:objects]
|
||||
[self assert:2 equals:COUNTER message:@"objectInValuesAtIndex: should have been called once"];
|
||||
}
|
||||
|
||||
- (void)testObjectAtIndexUsesKeyAtIndexes
|
||||
{
|
||||
[self _patchSelector:@selector(valuesAtIndexes:)];
|
||||
|
||||
var values = [[self object] mutableArrayValueForKey:@"values"],
|
||||
object = [values objectAtIndex:0];
|
||||
|
||||
[self assert:0 equals:object]
|
||||
[self assert:1 equals:COUNTER message:@"valuesAtIndexes: should have been called once"];
|
||||
}
|
||||
|
||||
- (void)testObjectsAtIndexesUsesKeyAtIndexes
|
||||
{
|
||||
[self _patchSelector:@selector(valuesAtIndexes:)];
|
||||
|
||||
var values = [[self object] mutableArrayValueForKey:@"values"],
|
||||
objects = [values objectsAtIndexes:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, 2)]];
|
||||
|
||||
[self assert:[0, 1] equals:objects]
|
||||
[self assert:1 equals:COUNTER message:@"valuesAtIndexes: should have been called once"];
|
||||
}
|
||||
|
||||
- (void)testInsertObjectAtIndexUsesInsertKeyAtIndex
|
||||
{
|
||||
[self _patchSelector:@selector(insertObject:inValuesAtIndex:)];
|
||||
|
||||
[[[self object] mutableArrayValueForKey:@"values"] insertObject:11 atIndex:10];
|
||||
|
||||
[self assert:11 equals:[[[self object] values] objectAtIndex:10]];
|
||||
[self assert:1 equals:COUNTER message:@"insertObject:inValuesAtIndex: should have been called once"];
|
||||
}
|
||||
|
||||
- (void)testInsertObjectsAtIndexesUsesInsertKeyAtIndex
|
||||
{
|
||||
[self _patchSelector:@selector(insertObject:inValuesAtIndex:)];
|
||||
|
||||
var indexes = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(10, 2)];
|
||||
[[[self object] mutableArrayValueForKey:@"values"] insertObjects:[11, 12] atIndexes:indexes];
|
||||
|
||||
[self assert:[11, 12] equals:[[[self object] values] objectsAtIndexes:indexes]];
|
||||
[self assert:2
|
||||
equals:COUNTER
|
||||
message:@"insertValues:atIndexes: should have been called once for each object"];
|
||||
}
|
||||
|
||||
- (void)testInsertObjectAtIndexUsesInsertKeyAtIndexes
|
||||
{
|
||||
[self _patchSelector:@selector(insertValues:atIndexes:)];
|
||||
|
||||
[[[self object] mutableArrayValueForKey:@"values"] insertObject:11 atIndex:10];
|
||||
|
||||
[self assert:11 equals:[[[self object] values] objectAtIndex:10]];
|
||||
[self assert:1 equals:COUNTER message:@"insertValues:atIndexes: should have been called once"];
|
||||
}
|
||||
|
||||
- (void)testInsertObjectsAtIndexesUsesInsertKeyAtIndexes
|
||||
{
|
||||
[self _patchSelector:@selector(insertValues:atIndexes:)];
|
||||
|
||||
var indexes = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(10, 2)];
|
||||
[[[self object] mutableArrayValueForKey:@"values"] insertObjects:[11, 12] atIndexes:indexes];
|
||||
|
||||
[self assert:[11, 12] equals:[[[self object] values] objectsAtIndexes:indexes]];
|
||||
[self assert:1
|
||||
equals:COUNTER
|
||||
message:@"insertValues:atIndexes: should have been called once for each object"];
|
||||
}
|
||||
|
||||
- (void)testRemoveObjectAtIndexUsesRemoveObjectFromKeyAtIndex
|
||||
{
|
||||
[self _patchSelector:@selector(removeObjectFromValuesAtIndex:)];
|
||||
|
||||
[[[self object] mutableArrayValueForKey:@"values"] removeObjectAtIndex:0];
|
||||
|
||||
[self assert:[1, 2, 3, 4, 5, 6, 7, 8, 9] equals:[[self object] values]];
|
||||
[self assert:1 equals:COUNTER message:@"removeObjectFromValuesAtIndex: should have been called once"];
|
||||
}
|
||||
|
||||
- (void)testRemoveObjectAtIndexesUsesRemoveObjectFromKeyAtIndex
|
||||
{
|
||||
[self _patchSelector:@selector(removeObjectFromValuesAtIndex:)];
|
||||
|
||||
var indexes = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, 2)];
|
||||
[[[self object] mutableArrayValueForKey:@"values"] removeObjectsAtIndexes:indexes];
|
||||
|
||||
[self assert:[2, 3, 4, 5, 6, 7, 8, 9] equals:[[self object] values]];
|
||||
[self assert:2 equals:COUNTER message:@"removeValuesAtIndex: should have been called once for each object"];
|
||||
}
|
||||
- (void)testRemoveObjectsAtIndexesUsesRemoveKeyAtIndex
|
||||
{
|
||||
[self _patchSelector:@selector(removeValuesAtIndexes:)];
|
||||
|
||||
[[[self object] mutableArrayValueForKey:@"values"] removeObjectAtIndex:0];
|
||||
|
||||
[self assert:[1, 2, 3, 4, 5, 6, 7, 8, 9] equals:[[self object] values]];
|
||||
[self assert:1
|
||||
equals:COUNTER
|
||||
message:@"removeValuesAtIndexes: should have been once for each object"];
|
||||
}
|
||||
|
||||
- (void)testRemoveObjectsAtIndexesUsesRemoveKeyAtIndexes
|
||||
{
|
||||
[self _patchSelector:@selector(removeValuesAtIndexes:)];
|
||||
|
||||
var indexes = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(3, 2)];
|
||||
[[[self object] mutableArrayValueForKey:@"values"] removeObjectsAtIndexes:indexes];
|
||||
|
||||
[self assert:[0, 1, 2, 5, 6, 7, 8, 9] equals:[[self object] values]];
|
||||
[self assert:1 equals:COUNTER message:@"removeValuesAtIndexes: should have been called once"];
|
||||
}
|
||||
|
||||
- (void)testRemoveObjectUsesRemoveKeyAtIndex
|
||||
{
|
||||
[self _patchSelector:@selector(removeObjectFromValuesAtIndex:)];
|
||||
|
||||
[[[self object] mutableArrayValueForKey:@"values"] removeObject:1];
|
||||
|
||||
[self assert:[0, 2, 3, 4, 5, 6, 7, 8, 9] equals:[[self object] values]];
|
||||
[self assert:1 equals:COUNTER message:"removeObjectFromValuesAtIndex: should have been called once"]
|
||||
}
|
||||
|
||||
- (void)testeRemoveObjectUsesRemoveKeyAtIndexes
|
||||
{
|
||||
[self _patchSelector:@selector(removeValuesAtIndexes:)];
|
||||
|
||||
[[[self object] mutableArrayValueForKey:@"values"] removeObject:5];
|
||||
|
||||
[self assert:[0, 1, 2, 3, 4, 6, 7, 8, 9] equals:[[self object] values]];
|
||||
[self assert:1 equals:COUNTER message:@"removeValuesAtIndexes: should have been called once"];
|
||||
}
|
||||
|
||||
- (void)testRemoveObjectsInArrayUsesRemoveKeyAtIndex
|
||||
{
|
||||
[self _patchSelector:@selector(removeObjectFromValuesAtIndex:)];
|
||||
|
||||
[[[self object] mutableArrayValueForKey:@"values"] removeObjectsInArray:[3, 6]];
|
||||
|
||||
[self assert:[0, 1, 2, 4, 5, 7, 8, 9] equals:[[self object] values]];
|
||||
[self assert:2 equals:COUNTER message:"removeObjectFromValuesAtIndex: should have been called once"]
|
||||
}
|
||||
|
||||
- (void)testRemoveObjectsInArrayUsesRemoveKeyAtIndexes
|
||||
{
|
||||
[self _patchSelector:@selector(removeValuesAtIndexes:)];
|
||||
|
||||
[[[self object] mutableArrayValueForKey:@"values"] removeObjectsInArray:[0, 9]];
|
||||
|
||||
[self assert:[1, 2, 3, 4, 5, 6, 7, 8] equals:[[self object] values]];
|
||||
[self assert:1 equals:COUNTER message:@"removeValuesAtIndexes: should have been called once"];
|
||||
}
|
||||
|
||||
- (void)testRemoveLastObjectUsesRemoveKeyAtIndex
|
||||
{
|
||||
[self _patchSelector:@selector(removeObjectFromValuesAtIndex:)];
|
||||
|
||||
[[[self object] mutableArrayValueForKey:@"values"] removeLastObject];
|
||||
|
||||
[self assert:[0, 1, 2, 3, 4, 5, 6, 7, 8] equals:[[self object] values]];
|
||||
[self assert:1 equals:COUNTER message:"removeObjectFromValuesAtIndex: should have been called once"]
|
||||
}
|
||||
|
||||
- (void)testRemoveLastObjectUsesRemoveKeyAtIndexes
|
||||
{
|
||||
[self _patchSelector:@selector(removeValuesAtIndexes:)];
|
||||
|
||||
[[[self object] mutableArrayValueForKey:@"values"] removeLastObject];
|
||||
|
||||
[self assert:[0, 1, 2, 3, 4, 5, 6, 7, 8] equals:[[self object] values]];
|
||||
[self assert:1 equals:COUNTER message:@"removeValuesAtIndexes: should have been called once"];
|
||||
}
|
||||
|
||||
- (void)testReplaceObjectAtIndexWithObjectUsesReplaceObjectInKeyAtIndexWithObject
|
||||
{
|
||||
[self _patchSelector:@selector(replaceObjectInValuesAtIndex:withObject:)];
|
||||
|
||||
[[[self object] mutableArrayValueForKey:@"values"] replaceObjectAtIndex:0 withObject:1];
|
||||
|
||||
[self assert:[1, 1, 2, 3, 4, 5, 6, 7, 8, 9] equals:[[self object] values]];
|
||||
[self assert:1
|
||||
equals:COUNTER
|
||||
message:@"replaceObjectInValuesAtIndex:withObject: should have been called once"];
|
||||
}
|
||||
|
||||
- (void)testReplaceObjectsAtIndexesWithObjectsUsesReplaceObjectInKeyAtIndexWithObject
|
||||
{
|
||||
[self _patchSelector:@selector(replaceObjectInValuesAtIndex:withObject:)];
|
||||
|
||||
var indexes = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(5, 3)];
|
||||
[[[self object] mutableArrayValueForKey:@"values"] replaceObjectsAtIndexes:indexes withObjects:[1, 2, 3]];
|
||||
|
||||
[self assert:[0, 1, 2, 3, 4, 1, 2, 3, 8, 9] equals:[[self object] values]];
|
||||
[self assert:3
|
||||
equals:COUNTER
|
||||
message:@"replaceObjectInValuesAtIndex:withObject: should have been called once"];
|
||||
}
|
||||
|
||||
- (void)testReplaceObjectAtIndexWithObjectUsesReplaceKeyAtIndexesWithKeys
|
||||
{
|
||||
[self _patchSelector:@selector(replaceValuesAtIndexes:withValues:)];
|
||||
|
||||
[[[self object] mutableArrayValueForKey:@"values"] replaceObjectAtIndex:5 withObject:6];
|
||||
|
||||
[self assert:[0, 1, 2, 3, 4, 6, 6, 7, 8, 9] equals:[[self object] values]];
|
||||
[self assert:1
|
||||
equals:COUNTER
|
||||
message:@"replaceObjectInValuesAtIndex:withObject: should have been called once"];
|
||||
}
|
||||
|
||||
- (void)testReplaceObjectsAtIndexesWithObjectsUsesReplaceKeyAtIndexesWithKeys
|
||||
{
|
||||
[self _patchSelector:@selector(replaceValuesAtIndexes:withValues:)];
|
||||
|
||||
var indexes = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(6, 2)];
|
||||
[[[self object] mutableArrayValueForKey:@"values"] replaceObjectsAtIndexes:indexes withObjects:[7, 8]];
|
||||
|
||||
[self assert:[0, 1, 2, 3, 4, 5, 7, 8, 8, 9] equals:[[self object] values]];
|
||||
[self assert:1
|
||||
equals:COUNTER
|
||||
message:@"replaceObjectInValuesAtIndexes:withValues: should have been called once"];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation TestObject : CPObject
|
||||
{
|
||||
CPArray _values @accessors(property=values);
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
_values = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation ImplementedTestObject : TestObject
|
||||
{
|
||||
}
|
||||
|
||||
- (int)countOfValues
|
||||
{
|
||||
// CPLog.warn(@"countOfValues");
|
||||
|
||||
COUNTER += 1;
|
||||
return [[self values] count];
|
||||
}
|
||||
|
||||
- (id)objectInValuesAtIndex:(int)theIndex
|
||||
{
|
||||
// CPLog.warn(@"objectInValuesAtIndex: %@", theIndex);
|
||||
|
||||
COUNTER += 1;
|
||||
return [[self values] objectAtIndex:theIndex];
|
||||
}
|
||||
|
||||
- (CPArray)valuesAtIndexes:(CPIndexSet)theIndexes
|
||||
{
|
||||
// CPLog.warn(@"valuesAtIndexes: %@", theIndexes);
|
||||
|
||||
COUNTER += 1;
|
||||
return [[self values] objectsAtIndexes:theIndexes];
|
||||
}
|
||||
|
||||
- (void)insertObject:(id)theObject inValuesAtIndex:(int)theIndex
|
||||
{
|
||||
// CPLog.warn(@"insertObject: %@ inValuesAtIndex: %@", theObject, theIndex);
|
||||
|
||||
COUNTER += 1;
|
||||
[[self values] insertObject:theObject atIndex:theIndex];
|
||||
}
|
||||
|
||||
- (void)insertValues:(CPArray)theObjects atIndexes:(CPIndexSet)theIndexes
|
||||
{
|
||||
// CPLog.warn(@"insertValues: %@ atIndexes: %@", theObjects, theIndexes);
|
||||
|
||||
COUNTER += 1;
|
||||
[[self values] insertObjects:theObjects atIndexes:theIndexes];
|
||||
}
|
||||
|
||||
- (void)removeObjectFromValuesAtIndex:(int)theIndex
|
||||
{
|
||||
// CPLog.warn(@"removeObjectFromValuesAtIndex: %@", theIndex);
|
||||
|
||||
COUNTER += 1;
|
||||
[[self values] removeObjectAtIndex:theIndex];
|
||||
}
|
||||
|
||||
- (void)removeValuesAtIndexes:(CPIndexSet)theIndexes
|
||||
{
|
||||
// CPLog.warn(@"removeValuesAtIndexes: %@", theIndexes);
|
||||
|
||||
COUNTER += 1;
|
||||
[[self values] removeObjectsAtIndexes:theIndexes];
|
||||
}
|
||||
|
||||
- (void)replaceObjectInValuesAtIndex:(int)theIndex withObject:(id)theObject
|
||||
{
|
||||
// CPLog.warn(@"replaceObjectInValuesAtIndex: %@ withObject: %@", theIndex, theObject);
|
||||
|
||||
COUNTER += 1;
|
||||
[[self values] replaceObjectAtIndex:theIndex withObject:theObject];
|
||||
}
|
||||
|
||||
- (void)replaceValuesAtIndexes:(CPIndexSet)theIndexes withValues:(id)theObjects
|
||||
{
|
||||
// CPLog.warn(@"replaceValuesAtIndexes: %@ withValues: %@", theIndexes, theObjects);
|
||||
|
||||
COUNTER += 1;
|
||||
[[self values] replaceObjectsAtIndexes:theIndexes withObjects:theObjects];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -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
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
Run this application with the current master, and note the following bugs:
|
||||
|
||||
- When you click on any item, there are two selection change notifications instead of one.
|
||||
|
||||
- If you compare the order of the notifications with Cocoa, they are slightly different.
|
||||
|
||||
- The right hand table is configured to allow an empty selection. If you click in the
|
||||
empty area below the names, there is an empty selection notification, but then the
|
||||
the current selection changes to the first item and generates another notification.
|
||||
|
||||
- If you select multiple items on the left, there is an error. This is a bug that will
|
||||
have to be fixed in a later commit.
|
||||
|
||||
If you apply the changes in these commits, the first three bugs are fixed.
|
||||
@@ -15,6 +15,7 @@ var STATES = ['Alabama', 'Alaska', 'Arizona', 'Arkansas', 'California', 'Colorad
|
||||
CPTokenField tokenFieldD;
|
||||
|
||||
CPArray allPersons;
|
||||
CPButton manipulateTokenInsertionButton;
|
||||
}
|
||||
|
||||
- (void)applicationDidFinishLaunching:(CPNotification)aNotification
|
||||
@@ -22,28 +23,28 @@ var STATES = ['Alabama', 'Alaska', 'Arizona', 'Arkansas', 'California', 'Colorad
|
||||
var theWindow = [[CPWindow alloc] initWithContentRect:CGRectMakeZero() styleMask:CPBorderlessBridgeWindowMask],
|
||||
contentView = [theWindow contentView],
|
||||
|
||||
tokenFieldA = [[CPTokenField alloc] initWithFrame:CGRectMake(15, 40, 500, 29)],
|
||||
tokenFieldA = [[CPTokenField alloc] initWithFrame:CGRectMake(15, 40, 500, 30)],
|
||||
label = [[CPTextField alloc] initWithFrame:CGRectMake(15, 15, 500, 24)];
|
||||
|
||||
[label setStringValue:@"This token field has no auto suggestions and uses space to separate tokens."];
|
||||
[label setStringValue:"This token field has no auto suggestions and uses space to separate tokens."];
|
||||
[contentView addSubview:label];
|
||||
|
||||
[tokenFieldA setEditable:YES];
|
||||
[tokenFieldA setPlaceholderString:@"Type in a token!"];
|
||||
[tokenFieldA setPlaceholderString:"Type in a token!"];
|
||||
|
||||
[tokenFieldA setTokenizingCharacterSet:[CPCharacterSet characterSetWithCharactersInString:@" "]];
|
||||
|
||||
[contentView addSubview:tokenFieldA];
|
||||
|
||||
var tokenFieldB = [[CPTokenField alloc] initWithFrame:CGRectMake(15, 110, 500, 29)],
|
||||
var tokenFieldB = [[CPTokenField alloc] initWithFrame:CGRectMake(15, 110, 500, 30)],
|
||||
labelB = [[CPTextField alloc] initWithFrame:CGRectMake(15, 80, 500, 24)];
|
||||
|
||||
[labelB setStringValue:@"This token field has no bezel, uses comma to separate tokens and has auto suggest."];
|
||||
[labelB setStringValue:"This token field has no bezel, uses comma to separate tokens and has auto suggest."];
|
||||
[contentView addSubview:labelB];
|
||||
|
||||
[tokenFieldB setEditable:YES];
|
||||
[tokenFieldB setBezeled:NO];
|
||||
[tokenFieldB setPlaceholderString:@"Edit me!"];
|
||||
[tokenFieldB setPlaceholderString:"Edit me!"];
|
||||
|
||||
[tokenFieldB setObjectValue:["Missouri", "California"]];
|
||||
[tokenFieldB setDelegate:self];
|
||||
@@ -53,14 +54,18 @@ var STATES = ['Alabama', 'Alaska', 'Arizona', 'Arkansas', 'California', 'Colorad
|
||||
|
||||
[contentView addSubview:tokenFieldB];
|
||||
|
||||
var tokenFieldC = [[CPTokenField alloc] initWithFrame:CGRectMake(15, 170, 500, 29)],
|
||||
manipulateTokenInsertionButton = [CPCheckBox checkBoxWithTitle:"Token transformations"];
|
||||
[manipulateTokenInsertionButton setFrame:CGRectMake(525, 110, 200, 50)];
|
||||
[contentView addSubview:manipulateTokenInsertionButton];
|
||||
|
||||
var tokenFieldC = [[CPTokenField alloc] initWithFrame:CGRectMake(15, 170, 500, 30)],
|
||||
labelC = [[CPTextField alloc] initWithFrame:CGRectMake(15, 150, 500, 24)];
|
||||
|
||||
[labelC setStringValue:@"This token field can't fit all its tokens."];
|
||||
[labelC setStringValue:"This token field can't fit all its tokens."];
|
||||
[contentView addSubview:labelC];
|
||||
|
||||
[tokenFieldC setEditable:YES];
|
||||
[tokenFieldC setPlaceholderString:@"Edit me!"];
|
||||
[tokenFieldC setPlaceholderString:"Edit me!"];
|
||||
|
||||
[tokenFieldC setObjectValue:['Alabama', 'Alaska', 'Arizona', 'Arkansas', 'California', 'Colorado']];
|
||||
[tokenFieldC setDelegate:self];
|
||||
@@ -71,10 +76,10 @@ var STATES = ['Alabama', 'Alaska', 'Arizona', 'Arkansas', 'California', 'Colorad
|
||||
[tokenFieldC setAction:@selector(tokenFieldAction:)];
|
||||
[tokenFieldC setTarget:self];
|
||||
|
||||
tokenFieldD = [[CPTokenField alloc] initWithFrame:CGRectMake(15, 230, 500, 29)],
|
||||
tokenFieldD = [[CPTokenField alloc] initWithFrame:CGRectMake(15, 230, 500, 30)],
|
||||
labelD = [[CPTextField alloc] initWithFrame:CGRectMake(15, 210, 500, 24)];
|
||||
|
||||
[labelD setStringValue:@"This token field contains represented objects."];
|
||||
[labelD setStringValue:"This token field contains represented objects."];
|
||||
[contentView addSubview:labelD];
|
||||
|
||||
[tokenFieldD setEditable:YES];
|
||||
@@ -93,7 +98,7 @@ var STATES = ['Alabama', 'Alaska', 'Arizona', 'Arkansas', 'California', 'Colorad
|
||||
[contentView addSubview:tokenFieldD];
|
||||
|
||||
var button = [[CPButton alloc] initWithFrame:CGRectMake(15, 270, 0, 0)];
|
||||
[button setTitle:@"Get Object Values"];
|
||||
[button setTitle:"Get Object Values"];
|
||||
[button sizeToFit];
|
||||
[button setTarget:self];
|
||||
[button setAction:@selector(getObjectValues:)];
|
||||
@@ -148,6 +153,25 @@ var STATES = ['Alabama', 'Alaska', 'Arizona', 'Arkansas', 'California', 'Colorad
|
||||
CPLog.info("tokenFieldAction: " + sender);
|
||||
}
|
||||
|
||||
- (CPArray)tokenField:tokenField shouldAddObjects:tokens atIndex:index
|
||||
{
|
||||
CPLog.info("tokenField: " + tokenField + " shouldAddObjects: " + tokens + " atIndex: " + index);
|
||||
|
||||
// Texas -> Utah, Michigan -> Michigan & Arkansas, Washington -> nil
|
||||
|
||||
if ([manipulateTokenInsertionButton intValue])
|
||||
{
|
||||
if (tokens[0] == "Texas")
|
||||
return ["Utah"];
|
||||
else if (tokens[0] == "Michigan")
|
||||
return ["Michigan", "Arkansas"];
|
||||
else if (tokens[0] == "Washington")
|
||||
return [];
|
||||
}
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
// A sample of a custom Object
|
||||
|
||||
@@ -37,44 +37,46 @@
|
||||
|
||||
platformWindow = [[CPPlatformWindow alloc] init];
|
||||
|
||||
// [platformWindow orderFront:self];//console.log(CPStringFromRect([theWindow contentRectForFrameRect:[theWindow frame]]));
|
||||
// [platformWindow orderFront:self];//console.log(CPStringFromRect([theWindow contentRectForFrameRect:[theWindow frame]]));
|
||||
[platformWindow setContentRect:[theWindow contentRectForFrameRect:[theWindow frame]]];
|
||||
|
||||
// [theWindow setTitle:@"My Custom Title"];
|
||||
//[platformWindow orderFront:self];
|
||||
|
||||
// seriously, this test file is dirty :)
|
||||
var slider = [[theWindow contentView] subviews][0];
|
||||
[slider setTarget:self];
|
||||
[slider setAction:@selector(randomTitle:)];
|
||||
[slider setContinuous:NO];
|
||||
|
||||
window.setTimeout(function(){
|
||||
[theWindow orderOut:self];
|
||||
[theWindow setPlatformWindow:platformWindow];
|
||||
[theWindow orderFront:self];
|
||||
[theWindow setFullBridge:YES];
|
||||
var button = [[theWindow contentView] subviews][2];
|
||||
[theWindow setTitle:"Initial title (pop out pending)"];
|
||||
window.setTimeout(function()
|
||||
{
|
||||
[theWindow orderOut:self];
|
||||
[theWindow setPlatformWindow:platformWindow];
|
||||
[theWindow orderFront:self];
|
||||
[theWindow setFullBridge:YES];
|
||||
var button = [[theWindow contentView] subviews][2];
|
||||
|
||||
[button setTarget:self];
|
||||
[button setAction:@selector(close)];
|
||||
[button setTarget:self];
|
||||
[button setAction:@selector(close)];
|
||||
[theWindow setTitle:"Title after pop out (red box pending)"];
|
||||
|
||||
window.setTimeout(function(){
|
||||
var view = [[CPView alloc] initWithFrame:CGRectMake(0.0, 0.0, 100.0, 100.0)];
|
||||
[view setBackgroundColor:[CPColor redColor]];
|
||||
|
||||
[[theWindow contentView] addSubview:view];},1000);
|
||||
|
||||
}, 1000);
|
||||
//window.setTimeout(function() { [platformWindow orderOut:self]; [platformWindow orderFront:self]; }, 2000);
|
||||
window.setTimeout(function()
|
||||
{
|
||||
var view = [[CPView alloc] initWithFrame:CGRectMake(0.0, 0.0, 100.0, 100.0)];
|
||||
[view setBackgroundColor:[CPColor redColor]];
|
||||
|
||||
[[theWindow contentView] addSubview:view];
|
||||
[theWindow setTitle:"Title in final stage"];
|
||||
}, 1000);
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
- (void)close
|
||||
{
|
||||
[[theWindow platformWindow] orderOut:self];
|
||||
window.setTimeout(function(){
|
||||
[[theWindow platformWindow] orderFront:self];}, 1000);
|
||||
window.setTimeout(function()
|
||||
{
|
||||
[[theWindow platformWindow] orderFront:self];
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
- (void)another:(id)aSender
|
||||
@@ -84,7 +86,7 @@ window.setTimeout(function(){
|
||||
|
||||
- (IBAction)randomTitle:(id)aSender
|
||||
{
|
||||
[theWindow setTitle:@"Window random number " + Math.random(1000)]
|
||||
[theWindow setTitle:@"Window random number " + Math.random(1000)];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -25,7 +25,7 @@ DOXYFILE_ENCODING = UTF-8
|
||||
# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
|
||||
# by quotes) that should identify the project.
|
||||
|
||||
PROJECT_NAME = "Cappuccino API"
|
||||
PROJECT_NAME = " API"
|
||||
|
||||
# The PROJECT_NUMBER tag can be used to enter a project or revision number.
|
||||
# This could be handy for archiving the generated documentation or
|
||||
@@ -33,6 +33,8 @@ PROJECT_NAME = "Cappuccino API"
|
||||
|
||||
PROJECT_NUMBER = 0.9.1
|
||||
|
||||
PROJECT_LOGO = ./Tools/Documentation/logo.png
|
||||
|
||||
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
|
||||
# base path where the generated documentation will be put.
|
||||
# If a relative path is entered, it will be relative to the location
|
||||
@@ -845,7 +847,7 @@ HTML_FOOTER =
|
||||
# the style sheet file to the HTML output directory, so don't put your own
|
||||
# stylesheet in the HTML output directory as well, or it will be erased!
|
||||
|
||||
HTML_STYLESHEET = ./Tools/Documentation/doxygen.css
|
||||
#HTML_STYLESHEET = doxygen.css
|
||||
|
||||
# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
|
||||
# Doxygen will adjust the colors in the stylesheet and background images
|
||||
|
||||
+770
-307
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
@@ -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
|
||||
{
|
||||
|
||||
+1
-1
@@ -133,7 +133,7 @@ global.$BUILD_CJS_CAPPUCCINO_FRAMEWORKS = FILE.join($BUILD_CJS_CAPPUCCINO, "Fram
|
||||
|
||||
global.CLEAN = require("jake/clean").CLEAN;
|
||||
global.CLOBBER = require("jake/clean").CLOBBER;
|
||||
global.CLEAN.include(global.$BUILD_DIR);
|
||||
global.CLEAN.include(FILE.join(global.$BUILD_DIR, "*.build"));
|
||||
global.CLOBBER.include(global.$BUILD_DIR);
|
||||
|
||||
global.$HOME_DIR = FILE.absolute(FILE.dirname(module.path));
|
||||
|
||||
Reference in New Issue
Block a user