diff --git a/AppKit/CPClipView.j b/AppKit/CPClipView.j index 8503c4cc5..93f8aacd6 100644 --- a/AppKit/CPClipView.j +++ b/AppKit/CPClipView.j @@ -46,75 +46,12 @@ return; if (_documentView) - { - [self _removeObserverDocumentView:_documentView]; [_documentView removeFromSuperview]; - } _documentView = aView; if (_documentView) - { [self addSubview:_documentView]; - [self _observeDocumentView]; - } -} - -- (void)_observeDocumentView -{ - var defaultCenter = [CPNotificationCenter defaultCenter]; - - [_documentView setPostsFrameChangedNotifications:YES]; - [_documentView setPostsBoundsChangedNotifications:YES]; - - [defaultCenter - addObserver:self - selector:@selector(viewFrameChanged:) - name:CPViewFrameDidChangeNotification - object:_documentView]; - - [defaultCenter - addObserver:self - selector:@selector(viewBoundsChanged:) - name:CPViewBoundsDidChangeNotification - object:_documentView]; -} - -- (void)_removeObserverDocumentView:(CPView)aDocumentView -{ - var defaultCenter = [CPNotificationCenter defaultCenter]; - - [defaultCenter - removeObserver:self - name:CPViewFrameDidChangeNotification - object:_documentView]; - - [defaultCenter - removeObserver:self - name:CPViewBoundsDidChangeNotification - object:_documentView]; -} - -- (void)_addObservers -{ - if (_isObserving) - return; - - [super _addObservers]; - - if (_documentView) - [self _observeDocumentView]; -} - -- (void)_removeObservers -{ - if (!_isObserving) - return; - - [super _removeObservers]; - - if (_documentView) - [self _removeObserverDocumentView:_documentView]; } /*! @@ -275,7 +212,6 @@ var CPClipViewDocumentViewKey = @"CPScrollViewDocumentView"; // Don't call setDocumentView: here. It calls addSubview:, but it's A) not necessary since the // view hierarchy is fully encoded and B) dangerous if the subview is not fully decoded. _documentView = [aCoder decodeObjectForKey:CPClipViewDocumentViewKey]; - [self _observeDocumentView]; } return self; diff --git a/AppKit/CPComboBox.j b/AppKit/CPComboBox.j index 6787948e3..f6d094eb1 100644 --- a/AppKit/CPComboBox.j +++ b/AppKit/CPComboBox.j @@ -53,6 +53,11 @@ CPComboBoxWillPopUpNotification = @"CPComboBoxWillPopUpNotification"; CPComboBoxStateButtonBordered = CPThemeState("button-bordered"); +var CPComboBoxDelegate_comboBoxSelectionIsChanging_ = 1 << 0, + CPComboBoxDelegate_comboBoxSelectionDidChange_ = 1 << 1, + CPComboBoxDelegate_comboBoxWillPopUp_ = 1 << 2, + CPComboBoxDelegate_comboBoxWillDismiss_ = 1 << 3; + var CPComboBoxTextSubview = @"text", CPComboBoxButtonSubview = @"button", CPComboBoxDefaultNumberOfVisibleItems = 5, @@ -61,19 +66,20 @@ var CPComboBoxTextSubview = @"text", @implementation CPComboBox : CPTextField { - CPArray _items; - _CPPopUpList _listDelegate; - id _dataSource; - BOOL _usesDataSource; - BOOL _completes; BOOL _canComplete; - int _numberOfVisibleItems; + BOOL _completes; BOOL _forceSelection; BOOL _hasVerticalScroller; - CPString _selectedStringValue; - CGSize _intercellSpacing; - float _itemHeight; BOOL _popUpButtonCausedResign; + BOOL _usesDataSource; + CGSize _intercellSpacing; + CPArray _items; + id _dataSource; + CPInteger _implementedDelegateComboBoxMethods; + CPString _selectedStringValue; + float _itemHeight; + int _numberOfVisibleItems; + _CPPopUpList _listDelegate; } + (CPString)defaultThemeClass @@ -228,41 +234,21 @@ var CPComboBoxTextSubview = @"text", if (aDelegate === delegate) return; - var defaultCenter = [CPNotificationCenter defaultCenter]; - - if (delegate) - { - [defaultCenter removeObserver:delegate name:CPComboBoxSelectionIsChangingNotification object:self]; - [defaultCenter removeObserver:delegate name:CPComboBoxSelectionDidChangeNotification object:self]; - [defaultCenter removeObserver:delegate name:CPComboBoxWillDismissNotification object:self]; - [defaultCenter removeObserver:delegate name:CPComboBoxWillPopUpNotification object:self]; - } + _implementedDelegateComboBoxMethods = 0; if (aDelegate) { if ([aDelegate respondsToSelector:@selector(comboBoxSelectionIsChanging:)]) - [defaultCenter addObserver:delegate - selector:@selector(comboBoxSelectionIsChanging:) - name:CPComboBoxSelectionIsChangingNotification - object:self]; + _implementedDelegateComboBoxMethods |= CPComboBoxDelegate_comboBoxSelectionIsChanging_; if ([aDelegate respondsToSelector:@selector(comboBoxSelectionDidChange:)]) - [defaultCenter addObserver:delegate - selector:@selector(comboBoxSelectionDidChange:) - name:CPComboBoxSelectionDidChangeNotification - object:self]; + _implementedDelegateComboBoxMethods |= CPComboBoxDelegate_comboBoxSelectionDidChange_; if ([aDelegate respondsToSelector:@selector(comboBoxWillPopUp:)]) - [defaultCenter addObserver:delegate - selector:@selector(comboBoxWillPopUp:) - name:CPComboBoxWillPopUpNotification - object:self]; + _implementedDelegateComboBoxMethods |= CPComboBoxDelegate_comboBoxWillPopUp_; if ([aDelegate respondsToSelector:@selector(comboBoxWillDismiss:)]) - [defaultCenter addObserver:delegate - selector:@selector(comboBoxWillDissmis:) - name:CPComboBoxWillDismissNotification - object:self]; + _implementedDelegateComboBoxMethods |= CPComboBoxDelegate_comboBoxWillDismiss_; } [super setDelegate:aDelegate]; @@ -412,49 +398,57 @@ var CPComboBoxTextSubview = @"text", if (_listDelegate === aDelegate) return; - var defaultCenter = [CPNotificationCenter defaultCenter]; - - if (_listDelegate) - { - [defaultCenter removeObserver:self name:_CPPopUpListWillPopUpNotification object:_listDelegate]; - [defaultCenter removeObserver:self name:_CPPopUpListWillDismissNotification object:_listDelegate]; - [defaultCenter removeObserver:self name:_CPPopUpListDidDismissNotification object:_listDelegate]; - [defaultCenter removeObserver:self name:_CPPopUpListItemWasClickedNotification object:_listDelegate]; - - var oldTableView = [_listDelegate tableView]; - - if (oldTableView) - { - [defaultCenter removeObserver:self name:CPTableViewSelectionIsChangingNotification object:oldTableView]; - [defaultCenter removeObserver:self name:CPTableViewSelectionDidChangeNotification object:oldTableView]; - } - } + [self _removeObserversForListDelegate:_listDelegate]; _listDelegate = aDelegate; + // We only add the observers if the CPComboBox is displayed + if ([self window]) + [self _addObserversForListDelegate:_listDelegate] + + // Apply our text style to the list + [_listDelegate setFont:[self font]]; + [_listDelegate setAlignment:[self alignment]]; + + [[_listDelegate scrollView] setHasVerticalScroller:_hasVerticalScroller]; + + if (_intercellSpacing) + [[_listDelegate tableView] setIntercellSpacing:_intercellSpacing]; + + if (_itemHeight) + [[_listDelegate tableView] setRowHeight:_itemHeight]; +} + +- (void)_addObserversForListDelegate:(_CPPopUpList)aDelegate +{ + if (!aDelegate) + return; + + var defaultCenter = [CPNotificationCenter defaultCenter]; + [defaultCenter addObserver:self selector:@selector(comboBoxWillPopUp:) name:_CPPopUpListWillPopUpNotification - object:_listDelegate]; + object:aDelegate]; [defaultCenter addObserver:self selector:@selector(comboBoxWillDismiss:) name:_CPPopUpListWillDismissNotification - object:_listDelegate]; + object:aDelegate]; [defaultCenter addObserver:self selector:@selector(listDidDismiss:) name:_CPPopUpListDidDismissNotification - object:_listDelegate]; + object:aDelegate]; [defaultCenter addObserver:self selector:@selector(itemWasClicked:) name:_CPPopUpListItemWasClickedNotification - object:_listDelegate]; + object:aDelegate]; - [[_listDelegate scrollView] setHasVerticalScroller:_hasVerticalScroller]; + [[aDelegate scrollView] setHasVerticalScroller:_hasVerticalScroller]; - var tableView = [_listDelegate tableView]; + var tableView = [aDelegate tableView]; [defaultCenter addObserver:self selector:@selector(comboBoxSelectionIsChanging:) @@ -465,13 +459,27 @@ var CPComboBoxTextSubview = @"text", selector:@selector(comboBoxSelectionDidChange:) name:CPTableViewSelectionDidChangeNotification object:tableView]; +} - // Apply our text style to the list - [_listDelegate setFont:[self font]]; - [_listDelegate setAlignment:[self alignment]]; - [[_listDelegate scrollView] setHasVerticalScroller:_hasVerticalScroller]; - [[_listDelegate tableView] setIntercellSpacing:_intercellSpacing]; - [[_listDelegate tableView] setRowHeight:_itemHeight]; +- (void)_removeObserversForListDelegate:(_CPPopUpList)aDelegate +{ + if (!aDelegate) + return; + + var defaultCenter = [CPNotificationCenter defaultCenter]; + + [defaultCenter removeObserver:self name:_CPPopUpListWillPopUpNotification object:aDelegate]; + [defaultCenter removeObserver:self name:_CPPopUpListWillDismissNotification object:aDelegate]; + [defaultCenter removeObserver:self name:_CPPopUpListDidDismissNotification object:aDelegate]; + [defaultCenter removeObserver:self name:_CPPopUpListItemWasClickedNotification object:aDelegate]; + + var oldTableView = [aDelegate tableView]; + + if (oldTableView) + { + [defaultCenter removeObserver:self name:CPTableViewSelectionIsChangingNotification object:oldTableView]; + [defaultCenter removeObserver:self name:CPTableViewSelectionDidChangeNotification object:oldTableView]; + } } - (int)indexOfItemWithObjectValue:(id)anObject @@ -516,8 +524,6 @@ var CPComboBoxTextSubview = @"text", if (!_listDelegate) [self setListDelegate:[[_CPPopUpList alloc] initWithDataSource:self]]; - [self _selectMatchingItem]; - // Note the offset here is 1 less than the focus ring width because the outer edge // of the focus ring is very transparent and it looks better if the list is closer. if (CPComboBoxFocusRingWidth < 0) @@ -528,6 +534,7 @@ var CPComboBoxTextSubview = @"text", } [_listDelegate popUpRelativeToRect:[self _borderFrame] view:self offset:CPComboBoxFocusRingWidth - 1]; + [self _selectMatchingItem]; } /*! @ignore */ @@ -998,6 +1005,28 @@ var CPComboBoxTextSubview = @"text", } } + +#pragma mark - +#pragma mark Observers method + +- (void)_addObservers +{ + if (_isObserving) + return; + + [super _addObservers]; + [self _addObserversForListDelegate:_listDelegate]; +} + +- (void)_removeObservers +{ + if (!_isObserving) + return; + + [super _removeObservers]; + [self _removeObserversForListDelegate:_listDelegate]; +} + @end @implementation CPComboBox (CPComboBoxDelegate) @@ -1005,24 +1034,36 @@ var CPComboBoxTextSubview = @"text", /*! @ignore */ - (void)comboBoxSelectionIsChanging:(CPNotification)aNotification { + if (_implementedDelegateComboBoxMethods & CPComboBoxDelegate_comboBoxSelectionIsChanging_) + [_delegate comboBoxSelectionIsChanging:[[CPNotification alloc] initWithName:CPComboBoxSelectionIsChangingNotification object:self userInfo:nil]]; + [[CPNotificationCenter defaultCenter] postNotificationName:CPComboBoxSelectionIsChangingNotification object:self]; } /*! @ignore */ - (void)comboBoxSelectionDidChange:(CPNotification)aNotification { + if (_implementedDelegateComboBoxMethods & CPComboBoxDelegate_comboBoxSelectionDidChange_) + [_delegate comboBoxSelectionDidChange:[[CPNotification alloc] initWithName:CPComboBoxSelectionDidChangeNotification object:self userInfo:nil]]; + [[CPNotificationCenter defaultCenter] postNotificationName:CPComboBoxSelectionDidChangeNotification object:self]; } /*! @ignore */ - (void)comboBoxWillPopUp:(CPNotification)aNotification { + if (_implementedDelegateComboBoxMethods & CPComboBoxDelegate_comboBoxWillPopUp_) + [_delegate comboBoxWillPopUp:[[CPNotification alloc] initWithName:CPComboBoxWillPopUpNotification object:self userInfo:nil]]; + [[CPNotificationCenter defaultCenter] postNotificationName:CPComboBoxWillPopUpNotification object:self]; } /*! @ignore */ - (void)comboBoxWillDismiss:(CPNotification)aNotification { + if (_implementedDelegateComboBoxMethods & CPComboBoxDelegate_comboBoxWillDismiss_) + [_delegate comboBoxWillDismiss:[[CPNotification alloc] initWithName:CPComboBoxWillDismissNotification object:self userInfo:nil]]; + [[CPNotificationCenter defaultCenter] postNotificationName:CPComboBoxWillDismissNotification object:self]; } diff --git a/AppKit/CPCursor.j b/AppKit/CPCursor.j index c6c96acdc..6e0b36cff 100755 --- a/AppKit/CPCursor.j +++ b/AppKit/CPCursor.j @@ -22,6 +22,7 @@ Cursor support by browser: */ @import +@import "CPImage.j" @global CPApp diff --git a/AppKit/CPEvent.j b/AppKit/CPEvent.j index 35bde5887..962980a40 100644 --- a/AppKit/CPEvent.j +++ b/AppKit/CPEvent.j @@ -31,6 +31,7 @@ @class CPTextField @class CPWindow +@class CPGraphicsContext @global CPApp @global CPNewlineCharacter diff --git a/AppKit/CPScrollView.j b/AppKit/CPScrollView.j index c93543315..aef8453a9 100644 --- a/AppKit/CPScrollView.j +++ b/AppKit/CPScrollView.j @@ -264,11 +264,6 @@ var CPScrollerStyleGlobal = CPScrollerStyleOverlay, _delegate = nil; _scrollTimer = nil; _implementedDelegateMethods = 0; - - [[CPNotificationCenter defaultCenter] addObserver:self - selector:@selector(_didReceiveDefaultStyleChange:) - name:CPScrollerStyleGlobalChangeNotification - object:nil]; } return self; @@ -1270,6 +1265,37 @@ Notifies the delegate when the scroll view has finished scrolling. #pragma mark - #pragma mark Overrides + +- (void)_removeObservers +{ + if (!_isObserving) + return; + + [[CPNotificationCenter defaultCenter] removeObserver:self + name:CPScrollerStyleGlobalChangeNotification + object:nil]; + + [super _removeObservers]; +} + +- (void)_addObservers +{ + if (_isObserving) + return; + + //Make sure to have the last global style for the scroller + [self _didReceiveDefaultStyleChange:nil]; + + [[CPNotificationCenter defaultCenter] addObserver:self + selector:@selector(_didReceiveDefaultStyleChange:) + name:CPScrollerStyleGlobalChangeNotification + object:nil]; + + [super _addObservers]; +} + + + - (void)drawRect:(CGRect)aRect { [super drawRect:aRect]; diff --git a/AppKit/CPTableColumn.j b/AppKit/CPTableColumn.j index e0f944a8e..8c25cfb3d 100644 --- a/AppKit/CPTableColumn.j +++ b/AppKit/CPTableColumn.j @@ -28,8 +28,6 @@ @import "CPTextField.j" -@global CPTableViewColumnDidResizeNotification - @class _CPTableColumnHeaderView @class CPTableView @@ -186,7 +184,7 @@ CPTableColumnUserResizingMask = 1 << 1; [tableView tile]; if (!_disableResizingPosting) - [self _postDidResizeNotificationWithOldWidth:oldWidth]; + [[self tableView] _didResizeTableColumn:self oldWidth:oldWidth]; } } @@ -539,19 +537,6 @@ CPTableColumnUserResizingMask = 1 << 1; return _headerToolTip; } -/*! - @ignore -*/ -- (void)_postDidResizeNotificationWithOldWidth:(float)oldWidth -{ - [[self tableView] _didResizeTableColumn:self]; - - [[CPNotificationCenter defaultCenter] - postNotificationName:CPTableViewColumnDidResizeNotification - object:[self tableView] - userInfo:@{ @"CPTableColumn": self, @"CPOldWidth": oldWidth }]; -} - @end @implementation CPTableColumnValueBinder : CPBinder diff --git a/AppKit/CPTableHeaderView.j b/AppKit/CPTableHeaderView.j index d22dc4f71..55e297eb8 100644 --- a/AppKit/CPTableHeaderView.j +++ b/AppKit/CPTableHeaderView.j @@ -625,7 +625,7 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal - (void)stopResizingTableColumn:(CPInteger)aColumnIndex at:(CGPoint)aPoint { var tableColumn = [[_tableView tableColumns] objectAtIndex:aColumnIndex]; - [tableColumn _postDidResizeNotificationWithOldWidth:_columnOldWidth]; + [_tableView _didResizeTableColumn:tableColumn oldWidth:_columnOldWidth]; [tableColumn setDisableResizingPosting:NO]; [_tableView setDisableAutomaticResizing:NO]; diff --git a/AppKit/CPTableView.j b/AppKit/CPTableView.j index e0961f6fa..110a42eaa 100644 --- a/AppKit/CPTableView.j +++ b/AppKit/CPTableView.j @@ -81,10 +81,12 @@ var CPTableViewDelegate_selectionShouldChangeInTableView_ CPTableViewDelegate_tableView_typeSelectStringForTableColumn_row_ = 1 << 17, CPTableViewDelegate_tableView_willDisplayView_forTableColumn_row_ = 1 << 18, CPTableViewDelegate_tableView_willRemoveView_forTableColumn_row_ = 1 << 19, - CPTableViewDelegate_tableViewSelectionDidChange_ = 1 << 20, - CPTableViewDelegate_tableViewSelectionIsChanging_ = 1 << 21, - CPTableViewDelegate_tableViewMenuForTableColumn_row_ = 1 << 22, - CPTableViewDelegate_tableView_shouldReorderColumn_toColumn_ = 1 << 23; + CPTableViewDelegate_tableViewColumnDidMove_ = 1 << 20, + CPTableViewDelegate_tableViewColumnDidResize_ = 1 << 21, + CPTableViewDelegate_tableViewSelectionDidChange_ = 1 << 22, + CPTableViewDelegate_tableViewSelectionIsChanging_ = 1 << 23, + CPTableViewDelegate_tableViewMenuForTableColumn_row_ = 1 << 24, + CPTableViewDelegate_tableView_shouldReorderColumn_toColumn_ = 1 << 25; //CPTableViewDraggingDestinationFeedbackStyles CPTableViewDraggingDestinationFeedbackStyleNone = -1; @@ -321,6 +323,8 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5; CPTableColumn _draggedColumn; CPArray _differedColumnDataToRemove; + + CPView _observedClipView; } /*! @@ -1167,6 +1171,9 @@ NOT YET IMPLEMENTED [[CPNotificationCenter defaultCenter] postNotificationName:CPTableViewColumnDidMoveNotification object:self userInfo:@{ @"CPOldColumn": fromIndex, @"CPNewColumn": toIndex }]; + + if (_implementedDelegateMethods & CPTableViewDelegate_tableViewColumnDidMove_) + [_delegate tableViewColumnDidMove:[[CPNotification alloc] initWithName:CPTableViewColumnDidMoveNotification object:self userInfo:@{ @"CPOldColumn": fromIndex, @"CPNewColumn": toIndex }]]; } /*! @@ -1245,9 +1252,17 @@ NOT YET IMPLEMENTED /*! @ignore */ -- (void)_didResizeTableColumn:(CPTableColumn)theColumn +- (void)_didResizeTableColumn:(CPTableColumn)theColumn oldWidth:(int)oldWidth { [self _autosave]; + + [[CPNotificationCenter defaultCenter] + postNotificationName:CPTableViewColumnDidResizeNotification + object:self + userInfo:@{ @"CPTableColumn": theColumn, @"CPOldWidth": oldWidth }]; + + if (_implementedDelegateMethods & CPTableViewDelegate_tableViewColumnDidResize_) + [_delegate tableViewColumnDidResize:[[CPNotification alloc] initWithName:CPTableViewColumnDidResizeNotification object:self userInfo:@{ @"CPTableColumn": theColumn, @"CPOldWidth": oldWidth }]]; } //Selecting Columns and Rows @@ -2860,35 +2875,6 @@ Your delegate can implement this method to avoid subclassing the tableview to ad if (_delegate === aDelegate) return; - var defaultCenter = [CPNotificationCenter defaultCenter]; - - if (_delegate) - { - if ([_delegate respondsToSelector:@selector(tableViewColumnDidMove:)]) - [defaultCenter - removeObserver:_delegate - name:CPTableViewColumnDidMoveNotification - object:self]; - - if ([_delegate respondsToSelector:@selector(tableViewColumnDidResize:)]) - [defaultCenter - removeObserver:_delegate - name:CPTableViewColumnDidResizeNotification - object:self]; - - if ([_delegate respondsToSelector:@selector(tableViewSelectionDidChange:)]) - [defaultCenter - removeObserver:_delegate - name:CPTableViewSelectionDidChangeNotification - object:self]; - - if ([_delegate respondsToSelector:@selector(tableViewSelectionIsChanging:)]) - [defaultCenter - removeObserver:_delegate - name:CPTableViewSelectionIsChangingNotification - object:self]; - } - _delegate = aDelegate; _implementedDelegateMethods = 0; @@ -2963,32 +2949,16 @@ Your delegate can implement this method to avoid subclassing the tableview to ad _implementedDelegateMethods |= CPTableViewDelegate_tableView_shouldReorderColumn_toColumn_; if ([_delegate respondsToSelector:@selector(tableViewColumnDidMove:)]) - [defaultCenter - addObserver:_delegate - selector:@selector(tableViewColumnDidMove:) - name:CPTableViewColumnDidMoveNotification - object:self]; + _implementedDelegateMethods |= CPTableViewDelegate_tableViewColumnDidMove_; if ([_delegate respondsToSelector:@selector(tableViewColumnDidResize:)]) - [defaultCenter - addObserver:_delegate - selector:@selector(tableViewColumnDidResize:) - name:CPTableViewColumnDidResizeNotification - object:self]; + _implementedDelegateMethods |= CPTableViewDelegate_tableViewColumnDidResize_; if ([_delegate respondsToSelector:@selector(tableViewSelectionDidChange:)]) - [defaultCenter - addObserver:_delegate - selector:@selector(tableViewSelectionDidChange:) - name:CPTableViewSelectionDidChangeNotification - object:self]; + _implementedDelegateMethods |= CPTableViewDelegate_tableViewSelectionDidChange_; if ([_delegate respondsToSelector:@selector(tableViewSelectionIsChanging:)]) - [defaultCenter - addObserver:_delegate - selector:@selector(tableViewSelectionIsChanging:) - name:CPTableViewSelectionIsChangingNotification - object:self]; + _implementedDelegateMethods |= CPTableViewDelegate_tableViewSelectionIsChanging_; } /*! @@ -4455,41 +4425,15 @@ Your delegate can implement this method to avoid subclassing the tableview to ad */ - (void)viewWillMoveToSuperview:(CPView)aView { - [super viewWillMoveToSuperview:aView]; - - var superview = [self superview], - defaultCenter = [CPNotificationCenter defaultCenter]; - - if (superview) - { - [defaultCenter - removeObserver:self - name:CPViewFrameDidChangeNotification - object:superview]; - - [defaultCenter - removeObserver:self - name:CPViewBoundsDidChangeNotification - object:superview]; - } - if ([aView isKindOfClass:[CPClipView class]]) + _observedClipView = aView; + else { - [aView setPostsFrameChangedNotifications:YES]; - [aView setPostsBoundsChangedNotifications:YES]; - - [defaultCenter - addObserver:self - selector:@selector(superviewFrameChanged:) - name:CPViewFrameDidChangeNotification - object:aView]; - - [defaultCenter - addObserver:self - selector:@selector(superviewBoundsChanged:) - name:CPViewBoundsDidChangeNotification - object:aView]; + [self _stopObservingClipView]; + _observedClipView = nil; } + + [super viewWillMoveToSuperview:aView]; } /*! @@ -5071,6 +5015,9 @@ Your delegate can implement this method to avoid subclassing the tableview to ad postNotificationName:CPTableViewSelectionIsChangingNotification object:self userInfo:nil]; + + if (_implementedDelegateMethods & CPTableViewDelegate_tableViewSelectionIsChanging_) + [_delegate tableViewSelectionIsChanging:[[CPNotification alloc] initWithName:CPTableViewSelectionIsChangingNotification object:self userInfo:nil]]; } /*! @@ -5082,6 +5029,9 @@ Your delegate can implement this method to avoid subclassing the tableview to ad postNotificationName:CPTableViewSelectionDidChangeNotification object:self userInfo:nil]; + + if (_implementedDelegateMethods & CPTableViewDelegate_tableViewSelectionDidChange_) + [_delegate tableViewSelectionDidChange:[[CPNotification alloc] initWithName:CPTableViewSelectionDidChangeNotification object:self userInfo:nil]]; } /*! @@ -5139,8 +5089,8 @@ Your delegate can implement this method to avoid subclassing the tableview to ad if (!_isObserving) return; + [self _stopObservingClipView]; [super _removeObservers]; - [self _stopObservingFirstResponder]; } - (void)_addObservers @@ -5148,13 +5098,64 @@ Your delegate can implement this method to avoid subclassing the tableview to ad if (_isObserving) return; + [self _startObservingClipView]; [super _addObservers]; - [self _startObservingFirstResponder]; } -- (void)_startObservingFirstResponder +/*! + Called when the receiver is about to be moved to a new window. + @param aWindow the window to which the receiver will be moved. +*/ +- (void)viewWillMoveToWindow:(CPWindow)aWindow { - [[CPNotificationCenter defaultCenter] addObserver:self selector:@selector(_firstResponderDidChange:) name:_CPWindowDidChangeFirstResponderNotification object:[self window]]; + [super viewWillMoveToWindow:aWindow]; + + [self _stopObservingFirstResponder]; + + if (aWindow) + [self _startObservingFirstResponderForWindow:aWindow]; +} + +- (void)_startObservingClipView +{ + if (!_observedClipView) + return; + + var defaultCenter = [CPNotificationCenter defaultCenter]; + + [_observedClipView setPostsFrameChangedNotifications:YES]; + [_observedClipView setPostsBoundsChangedNotifications:YES]; + + [defaultCenter addObserver:self + selector:@selector(superviewFrameChanged:) + name:CPViewFrameDidChangeNotification + object:_observedClipView]; + + [defaultCenter addObserver:self + selector:@selector(superviewBoundsChanged:) + name:CPViewBoundsDidChangeNotification + object:_observedClipView]; +} + +- (void)_stopObservingClipView +{ + if (!_observedClipView) + return; + + var defaultCenter = [CPNotificationCenter defaultCenter]; + + [defaultCenter removeObserver:self + name:CPViewFrameDidChangeNotification + object:_observedClipView]; + + [defaultCenter removeObserver:self + name:CPViewBoundsDidChangeNotification + object:_observedClipView]; +} + +- (void)_startObservingFirstResponderForWindow:(CPWindow)aWindow +{ + [[CPNotificationCenter defaultCenter] addObserver:self selector:@selector(_firstResponderDidChange:) name:_CPWindowDidChangeFirstResponderNotification object:aWindow]; } - (void)_stopObservingFirstResponder diff --git a/AppKit/CPTextField.j b/AppKit/CPTextField.j index dd41b0591..0d8576a45 100644 --- a/AppKit/CPTextField.j +++ b/AppKit/CPTextField.j @@ -38,8 +38,13 @@ @end +var CPTextFieldDelegate_control_didFailToFormatString_errorDescription_ = 1 << 1, + CPTextFieldDelegate_controlTextDidBeginEditing_ = 1 << 2, + CPTextFieldDelegate_controlTextDidChange_ = 1 << 3, + CPTextFieldDelegate_controlTextDidEndEditing_ = 1 << 4, + CPTextFieldDelegate_controlTextDidFocus_ = 1 << 5, + CPTextFieldDelegate_controlTextDidBlur_ = 1 << 6; -var CPTextFieldDelegate_control_didFailToFormatString_errorDescription_ = 1 << 1; @typedef CPTextFieldBezelStyle CPTextFieldSquareBezel = 0; /*! A textfield bezel with squared corners. */ @@ -877,7 +882,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder"); [super _addObservers]; - if ([self window] === self) + if ([[self window] firstResponder] === self) [self _setObserveWindowKeyNotifications:YES]; } @@ -1147,6 +1152,9 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder"); if ([note object] != self) return; + if (_implementedDelegateMethods & CPTextFieldDelegate_controlTextDidBlur_) + [_delegate controlTextDidBlur:note]; + [[CPNotificationCenter defaultCenter] postNotification:note]; } @@ -1156,6 +1164,9 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder"); if ([note object] != self) return; + if (_implementedDelegateMethods & CPTextFieldDelegate_controlTextDidFocus_) + [_delegate controlTextDidFocus:note]; + [[CPNotificationCenter defaultCenter] postNotification:note]; } @@ -1166,9 +1177,36 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder"); [self _continuouslyReverseSetBinding]; + if (_implementedDelegateMethods & CPTextFieldDelegate_controlTextDidChange_) + [_delegate controlTextDidChange:note]; + [super textDidChange:note]; } +- (void)textDidBeginEditing:(CPNotification)note +{ + //this looks to prevent false propagation of notifications for other objects + if ([note object] != self) + return; + + if (_implementedDelegateMethods & CPTextFieldDelegate_controlTextDidBeginEditing_) + [_delegate controlTextDidBeginEditing:[[CPNotification alloc] initWithName:CPControlTextDidBeginEditingNotification object:self userInfo:@{"CPFieldEditor": [note object]}]] + + [super textDidBeginEditing:note]; +} + +- (void)textDidEndEditing:(CPNotification)note +{ + //this looks to prevent false propagation of notifications for other objects + if ([note object] != self) + return; + + [super textDidEndEditing:note]; + + if (_implementedDelegateMethods & CPTextFieldDelegate_controlTextDidEndEditing_) + [_delegate controlTextDidEndEditing:note]; +} + - (void)_updateCursorForEvent:(CPEvent)anEvent { var frame = CGRectMakeCopy([self frame]), @@ -1713,17 +1751,8 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder"); - (void)setDelegate:(id )aDelegate { - var defaultCenter = [CPNotificationCenter defaultCenter]; - - //unsubscribe the existing delegate if it exists - if (_delegate) - { - [defaultCenter removeObserver:_delegate name:CPControlTextDidBeginEditingNotification object:self]; - [defaultCenter removeObserver:_delegate name:CPControlTextDidChangeNotification object:self]; - [defaultCenter removeObserver:_delegate name:CPControlTextDidEndEditingNotification object:self]; - [defaultCenter removeObserver:_delegate name:CPTextFieldDidFocusNotification object:self]; - [defaultCenter removeObserver:_delegate name:CPTextFieldDidBlurNotification object:self]; - } + if (_delegate === aDelegate) + return; _delegate = aDelegate; _implementedDelegateMethods = 0; @@ -1732,40 +1761,19 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder"); _implementedDelegateMethods |= CPTextFieldDelegate_control_didFailToFormatString_errorDescription_ if ([_delegate respondsToSelector:@selector(controlTextDidBeginEditing:)]) - [defaultCenter - addObserver:_delegate - selector:@selector(controlTextDidBeginEditing:) - name:CPControlTextDidBeginEditingNotification - object:self]; + _implementedDelegateMethods |= CPTextFieldDelegate_controlTextDidBeginEditing_; if ([_delegate respondsToSelector:@selector(controlTextDidChange:)]) - [defaultCenter - addObserver:_delegate - selector:@selector(controlTextDidChange:) - name:CPControlTextDidChangeNotification - object:self]; - + _implementedDelegateMethods |= CPTextFieldDelegate_controlTextDidChange_; if ([_delegate respondsToSelector:@selector(controlTextDidEndEditing:)]) - [defaultCenter - addObserver:_delegate - selector:@selector(controlTextDidEndEditing:) - name:CPControlTextDidEndEditingNotification - object:self]; + _implementedDelegateMethods |= CPTextFieldDelegate_controlTextDidEndEditing_; if ([_delegate respondsToSelector:@selector(controlTextDidFocus:)]) - [defaultCenter - addObserver:_delegate - selector:@selector(controlTextDidFocus:) - name:CPTextFieldDidFocusNotification - object:self]; + _implementedDelegateMethods |= CPTextFieldDelegate_controlTextDidFocus_; if ([_delegate respondsToSelector:@selector(controlTextDidBlur:)]) - [defaultCenter - addObserver:_delegate - selector:@selector(controlTextDidBlur:) - name:CPTextFieldDidBlurNotification - object:self]; + _implementedDelegateMethods |= CPTextFieldDelegate_controlTextDidBlur_; } - (id)delegate diff --git a/AppKit/CPView.j b/AppKit/CPView.j index 70068d668..01e486fe2 100644 --- a/AppKit/CPView.j +++ b/AppKit/CPView.j @@ -175,6 +175,7 @@ var CPViewFlags = { }, BOOL _postsBoundsChangedNotifications; BOOL _inhibitFrameAndBoundsChangedNotifications; BOOL _inLiveResize; + BOOL _isSuperviewAClipView; #if PLATFORM(DOM) DOMElement _DOMElement; @@ -824,6 +825,8 @@ var CPViewFlags = { }, */ - (void)viewWillMoveToSuperview:(CPView)aView { + _isSuperviewAClipView = [aView isKindOfClass:[CPClipView class]]; + [self _removeObservers]; if (aView) @@ -955,6 +958,9 @@ var CPViewFlags = { }, if (_postsFrameChangedNotifications) [CachedNotificationCenter postNotificationName:CPViewFrameDidChangeNotification object:self]; + + if (_isSuperviewAClipView) + [[self superview] viewFrameChanged:[[CPNotification alloc] initWithName:CPViewFrameDidChangeNotification object:self userInfo:nil]]; } /*! @@ -1017,6 +1023,9 @@ var CPViewFlags = { }, if (_postsFrameChangedNotifications && !_inhibitFrameAndBoundsChangedNotifications) [CachedNotificationCenter postNotificationName:CPViewFrameDidChangeNotification object:self]; + if (_isSuperviewAClipView && !_inhibitFrameAndBoundsChangedNotifications) + [[self superview] viewFrameChanged:[[CPNotification alloc] initWithName:CPViewFrameDidChangeNotification object:self userInfo:nil]]; + #if PLATFORM(DOM) var transform = _superview ? _superview._boundsTransform : NULL; @@ -1176,6 +1185,9 @@ var CPViewFlags = { }, if (_postsFrameChangedNotifications && !_inhibitFrameAndBoundsChangedNotifications) [CachedNotificationCenter postNotificationName:CPViewFrameDidChangeNotification object:self]; + + if (_isSuperviewAClipView && !_inhibitFrameAndBoundsChangedNotifications) + [[self superview] viewFrameChanged:[[CPNotification alloc] initWithName:CPViewFrameDidChangeNotification object:self userInfo:nil]]; } /*! @@ -1210,6 +1222,9 @@ var CPViewFlags = { }, if (_postsBoundsChangedNotifications) [CachedNotificationCenter postNotificationName:CPViewBoundsDidChangeNotification object:self]; + + if (_isSuperviewAClipView) + [[self superview] viewBoundsChanged:[[CPNotification alloc] initWithName:CPViewBoundsDidChangeNotification object:self userInfo:nil]]; } /*! @@ -1272,6 +1287,9 @@ var CPViewFlags = { }, if (_postsBoundsChangedNotifications && !_inhibitFrameAndBoundsChangedNotifications) [CachedNotificationCenter postNotificationName:CPViewBoundsDidChangeNotification object:self]; + + if (_isSuperviewAClipView && !_inhibitFrameAndBoundsChangedNotifications) + [[self superview] viewBoundsChanged:[[CPNotification alloc] initWithName:CPViewBoundsDidChangeNotification object:self userInfo:nil]]; } /*! @@ -1310,6 +1328,9 @@ var CPViewFlags = { }, if (_postsBoundsChangedNotifications && !_inhibitFrameAndBoundsChangedNotifications) [CachedNotificationCenter postNotificationName:CPViewBoundsDidChangeNotification object:self]; + + if (_isSuperviewAClipView && !_inhibitFrameAndBoundsChangedNotifications) + [[self superview] viewBoundsChanged:[[CPNotification alloc] initWithName:CPViewBoundsDidChangeNotification object:self userInfo:nil]]; } diff --git a/AppKit/CPWindow/CPWindow.j b/AppKit/CPWindow/CPWindow.j index e2836fe32..160a03ee4 100644 --- a/AppKit/CPWindow/CPWindow.j +++ b/AppKit/CPWindow/CPWindow.j @@ -907,18 +907,21 @@ CPTexturedBackgroundWindowMask - (void)_orderFront { - [[self contentView] _addObservers]; #if PLATFORM(DOM) // -dw- if a sheet is clicked, the parent window should come up too if (_isSheet) [_parentView orderFront:self]; - if (!_isVisible) - [self _setFrame:_frame display:YES animate:NO constrainWidth:YES constrainHeight:YES]; + // Save the boolean since it will be updated in the method order:window:relativeTo: + var wasVisible = _isVisible; [_platformWindow orderFront:self]; [_platformWindow order:CPWindowAbove window:self relativeTo:nil]; + + // setFrame is set after ordering the window as this method can send some notifications + if (!wasVisible) + [self _setFrame:_frame display:YES animate:NO constrainWidth:YES constrainHeight:YES]; #endif if (!CPApp._keyWindow) @@ -939,6 +942,23 @@ CPTexturedBackgroundWindowMask { } +/* + Called when the window is displayed in the DOM +*/ +- (void)_windowWillBeAddedToTheDOM +{ + [[self contentView] _addObservers]; +} + +/* + Called when the window is removed in the DOM +*/ +- (void)_windowWillBeRemovedFromTheDOM +{ + [[self contentView] _removeObservers]; +} + + /* Makes the receiver the last window in the screen ordering. @param aSender the object that requested this @@ -968,8 +988,6 @@ CPTexturedBackgroundWindowMask if (!_isVisible) return; - [[self contentView] _removeObservers]; - if ([self isSheet]) { // -dw- as in Cocoa, orderOut: detaches the sheet and animates out diff --git a/AppKit/Platform/DOM/CPPlatformWindow+DOM.j b/AppKit/Platform/DOM/CPPlatformWindow+DOM.j index eeba3a894..80a04feae 100644 --- a/AppKit/Platform/DOM/CPPlatformWindow+DOM.j +++ b/AppKit/Platform/DOM/CPPlatformWindow+DOM.j @@ -1299,7 +1299,10 @@ var PreventScroll = true; // When ordering out, ignore otherWindow, simply remove aWindow from its level. // If layer is nil, this will be a no-op. if (orderingMode === CPWindowOut) + { + [aWindow _windowWillBeRemovedFromTheDOM]; return [layer removeWindow:aWindow]; + } /* If aWindow is a child of otherWindow and is not yet visible, @@ -1345,6 +1348,8 @@ var PreventScroll = true; if (otherWindow) insertionIndex = orderingMode === CPWindowAbove ? otherWindow._index + 1 : otherWindow._index; + [aWindow _windowWillBeAddedToTheDOM]; + // Place the window at the appropriate index. [layer insertWindow:aWindow atIndex:insertionIndex]; @@ -1395,6 +1400,9 @@ var PreventScroll = true; var index = ordering === CPWindowAbove ? parent._index + 1 : parent._index; + if (!childWasVisible) + [child _windowWillBeAddedToTheDOM]; + [aLayer insertWindow:child atIndex:index]; if (!childWasVisible) diff --git a/AppKit/_CPPopUpList.j b/AppKit/_CPPopUpList.j index 3dd357132..6ee4e1ff0 100644 --- a/AppKit/_CPPopUpList.j +++ b/AppKit/_CPPopUpList.j @@ -96,28 +96,41 @@ var ListColumnIdentifier = @"1"; return [super sendEvent:anEvent]; } -- (void)orderFront:(id)sender -{ - [self _trapNextMouseDown]; - [super orderFront:sender]; -} - - (void)_mouseWasClicked:(CPEvent)anEvent { + // This is needed, when the user close the list with the key enter + if (![self isVisible]) + { + [CPApp sendEvent:anEvent]; + return; + } + var mouseWindow = [anEvent window], - rect = [[[self delegate] dataSource] bounds], + rect = CGRectInsetByInset([[[self delegate] dataSource] bounds], [[[self delegate] dataSource] currentValueForThemeAttribute:@"content-inset"]), point = [[[self delegate] dataSource] convertPoint:[anEvent locationInWindow] fromView:nil]; + // If we click somewhere else than the comboBox or the panel we close the panel if (mouseWindow != self && !CGRectContainsPoint(rect, point)) + { [[self delegate] close]; + } else - [self _trapNextMouseDown]; + { + // If we click on the panel, the app will know what to do + if (mouseWindow == self) + [CPApp sendEvent:anEvent]; + + // If we click on the comboBox field, we will trap the next mouse down + if (CGRectContainsPoint(rect, point)) + [self _trapNextMouseDown]; + } + } - (void)_trapNextMouseDown { - // Don't dequeue the event so clicks in controls will work - [CPApp setTarget:self selector:@selector(_mouseWasClicked:) forNextEventMatchingMask:CPLeftMouseDownMask untilDate:nil inMode:CPDefaultRunLoopMode dequeue:NO]; + // Dequeue the event and mouseWasClicked will do what it needs to do + [CPApp setTarget:self selector:@selector(_mouseWasClicked:) forNextEventMatchingMask:CPLeftMouseDownMask untilDate:nil inMode:CPDefaultRunLoopMode dequeue:YES]; } @end @@ -323,6 +336,11 @@ var ListColumnIdentifier = @"1"; if ([_panel isVisible]) return; + [self listWillPopUp]; + + [_panel _trapNextMouseDown]; + [[aView window] addChildWindow:_panel ordered:CPWindowAbove]; + var rowRect = [_tableView rectOfRow:[self numberOfRowsInTableView:_tableView] - 1], frame = CGRectMake(0, 0, MAX(_listWidth, CGRectGetWidth(aRect)), CGRectGetMaxY(rowRect)); @@ -333,10 +351,6 @@ var ListColumnIdentifier = @"1"; [_scrollView setFrameSize:CGSizeMakeCopy(frame.size)]; [_tableView setEnabled:[_dataSource numberOfItemsInList:self] > 0]; [self scrollItemAtIndexToTop:[_tableView selectedRow]]; - - [self listWillPopUp]; - - [[aView window] addChildWindow:_panel ordered:CPWindowAbove]; } #pragma mark Setting Display Attributes diff --git a/AppKit/_CPPopoverWindow.j b/AppKit/_CPPopoverWindow.j index ce2a7898b..27d160e17 100644 --- a/AppKit/_CPPopoverWindow.j +++ b/AppKit/_CPPopoverWindow.j @@ -64,6 +64,7 @@ var _CPPopoverWindow_shouldClose_ = 1 << 4, BOOL _isObservingFrame; BOOL _shouldPerformAnimation; CPInteger _implementedDelegateMethods; + CGRect _targetRect; CPWindow _targetWindow; JSObject _orderOutTransitionFunction; JSObject _transitionCompleteFunction; @@ -206,7 +207,7 @@ var _CPPopoverWindow_shouldClose_ = 1 << 4, if (![_targetView window]) return; - var point = [self computeOriginFromRect:[_targetView bounds] ofView:_targetView preferredEdge:[_windowView preferredEdge]]; + var point = [self computeOriginFromRect:_targetRect ofView:_targetView preferredEdge:[_windowView preferredEdge]]; [self setFrameOrigin:point]; } } @@ -369,6 +370,7 @@ var _CPPopoverWindow_shouldClose_ = 1 << 4, _targetView = positioningView; } + _targetRect = aRect; [self makeKeyAndOrderFront:nil]; /* @@ -417,7 +419,7 @@ var _CPPopoverWindow_shouldClose_ = 1 << 4, if ([self isVisible]) { - var point = [self computeOriginFromRect:[_targetView bounds] ofView:_targetView preferredEdge:[_windowView preferredEdge]]; + var point = [self computeOriginFromRect:_targetRect ofView:_targetView preferredEdge:[_windowView preferredEdge]]; [self setFrameOrigin:point]; } } @@ -581,10 +583,12 @@ var _CPPopoverWindow_shouldClose_ = 1 << 4, - (void)_orderFront { - if (![self isVisible]) - [self _addFrameObserver]; + var wasVisible = [self isVisible]; [super _orderFront]; + + if (!wasVisible) + [self _addFrameObserver]; } - (void)_parentDidOrderInChild diff --git a/Foundation/CPCoder.j b/Foundation/CPCoder.j index d02d918e0..0bdc7b89a 100644 --- a/Foundation/CPCoder.j +++ b/Foundation/CPCoder.j @@ -53,7 +53,7 @@ @param aType the structure or object type @param anObject the object to be encoded */ -- (void)encodeValueOfObjCType:(CPString)aType at:(id)anObject +- (void)encodeValueOfObjJType:(CPString)aType at:(id)anObject { _CPRaiseInvalidAbstractInvocation(self, _cmd); } diff --git a/Foundation/CPDate.j b/Foundation/CPDate.j index 736ca9ff2..574873ef6 100644 --- a/Foundation/CPDate.j +++ b/Foundation/CPDate.j @@ -74,24 +74,36 @@ var CPDateReferenceDate = new Date(Date.UTC(2001, 0, 1, 0, 0, 0, 0)); - (id)initWithTimeIntervalSinceNow:(CPTimeInterval)seconds { + if (!_isNumberType(seconds)) + CPLog.warn(@"The parameter of the method initWithTimeIntervalSinceNow: should be an integer or a float"); + self = new Date((new Date()).getTime() + seconds * 1000); return self; } - (id)initWithTimeIntervalSince1970:(CPTimeInterval)seconds { + if (!_isNumberType(seconds)) + CPLog.warn(@"The parameter of the method initWithTimeIntervalSince1970: should be an integer or a float"); + self = new Date(seconds * 1000); return self; } - (id)initWithTimeIntervalSinceReferenceDate:(CPTimeInterval)seconds { + if (!_isNumberType(seconds)) + CPLog.warn(@"The parameter of the method initWithTimeIntervalSinceReferenceDate: should be an integer or a float"); + self = [self initWithTimeInterval:seconds sinceDate:CPDateReferenceDate]; return self; } - (id)initWithTimeInterval:(CPTimeInterval)seconds sinceDate:(CPDate)refDate { + if (!_isNumberType(seconds)) + CPLog.warn(@"The parameter of the method initWithTimeInterval:sinceDate: should be an integer or a float"); + self = new Date(refDate.getTime() + seconds * 1000); return self; } @@ -275,3 +287,11 @@ Date.parseISO8601 = function (date) }; Date.prototype.isa = CPDate; + +function _isNumberType(value) +{ + if (typeof value === 'number') + return YES; + else + return NO; +} \ No newline at end of file diff --git a/Foundation/CPError.j b/Foundation/CPError.j index 4acafe2ce..edc270514 100644 --- a/Foundation/CPError.j +++ b/Foundation/CPError.j @@ -24,31 +24,52 @@ @import "CPObject.j" @import "CPString.j" -CPCappuccinoErrorDomain = CPCocoaErrorDomain = @"CPCappuccinoErrorDomain"; -// CPPOSIXErrorDomain = @"CPPOSIXErrorDomain"; -// CPOSStatusErrorDomain = @"CPOSStatusErrorDomain"; +CPCappuccinoErrorDomain = kCFErrorDomainCappuccino; +CPCocoaErrorDomain = kCFErrorDomainCappuccino; // compat -CPUnderlyingErrorKey = @"CPUnderlyingErrorKey"; +CPUnderlyingErrorKey = kCFErrorUnderlyingErrorKey; -CPLocalizedDescriptionKey = @"CPLocalizedDescriptionKey"; -CPLocalizedFailureReasonErrorKey = @"CPLocalizedFailureReasonErrorKey"; -CPLocalizedRecoverySuggestionErrorKey = @"CPLocalizedRecoverySuggestionErrorKey"; +CPLocalizedDescriptionKey = kCFErrorLocalizedDescriptionKey; +CPLocalizedFailureReasonErrorKey = kCFErrorLocalizedFailureReasonKey; +CPLocalizedRecoverySuggestionErrorKey = kCFErrorLocalizedRecoverySuggestionKey; CPLocalizedRecoveryOptionsErrorKey = @"CPLocalizedRecoveryOptionsErrorKey"; CPRecoveryAttempterErrorKey = @"CPRecoveryAttempterErrorKey"; CPHelpAnchorErrorKey = @"CPHelpAnchorErrorKey"; CPStringEncodingErrorKey = @"CPStringEncodingErrorKey"; -CPURLErrorKey = @"CPURLErrorKey"; -CPFilePathErrorKey = @"CPFilePathErrorKey"; +CPURLErrorKey = kCFErrorURLKey; +CPFilePathErrorKey = kCFErrorFilePathKey; +/*! + @class CPError + @ingroup foundation + @brief Used for encapsulating, presenting, and recovery from errors. + CPError is toll-free bridged with CFError() methods. + + An example of initializing a CPError: +
+
+var userInfo = @{CPLocalizedDescriptionKey: @"A localized error description",
+                 CPLocalizedFailureReasonErrorKey: @"A localized failure reason",
+                 CPUnderlyingErrorKey: @"An underlying error message"},
+
+    err = [CPError errorWithDomain:CPCappuccinoErrorDomain code:-10 userInfo:userInfo];
+
+ */ @implementation CPError : CPObject { - CPInteger _code @accessors(property=code, readonly); - CPString _domain @accessors(property=domain, readonly); - CPDictionary _userInfo @accessors(property=userInfo, readonly); } ++ (id)alloc +{ + var obj = new CFError(); + obj.isa = [self class]; + + return obj; +} + + + (id)errorWithDomain:(CPString)aDomain code:(CPInteger)aCode userInfo:(CPDictionary)aDict { return [[CPError alloc] initWithDomain:aDomain code:aCode userInfo:aDict]; @@ -56,44 +77,94 @@ CPFilePathErrorKey = @"CPFilePathErrorKey"; - (id)initWithDomain:(CPString)aDomain code:(CPInteger)aCode userInfo:(CPDictionary)aDict { - if (self = [super init]) - { - _domain = aDomain; - _code = aCode; - _userInfo = aDict; - } - - return self; + var result = new CFError(aDomain, aCode, aDict); + result.isa = [self class]; + return result; } +- (CPInteger)code +{ + return self.code(); +} + +- (CPString)userInfo +{ + return self.userInfo(); +} + +- (CPString)domain +{ + return self.domain(); +} + +/*! + By default this method returns the object in the user info dictionary for the key + CPLocalizedDescriptionKey. If the user info dictionary doesn’t contain a value for + CPLocalizedDescriptionKey, a default string is constructed from the domain and code. + */ - (CPString)localizedDescription { - return [_userInfo objectForKey:CPLocalizedDescriptionKey]; + return self.description(); } - (CPString)localizedFailureReason { - return [_userInfo objectForKey:CPLocalizedFailureReasonErrorKey]; + return self.failureReason(); } - (CPArray)localizedRecoveryOptions { - return [_userInfo objectForKey:CPLocalizedRecoveryOptionsErrorKey]; + var userInfo = self.userInfo(), + recoveryOptions = userInfo.valueForKey(CPLocalizedRecoveryOptionsErrorKey); + + return recoveryOptions; } - (CPString)localizedRecoverySuggestion { - return [_userInfo objectForKey:CPLocalizedRecoverySuggestionErrorKey]; + return self.recoverySuggestion(); } - (id)recoveryAttempter { - return [_userInfo objectForKey:CPRecoveryAttempterErrorKey]; + var userInfo = self.userInfo(), + recoveryAttempter = userInfo.valueForKey(CPRecoveryAttempterErrorKey); + + return recoveryAttempter; } - (CPString)description { - return [CPString stringWithFormat:@"Error Domain=%@ Code=%d UserInfo=%p %@", _domain, _code, _userInfo, [self localizedDescription]]; + return [CPString stringWithFormat:@"Error Domain=%@ Code=%d \"%@\" UserInfo=%@", self.domain(), self.code(), self.description(), self.userInfo()]; } @end + +var CPErrorCodeKey = @"CPErrorCodeKey", + CPErrorDomainKey = @"CPErrorDomainKey", + CPErrorUserInfoKey = @"CPErrorUserInfoKey"; + +@implementation CPError (CPCoding) + +- (id)initWithCoder:(CPCoder)aCoder +{ + var code = [aCoder decodeIntForKey:CPErrorCodeKey], + domain = [aCoder decodeObjectForKey:CPErrorDomainKey], + userInfo = [aCoder decodeObjectForKey:CPErrorUserInfoKey]; + + return [self initWithDomain:domain + code:code + userInfo:userInfo]; +} + +- (void)encodeWithCoder:(CPCoder)aCoder +{ + [aCoder encodeObject:self.domain() forKey:CPErrorDomainKey]; + [aCoder encodeObject:self.code() forKey:CPErrorCodeKey]; + [aCoder encodeObject:self.userInfo() forKey:CPErrorUserInfoKey]; +} + +@end + +CFError.prototype.isa = CPError; + diff --git a/Foundation/CPOperation.j b/Foundation/CPOperation.j index 4fbdd5612..b0b8a6154 100644 --- a/Foundation/CPOperation.j +++ b/Foundation/CPOperation.j @@ -117,10 +117,11 @@ CPOperationQueuePriorityVeryHigh = 8; [self willChangeValueForKey:@"isExecuting"]; _executing = NO; [self didChangeValueForKey:@"isExecuting"]; - [self willChangeValueForKey:@"isFinished"]; - _finished = YES; - [self didChangeValueForKey:@"isFinished"]; } + + [self willChangeValueForKey:@"isFinished"]; + _finished = YES; + [self didChangeValueForKey:@"isFinished"]; } /*! diff --git a/Foundation/CPOperationQueue.j b/Foundation/CPOperationQueue.j index 1e018df15..de9322ce6 100644 --- a/Foundation/CPOperationQueue.j +++ b/Foundation/CPOperationQueue.j @@ -70,7 +70,7 @@ var cpOperationMainQueue = nil; for (; i < count; i++) { var op = [_operations objectAtIndex:i]; - if ([op isReady] && ![op isCancelled] && ![op isFinished] && ![op isExecuting]) + if ([op isReady] && ![op isFinished] && ![op isExecuting]) { [op start]; } @@ -260,7 +260,7 @@ var cpOperationMainQueue = nil; for (; i < count; i++) { var op = [ops objectAtIndex:i]; - if ([op isReady] && ![op isCancelled] && ![op isFinished] && ![op isExecuting]) + if ([op isReady] && ![op isFinished] && ![op isExecuting]) { [op start]; } diff --git a/Foundation/CPURLError.j b/Foundation/CPURLError.j new file mode 100644 index 000000000..77d901256 --- /dev/null +++ b/Foundation/CPURLError.j @@ -0,0 +1,39 @@ + +/* + * The CPURL Error Domain + */ +CPURLErrorDomain = @"CPURLErrorDomain"; + +/* + * CPURL UserInfo Error Keys + */ +CPURLErrorFailingURLErrorKey = @"CPErrorFailingURLKey"; +CPURLErrorFailingURLStringErrorKey = @"CPURLErrorFailingURLStringKey"; + +/* + * CPURL Error Codes + */ +CPURLErrorUnknown = -1; +CPURLErrorCancelled = kCFURLErrorCancelled; +CPURLErrorBadURL = kCFURLErrorBadURL; +CPURLErrorTimedOut = kCFURLErrorTimedOut; +CPURLErrorUnsupportedURL = kCFURLErrorUnsupportedURL; +CPURLErrorCannotFindHost = kCFURLErrorCannotFindHost; +CPURLErrorCannotConnectToHost = kCFURLErrorCannotConnectToHost; +CPURLErrorNetworkConnectionLost = kCFURLErrorNetworkConnectionLost; +CPURLErrorDNSLookupFailed = kCFURLErrorDNSLookupFailed; +CPURLErrorHTTPTooManyRedirects = kCFURLErrorHTTPTooManyRedirects; +CPURLErrorResourceUnavailable = kCFURLErrorResourceUnavailable; +CPURLErrorNotConnectedToInternet = kCFURLErrorNotConnectedToInternet; +CPURLErrorRedirectToNonExistentLocation = kCFURLErrorRedirectToNonExistentLocation; +CPURLErrorBadServerResponse = kCFURLErrorBadServerResponse; +CPURLErrorUserCancelledAuthentication = kCFURLErrorUserCancelledAuthentication; +CPURLErrorUserAuthenticationRequired = kCFURLErrorUserAuthenticationRequired; +CPURLErrorZeroByteResource = kCFURLErrorZeroByteResource; +CPURLErrorCannotDecodeRawData = kCFURLErrorCannotDecodeRawData; +CPURLErrorCannotDecodeContentData = kCFURLErrorCannotDecodeContentData; +CPURLErrorCannotParseResponse = kCFURLErrorCannotParseResponse; +CPURLErrorFileDoesNotExist = kCFURLErrorFileDoesNotExist; +CPURLErrorFileIsDirectory = kCFURLErrorFileIsDirectory; +CPURLErrorNoPermissionsToReadFile = kCFURLErrorNoPermissionsToReadFile; +CPURLErrorDataLengthExceedsMaximum = kCFURLErrorDataLengthExceedsMaximum; \ No newline at end of file diff --git a/Foundation/Foundation.j b/Foundation/Foundation.j index ab9d5fdb2..3fa485412 100755 --- a/Foundation/Foundation.j +++ b/Foundation/Foundation.j @@ -73,6 +73,7 @@ @import "CPUndoManager.j" @import "CPURL.j" @import "CPURLConnection.j" +@import "CPURLError.j" @import "CPURLRequest.j" @import "CPURLResponse.j" @import "CPUserDefaults.j" diff --git a/Objective-J/CFError.js b/Objective-J/CFError.js new file mode 100644 index 000000000..615bbade8 --- /dev/null +++ b/Objective-J/CFError.js @@ -0,0 +1,164 @@ +/* + * CFError.js + * Objective-J + * + * Created by Andrew Hankinson. + * Copyright 2014, Andrew Hankinson. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +GLOBAL(kCFErrorLocalizedDescriptionKey) = "CPLocalizedDescription"; +GLOBAL(kCFErrorLocalizedFailureReasonKey) = "CPLocalizedFailureReason"; +GLOBAL(kCFErrorLocalizedRecoverySuggestionKey) = "CPLocalizedRecoverySuggestion"; +GLOBAL(kCFErrorDescriptionKey) = "CPDescription"; +GLOBAL(kCFErrorUnderlyingErrorKey) = "CPUnderlyingError"; + +GLOBAL(kCFErrorURLKey) = "CPURL"; +GLOBAL(kCFErrorFilePathKey) = "CPFilePath"; + +// GLOBAL(kCFErrorDomainPOSIX) = ""; +// GLOBAL(kCFErrorDomainOSStatus) = ""; +// GLOBAL(kCFErrorDomainMach) = ""; +GLOBAL(kCFErrorDomainCappuccino) = "CPCappuccinoErrorDomain"; +GLOBAL(kCFErrorDomainCocoa) = kCFErrorDomainCappuccino; + + +GLOBAL(CFError) = function(/* CFString */ domain, /* int */ code, /* CFDictionary */ userInfo) +{ + this._domain = domain || NULL; + this._code = code || 0; + this._userInfo = userInfo || new CFDictionary(); + this._UID = objj_generateObjectUID(); +}; + +CFError.prototype.domain = function() +{ + return this._domain; +}; + +DISPLAY_NAME(CFError.prototype.domain); + +CFError.prototype.code = function() +{ + return this._code; +}; + +DISPLAY_NAME(CFError.prototype.code); + +/* + This follows the same logic to generate a description as the "real" CFError. +*/ +CFError.prototype.description = function() +{ + var localizedDesc = this._userInfo.valueForKey(kCFErrorLocalizedDescriptionKey); + if (localizedDesc) + return localizedDesc; + + var reason = this._userInfo.valueForKey(kCFErrorLocalizedFailureReasonKey); + if (reason) + { + var operationFailedStr = "The operation couldn\u2019t be completed. " + reason; + return operationFailedStr; + } + + // @TODO Add the bundle localized domain handler. + var result = "", + desc = this._userInfo.valueForKey(kCFErrorDescriptionKey); + if (desc) + { + // we have a description key. + var result = "The operation couldn\u2019t be completed. (error " + this._code + " - " + desc + ")"; + } + else + { + // just use error and code; + var result = "The operation couldn\u2019t be completed. (error " + this._code + ")"; + } + + return result; +}; + +DISPLAY_NAME(CFError.prototype.description); + +CFError.prototype.failureReason = function() +{ + return this._userInfo.valueForKey(kCFErrorLocalizedFailureReasonKey); +}; + +DISPLAY_NAME(CFError.prototype.failureReason); + +CFError.prototype.recoverySuggestion = function() +{ + return this._userInfo.valueForKey(kCFErrorLocalizedRecoverySuggestionKey); +}; + +DISPLAY_NAME(CFError.prototype.recoverySuggestion); + +CFError.prototype.userInfo = function () +{ + return this._userInfo; +}; + +DISPLAY_NAME(CFError.prototype.userInfo); + +/* + CFError Bridge Functions + The "Create" and "Copy" in the function names do not have any meaning + in Cappuccino; they are bridged here for compatibility reasons only. +*/ +GLOBAL(CFErrorCreate) = function(/* String */ domain, /*int */ code, /* CFDictionary */ userInfo) +{ + return new CFError(domain, code, userInfo); +}; + +GLOBAL(CFErrorCreateWithUserInfoKeysAndValues) = function(/* String */ domain, /* int */ code, /* array */ userInfoKeys, /* array */ userInfoValues, /* int */ numUserInfoValues) +{ + var userInfo = new CFMutableDictionary(); + while (numUserInfoValues--) + userInfo.setValueForKey(userInfoKeys[numUserInfoValues], userInfoValues[numUserInfoValues]); + + return new CFError(domain, code, userInfo); +}; + +GLOBAL(CFErrorGetCode) = function(/* CFError */ err) +{ + return err.code(); +}; + +GLOBAL(CFErrorGetDomain) = function(/* CFError */ err) +{ + return err.domain(); +}; + +GLOBAL(CFErrorCopyDescription) = function(/* CFError */ err) +{ + return err.description(); +}; + +GLOBAL(CFErrorCopyUserInfo) = function(/* CFError */ err) +{ + return err.userInfo(); +}; + +GLOBAL(CFErrorCopyFailureReason) = function(/* CFError */ err) +{ + return err.failureReason(); +}; + +GLOBAL(CFErrorCopyRecoverySuggestion) = function(/* CFError */err) +{ + return err.recoverySuggestion(); +}; diff --git a/Objective-J/CFNetworkErrors.js b/Objective-J/CFNetworkErrors.js new file mode 100644 index 000000000..9473579b9 --- /dev/null +++ b/Objective-J/CFNetworkErrors.js @@ -0,0 +1,25 @@ +GLOBAL(kCFURLErrorUnknown) = -998; +GLOBAL(kCFURLErrorCancelled) = -999; +GLOBAL(kCFURLErrorBadURL) = -1000; +GLOBAL(kCFURLErrorTimedOut) = -1001; +GLOBAL(kCFURLErrorUnsupportedURL) = -1002; +GLOBAL(kCFURLErrorCannotFindHost) = -1003; +GLOBAL(kCFURLErrorCannotConnectToHost) = -1004; +GLOBAL(kCFURLErrorNetworkConnectionLost) = -1005; +GLOBAL(kCFURLErrorDNSLookupFailed) = -1006; +GLOBAL(kCFURLErrorHTTPTooManyRedirects) = -1007; +GLOBAL(kCFURLErrorResourceUnavailable) = -1008; +GLOBAL(kCFURLErrorNotConnectedToInternet) = -1009; +GLOBAL(kCFURLErrorRedirectToNonExistentLocation) = -1010; +GLOBAL(kCFURLErrorBadServerResponse) = -1011; +GLOBAL(kCFURLErrorUserCancelledAuthentication) = -1012; +GLOBAL(kCFURLErrorUserAuthenticationRequired) = -1013; +GLOBAL(kCFURLErrorZeroByteResource) = -1014; +GLOBAL(kCFURLErrorCannotDecodeRawData) = -1015; +GLOBAL(kCFURLErrorCannotDecodeContentData) = -1016; +GLOBAL(kCFURLErrorCannotParseResponse) = -1017; +GLOBAL(kCFURLErrorRequestBodyStreamExhausted) = -1021; +GLOBAL(kCFURLErrorFileDoesNotExist) = -1100; +GLOBAL(kCFURLErrorFileIsDirectory) = -1101; +GLOBAL(kCFURLErrorNoPermissionsToReadFile) = -1102; +GLOBAL(kCFURLErrorDataLengthExceedsMaximum) = -1103; \ No newline at end of file diff --git a/Objective-J/Includes.js b/Objective-J/Includes.js index a4373e3a0..43adc2de2 100644 --- a/Objective-J/Includes.js +++ b/Objective-J/Includes.js @@ -38,6 +38,8 @@ #include "CFHTTPRequest.js" #include "CFPropertyList.js" #include "CFDictionary.js" +#include "CFError.js" +#include "CFNetworkErrors.js" #include "CFData.js" #include "CFURL.js" #include "MarkedStream.js" diff --git a/Objective-J/ObjJAcornCompiler.js b/Objective-J/ObjJAcornCompiler.js index 79646a0e6..0e9d16f98 100644 --- a/Objective-J/ObjJAcornCompiler.js +++ b/Objective-J/ObjJAcornCompiler.js @@ -2381,9 +2381,9 @@ Reference: function(node, st, c) { buffer.concat(" "); // Add an extra space if it looks something like this: "return()". No space between return and expression. } buffer.concat("function(__input) { if (arguments.length) return "); - buffer.concat(node.element.name); + c(node.element, st, "Expression"); buffer.concat(" = __input; return "); - buffer.concat(node.element.name); + c(node.element, st, "Expression"); buffer.concat("; }"); if (!generate) compiler.lastPos = node.end; }, diff --git a/Tests/AppKit/CPComboBoxTest.j b/Tests/AppKit/CPComboBoxTest.j new file mode 100644 index 000000000..3d12ed91c --- /dev/null +++ b/Tests/AppKit/CPComboBoxTest.j @@ -0,0 +1,78 @@ +@import +@import +@import + +@import "CPNotificationCenterHelper.j" + +[CPApplication sharedApplication]; + +@implementation CPComboBoxTest : OJTestCase +{ + CPComboBox comboBox; + BOOL wasClicked +} + +- (void)setUp +{ + comboBox = [[CPComboBox alloc] initWithFrame:CGRectMake(0, 0, 200, 30)]; +} + +- (void)testCanCreate +{ + [self assertTrue:!!comboBox]; +} + +- (void)testPublicAccessors +{ + [comboBox setHasVerticalScroller:YES]; + [comboBox setIntercellSpacing:CGSizeMakeZero()]; + [comboBox setButtonBordered:YES]; + [comboBox setItemHeight:30]; + [comboBox setNumberOfVisibleItems:10]; +} + +- (void)testPerformClick +{ + [comboBox setTarget:self]; + [comboBox setAction:@selector(clickMe:)]; + [comboBox performClick:nil]; + [self assertTrue:wasClicked]; +} + +- (void)clickMe:(id)sender +{ + wasClicked = YES; +} + +- (void)testNotificationsRegistered +{ + [self assert:[CPNotificationCenterHelper registeredNotificationsForObserver:comboBox] equals:[] message:@"Notications registered for the CPComboBox in the notification center are wrong"]; + [comboBox setListDelegate:[[_CPPopUpList alloc] initWithDataSource:comboBox]]; + [self assert:[CPNotificationCenterHelper registeredNotificationsForObserver:comboBox] equals:[] message:@"Notications registered for the CPComboBox in the notification center are wrong"]; + + var theWindow = [[CPWindow alloc] initWithContentRect:CGRectMake(0.0, 0.0, 1024.0, 768.0) + styleMask:CPWindowNotSizable]; + + [[theWindow contentView] addSubview:comboBox]; + + var expectedNotifications = [@"_CPPopUpListWillPopUpNotification", @"_CPPopUpListWillDismissNotification", @"_CPPopUpListDidDismissNotification", @"_CPPopUpListItemWasClickedNotification", @"CPTableViewSelectionIsChangingNotification", @"CPTableViewSelectionDidChangeNotification"].sort(); + + [self assert:[CPNotificationCenterHelper registeredNotificationsForObserver:comboBox] equals:expectedNotifications message:@"Notications registered for the CPComboBox in the notification center are wrong"]; + + [comboBox removeFromSuperview]; + [self assert:[CPNotificationCenterHelper registeredNotificationsForObserver:comboBox] equals:[] message:@"Notications registered for the CPComboBox in the notification center are wrong"]; + + [[theWindow contentView] addSubview:comboBox]; + [self assert:[CPNotificationCenterHelper registeredNotificationsForObserver:comboBox] equals:expectedNotifications message:@"Notications registered for the CPComboBox in the notification center are wrong"]; + + [[theWindow contentView] addSubview:comboBox]; + [self assert:[CPNotificationCenterHelper registeredNotificationsForObserver:comboBox] equals:expectedNotifications message:@"Notications registered for the CPComboBox in the notification center are wrong"]; + + [comboBox setListDelegate:nil]; + [self assert:[CPNotificationCenterHelper registeredNotificationsForObserver:comboBox] equals:[] message:@"Notications registered for the CPComboBox in the notification center are wrong"]; + + [comboBox setListDelegate:[[_CPPopUpList alloc] initWithDataSource:comboBox]]; + [self assert:[CPNotificationCenterHelper registeredNotificationsForObserver:comboBox] equals:expectedNotifications message:@"Notications registered for the CPComboBox in the notification center are wrong"]; +} + +@end \ No newline at end of file diff --git a/Tests/AppKit/CPNotificationCenterHelper.j b/Tests/AppKit/CPNotificationCenterHelper.j new file mode 100644 index 000000000..69aaed26c --- /dev/null +++ b/Tests/AppKit/CPNotificationCenterHelper.j @@ -0,0 +1,39 @@ +@import + +@implementation CPNotificationCenterHelper : CPObject +{ +} + ++ (void)registeredNotificationsForObserver:(id)anObserver +{ + var defaultCenter = [CPNotificationCenter defaultCenter], + names = [defaultCenter._namedRegistries keyEnumerator], + notifications = [], + name; + + while ((name = [names nextObject]) !== nil) + { + var notificationRegistry = [defaultCenter._namedRegistries objectForKey:name], + objectObservers = notificationRegistry._objectObservers, + keys = [objectObservers keyEnumerator], + key; + + // Iterate through every set of observers + while ((key = [keys nextObject]) !== nil) + { + var observers = [objectObservers objectForKey:key], + observer = nil, + observersEnumerator = [observers objectEnumerator]; + + while ((observer = [observersEnumerator nextObject]) !== nil) + { + if ([observer observer] == anObserver) + [notifications addObject:name]; + } + } + } + + return notifications.sort(); +} + +@end diff --git a/Tests/AppKit/CPScrollViewTest.j b/Tests/AppKit/CPScrollViewTest.j index d95839438..379cea865 100644 --- a/Tests/AppKit/CPScrollViewTest.j +++ b/Tests/AppKit/CPScrollViewTest.j @@ -1,5 +1,9 @@ @import +@import "CPNotificationCenterHelper.j" + +[CPApplication sharedApplication]; + @implementation CPScrollViewTest : OJTestCase { } @@ -256,6 +260,24 @@ [self assertPoint:CGPointMake(0, 0) equals:visibleRect.origin message:@"VisibleRect origin not at top left corner again"]; } +-(void)testNotificationsRegistered +{ + var scrollView = [[CPScrollView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)], + theWindow = [[CPWindow alloc] initWithContentRect:CGRectMake(0.0, 0.0, 1024.0, 768.0) + styleMask:CPWindowNotSizable]; + + [self assert:[CPNotificationCenterHelper registeredNotificationsForObserver:scrollView] equals:[] message:@"Notications registered for the scrollView in the notification center are wrong"]; + + [[theWindow contentView] addSubview:scrollView]; + [self assert:[CPNotificationCenterHelper registeredNotificationsForObserver:scrollView] equals:[@"CPScrollerStyleGlobalChangeNotification"] message:@"Notications registered for the scrollView in the notification center are wrong"]; + + [[theWindow contentView] addSubview:scrollView]; + [self assert:[CPNotificationCenterHelper registeredNotificationsForObserver:scrollView] equals:[@"CPScrollerStyleGlobalChangeNotification"] message:@"Notications registered for the scrollView in the notification center are wrong"]; + + [scrollView removeFromSuperview]; + [self assert:[CPNotificationCenterHelper registeredNotificationsForObserver:scrollView] equals:[] message:@"Notications registered for the scrollView in the notification center are wrong"]; +} + - (void)assertPoint:(CGPoint)expected equals:(CGPoint)actual message:(CPString)message { [self assert:expected.x equals:actual.x message:@"X: " + message]; diff --git a/Tests/AppKit/CPTableViewTest.j b/Tests/AppKit/CPTableViewTest.j index c1c5d0180..ede7ed1e9 100644 --- a/Tests/AppKit/CPTableViewTest.j +++ b/Tests/AppKit/CPTableViewTest.j @@ -1,5 +1,7 @@ @import +@import "CPNotificationCenterHelper.j" + [CPApplication sharedApplication]; @implementation CPTableViewTest : OJTestCase @@ -303,6 +305,36 @@ [self assertTrue:[table bounds].size.width >= 200]; } +-(void)testNotificationsRegistered +{ + [self assert:[CPNotificationCenterHelper registeredNotificationsForObserver:tableView] equals:[@"_CPWindowDidChangeFirstResponderNotification"] message:@"Notications registered for the tableView in the notification center are wrong"]; + + [tableView removeFromSuperview]; + [self assert:[CPNotificationCenterHelper registeredNotificationsForObserver:tableView] equals:[] message:@"Notications registered for the tableView in the notification center are wrong"]; + + [[theWindow contentView] addSubview:tableView]; + [self assert:[CPNotificationCenterHelper registeredNotificationsForObserver:tableView] equals:[@"_CPWindowDidChangeFirstResponderNotification"] message:@"Notications registered for the tableView in the notification center are wrong"]; + + [[theWindow contentView] addSubview:tableView]; + [self assert:[CPNotificationCenterHelper registeredNotificationsForObserver:tableView] equals:[@"_CPWindowDidChangeFirstResponderNotification"] message:@"Notications registered for the tableView in the notification center are wrong"]; + + + var scrollView = [[CPScrollView alloc] initWithFrame:CGRectMake(0, 0, 100.0, 100.0)], + expectedNotifications = [@"_CPWindowDidChangeFirstResponderNotification", @"CPViewFrameDidChangeNotification", @"CPViewBoundsDidChangeNotification"].sort(); + + [scrollView setDocumentView:tableView]; + + [[theWindow contentView] addSubview:scrollView]; + [self assert:[CPNotificationCenterHelper registeredNotificationsForObserver:tableView] equals:expectedNotifications message:@"Notications registered for the tableView in the notification center are wrong"]; + + [[theWindow contentView] addSubview:scrollView]; + [self assert:[CPNotificationCenterHelper registeredNotificationsForObserver:tableView] equals:expectedNotifications message:@"Notications registered for the tableView in the notification center are wrong"]; + + [scrollView removeFromSuperview]; + [self assert:[CPNotificationCenterHelper registeredNotificationsForObserver:tableView] equals:[] message:@"Notications registered for the tableView in the notification center are wrong"]; + +} + @end @implementation FirstResponderConfigurableTableView : CPTableView diff --git a/Tests/AppKit/CPWindowTest.j b/Tests/AppKit/CPWindowTest.j index 2244a28d9..cf51e8cba 100644 --- a/Tests/AppKit/CPWindowTest.j +++ b/Tests/AppKit/CPWindowTest.j @@ -128,4 +128,12 @@ [self assertTrue:[[[self window] representedURL] class] === [CPURL class]]; } +- (void)testOrderingMethod +{ + [_window orderFront:self]; + [_window orderBack:self]; + [_window orderFront:self]; + [_window orderOut:self]; +} + @end diff --git a/Tests/Foundation/CPErrorTest.j b/Tests/Foundation/CPErrorTest.j new file mode 100644 index 000000000..7e482a3a2 --- /dev/null +++ b/Tests/Foundation/CPErrorTest.j @@ -0,0 +1,43 @@ +@import +@import + +@implementation CPErrorTest : OJTestCase +{ +} + +- (void)testInstanceInstantiation +{ + var err = [[CPError alloc] initWithDomain:CPCappuccinoErrorDomain + code:-10 + userInfo:nil]; + [self assertNotNull:err]; +} + +- (void)testClassInstantiation +{ + var err = [CPError errorWithDomain:CPCappuccinoErrorDomain + code:-10 + userInfo:nil]; + [self assertNotNull:err]; +} + +- (void)testUserInfoDict +{ + var userInfo = @{ + CPLocalizedDescriptionKey: @"A localized error description", + CPUnderlyingErrorKey: @"An underlying error", + CPLocalizedFailureReasonErrorKey: @"A localized error reason", + CPLocalizedRecoverySuggestionErrorKey: @"The world is about to explode. You can choose to ignore this.", + CPLocalizedRecoveryOptionsErrorKey: ["Cry", "Ignore"] + }, + err = [CPError errorWithDomain:CPCappuccinoErrorDomain + code:-10 + userInfo:userInfo]; + + [self assertNotNull:[err userInfo]]; + [self assert:[err localizedDescription] equals:@"A localized error description"]; + [self assert:[err localizedRecoveryOptions] equals:["Cry", "Ignore"]]; + [self assertNull:[err recoveryAttempter]]; +} + +@end \ No newline at end of file diff --git a/Tests/Foundation/CPOperationQueueTest.j b/Tests/Foundation/CPOperationQueueTest.j index 0cc5de666..5005a1ae7 100644 --- a/Tests/Foundation/CPOperationQueueTest.j +++ b/Tests/Foundation/CPOperationQueueTest.j @@ -16,6 +16,33 @@ globalResults = []; @end +@implementation TestCancelOperation : CPOperation +{ + BOOL _started @accessors(getter=didStart); + BOOL _mained @accessors(getter=didMain); +} + +- (id)init +{ + self = [super init]; + _started = NO; + _mained = NO; + return self; +} + +- (void)main +{ + _mained = YES; +} + +- (void)start +{ + [super start]; + _started = YES; +} + +@end + @implementation TestObserver : CPObject { CPArray changedKeyPaths @accessors; @@ -167,4 +194,27 @@ globalResults = []; [self assert:@"name" equals:[[obs changedKeyPaths] objectAtIndex:4]]; } +- (void)testCancelledOperationDoesStart +{ + var op = [[TestCancelOperation alloc] init], + queue = [[CPOperationQueue alloc] init]; + + [self assertFalse:[op isCancelled]]; + [self assertFalse:[op isFinished]]; + [self assertFalse:[op didMain]]; + [self assertFalse:[op didStart]]; + + [op cancel]; + + [self assertTrue:[op isCancelled]]; + [self assertFalse:[op isFinished]]; + + [queue addOperations:[op] waitUntilFinished:YES]; + + [self assertFalse:[op didMain]]; + [self assertTrue:[op didStart]]; + [self assertTrue:[op isCancelled]]; + [self assertTrue:[op isFinished]]; +} + @end \ No newline at end of file diff --git a/Tests/Foundation/CPOperationTest.j b/Tests/Foundation/CPOperationTest.j index 1c1beef56..ea424ef56 100644 --- a/Tests/Foundation/CPOperationTest.j +++ b/Tests/Foundation/CPOperationTest.j @@ -180,4 +180,21 @@ [self assert:@"isCancelled" equals:[[obs changedKeyPaths] objectAtIndex:9]]; } +- (void)testCancelledOperationIsFinished +{ + var results = @[], + funcOp = [CPFunctionOperation functionOperationWithFunction:function() {[results addObject:"funcOp"];}]; + + [funcOp cancel]; + [self assertTrue:[funcOp isCancelled]]; + [self assertFalse:[funcOp isFinished]]; + + [funcOp start]; + + [self assertTrue:[funcOp isCancelled]]; + [self assertTrue:[funcOp isFinished]]; + + [self assertTrue:([results count] == 0)]; +} + @end diff --git a/Tests/Objective-J/CFErrorTest.j b/Tests/Objective-J/CFErrorTest.j new file mode 100644 index 000000000..cea9ba69a --- /dev/null +++ b/Tests/Objective-J/CFErrorTest.j @@ -0,0 +1,60 @@ +@import + + +@implementation CFErrorTest : OJTestCase + +- (void)testCreate +{ + var err = new CFError(); + [self assertNotNull:err]; +} + +- (void)testCreateWithParams +{ + var err = new CFError(kCFErrorDomainCappuccino, -1000, nil); + [self assertNotNull:err]; + [self assert:-1000 equals:err.code()]; +} + +- (void)testCreateGlobal +{ + var err = CFErrorCreate(kCFErrorDomainCappuccino, -1000, nil); + [self assertNotNull:err]; + + [self assert:kCFErrorDomainCappuccino equals:err.domain()]; + [self assert:-1000 equals:err.code()]; + [self assert:@"CPCappuccinoErrorDomain" equals:CFErrorGetDomain(err)]; +} + +- (void)testCreateWithUserInfoKeysAndValues +{ + var err = CFErrorCreateWithUserInfoKeysAndValues(kCFErrorDomainCappuccino, -1000, [kCFErrorLocalizedDescriptionKey, kCFErrorDescriptionKey], [@"A localized description", @"An error description"], 2); + [self assertNotNull:err]; + + var info = err.userInfo(); + [self assert:2 equals:info.count()]; +} + +- (void)testDescriptionCaseOne +{ + // Description case 1: Localized Key set + var err = CFErrorCreateWithUserInfoKeysAndValues(kCFErrorDomainCappuccino, -1000, [kCFErrorLocalizedDescriptionKey], [@"A localized Description Key"], 1); + [self assert:@"A localized Description Key" equals:err.description()]; + [self assert:@"A localized Description Key" equals:CFErrorCopyDescription(err)]; +} + +- (void)testDescriptionCaseTwo +{ + // Case 2: Reason set; description generated + var err = CFErrorCreateWithUserInfoKeysAndValues(kCFErrorDomainCappuccino, -1000, [kCFErrorLocalizedFailureReasonKey], [@"A localized reason"], 1); + [self assert:@"The operation couldn\u2019t be completed. A localized reason" equals:err.description()]; +} + +- (void)testDescriptionCaseThree +{ + // Case 3: Final fall-back. + var err = CFErrorCreateWithUserInfoKeysAndValues(kCFErrorDomainCappuccino, -1000, [kCFErrorDescriptionKey], [@"A description key"], 1); + [self assert:@"The operation couldn\u2019t be completed. (error -1000 - A description key)" equals:err.description()]; +} + +@end \ No newline at end of file diff --git a/Tests/Objective-J/Preprocessor/OutputTests/Misc/ref-self.j b/Tests/Objective-J/Preprocessor/OutputTests/Misc/ref-self.j new file mode 100644 index 000000000..d94fa2450 --- /dev/null +++ b/Tests/Objective-J/Preprocessor/OutputTests/Misc/ref-self.j @@ -0,0 +1,11 @@ +@implementation TC +{ + id _control; +} + +- (id)a +{ + @ref(_control); +} + +@end diff --git a/Tests/Objective-J/Preprocessor/OutputTests/Misc/ref-self.js b/Tests/Objective-J/Preprocessor/OutputTests/Misc/ref-self.js new file mode 100644 index 000000000..778d931d6 --- /dev/null +++ b/Tests/Objective-J/Preprocessor/OutputTests/Misc/ref-self.js @@ -0,0 +1,9 @@ +{var the_class = objj_allocateClassPair(Nil, "TC"), +meta_class = the_class.isa;class_addIvars(the_class, [new objj_ivar("_control")]);objj_registerClassPair(the_class); +class_addMethods(the_class, [new objj_method(sel_getUid("a"), function $TC__a(self, _cmd) +{ + function(__input) { if (arguments.length) return self._control = __input; return self._control; }; +} + +,["id"])]); +} diff --git a/Tests/Objective-J/Preprocessor/OutputTests/OutputTest.j b/Tests/Objective-J/Preprocessor/OutputTests/OutputTest.j index a94c74f3b..fcd96ba3c 100644 --- a/Tests/Objective-J/Preprocessor/OutputTests/OutputTest.j +++ b/Tests/Objective-J/Preprocessor/OutputTests/OutputTest.j @@ -22,6 +22,7 @@ var FILENAMES = [ "Misc/regex-simple-char-classes", "Misc/empty-loops", "Misc/empty-statements", + "Misc/ref-self", ]; @implementation OutputTest : OJTestCase diff --git a/Tools/capp/Resources/Templates/Application/index-debug.html b/Tools/capp/Resources/Templates/Application/index-debug.html index 525fedab5..d862fd07e 100644 --- a/Tools/capp/Resources/Templates/Application/index-debug.html +++ b/Tools/capp/Resources/Templates/Application/index-debug.html @@ -102,7 +102,7 @@ width: 100%; /* Put it at the bottom of the stack so it doesn't interfere with UI */ - z-index: -1000; + z-index: 0; } #cappuccino-body .container { diff --git a/Tools/capp/Resources/Templates/Application/index.html b/Tools/capp/Resources/Templates/Application/index.html index 9dec4e8db..414301b1e 100644 --- a/Tools/capp/Resources/Templates/Application/index.html +++ b/Tools/capp/Resources/Templates/Application/index.html @@ -72,7 +72,7 @@ width: 100%; /* Put it at the bottom of the stack so it doesn't interfere with UI */ - z-index: -1000; + z-index: 0; } #cappuccino-body .container { diff --git a/Tools/capp/Resources/Templates/NibApplication/index-debug.html b/Tools/capp/Resources/Templates/NibApplication/index-debug.html index 525fedab5..d862fd07e 100644 --- a/Tools/capp/Resources/Templates/NibApplication/index-debug.html +++ b/Tools/capp/Resources/Templates/NibApplication/index-debug.html @@ -102,7 +102,7 @@ width: 100%; /* Put it at the bottom of the stack so it doesn't interfere with UI */ - z-index: -1000; + z-index: 0; } #cappuccino-body .container { diff --git a/Tools/capp/Resources/Templates/NibApplication/index.html b/Tools/capp/Resources/Templates/NibApplication/index.html index 9dec4e8db..414301b1e 100644 --- a/Tools/capp/Resources/Templates/NibApplication/index.html +++ b/Tools/capp/Resources/Templates/NibApplication/index.html @@ -72,7 +72,7 @@ width: 100%; /* Put it at the bottom of the stack so it doesn't interfere with UI */ - z-index: -1000; + z-index: 0; } #cappuccino-body .container {