From 00f470763f4648dbe9228af7f09ce108ba625d7b Mon Sep 17 00:00:00 2001 From: Ross Boucher Date: Sat, 23 Jan 2010 18:08:45 -0800 Subject: [PATCH 01/39] Make CPFlashMovie CPCoding compliant. --- AppKit/CPFlashMovie.j | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/AppKit/CPFlashMovie.j b/AppKit/CPFlashMovie.j index b666c2391..89534d04f 100644 --- a/AppKit/CPFlashMovie.j +++ b/AppKit/CPFlashMovie.j @@ -58,4 +58,22 @@ return self; } +@end + +var CPFlashMovieFileNameKey = "CPFlashMovieFileNameKey"; + +@implementation CPFlashMovie (CPCoding) + +- (id)initWithCoder:(CPCoder)aCoder +{ + _fileName = [aCoder decodeObjectForKey:CPFlashMovieFileNameKey]; + + return self; +} + +- (void)encodeWithCoder:(CPCoder)aCoder +{ + [aCoder encodeObject:_fileName forKey:CPFlashMovieFileNameKey]; +} + @end \ No newline at end of file From dc0439227ee7e78485e8f5199de53c4380491960 Mon Sep 17 00:00:00 2001 From: Ross Boucher Date: Sat, 23 Jan 2010 23:28:39 -0800 Subject: [PATCH 02/39] Add an accessor for the filename on CPFlashMovie --- AppKit/CPFlashMovie.j | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/AppKit/CPFlashMovie.j b/AppKit/CPFlashMovie.j index 89534d04f..cf7c5cd08 100644 --- a/AppKit/CPFlashMovie.j +++ b/AppKit/CPFlashMovie.j @@ -58,6 +58,11 @@ return self; } +- (CPString)fileName +{ + return _fileName; +} + @end var CPFlashMovieFileNameKey = "CPFlashMovieFileNameKey"; From dd4735026a4f9bf56dd8888a269b23355e42ae1a Mon Sep 17 00:00:00 2001 From: Randall Luecke Date: Sun, 24 Jan 2010 02:31:01 -0500 Subject: [PATCH 03/39] Reset position of the tableview in tabletest --- Tests/Manual/TableTest/AppController.j | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/Manual/TableTest/AppController.j b/Tests/Manual/TableTest/AppController.j index e4f58c534..f9fbb76bd 100644 --- a/Tests/Manual/TableTest/AppController.j +++ b/Tests/Manual/TableTest/AppController.j @@ -87,7 +87,7 @@ CPLogRegister(CPLogConsole); [tableView setColumnAutoresizingStyle:CPTableViewUniformColumnAutoresizingStyle]; - var scrollView = [[CPScrollView alloc] initWithFrame:CGRectMake(100, 100, CGRectGetWidth([view bounds]), CGRectGetHeight([view bounds]))]; + var scrollView = [[CPScrollView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth([view bounds]), CGRectGetHeight([view bounds]))]; [tableView setRowHeight:22.0]; [scrollView setDocumentView:tableView]; [scrollView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable]; From c6a4eb28a88c04d49500f8ee582dab40dc65c623 Mon Sep 17 00:00:00 2001 From: Klaas Pieter Annema Date: Fri, 22 Jan 2010 20:44:58 +0100 Subject: [PATCH 04/39] added cpviewanimation --- AppKit/CPViewAnimation.j | 184 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 184 insertions(+) create mode 100644 AppKit/CPViewAnimation.j diff --git a/AppKit/CPViewAnimation.j b/AppKit/CPViewAnimation.j new file mode 100644 index 000000000..5950d9609 --- /dev/null +++ b/AppKit/CPViewAnimation.j @@ -0,0 +1,184 @@ +/* + * CPViewAnimation.j + * + * Created by Klaas Pieter Annema on September 3, 2009. + * Copyright 2009, Sofa BV + */ + + +@import + +CPViewAnimationTargetKey = @"CPViewAnimationTarget"; +CPViewAnimationStartFrameKey = @"CPViewAnimationStartFrame"; +CPViewAnimationEndFrameKey = @"CPViewAnimationEndFrame"; +CPViewAnimationEffectKey = @"CPViewAnimationEffect"; + +CPViewAnimationFadeInEffect = @"CPViewAnimationFadeIn"; +CPViewAnimationFadeOutEffect = @"CPViewAnimationFadeOut"; + +@implementation CPViewAnimation : CPAnimation +{ + CPArray _viewAnimations; +} + +- (id)initWithViewAnimations:(CPArray)viewAnimations +{ + if(self = [super initWithDuration:0.5 animationCurve:CPAnimationLinear]) + { + [self setViewAnimations:viewAnimations]; + } + return self; +} + +// ============= +// = ANIMATION = +// ============= +- (void)startAnimation +{ + var animationIndex = [_viewAnimations count]; + + while (animationIndex--) + { + var dictionary = [_viewAnimations objectAtIndex:animationIndex]; + + var view = [self _targetView:dictionary], + startFrame = [self _startFrame:dictionary]; + + [view setFrame:startFrame]; + + var effect = [self _effect:dictionary]; + + if (effect === CPViewAnimationFadeInEffect) + { + [view setAlphaValue:0.0]; + [view setHidden:NO]; + } + else if (effect === CPViewAnimationFadeOutEffect) + [view setAlphaValue:1.0]; + } + + [super startAnimation]; +} + +- (void)setCurrentProgress:(NSAnimationProgress)progress +{ + [super setCurrentProgress:progress]; + + var animationIndex = [_viewAnimations count]; + while (animationIndex--) + { + var dictionary = [_viewAnimations objectAtIndex:animationIndex]; + + // Update the view's frame + var view = [self _targetView:dictionary] + startFrame = [self _startFrame:dictionary] + endFrame = [self _endFrame:dictionary] + differenceFrame = CPRectMakeZero(); + + differenceFrame.origin.x = endFrame.origin.x - startFrame.origin.x; + differenceFrame.origin.y = endFrame.origin.y - startFrame.origin.y; + differenceFrame.size.width = endFrame.size.width - startFrame.size.width; + differenceFrame.size.height = endFrame.size.height - startFrame.size.height; + + var intermediateFrame = CPRectMakeZero(); + intermediateFrame.origin.x = startFrame.origin.x + differenceFrame.origin.x * progress; + intermediateFrame.origin.y = startFrame.origin.y + differenceFrame.origin.y * progress; + intermediateFrame.size.width = startFrame.size.width + differenceFrame.size.width * progress; + intermediateFrame.size.height = startFrame.size.height + differenceFrame.size.height * progress; + + [view setFrame:intermediateFrame]; + + // Update the view's alpha value + var effect = [self _effect:dictionary]; + + if (effect === CPViewAnimationFadeInEffect) + [view setAlphaValue:1.0 * progress]; + else if (effect === CPViewAnimationFadeOutEffect) + [view setAlphaValue:1.0 + ( 0.0 - 1.0 ) * progress]; + + if (progress === 1.0) + [view setHidden:CPRectIsNull(endFrame) || [view alphaValue] === 0.0]; + } +} + +- (void)stopAnimation +{ + var animationIndex = [_viewAnimations count]; + while (animationIndex--) + { + var dictionary = [_viewAnimations objectAtIndex:animationIndex]; + + var view = [self _targetView:dictionary], + endFrame = [self _endFrame:dictionary]; + + [view setFrame:endFrame]; + + var effect = [self _effect:dictionary]; + + if (effect === CPViewAnimationFadeInEffect) + [view setAlphaValue:1.0]; + else if (effect === CPViewAnimationFadeOutEffect) + [view setAlphaValue:0.0]; + + [view setHidden:CPRectIsNull(endFrame) || [view alphaValue] === 0.0]; + } + + [super stopAnimation]; +} + +// =============== +// = CONVENIENCE = +// =============== +- (id)_targetView:(CPDictionary)dictionary +{ + var targetView = [dictionary valueForKey:CPViewAnimationTargetKey]; + + if (!targetView) + [CPException raise:CPInternalInconsistencyException reason:[CPString stringWithFormat:@"view animation: %@ does not have a target view", [dictionary description]]]; + + return targetView; +} + +- (CPRect)_startFrame:(CPDictionary)dictionary +{ + var startFrame = [dictionary valueForKey:CPViewAnimationStartFrameKey]; + + if (!startFrame) + return [[self _targetView:dictionary] frame]; + + return startFrame; +} + +- (CPRect)_endFrame:(CPDictionary)dictionary +{ + var endFrame = [dictionary valueForKey:CPViewAnimationEndFrameKey]; + + if (!endFrame) + return [[self _targetView:dictionary] frame]; + + return endFrame; +} + +- (CPString)_effect:(CPDictionary)dictionary +{ + return [dictionary valueForKey:CPViewAnimationEffectKey]; +} + +// ============= +// = ACCESSORS = +// ============= +- (CPArray)viewAnimations +{ + return _viewAnimations; +} + +- (void)setViewAnimations:(CPArray)viewAnimations +{ + if (viewAnimations != _viewAnimations) + { + [self stopAnimation]; + _viewAnimations = [viewAnimations copy]; + } +} + +@end From c291ad83f17eeeb401abebad3b7706b1abf78fd4 Mon Sep 17 00:00:00 2001 From: Klaas Pieter Annema Date: Sun, 24 Jan 2010 02:37:25 -0500 Subject: [PATCH 05/39] Implemented visual feedback when dragging in CPTableView Conflicts: AppKit/CPTableView.j --- AppKit/CPTableView.j | 161 +++++++++++++++++++------------------------ 1 file changed, 70 insertions(+), 91 deletions(-) diff --git a/AppKit/CPTableView.j b/AppKit/CPTableView.j index 8cfcd9409..8085e34e6 100644 --- a/AppKit/CPTableView.j +++ b/AppKit/CPTableView.j @@ -1432,72 +1432,56 @@ window.setTimeout(function(){ return YES; } -- (CPImage)dragImageForRowsWithIndexes:(CPIndexSet)dragRows tableColumns:(CPArray)theTableColumns event:(CPEvent)dragEvent offset:(CPPointPointer)dragImageOffset +- (CPImage)dragImageForRowsWithIndexes:(CPIndexSet)dragRows + tableColumns:(CPArray)theTableColumns + event:(CPEvent)dragEvent + offset:(CPPointPointer)dragImageOffset { - // FIXME: by default we should construct an image/view of the selcted rows which are visible - //var theDragView = [[CPImageView alloc] initWithFrame:_CGRectMake(0,0,32,32)]; - //[theDragView setImage:[[CPImage alloc] initWithContentsOfFile:@"Frameworks/AppKit/Resources/GenericFile.png" size:CGSizeMake(32,32)]]; - var image = [[CPImage alloc] initWithContentsOfFile:@"Frameworks/AppKit/Resources/GenericFile.png" size:CGSizeMake(32,32)]; - //return theDragView; - return image; + return [[CPImage alloc] initWithContentsOfFile:@"Frameworks/AppKit/Resources/GenericFile.png" size:CGSizeMake(32,32)]; } -- (CPImage)dragViewForRowsWithIndexes:(CPIndexSet)dragRows tableColumns:(CPArray)theTableColumns event:(CPEvent)dragEvent offset:(CPPointPointer)dragImageOffset +- (CPView)dragViewForRowsWithIndexes:(CPIndexSet)theDraggedRows + tableColumns:(CPArray)theTableColumns + event:(CPEvent)theDragEvent + offset:(CPPoint)dragViewOffset { - var draggedRowsArray = [], - colCount = [theTableColumns count], - rowsCount = [dragRows count], - firstRowIndex = [dragRows firstIndex], - rowIndexesLength = [dragRows lastIndex] - firstRowIndex + 1, - - firstRowRect = [self rectOfRow:firstRowIndex], - exposedMinX = CGRectGetMinX([self exposedClipRect]), - dragViewWidth = CGRectGetWidth([self exposedClipRect]), - dragViewHeight = rowIndexesLength * _rowHeight, - - location = [self convertPoint:[dragEvent locationInWindow] fromView:nil]; - - dragImageOffset.x = exposedMinX + CGRectGetMinX(firstRowRect) - location.x + dragViewWidth/2; - dragImageOffset.y = CGRectGetMinY(firstRowRect) - location.y + dragViewHeight/2; - - var draggedView = [[CPView alloc] initWithFrame:CGRectMake(0, 0, dragViewWidth, dragViewHeight)]; - - [dragRows getIndexes:draggedRowsArray maxCount:-1 inIndexRange:CPMakeRange(firstRowIndex, rowIndexesLength)]; - - // calculate the dragImageOffset : we want the ghost row to be where the real row is. - - for (var i = 0; i < rowsCount ; i++) - { - var rowIndex = draggedRowsArray[i]; - var dataViewOriginY = (rowIndex - firstRowIndex) * [self rowHeight]; - for (var c = 0; c < colCount; c++) - { - var column = [theTableColumns objectAtIndex:c], - tableColumnUID = [column UID], - dataView = _dataViewsForTableColumns[tableColumnUID][rowIndex]; - - var frame = [dataView frame]; - frame.origin.y = dataViewOriginY; - frame.origin.x -= exposedMinX; - - // Mega hack: The default CPView impl. doesn't implement -copy so we copy the innerHTML - // It ok because it's a temporary view and we won't have to interact with it later ... - // ... except we want the text to be normal when it's a selected row (unset the highlighted theme state) - // and that does'nt work. - // Possible fix: only for default dataviews (CPTextField), we can implement -copy and unset the state - var html = dataView._DOMElement.innerHTML; - var dataViewCopy = [[CPView alloc] initWithFrame:frame]; - dataViewCopy._DOMElement.innerHTML = html; - - // This works (don't know how ?!). Until we fix the previous bug, we can stay with a white transparent bg - // so at least we have a visible feedback of the dragged row. Maybe less opaque ... - [dataViewCopy setBackgroundColor:[CPColor colorWithWhite:1 alpha:0.7]]; - - [draggedView addSubview:dataViewCopy]; - } - } - - return draggedView; + var size = [self bounds].size, + view = [[CPView alloc] initWithFrame:CPMakeRect(dragViewOffset.x, dragViewOffset.y, size.width, size.height)]; + + [view setBackgroundColor:[CPColor clearColor]]; + [view setAlphaValue:0.7]; + + // We have to fetch all the data views for the selected rows and columns + // After that we can copy these add them to a transparent drag view and use that drag view + // to make it appear we are dragging images of those rows (as you would do in regular Cocoa) + var firstExposedColumn = [_exposedColumns firstIndex], + exposedLength = [_exposedColumns lastIndex] - firstExposedColumn + 1, + columns = []; + + [_exposedColumns getIndexes:columns maxCount:-1 inIndexRange:CPMakeRange(firstExposedColumn, exposedLength)]; + + var columnIndex = [columns count], + draggedDataViews = [], + dragViewHeight = 0.0; + + while (columnIndex--) { + var column = [_tableColumns objectAtIndex:columnIndex], + yOffset = 0, + rowIndex = CPNotFound; + + while ((rowIndex = [_selectedRowIndexes indexGreaterThanIndex:rowIndex]) !== CPNotFound) + { + var dataView = [self _newDataViewForRow:rowIndex tableColumn:column]; + + [dataView setBackgroundColor:[CPColor clearColor]]; + [dataView setFrame:[self frameOfDataViewAtColumn:columnIndex row:rowIndex]]; + [dataView setObjectValue:[self _objectValueForTableColumn:column row:rowIndex]]; + + [view addSubview:dataView]; + } + } + + return view; } - (void)setDraggingSourceOperationMask:(CPDragOperation)mask forLocal:(BOOL)isLocal @@ -2200,35 +2184,30 @@ window.setTimeout(function(){ if([self canDragRowsWithIndexes:_draggedRowIndexes atPoint:aPoint] && [_dataSource tableView:self writeRowsWithIndexes:_draggedRowIndexes toPasteboard:pboard]) { - //create drag view/image - var draggedColumns = [_tableColumns objectsAtIndexes:_exposedColumns]; - - var dragViewOffset = CGPointMakeZero(); - var theDragView = [self dragViewForRowsWithIndexes:_draggedRowIndexes tableColumns:draggedColumns event:[CPApp currentEvent] offset:dragViewOffset]; - - //we should begin the drag opperation here - if (theDragView != nil) // special behavior: if the subclass returns nil, ask the subclass for an image - { - var bounds = [theDragView bounds]; - // Cocoa doc:"A dragImageOffset of NSZeroPoint will cause the image to be centered under the cursor." - var dragViewLocation = CGPointMake(aPoint.x + dragViewOffset.x - CGRectGetWidth(bounds)/2, aPoint.y + dragViewOffset.y - CGRectGetHeight(bounds)/2); - [self dragView:theDragView at:dragViewLocation offset:CGSizeMakeZero() event:[CPApp currentEvent] pasteboard:pboard source:self slideBack:YES]; - } - else - { - var dragImageOffset = CGPointMakeZero(); - var theDragImage = [self dragImageForRowsWithIndexes:_draggedRowIndexes tableColumns:draggedColumns event:[CPApp currentEvent] offset:dragImageOffset]; - - var imageSize = [theDragImage size]; - var dragImageLocation = CGPointMake(aPoint.x + dragImageOffset.x - imageSize.width/2, aPoint.y + dragImageOffset.y - imageSize.height/2); - - [self dragImage:theDragImage at:dragImageLocation offset:CGSizeMakeZero() event:[CPApp currentEvent] pasteboard:pboard source:self slideBack:YES]; - } - //console.log([[CPDragServer sharedDragServer] draggingSource]) - // FIX ME: figure out what to do with the damn operation mask, does capp not support this yet? - // FIX ME: set the operation mask to _dragOperationDefaultMask - - //stop tracking + var currentEvent = [CPApp currentEvent], + offset = CPPointMakeZero(); + + // We deviate from the default Cocoa implementation here by asking for a view in stead of an image + // We support both, but the view prefered over the image because we can mimic the rows we are dragging + // by re-creating the data views for the dragged rows + var view = [self dragViewForRowsWithIndexes:_draggedRowIndexes + tableColumns:_exposedColumns + event:currentEvent + offset:CPPointMakeZero()]; + + if (!view) { + var image = [self dragImageForRowsWithIndexes:_draggedRowIndexes + tableColumns:_exposedColumns + event:currentEvent + offset:CPPointMakeZero()]; + + view = [[CPImageView alloc] initWithFrame:CPMakeRect(aPoint.x, aPoint.y, [image size].width, [image size].height)]; + [view setImage:image]; + + offset = aPoint; + } + + [self dragView:view at:offset offset:CPPointMakeZero() event:[CPApp currentEvent] pasteboard:pboard source:self slideBack:YES]; return NO; } } From 34a5d9e61b6c913cefc931b29e76a8577e9fe48e Mon Sep 17 00:00:00 2001 From: Ross Boucher Date: Sun, 24 Jan 2010 00:10:04 -0800 Subject: [PATCH 06/39] Fix spacing issues in CPViewAnimation. --- AppKit/CPViewAnimation.j | 241 +++++++++++++++++++-------------------- 1 file changed, 118 insertions(+), 123 deletions(-) diff --git a/AppKit/CPViewAnimation.j b/AppKit/CPViewAnimation.j index 5950d9609..692523584 100644 --- a/AppKit/CPViewAnimation.j +++ b/AppKit/CPViewAnimation.j @@ -1,11 +1,25 @@ /* - * CPViewAnimation.j + * CPFlashView.j + * AppKit * * Created by Klaas Pieter Annema on September 3, 2009. * Copyright 2009, Sofa BV + * + * 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 */ - @import CPViewAnimationTargetKey = @"CPViewAnimationTarget"; @@ -18,167 +32,148 @@ CPViewAnimationFadeOutEffect = @"CPViewAnimationFadeOut"; @implementation CPViewAnimation : CPAnimation { - CPArray _viewAnimations; + CPArray _viewAnimations; } - (id)initWithViewAnimations:(CPArray)viewAnimations { - if(self = [super initWithDuration:0.5 animationCurve:CPAnimationLinear]) - { - [self setViewAnimations:viewAnimations]; - } - return self; + if (self = [super initWithDuration:0.5 animationCurve:CPAnimationLinear]) + { + [self setViewAnimations:viewAnimations]; + } + + return self; } -// ============= -// = ANIMATION = -// ============= - (void)startAnimation { - var animationIndex = [_viewAnimations count]; - - while (animationIndex--) - { - var dictionary = [_viewAnimations objectAtIndex:animationIndex]; - - var view = [self _targetView:dictionary], - startFrame = [self _startFrame:dictionary]; - - [view setFrame:startFrame]; - - var effect = [self _effect:dictionary]; - - if (effect === CPViewAnimationFadeInEffect) - { - [view setAlphaValue:0.0]; - [view setHidden:NO]; - } - else if (effect === CPViewAnimationFadeOutEffect) - [view setAlphaValue:1.0]; - } - - [super startAnimation]; + var animationIndex = [_viewAnimations count]; + while (animationIndex--) + { + var dictionary = [_viewAnimations objectAtIndex:animationIndex], + view = [self _targetView:dictionary], + startFrame = [self _startFrame:dictionary]; + + [view setFrame:startFrame]; + + var effect = [self _effect:dictionary]; + if (effect === CPViewAnimationFadeInEffect) + { + [view setAlphaValue:0.0]; + [view setHidden:NO]; + } + else if (effect === CPViewAnimationFadeOutEffect) + [view setAlphaValue:1.0]; + } + + [super startAnimation]; } - (void)setCurrentProgress:(NSAnimationProgress)progress { - [super setCurrentProgress:progress]; - - var animationIndex = [_viewAnimations count]; - while (animationIndex--) - { - var dictionary = [_viewAnimations objectAtIndex:animationIndex]; - - // Update the view's frame - var view = [self _targetView:dictionary] - startFrame = [self _startFrame:dictionary] - endFrame = [self _endFrame:dictionary] - differenceFrame = CPRectMakeZero(); - - differenceFrame.origin.x = endFrame.origin.x - startFrame.origin.x; - differenceFrame.origin.y = endFrame.origin.y - startFrame.origin.y; - differenceFrame.size.width = endFrame.size.width - startFrame.size.width; - differenceFrame.size.height = endFrame.size.height - startFrame.size.height; - - var intermediateFrame = CPRectMakeZero(); - intermediateFrame.origin.x = startFrame.origin.x + differenceFrame.origin.x * progress; - intermediateFrame.origin.y = startFrame.origin.y + differenceFrame.origin.y * progress; - intermediateFrame.size.width = startFrame.size.width + differenceFrame.size.width * progress; - intermediateFrame.size.height = startFrame.size.height + differenceFrame.size.height * progress; - - [view setFrame:intermediateFrame]; - - // Update the view's alpha value - var effect = [self _effect:dictionary]; - - if (effect === CPViewAnimationFadeInEffect) - [view setAlphaValue:1.0 * progress]; - else if (effect === CPViewAnimationFadeOutEffect) - [view setAlphaValue:1.0 + ( 0.0 - 1.0 ) * progress]; - - if (progress === 1.0) - [view setHidden:CPRectIsNull(endFrame) || [view alphaValue] === 0.0]; - } + [super setCurrentProgress:progress]; + + var animationIndex = [_viewAnimations count]; + while (animationIndex--) + { + var dictionary = [_viewAnimations objectAtIndex:animationIndex], + view = [self _targetView:dictionary] + startFrame = [self _startFrame:dictionary] + endFrame = [self _endFrame:dictionary] + differenceFrame = CPRectMakeZero(); + + differenceFrame.origin.x = endFrame.origin.x - startFrame.origin.x; + differenceFrame.origin.y = endFrame.origin.y - startFrame.origin.y; + differenceFrame.size.width = endFrame.size.width - startFrame.size.width; + differenceFrame.size.height = endFrame.size.height - startFrame.size.height; + + var intermediateFrame = CPRectMakeZero(); + intermediateFrame.origin.x = startFrame.origin.x + differenceFrame.origin.x * progress; + intermediateFrame.origin.y = startFrame.origin.y + differenceFrame.origin.y * progress; + intermediateFrame.size.width = startFrame.size.width + differenceFrame.size.width * progress; + intermediateFrame.size.height = startFrame.size.height + differenceFrame.size.height * progress; + + [view setFrame:intermediateFrame]; + + // Update the view's alpha value + var effect = [self _effect:dictionary]; + if (effect === CPViewAnimationFadeInEffect) + [view setAlphaValue:1.0 * progress]; + else if (effect === CPViewAnimationFadeOutEffect) + [view setAlphaValue:1.0 + ( 0.0 - 1.0 ) * progress]; + + if (progress === 1.0) + [view setHidden:CPRectIsNull(endFrame) || [view alphaValue] === 0.0]; + } } - (void)stopAnimation { - var animationIndex = [_viewAnimations count]; - while (animationIndex--) - { - var dictionary = [_viewAnimations objectAtIndex:animationIndex]; - - var view = [self _targetView:dictionary], - endFrame = [self _endFrame:dictionary]; - - [view setFrame:endFrame]; - - var effect = [self _effect:dictionary]; - - if (effect === CPViewAnimationFadeInEffect) - [view setAlphaValue:1.0]; - else if (effect === CPViewAnimationFadeOutEffect) - [view setAlphaValue:0.0]; - - [view setHidden:CPRectIsNull(endFrame) || [view alphaValue] === 0.0]; - } - - [super stopAnimation]; + var animationIndex = [_viewAnimations count]; + while (animationIndex--) + { + var dictionary = [_viewAnimations objectAtIndex:animationIndex], + view = [self _targetView:dictionary], + endFrame = [self _endFrame:dictionary]; + + [view setFrame:endFrame]; + + var effect = [self _effect:dictionary]; + if (effect === CPViewAnimationFadeInEffect) + [view setAlphaValue:1.0]; + else if (effect === CPViewAnimationFadeOutEffect) + [view setAlphaValue:0.0]; + + [view setHidden:CPRectIsNull(endFrame) || [view alphaValue] === 0.0]; + } + + [super stopAnimation]; } -// =============== -// = CONVENIENCE = -// =============== - (id)_targetView:(CPDictionary)dictionary { - var targetView = [dictionary valueForKey:CPViewAnimationTargetKey]; - - if (!targetView) - [CPException raise:CPInternalInconsistencyException reason:[CPString stringWithFormat:@"view animation: %@ does not have a target view", [dictionary description]]]; - - return targetView; + var targetView = [dictionary valueForKey:CPViewAnimationTargetKey]; + if (!targetView) + [CPException raise:CPInternalInconsistencyException reason:[CPString stringWithFormat:@"view animation: %@ does not have a target view", [dictionary description]]]; + + return targetView; } - (CPRect)_startFrame:(CPDictionary)dictionary { - var startFrame = [dictionary valueForKey:CPViewAnimationStartFrameKey]; - - if (!startFrame) - return [[self _targetView:dictionary] frame]; - - return startFrame; + var startFrame = [dictionary valueForKey:CPViewAnimationStartFrameKey]; + if (!startFrame) + return [[self _targetView:dictionary] frame]; + + return startFrame; } - (CPRect)_endFrame:(CPDictionary)dictionary { - var endFrame = [dictionary valueForKey:CPViewAnimationEndFrameKey]; - - if (!endFrame) - return [[self _targetView:dictionary] frame]; - - return endFrame; + var endFrame = [dictionary valueForKey:CPViewAnimationEndFrameKey]; + if (!endFrame) + return [[self _targetView:dictionary] frame]; + + return endFrame; } - (CPString)_effect:(CPDictionary)dictionary { - return [dictionary valueForKey:CPViewAnimationEffectKey]; + return [dictionary valueForKey:CPViewAnimationEffectKey]; } -// ============= -// = ACCESSORS = -// ============= - (CPArray)viewAnimations { - return _viewAnimations; + return _viewAnimations; } - (void)setViewAnimations:(CPArray)viewAnimations { - if (viewAnimations != _viewAnimations) - { - [self stopAnimation]; - _viewAnimations = [viewAnimations copy]; - } + if (viewAnimations != _viewAnimations) + { + [self stopAnimation]; + _viewAnimations = [viewAnimations copy]; + } } @end From 5a8d491f10cb7c2a03bd48563c33cc52d9f766f8 Mon Sep 17 00:00:00 2001 From: Ross Boucher Date: Sun, 24 Jan 2010 00:34:35 -0800 Subject: [PATCH 07/39] Check the Info.plist for a CPDefaultTheme key for the name of the default theme. Closes #418. --- AppKit/CPApplication.j | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/AppKit/CPApplication.j b/AppKit/CPApplication.j index ee53c6643..c8debf421 100644 --- a/AppKit/CPApplication.j +++ b/AppKit/CPApplication.j @@ -962,7 +962,7 @@ CPRunContinuesResponse = -1002; + (CPString)defaultThemeName { // FIXME: don't hardcode - return @"Aristo.blend"; + return ([[CPBundle mainBundle] objectForInfoDictionaryKey:"CPDefaultTheme"] || @"Aristo"); } @end @@ -1069,7 +1069,7 @@ var _CPAppBootstrapperActions = nil; + (BOOL)loadDefaultTheme { - var blend = [[CPThemeBlend alloc] initWithContentsOfURL:[[CPBundle bundleForClass:[CPApplication class]] pathForResource:[CPApplication defaultThemeName]]]; + var blend = [[CPThemeBlend alloc] initWithContentsOfURL:[[CPBundle bundleForClass:[CPApplication class]] pathForResource:[CPApplication defaultThemeName] + ".blend"]]; [blend loadWithDelegate:self]; @@ -1078,7 +1078,7 @@ var _CPAppBootstrapperActions = nil; + (void)blendDidFinishLoading:(CPBundle)aBundle { - [CPTheme setDefaultTheme:[CPTheme themeNamed:@"Aristo"]]; + [CPTheme setDefaultTheme:[CPTheme themeNamed:[CPApplication defaultThemeName]]]; [self performActions]; } From fb89450b902b630be9c9286a22e744f732fbd4a3 Mon Sep 17 00:00:00 2001 From: Randall Luecke Date: Sun, 24 Jan 2010 03:39:58 -0500 Subject: [PATCH 08/39] Refactored keyboard navigation for CPTableView --- AppKit/CPTableView.j | 188 +++++++++++++++++++++---------------------- 1 file changed, 91 insertions(+), 97 deletions(-) diff --git a/AppKit/CPTableView.j b/AppKit/CPTableView.j index 8085e34e6..ba6f22d02 100644 --- a/AppKit/CPTableView.j +++ b/AppKit/CPTableView.j @@ -2611,108 +2611,102 @@ window.setTimeout(function(){ [self interpretKeyEvents:[CPArray arrayWithObject:anEvent]]; } -- (void)interpretKeyEvents:(CPArray)events +- (void)moveDown:(id)sender { - [super interpretKeyEvents:events]; - - for(var i = 0; i < [events count]; i++) + var anEvent = [CPApp currentEvent]; + if([[self selectedRowIndexes] count] > 0) { - var anEvent = [events objectAtIndex:i], - key = [anEvent keyCode]; + var extend = NO; - if(key === CPDeleteKeyCode && [_delegate respondsToSelector: @selector(tableViewDeleteKeyPressed:)]) - [_delegate tableViewDeleteKeyPressed:self]; + if(([anEvent modifierFlags] & CPShiftKeyMask) && _allowsMultipleSelection) + extend = YES; - if(key === CPUpArrowKeyCode) - { - if([[self selectedRowIndexes] count] > 0) - { - var extend = NO; - - if(([anEvent modifierFlags] & CPShiftKeyMask) && _allowsMultipleSelection) - extend = YES; - - var i = [[self selectedRowIndexes] firstIndex]; - if(i > 0) - i--; //set index to the prev row before the first row selected - } - else - { - var extend = NO; - //no rows are currently selected - if([self numberOfRows] > 0) - var i = [self numberOfRows] - 1; //select the first row - } - - - if(_implementedDelegateMethods & CPTableViewDelegate_tableView_shouldSelectRow_) - { - - while((![_delegate tableView:self shouldSelectRow:i]) && i > 0) - { - //check to see if the row can be selected if it can't be then see if the prev row can be selected - i--; - } - - //if the index still can be selected after the loop then just return - if(![_delegate tableView:self shouldSelectRow:i]) - return; - } - - [self selectRowIndexes:[CPIndexSet indexSetWithIndex:i] byExtendingSelection:extend]; - - if(i) - { - [self scrollRowToVisible:i]; - [self _noteSelectionDidChange]; - } - } - - if(key == CPDownArrowKeyCode) - { - if([[self selectedRowIndexes] count] > 0) - { - var extend = NO; - - if(([anEvent modifierFlags] & CPShiftKeyMask) && _allowsMultipleSelection) - extend = YES; - - var i = [[self selectedRowIndexes] lastIndex]; - if(i<[self numberOfRows] - 1) - i++; //set index to the next row after the last row selected - } - else - { - var extend = NO; - //no rows are currently selected - if([self numberOfRows] > 0) - var i = 0; //select the first row - } - - - if(_implementedDelegateMethods & CPTableViewDelegate_tableView_shouldSelectRow_) - { - - while((![_delegate tableView:self shouldSelectRow:i]) && i<[self numberOfRows]) - { - //check to see if the row can be selected if it can't be then see if the next row can be selected - i++; - } - - //if the index still can be selected after the loop then just return - if(![_delegate tableView:self shouldSelectRow:i]) - return; - } - - [self selectRowIndexes:[CPIndexSet indexSetWithIndex:i] byExtendingSelection:extend]; - - if(i) - { - [self scrollRowToVisible:i]; - [self _noteSelectionDidChange]; - } - } + var i = [[self selectedRowIndexes] lastIndex]; + if(i<[self numberOfRows] - 1) + i++; //set index to the next row after the last row selected } + else + { + var extend = NO; + //no rows are currently selected + if([self numberOfRows] > 0) + var i = 0; //select the first row + } + + + if(_implementedDelegateMethods & CPTableViewDelegate_tableView_shouldSelectRow_) + { + + while((![_delegate tableView:self shouldSelectRow:i]) && i<[self numberOfRows]) + { + //check to see if the row can be selected if it can't be then see if the next row can be selected + i++; + } + + //if the index still can be selected after the loop then just return + if(![_delegate tableView:self shouldSelectRow:i]) + return; + } + + [self selectRowIndexes:[CPIndexSet indexSetWithIndex:i] byExtendingSelection:extend]; + + if(i) + { + [self scrollRowToVisible:i]; + [self _noteSelectionDidChange]; + } +} + +- (void)moveUp:(id)sender +{ + var anEvent = [CPApp currentEvent]; + if([[self selectedRowIndexes] count] > 0) + { + var extend = NO; + + if(([anEvent modifierFlags] & CPShiftKeyMask) && _allowsMultipleSelection) + extend = YES; + + var i = [[self selectedRowIndexes] firstIndex]; + if(i > 0) + i--; //set index to the prev row before the first row selected + } + else + { + var extend = NO; + //no rows are currently selected + if([self numberOfRows] > 0) + var i = [self numberOfRows] - 1; //select the first row + } + + + if(_implementedDelegateMethods & CPTableViewDelegate_tableView_shouldSelectRow_) + { + + while((![_delegate tableView:self shouldSelectRow:i]) && i > 0) + { + //check to see if the row can be selected if it can't be then see if the prev row can be selected + i--; + } + + //if the index still can be selected after the loop then just return + if(![_delegate tableView:self shouldSelectRow:i]) + return; + } + + [self selectRowIndexes:[CPIndexSet indexSetWithIndex:i] byExtendingSelection:extend]; + + if(i) + { + [self scrollRowToVisible:i]; + [self _noteSelectionDidChange]; + } +} + +- (void)deleteBackward:(id)sender +{ + if([_delegate respondsToSelector: @selector(tableViewDeleteKeyPressed:)]) + [_delegate tableViewDeleteKeyPressed:self]; } @end From 9b015611272691a41c0c3a43a76d4ca12516aae9 Mon Sep 17 00:00:00 2001 From: Ross Boucher Date: Sun, 24 Jan 2010 01:15:10 -0800 Subject: [PATCH 09/39] Fix the collection view resizing behavior. --- AppKit/CPCollectionView.j | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/AppKit/CPCollectionView.j b/AppKit/CPCollectionView.j index 40413eb5b..4a9ddecba 100644 --- a/AppKit/CPCollectionView.j +++ b/AppKit/CPCollectionView.j @@ -359,21 +359,29 @@ - (void)tile { var width = CGRectGetWidth([self bounds]); - + if (![_content count] || width == _tileWidth) return; - + // We try to fit as many views per row as possible. Any remaining space is then // either proportioned out to the views (if their minSize != maxSize) or used as // margin var itemSize = CGSizeMakeCopy(_minItemSize); - + _numberOfColumns = MAX(1.0, FLOOR(width / itemSize.width)); - + if (_maxNumberOfColumns > 0) _numberOfColumns = MIN(_maxNumberOfColumns, _numberOfColumns); - - var remaining = width - _numberOfColumns * itemSize.width, + + var nbItems = [_items count]; + if (_numberOfColumns > nbItems) + _numberOfColumns = nbItems; + + var maxItemSize = CGSizeMakeCopy(_maxItemSize); + if (maxItemSize.width==0) + maxItemSize.width = FLOOR(width / _numberOfColumns); + + var remaining = width - _numberOfColumns * itemSize.width, itemsNeedSizeUpdate = NO; if (remaining > 0 && itemSize.width < _maxItemSize.width) @@ -406,7 +414,7 @@ { if (index % _numberOfColumns == 0) { - x = _horizontalMargin; + x = 0; y += _verticalMargin + itemSize.height; } @@ -417,7 +425,7 @@ if (itemsNeedSizeUpdate) [view setFrameSize:_itemSize]; - x += itemSize.width + _horizontalMargin; + x += itemSize.width; } _tileWidth = width; From 62379b21c164a4ba0e0941a4483769d72db530d3 Mon Sep 17 00:00:00 2001 From: luddep Date: Sun, 24 Jan 2010 10:15:15 +0100 Subject: [PATCH 10/39] Quick fix for @"Aristo" being hardcoded in CPAlert.j --- AppKit/CPAlert.j | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/AppKit/CPAlert.j b/AppKit/CPAlert.j index 14bf6ccd5..97a2f1518 100644 --- a/AppKit/CPAlert.j +++ b/AppKit/CPAlert.j @@ -148,7 +148,8 @@ var CPAlertWarningImage, var button = _buttons[i]; [button setFrameSize:CGSizeMake([button frame].size.width, (styleMask == CPHUDBackgroundWindowMask) ? 20.0 : 24.0)]; - [button setTheme:[CPTheme themeNamed: _windowStyle === CPHUDBackgroundWindowMask ? "Aristo-HUD" : "Aristo"]]; + + [button setTheme:(_windowStyle === CPHUDBackgroundWindowMask) ? [CPTheme themeNamed:"Aristo-HUD"] : [CPTheme defaultTheme]]; [[_alertPanel contentView] addSubview:button]; } @@ -263,8 +264,8 @@ var CPAlertWarningImage, [button setTag:_buttonCount]; [button setAction:@selector(_notifyDelegate:)]; - [button setTheme:[CPTheme themeNamed: _windowStyle === CPHUDBackgroundWindowMask ? "Aristo-HUD" : "Aristo"]]; - [button setAutoresizingMask:CPViewMinXMargin|CPViewMinYMargin]; + [button setTheme:(_windowStyle === CPHUDBackgroundWindowMask) ? [CPTheme themeNamed:"Aristo-HUD"] : [CPTheme defaultTheme]]; + [button setAutoresizingMask:CPViewMinXMargin | CPViewMinYMargin]; [[_alertPanel contentView] addSubview:button]; From e3cdcbc5ab02a5871e7bb53ee9a9d5665ee4eb20 Mon Sep 17 00:00:00 2001 From: Ross Boucher Date: Sun, 24 Jan 2010 15:24:59 -0800 Subject: [PATCH 11/39] Add CPSpaceKeyCode. Closes #424. --- AppKit/CPResponder.j | 1 + 1 file changed, 1 insertion(+) diff --git a/AppKit/CPResponder.j b/AppKit/CPResponder.j index 32de1dfaa..99c9b0ab5 100644 --- a/AppKit/CPResponder.j +++ b/AppKit/CPResponder.j @@ -27,6 +27,7 @@ CPDeleteKeyCode = 8; CPTabKeyCode = 9; CPReturnKeyCode = 13; CPEscapeKeyCode = 27; +CPSpaceKeyCode = 32; CPLeftArrowKeyCode = 37; CPUpArrowKeyCode = 38; CPRightArrowKeyCode = 39; From 712a991c86ef130b28b0ecc23241b0b604195e88 Mon Sep 17 00:00:00 2001 From: Ross Boucher Date: Sun, 24 Jan 2010 15:48:25 -0800 Subject: [PATCH 12/39] Fix a bug introduced by the new document.body code when opening a second platform window. --- AppKit/Platform/DOM/CPPlatformWindow+DOM.j | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AppKit/Platform/DOM/CPPlatformWindow+DOM.j b/AppKit/Platform/DOM/CPPlatformWindow+DOM.j index 152373698..5c73c69cc 100644 --- a/AppKit/Platform/DOM/CPPlatformWindow+DOM.j +++ b/AppKit/Platform/DOM/CPPlatformWindow+DOM.j @@ -462,9 +462,9 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop]; _DOMWindow.cpSetShadowStyle(_shadowStyle); } - _DOMBodyElement.style.cursor = [[CPCursor currentCursor] _cssString]; - [self registerDOMWindow]; + + _DOMBodyElement.style.cursor = [[CPCursor currentCursor] _cssString]; } - (void)orderOut:(id)aSender From 1170673b92a1a12a63005275ca5443a92582899f Mon Sep 17 00:00:00 2001 From: Randall Luecke Date: Sun, 24 Jan 2010 20:01:58 -0500 Subject: [PATCH 13/39] selection did change is no longer posted if selectionShouldChangeInTableView returns NO. Closes 372. Keyboard navigation in tableview now honors selectionShouldChange. --- AppKit/CPTableView.j | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/AppKit/CPTableView.j b/AppKit/CPTableView.j index ba6f22d02..385a980ed 100644 --- a/AppKit/CPTableView.j +++ b/AppKit/CPTableView.j @@ -2146,10 +2146,7 @@ window.setTimeout(function(){ // if the table has drag support then we use mouseUp to select a single row. // otherwise it uses mouse down. if(!(_implementedDataSourceMethods & CPTableViewDataSource_tableView_writeRowsWithIndexes_toPasteboard_)) - { - _previouslySelectedRowIndexes = nil; [self _updateSelectionWithMouseAtRow:row]; - } [[self window] makeFirstResponder:self]; return YES; @@ -2251,7 +2248,7 @@ window.setTimeout(function(){ return; } // if the table has drag support then we use mouseUp to select a single row. - _previouslySelectedRowIndexes = nil; + _previouslySelectedRowIndexes = [_selectedRowIndexes copy]; [self _updateSelectionWithMouseAtRow:rowIndex]; } } @@ -2571,8 +2568,6 @@ window.setTimeout(function(){ if ([newSelection isEqualToIndexSet:_selectedRowIndexes]) return; - if (!_previouslySelectedRowIndexes) - _previouslySelectedRowIndexes = [_selectedRowIndexes copy]; [self selectRowIndexes:newSelection byExtendingSelection:NO]; @@ -2613,6 +2608,10 @@ window.setTimeout(function(){ - (void)moveDown:(id)sender { + if (_implementedDelegateMethods & CPTableViewDelegate_selectionShouldChangeInTableView_ && + ![_delegate selectionShouldChangeInTableView:self]) + return; + var anEvent = [CPApp currentEvent]; if([[self selectedRowIndexes] count] > 0) { @@ -2659,6 +2658,10 @@ window.setTimeout(function(){ - (void)moveUp:(id)sender { + if (_implementedDelegateMethods & CPTableViewDelegate_selectionShouldChangeInTableView_ && + ![_delegate selectionShouldChangeInTableView:self]) + return; + var anEvent = [CPApp currentEvent]; if([[self selectedRowIndexes] count] > 0) { From 09306aa2cd6c1431281f5b7cd083c14f31af2b31 Mon Sep 17 00:00:00 2001 From: Tom Robinson Date: Mon, 25 Jan 2010 12:49:11 -0800 Subject: [PATCH 14/39] Fixed cplutil and updated to use args parser, added lint option, etc. --- Objective-J/CommonJS/bin/cplutil | 138 +++++++++++++++++-------------- 1 file changed, 75 insertions(+), 63 deletions(-) diff --git a/Objective-J/CommonJS/bin/cplutil b/Objective-J/CommonJS/bin/cplutil index 194095d2b..000e7d1be 100755 --- a/Objective-J/CommonJS/bin/cplutil +++ b/Objective-J/CommonJS/bin/cplutil @@ -1,79 +1,91 @@ #!/usr/bin/env narwhal var FILE = require("file"); +var SYSTEM = require("system"); +var OS = require("os"); +var OBJJ = require("objective-j"); +var parser = new (require("args").Parser)(); -function printUsage() -{ - print("this is where you say the usage"); -} +parser.usage("file..."); +parser.help("Cappuccino plist converter."); + +parser.option("-c", "--convert", "format") + .help("rewrite property list files in format") + .choices({ + "280north1" : OBJJ.kCFPropertyList280NorthFormat_v1_0, + "xml1": OBJJ.kCFPropertyListXMLFormat_v1_0 + }); + +parser.option("-l", "--lint", "lint") + .help("check the property list files for syntax errors") + .set(true); + +parser.option("-h", "--help") + .action(parser.printHelp); + +parser.option("-o", "outPath") + .help("specify alternate file path name for result;\n\ +the -o option is used with -convert, and is only\n\ +useful with one file argument (last file overwrites);\n\ +the path '-' means stdout.") + .set(); + +parser.option("-e", "outExtension") + .help("specify alternate extension for converted files") + .set(); + +parser.option("-s", "silent") + .help("be silent on success") + .set(true); exports.main = function(args) { - // TODO: args parser - args.shift(); + // HACK: add extra "-" to "-convert", etc, for plutil compatibility + var args = args.map(function(arg) { return arg.replace(/^(-[^-].+)$/, "-$1"); }); + var options = parser.parse(args); - if (args.length < 1) - return printUsage(); - - var allFiles = NO, - format = nil, - filePaths = [], - outPath = nil, - outExtension = nil; - - index = 0, - count = args.length; - - for (; index < count; ++index) + var ok = false; + + if (typeof options.format !== "undefined" || options.lint) { - var argument = args[index]; - - if (argument.charAt(0) === '-' && !allFiles) - { - if (filePaths.length > 0) - return printUsage(); - - else if (argument === "-convert") - format = args[++index]; - - else if (argument === "-o") - outPath = args[++index]; - - else if (argument === "-e") - outExtension = args[++index]; - - else if (argument === "--") - allFiles = YES; - - else if (argument === "-help") - return printUsage(); - } - else - filePaths.push(args[index]); + ok = true; + options.args.forEach(function(filePath) { + var data = new OBJJ.objj_data(); + data.string = FILE.read(filePath, { charset:"UTF-8" }); + + var plistObject = OBJJ.CPPropertyListCreateFromData(data); + if (!plistObject) + { + // FIXME: more useful error messages + SYSTEM.stderr.print(filePath + ": parse error"); + ok = false; + return; + } + + if (options.lint) + { + if (!options.silent) + SYSTEM.stderr.print(filePath + ": OK"); + } + else + { + data = OBJJ.CPPropertyListCreateData(plistObject, options.format); + + // outextension? + if (options.outPath === "-") + SYSTEM.stdout.write(data.string); + else + FILE.write(options.outPath || filePath, data.string, { charset:"UTF-8" }); + } + }); } - - index = 0; - count = filePaths.length; - - for (; index < count; ++index) + else { - var filePath = filePaths[index], - data = new objj_data(); - - data.string = FILE.read(filePaths[index], { charset:"UTF-8" }); - - var plistObject = new CPPropertyListCreateFromData(data); - - if (format === "280north1") - data = CPPropertyListCreate280NorthData(plistObject); - - else if (format === "xml1") - data = CPPropertyListCreateXMLData(plistObject); - - // outextension? - FILE.write(outPath || filePath, data.string, { charset:"UTF-8" }); + parser.printHelp(options); } + + OS.exit(ok ? 0 : 1); } if (require.main == module.id) From 0c217242708e2bd0756899c77bd1fa8c45d21afc Mon Sep 17 00:00:00 2001 From: Tom Robinson Date: Mon, 25 Jan 2010 13:35:27 -0800 Subject: [PATCH 15/39] Give debug frameworks higher priority in objj. --- Objective-J/CommonJS/lib/objective-j.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Objective-J/CommonJS/lib/objective-j.js b/Objective-J/CommonJS/lib/objective-j.js index f0bf6b0a9..5b000e61a 100644 --- a/Objective-J/CommonJS/lib/objective-j.js +++ b/Objective-J/CommonJS/lib/objective-j.js @@ -52,8 +52,8 @@ for (var name in catalog) } // push to the front of the array lowest priority first. -OBJJ_INCLUDE_PATHS.unshift.apply(OBJJ_INCLUDE_PATHS, exports.objj_debug_frameworks); OBJJ_INCLUDE_PATHS.unshift.apply(OBJJ_INCLUDE_PATHS, exports.objj_frameworks); +OBJJ_INCLUDE_PATHS.unshift.apply(OBJJ_INCLUDE_PATHS, exports.objj_debug_frameworks); if (system.env["OBJJ_INCLUDE_PATHS"]) OBJJ_INCLUDE_PATHS.unshift.apply(OBJJ_INCLUDE_PATHS, system.env["OBJJ_INCLUDE_PATHS"].split(":")); From 4e50244dcaac5fdd32b62e258688cdad9e8bdf56 Mon Sep 17 00:00:00 2001 From: Tom Robinson Date: Mon, 25 Jan 2010 14:14:34 -0800 Subject: [PATCH 16/39] Handle more bootstrap.sh edge cases. --- bootstrap.sh | 40 ++++++++++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/bootstrap.sh b/bootstrap.sh index df2c06bb9..1243829c5 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -33,7 +33,7 @@ function ask_remove_dir () { function ask_append_shell_config () { config_string="$1" - + shell_config_file="" # use order outlined by http://hayne.net/MacDev/Notes/unixFAQ.html#shellStartup if [ -f "$HOME/.bash_profile" ]; then @@ -60,6 +60,13 @@ function ask_append_shell_config () { return 1 } +function check_and_exit () { + if [ ! "$?" = "0" ]; then + echo "Error: problem running boostrap.sh. Exiting." + exit 1 + fi +} + if [ "--clone" = "$1" ]; then tusk_install_command="clone" git_clone=1 @@ -72,6 +79,8 @@ github_path=$(echo "$github_project" | tr '-' '/') install_directory="/usr/local/narwhal" tmp_zip="/tmp/narwhal.zip" +unset NARWHAL_ENGINE + PATH_SAVED="$PATH" ask_remove_dir "/usr/local/share/objj" @@ -109,22 +118,37 @@ if [ "$install_narwhal" ]; then install_directory="`cd \`dirname $input\`; pwd`/`basename $input`" fi + if [ -d "$install_directory" ]; then + echo "================================================================================" + echo "Directory exists at $install_directory. Delete it?" + if prompt; then + rm -rf "$install_directory" + fi + fi + if [ "$git_clone" ]; then git_repo="git://github.com/$github_path.git" echo "Cloning Narwhal from \"$git_repo\"..." git clone "$git_repo" "$install_directory" else zip_ball="http://github.com/$github_path/zipball/master" + echo "Downloading Narwhal from \"$zip_ball\"..." curl -L -o "$tmp_zip" "$zip_ball" + check_and_exit + echo "Installing Narwhal..." unzip "$tmp_zip" -d "$install_directory" + check_and_exit rm "$tmp_zip" + check_and_exit - mv $install_directory/$github_project-*/* $install_directory/. - rm -rf $install_directory/$github_project-* + mv "$install_directory/$github_project-"*/* "$install_directory/." + check_and_exit + rm -rf "$install_directory/$github_project-"* + check_and_exit fi - + export PATH="$install_directory/bin:$PATH" fi @@ -156,7 +180,7 @@ if [ `uname` = "Darwin" ]; then echo "This is optional but will make building and running Objective-J much faster." if prompt; then tusk $tusk_install_command narwhal-jsc - + if ! (cd "$install_directory/packages/narwhal-jsc" && make webkit); then echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" echo "WARNING: building narwhal-jsc failed. Hit enter to continue." @@ -175,9 +199,9 @@ export PATH="$PATH_SAVED" if ! which "narwhal" > /dev/null; then echo "================================================================================" echo "You must add Narwhal's \"bin\" directory to your PATH environment variable. Do this automatically now?" - + export_path_string="export PATH=\"$install_directory/bin:\$PATH\"" - + if ! ask_append_shell_config "$export_path_string"; then echo "Add \"$install_directory/bin\" to your PATH environment variable in your shell configuration file (e.x. .profile, .bashrc, .bash_profile)." echo "For example:" @@ -193,7 +217,7 @@ if [ "$CAPP_BUILD" ]; then rm -rf "$CAPP_BUILD" fi fi -else +else echo "================================================================================" echo "Before building Cappuccino we recommend you set the \$CAPP_BUILD environment variable to a path where you wish to build Cappuccino." echo "If you have previously set \$CAPP_BUILD and built Cappuccino you may want to delete the directory before rebuilding." From 27aca4c84a14952c17b28c16163f644552aacfc1 Mon Sep 17 00:00:00 2001 From: Tom Robinson Date: Mon, 25 Jan 2010 14:19:30 -0800 Subject: [PATCH 17/39] bootstrap.sh warning about shell config --- bootstrap.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bootstrap.sh b/bootstrap.sh index 1243829c5..41a4fd978 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -220,8 +220,9 @@ if [ "$CAPP_BUILD" ]; then else echo "================================================================================" echo "Before building Cappuccino we recommend you set the \$CAPP_BUILD environment variable to a path where you wish to build Cappuccino." - echo "If you have previously set \$CAPP_BUILD and built Cappuccino you may want to delete the directory before rebuilding." + echo "NOTE: If you have previously set \$CAPP_BUILD and built Cappuccino you may want to delete the directory before rebuilding." fi echo "================================================================================" echo "Bootstrapping of Narwhal and other required tools is complete. You can now build Cappuccino." +echo "NOTE: any changes made to the shell configuration files won't take place until you restart the shell." From 9db77016912696a7e75294971b09e26dbddeb87a Mon Sep 17 00:00:00 2001 From: Ross Boucher Date: Mon, 25 Jan 2010 17:03:24 -0800 Subject: [PATCH 18/39] Fixes a bug when changing splitPane status for a CPSplitView. Closes #419. --- AppKit/CPSplitView.j | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/AppKit/CPSplitView.j b/AppKit/CPSplitView.j index 8022dad0e..33d243f22 100644 --- a/AppKit/CPSplitView.j +++ b/AppKit/CPSplitView.j @@ -144,12 +144,11 @@ var CPSplitViewHorizontalImage = nil, { if (_isPaneSplitter == shouldBePaneSplitter) return; - + _isPaneSplitter = shouldBePaneSplitter; -#if PLATFORM(DOM) - _DOMDividerElements = []; -#endif + if(_DOMDividerElements[_drawingDivider]) + [self _setupDOMDivider] // The divider changes size when pane splitter mode is toggled, so the // subviews need to change size too. @@ -160,7 +159,6 @@ var CPSplitViewHorizontalImage = nil, - (void)didAddSubview:(CPView)aSubview { _needsResizeSubviews = YES; -// [self adjustSubviews]; } - (BOOL)isSubviewCollapsed:(CPView)subview @@ -216,16 +214,7 @@ var CPSplitViewHorizontalImage = nil, CPDOMDisplayServerAppendChild(_DOMElement, _DOMDividerElements[_drawingDivider]); - if (_isPaneSplitter) - { - _DOMDividerElements[_drawingDivider].style.backgroundColor = "#A5A5A5"; - _DOMDividerElements[_drawingDivider].style.backgroundImage = ""; - } - else - { - _DOMDividerElements[_drawingDivider].style.backgroundColor = ""; - _DOMDividerElements[_drawingDivider].style.backgroundImage = "url('"+_dividerImagePath+"')"; - } + [self _setupDOMDivider]; } CPDOMDisplayServerSetStyleLeftTop(_DOMDividerElements[_drawingDivider], NULL, _CGRectGetMinX(aRect), _CGRectGetMinY(aRect)); @@ -233,6 +222,20 @@ var CPSplitViewHorizontalImage = nil, #endif } +- (void)_setupDOMDivider +{ + if (_isPaneSplitter) + { + _DOMDividerElements[_drawingDivider].style.backgroundColor = "#A5A5A5"; + _DOMDividerElements[_drawingDivider].style.backgroundImage = ""; + } + else + { + _DOMDividerElements[_drawingDivider].style.backgroundColor = ""; + _DOMDividerElements[_drawingDivider].style.backgroundImage = "url('"+_dividerImagePath+"')"; + } +} + - (void)viewWillDraw { [self _adjustSubviewsWithCalculatedSize]; @@ -515,8 +518,6 @@ var CPSplitViewHorizontalImage = nil, } else if (totalSizableSpace && !isSizable) viewFrame.size[_sizeComponent] = [view frame].size[_sizeComponent]; - else - alert("SHOULD NEVER GET HERE"); bounds.origin[_originComponent] += viewFrame.size[_sizeComponent] + dividerThickness; From 9cb1e28ad15112641f2e29ea0742c789a50c0663 Mon Sep 17 00:00:00 2001 From: tlrobinson Date: Mon, 25 Jan 2010 20:43:37 -0800 Subject: [PATCH 19/39] bootstrap option to install Cappuccino/Objective-J packages. --- bootstrap.sh | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/bootstrap.sh b/bootstrap.sh index 41a4fd978..9a6e3fe45 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -165,6 +165,14 @@ if ! prompt; then exit 1 fi +echo "================================================================================" +echo "Would you like to install the pre-built Objective-J and Cappuccino packages?" +echo "If you intend to build Cappuccino yourself this is not neccessary." +extra_packages="" +if prompt; then + extra_packages="objective-j cappuccino" +fi + echo "Installing necessary packages..." if ! tusk update; then @@ -172,7 +180,7 @@ if ! tusk update; then exit 1 fi -tusk $tusk_install_command browserjs jake +tusk $tusk_install_command browserjs jake $extra_packages if [ `uname` = "Darwin" ]; then echo "================================================================================" @@ -224,5 +232,5 @@ else fi echo "================================================================================" -echo "Bootstrapping of Narwhal and other required tools is complete. You can now build Cappuccino." +echo "Bootstrapping of Narwhal and other required tools is complete." echo "NOTE: any changes made to the shell configuration files won't take place until you restart the shell." From 3b7a77ae0c969e6bbe40a2c6dc9ee9ca1004204c Mon Sep 17 00:00:00 2001 From: Paul Baumgart Date: Mon, 25 Jan 2010 20:08:06 -0800 Subject: [PATCH 20/39] Minor fix: change emdash to regular hyphen. --- Foundation/CPOperation.j | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Foundation/CPOperation.j b/Foundation/CPOperation.j index a4503f41a..e24617ae7 100644 --- a/Foundation/CPOperation.j +++ b/Foundation/CPOperation.j @@ -168,7 +168,7 @@ CPOperationQueuePriorityVeryHigh = 8; The JS function that should be run after the main method @return JS function */ -– (JSObject)completionFunction +- (JSObject)completionFunction { return _completionFunction; } @@ -290,4 +290,4 @@ CPOperationQueuePriorityVeryHigh = 8; } } -@end \ No newline at end of file +@end From da146b08737b4d0a3dc15fe361c34de341a8a6ea Mon Sep 17 00:00:00 2001 From: Nicholas Small Date: Tue, 26 Jan 2010 10:53:07 -0500 Subject: [PATCH 21/39] Move table view header resources into AppKit/Resources. --- .../Resources/tableview-headerview-highlighted.png | Bin .../Aristo => }/Resources/tableview-headerview.png | Bin 2 files changed, 0 insertions(+), 0 deletions(-) rename AppKit/{Themes/Aristo => }/Resources/tableview-headerview-highlighted.png (100%) rename AppKit/{Themes/Aristo => }/Resources/tableview-headerview.png (100%) diff --git a/AppKit/Themes/Aristo/Resources/tableview-headerview-highlighted.png b/AppKit/Resources/tableview-headerview-highlighted.png similarity index 100% rename from AppKit/Themes/Aristo/Resources/tableview-headerview-highlighted.png rename to AppKit/Resources/tableview-headerview-highlighted.png diff --git a/AppKit/Themes/Aristo/Resources/tableview-headerview.png b/AppKit/Resources/tableview-headerview.png similarity index 100% rename from AppKit/Themes/Aristo/Resources/tableview-headerview.png rename to AppKit/Resources/tableview-headerview.png From 07f320def95c0b8413229f3ade4b6436e55d20ec Mon Sep 17 00:00:00 2001 From: Tom Robinson Date: Wed, 27 Jan 2010 00:08:42 -0800 Subject: [PATCH 22/39] Fixed jake tasks for pushing packages to package repo. --- Jakefile | 48 +++++++++++++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/Jakefile b/Jakefile index 54161d8b7..1a53ee621 100644 --- a/Jakefile +++ b/Jakefile @@ -176,41 +176,55 @@ task("test-only", function() { var tests = new FileList('Tests/**/*Test.j'); var cmd = ["ojtest"].concat(tests.items()); - + var code = OS.system(cmd); if (code !== 0) OS.exit(code); }); -task("push-packages", ["CommonJS"], function() -{ +task("push-packages", ["CommonJS", "push-cappuccino", "push-objective-j"]); + +task("push-cappuccino", function() { pushPackage( - BUILD_CJS_CAPPUCCINO, + $BUILD_CJS_CAPPUCCINO, "git@github.com:280north/cappuccino-package.git" ); +}); + +task("push-objective-j", function() { pushPackage( - BUILD_CJS_OBJECTIVE_J, + $BUILD_CJS_OBJECTIVE_J, "git@github.com:280north/objective-j-package.git" ); }); function pushPackage(path, remote) { - // FIXME: this will probably fail next time... - var cmds = - [ - ["cd", path], - //["rm", "-rf", ".git*"], - ["git", "init"], + stream.print("Pushing \0blue(" + path + "\0) to \0blue(" + remote + "\0)"); + + FILE.mkdirs("push-package"); + + var pushPackageDir = FILE.join("push-package", remote.replace(/[^\w]/g, "_")); + + if (FILE.exists(pushPackageDir)) + OS.system(buildCommandString([["cd", pushPackageDir], ["git", "pull"]])); + else + OS.system(["git", "clone", remote, pushPackageDir]); + + OS.system("cd "+OS.enquote(pushPackageDir)+" && git rm --ignore-unmatch -r * && rm -rf *"); + OS.system("cp -R "+OS.enquote(path)+"/* "+OS.enquote(pushPackageDir)+"/."); + + OS.system(buildCommandString([ + ["cd", pushPackageDir], ["git", "add", "."], ["git", "commit", "-m", "Pushed on " + new Date()], - ["git", "remote", "add", "origin", remote], ["git", "push", "origin", "master"] - ]; - - var cmdString = cmds.map(function(cmd) { + ])); +} + +function buildCommandString(arrayOfCommands) +{ + return arrayOfCommands.map(function(cmd) { return cmd.map(OS.enquote).join(" "); }).join(" && "); - - OS.system(cmdString); } From 35adaee1bb4aa081bb98828aece7cb7d90fd69cb Mon Sep 17 00:00:00 2001 From: Tom Robinson Date: Wed, 27 Jan 2010 02:27:32 -0800 Subject: [PATCH 23/39] Initial press cib analysis --- Tools/press/cib-analysis-tools.j | 74 ++++++++++++++++++++++++++++++++ Tools/press/cib-dump | 18 ++++++++ 2 files changed, 92 insertions(+) create mode 100644 Tools/press/cib-analysis-tools.j create mode 100755 Tools/press/cib-dump diff --git a/Tools/press/cib-analysis-tools.j b/Tools/press/cib-analysis-tools.j new file mode 100644 index 000000000..8482fd423 --- /dev/null +++ b/Tools/press/cib-analysis-tools.j @@ -0,0 +1,74 @@ +@import +@import + +function findCibClassDependencies(cibPath) { + var cib = [[CPCib alloc] initWithContentsOfURL:cibPath]; + + var dependencies = {}; + + var CPClassFromStringOriginal = CPClassFromString; + CPClassFromString = function(aClassName) { + var result = CPClassFromStringOriginal(aClassName); + + // print("CPClassFromString: " + Array.prototype.slice.call(arguments) + " => " + result); + dependencies[aClassName] = true; + + return result; + } + + // make sure CPApp is init'd + [CPApplication sharedApplication] + + try { + var x = [cib pressInstantiate]; + } catch (e) { + CPLog.warn("Exception thrown when instantiating " + cibPath + ": " + e); + } finally { + CPClassFromString = CPClassFromStringOriginal; + } + + return Object.keys(dependencies); +} +// this is copied from CPCib's "instantiateCibWithExternalNameTable:" +@implementation CPCib (Press) + +- (BOOL)pressInstantiate +{ + var bundle = _bundle, + owner = nil;//[anExternalNameTable objectForKey:CPCibOwner]; + + if (!bundle && owner) + bundle = [CPBundle bundleForClass:[owner class]]; + + var unarchiver = [[_CPCibKeyedUnarchiver alloc] initForReadingWithData:_data bundle:bundle awakenCustomResources:_awakenCustomResources], + replacementClasses = nil;//[anExternalNameTable objectForKey:CPCibReplacementClasses]; + + if (replacementClasses) + { + var key = nil, + keyEnumerator = [replacementClasses keyEnumerator]; + + while (key = [keyEnumerator nextObject]) + [unarchiver setClass:[replacementClasses objectForKey:key] forClassName:key]; + } + + [unarchiver setExternalObjectsForProxyIdentifiers:nil/*[anExternalNameTable objectForKey:CPCibExternalObjects]*/]; + + var objectData = [unarchiver decodeObjectForKey:"CPCibObjectDataKey"]; + + if (!objectData || ![objectData isKindOfClass:[_CPCibObjectData class]]) + return NO; + + var topLevelObjects = nil;//[anExternalNameTable objectForKey:CPCibTopLevelObjects]; + + [objectData instantiateWithOwner:owner topLevelObjects:topLevelObjects] + // [objectData establishConnectionsWithOwner:owner topLevelObjects:topLevelObjects]; + // [objectData awakeWithOwner:owner topLevelObjects:topLevelObjects]; + + // Display Visible Windows. + // [objectData displayVisibleWindows]; + + return YES; +} + +@end diff --git a/Tools/press/cib-dump b/Tools/press/cib-dump new file mode 100755 index 000000000..87fff35a5 --- /dev/null +++ b/Tools/press/cib-dump @@ -0,0 +1,18 @@ +#!/usr/bin/env objj + +@import +@import "cib-analysis-tools.j" + +var FILE = require("file"); + +CPLogRegister(CPLogPrint); + +function main(args) +{ + args.slice(1).forEach(function(path) { + var classes = findCibClassDependencies(FILE.absolute(path)); + + print(path+":"); + classes.sort().forEach(function(className) { print(" + " + className) }); + }); +} From 8e87a4a94110e06c430d209501973ffbc0f4bab7 Mon Sep 17 00:00:00 2001 From: Tom Robinson Date: Wed, 27 Jan 2010 10:17:36 -0800 Subject: [PATCH 24/39] Make bootstrap warning very explicit. --- bootstrap.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bootstrap.sh b/bootstrap.sh index 9a6e3fe45..8d03401f5 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -24,7 +24,8 @@ function ask_remove_dir () { if [ -d "$dir" ]; then echo "================================================================================" echo "Found an existing Narwhal/Cappuccino installation, $dir. Remove it automatically now?" - echo "WARNING: custom modifications and installed packages in this installation WILL BE DELETED." + echo "WARNING: the ENTIRE directory, $dir, will be removed (i.e. 'rm -rf $dir')." + echo "Be sure this is correct. Custom modifications and installed packages WILL BE DELETED." if prompt; then rm -rf "$dir" fi From a354b4acd50b28d4d637c40e8ec6c0f64ba358ff Mon Sep 17 00:00:00 2001 From: Ross Boucher Date: Wed, 27 Jan 2010 16:36:39 -0800 Subject: [PATCH 25/39] Temporary fix for 0.8 for copy/paste behavior in CPTextField. Closes #407. --- AppKit/CPTextField.j | 51 ++++++++++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/AppKit/CPTextField.j b/AppKit/CPTextField.j index e647e542f..c906e80ca 100644 --- a/AppKit/CPTextField.j +++ b/AppKit/CPTextField.j @@ -799,41 +799,50 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder"); - (void)copy:(id)sender { - var selectedRange = [self selectedRange]; + if (![CPPlatform isBrowser]) + { + var selectedRange = [self selectedRange]; - if (selectedRange.length < 1) - return; + if (selectedRange.length < 1) + return; - var pasteboard = [CPPasteboard generalPasteboard], - stringValue = [self stringValue], - stringForPasting = [stringValue substringWithRange:selectedRange]; + var pasteboard = [CPPasteboard generalPasteboard], + stringValue = [self stringValue], + stringForPasting = [stringValue substringWithRange:selectedRange]; - [pasteboard declareTypes:[CPStringPboardType] owner:nil]; - [pasteboard setString:stringForPasting forType:CPStringPboardType]; + [pasteboard declareTypes:[CPStringPboardType] owner:nil]; + [pasteboard setString:stringForPasting forType:CPStringPboardType]; + } } - (void)cut:(id)sender { - [self copy:sender]; - [self deleteBackwards:sender]; + if (![CPPlatform isBrowser]) + { + [self copy:sender]; + [self deleteBackwards:sender]; + } } - (void)paste:(id)sender { - var pasteboard = [CPPasteboard generalPasteboard]; - - if (![[pasteboard types] containsObject:CPStringPboardType]) - return; + if (![CPPlatform isBrowser]) + { + var pasteboard = [CPPasteboard generalPasteboard]; - [self deleteBackwards:sender]; + if (![[pasteboard types] containsObject:CPStringPboardType]) + return; - var selectedRange = [self selectedRange], - stringValue = [self stringValue], - pasteString = [pasteboard stringForType:CPStringPboardType], - newValue = [stringValue stringByReplacingCharactersInRange:selectedRange withString:pasteString]; + [self deleteBackwards:sender]; - [self setStringValue:newValue]; - [self setSelectedRange:CPMakeRange(selectedRange.location+pasteString.length, 0)]; + var selectedRange = [self selectedRange], + stringValue = [self stringValue], + pasteString = [pasteboard stringForType:CPStringPboardType], + newValue = [stringValue stringByReplacingCharactersInRange:selectedRange withString:pasteString]; + + [self setStringValue:newValue]; + [self setSelectedRange:CPMakeRange(selectedRange.location+pasteString.length, 0)]; + } } - (CPRange)selectedRange From ee5817e48066e8b16b28096a50cb1b793568569a Mon Sep 17 00:00:00 2001 From: Tom Robinson Date: Wed, 27 Jan 2010 17:53:05 -0800 Subject: [PATCH 26/39] Cleanup objj arguments story. objj-executable and "objj path/to/main.j" are now consistent. --- Objective-J/CommonJS/lib/objective-j.js | 7 +++++++ Objective-J/CommonJS/objj-executable | 16 ++++------------ Tools/capp/Generate.j | 4 +++- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/Objective-J/CommonJS/lib/objective-j.js b/Objective-J/CommonJS/lib/objective-j.js index 5b000e61a..cc2fb248c 100644 --- a/Objective-J/CommonJS/lib/objective-j.js +++ b/Objective-J/CommonJS/lib/objective-j.js @@ -112,6 +112,13 @@ exports.run = function(args) { if (args && args.length > 1) { + // we expect args to be in the format: + // 1) "objj" path + // 2) optional "-I" args + // 3) real or "virtual" main.j + // 4) optional program arguments + + // copy the args since we're going to modify them var argv = args.slice(1); while (argv.length && argv[0].indexOf('-I') === 0) diff --git a/Objective-J/CommonJS/objj-executable b/Objective-J/CommonJS/objj-executable index 36a954d58..919302c11 100755 --- a/Objective-J/CommonJS/objj-executable +++ b/Objective-J/CommonJS/objj-executable @@ -1,15 +1,7 @@ #!/usr/bin/env narwhal -var FILE = require("file"); +var execPath = require("file").path(module.path); +var mainPath = execPath.dirname().dirname().join("lib", execPath.basename(), "main.j"); -var cappuccinoPackage = FILE.path(module.path).dirname().dirname(); -var mainPath = cappuccinoPackage.join("lib", FILE.basename(module.path), "main.j"); -var frameworksPath = cappuccinoPackage.join("Frameworks"); - -// TODO: is specifying the Frameworks necessary? -system.args.splice(1, 0, "-I"+frameworksPath, String(mainPath)); - -// HACK: remove use of SELF_HOME from capp gen -system.env["SELF_HOME"] = cappuccinoPackage; - -require("objective-j").run(system.args); +var args = ["objj", String(mainPath)].concat(system.args.slice(1)); +require("objective-j").run(args); diff --git a/Tools/capp/Generate.j b/Tools/capp/Generate.j index ba10684f7..5e3e7b8e1 100644 --- a/Tools/capp/Generate.j +++ b/Tools/capp/Generate.j @@ -6,6 +6,8 @@ var OS = require("os"), FILE = require("file"), OBJJ = require("objective-j"); +// FIXME: better way to do this: +var CAPP_HOME = require("packages").catalog["cappuccino"].directory; function gen(/*va_args*/) { @@ -56,7 +58,7 @@ function gen(/*va_args*/) if (FILE.isAbsolute(template)) sourceTemplate = FILE.join(template); else - sourceTemplate = FILE.join(SYSTEM.env["SELF_HOME"], "lib", "capp", "Resources", "Templates", template); + sourceTemplate = FILE.join(CAPP_HOME, "lib", "capp", "Resources", "Templates", template); var configFile = FILE.join(sourceTemplate, "template.config"), config = {}; From 3ee63b754c274b6bb32d928fc056ac0c065ec16f Mon Sep 17 00:00:00 2001 From: Tom Robinson Date: Wed, 27 Jan 2010 17:54:28 -0800 Subject: [PATCH 27/39] Remove theme bundle loading in press for now. --- Tools/press/main.j | 7 +------ Tools/press/objj-analysis-tools.j | 6 +++--- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/Tools/press/main.j b/Tools/press/main.j index f92270a50..3ea3bcd47 100644 --- a/Tools/press/main.j +++ b/Tools/press/main.j @@ -1,9 +1,4 @@ -var OS = require("os"); -if (system.engine !== "rhino") { - system.args.splice(1,2); // remove library path and main.j - var cmd = "NARWHAL_ENGINE_HOME='' NARWHAL_ENGINE='rhino' " + system.args.map(OS.enquote).join(" "); - OS.exit(OS.system(cmd)); -} +require("narwhal").ensureEngine("rhino"); @import diff --git a/Tools/press/objj-analysis-tools.j b/Tools/press/objj-analysis-tools.j index 344148bc8..24b6e9a8f 100644 --- a/Tools/press/objj-analysis-tools.j +++ b/Tools/press/objj-analysis-tools.j @@ -272,9 +272,9 @@ function findGlobalDefines(context, mainPath, evaledFragments, bundleCallback) bundlePaths = bundlePaths || []; // load default theme bundle - var themePath = [[CPBundle bundleForClass:[CPApplication class]] pathForResource:[CPApplication defaultThemeName]]; - var themeBundle = [[CPBundle alloc] initWithPath:themePath + "/Info.plist"]; - [themeBundle loadWithDelegate:bundleDelegate]; + // var themePath = [[CPBundle bundleForClass:[CPApplication class]] pathForResource:[CPApplication defaultThemeName]]; + // var themeBundle = [[CPBundle alloc] initWithPath:themePath + "/Info.plist"]; + // [themeBundle loadWithDelegate:bundleDelegate]; // load additional bundles bundlePaths.forEach(function(bundlePath) { From 7fe3b115f1318dc514631d5c17ed6429c759c998 Mon Sep 17 00:00:00 2001 From: Tom Robinson Date: Wed, 27 Jan 2010 20:46:35 -0800 Subject: [PATCH 28/39] Remove legacy image spriting code from press. --- Tools/press/main.j | 10 ---------- Tools/press/objj-analysis-tools.j | 26 -------------------------- 2 files changed, 36 deletions(-) diff --git a/Tools/press/main.j b/Tools/press/main.j index 3ea3bcd47..0d723b839 100644 --- a/Tools/press/main.j +++ b/Tools/press/main.j @@ -241,16 +241,6 @@ function pressEnvironment(rootPath, outputFiles, environment, options) { total++; } CPLog.warn("Total required files: " + count + " out of " + total); - - // FIXME: sprite images - //for (var i in context.bundleImages) - //{ - // var images = context.bundleImages[i]; - // - // CPLog.debug("Bundle images for " + i); - // for (var j in images) - // CPLog.trace(j + " = " + images[j]); - //} } if (options.flatten) diff --git a/Tools/press/objj-analysis-tools.j b/Tools/press/objj-analysis-tools.j index 24b6e9a8f..495bbcbf7 100644 --- a/Tools/press/objj-analysis-tools.j +++ b/Tools/press/objj-analysis-tools.j @@ -48,28 +48,6 @@ function traverseDependencies(context, file) file.fragments = objj_preprocess(file.contents, file.bundle, file); } - // sprite: look for pngs in the Resources directory - if (!context.bundleImages) - context.bundleImages = {}; - - if (!context.bundleImages[file.bundle.path]) - { - var resourcesPath = FILE.path(file.bundle.path).dirname().join("/Resources"); - if (resourcesPath.exists()) - { - context.bundleImages[file.bundle.path] = {}; - - resourcesPath.glob("**/*.png").forEach(function(png) { - var pngPath = resourcesPath.join(png); - var relativePath = pathRelativeTo(pngPath.absolute(), resourcesPath.absolute()); - - // this is used as a bit mask, not a boolean - context.bundleImages[file.bundle.path][relativePath] = 1; - }); - } - } - var images = context.bundleImages[file.bundle.path]; - var referencedFiles = {}, importedFiles = {}; @@ -100,10 +78,6 @@ function traverseDependencies(context, file) } } } - - var matches = token.match(new RegExp("^['\"](.*)['\"]$")); - if (matches && images && images[matches[1]]) - images[matches[1]] = (images[matches[1]] | 2); } } else if (fragment.type & FRAGMENT_FILE) From 5478c640cc4e5c6524f4b1ee4e1c3ddba43c1311 Mon Sep 17 00:00:00 2001 From: Tom Robinson Date: Wed, 27 Jan 2010 22:35:19 -0800 Subject: [PATCH 29/39] Refactor of press's objj-analysis-tools in prep for inspecting cibs. --- Tools/press/objj-analysis-tools.j | 92 ++++++++++++++++++++----------- 1 file changed, 61 insertions(+), 31 deletions(-) diff --git a/Tools/press/objj-analysis-tools.j b/Tools/press/objj-analysis-tools.j index 495bbcbf7..c45a7601b 100644 --- a/Tools/press/objj-analysis-tools.j +++ b/Tools/press/objj-analysis-tools.j @@ -58,27 +58,9 @@ function traverseDependencies(context, file) if (fragment.type & FRAGMENT_CODE) { - var lexer = new objj_lexer(fragment.info, NULL); - - var token; - while (token = lexer.skip_whitespace()) - { - if (context.dependencies.hasOwnProperty(token)) - { - var files = context.dependencies[token]; - for (var j = 0; j < files.length; j++) - { - // don't record references to self - if (files[j] != file.path) - { - if (!referencedFiles[files[j]]) - referencedFiles[files[j]] = {}; - - referencedFiles[files[j]][token] = true; - } - } - } - } + var referencedTokens = uniqueTokens(fragment.info); + + markFilesReferencedByTokens(referencedTokens, context.dependencies, referencedFiles); } else if (fragment.type & FRAGMENT_FILE) { @@ -104,12 +86,25 @@ function traverseDependencies(context, file) } // check each imported file + checkImported(context, file.path, importedFiles); + + if (context.importedFiles) + context.importedFiles[file.path] = importedFiles; + + // check each referenced file + checkReferenced(context, file.path, referencedFiles); + + if (context.referencedFiles) + context.referencedFiles[file.path] = referencedFiles; +} + +function checkImported(context, path, importedFiles) { for (var importedFile in importedFiles) { - if (importedFile != file.path) + if (importedFile != path) { if (context.importCallback) - context.importCallback(file.path, importedFile); + context.importCallback(path, importedFile); if (context.scope.objj_files[importedFile]) traverseDependencies(context, context.scope.objj_files[importedFile]); @@ -117,17 +112,15 @@ function traverseDependencies(context, file) CPLog.error("Missing imported file: " + importedFile); } } +} - if (context.importedFiles) - context.importedFiles[file.path] = importedFiles; - - // check each referenced file +function checkReferenced(context, path, referencedFiles) { for (var referencedFile in referencedFiles) { - if (referencedFile != file.path) + if (referencedFile != path) { if (context.referenceCallback) - context.referenceCallback(file.path, referencedFile, referencedFiles[referencedFile]); + context.referenceCallback(path, referencedFile, referencedFiles[referencedFile]); if (context.scope.objj_files.hasOwnProperty(referencedFile)) traverseDependencies(context, context.scope.objj_files[referencedFile]); @@ -135,9 +128,46 @@ function traverseDependencies(context, file) CPLog.error("Missing referenced file: " + referencedFile); } } +} - if (context.referencedFiles) - context.referencedFiles[file.path] = referencedFiles; +// returns a unique list of tokens for a piece of code. +// ideally this should return identifiers only +function uniqueTokens(code) { + // FIXME: this breaks for indentifiers containing "$" since it's considered a distinct token by the parser + var lexer = new objj_lexer(code, null); + + var token, tokens = {}; + while (token = lexer.skip_whitespace()) { + tokens[token] = true; + } + + return Object.keys(tokens); +} + +/* + params: + tokens (in): list of tokens to mark as required + tokenDependenciesMap (in): map from tokens to files which define those tokens + referencedFiles (out): map of required files (to map of tokens defined in that file) +*/ +function markFilesReferencedByTokens(tokens, tokenDependenciesMap, referencedFiles) { + tokens.forEach(function(token) { + if (tokenDependenciesMap.hasOwnProperty(token)) + { + var files = tokenDependenciesMap[token]; + for (var j = 0; j < files.length; j++) + { + // don't record references to self + if (files[j] != file.path) + { + if (!referencedFiles[files[j]]) + referencedFiles[files[j]] = {}; + + referencedFiles[files[j]][token] = true; + } + } + } + }); } function findImportInObjjFiles(scope, fragment) From 60f2eaadf51d9775b255f168f7c7ebb1d35b2688 Mon Sep 17 00:00:00 2001 From: Tom Robinson Date: Wed, 27 Jan 2010 22:36:28 -0800 Subject: [PATCH 30/39] Modify press to inspect cibs for dependent classes. --- Tools/press/main.j | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/Tools/press/main.j b/Tools/press/main.j index 0d723b839..87b02a791 100644 --- a/Tools/press/main.j +++ b/Tools/press/main.j @@ -1,13 +1,16 @@ require("narwhal").ensureEngine("rhino"); @import +@import @import "objj-analysis-tools.j" +@import "cib-analysis-tools.j" var ARGS = require("args"); var FILE = require("file"); var OS = require("os"); var DOM = require("browser/dom"); +var UTIL = require("util"); var INTERPRETER = require("interpreter"); var serializer = new DOM.XMLSerializer(); @@ -150,6 +153,10 @@ function pressEnvironment(rootPath, outputFiles, environment, options) { scope.OBJJ_INCLUDE_PATHS = frameworks; scope.OBJJ_ENVIRONMENTS = [environment, "ObjJ"]; + // build list of cibs to inspect for dependent classes + // FIXME: what's the best way to determine which cibs to look in? + var cibs = FILE.glob(rootPath.join("**", "*.cib")).filter(function(path) { return !(/Frameworks/).test(path); }); + // flattening bookkeeping. keep track of the bundles and evaled code (in the correct order!) var bundleArchiveResponses = []; var exectuableResponses = []; @@ -186,7 +193,7 @@ function pressEnvironment(rootPath, outputFiles, environment, options) { // coalesce the results var dependencies = coalesceGlobalDefines(globals); - // Log + // log identifer => files defining CPLog.trace("Global defines:"); Object.keys(dependencies).sort().forEach(function(identifier) { CPLog.trace(" " + identifier + " => " + rootPath.relative(dependencies[identifier])); @@ -219,8 +226,21 @@ function pressEnvironment(rootPath, outputFiles, environment, options) { requiredFiles[mainPath] = true; + // check the code traverseDependencies(context, scope.objj_files[mainPath]); + // check the cibs + cibs.forEach(function(cibPath) { + var cibClasses = findCibClassDependencies(cibPath); + CPLog.debug(cibPath + " => " + cibClasses); + + var referencedFiles = {}; + markFilesReferencedByTokens(cibClasses, context.dependencies, referencedFiles); + checkReferenced(context, null, referencedFiles); + + print(UTIL.repr(referencedFiles)); + }); + var count = 0, total = 0; for (var path in scope.objj_files) From 981132767230db0a76a3cc63d33a7d74f2206132 Mon Sep 17 00:00:00 2001 From: Tom Robinson Date: Wed, 27 Jan 2010 22:37:26 -0800 Subject: [PATCH 31/39] press whitespace cleanup --- Tools/press/cib-analysis-tools.j | 22 +++--- Tools/press/main.j | 122 +++++++++++++++---------------- 2 files changed, 72 insertions(+), 72 deletions(-) diff --git a/Tools/press/cib-analysis-tools.j b/Tools/press/cib-analysis-tools.j index 8482fd423..cab6a919a 100644 --- a/Tools/press/cib-analysis-tools.j +++ b/Tools/press/cib-analysis-tools.j @@ -3,20 +3,20 @@ function findCibClassDependencies(cibPath) { var cib = [[CPCib alloc] initWithContentsOfURL:cibPath]; - + var dependencies = {}; - + var CPClassFromStringOriginal = CPClassFromString; CPClassFromString = function(aClassName) { var result = CPClassFromStringOriginal(aClassName); - + // print("CPClassFromString: " + Array.prototype.slice.call(arguments) + " => " + result); dependencies[aClassName] = true; - + return result; } - - // make sure CPApp is init'd + + // make sure CPApp is init'd [CPApplication sharedApplication] try { @@ -26,7 +26,7 @@ function findCibClassDependencies(cibPath) { } finally { CPClassFromString = CPClassFromStringOriginal; } - + return Object.keys(dependencies); } // this is copied from CPCib's "instantiateCibWithExternalNameTable:" @@ -55,16 +55,16 @@ function findCibClassDependencies(cibPath) { [unarchiver setExternalObjectsForProxyIdentifiers:nil/*[anExternalNameTable objectForKey:CPCibExternalObjects]*/]; var objectData = [unarchiver decodeObjectForKey:"CPCibObjectDataKey"]; - + if (!objectData || ![objectData isKindOfClass:[_CPCibObjectData class]]) return NO; - + var topLevelObjects = nil;//[anExternalNameTable objectForKey:CPCibTopLevelObjects]; - + [objectData instantiateWithOwner:owner topLevelObjects:topLevelObjects] // [objectData establishConnectionsWithOwner:owner topLevelObjects:topLevelObjects]; // [objectData awakeWithOwner:owner topLevelObjects:topLevelObjects]; - + // Display Visible Windows. // [objectData displayVisibleWindows]; diff --git a/Tools/press/main.j b/Tools/press/main.j index 87b02a791..f8f497823 100644 --- a/Tools/press/main.j +++ b/Tools/press/main.j @@ -50,7 +50,7 @@ parser.option("-n", "--nostrip", "strip") .set(false) .help("Do not strip any files"); -parser.option("-p", "--pngcrush", "png") +parser.option("-p", "--pngcrush", "png") .def(false) .set(true) .help("Run pngcrush on all PNGs (pngcrush must be installed!)"); @@ -65,12 +65,12 @@ parser.helpful(); function main(args) { var options = parser.parse(args); - + if (options.args.length < 2) { parser.printUsage(options); return; } - + //if (options.verbose) CPLogRegister(CPLogPrint); //else @@ -96,67 +96,67 @@ function press(rootPath, outputPath, options) { CPLog.info("==========================================="); CPLog.info("Application root: " + rootPath); CPLog.info("Output directory: " + outputPath); - + var outputFiles = {}; - + // analyze and gather files for each environment: options.environments.forEach(function(environment) { pressEnvironment(rootPath, outputFiles, environment, options); }); - + // phase 4: copy everything and write out the new files CPLog.error("PHASE 4: copy to output ("+rootPath+" to "+outputPath+")"); - + FILE.copyTree(rootPath, outputPath); - + for (var path in outputFiles) { var file = outputPath.join(rootPath.relative(path)); - + var parent = file.dirname(); if (!parent.exists()) { CPLog.warn(parent + " doesn't exist, creating directories."); parent.mkdirs(); } - + if (typeof outputFiles[path] !== "string") outputFiles[path] = outputFiles[path].join(""); - + CPLog.info((file.exists() ? "Overwriting: " : "Writing: ") + file); FILE.write(file, outputFiles[path], { charset : "UTF-8" }); } - + // strip known unnecessary files // outputPath.glob("**/Frameworks/Debug").forEach(function(debugFramework) { // outputPath.join(debugFramework).rmtree(); // }); // outputPath.join("index-debug.html").remove(); - + if (options.png) { pngcrushDirectory(outputPath); } } function pressEnvironment(rootPath, outputFiles, environment, options) { - + var mainPath = String(rootPath.join(options.main)); var frameworks = options.frameworks.map(function(framework) { return rootPath.join(framework); }); - + CPLog.info("==========================================="); CPLog.info("Main file: " + mainPath) CPLog.info("Frameworks: " + frameworks); CPLog.info("Environment: " + environment); - + // get a Rhino context var context = new INTERPRETER.Context(); var scope = setupObjectiveJ(context); - + scope.OBJJ_INCLUDE_PATHS = frameworks; scope.OBJJ_ENVIRONMENTS = [environment, "ObjJ"]; - + // build list of cibs to inspect for dependent classes // FIXME: what's the best way to determine which cibs to look in? var cibs = FILE.glob(rootPath.join("**", "*.cib")).filter(function(path) { return !(/Frameworks/).test(path); }); - + // flattening bookkeeping. keep track of the bundles and evaled code (in the correct order!) var bundleArchiveResponses = []; var exectuableResponses = []; @@ -168,42 +168,42 @@ function pressEnvironment(rootPath, outputFiles, environment, options) { success : aResponse.success, filePath : rootPath.relative(aResponse.filePath).toString() }; - + if (aResponse.success) { var xmlString = serializer.serializeToString(aResponse.xml); response.text = CPPropertyListCreate280NorthData(CPPropertyListCreateFromXMLData({ string: xmlString })).string; } - + bundleArchiveResponses.push(response); }); - + functionHookBefore(scope.objj_search.prototype, "didReceiveExecutableResponse", function(aResponse) { exectuableResponses.push(aResponse); }); - + // lets just use the Context object as our con context.rootPath = rootPath; context.scope = scope; - + // phase 1: get global defines CPLog.error("PHASE 1: Loading application..."); - + var globals = findGlobalDefines(context, mainPath, evaledFragments); - + // coalesce the results var dependencies = coalesceGlobalDefines(globals); - + // log identifer => files defining CPLog.trace("Global defines:"); Object.keys(dependencies).sort().forEach(function(identifier) { CPLog.trace(" " + identifier + " => " + rootPath.relative(dependencies[identifier])); }); - + // phase 2: walk the dependency tree (both imports and references) to determine exactly which files need to be included CPLog.error("PHASE 2: Walk dependency tree..."); - + var requiredFiles = {}; - + if (options.nostrip) { // all files are required. no need for analysis @@ -216,31 +216,31 @@ function pressEnvironment(rootPath, outputFiles, environment, options) { CPLog.error("Root file not loaded!"); return; } - + CPLog.warn("Analyzing dependencies..."); - + context.dependencies = dependencies; context.ignoreFrameworkImports = true; // ignores "XXX/XXX.j" imports context.importCallback = function(importing, imported) { requiredFiles[imported] = true; }; context.referenceCallback = function(referencing, referenced) { requiredFiles[referenced] = true; } - + requiredFiles[mainPath] = true; - + // check the code traverseDependencies(context, scope.objj_files[mainPath]); - + // check the cibs cibs.forEach(function(cibPath) { var cibClasses = findCibClassDependencies(cibPath); CPLog.debug(cibPath + " => " + cibClasses); - + var referencedFiles = {}; markFilesReferencedByTokens(cibClasses, context.dependencies, referencedFiles); checkReferenced(context, null, referencedFiles); - + print(UTIL.repr(referencedFiles)); }); - + var count = 0, total = 0; for (var path in scope.objj_files) @@ -248,7 +248,7 @@ function pressEnvironment(rootPath, outputFiles, environment, options) { // mark all ".keytheme"s as required if (/\.keyedtheme$/.test(path)) requiredFiles[path] = true; - + if (requiredFiles[path]) { CPLog.debug("Included: " + rootPath.relative(path)); @@ -257,20 +257,20 @@ function pressEnvironment(rootPath, outputFiles, environment, options) { else { CPLog.info("Excluded: " + rootPath.relative(path)); - } + } total++; } CPLog.warn("Total required files: " + count + " out of " + total); } - + if (options.flatten) { // phase 3a: build single Application.js file (and modified index.html) CPLog.error("PHASE 3a: Flattening..."); - + var applicationScriptName = "Application-"+environment+".js"; var indexHTMLName = "index-"+environment+".html"; - + // Shim for faking bundle responses. // We're just defining it here so we can serialize the function. It's not used within press. // ************************************************** @@ -308,9 +308,9 @@ function pressEnvironment(rootPath, outputFiles, environment, options) { } } // ************************************************** - + var applicationScript = []; - + var URIMaps = {}; Object.keys(scope.objj_bundles).forEach(function(bundleName) { var bundle = scope.objj_bundles[bundleName]; @@ -337,7 +337,7 @@ function pressEnvironment(rootPath, outputFiles, environment, options) { applicationScript.push(" var URIMaps = " + JSON.stringify(URIMaps) + ";"); applicationScript.push(" setupURIMaps(URIMaps);"); applicationScript.push("})();"); - + // add each fragment, wrapped in a function, along with OBJJ_CURRENT_BUNDLE bookkeeping evaledFragments.forEach(function(fragment) { if (requiredFiles[fragment.file.path]) @@ -351,13 +351,13 @@ function pressEnvironment(rootPath, outputFiles, environment, options) { CPLog.info("Stripping " + rootPath.relative(fragment.file.path)); } }); - + // call main once the page has loaded. FIXME: assumes synchronous script loading? applicationScript.push("if (window.addEventListener)"); applicationScript.push(" window.addEventListener('load', main, false);") applicationScript.push("else if (window.attachEvent)") applicationScript.push(" window.attachEvent('onload', main);"); - + // MHTML // TODO: combine multiple MHTMLs exectuableResponses.forEach(function(aResponse) { @@ -367,15 +367,15 @@ function pressEnvironment(rootPath, outputFiles, environment, options) { applicationScript.push(aResponse.text.slice(mhtmlStart, mhtmlEnd+2)); } }); - + var indexHTML = FILE.read(FILE.join(rootPath, "index.html"), { charset : "UTF-8" }); - + // comment out any OBJJ_MAIN_FILE defintions or objj_import() calls indexHTML = indexHTML.replace(/(\bOBJJ_MAIN_FILE\s*=|\bobjj_import\s*\()/g, '//$&'); - + // add a script tag for Application.js at the very end of the block indexHTML = indexHTML.replace(/([ \t]*)(<\/head>)/, '$1 \n$1$2'); - + // output Application.js and index.html outputFiles[rootPath.join(applicationScriptName)] = applicationScript.join("\n"); outputFiles[rootPath.join(indexHTMLName)] = indexHTML; @@ -428,7 +428,7 @@ function pressEnvironment(rootPath, outputFiles, environment, options) { outputFiles[staticPath].push("p;"); outputFiles[staticPath].push(filename.length+";"); outputFiles[staticPath].push(filename); - + for (var i = 0; i < file.fragments.length; i++) { if (file.fragments[i].type & FRAGMENT_CODE) @@ -448,13 +448,13 @@ function pressEnvironment(rootPath, outputFiles, environment, options) { ignoreFragment = true; } } - + if (!ignoreFragment) { if (file.fragments[i].type & FRAGMENT_LOCAL) { var relativePath = pathRelativeTo(file.fragments[i].info, directory) - + outputFiles[staticPath].push("i;"); outputFiles[staticPath].push(relativePath.length+";"); outputFiles[staticPath].push(relativePath); @@ -485,20 +485,20 @@ function pressEnvironment(rootPath, outputFiles, environment, options) { // phase 3.5: fix bundle plists CPLog.error("PHASE 3.5: fix bundle plists"); - + for (var path in bundles) { var directory = FILE.dirname(path), dict = bundles[path].info, replacedFiles = [dict objectForKey:"CPBundleReplacedFiles"]; - + CPLog.info("Modifying .sj: " + rootPath.relative(path)); - + if (replacedFiles) { var newReplacedFiles = []; [dict setObject:newReplacedFiles forKey:"CPBundleReplacedFiles"]; - + for (var i = 0; i < replacedFiles.length; i++) { var replacedFilePath = directory + "/" + replacedFiles[i] @@ -521,12 +521,12 @@ function pressEnvironment(rootPath, outputFiles, environment, options) { function pngcrushDirectory(directory) { var directoryPath = FILE.path(directory); var pngs = directoryPath.glob("**/*.png"); - + system.stderr.print("Running pngcrush on " + pngs.length + " pngs:"); pngs.forEach(function(dst) { var dstPath = directoryPath.join(dst); var tmpPath = FILE.path(dstPath+".tmp"); - + var p = OS.popen(["pngcrush", "-rem", "alla", "-reduce", /*"-brute",*/ dstPath, tmpPath]); if (p.wait()) { CPLog.warn("pngcrush failed. Ensure it's installed and on your PATH."); From 0791fa0ad3c9be2b667c4abf9aa02dc2a631c722 Mon Sep 17 00:00:00 2001 From: Tom Robinson Date: Thu, 28 Jan 2010 00:18:40 -0800 Subject: [PATCH 32/39] Eliminate a couple accidental globals. --- AppKit/CPColor.j | 3 +++ Foundation/CPArray+KVO.j | 3 +++ 2 files changed, 6 insertions(+) diff --git a/AppKit/CPColor.j b/AppKit/CPColor.j index 636f96a3f..cdc3af3a7 100644 --- a/AppKit/CPColor.j +++ b/AppKit/CPColor.j @@ -735,6 +735,9 @@ var CPColorComponentsKey = @"CPColorComponentsKey", var hexCharacters = "0123456789ABCDEF"; +// HACK: prevent these from becoming globals. workaround for obj-j "function foo(){}" behavior +var hexToRGB, integerToBytes, rgbToHex, byteToHex; + /*! Used for the CPColor \c +colorWithHexString: implementation @ignore diff --git a/Foundation/CPArray+KVO.j b/Foundation/CPArray+KVO.j index 1388f8a6e..4c56931a3 100644 --- a/Foundation/CPArray+KVO.j +++ b/Foundation/CPArray+KVO.j @@ -403,6 +403,9 @@ var kvoOperators = []; +// HACK: prevent these from becoming globals. workaround for obj-j "function foo(){}" behavior +var avgOperator, maxOperator, minOperator, countOperator, sumOperator; + kvoOperators["avg"] = function avgOperator(self, _cmd, param) { var objects = [self valueForKeyPath:param], From 5ccf6eefc0894f600a61c3d5a7cba8fadcc6d699 Mon Sep 17 00:00:00 2001 From: Nicholas Small Date: Thu, 28 Jan 2010 17:58:08 -0500 Subject: [PATCH 33/39] Changed CPFlashMovie -fileName to -filename. --- AppKit/CPFlashMovie.j | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/AppKit/CPFlashMovie.j b/AppKit/CPFlashMovie.j index cf7c5cd08..22cd0c520 100644 --- a/AppKit/CPFlashMovie.j +++ b/AppKit/CPFlashMovie.j @@ -30,7 +30,7 @@ */ @implementation CPFlashMovie : CPObject { - CPString _fileName; + CPString _filename; } /*! @@ -38,9 +38,9 @@ @param aFilename the swf to load @return the initialized CPFlashMovie */ -+ (id)flashMovieWithFile:(CPString)aFileName ++ (id)flashMovieWithFile:(CPString)aFilename { - return [[self alloc] initWithFile:aFileName]; + return [[self alloc] initWithFile:aFilename]; } /*! @@ -48,37 +48,37 @@ @param aFilename the swf to load @return the initialized CPFlashMovie */ -- (id)initWithFile:(CPString)aFileName +- (id)initWithFile:(CPString)aFilename { self = [super init]; if (self) - _fileName = aFileName; + _filename = aFilename; return self; } -- (CPString)fileName +- (CPString)filename { - return _fileName; + return _filename; } @end -var CPFlashMovieFileNameKey = "CPFlashMovieFileNameKey"; +var CPFlashMovieFilenameKey = "CPFlashMovieFilenameKey"; @implementation CPFlashMovie (CPCoding) - (id)initWithCoder:(CPCoder)aCoder { - _fileName = [aCoder decodeObjectForKey:CPFlashMovieFileNameKey]; + _filename = [aCoder decodeObjectForKey:CPFlashMovieFilenameKey]; return self; } - (void)encodeWithCoder:(CPCoder)aCoder { - [aCoder encodeObject:_fileName forKey:CPFlashMovieFileNameKey]; + [aCoder encodeObject:_filename forKey:CPFlashMovieFilenameKey]; } @end \ No newline at end of file From bda5ea1c591555d767b641b47c030f60290f745d Mon Sep 17 00:00:00 2001 From: Nicholas Small Date: Thu, 28 Jan 2010 17:58:50 -0500 Subject: [PATCH 34/39] Rewrote CPFlashView implementation to be fully cross-browser and standards compliant. --- AppKit/CPFlashView.j | 137 ++++++++++++++++++++++++------------------- 1 file changed, 78 insertions(+), 59 deletions(-) diff --git a/AppKit/CPFlashView.j b/AppKit/CPFlashView.j index d1c0a9ba0..8f1d99fa8 100644 --- a/AppKit/CPFlashView.j +++ b/AppKit/CPFlashView.j @@ -24,20 +24,21 @@ @import "CPView.j" +var IEFlashCLSID = "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"; + /*! @ingroup appkit */ @implementation CPFlashView : CPView { CPFlashMovie _flashMovie; - CPDictionary _flashVars; CPDictionary _params; CPDictionary _paramElements; - DOMElement _DOMEmbedElement; - DOMElement _DOMMParamElement; + DOMElement _DOMParamElement; DOMElement _DOMObjectElement; + DOMElement _DOMInnerObjectElement; } - (id)initWithFrame:(CGRect)aFrame @@ -46,35 +47,30 @@ if (self) { - _DOMObjectElement = document.createElement("object"); - _DOMObjectElement.width = "100%"; - _DOMObjectElement.height = "100%"; - _DOMObjectElement.style.top = "0px"; - _DOMObjectElement.style.left = "0px"; - - _DOMParamElement = document.createElement("param"); - _DOMParamElement.name = "movie"; - - _DOMObjectElement.appendChild(_DOMParamElement); - - var param = document.createElement("param"); - - param.name = "wmode"; - param.value = "transparent"; - - _DOMObjectElement.appendChild(param); - - _DOMEmbedElement = document.createElement("embed"); - - _DOMEmbedElement.type = "application/x-shockwave-flash"; - _DOMEmbedElement.setAttribute("wmode", "transparent"); - _DOMEmbedElement.width = "100%"; - _DOMEmbedElement.height = "100%"; - - // IE requires this thing to be in the _DOMElement and not the _DOMObjectElement. - _DOMElement.appendChild(_DOMEmbedElement); - - _DOMElement.appendChild(_DOMObjectElement); + if (!CPBrowserIsEngine(CPInternetExplorerBrowserEngine)) + { + _DOMObjectElement = document.createElement(@"object"); + _DOMObjectElement.width = @"100%"; + _DOMObjectElement.height = @"100%"; + _DOMObjectElement.style.top = @"0px"; + _DOMObjectElement.style.left = @"0px"; + _DOMObjectElement.type = @"application/x-shockwave-flash"; + _DOMObjectElement.setAttribute(@"classid", IEFlashCLSID); + + _DOMParamElement = document.createElement(@"param"); + _DOMParamElement.name = @"movie"; + + _DOMInnerObjectElement = document.createElement(@"object"); + _DOMInnerObjectElement.width = @"100%"; + _DOMInnerObjectElement.height = @"100%"; + + _DOMObjectElement.appendChild(_DOMParamElement); + _DOMObjectElement.appendChild(_DOMInnerObjectElement); + + _DOMElement.appendChild(_DOMObjectElement); + } + else + [self _rebuildIEObjects]; } return self; @@ -87,10 +83,13 @@ _flashMovie = aFlashMovie; - _DOMParamElement.value = aFlashMovie._fileName; - - if (_DOMEmbedElement) - _DOMEmbedElement.src = aFlashMovie._fileName; + if (!CPBrowserIsEngine(CPInternetExplorerBrowserEngine)) + { + _DOMParamElement.value = [aFlashMovie filename]; + _DOMInnerObjectElement.data = [aFlashMovie filename]; + } + else + [self _rebuildIEObjects]; } - (CPFlashMovie)flashMovie @@ -100,33 +99,28 @@ - (void)setFlashVars:(CPDictionary)aDictionary { - _flashVars = aDictionary; - var varString = @"", - enumerator = [_flashVars keyEnumerator]; + enumerator = [aDictionary keyEnumerator]; var key; while (key = [enumerator nextObject]) - varString = [varString stringByAppendingFormat:@"&%@=%@", key, [_flashVars objectForKey:key]]; + varString = [varString stringByAppendingFormat:@"&%@=%@", key, [aDictionary objectForKey:key]]; - var param = document.createElement(@"param"); - param.name = @"flashvars"; - param.value = varString; + if (!_params) + _params = [CPDictionary dictionary]; - _DOMObjectElement.appendChild(param); - - if (_DOMEmbedElement) - _DOMEmbedElement.setAttribute(@"flashvars", varString); + [_params setObject:varString forKey:@"flashvars"]; + [self setParameters:_params]; } - (CPDictionary)flashVars { - return _flashVars; + return [_params objectForKey:@"flashvars"]; } - (void)setParameters:(CPDictionary)aDictionary { - if (_paramElements) + if (_paramElements && !CPBrowserIsEngine(CPInternetExplorerBrowserEngine)) { var elements = [_paramElements allValues], count = [elements count]; @@ -136,21 +130,27 @@ } _params = aDictionary; - _paramElements = [CPDictionary dictionary]; - var enumerator = [_params keyEnumerator], - key; - - while (key = [enumerator nextObject]) + if (!CPBrowserIsEngine(CPInternetExplorerBrowserEngine)) { - var param = document.createElement(@"param"); - param.name = key; - param.value = [_params objectForKey:key]; + _paramElements = [CPDictionary dictionary]; - _DOMObjectElement.appendChild(param); + var enumerator = [_params keyEnumerator], + key; - [_paramElements setObject:param forKey:key]; + while (key = [enumerator nextObject] && _DOMObjectElement) + { + var param = document.createElement(@"param"); + param.name = key; + param.value = [_params objectForKey:key]; + + _DOMObjectElement.appendChild(param); + + [_paramElements setObject:param forKey:key]; + } } + else + [self _rebuildIEObjects]; } - (CPDictionary)parameters @@ -158,6 +158,25 @@ return _params; } +- (void)_rebuildIEObjects +{ + _DOMElement.innerHTML = @""; + if (![_flashMovie filename]) + return; + + var paramString = [CPString stringWithFormat:@"", [_flashMovie filename]], + paramEnumerator = [_params keyEnumerator], + key; + + while (key = [paramEnumerator nextObject]) + paramString = [paramString stringByAppendingFormat:@"", key, [_params objectForKey:key]]; + + _DOMObjectElement = document.createElement(@"object"); + _DOMElement.appendChild(_DOMObjectElement); + + _DOMObjectElement.outerHTML = [CPString stringWithFormat:@"%@", IEFlashCLSID, CGRectGetWidth([self bounds]), CGRectGetHeight([self bounds]), paramString]; +} + - (void)mouseDragged:(CPEvent)anEvent { [[[self window] platformWindow] _propagateCurrentDOMEvent:YES]; From b081debf886a5b142b0a2e24ae3684107e6fff56 Mon Sep 17 00:00:00 2001 From: Ross Boucher Date: Thu, 28 Jan 2010 15:49:03 -0800 Subject: [PATCH 35/39] Missing #include for the platform header. Closes #412. --- AppKit/CPMenu/_CPMenuBarWindow.j | 1 + 1 file changed, 1 insertion(+) diff --git a/AppKit/CPMenu/_CPMenuBarWindow.j b/AppKit/CPMenu/_CPMenuBarWindow.j index 1898e7252..da829d047 100644 --- a/AppKit/CPMenu/_CPMenuBarWindow.j +++ b/AppKit/CPMenu/_CPMenuBarWindow.j @@ -1,5 +1,6 @@ #include "../CoreGraphics/CGGeometry.h" +#include "../Platform/Platform.h" @import "_CPMenuWindow.j" From 37fbeaad026479cf49319c7dc91c9fafdf234ff3 Mon Sep 17 00:00:00 2001 From: cacaodev Date: Mon, 25 Jan 2010 09:46:22 +0100 Subject: [PATCH 36/39] CPTableView -dragViewForRowsWithIndexes: - Change the way dragViewOffset is computed to conform to cocoa - Drag exposed rows only --- AppKit/CPTableView.j | 74 ++++++++++++++++++++++++-------------------- 1 file changed, 41 insertions(+), 33 deletions(-) diff --git a/AppKit/CPTableView.j b/AppKit/CPTableView.j index 385a980ed..ef8b36f68 100644 --- a/AppKit/CPTableView.j +++ b/AppKit/CPTableView.j @@ -1445,8 +1445,8 @@ window.setTimeout(function(){ event:(CPEvent)theDragEvent offset:(CPPoint)dragViewOffset { - var size = [self bounds].size, - view = [[CPView alloc] initWithFrame:CPMakeRect(dragViewOffset.x, dragViewOffset.y, size.width, size.height)]; + var bounds = [self bounds], + view = [[CPView alloc] initWithFrame:bounds]; [view setBackgroundColor:[CPColor clearColor]]; [view setAlphaValue:0.7]; @@ -1455,32 +1455,40 @@ window.setTimeout(function(){ // After that we can copy these add them to a transparent drag view and use that drag view // to make it appear we are dragging images of those rows (as you would do in regular Cocoa) var firstExposedColumn = [_exposedColumns firstIndex], - exposedLength = [_exposedColumns lastIndex] - firstExposedColumn + 1, - columns = []; + firstExposedRow = [_exposedRows firstIndex], + exposedColumnsLength = [_exposedColumns lastIndex] - firstExposedColumn + 1, + exposedRowsLength = [_exposedRows lastIndex] - firstExposedRow + 1, + columns = [], + rows = []; - [_exposedColumns getIndexes:columns maxCount:-1 inIndexRange:CPMakeRange(firstExposedColumn, exposedLength)]; - - var columnIndex = [columns count], - draggedDataViews = [], - dragViewHeight = 0.0; + [_exposedColumns getIndexes:columns maxCount:-1 inIndexRange:CPMakeRange(firstExposedColumn, exposedColumnsLength)]; + [theDraggedRows getIndexes:rows maxCount:-1 inIndexRange:CPMakeRange(firstExposedRow, exposedRowsLength)]; + + var columnIndex = [columns count]; - while (columnIndex--) { - var column = [_tableColumns objectAtIndex:columnIndex], - yOffset = 0, - rowIndex = CPNotFound; + while (columnIndex--) + { + var column = columns[columnIndex], + tableColumn = [_tableColumns objectAtIndex:column], + rowIndex = [rows count]; - while ((rowIndex = [_selectedRowIndexes indexGreaterThanIndex:rowIndex]) !== CPNotFound) - { - var dataView = [self _newDataViewForRow:rowIndex tableColumn:column]; + while (rowIndex--) + { + var row = rows[rowIndex]; + var dataView = [self _newDataViewForRow:row tableColumn:tableColumn]; [dataView setBackgroundColor:[CPColor clearColor]]; - [dataView setFrame:[self frameOfDataViewAtColumn:columnIndex row:rowIndex]]; - [dataView setObjectValue:[self _objectValueForTableColumn:column row:rowIndex]]; + [dataView setFrame:[self frameOfDataViewAtColumn:column row:row]]; + [dataView setObjectValue:[self _objectValueForTableColumn:tableColumn row:row]]; [view addSubview:dataView]; } } + var dragPoint = [self convertPoint:[theDragEvent locationInWindow] fromView:nil]; + dragViewOffset.x = CGRectGetWidth(bounds)/2 - dragPoint.x; + dragViewOffset.y = CGRectGetHeight(bounds)/2 - dragPoint.y; + return view; } @@ -2155,10 +2163,8 @@ window.setTimeout(function(){ /* ignore */ - (BOOL)continueTracking:(CGPoint)lastPoint at:(CGPoint)aPoint { - var row = [self rowAtPoint:aPoint]; - // begin the drag is the datasource lets us, we've move at least +-3px vertical or horizontal, or we're dragging from selected rows and we haven't begun a drag session if ( @@ -2170,7 +2176,7 @@ window.setTimeout(function(){ ) ) { - if([_selectedRowIndexes containsIndex:row]) + if ([_selectedRowIndexes containsIndex:row]) _draggedRowIndexes = [[CPIndexSet alloc] initWithIndexSet:_selectedRowIndexes]; else _draggedRowIndexes = [CPIndexSet indexSetWithIndex:row]; @@ -2179,32 +2185,34 @@ window.setTimeout(function(){ //ask the datasource for the data var pboard = [CPPasteboard pasteboardWithName:CPDragPboard]; - if([self canDragRowsWithIndexes:_draggedRowIndexes atPoint:aPoint] && [_dataSource tableView:self writeRowsWithIndexes:_draggedRowIndexes toPasteboard:pboard]) + if ([self canDragRowsWithIndexes:_draggedRowIndexes atPoint:aPoint] && [_dataSource tableView:self writeRowsWithIndexes:_draggedRowIndexes toPasteboard:pboard]) { var currentEvent = [CPApp currentEvent], - offset = CPPointMakeZero(); + offset = CPPointMakeZero(), + tableColumns = [_tableColumns objectsAtIndexes:_exposedColumns]; // We deviate from the default Cocoa implementation here by asking for a view in stead of an image // We support both, but the view prefered over the image because we can mimic the rows we are dragging // by re-creating the data views for the dragged rows var view = [self dragViewForRowsWithIndexes:_draggedRowIndexes - tableColumns:_exposedColumns + tableColumns:tableColumns event:currentEvent - offset:CPPointMakeZero()]; + offset:offset]; - if (!view) { + if (!view) + { var image = [self dragImageForRowsWithIndexes:_draggedRowIndexes - tableColumns:_exposedColumns + tableColumns:tableColumns event:currentEvent - offset:CPPointMakeZero()]; - - view = [[CPImageView alloc] initWithFrame:CPMakeRect(aPoint.x, aPoint.y, [image size].width, [image size].height)]; + offset:offset]; + view = [[CPImageView alloc] initWithFrame:CPMakeRect(0, 0, [image size].width, [image size].height)]; [view setImage:image]; - - offset = aPoint; } - [self dragView:view at:offset offset:CPPointMakeZero() event:[CPApp currentEvent] pasteboard:pboard source:self slideBack:YES]; + var bounds = [view bounds]; + var viewLocation = CPPointMake(aPoint.x - CGRectGetWidth(bounds)/2 + offset.x, aPoint.y - CGRectGetHeight(bounds)/2 + offset.y); + [self dragView:view at:viewLocation offset:CPPointMakeZero() event:[CPApp currentEvent] pasteboard:pboard source:self slideBack:YES]; + return NO; } } From df98483b065a61cdd3b941ce2d86e4c94bfd595b Mon Sep 17 00:00:00 2001 From: Derek Hammer Date: Wed, 27 Jan 2010 01:13:19 -0500 Subject: [PATCH 37/39] Removing extraneous selection did change notifications --- AppKit/CPTableView.j | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/AppKit/CPTableView.j b/AppKit/CPTableView.j index ef8b36f68..1019bbe9b 100644 --- a/AppKit/CPTableView.j +++ b/AppKit/CPTableView.j @@ -658,6 +658,9 @@ window.setTimeout(function(){ { if (([rows firstIndex] != CPNotFound && [rows firstIndex] < 0) || [rows lastIndex] >= [self numberOfRows]) return; + + if ([rows isEqualToIndexSet:_selectedRowIndexes]) + return; // We deselect all columns when selecting rows. if ([_selectedColumnIndexes count] > 0) @@ -2261,10 +2264,6 @@ window.setTimeout(function(){ } } - if (![_previouslySelectedRowIndexes isEqualToIndexSet:_selectedRowIndexes]) - [self _noteSelectionDidChange]; - - if (mouseIsUp && (_implementedDataSourceMethods & CPTableViewDataSource_tableView_setObjectValue_forTableColumn_row_) && !_trackingPointMovedOutOfClickSlop @@ -2660,7 +2659,6 @@ window.setTimeout(function(){ if(i) { [self scrollRowToVisible:i]; - [self _noteSelectionDidChange]; } } @@ -2710,7 +2708,6 @@ window.setTimeout(function(){ if(i) { [self scrollRowToVisible:i]; - [self _noteSelectionDidChange]; } } From b9b2649db915683c58da2b6a4fb02dccddd425b7 Mon Sep 17 00:00:00 2001 From: Randall Luecke Date: Fri, 29 Jan 2010 01:37:57 -0500 Subject: [PATCH 38/39] Cleaned up merge and removed unnessisary selectionIsChanging notification. --- AppKit/CPTableView.j | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/AppKit/CPTableView.j b/AppKit/CPTableView.j index 1019bbe9b..ff105e47a 100644 --- a/AppKit/CPTableView.j +++ b/AppKit/CPTableView.j @@ -656,10 +656,7 @@ window.setTimeout(function(){ - (void)selectRowIndexes:(CPIndexSet)rows byExtendingSelection:(BOOL)shouldExtendSelection { - if (([rows firstIndex] != CPNotFound && [rows firstIndex] < 0) || [rows lastIndex] >= [self numberOfRows]) - return; - - if ([rows isEqualToIndexSet:_selectedRowIndexes]) + if ([rows isEqualToIndexSet:_selectedRowIndexes] || (([rows firstIndex] != CPNotFound && [rows firstIndex] < 0) || [rows lastIndex] >= [self numberOfRows])) return; // We deselect all columns when selecting rows. @@ -2578,8 +2575,6 @@ window.setTimeout(function(){ [self selectRowIndexes:newSelection byExtendingSelection:NO]; - [self _noteSelectionIsChanging]; - } - (void)_noteSelectionIsChanging From b4cd9c4c4537720e191c3d53c2fbfc44e099fc7c Mon Sep 17 00:00:00 2001 From: Ross Boucher Date: Fri, 29 Jan 2010 00:12:51 -0800 Subject: [PATCH 39/39] Remove HTML5 drag and drop support from browsers. --- AppKit/Platform/DOM/CPPlatform.j | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AppKit/Platform/DOM/CPPlatform.j b/AppKit/Platform/DOM/CPPlatform.j index 535644ea2..47af4b175 100644 --- a/AppKit/Platform/DOM/CPPlatform.j +++ b/AppKit/Platform/DOM/CPPlatform.j @@ -44,7 +44,7 @@ var screenNeedsInitialization = NO, + (BOOL)supportsDragAndDrop { - return CPFeatureIsCompatible(CPHTMLDragAndDropFeature); + return CPFeatureIsCompatible(CPHTMLDragAndDropFeature) && ![self isBrowser]; } + (BOOL)supportsNativeMainMenu