diff --git a/AppKit/CPAlert.j b/AppKit/CPAlert.j index 5d7a50bc5..24180055d 100644 --- a/AppKit/CPAlert.j +++ b/AppKit/CPAlert.j @@ -51,7 +51,6 @@ CPInformationalAlertStyle = 1; */ CPCriticalAlertStyle = 2; - /*! @ingroup appkit @@ -88,6 +87,8 @@ CPCriticalAlertStyle = 2; CPArray _buttons; id _delegate; + SEL _didEndSelector; + id _modalDelegate; } + (CPString)themeClass @@ -126,6 +127,7 @@ CPCriticalAlertStyle = 2; _alertStyle = CPWarningAlertStyle; _alertPanel = nil; _windowStyle = nil; + _didEndSelector = nil; _messageLabel = [CPTextField labelWithTitle:@"Alert"]; _alertImageView = [[CPImageView alloc] initWithFrame:CGRectMakeZero()]; @@ -145,7 +147,7 @@ CPCriticalAlertStyle = 2; { _windowStyle = styleMask; - [self setTheme:(_windowStyle === CPHUDBackgroundWindowMask) ? [CPTheme defaultHudTheme] : [CPTheme defaultTheme]]; + [self setTheme:(_windowStyle & CPHUDBackgroundWindowMask) ? [CPTheme defaultHudTheme] : [CPTheme defaultTheme]]; // We'll need to recreate the panel to get the new window style. _alertPanel = nil; @@ -171,9 +173,6 @@ CPCriticalAlertStyle = 2; [contentView addSubview:_messageLabel]; [contentView addSubview:_alertImageView]; [contentView addSubview:_informativeLabel]; - - // For reference: does not actually work since this 'view' is not in the hierarchy. - // [self setNeedsLayout]; } /*! @@ -225,9 +224,6 @@ CPCriticalAlertStyle = 2; - (void)setAlertStyle:(CPAlertStyle)style { _alertStyle = style; - - // For reference: does not actually work since this 'view' is not in the hierarchy. - // [self setNeedsLayout]; } /*! @@ -245,9 +241,6 @@ CPCriticalAlertStyle = 2; - (void)setMessageText:(CPString)messageText { [_messageLabel setStringValue:messageText]; - - // For reference: does not actually work since this 'view' is not in the hierarchy. - // [self setNeedsLayout]; } /*! @@ -265,8 +258,6 @@ CPCriticalAlertStyle = 2; - (void)setInformativeText:(CPString)informativeText { [_informativeLabel setStringValue:informativeText]; - // No need to call _layoutMessage - only the length of the messageText - // can affect anything there. } /*! @@ -296,7 +287,7 @@ CPCriticalAlertStyle = 2; [button setTitle:title]; [button setTarget:self]; [button setTag:_buttonCount]; - [button setAction:@selector(_notifyDelegate:)]; + [button setAction:@selector(_dismissAlert:)]; [[_alertPanel contentView] addSubview:button]; @@ -308,12 +299,9 @@ CPCriticalAlertStyle = 2; [button setKeyEquivalent:nil]; [_buttons insertObject:button atIndex:0]; - - // For reference: does not actually work since this 'view' is not in the hierarchy. - // [self setNeedsLayout]; } -- (void)layoutSubviews +- (void)layoutPanel { if (!_alertPanel) [self _createPanel]; @@ -415,18 +403,68 @@ CPCriticalAlertStyle = 2; */ - (void)runModal { - [self layoutSubviews]; + [self layoutPanel]; [CPApp runModalForWindow:_alertPanel]; } -/* @ignore */ -- (void)_notifyDelegate:(id)button -{ - [CPApp abortModal]; - [_alertPanel close]; +/*! + Runs the receiver modally as an alert sheet attached to a specified window. - if (_delegate && [_delegate respondsToSelector:@selector(alertDidEnd:returnCode:)]) - [_delegate alertDidEnd:self returnCode:[button tag]]; + @param window The parent window for the sheet. + @param modalDelegate The delegate for the modal-dialog session. + @param alertDidEndSelector Message the alert sends to modalDelegate after the sheet is dismissed. + @param contextInfo Contextual data passed to modalDelegate in didEndSelector message. +*/ +- (void)beginSheetModalForWindow:(CPWindow)window modalDelegate:(id)modalDelegate didEndSelector:(SEL)alertDidEndSelector contextInfo:(void)contextInfo +{ + if (!(_windowStyle & CPDocModalWindowMask)) + [self setWindowStyle:CPDocModalWindowMask]; + [self layoutPanel]; + + _didEndSelector = alertDidEndSelector; + _modalDelegate = modalDelegate; + + [CPApp beginSheet:_alertPanel modalForWindow:window modalDelegate:self didEndSelector:@selector(_alertDidEnd:returnCode:contextInfo:) contextInfo:contextInfo]; +} + +/*! + Runs the receiver modally as an alert sheet attached to a specified window. + + @param window The parent window for the sheet. +*/ +- (void)beginSheetModalForWindow:(CPWindow)window +{ + if (!(_windowStyle & CPDocModalWindowMask)) + [self setWindowStyle:CPDocModalWindowMask]; + [self layoutPanel]; + + [CPApp beginSheet:_alertPanel modalForWindow:window modalDelegate:self didEndSelector:@selector(_alertDidEnd:returnCode:contextInfo:) contextInfo:nil]; +} + +- (void)_alertDidEnd:(CPWindow)aSheet returnCode:(CPInteger)returnCode contextInfo:(id)contextInfo +{ + if ([_delegate respondsToSelector:@selector(alertDidEnd:returnCode:)]) + [_delegate alertDidEnd:self returnCode:returnCode]; + + if (_didEndSelector) + objj_msgSend(_modalDelegate, _didEndSelector, self, returnCode, contextInfo); + + _didEndSelector = nil; + _modalDelegate = nil; +} + +/* @ignore */ +- (void)_dismissAlert:(CPButton)button +{ + if ([_alertPanel isSheet]) + [CPApp endSheet:_alertPanel returnCode:[button tag]]; + else + { + [CPApp abortModal]; + [_alertPanel close]; + + [self _alertDidEnd:nil returnCode:[button tag] contextInfo:nil]; + } } @end diff --git a/AppKit/CPArrayController.j b/AppKit/CPArrayController.j index b04f4e0f9..68949bcd6 100644 --- a/AppKit/CPArrayController.j +++ b/AppKit/CPArrayController.j @@ -58,22 +58,30 @@ + (CPSet)keyPathsForValuesAffectingArrangedObjects { - return [CPSet setWithObjects:"content", "contentArray", "contentSet", "filterPredicate", "sortDescriptors"]; + return [CPSet setWithObjects:"content", "filterPredicate", "sortDescriptors"]; } + (CPSet)keyPathsForValuesAffectingSelection { - return [CPSet setWithObjects:"content", "contentArray", "contentSet", "selectionIndexes"]; + return [CPSet setWithObjects:"selectionIndexes"]; } + (CPSet)keyPathsForValuesAffectingSelectionIndex { - return [CPSet setWithObjects:"content", "contentArray", "contentSet", "selectionIndexes", "selection"]; + return [CPSet setWithObjects:"selectionIndexes"]; +} + ++ (CPSet)keyPathsForValuesAffectingSelectionIndexes +{ + // When the arranged objects change, selection preservation may cause the indexes + // to change. + return [CPSet setWithObjects:"arrangedObjects"]; } + (CPSet)keyPathsForValuesAffectingSelectedObjects { - return [CPSet setWithObjects:"content", "contentArray", "contentSet", "selectionIndexes", "selection"]; + // Don't need to depend on arrangedObjects here because selectionIndexes already does. + return [CPSet setWithObjects:"selectionIndexes"]; } + (CPSet)keyPathsForValuesAffectingCanRemove @@ -91,16 +99,6 @@ return [CPSet setWithObjects:"selectionIndexes"]; } -+ (BOOL)automaticallyNotifiesObserversForKey:(CPString)aKey -{ - if (![super automaticallyNotifiesObserversForKey:aKey]) - return NO; - if (aKey === @"selectionIndexes") - return NO; - - return YES; -} - - (id)init { self = [super init]; @@ -153,31 +151,50 @@ if(![value isKindOfClass:[CPArray class]]) value = [value]; - var oldSelection = nil, - oldSelectionIndexes = [self selectionIndexes]; + var oldSelectedObjects = nil, + oldSelectionIndexes = nil; if ([self preservesSelection]) - oldSelection = [self selectedObjects]; + oldSelectedObjects = [self selectedObjects]; + else + oldSelectionIndexes = [self selectionIndexes]; - // Avoid out of bounds selections. - _selectionIndexes = [CPIndexSet indexSet]; - //FIXME: copy? - [super setContent:value]; + /* + When the contents are changed, the selected indexes may no longer refer to the + same items. This would cause problems when setSelectedObjects is called below. + Any KVO observation would try to retrieve the 'before' value which could be + wrong or even throw an exception for no longer existing indexes. + + To avoid that, use the internal __setSelectedObjects which fires no notifications. + The selectionIndexes notifications will fire later since they depend on the + content key. This pattern is also applied for many other methods throughout this + class. + */ + + if (_clearsFilterPredicateOnInsertion) + [self willChangeValueForKey:@"filterPredicate"]; + + // Don't use [super setContent:] as that would fire the contentObject change. + // We need to be in control of when notifications fire. + _contentObject = value; if(_clearsFilterPredicateOnInsertion) - [self setFilterPredicate:nil]; - - [self rearrangeObjects]; - - if (oldSelection) - [self setSelectedObjects:oldSelection]; + [self __setFilterPredicate:nil]; // Causes a _rearrangeObjects. else - [self setSelectionIndexes:oldSelectionIndexes]; + [self _rearrangeObjects]; + + if ([self preservesSelection]) + [self __setSelectedObjects:oldSelectedObjects]; + else + [self __setSelectionIndexes:oldSelectionIndexes]; + + if (_clearsFilterPredicateOnInsertion) + [self didChangeValueForKey:@"filterPredicate"]; } - (void)_setContentArray:(id)anArray { - [self setContent:anArray]; + [self setContent:anArray]; } - (void)_setContentSet:(id)aSet @@ -216,30 +233,38 @@ - (void)rearrangeObjects { - // Rearranging reapplies the selection criteria and may cause objects to disappear, - // so take care of the selection. - // - // Sometimes rearrangeObjects is called by setContent which may cause two rounds of - // selection preservation. This is okay because setContent temporarily clears the - // selection and so this code below ends up preserving nothing in that case. - var oldSelection = nil, - oldSelectionIndexes = [[self selectionIndexes] copy]; - - if ([self preservesSelection]) - oldSelection = [self selectedObjects]; - - // Avoid out of bounds selections. - _selectionIndexes = [CPIndexSet indexSet]; - - [self _setArrangedObjects:[self arrangeObjects:[self contentArray]]]; - - if (oldSelection) - [self setSelectedObjects:oldSelection]; - else - [self setSelectionIndexes:oldSelectionIndexes]; + [self willChangeValueForKey:@"arrangedObjects"]; + [self _rearrangeObjects]; + [self didChangeValueForKey:@"arrangedObjects"]; } -- (void)_setArrangedObjects:(id)value +/* + Like rearrangeObjects but don't fire any change notifications. + @ignore +*/ +- (void)_rearrangeObjects +{ + /* + Rearranging reapplies the selection criteria and may cause objects to disappear, + so take care of the selection. + */ + var oldSelectedObjects = nil, + oldSelectionIndexes = nil; + + if ([self preservesSelection]) + oldSelectedObjects = [self selectedObjects]; + else + oldSelectionIndexes = [self selectionIndexes]; + + [self __setArrangedObjects:[self arrangeObjects:[self contentArray]]]; + + if ([self preservesSelection]) + [self __setSelectedObjects:oldSelectedObjects]; + else + [self __setSelectionIndexes:oldSelectionIndexes]; +} + +- (void)__setArrangedObjects:(id)value { if (_arrangedObjects === value) return; @@ -252,7 +277,6 @@ return _arrangedObjects; } - - (CPArray)sortDescriptors { return _sortDescriptors; @@ -264,7 +288,9 @@ return; _sortDescriptors = [value copy]; - [self rearrangeObjects]; + // Use the non-notification version since arrangedObjects already depends + // on sortDescriptors. + [self _rearrangeObjects]; } - (CPPredicate)filterPredicate @@ -273,12 +299,23 @@ } - (void)setFilterPredicate:(CPPredicate)value +{ + [self __setFilterPredicate:value]; +} + +/* + Like setFilterPredicate but don't fire any change notifications. + @ignore +*/ +- (void)__setFilterPredicate:(CPPredicate)value { if (_filterPredicate === value) return; _filterPredicate = value; - [self rearrangeObjects]; + // Use the non-notification version since arrangedObjects already depends + // on filterPredicate. + [self _rearrangeObjects]; } - (BOOL)alwaysUsesMultipleValuesMarker @@ -305,6 +342,27 @@ - (BOOL)setSelectionIndexes:(CPIndexSet)indexes { + [self __setSelectionIndexes:indexes]; +} + +/* + Like setSelectionIndex but don't fire any change notifications. + @ignore +*/ +- (BOOL)__setSelectionIndex:(int)theIndex +{ + [self __setSelectionIndexes:[CPIndexSet indexSetWithIndex:theIndex]]; +} + +/* + Like setSelectionIndexes but don't fire any change notifications. + @ignore +*/ +- (BOOL)__setSelectionIndexes:(CPIndexSet)indexes +{ + if (!indexes) + indexes = [CPIndexSet indexSet]; + if ([_selectionIndexes isEqualToIndexSet:indexes]) return NO; @@ -323,14 +381,8 @@ indexes = [CPIndexSet indexSetWithIndex:objectsCount-1]; } - [self willChangeValueForKey:@"selectionIndexes"]; - [self _selectionWillChange]; - _selectionIndexes = [indexes copy]; - [self _selectionDidChange]; - [self didChangeValueForKey:@"selectionIndexes"]; - // 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. [[CPKeyValueBinding getBinding:@"selectionIndexes" forObject:self] reverseSetValueFor:@"selectionIndexes"]; @@ -346,6 +398,21 @@ } - (BOOL)setSelectedObjects:(CPArray)objects +{ + [self willChangeValueForKey:@"selectionIndexes"]; + [self _selectionWillChange]; + + [self __setSelectedObjects:objects]; + + [self didChangeValueForKey:@"selectionIndexes"]; + [self _selectionDidChange]; +} + +/* + Like setSelectedObjects but don't fire any change notifications. + @ignore +*/ +- (BOOL)__setSelectedObjects:(CPArray)objects { var set = [CPIndexSet indexSet], count = [objects count], @@ -359,7 +426,7 @@ [set addIndex:index]; } - [self setSelectionIndexes:set]; + [self __setSelectionIndexes:set]; return YES; } @@ -398,30 +465,32 @@ if (![self canAdd]) return; + if (_clearsFilterPredicateOnInsertion) + [self willChangeValueForKey:@"filterPredicate"]; + [self willChangeValueForKey:@"content"]; [_contentObject addObject:object]; - [self didChangeValueForKey:@"content"]; if (_clearsFilterPredicateOnInsertion) - [self setFilterPredicate:nil]; + [self __setFilterPredicate:nil]; if (_filterPredicate === nil || [_filterPredicate evaluateWithObject:object]) { var pos = [_arrangedObjects insertObject:object inArraySortedByDescriptors:_sortDescriptors]; + // selectionIndexes change notification will be fired as a result of the + // content change. Don't fire manually. if (_selectsInsertedObjects) - { - [self setSelectionIndex:pos]; - } + [self __setSelectionIndex:pos]; else - { - [self willChangeValueForKey:@"selectionIndexes"]; [_selectionIndexes shiftIndexesStartingAtIndex:pos by:1]; - [self didChangeValueForKey:@"selectionIndexes"]; - } } else - [self rearrangeObjects]; + [self _rearrangeObjects]; + + [self didChangeValueForKey:@"content"]; + if (_clearsFilterPredicateOnInsertion) + [self didChangeValueForKey:@"filterPredicate"]; } - (void)insertObject:(id)anObject atArrangedObjectIndex:(int)anIndex @@ -429,26 +498,30 @@ if (![self canAdd]) return; + if (_clearsFilterPredicateOnInsertion) + [self willChangeValueForKey:@"filterPredicate"]; + [self willChangeValueForKey:@"content"]; [_contentObject insertObject:anObject atIndex:anIndex]; - [self didChangeValueForKey:@"content"]; if (_clearsFilterPredicateOnInsertion) - [self setFilterPredicate:nil]; + [self __setFilterPredicate:nil]; [[self arrangedObjects] insertObject:anObject atIndex:anIndex]; + // selectionIndexes change notification will be fired as a result of the + // content change. Don't fire manually. if ([self selectsInsertedObjects]) - [self setSelectionIndex:anIndex]; + [self __setSelectionIndex:anIndex]; else - { - [self willChangeValueForKey:@"selectionIndexes"] [[self selectionIndexes] shiftIndexesStartingAtIndex:anIndex by:1]; - [self didChangeValueForKey:@"selectionIndexes"]; - } if ([self avoidsEmptySelection] && [[self selectionIndexes] count] <= 0 && [_contentObject count] > 0) - [self setSelectionIndexes:[CPIndexSet indexSetWithIndex:0]]; + [self __setSelectionIndexes:[CPIndexSet indexSetWithIndex:0]]; + + [self didChangeValueForKey:@"content"]; + if (_clearsFilterPredicateOnInsertion) + [self didChangeValueForKey:@"filterPredicate"]; } - (void)removeObject:(id)object @@ -458,18 +531,18 @@ [self willChangeValueForKey:@"content"]; [_contentObject removeObject:object]; - [self didChangeValueForKey:@"content"]; if (_filterPredicate === nil || [_filterPredicate evaluateWithObject:object]) { - [self willChangeValueForKey:@"selectionIndexes"]; + // selectionIndexes change notification will be fired as a result of the + // content change. Don't fire manually. var pos = [_arrangedObjects indexOfObject:object]; [_arrangedObjects removeObjectAtIndex:pos]; [_selectionIndexes shiftIndexesStartingAtIndex:pos by:-1]; - - [self didChangeValueForKey:@"selectionIndexes"]; } + + [self didChangeValueForKey:@"content"]; } -(void)add:(id)sender @@ -527,7 +600,6 @@ { [self willChangeValueForKey:@"content"]; [_contentObject removeObjectsInArray:objects]; - [self didChangeValueForKey:@"content"]; var arrangedObjects = [self arrangedObjects], position = [arrangedObjects indexOfObject:[objects objectAtIndex:0]]; @@ -550,9 +622,9 @@ selectionIndexes = [CPIndexSet indexSetWithIndex:objectsCount - 1]; } - [self willChangeValueForKey:@"selectionIndexes"]; _selectionIndexes = selectionIndexes; - [self didChangeValueForKey:@"selectionIndexes"]; + + [self didChangeValueForKey:@"content"]; } - (BOOL)canInsert diff --git a/AppKit/CPButton.j b/AppKit/CPButton.j index 9cbc33b20..291b1086d 100644 --- a/AppKit/CPButton.j +++ b/AppKit/CPButton.j @@ -89,9 +89,6 @@ CPButtonStateMixed = CPThemeState("mixed"); CPString _title; CPString _alternateTitle; - CPImage _image; - CPImage _alternateImage; - CPInteger _showsStateBy; CPInteger _highlightsBy; BOOL _imageDimsWhenDisabled; @@ -127,8 +124,8 @@ CPButtonStateMixed = CPThemeState("mixed"); + (id)themeAttributes { - return [CPDictionary dictionaryWithObjects:[_CGInsetMakeZero(), _CGInsetMakeZero(), [CPNull null]] - forKeys:[@"bezel-inset", @"content-inset", @"bezel-color"]]; + return [CPDictionary dictionaryWithObjects:[[CPNull null], 0.0, _CGInsetMakeZero(), _CGInsetMakeZero(), [CPNull null]] + forKeys:[@"image", @"imageOffset", @"bezel-inset", @"content-inset", @"bezel-color"]]; } - (id)initWithFrame:(CGRect)aFrame @@ -284,18 +281,12 @@ CPButtonStateMixed = CPThemeState("mixed"); - (void)setImage:(CPImage)anImage { - if (_image === anImage) - return; - - _image = anImage; - - [self setNeedsLayout]; - [self setNeedsDisplay:YES]; + [self setValue:anImage forThemeAttribute:@"image"]; } - (CPImage)image { - return _image; + return [self valueForThemeAttribute:@"image" inState:CPThemeStateNormal]; } /*! @@ -304,13 +295,7 @@ CPButtonStateMixed = CPThemeState("mixed"); */ - (void)setAlternateImage:(CPImage)anImage { - if (_alternateImage === anImage) - return; - - _alternateImage = anImage; - - [self setNeedsLayout]; - [self setNeedsDisplay:YES]; + [self setValue:anImage forThemeAttribute:@"image" inState:CPThemeStateHighlighted]; } /*! @@ -318,7 +303,17 @@ CPButtonStateMixed = CPThemeState("mixed"); */ - (CPImage)alternateImage { - return _alternateImage; + return [self valueForThemeAttribute:@"image" inState:CPThemeStateHighlighted]; +} + +- (void)setImageOffset:(float)theImageOffset +{ + [self setValue:theImageOffset forThemeAttribute:@"imageOffset"]; +} + +- (float)imageOffset +{ + return [self valueForThemeAttribute:@"imageOffset"]; } - (void)setShowsStateBy:(CPInteger)aMask @@ -525,7 +520,8 @@ CPButtonStateMixed = CPThemeState("mixed"); if (contentView) { [contentView setText:([self hasThemeState:CPThemeStateHighlighted] && _alternateTitle) ? _alternateTitle : _title]; - [contentView setImage:([self hasThemeState:CPThemeStateHighlighted] && _alternateImage) ? _alternateImage : _image]; + [contentView setImage:[self currentValueForThemeAttribute:@"image"]]; + [contentView setImageOffset:[self currentValueForThemeAttribute:@"imageOffset"]]; [contentView setFont:[self currentValueForThemeAttribute:@"font"]]; [contentView setTextColor:[self currentValueForThemeAttribute:@"text-color"]]; @@ -692,8 +688,8 @@ var CPButtonImageKey = @"CPButtonImageKey", { [super encodeWithCoder:aCoder]; - [aCoder encodeObject:_image forKey:CPButtonImageKey]; - [aCoder encodeObject:_alternateImage forKey:CPButtonAlternateImageKey]; + [aCoder encodeObject:[self image] forKey:CPButtonImageKey]; + [aCoder encodeObject:[self alternateImage] forKey:CPButtonAlternateImageKey]; [aCoder encodeObject:_title forKey:CPButtonTitleKey]; [aCoder encodeObject:_alternateTitle forKey:CPButtonAlternateTitleKey]; diff --git a/AppKit/CPObjectController.j b/AppKit/CPObjectController.j index f070d33bf..fe12d1224 100644 --- a/AppKit/CPObjectController.j +++ b/AppKit/CPObjectController.j @@ -51,15 +51,20 @@ return [CPSet setWithObjects:"editable", "selection"]; } +- (id)init +{ + return [self initWithContent:nil]; +} + - (id)initWithContent:(id)aContent { - self = [super init]; - - if (self) + if (self = [super init]) { [self setContent:aContent]; [self setEditable:YES]; [self setObjectClass:[CPMutableDictionary class]]; + + _observedKeys = [[CPCountedSet alloc] init]; } return self; @@ -455,6 +460,11 @@ var CPObjectControllerObjectClassNameKey = @"CPObjectControllerOb [super removeObjectAtIndex:anIndex]; } +- (_CPObservableArray)objectsAtIndexes:(CPIndexSet)theIndexes +{ + return [_CPObservableArray arrayWithArray:[super objectsAtIndexes:theIndexes]]; +} + - (void)addObject:(id)anObject { [self insertObject:anObject atIndex:[self count]]; diff --git a/AppKit/CPTableView.j b/AppKit/CPTableView.j index d5d07e12d..41276856f 100644 --- a/AppKit/CPTableView.j +++ b/AppKit/CPTableView.j @@ -882,7 +882,10 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5; - (void)_setSelectedRowIndexes:(CPIndexSet)rows { - var previousSelectedIndexes = [_selectedRowIndexes copy]; + if ([_selectedRowIndexes isEqualToIndexSet:rows]) + return; + + var previousSelectedIndexes = _selectedRowIndexes; _lastSelectedRow = ([rows count] > 0) ? [rows lastIndex] : -1; _selectedRowIndexes = [rows copy]; @@ -3547,7 +3550,7 @@ var CPTableViewDataSourceKey = @"CPTableViewDataSourceKey", _intercellSpacing = [aCoder decodeSizeForKey:CPTableViewIntercellSpacingKey] || _CGSizeMake(3.0, 2.0); - [self setGridColor:[aCoder decodeObjectForKey:CPTableViewGridColorKey] || [CPColor grayColor]]; + [self setGridColor:[aCoder decodeObjectForKey:CPTableViewGridColorKey]]; _gridStyleMask = [aCoder decodeIntForKey:CPTableViewGridStyleMaskKey] || CPTableViewGridNone; _usesAlternatingRowBackgroundColors = [aCoder decodeObjectForKey:CPTableViewUsesAlternatingBackgroundKey]; diff --git a/AppKit/CPTextField.j b/AppKit/CPTextField.j index a0eb82303..3a82d8ca0 100644 --- a/AppKit/CPTextField.j +++ b/AppKit/CPTextField.j @@ -278,6 +278,10 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder"); return; _isEditable = shouldBeEditable; + + if(shouldBeEditable) + _isSelectable = YES; + // We only allow first responder status if the field is editable and enabled. if (!shouldBeEditable && [[self window] firstResponder] === self) [[self window] makeFirstResponder:nil]; diff --git a/AppKit/CPWindow/CPWindow.j b/AppKit/CPWindow/CPWindow.j index 70e7e9128..5130b5ebb 100644 --- a/AppKit/CPWindow/CPWindow.j +++ b/AppKit/CPWindow/CPWindow.j @@ -200,6 +200,8 @@ var SHADOW_MARGIN_LEFT = 20.0, var CPWindowSaveImage = nil, CPWindowSavingImage = nil; +var CPWindowResizeTime = 0.2; + /*! @ingroup appkit @class CPWindow @@ -2098,6 +2100,11 @@ CPTexturedBackgroundWindowMask [_frameAnimation startAnimation]; } +- (CPTimeInterval)animationResizeTime:(CGRect)newWindowFrame +{ + return CPWindowResizeTime; +} + /* @ignore */ - (void)_setAttachedSheetFrameOrigin { @@ -2146,7 +2153,7 @@ CPTexturedBackgroundWindowMask [aSheet setFrame:startFrame display:YES animate:NO]; _sheetContext["opened"] = YES; - [aSheet _setFrame:endFrame delegate:self duration:0.2 curve:CPAnimationEaseOut]; + [aSheet _setFrame:endFrame delegate:self duration:[self animationResizeTime:endFrame] curve:CPAnimationEaseOut]; // Should run the main loop here until _isAnimating = FALSE [aSheet becomeKeyWindow]; @@ -2167,7 +2174,7 @@ CPTexturedBackgroundWindowMask [self _setUpMasksForView:sheetContent]; _sheetContext["opened"] = NO; - [sheet _setFrame:endFrame delegate:self duration:0.2 curve:CPAnimationEaseIn]; + [sheet _setFrame:endFrame delegate:self duration:[self animationResizeTime:endFrame] curve:CPAnimationEaseIn]; } /* @ignore */ @@ -2196,13 +2203,15 @@ CPTexturedBackgroundWindowMask [self _restoreMasksForView:sheetContent]; var delegate = _sheetContext["modalDelegate"], - endSelector = _sheetContext["endSelector"]; - - if (delegate != nil && endSelector != nil) - objj_msgSend(delegate, endSelector, sheet, _sheetContext["returnCode"], _sheetContext["contextInfo"]); + endSelector = _sheetContext["endSelector"], + returnCode = _sheetContext["returnCode"], + contextInfo = _sheetContext["contextInfo"]; _sheetContext = nil; sheet._parentView = nil; + + if (delegate != nil && endSelector != nil) + objj_msgSend(delegate, endSelector, sheet, returnCode, contextInfo); } - (void)_setUpMasksForView:(CPView)aView @@ -2675,7 +2684,7 @@ var interpolate = function(fromValue, toValue, progress) - (id)initWithWindow:(CPWindow)aWindow targetFrame:(CGRect)aTargetFrame { - self = [super initWithDuration:0.2 animationCurve:CPAnimationLinear]; + self = [super initWithDuration:[aWindow animationResizeTime:aTargetFrame] animationCurve:CPAnimationLinear]; if (self) { diff --git a/AppKit/Themes/Aristo/ThemeDescriptors.j b/AppKit/Themes/Aristo/ThemeDescriptors.j index 6d75b481a..93095fb66 100755 --- a/AppKit/Themes/Aristo/ThemeDescriptors.j +++ b/AppKit/Themes/Aristo/ThemeDescriptors.j @@ -324,7 +324,7 @@ var themedButtonValues = nil, + (CPArray)themeShowcaseExcludes { - return ["alert", "cornerview", "tableView", "tableDataView"]; + return ["alert", "cornerview", "columnHeader", "tableView", "tableHeaderRow", "tableDataView"]; } + (CPButton)makeButton @@ -410,7 +410,9 @@ var themedButtonValues = nil, [@"bezel-color", defaultHighlightedBezelColor, CPThemeStateBordered | CPThemeStateHighlighted | CPThemeStateDefault], [@"min-size", CGSizeMake(0.0, 24.0)], - [@"max-size", CGSizeMake(-1.0, 24.0)] + [@"max-size", CGSizeMake(-1.0, 24.0)], + + [@"imageOffset", 3.0] ]; [self registerThemeValues:themedButtonValues forView:button]; @@ -1292,8 +1294,8 @@ var themedButtonValues = nil, var tableview = [[CPTableView alloc] initWithFrame:CGRectMake(0.0, 0.0, 150.0, 150.0)], - sortImage = PatternColor("tableview-headerview-ascending.png", 9.0, 8.0), - sortImageReversed = PatternColor("tableview-headerview-descending.png", 9.0, 8.0), + sortImage = PatternImage("tableview-headerview-ascending.png", 9.0, 8.0), + sortImageReversed = PatternImage("tableview-headerview-descending.png", 9.0, 8.0), alternatingRowColors = [[CPColor whiteColor], [CPColor colorWithRed:245.0 / 255.0 green:249.0 / 255.0 blue:252.0 / 255.0 alpha:1.0]], gridColor = [CPColor colorWithHexString:@"dce0e2"], selectionColor = [CPColor colorWithHexString:@"5f83b9"], diff --git a/AppKit/_CPImageAndTextView.j b/AppKit/_CPImageAndTextView.j index 79a65f2ac..dde3311f5 100644 --- a/AppKit/_CPImageAndTextView.j +++ b/AppKit/_CPImageAndTextView.j @@ -45,9 +45,6 @@ var _CPimageAndTextViewFrameSizeChangedFlag = 1 << 0, _CPImageAndTextViewImagePositionChangedFlag = 1 << 9, _CPImageAndTextViewImageScalingChangedFlag = 1 << 10; -var HORIZONTAL_MARGIN = 3.0, - VERTICAL_MARGIN = 5.0; - /* @ignore */ @implementation _CPImageAndTextView : CPView { @@ -63,6 +60,7 @@ var HORIZONTAL_MARGIN = 3.0, CPCellImagePosition _imagePosition; CPImageScaling _imageScaling; + float _imageOffset; BOOL _shouldDimImage; CPImage _image; @@ -97,6 +95,7 @@ var HORIZONTAL_MARGIN = 3.0, [self setFont:[aControl font]]; [self setImagePosition:[aControl imagePosition]]; [self setImageScaling:[aControl imageScaling]]; + [self setImageOffset:[aControl imageOffset]]; } else { @@ -305,6 +304,20 @@ var HORIZONTAL_MARGIN = 3.0, [self setNeedsLayout]; } +- (void)setImageOffset:(float)theImageOffset +{ + if (_imageOffset === theImageOffset) + return; + + _imageOffset = theImageOffset; + [self setNeedsLayout]; +} + +- (float)imageOffset +{ + return _imageOffset; +} + - (void)imageDidLoad:(id)anImage { if (anImage === _image) @@ -599,29 +612,29 @@ var HORIZONTAL_MARGIN = 3.0, imageStyle.left = FLOOR(centerX - imageWidth / 2.0) + "px"; imageStyle.top = FLOOR(size.height - imageHeight) + "px"; - textRect.size.height = size.height - imageHeight - VERTICAL_MARGIN; + textRect.size.height = size.height - imageHeight - _imageOffset; } else if (_imagePosition === CPImageAbove) { CPDOMDisplayServerSetStyleLeftTop(_DOMImageElement, NULL, FLOOR(centerX - imageWidth / 2.0), 0); - textRect.origin.y += imageHeight + VERTICAL_MARGIN; - textRect.size.height = size.height - imageHeight - VERTICAL_MARGIN; + textRect.origin.y += imageHeight + _imageOffset; + textRect.size.height = size.height - imageHeight - _imageOffset; } else if (_imagePosition === CPImageLeft) { imageStyle.top = FLOOR(centerY - imageHeight / 2.0) + "px"; imageStyle.left = "0px"; - textRect.origin.x = imageWidth + HORIZONTAL_MARGIN; - textRect.size.width -= imageWidth + HORIZONTAL_MARGIN; + textRect.origin.x = imageWidth + _imageOffset; + textRect.size.width -= imageWidth + _imageOffset; } else if (_imagePosition === CPImageRight) { imageStyle.top = FLOOR(centerY - imageHeight / 2.0) + "px"; imageStyle.left = FLOOR(size.width - imageWidth) + "px"; - textRect.size.width -= imageWidth + HORIZONTAL_MARGIN; + textRect.size.width -= imageWidth + _imageOffset; } else if (_imagePosition === CPImageOnly) { @@ -701,13 +714,13 @@ var HORIZONTAL_MARGIN = 3.0, if (_imagePosition === CPImageLeft || _imagePosition === CPImageRight) { - size.width += _textSize.width + HORIZONTAL_MARGIN; + size.width += _textSize.width + _imageOffset; size.height = MAX(size.height, _textSize.height); } else if (_imagePosition === CPImageAbove || _imagePosition === CPImageBelow) { size.width = MAX(size.width, _textSize.width); - size.height += _textSize.height + VERTICAL_MARGIN; + size.height += _textSize.height + _imageOffset; } else // if (_imagePosition == CPImageOverlaps) { diff --git a/Foundation/CPArray+KVO.j b/Foundation/CPArray+KVO.j index 49ff5d621..2d51447a6 100644 --- a/Foundation/CPArray+KVO.j +++ b/Foundation/CPArray+KVO.j @@ -120,7 +120,7 @@ _insertManySEL = sel_getName(@"insertObjects:in"+capitalizedKey+"AtIndexes:"); if ([_proxyObject respondsToSelector:_insertManySEL]) - _insert = [_proxyObject methodForSelector:_insertManySEL]; + _insertMany = [_proxyObject methodForSelector:_insertManySEL]; _removeManySEL = sel_getName(@"removeObjectsFrom"+capitalizedKey+"AtIndexes:"); if ([_proxyObject respondsToSelector:_removeManySEL]) @@ -234,13 +234,7 @@ - (void)addObject:(id)anObject { - if (_insert) - return _insert(_proxyObject, _insertSEL, anObject, [self count]); - - var target = [[self _representedObject] copy]; - - [target addObject:anObject]; - [self _setRepresentedObject:target]; + [self insertObject:anObject atIndex:[self count]]; } - (void)addObjectsFromArray:(CPArray)anArray @@ -248,19 +242,38 @@ var index = 0, count = [anArray count]; - for (; index < count; ++index) - [self addObject:[anArray objectAtIndex:index]]; + [self insertObjects:anArray atIndexes:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange([self count], count)]]; } - (void)insertObject:(id)anObject atIndex:(unsigned)anIndex { - if (_insert) - return _insert(_proxyObject, _insertSEL, anObject, anIndex); + [self insertObjects:[anObject] atIndexes:[CPIndexSet indexSetWithIndex:anIndex]]; +} - var target = [[self _representedObject] copy]; +- (void)insertObjects:(CPArray)theObjects atIndexes:(CPIndexSet)theIndexes +{ + if (_insertMany) + _insertMany(_proxyObject, _insertManySEL, theObjects, theIndexes); + else if (_insert) + { + var indexesArray = []; + [theIndexes getIndexes:indexesArray maxCount:-1 inIndexRange:nil]; - [target insertObject:anObject atIndex:anIndex]; - [self _setRepresentedObject:target]; + for (var index = 0; index < [indexesArray count]; index++) + { + var objectIndex = [indexesArray objectAtIndex:index], + object = [theObjects objectAtIndex:index]; + + _insert(_proxyObject, _insertSEL, object, objectIndex); + } + } + else + { + var target = [[self _representedObject] copy]; + + [target insertObjects:theObjects atIndexes:theIndexes]; + [self _setRepresentedObject:target]; + } } - (void)removeObject:(id)anObject diff --git a/Foundation/CPKeyValueObserving.j b/Foundation/CPKeyValueObserving.j index 6ea9c9a42..a08100481 100644 --- a/Foundation/CPKeyValueObserving.j +++ b/Foundation/CPKeyValueObserving.j @@ -27,7 +27,6 @@ @import "CPObject.j" @import "CPSet.j" - @implementation CPObject (KeyValueObserving) - (void)willChangeValueForKey:(CPString)aKey @@ -394,6 +393,7 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew|CPKeyValueObservingOptionOld, - (void)_sendNotificationsForKey:(CPString)aKey changeOptions:(CPDictionary)changeOptions isBefore:(BOOL)isBefore { + // CPLog.warn("_sendNotificationsForKey: " + aKey + " ...isBefore: " + isBefore); var changes = _changesForKey[aKey]; if (isBefore) @@ -492,6 +492,9 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew|CPKeyValueObservingOptionOld, { var keyPath = dependentKeyPaths[index]; + // CPLog.warn("firing dependepent key " + index + " for " + aKey + ": "+keyPath); + // objj_backtrace_print(CPLog.error); + [self _sendNotificationsForKey:keyPath changeOptions:isBefore ? [changeOptions copy] : _changesForKey[keyPath] isBefore:isBefore]; diff --git a/Foundation/CPPredicate/CPPredicate.j b/Foundation/CPPredicate/CPPredicate.j index 18a0c5b51..ff5f5f092 100644 --- a/Foundation/CPPredicate/CPPredicate.j +++ b/Foundation/CPPredicate/CPPredicate.j @@ -52,12 +52,12 @@ @param arguments The arguments to substitute into predicateFormat. Values are substituted into predicateFormat in the order they appear in the array. @return A new predicate by substituting the values in arguments into predicateFormat, and parsing the result. */ -+ (CPPredicate)predicateWithFormat:(CPString)format argumentArray:(CPArray)arguments ++ (CPPredicate)predicateWithFormat:(CPString)format argumentArray:(CPArray)args { if (!format) [CPException raise:CPInvalidArgumentException reason:_cmd + " the format can't be 'nil'"]; - var s = [[CPPredicateScanner alloc] initWithString:format args:arguments], + var s = [[CPPredicateScanner alloc] initWithString:format args:args], p = [s parse]; return p; diff --git a/Jakefile b/Jakefile index 9d7cdd150..83ca1c030 100644 --- a/Jakefile +++ b/Jakefile @@ -111,7 +111,7 @@ task ("documentation", function() var documentationDir = FILE.join("Tools", "Documentation"); - if (OS.system(["ruby", FILE.join(documentationDir, "make_headers")])) + if (OS.system([FILE.join(documentationDir, "make_headers.sh")])) OS.exit(1); //rake abort if ($? != 0) if (!OS.system([doxygen, FILE.join(documentationDir, "Cappuccino.doxygen")])) diff --git a/Objective-J/CFBundle.js b/Objective-J/CFBundle.js index 03639c8b0..a64ecf91d 100644 --- a/Objective-J/CFBundle.js +++ b/Objective-J/CFBundle.js @@ -344,7 +344,7 @@ function loadExecutableAndResources(/*Bundle*/ aBundle, /*BOOL*/ shouldExecute) if ((typeof CPApp === "undefined" || !CPApp || !CPApp._finishedLaunching) && typeof OBJJ_PROGRESS_CALLBACK === "function" && CPApplicationSizeInBytes) { - OBJJ_PROGRESS_CALLBACK(MAX(MIN(1.0, CFTotalBytesLoaded / CPApplicationSizeInBytes), 0.0), CPApplicationSizeInBytes, aBundle.path()) + OBJJ_PROGRESS_CALLBACK(MAX(MIN(1.0, CFTotalBytesLoaded / CPApplicationSizeInBytes), 0.0), CPApplicationSizeInBytes, aBundle.bundlePath()) } if (aBundle._loadStatus === CFBundleLoading) diff --git a/Objective-J/Preprocessor.js b/Objective-J/Preprocessor.js index d66d47144..75a4edc21 100644 --- a/Objective-J/Preprocessor.js +++ b/Objective-J/Preprocessor.js @@ -61,7 +61,7 @@ var TOKEN_ACCESSORS = "accessors", TOKEN_WHITESPACE = /^(?:(?:\s+$)|(?:\/(?:\/|\*)))/, TOKEN_NUMBER = /^[+-]?\d+(([.]\d+)*([eE][+-]?\d+))?$/, TOKEN_IDENTIFIER = /^[a-zA-Z_$](\w|$)*$/; - + #define IS_WORD(token) /^\w+$/.test(token) function Lexer(/*String*/ aString) @@ -70,7 +70,7 @@ function Lexer(/*String*/ aString) // FIXME: Used fixed regex this._tokens = (aString + '\n').match(/\/\/.*(\r|\n)?|\/\*(?:.|\n|\r)*?\*\/|\w+\b|[+-]?\d+(([.]\d+)*([eE][+-]?\d+))?|"[^"\\]*(\\[\s\S][^"\\]*)*"|'[^'\\]*(\\[\s\S][^'\\]*)*'|\s+|./g); this._context = []; - + return this; } @@ -84,17 +84,17 @@ Lexer.prototype.pop = function() this._index = this._context.pop(); } -Lexer.prototype.peak = function(shouldSkipWhitespace) +Lexer.prototype.peek = function(shouldSkipWhitespace) { if (shouldSkipWhitespace) { this.push(); var token = this.skip_whitespace(); this.pop(); - + return token; } - + return this._tokens[this._index + 1]; } @@ -112,18 +112,18 @@ Lexer.prototype.last = function() { if (this._index < 0) return NULL; - + return this._tokens[this._index - 1]; } -Lexer.prototype.skip_whitespace= function(shouldMoveBackwards) -{ +Lexer.prototype.skip_whitespace = function(shouldMoveBackwards) +{ var token; - + if (shouldMoveBackwards) - while((token = this.previous()) && TOKEN_WHITESPACE.test(token)) ; + while ((token = this.previous()) && TOKEN_WHITESPACE.test(token)) ; else - while((token = this.next()) && TOKEN_WHITESPACE.test(token)) ; + while ((token = this.next()) && TOKEN_WHITESPACE.test(token)) ; return token; } @@ -187,7 +187,7 @@ var Preprocessor = function(/*String*/ aString, /*CFURL|String*/ aURL, /*unsigne Preprocessor.prototype.setClassInfo = function(className, superClassName, ivars) { - this._classLookupTable[className] = {superClassName:superClassName, ivars:ivars}; + this._classLookupTable[className] = { superClassName:superClassName, ivars:ivars }; } Preprocessor.prototype.getClassInfo = function(className) @@ -234,7 +234,7 @@ Preprocessor.prototype.accessors = function(tokens) if (token != TOKEN_OPEN_PARENTHESIS) { tokens.previous(); - + return attributes; } @@ -249,7 +249,7 @@ Preprocessor.prototype.accessors = function(tokens) if ((token = tokens.skip_whitespace()) == TOKEN_EQUAL) { value = tokens.skip_whitespace(); - + if (!IS_WORD(value)) throw new SyntaxError(this.error_message("*** @accessors attribute value not valid.")); @@ -257,7 +257,7 @@ Preprocessor.prototype.accessors = function(tokens) { if ((token = tokens.next()) != TOKEN_COLON) throw new SyntaxError(this.error_message("*** @accessors setter attribute requires argument with \":\" at end of selector name.")); - + value += ":"; } @@ -268,30 +268,30 @@ Preprocessor.prototype.accessors = function(tokens) if (token == TOKEN_CLOSE_PARENTHESIS) break; - + if (token != TOKEN_COMMA) throw new SyntaxError(this.error_message("*** Expected ',' or ')' in @accessors attribute list.")); } - + return attributes; } Preprocessor.prototype.brackets = function(/*Lexer*/ tokens, /*StringBuffer*/ aStringBuffer) { var tuples = []; - + while (this.preprocess(tokens, NULL, NULL, NULL, tuples[tuples.length] = [])) ; if (tuples[0].length === 1) { CONCAT(aStringBuffer, '['); - + // When we have an empty array literal ([]), tuples[0][0] will be an empty StringBuffer CONCAT(aStringBuffer, tuples[0][0]); - + CONCAT(aStringBuffer, ']'); } - + else { var selector = new StringBuffer(); @@ -313,15 +313,15 @@ Preprocessor.prototype.brackets = function(/*Lexer*/ tokens, /*StringBuffer*/ aS var index = 1, count = tuples.length, marg_list = new StringBuffer(); - + for(; index < count; ++index) { var pair = tuples[index]; - - CONCAT(selector, pair[1]) + + CONCAT(selector, pair[1]); CONCAT(marg_list, ", " + pair[0]); } - + CONCAT(aStringBuffer, ", \""); CONCAT(aStringBuffer, selector); // FIXME: sel_getUid(selector + "") ? CONCAT(aStringBuffer, '\"'); @@ -335,21 +335,21 @@ Preprocessor.prototype.directive = function(tokens, aStringBuffer, allowedDirect // Grab the next token, preprocessor directives follow '@' immediately. var buffer = aStringBuffer ? aStringBuffer : new StringBuffer(), token = tokens.next(); - - // To provide compatibility with Objective-C files, we convert NSString literals into + + // To provide compatibility with Objective-C files, we convert NSString literals into // toll-freed JavaScript/CPString strings. if (token.charAt(0) == TOKEN_DOUBLE_QUOTE) CONCAT(buffer, token); - - // Currently we simply swallow forward declarations and only provide them to allow + + // Currently we simply swallow forward declarations and only provide them to allow // compatibility with Objective-C files. else if (token === TOKEN_CLASS) { tokens.skip_whitespace(); - + return; } - + // @implementation Class implementations else if (token === TOKEN_IMPLEMENTATION) this.implementation(tokens, buffer); @@ -361,7 +361,7 @@ Preprocessor.prototype.directive = function(tokens, aStringBuffer, allowedDirect // @selector else if (token === TOKEN_SELECTOR) this.selector(tokens, buffer); - + if (!aStringBuffer) return buffer; } @@ -376,7 +376,7 @@ Preprocessor.prototype.hash = function(tokens, aStringBuffer) if (token === TOKEN_PRAGMA) { token = tokens.skip_whitespace(); - + // '#pragma mark' directive is used in Xcode editor for creating labels, // which is irrelevant to Cappuccino - just swallow this line if (token === TOKEN_MARK) @@ -399,7 +399,7 @@ Preprocessor.prototype.implementation = function(tokens, /*StringBuffer*/ aStrin instance_methods = new StringBuffer(), class_methods = new StringBuffer(); - + if (!(/^\w/).test(class_name)) throw new Error(this.error_message("*** Expected class name, found \"" + class_name + "\".")); @@ -410,16 +410,16 @@ Preprocessor.prototype.implementation = function(tokens, /*StringBuffer*/ aStrin this._currentSelector = ""; // If we reach an open parenthesis, we are declaring a category. - if((token = tokens.skip_whitespace()) == TOKEN_OPEN_PARENTHESIS) + if ((token = tokens.skip_whitespace()) == TOKEN_OPEN_PARENTHESIS) { token = tokens.skip_whitespace(); - + if (token == TOKEN_CLOSE_PARENTHESIS) throw new SyntaxError(this.error_message("*** Can't Have Empty Category Name for class \"" + class_name + "\".")); - + if (tokens.skip_whitespace() != TOKEN_CLOSE_PARENTHESIS) throw new SyntaxError(this.error_message("*** Improper Category Definition for class \"" + class_name + "\".")); - + CONCAT(buffer, "{\nvar the_class = objj_getClass(\"" + class_name + "\")\n"); CONCAT(buffer, "if(!the_class) throw new SyntaxError(\"*** Could not find definition for class \\\"" + class_name + "\\\"\");\n"); CONCAT(buffer, "var meta_class = the_class.isa;"); @@ -430,17 +430,17 @@ Preprocessor.prototype.implementation = function(tokens, /*StringBuffer*/ aStrin if(token == TOKEN_COLON) { token = tokens.skip_whitespace(); - + if (!TOKEN_IDENTIFIER.test(token)) throw new SyntaxError(this.error_message("*** Expected class name, found \"" + token + "\".")); - + superclass_name = token; token = tokens.skip_whitespace(); } - + CONCAT(buffer, "{var the_class = objj_allocateClassPair(" + superclass_name + ", \"" + class_name + "\"),\nmeta_class = the_class.isa;"); - + // If we are at an opening curly brace ('{'), then we have an ivar declaration. if (token == TOKEN_OPEN_BRACE) { @@ -449,7 +449,7 @@ Preprocessor.prototype.implementation = function(tokens, /*StringBuffer*/ aStrin declaration = [], attributes, accessors = {}; - + while((token = tokens.skip_whitespace()) && token != TOKEN_CLOSE_BRACE) { if (token === TOKEN_PREPROCESSOR) @@ -470,10 +470,10 @@ Preprocessor.prototype.implementation = function(tokens, /*StringBuffer*/ aStrin var name = declaration[declaration.length - 1]; CONCAT(buffer, "new objj_ivar(\"" + name + "\")"); - + ivar_names[name] = 1; declaration = []; - + if (attributes) { accessors[name] = attributes; @@ -490,7 +490,7 @@ Preprocessor.prototype.implementation = function(tokens, /*StringBuffer*/ aStrin if (ivar_count) CONCAT(buffer, "]);\n"); - + if (!token) throw new SyntaxError(this.error_message("*** Expected '}'")); @@ -503,44 +503,44 @@ Preprocessor.prototype.implementation = function(tokens, /*StringBuffer*/ aStrin { var accessor = accessors[ivar_name], property = accessor["property"] || ivar_name; - + // getter var getterName = accessor["getter"] || property, getterCode = "(id)" + getterName + "\n{\nreturn " + ivar_name + ";\n}"; if (IS_NOT_EMPTY(instance_methods)) CONCAT(instance_methods, ",\n"); - + CONCAT(instance_methods, this.method(new Lexer(getterCode), ivar_names)); - + // setter if (accessor["readonly"]) continue; - + var setterName = accessor["setter"]; - + if (!setterName) { var start = property.charAt(0) == '_' ? 1 : 0; setterName = (start ? "_" : "") + "set" + property.substr(start, 1).toUpperCase() + property.substring(start + 1) + ":"; } - + var setterCode = "(void)" + setterName + "(id)newValue\n{\n"; - + if (accessor["copy"]) setterCode += "if (" + ivar_name + " !== newValue)\n" + ivar_name + " = [newValue copy];\n}"; else setterCode += ivar_name + " = newValue;\n}"; - + if (IS_NOT_EMPTY(instance_methods)) CONCAT(instance_methods, ",\n"); - + CONCAT(instance_methods, this.method(new Lexer(setterCode), ivar_names)); } } else tokens.previous(); - + // We must make a new class object for our class definition. CONCAT(buffer, "objj_registerClassPair(the_class);\n"); } @@ -565,7 +565,7 @@ Preprocessor.prototype.implementation = function(tokens, /*StringBuffer*/ aStrin if (IS_NOT_EMPTY(instance_methods)) CONCAT(instance_methods, ", "); - + CONCAT(instance_methods, this.method(tokens, ivar_names)); } // If we reach a # symbol, we may be at a C preprocessor directive. @@ -585,21 +585,21 @@ Preprocessor.prototype.implementation = function(tokens, /*StringBuffer*/ aStrin //else // throw new SyntaxError(this.error_message("*** Expected a method declaration, or \"@end\", found \"" + token + "\".")); } - + if (IS_NOT_EMPTY(instance_methods)) { CONCAT(buffer, "class_addMethods(the_class, ["); CONCAT(buffer, instance_methods); CONCAT(buffer, "]);\n"); } - + if (IS_NOT_EMPTY(class_methods)) { CONCAT(buffer, "class_addMethods(meta_class, ["); CONCAT(buffer, class_methods); CONCAT(buffer, "]);\n"); } - + CONCAT(buffer, '}'); this._currentClass = ""; @@ -613,16 +613,16 @@ Preprocessor.prototype._import = function(tokens) if (token === TOKEN_LESS_THAN) { - while((token = tokens.next()) && token !== TOKEN_GREATER_THAN) + while ((token = tokens.next()) && token !== TOKEN_GREATER_THAN) URLString += token; - - if(!token) + + if (!token) throw new SyntaxError(this.error_message("*** Unterminated import statement.")); } - + else if (token.charAt(0) === TOKEN_DOUBLE_QUOTE) URLString = token.substr(1, token.length - 2); - + else throw new SyntaxError(this.error_message("*** Expecting '<' or '\"', found \"" + token + "\".")); @@ -643,7 +643,7 @@ Preprocessor.prototype.method = function(/*Lexer*/ tokens, ivar_names) ivar_names = ivar_names || {}; - while((token = tokens.skip_whitespace()) && token !== TOKEN_OPEN_BRACE && token !== TOKEN_SEMICOLON) + while ((token = tokens.skip_whitespace()) && token !== TOKEN_OPEN_BRACE && token !== TOKEN_SEMICOLON) { if (token == TOKEN_COLON) { @@ -651,33 +651,33 @@ Preprocessor.prototype.method = function(/*Lexer*/ tokens, ivar_names) // Colons are part of the selector name selector += token; - + token = tokens.skip_whitespace(); - + if (token == TOKEN_OPEN_PARENTHESIS) { // Swallow parameter/return type. Perhaps later we can use this for debugging? - while((token = tokens.skip_whitespace()) && token != TOKEN_CLOSE_PARENTHESIS) + while ((token = tokens.skip_whitespace()) && token != TOKEN_CLOSE_PARENTHESIS) type += token; - + token = tokens.skip_whitespace(); } - + // Add the type. If it's empty, add null instead. - types[parameters.length+1] = type || null; + types[parameters.length + 1] = type || null; // Since this follows a colon, this must be the parameter name. parameters[parameters.length] = token; if (token in ivar_names) - throw new SyntaxError(this.error_message("*** Method ( "+selector+" ) uses a parameter name that is already in use ( "+token+" )")); + throw new SyntaxError(this.error_message("*** Method ( "+selector+" ) uses a parameter name that is already in use ( "+token+" )")); } else if (token == TOKEN_OPEN_PARENTHESIS) { var type = ""; // Since :( is handled above, this must be the return type, just swallow it. - while((token = tokens.skip_whitespace()) && token != TOKEN_CLOSE_PARENTHESIS) + while ((token = tokens.skip_whitespace()) && token != TOKEN_CLOSE_PARENTHESIS) type += token; // types[0] is the return argument @@ -709,7 +709,7 @@ Preprocessor.prototype.method = function(/*Lexer*/ tokens, ivar_names) var index = 0, count = parameters.length; - + CONCAT(buffer, "new objj_method(sel_getUid(\""); CONCAT(buffer, selector); CONCAT(buffer, "\"), function"); @@ -718,10 +718,10 @@ Preprocessor.prototype.method = function(/*Lexer*/ tokens, ivar_names) if (this._flags & Preprocessor.Flags.IncludeDebugSymbols) CONCAT(buffer, " $" + this._currentClass + "__" + selector.replace(/:/g, "_")); - + CONCAT(buffer, "(self, _cmd"); - - for(; index < count; ++index) + + for (; index < count; ++index) { CONCAT(buffer, ", "); CONCAT(buffer, parameters[index]); @@ -749,53 +749,53 @@ Preprocessor.prototype.preprocess = function(tokens, /*StringBuffer*/ aStringBuf if (tuple) { tuple[0] = buffer; - + var bracket = false, closures = [0, 0, 0]; } - + while ((token = tokens.next()) && ((token !== terminator) || count)) { if (tuple) { - // Ignore :'s the belong to tertiary operators (?:) + // Ignore :'s the belong to tertiary operators (?:) if (token === TOKEN_QUESTION_MARK) ++closures[2]; - - // Ingore anything between { } and () + + // Ingore anything between { } and () else if (token === TOKEN_OPEN_BRACE) ++closures[0]; - + else if (token === TOKEN_CLOSE_BRACE) --closures[0]; - + else if (token === TOKEN_OPEN_PARENTHESIS) ++closures[1]; - + else if (token === TOKEN_CLOSE_PARENTHESIS) --closures[1]; - + // If not in {} and not in () and this is a colon and we don't belong to a tertiary operator OR this is a closing bracket... - else if ((token === TOKEN_COLON && closures[2]-- === 0 || + else if ((token === TOKEN_COLON && closures[2]-- === 0 || (bracket = (token === TOKEN_CLOSE_BRACKET))) && closures[0] === 0 && closures[1] === 0) { tokens.push(); // 1 - - // If a bracket made us enter, go backwards skipping whitespace ([a b ] allowed), + + // If a bracket made us enter, go backwards skipping whitespace ([a b ] allowed), // if not grab token immediately behind us ([a b : c] not allowed var label = bracket ? tokens.skip_whitespace(true) : tokens.previous(), isEmptyLabel = TOKEN_WHITESPACE.test(label); - + // The label must be an identifier, and preceded by whitespace, or whitespace itself (the "empty label") if (isEmptyLabel || TOKEN_IDENTIFIER.test(label) && TOKEN_WHITESPACE.test(tokens.previous())) { tokens.push(); // 2 - + var last = tokens.skip_whitespace(true), operatorCheck = true, isDoubleOperator = false; - + // unary or binary, still disables. // + - + x is bad because it could be (+ - + x) or (a + - + x) // the only good is unbroken chain @@ -807,28 +807,28 @@ Preprocessor.prototype.preprocess = function(tokens, /*StringBuffer*/ aStringBuf last = tokens.skip_whitespace(true); isDoubleOperator = true; }} - + tokens.pop(); // 2 - + tokens.pop(); // 1 - + //alert(operatorCheck + "operatorCheck for " + label + " and " + last); if (operatorCheck && ( - + // <)>